Extending NEP-171 Events Standard to include NFT `update` events
- lachlanglenOP 5
Summary:
To extend the NEP-171 events standard to include
nft_updateevents.Motivation:
The existing events standard for NFTs as described in NEP-171 does not include any events to be logged when the metadata for an individual NFT is updated.
An increasing number of NFT projects (e.g. NEAR Future) are creating upgradable/evolving NFTs, where the individual NFT will change over time. A
nft_updateevent would notify listeners of these updates, ensuring consistent rendering of the latest NFT data.Some projects and marketplaces (e.g. TENK, Paras, NEAR Future) have implemented a solution for this and the purpose here is to discuss this implementation, iterate as necessary and move to standardize it.
Specification
nft_updateEmitted by NFT contract when any field of
TokenMetadataor its associated off-chain data (e.g.referenceJSON) is updated.Not sure if this should include the updated data in question - this could get large for multiple
token_ids. Suggest instead that the receiver can then querynft_token()for the providedtoken_ids to get the updated data.EVENT_JSON:{ "standard": "nep171", "version": "1.1.0", "event": "nft_update", "data": [{"token_ids": ["1", "2"]}] } - minorityprogrammers 2
- Outside of listening for updates, I think there should be a way to check whether a NFT updates (is dynamic) vs static, so indexers + marketplaces can optimize which NFTs they are listening for. One of the things that I’m most skeptical about is NFT projects rugging on the art and updating it later when the buyer did not intend for this. Would appreciate it if nonupdatable static NFTs had a lock next to the URI and attributes on marketplaces to indicate this. Would also appreciate if this was indicated by a number_of_updates where
- — 0 = static, 1-many (phased), and -1 meant this NFT can be updated forever (dynamic NFT)
- With this, would appreciate And there was also a way to check the times a NFT has been updated
- A use case for phased NFTs is like Pokemon, where you know a NFT has a certain number of levels, until it reaches its final form, where it becomes static again.
- Would also appreciate it if this was combined with a rich media standard in development as we could tell if the update also changed with the file type, but an aggregation of all these features may result in greater chances of having standard denied
- lachlanglen 1
Great points @minorityprogrammers, thanks for the comment!
Re. the first point, this sounds like it could benefit from more robust conversation, but I would argue that the specific implementation details of these kinds of “dynamic” NFTs are beyond the scope of this specific standard addition. Ultimately, developers and project creators will make NFTs that have the functionality specific to their use case, and I imagine this could be a controversial topic. However, I believe a less controversial point is that if/when an update is made, NFT UI’s should reflect this update (at the very least so that the owner is aware that their NFT has updated/evolved), so I would propose that this basic event is implemented first; otherwise IMO we risk putting the cart before the horse and agreeing on nothing rather than making incremental progress. The reality to me is that NFTs are already evolving/updating (see NEAR Future), and as a marketplace we need to know about these updates.
However, if you and others feel that this standard shouldn’t be pushed through before these larger questions are answered, I should probably split these two events into separate proposals, as I think the
contract_metadata_updateevent is less controversial and frankly that’s the one that we really need right now, as a marketplace.Re. rich media metadata - perhaps this proposal would encompass what you are describing? E.g. according to this proposal, if the media changed (even a minimal change such as filetype), an
nft_updateevent would be logged. Or perhaps I’m not quite understanding, in which case feel free to correct me! - nearbuild
For rich media however standard would still be needed to interpret file type even before a change or update occurred. I believe the rich media standard would optimize display different media types for static NFTs as well.
- starpause 1
Good stuff, love to see the work TENK + Paras put in for NEAR Future Primal getting baked into standards everyone can benefit from!
minorityprogrammers:so indexers + marketplaces can optimize which NFTs they are listening for.
The benefit of events is being able to efficiently listen to them from all assets.
Permanently locking a collection (so that the ART URI or other metadata can not change) can be done by removing all keys from the contract. Whether or not there are keys on a contract is publicly visible.
lachlanglen:“dynamic” NFTs are beyond the scope of this specific standard
It would be beneficial for the community to agree on some definitions.
When I think of a dynamic NFT I think of something which doesn’t require events to update itself. Something that’s dynamically rendering itself with code on the client side which allows for data to be pulled from oracles or HCI inputs like webcams/kinect/etc.
There are a different class of dynamic NFTs that render themselves with code on a server and update the image sitting at the NFT art URI. This kind of NFT do benefit from events in case a marketplace or wallet has cached a previous render of the NFT image. Maybe the update standard can have a way for the NFT to indicate NOT to cache the image and always go to the URI (something like the suggested -1 forever updating proposed by @minorityprogrammers ). Otherwise the events channel will become full of spam from NFTs that are constantly re-rendering themselves.
- minorityprogrammers
I have been under the impression the dynamic NFTs or dNFTs meant that NFTs were not static and could be changed. This may be from a data stream, on chain interactions, or predetermined phases or preuploaded URI links like in pokemon as Starpause is mentioning.
Decrypt – 27 Jul 22

What are Dynamic NFTs? The ‘Living’ Tokens That Change Over Time - Decrypt
Dynamic NFTs, also known as Living NFTs, use smart contracts to change based on external data sources. Here’s how they work.
To the point of removing all keys, this seems not directly the same thing as the inability to change metadata. Would be interested to brainstorm on possibilities for creating a locked contract with the ability to still change NFT metadata. Also would be interested in brainstorming scenarios where a contract isn’t locked but NFTs can only remain static.
- nearbuild
UPDATE: @lachlanglen will be separating this into general contract events as NFTs are adding complexity that may get the standard rejected
- lachlanglen
Updated to focus on individual NFT updates, moved contract metadata updates proposal here: Extending NEP-171 Events Standard to include contract metadata `update` events
- till_mb 1
As much as I would be in favor of merging a token/contract metadata update standard, how about discussing these separately but tying their interface. First of all I’d use NEP177 as base for the events, as they clearly have more to do with metadata than with NFT core functionality. I’d also include updates of on-chain properties:
EVENT_JSON:{ "standard": "nep177", // use corresponding metadata NEP as event basis "version":" 1.0.0", "event": "nft_update_token", // name consistently with other events and contract update event "data": [ { "token_id": "1", "metadata": { "refererence": "..." } }, // add info on what exactly is updated { "token_id": "2", "metadata": { "media": "..." } } ] }This way, if I was to update whatever is stored in my reference, I could emit the event, leaving the URI unchanged, but notifying indexers that changes took place. I could also store everything in an IPFS/arweave/whatever repo, and change the reference to point to the current state of a dNFT, while leaving all possible states inspectable. If future states should not be inspectable, that’s certainly possible with this structure as well. I also propose to change the event name to stay consistent with other events/NFT standards and to relate it to my proposal on the NFT contract update.
The other part of this discussion is the data guarantee for immutable NFTs. Right now we have a base URI/reference/media combo with no mandate whatsoever to use permanent data storage. I have seen endpoints going down, so we could argue that we already have unintentional dNFTs. The most secure way to ensure that NFTs never change is including all the material on-chain or exclusively using decentralized permanent storage and then burning keys. However I haven’t tested yet if
near_sdk::Promise::add_full_access_key(sry, can’t link for some reason) used in a contract method might be able to add a key to an account with no key, voiding those guarantees as well. So even by marking NFTs or NFT contract as mutable/limitedly mutable/immutable, we never know what happens. There are more edge cases to this as well. E.g. a token wheremediamight change has vastly different implications to users as a token wherecopiesmay change. So ideally all those fields are marked with their mutability, and for now we would have to trust NFT contract implementors to behave as they advertised.Update: Need to wait until tomorrow until I can post my thoughts on the contract metadata update
- lachlanglen 1
Thanks for the comment @till_mb! Really happy that conversation is starting up on this again.
First of all I’d use NEP177 as base for the events, as they clearly have more to do with metadata than with NFT core functionality
I don’t have a super strong opinion here but can see both POVs. There is more to an NFT than its metadata, so this event could indicate a higher-level update at the
Token’s base level. But I also see what you’re saying. Honestly, I think most indexers will be looking for the event name rather than the standard anyway, so I don’t think it really matters that much either way. I’m open to changing tonep177if others share your POV.I also propose to change the event name to stay consistent with other events/NFT standards
I don’t understand - other NFT events are
nft_mint,nft_burnandnft_transfer. None of them contain “token”. I think the naming proposed here (nft_update) is consistent with existing events.I also don’t think that the updated data should be required in the log. There is a 16kb log limit and certain items (e.g. a base64 media string - not that anyone I know actually stores media data on-chain, but technically it’s allowed) may surpass this limit. I am in favor of just specifying the
token_idand allowing indexers to callnft_token()to get the updated Token. Perhaps updated fields could be specified, but that adds complexity once you get into nested structures and it’s not like knowing the fields that were updated makes it any less work to get the updated data - you still have to callnft_token().The other thing to bear in mind is that many projects (Few and Far, Paras, TENK, probably others) already have these events running in production in the format described in my original post, so any changes should be essential (IMO) as these would require a lot of contract upgrades and indexer updates.
- till_mb
Regarding the NEP177/171, I see your point that this update could be more than just metadata. Our indexer uses the full triplet, and that saved us from some spam engines that would just mint garbage with wrong triplets. All in all I think this is the least point of debate.
Regarding event names,
nft_updatemight be more consistent, but it’s better to have clear distinction between contract-level update and token-level update. Given that thenft_metadatamethod returns contract metadata, I interpret thenft_prefix to tell me that this method/event is from an NFT contract, and not necessarily related to a NF Token. So annft_updateevent for me still has the question attached as to what is being updated, as it is way less obvious than withnft_{mint,transfer,burn}. The consistency comment was more update the update contract event. I find thecontract_update_metadataquite confusing, especially if you’re telling me that you’re not really paying attention to the standard part of the event triplet. How do you know that the event was emitted from an NFT contract and not some FT contract or DAO or anything else? So IMO the contract update should have anft_prefix. And from there on having a clear line between update token and update contract is logical to me. Also updating the indexer with a new event name if the data might be incompatible with the existing, pre-used format is actually a plus, since that way you can update backwards-compatible and go back in time more easily than by coding special rules for block heights when events were updated in a per-contract-level.The 16 kB log limit is kind of a point, since I already saw people using the media field to store base64. I do not like the current pattern of the indexer having to call RPC methods, as it breaks unidirectional data flow and creates HTTP requests that increase cloud costs and load on all systems (NEAR, yours, ours). So replacing
nft_tokencalls with event logs would be a big improvement, but that might better be paired as an NEP that allows for increasing the log limit and then the mint event should emit metadata as well. Making metadata optional in the update event could spare us some of that, but create one path for unidirectional and another path for the current data flow. I think that’s more an introduction of complexity than is worth for our road to have sane data flow.All in all, no problem with NEP171/177, I think we can skip emitting metadata, even though current data flow is messy. On the event name I am still convinced that
nft_updateis not really clear. I could also make my comment on the proposal for updating contract metadata. If everything else stays the same, all you need to do on your indexers is:match (standard, version, event) { ... (_, _, "nft_update") => handle_update(data), (_, _, "nft_update_token") => handle_update(data), ... }Ofc this assumes you are ignoring standard as you said, not sure what you’re doing with version. But it’s literally one line of code. Let’s just make the standards as clear and self-communicating as possible.
- lachlanglen
@till_mb I agree that the event naming hasn’t been clearly implemented from the start.
nft_metadatais the worst-named method IMO. My approach working with (and extending) NEP-171 has been to treat that particular event as an anomaly, and usecontract_prefix for contract-level events, andnft_prefix for NFT-level events. So while I completely agree with your point, my proposal would be to make the NFT update event consistent with existing individual NFT events (nft_) and just remember thatnft_metadatais actually a contract metadata method. In our spec we have an additional methodcontract_metadatawhich returns the same result asnft_metadatajust to avoid this confusion. I think this is an easier solution for now, but honestly I think we need a new standard which addresses this and many other issues with NEP-171. But that’s a topic for a different conversation…I do agree that it would be helpful to have the contract “type” available somewhere, but maybe that belongs as a separate field on the Event object rather than a prefix on the event name?
Re. logs - another serious consideration is that logs are stored on-chain, so I think it makes sense to minimize size and allowing interested clients to query.
Finally, to clarify, the contract metadata update NEP has passed and is being implemented, so that is no longer a proposal. You can see it here. The event name is
contract_metadata_update(which further strengthens the position of treatingnft_metadataas an annoying anomaly). - lachlanglen
@frol where do we go from here?
- lachlanglen
@frol, @starpause, @mattlockyer any thoughts on this?
- irfianto 1
Good stuff!
from Paras here, we are struggling with NFT projects that needed to update metadata, since they must contact us manually. We already provided an update API to NFT projects, but the proposal here will be necessary for the whole NFT space to know if an update is happening. Is there any way to speed up this proposal?
- lachlanglen
@irfianto Any thoughts on the discussion points raised here?
- frol
lachlanglen:An increasing number of NFT projects (e.g. NEAR Future) are creating upgradable/evolving NFTs, where the individual NFT will change over time. A
nft_updateevent would notify listeners of these updates, ensuring consistent rendering of the latest NFT data.I want to define the motivation more precisely. One alternative to
nft_updateis to emit two events:nft_burn+nft_mint, right? That would be a bit cumbersome, so I agree thatnft_updatemight be a better option.Here are my concerns:
- What is the definition of NFT change? Is it expected that
nft_updatewill only be triggered if the metadata is changed or might it also mean that the underlying media or reference has changed (while the URI in the metadata has not)?- I treat NFT to be the metadata that is stored on the chain and the set of common operations (function interfaces). There are no guarantees of transferability (transferable or non-transferable) or immutability - those cannot be enforced on the API level. (Just to throw some ideas here: we may create an accounting contract and force NFT/FT contracts to send all the operations through it, and that contract will track all the tokens to ensure consistency, but that feels like it will be a mega-multi-token contract after all).
- Making a view call for each
nft_update’ed token might be overwhelming (slowing down the indexers due to the extra network requests that introduce unpredictable latency, making indexers dependent on RPCs).- I don’t think it is a blocker, and I don’t see a better way, so indexers will probably need to be implemented in an asynchronous way (backfilling the data from RPC in the background)
- View-calls are granular to the block, so if the token was updated and burned or updated several times in the same receipt or in parallel receipts in the same block, there won’t be an easy way to get the content of the token.
- I don’t think it is a blocker, as the same problem is applicable to
nft_mint→nft_transfer→nft_burnin the same block/receipt.
- I don’t think it is a blocker, as the same problem is applicable to
- Some of the fields in NFT metadata might be irrelevant to the indexers (e.g.
extraandcopies), but at the same time, I believe, thenft_updateevent should be emitted if any of the fields are changed. This means that this will dump more work on the indexers that only need to index the title changes.- I don’t think it is a huge blocker, but we might at least consider allowing developers to specify which fields were updated (if smart contract developers are lazy and don’t provide them, indexers will need to consider that any of the fields could have changed and thus would need to make a view-call to check; yet, if the fields are provided and indexer does not care about those fields, then no view-call is needed)
till_mb:The most secure way to ensure that NFTs never change is including all the material on-chain or exclusively using decentralized permanent storage and then burning keys. However I haven’t tested yet if
near_sdk::Promise::add_full_access_key(sry, can’t link for some reason) used in a contract method might be able to add a key to an account with no key, voiding those guarantees as well. So even by marking NFTs or NFT contract as mutable/limitedly mutable/immutable, we never know what happens.Exactly, the contract still can add full access keys to the locked account, so there are no immutability guarantees (see my (1) point above).
lachlanglen:@frol where do we go from here?
Overall, the proposal looks reasonable to me, so I would suggest creating a NEP extension. Yet, I got stuck with the previous PR you submitted as it was not a standalone NEP document, instead, it was an edit of the nomicon, so I will need to refactor that into the NEP before you can submit a NEP extension to it.
- What is the definition of NFT change? Is it expected that
- lachlanglen
Sorry @frol, that’s how I was directed to do it. Please let me know how best to create this extension proposal when it is ready.
- frol
No problem. We were still in transition mode back then. I will refactor it by Thursday.
- frol 2
It took me less than I expected and NEP-0423 is now merged! Please, feel free to submit a new PR with the NEP extension to NEP-0171. There is no rocket science there, see how NEP-0423 became a NEP extension to NEP-0171; basically, submit changes to the NEP instead of the nomicon page (one day we will integrate NEPs directly into the nomicon
) - lachlanglen 1
- Benny_Blanco 2
That’s great. We are also working with dynamic traits through our new El Cafe Cartel NFT collection and agree wholeheartedly with this proposal. Its is of some urgency I would say.
- frol
Cool! If you want to expedite the process, you should join the discussions on the PR and also consider presenting your implementation and how it is used. E.g. there is still a concern that the proposed
nft_updatedata does not include more details which means that indexers that observe the logs will have to make extra view function calls for every updated token - is that an issue for you? (please, reply on GitHub) - Benny
Not an issue for us. The Near NFT standards will soon be pretty outdated so any move forward, however small, is a good result. I’ll join the discussion. Thanks buddy
