This guide walks you through integrating your Spark wallet with the Dynamic SDK. By implementing the ISparkProvider interface, your wallet will be compatible with Dynamic’s Spark network support.

Overview

To integrate your Spark wallet with Dynamic, you’ll need to follow these steps: For general information about custom wallet connectors, see our Integrate your Wallet guide.
1

Implement the ISparkProvider Interface

Your wallet must implement the ISparkProvider interface. This interface defines all the methods Dynamic expects from a Spark wallet.

Required Methods

  • connect() - Establishes connection and returns the public key
  • disconnect() - Disconnects from the wallet
  • getAddress() - Retrieves the current wallet address
  • signMessage() - Signs messages for authentication
  • signMessageWithTaproot() - Signs messages using Taproot protocol
  • transferBitcoin() - Sends Bitcoin to other Spark addresses
  • transferTokens() - Transfers tokens between Spark addresses
  • request() - Handles generic RPC requests

Example Implementation

Your Wallet Provider Implementation
// This is what your wallet should implement
class YourWalletProvider implements ISparkProvider {
  async connect(): Promise<string> {
    // Your wallet's connection logic
    const publicKey = await this.connectToWallet();
    this.isConnected = true;
    return publicKey;
  }

  async disconnect(): Promise<void> {
    await this.disconnectFromWallet();
    this.isConnected = false;
  }

  async getAddress(): Promise<string> {
    return await this.getWalletAddress();
  }

  async signMessage(message: string | SparkSignMessageRequest): Promise<SparkSignatureResult> {
    const messageText = typeof message === 'string' ? message : message.message;
    const isTaproot = typeof message === 'object' ? message.isTaproot : false;
    
    if (isTaproot) {
      const signature = await this.signMessageWithTaproot(messageText);
      return { signature };
    } else {
      const signature = await this.signMessageRegular(messageText);
      return { signature };
    }
  }

  async signMessageWithTaproot(message: string): Promise<SparkSignatureResult> {
    const signature = await this.signMessageWithTaproot(message);
    return { signature };
  }

  async transferBitcoin(params: {
    receiverSparkAddress: string;
    amountSats: bigint;
    isTaproot?: boolean;
  }): Promise<string> {
    return await this.sendBitcoin({
      to: params.receiverSparkAddress,
      amount: params.amountSats,
      useTaproot: params.isTaproot || false
    });
  }

  async transferTokens(params: {
    tokenPublicKey: string;
    receiverSparkAddress: string;
    tokenAmount: number;
    isTaproot?: boolean;
  }): Promise<string> {
    return await this.sendToken({
      tokenPublicKey: params.tokenPublicKey,
      to: params.receiverSparkAddress,
      amount: params.tokenAmount,
      useTaproot: params.isTaproot || false
    });
  }

  async request(method: string, params?: unknown): Promise<unknown> {
    return await this.handleRequest(method, params);
  }

  // Your wallet's internal methods
  private async connectToWallet(): Promise<string> { /* ... */ }
  private async disconnectFromWallet(): Promise<void> { /* ... */ }
  private async getWalletAddress(): Promise<string> { /* ... */ }
  private async signMessageRegular(message: string): Promise<string> { /* ... */ }
  private async sendBitcoin(params: any): Promise<string> { /* ... */ }
  private async sendToken(params: any): Promise<string> { /* ... */ }
  private async handleRequest(method: string, params?: unknown): Promise<unknown> { /* ... */ }
}
2

Create a Custom Wallet Connector

Follow the process outlined in our Custom Wallet Connectors guide:
  1. Visit the Dynamic Public Wallet Connectors repository
  2. Follow the guidelines in the CONTRIBUTING.md file
  3. Create your connector package following the established patterns

Connector Structure

Your connector should extend SparkWalletConnector and implement the required overrides:
YourSparkConnector Implementation
import { SparkWalletConnector, type ISparkProvider } from '@dynamic-labs/spark';

export class YourSparkConnector extends SparkWalletConnector {
  /**
   * The name of the wallet connector
   * @override Required override from the base connector class
   */
  override name = 'Your Wallet Name';

  /**
   * Unique identifier for your wallet
   * @override Required override from the base connector class
   */
  override overrideKey = 'yourwallet';

  /**
   * Whether the wallet can connect via custodial service
   * @override Optional override
   */
  override canConnectViaCustodialService = false;

  /**
   * The constructor for the connector, with the relevant metadata
   * @param props The options for the connector
   */
  constructor(props: any) {
    super({
      ...props,
      metadata: {
        id: 'yourwallet',
        name: 'Your Wallet Name',
        icon: 'https://your-wallet.com/icon.png',
      },
    });
  }

  /**
   * Returns the wallet provider instance
   * @override Required override from the base connector class
   */
  public override getProvider(): ISparkProvider | undefined {
    return window.yourWalletProvider;
  }
}
3

Test Your Integration

Before submitting, thoroughly test your integration:
  1. Connection Testing - Verify wallet connects and disconnects properly
  2. Address Retrieval - Ensure addresses are returned correctly
  3. Message Signing - Test both regular and Taproot message signing
  4. Transaction Testing - Test Bitcoin and token transfers
  5. Error Handling - Verify proper error handling for all methods
4

Submit Your Connector

Once your implementation is complete and tested:
  1. Submit your connector following the Custom Wallet Connectors process
  2. Fill out the submission form to get allowlisted
  3. Our team will review and integrate your connector

Supported Networks

NetworkChain IDDescriptionBlock Explorer
Mainnet301Production networkmempool.space
Note: Currently only mainnet is supported. Testnet, signet, and regtest support may be added in future versions.

Reference: ISparkProvider Interface

The complete ISparkProvider interface specification:
ISparkProvider Interface
interface ISparkProvider {
  /** Whether the wallet is currently connected */
  isConnected: boolean;

  /** The current network chain ID (e.g., '301' for mainnet) */
  chainId?: string;

  /** The current network name (e.g., 'mainnet', 'testnet') */
  network?: string;

  /**
   * Establishes a connection to the wallet
   * @returns Promise resolving to the public key (string)
   */
  connect(): Promise<string>;

  /**
   * Disconnects from the wallet
   * @returns Promise that resolves when disconnected
   */
  disconnect(): Promise<void>;

  /**
   * Retrieves the current wallet address
   * @returns Promise resolving to address information
   */
  getAddress(): Promise<SparkAddressResult>;

  /**
   * Signs a message for authentication
   * @param message - Message to sign or signing request object
   * @returns Promise resolving to signature result
   */
  signMessage(
    message: string | SparkSignMessageRequest,
  ): Promise<SparkSignatureResult>;

  /**
   * Signs a message using Taproot protocol
   * @param message - Message to sign
   * @returns Promise resolving to signature result
   */
  signMessageWithTaproot(message: string): Promise<SparkSignatureResult>;

  /**
   * Transfers Bitcoin to another Spark address
   * @param params - Transfer parameters
   * @param params.receiverSparkAddress - The recipient's Spark address
   * @param params.amountSats - Amount to transfer in satoshis
   * @param params.isTaproot - Whether to use Taproot (defaults to false)
   * @returns Promise resolving to transaction hash
   */
  transferBitcoin(params: {
    receiverSparkAddress: string;
    amountSats: bigint;
    isTaproot?: boolean;
  }): Promise<string>;

  /**
   * Transfers tokens to another Spark address
   * @param params - Transfer parameters
   * @param params.tokenPublicKey - The token's public key
   * @param params.receiverSparkAddress - The recipient's Spark address
   * @param params.tokenAmount - Amount of tokens to transfer
   * @param params.isTaproot - Whether to use Taproot (defaults to false)
   * @returns Promise resolving to transaction hash
   */
  transferTokens(params: {
    tokenPublicKey: string;
    receiverSparkAddress: string;
    tokenAmount: number;
    isTaproot?: boolean;
  }): Promise<string>;

  /**
   * Generic RPC method for custom requests
   * @param method - The RPC method name
   * @param params - Optional parameters for the method
   * @returns Promise resolving to the method result
   */
  request(method: string, params?: unknown): Promise<unknown>;

  // Allow any additional properties for flexibility
  /** Additional properties that providers may implement */
  [key: string]: unknown;
}

Reference: Type Definitions

Spark Type Definitions
/**
 * Result from address retrieval, supporting different formats
 */
type SparkAddressResult = string;

/**
 * Message signing request, supporting both simple and advanced options
 */
type SparkSignMessageRequest =
  | {
      message: string;
      isTaproot?: boolean; // defaults to false
    }
  | string;

/**
 * Result from message signing, supporting different return formats
 */
type SparkSignatureResult =
  | {
      signature: string;
    }
  | string;