Closed friedger closed 2 years ago
I am looking for more real world examples like:
{
"collection": "PoX Monks",
"id": 1,
"name": "PoX Monk 1",
"image": "ipfs://ipfs/QmegB9FURt6Jz1kGbaQxNqhvoPk9G2b87rxUE1h9DLDbDF/PoX%20Monk%201.jpg",
"attributes": [
{
"type": "body",
"value": "buff monk"
},
{
"type": "headwear",
"value": "orange hat"
}
]
}
Here we see additional properties: "collection" and "id", also "type" instead of "trait_type".
The SIP contains a JSON schema definition written in JSON. Makers of $65m NFT "First 5000 days" by Beeple mixed schema definition with schema values. Metadata should NOT look like this. While valid according to the sip, providing values in "description" fields is counter-intuitive.
{"title": "EVERYDAYS: THE FIRST 5000 DAYS",
"name": "EVERYDAYS: THE FIRST 5000 DAYS",
"type": "object",
"imageUrl": "https://ipfsgateway.makersplace.com/ipfs/QmZ15eQX8FPjfrtdX3QYbrhZxJpbLpvDpsgb2p3VEH8Bqq",
"description": "I made a picture from start to finish every single day from May 1st, 2007 - January 7th, 2021. This is every motherfucking one of those pictures.", "attributes": [{"trait_type": "Creator", "value": "beeple"}],
"properties":
{"name": {"type": "string", "description": "EVERYDAYS: THE FIRST 5000 DAYS"},
"description": {"type": "string", "description": "I made a picture from start to finish every single day from May 1st, 2007 - January 7th, 2021. This is every motherfucking one of those pictures."},
"preview_media_file": {"type": "string", "description": "https://ipfsgateway.makersplace.com/ipfs/QmZ15eQX8FPjfrtdX3QYbrhZxJpbLpvDpsgb2p3VEH8Bqq"},
"preview_media_file_type": {"type": "string", "description": "jpg"}, "created_at": {"type": "datetime", "description": "2021-02-16T00:07:31.674688+00:00"}, "total_supply": {"type": "int", "description": 1},
"digital_media_signature_type": {"type": "string", "description": "SHA-256"},
"digital_media_signature": {"type": "string", "description": "6314b55cc6ff34f67a18e1ccc977234b803f7a5497b94f1f994ac9d1b896a017"},
"raw_media_file": {"type": "string", "description": "https://ipfsgateway.makersplace.com/ipfs/QmXkxpwAHCtDXbbZHUwqtFucG1RMS6T87vi1CdvadfL7qA"}}}
Example with extensive use of properties. Here trait definitions (that can be defined in attributes) and properties are mixed into the "properties" value.
{
"name": "foo",
"image": "bar",
"properties": {
"making_of_video": {
"display_type": "uri",
"trait_type": "string",
"value": "protocol://identifier"
},
"video_md": {
"display_type": "none",
"trait_type": "xml", // string???
"value": " <xs:element name="VIDEOMD" type="videoType"/>
<xs:element name="VIDEOSRC" type="videoType"/>
<xs:annotation>
<xs:documentation>VIDEOMD: LC-AV Video Metadata Extension Schema. VIDEOMD contains technical ...
</xs:documentation>
</xs:annotation>
<xs:complexType name="videoType">
<xs:annotation>
<xs:documentation>A complexType for encapsulating and organizing within a singleparent element ...
</xs:documentation>
</xs:annotation>
..."
},
}
Hoi Axopoa here. For most of the NFT collections where the "source" is "hosted", a format like this would (maybe) be ok.
{
"name": "PoX Monks",
"description": "A unique collection of monks",
"author": "Monks Studio",
"version": 1,
"date": 1631645313396,
"items": [
{
"id": 1,
"name": "PoX Monk 1",
"source": "ipfs://ipfs/QmegB9FURt6Jz1kGbaQxNqhvoPk9G2b87rxUE1h9DLDbDF/PoX%20Monk%201.jpg",
"attributes": [
{
"type": "body",
"value": "buff monk"
},
{
"type": "headwear",
"value": "orange hat"
}
]
},
{
"id": 2,
"name": "PoX Monk 2",
"source": "ipfs://ipfs/QmegB9FURt6Jz1kGbaQxNqhvoPk9G2b87rxUE1h9DLDbDF/PoX%20Monk%201.jpg",
"attributes": [
{
"type": "body",
"value": "slim monk"
},
{
"type": "headwear",
"value": "blue hat"
}
]
}
]
}
Edit: Seems like I miss a lot of information. I will do my homework and come back
For new collections, stxnft.com goes with a format that looks something like this:
{
"collection": "StacksArmy",
"name": "StacksArmy Captain #9",
"image": "ipfs://ipfs/QmPPQBNKohiDYAnjKkdrQcf2LupqixFMEpyvnNWQXz7ozq/StacksArmy%20Captain%20%239.png",
"attributes": [
{
"trait_type": "class",
"value": "Captain"
}
]
}
This is similar to the OpenSea metadata format, with the addition of the additional field collection
.
This is because right now SIP-009 doesn't support "collection-level" metadata (ERC721Metadata
for example, specifies name()
and symbol()
functions). I'm not terribly opinionated on whether this should be done at the contract level with an additional function to get the name/symbol, or whether it should be handled in get-token-uri
as we do now.
To me, the OpenSea metadata format works just fine with this collection-level metadata caveat. I'd support either (a) an extension to SIP-009 allowing for NFT contracts to return a name and symbol (e.g., get-name
and get-symbol
functions which take no arguments), or (b) adopting a metadata format that is OpenSea + some field to specify the collection name.
Which properties should be required? This sip proposed "version", "name" and "image"
@MarvinJanssen This SIP tries to specifies metadata for any type of tokens. It should be applicable also for SIP-013 tokens (https://github.com/stacksgov/sips/pull/42). Does it make sense?
To me, the OpenSea metadata format works just fine with this collection-level metadata caveat. I'd support either (a) an extension to SIP-009 allowing for NFT contracts to return a name and symbol (e.g., get-name and get-symbol functions which take no arguments), or (b) adopting a metadata format that is OpenSea + some field to specify the collection name.
@Jamil I support b). I propose field "properties.collection".
Thanks @friedger this is great. I think bringing in lots of real world examples makes sense (like you have been doing). We should tap into our friends in the EVM space and see what they are doing these days too. As long as there is a version field, mentioned in #42, then I think we can come up with anything.
Which properties should be required? This sip proposed "version", "name" and "image"
On stxnft.com right now we'll render any metadata as long as name
and image
are provided.
However, are we going to require tokens which represent non-image data (e.g., bns
, quotes
) to have a corresponding render/image?
It'd certainly make things easier on my side but I'm not sure people will follow that for non-image NFTs.
This sip is mainly about sip compatible contracts, not about native assets, like bns.
BNS needs a wrapper!
It would be great to specify an algorithm to display tokens without image like identicon.
Non-image NFTs would still need some kind of image to represent on a marketplace assuming users use the marketplace with their eyes. We need to think about accessibility!
For new collections, stxnft.com goes with a format that looks something like this:
{ "collection": "StacksArmy", "name": "StacksArmy Captain #9", "image": "ipfs://ipfs/QmPPQBNKohiDYAnjKkdrQcf2LupqixFMEpyvnNWQXz7ozq/StacksArmy%20Captain%20%239.png", "attributes": [ { "trait_type": "class", "value": "Captain" } ] }
This is similar to the OpenSea metadata format, with the addition of the additional field
collection
.This is because right now SIP-009 doesn't support "collection-level" metadata (
ERC721Metadata
for example, specifiesname()
andsymbol()
functions). I'm not terribly opinionated on whether this should be done at the contract level with an additional function to get the name/symbol, or whether it should be handled inget-token-uri
as we do now.To me, the OpenSea metadata format works just fine with this collection-level metadata caveat. I'd support either (a) an extension to SIP-009 allowing for NFT contracts to return a name and symbol (e.g.,
get-name
andget-symbol
functions which take no arguments), or (b) adopting a metadata format that is OpenSea + some field to specify the collection name.
The specification for metadata should apply to individual NFTs ... for collection data, that should be included in attributes. ie. a "collection" is not an nft. Ie for Boom NFTs the metadata would look something like this below.
{
"name": "Foo #101",
"image": "ipfs://somerandomecid",
"attributes": [
{ "collection_name": "Foo Collection" },
{ "collection_size": "10000" },
],
"properties": {
"prop_1": {
"prop_type": "string",
"display_type": "string",
"value": "foo",
},
"prop_2": {
"prop_type": "number",
"display_type": "number",
"value": 99,
},
},
"localization": {
"uri": "ipfs://somerandomcid/{locale}.json",
"default": "en",
"locales": ["en", "ar-eg", "de"]
}
}
Further I suggest: name - required image - required, can be blank attributes: optional. Client may optionally display. properties: optional. Client may optionally display.
@dantrevino properties and attributes should be swapped, shouldn't it? At least that is how opensea defines it: https://docs.opensea.io/docs/metadata-standards#metadata-structure
attributes consists of trait_type, display_type and value.
properties can be anything.
@dantrevino properties and attributes should be swapped, shouldn't it? At least that is how opensea defines it: https://docs.opensea.io/docs/metadata-standards#metadata-structure
attributes consists of trait_type, display_type and value.
properties can be anything.
yup got that backwards
@Jamil Thank you for your review!
What is a good way to describe image_hash
?
Thanks for initiating this SIP @friedger. Something I'd like to point out is that this SIP does not include requirements needed for implementing NFT metadata support in the current Stacks Blockchain API. See issue https://github.com/hirosystems/stacks-blockchain-api/issues/846
For example, the SIP does not include anything related to making the metadata easily (and at scale) accessible for a Stacks Node event observer such as the API. That would entail something like a print
(AKA contract log event) requirement to output the NFT metadata URI (possibly more, like all mandatory properties of the metadata schema) on NFT mints. It also doesn't address a concern that has been brought up a few times around "refreshing" NFT metadata, for example, a metadata "reveal" sometime after an NFT is minted. That would likely also entail something like a contract log event to inform consumers to "re-resolve" the metadata for a given NFT.
These concerns may be out of scope for this SIP, but I figured it would be good to bring up in case anyone was planning on API support related to this SIP.
@friedger re raw media - things like real world addresses, maybe thats too much feature creep? personally I'd like to nail digital assets first without adding the kitchen sink.
Did you see this example from the solana ecosystem just allowing files to be added. isn't this similar to the spec for files and folders for IPFS?
one other comment, this might not be the place.. but when minting an NFT on a gas-cheap chain like STX, i'd like to add metadata to allow customization of NFTs, and keeping some of that data on-chain.
SIP009 sets out the transfer
fn but it doesn't take any extra optional parameters.
so while this spec allows for richer metadata, i'm wondering where that metadata lives. For simple text metadata, we might want to look at eventually removing the need for external URLs and just exposing it directly from the contract (much like solidity allows for SVG data on-chain). Hence standardized functions for getting data in, and reading it back out, and also a very simple and type-limited version (no 'any') of the spec since Clarity is so strict on types.
Thx @friedger other than the any
comment from @kyranjamie looks good
@friedger you mentioned this PR supersedes https://github.com/stacksgov/sips/pull/18 -- is NFT class-level metadata intended to be out of scope? The previous PR specified a (get-nft-meta () ... )
.
The API has some initial support for resolving and indexing NFT class metadata (similar to FT metadata), but it's disabled by default, and was pending ratification of the previous NFT metadata PR. See issue https://github.com/hirosystems/stacks-blockchain-api/issues/742
Actually @zone117x reminds of a good point ...
One thing that should be added to the SIP, imho, is that a protocol is not specified.
https://, ar://, ipfs://, sia://, etc, etc, as well as any future protocols should all be valid.
Section about uri schemes has been added @dantrevino
Metadata function have their own section now @zone117x As the metadata is not stored on-chain the api node is maybe not the right place to cache the meta data.
I have added more properties to make the metadata compatible with opensea. I even removed the requirement for a value
of properties. I refused to add youtube_url
because in a few years nobody knows what youtube was. I added a generic files
property taken from metaplex (solana). @dcsan
I came across this project which is one of the first "on chain" STX generative art projects I've seen: https://stxplates.xyz/
discussion: https://discord.com/channels/621759717756370964/877879077569171456/911219717610545193
This uses a 'bytes' field to expose some metadata from the contract, used for rendering the generative art.
Since clarity requires the exact string len, emitting metadata directly from the contract in the tokenURI seems very impractical.
But this bytes
field approach where the seed for the artwork is always the same length is very practical. This is similar to artblocks (docs link) which just passes in the last mint hash. It makes the artwork deterministic and reproducible which is critical for value - not just random each time.
The contract metadata for Plates is like below, but perhaps this bytes
field (or seed
maybe?) could go elsewhere in the final JSON hierarchy.
I think the process is a bit duplicative since the JSON metadata here is just stored in IPFS, not read in realtime from the contract. But all of the required data to render the artwork is inside the contract, and the json could be reconstructed, so buyers can be assured their art isn't going to vanish at some point
returns:
(ok (some "ipfs://Qmd4zgFFhe85roe9e9zDVzP5GquaRAgbV7KaS524mMVQaz/624.json"))
so view it here: https://ipfs.io/ipfs/Qmd4zgFFhe85roe9e9zDVzP5GquaRAgbV7KaS524mMVQaz/624.json
gives
{
"external_url": "https://stxplates.xyz",
"image": "ipfs://QmNNfMNaH7wJkhhkfpyDyiLy5VEKbncoQ7oknv6ZGqSN1d/624.png",
"description": "STXPLATES is a collection of 10,000 randomly generated, unique, on-chain NFTS for the Stacks community. This is plate 44L5GHE6R",
"name": "44L5GHE6R",
"attributes": [
{
"trait_type": "Text",
"value": "44L5GHE6R"
},
{
"trait_type": "Text Color",
"value": "#F90F1B"
},
{
"trait_type": "Background Color",
"value": "#B5E3FC"
},
{
"trait_type": "Border Color",
"value": "#303649"
}
],
"artist": "@txp0st",
"collectionSize": 10000,
"index": 624,
"bytes": "666666666666666666666688888888888888888886688822888822882888886688282888282882888886682882882882882888886682222282222282888886688882888882882222286688888888888888888886682222288222282888286682888882888882888286682222882882282888286688888282888282222286682222888222282888286688888888888888888886682222288222282222886682888882888882888286682228882222882888286682888882888282222886682222288222882888286688888888888888888886666666666666666666666",
"collectionName": "STXPLATES"
}
This SIP has been moved to accepted by the author. Please take the next steps @jcnelson
The spec now includes a property sip
that is the number of the sip that specifies the json schema for the meta data. In this case 16
.
Property version
has been removed.
Not saying I am against the sip
field in favour of version
but I wonder if it may be too restrictive. (Still trying to think of scenarios.)
However, I like that it is nice and short. I'll bring it into the next draft for SIP013.
Not saying I am against the
sip
field in favour ofversion
but I wonder if it may be too restrictive. (Still trying to think of scenarios.)However, I like that it is nice and short. I'll bring it into the next draft for SIP013.
I think its more descriptive than 'version' ... the version really does refer to a sip, and the version would not change without a new sip ... for that reason, I think its actually more clear.
Hey @friedger @dantrevino, thanks for being patient -- I didn't see the original github notification from December. I left my initial feedback pass. Hope it's helpful!
Right now, the consideration advisory board for technical-track SIPs has no members besides myself [1]. That said, I'm happy to advance this to Recommended
status on the board's behalf if there are no other objections or feedback. Once at least one mainnet contract is using this trait, the SIP can be transitioned to Activation-in-Progress
. Feel free to add my email to the Signed-off:
section in the preamble.
[1] I want to change this -- the governance group is happy to take new members. The list of all consideration advisory boards and their members can be found here: https://github.com/stacksgov/sips/tree/main/considerations
seed
|string
| hex string represented the DNA of the NFT. The seed is usually stored on-chain, it might be contained in the metadata for convenience.
I would be tempted to make the seed a plain/not hex string, unless there's a real benefit in terms of gas or other on-chain storage for using hex. Some people might use it to just store a text message, like a title or a couple of words. As (hex) data the stacks API functions had problems automatically detecting types when returning as JSON, mixed with other fields, and also this required conversions before storing in a DB etc. Overall it makes things much more cumbersome. It reminds me of the days before unicode of all kinds of ugly encoding errors.
seed
I would be tempted to make the seed a plain/not hex string, unless there's a real benefit in terms of gas or other on-chain storage for using hex. Some people might use it to just store a text message, like a title or a couple of words. As (hex) data the stacks API functions had problems automatically detecting types when returning as JSON, mixed with other fields, and also this required conversions before storing in a DB etc. Overall it makes things much more cumbersome. It reminds me of the days before unicode of all kinds of ugly encoding errors.
My idea was to provide a well-defined machine processable field. If it is not useful, could you please provide a better description, maybe also a better name instead of seed
? Maybe we want to define two different fields. @dcsan
If the API has a problem then we should fix the API not the standard :-)
seed
is fine as a name, i think its over-complex to have two fields for this too.
the API is fine when it's reading just one data type but when it's a mixed struct it fails.
But it's unavoidable that this also means the client has to encode data specially for this seeding, which is just extra potential for error, and in fact occurred for me. What worked in the test suite didn't work when run through (hiros) API contract functions.
So, i'd suggest to keep it simple so we can see and confirm what's getting posted in, and what we get out - as strings, without adding a double-dutch sandwich of encoding/decoding for little purpose, that (currently) mangles the data.
I think UTF8 would give a lot more options for content too, notwithstanding Terje's point. Even if it is a minefield internally at least it's relatively standard now.
@dcsan I replaced the description with something more general. The on-chain seed can use a different type.
@jcnelson The schema has been validated using JsonValidator. See https://github.com/Light-Labs/media-metadata-schemas The found errors has been fixed.
Just checking in. Is there a mainnet contract using this trait yet?
@friedger Feel free to advance the status of this SIP to Activation-in-Progress
, per my earlier comment. I can't commit directly to your PR since it's in your repo.
Hi all just wanna make sure this is captured officially.
During last Friday's weekly SIP call https://github.com/stacksgov/sips/issues/79#issuecomment-1212993156 I raised this point.
According to the activation criteria: "This SIP is activated if 10 contracts are deployed that follows this specification. This must happen before Bitcoin tip #750,000."
We are now at bitcoin block 749,565. 435 blocks left as of writing, which is approx. 3 days.
So I'm now working with the NFT marketplaces to fetch data to confirm 10 contracts have been deployed already. Will update all how it goes.
There's a miss here @friedger ... this is not a contract sip, its a metadata sip. @Hero-Gamer there is no way to look at a contract and determine if the metadata supports sip-016. The contract only has a link to metadata, but, for instance, the boom-300 "contract" was deployed 10 months ago, before the sip was complete, and initially we created nfts that were not sip-016 compliant. However, now it is deploying nfts that are sip-016 compliant. Same contract.
https://explorer.stacks.co/txid/SP1QK1AZ24R132C0D84EEQ8Y2JDHARDR58R72E1ZW.boom-nft-300?chain=mainnet
both minted from the same contract: non-sip016 nft, created jan 17: https://bafkreie3abg3pp4573zkjmxcgaosifpyof5hi5gxelnqutnyhelihjqeqq.ipfs.nftstorage.link/ sip016 nft, created recently: https://bafkreicg63zfryk2cvrucztb5bztg4vh6hj2e65yxcr7nld2rtryobigcy.ipfs.nftstorage.link/
Our Create Portal contracts are almost SIP-16 compliant but not quite, since we don't have the required "sip" field in the JSON. Working with our developers to add that for all future contracts, but not sure 10 contracts like this will be deployed by Wednesday. What is the process if this SIP does not meet the requirements by then, and we have to re-start?
I found these with "sip": 16
in metadata:
name | contract | metadata_url |
---|---|---|
Boombox [10th Edition] | SP1QK1AZ24R132C0D84EEQ8Y2JDHARDR58R72E1ZW.boomboxes-cycle-32 | https://cloudflare-ipfs.com/ipfs/bafkreigzei42ktazjl3b6ounitwazbmcz7zzn5vb7poevx2xoroau7ftnm |
Boombox [12th Edition] - Bloombox by Grace Hye | SP1QK1AZ24R132C0D84EEQ8Y2JDHARDR58R72E1ZW.boomboxes-cycle-36 | ipfs://bafkreidowklqh7nub2dxrh2mj4tu46inqtapqfidqnwk5taown5w4li6pq |
Boombox [13th Edition] | SP1QK1AZ24R132C0D84EEQ8Y2JDHARDR58R72E1ZW.boomboxes-cycle-38 | ipfs://bafkreibduyxkdcp3djswzjjoexoi6hcvk2suaoyri2uqpiuyxbhkh6bwxe |
Boombox [14th Edition] - STX of Toys | SPMS4E9RQ4GCGG68R6D15PKV01TYNCBPYZG1ZMFE.boomboxes-cycle-40 | ipfs://bafkreieanobksr6qetgqupx7zpn2aebw742siwsqa4ef6i7aybxvnku2ze |
Boombox [8th Edition] - Moonbox | SP1QK1AZ24R132C0D84EEQ8Y2JDHARDR58R72E1ZW.boomboxes-cycle-28 | https://cloudflare-ipfs.com/ipfs/bafkreic3emy7vy4azvm56sy3dwsuny4653uzi7ubb36ui63w424kagzfym |
Boombox [8th Edition] - Moonbox | SP1QK1AZ24R132C0D84EEQ8Y2JDHARDR58R72E1ZW.boomboxes-cycle-28-moonbox | https://cloudflare-ipfs.com/ipfs/bafkreic3emy7vy4azvm56sy3dwsuny4653uzi7ubb36ui63w424kagzfym |
Boombox [9th Edition] - Mushroom by Flower | SP1QK1AZ24R132C0D84EEQ8Y2JDHARDR58R72E1ZW.boomboxes-cycle-30 | https://cloudflare-ipfs.com/ipfs/bafkreibszx62vqkce7pefr6xtcqvyec7lo5xdjy6c7r3dhfd5bkl3wyvku |
Technically yes, you would need to restart in general since it indicates that the SIP doesn't have enough interest. But it seems that in this specific case, the only reason this SIP hasn't activated was due to a bug in the implementation (e.g. a missing sip
field in the JSON), but there is otherwise sufficient interest. In this case, it would be fine by me to just combine the whole "withdraw, re-submit, accept, recommend, activation-in-progress" sequence into simply the act of increasing the activation-in-progress block height to whatever you need (a week?). The effect is the same either way; the SIP text wouldn't be changing and the activation-in-progress section only needs the steering committee's sign-off (which right now is just me).
So yeah, go ahead and change the block height and keep everything else as-is.
Here is another one: https://explorer.stacks.co/txid/SP1QK1AZ24R132C0D84EEQ8Y2JDHARDR58R72E1ZW.boom-nft-300?chain=mainnet Makes it 8.
No. 9 - a new one deployed a few hours ago: SPQE8N8BHMT462W2XPK028GDM4RMQBSHAAY8D37G.crashpunks-punkettes https://explorer.stacks.co/txid/0x95c03cb957324a9983430b4419c4d4180d13ca5b067cda7e23d314df4f762443?chain=mainnet ipfs://QmcUiKLAeYbx78UZWoCuyKdm5GhZH6Jhto9yhKt1oPvWHY/
153 bitcoin block time left, approx. 1 day. (current block time 749,847) Who else/where else can we ask to find the last contract required? almost there!
The contract above (SPQE8N8BHMT462W2XPK028GDM4RMQBSHAAY8D37G.crashpunks-punkettes
) was deployed with the Gamma Create Portal, which now has the sip
field in the JSON.
The next contract deployed with the Gamma Portal will be the 10th :)
No. 10? This NFT has a dynamic metadata. The latest mint has a SIP-016 metadata, prior mints were not.
Latest mint with "sip": 16
:
Earlier mint example without "sip": 16
:
If all contracts checks out: Current Bitcoin block height: 749,876 https://mempool.space/block/0000000000000000000894d96480628dcd38fd9424750e9d2638aa75233e19f0
Awesome to see! I will take a look as soon as I am able, and if it all checks out, it will be my pleasure and honor to advance this to Ratified :tada:
Here is another one: https://explorer.stacks.co/txid/SP1QK1AZ24R132C0D84EEQ8Y2JDHARDR58R72E1ZW.boom-nft-300?chain=mainnet Makes it 8.
@friedger I'm not sure where to look for the metadata URL here? This appears to be a contract deploy from 6 months ago.
The rest look good to me.
This PR