- DApp developers attempting to create an NFT contract should be able to install the contract as is configured for the specific built-in behavior they want their NFT contract instance to have. Must work out of the box.
- Reference implementation must be straightforward, clear, and obvious.
- Externally observable association between
Accounts
and/orContracts
andNFT
s they "own". - Should be well documented with exhaustive tests that prove all possible combinations of defined behavior work as intended.
- Must be entirely self-contained within a singular repo; this includes all code, all tests, all relevant CasperLabs provided SDKs and all relevant documentation.
- Must support mainstream expectations about common NFT conventions.
- A given NFT contract instance must be able to choose when created if it is using a Metadata schema conformant with existing community standards or a specific custom schema that they provide.
- A NFT contract instance must validate provided metadata against the specified metadata schema for that contract.
- Standardized session code to interact with an NFT contract instance must be usable as is, so that a given DApp developer doesn't have to write any Wasm producing logic for normal usage of NFT contract instances produced by this contract.
Please refer to the release notes
The main.rs
file within the contract provides the installer for the NFT contract. Users can compile the contract to Wasm using the make build-contract
command from the Makefile provided.
The pre-built Wasm for the contract and all other utility session code can be found as part of the most current release. Users wishing to build the Wasm themselves can pull the code and use the make build-contract
command provided in the Makefile. Please note, however, that you must install wasm-strip
to build the contract.
The call
method will install the contract with the necessary entrypoints and call the init()
entrypoint, which allows the contract to self-initialize and set up the necessary state variables for operation.
The following are the required runtime arguments that must be passed to the installer session code to correctly install the NFT contract. For more information on the modalities that these arguments set, please refer to the Modalities documentation.
"collection_name":
The name of the NFT collection, passed in as aString
. This parameter is required and cannot be changed post installation."collection_symbol"
: The symbol representing a given NFT collection, passed in as aString
. This parameter is required and cannot be changed post installation."total_token_supply"
: The total number of NFTs that a specific instance of a contract will mint passed in as aU64
value. This parameter is required."ownership_mode"
: TheOwnershipMode
modality that dictates the ownership behavior of the NFT contract. This argument is passed in as au8
value and is required at the time of installation."json_schema"
: The JSON schema for the NFT tokens that will be minted by the NFT contract passed in as aString
. This parameter is required if the metadata kind is set toCustomValidated(3)
and cannot be changed post installation."nft_metadata_kind"
: The base metadata schema for the NFTs to be minted by the NFT contract. This argument is passed in as au8
value and is required at the time of installation."identifier_mode"
: TheNFTIdentifierMode
modality dictates the primary identifier for NFTs minted by the contract. This argument is passed in as au8
value and is required at the time of installation."metadata_mutability"
: TheMetadataMutability
modality dictates whether the metadata of minted NFTs can be updated. This argument is passed in as au8
value and is required at the time of installation.
The following are the optional parameters that can be passed in at the time of installation.
"minting_mode"
: TheMintingMode
modality that dictates the access to themint()
entry-point in the NFT contract. This is an optional parameter that will default to restricting access to the installer of the contract. This parameter cannot be changed once the contract has been installed."allow_minting"
: The"allow_minting"
flag allows the installer of the contract to pause the minting of new NFTs. Theallow_minting
is a boolean toggle that allows minting whentrue
. If not provided at install the toggle will default totrue
. This value can be changed by the installer by calling theset_variables()
entrypoint."nft_kind"
: TheNFTKind
modality that specifies the off-chain items represented by the on-chain NFT data. This argument is passed in as au8
value and is optional at the time of installation. If not provided at install the toggle will default toVirtual
This parameter cannot be changed once the contract has been installed."whitelist_mode"
: TheWhitelistMode
modality dictates whether the contract whitelist can be updated. This optional parameter will default to an unlocked whitelist that can be updated post installation. This parameter cannot be changed once the contract has been installed."holder_mode"
: TheNFTHolderMode
modality dictates which entities can hold NFTs. This is an optional parameter and will default to a mixed mode allowing eitherAccounts
orContracts
to hold NFTs. This parameter cannot be changed once the contract has been installed."acl_whitelist"
: The ACL whitelist is a list of accounts and/or contract/package hashes that specifies which accounts and/or contracts can call themint()
entrypoint to mint NFTs. This is an optional parameter which will default to an empty whitelist. This value can be changed via theset_variables
post installation. If the whitelist mode is set to locked, a non-empty whitelist must be passed; else, installation of the contract will fail."burn_mode"
: TheBurnMode
modality dictates whether minted NFTs can be burnt. This is an optional parameter and will allow tokens to be burnt by default. This parameter cannot be changed once the contract has been installed."owner_reverse_lookup_mode"
: TheOwnerReverseLookupMode
modality dictates whether the lookup for owners to token identifiers is available. This is an optional parameter and will not provide the lookup by default. This parameter cannot be changed once the contract has been installed."events_mode"
: TheEventsMode
modality selects the event schema used to record any changes that occur to tokens issued by the contract instance."additional_required_metdata"
: An additional metadata schema that must be included. This argument is passed in as au8
value."optional_metdata"
: An optional metadata schema that may be included. This argument is passed in as au8
value.
The following is an example of installing the NFT contract via a deploy using the Rust CLI Casper client. You can find more examples here.
casper-client put-deploy -n https://rpc.testnet.casperlabs.io/ --chain-name "casper-test" --payment-amount 500000000000 -k keys/secret_key.pem --session-path contract/target/wasm32-unknown-unknown/release/contract.wasm \
--session-arg "collection_name:string='enhanced-nft-1'" \
--session-arg "collection_symbol:string='ENFT-1'" \
--session-arg "total_token_supply:u64='10'" \
--session-arg "ownership_mode:u8='0'" \
--session-arg "nft_kind:u8='1'" \
--session-arg "json_schema:string='nft-schema'" \
--session-arg "allow_minting:bool='true'" \
--session-arg "owner_reverse_lookup_mode:u8='0'" \
--session-arg "nft_metadata_kind:u8='2'" \
--session-arg "identifier_mode:u8='0'" \
--session-arg "metadata_mutability:u8='1'"
Specific entrypoints in use by the current implementation of the NFT contract require session code to accept return values passed by the contract over the Wasm boundary.
In order to help with the installation and use of the NFT contract, session code for such entrypoints has been provided. It is recommended that
users and DApp developers attempting to engage with the NFT contract do so with the help of the provided utility session code. The session code can be found in the client
folder within the project folder.
Entrypoint name | Session code |
---|---|
"mint" |
client/mint_session |
"balance_of" |
client/balance_of_session |
"get_approved |
client/get_approved_session |
"owner_of" |
client/owner_of_session |
"transfer" |
client/transfer_session |
Learn to check token ownership starting with version v1.1.1. The OwnerReverseLookupMode
modality must be set to Complete
as described here.
Upgrade to v1.1.1 using a Standard NamedKey Convention or a Custom NamedKey Convention.
You can find instructions on installing an instance of the CEP-78 contract using the Rust CLI Casper client here.
The expected behavior of the NFT contract implementation is asserted by its test suite found in the tests
folder.
The test suite and the corresponding unit tests comprise the specification around the contract and outline the expected behaviors
of the NFT contract across the entire range of possible configurations (i.e modalities and toggles like allow minting). The test suite
ensures that as new modalities are added, and current modalities are extended, no regressions and conflicting behaviors are introduced.
The test suite also asserts the correct working behavior of the utility session code provided in the client folder. The tests can be run
by using the provided Makefile
and running the make test
command.
Code | Error |
---|---|
1 | InvalidAccount |
2 | MissingInstaller |
3 | InvalidInstaller |
4 | UnexpectedKeyVariant |
5 | MissingTokenOwner |
6 | InvalidTokenOwner |
7 | FailedToGetArgBytes |
8 | FailedToCreateDictionary |
9 | MissingStorageUref |
10 | InvalidStorageUref |
11 | MissingOwnerUref |
12 | InvalidOwnersUref |
13 | FailedToAccessStorageDictionary |
14 | FailedToAccessOwnershipDictionary |
15 | DuplicateMinted |
16 | FailedToConvertCLValue |
17 | MissingCollectionName |
18 | InvalidCollectionName |
19 | FailedToSerializeMetaData |
20 | MissingAccount |
21 | MissingMintingStatus |
22 | InvalidMintingStatus |
23 | MissingCollectionSymbol |
24 | InvalidCollectionSymbol |
25 | MissingTotalTokenSupply |
26 | InvalidTotalTokenSupply |
27 | MissingTokenID |
28 | InvalidTokenIdentifier |
29 | MissingTokenOwners |
30 | MissingAccountHash |
31 | InvalidAccountHash |
32 | TokenSupplyDepleted |
33 | MissingOwnedTokensDictionary |
34 | TokenAlreadyBelongsToMinterFatal |
35 | FatalTokenIdDuplication |
36 | InvalidMinter |
37 | MissingMintingMode |
38 | InvalidMintingMode |
39 | MissingInstallerKey |
40 | FailedToConvertToAccountHash |
41 | InvalidBurner |
42 | PreviouslyBurntToken |
43 | MissingAllowMinting |
44 | InvalidAllowMinting |
45 | MissingNumberOfMintedTokens |
46 | InvalidNumberOfMintedTokens |
47 | MissingTokenMetaData |
48 | InvalidTokenMetaData |
49 | MissingApprovedAccountHash |
50 | InvalidApprovedAccountHash |
51 | MissingApprovedTokensDictionary |
52 | TokenAlreadyApproved |
53 | MissingApproveAll |
54 | InvalidApproveAll |
55 | MissingOperator |
56 | InvalidOperator |
57 | Phantom |
58 | ContractAlreadyInitialized |
59 | MintingIsPaused |
60 | FailureToParseAccountHash |
61 | VacantValueInDictionary |
62 | MissingOwnershipMode |
63 | InvalidOwnershipMode |
64 | InvalidTokenMinter |
65 | MissingOwnedTokens |
66 | InvalidAccountKeyInDictionary |
67 | MissingJsonSchema |
68 | InvalidJsonSchema |
69 | InvalidKey |
70 | InvalidOwnedTokens |
71 | MissingTokenURI |
72 | InvalidTokenURI |
73 | MissingNftKind |
74 | InvalidNftKind |
75 | MissingHolderMode |
76 | InvalidHolderMode |
77 | MissingWhitelistMode |
78 | InvalidWhitelistMode |
79 | MissingContractWhiteList |
80 | InvalidContractWhitelist |
81 | UnlistedContractHash |
82 | InvalidContract |
83 | EmptyContractWhitelist |
84 | MissingReceiptName |
85 | InvalidReceiptName |
86 | InvalidJsonMetadata |
87 | InvalidJsonFormat |
88 | FailedToParseCep78Metadata |
89 | FailedToParse721Metadata |
90 | FailedToParseCustomMetadata |
91 | InvalidCEP78Metadata |
92 | FailedToJsonifyCEP78Metadata |
93 | InvalidNFT721Metadata |
94 | FailedToJsonifyNFT721Metadata |
95 | InvalidCustomMetadata |
96 | MissingNFTMetadataKind |
97 | InvalidNFTMetadataKind |
98 | MissingIdentifierMode |
99 | InvalidIdentifierMode |
100 | FailedToParseTokenId |
101 | MissingMetadataMutability |
102 | InvalidMetadataMutability |
103 | FailedToJsonifyCustomMetadata |
104 | ForbiddenMetadataUpdate |
105 | MissingBurnMode |
106 | InvalidBurnMode |
107 | MissingHashByIndex |
108 | InvalidHashByIndex |
109 | MissingIndexByHash |
110 | InvalidIndexByHash |
111 | MissingPageTableURef |
112 | InvalidPageTableURef |
113 | MissingPageLimit |
114 | InvalidPageLimit |
115 | InvalidPageNumber |
116 | InvalidPageIndex |
117 | MissingUnmatchedHashCount |
118 | InvalidUnmatchedHashCount |
119 | MissingPackageHashForUpgrade |
120 | MissingPageUref |
121 | InvalidPageUref |
122 | CannotUpgradeWithZeroSupply |
123 | CannotInstallWithZeroSupply |
124 | MissingMigrationFlag |
125 | InvalidMigrationFlag |
126 | ContractAlreadyMigrated |
127 | UnregisteredOwnerInMint |
128 | UnregisteredOwnerInTransfer |
129 | MissingReportingMode |
130 | InvalidReportingMode |
131 | MissingPage |
132 | UnregisteredOwnerFromMigration |
133 | ExceededMaxTotalSupply |
134 | MissingCep78PackageHash |
135 | InvalidCep78InvalidHash |
136 | InvalidPackageHashName |
137 | InvalidAccessKeyName |
138 | InvalidCheckForUpgrade |
139 | InvalidNamedKeyConvention |
140 | OwnerReverseLookupModeNotTransferable |
141 | InvalidAdditionalRequiredMetadata |
142 | InvalidOptionalMetadata |
143 | MissingOptionalNFTMetadataKind |
144 | InvalidOptionalNFTMetadataKind |
145 | MissingAdditionalNFTMetadataKind |
146 | InvalidAdditionalNFTMetadataKind |
147 | InvalidRequirement |
148 | MissingEventsMode |
149 | InvalidEventsMode |
150 | CannotUpgradeToMoreSupply |
151 | MissingOperatorDict |
152 | MissingApprovedDict |
153 | MissingSpenderAccountHash |
154 | InvalidSpenderAccountHash |
155 | MissingOwnerTokenIdentifierKey |
156 | InvalidTransferFilterContract |
157 | MissingTransferFilterContract |
158 | TransferFilterContractNeedsTransferableMode |
159 | TransferFilterContractDenied |
160 | MissingACLWhiteList |
161 | InvalidACLWhitelist |
162 | EmptyACLWhitelist |
163 | InvalidACLPackageMode |
164 | MissingACLPackageMode |
165 | InvalidPackageOperatorMode |
166 | MissingPackageOperatorMode |
167 | InvalidOperatorBurnMode |
168 | MissingOperatorBurnMode |
169 | InvalidIdentifier |
170 | DuplicateIdentifier |