Skip to content

The marketplace model

GraveMarket indexes NFT collections across nine chainssolana-mainnet, ethereum-mainnet, polygon-mainnet, base-mainnet, bsc-mainnet, xrpl-mainnet, cronos-mainnet, abstract-mainnet and robinhood-mainnet.

That breadth is the source of most of the mistakes on this page. Code written against one chain reads as correct and quietly misbehaves on the others.

bash
npm install @solanadeads/gravemarket
ts
import { GraveyardClient } from '@solanadeads/gravemarket';
const gmk = new GraveyardClient();   // public API — no key, no origin registration

Collections

Addressable three ways — slug (solana-deads), on-chain address, or internal id. Prefer the slug: it is stable, readable, and what the marketplace URLs use.

ts
const c = await gmk.collections.get('solana-deads');

Related reads hang off the same identifier: items, activity, stats, traits, holders, offers, floor.

Items

An item is identified by token_address plus token_id.

token_id is null on Solana

Solana NFTs are one mint address each, so there is no token id. EVM collections have both. Code that builds a key as `${token_address}:${token_id}` produces Abc…:null on Solana — which collides across every unlisted item if you use it as a map key, and looks like a real id in a URL.

Key on token_address alone unless you know you are on an EVM chain.

Listings and prices

listing_price and listing_currency are nullable, and null means not listed — not free, and not zero.

ts
const label = item.listing_price == null
  ? 'Not listed'
  : `${item.listing_price} ${item.listing_currency}`;

Never compare or sum prices across chains

listing_currency and floor_currency vary by chain — SOL, ETH, MATIC, XRP and more. A "cheapest item" sort across a multi-chain result set compares SOL to ETH and produces nonsense, and a summed "total volume" is a meaningless number.

Filter to one chain before ranking or aggregating, or convert deliberately with a rate you control. There is no implicit common unit.

This is the same shape as GraveMint's rule that a price is a resolved shape rather than a number — see the GraveMint read model.

Stats, and a caveat worth reading

ts
const s = await gmk.collections.stats('solana-deads');

Returns headline stats plus a daily series — and wash_trade_count_7d.

Volume figures are not wash-filtered

The stats endpoint reports wash-trade count separately rather than silently subtracting it. If you are surfacing volume to users, read that number and decide what to do with it. A headline volume that includes wash trades is not wrong data — it is unqualified data, and presenting it as clean is your call to get right.

Activity

Sales and listings, either for one collection or globally:

ts
await gmk.collections.activity('solana-deads', { limit: 50 });
await gmk.activity.list({ limit: 50 });

Pairs naturally with a GraveMint drop: "is this still minting, and what is it doing on secondary?" is one question, which is why the MCP server exposes both products.

Traits and rarity

ts
const t = await gmk.collections.traits('solana-deads');

Every trait and value with counts — what a rarity filter is built from. Items carry rarity_rank and rarity_score.

⚠️ Rarity rank is per collection and per methodology. Rank 1 in a 10,000-item collection and rank 1 in a 100-item one are not comparable, and the rank means nothing across collections. Show it beside the collection, never in a cross-collection list.

Pagination — cursors, and why a partial page lies

Every list endpoint is cursor-paginated and returns cursor and hasMore.

ts
let cursor;
const all = [];
do {
  const page = await gmk.collections.activity('solana-deads', { limit: 50, cursor });
  all.push(...page.data);
  cursor = page.cursor;
} while (cursor);

The SDK also exposes listAll / activityAll async generators that drain for you.

A bare limit silently drops the rest

If you are computing a total, a holder count, a volume figure or a floor, drain the cursor. A first page produces a confidently wrong number with no error — which is worse than a failure, because nothing tells you to look.

How this differs from GraveMint

GraveMint v1GraveMarket v1
purposemint a drop on your siteread marketplace state
credentialinvite-only, origin-bound, collection-scopednone
writesprepare / executeread-only
paginationlimit + offsetcursor
chainsSolana onlynine

Next

SDK pages are generated from the published npm tarballs and cannot drift.