GraveMint SDK — method reference
@solanadeads/gravemint · version 1.3.0 · install with npm i @solanadeads/gravemint
Signatures and types are generated from the published package. Explanatory prose is edited for clarity. For how to put them together, start with the GraveMint SDK guide.
import { GraveMintClient } from "@solanadeads/gravemint";
const client = new GraveMintClient({ apiKey: "gm_pub_..." });Contents
client.platform— 2 methodsclient.discovery— 7 methodsclient.collections— 3 methodsclient.nfts— 1 methodclient.bounty— 2 methodsclient.codes— 1 methodclient.v1— 19 methodsclient.mint— 4 methods- Functions — 11
- Classes — 12
- Types — 68
client.platform
First-party only — not usable with a partner key. See Legacy read API.
platform.getCapabilities()
GET /platform-capabilities
Public feature-flag snapshot — which chains are enabled, which optional features (raffles, packs) the platform exposes.
getCapabilities(options?: RequestOptions): Promise<PlatformCapabilities>;platform.getTokenPrices()
GET /token-prices
USD pricing snapshot for supported chain natives. Returns a map keyed by symbol — e.g. { SOL: 240, ETH: 0, POL: 0, SUI: 0 }. The sources map tells you which provider each price came from (or "unavailable"). Intended for display, not for settlement-grade pricing.
getTokenPrices(options?: RequestOptions): Promise<TokenPricesResponse>;client.discovery
First-party only — not usable with a partner key. See Legacy read API.
discovery.getFeatured()
GET /featured — {success, collections: [...]}
getFeatured(filters?: DiscoveryFilters, options?: RequestOptions): Promise<CollectionSummary[]>;discovery.getLiveMints()
GET /live-mints — {success, mints: [...]}
getLiveMints(filters?: DiscoveryFilters, options?: RequestOptions): Promise<CollectionSummary[]>;discovery.getTrending()
GET /trending — {success, type, collections: [...]}
getTrending(filters?: DiscoveryFilters, options?: RequestOptions): Promise<CollectionSummary[]>;discovery.getUpcoming()
GET /upcoming-mints — {success, mints: [...]}
getUpcoming(filters?: DiscoveryFilters, options?: RequestOptions): Promise<CollectionSummary[]>;discovery.getRecentSoldouts()
GET /recent-soldouts — {collections: [...]} (no success wrapper)
getRecentSoldouts(filters?: DiscoveryFilters, options?: RequestOptions): Promise<CollectionSummary[]>;discovery.listCollections()
GET /collections — {success, collections: [...]}
listCollections(params?: DiscoveryFilters & {
offset?: number;
status?: "active" | "ended" | "upcoming";
}, options?: RequestOptions): Promise<CollectionSummary[]>;discovery.searchCollections()
GET /collections/search?q=... — {success, collections: [...]} (snake_case items).
Min query length 2 (server-enforced). Items returned use snake_case (short_id, total_supply, blockchain_network) unlike the other discovery feeds which use camelCase. We type this distinctly so the compiler catches the mismatch.
searchCollections(query: string, params?: {
limit?: number;
}, options?: RequestOptions): Promise<CollectionSearchResult[]>;client.collections
First-party only — not usable with a partner key. See Legacy read API.
collections.get()
GET /collection/:identifier
Full collection record + stats + mint phases + server time. The response is a discriminated union — when the collection isn't fully deployed yet the server returns a pre-launch envelope (status: 'coming_soon' | 'deploying' | 'maintenance') instead of the full record. Branch on ('status' in result) or check for the presence of result.stats.
get(identifier: CollectionIdentifier, options?: RequestOptions): Promise<CollectionResponse>;collections.getGallery()
GET /collection/:identifier/gallery
Paginated list of NFTs in the collection.
getGallery(identifier: CollectionIdentifier, params?: {
limit?: number;
offset?: number;
mintedOnly?: boolean;
}, options?: RequestOptions): Promise<RecentMint[]>;collections.getRecentlyMinted()
GET /collection/:identifier/recently-minted
Live mint activity. Returns minter wallets (publicly on-chain).
getRecentlyMinted(identifier: CollectionIdentifier, params?: {
limit?: number;
}, options?: RequestOptions): Promise<RecentMint[]>;client.nfts
First-party only — not usable with a partner key. See Legacy read API.
nfts.get()
GET /nft/:identifier
Single NFT record. Identifier is mint_address (Solana), contract:tokenId (EVM), or numeric token id. UUIDs rejected.
get(identifier: NftIdentifier, options?: RequestOptions): Promise<RecentMint>;client.bounty
First-party only — not usable with a partner key. See Legacy read API.
bounty.getSummary()
getSummary(identifier: CollectionIdentifier, options?: RequestOptions): Promise<BountySummary>;bounty.getPrizes()
getPrizes(identifier: CollectionIdentifier, options?: RequestOptions): Promise<BountyPrizesResponse>;client.codes
First-party only — not usable with a partner key. See Legacy read API.
codes.validate()
POST /code/validate
Check whether a claim code is valid. Does NOT consume the code.
Note: this endpoint does NOT use the {success:true} envelope. The response shape is {valid: boolean, error?: string, ...}.
validate(code: string, options?: RequestOptions): Promise<CodeValidationResult>;client.v1
Works with a partner key.
The versioned contract: everything needed to BUILD a mint page.
v1.collection()
Everything needed to render the drop.
collection(identifier: string): Promise<V1Collection>;v1.eligibility()
Checks whether a wallet meets a phase’s requirements, including NFT holdings and allowlists. The response keeps eligibility separate from the time window: also check phase.hasStarted and phase.hasEnded before enabling minting.
eligibility(collectionId: string, phaseId: string, walletAddress: string): Promise<V1Eligibility>;v1.gallery()
The drop's NFTs that can still be minted, paginated. Minted NFTs are not included — use allMinted() for those. limit defaults to 100.
Scoped server-side to the collections your key covers, so this can only ever return your own drop — a key issued for another collection gets a 403 COLLECTION_NOT_IN_SCOPE, not an empty list.
gallery(identifier: string, params?: {
limit?: number;
offset?: number;
}): Promise<V1Gallery>;v1.recentlyMinted()
The live "just minted" feed for the drop — the same one gravemint.io renders.
recentlyMinted(identifier: string, params?: {
limit?: number;
}): Promise<V1RecentlyMinted>;v1.bounty()
Bounty summary for the drop, when it has one.
bounty(identifier: string): Promise<V1BountySummary>;v1.bountyPrizes()
The bounty's public prize table.
bountyPrizes(identifier: string): Promise<V1BountyPrizes>;v1.walletMints()
Returns minted and remaining counts per phase, including bonus mints such as BOGO and bounty mints. Use this response for wallet counts; eligibility uses separate allocation rules.
walletMints(collectionId: string, walletAddress: string): Promise<V1WalletMints>;v1.claimCodes()
Does this drop use claim codes, and of what kind?
claimCodes(collectionId: string): Promise<V1ClaimCodes>;v1.claimCodeBenefits()
What a specific wallet is already entitled to from codes it has redeemed.
claimCodeBenefits(collectionId: string, walletAddress: string, params?: {
phaseId?: string;
blockchain?: string;
packTierId?: string;
}): Promise<V1ClaimCodeBenefits>;v1.validateClaimCode()
Check a code against THIS drop.
Scoped on purpose. The code is resolved and its own collection compared with the one you name, and a code belonging to another drop answers exactly as an unknown code does — so this can never be used to discover that a code exists elsewhere.
validateClaimCode(collectionId: string, code: string): Promise<V1ClaimCodeCheck>;v1.tokenPrices()
Platform token prices — what an SPL-priced drop is worth in USD. Global, no drop.
tokenPrices(): Promise<V1TokenPrices>;v1.peggedPrice()
The resolved amount for a USD- or native-pegged phase, on its own — e.g. to refresh a live peg without re-reading the whole drop. phase.priceDisplay on the collection response already resolves pegs for display. (v1 phases carry no raw price field.)
peggedPrice(phaseId: string): Promise<V1PeggedPrice>;v1.dutchPrice()
The live price of a dynamic Dutch phase, which moves with time.
dutchPrice(phaseId: string): Promise<V1DutchPrice>;v1.availability()
Supply counts for the drop (see V1Availability): fine for a progress bar, not for deciding a drop is sold out. prepare is what actually allocates supply.
availability(collectionId: string): Promise<V1Availability>;v1.traits()
Trait names and values, for filtering a gallery.
traits(identifier: string): Promise<V1Traits>;v1.allMinted()
Returns one page of minted items. limit defaults to 20 and is capped at 100. Increment offset until hasMore is false. Use newest or oldest for a full paginated history; popular ranks within each page.
allMinted(identifier: string, params?: {
limit?: number;
offset?: number;
sort?: "newest" | "oldest" | "popular";
search?: string;
}): Promise<V1AllMinted>;v1.topHolders()
Largest holders — social proof for a mint page.
topHolders(identifier: string): Promise<V1TopHolders>;v1.topMinters()
Who minted the most.
topMinters(identifier: string): Promise<V1TopMinters>;v1.version()
What the API is; useful for degrading deliberately rather than on a 404.
version(): Promise<{
version: string;
stability: string;
docs: string;
}>;client.mint
Works with a partner key.
prepare -> sign -> execute. You sign; GraveMint broadcasts.
mint.prepare()
Ask GraveMint to price, check eligibility, reserve supply and build the transaction.
prepare(input: PrepareMintInput): Promise<PreparedMint>;mint.execute()
Submits the signed transaction through GraveMint. The default timeout is 130 seconds; a configured client-wide timeoutMs takes precedence. Execute is not retried automatically by default. An uncertain result requires checking the existing mint before submitting again. Override options.maxRetries only with application-level outcome handling.
execute(input: {
sessionId: string;
signedTransaction: string;
}, options?: RequestOptions): Promise<V1ExecuteResult>;mint.executeBatch()
Hand back SEVERAL signed transactions at once.
Required for any quantity above 1: every Solana standard mints exactly one NFT per transaction (maxPerTx = 1 for mpl-core, legacy and compressed), so a prepare for N returns N transactions and N sessions.
executeBatch(entries: Array<{
sessionId: string;
signedTransaction: string;
}>, options?: RequestOptions): Promise<V1ExecuteBatchResult>;mint.mint()
Coordinates preparation, signing and execution. Check capabilities.supportedBySurface first and offer mintUrl for collections outside the core flow. Throws if preparation returns no transaction to sign.
mint(input: PrepareMintInput & {
signer: TransactionSigner;
},
/** Applied to the EXECUTE call (timeout, signal). Prepare uses the client defaults. */
executeOptionsOverride?: RequestOptions): Promise<MintResult>;Functions
fromHttpStatus
Map an HTTP status to the right error class. Used internally by the HTTP transport. The body is the parsed JSON response if available.
export declare function fromHttpStatus(status: number, message: string, body: unknown, requestId?: string): GraveMintError;isBatchMintResult
True when mint() minted more than one NFT and answered with the batch shape.
export declare function isBatchMintResult(result: MintResult): result is V1MintBatchOutcome;isUuid
Input validation. Catches obvious bad inputs client-side to save round-trips and rate-limit budget. Server-side validation is still authoritative.
Returns true if the string looks like a UUID.
export declare function isUuid(s: string): boolean;looksLikeAddress
Returns true if the string looks like a valid on-chain address (any chain).
export declare function looksLikeAddress(s: string): boolean;normalizeClaimCode
Normalize a claim code (trim, then upper-case), rejecting only what can never be a code: a non-string, or something under 4 or over 1024 UTF-16 code units after trimming and upper-casing. Format-level only; existence is the server's answer.
Deliberately permissive. This used to allow only 6-40 characters of A-Z, 0-9 and '-', and so rejected real codes before the server was ever asked: imported codes of 4-5 or 41-64 characters, codes with _ or ., and generated codes whose prefix has a space or an accented letter. Seven such codes are live (measured 2026-09-23). A client check stricter than the server can only produce false rejections.
NOTE for senders: the server upper-cases the code itself. codes.validate() sends the TRIMMED code rather than this upper-cased form, because toUpperCase() differs across JS engines' Unicode versions for a handful of letters, and the server's answer must not depend on the caller's runtime.
export declare function normalizeClaimCode(code: unknown): string;sanitize
Response sanitization. Removes PII and internal identifiers before returning data to SDK consumers.
Policy ("standard"):
- STRIP: emails, IP addresses, push subscriptions, fingerprints, internal UUIDs (any field literally named
idat any depth, plus known UUID-shaped fields) - KEEP: wallet addresses (publicly on-chain), display names, mint addresses, short_ids, content URLs.
The sanitizer is conservative — when in doubt, it strips. Consumers who need the raw response can set client.options.sanitize = false per-request, but doing so re-exposes UUIDs and is documented as unsafe for public callers.
Returns a deep-cleaned copy of the input. Does not mutate. Safe to call on arbitrary JSON-shaped data (arrays, objects, primitives, null).
export declare function sanitize<T = unknown>(value: T): T;validateCollectionIdentifier
Validate a collection identifier. Accepts a short_id or an on-chain address. Explicitly rejects UUIDs — the SDK does not expose internal IDs.
Throws ValidationError on bad input.
export declare function validateCollectionIdentifier(id: unknown): string;validateLimit
Validate a pagination limit against a max. Returns the clamped value.
export declare function validateLimit(limit: unknown, max: number, fallback: number): number;validateNftIdentifier
Validate an NFT identifier. Accepts a mint_address, a contract:tokenId pair, or a numeric token ID. Rejects UUIDs.
export declare function validateNftIdentifier(id: unknown): string;validateOffset
Validate an offset.
export declare function validateOffset(offset: unknown): number;validateWalletAddress
Validate a wallet address. Returns the input unchanged on success.
export declare function validateWalletAddress(addr: unknown): string;Classes
AuthError
Server returned 401 / 403 — the request was refused. NOT only a key or origin problem: on v1 a 403 is also a held or blocked wallet, a banned asset, a missing key scope or a key class used on the wrong network. Branch on code, not on the class.
export declare class AuthError extends GraveMintError {
}ConfigError
Configuration error — bad apiKey, missing baseUrl, invalid options.
export declare class ConfigError extends GraveMintError {
}GraveMintError
GraveMint SDK error hierarchy.
All errors thrown by the SDK extend GraveMintError. Catch this base class to handle any SDK failure; catch a subclass to react to a specific failure mode (rate limit, auth, bad input, etc.).
export declare class GraveMintError extends Error {
readonly name: string;
readonly status?: number | undefined;
readonly code?: string | undefined;
readonly requestId?: string | undefined;
/**
* The server's parsed error body, when it sent JSON. This is where a code's
* documented extra fields live — e.g. `reason` / `blockedUntil` / `permanent` on
* `WALLET_BLOCKED`, `retryAfter` on `COLLECTION_RATE_LIMITED`.
* RAW and unsanitized (error bodies are not run through the response sanitizer), and
* its shape varies by route — read the documented field you need, do not forward it
* whole. Undefined for errors raised on the client.
*/
readonly details?: unknown;
constructor(message: string, opts?: {
status?: number;
code?: string;
requestId?: string;
details?: unknown;
cause?: unknown;
});
}HttpClient
export declare class HttpClient {
private readonly baseUrl;
private readonly apiKey;
private readonly timeoutMs;
private readonly maxRetries;
/** The client-wide timeout the CALLER set, or undefined when it is the default. */
readonly configuredTimeoutMs: number | undefined;
private readonly sanitize;
private readonly fetchImpl;
private readonly clientId;
private readonly limiter;
constructor(opts: HttpClientOptions);
get<T>(path: string, options?: RequestOptions): Promise<T>;
post<T>(path: string, body: unknown, options?: RequestOptions): Promise<T>;
private request;
}MintOutcomeUnknownError
mint() submitted signed transactions but could not confirm the outcome. This can follow a timeout, connection loss, server error or an already-processing response. The mint may still complete. Retain sessionIds, display the message and check the wallet before starting another mint. This SDK error uses code: "OUTCOME_UNKNOWN".
export declare class MintOutcomeUnknownError extends GraveMintError {
readonly sessionIds: string[];
constructor(sessionIds: string[], opts?: {
cause?: unknown;
requestId?: string;
});
}NetworkError
Network failure — DNS, TCP, TLS, fetch timeout.
export declare class NetworkError extends GraveMintError {
}NotFoundError
Server returned 404 — resource does not exist.
export declare class NotFoundError extends GraveMintError {
}RateLimitError
Server returned 429 — rate limit hit. retryAfterMs is the server-suggested wait.
export declare class RateLimitError extends GraveMintError {
readonly retryAfterMs: number;
constructor(message: string, retryAfterMs: number, opts?: {
status?: number;
code?: string;
requestId?: string;
details?: unknown;
});
}ServerError
Server returned 5xx after exhausting retries.
export declare class ServerError extends GraveMintError {
}TimeoutError
Request was aborted (client-side timeout or external AbortSignal).
export declare class TimeoutError extends GraveMintError {
}TokenBucketRateLimiter
export declare class TokenBucketRateLimiter {
private tokens;
private lastRefill;
private readonly refillRate;
private readonly capacity;
private readonly queue;
/** The ONE pending wake-up while anyone is queued. */
private timer;
constructor(config: RateLimitConfig);
/**
* Acquire one token. Resolves immediately if one is available and nobody is waiting, otherwise
* queues (first come, first served). Honors `signal` — aborting rejects the wait.
*
*: this used to arm one timer per waiter, and a woken waiter that found no token
* re-queued itself WITHOUT a timer — once the armed timers had fired, everyone still queued hung
* forever (12 concurrent calls at the defaults: 11 resolved). Now a single drain loop owns the
* queue: one timer while anyone waits, each tick hands out every available token in order, then
* re-arms for the next.
*/
acquire(signal?: AbortSignal): Promise<void>;
/** Arm the single wake-up for the time the next token needs, if anyone is waiting. */
private schedule;
/** Hand every available token to the queue in order, then re-arm for whoever is left. */
private drain;
private refill;
}ValidationError
Input validation error — bad address, malformed identifier, out-of-range limit.
export declare class ValidationError extends GraveMintError {
}Types
Blockchain
Public-facing types for the GraveMint SDK.
export type Blockchain = "solana-mainnet" | "solana-devnet" | "ethereum-mainnet" | "polygon-mainnet" | "base-mainnet" | "arbitrum-mainnet" | "optimism-mainnet" | "bsc-mainnet" | "avalanche-mainnet" | "apechain-mainnet" | "abstract-mainnet" | "cronos-mainnet" | "berachain-mainnet" | "sui-mainnet" | "sui-testnet" | string;BountyPrize
export interface BountyPrize {
kind: "milestone_group" | "individual";
name: string;
bounties: Array<{
mint_count?: number;
name: string;
nft?: {
mint_address: string | null;
name: string | null;
image_url: string | null;
};
token?: {
symbol: string;
amount: string | number;
decimals: number;
};
grant?: string;
status: "open" | "claimed" | "expired";
winner_wallet?: string | null;
claimed_at?: string | null;
}>;
}BountyPrizesResponse
export interface BountyPrizesResponse {
enabled: boolean;
show_occurrence?: boolean;
show_winners?: boolean;
sections?: BountyPrize[];
}BountySummary
export interface BountySummary {
enabled: boolean;
total_milestones?: number;
triggered?: number;
total_funded_usd?: number;
next_threshold?: number | null;
[key: string]: unknown;
}CodeValidationResult
/code/validate does NOT use the {success:true} envelope.
export interface CodeValidationResult {
valid: boolean;
/** Server-side reason string when valid=false. */
error?: string;
/** `external` and `nft_redemption` are real grant types the server returns. */
grantType?: "whitelist" | "free_mint" | "discount" | "external" | "nft_redemption";
/** Number of mints the code grants, when it grants any. */
mintAllocation?: number | null;
/** Percentage discount — the field the server actually sends. */
discountPercentage?: number | null;
/** Fixed-amount discount, in the drop's currency. */
discountFixed?: number | null;
usesRemaining?: number | null;
/**
* @deprecated The server has never sent this name — it sends `discountPercentage`.
* Always undefined; kept so existing code still compiles.
*/
discountPercent?: number;
collectionShortId?: string | null;
collectionAddress?: string | null;
[key: string]: unknown;
}CollectionDetail
export interface CollectionDetail {
/** Short, URL-safe public ID (e.g. "deads"). Preferred identifier. */
shortId: string;
name: string;
symbol: string | null;
bio: string | null;
description: string | null;
image: string | null;
imageFocalY: number;
bannerImage: string | null;
bannerFocalY: number;
externalUrl: string | null;
blockchain: Blockchain;
collectionAddress: string | null;
contractAddress: string | null;
nftStandard: "mpl-core" | "legacy" | "compressed" | "erc721" | "erc1155" | string;
contentType: "image" | "audio" | "video" | string;
enableGalleryMode: boolean;
allowNftSelection: boolean;
editionType: "unique" | "limited" | "limited_edition" | string;
maxEditionSupply: number | null;
allowImageDownload: boolean;
showTraitRarity: boolean;
twitter: string | null;
discord: string | null;
telegram: string | null;
creatorWallet: string | null;
twitterVerified: boolean;
discordVerified: boolean;
creatorVerified: boolean;
royaltyPercentage: number;
promoVideoUrl: string | null;
isGenerative: boolean;
enableDelayedReveal: boolean;
isRevealed: boolean;
roadmap: unknown;
affiliateEnabled: boolean;
mintReplaysEnabled: boolean;
[key: string]: unknown;
}CollectionDetailResponse
export interface CollectionDetailResponse {
collection: CollectionDetail;
stats: CollectionStats;
mintStatus: "upcoming" | "live" | "paused" | "ended" | "sold_out" | string;
countdown: unknown;
phases: {
active: Phase[];
upcoming: Phase[];
all: Phase[];
};
serverTime: string;
}CollectionIdentifier
export type CollectionIdentifier = string;CollectionPreLaunchResponse
Pre-launch / maintenance variants returned by the same endpoint when the collection is not yet fully deployed. The SDK type for getCollection unions these so callers can branch on status.
export interface CollectionPreLaunchResponse {
status: "coming_soon" | "deploying" | "maintenance";
message: string;
collection: {
shortId?: string;
name: string;
image: string | null;
blockchain: Blockchain;
description?: string | null;
totalSupply?: number;
mintedCount?: number;
};
/** present only when status === 'coming_soon' */
raffles?: Array<{
name: string;
status: string;
winnerCount: number;
registrationStart: string;
registrationEnd: string;
}>;
hasClaimCodes?: boolean;
hasWhitelistCodes?: boolean;
}CollectionResponse
export type CollectionResponse = CollectionDetailResponse | CollectionPreLaunchResponse;CollectionSearchResult
/collections/search returns snake_case unlike the camelCase feeds.
export interface CollectionSearchResult {
short_id: string;
name: string;
symbol: string | null;
image: string | null;
image_url: string | null;
collection_address: string | null;
contract_address: string | null;
blockchain_network: Blockchain;
total_supply: number;
minted_count: number;
}CollectionStats
export interface CollectionStats {
totalSupply: number;
mintedCount: number;
burnedCount: number;
availableCount: number | null;
percentMinted: number;
}CollectionSummary
Discovery-feed summary (camelCase). Used by /featured, /trending, /live-mints, /upcoming-mints, /recent-soldouts.
export interface CollectionSummary {
shortId: string;
name: string;
symbol: string | null;
bio?: string | null;
description?: string | null;
image: string | null;
imageCropPosition?: string;
imageFocalY?: number;
bannerImage?: string | null;
bannerFocalY?: number;
blockchain: Blockchain;
collectionAddress?: string | null;
contractAddress?: string | null;
isGenerative?: boolean;
promoVideoUrl?: string | null;
verified?: boolean;
socialVerified?: boolean;
stats: {
totalSupply: number;
mintedCount: number;
burnedCount?: number;
availableCount?: number;
percentMinted?: number;
totalVolume?: number;
floorPrice?: number;
mintPrice?: number;
mintCurrency?: string;
isSoldOut?: boolean;
};
badges?: {
hasBounties?: boolean;
hasAffiliates?: boolean;
};
trending?: {
rank?: number;
mints24h?: number;
mints7d?: number;
mints30d?: number;
volume24h?: number;
};
/** Present on /recent-soldouts */
deployedAt?: string;
firstMintedAt?: string;
effectiveSoldOutAt?: string;
soldOutAt?: string;
selloutSeconds?: number;
selloutLabel?: string;
/** Present on /upcoming-mints */
phase?: {
name: string;
type: string;
price: number;
currency: string;
paymentToken: unknown;
[k: string]: unknown;
};
}DiscoveryFilters
export interface DiscoveryFilters {
/** Limit results. Capped at 100. Default 20. */
limit?: number;
/** Filter by blockchain. Server ignores unknown values. */
blockchain?: Blockchain;
}GraveMintClientOptions
export interface GraveMintClientOptions {
/**
* API key issued by GraveMint. Sent on every request as `X-API-Key`.
* Required — even browser-side, the server enforces origin + key.
*/
apiKey: string;
/**
* Override the API base URL. Default: production.
* The public router is mounted at `/gravemint/public`; if you point at a
* different host, keep the same path suffix.
*/
baseUrl?: string;
/**
* Override the base URL for the versioned contract (`gm.v1`, `gm.mint`).
*
* You only need this when routing through your own proxy, or when `baseUrl`
* points somewhere whose shape we cannot infer. If you set `baseUrl` to a
* host ending in `/public`, this is derived for you by swapping the suffix.
* If it ends in anything else we do NOT guess — the constructor throws,
* because a wrong base on the mint path is a 404 discovered by a collector
* mid-transaction rather than by you at startup.
*/
v1BaseUrl?: string;
/** Per-request timeout (ms). Default 30_000. */
timeoutMs?: number;
/** Max retry attempts on 429 / 5xx / network. Default 3. */
maxRetries?: number;
/**
* Strip PII / internal UUIDs from responses. Default true.
* Setting `false` re-exposes internal identifiers — not recommended for
* production callers.
*/
sanitize?: boolean;
/**
* Client-side rate limit. Pass `null` to disable. Default 5 RPS sustained,
* 10 RPS burst (300/min) — below the server's published 600 reads/min/IP floor
* (README "Rate limits"). Mints have their own, lower per-key budget.
*/
rateLimit?: RateLimitConfig | null;
/**
* Custom fetch implementation. Defaults to `globalThis.fetch` (Node 18+,
* browsers, Bun, Deno). On older Node, pass `undici.fetch` or `node-fetch`.
*/
fetch?: typeof fetch;
}HttpClientOptions
export interface HttpClientOptions {
baseUrl: string;
apiKey: string;
/** Default timeout per request in milliseconds. Default 30s. */
timeoutMs?: number;
/** Max retry attempts on 429 / 5xx / network. Default 3. */
maxRetries?: number;
/** Sanitize responses (strip PII / UUIDs). Default true. */
sanitize?: boolean;
/** Client-side rate limit. Pass `null` to disable. Default {requestsPerSecond:5,burst:10}. */
rateLimit?: RateLimitConfig | null;
/** Custom fetch impl. Defaults to globalThis.fetch. */
fetch?: typeof fetch;
/** SDK version, sent as `x-gm-client`. Set by index.ts. */
sdkVersion?: string;
}MintResult
mint(): a single result for quantity 1, a batch outcome (with results) above that.
export type MintResult = V1ExecuteResult | V1MintBatchOutcome;NftIdentifier
export type NftIdentifier = string;Phase
export interface Phase {
name: string;
type: "public" | "whitelist" | "presale" | "dutch_auction" | "dynamic_dutch" | string;
status: "active" | "upcoming" | "ended";
price: number;
priceCurrency?: string | null;
paymentTokenSymbol?: string | null;
paymentTokenAddress?: string | null;
paymentTokenDecimals?: number | null;
startDate: string;
endDate: string | null;
maxPerWallet?: number | null;
maxPerTransaction?: number | null;
globalMintLimit?: number | null;
timeUntilStart?: number | null;
timeUntilEnd?: number | null;
[key: string]: unknown;
}PlatformCapabilities
export interface PlatformCapabilities {
solana?: {
mplCore: boolean;
legacy: boolean;
compressed: boolean;
};
evm?: {
erc721: boolean;
erc1155: boolean;
};
sui?: {
enabled: boolean;
mainnet: boolean;
testnet: boolean;
editions: boolean;
};
features?: Record<string, boolean>;
raffles?: {
twitterRequirements: boolean;
maxActiveRaffles: number;
};
enabledChains?: Record<string, boolean>;
[key: string]: unknown;
}PlatformSetting
export interface PlatformSetting {
setting_key: string;
setting_value: unknown;
is_public: boolean;
}PreparedMint
Response from /v1/prepare-mint. v1 returns transactions[] and sessions[], including for quantity one. Use sessions[0].sessionId for the first transaction. The optional top-level sessionId supports legacy proxy responses.
export interface PreparedMint {
success: boolean;
/** Legacy single-shape only. On v1 this is undefined — use `sessions[0]`. */
sessionId?: string;
sessions?: Array<{
sessionId: string;
nftIds: string[];
nftCount: number;
}>;
/** What was asked for. Compare with `totalQuantity`: see `quantityAdjusted`. */
requestedQuantity?: number;
/**
* TRUE when fewer NFTs were prepared than requested (a wallet or phase limit, or supply).
* Tell the collector — they are about to sign for fewer than they asked for.
*/
quantityAdjusted?: boolean;
/** Why, in a sentence, when the reason is a user-facing limit. */
adjustmentReason?: string | null;
/** Session lifetime in ms — use this for a countdown (clock-skew safe), not `expiresAt`. */
expiresInMs?: number;
transactions?: Array<{
transaction?: string | null;
isVersionedTransaction?: boolean;
nftCount?: number;
}>;
totalQuantity?: number;
/**
* The AUTHORITATIVE price breakdown. This — not `priceDisplay` — is what the
* collector actually pays. `priceDisplay` is for rendering the phase before a
* mint is requested; the real figure only exists once GraveMint has run the
* full cascade and reserved what the price depends on.
*/
pricing?: Record<string, unknown>;
bogo?: Record<string, unknown> | null;
/** The session expires. Do not hold a signature and submit it later. */
expiresAt?: string;
chainType?: string;
}PrepareMintInput
export interface PrepareMintInput {
/**
* The drop to mint from: its UUID (`drop.collection.id`), `shortId`, or on-chain address. A
* non-UUID is resolved within your key's own drops only.
*/
collectionId: string;
phaseId: string;
quantity: number;
walletAddress: string;
/** Referral attribution. Validated server-side; ignored if the drop has none. */
affiliateCode?: string | null;
/**
* The connected wallet's display name, e.g. `wallet.adapter.name` ("Phantom", "Solflare").
* Analytics only: it fills the creator's wallet breakdown, which otherwise reads "Unknown"
* for every mint from your site. Invalid values are dropped server-side.
*/
walletProvider?: string | null;
}PriceDisplay
The four display-price variants returned by GraveMint’s price resolver.
export type PriceDisplay = {
kind: "amount";
amount: number;
currency: string;
isFree: boolean;
approximate?: boolean;
} | {
kind: "range";
min: number;
max: number;
currency: string;
} | {
kind: "hidden";
} | {
kind: "unknown";
};RateLimitConfig
Client-side rate limiter (token bucket).
Why this exists: the server enforces its own limits (~80 reads/min/IP by default) and returns 429 on overage. Hitting 429s wastes round-trips and triggers bot-detection penalties. The SDK's client-side limiter keeps callers under a configurable ceiling and serializes overage requests into a queue rather than firing them off and getting blocked.
Configurable via GraveMintClientOptions.rateLimit: { requestsPerSecond: 5, burst: 10 } // 5 RPS sustained, 10 RPS short burst
Setting rateLimit: null disables the client-side limiter (server still enforces). Default: { requestsPerSecond: 5, burst: 10 }.
export interface RateLimitConfig {
/** Sustained request rate per second. */
requestsPerSecond: number;
/** Max burst — tokens accumulated when idle. */
burst: number;
}RecentMint
export interface RecentMint {
tokenId: number | null;
mintAddress: string | null;
name: string | null;
image: string | null;
previewImage: string | null;
fullImageUrl: string | null;
generationStatus: string | null;
ownerWallet: string | null;
mintedAt: string;
isRevealed: boolean;
attributes: Array<{
trait_type: string;
value: string | number;
}>;
editionNumber: number | null;
maxEditionSupply: number | null;
isMasterEdition: boolean;
currentEditionSupply?: number;
}RequestOptions
export interface RequestOptions {
/** Per-request timeout override (ms). */
timeoutMs?: number;
/**
* Per-request retry override. `execute` uses 0: re-POSTing signed transactions after an
* ambiguous failure makes the server answer ALREADY_PROCESSING / ALREADY_COMPLETED /
* SESSION_NOT_FOUND, which read as definite failures and hide that the first attempt
* may have landed.
*/
maxRetries?: number;
/** Per-request signal for cancellation. Combined with timeout signal. */
signal?: AbortSignal;
/** Override sanitization for this request only. */
sanitize?: boolean;
/** Query-string params (values stringified). Undefined/null entries are skipped. */
query?: Record<string, string | number | boolean | undefined | null>;
/** JSON body for POST. */
body?: unknown;
}TokenPricesResponse
export interface TokenPricesResponse {
/** Map of native-chain symbol → USD price. e.g. { SOL: 240, ETH: 0, POL: 0, SUI: 0 }. */
prices: Record<string, number>;
/** Per-symbol source (e.g. "coingecko", "jupiter", "unavailable"). */
sources: Record<string, string>;
/** ms epoch when the snapshot was taken server-side. */
timestamp: number;
/** Server-side cache TTL in seconds. */
cacheTTL: number;
}TransactionSigner
Anything that can sign a base64 transaction and hand back a base64 transaction.
Deliberately the narrowest possible surface: the less this SDK knows about your wallet, the fewer ways it can break when you change it.
export interface TransactionSigner {
signTransaction(base64Transaction: string): Promise<string>;
/**
* Optional. When present it is used for multi-transaction mints, so the
* collector approves ONCE instead of N times. `@solana/wallet-adapter`
* exposes this; a signer without it still works, one prompt per NFT.
*/
signAllTransactions?(base64Transactions: string[]): Promise<string[]>;
}V1AllMinted
allMinted() — one page. Keep requesting with offset until hasMore is false.
export interface V1AllMinted {
success: true;
nfts: V1MintedItem[];
/** Matching minted items in the whole drop. */
total: number;
hasMore: boolean;
offset: number;
/** The page size actually applied (the server caps it at 100). */
limit: number;
collection: {
id: string;
shortId: string | null;
name: string | null;
twitterHandle: string | null;
};
allowImageDownload: boolean;
}V1Availability
availability() — read fresh, the same counts as getCollection().stats. available is total - minted; minted includes burned (a burned NFT was minted). An NFT held by a mint still in progress counts as available until that mint completes, so available can briefly read high: use it for a progress bar, and let prepare decide whether supply remains. available is null for an open-ended generative drop, which has no fixed supply. percentMinted is 0-100.
export interface V1Availability {
success: true;
collectionId: string;
stats: {
total: number;
minted: number;
burned: number;
available: number | null;
percentMinted: number;
};
}V1BatchTransactionResult
One transaction's outcome inside a batch. success: false carries error (a sentence) and usually errorCode. pending: true means it may still land — check the wallet, do not retry. A success carries the minted NFTs under minted (an array here, unlike the single-transaction answer, where minted is a count).
export interface V1BatchTransactionResult {
sessionId: string;
success: boolean;
error?: string | null;
errorCode?: string;
pending?: boolean;
recovered?: boolean;
signature?: string;
mintedCount?: number;
minted?: V1MintedNft[];
[k: string]: unknown;
}V1BountyInventoryItem
export interface V1BountyInventoryItem {
id: string;
name: string | null;
image_url: string | null;
mint_address: string | null;
state: "unassigned" | "assigned" | "claimed";
claimed_winner?: string | null;
claimed_at?: string | null;
}V1BountyItem
One bounty in the prize table. Which of nft / grant / token is set depends on bounty_type.
export interface V1BountyItem {
id: string;
name: string | null;
description: string | null;
bounty_type: string | null;
is_claimed: boolean;
/** A shortened wallet, and only when the creator shows winners. */
claimed_winner: string | null;
claimed_at: string | null;
nft?: {
name: string | null;
image_url: string | null;
mint_address: string | null;
};
grant?: {
label: string;
};
/** `amount` is in the token's smallest unit; null when the creator left it unset. */
token?: {
symbol: string | null;
amount: number | null;
decimals: number;
};
trigger?: {
mint_count: number | null;
starts_at: string | null;
};
}V1BountyPoolSection
A recurring prize pool. weight_pct is the chance of each entry per draw.
export interface V1BountyPoolSection {
id: string;
name: string | null;
description: string | null;
mode: "recurring_pool";
recur_every_n_mints: number | null;
recur_max_fires: number | null;
recur_fires_count: number;
counter_starts_at: string | null;
entries: Array<{
reward_type: string;
weight_bps: number;
weight_pct: number;
payout_amount: number | null;
token_symbol: string | null;
token_decimals: number | null;
nft_pool_label: string | null;
display_label: string | null;
display_image_url: string | null;
}>;
}V1BountyPrizes
bountyPrizes(). enabled: false when the creator does not show the prize table.
export type V1BountyPrizes = {
success: true;
enabled: false;
} | {
success: true;
enabled: true;
show_occurrence: boolean;
show_winners: boolean;
show_inventory_preview: boolean;
sections: V1BountySection[];
pool_sections: V1BountyPoolSection[];
};V1BountySection
export type V1BountySection = {
kind: "milestone_group";
id: string;
name: string;
counter_starts_at?: string | null;
bounties: V1BountyItem[];
} | {
kind: "individual";
id?: string;
name: string;
bounties: V1BountyItem[];
} | {
kind: "inventory_preview";
id?: string;
name: string;
items: V1BountyInventoryItem[];
};V1BountySummary
bounty(). total_pool and pool_raw are in each token's smallest unit.
export interface V1BountySummary {
success: true;
remaining_bounties: number;
free_mint_bounties: number;
total_pool: number;
/** One symbol, `"tokens"` when the pool spans several, or null when there is none. */
token_symbol: string | null;
tokens: Array<{
address: string;
symbol: string | null;
decimals: number;
pool_raw: number;
}>;
has_bounties: boolean;
}V1Capabilities
Describes the features a collection requires and whether the core mint flow supports them. When supportedBySurface is false, offer the hosted mintUrl when available.
export interface V1Capabilities {
requiresFeatures: string[];
supportedBySurface: boolean;
mintUrl: string | null;
}V1ClaimCodeBenefits
claimCodeBenefits(). All zeros with source: null when the wallet has redeemed nothing. success is false (and everything zero) when the server could not look it up, so do not read zeros as "no benefits" without checking it.
export interface V1ClaimCodeBenefits {
success: boolean;
freeMints: number;
discountPercent: number;
discountFixed: number;
discountRemaining?: number;
packTierId?: string | null;
source: null | {
type: "free_mint";
batchName: string | null;
allocation: number | null;
used: number;
batchId?: string;
packTierId?: string | null;
} | {
type: "discount";
batchName: string | null;
percent?: number;
fixed?: number;
allocation: number;
used: number;
remaining: number;
batchId?: string;
};
}V1ClaimCodeCheck
validateClaimCode() — the v1 answer. A code for another drop answers { valid: false }.
export interface V1ClaimCodeCheck {
valid: boolean;
grantType?: "whitelist" | "free_mint" | "discount" | "external" | "nft_redemption";
mintAllocation?: number | null;
discountPercentage?: number | null;
discountFixed?: number | null;
usesRemaining?: number | null;
[k: string]: unknown;
}V1ClaimCodes
claimCodes().
export interface V1ClaimCodes {
hasClaimCodes: boolean;
hasDiscountCodes: boolean;
hasWhitelistCodes: boolean;
hasPackCodes: boolean;
}V1Collection
export interface V1Collection {
collection: {
id: string;
shortId: string | null;
name: string | null;
symbol: string | null;
description: string | null;
image: string | null;
bannerImage: string | null;
chain: string | null;
collectionAddress: string | null;
contractAddress: string | null;
externalUrl: string | null;
twitter: string | null;
discord: string | null;
isVerified: boolean;
};
stats: {
totalSupply: number;
mintedCount: number;
availableCount: number;
percentMinted: number;
};
/** `all` INCLUDES ended phases, for displaying the full schedule. */
phases: {
all: V1Phase[];
active: V1Phase[];
upcoming: V1Phase[];
};
capabilities: V1Capabilities;
/** GraveMint's clock. Render countdowns against this, never `Date.now()`. */
serverTime: string;
}V1DutchPrice
dutchPrice(). price is the live figure; the rest explains how it got there.
export interface V1DutchPrice {
success: true;
price: number;
basePrice: number;
surgePremium: number;
decayAmount: number;
isSurging: boolean;
mintsInWindow: number;
totalMints: number;
surgeThreshold: number;
floorPrice: number;
ceilingPrice: number;
surgeWindowMinutes: number;
startedAt: string | null;
lastMintAt: string | null;
/** The phase's Dutch-auction settings, as configured. */
config: Record<string, unknown>;
/** Milliseconds since the epoch. */
timestamp: number;
}V1Eligibility
export interface V1Eligibility {
gated: boolean;
meetsRequirements?: boolean;
onAllowlist?: boolean | null;
spotsAllocated?: number | null;
spotsRemaining?: number | null;
requirements?: Array<{
type: string;
met: boolean;
description: string;
}>;
/**
* `name` can be null at runtime for an unnamed phase; it stays typed `string` because
* narrowing a published type is a breaking change here. `startDate`/`endDate` are ISO
* strings or null (open-ended).
*/
phase: {
id: string;
name: string;
hasStarted: boolean;
hasEnded: boolean;
startDate?: string | null;
endDate?: string | null;
};
message?: string;
}V1ExecuteBatchResult
executeBatch(). ALWAYS answered with HTTP 200, even when every transaction failed, and success is true when ANY landed — read results[] and the counts, never success alone.
export interface V1ExecuteBatchResult {
success: boolean;
results: V1BatchTransactionResult[];
totalMinted: number;
successfulTransactions: number;
failedTransactions: number;
executionTimeMs: number;
/** Kept from the 1.2.x `Record<string, unknown>` type, so existing reads still compile. */
[k: string]: unknown;
}V1ExecuteResult
execute() — the server's answer for ONE signed transaction.
A failure is thrown, never returned. Fields other than the reveal settings and totalCost / currency can be absent: a session the server recovered after an interruption answers with the count only (minted) and no signature or nfts.
export interface V1ExecuteResult {
success: true;
/** How many NFTs this transaction minted (a COUNT — the NFTs themselves are in `nfts`). */
minted: number;
signature?: string;
/** Same value as `signature`. */
transactionHash?: string;
chainType: string;
nfts?: V1MintedNft[];
/** What the collector paid, in `currency`: mint price plus platform fee. */
totalCost: number;
currency: string;
collectionStats?: {
totalSupply: number;
mintedCount: number;
percentMinted: number;
isMintedOut: boolean;
} | null;
collectionUpdate?: {
itemsRedeemed: number;
isFrozen: boolean;
autoUnfrozen: boolean;
} | null;
delayedReveal?: boolean;
instantRevealAfterMint?: boolean;
timeDelayedReveal?: boolean;
revealDelayMinutes?: number | null;
revealAfter?: string | null;
revealAnimationUrl: string | null;
revealTransitionType: string;
revealAnimationTrigger: string;
revealTapCaption: string | null;
isPlatformPaid?: boolean;
bountyInfo?: {
directBounties: Array<Record<string, unknown>>;
} | null;
/** Never set on a single result — lets `if (res.results)` tell a batch apart. */
results?: undefined;
/**
* 1.2.x typed this result as `Record<string, unknown>`; the index signature keeps code that
* read other fields that way compiling within 1.x ( review).
*/
[k: string]: unknown;
}V1Gallery
gallery().
export interface V1Gallery {
success: true;
nfts: V1GalleryNft[];
total: number;
limit: number;
offset: number;
}V1GalleryNft
One NFT that can still be minted.
export interface V1GalleryNft {
id: string;
/** null for a limited-edition drop, whose editions share one artwork. */
token_id: number | null;
name: string | null;
description: string | null;
image_url: string | null;
preview_image_url: string | null;
cover_art_url: string | null;
animation_url: string | null;
/** Always null here: full audio is never public. Use `audio_preview_url`. */
audio_url: null;
audio_preview_url: string | null;
media_type: string | null;
preview_url: string | null;
preview_start_time: number | null;
preview_duration: number | null;
audio_duration_seconds: number | null;
mint_status: string | null;
/** The NFT's metadata attributes, passed through as stored. */
attributes: unknown;
is_master_edition: boolean;
current_edition_supply: number;
max_edition_supply: number | null;
edition_number: number | null;
master_nft_id: string | null;
/** Set when the NFT is reserved for one wallet. */
reservation_wallet: string | null;
reserved_by: string | null;
}V1LeaderboardEntry
One entry on a leaderboard. platformRank is the wallet's GraveLink rank name.
export interface V1LeaderboardEntry {
rank: number;
walletAddress: string;
displayName: string | null;
avatarUrl: string | null;
platformRank: string;
effects: V1ProfileEffect[];
}V1MintBatchOutcome
What mint() returns for a quantity above 1: the batch answer, annotated. When any transaction failed, success is false, partial says whether SOME landed, minted of requested is the tally, and message is a sentence safe to show the collector.
export interface V1MintBatchOutcome extends V1ExecuteBatchResult {
partial?: boolean;
requested?: number;
minted?: number;
message?: string;
}V1MintedItem
One NFT in a minted feed. Regular drops and limited editions carry different extras, so every field outside the common set is optional.
export interface V1MintedItem {
id: string;
/** null for a limited-edition drop — use `editionNumber` there. */
tokenId: number | null;
name: string | null;
image: string | null;
fullImageUrl: string | null;
mintAddress: string | null;
mintedAt: string | null;
isRevealed: boolean;
ownerWallet: string | null;
attributes: unknown[];
minterDisplayName: string | null;
minterAvatarUrl: string | null;
minterEffects: V1ProfileEffect[];
reactions?: Record<string, number>;
editionNumber?: number | null;
packTierName?: string | null;
rarityRank?: number | null;
rarityTier?: string | null;
rarityScore?: number | null;
bounty?: V1NftBounty | null;
isCrossChain?: boolean;
sourceChain?: string | null;
}V1MintedNft
One NFT a successful execute minted.
export interface V1MintedNft {
nftId?: string;
/** null for a limited-edition drop — use `editionNumber` there. */
tokenId: number | string | null;
name: string | null;
mintAddress?: string;
imageUrl: string | null;
animationUrl?: string | null;
mediaType: string;
previewImageUrl: string | null;
isRevealed: boolean;
revealAfter: string | null;
editionNumber?: number;
maxEditionSupply?: number;
rarity_score?: number | null;
rarity_rank?: number | null;
rarity_tier?: string | null;
pityTriggered?: boolean;
poolName?: string;
/** `amount` is in the token's smallest unit. */
bounty?: {
bountyType: string;
amount: number | null;
tokenSymbol: string | null;
payoutStatus: string;
} | null;
}V1NftBounty
A bounty attached to a minted NFT. amount is in the token's smallest unit.
export interface V1NftBounty {
bountyType: string;
amount: number | null;
tokenSymbol: string | null;
payoutStatus: string;
memo?: string | null;
}V1PeggedPrice
peggedPrice(). The quote expires at expiresAt; refresh it rather than reuse it.
export type V1PeggedPrice = (V1PeggedPriceBase & {
pegMode: "usd";
usdEquivalent: number;
}) | (V1PeggedPriceBase & {
pegMode: "native";
nativeEquivalent: number | null;
nativePrice: number;
});V1PeggedPriceBase
Fields shared by both peg modes of peggedPrice(). Times are milliseconds since the epoch.
export interface V1PeggedPriceBase {
success: true;
phaseId: string;
tokenAmount: number;
tokenSymbol: string | null;
tokenDecimals: number | null;
tokenPrice: number;
usdValue: number;
slippageTolerance: number;
source: string;
isStale: boolean;
quotedAt: number;
expiresAt: number;
validitySeconds: number;
}V1Phase
export interface V1Phase {
id: string;
name: string | null;
status: "active" | "upcoming" | "ended";
startDate: string | null;
endDate: string | null;
/** Resolved. See the file header — never recompute this. */
priceDisplay: PriceDisplay;
bogo: {
buy: number;
get: number;
} | null;
isGated: boolean;
maxPerWallet: number | null;
maxPerTransaction: number | null;
phaseSupply: number | null;
/** Derived from the SERVER clock, so a skewed device clock cannot mislead. */
msUntilStart: number | null;
msUntilEnd: number | null;
}V1ProfileEffect
A minter's cosmetic profile effect, as rendered next to their name.
export interface V1ProfileEffect {
effect_type: string | null;
effect_category: string | null;
config: unknown;
}V1RecentlyMinted
recentlyMinted(). total is the length of this feed, not the drop's supply.
export interface V1RecentlyMinted {
success: true;
nfts: V1RecentMint[];
total: number;
delayedRevealEnabled: boolean;
editionType: string;
allowImageDownload: boolean;
}V1RecentMint
One entry in the live "just minted" feed.
export interface V1RecentMint extends V1MintedItem {
masterNftId?: string | null;
previewImage: string | null;
maxEditionSupply: number | null;
isMasterEdition: boolean;
currentEditionSupply: number | null;
animationUrl: string | null;
mediaType: string | null;
isPatron: boolean;
customTag: string | null;
generationStatus?: string | null;
currentPlaceholderSequence?: number | null;
audioUrl?: string | null;
audioPreviewUrl?: string | null;
coverArtUrl?: string | null;
}V1TokenPrices
tokenPrices(). A price of 0 means unavailable — check sources[symbol].
export interface V1TokenPrices {
success: true;
prices: Record<string, number>;
sources: Record<string, string>;
/** Milliseconds since the epoch. */
timestamp: number;
cacheTTL: number;
}V1TopHolders
topHolders(). Up to 10. error is set (with an empty list) when on-chain holder data could not be read — the request itself still succeeds, so check it before rendering "no holders".
export interface V1TopHolders {
success: true;
topHolders: Array<V1LeaderboardEntry & {
holdCount: number;
}>;
/** Distinct holders found, not the drop's supply. */
total: number;
error?: string;
}V1TopMinters
topMinters(). Up to 10.
export interface V1TopMinters {
success: true;
topMinters: Array<V1LeaderboardEntry & {
mintCount: number;
}>;
total: number;
}V1Trait
export interface V1Trait {
traitType: string;
uniqueValues: number;
values: V1TraitValue[];
}V1Traits
traits(). Before a delayed reveal, traits is empty and message says why; totalNfts and methodology are only present once traits are public.
export interface V1Traits {
success: true;
traits: V1Trait[];
totalNfts?: number;
methodology?: string;
message?: string;
}V1TraitValue
export interface V1TraitValue {
value: string;
count: number;
minted: number;
available: number;
probability: number;
/** 0-100. */
percentage: number;
informationContent: number;
rarityScore: number;
}V1WalletMints
walletMints(). phaseMints is keyed by phase id. totalMinted counts every NFT this wallet minted from the drop, bonus mints included — it is not a sum of phaseMints.
export interface V1WalletMints {
success: true;
walletAddress: string;
collectionId: string;
phaseMints: Record<string, V1WalletPhaseMints>;
totalMinted: number;
}V1WalletPhaseMints
One phase in walletMints().
export interface V1WalletPhaseMints {
phaseName: string | null;
minted: number;
/** 0 means unlimited. */
maxPerWallet: number;
whitelistAllocation: number | null;
nftGateAllocation: number | null;
tokenGateAllocation: number | null;
allocationDetails: Array<{
type: "whitelist";
allocation: number;
} | {
type: "nft_gate";
ruleName: string;
multiplier: number;
collections: unknown;
} | {
type: "token_gate";
ruleName: string;
multiplier: number;
tokenAddress: unknown;
}>;
hasAllocationOverride: boolean | null;
/** null when the phase has no per-wallet limit. */
remaining: number | null;
canMintMore: boolean;
isPackPhase?: boolean;
}