ERC-721
Overview
Max Total Supply
122 AVATR
Holders
15
Market
Volume (24H)
N/A
Min Price (24H)
N/A
Max Price (24H)
N/A
Other Info
Token Contract
Balance
1 AVATRLoading...
Loading
Loading...
Loading
Loading...
Loading
# | Exchange | Pair | Price | 24H Volume | % Volume |
---|
Contract Name:
OpenAvatarGen0Token
Compiler Version
v0.8.20+commit.a1b79de6
Optimization Enabled:
Yes with 1000000 runs
Other Settings:
shanghai EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity ^0.8.20; import {Ownable} from '@openzeppelin/contracts/access/Ownable.sol'; import {IERC165} from '@openzeppelin/contracts/utils/introspection/IERC165.sol'; import {Strings} from '@openzeppelin/contracts/utils/Strings.sol'; import {ERC721A, ERC721AQueryable, IERC721A} from 'erc721a/contracts/extensions/ERC721AQueryable.sol'; import {Base64} from './core/dependencies/Base64.sol'; import {AOpenAvatarMintStateMachine} from './core/erc721/AOpenAvatarMintStateMachine.sol'; import {Treasury} from './core/erc721/Treasury.sol'; import {ENSReverseClaimer} from './core/lib/ENSReverseClaimer.sol'; import {OpenAvatarGenerationZero} from './OpenAvatarGenerationZero.sol'; import {IOpenAvatarGen0RendererRegistry} from './IOpenAvatarGen0RendererRegistry.sol'; import {IOpenAvatarGen0Renderer} from './IOpenAvatarGen0Renderer.sol'; import {IOpenAvatarGen0Token, OpenAvatarGen0TokenMetadata} from './IOpenAvatarGen0Token.sol'; /** * @title OpenAvatarGen0Token * @author Cory Gabrielsen (cory.eth) * * @notice OpenAvatar is an onchain protocol for Avatars. * @dev This contract is the main entry point for the OpenAvatar protocol. * * ---------------------------------------------------------------------------- * Generation 0 * ---------------------------------------------------------------------------- * This is OpenAvatar Generation 0. * * * ---------------------------------------------------------------------------- * ERC-721 * ---------------------------------------------------------------------------- * OpenAvatarGen0Token is an ERC-721 token that represents an OpenAvatar. * * * ---------------------------------------------------------------------------- * DNA * ---------------------------------------------------------------------------- * *DNA* is a core building block of OpenAvatar. * * Every OpenAvatar is defined by a unique 32-byte DNA (gen 0). No two * OpenAvatars can have the same DNA. * * DNA is an extensible building block of OpenAvatar. Application-specific * re-interpretations of OpenAvatar DNA are entirely possible and encouraged. * * DNA determines how an OpenAvatar is rendered (gen 0). When users select how * their Avatar looks, they are by proxy choosing the Avatar's 32-byte DNA. * * * ---------------------------------------------------------------------------- * Renderer * ---------------------------------------------------------------------------- * A *Renderer* is a core building block of OpenAvatar. * * A render interface is defined, consisting of the following method: * ``` * interface IOpenAvatarGen0Renderer { * function renderURI(bytes32 dna) external view returns (string memory); * } * ``` * * * ---------------------------------------------------------------------------- * Text Records * ---------------------------------------------------------------------------- * A *Text Record* is a core building block of OpenAvatar. * * Text records are key-value pairs of strings stored onchain by Avatar DNA * in the `OpenAvatarGen0TextRecords` contract. Token owners may store any * key/value pair for their token's DNA. * * This mechanism provides an onchain key/value data store for Avatar DNA. * * Text records may be used by other contracts and applications to read/write * arbitrary data to an OpenAvatar. * * For example, text records are used to determine if the token owner has * set the background color for their Profile Picture Renderer. This allows * token owners to dynamically customize their Avatar onchain, and provides * an example for more complex integrations. * * * ---------------------------------------------------------------------------- * Renderer Registry * ---------------------------------------------------------------------------- * A separate `OpenAvatarGen0RendererRegistry` contract is used to register * Renderers for use by the `OpenAvatarGen0Token` contract. * * The `OpenAvatarGen0Token` contract determines which Renderer to use for a * given token by delegating to the `OpenAvatarGen0RendererRegistry` contract. * * Default Renderer: * The registry maintains a concept of "default" Renderer, which may be * modified by the contract owner. * * Token Owners may optionally override the default Renderer for their token by * writing a valid 'gen0.renderer` text record to the * `OpenAvatarGen0TextRecords` contract. * * When the ERC721::tokenURI method is called, the Renderer (token default if * exists, otherwise registry default) is called to render the URI. * * The net effect is that rendering is invariant and onchain. * * If further Renderers are made available by the registry owner, token owners * may opt-in to those upgrades either in general (by not setting a token * default) or explicit choice (by setting their corresponding text record). * * This "soft-upgradeability" can be sunset by burning the registry fuse which * blocks further additions, thereby making the list of available Renderers * fully immutable. * * At launch, two Renderers are provided, described below. * * * ---------------------------------------------------------------------------- * 'base' Renderer * ---------------------------------------------------------------------------- * A base Renderer is provided with the key "base". * * The 'base' Renderer renders the Avatar with transparent background as a * forward-facing sprite. * * * ---------------------------------------------------------------------------- * 'pfp' Renderer * ---------------------------------------------------------------------------- * A Profile Picture Renderer is provided with the key "pfp". * * The 'pfp' Renderer renders the Avatar with a configurable background color * determined by the 'gen0.renderer.pfp.background-color' text record, which * should be a valid RGB hex color code (e.g. "#ff0000" for red). * * Further, the 'pfp' Renderer provides the option of masking the Avatar * below the neck to create a "floating head" effect. This can be configured * by setting the `gen0.renderer.pfp.mask` text record to "below-the-neck". * * The Profile Picture Renderer is a demonstration of combining the OpenAvatar * building blocks together to create dynamic, owner-customizable behavior. It * utilizes onchain assets, onchain rendering, and onchain text records to * render a customizable Avatar pfp. * * This pattern is permissionless. */ contract OpenAvatarGen0Token is OpenAvatarGenerationZero, IOpenAvatarGen0Renderer, IOpenAvatarGen0Token, AOpenAvatarMintStateMachine, Treasury, ENSReverseClaimer, ERC721AQueryable { using Strings for address; using Strings for uint256; /// @dev Event emitted when the fuse is burned for increasing the soft cap /// supply event FuseBurnedCanIncreaseSupplySoftCap(); /// @dev Event emitted when the fuse is burned for lowering the mint price event FuseBurnedCanLowerMintPrice(); /// @dev Event emitted when the fuse is burned for raising the mint price event FuseBurnedCanRaiseMintPrice(); /// @dev Event emitted when a new avatar is minted. event Mint(address to, bytes32 dna, uint tokenId); /// @dev Event emitted when the mint price is changed event MintPriceChange(uint oldPrice, uint newPrice); /// @dev Error when a component is already initialized. error AlreadyInitialized(); /// @dev Revert error when the DNA does already minted. error DNAAlreadyMinted(bytes32 dna); /// @dev Revert error when the DNA does not exist. error DNADoesNotExist(bytes32 dna); /// @dev Event emitted when an interface is not supported. error InterfaceUnsupported(address contractAddress, bytes4 interfaceId); /// @dev Revert error when the batch size limit is exceeded. error MintBatchSizeLimitExceeded(uint batchSize); /// @dev Revert error when underpaid for a mint. error MintUnderpaid(); /// @dev Revert error when not token owner for a permissioned action. error NotTokenOwner(uint tokenId); /// @dev Revert error when trying to batch mint the null DNA, which is not /// allowed. error NullDNARestrictedFromBatchMint(bytes32 dna); /// @dev Revert error when the supply cap is exceeded. error SupplyCapExceeded(); /// @dev Revert error when the supply cap change would decrease the supply /// cap. error SupplySoftCapChangeIsNotAnIncrease(); /// @dev Revert error when the supply cap increase is not higher than the /// current supply cap. error SupplySoftCapIsMonotonicallyIncraesing(); /// @dev Revert error when the supply cap change exceeds hard cap. error SupplySoftCapWouldExceedHardCap(uint newSupplyCap); /// @dev Revert error when token id does not exist. error TokenIdDoesNotExist(uint tokenId); ///////////////////////////////////////////////////////////////////////////// // Dependencies ///////////////////////////////////////////////////////////////////////////// /// @dev The ERC-165 interface id for the OpenAvatarGen0Renderer /// (dependency). bytes4 private constant INTERFACE_ID_OPENAVATAR_GEN0_RENDERER_REGISTRY = 0x8646df82; /// @dev The IOpenAvatarGen0RendererRegistry dependency. IOpenAvatarGen0RendererRegistry public openAvatarGen0RendererRegistry; ///////////////////////////////////////////////////////////////////////////// // Internal Data Structures ///////////////////////////////////////////////////////////////////////////// // bidirectional mapping from token id <--> dna /// @dev One half of bi-directional mapping from DNA <-> token id. mapping(bytes32 dna => uint tokenId) private dnaToTokenId; /// @dev One half of bi-directional mapping from token id <-> DNA. mapping(uint tokenId => bytes32 dna) private tokenIdToDna; /// @dev track who minted each token mapping(bytes32 dna => address minter) private dnaToCreator; ///////////////////////////////////////////////////////////////////////////// // Mint Supply ///////////////////////////////////////////////////////////////////////////// /// @dev The maxium number of avatars. /// Invariant: totalSupply() <= supplySoftCap() <= supplyHardCap() uint16 public constant MAX_SUPPLY = 16384; /// @dev The maxium number of avatars that can be minted in a single batch. uint8 public constant MAX_MINT_BATCH_SIZE_LIMIT = 20; /// @dev The total number of avatars that can be minted. /// Invariant: totalSupply() <= supplySoftCap() <= supplyHardCap() uint16 public _supplySoftCap = 0; ///////////////////////////////////////////////////////////////////////////// // Mint Price ///////////////////////////////////////////////////////////////////////////// /// @dev The price to mint an avatar. uint public mintPrice; ///////////////////////////////////////////////////////////////////////////// // Fuses ///////////////////////////////////////////////////////////////////////////// /// @dev Flag to indicate if the fuse has been burned for increasing the /// supply cap bool public fuseBurnedCanIncreaseSupplySoftCap = false; /// @dev Flag to indicate if the fuse has been burned for lowering the mint /// price bool public fuseBurnedCanLowerMintPrice = false; /// @dev Flag to indicate if the fuse has been burned for raising the mint /// price bool public fuseBurnedCanRaiseMintPrice = false; ///////////////////////////////////////////////////////////////////////////// // Constructor ///////////////////////////////////////////////////////////////////////////// constructor(address ownerProxy) ERC721A('OpenAvatar', 'AVATR') { // will be deployed by ImmutableCreate2Factory and then transferred to the // configured owner. // using a proxy allows for using same constructor args and thus same // bytecode for all instances of this contract. address wantOwner = Ownable(ownerProxy).owner(); if (owner() != wantOwner) { transferOwnership(wantOwner); } } ///////////////////////////////////////////////////////////////////////////// // ERC-165: Standard Interface Detection ///////////////////////////////////////////////////////////////////////////// /** * @notice Checks if the contract supports an interface. * @param interfaceId The interface identifier, as specified in ERC-165. * @return True if the contract supports interfaceID, false otherwise. */ function supportsInterface( bytes4 interfaceId ) public pure override(OpenAvatarGenerationZero, ERC721A, IERC721A) returns (bool) { return interfaceId == 0x01ffc9a7 || // ERC165 interface ID for ERC165. // ERC721 interfaceId == 0x80ac58cd || // ERC165 interface ID for ERC721. interfaceId == 0x5b5e139f || // ERC165 interface ID for ERC721Metadata. // IOpenAvatarGen0Renderer interfaceId == 0xb93e4881 || // ERC165 interface ID for IOpenAvatarGen0Renderer. // IOpenAvatar interfaceId == 0xfdf02ac8 || // ERC165 interface ID for IOpenAvatarGeneration. interfaceId == 0x7b65147c || // ERC165 interface ID for IOpenAvatarSentinel. interfaceId == 0x86953eb4 || // ERC165 interface ID for IOpenAvatar. // IOpenAvatarGen0Token interfaceId == 0xc5f6fb61 || // ERC165 interface ID for IOpenAvatarGen0TokenMetadata. interfaceId == 0x2717336f || // ERC165 interface ID for IOpenAvatarGen0TokenDNA. interfaceId == 0x9840fe50 || // ERC165 interface ID for IOpenAvatarGen0Token. // IOpenAvatarGen0TokenMint interfaceId == 0x27fc9ea2 || // ERC165 interface ID for IOpenAvatarGen0TokenMintRead. interfaceId == 0x2f683360 || // ERC165 interface ID for IOpenAvatarGen0TokenMintWrite interfaceId == 0xf4a0a528 || // ERC165 interface ID for IOpenAvatarGen0TokenMintAdmin interfaceId == 0xfc3408ea; // ERC165 interface ID for IOpenAvatarGen0TokenMint. } ///////////////////////////////////////////////////////////////////////////// // Initialize Dependencies ///////////////////////////////////////////////////////////////////////////// /** * @dev Initialize the contract. * @param openAvatarGen0RendererRegistry_ The address of the `OpenAvatarGen0RendererRegistry` contract. */ function initialize(address openAvatarGen0RendererRegistry_) external onlyOwner { setOpenAvatarGen0RendererRegistry(openAvatarGen0RendererRegistry_); } /** * @notice Check if the contract has been initialized. * @return True if the contract has been initialized, false otherwise. */ function isInitialized() external view returns (bool) { return address(openAvatarGen0RendererRegistry) != address(0); } /** * @notice Retrieves the address of the `OpenAvatarGen0RendererRegistry` * contract. * * The `OpenAvatarGen0RendererRegistry` contract is a registry that contains * references to all available renderers. A renderer is a contract that * takes avatar DNA and generates an image representation of the avatar. * * These renderers can be selected by token owners to customize the look * of their avatar, as specified in the 'gen0.renderer' field in the * `OpenAvatarGen0TextRecords` contract. * * Furthermore, the `OpenAvatarGen0RendererRegistry` contract can be * controlled by the registry owner, who can add new renderers or * permanently lock the list of registered renderers. * * @return The address of the `OpenAvatarGen0RendererRegistry` contract. */ function getOpenAvatarGen0RendererRegistry() external view returns (address) { return address(openAvatarGen0RendererRegistry); } /** * @notice Set the OpenAvatarGen0RendererRegistry address. * @param openAvatarGen0RendererRegistry_ The address of the * OpenAvatarGen0RendererRegistry contract. * @dev This function can only be called once. */ function setOpenAvatarGen0RendererRegistry(address openAvatarGen0RendererRegistry_) internal { // only set once if (address(openAvatarGen0RendererRegistry) != address(0)) revert AlreadyInitialized(); // check ERC-165 support // only DNA interface is required if (!IERC165(openAvatarGen0RendererRegistry_).supportsInterface(INTERFACE_ID_OPENAVATAR_GEN0_RENDERER_REGISTRY)) { revert InterfaceUnsupported(openAvatarGen0RendererRegistry_, INTERFACE_ID_OPENAVATAR_GEN0_RENDERER_REGISTRY); } // set openAvatarGen0RendererRegistry = IOpenAvatarGen0RendererRegistry(openAvatarGen0RendererRegistry_); } ///////////////////////////////////////////////////////////////////////////// // ERC721Metadata ///////////////////////////////////////////////////////////////////////////// /** * @notice Generates and returns a base64 encoded JSON URI containing * metadata for a specified token ID. * * This URI encapsulates data such as the Generation of the avatar, * the DNA (unique identifier), the address of the OpenAvatarGen0Renderer * used to render the avatar, the ABI signature of the rendering function, * the token ID, and the owner of the token. It also includes an 'image' * field with a URI representing the avatar's image, and an 'attributes' * array containing trait metadata. * * @param tokenId The unique identifier of the token whose metadata to * retrieve. * * @return A base64 encoded JSON URI that contains metadata of the * given token ID. * * @dev This function will revert if the provided token ID has not * been minted yet. * * @dev The provided metadata extends the OpenAvatar URI to include * adherence to the OpenSea metadata standards. */ function tokenURI(uint tokenId) public view override(ERC721A, IERC721A) returns (string memory) { if (!_exists(tokenId)) revert URIQueryForNonexistentToken(); string memory generationZeroStr = OPENAVATAR_GENERATION_ZERO.toString(); bytes32 dna = tokenIdToDna[tokenId]; address mintedBy = dnaToCreator[dna]; string memory mintedByStr = mintedBy.toHexString(); address rendererAddress = openAvatarGen0RendererRegistry.getRendererByDNA(dna); string memory rendererAddressStr = rendererAddress.toHexString(); string memory tokenIdStr = tokenId.toString(); string memory dnaStr = uint(dna).toHexString(32); return string( abi.encodePacked( 'data:application/json;base64,', Base64.encode( abi.encodePacked( '{"generation":', generationZeroStr, ',"dna":"', dnaStr, '","creator":"', dnaToCreator[dna].toHexString(), '","renderer":"', rendererAddressStr, '","renderer_abi":"renderURI(bytes32)","token_id":', tokenIdStr, ',"name":"OpenAvatar #', tokenIdStr, '","description":"OpenAvatar is an onchain protocol for Avatars.","image":"', IOpenAvatarGen0Renderer(rendererAddress).renderURI(dna), '","attributes":[{"trait_type":"Generation","value":', generationZeroStr, ',"display_type":"number"},{"trait_type":"DNA","value":"', dnaStr, '"},{"trait_type":"Creator","value":"', mintedByStr, '"},{"trait_type":"Renderer","value":"', rendererAddressStr, '"},{"trait_type":"Token ID","value":', tokenIdStr, ',"display_type":"number"}]}' ) ) ) ); } ///////////////////////////////////////////////////////////////////////////// // IOpenAvatarGen0Renderer ///////////////////////////////////////////////////////////////////////////// /** * @notice This function provides a render URI that represents the renderered * image for a specific avatar DNA. The `OpenAvatarGen0RendererRegistry` * contract manages a list of available renderers. * * The token owner can specify their preferred renderer from these * available options by setting the 'gen0.renderer' key in the * `OpenAvatarGen0TextRecords` contract to the renderer key. For example, * ``` * openAvatarGen0TextRecords.setText(<dna>, 'gen0.renderer', 'base'); * ``` * or, * ``` * openAvatarGen0TextRecords.setText(<dna>, 'gen0.renderer', 'pfp'); * ``` * * The registry owner may add more registries to become available * in the future, or the registry owner may burn a fuse to prevent * new renderes being added and permanently lock list of registered * renderers. * * This function, essentially, delegates the rendering task to the * `renderURI` function of the `OpenAvatarGen0Renderer` that matches the * given DNA. If no specific renderer is assigned to a token, it uses * the default renderer. * * @param dna The unique DNA of the avatar to render. * @return A base64 encoded URI that represents the rendered image * for the provided avatar DNA. * * @dev For a minted DNA, this function uses the assigned renderer or * the default renderer if none is set. For an un-minted DNA, it always * uses the default renderer. */ function renderURI(bytes32 dna) external view returns (string memory) { return IOpenAvatarGen0Renderer(openAvatarGen0RendererRegistry.getRendererByDNA(dna)).renderURI(dna); } ///////////////////////////////////////////////////////////////////////////// // IOpenAvatarGen0TokenMetadata - An interface for the OpenAvatar metadata. ///////////////////////////////////////////////////////////////////////////// /** * @notice Returns a base64-encoded JSON URI that encapsulates OpenAvatar * metadata for the given OpenAvatar DNA. * * The resulting URI includes the Generation of the avatar, the DNA * itself, the OpenAvatarGen0Renderer used to render the image of the avatar, * the ABI signature of the rendering function, the token ID associated * with the DNA, and the owner of that token. * * @param dna The unique DNA of the avatar whose metadata to retrieve. * * @return A base64 encoded URI representing the metadata of the given * avatar DNA. * * @dev This function will revert if the provided DNA has not been minted * into a token yet. */ function openAvatarURI(bytes32 dna) external view returns (string memory) { uint tokenId = _getTokenIdByDNA(dna); string memory generationZeroStr = OPENAVATAR_GENERATION_ZERO.toString(); address creatorAddress = dnaToCreator[dna]; string memory creatorAddressStr = creatorAddress.toHexString(); address rendererAddress = openAvatarGen0RendererRegistry.getRendererByDNA(dna); string memory rendererAddressStr = rendererAddress.toHexString(); string memory tokenIdStr = tokenId.toString(); string memory dnaStr = uint(dna).toHexString(32); return string( abi.encodePacked( 'data:application/json;base64,', Base64.encode( abi.encodePacked( '{"generation":', generationZeroStr, ',"dna":"', dnaStr, '","creator":"', creatorAddressStr, '","renderer":"', rendererAddressStr, '","renderer_abi":"renderURI(bytes32)","token_id":', tokenIdStr, '}' ) ) ) ); } /** * @notice Retrieves OpenAvatar metadata associated with a specific token ID. * * The metadata encapsulates details such as the generation of the avatar, * the DNA, the chosen renderer for the avatar, the ABI signature of the * rendering function, the token ID itself, and the owner of the token. * * @param tokenId The unique identifier of the token to query. * * @return A `OpenAvatarGen0TokenMetadata` struct representing the metadata * associated with the token ID. * * @dev This function will revert if the provided token ID has not been * minted yet. */ function _getOpenAvatarGen0TokenMetadataByTokenId( uint tokenId ) internal view returns (OpenAvatarGen0TokenMetadata memory) { if (!_exists(tokenId)) revert TokenIdDoesNotExist(tokenId); // struct OpenAvatarGen0TokenMetadata { // uint generation; // bytes32 dna; // address renderer; // uint tokenId; // address owner; // } bytes32 dna = _getDNAByTokenId(tokenId); return OpenAvatarGen0TokenMetadata( OPENAVATAR_GENERATION_ZERO, tokenId, dna, dnaToCreator[dna], openAvatarGen0RendererRegistry.getRendererByDNA(dna) ); } /** * @notice Retrieves OpenAvatar metadata associated with a specific DNA. * * The metadata encapsulates details such as the generation of the avatar, * the DNA itself, the chosen renderer for the avatar, the ABI signature of * the rendering function, the token ID, and the owner of the token. * * @param dna The unique DNA identifier of the avatar to query. * * @return A `OpenAvatarGen0TokenMetadata` struct representing the metadata * associated with the avatar's DNA. * * @dev This function will revert if the provided DNA has not been * minted into a token yet. */ function getOpenAvatarGen0TokenMetadataByDNA(bytes32 dna) external view returns (OpenAvatarGen0TokenMetadata memory) { return _getOpenAvatarGen0TokenMetadataByTokenId(_getTokenIdByDNA(dna)); } /** * @notice Retrieves OpenAvatar metadata associated with a specific token ID. * * The metadata encapsulates details such as the generation of the avatar, * the DNA, the chosen renderer for the avatar, the ABI signature of the * rendering function, the token ID itself, and the owner of the token. * * @param tokenId The unique identifier of the token to query. * * @return A `OpenAvatarGen0TokenMetadata` struct representing the metadata * associated with the token ID. * * @dev This function will revert if the provided token ID has not been * minted yet. */ function getOpenAvatarGen0TokenMetadataByTokenId( uint tokenId ) external view returns (OpenAvatarGen0TokenMetadata memory) { return _getOpenAvatarGen0TokenMetadataByTokenId(tokenId); } ///////////////////////////////////////////////////////////////////////////// // IOpenAvatarGen0TokenDNA - An interface for the OpenAvatar DNA. ///////////////////////////////////////////////////////////////////////////// /** * @notice Retrieves the DNA associated with a specific token ID. * * This function is a helper function to fetch the DNA for a given token ID. * The DNA serves as a unique identifier for each token, which can then be * used to retrieve other related information such as the token's metadata. * * @param tokenId The unique identifier of the token for which to retrieve * the DNA. * * @return The DNA represented as a bytes32 value associated with the token * ID. * * @dev This function will revert if the token ID provided has not been * minted yet. */ function _getDNAByTokenId(uint tokenId) internal view returns (bytes32) { if (!_exists(tokenId)) revert TokenIdDoesNotExist(tokenId); return tokenIdToDna[tokenId]; } /** * @notice Retrieves the DNA associated with a specific token ID for external * callers. * * @param tokenId The unique identifier of the token for which to retrieve * the DNA. * * @return The DNA represented as a bytes32 value associated with the token * ID. * * @dev This function will revert if the token ID provided has not been * minted yet. */ function getDNAByTokenId(uint tokenId) external view returns (bytes32) { return _getDNAByTokenId(tokenId); } /** * @notice Retrieves the DNAs associated with an array of token IDs. * * This function accepts an array of token IDs and returns an array of DNAs * corresponding to those IDs. * * @param tokenIds An array of unique identifiers for which to retrieve the * DNAs. * * @return An array of DNAs represented as bytes32 values associated with the * token IDs. * * @dev This function will revert if any of the token IDs provided have not * been minted yet. */ function getDNAsByTokenIds(uint[] calldata tokenIds) external view returns (bytes32[] memory) { uint length = tokenIds.length; bytes32[] memory dnas = new bytes32[](length); for (uint i = 0; i < length; ) { dnas[i] = _getDNAByTokenId(tokenIds[i]); unchecked { ++i; } } return dnas; } /** * @notice Retrieves the token ID associated with a specific DNA. * * This internal function takes a DNA value and returns the corresponding * token ID. If the DNA provided does not correspond to a minted token, the * function will revert * * @param dna The DNA for which to retrieve the token ID. * * @return The token ID associated with the provided DNA. * * @dev This function will revert if the DNA provided has not been minted * yet. */ function _getTokenIdByDNA(bytes32 dna) internal view returns (uint) { uint tokenId = dnaToTokenId[dna]; // defaults to 0 in which case we need to double check // that tokenId exists and matches provided dna // else it's just a consequence of 0 being default return if (tokenId == 0) { // Happy case : tokenId exists, can return // Revert case : tokenId does not exist, revert // // We need to determine happy vs revert case // // CASE 1 (revert): // tokenId == 0 && it doesn't exist // --> revert (since not exists) // // CASE 2 // tokenId == 0 && it does exist // => the 0 token has been minted // => we need to disambiguate whether we got tokenId == 0 // because of the default return or because it's the 0 token // // CASE A (happy): // tokenIdToDna[tokenId] == dna // => we got the 0 token // => return tokenId (which is 0) // // CASE B: (revert) // tokenIdToDna[tokenId] != dna // => we got the default return // => revert (since not exists) // if (!_exists(tokenId) || tokenIdToDna[tokenId] != dna) { revert DNADoesNotExist(dna); } } return tokenId; } /** * @notice Retrieves the token ID associated with a specific DNA. * * @param dna The DNA for which to retrieve the token ID. * * @return The token ID associated with the provided DNA. * * @dev This function will revert if the DNA provided has not been minted * yet. */ function getTokenIdByDNA(bytes32 dna) external view returns (uint) { return _getTokenIdByDNA(dna); } /** * @notice Retrieves the token IDs associated with an array of DNAs. * * This function accepts an array of DNAs and returns an array of token * IDs corresponding to those DNAs. * * @param dnas An array of DNAs for which to retrieve the token IDs. * * @return An array of token IDs associated with the provided DNAs. * * @dev This function will revert if any of the DNAs provided have not been * minted yet. */ function getTokenIdsByDNAs(bytes32[] calldata dnas) external view returns (uint[] memory) { uint length = dnas.length; uint[] memory tokenIds = new uint[](length); for (uint i = 0; i < length; ) { tokenIds[i] = _getTokenIdByDNA(dnas[i]); unchecked { ++i; } } return tokenIds; } /** * @notice Retrieves the creator of a token. * * This function uses the DNA to identify the corresponding token and fetch * the creator of that token. * * @param tokenId The unique identifier of the token for which to retrieve * the DNA. * * @return The address of the creator of the token associated with the * provided token ID. If the token ID has not been minted yet, the zero * address is returned. */ function creatorOf(uint tokenId) external view returns (address) { return dnaToCreator[_getDNAByTokenId(tokenId)]; } /** * @notice Retrieves the creator of a token given its DNA. * * This function uses the DNA to identify the corresponding token and fetch * the creator of that token. * * @param dna The DNA of the token for which to retrieve the creator. * * @return The address of the creator of the token associated with the provided * DNA. * * @dev This function will revert if the DNA provided has not been minted * yet. */ function creatorOfDNA(bytes32 dna) external view returns (address) { if (!_exists(_getTokenIdByDNA(dna))) revert DNADoesNotExist(dna); return dnaToCreator[dna]; } /** * @notice Retrieves the owner of a token given its DNA. * * This function uses the DNA to identify the corresponding token and fetch * the owner of that token. * * @param dna The DNA of the token for which to retrieve the owner. * * @return The address of the owner of the token associated with the provided * DNA. * @dev This function will revert if the DNA provided has not been minted * yet. */ function ownerOfDNA(bytes32 dna) external view returns (address) { return ownerOf(_getTokenIdByDNA(dna)); } /** * @notice Retrieves the owners of tokens given an array of DNAs. * * This function accepts an array of DNAs and returns an array of owners * corresponding to those DNAs. * * @param dnas An array of DNAs for which to retrieve the owners. * * @return An array of addresses representing the owners of the tokens * associated with the provided DNAs. * * @dev This function will revert if any of the DNAs provided have not been * minted yet. */ function ownerOfDNAs(bytes32[] calldata dnas) external view returns (address[] memory) { uint length = dnas.length; address[] memory owners = new address[](length); for (uint i = 0; i < length; i++) { owners[i] = ownerOf(_getTokenIdByDNA(dnas[i])); } return owners; } ///////////////////////////////////////////////////////////////////////////// // IOpenAvatarMintRead - An interface for reading OpenAvatar minting state. ///////////////////////////////////////////////////////////////////////////// /** * @notice Checks if the specified DNA has been associated with a token * (minted). * @param dna The DNA string to be checked. * @return Returns true if the DNA has been minted, false otherwise. */ function isMinted(bytes32 dna) external view returns (bool) { uint tokenId = dnaToTokenId[dna]; if (tokenId > 0) { // if the token id is > 0, then the dna has been minted // we don't need to check _exists because this mapping is // only set when the token is minted // (but it's not trivial because of the way batch mint // has to check this data structure first) return true; } // we need to double check that the token id exists and matches the // provided dna bytes32 dnaFromTokenId = tokenIdToDna[tokenId]; // if the dna from the token id does not match the provided dna // then the dna has not been minted if (dnaFromTokenId != dna) return false; // if they are both zero, then the dna has not been minted // except in one exceptional case: // an invalid batch mint of [DNA=0, DNA=0] as the first two tokens // otherwise, the dna has been minted return _exists(tokenId); } /** * @notice Checks the mint status for each DNA in the provided list. * @param dnas An array of DNA strings to be checked. * @return An array of booleans indicating the mint status for each DNA. A * true value at index i indicates that dnas[i] has been minted. */ function isMintedEach(bytes32[] calldata dnas) external view returns (bool[] memory) { uint length = dnas.length; bool[] memory minted = new bool[](length); for (uint i = 0; i < length; ) { minted[i] = this.isMinted(dnas[i]); unchecked { ++i; } } return minted; } /** * @notice Retrieves the current price required to mint a new token via the * mint function. * @return The current price in wei for minting a new token. */ function getMintPrice() external view returns (uint) { return mintPrice; } /** * @notice Retrieves the "soft cap" token mint quantity. This number is * tokens tha can be minted. This number is mutable and can increase up * to the hard cap. * Invariant: totalSupply() <= supplySoftCap() <= supplyHardCap(). * @return Maximum quantity of tokens mintable. */ function supplySoftCap() external view returns (uint16) { return _supplySoftCap; } /** * @notice increases the soft cap supply. This number is mutable and can * increase up to the hard cap. Invariant: totalSupply() <= supplySoftCap() * <= supplyHardCap(). * @param amount The new soft cap supply. */ function increaseSupplySoftCap(uint16 amount) external onlyOwner { if (fuseBurnedCanIncreaseSupplySoftCap) revert OperationBlockedByBurnedFuse(); if (amount <= _supplySoftCap) revert SupplySoftCapChangeIsNotAnIncrease(); if (amount > MAX_SUPPLY) revert SupplySoftCapWouldExceedHardCap(amount); _supplySoftCap = amount; } /** * @notice Retrieves the maximum token mint quantity. This number is immutable * and can never change. The number of tokens minted can never exceed this * number. Invariant: totalSupply() <= supplySoftCap() <= supplyHardCap(). * @return Maximum quantity of tokens mintable. */ function supplyHardCap() external pure returns (uint16) { return MAX_SUPPLY; } ///////////////////////////////////////////////////////////////////////////// // IOpenAvatarMint - An interface allowing the mint price to be updated. ///////////////////////////////////////////////////////////////////////////// /** * @notice Sets the price to mint a new token using the mint function. * @param val The new price to mint a new token using the mint function. * @dev Only callable by the owner. */ function setMintPrice(uint val) external onlyOwner { if (val > mintPrice && fuseBurnedCanRaiseMintPrice) revert OperationBlockedByBurnedFuse(); if (val < mintPrice && fuseBurnedCanLowerMintPrice) revert OperationBlockedByBurnedFuse(); if (mintPrice == val) return; uint oldPrice = mintPrice; mintPrice = val; emit MintPriceChange(oldPrice, val); } ///////////////////////////////////////////////////////////////////////////// // Fuses ///////////////////////////////////////////////////////////////////////////// /** * @notice Check if fuse for increasing soft cap supply. * @return True if fuse is burned, false otherwise. */ function isFuseBurnedCanIncreaseSupplySoftCap() external view returns (bool) { return fuseBurnedCanIncreaseSupplySoftCap; } /** * @notice Burns the fuse that prohibits increasing the soft cap supply. * @dev Executable only by owner. No effect if fuse is already burned. */ function burnFuseCanIncreaseSupplySoftCap() external onlyOwner { if (fuseBurnedCanIncreaseSupplySoftCap) return; fuseBurnedCanIncreaseSupplySoftCap = true; emit FuseBurnedCanIncreaseSupplySoftCap(); } /** * @notice Check if fuse for lowering mint price is burned. * @return True if fuse is burned, false otherwise. */ function isFuseBurnedCanLowerMintPrice() external view returns (bool) { return fuseBurnedCanLowerMintPrice; } /** * @notice Burns the fuse that prohibits decreasing the mint price. * @dev Executable only by owner. No effect if fuse is already burned. */ function burnFuseCanLowerMintPrice() external onlyOwner { if (fuseBurnedCanLowerMintPrice) return; fuseBurnedCanLowerMintPrice = true; emit FuseBurnedCanLowerMintPrice(); } /** * @notice Check if fuse for raising mint price is burned. * @return True if fuse is burned, false otherwise. */ function isFuseBurnedCanRaiseMintPrice() external view returns (bool) { return fuseBurnedCanRaiseMintPrice; } /** * @notice Burns the fuse that prohibits increasing the mint price. * @dev Executable only by owner. No effect if fuse is already burned. */ function burnFuseCanRaiseMintPrice() external onlyOwner { if (fuseBurnedCanRaiseMintPrice) return; fuseBurnedCanRaiseMintPrice = true; emit FuseBurnedCanRaiseMintPrice(); } ///////////////////////////////////////////////////////////////////////////// // IOpenAvatarMintWrite - An interface for minting OpenAvatars. ///////////////////////////////////////////////////////////////////////////// /** * @notice Mints a token for a given address. * @param to The recipient of the minted token. * @param dna The dna of the minted token. */ function _mintTo(address to, bytes32 dna) internal { // ensure dna is not already used if (this.isMinted(dna)) revert DNAAlreadyMinted(dna); // 0-indexed token id uint tokenId = totalSupply(); // ensure its not bigger than max supply if (tokenId < _supplySoftCap) { tokenIdToDna[tokenId] = dna; dnaToTokenId[dna] = tokenId; dnaToCreator[dna] = to; // mint _safeMint(to, 1); // emit event emit Mint(to, dna, tokenId); } else { revert SupplyCapExceeded(); } } /** * @notice Mints a token. * @param dna The dna of the minted token. * @dev Only callable if mint is public. */ function mint(bytes32 dna) external payable onlyAuthorizedMint { if (msg.value < mintPrice) revert MintUnderpaid(); // mint to the immediate caller, either EOA or contract _mintTo(msg.sender, dna); } /** * @notice Mints a token for a given address. * @param to The recipient of the minted token. * @param dna The dna of the minted token. * @dev Only callable if mint is public. */ function mintTo(address to, bytes32 dna) external payable onlyAuthorizedMint { if (msg.value < mintPrice) revert MintUnderpaid(); // mint to provided address _mintTo(to, dna); } /** * @notice Mints a batch of tokens for a given address. * @param to The recipient of the minted tokens. * @param dnas The dnas of the minted tokens. */ function _mintBatchTo(address to, bytes32[] calldata dnas) internal { uint totalSupply = totalSupply(); uint n = dnas.length; if (n == 0) return; // no-op if (n > MAX_MINT_BATCH_SIZE_LIMIT) revert MintBatchSizeLimitExceeded(n); if (totalSupply + n - 1 < _supplySoftCap) { for (uint i; i < n; ) { bytes32 dna = dnas[i]; // don't allow batch minting of the null DNA // else we might end up with multiple tokens with the same DNA // because the way the mapping checks work in the isMinted function // we cannot distinguish between a DNA with tokenId=0 and a DNA that // has not been minted // // so ultimately this primarily prevents a bug of calling // mintBatch([0, 0, ...]) before mint(0) has been called // // for prod, call mint(0) before public mint opens then this can be // removed for gas optimization since isMinted will return true for // null DNA if (dna == 0) revert NullDNARestrictedFromBatchMint(dna); // ensure dna is not already used if (this.isMinted(dna)) revert DNAAlreadyMinted(dna); // 0-indexed token id uint tokenId = totalSupply + i; tokenIdToDna[tokenId] = dna; dnaToTokenId[dna] = tokenId; dnaToCreator[dna] = to; // emit event emit Mint(to, dna, tokenId); unchecked { ++i; } } // mint all as one batch _safeMint(to, n); } else { revert SupplyCapExceeded(); } } /** * @notice Mints a batch of tokens. * @param dnas The dnas of the minted tokens. * @dev Only callable if mint is public. */ function mintBatch(bytes32[] calldata dnas) external payable onlyAuthorizedMint { if (msg.value < mintPrice * dnas.length) revert MintUnderpaid(); // mint to the immediate caller, either EOA or contract _mintBatchTo(msg.sender, dnas); } /** * @notice Mints a batch of tokens for a given address. * @param to The recipient of the minted tokens. * @param dnas The dnas of the minted tokens. * @dev Only callable if mint is public. */ function mintBatchTo(address to, bytes32[] calldata dnas) external payable onlyAuthorizedMint { if (msg.value < mintPrice * dnas.length) revert MintUnderpaid(); // mint to the provided address _mintBatchTo(to, dnas); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol) pragma solidity ^0.8.0; import "../utils/Context.sol"; /** * @dev Contract module which provides a basic access control mechanism, where * there is an account (an owner) that can be granted exclusive access to * specific functions. * * By default, the owner account will be the one that deploys the contract. This * can later be changed with {transferOwnership}. * * This module is used through inheritance. It will make available the modifier * `onlyOwner`, which can be applied to your functions to restrict their use to * the owner. */ abstract contract Ownable is Context { address private _owner; event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); /** * @dev Initializes the contract setting the deployer as the initial owner. */ constructor() { _transferOwnership(_msgSender()); } /** * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { _checkOwner(); _; } /** * @dev Returns the address of the current owner. */ function owner() public view virtual returns (address) { return _owner; } /** * @dev Throws if the sender is not the owner. */ function _checkOwner() internal view virtual { require(owner() == _msgSender(), "Ownable: caller is not the owner"); } /** * @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`). * Can only be called by the current owner. */ function transferOwnership(address newOwner) public virtual onlyOwner { require(newOwner != address(0), "Ownable: new owner is the zero address"); _transferOwnership(newOwner); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Internal function without access restriction. */ function _transferOwnership(address newOwner) internal virtual { address oldOwner = _owner; _owner = newOwner; emit OwnershipTransferred(oldOwner, newOwner); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/Context.sol) pragma solidity ^0.8.0; /** * @dev Provides information about the current execution context, including the * sender of the transaction and its data. While these are generally available * via msg.sender and msg.data, they should not be accessed in such a direct * manner, since when dealing with meta-transactions the account sending and * paying for execution may not be the actual sender (as far as an application * is concerned). * * This contract is only required for intermediate, library-like contracts. */ abstract contract Context { function _msgSender() internal view virtual returns (address) { return msg.sender; } function _msgData() internal view virtual returns (bytes calldata) { return msg.data; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts 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) (utils/math/Math.sol) pragma solidity ^0.8.0; /** * @dev Standard math utilities missing in the Solidity language. */ library Math { enum Rounding { Down, // Toward negative infinity Up, // Toward infinity Zero // Toward zero } /** * @dev Returns the largest of two numbers. */ function max(uint256 a, uint256 b) internal pure returns (uint256) { return a > b ? a : b; } /** * @dev Returns the smallest of two numbers. */ function min(uint256 a, uint256 b) internal pure returns (uint256) { return a < b ? a : b; } /** * @dev Returns the average of two numbers. The result is rounded towards * zero. */ function average(uint256 a, uint256 b) internal pure returns (uint256) { // (a + b) / 2 can overflow. return (a & b) + (a ^ b) / 2; } /** * @dev Returns the ceiling of the division of two numbers. * * This differs from standard division with `/` in that it rounds up instead * of rounding down. */ function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) { // (a + b - 1) / b can overflow on addition, so we distribute. return a == 0 ? 0 : (a - 1) / b + 1; } /** * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0 * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) * with further edits by Uniswap Labs also under MIT license. */ function mulDiv( uint256 x, uint256 y, uint256 denominator ) internal pure returns (uint256 result) { unchecked { // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256 // variables such that product = prod1 * 2^256 + prod0. uint256 prod0; // Least significant 256 bits of the product uint256 prod1; // Most significant 256 bits of the product assembly { let mm := mulmod(x, y, not(0)) prod0 := mul(x, y) prod1 := sub(sub(mm, prod0), lt(mm, prod0)) } // Handle non-overflow cases, 256 by 256 division. if (prod1 == 0) { return prod0 / denominator; } // Make sure the result is less than 2^256. Also prevents denominator == 0. require(denominator > prod1); /////////////////////////////////////////////// // 512 by 256 division. /////////////////////////////////////////////// // Make division exact by subtracting the remainder from [prod1 prod0]. uint256 remainder; assembly { // Compute remainder using mulmod. remainder := mulmod(x, y, denominator) // Subtract 256 bit number from 512 bit number. prod1 := sub(prod1, gt(remainder, prod0)) prod0 := sub(prod0, remainder) } // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1. // See https://cs.stackexchange.com/q/138556/92363. // Does not overflow because the denominator cannot be zero at this stage in the function. uint256 twos = denominator & (~denominator + 1); assembly { // Divide denominator by twos. denominator := div(denominator, twos) // Divide [prod1 prod0] by twos. prod0 := div(prod0, twos) // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one. twos := add(div(sub(0, twos), twos), 1) } // Shift in bits from prod1 into prod0. prod0 |= prod1 * twos; // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for // four bits. That is, denominator * inv = 1 mod 2^4. uint256 inverse = (3 * denominator) ^ 2; // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works // in modular arithmetic, doubling the correct bits in each step. inverse *= 2 - denominator * inverse; // inverse mod 2^8 inverse *= 2 - denominator * inverse; // inverse mod 2^16 inverse *= 2 - denominator * inverse; // inverse mod 2^32 inverse *= 2 - denominator * inverse; // inverse mod 2^64 inverse *= 2 - denominator * inverse; // inverse mod 2^128 inverse *= 2 - denominator * inverse; // inverse mod 2^256 // Because the division is now exact we can divide by multiplying with the modular inverse of denominator. // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1 // is no longer required. result = prod0 * inverse; return result; } } /** * @notice Calculates x * y / denominator with full precision, following the selected rounding direction. */ function mulDiv( uint256 x, uint256 y, uint256 denominator, Rounding rounding ) internal pure returns (uint256) { uint256 result = mulDiv(x, y, denominator); if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) { result += 1; } return result; } /** * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down. * * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11). */ function sqrt(uint256 a) internal pure returns (uint256) { if (a == 0) { return 0; } // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target. // // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`. // // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)` // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))` // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)` // // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit. uint256 result = 1 << (log2(a) >> 1); // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128, // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision // into the expected uint128 result. unchecked { result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; return min(result, a / result); } } /** * @notice Calculates sqrt(a), following the selected rounding direction. */ function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = sqrt(a); return result + (rounding == Rounding.Up && result * result < a ? 1 : 0); } } /** * @dev Return the log in base 2, rounded down, of a positive value. * Returns 0 if given 0. */ function log2(uint256 value) internal pure returns (uint256) { uint256 result = 0; unchecked { if (value >> 128 > 0) { value >>= 128; result += 128; } if (value >> 64 > 0) { value >>= 64; result += 64; } if (value >> 32 > 0) { value >>= 32; result += 32; } if (value >> 16 > 0) { value >>= 16; result += 16; } if (value >> 8 > 0) { value >>= 8; result += 8; } if (value >> 4 > 0) { value >>= 4; result += 4; } if (value >> 2 > 0) { value >>= 2; result += 2; } if (value >> 1 > 0) { result += 1; } } return result; } /** * @dev Return the log in base 2, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ function log2(uint256 value, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = log2(value); return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0); } } /** * @dev Return the log in base 10, rounded down, of a positive value. * Returns 0 if given 0. */ function log10(uint256 value) internal pure returns (uint256) { uint256 result = 0; unchecked { if (value >= 10**64) { value /= 10**64; result += 64; } if (value >= 10**32) { value /= 10**32; result += 32; } if (value >= 10**16) { value /= 10**16; result += 16; } if (value >= 10**8) { value /= 10**8; result += 8; } if (value >= 10**4) { value /= 10**4; result += 4; } if (value >= 10**2) { value /= 10**2; result += 2; } if (value >= 10**1) { result += 1; } } return result; } /** * @dev Return the log in base 10, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ function log10(uint256 value, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = log10(value); return result + (rounding == Rounding.Up && 10**result < value ? 1 : 0); } } /** * @dev Return the log in base 256, rounded down, of a positive value. * Returns 0 if given 0. * * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string. */ function log256(uint256 value) internal pure returns (uint256) { uint256 result = 0; unchecked { if (value >> 128 > 0) { value >>= 128; result += 16; } if (value >> 64 > 0) { value >>= 64; result += 8; } if (value >> 32 > 0) { value >>= 32; result += 4; } if (value >> 16 > 0) { value >>= 16; result += 2; } if (value >> 8 > 0) { result += 1; } } return result; } /** * @dev Return the log in base 10, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ function log256(uint256 value, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = log256(value); return result + (rounding == Rounding.Up && 1 << (result * 8) < value ? 1 : 0); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.0) (utils/Strings.sol) pragma solidity ^0.8.0; import "./math/Math.sol"; /** * @dev String operations. */ library Strings { bytes16 private constant _SYMBOLS = "0123456789abcdef"; uint8 private constant _ADDRESS_LENGTH = 20; /** * @dev Converts a `uint256` to its ASCII `string` decimal representation. */ function toString(uint256 value) internal pure returns (string memory) { unchecked { uint256 length = Math.log10(value) + 1; string memory buffer = new string(length); uint256 ptr; /// @solidity memory-safe-assembly assembly { ptr := add(buffer, add(32, length)) } while (true) { ptr--; /// @solidity memory-safe-assembly assembly { mstore8(ptr, byte(mod(value, 10), _SYMBOLS)) } value /= 10; if (value == 0) break; } return buffer; } } /** * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation. */ function toHexString(uint256 value) internal pure returns (string memory) { unchecked { return toHexString(value, Math.log256(value) + 1); } } /** * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length. */ function toHexString(uint256 value, uint256 length) internal pure returns (string memory) { bytes memory buffer = new bytes(2 * length + 2); buffer[0] = "0"; buffer[1] = "x"; for (uint256 i = 2 * length + 1; i > 1; --i) { buffer[i] = _SYMBOLS[value & 0xf]; value >>= 4; } require(value == 0, "Strings: hex length insufficient"); return string(buffer); } /** * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation. */ function toHexString(address addr) internal pure returns (string memory) { return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH); } }
// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity ^0.8.20; /** * @title OpenAvatarGen0AssetsCanvasIdStore * @dev This contracts stores a canvas ID. */ abstract contract OpenAvatarGen0AssetsCanvasIdStore { /// @notice The canvas ID. uint8 public canvasId; constructor(uint8 canvasId_) { canvasId = canvasId_; } /** * @notice Get the canvas ID. * @return The canvas ID. */ function getCanvasId() external view returns (uint8) { return canvasId; } }
// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity ^0.8.20; import {Ownable} from '@openzeppelin/contracts/access/Ownable.sol'; import {CanvasHeader, IOpenAvatarGen0AssetsCanvasStore} from '../interfaces/assets/IOpenAvatarGen0AssetsCanvasStore.sol'; import {FuseGuard} from '../lib/FuseGuard.sol'; import {KeepAlive} from '../lib/KeepAlive.sol'; /** * @title OpenAvatarGen0AssetsCanvasStore * @dev This contract stores canvas headers * @dev This contract is used for off-chain rendering */ contract OpenAvatarGen0AssetsCanvasStore is IOpenAvatarGen0AssetsCanvasStore, FuseGuard, KeepAlive { /// @dev Event emitted when a canvas is added. event CanvasAdd(uint8 id); /// @dev Event emitted when the fuse is burned to disable adding a new canvas. event FuseBurnedCanAddCanvas(); /// @dev Rvert error when canvas already exists. error CanvasAlreadyExists(uint8 id); /// @dev Revert error when canvas does not exist. error CanvasDoesNotExist(uint8 id); /// @dev Revert error when canvas size is invalid. error InvalidCanvasSize(uint8 width, uint8 height); /// @dev The canvas bytes per pixel. /// @dev the only reason this is separate and named with CANVAS_ prefix is because // there is another one in PaletteStore and they conflict uint8 public constant CANVAS_BYTES_PER_PIXEL = 4; // RGBA ///////////////////////////////////////////////////////////////////////////// // Internal Data Structures ///////////////////////////////////////////////////////////////////////////// /// @dev The canvas ids. uint8[] public canvasIds; /// @dev The canvas headers. mapping(uint => CanvasHeader) public canvasHeaders; /// @dev Whether a canvas exists. mapping(uint => bool) public canvasExists; ///////////////////////////////////////////////////////////////////////////// // Fuses ///////////////////////////////////////////////////////////////////////////// /// @dev Flag to indicate if the fuse has been burned for adding a new canvas. bool public fuseBurnedCanAddCanvas = false; constructor(address ownerProxy) { // will be deployed by ImmutableCreate2Factory and then transferred to the configured owner // using a proxy allows for using same bytecode in test and prod address wantOwner = Ownable(ownerProxy).owner(); if (owner() != wantOwner) { transferOwnership(wantOwner); } } ///////////////////////////////////////////////////////////////////////////// // ERC-165: Standard Interface Detection ///////////////////////////////////////////////////////////////////////////// /** * @notice Checks if the contract supports an interface. * @param interfaceId The interface identifier, as specified in ERC-165. * @return True if the contract supports interfaceID, false otherwise. */ function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) { return interfaceId == 0x01ffc9a7 || // ERC165 interface ID for ERC165. // canvas interfaceId == 0x91411495 || // ERC165 interface ID for IOpenAvatarGen0AssetsCanvasStoreRead. interfaceId == 0x4d4a1c57 || // ERC165 interface ID for IOpenAvatarGen0AssetsCanvasStoreWrite. interfaceId == 0xdc0b08c2; // ERC165 interface ID for IOpenAvatarGen0AssetsCanvasStore. } ///////////////////////////////////////////////////////////////////////////// // Fuses ///////////////////////////////////////////////////////////////////////////// /** * @notice Burn the fuse to permanently disable adding a new canvas. * @dev Only callable by owner. * @dev no-op if fuse is already burned */ function burnFuseCanAddCanvas() external onlyOwner { if (fuseBurnedCanAddCanvas) return; fuseBurnedCanAddCanvas = true; emit FuseBurnedCanAddCanvas(); } /** * @notice Returns whether the fuse is burned to permanently disable adding a new canvas. * @return Whether the fuse is burned to permanently disable adding a new canvas. */ function isFuseBurnedCanAddCanvas() external view returns (bool) { return fuseBurnedCanAddCanvas; } ///////////////////////////////////////////////////////////////////////////// // Canvas ///////////////////////////////////////////////////////////////////////////// /** * @notice Add a canvas to the store. * @param header The canvas header. */ function addCanvas(CanvasHeader calldata header) external override onlyOwner { if (fuseBurnedCanAddCanvas) revert OperationBlockedByBurnedFuse(); if (canvasExists[header.id]) revert CanvasAlreadyExists(header.id); if (header.width == 0 || header.height == 0) revert InvalidCanvasSize(header.width, header.height); canvasHeaders[header.id] = header; canvasIds.push(header.id); canvasExists[header.id] = true; emit CanvasAdd(header.id); } /** * @notice Return the number of canvases stored in the contract. * @return The number of canvases stored in the contract. */ function hasCanvas(uint8 id) external view override returns (bool) { return canvasExists[id]; } /** * @notice Return the canvas header. * @param id The canvas to query. * @return The canvas header. * @dev Returns all zeroes if the canvas does not exist. */ function getCanvasHeader(uint8 id) external view override returns (CanvasHeader memory) { return canvasHeaders[id]; } /** * @notice Return the number of canvases stored in the contract. * @return The number of canvases stored in the contract. */ function getNumCanvasIds() external view override returns (uint) { return canvasIds.length; } /** * @notice Return the number of canvases stored in the contract. * @return The array of canvas ids stored in the contract. */ function getCanvasIds() external view override returns (uint8[] memory) { return canvasIds; } /** * @notice Return the height of the canvas. * @param id The canvas to query. * @return The height of the canvas. */ function getCanvasHeight(uint8 id) public view override returns (uint8) { return canvasHeaders[id].height; } /** * @notice Return the width of the canvas. * @param id The canvas to query. * @return The width of the canvas. */ function getCanvasWidth(uint8 id) public view override returns (uint8) { return canvasHeaders[id].width; } /** * @notice Return the number of bytes in the canvas. * @param id The canvas to query. * @return The number of bytes in the canvas. */ function getCanvasNumBytes(uint8 id) public view override returns (uint) { return CANVAS_BYTES_PER_PIXEL * uint(canvasHeaders[id].width) * uint(canvasHeaders[id].height); } /** * @notice Return the number of pixels in the canvas. * @param id The canvas to query. * @return The number of pixels in the canvas. */ function getCanvasNumPixels(uint8 id) public view override returns (uint) { return uint(canvasHeaders[id].width) * uint(canvasHeaders[id].height); } }
// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity ^0.8.20; import {Ownable} from '@openzeppelin/contracts/access/Ownable.sol'; import {IOpenAvatarGen0AssetsPaletteStore, UploadPaletteBatchInput, UploadPaletteInput} from '../interfaces/assets/IOpenAvatarGen0AssetsPaletteStore.sol'; import {FuseGuard} from '../lib/FuseGuard.sol'; import {KeepAlive} from '../lib/KeepAlive.sol'; /** * @title OpenAvatarGen0AssetsPaletteStore * @dev This contract stores color palettes by (code, index). * @dev All palettes of a given code should be the same length. */ contract OpenAvatarGen0AssetsPaletteStore is IOpenAvatarGen0AssetsPaletteStore, FuseGuard, KeepAlive { /// @dev Error for invalid palette code. error InvalidPaletteCode(uint8 code); /// @dev Error for empty palette array. error EmptyPaletteArray(); /// @dev Error for invalid palette index. error PaletteIndexOutOfBounds(uint8 code, uint8 index); /// @dev Error for palette already exists. error PaletteAlreadyExists(uint8 code, uint8 index); /// @dev Error for invalid palette length. error InvalidPaletteLength(uint length); /// @dev Error for invalid transparent color code. error InvalidTransparentColorCode(bytes4 code); /// @dev Event emitted when the fuse is burned to disable uploading a new palette. event FuseBurnedCanUploadPalette(); /// @dev Event emitted when a palette is uploaded. event PaletteUpload(uint8 code, uint8 index); /// @dev RGBA color depth. uint8 public constant DEPTH = 4; /// @dev Whether the canvas has an alpha channel. bool public constant HAS_ALPHA_CHANNEL = true; ///////////////////////////////////////////////////////////////////////////// // Internal Data Structures ///////////////////////////////////////////////////////////////////////////// /// @dev The color palettes. /// @dev 5D array /// bytes4 -----------> (red, green, blue, alpha) color /// bytes4[] ---------> A color palette /// bytes4[][] -------> A set of color palettes (for a palette code) /// bytes4[][][] -----> All the color palette sets for all the palette codes bytes4[][][] public palettes; ///////////////////////////////////////////////////////////////////////////// // Fuses ///////////////////////////////////////////////////////////////////////////// /// @dev Flag to indicate if the fuse has been burned for uploading a palette. bool public fuseBurnedCanUploadPalette = false; constructor(address ownerProxy) { // will be deployed by ImmutableCreate2Factory and then transferred to the configured owner // using a proxy allows for using same bytecode in test and prod address wantOwner = Ownable(ownerProxy).owner(); if (owner() != wantOwner) { transferOwnership(wantOwner); } } /** * @notice Return whether the canvas has an alpha channel. * @return Whether the canvas has an alpha channel. */ function hasAlphaChannel() external pure override returns (bool) { return HAS_ALPHA_CHANNEL; } /** * @notice Return the number of bytes per pixel. * @return The number of bytes per pixel. */ function getBytesPerPixel() public pure override returns (uint8) { return DEPTH; } ///////////////////////////////////////////////////////////////////////////// // ERC-165: Standard Interface Detection ///////////////////////////////////////////////////////////////////////////// /** * @notice Checks if the contract supports an interface. * @param interfaceId The interface identifier, as specified in ERC-165. * @return True if the contract supports interfaceID, false otherwise. */ function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) { return interfaceId == 0x01ffc9a7 || // ERC165 interface ID for ERC165. // palette interfaceId == 0x5577825f || // ERC165 interface ID for IOpenAvatarGen0AssetsPaletteStoreRead. interfaceId == 0x9c9764e9 || // ERC165 interface ID for IOpenAvatarGen0AssetsPaletteStoreWrite. interfaceId == 0xc9e0e6b6; // ERC165 interface ID for IOpenAvatarGen0AssetsPaletteStore. } ///////////////////////////////////////////////////////////////////////////// // Fuses ///////////////////////////////////////////////////////////////////////////// /** * @notice Burn the fuse to permanently disable uploading a new palette. * @dev Only callable by owner. * @dev no-op if fuse is already burned */ function burnFuseCanUploadPalette() external onlyOwner { if (fuseBurnedCanUploadPalette) return; fuseBurnedCanUploadPalette = true; emit FuseBurnedCanUploadPalette(); } /** * @notice Returns whether the fuse is burned to permanently disable uploading a new palette. * @return Whether the fuse is burned to permanently disable uploading a new palette. */ function isFuseBurnedCanUploadPalette() external view returns (bool) { return fuseBurnedCanUploadPalette; } ///////////////////////////////////////////////////////////////////////////// // Palettes ///////////////////////////////////////////////////////////////////////////// /** * @notice Get the number of palette codes. */ function getNumPaletteCodes() external view override returns (uint) { return palettes.length; } /** * @notice Get the number of palettes for the given layer and index. * @param code The palette code. */ function getNumPalettes(uint8 code) external view override returns (uint) { return (code < palettes.length) ? palettes[code].length : 0; } /** * @notice Get the palette for the given palette code and index. * @param code The code for which to get the palette. * @param index The index of the palette to get. * @return The palette for the given layer, pattern, index. * @dev Returns an empty array if the palette does not exist. */ function getPalette(uint8 code, uint8 index) external view override returns (bytes4[] memory) { if (code < palettes.length) { bytes4[][] storage codePalettes = palettes[code]; if (index < codePalettes.length) { return codePalettes[index]; } } // return empty array if not found return new bytes4[](0); } /** * @notice Store the given palette in the contract * @param input The palette to store. */ function uploadPalette(UploadPaletteInput calldata input) external override onlyOwner { // check fuse if (fuseBurnedCanUploadPalette) revert OperationBlockedByBurnedFuse(); // should always include transparent (will revert later if index 0 is not transparent) if (input.palette.length == 0) revert InvalidPaletteLength(input.palette.length); // Base Case: palettes[code] does not exist yet if (input.code == palettes.length) { // create new array of palettes palettes.push(new bytes4[][](0)); } // Append Case: palettes[code] exists if (input.code < palettes.length) { bytes4[][] storage codePalettes = palettes[input.code]; // 3 cases: // 1. index == length, append (happy path) // 2. index < length, revert // 3. index > length, revert uint ct = codePalettes.length; if (input.index == ct) { // append // check that the first color is transparent if (input.palette[0] != 0x00000000) { revert InvalidTransparentColorCode(input.palette[0]); } // TODO: verify length of each palette matches the same as the first // palette uploaded for that code codePalettes.push(input.palette); emit PaletteUpload(input.code, input.index); } else if (input.index < ct) { // already exists revert PaletteAlreadyExists(input.code, input.index); } else { // index > ct out of bounds revert PaletteIndexOutOfBounds(input.code, input.index); } } else { // out of bounds revert InvalidPaletteCode(input.code); } } /** * @notice Store the given palettes in the contract * @param input The paletted to store. */ function uploadPaletteBatch(UploadPaletteBatchInput calldata input) external override onlyOwner { _uploadPaletteBatch(input); } /** * @notice Store the given palettes in the contract * @param input The paletted to store. */ function _uploadPaletteBatch(UploadPaletteBatchInput calldata input) internal { // check fuse if (fuseBurnedCanUploadPalette) revert OperationBlockedByBurnedFuse(); // should always include transparent (will revert later if index 0 is not transparent) if (input.palettes.length == 0) revert EmptyPaletteArray(); // Base Case: palettes[code] does not exist yet if (input.code == palettes.length) { // create new array of palettes palettes.push(new bytes4[][](0)); } // Append Case: palettes[code] exists if (input.code < palettes.length) { bytes4[][] storage codePalettes = palettes[input.code]; // 3 cases: // 1. index == length, append (happy path) // 2. index < length, revert // 3. index > length, revert uint ct = codePalettes.length; if (input.fromIndex == ct) { // append all palettes uint len = input.palettes.length; for (uint i = 0; i < len; ) { bytes4[] calldata palette = input.palettes[i]; // check that the first color is transparent if (palette[0] != 0x00000000) { revert InvalidTransparentColorCode(palette[0]); } // TODO: verify length of each palette matches? codePalettes.push(palette); emit PaletteUpload(input.code, uint8(i)); unchecked { ++i; } } } else if (input.fromIndex < ct) { // already exists revert PaletteAlreadyExists(input.code, input.fromIndex); } else { // index > ct out of bounds revert PaletteIndexOutOfBounds(input.code, input.fromIndex); } } else { // out of bounds revert InvalidPaletteCode(input.code); } } /** * @notice Batch upload multiple palette batches. * @param inputs The palette batches to upload. */ function uploadPaletteBatches(UploadPaletteBatchInput[] calldata inputs) external override onlyOwner { uint len = inputs.length; for (uint i = 0; i < len; ) { _uploadPaletteBatch(inputs[i]); unchecked { ++i; } } } }
// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity ^0.8.20; import {IOpenAvatarGen0AssetsPatternStore, OptionalPatternHeader, PatternHeader, PatternBlob, UploadPatternInput} from '../interfaces/assets/IOpenAvatarGen0AssetsPatternStore.sol'; import {OpenAvatarGen0AssetsCanvasStore} from './OpenAvatarGen0AssetsCanvasStore.sol'; import {OpenAvatarGen0AssetsPaletteStore} from './OpenAvatarGen0AssetsPaletteStore.sol'; /** * @title OpenAvatarGen0AssetsPatternStore * @dev This contract stores assets via: * - canvas * - layer * - pattern * and for palettes, by: * - palette code * - palette */ contract OpenAvatarGen0AssetsPatternStore is IOpenAvatarGen0AssetsPatternStore, OpenAvatarGen0AssetsCanvasStore, OpenAvatarGen0AssetsPaletteStore { /// @dev Event emitted when the fuse is burned to disable adding a new layer. event FuseBurnedCanAddLayer(); /// @dev Event emitted when the fuse is burned to disable uploading a pattern. event FuseBurnedCanUploadPattern(); /// @dev Event emitted when a layer is added to a canvas. event LayerAdd(uint8 canvasId, uint8 layer); /// @dev Event emitted when a pattern is uploaded. event PatternUpload(uint8 canvasId, uint8 layer, uint8 pattern); /// @dev Revert error when layer already exists. error LayerAlreadyExists(uint8 canvasId, uint8 layer); /// @dev Revert error when layer index is out of bounds. error LayerIndexOutOfBounds(uint8 canvasId, uint8 layer); /// @dev Revert error when pattern has invalid length. error InvalidPatternLength(uint width, uint height, uint length); /// @dev Revert error when pattern already exists. error PatternAlreadyExists(uint8 canvasId, uint8 layer, uint8 index); /// @dev Revert error when pattern index is out of bounds. error PatternIndexOutOfBounds(uint8 canvasId, uint8 layer, uint8 index); /// @dev Revert error when palette code does not exist. error UnknownPaletteCode(uint8 code); ///////////////////////////////////////////////////////////////////////////// // Internal Data Structures ///////////////////////////////////////////////////////////////////////////// /// @dev Each pattern has a corresponding header describing the pattern data /// @dev Every pattern is an arbitrary blob of bytes mapping(uint => PatternBlob[][]) public patterns; ///////////////////////////////////////////////////////////////////////////// // Fuses ///////////////////////////////////////////////////////////////////////////// /// @dev Flag to indicate if the fuse has been burned for adding a new layer. bool public fuseBurnedCanAddLayer = false; /// @dev Flag to indicate if the fuse has been burned for uploading a pattern. bool public fuseBurnedCanUploadPattern = false; // solhint-disable-next-line no-empty-blocks constructor( address ownerProxy ) OpenAvatarGen0AssetsCanvasStore(ownerProxy) OpenAvatarGen0AssetsPaletteStore(ownerProxy) {} ///////////////////////////////////////////////////////////////////////////// // ERC-165: Standard Interface Detection ///////////////////////////////////////////////////////////////////////////// /** * @notice Checks if the contract supports an interface. * @param interfaceId The interface identifier, as specified in ERC-165. * @return True if the contract supports interfaceID, false otherwise. */ function supportsInterface( bytes4 interfaceId ) public view virtual override(OpenAvatarGen0AssetsCanvasStore, OpenAvatarGen0AssetsPaletteStore) returns (bool) { return interfaceId == 0x01ffc9a7 || // ERC165 interface ID for ERC165. // canvas interfaceId == 0x91411495 || // ERC165 interface ID for IOpenAvatarGen0AssetsCanvasStoreRead. interfaceId == 0x4d4a1c57 || // ERC165 interface ID for IOpenAvatarGen0AssetsCanvasStoreWrite. interfaceId == 0xdc0b08c2 || // ERC165 interface ID for IOpenAvatarGen0AssetsCanvasStore. // palette interfaceId == 0x5577825f || // ERC165 interface ID for IOpenAvatarGen0AssetsPaletteStoreRead. interfaceId == 0x9c9764e9 || // ERC165 interface ID for IOpenAvatarGen0AssetsPaletteStoreWrite. interfaceId == 0xc9e0e6b6 || // ERC165 interface ID for IOpenAvatarGen0AssetsPaletteStore. // pattern interfaceId == 0x32c8b38e || // ERC165 interface ID for IOpenAvatarGen0AssetsPatternStoreRead. interfaceId == 0xcd89a9e1 || // ERC165 interface ID for IOpenAvatarGen0AssetsPatternStoreWrite. interfaceId == 0xff411a6f; // ERC165 interface ID for IOpenAvatarGen0AssetsPatternStore. } ///////////////////////////////////////////////////////////////////////////// // Fuses ///////////////////////////////////////////////////////////////////////////// /** * @notice Burn the fuse to permanently disable adding a new layer. * @dev Only callable by owner. * @dev no-op if fuse is already burned */ function burnFuseCanAddLayer() external onlyOwner { if (fuseBurnedCanAddLayer) return; fuseBurnedCanAddLayer = true; emit FuseBurnedCanAddLayer(); } /** * @notice Returns whether the fuse is burned to permanently disable adding a new layer. * @return Whether the fuse is burned to permanently disable adding a new layer. */ function isFuseBurnedCanAddLayer() external view returns (bool) { return fuseBurnedCanAddLayer; } /** * @notice Burn the fuse to permanently disable uploading a new pattern. * @dev Only callable by owner. * @dev no-op if fuse is already burned */ function burnFuseCanUploadPattern() external onlyOwner { if (fuseBurnedCanUploadPattern) return; fuseBurnedCanUploadPattern = true; emit FuseBurnedCanUploadPattern(); } /** * @notice Returns whether the fuse is burned to permanently disable uploading a new pattern. * @return Whether the fuse is burned to permanently disable uploading a new pattern. */ function isFuseBurnedCanUploadPattern() external view returns (bool) { return fuseBurnedCanUploadPattern; } /************************************************************************** * Helpers *************************************************************************/ /** * @notice Pack four uint8 values into a single uint32. * @param a The first uint8 value. * @param b The second uint8 value. * @param c The third uint8 value. * @param d The fourth uint8 value. * @param e The fifth uint8 value. * @return The packed uint40 value. */ function packUint40(uint8 a, uint8 b, uint8 c, uint8 d, uint8 e) public pure returns (uint40) { return (uint40(a) << 32) | (uint40(b) << 24) | (uint40(c) << 16) | (uint40(d) << 8) | uint40(e); } /************************************************************************** * Layers *************************************************************************/ /** * @notice Get the number of layers stored in the contract. * @param canvasId The canvas for which to get the pattern data. * @return The number of layers stored in the contract. */ function getNumLayers(uint8 canvasId) public view override returns (uint) { return patterns[canvasId].length; } /** * @notice Add a new layer to the contract. The layer must not already exist. Layer indices may be skipped. * @param canvasId The canvas for which to get the pattern data. * @param layer The layer to add. * @dev This function is only callable by the contract owner. */ function addLayer(uint8 canvasId, uint8 layer) public override onlyOwner { if (fuseBurnedCanAddLayer) revert OperationBlockedByBurnedFuse(); if (!canvasExists[canvasId]) revert CanvasDoesNotExist(canvasId); unchecked { // layers can be skipped when adding to reserve for later // so we need to fill in the skipped layers with empty arrays PatternBlob[][] storage canvasPatterns = patterns[canvasId]; while (canvasPatterns.length < layer + 1) { canvasPatterns.push(new PatternBlob[](0)); } // now we have backfilled the layers, so procede with the main data PatternBlob[] storage patternsArrayForNewLayer = canvasPatterns[layer]; if (patternsArrayForNewLayer.length == 0) { // transparent means no height/width/offsets // transparent layer has all zeros // transparent pattern is empty bytes patternsArrayForNewLayer.push(PatternBlob(PatternHeader(0, 0, 0, 0, 0), new bytes(0))); emit LayerAdd(canvasId, layer); } else { revert LayerAlreadyExists(canvasId, layer); } } } /** * @notice Add multiple layers to the contract. The layers must not already exist. Layer indices may be skipped. * @param canvasId The canvas for which to get the pattern data. * @param layers_ The layers to add. * @dev This function is only callable by the contract owner. */ function addLayers(uint8 canvasId, uint8[] calldata layers_) public override onlyOwner { if (!canvasExists[canvasId]) revert CanvasDoesNotExist(canvasId); uint len = layers_.length; for (uint i = 0; i < len; ) { addLayer(canvasId, layers_[i]); unchecked { ++i; } } } /************************************************************************** * Patterns *************************************************************************/ /** * @notice Get the number of patterns for the given layer and index. * @param canvasId The canvas for which to get the pattern data. * @param layer The layer for which to get the number of patterns. * @return The number of patterns for the given layer and index. */ function getNumPatterns(uint8 canvasId, uint8 layer) public view override returns (uint) { PatternBlob[][] storage canvasPatterns = patterns[canvasId]; if (layer < canvasPatterns.length) { return canvasPatterns[layer].length; } else { // we have a choice to return 0 or revert // since any valid layer always has a transparent pattern, we can return 0 // and callers can use that to determine if the layer exists without handling // a revert return 0; } } /** * @notice Get the pattern header for the given layer and index. * @param canvasId The canvas for which to get the pattern data. * @param layer The layer for which to get the pattern header. * @param index The index of the pattern header to get. * @return The pattern header for the given layer and index. */ function getPatternHeader( uint8 canvasId, uint8 layer, uint8 index ) external view override returns (OptionalPatternHeader memory) { PatternBlob[][] storage canvasPatterns = patterns[canvasId]; if (layer < canvasPatterns.length) { PatternBlob[] storage layerPatterns = canvasPatterns[layer]; if (index < layerPatterns.length) { return OptionalPatternHeader(true, layerPatterns[index].header); } } return OptionalPatternHeader(false, PatternHeader(0, 0, 0, 0, 0)); } /** * @notice Get the pattern for the given layer and index. * @param canvasId The canvas for which to get the pattern data. * @param layer The layer for which to get the pattern data. * @param index The index of the pattern to get. * @return The pattern for the given layer and index. */ function getPatternData(uint8 canvasId, uint8 layer, uint8 index) public view override returns (bytes memory) { PatternBlob[][] storage canvasPatterns = patterns[canvasId]; if (layer < canvasPatterns.length) { PatternBlob[] storage layerPatterns = canvasPatterns[layer]; if (index < layerPatterns.length) { return layerPatterns[index].data; } } return new bytes(0); } /** * @notice Store the given pattern in the contract. * @param input The input. */ function uploadPattern(UploadPatternInput calldata input) public override onlyOwner { if (fuseBurnedCanUploadPattern) revert OperationBlockedByBurnedFuse(); if (!canvasExists[input.canvasId]) revert CanvasDoesNotExist(input.canvasId); // consistency checks if (input.data.length != uint(input.width) * uint(input.height)) { revert InvalidPatternLength(input.width, input.height, input.data.length); } if (input.paletteCode < palettes.length) { PatternBlob[][] storage canvasPatterns = patterns[input.canvasId]; if (input.layer < canvasPatterns.length) { PatternBlob[] storage layerPatterns = canvasPatterns[input.layer]; uint ct = layerPatterns.length; // 3 cases: // 1. index == length, append (happy path) // 2. index < length, revert // 3. index > length, revert if (input.index == ct) { // append layerPatterns.push( PatternBlob( PatternHeader(input.width, input.height, input.offsetX, input.offsetY, input.paletteCode), input.data ) ); emit PatternUpload(input.canvasId, input.layer, input.index); } else if (input.index < ct) { // already exists revert PatternAlreadyExists(input.canvasId, input.layer, input.index); } else { // out of bounds revert PatternIndexOutOfBounds(input.canvasId, input.layer, input.index); } } else { revert LayerIndexOutOfBounds(input.canvasId, input.layer); } } else { revert UnknownPaletteCode(input.paletteCode); } } /** * @notice Batch upload multiple patterns. * @param inputs The uploadPattern inputs. */ function uploadPatterns(UploadPatternInput[] calldata inputs) public override onlyOwner { if (fuseBurnedCanUploadPattern) revert OperationBlockedByBurnedFuse(); uint len = inputs.length; for (uint i = 0; i < len; ) { uploadPattern(inputs[i]); unchecked { ++i; } } } }
// SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.20; // Adapted from: // https://github.com/Brechtpd/base64/blob/4d85607b18d981acff392d2e99ba654305552a97/base64.sol // solhint-disable no-empty-blocks /// @title Base64 /// @author Brecht Devos - <[email protected]> /// @notice Provides functions for encoding/decoding base64 library Base64 { string internal constant TABLE_ENCODE = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; bytes internal constant TABLE_DECODE = hex'0000000000000000000000000000000000000000000000000000000000000000' hex'00000000000000000000003e0000003f3435363738393a3b3c3d000000000000' hex'00000102030405060708090a0b0c0d0e0f101112131415161718190000000000' hex'001a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132330000000000'; function encode(bytes memory data) internal pure returns (bytes memory) { if (data.length == 0) return ''; // load the table into memory string memory table = TABLE_ENCODE; // multiply by 4/3 rounded up uint encodedLen = 4 * ((data.length + 2) / 3); // add some extra buffer at the end required for the writing bytes memory result = new bytes(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) { } { // read 3 bytes dataPtr := add(dataPtr, 3) let input := mload(dataPtr) // write 4 characters mstore8(resultPtr, mload(add(tablePtr, and(shr(18, input), 0x3F)))) resultPtr := add(resultPtr, 1) mstore8(resultPtr, mload(add(tablePtr, and(shr(12, input), 0x3F)))) resultPtr := add(resultPtr, 1) mstore8(resultPtr, mload(add(tablePtr, and(shr(6, input), 0x3F)))) resultPtr := add(resultPtr, 1) mstore8(resultPtr, 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 decode(string memory _data) internal pure returns (bytes memory) { bytes memory data = bytes(_data); if (data.length == 0) return new bytes(0); require(data.length % 4 == 0, 'invalid base64 decoder input'); // load the table into memory bytes memory table = TABLE_DECODE; // every 4 characters represent 3 bytes uint decodedLen = (data.length / 4) * 3; // add some extra buffer at the end required for the writing bytes memory result = new bytes(decodedLen + 32); assembly { // padding with '=' let lastBytes := mload(add(data, mload(data))) if eq(and(lastBytes, 0xFF), 0x3d) { decodedLen := sub(decodedLen, 1) if eq(and(lastBytes, 0xFFFF), 0x3d3d) { decodedLen := sub(decodedLen, 1) } } // set the actual output length mstore(result, decodedLen) // 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, 4 characters at a time for { } lt(dataPtr, endPtr) { } { // read 4 characters dataPtr := add(dataPtr, 4) let input := mload(dataPtr) // write 3 bytes let output := add( add( shl(18, and(mload(add(tablePtr, and(shr(24, input), 0xFF))), 0xFF)), shl(12, and(mload(add(tablePtr, and(shr(16, input), 0xFF))), 0xFF)) ), add( shl(6, and(mload(add(tablePtr, and(shr(8, input), 0xFF))), 0xFF)), and(mload(add(tablePtr, and(input, 0xFF))), 0xFF) ) ) mstore(resultPtr, shl(232, output)) resultPtr := add(resultPtr, 3) } } return result; } }
// SPDX-License-Identifier: UNLICENSED pragma solidity >=0.8.4; interface ENS { /** * @dev Returns the address that owns the specified node. * @param node The specified node. * @return address of the owner. */ function owner(bytes32 node) external view returns (address); }
// SPDX-License-Identifier: UNLICENSED pragma solidity >=0.8.4; interface IReverseRegistrar { /** * @dev Transfers ownership of the reverse ENS record associated with the * calling account. * @param owner The address to set as the owner of the reverse record in ENS. * @return The ENS node hash of the reverse record. */ function claim(address owner) external returns (bytes32); /** * @dev Sets the `name()` record for the reverse ENS record associated with * the calling account. First updates the resolver to the default reverse * resolver if necessary. * @param name The name to set for this address. * @return The ENS node hash of the reverse record. */ function setName(string memory name) external returns (bytes32); }
// SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.20; /** * @title IERC634: Text Data Interface * @dev This is the same as EIP-634 with different variable names * - rename node -> dna * - +calldata for key in text() * - +external for text() * - +memory for return value of text() */ interface IERC634 { /** * @notice Returns the text data associated with a DNA * @param dna A DNA to lookup text data for * @param key A key to lookup text data for * @return text The text data */ function text(bytes32 dna, string calldata key) external view returns (string memory text); }
// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity ^0.8.20; import {Ownable} from '@openzeppelin/contracts/access/Ownable.sol'; import {FuseGuard} from '../lib/FuseGuard.sol'; /** * @title AOpenAvatarMintStateMachine * @notice A contract that manages the mint state machine. */ abstract contract AOpenAvatarMintStateMachine is FuseGuard, Ownable { /// @dev The mint states. enum MintState { PermanentlyDisabled, Disabled, OnlyOwner, PublicPendingBlockTimestamp, Public } /// @dev Event emitted when the fuse is burned to disable changing the mint state. event FuseBurnedCanChangeMintState(); /// @dev Event emitted when the fuse is burned to disable changing the public mint time. event FuseBurnedCanChangePublicMintTime(); /// @dev Event emitted when the mint state changes. event MintStateChange(MintState state); /// @dev Event emitted when the public mint time changes. event PublicMintTimeChange(uint time); /// @dev Revert error when mint is permanently disabled. error MintPermanentlyDisabled(); /// @dev Revert error when public mint is not yet started. error PublicMintNotStarted(); /// @dev Revert error when public mint is disabled. error MintDisabled(); /// @dev Revert error when public mint is not authorized. error MintNotAuthorized(); ///////////////////////////////////////////////////////////////////////////// // Internal Data Structures ///////////////////////////////////////////////////////////////////////////// /// @dev The current mint state. MintState public mintStatus = MintState.Disabled; /// @dev The time when public minting will be enabled, if pending public mint time. uint256 public publicMintTime; ///////////////////////////////////////////////////////////////////////////// // Fuses ///////////////////////////////////////////////////////////////////////////// /// @dev Flag to indicate if the fuse has been burned for changing the mint state. bool public fuseBurnedCanChangeMintState = false; /// @dev Flag to indicate if the fuse has been burned for changing the public mint time. bool public fuseBurnedCanChangePublicMintTime = false; /** * @notice Sets whether mint is active. * @param val True to enable mint, false to disable. * @dev Only callable by owner. * @dev reverts if mint is fuse is burned * @dev no-op if mint is already set to the desired value * @dev reverts if mint is permanently disabled */ function setMintState(MintState val) external onlyOwner { if (fuseBurnedCanChangeMintState) revert OperationBlockedByBurnedFuse(); if (val == mintStatus) return; if (mintStatus == MintState.PermanentlyDisabled) revert MintPermanentlyDisabled(); mintStatus = val; emit MintStateChange(val); } /** * @notice Burn the fuse to permanently disable changing the mint state. * @dev Only callable by owner. * @dev no-op if fuse is already burned */ function burnFuseCanChangeMintState() external onlyOwner { if (fuseBurnedCanChangeMintState) return; fuseBurnedCanChangeMintState = true; emit FuseBurnedCanChangeMintState(); } /** * @notice Returns whether the fuse is burned to permanently disable changing the mint state. * @return Whether the fuse is burned to permanently disable changing the mint state. */ function isFuseBurnedCanChangeMintState() external view returns (bool) { return fuseBurnedCanChangeMintState; } /** * @notice Get the public mint time. * @return The public mint time. */ function getPublicMintTime() external view returns (uint256) { return publicMintTime; } /** * @notice Set the public mint time. * @param _publicMintTime The public mint time. * @dev reverts if fuse is burned */ function setPublicMintTime(uint256 _publicMintTime) external onlyOwner { if (fuseBurnedCanChangePublicMintTime) revert OperationBlockedByBurnedFuse(); if (publicMintTime == _publicMintTime) return; publicMintTime = _publicMintTime; emit PublicMintTimeChange(_publicMintTime); } /** * @notice Burn the fuse to permanently disable changing the public mint time. * @dev Only callable by owner. * @dev no-op if fuse is already burned */ function burnFuseCanChangePublicMintTime() external onlyOwner { if (fuseBurnedCanChangePublicMintTime) return; fuseBurnedCanChangePublicMintTime = true; emit FuseBurnedCanChangePublicMintTime(); } /** * @notice Returns whether the fuse is burned to permanently disable changing the public mint time. * @return Whether the fuse is burned to permanently disable changing the public mint time. */ function isFuseBurnedCanChangePublicMintTime() external view returns (bool) { return fuseBurnedCanChangePublicMintTime; } /** * @notice Returns the current mint state. * @return The current mint state. */ function getMintState() external view returns (MintState) { return mintStatus; } /** * @notice Returns whether mint state is public. * @return True if mint state is public, false otherwise. */ function isMintPublic() external view returns (bool) { return mintStatus == MintState.Public; } /** * @notice Returns whether mint state is public pending block timestamp. * @return True if mint state is public pending block timestamp, false otherwise. */ function isMintPublicPendingBlockTimestamp() external view returns (bool) { return mintStatus == MintState.PublicPendingBlockTimestamp; } /** * @notice Returns whether mint state is only owner can mint. * @return True if mint state is only owner can mint, false otherwise. */ function isMintOnlyOwner() external view returns (bool) { return mintStatus == MintState.OnlyOwner; } /** * @notice Returns whether mint state is paused or permanently disabled. * @return True if mint state is paused or permanently disabled, false otherwise. */ function isMintDisabled() external view returns (bool) { return mintStatus == MintState.Disabled || mintStatus == MintState.PermanentlyDisabled; } /** * @notice Returns whether mint state is permanently disabled. * @return True if mint state is permanently disabled, false otherwise. */ function isMintPermanentlyDisabled() external view returns (bool) { return mintStatus == MintState.PermanentlyDisabled; } ////////////////////////// modifiers ////////////////////////// /** * @notice Modifier to check if mint is public. * ---------------------------------------------------------------------- * | MintState | Owner | Non-owner | Result | * ---------------------------------------------------------------------- * | PermanentlyDisabled | No | No | Revert | * ---------------------------------------------------------------------- * | Disabled | No | No | Revert | * ---------------------------------------------------------------------- * | OnlyOwner | Yes | No | Proceed | * ---------------------------------------------------------------------- * | PublicPendingBlockTimestamp | No | No | Revert | * | (Before Time) | | | | * ---------------------------------------------------------------------- * | PublicPendingBlockTimestamp | Yes | Yes | Proceed | * | (At/After Time) | | | | * ---------------------------------------------------------------------- * | Public | Yes | Yes | Proceed | * ---------------------------------------------------------------------- * @dev Reverts if mint is not authorized. */ modifier onlyAuthorizedMint() { if (mintStatus == MintState.Public) { // CASE 1: mint is public _; } else if (mintStatus == MintState.PublicPendingBlockTimestamp) { // CASE 2: mint is public pending block timestamp // // in our normal scenario, this will only happen once, unless contract owner puts it back to public // pending block timestamp // // we are going to check the block timestamp to see if it is greater than or equal to the public mint time // if it is, we will update the mint state to public // otherwise, we will revert with an error // // solhint-disable-next-line not-rely-on-time if (block.timestamp < publicMintTime) { // CASE 2.1: && block timestamp is NOT greater than public mint time // => mint is not yet public so revert // // this happens if callers try to mint before the public mint time // revert PublicMintNotStarted(); } else { // CASE 2.2: && block timestamp IS greater than or equal public mint time // => update the mint state to public // => mint is now public so continue // // this will only happen once, unless contract owner puts it back to public // pending block timestamp // mintStatus = MintState.Public; emit MintStateChange(MintState.Public); } } else if (mintStatus == MintState.OnlyOwner) { // CASE 3: mint is only owner // // this should be the normal scenario with a single boolean check if (owner() == _msgSender()) { // CASE 3.1: && caller is owner // => mint is only owner && caller is owner so continue _; } else { // CASE 3.2: && caller is NOT owner // => mint is only owner && caller is NOT owner so revert revert MintNotAuthorized(); } } else if (mintStatus == MintState.Disabled) { revert MintDisabled(); } else { revert MintPermanentlyDisabled(); } } }
// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity ^0.8.20; import {Ownable} from '@openzeppelin/contracts/access/Ownable.sol'; /** * @title Treasury * @dev The Treasury contract is a contract that holds funds for the protocol. */ abstract contract Treasury is Ownable { event TreasuryWithdrawal(address indexed to, uint amount); /** * @notice Withdraw funds from the contract. * @param amount The amount to withdraw. * @dev Only the owner can withdraw funds. */ function withdraw(uint amount) public onlyOwner { require(amount <= address(this).balance, 'Insufficient balance'); payable(msg.sender).transfer(amount); emit TreasuryWithdrawal(msg.sender, amount); } }
// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity ^0.8.20; struct CanvasHeader { uint8 id; uint8 width; uint8 height; } /** * @title IOpenAvatarGen0AssetsCanvasStoreRead * @dev This interface reads canvas headers. */ interface IOpenAvatarGen0AssetsCanvasStoreRead { function hasCanvas(uint8 id) external view returns (bool); function getCanvasHeader(uint8 id) external view returns (CanvasHeader memory); function getNumCanvasIds() external view returns (uint); function getCanvasIds() external view returns (uint8[] memory); function getCanvasHeight(uint8 id) external view returns (uint8); function getCanvasWidth(uint8 id) external view returns (uint8); function getCanvasNumBytes(uint8 id) external view returns (uint); function getCanvasNumPixels(uint8 id) external view returns (uint); } /** * @title IOpenAvatarGen0AssetsCanvasStoreWrite * @dev This interface writes canvas headers */ interface IOpenAvatarGen0AssetsCanvasStoreWrite { function addCanvas(CanvasHeader calldata header) external; } /** * @title IOpenAvatarGen0AssetsCanvasStore * @dev This interface reads and writes canvas headers */ interface IOpenAvatarGen0AssetsCanvasStore is IOpenAvatarGen0AssetsCanvasStoreRead, IOpenAvatarGen0AssetsCanvasStoreWrite { }
// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity ^0.8.20; /** * @title IOpenAvatarGen0AssetsPaletteStoreRead * @dev This interface allows reading from the palette store. */ interface IOpenAvatarGen0AssetsPaletteStoreRead { ///////////////////////////////////////////////////////////////////////////// // Constants ///////////////////////////////////////////////////////////////////////////// function hasAlphaChannel() external view returns (bool); function getBytesPerPixel() external view returns (uint8); ///////////////////////////////////////////////////////////////////////////// // Palettes ///////////////////////////////////////////////////////////////////////////// function getNumPaletteCodes() external view returns (uint); function getNumPalettes(uint8 code) external view returns (uint); function getPalette(uint8 code, uint8 index) external view returns (bytes4[] memory); } struct UploadPaletteInput { uint8 code; uint8 index; bytes4[] palette; } struct UploadPaletteBatchInput { uint8 code; uint8 fromIndex; bytes4[][] palettes; } /** * @title IOpenAvatarGen0AssetsPaletteStoreWrite * @dev This interface allows writing to the palette store. */ interface IOpenAvatarGen0AssetsPaletteStoreWrite { function uploadPalette(UploadPaletteInput calldata input) external; function uploadPaletteBatch(UploadPaletteBatchInput calldata input) external; function uploadPaletteBatches(UploadPaletteBatchInput[] calldata input) external; } /** * @title IOpenAvatarGen0AssetsPaletteStore * @dev This interface allows reading from and writing to the palette store. */ interface IOpenAvatarGen0AssetsPaletteStore is IOpenAvatarGen0AssetsPaletteStoreRead, IOpenAvatarGen0AssetsPaletteStoreWrite { }
// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity ^0.8.20; import {IOpenAvatarGen0AssetsCanvasStore, IOpenAvatarGen0AssetsCanvasStoreRead, IOpenAvatarGen0AssetsCanvasStoreWrite} from './IOpenAvatarGen0AssetsCanvasStore.sol'; import {IOpenAvatarGen0AssetsPaletteStoreRead, IOpenAvatarGen0AssetsPaletteStoreWrite, IOpenAvatarGen0AssetsPaletteStore} from './IOpenAvatarGen0AssetsPaletteStore.sol'; struct PatternHeader { /// @dev width of the pattern uint8 width; /// @dev height of the pattern uint8 height; /// @dev x offset of the pattern within the canvas uint8 offsetX; /// @dev y offset of the pattern within the canvas uint8 offsetY; /// @dev the palette code for the pattern uint8 paletteCode; } struct OptionalPatternHeader { /// @dev true if the header exists bool exists; /// @dev the pattern header /// @dev all zeroes is valid header PatternHeader header; } struct PatternBlob { /// @dev the pattern header PatternHeader header; /// @dev the pattern data bytes data; } struct UploadPatternInput { /// @dev the canvas id uint8 canvasId; /// @dev index of the layer within the canvas uint8 layer; /// @dev index of the pattern within the layer uint8 index; /// @dev width of the pattern uint8 width; /// @dev height of the pattern uint8 height; /// @dev x offset of the pattern within the canvas uint8 offsetX; /// @dev y offset of the pattern within the canvas uint8 offsetY; /// @dev the palette code for the pattern uint8 paletteCode; /// @dev the pattern data bytes data; } /** * @title IOpenAvatarGen0AssetsPatternStoreRead * @dev This interface reads pattern data */ interface IOpenAvatarGen0AssetsPatternStoreRead is IOpenAvatarGen0AssetsCanvasStoreRead { ///////////////////////////////////////////////////////////////////////////// // Layers ///////////////////////////////////////////////////////////////////////////// function getNumLayers(uint8 canvasId) external view returns (uint); ///////////////////////////////////////////////////////////////////////////// // Patterns ///////////////////////////////////////////////////////////////////////////// function getNumPatterns(uint8 canvasId, uint8 layer) external view returns (uint); function getPatternHeader( uint8 canvasId, uint8 layer, uint8 index ) external view returns (OptionalPatternHeader memory); function getPatternData(uint8 canvasId, uint8 layer, uint8 index) external view returns (bytes memory); } /** * @title IOpenAvatarGen0AssetsPatternStoreWrite * @dev This interface writes pattern data */ interface IOpenAvatarGen0AssetsPatternStoreWrite is IOpenAvatarGen0AssetsCanvasStoreWrite { ///////////////////////////////////////////////////////////////////////////// // Layers ///////////////////////////////////////////////////////////////////////////// function addLayer(uint8 canvasId, uint8 layer) external; function addLayers(uint8 canvasId, uint8[] calldata layers) external; ///////////////////////////////////////////////////////////////////////////// // Patterns ///////////////////////////////////////////////////////////////////////////// function uploadPattern(UploadPatternInput calldata input) external; function uploadPatterns(UploadPatternInput[] calldata inputs) external; } /** * @title IOpenAvatarGen0AssetsPatternStore * @dev This interface reads and writes pattern data */ interface IOpenAvatarGen0AssetsPatternStore is IOpenAvatarGen0AssetsPatternStoreRead, IOpenAvatarGen0AssetsPatternStoreWrite, IOpenAvatarGen0AssetsCanvasStore { }
// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity ^0.8.20; import {PatternHeader} from '../assets/IOpenAvatarGen0AssetsPatternStore.sol'; struct LayerPatternPalette { uint8 layer; uint8 pattern; uint8 palette; } /** * @title IOpenAvatarGen0AssetsCanvasLayerCompositor * @dev This contract composes palettized patterns into a single image. */ interface IOpenAvatarGen0AssetsCanvasLayerCompositor { function createLayerComposition( uint8 canvasId, // is view function, so not concerned about calldata being cheaper // need memory because this function is called by other functions that // may compute layers dynamically in memory LayerPatternPalette[] memory layerPatternPalette ) external view returns (bytes memory); function drawLayerComposition( bytes memory out, uint8 canvasId, // is view function, so not concerned about calldata being cheaper // need memory because this function is called by other functions that // may compute layers dynamically in memory LayerPatternPalette[] memory layerPatternPalette ) external view returns (bytes memory); function drawLayer( bytes memory image, uint8 canvasId, uint8 layer, uint8 pattern, uint8 palette ) external view returns (bytes memory); function drawMaskedLayer( bytes memory image, bytes memory mask, uint8 canvasId, uint8 layer, uint8 pattern, uint8 palette ) external view returns (bytes memory); function drawPattern( bytes memory image, uint8 canvasId, PatternHeader memory header, bytes memory pattern, bytes4[] memory palette ) external view returns (bytes memory); function drawMaskedPattern( bytes memory image, bytes memory mask, uint8 canvasId, PatternHeader memory header, bytes memory pattern, bytes4[] memory palette ) external view returns (bytes memory); }
// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity ^0.8.20; interface IOpenAvatarGen0CanvasRenderer { function drawOpenAvatar(uint8 canvasId, bytes32 dna) external view returns (bytes memory); function drawOpenAvatarOverlay(bytes memory image, uint8 canvasId, bytes32 dna) external view returns (bytes memory); }
// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity ^0.8.20; /** * @title Adler32 * @notice This contract implements the Adler32 checksum algorithm. */ library Adler32 { function adler32(bytes memory self, uint offset, uint end) internal pure returns (uint32) { unchecked { uint32 a = 1; uint32 b = 0; // Process each byte of the data in order for (uint i = offset; i < end; i++) { a = (a + uint32(uint8(self[i]))) % 65521; b = (b + a) % 65521; } // The Adler-32 checksum is stored as a 4-byte value return (b << 16) | a; } } }
// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity ^0.8.20; /** * @title CRC32 * @notice This contract implements the CRC32 checksum algorithm. */ library CRC32 { // CRC32 algorithm: https://en.wikipedia.org/wiki/Cyclic_redundancy_check /** * @dev Calculates the CRC32 checksum of a chunk of data. * @param self The data to calculate the checksum of. * @param start The start index of the data. * @param end The end index of the data. * @return checksum The CRC32 checksum of the data. */ function crc32(bytes memory self, uint start, uint end) internal pure returns (uint32 checksum) { // Initialize the checksum to 0xffffffff checksum = 0xffffffff; // Loop through each byte of the chunk data for (uint i = start; i < end; i++) { // XOR the byte with the checksum checksum = checksum ^ uint8(self[i]); // Loop through each bit of the byte for (uint j = 0; j < 8; j++) { // If the LSB of the checksum is 1 if ((checksum & 1) == 1) { // 0xEDB88320 is the CRC-32 polynomial in reversed bit order // this translates to the polynomial with equation // x^32 + x^26 + x^23 + x^22 + x^16 + x^12 + x^11 + x^10 + x^8 + x^7 + x^5 + x^4 + x^2 + x + 1 // which is the same as the one used in the PNG specification checksum = (checksum >> 1) ^ 0xedb88320; } // If the LSB of the checksum is 0 else { // Shift the checksum right by 1 bit checksum = (checksum >> 1); } } } // Return the inverted checksum return ~checksum; } }
// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity ^0.8.20; /** * @title DNA * @notice This library implements the DNA as defined by OpenAvatar. * @dev The DNA string is a 32-byte hex string. The DNA string is immutable. * The bytes represent the following: * ZZZZ YYYY XXXX WWWW VVVV UUUU TTTT SSSS * 0000 0000 0000 0000 0000 0000 0000 0000 * * Bytes | Chars | Description * ---------|---------|------------- * [0:1] | [0:3] | body * [2:3] | [4:7] | tattoos * [4:5] | [8:11] | makeup * [6:7] | [12:15] | left eye * [8:9] | [16:19] | right eye * [10:11] | [20:23] | bottomwear * [12:13] | [24:27] | footwear * [14:15] | [28:31] | topwear * [16:17] | [32:35] | handwear * [18:19] | [36:39] | outerwear * [20:21] | [40:43] | jewelry * [22:23] | [44:47] | facial hair * [24:25] | [48:51] | facewear * [26:27] | [52:55] | eyewear * [28:29] | [56:59] | hair * [30:31] | [60:63] | reserved * * Each 2-byte section is a struct of the following: * [0] | [0:1] | pattern * [1] | [2:3] | palette * * The pattern is an index into the pattern array. * The palette is an index into the palette array. */ library DNA { ///////////////////////////////////////////////////////////////////////////// /// Body ///////////////////////////////////////////////////////////////////////////// /// bytes [0:1] /// @notice Returns the body pattern index from the DNA. function bodyPattern(bytes32 self) internal pure returns (uint8) { return uint8(self[0]); } /// @notice Returns the body palette index from the DNA. function bodyPalette(bytes32 self) internal pure returns (uint8) { return uint8(self[1]); } ///////////////////////////////////////////////////////////////////////////// /// Tattoos ///////////////////////////////////////////////////////////////////////////// /// bytes [2:3] /// @notice Returns the tattoos pattern index from the DNA. function tattoosPattern(bytes32 self) internal pure returns (uint8) { return uint8(self[2]); } /// @notice Returns the tattoos palette index from the DNA. function tattoosPalette(bytes32 self) internal pure returns (uint8) { return uint8(self[3]); } ///////////////////////////////////////////////////////////////////////////// /// Makeup ///////////////////////////////////////////////////////////////////////////// /// bytes [4:5] /// @notice Returns the makeup pattern index from the DNA. function makeupPattern(bytes32 self) internal pure returns (uint8) { return uint8(self[4]); } /// @notice Returns the makeup palette index from the DNA. function makeupPalette(bytes32 self) internal pure returns (uint8) { return uint8(self[5]); } ///////////////////////////////////////////////////////////////////////////// /// Left Eye ///////////////////////////////////////////////////////////////////////////// /// bytes [6:7] /// @notice Returns the left eye pattern index from the DNA. function leftEyePattern(bytes32 self) internal pure returns (uint8) { return uint8(self[6]); } /// @notice Returns the left eye palette index from the DNA. function leftEyePalette(bytes32 self) internal pure returns (uint8) { return uint8(self[7]); } ///////////////////////////////////////////////////////////////////////////// /// Right Eye ///////////////////////////////////////////////////////////////////////////// /// bytes [8:9] /// @notice Returns the right eye pattern index from the DNA. function rightEyePattern(bytes32 self) internal pure returns (uint8) { return uint8(self[8]); } /// @notice Returns the right eye palette index from the DNA. function rightEyePalette(bytes32 self) internal pure returns (uint8) { return uint8(self[9]); } ///////////////////////////////////////////////////////////////////////////// /// Bottomwear ///////////////////////////////////////////////////////////////////////////// /// bytes [10:11] /// @notice Returns the bottomwear pattern index from the DNA. function bottomwearPattern(bytes32 self) internal pure returns (uint8) { return uint8(self[10]); } /// @notice Returns the bottomwear palette index from the DNA. function bottomwearPalette(bytes32 self) internal pure returns (uint8) { return uint8(self[11]); } ///////////////////////////////////////////////////////////////////////////// /// Footwear ///////////////////////////////////////////////////////////////////////////// /// bytes [12:13] /// @notice Returns the footwear pattern index from the DNA. function footwearPattern(bytes32 self) internal pure returns (uint8) { return uint8(self[12]); } /// @notice Returns the footwear palette index from the DNA. function footwearPalette(bytes32 self) internal pure returns (uint8) { return uint8(self[13]); } ///////////////////////////////////////////////////////////////////////////// /// Topwear ///////////////////////////////////////////////////////////////////////////// /// bytes [14:15] /// @notice Returns the topwear pattern index from the DNA. function topwearPattern(bytes32 self) internal pure returns (uint8) { return uint8(self[14]); } /// @notice Returns the topwear palette index from the DNA. function topwearPalette(bytes32 self) internal pure returns (uint8) { return uint8(self[15]); } ///////////////////////////////////////////////////////////////////////////// /// Handwear ///////////////////////////////////////////////////////////////////////////// /// bytes [16:17] /// @notice Returns the handwear pattern index from the DNA. function handwearPattern(bytes32 self) internal pure returns (uint8) { return uint8(self[16]); } /// @notice Returns the handwear palette index from the DNA. function handwearPalette(bytes32 self) internal pure returns (uint8) { return uint8(self[17]); } ///////////////////////////////////////////////////////////////////////////// /// Outerwear ///////////////////////////////////////////////////////////////////////////// /// bytes [18:19] /// @notice Returns the outerwear pattern index from the DNA. function outerwearPattern(bytes32 self) internal pure returns (uint8) { return uint8(self[18]); } /// @notice Returns the outerwear palette index from the DNA. function outerwearPalette(bytes32 self) internal pure returns (uint8) { return uint8(self[19]); } ///////////////////////////////////////////////////////////////////////////// /// Jewelry ///////////////////////////////////////////////////////////////////////////// /// bytes [20:21] /// @notice Returns the jewelry pattern index from the DNA. function jewelryPattern(bytes32 self) internal pure returns (uint8) { return uint8(self[20]); } /// @notice Returns the jewelry palette index from the DNA. function jewelryPalette(bytes32 self) internal pure returns (uint8) { return uint8(self[21]); } ///////////////////////////////////////////////////////////////////////////// /// Facial Hair ///////////////////////////////////////////////////////////////////////////// /// bytes [22:23] /// @notice Returns the facial hair pattern index from the DNA. function facialHairPattern(bytes32 self) internal pure returns (uint8) { return uint8(self[22]); } /// @notice Returns the facial hair palette index from the DNA. function facialHairPalette(bytes32 self) internal pure returns (uint8) { return uint8(self[23]); } ///////////////////////////////////////////////////////////////////////////// /// Facewear ///////////////////////////////////////////////////////////////////////////// /// bytes [24:25] /// @notice Returns the facewear pattern index from the DNA. function facewearPattern(bytes32 self) internal pure returns (uint8) { return uint8(self[24]); } /// @notice Returns the facewear palette index from the DNA. function facewearPalette(bytes32 self) internal pure returns (uint8) { return uint8(self[25]); } ///////////////////////////////////////////////////////////////////////////// /// Eyewear ///////////////////////////////////////////////////////////////////////////// /// bytes [26:27] /// @notice Returns the eyewear pattern index from the DNA. function eyewearPattern(bytes32 self) internal pure returns (uint8) { return uint8(self[26]); } /// @notice Returns the eyewear palette index from the DNA. function eyewearPalette(bytes32 self) internal pure returns (uint8) { return uint8(self[27]); } ///////////////////////////////////////////////////////////////////////////// /// Hair ///////////////////////////////////////////////////////////////////////////// /// bytes [28:29] /// @notice Returns the hair pattern index from the DNA. function hairPattern(bytes32 self) internal pure returns (uint8) { return uint8(self[28]); } /// @notice Returns the hair palette index from the DNA. function hairPalette(bytes32 self) internal pure returns (uint8) { return uint8(self[29]); } }
//SPDX-License-Identifier: MIT pragma solidity ^0.8.20; import {Ownable} from '@openzeppelin/contracts/access/Ownable.sol'; import {ENS} from '../dependencies/ens/registry/ENS.sol'; import {IReverseRegistrar} from '../dependencies/ens/reverseRegistrar/IReverseRegistrar.sol'; /** * @title ENSReverseClaimer * @dev This contract is used to claim reverse ENS records. */ abstract contract ENSReverseClaimer is Ownable { /// @dev The namehash of 'addr.reverse', the domain at which reverse records /// are stored in ENS. bytes32 constant ADDR_REVERSE_NODE = 0x91d1777781884d03a6757a803996e38de2a42967fb37eeaca72729271025a9e2; /** * @dev Transfers ownership of the reverse ENS record associated with the * contract. * @param ens The ENS registry. * @param claimant The address to set as the owner of the reverse record in * ENS. * @return The ENS node hash of the reverse record. */ function claimReverseENS(ENS ens, address claimant) external onlyOwner returns (bytes32) { return IReverseRegistrar(ens.owner(ADDR_REVERSE_NODE)).claim(claimant); } /** * @dev Sets the reverse ENS record associated with the contract. * @param ens The ENS registry. * @param name The name to set as the reverse record in ENS. * @return The ENS node hash of the reverse record. */ function setReverseENS(ENS ens, string calldata name) external onlyOwner returns (bytes32) { return IReverseRegistrar(ens.owner(ADDR_REVERSE_NODE)).setName(name); } }
// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity ^0.8.20; import {Ownable} from '@openzeppelin/contracts/access/Ownable.sol'; /** * @title FuseGuard * @dev A contract that manages fuses. */ abstract contract FuseGuard { /// @dev Revert error when operation is blocked by burned fuse. error OperationBlockedByBurnedFuse(); }
// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity ^0.8.20; import {Strings} from '@openzeppelin/contracts/utils/Strings.sol'; import {Base64} from '../dependencies/Base64.sol'; import {PNG} from './PNG.sol'; /** * @title ImageEncoder * @dev A library for encoding images as PNG or SVG. */ contract ImageEncoder is PNG { /** * @notice Encodes the image as a Base64-encoded PNG. * @param data The raw image data. * @param width Width of the image data, in pixels. * @param height Height of the image data, in pixels. * @param alpha Whether the image has an alpha channel. * @return The encoded Base64-encoded PNG. */ function encodeBase64PNG(bytes memory data, uint width, uint height, bool alpha) public pure returns (bytes memory) { bytes memory png = encodePNG(data, width, height, alpha); return Base64.encode(png); } /** * @notice Encodes the image as an SVG. * @param data The raw image data. * @param width Width of the image data, in pixels. * @param height Height of the image, in pixels. * @param alpha Whether the image has an alpha channel. * @param svgWidth Width of the scaled SVG, in pixels. * @param svgHeight Height of the scaled SVG, in pixels. * @return The encoded SVG. */ function encodeSVG( bytes memory data, uint width, uint height, bool alpha, uint svgWidth, uint svgHeight ) public pure returns (bytes memory) { bytes memory base64PNG = encodeBase64PNG(data, width, height, alpha); string memory svgWidthStr = Strings.toString(svgWidth); string memory svgHeightStr = Strings.toString(svgHeight); return abi.encodePacked( '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ', svgWidthStr, ' ', svgHeightStr, '">\n\t<foreignObject width="', svgWidthStr, '" height="', svgHeightStr, '">\n\t\t<img xmlns="http://www.w3.org/1999/xhtml" width="', svgWidthStr, '" height="', svgHeightStr, '" style="image-rendering: pixelated;" src="data:image/png;base64,', base64PNG, '"/>\n\t</foreignObject>\n</svg>' ); } /** * @notice Encodes the image as a Base64-encoded SVG. * @param data The raw image data. * @param width Width of the image data, in pixels. * @param height Height of the image, in pixels. * @param alpha Whether the image has an alpha channel. * @param svgWidth Width of the scaled SVG, in pixels. * @param svgHeight Height of the scaled SVG, in pixels. * @return The encoded Base64-encoded SVG. */ function encodeBase64SVG( bytes memory data, uint width, uint height, bool alpha, uint svgWidth, uint svgHeight ) public pure returns (bytes memory) { bytes memory svg = encodeSVG(data, width, height, alpha, svgWidth, svgHeight); return Base64.encode(svg); } }
// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity ^0.8.20; import {Ownable} from '@openzeppelin/contracts/access/Ownable.sol'; /** * @title KeepAlive * @dev KeepAlive is a contract designed to maintain its onchain presence in * case of state expiration by using payable receive() and fallback() functions, * while allow the owner to still withdraw funds. * fallback(): * - Default function that gets executed when no other function in the contract * matches the provided function signature, or when the contract receives * Ether along with data * - Can be payable or non-payable * - Must be marked external * receive(): * - Introduced in Solidity 0.6.0 * - Special function that is executed when a contract receives Ether without * any data * - Must be payable * - Must be marked external * - Makes it easier to differentiate between intended Ether transfers and * other function calls */ contract KeepAlive is Ownable { /** * @notice Fallback function. * @dev fallback(): * - Default function that gets executed when no other function in the contract * matches the provided function signature, or when the contract receives * Ether along with data * - Can be payable or non-payable * - Must be marked external */ // solhint-disable-next-line no-empty-blocks fallback() external payable {} /** * @notice Receive funds. * @dev receive(): * - Introduced in Solidity 0.6.0 * - Special function that is executed when a contract receives Ether without * any data * - Must be payable * - Must be marked external * - Makes it easier to differentiate between intended Ether transfers and * other function calls */ // solhint-disable-next-line no-empty-blocks receive() external payable {} /** * @notice Withdraw funds from the contract. */ function withdraw(uint amount) external onlyOwner { payable(msg.sender).transfer(amount); } }
// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity ^0.8.20; import {Ownable} from '@openzeppelin/contracts/access/Ownable.sol'; /** * @title OwnerProxy * @dev OwnerProxy is a proxy contract that provides an onchain interface of * just the Ownable functions. * * The main purpose of this contract is to allow for deploying to * deterministic addresses via CREATE2, while still allowing for the owner * address to be updated. Normally, the owner address is set at deployment * time as a constructor arg, which means that the address of the contract * is not known until after it is deployed. This makes it difficult to * deploy contracts to a deterministic address. * * The OwnerProxy contract can be deployed to a preconfigured address via a * CREATE2 factory, after which subsequent contracts can be written to * refer to the OwnerProxy contract (at its preconfigured address) as * a way to determine the owner of the address at deployment time * for _subsequent_ CREATE2 deployments of _other_ contracts. * * Other contracts should be written to accept an OwnerProxy address as a * constructor arg, and then use that OwnerProxy's owner() as the owner * address for the contract being deployed. * * ``` * constructor(address ownerProxy) { * transferOwnership(Ownable(ownerProxy).owner()); * } * ``` * * This allows developers to mine CREATE2 addresses with static constructor * args input once this OwnerProxy is deployed, while still using different * owner addresses on different chains, (e.g. test or prod) by manipulating the * owner() of the OwnerProxy at deployment time of future contracts that * refer to it. * * This mechanism allows for developers to: * - deploy initization code via CREATE2 to static address (can be mined) * - to a developer-controlled owner address * - on any EVM chain * - now or in the future * - while allowing the post-deployment owner address to be updated for each * deployment or chain * * so long as: * - OwnerProxy is deployed via CREATE2 to the same address across all chains * - such that it can be passed in as a deterministic constructor arg to * contracts * * To ensure OwnerProxy is deployed at the same address it can either be: * - deployed via a CREATE2 factory with a deterministic salt * - deployed via the same deployer at the same nonce across all EVM chains * * The standard sequence as designed is: * - Determine deployer key * - Deploy 0age's ImmutableCreate2Factory as nonce 0 transaction * (deterministic address) * - Deploy OwnerProxy via ImmutableCreate2Factory::safeCreate2 with * deterministic salt S * - Deploy contract(s) with constructor arg of OwnerProxy address * - In contract constructor, transfer ownership to OwnerProxy::owner() */ contract OwnerProxy is Ownable { error NotMasterOwner(); address public immutable masterOwner; /** * @dev Construct the OwnerProxy. * @param masterOwner_ The address of the master owner. */ constructor(address masterOwner_) { masterOwner = masterOwner_; // test test test test test test test test test test test junk // m/44'/60'/0'/0/0 transferOwnership(0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266); } /** * @dev Modifier to ensure that only the master owner can call a function. */ modifier onlyMasterOwner() { if (msg.sender != masterOwner) revert NotMasterOwner(); _; } /** * @dev Transfer ownership to the master owner. */ function takeOwnership() external onlyMasterOwner { _transferOwnership(masterOwner); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.20; /** * @title PixelBlender * @dev This contract blends pixels together. */ contract PixelBlender { /** * @notice Blend two pixels together. * @param foreground The foreground pixel color value. * @param background The background pixel color value. * @param foregroundAlpha The alpha of the foreground pixel. * @return The blended pixel color value. */ function blendPixel(uint8 foreground, uint8 background, uint8 foregroundAlpha) internal pure returns (uint8) { return uint8((uint(foreground) * uint(foregroundAlpha) + uint(background) * (255 - uint(foregroundAlpha))) / 255); } /** * @notice Blend two alpha values together. * @param foregroundAlpha The foreground alpha value. * @param backgroundAlpha The background alpha value. * @return The blended alpha value. */ function blendAlpha(uint8 foregroundAlpha, uint8 backgroundAlpha) internal pure returns (uint8) { if (foregroundAlpha == 255) return 255; if (backgroundAlpha == 255) return 255; return uint8(uint(foregroundAlpha) + (uint(backgroundAlpha) * (255 - uint(foregroundAlpha))) / 255); } }
// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity ^0.8.20; import {Adler32} from './Adler32.sol'; import {CRC32} from './CRC32.sol'; /** * @title PNG * @dev PNG is a contract for generating PNG images from raw image data in Solidity. * It includes functions for encoding and decoding PNG images, as well as for * calculating Adler32 and CRC32 checksums. * * This contract is based on the PNG specification: * * http://www.libpng.org/pub/png/spec/1.2/PNG-Contents.html * * It supports only 8 bit images and supports RGB or RGBA color formats. * It uses compression method 0, filter method 0, and interlace method 0. */ contract PNG { using Adler32 for bytes; using CRC32 for bytes; /** * The PNG signature is a fixed eight-byte sequence: * 89 50 4e 47 0d 0a 1a 0a */ bytes public constant PNG_SIGNATURE = hex'89504e470d0a1a0a'; /** * The IEND chunk marks the end of the PNG datastream. * It contains no data. * * The IEND chunk must appear last. * It is an error to place any data after the IEND chunk. * * The IEND chunk is always equal to 12 bytes * 00 00 00 00 49 45 4e 44 ae 42 60 82 */ bytes public constant IEND = hex'0000000049454e44ae426082'; /** * @notice Encodes a PNG image from raw image data * @param data Raw image data * @param width The width of the image, in pixels * @param height The height of the image, in pixels * @param alpha Whether the image has an alpha channel * @return PNG image */ function encodePNG(bytes memory data, uint width, uint height, bool alpha) public pure returns (bytes memory) { unchecked { // Determine the width of each pixel uint pixelWidth = (alpha) ? 4 : 3; // Check that the length of the data is correct require(data.length == pixelWidth * width * height, 'Invalid image data length'); // Create the IHDR chunk bytes memory chunkIHDR = encodeIHDR(width, height, alpha); // Create the IDAT chunk bytes memory chunkIDAT = encodeIDAT(data, width, height, alpha); // Concatenate the chunks into a single bytes array. return abi.encodePacked(PNG_SIGNATURE, chunkIHDR, chunkIDAT, IEND); } } /** * @dev Generates an IHDR chunk for a PNG image with the given width and height. * This function generates an IHDR chunk according to the PNG specification * (http://libpng.org/pub/png/spec/1.2/PNG-Chunks.html#C.IHDR). * * @param width The width of the image. * @param height The height of the image. * @param alpha Whether the image has alpha transparency. * @return A bytes memory array containing the IDAT chunk data. */ function encodeIHDR(uint width, uint height, bool alpha) public pure returns (bytes memory) { // Create the IHDR chunk // The IHDR chunk length is 13 bytes (0x0000000d in hex) // The IHDR type 49 48 44 52 (IHDR) // // The IHDR chunk data consists of the following fields: // 4 bytes: width // 4 bytes: height // 1 byte: bit depth (8) // 1 byte: color type (2 for RGB, 6 for RGBA) // 1 byte: compression method (0) // 1 byte: filter method (0) // 1 byte: interlace method (0) // // 4 bytes: CRC32 checksum bytes memory chunkIHDR = hex'0000000d494844520000000000000000080200000000000000'; // Set the width and height of the image in the chunk data chunkIHDR[8] = bytes1(uint8(width >> 24)); chunkIHDR[9] = bytes1(uint8(width >> 16)); chunkIHDR[10] = bytes1(uint8(width >> 8)); chunkIHDR[11] = bytes1(uint8(width)); chunkIHDR[12] = bytes1(uint8(height >> 24)); chunkIHDR[13] = bytes1(uint8(height >> 16)); chunkIHDR[14] = bytes1(uint8(height >> 8)); chunkIHDR[15] = bytes1(uint8(height)); // Set the color type of the image in the chunk data if (alpha) { // truecolor image with alpha channel chunkIHDR[17] = hex'06'; } else { // truecolor image without alpha channel chunkIHDR[17] = hex'02'; } // Calculate and set the CRC32 checksum of the chunk uint32 checksum = chunkIHDR.crc32(4, 21); chunkIHDR[21] = bytes1(uint8(checksum >> 24)); chunkIHDR[22] = bytes1(uint8(checksum >> 16)); chunkIHDR[23] = bytes1(uint8(checksum >> 8)); chunkIHDR[24] = bytes1(uint8(checksum)); return chunkIHDR; } /** * @dev Interlaces a given bytes array of image data. * @param data The bytes array of image data. * @param width The width of the image, in pixels. * @param height The height of the image, in pixels. * @param alpha Whether the image has an alpha channel. * @return The interlaced bytes array. */ function interlace(bytes memory data, uint width, uint height, bool alpha) internal pure returns (bytes memory) { unchecked { uint pixelWidth = alpha ? 4 : 3; // IDAT chunk // The IDAT chunk contains the actual image data. // The layout and total size of this raw data are determined by the fields of IHDR. // The filtered data is then compressed using the method specified by the IHDR chunk. // Since our image has no filtering, // the filter type byte for each scanline would be 0x00 (no filtering). // Interlacing method 0 is used, so pixels are stored sequentially from left to right, // and scanlines sequentially from top to bottom (no interlacing). uint rowWidth = pixelWidth * width; uint rowWidthPadded = rowWidth + 1; // Declare a bytes array to hold the interlaced data. bytes memory interlacedData = new bytes(rowWidthPadded * height); // Loop over the scanlines. for (uint row = 0; row < height; row++) { // Calculate the starting index for the current scanline. uint startIndex = rowWidthPadded * row; // Set the filter type byte for the current scanline. interlacedData[startIndex] = 0x00; // Filter type 0 (no filtering) // Copy the scanline data into the interlaced data array. // No filtering is used, so the scanline data starts at index 1. for (uint j = 0; j < rowWidth; j++) { interlacedData[startIndex + 1 + j] = data[row * rowWidth + j]; } } return interlacedData; } } /** * @dev Generates a zlib-compressed version of the given image data using the Deflate algorithm. * This function generates a zlib-compressed version of the given image data using the Deflate algorithm, * as specified in the PNG specification (http://www.libpng.org/pub/png/spec/1.2/PNG-Compression.html). * The resulting data is suitable for storage in an IDAT chunk of a PNG file. * * @param data The image data to be compressed. * @param width The width of the image, in pixels. * @param height The height of the image, in pixels. * @param alpha Whether the image has alpha transparency. * @return A bytes array containing the zlib-compressed image data. */ function zlibCompressDeflate( bytes memory data, uint width, uint height, bool alpha ) internal pure returns (bytes memory) { unchecked { // Generate Deflate-compressed data bytes memory deflateCompressedData = interlace(data, width, height, alpha); // Calculate Adler-32 checksum of Deflate-compressed data uint32 blockAdler32 = deflateCompressedData.adler32(0, deflateCompressedData.length); // zlib block header (BFINAL = 1, BTYPE = 0) bytes memory zlibBlockHeader = hex'01'; // LEN is the length of the data bytes32 len = bytes32(deflateCompressedData.length); // Generate zlib-compressed data bytes memory result = abi.encodePacked( // zlib header // CM = 8 (deflate), CINFO = 7 (32K window size) hex'78', // FCHECK = 0 (no check) // FDICT = 0 (no preset dictionary) // FLEVEL = 0 (fastest compression) hex'01', // block header (BFINAL = 1, BTYPE = 0) zlibBlockHeader, // LEN (2 bytes) (length of the data) len[31], len[30], // NLEN (2 bytes) (one's complement of LEN) ~len[31], ~len[30], // Deflate-compressed data deflateCompressedData, // block footer (adler32 checksum) blockAdler32 ); return result; } } /** * @dev Generates an IDAT chunk for a PNG image with the given width and height. * This function generates an IDAT chunk according to the PNG specification * (http://www.libpng.org/pub/png/spec/1.2/PNG-Filters.html). * * @param data The filtered image data. * @param width The width of the image. * @param height The height of the image. * @param alpha Whether the image has alpha transparency. * @return A bytes memory array containing the IDAT chunk data. */ function encodeIDAT(bytes memory data, uint width, uint height, bool alpha) internal pure returns (bytes memory) { unchecked { // The IDAT data is compressed using the deflate algorithm. bytes memory compressed = zlibCompressDeflate(data, width, height, alpha); // The compressed data stream is then stored in the IDAT chunk. bytes memory typedata = abi.encodePacked( hex'49444154', // Chunk type: "IDAT" in ASCII compressed ); // CRC calculated from the chunk type and chunk data uint32 crc = typedata.crc32(0, typedata.length); // Append the CRC32 checksum to the end of the chunk return abi.encodePacked(uint32(compressed.length), typedata, crc); } } }
// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity ^0.8.20; import '../CRC32.sol'; // This contract is used to test the CRC32 library contract CRC32Test { function crc32(bytes memory data) public pure returns (uint32) { return CRC32.crc32(data, 0, data.length); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.20; import {PixelBlender} from '../PixelBlender.sol'; contract PixelBlenderTest is PixelBlender { function testBlendPixel(uint8 foreground, uint8 background, uint8 foregroundAlpha) public pure returns (uint8) { return blendPixel(foreground, background, foregroundAlpha); } function testBlendAlpha(uint8 foregroundAlpha, uint8 backgroundAlpha) public pure returns (uint8) { return blendAlpha(foregroundAlpha, backgroundAlpha); } }
//SPDX-License-Identifier: MIT pragma solidity ^0.8.20; import {ENSReverseClaimer} from '../ENSReverseClaimer.sol'; import {KeepAlive} from '../KeepAlive.sol'; /** * @title TestENSReverseClaimer * @dev This contract is test contract to test functionality for * ENSReverseClaimer. */ contract TestENSReverseClaimer is KeepAlive, ENSReverseClaimer { constructor(address owner) { transferOwnership(owner); } }
// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity ^0.8.20; import {ERC721A, ERC721AQueryable, IERC721A} from 'erc721a/contracts/extensions/ERC721AQueryable.sol'; import {Strings} from '@openzeppelin/contracts/utils/Strings.sol'; import {Base64} from '../../dependencies/Base64.sol'; import {ImageEncoder} from '../ImageEncoder.sol'; /** * @title TestSVGRenderer * @dev This contract is used for testing how websites render SVGs differently. * * Every token is rendered with a different "image" element where the SVG has * different formatting, attributes, workarounds, foreign objects, etc. * * This allows us to mint a bunch of tokens at once in the constructor and then * test how different websites render the SVGs on a testnet. */ contract TestSVGRenderer is ImageEncoder, ERC721AQueryable { using Strings for uint256; // SVG 1: Testing a simple SVG rendering with specific viewBox and fill color string private constant svg1 = '<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">\n' ' <rect width="100" height="100" fill="#FF0000" />\n' '</svg>'; // SVG 2: Similar to SVG 1, but with an overlapping green rectangle string private constant svg2 = '<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">\n' ' <rect width="100" height="100" fill="#FF0000" />\n' ' <rect width="10" height="10" fill="#00FF00" />\n' '</svg>'; // SVG 3: black border string private constant svg3 = '<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">\n' ' <rect x="0" y="0" width="100" height="100" fill="none" stroke="#FF0000" />\n' '</svg>'; // SVG 4: Testing overlapping circles with alpha transparency (Venn diagram) with border string private constant svg4 = '<svg viewBox="0 0 120 120" xmlns="http://www.w3.org/2000/svg">\n' ' <rect x="0" y="0" width="120" height="120" fill="none" stroke="#FF0000" />\n' ' <circle cx="40" cy="60" r="30" fill="rgba(255, 0, 0, 0.5)" />\n' ' <circle cx="80" cy="60" r="30" fill="rgba(0, 255, 0, 0.5)" />\n' ' <circle cx="60" cy="40" r="30" fill="rgba(0, 0, 255, 0.5)" />\n' '</svg>'; string private constant url = 'https://raw.githubusercontent.com/stoooops/explore/master/image_rgba_32x32_cyan_checkerboard.png'; ///////////////////////////////////////////////////////////////////////////// // Constructor ///////////////////////////////////////////////////////////////////////////// constructor() ERC721A('SVG Test', 'SVG') { // mint 100 tokens // _mintBatchTo(msg.sender, 100); } ///////////////////////////////////////////////////////////////////////////// // ERC721Metadata ///////////////////////////////////////////////////////////////////////////// function tokenURI(uint tokenId) public view override(ERC721A, IERC721A) returns (string memory) { if (!_exists(tokenId)) revert URIQueryForNonexistentToken(); return string( abi.encodePacked( 'data:application/json;base64,', Base64.encode( abi.encodePacked( '{"description":"TestSVGRenderer is a test contract for testing how base64-encoded SVGs are rendered on NFT marketplace websites.","image":"', getImageURI(tokenId), '","attributes":[{"trait_type":"Token ID","value":', tokenId.toString(), ',"display_type":"number"}]}' ) ) ) ); } ///////////////////////////////////////////////////////////////////////////// // Art ///////////////////////////////////////////////////////////////////////////// function packSVG(string memory svg) internal pure returns (bytes memory) { return abi.encodePacked('data:image/svg+xml;base64,', Base64.encode(abi.encodePacked(svg))); } function packPNG(bytes memory png) internal pure returns (bytes memory) { return abi.encodePacked('data:image/png;base64,', Base64.encode(png)); } function getImageURI(uint tokenId) internal pure returns (bytes memory) { uint width = 32; uint height = 32; uint scaleWidth = 128; uint scaleHeight = 128; bytes memory checkerboard = fill(width, height, 0, 0, 255, 255); bytes memory imageUri; if (tokenId == 1) { imageUri = packSVG(svg1); } else if (tokenId == 2) { imageUri = packSVG(svg2); } else if (tokenId == 3) { imageUri = packSVG(svg3); } else if (tokenId == 4) { imageUri = packSVG(svg4); } else if (tokenId == 5) { imageUri = packPNG(encodePNG(checkerboard, width, height, true)); } else if (tokenId == 6) { imageUri = packSVG(encodePNGBase64ImageSVG(checkerboard, width, height, true, scaleWidth, scaleHeight, false)); } else if (tokenId == 7) { imageUri = packSVG(encodePNGBase64ImageSVG(checkerboard, width, height, true, scaleWidth, scaleHeight, true)); } else if (tokenId == 8) { imageUri = packSVG(encodePNGBase64ForeignObjectSVG(checkerboard, width, height, true, scaleWidth, scaleHeight)); } else if (tokenId == 9) { imageUri = packSVG(string(encodeSVG(checkerboard, width, height, true, scaleWidth, scaleHeight))); } else if (tokenId == 10) { imageUri = packSVG(encodeImageURLForeignObjectSVG(url, scaleWidth, scaleHeight)); } else { // do nothing } return imageUri; } function fill( uint width, uint height, uint8 red, uint8 green, uint8 blue, uint8 alpha ) internal pure returns (bytes memory) { bytes memory result = new bytes(width * height * 4); for (uint i = 0; i < height; i++) { for (uint j = 0; j < width; j++) { uint offset = (i * width + j) * 4; // color the pixel if its row number and column number add up to an even number if ((i + j) % 2 == 0) { result[offset] = bytes1(red); result[offset + 1] = bytes1(green); result[offset + 2] = bytes1(blue); result[offset + 3] = bytes1(alpha); } } } return result; } // Helper function to create SVG start tag function startSVG(uint svgWidth, uint svgHeight, bool isForeignObject) internal pure returns (bytes memory) { string memory svgWidthStr = Strings.toString(svgWidth); string memory svgHeightStr = Strings.toString(svgHeight); if (isForeignObject) { return abi.encodePacked( '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ', svgWidthStr, ' ', svgHeightStr, '">\n' ); } else { return abi.encodePacked( '<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 ', svgWidthStr, ' ', svgHeightStr, '">\n' ); } } // Helper function to close SVG function closeSVG() internal pure returns (bytes memory) { return '</svg>'; } // Helper function to add a border to SVG function addBorder(uint svgWidth, uint svgHeight) internal pure returns (bytes memory) { string memory svgWidthStr = Strings.toString(svgWidth); string memory svgHeightStr = Strings.toString(svgHeight); return abi.encodePacked( '\t<rect x="0" y="0" width="', svgWidthStr, '" height="', svgHeightStr, '" fill="none" stroke="#FF0000" />\n' ); } function encodePNGBase64ImageSVG( bytes memory data, uint width, uint height, bool alpha, uint svgWidth, uint svgHeight, bool usePixelatedRendering ) public pure returns (string memory) { bytes memory base64PNG = encodeBase64PNG(data, width, height, alpha); bytes memory result = startSVG(svgWidth, svgHeight, false); // result = abi.encodePacked(result, addBorder(svgWidth, svgHeight)); string memory imageRendering = usePixelatedRendering ? ' style="image-rendering: pixelated;"' : ''; result = abi.encodePacked( result, '\t<image x="0" y="0" width="', Strings.toString(svgWidth), '" height="', Strings.toString(svgHeight), '" preserveAspectRatio="none" xlink:href="data:image/png;base64,', base64PNG, '"', imageRendering, '/>\n', closeSVG() ); return string(result); } function encodePNGBase64ForeignObjectSVG( bytes memory data, uint width, uint height, bool alpha, uint svgWidth, uint svgHeight ) public pure returns (string memory) { bytes memory base64PNG = encodeBase64PNG(data, width, height, alpha); bytes memory result = startSVG(svgWidth, svgHeight, true); // result = abi.encodePacked(result, addBorder(svgWidth, svgHeight)); result = abi.encodePacked( result, '\t<foreignObject width="', Strings.toString(svgWidth), '" height="', Strings.toString(svgHeight), '">\n' ); result = abi.encodePacked( result, '\t\t<img xmlns="http://www.w3.org/1999/xhtml" width="', Strings.toString(svgWidth), '" height="', Strings.toString(svgHeight), '" style="image-rendering: pixelated;" src="data:image/png;base64,', base64PNG, '"/>\n' ); result = abi.encodePacked(result, '\t</foreignObject>\n', closeSVG()); return string(result); } function encodeImageURLForeignObjectSVG( string memory imageURL, uint svgWidth, uint svgHeight ) public pure returns (string memory) { bytes memory result = startSVG(svgWidth, svgHeight, true); // result = abi.encodePacked(result, addBorder(svgWidth, svgHeight)); result = abi.encodePacked( result, '\t<foreignObject width="', Strings.toString(svgWidth), '" height="', Strings.toString(svgHeight), '">\n\t\t<img xmlns="http://www.w3.org/1999/xhtml" width="', Strings.toString(svgWidth), '" height="', Strings.toString(svgHeight), '" style="image-rendering: pixelated;" src="', bytes(imageURL), '"/>\n\t</foreignObject>\n', closeSVG() ); return string(result); } ///////////////////////////////////////////////////////////////////////////// // Mint ///////////////////////////////////////////////////////////////////////////// function _mintTo(address to) internal { _safeMint(to, 1); } function mint() external payable { _mintTo(msg.sender); } function mintTo(address to) external payable { _mintTo(to); } function _mintBatchTo(address to, uint n) internal { _safeMint(to, n); } function mintBatch(uint n) external payable { _mintBatchTo(msg.sender, n); } function mintBatchTo(address to, uint n) external payable { _mintBatchTo(to, n); } }
// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity ^0.8.20; import {Ownable} from '@openzeppelin/contracts/access/Ownable.sol'; import {IERC165} from '@openzeppelin/contracts/utils/introspection/IERC165.sol'; import {IOpenAvatarGen0AssetsPaletteStoreRead} from '../interfaces/assets/IOpenAvatarGen0AssetsPaletteStore.sol'; import {OptionalPatternHeader, PatternHeader} from '../interfaces/assets/IOpenAvatarGen0AssetsPatternStore.sol'; import {IOpenAvatarGen0AssetsCanvasLayerCompositor, LayerPatternPalette} from '../interfaces/render/IOpenAvatarGen0AssetsCanvasLayerCompositor.sol'; import {KeepAlive} from '../lib/KeepAlive.sol'; import {PixelBlender} from '../lib/PixelBlender.sol'; import {IOpenAvatarGen0AssetsRead} from '../../IOpenAvatarGen0Assets.sol'; /** * @title OpenAvatarGen0AssetsCanvasLayerCompositor * @dev This contract composes layer patterns into a single image. * @dev A pattern is a 2d byte array with a corresponding color palette. * @dev The values of the pattern array are indexes into the color palette. */ contract OpenAvatarGen0AssetsCanvasLayerCompositor is KeepAlive, PixelBlender, IOpenAvatarGen0AssetsCanvasLayerCompositor { /// @dev Error reverted when the component is already initialized. error AlreadyInitialized(); /// @dev Error reverted when provided address does not support the required interface. error InterfaceUnsupported(address contractAddress, bytes4 interfaceId); /// @dev Error reverted when the canvas bytes per pixel is not 4. error InvalidCanvasBytesPerPixel(); /// @dev Error reverted when the canvas size is invalid. error InvalidCanvasSize(uint8 canvasId, uint invalidNumBytes); /// @dev Error reverted when the mask length is invalid. error InvalidMaskLength(uint maskLength, uint canvasSize); /// @dev The transparent alpha value. bytes1 public constant TRANSPARENT = 0x00; /// @dev The opaque alpha value. bytes1 public constant OPAQUE = 0xff; /// @dev The IOpenAvatarGen0AssetsRead public openAvatarGen0AssetsRead; /// @dev The ERC-165 interface id for the OpenAvatarGen0AssetsCanvasLayerCompositor (for clients). bytes4 private constant INTERFACE_ID_OPENAVATAR_GEN0_ASSETS_CANVAS_LAYER_COMPOSITOR = 0x2638c94b; /// @dev The ERC-165 interface id for the OpenAvatarGen0AssetsRead (dependency). bytes4 private constant INTERFACE_ID_OPENAVATAR_GEN0_ASSETS_READ = 0x67bf31d1; constructor(address ownerProxy) { // will be deployed by ImmutableCreate2Factory and then transferred to the // configured owner. // using a proxy allows for using same constructor args and thus same // bytecode for all instances of this contract. address wantOwner = Ownable(ownerProxy).owner(); if (owner() != wantOwner) { transferOwnership(wantOwner); } } ///////////////////////////////////////////////////////////////////////////// // ERC-165: Standard Interface Detection ///////////////////////////////////////////////////////////////////////////// /** * @notice Checks if the contract supports an interface. * @param interfaceId The interface identifier, as specified in ERC-165. * @return True if the contract supports interfaceID, false otherwise. */ function supportsInterface(bytes4 interfaceId) public pure virtual returns (bool) { return interfaceId == 0x01ffc9a7 || // ERC165 interface ID for ERC165. interfaceId == INTERFACE_ID_OPENAVATAR_GEN0_ASSETS_CANVAS_LAYER_COMPOSITOR; } ///////////////////////////////////////////////////////////////////////////// // Initialize Dependencies ///////////////////////////////////////////////////////////////////////////// /** * @dev Initialize the contract. * @param openAvatarGen0Assets_ The address of the asset store read interface * contract. */ function initialize(address openAvatarGen0Assets_) external onlyOwner { setOpenAvatarGen0Assets(openAvatarGen0Assets_); } /** * @notice Check if the contract has been initialized. * @return True if the contract has been initialized, false otherwise. */ function isInitialized() external view returns (bool) { return address(openAvatarGen0AssetsRead) != address(0); } /** * @dev Get the OpenAvatarGen0Assets address. * @return The OpenAvatarGen0Assets address. */ function getOpenAvatarGen0Assets() external view returns (address) { return address(openAvatarGen0AssetsRead); } /** * @notice Set the asset store. * @param openAvatarGen0Assets_ The address of the asset store read interface * contract. */ function setOpenAvatarGen0Assets(address openAvatarGen0Assets_) internal { // only set once if (address(openAvatarGen0AssetsRead) != address(0)) revert AlreadyInitialized(); // check ERC-165 support // only read interface is required if (!IERC165(openAvatarGen0Assets_).supportsInterface(INTERFACE_ID_OPENAVATAR_GEN0_ASSETS_READ)) { revert InterfaceUnsupported(openAvatarGen0Assets_, INTERFACE_ID_OPENAVATAR_GEN0_ASSETS_READ); } // set openAvatarGen0AssetsRead = IOpenAvatarGen0AssetsRead(openAvatarGen0Assets_); // sanity check if (openAvatarGen0AssetsRead.getBytesPerPixel() != 4) revert InvalidCanvasBytesPerPixel(); } ///////////////////////////////////////////////////////////////////////////// // Layer Composition ///////////////////////////////////////////////////////////////////////////// /** * @dev Create a layer composition. * @param canvasId The id of the canvas to use * @param layerPatternPalette The layer, pattern, and palette to use for each * layer */ function createLayerComposition( uint8 canvasId, LayerPatternPalette[] memory layerPatternPalette ) external view override returns (bytes memory) { bytes memory out = new bytes(openAvatarGen0AssetsRead.getCanvasNumBytes(canvasId)); _drawLayerComposition(out, canvasId, layerPatternPalette); return out; } /** * @dev Draw a layer composition onto the image * @param out The image to draw the layer composition onto * @param canvasId The id of the canvas to use * @param layerPatternPalette The layer, pattern, and palette to use for each * layer */ function drawLayerComposition( bytes memory out, uint8 canvasId, LayerPatternPalette[] memory layerPatternPalette ) public view override returns (bytes memory) { _drawLayerComposition(out, canvasId, layerPatternPalette); return out; } /** * @dev Draw a layer composition onto the image * @param out The image to draw the layer composition onto * @param canvasId The id of the canvas to use * @param layerPatternPalette The layer, pattern, and palette to use for each * layer */ function _drawLayerComposition( bytes memory out, uint8 canvasId, LayerPatternPalette[] memory layerPatternPalette ) internal view { // sanity check so we don't out of bounds for bad input if (out.length != openAvatarGen0AssetsRead.getCanvasNumBytes(canvasId)) { revert InvalidCanvasSize(canvasId, out.length); } uint length = layerPatternPalette.length; for (uint i = 0; i < length; ) { LayerPatternPalette memory lpp = layerPatternPalette[i]; _drawLayer(out, canvasId, lpp.layer, lpp.pattern, lpp.palette); unchecked { ++i; } } } /** * @dev Overlay a layer on top of the image * @param image The image to overlay the layer on top of * @param canvasId The id of the canvas to use * @param layer The index of the layer to overlay * @param pattern The index of the pattern to use for the layer * @param palette The index of the palette to use for the layer/pattern */ function drawLayer( bytes memory image, uint8 canvasId, uint8 layer, uint8 pattern, uint8 palette ) public view override returns (bytes memory) { _drawLayer(image, canvasId, layer, pattern, palette); return image; } /** * @dev Overlay a layer on top of the image * @param image The image to overlay the layer on top of * @param canvasId The id of the canvas to use * @param layer The index of the layer to overlay * @param pattern The index of the pattern to use for the layer * @param palette The index of the palette to use for the layer/pattern */ function _drawLayer(bytes memory image, uint8 canvasId, uint8 layer, uint8 pattern, uint8 palette) internal view { OptionalPatternHeader memory optionalHeader = openAvatarGen0AssetsRead.getPatternHeader(canvasId, layer, pattern); // gracefully handle missing pattern if (optionalHeader.exists) { bytes memory patternData = openAvatarGen0AssetsRead.getPatternData(canvasId, layer, pattern); // transparent patterns will exist and have length 0 if (patternData.length > 0) { bytes4[] memory paletteData = openAvatarGen0AssetsRead.getPalette(optionalHeader.header.paletteCode, palette); // gracefully handle missing palette if (paletteData.length > 0) { _drawPattern(image, canvasId, optionalHeader.header, patternData, paletteData); } } } } /** * @dev Overlay a layer on top of the image * @param image The image to overlay the layer on top of * @param mask The mask to apply to the pattern before overlaying it * @param canvasId The id of the canvas to use * @param layer The index of the layer to overlay * @param pattern The index of the pattern to use for the layer * @param palette The index of the palette to use for the layer/pattern */ function drawMaskedLayer( bytes memory image, bytes memory mask, uint8 canvasId, uint8 layer, uint8 pattern, uint8 palette ) public view override returns (bytes memory) { _drawMaskedLayer(image, mask, canvasId, layer, pattern, palette); return image; } /** * @dev Overlay a layer on top of the image * @param image The image to overlay the layer on top of * @param mask The mask to apply to the pattern before overlaying it * @param canvasId The id of the canvas to use * @param layer The index of the layer to overlay * @param pattern The index of the pattern to use for the layer * @param palette The index of the palette to use for the layer/pattern */ function _drawMaskedLayer( bytes memory image, bytes memory mask, uint8 canvasId, uint8 layer, uint8 pattern, uint8 palette ) internal view { OptionalPatternHeader memory optionalHeader = openAvatarGen0AssetsRead.getPatternHeader(canvasId, layer, pattern); // gracefully handle missing pattern if (optionalHeader.exists) { bytes memory patternData = openAvatarGen0AssetsRead.getPatternData(canvasId, layer, pattern); // transparent patterns will exist and have length 0 if (patternData.length > 0) { bytes4[] memory paletteData = openAvatarGen0AssetsRead.getPalette(optionalHeader.header.paletteCode, palette); // gracefully handle missing palette if (paletteData.length > 0) { _drawMaskedPattern(image, mask, canvasId, optionalHeader.header, patternData, paletteData); } } } } /** * @dev Overlay a pattern on top of the image * @param image The image to overlay the layer on top of * @param header The header of the pattern to use for the layer * @param pattern The pattern to use for the layer * @param palette The palette to use for the layer/pattern */ function drawPattern( bytes memory image, uint8 canvasId, PatternHeader memory header, bytes memory pattern, bytes4[] memory palette ) public view returns (bytes memory) { _drawPattern(image, canvasId, header, pattern, palette); return image; } /** * @dev Overlay a pattern on top of the image * @param image The image to overlay the layer on top of * @param header The header of the pattern to use for the layer * @param pattern The pattern to use for the layer * @param palette The palette to use for the layer/pattern */ function _drawPattern( bytes memory image, uint8 canvasId, PatternHeader memory header, bytes memory pattern, bytes4[] memory palette ) internal view { _drawMaskedPattern( image, // this is wasteful new bytes(openAvatarGen0AssetsRead.getCanvasNumPixels(canvasId)), canvasId, header, pattern, palette ); } /** * @dev Overlay a pattern on top of the image * @param image The image to overlay the layer on top of * @param header The header of the pattern to use for the layer * @param pattern The pattern to use for the layer * @param palette The palette to use for the layer/pattern */ function drawMaskedPattern( bytes memory image, bytes memory mask, uint8 canvasId, PatternHeader memory header, bytes memory pattern, bytes4[] memory palette ) public view returns (bytes memory) { _drawMaskedPattern(image, mask, canvasId, header, pattern, palette); return image; } /** * @dev Overlay a pattern on top of the image, applying a mask * @param image The image to overlay the layer on top of * @param mask The mask to apply to the pattern before overlaying it * @param header The header of the pattern to use for the layer * @param pattern The pattern to use for the layer * @param palette The palette to use for the layer/pattern */ function _drawMaskedPattern( bytes memory image, bytes memory mask, uint8 canvasId, PatternHeader memory header, bytes memory pattern, bytes4[] memory palette ) internal view { unchecked { uint8 canvasWidth = openAvatarGen0AssetsRead.getCanvasWidth(canvasId); // loop through pixels in the image for (uint y = 0; y < header.height; ) { uint colY = y + header.offsetY; for (uint x = 0; x < header.width; ) { uint rowX = x + header.offsetX; uint imagePixel = colY * canvasWidth + rowX; if (imagePixel < mask.length && mask[imagePixel] != 0) { // skip transparent pixels ++x; continue; } // calculate the offset of the pixel in the pattern // get the color index from the pattern uint8 colorIndex = uint8(pattern[y * header.width + x]); // colorIndex == 0 means transparent if (colorIndex > 0) { // get the color from the palette bytes4 rgba = palette[colorIndex]; // calculate the offset of the pixel in the image uint offset = 4 * imagePixel; if (rgba[3] == OPAQUE) { image[offset] = rgba[0]; image[offset + 1] = rgba[1]; image[offset + 2] = rgba[2]; image[offset + 3] = OPAQUE; // solhint-disable-next-line no-empty-blocks } else if (rgba[3] == TRANSPARENT) { // do nothing } else { // blend the pixel with the existing pixel // again there are two subcases based on whether the existing // pixel is transparent or not if (image[offset + 3] == TRANSPARENT) { // CASE 1: existing pixel is transparent // so just copy the pixel exactly // including the semi-transparent alpha value image[offset] = rgba[0]; image[offset + 1] = rgba[1]; image[offset + 2] = rgba[2]; image[offset + 3] = rgba[3]; } else { // CASE 2: existing pixel is not transparent // we need to blend image[offset] = bytes1(blendPixel(uint8(rgba[0]), uint8(image[offset]), uint8(rgba[3]))); image[offset + 1] = bytes1(blendPixel(uint8(rgba[1]), uint8(image[offset + 1]), uint8(rgba[3]))); image[offset + 2] = bytes1(blendPixel(uint8(rgba[2]), uint8(image[offset + 2]), uint8(rgba[3]))); image[offset + 3] = bytes1(blendAlpha(uint8(rgba[3]), uint8(image[offset + 3]))); } } } ++x; } ++y; } } } }
// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity ^0.8.20; import {Ownable} from '@openzeppelin/contracts/access/Ownable.sol'; import {IOpenAvatarGen0RendererDecorator} from '../../IOpenAvatarGen0Renderer.sol'; import {OpenAvatarGen0AssetsCanvasIdStore} from '../assets/OpenAvatarGen0AssetsCanvasIdStore.sol'; import {IOpenAvatarGen0AssetsCanvasLayerCompositor, LayerPatternPalette} from '../interfaces/render/IOpenAvatarGen0AssetsCanvasLayerCompositor.sol'; import {IOpenAvatarGen0CanvasRenderer} from '../interfaces/render/IOpenAvatarGen0CanvasRenderer.sol'; import {ImageEncoder} from '../lib/ImageEncoder.sol'; import {DNA} from '../lib/DNA.sol'; import {OpenAvatarGen0AssetsCanvasLayerCompositor} from './OpenAvatarGen0AssetsCanvasLayerCompositor.sol'; /** * @title OpenAvatarGen0CanvasRenderer * @dev This contract renders a DNA as an image in a variety of formats. */ contract OpenAvatarGen0CanvasRenderer is IOpenAvatarGen0CanvasRenderer, IOpenAvatarGen0RendererDecorator, ImageEncoder, OpenAvatarGen0AssetsCanvasIdStore, OpenAvatarGen0AssetsCanvasLayerCompositor { using DNA for bytes32; /// @dev The layer index for the body layer. uint8 public constant LAYER_INDEX_BODY = 10; /// @dev The layer index for the tattoos layer. uint8 public constant LAYER_INDEX_TATTOOS = 20; /// @dev The layer index for the makeup layer. uint8 public constant LAYER_INDEX_MAKEUP = 30; /// @dev The layer index for the left eye layer. uint8 public constant LAYER_INDEX_LEFT_EYE = 40; /// @dev The layer index for the right eye layer. uint8 public constant LAYER_INDEX_RIGHT_EYE = 50; /// @dev The layer index for the bottomwear layer. uint8 public constant LAYER_INDEX_BOTTOMWEAR = 60; /// @dev The layer index for the footwear layer. uint8 public constant LAYER_INDEX_FOOTWEAR = 70; /// @dev The layer index for the topwear layer. uint8 public constant LAYER_INDEX_TOPWEAR = 80; /// @dev The layer index for the handwear layer. uint8 public constant LAYER_INDEX_HANDWEAR = 90; /// @dev The layer index for the outerwear layer. uint8 public constant LAYER_INDEX_OUTERWEAR = 100; /// @dev The layer index for the jewelry layer. uint8 public constant LAYER_INDEX_JEWELRY = 110; /// @dev The layer index for the facial hair layer. uint8 public constant LAYER_INDEX_FACIAL_HAIR = 120; /// @dev The layer index for the facewear layer. uint8 public constant LAYER_INDEX_FACEWEAR = 130; /// @dev The layer index for the eyewear layer. uint8 public constant LAYER_INDEX_EYEWEAR = 140; /// @dev The layer index for the hair layer. uint8 public constant LAYER_INDEX_HAIR = 150; /// @dev scale the base PNG to an SVG by this factor uint public constant SVG_SCALE = 10; constructor( address ownerProxy, uint8 canvasId_ ) OpenAvatarGen0AssetsCanvasIdStore(canvasId_) OpenAvatarGen0AssetsCanvasLayerCompositor(ownerProxy) // solhint-disable-next-line no-empty-blocks { } ///////////////////////////////////////////////////////////////////////////// // ERC-165: Standard Interface Detection ///////////////////////////////////////////////////////////////////////////// /** * @notice Checks if the contract supports an interface. * @param interfaceId The interface identifier, as specified in ERC-165. * @return True if the contract supports interfaceID, false otherwise. */ function supportsInterface( bytes4 interfaceId ) public pure virtual override(OpenAvatarGen0AssetsCanvasLayerCompositor) returns (bool) { return interfaceId == 0x01ffc9a7 || // ERC165 interface ID for ERC165. // IOpenAvatarGen0AssetsCanvasLayerCompositor interfaceId == 0xb93e4881 || // ERC165 interface ID for IOpenAvatarGen0Renderer. interfaceId == 0x00a663b1 || // ERC165 interface ID for IOpenAvatarGen0RendererDecorator. interfaceId == 0x13247985 || // ERC165 interface ID for IOpenAvatarGen0CanvasRenderer. interfaceId == 0x2638c94b; // ERC165 interface ID for IOpenAvatarGen0AssetsCanvasLayerCompositor. } ///////////////////////////////////////////////////////////////////////////// // Drawing ///////////////////////////////////////////////////////////////////////////// function drawOpenAvatar(uint8 canvasId, bytes32 dna) external view returns (bytes memory) { return _drawOpenAvatar(canvasId, dna); } function _drawOpenAvatar(uint8 canvasId, bytes32 dna) internal view returns (bytes memory) { bytes memory image = new bytes(openAvatarGen0AssetsRead.getCanvasNumBytes(canvasId)); return _drawOpenAvatarOverlay(image, canvasId, dna); } function drawOpenAvatarOverlay(bytes memory image, uint8 canvasId, bytes32 dna) external view returns (bytes memory) { return _drawOpenAvatarOverlay(image, canvasId, dna); } /** * @notice Compose the given DNA into a single image, on top of the given * base image. * @param image The base image. * @param canvasId The canvas ID. Between [0, 11]. Behavior for 12 or * higher undefined. * @param dna The DNA to compose. * @return The image. */ function _drawOpenAvatarOverlay( bytes memory image, uint8 canvasId, bytes32 dna ) internal view returns (bytes memory) { uint expectedNumBytes = openAvatarGen0AssetsRead.getCanvasNumBytes(canvasId); // sanity check the provided image array // must be at least as long as the expected image length if (image.length < expectedNumBytes) { revert InvalidCanvasSize(canvasId, image.length); } /** * A 32 byte hex string * @dev The DNA string is a 32 byte hex string. * @dev The DNA string is immutable. * @dev The bytes represent the following: * The bytes represent the following: * ZZZZ YYYY XXXX WWWW VVVV UUUU TTTT SSSS * 0000 0000 0000 0000 0000 0000 0000 0000 * * Bytes | Chars | Description * ---------|---------|------------- * [0:1] | [0:3] | body * [2:3] | [4:7] | tattoos * [4:5] | [8:11] | makeup * [6:7] | [12:15] | left eye * [8:9] | [16:19] | right eye * [10:11] | [20:23] | bottomwear * [12:13] | [24:27] | footwear * [14:15] | [28:31] | topwear * [16:17] | [32:35] | handwear * [18:19] | [36:39] | outerwear * [20:21] | [40:43] | jewelry * [22:23] | [44:47] | facial hair * [24:25] | [48:51] | facewear * [26:27] | [52:55] | eyewear * [28:29] | [56:59] | hair * [30:31] | [60:63] | reserved * * Each 2-byte section is a struct of the following: * [0] | [0:1] | pattern * [1] | [2:3] | palette */ LayerPatternPalette[] memory layers = new LayerPatternPalette[](15); layers[0] = LayerPatternPalette(LAYER_INDEX_BODY, dna.bodyPattern(), dna.bodyPalette()); layers[1] = LayerPatternPalette(LAYER_INDEX_TATTOOS, dna.tattoosPattern(), dna.tattoosPalette()); layers[2] = LayerPatternPalette(LAYER_INDEX_MAKEUP, dna.makeupPattern(), dna.makeupPalette()); layers[3] = LayerPatternPalette(LAYER_INDEX_LEFT_EYE, dna.leftEyePattern(), dna.leftEyePalette()); layers[4] = LayerPatternPalette(LAYER_INDEX_RIGHT_EYE, dna.rightEyePattern(), dna.rightEyePalette()); layers[5] = LayerPatternPalette(LAYER_INDEX_BOTTOMWEAR, dna.bottomwearPattern(), dna.bottomwearPalette()); layers[6] = LayerPatternPalette(LAYER_INDEX_FOOTWEAR, dna.footwearPattern(), dna.footwearPalette()); layers[7] = LayerPatternPalette(LAYER_INDEX_TOPWEAR, dna.topwearPattern(), dna.topwearPalette()); layers[8] = LayerPatternPalette(LAYER_INDEX_OUTERWEAR, dna.outerwearPattern(), dna.outerwearPalette()); layers[9] = LayerPatternPalette(LAYER_INDEX_HANDWEAR, dna.handwearPattern(), dna.handwearPalette()); layers[10] = LayerPatternPalette(LAYER_INDEX_JEWELRY, dna.jewelryPattern(), dna.jewelryPalette()); layers[11] = LayerPatternPalette(LAYER_INDEX_FACIAL_HAIR, dna.facialHairPattern(), dna.facialHairPalette()); layers[12] = LayerPatternPalette(LAYER_INDEX_FACEWEAR, dna.facewearPattern(), dna.facewearPalette()); layers[13] = LayerPatternPalette(LAYER_INDEX_EYEWEAR, dna.eyewearPattern(), dna.eyewearPalette()); layers[14] = LayerPatternPalette(LAYER_INDEX_HAIR, dna.hairPattern(), dna.hairPalette()); return drawLayerComposition(image, canvasId, layers); } /** * @notice Render the given DNA as a base64-encoded SVG URI. * @param dna The DNA to render. * @return The SVG URI. */ function renderURI(bytes32 dna) public view override returns (string memory) { return string(abi.encodePacked('data:image/svg+xml;base64,', renderBase64SVG(dna))); } /** * @notice Render the dna as a byte array. * @param dna The DNA to render. * @return The byte array of the image. */ function renderHex(bytes32 dna) public view override returns (bytes memory) { return _drawOpenAvatar(canvasId, dna); } /** * @notice Render the given DNA as a PNG. * @param dna The DNA to render. * @return The PNG image. */ function renderPNG(bytes32 dna) public view override returns (bytes memory) { return encodePNG( _drawOpenAvatar(canvasId, dna), openAvatarGen0AssetsRead.getCanvasWidth(canvasId), openAvatarGen0AssetsRead.getCanvasHeight(canvasId), openAvatarGen0AssetsRead.hasAlphaChannel() ); } /** * @notice Render the given DNA as a base64-encoded PNG. * @param dna The DNA to render. * @return The PNG image. */ function renderBase64PNG(bytes32 dna) public view override returns (bytes memory) { return encodeBase64PNG( _drawOpenAvatar(canvasId, dna), openAvatarGen0AssetsRead.getCanvasWidth(canvasId), openAvatarGen0AssetsRead.getCanvasHeight(canvasId), openAvatarGen0AssetsRead.hasAlphaChannel() ); } function renderSVG(bytes32 dna) public view override returns (bytes memory) { uint width = openAvatarGen0AssetsRead.getCanvasWidth(canvasId); uint height = openAvatarGen0AssetsRead.getCanvasHeight(canvasId); return encodeSVG( _drawOpenAvatar(canvasId, dna), width, height, openAvatarGen0AssetsRead.hasAlphaChannel(), SVG_SCALE * width, SVG_SCALE * height ); } function renderBase64SVG(bytes32 dna) public view override returns (bytes memory) { uint width = openAvatarGen0AssetsRead.getCanvasWidth(canvasId); uint height = openAvatarGen0AssetsRead.getCanvasHeight(canvasId); return encodeBase64SVG( _drawOpenAvatar(canvasId, dna), width, height, openAvatarGen0AssetsRead.hasAlphaChannel(), SVG_SCALE * width, SVG_SCALE * height ); } }
// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity ^0.8.20; import {OpenAvatarGen0Renderer} from '../../OpenAvatarGen0Renderer.sol'; /** * @title OpenAvatarGen0ExampleMutableCanvasRenderer * @dev This contract renders a DNA as an image in a variety of formats, * and allows for mutating the canvas id by the contract owner. */ contract OpenAvatarGen0ExampleMutableCanvasRenderer is OpenAvatarGen0Renderer { error CanvasDoesNotExist(uint8 canvasId); // solhint-disable-next-line no-empty-blocks constructor(address ownerProxy) OpenAvatarGen0Renderer(ownerProxy) {} /** * @notice Set the canvas ID. * @param newCanvasId The new canvas ID. */ function setCanvasId(uint8 newCanvasId) external onlyOwner { if (!openAvatarGen0AssetsRead.hasCanvas(newCanvasId)) revert CanvasDoesNotExist(newCanvasId); canvasId = newCanvasId; } }
// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity ^0.8.20; import {Ownable} from '@openzeppelin/contracts/access/Ownable.sol'; import {IERC165} from '@openzeppelin/contracts/utils/introspection/IERC165.sol'; import {OpenAvatarGen0AssetsCanvasIdStore} from '../core/assets/OpenAvatarGen0AssetsCanvasIdStore.sol'; import {IOpenAvatarGen0CanvasRenderer} from '../core/interfaces/render/IOpenAvatarGen0CanvasRenderer.sol'; import {ENSReverseClaimer} from '../core/lib/ENSReverseClaimer.sol'; import {ImageEncoder} from '../core/lib/ImageEncoder.sol'; import {KeepAlive} from '../core/lib/KeepAlive.sol'; import {IOpenAvatarGen0AssetsRead} from '../IOpenAvatarGen0Assets.sol'; import {IOpenAvatarGen0Renderer} from '../IOpenAvatarGen0Renderer.sol'; import {IOpenAvatarGen0TextRecords} from '../IOpenAvatarGen0TextRecords.sol'; import {IOpenAvatarGen0TokenDNA} from '../IOpenAvatarGen0Token.sol'; import {OpenAvatarGenerationZero} from '../OpenAvatarGenerationZero.sol'; import {DNA} from '../core/lib/DNA.sol'; import {IOpenAvatarGen0AssetsCanvasLayerCompositor, LayerPatternPalette} from '../core/interfaces/render/IOpenAvatarGen0AssetsCanvasLayerCompositor.sol'; struct OpenAvatarProfilePictureSettings { /// @dev Whether or not to use a solid background color. bool overrideBackground; /// @dev The background color. bytes3 backgroundColor; /// @dev Whether or not to mask below the neck for a "floating head" effect. bool maskBelowTheNeck; } /** * @title OpenAvatarGen0ProfilePictureRenderer * @author Cory Gabrielsen (cory.eth) * * @notice A contract for rendering OpenAvatar Gen0 profile pictures. * @dev This contract renders an Avatar against a background image. * * ---------------------------------------------------------------------------- * 'pfp' Renderer * ---------------------------------------------------------------------------- * A Profile Picture Renderer is provided with the key "pfp". * * The 'pfp' Renderer renders the Avatar with a configurable background color * determined by the 'gen0.renderer.pfp.background-color' text record, which * should be a valid RGB hex color code (e.g. "#ff0000" for red). * * Further, the 'pfp' Renderer provides the option of masking the Avatar * below the neck to create a "floating head" effect. This can be configured * by setting the `gen0.renderer.pfp.mask` text record to "below-the-neck". * * The Profile Picture Renderer is a demonstration of combining the OpenAvatar * building blocks together to create dynamic, owner-customizable behavior. It * utilizes onchain assets, onchain rendering, and onchain text records to * render a customizable Avatar pfp. * * This pattern is permissionless. * * * ---------------------------------------------------------------------------- * Background Image * ---------------------------------------------------------------------------- * The background image is by default a white/light gray checkerboard of 8x8 * squares arranged in a 4x4 grid. * * The background image can be changed by the token owner by setting the * 'gen0.renderer.pfp.background-image' text record to a valid RGB hex color * code (e.g. "#ff0000" for red). */ contract OpenAvatarGen0ProfilePictureRenderer is IOpenAvatarGen0Renderer, OpenAvatarGenerationZero, OpenAvatarGen0AssetsCanvasIdStore, ImageEncoder, ENSReverseClaimer, KeepAlive { using DNA for bytes32; /// @dev Emitted when the fuse is burned to make the background image immutable. event FuseBurnedChangeBackgroundImage(); /// @dev Error when a component is already initialized. error AlreadyInitialized(); /// @dev Error when the fuse is already burned. error FuseBurned(); /// @dev Error when the required ERC-165 interfaces are not supported. error InterfaceUnsupported(address contractAddress, bytes4 interfaceId); /// @dev Error when the provided canvas has an invalid number of bytes per pixel. error InvalidCanvasBytesPerPixel(); /// @dev Error when calling write-protection function on a non-owned DNA. error NotOwner(bytes32 dna); /// @dev The ERC-165 interface id for the OpenAvatarGen0Renderer (for clients). bytes4 private constant INTERFACE_ID_OPENAVATAR_GEN0_RENDERER = 0xb93e4881; /// @dev The ERC-165 interface id for the OpenAvatarGen0AssetsRead (dependency). bytes4 private constant INTERFACE_ID_OPENAVATAR_GEN0_ASSETS_READ = 0x67bf31d1; /// @dev The ERC-165 interface id for the OpenAvatarGen0AssetsCanvasLayerCompositor (dependency). bytes4 private constant INTERFACE_ID_OPENAVATAR_GEN0_ASSETS_CANVAS_LAYER_COMPOSITOR = 0x2638c94b; /// @dev The ERC-165 interface id for the OpenAvatarGen0TokenDNA (dependency). bytes4 private constant INTERFACE_ID_OPENAVATAR_GEN0_TOKEN_DNA = 0x2717336f; /// @dev The ERC-165 interface id for the OpenAvatarGen0TextRecords ERC-634 text() (dependency). bytes4 private constant INTERFACE_ID_OPENAVATAR_GEN0_TEXT = 0x59d1d43c; /// @dev The canvas id. uint8 public constant CANVAS_ID = 0; /// @dev The pixel width of the canvas. uint8 public constant CANVAS_WIDTH = 32; /// @dev The pixel height of the canvas. uint8 public constant CANVAS_HEIGHT = 32; /// @dev The number of bytes in the canvas - 32 * 32 * 4. uint16 public constant CANVAS_NUM_BYTES = 4096; /// @dev The layer index for the body layer. uint8 public constant LAYER_INDEX_BODY = 10; /// @dev The layer index for the tattoos layer. uint8 public constant LAYER_INDEX_TATTOOS = 20; /// @dev The layer index for the makeup layer. uint8 public constant LAYER_INDEX_MAKEUP = 30; /// @dev The layer index for the left eye layer. uint8 public constant LAYER_INDEX_LEFT_EYE = 40; /// @dev The layer index for the right eye layer. uint8 public constant LAYER_INDEX_RIGHT_EYE = 50; /// @dev The layer index for the bottomwear layer. uint8 public constant LAYER_INDEX_BOTTOMWEAR = 60; /// @dev The layer index for the footwear layer. uint8 public constant LAYER_INDEX_FOOTWEAR = 70; /// @dev The layer index for the topwear layer. uint8 public constant LAYER_INDEX_TOPWEAR = 80; /// @dev The layer index for the handwear layer. uint8 public constant LAYER_INDEX_HANDWEAR = 90; /// @dev The layer index for the outerwear layer. uint8 public constant LAYER_INDEX_OUTERWEAR = 100; /// @dev The layer index for the jewelry layer. uint8 public constant LAYER_INDEX_JEWELRY = 110; /// @dev The layer index for the facial hair layer. uint8 public constant LAYER_INDEX_FACIAL_HAIR = 120; /// @dev The layer index for the facewear layer. uint8 public constant LAYER_INDEX_FACEWEAR = 130; /// @dev The layer index for the eyewear layer. uint8 public constant LAYER_INDEX_EYEWEAR = 140; /// @dev The layer index for the hair layer. uint8 public constant LAYER_INDEX_HAIR = 150; /// @dev Scale the SVG by this amount. uint public constant SVG_SCALE = 10; /// @dev The "only head" mask which masks below the neck. bytes public constant BELOW_THE_NECK_MASK = hex'0000000000000000000000000000000000000000000000000000000000000000' hex'0000000000000000000000000000000000000000000000000000000000000000' hex'0000000000000000000000000000000000000000000000000000000000000000' hex'0000000000000000000000000000000000000000000000000000000000000000' hex'0000000000000000000000000000000000000000000000000000000000000000' hex'0000000000000000000000000000000000000000000000000000000000000000' hex'0000000000000000000000000000000000000000000000000000000000000000' hex'0000000000000000000000000000000000000000000000000000000000000000' hex'0000000000000000000000000000000000000000000000000000000000000000' hex'0000000000000000000000000000000000000000000000000000000000000000' hex'0000000000000000000000000000000000000000000000000000000000000000' hex'0000000000000000000000000000000000000000000000000000000000000000' hex'0000000000000000000000000000000000000000000000000000000000000000' hex'0000000000000000000000000000000000000000000000000000000000000000' hex'0000000000000000000000000000000000000000000000000000000000000000' hex'0000000000000000000000000000000000000000000000000000000000000000' hex'0000000000000000000000000000000000000000000000000000000000000000' hex'0000000000000000000000000000000000000000000000000000000000000000' hex'0000000000000000000000000000000000000000000000000000000000000000' hex'0000000000000000000000000000000000000000000000000000000000000000' hex'0000000000000000000000000000000000000000000000000000000000000000' hex'0000000000000000000000000000000000000000000000000000000000000000' hex'0101010101010101010101010000000000000000010101010101010101010101' hex'0101010101010101010101010101010101010101010101010101010101010101' hex'0101010101010101010101010101010101010101010101010101010101010101' hex'0101010101010101010101010101010101010101010101010101010101010101' hex'0101010101010101010101010101010101010101010101010101010101010101' hex'0101010101010101010101010101010101010101010101010101010101010101' hex'0101010101010101010101010101010101010101010101010101010101010101' hex'0101010101010101010101010101010101010101010101010101010101010101' hex'0101010101010101010101010101010101010101010101010101010101010101' hex'0101010101010101010101010101010101010101010101010101010101010101'; /// @dev The background image, an 4x4 grid of 8x8 checkerboard squares. /// Every four lines is one row of the image. /// The pattern switches every 8 rows, or 32 lines /// /// This value is constant even though it isn't declared constant, /// because declaring it as a constant makes renderURI fail /// with out-of-gas? bytes public backgroundImage = hex'DDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDff' hex'EEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEff' hex'DDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDff' hex'EEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEff' hex'DDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDff' hex'EEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEff' hex'DDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDff' hex'EEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEff' hex'DDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDff' hex'EEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEff' hex'DDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDff' hex'EEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEff' hex'DDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDff' hex'EEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEff' hex'DDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDff' hex'EEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEff' hex'DDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDff' hex'EEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEff' hex'DDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDff' hex'EEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEff' hex'DDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDff' hex'EEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEff' hex'DDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDff' hex'EEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEff' hex'DDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDff' hex'EEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEff' hex'DDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDff' hex'EEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEff' hex'DDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDff' hex'EEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEff' hex'DDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDff' hex'EEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEff' hex'EEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEff' hex'DDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDff' hex'EEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEff' hex'DDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDff' hex'EEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEff' hex'DDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDff' hex'EEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEff' hex'DDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDff' hex'EEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEff' hex'DDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDff' hex'EEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEff' hex'DDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDff' hex'EEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEff' hex'DDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDff' hex'EEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEff' hex'DDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDff' hex'EEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEff' hex'DDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDff' hex'EEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEff' hex'DDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDff' hex'EEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEff' hex'DDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDff' hex'EEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEff' hex'DDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDff' hex'EEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEff' hex'DDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDff' hex'EEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEff' hex'DDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDff' hex'EEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEff' hex'DDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDff' hex'EEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEff' hex'DDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDff' hex'DDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDff' hex'EEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEff' hex'DDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDff' hex'EEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEff' hex'DDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDff' hex'EEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEff' hex'DDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDff' hex'EEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEff' hex'DDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDff' hex'EEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEff' hex'DDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDff' hex'EEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEff' hex'DDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDff' hex'EEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEff' hex'DDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDff' hex'EEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEff' hex'DDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDff' hex'EEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEff' hex'DDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDff' hex'EEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEff' hex'DDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDff' hex'EEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEff' hex'DDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDff' hex'EEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEff' hex'DDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDff' hex'EEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEff' hex'DDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDff' hex'EEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEff' hex'DDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDff' hex'EEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEff' hex'DDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDff' hex'EEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEff' hex'EEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEff' hex'DDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDff' hex'EEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEff' hex'DDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDff' hex'EEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEff' hex'DDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDff' hex'EEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEff' hex'DDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDff' hex'EEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEff' hex'DDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDff' hex'EEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEff' hex'DDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDff' hex'EEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEff' hex'DDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDff' hex'EEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEff' hex'DDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDff' hex'EEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEff' hex'DDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDff' hex'EEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEff' hex'DDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDff' hex'EEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEff' hex'DDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDff' hex'EEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEff' hex'DDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDff' hex'EEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEff' hex'DDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDff' hex'EEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEff' hex'DDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDff' hex'EEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEff' hex'DDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDff' hex'EEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEff' hex'DDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDff'; /// @dev The text key for the background color of the avatar pfp. string public constant TEXT_KEY_PFP_BACKGROUND_COLOR = 'gen0.renderer.pfp.background-color'; /// @dev The text key for the mask of the avatar pfp. string public constant TEXT_KEY_PFP_MASK = 'gen0.renderer.pfp.mask'; /// @dev The text value for a pfp rendering that only displays the head. string public constant TEXT_KEY_PFP_MASK_VALUE_BELOW_THE_NECK = 'below-the-neck'; /// @dev The keccak26 hash 'below-the-neck', for use in the contract. bytes32 private constant TEXT_KEY_PFP_MASK_VALUE_BELOW_THE_NECK_HASH = keccak256(abi.encodePacked(TEXT_KEY_PFP_MASK_VALUE_BELOW_THE_NECK)); ///////////////////////////////////////////////////////////////////////////// // State variables ///////////////////////////////////////////////////////////////////////////// /// @dev fuse can be burned to make the background image immutable bool public fuseBurnedChangeBackgroundImage = false; /// @dev The OpenAvatarGen0AssetsRead dependency. IOpenAvatarGen0AssetsRead public openAvatarGen0AssetsRead; /// @dev The OpenAvatarGen0AssetsCanvasLayerCompositor dependency. IOpenAvatarGen0AssetsCanvasLayerCompositor public openAvatarGen0AssetsCanvasLayerCompositor; /// @dev The OpenAvatarDNA dependency. IOpenAvatarGen0TokenDNA public openAvatarGen0Token; /// @dev The OpenAvatarGen0TextRecords dependency. IOpenAvatarGen0TextRecords public openAvatarGen0TextRecords; // solhint-disable-next-line no-empty-blocks constructor(address ownerProxy) OpenAvatarGen0AssetsCanvasIdStore(CANVAS_ID) { // will be deployed by ImmutableCreate2Factory and then transferred to the configured owner // using a proxy allows for using same bytecode in test and prod address wantOwner = Ownable(ownerProxy).owner(); if (owner() != wantOwner) { transferOwnership(wantOwner); } } ///////////////////////////////////////////////////////////////////////////// // ERC-165: Standard Interface Detection ///////////////////////////////////////////////////////////////////////////// /** * @notice Checks if the contract supports an interface. * @param interfaceId The interface identifier, as specified in ERC-165. * @return True if the contract supports interfaceID, false otherwise. */ function supportsInterface(bytes4 interfaceId) public pure override(OpenAvatarGenerationZero) returns (bool) { return interfaceId == 0x01ffc9a7 || // ERC165 interface ID for ERC165. // IOpenAvatar interfaceId == 0xfdf02ac8 || // ERC165 interface ID for IOpenAvatarGeneration. interfaceId == 0x7b65147c || // ERC165 interface ID for IOpenAvatarSentinel. interfaceId == 0x86953eb4 || // ERC165 interface ID for IOpenAvatar. // renderer interfaceId == INTERFACE_ID_OPENAVATAR_GEN0_RENDERER; } ///////////////////////////////////////////////////////////////////////////// // Initialize Dependencies ///////////////////////////////////////////////////////////////////////////// /** * @dev Initialize the contract. * @param openAvatarGen0Assets_ The address of the asset store read interface contract. * @param openAvatarAssesCanvasLayerCompositor_ The address of the canvas layer compositor contract. * @param openAvatarGen0Token_ The address of the OpenAvatar token contract. * @param openAvatarGen0TextRecords_ The address of the text records contract. */ function initialize( address openAvatarGen0Assets_, address openAvatarAssesCanvasLayerCompositor_, address openAvatarGen0Token_, address openAvatarGen0TextRecords_ ) external onlyOwner { // helpers enforce only-once invariant and check ERC-165 supportsInterface() setOpenAvatarGen0Assets(openAvatarGen0Assets_); setOpenAvatarGen0AssetsCanvasLayerCompositor(openAvatarAssesCanvasLayerCompositor_); setOpenAvatarGen0Token(openAvatarGen0Token_); setOpenAvatarGen0TextRecords(openAvatarGen0TextRecords_); } /** * @notice Check if the contract has been initialized. * @return True if the contract has been initialized, false otherwise. */ function isInitialized() external view returns (bool) { return address(openAvatarGen0AssetsRead) != address(0); } /** * @notice Get the asset store. * @return The address of the asset store read interface contract. */ function getOpenAvatarGen0Assets() external view returns (address) { return address(openAvatarGen0AssetsRead); } /** * @notice Set the asset store. * @param openAvatarGen0Assets_ The address of the asset store read interface contract. */ function setOpenAvatarGen0Assets(address openAvatarGen0Assets_) internal { // only set once if (address(openAvatarGen0AssetsRead) != address(0)) revert AlreadyInitialized(); // check ERC-165 support // only read interface is required if (!IERC165(openAvatarGen0Assets_).supportsInterface(INTERFACE_ID_OPENAVATAR_GEN0_ASSETS_READ)) { revert InterfaceUnsupported(openAvatarGen0Assets_, INTERFACE_ID_OPENAVATAR_GEN0_ASSETS_READ); } // set openAvatarGen0AssetsRead = IOpenAvatarGen0AssetsRead(openAvatarGen0Assets_); // sanity check if (openAvatarGen0AssetsRead.getBytesPerPixel() != 4) revert InvalidCanvasBytesPerPixel(); } /** * @notice Get the IOpenAvatarGen0AssetsCanvasLayerCompositor. * @return The address of the IOpenAvatarGen0AssetsCanvasLayerCompositor interface contract. */ function getOpenAvatarGen0AssetsCanvasLayerCompositor() external view returns (address) { return address(openAvatarGen0AssetsCanvasLayerCompositor); } /** * @notice Set the IOpenAvatarGen0AssetsCanvasLayerCompositor. * @param openAvatarAssesCanvasLayerCompositor_ The address of the * IOpenAvatarGen0AssetsCanvasLayerCompositor interface contract. */ function setOpenAvatarGen0AssetsCanvasLayerCompositor(address openAvatarAssesCanvasLayerCompositor_) internal { // only set once if (address(openAvatarGen0AssetsCanvasLayerCompositor) != address(0)) revert AlreadyInitialized(); // check ERC-165 support // // we don't need use renderURI from OpenAvatarGen0Renderer, which is too high level here // // instead we use the compositor interface to manually layer assets on top of each other // allowing us to modify the image layers before encoding as a PNG/SVG if ( !IERC165(openAvatarAssesCanvasLayerCompositor_).supportsInterface( INTERFACE_ID_OPENAVATAR_GEN0_ASSETS_CANVAS_LAYER_COMPOSITOR ) ) { revert InterfaceUnsupported( openAvatarAssesCanvasLayerCompositor_, INTERFACE_ID_OPENAVATAR_GEN0_ASSETS_CANVAS_LAYER_COMPOSITOR ); } // set openAvatarGen0AssetsCanvasLayerCompositor = IOpenAvatarGen0AssetsCanvasLayerCompositor( openAvatarAssesCanvasLayerCompositor_ ); } /** * @notice Get the OpenAvatar token. * @return The address of the OpenAvatar token read interface contract. */ function getOpenAvatarGen0Token() external view returns (address) { return address(openAvatarGen0Token); } /** * @notice Set the OpenAvatar token address. * @param openAvatarGen0Token_ The address of the OpenAvatar token contract. */ function setOpenAvatarGen0Token(address openAvatarGen0Token_) internal { // only set once if (address(openAvatarGen0Token) != address(0)) revert AlreadyInitialized(); // check ERC-165 support // only DNA interface is required if (!IERC165(openAvatarGen0Token_).supportsInterface(INTERFACE_ID_OPENAVATAR_GEN0_TOKEN_DNA)) { revert InterfaceUnsupported(openAvatarGen0Token_, INTERFACE_ID_OPENAVATAR_GEN0_TOKEN_DNA); } // set openAvatarGen0Token = IOpenAvatarGen0TokenDNA(openAvatarGen0Token_); } /** * @notice Get the OpenAvatarGen0TextRecords. * @return The address of the OpenAvatarGen0TextRecords. */ function getOpenAvatarGen0TextRecords() external view returns (address) { return address(openAvatarGen0TextRecords); } /** * @notice Set the OpenAvatarGen0TextRecords address. * @param openAvatarGen0TextRecords_ The address of the OpenAvatarGen0TextRecords contract. */ function setOpenAvatarGen0TextRecords(address openAvatarGen0TextRecords_) internal { // only set once if (address(openAvatarGen0TextRecords) != address(0)) revert AlreadyInitialized(); // check ERC-165 support // only text() interface is required if (!IERC165(openAvatarGen0TextRecords_).supportsInterface(INTERFACE_ID_OPENAVATAR_GEN0_TEXT)) { revert InterfaceUnsupported(openAvatarGen0TextRecords_, INTERFACE_ID_OPENAVATAR_GEN0_TEXT); } // set openAvatarGen0TextRecords = IOpenAvatarGen0TextRecords(openAvatarGen0TextRecords_); } ///////////////////////////////////////////////////////////////////////////// // Modifiers ///////////////////////////////////////////////////////////////////////////// /** * @notice Modifier to check that the caller is the owner of the token. */ modifier onlyTokenOwner(bytes32 dna) { if (msg.sender != openAvatarGen0Token.ownerOfDNA(dna)) { revert NotOwner(dna); } _; } ///////////////////////////////////////////////////////////////////////////// // Fuses ///////////////////////////////////////////////////////////////////////////// /** * @dev Get the fuse burned status for changing the background image. */ function getFuseBurnedChangeBackgroundImage() external view returns (bool) { return fuseBurnedChangeBackgroundImage; } /** * @dev Burn the fuse to prevent changing the background image. */ function burnFuseChangeBackgroundImage() external onlyOwner { if (!fuseBurnedChangeBackgroundImage) { fuseBurnedChangeBackgroundImage = true; emit FuseBurnedChangeBackgroundImage(); } } ///////////////////////////////////////////////////////////////////////////// // Background Image ///////////////////////////////////////////////////////////////////////////// /** * @dev Get the default background image. */ function getBackgroundImage() external view returns (bytes memory) { return backgroundImage; } ///////////////////////////////////////////////////////////////////////////// // Profile Picture Settings ///////////////////////////////////////////////////////////////////////////// /** * @notice Return the background settings for the given DNA. * @param dna The DNA to set the background color for. * @return The background settings. */ function getProfilePictureSettings(bytes32 dna) external view returns (OpenAvatarProfilePictureSettings memory) { string memory pfpBackground = openAvatarGen0TextRecords.text(dna, TEXT_KEY_PFP_BACKGROUND_COLOR); // pfpBackground is a RGB hexstring like #AABBCC // we need to decompose into a bytes3 0xAABBCC bool overrideBackground = bytes(pfpBackground).length == 7; bytes3 backgroundColor = _hexStrToBytes3(pfpBackground); // pfpMask is a string like 'below-the-neck' or '' string memory pfpMask = openAvatarGen0TextRecords.text(dna, TEXT_KEY_PFP_MASK); // security (gas) - don't compute hash if incorrect length bool validLength = bytes(pfpMask).length == 14; // 'below-the-neck' is 14 bytes bool maskBelowTheNeck = false; if (validLength) { maskBelowTheNeck = keccak256(abi.encodePacked(pfpMask)) == TEXT_KEY_PFP_MASK_VALUE_BELOW_THE_NECK_HASH; } return OpenAvatarProfilePictureSettings(overrideBackground, backgroundColor, maskBelowTheNeck); } /** * @notice Convert an RGB hextring #AABBCC to a bytes3 0xAABBCC. * @param _str The string to convert. * @return The bytes3 value * @dev if the string is not length-7 or leading # is missing, return 0x000000 * @dev if a pair of characters is not a valid hex string, return 0x00 for that byte */ function _hexStrToBytes3(string memory _str) internal pure returns (bytes3) { bytes memory b = bytes(_str); if (b.length != 7) return 0x000000; // the first character should be '#' if (b[0] != 0x23) return 0x000000; bytes3 rgb; for (uint i = 0; i < 3; i++) { rgb |= bytes3(_safeParseByte(_str, 2 * i + 1)) >> (8 * i); } return rgb; } /** * @notice Safely parse a byte. * @param _str The string to parse. * @param _start The start index. * @return The parsed byte, or 0x00 if the byte is invalid. */ function _safeParseByte(string memory _str, uint _start) private pure returns (bytes1) { bytes1 b1 = _safeParseHexDigit(bytes(_str)[_start]); bytes1 b2 = _safeParseHexDigit(bytes(_str)[_start + 1]); return (b1 << 4) | b2; } /** * @dev Safely parse a hex digit. * @param _hex The hex digit to parse. * @return The parsed digit, or 0x00 if the digit is invalid. */ function _safeParseHexDigit(bytes1 _hex) private pure returns (bytes1) { if (_hex >= '0' && _hex <= '9') { return bytes1(uint8(_hex) - 48); } if (_hex >= 'a' && _hex <= 'f') { return bytes1(uint8(_hex) - 97 + 10); } if (_hex >= 'A' && _hex <= 'F') { return bytes1(uint8(_hex) - 65 + 10); } return 0; } ///////////////////////////////////////////////////////////////////////////// // Rendering - external ///////////////////////////////////////////////////////////////////////////// /** * @notice Render the given DNA as a base64-encoded SVG URI. * @param dna The DNA to render. * @return The SVG URI. */ function renderURI(bytes32 dna) external view override returns (string memory) { return string(abi.encodePacked('data:image/svg+xml;base64,', renderBase64SVG(dna))); } /** * @notice Render the given DNA as a base64-encoded SVG URI with the provided settings. * @param dna The DNA to render. * @param pfpSettings The background settings to use. * @return The SVG URI. */ function renderURIWithSettings( bytes32 dna, OpenAvatarProfilePictureSettings memory pfpSettings ) external view returns (string memory) { return string(abi.encodePacked('data:image/svg+xml;base64,', renderBase64SVGWithSettings(dna, pfpSettings))); } ///////////////////////////////////////////////////////////////////////////// // Rendering - helpers ///////////////////////////////////////////////////////////////////////////// function createBackgroundImage( OpenAvatarProfilePictureSettings memory pfpSettings ) internal view returns (bytes memory, uint8, uint8) { bytes memory image = new bytes(openAvatarGen0AssetsRead.getCanvasNumBytes(canvasId)); uint8 width = openAvatarGen0AssetsRead.getCanvasWidth(canvasId); uint8 height = openAvatarGen0AssetsRead.getCanvasHeight(canvasId); if (pfpSettings.overrideBackground) { uint numPixels = uint(width) * uint(height); // override default background with solid color for (uint i = 0; i < numPixels; ) { uint offset = i * 4; image[offset] = pfpSettings.backgroundColor[0]; image[offset + 1] = pfpSettings.backgroundColor[1]; image[offset + 2] = pfpSettings.backgroundColor[2]; image[offset + 3] = 0xff; unchecked { ++i; } } } else { // copy default background for (uint i = 0; i < image.length; ) { image[i] = backgroundImage[i]; unchecked { ++i; } } } return (image, width, height); } /// @dev kind of a hack but doesn't matter. seems like a one-off so just put it here function _drawNeckLine(bytes memory image) internal pure returns (bytes memory) { // the bottom of the head is an 8-pixel line representing the chinline/neck // by default, this is will be the "dark" color for that body color palette, // which is not black but rather a dark skin tone color. // // but if we are drawing just the head without the body, we actually want to have // the head outlined in black because that's the boundary shape. // // so we draw 8 black pixels over top of the "dark" pixels // // another design considered was uploading the "head" pattern separately to // the assets contract, for the same canvas, but on a "off" layer (11?) (body=10) // which fits with the intended design of how to use the assets contract // // but // // this was a one-off for now, so we are just doing the 8 pixel difference here // 8 pixels across // 1st pixel image[2864] = 0x00; image[2865] = 0x00; image[2866] = 0x00; image[2867] = 0xff; // 2nd pixel image[2868] = 0x00; image[2869] = 0x00; image[2870] = 0x00; image[2871] = 0xff; // 3rd pixel image[2872] = 0x00; image[2873] = 0x00; image[2874] = 0x00; image[2875] = 0xff; // 4th pixel image[2876] = 0x00; image[2877] = 0x00; image[2878] = 0x00; image[2879] = 0xff; // 5th pixel image[2880] = 0x00; image[2881] = 0x00; image[2882] = 0x00; image[2883] = 0xff; // 6th pixel image[2884] = 0x00; image[2885] = 0x00; image[2886] = 0x00; image[2887] = 0xff; // 7th pixel image[2888] = 0x00; image[2889] = 0x00; image[2890] = 0x00; // 8th pixel image[2891] = 0xff; image[2892] = 0x00; image[2893] = 0x00; image[2894] = 0x00; image[2895] = 0xff; return image; } /** * @dev Draw the layers of the avatar. * @param image The image to draw on. * @param mask The mask to apply before drawing. * @param canvasId The canvas ID to draw on. * @param dna The DNA to use. * @param pfpSettings The background settings to use. * @return The image with the layers drawn. */ function _drawLayers( bytes memory image, bytes memory mask, uint8 canvasId, bytes32 dna, OpenAvatarProfilePictureSettings memory pfpSettings ) internal view returns (bytes memory) { // body uint8 bodyPattern = dna.bodyPattern(); image = openAvatarGen0AssetsCanvasLayerCompositor.drawMaskedLayer( image, mask, canvasId, LAYER_INDEX_BODY, bodyPattern, dna.bodyPalette() ); // the neck is normally drawn on top of the body with a skin tone "neck" // but for this we want black border around the head not the body // // transparent bodies don't have a neck line if (pfpSettings.maskBelowTheNeck && bodyPattern != 0) { image = _drawNeckLine(image); } // tattoos image = openAvatarGen0AssetsCanvasLayerCompositor.drawMaskedLayer( image, mask, canvasId, LAYER_INDEX_TATTOOS, dna.tattoosPattern(), dna.tattoosPalette() ); // makeup image = openAvatarGen0AssetsCanvasLayerCompositor.drawMaskedLayer( image, mask, canvasId, LAYER_INDEX_MAKEUP, dna.makeupPattern(), dna.makeupPalette() ); // eyes image = openAvatarGen0AssetsCanvasLayerCompositor.drawMaskedLayer( image, mask, canvasId, LAYER_INDEX_LEFT_EYE, dna.leftEyePattern(), dna.leftEyePalette() ); image = openAvatarGen0AssetsCanvasLayerCompositor.drawMaskedLayer( image, mask, canvasId, LAYER_INDEX_RIGHT_EYE, dna.rightEyePattern(), dna.rightEyePalette() ); // clothes image = openAvatarGen0AssetsCanvasLayerCompositor.drawMaskedLayer( image, mask, canvasId, LAYER_INDEX_BOTTOMWEAR, dna.bottomwearPattern(), dna.bottomwearPalette() ); image = openAvatarGen0AssetsCanvasLayerCompositor.drawMaskedLayer( image, mask, canvasId, LAYER_INDEX_FOOTWEAR, dna.footwearPattern(), dna.footwearPalette() ); image = openAvatarGen0AssetsCanvasLayerCompositor.drawMaskedLayer( image, mask, canvasId, LAYER_INDEX_TOPWEAR, dna.topwearPattern(), dna.topwearPalette() ); image = openAvatarGen0AssetsCanvasLayerCompositor.drawMaskedLayer( image, mask, canvasId, LAYER_INDEX_HANDWEAR, dna.handwearPattern(), dna.handwearPalette() ); image = openAvatarGen0AssetsCanvasLayerCompositor.drawMaskedLayer( image, mask, canvasId, LAYER_INDEX_OUTERWEAR, dna.outerwearPattern(), dna.outerwearPalette() ); image = openAvatarGen0AssetsCanvasLayerCompositor.drawMaskedLayer( image, mask, canvasId, LAYER_INDEX_JEWELRY, dna.jewelryPattern(), dna.jewelryPalette() ); // facial hair - no mask image = openAvatarGen0AssetsCanvasLayerCompositor.drawLayer( image, canvasId, LAYER_INDEX_FACIAL_HAIR, dna.facialHairPattern(), dna.facialHairPalette() ); image = openAvatarGen0AssetsCanvasLayerCompositor.drawMaskedLayer( image, mask, canvasId, LAYER_INDEX_FACEWEAR, dna.facewearPattern(), dna.facewearPalette() ); image = openAvatarGen0AssetsCanvasLayerCompositor.drawMaskedLayer( image, mask, canvasId, LAYER_INDEX_EYEWEAR, dna.eyewearPattern(), dna.eyewearPalette() ); // hair - no mask image = openAvatarGen0AssetsCanvasLayerCompositor.drawLayer( image, canvasId, LAYER_INDEX_HAIR, dna.hairPattern(), dna.hairPalette() ); return image; } /** * @notice Render the given DNA as a base64-encoded SVG. * @param dna The DNA to render. * @return The base64-encoded SVG. */ function renderBase64SVG(bytes32 dna) public view returns (bytes memory) { return renderBase64SVGWithSettings(dna, this.getProfilePictureSettings(dna)); } /** * @notice Render the given DNA as a base64-encoded SVG. * @param dna The DNA to render. * @param pfpSettings The settings to use for the profile picture. * @return The base64-encoded SVG. */ function renderBase64SVGWithSettings( bytes32 dna, OpenAvatarProfilePictureSettings memory pfpSettings ) public view returns (bytes memory) { (bytes memory image, uint8 width, uint8 height) = createBackgroundImage(pfpSettings); // copy mask into memory bytes memory mask = new bytes(openAvatarGen0AssetsRead.getCanvasNumPixels(canvasId)); if (pfpSettings.maskBelowTheNeck) { uint length = mask.length; for (uint i = 0; i < length; ) { mask[i] = BELOW_THE_NECK_MASK[i]; unchecked { ++i; } } } image = _drawLayers(image, mask, canvasId, dna, pfpSettings); return encodeBase64SVG( image, width, height, openAvatarGen0AssetsRead.hasAlphaChannel(), SVG_SCALE * width, SVG_SCALE * height ); } }
// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity ^0.8.20; /** * @title IOpenAvatarSentinel * @dev An interface for the OpenAvatar sentinel. */ interface IOpenAvatarSentinel { /// @dev Returns true function openAvatar() external view returns (bool); } /** * @title IOpenAvatarGeneration * @dev An interface for the OpenAvatar generation. */ interface IOpenAvatarGeneration { /// @dev Returns the generation of the OpenAvatar function openAvatarGeneration() external view returns (uint); } /** * @title IOpenAvatar * @dev The OpenAvatar interface. */ interface IOpenAvatar is IOpenAvatarSentinel, IOpenAvatarGeneration { }
// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity ^0.8.20; import {IOpenAvatarGen0AssetsCanvasStore, IOpenAvatarGen0AssetsCanvasStoreRead, IOpenAvatarGen0AssetsCanvasStoreWrite} from './core/interfaces/assets/IOpenAvatarGen0AssetsCanvasStore.sol'; import {IOpenAvatarGen0AssetsPaletteStoreRead, IOpenAvatarGen0AssetsPaletteStoreWrite, IOpenAvatarGen0AssetsPaletteStore} from './core/interfaces/assets/IOpenAvatarGen0AssetsPaletteStore.sol'; import {IOpenAvatarGen0AssetsPatternStoreRead, IOpenAvatarGen0AssetsPatternStoreWrite, IOpenAvatarGen0AssetsPatternStore} from './core/interfaces/assets/IOpenAvatarGen0AssetsPatternStore.sol'; /** * @title IOpenAvatarGen0AssetsRead * @dev This interface reads asset data */ interface IOpenAvatarGen0AssetsRead is IOpenAvatarGen0AssetsCanvasStoreRead, IOpenAvatarGen0AssetsPatternStoreRead, IOpenAvatarGen0AssetsPaletteStoreRead { } /** * @title IOpenAvatarGen0AssetsWrite * @dev This interface writes asset data */ interface IOpenAvatarGen0AssetsWrite is IOpenAvatarGen0AssetsCanvasStoreWrite, IOpenAvatarGen0AssetsPatternStoreWrite, IOpenAvatarGen0AssetsPaletteStoreWrite { } /** * @title IOpenAvatarGen0Assets * @dev This interface reads and writes asset data */ interface IOpenAvatarGen0Assets is IOpenAvatarGen0AssetsRead, IOpenAvatarGen0AssetsWrite { }
// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity ^0.8.20; /** * @title IOpenAvatarGen0Renderer * @dev The primary interface for rendering an Avatar. */ interface IOpenAvatarGen0Renderer { function renderURI(bytes32 dna) external view returns (string memory); } /** * @title IOpenAvatarGen0RendererDecorator * @dev The IOpenAvatarGen0RendererDecorator interface. */ interface IOpenAvatarGen0RendererDecorator is IOpenAvatarGen0Renderer { function renderHex(bytes32 dna) external view returns (bytes memory); function renderPNG(bytes32 dna) external view returns (bytes memory); function renderBase64PNG(bytes32 dna) external view returns (bytes memory); function renderSVG(bytes32 dna) external view returns (bytes memory); function renderBase64SVG(bytes32 dna) external view returns (bytes memory); }
// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity ^0.8.20; /** * @title IOpenAvatarGen0RendererRegistryRead * @dev An interface for reading registered OpenAvatar renderers. */ interface IOpenAvatarGen0RendererRegistryRead { ///////////////////////////////////////////////////////////////////////////// // Read ///////////////////////////////////////////////////////////////////////////// function getNumRenderers() external view returns (uint); function getRendererByKey(string calldata key) external view returns (address); function getRendererByDNA(bytes32 dna) external view returns (address); function getDefaultRenderer() external view returns (address); } /** * @title IOpenAvatarGen0RendererRegistryWrite * @dev An interface for registering OpenAvatar renderers. */ interface IOpenAvatarGen0RendererRegistryWrite { ///////////////////////////////////////////////////////////////////////////// // Write ///////////////////////////////////////////////////////////////////////////// function addRenderer(string calldata key, address _renderer) external; function setDefaultRendererByKey(string calldata key) external; } /** * @title IOpenAvatarGen0RendererRegistry * @dev An interface for registering OpenAvatar renderers. */ interface IOpenAvatarGen0RendererRegistry is IOpenAvatarGen0RendererRegistryRead, IOpenAvatarGen0RendererRegistryWrite { }
// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity ^0.8.20; import {IERC634} from './core/dependencies/IERC634.sol'; /** * @title Text Record * @notice A key-value string pair. */ struct TextRecord { string key; string value; } /** * @title DNA Text Record * @notice A DNA and a key-value string pair. */ struct DNATextRecord { bytes32 dna; string key; string value; } /** * @title IOpenAvatarGen0TextRecords * @dev The TextStore contract stores text data by 32-byte OpenAvatar DNA. * * This contract is based on ERC-634 originally developed for ENS. */ interface IOpenAvatarGen0TextRecords is IERC634 { function setText(bytes32 dna, string calldata key, string calldata value) external; function setText2( bytes32 dna, string calldata key, string calldata value, string calldata key2, string calldata value2 ) external; function setText3( bytes32 dna, string calldata key, string calldata value, string calldata key2, string calldata value2, string calldata key3, string calldata value3 ) external; function setText4( bytes32 dna, string calldata key, string calldata value, string calldata key2, string calldata value2, string calldata key3, string calldata value3, string calldata key4, string calldata value4 ) external; function setTexts(bytes32 dna, TextRecord[] calldata records) external; function setTextBatch(DNATextRecord[] calldata records) external; }
// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity ^0.8.20; import {IOpenAvatar} from './IOpenAvatar.sol'; /** * @title OpenAvatarGen0TokenMetadata * @dev The OpenAvatar metadata. */ struct OpenAvatarGen0TokenMetadata { uint generation; uint tokenId; bytes32 dna; address creator; address renderer; } /** * @title IOpenAvatarGen0TokenMetadata * @dev An interface for the OpenAvatar metadata. */ interface IOpenAvatarGen0TokenMetadata { function getOpenAvatarGen0TokenMetadataByDNA(bytes32 dna) external view returns (OpenAvatarGen0TokenMetadata memory); function getOpenAvatarGen0TokenMetadataByTokenId( uint tokenId ) external view returns (OpenAvatarGen0TokenMetadata memory); function openAvatarURI(bytes32 dna) external view returns (string memory); } /** * @title IOpenAvatarGen0TokenDNA * @dev An interface for the OpenAvatar DNA. */ interface IOpenAvatarGen0TokenDNA { function getDNAByTokenId(uint tokenId) external view returns (bytes32); function getDNAsByTokenIds(uint[] calldata tokenIds) external view returns (bytes32[] memory); function getTokenIdByDNA(bytes32 dna) external view returns (uint); function getTokenIdsByDNAs(bytes32[] calldata dnas) external view returns (uint[] memory); function creatorOf(uint tokenId) external view returns (address); function creatorOfDNA(bytes32 dna) external view returns (address); function ownerOfDNA(bytes32 dna) external view returns (address); function ownerOfDNAs(bytes32[] calldata dnas) external view returns (address[] memory); } /* | Function | Mint State | Payment | Batch | Specify Recipient | |---------------------------|------------|---------------|--------|-------------------| | mint(dna) | Public | mintPrice | No | No | | mintTo(to, dna) | Public | mintPrice | No | Yes | | mintBatch(dnas) | Public | mintPrice * N | Yes | No | | mintBatchTo(to, dnas) | Public | mintPrice * N | Yes | Yes | */ /** * @title IOpenAvatarGen0TokenMintRead * @notice An interface for reading OpenAvatar minting state. */ interface IOpenAvatarGen0TokenMintRead { ///////////////////////////////////////////////////////////////////////////// // Supply ///////////////////////////////////////////////////////////////////////////// function supplySoftCap() external view returns (uint16); function supplyHardCap() external view returns (uint16); ///////////////////////////////////////////////////////////////////////////// // Mint Price ///////////////////////////////////////////////////////////////////////////// function getMintPrice() external view returns (uint); ///////////////////////////////////////////////////////////////////////////// // Mint State ///////////////////////////////////////////////////////////////////////////// function isMinted(bytes32 dna) external view returns (bool); function isMintedEach(bytes32[] calldata dnas) external view returns (bool[] memory); } /** * @title IOpenAvatarGen0TokenMintWrite * @notice An interface for minting OpenAvatars. */ interface IOpenAvatarGen0TokenMintWrite { ///////////////////////////////////////////////////////////////////////////// // Mint ///////////////////////////////////////////////////////////////////////////// function mint(bytes32 dna) external payable; function mintTo(address to, bytes32 dna) external payable; function mintBatch(bytes32[] calldata dnas) external payable; function mintBatchTo(address to, bytes32[] calldata dnas) external payable; } /** * @title IOpenAvatarGen0TokenMintAdmin * @notice An interface allowing the public mint price to be updated. */ interface IOpenAvatarGen0TokenMintAdmin { ///////////////////////////////////////////////////////////////////////////// // Mint Price ///////////////////////////////////////////////////////////////////////////// function setMintPrice(uint val) external; } /** * @title IOpenAvatarGen0TokenMint * @notice The mint interfaces for OpenAvatarGen0Token. */ interface IOpenAvatarGen0TokenMint is IOpenAvatarGen0TokenMintRead, IOpenAvatarGen0TokenMintWrite, IOpenAvatarGen0TokenMintAdmin { } /** * @title IOpenAvatar * @dev The OpenAvatar interface. */ interface IOpenAvatarGen0Token is IOpenAvatar, IOpenAvatarGen0TokenMetadata, IOpenAvatarGen0TokenDNA, IOpenAvatarGen0TokenMint { }
// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity ^0.8.20; import {OpenAvatarGen0AssetsCanvasStore} from './core/assets/OpenAvatarGen0AssetsCanvasStore.sol'; import {OpenAvatarGen0AssetsPaletteStore} from './core/assets/OpenAvatarGen0AssetsPaletteStore.sol'; import {OpenAvatarGen0AssetsPatternStore} from './core/assets/OpenAvatarGen0AssetsPatternStore.sol'; import {ENSReverseClaimer} from './core/lib/ENSReverseClaimer.sol'; import {IOpenAvatarGen0Assets} from './IOpenAvatarGen0Assets.sol'; import {OpenAvatarGenerationZero} from './OpenAvatarGenerationZero.sol'; /** * @title OpenAvatarGen0Assets * @author Cory Gabrielsen (cory.eth) * * @dev This contract is responsible for storing OpenAvatar art assets and * metadata. The art was created by hand by the contract author. * * Immutability: * - This contract references no other contracts * - All assets are stored in append-only arrays. * - Once uploaded, no asset can be modified or deleted. * - So, asset references are immutable and reads are invariant. * - Fuses can be burned to prevent contract owner uploading further assets. * - If all fuses are burned, no further assets can be uploaded and storage is * effectively frozen. * * Terminology: * Colors are stored as color palettes. * Patterns are byte arrays that reference colors in a palette. * * - Palettes: * - A "palette" is an array of RGBA colors (bytes4[]). * - A "palette code" defines an array of palettes (bytes4[][]). * * - Canvas * - A "canvas" is an image canvas with header (uint8 id, uint8 height, * uint8 width), and an array of layers. * * - Layer * - A "layer" is a layer of a canvas, which references an array of patterns. * * - Pattern * - A "pattern" is a byte array that references colors by index in a palette. * * Fuses: * - A fuse can be burned to prevent adding new canvases. * - A fuse can be burned to prevent adding new layers (for all canvases). * - A fuse can be burned to prevent adding new patterns (for all layers). * - A fuse can be burned to prevent adding new palettes (for all patterns). * * Solidity: * Due to C3 Linearization, we cannot "is" IOpenAvatarGen0Assets. * However, IOpenAvatarGen0Assets is indeed implemented fully by OpenAvatarGen0AssetsPatternStore. */ contract OpenAvatarGen0Assets is OpenAvatarGenerationZero, OpenAvatarGen0AssetsPatternStore, ENSReverseClaimer { // solhint-disable-next-line no-empty-blocks constructor(address ownerProxy) OpenAvatarGen0AssetsPatternStore(ownerProxy) {} ///////////////////////////////////////////////////////////////////////////// // ERC-165: Standard Interface Detection ///////////////////////////////////////////////////////////////////////////// /** * @notice Checks if the contract supports an interface. * @param interfaceId The interface identifier, as specified in ERC-165. * @return True if the contract supports interfaceID, false otherwise. */ function supportsInterface( bytes4 interfaceId ) public pure override(OpenAvatarGenerationZero, OpenAvatarGen0AssetsPatternStore) returns (bool) { return interfaceId == 0x01ffc9a7 || // ERC165 interface ID for ERC165. // IOpenAvatar interfaceId == 0xfdf02ac8 || // ERC165 interface ID for IOpenAvatarGeneration. interfaceId == 0x7b65147c || // ERC165 interface ID for IOpenAvatarSentinel. interfaceId == 0x86953eb4 || // ERC165 interface ID for IOpenAvatar. // assets interfaceId == 0x67bf31d1 || // ERC165 interface ID for IOpenAvatarGen0AssetsRead. interfaceId == 0x511ecd08 || // ERC165 interface ID for IOpenAvatarGen0AssetsWrite. interfaceId == 0x36a1fcd9 || // ERC165 interface ID for IOpenAvatarGen0Assets. // canvas interfaceId == 0x91411495 || // ERC165 interface ID for IOpenAvatarGen0AssetsCanvasStoreRead. interfaceId == 0x4d4a1c57 || // ERC165 interface ID for IOpenAvatarGen0AssetsCanvasStoreWrite. interfaceId == 0xdc0b08c2 || // ERC165 interface ID for IOpenAvatarGen0AssetsCanvasStore. // palette interfaceId == 0x5577825f || // ERC165 interface ID for IOpenAvatarGen0AssetsPaletteStoreRead. interfaceId == 0x9c9764e9 || // ERC165 interface ID for IOpenAvatarGen0AssetsPaletteStoreWrite. interfaceId == 0xc9e0e6b6 || // ERC165 interface ID for IOpenAvatarGen0AssetsPaletteStore. // pattern interfaceId == 0x32c8b38e || // ERC165 interface ID for IOpenAvatarGen0AssetsPatternStoreRead. interfaceId == 0xcd89a9e1 || // ERC165 interface ID for IOpenAvatarGen0AssetsPatternStoreWrite. interfaceId == 0xff411a6f; // ERC165 interface ID for IOpenAvatarGen0AssetsPatternStore. } }
// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity ^0.8.20; import {ENSReverseClaimer} from './core/lib/ENSReverseClaimer.sol'; import {OpenAvatarGen0CanvasRenderer} from './core/render/OpenAvatarGen0CanvasRenderer.sol'; import {OpenAvatarGenerationZero} from './OpenAvatarGenerationZero.sol'; /** * @title OpenAvatarGen0Renderer * @author Cory Gabrielsen (cory.eth) * * @dev This contract is responsible for rendering an Avatar based on its DNA. * Avatars are rendered as 32x32 RGBA images with transparency around * the Avatar's body. Avatars are rendered using hand-drawn, original art * created by the contract author (one pixel at a time with a ball mouse). * * Immutable: * Once initialized, the contract is immutable. * Art stored in OpenAvatarGen0Assets is immutable (append-only storage). * So, rendering is deterministic. * * Gas: * Due to PNG encoding, rendering may cost 10,000,000+ gas. * With further base64 encoding (i.e. token URI), combined rendering and * encoding may cost 15,000,000+ gas. * * Terminology: * - Canvas Id (uint8): Selects from an array of canvases * - Layer Index (uint8): Selects from an array of layers for a given canvas * - Pattern Index (uint8): Selects from an array of patterns for a given layer * - Palette Index (uint8): Selects from an array of palettes for a given * pattern * - Color Index (uint8): Selects from an array of colors for a given palette * - Color (bytes4): RGBA * * Structure: * - Every layer contains an array of selectable patterns. * - Each pattern references a specific palette code. * - Each palette code corresponds to an array of palettes. * - Each pattern (uncompressed) is a 32x32=1024 array of color indices. * - compressed as [height, width, offsetX, offsetY, bytes] * -Each palette is an array of RGBA (bytes4[]) which always starts with * 0x00000000 (transparent). * - So, a color index of 0x00 is transparent. * - In essence, each byte in a pattern is a color index which defines an * RGBA color in the corresponding palette array to draw for that pixel. * * Rendering: * Layers are drawn one over another, from lowest to highest layer index, * with alpha-blending. The 32-byte Avatar DNA is interpretted to as * defining the pattern and palette for each layer. Pixels are drawn based * on color derived from DNA-encoded pattern and palette index. If a DNA * defines an invalid index, the layer is drawn as transparent. * * DNA: * It should be noted that while OpenAvatar DNA is interpretted here as * implicitly defining asset references for drawing the Avatar, the concept * of DNA is designed as a standalone building block. Application-specific * re-interpretations of OpenAvatar DNA are entirely possible and encouraged. * * Encoding: * Avatars are rendered as base64-encoded PNGs, embedded within an SVG. The * SVG embeds the PNG as a <foreignObject>, chosen due to Safari's lack of * support (bug?) for "image-rendering: pixelated" of <image> elements within * SVGs. */ contract OpenAvatarGen0Renderer is OpenAvatarGenerationZero, OpenAvatarGen0CanvasRenderer, ENSReverseClaimer { /// @dev The canvas ID for the default, front-facing Avatar pose. uint8 public constant CANVAS_ID = 0; // solhint-disable-next-line no-empty-blocks constructor(address ownerProxy) OpenAvatarGen0CanvasRenderer(ownerProxy, CANVAS_ID) {} ///////////////////////////////////////////////////////////////////////////// // ERC-165: Standard Interface Detection ///////////////////////////////////////////////////////////////////////////// /** * @notice Checks if the contract supports an interface. * @param interfaceId The interface identifier, as specified in ERC-165. * @return True if the contract supports interfaceID, false otherwise. */ function supportsInterface( bytes4 interfaceId ) public pure override(OpenAvatarGenerationZero, OpenAvatarGen0CanvasRenderer) returns (bool) { return interfaceId == 0x01ffc9a7 || // ERC165 interface ID for ERC165. // IOpenAvatar interfaceId == 0xfdf02ac8 || // ERC165 interface ID for IOpenAvatarGeneration. interfaceId == 0x7b65147c || // ERC165 interface ID for IOpenAvatarSentinel. interfaceId == 0x86953eb4 || // ERC165 interface ID for IOpenAvatar. // IOpenAvatarGen0AssetsCanvasLayerCompositor interfaceId == 0xb93e4881 || // ERC165 interface ID for IOpenAvatarGen0Renderer. interfaceId == 0x00a663b1 || // ERC165 interface ID for IOpenAvatarGen0RendererDecorator. interfaceId == 0x13247985 || // ERC165 interface ID for IOpenAvatarGen0CanvasRenderer. interfaceId == 0x2638c94b; // ERC165 interface ID for IOpenAvatarGen0AssetsCanvasLayerCompositor. } }
// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity ^0.8.20; import {Ownable} from '@openzeppelin/contracts/access/Ownable.sol'; import {IERC165} from '@openzeppelin/contracts/utils/introspection/IERC165.sol'; import {ENSReverseClaimer} from './core/lib/ENSReverseClaimer.sol'; import {FuseGuard} from './core/lib/FuseGuard.sol'; import {IOpenAvatarGen0TokenDNA} from './IOpenAvatarGen0Token.sol'; import {IOpenAvatarGen0RendererRegistry} from './IOpenAvatarGen0RendererRegistry.sol'; import {IOpenAvatarGen0TextRecords} from './IOpenAvatarGen0TextRecords.sol'; import {OpenAvatarGenerationZero} from './OpenAvatarGenerationZero.sol'; /** * @title OpenAvatarGen0RendererRegistry * @author Cory Gabrielsen (cory.eth) * * @notice An append-only registry of OpenAvatarGen0Renderer contracts. * @dev An append-only registry of renderer addresses. * * * ---------------------------------------------------------------------------- * Renderer Registry * ---------------------------------------------------------------------------- * A separate `OpenAvatarGen0RendererRegistry` contract is used to register * Renderers for use by the `OpenAvatarGen0Token` contract. * * The `OpenAvatarGen0Token` contract determines which Renderer to use for a * given token by delegating to the `OpenAvatarGen0RendererRegistry` contract. * * Default Renderer: * The registry maintains a concept of "default" Renderer, which may be * modified by the contract owner. * * Token Owners may optionally override the default Renderer for their token by * writing a valid 'gen0.renderer` text record to the * `OpenAvatarGen0TextRecords` contract. * * When the ERC721::tokenURI method is called, the Renderer (token default if * exists, otherwise registry default) is called to render the URI. * * The net effect is that rendering is invariant and onchain. * * If further Renderers are made available by the registry owner, token owners * may opt-in to those upgrades either in general (by not setting a token * default) or explicit choice (by setting their corresponding text record). * * This "soft-upgradeability" can be sunset by burning the registry fuse which * blocks further additions, thereby making the list of available Renderers * fully immutable. * * At launch, two Renderers are provided, described below. * * * ---------------------------------------------------------------------------- * 'base' Renderer * ---------------------------------------------------------------------------- * A base Renderer is provided with the key "base". * * The 'base' Renderer renders the Avatar with transparent background as a * forward-facing sprite. * * * ---------------------------------------------------------------------------- * 'pfp' Renderer * ---------------------------------------------------------------------------- * A Profile Picture Renderer is provided with the key "pfp". * * The 'pfp' Renderer renders the Avatar with a configurable background color * determined by the 'gen0.renderer.pfp.background-color' text record, which * should be a valid RGB hex color code (e.g. "#ff0000" for red). * * Further, the 'pfp' Renderer provides the option of masking the Avatar * below the neck to create a "floating head" effect. This can be configured * by setting the `gen0.renderer.pfp.mask` text record to "below-the-neck". * * The Profile Picture Renderer is a demonstration of combining the OpenAvatar * building blocks together to create dynamic, owner-customizable behavior. It * utilizes onchain assets, onchain rendering, and onchain text records to * render a customizable Avatar pfp. * * This pattern is permissionless. */ contract OpenAvatarGen0RendererRegistry is IOpenAvatarGen0RendererRegistry, OpenAvatarGenerationZero, FuseGuard, ENSReverseClaimer { /// @dev Event emitted when default renderer is changed. event DefaultRendererChange(string indexed key); /// @dev Event emitted when the fuse is burned to disable adding renderers. event FuseBurnedCanAddRenderer(); /// @dev Event emitted when a renderer is added. event RendererAdd(string indexed key, address renderer); /// @dev Error when a component is already initialized. error AlreadyInitialized(); /// @dev Event emitted when an interface is not supported. error InterfaceUnsupported(address contractAddress, bytes4 interfaceId); /// @dev Revert error when renderer index is out of bounds. error RendererDoesNotExist(string key); /// @dev Revert error when renderer already exists. error RendererAlreadyExists(address renderer); /// @dev Event emitted when a renderer key already exists. error RendererKeyAlreadyExists(string key); /// @dev Revert error when renderer key is empty. error RendererKeyCannotBeEmpty(); ///////////////////////////////////////////////////////////////////////////// // Dependencies ///////////////////////////////////////////////////////////////////////////// /// @dev The ERC-165 interface id for the OpenAvatarGen0Renderer (dependency). bytes4 private constant INTERFACE_ID_OPENAVATAR_GEN0_RENDERER = 0xb93e4881; /// @dev The ERC-165 interface id for the OpenAvatarGen0TextRecords ERC-634 text() (dependency). bytes4 private constant INTERFACE_ID_OPENAVATAR_GEN0_TEXT = 0x59d1d43c; /// @dev The text key for the renderer. string public constant TEXT_KEY_RENDERER = 'gen0.renderer'; /// @dev The OpenAvatarGen0TextRecords dependency. IOpenAvatarGen0TextRecords public openAvatarGen0TextRecords; /// @dev The default renderer index. string private defaultRendererKey; /// @dev The list of renderers. mapping(string key => address renderer) public renderers; /// @dev The list of renderer keys. string[] public rendererKeys; /// @dev The list of renderer keys by address. mapping(address renderer => bool) public isRenderer; ///////////////////////////////////////////////////////////////////////////// // Fuses ///////////////////////////////////////////////////////////////////////////// /// @dev Flag to indicate if the fuse has been burned for changing the mint state. bool public fuseBurnedCanAddRenderer = false; ///////////////////////////////////////////////////////////////////////////// // Initialization ///////////////////////////////////////////////////////////////////////////// constructor(address ownerProxy) { // will be deployed by ImmutableCreate2Factory and then transferred to the // configured owner. // using a proxy allows for using same constructor args and thus same // bytecode for all instances of this contract. address wantOwner = Ownable(ownerProxy).owner(); if (owner() != wantOwner) { transferOwnership(wantOwner); } } ///////////////////////////////////////////////////////////////////////////// // ERC-165: Standard Interface Detection ///////////////////////////////////////////////////////////////////////////// /** * @notice Checks if the contract supports an interface. * @param interfaceId The interface identifier, as specified in ERC-165. * @return True if the contract supports interfaceID, false otherwise. */ function supportsInterface(bytes4 interfaceId) public pure override(OpenAvatarGenerationZero) returns (bool) { return interfaceId == 0x01ffc9a7 || // ERC165 interface ID for ERC165. // IOpenAvatar interfaceId == 0xfdf02ac8 || // ERC165 interface ID for IOpenAvatarGeneration. interfaceId == 0x7b65147c || // ERC165 interface ID for IOpenAvatarSentinel. interfaceId == 0x86953eb4 || // ERC165 interface ID for IOpenAvatar. // IOpenAvatarGen0RendererRegistry interfaceId == 0x8646df82; // ERC165 interface ID for IOpenAvatarGen0RendererRegistry } ///////////////////////////////////////////////////////////////////////////// // Initialize Dependencies ///////////////////////////////////////////////////////////////////////////// /** * @dev Initialize the contract. * @param openAvatarGen0TextRecords_ The address of the OpenAvatarGen0TextRecords contract. */ function initialize(address openAvatarGen0TextRecords_) external onlyOwner { setOpenAvatarGen0TextRecords(openAvatarGen0TextRecords_); } /** * @notice Check if the contract has been initialized. * @return True if the contract has been initialized, false otherwise. */ function isInitialized() external view returns (bool) { return address(openAvatarGen0TextRecords) != address(0); } /** * @notice Get the OpenAvatarGen0TextRecords. * @return The address of the OpenAvatarGen0TextRecords. */ function getOpenAvatarGen0TextRecords() external view returns (address) { return address(openAvatarGen0TextRecords); } /** * @notice Set the OpenAvatarGen0TextRecords address. * @param openAvatarGen0TextRecords_ The address of the OpenAvatarGen0TextRecords contract. */ function setOpenAvatarGen0TextRecords(address openAvatarGen0TextRecords_) internal { // only set once if (address(openAvatarGen0TextRecords) != address(0)) revert AlreadyInitialized(); // check ERC-165 support // only text() interface is required if (!IERC165(openAvatarGen0TextRecords_).supportsInterface(INTERFACE_ID_OPENAVATAR_GEN0_TEXT)) { revert InterfaceUnsupported(openAvatarGen0TextRecords_, INTERFACE_ID_OPENAVATAR_GEN0_TEXT); } // set openAvatarGen0TextRecords = IOpenAvatarGen0TextRecords(openAvatarGen0TextRecords_); } ///////////////////////////////////////////////////////////////////////////// // Default renderer ///////////////////////////////////////////////////////////////////////////// /** * @notice Returns the default Renderer address for all token ids. * @return The default Renderer address for all token ids. */ function getDefaultRenderer() external view returns (address) { return renderers[defaultRendererKey]; } /** * @notice Sets the default Renderer address for all token ids. * @param key The key of the Renderer to set as the default. */ function setDefaultRendererByKey(string calldata key) external onlyOwner { // check if the key exists address renderer = renderers[key]; if (renderer == address(0)) revert RendererDoesNotExist(key); // check if the key is the same as the current default bool isSame = (bytes(defaultRendererKey).length == bytes(key).length) && (keccak256(bytes(defaultRendererKey)) == keccak256(bytes(key))); if (isSame) return; // set the new default defaultRendererKey = key; emit DefaultRendererChange(key); } ///////////////////////////////////////////////////////////////////////////// // Read ///////////////////////////////////////////////////////////////////////////// /** * @notice Returns the number of renderers available. * @return The number of renderers available. */ function getNumRenderers() external view returns (uint) { return rendererKeys.length; } /** * @notice Returns the keys of the renderers. * @return The keys of the renderers. */ function getRendererKeys() external view returns (string[] memory) { return rendererKeys; } /** * @notice Returns the address of the renderer. * @return The address of the renderer or 0x0 if not found. */ function getRendererByKey(string calldata key) external view returns (address) { return renderers[key]; } /** * @notice Returns the Renderer address for a given DNA. * @param dna The dna to lookup. * @return The Renderer address for the given DNA. * @dev If the DNA has not been minted, this will revert. */ function getRendererByDNA(bytes32 dna) public view returns (address) { address renderer = address(0); if (address(openAvatarGen0TextRecords) != address(0)) { string memory rendererKey = openAvatarGen0TextRecords.text(dna, TEXT_KEY_RENDERER); renderer = renderers[rendererKey]; } if (renderer == address(0)) return renderers[defaultRendererKey]; return renderer; } ///////////////////////////////////////////////////////////////////////////// // Write ///////////////////////////////////////////////////////////////////////////// /** * @notice Pushes the address of the renderer to the end of the list. * @param key The key of the renderer. * @param renderer The address of the renderer. * @dev Only callable by owner. Reverts if the key already exists. */ function addRenderer(string calldata key, address renderer) external onlyOwner { // fuse must not be burned if (fuseBurnedCanAddRenderer) revert OperationBlockedByBurnedFuse(); // key must not be empty if (bytes(key).length == 0) revert RendererKeyCannotBeEmpty(); // must support the interface if (!IERC165(renderer).supportsInterface(INTERFACE_ID_OPENAVATAR_GEN0_RENDERER)) { revert InterfaceUnsupported(renderer, INTERFACE_ID_OPENAVATAR_GEN0_RENDERER); } // should not already exist if (renderers[key] != address(0)) revert RendererKeyAlreadyExists(key); // address should not already exist if (isRenderer[renderer]) revert RendererAlreadyExists(renderer); // add the renderer renderers[key] = renderer; rendererKeys.push(key); isRenderer[renderer] = true; emit RendererAdd(key, renderer); // set the default if it is empty if (bytes(defaultRendererKey).length == 0) { defaultRendererKey = key; emit DefaultRendererChange(key); } } ///////////////////////////////////////////////////////////////// // Fuse - Can Add Renderer ///////////////////////////////////////////////////////////////// /** * @notice Burn the fuse to permanently disable changing the public mint time. * @dev Only callable by owner. * @dev no-op if fuse is already burned */ function burnFuseCanAddRenderer() external onlyOwner { if (fuseBurnedCanAddRenderer) return; fuseBurnedCanAddRenderer = true; emit FuseBurnedCanAddRenderer(); } /** * @notice Returns whether the fuse is burned to permanently disable changing the public mint time. * @return Whether the fuse is burned to permanently disable changing the public mint time. */ function isFuseBurnedCanAddRenderer() external view returns (bool) { return fuseBurnedCanAddRenderer; } }
// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity ^0.8.20; import {Ownable} from '@openzeppelin/contracts/access/Ownable.sol'; import {IERC165} from '@openzeppelin/contracts/utils/introspection/IERC165.sol'; import {DNATextRecord, IOpenAvatarGen0TextRecords, TextRecord} from './IOpenAvatarGen0TextRecords.sol'; import {IERC634} from './core/dependencies/IERC634.sol'; import {ENSReverseClaimer} from './core/lib/ENSReverseClaimer.sol'; import {KeepAlive} from './core/lib/KeepAlive.sol'; import {IOpenAvatarGen0TokenDNA} from './IOpenAvatarGen0Token.sol'; import {OpenAvatarGenerationZero} from './OpenAvatarGenerationZero.sol'; /** * @title OpenAvatarGen0TextRecords * @author Cory Gabrielsen (cory.eth) * * @notice This contract stores text records as key/value pairs, by 32-byte * OpenAvatar DNA. * @dev This contract is based on ERC-634 text() originally developed for ENS. * * * ---------------------------------------------------------------------------- * Text Records * ---------------------------------------------------------------------------- * A *Text Record* is a core building block of OpenAvatar. * * Text records are key-value pairs of strings stored onchain by Avatar DNA * in the `OpenAvatarGen0TextRecords` contract. Token owners may store any * key/value pair for their token's DNA. * * This mechanism provides an onchain key/value data store for Avatar DNA. * * Text records may be used by other contracts and applications to read/write * arbitrary data to an OpenAvatar. * * For example, text records are used to determine if the token owner has * set the background color for their Profile Picture Renderer. This allows * token owners to dynamically customize their Avatar onchain, and provides * an example for more complex integrations. */ contract OpenAvatarGen0TextRecords is IOpenAvatarGen0TextRecords, OpenAvatarGenerationZero, ENSReverseClaimer, KeepAlive { /// @dev Error when a component is already initialized. error AlreadyInitialized(); /// @dev Error when the required ERC-165 interfaces are not supported. error InterfaceUnsupported(address contractAddress, bytes4 interfaceId); /// @dev Error when the caller is not the token owner. error NotTokenOwner(); /// @dev The EIP-165 interface id for the text data extension bytes4 private constant INTERFACE_ID_TEXT = 0x59d1d43c; /// @dev The ERC-165 interface id for the OpenAvatarDNA (dependency). bytes4 private constant INTERFACE_ID_OPENAVATAR_GEN0_TOKEN_DNA = 0x2717336f; /// @dev An event emitted when text data is set for a DNA event TextChanged(bytes32 indexed dna, string indexedKey, string key); /// @dev The text data for each DNA mapping(bytes32 => mapping(string => string)) private texts; /// @dev The OpenAvatar contract IOpenAvatarGen0TokenDNA public openAvatarGen0Token; constructor(address ownerProxy) { // will be deployed by ImmutableCreate2Factory and then transferred to the // configured owner. // using a proxy allows for using same constructor args and thus same // bytecode for all instances of this contract. address wantOwner = Ownable(ownerProxy).owner(); if (owner() != wantOwner) { transferOwnership(wantOwner); } } ///////////////////////////////////////////////////////////////////////////// // Initialize Dependencies ///////////////////////////////////////////////////////////////////////////// /** * @dev Initialize the contract. * @param openAvatarGen0Token_ The address of the OpenAvatar token contract. */ function initialize(address openAvatarGen0Token_) external onlyOwner { setOpenAvatarGen0Token(openAvatarGen0Token_); } /** * @notice Check if the contract has been initialized. * @return True if the contract has been initialized, false otherwise. */ function isInitialized() external view returns (bool) { return address(openAvatarGen0Token) != address(0); } /** * @dev Get the OpenAvatar token. * @return The address of the OpenAvatar token read interface contract. */ function getOpenAvatarGen0Token() external view returns (address) { return address(openAvatarGen0Token); } /** * @dev Set the OpenAvatar token address. * @param openAvatarGen0Token_ The address of the OpenAvatar token contract. */ function setOpenAvatarGen0Token(address openAvatarGen0Token_) internal { // only set once if (address(openAvatarGen0Token) != address(0)) revert AlreadyInitialized(); // check ERC-165 support // only DNA interface is required if (!IERC165(openAvatarGen0Token_).supportsInterface(INTERFACE_ID_OPENAVATAR_GEN0_TOKEN_DNA)) { revert InterfaceUnsupported(openAvatarGen0Token_, INTERFACE_ID_OPENAVATAR_GEN0_TOKEN_DNA); } // set openAvatarGen0Token = IOpenAvatarGen0TokenDNA(openAvatarGen0Token_); } ///////////////////////////////////////////////////////////////////////////// // ERC-165: Standard Interface Detection ///////////////////////////////////////////////////////////////////////////// /** * @notice Checks if the contract supports an interface. * @param interfaceId The interface identifier, as specified in ERC-165. * @return True if the contract supports interfaceID, false otherwise. */ function supportsInterface(bytes4 interfaceId) public pure override(OpenAvatarGenerationZero) returns (bool) { return interfaceId == 0x01ffc9a7 || // ERC165 interface ID for ERC165. // ERC634 interfaceId == INTERFACE_ID_TEXT || // ERC165 interface ID for ERC634. // IOpenAvatar interfaceId == 0xfdf02ac8 || // ERC165 interface ID for IOpenAvatarGeneration. interfaceId == 0x7b65147c || // ERC165 interface ID for IOpenAvatarSentinel. interfaceId == 0x86953eb4 || // ERC165 interface ID for IOpenAvatar. // IOpenAvatarGen0TextRecords interfaceId == 0x8aacdebd; // ERC165 interface ID for IOpenAvatarGen0TextRecords. } ///////////////////////////////////////////////////////////////////////////// // ERC-634 equivalent ///////////////////////////////////////////////////////////////////////////// /** * @dev Retrieves text metadata for DNA. * Each DNA may have multiple pieces of metadata, identified by a unique string key. * f no text data exists for DNA with the key key, the empty string is returned. * @param dna The DNA to query. * @param key The text data key to query. * @return The associated text data. */ function text(bytes32 dna, string calldata key) external view returns (string memory) { return texts[dna][key]; } ///////////////////////////////////////////////////////////////////////////// // Setters ///////////////////////////////////////////////////////////////////////////// /** * @dev Sets text records for DNA with the unique key key to value, * overwriting anything previously stored for DNA and key. To clear a * text field, set it to the empty string. * @param dna The DNA to update. * @param key The key to set. * @param value The text data value to set. */ function _setText(bytes32 dna, string calldata key, string calldata value) internal { texts[dna][key] = value; emit TextChanged(dna, key, key); } /** * @dev Sets text records for DNA with the unique key key to value, * overwriting anything previously stored for DNA and key. To clear a * text field, set it to the empty string. * Only callable by the token owner. * @param dna The DNA to update. * @param key The key to set. * @param value The text data value to set. */ function setText(bytes32 dna, string calldata key, string calldata value) external { if (openAvatarGen0Token.ownerOfDNA(dna) != msg.sender) revert NotTokenOwner(); _setText(dna, key, value); } /** * @dev Sets text records for DNA with the unique key key to value, * overwriting anything previously stored for DNA and key. To clear a * text field, set it to the empty string. * Only callable by the token owner. * @param dna The DNA to update. * @param key The key to set. * @param value The text data value to set. * @param key2 The second key to set. * @param value2 The second text data value to set. */ function setText2( bytes32 dna, string calldata key, string calldata value, string calldata key2, string calldata value2 ) external { if (openAvatarGen0Token.ownerOfDNA(dna) != msg.sender) revert NotTokenOwner(); _setText(dna, key, value); _setText(dna, key2, value2); } /** * @dev Sets text records for DNA with the unique key key to value, * overwriting anything previously stored for DNA and key. To clear a * text field, set it to the empty string. * Only callable by the token owner. * @param dna The DNA to update. * @param key The key to set. * @param value The text data value to set. * @param key2 The second key to set. * @param value2 The second text data value to set. * @param key3 The third key to set. * @param value3 The third text data value to set. */ function setText3( bytes32 dna, string calldata key, string calldata value, string calldata key2, string calldata value2, string calldata key3, string calldata value3 ) external { if (openAvatarGen0Token.ownerOfDNA(dna) != msg.sender) revert NotTokenOwner(); _setText(dna, key, value); _setText(dna, key2, value2); _setText(dna, key3, value3); } /** * @dev Sets text records for DNA with the unique key key to value, * overwriting anything previously stored for DNA and key. To clear a * text field, set it to the empty string. * Only callable by the token owner. * @param dna The DNA to update. * @param key The key to set. * @param value The text data value to set. * @param key2 The second key to set. * @param value2 The second text data value to set. * @param key3 The third key to set. * @param value3 The third text data value to set. * @param key4 The fourth key to set. */ function setText4( bytes32 dna, string calldata key, string calldata value, string calldata key2, string calldata value2, string calldata key3, string calldata value3, string calldata key4, string calldata value4 ) external { if (openAvatarGen0Token.ownerOfDNA(dna) != msg.sender) revert NotTokenOwner(); _setText(dna, key, value); _setText(dna, key2, value2); _setText(dna, key3, value3); _setText(dna, key4, value4); } /** * @dev Sets an array of text records for a DNA. Each text record is a key/value pair. * Only callable by the token owner. * @param dna The DNA to update. * @param records The text records to set. */ function setTexts(bytes32 dna, TextRecord[] calldata records) external { if (openAvatarGen0Token.ownerOfDNA(dna) != msg.sender) revert NotTokenOwner(); uint length = records.length; for (uint256 i = 0; i < length; ) { TextRecord calldata record = records[i]; _setText(dna, record.key, record.value); unchecked { ++i; } } } /** * @dev Set a batch text records where each record may be a different DNA. * * @param records The records to set. */ function setTextBatch(DNATextRecord[] calldata records) external { uint length = records.length; for (uint256 i = 0; i < length; ) { DNATextRecord calldata record = records[i]; if (openAvatarGen0Token.ownerOfDNA(record.dna) != msg.sender) revert NotTokenOwner(); _setText(record.dna, record.key, record.value); unchecked { ++i; } } } }
// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity ^0.8.20; import {IOpenAvatar} from './IOpenAvatar.sol'; /** * @title IOpenAvatarGeneration * @dev OpenAvatar Generation 0 common definitions. */ abstract contract OpenAvatarGenerationZero is IOpenAvatar { /// @dev OpenAvatar Generation 0. uint public constant OPENAVATAR_GENERATION_ZERO = 0; ///////////////////////////////////////////////////////////////////////////// // IOpenAvatarGeneration ///////////////////////////////////////////////////////////////////////////// /** * @notice Returns 0, in reference to Open Avatar Generation 0. * @return 0 (zero). */ function openAvatarGeneration() external pure returns (uint) { return OPENAVATAR_GENERATION_ZERO; } ///////////////////////////////////////////////////////////////////////////// // IOpenAvatarSentinel ///////////////////////////////////////////////////////////////////////////// /** * @notice Returns true. * @dev This is a sentinel function to indicate that this contract is an * OpenAvatar contract. * @return True. */ function openAvatar() public pure returns (bool) { return true; } ///////////////////////////////////////////////////////////////////////////// // ERC-165: Standard Interface Detection ///////////////////////////////////////////////////////////////////////////// /** * @notice Checks if the contract supports an interface. * @param interfaceId The interface identifier, as specified in ERC-165. * @return True if the contract supports interfaceID, false otherwise. */ function supportsInterface(bytes4 interfaceId) public pure virtual returns (bool) { return interfaceId == 0x01ffc9a7 || // ERC165 interface ID for ERC165. // IOpenAvatar interfaceId == 0xfdf02ac8 || // ERC165 interface ID for IOpenAvatarGeneration. interfaceId == 0x7b65147c || // ERC165 interface ID for IOpenAvatarSentinel. interfaceId == 0x86953eb4; // ERC165 interface ID for IOpenAvatar. } }
// 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 0; } /** * @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 tokenId) public view virtual override returns (string memory) { if (!_exists(tokenId)) revert URIQueryForNonexistentToken(); string memory baseURI = _baseURI(); return bytes(baseURI).length != 0 ? string(abi.encodePacked(baseURI, _toString(tokenId))) : ''; } /** * @dev Base URI for computing {tokenURI}. If set, the resulting URI for each * token will be the concatenation of the `baseURI` and the `tokenId`. Empty * by default, it can be overridden in child contracts. */ function _baseURI() internal view virtual returns (string memory) { return ''; } // ============================================================= // OWNERSHIPS OPERATIONS // ============================================================= /** * @dev Returns the owner of the `tokenId` token. * * Requirements: * * - `tokenId` must exist. */ function ownerOf(uint256 tokenId) public view virtual override returns (address) { return address(uint160(_packedOwnershipOf(tokenId))); } /** * @dev Gas spent here starts off proportional to the maximum mint batch size. * It gradually moves to O(1) as tokens get transferred around over time. */ function _ownershipOf(uint256 tokenId) internal view virtual returns (TokenOwnership memory) { return _unpackedOwnership(_packedOwnershipOf(tokenId)); } /** * @dev Returns the unpacked `TokenOwnership` struct at `index`. */ function _ownershipAt(uint256 index) internal view virtual returns (TokenOwnership memory) { return _unpackedOwnership(_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 // ERC721A Contracts v4.2.3 // Creator: Chiru Labs pragma solidity ^0.8.4; import './IERC721AQueryable.sol'; import '../ERC721A.sol'; /** * @title ERC721AQueryable. * * @dev ERC721A subclass with convenience query functions. */ abstract contract ERC721AQueryable is ERC721A, IERC721AQueryable { /** * @dev Returns the `TokenOwnership` struct at `tokenId` without reverting. * * If the `tokenId` is out of bounds: * * - `addr = address(0)` * - `startTimestamp = 0` * - `burned = false` * - `extraData = 0` * * If the `tokenId` is burned: * * - `addr = <Address of owner before token was burned>` * - `startTimestamp = <Timestamp when token was burned>` * - `burned = true` * - `extraData = <Extra data when token was burned>` * * Otherwise: * * - `addr = <Address of owner>` * - `startTimestamp = <Timestamp of start of ownership>` * - `burned = false` * - `extraData = <Extra data at start of ownership>` */ function explicitOwnershipOf(uint256 tokenId) public view virtual override returns (TokenOwnership memory) { TokenOwnership memory ownership; if (tokenId < _startTokenId() || tokenId >= _nextTokenId()) { return ownership; } ownership = _ownershipAt(tokenId); if (ownership.burned) { return ownership; } return _ownershipOf(tokenId); } /** * @dev Returns an array of `TokenOwnership` structs at `tokenIds` in order. * See {ERC721AQueryable-explicitOwnershipOf} */ function explicitOwnershipsOf(uint256[] calldata tokenIds) external view virtual override returns (TokenOwnership[] memory) { unchecked { uint256 tokenIdsLength = tokenIds.length; TokenOwnership[] memory ownerships = new TokenOwnership[](tokenIdsLength); for (uint256 i; i != tokenIdsLength; ++i) { ownerships[i] = explicitOwnershipOf(tokenIds[i]); } return ownerships; } } /** * @dev Returns an array of token IDs owned by `owner`, * in the range [`start`, `stop`) * (i.e. `start <= tokenId < stop`). * * This function allows for tokens to be queried if the collection * grows too big for a single call of {ERC721AQueryable-tokensOfOwner}. * * Requirements: * * - `start < stop` */ function tokensOfOwnerIn( address owner, uint256 start, uint256 stop ) external view virtual override returns (uint256[] memory) { unchecked { if (start >= stop) revert InvalidQueryRange(); uint256 tokenIdsIdx; uint256 stopLimit = _nextTokenId(); // Set `start = max(start, _startTokenId())`. if (start < _startTokenId()) { start = _startTokenId(); } // Set `stop = min(stop, stopLimit)`. if (stop > stopLimit) { stop = stopLimit; } uint256 tokenIdsMaxLength = balanceOf(owner); // Set `tokenIdsMaxLength = min(balanceOf(owner), stop - start)`, // to cater for cases where `balanceOf(owner)` is too big. if (start < stop) { uint256 rangeLength = stop - start; if (rangeLength < tokenIdsMaxLength) { tokenIdsMaxLength = rangeLength; } } else { tokenIdsMaxLength = 0; } uint256[] memory tokenIds = new uint256[](tokenIdsMaxLength); if (tokenIdsMaxLength == 0) { return tokenIds; } // We need to call `explicitOwnershipOf(start)`, // because the slot at `start` may not be initialized. TokenOwnership memory ownership = explicitOwnershipOf(start); address currOwnershipAddr; // If the starting slot exists (i.e. not burned), initialize `currOwnershipAddr`. // `ownership.address` will not be zero, as `start` is clamped to the valid token ID range. if (!ownership.burned) { currOwnershipAddr = ownership.addr; } for (uint256 i = start; i != stop && tokenIdsIdx != tokenIdsMaxLength; ++i) { ownership = _ownershipAt(i); if (ownership.burned) { continue; } if (ownership.addr != address(0)) { currOwnershipAddr = ownership.addr; } if (currOwnershipAddr == owner) { tokenIds[tokenIdsIdx++] = i; } } // Downsize the array to fit. assembly { mstore(tokenIds, tokenIdsIdx) } return tokenIds; } } /** * @dev Returns an array of token IDs owned by `owner`. * * This function scans the ownership mapping and is O(`totalSupply`) in complexity. * It is meant to be called off-chain. * * See {ERC721AQueryable-tokensOfOwnerIn} for splitting the scan into * multiple smaller scans if the collection is large enough to cause * an out-of-gas error (10K collections should be fine). */ function tokensOfOwner(address owner) external view virtual override returns (uint256[] memory) { unchecked { uint256 tokenIdsIdx; address currOwnershipAddr; uint256 tokenIdsLength = balanceOf(owner); uint256[] memory tokenIds = new uint256[](tokenIdsLength); TokenOwnership memory ownership; for (uint256 i = _startTokenId(); tokenIdsIdx != tokenIdsLength; ++i) { ownership = _ownershipAt(i); if (ownership.burned) { continue; } if (ownership.addr != address(0)) { currOwnershipAddr = ownership.addr; } if (currOwnershipAddr == owner) { tokenIds[tokenIdsIdx++] = i; } } return tokenIds; } } }
// SPDX-License-Identifier: MIT // ERC721A Contracts v4.2.3 // Creator: Chiru Labs pragma solidity ^0.8.4; import '../IERC721A.sol'; /** * @dev Interface of ERC721AQueryable. */ interface IERC721AQueryable is IERC721A { /** * Invalid query range (`start` >= `stop`). */ error InvalidQueryRange(); /** * @dev Returns the `TokenOwnership` struct at `tokenId` without reverting. * * If the `tokenId` is out of bounds: * * - `addr = address(0)` * - `startTimestamp = 0` * - `burned = false` * - `extraData = 0` * * If the `tokenId` is burned: * * - `addr = <Address of owner before token was burned>` * - `startTimestamp = <Timestamp when token was burned>` * - `burned = true` * - `extraData = <Extra data when token was burned>` * * Otherwise: * * - `addr = <Address of owner>` * - `startTimestamp = <Timestamp of start of ownership>` * - `burned = false` * - `extraData = <Extra data at start of ownership>` */ function explicitOwnershipOf(uint256 tokenId) external view returns (TokenOwnership memory); /** * @dev Returns an array of `TokenOwnership` structs at `tokenIds` in order. * See {ERC721AQueryable-explicitOwnershipOf} */ function explicitOwnershipsOf(uint256[] memory tokenIds) external view returns (TokenOwnership[] memory); /** * @dev Returns an array of token IDs owned by `owner`, * in the range [`start`, `stop`) * (i.e. `start <= tokenId < stop`). * * This function allows for tokens to be queried if the collection * grows too big for a single call of {ERC721AQueryable-tokensOfOwner}. * * Requirements: * * - `start < stop` */ function tokensOfOwnerIn( address owner, uint256 start, uint256 stop ) external view returns (uint256[] memory); /** * @dev Returns an array of token IDs owned by `owner`. * * This function scans the ownership mapping and is O(`totalSupply`) in complexity. * It is meant to be called off-chain. * * See {ERC721AQueryable-tokensOfOwnerIn} for splitting the scan into * multiple smaller scans if the collection is large enough to cause * an out-of-gas error (10K collections should be fine). */ function tokensOfOwner(address owner) external view returns (uint256[] memory); }
// 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); }
{ "evmVersion": "shanghai", "optimizer": { "enabled": true, "runs": 1000000, "details": { "yul": true, "yulDetails": { "stackAllocation": true } } }, "viaIR": true, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "libraries": {} }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[{"internalType":"address","name":"ownerProxy","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AlreadyInitialized","type":"error"},{"inputs":[],"name":"ApprovalCallerNotOwnerNorApproved","type":"error"},{"inputs":[],"name":"ApprovalQueryForNonexistentToken","type":"error"},{"inputs":[],"name":"BalanceQueryForZeroAddress","type":"error"},{"inputs":[{"internalType":"bytes32","name":"dna","type":"bytes32"}],"name":"DNAAlreadyMinted","type":"error"},{"inputs":[{"internalType":"bytes32","name":"dna","type":"bytes32"}],"name":"DNADoesNotExist","type":"error"},{"inputs":[{"internalType":"address","name":"contractAddress","type":"address"},{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"InterfaceUnsupported","type":"error"},{"inputs":[],"name":"InvalidQueryRange","type":"error"},{"inputs":[{"internalType":"uint256","name":"batchSize","type":"uint256"}],"name":"MintBatchSizeLimitExceeded","type":"error"},{"inputs":[],"name":"MintDisabled","type":"error"},{"inputs":[],"name":"MintERC2309QuantityExceedsLimit","type":"error"},{"inputs":[],"name":"MintNotAuthorized","type":"error"},{"inputs":[],"name":"MintPermanentlyDisabled","type":"error"},{"inputs":[],"name":"MintToZeroAddress","type":"error"},{"inputs":[],"name":"MintUnderpaid","type":"error"},{"inputs":[],"name":"MintZeroQuantity","type":"error"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"NotTokenOwner","type":"error"},{"inputs":[{"internalType":"bytes32","name":"dna","type":"bytes32"}],"name":"NullDNARestrictedFromBatchMint","type":"error"},{"inputs":[],"name":"OperationBlockedByBurnedFuse","type":"error"},{"inputs":[],"name":"OwnerQueryForNonexistentToken","type":"error"},{"inputs":[],"name":"OwnershipNotInitializedForExtraData","type":"error"},{"inputs":[],"name":"PublicMintNotStarted","type":"error"},{"inputs":[],"name":"SupplyCapExceeded","type":"error"},{"inputs":[],"name":"SupplySoftCapChangeIsNotAnIncrease","type":"error"},{"inputs":[],"name":"SupplySoftCapIsMonotonicallyIncraesing","type":"error"},{"inputs":[{"internalType":"uint256","name":"newSupplyCap","type":"uint256"}],"name":"SupplySoftCapWouldExceedHardCap","type":"error"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"TokenIdDoesNotExist","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":[],"name":"FuseBurnedCanChangeMintState","type":"event"},{"anonymous":false,"inputs":[],"name":"FuseBurnedCanChangePublicMintTime","type":"event"},{"anonymous":false,"inputs":[],"name":"FuseBurnedCanIncreaseSupplySoftCap","type":"event"},{"anonymous":false,"inputs":[],"name":"FuseBurnedCanLowerMintPrice","type":"event"},{"anonymous":false,"inputs":[],"name":"FuseBurnedCanRaiseMintPrice","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"bytes32","name":"dna","type":"bytes32"},{"indexed":false,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Mint","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"oldPrice","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newPrice","type":"uint256"}],"name":"MintPriceChange","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"enum AOpenAvatarMintStateMachine.MintState","name":"state","type":"uint8"}],"name":"MintStateChange","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":"uint256","name":"time","type":"uint256"}],"name":"PublicMintTimeChange","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"TreasuryWithdrawal","type":"event"},{"inputs":[],"name":"MAX_MINT_BATCH_SIZE_LIMIT","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_SUPPLY","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"OPENAVATAR_GENERATION_ZERO","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"_supplySoftCap","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"approve","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"burnFuseCanChangeMintState","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"burnFuseCanChangePublicMintTime","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"burnFuseCanIncreaseSupplySoftCap","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"burnFuseCanLowerMintPrice","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"burnFuseCanRaiseMintPrice","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract ENS","name":"ens","type":"address"},{"internalType":"address","name":"claimant","type":"address"}],"name":"claimReverseENS","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"creatorOf","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"dna","type":"bytes32"}],"name":"creatorOfDNA","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"explicitOwnershipOf","outputs":[{"components":[{"internalType":"address","name":"addr","type":"address"},{"internalType":"uint64","name":"startTimestamp","type":"uint64"},{"internalType":"bool","name":"burned","type":"bool"},{"internalType":"uint24","name":"extraData","type":"uint24"}],"internalType":"struct IERC721A.TokenOwnership","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"}],"name":"explicitOwnershipsOf","outputs":[{"components":[{"internalType":"address","name":"addr","type":"address"},{"internalType":"uint64","name":"startTimestamp","type":"uint64"},{"internalType":"bool","name":"burned","type":"bool"},{"internalType":"uint24","name":"extraData","type":"uint24"}],"internalType":"struct IERC721A.TokenOwnership[]","name":"","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"fuseBurnedCanChangeMintState","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"fuseBurnedCanChangePublicMintTime","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"fuseBurnedCanIncreaseSupplySoftCap","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"fuseBurnedCanLowerMintPrice","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"fuseBurnedCanRaiseMintPrice","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getDNAByTokenId","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"}],"name":"getDNAsByTokenIds","outputs":[{"internalType":"bytes32[]","name":"","type":"bytes32[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getMintPrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getMintState","outputs":[{"internalType":"enum AOpenAvatarMintStateMachine.MintState","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getOpenAvatarGen0RendererRegistry","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"dna","type":"bytes32"}],"name":"getOpenAvatarGen0TokenMetadataByDNA","outputs":[{"components":[{"internalType":"uint256","name":"generation","type":"uint256"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"bytes32","name":"dna","type":"bytes32"},{"internalType":"address","name":"creator","type":"address"},{"internalType":"address","name":"renderer","type":"address"}],"internalType":"struct OpenAvatarGen0TokenMetadata","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getOpenAvatarGen0TokenMetadataByTokenId","outputs":[{"components":[{"internalType":"uint256","name":"generation","type":"uint256"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"bytes32","name":"dna","type":"bytes32"},{"internalType":"address","name":"creator","type":"address"},{"internalType":"address","name":"renderer","type":"address"}],"internalType":"struct OpenAvatarGen0TokenMetadata","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getPublicMintTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"dna","type":"bytes32"}],"name":"getTokenIdByDNA","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32[]","name":"dnas","type":"bytes32[]"}],"name":"getTokenIdsByDNAs","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint16","name":"amount","type":"uint16"}],"name":"increaseSupplySoftCap","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"openAvatarGen0RendererRegistry_","type":"address"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"operator","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isFuseBurnedCanChangeMintState","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isFuseBurnedCanChangePublicMintTime","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isFuseBurnedCanIncreaseSupplySoftCap","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isFuseBurnedCanLowerMintPrice","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isFuseBurnedCanRaiseMintPrice","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isInitialized","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isMintDisabled","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isMintOnlyOwner","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isMintPermanentlyDisabled","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isMintPublic","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isMintPublicPendingBlockTimestamp","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"dna","type":"bytes32"}],"name":"isMinted","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32[]","name":"dnas","type":"bytes32[]"}],"name":"isMintedEach","outputs":[{"internalType":"bool[]","name":"","type":"bool[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"dna","type":"bytes32"}],"name":"mint","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes32[]","name":"dnas","type":"bytes32[]"}],"name":"mintBatch","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"bytes32[]","name":"dnas","type":"bytes32[]"}],"name":"mintBatchTo","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"mintPrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"mintStatus","outputs":[{"internalType":"enum AOpenAvatarMintStateMachine.MintState","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"bytes32","name":"dna","type":"bytes32"}],"name":"mintTo","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"openAvatar","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"openAvatarGen0RendererRegistry","outputs":[{"internalType":"contract IOpenAvatarGen0RendererRegistry","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"openAvatarGeneration","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes32","name":"dna","type":"bytes32"}],"name":"openAvatarURI","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":[{"internalType":"bytes32","name":"dna","type":"bytes32"}],"name":"ownerOfDNA","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32[]","name":"dnas","type":"bytes32[]"}],"name":"ownerOfDNAs","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"publicMintTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"dna","type":"bytes32"}],"name":"renderURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bool","name":"approved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"val","type":"uint256"}],"name":"setMintPrice","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"enum AOpenAvatarMintStateMachine.MintState","name":"val","type":"uint8"}],"name":"setMintState","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_publicMintTime","type":"uint256"}],"name":"setPublicMintTime","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract ENS","name":"ens","type":"address"},{"internalType":"string","name":"name","type":"string"}],"name":"setReverseENS","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"supplyHardCap","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"supplySoftCap","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","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":[{"internalType":"address","name":"owner","type":"address"}],"name":"tokensOfOwner","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"start","type":"uint256"},{"internalType":"uint256","name":"stop","type":"uint256"}],"name":"tokensOfOwnerIn","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"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":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"}]
Contract Creation Code
60806040908082523462000496576200003290620063828038038091620000278285620004ca565b8339810190620004ee565b81519062000040826200049a565b600a82526020906927b832b720bb30ba30b960b11b82840152835162000066816200049a565b6005938482526420ab20aa2960d91b8483015262000084336200050f565b5f805460ff60a01b1916600160a01b1781556002805461ffff199081169091558251919690936001600160401b039291838311620003a2578054926001958685811c951680156200048b575b8a86101462000477578190601f9586811162000426575b508a90868311600114620003c2578c92620003b6575b50505f19600383901b1c191690861b1781555b8151938411620003a257600654908582811c9216801562000397575b8983101462000383578382116200033c575b505086918311600114620002d7579282939183928994620002cb575b50501b915f199060031b1c1916176006555b83600355600f5416600f5562ffffff1960115416601155835192638da5cb5b60e01b8452828460048160018060a01b038096165afa938415620002bf57819462000287575b50548382169116818103620001d0575b8451615e2c9081620005568239f35b3303620002455715620001f35750620001e9906200050f565b5f808080620001c1565b60849083519062461bcd60e51b82526004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b6064820152fd5b60648285519062461bcd60e51b825280600483015260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152fd5b82919450620002af90843d8611620002b7575b620002a68183620004ca565b810190620004ee565b9390620001b1565b503d6200029a565b508451903d90823e3d90fd5b015192505f806200015a565b60068852868820919083601f1981168a5b8a888383106200032457505050106200030b575b505050811b016006556200016c565b01515f1960f88460031b161c191690555f8080620002fc565b868601518855909601959485019487935001620002e8565b60068a52888a209084808701821c8301938b881062000379575b01901c019085905b8281106200036d57506200013e565b8a81550185906200035e565b9350829362000356565b634e487b7160e01b8a52602260045260248afd5b91607f16916200012c565b634e487b7160e01b89526041600452602489fd5b015190505f80620000fd565b848d528b8d208994509190601f1984168e8e5b8282106200040e5750508411620003f5575b505050811b01815562000110565b01515f1960f88460031b161c191690555f8080620003e7565b8385015186558c979095019493840193018e620003d5565b909150838c528a8c2086808501861c8201928d86106200046d575b918a918695949301871c01915b8281106200045e575050620000e7565b8e81558594508a91016200044e565b9250819262000441565b634e487b7160e01b8b52602260045260248bfd5b94607f1694620000d0565b5f80fd5b604081019081106001600160401b03821117620004b657604052565b634e487b7160e01b5f52604160045260245ffd5b601f909101601f19168101906001600160401b03821190821017620004b657604052565b908160209103126200049657516001600160a01b0381168103620004965790565b5f80546001600160a01b039283166001600160a01b03198216811783559216907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09080a356fe60806040526004361015610011575f80fd5b5f3560e01c806301ffc9a7146105185780630220dabb1461051357806306fdde031461050e578063081812fc146105095780630820774c1461042357806308f4c7ff14610504578063095ea7b3146104ff5780630f746edc146104c357806316ec40ca146104d257806318160ddd146104fa57806319ae0182146104965780631e9380fd146104f557806323b872dd146104f05780632e1a7d4d146104eb5780632f4ea757146104e657806332cb6b0c1461040f578063335e501d146104e1578063392e53cd146104dc57806342842e0e146104d75780634862e8af146104aa5780634e76aea8146104d2578063589a1743146104cd5780635bbb2177146104c85780635e403472146104c357806360745cde146104be5780636171673c146104b95780636352211e146104b4578063673c9bad146104915780636817c76c1461043757806369117bd8146104af5780636967b996146104aa5780636fdc6dc8146104a557806370a08231146104a0578063715018a61461049b57806371c38ae6146104965780637657883e14610491578063774a88351461044b578063793d77c51461048c5780637a58aa72146104875780637a93361e146103ba5780637b65147c146104825780637f0ae9aa1461047d5780638007c940146104785780638462151c1461047357806389c5b3b31461046e5780638da5cb5b146104695780638fc8a2281461046457806392ace5871461045f5780639413feb81461045a57806395d89b411461045557806399a2557a146104505780639c3561ef146103e75780639da3f8fd1461044b5780639e4c0be3146104465780639e9b982e14610441578063a22cb4651461043c578063a7f93ebd14610437578063a8f5468b14610432578063adf288df1461042d578063adf2cead14610428578063b3331d5614610423578063b5ab43851461041e578063b68cf6f9146103b5578063b88d4fde14610419578063b93e488114610414578063bad0c2471461040f578063bb058f6d1461040a578063bdc4909514610405578063c23dc68f14610400578063c3a5f216146103fb578063c4d66de8146103f6578063c87b56dd146103f1578063cf7e9df7146103ec578063d02f07b8146103e7578063d42c7979146103e2578063db5c7f85146103dd578063e0c852cc146103d8578063e985e9c5146103d3578063f11cb0af146103ce578063f2fde38b146103c9578063f4a0a528146103c4578063f4f47f1e146103bf578063f55a92bd146103ba5763fdf02ac8146103b5575f80fd5b612a8d565b6118e7565b613cbb565b613c79565b613b41565b613af5565b613a57565b613a05565b6139c7565b61397a565b6124c6565b61392c565b613397565b6131a4565b6130d5565b613009565b612e89565b612db7565b6110fa565b612c57565b612bae565b612a21565b610b48565b61284e565b61272e565b6126f0565b6115f7565b612601565b61255d565b612517565b6117d7565b61246c565b61238d565b612056565b611e4e565b611e10565b611dc0565b611d71565b611c7c565b611a93565b6119b0565b611927565b611855565b61181c565b6115b8565b610de4565b6116f1565b6116ac565b61166e565b6111ed565b611632565b611567565b6114c2565b611460565b610d26565b6113a0565b611230565b610d61565b6111cb565b611178565b611134565b610fc6565b610ed2565b610ec0565b610e27565b610da4565b610bea565b610b88565b610ab2565b61095a565b610863565b61054b565b7fffffffff0000000000000000000000000000000000000000000000000000000081160361054757565b5f80fd5b346105475760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126105475761063d7fffffffff000000000000000000000000000000000000000000000000000000006004356105aa8161051d565b167f01ffc9a7000000000000000000000000000000000000000000000000000000008114908115610839575b811561080f575b81156107e5575b81156107bb575b8115610791575b8115610767575b811561073d575b8115610713575b81156106e9575b81156106bf575b8115610695575b811561066b575b8115610641575b5060405190151581529081906020820190565b0390f35b7ffc3408ea000000000000000000000000000000000000000000000000000000009150145f61062a565b7ff4a0a5280000000000000000000000000000000000000000000000000000000081149150610623565b7f2f683360000000000000000000000000000000000000000000000000000000008114915061061c565b7f27fc9ea20000000000000000000000000000000000000000000000000000000081149150610615565b7f9840fe50000000000000000000000000000000000000000000000000000000008114915061060e565b7f2717336f0000000000000000000000000000000000000000000000000000000081149150610607565b7fc5f6fb610000000000000000000000000000000000000000000000000000000081149150610600565b7f86953eb400000000000000000000000000000000000000000000000000000000811491506105f9565b7f7b65147c00000000000000000000000000000000000000000000000000000000811491506105f2565b7ffdf02ac800000000000000000000000000000000000000000000000000000000811491506105eb565b7fb93e488100000000000000000000000000000000000000000000000000000000811491506105e4565b7f5b5e139f00000000000000000000000000000000000000000000000000000000811491506105dd565b7f80ac58cd00000000000000000000000000000000000000000000000000000000811491506105d6565b34610547575f807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126108df5760ff815460a01c169060058210156108b257602082600260405191148152f35b807f4e487b7100000000000000000000000000000000000000000000000000000000602492526021600452fd5b80fd5b5f5b8381106108f35750505f910152565b81810151838201526020016108e4565b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f60209361093f815180928187528780880191016108e2565b0116010190565b906020610957928181520190610903565b90565b34610547575f807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126108df5760405190806005549060019180831c92808216928315610aa8575b6020928386108514610a7b578588526020880194908115610a3c57506001146109e4575b61063d876109d881890382612b13565b60405191829182610946565b60055f5294509192917f036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db05b838610610a2b57505050910190506109d88261063d5f806109c8565b805485870152948201948101610a0f565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001685525050505090151560051b0190506109d88261063d5f806109c8565b6024827f4e487b710000000000000000000000000000000000000000000000000000000081526022600452fd5b93607f16936109a4565b346105475760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261054757600435610aed816152fd565b15610b1e575f526009602052602073ffffffffffffffffffffffffffffffffffffffff60405f205416604051908152f35b60046040517fcf4700e4000000000000000000000000000000000000000000000000000000008152fd5b34610547575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261054757602060ff601154166040519015158152f35b346105475760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261054757610bbf613cf9565b610bca600435614fb8565b005b73ffffffffffffffffffffffffffffffffffffffff81160361054757565b60407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261054757600435610c2081610bcc565b60243573ffffffffffffffffffffffffffffffffffffffff80610c428361524f565b1690813303610cc1575b5f83815260096020526040812080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff87161790559316907f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9258480a480f35b815f52600a60205260ff610cf63360405f209073ffffffffffffffffffffffffffffffffffffffff165f5260205260405f2090565b5416610c4c5760046040517fcfb3b942000000000000000000000000000000000000000000000000000000008152fd5b34610547575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610547576020600154604051908152f35b34610547575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261054757602060ff60115460101c166040519015158152f35b34610547575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126105475760206003546004549003604051908152f35b34610547575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261054757602060ff60115460081c166040519015158152f35b34610547575f807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126108df5760ff815460a01c169060058210156108b257602082600460405191148152f35b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc606091011261054757600435610eac81610bcc565b90602435610eb981610bcc565b9060443590565b610bca610ecc36610e76565b9161533d565b346105475760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261054757600435610f0c613cf9565b478111610f6857805f8115610f5f575b5f80809381933390f115610f5a5760405190815233907f233c84dc0c19fbf51f9fa92214ffd8a59f512e0098bff5827455d02e401221c090602090a2005b613d8c565b506108fc610f1c565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f496e73756666696369656e742062616c616e63650000000000000000000000006044820152fd5b346105475760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126105475760043561ffff81168082036105475761100c613cf9565b60115460ff166110d05761102d611026600f5461ffff1690565b61ffff1690565b8111156110a6576140001061106f57610bca9061ffff167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000600f541617600f55565b6040517fcb71a4e700000000000000000000000000000000000000000000000000000000815261ffff919091166004820152602490fd5b60046040517f14b50672000000000000000000000000000000000000000000000000000000008152fd5b60046040517f9268e523000000000000000000000000000000000000000000000000000000008152fd5b34610547575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126105475760206040516140008152f35b346105475760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126105475760206111706004356145b8565b604051908152f35b34610547575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261054757602073ffffffffffffffffffffffffffffffffffffffff600b54161515604051908152f35b610bca6111d736610e76565b90604051926111e584612af2565b5f84526155c7565b34610547575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261054757602060ff60025460081c166040519015158152f35b346105475760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126105475761126a60043561452e565b5f52600e602052602073ffffffffffffffffffffffffffffffffffffffff60405f205416604051908152f35b9181601f840112156105475782359167ffffffffffffffff8311610547576020808501948460051b01011161054757565b60207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc820112610547576004359067ffffffffffffffff82116105475761131091600401611296565b9091565b602090816040818301928281528551809452019301915f5b82811061133a575050505090565b9091929382608082611394600194895162ffffff6060809273ffffffffffffffffffffffffffffffffffffffff815116855267ffffffffffffffff6020820151166020860152604081015115156040860152015116910152565b0195019392910161132c565b34610547576113ae366112c7565b906113b88261457c565b916113c66040519384612b13565b8083527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe06113f38261457c565b015f5b8181106114495750505f5b818103611416576040518061063d8682611314565b8061142d6114276001938587614594565b35615bc3565b61143782876145a4565b5261144281866145a4565b5001611401565b602090611454615b8e565b828288010152016113f6565b346105475760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261054757602073ffffffffffffffffffffffffffffffffffffffff6114b96114b46004356145b8565b61524f565b16604051908152f35b346105475760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261054757600435611505611500826145b8565b6152fd565b15611536575f52600e602052602073ffffffffffffffffffffffffffffffffffffffff60405f205416604051908152f35b602490604051907f813010d80000000000000000000000000000000000000000000000000000000082526004820152fd5b346105475760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261054757602073ffffffffffffffffffffffffffffffffffffffff6114b960043561524f565b34610547575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261054757602061ffff600f5416604051908152f35b34610547575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610547576020601054604051908152f35b346105475760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261054757602061117060043561452e565b34610547575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610547576116a4613cf9565b610bca61470a565b346105475760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126105475760206111706004356116ec81610bcc565b6151ce565b34610547575f807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126108df57611728613cf9565b8073ffffffffffffffffffffffffffffffffffffffff81547fffffffffffffffffffffffff000000000000000000000000000000000000000081168355167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a380f35b6005111561179757565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602160045260245ffd5b9190602083019260058210156117975752565b34610547575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126105475761063d60ff5f5460a01c16604051918291826117c4565b34610547575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261054757602060405160148152f35b346105475760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126105475760043561189081610bcc565b60243567ffffffffffffffff80821161054757366023830112156105475781600401359081116105475736602482840101116105475761063d9260246118d793019061506b565b6040519081529081906020820190565b34610547575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261054757602060ff600254166040519015158152f35b34610547575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261054757602060405160018152f35b602090816040818301928281528551809452019301915f5b828110611986575050505090565b835173ffffffffffffffffffffffffffffffffffffffff1685529381019392810192600101611978565b34610547576119be366112c7565b6119c78161457c565b906119d56040519283612b13565b8082527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0611a028261457c565b013660208401375f5b818110611a20576040518061063d8582611960565b611a3c611a37611a31838588614594565b356145b8565b61522d565b73ffffffffffffffffffffffffffffffffffffffff611a5b83866145a4565b911690527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8114611a8e57600101611a0b565b6140ab565b346105475760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261054757600435611ace81610bcc565b60243590611adb82610bcc565b611ae3613cf9565b6040517f02571be30000000000000000000000000000000000000000000000000000000081527f91d1777781884d03a6757a803996e38de2a42967fb37eeaca72729271025a9e260048201526020929083818060248101038173ffffffffffffffffffffffffffffffffffffffff8097165afa8015610f5a578492611bbd925f92611c13575b505f6040519586809581947f1e83409a0000000000000000000000000000000000000000000000000000000083526004830191909173ffffffffffffffffffffffffffffffffffffffff6020820193169052565b0393165af1908115610f5a5761063d925f92611be6575b50506040519081529081906020820190565b611c059250803d10611c0c575b611bfd8183612b13565b81019061505c565b5f80611bd4565b503d611bf3565b611c34919250843d8611611c3b575b611c2c8183612b13565b810190613d97565b905f611b69565b503d611c22565b602090816040818301928281528551809452019301915f5b828110611c68575050505090565b835185529381019392810192600101611c5a565b346105475760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261054757600435611cb781610bcc565b5f80611cc2836151ce565b91611ccc836145ff565b93611cd5615b8e565b5073ffffffffffffffffffffffffffffffffffffffff90811691835b858503611d06576040518061063d8982611c42565b611d0f81615c11565b6040810151611d68575173ffffffffffffffffffffffffffffffffffffffff16838116611d5f575b506001908484841614611d4b575b01611cf1565b80611d59838801978a6145a4565b52611d45565b91506001611d37565b50600190611d45565b34610547575f807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126108df5760ff815460a01c169060058210156108b257602082600360405191148152f35b34610547575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261054757602073ffffffffffffffffffffffffffffffffffffffff5f5416604051908152f35b34610547575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261054757611e46613cf9565b610bca6147c3565b6040807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261054757600490813590611e8882610bcc565b60243567ffffffffffffffff811161054757611ea79036908501611296565b9290915f5460ff8160a01c16611ebc8161178d565b808703611ed057505050610bca9350614cbe565b611ede81969594939661178d565b60038103611f9157505050505060015442105f14611f1c57517fb35ba98d000000000000000000000000000000000000000000000000000000008152fd5b7fb7b9fda281e81cfc174d175e2b3d01a519b60d7133817213012066742bf277499150611f81740400000000000000000000000000000000000000007fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff5f5416175f55565b516004815280602081015b0390a1005b611f9f81969394959661178d565b60028103611ff7575073ffffffffffffffffffffffffffffffffffffffff163303611fcf5750610bca9350614cbe565b8490517fc5ad1c2d000000000000000000000000000000000000000000000000000000008152fd5b826001826120058a9461178d565b0361203057517f17efbd6b000000000000000000000000000000000000000000000000000000008152fd5b517fa6d649c3000000000000000000000000000000000000000000000000000000008152fd5b34610547576020807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261054757600435612092816145b8565b9061209b613eae565b906120d36120ce6120b4835f52600e60205260405f2090565b5473ffffffffffffffffffffffffffffffffffffffff1690565b614378565b9261210f6120f6600b5473ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff1690565b8560405180927f73d9c94d000000000000000000000000000000000000000000000000000000008252818061214c88600483019190602083019252565b03915afa8015610f5a5773ffffffffffffffffffffffffffffffffffffffff915f91612370575b501661217e906142db565b9061218890613f3e565b9161219290614234565b936040519485948786016121ca90600e907f7b2267656e65726174696f6e223a00000000000000000000000000000000000081520190565b6121d391613e0b565b7f2c22646e61223a22000000000000000000000000000000000000000000000000815260080161220291613e0b565b7f222c2263726561746f72223a22000000000000000000000000000000000000008152600d0161223191613e0b565b7f222c2272656e6465726572223a220000000000000000000000000000000000008152600e0161226091613e0b565b7f222c2272656e64657265725f616269223a2272656e646572555249286279746581527f73333229222c22746f6b656e5f6964223a00000000000000000000000000000060208201526031016122b591613e0b565b7f7d0000000000000000000000000000000000000000000000000000000000000081526001015b03907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe091828101825261230f9082612b13565b61231890614d4b565b6040517f646174613a6170706c69636174696f6e2f6a736f6e3b6261736536342c00000093810193845292839190601d0161235291613e0b565b0390810182526123629082612b13565b60405161063d819282610946565b6123879150873d8911611c3b57611c2c8183612b13565b5f612173565b34610547575f807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126108df5760405190806006549060019180831c92808216928315612462575b6020928386108514610a7b578588526020880194908115610a3c575060011461240a5761063d876109d881890382612b13565b60065f5294509192917ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f5b83861061245157505050910190506109d88261063d5f806109c8565b805485870152948201948101612435565b93607f16936123d7565b346105475760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126105475761063d6124ba6004356124ad81610bcc565b6044359060243590615c91565b60405191829182611c42565b34610547575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261054757602073ffffffffffffffffffffffffffffffffffffffff600b5416604051908152f35b346105475760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610547576020612553600435614644565b6040519015158152f35b346105475760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261054757612594614397565b5061063d6125a36004356143d2565b60405191829182919091608060a08201938051835260208101516020840152604081015160408401528173ffffffffffffffffffffffffffffffffffffffff91826060820151166060860152015116910152565b8015150361054757565b346105475760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126105475760043561263c81610bcc565b73ffffffffffffffffffffffffffffffffffffffff6024359161265e836125f7565b335f52600a6020526126918160405f209073ffffffffffffffffffffffffffffffffffffffff165f5260205260405f2090565b921515927fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0081541660ff851617905560405192835216907f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c3160203392a3005b34610547575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261054757612726613cf9565b610bca614f5f565b6040807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261054757600490813561276781610bcc565b602435915f5460ff8160a01c1661277d8161178d565b80860361279157505050610bca9250614a2f565b61279e819594939561178d565b600381036127db575050505060015442105f14611f1c57517fb35ba98d000000000000000000000000000000000000000000000000000000008152fd5b6127e8819593949561178d565b60028103612840575073ffffffffffffffffffffffffffffffffffffffff1633036128185750610bca9250614a2f565b8390517fc5ad1c2d000000000000000000000000000000000000000000000000000000008152fd5b82600182612005899461178d565b60207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261054757600480355f5460ff8160a01c1661288e8161178d565b8084036128a1575050610bca9150614821565b6128aa8161178d565b6003810361295e5750505060015442105f146128e8576040517fb35ba98d000000000000000000000000000000000000000000000000000000008152fd5b5061292b740400000000000000000000000000000000000000007fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff5f5416175f55565b604051600481527fb7b9fda281e81cfc174d175e2b3d01a519b60d7133817213012066742bf27749908060208101611f8c565b6129678161178d565b600281036129bf575073ffffffffffffffffffffffffffffffffffffffff16330361299657610bca9150614821565b506040517fc5ad1c2d000000000000000000000000000000000000000000000000000000008152fd5b6001816129cc869361178d565b036129f9576040517f17efbd6b000000000000000000000000000000000000000000000000000000008152fd5b6040517fa6d649c3000000000000000000000000000000000000000000000000000000008152fd5b34610547575f807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126108df5760ff815460a01c169060058210156108b257602060018314838115612a7b575b506040519015158152f35b9050612a868161178d565b1582612a70565b34610547575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126105475760206040515f8152f35b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b6020810190811067ffffffffffffffff821117612b0e57604052565b612ac5565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff821117612b0e57604052565b6040519060a0820182811067ffffffffffffffff821117612b0e57604052565b67ffffffffffffffff8111612b0e57601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200190565b60807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261054757600435612be481610bcc565b602435612bf081610bcc565b6064359167ffffffffffffffff8311610547573660238401121561054757826004013591612c1d83612b74565b92612c2b6040519485612b13565b8084523660248287010111610547576020815f926024610bca98018388013785010152604435916155c7565b346105475760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261054757600435612cab6120f6600b5473ffffffffffffffffffffffffffffffffffffffff1690565b90602060405180937f73d9c94d0000000000000000000000000000000000000000000000000000000082528180612cea86600483019190602083019252565b03915afa918215610f5a5773ffffffffffffffffffffffffffffffffffffffff925f92612d53928492612d97575b506040519485809481937fb93e4881000000000000000000000000000000000000000000000000000000008352600483019190602083019252565b0392165afa8015610f5a5761063d915f91612d76575b5060405191829182610946565b612d91913d8091833e612d898183612b13565b810190613dac565b5f612d69565b612db091925060203d8111611c3b57611c2c8183612b13565b905f612d18565b3461054757612dc5366112c7565b90612dcf8261457c565b612ddc6040519182612b13565b828152612de88361457c565b916020937fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe085840194013685375f5b818110612e61575050509060405192839281840190828552518091526040840192915f5b828110612e4a57505050500390f35b835185528695509381019392810192600101612e3b565b80612e78612e726001938587614594565b3561452e565b612e8282876145a4565b5201612e17565b612e92366112c7565b5f5460ff8160a01c16612ea48161178d565b60048103612eb7575050610bca91614a42565b612ec08161178d565b60038103612f43575050505060015442105f14612f015760046040517fb35ba98d000000000000000000000000000000000000000000000000000000008152fd5b61292b740400000000000000000000000000000000000000007fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff5f5416175f55565b612f4c8161178d565b60028103612fa4575073ffffffffffffffffffffffffffffffffffffffff163303612f7a57610bca91614a42565b60046040517fc5ad1c2d000000000000000000000000000000000000000000000000000000008152fd5b80612fb060019261178d565b03612fdf5760046040517f17efbd6b000000000000000000000000000000000000000000000000000000008152fd5b60046040517fa6d649c3000000000000000000000000000000000000000000000000000000008152fd5b346105475760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610547576080613045600435615bc3565b613097604051809262ffffff6060809273ffffffffffffffffffffffffffffffffffffffff815116855267ffffffffffffffff6020820151166020860152604081015115156040860152015116910152565bf35b602090816040818301928281528551809452019301915f5b8281106130bf575050505090565b83511515855293810193928101926001016130b1565b34610547576130e3366112c7565b906130ed826145ff565b905f92835b818110613107576040518061063d8682613099565b613112818385614594565b6040517f9e4c0be3000000000000000000000000000000000000000000000000000000008152903560048201529060208083602481305afa8015610f5a57600193613171928992613177575b505061316a83886145a4565b9015159052565b016130f2565b6131969250803d1061319d575b61318e8183612b13565b810190613d77565b5f8061315e565b503d613184565b346105475760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610547576004356131df81610bcc565b6131e7613cf9565b73ffffffffffffffffffffffffffffffffffffffff8061321f6120f6600b5473ffffffffffffffffffffffffffffffffffffffff1690565b1661336d578116906040517f01ffc9a70000000000000000000000000000000000000000000000000000000081526020818061328260048201907f8646df8200000000000000000000000000000000000000000000000000000000602083019252565b0381865afa908115610f5a575f9161334f575b50156132e057610bca8273ffffffffffffffffffffffffffffffffffffffff167fffffffffffffffffffffffff0000000000000000000000000000000000000000600b541617600b55565b6040517fe4786fd500000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff9190911660048201527f8646df82000000000000000000000000000000000000000000000000000000006024820152604490fd5b613367915060203d811161319d5761318e8183612b13565b5f613295565b60046040517f0dc149f0000000000000000000000000000000000000000000000000000000008152fd5b34610547576020807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610547576004356133da6133d6826152fd565b1590565b613902576133e6613eae565b6133f8825f52600d60205260405f2090565b54906134126120ce6120b4845f52600e60205260405f2090565b926134356120f6600b5473ffffffffffffffffffffffffffffffffffffffff1690565b938560405180967f73d9c94d000000000000000000000000000000000000000000000000000000008252818061347389600483019190602083019252565b03915afa948515610f5a576135129573ffffffffffffffffffffffffffffffffffffffff915f916138e5575b5016936134b46134ae866142db565b93613f3e565b935f6134bf83614234565b926134d86120ce6120b4835f52600e60205260405f2090565b9760405180809b81947fb93e4881000000000000000000000000000000000000000000000000000000008352600483019190602083019252565b03915afa968715610f5a575f976138ca575b5060405196879689880161355c90600e907f7b2267656e65726174696f6e223a00000000000000000000000000000000000081520190565b6135669084613e0b565b7f2c22646e61223a2200000000000000000000000000000000000000000000000081526008016135969085613e0b565b7f222c2263726561746f72223a22000000000000000000000000000000000000008152600d016135c591613e0b565b7f222c2272656e6465726572223a220000000000000000000000000000000000008152600e016135f59086613e0b565b7f222c2272656e64657265725f616269223a2272656e646572555249286279746581527f73333229222c22746f6b656e5f6964223a000000000000000000000000000000602082015260310161364b9087613e0b565b7f2c226e616d65223a224f70656e41766174617220230000000000000000000000815260150161367b9087613e0b565b7f222c226465736372697074696f6e223a224f70656e417661746172206973206181527f6e206f6e636861696e2070726f746f636f6c20666f7220417661746172732e2260208201527f2c22696d616765223a22000000000000000000000000000000000000000000006040820152604a016136f691613e0b565b7f222c2261747472696275746573223a5b7b2274726169745f74797065223a224781527f656e65726174696f6e222c2276616c7565223a00000000000000000000000000602082015260330161374b91613e0b565b7f2c22646973706c61795f74797065223a226e756d626572227d2c7b227472616981527f745f74797065223a22444e41222c2276616c7565223a2200000000000000000060208201526037016137a091613e0b565b7f227d2c7b2274726169745f74797065223a2243726561746f72222c2276616c7581527f65223a220000000000000000000000000000000000000000000000000000000060208201526024016137f591613e0b565b7f227d2c7b2274726169745f74797065223a2252656e6465726572222c2276616c81527f7565223a22000000000000000000000000000000000000000000000000000000602082015260250161384a91613e0b565b7f227d2c7b2274726169745f74797065223a22546f6b656e204944222c2276616c81527f7565223a00000000000000000000000000000000000000000000000000000000602082015260240161389f91613e0b565b7f2c22646973706c61795f74797065223a226e756d626572227d5d7d00000000008152601b016122dc565b6138de91973d8091833e612d898183612b13565b955f613524565b6138fc9150883d8a11611c3b57611c2c8183612b13565b5f61349f565b60046040517fa14c4b50000000000000000000000000000000000000000000000000000000008152fd5b346105475760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261054757613963614397565b5061063d6125a36139756004356145b8565b6143d2565b34610547575f807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126108df5760ff815460a01c169060058210156108b25760208260405190158152f35b34610547575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610547576139fd613cf9565b610bca614766565b3461054757613a13366112c7565b90613a1d826145ff565b915f5b818110613a35576040518061063d8682611c42565b80613a46611a316001938587614594565b613a5082876145a4565b5201613a20565b346105475760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261054757602060ff613ae9600435613a9981610bcc565b73ffffffffffffffffffffffffffffffffffffffff60243591613abb83610bcc565b165f52600a845260405f209073ffffffffffffffffffffffffffffffffffffffff165f5260205260405f2090565b54166040519015158152f35b346105475760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261054757600435600581101561054757610bca90613b3c613cf9565b614ea5565b346105475760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261054757600435613b7c81610bcc565b613b84613cf9565b73ffffffffffffffffffffffffffffffffffffffff8091168015613bf5575f918254827fffffffffffffffffffffffff00000000000000000000000000000000000000008216178455167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08380a380f35b60846040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201527f64647265737300000000000000000000000000000000000000000000000000006064820152fd5b346105475760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261054757613cb0613cf9565b610bca60043561467e565b34610547575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261054757613cf1613cf9565b610bca614fff565b73ffffffffffffffffffffffffffffffffffffffff5f54163303613d1957565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602060248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152fd5b908160209103126105475751610957816125f7565b6040513d5f823e3d90fd5b90816020910312610547575161095781610bcc565b6020818303126105475780519067ffffffffffffffff8211610547570181601f82011215610547578051613ddf81612b74565b92613ded6040519485612b13565b818452602082840101116105475761095791602080850191016108e2565b90613e1e602092828151948592016108e2565b0190565b60405190613e2f82612af2565b5f8252565b604051906040820182811067ffffffffffffffff821117612b0e576040526001825260203681840137565b90613e6982612b74565b613e766040519182612b13565b8281527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0613ea48294612b74565b0190602036910137565b5f613eb7613e34565b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff602183015b0190600a907f30313233343536373839616263646566000000000000000000000000000000008282061a835304908115613f39577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90613ede565b505090565b805f917a184f03e93ff9f4daa797ed6e38ed64bf6a1f0100000000000000008082101561409d575b506d04ee2d6d415b85acef81000000008083101561408e575b50662386f26fc100008083101561407f575b506305f5e10080831015614070575b5061271080831015614061575b506064821015614051575b600a80921015614047575b600190816021613fd4828701613e5f565b95860101905b613fe6575b5050505090565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff849101917f30313233343536373839616263646566000000000000000000000000000000008282061a83530491821561404257919082613fda565b613fdf565b9160010191613fc3565b9190606460029104910191613fb8565b6004919392049101915f613fad565b6008919392049101915f613fa0565b6010919392049101915f613f91565b6020919392049101915f613f7f565b60409350810491505f613f66565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b908160021b917f3fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff811603611a8e57565b81810292918115918404141715611a8e57565b9060028201809211611a8e57565b9060208201809211611a8e57565b91908201809211611a8e57565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b80511561417e5760200190565b614144565b80516001101561417e5760210190565b90815181101561417e570160200190565b8015611a8e577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190565b156141d657565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602060248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e746044820152fd5b604051906080820182811067ffffffffffffffff821117612b0e57604052604282526060366020840137603061426983614171565b53607861427583614183565b536041905b6001821161428d576109579150156141cf565b600f811690601082101561417e577f30313233343536373839616263646566000000000000000000000000000000006142d5921a6142cb8486614193565b5360041c916141a4565b9061427a565b604051906060820182811067ffffffffffffffff821117612b0e57604052602a82526040366020840137603061431083614171565b53607861431c83614183565b536029905b60018211614334576109579150156141cf565b600f811690601082101561417e577f3031323334353637383961626364656600000000000000000000000000000000614372921a6142cb8486614193565b90614321565b73ffffffffffffffffffffffffffffffffffffffff61095791166142db565b6040519060a0820182811067ffffffffffffffff821117612b0e576040525f6080838281528260208201528260408201528260608201520152565b6143da614397565b506143e76133d6826152fd565b6144fb576143f48161452e565b6144096120b4825f52600e60205260405f2090565b9061442c6120f6600b5473ffffffffffffffffffffffffffffffffffffffff1690565b91602060405180947f73d9c94d000000000000000000000000000000000000000000000000000000008252818061446b87600483019190602083019252565b03915afa908115610f5a57610957935f926144d9575b506144bc919261448f612b54565b955f875260208701526040860152606085019073ffffffffffffffffffffffffffffffffffffffff169052565b73ffffffffffffffffffffffffffffffffffffffff166080830152565b6144bc92506144f59060203d8111611c3b57611c2c8183612b13565b91614481565b6040517f3374f12d0000000000000000000000000000000000000000000000000000000081526004810191909152602490fd5b614537816152fd565b1561454b575f52600d60205260405f205490565b602490604051907f3374f12d0000000000000000000000000000000000000000000000000000000082526004820152fd5b67ffffffffffffffff8111612b0e5760051b60200190565b919081101561417e5760051b0190565b805182101561417e5760209160051b010190565b805f52600c60205260405f20549081156145d0575090565b6145d9826152fd565b1580156145e9575b611536575090565b50815f52600d6020528060405f205414156145e1565b906146098261457c565b6146166040519182612b13565b8281527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0613ea4829461457c565b805f52600c60205260405f2054908161467757815f52600d60205260405f20540361467257610957906152fd565b505f90565b5050600190565b60105490818111806146fb575b6110d057818110806146ec575b6110d0578082146146e8577fc10030c49484f3ab8e0592f7c3fa8bb25d7edc84f90e0c3c4cfa2f1a935a1a96916146ce82601055565b60408051918252602082019290925290819081015b0390a1565b5050565b5060ff60115460081c16614698565b5060ff60115460101c1661468b565b60115460ff8116614763577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001176011557f92f423b64ccb1952ece9fb716ca1c093997b4166e58a847914759cf448952d795f80a1565b50565b60115460ff8160081c16614763577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff16610100176011557f43068954c894701a7b2c81514382ecbe1248b01dfab74501eba68600391d88215f80a1565b60115460ff8160101c16614763577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffff1662010000176011557f65bc4645b2477bed6ac9e1e602bf139ff267541d54b647053a449b766c3d5adf5f80a1565b6010543410614836576148349033614860565b565b60046040517f580ec48c000000000000000000000000000000000000000000000000000000008152fd5b906040517f9e4c0be30000000000000000000000000000000000000000000000000000000081526020818061489d85600483019190602083019252565b0381305afa908115610f5a575f91614a11575b506149de576003546004549003916148ce611026600f5461ffff1690565b8310156149b4576146e3838361490d7f103a2d32aec953695f3b9ec5ed6c1c6cb822debe92cf1fcf0832cb2c262c7eec965f52600d60205260405f2090565b5580614921855f52600c60205260405f2090565b5561497883614938865f52600e60205260405f2090565b9073ffffffffffffffffffffffffffffffffffffffff167fffffffffffffffffffffffff0000000000000000000000000000000000000000825416179055565b6149818361582e565b6040519384938460409194939273ffffffffffffffffffffffffffffffffffffffff606083019616825260208201520152565b60046040517ff58f733a000000000000000000000000000000000000000000000000000000008152fd5b6040517ff852d0e90000000000000000000000000000000000000000000000000000000081526004810191909152602490fd5b614a29915060203d811161319d5761318e8183612b13565b5f6148b0565b9060105434106148365761483491614860565b90614a4f81601054614108565b3410614836576148349133614a8c565b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8201918211611a8e57565b6003546004549294939290038215614cb75760148311614c8557614ab8614ab38483614137565b614a5f565b614ac8611026600f5461ffff1690565b11156149b4575f805b848110614ae6575050506148349293506159f5565b614af1818689614594565b358015614c525760409081517f9e4c0be30000000000000000000000000000000000000000000000000000000081526020600491818180614b39878783019190602083019252565b0381305afa918215610f5a578792614c35575b5050614c00575082917f103a2d32aec953695f3b9ec5ed6c1c6cb822debe92cf1fcf0832cb2c262c7eec91614bf7614b8660019689614137565b9282614b9a855f52600d60205260405f2090565b5583614bae845f52600c60205260405f2090565b55614bc58a614938855f52600e60205260405f2090565b519283928a8460409194939273ffffffffffffffffffffffffffffffffffffffff606083019616825260208201520152565b0390a101614ad1565b82517ff852d0e90000000000000000000000000000000000000000000000000000000081529081019182529081906020010390fd5b614c4b9250803d1061319d5761318e8183612b13565b5f80614b4c565b6040517fdb5fe4350000000000000000000000000000000000000000000000000000000081526004810191909152602490fd5b6040517f722c972300000000000000000000000000000000000000000000000000000000815260048101849052602490fd5b5050509050565b9190614ccc82601054614108565b34106148365761483492614a8c565b604051906060820182811067ffffffffffffffff821117612b0e57604052604082527f6768696a6b6c6d6e6f707172737475767778797a303132333435363738392b2f6040837f4142434445464748494a4b4c4d4e4f505152535455565758595a61626364656660208201520152565b805115614e9c57614d5a614cdb565b614d76614d71614d6a845161411b565b6003900490565b6140d8565b91614d88614d8384614129565b613e5f565b92835280815182019060208501935b828210614e4a57505050600390510680600114614e0157600214614db9575090565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f3d0000000000000000000000000000000000000000000000000000000000000091015290565b507ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe7f3d3d00000000000000000000000000000000000000000000000000000000000091015290565b9091936004906003809401938451600190603f9082828260121c16880101518553828282600c1c16880101518386015382828260061c1688010151600286015316850101519082015301939190614d97565b50610957613e22565b60ff600254166110d0575f5460ff8160a01c16614ec18161178d565b614eca8361178d565b808314614f5a57614eda8161178d565b15612fdf576005821015611797577fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff1660a082901b74ff000000000000000000000000000000000000000016175f556040517fb7b9fda281e81cfc174d175e2b3d01a519b60d7133817213012066742bf277499181906146e390826117c4565b505050565b60025460ff8116614763577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001176002557ff84b894acb3362a642fb7cc6c5cf8ae86d8f634a92f4ac0f28de735d928c22165f80a1565b60ff60025460081c166110d0578060015414614763576020817f3a40c106e74201e60ef08074000ad1108833249acdc0529b0438bb56072d85ed92600155604051908152a1565b60025460ff8160081c16614763577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff16610100176002557f71cace190dc42826c4dec7d6d422424e12928fa2fcc540dab75f13e6380a413f5f80a1565b90816020910312610547575190565b9091615075613cf9565b604051907f02571be30000000000000000000000000000000000000000000000000000000082526020938483806150d360048201907f91d1777781884d03a6757a803996e38de2a42967fb37eeaca72729271025a9e2602083019252565b038173ffffffffffffffffffffffffffffffffffffffff8098165afa908115610f5a5761513b9486945f93615168575b505f906040518097819682957fc47f002700000000000000000000000000000000000000000000000000000000845260048401615189565b0393165af1918215610f5a575f9261515257505090565b6109579250803d10611c0c57611bfd8183612b13565b5f91935061518290863d8811611c3b57611c2c8183612b13565b9290615103565b90601f836040947fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09360208652816020870152868601375f8582860101520116010190565b73ffffffffffffffffffffffffffffffffffffffff168015615203575f52600860205267ffffffffffffffff60405f20541690565b60046040517f8f4eb604000000000000000000000000000000000000000000000000000000008152fd5b61524b73ffffffffffffffffffffffffffffffffffffffff9161524f565b1690565b806003548110615284575b60046040517fdf2d9b42000000000000000000000000000000000000000000000000000000008152fd5b5f52600760205260405f2054907c0100000000000000000000000000000000000000000000000000000000821661525a575b81156152c0575090565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9150016152f6815f52600760205260405f2090565b54906152b6565b6003548110908161530c575090565b90505f5260076020527c010000000000000000000000000000000000000000000000000000000060405f2054161590565b906153478361524f565b73ffffffffffffffffffffffffffffffffffffffff80841692838284160361559d575f868152600960205260409020805490926153a073ffffffffffffffffffffffffffffffffffffffff881633908114908414171590565b615512575b82169586156154e85761542a936153e0926154df575b5073ffffffffffffffffffffffffffffffffffffffff165f52600860205260405f2090565b80547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01905573ffffffffffffffffffffffffffffffffffffffff165f52600860205260405f2090565b805460010190557c0200000000000000000000000000000000000000000000000000000000804260a01b851717615469865f52600760205260405f2090565b55811615615499575b507fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef5f80a4565b600184016154af815f52600760205260405f2090565b54156154bc575b50615472565b60035481146154b6576154d7905f52600760205260405f2090565b555f806154b6565b5f90555f6153bb565b60046040517fea553b34000000000000000000000000000000000000000000000000000000008152fd5b61556e6133d6615567336155448b73ffffffffffffffffffffffffffffffffffffffff165f52600a60205260405f2090565b9073ffffffffffffffffffffffffffffffffffffffff165f5260205260405f2090565b5460ff1690565b156153a55760046040517f59c896be000000000000000000000000000000000000000000000000000000008152fd5b60046040517fa1148100000000000000000000000000000000000000000000000000000000008152fd5b9291906155d582828661533d565b803b6155e2575b50505050565b6155eb936157d8565b156155f9575f8080806155dc565b60046040517fd1a57ed6000000000000000000000000000000000000000000000000000000008152fd5b9081602091031261054757516109578161051d565b610957939273ffffffffffffffffffffffffffffffffffffffff6080931682525f602083015260408201528160608201520190610903565b9092610957949360809373ffffffffffffffffffffffffffffffffffffffff809216845216602083015260408201528160608201520190610903565b3d156156d6573d906156bd82612b74565b916156cb6040519384612b13565b82523d5f602084013e565b606090565b61573160209173ffffffffffffffffffffffffffffffffffffffff93945f6040519586809581947f150b7a02000000000000000000000000000000000000000000000000000000009a8b84523360048501615638565b0393165af15f91816157a8575b506157825761574b6156ac565b8051908161577d5760046040517fd1a57ed6000000000000000000000000000000000000000000000000000000008152fd5b602001fd5b7fffffffff00000000000000000000000000000000000000000000000000000000161490565b6157ca91925060203d81116157d1575b6157c28183612b13565b810190615623565b905f61573e565b503d6157b8565b92602091615731935f73ffffffffffffffffffffffffffffffffffffffff6040518097819682957f150b7a02000000000000000000000000000000000000000000000000000000009b8c85523360048601615670565b60409081519161583d83612af2565b5f9283815260035461586d8473ffffffffffffffffffffffffffffffffffffffff165f52600860205260405f2090565b68010000000000000001815401905573ffffffffffffffffffffffffffffffffffffffff8416907c02000000000000000000000000000000000000000000000000000000004260a01b8317176158cb825f52600760205260405f2090565b55600191828201917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908289838180a483835b8481036159e657505050156159bd57600355833b61591e575b5050505050565b600354937fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff85019180805b615965575b505050505050600354036108df5780808080615917565b156159b0575b8661597d6133d68684870196866156db565b6159875781615949565b600486517fd1a57ed6000000000000000000000000000000000000000000000000000000008152fd5b85831061596b578061594e565b600484517f2e076300000000000000000000000000000000000000000000000000000000008152fd5b80848b858180a40184906158fe565b9060408051615a0381612af2565b5f93848252600354908415615b6557615a3a8173ffffffffffffffffffffffffffffffffffffffff165f52600860205260405f2090565b680100000000000000018602815401905560019173ffffffffffffffffffffffffffffffffffffffff821683871460e11b4260a01b178117615a84835f52600760205260405f2090565b558682019184807fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9280858d868180a4015b848103615b565750505015615b2d57600355803b615ad7575b505050505050565b6003549485039180805b615afe575b505050505050600354036108df578080808080615acf565b15615b20575b86615b166133d68684870196866156db565b6159875781615ae1565b858310615b045780615ae6565b600485517f2e076300000000000000000000000000000000000000000000000000000000008152fd5b80848c858180a4018590615ab6565b600484517fb562e8dd000000000000000000000000000000000000000000000000000000008152fd5b604051906080820182811067ffffffffffffffff821117612b0e576040525f6060838281528260208201528260408201520152565b615bcb615b8e565b50615bd4615b8e565b600354821015615c0c5750615be881615c11565b6040810151615c0c5750615c0761095791615c01615b8e565b5061524f565b615c2a565b905090565b615c19615b8e565b505f52600760205261095760405f20545b90615c33615b8e565b9173ffffffffffffffffffffffffffffffffffffffff8116835267ffffffffffffffff8160a01c1660208401527c010000000000000000000000000000000000000000000000000000000081161515604084015260e81c6060830152565b9082811015615dcc575f91600354808511615dc4575b50615cb1816151ce565b84831015615dbd57828503818110615db5575b505b615ccf816145ff565b958115615dad57615cdf84615bc3565b918594604093615cf46133d686830151151590565b615d8e575b505b8781141580615d84575b15615d7757615d1381615c11565b80850151615d6e575173ffffffffffffffffffffffffffffffffffffffff90811680615d65575b509081600192871690881614615d51575b01615cfb565b80615d5f838a01998c6145a4565b52615d4b565b96506001615d3a565b50600190615d4b565b5050959450505050815290565b5081871415615d05565b5173ffffffffffffffffffffffffffffffffffffffff1695505f615cf9565b945050505050565b90505f615cc4565b5082615cc6565b93505f615ca7565b60046040517f32c1995a000000000000000000000000000000000000000000000000000000008152fdfea2646970667358221220f080637745391c98b44ec28b348e4547aae59fc52b1ca8937710004ded36daa464736f6c6343000814003300000000000000000000000000000000000027390b412440c58100929acfeae2
Deployed Bytecode
0x60806040526004361015610011575f80fd5b5f3560e01c806301ffc9a7146105185780630220dabb1461051357806306fdde031461050e578063081812fc146105095780630820774c1461042357806308f4c7ff14610504578063095ea7b3146104ff5780630f746edc146104c357806316ec40ca146104d257806318160ddd146104fa57806319ae0182146104965780631e9380fd146104f557806323b872dd146104f05780632e1a7d4d146104eb5780632f4ea757146104e657806332cb6b0c1461040f578063335e501d146104e1578063392e53cd146104dc57806342842e0e146104d75780634862e8af146104aa5780634e76aea8146104d2578063589a1743146104cd5780635bbb2177146104c85780635e403472146104c357806360745cde146104be5780636171673c146104b95780636352211e146104b4578063673c9bad146104915780636817c76c1461043757806369117bd8146104af5780636967b996146104aa5780636fdc6dc8146104a557806370a08231146104a0578063715018a61461049b57806371c38ae6146104965780637657883e14610491578063774a88351461044b578063793d77c51461048c5780637a58aa72146104875780637a93361e146103ba5780637b65147c146104825780637f0ae9aa1461047d5780638007c940146104785780638462151c1461047357806389c5b3b31461046e5780638da5cb5b146104695780638fc8a2281461046457806392ace5871461045f5780639413feb81461045a57806395d89b411461045557806399a2557a146104505780639c3561ef146103e75780639da3f8fd1461044b5780639e4c0be3146104465780639e9b982e14610441578063a22cb4651461043c578063a7f93ebd14610437578063a8f5468b14610432578063adf288df1461042d578063adf2cead14610428578063b3331d5614610423578063b5ab43851461041e578063b68cf6f9146103b5578063b88d4fde14610419578063b93e488114610414578063bad0c2471461040f578063bb058f6d1461040a578063bdc4909514610405578063c23dc68f14610400578063c3a5f216146103fb578063c4d66de8146103f6578063c87b56dd146103f1578063cf7e9df7146103ec578063d02f07b8146103e7578063d42c7979146103e2578063db5c7f85146103dd578063e0c852cc146103d8578063e985e9c5146103d3578063f11cb0af146103ce578063f2fde38b146103c9578063f4a0a528146103c4578063f4f47f1e146103bf578063f55a92bd146103ba5763fdf02ac8146103b5575f80fd5b612a8d565b6118e7565b613cbb565b613c79565b613b41565b613af5565b613a57565b613a05565b6139c7565b61397a565b6124c6565b61392c565b613397565b6131a4565b6130d5565b613009565b612e89565b612db7565b6110fa565b612c57565b612bae565b612a21565b610b48565b61284e565b61272e565b6126f0565b6115f7565b612601565b61255d565b612517565b6117d7565b61246c565b61238d565b612056565b611e4e565b611e10565b611dc0565b611d71565b611c7c565b611a93565b6119b0565b611927565b611855565b61181c565b6115b8565b610de4565b6116f1565b6116ac565b61166e565b6111ed565b611632565b611567565b6114c2565b611460565b610d26565b6113a0565b611230565b610d61565b6111cb565b611178565b611134565b610fc6565b610ed2565b610ec0565b610e27565b610da4565b610bea565b610b88565b610ab2565b61095a565b610863565b61054b565b7fffffffff0000000000000000000000000000000000000000000000000000000081160361054757565b5f80fd5b346105475760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126105475761063d7fffffffff000000000000000000000000000000000000000000000000000000006004356105aa8161051d565b167f01ffc9a7000000000000000000000000000000000000000000000000000000008114908115610839575b811561080f575b81156107e5575b81156107bb575b8115610791575b8115610767575b811561073d575b8115610713575b81156106e9575b81156106bf575b8115610695575b811561066b575b8115610641575b5060405190151581529081906020820190565b0390f35b7ffc3408ea000000000000000000000000000000000000000000000000000000009150145f61062a565b7ff4a0a5280000000000000000000000000000000000000000000000000000000081149150610623565b7f2f683360000000000000000000000000000000000000000000000000000000008114915061061c565b7f27fc9ea20000000000000000000000000000000000000000000000000000000081149150610615565b7f9840fe50000000000000000000000000000000000000000000000000000000008114915061060e565b7f2717336f0000000000000000000000000000000000000000000000000000000081149150610607565b7fc5f6fb610000000000000000000000000000000000000000000000000000000081149150610600565b7f86953eb400000000000000000000000000000000000000000000000000000000811491506105f9565b7f7b65147c00000000000000000000000000000000000000000000000000000000811491506105f2565b7ffdf02ac800000000000000000000000000000000000000000000000000000000811491506105eb565b7fb93e488100000000000000000000000000000000000000000000000000000000811491506105e4565b7f5b5e139f00000000000000000000000000000000000000000000000000000000811491506105dd565b7f80ac58cd00000000000000000000000000000000000000000000000000000000811491506105d6565b34610547575f807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126108df5760ff815460a01c169060058210156108b257602082600260405191148152f35b807f4e487b7100000000000000000000000000000000000000000000000000000000602492526021600452fd5b80fd5b5f5b8381106108f35750505f910152565b81810151838201526020016108e4565b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f60209361093f815180928187528780880191016108e2565b0116010190565b906020610957928181520190610903565b90565b34610547575f807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126108df5760405190806005549060019180831c92808216928315610aa8575b6020928386108514610a7b578588526020880194908115610a3c57506001146109e4575b61063d876109d881890382612b13565b60405191829182610946565b60055f5294509192917f036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db05b838610610a2b57505050910190506109d88261063d5f806109c8565b805485870152948201948101610a0f565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001685525050505090151560051b0190506109d88261063d5f806109c8565b6024827f4e487b710000000000000000000000000000000000000000000000000000000081526022600452fd5b93607f16936109a4565b346105475760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261054757600435610aed816152fd565b15610b1e575f526009602052602073ffffffffffffffffffffffffffffffffffffffff60405f205416604051908152f35b60046040517fcf4700e4000000000000000000000000000000000000000000000000000000008152fd5b34610547575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261054757602060ff601154166040519015158152f35b346105475760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261054757610bbf613cf9565b610bca600435614fb8565b005b73ffffffffffffffffffffffffffffffffffffffff81160361054757565b60407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261054757600435610c2081610bcc565b60243573ffffffffffffffffffffffffffffffffffffffff80610c428361524f565b1690813303610cc1575b5f83815260096020526040812080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff87161790559316907f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9258480a480f35b815f52600a60205260ff610cf63360405f209073ffffffffffffffffffffffffffffffffffffffff165f5260205260405f2090565b5416610c4c5760046040517fcfb3b942000000000000000000000000000000000000000000000000000000008152fd5b34610547575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610547576020600154604051908152f35b34610547575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261054757602060ff60115460101c166040519015158152f35b34610547575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126105475760206003546004549003604051908152f35b34610547575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261054757602060ff60115460081c166040519015158152f35b34610547575f807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126108df5760ff815460a01c169060058210156108b257602082600460405191148152f35b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc606091011261054757600435610eac81610bcc565b90602435610eb981610bcc565b9060443590565b610bca610ecc36610e76565b9161533d565b346105475760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261054757600435610f0c613cf9565b478111610f6857805f8115610f5f575b5f80809381933390f115610f5a5760405190815233907f233c84dc0c19fbf51f9fa92214ffd8a59f512e0098bff5827455d02e401221c090602090a2005b613d8c565b506108fc610f1c565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f496e73756666696369656e742062616c616e63650000000000000000000000006044820152fd5b346105475760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126105475760043561ffff81168082036105475761100c613cf9565b60115460ff166110d05761102d611026600f5461ffff1690565b61ffff1690565b8111156110a6576140001061106f57610bca9061ffff167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000600f541617600f55565b6040517fcb71a4e700000000000000000000000000000000000000000000000000000000815261ffff919091166004820152602490fd5b60046040517f14b50672000000000000000000000000000000000000000000000000000000008152fd5b60046040517f9268e523000000000000000000000000000000000000000000000000000000008152fd5b34610547575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126105475760206040516140008152f35b346105475760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126105475760206111706004356145b8565b604051908152f35b34610547575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261054757602073ffffffffffffffffffffffffffffffffffffffff600b54161515604051908152f35b610bca6111d736610e76565b90604051926111e584612af2565b5f84526155c7565b34610547575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261054757602060ff60025460081c166040519015158152f35b346105475760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126105475761126a60043561452e565b5f52600e602052602073ffffffffffffffffffffffffffffffffffffffff60405f205416604051908152f35b9181601f840112156105475782359167ffffffffffffffff8311610547576020808501948460051b01011161054757565b60207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc820112610547576004359067ffffffffffffffff82116105475761131091600401611296565b9091565b602090816040818301928281528551809452019301915f5b82811061133a575050505090565b9091929382608082611394600194895162ffffff6060809273ffffffffffffffffffffffffffffffffffffffff815116855267ffffffffffffffff6020820151166020860152604081015115156040860152015116910152565b0195019392910161132c565b34610547576113ae366112c7565b906113b88261457c565b916113c66040519384612b13565b8083527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe06113f38261457c565b015f5b8181106114495750505f5b818103611416576040518061063d8682611314565b8061142d6114276001938587614594565b35615bc3565b61143782876145a4565b5261144281866145a4565b5001611401565b602090611454615b8e565b828288010152016113f6565b346105475760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261054757602073ffffffffffffffffffffffffffffffffffffffff6114b96114b46004356145b8565b61524f565b16604051908152f35b346105475760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261054757600435611505611500826145b8565b6152fd565b15611536575f52600e602052602073ffffffffffffffffffffffffffffffffffffffff60405f205416604051908152f35b602490604051907f813010d80000000000000000000000000000000000000000000000000000000082526004820152fd5b346105475760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261054757602073ffffffffffffffffffffffffffffffffffffffff6114b960043561524f565b34610547575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261054757602061ffff600f5416604051908152f35b34610547575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610547576020601054604051908152f35b346105475760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261054757602061117060043561452e565b34610547575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610547576116a4613cf9565b610bca61470a565b346105475760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126105475760206111706004356116ec81610bcc565b6151ce565b34610547575f807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126108df57611728613cf9565b8073ffffffffffffffffffffffffffffffffffffffff81547fffffffffffffffffffffffff000000000000000000000000000000000000000081168355167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a380f35b6005111561179757565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602160045260245ffd5b9190602083019260058210156117975752565b34610547575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126105475761063d60ff5f5460a01c16604051918291826117c4565b34610547575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261054757602060405160148152f35b346105475760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126105475760043561189081610bcc565b60243567ffffffffffffffff80821161054757366023830112156105475781600401359081116105475736602482840101116105475761063d9260246118d793019061506b565b6040519081529081906020820190565b34610547575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261054757602060ff600254166040519015158152f35b34610547575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261054757602060405160018152f35b602090816040818301928281528551809452019301915f5b828110611986575050505090565b835173ffffffffffffffffffffffffffffffffffffffff1685529381019392810192600101611978565b34610547576119be366112c7565b6119c78161457c565b906119d56040519283612b13565b8082527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0611a028261457c565b013660208401375f5b818110611a20576040518061063d8582611960565b611a3c611a37611a31838588614594565b356145b8565b61522d565b73ffffffffffffffffffffffffffffffffffffffff611a5b83866145a4565b911690527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8114611a8e57600101611a0b565b6140ab565b346105475760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261054757600435611ace81610bcc565b60243590611adb82610bcc565b611ae3613cf9565b6040517f02571be30000000000000000000000000000000000000000000000000000000081527f91d1777781884d03a6757a803996e38de2a42967fb37eeaca72729271025a9e260048201526020929083818060248101038173ffffffffffffffffffffffffffffffffffffffff8097165afa8015610f5a578492611bbd925f92611c13575b505f6040519586809581947f1e83409a0000000000000000000000000000000000000000000000000000000083526004830191909173ffffffffffffffffffffffffffffffffffffffff6020820193169052565b0393165af1908115610f5a5761063d925f92611be6575b50506040519081529081906020820190565b611c059250803d10611c0c575b611bfd8183612b13565b81019061505c565b5f80611bd4565b503d611bf3565b611c34919250843d8611611c3b575b611c2c8183612b13565b810190613d97565b905f611b69565b503d611c22565b602090816040818301928281528551809452019301915f5b828110611c68575050505090565b835185529381019392810192600101611c5a565b346105475760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261054757600435611cb781610bcc565b5f80611cc2836151ce565b91611ccc836145ff565b93611cd5615b8e565b5073ffffffffffffffffffffffffffffffffffffffff90811691835b858503611d06576040518061063d8982611c42565b611d0f81615c11565b6040810151611d68575173ffffffffffffffffffffffffffffffffffffffff16838116611d5f575b506001908484841614611d4b575b01611cf1565b80611d59838801978a6145a4565b52611d45565b91506001611d37565b50600190611d45565b34610547575f807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126108df5760ff815460a01c169060058210156108b257602082600360405191148152f35b34610547575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261054757602073ffffffffffffffffffffffffffffffffffffffff5f5416604051908152f35b34610547575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261054757611e46613cf9565b610bca6147c3565b6040807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261054757600490813590611e8882610bcc565b60243567ffffffffffffffff811161054757611ea79036908501611296565b9290915f5460ff8160a01c16611ebc8161178d565b808703611ed057505050610bca9350614cbe565b611ede81969594939661178d565b60038103611f9157505050505060015442105f14611f1c57517fb35ba98d000000000000000000000000000000000000000000000000000000008152fd5b7fb7b9fda281e81cfc174d175e2b3d01a519b60d7133817213012066742bf277499150611f81740400000000000000000000000000000000000000007fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff5f5416175f55565b516004815280602081015b0390a1005b611f9f81969394959661178d565b60028103611ff7575073ffffffffffffffffffffffffffffffffffffffff163303611fcf5750610bca9350614cbe565b8490517fc5ad1c2d000000000000000000000000000000000000000000000000000000008152fd5b826001826120058a9461178d565b0361203057517f17efbd6b000000000000000000000000000000000000000000000000000000008152fd5b517fa6d649c3000000000000000000000000000000000000000000000000000000008152fd5b34610547576020807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261054757600435612092816145b8565b9061209b613eae565b906120d36120ce6120b4835f52600e60205260405f2090565b5473ffffffffffffffffffffffffffffffffffffffff1690565b614378565b9261210f6120f6600b5473ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff1690565b8560405180927f73d9c94d000000000000000000000000000000000000000000000000000000008252818061214c88600483019190602083019252565b03915afa8015610f5a5773ffffffffffffffffffffffffffffffffffffffff915f91612370575b501661217e906142db565b9061218890613f3e565b9161219290614234565b936040519485948786016121ca90600e907f7b2267656e65726174696f6e223a00000000000000000000000000000000000081520190565b6121d391613e0b565b7f2c22646e61223a22000000000000000000000000000000000000000000000000815260080161220291613e0b565b7f222c2263726561746f72223a22000000000000000000000000000000000000008152600d0161223191613e0b565b7f222c2272656e6465726572223a220000000000000000000000000000000000008152600e0161226091613e0b565b7f222c2272656e64657265725f616269223a2272656e646572555249286279746581527f73333229222c22746f6b656e5f6964223a00000000000000000000000000000060208201526031016122b591613e0b565b7f7d0000000000000000000000000000000000000000000000000000000000000081526001015b03907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe091828101825261230f9082612b13565b61231890614d4b565b6040517f646174613a6170706c69636174696f6e2f6a736f6e3b6261736536342c00000093810193845292839190601d0161235291613e0b565b0390810182526123629082612b13565b60405161063d819282610946565b6123879150873d8911611c3b57611c2c8183612b13565b5f612173565b34610547575f807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126108df5760405190806006549060019180831c92808216928315612462575b6020928386108514610a7b578588526020880194908115610a3c575060011461240a5761063d876109d881890382612b13565b60065f5294509192917ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f5b83861061245157505050910190506109d88261063d5f806109c8565b805485870152948201948101612435565b93607f16936123d7565b346105475760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126105475761063d6124ba6004356124ad81610bcc565b6044359060243590615c91565b60405191829182611c42565b34610547575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261054757602073ffffffffffffffffffffffffffffffffffffffff600b5416604051908152f35b346105475760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610547576020612553600435614644565b6040519015158152f35b346105475760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261054757612594614397565b5061063d6125a36004356143d2565b60405191829182919091608060a08201938051835260208101516020840152604081015160408401528173ffffffffffffffffffffffffffffffffffffffff91826060820151166060860152015116910152565b8015150361054757565b346105475760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126105475760043561263c81610bcc565b73ffffffffffffffffffffffffffffffffffffffff6024359161265e836125f7565b335f52600a6020526126918160405f209073ffffffffffffffffffffffffffffffffffffffff165f5260205260405f2090565b921515927fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0081541660ff851617905560405192835216907f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c3160203392a3005b34610547575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261054757612726613cf9565b610bca614f5f565b6040807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261054757600490813561276781610bcc565b602435915f5460ff8160a01c1661277d8161178d565b80860361279157505050610bca9250614a2f565b61279e819594939561178d565b600381036127db575050505060015442105f14611f1c57517fb35ba98d000000000000000000000000000000000000000000000000000000008152fd5b6127e8819593949561178d565b60028103612840575073ffffffffffffffffffffffffffffffffffffffff1633036128185750610bca9250614a2f565b8390517fc5ad1c2d000000000000000000000000000000000000000000000000000000008152fd5b82600182612005899461178d565b60207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261054757600480355f5460ff8160a01c1661288e8161178d565b8084036128a1575050610bca9150614821565b6128aa8161178d565b6003810361295e5750505060015442105f146128e8576040517fb35ba98d000000000000000000000000000000000000000000000000000000008152fd5b5061292b740400000000000000000000000000000000000000007fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff5f5416175f55565b604051600481527fb7b9fda281e81cfc174d175e2b3d01a519b60d7133817213012066742bf27749908060208101611f8c565b6129678161178d565b600281036129bf575073ffffffffffffffffffffffffffffffffffffffff16330361299657610bca9150614821565b506040517fc5ad1c2d000000000000000000000000000000000000000000000000000000008152fd5b6001816129cc869361178d565b036129f9576040517f17efbd6b000000000000000000000000000000000000000000000000000000008152fd5b6040517fa6d649c3000000000000000000000000000000000000000000000000000000008152fd5b34610547575f807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126108df5760ff815460a01c169060058210156108b257602060018314838115612a7b575b506040519015158152f35b9050612a868161178d565b1582612a70565b34610547575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126105475760206040515f8152f35b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b6020810190811067ffffffffffffffff821117612b0e57604052565b612ac5565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff821117612b0e57604052565b6040519060a0820182811067ffffffffffffffff821117612b0e57604052565b67ffffffffffffffff8111612b0e57601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200190565b60807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261054757600435612be481610bcc565b602435612bf081610bcc565b6064359167ffffffffffffffff8311610547573660238401121561054757826004013591612c1d83612b74565b92612c2b6040519485612b13565b8084523660248287010111610547576020815f926024610bca98018388013785010152604435916155c7565b346105475760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261054757600435612cab6120f6600b5473ffffffffffffffffffffffffffffffffffffffff1690565b90602060405180937f73d9c94d0000000000000000000000000000000000000000000000000000000082528180612cea86600483019190602083019252565b03915afa918215610f5a5773ffffffffffffffffffffffffffffffffffffffff925f92612d53928492612d97575b506040519485809481937fb93e4881000000000000000000000000000000000000000000000000000000008352600483019190602083019252565b0392165afa8015610f5a5761063d915f91612d76575b5060405191829182610946565b612d91913d8091833e612d898183612b13565b810190613dac565b5f612d69565b612db091925060203d8111611c3b57611c2c8183612b13565b905f612d18565b3461054757612dc5366112c7565b90612dcf8261457c565b612ddc6040519182612b13565b828152612de88361457c565b916020937fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe085840194013685375f5b818110612e61575050509060405192839281840190828552518091526040840192915f5b828110612e4a57505050500390f35b835185528695509381019392810192600101612e3b565b80612e78612e726001938587614594565b3561452e565b612e8282876145a4565b5201612e17565b612e92366112c7565b5f5460ff8160a01c16612ea48161178d565b60048103612eb7575050610bca91614a42565b612ec08161178d565b60038103612f43575050505060015442105f14612f015760046040517fb35ba98d000000000000000000000000000000000000000000000000000000008152fd5b61292b740400000000000000000000000000000000000000007fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff5f5416175f55565b612f4c8161178d565b60028103612fa4575073ffffffffffffffffffffffffffffffffffffffff163303612f7a57610bca91614a42565b60046040517fc5ad1c2d000000000000000000000000000000000000000000000000000000008152fd5b80612fb060019261178d565b03612fdf5760046040517f17efbd6b000000000000000000000000000000000000000000000000000000008152fd5b60046040517fa6d649c3000000000000000000000000000000000000000000000000000000008152fd5b346105475760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610547576080613045600435615bc3565b613097604051809262ffffff6060809273ffffffffffffffffffffffffffffffffffffffff815116855267ffffffffffffffff6020820151166020860152604081015115156040860152015116910152565bf35b602090816040818301928281528551809452019301915f5b8281106130bf575050505090565b83511515855293810193928101926001016130b1565b34610547576130e3366112c7565b906130ed826145ff565b905f92835b818110613107576040518061063d8682613099565b613112818385614594565b6040517f9e4c0be3000000000000000000000000000000000000000000000000000000008152903560048201529060208083602481305afa8015610f5a57600193613171928992613177575b505061316a83886145a4565b9015159052565b016130f2565b6131969250803d1061319d575b61318e8183612b13565b810190613d77565b5f8061315e565b503d613184565b346105475760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610547576004356131df81610bcc565b6131e7613cf9565b73ffffffffffffffffffffffffffffffffffffffff8061321f6120f6600b5473ffffffffffffffffffffffffffffffffffffffff1690565b1661336d578116906040517f01ffc9a70000000000000000000000000000000000000000000000000000000081526020818061328260048201907f8646df8200000000000000000000000000000000000000000000000000000000602083019252565b0381865afa908115610f5a575f9161334f575b50156132e057610bca8273ffffffffffffffffffffffffffffffffffffffff167fffffffffffffffffffffffff0000000000000000000000000000000000000000600b541617600b55565b6040517fe4786fd500000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff9190911660048201527f8646df82000000000000000000000000000000000000000000000000000000006024820152604490fd5b613367915060203d811161319d5761318e8183612b13565b5f613295565b60046040517f0dc149f0000000000000000000000000000000000000000000000000000000008152fd5b34610547576020807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610547576004356133da6133d6826152fd565b1590565b613902576133e6613eae565b6133f8825f52600d60205260405f2090565b54906134126120ce6120b4845f52600e60205260405f2090565b926134356120f6600b5473ffffffffffffffffffffffffffffffffffffffff1690565b938560405180967f73d9c94d000000000000000000000000000000000000000000000000000000008252818061347389600483019190602083019252565b03915afa948515610f5a576135129573ffffffffffffffffffffffffffffffffffffffff915f916138e5575b5016936134b46134ae866142db565b93613f3e565b935f6134bf83614234565b926134d86120ce6120b4835f52600e60205260405f2090565b9760405180809b81947fb93e4881000000000000000000000000000000000000000000000000000000008352600483019190602083019252565b03915afa968715610f5a575f976138ca575b5060405196879689880161355c90600e907f7b2267656e65726174696f6e223a00000000000000000000000000000000000081520190565b6135669084613e0b565b7f2c22646e61223a2200000000000000000000000000000000000000000000000081526008016135969085613e0b565b7f222c2263726561746f72223a22000000000000000000000000000000000000008152600d016135c591613e0b565b7f222c2272656e6465726572223a220000000000000000000000000000000000008152600e016135f59086613e0b565b7f222c2272656e64657265725f616269223a2272656e646572555249286279746581527f73333229222c22746f6b656e5f6964223a000000000000000000000000000000602082015260310161364b9087613e0b565b7f2c226e616d65223a224f70656e41766174617220230000000000000000000000815260150161367b9087613e0b565b7f222c226465736372697074696f6e223a224f70656e417661746172206973206181527f6e206f6e636861696e2070726f746f636f6c20666f7220417661746172732e2260208201527f2c22696d616765223a22000000000000000000000000000000000000000000006040820152604a016136f691613e0b565b7f222c2261747472696275746573223a5b7b2274726169745f74797065223a224781527f656e65726174696f6e222c2276616c7565223a00000000000000000000000000602082015260330161374b91613e0b565b7f2c22646973706c61795f74797065223a226e756d626572227d2c7b227472616981527f745f74797065223a22444e41222c2276616c7565223a2200000000000000000060208201526037016137a091613e0b565b7f227d2c7b2274726169745f74797065223a2243726561746f72222c2276616c7581527f65223a220000000000000000000000000000000000000000000000000000000060208201526024016137f591613e0b565b7f227d2c7b2274726169745f74797065223a2252656e6465726572222c2276616c81527f7565223a22000000000000000000000000000000000000000000000000000000602082015260250161384a91613e0b565b7f227d2c7b2274726169745f74797065223a22546f6b656e204944222c2276616c81527f7565223a00000000000000000000000000000000000000000000000000000000602082015260240161389f91613e0b565b7f2c22646973706c61795f74797065223a226e756d626572227d5d7d00000000008152601b016122dc565b6138de91973d8091833e612d898183612b13565b955f613524565b6138fc9150883d8a11611c3b57611c2c8183612b13565b5f61349f565b60046040517fa14c4b50000000000000000000000000000000000000000000000000000000008152fd5b346105475760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261054757613963614397565b5061063d6125a36139756004356145b8565b6143d2565b34610547575f807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126108df5760ff815460a01c169060058210156108b25760208260405190158152f35b34610547575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610547576139fd613cf9565b610bca614766565b3461054757613a13366112c7565b90613a1d826145ff565b915f5b818110613a35576040518061063d8682611c42565b80613a46611a316001938587614594565b613a5082876145a4565b5201613a20565b346105475760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261054757602060ff613ae9600435613a9981610bcc565b73ffffffffffffffffffffffffffffffffffffffff60243591613abb83610bcc565b165f52600a845260405f209073ffffffffffffffffffffffffffffffffffffffff165f5260205260405f2090565b54166040519015158152f35b346105475760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261054757600435600581101561054757610bca90613b3c613cf9565b614ea5565b346105475760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261054757600435613b7c81610bcc565b613b84613cf9565b73ffffffffffffffffffffffffffffffffffffffff8091168015613bf5575f918254827fffffffffffffffffffffffff00000000000000000000000000000000000000008216178455167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08380a380f35b60846040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201527f64647265737300000000000000000000000000000000000000000000000000006064820152fd5b346105475760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261054757613cb0613cf9565b610bca60043561467e565b34610547575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261054757613cf1613cf9565b610bca614fff565b73ffffffffffffffffffffffffffffffffffffffff5f54163303613d1957565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602060248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152fd5b908160209103126105475751610957816125f7565b6040513d5f823e3d90fd5b90816020910312610547575161095781610bcc565b6020818303126105475780519067ffffffffffffffff8211610547570181601f82011215610547578051613ddf81612b74565b92613ded6040519485612b13565b818452602082840101116105475761095791602080850191016108e2565b90613e1e602092828151948592016108e2565b0190565b60405190613e2f82612af2565b5f8252565b604051906040820182811067ffffffffffffffff821117612b0e576040526001825260203681840137565b90613e6982612b74565b613e766040519182612b13565b8281527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0613ea48294612b74565b0190602036910137565b5f613eb7613e34565b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff602183015b0190600a907f30313233343536373839616263646566000000000000000000000000000000008282061a835304908115613f39577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90613ede565b505090565b805f917a184f03e93ff9f4daa797ed6e38ed64bf6a1f0100000000000000008082101561409d575b506d04ee2d6d415b85acef81000000008083101561408e575b50662386f26fc100008083101561407f575b506305f5e10080831015614070575b5061271080831015614061575b506064821015614051575b600a80921015614047575b600190816021613fd4828701613e5f565b95860101905b613fe6575b5050505090565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff849101917f30313233343536373839616263646566000000000000000000000000000000008282061a83530491821561404257919082613fda565b613fdf565b9160010191613fc3565b9190606460029104910191613fb8565b6004919392049101915f613fad565b6008919392049101915f613fa0565b6010919392049101915f613f91565b6020919392049101915f613f7f565b60409350810491505f613f66565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b908160021b917f3fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff811603611a8e57565b81810292918115918404141715611a8e57565b9060028201809211611a8e57565b9060208201809211611a8e57565b91908201809211611a8e57565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b80511561417e5760200190565b614144565b80516001101561417e5760210190565b90815181101561417e570160200190565b8015611a8e577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190565b156141d657565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602060248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e746044820152fd5b604051906080820182811067ffffffffffffffff821117612b0e57604052604282526060366020840137603061426983614171565b53607861427583614183565b536041905b6001821161428d576109579150156141cf565b600f811690601082101561417e577f30313233343536373839616263646566000000000000000000000000000000006142d5921a6142cb8486614193565b5360041c916141a4565b9061427a565b604051906060820182811067ffffffffffffffff821117612b0e57604052602a82526040366020840137603061431083614171565b53607861431c83614183565b536029905b60018211614334576109579150156141cf565b600f811690601082101561417e577f3031323334353637383961626364656600000000000000000000000000000000614372921a6142cb8486614193565b90614321565b73ffffffffffffffffffffffffffffffffffffffff61095791166142db565b6040519060a0820182811067ffffffffffffffff821117612b0e576040525f6080838281528260208201528260408201528260608201520152565b6143da614397565b506143e76133d6826152fd565b6144fb576143f48161452e565b6144096120b4825f52600e60205260405f2090565b9061442c6120f6600b5473ffffffffffffffffffffffffffffffffffffffff1690565b91602060405180947f73d9c94d000000000000000000000000000000000000000000000000000000008252818061446b87600483019190602083019252565b03915afa908115610f5a57610957935f926144d9575b506144bc919261448f612b54565b955f875260208701526040860152606085019073ffffffffffffffffffffffffffffffffffffffff169052565b73ffffffffffffffffffffffffffffffffffffffff166080830152565b6144bc92506144f59060203d8111611c3b57611c2c8183612b13565b91614481565b6040517f3374f12d0000000000000000000000000000000000000000000000000000000081526004810191909152602490fd5b614537816152fd565b1561454b575f52600d60205260405f205490565b602490604051907f3374f12d0000000000000000000000000000000000000000000000000000000082526004820152fd5b67ffffffffffffffff8111612b0e5760051b60200190565b919081101561417e5760051b0190565b805182101561417e5760209160051b010190565b805f52600c60205260405f20549081156145d0575090565b6145d9826152fd565b1580156145e9575b611536575090565b50815f52600d6020528060405f205414156145e1565b906146098261457c565b6146166040519182612b13565b8281527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0613ea4829461457c565b805f52600c60205260405f2054908161467757815f52600d60205260405f20540361467257610957906152fd565b505f90565b5050600190565b60105490818111806146fb575b6110d057818110806146ec575b6110d0578082146146e8577fc10030c49484f3ab8e0592f7c3fa8bb25d7edc84f90e0c3c4cfa2f1a935a1a96916146ce82601055565b60408051918252602082019290925290819081015b0390a1565b5050565b5060ff60115460081c16614698565b5060ff60115460101c1661468b565b60115460ff8116614763577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001176011557f92f423b64ccb1952ece9fb716ca1c093997b4166e58a847914759cf448952d795f80a1565b50565b60115460ff8160081c16614763577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff16610100176011557f43068954c894701a7b2c81514382ecbe1248b01dfab74501eba68600391d88215f80a1565b60115460ff8160101c16614763577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffff1662010000176011557f65bc4645b2477bed6ac9e1e602bf139ff267541d54b647053a449b766c3d5adf5f80a1565b6010543410614836576148349033614860565b565b60046040517f580ec48c000000000000000000000000000000000000000000000000000000008152fd5b906040517f9e4c0be30000000000000000000000000000000000000000000000000000000081526020818061489d85600483019190602083019252565b0381305afa908115610f5a575f91614a11575b506149de576003546004549003916148ce611026600f5461ffff1690565b8310156149b4576146e3838361490d7f103a2d32aec953695f3b9ec5ed6c1c6cb822debe92cf1fcf0832cb2c262c7eec965f52600d60205260405f2090565b5580614921855f52600c60205260405f2090565b5561497883614938865f52600e60205260405f2090565b9073ffffffffffffffffffffffffffffffffffffffff167fffffffffffffffffffffffff0000000000000000000000000000000000000000825416179055565b6149818361582e565b6040519384938460409194939273ffffffffffffffffffffffffffffffffffffffff606083019616825260208201520152565b60046040517ff58f733a000000000000000000000000000000000000000000000000000000008152fd5b6040517ff852d0e90000000000000000000000000000000000000000000000000000000081526004810191909152602490fd5b614a29915060203d811161319d5761318e8183612b13565b5f6148b0565b9060105434106148365761483491614860565b90614a4f81601054614108565b3410614836576148349133614a8c565b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8201918211611a8e57565b6003546004549294939290038215614cb75760148311614c8557614ab8614ab38483614137565b614a5f565b614ac8611026600f5461ffff1690565b11156149b4575f805b848110614ae6575050506148349293506159f5565b614af1818689614594565b358015614c525760409081517f9e4c0be30000000000000000000000000000000000000000000000000000000081526020600491818180614b39878783019190602083019252565b0381305afa918215610f5a578792614c35575b5050614c00575082917f103a2d32aec953695f3b9ec5ed6c1c6cb822debe92cf1fcf0832cb2c262c7eec91614bf7614b8660019689614137565b9282614b9a855f52600d60205260405f2090565b5583614bae845f52600c60205260405f2090565b55614bc58a614938855f52600e60205260405f2090565b519283928a8460409194939273ffffffffffffffffffffffffffffffffffffffff606083019616825260208201520152565b0390a101614ad1565b82517ff852d0e90000000000000000000000000000000000000000000000000000000081529081019182529081906020010390fd5b614c4b9250803d1061319d5761318e8183612b13565b5f80614b4c565b6040517fdb5fe4350000000000000000000000000000000000000000000000000000000081526004810191909152602490fd5b6040517f722c972300000000000000000000000000000000000000000000000000000000815260048101849052602490fd5b5050509050565b9190614ccc82601054614108565b34106148365761483492614a8c565b604051906060820182811067ffffffffffffffff821117612b0e57604052604082527f6768696a6b6c6d6e6f707172737475767778797a303132333435363738392b2f6040837f4142434445464748494a4b4c4d4e4f505152535455565758595a61626364656660208201520152565b805115614e9c57614d5a614cdb565b614d76614d71614d6a845161411b565b6003900490565b6140d8565b91614d88614d8384614129565b613e5f565b92835280815182019060208501935b828210614e4a57505050600390510680600114614e0157600214614db9575090565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f3d0000000000000000000000000000000000000000000000000000000000000091015290565b507ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe7f3d3d00000000000000000000000000000000000000000000000000000000000091015290565b9091936004906003809401938451600190603f9082828260121c16880101518553828282600c1c16880101518386015382828260061c1688010151600286015316850101519082015301939190614d97565b50610957613e22565b60ff600254166110d0575f5460ff8160a01c16614ec18161178d565b614eca8361178d565b808314614f5a57614eda8161178d565b15612fdf576005821015611797577fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff1660a082901b74ff000000000000000000000000000000000000000016175f556040517fb7b9fda281e81cfc174d175e2b3d01a519b60d7133817213012066742bf277499181906146e390826117c4565b505050565b60025460ff8116614763577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001176002557ff84b894acb3362a642fb7cc6c5cf8ae86d8f634a92f4ac0f28de735d928c22165f80a1565b60ff60025460081c166110d0578060015414614763576020817f3a40c106e74201e60ef08074000ad1108833249acdc0529b0438bb56072d85ed92600155604051908152a1565b60025460ff8160081c16614763577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff16610100176002557f71cace190dc42826c4dec7d6d422424e12928fa2fcc540dab75f13e6380a413f5f80a1565b90816020910312610547575190565b9091615075613cf9565b604051907f02571be30000000000000000000000000000000000000000000000000000000082526020938483806150d360048201907f91d1777781884d03a6757a803996e38de2a42967fb37eeaca72729271025a9e2602083019252565b038173ffffffffffffffffffffffffffffffffffffffff8098165afa908115610f5a5761513b9486945f93615168575b505f906040518097819682957fc47f002700000000000000000000000000000000000000000000000000000000845260048401615189565b0393165af1918215610f5a575f9261515257505090565b6109579250803d10611c0c57611bfd8183612b13565b5f91935061518290863d8811611c3b57611c2c8183612b13565b9290615103565b90601f836040947fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09360208652816020870152868601375f8582860101520116010190565b73ffffffffffffffffffffffffffffffffffffffff168015615203575f52600860205267ffffffffffffffff60405f20541690565b60046040517f8f4eb604000000000000000000000000000000000000000000000000000000008152fd5b61524b73ffffffffffffffffffffffffffffffffffffffff9161524f565b1690565b806003548110615284575b60046040517fdf2d9b42000000000000000000000000000000000000000000000000000000008152fd5b5f52600760205260405f2054907c0100000000000000000000000000000000000000000000000000000000821661525a575b81156152c0575090565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9150016152f6815f52600760205260405f2090565b54906152b6565b6003548110908161530c575090565b90505f5260076020527c010000000000000000000000000000000000000000000000000000000060405f2054161590565b906153478361524f565b73ffffffffffffffffffffffffffffffffffffffff80841692838284160361559d575f868152600960205260409020805490926153a073ffffffffffffffffffffffffffffffffffffffff881633908114908414171590565b615512575b82169586156154e85761542a936153e0926154df575b5073ffffffffffffffffffffffffffffffffffffffff165f52600860205260405f2090565b80547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01905573ffffffffffffffffffffffffffffffffffffffff165f52600860205260405f2090565b805460010190557c0200000000000000000000000000000000000000000000000000000000804260a01b851717615469865f52600760205260405f2090565b55811615615499575b507fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef5f80a4565b600184016154af815f52600760205260405f2090565b54156154bc575b50615472565b60035481146154b6576154d7905f52600760205260405f2090565b555f806154b6565b5f90555f6153bb565b60046040517fea553b34000000000000000000000000000000000000000000000000000000008152fd5b61556e6133d6615567336155448b73ffffffffffffffffffffffffffffffffffffffff165f52600a60205260405f2090565b9073ffffffffffffffffffffffffffffffffffffffff165f5260205260405f2090565b5460ff1690565b156153a55760046040517f59c896be000000000000000000000000000000000000000000000000000000008152fd5b60046040517fa1148100000000000000000000000000000000000000000000000000000000008152fd5b9291906155d582828661533d565b803b6155e2575b50505050565b6155eb936157d8565b156155f9575f8080806155dc565b60046040517fd1a57ed6000000000000000000000000000000000000000000000000000000008152fd5b9081602091031261054757516109578161051d565b610957939273ffffffffffffffffffffffffffffffffffffffff6080931682525f602083015260408201528160608201520190610903565b9092610957949360809373ffffffffffffffffffffffffffffffffffffffff809216845216602083015260408201528160608201520190610903565b3d156156d6573d906156bd82612b74565b916156cb6040519384612b13565b82523d5f602084013e565b606090565b61573160209173ffffffffffffffffffffffffffffffffffffffff93945f6040519586809581947f150b7a02000000000000000000000000000000000000000000000000000000009a8b84523360048501615638565b0393165af15f91816157a8575b506157825761574b6156ac565b8051908161577d5760046040517fd1a57ed6000000000000000000000000000000000000000000000000000000008152fd5b602001fd5b7fffffffff00000000000000000000000000000000000000000000000000000000161490565b6157ca91925060203d81116157d1575b6157c28183612b13565b810190615623565b905f61573e565b503d6157b8565b92602091615731935f73ffffffffffffffffffffffffffffffffffffffff6040518097819682957f150b7a02000000000000000000000000000000000000000000000000000000009b8c85523360048601615670565b60409081519161583d83612af2565b5f9283815260035461586d8473ffffffffffffffffffffffffffffffffffffffff165f52600860205260405f2090565b68010000000000000001815401905573ffffffffffffffffffffffffffffffffffffffff8416907c02000000000000000000000000000000000000000000000000000000004260a01b8317176158cb825f52600760205260405f2090565b55600191828201917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908289838180a483835b8481036159e657505050156159bd57600355833b61591e575b5050505050565b600354937fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff85019180805b615965575b505050505050600354036108df5780808080615917565b156159b0575b8661597d6133d68684870196866156db565b6159875781615949565b600486517fd1a57ed6000000000000000000000000000000000000000000000000000000008152fd5b85831061596b578061594e565b600484517f2e076300000000000000000000000000000000000000000000000000000000008152fd5b80848b858180a40184906158fe565b9060408051615a0381612af2565b5f93848252600354908415615b6557615a3a8173ffffffffffffffffffffffffffffffffffffffff165f52600860205260405f2090565b680100000000000000018602815401905560019173ffffffffffffffffffffffffffffffffffffffff821683871460e11b4260a01b178117615a84835f52600760205260405f2090565b558682019184807fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9280858d868180a4015b848103615b565750505015615b2d57600355803b615ad7575b505050505050565b6003549485039180805b615afe575b505050505050600354036108df578080808080615acf565b15615b20575b86615b166133d68684870196866156db565b6159875781615ae1565b858310615b045780615ae6565b600485517f2e076300000000000000000000000000000000000000000000000000000000008152fd5b80848c858180a4018590615ab6565b600484517fb562e8dd000000000000000000000000000000000000000000000000000000008152fd5b604051906080820182811067ffffffffffffffff821117612b0e576040525f6060838281528260208201528260408201520152565b615bcb615b8e565b50615bd4615b8e565b600354821015615c0c5750615be881615c11565b6040810151615c0c5750615c0761095791615c01615b8e565b5061524f565b615c2a565b905090565b615c19615b8e565b505f52600760205261095760405f20545b90615c33615b8e565b9173ffffffffffffffffffffffffffffffffffffffff8116835267ffffffffffffffff8160a01c1660208401527c010000000000000000000000000000000000000000000000000000000081161515604084015260e81c6060830152565b9082811015615dcc575f91600354808511615dc4575b50615cb1816151ce565b84831015615dbd57828503818110615db5575b505b615ccf816145ff565b958115615dad57615cdf84615bc3565b918594604093615cf46133d686830151151590565b615d8e575b505b8781141580615d84575b15615d7757615d1381615c11565b80850151615d6e575173ffffffffffffffffffffffffffffffffffffffff90811680615d65575b509081600192871690881614615d51575b01615cfb565b80615d5f838a01998c6145a4565b52615d4b565b96506001615d3a565b50600190615d4b565b5050959450505050815290565b5081871415615d05565b5173ffffffffffffffffffffffffffffffffffffffff1695505f615cf9565b945050505050565b90505f615cc4565b5082615cc6565b93505f615ca7565b60046040517f32c1995a000000000000000000000000000000000000000000000000000000008152fdfea2646970667358221220f080637745391c98b44ec28b348e4547aae59fc52b1ca8937710004ded36daa464736f6c63430008140033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
00000000000000000000000000000000000027390b412440c58100929acfeae2
-----Decoded View---------------
Arg [0] : ownerProxy (address): 0x00000000000027390b412440C58100929AcfEAe2
-----Encoded View---------------
1 Constructor Arguments found :
Arg [0] : 00000000000000000000000000000000000027390b412440c58100929acfeae2
Loading...
Loading
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.