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
| Adapter | Use Case | Environment |
|---|---|---|
PrivateKeyAdapter | Server-side, scripts, CLI | Node.js/Bun |
WagmiAdapter | React apps with wagmi | Browser |
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)| Parameter | Type | Description |
|---|---|---|
privateKey | string | Private key (0x-prefixed, 66 chars) |
isTestnet | boolean | true 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)| Parameter | Type | Description |
|---|---|---|
walletClient | WalletClient | Viem 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()); // falseSupported Networks
| Network | Chain ID | isTestnet |
|---|---|---|
| Base | 8453 | false |
| Base Sepolia | 84532 | true |
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
- Configuration - Complete configuration options
- React Integration - React hooks for wagmi
- Error Handling - Authentication errors