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 Name | Degree | Security | Use Case |
|---|---|---|---|
INSECURE_THRESHOLD_512 | 512 | Insecure | Development and testing only |
SECURE_THRESHOLD_8192 | 8192 | Secure | Production 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
| Method | Description |
|---|---|
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 Key | Solidity Event Name |
|---|---|
E3_REQUESTED | E3Requested |
CIPHERTEXT_OUTPUT_PUBLISHED | CiphertextOutputPublished |
PLAINTEXT_OUTPUT_PUBLISHED | PlaintextOutputPublished |
E3_PROGRAM_ENABLED | E3ProgramEnabled |
E3_PROGRAM_DISABLED | E3ProgramDisabled |
ENCRYPTION_SCHEME_ENABLED | EncryptionSchemeEnabled |
ENCRYPTION_SCHEME_DISABLED | EncryptionSchemeDisabled |
CIPHERNODE_REGISTRY_SET | CiphernodeRegistrySet |
MAX_DURATION_SET | MaxDurationSet |
ALLOWED_E3_PROGRAMS_PARAMS_SET | AllowedE3ProgramsParamsSet |
OWNERSHIP_TRANSFERRED | OwnershipTransferred |
INITIALIZED | Initialized |
RegistryEventType
| Enum Key | Solidity Event Name |
|---|---|
COMMITTEE_REQUESTED | CommitteeRequested |
COMMITTEE_PUBLISHED | CommitteePublished |
COMMITTEE_FINALIZED | CommitteeFinalized |
ENCLAVE_SET | EnclaveSet |
OWNERSHIP_TRANSFERRED | OwnershipTransferred |
INITIALIZED | Initialized |
E3 lifecycle enums
E3Stage
| Value | Stage |
|---|---|
| 0 | None |
| 1 | Requested |
| 2 | CommitteeFinalized |
| 3 | KeyPublished |
| 4 | CiphertextReady |
| 5 | Complete |
| 6 | Failed |
FailureReason
| Value | Reason |
|---|---|
| 0 | None |
| 1 | CommitteeFormationTimeout |
| 2 | InsufficientCommitteeMembers |
| 3 | DKGTimeout |
| 4 | DKGInvalidShares |
| 5 | NoInputsReceived |
| 6 | ComputeTimeout |
| 7 | ComputeProviderExpired |
| 8 | ComputeProviderFailed |
| 9 | RequesterCancelled |
| 10 | DecryptionTimeout |
| 11 | DecryptionInvalidShares |
| 12 | VerificationFailed |
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'| Export | Description |
|---|---|
SDKError | Custom error class with an error code property |
encodeBfvParams(params) | ABI-encode BFV parameters for smart contract calls |
encodeComputeProviderParams | Encode compute provider params (supports mock mode) |
encodeCustomParams(params) | Encode arbitrary custom parameters as hex |
calculateInputWindow | Compute 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_PARAMS | Default compute provider configuration object |
DEFAULT_E3_CONFIG | Default 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.tsbuilds the shared SDK instanceclient/src/hooks/useE3.tsusesuseEnclaveSDKto expose helper hooks throughout the UIserver/src/services/enclave.tsinstantiates the SDK with a server wallet usingcreateWalletClient
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
gasLimittorequestE3,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 theTransactionReceipt. - 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)beforerequestE3to 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 (returnsE3Stageenum), andsdk.getFailureReason(e3Id)if the E3 has failed (returnsFailureReasonenum). - 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 viemPublicClient. - Cleanup: call
sdk.cleanup()when shutting down to unsubscribe all event listeners and stop polling.
Troubleshooting
| Symptom | Triage |
|---|---|
MISSING_PUBLIC_CLIENT errors | Ensure createPublicClient uses an HTTP or WebSocket RPC reachable from the app. |
INVALID_ADDRESS | Double-check contract addresses passed into the SDK match the current chain. |
| Hooks never initialize | Confirm autoConnect is true, wallet is connected, and React component is inside a wagmi provider that renders on the client. |
| No events arrive | Switch 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.