vincentdnl

Update NFTs metadata when using IPFS

October 14th, 2022

NFTs are tokens living inside a smart contract. It often represents media, like an image. Due to its distributed nature, storage on a blockchain is costly, making it impractical to store large assets.

That's why NFT contracts usually just store a URI telling where to find NFT metadata. These metadata contain the NFT attributes (like title, description, image, etc.) and are stored somewhere else. This storage can be centralized (a server) or decentralized (IPFS).

As an NFT creator, sometimes you need to modify the asset of an NFT. You might have messed up something (made a typo in the title or added the wrong image), or you just want some sweet reveal mechanism.

If you store your NFTs metadata on a centralized service (most websites on the internet are), you won’t run into any of these issues as you have full control over your server. You can create, update or delete tokens metadata whenever you want!

For some usages, that’s fine. For example, if the NFT is supposed to be burned quickly you would not need to worry about what follows. But for most cases, NFT holders expect their token metadata to be immutable! One of the pros of doing something on a distributed and decentralized system is that it will be available forever (or as long as the Ethereum blockchain is online). No one wants their favorite NFT to be changed to something awful by the contract owner!

That’s why most NFT projects chose to host their metadata on IPFS. IPFS (InterPlanetary File System) is a protocol that I would describe as a decentralized Dropbox or Google Drive. Once a file is uploaded on IPFS, it gets a unique content identifier (CID). If the same file is uploaded on IPFS, it will get the same CID. But what if you upload a revised version of the same file? Well, it will get a different CID. There is no way to update a file on IPFS.

IPNS (InterPlanetary Name System) was created to solve this problem! IPNS acts like a pointer toward a ressoure. Except this pointer can be updated! If you made a mistake on your image, for example, you can upload a new version to IPFS and change your IPNS to your new version! Isn’t that great?

Well, it is but in our case I would do things a bit differently. What’s the problem with IPNS? The pointer can be modified at any point in the future, meaning we are back to the same problem as with the centralized version. Also, I think it’s better to keep mechanisms like this in the smart contract, for transparency reasons.

For me, the best solution is to use updatable URIs (identifier for a ressource) and a permanent lock in the smart contract. Here is how it works:

  • There is a base URI for the NFTs metadata, like for example ipfs://QmTecK6aZLBteHcx7zP7jCgWELFwkPPgF4aWBJmB7RJnDg/
  • This URI is prefixed by the token ID in the smart contract, like for example ipfs://QmTecK6aZLBteHcx7zP7jCgWELFwkPPgF4aWBJmB7RJnDg/12345 (where 12345 is the token id)
  • This base URI can be updated in the smart contract with a function:
string public baseURI = "";

function setBaseURI(string memory newBaseURI) public onlyOwner {
    baseURI = newBaseURI;
}

As of Open Zeppelin implementation of the ERC721 standard (NFT tokens), a _baseURI function should return that URI.

  • To prevent it to be callable forever, we must implement some locking mechanism. This is very straightforward:
string public baseURI = "";
bool public isBaseUriLocked = false;

function setBaseUri(string memory newBaseURI) public onlyOwner {
    require(!isBaseUriLocked , "Base URI has been locked and cannot be changed anymore!");
    baseUri = newBaseURI;
}

function lockBaseUri() public onlyOwner {
    isBaseUriLocked = true;
}

With this implementation, the contract owner can update the URI if they messed up the token metadata and at the same time be able to lock the metadata and provably guarantee their long-term immutability! This is the way I would go If I was making:

  • A reveal → possibility to lock the metadata after the reveal
  • An open edition mint → possibility to add more tokens if a lot of people mint them and lock the contract after the end date of the open mint
  • Make sure I can fix metadata for some time after releasing a collection

This article is the result of my learning at a given time. Things might change in the future. If you have a different opinion on the topic feel free to open a discussion with me. Feedback is most welcome!

Edit 1: You should also emit a specific event in your lockBaseUri function once you freeze your metadata.

event PermanentURI(string _value, uint256 indexed _id);

See this article from OpenSea for more explanations.