@interfold/sdk
v0.12.1
Published
A powerful, type-safe TypeScript SDK for interacting with Interfold smart contracts. This SDK provides real-time event listening, contract interaction methods, and comprehensive error handling.
Readme
Interfold TypeScript SDK
A powerful, type-safe TypeScript SDK for interacting with Interfold smart contracts. This SDK provides real-time event listening, contract interaction methods, and comprehensive error handling.
Features
- Event-driven architecture: Listen to smart contract events in real-time
- Type-safe: Built with TypeScript and uses generated types from contracts
- Easy contract interactions: Simple methods for reading from and writing to contracts
- React integration: Includes React hooks for easy frontend integration (via
@interfold/react) - Modular architecture: Tree-shakeable sub-modules for contracts, events, and encryption
- Encryption helpers: Standalone FHE encryption functions with optional ZK proof generation
- Error handling: Comprehensive error handling with custom error types
- Gas estimation: Built-in gas estimation for transactions
- Event polling: Support for both WebSocket and polling-based event listening
Installation
pnpm add @interfold/sdkQuick Start
import { InterfoldSDK, InterfoldEventType, RegistryEventType } from '@interfold/sdk'
import { createPublicClient, createWalletClient, http, custom } from 'viem'
import { sepolia } from 'viem/chains'
// Initialize clients
const publicClient = createPublicClient({
chain: sepolia,
transport: http('YOUR_RPC_URL'),
})
const walletClient = createWalletClient({
chain: sepolia,
transport: custom(window.ethereum),
})
// Create SDK instance
const sdk = new InterfoldSDK({
publicClient,
walletClient,
contracts: {
interfold: '0x...', // Your Interfold contract address
ciphernodeRegistry: '0x...', // Your CiphernodeRegistry contract address
feeToken: '0x...', // Your ERC-20 fee token address
},
chain: sepolia,
// Use 'SECURE_THRESHOLD_8192' for production; 'INSECURE_THRESHOLD_512' for local dev only
thresholdBfvParamsPresetName: 'SECURE_THRESHOLD_8192',
})
// Listen to events with the unified event system
sdk.onInterfoldEvent(InterfoldEventType.E3_REQUESTED, (event) => {
console.log('E3 Requested:', event.data)
})
sdk.onInterfoldEvent(RegistryEventType.COMMITTEE_REQUESTED, (event) => {
console.log('Committee Requested:', event.data)
})
// Interact with contracts
const hash = await sdk.requestE3({
threshold: [1, 3],
inputWindow: [BigInt(0), BigInt(100)],
e3Program: '0x...',
e3ProgramParams: '0x...',
computeProviderParams: '0x...',
customParams: '0x...',
})Factory Method
For a simpler setup (especially on the server), use the static InterfoldSDK.create() factory:
import { InterfoldSDK } from '@interfold/sdk'
import { sepolia } from 'viem/chains'
const sdk = InterfoldSDK.create({
rpcUrl: 'wss://sepolia.example.com',
contracts: {
interfold: '0x...',
ciphernodeRegistry: '0x...',
feeToken: '0x...',
},
chain: sepolia,
privateKey: '0x...', // optional — omit for read-only
// Use 'SECURE_THRESHOLD_8192' for production; 'INSECURE_THRESHOLD_512' for local dev only
thresholdBfvParamsPresetName: 'SECURE_THRESHOLD_8192',
})The factory auto-detects HTTP vs WebSocket transports and creates the appropriate viem clients.
Usage within a browser
Usage within a typescript project should work out of the box, however in order to use wasm related functionality of the SDK within the browser vite you must do the following:
- Use
vite - Use the
vite-plugin-top-level-awaitplugin - Use the
vite-plugin-wasmplugin - Exclude the
@interfold/wasmpackage from bundling optimization.
This will enable vite to correctly bundle and serve the wasm bundle we use effectively.
import { defineConfig } from 'vite'
import wasm from 'vite-plugin-wasm'
import topLevelAwait from 'vite-plugin-top-level-await'
export default defineConfig({
// other config ...
optimizeDeps: {
exclude: ['@interfold/wasm'],
},
plugins: [wasm(), topLevelAwait()],
})Event System
The SDK uses a unified event system with TypeScript enums for type safety:
Interfold Events
enum InterfoldEventType {
// E3 Lifecycle
E3_REQUESTED = 'E3Requested',
CIPHERTEXT_OUTPUT_PUBLISHED = 'CiphertextOutputPublished',
PLAINTEXT_OUTPUT_PUBLISHED = 'PlaintextOutputPublished',
// E3 Program Management
E3_PROGRAM_ENABLED = 'E3ProgramEnabled',
E3_PROGRAM_DISABLED = 'E3ProgramDisabled',
// Encryption Scheme Management
ENCRYPTION_SCHEME_ENABLED = 'EncryptionSchemeEnabled',
ENCRYPTION_SCHEME_DISABLED = 'EncryptionSchemeDisabled',
// Configuration
CIPHERNODE_REGISTRY_SET = 'CiphernodeRegistrySet',
MAX_DURATION_SET = 'MaxDurationSet',
ALLOWED_E3_PROGRAMS_PARAMS_SET = 'AllowedE3ProgramsParamsSet',
OWNERSHIP_TRANSFERRED = 'OwnershipTransferred',
INITIALIZED = 'Initialized',
}Registry Events
enum RegistryEventType {
COMMITTEE_REQUESTED = 'CommitteeRequested',
COMMITTEE_PUBLISHED = 'CommitteePublished',
COMMITTEE_FINALIZED = 'CommitteeFinalized',
INTERFOLD_SET = 'InterfoldSet',
OWNERSHIP_TRANSFERRED = 'OwnershipTransferred',
INITIALIZED = 'Initialized',
}Event Data Structure
Each event follows a consistent structure:
interface InterfoldEvent<T extends AllEventTypes> {
type: T
data: EventData[T] // Typed based on event type
log: Log // Raw viem log
timestamp: Date
blockNumber: bigint
transactionHash: string
}React Integration
The SDK includes a React hook via the @interfold/react package:
pnpm add @interfold/reactimport { useInterfoldSDK } from '@interfold/react'
function MyComponent() {
const {
sdk,
isInitialized,
error,
requestE3,
onInterfoldEvent,
off,
InterfoldEventType,
RegistryEventType,
} = useInterfoldSDK({
contracts: {
interfold: '0x...',
ciphernodeRegistry: '0x...',
feeToken: '0x...',
},
autoConnect: true,
// Use 'SECURE_THRESHOLD_8192' for production; 'INSECURE_THRESHOLD_512' for local dev only
thresholdBfvParamsPresetName: 'SECURE_THRESHOLD_8192',
})
useEffect(() => {
if (isInitialized) {
const handler = (event) => {
console.log('New E3 request:', event)
}
onInterfoldEvent(InterfoldEventType.E3_REQUESTED, handler)
return () => off(InterfoldEventType.E3_REQUESTED, handler)
}
}, [isInitialized])
return (
<div>
{error && <p>Error: {error}</p>}
{!isInitialized && <p>Initializing...</p>}
{/* Your UI */}
</div>
)
}The hook uses wagmi's usePublicClient and useWalletClient under the hood, so your app must be
wrapped in a wagmi provider.
Encryption Functions
The SDK provides standalone encryption functions for FHE (Fully Homomorphic Encryption) operations. These can be used via the SDK instance or imported directly for tree-shaking:
Via the SDK instance
// Generate a public key
const publicKey = await sdk.generatePublicKey()
// Encrypt a single number
const encrypted = await sdk.encryptNumber(42n, publicKey)
// Encrypt a vector
const encryptedVec = await sdk.encryptVector(BigUint64Array.from([1n, 2n, 3n]), publicKey)
// Encrypt with ZK proof generation
const { encryptedData, proof } = await sdk.encryptNumberAndGenProof(42n, publicKey)Standalone imports
import {
generatePublicKey,
encryptNumber,
encryptVector,
encryptNumberAndGenProof,
encryptVectorAndGenProof,
encryptNumberAndGenInputs,
encryptVectorAndGenInputs,
computePublicKeyCommitment,
getThresholdBfvParamsSet,
} from '@interfold/sdk'
// Use 'SECURE_THRESHOLD_8192' for production; 'INSECURE_THRESHOLD_512' for local dev only
const presetName = 'SECURE_THRESHOLD_8192'
const publicKey = await generatePublicKey(presetName)
const encrypted = await encryptNumber(42n, publicKey, presetName)
const { encryptedData, proof } = await encryptNumberAndGenProof(42n, publicKey, presetName)Modular Imports
The SDK is organized into three sub-modules that can be imported independently for tree-shaking:
// Encryption functions and types
import { generatePublicKey, encryptNumber } from '@interfold/sdk/crypto'
// Contract client and types
import { ContractClient } from '@interfold/sdk/contracts'
import type { ContractAddresses, E3 } from '@interfold/sdk/contracts'
// Event listener and types
import { EventListener, InterfoldEventType, RegistryEventType } from '@interfold/sdk/events'All sub-module exports are also re-exported from the main @interfold/sdk entry point for
convenience.
API Reference
Core Methods
Contract Interactions
// Approve fee token spending
await sdk.approveFeeToken(amount: bigint);
// Request a new E3 computation
await sdk.requestE3({
threshold: [number, number],
inputWindow: [bigint, bigint],
e3Program: `0x${string}`,
e3ProgramParams: `0x${string}`,
computeProviderParams: `0x${string}`,
customParams?: `0x${string}`,
gasLimit?: bigint
});
// Publish ciphertext output
await sdk.publishCiphertextOutput(e3Id: bigint, ciphertextOutput: `0x${string}`, ciphertextCommitment: `0x${string}`, proof: `0x${string}`, gasLimit?: bigint);
// Read operations
const e3Data = await sdk.getE3(e3Id: bigint);
const publicKey = await sdk.getE3PublicKey(e3Id: bigint);
const quote = await sdk.getE3Quote(params: E3RequestParams);
const stage = await sdk.getE3Stage(e3Id: bigint);
const reason = await sdk.getFailureReason(e3Id: bigint);The original requester can cancel an E3 while it is in Requested, CommitteeFinalized,
KeyPublished, or CiphertextReady. Operators retain the configured value of completed milestones,
and the remaining work allocation becomes claimable through the refund manager.
const hash = await sdk.cancelE3(e3Id)
await sdk.waitForTransaction(hash)Event Handling
sdk.onInterfoldEvent(eventType: AllEventTypes, callback: EventCallback);
sdk.off(eventType: AllEventTypes, callback: EventCallback);
sdk.once(eventType: AllEventTypes, callback: EventCallback);
const logs = await sdk.getHistoricalEvents(
eventType: AllEventTypes,
fromBlock?: bigint,
toBlock?: bigint
);
// Event polling (if websockets unavailable)
await sdk.startEventPolling();
sdk.stopEventPolling();CommitteePublished.publicKey is an untrusted transport value. Validate it against the event's
on-chain commitment before using it for encryption:
import { hexToBytes } from 'viem'
await sdk.onInterfoldEvent(RegistryEventType.COMMITTEE_PUBLISHED, async (event) => {
const publicKey = hexToBytes(event.data.publicKey)
const expectedCommitment = hexToBytes(event.data.pkCommitment)
if (!(await sdk.validatePublicKeyCommitment(publicKey, expectedCommitment))) {
throw new Error('Committee public-key commitment mismatch')
}
// The key is now safe to pass to the encryption methods.
})Encryption
// Get BFV parameter set
const params = await sdk.getThresholdBfvParamsSet();
// Key generation
const publicKey = await sdk.generatePublicKey();
const commitment = await sdk.computePublicKeyCommitment(publicKey);
const isValid = await sdk.validatePublicKeyCommitment(publicKey, commitment);
// Encrypt data
const encrypted = await sdk.encryptNumber(data: bigint, publicKey: Uint8Array);
const encryptedVec = await sdk.encryptVector(data: BigUint64Array, publicKey: Uint8Array);
// Encrypt with proof inputs (for ZK verification)
const { encryptedData, circuitInputs } = await sdk.encryptNumberAndGenInputs(data, publicKey);
const { encryptedData, circuitInputs } = await sdk.encryptVectorAndGenInputs(data, publicKey);
// Encrypt with full ZK proof generation
const { encryptedData, proof } = await sdk.encryptNumberAndGenProof(data, publicKey);
const { encryptedData, proof } = await sdk.encryptVectorAndGenProof(data, publicKey);Utilities
// Gas estimation
const gas = await sdk.estimateGas(functionName, args, contractAddress, abi, value?);
// Transaction waiting
const receipt = await sdk.waitForTransaction(hash);
// Cleanup
sdk.cleanup();Configuration
interface SDKConfig {
publicClient: PublicClient
walletClient?: WalletClient
contracts: {
interfold: `0x${string}`
ciphernodeRegistry: `0x${string}`
feeToken: `0x${string}`
}
chain?: Chain
thresholdBfvParamsPresetName: ThresholdBfvParamsPresetName
}thresholdBfvParamsPresetName selects the BFV parameter set used for encryption. It must match the
on-chain paramSet index registered in the Interfold contract:
| Preset name | On-chain paramSet index | Use case |
| -------------------------- | ------------------------- | ----------------------------------------------------------------------------------------------------------- |
| 'INSECURE_THRESHOLD_512' | 0 | Local development and testing only — small polynomial degree (N=512), fast but not cryptographically secure |
| 'SECURE_THRESHOLD_8192' | 1 | Production — full security parameters (N=8192, L=4 CRT moduli) |
Always use 'SECURE_THRESHOLD_8192' in production. The insecure preset exists solely to speed up
local dev cycles.
Error Handling
The SDK includes comprehensive error handling:
import { SDKError } from '@interfold/sdk'
try {
await sdk.requestE3(params)
} catch (error) {
if (error instanceof SDKError) {
console.error(`SDK Error (${error.code}): ${error.message}`)
} else {
console.error('Unexpected error:', error)
}
}Development
Building the SDK
cd packages/interfold-sdk
pnpm buildRunning the Demo
cd examples/basic/client
pnpm install
pnpm devThe demo showcases all SDK features including real-time event listening and contract interactions.
Testing
cd packages/interfold-sdk
pnpm testArchitecture
The SDK is organized into a modular architecture with three domain-specific sub-modules:
- InterfoldSDK (
interfold-sdk.ts): Main orchestrator class that delegates to sub-modules - Contracts (
contracts/):ContractClientfor contract read/write operations, type definitions for contract addresses and E3 data - Events (
events/):EventListenerfor real-time and historical event subscriptions, typed event enums and data interfaces - Encryption (
encryption/): Standalone FHE encryption functions, BFV parameter management, ZK proof generation - Utils (
utils.ts): Helper functions, error classes, encoding utilities
Each sub-module has its own index.ts entry point and can be imported independently.
License
This project is licensed under the MIT License.
