Setting Up the Server

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:

  1. Client Application: User interface with wallet integration and FHE encryption
  2. Coordination Logic: Server-side or client-side coordination of E3 processes
  3. Event Management: Real-time monitoring of E3 lifecycle events
  4. 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.