@zama-fhe/sdk 3.2.0 → 3.3.0-alpha.10
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/cjs/assertions.cjs +1 -1
- package/dist/cjs/assertions.cjs.map +1 -1
- package/dist/cjs/base-signer.cjs +1 -1
- package/dist/cjs/base-signer.cjs.map +1 -1
- package/dist/cjs/ethers/index.cjs +1 -1
- package/dist/cjs/index.cjs +1 -1
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/query/index.cjs +1 -1
- package/dist/cjs/query/index.cjs.map +1 -1
- package/dist/cjs/relayer-cleartext.cjs +1 -1
- package/dist/cjs/relayer-cleartext.cjs.map +1 -1
- package/dist/cjs/relayer-sdk.worker.js +134 -34
- package/dist/cjs/relayer.cjs +1 -1
- package/dist/cjs/relayer.cjs.map +1 -1
- package/dist/cjs/timeout.cjs +2 -0
- package/dist/cjs/timeout.cjs.map +1 -0
- 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 +136 -36
- package/dist/cjs/web/index.cjs.map +1 -1
- package/dist/cjs/wrappers-registry.cjs +1 -1
- package/dist/cjs/wrappers-registry.cjs.map +1 -1
- package/dist/esm/assertions-zYseN5Xz.js +2 -0
- package/dist/esm/assertions-zYseN5Xz.js.map +1 -0
- package/dist/esm/{base-signer-ByGw5ReU.d.ts → base-signer-C3Wc5oi9.d.ts} +2 -2
- package/dist/esm/base-signer-DjytqPkd.js +2 -0
- package/dist/esm/base-signer-DjytqPkd.js.map +1 -0
- package/dist/esm/cleartext/index.d.ts +1 -1
- package/dist/esm/cleartext/index.js +1 -1
- package/dist/esm/{cleartext-DMW5nK1M.d.ts → cleartext-DAkOTlrE.d.ts} +2 -2
- package/dist/esm/{cleartext-CTla3A3O.js → cleartext-rTWFctyd.js} +2 -2
- package/dist/esm/{cleartext-CTla3A3O.js.map → cleartext-rTWFctyd.js.map} +1 -1
- package/dist/esm/{encryption-BGmINekJ.js → encryption-x4Z_Icqk.js} +2 -2
- package/dist/esm/{encryption-BGmINekJ.js.map → encryption-x4Z_Icqk.js.map} +1 -1
- package/dist/esm/ethers/index.d.ts +4 -4
- package/dist/esm/ethers/index.js +1 -1
- package/dist/esm/ethers/index.js.map +1 -1
- package/dist/esm/{hex-CxtyupeQ.js → hex-A72GrTf9.js} +2 -2
- package/dist/esm/{hex-CxtyupeQ.js.map → hex-A72GrTf9.js.map} +1 -1
- package/dist/esm/{index-DoVo9J1n.d.ts → index-C1Mtc8Rw.d.ts} +450 -163
- package/dist/esm/index.d.ts +17 -13
- package/dist/esm/index.js +1 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/node/index.d.ts +25 -7
- package/dist/esm/node/index.js +1 -1
- package/dist/esm/node/index.js.map +1 -1
- package/dist/esm/node/relayer-sdk.node-worker.js +1 -1
- package/dist/esm/node/relayer-sdk.node-worker.js.map +1 -1
- package/dist/esm/query/index.d.ts +42 -4
- package/dist/esm/query/index.js +1 -1
- package/dist/esm/query/index.js.map +1 -1
- package/dist/esm/relayer-cleartext-Bp6jtxDd.js +2 -0
- package/dist/esm/relayer-cleartext-Bp6jtxDd.js.map +1 -0
- package/dist/esm/{relayer-cleartext-DkKKjfI6.d.ts → relayer-cleartext-CSeMlmGO.d.ts} +102 -13
- package/dist/esm/relayer-eavK5bfZ.js +2 -0
- package/dist/esm/relayer-eavK5bfZ.js.map +1 -0
- package/dist/esm/relayer-sdk.worker.js +134 -34
- package/dist/esm/timeout-CNVfiaJD.js +2 -0
- package/dist/esm/timeout-CNVfiaJD.js.map +1 -0
- package/dist/esm/token-Bt33g0zQ.js +2 -0
- package/dist/esm/token-Bt33g0zQ.js.map +1 -0
- package/dist/esm/{types-BjDu-RZg.d.ts → types-Gn5MeDsr.d.ts} +14 -2
- package/dist/esm/{types-Xg_On_yY.d.ts → types-OHIaPwCy.d.ts} +2 -2
- package/dist/esm/{types-DiZhtG8i.d.ts → types-piBsdTIP.d.ts} +2 -2
- package/dist/esm/{validation-DHZ3JPOn.js → validation-DLszOweh.js} +2 -2
- package/dist/esm/{validation-DHZ3JPOn.js.map → validation-DLszOweh.js.map} +1 -1
- package/dist/esm/viem/index.d.ts +4 -4
- package/dist/esm/viem/index.js +1 -1
- package/dist/esm/viem/index.js.map +1 -1
- package/dist/esm/web/index.d.ts +2 -2
- package/dist/esm/web/index.js +135 -35
- package/dist/esm/web/index.js.map +1 -1
- package/dist/esm/worker.base-client-BVnT4yJa.js +2 -0
- package/dist/esm/worker.base-client-BVnT4yJa.js.map +1 -0
- package/dist/esm/{wrappers-registry-BWWG0aze.js → wrappers-registry-C1oK8Bi6.js} +2 -2
- package/dist/esm/wrappers-registry-C1oK8Bi6.js.map +1 -0
- package/package.json +51 -51
- package/dist/esm/assertions-CnPIS0VN.js +0 -2
- package/dist/esm/assertions-CnPIS0VN.js.map +0 -1
- package/dist/esm/base-signer-BVqcM5jt.js +0 -2
- package/dist/esm/base-signer-BVqcM5jt.js.map +0 -1
- package/dist/esm/error-W1N-FGwV.js +0 -2
- package/dist/esm/error-W1N-FGwV.js.map +0 -1
- package/dist/esm/relayer-D-nicEYJ.js +0 -2
- package/dist/esm/relayer-D-nicEYJ.js.map +0 -1
- package/dist/esm/relayer-cleartext-DGcb5qtE.js +0 -2
- package/dist/esm/relayer-cleartext-DGcb5qtE.js.map +0 -1
- package/dist/esm/token-TQ-VFHS4.js +0 -2
- package/dist/esm/token-TQ-VFHS4.js.map +0 -1
- package/dist/esm/worker.base-client-x522sMBy.js +0 -2
- package/dist/esm/worker.base-client-x522sMBy.js.map +0 -1
- package/dist/esm/wrappers-registry-BWWG0aze.js.map +0 -1
package/dist/esm/web/index.js
CHANGED
|
@@ -1,4 +1,121 @@
|
|
|
1
|
-
import{t as
|
|
1
|
+
import{a as e,o as t,r as n}from"../assertions-zYseN5Xz.js";import{t as r}from"../indexeddb-storage-Mi-fCWN3.js";import{t as i}from"../validation-DLszOweh.js";import{i as a,n as o,r as s,t as c}from"../worker.base-client-BVnT4yJa.js";import{toHex as l}from"viem";import{z as u}from"zod/mini";var d=class extends a{#e=null;#t=null;#n;constructor(e){super(),this.#n=e}get chain(){return this.#n.chain}async init(){await this.#r.initWorker()}get#r(){return this.#n.worker}#i(){return this.#e||=(this.#t||=this.#n.fheArtifactStorage??new r(`FheArtifactCache`,1,`artifacts`),new s({storage:this.#t,chainId:this.chain.id,relayerUrl:this.chain.relayerUrl,ttl:this.#n.fheArtifactCacheTTL,logger:this.#n.logger})),this.#e}terminate(){this.#e=null,this.resetInit()}[Symbol.dispose](){this.terminate()}async#a(){let e=this.#n.security?.getCsrfToken?.()??``;e&&await this.#r.updateCsrf(e)}async generateTransportKeyPair(){await this.ensureInit();let e=this.chain.id,t=await this.#r.generateKeypair({chainId:e});return{publicKey:t.publicKey,privateKey:t.privateKey}}async createEIP712(e,t,n,r=7){await this.ensureInit();let i=this.chain.id;return this.#r.createEIP712({chainId:i,publicKey:e,contractAddresses:t,startTimestamp:n,durationDays:r})}async encrypt(e){let{values:t,contractAddress:n,userAddress:r}=e;await this.ensureInit();let i=this.chain.id;return o(async()=>{await this.#a();let e=await this.#r.encrypt({chainId:i,values:t,contractAddress:n,userAddress:r});return{encryptedValues:e.handles.map(e=>l(e)),inputProof:l(e.inputProof)}})}async userDecrypt(e){await this.ensureInit();let t=this.chain.id;return o(async()=>(await this.#a(),(await this.#r.userDecrypt({chainId:t,...e})).clearValues))}async publicDecrypt(e){await this.ensureInit();let t=this.chain.id;return o(async()=>{await this.#a();let n=await this.#r.publicDecrypt({chainId:t,encryptedValues:e});return{clearValues:n.clearValues,abiEncodedClearValues:n.abiEncodedClearValues,decryptionProof:n.decryptionProof}})}async createDelegatedUserDecryptEIP712(e,t,n,r,i=7){await this.ensureInit();let a=this.chain.id;return this.#r.createDelegatedUserDecryptEIP712({chainId:a,publicKey:e,contractAddresses:t,delegatorAddress:n,startTimestamp:r,durationDays:i})}async delegatedUserDecrypt(e){await this.ensureInit();let t=this.chain.id;return o(async()=>(await this.#a(),(await this.#r.delegatedUserDecrypt({chainId:t,...e})).clearValues))}async requestZKProofVerification(e){await this.ensureInit();let t=this.chain.id;return o(async()=>(await this.#a(),this.#r.requestZKProofVerification({chainId:t,zkProof:e})))}async fetchFheEncryptionKeyBytes(){await this.ensureInit();let e=this.chain.id;return this.#i().fetchFheEncryptionKeyBytes(async()=>(await this.#r.getPublicKey({chainId:e})).result)}async getPublicParams(e){await this.ensureInit();let t=this.chain.id;return this.#i().getPublicParams(e,async()=>(await this.#r.getPublicParams({chainId:t,bits:e})).result)}};function f(r){try{return e(r,`runtime`),t(r,`id`,`runtime.id`),n(r,`getURL`,`runtime.getURL`),!0}catch{return!1}}function p(){let t=globalThis;for(let n of[t.chrome,t.browser])try{if(e(n,`ns`),f(n.runtime))return n.runtime}catch{continue}}var m=class extends c{env=`web`;constructor(e){super({workerLabel:`web-worker`,...e,recycleWorkerOnTimeout:!1},e.logger)}createWorker(){let e=p();if(e)return new Worker(e.getURL(`relayer-sdk.worker.js`));let t=URL.createObjectURL(new Blob([`(function() {
|
|
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
|
|
2
119
|
//#region src/utils/assertions.ts
|
|
3
120
|
function assertObject(value, context) {
|
|
4
121
|
if (typeof value !== "object" || value === null || Array.isArray(value)) throw new TypeError(\`\${context} must be an object, got \${typeof value}\`);
|
|
@@ -130,15 +247,14 @@ import{t as e}from"../indexeddb-storage-Mi-fCWN3.js";import{a as t,o as n,r}from
|
|
|
130
247
|
/**
|
|
131
248
|
* Send an error response back to the main thread.
|
|
132
249
|
*/
|
|
133
|
-
function sendError(id, type, error
|
|
134
|
-
|
|
250
|
+
function sendError(id, type, error) {
|
|
251
|
+
self.postMessage({
|
|
135
252
|
id,
|
|
136
253
|
type,
|
|
137
254
|
success: false,
|
|
138
|
-
error
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
self.postMessage(response);
|
|
255
|
+
error: error instanceof Error ? error.message : String(error),
|
|
256
|
+
serialized: serializeError(error)
|
|
257
|
+
});
|
|
142
258
|
}
|
|
143
259
|
const originalFetch = fetch;
|
|
144
260
|
/** Allowed CDN hostnames for loading the relayer SDK script. */
|
|
@@ -240,7 +356,7 @@ import{t as e}from"../indexeddb-storage-Mi-fCWN3.js";import{a as t,o as n,r}from
|
|
|
240
356
|
for (const chain of payload.chains) configs.set(chain.id, chain);
|
|
241
357
|
sendSuccess(id, type, { initialized: true });
|
|
242
358
|
} catch (error) {
|
|
243
|
-
sendError(id, type, error
|
|
359
|
+
sendError(id, type, error);
|
|
244
360
|
}
|
|
245
361
|
}
|
|
246
362
|
/** Coerce a boolean to bigint for numeric FHE types. */
|
|
@@ -295,7 +411,7 @@ import{t as e}from"../indexeddb-storage-Mi-fCWN3.js";import{a as t,o as n,r}from
|
|
|
295
411
|
inputProof: encrypted.inputProof
|
|
296
412
|
}, [encrypted.inputProof.buffer, ...encrypted.handles.map((h) => h.buffer)]);
|
|
297
413
|
} catch (error) {
|
|
298
|
-
sendError(id, type, error
|
|
414
|
+
sendError(id, type, error);
|
|
299
415
|
}
|
|
300
416
|
}
|
|
301
417
|
/**
|
|
@@ -311,22 +427,7 @@ import{t as e}from"../indexeddb-storage-Mi-fCWN3.js";import{a as t,o as n,r}from
|
|
|
311
427
|
}));
|
|
312
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) });
|
|
313
429
|
} catch (error) {
|
|
314
|
-
sendError(id, type, error
|
|
315
|
-
}
|
|
316
|
-
}
|
|
317
|
-
/**
|
|
318
|
-
* Extract an HTTP status code from an error, if present.
|
|
319
|
-
* Relayer SDK errors may carry a \`status\` or \`statusCode\` property.
|
|
320
|
-
*/
|
|
321
|
-
function extractHttpStatus(error) {
|
|
322
|
-
if (error === null || error === void 0 || typeof error !== "object") return;
|
|
323
|
-
const e = error;
|
|
324
|
-
if (typeof e.statusCode === "number") return e.statusCode;
|
|
325
|
-
if (typeof e.status === "number") return e.status;
|
|
326
|
-
if (e.cause !== null && e.cause !== void 0 && typeof e.cause === "object") {
|
|
327
|
-
const cause = e.cause;
|
|
328
|
-
if (typeof cause.statusCode === "number") return cause.statusCode;
|
|
329
|
-
if (typeof cause.status === "number") return cause.status;
|
|
430
|
+
sendError(id, type, error);
|
|
330
431
|
}
|
|
331
432
|
}
|
|
332
433
|
/**
|
|
@@ -337,7 +438,7 @@ import{t as e}from"../indexeddb-storage-Mi-fCWN3.js";import{a as t,o as n,r}from
|
|
|
337
438
|
try {
|
|
338
439
|
sendSuccess(id, type, { ...await (await getInstance(payload.chainId)).publicDecrypt(payload.encryptedValues) });
|
|
339
440
|
} catch (error) {
|
|
340
|
-
sendError(id, type, error
|
|
441
|
+
sendError(id, type, error);
|
|
341
442
|
}
|
|
342
443
|
}
|
|
343
444
|
/**
|
|
@@ -352,7 +453,7 @@ import{t as e}from"../indexeddb-storage-Mi-fCWN3.js";import{a as t,o as n,r}from
|
|
|
352
453
|
privateKey: prefixHex(keypair.privateKey)
|
|
353
454
|
});
|
|
354
455
|
} catch (error) {
|
|
355
|
-
sendError(id, type, error
|
|
456
|
+
sendError(id, type, error);
|
|
356
457
|
}
|
|
357
458
|
}
|
|
358
459
|
/**
|
|
@@ -363,7 +464,7 @@ import{t as e}from"../indexeddb-storage-Mi-fCWN3.js";import{a as t,o as n,r}from
|
|
|
363
464
|
try {
|
|
364
465
|
sendSuccess(id, type, (await getInstance(payload.chainId)).createEIP712(unprefixHex(payload.publicKey), payload.contractAddresses, payload.startTimestamp, payload.durationDays));
|
|
365
466
|
} catch (error) {
|
|
366
|
-
sendError(id, type, error
|
|
467
|
+
sendError(id, type, error);
|
|
367
468
|
}
|
|
368
469
|
}
|
|
369
470
|
/**
|
|
@@ -374,7 +475,7 @@ import{t as e}from"../indexeddb-storage-Mi-fCWN3.js";import{a as t,o as n,r}from
|
|
|
374
475
|
try {
|
|
375
476
|
sendSuccess(id, type, (await getInstance(payload.chainId)).createDelegatedUserDecryptEIP712(unprefixHex(payload.publicKey), payload.contractAddresses, payload.delegatorAddress, payload.startTimestamp, payload.durationDays));
|
|
376
477
|
} catch (error) {
|
|
377
|
-
sendError(id, type, error
|
|
478
|
+
sendError(id, type, error);
|
|
378
479
|
}
|
|
379
480
|
}
|
|
380
481
|
/**
|
|
@@ -390,7 +491,7 @@ import{t as e}from"../indexeddb-storage-Mi-fCWN3.js";import{a as t,o as n,r}from
|
|
|
390
491
|
}));
|
|
391
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) });
|
|
392
493
|
} catch (error) {
|
|
393
|
-
sendError(id, type, error
|
|
494
|
+
sendError(id, type, error);
|
|
394
495
|
}
|
|
395
496
|
}
|
|
396
497
|
/**
|
|
@@ -402,7 +503,7 @@ import{t as e}from"../indexeddb-storage-Mi-fCWN3.js";import{a as t,o as n,r}from
|
|
|
402
503
|
const result = await (await getInstance(payload.chainId)).requestZKProofVerification(payload.zkProof);
|
|
403
504
|
sendSuccess(id, type, result, [result.inputProof.buffer, ...result.handles.map((h) => h.buffer)]);
|
|
404
505
|
} catch (error) {
|
|
405
|
-
sendError(id, type, error
|
|
506
|
+
sendError(id, type, error);
|
|
406
507
|
}
|
|
407
508
|
}
|
|
408
509
|
/**
|
|
@@ -413,7 +514,7 @@ import{t as e}from"../indexeddb-storage-Mi-fCWN3.js";import{a as t,o as n,r}from
|
|
|
413
514
|
try {
|
|
414
515
|
sendSuccess(id, type, { result: (await getInstance(payload.chainId)).getPublicKey() });
|
|
415
516
|
} catch (error) {
|
|
416
|
-
sendError(id, type, error
|
|
517
|
+
sendError(id, type, error);
|
|
417
518
|
}
|
|
418
519
|
}
|
|
419
520
|
/**
|
|
@@ -424,7 +525,7 @@ import{t as e}from"../indexeddb-storage-Mi-fCWN3.js";import{a as t,o as n,r}from
|
|
|
424
525
|
try {
|
|
425
526
|
sendSuccess(id, type, { result: (await getInstance(payload.chainId)).getPublicParams(payload.bits) });
|
|
426
527
|
} catch (error) {
|
|
427
|
-
sendError(id, type, error
|
|
528
|
+
sendError(id, type, error);
|
|
428
529
|
}
|
|
429
530
|
}
|
|
430
531
|
/**
|
|
@@ -481,8 +582,7 @@ import{t as e}from"../indexeddb-storage-Mi-fCWN3.js";import{a as t,o as n,r}from
|
|
|
481
582
|
default: throw new Error(\`Unknown request type: \${request.type}\`);
|
|
482
583
|
}
|
|
483
584
|
} catch (error) {
|
|
484
|
-
|
|
485
|
-
sendError(request?.id ?? "unknown", request?.type ?? "UNKNOWN", message);
|
|
585
|
+
sendError(request?.id ?? "unknown", request?.type ?? "UNKNOWN", error);
|
|
486
586
|
}
|
|
487
587
|
};
|
|
488
588
|
//#endregion
|
|
@@ -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 {\n publicKey: result.publicKey,\n privateKey: result.privateKey,\n };\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({\n chainId,\n values,\n contractAddress,\n userAddress,\n });\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({\n chainId,\n encryptedValues,\n });\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({\n chainId,\n ...params,\n });\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 { 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 {\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 super(config, 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(): {\n type: WorkerRequestType;\n payload: WorkerRequest[\"payload\"];\n } {\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\", {\n csrfToken,\n });\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":["#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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"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{n as e,t}from"./timeout-CNVfiaJD.js";import{_ as n,i as r,p as i,s as a}from"./assertions-zYseN5Xz.js";import{z as o}from"zod/mini";var s=class{#e=null;async ensureInit(){return this.#e||=this.init().catch(e=>{throw this.#e=null,e}),this.#e}resetInit(){this.#e=null}async getAclAddress(){return this.chain.aclContractAddress}};const c=o.object({artifactUrl:o.optional(o.string()),etag:o.optional(o.string()),lastModified:o.optional(o.string()),lastValidatedAt:o._default(o.optional(o.number().check(o.nonnegative())),0)}),l=o.extend(c,{publicKeyId:o.string(),publicKey:o.string()}),u=o.extend(c,{publicParamsId:o.string(),publicParams:o.string()}),d=o.array(o.int().check(o.nonnegative())),f=o.object({status:o.literal(`succeeded`),response:o.object({fheKeyInfo:o.array(o.object({fhePublicKey:o.object({urls:o.array(o.string()).check(o.minLength(1))})})).check(o.minLength(1)),crs:o.record(o.string(),o.object({urls:o.array(o.string()).check(o.minLength(1))}))})}),p=8192,m=300*1e3;function h(e){let t=[];for(let n=0;n<e.length;n+=p)t.push(String.fromCharCode(...e.subarray(n,n+p)));return btoa(t.join(``))}function g(e){let t;try{t=atob(e)}catch{throw Error(`Invalid base64 data (length: ${e.length})`)}if(t.length===0)throw Error(`Decoded artifact is empty`);let n=new Uint8Array(t.length);for(let e=0;e<t.length;e++)n[e]=t.charCodeAt(e);return n}function _(e){return`fhe:pubkey:${e}`}function v(e,t){return`fhe:params:${e}:${t}`}function y(e){return`fhe:params-index:${e}`}var b=class{#e;#t;#n;#r;#i;#a;#o=new Map;#s=null;#c=new Map;#l=null;#u=null;constructor(e){this.#e=e.storage,this.#t=e.chainId,this.#n=e.relayerUrl,this.#r=(e.ttl??86400)*1e3,this.#i=e.logger}async fetchFheEncryptionKeyBytes(e){if(this.#a!==void 0)return this.#a;if(this.#s)return this.#s;this.#s=this.#d(e);try{return await this.#s}finally{this.#s=null}}async#d(e){let t=_(this.#t),r=await this.#g(t,l,`public key`,`Failed to read public key from persistent storage, falling back to network fetch`);if(r)try{let e={publicKeyId:r.publicKeyId,publicKey:g(r.publicKey)};return this.#a=e,e}catch(e){await this.#y(t,`public key`,{error:n(e).message})}let i=await e();if(i===null)return null;this.#a=i;try{let e={publicKeyId:i.publicKeyId,publicKey:h(i.publicKey),lastValidatedAt:Date.now()};await this.#e.set(t,e)}catch(e){this.#i.warn(`Failed to persist public key to storage`,{chainId:this.#t,error:n(e).message})}return i}async getPublicParams(e,t){let n=this.#o.get(e);if(n!==void 0)return n;let r=this.#c.get(e);if(r)return r;let i=this.#f(e,t);this.#c.set(e,i);try{return await i}finally{this.#c.delete(e)}}async#f(e,t){let r=v(this.#t,e),i=await this.#g(r,u,`params`,`Failed to read public params from persistent storage, falling back to network fetch`,{bits:e});if(i)try{let t={publicParamsId:i.publicParamsId,publicParams:g(i.publicParams)};return this.#o.set(e,t),t}catch(t){await this.#y(r,`params`,{bits:e,error:n(t).message})}let a=await t();if(a===null)return null;this.#o.set(e,a);try{let t={publicParamsId:a.publicParamsId,publicParams:h(a.publicParams),lastValidatedAt:Date.now()};await this.#e.set(r,t);let n=y(this.#t),i=await this.#v(`Failed to read params index from storage`);i.includes(e)||await this.#e.set(n,[...i,e])}catch(t){this.#i.warn(`Failed to persist public params to storage`,{chainId:this.#t,bits:e,error:n(t).message})}return a}async revalidateIfDue(){if(this.#l)return this.#l;this.#l=this.#p();try{return await this.#l}finally{this.#l=null}}async#p(){let e=Date.now();if(this.#u!==null&&e-this.#u<this.#r||!this.#n)return!1;let t=_(this.#t),i=null,a=[];try{let[n,s]=await Promise.all([this.#e.get(t),this.#h()]);if(n!=null&&(i=await this.#_(t,n,l,`public key`)),a=s,!i)return!1;if([i,...a.map(e=>e.data)].every(t=>e-t.lastValidatedAt<this.#r))return this.#u=e,!1;let c=await globalThis.fetch(`${this.#n}/keyurl`);if(!c.ok){let n=e-this.#r+m;return this.#i.warn(`Manifest fetch failed during revalidation, retrying in 5 min`,{status:c.status,relayerUrl:this.#n}),await this.#S(t,{...i,lastValidatedAt:n},a.map(e=>({...e,data:{...e.data,lastValidatedAt:n}}))),this.#u=n,!1}let u=f.safeParse(await c.json());if(!u.success){this.#i.error(`Relayer manifest has unexpected shape — check relayer URL and API version`,{relayerUrl:this.#n,error:o.prettifyError(u.error)});let n=e-this.#r+m;return await this.#S(t,{...i,lastValidatedAt:n},a.map(e=>({...e,data:{...e.data,lastValidatedAt:n}}))),this.#u=n,!1}let d=u.data.response,p=d.fheKeyInfo[0];r(p,`manifest.response.fheKeyInfo[0]`);let h=p.fhePublicKey.urls[0];if(i.artifactUrl&&h&&h!==i.artifactUrl)return await this.#x(t,a),this.#u=null,!0;let g={...i,lastValidatedAt:e};if(h){let e=await this.#m(h,i);if(!e.fresh)return await this.#x(t,a),this.#u=null,!0;g={...g,artifactUrl:h,etag:e.etag,lastModified:e.lastModified}}let _=[];for(let n of a){let r=d.crs[String(n.bits)]?.urls[0];if(n.data.artifactUrl&&r&&r!==n.data.artifactUrl)return await this.#x(t,a),this.#u=null,!0;let i={...n.data,lastValidatedAt:e};if(r){let e=await this.#m(r,n.data);if(!e.fresh)return await this.#x(t,a),this.#u=null,!0;i={...i,artifactUrl:r,etag:e.etag,lastModified:e.lastModified}}_.push({...n,data:i})}return await this.#S(t,g,_),this.#u=e,!1}catch(r){let o=n(r),s=r instanceof TypeError||r instanceof ReferenceError||r instanceof RangeError||r instanceof SyntaxError,c=s?`error`:`warn`;this.#i[c](s?`Unexpected error during revalidation (possible bug)`:`Revalidation failed, using cached artifacts (fail-open)`,{chainId:this.#t,relayerUrl:this.#n,error:o.message});let l=e-this.#r+m;try{i&&await this.#S(t,{...i,lastValidatedAt:l},a.map(e=>({...e,data:{...e.data,lastValidatedAt:l}})))}catch(e){this.#i.warn(`Failed to update validation timestamps after revalidation error`,{chainId:this.#t,error:n(e).message})}return this.#u=l,!1}}async#m(e,t){let n=!!(t.etag||t.lastModified),r={};t.etag&&(r[`If-None-Match`]=t.etag),t.lastModified&&(r[`If-Modified-Since`]=t.lastModified);let i=await globalThis.fetch(e,{method:`HEAD`,headers:r});if(i.status===405&&(i=await globalThis.fetch(e,{headers:r})),!i.ok&&i.status!==304)throw Error(`Artifact freshness check failed: HEAD ${e} returned ${i.status}`);let a=i.headers.get(`etag`)??void 0,o=i.headers.get(`last-modified`)??void 0;return i.status===304?{fresh:!0,etag:a??t.etag,lastModified:o??t.lastModified}:n?{fresh:!1,etag:a,lastModified:o}:{fresh:!0,etag:a,lastModified:o}}async#h(){let e=await this.#v(`Failed to read params index, CRS revalidation may be incomplete`),t=new Set([...this.#o.keys(),...e]),r=Array.from(t);return(await Promise.all(r.map(async e=>{let t=v(this.#t,e),r;try{r=await this.#e.get(t)}catch(t){return this.#i.warn(`Failed to read cached params entry during revalidation`,{chainId:this.#t,bits:e,error:n(t).message}),null}if(r==null)return null;let i=await this.#_(t,r,u,`params`,{bits:e});return i?{bits:e,key:t,data:i}:null}))).filter(e=>e!==null)}async#g(e,t,r,i,a={}){let o;try{o=await this.#e.get(e)}catch(e){return this.#i.warn(i,{chainId:this.#t,...a,error:n(e).message}),null}return o==null?null:this.#_(e,o,t,r,a)}async#_(e,t,n,r,i={}){let a=o.safeParse(n,t);return a.success?a.data:(await this.#y(e,r,{...i,error:o.prettifyError(a.error)}),null)}async#v(e){let t=y(this.#t);return await this.#g(t,d,`params index`,e)??[]}async#y(e,t,n={}){await this.#b(e),this.#i.warn(`Corrupt ${t} cache entry detected, deleting`,{chainId:this.#t,...n})}async#b(e){await this.#e.delete(e).catch(t=>{this.#i.warn(`Failed to delete cache entry`,{chainId:this.#t,key:e,error:n(t).message})})}async#x(e,t){let r=y(this.#t);try{await Promise.all([this.#e.delete(e),this.#e.delete(r),...t.map(e=>this.#e.delete(e.key))])}catch(e){this.#i.warn(`Failed to clear stale artifacts from persistent storage`,{chainId:this.#t,error:n(e).message})}this.#a=void 0,this.#o.clear()}async#S(e,t,r){let i=[this.#e.set(e,t).catch(e=>{this.#i.warn(`Failed to update public key validation timestamp`,{chainId:this.#t,error:n(e).message})}),...r.map(e=>this.#e.set(e.key,e.data).catch(e=>{this.#i.warn(`Failed to update params validation timestamp`,{chainId:this.#t,error:n(e).message})}))];await Promise.all(i)}};async function x(e,t=2){let n;for(let r=0;r<=t;r++)try{return await e()}catch(e){if(n=e,r<t&&i(e)){await S(500*2**r);continue}throw e}throw n}function S(e){return new Promise(t=>setTimeout(t,e))}var C=class{#e=null;#t=new Map;#n=null;config;logger;constructor(e,t){this.config=e,this.logger=t}async initWorker(){return this.#e?this.#e:(this.#n||=this.#r().catch(e=>{throw this.#n=null,e}),this.#n)}async#r(){let e=this.createWorker();this.wireEvents(e);try{let{type:t,payload:n}=this.getInitPayload();await this.sendRequestTo(e,t,n,this.config.initTimeout===void 0?6e4:this.config.initTimeout*1e3),this.onWorkerReady?.(e),this.#e=e}catch(t){throw this.terminateWorker(e),t}return this.#e}terminate(){if(this.#e){for(let[e,t]of this.#t)clearTimeout(t.timeoutId),t.reject(Error(`Worker terminated`)),this.#t.delete(e);this.terminateWorker(this.#e),this.#e=null}this.#n=null}handleResponse(e){let t=this.#t.get(e.id);if(!t){this.logger.warn(`[WorkerClient] Received response for unknown request`,{id:e.id});return}let n=Math.round(performance.now()-t.startTime);if(clearTimeout(t.timeoutId),this.#t.delete(e.id),e.success)this.logger.debug(`[WorkerClient] ← ${t.type} OK`,{id:e.id,elapsed:n}),t.resolve(e.data);else{this.logger.debug(`[WorkerClient] ← ${t.type} FAILED`,{id:e.id,elapsed:n,error:e.error});let r=e.serialized?a(e.serialized):Error(e.error);t.reject(r)}}handleWorkerError(e){this.logger.error(`[WorkerClient] Worker error`,{error:e});let t=this.#e;this.#e=null,this.#a(`Worker error: ${e}`),t&&this.terminateWorker(t)}handleWorkerMessageError(){this.logger.error(`[WorkerClient] Message deserialization failed`);let e=this.#e;this.#e=null,this.#a(`Worker message deserialization failed`),e&&this.terminateWorker(e)}sendRequestTo(t,n,r,i=3e4){return new Promise((a,o)=>{let s=this.generateRequestId(),c=performance.now();this.logger.debug(`[WorkerClient] → ${n}`,{id:s});let l=setTimeout(()=>{this.#t.delete(s);let t=Math.round(performance.now()-c);this.logger.debug(`[WorkerClient] ${n} timed out after ${i}ms`,{id:s,elapsed:t}),o(new e({operation:n,timeout:i/1e3,elapsed:t/1e3,worker:this.config.workerLabel})),this.config.recycleWorkerOnTimeout!==!1&&this.#i()},i);this.#t.set(s,{resolve:a,reject:o,timeoutId:l,startTime:c,type:n});let u={id:s,type:n,payload:r};this.postMessage(t,u)})}async sendRequest(e,t,n){let r=await this.initWorker();return this.sendRequestTo(r,e,t,n??(this.config.operationTimeout===void 0?3e4:this.config.operationTimeout*1e3))}#i(){let e=this.#e;if(e){this.#e=null,this.#n=null;for(let[e,n]of this.#t)clearTimeout(n.timeoutId),n.reject(new t({operation:n.type,worker:this.config.workerLabel})),this.#t.delete(e);this.logger.warn(`[WorkerClient] recycled worker after a timeout`,{worker:this.config.workerLabel}),this.terminateWorker(e)}}async generateKeypair(e){return this.sendRequest(`GENERATE_KEYPAIR`,e)}async createEIP712(e){return this.sendRequest(`CREATE_EIP712`,e)}async encrypt(e){return this.sendRequest(`ENCRYPT`,e)}async userDecrypt(e){return this.sendRequest(`USER_DECRYPT`,e)}async publicDecrypt(e){return this.sendRequest(`PUBLIC_DECRYPT`,e)}async createDelegatedUserDecryptEIP712(e){return this.sendRequest(`CREATE_DELEGATED_EIP712`,e)}async delegatedUserDecrypt(e){return this.sendRequest(`DELEGATED_USER_DECRYPT`,e)}async requestZKProofVerification(e){return this.sendRequest(`REQUEST_ZK_PROOF_VERIFICATION`,e)}async getPublicKey(e){return this.sendRequest(`GET_PUBLIC_KEY`,e)}async getPublicParams(e){return this.sendRequest(`GET_PUBLIC_PARAMS`,e)}#a(e){for(let[t,n]of this.#t)clearTimeout(n.timeoutId),n.reject(Error(e)),this.#t.delete(t)}};export{s as i,x as n,b as r,C as t};
|
|
2
|
+
//# sourceMappingURL=worker.base-client-BVnT4yJa.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"worker.base-client-BVnT4yJa.js","names":["#initPromise","#storage","#chainId","#relayerUrl","#ttlMs","#logger","#publicKeyMem","#publicKeyInflight","#loadFheEncryptionKey","#readCachedEntry","#deleteCorruptEntry","#publicParamsMem","#publicParamsInflight","#loadPublicParams","#readParamsIndex","#revalidationInflight","#revalidateIfDueInner","#lastRevalidatedAt","#collectParamEntries","#parseCachedEntry","#writeEntries","#clearAll","#checkArtifactFreshness","#deleteQuietly","#worker","#initPromise","#doInitWorker","#pendingRequests","#rejectAllPending","#recycleStuckWorker"],"sources":["../../src/relayer/base-relayer.ts","../../src/relayer/fhe-artifact-cache.ts","../../src/relayer/relayer-utils.ts","../../src/worker/worker.base-client.ts"],"sourcesContent":["import type { Address } from \"viem\";\nimport type { FheChain } from \"../chains/types\";\n\nexport abstract class BaseRelayer {\n #initPromise: Promise<void> | null = null;\n protected abstract readonly chain: FheChain;\n protected abstract init(): Promise<void>;\n\n protected async ensureInit(): Promise<void> {\n if (!this.#initPromise) {\n this.#initPromise = this.init().catch((error) => {\n this.#initPromise = null;\n throw error;\n });\n }\n return this.#initPromise;\n }\n\n protected resetInit(): void {\n this.#initPromise = null;\n }\n\n async getAclAddress(): Promise<Address> {\n return this.chain.aclContractAddress;\n }\n}\n","import { z } from \"zod/mini\";\nimport type { GenericLogger } from \"../worker/worker.types\";\nimport type { GenericStorage } from \"../types\";\nimport { assertNonNullable, toError } from \"../utils\";\nimport type { FheEncryptionKey, PublicParamsData } from \"./relayer-sdk.types\";\n\n// ── Cached data shapes ──────────────────────────────────────\n\nconst CachedArtifactBase = z.object({\n artifactUrl: z.optional(z.string()),\n etag: z.optional(z.string()),\n lastModified: z.optional(z.string()),\n // Optional + default(0) preserves the prior `?? 0` fallback for entries\n // persisted before this field was added; 0 marks them stale via the TTL check.\n // oxlint-disable-next-line no-underscore-dangle -- z._default is the Zod v4 mini API\n lastValidatedAt: z._default(z.optional(z.number().check(z.nonnegative())), 0),\n});\n\n/** Cached shape for the FHE network public key. */\nconst CachedPublicKeySchema = z.extend(CachedArtifactBase, {\n publicKeyId: z.string(),\n publicKey: z.string(),\n});\ntype CachedPublicKey = z.infer<typeof CachedPublicKeySchema>;\n\n/** Cached shape for FHE public params. */\nconst CachedPublicParamsSchema = z.extend(CachedArtifactBase, {\n publicParamsId: z.string(),\n publicParams: z.string(),\n});\ntype CachedPublicParams = z.infer<typeof CachedPublicParamsSchema>;\n\nconst ParamsIndexSchema = z.array(z.int().check(z.nonnegative()));\n\n/**\n * Manifest shape returned by the relayer `/keyurl` endpoint. Only the fields\n * actually consumed (status + first URL of each artifact) are validated; the\n * upstream payload contains additional metadata we ignore.\n */\nconst ManifestSchema = z.object({\n status: z.literal(\"succeeded\"),\n response: z.object({\n fheKeyInfo: z\n .array(\n z.object({ fhePublicKey: z.object({ urls: z.array(z.string()).check(z.minLength(1)) }) }),\n )\n .check(z.minLength(1)),\n crs: z.record(z.string(), z.object({ urls: z.array(z.string()).check(z.minLength(1)) })),\n }),\n});\n\n// ── Return types ────────────────────────────────────────────\n\n/** Return type of the FHE encryption key fetcher. */\ntype FheEncryptionKeyResult = FheEncryptionKey | null;\n\n/** Return type of the public params fetcher. */\ntype PublicParamsResult = PublicParamsData | null;\n\n// ── Constants ───────────────────────────────────────────────\n\n/** Max chunk size for String.fromCharCode to avoid call-stack overflow on large buffers. */\nconst CHUNK_SIZE = 8192;\n\n/** On revalidation failure, retry after 5 minutes instead of the full TTL. */\nconst SHORT_RETRY_MS = 5 * 60 * 1000;\n\n// ── Helpers ─────────────────────────────────────────────────\n\nfunction toBase64(bytes: Uint8Array): string {\n const chunks: string[] = [];\n for (let i = 0; i < bytes.length; i += CHUNK_SIZE) {\n chunks.push(String.fromCharCode(...bytes.subarray(i, i + CHUNK_SIZE)));\n }\n return btoa(chunks.join(\"\"));\n}\n\nfunction fromBase64(b64: string): Uint8Array {\n let binary: string;\n try {\n binary = atob(b64);\n } catch {\n throw new Error(`Invalid base64 data (length: ${b64.length})`);\n }\n if (binary.length === 0) {\n throw new Error(\"Decoded artifact is empty\");\n }\n const bytes = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) {\n bytes[i] = binary.charCodeAt(i);\n }\n return bytes;\n}\n\nfunction pubkeyStorageKey(chainId: number): string {\n return `fhe:pubkey:${chainId}`;\n}\n\nfunction paramsStorageKey(chainId: number, bits: number): string {\n return `fhe:params:${chainId}:${bits}`;\n}\n\nfunction paramsIndexKey(chainId: number): string {\n return `fhe:params-index:${chainId}`;\n}\n\ntype LogContext = Record<string, unknown>;\n\n// ── ArtifactCache ──────────────────────────────────────────\n\n/**\n * Persistent cache for FHE network public key and public params.\n * Uses a {@link GenericStorage} backend (e.g. MemoryStorage, or any\n * user-provided async key-value adapter) to avoid re-downloading large\n * binary data on every app instantiation.\n *\n * Cache keys are scoped by chain ID.\n */\nexport class FheArtifactCache {\n readonly #storage: GenericStorage;\n readonly #chainId: number;\n readonly #relayerUrl: string;\n readonly #ttlMs: number;\n readonly #logger: GenericLogger;\n #publicKeyMem: FheEncryptionKeyResult | undefined;\n #publicParamsMem = new Map<number, PublicParamsResult>();\n #publicKeyInflight: Promise<FheEncryptionKeyResult> | null = null;\n #publicParamsInflight = new Map<number, Promise<PublicParamsResult>>();\n #revalidationInflight: Promise<boolean> | null = null;\n /** In-memory guard to skip storage reads when revalidation isn't due. */\n #lastRevalidatedAt: number | null = null;\n\n constructor(opts: {\n storage: GenericStorage;\n chainId: number;\n relayerUrl: string;\n /** Cache TTL in seconds. Default: 86 400 (24 h). Set to 0 to revalidate on every operation. */\n ttl?: number;\n logger: GenericLogger;\n }) {\n this.#storage = opts.storage;\n this.#chainId = opts.chainId;\n this.#relayerUrl = opts.relayerUrl;\n this.#ttlMs = (opts.ttl ?? 86_400) * 1000;\n this.#logger = opts.logger;\n }\n\n // ── fetchFheEncryptionKeyBytes ──────────────────────────\n\n async fetchFheEncryptionKeyBytes(\n fetcher: () => Promise<FheEncryptionKeyResult>,\n ): Promise<FheEncryptionKeyResult> {\n if (this.#publicKeyMem !== undefined) {\n return this.#publicKeyMem;\n }\n\n // Deduplicate concurrent calls\n if (this.#publicKeyInflight) {\n return this.#publicKeyInflight;\n }\n\n this.#publicKeyInflight = this.#loadFheEncryptionKey(fetcher);\n try {\n return await this.#publicKeyInflight;\n } finally {\n this.#publicKeyInflight = null;\n }\n }\n\n async #loadFheEncryptionKey(\n fetcher: () => Promise<FheEncryptionKeyResult>,\n ): Promise<FheEncryptionKeyResult> {\n const key = pubkeyStorageKey(this.#chainId);\n\n const stored = await this.#readCachedEntry(\n key,\n CachedPublicKeySchema,\n \"public key\",\n \"Failed to read public key from persistent storage, falling back to network fetch\",\n );\n if (stored) {\n try {\n const result: FheEncryptionKeyResult = {\n publicKeyId: stored.publicKeyId,\n publicKey: fromBase64(stored.publicKey),\n };\n this.#publicKeyMem = result;\n return result;\n } catch (err) {\n await this.#deleteCorruptEntry(key, \"public key\", { error: toError(err).message });\n }\n }\n\n const result = await fetcher();\n if (result === null) {\n return null;\n }\n\n this.#publicKeyMem = result;\n\n try {\n const entry: CachedPublicKey = {\n publicKeyId: result.publicKeyId,\n publicKey: toBase64(result.publicKey),\n lastValidatedAt: Date.now(),\n };\n await this.#storage.set(key, entry);\n } catch (err) {\n this.#logger.warn(\"Failed to persist public key to storage\", {\n chainId: this.#chainId,\n error: toError(err).message,\n });\n }\n\n return result;\n }\n\n // ── getPublicParams ─────────────────────────────────────\n\n async getPublicParams(\n bits: number,\n fetcher: () => Promise<PublicParamsResult>,\n ): Promise<PublicParamsResult> {\n const mem = this.#publicParamsMem.get(bits);\n if (mem !== undefined) {\n return mem;\n }\n\n // Deduplicate concurrent calls\n const inflight = this.#publicParamsInflight.get(bits);\n if (inflight) {\n return inflight;\n }\n\n const promise = this.#loadPublicParams(bits, fetcher);\n this.#publicParamsInflight.set(bits, promise);\n try {\n return await promise;\n } finally {\n this.#publicParamsInflight.delete(bits);\n }\n }\n\n async #loadPublicParams(\n bits: number,\n fetcher: () => Promise<PublicParamsResult>,\n ): Promise<PublicParamsResult> {\n const key = paramsStorageKey(this.#chainId, bits);\n\n const stored = await this.#readCachedEntry(\n key,\n CachedPublicParamsSchema,\n \"params\",\n \"Failed to read public params from persistent storage, falling back to network fetch\",\n { bits },\n );\n if (stored) {\n try {\n const result: PublicParamsResult = {\n publicParamsId: stored.publicParamsId,\n publicParams: fromBase64(stored.publicParams),\n };\n this.#publicParamsMem.set(bits, result);\n return result;\n } catch (err) {\n await this.#deleteCorruptEntry(key, \"params\", { bits, error: toError(err).message });\n }\n }\n\n const result = await fetcher();\n if (result === null) {\n return null;\n }\n\n this.#publicParamsMem.set(bits, result);\n\n try {\n const entry: CachedPublicParams = {\n publicParamsId: result.publicParamsId,\n publicParams: toBase64(result.publicParams),\n lastValidatedAt: Date.now(),\n };\n await this.#storage.set(key, entry);\n\n // Update params index for cold-start CRS detection\n const idxKey = paramsIndexKey(this.#chainId);\n const existing = await this.#readParamsIndex(\"Failed to read params index from storage\");\n if (!existing.includes(bits)) {\n await this.#storage.set(idxKey, [...existing, bits]);\n }\n } catch (err) {\n this.#logger.warn(\"Failed to persist public params to storage\", {\n chainId: this.#chainId,\n bits,\n error: toError(err).message,\n });\n }\n\n return result;\n }\n\n // ── Artifact-level revalidation ─────────────────────────\n\n /**\n * Check whether cached FHE artifacts are still fresh by issuing\n * HTTP conditional requests (ETag / If-None-Match, Last-Modified /\n * If-Modified-Since) against the actual artifact CDN URLs.\n *\n * @returns `true` if the cache was invalidated and the caller should\n * re-fetch artifacts, `false` otherwise.\n */\n async revalidateIfDue(): Promise<boolean> {\n // Concurrency guard — coalesce overlapping calls\n if (this.#revalidationInflight) {\n return this.#revalidationInflight;\n }\n this.#revalidationInflight = this.#revalidateIfDueInner();\n try {\n return await this.#revalidationInflight;\n } finally {\n this.#revalidationInflight = null;\n }\n }\n\n async #revalidateIfDueInner(): Promise<boolean> {\n // Fast path: in-memory timestamp check avoids storage I/O on every call\n const now = Date.now();\n if (this.#lastRevalidatedAt !== null && now - this.#lastRevalidatedAt < this.#ttlMs) {\n return false;\n }\n\n // Skip revalidation when relayerUrl is not configured (e.g. Hardhat)\n if (!this.#relayerUrl) {\n return false;\n }\n\n const pkKey = pubkeyStorageKey(this.#chainId);\n\n // Track partial progress so the catch block can reuse already-read data\n let storedPk: CachedPublicKey | null = null;\n let paramEntries: Array<{ bits: number; key: string; data: CachedPublicParams }> = [];\n\n try {\n // 1. Read PK cache entry and collect params entries in parallel\n const [pkRaw, entries] = await Promise.all([\n this.#storage.get(pkKey),\n this.#collectParamEntries(),\n ]);\n\n // Validate PK shape\n if (pkRaw !== null && pkRaw !== undefined) {\n storedPk = await this.#parseCachedEntry(pkKey, pkRaw, CachedPublicKeySchema, \"public key\");\n }\n\n paramEntries = entries;\n\n if (!storedPk) {\n return false;\n }\n\n // 2. Check if all entries are within TTL\n const allEntries: Array<{ lastValidatedAt: number }> = [\n storedPk,\n ...paramEntries.map((e) => e.data),\n ];\n const allFresh = allEntries.every((e) => now - e.lastValidatedAt < this.#ttlMs);\n if (allFresh) {\n this.#lastRevalidatedAt = now;\n return false;\n }\n\n // 3. Fetch manifest to discover current artifact URLs\n const manifestRes = await globalThis.fetch(`${this.#relayerUrl}/keyurl`);\n if (!manifestRes.ok) {\n // Treat as transient failure — use short retry instead of full TTL\n const retryTimestamp = now - this.#ttlMs + SHORT_RETRY_MS;\n this.#logger.warn(\"Manifest fetch failed during revalidation, retrying in 5 min\", {\n status: manifestRes.status,\n relayerUrl: this.#relayerUrl,\n });\n await this.#writeEntries(\n pkKey,\n { ...storedPk, lastValidatedAt: retryTimestamp },\n paramEntries.map((e) => ({ ...e, data: { ...e.data, lastValidatedAt: retryTimestamp } })),\n );\n this.#lastRevalidatedAt = retryTimestamp;\n return false;\n }\n\n // Validate manifest shape — a malformed response indicates a permanent\n // config error (wrong relayer URL, API version mismatch), not a transient\n // network issue. Log at error level so it's actionable.\n const manifestParsed = ManifestSchema.safeParse(await manifestRes.json());\n if (!manifestParsed.success) {\n this.#logger.error(\n \"Relayer manifest has unexpected shape — check relayer URL and API version\",\n { relayerUrl: this.#relayerUrl, error: z.prettifyError(manifestParsed.error) },\n );\n // Fail-open with short retry — but the error-level log distinguishes this from transient failures\n const retryTimestamp = now - this.#ttlMs + SHORT_RETRY_MS;\n await this.#writeEntries(\n pkKey,\n { ...storedPk, lastValidatedAt: retryTimestamp },\n paramEntries.map((e) => ({ ...e, data: { ...e.data, lastValidatedAt: retryTimestamp } })),\n );\n this.#lastRevalidatedAt = retryTimestamp;\n return false;\n }\n const validManifest = manifestParsed.data.response;\n const fheKeyEntry = validManifest.fheKeyInfo[0];\n assertNonNullable(fheKeyEntry, \"manifest.response.fheKeyInfo[0]\");\n\n // ── 4. Check PK artifact ──────────────────────────\n const pkArtifactUrl = fheKeyEntry.fhePublicKey.urls[0];\n // URL change → stale\n if (storedPk.artifactUrl && pkArtifactUrl && pkArtifactUrl !== storedPk.artifactUrl) {\n await this.#clearAll(pkKey, paramEntries);\n this.#lastRevalidatedAt = null;\n return true;\n }\n\n let updatedPk: CachedPublicKey = { ...storedPk, lastValidatedAt: now };\n if (pkArtifactUrl) {\n const freshness = await this.#checkArtifactFreshness(pkArtifactUrl, storedPk);\n if (!freshness.fresh) {\n await this.#clearAll(pkKey, paramEntries);\n this.#lastRevalidatedAt = null;\n return true;\n }\n updatedPk = {\n ...updatedPk,\n artifactUrl: pkArtifactUrl,\n etag: freshness.etag,\n lastModified: freshness.lastModified,\n };\n }\n\n // ── 5. Check each CRS artifact ────────────────────\n const updatedParamEntries: typeof paramEntries = [];\n for (const entry of paramEntries) {\n const manifestCrs = validManifest.crs[String(entry.bits)];\n const crsUrl = manifestCrs?.urls[0];\n\n // URL change → stale\n if (entry.data.artifactUrl && crsUrl && crsUrl !== entry.data.artifactUrl) {\n await this.#clearAll(pkKey, paramEntries);\n this.#lastRevalidatedAt = null;\n return true;\n }\n\n let updatedData: CachedPublicParams = { ...entry.data, lastValidatedAt: now };\n if (crsUrl) {\n const freshness = await this.#checkArtifactFreshness(crsUrl, entry.data);\n if (!freshness.fresh) {\n await this.#clearAll(pkKey, paramEntries);\n this.#lastRevalidatedAt = null;\n return true;\n }\n updatedData = {\n ...updatedData,\n artifactUrl: crsUrl,\n etag: freshness.etag,\n lastModified: freshness.lastModified,\n };\n }\n updatedParamEntries.push({ ...entry, data: updatedData });\n }\n\n // 6. All fresh — update timestamps and HTTP validators\n await this.#writeEntries(pkKey, updatedPk, updatedParamEntries);\n this.#lastRevalidatedAt = now;\n return false;\n } catch (err) {\n const error = toError(err);\n const isProgrammingError =\n err instanceof TypeError ||\n err instanceof ReferenceError ||\n err instanceof RangeError ||\n err instanceof SyntaxError;\n const level = isProgrammingError ? \"error\" : \"warn\";\n this.#logger[level](\n isProgrammingError\n ? \"Unexpected error during revalidation (possible bug)\"\n : \"Revalidation failed, using cached artifacts (fail-open)\",\n { chainId: this.#chainId, relayerUrl: this.#relayerUrl, error: error.message },\n );\n\n // Fail-open: use short retry interval (5 min) instead of full TTL\n const retryTimestamp = now - this.#ttlMs + SHORT_RETRY_MS;\n try {\n if (storedPk) {\n await this.#writeEntries(\n pkKey,\n { ...storedPk, lastValidatedAt: retryTimestamp },\n paramEntries.map((e) => ({\n ...e,\n data: { ...e.data, lastValidatedAt: retryTimestamp },\n })),\n );\n }\n } catch (innerErr) {\n this.#logger.warn(\"Failed to update validation timestamps after revalidation error\", {\n chainId: this.#chainId,\n error: toError(innerErr).message,\n });\n }\n this.#lastRevalidatedAt = retryTimestamp;\n return false;\n }\n }\n\n // ── Artifact freshness via HTTP conditional requests ───\n\n async #checkArtifactFreshness(\n url: string,\n cached: { etag?: string; lastModified?: string },\n ): Promise<{ fresh: boolean; etag?: string; lastModified?: string }> {\n const hasValidators = Boolean(cached.etag || cached.lastModified);\n const headers: Record<string, string> = {};\n if (cached.etag) {\n headers[\"If-None-Match\"] = cached.etag;\n }\n if (cached.lastModified) {\n headers[\"If-Modified-Since\"] = cached.lastModified;\n }\n\n // HEAD avoids downloading the (potentially multi-MB) artifact body.\n // With conditional headers, the server returns 304 if unchanged or 200 (no body) if stale.\n let res = await globalThis.fetch(url, { method: \"HEAD\", headers });\n\n // Fallback to GET if HEAD is not supported (e.g. some CDN/WAF configs)\n if (res.status === 405) {\n res = await globalThis.fetch(url, { headers });\n }\n\n // Treat server errors as transient — throw so the outer catch applies fail-open with short retry.\n // Without this, a CDN 5xx would be misinterpreted as \"artifact changed\" and wipe the cache.\n if (!res.ok && res.status !== 304) {\n throw new Error(`Artifact freshness check failed: HEAD ${url} returned ${res.status}`);\n }\n\n const etag = res.headers.get(\"etag\") ?? undefined;\n const lastModified = res.headers.get(\"last-modified\") ?? undefined;\n\n if (res.status === 304) {\n return {\n fresh: true,\n etag: etag ?? cached.etag,\n lastModified: lastModified ?? cached.lastModified,\n };\n }\n\n if (!hasValidators) {\n // First revalidation — capture validators, treat as fresh\n return { fresh: true, etag, lastModified };\n }\n\n // 200 = artifact changed\n return { fresh: false, etag, lastModified };\n }\n\n // ── Internal helpers ────────────────────────────────────\n\n async #collectParamEntries(): Promise<\n Array<{ bits: number; key: string; data: CachedPublicParams }>\n > {\n // Merge in-memory keys with persisted index for cold-start CRS detection\n const persistedBits = await this.#readParamsIndex(\n \"Failed to read params index, CRS revalidation may be incomplete\",\n );\n const allBits = new Set([...this.#publicParamsMem.keys(), ...persistedBits]);\n\n const bitsArray = Array.from(allBits);\n const results = await Promise.all(\n bitsArray.map(async (bits) => {\n const pKey = paramsStorageKey(this.#chainId, bits);\n // Separate storage read from validation so we only delete on corruption\n let raw: unknown;\n try {\n raw = await this.#storage.get(pKey);\n } catch (err) {\n this.#logger.warn(\"Failed to read cached params entry during revalidation\", {\n chainId: this.#chainId,\n bits,\n error: toError(err).message,\n });\n return null;\n }\n if (raw === null || raw === undefined) {\n return null;\n }\n const data = await this.#parseCachedEntry(pKey, raw, CachedPublicParamsSchema, \"params\", {\n bits,\n });\n if (!data) {\n return null;\n }\n return { bits, key: pKey, data };\n }),\n );\n return results.filter(\n (e): e is { bits: number; key: string; data: CachedPublicParams } => e !== null,\n );\n }\n\n async #readCachedEntry<T>(\n key: string,\n schema: z.core.$ZodType<T>,\n label: string,\n readError: string,\n context: LogContext = {},\n ): Promise<T | null> {\n let raw: unknown;\n try {\n raw = await this.#storage.get(key);\n } catch (err) {\n this.#logger.warn(readError, {\n chainId: this.#chainId,\n ...context,\n error: toError(err).message,\n });\n return null;\n }\n if (raw === null || raw === undefined) {\n return null;\n }\n return this.#parseCachedEntry(key, raw, schema, label, context);\n }\n\n async #parseCachedEntry<T>(\n key: string,\n raw: unknown,\n schema: z.core.$ZodType<T>,\n label: string,\n context: LogContext = {},\n ): Promise<T | null> {\n const parsed = z.safeParse(schema, raw);\n if (parsed.success) {\n return parsed.data;\n }\n await this.#deleteCorruptEntry(key, label, {\n ...context,\n error: z.prettifyError(parsed.error),\n });\n return null;\n }\n\n async #readParamsIndex(readError: string): Promise<number[]> {\n const idxKey = paramsIndexKey(this.#chainId);\n const index = await this.#readCachedEntry(idxKey, ParamsIndexSchema, \"params index\", readError);\n return index ?? [];\n }\n\n async #deleteCorruptEntry(key: string, label: string, context: LogContext = {}): Promise<void> {\n await this.#deleteQuietly(key);\n this.#logger.warn(`Corrupt ${label} cache entry detected, deleting`, {\n chainId: this.#chainId,\n ...context,\n });\n }\n\n async #deleteQuietly(key: string): Promise<void> {\n await this.#storage.delete(key).catch((err) => {\n this.#logger.warn(\"Failed to delete cache entry\", {\n chainId: this.#chainId,\n key,\n error: toError(err).message,\n });\n });\n }\n\n async #clearAll(pkKey: string, paramEntries: Array<{ key: string }>): Promise<void> {\n const idxKey = paramsIndexKey(this.#chainId);\n // Delete from persistent storage first — if this fails, in-memory cache\n // still serves stale data, but the next revalidation cycle will retry.\n try {\n await Promise.all([\n this.#storage.delete(pkKey),\n this.#storage.delete(idxKey),\n ...paramEntries.map((entry) => this.#storage.delete(entry.key)),\n ]);\n } catch (err) {\n this.#logger.warn(\"Failed to clear stale artifacts from persistent storage\", {\n chainId: this.#chainId,\n error: toError(err).message,\n });\n }\n // Clear in-memory after storage to avoid re-loading stale entries on failure\n this.#publicKeyMem = undefined;\n this.#publicParamsMem.clear();\n }\n\n async #writeEntries(\n pkKey: string,\n pk: CachedPublicKey,\n paramEntries: Array<{ key: string; data: CachedPublicParams }>,\n ): Promise<void> {\n const writes = [\n this.#storage.set(pkKey, pk).catch((err) => {\n this.#logger.warn(\"Failed to update public key validation timestamp\", {\n chainId: this.#chainId,\n error: toError(err).message,\n });\n }),\n ...paramEntries.map((entry) =>\n this.#storage.set(entry.key, entry.data).catch((err) => {\n this.#logger.warn(\"Failed to update params validation timestamp\", {\n chainId: this.#chainId,\n error: toError(err).message,\n });\n }),\n ),\n ];\n await Promise.all(writes);\n }\n}\n","import { isRetryableRelayerError } from \"../utils/error\";\n\nconst MAX_RETRIES = 2;\nconst RETRY_BASE_MS = 500;\n\n/**\n * Retry a **relayer** operation with exponential backoff.\n *\n * The SDK only owns retries for the relayer transport — the one network actor\n * viem/ethers don't model. Consumer-RPC reads (e.g. the worker's ACL\n * `persistAllowed` pre-check) go through the integrator's own viem/ethers\n * client, which already retries transport faults; retrying them again here would\n * stack on top of that. So this gate ({@link isRetryableRelayerError}) retries\n * only relayer-attributable transients (HTTP 502/503/504 and relayer-boundary\n * network failures) and defers everything else — consumer-RPC faults, timeouts,\n * relayer back-pressure (429, surfaced with `retryAfter`), and terminal errors.\n */\nexport async function withRetry<T>(fn: () => Promise<T>, retries = MAX_RETRIES): Promise<T> {\n let lastError: unknown;\n for (let attempt = 0; attempt <= retries; attempt++) {\n try {\n return await fn();\n } catch (error) {\n lastError = error;\n if (attempt < retries && isRetryableRelayerError(error)) {\n await sleep(RETRY_BASE_MS * 2 ** attempt);\n continue;\n }\n throw error;\n }\n }\n throw lastError;\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n","import type {\n CreateDelegatedEIP712Payload,\n CreateDelegatedEIP712ResponseData,\n CreateEIP712Payload,\n CreateEIP712ResponseData,\n DelegatedUserDecryptPayload,\n DelegatedUserDecryptResponseData,\n EncryptPayload,\n EncryptResponseData,\n GenerateKeypairRequest,\n GenerateKeypairResponseData,\n GetPublicKeyRequest,\n GetPublicKeyResponseData,\n GetPublicParamsRequest,\n GetPublicParamsResponseData,\n InitResponseData,\n PublicDecryptPayload,\n PublicDecryptResponseData,\n RequestZKProofVerificationRequest,\n RequestZKProofVerificationResponseData,\n GenericLogger,\n UserDecryptPayload,\n UserDecryptResponseData,\n WorkerEnv,\n WorkerRequest,\n WorkerRequestType,\n WorkerResponse,\n} from \"./worker.types\";\nimport { deserializeError } from \"../utils/error\";\nimport { WorkerRecycledError, WorkerTimeoutError } from \"../errors/timeout\";\n\n/**\n * Timeout / recovery knobs shared by every worker client (node + web), so the\n * base class can read them generically off `config`.\n */\nexport interface WorkerClientTimeoutConfig {\n /** Per-operation timeout in **seconds**. Defaults to 30. */\n operationTimeout?: number;\n /** WASM-init timeout in **seconds**. Defaults to 60. */\n initTimeout?: number;\n /**\n * Recycle (terminate + lazily re-init) the worker after an operation timeout,\n * so a hung thread can't keep serving requests. Defaults to `true`.\n */\n recycleWorkerOnTimeout?: boolean;\n /** Diagnostic label surfaced on {@link WorkerTimeoutError} (e.g. `node-worker-2`). */\n workerLabel?: string;\n}\n\n/** Pending request tracker */\ninterface PendingRequest<T> {\n resolve: (value: T) => void;\n reject: (error: Error) => void;\n timeoutId: ReturnType<typeof setTimeout>;\n startTime: number;\n type: WorkerRequestType;\n}\n\n/** Default timeout for operations (30 seconds) */\nexport const DEFAULT_TIMEOUT_MS = 30_000;\n\n/** Extended timeout for WASM initialization (60 seconds) */\nexport const INIT_TIMEOUT_MS = 60_000;\n\n/**\n * Abstract base class for worker clients (browser Web Worker and Node.js worker_threads).\n * Encapsulates all shared logic: pending request tracking, timeouts, init dedup, domain methods.\n * Subclasses implement the abstract hooks for platform-specific worker creation and messaging.\n */\nexport abstract class BaseWorkerClient<TWorker, TConfig extends WorkerClientTimeoutConfig> {\n #worker: TWorker | null = null;\n #pendingRequests = new Map<string, PendingRequest<unknown>>();\n #initPromise: Promise<TWorker> | null = null;\n protected readonly config: TConfig;\n protected readonly logger: GenericLogger;\n\n constructor(config: TConfig, logger: GenericLogger) {\n this.config = config;\n this.logger = logger;\n }\n\n // ===========================================================================\n // Abstract hooks — subclasses must implement\n // ===========================================================================\n\n /** Create the platform-specific worker instance. */\n protected abstract createWorker(): TWorker;\n\n /** Wire message/error/messageerror events on the worker. */\n protected abstract wireEvents(worker: TWorker): void;\n\n /** Post a message to the worker. */\n protected abstract postMessage(worker: TWorker, request: WorkerRequest): void;\n\n /** Terminate the platform-specific worker. */\n protected abstract terminateWorker(worker: TWorker): void;\n\n /** Generate a unique request ID. */\n protected abstract generateRequestId(): string;\n\n /** Return the init request type and payload. */\n protected abstract getInitPayload(): {\n type: WorkerRequestType;\n payload: WorkerRequest[\"payload\"];\n };\n\n /** Subclasses set \"web\" or \"node\" — stamps the env discriminant on INIT payloads. */\n protected abstract readonly env: WorkerEnv;\n\n /** Optional hook called after worker init succeeds (e.g. for node worker.unref()). */\n protected onWorkerReady?(_worker: TWorker): void;\n\n // ===========================================================================\n // Shared init / terminate\n // ===========================================================================\n\n async initWorker(): Promise<TWorker> {\n if (this.#worker) {\n return this.#worker;\n }\n\n if (!this.#initPromise) {\n this.#initPromise = this.#doInitWorker().catch((error) => {\n this.#initPromise = null;\n throw error;\n });\n }\n return this.#initPromise;\n }\n\n async #doInitWorker(): Promise<TWorker> {\n const worker = this.createWorker();\n this.wireEvents(worker);\n\n try {\n const { type, payload } = this.getInitPayload();\n await this.sendRequestTo<InitResponseData>(\n worker,\n type,\n payload,\n this.config.initTimeout !== undefined ? this.config.initTimeout * 1000 : INIT_TIMEOUT_MS,\n );\n this.onWorkerReady?.(worker);\n this.#worker = worker;\n } catch (error) {\n this.terminateWorker(worker);\n throw error;\n }\n\n return this.#worker;\n }\n\n terminate(): void {\n if (this.#worker) {\n for (const [id, pending] of this.#pendingRequests) {\n clearTimeout(pending.timeoutId);\n pending.reject(new Error(\"Worker terminated\"));\n this.#pendingRequests.delete(id);\n }\n\n this.terminateWorker(this.#worker);\n this.#worker = null;\n }\n this.#initPromise = null;\n }\n\n // ===========================================================================\n // Message handling\n // ===========================================================================\n\n protected handleResponse(response: WorkerResponse<unknown>): void {\n const pending = this.#pendingRequests.get(response.id);\n\n if (!pending) {\n this.logger.warn(\"[WorkerClient] Received response for unknown request\", { id: response.id });\n return;\n }\n\n const elapsed = Math.round(performance.now() - pending.startTime);\n\n clearTimeout(pending.timeoutId);\n this.#pendingRequests.delete(response.id);\n\n if (response.success) {\n this.logger.debug(`[WorkerClient] ← ${pending.type} OK`, { id: response.id, elapsed });\n pending.resolve(response.data);\n } else {\n // A failed worker response is a handled operation failure: it is\n // propagated to the caller via `pending.reject(err)` below. Logging it at\n // `error` would duplicate a handled failure into the consumer's\n // monitoring (the regression Dfns hit), so it stays at `debug`.\n this.logger.debug(`[WorkerClient] ← ${pending.type} FAILED`, {\n id: response.id,\n elapsed,\n error: response.error,\n });\n // Rebuild the error (and its cause chain) from the structured-clone-safe\n // envelope the worker serialized. Classification happens once on the main\n // thread (`wrapDecryptError`) — the worker stays taxonomy-agnostic.\n const err = response.serialized\n ? deserializeError(response.serialized)\n : new Error(response.error);\n pending.reject(err);\n }\n }\n\n protected handleWorkerError(message: string): void {\n this.logger.error(\"[WorkerClient] Worker error\", { error: message });\n const worker = this.#worker;\n this.#worker = null;\n this.#rejectAllPending(`Worker error: ${message}`);\n if (worker) {\n this.terminateWorker(worker);\n }\n }\n\n protected handleWorkerMessageError(): void {\n this.logger.error(\"[WorkerClient] Message deserialization failed\");\n const worker = this.#worker;\n this.#worker = null;\n this.#rejectAllPending(\"Worker message deserialization failed\");\n if (worker) {\n this.terminateWorker(worker);\n }\n }\n\n // ===========================================================================\n // Request sending\n // ===========================================================================\n\n protected sendRequestTo<T>(\n worker: TWorker,\n type: WorkerRequestType,\n payload: WorkerRequest[\"payload\"],\n timeoutMs: number = DEFAULT_TIMEOUT_MS,\n ): Promise<T> {\n return new Promise<T>((resolve, reject) => {\n const id = this.generateRequestId();\n const startTime = performance.now();\n this.logger.debug(`[WorkerClient] → ${type}`, { id });\n\n const timeoutId = setTimeout(() => {\n this.#pendingRequests.delete(id);\n const elapsed = Math.round(performance.now() - startTime);\n // A timeout is surfaced via the rejected promise below, so it is a\n // handled failure and stays at `debug` rather than `error`.\n this.logger.debug(`[WorkerClient] ${type} timed out after ${timeoutMs}ms`, { id, elapsed });\n reject(\n new WorkerTimeoutError({\n operation: type,\n timeout: timeoutMs / 1000,\n elapsed: elapsed / 1000,\n worker: this.config.workerLabel,\n }),\n );\n // Self-heal: the worker is presumed stuck, so recycle it (terminate +\n // lazy re-init) — otherwise the hung thread keeps attracting work (the\n // pool's least-connections sees it idle once this request settles).\n if (this.config.recycleWorkerOnTimeout !== false) {\n this.#recycleStuckWorker();\n }\n }, timeoutMs);\n\n this.#pendingRequests.set(id, {\n resolve: resolve as (value: unknown) => void,\n reject,\n timeoutId,\n startTime,\n type,\n });\n\n const request = { id, type, payload } as WorkerRequest;\n this.postMessage(worker, request);\n });\n }\n\n protected async sendRequest<T>(\n type: WorkerRequestType,\n payload: WorkerRequest[\"payload\"],\n timeoutMs?: number,\n ): Promise<T> {\n const worker = await this.initWorker();\n return this.sendRequestTo<T>(\n worker,\n type,\n payload,\n timeoutMs ??\n (this.config.operationTimeout !== undefined\n ? this.config.operationTimeout * 1000\n : DEFAULT_TIMEOUT_MS),\n );\n }\n\n /**\n * Terminate the (presumed-stuck) active worker after a timeout and reject any\n * other in-flight requests on it, so the next call lazily re-inits a fresh\n * worker. Mirrors {@link handleWorkerError}'s crash-recovery shape.\n */\n #recycleStuckWorker(): void {\n const worker = this.#worker;\n if (!worker) {\n return;\n }\n this.#worker = null;\n this.#initPromise = null;\n // The sibling requests still in flight did not time out — they are aborted\n // collateral of recycling the worker. Reject them with a typed, retryable\n // `WorkerRecycledError` (not their own `WorkerTimeoutError`, since they never\n // exceeded a bound) so consumers can retry them instead of receiving a\n // terminal `DecryptionFailedError` — the fate of a bare `Error` through\n // `wrapDecryptError`. Only the operation that actually exceeded its bound\n // gets a `WorkerTimeoutError`.\n for (const [id, pending] of this.#pendingRequests) {\n clearTimeout(pending.timeoutId);\n pending.reject(\n new WorkerRecycledError({ operation: pending.type, worker: this.config.workerLabel }),\n );\n this.#pendingRequests.delete(id);\n }\n this.logger.warn(\"[WorkerClient] recycled worker after a timeout\", {\n worker: this.config.workerLabel,\n });\n this.terminateWorker(worker);\n }\n\n // ===========================================================================\n // Domain methods\n // ===========================================================================\n\n async generateKeypair(\n params: GenerateKeypairRequest[\"payload\"],\n ): Promise<GenerateKeypairResponseData> {\n return this.sendRequest<GenerateKeypairResponseData>(\"GENERATE_KEYPAIR\", params);\n }\n\n async createEIP712(params: CreateEIP712Payload): Promise<CreateEIP712ResponseData> {\n return this.sendRequest<CreateEIP712ResponseData>(\"CREATE_EIP712\", params);\n }\n\n async encrypt(params: EncryptPayload): Promise<EncryptResponseData> {\n return this.sendRequest<EncryptResponseData>(\"ENCRYPT\", params);\n }\n\n async userDecrypt(params: UserDecryptPayload): Promise<UserDecryptResponseData> {\n return this.sendRequest<UserDecryptResponseData>(\"USER_DECRYPT\", params);\n }\n\n async publicDecrypt(params: PublicDecryptPayload): Promise<PublicDecryptResponseData> {\n return this.sendRequest<PublicDecryptResponseData>(\"PUBLIC_DECRYPT\", params);\n }\n\n async createDelegatedUserDecryptEIP712(\n params: CreateDelegatedEIP712Payload,\n ): Promise<CreateDelegatedEIP712ResponseData> {\n return this.sendRequest<CreateDelegatedEIP712ResponseData>(\"CREATE_DELEGATED_EIP712\", params);\n }\n\n async delegatedUserDecrypt(\n params: DelegatedUserDecryptPayload,\n ): Promise<DelegatedUserDecryptResponseData> {\n return this.sendRequest<DelegatedUserDecryptResponseData>(\"DELEGATED_USER_DECRYPT\", params);\n }\n\n async requestZKProofVerification(\n params: RequestZKProofVerificationRequest[\"payload\"],\n ): Promise<RequestZKProofVerificationResponseData> {\n return this.sendRequest<RequestZKProofVerificationResponseData>(\n \"REQUEST_ZK_PROOF_VERIFICATION\",\n params,\n );\n }\n\n async getPublicKey(params: GetPublicKeyRequest[\"payload\"]): Promise<GetPublicKeyResponseData> {\n return this.sendRequest<GetPublicKeyResponseData>(\"GET_PUBLIC_KEY\", params);\n }\n\n async getPublicParams(\n params: GetPublicParamsRequest[\"payload\"],\n ): Promise<GetPublicParamsResponseData> {\n return this.sendRequest<GetPublicParamsResponseData>(\"GET_PUBLIC_PARAMS\", params);\n }\n\n // ===========================================================================\n // Internal helpers\n // ===========================================================================\n\n #rejectAllPending(message: string): void {\n for (const [id, pending] of this.#pendingRequests) {\n clearTimeout(pending.timeoutId);\n pending.reject(new Error(message));\n this.#pendingRequests.delete(id);\n }\n }\n}\n"],"mappings":"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"}
|