# 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 `` 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_ipfs` must be a CID and must fit in the fixed 100 byte field. - `desc` is only a short on-chain description. Use the off-chain metadata JSON for full descriptions. - Standalone NFTs must use `series = 0` and `count = 1`. - Collections must use `series = 1` and `count >= 2`. ## Standalone NFT vs Collection vs RWA ### Standalone NFT A standalone NFT is one item. It uses: ```text 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: ```text series = 1 count = ``` If `count = 100`, the chain creates 100 numbered NFT assets: ```text 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: - [Pinata](https://www.pinata.cloud/) - [Filebase](https://filebase.com/) - [thirdweb Storage](https://thirdweb.com/storage) - [Lighthouse](https://www.lighthouse.storage/) Public gateways can be checked here: [https://ipfs.github.io/public-gateway-checker/](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: ```text 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: ```json { "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_ipfs` should be the CID of the metadata JSON. - `series` should be `0`. - `count` should be `1`. - `lookup_nft 0` is 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 `metadata` folder alone. - The CID of the `images` folder alone. Use the CID of this parent folder: ```text 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: ```text 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`: ```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_ipfs` should be the CID of `collection-root/`. - `series` should be `1`. - `count` should match the number of JSON files/items. - `lookup_nft 1` looks up item 1. - `lookup_nft 2` looks up item 2. The current CLI `lookup_nft` output formats series IPFS display as `ipfs:///.json`. The GUI wallet uses the recommended `metadata/.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: ```json { "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 `image` for a preview image or certificate preview. - Use `contractless.document` for 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: ```text ./transactions/.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: ```text create_nft_tx ``` Interactive prompts: ```text 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 `` for completion. | | Wallet key | Creator wallet decryption key. | Output JSON shape: ```json { "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: ```text lookup_nft ``` Use item number `0` for a standalone NFT: ```text lookup_nft example 0 ``` Use item numbers `1` through `count` for collection items: ```text lookup_nft example 1 lookup_nft example 2 ``` Interactive prompts: ```text 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: ```json { "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: ```text lookup_nft_list ``` Interactive prompts: ```text Please enter the path to your wallet file: What is your wallet decryption key? ``` Expected output shape: ```json { "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: ```text asset = example series = 0 ``` Collection ownership: ```text 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.