Create and Transfer an NFT using a Solidity Contract
Summary
Besides creating NFTs using Hedera SDK, you can use a Solidity Contract to create, mint, and transfer NFTs by calling contract functions directly. These are the contracts you will need to import into your working directory provided by Hedera that you can find in the contracts folder here:
HederaTokenService.sol
HederaResponseCodes.sol
IHederaTokenService.sol
ExpiryHelper.sol
FeeHelper.sol
KeyHelper.sol
Prerequisites
We recommend you complete the following introduction to get a basic understanding of Hedera transactions. This example does not build upon the previous examples.
Get a Hedera testnet account.
Set up your environment here.
If you are interested in creating, minting, and transferring NFTs using Hedera SDKs you can find the example here.
In this example, you will set gas for smart contract transactions multiple times. If you don't have enough gas you will receive anINSUFFICIENT_GAS
response. If you set the value too high you will be refunded a maximum of 20% of the amount that was set for the transaction.
1. Create an “NFT Creator” Smart Contract
You can find an NFTCreator Solidity contract sample below with the contract bytecode obtained by compiling the solidity contract using Remix IDE. If you are not familiar with Solidity, you can take a look at the docs here.
The following contract is composed of three functions:
createNft
mintNft
transferNft
The important thing to know is that the NFT created in this example will have the contract itself as Treasury Account, Supply Key, and Auto-renew account. There’s NO admin key for the NFT or the contract.
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.5.0 <0.9.0;
import "./HederaResponseCodes.sol";
import "./IHederaTokenService.sol";
import "./HederaTokenService.sol";
import "./ExpiryHelper.sol";
import "./KeyHelper.sol";
contract NFTCreator is ExpiryHelper, KeyHelper, HederaTokenService {
function createNft(
string memory name,
string memory symbol,
string memory memo,
int64 maxSupply,
int64 autoRenewPeriod
) external payable returns (address){
IHederaTokenService.TokenKey[] memory keys = new IHederaTokenService.TokenKey[](1);
// Set this contract as supply for the token
keys[0] = getSingleKey(KeyType.SUPPLY, KeyValueType.CONTRACT_ID, address(this));
IHederaTokenService.HederaToken memory token;
token.name = name;
token.symbol = symbol;
token.memo = memo;
token.treasury = address(this);
token.tokenSupplyType = true; // set supply to FINITE
token.maxSupply = maxSupply;
token.tokenKeys = keys;
token.freezeDefault = false;
token.expiry = createAutoRenewExpiry(address(this), autoRenewPeriod); // Contract auto-renews the token
(int responseCode, address createdToken) = HederaTokenService.createNonFungibleToken(token);
if(responseCode != HederaResponseCodes.SUCCESS){
revert("Failed to create non-fungible token");
}
return createdToken;
}
function mintNft(
address token,
bytes[] memory metadata
) external returns(int64){
(int response, , int64[] memory serial) = HederaTokenService.mintToken(token, 0, metadata);
if(response != HederaResponseCodes.SUCCESS){
revert("Failed to mint non-fungible token");
}
return serial[0];
}
function transferNft(
address token,
address receiver,
int64 serial
) external returns(int){
int response = HederaTokenService.transferNFT(token, address(this), receiver, serial);
if(response != HederaResponseCodes.SUCCESS){
revert("Failed to transfer non-fungible token");
}
return response;
}
}
Store your contract on Hedera using ContractCreateFlow()
. This single call performs FileCreateTransaction()
,FileAppendTransaction()
, and ContractCreateTransaction()
for you. See the difference here.
// Create contract
ContractCreateFlow createContract = new ContractCreateFlow()
.setBytecode(bytecode) // Contract bytecode
.setGas(4_000_000); // Increase if revert
TransactionResponse createContractTx = createContract.execute(client);
TransactionReceipt createContractRx = createContractTx.getReceipt(client);
// Get the new contract ID
ContractId newContractId = createContractRx.contractId;
System.out.println("Contract created with ID: " + newContractId);
3. Execute the Contract to Create an NFT
The parameters you need to specify for this contract call are Name, Symbol, Memo, Maximum Supply, and Expiration. The smart contract "rent" feature is currently NOT enabled. Once enabled in the future, setting an expiration date in seconds is required because entities on Hedera will need to pay "rent" to persist. In this case, the contract entity will pay all NFT auto-renewal fees.
// Create NFT using contract
ContractExecuteTransaction createToken = new ContractExecuteTransaction()
.setContractId(newContractId) // Contract id
.setGas(4_000_000) // Increase if revert
.setPayableAmount(new Hbar(50)) // Increase if revert
.setFunction("createNft", new ContractFunctionParameters()
.addString("Fall Collection") // NFT Name
.addString("LEAF") // NFT Symbol
.addString("Just a memo") // NFT Memo
.addInt64(250) // NFT max supply
.addInt64(7_000_000)); // Expiration: Needs to be between 6999999 and 8000001
TransactionResponse createTokenTx = createToken.execute(client);
TransactionRecord createTokenRx = createTokenTx.getRecord(client);
String tokenIdSolidityAddr = createTokenRx.contractFunctionResult.getAddress(0);
AccountId tokenId = AccountId.fromSolidityAddress(tokenIdSolidityAddr);
System.out.println("Token created with ID: " + tokenId);
4. Execute the Contract to Mint a New NFT
After the token ID is created, you mint each NFT under that ID using the mintNft
function. For the minting, you must specify the token ID as a Solidity address and the NFT metadata.
Both the NFT image and metadata live in the InterPlanetary File System (IPFS), which provides decentralized storage. The file metadata.json contains the metadata for the NFT. An IPFS URI pointing to the metadata file is used during minting of a new NFT. Notice that the metadata file contains a URI pointing to the NFT image.
// Mint NFT
ContractExecuteTransaction mintToken = new ContractExecuteTransaction()
.setContractId(newContractId)
.setGas(4_000_000)
.setMaxTransactionFee(new Hbar(20)) //Use when HBAR is <10 cents
.setFunction("mintNft", new ContractFunctionParameters()
.addAddress(tokenIdSolidityAddr) // Token address
.addBytesArray(byteArray)); // Metadata
TransactionResponse mintTokenTx = mintToken.execute(client);
TransactionRecord mintTokenRx = mintTokenTx.getRecord(client);
// NFT serial number
long serial = mintTokenRx.contractFunctionResult.getInt64(0);
System.out.println("Minted NFT with serial: " + serial);
{
"name": "LEAF1.jpg",
"creator": "Mother Nature",
"description": "Autumn",
"type": "image/jpg",
"format": "none",
"properties": {
"city": "Boston",
"season": "Fall",
"decade": "20's"
},
"image": "ipfs://bafybeig35bheyqpi4qlnuljpok54ud753bnp62fe6jit343hv3oxhgnbfm/LEAF1.jpg"
}
5. Execute the Contract to Transfer the NFT
The NFT is minted to the contract address because the contract is the treasury for the token. Now transfer the NFT to another account or contract address. In this example, you will transfer the NFT to Alice. For the transfer, you must specify the token address and NFT serial number.
The transferNft
function in the Solidity contract contains a call to an associateToken
function that will automatically associate Alice to the token ID. This association transaction must be signed using Alice's private key. After signing, Alice will receive the NFT.
// Transfer NFT to Alice
ContractExecuteTransaction transferToken = new ContractExecuteTransaction()
.setContractId(newContractId)
.setGas(4_000_000)
.setFunction("transferNft", new ContractFunctionParameters()
.addAddress(tokenIdSolidityAddr) // Token id
.addAddress(aliceId.toSolidityAddress()) // Token receiver (Alice)
.addInt64(serial)) // Serial number
.freezeWith(client) // Freeze transaction using client
.sign(aliceKey); //Sign using Alice Private Key
TransactionResponse transferTokenTx = transferToken.execute(client);
TransactionReceipt transferTokenRx = transferTokenTx.getReceipt(client);
System.out.println("Transfer status: " + transferTokenRx.status);
Code Check ✅
Last updated
Was this helpful?