14 KiB
NFT Transactions
Contractless supports native NFT creation, NFT lookup, NFT transfers, NFT burns, and NFT use inside swaps or loan collateral. This guide focuses on NFT creation and lookup tools, metadata requirements, IPFS/RWA preparation, and the difference between standalone NFTs and collections.
Contractless stores the NFT transaction and the metadata CID on chain. It does not store your image, media, document, or full metadata JSON on chain. You must upload and pin those files before creating the NFT transaction.
Important permanence note: Anyone who owns an NFT or RWA should run a local IPFS node and pin the metadata and media for the NFTs/RWAs they care about. On other chains, NFT platforms and hosting services have disappeared in the past, causing people to lose access to the images, documents, or metadata tied to assets they still technically owned on chain. Pinning your own NFTs and RWAs helps ensure the data continues to exist even if the seller, marketplace, minting platform, or public gateway goes offline.
CLI Tools
NFT-related CLI tools:
| Tool | Purpose |
|---|---|
create_nft_tx |
Creates and signs a new NFT creation transaction JSON. |
lookup_nft |
Looks up one NFT by name and item number. |
lookup_nft_list |
Lists NFTs known to the connected node. |
On Windows, add .exe to each command name.
When a tool asks for a wallet path or file path, press <Tab> to search and auto-complete files and folders.
NFT Creation Transaction Fields
The NFT creation transaction is transaction type 4.
Struct fields:
| Field | Size | Meaning |
|---|---|---|
txtype |
1 byte | Transaction type. For NFT creation this is 4. |
time / timestamp |
4 bytes | UTC timestamp when the transaction is created. |
creator |
22 bytes | Creator short address. |
series |
1 byte | 0 for a standalone NFT, 1 for a collection/series. |
nft_name |
15 bytes | Unique NFT or collection name, padded to 15 bytes. |
item_ipfs |
100 bytes | Metadata CID, padded to 100 bytes. |
count |
4 bytes | 1 for standalone NFTs; collection item count for series NFTs. |
desc |
100 bytes | Short on-chain description, padded to 100 bytes. |
txfee |
8 bytes | Fee paid by creator in base currency. Minimum is 0.5 CLC or CLTC. |
signature |
666 bytes | Creator signature. |
Important limits:
- NFT names must normalize to 3 to 15 alphanumeric characters.
- NFT names must be unique. First confirmed creation gets the name.
item_ipfsmust be a CID and must fit in the fixed 100 byte field.descis only a short on-chain description. Use the off-chain metadata JSON for full descriptions.- Standalone NFTs must use
series = 0andcount = 1. - Collections must use
series = 1andcount >= 2.
Standalone NFT vs Collection vs RWA
Standalone NFT
A standalone NFT is one item. It uses:
series = 0
count = 1
The wallet and lookup tooling refer to standalone NFTs with item number 0.
Use a standalone NFT for:
- One-of-one artwork.
- A single certificate.
- A single RWA record.
- A single media item.
Collection / Series
A collection creates many numbered NFTs from one creation transaction. It uses:
series = 1
count = <number of items>
If count = 100, the chain creates 100 numbered NFT assets:
collectionname_1
collectionname_2
collectionname_3
...
collectionname_100
The creator receives all items at creation. This means if you create a large series, every NFT in that series appears in your wallet until you transfer, swap, burn, or lock those items as collateral.
This matters for wallet usability. A 10,000 item series creates 10,000 owned NFT records immediately. The GUI paginates NFTs, but large collections still mean more wallet data and more metadata to cache over time.
Use a collection for:
- Generative art collections.
- Trading-card style collections.
- Ticket batches.
- Numbered certificate sets.
- Numbered RWA records.
RWA / Non-Image NFT
An RWA is not a separate on-chain transaction type. It is an NFT whose metadata describes a real-world asset or non-image record.
Use an RWA NFT for:
- Equipment certificates.
- Property or inventory records.
- Authenticity certificates.
- Legal or business documents.
- Membership or access records.
RWA metadata should include normal NFT fields plus a contractless object that identifies it as an RWA.
IPFS Hosting
Contractless does not provide IPFS hosting. Before creating an NFT, upload and pin your metadata and media files with a provider.
Hosting providers that currently offer IPFS or decentralized storage services include:
Public gateways can be checked here:
https://ipfs.github.io/public-gateway-checker/
Active gateways can still fail to return specific data if they cannot find or stream a provider. The default GUI gateway is:
https://gateway.pinata.cloud/ipfs/
Metadata Rules
Contractless follows common NFT metadata conventions. The chain does not enforce every JSON field, but the GUI wallet expects common fields so it can display names, descriptions, images, traits, and RWA details.
Recommended common fields:
| Field | Purpose |
|---|---|
name |
User-facing NFT name. |
description |
Full description shown in wallets and marketplaces. |
image |
Image URI, usually ipfs://.... |
image_url |
Optional image URI fallback. |
animation_url |
Optional video, audio, HTML, or animation URI. |
external_url |
Optional website or public detail page. |
attributes |
Trait list. |
contractless |
Optional Contractless-specific metadata, especially for RWAs. |
Use ipfs:// URIs inside metadata. The wallet converts them through the configured IPFS gateway.
Single Image NFT Metadata
For a standalone NFT, upload the image first. Then upload one metadata JSON file. Use the CID of that metadata JSON in create_nft_tx.
Example:
{
"name": "Example Artwork #1",
"description": "A short description of the NFT.",
"image": "ipfs://IMAGE_CID/example.png",
"external_url": "https://example.com/nft/1",
"attributes": [
{ "trait_type": "Background", "value": "Blue" },
{ "trait_type": "Edition", "value": "1 of 1" }
]
}
For this format:
item_ipfsshould be the CID of the metadata JSON.seriesshould be0.countshould be1.lookup_nft <name> 0is used for lookup.
Collection / Series Metadata
For a collection, pin one parent folder that contains both a metadata folder and an images folder. Use the CID of the parent folder in the NFT transaction.
Do not use:
- The CID of one numbered JSON file.
- The CID of the
metadatafolder alone. - The CID of the
imagesfolder alone.
Use the CID of this parent folder:
collection-root/ <- use this folder CID in the transaction
metadata/
1.json
2.json
3.json
images/
1.png
2.png
3.png
The GUI wallet builds metadata paths like:
ipfs://PARENT_FOLDER_CID/metadata/1.json
ipfs://PARENT_FOLDER_CID/metadata/2.json
ipfs://PARENT_FOLDER_CID/metadata/3.json
Each numbered JSON file should describe one item and point to the matching image.
Example metadata/1.json:
{
"name": "Example Collection #1",
"description": "Item 1 from Example Collection.",
"image": "ipfs://PARENT_FOLDER_CID/images/1.png",
"attributes": [
{ "trait_type": "Background", "value": "Blue" },
{ "trait_type": "Rarity", "value": "Common" }
]
}
For this format:
item_ipfsshould be the CID ofcollection-root/.seriesshould be1.countshould match the number of JSON files/items.lookup_nft <name> 1looks up item 1.lookup_nft <name> 2looks up item 2.
The current CLI lookup_nft output formats series IPFS display as ipfs://<CID>/<series>.json. The GUI wallet uses the recommended metadata/<series>.json path. For collection creation, follow the parent-folder structure above.
RWA / Non-Image Metadata
For a real-world asset or non-image NFT, keep the common NFT fields and add structured details under attributes and contractless.
Example:
{
"name": "Equipment Certificate #42",
"description": "Ownership record for a physical asset.",
"image": "ipfs://IMAGE_CID/certificate-preview.png",
"external_url": "https://example.com/certificates/42",
"attributes": [
{ "trait_type": "Asset Type", "value": "Equipment" },
{ "trait_type": "Serial Number", "value": "EQ-2026-0042" },
{ "trait_type": "Issuer", "value": "Example Agency" }
],
"contractless": {
"metadata_type": "rwa",
"issuer": "Example Agency",
"document": "ipfs://DOCUMENT_CID/asset-record.pdf",
"jurisdiction": "US",
"record_date": "2026-07-12"
}
}
For RWA records:
- Use
contractless.metadata_type = "rwa"so the GUI can label the NFT as an RWA. - Use
imagefor a preview image or certificate preview. - Use
contractless.documentfor the primary document if there is one. - Avoid putting private personal data in public metadata. IPFS data is public if the CID is shared or discovered.
RWAs can be standalone or collections. For example, one equipment certificate can be standalone, while a batch of numbered tickets or certificates can be a collection.
create_nft_tx
Creates a signed NFT creation transaction JSON and saves it under:
./transactions/<hash>.json
The command prints the signed JSON to the terminal. Creating the file does not guarantee the transaction is mined. Broadcast the transaction with broadcast_transaction or through the GUI wallet.
Usage:
create_nft_tx
Interactive prompts:
Are you creating a 1/1 or a collection?
Please enter the name for your NFT. This can be 3 to 15 characters and must be 100% unique. First come first serve:
Enter your IPFS CID for your NFT. This will be stored in a fixed 100 character field, so shorter CIDs are padded with spaces:
Please enter the number of items in your collection, enter 1 for 1/1s:
Enter your nft description, A max of 100 characters:
Please enter the amount for the fee: (eg. 0.005, minimum fee 0.50000000):
Please enter the path to your wallet file:
What is your wallet decryption key?
Prompt notes:
| Prompt | What To Enter |
|---|---|
1/1 or collection |
Enter collection for a collection. Anything else is treated as standalone. |
| NFT name | Unique 3 to 15 character alphanumeric name. |
| IPFS CID | Standalone: metadata JSON CID. Collection: parent folder CID. |
| Number of items | 1 for standalone. Collection count for series. |
| Description | Short on-chain description, max 100 characters. |
| Fee | Minimum 0.5 CLC or CLTC. |
| Wallet path | Path to the creator wallet file. Use <Tab> for completion. |
| Wallet key | Creator wallet decryption key. |
Output JSON shape:
{
"txtype": 4,
"timestamp": 1780000000,
"creator": "creator_short_address.cltc",
"series": 0,
"nft_name": "example ",
"item_ipfs": "bafy...padded...",
"count": 1,
"desc": "short description padded...",
"txfee": 50000000,
"hash": "transaction_hash",
"signature": "creator_signature"
}
lookup_nft
Looks up one NFT by NFT name and item number.
Usage:
lookup_nft <nft_name> <item_number>
Use item number 0 for a standalone NFT:
lookup_nft example 0
Use item numbers 1 through count for collection items:
lookup_nft example 1
lookup_nft example 2
Interactive prompts:
Please enter the path to your wallet file:
What is your wallet decryption key?
The wallet is used for authenticated RPC handshake. The lookup itself does not spend funds.
Expected output shape:
{
"nft_name": "example",
"series": 1,
"ipfs": "ipfs://CID/1.json",
"asset_name": "example_1",
"genesis": "creation_txid",
"creator": "creator_short_address.cltc",
"current_holder": "holder_short_address.cltc",
"history_count": 1,
"history": [
{
"txid": "transaction_id",
"block": 123,
"txtype": 4,
"action": "create_nft",
"to": "creator_short_address.cltc"
}
]
}
Common history actions:
| Action | Meaning |
|---|---|
create_nft |
NFT was created. |
transfer |
NFT was transferred. |
swap |
NFT moved through a swap. |
loan_locked |
NFT was locked as loan collateral. |
loan_payment |
NFT moved through loan payment handling. |
collateral_claimed |
NFT collateral was claimed. |
loan_issued |
NFT moved as part of loan issuance. |
lookup_nft_list
Returns the NFT list known to the connected peer.
Usage:
lookup_nft_list
Interactive prompts:
Please enter the path to your wallet file:
What is your wallet decryption key?
Expected output shape:
{
"nfts": {
"creation_txid": [
{
"nft_name": "example",
"series": 0
},
{
"nft_name": "collection",
"series": 1
}
]
}
}
The list is grouped by creation transaction hash. A collection can produce many rows under the same creation hash because each numbered item is its own NFT asset.
Ownership and Wallet Display
NFT balances are tracked like native assets, but NFTs use a fixed unit internally. Wallets display NFT ownership based on whether the active address has a positive balance for that NFT asset.
Standalone ownership:
asset = example
series = 0
Collection ownership:
asset = collection
series = 1
asset = collection
series = 2
asset = collection
series = 3
When you create a collection, the creator receives every item from 1 through count. If you create a large collection, expect the NFT page to show many items.
NFT Transfers, Burns, Swaps, and Loans
NFTs can be used by other native transaction types:
- Transfer: move the NFT to another address.
- Burn: destroy the NFT.
- Swap: trade the NFT for another asset.
- Loan collateral: lock the NFT as collateral.
- Collateral claim: transfer locked NFT collateral when claim rules are met.
For numbered collection items, the transaction must include the correct NFT series/item number. For standalone NFTs, the series is 0.
Details for those transaction flows are covered in the transfer, burn, swap, and loan documentation.