shazow / shazow.net

https://shazow.net
27 stars 0 forks source link

post: Procedurally rendered NFTs #38

Open shazow opened 3 years ago

shazow commented 3 years ago

Procedurally Rendered NFTs

Proposal

To enable NFTs with on-chain procedurally-generated assets, renderers should support rendering inlined data:... tokenURI output.

Background

ERC-721 is the NFT specification, but it under-specifies the output formats supported by tokenURI. It says:

    /// @notice A distinct Uniform Resource Identifier (URI) for a given asset.
    /// @dev Throws if `_tokenId` is not a valid NFT. URIs are defined in RFC
    ///  3986. The URI may point to a JSON file that conforms to the "ERC721
    ///  Metadata JSON Schema".
    function tokenURI(uint256 _tokenId) external view returns (string);

Most implementations assume this means an https://... URL or even an ipfs://... URL, but what about in-line data:... URIs?

Why? Not all NFTs are just off-chain JPEGs. "Natural NFTs" which contain all of their relevant state on-chain benefit from being able to render themselves using on-chain code. The rendering happens client-side, so the only gas cost is to host the rendering code.

In The Wild

NFT can procedurally generate all of their assets on-chain and return them. Some examples:

Marketplaces

Most marketplaces don't support data:... URIs, or do so on a case-by-case basis:

NFT Renderers

How can we make it even better? Related challenges?

  1. EIP to clarify ERC-721 with additional details?
  2. Image-only ERC-721 output interface?
  3. Expand ERC-721 spec for maketplace awareness?
  4. Others ideas?
wighawag commented 3 years ago

Hi, thanks for bringing that up here for discussion.

Some comments:

As per EIP-721 spec and the URI spec that it refers implicitly, data: uri should be considered valid. They are one of the accepted URI scheme: https://datatracker.ietf.org/doc/html/rfc2397

They are also widely supported : https://caniuse.com/datauri

Each browser have different limit to the number of characters though.

as for the mediaType to use, the tokenURI is defined as JSON, as such the mediaType should be application/json

For mandalas.eth I used the generic text/plain mediaType instead as I did not want to base64 encode the text and encountered some issue with application/json mediaType and had to deploy before I could realise what was the problem:

With application/json things like space need to be URI encoded, so space (`) becomes%20while withtext/plain` no encoding is required.

Tested on chrome and firefox,

similarly the following

fetch('data:application/json,{"test": "hello world"}').then(r => r.json()).then(console.log)

outputs this :

{test: "helloworld"}

while this

fetch('data:application/json,{"test": "hello%20world"}').then(r => r.json()).then(console.log)

and this

fetch('data:text/plain,{"test": "hello world"}').then(r => r.json()).then(console.log)

output this :

{test: "hello world"}

Overall I don't think we need any new standard, but we should aim at making support for data uri based tokenURI more widespread.

shazow commented 3 years ago

@wighawag That's very useful, thank you for sharing your thought process!

I agree that data:text/plain is a convenient shortcut which requires less encoding. I like that.

I also agree we don't necessarily need another standard, but I would love to get some agreement from stakeholders about data uri tokenURI usage in the existing standard. Especially as new players come into the space, would suck to have to repeat this over and over

In general, I like the rule of thumb of "any format that fetch(...) supports is fair game (plus IPFS, etc)".

And then of course we have embedded images inside of the data URI, which should also be supported.