Remix IDE: Creating Interactive NFTs with IPFS and Remix IDE

Learn how to create an interactive NFT with IPFS and Remix IDE.

What is Remix IDE?

Remix Online IDE is a web browser-based Ethereum smart contract development and deployment tool.

Read below to learn how to create an interactive NFT with IPFS and Remix IDE.

Prerequisites:

1. Start by navigating to the Remix Online IDE.

Next to ‘Workspaces’, select the plus sign.

2. Choose the ‘Blank’ template, then give your workspace a name.

3. Then select ‘New File’ to create a new smart contract in your workspace.

4. Name the new file contract.sol and insert the following content:

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

import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/utils/Counters.sol";

contract MyNFT0 is ERC721, ERC721URIStorage, Ownable {
  using Counters for Counters.Counter;
  Counters.Counter private _tokenIdCounter;

  constructor() ERC721("MyNFT0", "MNFT0") {}

  function _baseURI() internal pure override returns (string memory) {
    return "https://ipfs.filebase.io/ipfs/";
  }

  function supportsInterface(bytes4 interfaceId)
    public view virtual override(ERC721) 
    returns (bool) {
    return super.supportsInterface(interfaceId);
  }

  function mintNFT(address to, string memory uri) public onlyOwner {
    _tokenIdCounter.increment();
    uint256 tokenId = _tokenIdCounter.current();
    _safeMint(to, tokenId);
    _setTokenURI(tokenId, uri);
  }

  // The following functions are overrides required by Solidity.

  function _burn(uint256 tokenId) internal override(ERC721, ERC721URIStorage) {
    super._burn(tokenId);
  }

  function tokenURI(uint256 tokenId)
    public
    view
    override(ERC721, ERC721URIStorage)
    returns (string memory)
  {
    return super.tokenURI(tokenId);
  }
}

5. Then, on your local computer create a new file called flip.js with the following content:

function flip () {
return true || false;
}

7. Select an IPFS bucket.

Select ‘Upload’, then select your flip.js file and an image file you’d like to use for your NFT.

8. Take note of the IPFS CID for each of these files.

9. On your local computer, create a new file called metadata.json.

In this file, insert the following content:

{
  "title": "Interactive NFT",
  "type": "object",
  "properties": {
    "name": {
      "type": "string",
      "description": "Interactive NFT 1"
    },
    "description": {
      "type": "string",
      "description": "This is an interactive NFT."
    },
    "image": {
      "type": "string",
      "description": "IMAGE_IPFS_CID"
    },
    "js": {
      "type": "string",
      "description": "JS_IPFS_CID"
    }
  }
}

Replace the following values:

  • IMAGE_IPFS_CID: The IPFS CID for your NFT’s image.

  • JS_IPFS_CID: The IPFS CID for your flip.js file.

10. Then, upload this metadata.json file to your Filebase bucket.

Take note of the IPFS CID that is returned.

11. Back in the Remix Online IDE, select ‘Compile’ to compile the smart contract code.

This will create a variety of files in the workspace’s file explorer.

12. Then, select ‘Deploy and Run Transactions’ from the left sidebar menu.

13. Confirm that the following settings are correct, then select ‘Deploy’.

14. The contract’s address will then be listed under ‘Deployed Contracts’.

15. Expand the contract’s information.

Expand the ‘approve’ tab, then insert your cryptowallet address in the ‘to’ field and insert the IPFS CID of your metadata.json file in the ‘tokenId’ field. Then select ‘transact’.

16. Use the ‘Copy’ icon to copy the contract’s address. Save this value to be used later.

17. Next, on your local machine in the directory that contains your flip.jsand metadata.json files, create a new file called index.html. Insert the following content into this file:

<!doctype html>
<html lang="en">

<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Interactive NFT</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css" rel="stylesheet"
        integrity="sha384-rbsA2VBKQhggwzxH7pPCaAqO46MgnOM80zW1RWuH61DGLwZJEdK2Kadq2F9CUG65" crossorigin="anonymous">
</head>

<body>
    <div class="row">
        <div class="col-md-2"></div>
        <div class="col-md-8">
            <center>
                <div class="card" style="width: 18rem;">
                    <img id="imgSrc" src="..." class="card-img-top" alt="...">
                    <div class="card-body">
                        <h5 class="card-title">Interactive NFT</h5>
                        <p class="card-text">This is an interactive NFT.</p>
                        <button id="nameBtn" class="btn btn-primary">What is your name?</a>
                            <button id="storyBtn" class="btn btn-primary">What is your story?</a>
                    </div>
                </div>
            </center>
        </div>
        <div class="col-md-2"></div>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/js/bootstrap.bundle.min.js"
        integrity="sha384-kenU1KFdBIe4zVF0s0G1M5b4hcpxyD9F7jL+jjXkk+Q2h455rYXK/7HAuoJl+0I4"
        crossorigin="anonymous"></script>
    <script src="https://cdn.jsdelivr.net/npm/web3@latest/dist/web3.min.js"></script>

    <script type="text/javascript">
        if (typeof window.ethereum !== 'undefined') {
            console.log('MetaMask is installed!');
        } else {
            alert('Please install Metamask first.');
        }

        const web3 = new Web3(Web3.givenProvider);

        let account = '';

        async function connectMetamask() {
            ethereum.request({ method: 'eth_requestAccounts' });
            const accounts = await ethereum.request({ method: 'eth_requestAccounts' });
            account = accounts[0];
        }

        connectMetamask();

        const MyNFT0ABI = [
            {
                "inputs": [],
                "stateMutability": "nonpayable",
                "type": "constructor"
            },
            {
                "anonymous": false,
                "inputs": [
                    {
                        "indexed": true,
                        "internalType": "address",
                        "name": "owner",
                        "type": "address"
                    },
                    {
                        "indexed": true,
                        "internalType": "address",
                        "name": "approved",
                        "type": "address"
                    },
                    {
                        "indexed": true,
                        "internalType": "uint256",
                        "name": "tokenId",
                        "type": "uint256"
                    }
                ],
                "name": "Approval",
                "type": "event"
            },
            {
                "anonymous": false,
                "inputs": [
                    {
                        "indexed": true,
                        "internalType": "address",
                        "name": "owner",
                        "type": "address"
                    },
                    {
                        "indexed": true,
                        "internalType": "address",
                        "name": "operator",
                        "type": "address"
                    },
                    {
                        "indexed": false,
                        "internalType": "bool",
                        "name": "approved",
                        "type": "bool"
                    }
                ],
                "name": "ApprovalForAll",
                "type": "event"
            },
            {
                "inputs": [
                    {
                        "internalType": "address",
                        "name": "to",
                        "type": "address"
                    },
                    {
                        "internalType": "uint256",
                        "name": "tokenId",
                        "type": "uint256"
                    }
                ],
                "name": "approve",
                "outputs": [],
                "stateMutability": "nonpayable",
                "type": "function"
            },
            {
                "inputs": [
                    {
                        "internalType": "address",
                        "name": "to",
                        "type": "address"
                    },
                    {
                        "internalType": "string",
                        "name": "uri",
                        "type": "string"
                    }
                ],
                "name": "mintNFT",
                "outputs": [],
                "stateMutability": "nonpayable",
                "type": "function"
            },
            {
                "anonymous": false,
                "inputs": [
                    {
                        "indexed": true,
                        "internalType": "address",
                        "name": "previousOwner",
                        "type": "address"
                    },
                    {
                        "indexed": true,
                        "internalType": "address",
                        "name": "newOwner",
                        "type": "address"
                    }
                ],
                "name": "OwnershipTransferred",
                "type": "event"
            },
            {
                "inputs": [],
                "name": "renounceOwnership",
                "outputs": [],
                "stateMutability": "nonpayable",
                "type": "function"
            },
            {
                "inputs": [
                    {
                        "internalType": "address",
                        "name": "from",
                        "type": "address"
                    },
                    {
                        "internalType": "address",
                        "name": "to",
                        "type": "address"
                    },
                    {
                        "internalType": "uint256",
                        "name": "tokenId",
                        "type": "uint256"
                    }
                ],
                "name": "safeTransferFrom",
                "outputs": [],
                "stateMutability": "nonpayable",
                "type": "function"
            },
            {
                "inputs": [
                    {
                        "internalType": "address",
                        "name": "from",
                        "type": "address"
                    },
                    {
                        "internalType": "address",
                        "name": "to",
                        "type": "address"
                    },
                    {
                        "internalType": "uint256",
                        "name": "tokenId",
                        "type": "uint256"
                    },
                    {
                        "internalType": "bytes",
                        "name": "data",
                        "type": "bytes"
                    }
                ],
                "name": "safeTransferFrom",
                "outputs": [],
                "stateMutability": "nonpayable",
                "type": "function"
            },
            {
                "inputs": [
                    {
                        "internalType": "address",
                        "name": "operator",
                        "type": "address"
                    },
                    {
                        "internalType": "bool",
                        "name": "approved",
                        "type": "bool"
                    }
                ],
                "name": "setApprovalForAll",
                "outputs": [],
                "stateMutability": "nonpayable",
                "type": "function"
            },
            {
                "anonymous": false,
                "inputs": [
                    {
                        "indexed": true,
                        "internalType": "address",
                        "name": "from",
                        "type": "address"
                    },
                    {
                        "indexed": true,
                        "internalType": "address",
                        "name": "to",
                        "type": "address"
                    },
                    {
                        "indexed": true,
                        "internalType": "uint256",
                        "name": "tokenId",
                        "type": "uint256"
                    }
                ],
                "name": "Transfer",
                "type": "event"
            },
            {
                "inputs": [
                    {
                        "internalType": "address",
                        "name": "from",
                        "type": "address"
                    },
                    {
                        "internalType": "address",
                        "name": "to",
                        "type": "address"
                    },
                    {
                        "internalType": "uint256",
                        "name": "tokenId",
                        "type": "uint256"
                    }
                ],
                "name": "transferFrom",
                "outputs": [],
                "stateMutability": "nonpayable",
                "type": "function"
            },
            {
                "inputs": [
                    {
                        "internalType": "address",
                        "name": "newOwner",
                        "type": "address"
                    }
                ],
                "name": "transferOwnership",
                "outputs": [],
                "stateMutability": "nonpayable",
                "type": "function"
            },
            {
                "inputs": [
                    {
                        "internalType": "address",
                        "name": "owner",
                        "type": "address"
                    }
                ],
                "name": "balanceOf",
                "outputs": [
                    {
                        "internalType": "uint256",
                        "name": "",
                        "type": "uint256"
                    }
                ],
                "stateMutability": "view",
                "type": "function"
            },
            {
                "inputs": [
                    {
                        "internalType": "uint256",
                        "name": "tokenId",
                        "type": "uint256"
                    }
                ],
                "name": "getApproved",
                "outputs": [
                    {
                        "internalType": "address",
                        "name": "",
                        "type": "address"
                    }
                ],
                "stateMutability": "view",
                "type": "function"
            },
            {
                "inputs": [
                    {
                        "internalType": "address",
                        "name": "owner",
                        "type": "address"
                    },
                    {
                        "internalType": "address",
                        "name": "operator",
                        "type": "address"
                    }
                ],
                "name": "isApprovedForAll",
                "outputs": [
                    {
                        "internalType": "bool",
                        "name": "",
                        "type": "bool"
                    }
                ],
                "stateMutability": "view",
                "type": "function"
            },
            {
                "inputs": [],
                "name": "name",
                "outputs": [
                    {
                        "internalType": "string",
                        "name": "",
                        "type": "string"
                    }
                ],
                "stateMutability": "view",
                "type": "function"
            },
            {
                "inputs": [],
                "name": "owner",
                "outputs": [
                    {
                        "internalType": "address",
                        "name": "",
                        "type": "address"
                    }
                ],
                "stateMutability": "view",
                "type": "function"
            },
            {
                "inputs": [
                    {
                        "internalType": "uint256",
                        "name": "tokenId",
                        "type": "uint256"
                    }
                ],
                "name": "ownerOf",
                "outputs": [
                    {
                        "internalType": "address",
                        "name": "",
                        "type": "address"
                    }
                ],
                "stateMutability": "view",
                "type": "function"
            },
            {
                "inputs": [
                    {
                        "internalType": "bytes4",
                        "name": "interfaceId",
                        "type": "bytes4"
                    }
                ],
                "name": "supportsInterface",
                "outputs": [
                    {
                        "internalType": "bool",
                        "name": "",
                        "type": "bool"
                    }
                ],
                "stateMutability": "view",
                "type": "function"
            },
            {
                "inputs": [],
                "name": "symbol",
                "outputs": [
                    {
                        "internalType": "string",
                        "name": "",
                        "type": "string"
                    }
                ],
                "stateMutability": "view",
                "type": "function"
            },
            {
                "inputs": [
                    {
                        "internalType": "uint256",
                        "name": "tokenId",
                        "type": "uint256"
                    }
                ],
                "name": "tokenURI",
                "outputs": [
                    {
                        "internalType": "string",
                        "name": "",
                        "type": "string"
                    }
                ],
                "stateMutability": "view",
                "type": "function"
            }
        ]

        const MyNFT0ContractAddress = "CONTRACT_ADDRESS";

        const MyNFT0Contract = new web3.eth.Contract(MyNFT0ABI, MyNFT0ContractAddress);

        async function getResult(){
            let result = await MyNFT0Contract.methods.tokenURI(2).call();
            console.log(result);

            fetch(result)
            .then(response => response.json())
            .then(data => {
                console.log(data);
                console.log(data.properties.image.description);
                console.log(data.properties.js.description);
                let imageHash = data.properties.image.description;
                let jsCodeHash = data.properties.js.description;

                let baseURL = 'https://ipfs.filebase.io/ipfs/';

                fetch('http://127.0.0.1:8000/flip.js')
                    .then(response => response.text())
                    .then(data => {
                        eval(data);
                        console.log(flip());
                    })
            })
        }

        getResult();

    </script>
</body>

</html>

Replace the CONTRACT_ADDRESS value with your smart contract’s address you copied previously.

18. In your local directory, run the following command to launch your interactive NFT:

python3 -m http.server

19. Then, navigate to https://localhost:8000 and your interactive NFT should look like this:

If you have any questions, please join our Discord server, or send us an email at hello@filebase.com

Last updated