diff --git a/docs/pages/sdk.mdx b/docs/pages/sdk.mdx
index 1360b60643..61a321ac50 100644
--- a/docs/pages/sdk.mdx
+++ b/docs/pages/sdk.mdx
@@ -128,7 +128,17 @@ sdk.off(EnclaveEventType.E3_REQUESTED, e3Handler)
import { useEnclaveSDK } from '@enclave-e3/react'
export function Dashboard() {
- const { isInitialized, requestE3, onEnclaveEvent, off, EnclaveEventType } = useEnclaveSDK({
+ const {
+ sdk,
+ isInitialized,
+ error,
+ requestE3,
+ getThresholdBfvParamsSet,
+ onEnclaveEvent,
+ off,
+ EnclaveEventType,
+ RegistryEventType,
+ } = useEnclaveSDK({
autoConnect: true,
contracts: {
enclave: import.meta.env.VITE_ENCLAVE_ADDRESS,
@@ -145,6 +155,8 @@ export function Dashboard() {
return () => off(EnclaveEventType.E3_REQUESTED, handle)
}, [isInitialized])
+ if (error) return
Error: {error}
+
return
}
```
@@ -168,8 +180,8 @@ const { encryptedData: vecData, proof: vecProof } = await sdk.encryptVectorAndGe
publicKey,
)
-// Generate public inputs without proof (useful for debugging)
-const { encryptedData, publicInputs } = await sdk.encryptNumberAndGenInputs(42n, 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'
@@ -180,17 +192,18 @@ 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 public 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 |
+| 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
@@ -205,6 +218,100 @@ 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:
+
+```ts
+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
@@ -244,11 +351,17 @@ that wraps or complements the core Interfold SDK.
- **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.
+- **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) })`
@@ -258,9 +371,11 @@ that wraps or complements the core Interfold SDK.
- **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, and `sdk.getFailureReason(e3Id)` if the E3 has failed.
+ 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.
diff --git a/packages/enclave-contracts/scripts/utils.ts b/packages/enclave-contracts/scripts/utils.ts
index e4f527aef1..bd1eeaf2b5 100644
--- a/packages/enclave-contracts/scripts/utils.ts
+++ b/packages/enclave-contracts/scripts/utils.ts
@@ -236,7 +236,11 @@ export const updateE3Config = (
}
// If we hit a top-level key while in the target chain, we've left it
- if (inTargetChain && indent <= chainBaseIndent && !trimmed.startsWith("-")) {
+ if (
+ inTargetChain &&
+ indent <= chainBaseIndent &&
+ !trimmed.startsWith("-")
+ ) {
break;
}
@@ -261,7 +265,10 @@ export const updateE3Config = (
// Detect contract key line (e.g., ` enclave:`)
const keyMatch = trimmed.match(/^(\w+):$/);
- if (keyMatch && (contractEntryIndent === -1 || indent === contractEntryIndent)) {
+ if (
+ keyMatch &&
+ (contractEntryIndent === -1 || indent === contractEntryIndent)
+ ) {
currentContractKey = keyMatch[1];
if (contractEntryIndent === -1) contractEntryIndent = indent;
continue;
@@ -276,8 +283,11 @@ export const updateE3Config = (
if (trimmed.startsWith("address:")) {
foundKeys.add(currentContractKey);
const ws = line.match(/^(\s*)/)?.[1] ?? "";
- const comment = trimmed.match(/^address:\s*["']?[^#"']*["']?\s*(#.*)$/)?.[1];
- lines[i] = `${ws}address: "${update.address}"${comment ? " " + comment : ""}`;
+ const comment = trimmed.match(
+ /^address:\s*["']?[^#"']*["']?\s*(#.*)$/,
+ )?.[1];
+ lines[i] =
+ `${ws}address: "${update.address}"${comment ? " " + comment : ""}`;
console.log(
`✓ Updated ${currentContractKey}: ${update.address} (block ${update.deployBlock})`,
);
@@ -286,7 +296,8 @@ export const updateE3Config = (
if (trimmed.startsWith("deploy_block:")) {
const ws = line.match(/^(\s*)/)?.[1] ?? "";
const comment = trimmed.match(/^deploy_block:\s*\S+\s*(#.*)$/)?.[1];
- lines[i] = `${ws}deploy_block: ${update.deployBlock}${comment ? " " + comment : ""}`;
+ lines[i] =
+ `${ws}deploy_block: ${update.deployBlock}${comment ? " " + comment : ""}`;
}
}
@@ -333,7 +344,9 @@ export const updateE3Config = (
const missingKeys = [...updates.keys()].filter((k) => !foundKeys.has(k));
if (missingKeys.length > 0 && lastContractsLine !== -1) {
const keyIndent =
- contractEntryIndent !== -1 ? contractEntryIndent : contractsKeyIndent + 2;
+ contractEntryIndent !== -1
+ ? contractEntryIndent
+ : contractsKeyIndent + 2;
const valIndent = keyIndent + 2;
const newLines: string[] = [];
@@ -341,7 +354,9 @@ export const updateE3Config = (
const update = updates.get(configKey)!;
newLines.push(`${" ".repeat(keyIndent)}${configKey}:`);
newLines.push(`${" ".repeat(valIndent)}address: "${update.address}"`);
- newLines.push(`${" ".repeat(valIndent)}deploy_block: ${update.deployBlock}`);
+ newLines.push(
+ `${" ".repeat(valIndent)}deploy_block: ${update.deployBlock}`,
+ );
console.log(
`✓ Added ${configKey}: ${update.address} (block ${update.deployBlock})`,
);