# 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 {#the-one-rule}

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 {#quantity-above-1-is-a-batch}

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.

---
Source: https://docs.deads.io/guides/signing
Markdown: https://docs.deads.io/guides/signing.md
