Is there a way to retrieve JSON on Solidity on ERC-721?

Viewed 578

I'm practicing on an ERC_721 contract and would like to know if there's a way to store JSON data directly on the contract, take a look at my approach and give your thoughts:

The variation1.json file uploaded to IPFS:

{
    "attributes": [
      {
        "trait_type": "Rank",
        "value": "1"
      }
    ],
    "description": "A simple NFT",
    "image": "Qmdasifhw89rv92enfkq128re3",
    "name": "NFT#1"
}

Then I would use an object like the one below to go with the contract that references the file to the urlOfTokenURI

[
  {
    "file": "variation1.json", "urlOfTokenURI": https://gateway.pinata.cloud/ipfs/Qmdasifhw89rv92enfkqeqe23f"
  },
  {
    "file": "variation2.json", "urlOfTokenURI": "https://gateway.pinata.cloud/ipfs/Qmgvfrg34vt4u785ygbmasdsa"
  },
  ...
]

Then get a random number, retrieve a random urlOfTokenURI and mint the token to the desired address, excluding that token from the list of available tokens:

function mintNFT(address recipient, string memory tokenURI)
    public onlyOwner
    returns (uint256)
{
    _tokenIds.increment();
    //retrieve object, get random number and mint with the urlOfTokenURI
    //from random number index
    uint256 newItemId = _tokenIds.current();
    _mint(recipient, newItemId);
    _setTokenURI(newItemId, tokenURI);

    return newItemId;
}

What's missing here for this to work?

Can someone guide me how to proceed or have a suggestion for another approach? I'm kind of lost.

Don't understand how to mint a random NFT and instantly revealing it to the user.

Sorry in advance if it's a stupid question. I'm learning, btw.

Appreciate the help!

1 Answers
  1. If you need a real random number fn, you need to look for oracle service (e.g. Chainlink)
  2. Yes, it is possible to generate an uri without publishing to IPFS. You can generate a base64 data uri instead. Check out Example from wizard and dragon game and their contract
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/utils/Strings.sol";
...
    using Strings for uint256;
    using Base64 for bytes;
...
    function tokenURI(uint256 id) public view returns (string memory) {

        string memory metadata = string(abi.encodePacked(
            "{\"name\": \"NFT#",
            id.toString(),
            "\", \"description\": \"A simple NFT\", \"attributes\":",
            "[{\"key\":\"trait_type\",\"value\":\"1\"}]",
            "}"
        ));

        return string(abi.encodePacked(
            "data:application/json;base64,",
            bytes(metadata).base64()
        ));
    }

Base64 lib

pragma solidity ^0.8.0;

library Base64 {

    string internal constant TABLE = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

    function base64(bytes memory data) internal pure returns (string memory) {
        if (data.length == 0) return "";
        
        // load the table into memory
        string memory table = TABLE;

        // multiply by 4/3 rounded up
        uint256 encodedLen = 4 * ((data.length + 2) / 3);

        // add some extra buffer at the end required for the writing
        string memory result = new string(encodedLen + 32);

        assembly {
            // set the actual output length
            mstore(result, encodedLen)
            
            // prepare the lookup table
            let tablePtr := add(table, 1)
            
            // input ptr
            let dataPtr := data
            let endPtr := add(dataPtr, mload(data))
            
            // result ptr, jump over length
            let resultPtr := add(result, 32)
            
            // run over the input, 3 bytes at a time
            for {} lt(dataPtr, endPtr) {}
            {
                dataPtr := add(dataPtr, 3)
                
                // read 3 bytes
                let input := mload(dataPtr)
                
                // write 4 characters
                mstore(resultPtr, shl(248, mload(add(tablePtr, and(shr(18, input), 0x3F)))))
                resultPtr := add(resultPtr, 1)
                mstore(resultPtr, shl(248, mload(add(tablePtr, and(shr(12, input), 0x3F)))))
                resultPtr := add(resultPtr, 1)
                mstore(resultPtr, shl(248, mload(add(tablePtr, and(shr( 6, input), 0x3F)))))
                resultPtr := add(resultPtr, 1)
                mstore(resultPtr, shl(248, mload(add(tablePtr, and(        input,  0x3F)))))
                resultPtr := add(resultPtr, 1)
            }
            
            // padding with '='
            switch mod(mload(data), 3)
            case 1 { mstore(sub(resultPtr, 2), shl(240, 0x3d3d)) }
            case 2 { mstore(sub(resultPtr, 1), shl(248, 0x3d)) }
        }
        
        return result;
    }
}
Related