@zama-fhe/sdk 3.3.0 → 3.3.1-alpha.2
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/README.md +0 -2
- package/dist/cjs/base-signer.cjs +1 -1
- package/dist/cjs/base-signer.cjs.map +1 -1
- package/dist/cjs/chains.cjs +1 -1
- package/dist/cjs/chains.cjs.map +1 -1
- package/dist/cjs/cleartext/index.cjs +1 -1
- package/dist/cjs/cleartext.cjs +2 -0
- package/dist/cjs/cleartext.cjs.map +1 -0
- package/dist/cjs/ethers/index.cjs.map +1 -1
- package/dist/cjs/fhevm-relayer.cjs +2 -0
- package/dist/cjs/fhevm-relayer.cjs.map +1 -0
- package/dist/cjs/index.cjs +1 -1
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/node/index.cjs +2 -0
- package/dist/cjs/node/index.cjs.map +1 -0
- package/dist/cjs/query/index.cjs +1 -1
- package/dist/cjs/query/index.cjs.map +1 -1
- package/dist/cjs/relayer.cjs +1 -1
- package/dist/cjs/relayer.cjs.map +1 -1
- package/dist/cjs/token.cjs +1 -1
- package/dist/cjs/token.cjs.map +1 -1
- package/dist/cjs/viem/index.cjs +1 -1
- package/dist/cjs/viem/index.cjs.map +1 -1
- package/dist/cjs/web/index.cjs +1 -590
- package/dist/cjs/web/index.cjs.map +1 -1
- package/dist/cjs/wrappers-registry.cjs.map +1 -1
- package/dist/esm/base-signer-CI-FYai2.js +2 -0
- package/dist/esm/base-signer-CI-FYai2.js.map +1 -0
- package/dist/esm/{base-signer-C3Wc5oi9.d.ts → base-signer-Q2EJECrc.d.ts} +2 -2
- package/dist/esm/chains/index.d.ts +2 -3
- package/dist/esm/chains/index.js +1 -1
- package/dist/esm/chains-CLv9HEJ_.js +2 -0
- package/dist/esm/chains-CLv9HEJ_.js.map +1 -0
- package/dist/esm/cleartext/index.d.ts +3 -2
- package/dist/esm/cleartext/index.js +1 -1
- package/dist/esm/cleartext-BfXTVQu-.js +2 -0
- package/dist/esm/cleartext-BfXTVQu-.js.map +1 -0
- package/dist/esm/cleartext-U6sPs_Kd.d.ts +19 -0
- package/dist/esm/ethers/index.d.ts +4 -5
- package/dist/esm/ethers/index.js +1 -1
- package/dist/esm/ethers/index.js.map +1 -1
- package/dist/esm/fhevm-relayer-B-lEKfPQ.js +2 -0
- package/dist/esm/fhevm-relayer-B-lEKfPQ.js.map +1 -0
- package/dist/esm/index-BnIp_-Ak.d.ts +328 -0
- package/dist/esm/{index-CYBH0nyo.d.ts → index-D66eC3G7.d.ts} +60 -13
- package/dist/esm/index.d.ts +8 -11
- package/dist/esm/index.js +1 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/node/index.d.ts +5 -194
- package/dist/esm/node/index.js +1 -1
- package/dist/esm/node/index.js.map +1 -1
- package/dist/esm/query/index.d.ts +3 -380
- package/dist/esm/query/index.js +1 -1
- package/dist/esm/query/index.js.map +1 -1
- package/dist/esm/relayer-BYq9bLh8.js +2 -0
- package/dist/esm/relayer-BYq9bLh8.js.map +1 -0
- package/dist/esm/token-CmHTAAWW.js +2 -0
- package/dist/esm/token-CmHTAAWW.js.map +1 -0
- package/dist/esm/{types-piBsdTIP.d.ts → types-Bov8XY36.d.ts} +3 -3
- package/dist/esm/{types-OHIaPwCy.d.ts → types-BpvL50qX.d.ts} +3 -3
- package/dist/esm/types-C-WTLcHZ.d.ts +988 -0
- package/dist/esm/viem/index.d.ts +4 -5
- package/dist/esm/viem/index.js +1 -1
- package/dist/esm/viem/index.js.map +1 -1
- package/dist/esm/web/index.d.ts +6 -9
- package/dist/esm/web/index.js +1 -590
- package/dist/esm/web/index.js.map +1 -1
- package/dist/esm/{wrappers-registry-C1oK8Bi6.js → wrappers-registry-xZWzyAi4.js} +2 -2
- package/dist/esm/wrappers-registry-xZWzyAi4.js.map +1 -0
- package/dist/esm/{index-C1Mtc8Rw.d.ts → zama-sdk-q4_qt64Q.d.ts} +1471 -1102
- package/package.json +6 -2
- package/dist/cjs/assertions.cjs +0 -2
- package/dist/cjs/assertions.cjs.map +0 -1
- package/dist/cjs/encryption.cjs +0 -2
- package/dist/cjs/encryption.cjs.map +0 -1
- package/dist/cjs/relayer-cleartext.cjs +0 -2
- package/dist/cjs/relayer-cleartext.cjs.map +0 -1
- package/dist/cjs/relayer-sdk.worker.js +0 -589
- package/dist/cjs/timeout.cjs +0 -2
- package/dist/cjs/timeout.cjs.map +0 -1
- package/dist/cjs/validation.cjs +0 -2
- package/dist/cjs/validation.cjs.map +0 -1
- package/dist/esm/assertions-zYseN5Xz.js +0 -2
- package/dist/esm/assertions-zYseN5Xz.js.map +0 -1
- package/dist/esm/base-signer-DjytqPkd.js +0 -2
- package/dist/esm/base-signer-DjytqPkd.js.map +0 -1
- package/dist/esm/chains-CXcO1DBP.js +0 -2
- package/dist/esm/chains-CXcO1DBP.js.map +0 -1
- package/dist/esm/cleartext-DAkOTlrE.d.ts +0 -19
- package/dist/esm/cleartext-rTWFctyd.js +0 -2
- package/dist/esm/cleartext-rTWFctyd.js.map +0 -1
- package/dist/esm/encryption-x4Z_Icqk.js +0 -2
- package/dist/esm/encryption-x4Z_Icqk.js.map +0 -1
- package/dist/esm/hex-A72GrTf9.js +0 -2
- package/dist/esm/hex-A72GrTf9.js.map +0 -1
- package/dist/esm/indexeddb-storage-Mi-fCWN3.js +0 -2
- package/dist/esm/indexeddb-storage-Mi-fCWN3.js.map +0 -1
- package/dist/esm/memory-storage-fp9RY5BQ.js +0 -2
- package/dist/esm/memory-storage-fp9RY5BQ.js.map +0 -1
- package/dist/esm/node/relayer-sdk.node-worker.d.ts +0 -1
- package/dist/esm/node/relayer-sdk.node-worker.js +0 -2
- package/dist/esm/node/relayer-sdk.node-worker.js.map +0 -1
- package/dist/esm/relayer-cleartext-Bp6jtxDd.js +0 -2
- package/dist/esm/relayer-cleartext-Bp6jtxDd.js.map +0 -1
- package/dist/esm/relayer-cleartext-CSeMlmGO.d.ts +0 -844
- package/dist/esm/relayer-eavK5bfZ.js +0 -2
- package/dist/esm/relayer-eavK5bfZ.js.map +0 -1
- package/dist/esm/relayer-sdk.worker.js +0 -589
- package/dist/esm/timeout-CNVfiaJD.js +0 -2
- package/dist/esm/timeout-CNVfiaJD.js.map +0 -1
- package/dist/esm/token-Bt33g0zQ.js +0 -2
- package/dist/esm/token-Bt33g0zQ.js.map +0 -1
- package/dist/esm/types-BgczXqW2.d.ts +0 -49
- package/dist/esm/types-Gn5MeDsr.d.ts +0 -669
- package/dist/esm/validation-DLszOweh.js +0 -2
- package/dist/esm/validation-DLszOweh.js.map +0 -1
- package/dist/esm/worker.base-client-BVnT4yJa.js +0 -2
- package/dist/esm/worker.base-client-BVnT4yJa.js.map +0 -1
- package/dist/esm/wrappers-registry-C1oK8Bi6.js.map +0 -1
package/dist/esm/web/index.js
CHANGED
|
@@ -1,591 +1,2 @@
|
|
|
1
|
-
import{
|
|
2
|
-
//#region src/utils/error.ts
|
|
3
|
-
/** Coerce an unknown caught value to an Error instance. */
|
|
4
|
-
function toError(error) {
|
|
5
|
-
if (error instanceof Error) return error;
|
|
6
|
-
if (typeof error === "object" && error !== null && "message" in error) return new Error(String(error.message));
|
|
7
|
-
return new Error(String(error));
|
|
8
|
-
}
|
|
9
|
-
/** Properties that may nest a lower-level cause across viem / ethers / fetch. */
|
|
10
|
-
const NESTED_ERROR_KEYS = [
|
|
11
|
-
"cause",
|
|
12
|
-
"error",
|
|
13
|
-
"info"
|
|
14
|
-
];
|
|
15
|
-
/**
|
|
16
|
-
* ethers reports its HTTP status only as a string on \`info.responseStatus\`
|
|
17
|
-
* (e.g. \`"429 Too Many Requests"\`). Read it directly off the node — not via the
|
|
18
|
-
* cause-chain flattening, which descends a single branch — so it survives even
|
|
19
|
-
* when the error also carries an \`error\` link that would be walked first.
|
|
20
|
-
*/
|
|
21
|
-
function responseStatusFromNode(node) {
|
|
22
|
-
const info = node.info;
|
|
23
|
-
if (info !== null && typeof info === "object") {
|
|
24
|
-
const responseStatus = info.responseStatus;
|
|
25
|
-
if (typeof responseStatus === "string") return responseStatus;
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
/**
|
|
29
|
-
* Copy the scalar signal fields the classifier keys on from a raw error node
|
|
30
|
-
* onto the envelope, only where not already set — so a shallower node keeps
|
|
31
|
-
* priority. Also lifts ethers' string \`info.responseStatus\` and the relayer's
|
|
32
|
-
* \`Retry-After\` header (both destroyed by structured clone: the header lives on
|
|
33
|
-
* a non-cloneable \`Headers\`, the status on a nested \`info\` the flattening skips).
|
|
34
|
-
*/
|
|
35
|
-
function captureNodeSignals(node, into) {
|
|
36
|
-
if (into.code === void 0 && (typeof node.code === "string" || typeof node.code === "number")) into.code = node.code;
|
|
37
|
-
if (into.status === void 0 && typeof node.status === "number") into.status = node.status;
|
|
38
|
-
if (into.statusCode === void 0 && typeof node.statusCode === "number") into.statusCode = node.statusCode;
|
|
39
|
-
if (into.responseStatus === void 0) {
|
|
40
|
-
const responseStatus = responseStatusFromNode(node);
|
|
41
|
-
if (responseStatus !== void 0) into.responseStatus = responseStatus;
|
|
42
|
-
}
|
|
43
|
-
if (into.retryAfter === void 0) if (typeof node.retryAfter === "number") into.retryAfter = node.retryAfter;
|
|
44
|
-
else {
|
|
45
|
-
const fromHeader = retryAfterFromHeader(node);
|
|
46
|
-
if (fromHeader !== void 0) into.retryAfter = fromHeader;
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
/** Lift any still-missing scalar signal from an already-serialized child up. */
|
|
50
|
-
function hoistMissingSignals(from, into) {
|
|
51
|
-
if (into.code === void 0 && from.code !== void 0) into.code = from.code;
|
|
52
|
-
if (into.status === void 0 && from.status !== void 0) into.status = from.status;
|
|
53
|
-
if (into.statusCode === void 0 && from.statusCode !== void 0) into.statusCode = from.statusCode;
|
|
54
|
-
if (into.responseStatus === void 0 && from.responseStatus !== void 0) into.responseStatus = from.responseStatus;
|
|
55
|
-
if (into.retryAfter === void 0 && from.retryAfter !== void 0) into.retryAfter = from.retryAfter;
|
|
56
|
-
}
|
|
57
|
-
/**
|
|
58
|
-
* Flatten an error (and its \`cause\` / \`error\` / \`info\` chain) into a
|
|
59
|
-
* structured-clone-safe {@link SerializedError}. Depth-bounded to mirror
|
|
60
|
-
* {@link findInErrorChain} and guard against cyclic structures.
|
|
61
|
-
*/
|
|
62
|
-
function serializeError(error, depth = 6) {
|
|
63
|
-
const coerced = toError(error);
|
|
64
|
-
const node = typeof error === "object" && error !== null ? error : coerced;
|
|
65
|
-
const serialized = {
|
|
66
|
-
name: coerced.name,
|
|
67
|
-
message: coerced.message
|
|
68
|
-
};
|
|
69
|
-
captureNodeSignals(node, serialized);
|
|
70
|
-
if (depth > 0) for (const key of NESTED_ERROR_KEYS) {
|
|
71
|
-
const next = node[key];
|
|
72
|
-
if (next !== void 0 && next !== null && typeof next === "object") {
|
|
73
|
-
const child = serializeError(next, depth - 1);
|
|
74
|
-
if (serialized.cause === void 0) serialized.cause = child;
|
|
75
|
-
hoistMissingSignals(child, serialized);
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
return serialized;
|
|
79
|
-
}
|
|
80
|
-
/**
|
|
81
|
-
* Parse an HTTP \`Retry-After\` header value into **seconds** (the SDK's duration
|
|
82
|
-
* unit and the header's own unit), or \`undefined\` when absent/unparseable. Per
|
|
83
|
-
* RFC 9110 the value is either a non-negative number of seconds (\`"120"\`) or an
|
|
84
|
-
* HTTP-date; a past date floors to \`0\`.
|
|
85
|
-
*/
|
|
86
|
-
function parseRetryAfterHeader(value) {
|
|
87
|
-
if (value === null || value === void 0) return;
|
|
88
|
-
const trimmed = value.trim();
|
|
89
|
-
if (trimmed === "") return;
|
|
90
|
-
if (/^\\d+$/.test(trimmed)) return Number(trimmed);
|
|
91
|
-
if (/[a-zA-Z]/.test(trimmed)) {
|
|
92
|
-
const dateMs = Date.parse(trimmed);
|
|
93
|
-
if (!Number.isNaN(dateMs)) return Math.max(0, Math.round((dateMs - Date.now()) / 1e3));
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
/** Read a \`Retry-After\` header (→ seconds) off a \`Headers\`-like object, if present. */
|
|
97
|
-
function readRetryAfterHeader(headers) {
|
|
98
|
-
if (headers === null || typeof headers !== "object") return;
|
|
99
|
-
const get = headers.get;
|
|
100
|
-
if (typeof get !== "function") return;
|
|
101
|
-
return parseRetryAfterHeader(get.call(headers, "Retry-After"));
|
|
102
|
-
}
|
|
103
|
-
/**
|
|
104
|
-
* A node's **relayer** \`Retry-After\` header (seconds), read from a wrapped fetch
|
|
105
|
-
* \`Response\` (the relayer SDK's \`cause.response.headers\`). The SDK owns the
|
|
106
|
-
* relayer fetch, so parsing the header there is legitimate.
|
|
107
|
-
*
|
|
108
|
-
* The viem/chain side (\`HttpRequestError.headers\`) is deliberately **not** read:
|
|
109
|
-
* for consumer RPC the integrator's viem/ethers transport already honors
|
|
110
|
-
* \`Retry-After\` in its own retry loop (and retries) before the error ever
|
|
111
|
-
* surfaces, so re-reading it here would re-implement a transport concern.
|
|
112
|
-
* Chain-RPC backoff is configured on the consumer's transport (\`chain.network\`).
|
|
113
|
-
*/
|
|
114
|
-
function retryAfterFromHeader(node) {
|
|
115
|
-
const response = node.response;
|
|
116
|
-
if (response !== null && typeof response === "object") return readRetryAfterHeader(response.headers);
|
|
117
|
-
}
|
|
118
|
-
//#endregion
|
|
119
|
-
//#region src/utils/assertions.ts
|
|
120
|
-
function assertObject(value, context) {
|
|
121
|
-
if (typeof value !== "object" || value === null || Array.isArray(value)) throw new TypeError(\`\${context} must be an object, got \${typeof value}\`);
|
|
122
|
-
}
|
|
123
|
-
function assertString(value, context) {
|
|
124
|
-
if (typeof value !== "string") throw new TypeError(\`\${context} must be a string, got \${typeof value}\`);
|
|
125
|
-
}
|
|
126
|
-
function assertFunction(value, context) {
|
|
127
|
-
if (typeof value !== "function") throw new TypeError(\`\${context} must be a function, got \${typeof value}\`);
|
|
128
|
-
}
|
|
129
|
-
/** Assert that \`obj[key]\` is a string. Narrows \`obj\` to include \`{ [key]: string }\`. */
|
|
130
|
-
function assertStringProp(obj, key, context) {
|
|
131
|
-
assertString(obj[key], context);
|
|
132
|
-
}
|
|
133
|
-
/** Assert that \`obj[key]\` is a function. Narrows \`obj\` to include \`{ [key]: F }\`. */
|
|
134
|
-
function assertFunctionProp(obj, key, context) {
|
|
135
|
-
assertFunction(obj[key], context);
|
|
136
|
-
}
|
|
137
|
-
function assertCondition(condition, message) {
|
|
138
|
-
if (!condition) throw new TypeError(message);
|
|
139
|
-
}
|
|
140
|
-
//#endregion
|
|
141
|
-
//#region src/utils/hex.ts
|
|
142
|
-
/** Normalize a un-prefixed hex payload to a 0x-prefixed \`Hex\` value. */
|
|
143
|
-
function prefixHex(value) {
|
|
144
|
-
return value.startsWith("0x") ? value : \`0x\${value}\`;
|
|
145
|
-
}
|
|
146
|
-
/** Convert a public \`Hex\` value back an unprefixed format. */
|
|
147
|
-
function unprefixHex(value) {
|
|
148
|
-
assertCondition(value.startsWith("0x"), \`Expected 0x-prefixed hex, got: \${value}\`);
|
|
149
|
-
return value.slice(2);
|
|
150
|
-
}
|
|
151
|
-
//#endregion
|
|
152
|
-
//#region src/worker/browser-extension.ts
|
|
153
|
-
function isValidRuntime(runtime) {
|
|
154
|
-
try {
|
|
155
|
-
assertObject(runtime, "runtime");
|
|
156
|
-
assertStringProp(runtime, "id", "runtime.id");
|
|
157
|
-
assertFunctionProp(runtime, "getURL", "runtime.getURL");
|
|
158
|
-
return true;
|
|
159
|
-
} catch {
|
|
160
|
-
return false;
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
/**
|
|
164
|
-
* Return the browser extension runtime object, or \`undefined\` outside extensions.
|
|
165
|
-
* Works across Chrome/Edge (\`chrome.runtime\`) and Firefox/Safari (\`browser.runtime\`).
|
|
166
|
-
* Extensions have restricted CSP that blocks \`blob:\` URLs, so callers use
|
|
167
|
-
* this to detect the environment and resolve file URLs via \`runtime.getURL\`.
|
|
168
|
-
*/
|
|
169
|
-
function getBrowserExtensionRuntime() {
|
|
170
|
-
const g = globalThis;
|
|
171
|
-
for (const ns of [g.chrome, g.browser]) try {
|
|
172
|
-
assertObject(ns, "ns");
|
|
173
|
-
if (isValidRuntime(ns.runtime)) return ns.runtime;
|
|
174
|
-
} catch {
|
|
175
|
-
continue;
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
//#endregion
|
|
179
|
-
//#region src/worker/relayer-sdk.worker.ts
|
|
180
|
-
const instances = /* @__PURE__ */ new Map();
|
|
181
|
-
const pending = /* @__PURE__ */ new Map();
|
|
182
|
-
const configs = /* @__PURE__ */ new Map();
|
|
183
|
-
/** Convert an FheChain to the FhevmInstanceConfig shape expected by createInstance. */
|
|
184
|
-
function toInstanceConfig(chain) {
|
|
185
|
-
return {
|
|
186
|
-
...chain,
|
|
187
|
-
chainId: chain.id
|
|
188
|
-
};
|
|
189
|
-
}
|
|
190
|
-
let sdkGlobal = null;
|
|
191
|
-
/**
|
|
192
|
-
* Get or lazily create an FhevmInstance for the given chain.
|
|
193
|
-
*/
|
|
194
|
-
async function getInstance(chainId) {
|
|
195
|
-
const existing = instances.get(chainId);
|
|
196
|
-
if (existing) return existing;
|
|
197
|
-
const inflight = pending.get(chainId);
|
|
198
|
-
if (inflight) return inflight;
|
|
199
|
-
const config = configs.get(chainId);
|
|
200
|
-
if (!config) throw new Error(\`No config for chain \${chainId}. Available: [\${[...configs.keys()].join(", ")}]\`);
|
|
201
|
-
if (!sdkGlobal) throw new Error("Relayer SDK is not initialized. Call INIT first.");
|
|
202
|
-
const promise = sdkGlobal.createInstance({
|
|
203
|
-
...toInstanceConfig(config),
|
|
204
|
-
batchRpcCalls: false
|
|
205
|
-
}).then((instance) => {
|
|
206
|
-
instances.set(chainId, instance);
|
|
207
|
-
pending.delete(chainId);
|
|
208
|
-
return instance;
|
|
209
|
-
}).catch((err) => {
|
|
210
|
-
pending.delete(chainId);
|
|
211
|
-
throw err;
|
|
212
|
-
});
|
|
213
|
-
pending.set(chainId, promise);
|
|
214
|
-
return promise;
|
|
215
|
-
}
|
|
216
|
-
function unreachableFheType(_) {
|
|
217
|
-
throw new Error("Unsupported FHE type");
|
|
218
|
-
}
|
|
219
|
-
const relayerUrls = /* @__PURE__ */ new Set();
|
|
220
|
-
let csrfTokenBase = "";
|
|
221
|
-
const CSRF_HEADER_NAME = "x-csrf-token";
|
|
222
|
-
const MUTATING_METHODS = /* @__PURE__ */ new Set([
|
|
223
|
-
"POST",
|
|
224
|
-
"PUT",
|
|
225
|
-
"DELETE",
|
|
226
|
-
"PATCH"
|
|
227
|
-
]);
|
|
228
|
-
/**
|
|
229
|
-
* Register relayer URLs from chain configs for fetch interception.
|
|
230
|
-
*/
|
|
231
|
-
function registerRelayerUrls(chainConfigs) {
|
|
232
|
-
for (const c of chainConfigs) if (c.relayerUrl) relayerUrls.add(c.relayerUrl);
|
|
233
|
-
}
|
|
234
|
-
/**
|
|
235
|
-
* Send a success response back to the main thread.
|
|
236
|
-
* Optionally transfers ArrayBuffers for zero-copy performance.
|
|
237
|
-
*/
|
|
238
|
-
function sendSuccess(id, type, data, transfer) {
|
|
239
|
-
const response = {
|
|
240
|
-
id,
|
|
241
|
-
type,
|
|
242
|
-
success: true,
|
|
243
|
-
data
|
|
244
|
-
};
|
|
245
|
-
return transfer ? self.postMessage(response, transfer) : self.postMessage(response);
|
|
246
|
-
}
|
|
247
|
-
/**
|
|
248
|
-
* Send an error response back to the main thread.
|
|
249
|
-
*/
|
|
250
|
-
function sendError(id, type, error) {
|
|
251
|
-
self.postMessage({
|
|
252
|
-
id,
|
|
253
|
-
type,
|
|
254
|
-
success: false,
|
|
255
|
-
error: error instanceof Error ? error.message : String(error),
|
|
256
|
-
serialized: serializeError(error)
|
|
257
|
-
});
|
|
258
|
-
}
|
|
259
|
-
const originalFetch = fetch;
|
|
260
|
-
/** Allowed CDN hostnames for loading the relayer SDK script. */
|
|
261
|
-
const ALLOWED_CDN_HOSTS = /* @__PURE__ */ new Set(["cdn.zama.org"]);
|
|
262
|
-
/**
|
|
263
|
-
* Validate the CDN URL supplied by the caller.
|
|
264
|
-
* Ensures only HTTPS URLs from approved hosts are used when loading
|
|
265
|
-
* SDK code into the worker.
|
|
266
|
-
*/
|
|
267
|
-
function validateCdnUrl(rawUrl) {
|
|
268
|
-
let url;
|
|
269
|
-
try {
|
|
270
|
-
url = new URL(rawUrl);
|
|
271
|
-
} catch {
|
|
272
|
-
throw new Error("Invalid CDN URL");
|
|
273
|
-
}
|
|
274
|
-
if (url.protocol !== "https:") throw new Error("CDN URL must use https");
|
|
275
|
-
if (!ALLOWED_CDN_HOSTS.has(url.hostname)) throw new Error(\`CDN URL host is not allowed: \${url.hostname}\`);
|
|
276
|
-
return url.toString();
|
|
277
|
-
}
|
|
278
|
-
/**
|
|
279
|
-
* Set up fetch interceptor to add credentials and CSRF token for relayer requests.
|
|
280
|
-
* Workers don't automatically include cookies, so we intercept fetch calls
|
|
281
|
-
* targeting our relayer proxy to inject credentials and CSRF headers.
|
|
282
|
-
*/
|
|
283
|
-
function setupFetchInterceptor() {
|
|
284
|
-
globalThis.fetch = async (input, init) => {
|
|
285
|
-
const url = typeof input === "string" ? input : input instanceof URL ? input.href : input.url;
|
|
286
|
-
const method = init?.method?.toUpperCase() ?? "GET";
|
|
287
|
-
if (relayerUrls.size > 0 && [...relayerUrls].some((base) => url.startsWith(base))) {
|
|
288
|
-
const headers = new Headers(init?.headers);
|
|
289
|
-
if (MUTATING_METHODS.has(method) && csrfTokenBase) headers.set(CSRF_HEADER_NAME, csrfTokenBase);
|
|
290
|
-
return originalFetch(input, {
|
|
291
|
-
...init,
|
|
292
|
-
headers,
|
|
293
|
-
credentials: "include"
|
|
294
|
-
});
|
|
295
|
-
}
|
|
296
|
-
return originalFetch(input, init);
|
|
297
|
-
};
|
|
298
|
-
}
|
|
299
|
-
/**
|
|
300
|
-
* Verify a fetched script's SHA-384 hash matches the expected integrity value.
|
|
301
|
-
*/
|
|
302
|
-
async function verifyIntegrity(content, expectedHash) {
|
|
303
|
-
const encoder = new TextEncoder();
|
|
304
|
-
const hashBuffer = await crypto.subtle.digest("SHA-384", encoder.encode(content));
|
|
305
|
-
const hashHex = [...new Uint8Array(hashBuffer)].map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
306
|
-
if (hashHex !== expectedHash) throw new Error(\`CDN integrity check failed: expected SHA-384 \${expectedHash}, got \${hashHex}\`);
|
|
307
|
-
}
|
|
308
|
-
/**
|
|
309
|
-
* Load SDK script from CDN.
|
|
310
|
-
* Uses two strategies depending on the environment:
|
|
311
|
-
* - **Web apps (default):** fetch + blob URL + importScripts. Avoids MIME-type
|
|
312
|
-
* rejections (some CDNs serve .cjs as \`application/node\`) and CSP
|
|
313
|
-
* \`unsafe-eval\` violations.
|
|
314
|
-
* - **Browser extensions (Chrome/Firefox/Safari):** importScripts directly.
|
|
315
|
-
* Blob URLs are blocked by extension CSP, but the CDN must be allowed
|
|
316
|
-
* in the extension's manifest CSP.
|
|
317
|
-
*
|
|
318
|
-
* Integrity is always verified when a hash is provided, regardless of strategy.
|
|
319
|
-
*/
|
|
320
|
-
async function fetchScript(cdnUrl) {
|
|
321
|
-
const response = await originalFetch(cdnUrl);
|
|
322
|
-
if (!response.ok) throw new Error(\`Failed to fetch SDK: \${response.status} \${response.statusText}\`);
|
|
323
|
-
return response.text();
|
|
324
|
-
}
|
|
325
|
-
async function loadSdkScript(cdnUrl, integrity) {
|
|
326
|
-
const validatedUrl = validateCdnUrl(cdnUrl);
|
|
327
|
-
if (getBrowserExtensionRuntime()) {
|
|
328
|
-
if (integrity) await verifyIntegrity(await fetchScript(validatedUrl), integrity);
|
|
329
|
-
return self.importScripts(validatedUrl);
|
|
330
|
-
}
|
|
331
|
-
const scriptContent = await fetchScript(validatedUrl);
|
|
332
|
-
if (integrity) await verifyIntegrity(scriptContent, integrity);
|
|
333
|
-
const blob = new Blob([scriptContent], { type: "application/javascript" });
|
|
334
|
-
const blobUrl = URL.createObjectURL(blob);
|
|
335
|
-
try {
|
|
336
|
-
self.importScripts(blobUrl);
|
|
337
|
-
} finally {
|
|
338
|
-
URL.revokeObjectURL(blobUrl);
|
|
339
|
-
}
|
|
340
|
-
}
|
|
341
|
-
/**
|
|
342
|
-
* Handle INIT request - load SDK WASM and register chain configs (instances are lazy).
|
|
343
|
-
*/
|
|
344
|
-
async function handleInit(request) {
|
|
345
|
-
const { id, type, payload } = request;
|
|
346
|
-
try {
|
|
347
|
-
if (payload.env !== "web") throw new Error(\`Web worker received unexpected env: \${payload.env}\`);
|
|
348
|
-
const { cdnUrl, csrfToken, integrity, thread } = payload;
|
|
349
|
-
csrfTokenBase = csrfToken;
|
|
350
|
-
setupFetchInterceptor();
|
|
351
|
-
await loadSdkScript(cdnUrl, integrity);
|
|
352
|
-
if (!self.relayerSDK) throw new Error("Failed to load relayerSDK from CDN");
|
|
353
|
-
sdkGlobal = self.relayerSDK;
|
|
354
|
-
await sdkGlobal.initSDK(thread !== null && thread !== void 0 ? { thread } : void 0);
|
|
355
|
-
registerRelayerUrls(payload.chains);
|
|
356
|
-
for (const chain of payload.chains) configs.set(chain.id, chain);
|
|
357
|
-
sendSuccess(id, type, { initialized: true });
|
|
358
|
-
} catch (error) {
|
|
359
|
-
sendError(id, type, error);
|
|
360
|
-
}
|
|
361
|
-
}
|
|
362
|
-
/** Coerce a boolean to bigint for numeric FHE types. */
|
|
363
|
-
function toBigInt(value) {
|
|
364
|
-
return typeof value === "boolean" ? value ? 1n : 0n : value;
|
|
365
|
-
}
|
|
366
|
-
/**
|
|
367
|
-
* Add a single typed value to the encrypted input builder.
|
|
368
|
-
*/
|
|
369
|
-
function addTypedValue(input, entry) {
|
|
370
|
-
const { value, type: fheType } = entry;
|
|
371
|
-
switch (fheType) {
|
|
372
|
-
case "ebool":
|
|
373
|
-
input.addBool(typeof value === "boolean" ? value : value !== 0n);
|
|
374
|
-
break;
|
|
375
|
-
case "euint8":
|
|
376
|
-
input.add8(toBigInt(value));
|
|
377
|
-
break;
|
|
378
|
-
case "euint16":
|
|
379
|
-
input.add16(toBigInt(value));
|
|
380
|
-
break;
|
|
381
|
-
case "euint32":
|
|
382
|
-
input.add32(toBigInt(value));
|
|
383
|
-
break;
|
|
384
|
-
case "euint64":
|
|
385
|
-
input.add64(toBigInt(value));
|
|
386
|
-
break;
|
|
387
|
-
case "euint128":
|
|
388
|
-
input.add128(toBigInt(value));
|
|
389
|
-
break;
|
|
390
|
-
case "euint256":
|
|
391
|
-
input.add256(toBigInt(value));
|
|
392
|
-
break;
|
|
393
|
-
case "eaddress":
|
|
394
|
-
input.addAddress(value);
|
|
395
|
-
break;
|
|
396
|
-
default: unreachableFheType(fheType);
|
|
397
|
-
}
|
|
398
|
-
}
|
|
399
|
-
/**
|
|
400
|
-
* Handle ENCRYPT request.
|
|
401
|
-
*/
|
|
402
|
-
async function handleEncrypt(request) {
|
|
403
|
-
const { id, type, payload } = request;
|
|
404
|
-
const { values, contractAddress, userAddress } = payload;
|
|
405
|
-
try {
|
|
406
|
-
const input = (await getInstance(payload.chainId)).createEncryptedInput(contractAddress, userAddress);
|
|
407
|
-
for (const entry of values) addTypedValue(input, entry);
|
|
408
|
-
const encrypted = await input.encrypt();
|
|
409
|
-
sendSuccess(id, type, {
|
|
410
|
-
handles: encrypted.handles,
|
|
411
|
-
inputProof: encrypted.inputProof
|
|
412
|
-
}, [encrypted.inputProof.buffer, ...encrypted.handles.map((h) => h.buffer)]);
|
|
413
|
-
} catch (error) {
|
|
414
|
-
sendError(id, type, error);
|
|
415
|
-
}
|
|
416
|
-
}
|
|
417
|
-
/**
|
|
418
|
-
* Handle USER_DECRYPT request.
|
|
419
|
-
*/
|
|
420
|
-
async function handleUserDecrypt(request) {
|
|
421
|
-
const { id, type, payload } = request;
|
|
422
|
-
try {
|
|
423
|
-
const instance = await getInstance(payload.chainId);
|
|
424
|
-
const handleContractPairs = payload.encryptedValues.map((encryptedValue) => ({
|
|
425
|
-
handle: encryptedValue,
|
|
426
|
-
contractAddress: payload.contractAddress
|
|
427
|
-
}));
|
|
428
|
-
sendSuccess(id, type, { clearValues: await instance.userDecrypt(handleContractPairs, unprefixHex(payload.privateKey), unprefixHex(payload.publicKey), payload.signature, payload.signedContractAddresses, payload.signerAddress, payload.startTimestamp, payload.durationDays) });
|
|
429
|
-
} catch (error) {
|
|
430
|
-
sendError(id, type, error);
|
|
431
|
-
}
|
|
432
|
-
}
|
|
433
|
-
/**
|
|
434
|
-
* Handle PUBLIC_DECRYPT request.
|
|
435
|
-
*/
|
|
436
|
-
async function handlePublicDecrypt(request) {
|
|
437
|
-
const { id, type, payload } = request;
|
|
438
|
-
try {
|
|
439
|
-
sendSuccess(id, type, { ...await (await getInstance(payload.chainId)).publicDecrypt(payload.encryptedValues) });
|
|
440
|
-
} catch (error) {
|
|
441
|
-
sendError(id, type, error);
|
|
442
|
-
}
|
|
443
|
-
}
|
|
444
|
-
/**
|
|
445
|
-
* Handle GENERATE_KEYPAIR request.
|
|
446
|
-
*/
|
|
447
|
-
async function handleGenerateKeypair(request) {
|
|
448
|
-
const { id, type, payload } = request;
|
|
449
|
-
try {
|
|
450
|
-
const keypair = (await getInstance(payload.chainId)).generateKeypair();
|
|
451
|
-
sendSuccess(id, type, {
|
|
452
|
-
publicKey: prefixHex(keypair.publicKey),
|
|
453
|
-
privateKey: prefixHex(keypair.privateKey)
|
|
454
|
-
});
|
|
455
|
-
} catch (error) {
|
|
456
|
-
sendError(id, type, error);
|
|
457
|
-
}
|
|
458
|
-
}
|
|
459
|
-
/**
|
|
460
|
-
* Handle CREATE_EIP712 request.
|
|
461
|
-
*/
|
|
462
|
-
async function handleCreateEIP712(request) {
|
|
463
|
-
const { id, type, payload } = request;
|
|
464
|
-
try {
|
|
465
|
-
sendSuccess(id, type, (await getInstance(payload.chainId)).createEIP712(unprefixHex(payload.publicKey), payload.contractAddresses, payload.startTimestamp, payload.durationDays));
|
|
466
|
-
} catch (error) {
|
|
467
|
-
sendError(id, type, error);
|
|
468
|
-
}
|
|
469
|
-
}
|
|
470
|
-
/**
|
|
471
|
-
* Handle CREATE_DELEGATED_EIP712 request.
|
|
472
|
-
*/
|
|
473
|
-
async function handleCreateDelegatedEIP712(request) {
|
|
474
|
-
const { id, type, payload } = request;
|
|
475
|
-
try {
|
|
476
|
-
sendSuccess(id, type, (await getInstance(payload.chainId)).createDelegatedUserDecryptEIP712(unprefixHex(payload.publicKey), payload.contractAddresses, payload.delegatorAddress, payload.startTimestamp, payload.durationDays));
|
|
477
|
-
} catch (error) {
|
|
478
|
-
sendError(id, type, error);
|
|
479
|
-
}
|
|
480
|
-
}
|
|
481
|
-
/**
|
|
482
|
-
* Handle DELEGATED_USER_DECRYPT request.
|
|
483
|
-
*/
|
|
484
|
-
async function handleDelegatedUserDecrypt(request) {
|
|
485
|
-
const { id, type, payload } = request;
|
|
486
|
-
try {
|
|
487
|
-
const instance = await getInstance(payload.chainId);
|
|
488
|
-
const handleContractPairs = payload.encryptedValues.map((encryptedValue) => ({
|
|
489
|
-
handle: encryptedValue,
|
|
490
|
-
contractAddress: payload.contractAddress
|
|
491
|
-
}));
|
|
492
|
-
sendSuccess(id, type, { clearValues: await instance.delegatedUserDecrypt(handleContractPairs, unprefixHex(payload.privateKey), unprefixHex(payload.publicKey), payload.signature, payload.signedContractAddresses, payload.delegatorAddress, payload.delegateAddress, payload.startTimestamp, payload.durationDays) });
|
|
493
|
-
} catch (error) {
|
|
494
|
-
sendError(id, type, error);
|
|
495
|
-
}
|
|
496
|
-
}
|
|
497
|
-
/**
|
|
498
|
-
* Handle REQUEST_ZK_PROOF_VERIFICATION request.
|
|
499
|
-
*/
|
|
500
|
-
async function handleRequestZKProofVerification(request) {
|
|
501
|
-
const { id, type, payload } = request;
|
|
502
|
-
try {
|
|
503
|
-
const result = await (await getInstance(payload.chainId)).requestZKProofVerification(payload.zkProof);
|
|
504
|
-
sendSuccess(id, type, result, [result.inputProof.buffer, ...result.handles.map((h) => h.buffer)]);
|
|
505
|
-
} catch (error) {
|
|
506
|
-
sendError(id, type, error);
|
|
507
|
-
}
|
|
508
|
-
}
|
|
509
|
-
/**
|
|
510
|
-
* Handle GET_PUBLIC_KEY request.
|
|
511
|
-
*/
|
|
512
|
-
async function handleGetPublicKey(request) {
|
|
513
|
-
const { id, type, payload } = request;
|
|
514
|
-
try {
|
|
515
|
-
sendSuccess(id, type, { result: (await getInstance(payload.chainId)).getPublicKey() });
|
|
516
|
-
} catch (error) {
|
|
517
|
-
sendError(id, type, error);
|
|
518
|
-
}
|
|
519
|
-
}
|
|
520
|
-
/**
|
|
521
|
-
* Handle GET_PUBLIC_PARAMS request.
|
|
522
|
-
*/
|
|
523
|
-
async function handleGetPublicParams(request) {
|
|
524
|
-
const { id, type, payload } = request;
|
|
525
|
-
try {
|
|
526
|
-
sendSuccess(id, type, { result: (await getInstance(payload.chainId)).getPublicParams(payload.bits) });
|
|
527
|
-
} catch (error) {
|
|
528
|
-
sendError(id, type, error);
|
|
529
|
-
}
|
|
530
|
-
}
|
|
531
|
-
/**
|
|
532
|
-
* Handle UPDATE_CSRF request - update the stored CSRF token.
|
|
533
|
-
*/
|
|
534
|
-
function handleUpdateCsrf(request) {
|
|
535
|
-
const { id, type, payload } = request;
|
|
536
|
-
csrfTokenBase = payload.csrfToken;
|
|
537
|
-
sendSuccess(id, type, { updated: true });
|
|
538
|
-
}
|
|
539
|
-
/**
|
|
540
|
-
* Main message handler.
|
|
541
|
-
*/
|
|
542
|
-
self.onmessage = async (event) => {
|
|
543
|
-
const request = event.data;
|
|
544
|
-
try {
|
|
545
|
-
switch (request.type) {
|
|
546
|
-
case "INIT":
|
|
547
|
-
await handleInit(request);
|
|
548
|
-
break;
|
|
549
|
-
case "UPDATE_CSRF":
|
|
550
|
-
handleUpdateCsrf(request);
|
|
551
|
-
break;
|
|
552
|
-
case "ENCRYPT":
|
|
553
|
-
await handleEncrypt(request);
|
|
554
|
-
break;
|
|
555
|
-
case "USER_DECRYPT":
|
|
556
|
-
await handleUserDecrypt(request);
|
|
557
|
-
break;
|
|
558
|
-
case "PUBLIC_DECRYPT":
|
|
559
|
-
await handlePublicDecrypt(request);
|
|
560
|
-
break;
|
|
561
|
-
case "GENERATE_KEYPAIR":
|
|
562
|
-
await handleGenerateKeypair(request);
|
|
563
|
-
break;
|
|
564
|
-
case "CREATE_EIP712":
|
|
565
|
-
await handleCreateEIP712(request);
|
|
566
|
-
break;
|
|
567
|
-
case "CREATE_DELEGATED_EIP712":
|
|
568
|
-
await handleCreateDelegatedEIP712(request);
|
|
569
|
-
break;
|
|
570
|
-
case "DELEGATED_USER_DECRYPT":
|
|
571
|
-
await handleDelegatedUserDecrypt(request);
|
|
572
|
-
break;
|
|
573
|
-
case "REQUEST_ZK_PROOF_VERIFICATION":
|
|
574
|
-
await handleRequestZKProofVerification(request);
|
|
575
|
-
break;
|
|
576
|
-
case "GET_PUBLIC_KEY":
|
|
577
|
-
await handleGetPublicKey(request);
|
|
578
|
-
break;
|
|
579
|
-
case "GET_PUBLIC_PARAMS":
|
|
580
|
-
await handleGetPublicParams(request);
|
|
581
|
-
break;
|
|
582
|
-
default: throw new Error(\`Unknown request type: \${request.type}\`);
|
|
583
|
-
}
|
|
584
|
-
} catch (error) {
|
|
585
|
-
sendError(request?.id ?? "unknown", request?.type ?? "UNKNOWN", error);
|
|
586
|
-
}
|
|
587
|
-
};
|
|
588
|
-
//#endregion
|
|
589
|
-
})();
|
|
590
|
-
`],{type:`application/javascript`}));try{return new Worker(t)}finally{URL.revokeObjectURL(t)}}wireEvents(e){e.onmessage=e=>this.handleResponse(e.data),e.onerror=e=>this.handleWorkerError(e.message),e.onmessageerror=()=>this.handleWorkerMessageError()}postMessage(e,t){e.postMessage(t)}terminateWorker(e){e.terminate()}generateRequestId(){return crypto.randomUUID()}getInitPayload(){let{cdnUrl:e,chains:t,csrfToken:n,integrity:r,thread:i}=this.config;return{type:`INIT`,payload:{env:`web`,cdnUrl:e,chains:t,csrfToken:n,integrity:r,thread:i}}}async updateCsrf(e){await this.sendRequest(`UPDATE_CSRF`,{csrfToken:e})}};const h=u.object({threads:u.optional(u.int().check(u.positive())),fheArtifactCacheTTL:u.optional(u.int().check(u.nonnegative()))});function g(e){return e!==void 0&&i(h,e),{type:`web`,createWorker:(t,n)=>new m({cdnUrl:`https://cdn.zama.org/relayer-sdk-js/0.4.4/relayer-sdk-js.umd.cjs`,chains:t,csrfToken:e?.security?.getCsrfToken?.()??``,integrity:e?.security?.integrityCheck===!1?void 0:`a50426aae8440e802102c8674dd8451f34fe79352d5e2cc6b89d9f1aa340296176c2ee33d9aa4657752f8ca96f74f921`,logger:n,thread:e?.threads}),createRelayer:(t,n,r)=>new d({chain:t,worker:n,...e,logger:r})}}export{d as RelayerWeb,g as web};
|
|
1
|
+
import{t as e}from"../fhevm-relayer-B-lEKfPQ.js";function t(t){return{type:`web`,createRelayer:n=>new e({chain:n,options:t})}}export{t as web};
|
|
591
2
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["#config","#worker","#artifactCache","#artifactStorage","#refreshCsrfToken","#getArtifactCache","workerFilename","workerCode"],"sources":["../../../src/relayer/relayer-web.ts","../../../src/worker/browser-extension.ts","../../../src/worker/worker.client.ts","../../../src/config/web.ts"],"sourcesContent":["import type {\n InputProofBytesType,\n KmsDelegatedUserDecryptEIP712Type,\n ZKProofLike,\n} from \"@zama-fhe/relayer-sdk/bundle\";\nimport { toHex } from \"viem\";\nimport type { Address, Hex } from \"viem\";\nimport { IndexedDBStorage } from \"../storage/indexeddb-storage\";\nimport type { GenericStorage } from \"../types\";\nimport type { RelayerWorkerClient } from \"../worker/worker.client\";\nimport type { FheChain } from \"../chains/types\";\nimport type { TransportKeyPair } from \"../credentials/types\";\nimport { BaseRelayer } from \"./base-relayer\";\nimport { FheArtifactCache } from \"./fhe-artifact-cache\";\nimport type { RelayerSDK } from \"./relayer-sdk\";\nimport type {\n ClearValue,\n DelegatedUserDecryptParams,\n EIP712TypedData,\n EncryptParams,\n EncryptResult,\n EncryptedValue,\n FheEncryptionKey,\n PublicDecryptResult,\n PublicParamsData,\n RelayerWebConfig,\n UserDecryptParams,\n} from \"./relayer-sdk.types\";\nimport { withRetry } from \"./relayer-utils\";\n\n/**\n * Pinned relayer SDK version used for the WASM CDN bundle.\n * Update this when upgrading @zama-fhe/relayer-sdk, and keep the\n * peerDependencies range in package.json in sync (~x.y.z).\n */\nexport const RELAYER_SDK_VERSION = \"0.4.4\";\nexport const CDN_URL = `https://cdn.zama.org/relayer-sdk-js/${RELAYER_SDK_VERSION}/relayer-sdk-js.umd.cjs`;\n/** SHA-384 hex digest of the pinned CDN bundle for integrity verification. */\nexport const CDN_INTEGRITY =\n \"a50426aae8440e802102c8674dd8451f34fe79352d5e2cc6b89d9f1aa340296176c2ee33d9aa4657752f8ca96f74f921\";\n\n/**\n * RelayerWeb — single-chain browser encryption/decryption layer.\n * The worker is injected at construction time; the relayer does not own its lifecycle.\n */\nexport class RelayerWeb extends BaseRelayer implements RelayerSDK, Disposable {\n #artifactCache: FheArtifactCache | null = null;\n #artifactStorage: GenericStorage | null = null;\n readonly #config: RelayerWebConfig;\n\n constructor(config: RelayerWebConfig) {\n super();\n this.#config = config;\n }\n\n protected get chain(): FheChain {\n return this.#config.chain;\n }\n\n protected async init(): Promise<void> {\n await this.#worker.initWorker();\n }\n\n get #worker(): RelayerWorkerClient {\n return this.#config.worker;\n }\n\n #getArtifactCache(): FheArtifactCache {\n if (!this.#artifactCache) {\n if (!this.#artifactStorage) {\n this.#artifactStorage =\n this.#config.fheArtifactStorage ??\n new IndexedDBStorage(\"FheArtifactCache\", 1, \"artifacts\");\n }\n this.#artifactCache = new FheArtifactCache({\n storage: this.#artifactStorage,\n chainId: this.chain.id,\n relayerUrl: this.chain.relayerUrl,\n ttl: this.#config.fheArtifactCacheTTL,\n logger: this.#config.logger,\n });\n }\n return this.#artifactCache;\n }\n\n /**\n * Terminate clears the artifact cache only.\n * The worker is externally owned — the relayer does not terminate it.\n */\n terminate(): void {\n this.#artifactCache = null;\n this.resetInit();\n }\n\n /** Calls {@link terminate}. */\n [Symbol.dispose](): void {\n this.terminate();\n }\n\n /**\n * Refresh the CSRF token in the worker.\n * Call this before making authenticated network requests.\n */\n async #refreshCsrfToken(): Promise<void> {\n const token = this.#config.security?.getCsrfToken?.() ?? \"\";\n if (token) {\n await this.#worker.updateCsrf(token);\n }\n }\n\n /**\n * Generate a transport key pair (ML-KEM public + private key) used for user-decryption.\n */\n async generateTransportKeyPair(): Promise<TransportKeyPair> {\n await this.ensureInit();\n const chainId = this.chain.id;\n const result = await this.#worker.generateKeypair({ chainId });\n return { publicKey: result.publicKey, privateKey: result.privateKey };\n }\n\n /**\n * Create EIP712 typed data for user decryption authorization.\n */\n async createEIP712(\n publicKey: Hex,\n contractAddresses: Address[],\n startTimestamp: number,\n durationDays = 7,\n ): Promise<EIP712TypedData> {\n await this.ensureInit();\n const chainId = this.chain.id;\n return this.#worker.createEIP712({\n chainId,\n publicKey,\n contractAddresses,\n startTimestamp,\n durationDays,\n });\n }\n\n /**\n * Encrypt values for use in smart contract calls.\n * Each value must specify its FHE type (ebool, euint8–256, eaddress).\n */\n async encrypt(params: EncryptParams): Promise<EncryptResult> {\n const { values, contractAddress, userAddress } = params;\n await this.ensureInit();\n const chainId = this.chain.id;\n\n return withRetry(async () => {\n await this.#refreshCsrfToken();\n const result = await this.#worker.encrypt({ chainId, values, contractAddress, userAddress });\n return {\n encryptedValues: result.handles.map((handle) => toHex(handle)),\n inputProof: toHex(result.inputProof),\n };\n });\n }\n\n /**\n * Decrypt ciphertexts using user's private key.\n * Requires a valid EIP712 signature.\n */\n async userDecrypt(\n params: UserDecryptParams,\n ): Promise<Readonly<Record<EncryptedValue, ClearValue>>> {\n await this.ensureInit();\n const chainId = this.chain.id;\n return withRetry(async () => {\n await this.#refreshCsrfToken();\n const result = await this.#worker.userDecrypt({ chainId, ...params });\n return result.clearValues;\n });\n }\n\n /**\n * Public decryption - no authorization needed.\n * Used for publicly visible encrypted values.\n */\n async publicDecrypt(encryptedValues: EncryptedValue[]): Promise<PublicDecryptResult> {\n await this.ensureInit();\n const chainId = this.chain.id;\n return withRetry(async () => {\n await this.#refreshCsrfToken();\n const result = await this.#worker.publicDecrypt({ chainId, encryptedValues });\n return {\n clearValues: result.clearValues,\n abiEncodedClearValues: result.abiEncodedClearValues,\n decryptionProof: result.decryptionProof,\n };\n });\n }\n\n /**\n * Create EIP712 typed data for delegated user decryption authorization.\n */\n async createDelegatedUserDecryptEIP712(\n publicKey: Hex,\n contractAddresses: Address[],\n delegatorAddress: Address,\n startTimestamp: number,\n durationDays = 7,\n ): Promise<KmsDelegatedUserDecryptEIP712Type> {\n await this.ensureInit();\n const chainId = this.chain.id;\n return this.#worker.createDelegatedUserDecryptEIP712({\n chainId,\n publicKey,\n contractAddresses,\n delegatorAddress,\n startTimestamp,\n durationDays,\n });\n }\n\n /**\n * Decrypt ciphertexts via delegation.\n * Requires a valid EIP712 signature from the delegator.\n */\n async delegatedUserDecrypt(\n params: DelegatedUserDecryptParams,\n ): Promise<Readonly<Record<EncryptedValue, ClearValue>>> {\n await this.ensureInit();\n const chainId = this.chain.id;\n return withRetry(async () => {\n await this.#refreshCsrfToken();\n const result = await this.#worker.delegatedUserDecrypt({ chainId, ...params });\n return result.clearValues;\n });\n }\n\n /**\n * Submit a ZK proof to the relayer for verification.\n */\n async requestZKProofVerification(zkProof: ZKProofLike): Promise<InputProofBytesType> {\n await this.ensureInit();\n const chainId = this.chain.id;\n return withRetry(async () => {\n await this.#refreshCsrfToken();\n return this.#worker.requestZKProofVerification({ chainId, zkProof });\n });\n }\n\n /**\n * Fetch the network's FHE encryption key (ID + bytes).\n * When storage is configured, the result is cached persistently.\n */\n async fetchFheEncryptionKeyBytes(): Promise<FheEncryptionKey | null> {\n await this.ensureInit();\n const chainId = this.chain.id;\n const artifactCache = this.#getArtifactCache();\n return artifactCache.fetchFheEncryptionKeyBytes(\n async () => (await this.#worker.getPublicKey({ chainId })).result,\n );\n }\n\n /**\n * Get public parameters for encryption capacity.\n * When storage is configured, the result is cached persistently.\n */\n async getPublicParams(bits: number): Promise<PublicParamsData | null> {\n await this.ensureInit();\n const chainId = this.chain.id;\n const artifactCache = this.#getArtifactCache();\n return artifactCache.getPublicParams(\n bits,\n async () => (await this.#worker.getPublicParams({ chainId, bits })).result,\n );\n }\n}\n","import { assertFunctionProp, assertObject, assertStringProp } from \"../utils/assertions\";\n\n/**\n * Subset of the WebExtensions `runtime` API used by the SDK.\n * @see https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/runtime\n */\nexport interface BrowserExtensionRuntime {\n /** The ID of the extension. */\n id: string;\n /** Convert a relative path within the extension to a fully-qualified URL. */\n getURL: (path: string) => string;\n}\n\nfunction isValidRuntime(runtime: unknown): runtime is BrowserExtensionRuntime {\n try {\n assertObject(runtime, \"runtime\");\n assertStringProp(runtime, \"id\", \"runtime.id\");\n assertFunctionProp<\"getURL\", (path: string) => string>(runtime, \"getURL\", \"runtime.getURL\");\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Return the browser extension runtime object, or `undefined` outside extensions.\n * Works across Chrome/Edge (`chrome.runtime`) and Firefox/Safari (`browser.runtime`).\n * Extensions have restricted CSP that blocks `blob:` URLs, so callers use\n * this to detect the environment and resolve file URLs via `runtime.getURL`.\n */\nexport function getBrowserExtensionRuntime(): BrowserExtensionRuntime | undefined {\n const g = globalThis as unknown as Record<string, unknown>;\n for (const ns of [g.chrome, g.browser]) {\n try {\n assertObject(ns, \"ns\");\n if (isValidRuntime(ns.runtime)) {\n return ns.runtime;\n }\n } catch {\n continue;\n }\n }\n return undefined;\n}\n","import type { FheChain } from \"../chains/types\";\nimport type {\n GenericLogger,\n UpdateCsrfResponseData,\n WorkerEnv,\n WorkerRequest,\n WorkerRequestType,\n WorkerResponse,\n} from \"./worker.types\";\nimport { BaseWorkerClient } from \"./worker.base-client\";\nimport type { WorkerClientTimeoutConfig } from \"./worker.base-client\";\nimport { getBrowserExtensionRuntime } from \"./browser-extension\";\nimport { default as workerCode, filename as workerFilename } from \"./relayer-sdk.worker.ts?iife\";\n\n/** Configuration for the worker client */\nexport interface WorkerClientConfig extends WorkerClientTimeoutConfig {\n cdnUrl: string;\n chains: FheChain[];\n csrfToken: string;\n /** Expected SHA-384 hex digest of the CDN bundle for integrity verification. */\n integrity?: string;\n /** SDK-wide logger for tracing worker request lifecycle. */\n logger: GenericLogger;\n /** Number of WASM threads for parallel FHE operations (passed to `initSDK({ thread })`). */\n thread?: number;\n}\n\n/**\n * Client for communicating with the RelayerSDK Web Worker.\n * Provides a promise-based API for FHE operations.\n */\nexport class RelayerWorkerClient extends BaseWorkerClient<Worker, WorkerClientConfig> {\n protected readonly env: WorkerEnv = \"web\";\n\n constructor(config: WorkerClientConfig) {\n // Recycling (terminate + re-init) is a Node pool recovery: there a hung\n // worker keeps attracting least-connections work, cascading timeouts. The\n // browser runs a single worker with no such pool, and `web()` exposes no\n // timeout knobs, so a timeout here stays a typed reject without tearing\n // down the worker. Force it off after the spread so the browser worker is\n // never recycled, regardless of config.\n super({ workerLabel: \"web-worker\", ...config, recycleWorkerOnTimeout: false }, config.logger);\n }\n\n protected createWorker(): Worker {\n const runtime = getBrowserExtensionRuntime();\n if (runtime) {\n return new Worker(runtime.getURL(workerFilename));\n }\n const blobUrl = URL.createObjectURL(new Blob([workerCode], { type: \"application/javascript\" }));\n try {\n return new Worker(blobUrl);\n } finally {\n URL.revokeObjectURL(blobUrl);\n }\n }\n\n protected wireEvents(worker: Worker): void {\n worker.onmessage = (event: MessageEvent<WorkerResponse<unknown>>) =>\n this.handleResponse(event.data);\n worker.onerror = (event: ErrorEvent) => this.handleWorkerError(event.message);\n worker.onmessageerror = () => this.handleWorkerMessageError();\n }\n\n protected postMessage(worker: Worker, request: WorkerRequest): void {\n worker.postMessage(request);\n }\n\n protected terminateWorker(worker: Worker): void {\n worker.terminate();\n }\n\n protected generateRequestId(): string {\n return crypto.randomUUID();\n }\n\n protected getInitPayload(): { type: WorkerRequestType; payload: WorkerRequest[\"payload\"] } {\n // Explicitly construct the payload from serializable fields only.\n // Functions (e.g. `logger`) cannot be cloned by the structured clone\n // algorithm used by `worker.postMessage()`.\n const { cdnUrl, chains, csrfToken, integrity, thread } = this.config;\n return {\n type: \"INIT\",\n payload: { env: \"web\" as const, cdnUrl, chains, csrfToken, integrity, thread },\n };\n }\n\n /**\n * Update the CSRF token in the worker.\n * Call this before making authenticated requests to ensure the token is fresh.\n */\n async updateCsrf(csrfToken: string): Promise<void> {\n await this.sendRequest<UpdateCsrfResponseData>(\"UPDATE_CSRF\", { csrfToken });\n }\n}\n","import { z } from \"zod/mini\";\nimport { CDN_INTEGRITY, CDN_URL, RelayerWeb } from \"../relayer/relayer-web\";\nimport { parseConfiguration } from \"../validation\";\nimport { RelayerWorkerClient } from \"../worker/worker.client\";\nimport type { WebRelayerConfig, WebRelayerOptions } from \"./types\";\n\nconst WebRelayerOptionsSchema = z.object({\n threads: z.optional(z.int().check(z.positive())),\n fheArtifactCacheTTL: z.optional(z.int().check(z.nonnegative())),\n});\n\n/**\n * Browser relayer — routes to RelayerWeb (Web Worker + WASM).\n *\n * @param options - Worker options (threads, security, logger, storage).\n *\n * @example\n * ```ts\n * relayers: {\n * [sepolia.id]: web(),\n * [mainnet.id]: web({ threads: 4 }),\n * }\n * ```\n */\nexport function web(options?: WebRelayerOptions): WebRelayerConfig {\n if (options !== undefined) {\n parseConfiguration(WebRelayerOptionsSchema, options);\n }\n return {\n type: \"web\",\n createWorker: (chains, logger) =>\n new RelayerWorkerClient({\n cdnUrl: CDN_URL,\n chains,\n csrfToken: options?.security?.getCsrfToken?.() ?? \"\",\n integrity: options?.security?.integrityCheck === false ? undefined : CDN_INTEGRITY,\n logger,\n thread: options?.threads,\n }),\n createRelayer: (chain, worker, logger) => new RelayerWeb({ chain, worker, ...options, logger }),\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../../src/config/web.ts"],"sourcesContent":["import { FhevmRelayer } from \"../relayer/fhevm-relayer\";\nimport type { RelayerOptions } from \"../relayer/types\";\nimport type { WebRelayerConfig } from \"./types\";\n\n/**\n * Browser relayer — drives `@fhevm/sdk` via {@link FhevmRelayer}.\n *\n * @example\n * ```ts\n * relayers: {\n * [sepolia.id]: web({ timeout: 5 * 60_000 }),\n * [mainnet.id]: web({ batchRpcCalls: true }),\n * }\n * ```\n */\nexport function web(options?: RelayerOptions): WebRelayerConfig {\n return { type: \"web\", createRelayer: (chain) => new FhevmRelayer({ chain, options }) };\n}\n"],"mappings":"iDAeA,SAAgB,EAAI,EAA4C,CAC9D,MAAO,CAAE,KAAM,MAAO,cAAgB,GAAU,IAAI,EAAa,CAAE,QAAO,SAAQ,CAAC,CAAE,CACvF"}
|