# NFT Collection

> Non-fungible token collection with unique token IDs, metadata URIs, and badge-based ownership

**Category:** Token Types

## Summary

Required standards: \["NFTs"]

* For tradable NFTs: \["NFTs", "NFTMarketplace", "NFTPricingDenom:ubadge"]
* validTokenIds: set to the range of unique token IDs (e.g. \[{ "start": "1", "end": "100" }])
* Each token ID represents a unique NFT; amount in transfers is typically "1"
* Use {id} placeholder in tokenMetadata URI for per-token metadata (e.g. "ipfs\://QmHash/{id}")
* Mint approvals MUST have overridesFromOutgoingApprovals: true
* Ownership times are usually forever for NFTs

## Instructions

## NFT Collection Configuration

When creating an NFT collection, follow this pattern:

### Required Configuration

1. **Standards**: Include "NFTs" in the standards array
   * Example: "standards": \["NFTs"]
   * For tradable NFTs: "standards": \["NFTs", "NFTMarketplace", "NFTPricingDenom:ubadge"]
2. **validTokenIds**: Set to the range of unique token IDs
   * Example for 100 NFTs: \[{ "start": "1", "end": "100" }]
   * Each token ID represents a unique NFT
3. **Token Metadata**: Each tokenMetadata entry must include tokenIds matching the range
   * Use {id} placeholder for per-token metadata URIs

### Pattern Example

```json
{
  "updateValidTokenIds": true,
  "validTokenIds": [{ "start": "1", "end": "100" }],
  "updateCollectionMetadata": true,
  "collectionMetadata": {
    "uri": "ipfs://QmCollectionMetadata",
    "customData": ""
  },
  "updateTokenMetadata": true,
  "tokenMetadata": [{
    "uri": "ipfs://QmTokenMetadata/{id}",
    "customData": "",
    "tokenIds": [{ "start": "1", "end": "100" }]
  }],
  "updateCollectionApprovals": true,
  "collectionApprovals": [{
    "fromListId": "Mint",
    "toListId": "All",
    "initiatedByListId": "bb1creator...",
    "approvalId": "manager-mint",
    "tokenIds": [{ "start": "1", "end": "100" }],
    "transferTimes": [{ "start": "1", "end": "18446744073709551615" }],
    "ownershipTimes": [{ "start": "1", "end": "18446744073709551615" }],
    "approvalCriteria": {
      "overridesFromOutgoingApprovals": true
    }
  }],
  "updateStandards": true,
  "standards": ["NFTs"]
}
```

### Sequential Minting with predeterminedBalances

For NFTs, use predeterminedBalances with incrementTokenIdsBy: "1" to mint tokens sequentially (token 1, then 2, then 3, etc.):

```json
{
  "approvalCriteria": {
    "overridesFromOutgoingApprovals": true,
    "predeterminedBalances": {
      "manualBalances": [],
      "incrementedBalances": {
        "startBalances": [{ "amount": "1", "tokenIds": [{ "start": "1", "end": "1" }], "ownershipTimes": [{ "start": "1", "end": "18446744073709551615" }] }],
        "incrementTokenIdsBy": "1",
        "incrementOwnershipTimesBy": "0",
        "durationFromTimestamp": "0",
        "allowOverrideTimestamp": false,
        "recurringOwnershipTimes": { "startTime": "0", "intervalLength": "0", "chargePeriodLength": "0" },
        "allowOverrideWithAnyValidToken": false
      },
      "orderCalculationMethod": {
        "useOverallNumTransfers": true,
        "usePerToAddressNumTransfers": false,
        "usePerFromAddressNumTransfers": false,
        "usePerInitiatedByAddressNumTransfers": false,
        "useMerkleChallengeLeafIndex": false,
        "challengeTrackerId": ""
      }
    },
    "maxNumTransfers": {
      "overallMaxNumTransfers": "100",
      "perInitiatedByAddressMaxNumTransfers": "1",
      "perToAddressMaxNumTransfers": "0",
      "perFromAddressMaxNumTransfers": "0",
      "amountTrackerId": "nft-mint-tracker",
      "resetTimeIntervals": { "startTime": "0", "intervalLength": "0" }
    }
  }
}
```

Key: incrementTokenIdsBy: "1" means each mint gets the next sequential token ID. Use maxNumTransfers to cap total mints and per-user mints.

IMPORTANT: predeterminedBalances and approvalAmounts are INCOMPATIBLE — use one or the other. NFTs use predeterminedBalances (not approvalAmounts).

### NFT-Specific Gotchas

* Each token ID is unique and represents a distinct NFT
* Amount in transfers is typically "1" (one NFT per transfer)
* Ownership times are usually forever for NFTs
* Mint approvals MUST have overridesFromOutgoingApprovals: true
* Use {id} in metadata URIs for per-token metadata

## Common Mistakes

* DON'T reuse token IDs across editions without understanding ownership times — each token ID is unique and represents a distinct NFT.
* DON'T forget tokenIds in canUpdateTokenMetadata permission structure — the permission must specify which token ID ranges it covers.
* DON'T use {id} in metadata name, description, or image fields — the {id} placeholder only works in the URI string itself (e.g. "ipfs\://abc/{id}").
* DON'T forget overridesFromOutgoingApprovals: true on Mint approvals — required for all minting operations.
* DON'T use custom list IDs — only reserved IDs are valid: "All", "Mint", "!Mint", "AllWithoutMint", or direct bb1... addresses.
