@cofhe/sdk 0.0.0-alpha-20260409113701
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/CHANGELOG.md +146 -0
- package/adapters/ethers5.test.ts +174 -0
- package/adapters/ethers5.ts +36 -0
- package/adapters/ethers6.test.ts +169 -0
- package/adapters/ethers6.ts +36 -0
- package/adapters/hardhat-node.ts +167 -0
- package/adapters/hardhat.hh2.test.ts +159 -0
- package/adapters/hardhat.ts +36 -0
- package/adapters/index.test.ts +20 -0
- package/adapters/index.ts +5 -0
- package/adapters/smartWallet.ts +99 -0
- package/adapters/test-utils.ts +53 -0
- package/adapters/types.ts +6 -0
- package/adapters/wagmi.test.ts +156 -0
- package/adapters/wagmi.ts +17 -0
- package/chains/chains/arbSepolia.ts +14 -0
- package/chains/chains/baseSepolia.ts +14 -0
- package/chains/chains/hardhat.ts +15 -0
- package/chains/chains/localcofhe.ts +14 -0
- package/chains/chains/sepolia.ts +14 -0
- package/chains/chains.test.ts +50 -0
- package/chains/defineChain.ts +18 -0
- package/chains/index.ts +35 -0
- package/chains/types.ts +32 -0
- package/core/baseBuilder.ts +119 -0
- package/core/client.test.ts +429 -0
- package/core/client.ts +341 -0
- package/core/clientTypes.ts +119 -0
- package/core/config.test.ts +242 -0
- package/core/config.ts +225 -0
- package/core/consts.ts +22 -0
- package/core/decrypt/MockThresholdNetworkAbi.ts +179 -0
- package/core/decrypt/cofheMocksDecryptForTx.ts +84 -0
- package/core/decrypt/cofheMocksDecryptForView.ts +48 -0
- package/core/decrypt/decryptForTxBuilder.ts +359 -0
- package/core/decrypt/decryptForViewBuilder.ts +332 -0
- package/core/decrypt/decryptUtils.ts +28 -0
- package/core/decrypt/pollCallbacks.test.ts +194 -0
- package/core/decrypt/polling.ts +14 -0
- package/core/decrypt/tnDecryptUtils.ts +65 -0
- package/core/decrypt/tnDecryptV1.ts +171 -0
- package/core/decrypt/tnDecryptV2.ts +365 -0
- package/core/decrypt/tnSealOutputV1.ts +59 -0
- package/core/decrypt/tnSealOutputV2.ts +324 -0
- package/core/decrypt/verifyDecryptResult.ts +52 -0
- package/core/encrypt/MockZkVerifierAbi.ts +106 -0
- package/core/encrypt/cofheMocksZkVerifySign.ts +281 -0
- package/core/encrypt/encryptInputsBuilder.test.ts +747 -0
- package/core/encrypt/encryptInputsBuilder.ts +583 -0
- package/core/encrypt/encryptUtils.ts +67 -0
- package/core/encrypt/zkPackProveVerify.ts +335 -0
- package/core/error.ts +168 -0
- package/core/fetchKeys.test.ts +195 -0
- package/core/fetchKeys.ts +144 -0
- package/core/index.ts +106 -0
- package/core/keyStore.test.ts +226 -0
- package/core/keyStore.ts +154 -0
- package/core/permits.test.ts +493 -0
- package/core/permits.ts +201 -0
- package/core/types.ts +419 -0
- package/core/utils.ts +130 -0
- package/dist/adapters.cjs +88 -0
- package/dist/adapters.d.cts +14576 -0
- package/dist/adapters.d.ts +14576 -0
- package/dist/adapters.js +83 -0
- package/dist/chains.cjs +111 -0
- package/dist/chains.d.cts +121 -0
- package/dist/chains.d.ts +121 -0
- package/dist/chains.js +1 -0
- package/dist/chunk-36FBWLUS.js +3310 -0
- package/dist/chunk-7HLGHV67.js +990 -0
- package/dist/chunk-TBLR7NNE.js +102 -0
- package/dist/clientTypes-AVSCBet7.d.cts +998 -0
- package/dist/clientTypes-flH1ju82.d.ts +998 -0
- package/dist/core.cjs +4362 -0
- package/dist/core.d.cts +138 -0
- package/dist/core.d.ts +138 -0
- package/dist/core.js +3 -0
- package/dist/node.cjs +4225 -0
- package/dist/node.d.cts +22 -0
- package/dist/node.d.ts +22 -0
- package/dist/node.js +91 -0
- package/dist/permit-jRirYqFt.d.cts +376 -0
- package/dist/permit-jRirYqFt.d.ts +376 -0
- package/dist/permits.cjs +1025 -0
- package/dist/permits.d.cts +353 -0
- package/dist/permits.d.ts +353 -0
- package/dist/permits.js +1 -0
- package/dist/types-YiAC4gig.d.cts +33 -0
- package/dist/types-YiAC4gig.d.ts +33 -0
- package/dist/web.cjs +4434 -0
- package/dist/web.d.cts +42 -0
- package/dist/web.d.ts +42 -0
- package/dist/web.js +256 -0
- package/dist/zkProve.worker.cjs +93 -0
- package/dist/zkProve.worker.d.cts +2 -0
- package/dist/zkProve.worker.d.ts +2 -0
- package/dist/zkProve.worker.js +91 -0
- package/node/client.test.ts +159 -0
- package/node/config.test.ts +68 -0
- package/node/encryptInputs.test.ts +155 -0
- package/node/index.ts +97 -0
- package/node/storage.ts +51 -0
- package/package.json +121 -0
- package/permits/index.ts +68 -0
- package/permits/localstorage.test.ts +113 -0
- package/permits/onchain-utils.ts +221 -0
- package/permits/permit.test.ts +534 -0
- package/permits/permit.ts +386 -0
- package/permits/sealing.test.ts +84 -0
- package/permits/sealing.ts +131 -0
- package/permits/signature.ts +79 -0
- package/permits/store.test.ts +88 -0
- package/permits/store.ts +156 -0
- package/permits/test-utils.ts +28 -0
- package/permits/types.ts +204 -0
- package/permits/utils.ts +58 -0
- package/permits/validation.test.ts +361 -0
- package/permits/validation.ts +327 -0
- package/web/client.web.test.ts +159 -0
- package/web/config.web.test.ts +69 -0
- package/web/const.ts +2 -0
- package/web/encryptInputs.web.test.ts +172 -0
- package/web/index.ts +166 -0
- package/web/storage.ts +49 -0
- package/web/worker.builder.web.test.ts +148 -0
- package/web/worker.config.web.test.ts +329 -0
- package/web/worker.output.web.test.ts +84 -0
- package/web/workerManager.test.ts +80 -0
- package/web/workerManager.ts +214 -0
- package/web/workerManager.web.test.ts +114 -0
- package/web/zkProve.worker.ts +133 -0
package/dist/node.cjs
ADDED
|
@@ -0,0 +1,4225 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var vanilla = require('zustand/vanilla');
|
|
4
|
+
var viem = require('viem');
|
|
5
|
+
var chains = require('viem/chains');
|
|
6
|
+
var accounts = require('viem/accounts');
|
|
7
|
+
var zod = require('zod');
|
|
8
|
+
var middleware = require('zustand/middleware');
|
|
9
|
+
var immer = require('immer');
|
|
10
|
+
var nacl = require('tweetnacl');
|
|
11
|
+
var fs = require('fs');
|
|
12
|
+
var path = require('path');
|
|
13
|
+
var nodeTfhe = require('node-tfhe');
|
|
14
|
+
|
|
15
|
+
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
16
|
+
|
|
17
|
+
var nacl__default = /*#__PURE__*/_interopDefault(nacl);
|
|
18
|
+
|
|
19
|
+
// core/client.ts
|
|
20
|
+
|
|
21
|
+
// core/error.ts
|
|
22
|
+
var CofheError = class _CofheError extends Error {
|
|
23
|
+
code;
|
|
24
|
+
cause;
|
|
25
|
+
hint;
|
|
26
|
+
context;
|
|
27
|
+
constructor({ code, message, cause, hint, context }) {
|
|
28
|
+
const fullMessage = cause ? `${message} | Caused by: ${cause.message}` : message;
|
|
29
|
+
super(fullMessage);
|
|
30
|
+
this.name = "CofheError";
|
|
31
|
+
this.code = code;
|
|
32
|
+
this.cause = cause;
|
|
33
|
+
this.hint = hint;
|
|
34
|
+
this.context = context;
|
|
35
|
+
if (Error.captureStackTrace) {
|
|
36
|
+
Error.captureStackTrace(this, _CofheError);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Creates a CofheError from an unknown error
|
|
41
|
+
* If the error is a CofheError, it is returned unchanged, else a new CofheError is created
|
|
42
|
+
* If a wrapperError is provided, it is used to create the new CofheError, else a default is used
|
|
43
|
+
*/
|
|
44
|
+
static fromError(error, wrapperError) {
|
|
45
|
+
if (isCofheError(error))
|
|
46
|
+
return error;
|
|
47
|
+
const cause = error instanceof Error ? error : new Error(`${error}`);
|
|
48
|
+
return new _CofheError({
|
|
49
|
+
code: wrapperError?.code ?? "INTERNAL_ERROR" /* InternalError */,
|
|
50
|
+
message: wrapperError?.message ?? "An internal error occurred",
|
|
51
|
+
hint: wrapperError?.hint,
|
|
52
|
+
context: wrapperError?.context,
|
|
53
|
+
cause
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Serializes the error to JSON string with proper handling of Error objects
|
|
58
|
+
*/
|
|
59
|
+
serialize() {
|
|
60
|
+
return bigintSafeJsonStringify({
|
|
61
|
+
name: this.name,
|
|
62
|
+
code: this.code,
|
|
63
|
+
message: this.message,
|
|
64
|
+
hint: this.hint,
|
|
65
|
+
context: this.context,
|
|
66
|
+
cause: this.cause ? {
|
|
67
|
+
name: this.cause.name,
|
|
68
|
+
message: this.cause.message,
|
|
69
|
+
stack: this.cause.stack
|
|
70
|
+
} : void 0,
|
|
71
|
+
stack: this.stack
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Returns a human-readable string representation of the error
|
|
76
|
+
*/
|
|
77
|
+
toString() {
|
|
78
|
+
const parts = [`${this.name} [${this.code}]: ${this.message}`];
|
|
79
|
+
if (this.hint) {
|
|
80
|
+
parts.push(`Hint: ${this.hint}`);
|
|
81
|
+
}
|
|
82
|
+
if (this.context && Object.keys(this.context).length > 0) {
|
|
83
|
+
parts.push(`Context: ${bigintSafeJsonStringify(this.context)}`);
|
|
84
|
+
}
|
|
85
|
+
if (this.stack) {
|
|
86
|
+
parts.push(`
|
|
87
|
+
Stack trace:`);
|
|
88
|
+
parts.push(this.stack);
|
|
89
|
+
}
|
|
90
|
+
if (this.cause) {
|
|
91
|
+
parts.push(`
|
|
92
|
+
Caused by: ${this.cause.name}: ${this.cause.message}`);
|
|
93
|
+
if (this.cause.stack) {
|
|
94
|
+
parts.push(this.cause.stack);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return parts.join("\n");
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
var bigintSafeJsonStringify = (value) => {
|
|
101
|
+
return JSON.stringify(value, (key, value2) => {
|
|
102
|
+
if (typeof value2 === "bigint") {
|
|
103
|
+
return `${value2}n`;
|
|
104
|
+
}
|
|
105
|
+
return value2;
|
|
106
|
+
});
|
|
107
|
+
};
|
|
108
|
+
var isCofheError = (error) => error instanceof CofheError;
|
|
109
|
+
|
|
110
|
+
// core/types.ts
|
|
111
|
+
var FheUintUTypes = [
|
|
112
|
+
2 /* Uint8 */,
|
|
113
|
+
3 /* Uint16 */,
|
|
114
|
+
4 /* Uint32 */,
|
|
115
|
+
5 /* Uint64 */,
|
|
116
|
+
6 /* Uint128 */
|
|
117
|
+
// [U256-DISABLED]
|
|
118
|
+
// FheTypes.Uint256,
|
|
119
|
+
];
|
|
120
|
+
var toHexString = (bytes) => bytes.reduce((str, byte) => str + byte.toString(16).padStart(2, "0"), "");
|
|
121
|
+
var toBigIntOrThrow = (value) => {
|
|
122
|
+
if (typeof value === "bigint") {
|
|
123
|
+
return value;
|
|
124
|
+
}
|
|
125
|
+
try {
|
|
126
|
+
return BigInt(value);
|
|
127
|
+
} catch (error) {
|
|
128
|
+
throw new Error("Invalid input: Unable to convert to bigint");
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
var validateBigIntInRange = (value, max, min = 0n) => {
|
|
132
|
+
if (typeof value !== "bigint") {
|
|
133
|
+
throw new Error("Value must be of type bigint");
|
|
134
|
+
}
|
|
135
|
+
if (value > max || value < min) {
|
|
136
|
+
throw new Error(`Value out of range: ${max} - ${min}, try a different uint type`);
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
var hexToBytes = (hex) => {
|
|
140
|
+
const bytes = new Uint8Array(hex.length / 2);
|
|
141
|
+
for (let i = 0; i < hex.length; i += 2) {
|
|
142
|
+
bytes[i / 2] = parseInt(hex.substr(i, 2), 16);
|
|
143
|
+
}
|
|
144
|
+
return bytes;
|
|
145
|
+
};
|
|
146
|
+
var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
147
|
+
async function getPublicClientChainID(publicClient) {
|
|
148
|
+
let chainId = null;
|
|
149
|
+
try {
|
|
150
|
+
chainId = publicClient.chain?.id ?? await publicClient.getChainId();
|
|
151
|
+
} catch (e) {
|
|
152
|
+
throw new CofheError({
|
|
153
|
+
code: "PUBLIC_WALLET_GET_CHAIN_ID_FAILED" /* PublicWalletGetChainIdFailed */,
|
|
154
|
+
message: "getting chain ID from public client failed",
|
|
155
|
+
cause: e instanceof Error ? e : void 0
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
if (chainId === null) {
|
|
159
|
+
throw new CofheError({
|
|
160
|
+
code: "PUBLIC_WALLET_GET_CHAIN_ID_FAILED" /* PublicWalletGetChainIdFailed */,
|
|
161
|
+
message: "chain ID from public client is null"
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
return chainId;
|
|
165
|
+
}
|
|
166
|
+
async function getWalletClientAccount(walletClient) {
|
|
167
|
+
let address;
|
|
168
|
+
try {
|
|
169
|
+
address = walletClient.account?.address;
|
|
170
|
+
if (!address) {
|
|
171
|
+
address = (await walletClient.getAddresses())?.[0];
|
|
172
|
+
}
|
|
173
|
+
} catch (e) {
|
|
174
|
+
throw new CofheError({
|
|
175
|
+
code: "PUBLIC_WALLET_GET_ADDRESSES_FAILED" /* PublicWalletGetAddressesFailed */,
|
|
176
|
+
message: "getting address from wallet client failed",
|
|
177
|
+
cause: e instanceof Error ? e : void 0
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
if (!address) {
|
|
181
|
+
throw new CofheError({
|
|
182
|
+
code: "PUBLIC_WALLET_GET_ADDRESSES_FAILED" /* PublicWalletGetAddressesFailed */,
|
|
183
|
+
message: "address from wallet client is null"
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
return address;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// core/encrypt/zkPackProveVerify.ts
|
|
190
|
+
var MAX_UINT8 = 255n;
|
|
191
|
+
var MAX_UINT16 = 65535n;
|
|
192
|
+
var MAX_UINT32 = 4294967295n;
|
|
193
|
+
var MAX_UINT64 = 18446744073709551615n;
|
|
194
|
+
var MAX_UINT128 = 340282366920938463463374607431768211455n;
|
|
195
|
+
var MAX_UINT160 = 1461501637330902918203684832716283019655932542975n;
|
|
196
|
+
var MAX_ENCRYPTABLE_BITS = 2048;
|
|
197
|
+
var zkPack = (items, builder) => {
|
|
198
|
+
let totalBits = 0;
|
|
199
|
+
for (const item of items) {
|
|
200
|
+
switch (item.utype) {
|
|
201
|
+
case 0 /* Bool */: {
|
|
202
|
+
builder.push_boolean(item.data);
|
|
203
|
+
totalBits += 1;
|
|
204
|
+
break;
|
|
205
|
+
}
|
|
206
|
+
case 2 /* Uint8 */: {
|
|
207
|
+
const bint = toBigIntOrThrow(item.data);
|
|
208
|
+
validateBigIntInRange(bint, MAX_UINT8);
|
|
209
|
+
builder.push_u8(parseInt(bint.toString()));
|
|
210
|
+
totalBits += 8;
|
|
211
|
+
break;
|
|
212
|
+
}
|
|
213
|
+
case 3 /* Uint16 */: {
|
|
214
|
+
const bint = toBigIntOrThrow(item.data);
|
|
215
|
+
validateBigIntInRange(bint, MAX_UINT16);
|
|
216
|
+
builder.push_u16(parseInt(bint.toString()));
|
|
217
|
+
totalBits += 16;
|
|
218
|
+
break;
|
|
219
|
+
}
|
|
220
|
+
case 4 /* Uint32 */: {
|
|
221
|
+
const bint = toBigIntOrThrow(item.data);
|
|
222
|
+
validateBigIntInRange(bint, MAX_UINT32);
|
|
223
|
+
builder.push_u32(parseInt(bint.toString()));
|
|
224
|
+
totalBits += 32;
|
|
225
|
+
break;
|
|
226
|
+
}
|
|
227
|
+
case 5 /* Uint64 */: {
|
|
228
|
+
const bint = toBigIntOrThrow(item.data);
|
|
229
|
+
validateBigIntInRange(bint, MAX_UINT64);
|
|
230
|
+
builder.push_u64(bint);
|
|
231
|
+
totalBits += 64;
|
|
232
|
+
break;
|
|
233
|
+
}
|
|
234
|
+
case 6 /* Uint128 */: {
|
|
235
|
+
const bint = toBigIntOrThrow(item.data);
|
|
236
|
+
validateBigIntInRange(bint, MAX_UINT128);
|
|
237
|
+
builder.push_u128(bint);
|
|
238
|
+
totalBits += 128;
|
|
239
|
+
break;
|
|
240
|
+
}
|
|
241
|
+
case 7 /* Uint160 */: {
|
|
242
|
+
const bint = toBigIntOrThrow(item.data);
|
|
243
|
+
validateBigIntInRange(bint, MAX_UINT160);
|
|
244
|
+
builder.push_u160(bint);
|
|
245
|
+
totalBits += 160;
|
|
246
|
+
break;
|
|
247
|
+
}
|
|
248
|
+
default: {
|
|
249
|
+
throw new CofheError({
|
|
250
|
+
code: "ZK_PACK_FAILED" /* ZkPackFailed */,
|
|
251
|
+
message: `Invalid utype: ${item.utype}`,
|
|
252
|
+
hint: `Ensure that the utype is valid, using the Encryptable type, for example: Encryptable.uint128(100n)`,
|
|
253
|
+
context: {
|
|
254
|
+
item
|
|
255
|
+
}
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
if (totalBits > MAX_ENCRYPTABLE_BITS) {
|
|
261
|
+
throw new CofheError({
|
|
262
|
+
code: "ZK_PACK_FAILED" /* ZkPackFailed */,
|
|
263
|
+
message: `Total bits ${totalBits} exceeds ${MAX_ENCRYPTABLE_BITS}`,
|
|
264
|
+
hint: `Ensure that the total bits of the items to encrypt does not exceed ${MAX_ENCRYPTABLE_BITS}`,
|
|
265
|
+
context: {
|
|
266
|
+
totalBits,
|
|
267
|
+
maxBits: MAX_ENCRYPTABLE_BITS,
|
|
268
|
+
items
|
|
269
|
+
}
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
return builder;
|
|
273
|
+
};
|
|
274
|
+
var zkProveWithWorker = async (workerFn, fheKeyHex, crsHex, items, metadata) => {
|
|
275
|
+
return await workerFn(fheKeyHex, crsHex, items, metadata);
|
|
276
|
+
};
|
|
277
|
+
var zkProve = async (builder, crs, metadata) => {
|
|
278
|
+
return new Promise((resolve) => {
|
|
279
|
+
setTimeout(() => {
|
|
280
|
+
const compactList = builder.build_with_proof_packed(
|
|
281
|
+
crs,
|
|
282
|
+
metadata,
|
|
283
|
+
1
|
|
284
|
+
// ZkComputeLoad.Verify
|
|
285
|
+
);
|
|
286
|
+
resolve(compactList.serialize());
|
|
287
|
+
}, 0);
|
|
288
|
+
});
|
|
289
|
+
};
|
|
290
|
+
var constructZkPoKMetadata = (accountAddr, securityZone, chainId) => {
|
|
291
|
+
const accountAddrNoPrefix = accountAddr.startsWith("0x") ? accountAddr.slice(2) : accountAddr;
|
|
292
|
+
const accountBytes = hexToBytes(accountAddrNoPrefix);
|
|
293
|
+
const chainIdBytes = new Uint8Array(32);
|
|
294
|
+
let value = chainId;
|
|
295
|
+
for (let i = 31; i >= 0 && value > 0; i--) {
|
|
296
|
+
chainIdBytes[i] = value & 255;
|
|
297
|
+
value = value >>> 8;
|
|
298
|
+
}
|
|
299
|
+
const metadata = new Uint8Array(1 + accountBytes.length + 32);
|
|
300
|
+
metadata[0] = securityZone;
|
|
301
|
+
metadata.set(accountBytes, 1);
|
|
302
|
+
metadata.set(chainIdBytes, 1 + accountBytes.length);
|
|
303
|
+
return metadata;
|
|
304
|
+
};
|
|
305
|
+
var zkVerify = async (verifierUrl, serializedBytes, address, securityZone, chainId) => {
|
|
306
|
+
const packed_list = toHexString(serializedBytes);
|
|
307
|
+
const sz_byte = new Uint8Array([securityZone]);
|
|
308
|
+
const payload = {
|
|
309
|
+
packed_list,
|
|
310
|
+
account_addr: address,
|
|
311
|
+
security_zone: sz_byte[0],
|
|
312
|
+
chain_id: chainId
|
|
313
|
+
};
|
|
314
|
+
const body = JSON.stringify(payload);
|
|
315
|
+
try {
|
|
316
|
+
const response = await fetch(`${verifierUrl}/verify`, {
|
|
317
|
+
method: "POST",
|
|
318
|
+
headers: {
|
|
319
|
+
"Content-Type": "application/json"
|
|
320
|
+
},
|
|
321
|
+
body
|
|
322
|
+
});
|
|
323
|
+
if (!response.ok) {
|
|
324
|
+
const errorBody = await response.text();
|
|
325
|
+
throw new CofheError({
|
|
326
|
+
code: "ZK_VERIFY_FAILED" /* ZkVerifyFailed */,
|
|
327
|
+
message: `HTTP error! ZK proof verification failed - ${errorBody}`
|
|
328
|
+
});
|
|
329
|
+
}
|
|
330
|
+
const json = await response.json();
|
|
331
|
+
if (json.status !== "success") {
|
|
332
|
+
throw new CofheError({
|
|
333
|
+
code: "ZK_VERIFY_FAILED" /* ZkVerifyFailed */,
|
|
334
|
+
message: `ZK proof verification response malformed - ${json.error}`
|
|
335
|
+
});
|
|
336
|
+
}
|
|
337
|
+
return json.data.map(({ ct_hash, signature, recid }) => {
|
|
338
|
+
return {
|
|
339
|
+
ct_hash,
|
|
340
|
+
signature: concatSigRecid(signature, recid)
|
|
341
|
+
};
|
|
342
|
+
});
|
|
343
|
+
} catch (e) {
|
|
344
|
+
throw new CofheError({
|
|
345
|
+
code: "ZK_VERIFY_FAILED" /* ZkVerifyFailed */,
|
|
346
|
+
message: `ZK proof verification failed`,
|
|
347
|
+
cause: e instanceof Error ? e : void 0
|
|
348
|
+
});
|
|
349
|
+
}
|
|
350
|
+
};
|
|
351
|
+
var concatSigRecid = (signature, recid) => {
|
|
352
|
+
return signature + (recid + 27).toString(16).padStart(2, "0");
|
|
353
|
+
};
|
|
354
|
+
|
|
355
|
+
// core/encrypt/MockZkVerifierAbi.ts
|
|
356
|
+
var MockZkVerifierAbi = [
|
|
357
|
+
{
|
|
358
|
+
type: "function",
|
|
359
|
+
name: "exists",
|
|
360
|
+
inputs: [],
|
|
361
|
+
outputs: [{ name: "", type: "bool", internalType: "bool" }],
|
|
362
|
+
stateMutability: "pure"
|
|
363
|
+
},
|
|
364
|
+
{
|
|
365
|
+
type: "function",
|
|
366
|
+
name: "insertCtHash",
|
|
367
|
+
inputs: [
|
|
368
|
+
{ name: "ctHash", type: "uint256", internalType: "uint256" },
|
|
369
|
+
{ name: "value", type: "uint256", internalType: "uint256" }
|
|
370
|
+
],
|
|
371
|
+
outputs: [],
|
|
372
|
+
stateMutability: "nonpayable"
|
|
373
|
+
},
|
|
374
|
+
{
|
|
375
|
+
type: "function",
|
|
376
|
+
name: "insertPackedCtHashes",
|
|
377
|
+
inputs: [
|
|
378
|
+
{ name: "ctHashes", type: "uint256[]", internalType: "uint256[]" },
|
|
379
|
+
{ name: "values", type: "uint256[]", internalType: "uint256[]" }
|
|
380
|
+
],
|
|
381
|
+
outputs: [],
|
|
382
|
+
stateMutability: "nonpayable"
|
|
383
|
+
},
|
|
384
|
+
{
|
|
385
|
+
type: "function",
|
|
386
|
+
name: "zkVerify",
|
|
387
|
+
inputs: [
|
|
388
|
+
{ name: "value", type: "uint256", internalType: "uint256" },
|
|
389
|
+
{ name: "utype", type: "uint8", internalType: "uint8" },
|
|
390
|
+
{ name: "user", type: "address", internalType: "address" },
|
|
391
|
+
{ name: "securityZone", type: "uint8", internalType: "uint8" },
|
|
392
|
+
{ name: "", type: "uint256", internalType: "uint256" }
|
|
393
|
+
],
|
|
394
|
+
outputs: [
|
|
395
|
+
{
|
|
396
|
+
name: "",
|
|
397
|
+
type: "tuple",
|
|
398
|
+
internalType: "struct EncryptedInput",
|
|
399
|
+
components: [
|
|
400
|
+
{ name: "ctHash", type: "uint256", internalType: "uint256" },
|
|
401
|
+
{ name: "securityZone", type: "uint8", internalType: "uint8" },
|
|
402
|
+
{ name: "utype", type: "uint8", internalType: "uint8" },
|
|
403
|
+
{ name: "signature", type: "bytes", internalType: "bytes" }
|
|
404
|
+
]
|
|
405
|
+
}
|
|
406
|
+
],
|
|
407
|
+
stateMutability: "nonpayable"
|
|
408
|
+
},
|
|
409
|
+
{
|
|
410
|
+
type: "function",
|
|
411
|
+
name: "zkVerifyCalcCtHash",
|
|
412
|
+
inputs: [
|
|
413
|
+
{ name: "value", type: "uint256", internalType: "uint256" },
|
|
414
|
+
{ name: "utype", type: "uint8", internalType: "uint8" },
|
|
415
|
+
{ name: "user", type: "address", internalType: "address" },
|
|
416
|
+
{ name: "securityZone", type: "uint8", internalType: "uint8" },
|
|
417
|
+
{ name: "", type: "uint256", internalType: "uint256" }
|
|
418
|
+
],
|
|
419
|
+
outputs: [{ name: "ctHash", type: "uint256", internalType: "uint256" }],
|
|
420
|
+
stateMutability: "view"
|
|
421
|
+
},
|
|
422
|
+
{
|
|
423
|
+
type: "function",
|
|
424
|
+
name: "zkVerifyCalcCtHashesPacked",
|
|
425
|
+
inputs: [
|
|
426
|
+
{ name: "values", type: "uint256[]", internalType: "uint256[]" },
|
|
427
|
+
{ name: "utypes", type: "uint8[]", internalType: "uint8[]" },
|
|
428
|
+
{ name: "user", type: "address", internalType: "address" },
|
|
429
|
+
{ name: "securityZone", type: "uint8", internalType: "uint8" },
|
|
430
|
+
{ name: "chainId", type: "uint256", internalType: "uint256" }
|
|
431
|
+
],
|
|
432
|
+
outputs: [{ name: "ctHashes", type: "uint256[]", internalType: "uint256[]" }],
|
|
433
|
+
stateMutability: "view"
|
|
434
|
+
},
|
|
435
|
+
{
|
|
436
|
+
type: "function",
|
|
437
|
+
name: "zkVerifyPacked",
|
|
438
|
+
inputs: [
|
|
439
|
+
{ name: "values", type: "uint256[]", internalType: "uint256[]" },
|
|
440
|
+
{ name: "utypes", type: "uint8[]", internalType: "uint8[]" },
|
|
441
|
+
{ name: "user", type: "address", internalType: "address" },
|
|
442
|
+
{ name: "securityZone", type: "uint8", internalType: "uint8" },
|
|
443
|
+
{ name: "chainId", type: "uint256", internalType: "uint256" }
|
|
444
|
+
],
|
|
445
|
+
outputs: [
|
|
446
|
+
{
|
|
447
|
+
name: "inputs",
|
|
448
|
+
type: "tuple[]",
|
|
449
|
+
internalType: "struct EncryptedInput[]",
|
|
450
|
+
components: [
|
|
451
|
+
{ name: "ctHash", type: "uint256", internalType: "uint256" },
|
|
452
|
+
{ name: "securityZone", type: "uint8", internalType: "uint8" },
|
|
453
|
+
{ name: "utype", type: "uint8", internalType: "uint8" },
|
|
454
|
+
{ name: "signature", type: "bytes", internalType: "bytes" }
|
|
455
|
+
]
|
|
456
|
+
}
|
|
457
|
+
],
|
|
458
|
+
stateMutability: "nonpayable"
|
|
459
|
+
},
|
|
460
|
+
{ type: "error", name: "InvalidInputs", inputs: [] }
|
|
461
|
+
];
|
|
462
|
+
|
|
463
|
+
// core/consts.ts
|
|
464
|
+
var TASK_MANAGER_ADDRESS = "0xeA30c4B8b44078Bbf8a6ef5b9f1eC1626C7848D9";
|
|
465
|
+
var MOCKS_ZK_VERIFIER_ADDRESS = "0x0000000000000000000000000000000000005001";
|
|
466
|
+
var MOCKS_THRESHOLD_NETWORK_ADDRESS = "0x0000000000000000000000000000000000005002";
|
|
467
|
+
var MOCKS_ZK_VERIFIER_SIGNER_PRIVATE_KEY = "0x6C8D7F768A6BB4AAFE85E8A2F5A9680355239C7E14646ED62B044E39DE154512";
|
|
468
|
+
var MOCKS_DECRYPT_RESULT_SIGNER_PRIVATE_KEY = "0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d";
|
|
469
|
+
|
|
470
|
+
// core/encrypt/cofheMocksZkVerifySign.ts
|
|
471
|
+
function createMockZkVerifierSigner() {
|
|
472
|
+
return viem.createWalletClient({
|
|
473
|
+
chain: chains.hardhat,
|
|
474
|
+
transport: viem.http(),
|
|
475
|
+
account: accounts.privateKeyToAccount(MOCKS_ZK_VERIFIER_SIGNER_PRIVATE_KEY)
|
|
476
|
+
});
|
|
477
|
+
}
|
|
478
|
+
async function cofheMocksCheckEncryptableBits(items) {
|
|
479
|
+
let totalBits = 0;
|
|
480
|
+
for (const item of items) {
|
|
481
|
+
switch (item.utype) {
|
|
482
|
+
case 0 /* Bool */: {
|
|
483
|
+
totalBits += 1;
|
|
484
|
+
break;
|
|
485
|
+
}
|
|
486
|
+
case 2 /* Uint8 */: {
|
|
487
|
+
totalBits += 8;
|
|
488
|
+
break;
|
|
489
|
+
}
|
|
490
|
+
case 3 /* Uint16 */: {
|
|
491
|
+
totalBits += 16;
|
|
492
|
+
break;
|
|
493
|
+
}
|
|
494
|
+
case 4 /* Uint32 */: {
|
|
495
|
+
totalBits += 32;
|
|
496
|
+
break;
|
|
497
|
+
}
|
|
498
|
+
case 5 /* Uint64 */: {
|
|
499
|
+
totalBits += 64;
|
|
500
|
+
break;
|
|
501
|
+
}
|
|
502
|
+
case 6 /* Uint128 */: {
|
|
503
|
+
totalBits += 128;
|
|
504
|
+
break;
|
|
505
|
+
}
|
|
506
|
+
case 7 /* Uint160 */: {
|
|
507
|
+
totalBits += 160;
|
|
508
|
+
break;
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
if (totalBits > MAX_ENCRYPTABLE_BITS) {
|
|
513
|
+
throw new CofheError({
|
|
514
|
+
code: "ZK_PACK_FAILED" /* ZkPackFailed */,
|
|
515
|
+
message: `Total bits ${totalBits} exceeds ${MAX_ENCRYPTABLE_BITS}`,
|
|
516
|
+
hint: `Ensure that the total bits of the items to encrypt does not exceed ${MAX_ENCRYPTABLE_BITS}`,
|
|
517
|
+
context: {
|
|
518
|
+
totalBits,
|
|
519
|
+
maxBits: MAX_ENCRYPTABLE_BITS,
|
|
520
|
+
items
|
|
521
|
+
}
|
|
522
|
+
});
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
async function calcCtHashes(items, account, securityZone, publicClient) {
|
|
526
|
+
const calcCtHashesArgs = [
|
|
527
|
+
items.map(({ data }) => BigInt(data)),
|
|
528
|
+
items.map(({ utype }) => utype),
|
|
529
|
+
account,
|
|
530
|
+
securityZone,
|
|
531
|
+
BigInt(chains.hardhat.id)
|
|
532
|
+
];
|
|
533
|
+
let ctHashes;
|
|
534
|
+
try {
|
|
535
|
+
ctHashes = await publicClient.readContract({
|
|
536
|
+
address: MOCKS_ZK_VERIFIER_ADDRESS,
|
|
537
|
+
abi: MockZkVerifierAbi,
|
|
538
|
+
functionName: "zkVerifyCalcCtHashesPacked",
|
|
539
|
+
args: calcCtHashesArgs
|
|
540
|
+
});
|
|
541
|
+
} catch (err) {
|
|
542
|
+
throw new CofheError({
|
|
543
|
+
code: "ZK_MOCKS_CALC_CT_HASHES_FAILED" /* ZkMocksCalcCtHashesFailed */,
|
|
544
|
+
message: `mockZkVerifySign calcCtHashes failed while calling zkVerifyCalcCtHashesPacked`,
|
|
545
|
+
cause: err instanceof Error ? err : void 0,
|
|
546
|
+
context: {
|
|
547
|
+
address: MOCKS_ZK_VERIFIER_ADDRESS,
|
|
548
|
+
items,
|
|
549
|
+
account,
|
|
550
|
+
securityZone,
|
|
551
|
+
publicClient,
|
|
552
|
+
calcCtHashesArgs
|
|
553
|
+
}
|
|
554
|
+
});
|
|
555
|
+
}
|
|
556
|
+
if (ctHashes.length !== items.length) {
|
|
557
|
+
throw new CofheError({
|
|
558
|
+
code: "ZK_MOCKS_CALC_CT_HASHES_FAILED" /* ZkMocksCalcCtHashesFailed */,
|
|
559
|
+
message: `mockZkVerifySign calcCtHashes returned incorrect number of ctHashes`,
|
|
560
|
+
context: {
|
|
561
|
+
items,
|
|
562
|
+
account,
|
|
563
|
+
securityZone,
|
|
564
|
+
publicClient,
|
|
565
|
+
calcCtHashesArgs,
|
|
566
|
+
ctHashes
|
|
567
|
+
}
|
|
568
|
+
});
|
|
569
|
+
}
|
|
570
|
+
return items.map((item, index) => ({
|
|
571
|
+
...item,
|
|
572
|
+
ctHash: ctHashes[index]
|
|
573
|
+
}));
|
|
574
|
+
}
|
|
575
|
+
async function insertCtHashes(items, walletClient) {
|
|
576
|
+
const insertPackedCtHashesArgs = [items.map(({ ctHash }) => ctHash), items.map(({ data }) => BigInt(data))];
|
|
577
|
+
try {
|
|
578
|
+
const account = walletClient.account;
|
|
579
|
+
await walletClient.writeContract({
|
|
580
|
+
address: MOCKS_ZK_VERIFIER_ADDRESS,
|
|
581
|
+
abi: MockZkVerifierAbi,
|
|
582
|
+
functionName: "insertPackedCtHashes",
|
|
583
|
+
args: insertPackedCtHashesArgs,
|
|
584
|
+
chain: chains.hardhat,
|
|
585
|
+
account
|
|
586
|
+
});
|
|
587
|
+
} catch (err) {
|
|
588
|
+
throw new CofheError({
|
|
589
|
+
code: "ZK_MOCKS_INSERT_CT_HASHES_FAILED" /* ZkMocksInsertCtHashesFailed */,
|
|
590
|
+
message: `mockZkVerifySign insertPackedCtHashes failed while calling insertPackedCtHashes`,
|
|
591
|
+
cause: err instanceof Error ? err : void 0,
|
|
592
|
+
context: {
|
|
593
|
+
items,
|
|
594
|
+
walletClient,
|
|
595
|
+
insertPackedCtHashesArgs
|
|
596
|
+
}
|
|
597
|
+
});
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
async function createProofSignatures(items, securityZone, account) {
|
|
601
|
+
let signatures = [];
|
|
602
|
+
let encInputSignerClient;
|
|
603
|
+
try {
|
|
604
|
+
encInputSignerClient = createMockZkVerifierSigner();
|
|
605
|
+
} catch (err) {
|
|
606
|
+
throw new CofheError({
|
|
607
|
+
code: "ZK_MOCKS_CREATE_PROOF_SIGNATURE_FAILED" /* ZkMocksCreateProofSignatureFailed */,
|
|
608
|
+
message: `mockZkVerifySign createProofSignatures failed while creating wallet client`,
|
|
609
|
+
cause: err instanceof Error ? err : void 0,
|
|
610
|
+
context: {
|
|
611
|
+
MOCKS_ZK_VERIFIER_SIGNER_PRIVATE_KEY
|
|
612
|
+
}
|
|
613
|
+
});
|
|
614
|
+
}
|
|
615
|
+
try {
|
|
616
|
+
for (const item of items) {
|
|
617
|
+
const packedData = viem.encodePacked(
|
|
618
|
+
["uint256", "uint8", "uint8", "address", "uint256"],
|
|
619
|
+
[BigInt(item.ctHash), item.utype, securityZone, account, BigInt(chains.hardhat.id)]
|
|
620
|
+
);
|
|
621
|
+
const messageHash = viem.keccak256(packedData);
|
|
622
|
+
const signature = await accounts.sign({
|
|
623
|
+
hash: messageHash,
|
|
624
|
+
privateKey: MOCKS_ZK_VERIFIER_SIGNER_PRIVATE_KEY,
|
|
625
|
+
to: "hex"
|
|
626
|
+
});
|
|
627
|
+
signatures.push(signature);
|
|
628
|
+
}
|
|
629
|
+
} catch (err) {
|
|
630
|
+
throw new CofheError({
|
|
631
|
+
code: "ZK_MOCKS_CREATE_PROOF_SIGNATURE_FAILED" /* ZkMocksCreateProofSignatureFailed */,
|
|
632
|
+
message: `mockZkVerifySign createProofSignatures failed while calling signMessage`,
|
|
633
|
+
cause: err instanceof Error ? err : void 0,
|
|
634
|
+
context: {
|
|
635
|
+
items,
|
|
636
|
+
securityZone
|
|
637
|
+
}
|
|
638
|
+
});
|
|
639
|
+
}
|
|
640
|
+
if (signatures.length !== items.length) {
|
|
641
|
+
throw new CofheError({
|
|
642
|
+
code: "ZK_MOCKS_CREATE_PROOF_SIGNATURE_FAILED" /* ZkMocksCreateProofSignatureFailed */,
|
|
643
|
+
message: `mockZkVerifySign createProofSignatures returned incorrect number of signatures`,
|
|
644
|
+
context: {
|
|
645
|
+
items,
|
|
646
|
+
securityZone
|
|
647
|
+
}
|
|
648
|
+
});
|
|
649
|
+
}
|
|
650
|
+
return signatures;
|
|
651
|
+
}
|
|
652
|
+
async function cofheMocksZkVerifySign(items, account, securityZone, publicClient, walletClient, zkvWalletClient) {
|
|
653
|
+
const _walletClient = zkvWalletClient ?? createMockZkVerifierSigner();
|
|
654
|
+
const encryptableItems = await calcCtHashes(items, account, securityZone, publicClient);
|
|
655
|
+
await insertCtHashes(encryptableItems, _walletClient);
|
|
656
|
+
const signatures = await createProofSignatures(encryptableItems, securityZone, account);
|
|
657
|
+
return encryptableItems.map((item, index) => ({
|
|
658
|
+
ct_hash: item.ctHash.toString(),
|
|
659
|
+
signature: signatures[index]
|
|
660
|
+
}));
|
|
661
|
+
}
|
|
662
|
+
var EnvironmentSchema = zod.z.enum(["MOCK", "TESTNET", "MAINNET"]);
|
|
663
|
+
var CofheChainSchema = zod.z.object({
|
|
664
|
+
/** Chain ID */
|
|
665
|
+
id: zod.z.number().int().positive(),
|
|
666
|
+
/** Human-readable chain name */
|
|
667
|
+
name: zod.z.string().min(1),
|
|
668
|
+
/** Network identifier */
|
|
669
|
+
network: zod.z.string().min(1),
|
|
670
|
+
/** coFhe service URL */
|
|
671
|
+
coFheUrl: zod.z.url(),
|
|
672
|
+
/** Verifier service URL */
|
|
673
|
+
verifierUrl: zod.z.url(),
|
|
674
|
+
/** Threshold network service URL */
|
|
675
|
+
thresholdNetworkUrl: zod.z.url(),
|
|
676
|
+
/** Environment type */
|
|
677
|
+
environment: EnvironmentSchema
|
|
678
|
+
});
|
|
679
|
+
function defineChain(chainConfig) {
|
|
680
|
+
const result = CofheChainSchema.safeParse(chainConfig);
|
|
681
|
+
if (!result.success) {
|
|
682
|
+
throw new Error(`Invalid chain configuration: ${zod.z.prettifyError(result.error)}`, { cause: result.error });
|
|
683
|
+
}
|
|
684
|
+
return result.data;
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
// chains/chains/hardhat.ts
|
|
688
|
+
var hardhat2 = defineChain({
|
|
689
|
+
id: 31337,
|
|
690
|
+
name: "Hardhat",
|
|
691
|
+
network: "localhost",
|
|
692
|
+
// These are unused in the mock environment
|
|
693
|
+
coFheUrl: "http://127.0.0.1:8448",
|
|
694
|
+
verifierUrl: "http://127.0.0.1:3001",
|
|
695
|
+
thresholdNetworkUrl: "http://127.0.0.1:3000",
|
|
696
|
+
environment: "MOCK"
|
|
697
|
+
});
|
|
698
|
+
var CofheConfigSchema = zod.z.object({
|
|
699
|
+
/** Environment that the SDK is running in */
|
|
700
|
+
environment: zod.z.enum(["node", "hardhat", "web", "react"]).optional().default("node"),
|
|
701
|
+
/** List of supported chain configurations */
|
|
702
|
+
supportedChains: zod.z.array(zod.z.custom()),
|
|
703
|
+
/** Default permit expiration in seconds, default is 30 days */
|
|
704
|
+
defaultPermitExpiration: zod.z.number().optional().default(60 * 60 * 24 * 30),
|
|
705
|
+
/** Storage method for fhe keys (defaults to indexedDB on web, filesystem on node) */
|
|
706
|
+
fheKeyStorage: zod.z.object({
|
|
707
|
+
getItem: zod.z.custom((val) => typeof val === "function", {
|
|
708
|
+
message: "getItem must be a function"
|
|
709
|
+
}),
|
|
710
|
+
setItem: zod.z.custom((val) => typeof val === "function", {
|
|
711
|
+
message: "setItem must be a function"
|
|
712
|
+
}),
|
|
713
|
+
removeItem: zod.z.custom((val) => typeof val === "function", {
|
|
714
|
+
message: "removeItem must be a function"
|
|
715
|
+
})
|
|
716
|
+
}).or(zod.z.null()).default(null),
|
|
717
|
+
/** Whether to use Web Workers for ZK proof generation (web platform only) */
|
|
718
|
+
useWorkers: zod.z.boolean().optional().default(true),
|
|
719
|
+
/** Mocks configs */
|
|
720
|
+
mocks: zod.z.object({
|
|
721
|
+
decryptDelay: zod.z.number().optional().default(0),
|
|
722
|
+
encryptDelay: zod.z.union([zod.z.number(), zod.z.tuple([zod.z.number(), zod.z.number(), zod.z.number(), zod.z.number(), zod.z.number()])]).optional().default([100, 100, 100, 500, 500])
|
|
723
|
+
}).optional().default({ decryptDelay: 0, encryptDelay: [100, 100, 100, 500, 500] }),
|
|
724
|
+
/** Internal configuration */
|
|
725
|
+
_internal: zod.z.object({
|
|
726
|
+
zkvWalletClient: zod.z.any().optional()
|
|
727
|
+
}).optional()
|
|
728
|
+
});
|
|
729
|
+
function createCofheConfigBase(config) {
|
|
730
|
+
const result = CofheConfigSchema.safeParse(config);
|
|
731
|
+
if (!result.success) {
|
|
732
|
+
throw new Error(`Invalid cofhe configuration: ${zod.z.prettifyError(result.error)}`, { cause: result.error });
|
|
733
|
+
}
|
|
734
|
+
return result.data;
|
|
735
|
+
}
|
|
736
|
+
function getSupportedChainOrThrow(config, chainId) {
|
|
737
|
+
const supportedChain = config.supportedChains.find((chain) => chain.id === chainId);
|
|
738
|
+
if (!supportedChain) {
|
|
739
|
+
throw new CofheError({
|
|
740
|
+
code: "UNSUPPORTED_CHAIN" /* UnsupportedChain */,
|
|
741
|
+
message: `Config does not support chain <${chainId}>`,
|
|
742
|
+
hint: "Ensure config passed to client has been created with this chain in the config.supportedChains array.",
|
|
743
|
+
context: {
|
|
744
|
+
chainId,
|
|
745
|
+
supportedChainIds: config.supportedChains.map((c) => c.id)
|
|
746
|
+
}
|
|
747
|
+
});
|
|
748
|
+
}
|
|
749
|
+
return supportedChain;
|
|
750
|
+
}
|
|
751
|
+
function getCoFheUrlOrThrow(config, chainId) {
|
|
752
|
+
const supportedChain = getSupportedChainOrThrow(config, chainId);
|
|
753
|
+
const url = supportedChain.coFheUrl;
|
|
754
|
+
if (!url) {
|
|
755
|
+
throw new CofheError({
|
|
756
|
+
code: "MISSING_CONFIG" /* MissingConfig */,
|
|
757
|
+
message: `CoFHE URL is not configured for chain <${chainId}>`,
|
|
758
|
+
hint: "Ensure this chain config includes a coFheUrl property.",
|
|
759
|
+
context: { chainId }
|
|
760
|
+
});
|
|
761
|
+
}
|
|
762
|
+
return url;
|
|
763
|
+
}
|
|
764
|
+
function getZkVerifierUrlOrThrow(config, chainId) {
|
|
765
|
+
const supportedChain = getSupportedChainOrThrow(config, chainId);
|
|
766
|
+
const url = supportedChain.verifierUrl;
|
|
767
|
+
if (!url) {
|
|
768
|
+
throw new CofheError({
|
|
769
|
+
code: "ZK_VERIFIER_URL_UNINITIALIZED" /* ZkVerifierUrlUninitialized */,
|
|
770
|
+
message: `ZK verifier URL is not configured for chain <${chainId}>`,
|
|
771
|
+
hint: "Ensure this chain config includes a verifierUrl property.",
|
|
772
|
+
context: { chainId }
|
|
773
|
+
});
|
|
774
|
+
}
|
|
775
|
+
return url;
|
|
776
|
+
}
|
|
777
|
+
function getThresholdNetworkUrlOrThrow(config, chainId) {
|
|
778
|
+
const supportedChain = getSupportedChainOrThrow(config, chainId);
|
|
779
|
+
const url = supportedChain.thresholdNetworkUrl;
|
|
780
|
+
if (!url) {
|
|
781
|
+
throw new CofheError({
|
|
782
|
+
code: "THRESHOLD_NETWORK_URL_UNINITIALIZED" /* ThresholdNetworkUrlUninitialized */,
|
|
783
|
+
message: `Threshold network URL is not configured for chain <${chainId}>`,
|
|
784
|
+
hint: "Ensure this chain config includes a thresholdNetworkUrl property.",
|
|
785
|
+
context: { chainId }
|
|
786
|
+
});
|
|
787
|
+
}
|
|
788
|
+
return url;
|
|
789
|
+
}
|
|
790
|
+
function isValidPersistedState(state) {
|
|
791
|
+
if (state && typeof state === "object") {
|
|
792
|
+
if ("fhe" in state && "crs" in state) {
|
|
793
|
+
return true;
|
|
794
|
+
} else {
|
|
795
|
+
throw new Error(
|
|
796
|
+
"Invalid persisted state structure for KeysStore. Is object but doesn't contain required fields 'fhe' and 'crs'."
|
|
797
|
+
);
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
return false;
|
|
801
|
+
}
|
|
802
|
+
var DEFAULT_KEYS_STORE = {
|
|
803
|
+
fhe: {},
|
|
804
|
+
crs: {}
|
|
805
|
+
};
|
|
806
|
+
function isStoreWithPersist(store) {
|
|
807
|
+
return "persist" in store;
|
|
808
|
+
}
|
|
809
|
+
function createKeysStore(storage) {
|
|
810
|
+
const keysStore = storage ? createStoreWithPersit(storage) : vanilla.createStore()(() => ({
|
|
811
|
+
fhe: {},
|
|
812
|
+
crs: {}
|
|
813
|
+
}));
|
|
814
|
+
const getFheKey = (chainId, securityZone = 0) => {
|
|
815
|
+
if (chainId == null || securityZone == null)
|
|
816
|
+
return void 0;
|
|
817
|
+
const stored = keysStore.getState().fhe[chainId]?.[securityZone];
|
|
818
|
+
return stored;
|
|
819
|
+
};
|
|
820
|
+
const getCrs = (chainId) => {
|
|
821
|
+
if (chainId == null)
|
|
822
|
+
return void 0;
|
|
823
|
+
const stored = keysStore.getState().crs[chainId];
|
|
824
|
+
return stored;
|
|
825
|
+
};
|
|
826
|
+
const setFheKey = (chainId, securityZone, key) => {
|
|
827
|
+
keysStore.setState(
|
|
828
|
+
immer.produce((state) => {
|
|
829
|
+
if (state.fhe[chainId] == null)
|
|
830
|
+
state.fhe[chainId] = {};
|
|
831
|
+
state.fhe[chainId][securityZone] = key;
|
|
832
|
+
})
|
|
833
|
+
);
|
|
834
|
+
};
|
|
835
|
+
const setCrs = (chainId, crs) => {
|
|
836
|
+
keysStore.setState(
|
|
837
|
+
immer.produce((state) => {
|
|
838
|
+
state.crs[chainId] = crs;
|
|
839
|
+
})
|
|
840
|
+
);
|
|
841
|
+
};
|
|
842
|
+
const clearKeysStorage = async () => {
|
|
843
|
+
if (storage) {
|
|
844
|
+
await storage.removeItem("cofhesdk-keys");
|
|
845
|
+
}
|
|
846
|
+
};
|
|
847
|
+
const rehydrateKeysStore = async () => {
|
|
848
|
+
if (!isStoreWithPersist(keysStore))
|
|
849
|
+
return;
|
|
850
|
+
if (keysStore.persist.hasHydrated())
|
|
851
|
+
return;
|
|
852
|
+
await keysStore.persist.rehydrate();
|
|
853
|
+
};
|
|
854
|
+
return {
|
|
855
|
+
store: keysStore,
|
|
856
|
+
getFheKey,
|
|
857
|
+
getCrs,
|
|
858
|
+
setFheKey,
|
|
859
|
+
setCrs,
|
|
860
|
+
clearKeysStorage,
|
|
861
|
+
rehydrateKeysStore
|
|
862
|
+
};
|
|
863
|
+
}
|
|
864
|
+
function createStoreWithPersit(storage) {
|
|
865
|
+
const result = vanilla.createStore()(
|
|
866
|
+
middleware.persist(() => DEFAULT_KEYS_STORE, {
|
|
867
|
+
// because earleir tests were written with on-init hydration skipped (due to the error suppression in zustand), returning this flag to fix test (i.e. KeyStore > Storage Utilities > should rehydrate keys store)
|
|
868
|
+
skipHydration: true,
|
|
869
|
+
// if onRehydrateStorage is not passed here, the errors thrown by storage layer are swallowed by zustand here: https://github.com/pmndrs/zustand/blob/39a391b6c1ff9aa89b81694d9bdb21da37dd4ac6/src/middleware/persist.ts#L321
|
|
870
|
+
onRehydrateStorage: () => (_state, _error) => {
|
|
871
|
+
if (_error)
|
|
872
|
+
throw new Error(`onRehydrateStorage: Error rehydrating keys store: ${_error}`);
|
|
873
|
+
},
|
|
874
|
+
name: "cofhesdk-keys",
|
|
875
|
+
storage: middleware.createJSONStorage(() => storage),
|
|
876
|
+
merge: (persistedState, currentState) => {
|
|
877
|
+
const persisted = isValidPersistedState(persistedState) ? persistedState : DEFAULT_KEYS_STORE;
|
|
878
|
+
const current = currentState;
|
|
879
|
+
const mergedFhe = { ...persisted.fhe };
|
|
880
|
+
const allChainIds = /* @__PURE__ */ new Set([...Object.keys(current.fhe), ...Object.keys(persisted.fhe)]);
|
|
881
|
+
for (const chainId of allChainIds) {
|
|
882
|
+
const persistedZones = persisted.fhe[chainId] || {};
|
|
883
|
+
const currentZones = current.fhe[chainId] || {};
|
|
884
|
+
mergedFhe[chainId] = { ...persistedZones, ...currentZones };
|
|
885
|
+
}
|
|
886
|
+
const mergedCrs = { ...persisted.crs, ...current.crs };
|
|
887
|
+
return {
|
|
888
|
+
fhe: mergedFhe,
|
|
889
|
+
crs: mergedCrs
|
|
890
|
+
};
|
|
891
|
+
}
|
|
892
|
+
})
|
|
893
|
+
);
|
|
894
|
+
return result;
|
|
895
|
+
}
|
|
896
|
+
|
|
897
|
+
// core/fetchKeys.ts
|
|
898
|
+
var PUBLIC_KEY_LENGTH_MIN = 15e3;
|
|
899
|
+
var checkKeyValidity = (key, serializer) => {
|
|
900
|
+
if (key == null || key.length === 0)
|
|
901
|
+
return [false, `Key is null or empty <${key}>`];
|
|
902
|
+
try {
|
|
903
|
+
serializer(key);
|
|
904
|
+
return [true, `Key is valid`];
|
|
905
|
+
} catch (err) {
|
|
906
|
+
return [false, `Serialization failed <${err}> key length <${key.length}>`];
|
|
907
|
+
}
|
|
908
|
+
};
|
|
909
|
+
var fetchFhePublicKey = async (coFheUrl, chainId, securityZone, tfhePublicKeyDeserializer2, keysStorage) => {
|
|
910
|
+
const storedKey = keysStorage?.getFheKey(chainId, securityZone);
|
|
911
|
+
const [storedKeyValid] = checkKeyValidity(storedKey, tfhePublicKeyDeserializer2);
|
|
912
|
+
if (storedKeyValid)
|
|
913
|
+
return [storedKey, false];
|
|
914
|
+
let pk_data = void 0;
|
|
915
|
+
try {
|
|
916
|
+
const pk_res = await fetch(`${coFheUrl}/GetNetworkPublicKey`, {
|
|
917
|
+
method: "POST",
|
|
918
|
+
headers: {
|
|
919
|
+
"Content-Type": "application/json"
|
|
920
|
+
},
|
|
921
|
+
body: JSON.stringify({ securityZone })
|
|
922
|
+
});
|
|
923
|
+
const json = await pk_res.json();
|
|
924
|
+
pk_data = json.publicKey;
|
|
925
|
+
} catch (err) {
|
|
926
|
+
throw new Error(`Error fetching FHE publicKey; fetching from CoFHE failed with error ${err}`);
|
|
927
|
+
}
|
|
928
|
+
if (pk_data == null || typeof pk_data !== "string") {
|
|
929
|
+
throw new Error(`Error fetching FHE publicKey; fetched result invalid: missing or not a string`);
|
|
930
|
+
}
|
|
931
|
+
if (pk_data === "0x") {
|
|
932
|
+
throw new Error("Error fetching FHE publicKey; provided chain is not FHE enabled / not found");
|
|
933
|
+
}
|
|
934
|
+
if (pk_data.length < PUBLIC_KEY_LENGTH_MIN) {
|
|
935
|
+
throw new Error(
|
|
936
|
+
`Error fetching FHE publicKey; got shorter than expected key length: ${pk_data.length}. Expected length >= ${PUBLIC_KEY_LENGTH_MIN}`
|
|
937
|
+
);
|
|
938
|
+
}
|
|
939
|
+
try {
|
|
940
|
+
tfhePublicKeyDeserializer2(pk_data);
|
|
941
|
+
} catch (err) {
|
|
942
|
+
throw new Error(`Error serializing FHE publicKey; ${err}`);
|
|
943
|
+
}
|
|
944
|
+
keysStorage?.setFheKey(chainId, securityZone, pk_data);
|
|
945
|
+
return [pk_data, true];
|
|
946
|
+
};
|
|
947
|
+
var fetchCrs = async (coFheUrl, chainId, securityZone, compactPkeCrsDeserializer2, keysStorage) => {
|
|
948
|
+
const storedKey = keysStorage?.getCrs(chainId);
|
|
949
|
+
const [storedKeyValid] = checkKeyValidity(storedKey, compactPkeCrsDeserializer2);
|
|
950
|
+
if (storedKeyValid)
|
|
951
|
+
return [storedKey, false];
|
|
952
|
+
let crs_data = void 0;
|
|
953
|
+
try {
|
|
954
|
+
const crs_res = await fetch(`${coFheUrl}/GetCrs`, {
|
|
955
|
+
method: "POST",
|
|
956
|
+
headers: {
|
|
957
|
+
"Content-Type": "application/json"
|
|
958
|
+
},
|
|
959
|
+
body: JSON.stringify({ securityZone })
|
|
960
|
+
});
|
|
961
|
+
const json = await crs_res.json();
|
|
962
|
+
crs_data = json.crs;
|
|
963
|
+
} catch (err) {
|
|
964
|
+
throw new Error(`Error fetching CRS; fetching failed with error ${err}`);
|
|
965
|
+
}
|
|
966
|
+
if (crs_data == null || typeof crs_data !== "string") {
|
|
967
|
+
throw new Error(`Error fetching CRS; invalid: missing or not a string`);
|
|
968
|
+
}
|
|
969
|
+
try {
|
|
970
|
+
compactPkeCrsDeserializer2(crs_data);
|
|
971
|
+
} catch (err) {
|
|
972
|
+
console.error(`Error serializing CRS ${err}`);
|
|
973
|
+
throw new Error(`Error serializing CRS; ${err}`);
|
|
974
|
+
}
|
|
975
|
+
keysStorage?.setCrs(chainId, crs_data);
|
|
976
|
+
return [crs_data, true];
|
|
977
|
+
};
|
|
978
|
+
var fetchKeys = async (config, chainId, securityZone = 0, tfhePublicKeyDeserializer2, compactPkeCrsDeserializer2, keysStorage) => {
|
|
979
|
+
const coFheUrl = getCoFheUrlOrThrow(config, chainId);
|
|
980
|
+
return await Promise.all([
|
|
981
|
+
fetchFhePublicKey(coFheUrl, chainId, securityZone, tfhePublicKeyDeserializer2, keysStorage),
|
|
982
|
+
fetchCrs(coFheUrl, chainId, securityZone, compactPkeCrsDeserializer2, keysStorage)
|
|
983
|
+
]);
|
|
984
|
+
};
|
|
985
|
+
var BaseBuilder = class {
|
|
986
|
+
config;
|
|
987
|
+
publicClient;
|
|
988
|
+
walletClient;
|
|
989
|
+
chainId;
|
|
990
|
+
account;
|
|
991
|
+
constructor(params) {
|
|
992
|
+
if (!params.config) {
|
|
993
|
+
throw new CofheError({
|
|
994
|
+
code: "MISSING_CONFIG" /* MissingConfig */,
|
|
995
|
+
message: "Builder config is undefined",
|
|
996
|
+
hint: "Ensure client has been created with a config.",
|
|
997
|
+
context: {
|
|
998
|
+
config: params.config
|
|
999
|
+
}
|
|
1000
|
+
});
|
|
1001
|
+
}
|
|
1002
|
+
this.config = params.config;
|
|
1003
|
+
this.publicClient = params.publicClient;
|
|
1004
|
+
this.walletClient = params.walletClient;
|
|
1005
|
+
this.chainId = params.chainId;
|
|
1006
|
+
this.account = params.account;
|
|
1007
|
+
params.requireConnected?.();
|
|
1008
|
+
}
|
|
1009
|
+
/**
|
|
1010
|
+
* Asserts that this.chainId is populated
|
|
1011
|
+
* @throws {CofheError} If chainId is not set
|
|
1012
|
+
*/
|
|
1013
|
+
assertChainId() {
|
|
1014
|
+
if (this.chainId)
|
|
1015
|
+
return;
|
|
1016
|
+
throw new CofheError({
|
|
1017
|
+
code: "CHAIN_ID_UNINITIALIZED" /* ChainIdUninitialized */,
|
|
1018
|
+
message: "Chain ID is not set",
|
|
1019
|
+
hint: "Ensure client.connect() has been called and awaited, or use setChainId(...) to set the chainId explicitly.",
|
|
1020
|
+
context: {
|
|
1021
|
+
chainId: this.chainId
|
|
1022
|
+
}
|
|
1023
|
+
});
|
|
1024
|
+
}
|
|
1025
|
+
/**
|
|
1026
|
+
* Asserts that this.account is populated
|
|
1027
|
+
* @throws {CofheError} If account is not set
|
|
1028
|
+
*/
|
|
1029
|
+
assertAccount() {
|
|
1030
|
+
if (this.account)
|
|
1031
|
+
return;
|
|
1032
|
+
throw new CofheError({
|
|
1033
|
+
code: "ACCOUNT_UNINITIALIZED" /* AccountUninitialized */,
|
|
1034
|
+
message: "Account is not set",
|
|
1035
|
+
hint: "Ensure client.connect() has been called and awaited, or use setAccount(...) to set the account explicitly.",
|
|
1036
|
+
context: {
|
|
1037
|
+
account: this.account
|
|
1038
|
+
}
|
|
1039
|
+
});
|
|
1040
|
+
}
|
|
1041
|
+
/**
|
|
1042
|
+
* Asserts that this.publicClient is populated
|
|
1043
|
+
* @throws {CofheError} If publicClient is not set
|
|
1044
|
+
*/
|
|
1045
|
+
assertPublicClient() {
|
|
1046
|
+
if (this.publicClient)
|
|
1047
|
+
return;
|
|
1048
|
+
throw new CofheError({
|
|
1049
|
+
code: "MISSING_PUBLIC_CLIENT" /* MissingPublicClient */,
|
|
1050
|
+
message: "Public client not found",
|
|
1051
|
+
hint: "Ensure client.connect() has been called with a publicClient.",
|
|
1052
|
+
context: {
|
|
1053
|
+
publicClient: this.publicClient
|
|
1054
|
+
}
|
|
1055
|
+
});
|
|
1056
|
+
}
|
|
1057
|
+
/**
|
|
1058
|
+
* Asserts that this.walletClient is populated
|
|
1059
|
+
* @throws {CofheError} If walletClient is not set
|
|
1060
|
+
*/
|
|
1061
|
+
assertWalletClient() {
|
|
1062
|
+
if (this.walletClient)
|
|
1063
|
+
return;
|
|
1064
|
+
throw new CofheError({
|
|
1065
|
+
code: "MISSING_WALLET_CLIENT" /* MissingWalletClient */,
|
|
1066
|
+
message: "Wallet client not found",
|
|
1067
|
+
hint: "Ensure client.connect() has been called with a walletClient.",
|
|
1068
|
+
context: {
|
|
1069
|
+
walletClient: this.walletClient
|
|
1070
|
+
}
|
|
1071
|
+
});
|
|
1072
|
+
}
|
|
1073
|
+
};
|
|
1074
|
+
|
|
1075
|
+
// core/encrypt/encryptInputsBuilder.ts
|
|
1076
|
+
var EncryptInputsBuilder = class extends BaseBuilder {
|
|
1077
|
+
securityZone;
|
|
1078
|
+
stepCallback;
|
|
1079
|
+
inputItems;
|
|
1080
|
+
zkvWalletClient;
|
|
1081
|
+
tfhePublicKeyDeserializer;
|
|
1082
|
+
compactPkeCrsDeserializer;
|
|
1083
|
+
zkBuilderAndCrsGenerator;
|
|
1084
|
+
initTfhe;
|
|
1085
|
+
zkProveWorkerFn;
|
|
1086
|
+
keysStorage;
|
|
1087
|
+
// Worker configuration (from config, overrideable)
|
|
1088
|
+
useWorker;
|
|
1089
|
+
stepTimestamps = {
|
|
1090
|
+
["initTfhe" /* InitTfhe */]: 0,
|
|
1091
|
+
["fetchKeys" /* FetchKeys */]: 0,
|
|
1092
|
+
["pack" /* Pack */]: 0,
|
|
1093
|
+
["prove" /* Prove */]: 0,
|
|
1094
|
+
["verify" /* Verify */]: 0
|
|
1095
|
+
};
|
|
1096
|
+
constructor(params) {
|
|
1097
|
+
super({
|
|
1098
|
+
config: params.config,
|
|
1099
|
+
publicClient: params.publicClient,
|
|
1100
|
+
walletClient: params.walletClient,
|
|
1101
|
+
chainId: params.chainId,
|
|
1102
|
+
account: params.account,
|
|
1103
|
+
requireConnected: params.requireConnected
|
|
1104
|
+
});
|
|
1105
|
+
this.inputItems = params.inputs;
|
|
1106
|
+
this.securityZone = params.securityZone ?? 0;
|
|
1107
|
+
this.zkvWalletClient = params.zkvWalletClient;
|
|
1108
|
+
if (!params.tfhePublicKeyDeserializer) {
|
|
1109
|
+
throw new CofheError({
|
|
1110
|
+
code: "MISSING_TFHE_PUBLIC_KEY_DESERIALIZER" /* MissingTfhePublicKeyDeserializer */,
|
|
1111
|
+
message: "EncryptInputsBuilder tfhePublicKeyDeserializer is undefined",
|
|
1112
|
+
hint: "Ensure client has been created with a tfhePublicKeyDeserializer.",
|
|
1113
|
+
context: {
|
|
1114
|
+
tfhePublicKeyDeserializer: params.tfhePublicKeyDeserializer
|
|
1115
|
+
}
|
|
1116
|
+
});
|
|
1117
|
+
}
|
|
1118
|
+
this.tfhePublicKeyDeserializer = params.tfhePublicKeyDeserializer;
|
|
1119
|
+
if (!params.compactPkeCrsDeserializer) {
|
|
1120
|
+
throw new CofheError({
|
|
1121
|
+
code: "MISSING_COMPACT_PKE_CRS_DESERIALIZER" /* MissingCompactPkeCrsDeserializer */,
|
|
1122
|
+
message: "EncryptInputsBuilder compactPkeCrsDeserializer is undefined",
|
|
1123
|
+
hint: "Ensure client has been created with a compactPkeCrsDeserializer.",
|
|
1124
|
+
context: {
|
|
1125
|
+
compactPkeCrsDeserializer: params.compactPkeCrsDeserializer
|
|
1126
|
+
}
|
|
1127
|
+
});
|
|
1128
|
+
}
|
|
1129
|
+
this.compactPkeCrsDeserializer = params.compactPkeCrsDeserializer;
|
|
1130
|
+
if (!params.zkBuilderAndCrsGenerator) {
|
|
1131
|
+
throw new CofheError({
|
|
1132
|
+
code: "MISSING_ZK_BUILDER_AND_CRS_GENERATOR" /* MissingZkBuilderAndCrsGenerator */,
|
|
1133
|
+
message: "EncryptInputsBuilder zkBuilderAndCrsGenerator is undefined",
|
|
1134
|
+
hint: "Ensure client has been created with a zkBuilderAndCrsGenerator.",
|
|
1135
|
+
context: {
|
|
1136
|
+
zkBuilderAndCrsGenerator: params.zkBuilderAndCrsGenerator
|
|
1137
|
+
}
|
|
1138
|
+
});
|
|
1139
|
+
}
|
|
1140
|
+
this.zkBuilderAndCrsGenerator = params.zkBuilderAndCrsGenerator;
|
|
1141
|
+
this.initTfhe = params.initTfhe;
|
|
1142
|
+
this.zkProveWorkerFn = params.zkProveWorkerFn;
|
|
1143
|
+
this.keysStorage = params.keysStorage;
|
|
1144
|
+
this.useWorker = params.config?.useWorkers ?? true;
|
|
1145
|
+
}
|
|
1146
|
+
/**
|
|
1147
|
+
* @param account - Account that will create the tx using the encrypted inputs.
|
|
1148
|
+
*
|
|
1149
|
+
* If not provided, the account will be fetched from the connected walletClient.
|
|
1150
|
+
*
|
|
1151
|
+
* Example:
|
|
1152
|
+
* ```typescript
|
|
1153
|
+
* const encrypted = await encryptInputs([Encryptable.uint128(10n)])
|
|
1154
|
+
* .setAccount("0x123")
|
|
1155
|
+
* .execute();
|
|
1156
|
+
* ```
|
|
1157
|
+
*
|
|
1158
|
+
* @returns The chainable EncryptInputsBuilder instance.
|
|
1159
|
+
*/
|
|
1160
|
+
setAccount(account) {
|
|
1161
|
+
this.account = account;
|
|
1162
|
+
return this;
|
|
1163
|
+
}
|
|
1164
|
+
getAccount() {
|
|
1165
|
+
return this.account;
|
|
1166
|
+
}
|
|
1167
|
+
/**
|
|
1168
|
+
* @param chainId - Chain that will consume the encrypted inputs.
|
|
1169
|
+
*
|
|
1170
|
+
* If not provided, the chainId will be fetched from the connected publicClient.
|
|
1171
|
+
*
|
|
1172
|
+
* Example:
|
|
1173
|
+
* ```typescript
|
|
1174
|
+
* const encrypted = await encryptInputs([Encryptable.uint128(10n)])
|
|
1175
|
+
* .setChainId(11155111)
|
|
1176
|
+
* .execute();
|
|
1177
|
+
* ```
|
|
1178
|
+
*
|
|
1179
|
+
* @returns The chainable EncryptInputsBuilder instance.
|
|
1180
|
+
*/
|
|
1181
|
+
setChainId(chainId) {
|
|
1182
|
+
this.chainId = chainId;
|
|
1183
|
+
return this;
|
|
1184
|
+
}
|
|
1185
|
+
getChainId() {
|
|
1186
|
+
return this.chainId;
|
|
1187
|
+
}
|
|
1188
|
+
/**
|
|
1189
|
+
* @param securityZone - Security zone to encrypt the inputs for.
|
|
1190
|
+
*
|
|
1191
|
+
* If not provided, the default securityZone 0 will be used.
|
|
1192
|
+
*
|
|
1193
|
+
* Example:
|
|
1194
|
+
* ```typescript
|
|
1195
|
+
* const encrypted = await encryptInputs([Encryptable.uint128(10n)])
|
|
1196
|
+
* .setSecurityZone(1)
|
|
1197
|
+
* .execute();
|
|
1198
|
+
* ```
|
|
1199
|
+
*
|
|
1200
|
+
* @returns The chainable EncryptInputsBuilder instance.
|
|
1201
|
+
*/
|
|
1202
|
+
setSecurityZone(securityZone) {
|
|
1203
|
+
this.securityZone = securityZone;
|
|
1204
|
+
return this;
|
|
1205
|
+
}
|
|
1206
|
+
getSecurityZone() {
|
|
1207
|
+
return this.securityZone;
|
|
1208
|
+
}
|
|
1209
|
+
/**
|
|
1210
|
+
* @param useWorker - Whether to use Web Workers for ZK proof generation.
|
|
1211
|
+
*
|
|
1212
|
+
* Overrides the config-level useWorkers setting for this specific encryption.
|
|
1213
|
+
*
|
|
1214
|
+
* Example:
|
|
1215
|
+
* ```typescript
|
|
1216
|
+
* const encrypted = await encryptInputs([Encryptable.uint128(10n)])
|
|
1217
|
+
* .setUseWorker(false)
|
|
1218
|
+
* .execute();
|
|
1219
|
+
* ```
|
|
1220
|
+
*
|
|
1221
|
+
* @returns The chainable EncryptInputsBuilder instance.
|
|
1222
|
+
*/
|
|
1223
|
+
setUseWorker(useWorker) {
|
|
1224
|
+
this.useWorker = useWorker;
|
|
1225
|
+
return this;
|
|
1226
|
+
}
|
|
1227
|
+
/**
|
|
1228
|
+
* Gets the current worker configuration.
|
|
1229
|
+
*
|
|
1230
|
+
* @returns Whether Web Workers are enabled for this encryption.
|
|
1231
|
+
*
|
|
1232
|
+
* Example:
|
|
1233
|
+
* ```typescript
|
|
1234
|
+
* const builder = encryptInputs([Encryptable.uint128(10n)]);
|
|
1235
|
+
* console.log(builder.getUseWorker()); // true (from config)
|
|
1236
|
+
* builder.setUseWorker(false);
|
|
1237
|
+
* console.log(builder.getUseWorker()); // false (overridden)
|
|
1238
|
+
* ```
|
|
1239
|
+
*/
|
|
1240
|
+
getUseWorker() {
|
|
1241
|
+
return this.useWorker;
|
|
1242
|
+
}
|
|
1243
|
+
/**
|
|
1244
|
+
* @param callback - Function to be called with the encryption step.
|
|
1245
|
+
*
|
|
1246
|
+
* Useful for debugging and tracking the progress of the encryption process.
|
|
1247
|
+
* Useful for a UI element that shows the progress of the encryption process.
|
|
1248
|
+
*
|
|
1249
|
+
* Example:
|
|
1250
|
+
* ```typescript
|
|
1251
|
+
* const encrypted = await encryptInputs([Encryptable.uint128(10n)])
|
|
1252
|
+
* .onStep((step: EncryptStep) => console.log(step))
|
|
1253
|
+
* .execute();
|
|
1254
|
+
* ```
|
|
1255
|
+
*
|
|
1256
|
+
* @returns The EncryptInputsBuilder instance.
|
|
1257
|
+
*/
|
|
1258
|
+
onStep(callback) {
|
|
1259
|
+
this.stepCallback = callback;
|
|
1260
|
+
return this;
|
|
1261
|
+
}
|
|
1262
|
+
getStepCallback() {
|
|
1263
|
+
return this.stepCallback;
|
|
1264
|
+
}
|
|
1265
|
+
/**
|
|
1266
|
+
* Fires the step callback if set
|
|
1267
|
+
*/
|
|
1268
|
+
fireStepStart(step, context = {}) {
|
|
1269
|
+
if (!this.stepCallback)
|
|
1270
|
+
return;
|
|
1271
|
+
this.stepTimestamps[step] = Date.now();
|
|
1272
|
+
this.stepCallback(step, { ...context, isStart: true, isEnd: false, duration: 0 });
|
|
1273
|
+
}
|
|
1274
|
+
fireStepEnd(step, context = {}) {
|
|
1275
|
+
if (!this.stepCallback)
|
|
1276
|
+
return;
|
|
1277
|
+
const duration = Date.now() - this.stepTimestamps[step];
|
|
1278
|
+
this.stepCallback(step, { ...context, isStart: false, isEnd: true, duration });
|
|
1279
|
+
}
|
|
1280
|
+
/**
|
|
1281
|
+
* zkVerifierUrl is included in the chains exported from @cofhe/sdk/chains for use in CofheConfig.supportedChains
|
|
1282
|
+
* Users should generally not set this manually.
|
|
1283
|
+
*/
|
|
1284
|
+
async getZkVerifierUrl() {
|
|
1285
|
+
this.assertChainId();
|
|
1286
|
+
return getZkVerifierUrlOrThrow(this.config, this.chainId);
|
|
1287
|
+
}
|
|
1288
|
+
/**
|
|
1289
|
+
* initTfhe is a platform-specific dependency injected into core/createCofheClientBase by web/createCofheClient and node/createCofheClient
|
|
1290
|
+
* web/ uses zama "tfhe"
|
|
1291
|
+
* node/ uses zama "node-tfhe"
|
|
1292
|
+
* Users should not set this manually.
|
|
1293
|
+
*/
|
|
1294
|
+
async initTfheOrThrow() {
|
|
1295
|
+
if (!this.initTfhe)
|
|
1296
|
+
return false;
|
|
1297
|
+
try {
|
|
1298
|
+
return await this.initTfhe();
|
|
1299
|
+
} catch (error) {
|
|
1300
|
+
throw CofheError.fromError(error, {
|
|
1301
|
+
code: "INIT_TFHE_FAILED" /* InitTfheFailed */,
|
|
1302
|
+
message: `Failed to initialize TFHE`,
|
|
1303
|
+
context: {
|
|
1304
|
+
initTfhe: this.initTfhe
|
|
1305
|
+
}
|
|
1306
|
+
});
|
|
1307
|
+
}
|
|
1308
|
+
}
|
|
1309
|
+
/**
|
|
1310
|
+
* Fetches the FHE key and CRS from the CoFHE API
|
|
1311
|
+
* If the key/crs already exists in the store it is returned, else it is fetched, stored, and returned
|
|
1312
|
+
*/
|
|
1313
|
+
async fetchFheKeyAndCrs() {
|
|
1314
|
+
this.assertChainId();
|
|
1315
|
+
const securityZone = this.getSecurityZone();
|
|
1316
|
+
try {
|
|
1317
|
+
await this.keysStorage?.rehydrateKeysStore();
|
|
1318
|
+
} catch (error) {
|
|
1319
|
+
throw CofheError.fromError(error, {
|
|
1320
|
+
code: "REHYDRATE_KEYS_STORE_FAILED" /* RehydrateKeysStoreFailed */,
|
|
1321
|
+
message: `Failed to rehydrate keys store`,
|
|
1322
|
+
context: {
|
|
1323
|
+
keysStorage: this.keysStorage
|
|
1324
|
+
}
|
|
1325
|
+
});
|
|
1326
|
+
}
|
|
1327
|
+
let fheKey;
|
|
1328
|
+
let fheKeyFetchedFromCoFHE = false;
|
|
1329
|
+
let crs;
|
|
1330
|
+
let crsFetchedFromCoFHE = false;
|
|
1331
|
+
try {
|
|
1332
|
+
[[fheKey, fheKeyFetchedFromCoFHE], [crs, crsFetchedFromCoFHE]] = await fetchKeys(
|
|
1333
|
+
this.config,
|
|
1334
|
+
this.chainId,
|
|
1335
|
+
securityZone,
|
|
1336
|
+
this.tfhePublicKeyDeserializer,
|
|
1337
|
+
this.compactPkeCrsDeserializer,
|
|
1338
|
+
this.keysStorage
|
|
1339
|
+
);
|
|
1340
|
+
} catch (error) {
|
|
1341
|
+
throw CofheError.fromError(error, {
|
|
1342
|
+
code: "FETCH_KEYS_FAILED" /* FetchKeysFailed */,
|
|
1343
|
+
message: `Failed to fetch FHE key and CRS`,
|
|
1344
|
+
context: {
|
|
1345
|
+
config: this.config,
|
|
1346
|
+
chainId: this.chainId,
|
|
1347
|
+
securityZone,
|
|
1348
|
+
compactPkeCrsDeserializer: this.compactPkeCrsDeserializer,
|
|
1349
|
+
tfhePublicKeyDeserializer: this.tfhePublicKeyDeserializer
|
|
1350
|
+
}
|
|
1351
|
+
});
|
|
1352
|
+
}
|
|
1353
|
+
if (!fheKey) {
|
|
1354
|
+
throw new CofheError({
|
|
1355
|
+
code: "MISSING_FHE_KEY" /* MissingFheKey */,
|
|
1356
|
+
message: `FHE key not found`,
|
|
1357
|
+
context: {
|
|
1358
|
+
chainId: this.chainId,
|
|
1359
|
+
securityZone
|
|
1360
|
+
}
|
|
1361
|
+
});
|
|
1362
|
+
}
|
|
1363
|
+
if (!crs) {
|
|
1364
|
+
throw new CofheError({
|
|
1365
|
+
code: "MISSING_CRS" /* MissingCrs */,
|
|
1366
|
+
message: `CRS not found for chainId <${this.chainId}>`,
|
|
1367
|
+
context: {
|
|
1368
|
+
chainId: this.chainId
|
|
1369
|
+
}
|
|
1370
|
+
});
|
|
1371
|
+
}
|
|
1372
|
+
return { fheKey, fheKeyFetchedFromCoFHE, crs, crsFetchedFromCoFHE };
|
|
1373
|
+
}
|
|
1374
|
+
/**
|
|
1375
|
+
* Resolves the encryptDelay config into an array of 5 per-step delays.
|
|
1376
|
+
* A single number is broadcast to all steps; a tuple is used as-is.
|
|
1377
|
+
*/
|
|
1378
|
+
resolveEncryptDelays() {
|
|
1379
|
+
const encryptDelay = this.config?.mocks?.encryptDelay ?? [100, 100, 100, 500, 500];
|
|
1380
|
+
if (typeof encryptDelay === "number") {
|
|
1381
|
+
return [encryptDelay, encryptDelay, encryptDelay, encryptDelay, encryptDelay];
|
|
1382
|
+
}
|
|
1383
|
+
return encryptDelay;
|
|
1384
|
+
}
|
|
1385
|
+
/**
|
|
1386
|
+
* @dev Encrypt against the cofheMocks instead of CoFHE
|
|
1387
|
+
*
|
|
1388
|
+
* In the cofheMocks, the MockZkVerifier contract is deployed on hardhat to a fixed address, this contract handles mocking the zk verifying.
|
|
1389
|
+
* cofheMocksInsertPackedHashes - stores the ctHashes and their plaintext values for on-chain mocking of FHE operations.
|
|
1390
|
+
* cofheMocksZkCreateProofSignatures - creates signatures to be included in the encrypted inputs. The signers address is known and verified in the mock contracts.
|
|
1391
|
+
*/
|
|
1392
|
+
async mocksExecute() {
|
|
1393
|
+
this.assertAccount();
|
|
1394
|
+
this.assertPublicClient();
|
|
1395
|
+
this.assertWalletClient();
|
|
1396
|
+
const [initTfheDelay, fetchKeysDelay, packDelay, proveDelay, verifyDelay] = this.resolveEncryptDelays();
|
|
1397
|
+
this.fireStepStart("initTfhe" /* InitTfhe */);
|
|
1398
|
+
await sleep(initTfheDelay);
|
|
1399
|
+
this.fireStepEnd("initTfhe" /* InitTfhe */, {
|
|
1400
|
+
tfheInitializationExecuted: false,
|
|
1401
|
+
isMocks: true,
|
|
1402
|
+
mockSleep: initTfheDelay
|
|
1403
|
+
});
|
|
1404
|
+
this.fireStepStart("fetchKeys" /* FetchKeys */);
|
|
1405
|
+
await sleep(fetchKeysDelay);
|
|
1406
|
+
this.fireStepEnd("fetchKeys" /* FetchKeys */, {
|
|
1407
|
+
fheKeyFetchedFromCoFHE: false,
|
|
1408
|
+
crsFetchedFromCoFHE: false,
|
|
1409
|
+
isMocks: true,
|
|
1410
|
+
mockSleep: fetchKeysDelay
|
|
1411
|
+
});
|
|
1412
|
+
this.fireStepStart("pack" /* Pack */);
|
|
1413
|
+
await cofheMocksCheckEncryptableBits(this.inputItems);
|
|
1414
|
+
await sleep(packDelay);
|
|
1415
|
+
this.fireStepEnd("pack" /* Pack */, { isMocks: true, mockSleep: packDelay });
|
|
1416
|
+
this.fireStepStart("prove" /* Prove */);
|
|
1417
|
+
await sleep(proveDelay);
|
|
1418
|
+
this.fireStepEnd("prove" /* Prove */, { isMocks: true, mockSleep: proveDelay });
|
|
1419
|
+
this.fireStepStart("verify" /* Verify */);
|
|
1420
|
+
await sleep(verifyDelay);
|
|
1421
|
+
const signedResults = await cofheMocksZkVerifySign(
|
|
1422
|
+
this.inputItems,
|
|
1423
|
+
this.account,
|
|
1424
|
+
this.securityZone,
|
|
1425
|
+
this.publicClient,
|
|
1426
|
+
this.walletClient,
|
|
1427
|
+
this.zkvWalletClient
|
|
1428
|
+
);
|
|
1429
|
+
const encryptedInputs = signedResults.map(({ ct_hash, signature }, index) => ({
|
|
1430
|
+
ctHash: BigInt(ct_hash),
|
|
1431
|
+
securityZone: this.securityZone,
|
|
1432
|
+
utype: this.inputItems[index].utype,
|
|
1433
|
+
signature
|
|
1434
|
+
}));
|
|
1435
|
+
this.fireStepEnd("verify" /* Verify */, { isMocks: true, mockSleep: verifyDelay });
|
|
1436
|
+
return encryptedInputs;
|
|
1437
|
+
}
|
|
1438
|
+
/**
|
|
1439
|
+
* In the production context, perform a true encryption with the CoFHE coprocessor.
|
|
1440
|
+
*/
|
|
1441
|
+
async productionExecute() {
|
|
1442
|
+
this.assertAccount();
|
|
1443
|
+
this.assertChainId();
|
|
1444
|
+
this.fireStepStart("initTfhe" /* InitTfhe */);
|
|
1445
|
+
const tfheInitializationExecuted = await this.initTfheOrThrow();
|
|
1446
|
+
this.fireStepEnd("initTfhe" /* InitTfhe */, { tfheInitializationExecuted });
|
|
1447
|
+
this.fireStepStart("fetchKeys" /* FetchKeys */);
|
|
1448
|
+
const { fheKey, fheKeyFetchedFromCoFHE, crs, crsFetchedFromCoFHE } = await this.fetchFheKeyAndCrs();
|
|
1449
|
+
let { zkBuilder, zkCrs } = this.zkBuilderAndCrsGenerator(fheKey, crs);
|
|
1450
|
+
this.fireStepEnd("fetchKeys" /* FetchKeys */, { fheKeyFetchedFromCoFHE, crsFetchedFromCoFHE });
|
|
1451
|
+
this.fireStepStart("pack" /* Pack */);
|
|
1452
|
+
zkBuilder = zkPack(this.inputItems, zkBuilder);
|
|
1453
|
+
this.fireStepEnd("pack" /* Pack */);
|
|
1454
|
+
this.fireStepStart("prove" /* Prove */);
|
|
1455
|
+
const metadata = constructZkPoKMetadata(this.account, this.securityZone, this.chainId);
|
|
1456
|
+
let proof = null;
|
|
1457
|
+
let usedWorker = false;
|
|
1458
|
+
let workerFailedError;
|
|
1459
|
+
if (this.useWorker && this.zkProveWorkerFn) {
|
|
1460
|
+
try {
|
|
1461
|
+
proof = await zkProveWithWorker(this.zkProveWorkerFn, fheKey, crs, this.inputItems, metadata);
|
|
1462
|
+
usedWorker = true;
|
|
1463
|
+
} catch (error) {
|
|
1464
|
+
workerFailedError = error instanceof Error ? error.message : String(error);
|
|
1465
|
+
}
|
|
1466
|
+
}
|
|
1467
|
+
if (proof == null) {
|
|
1468
|
+
proof = await zkProve(zkBuilder, zkCrs, metadata);
|
|
1469
|
+
usedWorker = false;
|
|
1470
|
+
}
|
|
1471
|
+
this.fireStepEnd("prove" /* Prove */, {
|
|
1472
|
+
useWorker: this.useWorker,
|
|
1473
|
+
usedWorker,
|
|
1474
|
+
workerFailedError
|
|
1475
|
+
});
|
|
1476
|
+
this.fireStepStart("verify" /* Verify */);
|
|
1477
|
+
const zkVerifierUrl = await this.getZkVerifierUrl();
|
|
1478
|
+
const verifyResults = await zkVerify(zkVerifierUrl, proof, this.account, this.securityZone, this.chainId);
|
|
1479
|
+
const encryptedInputs = verifyResults.map(
|
|
1480
|
+
({ ct_hash, signature }, index) => ({
|
|
1481
|
+
ctHash: BigInt(ct_hash),
|
|
1482
|
+
securityZone: this.securityZone,
|
|
1483
|
+
utype: this.inputItems[index].utype,
|
|
1484
|
+
signature
|
|
1485
|
+
})
|
|
1486
|
+
);
|
|
1487
|
+
this.fireStepEnd("verify" /* Verify */);
|
|
1488
|
+
return encryptedInputs;
|
|
1489
|
+
}
|
|
1490
|
+
/**
|
|
1491
|
+
* Final step of the encryption process. MUST BE CALLED LAST IN THE CHAIN.
|
|
1492
|
+
*
|
|
1493
|
+
* This will:
|
|
1494
|
+
* - Pack the encryptable items into a zk proof
|
|
1495
|
+
* - Prove the zk proof
|
|
1496
|
+
* - Verify the zk proof with CoFHE
|
|
1497
|
+
* - Package and return the encrypted inputs
|
|
1498
|
+
*
|
|
1499
|
+
* Example:
|
|
1500
|
+
* ```typescript
|
|
1501
|
+
* const encrypted = await encryptInputs([Encryptable.uint128(10n)])
|
|
1502
|
+
* .setAccount('0x123...890') // optional
|
|
1503
|
+
* .setChainId(11155111) // optional
|
|
1504
|
+
* .execute(); // execute
|
|
1505
|
+
* ```
|
|
1506
|
+
*
|
|
1507
|
+
* @returns The encrypted inputs.
|
|
1508
|
+
*/
|
|
1509
|
+
async execute() {
|
|
1510
|
+
if (this.chainId === chains.hardhat.id)
|
|
1511
|
+
return this.mocksExecute();
|
|
1512
|
+
return this.productionExecute();
|
|
1513
|
+
}
|
|
1514
|
+
};
|
|
1515
|
+
|
|
1516
|
+
// permits/utils.ts
|
|
1517
|
+
var fromHexString = (hexString) => {
|
|
1518
|
+
const cleanString = hexString.length % 2 === 1 ? `0${hexString}` : hexString;
|
|
1519
|
+
const arr = cleanString.replace(/^0x/, "").match(/.{1,2}/g);
|
|
1520
|
+
if (!arr)
|
|
1521
|
+
return new Uint8Array();
|
|
1522
|
+
return new Uint8Array(arr.map((byte) => parseInt(byte, 16)));
|
|
1523
|
+
};
|
|
1524
|
+
var toHexString2 = (bytes) => bytes.reduce((str, byte) => str + byte.toString(16).padStart(2, "0"), "");
|
|
1525
|
+
function toBigInt(value) {
|
|
1526
|
+
if (typeof value === "string") {
|
|
1527
|
+
return BigInt(value);
|
|
1528
|
+
} else if (typeof value === "number") {
|
|
1529
|
+
return BigInt(value);
|
|
1530
|
+
} else if (typeof value === "object") {
|
|
1531
|
+
return BigInt("0x" + toHexString2(value));
|
|
1532
|
+
} else {
|
|
1533
|
+
return value;
|
|
1534
|
+
}
|
|
1535
|
+
}
|
|
1536
|
+
function toBeArray(value) {
|
|
1537
|
+
const bigIntValue = typeof value === "number" ? BigInt(value) : value;
|
|
1538
|
+
const hex = bigIntValue.toString(16);
|
|
1539
|
+
const paddedHex = hex.length % 2 === 0 ? hex : "0" + hex;
|
|
1540
|
+
return fromHexString(paddedHex);
|
|
1541
|
+
}
|
|
1542
|
+
function isString(value) {
|
|
1543
|
+
if (typeof value !== "string") {
|
|
1544
|
+
throw new Error(`Expected value which is \`string\`, received value of type \`${typeof value}\`.`);
|
|
1545
|
+
}
|
|
1546
|
+
}
|
|
1547
|
+
function isNumber(value) {
|
|
1548
|
+
const is = typeof value === "number" && !Number.isNaN(value);
|
|
1549
|
+
if (!is) {
|
|
1550
|
+
throw new Error(`Expected value which is \`number\`, received value of type \`${typeof value}\`.`);
|
|
1551
|
+
}
|
|
1552
|
+
}
|
|
1553
|
+
function isBigIntOrNumber(value) {
|
|
1554
|
+
const is = typeof value === "bigint";
|
|
1555
|
+
if (!is) {
|
|
1556
|
+
try {
|
|
1557
|
+
isNumber(value);
|
|
1558
|
+
} catch (e) {
|
|
1559
|
+
throw new Error(`Value ${value} is not a number or bigint: ${typeof value}`);
|
|
1560
|
+
}
|
|
1561
|
+
}
|
|
1562
|
+
}
|
|
1563
|
+
|
|
1564
|
+
// permits/sealing.ts
|
|
1565
|
+
var PRIVATE_KEY_LENGTH = 64;
|
|
1566
|
+
var PUBLIC_KEY_LENGTH = 64;
|
|
1567
|
+
var SealingKey = class _SealingKey {
|
|
1568
|
+
/**
|
|
1569
|
+
* The private key used for decryption.
|
|
1570
|
+
*/
|
|
1571
|
+
privateKey;
|
|
1572
|
+
/**
|
|
1573
|
+
* The public key used for encryption.
|
|
1574
|
+
*/
|
|
1575
|
+
publicKey;
|
|
1576
|
+
/**
|
|
1577
|
+
* Constructs a SealingKey instance with the given private and public keys.
|
|
1578
|
+
*
|
|
1579
|
+
* @param {string} privateKey - The private key used for decryption.
|
|
1580
|
+
* @param {string} publicKey - The public key used for encryption.
|
|
1581
|
+
* @throws Will throw an error if the provided keys lengths do not match
|
|
1582
|
+
* the required lengths for private and public keys.
|
|
1583
|
+
*/
|
|
1584
|
+
constructor(privateKey, publicKey) {
|
|
1585
|
+
if (privateKey.length !== PRIVATE_KEY_LENGTH) {
|
|
1586
|
+
throw new Error(`Private key must be of length ${PRIVATE_KEY_LENGTH}`);
|
|
1587
|
+
}
|
|
1588
|
+
if (publicKey.length !== PUBLIC_KEY_LENGTH) {
|
|
1589
|
+
throw new Error(`Public key must be of length ${PUBLIC_KEY_LENGTH}`);
|
|
1590
|
+
}
|
|
1591
|
+
this.privateKey = privateKey;
|
|
1592
|
+
this.publicKey = publicKey;
|
|
1593
|
+
}
|
|
1594
|
+
unseal = (parsedData) => {
|
|
1595
|
+
const nonce = parsedData.nonce instanceof Uint8Array ? parsedData.nonce : new Uint8Array(parsedData.nonce);
|
|
1596
|
+
const ephemPublicKey = parsedData.public_key instanceof Uint8Array ? parsedData.public_key : new Uint8Array(parsedData.public_key);
|
|
1597
|
+
const dataToDecrypt = parsedData.data instanceof Uint8Array ? parsedData.data : new Uint8Array(parsedData.data);
|
|
1598
|
+
const privateKeyBytes = fromHexString(this.privateKey);
|
|
1599
|
+
const decryptedMessage = nacl__default.default.box.open(dataToDecrypt, nonce, ephemPublicKey, privateKeyBytes);
|
|
1600
|
+
if (!decryptedMessage) {
|
|
1601
|
+
throw new Error("Failed to decrypt message");
|
|
1602
|
+
}
|
|
1603
|
+
return toBigInt(decryptedMessage);
|
|
1604
|
+
};
|
|
1605
|
+
/**
|
|
1606
|
+
* Serializes the SealingKey to a JSON object.
|
|
1607
|
+
*/
|
|
1608
|
+
serialize = () => {
|
|
1609
|
+
return {
|
|
1610
|
+
privateKey: this.privateKey,
|
|
1611
|
+
publicKey: this.publicKey
|
|
1612
|
+
};
|
|
1613
|
+
};
|
|
1614
|
+
/**
|
|
1615
|
+
* Deserializes the SealingKey from a JSON object.
|
|
1616
|
+
*/
|
|
1617
|
+
static deserialize = (privateKey, publicKey) => {
|
|
1618
|
+
return new _SealingKey(privateKey, publicKey);
|
|
1619
|
+
};
|
|
1620
|
+
/**
|
|
1621
|
+
* Seals (encrypts) the provided message for a receiver with the specified public key.
|
|
1622
|
+
*
|
|
1623
|
+
* @param {bigint | number} value - The message to be encrypted.
|
|
1624
|
+
* @param {string} publicKey - The public key of the intended recipient.
|
|
1625
|
+
* @returns string - The encrypted message in hexadecimal format.
|
|
1626
|
+
* @static
|
|
1627
|
+
* @throws Will throw if the provided publicKey or value do not meet defined preconditions.
|
|
1628
|
+
*/
|
|
1629
|
+
static seal = (value, publicKey) => {
|
|
1630
|
+
isString(publicKey);
|
|
1631
|
+
isBigIntOrNumber(value);
|
|
1632
|
+
const ephemeralKeyPair = nacl__default.default.box.keyPair();
|
|
1633
|
+
const nonce = nacl__default.default.randomBytes(nacl__default.default.box.nonceLength);
|
|
1634
|
+
const encryptedMessage = nacl__default.default.box(toBeArray(value), nonce, fromHexString(publicKey), ephemeralKeyPair.secretKey);
|
|
1635
|
+
return {
|
|
1636
|
+
data: encryptedMessage,
|
|
1637
|
+
public_key: ephemeralKeyPair.publicKey,
|
|
1638
|
+
nonce
|
|
1639
|
+
};
|
|
1640
|
+
};
|
|
1641
|
+
};
|
|
1642
|
+
var GenerateSealingKey = () => {
|
|
1643
|
+
const sodiumKeypair = nacl__default.default.box.keyPair();
|
|
1644
|
+
return new SealingKey(toHexString2(sodiumKeypair.secretKey), toHexString2(sodiumKeypair.publicKey));
|
|
1645
|
+
};
|
|
1646
|
+
var SerializedSealingPair = zod.z.object({
|
|
1647
|
+
privateKey: zod.z.string(),
|
|
1648
|
+
publicKey: zod.z.string()
|
|
1649
|
+
});
|
|
1650
|
+
var addressSchema = zod.z.string().refine((val) => viem.isAddress(val), {
|
|
1651
|
+
error: "Invalid address"
|
|
1652
|
+
}).transform((val) => viem.getAddress(val));
|
|
1653
|
+
var addressNotZeroSchema = addressSchema.refine((val) => val !== viem.zeroAddress, {
|
|
1654
|
+
error: "Must not be zeroAddress"
|
|
1655
|
+
});
|
|
1656
|
+
var bytesSchema = zod.z.custom(
|
|
1657
|
+
(val) => {
|
|
1658
|
+
return typeof val === "string" && viem.isHex(val);
|
|
1659
|
+
},
|
|
1660
|
+
{
|
|
1661
|
+
message: "Invalid hex value"
|
|
1662
|
+
}
|
|
1663
|
+
);
|
|
1664
|
+
var bytesNotEmptySchema = bytesSchema.refine((val) => val !== "0x", {
|
|
1665
|
+
error: "Must not be empty"
|
|
1666
|
+
});
|
|
1667
|
+
var DEFAULT_EXPIRATION_FN = () => Math.round(Date.now() / 1e3) + 7 * 24 * 60 * 60;
|
|
1668
|
+
var zPermitWithDefaults = zod.z.object({
|
|
1669
|
+
name: zod.z.string().optional().default("Unnamed Permit"),
|
|
1670
|
+
type: zod.z.enum(["self", "sharing", "recipient"]),
|
|
1671
|
+
issuer: addressNotZeroSchema,
|
|
1672
|
+
expiration: zod.z.int().optional().default(DEFAULT_EXPIRATION_FN),
|
|
1673
|
+
recipient: addressSchema.optional().default(viem.zeroAddress),
|
|
1674
|
+
validatorId: zod.z.int().optional().default(0),
|
|
1675
|
+
validatorContract: addressSchema.optional().default(viem.zeroAddress),
|
|
1676
|
+
issuerSignature: bytesSchema.optional().default("0x"),
|
|
1677
|
+
recipientSignature: bytesSchema.optional().default("0x")
|
|
1678
|
+
});
|
|
1679
|
+
var zPermitWithSealingPair = zPermitWithDefaults.extend({
|
|
1680
|
+
sealingPair: SerializedSealingPair.optional()
|
|
1681
|
+
});
|
|
1682
|
+
var ExternalValidatorRefinement = [
|
|
1683
|
+
(data) => data.validatorId !== 0 && data.validatorContract !== viem.zeroAddress || data.validatorId === 0 && data.validatorContract === viem.zeroAddress,
|
|
1684
|
+
{
|
|
1685
|
+
error: "Permit external validator :: validatorId and validatorContract must either both be set or both be unset.",
|
|
1686
|
+
path: ["validatorId", "validatorContract"]
|
|
1687
|
+
}
|
|
1688
|
+
];
|
|
1689
|
+
var RecipientRefinement = [
|
|
1690
|
+
(data) => data.issuer !== data.recipient,
|
|
1691
|
+
{
|
|
1692
|
+
error: "Sharing permit :: issuer and recipient must not be the same",
|
|
1693
|
+
path: ["issuer", "recipient"]
|
|
1694
|
+
}
|
|
1695
|
+
];
|
|
1696
|
+
var SelfPermitOptionsValidator = zod.z.object({
|
|
1697
|
+
type: zod.z.literal("self").optional().default("self"),
|
|
1698
|
+
issuer: addressNotZeroSchema,
|
|
1699
|
+
name: zod.z.string().optional().default("Unnamed Permit"),
|
|
1700
|
+
expiration: zod.z.int().optional().default(DEFAULT_EXPIRATION_FN),
|
|
1701
|
+
recipient: addressSchema.optional().default(viem.zeroAddress),
|
|
1702
|
+
validatorId: zod.z.int().optional().default(0),
|
|
1703
|
+
validatorContract: addressSchema.optional().default(viem.zeroAddress),
|
|
1704
|
+
issuerSignature: bytesSchema.optional().default("0x"),
|
|
1705
|
+
recipientSignature: bytesSchema.optional().default("0x")
|
|
1706
|
+
}).refine(...ExternalValidatorRefinement);
|
|
1707
|
+
var SelfPermitValidator = zPermitWithSealingPair.refine((data) => data.type === "self", {
|
|
1708
|
+
error: "Type must be 'self'"
|
|
1709
|
+
}).refine((data) => data.recipient === viem.zeroAddress, {
|
|
1710
|
+
error: "Recipient must be zeroAddress"
|
|
1711
|
+
}).refine((data) => data.issuerSignature !== "0x", {
|
|
1712
|
+
error: "IssuerSignature must be populated"
|
|
1713
|
+
}).refine((data) => data.recipientSignature === "0x", {
|
|
1714
|
+
error: "RecipientSignature must be empty"
|
|
1715
|
+
}).refine(...ExternalValidatorRefinement);
|
|
1716
|
+
var SharingPermitOptionsValidator = zod.z.object({
|
|
1717
|
+
type: zod.z.literal("sharing").optional().default("sharing"),
|
|
1718
|
+
issuer: addressNotZeroSchema,
|
|
1719
|
+
recipient: addressNotZeroSchema,
|
|
1720
|
+
name: zod.z.string().optional().default("Unnamed Permit"),
|
|
1721
|
+
expiration: zod.z.int().optional().default(DEFAULT_EXPIRATION_FN),
|
|
1722
|
+
validatorId: zod.z.int().optional().default(0),
|
|
1723
|
+
validatorContract: addressSchema.optional().default(viem.zeroAddress),
|
|
1724
|
+
issuerSignature: bytesSchema.optional().default("0x"),
|
|
1725
|
+
recipientSignature: bytesSchema.optional().default("0x")
|
|
1726
|
+
}).refine(...RecipientRefinement).refine(...ExternalValidatorRefinement);
|
|
1727
|
+
var SharingPermitValidator = zPermitWithSealingPair.refine((data) => data.type === "sharing", {
|
|
1728
|
+
error: "Type must be 'sharing'"
|
|
1729
|
+
}).refine((data) => data.recipient !== viem.zeroAddress, {
|
|
1730
|
+
error: "Recipient must not be zeroAddress"
|
|
1731
|
+
}).refine((data) => data.issuerSignature !== "0x", {
|
|
1732
|
+
error: "IssuerSignature must be populated"
|
|
1733
|
+
}).refine((data) => data.recipientSignature === "0x", {
|
|
1734
|
+
error: "RecipientSignature must be empty"
|
|
1735
|
+
}).refine(...ExternalValidatorRefinement);
|
|
1736
|
+
var ImportPermitOptionsValidator = zod.z.object({
|
|
1737
|
+
type: zod.z.literal("recipient").optional().default("recipient"),
|
|
1738
|
+
issuer: addressNotZeroSchema,
|
|
1739
|
+
recipient: addressNotZeroSchema,
|
|
1740
|
+
name: zod.z.string().optional().default("Unnamed Permit"),
|
|
1741
|
+
expiration: zod.z.int(),
|
|
1742
|
+
validatorId: zod.z.int().optional().default(0),
|
|
1743
|
+
validatorContract: addressSchema.optional().default(viem.zeroAddress),
|
|
1744
|
+
issuerSignature: bytesNotEmptySchema,
|
|
1745
|
+
recipientSignature: bytesSchema.optional().default("0x")
|
|
1746
|
+
}).refine(...ExternalValidatorRefinement);
|
|
1747
|
+
var ImportPermitValidator = zPermitWithSealingPair.refine((data) => data.type === "recipient", {
|
|
1748
|
+
error: "Type must be 'recipient'"
|
|
1749
|
+
}).refine((data) => data.recipient !== viem.zeroAddress, {
|
|
1750
|
+
error: "Recipient must not be zeroAddress"
|
|
1751
|
+
}).refine((data) => data.issuerSignature !== "0x", {
|
|
1752
|
+
error: "IssuerSignature must be populated"
|
|
1753
|
+
}).refine((data) => data.recipientSignature !== "0x", {
|
|
1754
|
+
error: "RecipientSignature must be populated"
|
|
1755
|
+
}).refine(...ExternalValidatorRefinement);
|
|
1756
|
+
var safeParseAndThrowFormatted = (schema, data, message) => {
|
|
1757
|
+
const result = schema.safeParse(data);
|
|
1758
|
+
if (!result.success) {
|
|
1759
|
+
throw new Error(`${message}: ${zod.z.prettifyError(result.error)}`, { cause: result.error });
|
|
1760
|
+
}
|
|
1761
|
+
return result.data;
|
|
1762
|
+
};
|
|
1763
|
+
var validateSelfPermitOptions = (options) => {
|
|
1764
|
+
return safeParseAndThrowFormatted(SelfPermitOptionsValidator, options, "Invalid self permit options");
|
|
1765
|
+
};
|
|
1766
|
+
var validateSharingPermitOptions = (options) => {
|
|
1767
|
+
return safeParseAndThrowFormatted(SharingPermitOptionsValidator, options, "Invalid sharing permit options");
|
|
1768
|
+
};
|
|
1769
|
+
var validateImportPermitOptions = (options) => {
|
|
1770
|
+
return safeParseAndThrowFormatted(ImportPermitOptionsValidator, options, "Invalid import permit options");
|
|
1771
|
+
};
|
|
1772
|
+
var validateSelfPermit = (permit) => {
|
|
1773
|
+
return safeParseAndThrowFormatted(SelfPermitValidator, permit, "Invalid self permit");
|
|
1774
|
+
};
|
|
1775
|
+
var validateSharingPermit = (permit) => {
|
|
1776
|
+
return safeParseAndThrowFormatted(SharingPermitValidator, permit, "Invalid sharing permit");
|
|
1777
|
+
};
|
|
1778
|
+
var validateImportPermit = (permit) => {
|
|
1779
|
+
return safeParseAndThrowFormatted(ImportPermitValidator, permit, "Invalid import permit");
|
|
1780
|
+
};
|
|
1781
|
+
var ValidationUtils = {
|
|
1782
|
+
/**
|
|
1783
|
+
* Check if permit is expired
|
|
1784
|
+
*/
|
|
1785
|
+
isExpired: (permit) => {
|
|
1786
|
+
return permit.expiration < Math.floor(Date.now() / 1e3);
|
|
1787
|
+
},
|
|
1788
|
+
/**
|
|
1789
|
+
* Check if permit is signed by the active party
|
|
1790
|
+
*/
|
|
1791
|
+
isSigned: (permit) => {
|
|
1792
|
+
if (permit.type === "self" || permit.type === "sharing") {
|
|
1793
|
+
return permit.issuerSignature !== "0x";
|
|
1794
|
+
}
|
|
1795
|
+
if (permit.type === "recipient") {
|
|
1796
|
+
return permit.recipientSignature !== "0x";
|
|
1797
|
+
}
|
|
1798
|
+
return false;
|
|
1799
|
+
},
|
|
1800
|
+
/**
|
|
1801
|
+
* Checks that a permit is signed and not expired.
|
|
1802
|
+
*/
|
|
1803
|
+
isSignedAndNotExpired: (permit) => {
|
|
1804
|
+
if (ValidationUtils.isExpired(permit)) {
|
|
1805
|
+
return { valid: false, error: "expired" };
|
|
1806
|
+
}
|
|
1807
|
+
if (!ValidationUtils.isSigned(permit)) {
|
|
1808
|
+
return { valid: false, error: "not-signed" };
|
|
1809
|
+
}
|
|
1810
|
+
return { valid: true, error: null };
|
|
1811
|
+
},
|
|
1812
|
+
/**
|
|
1813
|
+
* Asserts that a permit is signed and not expired.
|
|
1814
|
+
*
|
|
1815
|
+
* Throws `Error` with message:
|
|
1816
|
+
* - `Permit is expired`
|
|
1817
|
+
* - `Permit is not signed`
|
|
1818
|
+
*/
|
|
1819
|
+
assertSignedAndNotExpired: (permit) => {
|
|
1820
|
+
const result = ValidationUtils.isSignedAndNotExpired(permit);
|
|
1821
|
+
if (result.valid)
|
|
1822
|
+
return;
|
|
1823
|
+
if (result.error === "expired") {
|
|
1824
|
+
throw new Error("Permit is expired");
|
|
1825
|
+
}
|
|
1826
|
+
if (result.error === "not-signed") {
|
|
1827
|
+
throw new Error("Permit is not signed");
|
|
1828
|
+
}
|
|
1829
|
+
throw new Error("Permit is invalid");
|
|
1830
|
+
},
|
|
1831
|
+
isValid: (permit) => {
|
|
1832
|
+
const schema = permit.type === "self" ? SelfPermitValidator : permit.type === "sharing" ? SharingPermitValidator : permit.type === "recipient" ? ImportPermitValidator : null;
|
|
1833
|
+
if (schema == null)
|
|
1834
|
+
return { valid: false, error: "invalid-schema" };
|
|
1835
|
+
const schemaResult = schema.safeParse(permit);
|
|
1836
|
+
if (!schemaResult.success)
|
|
1837
|
+
return { valid: false, error: "invalid-schema" };
|
|
1838
|
+
return ValidationUtils.isSignedAndNotExpired(permit);
|
|
1839
|
+
}
|
|
1840
|
+
};
|
|
1841
|
+
|
|
1842
|
+
// permits/signature.ts
|
|
1843
|
+
var PermitSignatureAllFields = [
|
|
1844
|
+
{ name: "issuer", type: "address" },
|
|
1845
|
+
{ name: "expiration", type: "uint64" },
|
|
1846
|
+
{ name: "recipient", type: "address" },
|
|
1847
|
+
{ name: "validatorId", type: "uint256" },
|
|
1848
|
+
{ name: "validatorContract", type: "address" },
|
|
1849
|
+
{ name: "sealingKey", type: "bytes32" },
|
|
1850
|
+
{ name: "issuerSignature", type: "bytes" }
|
|
1851
|
+
];
|
|
1852
|
+
var SignatureTypes = {
|
|
1853
|
+
PermissionedV2IssuerSelf: [
|
|
1854
|
+
"issuer",
|
|
1855
|
+
"expiration",
|
|
1856
|
+
"recipient",
|
|
1857
|
+
"validatorId",
|
|
1858
|
+
"validatorContract",
|
|
1859
|
+
"sealingKey"
|
|
1860
|
+
],
|
|
1861
|
+
PermissionedV2IssuerShared: [
|
|
1862
|
+
"issuer",
|
|
1863
|
+
"expiration",
|
|
1864
|
+
"recipient",
|
|
1865
|
+
"validatorId",
|
|
1866
|
+
"validatorContract"
|
|
1867
|
+
],
|
|
1868
|
+
PermissionedV2Recipient: ["sealingKey", "issuerSignature"]
|
|
1869
|
+
};
|
|
1870
|
+
var getSignatureTypesAndMessage = (primaryType, fields, values) => {
|
|
1871
|
+
const types = {
|
|
1872
|
+
[primaryType]: PermitSignatureAllFields.filter((fieldType) => fields.includes(fieldType.name))
|
|
1873
|
+
};
|
|
1874
|
+
const message = {};
|
|
1875
|
+
fields.forEach((field) => {
|
|
1876
|
+
if (field in values) {
|
|
1877
|
+
message[field] = values[field];
|
|
1878
|
+
}
|
|
1879
|
+
});
|
|
1880
|
+
return { types, primaryType, message };
|
|
1881
|
+
};
|
|
1882
|
+
var SignatureUtils = {
|
|
1883
|
+
/**
|
|
1884
|
+
* Get signature parameters for a permit
|
|
1885
|
+
*/
|
|
1886
|
+
getSignatureParams: (permit, primaryType) => {
|
|
1887
|
+
return getSignatureTypesAndMessage(primaryType, SignatureTypes[primaryType], permit);
|
|
1888
|
+
},
|
|
1889
|
+
/**
|
|
1890
|
+
* Determine the required signature type based on permit type
|
|
1891
|
+
*/
|
|
1892
|
+
getPrimaryType: (permitType) => {
|
|
1893
|
+
if (permitType === "self")
|
|
1894
|
+
return "PermissionedV2IssuerSelf";
|
|
1895
|
+
if (permitType === "sharing")
|
|
1896
|
+
return "PermissionedV2IssuerShared";
|
|
1897
|
+
if (permitType === "recipient")
|
|
1898
|
+
return "PermissionedV2Recipient";
|
|
1899
|
+
throw new Error(`Unknown permit type: ${permitType}`);
|
|
1900
|
+
}
|
|
1901
|
+
};
|
|
1902
|
+
var getAclAddress = async (publicClient) => {
|
|
1903
|
+
const ACL_IFACE = "function acl() view returns (address)";
|
|
1904
|
+
const aclAbi = viem.parseAbi([ACL_IFACE]);
|
|
1905
|
+
return await publicClient.readContract({
|
|
1906
|
+
address: TASK_MANAGER_ADDRESS,
|
|
1907
|
+
abi: aclAbi,
|
|
1908
|
+
functionName: "acl"
|
|
1909
|
+
});
|
|
1910
|
+
};
|
|
1911
|
+
var getAclEIP712Domain = async (publicClient) => {
|
|
1912
|
+
const aclAddress = await getAclAddress(publicClient);
|
|
1913
|
+
const EIP712_DOMAIN_IFACE = "function eip712Domain() public view returns (bytes1 fields, string name, string version, uint256 chainId, address verifyingContract, bytes32 salt, uint256[] extensions)";
|
|
1914
|
+
const domainAbi = viem.parseAbi([EIP712_DOMAIN_IFACE]);
|
|
1915
|
+
const domain = await publicClient.readContract({
|
|
1916
|
+
address: aclAddress,
|
|
1917
|
+
abi: domainAbi,
|
|
1918
|
+
functionName: "eip712Domain"
|
|
1919
|
+
});
|
|
1920
|
+
const [_fields, name, version, chainId, verifyingContract, _salt, _extensions] = domain;
|
|
1921
|
+
return {
|
|
1922
|
+
name,
|
|
1923
|
+
version,
|
|
1924
|
+
chainId: Number(chainId),
|
|
1925
|
+
verifyingContract
|
|
1926
|
+
};
|
|
1927
|
+
};
|
|
1928
|
+
var checkPermitValidityOnChain = async (permission, publicClient) => {
|
|
1929
|
+
const aclAddress = await getAclAddress(publicClient);
|
|
1930
|
+
try {
|
|
1931
|
+
await publicClient.simulateContract({
|
|
1932
|
+
address: aclAddress,
|
|
1933
|
+
abi: checkPermitValidityAbi,
|
|
1934
|
+
functionName: "checkPermitValidity",
|
|
1935
|
+
args: [
|
|
1936
|
+
{
|
|
1937
|
+
issuer: permission.issuer,
|
|
1938
|
+
expiration: BigInt(permission.expiration),
|
|
1939
|
+
recipient: permission.recipient,
|
|
1940
|
+
validatorId: BigInt(permission.validatorId),
|
|
1941
|
+
validatorContract: permission.validatorContract,
|
|
1942
|
+
sealingKey: permission.sealingKey,
|
|
1943
|
+
issuerSignature: permission.issuerSignature,
|
|
1944
|
+
recipientSignature: permission.recipientSignature
|
|
1945
|
+
}
|
|
1946
|
+
]
|
|
1947
|
+
});
|
|
1948
|
+
return true;
|
|
1949
|
+
} catch (err) {
|
|
1950
|
+
if (err instanceof viem.BaseError) {
|
|
1951
|
+
const revertError = err.walk((err2) => err2 instanceof viem.ContractFunctionRevertedError);
|
|
1952
|
+
if (revertError instanceof viem.ContractFunctionRevertedError) {
|
|
1953
|
+
const errorName = revertError.data?.errorName ?? "";
|
|
1954
|
+
throw new Error(errorName);
|
|
1955
|
+
}
|
|
1956
|
+
}
|
|
1957
|
+
const customErrorName = extractCustomErrorFromDetails(err, checkPermitValidityAbi);
|
|
1958
|
+
if (customErrorName) {
|
|
1959
|
+
throw new Error(customErrorName);
|
|
1960
|
+
}
|
|
1961
|
+
const hhDetailsData = extractReturnData(err);
|
|
1962
|
+
if (hhDetailsData != null) {
|
|
1963
|
+
const decoded = viem.decodeErrorResult({
|
|
1964
|
+
abi: checkPermitValidityAbi,
|
|
1965
|
+
data: hhDetailsData
|
|
1966
|
+
});
|
|
1967
|
+
throw new Error(decoded.errorName);
|
|
1968
|
+
}
|
|
1969
|
+
throw err;
|
|
1970
|
+
}
|
|
1971
|
+
};
|
|
1972
|
+
function extractCustomErrorFromDetails(err, abi) {
|
|
1973
|
+
const anyErr = err;
|
|
1974
|
+
const details = anyErr?.details ?? anyErr?.cause?.details;
|
|
1975
|
+
if (typeof details === "string") {
|
|
1976
|
+
const customErrorMatch = details.match(/reverted with custom error '(\w+)\(\)'/);
|
|
1977
|
+
if (customErrorMatch) {
|
|
1978
|
+
const errorName = customErrorMatch[1];
|
|
1979
|
+
const errorExists = abi.some((item) => item.type === "error" && item.name === errorName);
|
|
1980
|
+
if (errorExists) {
|
|
1981
|
+
return errorName;
|
|
1982
|
+
}
|
|
1983
|
+
}
|
|
1984
|
+
}
|
|
1985
|
+
return void 0;
|
|
1986
|
+
}
|
|
1987
|
+
function extractReturnData(err) {
|
|
1988
|
+
const anyErr = err;
|
|
1989
|
+
const s = anyErr?.details ?? anyErr?.cause?.details ?? anyErr?.shortMessage ?? anyErr?.message ?? String(err);
|
|
1990
|
+
return s.match(/return data:\s*(0x[a-fA-F0-9]+)/)?.[1];
|
|
1991
|
+
}
|
|
1992
|
+
var checkPermitValidityAbi = [
|
|
1993
|
+
{
|
|
1994
|
+
type: "function",
|
|
1995
|
+
name: "checkPermitValidity",
|
|
1996
|
+
inputs: [
|
|
1997
|
+
{
|
|
1998
|
+
name: "permission",
|
|
1999
|
+
type: "tuple",
|
|
2000
|
+
internalType: "struct Permission",
|
|
2001
|
+
components: [
|
|
2002
|
+
{
|
|
2003
|
+
name: "issuer",
|
|
2004
|
+
type: "address",
|
|
2005
|
+
internalType: "address"
|
|
2006
|
+
},
|
|
2007
|
+
{
|
|
2008
|
+
name: "expiration",
|
|
2009
|
+
type: "uint64",
|
|
2010
|
+
internalType: "uint64"
|
|
2011
|
+
},
|
|
2012
|
+
{
|
|
2013
|
+
name: "recipient",
|
|
2014
|
+
type: "address",
|
|
2015
|
+
internalType: "address"
|
|
2016
|
+
},
|
|
2017
|
+
{
|
|
2018
|
+
name: "validatorId",
|
|
2019
|
+
type: "uint256",
|
|
2020
|
+
internalType: "uint256"
|
|
2021
|
+
},
|
|
2022
|
+
{
|
|
2023
|
+
name: "validatorContract",
|
|
2024
|
+
type: "address",
|
|
2025
|
+
internalType: "address"
|
|
2026
|
+
},
|
|
2027
|
+
{
|
|
2028
|
+
name: "sealingKey",
|
|
2029
|
+
type: "bytes32",
|
|
2030
|
+
internalType: "bytes32"
|
|
2031
|
+
},
|
|
2032
|
+
{
|
|
2033
|
+
name: "issuerSignature",
|
|
2034
|
+
type: "bytes",
|
|
2035
|
+
internalType: "bytes"
|
|
2036
|
+
},
|
|
2037
|
+
{
|
|
2038
|
+
name: "recipientSignature",
|
|
2039
|
+
type: "bytes",
|
|
2040
|
+
internalType: "bytes"
|
|
2041
|
+
}
|
|
2042
|
+
]
|
|
2043
|
+
}
|
|
2044
|
+
],
|
|
2045
|
+
outputs: [
|
|
2046
|
+
{
|
|
2047
|
+
name: "",
|
|
2048
|
+
type: "bool",
|
|
2049
|
+
internalType: "bool"
|
|
2050
|
+
}
|
|
2051
|
+
],
|
|
2052
|
+
stateMutability: "view"
|
|
2053
|
+
},
|
|
2054
|
+
{
|
|
2055
|
+
type: "error",
|
|
2056
|
+
name: "PermissionInvalid_Disabled",
|
|
2057
|
+
inputs: []
|
|
2058
|
+
},
|
|
2059
|
+
{
|
|
2060
|
+
type: "error",
|
|
2061
|
+
name: "PermissionInvalid_Expired",
|
|
2062
|
+
inputs: []
|
|
2063
|
+
},
|
|
2064
|
+
{
|
|
2065
|
+
type: "error",
|
|
2066
|
+
name: "PermissionInvalid_IssuerSignature",
|
|
2067
|
+
inputs: []
|
|
2068
|
+
},
|
|
2069
|
+
{
|
|
2070
|
+
type: "error",
|
|
2071
|
+
name: "PermissionInvalid_RecipientSignature",
|
|
2072
|
+
inputs: []
|
|
2073
|
+
}
|
|
2074
|
+
];
|
|
2075
|
+
|
|
2076
|
+
// permits/permit.ts
|
|
2077
|
+
var PermitUtils = {
|
|
2078
|
+
/**
|
|
2079
|
+
* Create a self permit for personal use
|
|
2080
|
+
*/
|
|
2081
|
+
createSelf: (options) => {
|
|
2082
|
+
const validation = validateSelfPermitOptions(options);
|
|
2083
|
+
const sealingPair = GenerateSealingKey();
|
|
2084
|
+
const permit = {
|
|
2085
|
+
hash: PermitUtils.getHash(validation),
|
|
2086
|
+
...validation,
|
|
2087
|
+
sealingPair,
|
|
2088
|
+
_signedDomain: void 0
|
|
2089
|
+
};
|
|
2090
|
+
return permit;
|
|
2091
|
+
},
|
|
2092
|
+
/**
|
|
2093
|
+
* Create a sharing permit to be shared with another user
|
|
2094
|
+
*/
|
|
2095
|
+
createSharing: (options) => {
|
|
2096
|
+
const validation = validateSharingPermitOptions(options);
|
|
2097
|
+
const sealingPair = GenerateSealingKey();
|
|
2098
|
+
const permit = {
|
|
2099
|
+
hash: PermitUtils.getHash(validation),
|
|
2100
|
+
...validation,
|
|
2101
|
+
sealingPair,
|
|
2102
|
+
_signedDomain: void 0
|
|
2103
|
+
};
|
|
2104
|
+
return permit;
|
|
2105
|
+
},
|
|
2106
|
+
/**
|
|
2107
|
+
* Import a shared permit from various input formats
|
|
2108
|
+
*/
|
|
2109
|
+
importShared: (options) => {
|
|
2110
|
+
let parsedOptions;
|
|
2111
|
+
if (typeof options === "string") {
|
|
2112
|
+
try {
|
|
2113
|
+
parsedOptions = JSON.parse(options);
|
|
2114
|
+
} catch (error) {
|
|
2115
|
+
throw new Error(`Failed to parse JSON string: ${error}`);
|
|
2116
|
+
}
|
|
2117
|
+
} else if (typeof options === "object" && options !== null) {
|
|
2118
|
+
parsedOptions = options;
|
|
2119
|
+
} else {
|
|
2120
|
+
throw new Error("Invalid input type, expected ImportSharedPermitOptions, object, or string");
|
|
2121
|
+
}
|
|
2122
|
+
if (parsedOptions.type != null && parsedOptions.type !== "sharing") {
|
|
2123
|
+
throw new Error(`Invalid permit type <${parsedOptions.type}>, must be "sharing"`);
|
|
2124
|
+
}
|
|
2125
|
+
const validation = validateImportPermitOptions({ ...parsedOptions, type: "recipient" });
|
|
2126
|
+
const sealingPair = GenerateSealingKey();
|
|
2127
|
+
const permit = {
|
|
2128
|
+
hash: PermitUtils.getHash(validation),
|
|
2129
|
+
...validation,
|
|
2130
|
+
sealingPair,
|
|
2131
|
+
_signedDomain: void 0
|
|
2132
|
+
};
|
|
2133
|
+
return permit;
|
|
2134
|
+
},
|
|
2135
|
+
/**
|
|
2136
|
+
* Sign a permit with the provided wallet client
|
|
2137
|
+
*/
|
|
2138
|
+
sign: async (permit, publicClient, walletClient) => {
|
|
2139
|
+
if (walletClient == null || walletClient.account == null) {
|
|
2140
|
+
throw new Error(
|
|
2141
|
+
"Missing walletClient, you must pass in a `walletClient` for the connected user to create a permit signature"
|
|
2142
|
+
);
|
|
2143
|
+
}
|
|
2144
|
+
const primaryType = SignatureUtils.getPrimaryType(permit.type);
|
|
2145
|
+
const domain = await getAclEIP712Domain(publicClient);
|
|
2146
|
+
const { types, message } = SignatureUtils.getSignatureParams(PermitUtils.getPermission(permit, true), primaryType);
|
|
2147
|
+
const signature = await walletClient.signTypedData({
|
|
2148
|
+
domain,
|
|
2149
|
+
types,
|
|
2150
|
+
primaryType,
|
|
2151
|
+
message,
|
|
2152
|
+
account: walletClient.account
|
|
2153
|
+
});
|
|
2154
|
+
let updatedPermit;
|
|
2155
|
+
if (permit.type === "self" || permit.type === "sharing") {
|
|
2156
|
+
updatedPermit = {
|
|
2157
|
+
...permit,
|
|
2158
|
+
issuerSignature: signature,
|
|
2159
|
+
_signedDomain: domain
|
|
2160
|
+
};
|
|
2161
|
+
} else {
|
|
2162
|
+
updatedPermit = {
|
|
2163
|
+
...permit,
|
|
2164
|
+
recipientSignature: signature,
|
|
2165
|
+
_signedDomain: domain
|
|
2166
|
+
};
|
|
2167
|
+
}
|
|
2168
|
+
return updatedPermit;
|
|
2169
|
+
},
|
|
2170
|
+
/**
|
|
2171
|
+
* Create and sign a self permit in one operation
|
|
2172
|
+
*/
|
|
2173
|
+
createSelfAndSign: async (options, publicClient, walletClient) => {
|
|
2174
|
+
const permit = PermitUtils.createSelf(options);
|
|
2175
|
+
return PermitUtils.sign(permit, publicClient, walletClient);
|
|
2176
|
+
},
|
|
2177
|
+
/**
|
|
2178
|
+
* Create and sign a sharing permit in one operation
|
|
2179
|
+
*/
|
|
2180
|
+
createSharingAndSign: async (options, publicClient, walletClient) => {
|
|
2181
|
+
const permit = PermitUtils.createSharing(options);
|
|
2182
|
+
return PermitUtils.sign(permit, publicClient, walletClient);
|
|
2183
|
+
},
|
|
2184
|
+
/**
|
|
2185
|
+
* Import and sign a shared permit in one operation from various input formats
|
|
2186
|
+
*/
|
|
2187
|
+
importSharedAndSign: async (options, publicClient, walletClient) => {
|
|
2188
|
+
const permit = PermitUtils.importShared(options);
|
|
2189
|
+
return PermitUtils.sign(permit, publicClient, walletClient);
|
|
2190
|
+
},
|
|
2191
|
+
/**
|
|
2192
|
+
* Deserialize a permit from serialized data
|
|
2193
|
+
*/
|
|
2194
|
+
deserialize: (data) => {
|
|
2195
|
+
return {
|
|
2196
|
+
...data,
|
|
2197
|
+
sealingPair: SealingKey.deserialize(data.sealingPair.privateKey, data.sealingPair.publicKey)
|
|
2198
|
+
};
|
|
2199
|
+
},
|
|
2200
|
+
/**
|
|
2201
|
+
* Serialize a permit for storage
|
|
2202
|
+
*/
|
|
2203
|
+
serialize: (permit) => {
|
|
2204
|
+
return {
|
|
2205
|
+
hash: permit.hash,
|
|
2206
|
+
name: permit.name,
|
|
2207
|
+
type: permit.type,
|
|
2208
|
+
issuer: permit.issuer,
|
|
2209
|
+
expiration: permit.expiration,
|
|
2210
|
+
recipient: permit.recipient,
|
|
2211
|
+
validatorId: permit.validatorId,
|
|
2212
|
+
validatorContract: permit.validatorContract,
|
|
2213
|
+
issuerSignature: permit.issuerSignature,
|
|
2214
|
+
recipientSignature: permit.recipientSignature,
|
|
2215
|
+
_signedDomain: permit._signedDomain,
|
|
2216
|
+
sealingPair: permit.sealingPair.serialize()
|
|
2217
|
+
};
|
|
2218
|
+
},
|
|
2219
|
+
/**
|
|
2220
|
+
* Validate a permit (schema-level validation)
|
|
2221
|
+
*/
|
|
2222
|
+
validateSchema: (permit) => {
|
|
2223
|
+
if (permit.type === "self") {
|
|
2224
|
+
return validateSelfPermit(permit);
|
|
2225
|
+
} else if (permit.type === "sharing") {
|
|
2226
|
+
return validateSharingPermit(permit);
|
|
2227
|
+
} else if (permit.type === "recipient") {
|
|
2228
|
+
return validateImportPermit(permit);
|
|
2229
|
+
} else {
|
|
2230
|
+
throw new Error("Invalid permit type");
|
|
2231
|
+
}
|
|
2232
|
+
},
|
|
2233
|
+
/**
|
|
2234
|
+
* Validate a permit (holistic validation).
|
|
2235
|
+
*
|
|
2236
|
+
* This validates:
|
|
2237
|
+
* - Permit schema (shape + invariants)
|
|
2238
|
+
* - Permit is signed
|
|
2239
|
+
* - Permit is not expired
|
|
2240
|
+
*
|
|
2241
|
+
* For schema-only validation, use `validateSchema(permit)`.
|
|
2242
|
+
*/
|
|
2243
|
+
validate: (permit) => {
|
|
2244
|
+
const validated = PermitUtils.validateSchema(permit);
|
|
2245
|
+
ValidationUtils.assertSignedAndNotExpired(validated);
|
|
2246
|
+
return validated;
|
|
2247
|
+
},
|
|
2248
|
+
/**
|
|
2249
|
+
* Get the permission object from a permit (for use in contracts)
|
|
2250
|
+
*/
|
|
2251
|
+
getPermission: (permit, skipValidation = false) => {
|
|
2252
|
+
if (!skipValidation) {
|
|
2253
|
+
PermitUtils.validateSchema(permit);
|
|
2254
|
+
}
|
|
2255
|
+
return {
|
|
2256
|
+
issuer: permit.issuer,
|
|
2257
|
+
expiration: permit.expiration,
|
|
2258
|
+
recipient: permit.recipient,
|
|
2259
|
+
validatorId: permit.validatorId,
|
|
2260
|
+
validatorContract: permit.validatorContract,
|
|
2261
|
+
sealingKey: `0x${permit.sealingPair.publicKey}`,
|
|
2262
|
+
issuerSignature: permit.issuerSignature,
|
|
2263
|
+
recipientSignature: permit.recipientSignature
|
|
2264
|
+
};
|
|
2265
|
+
},
|
|
2266
|
+
/**
|
|
2267
|
+
* Get a stable hash for the permit (used as key in storage)
|
|
2268
|
+
*/
|
|
2269
|
+
getHash: (permit) => {
|
|
2270
|
+
const data = JSON.stringify({
|
|
2271
|
+
type: permit.type,
|
|
2272
|
+
issuer: permit.issuer,
|
|
2273
|
+
expiration: permit.expiration,
|
|
2274
|
+
recipient: permit.recipient,
|
|
2275
|
+
validatorId: permit.validatorId,
|
|
2276
|
+
validatorContract: permit.validatorContract
|
|
2277
|
+
});
|
|
2278
|
+
return viem.keccak256(viem.toHex(data));
|
|
2279
|
+
},
|
|
2280
|
+
/**
|
|
2281
|
+
* Export permit data for sharing (removes sensitive fields)
|
|
2282
|
+
*/
|
|
2283
|
+
export: (permit) => {
|
|
2284
|
+
const cleanedPermit = {
|
|
2285
|
+
name: permit.name,
|
|
2286
|
+
type: permit.type,
|
|
2287
|
+
issuer: permit.issuer,
|
|
2288
|
+
expiration: permit.expiration
|
|
2289
|
+
};
|
|
2290
|
+
if (permit.recipient !== viem.zeroAddress)
|
|
2291
|
+
cleanedPermit.recipient = permit.recipient;
|
|
2292
|
+
if (permit.validatorId !== 0)
|
|
2293
|
+
cleanedPermit.validatorId = permit.validatorId;
|
|
2294
|
+
if (permit.validatorContract !== viem.zeroAddress)
|
|
2295
|
+
cleanedPermit.validatorContract = permit.validatorContract;
|
|
2296
|
+
if (permit.type === "sharing" && permit.issuerSignature !== "0x")
|
|
2297
|
+
cleanedPermit.issuerSignature = permit.issuerSignature;
|
|
2298
|
+
return JSON.stringify(cleanedPermit, void 0, 2);
|
|
2299
|
+
},
|
|
2300
|
+
/**
|
|
2301
|
+
* Unseal encrypted data using the permit's sealing key
|
|
2302
|
+
*/
|
|
2303
|
+
unseal: (permit, ciphertext) => {
|
|
2304
|
+
return permit.sealingPair.unseal(ciphertext);
|
|
2305
|
+
},
|
|
2306
|
+
/**
|
|
2307
|
+
* Check if permit is expired
|
|
2308
|
+
*/
|
|
2309
|
+
isExpired: (permit) => {
|
|
2310
|
+
return ValidationUtils.isExpired(permit);
|
|
2311
|
+
},
|
|
2312
|
+
/**
|
|
2313
|
+
* Check if permit is signed
|
|
2314
|
+
*/
|
|
2315
|
+
isSigned: (permit) => {
|
|
2316
|
+
return ValidationUtils.isSigned(permit);
|
|
2317
|
+
},
|
|
2318
|
+
/**
|
|
2319
|
+
* Check if permit is signed and not expired
|
|
2320
|
+
*/
|
|
2321
|
+
isSignedAndNotExpired: (permit) => {
|
|
2322
|
+
return ValidationUtils.isSignedAndNotExpired(permit);
|
|
2323
|
+
},
|
|
2324
|
+
/**
|
|
2325
|
+
* Assert that permit is signed and not expired
|
|
2326
|
+
*/
|
|
2327
|
+
assertSignedAndNotExpired: (permit) => {
|
|
2328
|
+
return ValidationUtils.assertSignedAndNotExpired(permit);
|
|
2329
|
+
},
|
|
2330
|
+
isValid: (permit) => {
|
|
2331
|
+
return ValidationUtils.isValid(permit);
|
|
2332
|
+
},
|
|
2333
|
+
/**
|
|
2334
|
+
* Update permit name (returns new permit instance)
|
|
2335
|
+
*/
|
|
2336
|
+
updateName: (permit, name) => {
|
|
2337
|
+
return { ...permit, name };
|
|
2338
|
+
},
|
|
2339
|
+
/**
|
|
2340
|
+
* Fetch EIP712 domain from the blockchain
|
|
2341
|
+
*/
|
|
2342
|
+
fetchEIP712Domain: async (publicClient) => {
|
|
2343
|
+
return getAclEIP712Domain(publicClient);
|
|
2344
|
+
},
|
|
2345
|
+
/**
|
|
2346
|
+
* Check if permit's signed domain matches the provided domain
|
|
2347
|
+
*/
|
|
2348
|
+
matchesDomain: (permit, domain) => {
|
|
2349
|
+
return permit._signedDomain?.name === domain.name && permit._signedDomain?.version === domain.version && permit._signedDomain?.verifyingContract === domain.verifyingContract && permit._signedDomain?.chainId === domain.chainId;
|
|
2350
|
+
},
|
|
2351
|
+
/**
|
|
2352
|
+
* Check if permit's signed domain is valid for the current chain
|
|
2353
|
+
*/
|
|
2354
|
+
checkSignedDomainValid: async (permit, publicClient) => {
|
|
2355
|
+
if (permit._signedDomain == null)
|
|
2356
|
+
return false;
|
|
2357
|
+
const domain = await getAclEIP712Domain(publicClient);
|
|
2358
|
+
return PermitUtils.matchesDomain(permit, domain);
|
|
2359
|
+
},
|
|
2360
|
+
/**
|
|
2361
|
+
* Check if permit passes the on-chain validation
|
|
2362
|
+
*/
|
|
2363
|
+
checkValidityOnChain: async (permit, publicClient) => {
|
|
2364
|
+
const permission = PermitUtils.getPermission(permit);
|
|
2365
|
+
return checkPermitValidityOnChain(permission, publicClient);
|
|
2366
|
+
}
|
|
2367
|
+
};
|
|
2368
|
+
var PERMIT_STORE_DEFAULTS = {
|
|
2369
|
+
permits: {},
|
|
2370
|
+
activePermitHash: {}
|
|
2371
|
+
};
|
|
2372
|
+
var _permitStore = vanilla.createStore()(
|
|
2373
|
+
middleware.persist(() => PERMIT_STORE_DEFAULTS, { name: "cofhesdk-permits" })
|
|
2374
|
+
);
|
|
2375
|
+
var clearStaleStore = () => {
|
|
2376
|
+
const state = _permitStore.getState();
|
|
2377
|
+
const hasExpectedStructure = state && typeof state === "object" && "permits" in state && "activePermitHash" in state && typeof state.permits === "object" && typeof state.activePermitHash === "object";
|
|
2378
|
+
if (hasExpectedStructure)
|
|
2379
|
+
return;
|
|
2380
|
+
_permitStore.setState({ permits: {}, activePermitHash: {} });
|
|
2381
|
+
};
|
|
2382
|
+
var getPermit = (chainId, account, hash) => {
|
|
2383
|
+
clearStaleStore();
|
|
2384
|
+
if (chainId == null || account == null || hash == null)
|
|
2385
|
+
return;
|
|
2386
|
+
const savedPermit = _permitStore.getState().permits[chainId]?.[account]?.[hash];
|
|
2387
|
+
if (savedPermit == null)
|
|
2388
|
+
return;
|
|
2389
|
+
return PermitUtils.deserialize(savedPermit);
|
|
2390
|
+
};
|
|
2391
|
+
var getActivePermit = (chainId, account) => {
|
|
2392
|
+
clearStaleStore();
|
|
2393
|
+
if (chainId == null || account == null)
|
|
2394
|
+
return;
|
|
2395
|
+
const activePermitHash = _permitStore.getState().activePermitHash[chainId]?.[account];
|
|
2396
|
+
return getPermit(chainId, account, activePermitHash);
|
|
2397
|
+
};
|
|
2398
|
+
var getPermits = (chainId, account) => {
|
|
2399
|
+
clearStaleStore();
|
|
2400
|
+
if (chainId == null || account == null)
|
|
2401
|
+
return {};
|
|
2402
|
+
return Object.entries(_permitStore.getState().permits[chainId]?.[account] ?? {}).reduce(
|
|
2403
|
+
(acc, [hash, permit]) => {
|
|
2404
|
+
if (permit == void 0)
|
|
2405
|
+
return acc;
|
|
2406
|
+
return { ...acc, [hash]: PermitUtils.deserialize(permit) };
|
|
2407
|
+
},
|
|
2408
|
+
{}
|
|
2409
|
+
);
|
|
2410
|
+
};
|
|
2411
|
+
var setPermit = (chainId, account, permit) => {
|
|
2412
|
+
clearStaleStore();
|
|
2413
|
+
_permitStore.setState(
|
|
2414
|
+
immer.produce((state) => {
|
|
2415
|
+
if (state.permits[chainId] == null)
|
|
2416
|
+
state.permits[chainId] = {};
|
|
2417
|
+
if (state.permits[chainId][account] == null)
|
|
2418
|
+
state.permits[chainId][account] = {};
|
|
2419
|
+
state.permits[chainId][account][permit.hash] = PermitUtils.serialize(permit);
|
|
2420
|
+
})
|
|
2421
|
+
);
|
|
2422
|
+
};
|
|
2423
|
+
var removePermit = (chainId, account, hash) => {
|
|
2424
|
+
clearStaleStore();
|
|
2425
|
+
_permitStore.setState(
|
|
2426
|
+
immer.produce((state) => {
|
|
2427
|
+
if (state.permits[chainId] == null)
|
|
2428
|
+
state.permits[chainId] = {};
|
|
2429
|
+
if (state.activePermitHash[chainId] == null)
|
|
2430
|
+
state.activePermitHash[chainId] = {};
|
|
2431
|
+
const accountPermits = state.permits[chainId][account];
|
|
2432
|
+
if (accountPermits == null)
|
|
2433
|
+
return;
|
|
2434
|
+
if (accountPermits[hash] == null)
|
|
2435
|
+
return;
|
|
2436
|
+
if (state.activePermitHash[chainId][account] === hash) {
|
|
2437
|
+
state.activePermitHash[chainId][account] = void 0;
|
|
2438
|
+
}
|
|
2439
|
+
accountPermits[hash] = void 0;
|
|
2440
|
+
})
|
|
2441
|
+
);
|
|
2442
|
+
};
|
|
2443
|
+
var getActivePermitHash = (chainId, account) => {
|
|
2444
|
+
clearStaleStore();
|
|
2445
|
+
if (chainId == null || account == null)
|
|
2446
|
+
return void 0;
|
|
2447
|
+
return _permitStore.getState().activePermitHash[chainId]?.[account];
|
|
2448
|
+
};
|
|
2449
|
+
var setActivePermitHash = (chainId, account, hash) => {
|
|
2450
|
+
clearStaleStore();
|
|
2451
|
+
_permitStore.setState(
|
|
2452
|
+
immer.produce((state) => {
|
|
2453
|
+
if (state.activePermitHash[chainId] == null)
|
|
2454
|
+
state.activePermitHash[chainId] = {};
|
|
2455
|
+
state.activePermitHash[chainId][account] = hash;
|
|
2456
|
+
})
|
|
2457
|
+
);
|
|
2458
|
+
};
|
|
2459
|
+
var removeActivePermitHash = (chainId, account) => {
|
|
2460
|
+
clearStaleStore();
|
|
2461
|
+
_permitStore.setState(
|
|
2462
|
+
immer.produce((state) => {
|
|
2463
|
+
if (state.activePermitHash[chainId])
|
|
2464
|
+
state.activePermitHash[chainId][account] = void 0;
|
|
2465
|
+
})
|
|
2466
|
+
);
|
|
2467
|
+
};
|
|
2468
|
+
var resetStore = () => {
|
|
2469
|
+
clearStaleStore();
|
|
2470
|
+
_permitStore.setState({ permits: {}, activePermitHash: {} });
|
|
2471
|
+
};
|
|
2472
|
+
var permitStore = {
|
|
2473
|
+
store: _permitStore,
|
|
2474
|
+
getPermit,
|
|
2475
|
+
getActivePermit,
|
|
2476
|
+
getPermits,
|
|
2477
|
+
setPermit,
|
|
2478
|
+
removePermit,
|
|
2479
|
+
getActivePermitHash,
|
|
2480
|
+
setActivePermitHash,
|
|
2481
|
+
removeActivePermitHash,
|
|
2482
|
+
resetStore
|
|
2483
|
+
};
|
|
2484
|
+
var storeActivePermit = async (permit, publicClient, walletClient) => {
|
|
2485
|
+
const chainId = await publicClient.getChainId();
|
|
2486
|
+
const account = walletClient.account.address;
|
|
2487
|
+
permitStore.setPermit(chainId, account, permit);
|
|
2488
|
+
permitStore.setActivePermitHash(chainId, account, permit.hash);
|
|
2489
|
+
};
|
|
2490
|
+
var createPermitWithSign = async (options, publicClient, walletClient, permitMethod) => {
|
|
2491
|
+
const permit = await permitMethod(options, publicClient, walletClient);
|
|
2492
|
+
await storeActivePermit(permit, publicClient, walletClient);
|
|
2493
|
+
return permit;
|
|
2494
|
+
};
|
|
2495
|
+
var createSelf = async (options, publicClient, walletClient) => {
|
|
2496
|
+
return createPermitWithSign(options, publicClient, walletClient, PermitUtils.createSelfAndSign);
|
|
2497
|
+
};
|
|
2498
|
+
var createSharing = async (options, publicClient, walletClient) => {
|
|
2499
|
+
return createPermitWithSign(options, publicClient, walletClient, PermitUtils.createSharingAndSign);
|
|
2500
|
+
};
|
|
2501
|
+
var importShared = async (options, publicClient, walletClient) => {
|
|
2502
|
+
return createPermitWithSign(options, publicClient, walletClient, PermitUtils.importSharedAndSign);
|
|
2503
|
+
};
|
|
2504
|
+
var getHash = (permit) => {
|
|
2505
|
+
return PermitUtils.getHash(permit);
|
|
2506
|
+
};
|
|
2507
|
+
var serialize = (permit) => {
|
|
2508
|
+
return PermitUtils.serialize(permit);
|
|
2509
|
+
};
|
|
2510
|
+
var deserialize = (serialized) => {
|
|
2511
|
+
return PermitUtils.deserialize(serialized);
|
|
2512
|
+
};
|
|
2513
|
+
var getPermit2 = (chainId, account, hash) => {
|
|
2514
|
+
return permitStore.getPermit(chainId, account, hash);
|
|
2515
|
+
};
|
|
2516
|
+
var getPermits2 = (chainId, account) => {
|
|
2517
|
+
return permitStore.getPermits(chainId, account);
|
|
2518
|
+
};
|
|
2519
|
+
var getActivePermit2 = (chainId, account) => {
|
|
2520
|
+
return permitStore.getActivePermit(chainId, account);
|
|
2521
|
+
};
|
|
2522
|
+
var getActivePermitHash2 = (chainId, account) => {
|
|
2523
|
+
return permitStore.getActivePermitHash(chainId, account);
|
|
2524
|
+
};
|
|
2525
|
+
var selectActivePermit = (chainId, account, hash) => {
|
|
2526
|
+
permitStore.setActivePermitHash(chainId, account, hash);
|
|
2527
|
+
};
|
|
2528
|
+
var getOrCreateSelfPermit = async (publicClient, walletClient, chainId, account, options) => {
|
|
2529
|
+
const _chainId = chainId ?? await publicClient.getChainId();
|
|
2530
|
+
const _account = account ?? walletClient.account.address;
|
|
2531
|
+
const activePermit = await getActivePermit2(_chainId, _account);
|
|
2532
|
+
if (activePermit && activePermit.type === "self") {
|
|
2533
|
+
return activePermit;
|
|
2534
|
+
}
|
|
2535
|
+
return createSelf(options ?? { issuer: _account, name: "Autogenerated Self Permit" }, publicClient, walletClient);
|
|
2536
|
+
};
|
|
2537
|
+
var getOrCreateSharingPermit = async (publicClient, walletClient, options, chainId, account) => {
|
|
2538
|
+
const _chainId = chainId ?? await publicClient.getChainId();
|
|
2539
|
+
const _account = account ?? walletClient.account.address;
|
|
2540
|
+
const activePermit = await getActivePermit2(_chainId, _account);
|
|
2541
|
+
if (activePermit && activePermit.type === "sharing") {
|
|
2542
|
+
return activePermit;
|
|
2543
|
+
}
|
|
2544
|
+
return createSharing(options, publicClient, walletClient);
|
|
2545
|
+
};
|
|
2546
|
+
var removePermit2 = async (chainId, account, hash) => permitStore.removePermit(chainId, account, hash);
|
|
2547
|
+
var removeActivePermit = async (chainId, account) => permitStore.removeActivePermitHash(chainId, account);
|
|
2548
|
+
var permits = {
|
|
2549
|
+
getSnapshot: permitStore.store.getState,
|
|
2550
|
+
subscribe: permitStore.store.subscribe,
|
|
2551
|
+
createSelf,
|
|
2552
|
+
createSharing,
|
|
2553
|
+
importShared,
|
|
2554
|
+
getOrCreateSelfPermit,
|
|
2555
|
+
getOrCreateSharingPermit,
|
|
2556
|
+
getHash,
|
|
2557
|
+
serialize,
|
|
2558
|
+
deserialize,
|
|
2559
|
+
getPermit: getPermit2,
|
|
2560
|
+
getPermits: getPermits2,
|
|
2561
|
+
getActivePermit: getActivePermit2,
|
|
2562
|
+
getActivePermitHash: getActivePermitHash2,
|
|
2563
|
+
removePermit: removePermit2,
|
|
2564
|
+
selectActivePermit,
|
|
2565
|
+
removeActivePermit
|
|
2566
|
+
};
|
|
2567
|
+
function uint160ToAddress(uint160) {
|
|
2568
|
+
const hexStr = uint160.toString(16).padStart(40, "0");
|
|
2569
|
+
return viem.getAddress("0x" + hexStr);
|
|
2570
|
+
}
|
|
2571
|
+
var isValidUtype = (utype) => {
|
|
2572
|
+
return utype === 0 /* Bool */ || utype === 7 /* Uint160 */ || utype == null || FheUintUTypes.includes(utype);
|
|
2573
|
+
};
|
|
2574
|
+
var convertViaUtype = (utype, value) => {
|
|
2575
|
+
if (utype === 0 /* Bool */) {
|
|
2576
|
+
return !!value;
|
|
2577
|
+
} else if (utype === 7 /* Uint160 */) {
|
|
2578
|
+
return uint160ToAddress(value);
|
|
2579
|
+
} else if (utype == null || FheUintUTypes.includes(utype)) {
|
|
2580
|
+
return value;
|
|
2581
|
+
} else {
|
|
2582
|
+
throw new Error(`convertViaUtype :: invalid utype :: ${utype}`);
|
|
2583
|
+
}
|
|
2584
|
+
};
|
|
2585
|
+
|
|
2586
|
+
// core/decrypt/MockThresholdNetworkAbi.ts
|
|
2587
|
+
var MockThresholdNetworkAbi = [
|
|
2588
|
+
{
|
|
2589
|
+
type: "function",
|
|
2590
|
+
name: "acl",
|
|
2591
|
+
inputs: [],
|
|
2592
|
+
outputs: [{ name: "", type: "address", internalType: "contract ACL" }],
|
|
2593
|
+
stateMutability: "view"
|
|
2594
|
+
},
|
|
2595
|
+
{
|
|
2596
|
+
type: "function",
|
|
2597
|
+
name: "decodeLowLevelReversion",
|
|
2598
|
+
inputs: [{ name: "data", type: "bytes", internalType: "bytes" }],
|
|
2599
|
+
outputs: [{ name: "error", type: "string", internalType: "string" }],
|
|
2600
|
+
stateMutability: "pure"
|
|
2601
|
+
},
|
|
2602
|
+
{
|
|
2603
|
+
type: "function",
|
|
2604
|
+
name: "exists",
|
|
2605
|
+
inputs: [],
|
|
2606
|
+
outputs: [{ name: "", type: "bool", internalType: "bool" }],
|
|
2607
|
+
stateMutability: "pure"
|
|
2608
|
+
},
|
|
2609
|
+
{
|
|
2610
|
+
type: "function",
|
|
2611
|
+
name: "initialize",
|
|
2612
|
+
inputs: [
|
|
2613
|
+
{ name: "_taskManager", type: "address", internalType: "address" },
|
|
2614
|
+
{ name: "_acl", type: "address", internalType: "address" }
|
|
2615
|
+
],
|
|
2616
|
+
outputs: [],
|
|
2617
|
+
stateMutability: "nonpayable"
|
|
2618
|
+
},
|
|
2619
|
+
{
|
|
2620
|
+
type: "function",
|
|
2621
|
+
name: "queryDecrypt",
|
|
2622
|
+
inputs: [
|
|
2623
|
+
{ name: "ctHash", type: "uint256", internalType: "uint256" },
|
|
2624
|
+
{ name: "", type: "uint256", internalType: "uint256" },
|
|
2625
|
+
{
|
|
2626
|
+
name: "permission",
|
|
2627
|
+
type: "tuple",
|
|
2628
|
+
internalType: "struct Permission",
|
|
2629
|
+
components: [
|
|
2630
|
+
{ name: "issuer", type: "address", internalType: "address" },
|
|
2631
|
+
{ name: "expiration", type: "uint64", internalType: "uint64" },
|
|
2632
|
+
{ name: "recipient", type: "address", internalType: "address" },
|
|
2633
|
+
{ name: "validatorId", type: "uint256", internalType: "uint256" },
|
|
2634
|
+
{ name: "validatorContract", type: "address", internalType: "address" },
|
|
2635
|
+
{ name: "sealingKey", type: "bytes32", internalType: "bytes32" },
|
|
2636
|
+
{ name: "issuerSignature", type: "bytes", internalType: "bytes" },
|
|
2637
|
+
{ name: "recipientSignature", type: "bytes", internalType: "bytes" }
|
|
2638
|
+
]
|
|
2639
|
+
}
|
|
2640
|
+
],
|
|
2641
|
+
outputs: [
|
|
2642
|
+
{ name: "allowed", type: "bool", internalType: "bool" },
|
|
2643
|
+
{ name: "error", type: "string", internalType: "string" },
|
|
2644
|
+
{ name: "", type: "uint256", internalType: "uint256" }
|
|
2645
|
+
],
|
|
2646
|
+
stateMutability: "view"
|
|
2647
|
+
},
|
|
2648
|
+
{
|
|
2649
|
+
type: "function",
|
|
2650
|
+
name: "querySealOutput",
|
|
2651
|
+
inputs: [
|
|
2652
|
+
{ name: "ctHash", type: "uint256", internalType: "uint256" },
|
|
2653
|
+
{ name: "", type: "uint256", internalType: "uint256" },
|
|
2654
|
+
{
|
|
2655
|
+
name: "permission",
|
|
2656
|
+
type: "tuple",
|
|
2657
|
+
internalType: "struct Permission",
|
|
2658
|
+
components: [
|
|
2659
|
+
{ name: "issuer", type: "address", internalType: "address" },
|
|
2660
|
+
{ name: "expiration", type: "uint64", internalType: "uint64" },
|
|
2661
|
+
{ name: "recipient", type: "address", internalType: "address" },
|
|
2662
|
+
{ name: "validatorId", type: "uint256", internalType: "uint256" },
|
|
2663
|
+
{ name: "validatorContract", type: "address", internalType: "address" },
|
|
2664
|
+
{ name: "sealingKey", type: "bytes32", internalType: "bytes32" },
|
|
2665
|
+
{ name: "issuerSignature", type: "bytes", internalType: "bytes" },
|
|
2666
|
+
{ name: "recipientSignature", type: "bytes", internalType: "bytes" }
|
|
2667
|
+
]
|
|
2668
|
+
}
|
|
2669
|
+
],
|
|
2670
|
+
outputs: [
|
|
2671
|
+
{ name: "allowed", type: "bool", internalType: "bool" },
|
|
2672
|
+
{ name: "error", type: "string", internalType: "string" },
|
|
2673
|
+
{ name: "", type: "bytes32", internalType: "bytes32" }
|
|
2674
|
+
],
|
|
2675
|
+
stateMutability: "view"
|
|
2676
|
+
},
|
|
2677
|
+
{
|
|
2678
|
+
type: "function",
|
|
2679
|
+
name: "seal",
|
|
2680
|
+
inputs: [
|
|
2681
|
+
{ name: "input", type: "uint256", internalType: "uint256" },
|
|
2682
|
+
{ name: "key", type: "bytes32", internalType: "bytes32" }
|
|
2683
|
+
],
|
|
2684
|
+
outputs: [{ name: "", type: "bytes32", internalType: "bytes32" }],
|
|
2685
|
+
stateMutability: "pure"
|
|
2686
|
+
},
|
|
2687
|
+
{
|
|
2688
|
+
type: "function",
|
|
2689
|
+
name: "unseal",
|
|
2690
|
+
inputs: [
|
|
2691
|
+
{ name: "hashed", type: "bytes32", internalType: "bytes32" },
|
|
2692
|
+
{ name: "key", type: "bytes32", internalType: "bytes32" }
|
|
2693
|
+
],
|
|
2694
|
+
outputs: [{ name: "", type: "uint256", internalType: "uint256" }],
|
|
2695
|
+
stateMutability: "pure"
|
|
2696
|
+
},
|
|
2697
|
+
{
|
|
2698
|
+
type: "function",
|
|
2699
|
+
name: "mockAcl",
|
|
2700
|
+
inputs: [],
|
|
2701
|
+
outputs: [{ name: "", type: "address", internalType: "contract MockACL" }],
|
|
2702
|
+
stateMutability: "view"
|
|
2703
|
+
},
|
|
2704
|
+
{
|
|
2705
|
+
type: "function",
|
|
2706
|
+
name: "mockTaskManager",
|
|
2707
|
+
inputs: [],
|
|
2708
|
+
outputs: [{ name: "", type: "address", internalType: "contract MockTaskManager" }],
|
|
2709
|
+
stateMutability: "view"
|
|
2710
|
+
},
|
|
2711
|
+
{
|
|
2712
|
+
type: "function",
|
|
2713
|
+
name: "mockQueryDecrypt",
|
|
2714
|
+
inputs: [
|
|
2715
|
+
{ name: "ctHash", type: "uint256", internalType: "uint256" },
|
|
2716
|
+
{ name: "", type: "uint256", internalType: "uint256" },
|
|
2717
|
+
{ name: "issuer", type: "address", internalType: "address" }
|
|
2718
|
+
],
|
|
2719
|
+
outputs: [
|
|
2720
|
+
{ name: "allowed", type: "bool", internalType: "bool" },
|
|
2721
|
+
{ name: "error", type: "string", internalType: "string" },
|
|
2722
|
+
{ name: "", type: "uint256", internalType: "uint256" }
|
|
2723
|
+
],
|
|
2724
|
+
stateMutability: "view"
|
|
2725
|
+
},
|
|
2726
|
+
{
|
|
2727
|
+
type: "function",
|
|
2728
|
+
name: "decryptForTxWithPermit",
|
|
2729
|
+
inputs: [
|
|
2730
|
+
{ name: "ctHash", type: "uint256", internalType: "uint256" },
|
|
2731
|
+
{
|
|
2732
|
+
name: "permission",
|
|
2733
|
+
type: "tuple",
|
|
2734
|
+
internalType: "struct Permission",
|
|
2735
|
+
components: [
|
|
2736
|
+
{ name: "issuer", type: "address", internalType: "address" },
|
|
2737
|
+
{ name: "expiration", type: "uint64", internalType: "uint64" },
|
|
2738
|
+
{ name: "recipient", type: "address", internalType: "address" },
|
|
2739
|
+
{ name: "validatorId", type: "uint256", internalType: "uint256" },
|
|
2740
|
+
{ name: "validatorContract", type: "address", internalType: "address" },
|
|
2741
|
+
{ name: "sealingKey", type: "bytes32", internalType: "bytes32" },
|
|
2742
|
+
{ name: "issuerSignature", type: "bytes", internalType: "bytes" },
|
|
2743
|
+
{ name: "recipientSignature", type: "bytes", internalType: "bytes" }
|
|
2744
|
+
]
|
|
2745
|
+
}
|
|
2746
|
+
],
|
|
2747
|
+
outputs: [
|
|
2748
|
+
{ name: "allowed", type: "bool", internalType: "bool" },
|
|
2749
|
+
{ name: "error", type: "string", internalType: "string" },
|
|
2750
|
+
{ name: "decryptedValue", type: "uint256", internalType: "uint256" }
|
|
2751
|
+
],
|
|
2752
|
+
stateMutability: "view"
|
|
2753
|
+
},
|
|
2754
|
+
{
|
|
2755
|
+
type: "function",
|
|
2756
|
+
name: "decryptForTxWithoutPermit",
|
|
2757
|
+
inputs: [{ name: "ctHash", type: "uint256", internalType: "uint256" }],
|
|
2758
|
+
outputs: [
|
|
2759
|
+
{ name: "allowed", type: "bool", internalType: "bool" },
|
|
2760
|
+
{ name: "error", type: "string", internalType: "string" },
|
|
2761
|
+
{ name: "decryptedValue", type: "uint256", internalType: "uint256" }
|
|
2762
|
+
],
|
|
2763
|
+
stateMutability: "view"
|
|
2764
|
+
}
|
|
2765
|
+
];
|
|
2766
|
+
|
|
2767
|
+
// core/decrypt/cofheMocksDecryptForView.ts
|
|
2768
|
+
async function cofheMocksDecryptForView(ctHash, utype, permit, publicClient) {
|
|
2769
|
+
const permission = PermitUtils.getPermission(permit, true);
|
|
2770
|
+
const permissionWithBigInts = {
|
|
2771
|
+
...permission,
|
|
2772
|
+
expiration: BigInt(permission.expiration),
|
|
2773
|
+
validatorId: BigInt(permission.validatorId)
|
|
2774
|
+
};
|
|
2775
|
+
const [allowed, error, result] = await publicClient.readContract({
|
|
2776
|
+
address: MOCKS_THRESHOLD_NETWORK_ADDRESS,
|
|
2777
|
+
abi: MockThresholdNetworkAbi,
|
|
2778
|
+
functionName: "querySealOutput",
|
|
2779
|
+
args: [BigInt(ctHash), BigInt(utype), permissionWithBigInts]
|
|
2780
|
+
});
|
|
2781
|
+
if (error != "") {
|
|
2782
|
+
throw new CofheError({
|
|
2783
|
+
code: "SEAL_OUTPUT_FAILED" /* SealOutputFailed */,
|
|
2784
|
+
message: `mocks querySealOutput call failed: ${error}`
|
|
2785
|
+
});
|
|
2786
|
+
}
|
|
2787
|
+
if (allowed == false) {
|
|
2788
|
+
throw new CofheError({
|
|
2789
|
+
code: "SEAL_OUTPUT_FAILED" /* SealOutputFailed */,
|
|
2790
|
+
message: `mocks querySealOutput call failed: ACL Access Denied (NotAllowed)`
|
|
2791
|
+
});
|
|
2792
|
+
}
|
|
2793
|
+
const sealedBigInt = BigInt(result);
|
|
2794
|
+
const sealingKeyBigInt = BigInt(permission.sealingKey);
|
|
2795
|
+
const unsealed = sealedBigInt ^ sealingKeyBigInt;
|
|
2796
|
+
return unsealed;
|
|
2797
|
+
}
|
|
2798
|
+
|
|
2799
|
+
// core/decrypt/polling.ts
|
|
2800
|
+
function computeMinuteRampPollIntervalMs(elapsedMs, params) {
|
|
2801
|
+
const elapsedSeconds = Math.floor(elapsedMs / 1e3);
|
|
2802
|
+
const intervalSeconds = 1 + Math.floor(elapsedSeconds / 60);
|
|
2803
|
+
const intervalMs = intervalSeconds * 1e3;
|
|
2804
|
+
return Math.min(params.maxIntervalMs, Math.max(params.minIntervalMs, intervalMs));
|
|
2805
|
+
}
|
|
2806
|
+
|
|
2807
|
+
// core/decrypt/tnSealOutputV2.ts
|
|
2808
|
+
var POLL_INTERVAL_MS = 1e3;
|
|
2809
|
+
var POLL_MAX_INTERVAL_MS = 1e4;
|
|
2810
|
+
var POLL_TIMEOUT_MS = 5 * 60 * 1e3;
|
|
2811
|
+
function numberArrayToUint8Array(arr) {
|
|
2812
|
+
return new Uint8Array(arr);
|
|
2813
|
+
}
|
|
2814
|
+
function convertSealedData(sealed) {
|
|
2815
|
+
if (!sealed) {
|
|
2816
|
+
throw new CofheError({
|
|
2817
|
+
code: "SEAL_OUTPUT_RETURNED_NULL" /* SealOutputReturnedNull */,
|
|
2818
|
+
message: "Sealed data is missing from completed response"
|
|
2819
|
+
});
|
|
2820
|
+
}
|
|
2821
|
+
return {
|
|
2822
|
+
data: numberArrayToUint8Array(sealed.data),
|
|
2823
|
+
public_key: numberArrayToUint8Array(sealed.public_key),
|
|
2824
|
+
nonce: numberArrayToUint8Array(sealed.nonce)
|
|
2825
|
+
};
|
|
2826
|
+
}
|
|
2827
|
+
async function submitSealOutputRequest(thresholdNetworkUrl, ctHash, chainId, permission) {
|
|
2828
|
+
const body = {
|
|
2829
|
+
ct_tempkey: BigInt(ctHash).toString(16).padStart(64, "0"),
|
|
2830
|
+
host_chain_id: chainId,
|
|
2831
|
+
permit: permission
|
|
2832
|
+
};
|
|
2833
|
+
let response;
|
|
2834
|
+
try {
|
|
2835
|
+
response = await fetch(`${thresholdNetworkUrl}/v2/sealoutput`, {
|
|
2836
|
+
method: "POST",
|
|
2837
|
+
headers: {
|
|
2838
|
+
"Content-Type": "application/json"
|
|
2839
|
+
},
|
|
2840
|
+
body: JSON.stringify(body)
|
|
2841
|
+
});
|
|
2842
|
+
} catch (e) {
|
|
2843
|
+
throw new CofheError({
|
|
2844
|
+
code: "SEAL_OUTPUT_FAILED" /* SealOutputFailed */,
|
|
2845
|
+
message: `sealOutput request failed`,
|
|
2846
|
+
hint: "Ensure the threshold network URL is valid and reachable.",
|
|
2847
|
+
cause: e instanceof Error ? e : void 0,
|
|
2848
|
+
context: {
|
|
2849
|
+
thresholdNetworkUrl,
|
|
2850
|
+
body
|
|
2851
|
+
}
|
|
2852
|
+
});
|
|
2853
|
+
}
|
|
2854
|
+
if (!response.ok) {
|
|
2855
|
+
let errorMessage = `HTTP ${response.status}`;
|
|
2856
|
+
try {
|
|
2857
|
+
const errorBody = await response.json();
|
|
2858
|
+
errorMessage = errorBody.error_message || errorBody.message || errorMessage;
|
|
2859
|
+
} catch {
|
|
2860
|
+
errorMessage = response.statusText || errorMessage;
|
|
2861
|
+
}
|
|
2862
|
+
throw new CofheError({
|
|
2863
|
+
code: "SEAL_OUTPUT_FAILED" /* SealOutputFailed */,
|
|
2864
|
+
message: `sealOutput request failed: ${errorMessage}`,
|
|
2865
|
+
hint: "Check the threshold network URL and request parameters.",
|
|
2866
|
+
context: {
|
|
2867
|
+
thresholdNetworkUrl,
|
|
2868
|
+
status: response.status,
|
|
2869
|
+
statusText: response.statusText,
|
|
2870
|
+
body
|
|
2871
|
+
}
|
|
2872
|
+
});
|
|
2873
|
+
}
|
|
2874
|
+
let submitResponse;
|
|
2875
|
+
try {
|
|
2876
|
+
submitResponse = await response.json();
|
|
2877
|
+
} catch (e) {
|
|
2878
|
+
throw new CofheError({
|
|
2879
|
+
code: "SEAL_OUTPUT_FAILED" /* SealOutputFailed */,
|
|
2880
|
+
message: `Failed to parse sealOutput submit response`,
|
|
2881
|
+
cause: e instanceof Error ? e : void 0,
|
|
2882
|
+
context: {
|
|
2883
|
+
thresholdNetworkUrl,
|
|
2884
|
+
body
|
|
2885
|
+
}
|
|
2886
|
+
});
|
|
2887
|
+
}
|
|
2888
|
+
if (!submitResponse.request_id) {
|
|
2889
|
+
throw new CofheError({
|
|
2890
|
+
code: "SEAL_OUTPUT_FAILED" /* SealOutputFailed */,
|
|
2891
|
+
message: `sealOutput submit response missing request_id`,
|
|
2892
|
+
context: {
|
|
2893
|
+
thresholdNetworkUrl,
|
|
2894
|
+
body,
|
|
2895
|
+
submitResponse
|
|
2896
|
+
}
|
|
2897
|
+
});
|
|
2898
|
+
}
|
|
2899
|
+
return submitResponse.request_id;
|
|
2900
|
+
}
|
|
2901
|
+
async function pollSealOutputStatus(thresholdNetworkUrl, requestId, onPoll) {
|
|
2902
|
+
const startTime = Date.now();
|
|
2903
|
+
let attemptIndex = 0;
|
|
2904
|
+
let completed = false;
|
|
2905
|
+
while (!completed) {
|
|
2906
|
+
const elapsedMs = Date.now() - startTime;
|
|
2907
|
+
const intervalMs = computeMinuteRampPollIntervalMs(elapsedMs, {
|
|
2908
|
+
minIntervalMs: POLL_INTERVAL_MS,
|
|
2909
|
+
maxIntervalMs: POLL_MAX_INTERVAL_MS
|
|
2910
|
+
});
|
|
2911
|
+
onPoll?.({
|
|
2912
|
+
operation: "sealoutput",
|
|
2913
|
+
requestId,
|
|
2914
|
+
attemptIndex,
|
|
2915
|
+
elapsedMs,
|
|
2916
|
+
intervalMs,
|
|
2917
|
+
timeoutMs: POLL_TIMEOUT_MS
|
|
2918
|
+
});
|
|
2919
|
+
if (elapsedMs > POLL_TIMEOUT_MS) {
|
|
2920
|
+
throw new CofheError({
|
|
2921
|
+
code: "SEAL_OUTPUT_FAILED" /* SealOutputFailed */,
|
|
2922
|
+
message: `sealOutput polling timed out after ${POLL_TIMEOUT_MS}ms`,
|
|
2923
|
+
hint: "The request may still be processing. Try again later.",
|
|
2924
|
+
context: {
|
|
2925
|
+
thresholdNetworkUrl,
|
|
2926
|
+
requestId,
|
|
2927
|
+
timeoutMs: POLL_TIMEOUT_MS
|
|
2928
|
+
}
|
|
2929
|
+
});
|
|
2930
|
+
}
|
|
2931
|
+
let response;
|
|
2932
|
+
try {
|
|
2933
|
+
response = await fetch(`${thresholdNetworkUrl}/v2/sealoutput/${requestId}`, {
|
|
2934
|
+
method: "GET",
|
|
2935
|
+
headers: {
|
|
2936
|
+
"Content-Type": "application/json"
|
|
2937
|
+
}
|
|
2938
|
+
});
|
|
2939
|
+
} catch (e) {
|
|
2940
|
+
throw new CofheError({
|
|
2941
|
+
code: "SEAL_OUTPUT_FAILED" /* SealOutputFailed */,
|
|
2942
|
+
message: `sealOutput status poll failed`,
|
|
2943
|
+
hint: "Ensure the threshold network URL is valid and reachable.",
|
|
2944
|
+
cause: e instanceof Error ? e : void 0,
|
|
2945
|
+
context: {
|
|
2946
|
+
thresholdNetworkUrl,
|
|
2947
|
+
requestId
|
|
2948
|
+
}
|
|
2949
|
+
});
|
|
2950
|
+
}
|
|
2951
|
+
if (response.status === 404) {
|
|
2952
|
+
throw new CofheError({
|
|
2953
|
+
code: "SEAL_OUTPUT_FAILED" /* SealOutputFailed */,
|
|
2954
|
+
message: `sealOutput request not found: ${requestId}`,
|
|
2955
|
+
hint: "The request may have expired or been invalid.",
|
|
2956
|
+
context: {
|
|
2957
|
+
thresholdNetworkUrl,
|
|
2958
|
+
requestId
|
|
2959
|
+
}
|
|
2960
|
+
});
|
|
2961
|
+
}
|
|
2962
|
+
if (!response.ok) {
|
|
2963
|
+
let errorMessage = `HTTP ${response.status}`;
|
|
2964
|
+
try {
|
|
2965
|
+
const errorBody = await response.json();
|
|
2966
|
+
errorMessage = errorBody.error_message || errorBody.message || errorMessage;
|
|
2967
|
+
} catch {
|
|
2968
|
+
errorMessage = response.statusText || errorMessage;
|
|
2969
|
+
}
|
|
2970
|
+
throw new CofheError({
|
|
2971
|
+
code: "SEAL_OUTPUT_FAILED" /* SealOutputFailed */,
|
|
2972
|
+
message: `sealOutput status poll failed: ${errorMessage}`,
|
|
2973
|
+
context: {
|
|
2974
|
+
thresholdNetworkUrl,
|
|
2975
|
+
requestId,
|
|
2976
|
+
status: response.status,
|
|
2977
|
+
statusText: response.statusText
|
|
2978
|
+
}
|
|
2979
|
+
});
|
|
2980
|
+
}
|
|
2981
|
+
let statusResponse;
|
|
2982
|
+
try {
|
|
2983
|
+
statusResponse = await response.json();
|
|
2984
|
+
} catch (e) {
|
|
2985
|
+
throw new CofheError({
|
|
2986
|
+
code: "SEAL_OUTPUT_FAILED" /* SealOutputFailed */,
|
|
2987
|
+
message: `Failed to parse sealOutput status response`,
|
|
2988
|
+
cause: e instanceof Error ? e : void 0,
|
|
2989
|
+
context: {
|
|
2990
|
+
thresholdNetworkUrl,
|
|
2991
|
+
requestId
|
|
2992
|
+
}
|
|
2993
|
+
});
|
|
2994
|
+
}
|
|
2995
|
+
if (statusResponse.status === "COMPLETED") {
|
|
2996
|
+
if (statusResponse.is_succeed === false) {
|
|
2997
|
+
const errorMessage = statusResponse.error_message || "Unknown error";
|
|
2998
|
+
throw new CofheError({
|
|
2999
|
+
code: "SEAL_OUTPUT_FAILED" /* SealOutputFailed */,
|
|
3000
|
+
message: `sealOutput request failed: ${errorMessage}`,
|
|
3001
|
+
context: {
|
|
3002
|
+
thresholdNetworkUrl,
|
|
3003
|
+
requestId,
|
|
3004
|
+
statusResponse
|
|
3005
|
+
}
|
|
3006
|
+
});
|
|
3007
|
+
}
|
|
3008
|
+
if (!statusResponse.sealed) {
|
|
3009
|
+
throw new CofheError({
|
|
3010
|
+
code: "SEAL_OUTPUT_RETURNED_NULL" /* SealOutputReturnedNull */,
|
|
3011
|
+
message: `sealOutput request completed but returned no sealed data`,
|
|
3012
|
+
context: {
|
|
3013
|
+
thresholdNetworkUrl,
|
|
3014
|
+
requestId,
|
|
3015
|
+
statusResponse
|
|
3016
|
+
}
|
|
3017
|
+
});
|
|
3018
|
+
}
|
|
3019
|
+
return convertSealedData(statusResponse.sealed);
|
|
3020
|
+
}
|
|
3021
|
+
await new Promise((resolve) => setTimeout(resolve, intervalMs));
|
|
3022
|
+
attemptIndex += 1;
|
|
3023
|
+
}
|
|
3024
|
+
throw new CofheError({
|
|
3025
|
+
code: "SEAL_OUTPUT_FAILED" /* SealOutputFailed */,
|
|
3026
|
+
message: "Polling loop exited unexpectedly",
|
|
3027
|
+
context: {
|
|
3028
|
+
thresholdNetworkUrl,
|
|
3029
|
+
requestId
|
|
3030
|
+
}
|
|
3031
|
+
});
|
|
3032
|
+
}
|
|
3033
|
+
async function tnSealOutputV2(params) {
|
|
3034
|
+
const { thresholdNetworkUrl, ctHash, chainId, permission, onPoll } = params;
|
|
3035
|
+
const requestId = await submitSealOutputRequest(thresholdNetworkUrl, ctHash, chainId, permission);
|
|
3036
|
+
return await pollSealOutputStatus(thresholdNetworkUrl, requestId, onPoll);
|
|
3037
|
+
}
|
|
3038
|
+
|
|
3039
|
+
// core/decrypt/decryptForViewBuilder.ts
|
|
3040
|
+
var DecryptForViewBuilder = class extends BaseBuilder {
|
|
3041
|
+
ctHash;
|
|
3042
|
+
utype;
|
|
3043
|
+
permitHash;
|
|
3044
|
+
permit;
|
|
3045
|
+
pollCallback;
|
|
3046
|
+
constructor(params) {
|
|
3047
|
+
super({
|
|
3048
|
+
config: params.config,
|
|
3049
|
+
publicClient: params.publicClient,
|
|
3050
|
+
walletClient: params.walletClient,
|
|
3051
|
+
chainId: params.chainId,
|
|
3052
|
+
account: params.account,
|
|
3053
|
+
requireConnected: params.requireConnected
|
|
3054
|
+
});
|
|
3055
|
+
this.ctHash = params.ctHash;
|
|
3056
|
+
this.utype = params.utype;
|
|
3057
|
+
this.permitHash = params.permitHash;
|
|
3058
|
+
this.permit = params.permit;
|
|
3059
|
+
}
|
|
3060
|
+
/**
|
|
3061
|
+
* @param chainId - Chain to decrypt values from. Used to fetch the threshold network URL and use the correct permit.
|
|
3062
|
+
*
|
|
3063
|
+
* If not provided, the chainId will be fetched from the connected publicClient.
|
|
3064
|
+
*
|
|
3065
|
+
* Example:
|
|
3066
|
+
* ```typescript
|
|
3067
|
+
* const unsealed = await client.decryptForView(ctHash, utype)
|
|
3068
|
+
* .setChainId(11155111)
|
|
3069
|
+
* .execute();
|
|
3070
|
+
* ```
|
|
3071
|
+
*
|
|
3072
|
+
* @returns The chainable DecryptForViewBuilder instance.
|
|
3073
|
+
*/
|
|
3074
|
+
setChainId(chainId) {
|
|
3075
|
+
this.chainId = chainId;
|
|
3076
|
+
return this;
|
|
3077
|
+
}
|
|
3078
|
+
getChainId() {
|
|
3079
|
+
return this.chainId;
|
|
3080
|
+
}
|
|
3081
|
+
/**
|
|
3082
|
+
* @param account - Account to decrypt values from. Used to fetch the correct permit.
|
|
3083
|
+
*
|
|
3084
|
+
* If not provided, the account will be fetched from the connected walletClient.
|
|
3085
|
+
*
|
|
3086
|
+
* Example:
|
|
3087
|
+
* ```typescript
|
|
3088
|
+
* const unsealed = await client.decryptForView(ctHash, utype)
|
|
3089
|
+
* .setAccount('0x1234567890123456789012345678901234567890')
|
|
3090
|
+
* .execute();
|
|
3091
|
+
* ```
|
|
3092
|
+
*
|
|
3093
|
+
* @returns The chainable DecryptForViewBuilder instance.
|
|
3094
|
+
*/
|
|
3095
|
+
setAccount(account) {
|
|
3096
|
+
this.account = account;
|
|
3097
|
+
return this;
|
|
3098
|
+
}
|
|
3099
|
+
getAccount() {
|
|
3100
|
+
return this.account;
|
|
3101
|
+
}
|
|
3102
|
+
onPoll(callback) {
|
|
3103
|
+
this.pollCallback = callback;
|
|
3104
|
+
return this;
|
|
3105
|
+
}
|
|
3106
|
+
withPermit(permitOrPermitHash) {
|
|
3107
|
+
if (typeof permitOrPermitHash === "string") {
|
|
3108
|
+
this.permitHash = permitOrPermitHash;
|
|
3109
|
+
this.permit = void 0;
|
|
3110
|
+
} else if (permitOrPermitHash === void 0) {
|
|
3111
|
+
this.permitHash = void 0;
|
|
3112
|
+
this.permit = void 0;
|
|
3113
|
+
} else {
|
|
3114
|
+
this.permit = permitOrPermitHash;
|
|
3115
|
+
this.permitHash = void 0;
|
|
3116
|
+
}
|
|
3117
|
+
return this;
|
|
3118
|
+
}
|
|
3119
|
+
/**
|
|
3120
|
+
* @param permitHash - Permit hash to decrypt values from. Used to fetch the correct permit.
|
|
3121
|
+
*
|
|
3122
|
+
* If not provided, the active permit for the chainId and account will be used.
|
|
3123
|
+
* If `setPermit()` is called, it will be used regardless of chainId, account, or permitHash.
|
|
3124
|
+
*
|
|
3125
|
+
* Example:
|
|
3126
|
+
* ```typescript
|
|
3127
|
+
* const unsealed = await client.decryptForView(ctHash, utype)
|
|
3128
|
+
* .setPermitHash('0x1234567890123456789012345678901234567890')
|
|
3129
|
+
* .execute();
|
|
3130
|
+
* ```
|
|
3131
|
+
*
|
|
3132
|
+
* @returns The chainable DecryptForViewBuilder instance.
|
|
3133
|
+
*/
|
|
3134
|
+
/** @deprecated Use `withPermit(permitHash)` instead. */
|
|
3135
|
+
setPermitHash(permitHash) {
|
|
3136
|
+
return this.withPermit(permitHash);
|
|
3137
|
+
}
|
|
3138
|
+
getPermitHash() {
|
|
3139
|
+
return this.permitHash;
|
|
3140
|
+
}
|
|
3141
|
+
/**
|
|
3142
|
+
* @param permit - Permit to decrypt values with. If provided, it will be used regardless of chainId, account, or permitHash.
|
|
3143
|
+
*
|
|
3144
|
+
* If not provided, the permit will be determined by chainId, account, and permitHash.
|
|
3145
|
+
*
|
|
3146
|
+
* Example:
|
|
3147
|
+
* ```typescript
|
|
3148
|
+
* const unsealed = await client.decryptForView(ctHash, utype)
|
|
3149
|
+
* .setPermit(permit)
|
|
3150
|
+
* .execute();
|
|
3151
|
+
* ```
|
|
3152
|
+
*
|
|
3153
|
+
* @returns The chainable DecryptForViewBuilder instance.
|
|
3154
|
+
*/
|
|
3155
|
+
/** @deprecated Use `withPermit(permit)` instead. */
|
|
3156
|
+
setPermit(permit) {
|
|
3157
|
+
return this.withPermit(permit);
|
|
3158
|
+
}
|
|
3159
|
+
getPermit() {
|
|
3160
|
+
return this.permit;
|
|
3161
|
+
}
|
|
3162
|
+
async getThresholdNetworkUrl() {
|
|
3163
|
+
this.assertChainId();
|
|
3164
|
+
return getThresholdNetworkUrlOrThrow(this.config, this.chainId);
|
|
3165
|
+
}
|
|
3166
|
+
validateUtypeOrThrow() {
|
|
3167
|
+
if (!isValidUtype(this.utype))
|
|
3168
|
+
throw new CofheError({
|
|
3169
|
+
code: "INVALID_UTYPE" /* InvalidUtype */,
|
|
3170
|
+
message: `Invalid utype to decrypt to`,
|
|
3171
|
+
context: {
|
|
3172
|
+
utype: this.utype
|
|
3173
|
+
}
|
|
3174
|
+
});
|
|
3175
|
+
}
|
|
3176
|
+
async getResolvedPermit() {
|
|
3177
|
+
if (this.permit)
|
|
3178
|
+
return this.permit;
|
|
3179
|
+
this.assertChainId();
|
|
3180
|
+
this.assertAccount();
|
|
3181
|
+
if (this.permitHash) {
|
|
3182
|
+
const permit2 = await permits.getPermit(this.chainId, this.account, this.permitHash);
|
|
3183
|
+
if (!permit2) {
|
|
3184
|
+
throw new CofheError({
|
|
3185
|
+
code: "PERMIT_NOT_FOUND" /* PermitNotFound */,
|
|
3186
|
+
message: `Permit with hash <${this.permitHash}> not found for account <${this.account}> and chainId <${this.chainId}>`,
|
|
3187
|
+
hint: "Ensure the permit exists and is valid.",
|
|
3188
|
+
context: {
|
|
3189
|
+
chainId: this.chainId,
|
|
3190
|
+
account: this.account,
|
|
3191
|
+
permitHash: this.permitHash
|
|
3192
|
+
}
|
|
3193
|
+
});
|
|
3194
|
+
}
|
|
3195
|
+
return permit2;
|
|
3196
|
+
}
|
|
3197
|
+
const permit = await permits.getActivePermit(this.chainId, this.account);
|
|
3198
|
+
if (!permit) {
|
|
3199
|
+
throw new CofheError({
|
|
3200
|
+
code: "PERMIT_NOT_FOUND" /* PermitNotFound */,
|
|
3201
|
+
message: `Active permit not found for chainId <${this.chainId}> and account <${this.account}>`,
|
|
3202
|
+
hint: "Ensure a permit exists for this account on this chain.",
|
|
3203
|
+
context: {
|
|
3204
|
+
chainId: this.chainId,
|
|
3205
|
+
account: this.account
|
|
3206
|
+
}
|
|
3207
|
+
});
|
|
3208
|
+
}
|
|
3209
|
+
return permit;
|
|
3210
|
+
}
|
|
3211
|
+
/**
|
|
3212
|
+
* On hardhat, interact with MockZkVerifier contract instead of CoFHE
|
|
3213
|
+
*/
|
|
3214
|
+
async mocksSealOutput(permit) {
|
|
3215
|
+
this.assertPublicClient();
|
|
3216
|
+
const mocksDecryptDelay = this.config.mocks.decryptDelay;
|
|
3217
|
+
if (mocksDecryptDelay > 0)
|
|
3218
|
+
await sleep(mocksDecryptDelay);
|
|
3219
|
+
return cofheMocksDecryptForView(this.ctHash, this.utype, permit, this.publicClient);
|
|
3220
|
+
}
|
|
3221
|
+
/**
|
|
3222
|
+
* In the production context, perform a true decryption with the CoFHE coprocessor.
|
|
3223
|
+
*/
|
|
3224
|
+
async productionSealOutput(permit) {
|
|
3225
|
+
this.assertChainId();
|
|
3226
|
+
this.assertPublicClient();
|
|
3227
|
+
const thresholdNetworkUrl = await this.getThresholdNetworkUrl();
|
|
3228
|
+
const permission = PermitUtils.getPermission(permit, true);
|
|
3229
|
+
const sealed = await tnSealOutputV2({
|
|
3230
|
+
ctHash: this.ctHash,
|
|
3231
|
+
chainId: this.chainId,
|
|
3232
|
+
permission,
|
|
3233
|
+
thresholdNetworkUrl,
|
|
3234
|
+
onPoll: this.pollCallback
|
|
3235
|
+
});
|
|
3236
|
+
return PermitUtils.unseal(permit, sealed);
|
|
3237
|
+
}
|
|
3238
|
+
/**
|
|
3239
|
+
* Final step of the decryption process. MUST BE CALLED LAST IN THE CHAIN.
|
|
3240
|
+
*
|
|
3241
|
+
* This will:
|
|
3242
|
+
* - Use a permit based on provided permit OR chainId + account + permitHash
|
|
3243
|
+
* - Check permit validity
|
|
3244
|
+
* - Call CoFHE `/sealoutput` with the permit, which returns a sealed (encrypted) item
|
|
3245
|
+
* - Unseal the sealed item with the permit
|
|
3246
|
+
* - Return the unsealed item
|
|
3247
|
+
*
|
|
3248
|
+
* Example:
|
|
3249
|
+
* ```typescript
|
|
3250
|
+
* const unsealed = await client.decryptForView(ctHash, utype)
|
|
3251
|
+
* .setChainId(11155111) // optional
|
|
3252
|
+
* .setAccount('0x123...890') // optional
|
|
3253
|
+
* .withPermit() // optional
|
|
3254
|
+
* .execute(); // execute
|
|
3255
|
+
* ```
|
|
3256
|
+
*
|
|
3257
|
+
* @returns The unsealed item.
|
|
3258
|
+
*/
|
|
3259
|
+
async execute() {
|
|
3260
|
+
this.validateUtypeOrThrow();
|
|
3261
|
+
const permit = await this.getResolvedPermit();
|
|
3262
|
+
PermitUtils.validate(permit);
|
|
3263
|
+
const chainId = permit._signedDomain.chainId;
|
|
3264
|
+
let unsealed;
|
|
3265
|
+
if (chainId === hardhat2.id) {
|
|
3266
|
+
unsealed = await this.mocksSealOutput(permit);
|
|
3267
|
+
} else {
|
|
3268
|
+
unsealed = await this.productionSealOutput(permit);
|
|
3269
|
+
}
|
|
3270
|
+
return convertViaUtype(this.utype, unsealed);
|
|
3271
|
+
}
|
|
3272
|
+
};
|
|
3273
|
+
async function cofheMocksDecryptForTx(ctHash, utype, permit, publicClient) {
|
|
3274
|
+
let allowed;
|
|
3275
|
+
let error;
|
|
3276
|
+
let decryptedValue;
|
|
3277
|
+
if (permit !== null) {
|
|
3278
|
+
let permission = PermitUtils.getPermission(permit, true);
|
|
3279
|
+
const permissionWithBigInts = {
|
|
3280
|
+
...permission,
|
|
3281
|
+
expiration: BigInt(permission.expiration),
|
|
3282
|
+
validatorId: BigInt(permission.validatorId)
|
|
3283
|
+
};
|
|
3284
|
+
[allowed, error, decryptedValue] = await publicClient.readContract({
|
|
3285
|
+
address: MOCKS_THRESHOLD_NETWORK_ADDRESS,
|
|
3286
|
+
abi: MockThresholdNetworkAbi,
|
|
3287
|
+
functionName: "decryptForTxWithPermit",
|
|
3288
|
+
args: [BigInt(ctHash), permissionWithBigInts]
|
|
3289
|
+
});
|
|
3290
|
+
} else {
|
|
3291
|
+
[allowed, error, decryptedValue] = await publicClient.readContract({
|
|
3292
|
+
address: MOCKS_THRESHOLD_NETWORK_ADDRESS,
|
|
3293
|
+
abi: MockThresholdNetworkAbi,
|
|
3294
|
+
functionName: "decryptForTxWithoutPermit",
|
|
3295
|
+
args: [BigInt(ctHash)]
|
|
3296
|
+
});
|
|
3297
|
+
}
|
|
3298
|
+
if (error != "") {
|
|
3299
|
+
throw new CofheError({
|
|
3300
|
+
code: "DECRYPT_FAILED" /* DecryptFailed */,
|
|
3301
|
+
message: `mocks decryptForTx call failed: ${error}`
|
|
3302
|
+
});
|
|
3303
|
+
}
|
|
3304
|
+
if (allowed == false) {
|
|
3305
|
+
throw new CofheError({
|
|
3306
|
+
code: "DECRYPT_FAILED" /* DecryptFailed */,
|
|
3307
|
+
message: `mocks decryptForTx call failed: ACL Access Denied (NotAllowed)`
|
|
3308
|
+
});
|
|
3309
|
+
}
|
|
3310
|
+
const packed = viem.encodePacked(["uint256", "uint256"], [BigInt(ctHash), decryptedValue]);
|
|
3311
|
+
const messageHash = viem.keccak256(packed);
|
|
3312
|
+
const signature = await accounts.sign({
|
|
3313
|
+
hash: messageHash,
|
|
3314
|
+
privateKey: MOCKS_DECRYPT_RESULT_SIGNER_PRIVATE_KEY,
|
|
3315
|
+
to: "hex"
|
|
3316
|
+
});
|
|
3317
|
+
return {
|
|
3318
|
+
ctHash,
|
|
3319
|
+
decryptedValue,
|
|
3320
|
+
signature
|
|
3321
|
+
};
|
|
3322
|
+
}
|
|
3323
|
+
function normalizeTnSignature(signature) {
|
|
3324
|
+
if (typeof signature !== "string") {
|
|
3325
|
+
throw new CofheError({
|
|
3326
|
+
code: "DECRYPT_RETURNED_NULL" /* DecryptReturnedNull */,
|
|
3327
|
+
message: "decrypt response missing signature",
|
|
3328
|
+
context: {
|
|
3329
|
+
signature
|
|
3330
|
+
}
|
|
3331
|
+
});
|
|
3332
|
+
}
|
|
3333
|
+
const trimmed = signature.trim();
|
|
3334
|
+
if (trimmed.length === 0) {
|
|
3335
|
+
throw new CofheError({
|
|
3336
|
+
code: "DECRYPT_RETURNED_NULL" /* DecryptReturnedNull */,
|
|
3337
|
+
message: "decrypt response returned empty signature"
|
|
3338
|
+
});
|
|
3339
|
+
}
|
|
3340
|
+
const prefixed = trimmed.startsWith("0x") ? trimmed : `0x${trimmed}`;
|
|
3341
|
+
const parsed = viem.parseSignature(prefixed);
|
|
3342
|
+
return viem.serializeSignature(parsed);
|
|
3343
|
+
}
|
|
3344
|
+
function parseDecryptedBytesToBigInt(decrypted) {
|
|
3345
|
+
if (!Array.isArray(decrypted)) {
|
|
3346
|
+
throw new CofheError({
|
|
3347
|
+
code: "DECRYPT_RETURNED_NULL" /* DecryptReturnedNull */,
|
|
3348
|
+
message: "decrypt response field <decrypted> must be a byte array",
|
|
3349
|
+
context: {
|
|
3350
|
+
decrypted
|
|
3351
|
+
}
|
|
3352
|
+
});
|
|
3353
|
+
}
|
|
3354
|
+
if (decrypted.length === 0) {
|
|
3355
|
+
throw new CofheError({
|
|
3356
|
+
code: "DECRYPT_RETURNED_NULL" /* DecryptReturnedNull */,
|
|
3357
|
+
message: "decrypt response field <decrypted> was an empty byte array",
|
|
3358
|
+
context: {
|
|
3359
|
+
decrypted
|
|
3360
|
+
}
|
|
3361
|
+
});
|
|
3362
|
+
}
|
|
3363
|
+
let hex = "";
|
|
3364
|
+
for (const b of decrypted) {
|
|
3365
|
+
if (typeof b !== "number" || !Number.isInteger(b) || b < 0 || b > 255) {
|
|
3366
|
+
throw new CofheError({
|
|
3367
|
+
code: "DECRYPT_RETURNED_NULL" /* DecryptReturnedNull */,
|
|
3368
|
+
message: "decrypt response field <decrypted> contained a non-byte value",
|
|
3369
|
+
context: {
|
|
3370
|
+
badElement: b,
|
|
3371
|
+
decrypted
|
|
3372
|
+
}
|
|
3373
|
+
});
|
|
3374
|
+
}
|
|
3375
|
+
hex += b.toString(16).padStart(2, "0");
|
|
3376
|
+
}
|
|
3377
|
+
return BigInt(`0x${hex}`);
|
|
3378
|
+
}
|
|
3379
|
+
|
|
3380
|
+
// core/decrypt/tnDecryptV2.ts
|
|
3381
|
+
var POLL_INTERVAL_MS2 = 1e3;
|
|
3382
|
+
var POLL_MAX_INTERVAL_MS2 = 1e4;
|
|
3383
|
+
var POLL_TIMEOUT_MS2 = 5 * 60 * 1e3;
|
|
3384
|
+
function assertDecryptSubmitResponseV2(value) {
|
|
3385
|
+
if (value == null || typeof value !== "object") {
|
|
3386
|
+
throw new CofheError({
|
|
3387
|
+
code: "DECRYPT_FAILED" /* DecryptFailed */,
|
|
3388
|
+
message: "decrypt submit response must be a JSON object",
|
|
3389
|
+
context: {
|
|
3390
|
+
value
|
|
3391
|
+
}
|
|
3392
|
+
});
|
|
3393
|
+
}
|
|
3394
|
+
const v = value;
|
|
3395
|
+
if (typeof v.request_id !== "string" || v.request_id.trim().length === 0) {
|
|
3396
|
+
throw new CofheError({
|
|
3397
|
+
code: "DECRYPT_FAILED" /* DecryptFailed */,
|
|
3398
|
+
message: "decrypt submit response missing request_id",
|
|
3399
|
+
context: {
|
|
3400
|
+
value
|
|
3401
|
+
}
|
|
3402
|
+
});
|
|
3403
|
+
}
|
|
3404
|
+
return { request_id: v.request_id };
|
|
3405
|
+
}
|
|
3406
|
+
function assertDecryptStatusResponseV2(value) {
|
|
3407
|
+
if (value == null || typeof value !== "object") {
|
|
3408
|
+
throw new CofheError({
|
|
3409
|
+
code: "DECRYPT_FAILED" /* DecryptFailed */,
|
|
3410
|
+
message: "decrypt status response must be a JSON object",
|
|
3411
|
+
context: {
|
|
3412
|
+
value
|
|
3413
|
+
}
|
|
3414
|
+
});
|
|
3415
|
+
}
|
|
3416
|
+
const v = value;
|
|
3417
|
+
const requestId = v.request_id;
|
|
3418
|
+
const status = v.status;
|
|
3419
|
+
const submittedAt = v.submitted_at;
|
|
3420
|
+
if (typeof requestId !== "string" || requestId.trim().length === 0) {
|
|
3421
|
+
throw new CofheError({
|
|
3422
|
+
code: "DECRYPT_FAILED" /* DecryptFailed */,
|
|
3423
|
+
message: "decrypt status response missing request_id",
|
|
3424
|
+
context: {
|
|
3425
|
+
value
|
|
3426
|
+
}
|
|
3427
|
+
});
|
|
3428
|
+
}
|
|
3429
|
+
if (status !== "PROCESSING" && status !== "COMPLETED") {
|
|
3430
|
+
throw new CofheError({
|
|
3431
|
+
code: "DECRYPT_FAILED" /* DecryptFailed */,
|
|
3432
|
+
message: "decrypt status response has invalid status",
|
|
3433
|
+
context: {
|
|
3434
|
+
value,
|
|
3435
|
+
status
|
|
3436
|
+
}
|
|
3437
|
+
});
|
|
3438
|
+
}
|
|
3439
|
+
if (typeof submittedAt !== "string" || submittedAt.trim().length === 0) {
|
|
3440
|
+
throw new CofheError({
|
|
3441
|
+
code: "DECRYPT_FAILED" /* DecryptFailed */,
|
|
3442
|
+
message: "decrypt status response missing submitted_at",
|
|
3443
|
+
context: {
|
|
3444
|
+
value
|
|
3445
|
+
}
|
|
3446
|
+
});
|
|
3447
|
+
}
|
|
3448
|
+
return value;
|
|
3449
|
+
}
|
|
3450
|
+
async function submitDecryptRequestV2(thresholdNetworkUrl, ctHash, chainId, permission) {
|
|
3451
|
+
const body = {
|
|
3452
|
+
ct_tempkey: BigInt(ctHash).toString(16).padStart(64, "0"),
|
|
3453
|
+
host_chain_id: chainId
|
|
3454
|
+
};
|
|
3455
|
+
if (permission) {
|
|
3456
|
+
body.permit = permission;
|
|
3457
|
+
}
|
|
3458
|
+
let response;
|
|
3459
|
+
try {
|
|
3460
|
+
response = await fetch(`${thresholdNetworkUrl}/v2/decrypt`, {
|
|
3461
|
+
method: "POST",
|
|
3462
|
+
headers: {
|
|
3463
|
+
"Content-Type": "application/json"
|
|
3464
|
+
},
|
|
3465
|
+
body: JSON.stringify(body)
|
|
3466
|
+
});
|
|
3467
|
+
} catch (e) {
|
|
3468
|
+
throw new CofheError({
|
|
3469
|
+
code: "DECRYPT_FAILED" /* DecryptFailed */,
|
|
3470
|
+
message: `decrypt request failed`,
|
|
3471
|
+
hint: "Ensure the threshold network URL is valid and reachable.",
|
|
3472
|
+
cause: e instanceof Error ? e : void 0,
|
|
3473
|
+
context: {
|
|
3474
|
+
thresholdNetworkUrl,
|
|
3475
|
+
body
|
|
3476
|
+
}
|
|
3477
|
+
});
|
|
3478
|
+
}
|
|
3479
|
+
if (!response.ok) {
|
|
3480
|
+
let errorMessage = `HTTP ${response.status}`;
|
|
3481
|
+
try {
|
|
3482
|
+
const errorBody = await response.json();
|
|
3483
|
+
const maybeMessage = errorBody.error_message || errorBody.message;
|
|
3484
|
+
if (typeof maybeMessage === "string" && maybeMessage.length > 0)
|
|
3485
|
+
errorMessage = maybeMessage;
|
|
3486
|
+
} catch {
|
|
3487
|
+
errorMessage = response.statusText || errorMessage;
|
|
3488
|
+
}
|
|
3489
|
+
throw new CofheError({
|
|
3490
|
+
code: "DECRYPT_FAILED" /* DecryptFailed */,
|
|
3491
|
+
message: `decrypt request failed: ${errorMessage}`,
|
|
3492
|
+
hint: "Check the threshold network URL and request parameters.",
|
|
3493
|
+
context: {
|
|
3494
|
+
thresholdNetworkUrl,
|
|
3495
|
+
status: response.status,
|
|
3496
|
+
statusText: response.statusText,
|
|
3497
|
+
body
|
|
3498
|
+
}
|
|
3499
|
+
});
|
|
3500
|
+
}
|
|
3501
|
+
let rawJson;
|
|
3502
|
+
try {
|
|
3503
|
+
rawJson = await response.json();
|
|
3504
|
+
} catch (e) {
|
|
3505
|
+
throw new CofheError({
|
|
3506
|
+
code: "DECRYPT_FAILED" /* DecryptFailed */,
|
|
3507
|
+
message: `Failed to parse decrypt submit response`,
|
|
3508
|
+
cause: e instanceof Error ? e : void 0,
|
|
3509
|
+
context: {
|
|
3510
|
+
thresholdNetworkUrl,
|
|
3511
|
+
body
|
|
3512
|
+
}
|
|
3513
|
+
});
|
|
3514
|
+
}
|
|
3515
|
+
const submitResponse = assertDecryptSubmitResponseV2(rawJson);
|
|
3516
|
+
return submitResponse.request_id;
|
|
3517
|
+
}
|
|
3518
|
+
async function pollDecryptStatusV2(thresholdNetworkUrl, requestId, onPoll) {
|
|
3519
|
+
const startTime = Date.now();
|
|
3520
|
+
let attemptIndex = 0;
|
|
3521
|
+
let completed = false;
|
|
3522
|
+
while (!completed) {
|
|
3523
|
+
const elapsedMs = Date.now() - startTime;
|
|
3524
|
+
const intervalMs = computeMinuteRampPollIntervalMs(elapsedMs, {
|
|
3525
|
+
minIntervalMs: POLL_INTERVAL_MS2,
|
|
3526
|
+
maxIntervalMs: POLL_MAX_INTERVAL_MS2
|
|
3527
|
+
});
|
|
3528
|
+
onPoll?.({
|
|
3529
|
+
operation: "decrypt",
|
|
3530
|
+
requestId,
|
|
3531
|
+
attemptIndex,
|
|
3532
|
+
elapsedMs,
|
|
3533
|
+
intervalMs,
|
|
3534
|
+
timeoutMs: POLL_TIMEOUT_MS2
|
|
3535
|
+
});
|
|
3536
|
+
if (elapsedMs > POLL_TIMEOUT_MS2) {
|
|
3537
|
+
throw new CofheError({
|
|
3538
|
+
code: "DECRYPT_FAILED" /* DecryptFailed */,
|
|
3539
|
+
message: `decrypt polling timed out after ${POLL_TIMEOUT_MS2}ms`,
|
|
3540
|
+
hint: "The request may still be processing. Try again later.",
|
|
3541
|
+
context: {
|
|
3542
|
+
thresholdNetworkUrl,
|
|
3543
|
+
requestId,
|
|
3544
|
+
timeoutMs: POLL_TIMEOUT_MS2
|
|
3545
|
+
}
|
|
3546
|
+
});
|
|
3547
|
+
}
|
|
3548
|
+
let response;
|
|
3549
|
+
try {
|
|
3550
|
+
response = await fetch(`${thresholdNetworkUrl}/v2/decrypt/${requestId}`, {
|
|
3551
|
+
method: "GET",
|
|
3552
|
+
headers: {
|
|
3553
|
+
"Content-Type": "application/json"
|
|
3554
|
+
}
|
|
3555
|
+
});
|
|
3556
|
+
} catch (e) {
|
|
3557
|
+
throw new CofheError({
|
|
3558
|
+
code: "DECRYPT_FAILED" /* DecryptFailed */,
|
|
3559
|
+
message: `decrypt status poll failed`,
|
|
3560
|
+
hint: "Ensure the threshold network URL is valid and reachable.",
|
|
3561
|
+
cause: e instanceof Error ? e : void 0,
|
|
3562
|
+
context: {
|
|
3563
|
+
thresholdNetworkUrl,
|
|
3564
|
+
requestId
|
|
3565
|
+
}
|
|
3566
|
+
});
|
|
3567
|
+
}
|
|
3568
|
+
if (response.status === 404) {
|
|
3569
|
+
throw new CofheError({
|
|
3570
|
+
code: "DECRYPT_FAILED" /* DecryptFailed */,
|
|
3571
|
+
message: `decrypt request not found: ${requestId}`,
|
|
3572
|
+
hint: "The request may have expired or been invalid.",
|
|
3573
|
+
context: {
|
|
3574
|
+
thresholdNetworkUrl,
|
|
3575
|
+
requestId
|
|
3576
|
+
}
|
|
3577
|
+
});
|
|
3578
|
+
}
|
|
3579
|
+
if (!response.ok) {
|
|
3580
|
+
let errorMessage = `HTTP ${response.status}`;
|
|
3581
|
+
try {
|
|
3582
|
+
const errorBody = await response.json();
|
|
3583
|
+
const maybeMessage = errorBody.error_message || errorBody.message;
|
|
3584
|
+
if (typeof maybeMessage === "string" && maybeMessage.length > 0)
|
|
3585
|
+
errorMessage = maybeMessage;
|
|
3586
|
+
} catch {
|
|
3587
|
+
errorMessage = response.statusText || errorMessage;
|
|
3588
|
+
}
|
|
3589
|
+
throw new CofheError({
|
|
3590
|
+
code: "DECRYPT_FAILED" /* DecryptFailed */,
|
|
3591
|
+
message: `decrypt status poll failed: ${errorMessage}`,
|
|
3592
|
+
context: {
|
|
3593
|
+
thresholdNetworkUrl,
|
|
3594
|
+
requestId,
|
|
3595
|
+
status: response.status,
|
|
3596
|
+
statusText: response.statusText
|
|
3597
|
+
}
|
|
3598
|
+
});
|
|
3599
|
+
}
|
|
3600
|
+
let rawJson;
|
|
3601
|
+
try {
|
|
3602
|
+
rawJson = await response.json();
|
|
3603
|
+
} catch (e) {
|
|
3604
|
+
throw new CofheError({
|
|
3605
|
+
code: "DECRYPT_FAILED" /* DecryptFailed */,
|
|
3606
|
+
message: `Failed to parse decrypt status response`,
|
|
3607
|
+
cause: e instanceof Error ? e : void 0,
|
|
3608
|
+
context: {
|
|
3609
|
+
thresholdNetworkUrl,
|
|
3610
|
+
requestId
|
|
3611
|
+
}
|
|
3612
|
+
});
|
|
3613
|
+
}
|
|
3614
|
+
const statusResponse = assertDecryptStatusResponseV2(rawJson);
|
|
3615
|
+
if (statusResponse.status === "COMPLETED") {
|
|
3616
|
+
if (statusResponse.is_succeed === false) {
|
|
3617
|
+
const errorMessage = statusResponse.error_message || "Unknown error";
|
|
3618
|
+
throw new CofheError({
|
|
3619
|
+
code: "DECRYPT_FAILED" /* DecryptFailed */,
|
|
3620
|
+
message: `decrypt request failed: ${errorMessage}`,
|
|
3621
|
+
context: {
|
|
3622
|
+
thresholdNetworkUrl,
|
|
3623
|
+
requestId,
|
|
3624
|
+
statusResponse
|
|
3625
|
+
}
|
|
3626
|
+
});
|
|
3627
|
+
}
|
|
3628
|
+
if (statusResponse.error_message) {
|
|
3629
|
+
throw new CofheError({
|
|
3630
|
+
code: "DECRYPT_FAILED" /* DecryptFailed */,
|
|
3631
|
+
message: `decrypt request failed: ${statusResponse.error_message}`,
|
|
3632
|
+
context: {
|
|
3633
|
+
thresholdNetworkUrl,
|
|
3634
|
+
requestId,
|
|
3635
|
+
statusResponse
|
|
3636
|
+
}
|
|
3637
|
+
});
|
|
3638
|
+
}
|
|
3639
|
+
if (!Array.isArray(statusResponse.decrypted)) {
|
|
3640
|
+
throw new CofheError({
|
|
3641
|
+
code: "DECRYPT_RETURNED_NULL" /* DecryptReturnedNull */,
|
|
3642
|
+
message: "decrypt completed but response missing <decrypted> byte array",
|
|
3643
|
+
context: {
|
|
3644
|
+
thresholdNetworkUrl,
|
|
3645
|
+
requestId,
|
|
3646
|
+
statusResponse
|
|
3647
|
+
}
|
|
3648
|
+
});
|
|
3649
|
+
}
|
|
3650
|
+
const decryptedValue = parseDecryptedBytesToBigInt(statusResponse.decrypted);
|
|
3651
|
+
const signature = normalizeTnSignature(statusResponse.signature);
|
|
3652
|
+
return { decryptedValue, signature };
|
|
3653
|
+
}
|
|
3654
|
+
await new Promise((resolve) => setTimeout(resolve, intervalMs));
|
|
3655
|
+
attemptIndex += 1;
|
|
3656
|
+
}
|
|
3657
|
+
throw new CofheError({
|
|
3658
|
+
code: "DECRYPT_FAILED" /* DecryptFailed */,
|
|
3659
|
+
message: "Polling loop exited unexpectedly",
|
|
3660
|
+
context: {
|
|
3661
|
+
thresholdNetworkUrl,
|
|
3662
|
+
requestId
|
|
3663
|
+
}
|
|
3664
|
+
});
|
|
3665
|
+
}
|
|
3666
|
+
async function tnDecryptV2(params) {
|
|
3667
|
+
const { thresholdNetworkUrl, ctHash, chainId, permission, onPoll } = params;
|
|
3668
|
+
const requestId = await submitDecryptRequestV2(thresholdNetworkUrl, ctHash, chainId, permission);
|
|
3669
|
+
return await pollDecryptStatusV2(thresholdNetworkUrl, requestId, onPoll);
|
|
3670
|
+
}
|
|
3671
|
+
|
|
3672
|
+
// core/decrypt/decryptForTxBuilder.ts
|
|
3673
|
+
var DecryptForTxBuilder = class extends BaseBuilder {
|
|
3674
|
+
ctHash;
|
|
3675
|
+
permitHash;
|
|
3676
|
+
permit;
|
|
3677
|
+
permitSelection = "unset";
|
|
3678
|
+
pollCallback;
|
|
3679
|
+
constructor(params) {
|
|
3680
|
+
super({
|
|
3681
|
+
config: params.config,
|
|
3682
|
+
publicClient: params.publicClient,
|
|
3683
|
+
walletClient: params.walletClient,
|
|
3684
|
+
chainId: params.chainId,
|
|
3685
|
+
account: params.account,
|
|
3686
|
+
requireConnected: params.requireConnected
|
|
3687
|
+
});
|
|
3688
|
+
this.ctHash = params.ctHash;
|
|
3689
|
+
}
|
|
3690
|
+
setChainId(chainId) {
|
|
3691
|
+
this.chainId = chainId;
|
|
3692
|
+
return this;
|
|
3693
|
+
}
|
|
3694
|
+
getChainId() {
|
|
3695
|
+
return this.chainId;
|
|
3696
|
+
}
|
|
3697
|
+
setAccount(account) {
|
|
3698
|
+
this.account = account;
|
|
3699
|
+
return this;
|
|
3700
|
+
}
|
|
3701
|
+
getAccount() {
|
|
3702
|
+
return this.account;
|
|
3703
|
+
}
|
|
3704
|
+
onPoll(callback) {
|
|
3705
|
+
this.pollCallback = callback;
|
|
3706
|
+
return this;
|
|
3707
|
+
}
|
|
3708
|
+
withPermit(permitOrPermitHash) {
|
|
3709
|
+
if (this.permitSelection === "with-permit") {
|
|
3710
|
+
throw new CofheError({
|
|
3711
|
+
code: "INTERNAL_ERROR" /* InternalError */,
|
|
3712
|
+
message: "decryptForTx: withPermit() can only be selected once.",
|
|
3713
|
+
hint: "Choose the permit mode once. If you need a different permit, start a new decryptForTx() builder chain."
|
|
3714
|
+
});
|
|
3715
|
+
}
|
|
3716
|
+
if (this.permitSelection === "without-permit") {
|
|
3717
|
+
throw new CofheError({
|
|
3718
|
+
code: "INTERNAL_ERROR" /* InternalError */,
|
|
3719
|
+
message: "decryptForTx: cannot call withPermit() after withoutPermit() has been selected.",
|
|
3720
|
+
hint: "Choose exactly one permit mode: either call .withPermit(...) or .withoutPermit(), but not both."
|
|
3721
|
+
});
|
|
3722
|
+
}
|
|
3723
|
+
this.permitSelection = "with-permit";
|
|
3724
|
+
if (typeof permitOrPermitHash === "string") {
|
|
3725
|
+
this.permitHash = permitOrPermitHash;
|
|
3726
|
+
this.permit = void 0;
|
|
3727
|
+
} else if (permitOrPermitHash === void 0) {
|
|
3728
|
+
this.permitHash = void 0;
|
|
3729
|
+
this.permit = void 0;
|
|
3730
|
+
} else {
|
|
3731
|
+
this.permit = permitOrPermitHash;
|
|
3732
|
+
this.permitHash = void 0;
|
|
3733
|
+
}
|
|
3734
|
+
return this;
|
|
3735
|
+
}
|
|
3736
|
+
/**
|
|
3737
|
+
* Select "no permit" mode.
|
|
3738
|
+
*
|
|
3739
|
+
* This uses global allowance (no permit required) and sends an empty permission payload to `/decrypt`.
|
|
3740
|
+
*/
|
|
3741
|
+
withoutPermit() {
|
|
3742
|
+
if (this.permitSelection === "without-permit") {
|
|
3743
|
+
throw new CofheError({
|
|
3744
|
+
code: "INTERNAL_ERROR" /* InternalError */,
|
|
3745
|
+
message: "decryptForTx: withoutPermit() can only be selected once.",
|
|
3746
|
+
hint: "Choose the permit mode once. If you need a different mode, start a new decryptForTx() builder chain."
|
|
3747
|
+
});
|
|
3748
|
+
}
|
|
3749
|
+
if (this.permitSelection === "with-permit") {
|
|
3750
|
+
throw new CofheError({
|
|
3751
|
+
code: "INTERNAL_ERROR" /* InternalError */,
|
|
3752
|
+
message: "decryptForTx: cannot call withoutPermit() after withPermit() has been selected.",
|
|
3753
|
+
hint: "Choose exactly one permit mode: either call .withPermit(...) or .withoutPermit(), but not both."
|
|
3754
|
+
});
|
|
3755
|
+
}
|
|
3756
|
+
this.permitSelection = "without-permit";
|
|
3757
|
+
this.permitHash = void 0;
|
|
3758
|
+
this.permit = void 0;
|
|
3759
|
+
return this;
|
|
3760
|
+
}
|
|
3761
|
+
getPermit() {
|
|
3762
|
+
return this.permit;
|
|
3763
|
+
}
|
|
3764
|
+
getPermitHash() {
|
|
3765
|
+
return this.permitHash;
|
|
3766
|
+
}
|
|
3767
|
+
async getThresholdNetworkUrl() {
|
|
3768
|
+
this.assertChainId();
|
|
3769
|
+
return getThresholdNetworkUrlOrThrow(this.config, this.chainId);
|
|
3770
|
+
}
|
|
3771
|
+
async getResolvedPermit() {
|
|
3772
|
+
if (this.permitSelection === "unset") {
|
|
3773
|
+
throw new CofheError({
|
|
3774
|
+
code: "INTERNAL_ERROR" /* InternalError */,
|
|
3775
|
+
message: "decryptForTx: missing permit selection; call withPermit(...) or withoutPermit() before execute().",
|
|
3776
|
+
hint: "Call .withPermit() to use the active permit, or .withoutPermit() for global allowance."
|
|
3777
|
+
});
|
|
3778
|
+
}
|
|
3779
|
+
if (this.permitSelection === "without-permit") {
|
|
3780
|
+
return null;
|
|
3781
|
+
}
|
|
3782
|
+
if (this.permit)
|
|
3783
|
+
return this.permit;
|
|
3784
|
+
this.assertChainId();
|
|
3785
|
+
this.assertAccount();
|
|
3786
|
+
if (this.permitHash) {
|
|
3787
|
+
const permit2 = await permits.getPermit(this.chainId, this.account, this.permitHash);
|
|
3788
|
+
if (!permit2) {
|
|
3789
|
+
throw new CofheError({
|
|
3790
|
+
code: "PERMIT_NOT_FOUND" /* PermitNotFound */,
|
|
3791
|
+
message: `Permit with hash <${this.permitHash}> not found for account <${this.account}> and chainId <${this.chainId}>`,
|
|
3792
|
+
hint: "Ensure the permit exists and is valid.",
|
|
3793
|
+
context: {
|
|
3794
|
+
chainId: this.chainId,
|
|
3795
|
+
account: this.account,
|
|
3796
|
+
permitHash: this.permitHash
|
|
3797
|
+
}
|
|
3798
|
+
});
|
|
3799
|
+
}
|
|
3800
|
+
return permit2;
|
|
3801
|
+
}
|
|
3802
|
+
const permit = await permits.getActivePermit(this.chainId, this.account);
|
|
3803
|
+
if (!permit) {
|
|
3804
|
+
throw new CofheError({
|
|
3805
|
+
code: "PERMIT_NOT_FOUND" /* PermitNotFound */,
|
|
3806
|
+
message: `Active permit not found for chainId <${this.chainId}> and account <${this.account}>`,
|
|
3807
|
+
hint: "Create a permit (e.g. client.permits.createSelf(...)) and/or set it active (client.permits.selectActivePermit(hash)).",
|
|
3808
|
+
context: {
|
|
3809
|
+
chainId: this.chainId,
|
|
3810
|
+
account: this.account
|
|
3811
|
+
}
|
|
3812
|
+
});
|
|
3813
|
+
}
|
|
3814
|
+
return permit;
|
|
3815
|
+
}
|
|
3816
|
+
/**
|
|
3817
|
+
* On hardhat, interact with MockThresholdNetwork contract
|
|
3818
|
+
*/
|
|
3819
|
+
async mocksDecryptForTx(permit) {
|
|
3820
|
+
this.assertPublicClient();
|
|
3821
|
+
const delay = this.config.mocks.decryptDelay;
|
|
3822
|
+
if (delay > 0)
|
|
3823
|
+
await sleep(delay);
|
|
3824
|
+
const result = await cofheMocksDecryptForTx(this.ctHash, 0, permit, this.publicClient);
|
|
3825
|
+
return result;
|
|
3826
|
+
}
|
|
3827
|
+
/**
|
|
3828
|
+
* In the production context, perform a true decryption with the CoFHE coprocessor.
|
|
3829
|
+
*/
|
|
3830
|
+
async productionDecryptForTx(permit) {
|
|
3831
|
+
this.assertChainId();
|
|
3832
|
+
this.assertPublicClient();
|
|
3833
|
+
const thresholdNetworkUrl = await this.getThresholdNetworkUrl();
|
|
3834
|
+
const permission = permit ? PermitUtils.getPermission(permit, true) : null;
|
|
3835
|
+
const { decryptedValue, signature } = await tnDecryptV2({
|
|
3836
|
+
ctHash: this.ctHash,
|
|
3837
|
+
chainId: this.chainId,
|
|
3838
|
+
permission,
|
|
3839
|
+
thresholdNetworkUrl,
|
|
3840
|
+
onPoll: this.pollCallback
|
|
3841
|
+
});
|
|
3842
|
+
return {
|
|
3843
|
+
ctHash: this.ctHash,
|
|
3844
|
+
decryptedValue,
|
|
3845
|
+
signature
|
|
3846
|
+
};
|
|
3847
|
+
}
|
|
3848
|
+
/**
|
|
3849
|
+
* Final step of the decryptForTx process. MUST BE CALLED LAST IN THE CHAIN.
|
|
3850
|
+
*
|
|
3851
|
+
* You must explicitly choose one permit mode before calling `execute()`:
|
|
3852
|
+
* - `withPermit(permit)` / `withPermit(permitHash)` / `withPermit()` (active permit)
|
|
3853
|
+
* - `withoutPermit()` (global allowance)
|
|
3854
|
+
*/
|
|
3855
|
+
async execute() {
|
|
3856
|
+
const permit = await this.getResolvedPermit();
|
|
3857
|
+
if (permit !== null) {
|
|
3858
|
+
PermitUtils.validate(permit);
|
|
3859
|
+
const chainId = permit._signedDomain.chainId;
|
|
3860
|
+
if (chainId === hardhat2.id) {
|
|
3861
|
+
return await this.mocksDecryptForTx(permit);
|
|
3862
|
+
} else {
|
|
3863
|
+
return await this.productionDecryptForTx(permit);
|
|
3864
|
+
}
|
|
3865
|
+
} else {
|
|
3866
|
+
if (!this.chainId) {
|
|
3867
|
+
this.assertPublicClient();
|
|
3868
|
+
this.chainId = await getPublicClientChainID(this.publicClient);
|
|
3869
|
+
}
|
|
3870
|
+
this.assertChainId();
|
|
3871
|
+
if (this.chainId === hardhat2.id) {
|
|
3872
|
+
return await this.mocksDecryptForTx(null);
|
|
3873
|
+
} else {
|
|
3874
|
+
return await this.productionDecryptForTx(null);
|
|
3875
|
+
}
|
|
3876
|
+
}
|
|
3877
|
+
}
|
|
3878
|
+
};
|
|
3879
|
+
var decryptResultSignerAbi = viem.parseAbi(["function decryptResultSigner() view returns (address)"]);
|
|
3880
|
+
async function verifyDecryptResult(handle, cleartext, signature, publicClient) {
|
|
3881
|
+
const expectedSigner = await publicClient.readContract({
|
|
3882
|
+
address: TASK_MANAGER_ADDRESS,
|
|
3883
|
+
abi: decryptResultSignerAbi,
|
|
3884
|
+
functionName: "decryptResultSigner",
|
|
3885
|
+
args: []
|
|
3886
|
+
});
|
|
3887
|
+
if (viem.isAddressEqual(expectedSigner, viem.zeroAddress))
|
|
3888
|
+
return true;
|
|
3889
|
+
const ctHash = BigInt(handle);
|
|
3890
|
+
const messageHash = viem.keccak256(viem.encodePacked(["uint256", "uint256"], [ctHash, cleartext]));
|
|
3891
|
+
try {
|
|
3892
|
+
const recovered = await viem.recoverAddress({ hash: messageHash, signature });
|
|
3893
|
+
return viem.isAddressEqual(recovered, expectedSigner);
|
|
3894
|
+
} catch {
|
|
3895
|
+
return false;
|
|
3896
|
+
}
|
|
3897
|
+
}
|
|
3898
|
+
|
|
3899
|
+
// core/client.ts
|
|
3900
|
+
var InitialConnectStore = {
|
|
3901
|
+
connected: false,
|
|
3902
|
+
connecting: false,
|
|
3903
|
+
connectError: void 0,
|
|
3904
|
+
chainId: void 0,
|
|
3905
|
+
account: void 0,
|
|
3906
|
+
publicClient: void 0,
|
|
3907
|
+
walletClient: void 0
|
|
3908
|
+
};
|
|
3909
|
+
function createCofheClientBase(opts) {
|
|
3910
|
+
const keysStorage = createKeysStore(opts.config.fheKeyStorage);
|
|
3911
|
+
const connectStore = vanilla.createStore(() => InitialConnectStore);
|
|
3912
|
+
let connectAttemptId = 0;
|
|
3913
|
+
const updateConnectState = (partial) => {
|
|
3914
|
+
connectStore.setState((state) => ({ ...state, ...partial }));
|
|
3915
|
+
};
|
|
3916
|
+
const _requireConnected = () => {
|
|
3917
|
+
const state = connectStore.getState();
|
|
3918
|
+
const notConnected = !state.connected || !state.account || !state.chainId || !state.publicClient || !state.walletClient;
|
|
3919
|
+
if (notConnected) {
|
|
3920
|
+
throw new CofheError({
|
|
3921
|
+
code: "NOT_CONNECTED" /* NotConnected */,
|
|
3922
|
+
message: "Client must be connected, account and chainId must be initialized",
|
|
3923
|
+
hint: "Ensure client.connect() has been called and awaited.",
|
|
3924
|
+
context: {
|
|
3925
|
+
connected: state.connected,
|
|
3926
|
+
account: state.account,
|
|
3927
|
+
chainId: state.chainId,
|
|
3928
|
+
publicClient: state.publicClient,
|
|
3929
|
+
walletClient: state.walletClient
|
|
3930
|
+
}
|
|
3931
|
+
});
|
|
3932
|
+
}
|
|
3933
|
+
};
|
|
3934
|
+
async function connect(publicClient, walletClient) {
|
|
3935
|
+
const state = connectStore.getState();
|
|
3936
|
+
if (state.connected && state.publicClient === publicClient && state.walletClient === walletClient)
|
|
3937
|
+
return;
|
|
3938
|
+
connectAttemptId += 1;
|
|
3939
|
+
const localAttemptId = connectAttemptId;
|
|
3940
|
+
updateConnectState({
|
|
3941
|
+
...InitialConnectStore,
|
|
3942
|
+
connecting: true
|
|
3943
|
+
});
|
|
3944
|
+
try {
|
|
3945
|
+
const chainId = await getPublicClientChainID(publicClient);
|
|
3946
|
+
const account = await getWalletClientAccount(walletClient);
|
|
3947
|
+
if (localAttemptId !== connectAttemptId)
|
|
3948
|
+
return;
|
|
3949
|
+
updateConnectState({
|
|
3950
|
+
connected: true,
|
|
3951
|
+
connecting: false,
|
|
3952
|
+
connectError: void 0,
|
|
3953
|
+
chainId,
|
|
3954
|
+
account,
|
|
3955
|
+
publicClient,
|
|
3956
|
+
walletClient
|
|
3957
|
+
});
|
|
3958
|
+
} catch (e) {
|
|
3959
|
+
if (localAttemptId !== connectAttemptId)
|
|
3960
|
+
return;
|
|
3961
|
+
updateConnectState({
|
|
3962
|
+
...InitialConnectStore,
|
|
3963
|
+
connectError: e
|
|
3964
|
+
});
|
|
3965
|
+
throw e;
|
|
3966
|
+
}
|
|
3967
|
+
}
|
|
3968
|
+
function disconnect() {
|
|
3969
|
+
connectAttemptId += 1;
|
|
3970
|
+
updateConnectState({ ...InitialConnectStore });
|
|
3971
|
+
}
|
|
3972
|
+
function encryptInputs(inputs) {
|
|
3973
|
+
const state = connectStore.getState();
|
|
3974
|
+
return new EncryptInputsBuilder({
|
|
3975
|
+
inputs,
|
|
3976
|
+
account: state.account ?? void 0,
|
|
3977
|
+
chainId: state.chainId ?? void 0,
|
|
3978
|
+
config: opts.config,
|
|
3979
|
+
publicClient: state.publicClient ?? void 0,
|
|
3980
|
+
walletClient: state.walletClient ?? void 0,
|
|
3981
|
+
zkvWalletClient: opts.config._internal?.zkvWalletClient,
|
|
3982
|
+
tfhePublicKeyDeserializer: opts.tfhePublicKeyDeserializer,
|
|
3983
|
+
compactPkeCrsDeserializer: opts.compactPkeCrsDeserializer,
|
|
3984
|
+
zkBuilderAndCrsGenerator: opts.zkBuilderAndCrsGenerator,
|
|
3985
|
+
initTfhe: opts.initTfhe,
|
|
3986
|
+
zkProveWorkerFn: opts.zkProveWorkerFn,
|
|
3987
|
+
keysStorage,
|
|
3988
|
+
requireConnected: _requireConnected
|
|
3989
|
+
});
|
|
3990
|
+
}
|
|
3991
|
+
function decryptForView(ctHash, utype) {
|
|
3992
|
+
const state = connectStore.getState();
|
|
3993
|
+
return new DecryptForViewBuilder({
|
|
3994
|
+
ctHash,
|
|
3995
|
+
utype,
|
|
3996
|
+
chainId: state.chainId,
|
|
3997
|
+
account: state.account,
|
|
3998
|
+
config: opts.config,
|
|
3999
|
+
publicClient: state.publicClient,
|
|
4000
|
+
walletClient: state.walletClient,
|
|
4001
|
+
requireConnected: _requireConnected
|
|
4002
|
+
});
|
|
4003
|
+
}
|
|
4004
|
+
function decryptForTx(ctHash) {
|
|
4005
|
+
const state = connectStore.getState();
|
|
4006
|
+
return new DecryptForTxBuilder({
|
|
4007
|
+
ctHash,
|
|
4008
|
+
chainId: state.chainId,
|
|
4009
|
+
account: state.account,
|
|
4010
|
+
config: opts.config,
|
|
4011
|
+
publicClient: state.publicClient,
|
|
4012
|
+
walletClient: state.walletClient,
|
|
4013
|
+
requireConnected: _requireConnected
|
|
4014
|
+
});
|
|
4015
|
+
}
|
|
4016
|
+
function verifyDecryptResult2(handle, cleartext, signature) {
|
|
4017
|
+
_requireConnected();
|
|
4018
|
+
const { publicClient } = connectStore.getState();
|
|
4019
|
+
return verifyDecryptResult(handle, cleartext, signature, publicClient);
|
|
4020
|
+
}
|
|
4021
|
+
const _getChainIdAndAccount = (chainId, account) => {
|
|
4022
|
+
const state = connectStore.getState();
|
|
4023
|
+
const _chainId = chainId ?? state.chainId;
|
|
4024
|
+
const _account = account ?? state.account;
|
|
4025
|
+
if (_chainId == null || _account == null) {
|
|
4026
|
+
throw new CofheError({
|
|
4027
|
+
code: "NOT_CONNECTED" /* NotConnected */,
|
|
4028
|
+
message: "ChainId or account not available.",
|
|
4029
|
+
hint: "Ensure client.connect() has been called, or provide chainId and account explicitly.",
|
|
4030
|
+
context: {
|
|
4031
|
+
chainId: _chainId,
|
|
4032
|
+
account: _account
|
|
4033
|
+
}
|
|
4034
|
+
});
|
|
4035
|
+
}
|
|
4036
|
+
return { chainId: _chainId, account: _account };
|
|
4037
|
+
};
|
|
4038
|
+
const clientPermits = {
|
|
4039
|
+
// Pass through store access
|
|
4040
|
+
getSnapshot: permits.getSnapshot,
|
|
4041
|
+
subscribe: permits.subscribe,
|
|
4042
|
+
// Creation methods (require connection)
|
|
4043
|
+
createSelf: async (options, clients) => {
|
|
4044
|
+
_requireConnected();
|
|
4045
|
+
const { publicClient, walletClient } = clients ?? connectStore.getState();
|
|
4046
|
+
return permits.createSelf(options, publicClient, walletClient);
|
|
4047
|
+
},
|
|
4048
|
+
createSharing: async (options, clients) => {
|
|
4049
|
+
_requireConnected();
|
|
4050
|
+
const { publicClient, walletClient } = clients ?? connectStore.getState();
|
|
4051
|
+
return permits.createSharing(options, publicClient, walletClient);
|
|
4052
|
+
},
|
|
4053
|
+
importShared: async (options, clients) => {
|
|
4054
|
+
_requireConnected();
|
|
4055
|
+
const { publicClient, walletClient } = clients ?? connectStore.getState();
|
|
4056
|
+
return permits.importShared(options, publicClient, walletClient);
|
|
4057
|
+
},
|
|
4058
|
+
// Get or create methods (require connection)
|
|
4059
|
+
getOrCreateSelfPermit: async (chainId, account, options) => {
|
|
4060
|
+
_requireConnected();
|
|
4061
|
+
const { chainId: _chainId, account: _account } = _getChainIdAndAccount(chainId, account);
|
|
4062
|
+
const { publicClient, walletClient } = connectStore.getState();
|
|
4063
|
+
return permits.getOrCreateSelfPermit(publicClient, walletClient, _chainId, _account, options);
|
|
4064
|
+
},
|
|
4065
|
+
getOrCreateSharingPermit: async (options, chainId, account) => {
|
|
4066
|
+
_requireConnected();
|
|
4067
|
+
const { chainId: _chainId, account: _account } = _getChainIdAndAccount(chainId, account);
|
|
4068
|
+
const { publicClient, walletClient } = connectStore.getState();
|
|
4069
|
+
return permits.getOrCreateSharingPermit(publicClient, walletClient, options, _chainId, _account);
|
|
4070
|
+
},
|
|
4071
|
+
// Retrieval methods (auto-fill chainId/account)
|
|
4072
|
+
getPermit: (hash, chainId, account) => {
|
|
4073
|
+
const { chainId: _chainId, account: _account } = _getChainIdAndAccount(chainId, account);
|
|
4074
|
+
return permits.getPermit(_chainId, _account, hash);
|
|
4075
|
+
},
|
|
4076
|
+
getPermits: (chainId, account) => {
|
|
4077
|
+
const { chainId: _chainId, account: _account } = _getChainIdAndAccount(chainId, account);
|
|
4078
|
+
return permits.getPermits(_chainId, _account);
|
|
4079
|
+
},
|
|
4080
|
+
getActivePermit: (chainId, account) => {
|
|
4081
|
+
const { chainId: _chainId, account: _account } = _getChainIdAndAccount(chainId, account);
|
|
4082
|
+
return permits.getActivePermit(_chainId, _account);
|
|
4083
|
+
},
|
|
4084
|
+
getActivePermitHash: (chainId, account) => {
|
|
4085
|
+
const { chainId: _chainId, account: _account } = _getChainIdAndAccount(chainId, account);
|
|
4086
|
+
return permits.getActivePermitHash(_chainId, _account);
|
|
4087
|
+
},
|
|
4088
|
+
// Mutation methods (auto-fill chainId/account)
|
|
4089
|
+
selectActivePermit: (hash, chainId, account) => {
|
|
4090
|
+
const { chainId: _chainId, account: _account } = _getChainIdAndAccount(chainId, account);
|
|
4091
|
+
return permits.selectActivePermit(_chainId, _account, hash);
|
|
4092
|
+
},
|
|
4093
|
+
removePermit: async (hash, chainId, account) => {
|
|
4094
|
+
const { chainId: _chainId, account: _account } = _getChainIdAndAccount(chainId, account);
|
|
4095
|
+
return permits.removePermit(_chainId, _account, hash);
|
|
4096
|
+
},
|
|
4097
|
+
removeActivePermit: async (chainId, account) => {
|
|
4098
|
+
const { chainId: _chainId, account: _account } = _getChainIdAndAccount(chainId, account);
|
|
4099
|
+
return permits.removeActivePermit(_chainId, _account);
|
|
4100
|
+
},
|
|
4101
|
+
// Utils (no context needed)
|
|
4102
|
+
getHash: permits.getHash,
|
|
4103
|
+
serialize: permits.serialize,
|
|
4104
|
+
deserialize: permits.deserialize
|
|
4105
|
+
};
|
|
4106
|
+
return {
|
|
4107
|
+
// Zustand reactive accessors (don't export store directly to prevent mutation)
|
|
4108
|
+
getSnapshot: connectStore.getState,
|
|
4109
|
+
subscribe: connectStore.subscribe,
|
|
4110
|
+
// flags (read-only: reflect snapshot)
|
|
4111
|
+
get connection() {
|
|
4112
|
+
return connectStore.getState();
|
|
4113
|
+
},
|
|
4114
|
+
get connected() {
|
|
4115
|
+
return connectStore.getState().connected;
|
|
4116
|
+
},
|
|
4117
|
+
get connecting() {
|
|
4118
|
+
return connectStore.getState().connecting;
|
|
4119
|
+
},
|
|
4120
|
+
// config & platform-specific (read-only)
|
|
4121
|
+
config: opts.config,
|
|
4122
|
+
connect,
|
|
4123
|
+
disconnect,
|
|
4124
|
+
encryptInputs,
|
|
4125
|
+
decryptForView,
|
|
4126
|
+
/**
|
|
4127
|
+
* @deprecated Use `decryptForView` instead. Kept for backward compatibility.
|
|
4128
|
+
*/
|
|
4129
|
+
decryptHandle: decryptForView,
|
|
4130
|
+
decryptForTx,
|
|
4131
|
+
verifyDecryptResult: verifyDecryptResult2,
|
|
4132
|
+
permits: clientPermits
|
|
4133
|
+
// Add SDK-specific methods below that require connection
|
|
4134
|
+
// Example:
|
|
4135
|
+
// async encryptData(data: unknown) {
|
|
4136
|
+
// requireConnected();
|
|
4137
|
+
// // Use state.publicClient and state.walletClient for implementation
|
|
4138
|
+
// },
|
|
4139
|
+
};
|
|
4140
|
+
}
|
|
4141
|
+
var memoryStorage = {};
|
|
4142
|
+
var createNodeStorage = () => {
|
|
4143
|
+
return {
|
|
4144
|
+
getItem: async (name) => {
|
|
4145
|
+
try {
|
|
4146
|
+
const storageDir = path.join(process.env.HOME || process.env.USERPROFILE || ".", ".cofhesdk");
|
|
4147
|
+
await fs.promises.mkdir(storageDir, { recursive: true });
|
|
4148
|
+
const filePath = path.join(storageDir, `${name}.json`);
|
|
4149
|
+
const data = await fs.promises.readFile(filePath, "utf8").catch(() => null);
|
|
4150
|
+
return data ? JSON.parse(data) : null;
|
|
4151
|
+
} catch (e) {
|
|
4152
|
+
console.warn("Node.js filesystem modules not available, falling back to memory storage" + e);
|
|
4153
|
+
return memoryStorage[name] || null;
|
|
4154
|
+
}
|
|
4155
|
+
},
|
|
4156
|
+
setItem: async (name, value) => {
|
|
4157
|
+
try {
|
|
4158
|
+
const storageDir = path.join(process.env.HOME || process.env.USERPROFILE || ".", ".cofhesdk");
|
|
4159
|
+
await fs.promises.mkdir(storageDir, { recursive: true });
|
|
4160
|
+
const filePath = path.join(storageDir, `${name}.json`);
|
|
4161
|
+
await fs.promises.writeFile(filePath, JSON.stringify(value));
|
|
4162
|
+
} catch (e) {
|
|
4163
|
+
console.warn("Node.js filesystem modules not available, falling back to memory storage" + e);
|
|
4164
|
+
memoryStorage[name] = JSON.stringify(value);
|
|
4165
|
+
}
|
|
4166
|
+
},
|
|
4167
|
+
removeItem: async (name) => {
|
|
4168
|
+
try {
|
|
4169
|
+
const storageDir = path.join(process.env.HOME || process.env.USERPROFILE || ".", ".cofhesdk");
|
|
4170
|
+
const filePath = path.join(storageDir, `${name}.json`);
|
|
4171
|
+
await fs.promises.unlink(filePath).catch(() => {
|
|
4172
|
+
});
|
|
4173
|
+
} catch (e) {
|
|
4174
|
+
console.warn("Node.js filesystem modules not available, falling back to memory storage" + e);
|
|
4175
|
+
delete memoryStorage[name];
|
|
4176
|
+
}
|
|
4177
|
+
}
|
|
4178
|
+
};
|
|
4179
|
+
};
|
|
4180
|
+
var tfheInitialized = false;
|
|
4181
|
+
async function initTfhe() {
|
|
4182
|
+
if (tfheInitialized)
|
|
4183
|
+
return false;
|
|
4184
|
+
await nodeTfhe.init_panic_hook();
|
|
4185
|
+
tfheInitialized = true;
|
|
4186
|
+
return true;
|
|
4187
|
+
}
|
|
4188
|
+
var fromHexString2 = (hexString) => {
|
|
4189
|
+
const cleanString = hexString.length % 2 === 1 ? `0${hexString}` : hexString;
|
|
4190
|
+
const arr = cleanString.replace(/^0x/, "").match(/.{1,2}/g);
|
|
4191
|
+
if (!arr)
|
|
4192
|
+
return new Uint8Array();
|
|
4193
|
+
return new Uint8Array(arr.map((byte) => parseInt(byte, 16)));
|
|
4194
|
+
};
|
|
4195
|
+
var tfhePublicKeyDeserializer = (buff) => {
|
|
4196
|
+
nodeTfhe.TfheCompactPublicKey.deserialize(fromHexString2(buff));
|
|
4197
|
+
};
|
|
4198
|
+
var compactPkeCrsDeserializer = (buff) => {
|
|
4199
|
+
nodeTfhe.CompactPkeCrs.deserialize(fromHexString2(buff));
|
|
4200
|
+
};
|
|
4201
|
+
var zkBuilderAndCrsGenerator = (fhe, crs) => {
|
|
4202
|
+
const fhePublicKey = nodeTfhe.TfheCompactPublicKey.deserialize(fromHexString2(fhe));
|
|
4203
|
+
const zkBuilder = nodeTfhe.ProvenCompactCiphertextList.builder(fhePublicKey);
|
|
4204
|
+
const zkCrs = nodeTfhe.CompactPkeCrs.deserialize(fromHexString2(crs));
|
|
4205
|
+
return { zkBuilder, zkCrs };
|
|
4206
|
+
};
|
|
4207
|
+
function createCofheConfig(config) {
|
|
4208
|
+
return createCofheConfigBase({
|
|
4209
|
+
environment: "node",
|
|
4210
|
+
...config,
|
|
4211
|
+
fheKeyStorage: config.fheKeyStorage === null ? null : config.fheKeyStorage ?? createNodeStorage()
|
|
4212
|
+
});
|
|
4213
|
+
}
|
|
4214
|
+
function createCofheClient(config) {
|
|
4215
|
+
return createCofheClientBase({
|
|
4216
|
+
config,
|
|
4217
|
+
zkBuilderAndCrsGenerator,
|
|
4218
|
+
tfhePublicKeyDeserializer,
|
|
4219
|
+
compactPkeCrsDeserializer,
|
|
4220
|
+
initTfhe
|
|
4221
|
+
});
|
|
4222
|
+
}
|
|
4223
|
+
|
|
4224
|
+
exports.createCofheClient = createCofheClient;
|
|
4225
|
+
exports.createCofheConfig = createCofheConfig;
|