UNPKG

@opendatalabs/vana-sdk

Version:

A TypeScript library for interacting with Vana Network smart contracts.

601 lines (474 loc) 17 kB
# Vana SDK The Vana SDK is a comprehensive TypeScript library for building applications on the Vana Network. It provides simple, powerful APIs for gasless data permissions, file management, and secure data portability. [![npm version](https://img.shields.io/npm/v/vana-sdk)](https://www.npmjs.com/package/vana-sdk) [![TypeScript](https://img.shields.io/badge/TypeScript-Ready-blue)](https://www.typescriptlang.org/) [![License: ISC](https://img.shields.io/badge/License-ISC-green.svg)](https://opensource.org/licenses/ISC) [Documentation](https://vana-com.github.io/vana-sdk) • [Examples](#examples) • [API Reference](https://vana-com.github.io/vana-sdk) ## Why Vana SDK? - **🔐 Gasless Permissions**: EIP-712 based permission system with zero gas fees for users - **📁 Data Management**: Query, upload, and manage encrypted user data files - **🔄 Flexible Relaying**: Callback-based relay system supporting any gasless transaction infrastructure - **📊 Schema Validation**: Built-in JSON Schema and SQLite schema validation with AJV - **🔧 Type-Safe**: Full TypeScript support with comprehensive type definitions - **🎯 Production Ready**: Battle-tested with comprehensive error handling and retry mechanisms ## Installation ```bash # npm npm install vana-sdk # yarn yarn add vana-sdk # pnpm pnpm add vana-sdk ``` **Peer Dependencies:** ```bash npm install viem@^2.31.7 ``` ## Quick Start ### Basic Setup ```typescript import { Vana } from "vana-sdk"; import { createWalletClient, http } from "viem"; import { privateKeyToAccount } from "viem/accounts"; import { mokshaTestnet } from "vana-sdk"; // Create wallet client const account = privateKeyToAccount("0x..."); const walletClient = createWalletClient({ account, chain: mokshaTestnet, transport: http("https://rpc.moksha.vana.org"), }); // Initialize Vana SDK const vana = new Vana({ walletClient, // Optional: configure gasless relayer relayerUrl: "https://relayer.moksha.vana.org", }); ``` ### Grant Data Permission (Gasless) ```typescript // Grant permission for an AI application to access user data const txHash = await vana.permissions.grant({ to: "0x742d35Cc6558Fd4D9e9E0E888F0462ef6919Bd36", // Application address operation: "llm_inference", parameters: { prompt: "Analyze my browsing data for insights", maxTokens: 1000, files: [12, 15, 28], // Specific file IDs model: "gpt-4", }, expiresAt: Math.floor(Date.now() / 1000) + 24 * 60 * 60, // 24 hours }); console.log("Permission granted:", txHash); ``` ### Query User Data Files ```typescript // Get all files owned by a user const files = await vana.data.getUserFiles({ user: "0x742d35Cc6558Fd4D9e9E0E888F0462ef6919Bd36", }); files.forEach((file) => { console.log(`File ${file.id}: ${file.url}`); console.log(`Schema: ${file.schemaId}, Added: ${file.addedAtBlock}`); }); ``` ### Upload Encrypted File ```typescript // Generate encryption key from wallet const encryptionKey = await generateEncryptionKey(walletClient); // Encrypt user data const userData = new Blob([ JSON.stringify({ browsing_history: [{ url: "https://example.com", timestamp: Date.now() }], }), ]); const encryptedData = await encryptUserData(userData, encryptionKey); // Upload to IPFS and register on-chain const result = await vana.data.uploadEncryptedFile({ data: encryptedData, schemaId: 123, // JSON schema for browsing data filename: "browsing-data.json", }); console.log("File uploaded:", result.fileId, result.url); ``` ## Architecture The Vana SDK follows a resource-oriented architecture with five main controllers: ### Core Controllers | Controller | Purpose | Key Methods | | ---------------------- | --------------------------------------- | ----------------------------------------------------------------- | | **`vana.permissions`** | Gasless permission grants & revocations | `grant()`, `revoke()`, `getUserPermissions()` | | **`vana.data`** | Data file management & encryption | `getUserFiles()`, `uploadEncryptedFile()`, `validateDataSchema()` | | **`vana.server`** | Trusted server management | `trustServer()`, `untrustServer()`, `processWithTrustedServer()` | | **`vana.protocol`** | Low-level contract access | `getContract()`, `getAvailableContracts()` | ### Configuration Options ```typescript const vana = new Vana({ // Required: Wallet client for signing walletClient, // Optional: Gasless transaction relay relayerUrl: "https://custom-relayer.com", // Optional: Custom callback-based relaying relayerCallbacks: { submitPermissionGrant: async (typedData, signature) => { // Custom relay implementation return await customRelayer.submit(typedData, signature); }, }, // Optional: Storage configuration storageManager: new StorageManager({ defaultProvider: "ipfs", providers: { ipfs: new PinataStorage({ apiKey: "...", secretKey: "..." }), }, }), // Optional: Subgraph for efficient data queries subgraphUrl: "https://api.thegraph.com/subgraphs/name/vana/moksha", }); ``` ## Core Concepts ### Gasless Permissions The Vana SDK enables applications to request data access permissions without users paying gas fees: ```typescript // Complete gasless permission flow const permission = await vana.permissions.grant({ to: applicationAddress, operation: "data_analysis", parameters: { // Structured parameters for the operation analysisType: "sentiment", outputFormat: "json", maxDataPoints: 1000, }, }); // The SDK handles: // 1. Parameter serialization & IPFS storage // 2. EIP-712 typed data creation // 3. User signature via wallet // 4. Relayer submission & gas payment // 5. On-chain permission registration ``` ### Data File Management Query and manage encrypted user data files using the subgraph for efficiency: ```typescript // Efficiently query user files (no contract scanning) const files = await vana.data.getUserFiles({ user: userAddress, // Optional: override subgraph URL subgraphUrl: "https://custom-subgraph.com/graphql", }); // Files are automatically deduplicated by ID // Latest timestamp wins for duplicate file IDs console.log(`Found ${files.length} unique files`); ``` ### Schema Validation Built-in support for validating data schemas and user data: ```typescript // Validate a data schema against Vana meta-schema const schema = { name: "Instagram Export", version: "1.0.0", dialect: "json", schema: { type: "object", properties: { posts: { type: "array" }, profile: { type: "object" }, }, }, }; vana.data.validateDataSchema(schema); // Validate user data against the schema const userData = { posts: [], profile: { username: "alice" } }; vana.data.validateDataAgainstSchema(userData, schema); ``` ### Flexible Relay System Configure gasless transactions using callbacks instead of fixed HTTP APIs: ```typescript const vana = new Vana({ walletClient, relayerCallbacks: { // Custom permission grant relaying submitPermissionGrant: async (typedData, signature) => { const response = await fetch("/api/relay/grant", { method: "POST", body: JSON.stringify({ typedData, signature }), }); const result = await response.json(); return result.transactionHash; }, // Custom revocation relaying submitPermissionRevoke: async (typedData, signature) => { return await myCustomRelayer.revoke(typedData, signature); }, }, }); ``` ## API Reference ### Permissions Controller ```typescript // Grant permission (gasless) await vana.permissions.grant({ to: Address, // Application address operation: string, // Operation type parameters: object, // Operation parameters expiresAt?: number // Optional expiration timestamp }): Promise<Hash> // Revoke permission (gasless) await vana.permissions.revoke({ grantId: string, // Grant ID to revoke nonce?: bigint // Optional nonce override }): Promise<Hash> // Query user permissions await vana.permissions.getUserPermissions({ user: Address, // User address subgraphUrl?: string // Optional subgraph override }): Promise<GrantedPermission[]> // Trust a server await vana.permissions.trustServer({ serverAddress: Address, // Server's address serverUrl: string // Server's URL }): Promise<Hash> ``` ### Data Controller ```typescript // Get user files await vana.data.getUserFiles({ user: Address, // File owner address subgraphUrl?: string // Optional subgraph override }): Promise<UserFile[]> // Upload encrypted file await vana.data.uploadEncryptedFile({ data: Blob, // Encrypted file data schemaId?: number, // Optional schema ID filename?: string // Optional filename }): Promise<UploadEncryptedFileResult> // Validate data schema vana.data.validateDataSchema( schema: unknown // Schema to validate ): asserts schema is DataSchema // Validate data against schema vana.data.validateDataAgainstSchema( data: unknown, // Data to validate schema: DataSchema // Data schema ): void // Fetch and validate remote schema await vana.data.fetchAndValidateSchema( url: string // Schema URL ): Promise<DataSchema> ``` ### Server Controller ```typescript // Process data with trusted server await vana.server.processWithTrustedServer({ serverUrl: string, // Trusted server URL operation: string, // Operation to perform parameters: object // Operation parameters }): Promise<any> // Check server trust status await vana.server.isServerTrusted({ user: Address, // User address serverAddress: Address // Server address }): Promise<boolean> ``` ### Protocol Controller ```typescript // Get contract information vana.protocol.getContract( contractName: VanaContract // Contract name ): ContractInfo // List available contracts vana.protocol.getAvailableContracts(): VanaContract[] // Get contract addresses for chain vana.protocol.getChainContracts( chainId: number // Chain ID ): Record<VanaContract, Address> ``` ## Storage Integration The SDK includes a powerful storage abstraction supporting multiple providers: ### IPFS Storage ```typescript import { StorageManager, PinataStorage } from "vana-sdk"; const storageManager = new StorageManager(); // Configure Pinata IPFS const pinataStorage = new PinataStorage({ apiKey: process.env.PINATA_API_KEY, secretKey: process.env.PINATA_SECRET_KEY, gatewayUrl: "https://gateway.pinata.cloud/ipfs", }); storageManager.register("ipfs", pinataStorage, true); // Default provider // Upload file const result = await storageManager.upload(encryptedBlob, "encrypted-data.bin"); ``` ### Google Drive Storage ```typescript import { StorageManager, GoogleDriveStorage } from "vana-sdk"; const googleDriveStorage = new GoogleDriveStorage({ accessToken: process.env.GOOGLE_DRIVE_ACCESS_TOKEN, refreshToken: process.env.GOOGLE_DRIVE_REFRESH_TOKEN, clientId: process.env.GOOGLE_DRIVE_CLIENT_ID, clientSecret: process.env.GOOGLE_DRIVE_CLIENT_SECRET, }); storageManager.register("google-drive", googleDriveStorage); ``` ### Custom Storage Provider ```typescript class CustomStorage implements StorageProvider { async upload(file: Blob, filename?: string): Promise<StorageUploadResult> { // Custom upload logic return { url: "custom://uploaded-file", size: file.size }; } async download(url: string): Promise<Blob> { // Custom download logic } // ... other required methods } storageManager.register("custom", new CustomStorage()); ``` ## Error Handling The SDK provides comprehensive error handling with specific error types: ```typescript import { RelayerError, UserRejectedRequestError, SchemaValidationError, InvalidConfigurationError, NetworkError, } from "vana-sdk"; try { await vana.permissions.grant(params); } catch (error) { if (error instanceof UserRejectedRequestError) { // User rejected the signature request console.log("User cancelled transaction"); } else if (error instanceof RelayerError) { // Relayer service error console.log(`Relayer error (${error.statusCode}): ${error.message}`); } else if (error instanceof SchemaValidationError) { // Schema validation failed console.log(`Schema error: ${error.message}`); } else if (error instanceof NetworkError) { // Network connectivity issue console.log(`Network error: ${error.message}`); } else { // Unexpected error console.error("Unexpected error:", error); } } ``` ## Supported Networks | Network | Chain ID | RPC URL | Explorer | | ------------------ | -------- | ----------------------------- | ------------------------------------------------ | | **Vana Mainnet** | `1480` | `https://rpc.vana.org` | [vanascan.io](https://vanascan.io) | | **Moksha Testnet** | `14800` | `https://rpc.moksha.vana.org` | [moksha.vanascan.io](https://moksha.vanascan.io) | ### Adding Networks to Wallet **Moksha Testnet:** ``` Network Name: Vana Moksha Testnet RPC URL: https://rpc.moksha.vana.org Chain ID: 14800 Currency Symbol: VANA Block Explorer: https://moksha.vanascan.io ``` ## Examples ### Complete Permission Flow ```typescript import { Vana, generateEncryptionKey, encryptUserData } from "vana-sdk"; async function completePermissionFlow() { // 1. Initialize SDK const vana = new Vana({ walletClient }); // 2. Encrypt user data const encryptionKey = await generateEncryptionKey(walletClient); const userData = new Blob([JSON.stringify({ data: "sensitive info" })]); const encryptedData = await encryptUserData(userData, encryptionKey); // 3. Upload encrypted file const uploadResult = await vana.data.uploadEncryptedFile({ data: encryptedData, schemaId: 123, filename: "user-data.json", }); // 4. Grant permission to access the file const permissionTx = await vana.permissions.grant({ to: "0x742d35Cc6558Fd4D9e9E0E888F0462ef6919Bd36", operation: "ai_training", parameters: { files: [uploadResult.fileId], model: "llm-v1", maxTokens: 500, }, }); console.log("Permission granted:", permissionTx); } ``` ### Schema Validation Example ```typescript // Define a data schema const instagramSchema = { name: "Instagram Export", version: "1.0.0", description: "User's Instagram profile and posts data", dialect: "json", schema: { type: "object", properties: { profile: { type: "object", properties: { username: { type: "string" }, followers: { type: "number" }, verified: { type: "boolean" }, }, required: ["username"], }, posts: { type: "array", items: { type: "object", properties: { id: { type: "string" }, likes: { type: "number" }, caption: { type: "string" }, }, }, }, }, required: ["profile"], }, }; // Validate the schema vana.data.validateDataSchema(instagramSchema); // Validate user data against the schema const userData = { profile: { username: "alice_smith", followers: 1500, verified: false }, posts: [{ id: "post_123", likes: 42, caption: "Beautiful sunset! 🌅" }], }; vana.data.validateDataAgainstSchema(userData, instagramSchema); ``` ## Contributing We welcome contributions to the Vana SDK! Please see our [Contributing Guide](../../CONTRIBUTING.md) for details. ### Development Setup ```bash git clone https://github.com/vana-com/vana-sdk.git cd vana-sdk npm install npm run build npm test ``` ### Running Examples ```bash cd examples/vana-sdk-demo npm install npm run dev ``` ## Documentation - [📚 API Documentation](https://vana-com.github.io/vana-sdk) - Complete TypeDoc API reference - [🚀 Getting Started Guide](https://docs.vana.org/vana-sdk) - Step-by-step setup - [🏗️ Architecture Guide](https://docs.vana.org/vana-sdk/architecture) - SDK design and patterns - [🔧 Configuration Guide](https://docs.vana.org/vana-sdk/configuration) - All configuration options - [🔒 Security Guide](https://docs.vana.org/vana-sdk/security) - Best practices and security ## Support - **📖 Documentation**: [API Reference](https://vana-com.github.io/vana-sdk) - **💬 Discord**: [Join our community](https://discord.gg/vanabuilders) - **🐛 Issues**: [GitHub Issues](https://github.com/vana-com/vana-sdk/issues) - **📧 Email**: [support@vana.org](mailto:support@vana.org) ## License [ISC License](LICENSE) © Vana Foundation