opensquare-network / statescan

Statemint asset explorer
https://www.statescan.io
Apache License 2.0
5 stars 2 forks source link

Support for on-chain metadata of NFT #1054

Closed tash-2s closed 2 years ago

tash-2s commented 2 years ago

Could Statescan support NFT metadata stored directly on-chain?

Here is an example of an on-chain metadata asset created on Westmint.

Background

Storing IPFS id or external URL as metadata is common practice in the blockchain space. However, fully on-chain NFTs are becoming popular as well. They don't rely on other infrastructure, so it's useful in some areas.

Examples of on-chain NFTs on Ethereum:

Also, it looks like putting JSON directly into metadata is expected on pallet-uniques.

So I want the support on Statescan.

A drawback for on-chain metadata for Statemint/e is a tight limit on metadata size. Up to 128 bytes is allowed now, so we cannot store general data. But this is enough for my use case.

@wliyongfeng

wliyongfeng commented 2 years ago

Thanks for reporting this issue, and I think it makes sense. I'll do some investigation further and implement it when ready.

tash-2s commented 2 years ago

Thank you for your response. The OpenSquare team is doing excellent work!

What can I do to move this forward?

wliyongfeng commented 2 years ago

We're a little busy with other products development. I'm wondering is there a standard for on-chain image? The format you mentioned is like following 2 json objects.

{"name":"🧬","image":"data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' style='background:black'></svg>"}
{"image":"data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 -7 9 9'><text font-size='7'>🆗</text></svg>"}

Is it a standard for image field in a json object?

tash-2s commented 2 years ago

I believe putting a data URL into the image field, like the above examples, is a standard for on-chain images on Ethereum. Although there's no established specification for on-chain metadata, here is some reasoning.

EIP-721 defines metadata extension JSON structure: name, description, and image. The image field is explained as:

A URI pointing to a resource with mime type image/* representing the asset to which this NFT represents.

SVG data URLs satisfy this requirement.

Nextly, I break down the metadata of the example NFTs, which are supported on OpenSea. Uniswap and Loot use the same metadata format, so I only show Loot here for them. Many projects follow a similar structure.

On ERC721 Metadata, tokenURI() returns metadata in the form of URI. (On pallet-uniques metadata, it looks like directly set IPFS id or JSON dump are more used.)

Loot

tokenURI(1) returns:

data:application/json;base64,eyJuYW1lIjogIkJhZyAjMSIsICJkZXNjcmlwdGlvbiI6ICJMb290IGlzIHJhbmRvbWl6ZWQgYWR2ZW50dXJlciBnZWFyIGdlbmVyYXRlZCBhbmQgc3RvcmVkIG9uIGNoYWluLiBTdGF0cywgaW1hZ2VzLCBhbmQgb3RoZXIgZnVuY3Rpb25hbGl0eSBhcmUgaW50ZW50aW9uYWxseSBvbWl0dGVkIGZvciBvdGhlcnMgdG8gaW50ZXJwcmV0LiBGZWVsIGZyZWUgdG8gdXNlIExvb3QgaW4gYW55IHdheSB5b3Ugd2FudC4iLCAiaW1hZ2UiOiAiZGF0YTppbWFnZS9zdmcreG1sO2Jhc2U2NCxQSE4yWnlCNGJXeHVjejBpYUhSMGNEb3ZMM2QzZHk1M015NXZjbWN2TWpBd01DOXpkbWNpSUhCeVpYTmxjblpsUVhOd1pXTjBVbUYwYVc4OUluaE5hVzVaVFdsdUlHMWxaWFFpSUhacFpYZENiM2c5SWpBZ01DQXpOVEFnTXpVd0lqNDhjM1I1YkdVK0xtSmhjMlVnZXlCbWFXeHNPaUIzYUdsMFpUc2dabTl1ZEMxbVlXMXBiSGs2SUhObGNtbG1PeUJtYjI1MExYTnBlbVU2SURFMGNIZzdJSDA4TDNOMGVXeGxQanh5WldOMElIZHBaSFJvUFNJeE1EQWxJaUJvWldsbmFIUTlJakV3TUNVaUlHWnBiR3c5SW1Kc1lXTnJJaUF2UGp4MFpYaDBJSGc5SWpFd0lpQjVQU0l5TUNJZ1kyeGhjM005SW1KaGMyVWlQaUpIY21sdElGTm9iM1YwSWlCSGNtRjJaU0JYWVc1a0lHOW1JRk5yYVd4c0lDc3hQQzkwWlhoMFBqeDBaWGgwSUhnOUlqRXdJaUI1UFNJME1DSWdZMnhoYzNNOUltSmhjMlVpUGtoaGNtUWdUR1ZoZEdobGNpQkJjbTF2Y2p3dmRHVjRkRDQ4ZEdWNGRDQjRQU0l4TUNJZ2VUMGlOakFpSUdOc1lYTnpQU0ppWVhObElqNUVhWFpwYm1VZ1NHOXZaRHd2ZEdWNGRENDhkR1Y0ZENCNFBTSXhNQ0lnZVQwaU9EQWlJR05zWVhOelBTSmlZWE5sSWo1SVlYSmtJRXhsWVhSb1pYSWdRbVZzZER3dmRHVjRkRDQ4ZEdWNGRDQjRQU0l4TUNJZ2VUMGlNVEF3SWlCamJHRnpjejBpWW1GelpTSStJa1JsWVhSb0lGSnZiM1FpSUU5eWJtRjBaU0JIY21WaGRtVnpJRzltSUZOcmFXeHNQQzkwWlhoMFBqeDBaWGgwSUhnOUlqRXdJaUI1UFNJeE1qQWlJR05zWVhOelBTSmlZWE5sSWo1VGRIVmtaR1ZrSUV4bFlYUm9aWElnUjJ4dmRtVnpQQzkwWlhoMFBqeDBaWGgwSUhnOUlqRXdJaUI1UFNJeE5EQWlJR05zWVhOelBTSmlZWE5sSWo1T1pXTnJiR0ZqWlNCdlppQkZibXhwWjJoMFpXNXRaVzUwUEM5MFpYaDBQangwWlhoMElIZzlJakV3SWlCNVBTSXhOakFpSUdOc1lYTnpQU0ppWVhObElqNUhiMnhrSUZKcGJtYzhMM1JsZUhRK1BDOXpkbWMrIn0=

The base64 part is decoded into:

{"name"=>"Bag #1", "description"=>"Loot is randomized adventurer gear generated and stored on chain. Stats, images, and other functionality are intentionally omitted for others to interpret. Feel free to use Loot in any way you want.", "image"=>"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHByZXNlcnZlQXNwZWN0UmF0aW89InhNaW5ZTWluIG1lZXQiIHZpZXdCb3g9IjAgMCAzNTAgMzUwIj48c3R5bGU+LmJhc2UgeyBmaWxsOiB3aGl0ZTsgZm9udC1mYW1pbHk6IHNlcmlmOyBmb250LXNpemU6IDE0cHg7IH08L3N0eWxlPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9ImJsYWNrIiAvPjx0ZXh0IHg9IjEwIiB5PSIyMCIgY2xhc3M9ImJhc2UiPiJHcmltIFNob3V0IiBHcmF2ZSBXYW5kIG9mIFNraWxsICsxPC90ZXh0Pjx0ZXh0IHg9IjEwIiB5PSI0MCIgY2xhc3M9ImJhc2UiPkhhcmQgTGVhdGhlciBBcm1vcjwvdGV4dD48dGV4dCB4PSIxMCIgeT0iNjAiIGNsYXNzPSJiYXNlIj5EaXZpbmUgSG9vZDwvdGV4dD48dGV4dCB4PSIxMCIgeT0iODAiIGNsYXNzPSJiYXNlIj5IYXJkIExlYXRoZXIgQmVsdDwvdGV4dD48dGV4dCB4PSIxMCIgeT0iMTAwIiBjbGFzcz0iYmFzZSI+IkRlYXRoIFJvb3QiIE9ybmF0ZSBHcmVhdmVzIG9mIFNraWxsPC90ZXh0Pjx0ZXh0IHg9IjEwIiB5PSIxMjAiIGNsYXNzPSJiYXNlIj5TdHVkZGVkIExlYXRoZXIgR2xvdmVzPC90ZXh0Pjx0ZXh0IHg9IjEwIiB5PSIxNDAiIGNsYXNzPSJiYXNlIj5OZWNrbGFjZSBvZiBFbmxpZ2h0ZW5tZW50PC90ZXh0Pjx0ZXh0IHg9IjEwIiB5PSIxNjAiIGNsYXNzPSJiYXNlIj5Hb2xkIFJpbmc8L3RleHQ+PC9zdmc+"}

The image base64 part is decoded into:.

<svg xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMinYMin meet" viewBox="0 0 350 350"><style>.base { fill: white; font-family: serif; font-size: 14px; }</style><rect width="100%" height="100%" fill="black" /><text x="10" y="20" class="base">"Grim Shout" Grave Wand of Skill +1</text><text x="10" y="40" class="base">Hard Leather Armor</text><text x="10" y="60" class="base">Divine Hood</text><text x="10" y="80" class="base">Hard Leather Belt</text><text x="10" y="100" class="base">"Death Root" Ornate Greaves of Skill</text><text x="10" y="120" class="base">Studded Leather Gloves</text><text x="10" y="140" class="base">Necklace of Enlightenment</text><text x="10" y="160" class="base">Gold Ring</text></svg>

Mandala

tokenURI(1208480988381788759709365916903450620926929939049) returns:

data:text/plain,{"name":"Mandala 0xd3ae29654c1d0638d194bd9110422da551402a69","description":"A Unique Mandala","image":"data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' shape-rendering='crispEdges' width='512' height='512'><g transform='scale(64)'><image width='8' height='8' style='image-rendering: pixelated;' href='data:image/gif;base64,R0lGODdhEwATAMQAAAAAAPb+Y/7EJfN3NNARQUUKLG0bMsR1SujKqW7wQwe/dQBcmQeEqjDR0UgXo4A0vrlq2AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAkKAAAALAAAAAATABMAAAdNgAAAAAAAAAAADQMNAAAAAAAAAAAAAAAKDgAAAAIJAgAAAA4KAAAAAAAABgUABAwBDQEMBAAFBgAAAAAKBhAGAwgNAQkBDQgDBhAGCgBNgAAOBQYECw0JAQEBCQ0LBAYFDgAAAAADCxAEAgINAgIEEAsDAAAAAAAECA0ECgUFAQUFCgQNCAQAAAAADA0JAgUEEAIQBAUCCQ0MAABNgA0CAQEBAgUQCgYKEAUCAQEBAg0DCQ0JAQ0BAgYJBgIBDQEJDQkDDQIBAQECBRAKBgoQBQIBAQECDQAADA0JAgUEEAIQBAUCCQ0MAABNgAAABAgNBAoFBQEFBQoEDQgEAAAAAAADCxAEAgINAgIEEAsDAAAAAA4FBgQLDQkBAQEJDQsEBgUOAAAKBhAGAwgNAQkBDQgDBhAGCgA6gAAAAAYFAAQMAQ0BDAQABQYAAAAAAAAKDgAAAAIJAgAAAA4KAAAAAAAAAAAAAAANAw0AAAAAAAAAAAGBADs='/></g></svg>"}

Their use of image fields matches ours.


OpenSea also accepts an image_data field, allowing a pure SVG string, not a data URL string. I'm not sure how widely used/supported this field is.

Therefore, "image":"data:image/svg+xml,<svg... is considered standard. "image_data":"<svg... may be good also, as it uses fewer bytes.

wliyongfeng commented 2 years ago

Many thanks to the detailed explanation. I think they are enough for our implementation.

tash-2s commented 2 years ago

Thank you for working on this. https://github.com/opensquare-network/statescan/pull/1055 @hyifeng I'm wondering about the current state of the PR. What can I help you with?

wliyongfeng commented 2 years ago

@wliyongfeng This PR is waiting for my review. I will do it soon.

tash-2s commented 2 years ago

I'm sure you're very busy, and I don't want to bother you, but how is this going? https://github.com/opensquare-network/statescan/pull/1057 has been merged but not deployed yet?

wliyongfeng commented 2 years ago

@tash-2s Yeah, it has been merged and we will deploy it recent days.

wliyongfeng commented 2 years ago

https://westmint.statescan.io/nft/classes/5

tash-2s commented 2 years ago

Many thanks, that's perfect! I'm grateful for your support.