How to: ERC-1155 ID Substitution for Token URI

It is not completely true.
If the metadata is known ahead of time, you can create an ipfs folder with the filenames being the id as per the ERC1155 rules

if metadata is to be provided as part of a minting process, you can as @abcoathup mentioned implement the uri function yourself or emit the URI event. if you use uri you ll need to store the ipfs hash but can use folder to do so in batch (one hash per batch). This is how we do it for https://sandbox.game

4 Likes

That’s really clever @wighawag! Thank you for the tip. I’ll checkout the sandbox to explore this further and try implementing it myself.

2 Likes

Hi @wighawag,

Thank you :pray:

I didn’t realise that you could do [ipfs directory]/[padded token id in hex]

1 Like

Hello @abcoathup ,
sorry to revive this topic but I was wondering how to deal with the initializer of ERC1155Upgradeable when you override the uri() function.

Let’s say you use IPFS for your URI and then can’t really have a common base URI because we are in situation 2 of what @wighawag describes.

Then OZ implementation of setUri() and string private _uri are not longer relevant because each batch of the minted tokens will have its specific IPFS URI.
What should be done then? set URI with a null value? Ovveride the initalizer of ERC1155Upgradeable and getting rid off this setter?

thanks for your help

1 Like

Just ran into this myself. Like @wighawag said, I used an ipfs folder with the file names as ids.

For example: I uploaded a folder called “foo” with 3 images.
The url is then:
https://gateway.pinata.cloud/ipfs/Qme92XuB3CPHqGHiXC3pSkAcctoGEfdHBpKeMziTTkZ6/foo/{id}.json, with the same hash for the 3 images in the batch.
This way they have the same base url

1 Like

Hey @mpng yes I agree with that part.
But if one needs to mint tokens later, the folder won’t accept new json files unless we use IPNS. Yet in that case why bother lol.
So each batch will have a different base folder path and set_uri won’t work that well.
Alternative is to store just the IPFS:// tag but it’s not sparing much data.

1 Like

Did you find solution if we want to add more files to the folder with IPFS? Or any alternatives?

1 Like

I think I found a way to do this. But it is a bit hackish. The trick is to use a non-default hash for the CID that encodes to 65 characters in base16 and use the hash minus the leading "f" as token-id. Both sha3-224 and blake2b-208 should do the trick. Example with "Hello World" as content:

$ ipfs add hello.txt  --cid-version=1 --hash=blake2b-208
added bafkzvzacdjx7hmxsyps4q3kneb5vktqzfekadpj6yv34xotaxeea hello.txt

$ ipfs cid format -b=base16 bafkzvzacdjx7hmxsyps4q3kneb5vktqzfekadpj6yv34xotaxeea
f01559ae4021a6ff3b2f2c3e5c86d4d207b554e19291401bd3ec577cbba60b908
Token-ID: 01559ae4021a6ff3b2f2c3e5c86d4d207b554e19291401bd3ec577cbba60b908
Template: https://gateway.ipfs.io/ipfs/f{id}
Without gateway: ipfs://f{id}

Have not yet tested this but it should work in theory :grinning:

2 Likes

Great idea and it works not only in theory :slight_smile: So no more reasons to have centralized NFTs :slight_smile:

The URI function needs to be overwritten like this:

    import "@openzeppelin/contracts/utils/Strings.sol"
    
    function uri(uint256 _tokenID) override public view returns (string memory) {
    
       string memory hexstringtokenID;
         hexstringtokenID = uint2hexstr(_tokenID);
    
    return string(
        abi.encodePacked(
        "ipfs://f0",
        hexstringtokenID)
        );
    }

Here is an [example](https://soenkeba.medium.com/truly-decentralized-nfts-by-erc-1155-b9be28db2aae).
2 Likes

This is a really cool approach, actually our team is messing with this.

From this V1 using hash blake2b-208 = we get bafkzvzacdkm3bu3t266ivacqjowxqi3hvpqsyijxhsb23rv7nj7a
then from base16 we get
f01559ae4021a99b0d373d7bc8a80504bad782367abe12c21373c83adc6bf6a7e

The question is:
From base16 and removing the "f" we get 01559ae4021a99b0d373d7bc8a80504bad782367abe12c21373c83adc6bf6a7e, then we need pass to hex from this? i mean how to handle this function in contract function mint(address account, uint256 cid).
CID is a uint256 need to hex(01559ae4021a99b0d373d7bc8a80504bad782367abe12c21373c83adc6bf6a7e) ?

NOTE: i am using python so i guess this type need to be casted to int(hex(01559ae4021a99b0d373d7bc8a80504bad782367abe12c21373c83adc6bf6a7e), 0).

Thank you :slight_smile:

Just in case that somebody has the same issue:

in python solution is :

# Format base16 => hex => int
cid = int("0x" + cid_to_uint256(cid)[1:].decode('utf-8'), 0)
logger.log.info(f"uint256: {cid}")

Thank you guys

1 Like

Hi ...

this is a working example:

Just put the "0xHEXNUMBER" to call the mint function. It is then changed to base10 integer. Open Sea, etc. will call the URI function with that number which will be changed back to a hex string with the uint2heststring ...

Let me know how it goes ...

2 Likes

Hey, thank you for your response. Yeah, actually i figure it out and now it working with python:
Please follow this: How to: ERC-1155 ID Substitution for Token URI - #18 by Geolffrey_Mena

Regards

Actually it can be done even simpler with the default sha256 hash if you add the static CIDv1 prefix to the URI template:

$ ipfs add hello.txt --cid-version=1
added bafkreigsvbhuxc3fbe36zd3tzwf6fr2k3vnjcg5gjxzhiwhnqiu5vackey hello.txt

$ ipfs cid format -b=base16 bafkreigsvbhuxc3fbe36zd3tzwf6fr2k3vnjcg5gjxzhiwhnqiu5vackey
f01551220d2a84f4b8b650937ec8f73cd8be2c74add5a911ba64df27458ed8229da804a26
Token-ID: d2a84f4b8b650937ec8f73cd8be2c74add5a911ba64df27458ed8229da804a26
Template: https://gateway.ipfs.io/ipfs/f01551220{id}
Without gateway: ipfs://f01551220{id}
3 Likes

Hello guys, I'm having erc1155 smart contract which is using following base url: https://token-cdn-domain/{id}.json . When i try to get the uri for token with id 5 for example i receive this: https://token-cdn-domain/5.json but in ipfs they are stored with leading zero padded to 64 hex characters length and 5 looks like this 0000000000000000000000000000000000000000000000000000000000000005.json any idea how to provide the correct uri?

1 Like

great its really help me. thank so much

Actually, since the openzeppelin URI function is virtual, you can override it to change the URI each time you mint tokens. See https://medium.com/coinsbench/fully-decentralized-erc-721-and-erc-1155-nfts-6c229adf9c9b

contract MyNFT_ERC1155 is ERC1155 {
    mapping (uint256 => string) private _tokenURIs;

    constructor() ERC1155("Anything_you_want") {} 

    function uri(uint256 tokenId) override public view 
    returns (string memory) { 
        return(_tokenURIs[tokenId]); 
    } 
    function _setTokenUri(uint256 tokenId, string memory tokenURI)
    private {
         _tokenURIs[tokenId] = tokenURI; 
    } 
}

We have a simple implementation of this idea in in python documented here: https://core.iscc.codes/utilities/utils/#iscc_core.utils.cidv1_hex ...
See functions:

cidv1_hex()
cidv1_to_token_id()
cidv1_from_token_id()

maybe that helps.

I have a similar question around ID substitution.

The standard implies that the {id} MUST be replaced in all attributes within the metadata JSON. In practice I am not seeing this. Does anyone have any experience with this?

I have implemented the example where v1 CID uri implemented

1 Like