Quick Start
Get end-to-end encrypted messaging working in your dApp.
Install
npm install @verbeth/sdk ethers
Setup Client
import {
createVerbethClient,
deriveIdentityKeyPairWithProof,
ExecutorFactory
} from '@verbeth/sdk';
import { ethers } from 'ethers';
// Contract address (Base mainnet)
const LOGCHAIN_ADDRESS = '0x62720f39d5Ec6501508bDe4D152c1E13Fd2F6707';
// 1. Connect wallet
const provider = new ethers.BrowserProvider(window.ethereum);
const signer = await provider.getSigner();
const address = await signer.getAddress();
// 2. Derive identity keys (requires 2 wallet signatures)
const { identityKeyPair, identityProof } = await deriveIdentityKeyPairWithProof(
signer,
address
);
// 3. Create executor for contract interactions
const contract = new ethers.Contract(LOGCHAIN_ADDRESS, LOGCHAIN_ABI, signer);
const executor = ExecutorFactory.createEOA(contract);
// 4. Create client
const client = createVerbethClient({
address,
signer,
identityKeyPair,
identityProof,
executor,
});
Send a Handshake
Start a conversation by sending a handshake to another address. The returned secrets must be stored until the recipient responds.
const recipientAddress = '0x...';
const { tx, ephemeralKeyPair, kemKeyPair } = await client.sendHandshake(
recipientAddress,
'Hello from Verbeth!'
);
await tx.wait();
Accept a Handshake
When you receive a handshake, accept it to establish the encrypted channel. You can implement your own storage to persist the session.
// Parse incoming handshake event from blockchain logs
const initiatorEphemeralPubKey = handshakeEvent.ephemeralPubKey;
const {
tx,
topicOutbound,
topicInbound,
responderEphemeralSecret,
responderEphemeralPublic,
salt,
kemSharedSecret,
} = await client.acceptHandshake(initiatorEphemeralPubKey, 'Hey!');
await tx.wait();
const session = client.createResponderSession({
contactAddress: handshakeEvent.sender,
responderEphemeralSecret,
responderEphemeralPublic,
initiatorEphemeralPubKey,
salt,
kemSharedSecret,
});
await sessionStore.save(session);
Create Session from Response
When the recipient responds to your handshake, create your session using the previously stored secrets.
// hsrEvent is the HandshakeResponse event from the blockchain
const session = client.createInitiatorSessionFromHsr({
contactAddress: recipientAddress,
myEphemeralSecret: storedEphemeralSecret,
myKemSecret: storedKemSecret,
hsrEvent: {
responderEphemeralPubKey: hsrEvent.responderEphemeralPubKey,
inResponseToTag: hsrEvent.inResponseTo,
kemCiphertext: hsrEvent.kemCiphertext,
},
});
await sessionStore.save(session);
Send Messages
Once you have a session, configure the storage and send encrypted messages.
client.setSessionStore(sessionStore);
client.setPendingStore(pendingStore);
const result = await client.sendMessage(
session.conversationId,
'This message is end-to-end encrypted!'
);
console.log('Sent:', result.txHash);
Decrypt Messages
Decrypt incoming messages from the blockchain.
const decrypted = await client.decryptMessage(
messageEvent.topic,
messageEvent.payload,
senderSigningKey,
false // isOwnMessage
);
if (decrypted) {
console.log('Received:', decrypted.plaintext);
}
Full Example
import {
createVerbethClient,
deriveIdentityKeyPairWithProof,
ExecutorFactory
} from '@verbeth/sdk';
import { ethers } from 'ethers';
const LOGCHAIN_ADDRESS = '0x62720f39d5Ec6501508bDe4D152c1E13Fd2F6707';
async function initVerbeth() {
const provider = new ethers.BrowserProvider(window.ethereum);
const signer = await provider.getSigner();
const address = await signer.getAddress();
const { identityKeyPair, identityProof } = await deriveIdentityKeyPairWithProof(
signer,
address
);
const contract = new ethers.Contract(LOGCHAIN_ADDRESS, LOGCHAIN_ABI, signer);
const executor = ExecutorFactory.createEOA(contract);
const client = createVerbethClient({
address,
signer,
identityKeyPair,
identityProof,
executor,
});
return { client, identityKeyPair };
}
async function startConversation(client, recipientAddress: string) {
const { tx, ephemeralKeyPair, kemKeyPair } = await client.sendHandshake(
recipientAddress,
'Starting secure conversation'
);
await tx.wait();
return {
ephemeralSecret: ephemeralKeyPair.secretKey,
kemSecret: kemKeyPair.secretKey,
};
}
Next Steps
- Identity binding: Keys are cryptographically bound to your Ethereum address via signed messages
- Handshake flow: X3DH-like protocol with ML-KEM-768 for post-quantum security
- Double Ratchet: Forward secrecy with automatic topic evolution