Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SEP-39: NFT minting and interoperability recommendations #1140

Merged
merged 18 commits into from
Mar 21, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions ecosystem/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@
| [SEP-0035](sep-0035.md) | Operation IDs | Isaiah Turner, Debnil Sur, Scott Fleckenstein | Standard | Draft |
| [SEP-0037](sep-0037.md) | Address Directory API | OrbitLens | Informational | Draft |
| [SEP-0038](sep-0038.md) | Anchor RFQ API | Jake Urban and Leigh McCulloch | Standard | Draft |
| [SEP-0039](sep-0039.md) | Interoperability Recommendations for NFTs | SDF, Litemint.io | Informational | Active |



Expand Down
155 changes: 155 additions & 0 deletions ecosystem/sep-0039.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@

## Preamble

```
SEP: 0039
Title: Interoperability Recommendations for NFTs
Authors: SDF, Frederic Rezeau <@FredericRezeau>
Track: Informational
Status: Draft
Created: 02-22-2022
Updated: 03-21-2022
Version: 1.0.0
Discussion: /~https://github.com/stellar/stellar-protocol/pull/1140
```

## Simple Summary
This SEP provides informational guidelines on how NFTs can be managed on the Stellar network and within the ecosystem. It outlines some best practices on both _minting_ and _representing_ NFTs to maximize interoperability.


## Dependencies
The guidelines around NFT representation are an extension to [SEP-1][sep1-currency], specifically around the way currencies are represented.


## Motivation
With the NFT marketplace exploding in popularity, two needs arise: ecosystem _best practices_ for creating NFTs as well as _interoperability guidelines_ that ensure compatibility throughout the space. While both of these exist throughout the ecosystem, a single informational SEP is a necessary point of reference. Note that while the focus of this document is inspired by NFTs, it applies more broadly to NFTs that don't necessarily have strict adherence to non-fungibility.


## Abstract
We address two key concerns of non-fractional tokens: _minting_ (creation) and _representation_ (parsing, rendering, etc.). For the former, we outline a set of community-driven best practices (that are by no means a _required_ minting standard), and for the latter, we extend the [SEP-1 currency specification][sep1-currency] in order to maximize interoperability with how the ecosystem already renders assets.


## Specification
As already noted, this informational SEP is split into two parts: NFT _minting_ best practices and NFT _representation_ interoperability.

The minting best practices are merely one way to create NFTs on Stellar and should not be taken as a standard. The interoperability guidelines, however, are important in representing your NFT via [SEP-1][sep1] and maximizing its compatibility with the greater ecosystem.

### Minting NFTs
This section presents a guide on _best practices_ (rather than a _standard_) for minting some types of NFTs. It's based heavily on [this community guide][litemint]. Your NFT may or may not need certain elements of this guide, or you may need additional components on top of it. These diversions are noted where appropriate.

At a high level, your NFT is represented by an asset on the Stellar network. Owning the asset _represents_ owning the NFT. This makes your NFT a "first-class citizen" on the network: it immediately benefits from all of Stellar's native features for assets like [path payments](https://developers.stellar.org/docs/start/list-of-operations/#path-payment-strict-send) and the [decentralized exchange](https://developers.stellar.org/docs/glossary/decentralized-exchange/).

#### Storing your NFT
Regardless of what your NFT represents (artwork, legal contracts, friendship), you need a way to store it. Using decentralized storage such as the [InterPlanetary File System][ipfs] (IPFS) is a recommended best-practice for future-proofing your NFTs. IPFS uses [content identifiers][cid] (CIDs) to address data. These provide **data integrity** and **immutability**: if the underlying NFT changes in any way, its content identifier will also change.

#### Describing your NFT
Since your NFT can be anything, another best practice is storing metadata that describes it. Inspired by Ethereum's [EIP-721](https://eips.ethereum.org/EIPS/eip-721) standard, many NFTs in the Stellar ecosystem store JSON metadata file like the following:


```json
{
"name": "A demonstration of NFT metadata",
"description": "This is a description of the cool NFT used for an informational SEP demo.",
"url": "ipfs://QmXoypizjW3WknFiJnKLwHCnL72vedxjQkDDP1mXWo6uco/I/m/RickAstleyNeverGonnaGiveYouUp7InchSingleCover.jpg",
"issuer": "GALAXYVOIDAOPZTDLHILAJQKCVVFMD4IKLXLSZV5YHO7VY74IWZILUTO",
"code": "DEMOASSET"
}
```

There are a few key elements here: a way to describe the NFT, a reference to what the NFT represents, and a "back-reference" to the Stellar asset that represents NFT ownership. Again, this isn't a standard but rather a set of common best practices: your metadata file (should you decide to use one) may need different fields. You could even publish a JSON schema to help clients validate the NFT's metadata structure.

#### Referencing your NFT
There is a common naming convention within the Ethereum ecosystem and other APIs to associate NFTs with their [content identifiers][cid]. You can use the [`ManageData` operation](https://developers.stellar.org/docs/start/list-of-operations/#manage-data) to store a data entry on your issuing account with `ipfshash` as the key and the IPFS [content identifier][cid] as the value to benefit from parts of the ecosystem that also follow the convention. For example, NFT marketplaces like [Litemint][litemint] use the naming convention and look up the CID to discover images, video, audio, full descriptions, and other properties about the NFT seamlessly.

Some marketplaces may do additional lookups. For example, if the `ipfshash` data entry is not found, [Litemint][litemint] also looks up the entry named `ipfshash-<ASSET CODE>`, allowing issuers to reference multiple assets per issuing account.

You may want to adopt a different model if this one doesn't fit your use case. For example, you could use a `url.<asset-code>` data entry or just rely on your [SEP-1][sep1] file's [currency description][sep1-currency]. Fundamentally, though, it's important to create relationships between your issuing accounts and the NFTs they issue.

#### Issuing your NFT
To implement non-fractional assets it is necessary to use Stellar's indivisible unit: the stroop. This is the smallest quantity in which a Stellar asset can be sent, received, or traded, and it's equal to one ten-millionth or 0.0000001 of a Stellar lumen (XLM). Therefore, to issue one NFT, you would send 0.0000001 of your asset.

**Note**: Such small amounts may cause issues on the decentralized exchange because of Stellar Core's internal representation of order prices. There are ways to bypass these limitations, such as the one [described by Litemint](https://blog.litemint.com/nft-sdex-pricing/).

#### Ensuring NFT immutability
If you plan never to increase the supply or modify your NFT, it is best practice to commit to this by locking the issuing account. Freezing the account representing your NFT is key to providing immutability for the above steps:

1. It prevents the account from issuing additional units of the NFT.
2. It prevents data entries from being modified.
3. It prevents the [`AccountMerge`](https://developers.stellar.org/docs/start/list-of-operations/#account-merge) operation so the issuing account persists.
4. It prevents revocation of ownership (the "Authorization Immutable" flag could also be used, but it would not prevent the other points above on its own).

By default (i.e. without a [multisig](https://developers.stellar.org/docs/glossary/multisig/) setup), to freeze an account you would set its `masterWeight` to zero via the [`SetOptions`][set-options] operation.

Freezing is an irreversible process once complete (by design), so take extra care when freezing your NFT issuing account. Some use cases _may_ require the option to unfreeze an account at a future date and this is possible if done in advance, prior to the freeze. This can be done on Stellar via a [pre-authorized transaction](https://developers.stellar.org/docs/glossary/multisig/#pre-authorized-transaction) submitted containing a [`SetOptions`][set-options] operation that sets the `masterWeight` back to 1.

### Representing NFTs
This section provides interoperability guidance around _representing_ NFTs.

Since NFTs are represented by Stellar assets and are thus "first-class citizens" in the ecosystem, they should leverage existing interoperability layers. Setting up a [SEP-1 `stellar.toml`][sep1] file provides immediate interoperability with all services and wallets on the Stellar ecosystem. It is highly recommended that all assets you issue on Stellar, NFTs or not, follow the [SEP-1][sep1] standard to provide a valid `stellar.toml` file. This grants your NFT a degree of legitimacy, because most Stellar ecosystem services and wallets tend to discard TOML-less assets and/or flag them as spam. There is detailed documentation about how to do that on [this page](https://developers.stellar.org/docs/issuing-assets/publishing-asset-info/).

#### Using SEP-1
Many of the [SEP-1][sep1] fields are highly relevant to NFTs. You should include as many of them as is appropriate for your use case:

* the `code` and `issuer` fields are essential to describe the Stellar asset that represents your NFT
* the `name` and `desc` fields provide human-readable information about your NFT
* the `fixed_number`, `max_number`, and `is_unlimited` fields are mutually exclusive ways to describe the supply of your NFT
* the `image` field is how the ecosystem will "draw" your NFT

Note that even if your NFT isn't an image, you may still want to provide a way to represent it as one (like a logo or symbol) to make it stand out. It's good practice to provide an optimized version of the art to allow fast-loading from services and wallets on Stellar.

Here is an example stellar.toml file describing an asset representing a unique digital image, mimicking the JSON metadata file we described [earlier](#describing-your-nft):

```toml
[DOCUMENTATION]
ORG_URL="<https://ink.litemint.store>"

[[CURRENCIES]]
issuer="GAKZGD5BFXZ7P7P45WHTM6DODMOEWWJUSCAIPGYIVIPYQSVR6MM6YR7M"
code="DEMOASSET"
name="A demonstration of NFT metadata"
desc="This is a description of the cool NFT used in this demo."
image="https://cloudflare-ipfs.com/ipfs/QmXoypizjW3WknFiJnKLwHCnL72vedxjQkDDP1mXWo6uco/I/m/RickAstleyNeverGonnaGiveYouUp7InchSingleCover.jpg"
fixed_number=1
display_decimals=7
```

Once you have a `stellar.toml` file under your domain, you should also configure the issuing account's `homedomain` to point to it via the [`SetOptions` operation][set-options]. This unifies the three separate components: the account issuing the NFT, the metadata describing the NFT, and the NFT itself. Note that this value can't be changed if the account gets locked (see [Immutability](ensuring-nft-immutability), above).

#### SEP-1 Extensions
Since NFTs can represent anything, but the SEP-1 currency specification does not have a generic way to refer to an "anything." There is an `image` key, but it is generally used for something like a currency logo.

Thus, this SEP adds additional supported fields to the SEP-1 currency specification to faciliate this need:

| Field Name | Description and purpose |
|--------------|-------------------------|
| `url` | A [valid URL][url] pointing to the NFT this asset represents |
| `url_sha256` | A [SHA-256][sha2] hash of the data pointed to by `url` |

The `url_sha256` is optional and provided here as a way to verify the integrity of the NFT. It's particularly useful if the NFT lives on a different server relative to the `stellar.toml` file. Some URLs (like IPFS CIDs) have integrity "batteries included" and won't need this field.


## Design Rationale
A key component of a flourishing NFTs marketplace is _interoperability_: that drives all of the design decisions in this informational SEP. The [first section](#minting-nfts) is a set of best practices for a _particular_ set of needs in creating NFTs. There is no "one size fits all" way to do this, so creators should feel free to deviate from the recommendations to fulfill their needs. The [second section](#representing-nfts) adds some extremely flexible fields to SEP-1 to accomodate the fact that NFTs can be anything.

It's worth noting that the idea of "ownership" described throughout the SEP—ownership of an asset and its relationship to owning the respective NFT—is a little diluted: neither the network nor a standard can make any claims about _legitimacy_ of the NFT itself. Owning a unit of the Stellar asset representing your NFT is a way to establish a _relationship_ between buyer and seller that is _linked_ to the NFT, nothing more.


## Security Concerns
This informational SEP does not introduce security concerns pertaining to the Stellar network itself.

However, it does introduce concerns around data integrity (i.e. changes to data should be detectable) that NFT issuers (and purchasers) should be aware of. NFTs can be changed after purchase of their representative token, since the `stellar.toml` file can be arbitrarily changed by its owner and is not explicitly tied to the NFT. If this "triangle of integrity" across the issuing account, the NFT metadata, and the corresponding TOML entry is important to you, you should take extra steps to ensure data integrity.


## Changelog
- `v1.0.0`: Initial release. [#1140](/~https://github.com/stellar/stellar-protocol/pull/1140)


[sep1]: https://stellar.org/protocol/sep-1
[sep1-currency]: https://stellar.org/protocol/sep-1#currency-documentation
[sep14]: https://stellar.org/protocol/sep-14
[litemint]: https://medium.com/stellar-community/best-practices-for-creating-nfts-on-stellar-5c91e53e9eb9
[ipfs]: https://docs.ipfs.io
[cid]: /~https://github.com/multiformats/cid
[url]: https://url.spec.whatwg.org/
[sha2]: https://datatracker.ietf.org/doc/html/rfc6234#section-6
[set-options]: https://developers.stellar.org/docs/start/list-of-operations/#set-options