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-whisperThat'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 modulesdk.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.