Skip to content

NFT/SFT Specification

Vocabulary

  • NFT: Nonfungible Token. A unique token on the blockchain (quantity 1). An NFT generally is part of a larger group (series) of tokens, and may reference an extensive set of data that is associated with this token. Within this document, any use of the term NFT also applies to SFTs, unless specifically indicated otherwise.
  • SFT: Semifungible Token. A token that has all of the properties of NFTs, but there can be a (generally small) number of identical (fungible) tokens.

  • Invoke: To invoke a token means to launch its related app or web site, providing that site the token identity and proof of ownership.

File Format

An NFT is a .zip format file. All applications MUST be able to handle an NFT .zip file that is 50MB (50 * 1024 * 1024 bytes) or less. Therefore it is RECOMMENDED that NFT creation tools limit the size of NFT files to 50MB or less. This compatibility minimum may be raised periodically as technology improves.

Applications MUST NOT rezip NFT files! This risks producing different bytes which will not be identifiable as this NFT.

It is recommended that an NFT file be named {groupId}.nft, or {title}-{author}.nft (replace spaces with _). However, applications MUST not rely on this naming convention.

An NFT MAY contain arbitrary contents (directories and other files), which may be used by applications paired with this specific NFT. Other NFT applications MUST ignore these files.

The following zip contents (files) are defined. Each file MUST have an extension that specifies the media type. e.g. "cardf.gif" or "cardf.svg"

info.json: NFT information file (see Info section for details)

cardf.[ext]: This is the front of the NFT "card" (e.g. thumbnail). It MUST not be larger than 2MB. It will typically be displayed in a small format, so it is recommended to use a square no more than 300 pixels wide. It MAY be any supported format (video or image). However, applications that do not support video thumbnails MAY use the first frame as an image thumbnail OR the NFT MAY include multiple cardf.[ext] files (with different extensions). In this case, which file an application chooses is undefined.

cardb.[ext]: (optional) This is the back of the NFT "card". It MUST not be larger than 2MB. It will typically be displayed in a small format, so it is recommended to use a square no more than 300 pixels wide. It MAY be any supported format (video or image). It MAY be any supported format (video or image). However, applications that do not support video thumbnails MAY use the first frame as an image thumbnail OR the NFT MAY include multiple cardf.[ext] files (with different extensions). In this case, which file an application chooses is undefined.

public.[ext]: (optional) This is the public, full resolution NFT image. If it does not exist, "cardf" is used.

owner.[ext]: (optional) This file is intended to be consumed by a proven owner of this NFT. Of course, it is not possible to technically enforce this -- for example, any prior owner could post this private media publicly. It is more of a convention. However, if the NFT license requires it (recommended), the NFT creator or a current owner can legally enforce that identified NFT-using applications do not display this media to non-owners.

Supported Media Types

It is a priority to support all major media types. At the same time, we do not want to require all marketplaces, etc to spend a lot of engineering effort developing code for tons of media types.

If a service does not support a media type, it MAY use a placeholder image indicating that. NFTs MAY provide multiple files (of the canonical names documented above) to increase the likelihood that a service can find a supported format.

The following types have broad support in web and mobile technologies, and standard open-source encoder/decoder libraries, and so effort SHOULD be made to support them in all NFT services:

Video

.avif, .webp, .ogg, .mp4, .mpeg, .mpg, .webm

Image

.svg, .gif, .png, .apng, .jpg, .jpeg

Info.json

This file holds NFT metadata information, in JSON format. This file is a single JSON dictionary. The key for this table is

{
    "niftyVer": "(Mandatory) String, currently 2",
    "title": "(Mandatory) String: title of this work",
    "series":"(Optional) String: if this work is part of a series, this is the name of the series",
    "author": "(Mandatory) String: Author's name",
    "keywords": [ "(Mandatory)", "Strings", "List of keywords or keyphrases for classification and search"],
    "appuri": "(Optional) String: if this work enables some feature of an application, place the URI here.  This will allow the NFT to be "invoked".  See the "Invocation Rules" section for details."
    "category": "(Optional) String: Recommended classification of this asset, to be used by wallets for organizational purposes.  This field is the same as in the Token Description Document (TDD).  If category fields differ, prefer any data associated with the subgroup first, then prefer the NFT over the TDD."
    "info": "(Mandatory, but may be the empty string) String: Any description or information to be presented to the user.  MAY be text or HTML format.  However, viewing applications may accept limited HTML.  An NFT MUST NOT include external resources!",
    "bindata": "(Optional) String:  Binary data in hex format.  The application using this NFT may interpret this data in an application-defined manner.  Note that for large amounts of binary data, an application SHOULD just create an additional file in the NFT.  This is a convenience field for small amounts of binary data related to the display of this NFT.",
    "data": { "(Optional)" : "dictionary", "Any": "application-specific fields may go here" }
    "license": "(Mandatory) String: HTML or text license covering the use and transfer of this NFT.  This text MUST NOT include external links!  A view application MUST NOT display any external links or media.  No externally linked license is legally binding since it may be changed at any time."
}

NFT Creation

As a subgroup

The subgroup identifier MUST be the double SHA256 of the NFT .zip file. This is how an owner proves that a particular NFT data file is actually this NFT.

In the Token Description Document

A group or subgroup's token description document MAY include a reference to an NFT (see the token description document spec). This allows non-NFT-aware applications to display a simple icoin, and NFT-aware applications to access the NFT.

In the Genesis Transaction

The group or subgroup genesis transaction MAY contain an additional OP_RETURN that defines NFT/SFT data. The data is serialized via the standard OP_PUSH mechanism and shall be as follows:

88888889
<title of work (should match info.json in the nft data file)>
<double SHA256 of the nft data file>
<NFT data URL (original home of the nft data file)>
[double SHA256 of the nft data file zipped without any owner-only information]

Invocation Rules

An NFT description contains an "appuri" field. If this field does not exist, the token cannot be invoked. This field is a URI. If it does not contain a colon, place "http:" in front.

If the URI starts with "http" (e.g. http or https, even if it was just added by the prior rule), then append the following 2 parameters to the URI. You must determine whether the appuri already contains other parameters or not.

tokenid: Hex representation of the token's ID. proof: Hex representation of a solved Challenge Transaction, with the data containing the block hash of the most recent Nexa block.

Home/Info/Marketplace Rules

This concept loosely defines a site/service to trade and show information about any NFT. This is not the application associated with particular NFTs, rather its a marketplace that can handle ALL NFTs. There could be many competing services that can accomplish this, so this section defines a set of rules to help applications find one. Note that any rule could result in http forwarding reply, which SHOULD be followed to find the actual service.

All service rules eventually provide a URI to check for a marketplace service. Given this URI, the following structure should be followed:

Standard Route

The "standard" route for NFT data is "/token/{id}", where {id} is EITHER the hex or address representation of the NFT groupID. In other words, the marketplace "www.nexaNfts.com" MUST provide a route "www/nexaNfts.com/token/{id}" that displays information about this token.

Query Marketplace Support of This NFT

  • To determine whether this service can support this token, take the schema and domain (e.g. http://www.myapp.com), and append the standard route "/token/{token ID in hex or address form}?query=true". The service can return any HTTP error (in which case the service is not supported), or success with the text string: "ok", "no", or "data required" (this access route is expected to be programmatic, not browser, which is why a simple text string is returned). In the "data required" case, the application must PUSH the token document in a separate HTML request (see below), before launching the browser to the page.
  • To form the page URL, take the schema and domain (e.g. http://www.myapp.com), and append the standard route "/token/{token ID in hex or address form}[?proof={hex signed challenge transaction}]".

Locations

  • Any provider of the NFT data file SHOULD also provide a home page, or at a minimum forward to a known home page. The "standard" path for NFT data is "/token/{id}", where {id} is EITHER the hex or address representation of the NFT groupID.

  • The token description document URL (located in the token genesis info) indicates the "canonical" location to access the token home/info/marketplace service. To form this URL, take the schema and domain (e.g. http://www.myapp.com) of the token description document.

  • Applications MAY hard-code one or several marketplaces and use the "NFT Supported Query" to ask them whether they support this NFT.

Standard NFT Data Push

A "standard" method to supply the NFT data file to the marketplace is defined. If a marketplace returns "data required" from the "NFT supported query", this push must happen before the marketplace's token home page will provide useful informaton. To accomplish a push:

Send a HTTP POST to the standard route (e.g. /token/{id}) with a single attachment containing the binary NFT data file.

The receiver MUST verify that the double sha256 of the POSTed file matches the NFT datafile hash (specified as the subgroup data, or in the group/subgroup genesis transaction).

Standard NFT data access

Owner access

Marketplaces and web applications SHOULD implement a "standard" location to access the raw NFT data file. This route is: "/raw/{id}?chalby={hexSignedChallengeTransaction}". Applications should verify that hexSignedChallengeTransaction, contains a validly signed challenge transaction proving that this NFT is owned by the client. If the client has already recently proven this ownership, the parameter may be omitted. This will return a file (not a web page).

The returned zip file's double SHA256 will match information in the groupID (as described above), proving that this data is truly the NFT.

Public access

Marketplaces and web applications SHOULD implement a "standard" location to access the raw NFT data file, with the owner-only information removed. This route is: "/public/{id}". This will return a file (not a web page).

The SHA256 of this zip file will NOT match information in the groupID, so users of this route are trusting the honesty of the marketplace.