@opendatalabs/vana-sdk
Version:
A TypeScript library for interacting with Vana Network smart contracts.
601 lines (474 loc) • 17 kB
Markdown
# 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.
[](https://www.npmjs.com/package/vana-sdk)
[](https://www.typescriptlang.org/)
[](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