@niftory/niftory-solidity v0.0.5
Niftory Smart Contracts for EVM Compatible Blockchains
This repository hosts all smart contracts (and related scripts, tests, etc.) for Niftory, an NFT management platform. Niftory NFTs are ERC1155 compatible.
Note: This README is autogenerated. Please modify docs from within the docs folder and run yarn build to regenerate.
Usage
In order to use the contract, just extend one of the contracts from the impl folder. Currently, there is only NiftoryStandardERC1155.
Typed APIs, generated with typechain, can be accessed via src/bindings. NiftoryStandardERC1155Interface from here is the best place to see the full accessible API in one place.
Overview
Each uniquely identifiable ERC1155 token maps to a unique set (via setId) and template (via templateId) pair. The IERC1155 token id (e.g. for the uri function) can be expressed as (setId << 64) + templateId, as setId and templateId are both 64 bits each.
A contract can have multiple sets, and each set can have multiple templates. Indexing for both starts at 1 (so there is no set with setId=0 or template with templateId=0 in any set).
Each template maps to exactly one uri, which can be modified by an Admin or SetAdmin (see next section). templates also keep track of how many NFTs have been minted for that template and how many are allowed to minted (maxMint). A template with a maxMint of 0 allows unlimited minting. A maxMint may be set later, however.
sets can be locked to prevent more templates from being added, and templates can have metadata modification and minting locked separately.
sets allow Admins to delegate control to a collection of templates to a SetAdmin, allowing the SetAdmin to lock and mint from sets it owns.
Roles and Actions
This contract uses a very simple RBAC style management with three different roles, as below.
Admin
An Admin is the superuser of the contract. An Admin can
- Assign (or revoke) more Admins
- Assign (or revoke) SetAdmins
- Create & lock sets
- Add templates to any set
- Mint from any mintable template
- Change the metadata for any template
- Modify the maximum mint amount for any template
- Lock metadata and minting for any template
- Modify the global default URI
Set Admin
A SetAdmin is an adminstrator of sets it has been given power over, either from an Admin or another previous SetAdmin. A SetAdmin can
- Assign (or revoke) SetAdmins from the sets it owns
- Lock sets it owns
- Add templates to sets it owns
- Mint from mintable templates of sets it owns
- Change the metadata of templates of sets it owns
- Modify the maximum mint amount for templates of sets it owns
- Lock metadata and minting for templates of sets it owns
Collector
Collector is another way of saying "no role" and can be anyone with an account on the blockchain. A Collector can
- Hold and transfer tokens
- Approve another account to manage tokens
- Burn tokens
All the above functionality is provided by OpenZeppelin's ERC1155 implementation.
Architecture
There are three extensions that can be used to power this token, which all assume that an ERC1155 token uses the set/template scheme mentioned above.
The main NiftoryStandardERC1155 uses OpenZeppelin's ERC1155Burnable.sol as a base. There are three custom extensions used, which can be found in contracts/extensions
UriManagermanages URI's for templates and allows modification. If the URI is blank, it will use the default_defaultUri.RoleManagermanages the Admin and SetAdmin based ownership.RoleManagermaps an(address, setId)pair to a boolean value indicating whetheraddressis a SetAdmin of setsetId. The 0-th bit is used to indicate an Admin. This extension uses a custom library (lib/AddressIndexedBitmap.sol) in order to tightly pack the boolean role indicatorTemplateManageris broken into two parts - theSetManagerand the derivingTemplateManager. TheSetManagerstoresSets in asetId => Setmap. ASetneeds to keep track of how many templates it contains, and also allows locking to stop more templates from being added.TemplateManagermanages eachTemplate, keeping track of what the max mint is, how many NFTs have been minted from it, whether metadata can still be modified, and whether minting is still unlocked.
There are also two libraries in the lib folder - AddressedIndexedBitmap.sol and TemplateBasedId.sol. AddressedIndexedBitmap was already mentioned above, but TemplateBasedId provides a 1-1 mapping from (uint64 setId, uint64 templateId) <=> uint256 id), in order to adhere to IERC1155.
Development
yarn- install all node dependenciesyarn clean- remove all hardhat generated filesyarn compile- compile Solidity files and generate docsyarn build- clean and compileyarn test- build and run tests (and show gas usage information)yarn coverage- generate coverage reports
Solidity API
MetadataManager
State
struct State {
string _contractUri;
string tokenUri;
mapping(uint256 => string) tokenUriOverrides;
struct LockManager.State locks;
}contractUri
function contractUri(struct MetadataManager.State self) internal view returns (string)uri
function uri(struct MetadataManager.State self, uint64 setId, uint64 templateId, uint64 serialNumber) internal view returns (string)modifyContractUri
function modifyContractUri(struct MetadataManager.State self, string newContractUri) internalmodifyTokenUri
function modifyTokenUri(struct MetadataManager.State self, string newTokenUri) internalmodifySetOverrides
function modifySetOverrides(struct MetadataManager.State self, uint64[] setIds, string[] newTokenUris) internalmodifyTemplateOverrides
function modifyTemplateOverrides(struct MetadataManager.State self, uint64 setId, uint64[] templateIds, string[] newTokenUris) internalmodifySerialOverrides
function modifySerialOverrides(struct MetadataManager.State self, uint64 setId, uint64 templateId, uint64[] serialNumbers, string[] newTokenUris) internallockContract
function lockContract(struct MetadataManager.State self) internallockSets
function lockSets(struct MetadataManager.State self, uint64[] setIds) internallockSetRange
function lockSetRange(struct MetadataManager.State self, uint64 setIdStart, uint64 setIdEnd) internallockTemplates
function lockTemplates(struct MetadataManager.State self, uint64 setId, uint64[] templateIds) internallockTemplateRange
function lockTemplateRange(struct MetadataManager.State self, uint64 setId, uint64 templateIdStart, uint64 templateIdEnd) internal_getBestUri
function _getBestUri(struct MetadataManager.State self, uint64 setId, uint64 templateId, uint64 serialNumber) private view returns (string)MintManager
Template
struct Template {
mapping(uint256 => uint256) balances;
uint64 minted;
uint64 maxMint;
}Set
struct Set {
uint64 numTemplates;
}State
struct State {
mapping(uint256 => struct MintManager.Set) sets;
mapping(uint256 => struct MintManager.Template) templates;
struct LockManager.State locks;
uint64 numSets;
}addressHasToken
function addressHasToken(struct MintManager.State self, address addr, uint64 setId, uint64 templateId, uint64 serialNumber) internal view returns (bool)doesSetExist
function doesSetExist(struct MintManager.State self, uint64 setId) internal view returns (bool)doesTemplateExist
function doesTemplateExist(struct MintManager.State self, uint64 setId, uint64 templateId) internal view returns (bool)checkSetExists
function checkSetExists(struct MintManager.State self, uint64 setId) internal viewcheckTemplateExists
function checkTemplateExists(struct MintManager.State self, uint64 setId, uint64 templateId) internal viewnumberOfSets
function numberOfSets(struct MintManager.State self) internal view returns (uint64)numberOfTemplates
function numberOfTemplates(struct MintManager.State self, uint64 setId) internal view returns (uint64)numberMinted
function numberMinted(struct MintManager.State self, uint64 setId, uint64 templateId) internal view returns (uint64)addSets
function addSets(struct MintManager.State self, uint64 numSets) internaladdTemplates
function addTemplates(struct MintManager.State self, uint64 setId, uint64 numTemplates) internalmint
function mint(struct MintManager.State self, address[] to, uint64 setId, uint64[] templateIds, uint64[] amounts) internallockContract
function lockContract(struct MintManager.State self) internallockSets
function lockSets(struct MintManager.State self, uint64[] setIds) internallockTemplates
function lockTemplates(struct MintManager.State self, uint64 setId, uint64[] templateIds) internalsetMaxMints
function setMaxMints(struct MintManager.State self, uint64 setId, uint64[] templateIds, uint64[] maxMints) internaltransfer
function transfer(struct MintManager.State self, address from, address to, uint256[] tokenIds) internalburn
function burn(struct MintManager.State self, address from, uint256[] tokenIds) internalOperatorManager
State
struct State {
mapping(uint256 => bool) operators;
}isApproved
function isApproved(struct OperatorManager.State self, address account, address operator) internal view returns (bool)checkApproved
function checkApproved(struct OperatorManager.State self, address account, address operator) internal viewmodifyApproval
function modifyApproval(struct OperatorManager.State self, address account, address operator, bool approved) internal_operatorKey
function _operatorKey(address account, address operator) private pure returns (uint256)RoleManager
_CONTRACT_ADMIN_BIT
uint64 _CONTRACT_ADMIN_BITState
struct State {
mapping(uint256 => uint256) roles;
}isContractAdmin
function isContractAdmin(struct RoleManager.State self, address addr) internal view returns (bool)isSetAdmin
function isSetAdmin(struct RoleManager.State self, address addr, uint64 setId) internal view returns (bool)checkContractAdmin
function checkContractAdmin(struct RoleManager.State self, address addr) internal viewcheckSetAdmin
function checkSetAdmin(struct RoleManager.State self, address addr, uint64 setId) internal viewgrantContractAdmin
function grantContractAdmin(struct RoleManager.State self, address[] addrs) internalgrantSetAdmin
function grantSetAdmin(struct RoleManager.State self, uint64[] setIds, address[] addrs) internalgrantSetRangeAdmin
function grantSetRangeAdmin(struct RoleManager.State self, uint64 fromSetId, uint64 toSetId, address[] addrs) internalrevokeContractAdmin
function revokeContractAdmin(struct RoleManager.State self, address[] addrs) internalrevokeSetRangeAdmin
function revokeSetRangeAdmin(struct RoleManager.State self, uint64 fromSetId, uint64 toSetId, address[] addrs) internalUpgradeManager
State
struct State {
mapping(address => bool) admins;
}isAdmin
function isAdmin(struct UpgradeManager.State self, address account) internal view returns (bool)checkAdmin
function checkAdmin(struct UpgradeManager.State self, address account) internal viewgrantAdmin
function grantAdmin(struct UpgradeManager.State self, address account) internalrevokeAdmin
function revokeAdmin(struct UpgradeManager.State self, address account) internalNiftoryOptimizedERC1155
AdminsAssigned
event AdminsAssigned(uint64 setId, address[] admins)AdminsRevoked
event AdminsRevoked(uint64 setId, address[] admins)SetsAdded
event SetsAdded(uint64[] setIds)SetsLocked
event SetsLocked(uint64[] setIds)TemplatesAdded
event TemplatesAdded(uint64 setId, uint64[] templateIds)TemplatesMetadataLocked
event TemplatesMetadataLocked(uint64 setId, uint64[] templateIds)SetMetadataLocked
event SetMetadataLocked(uint64 setId)TemplatesMaxMintModified
event TemplatesMaxMintModified(uint64 setId, uint64[] templateIds, uint64[] newMaxes)TemplatesMintingLocked
event TemplatesMintingLocked(uint64 setId, uint64[] templateIds)SetMintingLocked
event SetMintingLocked(uint64 setId)_roleManager
struct RoleManager.State _roleManager_operatorManager
struct OperatorManager.State _operatorManager_mintManager
struct MintManager.State _mintManager_metadataManager
struct MetadataManager.State _metadataManagerconstructor
constructor() publicinitialize
function initialize(address owner, string contractUri, string tokenUri) publicsupportsInterface
function supportsInterface(bytes4 interfaceId) public pure returns (bool)_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._
balanceOf
function balanceOf(address account, uint256 id) public view returns (uint256)_Returns the amount of tokens of token type id owned by account.
Requirements:
accountcannot be the zero address._
balanceOfBatch
function balanceOfBatch(address[] accounts, uint256[] ids) public view returns (uint256[])_xref:ROOT:erc1155.adoc#batch-operationsBatched version of {balanceOf}.
Requirements:
accountsandidsmust have the same length._
setApprovalForAll
function setApprovalForAll(address operator, bool approved) public_Grants or revokes permission to operator to transfer the caller's tokens, according to approved,
Emits an {ApprovalForAll} event.
Requirements:
operatorcannot be the caller._
isApprovedForAll
function isApprovedForAll(address account, address operator) public view returns (bool)_Returns true if operator is approved to transfer account's tokens.
See {setApprovalForAll}._
safeTransferFrom
function safeTransferFrom(address from, address to, uint256 id, uint256 amount, bytes) publicsafeBatchTransferFrom
function safeBatchTransferFrom(address from, address to, uint256[] ids, uint256[] amounts, bytes) publicuri
function uri(uint256 id) public view returns (string)_Returns the URI for token type id.
If the \{id\} substring is present in the URI, it must be replaced by
clients with the actual token type ID._
contractURI
function contractURI() public view returns (string)_checkOwner
function _checkOwner() internal viewThrows if the sender is not the owner.
burn
function burn(uint256[] ids) publicaddSets
function addSets(uint64 numSets) publicaddTemplates
function addTemplates(uint64 setId, uint64 numTemplates) publicmint
function mint(address[] to, uint64 setId, uint64[] templateIds, uint64[] amounts) publiclockMintingForContract
function lockMintingForContract() publiclockMintingForSets
function lockMintingForSets(uint64[] setIds) publiclockMintingForTemplates
function lockMintingForTemplates(uint64 setId, uint64[] templateIds) publicsetMaxMints
function setMaxMints(uint64 setId, uint64[] templateIds, uint64[] maxMints) publicmodifyContractMetadataUri
function modifyContractMetadataUri(string newUri) publicmodifyTokenMetadataUri
function modifyTokenMetadataUri(string newUri) publicmodifySetMetadataOverrides
function modifySetMetadataOverrides(uint64[] setIds, string[] newUris) publicmodifyTemplateMetadataOverrides
function modifyTemplateMetadataOverrides(uint64 setId, uint64[] templateIds, string[] newUris) publicmodifySerialMetadataOverrides
function modifySerialMetadataOverrides(uint64 setId, uint64 templateId, uint64[] serialNumbers, string[] newUris) publiclockMetadataForContract
function lockMetadataForContract() publiclockMetadataForSets
function lockMetadataForSets(uint64[] setIds) publiclockMetadataForTemplates
function lockMetadataForTemplates(uint64 setId, uint64[] templateIds) publicgrantContractAdmin
function grantContractAdmin(address[] admins) publicgrantSetAdmin
function grantSetAdmin(uint64[] setIds, address[] admins) publicgrantSetRangeAdmin
function grantSetRangeAdmin(uint64 fromSetId, uint64 toSetId, address[] admins) publicBeacon
_upgradeManager
struct UpgradeManager.State _upgradeManagerconstructor
constructor(address implementation) public_checkOwner
function _checkOwner() internal viewThrows if the sender is not the owner.
grantAdmin
function grantAdmin(address account) publicrevokeAdmin
function revokeAdmin(address account) publicdeployProxy
function deployProxy(bytes data, bool forceCall) public returns (address proxy)BeaconProxy
_BEACON_SLOT
bytes32 _BEACON_SLOTBeaconUpgraded
event BeaconUpgraded(address beacon)_roleManager
struct RoleManager.State _roleManagerconstructor
constructor(address beacon, bytes data, bool forceCall) public payable_implementation
function _implementation() internal view returns (address)This is a virtual function that should be overridden so it returns the address to which the fallback function and {_fallback} should delegate.
redirectBeaconProxy
function redirectBeaconProxy(address newBeacon, bytes data, bool forceCall) public_setBeacon
function _setBeacon(address newBeacon, bytes data, bool forceCall) privateAddressIndexedBitmap
A bitmap that can be efficiently indexed by an (address, uint64) pair.
Best if used as a decorator for a mapping of (uint256 => uint256)
_ADDRESS_SHIFT
uint256 _ADDRESS_SHIFTgetBit
function getBit(mapping(uint256 => uint256) bitmap, address addr, uint64 index) internal view returns (bool)setBit
function setBit(mapping(uint256 => uint256) bitmap, address addr, uint64 index) internalset the bit at position index for addr in bitmap
Parameters
| Name | Type | Description |
|---|---|---|
| bitmap | mapping(uint256 => uint256) | the underlying bitmap - target of decorator |
| addr | address | the address to index into the bitmap |
| index | uint64 | the bit to set |
setBitRange
function setBitRange(mapping(uint256 => uint256) bitmap, address addr, uint64 start, uint64 end) internalclearBit
function clearBit(mapping(uint256 => uint256) bitmap, address addr, uint64 index) internalclear the bit at position index for addr in bitmap
Parameters
| Name | Type | Description |
|---|---|---|
| bitmap | mapping(uint256 => uint256) | the underlying bitmap - target of decorator |
| addr | address | the address to index into the bitmap |
| index | uint64 | the bit to clear |
clearBitRange
function clearBitRange(mapping(uint256 => uint256) bitmap, address addr, uint64 start, uint64 end) internal_addressKey
function _addressKey(address addr) private pure returns (uint256)ArrayOps
singleton
function singleton(address a) internal pure returns (address[])singleton
function singleton(uint256 a) internal pure returns (uint256[])singleton
function singleton(uint64 a) internal pure returns (uint64[])singleton
function singleton(bool a) internal pure returns (bool[])fill
function fill(address[] arr, address a) internal pureBitmapOps
Library for manipulating bitmaps. A bitmap is a uint256 -> uint256 mapping. It is assumed that the "word selector" is actually 64 bits, but we use 256 bits for the mapping key because we can do more with it. However, this means that we always need to provide the full key separately.
getBit
function getBit(mapping(uint256 => uint256) bitmap, uint256 key, uint64 index, uint256 subWordIndexShift) internal view returns (bool)getBitRangeForWord
function getBitRangeForWord(mapping(uint256 => uint256) bitmap, uint256 key, uint64 start, uint64 end, uint256 subWordIndexShift) internal view returns (uint256)setBit
function setBit(mapping(uint256 => uint256) bitmap, uint256 key, uint64 index, uint256 subWordIndexShift) internalsetBitRange
function setBitRange(mapping(uint256 => uint256) bitmap, uint256 key, uint64 start, uint64 end, uint256 subWordIndexShift) internalclearBit
function clearBit(mapping(uint256 => uint256) bitmap, uint256 key, uint64 index, uint256 subWordIndexShift) internalclearBitRange
function clearBitRange(mapping(uint256 => uint256) bitmap, uint256 key, uint64 start, uint64 end, uint256 subWordIndexShift) internal_wordIndex
function _wordIndex(uint256 key, uint64 index, uint256 subWordIndexShift) private pure returns (uint256)BytesOps
toAsciiBytes
function toAsciiBytes(uint64 value) internal pure returns (bytes)bytesMatchAtOffset
function bytesMatchAtOffset(bytes input, bytes target, uint256 offset) internal pure returns (bool)slice
function slice(bytes input, uint256 start, uint256 end) internal pure returns (bytes)flatten
function flatten(bytes[] input, uint256 numSegments) internal pure returns (bytes)IdCodec
_TEMPLATE_SHIFT
uint256 _TEMPLATE_SHIFT_SET_SHIFT
uint256 _SET_SHIFTDecoded
struct Decoded {
uint64 setId;
uint64 templateId;
uint64 serialNumber;
}encodeAsSetMask
function encodeAsSetMask(uint64 setId) internal pure returns (uint256)encodeAsTemplateMask
function encodeAsTemplateMask(uint64 setId, uint64 templateId) internal pure returns (uint256)encodeAsTokenId
function encodeAsTokenId(uint64 setId, uint64 templateId, uint64 serialNumber) internal pure returns (uint256)decodeSetId
function decodeSetId(uint256 tokenId) internal pure returns (uint64)decodeTemplateId
function decodeTemplateId(uint256 tokenId) internal pure returns (uint64)decodeSerialNumber
function decodeSerialNumber(uint256 tokenId) internal pure returns (uint64)decode
function decode(uint256 tokenId) internal pure returns (struct IdCodec.Decoded decoded)LockManager
This library provides arbitrary bitmap-based locking facilities for Contracts - 0 | 0 | 0 | 0 Sets - 0 | setIdWordIndex | 0 | 0 Templates - 0 | setId | templateIdWordIndex | 0 Serials - 0 | setId | templateId | serialNumberWordIndex In the above table,
- 0 represents a 64 bit word
- setId, templateId, serialNumber are 64 bit numbers
- each value in the mapping
- a 'word' is a 256 bit segment of the bitmap, LSB aligned
- *WordIndex is the 64-bit index of the word in the bitmap (i / 256)
_SET_SHIFT
uint256 _SET_SHIFT_TEMPLATE_SHIFT
uint256 _TEMPLATE_SHIFT_CONTRACT_KEY
uint256 _CONTRACT_KEY_SET_BLANK_KEY
uint256 _SET_BLANK_KEYState
struct State {
mapping(uint256 => uint256) locks;
}isContractLocked
function isContractLocked(struct LockManager.State self) internal view returns (bool)isSetLocked
function isSetLocked(struct LockManager.State self, uint64 setId) internal view returns (bool)isTemplateLocked
function isTemplateLocked(struct LockManager.State self, uint64 setId, uint64 templateId) internal view returns (bool)isSerialLocked
function isSerialLocked(struct LockManager.State self, uint64 setId, uint64 templateId, uint64 serialNumber) internal view returns (bool)checkContractUnlocked
function checkContractUnlocked(struct LockManager.State self) internal viewcheckSetUnlocked
function checkSetUnlocked(struct LockManager.State self, uint64 setId) internal viewcheckTemplateUnlocked
function checkTemplateUnlocked(struct LockManager.State self, uint64 setId, uint64 templateId) internal viewlockContract
function lockContract(struct LockManager.State self) internallockSets
function lockSets(struct LockManager.State self, uint64[] setIds) internalcheckSerialUnlocked
function checkSerialUnlocked(struct LockManager.State self, uint64 setId, uint64 templateId, uint64 serialNumber) internal viewlockSetRange
function lockSetRange(struct LockManager.State self, uint64 start, uint64 end) internallockTemplates
function lockTemplates(struct LockManager.State self, uint64 setId, uint64[] templateIds) internallockTemplateRange
function lockTemplateRange(struct LockManager.State self, uint64 setId, uint64 start, uint64 end) internallockSerials
function lockSerials(struct LockManager.State self, uint64 setId, uint64 templateId, uint64[] serialNumbers) internallockSerialRange
function lockSerialRange(struct LockManager.State self, uint64 setId, uint64 templateId, uint64 start, uint64 end) internal_templateBlankKey
function _templateBlankKey(uint64 setId) private pure returns (uint256)_serialBlankKey
function _serialBlankKey(uint64 setId, uint64 templateId) private pure returns (uint256)_indexToWordIndex
function _indexToWordIndex(uint64 index) private pure returns (uint256)_setIdWordIndex
function _setIdWordIndex(uint64 setId) private pure returns (uint256)_templateIdWordIndex
function _templateIdWordIndex(uint64 setId, uint64 templateId) private pure returns (uint256)_serialNumberWordIndex
function _serialNumberWordIndex(uint64 setId, uint64 templateId, uint64 serialNumber) private pure returns (uint256)UriEncoder
_SET_TAG
bytes _SET_TAG_TEMPLATE_TAG
bytes _TEMPLATE_TAG_SERIAL_TAG
bytes _SERIAL_TAG_MAX_SEGMENTS
uint256 _MAX_SEGMENTSencode
function encode(string uri, uint64 setId, uint64 templateId, uint64 serialNumber) internal pure returns (string)3 years ago