Skip to main content
ESC

Deploy an NFT Contract

This tutorial shows how to create, deploy, and mint an ERC-721 NFT on Elastos Smart Chain (ESC). ESC is a geth fork with Chain ID 20 (mainnet) and 21 (testnet). Standard EVM NFT tooling (MetaMask, Hardhat, OpenZeppelin, and IPFS metadata) works the same as on other EVM chains.

ERC-721 on ESC

ESC supports all standard ERC-721 features: minting, transfers, approvals, enumeration (if you add extensions), and URI-based metadata. Wallets and explorers that understand Ethereum NFTs will behave the same on ESC.

Step 1: Prerequisites

  • Node.js 18+ (LTS recommended)
  • MetaMask (or another Web3 wallet) configured for ESC Testnet
Network Configuration

For MetaMask and wallet network setup, see Add ESC Network.

Fund your deployer account with testnet gas token from the official faucet: https://esc-faucet.elastos.io/

Testnet tokens

tELA on ESC Testnet has no real monetary value. It is only for development and testing. Never send mainnet assets or share mainnet keys when following this guide.

Step 2: Create a Hardhat Project

From a directory where you keep projects:

mkdir my-nft && cd my-nft
npm init -y
npm install --save-dev hardhat @nomicfoundation/hardhat-toolbox
npm install @openzeppelin/contracts
npx hardhat init

When prompted, choose Create a JavaScript project and accept the defaults (or adjust paths to match your setup). You should get contracts/, scripts/, and hardhat.config.js.

Step 3: Write the NFT Contract

Create contracts/MyNFT.sol:

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

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

contract MyNFT is ERC721URIStorage, Ownable {
uint256 private _nextTokenId;

constructor() ERC721("My NFT", "MNFT") Ownable(msg.sender) {}

function mintNFT(address recipient, string memory tokenURI)
public
onlyOwner
returns (uint256)
{
uint256 tokenId = _nextTokenId++;
_mint(recipient, tokenId);
_setTokenURI(tokenId, tokenURI);
return tokenId;
}
}

The contract uses ERC721URIStorage so each token can store a metadata URI (typically ipfs://...). Only the owner (the deployer) can call mintNFT.

Step 4: Configure Hardhat for ESC

Use the same ESC testnet configuration as the ERC-20 tutorial: Chain ID 21 and RPC https://api-testnet.elastos.io/esc.

Replace or merge hardhat.config.js:

require("@nomicfoundation/hardhat-toolbox");

module.exports = {
solidity: "0.8.20",
networks: {
escTestnet: {
url: "https://api-testnet.elastos.io/esc",
chainId: 21,
accounts: [process.env.PRIVATE_KEY],
},
escMainnet: {
url: "https://api.elastos.io/esc",
chainId: 20,
accounts: [process.env.PRIVATE_KEY],
},
},
};
Private keys

Never commit private keys, seed phrases, or .env files with secrets to Git. Add .env to .gitignore and use environment variables only on trusted machines.

Step 5: Deploy the Contract

Create scripts/deploy.js:

const { ethers } = require("hardhat");

async function main() {
const MyNFT = await ethers.getContractFactory("MyNFT");
const nft = await MyNFT.deploy();
await nft.waitForDeployment();
const address = await nft.getAddress();
console.log(`MyNFT deployed to: ${address}`);
}

main().catch((error) => {
console.error(error);
process.exitCode = 1;
});

Compile and deploy to ESC Testnet:

npx hardhat compile
PRIVATE_KEY=your_private_key npx hardhat run scripts/deploy.js --network escTestnet

Replace your_private_key with the 0x-prefixed key for the same MetaMask account you funded with tELA. Save the printed contract address for minting and explorer lookup.

Step 6: Prepare NFT Metadata

Marketplaces and wallets expect an ERC-721 metadata JSON document. At minimum, include name, description, and image (often an IPFS CID). Optional attributes follow the OpenSea-style convention:

{
"name": "My First Elastos NFT",
"description": "An NFT deployed on Elastos Smart Chain",
"image": "ipfs://QmExample...",
"attributes": [
{ "trait_type": "Chain", "value": "Elastos ESC" },
{ "trait_type": "Standard", "value": "ERC-721" }
]
}

Workflow:

  1. Upload your image file to IPFS and note its CID (e.g. Qm...).
  2. Put that CID in image as ipfs://<CID>.
  3. Upload the JSON file itself to IPFS and note its CID. That JSON URI is what you pass to mintNFT as tokenURI (e.g. ipfs://QmMetadata...).

Popular pinning services:

  • Pinata: Web UI and API to pin files and folders to IPFS.
  • Pinata: Popular IPFS pinning service with a free tier for NFT metadata and assets.
Pinata and IPFS

Pinata is a common choice for pinning images and metadata so they stay available on IPFS. After upload, copy the IPFS URI (ipfs://...) and use it as tokenURI when minting. Without pinning, CIDs may become unreachable if nodes stop serving your content.

Step 7: Mint an NFT

Create scripts/mint.js to call mintNFT with your metadata URI:

const hre = require("hardhat");

async function main() {
const contractAddress = process.env.NFT_CONTRACT;
const tokenURI = process.env.TOKEN_URI;

if (!contractAddress || !tokenURI) {
throw new Error("Set NFT_CONTRACT and TOKEN_URI (e.g. ipfs://Qm...)");
}

const [signer] = await hre.ethers.getSigners();
const recipient = process.env.RECIPIENT || signer.address;

const nft = await hre.ethers.getContractAt("MyNFT", contractAddress);
const tx = await nft.mintNFT(recipient, tokenURI);
const receipt = await tx.wait();

console.log("Mint tx:", receipt.hash);
console.log("Recipient:", recipient);
}

main().catch((error) => {
console.error(error);
process.exitCode = 1;
});

Run on ESC Testnet (use the address from Step 5 and your IPFS metadata URI):

PRIVATE_KEY=your_private_key \
NFT_CONTRACT=0xYourDeployedContract \
TOKEN_URI=ipfs://QmYourMetadataHash \
npx hardhat run scripts/mint.js --network escTestnet

Optional: set RECIPIENT=0x... to mint to another address; otherwise the deployer receives the token.

Step 8: View on Blockscout

  1. Open the ESC Testnet explorer: https://esc-testnet.elastos.io/
  2. Search for your contract address.
  3. Review transactions, token transfers, and contract interactions. Verified contracts show readable source; NFT metadata may appear in the explorer or linked wallet depending on how the indexer resolves tokenURI.

You can also look up the mint transaction by hash to confirm success and gas usage.


Summary: You configured ESC Testnet (Chain ID 21), deployed MyNFT with OpenZeppelin ERC721URIStorage, pinned metadata to IPFS, minted with mintNFT, and verified activity on Blockscout.