0

I am developing a NFT contract on ETH using open Zeppelin, and reached the stage where, I am deploy on rinkeby. However there appears to an issue with the tokenURI and contractURI as the image and metadata is not showing on opensea.

I implement the tokenURI and ContractURI like this:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.1;

import "hardhat/console.sol";
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
//access control
import "@openzeppelin/contracts/access/Ownable.sol";

// Helper functions OpenZeppelin provides.
import "@openzeppelin/contracts/utils/Counters.sol";
import "@openzeppelin/contracts/utils/Strings.sol";

import "./libraries/Base64.sol";

contract MetropolisWorldGenesis is ERC721, Ownable {

....

string private _contractURI;

function contractURI() public view returns (string memory) {
        return _contractURI;
    }

function tokenURI(uint256 _tokenId) public view override returns (string memory) {
        PropertyAttributes memory propAttributes = nftHolderAttributes[_tokenId];

        string memory json = Base64.encode(
            abi.encodePacked(
                '{"name": "',
                propAttributes.name,
                '", "description": "',propAttributes.description,'", "image": "',
                propAttributes.image,
                '", "attributes": [ { "trait_type": "Tower", "value": ',propAttributes.properties.tower,', "trait_type": "District", "value":',propAttributes.properties.district, ', "trait_type": "neighborhood", "value":',propAttributes.properties.neighborhood,',]}'
            )
        );

        string memory output = string(
            abi.encodePacked("data:application/json;base64,", json)
        );
        
        return output;
    }


    function setContractURI(
        string memory name, string memory desc,
        string memory image,string memory link, 
        uint royalty) public onlyOwner() {

        string memory x = Base64.encode(
            abi.encodePacked(
                '{"name": "',
                name,
                '", "description": "',
                desc,
                '", "image": "', 
                image,
                '", "external_link": "', 
                link,
                '","seller_fee_basis_points":"', royalty, // 100 Indicates a 1% seller fee
                '", "fee_recipient": "0xA97F337c39cccE66adfeCB2BF99C1DdC54C2D721" }' // Where seller fees will be paid to.}
            )
        );
        _contractURI = string(abi.encodePacked(
                "data:application/json;base64,", x
            ));
        console.log("contract uri updated");
    }
}

When run locally the return the tokenURI gives the following out put, which resolves to correct data in the browser:

data:application/json;base64,eyJuYW1lIjogIkdpbGRlZCBBdHRpYyIsICJkZXNjcmlwdGlvbiI6ICJUaGUgQXN0cm9ub21lcidzIFN0dWRpbwpVbmRlciB0aGUgZG9tZSBpcyBhIHBsYW5ldHJpdW0gClJlc2VhcmNoZXMgdGhlIG9yaWdpbnMgb2YgdGhlIE1ldHJvcG9saXMiLCAiaW1hZ2UiOiAiaXBmczovL1FtWnc3NzY1ZG04aHBIRndITDl3b29RSkNzSkd6TFR3WTIyNjVDR1lpeFB3aUciLCAiYXR0cmlidXRlcyI6IFsgeyAidHJhaXRfdHlwZSI6ICJUb3dlciIsICJ2YWx1ZSI6IFRvd2VyIDEsICJ0cmFpdF90eXBlIjogIkRpc3RyaWN0IiwgInZhbHVlIjpIaWdoLUZseWVyLCAidHJhaXRfdHlwZSI6ICJuZWlnaGJvcmhvb2QiLCAidmFsdWUiOkZyZWUgQWxsZXkgUXVhcnRlcnMsXX0=

Any Ideas where I am going wrong?

2 Answers 2

0

Token uri should be a URI, not a JSON type. You should upload your jsons to a server or something like IPFS and take your URIs to your contract.

For more info check the original website of the proposal. Especially check the metadata section.

3
  • Can I not build it inside the contract? Mar 28, 2022 at 11:14
  • Nope! Opensea tries to fetch data from your TokenURI. But your URI is not an URI. Because of that it can't show your metadata. Mar 28, 2022 at 12:12
  • After some more digging I have found that this is a perfectly fine way to approach the TokenURI, see my answer for the details. Apr 17, 2022 at 20:43
0

So as I thought this is a totally doable way of creating dynamic metadata, the change I needed was to simply add the correct brackets at the end of the token URI function. So the function changes to this. A tiny but frustrating change.

function tokenURI(uint256 _tokenId) public view override returns (string memory) {
        PropertyAttributes memory propAttributes = nftHolderAttributes[_tokenId];

        string memory json = Base64.encode(
            abi.encodePacked(
                '{"name": "',
                propAttributes.name,
                '", "description": "',propAttributes.description,'", "image": "',
                propAttributes.image,
                '", "attributes": [ { "trait_type": "Tower", "value": ',propAttributes.properties.tower,', "trait_type": "District", "value":',propAttributes.properties.district, ', "trait_type": "neighborhood", "value":',propAttributes.properties.neighborhood,'}]}'
            )
        );

        string memory output = string(
            abi.encodePacked("data:application/json;base64,", json)
        );
        
        return output;
    }

I also learned that all the variables need to be strings, so the royalty variable in the contractURI function was causing it to not work. And the set contract URI function needed to change to this.

function setContractURI(
        string memory name, string memory desc,
        string memory image,string memory link, 
        string memory royalty) public onlyOwner() {

        string memory x = Base64.encode(
            abi.encodePacked(
                '{"name": "',
                name,
                '", "description": "',
                desc,
                '", "image": "', 
                image,
                '", "external_link": "', 
                link,
                '","seller_fee_basis_points":"', royalty, // 100 Indicates a 1% seller fee
                '", "fee_recipient": "0xA97F337c39cccE66adfeCB2BF99C1DdC54C2D721" }' // Where seller fees will be paid to.}
            )
        );
        _contractURI = string(abi.encodePacked(
                "data:application/json;base64,", x
            ));
        console.log("contract uri updated");
    }
2
  • Is this method works on opensea? Btw, this is a very interesting approach. Congratulations! Apr 18, 2022 at 14:22
  • Yes it works for Opensea. Apr 19, 2022 at 21:11

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.