Enclave is now The Interfold. Documentation is being updated.
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 Interfold SDK provides powerful TypeScript and React tools to streamline development.

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 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/sdk

For React applications add the hook helpers as well:

pnpm add @enclave-e3/sdk @enclave-e3/react

Basic 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.