NFT Collection Development on TON
TON is architecturally different blockchain. Developer experienced only in EVM approaches TON NFT and discovers that concepts like "contract-address", "transfer", "approve" work fundamentally differently. Every NFT token on TON is a separate smart contract. Not a record in mapping, but fully deployed contract with its own address and storage. This changes entire logic of deployment, mint, and transfer. TEP-62 standard describes interface, but understanding TON's sharding architecture is prerequisite for correct implementation.
How NFT Work on TON: TEP-62
Architecture: Collection + Item Contracts
Collection on TON consists of two contract types:
NFT Collection contract — main collection contract. Stores: owner_address, next_item_index, content (collection metadata), nft_item_code (code for deploying item contracts). On mint deploys new NFT Item contract via deploy_nft_item internal message.
NFT Item contract — each token. Stores: index (sequence number), collection_address, owner_address, individual_content. Item contract address computed deterministically from collection_address and index via stateInit.
;; Get NFT item address by index (FunC)
cell calculate_nft_item_state_init(int item_index, cell nft_item_code) {
cell data = begin_cell()
.store_uint(item_index, 64)
.store_slice(my_address())
.end_cell();
return begin_cell()
.store_uint(0, 2)
.store_dict(nft_item_code)
.store_dict(data)
.store_uint(0, 1)
.end_cell();
}
This means: any NFT address in collection can be computed off-chain without blockchain query. Important for indexers and marketplaces.
TEP-62 Transfer Mechanics
Transfer NFT on TON is sending internal message from current owner to NFT Item contract with operation code transfer. Item contract changes owner_address and optionally sends ownership_assigned notification to new owner.
;; NFT Item: handling transfer
if (op == op::transfer()) {
slice new_owner = in_msg_body~load_msg_addr();
slice response_destination = in_msg_body~load_msg_addr();
throw_unless(401, equal_slices(sender_address, owner));
owner = new_owner;
save_data();
;; Notifying new owner (optional)
send_msg(new_owner, 0, op::ownership_assigned(), ...);
}
Unlike EVM where transferFrom is synchronous operation, TON transfer is async message. New owner receives notification in next block. Important for marketplace logic: listing and delisting require handling async confirmation.
Metadata Standard (TEP-64)
TON NFT metadata stored in two formats: on-chain (TL-B encoded directly in contract) and off-chain (URL to JSON file). For collections with generative metadata typical snake encoding URL:
Cell content = begin_cell()
.store_uint(0x01, 8) // off-chain flag
.store_slice("https://example.com/nft/")
.end_cell()
Individual content stores suffix (e.g., 123.json), collection stores base URL. get_nft_data() getter combines them for full URI.
JSON format identical to EVM: name, description, image, attributes. IPFS storage works same — only difference is how URI passed to contract.
Integration with TON Marketplaces
GetGems — main NFT marketplace on TON. Uses TEP-62 standard directly. For listing on GetGems contract must correctly return get_nft_data(): (init?, index, collection_address, owner_address, individual_content).
TON Diamonds, Fragment — additional marketplaces. Fragment specializes in Telegram usernames as NFT (also TEP-62).
Check compatibility via toncenter.com API: call get_nft_data() via RunGetMethod and compare with marketplace expected format.
Royalty: TEP-66
TEP-66 is royalty standard for TON, analogue to EIP-2981. Collection contract adds getter royalty_params(), returning (numerator, denominator, destination). 5% royalty = numerator 50, denominator 1000.
GetGems supports TEP-66 since 2023. Without implementation royalty won't be paid on major marketplaces.
Mint Mechanics and Tact vs FunC
Language Choice: FunC or Tact
FunC — native TON smart contract language. Low-level, requires explicit memory management and TL-B serialization. All TON production contracts written in FunC, well documented and audited.
Tact — high-level language for TON, syntactically closer to TypeScript. Compiles to FunC. Simplifies development, but ecosystem and audit tools less mature. For production collections in 2024 we use FunC with proven templates from ton-blockchain/token-contract repository.
Batch Mint in Sharding Architecture
Each mint is contract deployment, costing more than EVM SLOAD. On Ethereum mainnet mint ~50K gas; on TON — from 0.05 TON per NFT Item deployment. Batch minting 10,000 tokens at once — 500 TON just on storage fees.
Optimization: lazy mint (NFT Item deploys on first transfer or explicit claim), or pre-deploy on owner side with distribution across batches with delay between blocks.
Workflow
Analysis (0.5-1 day). Collection type (standard, soulbound, fractional), token count, metadata format, royalty, target marketplaces.
Development (3-4 days). Collection + Item contracts in FunC with TEP-62/TEP-64/TEP-66 implementation, tests via ton-jest/sandbox, deployment scripts via ton/core and @ton/ton SDK.
Mint site (1-2 days). Frontend via TonConnect 2.0, integration with TON Wallet, Tonkeeper.
Deployment (0.5-1 day). Testnet (TON Testnet), then mainnet. Verification on GetGems.
Basic collection without mint site — 3-4 days. With mint site, whitelist and GetGems integration — 5-7 days. Cost calculated individually.







