- Intro
- Alchemy's Web3 Library
- Contract ABI
- Pinata
- Minting Script
- Call the mintNFT function
- View your NFT in MetaMask and on OpenSea
- What did we miss?
- Further Reading - the very short list
- Exercises
- Sample Code
In NFTs Part I we wrote a smart contract to mint an ERC721 (NFT) token for our Course Credits app. We used Hardhat to create our app and Alchemy to communicate with Ethereum and MetaMask to sign transactions. Finally we compiled our contract with Hardhat and deployed to Ethereum's testnet Goerli chain.
Here in Part II we will interact with the smart contract and mint our NFT.
- Same from last time, plus
- Get yourself a Pinata account; this is for decentralised storage
Install Alchemy's Web3 library
In your home directory type npm install @alch/alchemy-web3
; a number of packages will be added to your environment
Go to your \scripts
directory and create a new file called mint-nft.js
require("dotenv").config()
const API_URL = process.env.API_URL
const { createAlchemyWeb3 } = require("@alch/alchemy-web3")
const web3 = createAlchemyWeb3(API_URL)
Note here that you have already installed the dotenv
package from last time. The Alchemy package was downloaded in the previous step.
ABI is the application binary interface which is the result after compiling our contract code. It interfaces the human-readable program language with the EVM executable bytecode. (EVM is the Ethereum Virtual Machine which does all the computation.)
To your mint-nft.js
script add the following:
const contract = require("../artifacts/contracts/courseNFT.sol/courseNFT.json")
console.log(JSON.stringify(contract.abi))
and run the script: node scripts/mint-nft.js
. The last line we added console.log
should write the ABI to the console. You can see the json
in your /contracts
folder.
NFTs need metadata to differentiate each one (the non-fungible bit). In our example of the Course Credits App, this will be fields like Name, ID, Course, Grade, Date, etc. For a PFP it might be character traits like colour, accessory, logo, shirt, background, and so on.
Login to Pinata and click the Upload+
button selecting a file. Any file will do. Satoshi's certificate is here if you want to use the same one. Upload your file and after the dashboard refreshes you'll see it along with a content identifier (CID).
You can view your file here: https://gateway.pinata.cloud/ipfs/<CID>
Back in your root directory create a new file: nft-metadata.json
that contains:
{
"attributes": [
{
"trait_type": "Course",
"value": "Cryptocurrency"
},
{
"trait_type": "Name",
"value": "Satoshi Nakamoto"
}
],
"description": "Course Credits Certificate in Cryptocurrency",
"image": "ipfs://QmVxFXNuVJdk5QFzvkpSSeXypdDsCf8LET1kDnkZQBFyvj",
"name": "Course Credit Certificate"
}
Change the IPFS link CID image link to yours. You can edit the other characteristics too (or add more). Save the .json
file and upload it to Pinata. Upload the nft-metadata.json
file to Pinata.
Check that its been uploaded to the IPFS via Pinata. Copy your CID hash from pinata and create a URL:
https://gateway.pinata.cloud/ipfs/QmXd6V3ASnzZpToshcwLZkLRxHxduZKeKSXj4zhHWQUcjB
Get your contract address from Part I, mine is 0xdda15afec918308e8c20f70fb5090ca134063598
.
Add the following to the mint-nft.js
file:
const contractAddress = "0xdda15afec918308e8c20f70fb5090ca134063598"
const nftContract = new web3.eth.Contract(contract.abi, contractAddress)
Add your public key to your .env
file. It should now look like:
API_URL="https://eth-goerli.g.alchemy.com/v2/EUd...crz"
PRIVATE_KEY="97a...b3d"
PUBLIC_KEY="0x99A95d4d7DDe6B9E663509a41CF3A9eeAfC07Ad9"
Update the mint-nft.js
file to include your public and private keys and setup the transaction:
require("dotenv").config()
const API_URL = process.env.API_URL
const PUBLIC_KEY = process.env.PUBLIC_KEY
const PRIVATE_KEY = process.env.PRIVATE_KEY
const { createAlchemyWeb3 } = require("@alch/alchemy-web3")
const web3 = createAlchemyWeb3(API_URL)
const contract = require("../artifacts/contracts/courseNFT.sol/courseNFT.json")
const contractAddress = "0xdda15afec918308e8c20f70fb5090ca134063598"
const nftContract = new web3.eth.Contract(contract.abi, contractAddress)
async function mintNFT(tokenURI) {
const nonce = await web3.eth.getTransactionCount(PUBLIC_KEY, 'latest'); //get latest nonce
//the transaction
const tx = {
'from': PUBLIC_KEY,
'to': contractAddress,
'nonce': nonce,
'gas': 500000,
'data': nftContract.methods.mintNFT(PUBLIC_KEY, tokenURI).encodeABI()
};
}
Don't trust, verify! When we send the transaction we need to sign it. For this we need access to our PRIVATE_KEY
variable; remember this is only stored locally in the .env
file. Add this code to mint-nft.js
. Most of the code below is for error logging.
const signPromise = web3.eth.accounts.signTransaction(tx, PRIVATE_KEY)
signPromise
.then((signedTx) => {
web3.eth.sendSignedTransaction(
signedTx.rawTransaction,
function (err, hash) {
if (!err) {
console.log(
"The hash of your transaction is: ",
hash,
"\nCheck Alchemy's Mempool to view the status of your transaction!"
)
} else {
console.log(
"Something went wrong when submitting your transaction:",
err
)
}
}
)
})
.catch((err) => {
console.log(" Promise failed:", err)
})
Lastly call mintNFT
by adding the following:
mintNFT("ipfs://QmXd6V3ASnzZpToshcwLZkLRxHxduZKeKSXj4zhHWQUcjB")
The complete mint-nft.js
file is here. Now run the script from the command line:
> node scripts/mint-nft.js
Success looks like
C:\my-nft-app>node scripts/mint-nft.js
The hash of your transaction is: 0x4877840a2e1e21f0cbeacc64801d629e816da93a33f02713697047c027b66cc2
Check Alchemy's Mempool to view the status of your transaction!
Go to your Alchemy dashboard to see the recent activty. You can also see it on goerli.etherscan.io.
You may get an error if you used a different account to call the contract. Searching the txid on goerli.etherscan.io this can look like:
We're told that Fail with error 'Ownable: caller is not the owner'
which is a permissions issue.
To view your freshly minted certificate go back to MetaMask and select collectibles. You need to tell MetaMask what contract to look in, and the tokenID. So put in the contract address 0x2bb13D3d4F60592611bc4910394aDFC1B4A9EF7C
and the tokenID ccNFT
and the decimals 0
(no fractional NFTs here!). I've minted two so my balance is 2.
Note Presently you can see the NFTs in your MetaMask wallet only as tokens. There is not yet native support for viewing custom NFTs (as images). See more: https://metamask.zendesk.com/hc/en-us/articles/360058238591-NFT-tokens-in-your-MetaMask-wallet
The NFT marketplace OpenSea has support to view custom tokens minted on testnets by constructing a url:
https://testnets.opensea.io/assets/<asset_contract_address>/<token_id>
Paste in the contract address and tokenID to see OpenSea's marketplace description of you NFT. The URL should automatically resolve to goerli.
- In the next tutorial we are looking al scaling via layer two solutions -- i.e. the Lightning network
- This has been based on this Tutorial from Ethereum.org
- OpenSea ERC721 Tutorial
- https://nftschool.dev/ looks like a promising resource
- Change the metadata and mint another NFT.
- Investiage how to actually view your NFT certificate that is stored on IPFS via Pinata.
- Write a script to generate a new random NFT every time
mint
is called.
Files be found here. Note your addresses will be different!