@pyratzlabs/react-fhevm-utils 3.5.7 → 3.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/client/context/FhevmProvider.d.ts +4 -2
- package/dist/client/context/FhevmProvider.js +15 -10
- package/dist/client/hooks/useFhevmInstance.d.ts +1 -0
- package/dist/client/hooks/useFhevmInstance.js +2 -2
- package/dist/lib/fhevmManager.d.ts +68 -0
- package/dist/lib/fhevmManager.js +231 -0
- package/package.json +1 -1
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
import React, { ReactNode } from "react";
|
|
2
|
+
import { FhevmInstance, FhevmInstanceConfig, FhevmStatus } from "../../lib/fhevmManager";
|
|
2
3
|
import { Network } from "../../types/fhevmConfig";
|
|
3
|
-
import { FhevmInstance, FhevmInstanceConfig } from "@zama-fhe/relayer-sdk/bundle";
|
|
4
4
|
interface ProviderProps {
|
|
5
5
|
children: ReactNode;
|
|
6
6
|
network?: Network;
|
|
7
7
|
config?: Partial<FhevmInstanceConfig>;
|
|
8
|
+
enabled?: boolean;
|
|
8
9
|
}
|
|
9
10
|
interface ContextProps {
|
|
10
11
|
instance?: FhevmInstance;
|
|
12
|
+
status: FhevmStatus;
|
|
11
13
|
config?: Partial<FhevmInstanceConfig>;
|
|
12
14
|
}
|
|
13
15
|
export declare const FhevmContext: React.Context<ContextProps>;
|
|
14
|
-
declare const FhevmProvider: ({ children, network, config, }: ProviderProps) => import("react/jsx-runtime").JSX.Element;
|
|
16
|
+
declare const FhevmProvider: ({ children, network, config, enabled, }: ProviderProps) => import("react/jsx-runtime").JSX.Element;
|
|
15
17
|
export declare const useFhevmContext: () => ContextProps;
|
|
16
18
|
export default FhevmProvider;
|
|
@@ -8,27 +8,32 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
8
8
|
});
|
|
9
9
|
};
|
|
10
10
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
11
|
-
import { createContext, useContext, useEffect,
|
|
12
|
-
import {
|
|
11
|
+
import { createContext, useContext, useEffect, useSyncExternalStore, } from "react";
|
|
12
|
+
import { fhevmManager, } from "../../lib/fhevmManager";
|
|
13
13
|
export const FhevmContext = createContext({
|
|
14
14
|
instance: undefined,
|
|
15
|
+
status: "uninitialized",
|
|
15
16
|
config: undefined,
|
|
16
17
|
});
|
|
17
|
-
const FhevmProvider = ({ children, network = "sepolia", config, }) => {
|
|
18
|
-
const
|
|
18
|
+
const FhevmProvider = ({ children, network = "sepolia", config, enabled = true, }) => {
|
|
19
|
+
const status = useSyncExternalStore(fhevmManager.subscribe, fhevmManager.getStatus, fhevmManager.getStatus);
|
|
19
20
|
useEffect(() => {
|
|
20
|
-
|
|
21
|
+
if (!enabled)
|
|
22
|
+
return;
|
|
23
|
+
const initFhevm = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
21
24
|
try {
|
|
22
|
-
|
|
23
|
-
|
|
25
|
+
yield fhevmManager.load();
|
|
26
|
+
yield fhevmManager.initSDK();
|
|
27
|
+
yield fhevmManager.createInstance({ network, customConfig: config });
|
|
24
28
|
}
|
|
25
29
|
catch (error) {
|
|
26
30
|
throw new Error(`Failed to initialize FHEVM: ${error}. With config: ${JSON.stringify(config)}`);
|
|
27
31
|
}
|
|
28
32
|
});
|
|
29
|
-
initFhevm(
|
|
30
|
-
}, [config, network]);
|
|
31
|
-
|
|
33
|
+
initFhevm();
|
|
34
|
+
}, [config, network, enabled]);
|
|
35
|
+
const instance = status === "ready" ? fhevmManager.getInstance() : undefined;
|
|
36
|
+
return (_jsx(FhevmContext.Provider, { value: { instance, status, config }, children: children }));
|
|
32
37
|
};
|
|
33
38
|
export const useFhevmContext = () => useContext(FhevmContext);
|
|
34
39
|
export default FhevmProvider;
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import type { FhevmInstanceConfig, FhevmInstance } from "@zama-fhe/relayer-sdk/bundle";
|
|
2
|
+
import { Network } from "../types/fhevmConfig";
|
|
3
|
+
export type { FhevmInstance, FhevmInstanceConfig };
|
|
4
|
+
export type FhevmStatus = "uninitialized" | "creating" | "ready" | "error" | "not-supported";
|
|
5
|
+
export type RelayerSDK = {
|
|
6
|
+
initSDK: (options?: {
|
|
7
|
+
tfheParams?: unknown;
|
|
8
|
+
kmsParams?: unknown;
|
|
9
|
+
thread?: number;
|
|
10
|
+
}) => Promise<boolean>;
|
|
11
|
+
createInstance: (config: FhevmInstanceConfig) => Promise<FhevmInstance>;
|
|
12
|
+
SepoliaConfig: FhevmInstanceConfig;
|
|
13
|
+
MainnetConfig: FhevmInstanceConfig;
|
|
14
|
+
};
|
|
15
|
+
export type CreateInstanceOptions = {
|
|
16
|
+
network?: Network;
|
|
17
|
+
customConfig?: Partial<FhevmInstanceConfig>;
|
|
18
|
+
};
|
|
19
|
+
declare class FhevmManager {
|
|
20
|
+
private sdk;
|
|
21
|
+
private instance;
|
|
22
|
+
private loadPromise;
|
|
23
|
+
private instancePromise;
|
|
24
|
+
private _status;
|
|
25
|
+
private listeners;
|
|
26
|
+
private currentNetwork;
|
|
27
|
+
get status(): FhevmStatus;
|
|
28
|
+
get isLoaded(): boolean;
|
|
29
|
+
/**
|
|
30
|
+
* Subscribe to status changes. Returns an unsubscribe function.
|
|
31
|
+
* Compatible with React's useSyncExternalStore.
|
|
32
|
+
*/
|
|
33
|
+
subscribe: (listener: () => void) => (() => void);
|
|
34
|
+
/**
|
|
35
|
+
* Get current status. For useSyncExternalStore.
|
|
36
|
+
*/
|
|
37
|
+
getStatus: () => FhevmStatus;
|
|
38
|
+
/**
|
|
39
|
+
* Set status and notify listeners.
|
|
40
|
+
* External callers should only use 'uninitialized' or 'not-supported'.
|
|
41
|
+
* Setting 'uninitialized' also clears the instance.
|
|
42
|
+
*/
|
|
43
|
+
setStatus: (status: FhevmStatus) => void;
|
|
44
|
+
private isBrowser;
|
|
45
|
+
private isValidSDK;
|
|
46
|
+
private getNetworkConfig;
|
|
47
|
+
/**
|
|
48
|
+
* Loads the relayer SDK script into the page.
|
|
49
|
+
* Safe to call multiple times - subsequent calls return the same promise.
|
|
50
|
+
*/
|
|
51
|
+
load: () => Promise<void>;
|
|
52
|
+
/**
|
|
53
|
+
* Initializes the SDK after loading.
|
|
54
|
+
* Must be called after load() completes.
|
|
55
|
+
*/
|
|
56
|
+
initSDK: () => Promise<boolean>;
|
|
57
|
+
/**
|
|
58
|
+
* Creates and initializes the FHEVM instance.
|
|
59
|
+
* Safe to call multiple times - returns existing promise if already creating.
|
|
60
|
+
*/
|
|
61
|
+
createInstance: (options?: CreateInstanceOptions) => Promise<void>;
|
|
62
|
+
/**
|
|
63
|
+
* Gets the ready FHEVM instance.
|
|
64
|
+
* @throws Error if instance is not ready
|
|
65
|
+
*/
|
|
66
|
+
getInstance: () => FhevmInstance;
|
|
67
|
+
}
|
|
68
|
+
export declare const fhevmManager: FhevmManager;
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
import { mainnet, sepolia } from "viem/chains";
|
|
12
|
+
import { ZAMA_RELAYER_CDN_URL } from "../config";
|
|
13
|
+
// ============================================================================
|
|
14
|
+
// FhevmManager Class
|
|
15
|
+
// ============================================================================
|
|
16
|
+
class FhevmManager {
|
|
17
|
+
constructor() {
|
|
18
|
+
this._status = "uninitialized";
|
|
19
|
+
this.listeners = new Set();
|
|
20
|
+
this.currentNetwork = "sepolia";
|
|
21
|
+
// ==========================================================================
|
|
22
|
+
// Subscription (for React's useSyncExternalStore)
|
|
23
|
+
// ==========================================================================
|
|
24
|
+
/**
|
|
25
|
+
* Subscribe to status changes. Returns an unsubscribe function.
|
|
26
|
+
* Compatible with React's useSyncExternalStore.
|
|
27
|
+
*/
|
|
28
|
+
this.subscribe = (listener) => {
|
|
29
|
+
this.listeners.add(listener);
|
|
30
|
+
return () => this.listeners.delete(listener);
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* Get current status. For useSyncExternalStore.
|
|
34
|
+
*/
|
|
35
|
+
this.getStatus = () => {
|
|
36
|
+
return this._status;
|
|
37
|
+
};
|
|
38
|
+
/**
|
|
39
|
+
* Set status and notify listeners.
|
|
40
|
+
* External callers should only use 'uninitialized' or 'not-supported'.
|
|
41
|
+
* Setting 'uninitialized' also clears the instance.
|
|
42
|
+
*/
|
|
43
|
+
this.setStatus = (status) => {
|
|
44
|
+
if (status === "uninitialized") {
|
|
45
|
+
this.instance = undefined;
|
|
46
|
+
this.instancePromise = undefined;
|
|
47
|
+
}
|
|
48
|
+
if (this._status !== status) {
|
|
49
|
+
this._status = status;
|
|
50
|
+
this.listeners.forEach((listener) => listener());
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
// ==========================================================================
|
|
54
|
+
// Public Methods
|
|
55
|
+
// ==========================================================================
|
|
56
|
+
/**
|
|
57
|
+
* Loads the relayer SDK script into the page.
|
|
58
|
+
* Safe to call multiple times - subsequent calls return the same promise.
|
|
59
|
+
*/
|
|
60
|
+
this.load = () => __awaiter(this, void 0, void 0, function* () {
|
|
61
|
+
if (!this.isBrowser()) {
|
|
62
|
+
throw new Error("Relayer SDK can only be used in the browser");
|
|
63
|
+
}
|
|
64
|
+
// Already loaded
|
|
65
|
+
if (this.isLoaded) {
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
// Return existing promise if already loading
|
|
69
|
+
if (this.loadPromise) {
|
|
70
|
+
return this.loadPromise;
|
|
71
|
+
}
|
|
72
|
+
// Check if SDK is already on window (loaded by another source)
|
|
73
|
+
const windowWithSDK = window;
|
|
74
|
+
if (this.isValidSDK(windowWithSDK.relayerSDK)) {
|
|
75
|
+
this.sdk = windowWithSDK.relayerSDK;
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
// Check if script tag already exists
|
|
79
|
+
const existingScript = document.querySelector(`script[src="${ZAMA_RELAYER_CDN_URL}"]`);
|
|
80
|
+
if (existingScript) {
|
|
81
|
+
// Wait for it to load and populate window
|
|
82
|
+
this.loadPromise = new Promise((resolve, reject) => {
|
|
83
|
+
const checkInterval = setInterval(() => {
|
|
84
|
+
if (this.isValidSDK(windowWithSDK.relayerSDK)) {
|
|
85
|
+
clearInterval(checkInterval);
|
|
86
|
+
this.sdk = windowWithSDK.relayerSDK;
|
|
87
|
+
resolve();
|
|
88
|
+
}
|
|
89
|
+
}, 50);
|
|
90
|
+
// Timeout after 10 seconds
|
|
91
|
+
setTimeout(() => {
|
|
92
|
+
clearInterval(checkInterval);
|
|
93
|
+
this.loadPromise = undefined;
|
|
94
|
+
reject(new Error("Timeout waiting for existing Relayer SDK script to load"));
|
|
95
|
+
}, 10000);
|
|
96
|
+
});
|
|
97
|
+
return this.loadPromise;
|
|
98
|
+
}
|
|
99
|
+
// Load the script
|
|
100
|
+
this.loadPromise = new Promise((resolve, reject) => {
|
|
101
|
+
const script = document.createElement("script");
|
|
102
|
+
script.src = ZAMA_RELAYER_CDN_URL;
|
|
103
|
+
script.type = "text/javascript";
|
|
104
|
+
script.async = true;
|
|
105
|
+
script.onload = () => {
|
|
106
|
+
const windowWithSDK = window;
|
|
107
|
+
if (this.isValidSDK(windowWithSDK.relayerSDK)) {
|
|
108
|
+
this.sdk = windowWithSDK.relayerSDK;
|
|
109
|
+
resolve();
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
this.loadPromise = undefined;
|
|
113
|
+
reject(new Error("Relayer SDK script loaded but SDK is invalid"));
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
script.onerror = () => {
|
|
117
|
+
this.loadPromise = undefined;
|
|
118
|
+
reject(new Error(`Failed to load Relayer SDK from ${ZAMA_RELAYER_CDN_URL}`));
|
|
119
|
+
};
|
|
120
|
+
document.head.appendChild(script);
|
|
121
|
+
});
|
|
122
|
+
return this.loadPromise;
|
|
123
|
+
});
|
|
124
|
+
/**
|
|
125
|
+
* Initializes the SDK after loading.
|
|
126
|
+
* Must be called after load() completes.
|
|
127
|
+
*/
|
|
128
|
+
this.initSDK = () => __awaiter(this, void 0, void 0, function* () {
|
|
129
|
+
if (!this.sdk) {
|
|
130
|
+
throw new Error("SDK not loaded. Call load() first.");
|
|
131
|
+
}
|
|
132
|
+
return this.sdk.initSDK();
|
|
133
|
+
});
|
|
134
|
+
/**
|
|
135
|
+
* Creates and initializes the FHEVM instance.
|
|
136
|
+
* Safe to call multiple times - returns existing promise if already creating.
|
|
137
|
+
*/
|
|
138
|
+
this.createInstance = (...args_1) => __awaiter(this, [...args_1], void 0, function* (options = {}) {
|
|
139
|
+
var _a;
|
|
140
|
+
const network = (_a = options.network) !== null && _a !== void 0 ? _a : "sepolia";
|
|
141
|
+
// Return early if already created for same network
|
|
142
|
+
if (this.instancePromise && network === this.currentNetwork) {
|
|
143
|
+
yield this.instancePromise;
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
if (!this.sdk) {
|
|
147
|
+
throw new Error("SDK not loaded. Call load() and initSDK() first.");
|
|
148
|
+
}
|
|
149
|
+
this.currentNetwork = network;
|
|
150
|
+
const baseConfig = this.getNetworkConfig(network);
|
|
151
|
+
try {
|
|
152
|
+
this.setStatus("creating");
|
|
153
|
+
const config = Object.assign(Object.assign({}, baseConfig), options.customConfig);
|
|
154
|
+
this.instancePromise = this.sdk.createInstance(config);
|
|
155
|
+
this.instance = yield this.instancePromise;
|
|
156
|
+
this.setStatus("ready");
|
|
157
|
+
}
|
|
158
|
+
catch (error) {
|
|
159
|
+
console.error("FHEVM instance creation failed:", error);
|
|
160
|
+
this.setStatus("error");
|
|
161
|
+
this.instancePromise = undefined;
|
|
162
|
+
throw error;
|
|
163
|
+
}
|
|
164
|
+
});
|
|
165
|
+
/**
|
|
166
|
+
* Gets the ready FHEVM instance.
|
|
167
|
+
* @throws Error if instance is not ready
|
|
168
|
+
*/
|
|
169
|
+
this.getInstance = () => {
|
|
170
|
+
if (!this.instance) {
|
|
171
|
+
throw new Error("FHEVM instance not created. Call createInstance() first or wait for initialization.");
|
|
172
|
+
}
|
|
173
|
+
if (this._status === "creating") {
|
|
174
|
+
throw new Error("FHEVM instance is still being created. Please wait for initialization.");
|
|
175
|
+
}
|
|
176
|
+
if (this._status === "error") {
|
|
177
|
+
throw new Error("FHEVM instance creation failed. Check previous errors or reinitialize.");
|
|
178
|
+
}
|
|
179
|
+
if (this._status === "not-supported") {
|
|
180
|
+
throw new Error(`FHEVM is not supported on the current network. Please switch to a supported network.`);
|
|
181
|
+
}
|
|
182
|
+
if (this._status !== "ready") {
|
|
183
|
+
throw new Error(`FHEVM instance is not ready. Current status: ${this._status}`);
|
|
184
|
+
}
|
|
185
|
+
return this.instance;
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
// ==========================================================================
|
|
189
|
+
// Getters
|
|
190
|
+
// ==========================================================================
|
|
191
|
+
get status() {
|
|
192
|
+
return this._status;
|
|
193
|
+
}
|
|
194
|
+
get isLoaded() {
|
|
195
|
+
return this.sdk !== undefined && this.isValidSDK(this.sdk);
|
|
196
|
+
}
|
|
197
|
+
// ==========================================================================
|
|
198
|
+
// Private Helpers
|
|
199
|
+
// ==========================================================================
|
|
200
|
+
isBrowser() {
|
|
201
|
+
return typeof window === "object";
|
|
202
|
+
}
|
|
203
|
+
isValidSDK(sdk) {
|
|
204
|
+
if (!sdk || typeof sdk !== "object")
|
|
205
|
+
return false;
|
|
206
|
+
const relayerSDK = sdk;
|
|
207
|
+
return (typeof relayerSDK.initSDK === "function" &&
|
|
208
|
+
typeof relayerSDK.createInstance === "function" &&
|
|
209
|
+
typeof relayerSDK.SepoliaConfig === "object" &&
|
|
210
|
+
typeof relayerSDK.MainnetConfig === "object");
|
|
211
|
+
}
|
|
212
|
+
getNetworkConfig(network) {
|
|
213
|
+
if (!this.sdk) {
|
|
214
|
+
throw new Error("SDK not loaded. Call load() first.");
|
|
215
|
+
}
|
|
216
|
+
const configByChainId = {
|
|
217
|
+
[sepolia.id]: this.sdk.SepoliaConfig,
|
|
218
|
+
[mainnet.id]: this.sdk.MainnetConfig,
|
|
219
|
+
};
|
|
220
|
+
const chainId = network === "mainnet" ? mainnet.id : sepolia.id;
|
|
221
|
+
const config = configByChainId[chainId];
|
|
222
|
+
if (!config) {
|
|
223
|
+
throw new Error(`No relayer SDK config found for network: ${network}`);
|
|
224
|
+
}
|
|
225
|
+
return config;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
// ============================================================================
|
|
229
|
+
// Singleton Export
|
|
230
|
+
// ============================================================================
|
|
231
|
+
export const fhevmManager = new FhevmManager();
|