Skip to content
IN THIS GUIDE

Wallets and signing ​

GraveMint accepts a small signer interface, so you can use your application's existing wallet library. The SDK itself does not require a chain SDK.

Transaction submission ​

Your wallet signs the transaction returned by prepare. Send those signed bytes to execute; GraveMint validates, submits and confirms the transaction.

Keep submission in this flow. A transactionHash is rejected with CLIENT_BROADCAST_NOT_ALLOWED. Signed bytes that change the prepared transaction's instructions or accounts are rejected with TX_MODIFIED.

The interface ​

ts
import type { TransactionSigner } from '@solanadeads/gravemint';

// TransactionSigner accepts and returns base64-encoded transactions.
// signTransaction(base64Transaction: string): Promise<string>
// signAllTransactions?(base64Transactions: string[]): Promise<string[]>

signAllTransactions is optional. When provided, the SDK can request approval for a batch together. Otherwise, it signs each transaction separately.

Solana wallet-adapter ​

Use this adapter inside a component or hook with a connected wallet. It supports versioned and legacy transactions. Browser builds also need a Buffer implementation, such as the buffer package.

ts
import { Buffer } from 'buffer';
import { Transaction, VersionedTransaction } from '@solana/web3.js';
import { useWallet } from '@solana/wallet-adapter-react';
import type { TransactionSigner } from '@solanadeads/gravemint';

export function useMintSigner(): TransactionSigner {
  const { signTransaction } = useWallet();
  return {
    async signTransaction(base64) {
      if (!signTransaction) throw new Error('Connect a wallet that supports signing.');
      const bytes = Buffer.from(base64, 'base64');
      let transaction: Transaction | VersionedTransaction;
      try {
        transaction = VersionedTransaction.deserialize(bytes);
      } catch {
        transaction = Transaction.from(bytes);
      }
      const signed = await signTransaction(transaction);
      const serialized = signed instanceof Transaction
        ? signed.serialize({ requireAllSignatures: false })
        : signed.serialize();
      return Buffer.from(serialized).toString('base64');
    },
  };
}

Privy and embedded wallets ​

Adapt your provider's Solana signing method to the same base64 input/output interface. Use a sign-only method; the provider must return the signed transaction for GraveMint to submit.

Batch mints ​

A quantity above one uses several transactions and sessions. The SDK's mint() method coordinates them and returns per-transaction results. A partial batch can return without throwing, so inspect the results before reporting completion.

ts
const result = await gm.mint.mint({ collectionId, phaseId, walletAddress, quantity, signer });
if (result.results) {
  for (const transaction of result.results) {
    if (transaction.pending) {
      // Keep transaction.sessionId and check the wallet before retrying.
    } else if (!transaction.success) {
      // Handle transaction.errorCode and display transaction.error.
    }
  }
}

For direct executeBatch() calls, success: true means at least one transaction succeeded. Read results, successfulTransactions and failedTransactions to determine the full outcome.

The full flow ​

For a single mint, the manual flow is:

ts
const prepared = await gm.mint.prepare({ collectionId, phaseId, walletAddress, quantity: 1 });
const transaction = prepared.transactions?.[0]?.transaction;
const sessionId = prepared.sessions?.[0]?.sessionId;
if (!transaction || !sessionId) throw new Error('No transaction was prepared.');

const signedTransaction = await signer.signTransaction(transaction);
const result = await gm.mint.execute({ sessionId, signedTransaction });

Preparation returns arrays even for a quantity of one. For larger quantities, sign each transaction and pair it with its corresponding session for executeBatch(), or use mint():

ts
const result = await gm.mint.mint({ collectionId, phaseId, walletAddress, quantity: 1, signer });

Sessions expire ​

Preparation reserves supply with a short-lived lock. Call it when the collector is ready to mint, rather than to obtain a price for page display. Use the prepared response's expiresInMs for the signing countdown.

A confirmed SESSION_EXPIRED response requires a new preparation. Keep expiry separate from an uncertain submission outcome: MintOutcomeUnknownError (OUTCOME_UNKNOWN) includes sessionIds for tracking a mint that may still complete. Retain those IDs, check the wallet and do not automatically mint again. Execute calls are not retried automatically by default.

Chains ​

The v1 mint flow supports Solana. CHAIN_NOT_SUPPORTED_BY_SURFACE identifies a collection outside that flow; CHAIN_DISABLED identifies a disabled chain. Use capabilities.supportedBySurface before preparation and offer capabilities.mintUrl when available.