跳到主要内容

IndexedDB Storage

Use IndexedDB for advanced browser storage with better performance and capacity.

Implementation

import { GenericStringStorage } from '@fhevm/sdk';

class IndexedDBStorage implements GenericStringStorage {
private dbName = 'fhevm-storage';
private storeName = 'signatures';
private db: IDBDatabase | null = null;

async init(): Promise<void> {
return new Promise((resolve, reject) => {
const request = indexedDB.open(this.dbName, 1);

request.onerror = () => reject(request.error);
request.onsuccess = () => {
this.db = request.result;
resolve();
};

request.onupgradeneeded = (event) => {
const db = (event.target as IDBOpenDBRequest).result;
if (!db.objectStoreNames.contains(this.storeName)) {
db.createObjectStore(this.storeName);
}
};
});
}

async getItem(key: string): Promise<string | null> {
if (!this.db) await this.init();

return new Promise((resolve, reject) => {
const transaction = this.db!.transaction([this.storeName], 'readonly');
const store = transaction.objectStore(this.storeName);
const request = store.get(key);

request.onsuccess = () => resolve(request.result || null);
request.onerror = () => reject(request.error);
});
}

async setItem(key: string, value: string): Promise<void> {
if (!this.db) await this.init();

return new Promise((resolve, reject) => {
const transaction = this.db!.transaction([this.storeName], 'readwrite');
const store = transaction.objectStore(this.storeName);
const request = store.put(value, key);

request.onsuccess = () => resolve();
request.onerror = () => reject(request.error);
});
}

async removeItem(key: string): Promise<void> {
if (!this.db) await this.init();

return new Promise((resolve, reject) => {
const transaction = this.db!.transaction([this.storeName], 'readwrite');
const store = transaction.objectStore(this.storeName);
const request = store.delete(key);

request.onsuccess = () => resolve();
request.onerror = () => reject(request.error);
});
}

async clear(): Promise<void> {
if (!this.db) await this.init();

return new Promise((resolve, reject) => {
const transaction = this.db!.transaction([this.storeName], 'readwrite');
const store = transaction.objectStore(this.storeName);
const request = store.clear();

request.onsuccess = () => resolve();
request.onerror = () => reject(request.error);
});
}
}

Usage

import { FhevmDecryptionSignature } from '@fhevm/sdk';

// Create storage instance
const storage = new IndexedDBStorage();

// Initialize before use
await storage.init();

// Use with SDK
const signature = await FhevmDecryptionSignature.loadOrSign(
instance,
contractAddresses,
signer,
storage // Use IndexedDB storage
);

With React Hook

import { useState, useEffect } from 'react';

function useIndexedDBStorage() {
const [storage, setStorage] = useState<IndexedDBStorage | null>(null);
const [isReady, setIsReady] = useState(false);

useEffect(() => {
const initStorage = async () => {
const db = new IndexedDBStorage();
await db.init();
setStorage(db);
setIsReady(true);
};

initStorage().catch(console.error);
}, []);

return { storage, isReady };
}

// In component
function MyComponent() {
const { storage, isReady } = useIndexedDBStorage();

const { decrypt } = useFHEDecrypt({
instance,
ethersSigner,
fhevmDecryptionSignatureStorage: storage!,
chainId,
requests,
});

if (!isReady) return <div>Initializing storage...</div>;

return <div>Ready to decrypt</div>;
}

Advanced Features

With Expiration

class IndexedDBStorageWithTTL extends IndexedDBStorage {
async setItemWithTTL(key: string, value: string, ttl: number): Promise<void> {
const item = {
value,
expiry: Date.now() + ttl,
};
await this.setItem(key, JSON.stringify(item));
}

async getItem(key: string): Promise<string | null> {
const data = await super.getItem(key);
if (!data) return null;

try {
const item = JSON.parse(data);
if (item.expiry && Date.now() > item.expiry) {
await this.removeItem(key);
return null;
}
return item.value;
} catch {
return data; // Return as-is if not TTL format
}
}
}

With Encryption

class EncryptedIndexedDBStorage extends IndexedDBStorage {
private encryptionKey: string;

constructor(encryptionKey: string) {
super();
this.encryptionKey = encryptionKey;
}

private async encrypt(data: string): Promise<string> {
// Implement encryption
// Use Web Crypto API for proper encryption
return data; // Placeholder
}

private async decrypt(data: string): Promise<string> {
// Implement decryption
return data; // Placeholder
}

async setItem(key: string, value: string): Promise<void> {
const encrypted = await this.encrypt(value);
await super.setItem(key, encrypted);
}

async getItem(key: string): Promise<string | null> {
const encrypted = await super.getItem(key);
if (!encrypted) return null;
return await this.decrypt(encrypted);
}
}

Benefits

vs LocalStorage

FeatureIndexedDBLocalStorage
Storage Limit~50MB+~5-10MB
AsyncYesNo
IndexingYesNo
PerformanceBetter for large dataBetter for small data
ComplexityHigherLower

Use IndexedDB When

  • Storing large amounts of data
  • Need better performance
  • Want structured storage
  • Need async operations

Use LocalStorage When

  • Small data storage
  • Simple key-value pairs
  • Synchronous access needed
  • Simpler implementation preferred

Browser Support

IndexedDB is supported in:

  • Chrome 24+
  • Firefox 16+
  • Safari 10+
  • Edge 12+

Check support:

const isIndexedDBSupported = 'indexedDB' in window;

Error Handling

try {
const storage = new IndexedDBStorage();
await storage.init();
} catch (error) {
console.error('IndexedDB not available:', error);
// Fallback to LocalStorage or InMemory
const fallbackStorage = new LocalStorageAdapter();
}

Next Steps