ERC-721
Overview
Max Total Supply
1 ✦ SHOWTIME
Holders
1
Market
Volume (24H)
N/A
Min Price (24H)
N/A
Max Price (24H)
N/A
Other Info
Token Contract
Balance
1 ✦ SHOWTIMELoading...
Loading
Loading...
Loading
Loading...
Loading
Minimal Proxy Contract for 0x50c00112ebf4dedae9712a3fc524505748b8a6dd
Contract Name:
Edition
Compiler Version
v0.8.17+commit.8df45f5f
Optimization Enabled:
Yes with 1000000 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: GPL-3.0 /** * █▄░█ █▀▀ ▀█▀ █▀▀ █▀▄ █ ▀█▀ █ █▀█ █▄░█ █▀ * █░▀█ █▀░ ░█░ ██▄ █▄▀ █ ░█░ █ █▄█ █░▀█ ▄█ * * ▀█ █▀█ █▀█ ▄▀█ * █▄ █▄█ █▀▄ █▀█ */ pragma solidity ^0.8.6; import { IERC2981Upgradeable, IERC165Upgradeable } from "@openzeppelin-contracts-upgradeable/interfaces/IERC2981Upgradeable.sol"; import {AddressUpgradeable} from "@openzeppelin-contracts-upgradeable/utils/AddressUpgradeable.sol"; import {ERC721, ERC721I} from "./solmate-initializable/tokens/ERC721I.sol"; import {OwnedInitializable} from "./solmate-initializable/auth/OwnedInitializable.sol"; import {EditionMetadataRenderer} from "./EditionMetadataRenderer.sol"; import {IEdition} from "./interfaces/IEdition.sol"; import {OptionalOperatorFilterer} from "./utils/OptionalOperatorFilterer.sol"; import "./interfaces/Errors.sol"; /// @notice This is a smart contract for handling dynamic contract minting. /// @dev This allows creators to mint a unique serial edition of the same media within a custom contract /// @dev This is a fork of ZORA Editions for Showtime Drops /// @author karmacoma [Showtime Drops](https://github.com/showtime-xyz/nft-editions) /// @author iain nash [ZORA Editions](https://github.com/ourzora/nft-editions) contract Edition is EditionMetadataRenderer, ERC721I, IEdition, IERC2981Upgradeable, OwnedInitializable, OptionalOperatorFilterer { struct EditionState { // how many tokens have been minted (can not be more than editionSize) uint64 numberMinted; // reserved space to keep the state a uint256 uint16 __reserved; // Price to mint in twei (1 twei = 1000 gwei), so the supported price range is 0.000001 to 4294.967295 ETH // To accept ERC20 or a different price range, use a specialized sales contract as the approved minter uint32 salePriceTwei; // Royalty amount in bps (uint16 is large enough to store 10000 bps) uint16 royaltyBPS; // the edition can be minted up to this timestamp in seconds -- 0 means no end date uint64 endOfMintPeriod; // Total size of edition that can be minted uint64 editionSize; } EditionState private state; // Addresses allowed to mint edition mapping(address => bool) allowedMinters; /// @notice Function to create a new edition. Can only be called by the allowed creator /// Sets the only allowed minter to the address that creates/owns the edition. /// This can be re-assigned or updated later /// @param _owner User that owns and can mint the edition, gets royalty and sales payouts and can update the base url if needed. /// @param _name Name of edition, used in the title as "$NAME NUMBER/TOTAL" /// @param _symbol Symbol of the new token contract /// @param _description Description of edition, used in the description field of the NFT /// @param _imageUrl Image URL of the edition. Strongly encouraged to be used, but if necessary, only animation URL can be used. One of animation and image url need to exist in a edition to render the NFT. /// @param _animationUrl Animation URL of the edition. Not required, but if omitted image URL needs to be included. This follows the opensea spec for NFTs /// @param _editionSize Number of editions that can be minted in total. If 0, unlimited editions can be minted. /// @param _royaltyBPS BPS of the royalty set on the contract. Can be 0 for no royalty. /// @param _mintPeriodSeconds The amount of time in seconds after which editions can no longer be minted or purchased. Use 0 to have no expiration function initialize( address _owner, string calldata _name, string calldata _symbol, string calldata _description, string calldata _animationUrl, string calldata _imageUrl, uint256 _editionSize, uint256 _royaltyBPS, uint256 _mintPeriodSeconds ) public override initializer { __ERC721_init(_name, _symbol); // Set ownership to original sender of contract call __Owned_init(_owner); description = _description; animationUrl = _animationUrl; imageUrl = _imageUrl; uint64 _endOfMintPeriod; if (_mintPeriodSeconds > 0) { // overflows are not expected to happen for timestamps, and have no security implications unchecked { uint256 endOfMintPeriodUint256 = block.timestamp + _mintPeriodSeconds; _endOfMintPeriod = requireUint64(endOfMintPeriodUint256); } } state = EditionState({ editionSize: requireUint64(_editionSize), endOfMintPeriod: _endOfMintPeriod, royaltyBPS: requireUint16(_royaltyBPS), salePriceTwei: 0, numberMinted: 0, __reserved: 0 }); } /*////////////////////////////////////////////////////////////// OPERATOR FILTERER OVERRIDES //////////////////////////////////////////////////////////////*/ function transferFrom(address from, address to, uint256 tokenId) public override onlyAllowedOperator(from) { super.transferFrom(from, to, tokenId); } function safeTransferFrom(address from, address to, uint256 tokenId) public override onlyAllowedOperator(from) { super.safeTransferFrom(from, to, tokenId); } function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) public override onlyAllowedOperator(from) { super.safeTransferFrom(from, to, tokenId, data); } /*////////////////////////////////////////////////////////////// CREATOR / COLLECTION OWNER FUNCTIONS //////////////////////////////////////////////////////////////*/ /// @notice This sets a simple ETH sales price /// Setting a sales price allows users to mint the edition until it sells out. /// The supported price range is 0.000001 to 4294.967295 ETH (or relevant chain gas token) /// For more granular sales, use an external sales contract. /// @param _salePriceWei sale price in wei, 0 to disable sales function setSalePrice(uint256 _salePriceWei) external override onlyOwner { // convert to milli-eth internally uint32 salePriceTwei = requireUint32(_salePriceWei / 1e12); if (salePriceTwei == 0 && _salePriceWei > 0) { revert PriceTooLow(); } state.salePriceTwei = salePriceTwei; emit PriceChanged(_salePriceWei); } /// @dev This withdraws ETH from the contract to the contract owner. function withdraw() external override onlyOwner { // No need for gas limit to trusted address. AddressUpgradeable.sendValue(payable(owner), address(this).balance); } /// @notice Sets the approved minting status of the given address. /// @param minter address to set approved minting status for /// @param allowed boolean if that address is allowed to mint /// @dev This requires that msg.sender is the owner of the given edition id. /// @dev If the ZeroAddress (address(0x0)) is set as a minter, anyone will be allowed to mint. /// @dev This setup is similar to setApprovalForAll in the ERC721 spec. function setApprovedMinter(address minter, bool allowed) public override onlyOwner { allowedMinters[minter] = allowed; } /// @notice Updates the external_url field in the metadata function setExternalUrl(string calldata _externalUrl) public override onlyOwner { emit ExternalUrlUpdated(externalUrl, _externalUrl); externalUrl = _externalUrl; } function setStringProperties(string[] calldata names, string[] calldata values) public override onlyOwner { uint256 length = names.length; if (values.length != length) { revert LengthMismatch(); } namesOfStringProperties = names; for (uint256 i = 0; i < length;) { string calldata name = names[i]; string calldata value = values[i]; if (bytes(name).length == 0 || bytes(value).length == 0) { revert BadAttribute(name, value); } emit PropertyUpdated(name, stringProperties[name], value); stringProperties[name] = value; unchecked { ++i; } } } function setOperatorFilter(address operatorFilter) public override onlyOwner { _setOperatorFilter(operatorFilter); } function enableDefaultOperatorFilter() public override onlyOwner { _setOperatorFilter(CANONICAL_OPENSEA_SUBSCRIPTION); } /*////////////////////////////////////////////////////////////// COLLECTOR / TOKEN OWNER FUNCTIONS //////////////////////////////////////////////////////////////*/ /// @param to address to send the newly minted edition to /// @dev This mints one edition to the given address by an allowed minter function mint(address to) external payable override returns (uint256 tokenId) { tokenId = _mintPreFlightChecks(1); _mint(to, tokenId); } function safeMint(address to) external payable override returns (uint256 tokenId) { tokenId = _mintPreFlightChecks(1); _safeMint(to, tokenId); } /// @param recipients list of addresses to send the newly minted editions to /// @dev This mints multiple editions to the given list of addresses. function mintBatch(address[] calldata recipients) external payable override returns (uint256 lastTokenId) { uint64 n = uint64(recipients.length); if (n == 0) { revert InvalidArgument(); } lastTokenId = _mintPreFlightChecks(n); unchecked { uint256 firstTokenId = lastTokenId + 1 - n; for (uint256 i = 0; i < n;) { _safeMint(recipients[i], firstTokenId + i); ++i; } } } /*////////////////////////////////////////////////////////////// INTERNAL FUNCTIONS //////////////////////////////////////////////////////////////*/ function requireUint16(uint256 value) internal pure returns (uint16) { if (value > uint256(type(uint16).max)) { revert IntegerOverflow(value); } return uint16(value); } function requireUint32(uint256 value) internal pure returns (uint32) { if (value > uint256(type(uint32).max)) { revert IntegerOverflow(value); } return uint32(value); } function requireUint64(uint256 value) internal pure returns (uint64) { if (value > uint256(type(uint64).max)) { revert IntegerOverflow(value); } return uint64(value); } /// @dev stateless version of isMintingEnded function enforceTimeLimit(uint64 _endOfMintPeriod) internal view { if (_endOfMintPeriod > 0 && uint64(block.timestamp) > _endOfMintPeriod) { revert TimeLimitReached(); } } function enforceSupplyLimit(uint64 _editionSize, uint64 _numberMinted) internal pure { if (_editionSize > 0 && _numberMinted > _editionSize) { revert SoldOut(); } } function enforceSalePrice(uint256 _salePriceTwei, uint256 quantity) internal view { unchecked { if (msg.value != quantity * _salePriceTwei * 1e12) { revert WrongPrice(); } } } /// @dev This helper function checks if the msg.sender is allowed to mint function _isAllowedToMint() internal view returns (bool) { // optimize by likelihood: // 1. check allowlist/minter contracts // 2. open mints // 3. owner mints return allowedMinters[msg.sender] || allowedMinters[address(0x0)] || owner == msg.sender; } /// @dev Validates the supply and time limits for minting with a single SLOAD and SSTORE function _mintPreFlightChecks(uint256 quantity) internal returns (uint64 _tokenId) { if (!_isAllowedToMint()) { revert Unauthorized(); } uint256 _state; uint256 _postState; uint64 _editionSize; uint64 _endOfMintPeriod; uint32 _salePriceTwei; assembly ("memory-safe") { _state := sload(state.slot) _editionSize := shr(192, _state) _endOfMintPeriod := shr(128, _state) _salePriceTwei := shr(80, _state) // can not realistically overflow // the fields in EditionState are ordered so that incrementing state increments numberMinted _postState := add(_state, quantity) // perform the addition only once and extract numberMinted + 1 from _postState _tokenId := and(_postState, 0xffffffffffffffff) } enforceSupplyLimit(_editionSize, _tokenId); enforceTimeLimit(_endOfMintPeriod); enforceSalePrice(_salePriceTwei, quantity); // update storage assembly ("memory-safe") { sstore(state.slot, _postState) } return _tokenId; } /*////////////////////////////////////////////////////////////// METADATA FUNCTIONS //////////////////////////////////////////////////////////////*/ function editionSize() external view override returns (uint256) { return state.editionSize; } /// @dev Returns the sale price in wei function salePrice() public view override returns (uint256) { unchecked { // can not overflow return uint256(state.salePriceTwei) * 1e12; } } /// Returns the timestamp when the minting period ends, or 0 if there is no time limit function endOfMintPeriod() public view override returns (uint256) { return state.endOfMintPeriod; } /// Returns whether the edition can still be minted/purchased function isMintingEnded() public view override returns (bool) { uint256 _endOfMintPeriod = state.endOfMintPeriod; return _endOfMintPeriod > 0 && uint64(block.timestamp) > _endOfMintPeriod; } function totalSupply() public view override returns (uint256) { return state.numberMinted; } /// @notice Get the base64-encoded json metadata for a token /// @param tokenId the token id to get the metadata for /// @return base64-encoded json metadata object function tokenURI(uint256 tokenId) public view override returns (string memory) { require(_ownerOf[tokenId] != address(0), "No token"); return createTokenMetadata(name, tokenId, state.editionSize); } /// @notice Get the base64-encoded json metadata object for the edition function contractURI() public view override returns (string memory) { return createContractMetadata(name, state.royaltyBPS, owner); } /// @notice Get royalty information for token /// @param _salePrice Sale price for the token function royaltyInfo(uint256, uint256 _salePrice) external view override returns (address receiver, uint256 royaltyAmount) { if (owner == address(0x0)) { return (address(0x0), 0); } return (owner, (_salePrice * state.royaltyBPS) / 10_000); } function supportsInterface(bytes4 interfaceId) public view override (ERC721, IERC165Upgradeable) returns (bool) { return type(IERC2981Upgradeable).interfaceId == interfaceId || ERC721.supportsInterface(interfaceId); } }
// SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.8.6; import {Base64} from "./utils/Base64.sol"; import {LibString} from "./utils/LibString.sol"; import {EditionMetadataState} from "./EditionMetadataState.sol"; /// logic for rendering metadata associated with editions contract EditionMetadataRenderer is EditionMetadataState { /// Generate edition metadata from storage information as base64-json blob /// Combines the media data and metadata /// @param name Name of NFT in metadata /// @param tokenId Token ID for specific token /// @param editionSize Size of entire edition to show function createTokenMetadata( string memory name, uint256 tokenId, uint256 editionSize ) internal view returns (string memory) { return toBase64DataUrl( createTokenMetadataJson(name, tokenId, editionSize) ); } /// Function to create the metadata json string for the nft edition /// @param name Name of NFT in metadata /// @param tokenId Token ID for specific token /// @param editionSize Size of entire edition to show function createTokenMetadataJson( string memory name, uint256 tokenId, uint256 editionSize ) internal view returns (string memory) { string memory editionSizeText; if (editionSize > 0) { editionSizeText = string.concat( "/", LibString.toString(editionSize) ); } string memory externalURLText = ""; if (bytes(externalUrl).length > 0) { externalURLText = string.concat('","external_url":"', externalUrl); } string memory mediaData = tokenMediaData(imageUrl, animationUrl); return string.concat( '{"name":"', LibString.escapeJSON(name), " #", LibString.toString(tokenId), editionSizeText, '","', 'description":"', LibString.escapeJSON(description), externalURLText, '"', mediaData, getPropertiesJson(), "}" ); } /// Encodes contract level metadata into base64-data url format /// @dev see https://docs.opensea.io/docs/contract-level-metadata /// @dev borrowed from https://github.com/ourzora/zora-drops-contracts/blob/main/src/utils/NFTMetadataRenderer.sol function createContractMetadata( string memory name, uint256 royaltyBPS, address royaltyRecipient ) internal view returns (string memory) { string memory imageSpace = ""; if (bytes(imageUrl).length > 0) { imageSpace = string.concat('","image":"', imageUrl); } string memory externalURLSpace = ""; if (bytes(externalUrl).length > 0) { externalURLSpace = string.concat( '","external_link":"', externalUrl ); } return toBase64DataUrl( string.concat( '{"name":"', LibString.escapeJSON(name), '","description":"', LibString.escapeJSON(description), // this is for opensea since they don't respect ERC2981 right now '","seller_fee_basis_points":', LibString.toString(royaltyBPS), ',"fee_recipient":"', LibString.toHexString(royaltyRecipient), imageSpace, externalURLSpace, '"}' ) ); } /// Encodes the argument json bytes into base64-data uri format /// @param json Raw json to base64 and turn into a data-uri function toBase64DataUrl(string memory json) internal pure returns (string memory) { return string.concat( "data:application/json;base64,", Base64.encode(bytes(json)) ); } function tokenMediaData(string memory imageUrl, string memory animationUrl) internal pure returns (string memory) { bool hasImage = bytes(imageUrl).length > 0; bool hasAnimation = bytes(animationUrl).length > 0; string memory buffer = ""; if (hasImage) { buffer = string.concat(',"image":"', imageUrl, '"'); } if (hasAnimation) { buffer = string.concat( buffer, ',"animation_url":"', animationUrl, '"' ); } return buffer; } /// Produces Enjin Metadata style simple properties /// @dev https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1155.md#erc-1155-metadata-uri-json-schema function getPropertiesJson() internal view returns (string memory) { uint256 length = namesOfStringProperties.length; if (length == 0) { return ',"properties":{}'; } string memory buffer = ',"properties":{'; unchecked { // `length - 1` can not underflow because of the `length == 0` check above uint256 lengthMinusOne = length - 1; for (uint256 i = 0; i < lengthMinusOne; ) { string storage _name = namesOfStringProperties[i]; string storage _value = stringProperties[_name]; buffer = string.concat( buffer, stringifyStringAttribute(_name, _value), "," ); // counter increment can not overflow ++i; } // add the last attribute without a trailing comma string storage lastName = namesOfStringProperties[lengthMinusOne]; buffer = string.concat( buffer, stringifyStringAttribute(lastName, stringProperties[lastName]) ); } buffer = string.concat(buffer, "}"); return buffer; } function stringifyStringAttribute(string storage name, string storage value) internal pure returns (string memory) { // let's only escape the value, property names should not be using any special characters return string.concat('"', name, '":"', LibString.escapeJSON(value), '"'); } }
// SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.8.6; contract EditionMetadataState { string public description; // Media Urls // animation_url field in the metadata string public animationUrl; // Image in the metadata string public imageUrl; // URL that will appear below the asset's image on OpenSea string public externalUrl; string[] internal namesOfStringProperties; mapping(string => string) internal stringProperties; }
// SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.8.6; error BadAttribute(string name, string value); error IntegerOverflow(uint256 value); error InvalidArgument(); error LengthMismatch(); error NotForSale(); error OperatorNotAllowed(address operator); error PriceTooLow(); error SoldOut(); error TimeLimitReached(); error Unauthorized(); error WrongPrice();
// SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.8.6; interface IEdition { event PriceChanged(uint256 amount); event ExternalUrlUpdated(string oldExternalUrl, string newExternalUrl); event PropertyUpdated(string name, string oldValue, string newValue); function contractURI() external view returns (string memory); function editionSize() external view returns (uint256); function initialize( address _owner, string memory _name, string memory _symbol, string memory _description, string memory _animationUrl, string memory _imageUrl, uint256 _editionSize, uint256 _royaltyBPS, uint256 _mintPeriodSeconds ) external; function enableDefaultOperatorFilter() external; function endOfMintPeriod() external view returns (uint256); function isMintingEnded() external view returns (bool); function mint(address to) external payable returns (uint256); function safeMint(address to) external payable returns (uint256); function mintBatch(address[] memory recipients) external payable returns (uint256); function salePrice() external view returns (uint256); function setApprovedMinter(address minter, bool allowed) external; function setExternalUrl(string calldata _externalUrl) external; function setOperatorFilter(address operatorFilter) external; function setStringProperties(string[] calldata names, string[] calldata values) external; function setSalePrice(uint256 _salePrice) external; function totalSupply() external view returns (uint256); function withdraw() external; }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity >=0.8.0; import {Initializable} from "../utils/Initializable.sol"; /// @notice Simple single owner authorization mixin. /// @author karmacoma (replaced constructor with initializer) /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/auth/Owned.sol) abstract contract OwnedInitializable is Initializable { /*////////////////////////////////////////////////////////////// EVENTS //////////////////////////////////////////////////////////////*/ event OwnershipTransferred(address indexed user, address indexed newOwner); /*////////////////////////////////////////////////////////////// OWNERSHIP STORAGE //////////////////////////////////////////////////////////////*/ address public owner; modifier onlyOwner() virtual { require(msg.sender == owner, "UNAUTHORIZED"); _; } /*////////////////////////////////////////////////////////////// INITIALIZER //////////////////////////////////////////////////////////////*/ function __Owned_init(address _owner) internal onlyInitializing { owner = _owner; emit OwnershipTransferred(address(0), _owner); } /*////////////////////////////////////////////////////////////// OWNERSHIP LOGIC //////////////////////////////////////////////////////////////*/ function transferOwnership(address newOwner) public virtual onlyOwner { owner = newOwner; emit OwnershipTransferred(msg.sender, newOwner); } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity >=0.8.0; import {Initializable} from "../utils/Initializable.sol"; import {ERC721} from "solmate/tokens/ERC721.sol"; import {ERC721TokenReceiver} from "./ERC721TokenReceiver.sol"; /// @notice Initializable version of Solmate's ERC721 abstract contract ERC721I is ERC721, Initializable { /*////////////////////////////////////////////////////////////// CONSTRUCTOR //////////////////////////////////////////////////////////////*/ constructor() ERC721("", "") { _lockInitializers(); } /*////////////////////////////////////////////////////////////// INITIALIZER //////////////////////////////////////////////////////////////*/ function __ERC721_init(string memory _name, string memory _symbol) internal onlyInitializing { name = _name; symbol = _symbol; } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity >=0.8.0; /// @notice A generic interface for a contract which properly accepts ERC721 tokens. /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC721.sol) abstract contract ERC721TokenReceiver { function onERC721Received( address, address, uint256, bytes calldata ) external virtual returns (bytes4) { return ERC721TokenReceiver.onERC721Received.selector; } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity >=0.8.0; /// @notice Modern, minimalist, and gas efficient Initializable implementation with no reinitializers /// @dev warning: this contract is not compatible with OpenZeppelin's Initializable /// @dev warning: this should be considered very experimental /// @author karmacoma abstract contract Initializable { /*////////////////////////////////////////////////////////////// EVENTS //////////////////////////////////////////////////////////////*/ event Initialized(); /*////////////////////////////////////////////////////////////// STORAGE //////////////////////////////////////////////////////////////*/ enum InitState { NOT_INITIALIZED, INITIALIZING, INITIALIZED } InitState private _initState; /*////////////////////////////////////////////////////////////// INITIALIZABLE LOGIC //////////////////////////////////////////////////////////////*/ modifier initializer() { bool isTopLevelCall = _initState == InitState.NOT_INITIALIZED; require( (isTopLevelCall) || (_initState == InitState.INITIALIZING), "ALREADY_INITIALIZED" ); if (isTopLevelCall) { _initState = InitState.INITIALIZING; } _; if (isTopLevelCall) { _initState = InitState.INITIALIZED; emit Initialized(); } } modifier onlyInitializing() { require(_initState == InitState.INITIALIZING, "NOT_INITIALIZING"); _; } /// locks the contract, preventing any further initialization function _lockInitializers() internal virtual { require( _initState == InitState.NOT_INITIALIZED, "MUST_BE_NOT_INITIALIZED" ); _initState = InitState.INITIALIZED; emit Initialized(); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.4; /// @notice Library to encode strings in Base64. /// @author Modified from Solady (https://github.com/vectorized/solady/blob/main/src/utils/Base64.sol) @ 41d29ed /// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/Base64.sol) /// @author Modified from (https://github.com/Brechtpd/base64/blob/main/base64.sol) by Brecht Devos - <[email protected]>. library Base64 { /// @dev Encodes `data` using the base64 encoding described in RFC 4648. /// See: https://datatracker.ietf.org/doc/html/rfc4648 /// @param fileSafe Whether to replace '+' with '-' and '/' with '_'. /// @param noPadding Whether to strip away the padding. function encode( bytes memory data, bool fileSafe, bool noPadding ) internal pure returns (string memory result) { assembly ("memory-safe") { let dataLength := mload(data) if dataLength { // Multiply by 4/3 rounded up. // The `shl(2, ...)` is equivalent to multiplying by 4. let encodedLength := shl(2, div(add(dataLength, 2), 3)) // Set `result` to point to the start of the free memory. result := mload(0x40) // Store the table into the scratch space. // Offsetted by -1 byte so that the `mload` will load the character. // We will rewrite the free memory pointer at `0x40` later with // the allocated size. // The magic constant 0x0230 will translate "-_" + "+/". mstore(0x1f, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdef") mstore( 0x3f, sub( "ghijklmnopqrstuvwxyz0123456789-_", mul(iszero(fileSafe), 0x0230) ) ) // Skip the first slot, which stores the length. let ptr := add(result, 0x20) let end := add(ptr, encodedLength) // Run over the input, 3 bytes at a time. // prettier-ignore for {} 1 {} { data := add(data, 3) // Advance 3 bytes. let input := mload(data) // Write 4 bytes. Optimized for fewer stack operations. mstore8( ptr , mload(and(shr(18, input), 0x3F))) mstore8(add(ptr, 1), mload(and(shr(12, input), 0x3F))) mstore8(add(ptr, 2), mload(and(shr( 6, input), 0x3F))) mstore8(add(ptr, 3), mload(and( input , 0x3F))) ptr := add(ptr, 4) // Advance 4 bytes. // prettier-ignore if iszero(lt(ptr, end)) { break } } let r := mod(dataLength, 3) switch noPadding case 0 { // Offset `ptr` and pad with '='. We can simply write over the end. mstore8(sub(ptr, iszero(iszero(r))), 0x3d) // Pad at `ptr - 1` if `r > 0`. mstore8(sub(ptr, shl(1, eq(r, 1))), 0x3d) // Pad at `ptr - 2` if `r == 1`. // Write the length of the string. mstore(result, encodedLength) } default { // Write the length of the string. mstore( result, sub(encodedLength, add(iszero(iszero(r)), eq(r, 1))) ) } // Allocate the memory for the string. // Add 31 and mask with `not(31)` to round the // free memory pointer up the next multiple of 32. mstore(0x40, and(add(end, 31), not(31))) } } } /// @dev Encodes `data` using the base64 encoding described in RFC 4648. /// Equivalent to `encode(data, false, false)`. function encode(bytes memory data) internal pure returns (string memory result) { result = encode(data, false, false); } /// @dev Encodes `data` using the base64 encoding described in RFC 4648. /// Equivalent to `encode(data, fileSafe, false)`. function encode(bytes memory data, bool fileSafe) internal pure returns (string memory result) { result = encode(data, fileSafe, false); } /// @dev Encodes base64 encoded `data`. /// /// Supports: /// - RFC 4648 (both standard and file-safe mode). /// - RFC 3501 (63: ','). /// /// Does not support: /// - Line breaks. /// /// Note: For performance reasons, /// this function will NOT revert on invalid `data` inputs. /// Outputs for invalid inputs will simply be undefined behaviour. /// It is the user's responsibility to ensure that the `data` /// is a valid base64 encoded string. function decode(string memory data) internal pure returns (bytes memory result) { assembly { let dataLength := mload(data) if dataLength { let end := add(data, dataLength) let decodedLength := mul(shr(2, dataLength), 3) switch and(dataLength, 3) case 0 { // If padded. decodedLength := sub( decodedLength, add( eq(and(mload(end), 0xFF), 0x3d), eq(and(mload(end), 0xFFFF), 0x3d3d) ) ) } default { // If non-padded. decodedLength := add( decodedLength, sub(and(dataLength, 3), 1) ) } result := mload(0x40) // Write the length of the string. mstore(result, decodedLength) // Skip the first slot, which stores the length. let ptr := add(result, 0x20) // Load the table into the scratch space. // Constants are optimized for smaller bytecode with zero gas overhead. // `m` also doubles as the mask of the upper 6 bits. let m := 0xfc000000fc00686c7074787c8084888c9094989ca0a4a8acb0b4b8bcc0c4c8cc mstore(0x5b, m) mstore( 0x3b, 0x04080c1014181c2024282c3034383c4044484c5054585c6064 ) mstore(0x1a, 0xf8fcf800fcd0d4d8dce0e4e8ecf0f4) // prettier-ignore for {} 1 {} { // Read 4 bytes. data := add(data, 4) let input := mload(data) // Write 3 bytes. mstore(ptr, or( and(m, mload(byte(28, input))), shr(6, or( and(m, mload(byte(29, input))), shr(6, or( and(m, mload(byte(30, input))), shr(6, mload(byte(31, input))) )) )) )) ptr := add(ptr, 3) // prettier-ignore if iszero(lt(data, end)) { break } } // Allocate the memory for the string. // Add 32 + 31 and mask with `not(31)` to round the // free memory pointer up the next multiple of 32. mstore(0x40, and(add(add(result, decodedLength), 63), not(31))) // Restore the zero slot. mstore(0x60, 0) } } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.4; /// @notice Library for converting numbers into strings and other string operations. /// @author Modified from Solady (https://github.com/vectorized/solady/blob/main/src/utils/LibString.sol) @ 016c4ac /// @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 = uint256(int256(-1)); /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* DECIMAL OPERATIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Returns the base 10 decimal representation of `value`. function toString(uint256 value) internal pure returns (string memory str) { assembly ("memory-safe") { // 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) } } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* 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) { assembly { let start := mload(0x40) // 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. let m := add(start, and(add(shl(1, length), 0x62), not(0x1f))) // Allocate the memory. 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 to calculate the length later. let end := str // Store "0123456789abcdef" in scratch space. mstore(0x0f, 0x30313233343536373839616263646566) 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. // prettier-ignore for {} 1 {} { str := sub(str, 2) mstore8(add(str, 1), mload(and(temp, 15))) mstore8(str, mload(and(shr(4, temp), 15))) temp := shr(8, temp) length := sub(length, 1) // prettier-ignore if iszero(length) { 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 := add(sub(end, str), 2) // Move the pointer and write the "0x" prefix. str := sub(str, 0x20) mstore(str, 0x3078) // Move the pointer and write the length. str := sub(str, 2) 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) { assembly { let start := mload(0x40) // 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. let m := add(start, 0xa0) // Allocate the memory. 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 to calculate the length later. let end := str // Store "0123456789abcdef" in scratch space. mstore(0x0f, 0x30313233343536373839616263646566) // 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, 2) mstore8(add(str, 1), mload(and(temp, 15))) mstore8(str, mload(and(shr(4, temp), 15))) temp := shr(8, temp) // prettier-ignore if iszero(temp) { break } } // Compute the string's length. let strLength := add(sub(end, str), 2) // Move the pointer and write the "0x" prefix. str := sub(str, 0x20) mstore(str, 0x3078) // Move the pointer and write the length. str := sub(str, 2) mstore(str, strLength) } } /// @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) { assembly ("memory-safe") { let start := mload(0x40) // We need 0x20 bytes for the length, 0x02 bytes for the prefix, // and 0x28 bytes for the digits. // The next multiple of 0x20 above (0x20 + 0x02 + 0x28) is 0x60. str := add(start, 0x60) // Allocate the memory. mstore(0x40, str) // Store "0123456789abcdef" in scratch space. mstore(0x0f, 0x30313233343536373839616263646566) let length := 20 // 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, 2) mstore8(add(str, 1), mload(and(temp, 15))) mstore8(str, mload(and(shr(4, temp), 15))) temp := shr(8, temp) length := sub(length, 1) // prettier-ignore if iszero(length) { break } } // Move the pointer and write the "0x" prefix. str := sub(str, 32) mstore(str, 0x3078) // Move the pointer and write the length. str := sub(str, 2) mstore(str, 42) } } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* OTHER 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 occurances of `search` replaced with `replacement`. function replace( string memory subject, string memory search, string memory replacement ) internal pure returns (string memory result) { 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) // prettier-ignore 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) // prettier-ignore if iszero(lt(subject, subjectSearchEnd)) { break } continue } } // Copy the `replacement` one word at a time. // prettier-ignore for { let o := 0 } 1 {} { mstore(add(result, o), mload(add(replacement, o))) o := add(o, 0x20) // prettier-ignore if iszero(lt(o, replacementLength)) { break } } result := add(result, replacementLength) subject := add(subject, searchLength) if searchLength { // prettier-ignore if iszero(lt(subject, subjectSearchEnd)) { break } continue } } mstore(result, t) result := add(result, 1) subject := add(subject, 1) // prettier-ignore 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. // prettier-ignore 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) { assembly { // prettier-ignore for { let subjectLength := mload(subject) } 1 {} { if iszero(mload(search)) { // `result = min(from, subjectLength)`. result := xor(from, mul(xor(from, subjectLength), lt(subjectLength, from))) break } let searchLength := mload(search) let subjectStart := add(subject, 0x20) result := not(0) // Initialize to `NOT_FOUND`. subject := add(subjectStart, from) let subjectSearchEnd := add(sub(add(subjectStart, subjectLength), searchLength), 1) let m := shl(3, sub(32, and(searchLength, 31))) let s := mload(add(search, 0x20)) // prettier-ignore if iszero(lt(subject, subjectSearchEnd)) { break } if iszero(lt(searchLength, 32)) { // prettier-ignore 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) // prettier-ignore if iszero(lt(subject, subjectSearchEnd)) { break } } break } // prettier-ignore for {} 1 {} { if iszero(shr(m, xor(mload(subject), s))) { result := sub(subject, subjectStart) break } subject := add(subject, 1) // prettier-ignore if iszero(lt(subject, subjectSearchEnd)) { 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) { assembly { // prettier-ignore for {} 1 {} { let searchLength := mload(search) let fromMax := sub(mload(subject), searchLength) if iszero(gt(fromMax, from)) { from := fromMax } if iszero(mload(search)) { result := from break } result := not(0) // Initialize to `NOT_FOUND`. let subjectSearchEnd := sub(add(subject, 0x20), 1) subject := add(add(subject, 0x20), from) // prettier-ignore if iszero(gt(subject, subjectSearchEnd)) { break } // As this function is not too often used, // we shall simply use keccak256 for smaller bytecode size. // prettier-ignore for { let h := keccak256(add(search, 0x20), searchLength) } 1 {} { if eq(keccak256(subject, searchLength), h) { result := sub(subject, add(subjectSearchEnd, 1)) break } subject := sub(subject, 1) // prettier-ignore if iszero(gt(subject, subjectSearchEnd)) { break } } break } } } /// @dev Returns the 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) { assembly { let searchLength := mload(search) // Just using keccak256 directly is actually cheaper. 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) { 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. 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) { assembly { let subjectLength := mload(subject) if iszero(or(iszero(times), iszero(subjectLength))) { subject := add(subject, 0x20) result := mload(0x40) let output := add(result, 0x20) // prettier-ignore for {} 1 {} { // Copy the `subject` one word at a time. // prettier-ignore for { let o := 0 } 1 {} { mstore(add(output, o), mload(add(subject, o))) o := add(o, 0x20) // prettier-ignore if iszero(lt(o, subjectLength)) { break } } output := add(output, subjectLength) times := sub(times, 1) // prettier-ignore 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) { 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) // Copy the `subject` one word at a time, backwards. // prettier-ignore for { let o := and(add(resultLength, 31), not(31)) } 1 {} { mstore(add(result, o), mload(add(subject, o))) o := sub(o, 0x20) // prettier-ignore 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), not(31)))) } } } /// @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) { 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) // prettier-ignore 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) // prettier-ignore 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 { // prettier-ignore if iszero(lt(subject, subjectSearchEnd)) { break } continue } } subject := add(subject, 1) // prettier-ignore 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); assembly { if mload(indices) { let indexPtr := add(indices, 0x20) let indicesEnd := add(indexPtr, shl(5, add(mload(indices), 1))) mstore(sub(indicesEnd, 0x20), mload(subject)) mstore(indices, add(mload(indices), 1)) let prevIndex := 0 // prettier-ignore 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. // prettier-ignore for { let o := and(add(elementLength, 31), not(31)) } 1 {} { mstore(add(element, o), mload(add(add(subject, prevIndex), o))) o := sub(o, 0x20) // prettier-ignore 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), not(31)))) // Store the `element` into the array. mstore(indexPtr, element) } prevIndex := add(index, mload(delimiter)) indexPtr := add(indexPtr, 0x20) // prettier-ignore 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) { assembly ("memory-safe") { result := mload(0x40) let aLength := mload(a) // Copy `a` one word at a time, backwards. // prettier-ignore for { let o := and(add(mload(a), 32), not(31)) } 1 {} { mstore(add(result, o), mload(add(a, o))) o := sub(o, 0x20) // prettier-ignore if iszero(o) { break } } let bLength := mload(b) let output := add(result, mload(a)) // Copy `b` one word at a time, backwards. // prettier-ignore for { let o := and(add(bLength, 32), not(31)) } 1 {} { mstore(add(output, o), mload(add(b, o))) o := sub(o, 0x20) // prettier-ignore 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), 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) { assembly ("memory-safe") { // prettier-ignore 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 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) { 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) { 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) { 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) { 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 { // Right pad with zeroes. Just in case the string is produced // by a method that doesn't zero right pad. mstore(add(add(a, 0x20), mload(a)), 0) // Store the return offset. // Assumes that the string does not start from the scratch space. mstore(sub(a, 0x20), 0x20) // End the transaction, returning the string. return(sub(a, 0x20), add(mload(a), 0x40)) } } }
// SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.8.6; import { IOperatorFilterRegistry } from "operator-filter-registry/IOperatorFilterRegistry.sol"; import { OperatorNotAllowed } from "../interfaces/Errors.sol"; /// Less aggro than DefaultOperatorFilterer, it does not automatically register and subscribe to the default filter /// Instead, this is off by default, opt-in, and relies only on view functions (no register call) abstract contract OptionalOperatorFilterer { address public constant CANONICAL_OPENSEA_SUBSCRIPTION = address(0x3cc6CddA760b79bAfa08dF41ECFA224f810dCeB6); address public constant CANONICAL_OPENSEA_REGISTRY = address(0x000000000000AAeB6D7670E522A718067333cd4E); address public activeOperatorFilter = address(0); event OperatorFilterChanged(address indexed operatorFilter); /// Set to CANONICAL_OPENSEA_SUBSCRIPTION to use the default OpenSea operator filter /// Set to address(0) to disable operator filtering function _setOperatorFilter(address operatorFilter) internal { activeOperatorFilter = operatorFilter; emit OperatorFilterChanged(operatorFilter); } /// @dev modified from operator-filter-registry/OperatorFilterer.sol modifier onlyAllowedOperator(address from) virtual { // Allow spending tokens from addresses with balance // Note that this still allows listings and marketplaces with escrow to transfer tokens if transferred // from an EOA. if (from != msg.sender) { _checkFilterOperator(msg.sender); } _; } /// @dev modified from operator-filter-registry/OperatorFilterer.sol function _checkFilterOperator(address operator) internal view { if (activeOperatorFilter == address(0)) { return; } // Check registry code length to facilitate testing in environments without a deployed registry. if (CANONICAL_OPENSEA_REGISTRY.code.length == 0) { return; } if (!IOperatorFilterRegistry(CANONICAL_OPENSEA_REGISTRY).isOperatorAllowed(activeOperatorFilter, operator)) { revert OperatorNotAllowed(operator); } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.13; interface IOperatorFilterRegistry { function isOperatorAllowed(address registrant, address operator) external view returns (bool); function register(address registrant) external; function registerAndSubscribe(address registrant, address subscription) external; function registerAndCopyEntries(address registrant, address registrantToCopy) external; function unregister(address addr) external; function updateOperator(address registrant, address operator, bool filtered) external; function updateOperators(address registrant, address[] calldata operators, bool filtered) external; function updateCodeHash(address registrant, bytes32 codehash, bool filtered) external; function updateCodeHashes(address registrant, bytes32[] calldata codeHashes, bool filtered) external; function subscribe(address registrant, address registrantToSubscribe) external; function unsubscribe(address registrant, bool copyExistingEntries) external; function subscriptionOf(address addr) external returns (address registrant); function subscribers(address registrant) external returns (address[] memory); function subscriberAt(address registrant, uint256 index) external returns (address); function copyEntriesOf(address registrant, address registrantToCopy) external; function isOperatorFiltered(address registrant, address operator) external returns (bool); function isCodeHashOfFiltered(address registrant, address operatorWithCode) external returns (bool); function isCodeHashFiltered(address registrant, bytes32 codeHash) external returns (bool); function filteredOperators(address addr) external returns (address[] memory); function filteredCodeHashes(address addr) external returns (bytes32[] memory); function filteredOperatorAt(address registrant, uint256 index) external returns (address); function filteredCodeHashAt(address registrant, uint256 index) external returns (bytes32); function isRegistered(address addr) external returns (bool); function codeHashOf(address addr) external returns (bytes32); }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity >=0.8.0; /// @notice Modern, minimalist, and gas efficient ERC-721 implementation. /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC721.sol) abstract contract ERC721 { /*////////////////////////////////////////////////////////////// EVENTS //////////////////////////////////////////////////////////////*/ event Transfer(address indexed from, address indexed to, uint256 indexed id); event Approval(address indexed owner, address indexed spender, uint256 indexed id); event ApprovalForAll(address indexed owner, address indexed operator, bool approved); /*////////////////////////////////////////////////////////////// METADATA STORAGE/LOGIC //////////////////////////////////////////////////////////////*/ string public name; string public symbol; function tokenURI(uint256 id) public view virtual returns (string memory); /*////////////////////////////////////////////////////////////// ERC721 BALANCE/OWNER STORAGE //////////////////////////////////////////////////////////////*/ mapping(uint256 => address) internal _ownerOf; mapping(address => uint256) internal _balanceOf; function ownerOf(uint256 id) public view virtual returns (address owner) { require((owner = _ownerOf[id]) != address(0), "NOT_MINTED"); } function balanceOf(address owner) public view virtual returns (uint256) { require(owner != address(0), "ZERO_ADDRESS"); return _balanceOf[owner]; } /*////////////////////////////////////////////////////////////// ERC721 APPROVAL STORAGE //////////////////////////////////////////////////////////////*/ mapping(uint256 => address) public getApproved; mapping(address => mapping(address => bool)) public isApprovedForAll; /*////////////////////////////////////////////////////////////// CONSTRUCTOR //////////////////////////////////////////////////////////////*/ constructor(string memory _name, string memory _symbol) { name = _name; symbol = _symbol; } /*////////////////////////////////////////////////////////////// ERC721 LOGIC //////////////////////////////////////////////////////////////*/ function approve(address spender, uint256 id) public virtual { address owner = _ownerOf[id]; require(msg.sender == owner || isApprovedForAll[owner][msg.sender], "NOT_AUTHORIZED"); getApproved[id] = spender; emit Approval(owner, spender, id); } function setApprovalForAll(address operator, bool approved) public virtual { isApprovedForAll[msg.sender][operator] = approved; emit ApprovalForAll(msg.sender, operator, approved); } function transferFrom( address from, address to, uint256 id ) public virtual { require(from == _ownerOf[id], "WRONG_FROM"); require(to != address(0), "INVALID_RECIPIENT"); require( msg.sender == from || isApprovedForAll[from][msg.sender] || msg.sender == getApproved[id], "NOT_AUTHORIZED" ); // Underflow of the sender's balance is impossible because we check for // ownership above and the recipient's balance can't realistically overflow. unchecked { _balanceOf[from]--; _balanceOf[to]++; } _ownerOf[id] = to; delete getApproved[id]; emit Transfer(from, to, id); } function safeTransferFrom( address from, address to, uint256 id ) public virtual { transferFrom(from, to, id); require( to.code.length == 0 || ERC721TokenReceiver(to).onERC721Received(msg.sender, from, id, "") == ERC721TokenReceiver.onERC721Received.selector, "UNSAFE_RECIPIENT" ); } function safeTransferFrom( address from, address to, uint256 id, bytes calldata data ) public virtual { transferFrom(from, to, id); require( to.code.length == 0 || ERC721TokenReceiver(to).onERC721Received(msg.sender, from, id, data) == ERC721TokenReceiver.onERC721Received.selector, "UNSAFE_RECIPIENT" ); } /*////////////////////////////////////////////////////////////// ERC165 LOGIC //////////////////////////////////////////////////////////////*/ function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) { return interfaceId == 0x01ffc9a7 || // ERC165 Interface ID for ERC165 interfaceId == 0x80ac58cd || // ERC165 Interface ID for ERC721 interfaceId == 0x5b5e139f; // ERC165 Interface ID for ERC721Metadata } /*////////////////////////////////////////////////////////////// INTERNAL MINT/BURN LOGIC //////////////////////////////////////////////////////////////*/ function _mint(address to, uint256 id) internal virtual { require(to != address(0), "INVALID_RECIPIENT"); require(_ownerOf[id] == address(0), "ALREADY_MINTED"); // Counter overflow is incredibly unrealistic. unchecked { _balanceOf[to]++; } _ownerOf[id] = to; emit Transfer(address(0), to, id); } function _burn(uint256 id) internal virtual { address owner = _ownerOf[id]; require(owner != address(0), "NOT_MINTED"); // Ownership check above ensures no underflow. unchecked { _balanceOf[owner]--; } delete _ownerOf[id]; delete getApproved[id]; emit Transfer(owner, address(0), id); } /*////////////////////////////////////////////////////////////// INTERNAL SAFE MINT LOGIC //////////////////////////////////////////////////////////////*/ function _safeMint(address to, uint256 id) internal virtual { _mint(to, id); require( to.code.length == 0 || ERC721TokenReceiver(to).onERC721Received(msg.sender, address(0), id, "") == ERC721TokenReceiver.onERC721Received.selector, "UNSAFE_RECIPIENT" ); } function _safeMint( address to, uint256 id, bytes memory data ) internal virtual { _mint(to, id); require( to.code.length == 0 || ERC721TokenReceiver(to).onERC721Received(msg.sender, address(0), id, data) == ERC721TokenReceiver.onERC721Received.selector, "UNSAFE_RECIPIENT" ); } } /// @notice A generic interface for a contract which properly accepts ERC721 tokens. /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC721.sol) abstract contract ERC721TokenReceiver { function onERC721Received( address, address, uint256, bytes calldata ) external virtual returns (bytes4) { return ERC721TokenReceiver.onERC721Received.selector; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.6.0) (interfaces/IERC2981.sol) pragma solidity ^0.8.0; import "../utils/introspection/IERC165Upgradeable.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 IERC2981Upgradeable is IERC165Upgradeable { /** * @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 (last updated v4.7.0) (utils/Address.sol) pragma solidity ^0.8.1; /** * @dev Collection of functions related to the address type */ library AddressUpgradeable { /** * @dev Returns true if `account` is a contract. * * [IMPORTANT] * ==== * It is unsafe to assume that an address for which this function returns * false is an externally-owned account (EOA) and not a contract. * * Among others, `isContract` will return false for the following * types of addresses: * * - an externally-owned account * - a contract in construction * - an address where a contract will be created * - an address where a contract lived, but was destroyed * ==== * * [IMPORTANT] * ==== * You shouldn't rely on `isContract` to protect against flash loan attacks! * * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract * constructor. * ==== */ function isContract(address account) internal view returns (bool) { // This method relies on extcodesize/address.code.length, which returns 0 // for contracts in construction, since the code is only stored at the end // of the constructor execution. return account.code.length > 0; } /** * @dev Replacement for Solidity's `transfer`: sends `amount` wei to * `recipient`, forwarding all available gas and reverting on errors. * * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost * of certain opcodes, possibly making contracts go over the 2300 gas limit * imposed by `transfer`, making them unable to receive funds via * `transfer`. {sendValue} removes this limitation. * * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. * * IMPORTANT: because control is transferred to `recipient`, care must be * taken to not create reentrancy vulnerabilities. Consider using * {ReentrancyGuard} or the * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ function sendValue(address payable recipient, uint256 amount) internal { require(address(this).balance >= amount, "Address: insufficient balance"); (bool success, ) = recipient.call{value: amount}(""); require(success, "Address: unable to send value, recipient may have reverted"); } /** * @dev Performs a Solidity function call using a low level `call`. A * plain `call` is an unsafe replacement for a function call: use this * function instead. * * If `target` reverts with a revert reason, it is bubbled up by this * function (like regular Solidity function calls). * * Returns the raw returned data. To convert to the expected return value, * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. * * Requirements: * * - `target` must be a contract. * - calling `target` with `data` must not revert. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, "Address: low-level call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with * `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but also transferring `value` wei to `target`. * * Requirements: * * - the calling contract must have an ETH balance of at least `value`. * - the called Solidity function must be `payable`. * * _Available since v3.1._ */ function functionCallWithValue( address target, bytes memory data, uint256 value ) internal returns (bytes memory) { return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); } /** * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but * with `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCallWithValue( address target, bytes memory data, uint256 value, string memory errorMessage ) internal returns (bytes memory) { require(address(this).balance >= value, "Address: insufficient balance for call"); (bool success, bytes memory returndata) = target.call{value: value}(data); return verifyCallResultFromTarget(target, success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { return functionStaticCall(target, data, "Address: low-level static call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall( address target, bytes memory data, string memory errorMessage ) internal view returns (bytes memory) { (bool success, bytes memory returndata) = target.staticcall(data); return verifyCallResultFromTarget(target, success, returndata, errorMessage); } /** * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract. * * _Available since v4.8._ */ function verifyCallResultFromTarget( address target, bool success, bytes memory returndata, string memory errorMessage ) internal view returns (bytes memory) { if (success) { if (returndata.length == 0) { // only check isContract if the call was successful and the return data is empty // otherwise we already know that it was a contract require(isContract(target), "Address: call to non-contract"); } return returndata; } else { _revert(returndata, errorMessage); } } /** * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the * revert reason or using the provided one. * * _Available since v4.3._ */ function verifyCallResult( bool success, bytes memory returndata, string memory errorMessage ) internal pure returns (bytes memory) { if (success) { return returndata; } else { _revert(returndata, errorMessage); } } function _revert(bytes memory returndata, string memory errorMessage) private pure { // Look for revert reason and bubble it up if present if (returndata.length > 0) { // The easiest way to bubble the revert reason is using memory via assembly /// @solidity memory-safe-assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC165 standard, as defined in the * https://eips.ethereum.org/EIPS/eip-165[EIP]. * * Implementers can declare support of contract interfaces, which can then be * queried by others ({ERC165Checker}). * * For an implementation, see {ERC165}. */ interface IERC165Upgradeable { /** * @dev Returns true if this contract implements the interface defined by * `interfaceId`. See the corresponding * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section] * to learn more about how these ids are created. * * This function call must use less than 30 000 gas. */ function supportsInterface(bytes4 interfaceId) external view returns (bool); }
{ "remappings": [ "@ensdomains/=lib/nft-editions/node_modules/@ensdomains/", "@openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/", "@openzeppelin/=lib/openzeppelin-contracts/", "@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/", "base64/=lib/base64/", "ds-test/=lib/ds-test/src/", "erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/", "eth-gas-reporter/=lib/nft-editions/node_modules/eth-gas-reporter/", "forge-std/=lib/forge-std/src/", "gsn/=lib/gsn/", "hardhat-deploy/=lib/nft-editions/node_modules/hardhat-deploy/", "hardhat/=lib/nft-editions/node_modules/hardhat/", "lib/=lib/", "nft-editions/=lib/nft-editions/contracts/", "openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/", "openzeppelin-contracts/=lib/openzeppelin-contracts/contracts/", "operator-filter-registry/=lib/nft-editions/lib/operator-filter-registry/src/", "solmate/=lib/nft-editions/lib/solmate/src/", "src/=src/", "test/=test/" ], "optimizer": { "enabled": true, "runs": 1000000 }, "metadata": { "bytecodeHash": "ipfs" }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "evmVersion": "london", "viaIR": true, "libraries": {} }
[{"inputs":[{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"value","type":"string"}],"name":"BadAttribute","type":"error"},{"inputs":[{"internalType":"uint256","name":"value","type":"uint256"}],"name":"IntegerOverflow","type":"error"},{"inputs":[],"name":"InvalidArgument","type":"error"},{"inputs":[],"name":"LengthMismatch","type":"error"},{"inputs":[{"internalType":"address","name":"operator","type":"address"}],"name":"OperatorNotAllowed","type":"error"},{"inputs":[],"name":"PriceTooLow","type":"error"},{"inputs":[],"name":"SoldOut","type":"error"},{"inputs":[],"name":"TimeLimitReached","type":"error"},{"inputs":[],"name":"Unauthorized","type":"error"},{"inputs":[],"name":"WrongPrice","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"approved","type":"bool"}],"name":"ApprovalForAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"oldExternalUrl","type":"string"},{"indexed":false,"internalType":"string","name":"newExternalUrl","type":"string"}],"name":"ExternalUrlUpdated","type":"event"},{"anonymous":false,"inputs":[],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"operatorFilter","type":"address"}],"name":"OperatorFilterChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"PriceChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"name","type":"string"},{"indexed":false,"internalType":"string","name":"oldValue","type":"string"},{"indexed":false,"internalType":"string","name":"newValue","type":"string"}],"name":"PropertyUpdated","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":"id","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[],"name":"CANONICAL_OPENSEA_REGISTRY","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"CANONICAL_OPENSEA_SUBSCRIPTION","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"activeOperatorFilter","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"animationUrl","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"}],"name":"approve","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"contractURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"description","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"editionSize","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"enableDefaultOperatorFilter","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"endOfMintPeriod","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"externalUrl","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"imageUrl","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"},{"internalType":"string","name":"_name","type":"string"},{"internalType":"string","name":"_symbol","type":"string"},{"internalType":"string","name":"_description","type":"string"},{"internalType":"string","name":"_animationUrl","type":"string"},{"internalType":"string","name":"_imageUrl","type":"string"},{"internalType":"uint256","name":"_editionSize","type":"uint256"},{"internalType":"uint256","name":"_royaltyBPS","type":"uint256"},{"internalType":"uint256","name":"_mintPeriodSeconds","type":"uint256"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isMintingEnded","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"}],"name":"mint","outputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address[]","name":"recipients","type":"address[]"}],"name":"mintBatch","outputs":[{"internalType":"uint256","name":"lastTokenId","type":"uint256"}],"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":"id","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"owner","type":"address"}],"stateMutability":"view","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":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"}],"name":"safeMint","outputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"stateMutability":"payable","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":"nonpayable","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":"nonpayable","type":"function"},{"inputs":[],"name":"salePrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bool","name":"approved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"minter","type":"address"},{"internalType":"bool","name":"allowed","type":"bool"}],"name":"setApprovedMinter","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"_externalUrl","type":"string"}],"name":"setExternalUrl","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operatorFilter","type":"address"}],"name":"setOperatorFilter","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_salePriceWei","type":"uint256"}],"name":"setSalePrice","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string[]","name":"names","type":"string[]"},{"internalType":"string[]","name":"values","type":"string[]"}],"name":"setStringProperties","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":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"}]
Loading...
Loading
[ Download: CSV Export ]
[ 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.