@cofhe/sdk 0.6.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +49 -0
- package/acps/acp.ts +411 -0
- package/acps/index.ts +70 -0
- package/{permits → acps}/onchain-utils.ts +49 -24
- package/acps/sealing.ts +90 -0
- package/acps/signature.ts +89 -0
- package/acps/store.ts +172 -0
- package/acps/test/acp.test.ts +615 -0
- package/acps/test/localstorage.test.ts +105 -0
- package/acps/test/sealing.test.ts +77 -0
- package/acps/test/store.test.ts +88 -0
- package/acps/test/validation.test.ts +361 -0
- package/acps/test-utils.ts +32 -0
- package/acps/types.ts +252 -0
- package/acps/validation.ts +392 -0
- package/adapters/test/ethers5.test.ts +4 -1
- package/adapters/test/ethers6.test.ts +4 -1
- package/adapters/test/wagmi.test.ts +5 -2
- package/chains/chains/stagingCofhe.ts +21 -0
- package/chains/index.ts +3 -1
- package/chains/test/chains.test.ts +2 -1
- package/core/acps.ts +625 -0
- package/core/client.ts +136 -39
- package/core/clientTypes.ts +52 -41
- package/core/config.ts +66 -5
- package/core/debug.ts +72 -0
- package/core/decrypt/MockThresholdNetworkAbi.ts +20 -11
- package/core/decrypt/apiError.ts +104 -0
- package/core/decrypt/cofheMocksDecryptForTx.ts +11 -11
- package/core/decrypt/cofheMocksDecryptForView.ts +7 -7
- package/core/decrypt/decryptForTxBuilder.ts +102 -102
- package/core/decrypt/decryptForViewBuilder.ts +90 -90
- package/core/decrypt/submitRetry.ts +38 -30
- package/core/decrypt/tnDecryptV1.ts +5 -5
- package/core/decrypt/tnDecryptV2.ts +45 -34
- package/core/decrypt/tnSealOutputV1.ts +4 -3
- package/core/decrypt/tnSealOutputV2.ts +44 -31
- package/core/encrypt/cofheMocksZkVerifySign.ts +59 -74
- package/core/encrypt/encryptInputsBuilder.ts +86 -52
- package/core/encrypt/zkPackProveVerify.ts +25 -18
- package/core/error.ts +34 -6
- package/core/index.ts +14 -14
- package/core/test/acpDefaults.test.ts +52 -0
- package/core/test/acps.test.ts +596 -0
- package/core/test/apiError.test.ts +130 -0
- package/core/test/client.test.ts +22 -19
- package/core/test/config.test.ts +25 -5
- package/core/test/decrypt.test.ts +40 -35
- package/core/test/decryptBuilders.test.ts +68 -68
- package/core/test/decryptErrorCodes.test.ts +217 -0
- package/core/test/encryptInputsBuilder.test.ts +72 -41
- package/core/test/pollCallbacks.test.ts +89 -18
- package/core/test/stagingRedirect.ts +18 -0
- package/core/test/submitRetry.test.ts +182 -0
- package/core/types.ts +9 -69
- package/dist/acp-Wi6isVQI.d.cts +407 -0
- package/dist/acp-Wi6isVQI.d.ts +407 -0
- package/dist/acps.cjs +1081 -0
- package/dist/acps.d.cts +480 -0
- package/dist/acps.d.ts +480 -0
- package/dist/acps.js +2 -0
- package/dist/chains.cjs +14 -1
- package/dist/chains.d.cts +30 -1
- package/dist/chains.d.ts +30 -1
- package/dist/chains.js +1 -1
- package/dist/{chunk-PE5V5CCV.js → chunk-43USWPEH.js} +1000 -604
- package/dist/{chunk-MTRAXQXC.js → chunk-N6IDQRRU.js} +14 -2
- package/dist/chunk-Q7CBWGQX.js +1029 -0
- package/dist/{clientTypes-CyUvRRzA.d.ts → clientTypes-BN3nbzYM.d.ts} +342 -165
- package/dist/{clientTypes-BDy1qIBu.d.cts → clientTypes-CYZjFznO.d.cts} +342 -165
- package/dist/core.cjs +1431 -1026
- package/dist/core.d.cts +55 -10
- package/dist/core.d.ts +55 -10
- package/dist/core.js +3 -3
- package/dist/node.cjs +1356 -976
- package/dist/node.d.cts +2 -2
- package/dist/node.d.ts +2 -2
- package/dist/node.js +3 -3
- package/dist/web.cjs +1356 -976
- package/dist/web.d.cts +2 -2
- package/dist/web.d.ts +2 -2
- package/dist/web.js +3 -3
- package/node/test/inherited.test.ts +75 -65
- package/node/test/tfheinit.test.ts +23 -8
- package/package.json +6 -6
- package/web/test/client.web.test.ts +5 -1
- package/web/test/inherited.web.test.ts +75 -65
- package/web/test/tfheinit.web.test.ts +14 -5
- package/web/test/worker.config.web.test.ts +38 -23
- package/web/test/worker.output.web.test.ts +25 -24
- package/core/encrypt/encryptUtils.ts +0 -67
- package/core/permits.ts +0 -206
- package/core/test/permits.test.ts +0 -534
- package/dist/chunk-VB62WYPL.js +0 -978
- package/dist/permit-DnVMDT5h.d.cts +0 -376
- package/dist/permit-DnVMDT5h.d.ts +0 -376
- package/dist/permits.cjs +0 -1026
- package/dist/permits.d.cts +0 -353
- package/dist/permits.d.ts +0 -353
- package/dist/permits.js +0 -2
- package/permits/index.ts +0 -68
- package/permits/permit.ts +0 -385
- package/permits/sealing.ts +0 -131
- package/permits/signature.ts +0 -79
- package/permits/store.ts +0 -157
- package/permits/test/localstorage.test.ts +0 -113
- package/permits/test/permit.test.ts +0 -557
- package/permits/test/sealing.test.ts +0 -84
- package/permits/test/store.test.ts +0 -88
- package/permits/test/validation.test.ts +0 -361
- package/permits/test-utils.ts +0 -28
- package/permits/types.ts +0 -204
- package/permits/validation.ts +0 -327
- /package/{permits → acps}/utils.ts +0 -0
package/dist/node.cjs
CHANGED
|
@@ -24,7 +24,8 @@ var CofheError = class _CofheError extends Error {
|
|
|
24
24
|
cause;
|
|
25
25
|
hint;
|
|
26
26
|
context;
|
|
27
|
-
|
|
27
|
+
apiErrorCode;
|
|
28
|
+
constructor({ code, message, cause, hint, context, apiErrorCode }) {
|
|
28
29
|
const fullMessage = cause ? `${message} | Caused by: ${cause.message}` : message;
|
|
29
30
|
super(fullMessage);
|
|
30
31
|
this.name = "CofheError";
|
|
@@ -32,6 +33,7 @@ var CofheError = class _CofheError extends Error {
|
|
|
32
33
|
this.cause = cause;
|
|
33
34
|
this.hint = hint;
|
|
34
35
|
this.context = context;
|
|
36
|
+
this.apiErrorCode = apiErrorCode;
|
|
35
37
|
if (Error.captureStackTrace) {
|
|
36
38
|
Error.captureStackTrace(this, _CofheError);
|
|
37
39
|
}
|
|
@@ -50,6 +52,7 @@ var CofheError = class _CofheError extends Error {
|
|
|
50
52
|
message: wrapperError?.message ?? "An internal error occurred",
|
|
51
53
|
hint: wrapperError?.hint,
|
|
52
54
|
context: wrapperError?.context,
|
|
55
|
+
apiErrorCode: wrapperError?.apiErrorCode,
|
|
53
56
|
cause
|
|
54
57
|
});
|
|
55
58
|
}
|
|
@@ -63,6 +66,7 @@ var CofheError = class _CofheError extends Error {
|
|
|
63
66
|
message: this.message,
|
|
64
67
|
hint: this.hint,
|
|
65
68
|
context: this.context,
|
|
69
|
+
apiErrorCode: this.apiErrorCode,
|
|
66
70
|
cause: this.cause ? {
|
|
67
71
|
name: this.cause.name,
|
|
68
72
|
message: this.cause.message,
|
|
@@ -75,7 +79,8 @@ var CofheError = class _CofheError extends Error {
|
|
|
75
79
|
* Returns a human-readable string representation of the error
|
|
76
80
|
*/
|
|
77
81
|
toString() {
|
|
78
|
-
const
|
|
82
|
+
const codeSuffix = this.apiErrorCode ? ` (api: ${this.apiErrorCode})` : "";
|
|
83
|
+
const parts = [`${this.name} [${this.code}]${codeSuffix}: ${this.message}`];
|
|
79
84
|
if (this.hint) {
|
|
80
85
|
parts.push(`Hint: ${this.hint}`);
|
|
81
86
|
}
|
|
@@ -311,18 +316,19 @@ var constructZkPoKMetadata = (accountAddr, securityZone, chainId) => {
|
|
|
311
316
|
metadata.set(chainIdBytes, 1 + accountBytes.length);
|
|
312
317
|
return metadata;
|
|
313
318
|
};
|
|
314
|
-
var
|
|
319
|
+
var zkVerifyBatch = async (verifierUrl, serializedBytes, address, securityZone, chainId, consumingContract) => {
|
|
315
320
|
const packed_list = toHexString(serializedBytes);
|
|
316
321
|
const sz_byte = new Uint8Array([securityZone]);
|
|
317
322
|
const payload = {
|
|
318
323
|
packed_list,
|
|
319
324
|
account_addr: address,
|
|
320
325
|
security_zone: sz_byte[0],
|
|
321
|
-
chain_id: chainId
|
|
326
|
+
chain_id: chainId,
|
|
327
|
+
contract_address: consumingContract
|
|
322
328
|
};
|
|
323
329
|
const body = JSON.stringify(payload);
|
|
324
330
|
try {
|
|
325
|
-
const response = await fetch(`${verifierUrl}/
|
|
331
|
+
const response = await fetch(`${verifierUrl}/verifyBatch`, {
|
|
326
332
|
method: "POST",
|
|
327
333
|
headers: {
|
|
328
334
|
"Content-Type": "application/json"
|
|
@@ -333,26 +339,25 @@ var zkVerify = async (verifierUrl, serializedBytes, address, securityZone, chain
|
|
|
333
339
|
const errorBody = await response.text();
|
|
334
340
|
throw new CofheError({
|
|
335
341
|
code: "ZK_VERIFY_FAILED" /* ZkVerifyFailed */,
|
|
336
|
-
message: `HTTP error! ZK proof verification failed - ${errorBody}`
|
|
342
|
+
message: `HTTP error! ZK batch proof verification failed - ${errorBody}`
|
|
337
343
|
});
|
|
338
344
|
}
|
|
339
345
|
const json = await response.json();
|
|
340
346
|
if (json.status !== "success") {
|
|
341
347
|
throw new CofheError({
|
|
342
348
|
code: "ZK_VERIFY_FAILED" /* ZkVerifyFailed */,
|
|
343
|
-
message: `ZK proof verification response malformed - ${json.error}`
|
|
349
|
+
message: `ZK batch proof verification response malformed - ${json.error}`
|
|
344
350
|
});
|
|
345
351
|
}
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
});
|
|
352
|
+
const { ciphertexts, signature, recid } = json.data;
|
|
353
|
+
return {
|
|
354
|
+
outputs: ciphertexts,
|
|
355
|
+
signature: concatSigRecid(signature, recid)
|
|
356
|
+
};
|
|
352
357
|
} catch (e) {
|
|
353
358
|
throw new CofheError({
|
|
354
359
|
code: "ZK_VERIFY_FAILED" /* ZkVerifyFailed */,
|
|
355
|
-
message: `ZK proof verification failed`,
|
|
360
|
+
message: `ZK batch proof verification failed`,
|
|
356
361
|
cause: e instanceof Error ? e : void 0
|
|
357
362
|
});
|
|
358
363
|
}
|
|
@@ -597,67 +602,59 @@ async function insertCtHashes(items, walletClient) {
|
|
|
597
602
|
});
|
|
598
603
|
}
|
|
599
604
|
}
|
|
600
|
-
async function
|
|
601
|
-
let signatures = [];
|
|
602
|
-
let encInputSignerClient;
|
|
605
|
+
async function createBatchProofSignature(items, securityZone, account, consumingContract) {
|
|
603
606
|
try {
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
607
|
+
const itemHashes = items.map(
|
|
608
|
+
(item) => viem.keccak256(
|
|
609
|
+
viem.encodePacked(
|
|
610
|
+
["uint256", "uint8", "uint8", "address", "uint256", "address"],
|
|
611
|
+
[
|
|
612
|
+
BigInt(item.ctHash),
|
|
613
|
+
item.utype,
|
|
614
|
+
securityZone,
|
|
615
|
+
account,
|
|
616
|
+
BigInt(chains.hardhat.id),
|
|
617
|
+
consumingContract
|
|
618
|
+
]
|
|
619
|
+
)
|
|
620
|
+
)
|
|
621
|
+
);
|
|
622
|
+
const batchDigest = viem.keccak256(
|
|
623
|
+
viem.encodePacked(
|
|
624
|
+
itemHashes.map(() => "bytes32"),
|
|
625
|
+
itemHashes
|
|
626
|
+
)
|
|
627
|
+
);
|
|
628
|
+
return await accounts.sign({
|
|
629
|
+
hash: batchDigest,
|
|
630
|
+
privateKey: MOCKS_ZK_VERIFIER_SIGNER_PRIVATE_KEY,
|
|
631
|
+
to: "hex"
|
|
613
632
|
});
|
|
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
633
|
} catch (err) {
|
|
630
634
|
throw new CofheError({
|
|
631
635
|
code: "ZK_MOCKS_CREATE_PROOF_SIGNATURE_FAILED" /* ZkMocksCreateProofSignatureFailed */,
|
|
632
|
-
message: `mockZkVerifySign
|
|
636
|
+
message: `mockZkVerifySign createBatchProofSignature failed while signing the batch digest`,
|
|
633
637
|
cause: err instanceof Error ? err : void 0,
|
|
634
638
|
context: {
|
|
635
639
|
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
|
|
640
|
+
securityZone,
|
|
641
|
+
consumingContract
|
|
647
642
|
}
|
|
648
643
|
});
|
|
649
644
|
}
|
|
650
|
-
return signatures;
|
|
651
645
|
}
|
|
652
|
-
async function cofheMocksZkVerifySign(items, account, securityZone, publicClient, walletClient, zkvWalletClient) {
|
|
646
|
+
async function cofheMocksZkVerifySign(items, account, securityZone, consumingContract, publicClient, walletClient, zkvWalletClient) {
|
|
653
647
|
const _walletClient = zkvWalletClient ?? createMockZkVerifierSigner();
|
|
654
648
|
const encryptableItems = await calcCtHashes(items, account, securityZone, publicClient);
|
|
655
649
|
await insertCtHashes(encryptableItems, _walletClient);
|
|
656
|
-
const
|
|
657
|
-
return
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
650
|
+
const signature = await createBatchProofSignature(encryptableItems, securityZone, account, consumingContract);
|
|
651
|
+
return {
|
|
652
|
+
outputs: encryptableItems.map((item) => ({
|
|
653
|
+
ct_hash: item.ctHash.toString(),
|
|
654
|
+
ct_type: item.utype
|
|
655
|
+
})),
|
|
656
|
+
signature
|
|
657
|
+
};
|
|
661
658
|
}
|
|
662
659
|
var EnvironmentSchema = zod.z.enum(["MOCK", "TESTNET", "MAINNET"]);
|
|
663
660
|
var CofheChainSchema = zod.z.object({
|
|
@@ -700,8 +697,8 @@ var CofheConfigSchema = zod.z.object({
|
|
|
700
697
|
environment: zod.z.enum(["node", "hardhat", "web", "react"]).optional().default("node"),
|
|
701
698
|
/** List of supported chain configurations */
|
|
702
699
|
supportedChains: zod.z.array(zod.z.custom()),
|
|
703
|
-
/** Default
|
|
704
|
-
|
|
700
|
+
/** Default acp expiration in seconds, default is 30 days */
|
|
701
|
+
defaultACPExpiration: zod.z.number().optional().default(60 * 60 * 24 * 30),
|
|
705
702
|
/** Storage method for fhe keys (defaults to indexedDB on web, filesystem on node) */
|
|
706
703
|
fheKeyStorage: zod.z.object({
|
|
707
704
|
getItem: zod.z.custom((val) => typeof val === "function", {
|
|
@@ -716,6 +713,12 @@ var CofheConfigSchema = zod.z.object({
|
|
|
716
713
|
}).or(zod.z.null()).default(null),
|
|
717
714
|
/** Whether to use Web Workers for ZK proof generation (web platform only) */
|
|
718
715
|
useWorkers: zod.z.boolean().optional().default(true),
|
|
716
|
+
/** ACP acp defaults */
|
|
717
|
+
acp: zod.z.object({
|
|
718
|
+
defaultRevoker: zod.z.custom().optional(),
|
|
719
|
+
defaultContractScopes: zod.z.custom().optional(),
|
|
720
|
+
sharingRegistry: zod.z.custom().optional()
|
|
721
|
+
}).optional().default({}),
|
|
719
722
|
/** Mocks configs */
|
|
720
723
|
mocks: zod.z.object({
|
|
721
724
|
decryptDelay: zod.z.number().optional().default(0),
|
|
@@ -726,8 +729,19 @@ var CofheConfigSchema = zod.z.object({
|
|
|
726
729
|
zkvWalletClient: zod.z.any().optional()
|
|
727
730
|
}).optional()
|
|
728
731
|
});
|
|
732
|
+
var RENAMED_COFHE_CONFIG_KEYS = {
|
|
733
|
+
defaultPermitExpiration: "defaultACPExpiration"
|
|
734
|
+
};
|
|
735
|
+
function assertNoRenamedConfigKeys(config, renamedKeys, label) {
|
|
736
|
+
const stale = Object.keys(config).filter((key) => key in renamedKeys);
|
|
737
|
+
if (stale.length === 0)
|
|
738
|
+
return;
|
|
739
|
+
const renames = stale.map((key) => `\`${key}\` is now \`${renamedKeys[key]}\``).join("; ");
|
|
740
|
+
throw new Error(`Invalid ${label}: ${renames}. See the v0.7.0 migration guide.`);
|
|
741
|
+
}
|
|
729
742
|
function createCofheConfigBase(config) {
|
|
730
|
-
|
|
743
|
+
assertNoRenamedConfigKeys(config, RENAMED_COFHE_CONFIG_KEYS, "cofhe configuration");
|
|
744
|
+
const result = CofheConfigSchema.strict().safeParse(config);
|
|
731
745
|
if (!result.success) {
|
|
732
746
|
throw new Error(`Invalid cofhe configuration: ${zod.z.prettifyError(result.error)}`, { cause: result.error });
|
|
733
747
|
}
|
|
@@ -1076,9 +1090,9 @@ var BaseBuilder = class {
|
|
|
1076
1090
|
// core/encrypt/encryptInputsBuilder.ts
|
|
1077
1091
|
var EncryptInputsBuilder = class extends BaseBuilder {
|
|
1078
1092
|
securityZone;
|
|
1093
|
+
consumingContract;
|
|
1079
1094
|
stepCallback;
|
|
1080
1095
|
inputItems;
|
|
1081
|
-
hpp = false;
|
|
1082
1096
|
zkvWalletClient;
|
|
1083
1097
|
tfhePublicKeyDeserializer;
|
|
1084
1098
|
compactPkeCrsDeserializer;
|
|
@@ -1145,20 +1159,6 @@ var EncryptInputsBuilder = class extends BaseBuilder {
|
|
|
1145
1159
|
this.keysStorage = params.keysStorage;
|
|
1146
1160
|
this.useWorker = params.config?.useWorkers ?? true;
|
|
1147
1161
|
}
|
|
1148
|
-
/**
|
|
1149
|
-
* @param account - Account that will create the tx using the encrypted inputs.
|
|
1150
|
-
*
|
|
1151
|
-
* If not provided, the account will be fetched from the connected walletClient.
|
|
1152
|
-
*
|
|
1153
|
-
* Example:
|
|
1154
|
-
* ```typescript
|
|
1155
|
-
* const encrypted = await encryptInputs([Encryptable.uint128(10n)])
|
|
1156
|
-
* .setAccount("0x123")
|
|
1157
|
-
* .execute();
|
|
1158
|
-
* ```
|
|
1159
|
-
*
|
|
1160
|
-
* @returns The chainable EncryptInputsBuilder instance.
|
|
1161
|
-
*/
|
|
1162
1162
|
setAccount(account) {
|
|
1163
1163
|
this.account = account;
|
|
1164
1164
|
return this;
|
|
@@ -1166,20 +1166,6 @@ var EncryptInputsBuilder = class extends BaseBuilder {
|
|
|
1166
1166
|
getAccount() {
|
|
1167
1167
|
return this.account;
|
|
1168
1168
|
}
|
|
1169
|
-
/**
|
|
1170
|
-
* @param chainId - Chain that will consume the encrypted inputs.
|
|
1171
|
-
*
|
|
1172
|
-
* If not provided, the chainId will be fetched from the connected publicClient.
|
|
1173
|
-
*
|
|
1174
|
-
* Example:
|
|
1175
|
-
* ```typescript
|
|
1176
|
-
* const encrypted = await encryptInputs([Encryptable.uint128(10n)])
|
|
1177
|
-
* .setChainId(11155111)
|
|
1178
|
-
* .execute();
|
|
1179
|
-
* ```
|
|
1180
|
-
*
|
|
1181
|
-
* @returns The chainable EncryptInputsBuilder instance.
|
|
1182
|
-
*/
|
|
1183
1169
|
setChainId(chainId) {
|
|
1184
1170
|
this.chainId = chainId;
|
|
1185
1171
|
return this;
|
|
@@ -1187,20 +1173,6 @@ var EncryptInputsBuilder = class extends BaseBuilder {
|
|
|
1187
1173
|
getChainId() {
|
|
1188
1174
|
return this.chainId;
|
|
1189
1175
|
}
|
|
1190
|
-
/**
|
|
1191
|
-
* @param securityZone - Security zone to encrypt the inputs for.
|
|
1192
|
-
*
|
|
1193
|
-
* If not provided, the default securityZone 0 will be used.
|
|
1194
|
-
*
|
|
1195
|
-
* Example:
|
|
1196
|
-
* ```typescript
|
|
1197
|
-
* const encrypted = await encryptInputs([Encryptable.uint128(10n)])
|
|
1198
|
-
* .setSecurityZone(1)
|
|
1199
|
-
* .execute();
|
|
1200
|
-
* ```
|
|
1201
|
-
*
|
|
1202
|
-
* @returns The chainable EncryptInputsBuilder instance.
|
|
1203
|
-
*/
|
|
1204
1176
|
setSecurityZone(securityZone) {
|
|
1205
1177
|
this.securityZone = securityZone;
|
|
1206
1178
|
return this;
|
|
@@ -1209,33 +1181,44 @@ var EncryptInputsBuilder = class extends BaseBuilder {
|
|
|
1209
1181
|
return this.securityZone;
|
|
1210
1182
|
}
|
|
1211
1183
|
/**
|
|
1184
|
+
* @param address - The contract that will consume the resulting hashes+signature (i.e. the
|
|
1185
|
+
* contract that will call `FHE.asEuint*`/`FHE.asEuint*s` with them).
|
|
1186
|
+
*
|
|
1187
|
+
* Required before `execute()` - the verifier binds this address into the signed digest, so a
|
|
1188
|
+
* batch signed for one contract cannot be replayed into another.
|
|
1189
|
+
*
|
|
1212
1190
|
* Example:
|
|
1213
1191
|
* ```typescript
|
|
1214
1192
|
* const encrypted = await encryptInputs([Encryptable.uint128(10n)])
|
|
1215
|
-
* .
|
|
1193
|
+
* .setConsumingContract("0x123...890")
|
|
1216
1194
|
* .execute();
|
|
1217
1195
|
* ```
|
|
1218
1196
|
*
|
|
1219
|
-
* @returns
|
|
1197
|
+
* @returns The chainable EncryptInputsBuilder instance.
|
|
1220
1198
|
*/
|
|
1221
|
-
|
|
1222
|
-
this.
|
|
1199
|
+
setConsumingContract(address) {
|
|
1200
|
+
this.consumingContract = address;
|
|
1223
1201
|
return this;
|
|
1224
1202
|
}
|
|
1203
|
+
getConsumingContract() {
|
|
1204
|
+
return this.consumingContract;
|
|
1205
|
+
}
|
|
1225
1206
|
/**
|
|
1226
|
-
*
|
|
1227
|
-
*
|
|
1228
|
-
* Overrides the config-level useWorkers setting for this specific encryption.
|
|
1229
|
-
*
|
|
1230
|
-
* Example:
|
|
1231
|
-
* ```typescript
|
|
1232
|
-
* const encrypted = await encryptInputs([Encryptable.uint128(10n)])
|
|
1233
|
-
* .setUseWorker(false)
|
|
1234
|
-
* .execute();
|
|
1235
|
-
* ```
|
|
1236
|
-
*
|
|
1237
|
-
* @returns The chainable EncryptInputsBuilder instance.
|
|
1207
|
+
* Asserts that this.consumingContract is populated
|
|
1208
|
+
* @throws {CofheError} If consumingContract is not set
|
|
1238
1209
|
*/
|
|
1210
|
+
assertConsumingContract() {
|
|
1211
|
+
if (this.consumingContract)
|
|
1212
|
+
return;
|
|
1213
|
+
throw new CofheError({
|
|
1214
|
+
code: "CONSUMING_CONTRACT_UNINITIALIZED" /* ConsumingContractUninitialized */,
|
|
1215
|
+
message: "Consuming contract is not set",
|
|
1216
|
+
hint: "Use setConsumingContract(...) to set the contract that will consume the encrypted inputs.",
|
|
1217
|
+
context: {
|
|
1218
|
+
consumingContract: this.consumingContract
|
|
1219
|
+
}
|
|
1220
|
+
});
|
|
1221
|
+
}
|
|
1239
1222
|
setUseWorker(useWorker) {
|
|
1240
1223
|
this.useWorker = useWorker;
|
|
1241
1224
|
return this;
|
|
@@ -1256,21 +1239,6 @@ var EncryptInputsBuilder = class extends BaseBuilder {
|
|
|
1256
1239
|
getUseWorker() {
|
|
1257
1240
|
return this.useWorker;
|
|
1258
1241
|
}
|
|
1259
|
-
/**
|
|
1260
|
-
* @param callback - Function to be called with the encryption step.
|
|
1261
|
-
*
|
|
1262
|
-
* Useful for debugging and tracking the progress of the encryption process.
|
|
1263
|
-
* Useful for a UI element that shows the progress of the encryption process.
|
|
1264
|
-
*
|
|
1265
|
-
* Example:
|
|
1266
|
-
* ```typescript
|
|
1267
|
-
* const encrypted = await encryptInputs([Encryptable.uint128(10n)])
|
|
1268
|
-
* .onStep((step: EncryptStep) => console.log(step))
|
|
1269
|
-
* .execute();
|
|
1270
|
-
* ```
|
|
1271
|
-
*
|
|
1272
|
-
* @returns The EncryptInputsBuilder instance.
|
|
1273
|
-
*/
|
|
1274
1242
|
onStep(callback) {
|
|
1275
1243
|
this.stepCallback = callback;
|
|
1276
1244
|
return this;
|
|
@@ -1409,6 +1377,7 @@ var EncryptInputsBuilder = class extends BaseBuilder {
|
|
|
1409
1377
|
this.assertAccount();
|
|
1410
1378
|
this.assertPublicClient();
|
|
1411
1379
|
this.assertWalletClient();
|
|
1380
|
+
this.assertConsumingContract();
|
|
1412
1381
|
const [initTfheDelay, fetchKeysDelay, packDelay, proveDelay, verifyDelay] = this.resolveEncryptDelays();
|
|
1413
1382
|
this.fireStepStart("initTfhe" /* InitTfhe */);
|
|
1414
1383
|
await sleep(initTfheDelay);
|
|
@@ -1434,22 +1403,17 @@ var EncryptInputsBuilder = class extends BaseBuilder {
|
|
|
1434
1403
|
this.fireStepEnd("prove" /* Prove */, { isMocks: true, mockSleep: proveDelay });
|
|
1435
1404
|
this.fireStepStart("verify" /* Verify */);
|
|
1436
1405
|
await sleep(verifyDelay);
|
|
1437
|
-
const
|
|
1406
|
+
const result = await cofheMocksZkVerifySign(
|
|
1438
1407
|
this.inputItems,
|
|
1439
1408
|
this.account,
|
|
1440
1409
|
this.securityZone,
|
|
1410
|
+
this.consumingContract,
|
|
1441
1411
|
this.publicClient,
|
|
1442
1412
|
this.walletClient,
|
|
1443
1413
|
this.zkvWalletClient
|
|
1444
1414
|
);
|
|
1445
|
-
const encryptedInputs = signedResults.map(({ ct_hash, signature }, index) => ({
|
|
1446
|
-
ctHash: BigInt(ct_hash),
|
|
1447
|
-
securityZone: this.securityZone,
|
|
1448
|
-
utype: this.inputItems[index].utype,
|
|
1449
|
-
signature
|
|
1450
|
-
}));
|
|
1451
1415
|
this.fireStepEnd("verify" /* Verify */, { isMocks: true, mockSleep: verifyDelay });
|
|
1452
|
-
return
|
|
1416
|
+
return result;
|
|
1453
1417
|
}
|
|
1454
1418
|
/**
|
|
1455
1419
|
* In the production context, perform a true encryption with the CoFHE coprocessor.
|
|
@@ -1457,6 +1421,7 @@ var EncryptInputsBuilder = class extends BaseBuilder {
|
|
|
1457
1421
|
async productionExecute() {
|
|
1458
1422
|
this.assertAccount();
|
|
1459
1423
|
this.assertChainId();
|
|
1424
|
+
this.assertConsumingContract();
|
|
1460
1425
|
this.fireStepStart("initTfhe" /* InitTfhe */);
|
|
1461
1426
|
const tfheInitializationExecuted = await this.initTfheOrThrow();
|
|
1462
1427
|
this.fireStepEnd("initTfhe" /* InitTfhe */, { tfheInitializationExecuted });
|
|
@@ -1491,26 +1456,24 @@ var EncryptInputsBuilder = class extends BaseBuilder {
|
|
|
1491
1456
|
});
|
|
1492
1457
|
this.fireStepStart("verify" /* Verify */);
|
|
1493
1458
|
const zkVerifierUrl = await this.getZkVerifierUrl();
|
|
1494
|
-
const
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
})
|
|
1459
|
+
const result = await zkVerifyBatch(
|
|
1460
|
+
zkVerifierUrl,
|
|
1461
|
+
proof,
|
|
1462
|
+
this.account,
|
|
1463
|
+
this.securityZone,
|
|
1464
|
+
this.chainId,
|
|
1465
|
+
this.consumingContract
|
|
1502
1466
|
);
|
|
1503
1467
|
this.fireStepEnd("verify" /* Verify */);
|
|
1504
|
-
return
|
|
1505
|
-
}
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
return [...hashes, proof];
|
|
1468
|
+
return result;
|
|
1469
|
+
}
|
|
1470
|
+
/**
|
|
1471
|
+
* Converts a batch-verified result (per-item ctHash/ctType + one shared signature) into the
|
|
1472
|
+
* tuple returned by `execute()`: per-item hashes in input order, followed by the single signature.
|
|
1473
|
+
*/
|
|
1474
|
+
buildBatchResult(result) {
|
|
1475
|
+
const hashes = result.outputs.map((output) => "0x" + BigInt(output.ct_hash).toString(16).padStart(64, "0"));
|
|
1476
|
+
return [...hashes, result.signature];
|
|
1514
1477
|
}
|
|
1515
1478
|
/**
|
|
1516
1479
|
* Final step of the encryption process. MUST BE CALLED LAST IN THE CHAIN.
|
|
@@ -1532,18 +1495,16 @@ var EncryptInputsBuilder = class extends BaseBuilder {
|
|
|
1532
1495
|
* @returns The encrypted inputs.
|
|
1533
1496
|
*/
|
|
1534
1497
|
async execute() {
|
|
1535
|
-
let
|
|
1498
|
+
let result;
|
|
1536
1499
|
if (this.chainId === chains.hardhat.id)
|
|
1537
|
-
|
|
1500
|
+
result = await this.mocksExecute();
|
|
1538
1501
|
else
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
return this.structsToHashPlusProof(items);
|
|
1542
|
-
return items;
|
|
1502
|
+
result = await this.productionExecute();
|
|
1503
|
+
return this.buildBatchResult(result);
|
|
1543
1504
|
}
|
|
1544
1505
|
};
|
|
1545
1506
|
|
|
1546
|
-
//
|
|
1507
|
+
// acps/utils.ts
|
|
1547
1508
|
var fromHexString = (hexString) => {
|
|
1548
1509
|
const cleanString = hexString.length % 2 === 1 ? `0${hexString}` : hexString;
|
|
1549
1510
|
const arr = cleanString.replace(/^0x/, "").match(/.{1,2}/g);
|
|
@@ -1563,120 +1524,33 @@ function toBigInt(value) {
|
|
|
1563
1524
|
return value;
|
|
1564
1525
|
}
|
|
1565
1526
|
}
|
|
1566
|
-
function toBeArray(value) {
|
|
1567
|
-
const bigIntValue = typeof value === "number" ? BigInt(value) : value;
|
|
1568
|
-
const hex = bigIntValue.toString(16);
|
|
1569
|
-
const paddedHex = hex.length % 2 === 0 ? hex : "0" + hex;
|
|
1570
|
-
return fromHexString(paddedHex);
|
|
1571
|
-
}
|
|
1572
|
-
function isString(value) {
|
|
1573
|
-
if (typeof value !== "string") {
|
|
1574
|
-
throw new Error(`Expected value which is \`string\`, received value of type \`${typeof value}\`.`);
|
|
1575
|
-
}
|
|
1576
|
-
}
|
|
1577
|
-
function isNumber(value) {
|
|
1578
|
-
const is = typeof value === "number" && !Number.isNaN(value);
|
|
1579
|
-
if (!is) {
|
|
1580
|
-
throw new Error(`Expected value which is \`number\`, received value of type \`${typeof value}\`.`);
|
|
1581
|
-
}
|
|
1582
|
-
}
|
|
1583
|
-
function isBigIntOrNumber(value) {
|
|
1584
|
-
const is = typeof value === "bigint";
|
|
1585
|
-
if (!is) {
|
|
1586
|
-
try {
|
|
1587
|
-
isNumber(value);
|
|
1588
|
-
} catch (e) {
|
|
1589
|
-
throw new Error(`Value ${value} is not a number or bigint: ${typeof value}`);
|
|
1590
|
-
}
|
|
1591
|
-
}
|
|
1592
|
-
}
|
|
1593
1527
|
|
|
1594
|
-
//
|
|
1595
|
-
var
|
|
1596
|
-
var PUBLIC_KEY_LENGTH = 64;
|
|
1597
|
-
var SealingKey = class _SealingKey {
|
|
1598
|
-
/**
|
|
1599
|
-
* The private key used for decryption.
|
|
1600
|
-
*/
|
|
1601
|
-
privateKey;
|
|
1602
|
-
/**
|
|
1603
|
-
* The public key used for encryption.
|
|
1604
|
-
*/
|
|
1605
|
-
publicKey;
|
|
1606
|
-
/**
|
|
1607
|
-
* Constructs a SealingKey instance with the given private and public keys.
|
|
1608
|
-
*
|
|
1609
|
-
* @param {string} privateKey - The private key used for decryption.
|
|
1610
|
-
* @param {string} publicKey - The public key used for encryption.
|
|
1611
|
-
* @throws Will throw an error if the provided keys lengths do not match
|
|
1612
|
-
* the required lengths for private and public keys.
|
|
1613
|
-
*/
|
|
1614
|
-
constructor(privateKey, publicKey) {
|
|
1615
|
-
if (privateKey.length !== PRIVATE_KEY_LENGTH) {
|
|
1616
|
-
throw new Error(`Private key must be of length ${PRIVATE_KEY_LENGTH}`);
|
|
1617
|
-
}
|
|
1618
|
-
if (publicKey.length !== PUBLIC_KEY_LENGTH) {
|
|
1619
|
-
throw new Error(`Public key must be of length ${PUBLIC_KEY_LENGTH}`);
|
|
1620
|
-
}
|
|
1621
|
-
this.privateKey = privateKey;
|
|
1622
|
-
this.publicKey = publicKey;
|
|
1623
|
-
}
|
|
1624
|
-
unseal = (parsedData) => {
|
|
1625
|
-
const nonce = parsedData.nonce instanceof Uint8Array ? parsedData.nonce : new Uint8Array(parsedData.nonce);
|
|
1626
|
-
const ephemPublicKey = parsedData.public_key instanceof Uint8Array ? parsedData.public_key : new Uint8Array(parsedData.public_key);
|
|
1627
|
-
const dataToDecrypt = parsedData.data instanceof Uint8Array ? parsedData.data : new Uint8Array(parsedData.data);
|
|
1628
|
-
const privateKeyBytes = fromHexString(this.privateKey);
|
|
1629
|
-
const decryptedMessage = nacl__default.default.box.open(dataToDecrypt, nonce, ephemPublicKey, privateKeyBytes);
|
|
1630
|
-
if (!decryptedMessage) {
|
|
1631
|
-
throw new Error("Failed to decrypt message");
|
|
1632
|
-
}
|
|
1633
|
-
return toBigInt(decryptedMessage);
|
|
1634
|
-
};
|
|
1635
|
-
/**
|
|
1636
|
-
* Serializes the SealingKey to a JSON object.
|
|
1637
|
-
*/
|
|
1638
|
-
serialize = () => {
|
|
1639
|
-
return {
|
|
1640
|
-
privateKey: this.privateKey,
|
|
1641
|
-
publicKey: this.publicKey
|
|
1642
|
-
};
|
|
1643
|
-
};
|
|
1644
|
-
/**
|
|
1645
|
-
* Deserializes the SealingKey from a JSON object.
|
|
1646
|
-
*/
|
|
1647
|
-
static deserialize = (privateKey, publicKey) => {
|
|
1648
|
-
return new _SealingKey(privateKey, publicKey);
|
|
1649
|
-
};
|
|
1650
|
-
/**
|
|
1651
|
-
* Seals (encrypts) the provided message for a receiver with the specified public key.
|
|
1652
|
-
*
|
|
1653
|
-
* @param {bigint | number} value - The message to be encrypted.
|
|
1654
|
-
* @param {string} publicKey - The public key of the intended recipient.
|
|
1655
|
-
* @returns string - The encrypted message in hexadecimal format.
|
|
1656
|
-
* @static
|
|
1657
|
-
* @throws Will throw if the provided publicKey or value do not meet defined preconditions.
|
|
1658
|
-
*/
|
|
1659
|
-
static seal = (value, publicKey) => {
|
|
1660
|
-
isString(publicKey);
|
|
1661
|
-
isBigIntOrNumber(value);
|
|
1662
|
-
const ephemeralKeyPair = nacl__default.default.box.keyPair();
|
|
1663
|
-
const nonce = nacl__default.default.randomBytes(nacl__default.default.box.nonceLength);
|
|
1664
|
-
const encryptedMessage = nacl__default.default.box(toBeArray(value), nonce, fromHexString(publicKey), ephemeralKeyPair.secretKey);
|
|
1665
|
-
return {
|
|
1666
|
-
data: encryptedMessage,
|
|
1667
|
-
public_key: ephemeralKeyPair.publicKey,
|
|
1668
|
-
nonce
|
|
1669
|
-
};
|
|
1670
|
-
};
|
|
1671
|
-
};
|
|
1528
|
+
// acps/sealing.ts
|
|
1529
|
+
var KEY_HEX_LENGTH = 64;
|
|
1672
1530
|
var GenerateSealingKey = () => {
|
|
1673
1531
|
const sodiumKeypair = nacl__default.default.box.keyPair();
|
|
1674
|
-
return
|
|
1532
|
+
return {
|
|
1533
|
+
privateKey: `0x${toHexString2(sodiumKeypair.secretKey)}`,
|
|
1534
|
+
publicKey: `0x${toHexString2(sodiumKeypair.publicKey)}`
|
|
1535
|
+
};
|
|
1536
|
+
};
|
|
1537
|
+
var unsealWithPrivateKey = (privateKey, parsedData) => {
|
|
1538
|
+
assertKeyLength(privateKey, "Private");
|
|
1539
|
+
const nonce = parsedData.nonce instanceof Uint8Array ? parsedData.nonce : new Uint8Array(parsedData.nonce);
|
|
1540
|
+
const ephemPublicKey = parsedData.public_key instanceof Uint8Array ? parsedData.public_key : new Uint8Array(parsedData.public_key);
|
|
1541
|
+
const dataToDecrypt = parsedData.data instanceof Uint8Array ? parsedData.data : new Uint8Array(parsedData.data);
|
|
1542
|
+
const decryptedMessage = nacl__default.default.box.open(dataToDecrypt, nonce, ephemPublicKey, fromHexString(privateKey));
|
|
1543
|
+
if (!decryptedMessage) {
|
|
1544
|
+
throw new Error("Failed to decrypt message");
|
|
1545
|
+
}
|
|
1546
|
+
return toBigInt(decryptedMessage);
|
|
1547
|
+
};
|
|
1548
|
+
var assertKeyLength = (key, kind) => {
|
|
1549
|
+
const bare = key.startsWith("0x") ? key.slice(2) : key;
|
|
1550
|
+
if (bare.length !== KEY_HEX_LENGTH) {
|
|
1551
|
+
throw new Error(`${kind} key must be of length ${KEY_HEX_LENGTH}`);
|
|
1552
|
+
}
|
|
1675
1553
|
};
|
|
1676
|
-
var SerializedSealingPair = zod.z.object({
|
|
1677
|
-
privateKey: zod.z.string(),
|
|
1678
|
-
publicKey: zod.z.string()
|
|
1679
|
-
});
|
|
1680
1554
|
var addressSchema = zod.z.string().refine((val) => viem.isAddress(val), {
|
|
1681
1555
|
error: "Invalid address"
|
|
1682
1556
|
}).transform((val) => viem.getAddress(val));
|
|
@@ -1695,46 +1569,70 @@ var bytesNotEmptySchema = bytesSchema.refine((val) => val !== "0x", {
|
|
|
1695
1569
|
error: "Must not be empty"
|
|
1696
1570
|
});
|
|
1697
1571
|
var DEFAULT_EXPIRATION_FN = () => Math.round(Date.now() / 1e3) + 7 * 24 * 60 * 60;
|
|
1698
|
-
var
|
|
1699
|
-
|
|
1572
|
+
var handlesSchema = zod.z.array(zod.z.string().regex(/^0x[0-9a-fA-F]{64}$/, "Invalid handle: expected 32-byte hex")).optional().default([]);
|
|
1573
|
+
var contractsSchema = zod.z.array(addressSchema).optional().default([]);
|
|
1574
|
+
var SCOPE_GLOBAL = 0;
|
|
1575
|
+
var SCOPE_CONTRACT = 1;
|
|
1576
|
+
var SCOPE_HANDLES = 2;
|
|
1577
|
+
var ScopeConsistencyRefinement = [
|
|
1578
|
+
(data) => data.scope === SCOPE_GLOBAL && data.contracts.length === 0 && data.handles.length === 0 || data.scope === SCOPE_CONTRACT && data.contracts.length > 0 && data.handles.length === 0 || data.scope === SCOPE_HANDLES && data.handles.length > 0 && data.contracts.length === 0,
|
|
1579
|
+
{
|
|
1580
|
+
error: "ACP scope :: arrays must match the scope mode (Global: both empty; Contract: contracts only; Handles: handles only)",
|
|
1581
|
+
path: ["scope"]
|
|
1582
|
+
}
|
|
1583
|
+
];
|
|
1584
|
+
var withDerivedScope = (data) => ({
|
|
1585
|
+
...data,
|
|
1586
|
+
scope: data.scope ?? (data.contracts.length > 0 ? SCOPE_CONTRACT : data.handles.length > 0 ? SCOPE_HANDLES : SCOPE_GLOBAL)
|
|
1587
|
+
});
|
|
1588
|
+
var zACPWithDefaults = zod.z.object({
|
|
1589
|
+
name: zod.z.string().optional().default("Unnamed ACP"),
|
|
1700
1590
|
type: zod.z.enum(["self", "sharing", "recipient"]),
|
|
1701
1591
|
issuer: addressNotZeroSchema,
|
|
1702
1592
|
expiration: zod.z.int().optional().default(DEFAULT_EXPIRATION_FN),
|
|
1703
1593
|
recipient: addressSchema.optional().default(viem.zeroAddress),
|
|
1704
|
-
|
|
1705
|
-
|
|
1594
|
+
revokerData: zod.z.int().optional().default(0),
|
|
1595
|
+
revokerContract: addressSchema.optional().default(viem.zeroAddress),
|
|
1596
|
+
scope: zod.z.int().min(0).max(2).optional().default(0),
|
|
1597
|
+
contracts: contractsSchema,
|
|
1598
|
+
handles: handlesSchema,
|
|
1706
1599
|
issuerSignature: bytesSchema.optional().default("0x"),
|
|
1707
1600
|
recipientSignature: bytesSchema.optional().default("0x")
|
|
1708
1601
|
});
|
|
1709
|
-
var
|
|
1710
|
-
|
|
1602
|
+
var zACPWithSealingKeys = zACPWithDefaults.extend({
|
|
1603
|
+
/** X25519 private key, 0x-prefixed 32-byte hex; never leaves the client */
|
|
1604
|
+
sealingPrivateKey: zod.z.string().regex(/^0x[0-9a-fA-F]{64}$/, "Invalid sealing private key").optional(),
|
|
1605
|
+
sealingKey: zod.z.string().regex(/^0x[0-9a-fA-F]{64}$/, "Invalid sealing key").optional()
|
|
1711
1606
|
});
|
|
1712
1607
|
var ExternalValidatorRefinement = [
|
|
1713
|
-
(data) => data.
|
|
1608
|
+
(data) => data.revokerData !== 0 && data.revokerContract !== viem.zeroAddress || data.revokerData === 0 && data.revokerContract === viem.zeroAddress,
|
|
1714
1609
|
{
|
|
1715
|
-
error: "
|
|
1716
|
-
path: ["
|
|
1610
|
+
error: "ACP external revoker :: revokerData and revokerContract must either both be set or both be unset.",
|
|
1611
|
+
path: ["revokerData", "revokerContract"]
|
|
1717
1612
|
}
|
|
1718
1613
|
];
|
|
1719
1614
|
var RecipientRefinement = [
|
|
1720
1615
|
(data) => data.issuer !== data.recipient,
|
|
1721
1616
|
{
|
|
1722
|
-
error: "Sharing
|
|
1617
|
+
error: "Sharing acp :: issuer and recipient must not be the same",
|
|
1723
1618
|
path: ["issuer", "recipient"]
|
|
1724
1619
|
}
|
|
1725
1620
|
];
|
|
1726
|
-
var
|
|
1621
|
+
var SelfACPOptionsValidator = zod.z.object({
|
|
1727
1622
|
type: zod.z.literal("self").optional().default("self"),
|
|
1728
1623
|
issuer: addressNotZeroSchema,
|
|
1729
|
-
name: zod.z.string().optional().default("Unnamed
|
|
1624
|
+
name: zod.z.string().optional().default("Unnamed ACP"),
|
|
1730
1625
|
expiration: zod.z.int().optional().default(DEFAULT_EXPIRATION_FN),
|
|
1731
1626
|
recipient: addressSchema.optional().default(viem.zeroAddress),
|
|
1732
|
-
|
|
1733
|
-
|
|
1627
|
+
revokerData: zod.z.int().optional().default(0),
|
|
1628
|
+
revokerContract: addressSchema.optional().default(viem.zeroAddress),
|
|
1629
|
+
scope: zod.z.int().min(0).max(2).optional(),
|
|
1630
|
+
contracts: contractsSchema,
|
|
1631
|
+
handles: handlesSchema,
|
|
1734
1632
|
issuerSignature: bytesSchema.optional().default("0x"),
|
|
1735
1633
|
recipientSignature: bytesSchema.optional().default("0x")
|
|
1736
|
-
}).refine(...ExternalValidatorRefinement);
|
|
1737
|
-
var
|
|
1634
|
+
}).refine(...ExternalValidatorRefinement).transform(withDerivedScope).refine(...ScopeConsistencyRefinement);
|
|
1635
|
+
var SelfACPValidator = zACPWithSealingKeys.refine((data) => data.type === "self", {
|
|
1738
1636
|
error: "Type must be 'self'"
|
|
1739
1637
|
}).refine((data) => data.recipient === viem.zeroAddress, {
|
|
1740
1638
|
error: "Recipient must be zeroAddress"
|
|
@@ -1743,18 +1641,21 @@ var SelfPermitValidator = zPermitWithSealingPair.refine((data) => data.type ===
|
|
|
1743
1641
|
}).refine((data) => data.recipientSignature === "0x", {
|
|
1744
1642
|
error: "RecipientSignature must be empty"
|
|
1745
1643
|
}).refine(...ExternalValidatorRefinement);
|
|
1746
|
-
var
|
|
1644
|
+
var SharingACPOptionsValidator = zod.z.object({
|
|
1747
1645
|
type: zod.z.literal("sharing").optional().default("sharing"),
|
|
1748
1646
|
issuer: addressNotZeroSchema,
|
|
1749
1647
|
recipient: addressNotZeroSchema,
|
|
1750
|
-
name: zod.z.string().optional().default("Unnamed
|
|
1648
|
+
name: zod.z.string().optional().default("Unnamed ACP"),
|
|
1751
1649
|
expiration: zod.z.int().optional().default(DEFAULT_EXPIRATION_FN),
|
|
1752
|
-
|
|
1753
|
-
|
|
1650
|
+
revokerData: zod.z.int().optional().default(0),
|
|
1651
|
+
revokerContract: addressSchema.optional().default(viem.zeroAddress),
|
|
1652
|
+
scope: zod.z.int().min(0).max(2).optional(),
|
|
1653
|
+
contracts: contractsSchema,
|
|
1654
|
+
handles: handlesSchema,
|
|
1754
1655
|
issuerSignature: bytesSchema.optional().default("0x"),
|
|
1755
1656
|
recipientSignature: bytesSchema.optional().default("0x")
|
|
1756
|
-
}).refine(...RecipientRefinement).refine(...ExternalValidatorRefinement);
|
|
1757
|
-
var
|
|
1657
|
+
}).refine(...RecipientRefinement).refine(...ExternalValidatorRefinement).transform(withDerivedScope).refine(...ScopeConsistencyRefinement);
|
|
1658
|
+
var SharingACPValidator = zACPWithSealingKeys.refine((data) => data.type === "sharing", {
|
|
1758
1659
|
error: "Type must be 'sharing'"
|
|
1759
1660
|
}).refine((data) => data.recipient !== viem.zeroAddress, {
|
|
1760
1661
|
error: "Recipient must not be zeroAddress"
|
|
@@ -1763,18 +1664,21 @@ var SharingPermitValidator = zPermitWithSealingPair.refine((data) => data.type =
|
|
|
1763
1664
|
}).refine((data) => data.recipientSignature === "0x", {
|
|
1764
1665
|
error: "RecipientSignature must be empty"
|
|
1765
1666
|
}).refine(...ExternalValidatorRefinement);
|
|
1766
|
-
var
|
|
1667
|
+
var ImportACPOptionsValidator = zod.z.object({
|
|
1767
1668
|
type: zod.z.literal("recipient").optional().default("recipient"),
|
|
1768
1669
|
issuer: addressNotZeroSchema,
|
|
1769
1670
|
recipient: addressNotZeroSchema,
|
|
1770
|
-
name: zod.z.string().optional().default("Unnamed
|
|
1671
|
+
name: zod.z.string().optional().default("Unnamed ACP"),
|
|
1771
1672
|
expiration: zod.z.int(),
|
|
1772
|
-
|
|
1773
|
-
|
|
1673
|
+
revokerData: zod.z.int().optional().default(0),
|
|
1674
|
+
revokerContract: addressSchema.optional().default(viem.zeroAddress),
|
|
1675
|
+
scope: zod.z.int().min(0).max(2).optional(),
|
|
1676
|
+
contracts: contractsSchema,
|
|
1677
|
+
handles: handlesSchema,
|
|
1774
1678
|
issuerSignature: bytesNotEmptySchema,
|
|
1775
1679
|
recipientSignature: bytesSchema.optional().default("0x")
|
|
1776
|
-
}).refine(...ExternalValidatorRefinement);
|
|
1777
|
-
var
|
|
1680
|
+
}).refine(...ExternalValidatorRefinement).transform(withDerivedScope).refine(...ScopeConsistencyRefinement);
|
|
1681
|
+
var ImportACPValidator = zACPWithSealingKeys.refine((data) => data.type === "recipient", {
|
|
1778
1682
|
error: "Type must be 'recipient'"
|
|
1779
1683
|
}).refine((data) => data.recipient !== viem.zeroAddress, {
|
|
1780
1684
|
error: "Recipient must not be zeroAddress"
|
|
@@ -1790,116 +1694,125 @@ var safeParseAndThrowFormatted = (schema, data, message) => {
|
|
|
1790
1694
|
}
|
|
1791
1695
|
return result.data;
|
|
1792
1696
|
};
|
|
1793
|
-
var
|
|
1794
|
-
return safeParseAndThrowFormatted(
|
|
1697
|
+
var validateSelfACPOptions = (options) => {
|
|
1698
|
+
return safeParseAndThrowFormatted(SelfACPOptionsValidator, options, "Invalid self acp options");
|
|
1795
1699
|
};
|
|
1796
|
-
var
|
|
1797
|
-
return safeParseAndThrowFormatted(
|
|
1700
|
+
var validateSharingACPOptions = (options) => {
|
|
1701
|
+
return safeParseAndThrowFormatted(SharingACPOptionsValidator, options, "Invalid sharing acp options");
|
|
1798
1702
|
};
|
|
1799
|
-
var
|
|
1800
|
-
return safeParseAndThrowFormatted(
|
|
1703
|
+
var validateImportACPOptions = (options) => {
|
|
1704
|
+
return safeParseAndThrowFormatted(ImportACPOptionsValidator, options, "Invalid import acp options");
|
|
1801
1705
|
};
|
|
1802
|
-
var
|
|
1803
|
-
return safeParseAndThrowFormatted(
|
|
1706
|
+
var validateSelfACP = (acp) => {
|
|
1707
|
+
return safeParseAndThrowFormatted(SelfACPValidator, acp, "Invalid self acp");
|
|
1804
1708
|
};
|
|
1805
|
-
var
|
|
1806
|
-
return safeParseAndThrowFormatted(
|
|
1709
|
+
var validateSharingACP = (acp) => {
|
|
1710
|
+
return safeParseAndThrowFormatted(SharingACPValidator, acp, "Invalid sharing acp");
|
|
1807
1711
|
};
|
|
1808
|
-
var
|
|
1809
|
-
return safeParseAndThrowFormatted(
|
|
1712
|
+
var validateImportACP = (acp) => {
|
|
1713
|
+
return safeParseAndThrowFormatted(ImportACPValidator, acp, "Invalid import acp");
|
|
1810
1714
|
};
|
|
1811
1715
|
var ValidationUtils = {
|
|
1812
1716
|
/**
|
|
1813
|
-
* Check if
|
|
1717
|
+
* Check if acp is expired
|
|
1814
1718
|
*/
|
|
1815
|
-
isExpired: (
|
|
1816
|
-
return
|
|
1719
|
+
isExpired: (acp) => {
|
|
1720
|
+
return acp.expiration < Math.floor(Date.now() / 1e3);
|
|
1817
1721
|
},
|
|
1818
1722
|
/**
|
|
1819
|
-
* Check if
|
|
1723
|
+
* Check if acp is signed by the active party
|
|
1820
1724
|
*/
|
|
1821
|
-
isSigned: (
|
|
1822
|
-
if (
|
|
1823
|
-
return
|
|
1725
|
+
isSigned: (acp) => {
|
|
1726
|
+
if (acp.type === "self" || acp.type === "sharing") {
|
|
1727
|
+
return acp.issuerSignature !== "0x";
|
|
1824
1728
|
}
|
|
1825
|
-
if (
|
|
1826
|
-
return
|
|
1729
|
+
if (acp.type === "recipient") {
|
|
1730
|
+
return acp.recipientSignature !== "0x";
|
|
1827
1731
|
}
|
|
1828
1732
|
return false;
|
|
1829
1733
|
},
|
|
1830
1734
|
/**
|
|
1831
|
-
* Checks that
|
|
1735
|
+
* Checks that an ACP is signed and not expired.
|
|
1832
1736
|
*/
|
|
1833
|
-
isSignedAndNotExpired: (
|
|
1834
|
-
if (ValidationUtils.isExpired(
|
|
1737
|
+
isSignedAndNotExpired: (acp) => {
|
|
1738
|
+
if (ValidationUtils.isExpired(acp)) {
|
|
1835
1739
|
return { valid: false, error: "expired" };
|
|
1836
1740
|
}
|
|
1837
|
-
if (!ValidationUtils.isSigned(
|
|
1741
|
+
if (!ValidationUtils.isSigned(acp)) {
|
|
1838
1742
|
return { valid: false, error: "not-signed" };
|
|
1839
1743
|
}
|
|
1840
1744
|
return { valid: true, error: null };
|
|
1841
1745
|
},
|
|
1842
1746
|
/**
|
|
1843
|
-
* Asserts that
|
|
1747
|
+
* Asserts that an ACP is signed and not expired.
|
|
1844
1748
|
*
|
|
1845
1749
|
* Throws `Error` with message:
|
|
1846
|
-
* - `
|
|
1847
|
-
* - `
|
|
1750
|
+
* - `ACP is expired`
|
|
1751
|
+
* - `ACP is not signed`
|
|
1848
1752
|
*/
|
|
1849
|
-
assertSignedAndNotExpired: (
|
|
1850
|
-
const result = ValidationUtils.isSignedAndNotExpired(
|
|
1753
|
+
assertSignedAndNotExpired: (acp) => {
|
|
1754
|
+
const result = ValidationUtils.isSignedAndNotExpired(acp);
|
|
1851
1755
|
if (result.valid)
|
|
1852
1756
|
return;
|
|
1853
1757
|
if (result.error === "expired") {
|
|
1854
|
-
throw new Error("
|
|
1758
|
+
throw new Error("ACP is expired");
|
|
1855
1759
|
}
|
|
1856
1760
|
if (result.error === "not-signed") {
|
|
1857
|
-
throw new Error("
|
|
1761
|
+
throw new Error("ACP is not signed");
|
|
1858
1762
|
}
|
|
1859
|
-
throw new Error("
|
|
1763
|
+
throw new Error("ACP is invalid");
|
|
1860
1764
|
},
|
|
1861
|
-
isValid: (
|
|
1862
|
-
const schema =
|
|
1765
|
+
isValid: (acp) => {
|
|
1766
|
+
const schema = acp.type === "self" ? SelfACPValidator : acp.type === "sharing" ? SharingACPValidator : acp.type === "recipient" ? ImportACPValidator : null;
|
|
1863
1767
|
if (schema == null)
|
|
1864
1768
|
return { valid: false, error: "invalid-schema" };
|
|
1865
|
-
const schemaResult = schema.safeParse(
|
|
1769
|
+
const schemaResult = schema.safeParse(acp);
|
|
1866
1770
|
if (!schemaResult.success)
|
|
1867
1771
|
return { valid: false, error: "invalid-schema" };
|
|
1868
|
-
return ValidationUtils.isSignedAndNotExpired(
|
|
1772
|
+
return ValidationUtils.isSignedAndNotExpired(acp);
|
|
1869
1773
|
}
|
|
1870
1774
|
};
|
|
1871
1775
|
|
|
1872
|
-
//
|
|
1873
|
-
var
|
|
1776
|
+
// acps/signature.ts
|
|
1777
|
+
var ACPSignatureAllFields = [
|
|
1874
1778
|
{ name: "issuer", type: "address" },
|
|
1875
1779
|
{ name: "expiration", type: "uint64" },
|
|
1876
1780
|
{ name: "recipient", type: "address" },
|
|
1877
|
-
{ name: "
|
|
1878
|
-
{ name: "
|
|
1781
|
+
{ name: "revokerData", type: "uint256" },
|
|
1782
|
+
{ name: "revokerContract", type: "address" },
|
|
1783
|
+
{ name: "scope", type: "uint8" },
|
|
1784
|
+
{ name: "contracts", type: "address[]" },
|
|
1785
|
+
{ name: "handles", type: "bytes32[]" },
|
|
1879
1786
|
{ name: "sealingKey", type: "bytes32" },
|
|
1880
1787
|
{ name: "issuerSignature", type: "bytes" }
|
|
1881
1788
|
];
|
|
1882
1789
|
var SignatureTypes = {
|
|
1883
|
-
|
|
1790
|
+
ACPIssuerSelf: [
|
|
1884
1791
|
"issuer",
|
|
1885
1792
|
"expiration",
|
|
1886
1793
|
"recipient",
|
|
1887
|
-
"
|
|
1888
|
-
"
|
|
1794
|
+
"revokerData",
|
|
1795
|
+
"revokerContract",
|
|
1796
|
+
"scope",
|
|
1797
|
+
"contracts",
|
|
1798
|
+
"handles",
|
|
1889
1799
|
"sealingKey"
|
|
1890
1800
|
],
|
|
1891
|
-
|
|
1801
|
+
ACPIssuerShared: [
|
|
1892
1802
|
"issuer",
|
|
1893
1803
|
"expiration",
|
|
1894
1804
|
"recipient",
|
|
1895
|
-
"
|
|
1896
|
-
"
|
|
1805
|
+
"revokerData",
|
|
1806
|
+
"revokerContract",
|
|
1807
|
+
"scope",
|
|
1808
|
+
"contracts",
|
|
1809
|
+
"handles"
|
|
1897
1810
|
],
|
|
1898
|
-
|
|
1811
|
+
ACPRecipient: ["sealingKey", "issuerSignature"]
|
|
1899
1812
|
};
|
|
1900
1813
|
var getSignatureTypesAndMessage = (primaryType, fields, values) => {
|
|
1901
1814
|
const types = {
|
|
1902
|
-
[primaryType]:
|
|
1815
|
+
[primaryType]: ACPSignatureAllFields.filter((fieldType) => fields.includes(fieldType.name))
|
|
1903
1816
|
};
|
|
1904
1817
|
const message = {};
|
|
1905
1818
|
fields.forEach((field) => {
|
|
@@ -1911,22 +1824,22 @@ var getSignatureTypesAndMessage = (primaryType, fields, values) => {
|
|
|
1911
1824
|
};
|
|
1912
1825
|
var SignatureUtils = {
|
|
1913
1826
|
/**
|
|
1914
|
-
* Get signature parameters for
|
|
1827
|
+
* Get signature parameters for an ACP
|
|
1915
1828
|
*/
|
|
1916
|
-
getSignatureParams: (
|
|
1917
|
-
return getSignatureTypesAndMessage(primaryType, SignatureTypes[primaryType],
|
|
1829
|
+
getSignatureParams: (acp, primaryType) => {
|
|
1830
|
+
return getSignatureTypesAndMessage(primaryType, SignatureTypes[primaryType], acp);
|
|
1918
1831
|
},
|
|
1919
1832
|
/**
|
|
1920
|
-
* Determine the required signature type based on
|
|
1833
|
+
* Determine the required signature type based on acp type
|
|
1921
1834
|
*/
|
|
1922
|
-
getPrimaryType: (
|
|
1923
|
-
if (
|
|
1924
|
-
return "
|
|
1925
|
-
if (
|
|
1926
|
-
return "
|
|
1927
|
-
if (
|
|
1928
|
-
return "
|
|
1929
|
-
throw new Error(`Unknown
|
|
1835
|
+
getPrimaryType: (acpType) => {
|
|
1836
|
+
if (acpType === "self")
|
|
1837
|
+
return "ACPIssuerSelf";
|
|
1838
|
+
if (acpType === "sharing")
|
|
1839
|
+
return "ACPIssuerShared";
|
|
1840
|
+
if (acpType === "recipient")
|
|
1841
|
+
return "ACPRecipient";
|
|
1842
|
+
throw new Error(`Unknown acp type: ${acpType}`);
|
|
1930
1843
|
}
|
|
1931
1844
|
};
|
|
1932
1845
|
var getAclAddress = async (publicClient) => {
|
|
@@ -1948,6 +1861,11 @@ var getAclEIP712Domain = async (publicClient) => {
|
|
|
1948
1861
|
functionName: "eip712Domain"
|
|
1949
1862
|
});
|
|
1950
1863
|
const [_fields, name, version, chainId, verifyingContract, _salt, _extensions] = domain;
|
|
1864
|
+
if (version !== "2") {
|
|
1865
|
+
throw new Error(
|
|
1866
|
+
`Chain ${chainId}'s ACL serves EIP-712 domain version "${version}" \u2014 this SDK requires the upgraded (ACP-era) ACL, which signs as version "2". Pre-upgrade (V2 Permission) chains are not supported.`
|
|
1867
|
+
);
|
|
1868
|
+
}
|
|
1951
1869
|
return {
|
|
1952
1870
|
name,
|
|
1953
1871
|
version,
|
|
@@ -1955,23 +1873,26 @@ var getAclEIP712Domain = async (publicClient) => {
|
|
|
1955
1873
|
verifyingContract
|
|
1956
1874
|
};
|
|
1957
1875
|
};
|
|
1958
|
-
var
|
|
1876
|
+
var checkACPValidityOnChain = async (acp, publicClient) => {
|
|
1959
1877
|
const aclAddress = await getAclAddress(publicClient);
|
|
1960
1878
|
try {
|
|
1961
1879
|
await publicClient.simulateContract({
|
|
1962
1880
|
address: aclAddress,
|
|
1963
|
-
abi:
|
|
1964
|
-
functionName: "
|
|
1881
|
+
abi: checkACPValidityAbi,
|
|
1882
|
+
functionName: "checkPermissionValidity",
|
|
1965
1883
|
args: [
|
|
1966
1884
|
{
|
|
1967
|
-
issuer:
|
|
1968
|
-
expiration: BigInt(
|
|
1969
|
-
recipient:
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1885
|
+
issuer: acp.issuer,
|
|
1886
|
+
expiration: BigInt(acp.expiration),
|
|
1887
|
+
recipient: acp.recipient,
|
|
1888
|
+
revokerData: BigInt(acp.revokerData),
|
|
1889
|
+
revokerContract: acp.revokerContract,
|
|
1890
|
+
scope: acp.scope,
|
|
1891
|
+
contracts: acp.contracts,
|
|
1892
|
+
handles: acp.handles,
|
|
1893
|
+
sealingKey: acp.sealingKey,
|
|
1894
|
+
issuerSignature: acp.issuerSignature,
|
|
1895
|
+
recipientSignature: acp.recipientSignature
|
|
1975
1896
|
}
|
|
1976
1897
|
]
|
|
1977
1898
|
});
|
|
@@ -1984,14 +1905,14 @@ var checkPermitValidityOnChain = async (permission, publicClient) => {
|
|
|
1984
1905
|
throw new Error(errorName);
|
|
1985
1906
|
}
|
|
1986
1907
|
}
|
|
1987
|
-
const customErrorName = extractCustomErrorFromDetails(err,
|
|
1908
|
+
const customErrorName = extractCustomErrorFromDetails(err, checkACPValidityAbi);
|
|
1988
1909
|
if (customErrorName) {
|
|
1989
1910
|
throw new Error(customErrorName);
|
|
1990
1911
|
}
|
|
1991
1912
|
const hhDetailsData = extractReturnData(err);
|
|
1992
1913
|
if (hhDetailsData != null) {
|
|
1993
1914
|
const decoded = viem.decodeErrorResult({
|
|
1994
|
-
abi:
|
|
1915
|
+
abi: checkACPValidityAbi,
|
|
1995
1916
|
data: hhDetailsData
|
|
1996
1917
|
});
|
|
1997
1918
|
throw new Error(decoded.errorName);
|
|
@@ -2019,15 +1940,15 @@ function extractReturnData(err) {
|
|
|
2019
1940
|
const s = anyErr?.details ?? anyErr?.cause?.details ?? anyErr?.shortMessage ?? anyErr?.message ?? String(err);
|
|
2020
1941
|
return s.match(/return data:\s*(0x[a-fA-F0-9]+)/)?.[1];
|
|
2021
1942
|
}
|
|
2022
|
-
var
|
|
1943
|
+
var checkACPValidityAbi = [
|
|
2023
1944
|
{
|
|
2024
1945
|
type: "function",
|
|
2025
|
-
name: "
|
|
1946
|
+
name: "checkPermissionValidity",
|
|
2026
1947
|
inputs: [
|
|
2027
1948
|
{
|
|
2028
|
-
name: "
|
|
1949
|
+
name: "acp",
|
|
2029
1950
|
type: "tuple",
|
|
2030
|
-
internalType: "struct
|
|
1951
|
+
internalType: "struct ACP",
|
|
2031
1952
|
components: [
|
|
2032
1953
|
{
|
|
2033
1954
|
name: "issuer",
|
|
@@ -2045,15 +1966,30 @@ var checkPermitValidityAbi = [
|
|
|
2045
1966
|
internalType: "address"
|
|
2046
1967
|
},
|
|
2047
1968
|
{
|
|
2048
|
-
name: "
|
|
1969
|
+
name: "revokerData",
|
|
2049
1970
|
type: "uint256",
|
|
2050
1971
|
internalType: "uint256"
|
|
2051
1972
|
},
|
|
2052
1973
|
{
|
|
2053
|
-
name: "
|
|
1974
|
+
name: "revokerContract",
|
|
2054
1975
|
type: "address",
|
|
2055
1976
|
internalType: "address"
|
|
2056
1977
|
},
|
|
1978
|
+
{
|
|
1979
|
+
name: "scope",
|
|
1980
|
+
type: "uint8",
|
|
1981
|
+
internalType: "uint8"
|
|
1982
|
+
},
|
|
1983
|
+
{
|
|
1984
|
+
name: "contracts",
|
|
1985
|
+
type: "address[]",
|
|
1986
|
+
internalType: "address[]"
|
|
1987
|
+
},
|
|
1988
|
+
{
|
|
1989
|
+
name: "handles",
|
|
1990
|
+
type: "bytes32[]",
|
|
1991
|
+
internalType: "bytes32[]"
|
|
1992
|
+
},
|
|
2057
1993
|
{
|
|
2058
1994
|
name: "sealingKey",
|
|
2059
1995
|
type: "bytes32",
|
|
@@ -2103,38 +2039,40 @@ var checkPermitValidityAbi = [
|
|
|
2103
2039
|
}
|
|
2104
2040
|
];
|
|
2105
2041
|
|
|
2106
|
-
//
|
|
2107
|
-
var
|
|
2042
|
+
// acps/acp.ts
|
|
2043
|
+
var ACPUtils = {
|
|
2108
2044
|
/**
|
|
2109
|
-
* Create a self
|
|
2045
|
+
* Create a self acp for personal use
|
|
2110
2046
|
*/
|
|
2111
2047
|
createSelf: (options) => {
|
|
2112
|
-
const validation =
|
|
2048
|
+
const validation = validateSelfACPOptions(options);
|
|
2113
2049
|
const sealingPair = GenerateSealingKey();
|
|
2114
|
-
const
|
|
2115
|
-
hash:
|
|
2050
|
+
const acp = {
|
|
2051
|
+
hash: ACPUtils.getHash(validation),
|
|
2116
2052
|
...validation,
|
|
2117
|
-
sealingPair,
|
|
2053
|
+
sealingPrivateKey: sealingPair.privateKey,
|
|
2054
|
+
sealingKey: sealingPair.publicKey,
|
|
2118
2055
|
_signedDomain: void 0
|
|
2119
2056
|
};
|
|
2120
|
-
return
|
|
2057
|
+
return acp;
|
|
2121
2058
|
},
|
|
2122
2059
|
/**
|
|
2123
|
-
* Create a sharing
|
|
2060
|
+
* Create a sharing acp to be shared with another user
|
|
2124
2061
|
*/
|
|
2125
2062
|
createSharing: (options) => {
|
|
2126
|
-
const validation =
|
|
2063
|
+
const validation = validateSharingACPOptions(options);
|
|
2127
2064
|
const sealingPair = GenerateSealingKey();
|
|
2128
|
-
const
|
|
2129
|
-
hash:
|
|
2065
|
+
const acp = {
|
|
2066
|
+
hash: ACPUtils.getHash(validation),
|
|
2130
2067
|
...validation,
|
|
2131
|
-
sealingPair,
|
|
2068
|
+
sealingPrivateKey: sealingPair.privateKey,
|
|
2069
|
+
sealingKey: sealingPair.publicKey,
|
|
2132
2070
|
_signedDomain: void 0
|
|
2133
2071
|
};
|
|
2134
|
-
return
|
|
2072
|
+
return acp;
|
|
2135
2073
|
},
|
|
2136
2074
|
/**
|
|
2137
|
-
* Import a shared
|
|
2075
|
+
* Import a shared acp from various input formats
|
|
2138
2076
|
*/
|
|
2139
2077
|
importShared: (options) => {
|
|
2140
2078
|
let parsedOptions;
|
|
@@ -2147,33 +2085,34 @@ var PermitUtils = {
|
|
|
2147
2085
|
} else if (typeof options === "object" && options !== null) {
|
|
2148
2086
|
parsedOptions = options;
|
|
2149
2087
|
} else {
|
|
2150
|
-
throw new Error("Invalid input type, expected
|
|
2088
|
+
throw new Error("Invalid input type, expected ImportSharedACPOptions, object, or string");
|
|
2151
2089
|
}
|
|
2152
2090
|
if (parsedOptions.type != null && parsedOptions.type !== "sharing") {
|
|
2153
|
-
throw new Error(`Invalid
|
|
2091
|
+
throw new Error(`Invalid acp type <${parsedOptions.type}>, must be "sharing"`);
|
|
2154
2092
|
}
|
|
2155
|
-
const validation =
|
|
2093
|
+
const validation = validateImportACPOptions({ ...parsedOptions, type: "recipient" });
|
|
2156
2094
|
const sealingPair = GenerateSealingKey();
|
|
2157
|
-
const
|
|
2158
|
-
hash:
|
|
2095
|
+
const acp = {
|
|
2096
|
+
hash: ACPUtils.getHash(validation),
|
|
2159
2097
|
...validation,
|
|
2160
|
-
sealingPair,
|
|
2098
|
+
sealingPrivateKey: sealingPair.privateKey,
|
|
2099
|
+
sealingKey: sealingPair.publicKey,
|
|
2161
2100
|
_signedDomain: void 0
|
|
2162
2101
|
};
|
|
2163
|
-
return
|
|
2102
|
+
return acp;
|
|
2164
2103
|
},
|
|
2165
2104
|
/**
|
|
2166
|
-
* Sign
|
|
2105
|
+
* Sign an ACP with the provided wallet client
|
|
2167
2106
|
*/
|
|
2168
|
-
sign: async (
|
|
2107
|
+
sign: async (acp, publicClient, walletClient) => {
|
|
2169
2108
|
if (walletClient == null || walletClient.account == null) {
|
|
2170
2109
|
throw new Error(
|
|
2171
|
-
"Missing walletClient, you must pass in a `walletClient` for the connected user to create
|
|
2110
|
+
"Missing walletClient, you must pass in a `walletClient` for the connected user to create an ACP signature"
|
|
2172
2111
|
);
|
|
2173
2112
|
}
|
|
2174
|
-
const primaryType = SignatureUtils.getPrimaryType(
|
|
2113
|
+
const primaryType = SignatureUtils.getPrimaryType(acp.type);
|
|
2175
2114
|
const domain = await getAclEIP712Domain(publicClient);
|
|
2176
|
-
const { types, message } = SignatureUtils.getSignatureParams(
|
|
2115
|
+
const { types, message } = SignatureUtils.getSignatureParams(ACPUtils.getPublic(acp, true), primaryType);
|
|
2177
2116
|
const signature = await walletClient.signTypedData({
|
|
2178
2117
|
domain,
|
|
2179
2118
|
types,
|
|
@@ -2181,190 +2120,209 @@ var PermitUtils = {
|
|
|
2181
2120
|
message,
|
|
2182
2121
|
account: walletClient.account
|
|
2183
2122
|
});
|
|
2184
|
-
let
|
|
2185
|
-
if (
|
|
2186
|
-
|
|
2187
|
-
...
|
|
2123
|
+
let updatedACP;
|
|
2124
|
+
if (acp.type === "self" || acp.type === "sharing") {
|
|
2125
|
+
updatedACP = {
|
|
2126
|
+
...acp,
|
|
2188
2127
|
issuerSignature: signature,
|
|
2189
2128
|
_signedDomain: domain
|
|
2190
2129
|
};
|
|
2191
2130
|
} else {
|
|
2192
|
-
|
|
2193
|
-
...
|
|
2131
|
+
updatedACP = {
|
|
2132
|
+
...acp,
|
|
2194
2133
|
recipientSignature: signature,
|
|
2195
2134
|
_signedDomain: domain
|
|
2196
2135
|
};
|
|
2197
2136
|
}
|
|
2198
|
-
return
|
|
2137
|
+
return updatedACP;
|
|
2199
2138
|
},
|
|
2200
2139
|
/**
|
|
2201
|
-
* Create and sign a self
|
|
2140
|
+
* Create and sign a self acp in one operation
|
|
2202
2141
|
*/
|
|
2203
2142
|
createSelfAndSign: async (options, publicClient, walletClient) => {
|
|
2204
|
-
const
|
|
2205
|
-
return
|
|
2143
|
+
const acp = ACPUtils.createSelf(options);
|
|
2144
|
+
return ACPUtils.sign(acp, publicClient, walletClient);
|
|
2206
2145
|
},
|
|
2207
2146
|
/**
|
|
2208
|
-
* Create and sign a sharing
|
|
2147
|
+
* Create and sign a sharing acp in one operation
|
|
2209
2148
|
*/
|
|
2210
2149
|
createSharingAndSign: async (options, publicClient, walletClient) => {
|
|
2211
|
-
const
|
|
2212
|
-
return
|
|
2150
|
+
const acp = ACPUtils.createSharing(options);
|
|
2151
|
+
return ACPUtils.sign(acp, publicClient, walletClient);
|
|
2213
2152
|
},
|
|
2214
2153
|
/**
|
|
2215
|
-
* Import and sign a shared
|
|
2154
|
+
* Import and sign a shared acp in one operation from various input formats
|
|
2216
2155
|
*/
|
|
2217
2156
|
importSharedAndSign: async (options, publicClient, walletClient) => {
|
|
2218
|
-
const
|
|
2219
|
-
return
|
|
2157
|
+
const acp = ACPUtils.importShared(options);
|
|
2158
|
+
return ACPUtils.sign(acp, publicClient, walletClient);
|
|
2220
2159
|
},
|
|
2221
2160
|
/**
|
|
2222
|
-
* Deserialize
|
|
2161
|
+
* Deserialize an ACP from serialized data
|
|
2223
2162
|
*/
|
|
2224
2163
|
deserialize: (data) => {
|
|
2225
|
-
return {
|
|
2226
|
-
...data,
|
|
2227
|
-
sealingPair: SealingKey.deserialize(data.sealingPair.privateKey, data.sealingPair.publicKey)
|
|
2228
|
-
};
|
|
2164
|
+
return { ...data };
|
|
2229
2165
|
},
|
|
2230
2166
|
/**
|
|
2231
|
-
* Serialize
|
|
2167
|
+
* Serialize an ACP for storage
|
|
2232
2168
|
*/
|
|
2233
|
-
serialize: (
|
|
2169
|
+
serialize: (acp) => {
|
|
2234
2170
|
return {
|
|
2235
|
-
hash:
|
|
2236
|
-
name:
|
|
2237
|
-
type:
|
|
2238
|
-
issuer:
|
|
2239
|
-
expiration:
|
|
2240
|
-
recipient:
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2171
|
+
hash: acp.hash,
|
|
2172
|
+
name: acp.name,
|
|
2173
|
+
type: acp.type,
|
|
2174
|
+
issuer: acp.issuer,
|
|
2175
|
+
expiration: acp.expiration,
|
|
2176
|
+
recipient: acp.recipient,
|
|
2177
|
+
revokerData: acp.revokerData,
|
|
2178
|
+
revokerContract: acp.revokerContract,
|
|
2179
|
+
scope: acp.scope,
|
|
2180
|
+
contracts: acp.contracts,
|
|
2181
|
+
handles: acp.handles,
|
|
2182
|
+
sealingKey: acp.sealingKey,
|
|
2183
|
+
issuerSignature: acp.issuerSignature,
|
|
2184
|
+
recipientSignature: acp.recipientSignature,
|
|
2185
|
+
_signedDomain: acp._signedDomain,
|
|
2186
|
+
sealingPrivateKey: acp.sealingPrivateKey
|
|
2247
2187
|
};
|
|
2248
2188
|
},
|
|
2249
2189
|
/**
|
|
2250
|
-
* Validate
|
|
2190
|
+
* Validate an ACP (schema-level validation)
|
|
2251
2191
|
*/
|
|
2252
|
-
validateSchema: (
|
|
2253
|
-
if (
|
|
2254
|
-
return
|
|
2255
|
-
} else if (
|
|
2256
|
-
return
|
|
2257
|
-
} else if (
|
|
2258
|
-
return
|
|
2192
|
+
validateSchema: (acp) => {
|
|
2193
|
+
if (acp.type === "self") {
|
|
2194
|
+
return validateSelfACP(acp);
|
|
2195
|
+
} else if (acp.type === "sharing") {
|
|
2196
|
+
return validateSharingACP(acp);
|
|
2197
|
+
} else if (acp.type === "recipient") {
|
|
2198
|
+
return validateImportACP(acp);
|
|
2259
2199
|
} else {
|
|
2260
|
-
throw new Error("Invalid
|
|
2200
|
+
throw new Error("Invalid ACP type");
|
|
2261
2201
|
}
|
|
2262
2202
|
},
|
|
2263
2203
|
/**
|
|
2264
|
-
* Validate
|
|
2204
|
+
* Validate an ACP (holistic validation).
|
|
2265
2205
|
*
|
|
2266
2206
|
* This validates:
|
|
2267
|
-
* -
|
|
2268
|
-
* -
|
|
2269
|
-
* -
|
|
2207
|
+
* - ACP schema (shape + invariants)
|
|
2208
|
+
* - ACP is signed
|
|
2209
|
+
* - ACP is not expired
|
|
2270
2210
|
*
|
|
2271
|
-
* For schema-only validation, use `validateSchema(
|
|
2211
|
+
* For schema-only validation, use `validateSchema(acp)`.
|
|
2272
2212
|
*/
|
|
2273
|
-
validate: (
|
|
2274
|
-
const validated =
|
|
2213
|
+
validate: (acp) => {
|
|
2214
|
+
const validated = ACPUtils.validateSchema(acp);
|
|
2275
2215
|
ValidationUtils.assertSignedAndNotExpired(validated);
|
|
2276
2216
|
return validated;
|
|
2277
2217
|
},
|
|
2278
2218
|
/**
|
|
2279
|
-
* Get the
|
|
2219
|
+
* Get the public component of an ACP — the signed struct sent on-chain / to the decryption backend.
|
|
2220
|
+
* Strips the private component (hash, name, type, sealing pair).
|
|
2280
2221
|
*/
|
|
2281
|
-
|
|
2222
|
+
getPublic: (acp, skipValidation = false) => {
|
|
2282
2223
|
if (!skipValidation) {
|
|
2283
|
-
|
|
2224
|
+
ACPUtils.validateSchema(acp);
|
|
2284
2225
|
}
|
|
2285
2226
|
return {
|
|
2286
|
-
issuer:
|
|
2287
|
-
expiration:
|
|
2288
|
-
recipient:
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2227
|
+
issuer: acp.issuer,
|
|
2228
|
+
expiration: acp.expiration,
|
|
2229
|
+
recipient: acp.recipient,
|
|
2230
|
+
revokerData: acp.revokerData,
|
|
2231
|
+
revokerContract: acp.revokerContract,
|
|
2232
|
+
scope: acp.scope,
|
|
2233
|
+
contracts: acp.contracts,
|
|
2234
|
+
handles: acp.handles,
|
|
2235
|
+
sealingKey: acp.sealingKey,
|
|
2236
|
+
issuerSignature: acp.issuerSignature,
|
|
2237
|
+
recipientSignature: acp.recipientSignature
|
|
2294
2238
|
};
|
|
2295
2239
|
},
|
|
2296
2240
|
/**
|
|
2297
|
-
* Get a stable hash for the
|
|
2241
|
+
* Get a stable hash for the acp (used as key in storage)
|
|
2298
2242
|
*/
|
|
2299
|
-
getHash: (
|
|
2243
|
+
getHash: (acp) => {
|
|
2300
2244
|
const data = JSON.stringify({
|
|
2301
|
-
type:
|
|
2302
|
-
issuer:
|
|
2303
|
-
expiration:
|
|
2304
|
-
recipient:
|
|
2305
|
-
|
|
2306
|
-
|
|
2245
|
+
type: acp.type,
|
|
2246
|
+
issuer: acp.issuer,
|
|
2247
|
+
expiration: acp.expiration,
|
|
2248
|
+
recipient: acp.recipient,
|
|
2249
|
+
revokerData: acp.revokerData,
|
|
2250
|
+
revokerContract: acp.revokerContract,
|
|
2251
|
+
scope: acp.scope,
|
|
2252
|
+
contracts: acp.contracts,
|
|
2253
|
+
handles: acp.handles
|
|
2307
2254
|
});
|
|
2308
2255
|
return viem.keccak256(viem.toHex(data));
|
|
2309
2256
|
},
|
|
2310
2257
|
/**
|
|
2311
|
-
* Export
|
|
2258
|
+
* Export acp data for sharing (strips the private component).
|
|
2259
|
+
* Fixed `SharedACP` shape — every field always present, aligned with
|
|
2260
|
+
* `ACPPublic` and the on-chain sharing payload.
|
|
2312
2261
|
*/
|
|
2313
|
-
export: (
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
2262
|
+
export: (acp) => {
|
|
2263
|
+
if (acp.type !== "sharing") {
|
|
2264
|
+
throw new Error(
|
|
2265
|
+
`Cannot export a '${acp.type}' ACP \u2014 only 'sharing' ACPs are exportable. The export includes the issuer signature.`
|
|
2266
|
+
);
|
|
2267
|
+
}
|
|
2268
|
+
if (acp.issuerSignature === "0x") {
|
|
2269
|
+
throw new Error(
|
|
2270
|
+
"Cannot export an unsigned sharing ACP \u2014 sign it first (the recipient needs the issuer signature)."
|
|
2271
|
+
);
|
|
2272
|
+
}
|
|
2273
|
+
const shared = {
|
|
2274
|
+
name: acp.name,
|
|
2275
|
+
type: acp.type,
|
|
2276
|
+
issuer: acp.issuer,
|
|
2277
|
+
expiration: acp.expiration,
|
|
2278
|
+
recipient: acp.recipient,
|
|
2279
|
+
revokerData: acp.revokerData,
|
|
2280
|
+
revokerContract: acp.revokerContract,
|
|
2281
|
+
scope: acp.scope,
|
|
2282
|
+
contracts: acp.contracts,
|
|
2283
|
+
handles: acp.handles,
|
|
2284
|
+
issuerSignature: acp.issuerSignature
|
|
2319
2285
|
};
|
|
2320
|
-
|
|
2321
|
-
cleanedPermit.recipient = permit.recipient;
|
|
2322
|
-
if (permit.validatorId !== 0)
|
|
2323
|
-
cleanedPermit.validatorId = permit.validatorId;
|
|
2324
|
-
if (permit.validatorContract !== viem.zeroAddress)
|
|
2325
|
-
cleanedPermit.validatorContract = permit.validatorContract;
|
|
2326
|
-
if (permit.type === "sharing" && permit.issuerSignature !== "0x")
|
|
2327
|
-
cleanedPermit.issuerSignature = permit.issuerSignature;
|
|
2328
|
-
return JSON.stringify(cleanedPermit, void 0, 2);
|
|
2286
|
+
return JSON.stringify(shared, void 0, 2);
|
|
2329
2287
|
},
|
|
2330
2288
|
/**
|
|
2331
|
-
* Unseal encrypted data using the
|
|
2289
|
+
* Unseal encrypted data using the acp's sealing key
|
|
2332
2290
|
*/
|
|
2333
|
-
unseal: (
|
|
2334
|
-
return
|
|
2291
|
+
unseal: (acp, ciphertext) => {
|
|
2292
|
+
return unsealWithPrivateKey(acp.sealingPrivateKey, ciphertext);
|
|
2335
2293
|
},
|
|
2336
2294
|
/**
|
|
2337
|
-
* Check if
|
|
2295
|
+
* Check if acp is expired
|
|
2338
2296
|
*/
|
|
2339
|
-
isExpired: (
|
|
2340
|
-
return ValidationUtils.isExpired(
|
|
2297
|
+
isExpired: (acp) => {
|
|
2298
|
+
return ValidationUtils.isExpired(acp);
|
|
2341
2299
|
},
|
|
2342
2300
|
/**
|
|
2343
|
-
* Check if
|
|
2301
|
+
* Check if acp is signed
|
|
2344
2302
|
*/
|
|
2345
|
-
isSigned: (
|
|
2346
|
-
return ValidationUtils.isSigned(
|
|
2303
|
+
isSigned: (acp) => {
|
|
2304
|
+
return ValidationUtils.isSigned(acp);
|
|
2347
2305
|
},
|
|
2348
2306
|
/**
|
|
2349
|
-
* Check if
|
|
2307
|
+
* Check if acp is signed and not expired
|
|
2350
2308
|
*/
|
|
2351
|
-
isSignedAndNotExpired: (
|
|
2352
|
-
return ValidationUtils.isSignedAndNotExpired(
|
|
2309
|
+
isSignedAndNotExpired: (acp) => {
|
|
2310
|
+
return ValidationUtils.isSignedAndNotExpired(acp);
|
|
2353
2311
|
},
|
|
2354
2312
|
/**
|
|
2355
|
-
* Assert that
|
|
2313
|
+
* Assert that acp is signed and not expired
|
|
2356
2314
|
*/
|
|
2357
|
-
assertSignedAndNotExpired: (
|
|
2358
|
-
return ValidationUtils.assertSignedAndNotExpired(
|
|
2315
|
+
assertSignedAndNotExpired: (acp) => {
|
|
2316
|
+
return ValidationUtils.assertSignedAndNotExpired(acp);
|
|
2359
2317
|
},
|
|
2360
|
-
isValid: (
|
|
2361
|
-
return ValidationUtils.isValid(
|
|
2318
|
+
isValid: (acp) => {
|
|
2319
|
+
return ValidationUtils.isValid(acp);
|
|
2362
2320
|
},
|
|
2363
2321
|
/**
|
|
2364
|
-
* Update
|
|
2322
|
+
* Update acp name (returns new acp instance)
|
|
2365
2323
|
*/
|
|
2366
|
-
updateName: (
|
|
2367
|
-
return { ...
|
|
2324
|
+
updateName: (acp, name) => {
|
|
2325
|
+
return { ...acp, name };
|
|
2368
2326
|
},
|
|
2369
2327
|
/**
|
|
2370
2328
|
* Fetch EIP712 domain from the blockchain
|
|
@@ -2373,230 +2331,468 @@ var PermitUtils = {
|
|
|
2373
2331
|
return getAclEIP712Domain(publicClient);
|
|
2374
2332
|
},
|
|
2375
2333
|
/**
|
|
2376
|
-
* Check if
|
|
2334
|
+
* Check if acp's signed domain matches the provided domain
|
|
2377
2335
|
*/
|
|
2378
|
-
matchesDomain: (
|
|
2379
|
-
return
|
|
2336
|
+
matchesDomain: (acp, domain) => {
|
|
2337
|
+
return acp._signedDomain?.name === domain.name && acp._signedDomain?.version === domain.version && acp._signedDomain?.verifyingContract === domain.verifyingContract && acp._signedDomain?.chainId === domain.chainId;
|
|
2380
2338
|
},
|
|
2381
2339
|
/**
|
|
2382
|
-
* Check if
|
|
2340
|
+
* Check if acp's signed domain is valid for the current chain
|
|
2383
2341
|
*/
|
|
2384
|
-
checkSignedDomainValid: async (
|
|
2385
|
-
if (
|
|
2342
|
+
checkSignedDomainValid: async (acp, publicClient) => {
|
|
2343
|
+
if (acp._signedDomain == null)
|
|
2386
2344
|
return false;
|
|
2387
2345
|
const domain = await getAclEIP712Domain(publicClient);
|
|
2388
|
-
return
|
|
2346
|
+
return ACPUtils.matchesDomain(acp, domain);
|
|
2389
2347
|
},
|
|
2390
2348
|
/**
|
|
2391
|
-
* Check if
|
|
2349
|
+
* Check if acp passes the on-chain validation
|
|
2392
2350
|
*/
|
|
2393
|
-
checkValidityOnChain: async (
|
|
2394
|
-
const
|
|
2395
|
-
return
|
|
2351
|
+
checkValidityOnChain: async (acp, publicClient) => {
|
|
2352
|
+
const publicAcp = ACPUtils.getPublic(acp);
|
|
2353
|
+
return checkACPValidityOnChain(publicAcp, publicClient);
|
|
2396
2354
|
}
|
|
2397
2355
|
};
|
|
2398
|
-
var
|
|
2399
|
-
|
|
2400
|
-
|
|
2356
|
+
var ACP_STORE_DEFAULTS = {
|
|
2357
|
+
acps: {},
|
|
2358
|
+
activeACPHash: {}
|
|
2401
2359
|
};
|
|
2402
|
-
var
|
|
2403
|
-
|
|
2360
|
+
var ACP_STORE_VERSION = 3;
|
|
2361
|
+
var _acpStore = vanilla.createStore()(
|
|
2362
|
+
middleware.persist(() => ACP_STORE_DEFAULTS, {
|
|
2363
|
+
name: "cofhesdk-acps",
|
|
2364
|
+
version: ACP_STORE_VERSION,
|
|
2365
|
+
migrate: (persistedState, version) => {
|
|
2366
|
+
if (version < ACP_STORE_VERSION)
|
|
2367
|
+
return ACP_STORE_DEFAULTS;
|
|
2368
|
+
return persistedState;
|
|
2369
|
+
}
|
|
2370
|
+
})
|
|
2404
2371
|
);
|
|
2405
2372
|
var clearStaleStore = () => {
|
|
2406
|
-
const state =
|
|
2407
|
-
const hasExpectedStructure = state && typeof state === "object" && "
|
|
2373
|
+
const state = _acpStore.getState();
|
|
2374
|
+
const hasExpectedStructure = state && typeof state === "object" && "acps" in state && "activeACPHash" in state && typeof state.acps === "object" && typeof state.activeACPHash === "object";
|
|
2408
2375
|
if (hasExpectedStructure)
|
|
2409
2376
|
return;
|
|
2410
|
-
|
|
2377
|
+
_acpStore.setState({ acps: {}, activeACPHash: {} });
|
|
2411
2378
|
};
|
|
2412
|
-
var
|
|
2379
|
+
var getACP = (chainId, account, hash) => {
|
|
2413
2380
|
clearStaleStore();
|
|
2414
2381
|
if (chainId == null || account == null || hash == null)
|
|
2415
2382
|
return;
|
|
2416
|
-
const
|
|
2417
|
-
if (
|
|
2383
|
+
const savedACP = _acpStore.getState().acps[chainId]?.[account]?.[hash];
|
|
2384
|
+
if (savedACP == null)
|
|
2418
2385
|
return;
|
|
2419
|
-
return
|
|
2386
|
+
return ACPUtils.deserialize(savedACP);
|
|
2420
2387
|
};
|
|
2421
|
-
var
|
|
2388
|
+
var getActiveACP = (chainId, account) => {
|
|
2422
2389
|
clearStaleStore();
|
|
2423
2390
|
if (chainId == null || account == null)
|
|
2424
2391
|
return;
|
|
2425
|
-
const
|
|
2426
|
-
return
|
|
2392
|
+
const activeACPHash = _acpStore.getState().activeACPHash[chainId]?.[account];
|
|
2393
|
+
return getACP(chainId, account, activeACPHash);
|
|
2427
2394
|
};
|
|
2428
|
-
var
|
|
2395
|
+
var getACPs = (chainId, account) => {
|
|
2429
2396
|
clearStaleStore();
|
|
2430
2397
|
if (chainId == null || account == null)
|
|
2431
2398
|
return {};
|
|
2432
|
-
return Object.entries(
|
|
2433
|
-
(acc, [hash,
|
|
2434
|
-
if (
|
|
2399
|
+
return Object.entries(_acpStore.getState().acps[chainId]?.[account] ?? {}).reduce(
|
|
2400
|
+
(acc, [hash, acp]) => {
|
|
2401
|
+
if (acp == void 0)
|
|
2435
2402
|
return acc;
|
|
2436
|
-
return { ...acc, [hash]:
|
|
2403
|
+
return { ...acc, [hash]: ACPUtils.deserialize(acp) };
|
|
2437
2404
|
},
|
|
2438
2405
|
{}
|
|
2439
2406
|
);
|
|
2440
2407
|
};
|
|
2441
|
-
var
|
|
2408
|
+
var setACP = (chainId, account, acp) => {
|
|
2442
2409
|
clearStaleStore();
|
|
2443
|
-
|
|
2410
|
+
_acpStore.setState(
|
|
2444
2411
|
immer.produce((state) => {
|
|
2445
|
-
if (state.
|
|
2446
|
-
state.
|
|
2447
|
-
if (state.
|
|
2448
|
-
state.
|
|
2449
|
-
state.
|
|
2412
|
+
if (state.acps[chainId] == null)
|
|
2413
|
+
state.acps[chainId] = {};
|
|
2414
|
+
if (state.acps[chainId][account] == null)
|
|
2415
|
+
state.acps[chainId][account] = {};
|
|
2416
|
+
state.acps[chainId][account][acp.hash] = ACPUtils.serialize(acp);
|
|
2450
2417
|
})
|
|
2451
2418
|
);
|
|
2452
2419
|
};
|
|
2453
|
-
var
|
|
2420
|
+
var removeACP = (chainId, account, hash) => {
|
|
2454
2421
|
clearStaleStore();
|
|
2455
|
-
|
|
2422
|
+
_acpStore.setState(
|
|
2456
2423
|
immer.produce((state) => {
|
|
2457
|
-
if (state.
|
|
2458
|
-
state.
|
|
2459
|
-
if (state.
|
|
2460
|
-
state.
|
|
2461
|
-
const
|
|
2462
|
-
if (
|
|
2424
|
+
if (state.acps[chainId] == null)
|
|
2425
|
+
state.acps[chainId] = {};
|
|
2426
|
+
if (state.activeACPHash[chainId] == null)
|
|
2427
|
+
state.activeACPHash[chainId] = {};
|
|
2428
|
+
const accountACPs = state.acps[chainId][account];
|
|
2429
|
+
if (accountACPs == null)
|
|
2463
2430
|
return;
|
|
2464
|
-
if (
|
|
2431
|
+
if (accountACPs[hash] == null)
|
|
2465
2432
|
return;
|
|
2466
|
-
if (state.
|
|
2467
|
-
state.
|
|
2433
|
+
if (state.activeACPHash[chainId][account] === hash) {
|
|
2434
|
+
state.activeACPHash[chainId][account] = void 0;
|
|
2468
2435
|
}
|
|
2469
|
-
|
|
2436
|
+
accountACPs[hash] = void 0;
|
|
2470
2437
|
})
|
|
2471
2438
|
);
|
|
2472
2439
|
};
|
|
2473
|
-
var
|
|
2440
|
+
var getActiveACPHash = (chainId, account) => {
|
|
2474
2441
|
clearStaleStore();
|
|
2475
2442
|
if (chainId == null || account == null)
|
|
2476
2443
|
return void 0;
|
|
2477
|
-
return
|
|
2444
|
+
return _acpStore.getState().activeACPHash[chainId]?.[account];
|
|
2478
2445
|
};
|
|
2479
|
-
var
|
|
2446
|
+
var setActiveACPHash = (chainId, account, hash) => {
|
|
2480
2447
|
clearStaleStore();
|
|
2481
|
-
|
|
2448
|
+
_acpStore.setState(
|
|
2482
2449
|
immer.produce((state) => {
|
|
2483
|
-
if (state.
|
|
2484
|
-
state.
|
|
2485
|
-
state.
|
|
2450
|
+
if (state.activeACPHash[chainId] == null)
|
|
2451
|
+
state.activeACPHash[chainId] = {};
|
|
2452
|
+
state.activeACPHash[chainId][account] = hash;
|
|
2486
2453
|
})
|
|
2487
2454
|
);
|
|
2488
2455
|
};
|
|
2489
|
-
var
|
|
2456
|
+
var removeActiveACPHash = (chainId, account) => {
|
|
2490
2457
|
clearStaleStore();
|
|
2491
|
-
|
|
2458
|
+
_acpStore.setState(
|
|
2492
2459
|
immer.produce((state) => {
|
|
2493
|
-
if (state.
|
|
2494
|
-
state.
|
|
2460
|
+
if (state.activeACPHash[chainId])
|
|
2461
|
+
state.activeACPHash[chainId][account] = void 0;
|
|
2495
2462
|
})
|
|
2496
2463
|
);
|
|
2497
2464
|
};
|
|
2498
2465
|
var resetStore = () => {
|
|
2499
2466
|
clearStaleStore();
|
|
2500
|
-
|
|
2467
|
+
_acpStore.setState({ acps: {}, activeACPHash: {} });
|
|
2501
2468
|
};
|
|
2502
|
-
var
|
|
2503
|
-
store:
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
|
|
2510
|
-
|
|
2511
|
-
|
|
2469
|
+
var acpStore = {
|
|
2470
|
+
store: _acpStore,
|
|
2471
|
+
getACP,
|
|
2472
|
+
getActiveACP,
|
|
2473
|
+
getACPs,
|
|
2474
|
+
setACP,
|
|
2475
|
+
removeACP,
|
|
2476
|
+
getActiveACPHash,
|
|
2477
|
+
setActiveACPHash,
|
|
2478
|
+
removeActiveACPHash,
|
|
2512
2479
|
resetStore
|
|
2513
2480
|
};
|
|
2514
|
-
var
|
|
2481
|
+
var ACP_VALIDATOR_ABI = viem.parseAbi([
|
|
2482
|
+
"function revokeSingle(uint256 id)",
|
|
2483
|
+
"function revokeAllExisting()",
|
|
2484
|
+
"function disabled(address issuer, uint256 id) view returns (bool)"
|
|
2485
|
+
]);
|
|
2486
|
+
var storeACP = async (acp, publicClient, walletClient) => {
|
|
2515
2487
|
const chainId = await publicClient.getChainId();
|
|
2516
2488
|
const account = walletClient.account.address;
|
|
2517
|
-
|
|
2518
|
-
permitStore.setActivePermitHash(chainId, account, permit.hash);
|
|
2489
|
+
acpStore.setACP(chainId, account, acp);
|
|
2519
2490
|
};
|
|
2520
|
-
var
|
|
2521
|
-
|
|
2522
|
-
await
|
|
2523
|
-
|
|
2491
|
+
var storeActiveACP = async (acp, publicClient, walletClient) => {
|
|
2492
|
+
await storeACP(acp, publicClient, walletClient);
|
|
2493
|
+
const chainId = await publicClient.getChainId();
|
|
2494
|
+
const account = walletClient.account.address;
|
|
2495
|
+
acpStore.setActiveACPHash(chainId, account, acp.hash);
|
|
2496
|
+
};
|
|
2497
|
+
var createACPWithSign = async (options, publicClient, walletClient, acpMethod, activate = true) => {
|
|
2498
|
+
const acp = await acpMethod(options, publicClient, walletClient);
|
|
2499
|
+
if (activate) {
|
|
2500
|
+
await storeActiveACP(acp, publicClient, walletClient);
|
|
2501
|
+
} else {
|
|
2502
|
+
await storeACP(acp, publicClient, walletClient);
|
|
2503
|
+
}
|
|
2504
|
+
return acp;
|
|
2524
2505
|
};
|
|
2525
2506
|
var createSelf = async (options, publicClient, walletClient) => {
|
|
2526
|
-
return
|
|
2507
|
+
return createACPWithSign(options, publicClient, walletClient, ACPUtils.createSelfAndSign);
|
|
2527
2508
|
};
|
|
2528
2509
|
var createSharing = async (options, publicClient, walletClient) => {
|
|
2529
|
-
return
|
|
2510
|
+
return createACPWithSign(options, publicClient, walletClient, ACPUtils.createSharingAndSign, false);
|
|
2530
2511
|
};
|
|
2531
2512
|
var importShared = async (options, publicClient, walletClient) => {
|
|
2532
|
-
return
|
|
2513
|
+
return createACPWithSign(options, publicClient, walletClient, ACPUtils.importSharedAndSign);
|
|
2533
2514
|
};
|
|
2534
|
-
var getHash = (
|
|
2535
|
-
return
|
|
2515
|
+
var getHash = (acp) => {
|
|
2516
|
+
return ACPUtils.getHash(acp);
|
|
2536
2517
|
};
|
|
2537
|
-
var exportShared = (
|
|
2538
|
-
return
|
|
2518
|
+
var exportShared = (acp) => {
|
|
2519
|
+
return ACPUtils.export(acp);
|
|
2539
2520
|
};
|
|
2540
|
-
var serialize = (
|
|
2541
|
-
return
|
|
2521
|
+
var serialize = (acp) => {
|
|
2522
|
+
return ACPUtils.serialize(acp);
|
|
2542
2523
|
};
|
|
2543
2524
|
var deserialize = (serialized) => {
|
|
2544
|
-
return
|
|
2525
|
+
return ACPUtils.deserialize(serialized);
|
|
2545
2526
|
};
|
|
2546
|
-
var
|
|
2547
|
-
return
|
|
2527
|
+
var getACP2 = (chainId, account, hash) => {
|
|
2528
|
+
return acpStore.getACP(chainId, account, hash);
|
|
2548
2529
|
};
|
|
2549
|
-
var
|
|
2550
|
-
return
|
|
2530
|
+
var getACPs2 = (chainId, account) => {
|
|
2531
|
+
return acpStore.getACPs(chainId, account);
|
|
2551
2532
|
};
|
|
2552
|
-
var
|
|
2553
|
-
return
|
|
2533
|
+
var getActiveACP2 = (chainId, account) => {
|
|
2534
|
+
return acpStore.getActiveACP(chainId, account);
|
|
2554
2535
|
};
|
|
2555
|
-
var
|
|
2556
|
-
return
|
|
2536
|
+
var getActiveACPHash2 = (chainId, account) => {
|
|
2537
|
+
return acpStore.getActiveACPHash(chainId, account);
|
|
2557
2538
|
};
|
|
2558
|
-
var
|
|
2559
|
-
|
|
2539
|
+
var selectActiveACP = (chainId, account, hash) => {
|
|
2540
|
+
acpStore.setActiveACPHash(chainId, account, hash);
|
|
2560
2541
|
};
|
|
2561
|
-
var
|
|
2542
|
+
var getOrCreateSelfACP = async (publicClient, walletClient, chainId, account, options) => {
|
|
2562
2543
|
const _chainId = chainId ?? await publicClient.getChainId();
|
|
2563
2544
|
const _account = account ?? walletClient.account.address;
|
|
2564
|
-
const
|
|
2565
|
-
if (
|
|
2566
|
-
return
|
|
2545
|
+
const activeACP = await getActiveACP2(_chainId, _account);
|
|
2546
|
+
if (activeACP && activeACP.type === "self" && ACPUtils.isValid(activeACP).valid) {
|
|
2547
|
+
return activeACP;
|
|
2567
2548
|
}
|
|
2568
|
-
return createSelf(options ?? { issuer: _account, name: "Autogenerated Self
|
|
2549
|
+
return createSelf(options ?? { issuer: _account, name: "Autogenerated Self ACP" }, publicClient, walletClient);
|
|
2569
2550
|
};
|
|
2570
|
-
var
|
|
2551
|
+
var getOrCreateSharingACP = async (publicClient, walletClient, options, chainId, account) => {
|
|
2571
2552
|
const _chainId = chainId ?? await publicClient.getChainId();
|
|
2572
2553
|
const _account = account ?? walletClient.account.address;
|
|
2573
|
-
const
|
|
2574
|
-
if (
|
|
2575
|
-
return
|
|
2554
|
+
const activeACP = await getActiveACP2(_chainId, _account);
|
|
2555
|
+
if (activeACP && activeACP.type === "sharing" && ACPUtils.isValid(activeACP).valid) {
|
|
2556
|
+
return activeACP;
|
|
2576
2557
|
}
|
|
2577
2558
|
return createSharing(options, publicClient, walletClient);
|
|
2578
2559
|
};
|
|
2579
|
-
var
|
|
2580
|
-
|
|
2581
|
-
|
|
2582
|
-
|
|
2583
|
-
|
|
2560
|
+
var applyACPDefaults = (options, acpConfig, chainId) => {
|
|
2561
|
+
const result = { ...options };
|
|
2562
|
+
const defaultRevoker = acpConfig?.defaultRevoker?.[chainId];
|
|
2563
|
+
const hasValidatorOptions = options.revokerData != null || options.revokerContract != null;
|
|
2564
|
+
if (defaultRevoker != null && !hasValidatorOptions) {
|
|
2565
|
+
result.revokerContract = defaultRevoker;
|
|
2566
|
+
result.revokerData = Math.round(Date.now() / 1e3) - 60;
|
|
2567
|
+
}
|
|
2568
|
+
const defaultContracts = acpConfig?.defaultContractScopes?.[chainId];
|
|
2569
|
+
const hasScopeOptions = options.scope != null || options.contracts != null || options.handles != null;
|
|
2570
|
+
if (defaultContracts != null && defaultContracts.length > 0 && !hasScopeOptions) {
|
|
2571
|
+
result.contracts = defaultContracts;
|
|
2572
|
+
}
|
|
2573
|
+
return result;
|
|
2574
|
+
};
|
|
2575
|
+
var ACL_SERVED_ADDRESSES_ABI = viem.parseAbi([
|
|
2576
|
+
"function acl() view returns (address)",
|
|
2577
|
+
"function defaultRevokerContract() view returns (address)",
|
|
2578
|
+
"function shareRegistry() view returns (address)"
|
|
2579
|
+
]);
|
|
2580
|
+
var aclServedAddressesCache = /* @__PURE__ */ new Map();
|
|
2581
|
+
var clearAclServedAddresses = () => aclServedAddressesCache.clear();
|
|
2582
|
+
var getAclServedAddresses = async (publicClient, chainId) => {
|
|
2583
|
+
const cached = aclServedAddressesCache.get(chainId);
|
|
2584
|
+
if (cached != null)
|
|
2585
|
+
return cached;
|
|
2586
|
+
let aclAddress;
|
|
2587
|
+
try {
|
|
2588
|
+
aclAddress = await publicClient.readContract({
|
|
2589
|
+
address: TASK_MANAGER_ADDRESS,
|
|
2590
|
+
abi: ACL_SERVED_ADDRESSES_ABI,
|
|
2591
|
+
functionName: "acl"
|
|
2592
|
+
});
|
|
2593
|
+
} catch {
|
|
2594
|
+
return {};
|
|
2595
|
+
}
|
|
2596
|
+
const [defaultRevoker, shareRegistry] = await Promise.all([
|
|
2597
|
+
publicClient.readContract({ address: aclAddress, abi: ACL_SERVED_ADDRESSES_ABI, functionName: "defaultRevokerContract" }).catch(() => void 0),
|
|
2598
|
+
publicClient.readContract({ address: aclAddress, abi: ACL_SERVED_ADDRESSES_ABI, functionName: "shareRegistry" }).catch(() => void 0)
|
|
2599
|
+
]);
|
|
2600
|
+
const resolved = {
|
|
2601
|
+
defaultRevoker: defaultRevoker != null && defaultRevoker !== viem.zeroAddress ? defaultRevoker : void 0,
|
|
2602
|
+
shareRegistry: shareRegistry != null && shareRegistry !== viem.zeroAddress ? shareRegistry : void 0
|
|
2603
|
+
};
|
|
2604
|
+
aclServedAddressesCache.set(chainId, resolved);
|
|
2605
|
+
return resolved;
|
|
2606
|
+
};
|
|
2607
|
+
var applyACPDefaultsFromChain = async (options, acpConfig, publicClient, chainId) => {
|
|
2608
|
+
const hasExplicitRevoker = acpConfig?.defaultRevoker?.[chainId] != null || options.revokerData != null || options.revokerContract != null;
|
|
2609
|
+
if (hasExplicitRevoker)
|
|
2610
|
+
return applyACPDefaults(options, acpConfig, chainId);
|
|
2611
|
+
const served = await getAclServedAddresses(publicClient, chainId);
|
|
2612
|
+
const effectiveConfig = served.defaultRevoker != null ? { ...acpConfig, defaultRevoker: { ...acpConfig?.defaultRevoker, [chainId]: served.defaultRevoker } } : acpConfig;
|
|
2613
|
+
return applyACPDefaults(options, effectiveConfig, chainId);
|
|
2614
|
+
};
|
|
2615
|
+
var revokeACP = async (acp, walletClient) => {
|
|
2616
|
+
if (acp.revokerContract === viem.zeroAddress || acp.revokerData === 0) {
|
|
2617
|
+
throw new Error("ACP is not revocable: it has no revoker (revokerContract/revokerData unset)");
|
|
2618
|
+
}
|
|
2619
|
+
if (walletClient.account == null)
|
|
2620
|
+
throw new Error("Missing walletClient account");
|
|
2621
|
+
if (walletClient.account.address.toLowerCase() !== acp.issuer.toLowerCase()) {
|
|
2622
|
+
throw new Error("Only the acp issuer can revoke it");
|
|
2623
|
+
}
|
|
2624
|
+
return walletClient.writeContract({
|
|
2625
|
+
address: acp.revokerContract,
|
|
2626
|
+
abi: ACP_VALIDATOR_ABI,
|
|
2627
|
+
functionName: "revokeSingle",
|
|
2628
|
+
args: [BigInt(acp.revokerData)],
|
|
2629
|
+
account: walletClient.account,
|
|
2630
|
+
chain: walletClient.chain
|
|
2631
|
+
});
|
|
2632
|
+
};
|
|
2633
|
+
var revokeAllACPs = async (walletClient, publicClient, revokerContract) => {
|
|
2634
|
+
if (walletClient.account == null)
|
|
2635
|
+
throw new Error("Missing walletClient account");
|
|
2636
|
+
let revoker = revokerContract;
|
|
2637
|
+
if (revoker == null) {
|
|
2638
|
+
const chainId = await publicClient.getChainId();
|
|
2639
|
+
const active = getActiveACP2(chainId, walletClient.account.address);
|
|
2640
|
+
revoker = active?.revokerContract;
|
|
2641
|
+
}
|
|
2642
|
+
if (revoker == null || revoker === viem.zeroAddress) {
|
|
2643
|
+
throw new Error("No revoker contract: pass `revokerContract` or activate a revocable acp first");
|
|
2644
|
+
}
|
|
2645
|
+
return walletClient.writeContract({
|
|
2646
|
+
address: revoker,
|
|
2647
|
+
abi: ACP_VALIDATOR_ABI,
|
|
2648
|
+
functionName: "revokeAllExisting",
|
|
2649
|
+
args: [],
|
|
2650
|
+
account: walletClient.account,
|
|
2651
|
+
chain: walletClient.chain
|
|
2652
|
+
});
|
|
2653
|
+
};
|
|
2654
|
+
var isACPRevoked = async (acp, publicClient) => {
|
|
2655
|
+
if (acp.revokerContract === viem.zeroAddress || acp.revokerData === 0)
|
|
2656
|
+
return false;
|
|
2657
|
+
return publicClient.readContract({
|
|
2658
|
+
address: acp.revokerContract,
|
|
2659
|
+
abi: ACP_VALIDATOR_ABI,
|
|
2660
|
+
functionName: "disabled",
|
|
2661
|
+
args: [acp.issuer, BigInt(acp.revokerData)]
|
|
2662
|
+
});
|
|
2663
|
+
};
|
|
2664
|
+
var ACP_SHARE_REGISTRY_ABI = viem.parseAbi([
|
|
2665
|
+
"struct ACP { address issuer; uint64 expiration; address recipient; uint256 revokerData; address revokerContract; uint8 scope; address[] contracts; bytes32[] handles; bytes32 sealingKey; bytes issuerSignature; bytes recipientSignature; }",
|
|
2666
|
+
"function share(ACP calldata acp) external returns (bytes32)",
|
|
2667
|
+
"function removeShare(bytes32 shareId) external",
|
|
2668
|
+
"function sharesFor(address recipient) external view returns (ACP[] memory)",
|
|
2669
|
+
"function getShare(bytes32 shareId) external view returns (ACP memory)",
|
|
2670
|
+
"function isShareValid(bytes32 shareId) external view returns (bool)"
|
|
2671
|
+
]);
|
|
2672
|
+
var ACP_TUPLE = [
|
|
2673
|
+
{
|
|
2674
|
+
type: "tuple",
|
|
2675
|
+
components: [
|
|
2676
|
+
{ name: "issuer", type: "address" },
|
|
2677
|
+
{ name: "expiration", type: "uint64" },
|
|
2678
|
+
{ name: "recipient", type: "address" },
|
|
2679
|
+
{ name: "revokerData", type: "uint256" },
|
|
2680
|
+
{ name: "revokerContract", type: "address" },
|
|
2681
|
+
{ name: "scope", type: "uint8" },
|
|
2682
|
+
{ name: "contracts", type: "address[]" },
|
|
2683
|
+
{ name: "handles", type: "bytes32[]" },
|
|
2684
|
+
{ name: "sealingKey", type: "bytes32" },
|
|
2685
|
+
{ name: "issuerSignature", type: "bytes" },
|
|
2686
|
+
{ name: "recipientSignature", type: "bytes" }
|
|
2687
|
+
]
|
|
2688
|
+
}
|
|
2689
|
+
];
|
|
2690
|
+
var ZERO_BYTES32 = `0x${"0".repeat(64)}`;
|
|
2691
|
+
var toChainShare = (acp) => {
|
|
2692
|
+
const pub = ACPUtils.getPublic(acp, true);
|
|
2693
|
+
return {
|
|
2694
|
+
...pub,
|
|
2695
|
+
expiration: BigInt(pub.expiration),
|
|
2696
|
+
revokerData: BigInt(pub.revokerData),
|
|
2697
|
+
sealingKey: ZERO_BYTES32,
|
|
2698
|
+
recipientSignature: "0x"
|
|
2699
|
+
};
|
|
2700
|
+
};
|
|
2701
|
+
var computeShareId = (acp) => {
|
|
2702
|
+
const p = toChainShare(acp);
|
|
2703
|
+
return viem.keccak256(viem.encodeAbiParameters(ACP_TUPLE, [p]));
|
|
2704
|
+
};
|
|
2705
|
+
var shareOnChain = async (acp, walletClient, registry) => {
|
|
2706
|
+
if (acp.type !== "sharing") {
|
|
2707
|
+
throw new Error(`Cannot share a '${acp.type}' ACP on-chain \u2014 only 'sharing' ACPs are shareable.`);
|
|
2708
|
+
}
|
|
2709
|
+
if (acp.issuerSignature === "0x") {
|
|
2710
|
+
throw new Error("Cannot share an unsigned sharing ACP \u2014 sign it first.");
|
|
2711
|
+
}
|
|
2712
|
+
if (walletClient.account == null)
|
|
2713
|
+
throw new Error("Missing walletClient account");
|
|
2714
|
+
if (walletClient.account.address.toLowerCase() !== acp.issuer.toLowerCase()) {
|
|
2715
|
+
throw new Error("Only the ACP issuer can share it on-chain");
|
|
2716
|
+
}
|
|
2717
|
+
const txHash = await walletClient.writeContract({
|
|
2718
|
+
address: registry,
|
|
2719
|
+
abi: ACP_SHARE_REGISTRY_ABI,
|
|
2720
|
+
functionName: "share",
|
|
2721
|
+
args: [toChainShare(acp)],
|
|
2722
|
+
account: walletClient.account,
|
|
2723
|
+
chain: walletClient.chain ?? null
|
|
2724
|
+
});
|
|
2725
|
+
return { txHash, shareId: computeShareId(acp) };
|
|
2726
|
+
};
|
|
2727
|
+
var getIncomingShares = async (publicClient, registry, recipient) => {
|
|
2728
|
+
const raw = await publicClient.readContract({
|
|
2729
|
+
address: registry,
|
|
2730
|
+
abi: ACP_SHARE_REGISTRY_ABI,
|
|
2731
|
+
functionName: "sharesFor",
|
|
2732
|
+
args: [recipient]
|
|
2733
|
+
});
|
|
2734
|
+
return raw.map((s) => ({
|
|
2735
|
+
shareId: viem.keccak256(viem.encodeAbiParameters(ACP_TUPLE, [s])),
|
|
2736
|
+
issuer: s.issuer,
|
|
2737
|
+
expiration: Number(s.expiration),
|
|
2738
|
+
recipient: s.recipient,
|
|
2739
|
+
revokerData: Number(s.revokerData),
|
|
2740
|
+
revokerContract: s.revokerContract,
|
|
2741
|
+
scope: Number(s.scope),
|
|
2742
|
+
contracts: [...s.contracts],
|
|
2743
|
+
handles: [...s.handles],
|
|
2744
|
+
issuerSignature: s.issuerSignature
|
|
2745
|
+
}));
|
|
2746
|
+
};
|
|
2747
|
+
var importFromChain = async (share, publicClient, walletClient) => {
|
|
2748
|
+
const { shareId: _shareId, ...options } = share;
|
|
2749
|
+
return importShared({ ...options, type: "sharing" }, publicClient, walletClient);
|
|
2750
|
+
};
|
|
2751
|
+
var removeShareOnChain = async (shareId, walletClient, registry) => {
|
|
2752
|
+
if (walletClient.account == null)
|
|
2753
|
+
throw new Error("Missing walletClient account");
|
|
2754
|
+
return walletClient.writeContract({
|
|
2755
|
+
address: registry,
|
|
2756
|
+
abi: ACP_SHARE_REGISTRY_ABI,
|
|
2757
|
+
functionName: "removeShare",
|
|
2758
|
+
args: [shareId],
|
|
2759
|
+
account: walletClient.account,
|
|
2760
|
+
chain: walletClient.chain ?? null
|
|
2761
|
+
});
|
|
2762
|
+
};
|
|
2763
|
+
var removeACP2 = async (chainId, account, hash) => acpStore.removeACP(chainId, account, hash);
|
|
2764
|
+
var removeActiveACP = async (chainId, account) => acpStore.removeActiveACPHash(chainId, account);
|
|
2765
|
+
var acps = {
|
|
2766
|
+
getSnapshot: acpStore.store.getState,
|
|
2767
|
+
subscribe: acpStore.store.subscribe,
|
|
2584
2768
|
createSelf,
|
|
2585
2769
|
createSharing,
|
|
2586
2770
|
importShared,
|
|
2587
|
-
|
|
2588
|
-
|
|
2771
|
+
getOrCreateSelfACP,
|
|
2772
|
+
getOrCreateSharingACP,
|
|
2589
2773
|
getHash,
|
|
2590
2774
|
export: exportShared,
|
|
2591
2775
|
serialize,
|
|
2592
2776
|
deserialize,
|
|
2593
|
-
|
|
2594
|
-
|
|
2595
|
-
|
|
2596
|
-
|
|
2597
|
-
|
|
2598
|
-
|
|
2599
|
-
|
|
2777
|
+
getACP: getACP2,
|
|
2778
|
+
getACPs: getACPs2,
|
|
2779
|
+
getActiveACP: getActiveACP2,
|
|
2780
|
+
getActiveACPHash: getActiveACPHash2,
|
|
2781
|
+
removeACP: removeACP2,
|
|
2782
|
+
selectActiveACP,
|
|
2783
|
+
removeActiveACP,
|
|
2784
|
+
revokeACP,
|
|
2785
|
+
revokeAllACPs,
|
|
2786
|
+
isACPRevoked,
|
|
2787
|
+
shareOnChain,
|
|
2788
|
+
getIncomingShares,
|
|
2789
|
+
importFromChain,
|
|
2790
|
+
removeShareOnChain,
|
|
2791
|
+
computeShareId,
|
|
2792
|
+
applyACPDefaults,
|
|
2793
|
+
applyACPDefaultsFromChain,
|
|
2794
|
+
getAclServedAddresses,
|
|
2795
|
+
clearAclServedAddresses
|
|
2600
2796
|
};
|
|
2601
2797
|
function uint160ToAddress(uint160) {
|
|
2602
2798
|
const hexStr = uint160.toString(16).padStart(40, "0");
|
|
@@ -2659,13 +2855,16 @@ var MockThresholdNetworkAbi = [
|
|
|
2659
2855
|
{
|
|
2660
2856
|
name: "permission",
|
|
2661
2857
|
type: "tuple",
|
|
2662
|
-
internalType: "struct
|
|
2858
|
+
internalType: "struct ACPermission",
|
|
2663
2859
|
components: [
|
|
2664
2860
|
{ name: "issuer", type: "address", internalType: "address" },
|
|
2665
2861
|
{ name: "expiration", type: "uint64", internalType: "uint64" },
|
|
2666
2862
|
{ name: "recipient", type: "address", internalType: "address" },
|
|
2667
|
-
{ name: "
|
|
2668
|
-
{ name: "
|
|
2863
|
+
{ name: "revokerData", type: "uint256", internalType: "uint256" },
|
|
2864
|
+
{ name: "revokerContract", type: "address", internalType: "address" },
|
|
2865
|
+
{ name: "scope", type: "uint8", internalType: "uint8" },
|
|
2866
|
+
{ name: "contracts", type: "address[]", internalType: "address[]" },
|
|
2867
|
+
{ name: "handles", type: "bytes32[]", internalType: "bytes32[]" },
|
|
2669
2868
|
{ name: "sealingKey", type: "bytes32", internalType: "bytes32" },
|
|
2670
2869
|
{ name: "issuerSignature", type: "bytes", internalType: "bytes" },
|
|
2671
2870
|
{ name: "recipientSignature", type: "bytes", internalType: "bytes" }
|
|
@@ -2688,13 +2887,16 @@ var MockThresholdNetworkAbi = [
|
|
|
2688
2887
|
{
|
|
2689
2888
|
name: "permission",
|
|
2690
2889
|
type: "tuple",
|
|
2691
|
-
internalType: "struct
|
|
2890
|
+
internalType: "struct ACPermission",
|
|
2692
2891
|
components: [
|
|
2693
2892
|
{ name: "issuer", type: "address", internalType: "address" },
|
|
2694
2893
|
{ name: "expiration", type: "uint64", internalType: "uint64" },
|
|
2695
2894
|
{ name: "recipient", type: "address", internalType: "address" },
|
|
2696
|
-
{ name: "
|
|
2697
|
-
{ name: "
|
|
2895
|
+
{ name: "revokerData", type: "uint256", internalType: "uint256" },
|
|
2896
|
+
{ name: "revokerContract", type: "address", internalType: "address" },
|
|
2897
|
+
{ name: "scope", type: "uint8", internalType: "uint8" },
|
|
2898
|
+
{ name: "contracts", type: "address[]", internalType: "address[]" },
|
|
2899
|
+
{ name: "handles", type: "bytes32[]", internalType: "bytes32[]" },
|
|
2698
2900
|
{ name: "sealingKey", type: "bytes32", internalType: "bytes32" },
|
|
2699
2901
|
{ name: "issuerSignature", type: "bytes", internalType: "bytes" },
|
|
2700
2902
|
{ name: "recipientSignature", type: "bytes", internalType: "bytes" }
|
|
@@ -2759,19 +2961,22 @@ var MockThresholdNetworkAbi = [
|
|
|
2759
2961
|
},
|
|
2760
2962
|
{
|
|
2761
2963
|
type: "function",
|
|
2762
|
-
name: "
|
|
2964
|
+
name: "decryptForTxWithACP",
|
|
2763
2965
|
inputs: [
|
|
2764
2966
|
{ name: "ctHash", type: "uint256", internalType: "uint256" },
|
|
2765
2967
|
{
|
|
2766
2968
|
name: "permission",
|
|
2767
2969
|
type: "tuple",
|
|
2768
|
-
internalType: "struct
|
|
2970
|
+
internalType: "struct ACPermission",
|
|
2769
2971
|
components: [
|
|
2770
2972
|
{ name: "issuer", type: "address", internalType: "address" },
|
|
2771
2973
|
{ name: "expiration", type: "uint64", internalType: "uint64" },
|
|
2772
2974
|
{ name: "recipient", type: "address", internalType: "address" },
|
|
2773
|
-
{ name: "
|
|
2774
|
-
{ name: "
|
|
2975
|
+
{ name: "revokerData", type: "uint256", internalType: "uint256" },
|
|
2976
|
+
{ name: "revokerContract", type: "address", internalType: "address" },
|
|
2977
|
+
{ name: "scope", type: "uint8", internalType: "uint8" },
|
|
2978
|
+
{ name: "contracts", type: "address[]", internalType: "address[]" },
|
|
2979
|
+
{ name: "handles", type: "bytes32[]", internalType: "bytes32[]" },
|
|
2775
2980
|
{ name: "sealingKey", type: "bytes32", internalType: "bytes32" },
|
|
2776
2981
|
{ name: "issuerSignature", type: "bytes", internalType: "bytes" },
|
|
2777
2982
|
{ name: "recipientSignature", type: "bytes", internalType: "bytes" }
|
|
@@ -2787,7 +2992,7 @@ var MockThresholdNetworkAbi = [
|
|
|
2787
2992
|
},
|
|
2788
2993
|
{
|
|
2789
2994
|
type: "function",
|
|
2790
|
-
name: "
|
|
2995
|
+
name: "decryptForTxWithoutACP",
|
|
2791
2996
|
inputs: [{ name: "ctHash", type: "uint256", internalType: "uint256" }],
|
|
2792
2997
|
outputs: [
|
|
2793
2998
|
{ name: "allowed", type: "bool", internalType: "bool" },
|
|
@@ -2799,12 +3004,12 @@ var MockThresholdNetworkAbi = [
|
|
|
2799
3004
|
];
|
|
2800
3005
|
|
|
2801
3006
|
// core/decrypt/cofheMocksDecryptForView.ts
|
|
2802
|
-
async function cofheMocksDecryptForView(ctHash, utype,
|
|
2803
|
-
const
|
|
3007
|
+
async function cofheMocksDecryptForView(ctHash, utype, acp, publicClient) {
|
|
3008
|
+
const wireAcp = ACPUtils.getPublic(acp, true);
|
|
2804
3009
|
const permissionWithBigInts = {
|
|
2805
|
-
...
|
|
2806
|
-
expiration: BigInt(
|
|
2807
|
-
|
|
3010
|
+
...wireAcp,
|
|
3011
|
+
expiration: BigInt(wireAcp.expiration),
|
|
3012
|
+
revokerData: BigInt(wireAcp.revokerData)
|
|
2808
3013
|
};
|
|
2809
3014
|
const [allowed, error, result] = await publicClient.readContract({
|
|
2810
3015
|
address: MOCKS_THRESHOLD_NETWORK_ADDRESS,
|
|
@@ -2825,11 +3030,34 @@ async function cofheMocksDecryptForView(ctHash, utype, permit, publicClient) {
|
|
|
2825
3030
|
});
|
|
2826
3031
|
}
|
|
2827
3032
|
const sealedBigInt = BigInt(result);
|
|
2828
|
-
const sealingKeyBigInt = BigInt(
|
|
3033
|
+
const sealingKeyBigInt = BigInt(acp.sealingKey);
|
|
2829
3034
|
const unsealed = sealedBigInt ^ sealingKeyBigInt;
|
|
2830
3035
|
return unsealed;
|
|
2831
3036
|
}
|
|
2832
3037
|
|
|
3038
|
+
// core/debug.ts
|
|
3039
|
+
var interceptors = {};
|
|
3040
|
+
async function cofheFetch(url, init, ctx = { op: "request" }) {
|
|
3041
|
+
let finalUrl = url;
|
|
3042
|
+
let finalInit = init;
|
|
3043
|
+
if (interceptors.onRequest) {
|
|
3044
|
+
const override = await interceptors.onRequest(finalUrl, finalInit, ctx);
|
|
3045
|
+
if (override) {
|
|
3046
|
+
if (override.url !== void 0)
|
|
3047
|
+
finalUrl = override.url;
|
|
3048
|
+
if (override.init !== void 0)
|
|
3049
|
+
finalInit = override.init;
|
|
3050
|
+
}
|
|
3051
|
+
}
|
|
3052
|
+
let response = await fetch(finalUrl, finalInit);
|
|
3053
|
+
if (interceptors.onResponse) {
|
|
3054
|
+
const replaced = await interceptors.onResponse(response, ctx);
|
|
3055
|
+
if (replaced)
|
|
3056
|
+
response = replaced;
|
|
3057
|
+
}
|
|
3058
|
+
return response;
|
|
3059
|
+
}
|
|
3060
|
+
|
|
2833
3061
|
// core/decrypt/polling.ts
|
|
2834
3062
|
function computeMinuteRampPollIntervalMs(elapsedMs, params) {
|
|
2835
3063
|
const elapsedSeconds = Math.floor(elapsedMs / 1e3);
|
|
@@ -2838,11 +3066,61 @@ function computeMinuteRampPollIntervalMs(elapsedMs, params) {
|
|
|
2838
3066
|
return Math.min(params.maxIntervalMs, Math.max(params.minIntervalMs, intervalMs));
|
|
2839
3067
|
}
|
|
2840
3068
|
|
|
3069
|
+
// core/decrypt/apiError.ts
|
|
3070
|
+
var BACKEND_ERROR_CODE_TO_COFHE_ERROR_CODE = {
|
|
3071
|
+
bad_request: "BAD_REQUEST" /* BadRequest */,
|
|
3072
|
+
unknown_chain: "UNKNOWN_CHAIN" /* UnknownChain */,
|
|
3073
|
+
acp_malformed: "ACP_MALFORMED" /* ACPMalformed */,
|
|
3074
|
+
acp_denied: "ACP_DENIED" /* ACPDenied */,
|
|
3075
|
+
// ACP-era backends fold revoked into denied
|
|
3076
|
+
acp_expired: "ACP_EXPIRED" /* ACPExpired */,
|
|
3077
|
+
acp_invalid: "ACP_INVALID" /* ACPInvalid */,
|
|
3078
|
+
acp_required: "ACP_REQUIRED" /* ACPRequired */,
|
|
3079
|
+
acp_verifier_error: "ACP_VERIFIER_ERROR" /* ACPVerifierError */,
|
|
3080
|
+
acp_verifier_timeout: "ACP_VERIFIER_TIMEOUT" /* ACPVerifierTimeout */,
|
|
3081
|
+
not_publicly_allowed: "NOT_PUBLICLY_ALLOWED" /* NotPubliclyAllowed */,
|
|
3082
|
+
ct_not_found: "CT_NOT_FOUND" /* CtNotFound */,
|
|
3083
|
+
unsupported_security_zone: "UNSUPPORTED_SECURITY_ZONE" /* UnsupportedSecurityZone */,
|
|
3084
|
+
unsupported_type: "UNSUPPORTED_TYPE" /* UnsupportedType */,
|
|
3085
|
+
internal_error: "INTERNAL_ERROR" /* InternalError */,
|
|
3086
|
+
signing_failed: "SIGNING_FAILED" /* SigningFailed */,
|
|
3087
|
+
ct_source_error: "CT_SOURCE_ERROR" /* CtSourceError */,
|
|
3088
|
+
ct_source_timeout: "CT_SOURCE_TIMEOUT" /* CtSourceTimeout */,
|
|
3089
|
+
seal_failed: "SEAL_FAILED" /* SealFailed */
|
|
3090
|
+
};
|
|
3091
|
+
function isBackendApiErrorCode(value) {
|
|
3092
|
+
return value in BACKEND_ERROR_CODE_TO_COFHE_ERROR_CODE;
|
|
3093
|
+
}
|
|
3094
|
+
async function parseApiErrorResponseBody(response) {
|
|
3095
|
+
let errorMessage = `HTTP ${response.status}`;
|
|
3096
|
+
let apiErrorCode;
|
|
3097
|
+
try {
|
|
3098
|
+
const body = await response.json();
|
|
3099
|
+
if (body && typeof body === "object") {
|
|
3100
|
+
const record = body;
|
|
3101
|
+
if (typeof record.error === "string" && record.error.length > 0) {
|
|
3102
|
+
apiErrorCode = record.error;
|
|
3103
|
+
}
|
|
3104
|
+
if (typeof record.error_message === "string" && record.error_message.length > 0) {
|
|
3105
|
+
errorMessage = record.error_message;
|
|
3106
|
+
} else if (typeof record.message === "string" && record.message.length > 0) {
|
|
3107
|
+
errorMessage = record.message;
|
|
3108
|
+
}
|
|
3109
|
+
}
|
|
3110
|
+
} catch {
|
|
3111
|
+
errorMessage = response.statusText || errorMessage;
|
|
3112
|
+
}
|
|
3113
|
+
return { apiErrorCode, errorMessage };
|
|
3114
|
+
}
|
|
3115
|
+
function mapApiErrorCodeToCofheErrorCode(apiErrorCode, fallback) {
|
|
3116
|
+
if (apiErrorCode && isBackendApiErrorCode(apiErrorCode)) {
|
|
3117
|
+
return BACKEND_ERROR_CODE_TO_COFHE_ERROR_CODE[apiErrorCode];
|
|
3118
|
+
}
|
|
3119
|
+
return fallback;
|
|
3120
|
+
}
|
|
3121
|
+
|
|
2841
3122
|
// core/decrypt/submitRetry.ts
|
|
2842
3123
|
var DEFAULT_404_RETRY_TIMEOUT_MS = 1e4;
|
|
2843
|
-
function isRetryableSubmitStatus(status) {
|
|
2844
|
-
return status === 204 || status === 404;
|
|
2845
|
-
}
|
|
2846
3124
|
function normalize404RetryTimeoutMs(params) {
|
|
2847
3125
|
const { timeoutMs, operationLabel, errorCode } = params;
|
|
2848
3126
|
if (timeoutMs === void 0)
|
|
@@ -2859,32 +3137,24 @@ function normalize404RetryTimeoutMs(params) {
|
|
|
2859
3137
|
return timeoutMs;
|
|
2860
3138
|
}
|
|
2861
3139
|
async function classifySubmitResponse(params) {
|
|
2862
|
-
const { response,
|
|
2863
|
-
if (
|
|
2864
|
-
return { kind: "retryable", status:
|
|
3140
|
+
const { response, fallbackErrorCode } = params;
|
|
3141
|
+
if (response.status === 204) {
|
|
3142
|
+
return { kind: "retryable", status: 204 };
|
|
2865
3143
|
}
|
|
2866
3144
|
if (response.ok) {
|
|
2867
3145
|
return { kind: "parse-json" };
|
|
2868
3146
|
}
|
|
2869
|
-
|
|
2870
|
-
|
|
2871
|
-
|
|
2872
|
-
const maybeErrorMessage = extractErrorMessage?.(errorBody);
|
|
2873
|
-
if (typeof maybeErrorMessage === "string" && maybeErrorMessage.length > 0) {
|
|
2874
|
-
errorMessage = maybeErrorMessage;
|
|
2875
|
-
} else if (errorBody && typeof errorBody === "object") {
|
|
2876
|
-
const defaultMessage = errorBody.error_message;
|
|
2877
|
-
const fallbackMessage = errorBody.message;
|
|
2878
|
-
if (typeof defaultMessage === "string" && defaultMessage.length > 0) {
|
|
2879
|
-
errorMessage = defaultMessage;
|
|
2880
|
-
} else if (typeof fallbackMessage === "string" && fallbackMessage.length > 0) {
|
|
2881
|
-
errorMessage = fallbackMessage;
|
|
2882
|
-
}
|
|
2883
|
-
}
|
|
2884
|
-
} catch {
|
|
2885
|
-
errorMessage = response.statusText || errorMessage;
|
|
3147
|
+
const { apiErrorCode, errorMessage } = await parseApiErrorResponseBody(response);
|
|
3148
|
+
if (response.status === 404) {
|
|
3149
|
+
return { kind: "retryable", status: 404, apiErrorMessage: errorMessage };
|
|
2886
3150
|
}
|
|
2887
|
-
return {
|
|
3151
|
+
return {
|
|
3152
|
+
kind: "fatal-http",
|
|
3153
|
+
status: response.status,
|
|
3154
|
+
cofheErrorCode: mapApiErrorCodeToCofheErrorCode(apiErrorCode, fallbackErrorCode),
|
|
3155
|
+
apiErrorCode,
|
|
3156
|
+
errorMessage
|
|
3157
|
+
};
|
|
2888
3158
|
}
|
|
2889
3159
|
function throwIfSubmitRetryTimedOut(params) {
|
|
2890
3160
|
const {
|
|
@@ -2896,12 +3166,14 @@ function throwIfSubmitRetryTimedOut(params) {
|
|
|
2896
3166
|
overallTimeoutMs,
|
|
2897
3167
|
thresholdNetworkUrl,
|
|
2898
3168
|
body,
|
|
2899
|
-
attemptIndex
|
|
3169
|
+
attemptIndex,
|
|
3170
|
+
lastKnownErrorMessage
|
|
2900
3171
|
} = params;
|
|
2901
3172
|
if (status === 404 && elapsedMs > retry404TimeoutMs) {
|
|
2902
3173
|
throw new CofheError({
|
|
2903
|
-
code:
|
|
2904
|
-
|
|
3174
|
+
code: "CT_NOT_FOUND" /* CtNotFound */,
|
|
3175
|
+
apiErrorCode: "ct_not_found",
|
|
3176
|
+
message: `${operationLabel} ciphertext not found after retrying for ${retry404TimeoutMs}ms` + (lastKnownErrorMessage ? `: ${lastKnownErrorMessage}` : ""),
|
|
2905
3177
|
hint: "The ciphertext may not be indexed yet. Increase set404RetryTimeout(...) if the backend is slow to index ciphertexts.",
|
|
2906
3178
|
context: {
|
|
2907
3179
|
thresholdNetworkUrl,
|
|
@@ -2927,6 +3199,62 @@ function throwIfSubmitRetryTimedOut(params) {
|
|
|
2927
3199
|
});
|
|
2928
3200
|
}
|
|
2929
3201
|
}
|
|
3202
|
+
function normalizeTnSignature(signature) {
|
|
3203
|
+
if (typeof signature !== "string") {
|
|
3204
|
+
throw new CofheError({
|
|
3205
|
+
code: "DECRYPT_RETURNED_NULL" /* DecryptReturnedNull */,
|
|
3206
|
+
message: "decrypt response missing signature",
|
|
3207
|
+
context: {
|
|
3208
|
+
signature
|
|
3209
|
+
}
|
|
3210
|
+
});
|
|
3211
|
+
}
|
|
3212
|
+
const trimmed = signature.trim();
|
|
3213
|
+
if (trimmed.length === 0) {
|
|
3214
|
+
throw new CofheError({
|
|
3215
|
+
code: "DECRYPT_RETURNED_NULL" /* DecryptReturnedNull */,
|
|
3216
|
+
message: "decrypt response returned empty signature"
|
|
3217
|
+
});
|
|
3218
|
+
}
|
|
3219
|
+
const prefixed = trimmed.startsWith("0x") ? trimmed : `0x${trimmed}`;
|
|
3220
|
+
const parsed = viem.parseSignature(prefixed);
|
|
3221
|
+
return viem.serializeSignature(parsed);
|
|
3222
|
+
}
|
|
3223
|
+
function parseDecryptedBytesToBigInt(decrypted) {
|
|
3224
|
+
if (!Array.isArray(decrypted)) {
|
|
3225
|
+
throw new CofheError({
|
|
3226
|
+
code: "DECRYPT_RETURNED_NULL" /* DecryptReturnedNull */,
|
|
3227
|
+
message: "decrypt response field <decrypted> must be a byte array",
|
|
3228
|
+
context: {
|
|
3229
|
+
decrypted
|
|
3230
|
+
}
|
|
3231
|
+
});
|
|
3232
|
+
}
|
|
3233
|
+
if (decrypted.length === 0) {
|
|
3234
|
+
throw new CofheError({
|
|
3235
|
+
code: "DECRYPT_RETURNED_NULL" /* DecryptReturnedNull */,
|
|
3236
|
+
message: "decrypt response field <decrypted> was an empty byte array",
|
|
3237
|
+
context: {
|
|
3238
|
+
decrypted
|
|
3239
|
+
}
|
|
3240
|
+
});
|
|
3241
|
+
}
|
|
3242
|
+
let hex = "";
|
|
3243
|
+
for (const b of decrypted) {
|
|
3244
|
+
if (typeof b !== "number" || !Number.isInteger(b) || b < 0 || b > 255) {
|
|
3245
|
+
throw new CofheError({
|
|
3246
|
+
code: "DECRYPT_RETURNED_NULL" /* DecryptReturnedNull */,
|
|
3247
|
+
message: "decrypt response field <decrypted> contained a non-byte value",
|
|
3248
|
+
context: {
|
|
3249
|
+
badElement: b,
|
|
3250
|
+
decrypted
|
|
3251
|
+
}
|
|
3252
|
+
});
|
|
3253
|
+
}
|
|
3254
|
+
hex += b.toString(16).padStart(2, "0");
|
|
3255
|
+
}
|
|
3256
|
+
return BigInt(`0x${hex}`);
|
|
3257
|
+
}
|
|
2930
3258
|
|
|
2931
3259
|
// core/decrypt/tnSealOutputV2.ts
|
|
2932
3260
|
var POLL_INTERVAL_MS = 1e3;
|
|
@@ -2989,23 +3317,28 @@ function parseCompletedSealOutputResponse(params) {
|
|
|
2989
3317
|
}
|
|
2990
3318
|
return convertSealedData(sealed);
|
|
2991
3319
|
}
|
|
2992
|
-
async function submitSealOutputRequest(thresholdNetworkUrl, ctHash, chainId,
|
|
3320
|
+
async function submitSealOutputRequest(thresholdNetworkUrl, ctHash, chainId, acp, overallStartTime, retry404TimeoutMs, onPoll) {
|
|
2993
3321
|
const body = {
|
|
2994
3322
|
ct_tempkey: BigInt(ctHash).toString(16).padStart(64, "0"),
|
|
2995
3323
|
host_chain_id: chainId,
|
|
2996
|
-
|
|
3324
|
+
acp
|
|
2997
3325
|
};
|
|
2998
3326
|
let attemptIndex = 0;
|
|
3327
|
+
let last404ApiErrorMessage;
|
|
2999
3328
|
for (; ; ) {
|
|
3000
3329
|
let response;
|
|
3001
3330
|
try {
|
|
3002
|
-
response = await
|
|
3003
|
-
|
|
3004
|
-
|
|
3005
|
-
|
|
3006
|
-
|
|
3007
|
-
|
|
3008
|
-
|
|
3331
|
+
response = await cofheFetch(
|
|
3332
|
+
`${thresholdNetworkUrl}/v2/sealoutput`,
|
|
3333
|
+
/*op:sealoutput*/
|
|
3334
|
+
{
|
|
3335
|
+
method: "POST",
|
|
3336
|
+
headers: {
|
|
3337
|
+
"Content-Type": "application/json"
|
|
3338
|
+
},
|
|
3339
|
+
body: JSON.stringify(body)
|
|
3340
|
+
}
|
|
3341
|
+
);
|
|
3009
3342
|
} catch (e) {
|
|
3010
3343
|
throw new CofheError({
|
|
3011
3344
|
code: "SEAL_OUTPUT_FAILED" /* SealOutputFailed */,
|
|
@@ -3019,16 +3352,19 @@ async function submitSealOutputRequest(thresholdNetworkUrl, ctHash, chainId, per
|
|
|
3019
3352
|
}
|
|
3020
3353
|
});
|
|
3021
3354
|
}
|
|
3022
|
-
const responseClassification = await classifySubmitResponse({
|
|
3355
|
+
const responseClassification = await classifySubmitResponse({
|
|
3356
|
+
response,
|
|
3357
|
+
fallbackErrorCode: "SEAL_OUTPUT_FAILED" /* SealOutputFailed */
|
|
3358
|
+
});
|
|
3023
3359
|
if (responseClassification.kind === "fatal-http") {
|
|
3024
3360
|
throw new CofheError({
|
|
3025
|
-
code:
|
|
3361
|
+
code: responseClassification.cofheErrorCode,
|
|
3362
|
+
apiErrorCode: responseClassification.apiErrorCode,
|
|
3026
3363
|
message: `sealOutput request failed: ${responseClassification.errorMessage}`,
|
|
3027
3364
|
hint: "Check the threshold network URL and request parameters.",
|
|
3028
3365
|
context: {
|
|
3029
3366
|
thresholdNetworkUrl,
|
|
3030
|
-
status:
|
|
3031
|
-
statusText: response.statusText,
|
|
3367
|
+
status: responseClassification.status,
|
|
3032
3368
|
body,
|
|
3033
3369
|
attemptIndex
|
|
3034
3370
|
}
|
|
@@ -3074,6 +3410,9 @@ async function submitSealOutputRequest(thresholdNetworkUrl, ctHash, chainId, per
|
|
|
3074
3410
|
}
|
|
3075
3411
|
});
|
|
3076
3412
|
}
|
|
3413
|
+
if (responseClassification.status === 404) {
|
|
3414
|
+
last404ApiErrorMessage = responseClassification.apiErrorMessage;
|
|
3415
|
+
}
|
|
3077
3416
|
const elapsedMs = Date.now() - overallStartTime;
|
|
3078
3417
|
throwIfSubmitRetryTimedOut({
|
|
3079
3418
|
operationLabel: "sealOutput",
|
|
@@ -3084,7 +3423,8 @@ async function submitSealOutputRequest(thresholdNetworkUrl, ctHash, chainId, per
|
|
|
3084
3423
|
overallTimeoutMs: SEAL_OUTPUT_TIMEOUT_MS,
|
|
3085
3424
|
thresholdNetworkUrl,
|
|
3086
3425
|
body,
|
|
3087
|
-
attemptIndex
|
|
3426
|
+
attemptIndex,
|
|
3427
|
+
lastKnownErrorMessage: last404ApiErrorMessage
|
|
3088
3428
|
});
|
|
3089
3429
|
onPoll?.({
|
|
3090
3430
|
operation: "sealoutput",
|
|
@@ -3128,12 +3468,16 @@ async function pollSealOutputStatus(thresholdNetworkUrl, requestId, overallStart
|
|
|
3128
3468
|
}
|
|
3129
3469
|
let response;
|
|
3130
3470
|
try {
|
|
3131
|
-
response = await
|
|
3132
|
-
|
|
3133
|
-
|
|
3134
|
-
|
|
3471
|
+
response = await cofheFetch(
|
|
3472
|
+
`${thresholdNetworkUrl}/v2/sealoutput/${requestId}`,
|
|
3473
|
+
/*op:sealoutput-poll*/
|
|
3474
|
+
{
|
|
3475
|
+
method: "GET",
|
|
3476
|
+
headers: {
|
|
3477
|
+
"Content-Type": "application/json"
|
|
3478
|
+
}
|
|
3135
3479
|
}
|
|
3136
|
-
|
|
3480
|
+
);
|
|
3137
3481
|
} catch (e) {
|
|
3138
3482
|
throw new CofheError({
|
|
3139
3483
|
code: "SEAL_OUTPUT_FAILED" /* SealOutputFailed */,
|
|
@@ -3158,15 +3502,10 @@ async function pollSealOutputStatus(thresholdNetworkUrl, requestId, overallStart
|
|
|
3158
3502
|
});
|
|
3159
3503
|
}
|
|
3160
3504
|
if (!response.ok) {
|
|
3161
|
-
|
|
3162
|
-
try {
|
|
3163
|
-
const errorBody = await response.json();
|
|
3164
|
-
errorMessage = errorBody.error_message || errorBody.message || errorMessage;
|
|
3165
|
-
} catch {
|
|
3166
|
-
errorMessage = response.statusText || errorMessage;
|
|
3167
|
-
}
|
|
3505
|
+
const { apiErrorCode, errorMessage } = await parseApiErrorResponseBody(response);
|
|
3168
3506
|
throw new CofheError({
|
|
3169
|
-
code: "SEAL_OUTPUT_FAILED" /* SealOutputFailed
|
|
3507
|
+
code: mapApiErrorCodeToCofheErrorCode(apiErrorCode, "SEAL_OUTPUT_FAILED" /* SealOutputFailed */),
|
|
3508
|
+
apiErrorCode,
|
|
3170
3509
|
message: `sealOutput status poll failed: ${errorMessage}`,
|
|
3171
3510
|
context: {
|
|
3172
3511
|
thresholdNetworkUrl,
|
|
@@ -3210,7 +3549,7 @@ async function pollSealOutputStatus(thresholdNetworkUrl, requestId, overallStart
|
|
|
3210
3549
|
});
|
|
3211
3550
|
}
|
|
3212
3551
|
async function tnSealOutputV2(params) {
|
|
3213
|
-
const { thresholdNetworkUrl, ctHash, chainId,
|
|
3552
|
+
const { thresholdNetworkUrl, ctHash, chainId, acp, retry404TimeoutMs, onPoll } = params;
|
|
3214
3553
|
const normalized404RetryTimeoutMs = normalize404RetryTimeoutMs({
|
|
3215
3554
|
timeoutMs: retry404TimeoutMs,
|
|
3216
3555
|
operationLabel: "sealOutput",
|
|
@@ -3221,7 +3560,7 @@ async function tnSealOutputV2(params) {
|
|
|
3221
3560
|
thresholdNetworkUrl,
|
|
3222
3561
|
ctHash,
|
|
3223
3562
|
chainId,
|
|
3224
|
-
|
|
3563
|
+
acp,
|
|
3225
3564
|
overallStartTime,
|
|
3226
3565
|
normalized404RetryTimeoutMs,
|
|
3227
3566
|
onPoll
|
|
@@ -3237,8 +3576,8 @@ var DEFAULT_404_RETRY_TIMEOUT_MS2 = 1e4;
|
|
|
3237
3576
|
var DecryptForViewBuilder = class extends BaseBuilder {
|
|
3238
3577
|
ctHash;
|
|
3239
3578
|
utype;
|
|
3240
|
-
|
|
3241
|
-
|
|
3579
|
+
acpHash;
|
|
3580
|
+
acp;
|
|
3242
3581
|
pollCallback;
|
|
3243
3582
|
retry404TimeoutMs = DEFAULT_404_RETRY_TIMEOUT_MS2;
|
|
3244
3583
|
constructor(params) {
|
|
@@ -3252,11 +3591,11 @@ var DecryptForViewBuilder = class extends BaseBuilder {
|
|
|
3252
3591
|
});
|
|
3253
3592
|
this.ctHash = params.ctHash;
|
|
3254
3593
|
this.utype = params.utype;
|
|
3255
|
-
this.
|
|
3256
|
-
this.
|
|
3594
|
+
this.acpHash = params.acpHash;
|
|
3595
|
+
this.acp = params.acp;
|
|
3257
3596
|
}
|
|
3258
3597
|
/**
|
|
3259
|
-
* @param chainId - Chain to decrypt values from. Used to fetch the threshold network URL and use the correct
|
|
3598
|
+
* @param chainId - Chain to decrypt values from. Used to fetch the threshold network URL and use the correct acp.
|
|
3260
3599
|
*
|
|
3261
3600
|
* If not provided, the chainId will be fetched from the connected publicClient.
|
|
3262
3601
|
*
|
|
@@ -3277,7 +3616,7 @@ var DecryptForViewBuilder = class extends BaseBuilder {
|
|
|
3277
3616
|
return this.chainId;
|
|
3278
3617
|
}
|
|
3279
3618
|
/**
|
|
3280
|
-
* @param account - Account to decrypt values from. Used to fetch the correct
|
|
3619
|
+
* @param account - Account to decrypt values from. Used to fetch the correct acp.
|
|
3281
3620
|
*
|
|
3282
3621
|
* If not provided, the account will be fetched from the connected walletClient.
|
|
3283
3622
|
*
|
|
@@ -3314,61 +3653,61 @@ var DecryptForViewBuilder = class extends BaseBuilder {
|
|
|
3314
3653
|
this.retry404TimeoutMs = timeoutMs;
|
|
3315
3654
|
return this;
|
|
3316
3655
|
}
|
|
3317
|
-
|
|
3318
|
-
if (typeof
|
|
3319
|
-
this.
|
|
3320
|
-
this.
|
|
3321
|
-
} else if (
|
|
3322
|
-
this.
|
|
3323
|
-
this.
|
|
3656
|
+
withACP(acpOrACPHash) {
|
|
3657
|
+
if (typeof acpOrACPHash === "string") {
|
|
3658
|
+
this.acpHash = acpOrACPHash;
|
|
3659
|
+
this.acp = void 0;
|
|
3660
|
+
} else if (acpOrACPHash === void 0) {
|
|
3661
|
+
this.acpHash = void 0;
|
|
3662
|
+
this.acp = void 0;
|
|
3324
3663
|
} else {
|
|
3325
|
-
this.
|
|
3326
|
-
this.
|
|
3664
|
+
this.acp = acpOrACPHash;
|
|
3665
|
+
this.acpHash = void 0;
|
|
3327
3666
|
}
|
|
3328
3667
|
return this;
|
|
3329
3668
|
}
|
|
3330
3669
|
/**
|
|
3331
|
-
* @param
|
|
3670
|
+
* @param acpHash - ACP hash to decrypt values from. Used to fetch the correct acp.
|
|
3332
3671
|
*
|
|
3333
|
-
* If not provided, the active
|
|
3334
|
-
* If `
|
|
3672
|
+
* If not provided, the active acp for the chainId and account will be used.
|
|
3673
|
+
* If `setACP()` is called, it will be used regardless of chainId, account, or acpHash.
|
|
3335
3674
|
*
|
|
3336
3675
|
* Example:
|
|
3337
3676
|
* ```typescript
|
|
3338
3677
|
* const unsealed = await client.decryptForView(ctHash, utype)
|
|
3339
|
-
* .
|
|
3678
|
+
* .setACPHash('0x1234567890123456789012345678901234567890')
|
|
3340
3679
|
* .execute();
|
|
3341
3680
|
* ```
|
|
3342
3681
|
*
|
|
3343
3682
|
* @returns The chainable DecryptForViewBuilder instance.
|
|
3344
3683
|
*/
|
|
3345
|
-
/** @deprecated Use `
|
|
3346
|
-
|
|
3347
|
-
return this.
|
|
3684
|
+
/** @deprecated Use `withACP(acpHash)` instead. */
|
|
3685
|
+
setACPHash(acpHash) {
|
|
3686
|
+
return this.withACP(acpHash);
|
|
3348
3687
|
}
|
|
3349
|
-
|
|
3350
|
-
return this.
|
|
3688
|
+
getACPHash() {
|
|
3689
|
+
return this.acpHash;
|
|
3351
3690
|
}
|
|
3352
3691
|
/**
|
|
3353
|
-
* @param
|
|
3692
|
+
* @param acp - ACP to decrypt values with. If provided, it will be used regardless of chainId, account, or acpHash.
|
|
3354
3693
|
*
|
|
3355
|
-
* If not provided, the
|
|
3694
|
+
* If not provided, the acp will be determined by chainId, account, and acpHash.
|
|
3356
3695
|
*
|
|
3357
3696
|
* Example:
|
|
3358
3697
|
* ```typescript
|
|
3359
3698
|
* const unsealed = await client.decryptForView(ctHash, utype)
|
|
3360
|
-
* .
|
|
3699
|
+
* .setACP(acp)
|
|
3361
3700
|
* .execute();
|
|
3362
3701
|
* ```
|
|
3363
3702
|
*
|
|
3364
3703
|
* @returns The chainable DecryptForViewBuilder instance.
|
|
3365
3704
|
*/
|
|
3366
|
-
/** @deprecated Use `
|
|
3367
|
-
|
|
3368
|
-
return this.
|
|
3705
|
+
/** @deprecated Use `withACP(acp)` instead. */
|
|
3706
|
+
setACP(acp) {
|
|
3707
|
+
return this.withACP(acp);
|
|
3369
3708
|
}
|
|
3370
|
-
|
|
3371
|
-
return this.
|
|
3709
|
+
getACP() {
|
|
3710
|
+
return this.acp;
|
|
3372
3711
|
}
|
|
3373
3712
|
async getThresholdNetworkUrl() {
|
|
3374
3713
|
this.assertChainId();
|
|
@@ -3384,77 +3723,77 @@ var DecryptForViewBuilder = class extends BaseBuilder {
|
|
|
3384
3723
|
}
|
|
3385
3724
|
});
|
|
3386
3725
|
}
|
|
3387
|
-
async
|
|
3388
|
-
if (this.
|
|
3389
|
-
return this.
|
|
3726
|
+
async getResolvedACP() {
|
|
3727
|
+
if (this.acp)
|
|
3728
|
+
return this.acp;
|
|
3390
3729
|
this.assertChainId();
|
|
3391
3730
|
this.assertAccount();
|
|
3392
|
-
if (this.
|
|
3393
|
-
const
|
|
3394
|
-
if (!
|
|
3731
|
+
if (this.acpHash) {
|
|
3732
|
+
const acp2 = await acps.getACP(this.chainId, this.account, this.acpHash);
|
|
3733
|
+
if (!acp2) {
|
|
3395
3734
|
throw new CofheError({
|
|
3396
|
-
code: "
|
|
3397
|
-
message: `
|
|
3398
|
-
hint: "Ensure the
|
|
3735
|
+
code: "ACP_NOT_FOUND" /* ACPNotFound */,
|
|
3736
|
+
message: `ACP with hash <${this.acpHash}> not found for account <${this.account}> and chainId <${this.chainId}>`,
|
|
3737
|
+
hint: "Ensure the acp exists and is valid.",
|
|
3399
3738
|
context: {
|
|
3400
3739
|
chainId: this.chainId,
|
|
3401
3740
|
account: this.account,
|
|
3402
|
-
|
|
3741
|
+
acpHash: this.acpHash
|
|
3403
3742
|
}
|
|
3404
3743
|
});
|
|
3405
3744
|
}
|
|
3406
|
-
return
|
|
3745
|
+
return acp2;
|
|
3407
3746
|
}
|
|
3408
|
-
const
|
|
3409
|
-
if (!
|
|
3747
|
+
const acp = await acps.getActiveACP(this.chainId, this.account);
|
|
3748
|
+
if (!acp) {
|
|
3410
3749
|
throw new CofheError({
|
|
3411
|
-
code: "
|
|
3412
|
-
message: `Active
|
|
3413
|
-
hint: "Ensure
|
|
3750
|
+
code: "ACP_NOT_FOUND" /* ACPNotFound */,
|
|
3751
|
+
message: `Active acp not found for chainId <${this.chainId}> and account <${this.account}>`,
|
|
3752
|
+
hint: "Ensure an ACP exists for this account on this chain.",
|
|
3414
3753
|
context: {
|
|
3415
3754
|
chainId: this.chainId,
|
|
3416
3755
|
account: this.account
|
|
3417
3756
|
}
|
|
3418
3757
|
});
|
|
3419
3758
|
}
|
|
3420
|
-
return
|
|
3759
|
+
return acp;
|
|
3421
3760
|
}
|
|
3422
3761
|
/**
|
|
3423
3762
|
* On hardhat, interact with MockZkVerifier contract instead of CoFHE
|
|
3424
3763
|
*/
|
|
3425
|
-
async mocksSealOutput(
|
|
3764
|
+
async mocksSealOutput(acp) {
|
|
3426
3765
|
this.assertPublicClient();
|
|
3427
3766
|
const mocksDecryptDelay = this.config.mocks.decryptDelay;
|
|
3428
3767
|
if (mocksDecryptDelay > 0)
|
|
3429
3768
|
await sleep(mocksDecryptDelay);
|
|
3430
|
-
return cofheMocksDecryptForView(this.ctHash, this.utype,
|
|
3769
|
+
return cofheMocksDecryptForView(this.ctHash, this.utype, acp, this.publicClient);
|
|
3431
3770
|
}
|
|
3432
3771
|
/**
|
|
3433
3772
|
* In the production context, perform a true decryption with the CoFHE coprocessor.
|
|
3434
3773
|
*/
|
|
3435
|
-
async productionSealOutput(
|
|
3774
|
+
async productionSealOutput(acp) {
|
|
3436
3775
|
this.assertChainId();
|
|
3437
3776
|
this.assertPublicClient();
|
|
3438
3777
|
const thresholdNetworkUrl = await this.getThresholdNetworkUrl();
|
|
3439
|
-
const
|
|
3778
|
+
const wireAcp = ACPUtils.getPublic(acp, true);
|
|
3440
3779
|
const sealed = await tnSealOutputV2({
|
|
3441
3780
|
ctHash: this.ctHash,
|
|
3442
3781
|
chainId: this.chainId,
|
|
3443
|
-
|
|
3782
|
+
acp: wireAcp,
|
|
3444
3783
|
thresholdNetworkUrl,
|
|
3445
3784
|
retry404TimeoutMs: this.retry404TimeoutMs,
|
|
3446
3785
|
onPoll: this.pollCallback
|
|
3447
3786
|
});
|
|
3448
|
-
return
|
|
3787
|
+
return ACPUtils.unseal(acp, sealed);
|
|
3449
3788
|
}
|
|
3450
3789
|
/**
|
|
3451
3790
|
* Final step of the decryption process. MUST BE CALLED LAST IN THE CHAIN.
|
|
3452
3791
|
*
|
|
3453
3792
|
* This will:
|
|
3454
|
-
* - Use
|
|
3455
|
-
* - Check
|
|
3456
|
-
* - Call CoFHE `/sealoutput` with the
|
|
3457
|
-
* - Unseal the sealed item with the
|
|
3793
|
+
* - Use an ACP based on provided acp OR chainId + account + acpHash
|
|
3794
|
+
* - Check acp validity
|
|
3795
|
+
* - Call CoFHE `/sealoutput` with the acp, which returns a sealed (encrypted) item
|
|
3796
|
+
* - Unseal the sealed item with the acp
|
|
3458
3797
|
* - Return the unsealed item
|
|
3459
3798
|
*
|
|
3460
3799
|
* Example:
|
|
@@ -3462,7 +3801,7 @@ var DecryptForViewBuilder = class extends BaseBuilder {
|
|
|
3462
3801
|
* const unsealed = await client.decryptForView(ctHash, utype)
|
|
3463
3802
|
* .setChainId(11155111) // optional
|
|
3464
3803
|
* .setAccount('0x123...890') // optional
|
|
3465
|
-
* .
|
|
3804
|
+
* .withACP() // optional
|
|
3466
3805
|
* .execute(); // execute
|
|
3467
3806
|
* ```
|
|
3468
3807
|
*
|
|
@@ -3470,14 +3809,14 @@ var DecryptForViewBuilder = class extends BaseBuilder {
|
|
|
3470
3809
|
*/
|
|
3471
3810
|
async execute() {
|
|
3472
3811
|
this.validateUtypeOrThrow();
|
|
3473
|
-
const
|
|
3474
|
-
|
|
3475
|
-
const chainId =
|
|
3812
|
+
const acp = await this.getResolvedACP();
|
|
3813
|
+
ACPUtils.validate(acp);
|
|
3814
|
+
const chainId = acp._signedDomain.chainId;
|
|
3476
3815
|
let unsealed;
|
|
3477
3816
|
if (chainId === hardhat2.id) {
|
|
3478
|
-
unsealed = await this.mocksSealOutput(
|
|
3817
|
+
unsealed = await this.mocksSealOutput(acp);
|
|
3479
3818
|
} else {
|
|
3480
|
-
unsealed = await this.productionSealOutput(
|
|
3819
|
+
unsealed = await this.productionSealOutput(acp);
|
|
3481
3820
|
}
|
|
3482
3821
|
return convertViaUtype(this.utype, unsealed);
|
|
3483
3822
|
}
|
|
@@ -3485,28 +3824,28 @@ var DecryptForViewBuilder = class extends BaseBuilder {
|
|
|
3485
3824
|
var UINT_TYPE_MASK = 0x7fn;
|
|
3486
3825
|
var TYPE_BYTE_OFFSET = 8n;
|
|
3487
3826
|
var getEncryptionTypeFromCtHash = (ctHash) => Number(ctHash >> TYPE_BYTE_OFFSET & UINT_TYPE_MASK);
|
|
3488
|
-
async function cofheMocksDecryptForTx(ctHash, utype,
|
|
3827
|
+
async function cofheMocksDecryptForTx(ctHash, utype, acp, publicClient) {
|
|
3489
3828
|
let allowed;
|
|
3490
3829
|
let error;
|
|
3491
3830
|
let decryptedValue;
|
|
3492
|
-
if (
|
|
3493
|
-
|
|
3831
|
+
if (acp !== null) {
|
|
3832
|
+
const wireAcp = ACPUtils.getPublic(acp, true);
|
|
3494
3833
|
const permissionWithBigInts = {
|
|
3495
|
-
...
|
|
3496
|
-
expiration: BigInt(
|
|
3497
|
-
|
|
3834
|
+
...wireAcp,
|
|
3835
|
+
expiration: BigInt(wireAcp.expiration),
|
|
3836
|
+
revokerData: BigInt(wireAcp.revokerData)
|
|
3498
3837
|
};
|
|
3499
3838
|
[allowed, error, decryptedValue] = await publicClient.readContract({
|
|
3500
3839
|
address: MOCKS_THRESHOLD_NETWORK_ADDRESS,
|
|
3501
3840
|
abi: MockThresholdNetworkAbi,
|
|
3502
|
-
functionName: "
|
|
3841
|
+
functionName: "decryptForTxWithACP",
|
|
3503
3842
|
args: [BigInt(ctHash), permissionWithBigInts]
|
|
3504
3843
|
});
|
|
3505
3844
|
} else {
|
|
3506
3845
|
[allowed, error, decryptedValue] = await publicClient.readContract({
|
|
3507
3846
|
address: MOCKS_THRESHOLD_NETWORK_ADDRESS,
|
|
3508
3847
|
abi: MockThresholdNetworkAbi,
|
|
3509
|
-
functionName: "
|
|
3848
|
+
functionName: "decryptForTxWithoutACP",
|
|
3510
3849
|
args: [BigInt(ctHash)]
|
|
3511
3850
|
});
|
|
3512
3851
|
}
|
|
@@ -3541,62 +3880,6 @@ async function cofheMocksDecryptForTx(ctHash, utype, permit, publicClient) {
|
|
|
3541
3880
|
signature
|
|
3542
3881
|
};
|
|
3543
3882
|
}
|
|
3544
|
-
function normalizeTnSignature(signature) {
|
|
3545
|
-
if (typeof signature !== "string") {
|
|
3546
|
-
throw new CofheError({
|
|
3547
|
-
code: "DECRYPT_RETURNED_NULL" /* DecryptReturnedNull */,
|
|
3548
|
-
message: "decrypt response missing signature",
|
|
3549
|
-
context: {
|
|
3550
|
-
signature
|
|
3551
|
-
}
|
|
3552
|
-
});
|
|
3553
|
-
}
|
|
3554
|
-
const trimmed = signature.trim();
|
|
3555
|
-
if (trimmed.length === 0) {
|
|
3556
|
-
throw new CofheError({
|
|
3557
|
-
code: "DECRYPT_RETURNED_NULL" /* DecryptReturnedNull */,
|
|
3558
|
-
message: "decrypt response returned empty signature"
|
|
3559
|
-
});
|
|
3560
|
-
}
|
|
3561
|
-
const prefixed = trimmed.startsWith("0x") ? trimmed : `0x${trimmed}`;
|
|
3562
|
-
const parsed = viem.parseSignature(prefixed);
|
|
3563
|
-
return viem.serializeSignature(parsed);
|
|
3564
|
-
}
|
|
3565
|
-
function parseDecryptedBytesToBigInt(decrypted) {
|
|
3566
|
-
if (!Array.isArray(decrypted)) {
|
|
3567
|
-
throw new CofheError({
|
|
3568
|
-
code: "DECRYPT_RETURNED_NULL" /* DecryptReturnedNull */,
|
|
3569
|
-
message: "decrypt response field <decrypted> must be a byte array",
|
|
3570
|
-
context: {
|
|
3571
|
-
decrypted
|
|
3572
|
-
}
|
|
3573
|
-
});
|
|
3574
|
-
}
|
|
3575
|
-
if (decrypted.length === 0) {
|
|
3576
|
-
throw new CofheError({
|
|
3577
|
-
code: "DECRYPT_RETURNED_NULL" /* DecryptReturnedNull */,
|
|
3578
|
-
message: "decrypt response field <decrypted> was an empty byte array",
|
|
3579
|
-
context: {
|
|
3580
|
-
decrypted
|
|
3581
|
-
}
|
|
3582
|
-
});
|
|
3583
|
-
}
|
|
3584
|
-
let hex = "";
|
|
3585
|
-
for (const b of decrypted) {
|
|
3586
|
-
if (typeof b !== "number" || !Number.isInteger(b) || b < 0 || b > 255) {
|
|
3587
|
-
throw new CofheError({
|
|
3588
|
-
code: "DECRYPT_RETURNED_NULL" /* DecryptReturnedNull */,
|
|
3589
|
-
message: "decrypt response field <decrypted> contained a non-byte value",
|
|
3590
|
-
context: {
|
|
3591
|
-
badElement: b,
|
|
3592
|
-
decrypted
|
|
3593
|
-
}
|
|
3594
|
-
});
|
|
3595
|
-
}
|
|
3596
|
-
hex += b.toString(16).padStart(2, "0");
|
|
3597
|
-
}
|
|
3598
|
-
return BigInt(`0x${hex}`);
|
|
3599
|
-
}
|
|
3600
3883
|
|
|
3601
3884
|
// core/decrypt/tnDecryptV2.ts
|
|
3602
3885
|
var POLL_INTERVAL_MS2 = 1e3;
|
|
@@ -3718,25 +4001,30 @@ function assertDecryptStatusResponseV2(value) {
|
|
|
3718
4001
|
}
|
|
3719
4002
|
return value;
|
|
3720
4003
|
}
|
|
3721
|
-
async function submitDecryptRequestV2(thresholdNetworkUrl, ctHash, chainId,
|
|
4004
|
+
async function submitDecryptRequestV2(thresholdNetworkUrl, ctHash, chainId, acp, overallStartTime, retry404TimeoutMs, onPoll) {
|
|
3722
4005
|
const body = {
|
|
3723
4006
|
ct_tempkey: BigInt(ctHash).toString(16).padStart(64, "0"),
|
|
3724
4007
|
host_chain_id: chainId
|
|
3725
4008
|
};
|
|
3726
|
-
if (
|
|
3727
|
-
body.
|
|
4009
|
+
if (acp) {
|
|
4010
|
+
body.acp = acp;
|
|
3728
4011
|
}
|
|
3729
4012
|
let attemptIndex = 0;
|
|
4013
|
+
let last404ApiErrorMessage;
|
|
3730
4014
|
for (; ; ) {
|
|
3731
4015
|
let response;
|
|
3732
4016
|
try {
|
|
3733
|
-
response = await
|
|
3734
|
-
|
|
3735
|
-
|
|
3736
|
-
|
|
3737
|
-
|
|
3738
|
-
|
|
3739
|
-
|
|
4017
|
+
response = await cofheFetch(
|
|
4018
|
+
`${thresholdNetworkUrl}/v2/decrypt`,
|
|
4019
|
+
/*op:decrypt*/
|
|
4020
|
+
{
|
|
4021
|
+
method: "POST",
|
|
4022
|
+
headers: {
|
|
4023
|
+
"Content-Type": "application/json"
|
|
4024
|
+
},
|
|
4025
|
+
body: JSON.stringify(body)
|
|
4026
|
+
}
|
|
4027
|
+
);
|
|
3740
4028
|
} catch (e) {
|
|
3741
4029
|
throw new CofheError({
|
|
3742
4030
|
code: "DECRYPT_FAILED" /* DecryptFailed */,
|
|
@@ -3750,16 +4038,19 @@ async function submitDecryptRequestV2(thresholdNetworkUrl, ctHash, chainId, perm
|
|
|
3750
4038
|
}
|
|
3751
4039
|
});
|
|
3752
4040
|
}
|
|
3753
|
-
const responseClassification = await classifySubmitResponse({
|
|
4041
|
+
const responseClassification = await classifySubmitResponse({
|
|
4042
|
+
response,
|
|
4043
|
+
fallbackErrorCode: "DECRYPT_FAILED" /* DecryptFailed */
|
|
4044
|
+
});
|
|
3754
4045
|
if (responseClassification.kind === "fatal-http") {
|
|
3755
4046
|
throw new CofheError({
|
|
3756
|
-
code:
|
|
4047
|
+
code: responseClassification.cofheErrorCode,
|
|
4048
|
+
apiErrorCode: responseClassification.apiErrorCode,
|
|
3757
4049
|
message: `decrypt request failed: ${responseClassification.errorMessage}`,
|
|
3758
4050
|
hint: "Check the threshold network URL and request parameters.",
|
|
3759
4051
|
context: {
|
|
3760
4052
|
thresholdNetworkUrl,
|
|
3761
|
-
status:
|
|
3762
|
-
statusText: response.statusText,
|
|
4053
|
+
status: responseClassification.status,
|
|
3763
4054
|
body,
|
|
3764
4055
|
attemptIndex
|
|
3765
4056
|
}
|
|
@@ -3807,6 +4098,9 @@ async function submitDecryptRequestV2(thresholdNetworkUrl, ctHash, chainId, perm
|
|
|
3807
4098
|
}
|
|
3808
4099
|
});
|
|
3809
4100
|
}
|
|
4101
|
+
if (responseClassification.status === 404) {
|
|
4102
|
+
last404ApiErrorMessage = responseClassification.apiErrorMessage;
|
|
4103
|
+
}
|
|
3810
4104
|
const elapsedMs = Date.now() - overallStartTime;
|
|
3811
4105
|
throwIfSubmitRetryTimedOut({
|
|
3812
4106
|
operationLabel: "decrypt",
|
|
@@ -3817,7 +4111,8 @@ async function submitDecryptRequestV2(thresholdNetworkUrl, ctHash, chainId, perm
|
|
|
3817
4111
|
overallTimeoutMs: DECRYPT_TIMEOUT_MS,
|
|
3818
4112
|
thresholdNetworkUrl,
|
|
3819
4113
|
body,
|
|
3820
|
-
attemptIndex
|
|
4114
|
+
attemptIndex,
|
|
4115
|
+
lastKnownErrorMessage: last404ApiErrorMessage
|
|
3821
4116
|
});
|
|
3822
4117
|
onPoll?.({
|
|
3823
4118
|
operation: "decrypt",
|
|
@@ -3861,12 +4156,16 @@ async function pollDecryptStatusV2(thresholdNetworkUrl, requestId, overallStartT
|
|
|
3861
4156
|
}
|
|
3862
4157
|
let response;
|
|
3863
4158
|
try {
|
|
3864
|
-
response = await
|
|
3865
|
-
|
|
3866
|
-
|
|
3867
|
-
|
|
4159
|
+
response = await cofheFetch(
|
|
4160
|
+
`${thresholdNetworkUrl}/v2/decrypt/${requestId}`,
|
|
4161
|
+
/*op:decrypt-poll*/
|
|
4162
|
+
{
|
|
4163
|
+
method: "GET",
|
|
4164
|
+
headers: {
|
|
4165
|
+
"Content-Type": "application/json"
|
|
4166
|
+
}
|
|
3868
4167
|
}
|
|
3869
|
-
|
|
4168
|
+
);
|
|
3870
4169
|
} catch (e) {
|
|
3871
4170
|
throw new CofheError({
|
|
3872
4171
|
code: "DECRYPT_FAILED" /* DecryptFailed */,
|
|
@@ -3891,17 +4190,10 @@ async function pollDecryptStatusV2(thresholdNetworkUrl, requestId, overallStartT
|
|
|
3891
4190
|
});
|
|
3892
4191
|
}
|
|
3893
4192
|
if (!response.ok) {
|
|
3894
|
-
|
|
3895
|
-
try {
|
|
3896
|
-
const errorBody = await response.json();
|
|
3897
|
-
const maybeMessage = errorBody.error_message || errorBody.message;
|
|
3898
|
-
if (typeof maybeMessage === "string" && maybeMessage.length > 0)
|
|
3899
|
-
errorMessage = maybeMessage;
|
|
3900
|
-
} catch {
|
|
3901
|
-
errorMessage = response.statusText || errorMessage;
|
|
3902
|
-
}
|
|
4193
|
+
const { apiErrorCode, errorMessage } = await parseApiErrorResponseBody(response);
|
|
3903
4194
|
throw new CofheError({
|
|
3904
|
-
code: "DECRYPT_FAILED" /* DecryptFailed
|
|
4195
|
+
code: mapApiErrorCodeToCofheErrorCode(apiErrorCode, "DECRYPT_FAILED" /* DecryptFailed */),
|
|
4196
|
+
apiErrorCode,
|
|
3905
4197
|
message: `decrypt status poll failed: ${errorMessage}`,
|
|
3906
4198
|
context: {
|
|
3907
4199
|
thresholdNetworkUrl,
|
|
@@ -3946,7 +4238,7 @@ async function pollDecryptStatusV2(thresholdNetworkUrl, requestId, overallStartT
|
|
|
3946
4238
|
});
|
|
3947
4239
|
}
|
|
3948
4240
|
async function tnDecryptV2(params) {
|
|
3949
|
-
const { thresholdNetworkUrl, ctHash, chainId,
|
|
4241
|
+
const { thresholdNetworkUrl, ctHash, chainId, acp, retry404TimeoutMs, onPoll } = params;
|
|
3950
4242
|
const normalized404RetryTimeoutMs = normalize404RetryTimeoutMs({
|
|
3951
4243
|
timeoutMs: retry404TimeoutMs,
|
|
3952
4244
|
operationLabel: "decrypt",
|
|
@@ -3957,7 +4249,7 @@ async function tnDecryptV2(params) {
|
|
|
3957
4249
|
thresholdNetworkUrl,
|
|
3958
4250
|
ctHash,
|
|
3959
4251
|
chainId,
|
|
3960
|
-
|
|
4252
|
+
acp,
|
|
3961
4253
|
overallStartTime,
|
|
3962
4254
|
normalized404RetryTimeoutMs,
|
|
3963
4255
|
onPoll
|
|
@@ -3972,9 +4264,9 @@ async function tnDecryptV2(params) {
|
|
|
3972
4264
|
var DEFAULT_404_RETRY_TIMEOUT_MS3 = 1e4;
|
|
3973
4265
|
var DecryptForTxBuilder = class extends BaseBuilder {
|
|
3974
4266
|
ctHash;
|
|
3975
|
-
|
|
3976
|
-
|
|
3977
|
-
|
|
4267
|
+
acpHash;
|
|
4268
|
+
acp;
|
|
4269
|
+
acpSelection = "unset";
|
|
3978
4270
|
pollCallback;
|
|
3979
4271
|
retry404TimeoutMs = DEFAULT_404_RETRY_TIMEOUT_MS3;
|
|
3980
4272
|
constructor(params) {
|
|
@@ -4019,137 +4311,137 @@ var DecryptForTxBuilder = class extends BaseBuilder {
|
|
|
4019
4311
|
this.retry404TimeoutMs = timeoutMs;
|
|
4020
4312
|
return this;
|
|
4021
4313
|
}
|
|
4022
|
-
|
|
4023
|
-
if (this.
|
|
4314
|
+
withACP(acpOrACPHash) {
|
|
4315
|
+
if (this.acpSelection === "with-acp") {
|
|
4024
4316
|
throw new CofheError({
|
|
4025
4317
|
code: "INTERNAL_ERROR" /* InternalError */,
|
|
4026
|
-
message: "decryptForTx:
|
|
4027
|
-
hint: "Choose the
|
|
4318
|
+
message: "decryptForTx: withACP() can only be selected once.",
|
|
4319
|
+
hint: "Choose the acp mode once. If you need a different acp, start a new decryptForTx() builder chain."
|
|
4028
4320
|
});
|
|
4029
4321
|
}
|
|
4030
|
-
if (this.
|
|
4322
|
+
if (this.acpSelection === "without-acp") {
|
|
4031
4323
|
throw new CofheError({
|
|
4032
4324
|
code: "INTERNAL_ERROR" /* InternalError */,
|
|
4033
|
-
message: "decryptForTx: cannot call
|
|
4034
|
-
hint: "Choose exactly one
|
|
4325
|
+
message: "decryptForTx: cannot call withACP() after withoutACP() has been selected.",
|
|
4326
|
+
hint: "Choose exactly one acp mode: either call .withACP(...) or .withoutACP(), but not both."
|
|
4035
4327
|
});
|
|
4036
4328
|
}
|
|
4037
|
-
this.
|
|
4038
|
-
if (typeof
|
|
4039
|
-
this.
|
|
4040
|
-
this.
|
|
4041
|
-
} else if (
|
|
4042
|
-
this.
|
|
4043
|
-
this.
|
|
4329
|
+
this.acpSelection = "with-acp";
|
|
4330
|
+
if (typeof acpOrACPHash === "string") {
|
|
4331
|
+
this.acpHash = acpOrACPHash;
|
|
4332
|
+
this.acp = void 0;
|
|
4333
|
+
} else if (acpOrACPHash === void 0) {
|
|
4334
|
+
this.acpHash = void 0;
|
|
4335
|
+
this.acp = void 0;
|
|
4044
4336
|
} else {
|
|
4045
|
-
this.
|
|
4046
|
-
this.
|
|
4337
|
+
this.acp = acpOrACPHash;
|
|
4338
|
+
this.acpHash = void 0;
|
|
4047
4339
|
}
|
|
4048
4340
|
return this;
|
|
4049
4341
|
}
|
|
4050
4342
|
/**
|
|
4051
|
-
* Select "no
|
|
4343
|
+
* Select "no acp" mode.
|
|
4052
4344
|
*
|
|
4053
|
-
* This uses global allowance (no
|
|
4345
|
+
* This uses global allowance (no acp required) and sends an empty permission payload to `/decrypt`.
|
|
4054
4346
|
*/
|
|
4055
|
-
|
|
4056
|
-
if (this.
|
|
4347
|
+
withoutACP() {
|
|
4348
|
+
if (this.acpSelection === "without-acp") {
|
|
4057
4349
|
throw new CofheError({
|
|
4058
4350
|
code: "INTERNAL_ERROR" /* InternalError */,
|
|
4059
|
-
message: "decryptForTx:
|
|
4060
|
-
hint: "Choose the
|
|
4351
|
+
message: "decryptForTx: withoutACP() can only be selected once.",
|
|
4352
|
+
hint: "Choose the acp mode once. If you need a different mode, start a new decryptForTx() builder chain."
|
|
4061
4353
|
});
|
|
4062
4354
|
}
|
|
4063
|
-
if (this.
|
|
4355
|
+
if (this.acpSelection === "with-acp") {
|
|
4064
4356
|
throw new CofheError({
|
|
4065
4357
|
code: "INTERNAL_ERROR" /* InternalError */,
|
|
4066
|
-
message: "decryptForTx: cannot call
|
|
4067
|
-
hint: "Choose exactly one
|
|
4358
|
+
message: "decryptForTx: cannot call withoutACP() after withACP() has been selected.",
|
|
4359
|
+
hint: "Choose exactly one acp mode: either call .withACP(...) or .withoutACP(), but not both."
|
|
4068
4360
|
});
|
|
4069
4361
|
}
|
|
4070
|
-
this.
|
|
4071
|
-
this.
|
|
4072
|
-
this.
|
|
4362
|
+
this.acpSelection = "without-acp";
|
|
4363
|
+
this.acpHash = void 0;
|
|
4364
|
+
this.acp = void 0;
|
|
4073
4365
|
return this;
|
|
4074
4366
|
}
|
|
4075
|
-
|
|
4076
|
-
return this.
|
|
4367
|
+
getACP() {
|
|
4368
|
+
return this.acp;
|
|
4077
4369
|
}
|
|
4078
|
-
|
|
4079
|
-
return this.
|
|
4370
|
+
getACPHash() {
|
|
4371
|
+
return this.acpHash;
|
|
4080
4372
|
}
|
|
4081
4373
|
async getThresholdNetworkUrl() {
|
|
4082
4374
|
this.assertChainId();
|
|
4083
4375
|
return getThresholdNetworkUrlOrThrow(this.config, this.chainId);
|
|
4084
4376
|
}
|
|
4085
|
-
async
|
|
4086
|
-
if (this.
|
|
4377
|
+
async getResolvedACP() {
|
|
4378
|
+
if (this.acpSelection === "unset") {
|
|
4087
4379
|
throw new CofheError({
|
|
4088
4380
|
code: "INTERNAL_ERROR" /* InternalError */,
|
|
4089
|
-
message: "decryptForTx: missing
|
|
4090
|
-
hint: "Call .
|
|
4381
|
+
message: "decryptForTx: missing acp selection; call withACP(...) or withoutACP() before execute().",
|
|
4382
|
+
hint: "Call .withACP() to use the active acp, or .withoutACP() for global allowance."
|
|
4091
4383
|
});
|
|
4092
4384
|
}
|
|
4093
|
-
if (this.
|
|
4385
|
+
if (this.acpSelection === "without-acp") {
|
|
4094
4386
|
return null;
|
|
4095
4387
|
}
|
|
4096
|
-
if (this.
|
|
4097
|
-
return this.
|
|
4388
|
+
if (this.acp)
|
|
4389
|
+
return this.acp;
|
|
4098
4390
|
this.assertChainId();
|
|
4099
4391
|
this.assertAccount();
|
|
4100
|
-
if (this.
|
|
4101
|
-
const
|
|
4102
|
-
if (!
|
|
4392
|
+
if (this.acpHash) {
|
|
4393
|
+
const acp2 = await acps.getACP(this.chainId, this.account, this.acpHash);
|
|
4394
|
+
if (!acp2) {
|
|
4103
4395
|
throw new CofheError({
|
|
4104
|
-
code: "
|
|
4105
|
-
message: `
|
|
4106
|
-
hint: "Ensure the
|
|
4396
|
+
code: "ACP_NOT_FOUND" /* ACPNotFound */,
|
|
4397
|
+
message: `ACP with hash <${this.acpHash}> not found for account <${this.account}> and chainId <${this.chainId}>`,
|
|
4398
|
+
hint: "Ensure the acp exists and is valid.",
|
|
4107
4399
|
context: {
|
|
4108
4400
|
chainId: this.chainId,
|
|
4109
4401
|
account: this.account,
|
|
4110
|
-
|
|
4402
|
+
acpHash: this.acpHash
|
|
4111
4403
|
}
|
|
4112
4404
|
});
|
|
4113
4405
|
}
|
|
4114
|
-
return
|
|
4406
|
+
return acp2;
|
|
4115
4407
|
}
|
|
4116
|
-
const
|
|
4117
|
-
if (!
|
|
4408
|
+
const acp = await acps.getActiveACP(this.chainId, this.account);
|
|
4409
|
+
if (!acp) {
|
|
4118
4410
|
throw new CofheError({
|
|
4119
|
-
code: "
|
|
4120
|
-
message: `Active
|
|
4121
|
-
hint: "Create
|
|
4411
|
+
code: "ACP_NOT_FOUND" /* ACPNotFound */,
|
|
4412
|
+
message: `Active acp not found for chainId <${this.chainId}> and account <${this.account}>`,
|
|
4413
|
+
hint: "Create an ACP (e.g. client.acp.createSelf(...)) and/or set it active (client.acp.selectActiveACP(hash)).",
|
|
4122
4414
|
context: {
|
|
4123
4415
|
chainId: this.chainId,
|
|
4124
4416
|
account: this.account
|
|
4125
4417
|
}
|
|
4126
4418
|
});
|
|
4127
4419
|
}
|
|
4128
|
-
return
|
|
4420
|
+
return acp;
|
|
4129
4421
|
}
|
|
4130
4422
|
/**
|
|
4131
4423
|
* On hardhat, interact with MockThresholdNetwork contract
|
|
4132
4424
|
*/
|
|
4133
|
-
async mocksDecryptForTx(
|
|
4425
|
+
async mocksDecryptForTx(acp) {
|
|
4134
4426
|
this.assertPublicClient();
|
|
4135
4427
|
const delay = this.config.mocks.decryptDelay;
|
|
4136
4428
|
if (delay > 0)
|
|
4137
4429
|
await sleep(delay);
|
|
4138
|
-
const result = await cofheMocksDecryptForTx(this.ctHash, 0,
|
|
4430
|
+
const result = await cofheMocksDecryptForTx(this.ctHash, 0, acp, this.publicClient);
|
|
4139
4431
|
return result;
|
|
4140
4432
|
}
|
|
4141
4433
|
/**
|
|
4142
4434
|
* In the production context, perform a true decryption with the CoFHE coprocessor.
|
|
4143
4435
|
*/
|
|
4144
|
-
async productionDecryptForTx(
|
|
4436
|
+
async productionDecryptForTx(acp) {
|
|
4145
4437
|
this.assertChainId();
|
|
4146
4438
|
this.assertPublicClient();
|
|
4147
4439
|
const thresholdNetworkUrl = await this.getThresholdNetworkUrl();
|
|
4148
|
-
const
|
|
4440
|
+
const wireAcp = acp ? ACPUtils.getPublic(acp, true) : null;
|
|
4149
4441
|
const { decryptedValue, signature } = await tnDecryptV2({
|
|
4150
4442
|
ctHash: this.ctHash,
|
|
4151
4443
|
chainId: this.chainId,
|
|
4152
|
-
|
|
4444
|
+
acp: wireAcp,
|
|
4153
4445
|
thresholdNetworkUrl,
|
|
4154
4446
|
retry404TimeoutMs: this.retry404TimeoutMs,
|
|
4155
4447
|
onPoll: this.pollCallback
|
|
@@ -4163,19 +4455,19 @@ var DecryptForTxBuilder = class extends BaseBuilder {
|
|
|
4163
4455
|
/**
|
|
4164
4456
|
* Final step of the decryptForTx process. MUST BE CALLED LAST IN THE CHAIN.
|
|
4165
4457
|
*
|
|
4166
|
-
* You must explicitly choose one
|
|
4167
|
-
* - `
|
|
4168
|
-
* - `
|
|
4458
|
+
* You must explicitly choose one acp mode before calling `execute()`:
|
|
4459
|
+
* - `withACP(acp)` / `withACP(acpHash)` / `withACP()` (active acp)
|
|
4460
|
+
* - `withoutACP()` (global allowance)
|
|
4169
4461
|
*/
|
|
4170
4462
|
async execute() {
|
|
4171
|
-
const
|
|
4172
|
-
if (
|
|
4173
|
-
|
|
4174
|
-
const chainId =
|
|
4463
|
+
const acp = await this.getResolvedACP();
|
|
4464
|
+
if (acp !== null) {
|
|
4465
|
+
ACPUtils.validate(acp);
|
|
4466
|
+
const chainId = acp._signedDomain.chainId;
|
|
4175
4467
|
if (chainId === hardhat2.id) {
|
|
4176
|
-
return await this.mocksDecryptForTx(
|
|
4468
|
+
return await this.mocksDecryptForTx(acp);
|
|
4177
4469
|
} else {
|
|
4178
|
-
return await this.productionDecryptForTx(
|
|
4470
|
+
return await this.productionDecryptForTx(acp);
|
|
4179
4471
|
}
|
|
4180
4472
|
} else {
|
|
4181
4473
|
if (!this.chainId) {
|
|
@@ -4238,6 +4530,21 @@ function createCofheClientBase(opts) {
|
|
|
4238
4530
|
const updateConnectState = (partial) => {
|
|
4239
4531
|
connectStore.setState((state) => ({ ...state, ...partial }));
|
|
4240
4532
|
};
|
|
4533
|
+
const _resolveSharingRegistry = async (publicClient, chainId) => {
|
|
4534
|
+
const configured = opts.config.acp?.sharingRegistry?.[chainId];
|
|
4535
|
+
if (configured != null)
|
|
4536
|
+
return configured;
|
|
4537
|
+
const registry = (await acps.getAclServedAddresses(publicClient, chainId)).shareRegistry;
|
|
4538
|
+
if (registry == null) {
|
|
4539
|
+
throw new CofheError({
|
|
4540
|
+
code: "MISSING_CONFIG" /* MissingConfig */,
|
|
4541
|
+
message: `No ACP share registry available for chainId <${chainId}>`,
|
|
4542
|
+
hint: "The ACL on this chain does not serve a share registry address. Set `acp.sharingRegistry` in the cofhe config to use on-chain sharing.",
|
|
4543
|
+
context: { chainId }
|
|
4544
|
+
});
|
|
4545
|
+
}
|
|
4546
|
+
return registry;
|
|
4547
|
+
};
|
|
4241
4548
|
const _requireConnected = () => {
|
|
4242
4549
|
const state = connectStore.getState();
|
|
4243
4550
|
const notConnected = !state.connected || !state.account || !state.chainId || !state.publicClient || !state.walletClient;
|
|
@@ -4360,74 +4667,147 @@ function createCofheClientBase(opts) {
|
|
|
4360
4667
|
}
|
|
4361
4668
|
return { chainId: _chainId, account: _account };
|
|
4362
4669
|
};
|
|
4363
|
-
const
|
|
4670
|
+
const clientACPs = {
|
|
4364
4671
|
// Pass through store access
|
|
4365
|
-
getSnapshot:
|
|
4366
|
-
subscribe:
|
|
4672
|
+
getSnapshot: acps.getSnapshot,
|
|
4673
|
+
subscribe: acps.subscribe,
|
|
4367
4674
|
// Creation methods (require connection)
|
|
4368
4675
|
createSelf: async (options, clients) => {
|
|
4369
4676
|
_requireConnected();
|
|
4370
4677
|
const { publicClient, walletClient } = clients ?? connectStore.getState();
|
|
4371
|
-
|
|
4678
|
+
const chainId = await publicClient.getChainId();
|
|
4679
|
+
return acps.createSelf(
|
|
4680
|
+
await acps.applyACPDefaultsFromChain(options, opts.config.acp, publicClient, chainId),
|
|
4681
|
+
publicClient,
|
|
4682
|
+
walletClient
|
|
4683
|
+
);
|
|
4372
4684
|
},
|
|
4373
4685
|
createSharing: async (options, clients) => {
|
|
4374
4686
|
_requireConnected();
|
|
4375
4687
|
const { publicClient, walletClient } = clients ?? connectStore.getState();
|
|
4376
|
-
|
|
4688
|
+
const chainId = await publicClient.getChainId();
|
|
4689
|
+
return acps.createSharing(
|
|
4690
|
+
await acps.applyACPDefaultsFromChain(options, opts.config.acp, publicClient, chainId),
|
|
4691
|
+
publicClient,
|
|
4692
|
+
walletClient
|
|
4693
|
+
);
|
|
4377
4694
|
},
|
|
4378
4695
|
importShared: async (options, clients) => {
|
|
4379
4696
|
_requireConnected();
|
|
4380
4697
|
const { publicClient, walletClient } = clients ?? connectStore.getState();
|
|
4381
|
-
return
|
|
4698
|
+
return acps.importShared(options, publicClient, walletClient);
|
|
4382
4699
|
},
|
|
4383
4700
|
// Get or create methods (require connection)
|
|
4384
|
-
|
|
4701
|
+
getOrCreateSelfACP: async (chainId, account, options) => {
|
|
4385
4702
|
_requireConnected();
|
|
4386
4703
|
const { chainId: _chainId, account: _account } = _getChainIdAndAccount(chainId, account);
|
|
4387
4704
|
const { publicClient, walletClient } = connectStore.getState();
|
|
4388
|
-
|
|
4705
|
+
const optionsWithDefaults = await acps.applyACPDefaultsFromChain(
|
|
4706
|
+
options ?? { issuer: _account, name: "Autogenerated Self ACP" },
|
|
4707
|
+
opts.config.acp,
|
|
4708
|
+
publicClient,
|
|
4709
|
+
_chainId
|
|
4710
|
+
);
|
|
4711
|
+
return acps.getOrCreateSelfACP(publicClient, walletClient, _chainId, _account, optionsWithDefaults);
|
|
4389
4712
|
},
|
|
4390
|
-
|
|
4713
|
+
getOrCreateSharingACP: async (options, chainId, account) => {
|
|
4391
4714
|
_requireConnected();
|
|
4392
4715
|
const { chainId: _chainId, account: _account } = _getChainIdAndAccount(chainId, account);
|
|
4393
4716
|
const { publicClient, walletClient } = connectStore.getState();
|
|
4394
|
-
return
|
|
4717
|
+
return acps.getOrCreateSharingACP(
|
|
4718
|
+
publicClient,
|
|
4719
|
+
walletClient,
|
|
4720
|
+
await acps.applyACPDefaultsFromChain(options, opts.config.acp, publicClient, _chainId),
|
|
4721
|
+
_chainId,
|
|
4722
|
+
_account
|
|
4723
|
+
);
|
|
4724
|
+
},
|
|
4725
|
+
// Revocation (require connection)
|
|
4726
|
+
revokeACP: async (acp) => {
|
|
4727
|
+
_requireConnected();
|
|
4728
|
+
const { walletClient } = connectStore.getState();
|
|
4729
|
+
return acps.revokeACP(acp, walletClient);
|
|
4730
|
+
},
|
|
4731
|
+
revokeAllACPs: async (revokerContract) => {
|
|
4732
|
+
_requireConnected();
|
|
4733
|
+
const { publicClient, walletClient } = connectStore.getState();
|
|
4734
|
+
return acps.revokeAllACPs(walletClient, publicClient, revokerContract);
|
|
4735
|
+
},
|
|
4736
|
+
isACPRevoked: async (acp) => {
|
|
4737
|
+
_requireConnected();
|
|
4738
|
+
const { publicClient } = connectStore.getState();
|
|
4739
|
+
return acps.isACPRevoked(acp, publicClient);
|
|
4740
|
+
},
|
|
4741
|
+
// On-chain sharing (require connection + config.acp.sharingRegistry)
|
|
4742
|
+
shareOnChain: async (acp) => {
|
|
4743
|
+
_requireConnected();
|
|
4744
|
+
const { publicClient, walletClient } = connectStore.getState();
|
|
4745
|
+
const chainId = await publicClient.getChainId();
|
|
4746
|
+
const sharingRegistry = await _resolveSharingRegistry(publicClient, chainId);
|
|
4747
|
+
return acps.shareOnChain(acp, walletClient, sharingRegistry);
|
|
4748
|
+
},
|
|
4749
|
+
getIncomingShares: async () => {
|
|
4750
|
+
_requireConnected();
|
|
4751
|
+
const { publicClient, walletClient } = connectStore.getState();
|
|
4752
|
+
const chainId = await publicClient.getChainId();
|
|
4753
|
+
const account = walletClient.account.address;
|
|
4754
|
+
const sharingRegistry = await _resolveSharingRegistry(publicClient, chainId);
|
|
4755
|
+
return acps.getIncomingShares(publicClient, sharingRegistry, account);
|
|
4756
|
+
},
|
|
4757
|
+
importFromChain: async (share) => {
|
|
4758
|
+
_requireConnected();
|
|
4759
|
+
const { publicClient, walletClient } = connectStore.getState();
|
|
4760
|
+
return acps.importFromChain(share, publicClient, walletClient);
|
|
4761
|
+
},
|
|
4762
|
+
dismissShare: async (shareId) => {
|
|
4763
|
+
_requireConnected();
|
|
4764
|
+
const { publicClient, walletClient } = connectStore.getState();
|
|
4765
|
+
const chainId = await publicClient.getChainId();
|
|
4766
|
+
const sharingRegistry = await _resolveSharingRegistry(publicClient, chainId);
|
|
4767
|
+
return acps.removeShareOnChain(shareId, walletClient, sharingRegistry);
|
|
4768
|
+
},
|
|
4769
|
+
cancelShare: async (shareId) => {
|
|
4770
|
+
_requireConnected();
|
|
4771
|
+
const { publicClient, walletClient } = connectStore.getState();
|
|
4772
|
+
const chainId = await publicClient.getChainId();
|
|
4773
|
+
const sharingRegistry = await _resolveSharingRegistry(publicClient, chainId);
|
|
4774
|
+
return acps.removeShareOnChain(shareId, walletClient, sharingRegistry);
|
|
4395
4775
|
},
|
|
4396
4776
|
// Retrieval methods (auto-fill chainId/account)
|
|
4397
|
-
|
|
4777
|
+
getACP: (hash, chainId, account) => {
|
|
4398
4778
|
const { chainId: _chainId, account: _account } = _getChainIdAndAccount(chainId, account);
|
|
4399
|
-
return
|
|
4779
|
+
return acps.getACP(_chainId, _account, hash);
|
|
4400
4780
|
},
|
|
4401
|
-
|
|
4781
|
+
getACPs: (chainId, account) => {
|
|
4402
4782
|
const { chainId: _chainId, account: _account } = _getChainIdAndAccount(chainId, account);
|
|
4403
|
-
return
|
|
4783
|
+
return acps.getACPs(_chainId, _account);
|
|
4404
4784
|
},
|
|
4405
|
-
|
|
4785
|
+
getActiveACP: (chainId, account) => {
|
|
4406
4786
|
const { chainId: _chainId, account: _account } = _getChainIdAndAccount(chainId, account);
|
|
4407
|
-
return
|
|
4787
|
+
return acps.getActiveACP(_chainId, _account);
|
|
4408
4788
|
},
|
|
4409
|
-
|
|
4789
|
+
getActiveACPHash: (chainId, account) => {
|
|
4410
4790
|
const { chainId: _chainId, account: _account } = _getChainIdAndAccount(chainId, account);
|
|
4411
|
-
return
|
|
4791
|
+
return acps.getActiveACPHash(_chainId, _account);
|
|
4412
4792
|
},
|
|
4413
4793
|
// Mutation methods (auto-fill chainId/account)
|
|
4414
|
-
|
|
4794
|
+
selectActiveACP: (hash, chainId, account) => {
|
|
4415
4795
|
const { chainId: _chainId, account: _account } = _getChainIdAndAccount(chainId, account);
|
|
4416
|
-
return
|
|
4796
|
+
return acps.selectActiveACP(_chainId, _account, hash);
|
|
4417
4797
|
},
|
|
4418
|
-
|
|
4798
|
+
removeACP: async (hash, chainId, account) => {
|
|
4419
4799
|
const { chainId: _chainId, account: _account } = _getChainIdAndAccount(chainId, account);
|
|
4420
|
-
return
|
|
4800
|
+
return acps.removeACP(_chainId, _account, hash);
|
|
4421
4801
|
},
|
|
4422
|
-
|
|
4802
|
+
removeActiveACP: async (chainId, account) => {
|
|
4423
4803
|
const { chainId: _chainId, account: _account } = _getChainIdAndAccount(chainId, account);
|
|
4424
|
-
return
|
|
4804
|
+
return acps.removeActiveACP(_chainId, _account);
|
|
4425
4805
|
},
|
|
4426
4806
|
// Utils (no context needed)
|
|
4427
|
-
getHash:
|
|
4428
|
-
export:
|
|
4429
|
-
serialize:
|
|
4430
|
-
deserialize:
|
|
4807
|
+
getHash: acps.getHash,
|
|
4808
|
+
export: acps.export,
|
|
4809
|
+
serialize: acps.serialize,
|
|
4810
|
+
deserialize: acps.deserialize
|
|
4431
4811
|
};
|
|
4432
4812
|
return {
|
|
4433
4813
|
// Zustand reactive accessors (don't export store directly to prevent mutation)
|
|
@@ -4455,7 +4835,7 @@ function createCofheClientBase(opts) {
|
|
|
4455
4835
|
decryptHandle: decryptForView,
|
|
4456
4836
|
decryptForTx,
|
|
4457
4837
|
verifyDecryptResult: verifyDecryptResult2,
|
|
4458
|
-
|
|
4838
|
+
acp: clientACPs
|
|
4459
4839
|
// Add SDK-specific methods below that require connection
|
|
4460
4840
|
// Example:
|
|
4461
4841
|
// async encryptData(data: unknown) {
|