Enclave is now The Interfold. Documentation is being updated.
Interfold SDK

Interfold SDK Overview

The Interfold SDK (@enclave-e3/sdk) and React helpers (@enclave-e3/react) provide a batteries-included client stack for requesting E3 computations, subscribing to protocol events, and coordinating custom frontends or backend services. Both packages ship in this repository (see packages/enclave-sdk and packages/enclave-react).

Installation

pnpm add @enclave-e3/sdk           # TypeScript SDK
pnpm add @enclave-e3/react         # React hooks (optional)

For browser projects using WASM (encryption helpers, FHE keygen), configure Vite per the SDK README:

// vite.config.ts
import wasm from 'vite-plugin-wasm'
import topLevelAwait from 'vite-plugin-top-level-await'
 
export default defineConfig({
  optimizeDeps: {
    exclude: ['@enclave-e3/wasm'],
  },
  plugins: [wasm(), topLevelAwait()],
})

Core client

import { EnclaveSDK, EnclaveEventType, RegistryEventType } from '@enclave-e3/sdk'
import { createPublicClient, createWalletClient, http, custom } from 'viem'
import { sepolia } from 'viem/chains'
 
const publicClient = createPublicClient({
  chain: sepolia,
  transport: http(import.meta.env.VITE_RPC_URL),
})
const walletClient = createWalletClient({
  chain: sepolia,
  transport: custom(window.ethereum),
})
 
const sdk = new EnclaveSDK({
  publicClient,
  walletClient,
  contracts: {
    enclave: import.meta.env.VITE_ENCLAVE_ADDRESS,
    ciphernodeRegistry: import.meta.env.VITE_REGISTRY_ADDRESS,
    feeToken: import.meta.env.VITE_FEE_TOKEN_ADDRESS,
  },
  chain: sepolia,
  thresholdBfvParamsPresetName: 'INSECURE_THRESHOLD_512',
})

For server-side or simpler setups, use the factory method:

import { EnclaveSDK } from '@enclave-e3/sdk'
import { sepolia } from 'viem/chains'
 
const sdk = EnclaveSDK.create({
  rpcUrl: import.meta.env.VITE_RPC_URL,
  contracts: {
    enclave: import.meta.env.VITE_ENCLAVE_ADDRESS,
    ciphernodeRegistry: import.meta.env.VITE_REGISTRY_ADDRESS,
    feeToken: import.meta.env.VITE_FEE_TOKEN_ADDRESS,
  },
  chain: sepolia,
  privateKey: '0x...', // optional — omit for read-only
  thresholdBfvParamsPresetName: 'INSECURE_THRESHOLD_512',
})

BFV Parameter Presets

The SDK supports two BFV parameter presets:

Preset NameDegreeSecurityUse Case
INSECURE_THRESHOLD_512512InsecureDevelopment and testing only
SECURE_THRESHOLD_81928192SecureProduction deployments

Requesting computations

const hash = await sdk.requestE3({
  threshold: [2, 3],
  inputWindow: [
    BigInt(Math.floor(Date.now() / 1000)),
    BigInt(Math.floor(Date.now() / 1000) + 5 * 60),
  ],
  e3Program: '0x...',
  e3ProgramParams: '0x',
  computeProviderParams: '0x',
  customParams: '0x',
})

Event subscriptions

const e3Handler = (event) => {
  console.log('New request', event.data)
}
 
sdk.onEnclaveEvent(EnclaveEventType.E3_REQUESTED, e3Handler)
 
sdk.onEnclaveEvent(RegistryEventType.COMMITTEE_REQUESTED, (event) => {
  console.log('Committee requested', event.data)
})
 
// Later, remember to clean up
sdk.off(EnclaveEventType.E3_REQUESTED, e3Handler)

React hook

import { useEnclaveSDK } from '@enclave-e3/react'
 
export function Dashboard() {
  const {
    sdk,
    isInitialized,
    error,
    requestE3,
    getThresholdBfvParamsSet,
    onEnclaveEvent,
    off,
    EnclaveEventType,
    RegistryEventType,
  } = useEnclaveSDK({
    autoConnect: true,
    contracts: {
      enclave: import.meta.env.VITE_ENCLAVE_ADDRESS,
      ciphernodeRegistry: import.meta.env.VITE_REGISTRY_ADDRESS,
      feeToken: import.meta.env.VITE_FEE_TOKEN_ADDRESS,
    },
    thresholdBfvParamsPresetName: 'INSECURE_THRESHOLD_512',
  })
 
  useEffect(() => {
    if (!isInitialized) return
    const handle = (event) => console.log('E3 Requested', event.data)
    onEnclaveEvent(EnclaveEventType.E3_REQUESTED, handle)
    return () => off(EnclaveEventType.E3_REQUESTED, handle)
  }, [isInitialized])
 
  if (error) return <div>Error: {error}</div>
 
  return <button onClick={() => requestE3(/* params */)}>Request E3</button>
}

The hook uses wagmi's usePublicClient and useWalletClient internally, so your app must be inside a wagmi provider.

Encryption functions

The SDK includes standalone FHE encryption functions that can be called via the SDK instance or imported directly:

// Via SDK instance (uses the configured preset name automatically)
const publicKey = await sdk.generatePublicKey()
const encrypted = await sdk.encryptNumber(42n, publicKey)
const encryptedVec = await sdk.encryptVector(new BigUint64Array([1n, 2n, 3n]), publicKey)
const { encryptedData, proof } = await sdk.encryptNumberAndGenProof(42n, publicKey)
const { encryptedData: vecData, proof: vecProof } = await sdk.encryptVectorAndGenProof(
  new BigUint64Array([1n, 2n]),
  publicKey,
)
 
// Generate circuit inputs without proof (useful for debugging)
const { encryptedData, circuitInputs } = await sdk.encryptNumberAndGenInputs(42n, publicKey)
 
// Standalone import (pass preset name explicitly)
import { generatePublicKey, encryptNumber } from '@enclave-e3/sdk'
 
const pk = await generatePublicKey('INSECURE_THRESHOLD_512')
const ct = await encryptNumber(42n, pk, 'INSECURE_THRESHOLD_512')

Full encryption method reference

MethodDescription
generatePublicKey()Generate a BFV public key
computePublicKeyCommitment(pk)Compute a commitment for a public key
encryptNumber(n, pk)Encrypt a single bigint
encryptVector(vec, pk)Encrypt a BigUint64Array
encryptNumberAndGenInputs(n, pk)Encrypt and return circuit inputs for ZK verification
encryptNumberAndGenProof(n, pk)Encrypt and generate a ZK proof of valid encryption
encryptVectorAndGenInputs(v, pk)Vector version of encryptNumberAndGenInputs
encryptVectorAndGenProof(v, pk)Vector version of encryptNumberAndGenProof
getThresholdBfvParamsSet()Get the parameter set for the configured preset
generateProof(circuitInputs)Generate a ZK proof from circuit inputs directly

Modular submodule imports

The SDK is organized into three submodules (contracts, events, crypto) that can be imported independently for tree-shaking:

import { generatePublicKey, encryptNumber } from '@enclave-e3/sdk/crypto'
import { ContractClient } from '@enclave-e3/sdk/contracts'
import { EventListener, EnclaveEventType } from '@enclave-e3/sdk/events'

All exports are also available from the main @enclave-e3/sdk entry point.

Event types

The SDK provides two event type enums for subscribing to on-chain events:

EnclaveEventType

Enum KeySolidity Event Name
E3_REQUESTEDE3Requested
CIPHERTEXT_OUTPUT_PUBLISHEDCiphertextOutputPublished
PLAINTEXT_OUTPUT_PUBLISHEDPlaintextOutputPublished
E3_PROGRAM_ENABLEDE3ProgramEnabled
E3_PROGRAM_DISABLEDE3ProgramDisabled
ENCRYPTION_SCHEME_ENABLEDEncryptionSchemeEnabled
ENCRYPTION_SCHEME_DISABLEDEncryptionSchemeDisabled
CIPHERNODE_REGISTRY_SETCiphernodeRegistrySet
MAX_DURATION_SETMaxDurationSet
ALLOWED_E3_PROGRAMS_PARAMS_SETAllowedE3ProgramsParamsSet
OWNERSHIP_TRANSFERREDOwnershipTransferred
INITIALIZEDInitialized

RegistryEventType

Enum KeySolidity Event Name
COMMITTEE_REQUESTEDCommitteeRequested
COMMITTEE_PUBLISHEDCommitteePublished
COMMITTEE_FINALIZEDCommitteeFinalized
ENCLAVE_SETEnclaveSet
OWNERSHIP_TRANSFERREDOwnershipTransferred
INITIALIZEDInitialized

E3 lifecycle enums

E3Stage

ValueStage
0None
1Requested
2CommitteeFinalized
3KeyPublished
4CiphertextReady
5Complete
6Failed

FailureReason

ValueReason
0None
1CommitteeFormationTimeout
2InsufficientCommitteeMembers
3DKGTimeout
4DKGInvalidShares
5NoInputsReceived
6ComputeTimeout
7ComputeProviderExpired
8ComputeProviderFailed
9RequesterCancelled
10DecryptionTimeout
11DecryptionInvalidShares
12VerificationFailed

Utility exports

The SDK also exports several utility functions and constants from the main entry point:

import {
  SDKError,
  encodeBfvParams,
  encodeComputeProviderParams,
  encodeCustomParams,
  calculateInputWindow,
  decodePlaintextOutput,
  getCurrentTimestamp,
  DEFAULT_COMPUTE_PROVIDER_PARAMS,
  DEFAULT_E3_CONFIG,
} from '@enclave-e3/sdk'
ExportDescription
SDKErrorCustom error class with an error code property
encodeBfvParams(params)ABI-encode BFV parameters for smart contract calls
encodeComputeProviderParamsEncode compute provider params (supports mock mode)
encodeCustomParams(params)Encode arbitrary custom parameters as hex
calculateInputWindowCompute an input window [start, end] from current on-chain time
decodePlaintextOutput(hex)Decode a plaintext output hex string to a number
getCurrentTimestamp(client)Fetch the current block timestamp from the chain
DEFAULT_COMPUTE_PROVIDER_PARAMSDefault compute provider configuration object
DEFAULT_E3_CONFIGDefault E3 config (threshold, duration, payment)

Configuration contract map

Provide the Interfold, CiphernodeRegistry, and FeeToken addresses. The template exposes these via .env / enclave.config.yaml. For multi-chain apps, instantiate separate SDK instances per chain.

Working with the template

enclave init generates the default template (see Project Template). The client already wires @enclave-e3/sdk and @enclave-e3/react:

  • client/src/sdk.ts builds the shared SDK instance
  • client/src/hooks/useE3.ts uses useEnclaveSDK to expose helper hooks throughout the UI
  • server/src/services/enclave.ts instantiates the SDK with a server wallet using createWalletClient

When you add new contracts or E3 programs, update client/.env and server/.env with the new addresses, then restart the dev server so the SDK reconnects.

Application-specific SDKs

For complex E3 programs with specialized requirements, you may want to build an application-specific SDK on top of or alongside the Interfold SDK. For example, CRISP (opens in a new tab) uses @crisp-e3/sdk, which provides:

  • Zero-knowledge proof generation for votes using Noir circuits
  • Merkle tree utilities for voter eligibility verification
  • Vote encryption helpers using FHE
  • Round management and token operations
  • CRISP server interaction APIs

If your E3 program requires specialized cryptographic operations, domain-specific logic, or application-level abstractions beyond basic protocol interactions, consider building a dedicated SDK that wraps or complements the core Interfold SDK.

Advanced usage

  • Historical events: sdk.getHistoricalEvents(type, fromBlock, toBlock) fetches logs without a WebSocket provider; useful for CRON jobs or health checks.
  • Publish ciphertext output: sdk.publishCiphertextOutput(e3Id, ciphertextOutput, proof, gasLimit?) submits the computed ciphertext output for an E3.
  • Gas controls: pass gasLimit to requestE3, publishCiphertextOutput, or other write methods if you want fixed limits when interacting with Interfold on L2s.
  • Gas estimation: sdk.estimateGas(functionName, args, contractAddress, abi) returns the estimated gas for a contract call before sending.
  • Transaction confirmation: sdk.waitForTransaction(hash) blocks until a transaction is mined and returns the TransactionReceipt.
  • One-shot events: sdk.once(eventType, callback) listens for an event and automatically unsubscribes after the first occurrence.
  • Event polling: sdk.startEventPolling() / sdk.stopEventPolling() are handy in serverless environments where websockets are unavailable.
  • Custom transports: on the server, call createWalletClient({ account, transport: http(rpc) }) and load the private key from Vaults/KMS.
  • Get E3 quote: call sdk.getE3Quote(requestParams) to get the fee amount required for an E3 request.
  • Fee token approval: call sdk.approveFeeToken(amount) before requestE3 to approve the fee token spend.
  • Query E3 data: use sdk.getE3(e3Id) to read the full E3 struct, sdk.getE3Stage(e3Id) for the current lifecycle stage (returns E3Stage enum), and sdk.getFailureReason(e3Id) if the E3 has failed (returns FailureReason enum).
  • Public key retrieval: sdk.getE3PublicKey(e3Id) returns the committee's aggregated public key for a given E3.
  • Access the public client: sdk.getPublicClient() returns the underlying viem PublicClient.
  • Cleanup: call sdk.cleanup() when shutting down to unsubscribe all event listeners and stop polling.

Troubleshooting

SymptomTriage
MISSING_PUBLIC_CLIENT errorsEnsure createPublicClient uses an HTTP or WebSocket RPC reachable from the app.
INVALID_ADDRESSDouble-check contract addresses passed into the SDK match the current chain.
Hooks never initializeConfirm autoConnect is true, wallet is connected, and React component is inside a wagmi provider that renders on the client.
No events arriveSwitch to a WebSocket RPC or call sdk.startEventPolling() so logs are polled over HTTP.

For additional API surface details see packages/enclave-sdk/README.md inside this repo.