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 Enclave SDK provides powerful TypeScript and React tools to streamline this development process.
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 Enclave 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:
pnpm add @gnosis-guild/enclave
For React applications:
pnpm add @gnosis-guild/enclave @gnosis-guild/enclave-react
Basic TypeScript Client
Create a type-safe client that can interact with Enclave contracts:
import { EnclaveSDK, EnclaveEventType } from '@gnosis-guild/enclave-sdk'
import { createPublicClient, createWalletClient, http, custom } from 'viem'
// Initialize clients
const publicClient = createPublicClient({
transport: http('http://localhost:8545'), // Your RPC URL
})
const walletClient = createWalletClient({
transport: custom(window.ethereum),
})
// Create SDK instance
const sdk = new EnclaveSDK({
publicClient,
walletClient,
contracts: {
enclave: '0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0',
ciphernodeRegistry: '0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9',
},
chainId: 31337, // Hardhat local network
})
// Initialize the SDK
await sdk.initialize()
// Request a new E3 computation
const hash = await sdk.requestE3({
filter: '0x0000000000000000000000000000000000000000',
threshold: [2, 3],
startWindow: [BigInt(0), BigInt(100)],
duration: BigInt(3600),
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 '@gnosis-guild/enclave-react';
function E3Dashboard() {
const [e3Data, setE3Data] = useState(null);
const {
sdk,
isInitialized,
error,
requestE3,
onEnclaveEvent,
off,
EnclaveEventType
} = useEnclaveSDK({
autoConnect: true,
contracts: {
enclave: '0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0',
ciphernodeRegistry: '0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9'
},
chainId: 31337
});
// Listen to E3 events
useEffect(() => {
if (!isInitialized) return;
const handleE3Requested = (event) => {
console.log('New E3 request:', event.data);
setE3Data(event.data);
};
const handleE3Activated = (event) => {
console.log('E3 activated:', event.data);
};
onEnclaveEvent(EnclaveEventType.E3_REQUESTED, handleE3Requested);
onEnclaveEvent(EnclaveEventType.E3_ACTIVATED, handleE3Activated);
return () => {
off(EnclaveEventType.E3_REQUESTED, handleE3Requested);
off(EnclaveEventType.E3_ACTIVATED, handleE3Activated);
};
}, [isInitialized, onEnclaveEvent, off, EnclaveEventType]);
const handleRequestE3 = async () => {
try {
const hash = await requestE3({
filter: '0x0000000000000000000000000000000000000000',
threshold: [2, 3],
startWindow: [BigInt(Date.now()), BigInt(Date.now() + 300000)],
duration: BigInt(1800),
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:
Enclave Events
// Listen to key E3 lifecycle events
sdk.onEnclaveEvent(EnclaveEventType.E3_REQUESTED, (event) => {
console.log('New E3 computation requested:', event.data)
})
sdk.onEnclaveEvent(EnclaveEventType.E3_ACTIVATED, (event) => {
console.log('E3 environment activated:', event.data)
})
sdk.onEnclaveEvent(EnclaveEventType.INPUT_PUBLISHED, (event) => {
console.log('Input data published:', event.data)
})
sdk.onEnclaveEvent(EnclaveEventType.CIPHERTEXT_OUTPUT_PUBLISHED, (event) => {
console.log('Computation result available:', event.data)
})
Registry Events
import { RegistryEventType } from '@gnosis-guild/enclave/sdk'
// Monitor ciphernode network changes
sdk.onEnclaveEvent(RegistryEventType.CIPHERNODE_ADDED, (event) => {
console.log('New ciphernode joined:', event.data)
})
sdk.onEnclaveEvent(RegistryEventType.COMMITTEE_PUBLISHED, (event) => {
console.log('Committee selection complete:', event.data)
})
Server-Side Coordination
For server-side applications, you can create automated coordination services:
import { EnclaveSDK } from '@gnosis-guild/enclave-sdk'
import { createPublicClient, createWalletClient, http } from 'viem'
import { privateKeyToAccount } from 'viem/accounts'
class E3CoordinationServer {
private sdk: EnclaveSDK
constructor(privateKey: string, rpcUrl: string) {
const account = privateKeyToAccount(privateKey as `0x${string}`)
const publicClient = createPublicClient({
transport: http(rpcUrl),
})
const walletClient = createWalletClient({
account,
transport: http(rpcUrl),
})
this.sdk = new EnclaveSDK({
publicClient,
walletClient,
contracts: {
enclave: '0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0',
ciphernodeRegistry: '0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9',
},
})
}
async start() {
await this.sdk.initialize()
this.setupEventListeners()
console.log('E3 Coordination Server started')
}
private setupEventListeners() {
// Auto-activate E3s when they become available
this.sdk.onEnclaveEvent(EnclaveEventType.E3_REQUESTED, async (event) => {
const { e3Id } = event.data
console.log(`Auto-activating E3 ${e3Id}`)
try {
// In a real implementation, you'd generate/retrieve the public key
const publicKey = '0x...' // Your FHE public key
await this.sdk.activateE3(e3Id, publicKey)
} catch (error) {
console.error(`Failed to activate E3 ${e3Id}:`, error)
}
})
// Handle input aggregation
this.sdk.onEnclaveEvent(EnclaveEventType.INPUT_PUBLISHED, async (event) => {
console.log('Input published:', event.data)
// Implement your input aggregation logic here
})
}
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 '@gnosis-guild/enclave-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:
const devConfig = {
contracts: {
enclave: '0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0',
ciphernodeRegistry: '0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9',
},
chainId: 31337,
rpcUrl: 'http://localhost:8545',
}
Production Configuration
For production deployments:
const prodConfig = {
contracts: {
enclave: process.env.ENCLAVE_CONTRACT_ADDRESS!,
ciphernodeRegistry: process.env.REGISTRY_CONTRACT_ADDRESS!,
},
chainId: 1, // Mainnet
rpcUrl: process.env.RPC_URL!,
}
The Enclave 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 Enclave protocol.