TRC-721 contract interaction
Read and write to a deployed TRC-721 contract using TronWeb — query name, symbol, balances, transfer NFTs, and manage approvals.
Prerequisites
Once a TRC-721 contract is deployed, you can read from it and write to it using TronWeb. This page walks through the common operations with runnable JavaScript examples on the Shasta testnet.
Shared setup
Every example on this page uses the same TronWeb setup. Define it once and reuse:
const { TronWeb } = require('tronweb');
const privateKey = process.env.PRIVATE_KEY;
if (!privateKey) {
throw new Error('Set the PRIVATE_KEY environment variable');
}
const tronWeb = new TronWeb({
fullHost: 'https://api.shasta.trongrid.io',
// headers: { 'TRON-PRO-API-KEY': 'YOUR_API_KEY' }, // only required for Mainnet (api.trongrid.io); Shasta and Nile testnets do not require an API key
privateKey
});
function requireAddress(name) {
const address = process.env[name];
if (!address || !TronWeb.isAddress(address)) {
throw new Error(`Set ${name} to a valid TRON Base58Check address`);
}
return address;
}
const trc721ContractAddress = requireAddress('TRC721_CONTRACT_ADDRESS');
const ownerAddress = tronWeb.defaultAddress.base58;Before running an example, set PRIVATE_KEY and TRC721_CONTRACT_ADDRESS. Transfers also require RECIPIENT_ADDRESS, while approval examples require OPERATOR_ADDRESS.
Never commit a real private keyStore keys in environment variables (
process.env.PRIVATE_KEY) or a secrets manager. See Security best practices.
feeLimit unitThe
feeLimitparameter used in.send()calls below is insun(1 TRX = 1,000,000 sun). The examples use100_000_000, which is 100 TRX.
1. Query the token name
Call name() to get the human-readable collection name. This is a view function — it costs no Energy.
async function trc721Name() {
try {
const contract = await tronWeb.contract().at(trc721ContractAddress);
// call() runs view/pure methods without broadcasting a transaction.
const name = await contract.name().call();
console.log('name:', name);
} catch (error) {
console.error('TRC-721 query error', error);
}
}
trc721Name();Example output:
name: TRC721TEST2. Query the token symbol
Call symbol() to get the short symbol.
async function trc721Symbol() {
try {
const contract = await tronWeb.contract().at(trc721ContractAddress);
const symbol = await contract.symbol().call();
console.log('symbol:', symbol);
} catch (error) {
console.error('TRC-721 query error', error);
}
}
trc721Symbol();Example output:
symbol: TEST3. Query the number of NFTs an address owns
Call balanceOf(address) to get the total NFTs held by an account in this collection.
async function trc721BalanceOf() {
try {
const contract = await tronWeb.contract().at(trc721ContractAddress);
const result = await contract.balanceOf(ownerAddress).call();
console.log('balance:', tronWeb.toDecimal(result));
} catch (error) {
console.error('TRC-721 query error', error);
}
}
trc721BalanceOf();Example output:
balance: 14. Transfer an NFT
Call transferFrom(from, to, tokenId) to transfer a specific NFT.
async function trc721TransferFrom() {
try {
const contract = await tronWeb.contract().at(trc721ContractAddress);
// send() executes state-changing methods and broadcasts the transaction.
const txHash = await contract
.transferFrom(
ownerAddress, // from
requireAddress('RECIPIENT_ADDRESS'), // to
666 // tokenId
)
.send({ feeLimit: 100_000_000 });
console.log('transferFrom tx hash:', txHash);
} catch (error) {
console.error('TRC-721 transaction error', error);
}
}
trc721TransferFrom();Example output:
transferFrom tx hash: 9f4d10713cb0406adb7c729013b941d35597afeeba56faf2a1bc7647fc0a92bdVerify the transaction on Shasta TronScan.
transferFromvssafeTransferFrom
transferFromdoes not check whether the recipient is a contract or whether that contract can accept NFTs. If you send an NFT to a contract that does not implementTRC721TokenReceiver, the token is permanently lost. UsesafeTransferFromfor unknown recipients.
Safe transfer alternative
For unknown recipients, use safeTransferFrom — it invokes the receiver hook on the destination contract and reverts if the contract does not implement TRC721TokenReceiver:
async function trc721SafeTransferFrom() {
try {
const contract = await tronWeb.contract().at(trc721ContractAddress);
const txHash = await contract
.safeTransferFrom(
ownerAddress, // from
requireAddress('RECIPIENT_ADDRESS'), // to (may be a contract)
666 // tokenId
)
.send({ feeLimit: 100_000_000 });
console.log('safeTransferFrom tx hash:', txHash);
} catch (error) {
console.error('TRC-721 transaction error', error);
}
}5. Grant approval over an NFT
Call approve(spender, tokenId) to authorize another address to transfer a specific NFT on your behalf. Only one address can be approved per token at a time — calling approve again replaces the previous approval.
async function trc721Approve() {
try {
const contract = await tronWeb.contract().at(trc721ContractAddress);
const txHash = await contract
.approve(
requireAddress('OPERATOR_ADDRESS'), // spender
666 // tokenId
)
.send({ feeLimit: 100_000_000 });
console.log('approve tx hash:', txHash);
} catch (error) {
console.error('TRC-721 transaction error', error);
}
}
trc721Approve();Example output:
approve tx hash: d7cb1451ed962667f3e24323655dadd8c650ee80d171ea5e44ea97b97eaa3118Approve all NFTs for a marketplace
For marketplace integrations, use setApprovalForAll(operator, true) to authorize an operator to transfer every NFT you own and will own:
async function trc721SetApprovalForAll() {
try {
const contract = await tronWeb.contract().at(trc721ContractAddress);
const txHash = await contract
.setApprovalForAll(
requireAddress('OPERATOR_ADDRESS'), // operator (e.g., marketplace contract)
true // approve all
)
.send({ feeLimit: 100_000_000 });
console.log('setApprovalForAll tx hash:', txHash);
} catch (error) {
console.error('TRC-721 transaction error', error);
}
}6. Query approval state
Use getApproved(tokenId) to check the current single-token approval, or isApprovedForAll(owner, operator) to check operator-level approval.
async function trc721GetApproved() {
try {
const contract = await tronWeb.contract().at(trc721ContractAddress);
const approved = await contract.getApproved(666).call();
console.log('approved address for tokenId 666:', approved);
} catch (error) {
console.error('TRC-721 query error', error);
}
}
async function trc721IsApprovedForAll() {
try {
const contract = await tronWeb.contract().at(trc721ContractAddress);
const approved = await contract
.isApprovedForAll(ownerAddress, requireAddress('OPERATOR_ADDRESS'))
.call();
console.log('isApprovedForAll:', approved);
} catch (error) {
console.error('TRC-721 query error', error);
}
}7. List every NFT an address owns
If the contract implements the TRC721Enumerable extension, you can enumerate every NFT held by an address in three steps: get the balance, walk each index, and fetch the token URI for each tokenId.
Step 7.1 — Get the balance
async function ownerBalance() {
const contract = await tronWeb.contract().at(trc721ContractAddress);
const result = await contract.balanceOf(ownerAddress).call();
console.log('count:', tronWeb.toDecimal(result));
}
ownerBalance();Suppose the result is 2 — the owner holds two NFTs.
Step 7.2 — Walk each index
Call tokenOfOwnerByIndex(owner, index) for each index from 0 to balance - 1:
async function tokenOfOwnerByIndex(index) {
try {
const contract = await tronWeb.contract().at(trc721ContractAddress);
const tokenId = await contract.tokenOfOwnerByIndex(ownerAddress, index).call();
console.log('token_id:', tronWeb.toDecimal(tokenId));
} catch (error) {
console.error('TRC-721 query error', error);
}
}
tokenOfOwnerByIndex(0);
tokenOfOwnerByIndex(1);Example output:
token_id: 666
token_id: 555Step 7.3 — Resolve the metadata URI for each token
Call tokenURI(tokenId) to get the pointer to the off-chain metadata JSON:
async function trc721TokenURI(tokenId) {
try {
const contract = await tronWeb.contract().at(trc721ContractAddress);
const uri = await contract.tokenURI(tokenId).call();
console.log(`${tokenId} tokenURI:`, uri);
} catch (error) {
console.error('TRC-721 query error', error);
}
}
trc721TokenURI(666);
trc721TokenURI(555);Different tokenIds map to different tokenURI values — each NFT in the collection has its own metadata file. Fetch each URI off-chain (over HTTPS or through a BTFS gateway) to get the JSON describing the NFT — name, description, image URL, and any attributes.
Related resources
- TRC-721 — standard overview
- TRC-721 protocol interfaces — function reference
- Issuing a TRC-721 token — deploy a new TRC-721 contract
- TronWeb SDK — SDK reference
Updated 2 days ago