Create an NFT and deploy to a public testnet, using Remix

In this tutorial we will create a non-fungible token (NFT) and deploy to a public testnet.

ERC721 is a standard for representing ownership of non-fungible tokens, that is, where each token is unique such as in real estate or collectibles.

We will use Presets contracts in OpenZeppelin Contracts 3 to create an ERC721 and deploy using Remix.

Setting up the Environment

Open https://remix.ethereum.org/ in your favorite browser.

If you haven't used Remix before, you need to setup plugins so that you can use the Solidity Compiler and Deploy and Run Transactions.

Select the Solidity button under the Environments heading and this will add the required plugins.

The plugins are shown on the left hand column.

Importing the contract

Update (September 2021): Instead of using the Preset contract we can use OpenZeppelin Contracts Wizard to create the Solidity and open in Remix

We are going to use Preset ERC721PresetMinterPauserAutoId which is an ERC721 that is preset so it can be minted, paused and burned.

We need to import ERC721PresetMinterPauserAutoId into Remix.

In the File Explorer press the :plus: to Create New File
Call the new file OpenZeppelinPresetContracts.sol

In the new file add the import statement for ERC721PresetMinterPauserAutoId below.

We specify a minimum version of the Solidity compiler to use and import ERC721PresetMinterPauserAutoId from GitHub.

Note: When importing via GitHub, you should specify the release tag, otherwise you will get the latest code in the master branch. For OpenZeppelin Contracts you should only use code published in an official release. We will import OpenZeppelin Contracts v3.4.0. (see Importing from GitHub in the Remix documentation).

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

import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v3.4.0/contracts/presets/ERC721PresetMinterPauserAutoId.sol";

Compile the contract

Select the Solidity Compiler plugin.
Press the Compile button to compile the contract.
The contract and the contracts it inherits from will be loaded into Remix.
The contract will then be compiled.

Deploy the contract

We can deploy our new token to a development blockchain. Providing a name, a symbol and a base URI as parameters to the constructor to create a new ERC721PresetMinterPauserAutoId.

Select the Deploy & Run Transactions plugin.
Environment should default to JavaScript VM, our development blockchain.
Change the gas limit to 5000000
Change the Contract to ERC721PresetMinterPauserAutoId
Specify the name, symbol and base URI to use for our new token. I am using "My NFT" and "NFT" and "https://my-json-server.typicode.com/abcoathup/samplenft/tokens/"
Press Deploy.

image

Remix will deploy our token.

Interact with our Token

We can interact with our deployed token using Remix.
In the Deploy & Run Transactions plugin, under Deployed Contracts expand our deployed token (ERC721PRESETMINTERPAUSERAUTOID) to show the functions we can interact with. (for help see Deployed Contracts Remix documentation)

Token metadata

We can call the contract functions to read token metadata such as name, symbol and baseURI

Press the name, symbol and baseURI function buttons and this will call the functions in our deployed token.
Showing values:

  • baseURI: 0: string: https://my-json-server.typicode.com/abcoathup/samplenft/tokens/
  • name: 0: string: My Token
  • symbol: 0: string: MYT

Mint

We can send a transaction to mint tokens to a given account, from an account with the minter role.
In our case we are minting from the account which deployed the token, which is given the minter role.

We will mint 1 NFT with token ID 0 to our account:
0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2

We can set the parameters as follows
"0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2"
Press mint
The transaction will be shown in the console

We can check the owner of the token and the token URI for the metadata
Showing values:

  • ownerOf: 0: address: 0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2
  • tokenURI: 0: string: https://my-json-server.typicode.com/abcoathup/samplenft/tokens/0

image

MetaData

EIP-721 includes an optional metadata extension with a name, symbol and for each tokenID a tokenURI with can point to a JSON file with name, description and image for the given token ID.

How you create and host this metadata is up to you.
I would suggest using a domain that you control to point to where you host the data so that you can move it as required.

For this tutorial, we will use My JSON Server where we can store a single JSON file in a GitHub repository that we can access via a fake JSON server.

:warning: For production we need to store our metadata in a permanent location that can exist for the life of the token.

For images we will use twemoji Graphics from https://github.com/twitter/twemoji which are licensed under CC BY 4.0

A sample JSON for tokenID 0 is:
http://my-json-server.typicode.com/abcoathup/samplenft/tokens/0

Deploy to a public testnet

Next we will deploy to Rinkeby public testnet as OpenSea supports Rinkeby for testing.

You will need the following:

Deploy to Rinkeby

Set the network in MetaMask to Rinkeby.
Change the environment in Deploy and Run Transactions to Injected Web3 so that Remix uses web3 from MetaMask

Check the gas limit is set to 5000000
Check the Contract is set to ERC721PresetMinterPauserAutoId
Specify the name, symbol and base URI to use for our new token. I am using "My NFT" and "NFT" and "https://my-json-server.typicode.com/abcoathup/samplenft/tokens/"
Press Deploy.
Confirm the transaction in MetaMask
The console will show creation of ERC721PresetMinterPauserAutoId pending... and we can wait while the transaction is mined.

Mint

We can send a transaction to mint tokens to a given account, from an account with the minter role.

In our case we are minting from the account which deployed the token, which is given the minter role.

We will mint 1 NFT with token ID 0. Specify the address that you want to be the token holder (0x77737a65C296012C67F8c7f656d1Df81827c9541 is one of my test accounts)

Press mint with the recipient address
Confirm the transaction in MetaMask
The console will show transact to ERC721PresetMinterPauserAutoId.mint pending ... and we can wait while the transaction is mined.

OpenSea

We can validate our metadata

:notebook: To obtain the address of our contract on Rinkeby we can press the copy button next to our contract ERC721PRESETMINTERPAUSERAUTOID

My NFT is at: 0x60682c557e061B2f252B39B7763c5A6399a6fc1e

image

OpenSea metadata validation is at https://rinkeby-api.opensea.io/asset/[nft contract address]/[token id]/validate

My example can be found at:
https://rinkeby-api.opensea.io/asset/0x60682c557e061B2f252B39B7763c5A6399a6fc1e/0/validate/

Assuming that there aren't any issues with our metadata, we can view our NFT on OpenSea at https://rinkeby.opensea.io/assets/[nft contract address]/[token id]

My example can be found at:

Next steps

  • Create your artwork and metadata.
  • Host your images and metadata in a location that will last the life of the token.
  • Define what rights your token holders have.
  • Prepare for mainnet.
4 Likes

=) can’t wait for next episode :muscle: :musical_keyboard:

1 Like

A post was split to a new topic: Can an NFT show for example a music player inside Metamask’s mobile collectible view?

2 posts were split to a new topic: How to trade ERC721 created from tutorial on OpenSea?

3 posts were split to a new topic: How to update ERC721 metadata on OpenSea

3 posts were split to a new topic: Add tutorial on selling NFTs

Thanks for tutorial! I did mine with hardhat instead of remix and this was the result:

1 Like

Welcome to the community @esaul_parra :wave:

Thanks for sharing.

thanks for your post. Do you have another ERC721 sol sample that does NOT add the token ID to the baseURI?
My baseURI is a JSON file stored on IPFS, I cannot change the URI, so I’d like to use my URL, and not having the sol script adding an ID to that URL for each mint.

thanks,
Rod

Very instructive post thank you.
I need help to understand the part about…
“an optional metadata extension with a name, symbol and for each tokenID a tokenURI with can point to a JSON file with name, description and image for the given token ID”

How is the call to the external JSON file made in the token script?

I see that the token metadata has a name, symbol and base URI, but I don’t see the optional metadata reference inside the token.

I have the same question. Did you found anything?

Did not found an answer yet

Thanks! This all was working great for me up until opensea validation.

The steps are all straightforward and I definitely minted the token to rinkeby, I see the contract address, and the token id that was minted, but using them with the link provided for validation fails - the asset doesnt exist also:

https://rinkeby-api.opensea.io/asset/0xb4cdd3674540b698448608cd0472b4a3dce91d85/0/

Anyone know why that might be? Thanks!

Not sure what happened here, but after trying following these steps twice, it didnt work on attmempt 1 and 2, 3rd deployed contract worked without a hitch though… wierd…

Thanks for the tutorial! Very helpful.

Hi, welcome! :wave:

Maybe you should try this URL: https://rinkeby-api.opensea.io/asset/0xb4cdd3674540b698448608cd0472b4a3dce91d85/0/validate/

Hi @abcoathup, just a quick question: where should the NFT price be specified, ideally? Would it be in a separate smart contract?

i have used your code to create a contract and it worked perfect but now when i am creating new contract it appears on opensea but i can not edit the collection as it is not belong to me although all items inside it are appear in my profile

1 Like

first off all Thanks for the tutorial ,it help so much for noobs like me
so after i deploy how i verify the contract , i always get error when i try verify the contract . will appreciate if anyone can help
Thanks

You probably should create your own implementation of ERC721 token, similar to ERC721PresetMinterPauserAutoId but without id autogeneration. Remove counter and pass tokenId to mint() method something like:

    function mint(address to, uint256 tokenId) public virtual {
        require(hasRole(MINTER_ROLE, _msgSender()), "ERC721PresetMinterPauserAutoId: must have minter role to mint");

        _mint(to, tokenId);
    }

First of all, thanks so much for the tutorial, I followed the guide and listed a NFT on opensea testnet, but I encountered a problem, I couldn't put them in a collection under my account, it shows in my account page though, it dosn't show any of my collection and I could not drag them into any of my collection as well.

1 Like