Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion packages/fhevm-sdk/package.json
Original file line number Diff line number Diff line change
@@ -1,32 +1,40 @@
{
"name": "@fhevm-sdk",
"version": "0.1.0",
"description": "Universal FHEVM SDK - Framework-agnostic toolkit for building confidential dApps with Fully Homomorphic Encryption",
"private": true,
"type": "module",
"main": "dist/index.js",
"module": "dist/index.js",
"types": "src/index.ts",
"sideEffects": false,
"exports": {
".": {
"types": "./src/index.ts",
"import": "./dist/index.js",
"default": "./dist/index.js"
},
"./core": {
"types": "./src/core/index.ts",
"import": "./dist/core/index.js",
"default": "./dist/core/index.js"
},
"./storage": {
"types": "./src/storage/index.ts",
"import": "./dist/storage/index.js",
"default": "./dist/storage/index.js"
},
"./types": {
"types": "./src/fhevmTypes.ts",
"import": "./dist/fhevmTypes.js",
"default": "./dist/fhevmTypes.js"
},
"./react": {
"types": "./src/react/index.ts",
"import": "./dist/react/index.js",
"default": "./dist/react/index.js"
}
},
"./package.json": "./package.json"
},
"scripts": {
"build": "tsc -p tsconfig.json",
Expand Down
285 changes: 285 additions & 0 deletions packages/fhevm-sdk/src/core/FhevmClient.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,285 @@
import { Eip1193Provider } from "ethers";
import { FhevmInstance } from "../fhevmTypes";
import { createFhevmInstance, FhevmAbortError } from "../internal/fhevm";
import { RelayerEncryptedInput } from "@zama-fhe/relayer-sdk/web";
import { FhevmError, ErrorCodes, createError } from "./errors";

/**
* Configuration options for creating an FHEVM client
*/
export interface FhevmClientConfig {
/**
* Ethereum provider (EIP-1193 compatible) or RPC URL
*/
provider: Eip1193Provider | string;

/**
* Optional mock chain configurations for testing
* Maps chainId to RPC URL
*
* @example
* ```typescript
* {
* 31337: "http://localhost:8545"
* }
* ```
*/
mockChains?: Record<number, string>;

/**
* Optional abort signal to cancel initialization
*/
signal?: AbortSignal;

/**
* Optional callback for status updates during initialization
*/
onStatusChange?: (status: FhevmClientStatus) => void;
}

/**
* Status of the FHEVM client during initialization
*/
export type FhevmClientStatus =
| "idle"
| "sdk-loading"
| "sdk-loaded"
| "sdk-initializing"
| "sdk-initialized"
| "creating"
| "ready"
| "error";

/**
* Result of an encryption operation
*/
export interface EncryptionResult {
/**
* Encrypted handles (one per encrypted value)
*/
handles: Uint8Array[];

/**
* Input proof for verification
*/
inputProof: Uint8Array;
}

/**
* Request for decryption
*/
export interface DecryptionRequest {
/**
* The encrypted handle to decrypt
*/
handle: string;

/**
* The contract address that owns this encrypted value
*/
contractAddress: `0x${string}`;
}

// Re-export error utilities for convenience
export { FhevmError, ErrorCodes, createError } from "./errors";

/**
* FhevmClient - Core client for FHEVM operations
*
* This class provides a framework-agnostic interface for working with
* Fully Homomorphic Encryption on Ethereum. It handles:
* - Instance initialization and management
* - Encryption of values
* - Decryption of encrypted handles
*
* @example
* ```typescript
* // Create and initialize a client
* const client = new FhevmClient({
* provider: window.ethereum,
* mockChains: { 31337: "http://localhost:8545" }
* });
*
* await client.initialize();
*
* // Encrypt a value
* const encrypted = await client.createEncryptedInput(
* "0x...", // contract address
* "0x..." // user address
* );
* encrypted.add32(42);
* const result = await encrypted.encrypt();
* ```
*/
export class FhevmClient {
private instance: FhevmInstance | undefined;
private config: FhevmClientConfig;
private status: FhevmClientStatus = "idle";
private initializationPromise: Promise<void> | undefined;
private abortController: AbortController | undefined;

/**
* Creates a new FHEVM client
*
* @param config - Configuration options
*/
constructor(config: FhevmClientConfig) {
this.config = config;
this.abortController = new AbortController();
}

/**
* Initialize the FHEVM client
*
* This method must be called before using any encryption/decryption features.
* It loads the FHEVM SDK, initializes it, and creates an instance.
*
* @throws {FhevmError} If initialization fails
* @throws {FhevmAbortError} If initialization is aborted
*
* @example
* ```typescript
* const client = new FhevmClient({ provider: window.ethereum });
* await client.initialize();
* ```
*/
async initialize(): Promise<void> {
// If already initializing, return the existing promise
if (this.initializationPromise) {
return this.initializationPromise;
}

// If already initialized, return immediately
if (this.instance && this.status === "ready") {
return Promise.resolve();
}

this.initializationPromise = this._doInitialize();
return this.initializationPromise;
}

private async _doInitialize(): Promise<void> {
try {
this.updateStatus("sdk-loading");

const signal = this.config.signal || this.abortController!.signal;

this.instance = await createFhevmInstance({
provider: this.config.provider,
mockChains: this.config.mockChains,
signal,
onStatusChange: (status) => {
this.updateStatus(status as FhevmClientStatus);
},
});

this.updateStatus("ready");
} catch (error) {
this.updateStatus("error");

if (error instanceof FhevmAbortError) {
throw error;
}

throw new FhevmError(
"INITIALIZATION_FAILED",
"Failed to initialize FHEVM client",
"Please check your provider and network configuration",
{ cause: error }
);
}
}

private updateStatus(status: FhevmClientStatus): void {
this.status = status;
if (this.config.onStatusChange) {
this.config.onStatusChange(status);
}
}

/**
* Get the current FHEVM instance
*
* @returns The FHEVM instance, or undefined if not initialized
*/
getInstance(): FhevmInstance | undefined {
return this.instance;
}

/**
* Get the current status of the client
*
* @returns The current status
*/
getStatus(): FhevmClientStatus {
return this.status;
}

/**
* Check if the client is ready for encryption/decryption
*
* @returns True if the client is ready
*/
isReady(): boolean {
return this.status === "ready" && this.instance !== undefined;
}

/**
* Create an encrypted input builder
*
* @param contractAddress - The contract address
* @param userAddress - The user address
* @returns An encrypted input builder
*
* @throws {FhevmError} If the client is not initialized
*
* @example
* ```typescript
* const input = client.createEncryptedInput(
* "0x1234...",
* "0x5678..."
* );
* input.add32(42);
* input.add64(1000n);
* const result = await input.encrypt();
* ```
*/
createEncryptedInput(
contractAddress: string,
userAddress: string
): RelayerEncryptedInput {
if (!this.instance) {
throw createError(
ErrorCodes.NOT_INITIALIZED,
"FHEVM client is not initialized"
);
}

return this.instance.createEncryptedInput(
contractAddress,
userAddress
) as RelayerEncryptedInput;
}

/**
* Abort any ongoing initialization
*
* This will cancel the initialization process if it's still running.
*/
abort(): void {
if (this.abortController) {
this.abortController.abort();
}
}

/**
* Dispose of the client and clean up resources
*/
dispose(): void {
this.abort();
this.instance = undefined;
this.initializationPromise = undefined;
this.status = "idle";
}
}

63 changes: 63 additions & 0 deletions packages/fhevm-sdk/src/core/createFhevmClient.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { FhevmClient, FhevmClientConfig } from "./FhevmClient";
import { getNetworkPreset } from "./presets";

/**
* Create and initialize an FHEVM client in one step
*
* This is a convenience function that creates a new FhevmClient
* and immediately initializes it.
*
* @param config - Configuration options for the client, or a network preset name
* @returns A promise that resolves to an initialized FhevmClient
*
* @throws {FhevmError} If initialization fails
* @throws {FhevmAbortError} If initialization is aborted
*
* @example
* ```typescript
* // Simple usage with provider
* const client = await createFhevmClient({
* provider: window.ethereum
* });
*
* // With mock chains for local development
* const client = await createFhevmClient({
* provider: "http://localhost:8545",
* mockChains: {
* 31337: "http://localhost:8545"
* }
* });
*
* // Using a network preset
* const client = await createFhevmClient("localhost");
*
* // With status updates
* const client = await createFhevmClient({
* provider: window.ethereum,
* onStatusChange: (status) => {
* console.log('FHEVM status:', status);
* }
* });
* ```
*/
export async function createFhevmClient(
config: FhevmClientConfig | string
): Promise<FhevmClient> {
let clientConfig: FhevmClientConfig;

// If config is a string, treat it as a network preset name
if (typeof config === "string") {
const preset = getNetworkPreset(config);
clientConfig = {
provider: preset.rpcUrl,
mockChains: preset.mockChains,
};
} else {
clientConfig = config;
}

const client = new FhevmClient(clientConfig);
await client.initialize();
return client;
}

Loading