A TypeScript SDK for building end-to-end encrypted messaging applications on Ethereum using Zama's Fully Homomorphic Encryption (FHE) technology.
- =� End-to-End Encryption: Messages are encrypted using FHE, ensuring complete privacy
- �� On-Chain Storage: All messages are stored on the Ethereum blockchain
- =�� Password-Based Authentication: Users authenticate with a password that derives their encryption keys
- =� Browser Compatible: Works seamlessly in modern web browsers
- =� Key Management: Automatic encryption key generation and management
- =� Dialogue System: Organize messages into conversations between users
npm install zama-whisper # or yarn add zama-whisper # or pnpm add zama-whisper
That's it! All dependencies (@zama-fhe/relayer-sdk, ethers, crypto-js) will be installed automatically. No additional setup required.
- Node.js >= 16
- A Web3 wallet (MetaMask, WalletConnect, etc.)
- Sepolia testnet ETH for gas fees
import { PrivateMsgSDK, initSDK, SepoliaConfig } from 'zama-whisper' import { createInstance } from '@zama-fhe/relayer-sdk/web' // Step 1: Initialize WASM modules await initSDK() // Step 2: Create FHE instance const fheInstance = await createInstance(SepoliaConfig) // Step 3: Create SDK instance const sdk = new PrivateMsgSDK() // Step 4: Initialize with wallet provider await sdk.initialize(fheInstance, window.ethereum)
// Check if user is registered const userAddress = '0x...' const isRegistered = await sdk.isRegistered(userAddress) if (!isRegistered) { // Register new user const result = await sdk.login('mySecurePassword123') console.log('User registered:', result.isNewUser) // true } else { // Login existing user const result = await sdk.login('mySecurePassword123') console.log('Login successful:', result.success) // true }
// Send an encrypted message const result = await sdk.sendMessage({ to: '0xRecipientAddress...', message: 'Hello, this is a private message!', // password is optional if already logged in }) console.log('Message sent:', result.transactionHash) console.log('Dialogue address:', result.dialogueAddress)
// Get all dialogues for the current user const dialogues = await sdk.getDialogues(userAddress) // Get messages from a specific dialogue const dialogue = await sdk.getDialogueWith({ otherUser: '0xOtherUserAddress...', // password is optional if already logged in }) console.log('Messages:', dialogue.messages)
const sdk = new PrivateMsgSDK()
Creates a new SDK instance.
const sdk = new PrivateMsgSDK({ contractAddress?: string // Optional: Contract address rpcUrl?: string // Optional: RPC URL })
Initializes the SDK with FHE instance and wallet provider.
instance: FHE instance fromcreateInstance()provider: Web3 provider (e.g.,window.ethereum)
Login or register a user with a password.
const result = await sdk.login('password') // Returns: { success: boolean, isNewUser: boolean, message: string }
Clears cached password and keys.
sdk.logout()
Check if a user is registered.
const isRegistered = await sdk.isRegistered('0x...')
Send an encrypted message.
await sdk.sendMessage({ to: '0x...', // Recipient address message: 'Hello!', // Plaintext message password?: string, // Optional if logged in dialogueKey?: bigint // Optional: For existing conversations })
Get all dialogues for a user.
const dialogues = await sdk.getDialogues('0x...')
Get messages from a dialogue with a specific user.
const result = await sdk.getDialogueWith({ otherUser: '0x...', password?: string // Optional if logged in }) // Returns: { dialogueAddress: string, messages: Message[] }
Get messages from a specific dialogue.
const messages = await sdk.getMessages({ dialogueAddress: '0x...', password?: string // Optional if logged in })
Listen for new messages.
const unsubscribe = sdk.onNewMessage((message) => { console.log('New message:', message) }) // Later: unsubscribe()
Check if SDK is initialized.
const initialized = sdk.isInitialized()
Check if user is logged in.
const loggedIn = sdk.isLoggedIn()
Get the contract address.
const address = sdk.getContractAddress()
interface Message { content: string // Decrypted message content encryptedContent: string // Encrypted content timestamp: number // Unix timestamp from: string // Sender address to: string // Recipient address }
interface Dialogue { dialogueAddress: string // Unique dialogue ID participants: string[] // User addresses otherUser?: string // The other participant messageCount: number // Total messages lastMessage?: Message // Last message encryptedPrivateKey: string // FHE encrypted key }
interface SendMessageResult { transactionHash: string // Transaction hash dialogueAddress: string // Dialogue address blockNumber?: number // Block number }
The SDK exposes internal modules for advanced use cases:
// Access individual modules sdk.auth // Authentication module sdk.keyManager // Key management module sdk.encryption // Encryption module sdk.message // Message module sdk.dialogue // Dialogue module
sdk.setCallbacks({ onMessageSent: (message) => { console.log('Message sent:', message) }, onMessageReceived: (message) => { console.log('Message received:', message) }, onError: (error) => { console.error('Error:', error) } })
try { await sdk.sendMessage({ to: '0x...', message: 'Hello!' }) } catch (error) { if (error.message.includes('password')) { console.error('Invalid password') } else if (error.message.includes('network')) { console.error('Network error') } else { console.error('Unknown error:', error) } }
- Password Management: Store passwords securely. The SDK does not persist passwords.
- Error Handling: Always wrap SDK calls in try-catch blocks.
- Initialization: Ensure the SDK is initialized before making any calls.
- Gas Optimization: Batch messages when possible to reduce gas costs.
- Key Caching: The SDK caches keys after login for better performance.
- Password Security: Passwords are used to derive encryption keys. Use strong passwords.
- Key Storage: Private keys are encrypted with FHE and stored on-chain.
- Message Privacy: All messages are end-to-end encrypted using FHE.
- No Backend Required: All encryption/decryption happens client-side.
Currently supports:
- � Sepolia Testnet (default)
Coming soon:
- Ethereum Mainnet
- Other EVM chains
Check out the /front directory for a complete Vue.js application example using this SDK.
If you encounter WASM loading errors in production:
// Use CDN URLs explicitly await initSDK()
- Ensure sufficient ETH balance for gas fees
- Check that you're connected to Sepolia testnet
- Verify the contract address is correct
- Verify the password is correct
- Ensure the FHE instance is properly initialized
- Check that WASM modules loaded successfully
Contributions are welcome! Please feel free to submit a Pull Request.
MIT
For questions and support, please open an issue on GitHub.