Setting Up the Client and Server
Building E3 applications involves creating client-side interfaces for users and coordination logic for managing the E3 lifecycle. The Interfold SDK provides powerful TypeScript and React tools to streamline development.
Overview
Modern E3 applications typically consist of:
- Client Application: User interface with wallet integration and FHE encryption
- Coordination Logic: Server-side or client-side coordination of E3 processes
- Event Management: Real-time monitoring of E3 lifecycle events
- Contract Interactions: Type-safe smart contract communication
The Interfold SDK handles much of this complexity for you, providing:
- Type-safe contract interactions
- Real-time event listening
- React hooks for easy frontend integration
- Comprehensive error handling
Setting Up a Client Application
Install the SDK
For TypeScript/JavaScript applications, install the TypeScript SDK:
pnpm add @enclave-e3/sdkFor React applications add the hook helpers as well:
pnpm add @enclave-e3/sdk @enclave-e3/reactBasic TypeScript Client
Create a type-safe client that can interact with Interfold contracts:
import { EnclaveSDK, EnclaveEventType } from '@enclave-e3/sdk'
import { createPublicClient, createWalletClient, http, custom } from 'viem'
import { hardhat } from 'viem/chains'
// Initialize clients
const publicClient = createPublicClient({
chain: hardhat,
transport: http('http://localhost:8545'),
})
const walletClient = createWalletClient({
transport: custom(window.ethereum),
})
// Create SDK instance (no separate initialize() call needed)
const sdk = new EnclaveSDK({
publicClient,
walletClient,
contracts: {
enclave: '0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0',
ciphernodeRegistry: '0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9',
feeToken: '0x5FbDB2315678afecb367f032d93F642f64180aa3',
},
})
// Approve the fee token before requesting
await sdk.approveFeeToken(BigInt(1000000))
// Request a new E3 computation
const hash = await sdk.requestE3({
threshold: [2, 3],
inputWindow: [BigInt(0), BigInt(100)],
e3Program: '0x9A676e781A523b5d0C0e43731313A708CB607508',
e3ProgramParams: '0x',
computeProviderParams: '0x',
})
console.log('E3 requested with hash:', hash)React Application with Hooks
For React applications, use the useEnclaveSDK hook for seamless integration:
import React, { useEffect, useState } from 'react';
import { useEnclaveSDK } from '@enclave-e3/react';
function E3Dashboard() {
const [e3Data, setE3Data] = useState(null);
const {
sdk,
isInitialized,
error,
requestE3,
onEnclaveEvent,
off,
EnclaveEventType,
RegistryEventType
} = useEnclaveSDK({
autoConnect: true,
contracts: {
enclave: '0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0',
ciphernodeRegistry: '0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9',
feeToken: '0x5FbDB2315678afecb367f032d93F642f64180aa3',
},
});
// Listen to E3 events
useEffect(() => {
if (!isInitialized) return;
const handleE3Requested = (event) => {
console.log('New E3 request:', event.data);
setE3Data(event.data);
};
const handleCommitteePublished = (event) => {
console.log('Committee published:', event.data);
};
onEnclaveEvent(EnclaveEventType.E3_REQUESTED, handleE3Requested);
onEnclaveEvent(RegistryEventType.COMMITTEE_PUBLISHED, handleCommitteePublished);
return () => {
off(EnclaveEventType.E3_REQUESTED, handleE3Requested);
off(RegistryEventType.COMMITTEE_PUBLISHED, handleCommitteePublished);
};
}, [isInitialized, onEnclaveEvent, off, EnclaveEventType, RegistryEventType]);
const handleRequestE3 = async () => {
try {
const hash = await requestE3({
threshold: [2, 3],
inputWindow: [BigInt(Date.now()), BigInt(Date.now() + 300000)],
e3Program: '0x9A676e781A523b5d0C0e43731313A708CB607508',
e3ProgramParams: '0x',
computeProviderParams: '0x',
});
console.log('E3 requested:', hash);
} catch (error) {
console.error('Failed to request E3:', error);
}
};
if (error) {
return <div>Error: {error}</div>;
}
if (!isInitialized) {
return <div>Connecting to Enclave...</div>;
}
return (
<div>
<h1>E3 Dashboard</h1>
<button onClick={handleRequestE3}>
Request E3 Computation
</button>
{e3Data && (
<div>
<h2>Latest E3 Request</h2>
<pre>{JSON.stringify(e3Data, null, 2)}</pre>
</div>
)}
</div>
);
}
export default E3Dashboard;Event-Driven Architecture
The SDK provides comprehensive event handling for the entire E3 lifecycle:
Interfold Events
// Listen to key E3 lifecycle events
sdk.onEnclaveEvent(EnclaveEventType.E3_REQUESTED, (event) => {
console.log('New E3 computation requested:', event.data)
})
sdk.onEnclaveEvent(EnclaveEventType.CIPHERTEXT_OUTPUT_PUBLISHED, (event) => {
console.log('Encrypted computation result available:', event.data)
})
sdk.onEnclaveEvent(EnclaveEventType.PLAINTEXT_OUTPUT_PUBLISHED, (event) => {
console.log('Decrypted plaintext output available:', event.data)
})Registry Events
import { RegistryEventType } from '@enclave-e3/sdk'
// Monitor committee lifecycle events
sdk.onEnclaveEvent(RegistryEventType.COMMITTEE_REQUESTED, (event) => {
console.log('Committee requested for E3:', event.data)
})
sdk.onEnclaveEvent(RegistryEventType.COMMITTEE_PUBLISHED, (event) => {
console.log('Committee selection complete:', event.data)
})
sdk.onEnclaveEvent(RegistryEventType.COMMITTEE_FINALIZED, (event) => {
console.log('Committee finalized:', event.data)
})Server-Side Coordination
For server-side applications, you can create automated coordination services:
import { EnclaveSDK, EnclaveEventType } from '@enclave-e3/sdk'
import { hardhat } from 'viem/chains'
class E3CoordinationServer {
private sdk: EnclaveSDK
constructor(privateKey: string, rpcUrl: string) {
// Use the static factory for convenience
this.sdk = EnclaveSDK.create({
rpcUrl,
privateKey: privateKey as `0x${string}`,
chain: hardhat,
contracts: {
enclave: '0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0',
ciphernodeRegistry: '0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9',
feeToken: '0x5FbDB2315678afecb367f032d93F642f64180aa3',
},
})
}
async start() {
this.setupEventListeners()
console.log('E3 Coordination Server started')
}
private setupEventListeners() {
// Monitor new E3 requests
this.sdk.onEnclaveEvent(EnclaveEventType.E3_REQUESTED, async (event) => {
const { e3Id } = event.data
console.log(`New E3 requested: ${e3Id}`)
})
// Monitor ciphertext output publication
this.sdk.onEnclaveEvent(EnclaveEventType.CIPHERTEXT_OUTPUT_PUBLISHED, async (event) => {
console.log('Ciphertext output published:', event.data)
})
// Monitor plaintext output publication
this.sdk.onEnclaveEvent(EnclaveEventType.PLAINTEXT_OUTPUT_PUBLISHED, async (event) => {
console.log('Plaintext output published:', event.data)
})
}
async stop() {
this.sdk.cleanup()
console.log('E3 Coordination Server stopped')
}
}
// Usage
const server = new E3CoordinationServer(process.env.PRIVATE_KEY!, process.env.RPC_URL!)
server.start()Error Handling
The SDK includes comprehensive error handling:
import { SDKError } from '@enclave-e3/sdk'
try {
await sdk.requestE3(params)
} catch (error) {
if (error instanceof SDKError) {
console.error(`SDK Error (${error.code}): ${error.message}`)
switch (error.code) {
case 'MISSING_PUBLIC_CLIENT':
// Handle missing client
break
case 'INVALID_ADDRESS':
// Handle invalid contract address
break
case 'TRANSACTION_FAILED':
// Handle transaction failure
break
default:
console.error('Unknown SDK error:', error)
}
} else {
console.error('Unexpected error:', error)
}
}Configuration Management
Development Configuration
For local development with the default template:
import { hardhat } from 'viem/chains'
const devSdk = EnclaveSDK.create({
rpcUrl: 'http://localhost:8545',
chain: hardhat,
contracts: {
enclave: '0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0',
ciphernodeRegistry: '0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9',
feeToken: '0x5FbDB2315678afecb367f032d93F642f64180aa3',
},
})Production Configuration
For production deployments:
import { mainnet } from 'viem/chains'
const prodSdk = EnclaveSDK.create({
rpcUrl: process.env.RPC_URL!,
privateKey: process.env.PRIVATE_KEY! as `0x${string}`,
chain: mainnet,
contracts: {
enclave: process.env.ENCLAVE_CONTRACT_ADDRESS! as `0x${string}`,
ciphernodeRegistry: process.env.REGISTRY_CONTRACT_ADDRESS! as `0x${string}`,
feeToken: process.env.FEE_TOKEN_ADDRESS! as `0x${string}`,
},
})The Interfold SDK abstracts away much of the complexity, allowing you to focus on your application's unique business logic while ensuring robust, type-safe interaction with the Interfold protocol.