The enhanced NFT implementation supports various 'modalities' that dictate the behavior of a specific contract instance. Modalities represent the common expectations around contract usage and behavior.
The following section discusses the currently implemented modalities and illustrates the significance of each.
Modalities
- Ownership
- NFTKind
- NFTHolderMode
- WhitelistMode
- Minting
- AllowMinting
- ACLPackageMode
- PackageOperatorMode
- NFTMetadataKind
- NFTIdentifierMode
- Metadata Mutability
- BurnMode
- OperatorBurnMode
- OwnerReverseLookupMode
- NamedKeyConventionMode
- EventsMode
Further Information
This modality specifies the behavior regarding ownership of NFTs and whether the owner of the NFT can change over the contract's lifetime. There are three modes:
Minter
:Minter
mode is where the ownership of the newly minted NFT is attributed to the minter of the NFT and cannot be specified by the minter. In theMinter
mode the owner of the NFT will not change and thus cannot be transferred to another entity.Assigned
:Assigned
mode is where the owner of the newly minted NFT must be specified by the minter of the NFT. In this mode, the assigned entity can be either minter themselves or a separate entity. However, similar to theMinter
mode, the ownership in this mode cannot be changed, and NFTs minted in this mode cannot be transferred from one entity to another.Transferable
: In theTransferable
mode the owner of the newly minted NFT must be specified by the minter. However, in theTransferable
mode, NFTs can be transferred from the owner to another entity.
In all the three mentioned modes, the owner entity is currently restricted to Accounts
on the Casper network.
Note: In the Transferable
mode, it is possible to transfer the NFT to an Account
that does not exist.
This Ownership
mode is a required installation parameter and cannot be changed once the contract has been installed.
The mode is passed in as u8
value to the "ownership_mode"
runtime argument.
Ownership | u8 |
---|---|
Minter | 0 |
Assigned | 1 |
Transferable | 2 |
The ownership mode of a contract can be determined by querying the ownership_mode
entry within the contract's NamedKeys
.
The NFTKind
modality specifies the commodity that NFTs minted by a particular contract will represent. Currently, the NFTKind
modality does not alter or govern the behavior of the contract itself
and only exists to specify the correlation between on-chain data and off-chain items. There are three different variations of the NFTKind
mode.
Physical
: The NFT represents a real-world physical item e.g., a house.Digital
: The NFT represents a digital item, e.g., a unique JPEG or digital art.Virtual
: The NFT is the virtual representation of a physical notion, e.g., a patent or copyright.
This modality is an optional installation parameter and will default to the Virtual
mode if not provided. However, this mode cannot be changed once the contract has been installed.
The mode is passed in as a u8
value to nft_kind
runtime argument.
NFTKind | u8 |
---|---|
Physical | 0 |
Digital | 1 |
Virtual | 2 |
The NFTHolderMode
dictates which entities on a Casper network can own and mint NFTs. There are three different options currently available:
Accounts
: In this mode, onlyAccounts
can own and mint NFTs.Contracts
: In this mode, onlyContracts
can own and mint NFTs.Mixed
: In this mode bothAccounts
andContracts
can own and mint NFTs.
If the NFTHolderMode
is set to Contracts
a ContractHash
whitelist must be provided. This whitelist dictates which
Contracts
are allowed to mint NFTs in the restricted Installer
minting mode.
NFTHolderMode | u8 |
---|---|
Accounts | 0 |
Contracts | 1 |
Mixed | 2 |
This modality is an optional installation parameter and will default to the Mixed
mode if not provided. However, this mode cannot be changed once the contract has been installed.
The mode is passed in as a u8
value to nft_holder_mode
runtime argument.
The WhitelistMode
dictates if the ACL whitelist restricting access to the mint entry point can be updated. There are currently two options:
Unlocked
: The ACL whitelist is unlocked and can be updated via the set variables endpoint.Locked
: The ACL whitelist is locked and cannot be updated further.
If the WhitelistMode
is set to Locked
an ACL whitelist of entity keys must be provided on installation. This whitelist dictates which entities can mint NFTs in the restricted ACL
minting mode. These entities include Accounts
, Contracts
and Contracts from a package
with ACL_PACKAGE_MODE.
This WhitelistMode
is an optional installation parameter and will be set to unlocked if not passed. However, the whitelist mode itself cannot be changed once the contract has been installed. The mode is passed in as a u8
value to whitelist_mode
runtime argument.
WhitelistMode | u8 |
---|---|
Unlocked | 0 |
Locked | 1 |
The minting mode governs the behavior of contract when minting new tokens. The minting modality provides two options:
Installer
: This mode restricts the ability to mint new NFT tokens only to the installing account of the NFT contract.Public
: This mode allows any account to mint NFT tokens.ACL
: This mode allows whitelisted accounts, contracts or contracts from a package with ACL_PACKAGE_MODE to mint NFT tokens.
This modality is an optional installation parameter and will default to the Installer
mode if not provided. However, this
mode cannot be changed once the contract has been installed. The mode is set by passing a u8
value to the minting_mode
runtime argument.
MintingMode | u8 |
---|---|
Installer | 0 |
Public | 1 |
ACL | 2 |
The AllowMinting
modality dictates the ability for any entity to mint or not. If allow_minting
is set to false, any attempt to mint will revert with a MintingIsPaused
error. This modality provides two options:
AllowMinting | bool |
---|
The ACL package mode modality governs the ability for whitelisting a package instead of a versioned contract. By whitelisting a package, a new contract version will be automatically whitelisted for minting after its package upgrade. This modality provides two options:
ACLPackageMode | bool |
---|
This modality is an optional installation parameter and will default to false
if not provided. This mode can be changed on contract upgrade or can be updated via the set variables endpoint.
Before using this modality, please understand the security implications of having automatic whitelisting of versioned contracts.
The package operator mode governs the ability for approving a package instead of a versioned contract. By approving a package as an operator, a new contract version will be automaticly approved or approved for all for transfer after its package upgrade. This modality provides two options:
PackageOperatorMode | bool |
---|
This modality is an optional installation parameter and will default to false
if not provided. This mode can be changed on contract upgrade or can be updated via the set variables endpoint.
Before using this modality, please understand the security implications of having automatic approvals and revocation rights for versioned contracts.
This modality dictates the schema for the metadata for NFTs minted by a given instance of an NFT contract. There are four supported modalities:
CEP78
: This mode specifies that NFTs minted must have valid metadata conforming to the CEP-78 schema.NFT721
: This mode specifies that NFTs minted must have valid metadata conforming to the NFT-721 metadata schema.Raw
: This mode specifies that metadata validation will not occur and raw strings can be passed totoken_metadata
runtime argument as part of the call tomint
entrypoint.CustomValidated
: This mode specifies that a custom schema provided at the time of install will be used when validating the metadata as part of the call tomint
entrypoint.
During installation, one NFTMetadataKind
must be chosen as the base metadata kind for the contract instance. Additional kinds may be included using either the additional_required_metadata
or optional_metadata
arguments.
{
"name": "John Doe",
"token_uri": "https://www.barfoo.com",
"checksum": "940bffb3f2bba35f84313aa26da09ece3ad47045c6a1292c2bbd2df4ab1a55fb"
}
{
"name": "John Doe",
"symbol": "abc",
"token_uri": "https://www.barfoo.com"
}
The CEP-78 implementation allows installers of the contract to provide their custom schema at the time of installation.
The schema is passed as a String value to json_schema
runtime argument at the time of installation. Once provided, the schema
for a given instance of the contract cannot be changed.
The custom JSON schema must contain a top-level properties
field. An example of a valid JSON schema
is provided. In this example, each property has a name, the description of the property itself, and whether the property is required to be present in the metadata.
If the metadata kind is not set to custom validated, then the value passed to the json_schema
runtime argument will be ignored.
{
"properties": {
"deity_name": {
"name": "deity_name",
"description": "The name of deity from a particular pantheon.",
"required": true
},
"mythology": {
"name": "mythology",
"description": "The mythology the deity belongs to.",
"required": true
}
}
}
{
"deity_name": "Baldur",
"mythology": "Nordic"
}
NFTMetadataKind | u8 |
---|---|
CEP78 | 0 |
NFT721 | 1 |
Raw | 2 |
CustomValidated | 3 |
The identifier mode governs the primary identifier for NFTs minted for a given instance on an installed contract. This modality provides two options:
Ordinal
: NFTs minted in this modality are identified by au64
value. This value is determined by the number of NFTs minted by the contract at the time the NFT is minted.Hash
: NFTs minted in this modality are identified by an optional custom string identifier or by default a base16 encoded representation of the blake2b hash of the metadata provided at the time of mint.
Since the default primary identifier in the Hash
mode is custom or derived by hashing over the metadata, making it a content-addressed identifier, the metadata for the minted NFT cannot be updated after the mint.
Attempting to install the contract with the MetadataMutability
modality set to Mutable
in the Hash
identifier mode will raise an error.
This modality is a required installation parameter and cannot be changed once the contract has been installed.
It is passed in as a u8
value to the identifier_mode
runtime argument.
NFTIdentifierMode | u8 |
---|---|
Ordinal | 0 |
Hash | 1 |
The metadata mutability mode governs the behavior around updates to a given NFTs metadata. This modality provides two options:
Immutable
: Metadata for NFTs minted in this mode cannot be updated once the NFT has been minted.Mutable
: Metadata for NFTs minted in this mode can update the metadata via theset_token_metadata
entrypoint.
The Mutable
option cannot be used in conjunction with the Hash
modality for the NFT identifier; attempting to install the contract with this configuration raises InvalidMetadataMutability
error.
This modality is a required installation parameter and cannot be changed once the contract has been installed.
It is passed in as a u8
value to the metadata_mutability
runtime argument.
MetadataMutability | u8 |
---|---|
Immutable | 0 |
Mutable | 1 |
The BurnMode
modality dictates whether tokens minted by a given instance of an NFT contract can be burnt. This modality
provides two options:
Burnable
: Minted tokens can be burnt.NonBurnable
: Minted tokens cannot be burnt.
BurnMode | u8 |
---|---|
Burnable | 0 |
NonBurnable | 1 |
This modality is an optional installation parameter and will default to the Burnable
mode if not provided. However, this
mode cannot be changed once the contract has been installed. The mode is set by passing a u8
value to the burn_mode
runtime argument.
The OperatorBurnMode
modality dictates whether tokens minted by a given instance of an NFT contract can be burned by an operator (thus accounts, contracts or contracts from a package approved for all by an owner). This modality provides two options:
OperatorBurnMode | bool |
---|
This modality is an optional installation parameter and will default to false
if not provided. This mode can be changed on contract upgrade or can be updated via the set variables endpoint.
Before using this modality, please understand the security implications of having burn rights as an operator.
The OwnerReverseLookupMode
modality is set at install and determines if a given contract instance writes necessary data to allow reverse lookup by owner in addition to by ID.
This modality provides the following options:
NoLookup
: The reporting and receipt functionality is not supported. In this option, the contract instance does not maintain a reverse lookup database of ownership and therefore has more predictable gas costs and greater scaling.Complete
: The reporting and receipt functionality is supported. Token ownership will be tracked by the contract instance using the system described here.TransfersOnly
: The reporting and receipt functionality is supported likeComplete
. However, it does not begin tracking until the first transfer. This modality is for use cases where the majority of NFTs are owned by a private minter and only NFT's that have been transferred benefit from reverse lookup tracking. Token ownership will also be tracked by the contract instance using the system described here.
Additionally, when set to Complete
, causes a receipt to be returned by the mint
or transfer
entrypoints, which the caller can store in their account or contract context for later reference.
Further, two special entrypoints are enabled in Complete
mode. First, register_owner
which when called will allocate the necessary tracking record for the imputed entity. This allows isolation of the one time gas cost to do this per owner, which is convenient for accounting purposes. Second, updated_receipts
, which allows an owner of one or more NFTs held by the contract instance to attain up to date receipt information for the NFTs they currently own.
OwnerReverseLookupMode | u8 |
---|---|
NoLookup | 0 |
Complete | 1 |
TransfersOnly | 2 |
This modality is an optional installation parameter and will default to the NoLookup
mode if not provided. The mode is set by passing a u8
value to the owner_reverse_lookup_mode
runtime argument. This mode cannot be changed once the contract has been installed.
Note : if ownership_mode
is set to Minter
and the minting_mode
is set to Installer
only, OwnerReverseLookupMode
will be set to NoLookup
. This is because the minter, by definition, owns all of the tokens forever. Therefore, there is no reason to do a reverse lookup for that owner. This rule applies only to newly installed contract instances.
Note : if OwnerReverseLookupMode
is set to TransfersOnly
then ownership_mode
has to be set to Transferable
only. This is because other ownership modes do not allow transfer.
If you are upgrading a contract from CEP-78 version 1.0 to 1.1, OwnerReverseLookupMode
will be set to Complete
, as this was the standard behavior of CEP-78 1.0. In addition to being set to Complete
, existing records will be migrated into the CEP-78 1.1 format, which will impose a one-time gas cost to cover the migration.
If you have an existing CEP-78 version 1.0 contract instance, and would prefer the newer functionality with no lookup, the only option is to install a separate, new contract instance and mint all of the NFTs anew in that instance and then burn the corresponding NFTs from the old instance. If you do not own all the NFTs held by the old contract instance, you do not have this option.
The NamedKeyConvention
modality dictates whether the Wasm passed will attempt to install a version 1.1.1 instance of CEP-78 or attempt to migrate a version 1.0 CEP-78 instance to version 1.1.1.
This modality provides three options:
DerivedFromCollectionName
: This modality will signal the contract to attempt to install a new version 1.1.1 instance of the CEP-78 contract. The contract package hash and the access URef will be saved in the installing account'sNamedKeys
ascep78_contract_package_<collection_name>
andcep78_contract_package_access_<collection_name>
.V_1_0_standard
: This modality will signal the contract to attempt to upgrade from version 1.0 to version 1.1.1. In this scenario, the contract will retrieve the package hash and the access URef from theNamedKey
entries originally created during the 1.0 installation.V_1_0_custom
: This modality will signal the contract to attempt to upgrade from version 1.0 to version 1.1.1. In this scenario, the calling account must provide theNamedKey
entries under which the package hash and the access URef are saved. Additionally, this requires the passing of the runtime argumentsaccess_key_name
andhash_key_name
for the access URef and package hash, respectively. In this modality, these arguments are required and must be passed in.
NamedKeyConvention | u8 |
---|---|
DerivedFromCollectionName | 0 |
V_1_0_standard | 1 |
V_1_0_custom | 2 |
The EventsMode
modality determines how the installed instance of CEP-78 will handle the recording of events that occur from interacting with the contract.
The modality provides three options:
NoEvents
: This modality will signal the contract to not record events at all. This is the default mode.CEP47
: This modality will signal the contract to record events using the CEP47 event schema. Further information can be found below.CES
: This modality will signal the contract to record events using the Casper Event Standard.
EventsMode | u8 |
---|---|
NoEvents | 0 |
CEP47 | 1 |
CES | 2 |
The transfer filter modality, if enabled, specifies a contract package hash pointing to a contract that will be called when the transfer
method is invoked on the contract. CEP-78 will call the can_transfer
method on the specified callback contract, which is expected to return a value of TransferFilterContractResult
, represented as a u8.
TransferFilterContractResult::DenyTransfer
will block the transfer regardless of the outcome of other checksTransferFilterContractResult::ProceedTransfer
will allow the transfer to proceed if other checks also pass
The transfer filter can be enabled by passing a ARG_TRANSFER_FILTER_CONTRACT
argument to the install method, with a value of type Option<Key>
The CEP47 EventsMode
modality mimics the event schema previously used in the CEP47 NFT standard. Events are stored as a BTreeMap
within a dictionary (EVENTS
) in the contract's context. Entries consist of the PREFIX_HASH_KEY_NAME
, followed by the EVENT_TYPE
and then variable data as listed in the table below. The events can be retrieved directly via their dictionary entry using the JSON-RPC, with more information on this process available here.
Event name | Included values and type |
---|---|
Mint | recipient (Key), token_id (String) |
Transfer | owner (Key), operator (Option), recipient (Key), token_id (String) |
Burn | owner (Key), token_id (String) |
ApprovalGranted | owner (Key), spender (Key), token_id (String) |
ApprovalRevoked | owner (Key), token_id (String) |
ApprovalForAll | owner (Key), operator (Key) |
RevokedForAll | owner (Key), operator (Key) |
MetadataUpdate | token_id (String) |
Migration | - |
VariablesSet | - |
CES
is an option within the EventsMode
modality that determines how changes to tokens issued by the contract instance will be recorded. Any changes are recorded in the __events
dictionary and can be observed via a node's Server Side Events stream. They may also be viewed by querying the dictionary at any time using the JSON-RPC interface.
The emitted events are encoded according to the Casper Event Standard, and the schema is visible to an observer reading the __events_schema
contract named key.
For this CEP-78 reference implementation, the events schema is as follows:
Event name | Included values and type |
---|---|
Mint | recipient (Key), token_id (String), data (String) |
Transfer | owner (Key), operator (Option), recipient (Key), token_id (String) |
Burn | owner (Key), token_id (String) |
Approval | owner (Key), spender (Key), token_id (String) |
ApprovalRevoked | owner (Key), token_id (String) |
ApprovalForAll | owner (Key), operator (Key) |
RevokedForAll | owner (Key), operator (Key) |
MetadataUpdated | token_id (String), data (String) |
Migration | - |
VariablesSet | - |
The MetadataMutability
option set to Mutable
cannot be used in conjunction with the NFTIdentifierMode
modality set to Hash
.