NFT provenance
You are tracing a Chia NFT from its mint to its current owner. The launcher coin (or any coin in the singleton's lineage) is enough — you follow the eve coin forward through every singleton spend, classifying each step and labelling any addresses chia-explorer recognises.
This skill follows the conventions in this repo's SKILLS.md. Read that if anything below is unclear.
Required capabilities
- chia-explorer (
mcp__chia-explorer__*) — mandatory. Usesget_coin_by_name,get_coin_records_by_parent_ids,get_puzzle_and_solution,decompile_puzzle,puzzle_hash_to_address,list_prefarm_addresses.
Resolve inputs
You need: $launcher_id, $include_metadata.
$launcher_id— the NFT singleton's launcher coin id as0x...hex (32 bytes / 64 hex chars). Required.$include_metadata—trueorfalse. Defaultfalse. If true, the skill will try to surface on-chain metadata URIs (data_uris, metadata_uris, license_uris). It will not fetch IPFS / HTTP content; just print the URIs.
Resolve: arguments → context → ask user.
Validate $launcher_id (32 bytes hex).
Note: if the user gives you a coin id rather than a launcher, you can still find the lineage by walking back to the launcher; do that first via coin-lineage-style traversal. Identify the launcher as the singleton's root coin (its puzzle decompiles as a launcher / eve puzzle).
Walk the singleton
Start from the launcher coin. For each step in the singleton chain:
- Look up the coin record by name. It should be spent (the eve coin always is). Capture the spent height.
- Get puzzle and solution at the spent block. Decompile.
- The first child of the launcher coin is the eve coin. From there, each subsequent NFT generation is a single child of the previous coin (singletons have a single child).
- Resolve the puzzle hash to an address. Check the prefarm registry for a label. Capture the address as the current holder at the end of that step.
- Move to the child coin. Repeat.
Stop when you reach a coin that is unspent. That coin is the current state of the NFT, and its puzzle hash maps to the current holder address.
Detect transfers vs no-op spends
Not every singleton spend is a transfer of ownership. The NFT can be:
- Transferred — the inner puzzle's owner changed. The puzzle hash post-spend resolves to a new address.
- Re-minted / state-updated — metadata changed (uri list updated, royalty tweaked) but ownership didn't.
- DID-locked / DID-released — assigned to or unassigned from a DID.
In the report, label each step as MINT, TRANSFER, METADATA_UPDATE, DID_ASSIGN, DID_RELEASE, or OTHER.
Compose the report
NFT provenance — launcher 0xabc...123
CURRENT HOLDER: xch1zzz...mno (no label)
TOTAL TRANSFERS: 3
AGE: ~62 days since mint
LINEAGE
MINT block 6,500,001 → xch1aaa...bbb (creator)
TRANSFER block 6,510,210 → xch1ccc...ddd (no label)
METADATA_UPDATE block 6,515,000 (uri added, owner unchanged)
TRANSFER block 6,525,800 → xch1eee...fff (no label)
TRANSFER block 6,530,915 → xch1zzz...mno (no label) ← current
METADATA (latest spend)
data_uri: ipfs://bafy.../image.png
metadata_uri: ipfs://bafy.../metadata.json
license_uri: ipfs://bafy.../license.txt
Full launcher_id: 0xabc123...
Current owner address: xch1zzz...mno
If $include_metadata = false, omit the METADATA section.
For very long lineages (hundreds of spends — rare but possible for active NFTs), summarise: show first 3 steps, last 5 steps, and a ... N intermediate steps omitted ... line.
Risk and authenticity notes
Surface at the bottom when applicable:
- Mint creator address has a chia-explorer label — flag if it matches a known scam-list (no built-in scam list yet; just note the label).
- NFT passed through a labelled exchange address — useful for buyers verifying secondary-market provenance.
- Owner currently a DID — note the DID identifier and that the NFT is bound to that DID.
- High transfer rate — if transfers happen many times per hour, flag it: could be wash-trading or a hot trading account.
Don't editorialise on whether the NFT is "legit". The on-chain trace doesn't tell you that.
Hard rules
- Read-only. Never sign, never claim, never mint. If the user wants to buy or sell, they go to a marketplace.
- launcher_id is canonical. Names and titles can be spoofed; launcher_ids cannot. Always print the full launcher in the report footer.
- Don't fetch off-chain content. If
$include_metadatais true, show the URIs only. The user opens them themselves. - Hop cap doesn't apply — singletons have a deterministic chain. Walk it to the end.
Recovery patterns
- Launcher not found. Emit
STATUS: failed,REASON: launcher not found. - Singleton chain broken (a step's child can't be found) — note the break, report what you walked, emit
STATUS: partial. - Puzzle classification ambiguous on some step. Mark that step
OTHERand continue.
Output
STATUS: success | partial | failed | aborted
LAUNCHER_ID: <full 0x... hex>
CURRENT_HOLDER: <full address>
CURRENT_HOLDER_LABEL: <registry label> | none
TRANSFERS: <count of TRANSFER steps>
TOTAL_STEPS: <count of all spends in the chain>
AGE_BLOCKS: <peak - mint_height>
MINTED_AT_HEIGHT: <int>
RISK_NOTES: <comma-separated tags> | none
ARTIFACT: none
REASON: <one line, only when STATUS is failed, partial, or aborted>