HTS x EVM - Part 1: How to Mint NFTs
Last updated
Was this helpful?
Last updated
Was this helpful?
In , we showed how to create, mint, burn, and transfer non-fungible tokens (NFTs) on Hedera without deploying any smart contracts—using only the Hedera Token Service (HTS) and official SDKs.
It's possible to create a token on Hedera using a smart contract and still benefit from the native Hedera Token Service. However, the contract needs to interact with the , which provides Hedera-specific token operations. By combining HTS and Solidity, you:
Get all the performance, cost-efficiency, and security of native HTS tokens.
Can embed custom, decentralized logic in your contract for advanced use cases.
In this tutorial, you’ll:
Create an NFT collection with a royalty fee schedule.
Mint new NFTs with metadata pointing to IPFS.
Transfer NFTs.
Burn an existing NFT.
ECDSA account from the .
Basic understanding of Solidity.
Clone the repository
Install dependencies
Create .env
file set up environment variables
Edit the .env
file to include your Hedera Testnet account's private key. Use your ECDSA Hex Encoded Private Key when interacting with Hedera's EVM via the JSON-RPC relay.
Run the test script
This script deploys and tests all of the functionality inside of the MintNFT
Smart Contract. We'll deep dive into the Smart Contract's functions and corresponding tests below!
createNFT(string memory name, string memory symbol, string memory memo)
Purpose: Deploy a Hedera NFT using Solidity. This involves specifying key token details (name, symbol, and memo), as well as setting up any custom fees (such as royalty fees).
Here, we pass "Test NFT"
, "TST"
, and "Test NFT"
as arguments. We also send some HBAR (in this case, 7 HBAR) to cover the creation costs on the Hedera network. We expect the NFTCreated
event to have a valid token address to confirm success.
Function: mintNFT(bytes[] memory metadata)
Purpose: Add new tokens to the existing NFT collection. Each minted token includes a unique piece of metadata (for instance, an IPFS URI).
Key Code Snippet:
We pass an array with one IPFS URI (encoded as bytes
). The test expects an NFTMinted
event on success, indicating new tokens are now part of our NFT supply.
transferNFT(address receiver, uint256 serialNumber)
Purpose: Transfer an existing NFT from the contract’s treasury (in this case, address(this)
) to another address. This leverages the standard ERC-721 transfer pattern.
Here, we transfer serial #1 from the contract to the owner
address. On success, we expect the NFTTransferred
event. We can also verify ownership using the standard IERC721(tokenAddress).ownerOf(serialNumber)
call.
burnNFT(int64[] memory serialNumbers)
Purpose: Burn (permanently remove) existing NFTs from circulation. This is helpful if you need to remove misprints, decommission tokens, or reduce supply to increase rarity. The serials must be in the Treasury account in order to burn them.
We pass an array of serial numbers—in this case, [1n]
for the first minted NFT. The test ensures that NFTBurned
fires, confirming the token is removed from the supply.
Using Solidity on Hedera, you can create, mint, burn, and transfer native NFTs with minimal code thanks to the HTS System Contract. In this tutorial, you saw how to:
Create a new NFT class with royalty fees (createNFT
).
Mint new tokens (mintNFT
).
Burn tokens when they are no longer needed (burnNFT
).
Transfer NFTs via standard ERC-721 calls (transferNFT
).
Check out our GitHub repo to find the full contract and Hardhat test scripts, along with the configuration files you need to deploy and test on Hedera!
Make sure to use an ECDSA account with for the test script to run successfully.
Create the NFT:
Calling createNonFungibleTokenWithCustomFees(...)
triggers the The call returns a response code and the token address (unique ID for the NFT class).
For most HTS calls, an HBAR value is not required to be sent in the contract call; the gas fee will cover it. However, for expensive transactions, like , the gas fee is reduced, and the transaction cost is covered by the payable amount. This is to reduce the gas consumed by the contract call.
As you can see, despite our NFT not actually being an ERC-721 (it is a Native Token; not a Smart Contract), we can treat it as though it is one. The same goes for Native Fungible Tokens and ERC-20 interfaces. Find out more .
Continue exploring our to see how advanced compliance flags (e.g., KYC) or updating tokens can be handled natively.
Writer: Jake, Developer Relations Engineer
Editor: Michiel, Developer Relations Engineer
Editor: Krystal, Technical Writer
|
|
|