NFT
Overview
TokenID
337
Total Transfers
-
Market
Onchain Market Cap
$0.00
Circulating Supply Market Cap
-
Other Info
Token Contract
Loading...
Loading
Loading...
Loading
Loading...
Loading
# | Exchange | Pair | Price | 24H Volume | % Volume |
---|
Contract Name:
Together
Compiler Version
v0.8.19+commit.7dd6d404
Optimization Enabled:
Yes with 20 runs
Other Settings:
paris EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT pragma solidity >=0.8.17 .0; import { ERC721A } from "@erc721a/ERC721A.sol"; import { NFTEventsAndErrors } from "./NFTEventsAndErrors.sol"; import { Base64 } from "@openzeppelin/contracts/utils/Base64.sol"; import { IERC2981 } from "@openzeppelin/contracts/interfaces/IERC2981.sol"; import { IERC721 } from "@openzeppelin/contracts/interfaces/IERC721.sol"; import { Utils } from "./utils/Utils.sol"; import { Constants } from "./utils/Constants.sol"; import { ColorfulArt } from "./utils/ColorfulArt.sol"; import { LibString } from "./utils/LibString.sol"; import { AllowList } from "./utils/AllowList.sol"; /// @title Together NFT /// @author Aspyn Palatnick (aspyn.eth, stuckinaboot.eth) /// @notice Together is an onchain NFT where the art of each token builds on the art of all previous tokens. contract Together is ColorfulArt, ERC721A, NFTEventsAndErrors, Constants, AllowList { using LibString for uint256; bool public publicMintEnabled; uint16 internal immutable _allowListMintMaxTotal; uint8 internal immutable _allowListMintMaxPerWallet; mapping(address user => uint8 minted) internal _allowListMinted; constructor( bytes32 allowListMerkleRoot, uint16 allowListMintMaxTotalVal, uint8 allowListMintMaxPerWalletVal ) AllowList(allowListMerkleRoot) ERC721A("Together", "TGR") { _allowListMintMaxTotal = allowListMintMaxTotalVal; _allowListMintMaxPerWallet = allowListMintMaxPerWalletVal; } // Commence /// @notice Update public mint enabled. function updatePublicMintEnabled(bool _publicMintEnabled) external onlyOwner { publicMintEnabled = _publicMintEnabled; } function commence() external onlyOwner { // Checks // Check if already commenced if (commenced) { revert AlreadyCommenced(); } // Effects // Update commenced to be true commenced = true; // Mint initial tokens _coreMint(msg.sender, _MINT_AMOUNT_DURING_COMMENCE); } // Mint function _coreMint(address to, uint8 amount) internal { // Checks if (!commenced) { // Check mint has started revert MintNotStarted(); } uint256 nextTokenIdToBeMinted = _nextTokenId(); unchecked { if (MAX_POINTS_TOTAL + 1 < nextTokenIdToBeMinted + amount) { // Check max supply not exceeded revert MaxSupplyReached(); } } // Effects // Set colors unchecked { for (uint256 i = nextTokenIdToBeMinted; i < nextTokenIdToBeMinted + amount; ) { tokenToColor[i] = Utils.getPackedRGBColor(keccak256(abi.encodePacked(block.prevrandao, i))); ++i; } } // Perform mint _mint(to, amount); } /// @notice Mint tokens for allowlist minters. /// @param proof proof /// @param amount amount of tokens to mint function mintAllowList(bytes32[] calldata proof, uint8 amount) external payable onlyAllowListed(proof) { // Checks unchecked { if (amount * PRICE != msg.value) { // Check payment by sender is correct revert IncorrectPayment(); } if (_totalMinted() + amount > _allowListMintMaxTotal) { // Check allowlist mint total is not exceeding max allowed to be minted during allowlist phase revert AllowListMintCapExceeded(); } if (_allowListMinted[msg.sender] + amount > _allowListMintMaxPerWallet) { // Check wallet is not exceeding max allowed during allowlist phase revert AllowListMintCapPerWalletExceeded(); } } // Effects // Increase allowlist minted by amount unchecked { _allowListMinted[msg.sender] += amount; } // Perform mint _coreMint(msg.sender, amount); } /// @notice Mint tokens. /// @param amount amount of tokens to mint function mintPublic(uint8 amount) external payable { // Checks if (!publicMintEnabled) { // Check public mint enabled revert PublicMintNotEnabled(); } if (amount > _MAX_PUBLIC_MINT_AMOUNT_PER_TRANSACTION) { revert PublicMintMaxPerTransactionExceeded(); } unchecked { if (amount * PRICE != msg.value) { // Check payment by sender is correct revert IncorrectPayment(); } } // Effects _coreMint(msg.sender, amount); } function _startTokenId() internal pure override returns (uint256) { return 1; } // Withdraw /// @notice Withdraw all ETH from the contract to the vault. function withdraw() external { (bool success, ) = _VAULT_ADDRESS.call{ value: address(this).balance }(""); require(success); } // Metadata /// @notice Set the background color of your token. /// @param tokenId Together token id /// @param xxyyzzTokenId XXYYZZ token id (this will be what your Together /// token's background color is set to) function colorBackground(uint256 tokenId, uint24 xxyyzzTokenId) external { // Checks if (ownerOf(tokenId) != msg.sender) { // Revert if msg.sender does not own this token revert MsgSenderNotTokenOwner(); } if ( // Background color can always be reset to black xxyyzzTokenId != 0 && // For any other color, check if msg.sender owns that xxyyzz token IERC721(_XXYYZZ_TOKEN_ADDRESS).ownerOf(xxyyzzTokenId) != msg.sender ) { // Revert if msg.sender is not owner of the input xxyyzz token revert MsgSenderDoesNotOwnXXYYZZToken(); } // Effects // Update token background to xxyyzz token color tokenToBackgroundColor[tokenId] = xxyyzzTokenId; emit MetadataUpdate(tokenId); } function _getTraits(uint256 tokenId) internal view returns (string memory) { unchecked { uint256 innerShapePoints = tokenId % MAX_POINTS_PER_POLYGON; return string.concat( "[", Utils.getTrait("Total Points", tokenId.toString(), true, true), Utils.getTrait("Depth", (tokenId / MAX_POINTS_PER_POLYGON).toString(), true, true), Utils.getTrait( "Inner Shape Points", (innerShapePoints > 0 ? innerShapePoints : MAX_POINTS_PER_POLYGON).toString(), true, true ), Utils.getTrait("New Line Color", Utils.getRGBStr(tokenToColor[tokenId]), false, true), Utils.getTrait("Background Color", Utils.getRGBStr(tokenToBackgroundColor[tokenId]), false, false), "]" ); } } /// @notice Get token uri for a particular token. /// @param tokenId token id /// @return tokenURI function tokenURI(uint256 tokenId) public view virtual override returns (string memory) { if (!_exists(tokenId)) { revert URIQueryForNonexistentToken(); } string memory artSvg = art(uint16(tokenId)); return Utils.formatTokenURI( tokenId, Utils.svgToURI(artSvg), string.concat( "data:text/html;base64,", Utils.encodeBase64( bytes( string.concat( '<html style="overflow:hidden"><body style="margin:0">', artSvg, '<script>let a=!1,b=!1,o=Array.from(document.getElementsByTagName("line")).map(e=>e.style.stroke),s=e=>new Promise(t=>setTimeout(t,e)),c=()=>Math.floor(256*Math.random());document.body.addEventListener("click",async()=>{if(!a||b)return;b=!0;let e=document.getElementsByTagName("line");for(;a;){for(let t=0;t<e.length;t++)e[t].style.stroke=`rgb(${c()},${c()},${c()})`;await s(50)}for(let l=0;l<e.length;l++)e[l].style.stroke=o[l];b=!1},!0),document.body.addEventListener("click",async()=>{if(a)return;a=!0;let e=document.getElementsByTagName("line");for(let t=0;t<2*e.length;t++){let l=t<e.length?"0":"1",n=t<e.length?t:2*e.length-t-1;"m"!==e[n].id&&(e[n].style.strokeOpacity=l);let g=e.length%100;await s((t<e.length?t>=e.length-g:t>e.length&&t-e.length<=g)?20+(100-g)/100*75:10)}a=!1},!0);</script></body></html>' ) ) ) ), _getTraits(tokenId) ); } // Royalties function royaltyInfo(uint256, uint256 salePrice) external pure returns (address receiver, uint256 royaltyAmount) { unchecked { return (_VAULT_ADDRESS, (salePrice * 250) / 10_000); } } // IERC165 function supportsInterface(bytes4 interfaceId) public view virtual override(ERC721A) returns (bool) { return interfaceId == type(IERC2981).interfaceId || super.supportsInterface(interfaceId); } }
// SPDX-License-Identifier: MIT // ERC721A Contracts v4.2.3 // Creator: Chiru Labs pragma solidity ^0.8.4; import "./IERC721A.sol"; /** * @dev Interface of ERC721 token receiver. */ interface ERC721A__IERC721Receiver { 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()`. * * 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 ERC721A is IERC721A { // Bypass for a `--via-ir` bug (https://github.com/chiru-labs/ERC721A/pull/364). struct TokenApprovalRef { address value; } // ============================================================= // 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; // ============================================================= // STORAGE // ============================================================= // The next token ID to be minted. uint256 private _currentIndex; // The number of tokens burned. uint256 private _burnCounter; // Token name string private _name; // Token symbol string private _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) private _packedOwnerships; // Mapping owner address to address data. // // Bits Layout: // - [0..63] `balance` // - [64..127] `numberMinted` // - [128..191] `numberBurned` // - [192..255] `aux` mapping(address => uint256) private _packedAddressData; // Mapping from token ID to approved address. mapping(uint256 => TokenApprovalRef) private _tokenApprovals; // Mapping from owner to operator approvals mapping(address => mapping(address => bool)) private _operatorApprovals; // ============================================================= // CONSTRUCTOR // ============================================================= constructor(string memory name_, string memory symbol_) { _name = name_; _symbol = symbol_; _currentIndex = _startTokenId(); } // ============================================================= // TOKEN COUNTING OPERATIONS // ============================================================= /** * @dev Returns the starting token ID. * To change the starting token ID, please override this function. */ function _startTokenId() internal view virtual returns (uint256) { return 1; } /** * @dev Returns the next token ID to be minted. */ function _nextTokenId() internal view virtual returns (uint256) { return _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) { // Counter underflow is impossible as _burnCounter cannot be incremented // more than `_currentIndex - _startTokenId()` times. unchecked { return _currentIndex - _burnCounter - _startTokenId(); } } /** * @dev Returns the total amount of tokens minted in the contract. */ function _totalMinted() internal view virtual returns (uint256) { // Counter underflow is impossible as `_currentIndex` does not decrement, // and it is initialized to `_startTokenId()`. unchecked { return _currentIndex - _startTokenId(); } } /** * @dev Returns the total number of tokens burned. */ function _totalBurned() internal view virtual returns (uint256) { return _burnCounter; } // ============================================================= // 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(); return _packedAddressData[owner] & _BITMASK_ADDRESS_DATA_ENTRY; } /** * Returns the number of tokens minted by `owner`. */ function _numberMinted(address owner) internal view returns (uint256) { return (_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 (_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(_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 = _packedAddressData[owner]; uint256 auxCasted; // Cast `aux` with assembly to avoid redundant masking. assembly { auxCasted := aux } packed = (packed & _BITMASK_AUX_COMPLEMENT) | (auxCasted << _BITPOS_AUX); _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 _name; } /** * @dev Returns the token collection symbol. */ function symbol() public view virtual override returns (string memory) { return _symbol; } /** * @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token. */ function tokenURI(uint256) public view virtual override 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(_packedOwnerships[index]); } /** * @dev Initializes the ownership slot minted at `index` for efficiency purposes. */ function _initializeOwnershipAt(uint256 index) internal virtual { if (_packedOwnerships[index] == 0) { _packedOwnerships[index] = _packedOwnershipOf(index); } } /** * Returns the packed ownership data of `tokenId`. */ function _packedOwnershipOf(uint256 tokenId) private view returns (uint256) { uint256 curr = tokenId; unchecked { if (_startTokenId() <= curr) { if (curr < _currentIndex) { uint256 packed = _packedOwnerships[curr]; // If not burned. if (packed & _BITMASK_BURNED == 0) { // 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, `curr` will not underflow. // // We can directly compare the packed value. // If the address is zero, packed will be zero. while (packed == 0) { packed = _packedOwnerships[--curr]; } return packed; } } } } revert OwnerQueryForNonexistentToken(); } /** * @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. * 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) public payable virtual override { address owner = ownerOf(tokenId); if (_msgSenderERC721A() != owner) { if (!isApprovedForAll(owner, _msgSenderERC721A())) { revert ApprovalCallerNotOwnerNorApproved(); } } _tokenApprovals[tokenId].value = to; emit Approval(owner, to, tokenId); } /** * @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(); return _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 { _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 _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) { return _startTokenId() <= tokenId && tokenId < _currentIndex // If within bounds, && _packedOwnerships[tokenId] & _BITMASK_BURNED == 0; // and not 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) { TokenApprovalRef storage tokenApproval = _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); if (address(uint160(prevOwnershipPacked)) != from) revert TransferFromIncorrectOwner(); (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(); } if (to == address(0)) revert TransferToZeroAddress(); _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. --_packedAddressData[from]; // Updates: `balance -= 1`. ++_packedAddressData[to]; // Updates: `balance += 1`. // Updates: // - `address` to the next owner. // - `startTimestamp` to the timestamp of transfering. // - `burned` to `false`. // - `nextInitialized` to `true`. _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 (_packedOwnerships[nextTokenId] == 0) { // If the next slot is within bounds. if (nextTokenId != _currentIndex) { // Initialize the next slot to maintain correctness for `ownerOf(tokenId + 1)`. _packedOwnerships[nextTokenId] = prevOwnershipPacked; } } } } emit Transfer(from, to, tokenId); _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(); } } } /** * @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__IERC721Receiver(to).onERC721Received(_msgSenderERC721A(), from, tokenId, _data) returns ( bytes4 retval ) { return retval == ERC721A__IERC721Receiver(to).onERC721Received.selector; } catch (bytes memory reason) { if (reason.length == 0) { revert TransferToNonERC721ReceiverImplementer(); } else { 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 = _currentIndex; if (quantity == 0) revert MintZeroQuantity(); _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: // - `balance += quantity`. // - `numberMinted += quantity`. // // We can directly add to the `balance` and `numberMinted`. _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`. _packedOwnerships[startTokenId] = _packOwnershipData(to, _nextInitializedFlag(quantity) | _nextExtraData(address(0), to, 0)); uint256 toMasked; uint256 end = startTokenId + quantity; // Use assembly to loop and emit the `Transfer` event for gas savings. // The duplicated `log4` removes an extra check and reduces stack juggling. // The assembly, together with the surrounding Solidity code, have been // delicately arranged to nudge the compiler into producing optimized opcodes. assembly { // Mask `to` to the lower 160 bits, in case the upper bits somehow aren't clean. toMasked := and(to, _BITMASK_ADDRESS) // 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`. startTokenId // `tokenId`. ) // The `iszero(eq(,))` check ensures that large values of `quantity` // that overflows uint256 will make the loop run out of gas. // The compiler will optimize the `iszero` away for performance. for { let tokenId := add(startTokenId, 1) } iszero(eq(tokenId, end)) { tokenId := add(tokenId, 1) } { // Emit the `Transfer` event. Similar to above. log4(0, 0, _TRANSFER_EVENT_SIGNATURE, 0, toMasked, tokenId) } } if (toMasked == 0) revert MintToZeroAddress(); _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 = _currentIndex; if (to == address(0)) revert MintToZeroAddress(); if (quantity == 0) revert MintZeroQuantity(); if (quantity > _MAX_MINT_ERC2309_QUANTITY_LIMIT) revert MintERC2309QuantityExceedsLimit(); _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`. _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`. _packedOwnerships[startTokenId] = _packOwnershipData(to, _nextInitializedFlag(quantity) | _nextExtraData(address(0), to, 0)); emit ConsecutiveTransfer(startTokenId, startTokenId + quantity - 1, address(0), to); _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 = _currentIndex; uint256 index = end - quantity; do { if (!_checkContractOnERC721Received(address(0), to, index++, _data)) { revert TransferToNonERC721ReceiverImplementer(); } } while (index < end); // Reentrancy protection. if (_currentIndex != end) revert(); } } } /** * @dev Equivalent to `_safeMint(to, quantity, '')`. */ function _safeMint(address to, uint256 quantity) internal virtual { _safeMint(to, quantity, ""); } // ============================================================= // 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(); } } _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;`. _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`. _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 (_packedOwnerships[nextTokenId] == 0) { // If the next slot is within bounds. if (nextTokenId != _currentIndex) { // Initialize the next slot to maintain correctness for `ownerOf(tokenId + 1)`. _packedOwnerships[nextTokenId] = prevOwnershipPacked; } } } } emit Transfer(from, address(0), tokenId); _afterTokenTransfers(from, address(0), tokenId, 1); // Overflow not possible, as _burnCounter cannot be exceed _currentIndex times. unchecked { _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 = _packedOwnerships[index]; if (packed == 0) revert OwnershipNotInitializedForExtraData(); uint256 extraDataCasted; // Cast `extraData` with assembly to avoid redundant masking. assembly { extraDataCasted := extraData } packed = (packed & _BITMASK_EXTRA_DATA_COMPLEMENT) | (extraDataCasted << _BITPOS_EXTRA_DATA); _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) } } }
// SPDX-License-Identifier: MIT pragma solidity >=0.8.17 .0; interface NFTEventsAndErrors { error AlreadyCommenced(); error PublicMintNotEnabled(); error AllowListMintCapPerWalletExceeded(); error AllowListMintCapExceeded(); error PublicMintMaxPerTransactionExceeded(); error MintNotStarted(); error MaxSupplyReached(); error IncorrectPayment(); error MsgSenderDoesNotOwnXXYYZZToken(); error MsgSenderNotTokenOwner(); event MetadataUpdate(uint256 tokenId); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.7.0) (utils/Base64.sol) pragma solidity ^0.8.0; /** * @dev Provides a set of functions to operate with Base64 strings. * * _Available since v4.5._ */ library Base64 { /** * @dev Base64 Encoding/Decoding Table */ string internal constant _TABLE = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; /** * @dev Converts a `bytes` to its Bytes64 `string` representation. */ function encode(bytes memory data) internal pure returns (string memory) { /** * Inspired by Brecht Devos (Brechtpd) implementation - MIT licence * https://github.com/Brechtpd/base64/blob/e78d9fd951e7b0977ddca77d92dc85183770daf4/base64.sol */ if (data.length == 0) return ""; // Loads the table into memory string memory table = _TABLE; // Encoding takes 3 bytes chunks of binary data from `bytes` data parameter // and split into 4 numbers of 6 bits. // The final Base64 length should be `bytes` data length multiplied by 4/3 rounded up // - `data.length + 2` -> Round up // - `/ 3` -> Number of 3-bytes chunks // - `4 *` -> 4 characters for each chunk string memory result = new string(4 * ((data.length + 2) / 3)); /// @solidity memory-safe-assembly assembly { // Prepare the lookup table (skip the first "length" byte) let tablePtr := add(table, 1) // Prepare result pointer, jump over length let resultPtr := add(result, 32) // Run over the input, 3 bytes at a time for { let dataPtr := data let endPtr := add(data, mload(data)) } lt(dataPtr, endPtr) { } { // Advance 3 bytes dataPtr := add(dataPtr, 3) let input := mload(dataPtr) // To write each character, shift the 3 bytes (18 bits) chunk // 4 times in blocks of 6 bits for each character (18, 12, 6, 0) // and apply logical AND with 0x3F which is the number of // the previous character in the ASCII table prior to the Base64 Table // The result is then added to the table to get the character to write, // and finally write it in the result pointer but with a left shift // of 256 (1 byte) - 8 (1 ASCII char) = 248 bits mstore8(resultPtr, mload(add(tablePtr, and(shr(18, input), 0x3F)))) resultPtr := add(resultPtr, 1) // Advance mstore8(resultPtr, mload(add(tablePtr, and(shr(12, input), 0x3F)))) resultPtr := add(resultPtr, 1) // Advance mstore8(resultPtr, mload(add(tablePtr, and(shr(6, input), 0x3F)))) resultPtr := add(resultPtr, 1) // Advance mstore8(resultPtr, mload(add(tablePtr, and(input, 0x3F)))) resultPtr := add(resultPtr, 1) // Advance } // When data `bytes` is not exactly 3 bytes long // it is padded with `=` characters at the end switch mod(mload(data), 3) case 1 { mstore8(sub(resultPtr, 1), 0x3d) mstore8(sub(resultPtr, 2), 0x3d) } case 2 { mstore8(sub(resultPtr, 1), 0x3d) } } return result; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.6.0) (interfaces/IERC2981.sol) pragma solidity ^0.8.0; import "../utils/introspection/IERC165.sol"; /** * @dev Interface for the NFT Royalty Standard. * * A standardized way to retrieve royalty payment information for non-fungible tokens (NFTs) to enable universal * support for royalty payments across all NFT marketplaces and ecosystem participants. * * _Available since v4.5._ */ interface IERC2981 is IERC165 { /** * @dev Returns how much royalty is owed and to whom, based on a sale price that may be denominated in any unit of * exchange. The royalty amount is denominated and should be paid in that same unit of exchange. */ function royaltyInfo(uint256 tokenId, uint256 salePrice) external view returns (address receiver, uint256 royaltyAmount); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (interfaces/IERC721.sol) pragma solidity ^0.8.0; import "../token/ERC721/IERC721.sol";
// SPDX-License-Identifier: MIT pragma solidity >=0.8.17 .0; import { LibPRNG } from "./LibPRNG.sol"; import { LibString } from "./LibString.sol"; /// @title Utils /// @author Aspyn Palatnick (aspyn.eth, stuckinaboot.eth) /// @notice Utility functions for constructing onchain Together NFT library Utils { using LibPRNG for LibPRNG.PRNG; using LibString for uint256; string internal constant BASE64_TABLE = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; function svgToURI(string memory _source) internal pure returns (string memory) { return string.concat("data:image/svg+xml;base64,", encodeBase64(bytes(_source))); } function formatTokenURI( uint256 _tokenId, string memory _imageURI, string memory _animationURI, string memory _properties ) internal pure returns (string memory) { return string.concat( "data:application/json;base64,", encodeBase64( bytes( string.concat( '{"name":"Together #', _tokenId.toString(), '","description":"Together is a generative onchain NFT with colorful art formed by its minters. The art of each token builds on the art of all previous tokens plus a new line generated at the time of mint. Tap once to unwind your art. Tap twice to unwind your mind.","attributes":', _properties, ',"image":"', _imageURI, '","animation_url":"', _animationURI, '"}' ) ) ) ); } function getTrait( string memory traitType, string memory value, bool isNumberValue, bool includeTrailingComma ) internal pure returns (string memory) { return string.concat( '{"trait_type":"', traitType, '","value":', isNumberValue ? value : string.concat('"', value, '"'), "}", includeTrailingComma ? "," : "" ); } // Encode some bytes in base64 // https://gist.github.com/mbvissers/8ba9ac1eca9ed0ef6973bd49b3c999ba function encodeBase64(bytes memory data) internal pure returns (string memory) { if (data.length == 0) return ""; // load the table into memory string memory table = BASE64_TABLE; unchecked { // multiply by 4/3 rounded up uint256 encodedLen = 4 * ((data.length + 2) / 3); // add some extra buffer at the end required for the writing string memory result = new string(encodedLen + 32); assembly { // set the actual output length mstore(result, encodedLen) // prepare the lookup table let tablePtr := add(table, 1) // input ptr let dataPtr := data let endPtr := add(dataPtr, mload(data)) // result ptr, jump over length let resultPtr := add(result, 32) // run over the input, 3 bytes at a time for { } lt(dataPtr, endPtr) { } { dataPtr := add(dataPtr, 3) // read 3 bytes let input := mload(dataPtr) // write 4 characters mstore(resultPtr, shl(248, mload(add(tablePtr, and(shr(18, input), 0x3F))))) resultPtr := add(resultPtr, 1) mstore(resultPtr, shl(248, mload(add(tablePtr, and(shr(12, input), 0x3F))))) resultPtr := add(resultPtr, 1) mstore(resultPtr, shl(248, mload(add(tablePtr, and(shr(6, input), 0x3F))))) resultPtr := add(resultPtr, 1) mstore(resultPtr, shl(248, mload(add(tablePtr, and(input, 0x3F))))) resultPtr := add(resultPtr, 1) } // padding with '=' switch mod(mload(data), 3) case 1 { mstore(sub(resultPtr, 2), shl(240, 0x3d3d)) } case 2 { mstore(sub(resultPtr, 1), shl(248, 0x3d)) } } return result; } } function getPackedRGBColor(bytes32 seed) internal pure returns (uint24) { LibPRNG.PRNG memory prng; prng.seed(seed); unchecked { return (uint24(prng.uniform(256)) << 16) + (uint24(prng.uniform(256)) << 8) + uint8(prng.uniform(256)); } } function getRGBStr(uint24 packedRgb) internal pure returns (string memory) { return string.concat("#", uint256(packedRgb).toHexStringNoPrefix(3)); } }
// SPDX-License-Identifier: MIT pragma solidity >=0.8.17 .0; /// @title Constants /// @author Aspyn Palatnick (aspyn.eth, stuckinaboot.eth) /// @notice Constants for constructing onchain Together NFT contract Constants { uint256 public constant PRICE = 0.01 ether; address payable internal constant _VAULT_ADDRESS = payable(address(0x39Ab90066cec746A032D67e4fe3378f16294CF6b)); address internal constant _XXYYZZ_TOKEN_ADDRESS = address(0xFf6000a85baAc9c4854fAA7155e70BA850BF726b); uint8 internal constant _MINT_AMOUNT_DURING_COMMENCE = 5; uint8 internal constant _MAX_PUBLIC_MINT_AMOUNT_PER_TRANSACTION = 5; }
// SPDX-License-Identifier: MIT pragma solidity >=0.8.17 .0; import { Utils } from "./Utils.sol"; import { Trigonometry } from "./Trigonometry.sol"; import { svg } from "./SVG.sol"; import { LibPRNG } from "./LibPRNG.sol"; import { LibString } from "./LibString.sol"; /// @title ColorfulArt /// @author Aspyn Palatnick (aspyn.eth, stuckinaboot.eth) /// @notice ColorfulArt provides utility functions for creating colorful polygon art contract ColorfulArt { using LibPRNG for LibPRNG.PRNG; using LibString for uint256; struct Point { // Scale by 1e18 uint256 x; uint256 y; } uint16 internal constant MAX_POINTS_TOTAL = 1000; uint24[MAX_POINTS_TOTAL + 1] internal tokenToColor; uint24[MAX_POINTS_TOTAL + 1] internal tokenToBackgroundColor; uint8 internal constant MAX_POINTS_PER_POLYGON = 100; uint8 private constant LINE_WIDTH = 10; uint8 private constant MAX_RADIUS = 120; bool internal commenced; function polarToCartesian(uint256 radius, uint256 angleInDegrees) internal pure returns (Point memory) { int256 angleInRadians = ((int256(angleInDegrees)) * int256(Trigonometry.PI)) / (180.0 * 1e18) + // Add 2 PI to ensure radians is always positive int256( Trigonometry.TWO_PI - // Rotate 90 degrees counterclockwise Trigonometry.PI / 2.0 ); return // Point has both x and y scaled by 1e18 Point({ x: uint256( int256( // Scale by 1e18 200 * 1e18 ) + ((int256(radius) * Trigonometry.cos(uint256(angleInRadians)))) ), y: uint256( int256( // Scale by 1e18 200 * 1e18 ) + ((int256(radius) * Trigonometry.sin(uint256(angleInRadians)))) ) }); } function getDecimalStringFrom1e18ScaledUint256(uint256 scaled) internal pure returns (string memory decimal) { uint256 partBeforeDecimal = scaled / 1e18; uint256 partAfterDecimal = (scaled % 1e18); if (partAfterDecimal > 1e17) { // Throw out last 12 digits, as that much precision is unnecessary and bloats the string size partAfterDecimal = partAfterDecimal / 1e12; } return string.concat(partBeforeDecimal.toString(), ".", partAfterDecimal.toString()); } function polygon(uint256 radius, uint16 numPoints) internal pure returns (Point[] memory points) { points = new Point[](numPoints); // Degrees scaled by 1e18 for precision unchecked { uint256 degreeIncrement = (360 * 1e18) / numPoints; for (uint32 i; i < numPoints; ++i) { uint256 angleInDegrees = degreeIncrement * i; points[i] = polarToCartesian(radius, angleInDegrees); } } } function linesSvgFromPoints( Point[] memory points, uint16 startColorIdx, // This is the token id uint16 pointWithIdIdx ) internal view returns (string memory linesSvg) { if (points.length == 1) { // Return a dot in the center return svg.line( string.concat( svg.prop("x1", "200"), svg.prop("y1", "200"), svg.prop("x2", "200"), svg.prop("y2", "200"), svg.prop("stroke-linecap", "round"), svg.prop("stroke", Utils.getRGBStr(tokenToColor[startColorIdx])), svg.prop("id", "m") ) ); } unchecked { for (uint16 i; i < points.length; ++i) { bool isEnd = i + 1 == points.length; uint32 colorIdx = i + startColorIdx; linesSvg = string.concat( linesSvg, svg.line( string.concat( svg.prop("x1", getDecimalStringFrom1e18ScaledUint256(points[i].x)), svg.prop("y1", getDecimalStringFrom1e18ScaledUint256(points[i].y)), svg.prop("x2", getDecimalStringFrom1e18ScaledUint256(isEnd ? points[0].x : points[i + 1].x)), svg.prop("y2", getDecimalStringFrom1e18ScaledUint256(isEnd ? points[0].y : points[i + 1].y)), svg.prop("stroke", Utils.getRGBStr(tokenToColor[colorIdx])), pointWithIdIdx == colorIdx ? svg.prop("id", "m") : "" ) ) ); } } } function art(uint16 numberOfPoints) public view returns (string memory) { if (!commenced) { return ""; } string memory lines; unchecked { uint256 fullPolygonsCount = numberOfPoints / MAX_POINTS_PER_POLYGON; for (uint8 i; i < fullPolygonsCount; ++i) { Point[] memory polyPoints = polygon(MAX_RADIUS - i * LINE_WIDTH, MAX_POINTS_PER_POLYGON); lines = string.concat( lines, linesSvgFromPoints(polyPoints, uint16(i * MAX_POINTS_PER_POLYGON + 1), numberOfPoints) ); } uint16 remainingPoints = numberOfPoints % MAX_POINTS_PER_POLYGON; if (remainingPoints > 0) { lines = string.concat( lines, linesSvgFromPoints( polygon(uint16(MAX_RADIUS - fullPolygonsCount * LINE_WIDTH), remainingPoints), uint16(fullPolygonsCount * MAX_POINTS_PER_POLYGON + 1), numberOfPoints ) ); } } return string.concat( '<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%" viewBox="0 0 400 400" style="background:', Utils.getRGBStr(tokenToBackgroundColor[numberOfPoints]), ';display:block;margin:auto"><style>line{stroke-width:10;}</style>', lines, "</svg>" ); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.4; /// @notice Library for converting numbers into strings and other string operations. /// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/LibString.sol) /// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/LibString.sol) library LibString { /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* CUSTOM ERRORS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev The `length` of the output is too small to contain all the hex digits. error HexLengthInsufficient(); /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* CONSTANTS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev The constant returned when the `search` is not found in the string. uint256 internal constant NOT_FOUND = type(uint256).max; /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* DECIMAL OPERATIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Returns the base 10 decimal representation of `value`. function toString(uint256 value) internal pure returns (string memory str) { /// @solidity memory-safe-assembly 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. str := add(mload(0x40), 0x80) // Update the free memory pointer to allocate. mstore(0x40, add(str, 0x20)) // Zeroize the slot after the string. mstore(str, 0) // Cache the end of the memory to calculate the length later. let end := str let w := not(0) // Tsk. // We write the string from rightmost digit to leftmost digit. // The following is essentially a do-while loop that also handles the zero case. for { let temp := value } 1 { } { str := add(str, w) // `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) 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 Returns the base 10 decimal representation of `value`. function toString(int256 value) internal pure returns (string memory str) { if (value >= 0) { return toString(uint256(value)); } unchecked { str = toString(uint256(-value)); } /// @solidity memory-safe-assembly assembly { // We still have some spare memory space on the left, // as we have allocated 3 words (96 bytes) for up to 78 digits. let length := mload(str) // Load the string length. mstore(str, 0x2d) // Store the '-' character. str := sub(str, 1) // Move back the string pointer by a byte. mstore(str, add(length, 1)) // Update the string length. } } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* HEXADECIMAL OPERATIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Returns the hexadecimal representation of `value`, /// left-padded to an input length of `length` bytes. /// The output is prefixed with "0x" encoded using 2 hexadecimal digits per byte, /// giving a total length of `length * 2 + 2` bytes. /// Reverts if `length` is too small for the output to contain all the digits. function toHexString(uint256 value, uint256 length) internal pure returns (string memory str) { str = toHexStringNoPrefix(value, length); /// @solidity memory-safe-assembly assembly { let strLength := add(mload(str), 2) // Compute the length. mstore(str, 0x3078) // Write the "0x" prefix. str := sub(str, 2) // Move the pointer. mstore(str, strLength) // Write the length. } } /// @dev Returns the hexadecimal representation of `value`, /// left-padded to an input length of `length` bytes. /// The output is prefixed with "0x" encoded using 2 hexadecimal digits per byte, /// giving a total length of `length * 2` bytes. /// Reverts if `length` is too small for the output to contain all the digits. function toHexStringNoPrefix(uint256 value, uint256 length) internal pure returns (string memory str) { /// @solidity memory-safe-assembly assembly { // We need 0x20 bytes for the trailing zeros padding, `length * 2` bytes // for the digits, 0x02 bytes for the prefix, and 0x20 bytes for the length. // We add 0x20 to the total and round down to a multiple of 0x20. // (0x20 + 0x20 + 0x02 + 0x20) = 0x62. str := add(mload(0x40), and(add(shl(1, length), 0x42), not(0x1f))) // Allocate the memory. mstore(0x40, add(str, 0x20)) // Zeroize the slot after the string. mstore(str, 0) // Cache the end to calculate the length later. let end := str // Store "0123456789abcdef" in scratch space. mstore(0x0f, 0x30313233343536373839616263646566) let start := sub(str, add(length, length)) let w := not(1) // Tsk. let temp := value // We write the string from rightmost digit to leftmost digit. // The following is essentially a do-while loop that also handles the zero case. for { } 1 { } { str := add(str, w) // `sub(str, 2)`. mstore8(add(str, 1), mload(and(temp, 15))) mstore8(str, mload(and(shr(4, temp), 15))) temp := shr(8, temp) if iszero(xor(str, start)) { break } } if temp { // Store the function selector of `HexLengthInsufficient()`. mstore(0x00, 0x2194895a) // Revert with (offset, size). revert(0x1c, 0x04) } // Compute the string's length. let strLength := sub(end, str) // Move the pointer and write the length. str := sub(str, 0x20) mstore(str, strLength) } } /// @dev Returns the hexadecimal representation of `value`. /// The output is prefixed with "0x" and encoded using 2 hexadecimal digits per byte. /// As address are 20 bytes long, the output will left-padded to have /// a length of `20 * 2 + 2` bytes. function toHexString(uint256 value) internal pure returns (string memory str) { str = toHexStringNoPrefix(value); /// @solidity memory-safe-assembly assembly { let strLength := add(mload(str), 2) // Compute the length. mstore(str, 0x3078) // Write the "0x" prefix. str := sub(str, 2) // Move the pointer. mstore(str, strLength) // Write the length. } } /// @dev Returns the hexadecimal representation of `value`. /// The output is encoded using 2 hexadecimal digits per byte. /// As address are 20 bytes long, the output will left-padded to have /// a length of `20 * 2` bytes. function toHexStringNoPrefix(uint256 value) internal pure returns (string memory str) { /// @solidity memory-safe-assembly assembly { // We need 0x20 bytes for the trailing zeros padding, 0x20 bytes for the length, // 0x02 bytes for the prefix, and 0x40 bytes for the digits. // The next multiple of 0x20 above (0x20 + 0x20 + 0x02 + 0x40) is 0xa0. str := add(mload(0x40), 0x80) // Allocate the memory. mstore(0x40, add(str, 0x20)) // Zeroize the slot after the string. mstore(str, 0) // Cache the end to calculate the length later. let end := str // Store "0123456789abcdef" in scratch space. mstore(0x0f, 0x30313233343536373839616263646566) let w := not(1) // Tsk. // We write the string from rightmost digit to leftmost digit. // The following is essentially a do-while loop that also handles the zero case. for { let temp := value } 1 { } { str := add(str, w) // `sub(str, 2)`. mstore8(add(str, 1), mload(and(temp, 15))) mstore8(str, mload(and(shr(4, temp), 15))) temp := shr(8, temp) if iszero(temp) { break } } // Compute the string's length. let strLength := sub(end, str) // Move the pointer and write the length. str := sub(str, 0x20) mstore(str, strLength) } } /// @dev Returns the hexadecimal representation of `value`. /// The output is prefixed with "0x", encoded using 2 hexadecimal digits per byte, /// and the alphabets are capitalized conditionally according to /// https://eips.ethereum.org/EIPS/eip-55 function toHexStringChecksumed(address value) internal pure returns (string memory str) { str = toHexString(value); /// @solidity memory-safe-assembly assembly { let mask := shl(6, div(not(0), 255)) // `0b010000000100000000 ...` let o := add(str, 0x22) let hashed := and(keccak256(o, 40), mul(34, mask)) // `0b10001000 ... ` let t := shl(240, 136) // `0b10001000 << 240` for { let i := 0 } 1 { } { mstore(add(i, i), mul(t, byte(i, hashed))) i := add(i, 1) if eq(i, 20) { break } } mstore(o, xor(mload(o), shr(1, and(mload(0x00), and(mload(o), mask))))) o := add(o, 0x20) mstore(o, xor(mload(o), shr(1, and(mload(0x20), and(mload(o), mask))))) } } /// @dev Returns the hexadecimal representation of `value`. /// The output is prefixed with "0x" and encoded using 2 hexadecimal digits per byte. function toHexString(address value) internal pure returns (string memory str) { str = toHexStringNoPrefix(value); /// @solidity memory-safe-assembly assembly { let strLength := add(mload(str), 2) // Compute the length. mstore(str, 0x3078) // Write the "0x" prefix. str := sub(str, 2) // Move the pointer. mstore(str, strLength) // Write the length. } } /// @dev Returns the hexadecimal representation of `value`. /// The output is encoded using 2 hexadecimal digits per byte. function toHexStringNoPrefix(address value) internal pure returns (string memory str) { /// @solidity memory-safe-assembly assembly { str := mload(0x40) // Allocate the memory. // We need 0x20 bytes for the trailing zeros padding, 0x20 bytes for the length, // 0x02 bytes for the prefix, and 0x28 bytes for the digits. // The next multiple of 0x20 above (0x20 + 0x20 + 0x02 + 0x28) is 0x80. mstore(0x40, add(str, 0x80)) // Store "0123456789abcdef" in scratch space. mstore(0x0f, 0x30313233343536373839616263646566) str := add(str, 2) mstore(str, 40) let o := add(str, 0x20) mstore(add(o, 40), 0) value := shl(96, value) // We write the string from rightmost digit to leftmost digit. // The following is essentially a do-while loop that also handles the zero case. for { let i := 0 } 1 { } { let p := add(o, add(i, i)) let temp := byte(i, value) mstore8(add(p, 1), mload(and(temp, 15))) mstore8(p, mload(shr(4, temp))) i := add(i, 1) if eq(i, 20) { break } } } } /// @dev Returns the hex encoded string from the raw bytes. /// The output is encoded using 2 hexadecimal digits per byte. function toHexString(bytes memory raw) internal pure returns (string memory str) { str = toHexStringNoPrefix(raw); /// @solidity memory-safe-assembly assembly { let strLength := add(mload(str), 2) // Compute the length. mstore(str, 0x3078) // Write the "0x" prefix. str := sub(str, 2) // Move the pointer. mstore(str, strLength) // Write the length. } } /// @dev Returns the hex encoded string from the raw bytes. /// The output is encoded using 2 hexadecimal digits per byte. function toHexStringNoPrefix(bytes memory raw) internal pure returns (string memory str) { /// @solidity memory-safe-assembly assembly { let length := mload(raw) str := add(mload(0x40), 2) // Skip 2 bytes for the optional prefix. mstore(str, add(length, length)) // Store the length of the output. // Store "0123456789abcdef" in scratch space. mstore(0x0f, 0x30313233343536373839616263646566) let o := add(str, 0x20) let end := add(raw, length) for { } iszero(eq(raw, end)) { } { raw := add(raw, 1) mstore8(add(o, 1), mload(and(mload(raw), 15))) mstore8(o, mload(and(shr(4, mload(raw)), 15))) o := add(o, 2) } mstore(o, 0) // Zeroize the slot after the string. mstore(0x40, and(add(o, 31), not(31))) // Allocate the memory. } } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* RUNE STRING OPERATIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Returns the number of UTF characters in the string. function runeCount(string memory s) internal pure returns (uint256 result) { /// @solidity memory-safe-assembly assembly { if mload(s) { mstore(0x00, div(not(0), 255)) mstore(0x20, 0x0202020202020202020202020202020202020202020202020303030304040506) let o := add(s, 0x20) let end := add(o, mload(s)) for { result := 1 } 1 { result := add(result, 1) } { o := add(o, byte(0, mload(shr(250, mload(o))))) if iszero(lt(o, end)) { break } } } } } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* BYTE STRING OPERATIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ // For performance and bytecode compactness, all indices of the following operations // are byte (ASCII) offsets, not UTF character offsets. /// @dev Returns `subject` all occurrences of `search` replaced with `replacement`. function replace( string memory subject, string memory search, string memory replacement ) internal pure returns (string memory result) { /// @solidity memory-safe-assembly assembly { let subjectLength := mload(subject) let searchLength := mload(search) let replacementLength := mload(replacement) subject := add(subject, 0x20) search := add(search, 0x20) replacement := add(replacement, 0x20) result := add(mload(0x40), 0x20) let subjectEnd := add(subject, subjectLength) if iszero(gt(searchLength, subjectLength)) { let subjectSearchEnd := add(sub(subjectEnd, searchLength), 1) let h := 0 if iszero(lt(searchLength, 32)) { h := keccak256(search, searchLength) } let m := shl(3, sub(32, and(searchLength, 31))) let s := mload(search) for { } 1 { } { let t := mload(subject) // Whether the first `searchLength % 32` bytes of // `subject` and `search` matches. if iszero(shr(m, xor(t, s))) { if h { if iszero(eq(keccak256(subject, searchLength), h)) { mstore(result, t) result := add(result, 1) subject := add(subject, 1) if iszero(lt(subject, subjectSearchEnd)) { break } continue } } // Copy the `replacement` one word at a time. for { let o := 0 } 1 { } { mstore(add(result, o), mload(add(replacement, o))) o := add(o, 0x20) if iszero(lt(o, replacementLength)) { break } } result := add(result, replacementLength) subject := add(subject, searchLength) if searchLength { if iszero(lt(subject, subjectSearchEnd)) { break } continue } } mstore(result, t) result := add(result, 1) subject := add(subject, 1) if iszero(lt(subject, subjectSearchEnd)) { break } } } let resultRemainder := result result := add(mload(0x40), 0x20) let k := add(sub(resultRemainder, result), sub(subjectEnd, subject)) // Copy the rest of the string one word at a time. for { } lt(subject, subjectEnd) { } { mstore(resultRemainder, mload(subject)) resultRemainder := add(resultRemainder, 0x20) subject := add(subject, 0x20) } result := sub(result, 0x20) // Zeroize the slot after the string. let last := add(add(result, 0x20), k) mstore(last, 0) // Allocate memory for the length and the bytes, // rounded up to a multiple of 32. mstore(0x40, and(add(last, 31), not(31))) // Store the length of the result. mstore(result, k) } } /// @dev Returns the byte index of the first location of `search` in `subject`, /// searching from left to right, starting from `from`. /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found. function indexOf(string memory subject, string memory search, uint256 from) internal pure returns (uint256 result) { /// @solidity memory-safe-assembly assembly { for { let subjectLength := mload(subject) } 1 { } { if iszero(mload(search)) { if iszero(gt(from, subjectLength)) { result := from break } result := subjectLength break } let searchLength := mload(search) let subjectStart := add(subject, 0x20) result := not(0) // Initialize to `NOT_FOUND`. subject := add(subjectStart, from) let end := add(sub(add(subjectStart, subjectLength), searchLength), 1) let m := shl(3, sub(32, and(searchLength, 31))) let s := mload(add(search, 0x20)) if iszero(and(lt(subject, end), lt(from, subjectLength))) { break } if iszero(lt(searchLength, 32)) { for { let h := keccak256(add(search, 0x20), searchLength) } 1 { } { if iszero(shr(m, xor(mload(subject), s))) { if eq(keccak256(subject, searchLength), h) { result := sub(subject, subjectStart) break } } subject := add(subject, 1) if iszero(lt(subject, end)) { break } } break } for { } 1 { } { if iszero(shr(m, xor(mload(subject), s))) { result := sub(subject, subjectStart) break } subject := add(subject, 1) if iszero(lt(subject, end)) { break } } break } } } /// @dev Returns the byte index of the first location of `search` in `subject`, /// searching from left to right. /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found. function indexOf(string memory subject, string memory search) internal pure returns (uint256 result) { result = indexOf(subject, search, 0); } /// @dev Returns the byte index of the first location of `search` in `subject`, /// searching from right to left, starting from `from`. /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found. function lastIndexOf( string memory subject, string memory search, uint256 from ) internal pure returns (uint256 result) { /// @solidity memory-safe-assembly assembly { for { } 1 { } { result := not(0) // Initialize to `NOT_FOUND`. let searchLength := mload(search) if gt(searchLength, mload(subject)) { break } let w := result let fromMax := sub(mload(subject), searchLength) if iszero(gt(fromMax, from)) { from := fromMax } let end := add(add(subject, 0x20), w) subject := add(add(subject, 0x20), from) if iszero(gt(subject, end)) { break } // As this function is not too often used, // we shall simply use keccak256 for smaller bytecode size. for { let h := keccak256(add(search, 0x20), searchLength) } 1 { } { if eq(keccak256(subject, searchLength), h) { result := sub(subject, add(end, 1)) break } subject := add(subject, w) // `sub(subject, 1)`. if iszero(gt(subject, end)) { break } } break } } } /// @dev Returns the byte index of the first location of `search` in `subject`, /// searching from right to left. /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found. function lastIndexOf(string memory subject, string memory search) internal pure returns (uint256 result) { result = lastIndexOf(subject, search, uint256(int256(-1))); } /// @dev Returns whether `subject` starts with `search`. function startsWith(string memory subject, string memory search) internal pure returns (bool result) { /// @solidity memory-safe-assembly assembly { let searchLength := mload(search) // Just using keccak256 directly is actually cheaper. // forgefmt: disable-next-item result := and( iszero(gt(searchLength, mload(subject))), eq(keccak256(add(subject, 0x20), searchLength), keccak256(add(search, 0x20), searchLength)) ) } } /// @dev Returns whether `subject` ends with `search`. function endsWith(string memory subject, string memory search) internal pure returns (bool result) { /// @solidity memory-safe-assembly assembly { let searchLength := mload(search) let subjectLength := mload(subject) // Whether `search` is not longer than `subject`. let withinRange := iszero(gt(searchLength, subjectLength)) // Just using keccak256 directly is actually cheaper. // forgefmt: disable-next-item result := and( withinRange, eq( keccak256( // `subject + 0x20 + max(subjectLength - searchLength, 0)`. add(add(subject, 0x20), mul(withinRange, sub(subjectLength, searchLength))), searchLength ), keccak256(add(search, 0x20), searchLength) ) ) } } /// @dev Returns `subject` repeated `times`. function repeat(string memory subject, uint256 times) internal pure returns (string memory result) { /// @solidity memory-safe-assembly assembly { let subjectLength := mload(subject) if iszero(or(iszero(times), iszero(subjectLength))) { subject := add(subject, 0x20) result := mload(0x40) let output := add(result, 0x20) for { } 1 { } { // Copy the `subject` one word at a time. for { let o := 0 } 1 { } { mstore(add(output, o), mload(add(subject, o))) o := add(o, 0x20) if iszero(lt(o, subjectLength)) { break } } output := add(output, subjectLength) times := sub(times, 1) if iszero(times) { break } } // Zeroize the slot after the string. mstore(output, 0) // Store the length. let resultLength := sub(output, add(result, 0x20)) mstore(result, resultLength) // Allocate memory for the length and the bytes, // rounded up to a multiple of 32. mstore(0x40, add(result, and(add(resultLength, 63), not(31)))) } } } /// @dev Returns a copy of `subject` sliced from `start` to `end` (exclusive). /// `start` and `end` are byte offsets. function slice(string memory subject, uint256 start, uint256 end) internal pure returns (string memory result) { /// @solidity memory-safe-assembly assembly { let subjectLength := mload(subject) if iszero(gt(subjectLength, end)) { end := subjectLength } if iszero(gt(subjectLength, start)) { start := subjectLength } if lt(start, end) { result := mload(0x40) let resultLength := sub(end, start) mstore(result, resultLength) subject := add(subject, start) let w := not(31) // Copy the `subject` one word at a time, backwards. for { let o := and(add(resultLength, 31), w) } 1 { } { mstore(add(result, o), mload(add(subject, o))) o := add(o, w) // `sub(o, 0x20)`. if iszero(o) { break } } // Zeroize the slot after the string. mstore(add(add(result, 0x20), resultLength), 0) // Allocate memory for the length and the bytes, // rounded up to a multiple of 32. mstore(0x40, add(result, and(add(resultLength, 63), w))) } } } /// @dev Returns a copy of `subject` sliced from `start` to the end of the string. /// `start` is a byte offset. function slice(string memory subject, uint256 start) internal pure returns (string memory result) { result = slice(subject, start, uint256(int256(-1))); } /// @dev Returns all the indices of `search` in `subject`. /// The indices are byte offsets. function indicesOf(string memory subject, string memory search) internal pure returns (uint256[] memory result) { /// @solidity memory-safe-assembly assembly { let subjectLength := mload(subject) let searchLength := mload(search) if iszero(gt(searchLength, subjectLength)) { subject := add(subject, 0x20) search := add(search, 0x20) result := add(mload(0x40), 0x20) let subjectStart := subject let subjectSearchEnd := add(sub(add(subject, subjectLength), searchLength), 1) let h := 0 if iszero(lt(searchLength, 32)) { h := keccak256(search, searchLength) } let m := shl(3, sub(32, and(searchLength, 31))) let s := mload(search) for { } 1 { } { let t := mload(subject) // Whether the first `searchLength % 32` bytes of // `subject` and `search` matches. if iszero(shr(m, xor(t, s))) { if h { if iszero(eq(keccak256(subject, searchLength), h)) { subject := add(subject, 1) if iszero(lt(subject, subjectSearchEnd)) { break } continue } } // Append to `result`. mstore(result, sub(subject, subjectStart)) result := add(result, 0x20) // Advance `subject` by `searchLength`. subject := add(subject, searchLength) if searchLength { if iszero(lt(subject, subjectSearchEnd)) { break } continue } } subject := add(subject, 1) if iszero(lt(subject, subjectSearchEnd)) { break } } let resultEnd := result // Assign `result` to the free memory pointer. result := mload(0x40) // Store the length of `result`. mstore(result, shr(5, sub(resultEnd, add(result, 0x20)))) // Allocate memory for result. // We allocate one more word, so this array can be recycled for {split}. mstore(0x40, add(resultEnd, 0x20)) } } } /// @dev Returns a arrays of strings based on the `delimiter` inside of the `subject` string. function split(string memory subject, string memory delimiter) internal pure returns (string[] memory result) { uint256[] memory indices = indicesOf(subject, delimiter); /// @solidity memory-safe-assembly assembly { let w := not(31) let indexPtr := add(indices, 0x20) let indicesEnd := add(indexPtr, shl(5, add(mload(indices), 1))) mstore(add(indicesEnd, w), mload(subject)) mstore(indices, add(mload(indices), 1)) let prevIndex := 0 for { } 1 { } { let index := mload(indexPtr) mstore(indexPtr, 0x60) if iszero(eq(index, prevIndex)) { let element := mload(0x40) let elementLength := sub(index, prevIndex) mstore(element, elementLength) // Copy the `subject` one word at a time, backwards. for { let o := and(add(elementLength, 31), w) } 1 { } { mstore(add(element, o), mload(add(add(subject, prevIndex), o))) o := add(o, w) // `sub(o, 0x20)`. if iszero(o) { break } } // Zeroize the slot after the string. mstore(add(add(element, 0x20), elementLength), 0) // Allocate memory for the length and the bytes, // rounded up to a multiple of 32. mstore(0x40, add(element, and(add(elementLength, 63), w))) // Store the `element` into the array. mstore(indexPtr, element) } prevIndex := add(index, mload(delimiter)) indexPtr := add(indexPtr, 0x20) if iszero(lt(indexPtr, indicesEnd)) { break } } result := indices if iszero(mload(delimiter)) { result := add(indices, 0x20) mstore(result, sub(mload(indices), 2)) } } } /// @dev Returns a concatenated string of `a` and `b`. /// Cheaper than `string.concat()` and does not de-align the free memory pointer. function concat(string memory a, string memory b) internal pure returns (string memory result) { /// @solidity memory-safe-assembly assembly { let w := not(31) result := mload(0x40) let aLength := mload(a) // Copy `a` one word at a time, backwards. for { let o := and(add(mload(a), 32), w) } 1 { } { mstore(add(result, o), mload(add(a, o))) o := add(o, w) // `sub(o, 0x20)`. if iszero(o) { break } } let bLength := mload(b) let output := add(result, mload(a)) // Copy `b` one word at a time, backwards. for { let o := and(add(bLength, 32), w) } 1 { } { mstore(add(output, o), mload(add(b, o))) o := add(o, w) // `sub(o, 0x20)`. if iszero(o) { break } } let totalLength := add(aLength, bLength) let last := add(add(result, 0x20), totalLength) // Zeroize the slot after the string. mstore(last, 0) // Stores the length. mstore(result, totalLength) // Allocate memory for the length and the bytes, // rounded up to a multiple of 32. mstore(0x40, and(add(last, 31), w)) } } /// @dev Returns a copy of the string in either lowercase or UPPERCASE. function toCase(string memory subject, bool toUpper) internal pure returns (string memory result) { /// @solidity memory-safe-assembly assembly { let length := mload(subject) if length { result := add(mload(0x40), 0x20) subject := add(subject, 1) let flags := shl(add(70, shl(5, toUpper)), 67108863) let w := not(0) for { let o := length } 1 { } { o := add(o, w) let b := and(0xff, mload(add(subject, o))) mstore8(add(result, o), xor(b, and(shr(b, flags), 0x20))) if iszero(o) { break } } // Restore the result. result := mload(0x40) // Stores the string length. mstore(result, length) // Zeroize the slot after the string. let last := add(add(result, 0x20), length) mstore(last, 0) // Allocate memory for the length and the bytes, // rounded up to a multiple of 32. mstore(0x40, and(add(last, 31), not(31))) } } } /// @dev Returns a lowercased copy of the string. function lower(string memory subject) internal pure returns (string memory result) { result = toCase(subject, false); } /// @dev Returns an UPPERCASED copy of the string. function upper(string memory subject) internal pure returns (string memory result) { result = toCase(subject, true); } /// @dev Escapes the string to be used within HTML tags. function escapeHTML(string memory s) internal pure returns (string memory result) { /// @solidity memory-safe-assembly assembly { for { let end := add(s, mload(s)) result := add(mload(0x40), 0x20) // Store the bytes of the packed offsets and strides into the scratch space. // `packed = (stride << 5) | offset`. Max offset is 20. Max stride is 6. mstore(0x1f, 0x900094) mstore(0x08, 0xc0000000a6ab) // Store ""&'<>" into the scratch space. mstore(0x00, shl(64, 0x2671756f743b26616d703b262333393b266c743b2667743b)) } iszero(eq(s, end)) { } { s := add(s, 1) let c := and(mload(s), 0xff) // Not in `["\"","'","&","<",">"]`. if iszero(and(shl(c, 1), 0x500000c400000000)) { mstore8(result, c) result := add(result, 1) continue } let t := shr(248, mload(c)) mstore(result, mload(and(t, 31))) result := add(result, shr(5, t)) } let last := result // Zeroize the slot after the string. mstore(last, 0) // Restore the result to the start of the free memory. result := mload(0x40) // Store the length of the result. mstore(result, sub(last, add(result, 0x20))) // Allocate memory for the length and the bytes, // rounded up to a multiple of 32. mstore(0x40, and(add(last, 31), not(31))) } } /// @dev Escapes the string to be used within double-quotes in a JSON. function escapeJSON(string memory s) internal pure returns (string memory result) { /// @solidity memory-safe-assembly assembly { for { let end := add(s, mload(s)) result := add(mload(0x40), 0x20) // Store "\\u0000" in scratch space. // Store "0123456789abcdef" in scratch space. // Also, store `{0x08:"b", 0x09:"t", 0x0a:"n", 0x0c:"f", 0x0d:"r"}`. // into the scratch space. mstore(0x15, 0x5c75303030303031323334353637383961626364656662746e006672) // Bitmask for detecting `["\"","\\"]`. let e := or(shl(0x22, 1), shl(0x5c, 1)) } iszero(eq(s, end)) { } { s := add(s, 1) let c := and(mload(s), 0xff) if iszero(lt(c, 0x20)) { if iszero(and(shl(c, 1), e)) { // Not in `["\"","\\"]`. mstore8(result, c) result := add(result, 1) continue } mstore8(result, 0x5c) // "\\". mstore8(add(result, 1), c) result := add(result, 2) continue } if iszero(and(shl(c, 1), 0x3700)) { // Not in `["\b","\t","\n","\f","\d"]`. mstore8(0x1d, mload(shr(4, c))) // Hex value. mstore8(0x1e, mload(and(c, 15))) // Hex value. mstore(result, mload(0x19)) // "\\u00XX". result := add(result, 6) continue } mstore8(result, 0x5c) // "\\". mstore8(add(result, 1), mload(add(c, 8))) result := add(result, 2) } let last := result // Zeroize the slot after the string. mstore(last, 0) // Restore the result to the start of the free memory. result := mload(0x40) // Store the length of the result. mstore(result, sub(last, add(result, 0x20))) // Allocate memory for the length and the bytes, // rounded up to a multiple of 32. mstore(0x40, and(add(last, 31), not(31))) } } /// @dev Returns whether `a` equals `b`. function eq(string memory a, string memory b) internal pure returns (bool result) { assembly { result := eq(keccak256(add(a, 0x20), mload(a)), keccak256(add(b, 0x20), mload(b))) } } /// @dev Packs a single string with its length into a single word. /// Returns `bytes32(0)` if the length is zero or greater than 31. function packOne(string memory a) internal pure returns (bytes32 result) { /// @solidity memory-safe-assembly assembly { // We don't need to zero right pad the string, // since this is our own custom non-standard packing scheme. result := mul( // Load the length and the bytes. mload(add(a, 0x1f)), // `length != 0 && length < 32`. Abuses underflow. // Assumes that the length is valid and within the block gas limit. lt(sub(mload(a), 1), 0x1f) ) } } /// @dev Unpacks a string packed using {packOne}. /// Returns the empty string if `packed` is `bytes32(0)`. /// If `packed` is not an output of {packOne}, the output behaviour is undefined. function unpackOne(bytes32 packed) internal pure returns (string memory result) { /// @solidity memory-safe-assembly assembly { // Grab the free memory pointer. result := mload(0x40) // Allocate 2 words (1 for the length, 1 for the bytes). mstore(0x40, add(result, 0x40)) // Zeroize the length slot. mstore(result, 0) // Store the length and bytes. mstore(add(result, 0x1f), packed) // Right pad with zeroes. mstore(add(add(result, 0x20), mload(result)), 0) } } /// @dev Packs two strings with their lengths into a single word. /// Returns `bytes32(0)` if combined length is zero or greater than 30. function packTwo(string memory a, string memory b) internal pure returns (bytes32 result) { /// @solidity memory-safe-assembly assembly { let aLength := mload(a) // We don't need to zero right pad the strings, // since this is our own custom non-standard packing scheme. result := mul( // Load the length and the bytes of `a` and `b`. or(shl(shl(3, sub(0x1f, aLength)), mload(add(a, aLength))), mload(sub(add(b, 0x1e), aLength))), // `totalLength != 0 && totalLength < 31`. Abuses underflow. // Assumes that the lengths are valid and within the block gas limit. lt(sub(add(aLength, mload(b)), 1), 0x1e) ) } } /// @dev Unpacks strings packed using {packTwo}. /// Returns the empty strings if `packed` is `bytes32(0)`. /// If `packed` is not an output of {packTwo}, the output behaviour is undefined. function unpackTwo(bytes32 packed) internal pure returns (string memory resultA, string memory resultB) { /// @solidity memory-safe-assembly assembly { // Grab the free memory pointer. resultA := mload(0x40) resultB := add(resultA, 0x40) // Allocate 2 words for each string (1 for the length, 1 for the byte). Total 4 words. mstore(0x40, add(resultB, 0x40)) // Zeroize the length slots. mstore(resultA, 0) mstore(resultB, 0) // Store the lengths and bytes. mstore(add(resultA, 0x1f), packed) mstore(add(resultB, 0x1f), mload(add(add(resultA, 0x20), mload(resultA)))) // Right pad with zeroes. mstore(add(add(resultA, 0x20), mload(resultA)), 0) mstore(add(add(resultB, 0x20), mload(resultB)), 0) } } /// @dev Directly returns `a` without copying. function directReturn(string memory a) internal pure { assembly { // Assumes that the string does not start from the scratch space. let retStart := sub(a, 0x20) let retSize := add(mload(a), 0x40) // Right pad with zeroes. Just in case the string is produced // by a method that doesn't zero right pad. mstore(add(retStart, retSize), 0) // Store the return offset. mstore(retStart, 0x20) // End the transaction, returning the string. return(retStart, retSize) } } }
// SPDX-License-Identifier: MIT pragma solidity >=0.8.17.0; import { TwoStepOwnable } from "./TwoStepOwnable.sol"; /** * @notice Smart contract that verifies and tracks allow list redemptions against a configurable Merkle root, up to a * max number configured at deploy */ contract AllowList is TwoStepOwnable { bytes32 public merkleRoot; error NotAllowListed(); ///@notice Checks if msg.sender is included in AllowList, revert otherwise ///@param proof Merkle proof modifier onlyAllowListed(bytes32[] calldata proof) { if (!isAllowListed(proof, msg.sender)) { revert NotAllowListed(); } _; } constructor(bytes32 _merkleRoot) { merkleRoot = _merkleRoot; } ///@notice set the Merkle root in the contract. OnlyOwner. ///@param _merkleRoot the new Merkle root function setMerkleRoot(bytes32 _merkleRoot) public onlyOwner { merkleRoot = _merkleRoot; } ///@notice Given a Merkle proof, check if an address is AllowListed against the root ///@param proof Merkle proof ///@param data abi-encoded data to be checked against the root ///@return boolean isAllowListed function isAllowListed(bytes32[] calldata proof, bytes memory data) public view returns (bool) { return verifyCalldata(proof, merkleRoot, keccak256(data)); } ///@notice Given a Merkle proof, check if an address is AllowListed against the root ///@param proof Merkle proof ///@param addr address to check against allow list ///@return boolean isAllowListed function isAllowListed(bytes32[] calldata proof, address addr) public view returns (bool) { return verifyCalldata(proof, merkleRoot, keccak256(abi.encodePacked(addr))); } /** * @dev Calldata version of {verify} * Copied from OpenZeppelin's MerkleProof.sol */ function verifyCalldata(bytes32[] calldata proof, bytes32 root, bytes32 leaf) internal pure returns (bool) { return processProofCalldata(proof, leaf) == root; } /** * @dev Calldata version of {processProof} * Copied from OpenZeppelin's MerkleProof.sol */ function processProofCalldata(bytes32[] calldata proof, bytes32 leaf) internal pure returns (bytes32) { bytes32 computedHash = leaf; for (uint256 i = 0; i < proof.length;) { computedHash = _hashPair(computedHash, proof[i]); unchecked { ++i; } } return computedHash; } /// @dev Copied from OpenZeppelin's MerkleProof.sol function _hashPair(bytes32 a, bytes32 b) private pure returns (bytes32) { return a < b ? _efficientHash(a, b) : _efficientHash(b, a); } /// @dev Copied from OpenZeppelin's MerkleProof.sol 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 // ERC721A Contracts v4.2.3 // Creator: Chiru Labs pragma solidity ^0.8.4; /** * @dev Interface of ERC721A. */ interface IERC721A { /** * 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(); // ============================================================= // 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); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC165 standard, as defined in the * https://eips.ethereum.org/EIPS/eip-165[EIP]. * * Implementers can declare support of contract interfaces, which can then be * queried by others ({ERC165Checker}). * * For an implementation, see {ERC165}. */ interface 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 v4.8.0) (token/ERC721/IERC721.sol) pragma solidity ^0.8.0; import "../../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 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); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.4; /// @notice Library for generating psuedorandom numbers. /// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/LibPRNG.sol) library LibPRNG { /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* STRUCTS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev A psuedorandom number state in memory. struct PRNG { uint256 state; } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* OPERATIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Seeds the `prng` with `state`. function seed(PRNG memory prng, bytes32 state) internal pure { /// @solidity memory-safe-assembly assembly { mstore(prng, state) } } /// @dev Returns a psuedorandom uint256, uniformly distributed /// between 0 (inclusive) and `upper` (exclusive). /// If your modulus is big, this method is recommended /// for uniform sampling to avoid modulo bias. /// For uniform sampling across all uint256 values, /// or for small enough moduli such that the bias is neligible, /// use {next} instead. function uniform(PRNG memory prng, uint256 upper) internal pure returns (uint256 result) { /// @solidity memory-safe-assembly assembly { // prettier-ignore for {} 1 {} { result := keccak256(prng, 0x20) mstore(prng, result) // prettier-ignore if iszero(lt(result, mod(sub(0, upper), upper))) { break } } result := mod(result, upper) } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; /** * @notice Solidity library offering basic trigonometry functions where inputs and outputs are * integers. Inputs are specified in radians scaled by 1e18, and similarly outputs are scaled by 1e18. * * This implementation is based off the Solidity trigonometry library written by Lefteris Karapetsas * which can be found here: https://github.com/Sikorkaio/sikorka/blob/e75c91925c914beaedf4841c0336a806f2b5f66d/contracts/trigonometry.sol * * Compared to Lefteris' implementation, this version makes the following changes: * - Uses a 32 bits instead of 16 bits for improved accuracy * - Updated for Solidity 0.8.x * - Various gas optimizations * - Change inputs/outputs to standard trig format (scaled by 1e18) instead of requiring the * integer format used by the algorithm * * Lefertis' implementation is based off Dave Dribin's trigint C library * http://www.dribin.org/dave/trigint/ * * Which in turn is based from a now deleted article which can be found in the Wayback Machine: * http://web.archive.org/web/20120301144605/http://www.dattalo.com/technical/software/pic/picsine.html */ library Trigonometry { // Table index into the trigonometric table uint256 constant INDEX_WIDTH = 8; // Interpolation between successive entries in the table uint256 constant INTERP_WIDTH = 16; uint256 constant INDEX_OFFSET = 28 - INDEX_WIDTH; uint256 constant INTERP_OFFSET = INDEX_OFFSET - INTERP_WIDTH; uint32 constant ANGLES_IN_CYCLE = 1073741824; uint32 constant QUADRANT_HIGH_MASK = 536870912; uint32 constant QUADRANT_LOW_MASK = 268435456; uint256 constant SINE_TABLE_SIZE = 256; // Pi as an 18 decimal value, which is plenty of accuracy: "For JPL's highest accuracy calculations, which are for // interplanetary navigation, we use 3.141592653589793: https://www.jpl.nasa.gov/edu/news/2016/3/16/how-many-decimals-of-pi-do-we-really-need/ uint256 constant PI = 3141592653589793238; uint256 constant TWO_PI = 2 * PI; uint256 constant PI_OVER_TWO = PI / 2; // The constant sine lookup table was generated by generate_trigonometry.py. We must use a constant // bytes array because constant arrays are not supported in Solidity. Each entry in the lookup // table is 4 bytes. Since we're using 32-bit parameters for the lookup table, we get a table size // of 2^(32/4) + 1 = 257, where the first and last entries are equivalent (hence the table size of // 256 defined above) uint8 constant entry_bytes = 4; // each entry in the lookup table is 4 bytes uint256 constant entry_mask = ((1 << (8 * entry_bytes)) - 1); // mask used to cast bytes32 -> lookup table entry bytes constant sin_table = hex"00_00_00_00_00_c9_0f_88_01_92_1d_20_02_5b_26_d7_03_24_2a_bf_03_ed_26_e6_04_b6_19_5d_05_7f_00_35_06_47_d9_7c_07_10_a3_45_07_d9_5b_9e_08_a2_00_9a_09_6a_90_49_0a_33_08_bc_0a_fb_68_05_0b_c3_ac_35_0c_8b_d3_5e_0d_53_db_92_0e_1b_c2_e4_0e_e3_87_66_0f_ab_27_2b_10_72_a0_48_11_39_f0_cf_12_01_16_d5_12_c8_10_6e_13_8e_db_b1_14_55_76_b1_15_1b_df_85_15_e2_14_44_16_a8_13_05_17_6d_d9_de_18_33_66_e8_18_f8_b8_3c_19_bd_cb_f3_1a_82_a0_25_1b_47_32_ef_1c_0b_82_6a_1c_cf_8c_b3_1d_93_4f_e5_1e_56_ca_1e_1f_19_f9_7b_1f_dc_dc_1b_20_9f_70_1c_21_61_b3_9f_22_23_a4_c5_22_e5_41_af_23_a6_88_7e_24_67_77_57_25_28_0c_5d_25_e8_45_b6_26_a8_21_85_27_67_9d_f4_28_26_b9_28_28_e5_71_4a_29_a3_c4_85_2a_61_b1_01_2b_1f_34_eb_2b_dc_4e_6f_2c_98_fb_ba_2d_55_3a_fb_2e_11_0a_62_2e_cc_68_1e_2f_87_52_62_30_41_c7_60_30_fb_c5_4d_31_b5_4a_5d_32_6e_54_c7_33_26_e2_c2_33_de_f2_87_34_96_82_4f_35_4d_90_56_36_04_1a_d9_36_ba_20_13_37_6f_9e_46_38_24_93_b0_38_d8_fe_93_39_8c_dd_32_3a_40_2d_d1_3a_f2_ee_b7_3b_a5_1e_29_3c_56_ba_70_3d_07_c1_d5_3d_b8_32_a5_3e_68_0b_2c_3f_17_49_b7_3f_c5_ec_97_40_73_f2_1d_41_21_58_9a_41_ce_1e_64_42_7a_41_d0_43_25_c1_35_43_d0_9a_ec_44_7a_cd_50_45_24_56_bc_45_cd_35_8f_46_75_68_27_47_1c_ec_e6_47_c3_c2_2e_48_69_e6_64_49_0f_57_ee_49_b4_15_33_4a_58_1c_9d_4a_fb_6c_97_4b_9e_03_8f_4c_3f_df_f3_4c_e1_00_34_4d_81_62_c3_4e_21_06_17_4e_bf_e8_a4_4f_5e_08_e2_4f_fb_65_4c_50_97_fc_5e_51_33_cc_94_51_ce_d4_6e_52_69_12_6e_53_02_85_17_53_9b_2a_ef_54_33_02_7d_54_ca_0a_4a_55_60_40_e2_55_f5_a4_d2_56_8a_34_a9_57_1d_ee_f9_57_b0_d2_55_58_42_dd_54_58_d4_0e_8c_59_64_64_97_59_f3_de_12_5a_82_79_99_5b_10_35_ce_5b_9d_11_53_5c_29_0a_cc_5c_b4_20_df_5d_3e_52_36_5d_c7_9d_7b_5e_50_01_5d_5e_d7_7c_89_5f_5e_0d_b2_5f_e3_b3_8d_60_68_6c_ce_60_ec_38_2f_61_6f_14_6b_61_f1_00_3e_62_71_fa_68_62_f2_01_ac_63_71_14_cc_63_ef_32_8f_64_6c_59_bf_64_e8_89_25_65_63_bf_91_65_dd_fb_d2_66_57_3c_bb_66_cf_81_1f_67_46_c7_d7_67_bd_0f_bc_68_32_57_aa_68_a6_9e_80_69_19_e3_1f_69_8c_24_6b_69_fd_61_4a_6a_6d_98_a3_6a_dc_c9_64_6b_4a_f2_78_6b_b8_12_d0_6c_24_29_5f_6c_8f_35_1b_6c_f9_34_fb_6d_62_27_f9_6d_ca_0d_14_6e_30_e3_49_6e_96_a9_9c_6e_fb_5f_11_6f_5f_02_b1_6f_c1_93_84_70_23_10_99_70_83_78_fe_70_e2_cb_c5_71_41_08_04_71_9e_2c_d1_71_fa_39_48_72_55_2c_84_72_af_05_a6_73_07_c3_cf_73_5f_66_25_73_b5_eb_d0_74_0b_53_fa_74_5f_9d_d0_74_b2_c8_83_75_04_d3_44_75_55_bd_4b_75_a5_85_ce_75_f4_2c_0a_76_41_af_3c_76_8e_0e_a5_76_d9_49_88_77_23_5f_2c_77_6c_4e_da_77_b4_17_df_77_fa_b9_88_78_40_33_28_78_84_84_13_78_c7_ab_a1_79_09_a9_2c_79_4a_7c_11_79_8a_23_b0_79_c8_9f_6d_7a_05_ee_ac_7a_42_10_d8_7a_7d_05_5a_7a_b6_cb_a3_7a_ef_63_23_7b_26_cb_4e_7b_5d_03_9d_7b_92_0b_88_7b_c5_e2_8f_7b_f8_88_2f_7c_29_fb_ed_7c_5a_3d_4f_7c_89_4b_dd_7c_b7_27_23_7c_e3_ce_b1_7d_0f_42_17_7d_39_80_eb_7d_62_8a_c5_7d_8a_5f_3f_7d_b0_fd_f7_7d_d6_66_8e_7d_fa_98_a7_7e_1d_93_e9_7e_3f_57_fe_7e_5f_e4_92_7e_7f_39_56_7e_9d_55_fb_7e_ba_3a_38_7e_d5_e5_c5_7e_f0_58_5f_7f_09_91_c3_7f_21_91_b3_7f_38_57_f5_7f_4d_e4_50_7f_62_36_8e_7f_75_4e_7f_7f_87_2b_f2_7f_97_ce_bc_7f_a7_36_b3_7f_b5_63_b2_7f_c2_55_95_7f_ce_0c_3d_7f_d8_87_8d_7f_e1_c7_6a_7f_e9_cb_bf_7f_f0_94_77_7f_f6_21_81_7f_fa_72_d0_7f_fd_88_59_7f_ff_62_15_7f_ff_ff_ff"; /** * @notice Return the sine of a value, specified in radians scaled by 1e18 * @dev This algorithm for converting sine only uses integer values, and it works by dividing the * circle into 30 bit angles, i.e. there are 1,073,741,824 (2^30) angle units, instead of the * standard 360 degrees (2pi radians). From there, we get an output in range -2,147,483,647 to * 2,147,483,647, (which is the max value of an int32) which is then converted back to the standard * range of -1 to 1, again scaled by 1e18 * @param _angle Angle to convert * @return Result scaled by 1e18 */ function sin(uint256 _angle) internal pure returns (int256) { unchecked { // Convert angle from from arbitrary radian value (range of 0 to 2pi) to the algorithm's range // of 0 to 1,073,741,824 _angle = (ANGLES_IN_CYCLE * (_angle % TWO_PI)) / TWO_PI; // Apply a mask on an integer to extract a certain number of bits, where angle is the integer // whose bits we want to get, the width is the width of the bits (in bits) we want to extract, // and the offset is the offset of the bits (in bits) we want to extract. The result is an // integer containing _width bits of _value starting at the offset bit uint256 interp = (_angle >> INTERP_OFFSET) & ((1 << INTERP_WIDTH) - 1); uint256 index = (_angle >> INDEX_OFFSET) & ((1 << INDEX_WIDTH) - 1); // The lookup table only contains data for one quadrant (since sin is symmetric around both // axes), so here we figure out which quadrant we're in, then we lookup the values in the // table then modify values accordingly bool is_odd_quadrant = (_angle & QUADRANT_LOW_MASK) == 0; bool is_negative_quadrant = (_angle & QUADRANT_HIGH_MASK) != 0; if (!is_odd_quadrant) { index = SINE_TABLE_SIZE - 1 - index; } bytes memory table = sin_table; // We are looking for two consecutive indices in our lookup table // Since EVM is left aligned, to read n bytes of data from idx i, we must read from `i * data_len` + `n` // therefore, to read two entries of size entry_bytes `index * entry_bytes` + `entry_bytes * 2` uint256 offset1_2 = (index + 2) * entry_bytes; // This following snippet will function for any entry_bytes <= 15 uint256 x1_2; assembly { // mload will grab one word worth of bytes (32), as that is the minimum size in EVM x1_2 := mload(add(table, offset1_2)) } // We now read the last two numbers of size entry_bytes from x1_2 // in example: entry_bytes = 4; x1_2 = 0x00...12345678abcdefgh // therefore: entry_mask = 0xFFFFFFFF // 0x00...12345678abcdefgh >> 8*4 = 0x00...12345678 // 0x00...12345678 & 0xFFFFFFFF = 0x12345678 uint256 x1 = (x1_2 >> (8 * entry_bytes)) & entry_mask; // 0x00...12345678abcdefgh & 0xFFFFFFFF = 0xabcdefgh uint256 x2 = x1_2 & entry_mask; // Approximate angle by interpolating in the table, accounting for the quadrant uint256 approximation = ((x2 - x1) * interp) >> INTERP_WIDTH; int256 sine = is_odd_quadrant ? int256(x1) + int256(approximation) : int256(x2) - int256(approximation); if (is_negative_quadrant) { sine *= -1; } // Bring result from the range of -2,147,483,647 through 2,147,483,647 to -1e18 through 1e18. // This can never overflow because sine is bounded by the above values return (sine * 1e18) / 2_147_483_647; } } /** * @notice Return the cosine of a value, specified in radians scaled by 1e18 * @dev This is identical to the sin() method, and just computes the value by delegating to the * sin() method using the identity cos(x) = sin(x + pi/2) * @dev Overflow when `angle + PI_OVER_TWO > type(uint256).max` is ok, results are still accurate * @param _angle Angle to convert * @return Result scaled by 1e18 */ function cos(uint256 _angle) internal pure returns (int256) { unchecked { return sin(_angle + PI_OVER_TWO); } } }
// SPDX-License-Identifier: MIT pragma solidity >=0.8.17 .0; // Core SVG utility library which helps us construct // onchain SVG's with a simple, web-like API. // Credit to w1nt3r.eth for the base of this. library svg { /* MAIN ELEMENTS */ function line(string memory _props) internal pure returns (string memory) { return string.concat("<line ", _props, "/>"); } /* COMMON */ // an SVG attribute function prop(string memory _key, string memory _val) internal pure returns (string memory) { return string.concat(_key, "=", '"', _val, '" '); } }
// SPDX-License-Identifier: MIT pragma solidity >=0.8.4; /** * @notice A two-step extension of Ownable, where the new owner must claim ownership of the contract after owner * initiates transfer * Owner can cancel the transfer at any point before the new owner claims ownership. * Helpful in guarding against transferring ownership to an address that is unable to act as the Owner. */ abstract contract TwoStepOwnable { address private _owner; event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); address internal potentialOwner; event PotentialOwnerUpdated(address newPotentialAdministrator); error NewOwnerIsZeroAddress(); error NotNextOwner(); error OnlyOwner(); modifier onlyOwner() { _checkOwner(); _; } constructor() { _transferOwnership(msg.sender); } ///@notice Initiate ownership transfer to newPotentialOwner. Note: new owner will have to manually acceptOwnership ///@param newPotentialOwner address of potential new owner function transferOwnership(address newPotentialOwner) public virtual onlyOwner { if (newPotentialOwner == address(0)) { revert NewOwnerIsZeroAddress(); } potentialOwner = newPotentialOwner; emit PotentialOwnerUpdated(newPotentialOwner); } ///@notice Claim ownership of smart contract, after the current owner has initiated the process with /// transferOwnership function acceptOwnership() public virtual { address _potentialOwner = potentialOwner; if (msg.sender != _potentialOwner) { revert NotNextOwner(); } delete potentialOwner; emit PotentialOwnerUpdated(address(0)); _transferOwnership(_potentialOwner); } ///@notice cancel ownership transfer function cancelOwnershipTransfer() public virtual onlyOwner { delete potentialOwner; emit PotentialOwnerUpdated(address(0)); } function owner() public view virtual returns (address) { return _owner; } /** * @dev Throws if the sender is not the owner. */ function _checkOwner() internal view virtual { if (_owner != msg.sender) { revert OnlyOwner(); } } /** * @dev Leaves the contract without owner. It will not be possible to call * `onlyOwner` functions anymore. Can only be called by the current owner. * * NOTE: Renouncing ownership will leave the contract without an owner, * thereby removing 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`). * Internal function without access restriction. */ function _transferOwnership(address newOwner) internal virtual { address oldOwner = _owner; _owner = newOwner; emit OwnershipTransferred(oldOwner, newOwner); } }
{ "remappings": [ "@prb/test/=lib/prb-test/src/", "forge-std/=lib/forge-std/src/", "src/=src/", "@erc721a/=lib/ERC721A/contracts/", "@openzeppelin/=lib/openzeppelin-contracts/", "@solady/=lib/solady/src/", "operator-filter-registry/=lib/operator-filter-registry/", "ERC721A/=lib/ERC721A/contracts/", "ds-test/=lib/forge-std/lib/ds-test/src/", "erc4626-tests/=lib/operator-filter-registry/lib/openzeppelin-contracts/lib/erc4626-tests/", "openzeppelin-contracts-upgradeable/=lib/operator-filter-registry/lib/openzeppelin-contracts-upgradeable/", "openzeppelin-contracts/=lib/openzeppelin-contracts/", "prb-math/=lib/prb-math/src/", "prb-test/=lib/prb-test/src/", "solady/=lib/solady/src/", "solidity-trigonometry/=lib/solidity-trigonometry/src/", "solmate/=lib/solady/lib/solmate/src/" ], "optimizer": { "enabled": true, "runs": 20 }, "metadata": { "useLiteralContent": false, "bytecodeHash": "none", "appendCBOR": false }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "evmVersion": "paris", "libraries": {} }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[{"internalType":"bytes32","name":"allowListMerkleRoot","type":"bytes32"},{"internalType":"uint16","name":"allowListMintMaxTotalVal","type":"uint16"},{"internalType":"uint8","name":"allowListMintMaxPerWalletVal","type":"uint8"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AllowListMintCapExceeded","type":"error"},{"inputs":[],"name":"AllowListMintCapPerWalletExceeded","type":"error"},{"inputs":[],"name":"AlreadyCommenced","type":"error"},{"inputs":[],"name":"ApprovalCallerNotOwnerNorApproved","type":"error"},{"inputs":[],"name":"ApprovalQueryForNonexistentToken","type":"error"},{"inputs":[],"name":"BalanceQueryForZeroAddress","type":"error"},{"inputs":[],"name":"IncorrectPayment","type":"error"},{"inputs":[],"name":"MaxSupplyReached","type":"error"},{"inputs":[],"name":"MintERC2309QuantityExceedsLimit","type":"error"},{"inputs":[],"name":"MintNotStarted","type":"error"},{"inputs":[],"name":"MintToZeroAddress","type":"error"},{"inputs":[],"name":"MintZeroQuantity","type":"error"},{"inputs":[],"name":"MsgSenderDoesNotOwnXXYYZZToken","type":"error"},{"inputs":[],"name":"MsgSenderNotTokenOwner","type":"error"},{"inputs":[],"name":"NewOwnerIsZeroAddress","type":"error"},{"inputs":[],"name":"NotAllowListed","type":"error"},{"inputs":[],"name":"NotNextOwner","type":"error"},{"inputs":[],"name":"OnlyOwner","type":"error"},{"inputs":[],"name":"OwnerQueryForNonexistentToken","type":"error"},{"inputs":[],"name":"OwnershipNotInitializedForExtraData","type":"error"},{"inputs":[],"name":"PublicMintMaxPerTransactionExceeded","type":"error"},{"inputs":[],"name":"PublicMintNotEnabled","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"},{"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":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":false,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"MetadataUpdate","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":"newPotentialAdministrator","type":"address"}],"name":"PotentialOwnerUpdated","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"},{"inputs":[],"name":"PRICE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"acceptOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"approve","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint16","name":"numberOfPoints","type":"uint16"}],"name":"art","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"cancelOwnershipTransfer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint24","name":"xxyyzzTokenId","type":"uint24"}],"name":"colorBackground","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"commence","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32[]","name":"proof","type":"bytes32[]"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"isAllowListed","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32[]","name":"proof","type":"bytes32[]"},{"internalType":"address","name":"addr","type":"address"}],"name":"isAllowListed","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","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":[],"name":"merkleRoot","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32[]","name":"proof","type":"bytes32[]"},{"internalType":"uint8","name":"amount","type":"uint8"}],"name":"mintAllowList","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint8","name":"amount","type":"uint8"}],"name":"mintPublic","outputs":[],"stateMutability":"payable","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":"publicMintEnabled","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"salePrice","type":"uint256"}],"name":"royaltyInfo","outputs":[{"internalType":"address","name":"receiver","type":"address"},{"internalType":"uint256","name":"royaltyAmount","type":"uint256"}],"stateMutability":"pure","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":"bytes32","name":"_merkleRoot","type":"bytes32"}],"name":"setMerkleRoot","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":"","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":"newPotentialOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"_publicMintEnabled","type":"bool"}],"name":"updatePublicMintEnabled","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"}]
Contract Creation Code
60c06040523480156200001157600080fd5b5060405162003d1238038062003d12833981016040819052620000349162000110565b82604051806040016040528060088152602001672a37b3b2ba3432b960c11b815250604051806040016040528060038152602001622a23a960e91b8152508160cd908162000083919062000208565b5060ce62000092828262000208565b5050600160cb5550620000a533620000be565b60d55561ffff90911660805260ff1660a05250620002d4565b60d380546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b6000806000606084860312156200012657600080fd5b83519250602084015161ffff811681146200014057600080fd5b604085015190925060ff811681146200015857600080fd5b809150509250925092565b634e487b7160e01b600052604160045260246000fd5b600181811c908216806200018e57607f821691505b602082108103620001af57634e487b7160e01b600052602260045260246000fd5b50919050565b601f8211156200020357600081815260208120601f850160051c81016020861015620001de5750805b601f850160051c820191505b81811015620001ff57828155600101620001ea565b5050505b505050565b81516001600160401b0381111562000224576200022462000163565b6200023c8162000235845462000179565b84620001b5565b602080601f8311600181146200027457600084156200025b5750858301515b600019600386901b1c1916600185901b178555620001ff565b600085815260208120601f198616915b82811015620002a55788860151825594840194600190910190840162000284565b5085821015620002c45787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b60805160a051613a18620002fa6000396000610c9601526000610c2e0152613a186000f3fe60806040526004361061018e5760003560e01c806370a08231116100dd57806370a0823114610385578063715018a6146103a55780637449da48146103ba57806379ba5097146103cd5780637cb64759146103e257806383281cec146104025780638d859f3e146104225780638da5cb5b1461043d57806395d89b411461045b578063a22cb46514610470578063a6b4086814610490578063b88d4fde146104b0578063c87b56dd146104c3578063dbd76cd9146104e3578063e985e9c5146104f8578063f2fde38b14610518578063f3b674a41461053857600080fd5b806301ffc9a71461019357806306fdde03146101c8578063081812fc146101ea57806308fe4be614610217578063095ea7b3146102375780630f4161aa1461024c57806318160ddd1461026657806323452b9c1461028d57806323b872dd146102a25780632a55205a146102b55780632eb4a7ab146102f45780633ccfd60b1461030a57806342842e0e1461031f57806356ff1be8146103325780636352211e1461035257806367dce1ed14610372575b600080fd5b34801561019f57600080fd5b506101b36101ae36600461233e565b610558565b60405190151581526020015b60405180910390f35b3480156101d457600080fd5b506101dd610583565b6040516101bf91906123ab565b3480156101f657600080fd5b5061020a6102053660046123be565b610615565b6040516101bf91906123d7565b34801561022357600080fd5b506101dd6102323660046123eb565b610659565b61024a610245366004612424565b6107b8565b005b34801561025857600080fd5b5060d6546101b39060ff1681565b34801561027257600080fd5b5060cc5460cb5403600019015b6040519081526020016101bf565b34801561029957600080fd5b5061024a610858565b61024a6102b0366004612450565b610899565b3480156102c157600080fd5b506102d56102d0366004612491565b610a1f565b604080516001600160a01b0390931683526020830191909152016101bf565b34801561030057600080fd5b5061027f60d55481565b34801561031657600080fd5b5061024a610a44565b61024a61032d366004612450565b610ab0565b34801561033e57600080fd5b5061024a61034d3660046124c8565b610ad0565b34801561035e57600080fd5b5061020a61036d3660046123be565b610aeb565b61024a6103803660046124f4565b610af6565b34801561039157600080fd5b5061027f6103a036600461250f565b610b74565b3480156103b157600080fd5b5061024a610bc2565b61024a6103c8366004612570565b610bd6565b3480156103d957600080fd5b5061024a610d0f565b3480156103ee57600080fd5b5061024a6103fd3660046123be565b610d7b565b34801561040e57600080fd5b506101b361041d366004612665565b610d88565b34801561042e57600080fd5b5061027f662386f26fc1000081565b34801561044957600080fd5b5060d3546001600160a01b031661020a565b34801561046757600080fd5b506101dd610da9565b34801561047c57600080fd5b5061024a61048b3660046126cd565b610db8565b34801561049c57600080fd5b5061024a6104ab366004612702565b610e24565b61024a6104be36600461273a565b610f81565b3480156104cf57600080fd5b506101dd6104de3660046123be565b610fcb565b3480156104ef57600080fd5b5061024a611062565b34801561050457600080fd5b506101b36105133660046127a5565b6110a6565b34801561052457600080fd5b5061024a61053336600461250f565b6110d4565b34801561054457600080fd5b506101b36105533660046127d3565b611147565b60006001600160e01b0319821663152a902d60e11b148061057d575061057d8261118a565b92915050565b606060cd805461059290612829565b80601f01602080910402602001604051908101604052809291908181526020018280546105be90612829565b801561060b5780601f106105e05761010080835404028352916020019161060b565b820191906000526020600020905b8154815290600101906020018083116105ee57829003601f168201915b5050505050905090565b6000610620826111d8565b61063d576040516333d1c03960e21b815260040160405180910390fd5b50600090815260d160205260409020546001600160a01b031690565b60ca5460609060ff1661067a57505060408051602081019091526000815290565b60606000606461ffff85160461ffff16905060005b818160ff1610156106f35760006106b060ff600a840260780316606461120d565b9050836106c7826064850260010160ff16896112ed565b6040516020016106d8929190612895565b60408051601f1981840301815291905293505060010161068f565b506000606461ffff861606905061ffff811615610752578261072f61072261ffff600a8602607803168461120d565b60016064860201886112ed565b604051602001610740929190612895565b60405160208183030381529060405292505b505061078f60658461ffff166103e9811061076f5761076f6128c4565b600a91828204019190066003029054906101000a900462ffffff16611744565b816040516020016107a19291906128da565b604051602081830303815290604052915050919050565b60006107c382610aeb565b9050336001600160a01b038216146107fc576107df81336110a6565b6107fc576040516367d9dca160e11b815260040160405180910390fd5b600082815260d1602052604080822080546001600160a01b0319166001600160a01b0387811691821790925591518593918516917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92591a4505050565b61086061177c565b60d480546001600160a01b03191690556040516000805160206139d88339815191529061088f906000906123d7565b60405180910390a1565b60006108a4826117a7565b9050836001600160a01b0316816001600160a01b0316146108d75760405162a1148160e81b815260040160405180910390fd5b600082815260d1602052604090208054338082146001600160a01b038816909114176109245761090786336110a6565b61092457604051632ce44b5f60e11b815260040160405180910390fd5b6001600160a01b03851661094b57604051633a954ecd60e21b815260040160405180910390fd5b801561095657600082555b6001600160a01b03868116600090815260d0602052604080822080546000190190559187168152208054600101905561099385600160e11b611816565b600085815260cf6020526040812091909155600160e11b841690036109e85760018401600081815260cf602052604081205490036109e65760cb5481146109e657600081815260cf602052604090208490555b505b83856001600160a01b0316876001600160a01b03166000805160206139f883398151915260405160405180910390a4505050505050565b7339ab90066cec746a032d67e4fe3378f16294cf6b61271060fa8302045b9250929050565b6040516000907339ab90066cec746a032d67e4fe3378f16294cf6b9047908381818185875af1925050503d8060008114610a9a576040519150601f19603f3d011682016040523d82523d6000602084013e610a9f565b606091505b5050905080610aad57600080fd5b50565b610acb83838360405180602001604052806000815250610f81565b505050565b610ad861177c565b60d6805460ff1916911515919091179055565b600061057d826117a7565b60d65460ff16610b19576040516393e8222560e01b815260040160405180910390fd5b600560ff82161115610b3e576040516392e5b1c960e01b815260040160405180910390fd5b34662386f26fc100008260ff160214610b6a5760405163569e8c1160e01b815260040160405180910390fd5b610aad338261182b565b60006001600160a01b038216610b9d576040516323d3ad8160e21b815260040160405180910390fd5b506001600160a01b0316600090815260d060205260409020546001600160401b031690565b610bca61177c565b610bd46000611918565b565b8282610be3828233611147565b610c0057604051630e5060e160e21b815260040160405180910390fd5b34662386f26fc100008460ff160214610c2c5760405163569e8c1160e01b815260040160405180910390fd5b7f000000000000000000000000000000000000000000000000000000000000000061ffff168360ff16610c6260cb546000190190565b011115610c8257604051637c44f5af60e11b815260040160405180910390fd5b33600090815260d7602052604090205460ff7f000000000000000000000000000000000000000000000000000000000000000081169181168501161115610cdc57604051632ae6c11560e21b815260040160405180910390fd5b33600081815260d760205260409020805460ff80821687011660ff19909116179055610d08908461182b565b5050505050565b60d4546001600160a01b0316338114610d3b57604051636b7584e760e11b815260040160405180910390fd5b60d480546001600160a01b03191690556040516000805160206139d883398151915290610d6a906000906123d7565b60405180910390a1610aad81611918565b610d8361177c565b60d555565b6000610d9f848460d554858051906020012061196a565b90505b9392505050565b606060ce805461059290612829565b33600081815260d2602090815260408083206001600160a01b03871680855290835292819020805460ff191686151590811790915590519081529192917f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31910160405180910390a35050565b33610e2e83610aeb565b6001600160a01b031614610e5557604051633533b18360e11b815260040160405180910390fd5b62ffffff811615801590610eea57506040516331a9108f60e11b815262ffffff82166004820152339073ff6000a85baac9c4854faa7155e70ba850bf726b90636352211e90602401602060405180830381865afa158015610eba573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ede91906129fe565b6001600160a01b031614155b15610f0857604051630140282360e71b815260040160405180910390fd5b806065836103e98110610f1d57610f1d6128c4565b600a91828204019190066003026101000a81548162ffffff021916908362ffffff1602179055507ff8e1a15aba9398e019f0b49df1a4fde98ee17ae345cb5f6b5e2c27f5033e8ce782604051610f7591815260200190565b60405180910390a15050565b610f8c848484610899565b6001600160a01b0383163b15610fc557610fa884848484611984565b610fc5576040516368d2bf6b60e11b815260040160405180910390fd5b50505050565b6060610fd6826111d8565b610ff357604051630a14c4b560e41b815260040160405180910390fd5b6000610ffe83610659565b9050610da28361100d83611a6c565b611035846040516020016110219190612a1b565b604051602081830303815290604052611a87565b6040516020016110459190612e5e565b60405160208183030381529060405261105d87611bd9565b611d37565b61106a61177c565b60ca5460ff161561108e5760405163532c890b60e11b815260040160405180910390fd5b60ca805460ff19166001179055610bd433600561182b565b6001600160a01b03918216600090815260d26020908152604080832093909416825291909152205460ff1690565b6110dc61177c565b6001600160a01b03811661110357604051633a247dd760e11b815260040160405180910390fd5b60d480546001600160a01b0319166001600160a01b0383161790556040516000805160206139d88339815191529061113c9083906123d7565b60405180910390a150565b60d5546040516001600160601b0319606084901b166020820152600091610d9f91869186916034016040516020818303038152906040528051906020012061196a565b60006301ffc9a760e01b6001600160e01b0319831614806111bb57506380ac58cd60e01b6001600160e01b03198316145b8061057d5750506001600160e01b031916635b5e139f60e01b1490565b6000816001111580156111ec575060cb5482105b801561057d575050600090815260cf6020526040902054600160e01b161590565b60608161ffff166001600160401b0381111561122b5761122b6125c3565b60405190808252806020026020018201604052801561126457816020015b61125161230e565b8152602001906001900390816112495790505b50905060008261ffff1668138400eca364a000008161128557611285612863565b046001600160481b0316905060005b8361ffff168163ffffffff1610156112e55763ffffffff811682026112b98682611d84565b848363ffffffff16815181106112d1576112d16128c4565b602090810291909101015250600101611294565b505092915050565b606083516001036114f4576114ed61133a60405180604001604052806002815260200161783160f01b8152506040518060400160405280600381526020016203230360ec1b815250611e56565b61137960405180604001604052806002815260200161793160f01b8152506040518060400160405280600381526020016203230360ec1b815250611e56565b6113b8604051806040016040528060028152602001613c1960f11b8152506040518060400160405280600381526020016203230360ec1b815250611e56565b6113f7604051806040016040528060028152602001613c9960f11b8152506040518060400160405280600381526020016203230360ec1b815250611e56565b6114446040518060400160405280600e81526020016d07374726f6b652d6c696e656361760941b815250604051806040016040528060058152602001641c9bdd5b9960da1b815250611e56565b611486604051806040016040528060068152602001657374726f6b6560d01b81525061148160008c61ffff166103e9811061076f5761076f6128c4565b611e56565b6114c3604051806040016040528060028152602001611a5960f21b815250604051806040016040528060018152602001606d60f81b815250611e56565b6040516020016114d99796959493929190612e9c565b604051602081830303815290604052611e82565b9050610da2565b60005b84518161ffff16101561173c57600085518260010161ffff16149050600085830161ffff1690508361170d61156a60405180604001604052806002815260200161783160f01b8152506114818b8861ffff1681518110611559576115596128c4565b602002602001015160000151611e95565b6115b260405180604001604052806002815260200161793160f01b8152506114818c8961ffff16815181106115a1576115a16128c4565b602002602001015160200151611e95565b611604604051806040016040528060028152602001613c1960f11b815250611481886115f1578d8a60010161ffff1681518110611559576115596128c4565b8d600081518110611559576115596128c4565b611656604051806040016040528060028152602001613c9960f11b81525061148189611643578e8b60010161ffff16815181106115a1576115a16128c4565b8e6000815181106115a1576115a16128c4565b611695604051806040016040528060068152602001657374726f6b6560d01b81525061148160008a63ffffffff166103e9811061076f5761076f6128c4565b8763ffffffff168c61ffff16146116bb57604051806020016040528060008152506116f8565b6116f8604051806040016040528060028152602001611a5960f21b815250604051806040016040528060018152602001606d60f81b815250611e56565b6040516020016114d996959493929190612f2e565b60405160200161171e929190612895565b604051602081830303815290604052935050508060010190506114f7565b509392505050565b606061175662ffffff83166003611f20565b6040516020016117669190612fad565b6040516020818303038152906040529050919050565b60d3546001600160a01b03163314610bd457604051635fc483c560e01b815260040160405180910390fd5b600081806001116117fd5760cb548110156117fd57600081815260cf602052604081205490600160e01b821690036117fb575b80600003610da2575060001901600081815260cf60205260409020546117da565b505b604051636f96cda160e11b815260040160405180910390fd5b4260a01b176001600160a01b03919091161790565b60ca5460ff1661184e57604051630314872760e11b815260040160405180910390fd5b600061185960cb5490565b905060ff821681016103e910156118835760405163d05cb60960e01b815260040160405180910390fd5b805b8260ff16820181101561190a57604080514460208201529081018290526118c49060600160405160208183030381529060405280519060200120611fa8565b6000826103e981106118d8576118d86128c4565b600a91828204019190066003026101000a81548162ffffff021916908362ffffff160217905550806001019050611885565b50610acb838360ff16612005565b60d380546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b6000826119788686856120ed565b1490505b949350505050565b604051630a85bd0160e11b81526000906001600160a01b0385169063150b7a02906119b9903390899088908890600401612fd6565b6020604051808303816000875af19250505080156119f4575060408051601f3d908101601f191682019092526119f191810190613013565b60015b611a52573d808015611a22576040519150601f19603f3d011682016040523d82523d6000602084013e611a27565b606091505b508051600003611a4a576040516368d2bf6b60e11b815260040160405180910390fd5b805181602001fd5b6001600160e01b031916630a85bd0160e11b14905061197c565b6060611a7782611a87565b6040516020016117669190613030565b60608151600003611aa657505060408051602081019091526000815290565b600060405180606001604052806040815260200161399860409139905060006003845160020181611ad957611ad9612863565b0460040290506000816020016001600160401b03811115611afc57611afc6125c3565b6040519080825280601f01601f191660200182016040528015611b26576020820181803683370190505b509050818152600183018586518101602084015b81831015611b945760039283018051603f601282901c811687015160f890811b8552600c83901c8216880151811b6001860152600683901c8216880151811b60028601529116860151901b93820193909352600401611b3a565b600389510660018114611bae5760028114611bbf57611bcb565b613d3d60f01b600119830152611bcb565b603d60f81b6000198301525b509398975050505050505050565b60606000606483069050611c1c6040518060400160405280600c81526020016b546f74616c20506f696e747360a01b815250611c148561212f565b600180612173565b604080518082019091526005815264088cae0e8d60db1b6020820152611c4890611c146064870461212f565b611c8e60405180604001604052806012815260200171496e6e657220536861706520506f696e747360701b815250611c1460008611611c8857606461212f565b8561212f565b611cd86040518060400160405280600e81526020016d2732bb902634b7329021b7b637b960911b815250611ccf6000896103e9811061076f5761076f6128c4565b60006001612173565b611d236040518060400160405280601081526020016f2130b1b5b3b937bab7321021b7b637b960811b815250611d1b60658a6103e9811061076f5761076f6128c4565b600080612173565b6040516020016107a1959493929190613072565b6060611d5b611d458661212f565b83868660405160200161102194939291906130fc565b604051602001611d6b91906132eb565b6040516020818303038152906040529050949350505050565b611d8c61230e565b6000611da16002672b992ddfa23249d6613346565b611db4672b992ddfa23249d6600261335a565b611dbe9190613371565b6809c2007651b2500000611dda672b992ddfa23249d686613384565b611de491906133b4565b611dee91906133e2565b90506040518060400160405280611e04836121e9565b611e0e9087613384565b611e2190680ad78ebc5ac62000006133e2565b8152602001611e2f836121fa565b611e399087613384565b611e4c90680ad78ebc5ac62000006133e2565b9052949350505050565b60608282604051602001611e6b929190613402565b604051602081830303815290604052905092915050565b6060816040516020016117669190613457565b60606000611eab670de0b6b3a764000084613346565b90506000611ec1670de0b6b3a764000085613491565b905067016345785d8a0000811115611ee557611ee264e8d4a5100082613346565b90505b611eee8261212f565b611ef78261212f565b604051602001611f089291906134a5565b60405160208183030381529060405292505050919050565b6060601f1960428360011b01166040510190506020810160405260008152806f30313233343536373839616263646566600f528283018203600119855b600f811651948201946001860153600f8160041c1651855360081c848303611f5d578015611f9357632194895a6000526004601cfd5b505050819003601f1990910190815292915050565b6000611fc06040518060200160405280600081525090565b828152611fcf816101006122c4565b60ff166008611fe0836101006122c4565b62ffffff16901b6010611ff5846101006122c4565b62ffffff16901b01019392505050565b60cb54600082900361202a5760405163b562e8dd60e01b815260040160405180910390fd5b6001600160a01b038316600090815260d06020526040902080546001600160401b018402019055612061836001841460e11b611816565b600082815260cf60205260408120919091556001600160a01b0384169083830190839083906000805160206139f88339815191528180a4600183015b8181146120c357808360006000805160206139f8833981519152600080a460010161209d565b50816000036120e457604051622e076360e81b815260040160405180910390fd5b60cb5550505050565b600081815b848110156121265761211c82878784818110612110576121106128c4565b905060200201356122e2565b91506001016120f2565b50949350505050565b60606080604051019050602081016040526000815280600019835b928101926030600a8206018453600a90048061214a575050819003601f19909101908152919050565b606084836121a0578460405160200161218c91906134e1565b6040516020818303038152906040526121a2565b845b836121bc57604051806020016040528060008152506121d7565b604051806040016040528060018152602001600b60fa1b8152505b604051602001611d6b9392919061350f565b600061057d6715cc96efd11924eb83015b6757325bbf446493ac9081900663400000000204600061ffff600483901c1660ff601484901c1663100000008416156320000000851615158161223f5760ff92909203915b60006040518061044001604052806104048152602001613594610404913960046002860102818101519192509063ffffffff602082901c8116908216818103890260101c60008861229257818303612296565b8184015b905087156122a2576000035b637fffffff670de0b6b3a76400008202059d9c50505050505050505050505050565b60005b60208320905080835281826000030681106122c75706919050565b60008183106122fe576000828152602084905260409020610da2565b5060009182526020526040902090565b604051806040016040528060008152602001600081525090565b6001600160e01b031981168114610aad57600080fd5b60006020828403121561235057600080fd5b8135610da281612328565b60005b8381101561237657818101518382015260200161235e565b50506000910152565b6000815180845261239781602086016020860161235b565b601f01601f19169290920160200192915050565b602081526000610da2602083018461237f565b6000602082840312156123d057600080fd5b5035919050565b6001600160a01b0391909116815260200190565b6000602082840312156123fd57600080fd5b813561ffff81168114610da257600080fd5b6001600160a01b0381168114610aad57600080fd5b6000806040838503121561243757600080fd5b82356124428161240f565b946020939093013593505050565b60008060006060848603121561246557600080fd5b83356124708161240f565b925060208401356124808161240f565b929592945050506040919091013590565b600080604083850312156124a457600080fd5b50508035926020909101359150565b803580151581146124c357600080fd5b919050565b6000602082840312156124da57600080fd5b610da2826124b3565b803560ff811681146124c357600080fd5b60006020828403121561250657600080fd5b610da2826124e3565b60006020828403121561252157600080fd5b8135610da28161240f565b60008083601f84011261253e57600080fd5b5081356001600160401b0381111561255557600080fd5b6020830191508360208260051b8501011115610a3d57600080fd5b60008060006040848603121561258557600080fd5b83356001600160401b0381111561259b57600080fd5b6125a78682870161252c565b90945092506125ba9050602085016124e3565b90509250925092565b634e487b7160e01b600052604160045260246000fd5b600082601f8301126125ea57600080fd5b81356001600160401b0380821115612604576126046125c3565b604051601f8301601f19908116603f0116810190828211818310171561262c5761262c6125c3565b8160405283815286602085880101111561264557600080fd5b836020870160208301376000602085830101528094505050505092915050565b60008060006040848603121561267a57600080fd5b83356001600160401b038082111561269157600080fd5b61269d8783880161252c565b909550935060208601359150808211156126b657600080fd5b506126c3868287016125d9565b9150509250925092565b600080604083850312156126e057600080fd5b82356126eb8161240f565b91506126f9602084016124b3565b90509250929050565b6000806040838503121561271557600080fd5b82359150602083013562ffffff8116811461272f57600080fd5b809150509250929050565b6000806000806080858703121561275057600080fd5b843561275b8161240f565b9350602085013561276b8161240f565b92506040850135915060608501356001600160401b0381111561278d57600080fd5b612799878288016125d9565b91505092959194509250565b600080604083850312156127b857600080fd5b82356127c38161240f565b9150602083013561272f8161240f565b6000806000604084860312156127e857600080fd5b83356001600160401b038111156127fe57600080fd5b61280a8682870161252c565b909450925050602084013561281e8161240f565b809150509250925092565b600181811c9082168061283d57607f821691505b60208210810361285d57634e487b7160e01b600052602260045260246000fd5b50919050565b634e487b7160e01b600052601260045260246000fd5b6000815161288b81856020860161235b565b9290920192915050565b600083516128a781846020880161235b565b8351908301906128bb81836020880161235b565b01949350505050565b634e487b7160e01b600052603260045260246000fd5b7f3c73766720786d6c6e733d22687474703a2f2f7777772e77332e6f72672f323081527f30302f737667222077696474683d223130302522206865696768743d2231303060208201527f25222076696577426f783d22302030203430302034303022207374796c653d2260408201526a3130b1b5b3b937bab7321d60a91b60608201526000835161297281606b85016020880161235b565b7f3b646973706c61793a626c6f636b3b6d617267696e3a6175746f223e3c737479606b918401918201527f6c653e6c696e657b7374726f6b652d77696474683a31303b7d3c2f7374796c65608b820152601f60f91b60ab82015283516129df8160ac84016020880161235b565b651e17b9bb339f60d11b60ac929091019182015260b201949350505050565b600060208284031215612a1057600080fd5b8151610da28161240f565b7f3c68746d6c207374796c653d226f766572666c6f773a68696464656e223e3c6281527437b23c9039ba3cb6329e9136b0b933b4b71d18111f60591b602082015260008251612a7181603585016020870161235b565b7f3c7363726970743e6c657420613d21312c623d21312c6f3d41727261792e667260359390910192830152507f6f6d28646f63756d656e742e676574456c656d656e747342795461674e616d6560558201527f28226c696e652229292e6d617028653d3e652e7374796c652e7374726f6b652960758201527f2c733d653d3e6e65772050726f6d69736528743d3e73657454696d656f75742860958201527f742c6529292c633d28293d3e4d6174682e666c6f6f72283235362a4d6174682e60b58201527f72616e646f6d2829293b646f63756d656e742e626f64792e6164644576656e7460d58201527f4c697374656e65722822636c69636b222c6173796e6328293d3e7b696628216160f58201527f7c7c622972657475726e3b623d21303b6c657420653d646f63756d656e742e676101158201527f6574456c656d656e747342795461674e616d6528226c696e6522293b666f72286101358201527f3b613b297b666f72286c657420743d303b743c652e6c656e6774683b742b2b296101558201527f655b745d2e7374796c652e7374726f6b653d6072676228247b6328297d2c247b6101758201527f6328297d2c247b6328297d29603b61776169742073283530297d666f72286c656101958201527f74206c3d303b6c3c652e6c656e6774683b6c2b2b29655b6c5d2e7374796c652e6101b58201527f7374726f6b653d6f5b6c5d3b623d21317d2c2130292c646f63756d656e742e626101d58201527f6f64792e6164644576656e744c697374656e65722822636c69636b222c6173796101f58201527f6e6328293d3e7b696628612972657475726e3b613d21303b6c657420653d646f6102158201527f63756d656e742e676574456c656d656e747342795461674e616d6528226c696e6102358201527f6522293b666f72286c657420743d303b743c322a652e6c656e6774683b742b2b6102558201527f297b6c6574206c3d743c652e6c656e6774683f2230223a2231222c6e3d743c656102758201527f2e6c656e6774683f743a322a652e6c656e6774682d742d313b226d22213d3d656102958201527f5b6e5d2e6964262628655b6e5d2e7374796c652e7374726f6b654f70616369746102b58201527f793d6c293b6c657420673d652e6c656e677468253130303b61776169742073286102d58201527f28743c652e6c656e6774683f743e3d652e6c656e6774682d673a743e652e6c656102f58201527f6e6774682626742d652e6c656e6774683c3d67293f32302b283130302d67292f6103158201527f3130302a37353a3130297d613d21317d2c2130293b3c2f7363726970743e3c2f6103358201526b3137b23c9f1e17b43a36b61f60a11b61035582015261036101919050565b7519185d184e9d195e1d0bda1d1b5b0ed8985cd94d8d0b60521b815260008251612e8f81601685016020870161235b565b9190910160160192915050565b600088516020612eaf8285838e0161235b565b895191840191612ec28184848e0161235b565b8951920191612ed48184848d0161235b565b8851920191612ee68184848c0161235b565b8751920191612ef88184848b0161235b565b8651920191612f0a8184848a0161235b565b8551920191612f1c818484890161235b565b919091019a9950505050505050505050565b600087516020612f418285838d0161235b565b885191840191612f548184848d0161235b565b8851920191612f668184848c0161235b565b8751920191612f788184848b0161235b565b8651920191612f8a8184848a0161235b565b8551920191612f9c818484890161235b565b919091019998505050505050505050565b602360f81b815260008251612fc981600185016020870161235b565b9190910160010192915050565b6001600160a01b03858116825284166020820152604081018390526080606082018190526000906130099083018461237f565b9695505050505050565b60006020828403121561302557600080fd5b8151610da281612328565b7919185d184e9a5b5859d94bdcdd99cade1b5b0ed8985cd94d8d0b60321b81526000825161306581601a85016020870161235b565b91909101601a0192915050565b605b60f81b815260006001875161308f8183860160208c0161235b565b8751908401906130a58184840160208c0161235b565b87519101906130ba8184840160208b0161235b565b86519101906130cf8184840160208a0161235b565b85519101906130e4818484016020890161235b565b605d60f81b9101820190815201979650505050505050565b727b226e616d65223a22546f676574686572202360681b8152845160009061312b816013850160208a0161235b565b7f222c226465736372697074696f6e223a22546f676574686572206973206120676013918401918201527f656e65726174697665206f6e636861696e204e4654207769746820636f6c6f7260338201527f66756c2061727420666f726d656420627920697473206d696e746572732e205460538201527f686520617274206f66206561636820746f6b656e206275696c6473206f6e207460738201527f686520617274206f6620616c6c2070726576696f757320746f6b656e7320706c60938201527f75732061206e6577206c696e652067656e65726174656420617420746865207460b38201527f696d65206f66206d696e742e20546170206f6e636520746f20756e77696e642060d38201527f796f7572206172742e2054617020747769636520746f20756e77696e6420796f60f3820152763ab91036b4b7321711161130ba3a3934b13aba32b9911d60491b61011382015261328e61012a820187612879565b69161134b6b0b3b2911d1160b11b815290506132ad600a820186612879565b7211161130b734b6b0ba34b7b72fbab936111d1160691b815290506132d56013820185612879565b61227d60f01b8152600201979650505050505050565b7f646174613a6170706c69636174696f6e2f6a736f6e3b6261736536342c00000081526000825161332381601d85016020870161235b565b91909101601d0192915050565b634e487b7160e01b600052601160045260246000fd5b60008261335557613355612863565b500490565b808202811582820484141761057d5761057d613330565b8181038181111561057d5761057d613330565b80820260008212600160ff1b841416156133a0576133a0613330565b818105831482151761057d5761057d613330565b6000826133c3576133c3612863565b600160ff1b8214600019841416156133dd576133dd613330565b500590565b80820182811260008312801582168215821617156112e5576112e5613330565b6000835161341481846020880161235b565b603d60f81b908301908152601160f91b6001820152835161343c81600284016020880161235b565b61011160f51b60029290910191820152600401949350505050565b6501e3634b732960d51b81526000825161347881600685016020870161235b565b61179f60f11b6006939091019283015250600801919050565b6000826134a0576134a0612863565b500690565b600083516134b781846020880161235b565b601760f91b90830190815283516134d581600184016020880161235b565b01600101949350505050565b6000601160f91b80835283516134fe81600186016020880161235b565b600193019283015250600201919050565b6e3d913a3930b4ba2fba3cb832911d1160891b8152835160009061353a81600f85016020890161235b565b691116113b30b63ab2911d60b11b600f91840191820152845161356481601984016020890161235b565b607d60f81b60199290910191820152835161358681601a84016020880161235b565b01601a019594505050505056fe0000000000c90f8801921d20025b26d703242abf03ed26e604b6195d057f00350647d97c0710a34507d95b9e08a2009a096a90490a3308bc0afb68050bc3ac350c8bd35e0d53db920e1bc2e40ee387660fab272b1072a0481139f0cf120116d512c8106e138edbb1145576b1151bdf8515e2144416a81305176dd9de183366e818f8b83c19bdcbf31a82a0251b4732ef1c0b826a1ccf8cb31d934fe51e56ca1e1f19f97b1fdcdc1b209f701c2161b39f2223a4c522e541af23a6887e2467775725280c5d25e845b626a8218527679df42826b92828e5714a29a3c4852a61b1012b1f34eb2bdc4e6f2c98fbba2d553afb2e110a622ecc681e2f8752623041c76030fbc54d31b54a5d326e54c73326e2c233def2873496824f354d905636041ad936ba2013376f9e46382493b038d8fe93398cdd323a402dd13af2eeb73ba51e293c56ba703d07c1d53db832a53e680b2c3f1749b73fc5ec974073f21d4121589a41ce1e64427a41d04325c13543d09aec447acd50452456bc45cd358f46756827471cece647c3c22e4869e664490f57ee49b415334a581c9d4afb6c974b9e038f4c3fdff34ce100344d8162c34e2106174ebfe8a44f5e08e24ffb654c5097fc5e5133cc9451ced46e5269126e53028517539b2aef5433027d54ca0a4a556040e255f5a4d2568a34a9571deef957b0d2555842dd5458d40e8c5964649759f3de125a8279995b1035ce5b9d11535c290acc5cb420df5d3e52365dc79d7b5e50015d5ed77c895f5e0db25fe3b38d60686cce60ec382f616f146b61f1003e6271fa6862f201ac637114cc63ef328f646c59bf64e889256563bf9165ddfbd266573cbb66cf811f6746c7d767bd0fbc683257aa68a69e806919e31f698c246b69fd614a6a6d98a36adcc9646b4af2786bb812d06c24295f6c8f351b6cf934fb6d6227f96dca0d146e30e3496e96a99c6efb5f116f5f02b16fc1938470231099708378fe70e2cbc571410804719e2cd171fa394872552c8472af05a67307c3cf735f662573b5ebd0740b53fa745f9dd074b2c8837504d3447555bd4b75a585ce75f42c0a7641af3c768e0ea576d9498877235f2c776c4eda77b417df77fab988784033287884841378c7aba17909a92c794a7c11798a23b079c89f6d7a05eeac7a4210d87a7d055a7ab6cba37aef63237b26cb4e7b5d039d7b920b887bc5e28f7bf8882f7c29fbed7c5a3d4f7c894bdd7cb727237ce3ceb17d0f42177d3980eb7d628ac57d8a5f3f7db0fdf77dd6668e7dfa98a77e1d93e97e3f57fe7e5fe4927e7f39567e9d55fb7eba3a387ed5e5c57ef0585f7f0991c37f2191b37f3857f57f4de4507f62368e7f754e7f7f872bf27f97cebc7fa736b37fb563b27fc255957fce0c3d7fd8878d7fe1c76a7fe9cbbf7ff094777ff621817ffa72d07ffd88597fff62157fffffff4142434445464748494a4b4c4d4e4f505152535455565758595a6162636465666768696a6b6c6d6e6f707172737475767778797a303132333435363738392b2f11a3cf439fb225bfe74225716b6774765670ec1060e3796802e62139d69974daddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efd1413436ac25d03aca50f1065e5c13af57f2d6b99ba49602405b80ae9f2a6c9a00000000000000000000000000000000000000000000000000000000000003200000000000000000000000000000000000000000000000000000000000000002
Deployed Bytecode
0x60806040526004361061018e5760003560e01c806370a08231116100dd57806370a0823114610385578063715018a6146103a55780637449da48146103ba57806379ba5097146103cd5780637cb64759146103e257806383281cec146104025780638d859f3e146104225780638da5cb5b1461043d57806395d89b411461045b578063a22cb46514610470578063a6b4086814610490578063b88d4fde146104b0578063c87b56dd146104c3578063dbd76cd9146104e3578063e985e9c5146104f8578063f2fde38b14610518578063f3b674a41461053857600080fd5b806301ffc9a71461019357806306fdde03146101c8578063081812fc146101ea57806308fe4be614610217578063095ea7b3146102375780630f4161aa1461024c57806318160ddd1461026657806323452b9c1461028d57806323b872dd146102a25780632a55205a146102b55780632eb4a7ab146102f45780633ccfd60b1461030a57806342842e0e1461031f57806356ff1be8146103325780636352211e1461035257806367dce1ed14610372575b600080fd5b34801561019f57600080fd5b506101b36101ae36600461233e565b610558565b60405190151581526020015b60405180910390f35b3480156101d457600080fd5b506101dd610583565b6040516101bf91906123ab565b3480156101f657600080fd5b5061020a6102053660046123be565b610615565b6040516101bf91906123d7565b34801561022357600080fd5b506101dd6102323660046123eb565b610659565b61024a610245366004612424565b6107b8565b005b34801561025857600080fd5b5060d6546101b39060ff1681565b34801561027257600080fd5b5060cc5460cb5403600019015b6040519081526020016101bf565b34801561029957600080fd5b5061024a610858565b61024a6102b0366004612450565b610899565b3480156102c157600080fd5b506102d56102d0366004612491565b610a1f565b604080516001600160a01b0390931683526020830191909152016101bf565b34801561030057600080fd5b5061027f60d55481565b34801561031657600080fd5b5061024a610a44565b61024a61032d366004612450565b610ab0565b34801561033e57600080fd5b5061024a61034d3660046124c8565b610ad0565b34801561035e57600080fd5b5061020a61036d3660046123be565b610aeb565b61024a6103803660046124f4565b610af6565b34801561039157600080fd5b5061027f6103a036600461250f565b610b74565b3480156103b157600080fd5b5061024a610bc2565b61024a6103c8366004612570565b610bd6565b3480156103d957600080fd5b5061024a610d0f565b3480156103ee57600080fd5b5061024a6103fd3660046123be565b610d7b565b34801561040e57600080fd5b506101b361041d366004612665565b610d88565b34801561042e57600080fd5b5061027f662386f26fc1000081565b34801561044957600080fd5b5060d3546001600160a01b031661020a565b34801561046757600080fd5b506101dd610da9565b34801561047c57600080fd5b5061024a61048b3660046126cd565b610db8565b34801561049c57600080fd5b5061024a6104ab366004612702565b610e24565b61024a6104be36600461273a565b610f81565b3480156104cf57600080fd5b506101dd6104de3660046123be565b610fcb565b3480156104ef57600080fd5b5061024a611062565b34801561050457600080fd5b506101b36105133660046127a5565b6110a6565b34801561052457600080fd5b5061024a61053336600461250f565b6110d4565b34801561054457600080fd5b506101b36105533660046127d3565b611147565b60006001600160e01b0319821663152a902d60e11b148061057d575061057d8261118a565b92915050565b606060cd805461059290612829565b80601f01602080910402602001604051908101604052809291908181526020018280546105be90612829565b801561060b5780601f106105e05761010080835404028352916020019161060b565b820191906000526020600020905b8154815290600101906020018083116105ee57829003601f168201915b5050505050905090565b6000610620826111d8565b61063d576040516333d1c03960e21b815260040160405180910390fd5b50600090815260d160205260409020546001600160a01b031690565b60ca5460609060ff1661067a57505060408051602081019091526000815290565b60606000606461ffff85160461ffff16905060005b818160ff1610156106f35760006106b060ff600a840260780316606461120d565b9050836106c7826064850260010160ff16896112ed565b6040516020016106d8929190612895565b60408051601f1981840301815291905293505060010161068f565b506000606461ffff861606905061ffff811615610752578261072f61072261ffff600a8602607803168461120d565b60016064860201886112ed565b604051602001610740929190612895565b60405160208183030381529060405292505b505061078f60658461ffff166103e9811061076f5761076f6128c4565b600a91828204019190066003029054906101000a900462ffffff16611744565b816040516020016107a19291906128da565b604051602081830303815290604052915050919050565b60006107c382610aeb565b9050336001600160a01b038216146107fc576107df81336110a6565b6107fc576040516367d9dca160e11b815260040160405180910390fd5b600082815260d1602052604080822080546001600160a01b0319166001600160a01b0387811691821790925591518593918516917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92591a4505050565b61086061177c565b60d480546001600160a01b03191690556040516000805160206139d88339815191529061088f906000906123d7565b60405180910390a1565b60006108a4826117a7565b9050836001600160a01b0316816001600160a01b0316146108d75760405162a1148160e81b815260040160405180910390fd5b600082815260d1602052604090208054338082146001600160a01b038816909114176109245761090786336110a6565b61092457604051632ce44b5f60e11b815260040160405180910390fd5b6001600160a01b03851661094b57604051633a954ecd60e21b815260040160405180910390fd5b801561095657600082555b6001600160a01b03868116600090815260d0602052604080822080546000190190559187168152208054600101905561099385600160e11b611816565b600085815260cf6020526040812091909155600160e11b841690036109e85760018401600081815260cf602052604081205490036109e65760cb5481146109e657600081815260cf602052604090208490555b505b83856001600160a01b0316876001600160a01b03166000805160206139f883398151915260405160405180910390a4505050505050565b7339ab90066cec746a032d67e4fe3378f16294cf6b61271060fa8302045b9250929050565b6040516000907339ab90066cec746a032d67e4fe3378f16294cf6b9047908381818185875af1925050503d8060008114610a9a576040519150601f19603f3d011682016040523d82523d6000602084013e610a9f565b606091505b5050905080610aad57600080fd5b50565b610acb83838360405180602001604052806000815250610f81565b505050565b610ad861177c565b60d6805460ff1916911515919091179055565b600061057d826117a7565b60d65460ff16610b19576040516393e8222560e01b815260040160405180910390fd5b600560ff82161115610b3e576040516392e5b1c960e01b815260040160405180910390fd5b34662386f26fc100008260ff160214610b6a5760405163569e8c1160e01b815260040160405180910390fd5b610aad338261182b565b60006001600160a01b038216610b9d576040516323d3ad8160e21b815260040160405180910390fd5b506001600160a01b0316600090815260d060205260409020546001600160401b031690565b610bca61177c565b610bd46000611918565b565b8282610be3828233611147565b610c0057604051630e5060e160e21b815260040160405180910390fd5b34662386f26fc100008460ff160214610c2c5760405163569e8c1160e01b815260040160405180910390fd5b7f000000000000000000000000000000000000000000000000000000000000032061ffff168360ff16610c6260cb546000190190565b011115610c8257604051637c44f5af60e11b815260040160405180910390fd5b33600090815260d7602052604090205460ff7f000000000000000000000000000000000000000000000000000000000000000281169181168501161115610cdc57604051632ae6c11560e21b815260040160405180910390fd5b33600081815260d760205260409020805460ff80821687011660ff19909116179055610d08908461182b565b5050505050565b60d4546001600160a01b0316338114610d3b57604051636b7584e760e11b815260040160405180910390fd5b60d480546001600160a01b03191690556040516000805160206139d883398151915290610d6a906000906123d7565b60405180910390a1610aad81611918565b610d8361177c565b60d555565b6000610d9f848460d554858051906020012061196a565b90505b9392505050565b606060ce805461059290612829565b33600081815260d2602090815260408083206001600160a01b03871680855290835292819020805460ff191686151590811790915590519081529192917f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31910160405180910390a35050565b33610e2e83610aeb565b6001600160a01b031614610e5557604051633533b18360e11b815260040160405180910390fd5b62ffffff811615801590610eea57506040516331a9108f60e11b815262ffffff82166004820152339073ff6000a85baac9c4854faa7155e70ba850bf726b90636352211e90602401602060405180830381865afa158015610eba573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ede91906129fe565b6001600160a01b031614155b15610f0857604051630140282360e71b815260040160405180910390fd5b806065836103e98110610f1d57610f1d6128c4565b600a91828204019190066003026101000a81548162ffffff021916908362ffffff1602179055507ff8e1a15aba9398e019f0b49df1a4fde98ee17ae345cb5f6b5e2c27f5033e8ce782604051610f7591815260200190565b60405180910390a15050565b610f8c848484610899565b6001600160a01b0383163b15610fc557610fa884848484611984565b610fc5576040516368d2bf6b60e11b815260040160405180910390fd5b50505050565b6060610fd6826111d8565b610ff357604051630a14c4b560e41b815260040160405180910390fd5b6000610ffe83610659565b9050610da28361100d83611a6c565b611035846040516020016110219190612a1b565b604051602081830303815290604052611a87565b6040516020016110459190612e5e565b60405160208183030381529060405261105d87611bd9565b611d37565b61106a61177c565b60ca5460ff161561108e5760405163532c890b60e11b815260040160405180910390fd5b60ca805460ff19166001179055610bd433600561182b565b6001600160a01b03918216600090815260d26020908152604080832093909416825291909152205460ff1690565b6110dc61177c565b6001600160a01b03811661110357604051633a247dd760e11b815260040160405180910390fd5b60d480546001600160a01b0319166001600160a01b0383161790556040516000805160206139d88339815191529061113c9083906123d7565b60405180910390a150565b60d5546040516001600160601b0319606084901b166020820152600091610d9f91869186916034016040516020818303038152906040528051906020012061196a565b60006301ffc9a760e01b6001600160e01b0319831614806111bb57506380ac58cd60e01b6001600160e01b03198316145b8061057d5750506001600160e01b031916635b5e139f60e01b1490565b6000816001111580156111ec575060cb5482105b801561057d575050600090815260cf6020526040902054600160e01b161590565b60608161ffff166001600160401b0381111561122b5761122b6125c3565b60405190808252806020026020018201604052801561126457816020015b61125161230e565b8152602001906001900390816112495790505b50905060008261ffff1668138400eca364a000008161128557611285612863565b046001600160481b0316905060005b8361ffff168163ffffffff1610156112e55763ffffffff811682026112b98682611d84565b848363ffffffff16815181106112d1576112d16128c4565b602090810291909101015250600101611294565b505092915050565b606083516001036114f4576114ed61133a60405180604001604052806002815260200161783160f01b8152506040518060400160405280600381526020016203230360ec1b815250611e56565b61137960405180604001604052806002815260200161793160f01b8152506040518060400160405280600381526020016203230360ec1b815250611e56565b6113b8604051806040016040528060028152602001613c1960f11b8152506040518060400160405280600381526020016203230360ec1b815250611e56565b6113f7604051806040016040528060028152602001613c9960f11b8152506040518060400160405280600381526020016203230360ec1b815250611e56565b6114446040518060400160405280600e81526020016d07374726f6b652d6c696e656361760941b815250604051806040016040528060058152602001641c9bdd5b9960da1b815250611e56565b611486604051806040016040528060068152602001657374726f6b6560d01b81525061148160008c61ffff166103e9811061076f5761076f6128c4565b611e56565b6114c3604051806040016040528060028152602001611a5960f21b815250604051806040016040528060018152602001606d60f81b815250611e56565b6040516020016114d99796959493929190612e9c565b604051602081830303815290604052611e82565b9050610da2565b60005b84518161ffff16101561173c57600085518260010161ffff16149050600085830161ffff1690508361170d61156a60405180604001604052806002815260200161783160f01b8152506114818b8861ffff1681518110611559576115596128c4565b602002602001015160000151611e95565b6115b260405180604001604052806002815260200161793160f01b8152506114818c8961ffff16815181106115a1576115a16128c4565b602002602001015160200151611e95565b611604604051806040016040528060028152602001613c1960f11b815250611481886115f1578d8a60010161ffff1681518110611559576115596128c4565b8d600081518110611559576115596128c4565b611656604051806040016040528060028152602001613c9960f11b81525061148189611643578e8b60010161ffff16815181106115a1576115a16128c4565b8e6000815181106115a1576115a16128c4565b611695604051806040016040528060068152602001657374726f6b6560d01b81525061148160008a63ffffffff166103e9811061076f5761076f6128c4565b8763ffffffff168c61ffff16146116bb57604051806020016040528060008152506116f8565b6116f8604051806040016040528060028152602001611a5960f21b815250604051806040016040528060018152602001606d60f81b815250611e56565b6040516020016114d996959493929190612f2e565b60405160200161171e929190612895565b604051602081830303815290604052935050508060010190506114f7565b509392505050565b606061175662ffffff83166003611f20565b6040516020016117669190612fad565b6040516020818303038152906040529050919050565b60d3546001600160a01b03163314610bd457604051635fc483c560e01b815260040160405180910390fd5b600081806001116117fd5760cb548110156117fd57600081815260cf602052604081205490600160e01b821690036117fb575b80600003610da2575060001901600081815260cf60205260409020546117da565b505b604051636f96cda160e11b815260040160405180910390fd5b4260a01b176001600160a01b03919091161790565b60ca5460ff1661184e57604051630314872760e11b815260040160405180910390fd5b600061185960cb5490565b905060ff821681016103e910156118835760405163d05cb60960e01b815260040160405180910390fd5b805b8260ff16820181101561190a57604080514460208201529081018290526118c49060600160405160208183030381529060405280519060200120611fa8565b6000826103e981106118d8576118d86128c4565b600a91828204019190066003026101000a81548162ffffff021916908362ffffff160217905550806001019050611885565b50610acb838360ff16612005565b60d380546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b6000826119788686856120ed565b1490505b949350505050565b604051630a85bd0160e11b81526000906001600160a01b0385169063150b7a02906119b9903390899088908890600401612fd6565b6020604051808303816000875af19250505080156119f4575060408051601f3d908101601f191682019092526119f191810190613013565b60015b611a52573d808015611a22576040519150601f19603f3d011682016040523d82523d6000602084013e611a27565b606091505b508051600003611a4a576040516368d2bf6b60e11b815260040160405180910390fd5b805181602001fd5b6001600160e01b031916630a85bd0160e11b14905061197c565b6060611a7782611a87565b6040516020016117669190613030565b60608151600003611aa657505060408051602081019091526000815290565b600060405180606001604052806040815260200161399860409139905060006003845160020181611ad957611ad9612863565b0460040290506000816020016001600160401b03811115611afc57611afc6125c3565b6040519080825280601f01601f191660200182016040528015611b26576020820181803683370190505b509050818152600183018586518101602084015b81831015611b945760039283018051603f601282901c811687015160f890811b8552600c83901c8216880151811b6001860152600683901c8216880151811b60028601529116860151901b93820193909352600401611b3a565b600389510660018114611bae5760028114611bbf57611bcb565b613d3d60f01b600119830152611bcb565b603d60f81b6000198301525b509398975050505050505050565b60606000606483069050611c1c6040518060400160405280600c81526020016b546f74616c20506f696e747360a01b815250611c148561212f565b600180612173565b604080518082019091526005815264088cae0e8d60db1b6020820152611c4890611c146064870461212f565b611c8e60405180604001604052806012815260200171496e6e657220536861706520506f696e747360701b815250611c1460008611611c8857606461212f565b8561212f565b611cd86040518060400160405280600e81526020016d2732bb902634b7329021b7b637b960911b815250611ccf6000896103e9811061076f5761076f6128c4565b60006001612173565b611d236040518060400160405280601081526020016f2130b1b5b3b937bab7321021b7b637b960811b815250611d1b60658a6103e9811061076f5761076f6128c4565b600080612173565b6040516020016107a1959493929190613072565b6060611d5b611d458661212f565b83868660405160200161102194939291906130fc565b604051602001611d6b91906132eb565b6040516020818303038152906040529050949350505050565b611d8c61230e565b6000611da16002672b992ddfa23249d6613346565b611db4672b992ddfa23249d6600261335a565b611dbe9190613371565b6809c2007651b2500000611dda672b992ddfa23249d686613384565b611de491906133b4565b611dee91906133e2565b90506040518060400160405280611e04836121e9565b611e0e9087613384565b611e2190680ad78ebc5ac62000006133e2565b8152602001611e2f836121fa565b611e399087613384565b611e4c90680ad78ebc5ac62000006133e2565b9052949350505050565b60608282604051602001611e6b929190613402565b604051602081830303815290604052905092915050565b6060816040516020016117669190613457565b60606000611eab670de0b6b3a764000084613346565b90506000611ec1670de0b6b3a764000085613491565b905067016345785d8a0000811115611ee557611ee264e8d4a5100082613346565b90505b611eee8261212f565b611ef78261212f565b604051602001611f089291906134a5565b60405160208183030381529060405292505050919050565b6060601f1960428360011b01166040510190506020810160405260008152806f30313233343536373839616263646566600f528283018203600119855b600f811651948201946001860153600f8160041c1651855360081c848303611f5d578015611f9357632194895a6000526004601cfd5b505050819003601f1990910190815292915050565b6000611fc06040518060200160405280600081525090565b828152611fcf816101006122c4565b60ff166008611fe0836101006122c4565b62ffffff16901b6010611ff5846101006122c4565b62ffffff16901b01019392505050565b60cb54600082900361202a5760405163b562e8dd60e01b815260040160405180910390fd5b6001600160a01b038316600090815260d06020526040902080546001600160401b018402019055612061836001841460e11b611816565b600082815260cf60205260408120919091556001600160a01b0384169083830190839083906000805160206139f88339815191528180a4600183015b8181146120c357808360006000805160206139f8833981519152600080a460010161209d565b50816000036120e457604051622e076360e81b815260040160405180910390fd5b60cb5550505050565b600081815b848110156121265761211c82878784818110612110576121106128c4565b905060200201356122e2565b91506001016120f2565b50949350505050565b60606080604051019050602081016040526000815280600019835b928101926030600a8206018453600a90048061214a575050819003601f19909101908152919050565b606084836121a0578460405160200161218c91906134e1565b6040516020818303038152906040526121a2565b845b836121bc57604051806020016040528060008152506121d7565b604051806040016040528060018152602001600b60fa1b8152505b604051602001611d6b9392919061350f565b600061057d6715cc96efd11924eb83015b6757325bbf446493ac9081900663400000000204600061ffff600483901c1660ff601484901c1663100000008416156320000000851615158161223f5760ff92909203915b60006040518061044001604052806104048152602001613594610404913960046002860102818101519192509063ffffffff602082901c8116908216818103890260101c60008861229257818303612296565b8184015b905087156122a2576000035b637fffffff670de0b6b3a76400008202059d9c50505050505050505050505050565b60005b60208320905080835281826000030681106122c75706919050565b60008183106122fe576000828152602084905260409020610da2565b5060009182526020526040902090565b604051806040016040528060008152602001600081525090565b6001600160e01b031981168114610aad57600080fd5b60006020828403121561235057600080fd5b8135610da281612328565b60005b8381101561237657818101518382015260200161235e565b50506000910152565b6000815180845261239781602086016020860161235b565b601f01601f19169290920160200192915050565b602081526000610da2602083018461237f565b6000602082840312156123d057600080fd5b5035919050565b6001600160a01b0391909116815260200190565b6000602082840312156123fd57600080fd5b813561ffff81168114610da257600080fd5b6001600160a01b0381168114610aad57600080fd5b6000806040838503121561243757600080fd5b82356124428161240f565b946020939093013593505050565b60008060006060848603121561246557600080fd5b83356124708161240f565b925060208401356124808161240f565b929592945050506040919091013590565b600080604083850312156124a457600080fd5b50508035926020909101359150565b803580151581146124c357600080fd5b919050565b6000602082840312156124da57600080fd5b610da2826124b3565b803560ff811681146124c357600080fd5b60006020828403121561250657600080fd5b610da2826124e3565b60006020828403121561252157600080fd5b8135610da28161240f565b60008083601f84011261253e57600080fd5b5081356001600160401b0381111561255557600080fd5b6020830191508360208260051b8501011115610a3d57600080fd5b60008060006040848603121561258557600080fd5b83356001600160401b0381111561259b57600080fd5b6125a78682870161252c565b90945092506125ba9050602085016124e3565b90509250925092565b634e487b7160e01b600052604160045260246000fd5b600082601f8301126125ea57600080fd5b81356001600160401b0380821115612604576126046125c3565b604051601f8301601f19908116603f0116810190828211818310171561262c5761262c6125c3565b8160405283815286602085880101111561264557600080fd5b836020870160208301376000602085830101528094505050505092915050565b60008060006040848603121561267a57600080fd5b83356001600160401b038082111561269157600080fd5b61269d8783880161252c565b909550935060208601359150808211156126b657600080fd5b506126c3868287016125d9565b9150509250925092565b600080604083850312156126e057600080fd5b82356126eb8161240f565b91506126f9602084016124b3565b90509250929050565b6000806040838503121561271557600080fd5b82359150602083013562ffffff8116811461272f57600080fd5b809150509250929050565b6000806000806080858703121561275057600080fd5b843561275b8161240f565b9350602085013561276b8161240f565b92506040850135915060608501356001600160401b0381111561278d57600080fd5b612799878288016125d9565b91505092959194509250565b600080604083850312156127b857600080fd5b82356127c38161240f565b9150602083013561272f8161240f565b6000806000604084860312156127e857600080fd5b83356001600160401b038111156127fe57600080fd5b61280a8682870161252c565b909450925050602084013561281e8161240f565b809150509250925092565b600181811c9082168061283d57607f821691505b60208210810361285d57634e487b7160e01b600052602260045260246000fd5b50919050565b634e487b7160e01b600052601260045260246000fd5b6000815161288b81856020860161235b565b9290920192915050565b600083516128a781846020880161235b565b8351908301906128bb81836020880161235b565b01949350505050565b634e487b7160e01b600052603260045260246000fd5b7f3c73766720786d6c6e733d22687474703a2f2f7777772e77332e6f72672f323081527f30302f737667222077696474683d223130302522206865696768743d2231303060208201527f25222076696577426f783d22302030203430302034303022207374796c653d2260408201526a3130b1b5b3b937bab7321d60a91b60608201526000835161297281606b85016020880161235b565b7f3b646973706c61793a626c6f636b3b6d617267696e3a6175746f223e3c737479606b918401918201527f6c653e6c696e657b7374726f6b652d77696474683a31303b7d3c2f7374796c65608b820152601f60f91b60ab82015283516129df8160ac84016020880161235b565b651e17b9bb339f60d11b60ac929091019182015260b201949350505050565b600060208284031215612a1057600080fd5b8151610da28161240f565b7f3c68746d6c207374796c653d226f766572666c6f773a68696464656e223e3c6281527437b23c9039ba3cb6329e9136b0b933b4b71d18111f60591b602082015260008251612a7181603585016020870161235b565b7f3c7363726970743e6c657420613d21312c623d21312c6f3d41727261792e667260359390910192830152507f6f6d28646f63756d656e742e676574456c656d656e747342795461674e616d6560558201527f28226c696e652229292e6d617028653d3e652e7374796c652e7374726f6b652960758201527f2c733d653d3e6e65772050726f6d69736528743d3e73657454696d656f75742860958201527f742c6529292c633d28293d3e4d6174682e666c6f6f72283235362a4d6174682e60b58201527f72616e646f6d2829293b646f63756d656e742e626f64792e6164644576656e7460d58201527f4c697374656e65722822636c69636b222c6173796e6328293d3e7b696628216160f58201527f7c7c622972657475726e3b623d21303b6c657420653d646f63756d656e742e676101158201527f6574456c656d656e747342795461674e616d6528226c696e6522293b666f72286101358201527f3b613b297b666f72286c657420743d303b743c652e6c656e6774683b742b2b296101558201527f655b745d2e7374796c652e7374726f6b653d6072676228247b6328297d2c247b6101758201527f6328297d2c247b6328297d29603b61776169742073283530297d666f72286c656101958201527f74206c3d303b6c3c652e6c656e6774683b6c2b2b29655b6c5d2e7374796c652e6101b58201527f7374726f6b653d6f5b6c5d3b623d21317d2c2130292c646f63756d656e742e626101d58201527f6f64792e6164644576656e744c697374656e65722822636c69636b222c6173796101f58201527f6e6328293d3e7b696628612972657475726e3b613d21303b6c657420653d646f6102158201527f63756d656e742e676574456c656d656e747342795461674e616d6528226c696e6102358201527f6522293b666f72286c657420743d303b743c322a652e6c656e6774683b742b2b6102558201527f297b6c6574206c3d743c652e6c656e6774683f2230223a2231222c6e3d743c656102758201527f2e6c656e6774683f743a322a652e6c656e6774682d742d313b226d22213d3d656102958201527f5b6e5d2e6964262628655b6e5d2e7374796c652e7374726f6b654f70616369746102b58201527f793d6c293b6c657420673d652e6c656e677468253130303b61776169742073286102d58201527f28743c652e6c656e6774683f743e3d652e6c656e6774682d673a743e652e6c656102f58201527f6e6774682626742d652e6c656e6774683c3d67293f32302b283130302d67292f6103158201527f3130302a37353a3130297d613d21317d2c2130293b3c2f7363726970743e3c2f6103358201526b3137b23c9f1e17b43a36b61f60a11b61035582015261036101919050565b7519185d184e9d195e1d0bda1d1b5b0ed8985cd94d8d0b60521b815260008251612e8f81601685016020870161235b565b9190910160160192915050565b600088516020612eaf8285838e0161235b565b895191840191612ec28184848e0161235b565b8951920191612ed48184848d0161235b565b8851920191612ee68184848c0161235b565b8751920191612ef88184848b0161235b565b8651920191612f0a8184848a0161235b565b8551920191612f1c818484890161235b565b919091019a9950505050505050505050565b600087516020612f418285838d0161235b565b885191840191612f548184848d0161235b565b8851920191612f668184848c0161235b565b8751920191612f788184848b0161235b565b8651920191612f8a8184848a0161235b565b8551920191612f9c818484890161235b565b919091019998505050505050505050565b602360f81b815260008251612fc981600185016020870161235b565b9190910160010192915050565b6001600160a01b03858116825284166020820152604081018390526080606082018190526000906130099083018461237f565b9695505050505050565b60006020828403121561302557600080fd5b8151610da281612328565b7919185d184e9a5b5859d94bdcdd99cade1b5b0ed8985cd94d8d0b60321b81526000825161306581601a85016020870161235b565b91909101601a0192915050565b605b60f81b815260006001875161308f8183860160208c0161235b565b8751908401906130a58184840160208c0161235b565b87519101906130ba8184840160208b0161235b565b86519101906130cf8184840160208a0161235b565b85519101906130e4818484016020890161235b565b605d60f81b9101820190815201979650505050505050565b727b226e616d65223a22546f676574686572202360681b8152845160009061312b816013850160208a0161235b565b7f222c226465736372697074696f6e223a22546f676574686572206973206120676013918401918201527f656e65726174697665206f6e636861696e204e4654207769746820636f6c6f7260338201527f66756c2061727420666f726d656420627920697473206d696e746572732e205460538201527f686520617274206f66206561636820746f6b656e206275696c6473206f6e207460738201527f686520617274206f6620616c6c2070726576696f757320746f6b656e7320706c60938201527f75732061206e6577206c696e652067656e65726174656420617420746865207460b38201527f696d65206f66206d696e742e20546170206f6e636520746f20756e77696e642060d38201527f796f7572206172742e2054617020747769636520746f20756e77696e6420796f60f3820152763ab91036b4b7321711161130ba3a3934b13aba32b9911d60491b61011382015261328e61012a820187612879565b69161134b6b0b3b2911d1160b11b815290506132ad600a820186612879565b7211161130b734b6b0ba34b7b72fbab936111d1160691b815290506132d56013820185612879565b61227d60f01b8152600201979650505050505050565b7f646174613a6170706c69636174696f6e2f6a736f6e3b6261736536342c00000081526000825161332381601d85016020870161235b565b91909101601d0192915050565b634e487b7160e01b600052601160045260246000fd5b60008261335557613355612863565b500490565b808202811582820484141761057d5761057d613330565b8181038181111561057d5761057d613330565b80820260008212600160ff1b841416156133a0576133a0613330565b818105831482151761057d5761057d613330565b6000826133c3576133c3612863565b600160ff1b8214600019841416156133dd576133dd613330565b500590565b80820182811260008312801582168215821617156112e5576112e5613330565b6000835161341481846020880161235b565b603d60f81b908301908152601160f91b6001820152835161343c81600284016020880161235b565b61011160f51b60029290910191820152600401949350505050565b6501e3634b732960d51b81526000825161347881600685016020870161235b565b61179f60f11b6006939091019283015250600801919050565b6000826134a0576134a0612863565b500690565b600083516134b781846020880161235b565b601760f91b90830190815283516134d581600184016020880161235b565b01600101949350505050565b6000601160f91b80835283516134fe81600186016020880161235b565b600193019283015250600201919050565b6e3d913a3930b4ba2fba3cb832911d1160891b8152835160009061353a81600f85016020890161235b565b691116113b30b63ab2911d60b11b600f91840191820152845161356481601984016020890161235b565b607d60f81b60199290910191820152835161358681601a84016020880161235b565b01601a019594505050505056fe0000000000c90f8801921d20025b26d703242abf03ed26e604b6195d057f00350647d97c0710a34507d95b9e08a2009a096a90490a3308bc0afb68050bc3ac350c8bd35e0d53db920e1bc2e40ee387660fab272b1072a0481139f0cf120116d512c8106e138edbb1145576b1151bdf8515e2144416a81305176dd9de183366e818f8b83c19bdcbf31a82a0251b4732ef1c0b826a1ccf8cb31d934fe51e56ca1e1f19f97b1fdcdc1b209f701c2161b39f2223a4c522e541af23a6887e2467775725280c5d25e845b626a8218527679df42826b92828e5714a29a3c4852a61b1012b1f34eb2bdc4e6f2c98fbba2d553afb2e110a622ecc681e2f8752623041c76030fbc54d31b54a5d326e54c73326e2c233def2873496824f354d905636041ad936ba2013376f9e46382493b038d8fe93398cdd323a402dd13af2eeb73ba51e293c56ba703d07c1d53db832a53e680b2c3f1749b73fc5ec974073f21d4121589a41ce1e64427a41d04325c13543d09aec447acd50452456bc45cd358f46756827471cece647c3c22e4869e664490f57ee49b415334a581c9d4afb6c974b9e038f4c3fdff34ce100344d8162c34e2106174ebfe8a44f5e08e24ffb654c5097fc5e5133cc9451ced46e5269126e53028517539b2aef5433027d54ca0a4a556040e255f5a4d2568a34a9571deef957b0d2555842dd5458d40e8c5964649759f3de125a8279995b1035ce5b9d11535c290acc5cb420df5d3e52365dc79d7b5e50015d5ed77c895f5e0db25fe3b38d60686cce60ec382f616f146b61f1003e6271fa6862f201ac637114cc63ef328f646c59bf64e889256563bf9165ddfbd266573cbb66cf811f6746c7d767bd0fbc683257aa68a69e806919e31f698c246b69fd614a6a6d98a36adcc9646b4af2786bb812d06c24295f6c8f351b6cf934fb6d6227f96dca0d146e30e3496e96a99c6efb5f116f5f02b16fc1938470231099708378fe70e2cbc571410804719e2cd171fa394872552c8472af05a67307c3cf735f662573b5ebd0740b53fa745f9dd074b2c8837504d3447555bd4b75a585ce75f42c0a7641af3c768e0ea576d9498877235f2c776c4eda77b417df77fab988784033287884841378c7aba17909a92c794a7c11798a23b079c89f6d7a05eeac7a4210d87a7d055a7ab6cba37aef63237b26cb4e7b5d039d7b920b887bc5e28f7bf8882f7c29fbed7c5a3d4f7c894bdd7cb727237ce3ceb17d0f42177d3980eb7d628ac57d8a5f3f7db0fdf77dd6668e7dfa98a77e1d93e97e3f57fe7e5fe4927e7f39567e9d55fb7eba3a387ed5e5c57ef0585f7f0991c37f2191b37f3857f57f4de4507f62368e7f754e7f7f872bf27f97cebc7fa736b37fb563b27fc255957fce0c3d7fd8878d7fe1c76a7fe9cbbf7ff094777ff621817ffa72d07ffd88597fff62157fffffff4142434445464748494a4b4c4d4e4f505152535455565758595a6162636465666768696a6b6c6d6e6f707172737475767778797a303132333435363738392b2f11a3cf439fb225bfe74225716b6774765670ec1060e3796802e62139d69974daddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
d1413436ac25d03aca50f1065e5c13af57f2d6b99ba49602405b80ae9f2a6c9a00000000000000000000000000000000000000000000000000000000000003200000000000000000000000000000000000000000000000000000000000000002
-----Decoded View---------------
Arg [0] : allowListMerkleRoot (bytes32): 0xd1413436ac25d03aca50f1065e5c13af57f2d6b99ba49602405b80ae9f2a6c9a
Arg [1] : allowListMintMaxTotalVal (uint16): 800
Arg [2] : allowListMintMaxPerWalletVal (uint8): 2
-----Encoded View---------------
3 Constructor Arguments found :
Arg [0] : d1413436ac25d03aca50f1065e5c13af57f2d6b99ba49602405b80ae9f2a6c9a
Arg [1] : 0000000000000000000000000000000000000000000000000000000000000320
Arg [2] : 0000000000000000000000000000000000000000000000000000000000000002
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
A token is a representation of an on-chain or off-chain asset. The token page shows information such as price, total supply, holders, transfers and social links. Learn more about this page in our Knowledge Base.