Skip to content

GraveMint SDK — method reference ​

@solanadeads/gravemint · version 1.2.1 · install with npm i @solanadeads/gravemint

Every public method in this version, with its signature and types. For how to put them together, start with the GraveMint SDK guide.

ts
import { GraveMintClient } from "@solanadeads/gravemint";
const client = new GraveMintClient({ apiKey: "gm_pub_..." });

Contents ​

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.

ts
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.

ts
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: [...]}

ts
getFeatured(filters?: DiscoveryFilters, options?: RequestOptions): Promise<CollectionSummary[]>;

discovery.getLiveMints() ​

GET /live-mints — {success, mints: [...]}

ts
getLiveMints(filters?: DiscoveryFilters, options?: RequestOptions): Promise<CollectionSummary[]>;

discovery.getTrending() ​

GET /trending — {success, type, collections: [...]}

ts
getTrending(filters?: DiscoveryFilters, options?: RequestOptions): Promise<CollectionSummary[]>;

discovery.getUpcoming() ​

GET /upcoming-mints — {success, mints: [...]}

ts
getUpcoming(filters?: DiscoveryFilters, options?: RequestOptions): Promise<CollectionSummary[]>;

discovery.getRecentSoldouts() ​

GET /recent-soldouts — {collections: [...]} (no success wrapper)

ts
getRecentSoldouts(filters?: DiscoveryFilters, options?: RequestOptions): Promise<CollectionSummary[]>;

discovery.listCollections() ​

GET /collections — {success, collections: [...]}

ts
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.

ts
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.

ts
get(identifier: CollectionIdentifier, options?: RequestOptions): Promise<CollectionResponse>;

collections.getGallery() ​

GET /collection/:identifier/gallery

Paginated list of NFTs in the collection.

ts
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).

ts
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.

ts
get(identifier: NftIdentifier, options?: RequestOptions): Promise<RecentMint>;

client.bounty ​

First-party only — not usable with a partner key. See Legacy read API.

bounty.getSummary() ​

ts
getSummary(identifier: CollectionIdentifier, options?: RequestOptions): Promise<BountySummary>;

bounty.getPrizes() ​

ts
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, ...}.

ts
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.

ts
collection(identifier: string): Promise<V1Collection>;

v1.eligibility() ​

Can this wallet mint in this phase, and for how many?

Server-evaluated by necessity: the dominant gate is an NFT holding check, which needs an on-chain lookup, and an allowlist must never be published to a client. Note the verdict deliberately EXCLUDES the time window — it answers "will this wallet qualify when the phase opens", with phase.hasStarted / hasEnded reported separately, so an upcoming phase does not read as "not eligible".

ts
eligibility(collectionId: string, phaseId: string, walletAddress: string): Promise<V1Eligibility>;

The drop's own art, paginated.

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.

ts
gallery(identifier: string, params?: {
        limit?: number;
        offset?: number;
    }): Promise<unknown>;

v1.recentlyMinted() ​

The live "just minted" feed for the drop — the same one gravemint.io renders.

ts
recentlyMinted(identifier: string, params?: {
        limit?: number;
    }): Promise<unknown>;

v1.bounty() ​

Bounty summary for the drop, when it has one.

ts
bounty(identifier: string): Promise<unknown>;

v1.bountyPrizes() ​

The bounty's public prize table.

ts
bountyPrizes(identifier: string): Promise<unknown>;

v1.walletMints() ​

How many this wallet has already minted, and how many remain — per phase.

🚨 Do NOT derive this from eligibility(). The two count differently on purpose: this endpoint INCLUDES bonus mints (BOGO, bounty) while the eligibility engine excludes them, so for any wallet that took one, a figure you compute yourself will be wrong in the direction that lets them try to over-mint and get refused at prepare.

ts
walletMints(collectionId: string, walletAddress: string): Promise<unknown>;

v1.claimCodes() ​

Does this drop use claim codes, and of what kind?

ts
claimCodes(collectionId: string): Promise<unknown>;

v1.claimCodeBenefits() ​

What a specific wallet is already entitled to from codes it has redeemed.

ts
claimCodeBenefits(collectionId: string, walletAddress: string, params?: {
        phaseId?: string;
        blockchain?: string;
        packTierId?: string;
    }): Promise<unknown>;

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.

ts
validateClaimCode(collectionId: string, code: string): Promise<{
        valid: boolean;
        [k: string]: unknown;
    }>;

v1.tokenPrices() ​

Platform token prices — what an SPL-priced drop is worth in USD. Global, no drop.

ts
tokenPrices(): Promise<unknown>;

v1.peggedPrice() ​

The resolved amount for a USD- or native-pegged phase.

⚠️ phase.price on the collection response is Dutch-resolved but NOT peg-resolved, so on a pegged phase it is a stale cached token amount. Use this, never that.

ts
peggedPrice(phaseId: string): Promise<unknown>;

v1.dutchPrice() ​

The live price of a dynamic Dutch phase, which moves with time.

ts
dutchPrice(phaseId: string): Promise<unknown>;

v1.availability() ​

How much is actually mintable right now, accounting for held reservations.

ts
availability(collectionId: string): Promise<unknown>;

v1.traits() ​

Trait names and values, for filtering a gallery.

ts
traits(identifier: string): Promise<unknown>;

v1.allMinted() ​

Every minted item in the drop.

ts
allMinted(identifier: string): Promise<unknown>;

v1.topHolders() ​

Largest holders — social proof for a mint page.

ts
topHolders(identifier: string): Promise<unknown>;

v1.topMinters() ​

Who minted the most.

ts
topMinters(identifier: string): Promise<unknown>;

v1.version() ​

What the API is; useful for degrading deliberately rather than on a 404.

ts
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.

ts
prepare(input: PrepareMintInput): Promise<PreparedMint>;

mint.execute() ​

Hand the signed transaction back so GraveMint broadcasts it.

ts
execute(input: {
        sessionId: string;
        signedTransaction: string;
    }): Promise<Record<string, unknown>>;

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.

ts
executeBatch(entries: Array<{
        sessionId: string;
        signedTransaction: string;
    }>): Promise<Record<string, unknown>>;

mint.mint() ​

prepare → sign → execute, in one call.

Throws when the prepared response carries no transaction to sign. That is not a failure so much as a drop this SDK cannot complete in-app — an EVM gasless mint has nothing for a wallet to sign, and an XRPL drop is custodial. Check capabilities.supportedBySurface first and send the collector to mintUrl rather than discovering it here.

ts
mint(input: PrepareMintInput & {
        signer: TransactionSigner;
    }): Promise<Record<string, unknown>>;

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.

ts
export declare function fromHttpStatus(status: number, message: string, body: unknown, requestId?: string): GraveMintError;

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.

ts
export declare function isUuid(s: string): boolean;

looksLikeAddress ​

Returns true if the string looks like a valid on-chain address (any chain).

ts
export declare function looksLikeAddress(s: string): boolean;

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 id at 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).

ts
export declare function sanitize<T = unknown>(value: T): T;

validateClaimCode ​

Validate a claim code (format-level only, not existence).

ts
export declare function validateClaimCode(code: unknown): string;

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.

ts
export declare function validateCollectionIdentifier(id: unknown): string;

validateLimit ​

Validate a pagination limit against a max. Returns the clamped value.

ts
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.

ts
export declare function validateNftIdentifier(id: unknown): string;

validateOffset ​

Validate an offset.

ts
export declare function validateOffset(offset: unknown): number;

validateWalletAddress ​

Validate a wallet address. Returns the input unchanged on success.

ts
export declare function validateWalletAddress(addr: unknown): string;

Classes ​

AuthError ​

Server returned 401 / 403 — API key missing, invalid, or origin not allowed.

ts
export declare class AuthError extends GraveMintError {
}

ConfigError ​

Configuration error — bad apiKey, missing baseUrl, invalid options.

ts
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.).

ts
export declare class GraveMintError extends Error {
    readonly name: string;
    readonly status?: number | undefined;
    readonly code?: string | undefined;
    readonly requestId?: string | undefined;
    constructor(message: string, opts?: {
        status?: number;
        code?: string;
        requestId?: string;
        cause?: unknown;
    });
}

HttpClient ​

ts
export declare class HttpClient {
    private readonly baseUrl;
    private readonly apiKey;
    private readonly timeoutMs;
    private readonly maxRetries;
    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;
}

NetworkError ​

Network failure — DNS, TCP, TLS, fetch timeout.

ts
export declare class NetworkError extends GraveMintError {
}

NotFoundError ​

Server returned 404 — resource does not exist.

ts
export declare class NotFoundError extends GraveMintError {
}

RateLimitError ​

Server returned 429 — rate limit hit. retryAfterMs is the server-suggested wait.

ts
export declare class RateLimitError extends GraveMintError {
    readonly retryAfterMs: number;
    constructor(message: string, retryAfterMs: number, opts?: {
        status?: number;
        code?: string;
        requestId?: string;
    });
}

ServerError ​

Server returned 5xx after exhausting retries.

ts
export declare class ServerError extends GraveMintError {
}

TimeoutError ​

Request was aborted (client-side timeout or external AbortSignal).

ts
export declare class TimeoutError extends GraveMintError {
}

TokenBucketRateLimiter ​

ts
export declare class TokenBucketRateLimiter {
    private tokens;
    private lastRefill;
    private readonly refillRate;
    private readonly capacity;
    private readonly queue;
    constructor(config: RateLimitConfig);
    /**
     * Acquire one token. Resolves immediately if available, otherwise waits.
     * Honors `signal` if provided — aborting the signal rejects the wait.
     */
    acquire(signal?: AbortSignal): Promise<void>;
    private refill;
}

ValidationError ​

Input validation error — bad address, malformed identifier, out-of-range limit.

ts
export declare class ValidationError extends GraveMintError {
}

Types ​

Blockchain ​

Public-facing types for the GraveMint SDK.

ts
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 ​

ts
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 ​

ts
export interface BountyPrizesResponse {
    enabled: boolean;
    show_occurrence?: boolean;
    show_winners?: boolean;
    sections?: BountyPrize[];
}

BountySummary ​

ts
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.

ts
export interface CodeValidationResult {
    valid: boolean;
    /** Server-side reason string when valid=false. */
    error?: string;
    grantType?: "whitelist" | "free_mint" | "discount";
    discountPercent?: number;
    collectionShortId?: string | null;
    collectionAddress?: string | null;
    [key: string]: unknown;
}

CollectionDetail ​

ts
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 ​

ts
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 ​

ts
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.

ts
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 ​

ts
export type CollectionResponse = CollectionDetailResponse | CollectionPreLaunchResponse;

CollectionSearchResult ​

/collections/search returns snake_case unlike the camelCase feeds.

ts
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 ​

ts
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.

ts
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 ​

ts
export interface DiscoveryFilters {
    /** Limit results. Capped at 100. Default 20. */
    limit?: number;
    /** Filter by blockchain. Server ignores unknown values. */
    blockchain?: Blockchain;
}

GraveMintClientOptions ​

ts
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 — comfortably below the server's ~80 reads/min/IP ceiling.
     */
    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 ​

ts
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;
}

NftIdentifier ​

ts
export type NftIdentifier = string;

Phase ​

ts
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 ​

ts
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 ​

ts
export interface PlatformSetting {
    setting_key: string;
    setting_value: unknown;
    is_public: boolean;
}

PreparedMint ​

The response from /v1/prepare-mint.

🚨 v1 always returns the BATCH shape — transactions[] and sessions[] — even for a quantity of 1. The first-party /gravemint/mint/prepare route rewrites single mints into a flatter legacy shape carrying a top-level sessionId, but that rewrite lives in that route's wrapper, and v1 calls prepareMintHandler directly. Read sessions[0].sessionId.

sessionId stays declared and optional so a host proxying through the legacy route still type-checks; prefer the array.

ts
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;
    }>;
    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 ​

ts
export interface PrepareMintInput {
    collectionId: string;
    phaseId: string;
    quantity: number;
    walletAddress: string;
    /** Referral attribution. Validated server-side; ignored if the drop has none. */
    affiliateCode?: string | null;
}

PriceDisplay ​

The four honest shapes GraveMint's price resolver returns.

ts
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 }.

ts
export interface RateLimitConfig {
    /** Sustained request rate per second. */
    requestsPerSecond: number;
    /** Max burst — tokens accumulated when idle. */
    burst: number;
}

RecentMint ​

ts
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 ​

ts
export interface RequestOptions {
    /** Per-request timeout override (ms). */
    timeoutMs?: 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 ​

ts
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.

ts
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[]>;
}

V1Capabilities ​

What a drop NEEDS, and whether a core-mint client can render it faithfully.

When supportedBySurface is false, show mintUrl rather than a panel that silently omits the mechanic the drop is built around. This is the protection that additive-only versioning cannot give you: a pinned client stays CORRECT as new mechanics ship, instead of quietly rendering a partial deal.

ts
export interface V1Capabilities {
    requiresFeatures: string[];
    supportedBySurface: boolean;
    mintUrl: string | null;
}

V1Collection ​

ts
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, so a schedule can be rendered honestly. */
    phases: {
        all: V1Phase[];
        active: V1Phase[];
        upcoming: V1Phase[];
    };
    capabilities: V1Capabilities;
    /** GraveMint's clock. Render countdowns against this, never `Date.now()`. */
    serverTime: string;
}

V1Eligibility ​

ts
export interface V1Eligibility {
    gated: boolean;
    meetsRequirements?: boolean;
    onAllowlist?: boolean | null;
    spotsAllocated?: number | null;
    spotsRemaining?: number | null;
    requirements?: Array<{
        type: string;
        met: boolean;
        description: string;
    }>;
    phase: {
        id: string;
        name: string;
        hasStarted: boolean;
        hasEnded: boolean;
    };
    message?: string;
}

V1Phase ​

ts
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;
}