Skip to content

Wallet Adapters

The Grapevine SDK uses a wallet adapter pattern to support multiple authentication methods. This allows the SDK to work seamlessly with both server-side private keys and browser-based wallet connections.

Overview

Adapters provide a common interface for wallet operations:

  • Getting the wallet address
  • Signing messages
  • Accessing the underlying wallet client

Available Adapters

AdapterUse CaseEnvironment
PrivateKeyAdapterServer-side, scripts, CLINode.js/Bun
WagmiAdapterReact apps with wagmiBrowser

WalletAdapter Interface

All adapters implement this interface:

interface WalletAdapter {
  // Get the wallet address
  getAddress(): string;
  
  // Sign a message with the wallet
  signMessage(message: string): Promise<string>;
  
  // Get the underlying Viem WalletClient
  getWalletClient(): WalletClient;
  
  // Get the chain ID
  getChainId(): string;
}

PrivateKeyAdapter

Used for server-side authentication with a private key.

Import

import { PrivateKeyAdapter } from '@pinata/grapevine-sdk';
// or
import { PrivateKeyAdapter } from '@pinata/grapevine-sdk/adapters';

Constructor

new PrivateKeyAdapter(privateKey: string, isTestnet: boolean)
ParameterTypeDescription
privateKeystringPrivate key (0x-prefixed, 66 chars)
isTestnetbooleantrue for Base Sepolia, false for Base

Usage

import { GrapevineClient, PrivateKeyAdapter } from '@pinata/grapevine-sdk';
 
// Direct usage (SDK handles internally)
const grapevine = new GrapevineClient({
  privateKey: process.env.PRIVATE_KEY,
  network: 'testnet'
});
 
// Manual adapter usage
const adapter = new PrivateKeyAdapter(process.env.PRIVATE_KEY!, true);
console.log('Address:', adapter.getAddress());
console.log('Chain ID:', adapter.getChainId()); // "84532" for testnet
 
const grapevine = new GrapevineClient({ network: 'testnet' });
grapevine.initializeAuthWithAdapter(adapter);

Features

  • Automatically creates a Viem wallet client
  • Handles raw message signing (required for Grapevine auth)
  • Supports Base and Base Sepolia networks

Error Handling

import { PrivateKeyAdapter, AuthError, ErrorCode } from '@pinata/grapevine-sdk';
 
try {
  const adapter = new PrivateKeyAdapter('invalid-key', true);
} catch (error) {
  if (error instanceof AuthError && error.code === ErrorCode.AUTH_INVALID_KEY) {
    console.error('Invalid private key format');
    console.log('Suggestion:', error.suggestion);
    console.log('Example:', error.example);
  }
}

WagmiAdapter

Used for browser-based wallet authentication with wagmi.

Import

import { WagmiAdapter } from '@pinata/grapevine-sdk';
// or
import { WagmiAdapter } from '@pinata/grapevine-sdk/adapters';

Constructor

new WagmiAdapter(walletClient: WalletClient)
ParameterTypeDescription
walletClientWalletClientViem wallet client from wagmi

Usage with React

import { useWalletClient } from 'wagmi';
import { GrapevineClient, WagmiAdapter } from '@pinata/grapevine-sdk';
 
function MyComponent() {
  const { data: walletClient } = useWalletClient();
  const [grapevine, setGrapevine] = useState<GrapevineClient | null>(null);
  
  useEffect(() => {
    if (walletClient) {
      const adapter = new WagmiAdapter(walletClient);
      const client = new GrapevineClient({ network: 'testnet' });
      client.initializeAuthWithAdapter(adapter);
      setGrapevine(client);
    }
  }, [walletClient]);
  
  // Use grapevine client...
}

Using the Hook (Recommended)

The SDK provides a hook that handles adapter creation automatically:

import { useGrapevine, useGrapevineWalletReady } from '@pinata/grapevine-sdk/react';
import { useWalletClient } from 'wagmi';
 
function MyComponent() {
  const { data: walletClient } = useWalletClient();
  
  // Hook handles adapter creation internally
  const grapevine = useGrapevine({
    walletClient,
    network: 'testnet'
  });
  
  const hasWallet = useGrapevineWalletReady(grapevine);
  
  if (!hasWallet) {
    return <div>Connect wallet to continue</div>;
  }
  
  // Use grapevine client...
}

Error Handling

import { WagmiAdapter, AuthError, ErrorCode } from '@pinata/grapevine-sdk';
 
try {
  const adapter = new WagmiAdapter(walletClient);
} catch (error) {
  if (error instanceof AuthError) {
    switch (error.code) {
      case ErrorCode.AUTH_INVALID_KEY:
        console.error('WalletClient is required');
        break;
      case ErrorCode.AUTH_NO_WALLET:
        console.error('Wallet not connected - no address available');
        break;
      case ErrorCode.CONFIG_INVALID:
        console.error('Chain ID not available - check wallet connection');
        break;
    }
  }
}

Creating Custom Adapters

You can create custom adapters for other wallet types:

import { WalletAdapter } from '@pinata/grapevine-sdk';
import type { WalletClient } from 'viem';
 
class MyCustomAdapter implements WalletAdapter {
  private address: string;
  private chainId: string;
  
  constructor(myWalletInstance: MyWalletType) {
    this.address = myWalletInstance.getAddress();
    this.chainId = myWalletInstance.getChainId().toString();
  }
  
  getAddress(): string {
    return this.address;
  }
  
  async signMessage(message: string): Promise<string> {
    // Implement signing logic for your wallet
    return await this.myWallet.sign(message);
  }
  
  getWalletClient(): WalletClient {
    // Return a Viem-compatible wallet client
    return this.myWallet.getViemClient();
  }
  
  getChainId(): string {
    return this.chainId;
  }
}
 
// Usage
const adapter = new MyCustomAdapter(myWallet);
const grapevine = new GrapevineClient({ network: 'testnet' });
grapevine.initializeAuthWithAdapter(adapter);

Dynamic Wallet Switching

The SDK supports dynamic wallet switching:

const grapevine = new GrapevineClient({ network: 'testnet' });
 
// Initial wallet
const adapter1 = new WagmiAdapter(walletClient1);
grapevine.setWalletClient(adapter1);
console.log('Wallet 1:', grapevine.getWalletAddress());
 
// Switch to different wallet
const adapter2 = new WagmiAdapter(walletClient2);
grapevine.setWalletClient(adapter2);
console.log('Wallet 2:', grapevine.getWalletAddress());
 
// Clear wallet (logout)
grapevine.clearWallet();
console.log('Has wallet:', grapevine.hasWallet()); // false

Supported Networks

NetworkChain IDisTestnet
Base8453false
Base Sepolia84532true

Best Practices

Server-Side (Node.js/Bun)

// ✅ Use environment variable
const grapevine = new GrapevineClient({
  privateKey: process.env.PRIVATE_KEY,
  network: process.env.NETWORK as 'testnet' | 'mainnet'
});
 
// ❌ Never hardcode private keys
const grapevine = new GrapevineClient({
  privateKey: '0x1234...', // NEVER do this!
  network: 'mainnet'
});

Client-Side (React)

// ✅ Use the provided hook
import { useGrapevine } from '@pinata/grapevine-sdk/react';
 
function App() {
  const { data: walletClient } = useWalletClient();
  const grapevine = useGrapevine({ walletClient, network: 'testnet' });
  // ...
}
 
// ✅ Handle wallet connection state
function CreateButton() {
  const { data: walletClient } = useWalletClient();
  const grapevine = useGrapevine({ walletClient, network: 'testnet' });
  const hasWallet = useGrapevineWalletReady(grapevine);
  
  if (!hasWallet) {
    return <button disabled>Connect Wallet First</button>;
  }
  
  return <button onClick={createFeed}>Create Feed</button>;
}

Related