Source Code
Overview
ETH Balance
0 ETH
Eth Value
$0.00View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Loading...
Loading
Loading...
Loading
Cross-Chain Transactions
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 Name:
NFTSale
Compiler Version
v0.8.28+commit.7893614a
Optimization Enabled:
Yes with 999999 runs
Other Settings:
cancun EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;
import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol";
import "@openzeppelin/contracts/utils/introspection/IERC165.sol";
import "@openzeppelin/contracts/utils/Address.sol";
import "erc721a-upgradeable/contracts/ERC721AUpgradeable.sol";
import "./interfaces/ISBTParticipation.sol";
import "./interfaces/INFTSale.sol";
/**
* @title NFTSale
* @notice ERC721A NFT Sale contract with whitelist and public allocations.
* @dev
* - Manages ETH deposits, refunds, and claims of ERC721A NFTs with MerkleTree verification.
* - Implements ERC721A (https://github.com/chiru-labs/ERC721A) standard.
* - UUPS upgradeable contract with custom storage slot noted as ERC7201.
*/
contract NFTSale is INFTSale, ERC721AUpgradeable, UUPSUpgradeable, AccessControlUpgradeable, OwnableUpgradeable {
using Address for *;
/// @notice Address of the {SBTParticipation} token contract.
address public immutable SBT_PARTICIPATION;
/// @notice Maximum total supply of {NFTSale} {ERC721A} NFTs.
uint64 public immutable MAX_TOTAL_SUPPLY;
/// @notice Price per one NFT for whitelist participants.
uint64 public immutable WHITELIST_PRICE;
/// @notice Price per one NFT for public participants.
uint64 public immutable PUBLIC_PRICE;
/// @custom:storage-location erc7201:Lumira.storage.NFTSale.NFTSaleStorage
struct NFTSaleStorage {
string _baseURI;
address _receiver;
uint32 _depositStart;
uint32 _depositEnd;
uint32 _claimStart;
uint64 _whitelistDeposited;
uint64 _publicDeposited;
uint64 _whitelistClaimed;
uint64 _publicClaimed;
bytes32 _whitelistRoot;
bytes32 _publicRoot;
bytes32 _refundRoot;
mapping(address user => UserData) _userData;
}
/// @dev keccak256(abi.encode(uint256(keccak256("Lumira.storage.NFTSale.NFTSaleStorage")) - 1)) & ~bytes32(uint256(0xff))
bytes32 public constant NFT_SALE_STORAGE_LOCATION = 0x707edf5e14bfdf1ba58ecef846c170a48a35301414f4b4056ce0592b3a7a1c00;
/// @notice Error indicating a {msg.value} is incorrect.
error NFTSale__InvalidValue();
/// @notice Error indicating a whitelist participant has already deposited.
error NFTSale__DepositedAlready();
/// @notice Error indicating an incorrect sale phase for the desired action.
error NFTSale__IncorrectState();
/// @notice Error indicating a {MerkleProof} verification fails.
error NFTSale__MerkleProofFailed();
/// @notice Error indicating there are no NFTs available to claim.
error NFTSale__NothingToClaim();
/// @notice Error indicating a parameters length mismatch.
error NFTSale__ParamsLengthMismatch();
/// @notice Error indicating an invalid specified timestamps.
error NFTSale__InvalidTimestamp();
/// @notice Error indicating a total supply would be exceeded.
error NFTSale__TotalSupplyExceeded();
/// @notice Error indicating an invalid specified new implementation during {upgradeToAndCall}.
error NFTSale__InvalidImplementation();
/**
* @notice Emitted when a user deposits funds for NFTs by {deposit} function.
* @param user Address of the user who made the deposit.
* @param amount Amount of ETH deposited.
* @param whitelist True if the deposit included the whitelist allocation, false if only public.
* @param publicAmount Number of NFTs paid within a public allocation.
*/
event Deposited(address indexed user, uint256 amount, bool indexed whitelist, uint64 publicAmount);
/**
* @notice Emitted when a user get an ETH refund within a public allocation by {refund} function.
* @param user Address of the user receiving the ETH refund.
* @param amount Amount of ETH refunded.
* @param publicAmount Number of NFTs corresponding to the refunded amount.
*/
event Refunded(address indexed user, uint256 amount, uint64 publicAmount);
/**
* @notice Emitted when a user claims NFTs by {claim} function.
* @param user Address of the user claiming NFTs.
* @param whitelist True if the claim included the whitelist allocation, false if only public.
* @param publicAmount Number of NFTs claimed within a public allocation.
*/
event Claimed(address indexed user, bool indexed whitelist, uint64 publicAmount);
/**
* @notice Emitted when baseURI is updated by {setBaseURI} function.
* @param newBaseURI The new {_baseURI} string.
* @param caller The {msg.sender} address that updated the {_baseURI}.
*/
event BaseURISet(string newBaseURI, address indexed caller);
/**
* @notice Emitted when receiver is updated by {setReceiver} function.
* @param newReceiver The new {_receiver} address.
* @param caller The {msg.sender} address that updated the {_receiver}.
*/
event ReceiverSet(address newReceiver, address indexed caller);
/**
* @notice Emitted when merkleTree roots are updated by {setRoots} function.
* @param newWhitelistRoot The new {_whitelistRoot} for whitelist allocation deposits.
* @param newPublicRoot The new {_publicRoot} for public allocation claims.
* @param newRefundRoot The new {_refundRoot} for public allocation refunds.
* @param caller The {msg.sender} address that updated the {_whitelistRoot}, {_publicRoot}, and {_refundRoot}.
*/
event RootsSet(bytes32 newWhitelistRoot, bytes32 newPublicRoot, bytes32 newRefundRoot, address indexed caller);
/**
* @notice Constructor disables initializer and sets immutable variables.
* @param participationToken Address of the {SBTParticipation} token contract.
* @param maxTotalSupply Maximum total supply of {NFTSale} {ERC721A} NFTs.
* @param whitelistPrice Price per one NFT for whitelist participants.
* @param publicPrice Price per one NFT for public sale participants.
* @custom:oz-upgrades-unsafe-allow constructor
*/
constructor(
address participationToken,
uint64 maxTotalSupply,
uint64 whitelistPrice,
uint64 publicPrice
) {
_disableInitializers();
SBT_PARTICIPATION = participationToken;
MAX_TOTAL_SUPPLY = maxTotalSupply;
WHITELIST_PRICE = whitelistPrice;
PUBLIC_PRICE = publicPrice;
}
/**
* @notice Initializes the proxy contract with admin and ERC721A parameters.
* @dev Grants {DEFAULT_ADMIN_ROLE} to {defaultAdmin}.
* @param defaultAdmin Address that will be granted of {DEFAULT_ADMIN_ROLE} role.
* @param name ERC721A token {name}.
* @param symbol ERC721A token {symbol}.
*/
function initialize(
address defaultAdmin,
string calldata name,
string calldata symbol
) external initializerERC721A() initializer() {
__AccessControl_init();
__UUPSUpgradeable_init();
__ERC721A_init(name, symbol);
_grantRole(DEFAULT_ADMIN_ROLE, defaultAdmin);
}
function initializeV2(address defaultOwner) external reinitializer(2) {
__Ownable_init(defaultOwner);
}
/**
* @notice Allows a user to deposit ETH to participate in the NFT sale within whitelist or/and public allocations.
* @dev Handles whitelist and public deposits, emits {Deposited} event, and mints {SBTParticipation} token.
* @param proof MerkleTree-proof array for whitelist eligibility verification.
*/
function deposit(bytes32[] calldata proof) external payable {
_validateState(State.Deposit);
require(msg.value > 0, NFTSale__InvalidValue());
NFTSaleStorage storage $ = _getNFTSaleStorage();
UserData storage userData = $._userData[msg.sender];
uint256 _publicDepositValue;
uint256 _valueToSend;
uint64 _publicAmount;
if (proof.length > 0) {
require(userData.whitelistDeposited == 0, NFTSale__DepositedAlready());
require(msg.value >= WHITELIST_PRICE, NFTSale__InvalidValue());
_merkleProofVerify(proof, $._whitelistRoot, abi.encode(msg.sender));
userData.whitelistDeposited += 1;
$._whitelistDeposited += 1;
_valueToSend += WHITELIST_PRICE;
if (msg.value > WHITELIST_PRICE) _publicDepositValue = msg.value - WHITELIST_PRICE;
} else {
_publicDepositValue = msg.value;
}
if (_publicDepositValue > 0) {
require(_publicDepositValue % PUBLIC_PRICE == 0, NFTSale__InvalidValue());
_publicAmount = uint64(_publicDepositValue / PUBLIC_PRICE);
userData.publicDeposited += _publicAmount;
$._publicDeposited += _publicAmount;
}
emit Deposited(msg.sender, msg.value, proof.length > 0, _publicAmount);
ISBTParticipation(SBT_PARTICIPATION).mint(msg.sender);
if (_valueToSend > 0) payable($._receiver).sendValue(_valueToSend);
}
/**
* @notice Refunds a user's previously deposited ETH within public allocation.
* @dev Verifies refund eligibility using MerkleTree-proof, send ETH, and emits {Refunded} event.
* @param amountToRefund Number of NFTs to refund within the public allocation.
* @param proof MerkleTree-proof array for refund eligibility verification.
*/
function refund(uint64 amountToRefund, bytes32[] calldata proof) external {
_validateState(State.Claim);
NFTSaleStorage storage $ = _getNFTSaleStorage();
UserData storage userData = $._userData[msg.sender];
_merkleProofVerify(proof, $._refundRoot, abi.encode(msg.sender, amountToRefund));
uint64 _publicUnclaimed = userData.publicDeposited - userData.publicClaimed;
require(_publicUnclaimed >= amountToRefund && _publicUnclaimed != 0, NFTSale__NothingToClaim());
uint64 _publicRefundAmount = amountToRefund * PUBLIC_PRICE;
userData.publicDeposited -= amountToRefund;
$._publicDeposited -= amountToRefund;
emit Refunded(msg.sender, _publicRefundAmount, amountToRefund);
payable(msg.sender).sendValue(_publicRefundAmount);
}
/**
* @notice Claims purchased NFTs after claim phase starts.
* @dev Verifies public allocation claims using MerkleTree-proof, mints NFTs, and emits {Claimed} event.
* @param publicAllocationAmount Number of NFTs to claim within public allocation.
* @param proof MerkleTree-proof array for public eligibility verification.
*/
function claim(uint64 publicAllocationAmount, bytes32[] calldata proof) external {
_validateState(State.Claim);
NFTSaleStorage storage $ = _getNFTSaleStorage();
UserData storage userData = $._userData[msg.sender];
require(userData.whitelistDeposited > 0 || userData.publicDeposited > 0, NFTSale__NothingToClaim());
uint64 _publicAmount;
if (proof.length > 0) {
_merkleProofVerify(proof, $._publicRoot, abi.encode(msg.sender, publicAllocationAmount));
_publicAmount = publicAllocationAmount - userData.publicClaimed;
}
uint64 _whitelistAmount = userData.whitelistDeposited - userData.whitelistClaimed;
require(_publicAmount > 0 || _whitelistAmount > 0, NFTSale__NothingToClaim());
if (_publicAmount > 0) {
$._publicClaimed += _publicAmount;
userData.publicClaimed += _publicAmount;
}
if (_whitelistAmount > 0) {
$._whitelistClaimed += _whitelistAmount;
userData.whitelistClaimed += _whitelistAmount;
}
_validateTotalSupply(_publicAmount + _whitelistAmount);
_safeMint(msg.sender, _publicAmount + _whitelistAmount);
emit Claimed(msg.sender, _whitelistAmount > 0, _publicAmount);
}
/**
* @notice Allows an admin to mint NFTs directly to specified addresses.
* @dev Validates arrays' lengths and total supply before minting.
* @dev Only callable by {DEFAULT_ADMIN_ROLE}.
* @param to Array of recievers addresses.
* @param amounts Array of amounts to mint per reciever.
*/
function mintByAdmin(address[] calldata to, uint16[] calldata amounts) external onlyRole(DEFAULT_ADMIN_ROLE) {
require(to.length == amounts.length, NFTSale__ParamsLengthMismatch());
uint16 _totalAmount;
for (uint16 i; to.length > i; i++) _totalAmount += amounts[i];
_validateTotalSupply(_totalAmount);
for (uint16 i; to.length > i; i++) _safeMint(to[i], amounts[i]);
}
/**
* @notice Sets deposit and claim phase timestamps.
* @dev Validates that timestamps are in chronological order.
* @dev Only callable by {DEFAULT_ADMIN_ROLE}.
* @param newDepositStart Start timestamp of the deposit phase.
* @param newDepositEnd End timestamp of the deposit phase.
* @param newClaimStart Start timestamp of the claim phase.
*/
function setTimestamps(
uint32 newDepositStart,
uint32 newDepositEnd,
uint32 newClaimStart
) external onlyRole(DEFAULT_ADMIN_ROLE) {
NFTSaleStorage storage $ = _getNFTSaleStorage();
if ($._depositStart == 0) require(newDepositStart > block.timestamp, NFTSale__InvalidTimestamp());
require(newDepositEnd > newDepositStart, NFTSale__InvalidTimestamp());
require(newClaimStart > newDepositEnd, NFTSale__InvalidTimestamp());
$._depositStart = newDepositStart;
$._depositEnd = newDepositEnd;
$._claimStart = newClaimStart;
}
/**
* @notice Updates the base URI for all minted NFTs.
* @dev Only callable by {DEFAULT_ADMIN_ROLE}. Emits {BaseURISet} event.
* @param newBaseURI The new {_baseURI} string.
*/
function setBaseURI(string calldata newBaseURI) external onlyRole(DEFAULT_ADMIN_ROLE) {
_setBaseURI(newBaseURI);
}
/**
* @notice Updates the receiver of deposited ETH (all of whitelist allocation and public allocation).
* @dev Only callable by {DEFAULT_ADMIN_ROLE}. Emits {ReceiverSet} event.
* @param newReceiver The new {_receiver} address.
*/
function setReceiver(address newReceiver) external onlyRole(DEFAULT_ADMIN_ROLE) {
_setReceiver(newReceiver);
}
/**
* @notice Sets all merkleTree roots for whitelist and public allocations, and refund eligibility.
* @dev Only callable by {DEFAULT_ADMIN_ROLE}. Emits {RootsSet} event.
* @param newWhitelistRoot The new {_whitelistRoot} for whitelist allocation deposits.
* @param newPublicRoot The new {_publicRoot} for public allocation claims.
* @param newRefundRoot The new {_refundRoot} for public allocation refunds.
*/
function setRoots(
bytes32 newWhitelistRoot,
bytes32 newPublicRoot,
bytes32 newRefundRoot
) external onlyRole(DEFAULT_ADMIN_ROLE) {
_setRoots(newWhitelistRoot, newPublicRoot, newRefundRoot);
}
function withdrawFunds(uint256 amount) external onlyRole(DEFAULT_ADMIN_ROLE) {
NFTSaleStorage storage $ = _getNFTSaleStorage();
payable($._receiver).sendValue(amount);
}
/**
* @notice Returns the current ETH receiver address.
* @return fundsReceiver The {_receiver} address.
*/
function receiver() external view returns(address fundsReceiver) {
NFTSaleStorage storage $ = _getNFTSaleStorage();
return $._receiver;
}
/**
* @notice Returns the current NFT sale phase.
* @return currentState Current {INFTSale.State} NFT sale phase.
*/
function getState() public view returns(State currentState) {
NFTSaleStorage storage $ = _getNFTSaleStorage();
if ($._depositStart == 0) return State.Preparation;
if ($._depositStart <= block.timestamp && block.timestamp <= $._depositEnd) return State.Deposit;
if ($._claimStart <= block.timestamp) return State.Claim;
return State.Preparation;
}
/**
* @notice Returns the IDs of all NFTs owned by a specified user.
* @param owner Address of the NFTs holder.
* @return ids Array of NFT token IDs.
*/
function getIdsOfOwner(address owner) external view returns(uint64[] memory ids) {
uint256 _ownerBalance = balanceOf(owner);
if (_ownerBalance == 0) return new uint64[](0);
ids = new uint64[](_ownerBalance);
uint16 _counter;
for (uint16 i; totalSupply() > i; i++) {
if (ownerOf(i) == owner) {
ids[_counter] = i;
_counter += 1;
if (_counter == _ownerBalance) return ids;
}
}
}
/**
* @notice Returns timestamps of deposit and claim phases.
* @return depositStart Start timestamp {_depositStart} of the deposit phase.
* @return depositEnd End timestamp {_depositEnd} of the deposit phase.
* @return claimStart Start timestamp {_claimStart} of the claim phase.
*/
function getTimestamps() external view returns(uint32 depositStart, uint32 depositEnd, uint32 claimStart) {
NFTSaleStorage storage $ = _getNFTSaleStorage();
return ($._depositStart, $._depositEnd, $._claimStart);
}
/**
* @notice Returns sale statistics of paid and claimed NFTs.
* @return whitelistDeposited The {_whitelistDeposited} number of paid NFTs within whitelist allocation.
* @return publicDeposited The {_publicDeposited} number of paid NFTs within public allocation.
* @return whitelistClaimed The {_whitelistClaimed} number of claimed NFTs within whitelist allocation.
* @return publicClaimed The {_publicClaimed} number of claimed NFTs within public allocation.
*/
function getStats() external view returns(
uint64 whitelistDeposited,
uint64 publicDeposited,
uint64 whitelistClaimed,
uint64 publicClaimed
) {
NFTSaleStorage storage $ = _getNFTSaleStorage();
return ($._whitelistDeposited, $._publicDeposited, $._whitelistClaimed, $._publicClaimed);
}
/**
* @notice Returns all merkleTree roots used for verification.
* @return whitelistRoot The {_whitelistRoot} for whitelist allocation deposits.
* @return publicRoot The {_publicRoot} for public allocation claims.
* @return refundRoot The{_refundRoot} for public allocation refunds.
*/
function getRoots() external view returns(bytes32 whitelistRoot, bytes32 publicRoot, bytes32 refundRoot) {
NFTSaleStorage storage $ = _getNFTSaleStorage();
return ($._whitelistRoot, $._publicRoot, $._refundRoot);
}
/**
* @notice Returns stored user's data for a specified user address.
* @param user Address of the user.
* @return userData {INFTSale.UserData} struct containing user's deposits and claims:
* - {_userData[user].whitelistDeposited} - number of paid NFTs within whitelist allocation.
* - {_userData[user].publicDeposited} - number of paid NFTs within public allocation.
* - {_userData[user].whitelistClaimed} - number of claimed NFTs within whitelist allocation.
* - {_userData[user].publicClaimed} - number of claimed NFTs within public allocation.
*/
function getUserData(address user) external view returns(UserData memory userData) {
NFTSaleStorage storage $ = _getNFTSaleStorage();
return $._userData[user];
}
/**
* @notice 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
* to learn more about how these ids are created.
*/
function supportsInterface(
bytes4 interfaceId
) public view override(IERC721AUpgradeable, ERC721AUpgradeable, AccessControlUpgradeable) returns(bool) {
return interfaceId == type(INFTSale).interfaceId || super.supportsInterface(interfaceId);
}
/// @notice Internal function to validate max total supply.
function _validateTotalSupply(uint64 newTokensAmount) internal view {
require(MAX_TOTAL_SUPPLY >= totalSupply() + newTokensAmount, NFTSale__TotalSupplyExceeded());
}
/// @notice Internal function to validate the sale phase.
function _validateState(State desiredState) internal view {
require(getState() == desiredState, NFTSale__IncorrectState());
}
/// @notice Overridden {ERC721A} internal function to returns base URI for NFTs metadata.
function _baseURI() internal view override returns(string memory) {
NFTSaleStorage storage $ = _getNFTSaleStorage();
return $._baseURI;
}
/// @notice Internal function to verify merkleTree-proof.
function _merkleProofVerify(bytes32[] calldata proof, bytes32 root, bytes memory leaf) internal pure {
require(MerkleProof.verify(proof, root, keccak256(bytes.concat(keccak256(leaf)))), NFTSale__MerkleProofFailed());
}
/**
* @notice Overridden {UUPSUpgradeable} internal function to restrict implementation upgrade.
* @dev Ensures {newImplementation} has correct storage slot and supports {AccessControl}.
* @param newImplementation Address of the specified {newImplementation}.
*/
function _authorizeUpgrade(address newImplementation) internal override onlyRole(DEFAULT_ADMIN_ROLE) {
require(
INFTSale(newImplementation).NFT_SALE_STORAGE_LOCATION() == NFT_SALE_STORAGE_LOCATION,
NFTSale__InvalidImplementation()
);
require(
IERC165(newImplementation).supportsInterface(type(IAccessControl).interfaceId),
NFTSale__InvalidImplementation()
);
}
function _setBaseURI(string calldata newBaseURI) internal {
NFTSaleStorage storage $ = _getNFTSaleStorage();
$._baseURI = newBaseURI;
emit BaseURISet(newBaseURI, msg.sender);
}
function _setReceiver(address newReceiver) internal {
NFTSaleStorage storage $ = _getNFTSaleStorage();
$._receiver = newReceiver;
emit ReceiverSet(newReceiver, msg.sender);
}
function _setRoots(bytes32 newWhitelistRoot, bytes32 newPublicRoot, bytes32 newRefundRoot) internal {
NFTSaleStorage storage $ = _getNFTSaleStorage();
$._whitelistRoot = newWhitelistRoot;
$._publicRoot = newPublicRoot;
$._refundRoot = newRefundRoot;
emit RootsSet(newWhitelistRoot, newPublicRoot, newRefundRoot, msg.sender);
}
function _getNFTSaleStorage() private pure returns(NFTSaleStorage storage $) {
assembly {
$.slot := NFT_SALE_STORAGE_LOCATION
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/AccessControl.sol)
pragma solidity ^0.8.20;
import {IAccessControl} from "@openzeppelin/contracts/access/IAccessControl.sol";
import {ContextUpgradeable} from "../utils/ContextUpgradeable.sol";
import {ERC165Upgradeable} from "../utils/introspection/ERC165Upgradeable.sol";
import {Initializable} from "../proxy/utils/Initializable.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 AccessControlUpgradeable is Initializable, ContextUpgradeable, IAccessControl, ERC165Upgradeable {
struct RoleData {
mapping(address account => bool) hasRole;
bytes32 adminRole;
}
bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
/// @custom:storage-location erc7201:openzeppelin.storage.AccessControl
struct AccessControlStorage {
mapping(bytes32 role => RoleData) _roles;
}
// keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.AccessControl")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant AccessControlStorageLocation = 0x02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b626800;
function _getAccessControlStorage() private pure returns (AccessControlStorage storage $) {
assembly {
$.slot := AccessControlStorageLocation
}
}
/**
* @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);
_;
}
function __AccessControl_init() internal onlyInitializing {
}
function __AccessControl_init_unchained() internal onlyInitializing {
}
/**
* @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) {
AccessControlStorage storage $ = _getAccessControlStorage();
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) {
AccessControlStorage storage $ = _getAccessControlStorage();
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 {
AccessControlStorage storage $ = _getAccessControlStorage();
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) {
AccessControlStorage storage $ = _getAccessControlStorage();
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) {
AccessControlStorage storage $ = _getAccessControlStorage();
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/Ownable.sol)
pragma solidity ^0.8.20;
import {ContextUpgradeable} from "../utils/ContextUpgradeable.sol";
import {Initializable} from "../proxy/utils/Initializable.sol";
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* The initial owner is set to the address provided by the deployer. This can
* later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/
abstract contract OwnableUpgradeable is Initializable, ContextUpgradeable {
/// @custom:storage-location erc7201:openzeppelin.storage.Ownable
struct OwnableStorage {
address _owner;
}
// keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Ownable")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant OwnableStorageLocation = 0x9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300;
function _getOwnableStorage() private pure returns (OwnableStorage storage $) {
assembly {
$.slot := OwnableStorageLocation
}
}
/**
* @dev The caller account is not authorized to perform an operation.
*/
error OwnableUnauthorizedAccount(address account);
/**
* @dev The owner is not a valid owner account. (eg. `address(0)`)
*/
error OwnableInvalidOwner(address owner);
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the address provided by the deployer as the initial owner.
*/
function __Ownable_init(address initialOwner) internal onlyInitializing {
__Ownable_init_unchained(initialOwner);
}
function __Ownable_init_unchained(address initialOwner) internal onlyInitializing {
if (initialOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(initialOwner);
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
_checkOwner();
_;
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view virtual returns (address) {
OwnableStorage storage $ = _getOwnableStorage();
return $._owner;
}
/**
* @dev Throws if the sender is not the owner.
*/
function _checkOwner() internal view virtual {
if (owner() != _msgSender()) {
revert OwnableUnauthorizedAccount(_msgSender());
}
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby disabling any functionality that is only available to the owner.
*/
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual onlyOwner {
if (newOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual {
OwnableStorage storage $ = _getOwnableStorage();
address oldOwner = $._owner;
$._owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (proxy/utils/Initializable.sol)
pragma solidity ^0.8.20;
/**
* @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
* behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
* external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
* function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
*
* The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
* reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
* case an upgrade adds a module that needs to be initialized.
*
* For example:
*
* [.hljs-theme-light.nopadding]
* ```solidity
* contract MyToken is ERC20Upgradeable {
* function initialize() initializer public {
* __ERC20_init("MyToken", "MTK");
* }
* }
*
* contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
* function initializeV2() reinitializer(2) public {
* __ERC20Permit_init("MyToken");
* }
* }
* ```
*
* TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
* possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
*
* CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
* that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
*
* [CAUTION]
* ====
* Avoid leaving a contract uninitialized.
*
* An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
* contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
* the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
*
* [.hljs-theme-light.nopadding]
* ```
* /// @custom:oz-upgrades-unsafe-allow constructor
* constructor() {
* _disableInitializers();
* }
* ```
* ====
*/
abstract contract Initializable {
/**
* @dev Storage of the initializable contract.
*
* It's implemented on a custom ERC-7201 namespace to reduce the risk of storage collisions
* when using with upgradeable contracts.
*
* @custom:storage-location erc7201:openzeppelin.storage.Initializable
*/
struct InitializableStorage {
/**
* @dev Indicates that the contract has been initialized.
*/
uint64 _initialized;
/**
* @dev Indicates that the contract is in the process of being initialized.
*/
bool _initializing;
}
// keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Initializable")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant INITIALIZABLE_STORAGE = 0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00;
/**
* @dev The contract is already initialized.
*/
error InvalidInitialization();
/**
* @dev The contract is not initializing.
*/
error NotInitializing();
/**
* @dev Triggered when the contract has been initialized or reinitialized.
*/
event Initialized(uint64 version);
/**
* @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
* `onlyInitializing` functions can be used to initialize parent contracts.
*
* Similar to `reinitializer(1)`, except that in the context of a constructor an `initializer` may be invoked any
* number of times. This behavior in the constructor can be useful during testing and is not expected to be used in
* production.
*
* Emits an {Initialized} event.
*/
modifier initializer() {
// solhint-disable-next-line var-name-mixedcase
InitializableStorage storage $ = _getInitializableStorage();
// Cache values to avoid duplicated sloads
bool isTopLevelCall = !$._initializing;
uint64 initialized = $._initialized;
// Allowed calls:
// - initialSetup: the contract is not in the initializing state and no previous version was
// initialized
// - construction: the contract is initialized at version 1 (no reininitialization) and the
// current contract is just being deployed
bool initialSetup = initialized == 0 && isTopLevelCall;
bool construction = initialized == 1 && address(this).code.length == 0;
if (!initialSetup && !construction) {
revert InvalidInitialization();
}
$._initialized = 1;
if (isTopLevelCall) {
$._initializing = true;
}
_;
if (isTopLevelCall) {
$._initializing = false;
emit Initialized(1);
}
}
/**
* @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
* contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
* used to initialize parent contracts.
*
* A reinitializer may be used after the original initialization step. This is essential to configure modules that
* are added through upgrades and that require initialization.
*
* When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
* cannot be nested. If one is invoked in the context of another, execution will revert.
*
* Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
* a contract, executing them in the right order is up to the developer or operator.
*
* WARNING: Setting the version to 2**64 - 1 will prevent any future reinitialization.
*
* Emits an {Initialized} event.
*/
modifier reinitializer(uint64 version) {
// solhint-disable-next-line var-name-mixedcase
InitializableStorage storage $ = _getInitializableStorage();
if ($._initializing || $._initialized >= version) {
revert InvalidInitialization();
}
$._initialized = version;
$._initializing = true;
_;
$._initializing = false;
emit Initialized(version);
}
/**
* @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
* {initializer} and {reinitializer} modifiers, directly or indirectly.
*/
modifier onlyInitializing() {
_checkInitializing();
_;
}
/**
* @dev Reverts if the contract is not in an initializing state. See {onlyInitializing}.
*/
function _checkInitializing() internal view virtual {
if (!_isInitializing()) {
revert NotInitializing();
}
}
/**
* @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
* Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
* to any version. It is recommended to use this to lock implementation contracts that are designed to be called
* through proxies.
*
* Emits an {Initialized} event the first time it is successfully executed.
*/
function _disableInitializers() internal virtual {
// solhint-disable-next-line var-name-mixedcase
InitializableStorage storage $ = _getInitializableStorage();
if ($._initializing) {
revert InvalidInitialization();
}
if ($._initialized != type(uint64).max) {
$._initialized = type(uint64).max;
emit Initialized(type(uint64).max);
}
}
/**
* @dev Returns the highest version that has been initialized. See {reinitializer}.
*/
function _getInitializedVersion() internal view returns (uint64) {
return _getInitializableStorage()._initialized;
}
/**
* @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
*/
function _isInitializing() internal view returns (bool) {
return _getInitializableStorage()._initializing;
}
/**
* @dev Returns a pointer to the storage namespace.
*/
// solhint-disable-next-line var-name-mixedcase
function _getInitializableStorage() private pure returns (InitializableStorage storage $) {
assembly {
$.slot := INITIALIZABLE_STORAGE
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (proxy/utils/UUPSUpgradeable.sol)
pragma solidity ^0.8.20;
import {IERC1822Proxiable} from "@openzeppelin/contracts/interfaces/draft-IERC1822.sol";
import {ERC1967Utils} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Utils.sol";
import {Initializable} from "./Initializable.sol";
/**
* @dev An upgradeability mechanism designed for UUPS proxies. The functions included here can perform an upgrade of an
* {ERC1967Proxy}, when this contract is set as the implementation behind such a proxy.
*
* A security mechanism ensures that an upgrade does not turn off upgradeability accidentally, although this risk is
* reinstated if the upgrade retains upgradeability but removes the security mechanism, e.g. by replacing
* `UUPSUpgradeable` with a custom implementation of upgrades.
*
* The {_authorizeUpgrade} function must be overridden to include access restriction to the upgrade mechanism.
*/
abstract contract UUPSUpgradeable is Initializable, IERC1822Proxiable {
/// @custom:oz-upgrades-unsafe-allow state-variable-immutable
address private immutable __self = address(this);
/**
* @dev The version of the upgrade interface of the contract. If this getter is missing, both `upgradeTo(address)`
* and `upgradeToAndCall(address,bytes)` are present, and `upgradeTo` must be used if no function should be called,
* while `upgradeToAndCall` will invoke the `receive` function if the second argument is the empty byte string.
* If the getter returns `"5.0.0"`, only `upgradeToAndCall(address,bytes)` is present, and the second argument must
* be the empty byte string if no function should be called, making it impossible to invoke the `receive` function
* during an upgrade.
*/
string public constant UPGRADE_INTERFACE_VERSION = "5.0.0";
/**
* @dev The call is from an unauthorized context.
*/
error UUPSUnauthorizedCallContext();
/**
* @dev The storage `slot` is unsupported as a UUID.
*/
error UUPSUnsupportedProxiableUUID(bytes32 slot);
/**
* @dev Check that the execution is being performed through a delegatecall call and that the execution context is
* a proxy contract with an implementation (as defined in ERC1967) pointing to self. This should only be the case
* for UUPS and transparent proxies that are using the current contract as their implementation. Execution of a
* function through ERC1167 minimal proxies (clones) would not normally pass this test, but is not guaranteed to
* fail.
*/
modifier onlyProxy() {
_checkProxy();
_;
}
/**
* @dev Check that the execution is not being performed through a delegate call. This allows a function to be
* callable on the implementing contract but not through proxies.
*/
modifier notDelegated() {
_checkNotDelegated();
_;
}
function __UUPSUpgradeable_init() internal onlyInitializing {
}
function __UUPSUpgradeable_init_unchained() internal onlyInitializing {
}
/**
* @dev Implementation of the ERC1822 {proxiableUUID} function. This returns the storage slot used by the
* implementation. It is used to validate the implementation's compatibility when performing an upgrade.
*
* IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks
* bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this
* function revert if invoked through a proxy. This is guaranteed by the `notDelegated` modifier.
*/
function proxiableUUID() external view virtual notDelegated returns (bytes32) {
return ERC1967Utils.IMPLEMENTATION_SLOT;
}
/**
* @dev Upgrade the implementation of the proxy to `newImplementation`, and subsequently execute the function call
* encoded in `data`.
*
* Calls {_authorizeUpgrade}.
*
* Emits an {Upgraded} event.
*
* @custom:oz-upgrades-unsafe-allow-reachable delegatecall
*/
function upgradeToAndCall(address newImplementation, bytes memory data) public payable virtual onlyProxy {
_authorizeUpgrade(newImplementation);
_upgradeToAndCallUUPS(newImplementation, data);
}
/**
* @dev Reverts if the execution is not performed via delegatecall or the execution
* context is not of a proxy with an ERC1967-compliant implementation pointing to self.
* See {_onlyProxy}.
*/
function _checkProxy() internal view virtual {
if (
address(this) == __self || // Must be called through delegatecall
ERC1967Utils.getImplementation() != __self // Must be called through an active proxy
) {
revert UUPSUnauthorizedCallContext();
}
}
/**
* @dev Reverts if the execution is performed via delegatecall.
* See {notDelegated}.
*/
function _checkNotDelegated() internal view virtual {
if (address(this) != __self) {
// Must not be called through delegatecall
revert UUPSUnauthorizedCallContext();
}
}
/**
* @dev Function that should revert when `msg.sender` is not authorized to upgrade the contract. Called by
* {upgradeToAndCall}.
*
* Normally, this function will use an xref:access.adoc[access control] modifier such as {Ownable-onlyOwner}.
*
* ```solidity
* function _authorizeUpgrade(address) internal onlyOwner {}
* ```
*/
function _authorizeUpgrade(address newImplementation) internal virtual;
/**
* @dev Performs an implementation upgrade with a security check for UUPS proxies, and additional setup call.
*
* As a security check, {proxiableUUID} is invoked in the new implementation, and the return value
* is expected to be the implementation slot in ERC1967.
*
* Emits an {IERC1967-Upgraded} event.
*/
function _upgradeToAndCallUUPS(address newImplementation, bytes memory data) private {
try IERC1822Proxiable(newImplementation).proxiableUUID() returns (bytes32 slot) {
if (slot != ERC1967Utils.IMPLEMENTATION_SLOT) {
revert UUPSUnsupportedProxiableUUID(slot);
}
ERC1967Utils.upgradeToAndCall(newImplementation, data);
} catch {
// The implementation is not UUPS
revert ERC1967Utils.ERC1967InvalidImplementation(newImplementation);
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)
pragma solidity ^0.8.20;
import {Initializable} from "../proxy/utils/Initializable.sol";
/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract ContextUpgradeable is Initializable {
function __Context_init() internal onlyInitializing {
}
function __Context_init_unchained() internal onlyInitializing {
}
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
function _contextSuffixLength() internal view virtual returns (uint256) {
return 0;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/ERC165.sol)
pragma solidity ^0.8.20;
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
import {Initializable} from "../../proxy/utils/Initializable.sol";
/**
* @dev Implementation of the {IERC165} interface.
*
* Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
* for the additional interface id that will be supported. For example:
*
* ```solidity
* function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
* return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
* }
* ```
*/
abstract contract ERC165Upgradeable is Initializable, IERC165 {
function __ERC165_init() internal onlyInitializing {
}
function __ERC165_init_unchained() internal onlyInitializing {
}
/**
* @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) (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-IERC1822.sol)
pragma solidity ^0.8.20;
/**
* @dev ERC1822: Universal Upgradeable Proxy Standard (UUPS) documents a method for upgradeability through a simplified
* proxy whose upgrades are fully controlled by the current implementation.
*/
interface IERC1822Proxiable {
/**
* @dev Returns the storage slot that the proxiable contract assumes is being used to store the implementation
* address.
*
* IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks
* bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this
* function revert if invoked through a proxy.
*/
function proxiableUUID() external view returns (bytes32);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (proxy/beacon/IBeacon.sol)
pragma solidity ^0.8.20;
/**
* @dev This is the interface that {BeaconProxy} expects of its beacon.
*/
interface IBeacon {
/**
* @dev Must return an address that can be used as a delegate call target.
*
* {UpgradeableBeacon} will check that this address is a contract.
*/
function implementation() external view returns (address);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (proxy/ERC1967/ERC1967Utils.sol)
pragma solidity ^0.8.20;
import {IBeacon} from "../beacon/IBeacon.sol";
import {Address} from "../../utils/Address.sol";
import {StorageSlot} from "../../utils/StorageSlot.sol";
/**
* @dev This abstract contract provides getters and event emitting update functions for
* https://eips.ethereum.org/EIPS/eip-1967[EIP1967] slots.
*/
library ERC1967Utils {
// We re-declare ERC-1967 events here because they can't be used directly from IERC1967.
// This will be fixed in Solidity 0.8.21. At that point we should remove these events.
/**
* @dev Emitted when the implementation is upgraded.
*/
event Upgraded(address indexed implementation);
/**
* @dev Emitted when the admin account has changed.
*/
event AdminChanged(address previousAdmin, address newAdmin);
/**
* @dev Emitted when the beacon is changed.
*/
event BeaconUpgraded(address indexed beacon);
/**
* @dev Storage slot with the address of the current implementation.
* This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1.
*/
// solhint-disable-next-line private-vars-leading-underscore
bytes32 internal constant IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
/**
* @dev The `implementation` of the proxy is invalid.
*/
error ERC1967InvalidImplementation(address implementation);
/**
* @dev The `admin` of the proxy is invalid.
*/
error ERC1967InvalidAdmin(address admin);
/**
* @dev The `beacon` of the proxy is invalid.
*/
error ERC1967InvalidBeacon(address beacon);
/**
* @dev An upgrade function sees `msg.value > 0` that may be lost.
*/
error ERC1967NonPayable();
/**
* @dev Returns the current implementation address.
*/
function getImplementation() internal view returns (address) {
return StorageSlot.getAddressSlot(IMPLEMENTATION_SLOT).value;
}
/**
* @dev Stores a new address in the EIP1967 implementation slot.
*/
function _setImplementation(address newImplementation) private {
if (newImplementation.code.length == 0) {
revert ERC1967InvalidImplementation(newImplementation);
}
StorageSlot.getAddressSlot(IMPLEMENTATION_SLOT).value = newImplementation;
}
/**
* @dev Performs implementation upgrade with additional setup call if data is nonempty.
* This function is payable only if the setup call is performed, otherwise `msg.value` is rejected
* to avoid stuck value in the contract.
*
* Emits an {IERC1967-Upgraded} event.
*/
function upgradeToAndCall(address newImplementation, bytes memory data) internal {
_setImplementation(newImplementation);
emit Upgraded(newImplementation);
if (data.length > 0) {
Address.functionDelegateCall(newImplementation, data);
} else {
_checkNonPayable();
}
}
/**
* @dev Storage slot with the admin of the contract.
* This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1.
*/
// solhint-disable-next-line private-vars-leading-underscore
bytes32 internal constant ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;
/**
* @dev Returns the current admin.
*
* TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using
* the https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
* `0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103`
*/
function getAdmin() internal view returns (address) {
return StorageSlot.getAddressSlot(ADMIN_SLOT).value;
}
/**
* @dev Stores a new address in the EIP1967 admin slot.
*/
function _setAdmin(address newAdmin) private {
if (newAdmin == address(0)) {
revert ERC1967InvalidAdmin(address(0));
}
StorageSlot.getAddressSlot(ADMIN_SLOT).value = newAdmin;
}
/**
* @dev Changes the admin of the proxy.
*
* Emits an {IERC1967-AdminChanged} event.
*/
function changeAdmin(address newAdmin) internal {
emit AdminChanged(getAdmin(), newAdmin);
_setAdmin(newAdmin);
}
/**
* @dev The storage slot of the UpgradeableBeacon contract which defines the implementation for this proxy.
* This is the keccak-256 hash of "eip1967.proxy.beacon" subtracted by 1.
*/
// solhint-disable-next-line private-vars-leading-underscore
bytes32 internal constant BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50;
/**
* @dev Returns the current beacon.
*/
function getBeacon() internal view returns (address) {
return StorageSlot.getAddressSlot(BEACON_SLOT).value;
}
/**
* @dev Stores a new beacon in the EIP1967 beacon slot.
*/
function _setBeacon(address newBeacon) private {
if (newBeacon.code.length == 0) {
revert ERC1967InvalidBeacon(newBeacon);
}
StorageSlot.getAddressSlot(BEACON_SLOT).value = newBeacon;
address beaconImplementation = IBeacon(newBeacon).implementation();
if (beaconImplementation.code.length == 0) {
revert ERC1967InvalidImplementation(beaconImplementation);
}
}
/**
* @dev Change the beacon and trigger a setup call if data is nonempty.
* This function is payable only if the setup call is performed, otherwise `msg.value` is rejected
* to avoid stuck value in the contract.
*
* Emits an {IERC1967-BeaconUpgraded} event.
*
* CAUTION: Invoking this function has no effect on an instance of {BeaconProxy} since v5, since
* it uses an immutable beacon without looking at the value of the ERC-1967 beacon slot for
* efficiency.
*/
function upgradeBeaconToAndCall(address newBeacon, bytes memory data) internal {
_setBeacon(newBeacon);
emit BeaconUpgraded(newBeacon);
if (data.length > 0) {
Address.functionDelegateCall(IBeacon(newBeacon).implementation(), data);
} else {
_checkNonPayable();
}
}
/**
* @dev Reverts if `msg.value` is not zero. It can be used to avoid `msg.value` stuck in the contract
* if an upgrade doesn't perform an initialization call.
*/
function _checkNonPayable() private {
if (msg.value > 0) {
revert ERC1967NonPayable();
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (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) (utils/Address.sol)
pragma solidity ^0.8.20;
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @dev The ETH balance of the account is not enough to perform the operation.
*/
error AddressInsufficientBalance(address account);
/**
* @dev There's no code at `target` (it is not a contract).
*/
error AddressEmptyCode(address target);
/**
* @dev A call to an address target failed. The target may have reverted.
*/
error FailedInnerCall();
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.8.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
if (address(this).balance < amount) {
revert AddressInsufficientBalance(address(this));
}
(bool success, ) = recipient.call{value: amount}("");
if (!success) {
revert FailedInnerCall();
}
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain `call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason or custom error, it is bubbled
* up by this function (like regular Solidity function calls). However, if
* the call reverted with no returned reason, this function reverts with a
* {FailedInnerCall} error.
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*/
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
if (address(this).balance < value) {
revert AddressInsufficientBalance(address(this));
}
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target
* was not a contract or bubbling up the revert reason (falling back to {FailedInnerCall}) in case of an
* unsuccessful call.
*/
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata
) internal view returns (bytes memory) {
if (!success) {
_revert(returndata);
} else {
// only check if target is a contract if the call was successful and the return data is empty
// otherwise we already know that it was a contract
if (returndata.length == 0 && target.code.length == 0) {
revert AddressEmptyCode(target);
}
return returndata;
}
}
/**
* @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the
* revert reason or with a default {FailedInnerCall} error.
*/
function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
if (!success) {
_revert(returndata);
} else {
return returndata;
}
}
/**
* @dev Reverts with returndata if present. Otherwise reverts with {FailedInnerCall}.
*/
function _revert(bytes memory returndata) private pure {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
/// @solidity memory-safe-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert FailedInnerCall();
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/MerkleProof.sol)
pragma solidity ^0.8.20;
/**
* @dev These functions deal with verification of Merkle Tree proofs.
*
* The tree and the proofs can be generated using our
* https://github.com/OpenZeppelin/merkle-tree[JavaScript library].
* You will find a quickstart guide in the readme.
*
* WARNING: You should avoid using leaf values that are 64 bytes long prior to
* hashing, or use a hash function other than keccak256 for hashing leaves.
* This is because the concatenation of a sorted pair of internal nodes in
* the Merkle tree could be reinterpreted as a leaf value.
* OpenZeppelin's JavaScript library generates Merkle trees that are safe
* against this attack out of the box.
*/
library MerkleProof {
/**
*@dev The multiproof provided is not valid.
*/
error MerkleProofInvalidMultiproof();
/**
* @dev Returns true if a `leaf` can be proved to be a part of a Merkle tree
* defined by `root`. For this, a `proof` must be provided, containing
* sibling hashes on the branch from the leaf to the root of the tree. Each
* pair of leaves and each pair of pre-images are assumed to be sorted.
*/
function verify(bytes32[] memory proof, bytes32 root, bytes32 leaf) internal pure returns (bool) {
return processProof(proof, leaf) == root;
}
/**
* @dev Calldata version of {verify}
*/
function verifyCalldata(bytes32[] calldata proof, bytes32 root, bytes32 leaf) internal pure returns (bool) {
return processProofCalldata(proof, leaf) == root;
}
/**
* @dev Returns the rebuilt hash obtained by traversing a Merkle tree up
* from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt
* hash matches the root of the tree. When processing the proof, the pairs
* of leafs & pre-images are assumed to be sorted.
*/
function processProof(bytes32[] memory proof, bytes32 leaf) internal pure returns (bytes32) {
bytes32 computedHash = leaf;
for (uint256 i = 0; i < proof.length; i++) {
computedHash = _hashPair(computedHash, proof[i]);
}
return computedHash;
}
/**
* @dev Calldata version of {processProof}
*/
function processProofCalldata(bytes32[] calldata proof, bytes32 leaf) internal pure returns (bytes32) {
bytes32 computedHash = leaf;
for (uint256 i = 0; i < proof.length; i++) {
computedHash = _hashPair(computedHash, proof[i]);
}
return computedHash;
}
/**
* @dev Returns true if the `leaves` can be simultaneously proven to be a part of a Merkle tree defined by
* `root`, according to `proof` and `proofFlags` as described in {processMultiProof}.
*
* CAUTION: Not all Merkle trees admit multiproofs. See {processMultiProof} for details.
*/
function multiProofVerify(
bytes32[] memory proof,
bool[] memory proofFlags,
bytes32 root,
bytes32[] memory leaves
) internal pure returns (bool) {
return processMultiProof(proof, proofFlags, leaves) == root;
}
/**
* @dev Calldata version of {multiProofVerify}
*
* CAUTION: Not all Merkle trees admit multiproofs. See {processMultiProof} for details.
*/
function multiProofVerifyCalldata(
bytes32[] calldata proof,
bool[] calldata proofFlags,
bytes32 root,
bytes32[] memory leaves
) internal pure returns (bool) {
return processMultiProofCalldata(proof, proofFlags, leaves) == root;
}
/**
* @dev Returns the root of a tree reconstructed from `leaves` and sibling nodes in `proof`. The reconstruction
* proceeds by incrementally reconstructing all inner nodes by combining a leaf/inner node with either another
* leaf/inner node or a proof sibling node, depending on whether each `proofFlags` item is true or false
* respectively.
*
* CAUTION: Not all Merkle trees admit multiproofs. To use multiproofs, it is sufficient to ensure that: 1) the tree
* is complete (but not necessarily perfect), 2) the leaves to be proven are in the opposite order they are in the
* tree (i.e., as seen from right to left starting at the deepest layer and continuing at the next layer).
*/
function processMultiProof(
bytes32[] memory proof,
bool[] memory proofFlags,
bytes32[] memory leaves
) internal pure returns (bytes32 merkleRoot) {
// This function rebuilds the root hash by traversing the tree up from the leaves. The root is rebuilt by
// consuming and producing values on a queue. The queue starts with the `leaves` array, then goes onto the
// `hashes` array. At the end of the process, the last hash in the `hashes` array should contain the root of
// the Merkle tree.
uint256 leavesLen = leaves.length;
uint256 proofLen = proof.length;
uint256 totalHashes = proofFlags.length;
// Check proof validity.
if (leavesLen + proofLen != totalHashes + 1) {
revert MerkleProofInvalidMultiproof();
}
// The xxxPos values are "pointers" to the next value to consume in each array. All accesses are done using
// `xxx[xxxPos++]`, which return the current value and increment the pointer, thus mimicking a queue's "pop".
bytes32[] memory hashes = new bytes32[](totalHashes);
uint256 leafPos = 0;
uint256 hashPos = 0;
uint256 proofPos = 0;
// At each step, we compute the next hash using two values:
// - a value from the "main queue". If not all leaves have been consumed, we get the next leaf, otherwise we
// get the next hash.
// - depending on the flag, either another value from the "main queue" (merging branches) or an element from the
// `proof` array.
for (uint256 i = 0; i < totalHashes; i++) {
bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++];
bytes32 b = proofFlags[i]
? (leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++])
: proof[proofPos++];
hashes[i] = _hashPair(a, b);
}
if (totalHashes > 0) {
if (proofPos != proofLen) {
revert MerkleProofInvalidMultiproof();
}
unchecked {
return hashes[totalHashes - 1];
}
} else if (leavesLen > 0) {
return leaves[0];
} else {
return proof[0];
}
}
/**
* @dev Calldata version of {processMultiProof}.
*
* CAUTION: Not all Merkle trees admit multiproofs. See {processMultiProof} for details.
*/
function processMultiProofCalldata(
bytes32[] calldata proof,
bool[] calldata proofFlags,
bytes32[] memory leaves
) internal pure returns (bytes32 merkleRoot) {
// This function rebuilds the root hash by traversing the tree up from the leaves. The root is rebuilt by
// consuming and producing values on a queue. The queue starts with the `leaves` array, then goes onto the
// `hashes` array. At the end of the process, the last hash in the `hashes` array should contain the root of
// the Merkle tree.
uint256 leavesLen = leaves.length;
uint256 proofLen = proof.length;
uint256 totalHashes = proofFlags.length;
// Check proof validity.
if (leavesLen + proofLen != totalHashes + 1) {
revert MerkleProofInvalidMultiproof();
}
// The xxxPos values are "pointers" to the next value to consume in each array. All accesses are done using
// `xxx[xxxPos++]`, which return the current value and increment the pointer, thus mimicking a queue's "pop".
bytes32[] memory hashes = new bytes32[](totalHashes);
uint256 leafPos = 0;
uint256 hashPos = 0;
uint256 proofPos = 0;
// At each step, we compute the next hash using two values:
// - a value from the "main queue". If not all leaves have been consumed, we get the next leaf, otherwise we
// get the next hash.
// - depending on the flag, either another value from the "main queue" (merging branches) or an element from the
// `proof` array.
for (uint256 i = 0; i < totalHashes; i++) {
bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++];
bytes32 b = proofFlags[i]
? (leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++])
: proof[proofPos++];
hashes[i] = _hashPair(a, b);
}
if (totalHashes > 0) {
if (proofPos != proofLen) {
revert MerkleProofInvalidMultiproof();
}
unchecked {
return hashes[totalHashes - 1];
}
} else if (leavesLen > 0) {
return leaves[0];
} else {
return proof[0];
}
}
/**
* @dev Sorts the pair (a, b) and hashes the result.
*/
function _hashPair(bytes32 a, bytes32 b) private pure returns (bytes32) {
return a < b ? _efficientHash(a, b) : _efficientHash(b, a);
}
/**
* @dev Implementation of keccak256(abi.encode(a, b)) that doesn't allocate or expand memory.
*/
function _efficientHash(bytes32 a, bytes32 b) private pure returns (bytes32 value) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, a)
mstore(0x20, b)
value := keccak256(0x00, 0x40)
}
}
}// 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/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
pragma solidity ^0.8.20;
import "erc721a-upgradeable/contracts/IERC721AUpgradeable.sol";
interface INFTSale is IERC721AUpgradeable {
struct UserData {
uint64 whitelistDeposited;
uint64 publicDeposited;
uint64 whitelistClaimed;
uint64 publicClaimed;
}
enum State {Preparation, Deposit, Claim}
function SBT_PARTICIPATION() external view returns(address participationTokenAddress);
function MAX_TOTAL_SUPPLY() external view returns(uint64 maxTotalSupply);
function WHITELIST_PRICE() external view returns(uint64 whitelistPrice);
function PUBLIC_PRICE() external view returns(uint64 publicPrice);
function NFT_SALE_STORAGE_LOCATION() external view returns(bytes32 storagePointer);
function getState() external view returns(State currentState);
function getIdsOfOwner(address owner) external view returns(uint64[] memory ids);
function receiver() external view returns(address fundsReceiver);
function getTimestamps() external view returns(uint32 depositStart, uint32 depositEnd, uint32 claimStart);
function getStats() external view returns(
uint64 whitelistDeposited,
uint64 publicDeposited,
uint64 whitelistClaimed,
uint64 publicClaimed
);
function getRoots() external view returns(bytes32 whitelistRoot, bytes32 publicRoot, bytes32 refundRoot);
function getUserData(address user) external view returns(UserData memory userData);
function deposit(bytes32[] calldata proof) external payable;
function refund(uint64 amountToRefund, bytes32[] calldata proof) external;
function claim(uint64 publicAllocationAmount, bytes32[] calldata proof) external;
function mintByAdmin(address[] calldata to, uint16[] calldata amounts) external;
function setTimestamps(uint32 newDepositStart, uint32 newDepositEnd, uint32 newClaimStart) external;
function setBaseURI(string calldata newBaseURI) external;
function setReceiver(address newReceiver) external;
function setRoots(bytes32 newWhitelistRoot, bytes32 newPublicRoot, bytes32 newRefundRoot) external;
function withdrawFunds(uint256 amount) external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "@openzeppelin/contracts/token/ERC721/extensions/IERC721Metadata.sol";
interface ISBTParticipation is IERC721Metadata {
function SBT_PARTICIPATION_STORAGE_LOCATION() external view returns(bytes32 storagePointer);
function totalSupply() external view returns(uint256 tokenTotalSupply);
function mint(address to) external;
function setTokenURI(string calldata newTokenURI) external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @dev This is a base contract to aid in writing upgradeable diamond facet contracts, or any kind of contract that will be deployed
* behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
* external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
* function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
*
* TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
* possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
*
* CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
* that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
*/
import {ERC721A__InitializableStorage} from './ERC721A__InitializableStorage.sol';
abstract contract ERC721A__Initializable {
using ERC721A__InitializableStorage for ERC721A__InitializableStorage.Layout;
/**
* @dev Modifier to protect an initializer function from being invoked twice.
*/
modifier initializerERC721A() {
// If the contract is initializing we ignore whether _initialized is set in order to support multiple
// inheritance patterns, but we only do this in the context of a constructor, because in other contexts the
// contract may have been reentered.
require(
ERC721A__InitializableStorage.layout()._initializing
? _isConstructor()
: !ERC721A__InitializableStorage.layout()._initialized,
'ERC721A__Initializable: contract is already initialized'
);
bool isTopLevelCall = !ERC721A__InitializableStorage.layout()._initializing;
if (isTopLevelCall) {
ERC721A__InitializableStorage.layout()._initializing = true;
ERC721A__InitializableStorage.layout()._initialized = true;
}
_;
if (isTopLevelCall) {
ERC721A__InitializableStorage.layout()._initializing = false;
}
}
/**
* @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
* {initializer} modifier, directly or indirectly.
*/
modifier onlyInitializingERC721A() {
require(
ERC721A__InitializableStorage.layout()._initializing,
'ERC721A__Initializable: contract is not initializing'
);
_;
}
/// @dev Returns true if and only if the function is running in the constructor
function _isConstructor() private view returns (bool) {
// extcodesize checks the size of the code stored in an address, and
// address returns the current address. Since the code is still not
// deployed when running a constructor, any checks on its code size will
// yield zero, making it an effective way to detect if a contract is
// under construction or not.
address self = address(this);
uint256 cs;
assembly {
cs := extcodesize(self)
}
return cs == 0;
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @dev This is a base storage for the initialization function for upgradeable diamond facet contracts
**/
library ERC721A__InitializableStorage {
struct Layout {
/*
* Indicates that the contract has been initialized.
*/
bool _initialized;
/*
* Indicates that the contract is in the process of being initialized.
*/
bool _initializing;
}
bytes32 internal constant STORAGE_SLOT = keccak256('ERC721A.contracts.storage.initializable.facet');
function layout() internal pure returns (Layout storage l) {
bytes32 slot = STORAGE_SLOT;
assembly {
l.slot := slot
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
library ERC721AStorage {
// Bypass for a `--via-ir` bug (https://github.com/chiru-labs/ERC721A/pull/364).
struct TokenApprovalRef {
address value;
}
struct Layout {
// =============================================================
// STORAGE
// =============================================================
// The next token ID to be minted.
uint256 _currentIndex;
// The number of tokens burned.
uint256 _burnCounter;
// Token name
string _name;
// Token symbol
string _symbol;
// Mapping from token ID to ownership details
// An empty struct value does not necessarily mean the token is unowned.
// See {_packedOwnershipOf} implementation for details.
//
// Bits Layout:
// - [0..159] `addr`
// - [160..223] `startTimestamp`
// - [224] `burned`
// - [225] `nextInitialized`
// - [232..255] `extraData`
mapping(uint256 => uint256) _packedOwnerships;
// Mapping owner address to address data.
//
// Bits Layout:
// - [0..63] `balance`
// - [64..127] `numberMinted`
// - [128..191] `numberBurned`
// - [192..255] `aux`
mapping(address => uint256) _packedAddressData;
// Mapping from token ID to approved address.
mapping(uint256 => ERC721AStorage.TokenApprovalRef) _tokenApprovals;
// Mapping from owner to operator approvals
mapping(address => mapping(address => bool)) _operatorApprovals;
// The amount of tokens minted above `_sequentialUpTo()`.
// We call these spot mints (i.e. non-sequential mints).
uint256 _spotMinted;
}
bytes32 internal constant STORAGE_SLOT = keccak256('ERC721A.contracts.storage.ERC721A');
function layout() internal pure returns (Layout storage l) {
bytes32 slot = STORAGE_SLOT;
assembly {
l.slot := slot
}
}
}// SPDX-License-Identifier: MIT
// ERC721A Contracts v4.3.0
// Creator: Chiru Labs
pragma solidity ^0.8.4;
import './IERC721AUpgradeable.sol';
import {ERC721AStorage} from './ERC721AStorage.sol';
import './ERC721A__Initializable.sol';
/**
* @dev Interface of ERC721 token receiver.
*/
interface ERC721A__IERC721ReceiverUpgradeable {
function onERC721Received(
address operator,
address from,
uint256 tokenId,
bytes calldata data
) external returns (bytes4);
}
/**
* @title ERC721A
*
* @dev Implementation of the [ERC721](https://eips.ethereum.org/EIPS/eip-721)
* Non-Fungible Token Standard, including the Metadata extension.
* Optimized for lower gas during batch mints.
*
* Token IDs are minted in sequential order (e.g. 0, 1, 2, 3, ...)
* starting from `_startTokenId()`.
*
* The `_sequentialUpTo()` function can be overriden to enable spot mints
* (i.e. non-consecutive mints) for `tokenId`s greater than `_sequentialUpTo()`.
*
* Assumptions:
*
* - An owner cannot have more than 2**64 - 1 (max value of uint64) of supply.
* - The maximum token ID cannot exceed 2**256 - 1 (max value of uint256).
*/
contract ERC721AUpgradeable is ERC721A__Initializable, IERC721AUpgradeable {
using ERC721AStorage for ERC721AStorage.Layout;
// =============================================================
// CONSTANTS
// =============================================================
// Mask of an entry in packed address data.
uint256 private constant _BITMASK_ADDRESS_DATA_ENTRY = (1 << 64) - 1;
// The bit position of `numberMinted` in packed address data.
uint256 private constant _BITPOS_NUMBER_MINTED = 64;
// The bit position of `numberBurned` in packed address data.
uint256 private constant _BITPOS_NUMBER_BURNED = 128;
// The bit position of `aux` in packed address data.
uint256 private constant _BITPOS_AUX = 192;
// Mask of all 256 bits in packed address data except the 64 bits for `aux`.
uint256 private constant _BITMASK_AUX_COMPLEMENT = (1 << 192) - 1;
// The bit position of `startTimestamp` in packed ownership.
uint256 private constant _BITPOS_START_TIMESTAMP = 160;
// The bit mask of the `burned` bit in packed ownership.
uint256 private constant _BITMASK_BURNED = 1 << 224;
// The bit position of the `nextInitialized` bit in packed ownership.
uint256 private constant _BITPOS_NEXT_INITIALIZED = 225;
// The bit mask of the `nextInitialized` bit in packed ownership.
uint256 private constant _BITMASK_NEXT_INITIALIZED = 1 << 225;
// The bit position of `extraData` in packed ownership.
uint256 private constant _BITPOS_EXTRA_DATA = 232;
// Mask of all 256 bits in a packed ownership except the 24 bits for `extraData`.
uint256 private constant _BITMASK_EXTRA_DATA_COMPLEMENT = (1 << 232) - 1;
// The mask of the lower 160 bits for addresses.
uint256 private constant _BITMASK_ADDRESS = (1 << 160) - 1;
// The maximum `quantity` that can be minted with {_mintERC2309}.
// This limit is to prevent overflows on the address data entries.
// For a limit of 5000, a total of 3.689e15 calls to {_mintERC2309}
// is required to cause an overflow, which is unrealistic.
uint256 private constant _MAX_MINT_ERC2309_QUANTITY_LIMIT = 5000;
// The `Transfer` event signature is given by:
// `keccak256(bytes("Transfer(address,address,uint256)"))`.
bytes32 private constant _TRANSFER_EVENT_SIGNATURE =
0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef;
// =============================================================
// CONSTRUCTOR
// =============================================================
function __ERC721A_init(string memory name_, string memory symbol_) internal onlyInitializingERC721A {
__ERC721A_init_unchained(name_, symbol_);
}
function __ERC721A_init_unchained(string memory name_, string memory symbol_) internal onlyInitializingERC721A {
ERC721AStorage.layout()._name = name_;
ERC721AStorage.layout()._symbol = symbol_;
ERC721AStorage.layout()._currentIndex = _startTokenId();
if (_sequentialUpTo() < _startTokenId()) _revert(SequentialUpToTooSmall.selector);
}
// =============================================================
// TOKEN COUNTING OPERATIONS
// =============================================================
/**
* @dev Returns the starting token ID for sequential mints.
*
* Override this function to change the starting token ID for sequential mints.
*
* Note: The value returned must never change after any tokens have been minted.
*/
function _startTokenId() internal view virtual returns (uint256) {
return 0;
}
/**
* @dev Returns the maximum token ID (inclusive) for sequential mints.
*
* Override this function to return a value less than 2**256 - 1,
* but greater than `_startTokenId()`, to enable spot (non-sequential) mints.
*
* Note: The value returned must never change after any tokens have been minted.
*/
function _sequentialUpTo() internal view virtual returns (uint256) {
return type(uint256).max;
}
/**
* @dev Returns the next token ID to be minted.
*/
function _nextTokenId() internal view virtual returns (uint256) {
return ERC721AStorage.layout()._currentIndex;
}
/**
* @dev Returns the total number of tokens in existence.
* Burned tokens will reduce the count.
* To get the total number of tokens minted, please see {_totalMinted}.
*/
function totalSupply() public view virtual override returns (uint256 result) {
// Counter underflow is impossible as `_burnCounter` cannot be incremented
// more than `_currentIndex + _spotMinted - _startTokenId()` times.
unchecked {
// With spot minting, the intermediate `result` can be temporarily negative,
// and the computation must be unchecked.
result = ERC721AStorage.layout()._currentIndex - ERC721AStorage.layout()._burnCounter - _startTokenId();
if (_sequentialUpTo() != type(uint256).max) result += ERC721AStorage.layout()._spotMinted;
}
}
/**
* @dev Returns the total amount of tokens minted in the contract.
*/
function _totalMinted() internal view virtual returns (uint256 result) {
// Counter underflow is impossible as `_currentIndex` does not decrement,
// and it is initialized to `_startTokenId()`.
unchecked {
result = ERC721AStorage.layout()._currentIndex - _startTokenId();
if (_sequentialUpTo() != type(uint256).max) result += ERC721AStorage.layout()._spotMinted;
}
}
/**
* @dev Returns the total number of tokens burned.
*/
function _totalBurned() internal view virtual returns (uint256) {
return ERC721AStorage.layout()._burnCounter;
}
/**
* @dev Returns the total number of tokens that are spot-minted.
*/
function _totalSpotMinted() internal view virtual returns (uint256) {
return ERC721AStorage.layout()._spotMinted;
}
// =============================================================
// ADDRESS DATA OPERATIONS
// =============================================================
/**
* @dev Returns the number of tokens in `owner`'s account.
*/
function balanceOf(address owner) public view virtual override returns (uint256) {
if (owner == address(0)) _revert(BalanceQueryForZeroAddress.selector);
return ERC721AStorage.layout()._packedAddressData[owner] & _BITMASK_ADDRESS_DATA_ENTRY;
}
/**
* Returns the number of tokens minted by `owner`.
*/
function _numberMinted(address owner) internal view returns (uint256) {
return
(ERC721AStorage.layout()._packedAddressData[owner] >> _BITPOS_NUMBER_MINTED) & _BITMASK_ADDRESS_DATA_ENTRY;
}
/**
* Returns the number of tokens burned by or on behalf of `owner`.
*/
function _numberBurned(address owner) internal view returns (uint256) {
return
(ERC721AStorage.layout()._packedAddressData[owner] >> _BITPOS_NUMBER_BURNED) & _BITMASK_ADDRESS_DATA_ENTRY;
}
/**
* Returns the auxiliary data for `owner`. (e.g. number of whitelist mint slots used).
*/
function _getAux(address owner) internal view returns (uint64) {
return uint64(ERC721AStorage.layout()._packedAddressData[owner] >> _BITPOS_AUX);
}
/**
* Sets the auxiliary data for `owner`. (e.g. number of whitelist mint slots used).
* If there are multiple variables, please pack them into a uint64.
*/
function _setAux(address owner, uint64 aux) internal virtual {
uint256 packed = ERC721AStorage.layout()._packedAddressData[owner];
uint256 auxCasted;
// Cast `aux` with assembly to avoid redundant masking.
assembly {
auxCasted := aux
}
packed = (packed & _BITMASK_AUX_COMPLEMENT) | (auxCasted << _BITPOS_AUX);
ERC721AStorage.layout()._packedAddressData[owner] = packed;
}
// =============================================================
// IERC165
// =============================================================
/**
* @dev Returns true if this contract implements the interface defined by
* `interfaceId`. See the corresponding
* [EIP section](https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified)
* to learn more about how these ids are created.
*
* This function call must use less than 30000 gas.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
// The interface IDs are constants representing the first 4 bytes
// of the XOR of all function selectors in the interface.
// See: [ERC165](https://eips.ethereum.org/EIPS/eip-165)
// (e.g. `bytes4(i.functionA.selector ^ i.functionB.selector ^ ...)`)
return
interfaceId == 0x01ffc9a7 || // ERC165 interface ID for ERC165.
interfaceId == 0x80ac58cd || // ERC165 interface ID for ERC721.
interfaceId == 0x5b5e139f; // ERC165 interface ID for ERC721Metadata.
}
// =============================================================
// IERC721Metadata
// =============================================================
/**
* @dev Returns the token collection name.
*/
function name() public view virtual override returns (string memory) {
return ERC721AStorage.layout()._name;
}
/**
* @dev Returns the token collection symbol.
*/
function symbol() public view virtual override returns (string memory) {
return ERC721AStorage.layout()._symbol;
}
/**
* @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token.
*/
function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
if (!_exists(tokenId)) _revert(URIQueryForNonexistentToken.selector);
string memory baseURI = _baseURI();
return bytes(baseURI).length != 0 ? string(abi.encodePacked(baseURI, _toString(tokenId))) : '';
}
/**
* @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, it can be overridden in child contracts.
*/
function _baseURI() internal view virtual returns (string memory) {
return '';
}
// =============================================================
// OWNERSHIPS OPERATIONS
// =============================================================
/**
* @dev Returns the owner of the `tokenId` token.
*
* Requirements:
*
* - `tokenId` must exist.
*/
function ownerOf(uint256 tokenId) public view virtual override returns (address) {
return address(uint160(_packedOwnershipOf(tokenId)));
}
/**
* @dev Gas spent here starts off proportional to the maximum mint batch size.
* It gradually moves to O(1) as tokens get transferred around over time.
*/
function _ownershipOf(uint256 tokenId) internal view virtual returns (TokenOwnership memory) {
return _unpackedOwnership(_packedOwnershipOf(tokenId));
}
/**
* @dev Returns the unpacked `TokenOwnership` struct at `index`.
*/
function _ownershipAt(uint256 index) internal view virtual returns (TokenOwnership memory) {
return _unpackedOwnership(ERC721AStorage.layout()._packedOwnerships[index]);
}
/**
* @dev Returns whether the ownership slot at `index` is initialized.
* An uninitialized slot does not necessarily mean that the slot has no owner.
*/
function _ownershipIsInitialized(uint256 index) internal view virtual returns (bool) {
return ERC721AStorage.layout()._packedOwnerships[index] != 0;
}
/**
* @dev Initializes the ownership slot minted at `index` for efficiency purposes.
*/
function _initializeOwnershipAt(uint256 index) internal virtual {
if (ERC721AStorage.layout()._packedOwnerships[index] == 0) {
ERC721AStorage.layout()._packedOwnerships[index] = _packedOwnershipOf(index);
}
}
/**
* @dev Returns the packed ownership data of `tokenId`.
*/
function _packedOwnershipOf(uint256 tokenId) private view returns (uint256 packed) {
if (_startTokenId() <= tokenId) {
packed = ERC721AStorage.layout()._packedOwnerships[tokenId];
if (tokenId > _sequentialUpTo()) {
if (_packedOwnershipExists(packed)) return packed;
_revert(OwnerQueryForNonexistentToken.selector);
}
// If the data at the starting slot does not exist, start the scan.
if (packed == 0) {
if (tokenId >= ERC721AStorage.layout()._currentIndex) _revert(OwnerQueryForNonexistentToken.selector);
// Invariant:
// There will always be an initialized ownership slot
// (i.e. `ownership.addr != address(0) && ownership.burned == false`)
// before an unintialized ownership slot
// (i.e. `ownership.addr == address(0) && ownership.burned == false`)
// Hence, `tokenId` will not underflow.
//
// We can directly compare the packed value.
// If the address is zero, packed will be zero.
for (;;) {
unchecked {
packed = ERC721AStorage.layout()._packedOwnerships[--tokenId];
}
if (packed == 0) continue;
if (packed & _BITMASK_BURNED == 0) return packed;
// Otherwise, the token is burned, and we must revert.
// This handles the case of batch burned tokens, where only the burned bit
// of the starting slot is set, and remaining slots are left uninitialized.
_revert(OwnerQueryForNonexistentToken.selector);
}
}
// Otherwise, the data exists and we can skip the scan.
// This is possible because we have already achieved the target condition.
// This saves 2143 gas on transfers of initialized tokens.
// If the token is not burned, return `packed`. Otherwise, revert.
if (packed & _BITMASK_BURNED == 0) return packed;
}
_revert(OwnerQueryForNonexistentToken.selector);
}
/**
* @dev Returns the unpacked `TokenOwnership` struct from `packed`.
*/
function _unpackedOwnership(uint256 packed) private pure returns (TokenOwnership memory ownership) {
ownership.addr = address(uint160(packed));
ownership.startTimestamp = uint64(packed >> _BITPOS_START_TIMESTAMP);
ownership.burned = packed & _BITMASK_BURNED != 0;
ownership.extraData = uint24(packed >> _BITPOS_EXTRA_DATA);
}
/**
* @dev Packs ownership data into a single uint256.
*/
function _packOwnershipData(address owner, uint256 flags) private view returns (uint256 result) {
assembly {
// Mask `owner` to the lower 160 bits, in case the upper bits somehow aren't clean.
owner := and(owner, _BITMASK_ADDRESS)
// `owner | (block.timestamp << _BITPOS_START_TIMESTAMP) | flags`.
result := or(owner, or(shl(_BITPOS_START_TIMESTAMP, timestamp()), flags))
}
}
/**
* @dev Returns the `nextInitialized` flag set if `quantity` equals 1.
*/
function _nextInitializedFlag(uint256 quantity) private pure returns (uint256 result) {
// For branchless setting of the `nextInitialized` flag.
assembly {
// `(quantity == 1) << _BITPOS_NEXT_INITIALIZED`.
result := shl(_BITPOS_NEXT_INITIALIZED, eq(quantity, 1))
}
}
// =============================================================
// APPROVAL OPERATIONS
// =============================================================
/**
* @dev Gives permission to `to` to transfer `tokenId` token to another account. See {ERC721A-_approve}.
*
* Requirements:
*
* - The caller must own the token or be an approved operator.
*/
function approve(address to, uint256 tokenId) public payable virtual override {
_approve(to, tokenId, true);
}
/**
* @dev Returns the account approved for `tokenId` token.
*
* Requirements:
*
* - `tokenId` must exist.
*/
function getApproved(uint256 tokenId) public view virtual override returns (address) {
if (!_exists(tokenId)) _revert(ApprovalQueryForNonexistentToken.selector);
return ERC721AStorage.layout()._tokenApprovals[tokenId].value;
}
/**
* @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 caller.
*
* Emits an {ApprovalForAll} event.
*/
function setApprovalForAll(address operator, bool approved) public virtual override {
ERC721AStorage.layout()._operatorApprovals[_msgSenderERC721A()][operator] = approved;
emit ApprovalForAll(_msgSenderERC721A(), operator, approved);
}
/**
* @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
*
* See {setApprovalForAll}.
*/
function isApprovedForAll(address owner, address operator) public view virtual override returns (bool) {
return ERC721AStorage.layout()._operatorApprovals[owner][operator];
}
/**
* @dev Returns whether `tokenId` exists.
*
* Tokens can be managed by their owner or approved accounts via {approve} or {setApprovalForAll}.
*
* Tokens start existing when they are minted. See {_mint}.
*/
function _exists(uint256 tokenId) internal view virtual returns (bool result) {
if (_startTokenId() <= tokenId) {
if (tokenId > _sequentialUpTo())
return _packedOwnershipExists(ERC721AStorage.layout()._packedOwnerships[tokenId]);
if (tokenId < ERC721AStorage.layout()._currentIndex) {
uint256 packed;
while ((packed = ERC721AStorage.layout()._packedOwnerships[tokenId]) == 0) --tokenId;
result = packed & _BITMASK_BURNED == 0;
}
}
}
/**
* @dev Returns whether `packed` represents a token that exists.
*/
function _packedOwnershipExists(uint256 packed) private pure returns (bool result) {
assembly {
// The following is equivalent to `owner != address(0) && burned == false`.
// Symbolically tested.
result := gt(and(packed, _BITMASK_ADDRESS), and(packed, _BITMASK_BURNED))
}
}
/**
* @dev Returns whether `msgSender` is equal to `approvedAddress` or `owner`.
*/
function _isSenderApprovedOrOwner(
address approvedAddress,
address owner,
address msgSender
) private pure returns (bool result) {
assembly {
// Mask `owner` to the lower 160 bits, in case the upper bits somehow aren't clean.
owner := and(owner, _BITMASK_ADDRESS)
// Mask `msgSender` to the lower 160 bits, in case the upper bits somehow aren't clean.
msgSender := and(msgSender, _BITMASK_ADDRESS)
// `msgSender == owner || msgSender == approvedAddress`.
result := or(eq(msgSender, owner), eq(msgSender, approvedAddress))
}
}
/**
* @dev Returns the storage slot and value for the approved address of `tokenId`.
*/
function _getApprovedSlotAndAddress(uint256 tokenId)
private
view
returns (uint256 approvedAddressSlot, address approvedAddress)
{
ERC721AStorage.TokenApprovalRef storage tokenApproval = ERC721AStorage.layout()._tokenApprovals[tokenId];
// The following is equivalent to `approvedAddress = _tokenApprovals[tokenId].value`.
assembly {
approvedAddressSlot := tokenApproval.slot
approvedAddress := sload(approvedAddressSlot)
}
}
// =============================================================
// TRANSFER OPERATIONS
// =============================================================
/**
* @dev Transfers `tokenId` from `from` to `to`.
*
* 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
) public payable virtual override {
uint256 prevOwnershipPacked = _packedOwnershipOf(tokenId);
// Mask `from` to the lower 160 bits, in case the upper bits somehow aren't clean.
from = address(uint160(uint256(uint160(from)) & _BITMASK_ADDRESS));
if (address(uint160(prevOwnershipPacked)) != from) _revert(TransferFromIncorrectOwner.selector);
(uint256 approvedAddressSlot, address approvedAddress) = _getApprovedSlotAndAddress(tokenId);
// The nested ifs save around 20+ gas over a compound boolean condition.
if (!_isSenderApprovedOrOwner(approvedAddress, from, _msgSenderERC721A()))
if (!isApprovedForAll(from, _msgSenderERC721A())) _revert(TransferCallerNotOwnerNorApproved.selector);
_beforeTokenTransfers(from, to, tokenId, 1);
// Clear approvals from the previous owner.
assembly {
if approvedAddress {
// This is equivalent to `delete _tokenApprovals[tokenId]`.
sstore(approvedAddressSlot, 0)
}
}
// Underflow of the sender's balance is impossible because we check for
// ownership above and the recipient's balance can't realistically overflow.
// Counter overflow is incredibly unrealistic as `tokenId` would have to be 2**256.
unchecked {
// We can directly increment and decrement the balances.
--ERC721AStorage.layout()._packedAddressData[from]; // Updates: `balance -= 1`.
++ERC721AStorage.layout()._packedAddressData[to]; // Updates: `balance += 1`.
// Updates:
// - `address` to the next owner.
// - `startTimestamp` to the timestamp of transfering.
// - `burned` to `false`.
// - `nextInitialized` to `true`.
ERC721AStorage.layout()._packedOwnerships[tokenId] = _packOwnershipData(
to,
_BITMASK_NEXT_INITIALIZED | _nextExtraData(from, to, prevOwnershipPacked)
);
// If the next slot may not have been initialized (i.e. `nextInitialized == false`) .
if (prevOwnershipPacked & _BITMASK_NEXT_INITIALIZED == 0) {
uint256 nextTokenId = tokenId + 1;
// If the next slot's address is zero and not burned (i.e. packed value is zero).
if (ERC721AStorage.layout()._packedOwnerships[nextTokenId] == 0) {
// If the next slot is within bounds.
if (nextTokenId != ERC721AStorage.layout()._currentIndex) {
// Initialize the next slot to maintain correctness for `ownerOf(tokenId + 1)`.
ERC721AStorage.layout()._packedOwnerships[nextTokenId] = prevOwnershipPacked;
}
}
}
}
// Mask `to` to the lower 160 bits, in case the upper bits somehow aren't clean.
uint256 toMasked = uint256(uint160(to)) & _BITMASK_ADDRESS;
assembly {
// Emit the `Transfer` event.
log4(
0, // Start of data (0, since no data).
0, // End of data (0, since no data).
_TRANSFER_EVENT_SIGNATURE, // Signature.
from, // `from`.
toMasked, // `to`.
tokenId // `tokenId`.
)
}
if (toMasked == 0) _revert(TransferToZeroAddress.selector);
_afterTokenTransfers(from, to, tokenId, 1);
}
/**
* @dev Equivalent to `safeTransferFrom(from, to, tokenId, '')`.
*/
function safeTransferFrom(
address from,
address to,
uint256 tokenId
) public payable virtual override {
safeTransferFrom(from, to, tokenId, '');
}
/**
* @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 memory _data
) public payable virtual override {
transferFrom(from, to, tokenId);
if (to.code.length != 0)
if (!_checkContractOnERC721Received(from, to, tokenId, _data)) {
_revert(TransferToNonERC721ReceiverImplementer.selector);
}
}
/**
* @dev Hook that is called before a set of serially-ordered token IDs
* are about to be transferred. This includes minting.
* And also called before burning one token.
*
* `startTokenId` - the first token ID to be transferred.
* `quantity` - the amount to be transferred.
*
* Calling conditions:
*
* - When `from` and `to` are both non-zero, `from`'s `tokenId` will be
* transferred to `to`.
* - When `from` is zero, `tokenId` will be minted for `to`.
* - When `to` is zero, `tokenId` will be burned by `from`.
* - `from` and `to` are never both zero.
*/
function _beforeTokenTransfers(
address from,
address to,
uint256 startTokenId,
uint256 quantity
) internal virtual {}
/**
* @dev Hook that is called after a set of serially-ordered token IDs
* have been transferred. This includes minting.
* And also called after one token has been burned.
*
* `startTokenId` - the first token ID to be transferred.
* `quantity` - the amount to be transferred.
*
* Calling conditions:
*
* - When `from` and `to` are both non-zero, `from`'s `tokenId` has been
* transferred to `to`.
* - When `from` is zero, `tokenId` has been minted for `to`.
* - When `to` is zero, `tokenId` has been burned by `from`.
* - `from` and `to` are never both zero.
*/
function _afterTokenTransfers(
address from,
address to,
uint256 startTokenId,
uint256 quantity
) internal virtual {}
/**
* @dev Private function to invoke {IERC721Receiver-onERC721Received} on a target contract.
*
* `from` - Previous owner of the given token ID.
* `to` - Target address that will receive the token.
* `tokenId` - Token ID to be transferred.
* `_data` - Optional data to send along with the call.
*
* Returns whether the call correctly returned the expected magic value.
*/
function _checkContractOnERC721Received(
address from,
address to,
uint256 tokenId,
bytes memory _data
) private returns (bool) {
try
ERC721A__IERC721ReceiverUpgradeable(to).onERC721Received(_msgSenderERC721A(), from, tokenId, _data)
returns (bytes4 retval) {
return retval == ERC721A__IERC721ReceiverUpgradeable(to).onERC721Received.selector;
} catch (bytes memory reason) {
if (reason.length == 0) {
_revert(TransferToNonERC721ReceiverImplementer.selector);
}
assembly {
revert(add(32, reason), mload(reason))
}
}
}
// =============================================================
// MINT OPERATIONS
// =============================================================
/**
* @dev Mints `quantity` tokens and transfers them to `to`.
*
* Requirements:
*
* - `to` cannot be the zero address.
* - `quantity` must be greater than 0.
*
* Emits a {Transfer} event for each mint.
*/
function _mint(address to, uint256 quantity) internal virtual {
uint256 startTokenId = ERC721AStorage.layout()._currentIndex;
if (quantity == 0) _revert(MintZeroQuantity.selector);
_beforeTokenTransfers(address(0), to, startTokenId, quantity);
// Overflows are incredibly unrealistic.
// `balance` and `numberMinted` have a maximum limit of 2**64.
// `tokenId` has a maximum limit of 2**256.
unchecked {
// Updates:
// - `address` to the owner.
// - `startTimestamp` to the timestamp of minting.
// - `burned` to `false`.
// - `nextInitialized` to `quantity == 1`.
ERC721AStorage.layout()._packedOwnerships[startTokenId] = _packOwnershipData(
to,
_nextInitializedFlag(quantity) | _nextExtraData(address(0), to, 0)
);
// Updates:
// - `balance += quantity`.
// - `numberMinted += quantity`.
//
// We can directly add to the `balance` and `numberMinted`.
ERC721AStorage.layout()._packedAddressData[to] += quantity * ((1 << _BITPOS_NUMBER_MINTED) | 1);
// Mask `to` to the lower 160 bits, in case the upper bits somehow aren't clean.
uint256 toMasked = uint256(uint160(to)) & _BITMASK_ADDRESS;
if (toMasked == 0) _revert(MintToZeroAddress.selector);
uint256 end = startTokenId + quantity;
uint256 tokenId = startTokenId;
if (end - 1 > _sequentialUpTo()) _revert(SequentialMintExceedsLimit.selector);
do {
assembly {
// Emit the `Transfer` event.
log4(
0, // Start of data (0, since no data).
0, // End of data (0, since no data).
_TRANSFER_EVENT_SIGNATURE, // Signature.
0, // `address(0)`.
toMasked, // `to`.
tokenId // `tokenId`.
)
}
// The `!=` check ensures that large values of `quantity`
// that overflows uint256 will make the loop run out of gas.
} while (++tokenId != end);
ERC721AStorage.layout()._currentIndex = end;
}
_afterTokenTransfers(address(0), to, startTokenId, quantity);
}
/**
* @dev Mints `quantity` tokens and transfers them to `to`.
*
* This function is intended for efficient minting only during contract creation.
*
* It emits only one {ConsecutiveTransfer} as defined in
* [ERC2309](https://eips.ethereum.org/EIPS/eip-2309),
* instead of a sequence of {Transfer} event(s).
*
* Calling this function outside of contract creation WILL make your contract
* non-compliant with the ERC721 standard.
* For full ERC721 compliance, substituting ERC721 {Transfer} event(s) with the ERC2309
* {ConsecutiveTransfer} event is only permissible during contract creation.
*
* Requirements:
*
* - `to` cannot be the zero address.
* - `quantity` must be greater than 0.
*
* Emits a {ConsecutiveTransfer} event.
*/
function _mintERC2309(address to, uint256 quantity) internal virtual {
uint256 startTokenId = ERC721AStorage.layout()._currentIndex;
if (to == address(0)) _revert(MintToZeroAddress.selector);
if (quantity == 0) _revert(MintZeroQuantity.selector);
if (quantity > _MAX_MINT_ERC2309_QUANTITY_LIMIT) _revert(MintERC2309QuantityExceedsLimit.selector);
_beforeTokenTransfers(address(0), to, startTokenId, quantity);
// Overflows are unrealistic due to the above check for `quantity` to be below the limit.
unchecked {
// Updates:
// - `balance += quantity`.
// - `numberMinted += quantity`.
//
// We can directly add to the `balance` and `numberMinted`.
ERC721AStorage.layout()._packedAddressData[to] += quantity * ((1 << _BITPOS_NUMBER_MINTED) | 1);
// Updates:
// - `address` to the owner.
// - `startTimestamp` to the timestamp of minting.
// - `burned` to `false`.
// - `nextInitialized` to `quantity == 1`.
ERC721AStorage.layout()._packedOwnerships[startTokenId] = _packOwnershipData(
to,
_nextInitializedFlag(quantity) | _nextExtraData(address(0), to, 0)
);
if (startTokenId + quantity - 1 > _sequentialUpTo()) _revert(SequentialMintExceedsLimit.selector);
emit ConsecutiveTransfer(startTokenId, startTokenId + quantity - 1, address(0), to);
ERC721AStorage.layout()._currentIndex = startTokenId + quantity;
}
_afterTokenTransfers(address(0), to, startTokenId, quantity);
}
/**
* @dev Safely mints `quantity` tokens and transfers them to `to`.
*
* Requirements:
*
* - If `to` refers to a smart contract, it must implement
* {IERC721Receiver-onERC721Received}, which is called for each safe transfer.
* - `quantity` must be greater than 0.
*
* See {_mint}.
*
* Emits a {Transfer} event for each mint.
*/
function _safeMint(
address to,
uint256 quantity,
bytes memory _data
) internal virtual {
_mint(to, quantity);
unchecked {
if (to.code.length != 0) {
uint256 end = ERC721AStorage.layout()._currentIndex;
uint256 index = end - quantity;
do {
if (!_checkContractOnERC721Received(address(0), to, index++, _data)) {
_revert(TransferToNonERC721ReceiverImplementer.selector);
}
} while (index < end);
// This prevents reentrancy to `_safeMint`.
// It does not prevent reentrancy to `_safeMintSpot`.
if (ERC721AStorage.layout()._currentIndex != end) revert();
}
}
}
/**
* @dev Equivalent to `_safeMint(to, quantity, '')`.
*/
function _safeMint(address to, uint256 quantity) internal virtual {
_safeMint(to, quantity, '');
}
/**
* @dev Mints a single token at `tokenId`.
*
* Note: A spot-minted `tokenId` that has been burned can be re-minted again.
*
* Requirements:
*
* - `to` cannot be the zero address.
* - `tokenId` must be greater than `_sequentialUpTo()`.
* - `tokenId` must not exist.
*
* Emits a {Transfer} event for each mint.
*/
function _mintSpot(address to, uint256 tokenId) internal virtual {
if (tokenId <= _sequentialUpTo()) _revert(SpotMintTokenIdTooSmall.selector);
uint256 prevOwnershipPacked = ERC721AStorage.layout()._packedOwnerships[tokenId];
if (_packedOwnershipExists(prevOwnershipPacked)) _revert(TokenAlreadyExists.selector);
_beforeTokenTransfers(address(0), to, tokenId, 1);
// Overflows are incredibly unrealistic.
// The `numberMinted` for `to` is incremented by 1, and has a max limit of 2**64 - 1.
// `_spotMinted` is incremented by 1, and has a max limit of 2**256 - 1.
unchecked {
// Updates:
// - `address` to the owner.
// - `startTimestamp` to the timestamp of minting.
// - `burned` to `false`.
// - `nextInitialized` to `true` (as `quantity == 1`).
ERC721AStorage.layout()._packedOwnerships[tokenId] = _packOwnershipData(
to,
_nextInitializedFlag(1) | _nextExtraData(address(0), to, prevOwnershipPacked)
);
// Updates:
// - `balance += 1`.
// - `numberMinted += 1`.
//
// We can directly add to the `balance` and `numberMinted`.
ERC721AStorage.layout()._packedAddressData[to] += (1 << _BITPOS_NUMBER_MINTED) | 1;
// Mask `to` to the lower 160 bits, in case the upper bits somehow aren't clean.
uint256 toMasked = uint256(uint160(to)) & _BITMASK_ADDRESS;
if (toMasked == 0) _revert(MintToZeroAddress.selector);
assembly {
// Emit the `Transfer` event.
log4(
0, // Start of data (0, since no data).
0, // End of data (0, since no data).
_TRANSFER_EVENT_SIGNATURE, // Signature.
0, // `address(0)`.
toMasked, // `to`.
tokenId // `tokenId`.
)
}
++ERC721AStorage.layout()._spotMinted;
}
_afterTokenTransfers(address(0), to, tokenId, 1);
}
/**
* @dev Safely mints a single token at `tokenId`.
*
* Note: A spot-minted `tokenId` that has been burned can be re-minted again.
*
* Requirements:
*
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}.
* - `tokenId` must be greater than `_sequentialUpTo()`.
* - `tokenId` must not exist.
*
* See {_mintSpot}.
*
* Emits a {Transfer} event.
*/
function _safeMintSpot(
address to,
uint256 tokenId,
bytes memory _data
) internal virtual {
_mintSpot(to, tokenId);
unchecked {
if (to.code.length != 0) {
uint256 currentSpotMinted = ERC721AStorage.layout()._spotMinted;
if (!_checkContractOnERC721Received(address(0), to, tokenId, _data)) {
_revert(TransferToNonERC721ReceiverImplementer.selector);
}
// This prevents reentrancy to `_safeMintSpot`.
// It does not prevent reentrancy to `_safeMint`.
if (ERC721AStorage.layout()._spotMinted != currentSpotMinted) revert();
}
}
}
/**
* @dev Equivalent to `_safeMintSpot(to, tokenId, '')`.
*/
function _safeMintSpot(address to, uint256 tokenId) internal virtual {
_safeMintSpot(to, tokenId, '');
}
// =============================================================
// APPROVAL OPERATIONS
// =============================================================
/**
* @dev Equivalent to `_approve(to, tokenId, false)`.
*/
function _approve(address to, uint256 tokenId) internal virtual {
_approve(to, tokenId, false);
}
/**
* @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:
*
* - `tokenId` must exist.
*
* Emits an {Approval} event.
*/
function _approve(
address to,
uint256 tokenId,
bool approvalCheck
) internal virtual {
address owner = ownerOf(tokenId);
if (approvalCheck && _msgSenderERC721A() != owner)
if (!isApprovedForAll(owner, _msgSenderERC721A())) {
_revert(ApprovalCallerNotOwnerNorApproved.selector);
}
ERC721AStorage.layout()._tokenApprovals[tokenId].value = to;
emit Approval(owner, to, tokenId);
}
// =============================================================
// BURN OPERATIONS
// =============================================================
/**
* @dev Equivalent to `_burn(tokenId, false)`.
*/
function _burn(uint256 tokenId) internal virtual {
_burn(tokenId, false);
}
/**
* @dev Destroys `tokenId`.
* The approval is cleared when the token is burned.
*
* Requirements:
*
* - `tokenId` must exist.
*
* Emits a {Transfer} event.
*/
function _burn(uint256 tokenId, bool approvalCheck) internal virtual {
uint256 prevOwnershipPacked = _packedOwnershipOf(tokenId);
address from = address(uint160(prevOwnershipPacked));
(uint256 approvedAddressSlot, address approvedAddress) = _getApprovedSlotAndAddress(tokenId);
if (approvalCheck) {
// The nested ifs save around 20+ gas over a compound boolean condition.
if (!_isSenderApprovedOrOwner(approvedAddress, from, _msgSenderERC721A()))
if (!isApprovedForAll(from, _msgSenderERC721A())) _revert(TransferCallerNotOwnerNorApproved.selector);
}
_beforeTokenTransfers(from, address(0), tokenId, 1);
// Clear approvals from the previous owner.
assembly {
if approvedAddress {
// This is equivalent to `delete _tokenApprovals[tokenId]`.
sstore(approvedAddressSlot, 0)
}
}
// Underflow of the sender's balance is impossible because we check for
// ownership above and the recipient's balance can't realistically overflow.
// Counter overflow is incredibly unrealistic as `tokenId` would have to be 2**256.
unchecked {
// Updates:
// - `balance -= 1`.
// - `numberBurned += 1`.
//
// We can directly decrement the balance, and increment the number burned.
// This is equivalent to `packed -= 1; packed += 1 << _BITPOS_NUMBER_BURNED;`.
ERC721AStorage.layout()._packedAddressData[from] += (1 << _BITPOS_NUMBER_BURNED) - 1;
// Updates:
// - `address` to the last owner.
// - `startTimestamp` to the timestamp of burning.
// - `burned` to `true`.
// - `nextInitialized` to `true`.
ERC721AStorage.layout()._packedOwnerships[tokenId] = _packOwnershipData(
from,
(_BITMASK_BURNED | _BITMASK_NEXT_INITIALIZED) | _nextExtraData(from, address(0), prevOwnershipPacked)
);
// If the next slot may not have been initialized (i.e. `nextInitialized == false`) .
if (prevOwnershipPacked & _BITMASK_NEXT_INITIALIZED == 0) {
uint256 nextTokenId = tokenId + 1;
// If the next slot's address is zero and not burned (i.e. packed value is zero).
if (ERC721AStorage.layout()._packedOwnerships[nextTokenId] == 0) {
// If the next slot is within bounds.
if (nextTokenId != ERC721AStorage.layout()._currentIndex) {
// Initialize the next slot to maintain correctness for `ownerOf(tokenId + 1)`.
ERC721AStorage.layout()._packedOwnerships[nextTokenId] = prevOwnershipPacked;
}
}
}
}
emit Transfer(from, address(0), tokenId);
_afterTokenTransfers(from, address(0), tokenId, 1);
// Overflow not possible, as `_burnCounter` cannot be exceed `_currentIndex + _spotMinted` times.
unchecked {
ERC721AStorage.layout()._burnCounter++;
}
}
// =============================================================
// EXTRA DATA OPERATIONS
// =============================================================
/**
* @dev Directly sets the extra data for the ownership data `index`.
*/
function _setExtraDataAt(uint256 index, uint24 extraData) internal virtual {
uint256 packed = ERC721AStorage.layout()._packedOwnerships[index];
if (packed == 0) _revert(OwnershipNotInitializedForExtraData.selector);
uint256 extraDataCasted;
// Cast `extraData` with assembly to avoid redundant masking.
assembly {
extraDataCasted := extraData
}
packed = (packed & _BITMASK_EXTRA_DATA_COMPLEMENT) | (extraDataCasted << _BITPOS_EXTRA_DATA);
ERC721AStorage.layout()._packedOwnerships[index] = packed;
}
/**
* @dev Called during each token transfer to set the 24bit `extraData` field.
* Intended to be overridden by the cosumer contract.
*
* `previousExtraData` - the value of `extraData` before transfer.
*
* Calling conditions:
*
* - When `from` and `to` are both non-zero, `from`'s `tokenId` will be
* transferred to `to`.
* - When `from` is zero, `tokenId` will be minted for `to`.
* - When `to` is zero, `tokenId` will be burned by `from`.
* - `from` and `to` are never both zero.
*/
function _extraData(
address from,
address to,
uint24 previousExtraData
) internal view virtual returns (uint24) {}
/**
* @dev Returns the next extra data for the packed ownership data.
* The returned result is shifted into position.
*/
function _nextExtraData(
address from,
address to,
uint256 prevOwnershipPacked
) private view returns (uint256) {
uint24 extraData = uint24(prevOwnershipPacked >> _BITPOS_EXTRA_DATA);
return uint256(_extraData(from, to, extraData)) << _BITPOS_EXTRA_DATA;
}
// =============================================================
// OTHER OPERATIONS
// =============================================================
/**
* @dev Returns the message sender (defaults to `msg.sender`).
*
* If you are writing GSN compatible contracts, you need to override this function.
*/
function _msgSenderERC721A() internal view virtual returns (address) {
return msg.sender;
}
/**
* @dev Converts a uint256 to its ASCII string decimal representation.
*/
function _toString(uint256 value) internal pure virtual returns (string memory str) {
assembly {
// The maximum value of a uint256 contains 78 digits (1 byte per digit), but
// we allocate 0xa0 bytes to keep the free memory pointer 32-byte word aligned.
// We will need 1 word for the trailing zeros padding, 1 word for the length,
// and 3 words for a maximum of 78 digits. Total: 5 * 0x20 = 0xa0.
let m := add(mload(0x40), 0xa0)
// Update the free memory pointer to allocate.
mstore(0x40, m)
// Assign the `str` to the end.
str := sub(m, 0x20)
// Zeroize the slot after the string.
mstore(str, 0)
// Cache the end of the memory to calculate the length later.
let end := str
// We write the string from rightmost digit to leftmost digit.
// The following is essentially a do-while loop that also handles the zero case.
// prettier-ignore
for { let temp := value } 1 {} {
str := sub(str, 1)
// Write the character to the pointer.
// The ASCII index of the '0' character is 48.
mstore8(str, add(48, mod(temp, 10)))
// Keep dividing `temp` until zero.
temp := div(temp, 10)
// prettier-ignore
if iszero(temp) { break }
}
let length := sub(end, str)
// Move the pointer 32 bytes leftwards to make room for the length.
str := sub(str, 0x20)
// Store the length.
mstore(str, length)
}
}
/**
* @dev For more efficient reverts.
*/
function _revert(bytes4 errorSelector) internal pure {
assembly {
mstore(0x00, errorSelector)
revert(0x00, 0x04)
}
}
}// SPDX-License-Identifier: MIT
// ERC721A Contracts v4.3.0
// Creator: Chiru Labs
pragma solidity ^0.8.4;
/**
* @dev Interface of ERC721A.
*/
interface IERC721AUpgradeable {
/**
* The caller must own the token or be an approved operator.
*/
error ApprovalCallerNotOwnerNorApproved();
/**
* The token does not exist.
*/
error ApprovalQueryForNonexistentToken();
/**
* Cannot query the balance for the zero address.
*/
error BalanceQueryForZeroAddress();
/**
* Cannot mint to the zero address.
*/
error MintToZeroAddress();
/**
* The quantity of tokens minted must be more than zero.
*/
error MintZeroQuantity();
/**
* The token does not exist.
*/
error OwnerQueryForNonexistentToken();
/**
* The caller must own the token or be an approved operator.
*/
error TransferCallerNotOwnerNorApproved();
/**
* The token must be owned by `from`.
*/
error TransferFromIncorrectOwner();
/**
* Cannot safely transfer to a contract that does not implement the
* ERC721Receiver interface.
*/
error TransferToNonERC721ReceiverImplementer();
/**
* Cannot transfer to the zero address.
*/
error TransferToZeroAddress();
/**
* The token does not exist.
*/
error URIQueryForNonexistentToken();
/**
* The `quantity` minted with ERC2309 exceeds the safety limit.
*/
error MintERC2309QuantityExceedsLimit();
/**
* The `extraData` cannot be set on an unintialized ownership slot.
*/
error OwnershipNotInitializedForExtraData();
/**
* `_sequentialUpTo()` must be greater than `_startTokenId()`.
*/
error SequentialUpToTooSmall();
/**
* The `tokenId` of a sequential mint exceeds `_sequentialUpTo()`.
*/
error SequentialMintExceedsLimit();
/**
* Spot minting requires a `tokenId` greater than `_sequentialUpTo()`.
*/
error SpotMintTokenIdTooSmall();
/**
* Cannot mint over a token that already exists.
*/
error TokenAlreadyExists();
/**
* The feature is not compatible with spot mints.
*/
error NotCompatibleWithSpotMints();
// =============================================================
// STRUCTS
// =============================================================
struct TokenOwnership {
// The address of the owner.
address addr;
// Stores the start time of ownership with minimal overhead for tokenomics.
uint64 startTimestamp;
// Whether the token has been burned.
bool burned;
// Arbitrary data similar to `startTimestamp` that can be set via {_extraData}.
uint24 extraData;
}
// =============================================================
// TOKEN COUNTERS
// =============================================================
/**
* @dev Returns the total number of tokens in existence.
* Burned tokens will reduce the count.
* To get the total number of tokens minted, please see {_totalMinted}.
*/
function totalSupply() external view returns (uint256);
// =============================================================
// IERC165
// =============================================================
/**
* @dev Returns true if this contract implements the interface defined by
* `interfaceId`. See the corresponding
* [EIP section](https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified)
* to learn more about how these ids are created.
*
* This function call must use less than 30000 gas.
*/
function supportsInterface(bytes4 interfaceId) external view returns (bool);
// =============================================================
// IERC721
// =============================================================
/**
* @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`,
* 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 be 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,
bytes calldata data
) external payable;
/**
* @dev Equivalent to `safeTransferFrom(from, to, tokenId, '')`.
*/
function safeTransferFrom(
address from,
address to,
uint256 tokenId
) external payable;
/**
* @dev Transfers `tokenId` from `from` to `to`.
*
* WARNING: Usage of this method is discouraged, use {safeTransferFrom}
* whenever possible.
*
* 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 payable;
/**
* @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 payable;
/**
* @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 caller.
*
* 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);
// =============================================================
// IERC721Metadata
// =============================================================
/**
* @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);
// =============================================================
// IERC2309
// =============================================================
/**
* @dev Emitted when tokens in `fromTokenId` to `toTokenId`
* (inclusive) is transferred from `from` to `to`, as defined in the
* [ERC2309](https://eips.ethereum.org/EIPS/eip-2309) standard.
*
* See {_mintERC2309} for more details.
*/
event ConsecutiveTransfer(uint256 indexed fromTokenId, uint256 toTokenId, address indexed from, address indexed to);
}{
"viaIR": true,
"evmVersion": "cancun",
"optimizer": {
"enabled": true,
"runs": 999999
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"address","name":"participationToken","type":"address"},{"internalType":"uint64","name":"maxTotalSupply","type":"uint64"},{"internalType":"uint64","name":"whitelistPrice","type":"uint64"},{"internalType":"uint64","name":"publicPrice","type":"uint64"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AccessControlBadConfirmation","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"bytes32","name":"neededRole","type":"bytes32"}],"name":"AccessControlUnauthorizedAccount","type":"error"},{"inputs":[{"internalType":"address","name":"target","type":"address"}],"name":"AddressEmptyCode","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"AddressInsufficientBalance","type":"error"},{"inputs":[],"name":"ApprovalCallerNotOwnerNorApproved","type":"error"},{"inputs":[],"name":"ApprovalQueryForNonexistentToken","type":"error"},{"inputs":[],"name":"BalanceQueryForZeroAddress","type":"error"},{"inputs":[{"internalType":"address","name":"implementation","type":"address"}],"name":"ERC1967InvalidImplementation","type":"error"},{"inputs":[],"name":"ERC1967NonPayable","type":"error"},{"inputs":[],"name":"FailedInnerCall","type":"error"},{"inputs":[],"name":"InvalidInitialization","type":"error"},{"inputs":[],"name":"MintERC2309QuantityExceedsLimit","type":"error"},{"inputs":[],"name":"MintToZeroAddress","type":"error"},{"inputs":[],"name":"MintZeroQuantity","type":"error"},{"inputs":[],"name":"NFTSale__DepositedAlready","type":"error"},{"inputs":[],"name":"NFTSale__IncorrectState","type":"error"},{"inputs":[],"name":"NFTSale__InvalidImplementation","type":"error"},{"inputs":[],"name":"NFTSale__InvalidTimestamp","type":"error"},{"inputs":[],"name":"NFTSale__InvalidValue","type":"error"},{"inputs":[],"name":"NFTSale__MerkleProofFailed","type":"error"},{"inputs":[],"name":"NFTSale__NothingToClaim","type":"error"},{"inputs":[],"name":"NFTSale__ParamsLengthMismatch","type":"error"},{"inputs":[],"name":"NFTSale__TotalSupplyExceeded","type":"error"},{"inputs":[],"name":"NotCompatibleWithSpotMints","type":"error"},{"inputs":[],"name":"NotInitializing","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"OwnableInvalidOwner","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"OwnableUnauthorizedAccount","type":"error"},{"inputs":[],"name":"OwnerQueryForNonexistentToken","type":"error"},{"inputs":[],"name":"OwnershipNotInitializedForExtraData","type":"error"},{"inputs":[],"name":"SequentialMintExceedsLimit","type":"error"},{"inputs":[],"name":"SequentialUpToTooSmall","type":"error"},{"inputs":[],"name":"SpotMintTokenIdTooSmall","type":"error"},{"inputs":[],"name":"TokenAlreadyExists","type":"error"},{"inputs":[],"name":"TransferCallerNotOwnerNorApproved","type":"error"},{"inputs":[],"name":"TransferFromIncorrectOwner","type":"error"},{"inputs":[],"name":"TransferToNonERC721ReceiverImplementer","type":"error"},{"inputs":[],"name":"TransferToZeroAddress","type":"error"},{"inputs":[],"name":"URIQueryForNonexistentToken","type":"error"},{"inputs":[],"name":"UUPSUnauthorizedCallContext","type":"error"},{"inputs":[{"internalType":"bytes32","name":"slot","type":"bytes32"}],"name":"UUPSUnsupportedProxiableUUID","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"approved","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"approved","type":"bool"}],"name":"ApprovalForAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"newBaseURI","type":"string"},{"indexed":true,"internalType":"address","name":"caller","type":"address"}],"name":"BaseURISet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"bool","name":"whitelist","type":"bool"},{"indexed":false,"internalType":"uint64","name":"publicAmount","type":"uint64"}],"name":"Claimed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"fromTokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"toTokenId","type":"uint256"},{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"}],"name":"ConsecutiveTransfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":true,"internalType":"bool","name":"whitelist","type":"bool"},{"indexed":false,"internalType":"uint64","name":"publicAmount","type":"uint64"}],"name":"Deposited","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint64","name":"version","type":"uint64"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"newReceiver","type":"address"},{"indexed":true,"internalType":"address","name":"caller","type":"address"}],"name":"ReceiverSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint64","name":"publicAmount","type":"uint64"}],"name":"Refunded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"previousAdminRole","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"newAdminRole","type":"bytes32"}],"name":"RoleAdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleGranted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleRevoked","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"newWhitelistRoot","type":"bytes32"},{"indexed":false,"internalType":"bytes32","name":"newPublicRoot","type":"bytes32"},{"indexed":false,"internalType":"bytes32","name":"newRefundRoot","type":"bytes32"},{"indexed":true,"internalType":"address","name":"caller","type":"address"}],"name":"RootsSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"implementation","type":"address"}],"name":"Upgraded","type":"event"},{"inputs":[],"name":"DEFAULT_ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_TOTAL_SUPPLY","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"NFT_SALE_STORAGE_LOCATION","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PUBLIC_PRICE","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"SBT_PARTICIPATION","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"UPGRADE_INTERFACE_VERSION","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"WHITELIST_PRICE","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"approve","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint64","name":"publicAllocationAmount","type":"uint64"},{"internalType":"bytes32[]","name":"proof","type":"bytes32[]"}],"name":"claim","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32[]","name":"proof","type":"bytes32[]"}],"name":"deposit","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"getIdsOfOwner","outputs":[{"internalType":"uint64[]","name":"ids","type":"uint64[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleAdmin","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getRoots","outputs":[{"internalType":"bytes32","name":"whitelistRoot","type":"bytes32"},{"internalType":"bytes32","name":"publicRoot","type":"bytes32"},{"internalType":"bytes32","name":"refundRoot","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getState","outputs":[{"internalType":"enum INFTSale.State","name":"currentState","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getStats","outputs":[{"internalType":"uint64","name":"whitelistDeposited","type":"uint64"},{"internalType":"uint64","name":"publicDeposited","type":"uint64"},{"internalType":"uint64","name":"whitelistClaimed","type":"uint64"},{"internalType":"uint64","name":"publicClaimed","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getTimestamps","outputs":[{"internalType":"uint32","name":"depositStart","type":"uint32"},{"internalType":"uint32","name":"depositEnd","type":"uint32"},{"internalType":"uint32","name":"claimStart","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"getUserData","outputs":[{"components":[{"internalType":"uint64","name":"whitelistDeposited","type":"uint64"},{"internalType":"uint64","name":"publicDeposited","type":"uint64"},{"internalType":"uint64","name":"whitelistClaimed","type":"uint64"},{"internalType":"uint64","name":"publicClaimed","type":"uint64"}],"internalType":"struct INFTSale.UserData","name":"userData","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"grantRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"hasRole","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"defaultAdmin","type":"address"},{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"symbol","type":"string"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"defaultOwner","type":"address"}],"name":"initializeV2","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"operator","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"to","type":"address[]"},{"internalType":"uint16[]","name":"amounts","type":"uint16[]"}],"name":"mintByAdmin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"proxiableUUID","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"receiver","outputs":[{"internalType":"address","name":"fundsReceiver","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint64","name":"amountToRefund","type":"uint64"},{"internalType":"bytes32[]","name":"proof","type":"bytes32[]"}],"name":"refund","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"callerConfirmation","type":"address"}],"name":"renounceRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"revokeRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bool","name":"approved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"newBaseURI","type":"string"}],"name":"setBaseURI","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newReceiver","type":"address"}],"name":"setReceiver","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"newWhitelistRoot","type":"bytes32"},{"internalType":"bytes32","name":"newPublicRoot","type":"bytes32"},{"internalType":"bytes32","name":"newRefundRoot","type":"bytes32"}],"name":"setRoots","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"newDepositStart","type":"uint32"},{"internalType":"uint32","name":"newDepositEnd","type":"uint32"},{"internalType":"uint32","name":"newClaimStart","type":"uint32"}],"name":"setTimestamps","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"tokenURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"result","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"transferFrom","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newImplementation","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"upgradeToAndCall","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"withdrawFunds","outputs":[],"stateMutability":"nonpayable","type":"function"}]Contract Creation Code
6101203461018857601f615d8c38819003918201601f19168301916001600160401b0383118484101761018c57808492608094604052833981010312610188578051906001600160a01b03821682036101885761005e602082016101a0565b610076606061006f604085016101a0565b93016101a0565b92306080525f516020615d6c5f395f51905f525460ff8160401c16610179576002600160401b03196001600160401b03821601610123575b5060a05260c05260e05261010052604051615bb790816101b582396080518181816111ff0152611937015260a051818181611ce401526123a4015260c051818181610d7d0152614e83015260e05181818161093601526121eb015261010051818181611d9e015281816124ad01526130560152f35b6001600160401b0319166001600160401b039081175f516020615d6c5f395f51905f52556040519081527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d290602090a15f6100ae565b63f92ee8a960e01b5f5260045ffd5b5f80fd5b634e487b7160e01b5f52604160045260245ffd5b51906001600160401b03821682036101885756fe60806040526004361015610011575f80fd5b5f3560e01c806301ffc9a71461032457806306fdde031461031f578063081812fc1461031a578063095ea7b314610315578063155dd5ee1461031057806317e7f2951461030b57806318160ddd146103065780631865c57d1461030157806323b872dd146102fc578063248a9ca3146102f757806329b6eca9146102f25780632f2ff15d146102ed57806333039d3d146102e857806335a5e06d146102e357806336568abe146102de57806342842e0e146102d9578063435399df146102d45780634f1ef286146102cf578063503f145f146102ca57806352d1902d146102c557806355f804b3146102c057806359abbfe4146102bb5780635c1db56f146102b65780635ff27f30146102b1578063611f3f10146102ac5780636299d5b6146102a75780636352211e146102a257806370a082311461029d578063715018a614610298578063718da7ee146102935780637b9d7d741461028e5780637feaaacc146102895780638da5cb5b14610284578063906571471461027f57806391d148541461027a5780639557bf4d1461027557806395d89b4114610270578063a217fddf1461026b578063a22cb46514610266578063ad3cb1cc14610261578063ae22b1311461025c578063b88d4fde14610257578063c59d484714610252578063c87b56dd1461024d578063d547741f14610248578063e985e9c514610243578063f2fde38b1461023e578063f7260d3e146102395763ffc9896b14610234575f80fd5b613569565b6134f9565b6134ae565b613419565b61339b565b613217565b613185565b613115565b612f4c565b612ecf565b612dc8565b612d86565b612c85565b612aae565b6129d8565b612721565b6126b1565b612620565b612116565b612027565b611f4d565b611f08565b611eae565b611dc2565b611d60565b611d08565b611c9a565b611c1f565b6119b5565b6118f2565b61146b565b611192565b610f2e565b610ed9565b610e4d565b610da1565b610d3f565b610cbc565b610b1a565b610aaa565b610a98565b6109d8565b61095a565b6108f8565b61087c565b6106cf565b6105fe565b6104aa565b610357565b7fffffffff0000000000000000000000000000000000000000000000000000000081160361035357565b5f80fd5b346103535760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103535760207fffffffff000000000000000000000000000000000000000000000000000000006004356103b581610329565b167f80d23b400000000000000000000000000000000000000000000000000000000081149081156103ec575b506040519015158152f35b7f7965db0b0000000000000000000000000000000000000000000000000000000081149150811561041f575b505f6103e1565b7f01ffc9a7000000000000000000000000000000000000000000000000000000009150145f610418565b5f91031261035357565b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f602080948051918291828752018686015e5f8582860101520116010190565b9060206104a7928181520190610453565b90565b34610353575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610353576040515f7f2569078dfb4b0305704d3008e7403993ae9601b85f7ae5e742de3de8f8011c425461050781613697565b80845290600181169081156105bc575060011461053f575b61053b8361052f818503826110c6565b60405191829182610496565b0390f35b7f2569078dfb4b0305704d3008e7403993ae9601b85f7ae5e742de3de8f8011c425f9081527f933ecf8acb7824b680a8d16f3ff3db8864228d986aa4c2ebab1eeb2703b4beb3939250905b8082106105a25750909150810160200161052f61051f565b91926001816020925483858801015201910190929161058a565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660208086019190915291151560051b8401909101915061052f905061051f565b346103535760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610353576004356106398161463f565b15610689575f527f2569078dfb4b0305704d3008e7403993ae9601b85f7ae5e742de3de8f8011c46602052602073ffffffffffffffffffffffffffffffffffffffff60405f205416604051908152f35b7fcf4700e4000000000000000000000000000000000000000000000000000000005f5260045ffd5b73ffffffffffffffffffffffffffffffffffffffff81160361035357565b60407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261035357600435610705816106b1565b6024359073ffffffffffffffffffffffffffffffffffffffff61072783614998565b16908133036107de575b73ffffffffffffffffffffffffffffffffffffffff90835f527f2569078dfb4b0305704d3008e7403993ae9601b85f7ae5e742de3de8f8011c466020526107b68160405f209073ffffffffffffffffffffffffffffffffffffffff167fffffffffffffffffffffffff0000000000000000000000000000000000000000825416179055565b16907f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9255f80a4005b60ff61084e3361082b8573ffffffffffffffffffffffffffffffffffffffff165f527f2569078dfb4b0305704d3008e7403993ae9601b85f7ae5e742de3de8f8011c4760205260405f2090565b9073ffffffffffffffffffffffffffffffffffffffff165f5260205260405f2090565b5416610731577fcfb3b942000000000000000000000000000000000000000000000000000000005f5260045ffd5b346103535760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610353576108f66004356108b96147eb565b73ffffffffffffffffffffffffffffffffffffffff7f707edf5e14bfdf1ba58ecef846c170a48a35301414f4b4056ce0592b3a7a1c01541661490f565b005b34610353575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261035357602060405167ffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b34610353575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103535760207f2569078dfb4b0305704d3008e7403993ae9601b85f7ae5e742de3de8f8011c40547f2569078dfb4b0305704d3008e7403993ae9601b85f7ae5e742de3de8f8011c41549003604051908152f35b34610353575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261035357610a0e6137fb565b6040516003821015610a21576020918152f35b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602160045260245ffd5b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc606091011261035357600435610a84816106b1565b90602435610a91816106b1565b9060443590565b6108f6610aa436610a4e565b9161386c565b346103535760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610353576020610b126004355f527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b626800602052600160405f20015490565b604051908152f35b346103535760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261035357600435610b55816106b1565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00549060ff8260401c168015610ca7575b610c7f577fffffffffffffffffffffffffffffffffffffffffffffff0000000000000000009190911668010000000000000002177ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0055610be590614af7565b610c507fffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffff7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0054167ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0055565b604051600281527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d290602090a1005b7ff92ee8a9000000000000000000000000000000000000000000000000000000005f5260045ffd5b50600267ffffffffffffffff83161015610b86565b346103535760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610353576108f6602435600435610cfd826106b1565b610d3a610d35825f527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b626800602052600160405f20015490565b614853565b614bf2565b34610353575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261035357602060405167ffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b34610353575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610353577f707edf5e14bfdf1ba58ecef846c170a48a35301414f4b4056ce0592b3a7a1c03547f707edf5e14bfdf1ba58ecef846c170a48a35301414f4b4056ce0592b3a7a1c04547f707edf5e14bfdf1ba58ecef846c170a48a35301414f4b4056ce0592b3a7a1c055460408051938452602084019290925290820152606090f35b346103535760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261035357600435602435610e8b816106b1565b3373ffffffffffffffffffffffffffffffffffffffff821603610eb1576108f691614d09565b7f6697b232000000000000000000000000000000000000000000000000000000005f5260045ffd5b6108f6610ee536610a4e565b9060405192610ef56020856110c6565b5f8452614499565b9181601f840112156103535782359167ffffffffffffffff8311610353576020808501948460051b01011161035357565b346103535760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103535760043567ffffffffffffffff811161035357610f7d903690600401610efd565b9060243567ffffffffffffffff811161035357610f9e903690600401610efd565b610fa99291926147eb565b808403611050575f92835b61ffff851680871115610fed57610fe791610fdb610fd6610fe1938787613be0565b613bf5565b90613c18565b94613b9b565b93610fb4565b5090610ffe61ffff87959316614e1e565b5f5b61ffff811690818611156108f65761104682611040611039610fd661103161102c61104b988d8c613be0565b613c2e565b938a89613be0565b61ffff1690565b90614ed2565b613b9b565b611000565b7fe2f8ab90000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b6080810190811067ffffffffffffffff8211176110c157604052565b611078565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff8211176110c157604052565b67ffffffffffffffff81116110c157601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200190565b92919261114d82611107565b9161115b60405193846110c6565b829481845281830111610353578281602093845f960137010152565b9080601f83011215610353578160206104a793359101611141565b60407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610353576004356111c8816106b1565b60243567ffffffffffffffff8111610353576111e8903690600401611177565b73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168030149081156113c6575b5061139e576112376147eb565b73ffffffffffffffffffffffffffffffffffffffff8216916040517f5ff27f30000000000000000000000000000000000000000000000000000000008152602081600481875afa90811561136a576112b9917f707edf5e14bfdf1ba58ecef846c170a48a35301414f4b4056ce0592b3a7a1c00915f9161136f575b50146150f8565b602060405180947f01ffc9a7000000000000000000000000000000000000000000000000000000008252818061131660048201907f7965db0b00000000000000000000000000000000000000000000000000000000602083019252565b03915afa92831561136a576108f693611336915f9161133b575b506150f8565b61513c565b61135d915060203d602011611363575b61135581836110c6565b810190615127565b5f611330565b503d61134b565b613e15565b611391915060203d602011611397575b61138981836110c6565b8101906150e9565b5f6112b2565b503d61137f565b7fe07c8dba000000000000000000000000000000000000000000000000000000005f5260045ffd5b905073ffffffffffffffffffffffffffffffffffffffff7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc541614155f61122a565b9060407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc8301126103535760043567ffffffffffffffff8116810361035357916024359067ffffffffffffffff82116103535761146791600401610efd565b9091565b346103535761147936611408565b90611482615372565b335f9081527f707edf5e14bfdf1ba58ecef846c170a48a35301414f4b4056ce0592b3a7a1c06602052604090208054909367ffffffffffffffff82169283158015906118ce575b6114d290613c38565b5f9480611807575b505050611555611505611568936114ff6115629467ffffffffffffffff9060801c1690565b90613c67565b67ffffffffffffffff851615801580916117f4575b61152390613c38565b6116da575b67ffffffffffffffff8116151595866115a4575b5061154f61154a8287613ca9565b614e1e565b84613ca9565b67ffffffffffffffff1690565b33614ed2565b60405167ffffffffffffffff91909116815233907fe92d85b79022e1534f6a5dd31dab0e27f003fc6c6d5a6057b45ae86fde6c5f1d90602090a3005b6116d4906116726115ea846115e57f707edf5e14bfdf1ba58ecef846c170a48a35301414f4b4056ce0592b3a7a1c025467ffffffffffffffff9060801c1690565b613ca9565b7fffffffffffffffff0000000000000000ffffffffffffffffffffffffffffffff77ffffffffffffffff000000000000000000000000000000007f707edf5e14bfdf1ba58ecef846c170a48a35301414f4b4056ce0592b3a7a1c02549260801b169116177f707edf5e14bfdf1ba58ecef846c170a48a35301414f4b4056ce0592b3a7a1c0255565b61168c836115e5835467ffffffffffffffff9060801c1690565b7fffffffffffffffff0000000000000000ffffffffffffffffffffffffffffffff77ffffffffffffffff0000000000000000000000000000000083549260801b169116179055565b5f61153c565b61179461170c866115e57f707edf5e14bfdf1ba58ecef846c170a48a35301414f4b4056ce0592b3a7a1c025460c01c90565b77ffffffffffffffffffffffffffffffffffffffffffffffff7fffffffffffffffff0000000000000000000000000000000000000000000000007f707edf5e14bfdf1ba58ecef846c170a48a35301414f4b4056ce0592b3a7a1c02549260c01b169116177f707edf5e14bfdf1ba58ecef846c170a48a35301414f4b4056ce0592b3a7a1c0255565b6117ef6117a6866115e5895460c01c90565b875477ffffffffffffffffffffffffffffffffffffffffffffffff1660c09190911b7fffffffffffffffff00000000000000000000000000000000000000000000000016178755565b611528565b5067ffffffffffffffff8216151561151a565b611562939550936114ff6118c4611555946118ba61150595611568997f707edf5e14bfdf1ba58ecef846c170a48a35301414f4b4056ce0592b3a7a1c045490604051926118b58461188988336020840190929167ffffffffffffffff60209173ffffffffffffffffffffffffffffffffffffffff604085019616845216910152565b037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081018652856110c6565b6153fd565b6114ff8960c01c90565b96945050936114da565b506114d26118e9604085901c67ffffffffffffffff16611555565b151590506114c9565b34610353575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103535773ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016300361139e5760206040517f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc8152f35b9181601f840112156103535782359167ffffffffffffffff8311610353576020838186019501011161035357565b346103535760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103535760043567ffffffffffffffff811161035357611a04903690600401611987565b611a0c6147eb565b67ffffffffffffffff81116110c157611a4e81611a497f707edf5e14bfdf1ba58ecef846c170a48a35301414f4b4056ce0592b3a7a1c0054613697565b61547d565b5f601f8211600114611afe5781611abd817f98d57c3f284cc158a0334fc376accadd65ddae6110b279c1fab292511230c65594611aee945f91611af3575b507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8260011b9260031b1c19161790565b7f707edf5e14bfdf1ba58ecef846c170a48a35301414f4b4056ce0592b3a7a1c00555b6040519182913395836155aa565b0390a2005b90508601355f611a8c565b7f707edf5e14bfdf1ba58ecef846c170a48a35301414f4b4056ce0592b3a7a1c005f527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08216907fad50d3b1a8f9a593754f2ce5b80ad3db496090d8a59a5edca0ec8bc841a2924a915f5b818110611c0757509183917f98d57c3f284cc158a0334fc376accadd65ddae6110b279c1fab292511230c65594611aee9410611bcf575b5050600181811b017f707edf5e14bfdf1ba58ecef846c170a48a35301414f4b4056ce0592b3a7a1c0055611ae0565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60f88460031b161c19908601351690555f80611ba0565b91926020600181928689013581550194019201611b69565b34610353575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103535760607f707edf5e14bfdf1ba58ecef846c170a48a35301414f4b4056ce0592b3a7a1c01546040519063ffffffff8160a01c16825263ffffffff8160c01c16602083015260e01c6040820152f35b34610353575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261035357602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b34610353575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103535760206040517f707edf5e14bfdf1ba58ecef846c170a48a35301414f4b4056ce0592b3a7a1c008152f35b34610353575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261035357602060405167ffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b346103535760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610353576004357fc1ff5e309bdd86f23cd0028c7b345bcdb3984b737495261606ad426b353d082c604435602435611e236147eb565b837f707edf5e14bfdf1ba58ecef846c170a48a35301414f4b4056ce0592b3a7a1c0355807f707edf5e14bfdf1ba58ecef846c170a48a35301414f4b4056ce0592b3a7a1c0455817f707edf5e14bfdf1ba58ecef846c170a48a35301414f4b4056ce0592b3a7a1c0555611aee6040519283923396846040919493926060820195825260208201520152565b346103535760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261035357602073ffffffffffffffffffffffffffffffffffffffff611eff600435614998565b16604051908152f35b346103535760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610353576020610b12600435611f48816106b1565b613ccb565b34610353575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261035357611f836155ef565b5f73ffffffffffffffffffffffffffffffffffffffff7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300547fffffffffffffffffffffffff000000000000000000000000000000000000000081167f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930055167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a3005b346103535760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103535773ffffffffffffffffffffffffffffffffffffffff600435612077816106b1565b61207f6147eb565b16807fffffffffffffffffffffffff00000000000000000000000000000000000000007f707edf5e14bfdf1ba58ecef846c170a48a35301414f4b4056ce0592b3a7a1c015416177f707edf5e14bfdf1ba58ecef846c170a48a35301414f4b4056ce0592b3a7a1c01556040519081527fc9c3685c43a110ec63da12acd71f195035fbe06238fc1b8989c122714c1d11eb60203392a2005b60207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103535760043567ffffffffffffffff811161035357612160903690600401610efd565b906121696153b4565b612174341515613d64565b335f9081527f707edf5e14bfdf1ba58ecef846c170a48a35301414f4b4056ce0592b3a7a1c0660205260408120831580159383918290612616575061225b612290916122566121cb865467ffffffffffffffff1690565b916121e067ffffffffffffffff841615613d93565b67ffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016996122188b341015613d64565b7f707edf5e14bfdf1ba58ecef846c170a48a35301414f4b4056ce0592b3a7a1c03546040805133602082015293919291906118b59085908101611889565b613c89565b839067ffffffffffffffff167fffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000825416179055565b61233b6122c86122567f707edf5e14bfdf1ba58ecef846c170a48a35301414f4b4056ce0592b3a7a1c025467ffffffffffffffff1690565b67ffffffffffffffff167fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000007f707edf5e14bfdf1ba58ecef846c170a48a35301414f4b4056ce0592b3a7a1c025416177f707edf5e14bfdf1ba58ecef846c170a48a35301414f4b4056ce0592b3a7a1c0255565b84803411612605575b505b80612495575b50506040805134815267ffffffffffffffff92909216602083015233917f13aca7d5b1c21ecefa8f16ba492ef2a173267e278b5753d0b3d0df30791f9fe89190a373ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016803b15610353576040517f6a627842000000000000000000000000000000000000000000000000000000008152336004820152905f908290602490829084905af1801561136a5761247b575b508061241857005b6108f69061247661245d7f707edf5e14bfdf1ba58ecef846c170a48a35301414f4b4056ce0592b3a7a1c015473ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff1690565b61490f565b806124895f61248f936110c6565b80610449565b5f612410565b6125419192506115556124e69167ffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016906124e16124db8383613dfc565b15613d64565b613e0b565b91612501836115e5835467ffffffffffffffff9060401c1690565b7fffffffffffffffffffffffffffffffff0000000000000000ffffffffffffffff6fffffffffffffffff000000000000000083549260401b169116179055565b6125fe61257e826115e57f707edf5e14bfdf1ba58ecef846c170a48a35301414f4b4056ce0592b3a7a1c025467ffffffffffffffff9060401c1690565b7fffffffffffffffffffffffffffffffff0000000000000000ffffffffffffffff6fffffffffffffffff00000000000000007f707edf5e14bfdf1ba58ecef846c170a48a35301414f4b4056ce0592b3a7a1c02549260401b169116177f707edf5e14bfdf1ba58ecef846c170a48a35301414f4b4056ce0592b3a7a1c0255565b5f8061234c565b612610915034613dc2565b5f612344565b9550505034612346565b346103535760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103535761266360043561265e816106b1565b613e9b565b6040518091602082016020835281518091526020604084019201905f5b81811061268e575050500390f35b825167ffffffffffffffff16845285945060209384019390920191600101612680565b34610353575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261035357602073ffffffffffffffffffffffffffffffffffffffff7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c1993005416604051908152f35b346103535760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103535760043561275c816106b1565b60243567ffffffffffffffff81116103535761277c903690600401611987565b60443567ffffffffffffffff81116103535761279c903690600401611987565b917fee151c8401928dc223602bb187aff91b9a56c7cae5476ef1b3287b085a16c85f549360ff8560081c165f146129ce57303b155b1561294a576127f7946127ec9060081c60ff161590565b1590565b958661286857613fbc565b6127fd57005b6108f67fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff7fee151c8401928dc223602bb187aff91b9a56c7cae5476ef1b3287b085a16c85f54167fee151c8401928dc223602bb187aff91b9a56c7cae5476ef1b3287b085a16c85f55565b6128d76101007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff7fee151c8401928dc223602bb187aff91b9a56c7cae5476ef1b3287b085a16c85f5416177fee151c8401928dc223602bb187aff91b9a56c7cae5476ef1b3287b085a16c85f55565b61294560017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff007fee151c8401928dc223602bb187aff91b9a56c7cae5476ef1b3287b085a16c85f5416177fee151c8401928dc223602bb187aff91b9a56c7cae5476ef1b3287b085a16c85f55565b613fbc565b60846040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603760248201527f455243373231415f5f496e697469616c697a61626c653a20636f6e747261637460448201527f20697320616c726561647920696e697469616c697a65640000000000000000006064820152fd5b60ff8516156127d1565b346103535760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261035357602060ff612a69602435600435612a1d826106b1565b5f527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b626800845260405f209073ffffffffffffffffffffffffffffffffffffffff165f5260205260405f2090565b54166040519015158152f35b6004359063ffffffff8216820361035357565b6024359063ffffffff8216820361035357565b6044359063ffffffff8216820361035357565b346103535760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610353576108f6612ae8612a75565b612af0612a88565b90612af9612a9b565b91612b026147eb565b7fffffffff0000000000000000ffffffffffffffffffffffffffffffffffffffff77ffffffff00000000000000000000000000000000000000007bffffffff0000000000000000000000000000000000000000000000007f707edf5e14bfdf1ba58ecef846c170a48a35301414f4b4056ce0592b3a7a1c01549363ffffffff8560a01c1615612c6f575b612bb463ffffffff8216612ba763ffffffff89168211614445565b63ffffffff891611614445565b60c01b169360a01b16911617177f707edf5e14bfdf1ba58ecef846c170a48a35301414f4b4056ce0592b3a7a1c01557bffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fffffffff000000000000000000000000000000000000000000000000000000007f707edf5e14bfdf1ba58ecef846c170a48a35301414f4b4056ce0592b3a7a1c01549260e01b169116177f707edf5e14bfdf1ba58ecef846c170a48a35301414f4b4056ce0592b3a7a1c0155565b612c804263ffffffff881611614445565b612b8c565b34610353575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610353576040515f7f2569078dfb4b0305704d3008e7403993ae9601b85f7ae5e742de3de8f8011c4354612ce281613697565b80845290600181169081156105bc5750600114612d095761053b8361052f818503826110c6565b7f2569078dfb4b0305704d3008e7403993ae9601b85f7ae5e742de3de8f8011c435f9081527f617167b76dcc8247761fd21f427ad8ec3be6b3be203aed34e3aac08b4d31817c939250905b808210612d6c5750909150810160200161052f61051f565b919260018160209254838588010152019101909291612d54565b34610353575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103535760206040515f8152f35b8015150361035357565b346103535760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261035357600435612e03816106b1565b73ffffffffffffffffffffffffffffffffffffffff60243591612e2583612dbe565b612e708161082b3373ffffffffffffffffffffffffffffffffffffffff165f527f2569078dfb4b0305704d3008e7403993ae9601b85f7ae5e742de3de8f8011c4760205260405f2090565b921515927fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0081541660ff851617905560405192835216907f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c3160203392a3005b34610353575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103535761053b604051612f0e6040826110c6565b600581527f352e302e300000000000000000000000000000000000000000000000000000006020820152604051918291602083526020830190610453565b34610353576108f667ffffffffffffffff613007612f6936611408565b9290612f73615372565b335f9081527f707edf5e14bfdf1ba58ecef846c170a48a35301414f4b4056ce0592b3a7a1c0660205260409020937f707edf5e14bfdf1ba58ecef846c170a48a35301414f4b4056ce0592b3a7a1c055490604051926118b58461188988336020840190929167ffffffffffffffff60209173ffffffffffffffffffffffffffffffffffffffff604085019616845216910152565b61308182549261304d8561303961303361302c8867ffffffffffffffff9060401c1690565b9760c01c90565b87613c67565b16868516811015908161310b575b50613c38565b6125018361307b7f000000000000000000000000000000000000000000000000000000000000000082614474565b95613c67565b6130c361257e826130be7f707edf5e14bfdf1ba58ecef846c170a48a35301414f4b4056ce0592b3a7a1c025467ffffffffffffffff9060401c1690565b613c67565b6040805167ffffffffffffffff848116825292909216602083015233917f3f59fd06eb9c0636a5eae031e65ba34fa18ab73b7c1718fa3984ab64b75d4a5e9190a2163361490f565b905015155f613047565b60807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103535760043561314b816106b1565b60243590613158826106b1565b6044356064359267ffffffffffffffff84116103535761317f6108f6943690600401611177565b92614499565b34610353575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103535760807f707edf5e14bfdf1ba58ecef846c170a48a35301414f4b4056ce0592b3a7a1c02546040519067ffffffffffffffff8116825267ffffffffffffffff8160401c16602083015267ffffffffffffffff81841c16604083015260c01c6060820152f35b346103535760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610353576004356132528161463f565b156133735761325f6136e8565b8051909190156133585760405160a08101604052600a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60808301935f8552935b01928181066030018453049182156132db577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600a916132a0565b61332692509261332c61052f92608061053b967fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08101920301815260405194859360208501906144f3565b906144f3565b037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081018352826110c6565b505061053b60405161336b6020826110c6565b5f815261052f565b7fa14c4b50000000000000000000000000000000000000000000000000000000005f5260045ffd5b346103535760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610353576108f66024356004356133dc826106b1565b613414610d35825f527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b626800602052600160405f20015490565b614d09565b346103535760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261035357602060ff612a6960043561345b816106b1565b61082b6024359161346b836106b1565b73ffffffffffffffffffffffffffffffffffffffff165f527f2569078dfb4b0305704d3008e7403993ae9601b85f7ae5e742de3de8f8011c4760205260405f2090565b346103535760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610353576108f66004356134ec816106b1565b6134f46155ef565b614552565b34610353575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261035357602073ffffffffffffffffffffffffffffffffffffffff7f707edf5e14bfdf1ba58ecef846c170a48a35301414f4b4056ce0592b3a7a1c015416604051908152f35b346103535760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103535761053b61360d6004356135aa816106b1565b5f60606040516135b9816110a5565b828152826020820152826040820152015273ffffffffffffffffffffffffffffffffffffffff165f527f707edf5e14bfdf1ba58ecef846c170a48a35301414f4b4056ce0592b3a7a1c0660205260405f2090565b6040519061361a826110a5565b5467ffffffffffffffff8116825267ffffffffffffffff8160401c16602083015267ffffffffffffffff8160801c16604083015260c01c606082015260405191829182919091606067ffffffffffffffff816080840195828151168552826020820151166020860152826040820151166040860152015116910152565b90600182811c921680156136de575b60208310146136b157565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602260045260245ffd5b91607f16916136a6565b604051905f827f707edf5e14bfdf1ba58ecef846c170a48a35301414f4b4056ce0592b3a7a1c00549161371a83613697565b80835292600181169081156137be5750600114613740575b61373e925003836110c6565b565b507f707edf5e14bfdf1ba58ecef846c170a48a35301414f4b4056ce0592b3a7a1c005f90815290917fad50d3b1a8f9a593754f2ce5b80ad3db496090d8a59a5edca0ec8bc841a2924a5b8183106137a257505090602061373e92820101613732565b602091935080600191548385890101520191019091849261378a565b6020925061373e9491507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001682840152151560051b820101613732565b7f707edf5e14bfdf1ba58ecef846c170a48a35301414f4b4056ce0592b3a7a1c015463ffffffff8160a01c1680156138665742101580613853575b61384d5760e01c421015613848575f90565b600290565b50600190565b5063ffffffff8160c01c16421115613836565b50505f90565b91909173ffffffffffffffffffffffffffffffffffffffff61388d83614998565b9116928373ffffffffffffffffffffffffffffffffffffffff831603613b69575f8381527f2569078dfb4b0305704d3008e7403993ae9601b85f7ae5e742de3de8f8011c46602052604090208054929061390373ffffffffffffffffffffffffffffffffffffffff871633908114908614171590565b613b52575b73ffffffffffffffffffffffffffffffffffffffff93613b49575b5061396b8573ffffffffffffffffffffffffffffffffffffffff165f527f2569078dfb4b0305704d3008e7403993ae9601b85f7ae5e742de3de8f8011c4560205260405f2090565b80547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190556139d88273ffffffffffffffffffffffffffffffffffffffff165f527f2569078dfb4b0305704d3008e7403993ae9601b85f7ae5e742de3de8f8011c4560205260405f2090565b8054600101905573ffffffffffffffffffffffffffffffffffffffff82164260a01b177c020000000000000000000000000000000000000000000000000000000017613a4b855f527f2569078dfb4b0305704d3008e7403993ae9601b85f7ae5e742de3de8f8011c4460205260405f2090565b557c0200000000000000000000000000000000000000000000000000000000811615613aa6575b501680927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef5f80a415613aa157565b61474b565b60018401613adb815f527f2569078dfb4b0305704d3008e7403993ae9601b85f7ae5e742de3de8f8011c4460205260405f2090565b5415613ae8575b50613a72565b7f2569078dfb4b0305704d3008e7403993ae9601b85f7ae5e742de3de8f8011c40548114613ae257613b41905f527f2569078dfb4b0305704d3008e7403993ae9601b85f7ae5e742de3de8f8011c4460205260405f2090565b555f80613ae2565b5f90555f613923565b613b5f6127e83388614505565b1561390857614723565b6146fb565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b61ffff1661ffff8114613bae5760010190565b613b6e565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b9190811015613bf05760051b0190565b613bb3565b3561ffff811681036103535790565b61ffff60019116019061ffff8211613bae57565b9061ffff8091169116019061ffff8211613bae57565b356104a7816106b1565b15613c3f57565b7f4cfe0a87000000000000000000000000000000000000000000000000000000005f5260045ffd5b9067ffffffffffffffff8091169116039067ffffffffffffffff8211613bae57565b67ffffffffffffffff60019116019067ffffffffffffffff8211613bae57565b9067ffffffffffffffff8091169116019067ffffffffffffffff8211613bae57565b73ffffffffffffffffffffffffffffffffffffffff811615613d3c57613d3767ffffffffffffffff9173ffffffffffffffffffffffffffffffffffffffff165f527f2569078dfb4b0305704d3008e7403993ae9601b85f7ae5e742de3de8f8011c4560205260405f2090565b541690565b7f8f4eb604000000000000000000000000000000000000000000000000000000005f5260045ffd5b15613d6b57565b7fff510849000000000000000000000000000000000000000000000000000000005f5260045ffd5b15613d9a57565b7f7d7a2ae4000000000000000000000000000000000000000000000000000000005f5260045ffd5b91908203918211613bae57565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b8115613e06570690565b613dcf565b8115613e06570490565b6040513d5f823e3d90fd5b67ffffffffffffffff81116110c15760051b60200190565b90613e4282613e20565b613e4f60405191826110c6565b8281527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0613e7d8294613e20565b0190602036910137565b8051821015613bf05760209160051b010190565b613ea481613ccb565b8015613f9f57613eb381613e38565b905f5f613f027f2569078dfb4b0305704d3008e7403993ae9601b85f7ae5e742de3de8f8011c40547f2569078dfb4b0305704d3008e7403993ae9601b85f7ae5e742de3de8f8011c4154900390565b905b61ffff811680831115613f9557613f2061245d61245d83614998565b73ffffffffffffffffffffffffffffffffffffffff808916911614613f4f575b50613f4a90613b9b565b613f04565b92613f75613f7a9294613f6661ffff841689613e87565b9067ffffffffffffffff169052565b613c04565b918361ffff841614613f8c575f613f40565b50505050905090565b5050505050919050565b5050604051613faf6020826110c6565b5f81525f36602083013790565b929093917ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00549467ffffffffffffffff61400d613fff60ff8960401c1615151590565b9767ffffffffffffffff1690565b16801590816141d8575b60011490816141ce575b1590816141c5575b50610c7f576140aa94866140a160017fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000007ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a005416177ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0055565b61414a576141e0565b6140b057565b61411b7fffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffff7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0054167ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0055565b604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d290602090a1565b6141c0680100000000000000007fffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffff7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a005416177ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0055565b6141e0565b9050155f614029565b303b159150614021565b879150614017565b92906142049061420c939695966141f5615882565b6141fd615882565b3691611141565b943691611141565b61424560ff7fee151c8401928dc223602bb187aff91b9a56c7cae5476ef1b3287b085a16c85f5460081c166142408161565b565b61565b565b835167ffffffffffffffff81116110c157614289816142847f2569078dfb4b0305704d3008e7403993ae9601b85f7ae5e742de3de8f8011c4254613697565b6154ec565b6020601f8211600114614340578161430593926142de9261433297985f92614335575b50507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8260011b9260031b1c19161790565b7f2569078dfb4b0305704d3008e7403993ae9601b85f7ae5e742de3de8f8011c425561592a565b61432d5f7f2569078dfb4b0305704d3008e7403993ae9601b85f7ae5e742de3de8f8011c4055565b614b0b565b50565b015190505f806142ac565b7f2569078dfb4b0305704d3008e7403993ae9601b85f7ae5e742de3de8f8011c425f527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08216957f933ecf8acb7824b680a8d16f3ff3db8864228d986aa4c2ebab1eeb2703b4beb3965f5b81811061442d57509660019284926143059695614332999a106143f6575b505050811b017f2569078dfb4b0305704d3008e7403993ae9601b85f7ae5e742de3de8f8011c425561592a565b01517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60f88460031b161c191690555f80806143c9565b838301518955600190980197602093840193016143ab565b1561444c57565b7f6bb1eaf4000000000000000000000000000000000000000000000000000000005f5260045ffd5b9067ffffffffffffffff8091169116029067ffffffffffffffff8216918203613bae57565b9291906144a782828661386c565b803b6144b4575b50505050565b6144bd9361582c565b156144cb575f8080806144ae565b7fd1a57ed6000000000000000000000000000000000000000000000000000000005f5260045ffd5b805191908290602001825e015f815290565b60ff9161082b613d379273ffffffffffffffffffffffffffffffffffffffff165f527f2569078dfb4b0305704d3008e7403993ae9601b85f7ae5e742de3de8f8011c4760205260405f2090565b73ffffffffffffffffffffffffffffffffffffffff1680156146135773ffffffffffffffffffffffffffffffffffffffff7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930054827fffffffffffffffffffffffff00000000000000000000000000000000000000008216177f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930055167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a3565b7f1e4fbdf7000000000000000000000000000000000000000000000000000000005f525f60045260245ffd5b905f917f2569078dfb4b0305704d3008e7403993ae9601b85f7ae5e742de3de8f8011c4054811061466d5750565b9091505b6146a2815f527f2569078dfb4b0305704d3008e7403993ae9601b85f7ae5e742de3de8f8011c4460205260405f2090565b54806146d657508015613bae577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01614671565b7c01000000000000000000000000000000000000000000000000000000009150161590565b7fa1148100000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f59c896be000000000000000000000000000000000000000000000000000000005f5260045ffd5b7fea553b34000000000000000000000000000000000000000000000000000000005f5260045ffd5b7fdf2d9b42000000000000000000000000000000000000000000000000000000005f5260045ffd5b7fb562e8dd000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f2e076300000000000000000000000000000000000000000000000000000000005f5260045ffd5b335f9081527fb7db2dd08fcb62d0c9e08c51941cae53c267786a0b75803fb7960902fc8ef97d602052604090205460ff161561482357565b7fe2517d3f000000000000000000000000000000000000000000000000000000005f52336004525f60245260445ffd5b805f527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b62680060205260ff6148a73360405f209073ffffffffffffffffffffffffffffffffffffffff165f5260205260405f2090565b5416156148b15750565b7fe2517d3f000000000000000000000000000000000000000000000000000000005f523360045260245260445ffd5b3d1561490a573d906148f182611107565b916148ff60405193846110c6565b82523d5f602084013e565b606090565b81471061496c575f80809373ffffffffffffffffffffffffffffffffffffffff8294165af161493c6148e0565b501561494457565b7f1425ea42000000000000000000000000000000000000000000000000000000005f5260045ffd5b7fcd786059000000000000000000000000000000000000000000000000000000005f523060045260245ffd5b6149c9815f527f2569078dfb4b0305704d3008e7403993ae9601b85f7ae5e742de3de8f8011c4460205260405f2090565b549081156149f957507c010000000000000000000000000000000000000000000000000000000081166147735790565b90507f2569078dfb4b0305704d3008e7403993ae9601b85f7ae5e742de3de8f8011c4054811015614773577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff905b01614a79815f527f2569078dfb4b0305704d3008e7403993ae9601b85f7ae5e742de3de8f8011c4460205260405f2090565b54908115614acf57507c01000000000000000000000000000000000000000000000000000000008116156104a7577fdf2d9b42000000000000000000000000000000000000000000000000000000005f5260045ffd5b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9150614a47565b61373e90614b03615882565b6134f4615882565b73ffffffffffffffffffffffffffffffffffffffff81165f9081527fb7db2dd08fcb62d0c9e08c51941cae53c267786a0b75803fb7960902fc8ef97d602052604090205460ff16614bed5773ffffffffffffffffffffffffffffffffffffffff165f8181527fb7db2dd08fcb62d0c9e08c51941cae53c267786a0b75803fb7960902fc8ef97d6020526040812080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790553391907f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d8180a4600190565b505f90565b805f527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b62680060205260ff614c468360405f209073ffffffffffffffffffffffffffffffffffffffff165f5260205260405f2090565b541661386657805f527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b626800602052614c9e8260405f209073ffffffffffffffffffffffffffffffffffffffff165f5260205260405f2090565b60017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0082541617905573ffffffffffffffffffffffffffffffffffffffff339216907f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d5f80a4600190565b805f527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b62680060205260ff614d5d8360405f209073ffffffffffffffffffffffffffffffffffffffff165f5260205260405f2090565b54161561386657805f527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b626800602052614db68260405f209073ffffffffffffffffffffffffffffffffffffffff165f5260205260405f2090565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00815416905573ffffffffffffffffffffffffffffffffffffffff339216907ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b5f80a4600190565b67ffffffffffffffff7f2569078dfb4b0305704d3008e7403993ae9601b85f7ae5e742de3de8f8011c40547f2569078dfb4b0305704d3008e7403993ae9601b85f7ae5e742de3de8f8011c4154900391168101809111613bae5767ffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001610614eaa57565b7f1407d9f5000000000000000000000000000000000000000000000000000000005f5260045ffd5b9160405190614ee26020836110c6565b5f82527f2569078dfb4b0305704d3008e7403993ae9601b85f7ae5e742de3de8f8011c40549183156150e45773ffffffffffffffffffffffffffffffffffffffff85164260a01b6001861460e11b1717614f63845f527f2569078dfb4b0305704d3008e7403993ae9601b85f7ae5e742de3de8f8011c4460205260405f2090565b55614fab8573ffffffffffffffffffffffffffffffffffffffff165f527f2569078dfb4b0305704d3008e7403993ae9601b85f7ae5e742de3de8f8011c4560205260405f2090565b680100000000000000018502815401905573ffffffffffffffffffffffffffffffffffffffff85169384156150df57808401939560015b15615015575b5f87875f7fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8180a4614fe2565b9560010195848703614fe8579295509261504f9194507f2569078dfb4b0305704d3008e7403993ae9601b85f7ae5e742de3de8f8011c4055565b803b61505a57505050565b9091927f2569078dfb4b0305704d3008e7403993ae9601b85f7ae5e742de3de8f8011c40549283039260015b156150aa575b5f61509f6127e887600188019787615736565b15615086575b6144cb565b80841061508c5792509250507f2569078dfb4b0305704d3008e7403993ae9601b85f7ae5e742de3de8f8011c40540361035357565b6147c3565b61479b565b90816020910312610353575190565b156150ff57565b7f4a2c7f44000000000000000000000000000000000000000000000000000000005f5260045ffd5b9081602091031261035357516104a781612dbe565b906040517f52d1902d00000000000000000000000000000000000000000000000000000000815260208160048173ffffffffffffffffffffffffffffffffffffffff87165afa5f9181615351575b506151d1577f4c9c8ce3000000000000000000000000000000000000000000000000000000005f5273ffffffffffffffffffffffffffffffffffffffff831660045260245ffd5b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc81036153265750813b156152e45773ffffffffffffffffffffffffffffffffffffffff8216807fffffffffffffffffffffffff00000000000000000000000000000000000000007f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5416177f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc557fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b5f80a28051156152b35761433291615af8565b5050346152bc57565b7fb398979f000000000000000000000000000000000000000000000000000000005f5260045ffd5b73ffffffffffffffffffffffffffffffffffffffff827f4c9c8ce3000000000000000000000000000000000000000000000000000000005f521660045260245ffd5b7faa1d49a4000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b61536b91925060203d6020116113975761138981836110c6565b905f61518a565b61537a6137fb565b6003811015610a215760020361538c57565b7fa8766b20000000000000000000000000000000000000000000000000000000005f5260045ffd5b6153bc6137fb565b6003811015610a215760010361538c57565b156153d557565b7f5bd1f4a0000000000000000000000000000000000000000000000000000000005f5260045ffd5b9260208151910120604051602081019182526020815261541e6040826110c6565b5190209261542b82613e20565b9161543960405193846110c6565b808352602083019060051b82019136831161035357905b82821061546d57505050916154689161373e936158d9565b6153ce565b8135815260209182019101615450565b601f8111615489575050565b7f707edf5e14bfdf1ba58ecef846c170a48a35301414f4b4056ce0592b3a7a1c005f5260205f20906020601f840160051c830193106154e2575b601f0160051c01905b8181106154d7575050565b5f81556001016154cc565b90915081906154c3565b601f81116154f8575050565b7f2569078dfb4b0305704d3008e7403993ae9601b85f7ae5e742de3de8f8011c425f5260205f20906020601f840160051c83019310615551575b601f0160051c01905b818110615546575050565b5f815560010161553b565b9091508190615532565b601f821161556857505050565b5f5260205f20906020601f840160051c830193106155a0575b601f0160051c01905b818110615595575050565b5f815560010161558a565b9091508190615581565b90601f836040947fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09360208652816020870152868601375f8582860101520116010190565b73ffffffffffffffffffffffffffffffffffffffff7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c1993005416330361562f57565b7f118cdaa7000000000000000000000000000000000000000000000000000000005f523360045260245ffd5b1561566257565b60846040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603460248201527f455243373231415f5f496e697469616c697a61626c653a20636f6e747261637460448201527f206973206e6f7420696e697469616c697a696e670000000000000000000000006064820152fd5b9081602091031261035357516104a781610329565b909273ffffffffffffffffffffffffffffffffffffffff608093816104a7979616845216602083015260408201528160608201520190610453565b9161578b926020925f73ffffffffffffffffffffffffffffffffffffffff6040518097819682957f150b7a020000000000000000000000000000000000000000000000000000000084528533600486016156fb565b0393165af15f91816157fb575b506157b4576157a56148e0565b8051156150a557805190602001fd5b7fffffffff00000000000000000000000000000000000000000000000000000000167f150b7a02000000000000000000000000000000000000000000000000000000001490565b61581e91925060203d602011615825575b61581681836110c6565b8101906156e6565b905f615798565b503d61580c565b906020925f73ffffffffffffffffffffffffffffffffffffffff61578b9694604051978896879586937f150b7a0200000000000000000000000000000000000000000000000000000000855233600486016156fb565b60ff7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a005460401c16156158b157565b7fd7e6bcf8000000000000000000000000000000000000000000000000000000005f5260045ffd5b929091905f915b8451831015615922576158f38386613e87565b519081811015615911575f52602052600160405f205b9201916158e0565b905f52602052600160405f20615909565b915092501490565b90815167ffffffffffffffff81116110c1576159908161596a7f2569078dfb4b0305704d3008e7403993ae9601b85f7ae5e742de3de8f8011c4354613697565b7f2569078dfb4b0305704d3008e7403993ae9601b85f7ae5e742de3de8f8011c4361555b565b602092601f8211600114615a01576159dd929382915f926143355750507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8260011b9260031b1c19161790565b7f2569078dfb4b0305704d3008e7403993ae9601b85f7ae5e742de3de8f8011c4355565b7f2569078dfb4b0305704d3008e7403993ae9601b85f7ae5e742de3de8f8011c435f527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08216937f617167b76dcc8247761fd21f427ad8ec3be6b3be203aed34e3aac08b4d31817c915f5b868110615ae05750836001959610615aa9575b505050811b017f2569078dfb4b0305704d3008e7403993ae9601b85f7ae5e742de3de8f8011c4355565b01517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60f88460031b161c191690555f8080615a7f565b91926020600181928685015181550194019201615a6c565b5f806104a793602081519101845af4615b0f6148e0565b9190615b25575080511561494457805190602001fd5b81511580615b78575b615b36575090565b73ffffffffffffffffffffffffffffffffffffffff907f9996b315000000000000000000000000000000000000000000000000000000005f521660045260245ffd5b50803b15615b2e56fea2646970667358221220eff6132e62a14d1616f91214eed74fcb1b98b88b7e12b96f07b56135492fe58864736f6c634300081c0033f0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0000000000000000000000000016a7b20cf490fff80ee08a064cfe7c093b0b132100000000000000000000000000000000000000000000000000000000000003e8000000000000000000000000000000000000000000000000006a94d74f430000000000000000000000000000000000000000000000000000006a94d74f430000
Deployed Bytecode
0x60806040526004361015610011575f80fd5b5f3560e01c806301ffc9a71461032457806306fdde031461031f578063081812fc1461031a578063095ea7b314610315578063155dd5ee1461031057806317e7f2951461030b57806318160ddd146103065780631865c57d1461030157806323b872dd146102fc578063248a9ca3146102f757806329b6eca9146102f25780632f2ff15d146102ed57806333039d3d146102e857806335a5e06d146102e357806336568abe146102de57806342842e0e146102d9578063435399df146102d45780634f1ef286146102cf578063503f145f146102ca57806352d1902d146102c557806355f804b3146102c057806359abbfe4146102bb5780635c1db56f146102b65780635ff27f30146102b1578063611f3f10146102ac5780636299d5b6146102a75780636352211e146102a257806370a082311461029d578063715018a614610298578063718da7ee146102935780637b9d7d741461028e5780637feaaacc146102895780638da5cb5b14610284578063906571471461027f57806391d148541461027a5780639557bf4d1461027557806395d89b4114610270578063a217fddf1461026b578063a22cb46514610266578063ad3cb1cc14610261578063ae22b1311461025c578063b88d4fde14610257578063c59d484714610252578063c87b56dd1461024d578063d547741f14610248578063e985e9c514610243578063f2fde38b1461023e578063f7260d3e146102395763ffc9896b14610234575f80fd5b613569565b6134f9565b6134ae565b613419565b61339b565b613217565b613185565b613115565b612f4c565b612ecf565b612dc8565b612d86565b612c85565b612aae565b6129d8565b612721565b6126b1565b612620565b612116565b612027565b611f4d565b611f08565b611eae565b611dc2565b611d60565b611d08565b611c9a565b611c1f565b6119b5565b6118f2565b61146b565b611192565b610f2e565b610ed9565b610e4d565b610da1565b610d3f565b610cbc565b610b1a565b610aaa565b610a98565b6109d8565b61095a565b6108f8565b61087c565b6106cf565b6105fe565b6104aa565b610357565b7fffffffff0000000000000000000000000000000000000000000000000000000081160361035357565b5f80fd5b346103535760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103535760207fffffffff000000000000000000000000000000000000000000000000000000006004356103b581610329565b167f80d23b400000000000000000000000000000000000000000000000000000000081149081156103ec575b506040519015158152f35b7f7965db0b0000000000000000000000000000000000000000000000000000000081149150811561041f575b505f6103e1565b7f01ffc9a7000000000000000000000000000000000000000000000000000000009150145f610418565b5f91031261035357565b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f602080948051918291828752018686015e5f8582860101520116010190565b9060206104a7928181520190610453565b90565b34610353575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610353576040515f7f2569078dfb4b0305704d3008e7403993ae9601b85f7ae5e742de3de8f8011c425461050781613697565b80845290600181169081156105bc575060011461053f575b61053b8361052f818503826110c6565b60405191829182610496565b0390f35b7f2569078dfb4b0305704d3008e7403993ae9601b85f7ae5e742de3de8f8011c425f9081527f933ecf8acb7824b680a8d16f3ff3db8864228d986aa4c2ebab1eeb2703b4beb3939250905b8082106105a25750909150810160200161052f61051f565b91926001816020925483858801015201910190929161058a565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660208086019190915291151560051b8401909101915061052f905061051f565b346103535760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610353576004356106398161463f565b15610689575f527f2569078dfb4b0305704d3008e7403993ae9601b85f7ae5e742de3de8f8011c46602052602073ffffffffffffffffffffffffffffffffffffffff60405f205416604051908152f35b7fcf4700e4000000000000000000000000000000000000000000000000000000005f5260045ffd5b73ffffffffffffffffffffffffffffffffffffffff81160361035357565b60407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261035357600435610705816106b1565b6024359073ffffffffffffffffffffffffffffffffffffffff61072783614998565b16908133036107de575b73ffffffffffffffffffffffffffffffffffffffff90835f527f2569078dfb4b0305704d3008e7403993ae9601b85f7ae5e742de3de8f8011c466020526107b68160405f209073ffffffffffffffffffffffffffffffffffffffff167fffffffffffffffffffffffff0000000000000000000000000000000000000000825416179055565b16907f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9255f80a4005b60ff61084e3361082b8573ffffffffffffffffffffffffffffffffffffffff165f527f2569078dfb4b0305704d3008e7403993ae9601b85f7ae5e742de3de8f8011c4760205260405f2090565b9073ffffffffffffffffffffffffffffffffffffffff165f5260205260405f2090565b5416610731577fcfb3b942000000000000000000000000000000000000000000000000000000005f5260045ffd5b346103535760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610353576108f66004356108b96147eb565b73ffffffffffffffffffffffffffffffffffffffff7f707edf5e14bfdf1ba58ecef846c170a48a35301414f4b4056ce0592b3a7a1c01541661490f565b005b34610353575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261035357602060405167ffffffffffffffff7f000000000000000000000000000000000000000000000000006a94d74f430000168152f35b34610353575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103535760207f2569078dfb4b0305704d3008e7403993ae9601b85f7ae5e742de3de8f8011c40547f2569078dfb4b0305704d3008e7403993ae9601b85f7ae5e742de3de8f8011c41549003604051908152f35b34610353575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261035357610a0e6137fb565b6040516003821015610a21576020918152f35b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602160045260245ffd5b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc606091011261035357600435610a84816106b1565b90602435610a91816106b1565b9060443590565b6108f6610aa436610a4e565b9161386c565b346103535760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610353576020610b126004355f527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b626800602052600160405f20015490565b604051908152f35b346103535760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261035357600435610b55816106b1565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00549060ff8260401c168015610ca7575b610c7f577fffffffffffffffffffffffffffffffffffffffffffffff0000000000000000009190911668010000000000000002177ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0055610be590614af7565b610c507fffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffff7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0054167ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0055565b604051600281527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d290602090a1005b7ff92ee8a9000000000000000000000000000000000000000000000000000000005f5260045ffd5b50600267ffffffffffffffff83161015610b86565b346103535760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610353576108f6602435600435610cfd826106b1565b610d3a610d35825f527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b626800602052600160405f20015490565b614853565b614bf2565b34610353575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261035357602060405167ffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000003e8168152f35b34610353575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610353577f707edf5e14bfdf1ba58ecef846c170a48a35301414f4b4056ce0592b3a7a1c03547f707edf5e14bfdf1ba58ecef846c170a48a35301414f4b4056ce0592b3a7a1c04547f707edf5e14bfdf1ba58ecef846c170a48a35301414f4b4056ce0592b3a7a1c055460408051938452602084019290925290820152606090f35b346103535760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261035357600435602435610e8b816106b1565b3373ffffffffffffffffffffffffffffffffffffffff821603610eb1576108f691614d09565b7f6697b232000000000000000000000000000000000000000000000000000000005f5260045ffd5b6108f6610ee536610a4e565b9060405192610ef56020856110c6565b5f8452614499565b9181601f840112156103535782359167ffffffffffffffff8311610353576020808501948460051b01011161035357565b346103535760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103535760043567ffffffffffffffff811161035357610f7d903690600401610efd565b9060243567ffffffffffffffff811161035357610f9e903690600401610efd565b610fa99291926147eb565b808403611050575f92835b61ffff851680871115610fed57610fe791610fdb610fd6610fe1938787613be0565b613bf5565b90613c18565b94613b9b565b93610fb4565b5090610ffe61ffff87959316614e1e565b5f5b61ffff811690818611156108f65761104682611040611039610fd661103161102c61104b988d8c613be0565b613c2e565b938a89613be0565b61ffff1690565b90614ed2565b613b9b565b611000565b7fe2f8ab90000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b6080810190811067ffffffffffffffff8211176110c157604052565b611078565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff8211176110c157604052565b67ffffffffffffffff81116110c157601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200190565b92919261114d82611107565b9161115b60405193846110c6565b829481845281830111610353578281602093845f960137010152565b9080601f83011215610353578160206104a793359101611141565b60407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610353576004356111c8816106b1565b60243567ffffffffffffffff8111610353576111e8903690600401611177565b73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000bde710fae0c0ccb2ce9db3503b61e98bc4e3fda5168030149081156113c6575b5061139e576112376147eb565b73ffffffffffffffffffffffffffffffffffffffff8216916040517f5ff27f30000000000000000000000000000000000000000000000000000000008152602081600481875afa90811561136a576112b9917f707edf5e14bfdf1ba58ecef846c170a48a35301414f4b4056ce0592b3a7a1c00915f9161136f575b50146150f8565b602060405180947f01ffc9a7000000000000000000000000000000000000000000000000000000008252818061131660048201907f7965db0b00000000000000000000000000000000000000000000000000000000602083019252565b03915afa92831561136a576108f693611336915f9161133b575b506150f8565b61513c565b61135d915060203d602011611363575b61135581836110c6565b810190615127565b5f611330565b503d61134b565b613e15565b611391915060203d602011611397575b61138981836110c6565b8101906150e9565b5f6112b2565b503d61137f565b7fe07c8dba000000000000000000000000000000000000000000000000000000005f5260045ffd5b905073ffffffffffffffffffffffffffffffffffffffff7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc541614155f61122a565b9060407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc8301126103535760043567ffffffffffffffff8116810361035357916024359067ffffffffffffffff82116103535761146791600401610efd565b9091565b346103535761147936611408565b90611482615372565b335f9081527f707edf5e14bfdf1ba58ecef846c170a48a35301414f4b4056ce0592b3a7a1c06602052604090208054909367ffffffffffffffff82169283158015906118ce575b6114d290613c38565b5f9480611807575b505050611555611505611568936114ff6115629467ffffffffffffffff9060801c1690565b90613c67565b67ffffffffffffffff851615801580916117f4575b61152390613c38565b6116da575b67ffffffffffffffff8116151595866115a4575b5061154f61154a8287613ca9565b614e1e565b84613ca9565b67ffffffffffffffff1690565b33614ed2565b60405167ffffffffffffffff91909116815233907fe92d85b79022e1534f6a5dd31dab0e27f003fc6c6d5a6057b45ae86fde6c5f1d90602090a3005b6116d4906116726115ea846115e57f707edf5e14bfdf1ba58ecef846c170a48a35301414f4b4056ce0592b3a7a1c025467ffffffffffffffff9060801c1690565b613ca9565b7fffffffffffffffff0000000000000000ffffffffffffffffffffffffffffffff77ffffffffffffffff000000000000000000000000000000007f707edf5e14bfdf1ba58ecef846c170a48a35301414f4b4056ce0592b3a7a1c02549260801b169116177f707edf5e14bfdf1ba58ecef846c170a48a35301414f4b4056ce0592b3a7a1c0255565b61168c836115e5835467ffffffffffffffff9060801c1690565b7fffffffffffffffff0000000000000000ffffffffffffffffffffffffffffffff77ffffffffffffffff0000000000000000000000000000000083549260801b169116179055565b5f61153c565b61179461170c866115e57f707edf5e14bfdf1ba58ecef846c170a48a35301414f4b4056ce0592b3a7a1c025460c01c90565b77ffffffffffffffffffffffffffffffffffffffffffffffff7fffffffffffffffff0000000000000000000000000000000000000000000000007f707edf5e14bfdf1ba58ecef846c170a48a35301414f4b4056ce0592b3a7a1c02549260c01b169116177f707edf5e14bfdf1ba58ecef846c170a48a35301414f4b4056ce0592b3a7a1c0255565b6117ef6117a6866115e5895460c01c90565b875477ffffffffffffffffffffffffffffffffffffffffffffffff1660c09190911b7fffffffffffffffff00000000000000000000000000000000000000000000000016178755565b611528565b5067ffffffffffffffff8216151561151a565b611562939550936114ff6118c4611555946118ba61150595611568997f707edf5e14bfdf1ba58ecef846c170a48a35301414f4b4056ce0592b3a7a1c045490604051926118b58461188988336020840190929167ffffffffffffffff60209173ffffffffffffffffffffffffffffffffffffffff604085019616845216910152565b037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081018652856110c6565b6153fd565b6114ff8960c01c90565b96945050936114da565b506114d26118e9604085901c67ffffffffffffffff16611555565b151590506114c9565b34610353575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103535773ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000bde710fae0c0ccb2ce9db3503b61e98bc4e3fda516300361139e5760206040517f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc8152f35b9181601f840112156103535782359167ffffffffffffffff8311610353576020838186019501011161035357565b346103535760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103535760043567ffffffffffffffff811161035357611a04903690600401611987565b611a0c6147eb565b67ffffffffffffffff81116110c157611a4e81611a497f707edf5e14bfdf1ba58ecef846c170a48a35301414f4b4056ce0592b3a7a1c0054613697565b61547d565b5f601f8211600114611afe5781611abd817f98d57c3f284cc158a0334fc376accadd65ddae6110b279c1fab292511230c65594611aee945f91611af3575b507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8260011b9260031b1c19161790565b7f707edf5e14bfdf1ba58ecef846c170a48a35301414f4b4056ce0592b3a7a1c00555b6040519182913395836155aa565b0390a2005b90508601355f611a8c565b7f707edf5e14bfdf1ba58ecef846c170a48a35301414f4b4056ce0592b3a7a1c005f527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08216907fad50d3b1a8f9a593754f2ce5b80ad3db496090d8a59a5edca0ec8bc841a2924a915f5b818110611c0757509183917f98d57c3f284cc158a0334fc376accadd65ddae6110b279c1fab292511230c65594611aee9410611bcf575b5050600181811b017f707edf5e14bfdf1ba58ecef846c170a48a35301414f4b4056ce0592b3a7a1c0055611ae0565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60f88460031b161c19908601351690555f80611ba0565b91926020600181928689013581550194019201611b69565b34610353575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103535760607f707edf5e14bfdf1ba58ecef846c170a48a35301414f4b4056ce0592b3a7a1c01546040519063ffffffff8160a01c16825263ffffffff8160c01c16602083015260e01c6040820152f35b34610353575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261035357602060405173ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000016a7b20cf490fff80ee08a064cfe7c093b0b1321168152f35b34610353575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103535760206040517f707edf5e14bfdf1ba58ecef846c170a48a35301414f4b4056ce0592b3a7a1c008152f35b34610353575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261035357602060405167ffffffffffffffff7f000000000000000000000000000000000000000000000000006a94d74f430000168152f35b346103535760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610353576004357fc1ff5e309bdd86f23cd0028c7b345bcdb3984b737495261606ad426b353d082c604435602435611e236147eb565b837f707edf5e14bfdf1ba58ecef846c170a48a35301414f4b4056ce0592b3a7a1c0355807f707edf5e14bfdf1ba58ecef846c170a48a35301414f4b4056ce0592b3a7a1c0455817f707edf5e14bfdf1ba58ecef846c170a48a35301414f4b4056ce0592b3a7a1c0555611aee6040519283923396846040919493926060820195825260208201520152565b346103535760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261035357602073ffffffffffffffffffffffffffffffffffffffff611eff600435614998565b16604051908152f35b346103535760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610353576020610b12600435611f48816106b1565b613ccb565b34610353575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261035357611f836155ef565b5f73ffffffffffffffffffffffffffffffffffffffff7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300547fffffffffffffffffffffffff000000000000000000000000000000000000000081167f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930055167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a3005b346103535760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103535773ffffffffffffffffffffffffffffffffffffffff600435612077816106b1565b61207f6147eb565b16807fffffffffffffffffffffffff00000000000000000000000000000000000000007f707edf5e14bfdf1ba58ecef846c170a48a35301414f4b4056ce0592b3a7a1c015416177f707edf5e14bfdf1ba58ecef846c170a48a35301414f4b4056ce0592b3a7a1c01556040519081527fc9c3685c43a110ec63da12acd71f195035fbe06238fc1b8989c122714c1d11eb60203392a2005b60207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103535760043567ffffffffffffffff811161035357612160903690600401610efd565b906121696153b4565b612174341515613d64565b335f9081527f707edf5e14bfdf1ba58ecef846c170a48a35301414f4b4056ce0592b3a7a1c0660205260408120831580159383918290612616575061225b612290916122566121cb865467ffffffffffffffff1690565b916121e067ffffffffffffffff841615613d93565b67ffffffffffffffff7f000000000000000000000000000000000000000000000000006a94d74f43000016996122188b341015613d64565b7f707edf5e14bfdf1ba58ecef846c170a48a35301414f4b4056ce0592b3a7a1c03546040805133602082015293919291906118b59085908101611889565b613c89565b839067ffffffffffffffff167fffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000825416179055565b61233b6122c86122567f707edf5e14bfdf1ba58ecef846c170a48a35301414f4b4056ce0592b3a7a1c025467ffffffffffffffff1690565b67ffffffffffffffff167fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000007f707edf5e14bfdf1ba58ecef846c170a48a35301414f4b4056ce0592b3a7a1c025416177f707edf5e14bfdf1ba58ecef846c170a48a35301414f4b4056ce0592b3a7a1c0255565b84803411612605575b505b80612495575b50506040805134815267ffffffffffffffff92909216602083015233917f13aca7d5b1c21ecefa8f16ba492ef2a173267e278b5753d0b3d0df30791f9fe89190a373ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000016a7b20cf490fff80ee08a064cfe7c093b0b132116803b15610353576040517f6a627842000000000000000000000000000000000000000000000000000000008152336004820152905f908290602490829084905af1801561136a5761247b575b508061241857005b6108f69061247661245d7f707edf5e14bfdf1ba58ecef846c170a48a35301414f4b4056ce0592b3a7a1c015473ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff1690565b61490f565b806124895f61248f936110c6565b80610449565b5f612410565b6125419192506115556124e69167ffffffffffffffff7f000000000000000000000000000000000000000000000000006a94d74f43000016906124e16124db8383613dfc565b15613d64565b613e0b565b91612501836115e5835467ffffffffffffffff9060401c1690565b7fffffffffffffffffffffffffffffffff0000000000000000ffffffffffffffff6fffffffffffffffff000000000000000083549260401b169116179055565b6125fe61257e826115e57f707edf5e14bfdf1ba58ecef846c170a48a35301414f4b4056ce0592b3a7a1c025467ffffffffffffffff9060401c1690565b7fffffffffffffffffffffffffffffffff0000000000000000ffffffffffffffff6fffffffffffffffff00000000000000007f707edf5e14bfdf1ba58ecef846c170a48a35301414f4b4056ce0592b3a7a1c02549260401b169116177f707edf5e14bfdf1ba58ecef846c170a48a35301414f4b4056ce0592b3a7a1c0255565b5f8061234c565b612610915034613dc2565b5f612344565b9550505034612346565b346103535760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103535761266360043561265e816106b1565b613e9b565b6040518091602082016020835281518091526020604084019201905f5b81811061268e575050500390f35b825167ffffffffffffffff16845285945060209384019390920191600101612680565b34610353575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261035357602073ffffffffffffffffffffffffffffffffffffffff7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c1993005416604051908152f35b346103535760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103535760043561275c816106b1565b60243567ffffffffffffffff81116103535761277c903690600401611987565b60443567ffffffffffffffff81116103535761279c903690600401611987565b917fee151c8401928dc223602bb187aff91b9a56c7cae5476ef1b3287b085a16c85f549360ff8560081c165f146129ce57303b155b1561294a576127f7946127ec9060081c60ff161590565b1590565b958661286857613fbc565b6127fd57005b6108f67fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff7fee151c8401928dc223602bb187aff91b9a56c7cae5476ef1b3287b085a16c85f54167fee151c8401928dc223602bb187aff91b9a56c7cae5476ef1b3287b085a16c85f55565b6128d76101007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff7fee151c8401928dc223602bb187aff91b9a56c7cae5476ef1b3287b085a16c85f5416177fee151c8401928dc223602bb187aff91b9a56c7cae5476ef1b3287b085a16c85f55565b61294560017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff007fee151c8401928dc223602bb187aff91b9a56c7cae5476ef1b3287b085a16c85f5416177fee151c8401928dc223602bb187aff91b9a56c7cae5476ef1b3287b085a16c85f55565b613fbc565b60846040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603760248201527f455243373231415f5f496e697469616c697a61626c653a20636f6e747261637460448201527f20697320616c726561647920696e697469616c697a65640000000000000000006064820152fd5b60ff8516156127d1565b346103535760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261035357602060ff612a69602435600435612a1d826106b1565b5f527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b626800845260405f209073ffffffffffffffffffffffffffffffffffffffff165f5260205260405f2090565b54166040519015158152f35b6004359063ffffffff8216820361035357565b6024359063ffffffff8216820361035357565b6044359063ffffffff8216820361035357565b346103535760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610353576108f6612ae8612a75565b612af0612a88565b90612af9612a9b565b91612b026147eb565b7fffffffff0000000000000000ffffffffffffffffffffffffffffffffffffffff77ffffffff00000000000000000000000000000000000000007bffffffff0000000000000000000000000000000000000000000000007f707edf5e14bfdf1ba58ecef846c170a48a35301414f4b4056ce0592b3a7a1c01549363ffffffff8560a01c1615612c6f575b612bb463ffffffff8216612ba763ffffffff89168211614445565b63ffffffff891611614445565b60c01b169360a01b16911617177f707edf5e14bfdf1ba58ecef846c170a48a35301414f4b4056ce0592b3a7a1c01557bffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fffffffff000000000000000000000000000000000000000000000000000000007f707edf5e14bfdf1ba58ecef846c170a48a35301414f4b4056ce0592b3a7a1c01549260e01b169116177f707edf5e14bfdf1ba58ecef846c170a48a35301414f4b4056ce0592b3a7a1c0155565b612c804263ffffffff881611614445565b612b8c565b34610353575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610353576040515f7f2569078dfb4b0305704d3008e7403993ae9601b85f7ae5e742de3de8f8011c4354612ce281613697565b80845290600181169081156105bc5750600114612d095761053b8361052f818503826110c6565b7f2569078dfb4b0305704d3008e7403993ae9601b85f7ae5e742de3de8f8011c435f9081527f617167b76dcc8247761fd21f427ad8ec3be6b3be203aed34e3aac08b4d31817c939250905b808210612d6c5750909150810160200161052f61051f565b919260018160209254838588010152019101909291612d54565b34610353575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103535760206040515f8152f35b8015150361035357565b346103535760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261035357600435612e03816106b1565b73ffffffffffffffffffffffffffffffffffffffff60243591612e2583612dbe565b612e708161082b3373ffffffffffffffffffffffffffffffffffffffff165f527f2569078dfb4b0305704d3008e7403993ae9601b85f7ae5e742de3de8f8011c4760205260405f2090565b921515927fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0081541660ff851617905560405192835216907f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c3160203392a3005b34610353575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103535761053b604051612f0e6040826110c6565b600581527f352e302e300000000000000000000000000000000000000000000000000000006020820152604051918291602083526020830190610453565b34610353576108f667ffffffffffffffff613007612f6936611408565b9290612f73615372565b335f9081527f707edf5e14bfdf1ba58ecef846c170a48a35301414f4b4056ce0592b3a7a1c0660205260409020937f707edf5e14bfdf1ba58ecef846c170a48a35301414f4b4056ce0592b3a7a1c055490604051926118b58461188988336020840190929167ffffffffffffffff60209173ffffffffffffffffffffffffffffffffffffffff604085019616845216910152565b61308182549261304d8561303961303361302c8867ffffffffffffffff9060401c1690565b9760c01c90565b87613c67565b16868516811015908161310b575b50613c38565b6125018361307b7f000000000000000000000000000000000000000000000000006a94d74f43000082614474565b95613c67565b6130c361257e826130be7f707edf5e14bfdf1ba58ecef846c170a48a35301414f4b4056ce0592b3a7a1c025467ffffffffffffffff9060401c1690565b613c67565b6040805167ffffffffffffffff848116825292909216602083015233917f3f59fd06eb9c0636a5eae031e65ba34fa18ab73b7c1718fa3984ab64b75d4a5e9190a2163361490f565b905015155f613047565b60807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103535760043561314b816106b1565b60243590613158826106b1565b6044356064359267ffffffffffffffff84116103535761317f6108f6943690600401611177565b92614499565b34610353575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103535760807f707edf5e14bfdf1ba58ecef846c170a48a35301414f4b4056ce0592b3a7a1c02546040519067ffffffffffffffff8116825267ffffffffffffffff8160401c16602083015267ffffffffffffffff81841c16604083015260c01c6060820152f35b346103535760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610353576004356132528161463f565b156133735761325f6136e8565b8051909190156133585760405160a08101604052600a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60808301935f8552935b01928181066030018453049182156132db577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600a916132a0565b61332692509261332c61052f92608061053b967fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08101920301815260405194859360208501906144f3565b906144f3565b037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081018352826110c6565b505061053b60405161336b6020826110c6565b5f815261052f565b7fa14c4b50000000000000000000000000000000000000000000000000000000005f5260045ffd5b346103535760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610353576108f66024356004356133dc826106b1565b613414610d35825f527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b626800602052600160405f20015490565b614d09565b346103535760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261035357602060ff612a6960043561345b816106b1565b61082b6024359161346b836106b1565b73ffffffffffffffffffffffffffffffffffffffff165f527f2569078dfb4b0305704d3008e7403993ae9601b85f7ae5e742de3de8f8011c4760205260405f2090565b346103535760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610353576108f66004356134ec816106b1565b6134f46155ef565b614552565b34610353575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261035357602073ffffffffffffffffffffffffffffffffffffffff7f707edf5e14bfdf1ba58ecef846c170a48a35301414f4b4056ce0592b3a7a1c015416604051908152f35b346103535760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103535761053b61360d6004356135aa816106b1565b5f60606040516135b9816110a5565b828152826020820152826040820152015273ffffffffffffffffffffffffffffffffffffffff165f527f707edf5e14bfdf1ba58ecef846c170a48a35301414f4b4056ce0592b3a7a1c0660205260405f2090565b6040519061361a826110a5565b5467ffffffffffffffff8116825267ffffffffffffffff8160401c16602083015267ffffffffffffffff8160801c16604083015260c01c606082015260405191829182919091606067ffffffffffffffff816080840195828151168552826020820151166020860152826040820151166040860152015116910152565b90600182811c921680156136de575b60208310146136b157565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602260045260245ffd5b91607f16916136a6565b604051905f827f707edf5e14bfdf1ba58ecef846c170a48a35301414f4b4056ce0592b3a7a1c00549161371a83613697565b80835292600181169081156137be5750600114613740575b61373e925003836110c6565b565b507f707edf5e14bfdf1ba58ecef846c170a48a35301414f4b4056ce0592b3a7a1c005f90815290917fad50d3b1a8f9a593754f2ce5b80ad3db496090d8a59a5edca0ec8bc841a2924a5b8183106137a257505090602061373e92820101613732565b602091935080600191548385890101520191019091849261378a565b6020925061373e9491507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001682840152151560051b820101613732565b7f707edf5e14bfdf1ba58ecef846c170a48a35301414f4b4056ce0592b3a7a1c015463ffffffff8160a01c1680156138665742101580613853575b61384d5760e01c421015613848575f90565b600290565b50600190565b5063ffffffff8160c01c16421115613836565b50505f90565b91909173ffffffffffffffffffffffffffffffffffffffff61388d83614998565b9116928373ffffffffffffffffffffffffffffffffffffffff831603613b69575f8381527f2569078dfb4b0305704d3008e7403993ae9601b85f7ae5e742de3de8f8011c46602052604090208054929061390373ffffffffffffffffffffffffffffffffffffffff871633908114908614171590565b613b52575b73ffffffffffffffffffffffffffffffffffffffff93613b49575b5061396b8573ffffffffffffffffffffffffffffffffffffffff165f527f2569078dfb4b0305704d3008e7403993ae9601b85f7ae5e742de3de8f8011c4560205260405f2090565b80547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190556139d88273ffffffffffffffffffffffffffffffffffffffff165f527f2569078dfb4b0305704d3008e7403993ae9601b85f7ae5e742de3de8f8011c4560205260405f2090565b8054600101905573ffffffffffffffffffffffffffffffffffffffff82164260a01b177c020000000000000000000000000000000000000000000000000000000017613a4b855f527f2569078dfb4b0305704d3008e7403993ae9601b85f7ae5e742de3de8f8011c4460205260405f2090565b557c0200000000000000000000000000000000000000000000000000000000811615613aa6575b501680927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef5f80a415613aa157565b61474b565b60018401613adb815f527f2569078dfb4b0305704d3008e7403993ae9601b85f7ae5e742de3de8f8011c4460205260405f2090565b5415613ae8575b50613a72565b7f2569078dfb4b0305704d3008e7403993ae9601b85f7ae5e742de3de8f8011c40548114613ae257613b41905f527f2569078dfb4b0305704d3008e7403993ae9601b85f7ae5e742de3de8f8011c4460205260405f2090565b555f80613ae2565b5f90555f613923565b613b5f6127e83388614505565b1561390857614723565b6146fb565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b61ffff1661ffff8114613bae5760010190565b613b6e565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b9190811015613bf05760051b0190565b613bb3565b3561ffff811681036103535790565b61ffff60019116019061ffff8211613bae57565b9061ffff8091169116019061ffff8211613bae57565b356104a7816106b1565b15613c3f57565b7f4cfe0a87000000000000000000000000000000000000000000000000000000005f5260045ffd5b9067ffffffffffffffff8091169116039067ffffffffffffffff8211613bae57565b67ffffffffffffffff60019116019067ffffffffffffffff8211613bae57565b9067ffffffffffffffff8091169116019067ffffffffffffffff8211613bae57565b73ffffffffffffffffffffffffffffffffffffffff811615613d3c57613d3767ffffffffffffffff9173ffffffffffffffffffffffffffffffffffffffff165f527f2569078dfb4b0305704d3008e7403993ae9601b85f7ae5e742de3de8f8011c4560205260405f2090565b541690565b7f8f4eb604000000000000000000000000000000000000000000000000000000005f5260045ffd5b15613d6b57565b7fff510849000000000000000000000000000000000000000000000000000000005f5260045ffd5b15613d9a57565b7f7d7a2ae4000000000000000000000000000000000000000000000000000000005f5260045ffd5b91908203918211613bae57565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b8115613e06570690565b613dcf565b8115613e06570490565b6040513d5f823e3d90fd5b67ffffffffffffffff81116110c15760051b60200190565b90613e4282613e20565b613e4f60405191826110c6565b8281527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0613e7d8294613e20565b0190602036910137565b8051821015613bf05760209160051b010190565b613ea481613ccb565b8015613f9f57613eb381613e38565b905f5f613f027f2569078dfb4b0305704d3008e7403993ae9601b85f7ae5e742de3de8f8011c40547f2569078dfb4b0305704d3008e7403993ae9601b85f7ae5e742de3de8f8011c4154900390565b905b61ffff811680831115613f9557613f2061245d61245d83614998565b73ffffffffffffffffffffffffffffffffffffffff808916911614613f4f575b50613f4a90613b9b565b613f04565b92613f75613f7a9294613f6661ffff841689613e87565b9067ffffffffffffffff169052565b613c04565b918361ffff841614613f8c575f613f40565b50505050905090565b5050505050919050565b5050604051613faf6020826110c6565b5f81525f36602083013790565b929093917ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00549467ffffffffffffffff61400d613fff60ff8960401c1615151590565b9767ffffffffffffffff1690565b16801590816141d8575b60011490816141ce575b1590816141c5575b50610c7f576140aa94866140a160017fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000007ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a005416177ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0055565b61414a576141e0565b6140b057565b61411b7fffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffff7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0054167ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0055565b604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d290602090a1565b6141c0680100000000000000007fffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffff7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a005416177ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0055565b6141e0565b9050155f614029565b303b159150614021565b879150614017565b92906142049061420c939695966141f5615882565b6141fd615882565b3691611141565b943691611141565b61424560ff7fee151c8401928dc223602bb187aff91b9a56c7cae5476ef1b3287b085a16c85f5460081c166142408161565b565b61565b565b835167ffffffffffffffff81116110c157614289816142847f2569078dfb4b0305704d3008e7403993ae9601b85f7ae5e742de3de8f8011c4254613697565b6154ec565b6020601f8211600114614340578161430593926142de9261433297985f92614335575b50507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8260011b9260031b1c19161790565b7f2569078dfb4b0305704d3008e7403993ae9601b85f7ae5e742de3de8f8011c425561592a565b61432d5f7f2569078dfb4b0305704d3008e7403993ae9601b85f7ae5e742de3de8f8011c4055565b614b0b565b50565b015190505f806142ac565b7f2569078dfb4b0305704d3008e7403993ae9601b85f7ae5e742de3de8f8011c425f527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08216957f933ecf8acb7824b680a8d16f3ff3db8864228d986aa4c2ebab1eeb2703b4beb3965f5b81811061442d57509660019284926143059695614332999a106143f6575b505050811b017f2569078dfb4b0305704d3008e7403993ae9601b85f7ae5e742de3de8f8011c425561592a565b01517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60f88460031b161c191690555f80806143c9565b838301518955600190980197602093840193016143ab565b1561444c57565b7f6bb1eaf4000000000000000000000000000000000000000000000000000000005f5260045ffd5b9067ffffffffffffffff8091169116029067ffffffffffffffff8216918203613bae57565b9291906144a782828661386c565b803b6144b4575b50505050565b6144bd9361582c565b156144cb575f8080806144ae565b7fd1a57ed6000000000000000000000000000000000000000000000000000000005f5260045ffd5b805191908290602001825e015f815290565b60ff9161082b613d379273ffffffffffffffffffffffffffffffffffffffff165f527f2569078dfb4b0305704d3008e7403993ae9601b85f7ae5e742de3de8f8011c4760205260405f2090565b73ffffffffffffffffffffffffffffffffffffffff1680156146135773ffffffffffffffffffffffffffffffffffffffff7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930054827fffffffffffffffffffffffff00000000000000000000000000000000000000008216177f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930055167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a3565b7f1e4fbdf7000000000000000000000000000000000000000000000000000000005f525f60045260245ffd5b905f917f2569078dfb4b0305704d3008e7403993ae9601b85f7ae5e742de3de8f8011c4054811061466d5750565b9091505b6146a2815f527f2569078dfb4b0305704d3008e7403993ae9601b85f7ae5e742de3de8f8011c4460205260405f2090565b54806146d657508015613bae577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01614671565b7c01000000000000000000000000000000000000000000000000000000009150161590565b7fa1148100000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f59c896be000000000000000000000000000000000000000000000000000000005f5260045ffd5b7fea553b34000000000000000000000000000000000000000000000000000000005f5260045ffd5b7fdf2d9b42000000000000000000000000000000000000000000000000000000005f5260045ffd5b7fb562e8dd000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f2e076300000000000000000000000000000000000000000000000000000000005f5260045ffd5b335f9081527fb7db2dd08fcb62d0c9e08c51941cae53c267786a0b75803fb7960902fc8ef97d602052604090205460ff161561482357565b7fe2517d3f000000000000000000000000000000000000000000000000000000005f52336004525f60245260445ffd5b805f527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b62680060205260ff6148a73360405f209073ffffffffffffffffffffffffffffffffffffffff165f5260205260405f2090565b5416156148b15750565b7fe2517d3f000000000000000000000000000000000000000000000000000000005f523360045260245260445ffd5b3d1561490a573d906148f182611107565b916148ff60405193846110c6565b82523d5f602084013e565b606090565b81471061496c575f80809373ffffffffffffffffffffffffffffffffffffffff8294165af161493c6148e0565b501561494457565b7f1425ea42000000000000000000000000000000000000000000000000000000005f5260045ffd5b7fcd786059000000000000000000000000000000000000000000000000000000005f523060045260245ffd5b6149c9815f527f2569078dfb4b0305704d3008e7403993ae9601b85f7ae5e742de3de8f8011c4460205260405f2090565b549081156149f957507c010000000000000000000000000000000000000000000000000000000081166147735790565b90507f2569078dfb4b0305704d3008e7403993ae9601b85f7ae5e742de3de8f8011c4054811015614773577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff905b01614a79815f527f2569078dfb4b0305704d3008e7403993ae9601b85f7ae5e742de3de8f8011c4460205260405f2090565b54908115614acf57507c01000000000000000000000000000000000000000000000000000000008116156104a7577fdf2d9b42000000000000000000000000000000000000000000000000000000005f5260045ffd5b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9150614a47565b61373e90614b03615882565b6134f4615882565b73ffffffffffffffffffffffffffffffffffffffff81165f9081527fb7db2dd08fcb62d0c9e08c51941cae53c267786a0b75803fb7960902fc8ef97d602052604090205460ff16614bed5773ffffffffffffffffffffffffffffffffffffffff165f8181527fb7db2dd08fcb62d0c9e08c51941cae53c267786a0b75803fb7960902fc8ef97d6020526040812080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790553391907f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d8180a4600190565b505f90565b805f527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b62680060205260ff614c468360405f209073ffffffffffffffffffffffffffffffffffffffff165f5260205260405f2090565b541661386657805f527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b626800602052614c9e8260405f209073ffffffffffffffffffffffffffffffffffffffff165f5260205260405f2090565b60017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0082541617905573ffffffffffffffffffffffffffffffffffffffff339216907f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d5f80a4600190565b805f527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b62680060205260ff614d5d8360405f209073ffffffffffffffffffffffffffffffffffffffff165f5260205260405f2090565b54161561386657805f527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b626800602052614db68260405f209073ffffffffffffffffffffffffffffffffffffffff165f5260205260405f2090565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00815416905573ffffffffffffffffffffffffffffffffffffffff339216907ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b5f80a4600190565b67ffffffffffffffff7f2569078dfb4b0305704d3008e7403993ae9601b85f7ae5e742de3de8f8011c40547f2569078dfb4b0305704d3008e7403993ae9601b85f7ae5e742de3de8f8011c4154900391168101809111613bae5767ffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000003e81610614eaa57565b7f1407d9f5000000000000000000000000000000000000000000000000000000005f5260045ffd5b9160405190614ee26020836110c6565b5f82527f2569078dfb4b0305704d3008e7403993ae9601b85f7ae5e742de3de8f8011c40549183156150e45773ffffffffffffffffffffffffffffffffffffffff85164260a01b6001861460e11b1717614f63845f527f2569078dfb4b0305704d3008e7403993ae9601b85f7ae5e742de3de8f8011c4460205260405f2090565b55614fab8573ffffffffffffffffffffffffffffffffffffffff165f527f2569078dfb4b0305704d3008e7403993ae9601b85f7ae5e742de3de8f8011c4560205260405f2090565b680100000000000000018502815401905573ffffffffffffffffffffffffffffffffffffffff85169384156150df57808401939560015b15615015575b5f87875f7fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8180a4614fe2565b9560010195848703614fe8579295509261504f9194507f2569078dfb4b0305704d3008e7403993ae9601b85f7ae5e742de3de8f8011c4055565b803b61505a57505050565b9091927f2569078dfb4b0305704d3008e7403993ae9601b85f7ae5e742de3de8f8011c40549283039260015b156150aa575b5f61509f6127e887600188019787615736565b15615086575b6144cb565b80841061508c5792509250507f2569078dfb4b0305704d3008e7403993ae9601b85f7ae5e742de3de8f8011c40540361035357565b6147c3565b61479b565b90816020910312610353575190565b156150ff57565b7f4a2c7f44000000000000000000000000000000000000000000000000000000005f5260045ffd5b9081602091031261035357516104a781612dbe565b906040517f52d1902d00000000000000000000000000000000000000000000000000000000815260208160048173ffffffffffffffffffffffffffffffffffffffff87165afa5f9181615351575b506151d1577f4c9c8ce3000000000000000000000000000000000000000000000000000000005f5273ffffffffffffffffffffffffffffffffffffffff831660045260245ffd5b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc81036153265750813b156152e45773ffffffffffffffffffffffffffffffffffffffff8216807fffffffffffffffffffffffff00000000000000000000000000000000000000007f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5416177f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc557fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b5f80a28051156152b35761433291615af8565b5050346152bc57565b7fb398979f000000000000000000000000000000000000000000000000000000005f5260045ffd5b73ffffffffffffffffffffffffffffffffffffffff827f4c9c8ce3000000000000000000000000000000000000000000000000000000005f521660045260245ffd5b7faa1d49a4000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b61536b91925060203d6020116113975761138981836110c6565b905f61518a565b61537a6137fb565b6003811015610a215760020361538c57565b7fa8766b20000000000000000000000000000000000000000000000000000000005f5260045ffd5b6153bc6137fb565b6003811015610a215760010361538c57565b156153d557565b7f5bd1f4a0000000000000000000000000000000000000000000000000000000005f5260045ffd5b9260208151910120604051602081019182526020815261541e6040826110c6565b5190209261542b82613e20565b9161543960405193846110c6565b808352602083019060051b82019136831161035357905b82821061546d57505050916154689161373e936158d9565b6153ce565b8135815260209182019101615450565b601f8111615489575050565b7f707edf5e14bfdf1ba58ecef846c170a48a35301414f4b4056ce0592b3a7a1c005f5260205f20906020601f840160051c830193106154e2575b601f0160051c01905b8181106154d7575050565b5f81556001016154cc565b90915081906154c3565b601f81116154f8575050565b7f2569078dfb4b0305704d3008e7403993ae9601b85f7ae5e742de3de8f8011c425f5260205f20906020601f840160051c83019310615551575b601f0160051c01905b818110615546575050565b5f815560010161553b565b9091508190615532565b601f821161556857505050565b5f5260205f20906020601f840160051c830193106155a0575b601f0160051c01905b818110615595575050565b5f815560010161558a565b9091508190615581565b90601f836040947fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09360208652816020870152868601375f8582860101520116010190565b73ffffffffffffffffffffffffffffffffffffffff7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c1993005416330361562f57565b7f118cdaa7000000000000000000000000000000000000000000000000000000005f523360045260245ffd5b1561566257565b60846040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603460248201527f455243373231415f5f496e697469616c697a61626c653a20636f6e747261637460448201527f206973206e6f7420696e697469616c697a696e670000000000000000000000006064820152fd5b9081602091031261035357516104a781610329565b909273ffffffffffffffffffffffffffffffffffffffff608093816104a7979616845216602083015260408201528160608201520190610453565b9161578b926020925f73ffffffffffffffffffffffffffffffffffffffff6040518097819682957f150b7a020000000000000000000000000000000000000000000000000000000084528533600486016156fb565b0393165af15f91816157fb575b506157b4576157a56148e0565b8051156150a557805190602001fd5b7fffffffff00000000000000000000000000000000000000000000000000000000167f150b7a02000000000000000000000000000000000000000000000000000000001490565b61581e91925060203d602011615825575b61581681836110c6565b8101906156e6565b905f615798565b503d61580c565b906020925f73ffffffffffffffffffffffffffffffffffffffff61578b9694604051978896879586937f150b7a0200000000000000000000000000000000000000000000000000000000855233600486016156fb565b60ff7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a005460401c16156158b157565b7fd7e6bcf8000000000000000000000000000000000000000000000000000000005f5260045ffd5b929091905f915b8451831015615922576158f38386613e87565b519081811015615911575f52602052600160405f205b9201916158e0565b905f52602052600160405f20615909565b915092501490565b90815167ffffffffffffffff81116110c1576159908161596a7f2569078dfb4b0305704d3008e7403993ae9601b85f7ae5e742de3de8f8011c4354613697565b7f2569078dfb4b0305704d3008e7403993ae9601b85f7ae5e742de3de8f8011c4361555b565b602092601f8211600114615a01576159dd929382915f926143355750507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8260011b9260031b1c19161790565b7f2569078dfb4b0305704d3008e7403993ae9601b85f7ae5e742de3de8f8011c4355565b7f2569078dfb4b0305704d3008e7403993ae9601b85f7ae5e742de3de8f8011c435f527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08216937f617167b76dcc8247761fd21f427ad8ec3be6b3be203aed34e3aac08b4d31817c915f5b868110615ae05750836001959610615aa9575b505050811b017f2569078dfb4b0305704d3008e7403993ae9601b85f7ae5e742de3de8f8011c4355565b01517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60f88460031b161c191690555f8080615a7f565b91926020600181928685015181550194019201615a6c565b5f806104a793602081519101845af4615b0f6148e0565b9190615b25575080511561494457805190602001fd5b81511580615b78575b615b36575090565b73ffffffffffffffffffffffffffffffffffffffff907f9996b315000000000000000000000000000000000000000000000000000000005f521660045260245ffd5b50803b15615b2e56fea2646970667358221220eff6132e62a14d1616f91214eed74fcb1b98b88b7e12b96f07b56135492fe58864736f6c634300081c0033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
00000000000000000000000016a7b20cf490fff80ee08a064cfe7c093b0b132100000000000000000000000000000000000000000000000000000000000003e8000000000000000000000000000000000000000000000000006a94d74f430000000000000000000000000000000000000000000000000000006a94d74f430000
-----Decoded View---------------
Arg [0] : participationToken (address): 0x16a7b20cF490fFf80ee08a064cFe7c093b0B1321
Arg [1] : maxTotalSupply (uint64): 1000
Arg [2] : whitelistPrice (uint64): 30000000000000000
Arg [3] : publicPrice (uint64): 30000000000000000
-----Encoded View---------------
4 Constructor Arguments found :
Arg [0] : 00000000000000000000000016a7b20cf490fff80ee08a064cfe7c093b0b1321
Arg [1] : 00000000000000000000000000000000000000000000000000000000000003e8
Arg [2] : 000000000000000000000000000000000000000000000000006a94d74f430000
Arg [3] : 000000000000000000000000000000000000000000000000006a94d74f430000
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in ETH
0
Multichain Portfolio | 33 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.