UNPKG

@keypo/synapse-storage-sdk

Version:
577 lines (454 loc) 16.9 kB
# Synapse Storage SDK A TypeScript SDK for encrypted file storage on Filecoin via Synapse, featuring Lit Protocol encryption, NFT-based access control, and ZeroDev account abstraction. ## Features - 🔐 **End-to-end encryption** with Lit Protocol v8 - 📁 **Filecoin storage** via Synapse network - 🎫 **NFT-based access control** with smart contracts - 🌍 **Public/private file modes** with granular permissions - 👥 **File sharing** by minting access NFTs - 🗑️ **File deletion** from permissions registry - 📋 **File listing** with metadata and filtering - 💰 **Payment management** for USDFC storage tokens - 🦺 **Type-safe** TypeScript implementation - ⚡ **Account abstraction** via ZeroDev for gasless transactions ## Installation ```bash npm install @keypo/synapse-storage-sdk ``` ## Peer Dependencies ```bash npm install @filoz/synapse-sdk ethers viem @zerodev/sdk @lit-protocol/lit-client ``` ## Quick Start ```typescript import { Synapse } from '@filoz/synapse-sdk'; import { SynapseStorageSDK } from '@keypo/synapse-storage-sdk'; import { ethers } from 'ethers'; // Initialize Synapse const wallet = new ethers.Wallet(process.env.PRIVATE_KEY!); const provider = new ethers.JsonRpcProvider('https://sepolia.base.org'); const signer = wallet.connect(provider); const synapse = await Synapse.create({ signer, withCDN: true, }); // Initialize SDK const sdk = new SynapseStorageSDK(synapse, { network: 'calibration', rpcUrl: 'https://sepolia.base.org', encryption: { registryAddress: '0x8370eE1a51B5F31cc10E2f4d786Ff20198B10BBE', validationAddress: '0x35ADB6b999AbcD5C9CdF2262c7190C7b96ABcE4C', bundlerRpcUrl: 'https://rpc.zerodev.app/api/v3/YOUR_PROJECT_ID' }, storage: { capacityGB: 10, persistenceDays: 30, withCDN: true } }, process.env.PRIVATE_KEY); // Upload encrypted file const fileData = new TextEncoder().encode("Hello, Filecoin!"); const result = await sdk.upload(fileData, { fileName: 'hello.txt', isPublic: false, // Private - requires NFT to decrypt onProgress: (progress) => console.log(progress.message) }); console.log('File uploaded:', result.pieceCid); console.log('Data identifier:', result.dataIdentifier); ``` ## Configuration ### SDKConfig ```typescript interface SDKConfig { /** Filecoin network */ network: 'mainnet' | 'calibration'; /** RPC endpoint for the blockchain */ rpcUrl?: string; /** Encryption and smart contract settings */ encryption?: { registryAddress: string; // Permissions registry contract validationAddress: string; // Validation contract bundlerRpcUrl: string; // ZeroDev bundler for account abstraction }; /** Filecoin storage settings */ storage?: { capacityGB?: number; // Storage capacity (default: 10GB) persistenceDays?: number; // Storage duration (default: 30 days) withCDN?: boolean; // Enable CDN acceleration (default: true) }; } ``` ## Complete API Reference ### Upload Files ```typescript async upload(data: Uint8Array, options?: UploadOptions): Promise<UploadResult> ``` Upload and encrypt a file to Filecoin with NFT-based access control. **Parameters:** - `data`: File data as Uint8Array - `options`: Upload configuration (all optional) **UploadOptions:** ```typescript interface UploadOptions { fileName?: string; // File name for metadata isPublic?: boolean; // Public access when encrypted (default: true) skipPaymentCheck?: boolean; // Skip USDFC balance validation metadata?: Record<string, any>; // Custom metadata onProgress?: (progress: UploadProgress) => void; // Progress callback callbacks?: StorageCallbacks; // Detailed operation callbacks serviceProvider?: { // Manual provider selection (optional) providerId?: number; // Specific provider ID (e.g., 8, 16) providerAddress?: string; // Provider wallet address forceCreateDataSet?: boolean; // Force new dataset creation }; } ``` **Returns:** `UploadResult` with `pieceCid`, `dataIdentifier`, `encrypted`, `accessType`, and metadata #### Provider Selection (Optional) By default, the SDK automatically selects the best available provider using this priority: 1. **Existing datasets** - Reuses your existing datasets for faster uploads 2. **Provider health** - Automatically pings providers to find responsive ones 3. **Random selection** - Falls back to random selection from approved providers For better reliability, you can manually specify a provider: ```typescript // Manual provider selection (recommended for reliability) serviceProvider: { providerId: 16, // Use specific provider (8=THCloudAI, 16=zens-ocean) forceCreateDataSet: true // Create new dataset (costs ~1-2 USDFC) } ``` **Available Providers** (Calibration testnet): - **8** - THCloudAI (reliable) - **16** - zens-ocean (reliable) - **2** - pspsps - **3** - ezpdpz-calib (may have issues) - **4** - infrafolio-calib - **13** - filstarry-pdp #### Examples **Simple upload (auto-provider selection):** ```typescript const fileData = new TextEncoder().encode("My secret data"); const result = await sdk.upload(fileData, { fileName: 'secret.txt', isPublic: false // Private - only NFT owner can access }); ``` **Upload with manual provider selection:** ```typescript const result = await sdk.upload(fileData, { fileName: 'secret.txt', isPublic: false, serviceProvider: { providerId: 16, // Use zens-ocean provider forceCreateDataSet: true // Create new dataset }, onProgress: (progress) => { console.log(`${progress.percentage}% - ${progress.message}`); } }); ``` **Upload with detailed callbacks:** ```typescript const result = await sdk.upload(fileData, { fileName: 'secret.txt', isPublic: false, callbacks: { onProviderSelected: (provider) => { console.log(`Using provider: ${provider.name}`); }, onDataSetCreationStarted: (tx, statusUrl) => { console.log(`Creating dataset: ${tx.hash}`); }, onUploadComplete: (piece) => { console.log(`Upload complete: ${piece}`); } } }); ``` ### Download Files ```typescript async download(pieceCid: string, options?: DownloadOptions): Promise<DownloadResult> ``` Download and optionally decrypt a file from Filecoin. **DownloadOptions:** ```typescript interface DownloadOptions { outputPath?: string; // Local file path to save decrypt?: boolean; // Attempt decryption (default: true) onProgress?: (progress: DownloadProgress) => void; } ``` **Example:** ```typescript const result = await sdk.download('bafk...', { outputPath: './downloaded-file.txt', decrypt: true, onProgress: ({ message, bytesDownloaded, totalBytes }) => { console.log(`${message} (${bytesDownloaded}/${totalBytes} bytes)`); } }); console.log('File data:', new TextDecoder().decode(result.data)); ``` ### List Files ```typescript async list(options?: ListOptions): Promise<FileListEntry[]> ``` List all files owned by or shared with the current wallet. **Example:** ```typescript const files = await sdk.list({ onProgress: ({ message }) => console.log(message) }); files.forEach(file => { console.log(`${file.fileName} (${file.pieceCid})`); console.log(` Size: ${file.fileSize} bytes`); console.log(` Access: ${file.isPublic ? 'Public' : 'Private'}`); console.log(` Owner: ${file.owner}`); console.log(` Uploaded: ${file.uploadedAt}`); }); ``` ### List Public Files ```typescript async listPublic(options?: ListPublicOptions): Promise<FileListEntry[]> ``` List all public files from all users on the network. **Example:** ```typescript const publicFiles = await sdk.listPublic({ onProgress: ({ message }) => console.log(message) }); console.log(`Found ${publicFiles.length} public files`); ``` ### Share Files ```typescript async share(pieceCid: string, options: ShareOptions): Promise<void> ``` Share a private file with another wallet by minting an access NFT. **📋 Important**: The recipient wallet must have at least one existing dataset (created by uploading a file) before they can download shared files. If the recipient has never uploaded a file, they should upload at least one file first to establish their dataset. **ShareOptions:** ```typescript interface ShareOptions { recipient: string; // Wallet address to share with debug?: boolean; // Enable debug logging } ``` **Example:** ```typescript await sdk.share('bafk...', { recipient: '0x742d35Cc6634C0532925a3b8D93A1e05441AB7E', debug: true }); console.log('File shared successfully'); ``` **Recipient Requirements:** ```typescript // Recipient must first create a dataset by uploading any file const recipientSDK = new SynapseStorageSDK(synapse, config, recipientPrivateKey); await recipientSDK.upload(new TextEncoder().encode("init"), { fileName: "init.txt", isPublic: true // Can be any file to establish dataset }); // Now the recipient can download shared files const sharedFile = await recipientSDK.download(sharedPieceCid); ``` ### Delete Files ```typescript async delete(pieceCid: string, options?: DeleteOptions): Promise<DeleteResult> ``` Delete a file from the permissions registry (revokes access, data remains on Filecoin). **📋 Important**: The delete function currently removes the file from the permissions registry, making it no longer downloadable through the SDK. However, the pieceCID remains stored on the Filecoin network. In a future version of the SDK, we will add the ability to schedule the deletion of the pieceCID from the storage provider. **Example:** ```typescript const result = await sdk.delete('bafk...', { debug: true, onProgress: ({ message, step, total }) => { console.log(`Step ${step}/${total}: ${message}`); } }); console.log(`File deleted: ${result.transactionHash}`); ``` ### Balance Management ```typescript async checkBalance(): Promise<BalanceInfo> async deposit(amount: number): Promise<void> ``` Manage USDFC tokens for paying Filecoin storage costs. **Example:** ```typescript // Check current balances const balance = await sdk.checkBalance(); console.log(`FIL: ${balance.formatted.fil}`); console.log(`USDFC: ${balance.formatted.usdfc}`); console.log(`Synapse: ${balance.formatted.synapse}`); // Deposit USDFC tokens (if needed) if (balance.usdfc < 1000000) { // Less than 1 USDFC await sdk.deposit(5); // Deposit 5 USDFC } ``` ## File Types and Metadata ### FileListEntry ```typescript interface FileListEntry { pieceCid: string; // Filecoin piece CID dataIdentifier?: string; // Smart contract data ID fileName?: string; // Original file name fileSize: number; // Size in bytes isPublic: boolean; // Public vs private access encrypted: boolean; // Whether file is encrypted owner?: string; // File owner wallet address uploader?: string; // Uploader wallet address uploadedAt?: string; // Upload timestamp contractAddress?: string; // Associated smart contract metadata?: Record<string, any>; // Custom metadata shares?: string[]; // Addresses file is shared with status?: string; // File status } ``` ## Error Handling The SDK provides structured error handling with detailed error information: ```typescript import { SDKError, ErrorCategory } from '@keypo/synapse-storage-sdk'; try { const result = await sdk.upload(data); } catch (error) { if (error instanceof SDKError) { console.log('Category:', error.category); // 'NETWORK', 'VALIDATION', etc. console.log('User message:', error.userMessage); // User-friendly message console.log('Recoverable:', error.recoverable); // Can user retry? console.log('Details:', error.details); // Technical details } } ``` ### Error Categories - `VALIDATION`: Input validation errors - `NETWORK`: Network connectivity issues - `PAYMENT`: USDFC balance or payment failures - `ENCRYPTION`: Lit Protocol encryption errors - `CONTRACT`: Smart contract transaction failures - `STORAGE`: Filecoin storage errors - `FILE`: File operation errors - `CONFIG`: Configuration errors ## Advanced Usage ### Custom Progress Tracking ```typescript const result = await sdk.upload(data, { fileName: 'large-file.bin', onProgress: ({ message, step, total, bytesProcessed }) => { // Update progress bar const percentage = step && total ? (step / total * 100).toFixed(1) : 0; console.log(`${percentage}% - ${message}`); if (bytesProcessed) { console.log(`Processed: ${bytesProcessed} bytes`); } } }); ``` ### Batch Operations ```typescript // Upload multiple files const files = [ { name: 'doc1.txt', data: new TextEncoder().encode('Document 1') }, { name: 'doc2.txt', data: new TextEncoder().encode('Document 2') }, ]; const results = await Promise.all( files.map(file => sdk.upload(file.data, { fileName: file.name })) ); console.log(`Uploaded ${results.length} files`); // Share with multiple recipients const recipients = ['0x123...', '0x456...', '0x789...']; await Promise.all( recipients.map(recipient => sdk.share(pieceCid, { recipient }) ) ); ``` ### File Filtering and Search ```typescript const allFiles = await sdk.list(); // Filter by owner const myFiles = allFiles.filter(file => file.owner?.toLowerCase() === myWallet.toLowerCase() ); // Filter by metadata const importantFiles = allFiles.filter(file => file.metadata?.tags?.includes('important') ); // Sort by upload date const recentFiles = allFiles .sort((a, b) => new Date(b.uploadedAt!).getTime() - new Date(a.uploadedAt!).getTime() ) .slice(0, 10); // Most recent 10 files ``` ## Network Configuration ### Base Sepolia Testnet (Recommended) ```typescript const config = { network: 'calibration', rpcUrl: 'https://sepolia.base.org', encryption: { registryAddress: '0x8370eE1a51B5F31cc10E2f4d786Ff20198B10BBE', validationAddress: '0x35ADB6b999AbcD5C9CdF2262c7190C7b96ABcE4C', bundlerRpcUrl: 'https://rpc.zerodev.app/api/v3/YOUR_PROJECT_ID' } }; ``` ### Environment Variables Create a `.env` file: ```bash # Wallet private key (with or without 0x prefix) PRIVATE_KEY=your_private_key_here # ZeroDev project ID (get from https://dashboard.zerodev.app/) ZERODEV_PROJECT_ID=your_project_id # Optional: Enable debug logging DEBUG=true ``` ## Security Considerations - **Private Key Management**: Store private keys securely, never commit to version control - **Public vs Private**: Public files can be decrypted by anyone, private files require NFT ownership - **File Deletion**: Deletion removes access permissions but encrypted data remains on Filecoin - **Network Security**: Use HTTPS RPC endpoints and verify contract addresses - **Access Control**: NFT-based permissions are enforced by smart contracts ## Troubleshooting ### Common Issues 1. **"Private key required" error** - Ensure private key is provided to SDK constructor - Check private key format (with or without 0x prefix both work) 2. **"Insufficient balance" error** - Check USDFC token balance with `sdk.checkBalance()` - Deposit more tokens or use `skipPaymentCheck: true` for testing 3. **"File not found" error** - Verify the piece CID is correct - Check if file was uploaded with same wallet address - For shared files: Ensure the recipient wallet has created at least one dataset (uploaded a file) 4. **Upload timeouts or provider issues** - Some providers may be unreliable (especially provider 3 on calibration) - Use manual provider selection with reliable providers: ```typescript serviceProvider: { providerId: 16 } // or 8 ``` - Check provider status with the list providers script 5. **Transaction hanging** - ZeroDev bundler may be slow, transactions have 30-60s timeouts - Check network status and try again 6. **Dataset creation failures** - Ensure sufficient USDFC balance (1-2 USDFC needed for new datasets) - Try using existing datasets first: `forceCreateDataSet: false` - Check if provider supports dataset creation 7. **Encryption/Decryption failures** - Ensure Lit Protocol network is accessible - Check wallet has permission to decrypt (NFT ownership for private files) ### Debug Mode Enable debug logging for detailed operation information: ```typescript const result = await sdk.upload(data, { debug: true }); const files = await sdk.list({ debug: true }); await sdk.share(pieceCid, { recipient: '0x...', debug: true }); ``` ## Contributing This SDK is part of the Keypo ecosystem for decentralized file storage. For issues and feature requests, please use the project repository. ## License MIT License - see [LICENSE](./LICENSE) file for details. --- **Built with ❤️ for the decentralized web**