Skip to content

feat(nfts): Read the display metadata of minted NFTs by instance id - #426

Merged
TarikGul merged 2 commits into
mainfrom
feat/nfts-getItemDisplay
Oct 6, 2026
Merged

TarikGul merged 2 commits into
mainfrom
feat/nfts-getItemDisplay

Conversation

@piggydoughnut

@piggydoughnut piggydoughnut commented Oct 2, 2026 •

Copy link
Copy Markdown
Contributor

Why

Today every app that shows a minted NFT re-implements the same three-layer metadata resolution by hand, e.g. scarcity-stash has ~200 lines of it — and each one independently re-derives the instance→item mapping, the precedence rules, and the text-or-bytes decoding that this package already does internally for catalogues and previews. getInstanceDisplays exposes that existing resolution for minted instances, so owned items, catalogue entries and mint previews finally render identically instead of drifting.

What

Closes the getItemDisplay / getItemDisplays items of
#318. Named for this package's vocabulary, where an item
is a catalogue definition and an instance is a minted NFT.

const result = await getInstanceDisplays(chain, [0n, 1n, 404n]);
if (result.ok) {
    for (const display of result.value.displays) {
        if (display.tag === "NotFound") continue;   // burned, or never minted
        console.log(display.name, display.collection, display.item);
        if (display.transferability === "Transferable") offerSend(display);
    }
}
interface InstanceDisplaysResult {
    at: FinalizedSnapshot;           // the one block every value was read at
    displays: InstanceDisplay[];     // one per instance asked for, in the order asked
}

interface InstanceDisplayResult {
    at: FinalizedSnapshot;
    display: InstanceDisplay;
}

type InstanceDisplay =
    | {
          tag: "Found";
          instance: bigint;
          collection: number;              // resolved by the runtime, not re-derived
          item: number;
          transferability: Transferability | null;  // "Transferable" | "Soulbound"
          supply: number | null;
          liveSupply: number | null;
          name: string | null;
          imageRef: ImageRef | null;       // { hex: string; text: string | null }
          rarity: string | null;
          attributes: Record<string, string>;       // all three layers merged, always filled
      }
    | { tag: "NotFound"; instance: bigint };
  • New contract NftsInstancesChain, separate so a catalogue-only app's
    whitelist does not grow. Apps calling this read must whitelist
    api.ScarcityApi.metadata_batch and query.Scarcity.ItemDefs.
  • NftsIdError now also reports out-of-range u64 instance ids; its id field
    widened from number to number | bigint.
  • Changeset parked in pending-changesets/, minor for @parity/product-sdk-nfts
    and the umbrella per the RELEASES.md rule.

Verification

  • 215 unit tests pass, 21 of them in instances.ts, covering layer precedence,
    the Found-with-empty-bag case, chunking at 128, the adaptive re-chunk on a
    smaller reported cap, dedup of definition keys, and a definition missing under
    a live instance.
  • @parity/product-sdk typecheck passes, which is the fidelity guard asserting
    the hand-written contract against the real descriptors. Added a positive
    assertion for paseo and a @ts-expect-error negative control for devnet,
    which carries no ScarcityApi.
  • Probed live next-asset-hub-paseo read-only to confirm the semantics the
    design rests on: instances 0/1/2 resolve to (0,3), (0,1), (2,3) and
    their definitions read Transferable with supplies 3/3, 5/5, 2/2, while u64
    max comes back unresolved.
  • pnpm check clean; examples/nfts-demo builds.
  • Not run: the e2e suite, which needs the test host and a live chain. The
    new Playwright assertion in catalogue.spec.ts is the one piece here that has
    not executed.

@github-actions

github-actions Bot commented Oct 2, 2026 •

Copy link
Copy Markdown

📦 Bundle size impact

Comparing 2026-10-05T22:08:58.255Z → 2026-10-05T22:09:50.607Z

Package Entry Bundled before Bundled after Δ Ship gzip Δ Shake ratio
🟢 @parity/product-sdk . 8.86 MB 8.86 MB +671 B (+0.0%) 0 B 0% (was 0%)
🟢 @parity/product-sdk ./core 8.86 MB 8.86 MB +671 B (+0.0%) 0 B 0% (was 0%)
🟡 @parity/product-sdk ./nfts 26.3 KB 29.3 KB +3.0 KB (+11.4%) 0 B —
🟢 @parity/product-sdk ./react 8.87 MB 8.87 MB +671 B (+0.0%) 0 B 0% (was 0%)
🟢 @parity/product-sdk ./wallet 208.6 KB 209.2 KB +659 B (+0.3%) 0 B 0% (was 0%)
🟢 @parity/product-sdk-signer . 208.6 KB 209.2 KB +659 B (+0.3%) +445 B 0% (was 0%)

Thresholds — 🟡 ≥10% or ≥5.0 KB · 🟠 ≥20% or ≥15.0 KB (bundled). Percentage only applies once the baseline is ≥ 10 KB. Informational — this check never blocks merge.

@piggydoughnut
piggydoughnut marked this pull request as draft October 2, 2026 07:08
getInstanceDisplays / getInstanceDisplay: positional per-instance reads of
name, collectionName, rarity, imageRef, the three-layer attributes bag, the
resolved (collection, item), and transferability/supply/liveSupply from the
item definition. Capped at MAX_INSTANCES_PER_READ (128), refused above it
rather than paged. name is the instance's own (instance layer, else item
layer) and never inherits the collection's, which is reported beside it as
collectionName.
@piggydoughnut
piggydoughnut force-pushed the feat/nfts-getItemDisplay branch from cf07e77 to 3473d2f Compare October 5, 2026 09:48
@piggydoughnut
piggydoughnut marked this pull request as ready for review October 5, 2026 09:48
Comment thread product-sdk/packages/nfts/src/instances.ts Outdated
Comment thread product-sdk/packages/nfts/src/items.ts
…ability behavior change

On a metadata_batch TooLarge the read now re-chunks only the instances
that refusal covered rather than re-running the whole list, keeping the
chunks that already answered. Observable behavior is unchanged for the
single-chunk public path; the saving shows if the input cap ever exceeds
the runtime cap.

Also calls out the getCollectionItems Transferability refusal in the
changeset as a behavior change for consumers.
@TarikGul
TarikGul merged commit bf4bc8d into main Oct 6, 2026
13 of 14 checks passed
@TarikGul TarikGul mentioned this pull request Oct 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants