@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/web.cjs
CHANGED
|
@@ -23,7 +23,8 @@ var CofheError = class _CofheError extends Error {
|
|
|
23
23
|
cause;
|
|
24
24
|
hint;
|
|
25
25
|
context;
|
|
26
|
-
|
|
26
|
+
apiErrorCode;
|
|
27
|
+
constructor({ code, message, cause, hint, context, apiErrorCode }) {
|
|
27
28
|
const fullMessage = cause ? `${message} | Caused by: ${cause.message}` : message;
|
|
28
29
|
super(fullMessage);
|
|
29
30
|
this.name = "CofheError";
|
|
@@ -31,6 +32,7 @@ var CofheError = class _CofheError extends Error {
|
|
|
31
32
|
this.cause = cause;
|
|
32
33
|
this.hint = hint;
|
|
33
34
|
this.context = context;
|
|
35
|
+
this.apiErrorCode = apiErrorCode;
|
|
34
36
|
if (Error.captureStackTrace) {
|
|
35
37
|
Error.captureStackTrace(this, _CofheError);
|
|
36
38
|
}
|
|
@@ -49,6 +51,7 @@ var CofheError = class _CofheError extends Error {
|
|
|
49
51
|
message: wrapperError?.message ?? "An internal error occurred",
|
|
50
52
|
hint: wrapperError?.hint,
|
|
51
53
|
context: wrapperError?.context,
|
|
54
|
+
apiErrorCode: wrapperError?.apiErrorCode,
|
|
52
55
|
cause
|
|
53
56
|
});
|
|
54
57
|
}
|
|
@@ -62,6 +65,7 @@ var CofheError = class _CofheError extends Error {
|
|
|
62
65
|
message: this.message,
|
|
63
66
|
hint: this.hint,
|
|
64
67
|
context: this.context,
|
|
68
|
+
apiErrorCode: this.apiErrorCode,
|
|
65
69
|
cause: this.cause ? {
|
|
66
70
|
name: this.cause.name,
|
|
67
71
|
message: this.cause.message,
|
|
@@ -74,7 +78,8 @@ var CofheError = class _CofheError extends Error {
|
|
|
74
78
|
* Returns a human-readable string representation of the error
|
|
75
79
|
*/
|
|
76
80
|
toString() {
|
|
77
|
-
const
|
|
81
|
+
const codeSuffix = this.apiErrorCode ? ` (api: ${this.apiErrorCode})` : "";
|
|
82
|
+
const parts = [`${this.name} [${this.code}]${codeSuffix}: ${this.message}`];
|
|
78
83
|
if (this.hint) {
|
|
79
84
|
parts.push(`Hint: ${this.hint}`);
|
|
80
85
|
}
|
|
@@ -346,18 +351,19 @@ var constructZkPoKMetadata = (accountAddr, securityZone, chainId) => {
|
|
|
346
351
|
metadata.set(chainIdBytes, 1 + accountBytes.length);
|
|
347
352
|
return metadata;
|
|
348
353
|
};
|
|
349
|
-
var
|
|
354
|
+
var zkVerifyBatch = async (verifierUrl, serializedBytes, address, securityZone, chainId, consumingContract) => {
|
|
350
355
|
const packed_list = toHexString(serializedBytes);
|
|
351
356
|
const sz_byte = new Uint8Array([securityZone]);
|
|
352
357
|
const payload = {
|
|
353
358
|
packed_list,
|
|
354
359
|
account_addr: address,
|
|
355
360
|
security_zone: sz_byte[0],
|
|
356
|
-
chain_id: chainId
|
|
361
|
+
chain_id: chainId,
|
|
362
|
+
contract_address: consumingContract
|
|
357
363
|
};
|
|
358
364
|
const body = JSON.stringify(payload);
|
|
359
365
|
try {
|
|
360
|
-
const response = await fetch(`${verifierUrl}/
|
|
366
|
+
const response = await fetch(`${verifierUrl}/verifyBatch`, {
|
|
361
367
|
method: "POST",
|
|
362
368
|
headers: {
|
|
363
369
|
"Content-Type": "application/json"
|
|
@@ -368,26 +374,25 @@ var zkVerify = async (verifierUrl, serializedBytes, address, securityZone, chain
|
|
|
368
374
|
const errorBody = await response.text();
|
|
369
375
|
throw new CofheError({
|
|
370
376
|
code: "ZK_VERIFY_FAILED" /* ZkVerifyFailed */,
|
|
371
|
-
message: `HTTP error! ZK proof verification failed - ${errorBody}`
|
|
377
|
+
message: `HTTP error! ZK batch proof verification failed - ${errorBody}`
|
|
372
378
|
});
|
|
373
379
|
}
|
|
374
380
|
const json = await response.json();
|
|
375
381
|
if (json.status !== "success") {
|
|
376
382
|
throw new CofheError({
|
|
377
383
|
code: "ZK_VERIFY_FAILED" /* ZkVerifyFailed */,
|
|
378
|
-
message: `ZK proof verification response malformed - ${json.error}`
|
|
384
|
+
message: `ZK batch proof verification response malformed - ${json.error}`
|
|
379
385
|
});
|
|
380
386
|
}
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
});
|
|
387
|
+
const { ciphertexts, signature, recid } = json.data;
|
|
388
|
+
return {
|
|
389
|
+
outputs: ciphertexts,
|
|
390
|
+
signature: concatSigRecid(signature, recid)
|
|
391
|
+
};
|
|
387
392
|
} catch (e) {
|
|
388
393
|
throw new CofheError({
|
|
389
394
|
code: "ZK_VERIFY_FAILED" /* ZkVerifyFailed */,
|
|
390
|
-
message: `ZK proof verification failed`,
|
|
395
|
+
message: `ZK batch proof verification failed`,
|
|
391
396
|
cause: e instanceof Error ? e : void 0
|
|
392
397
|
});
|
|
393
398
|
}
|
|
@@ -632,67 +637,59 @@ async function insertCtHashes(items, walletClient) {
|
|
|
632
637
|
});
|
|
633
638
|
}
|
|
634
639
|
}
|
|
635
|
-
async function
|
|
636
|
-
let signatures = [];
|
|
637
|
-
let encInputSignerClient;
|
|
640
|
+
async function createBatchProofSignature(items, securityZone, account, consumingContract) {
|
|
638
641
|
try {
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
642
|
+
const itemHashes = items.map(
|
|
643
|
+
(item) => viem.keccak256(
|
|
644
|
+
viem.encodePacked(
|
|
645
|
+
["uint256", "uint8", "uint8", "address", "uint256", "address"],
|
|
646
|
+
[
|
|
647
|
+
BigInt(item.ctHash),
|
|
648
|
+
item.utype,
|
|
649
|
+
securityZone,
|
|
650
|
+
account,
|
|
651
|
+
BigInt(chains.hardhat.id),
|
|
652
|
+
consumingContract
|
|
653
|
+
]
|
|
654
|
+
)
|
|
655
|
+
)
|
|
656
|
+
);
|
|
657
|
+
const batchDigest = viem.keccak256(
|
|
658
|
+
viem.encodePacked(
|
|
659
|
+
itemHashes.map(() => "bytes32"),
|
|
660
|
+
itemHashes
|
|
661
|
+
)
|
|
662
|
+
);
|
|
663
|
+
return await accounts.sign({
|
|
664
|
+
hash: batchDigest,
|
|
665
|
+
privateKey: MOCKS_ZK_VERIFIER_SIGNER_PRIVATE_KEY,
|
|
666
|
+
to: "hex"
|
|
648
667
|
});
|
|
649
|
-
}
|
|
650
|
-
try {
|
|
651
|
-
for (const item of items) {
|
|
652
|
-
const packedData = viem.encodePacked(
|
|
653
|
-
["uint256", "uint8", "uint8", "address", "uint256"],
|
|
654
|
-
[BigInt(item.ctHash), item.utype, securityZone, account, BigInt(chains.hardhat.id)]
|
|
655
|
-
);
|
|
656
|
-
const messageHash = viem.keccak256(packedData);
|
|
657
|
-
const signature = await accounts.sign({
|
|
658
|
-
hash: messageHash,
|
|
659
|
-
privateKey: MOCKS_ZK_VERIFIER_SIGNER_PRIVATE_KEY,
|
|
660
|
-
to: "hex"
|
|
661
|
-
});
|
|
662
|
-
signatures.push(signature);
|
|
663
|
-
}
|
|
664
668
|
} catch (err) {
|
|
665
669
|
throw new CofheError({
|
|
666
670
|
code: "ZK_MOCKS_CREATE_PROOF_SIGNATURE_FAILED" /* ZkMocksCreateProofSignatureFailed */,
|
|
667
|
-
message: `mockZkVerifySign
|
|
671
|
+
message: `mockZkVerifySign createBatchProofSignature failed while signing the batch digest`,
|
|
668
672
|
cause: err instanceof Error ? err : void 0,
|
|
669
673
|
context: {
|
|
670
674
|
items,
|
|
671
|
-
securityZone
|
|
672
|
-
|
|
673
|
-
});
|
|
674
|
-
}
|
|
675
|
-
if (signatures.length !== items.length) {
|
|
676
|
-
throw new CofheError({
|
|
677
|
-
code: "ZK_MOCKS_CREATE_PROOF_SIGNATURE_FAILED" /* ZkMocksCreateProofSignatureFailed */,
|
|
678
|
-
message: `mockZkVerifySign createProofSignatures returned incorrect number of signatures`,
|
|
679
|
-
context: {
|
|
680
|
-
items,
|
|
681
|
-
securityZone
|
|
675
|
+
securityZone,
|
|
676
|
+
consumingContract
|
|
682
677
|
}
|
|
683
678
|
});
|
|
684
679
|
}
|
|
685
|
-
return signatures;
|
|
686
680
|
}
|
|
687
|
-
async function cofheMocksZkVerifySign(items, account, securityZone, publicClient, walletClient, zkvWalletClient) {
|
|
681
|
+
async function cofheMocksZkVerifySign(items, account, securityZone, consumingContract, publicClient, walletClient, zkvWalletClient) {
|
|
688
682
|
const _walletClient = zkvWalletClient ?? createMockZkVerifierSigner();
|
|
689
683
|
const encryptableItems = await calcCtHashes(items, account, securityZone, publicClient);
|
|
690
684
|
await insertCtHashes(encryptableItems, _walletClient);
|
|
691
|
-
const
|
|
692
|
-
return
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
685
|
+
const signature = await createBatchProofSignature(encryptableItems, securityZone, account, consumingContract);
|
|
686
|
+
return {
|
|
687
|
+
outputs: encryptableItems.map((item) => ({
|
|
688
|
+
ct_hash: item.ctHash.toString(),
|
|
689
|
+
ct_type: item.utype
|
|
690
|
+
})),
|
|
691
|
+
signature
|
|
692
|
+
};
|
|
696
693
|
}
|
|
697
694
|
var EnvironmentSchema = zod.z.enum(["MOCK", "TESTNET", "MAINNET"]);
|
|
698
695
|
var CofheChainSchema = zod.z.object({
|
|
@@ -735,8 +732,8 @@ var CofheConfigSchema = zod.z.object({
|
|
|
735
732
|
environment: zod.z.enum(["node", "hardhat", "web", "react"]).optional().default("node"),
|
|
736
733
|
/** List of supported chain configurations */
|
|
737
734
|
supportedChains: zod.z.array(zod.z.custom()),
|
|
738
|
-
/** Default
|
|
739
|
-
|
|
735
|
+
/** Default acp expiration in seconds, default is 30 days */
|
|
736
|
+
defaultACPExpiration: zod.z.number().optional().default(60 * 60 * 24 * 30),
|
|
740
737
|
/** Storage method for fhe keys (defaults to indexedDB on web, filesystem on node) */
|
|
741
738
|
fheKeyStorage: zod.z.object({
|
|
742
739
|
getItem: zod.z.custom((val) => typeof val === "function", {
|
|
@@ -751,6 +748,12 @@ var CofheConfigSchema = zod.z.object({
|
|
|
751
748
|
}).or(zod.z.null()).default(null),
|
|
752
749
|
/** Whether to use Web Workers for ZK proof generation (web platform only) */
|
|
753
750
|
useWorkers: zod.z.boolean().optional().default(true),
|
|
751
|
+
/** ACP acp defaults */
|
|
752
|
+
acp: zod.z.object({
|
|
753
|
+
defaultRevoker: zod.z.custom().optional(),
|
|
754
|
+
defaultContractScopes: zod.z.custom().optional(),
|
|
755
|
+
sharingRegistry: zod.z.custom().optional()
|
|
756
|
+
}).optional().default({}),
|
|
754
757
|
/** Mocks configs */
|
|
755
758
|
mocks: zod.z.object({
|
|
756
759
|
decryptDelay: zod.z.number().optional().default(0),
|
|
@@ -761,8 +764,19 @@ var CofheConfigSchema = zod.z.object({
|
|
|
761
764
|
zkvWalletClient: zod.z.any().optional()
|
|
762
765
|
}).optional()
|
|
763
766
|
});
|
|
767
|
+
var RENAMED_COFHE_CONFIG_KEYS = {
|
|
768
|
+
defaultPermitExpiration: "defaultACPExpiration"
|
|
769
|
+
};
|
|
770
|
+
function assertNoRenamedConfigKeys(config, renamedKeys, label) {
|
|
771
|
+
const stale = Object.keys(config).filter((key) => key in renamedKeys);
|
|
772
|
+
if (stale.length === 0)
|
|
773
|
+
return;
|
|
774
|
+
const renames = stale.map((key) => `\`${key}\` is now \`${renamedKeys[key]}\``).join("; ");
|
|
775
|
+
throw new Error(`Invalid ${label}: ${renames}. See the v0.7.0 migration guide.`);
|
|
776
|
+
}
|
|
764
777
|
function createCofheConfigBase(config) {
|
|
765
|
-
|
|
778
|
+
assertNoRenamedConfigKeys(config, RENAMED_COFHE_CONFIG_KEYS, "cofhe configuration");
|
|
779
|
+
const result = CofheConfigSchema.strict().safeParse(config);
|
|
766
780
|
if (!result.success) {
|
|
767
781
|
throw new Error(`Invalid cofhe configuration: ${zod.z.prettifyError(result.error)}`, { cause: result.error });
|
|
768
782
|
}
|
|
@@ -1111,9 +1125,9 @@ var BaseBuilder = class {
|
|
|
1111
1125
|
// core/encrypt/encryptInputsBuilder.ts
|
|
1112
1126
|
var EncryptInputsBuilder = class extends BaseBuilder {
|
|
1113
1127
|
securityZone;
|
|
1128
|
+
consumingContract;
|
|
1114
1129
|
stepCallback;
|
|
1115
1130
|
inputItems;
|
|
1116
|
-
hpp = false;
|
|
1117
1131
|
zkvWalletClient;
|
|
1118
1132
|
tfhePublicKeyDeserializer;
|
|
1119
1133
|
compactPkeCrsDeserializer;
|
|
@@ -1180,20 +1194,6 @@ var EncryptInputsBuilder = class extends BaseBuilder {
|
|
|
1180
1194
|
this.keysStorage = params.keysStorage;
|
|
1181
1195
|
this.useWorker = params.config?.useWorkers ?? true;
|
|
1182
1196
|
}
|
|
1183
|
-
/**
|
|
1184
|
-
* @param account - Account that will create the tx using the encrypted inputs.
|
|
1185
|
-
*
|
|
1186
|
-
* If not provided, the account will be fetched from the connected walletClient.
|
|
1187
|
-
*
|
|
1188
|
-
* Example:
|
|
1189
|
-
* ```typescript
|
|
1190
|
-
* const encrypted = await encryptInputs([Encryptable.uint128(10n)])
|
|
1191
|
-
* .setAccount("0x123")
|
|
1192
|
-
* .execute();
|
|
1193
|
-
* ```
|
|
1194
|
-
*
|
|
1195
|
-
* @returns The chainable EncryptInputsBuilder instance.
|
|
1196
|
-
*/
|
|
1197
1197
|
setAccount(account) {
|
|
1198
1198
|
this.account = account;
|
|
1199
1199
|
return this;
|
|
@@ -1201,20 +1201,6 @@ var EncryptInputsBuilder = class extends BaseBuilder {
|
|
|
1201
1201
|
getAccount() {
|
|
1202
1202
|
return this.account;
|
|
1203
1203
|
}
|
|
1204
|
-
/**
|
|
1205
|
-
* @param chainId - Chain that will consume the encrypted inputs.
|
|
1206
|
-
*
|
|
1207
|
-
* If not provided, the chainId will be fetched from the connected publicClient.
|
|
1208
|
-
*
|
|
1209
|
-
* Example:
|
|
1210
|
-
* ```typescript
|
|
1211
|
-
* const encrypted = await encryptInputs([Encryptable.uint128(10n)])
|
|
1212
|
-
* .setChainId(11155111)
|
|
1213
|
-
* .execute();
|
|
1214
|
-
* ```
|
|
1215
|
-
*
|
|
1216
|
-
* @returns The chainable EncryptInputsBuilder instance.
|
|
1217
|
-
*/
|
|
1218
1204
|
setChainId(chainId) {
|
|
1219
1205
|
this.chainId = chainId;
|
|
1220
1206
|
return this;
|
|
@@ -1222,20 +1208,6 @@ var EncryptInputsBuilder = class extends BaseBuilder {
|
|
|
1222
1208
|
getChainId() {
|
|
1223
1209
|
return this.chainId;
|
|
1224
1210
|
}
|
|
1225
|
-
/**
|
|
1226
|
-
* @param securityZone - Security zone to encrypt the inputs for.
|
|
1227
|
-
*
|
|
1228
|
-
* If not provided, the default securityZone 0 will be used.
|
|
1229
|
-
*
|
|
1230
|
-
* Example:
|
|
1231
|
-
* ```typescript
|
|
1232
|
-
* const encrypted = await encryptInputs([Encryptable.uint128(10n)])
|
|
1233
|
-
* .setSecurityZone(1)
|
|
1234
|
-
* .execute();
|
|
1235
|
-
* ```
|
|
1236
|
-
*
|
|
1237
|
-
* @returns The chainable EncryptInputsBuilder instance.
|
|
1238
|
-
*/
|
|
1239
1211
|
setSecurityZone(securityZone) {
|
|
1240
1212
|
this.securityZone = securityZone;
|
|
1241
1213
|
return this;
|
|
@@ -1244,33 +1216,44 @@ var EncryptInputsBuilder = class extends BaseBuilder {
|
|
|
1244
1216
|
return this.securityZone;
|
|
1245
1217
|
}
|
|
1246
1218
|
/**
|
|
1219
|
+
* @param address - The contract that will consume the resulting hashes+signature (i.e. the
|
|
1220
|
+
* contract that will call `FHE.asEuint*`/`FHE.asEuint*s` with them).
|
|
1221
|
+
*
|
|
1222
|
+
* Required before `execute()` - the verifier binds this address into the signed digest, so a
|
|
1223
|
+
* batch signed for one contract cannot be replayed into another.
|
|
1224
|
+
*
|
|
1247
1225
|
* Example:
|
|
1248
1226
|
* ```typescript
|
|
1249
1227
|
* const encrypted = await encryptInputs([Encryptable.uint128(10n)])
|
|
1250
|
-
* .
|
|
1228
|
+
* .setConsumingContract("0x123...890")
|
|
1251
1229
|
* .execute();
|
|
1252
1230
|
* ```
|
|
1253
1231
|
*
|
|
1254
|
-
* @returns
|
|
1232
|
+
* @returns The chainable EncryptInputsBuilder instance.
|
|
1255
1233
|
*/
|
|
1256
|
-
|
|
1257
|
-
this.
|
|
1234
|
+
setConsumingContract(address) {
|
|
1235
|
+
this.consumingContract = address;
|
|
1258
1236
|
return this;
|
|
1259
1237
|
}
|
|
1238
|
+
getConsumingContract() {
|
|
1239
|
+
return this.consumingContract;
|
|
1240
|
+
}
|
|
1260
1241
|
/**
|
|
1261
|
-
*
|
|
1262
|
-
*
|
|
1263
|
-
* Overrides the config-level useWorkers setting for this specific encryption.
|
|
1264
|
-
*
|
|
1265
|
-
* Example:
|
|
1266
|
-
* ```typescript
|
|
1267
|
-
* const encrypted = await encryptInputs([Encryptable.uint128(10n)])
|
|
1268
|
-
* .setUseWorker(false)
|
|
1269
|
-
* .execute();
|
|
1270
|
-
* ```
|
|
1271
|
-
*
|
|
1272
|
-
* @returns The chainable EncryptInputsBuilder instance.
|
|
1242
|
+
* Asserts that this.consumingContract is populated
|
|
1243
|
+
* @throws {CofheError} If consumingContract is not set
|
|
1273
1244
|
*/
|
|
1245
|
+
assertConsumingContract() {
|
|
1246
|
+
if (this.consumingContract)
|
|
1247
|
+
return;
|
|
1248
|
+
throw new CofheError({
|
|
1249
|
+
code: "CONSUMING_CONTRACT_UNINITIALIZED" /* ConsumingContractUninitialized */,
|
|
1250
|
+
message: "Consuming contract is not set",
|
|
1251
|
+
hint: "Use setConsumingContract(...) to set the contract that will consume the encrypted inputs.",
|
|
1252
|
+
context: {
|
|
1253
|
+
consumingContract: this.consumingContract
|
|
1254
|
+
}
|
|
1255
|
+
});
|
|
1256
|
+
}
|
|
1274
1257
|
setUseWorker(useWorker) {
|
|
1275
1258
|
this.useWorker = useWorker;
|
|
1276
1259
|
return this;
|
|
@@ -1291,21 +1274,6 @@ var EncryptInputsBuilder = class extends BaseBuilder {
|
|
|
1291
1274
|
getUseWorker() {
|
|
1292
1275
|
return this.useWorker;
|
|
1293
1276
|
}
|
|
1294
|
-
/**
|
|
1295
|
-
* @param callback - Function to be called with the encryption step.
|
|
1296
|
-
*
|
|
1297
|
-
* Useful for debugging and tracking the progress of the encryption process.
|
|
1298
|
-
* Useful for a UI element that shows the progress of the encryption process.
|
|
1299
|
-
*
|
|
1300
|
-
* Example:
|
|
1301
|
-
* ```typescript
|
|
1302
|
-
* const encrypted = await encryptInputs([Encryptable.uint128(10n)])
|
|
1303
|
-
* .onStep((step: EncryptStep) => console.log(step))
|
|
1304
|
-
* .execute();
|
|
1305
|
-
* ```
|
|
1306
|
-
*
|
|
1307
|
-
* @returns The EncryptInputsBuilder instance.
|
|
1308
|
-
*/
|
|
1309
1277
|
onStep(callback) {
|
|
1310
1278
|
this.stepCallback = callback;
|
|
1311
1279
|
return this;
|
|
@@ -1444,6 +1412,7 @@ var EncryptInputsBuilder = class extends BaseBuilder {
|
|
|
1444
1412
|
this.assertAccount();
|
|
1445
1413
|
this.assertPublicClient();
|
|
1446
1414
|
this.assertWalletClient();
|
|
1415
|
+
this.assertConsumingContract();
|
|
1447
1416
|
const [initTfheDelay, fetchKeysDelay, packDelay, proveDelay, verifyDelay] = this.resolveEncryptDelays();
|
|
1448
1417
|
this.fireStepStart("initTfhe" /* InitTfhe */);
|
|
1449
1418
|
await sleep(initTfheDelay);
|
|
@@ -1469,22 +1438,17 @@ var EncryptInputsBuilder = class extends BaseBuilder {
|
|
|
1469
1438
|
this.fireStepEnd("prove" /* Prove */, { isMocks: true, mockSleep: proveDelay });
|
|
1470
1439
|
this.fireStepStart("verify" /* Verify */);
|
|
1471
1440
|
await sleep(verifyDelay);
|
|
1472
|
-
const
|
|
1441
|
+
const result = await cofheMocksZkVerifySign(
|
|
1473
1442
|
this.inputItems,
|
|
1474
1443
|
this.account,
|
|
1475
1444
|
this.securityZone,
|
|
1445
|
+
this.consumingContract,
|
|
1476
1446
|
this.publicClient,
|
|
1477
1447
|
this.walletClient,
|
|
1478
1448
|
this.zkvWalletClient
|
|
1479
1449
|
);
|
|
1480
|
-
const encryptedInputs = signedResults.map(({ ct_hash, signature }, index) => ({
|
|
1481
|
-
ctHash: BigInt(ct_hash),
|
|
1482
|
-
securityZone: this.securityZone,
|
|
1483
|
-
utype: this.inputItems[index].utype,
|
|
1484
|
-
signature
|
|
1485
|
-
}));
|
|
1486
1450
|
this.fireStepEnd("verify" /* Verify */, { isMocks: true, mockSleep: verifyDelay });
|
|
1487
|
-
return
|
|
1451
|
+
return result;
|
|
1488
1452
|
}
|
|
1489
1453
|
/**
|
|
1490
1454
|
* In the production context, perform a true encryption with the CoFHE coprocessor.
|
|
@@ -1492,6 +1456,7 @@ var EncryptInputsBuilder = class extends BaseBuilder {
|
|
|
1492
1456
|
async productionExecute() {
|
|
1493
1457
|
this.assertAccount();
|
|
1494
1458
|
this.assertChainId();
|
|
1459
|
+
this.assertConsumingContract();
|
|
1495
1460
|
this.fireStepStart("initTfhe" /* InitTfhe */);
|
|
1496
1461
|
const tfheInitializationExecuted = await this.initTfheOrThrow();
|
|
1497
1462
|
this.fireStepEnd("initTfhe" /* InitTfhe */, { tfheInitializationExecuted });
|
|
@@ -1526,26 +1491,24 @@ var EncryptInputsBuilder = class extends BaseBuilder {
|
|
|
1526
1491
|
});
|
|
1527
1492
|
this.fireStepStart("verify" /* Verify */);
|
|
1528
1493
|
const zkVerifierUrl = await this.getZkVerifierUrl();
|
|
1529
|
-
const
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
})
|
|
1494
|
+
const result = await zkVerifyBatch(
|
|
1495
|
+
zkVerifierUrl,
|
|
1496
|
+
proof,
|
|
1497
|
+
this.account,
|
|
1498
|
+
this.securityZone,
|
|
1499
|
+
this.chainId,
|
|
1500
|
+
this.consumingContract
|
|
1537
1501
|
);
|
|
1538
1502
|
this.fireStepEnd("verify" /* Verify */);
|
|
1539
|
-
return
|
|
1540
|
-
}
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
return [...hashes, proof];
|
|
1503
|
+
return result;
|
|
1504
|
+
}
|
|
1505
|
+
/**
|
|
1506
|
+
* Converts a batch-verified result (per-item ctHash/ctType + one shared signature) into the
|
|
1507
|
+
* tuple returned by `execute()`: per-item hashes in input order, followed by the single signature.
|
|
1508
|
+
*/
|
|
1509
|
+
buildBatchResult(result) {
|
|
1510
|
+
const hashes = result.outputs.map((output) => "0x" + BigInt(output.ct_hash).toString(16).padStart(64, "0"));
|
|
1511
|
+
return [...hashes, result.signature];
|
|
1549
1512
|
}
|
|
1550
1513
|
/**
|
|
1551
1514
|
* Final step of the encryption process. MUST BE CALLED LAST IN THE CHAIN.
|
|
@@ -1567,18 +1530,16 @@ var EncryptInputsBuilder = class extends BaseBuilder {
|
|
|
1567
1530
|
* @returns The encrypted inputs.
|
|
1568
1531
|
*/
|
|
1569
1532
|
async execute() {
|
|
1570
|
-
let
|
|
1533
|
+
let result;
|
|
1571
1534
|
if (this.chainId === chains.hardhat.id)
|
|
1572
|
-
|
|
1535
|
+
result = await this.mocksExecute();
|
|
1573
1536
|
else
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
return this.structsToHashPlusProof(items);
|
|
1577
|
-
return items;
|
|
1537
|
+
result = await this.productionExecute();
|
|
1538
|
+
return this.buildBatchResult(result);
|
|
1578
1539
|
}
|
|
1579
1540
|
};
|
|
1580
1541
|
|
|
1581
|
-
//
|
|
1542
|
+
// acps/utils.ts
|
|
1582
1543
|
var fromHexString = (hexString) => {
|
|
1583
1544
|
const cleanString = hexString.length % 2 === 1 ? `0${hexString}` : hexString;
|
|
1584
1545
|
const arr = cleanString.replace(/^0x/, "").match(/.{1,2}/g);
|
|
@@ -1598,120 +1559,33 @@ function toBigInt(value) {
|
|
|
1598
1559
|
return value;
|
|
1599
1560
|
}
|
|
1600
1561
|
}
|
|
1601
|
-
function toBeArray(value) {
|
|
1602
|
-
const bigIntValue = typeof value === "number" ? BigInt(value) : value;
|
|
1603
|
-
const hex = bigIntValue.toString(16);
|
|
1604
|
-
const paddedHex = hex.length % 2 === 0 ? hex : "0" + hex;
|
|
1605
|
-
return fromHexString(paddedHex);
|
|
1606
|
-
}
|
|
1607
|
-
function isString(value) {
|
|
1608
|
-
if (typeof value !== "string") {
|
|
1609
|
-
throw new Error(`Expected value which is \`string\`, received value of type \`${typeof value}\`.`);
|
|
1610
|
-
}
|
|
1611
|
-
}
|
|
1612
|
-
function isNumber(value) {
|
|
1613
|
-
const is = typeof value === "number" && !Number.isNaN(value);
|
|
1614
|
-
if (!is) {
|
|
1615
|
-
throw new Error(`Expected value which is \`number\`, received value of type \`${typeof value}\`.`);
|
|
1616
|
-
}
|
|
1617
|
-
}
|
|
1618
|
-
function isBigIntOrNumber(value) {
|
|
1619
|
-
const is = typeof value === "bigint";
|
|
1620
|
-
if (!is) {
|
|
1621
|
-
try {
|
|
1622
|
-
isNumber(value);
|
|
1623
|
-
} catch (e) {
|
|
1624
|
-
throw new Error(`Value ${value} is not a number or bigint: ${typeof value}`);
|
|
1625
|
-
}
|
|
1626
|
-
}
|
|
1627
|
-
}
|
|
1628
1562
|
|
|
1629
|
-
//
|
|
1630
|
-
var
|
|
1631
|
-
var PUBLIC_KEY_LENGTH = 64;
|
|
1632
|
-
var SealingKey = class _SealingKey {
|
|
1633
|
-
/**
|
|
1634
|
-
* The private key used for decryption.
|
|
1635
|
-
*/
|
|
1636
|
-
privateKey;
|
|
1637
|
-
/**
|
|
1638
|
-
* The public key used for encryption.
|
|
1639
|
-
*/
|
|
1640
|
-
publicKey;
|
|
1641
|
-
/**
|
|
1642
|
-
* Constructs a SealingKey instance with the given private and public keys.
|
|
1643
|
-
*
|
|
1644
|
-
* @param {string} privateKey - The private key used for decryption.
|
|
1645
|
-
* @param {string} publicKey - The public key used for encryption.
|
|
1646
|
-
* @throws Will throw an error if the provided keys lengths do not match
|
|
1647
|
-
* the required lengths for private and public keys.
|
|
1648
|
-
*/
|
|
1649
|
-
constructor(privateKey, publicKey) {
|
|
1650
|
-
if (privateKey.length !== PRIVATE_KEY_LENGTH) {
|
|
1651
|
-
throw new Error(`Private key must be of length ${PRIVATE_KEY_LENGTH}`);
|
|
1652
|
-
}
|
|
1653
|
-
if (publicKey.length !== PUBLIC_KEY_LENGTH) {
|
|
1654
|
-
throw new Error(`Public key must be of length ${PUBLIC_KEY_LENGTH}`);
|
|
1655
|
-
}
|
|
1656
|
-
this.privateKey = privateKey;
|
|
1657
|
-
this.publicKey = publicKey;
|
|
1658
|
-
}
|
|
1659
|
-
unseal = (parsedData) => {
|
|
1660
|
-
const nonce = parsedData.nonce instanceof Uint8Array ? parsedData.nonce : new Uint8Array(parsedData.nonce);
|
|
1661
|
-
const ephemPublicKey = parsedData.public_key instanceof Uint8Array ? parsedData.public_key : new Uint8Array(parsedData.public_key);
|
|
1662
|
-
const dataToDecrypt = parsedData.data instanceof Uint8Array ? parsedData.data : new Uint8Array(parsedData.data);
|
|
1663
|
-
const privateKeyBytes = fromHexString(this.privateKey);
|
|
1664
|
-
const decryptedMessage = nacl__default.default.box.open(dataToDecrypt, nonce, ephemPublicKey, privateKeyBytes);
|
|
1665
|
-
if (!decryptedMessage) {
|
|
1666
|
-
throw new Error("Failed to decrypt message");
|
|
1667
|
-
}
|
|
1668
|
-
return toBigInt(decryptedMessage);
|
|
1669
|
-
};
|
|
1670
|
-
/**
|
|
1671
|
-
* Serializes the SealingKey to a JSON object.
|
|
1672
|
-
*/
|
|
1673
|
-
serialize = () => {
|
|
1674
|
-
return {
|
|
1675
|
-
privateKey: this.privateKey,
|
|
1676
|
-
publicKey: this.publicKey
|
|
1677
|
-
};
|
|
1678
|
-
};
|
|
1679
|
-
/**
|
|
1680
|
-
* Deserializes the SealingKey from a JSON object.
|
|
1681
|
-
*/
|
|
1682
|
-
static deserialize = (privateKey, publicKey) => {
|
|
1683
|
-
return new _SealingKey(privateKey, publicKey);
|
|
1684
|
-
};
|
|
1685
|
-
/**
|
|
1686
|
-
* Seals (encrypts) the provided message for a receiver with the specified public key.
|
|
1687
|
-
*
|
|
1688
|
-
* @param {bigint | number} value - The message to be encrypted.
|
|
1689
|
-
* @param {string} publicKey - The public key of the intended recipient.
|
|
1690
|
-
* @returns string - The encrypted message in hexadecimal format.
|
|
1691
|
-
* @static
|
|
1692
|
-
* @throws Will throw if the provided publicKey or value do not meet defined preconditions.
|
|
1693
|
-
*/
|
|
1694
|
-
static seal = (value, publicKey) => {
|
|
1695
|
-
isString(publicKey);
|
|
1696
|
-
isBigIntOrNumber(value);
|
|
1697
|
-
const ephemeralKeyPair = nacl__default.default.box.keyPair();
|
|
1698
|
-
const nonce = nacl__default.default.randomBytes(nacl__default.default.box.nonceLength);
|
|
1699
|
-
const encryptedMessage = nacl__default.default.box(toBeArray(value), nonce, fromHexString(publicKey), ephemeralKeyPair.secretKey);
|
|
1700
|
-
return {
|
|
1701
|
-
data: encryptedMessage,
|
|
1702
|
-
public_key: ephemeralKeyPair.publicKey,
|
|
1703
|
-
nonce
|
|
1704
|
-
};
|
|
1705
|
-
};
|
|
1706
|
-
};
|
|
1563
|
+
// acps/sealing.ts
|
|
1564
|
+
var KEY_HEX_LENGTH = 64;
|
|
1707
1565
|
var GenerateSealingKey = () => {
|
|
1708
1566
|
const sodiumKeypair = nacl__default.default.box.keyPair();
|
|
1709
|
-
return
|
|
1567
|
+
return {
|
|
1568
|
+
privateKey: `0x${toHexString2(sodiumKeypair.secretKey)}`,
|
|
1569
|
+
publicKey: `0x${toHexString2(sodiumKeypair.publicKey)}`
|
|
1570
|
+
};
|
|
1571
|
+
};
|
|
1572
|
+
var unsealWithPrivateKey = (privateKey, parsedData) => {
|
|
1573
|
+
assertKeyLength(privateKey, "Private");
|
|
1574
|
+
const nonce = parsedData.nonce instanceof Uint8Array ? parsedData.nonce : new Uint8Array(parsedData.nonce);
|
|
1575
|
+
const ephemPublicKey = parsedData.public_key instanceof Uint8Array ? parsedData.public_key : new Uint8Array(parsedData.public_key);
|
|
1576
|
+
const dataToDecrypt = parsedData.data instanceof Uint8Array ? parsedData.data : new Uint8Array(parsedData.data);
|
|
1577
|
+
const decryptedMessage = nacl__default.default.box.open(dataToDecrypt, nonce, ephemPublicKey, fromHexString(privateKey));
|
|
1578
|
+
if (!decryptedMessage) {
|
|
1579
|
+
throw new Error("Failed to decrypt message");
|
|
1580
|
+
}
|
|
1581
|
+
return toBigInt(decryptedMessage);
|
|
1582
|
+
};
|
|
1583
|
+
var assertKeyLength = (key, kind) => {
|
|
1584
|
+
const bare = key.startsWith("0x") ? key.slice(2) : key;
|
|
1585
|
+
if (bare.length !== KEY_HEX_LENGTH) {
|
|
1586
|
+
throw new Error(`${kind} key must be of length ${KEY_HEX_LENGTH}`);
|
|
1587
|
+
}
|
|
1710
1588
|
};
|
|
1711
|
-
var SerializedSealingPair = zod.z.object({
|
|
1712
|
-
privateKey: zod.z.string(),
|
|
1713
|
-
publicKey: zod.z.string()
|
|
1714
|
-
});
|
|
1715
1589
|
var addressSchema = zod.z.string().refine((val) => viem.isAddress(val), {
|
|
1716
1590
|
error: "Invalid address"
|
|
1717
1591
|
}).transform((val) => viem.getAddress(val));
|
|
@@ -1730,46 +1604,70 @@ var bytesNotEmptySchema = bytesSchema.refine((val) => val !== "0x", {
|
|
|
1730
1604
|
error: "Must not be empty"
|
|
1731
1605
|
});
|
|
1732
1606
|
var DEFAULT_EXPIRATION_FN = () => Math.round(Date.now() / 1e3) + 7 * 24 * 60 * 60;
|
|
1733
|
-
var
|
|
1734
|
-
|
|
1607
|
+
var handlesSchema = zod.z.array(zod.z.string().regex(/^0x[0-9a-fA-F]{64}$/, "Invalid handle: expected 32-byte hex")).optional().default([]);
|
|
1608
|
+
var contractsSchema = zod.z.array(addressSchema).optional().default([]);
|
|
1609
|
+
var SCOPE_GLOBAL = 0;
|
|
1610
|
+
var SCOPE_CONTRACT = 1;
|
|
1611
|
+
var SCOPE_HANDLES = 2;
|
|
1612
|
+
var ScopeConsistencyRefinement = [
|
|
1613
|
+
(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,
|
|
1614
|
+
{
|
|
1615
|
+
error: "ACP scope :: arrays must match the scope mode (Global: both empty; Contract: contracts only; Handles: handles only)",
|
|
1616
|
+
path: ["scope"]
|
|
1617
|
+
}
|
|
1618
|
+
];
|
|
1619
|
+
var withDerivedScope = (data) => ({
|
|
1620
|
+
...data,
|
|
1621
|
+
scope: data.scope ?? (data.contracts.length > 0 ? SCOPE_CONTRACT : data.handles.length > 0 ? SCOPE_HANDLES : SCOPE_GLOBAL)
|
|
1622
|
+
});
|
|
1623
|
+
var zACPWithDefaults = zod.z.object({
|
|
1624
|
+
name: zod.z.string().optional().default("Unnamed ACP"),
|
|
1735
1625
|
type: zod.z.enum(["self", "sharing", "recipient"]),
|
|
1736
1626
|
issuer: addressNotZeroSchema,
|
|
1737
1627
|
expiration: zod.z.int().optional().default(DEFAULT_EXPIRATION_FN),
|
|
1738
1628
|
recipient: addressSchema.optional().default(viem.zeroAddress),
|
|
1739
|
-
|
|
1740
|
-
|
|
1629
|
+
revokerData: zod.z.int().optional().default(0),
|
|
1630
|
+
revokerContract: addressSchema.optional().default(viem.zeroAddress),
|
|
1631
|
+
scope: zod.z.int().min(0).max(2).optional().default(0),
|
|
1632
|
+
contracts: contractsSchema,
|
|
1633
|
+
handles: handlesSchema,
|
|
1741
1634
|
issuerSignature: bytesSchema.optional().default("0x"),
|
|
1742
1635
|
recipientSignature: bytesSchema.optional().default("0x")
|
|
1743
1636
|
});
|
|
1744
|
-
var
|
|
1745
|
-
|
|
1637
|
+
var zACPWithSealingKeys = zACPWithDefaults.extend({
|
|
1638
|
+
/** X25519 private key, 0x-prefixed 32-byte hex; never leaves the client */
|
|
1639
|
+
sealingPrivateKey: zod.z.string().regex(/^0x[0-9a-fA-F]{64}$/, "Invalid sealing private key").optional(),
|
|
1640
|
+
sealingKey: zod.z.string().regex(/^0x[0-9a-fA-F]{64}$/, "Invalid sealing key").optional()
|
|
1746
1641
|
});
|
|
1747
1642
|
var ExternalValidatorRefinement = [
|
|
1748
|
-
(data) => data.
|
|
1643
|
+
(data) => data.revokerData !== 0 && data.revokerContract !== viem.zeroAddress || data.revokerData === 0 && data.revokerContract === viem.zeroAddress,
|
|
1749
1644
|
{
|
|
1750
|
-
error: "
|
|
1751
|
-
path: ["
|
|
1645
|
+
error: "ACP external revoker :: revokerData and revokerContract must either both be set or both be unset.",
|
|
1646
|
+
path: ["revokerData", "revokerContract"]
|
|
1752
1647
|
}
|
|
1753
1648
|
];
|
|
1754
1649
|
var RecipientRefinement = [
|
|
1755
1650
|
(data) => data.issuer !== data.recipient,
|
|
1756
1651
|
{
|
|
1757
|
-
error: "Sharing
|
|
1652
|
+
error: "Sharing acp :: issuer and recipient must not be the same",
|
|
1758
1653
|
path: ["issuer", "recipient"]
|
|
1759
1654
|
}
|
|
1760
1655
|
];
|
|
1761
|
-
var
|
|
1656
|
+
var SelfACPOptionsValidator = zod.z.object({
|
|
1762
1657
|
type: zod.z.literal("self").optional().default("self"),
|
|
1763
1658
|
issuer: addressNotZeroSchema,
|
|
1764
|
-
name: zod.z.string().optional().default("Unnamed
|
|
1659
|
+
name: zod.z.string().optional().default("Unnamed ACP"),
|
|
1765
1660
|
expiration: zod.z.int().optional().default(DEFAULT_EXPIRATION_FN),
|
|
1766
1661
|
recipient: addressSchema.optional().default(viem.zeroAddress),
|
|
1767
|
-
|
|
1768
|
-
|
|
1662
|
+
revokerData: zod.z.int().optional().default(0),
|
|
1663
|
+
revokerContract: addressSchema.optional().default(viem.zeroAddress),
|
|
1664
|
+
scope: zod.z.int().min(0).max(2).optional(),
|
|
1665
|
+
contracts: contractsSchema,
|
|
1666
|
+
handles: handlesSchema,
|
|
1769
1667
|
issuerSignature: bytesSchema.optional().default("0x"),
|
|
1770
1668
|
recipientSignature: bytesSchema.optional().default("0x")
|
|
1771
|
-
}).refine(...ExternalValidatorRefinement);
|
|
1772
|
-
var
|
|
1669
|
+
}).refine(...ExternalValidatorRefinement).transform(withDerivedScope).refine(...ScopeConsistencyRefinement);
|
|
1670
|
+
var SelfACPValidator = zACPWithSealingKeys.refine((data) => data.type === "self", {
|
|
1773
1671
|
error: "Type must be 'self'"
|
|
1774
1672
|
}).refine((data) => data.recipient === viem.zeroAddress, {
|
|
1775
1673
|
error: "Recipient must be zeroAddress"
|
|
@@ -1778,18 +1676,21 @@ var SelfPermitValidator = zPermitWithSealingPair.refine((data) => data.type ===
|
|
|
1778
1676
|
}).refine((data) => data.recipientSignature === "0x", {
|
|
1779
1677
|
error: "RecipientSignature must be empty"
|
|
1780
1678
|
}).refine(...ExternalValidatorRefinement);
|
|
1781
|
-
var
|
|
1679
|
+
var SharingACPOptionsValidator = zod.z.object({
|
|
1782
1680
|
type: zod.z.literal("sharing").optional().default("sharing"),
|
|
1783
1681
|
issuer: addressNotZeroSchema,
|
|
1784
1682
|
recipient: addressNotZeroSchema,
|
|
1785
|
-
name: zod.z.string().optional().default("Unnamed
|
|
1683
|
+
name: zod.z.string().optional().default("Unnamed ACP"),
|
|
1786
1684
|
expiration: zod.z.int().optional().default(DEFAULT_EXPIRATION_FN),
|
|
1787
|
-
|
|
1788
|
-
|
|
1685
|
+
revokerData: zod.z.int().optional().default(0),
|
|
1686
|
+
revokerContract: addressSchema.optional().default(viem.zeroAddress),
|
|
1687
|
+
scope: zod.z.int().min(0).max(2).optional(),
|
|
1688
|
+
contracts: contractsSchema,
|
|
1689
|
+
handles: handlesSchema,
|
|
1789
1690
|
issuerSignature: bytesSchema.optional().default("0x"),
|
|
1790
1691
|
recipientSignature: bytesSchema.optional().default("0x")
|
|
1791
|
-
}).refine(...RecipientRefinement).refine(...ExternalValidatorRefinement);
|
|
1792
|
-
var
|
|
1692
|
+
}).refine(...RecipientRefinement).refine(...ExternalValidatorRefinement).transform(withDerivedScope).refine(...ScopeConsistencyRefinement);
|
|
1693
|
+
var SharingACPValidator = zACPWithSealingKeys.refine((data) => data.type === "sharing", {
|
|
1793
1694
|
error: "Type must be 'sharing'"
|
|
1794
1695
|
}).refine((data) => data.recipient !== viem.zeroAddress, {
|
|
1795
1696
|
error: "Recipient must not be zeroAddress"
|
|
@@ -1798,18 +1699,21 @@ var SharingPermitValidator = zPermitWithSealingPair.refine((data) => data.type =
|
|
|
1798
1699
|
}).refine((data) => data.recipientSignature === "0x", {
|
|
1799
1700
|
error: "RecipientSignature must be empty"
|
|
1800
1701
|
}).refine(...ExternalValidatorRefinement);
|
|
1801
|
-
var
|
|
1702
|
+
var ImportACPOptionsValidator = zod.z.object({
|
|
1802
1703
|
type: zod.z.literal("recipient").optional().default("recipient"),
|
|
1803
1704
|
issuer: addressNotZeroSchema,
|
|
1804
1705
|
recipient: addressNotZeroSchema,
|
|
1805
|
-
name: zod.z.string().optional().default("Unnamed
|
|
1706
|
+
name: zod.z.string().optional().default("Unnamed ACP"),
|
|
1806
1707
|
expiration: zod.z.int(),
|
|
1807
|
-
|
|
1808
|
-
|
|
1708
|
+
revokerData: zod.z.int().optional().default(0),
|
|
1709
|
+
revokerContract: addressSchema.optional().default(viem.zeroAddress),
|
|
1710
|
+
scope: zod.z.int().min(0).max(2).optional(),
|
|
1711
|
+
contracts: contractsSchema,
|
|
1712
|
+
handles: handlesSchema,
|
|
1809
1713
|
issuerSignature: bytesNotEmptySchema,
|
|
1810
1714
|
recipientSignature: bytesSchema.optional().default("0x")
|
|
1811
|
-
}).refine(...ExternalValidatorRefinement);
|
|
1812
|
-
var
|
|
1715
|
+
}).refine(...ExternalValidatorRefinement).transform(withDerivedScope).refine(...ScopeConsistencyRefinement);
|
|
1716
|
+
var ImportACPValidator = zACPWithSealingKeys.refine((data) => data.type === "recipient", {
|
|
1813
1717
|
error: "Type must be 'recipient'"
|
|
1814
1718
|
}).refine((data) => data.recipient !== viem.zeroAddress, {
|
|
1815
1719
|
error: "Recipient must not be zeroAddress"
|
|
@@ -1825,116 +1729,125 @@ var safeParseAndThrowFormatted = (schema, data, message) => {
|
|
|
1825
1729
|
}
|
|
1826
1730
|
return result.data;
|
|
1827
1731
|
};
|
|
1828
|
-
var
|
|
1829
|
-
return safeParseAndThrowFormatted(
|
|
1732
|
+
var validateSelfACPOptions = (options) => {
|
|
1733
|
+
return safeParseAndThrowFormatted(SelfACPOptionsValidator, options, "Invalid self acp options");
|
|
1830
1734
|
};
|
|
1831
|
-
var
|
|
1832
|
-
return safeParseAndThrowFormatted(
|
|
1735
|
+
var validateSharingACPOptions = (options) => {
|
|
1736
|
+
return safeParseAndThrowFormatted(SharingACPOptionsValidator, options, "Invalid sharing acp options");
|
|
1833
1737
|
};
|
|
1834
|
-
var
|
|
1835
|
-
return safeParseAndThrowFormatted(
|
|
1738
|
+
var validateImportACPOptions = (options) => {
|
|
1739
|
+
return safeParseAndThrowFormatted(ImportACPOptionsValidator, options, "Invalid import acp options");
|
|
1836
1740
|
};
|
|
1837
|
-
var
|
|
1838
|
-
return safeParseAndThrowFormatted(
|
|
1741
|
+
var validateSelfACP = (acp) => {
|
|
1742
|
+
return safeParseAndThrowFormatted(SelfACPValidator, acp, "Invalid self acp");
|
|
1839
1743
|
};
|
|
1840
|
-
var
|
|
1841
|
-
return safeParseAndThrowFormatted(
|
|
1744
|
+
var validateSharingACP = (acp) => {
|
|
1745
|
+
return safeParseAndThrowFormatted(SharingACPValidator, acp, "Invalid sharing acp");
|
|
1842
1746
|
};
|
|
1843
|
-
var
|
|
1844
|
-
return safeParseAndThrowFormatted(
|
|
1747
|
+
var validateImportACP = (acp) => {
|
|
1748
|
+
return safeParseAndThrowFormatted(ImportACPValidator, acp, "Invalid import acp");
|
|
1845
1749
|
};
|
|
1846
1750
|
var ValidationUtils = {
|
|
1847
1751
|
/**
|
|
1848
|
-
* Check if
|
|
1752
|
+
* Check if acp is expired
|
|
1849
1753
|
*/
|
|
1850
|
-
isExpired: (
|
|
1851
|
-
return
|
|
1754
|
+
isExpired: (acp) => {
|
|
1755
|
+
return acp.expiration < Math.floor(Date.now() / 1e3);
|
|
1852
1756
|
},
|
|
1853
1757
|
/**
|
|
1854
|
-
* Check if
|
|
1758
|
+
* Check if acp is signed by the active party
|
|
1855
1759
|
*/
|
|
1856
|
-
isSigned: (
|
|
1857
|
-
if (
|
|
1858
|
-
return
|
|
1760
|
+
isSigned: (acp) => {
|
|
1761
|
+
if (acp.type === "self" || acp.type === "sharing") {
|
|
1762
|
+
return acp.issuerSignature !== "0x";
|
|
1859
1763
|
}
|
|
1860
|
-
if (
|
|
1861
|
-
return
|
|
1764
|
+
if (acp.type === "recipient") {
|
|
1765
|
+
return acp.recipientSignature !== "0x";
|
|
1862
1766
|
}
|
|
1863
1767
|
return false;
|
|
1864
1768
|
},
|
|
1865
1769
|
/**
|
|
1866
|
-
* Checks that
|
|
1770
|
+
* Checks that an ACP is signed and not expired.
|
|
1867
1771
|
*/
|
|
1868
|
-
isSignedAndNotExpired: (
|
|
1869
|
-
if (ValidationUtils.isExpired(
|
|
1772
|
+
isSignedAndNotExpired: (acp) => {
|
|
1773
|
+
if (ValidationUtils.isExpired(acp)) {
|
|
1870
1774
|
return { valid: false, error: "expired" };
|
|
1871
1775
|
}
|
|
1872
|
-
if (!ValidationUtils.isSigned(
|
|
1776
|
+
if (!ValidationUtils.isSigned(acp)) {
|
|
1873
1777
|
return { valid: false, error: "not-signed" };
|
|
1874
1778
|
}
|
|
1875
1779
|
return { valid: true, error: null };
|
|
1876
1780
|
},
|
|
1877
1781
|
/**
|
|
1878
|
-
* Asserts that
|
|
1782
|
+
* Asserts that an ACP is signed and not expired.
|
|
1879
1783
|
*
|
|
1880
1784
|
* Throws `Error` with message:
|
|
1881
|
-
* - `
|
|
1882
|
-
* - `
|
|
1785
|
+
* - `ACP is expired`
|
|
1786
|
+
* - `ACP is not signed`
|
|
1883
1787
|
*/
|
|
1884
|
-
assertSignedAndNotExpired: (
|
|
1885
|
-
const result = ValidationUtils.isSignedAndNotExpired(
|
|
1788
|
+
assertSignedAndNotExpired: (acp) => {
|
|
1789
|
+
const result = ValidationUtils.isSignedAndNotExpired(acp);
|
|
1886
1790
|
if (result.valid)
|
|
1887
1791
|
return;
|
|
1888
1792
|
if (result.error === "expired") {
|
|
1889
|
-
throw new Error("
|
|
1793
|
+
throw new Error("ACP is expired");
|
|
1890
1794
|
}
|
|
1891
1795
|
if (result.error === "not-signed") {
|
|
1892
|
-
throw new Error("
|
|
1796
|
+
throw new Error("ACP is not signed");
|
|
1893
1797
|
}
|
|
1894
|
-
throw new Error("
|
|
1798
|
+
throw new Error("ACP is invalid");
|
|
1895
1799
|
},
|
|
1896
|
-
isValid: (
|
|
1897
|
-
const schema =
|
|
1800
|
+
isValid: (acp) => {
|
|
1801
|
+
const schema = acp.type === "self" ? SelfACPValidator : acp.type === "sharing" ? SharingACPValidator : acp.type === "recipient" ? ImportACPValidator : null;
|
|
1898
1802
|
if (schema == null)
|
|
1899
1803
|
return { valid: false, error: "invalid-schema" };
|
|
1900
|
-
const schemaResult = schema.safeParse(
|
|
1804
|
+
const schemaResult = schema.safeParse(acp);
|
|
1901
1805
|
if (!schemaResult.success)
|
|
1902
1806
|
return { valid: false, error: "invalid-schema" };
|
|
1903
|
-
return ValidationUtils.isSignedAndNotExpired(
|
|
1807
|
+
return ValidationUtils.isSignedAndNotExpired(acp);
|
|
1904
1808
|
}
|
|
1905
1809
|
};
|
|
1906
1810
|
|
|
1907
|
-
//
|
|
1908
|
-
var
|
|
1811
|
+
// acps/signature.ts
|
|
1812
|
+
var ACPSignatureAllFields = [
|
|
1909
1813
|
{ name: "issuer", type: "address" },
|
|
1910
1814
|
{ name: "expiration", type: "uint64" },
|
|
1911
1815
|
{ name: "recipient", type: "address" },
|
|
1912
|
-
{ name: "
|
|
1913
|
-
{ name: "
|
|
1816
|
+
{ name: "revokerData", type: "uint256" },
|
|
1817
|
+
{ name: "revokerContract", type: "address" },
|
|
1818
|
+
{ name: "scope", type: "uint8" },
|
|
1819
|
+
{ name: "contracts", type: "address[]" },
|
|
1820
|
+
{ name: "handles", type: "bytes32[]" },
|
|
1914
1821
|
{ name: "sealingKey", type: "bytes32" },
|
|
1915
1822
|
{ name: "issuerSignature", type: "bytes" }
|
|
1916
1823
|
];
|
|
1917
1824
|
var SignatureTypes = {
|
|
1918
|
-
|
|
1825
|
+
ACPIssuerSelf: [
|
|
1919
1826
|
"issuer",
|
|
1920
1827
|
"expiration",
|
|
1921
1828
|
"recipient",
|
|
1922
|
-
"
|
|
1923
|
-
"
|
|
1829
|
+
"revokerData",
|
|
1830
|
+
"revokerContract",
|
|
1831
|
+
"scope",
|
|
1832
|
+
"contracts",
|
|
1833
|
+
"handles",
|
|
1924
1834
|
"sealingKey"
|
|
1925
1835
|
],
|
|
1926
|
-
|
|
1836
|
+
ACPIssuerShared: [
|
|
1927
1837
|
"issuer",
|
|
1928
1838
|
"expiration",
|
|
1929
1839
|
"recipient",
|
|
1930
|
-
"
|
|
1931
|
-
"
|
|
1840
|
+
"revokerData",
|
|
1841
|
+
"revokerContract",
|
|
1842
|
+
"scope",
|
|
1843
|
+
"contracts",
|
|
1844
|
+
"handles"
|
|
1932
1845
|
],
|
|
1933
|
-
|
|
1846
|
+
ACPRecipient: ["sealingKey", "issuerSignature"]
|
|
1934
1847
|
};
|
|
1935
1848
|
var getSignatureTypesAndMessage = (primaryType, fields, values) => {
|
|
1936
1849
|
const types = {
|
|
1937
|
-
[primaryType]:
|
|
1850
|
+
[primaryType]: ACPSignatureAllFields.filter((fieldType) => fields.includes(fieldType.name))
|
|
1938
1851
|
};
|
|
1939
1852
|
const message = {};
|
|
1940
1853
|
fields.forEach((field) => {
|
|
@@ -1946,22 +1859,22 @@ var getSignatureTypesAndMessage = (primaryType, fields, values) => {
|
|
|
1946
1859
|
};
|
|
1947
1860
|
var SignatureUtils = {
|
|
1948
1861
|
/**
|
|
1949
|
-
* Get signature parameters for
|
|
1862
|
+
* Get signature parameters for an ACP
|
|
1950
1863
|
*/
|
|
1951
|
-
getSignatureParams: (
|
|
1952
|
-
return getSignatureTypesAndMessage(primaryType, SignatureTypes[primaryType],
|
|
1864
|
+
getSignatureParams: (acp, primaryType) => {
|
|
1865
|
+
return getSignatureTypesAndMessage(primaryType, SignatureTypes[primaryType], acp);
|
|
1953
1866
|
},
|
|
1954
1867
|
/**
|
|
1955
|
-
* Determine the required signature type based on
|
|
1868
|
+
* Determine the required signature type based on acp type
|
|
1956
1869
|
*/
|
|
1957
|
-
getPrimaryType: (
|
|
1958
|
-
if (
|
|
1959
|
-
return "
|
|
1960
|
-
if (
|
|
1961
|
-
return "
|
|
1962
|
-
if (
|
|
1963
|
-
return "
|
|
1964
|
-
throw new Error(`Unknown
|
|
1870
|
+
getPrimaryType: (acpType) => {
|
|
1871
|
+
if (acpType === "self")
|
|
1872
|
+
return "ACPIssuerSelf";
|
|
1873
|
+
if (acpType === "sharing")
|
|
1874
|
+
return "ACPIssuerShared";
|
|
1875
|
+
if (acpType === "recipient")
|
|
1876
|
+
return "ACPRecipient";
|
|
1877
|
+
throw new Error(`Unknown acp type: ${acpType}`);
|
|
1965
1878
|
}
|
|
1966
1879
|
};
|
|
1967
1880
|
var getAclAddress = async (publicClient) => {
|
|
@@ -1983,6 +1896,11 @@ var getAclEIP712Domain = async (publicClient) => {
|
|
|
1983
1896
|
functionName: "eip712Domain"
|
|
1984
1897
|
});
|
|
1985
1898
|
const [_fields, name, version, chainId, verifyingContract, _salt, _extensions] = domain;
|
|
1899
|
+
if (version !== "2") {
|
|
1900
|
+
throw new Error(
|
|
1901
|
+
`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.`
|
|
1902
|
+
);
|
|
1903
|
+
}
|
|
1986
1904
|
return {
|
|
1987
1905
|
name,
|
|
1988
1906
|
version,
|
|
@@ -1990,23 +1908,26 @@ var getAclEIP712Domain = async (publicClient) => {
|
|
|
1990
1908
|
verifyingContract
|
|
1991
1909
|
};
|
|
1992
1910
|
};
|
|
1993
|
-
var
|
|
1911
|
+
var checkACPValidityOnChain = async (acp, publicClient) => {
|
|
1994
1912
|
const aclAddress = await getAclAddress(publicClient);
|
|
1995
1913
|
try {
|
|
1996
1914
|
await publicClient.simulateContract({
|
|
1997
1915
|
address: aclAddress,
|
|
1998
|
-
abi:
|
|
1999
|
-
functionName: "
|
|
1916
|
+
abi: checkACPValidityAbi,
|
|
1917
|
+
functionName: "checkPermissionValidity",
|
|
2000
1918
|
args: [
|
|
2001
1919
|
{
|
|
2002
|
-
issuer:
|
|
2003
|
-
expiration: BigInt(
|
|
2004
|
-
recipient:
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
1920
|
+
issuer: acp.issuer,
|
|
1921
|
+
expiration: BigInt(acp.expiration),
|
|
1922
|
+
recipient: acp.recipient,
|
|
1923
|
+
revokerData: BigInt(acp.revokerData),
|
|
1924
|
+
revokerContract: acp.revokerContract,
|
|
1925
|
+
scope: acp.scope,
|
|
1926
|
+
contracts: acp.contracts,
|
|
1927
|
+
handles: acp.handles,
|
|
1928
|
+
sealingKey: acp.sealingKey,
|
|
1929
|
+
issuerSignature: acp.issuerSignature,
|
|
1930
|
+
recipientSignature: acp.recipientSignature
|
|
2010
1931
|
}
|
|
2011
1932
|
]
|
|
2012
1933
|
});
|
|
@@ -2019,14 +1940,14 @@ var checkPermitValidityOnChain = async (permission, publicClient) => {
|
|
|
2019
1940
|
throw new Error(errorName);
|
|
2020
1941
|
}
|
|
2021
1942
|
}
|
|
2022
|
-
const customErrorName = extractCustomErrorFromDetails(err,
|
|
1943
|
+
const customErrorName = extractCustomErrorFromDetails(err, checkACPValidityAbi);
|
|
2023
1944
|
if (customErrorName) {
|
|
2024
1945
|
throw new Error(customErrorName);
|
|
2025
1946
|
}
|
|
2026
1947
|
const hhDetailsData = extractReturnData(err);
|
|
2027
1948
|
if (hhDetailsData != null) {
|
|
2028
1949
|
const decoded = viem.decodeErrorResult({
|
|
2029
|
-
abi:
|
|
1950
|
+
abi: checkACPValidityAbi,
|
|
2030
1951
|
data: hhDetailsData
|
|
2031
1952
|
});
|
|
2032
1953
|
throw new Error(decoded.errorName);
|
|
@@ -2054,15 +1975,15 @@ function extractReturnData(err) {
|
|
|
2054
1975
|
const s = anyErr?.details ?? anyErr?.cause?.details ?? anyErr?.shortMessage ?? anyErr?.message ?? String(err);
|
|
2055
1976
|
return s.match(/return data:\s*(0x[a-fA-F0-9]+)/)?.[1];
|
|
2056
1977
|
}
|
|
2057
|
-
var
|
|
1978
|
+
var checkACPValidityAbi = [
|
|
2058
1979
|
{
|
|
2059
1980
|
type: "function",
|
|
2060
|
-
name: "
|
|
1981
|
+
name: "checkPermissionValidity",
|
|
2061
1982
|
inputs: [
|
|
2062
1983
|
{
|
|
2063
|
-
name: "
|
|
1984
|
+
name: "acp",
|
|
2064
1985
|
type: "tuple",
|
|
2065
|
-
internalType: "struct
|
|
1986
|
+
internalType: "struct ACP",
|
|
2066
1987
|
components: [
|
|
2067
1988
|
{
|
|
2068
1989
|
name: "issuer",
|
|
@@ -2080,15 +2001,30 @@ var checkPermitValidityAbi = [
|
|
|
2080
2001
|
internalType: "address"
|
|
2081
2002
|
},
|
|
2082
2003
|
{
|
|
2083
|
-
name: "
|
|
2004
|
+
name: "revokerData",
|
|
2084
2005
|
type: "uint256",
|
|
2085
2006
|
internalType: "uint256"
|
|
2086
2007
|
},
|
|
2087
2008
|
{
|
|
2088
|
-
name: "
|
|
2009
|
+
name: "revokerContract",
|
|
2089
2010
|
type: "address",
|
|
2090
2011
|
internalType: "address"
|
|
2091
2012
|
},
|
|
2013
|
+
{
|
|
2014
|
+
name: "scope",
|
|
2015
|
+
type: "uint8",
|
|
2016
|
+
internalType: "uint8"
|
|
2017
|
+
},
|
|
2018
|
+
{
|
|
2019
|
+
name: "contracts",
|
|
2020
|
+
type: "address[]",
|
|
2021
|
+
internalType: "address[]"
|
|
2022
|
+
},
|
|
2023
|
+
{
|
|
2024
|
+
name: "handles",
|
|
2025
|
+
type: "bytes32[]",
|
|
2026
|
+
internalType: "bytes32[]"
|
|
2027
|
+
},
|
|
2092
2028
|
{
|
|
2093
2029
|
name: "sealingKey",
|
|
2094
2030
|
type: "bytes32",
|
|
@@ -2138,38 +2074,40 @@ var checkPermitValidityAbi = [
|
|
|
2138
2074
|
}
|
|
2139
2075
|
];
|
|
2140
2076
|
|
|
2141
|
-
//
|
|
2142
|
-
var
|
|
2077
|
+
// acps/acp.ts
|
|
2078
|
+
var ACPUtils = {
|
|
2143
2079
|
/**
|
|
2144
|
-
* Create a self
|
|
2080
|
+
* Create a self acp for personal use
|
|
2145
2081
|
*/
|
|
2146
2082
|
createSelf: (options) => {
|
|
2147
|
-
const validation =
|
|
2083
|
+
const validation = validateSelfACPOptions(options);
|
|
2148
2084
|
const sealingPair = GenerateSealingKey();
|
|
2149
|
-
const
|
|
2150
|
-
hash:
|
|
2085
|
+
const acp = {
|
|
2086
|
+
hash: ACPUtils.getHash(validation),
|
|
2151
2087
|
...validation,
|
|
2152
|
-
sealingPair,
|
|
2088
|
+
sealingPrivateKey: sealingPair.privateKey,
|
|
2089
|
+
sealingKey: sealingPair.publicKey,
|
|
2153
2090
|
_signedDomain: void 0
|
|
2154
2091
|
};
|
|
2155
|
-
return
|
|
2092
|
+
return acp;
|
|
2156
2093
|
},
|
|
2157
2094
|
/**
|
|
2158
|
-
* Create a sharing
|
|
2095
|
+
* Create a sharing acp to be shared with another user
|
|
2159
2096
|
*/
|
|
2160
2097
|
createSharing: (options) => {
|
|
2161
|
-
const validation =
|
|
2098
|
+
const validation = validateSharingACPOptions(options);
|
|
2162
2099
|
const sealingPair = GenerateSealingKey();
|
|
2163
|
-
const
|
|
2164
|
-
hash:
|
|
2100
|
+
const acp = {
|
|
2101
|
+
hash: ACPUtils.getHash(validation),
|
|
2165
2102
|
...validation,
|
|
2166
|
-
sealingPair,
|
|
2103
|
+
sealingPrivateKey: sealingPair.privateKey,
|
|
2104
|
+
sealingKey: sealingPair.publicKey,
|
|
2167
2105
|
_signedDomain: void 0
|
|
2168
2106
|
};
|
|
2169
|
-
return
|
|
2107
|
+
return acp;
|
|
2170
2108
|
},
|
|
2171
2109
|
/**
|
|
2172
|
-
* Import a shared
|
|
2110
|
+
* Import a shared acp from various input formats
|
|
2173
2111
|
*/
|
|
2174
2112
|
importShared: (options) => {
|
|
2175
2113
|
let parsedOptions;
|
|
@@ -2182,33 +2120,34 @@ var PermitUtils = {
|
|
|
2182
2120
|
} else if (typeof options === "object" && options !== null) {
|
|
2183
2121
|
parsedOptions = options;
|
|
2184
2122
|
} else {
|
|
2185
|
-
throw new Error("Invalid input type, expected
|
|
2123
|
+
throw new Error("Invalid input type, expected ImportSharedACPOptions, object, or string");
|
|
2186
2124
|
}
|
|
2187
2125
|
if (parsedOptions.type != null && parsedOptions.type !== "sharing") {
|
|
2188
|
-
throw new Error(`Invalid
|
|
2126
|
+
throw new Error(`Invalid acp type <${parsedOptions.type}>, must be "sharing"`);
|
|
2189
2127
|
}
|
|
2190
|
-
const validation =
|
|
2128
|
+
const validation = validateImportACPOptions({ ...parsedOptions, type: "recipient" });
|
|
2191
2129
|
const sealingPair = GenerateSealingKey();
|
|
2192
|
-
const
|
|
2193
|
-
hash:
|
|
2130
|
+
const acp = {
|
|
2131
|
+
hash: ACPUtils.getHash(validation),
|
|
2194
2132
|
...validation,
|
|
2195
|
-
sealingPair,
|
|
2133
|
+
sealingPrivateKey: sealingPair.privateKey,
|
|
2134
|
+
sealingKey: sealingPair.publicKey,
|
|
2196
2135
|
_signedDomain: void 0
|
|
2197
2136
|
};
|
|
2198
|
-
return
|
|
2137
|
+
return acp;
|
|
2199
2138
|
},
|
|
2200
2139
|
/**
|
|
2201
|
-
* Sign
|
|
2140
|
+
* Sign an ACP with the provided wallet client
|
|
2202
2141
|
*/
|
|
2203
|
-
sign: async (
|
|
2142
|
+
sign: async (acp, publicClient, walletClient) => {
|
|
2204
2143
|
if (walletClient == null || walletClient.account == null) {
|
|
2205
2144
|
throw new Error(
|
|
2206
|
-
"Missing walletClient, you must pass in a `walletClient` for the connected user to create
|
|
2145
|
+
"Missing walletClient, you must pass in a `walletClient` for the connected user to create an ACP signature"
|
|
2207
2146
|
);
|
|
2208
2147
|
}
|
|
2209
|
-
const primaryType = SignatureUtils.getPrimaryType(
|
|
2148
|
+
const primaryType = SignatureUtils.getPrimaryType(acp.type);
|
|
2210
2149
|
const domain = await getAclEIP712Domain(publicClient);
|
|
2211
|
-
const { types, message } = SignatureUtils.getSignatureParams(
|
|
2150
|
+
const { types, message } = SignatureUtils.getSignatureParams(ACPUtils.getPublic(acp, true), primaryType);
|
|
2212
2151
|
const signature = await walletClient.signTypedData({
|
|
2213
2152
|
domain,
|
|
2214
2153
|
types,
|
|
@@ -2216,190 +2155,209 @@ var PermitUtils = {
|
|
|
2216
2155
|
message,
|
|
2217
2156
|
account: walletClient.account
|
|
2218
2157
|
});
|
|
2219
|
-
let
|
|
2220
|
-
if (
|
|
2221
|
-
|
|
2222
|
-
...
|
|
2158
|
+
let updatedACP;
|
|
2159
|
+
if (acp.type === "self" || acp.type === "sharing") {
|
|
2160
|
+
updatedACP = {
|
|
2161
|
+
...acp,
|
|
2223
2162
|
issuerSignature: signature,
|
|
2224
2163
|
_signedDomain: domain
|
|
2225
2164
|
};
|
|
2226
2165
|
} else {
|
|
2227
|
-
|
|
2228
|
-
...
|
|
2166
|
+
updatedACP = {
|
|
2167
|
+
...acp,
|
|
2229
2168
|
recipientSignature: signature,
|
|
2230
2169
|
_signedDomain: domain
|
|
2231
2170
|
};
|
|
2232
2171
|
}
|
|
2233
|
-
return
|
|
2172
|
+
return updatedACP;
|
|
2234
2173
|
},
|
|
2235
2174
|
/**
|
|
2236
|
-
* Create and sign a self
|
|
2175
|
+
* Create and sign a self acp in one operation
|
|
2237
2176
|
*/
|
|
2238
2177
|
createSelfAndSign: async (options, publicClient, walletClient) => {
|
|
2239
|
-
const
|
|
2240
|
-
return
|
|
2178
|
+
const acp = ACPUtils.createSelf(options);
|
|
2179
|
+
return ACPUtils.sign(acp, publicClient, walletClient);
|
|
2241
2180
|
},
|
|
2242
2181
|
/**
|
|
2243
|
-
* Create and sign a sharing
|
|
2182
|
+
* Create and sign a sharing acp in one operation
|
|
2244
2183
|
*/
|
|
2245
2184
|
createSharingAndSign: async (options, publicClient, walletClient) => {
|
|
2246
|
-
const
|
|
2247
|
-
return
|
|
2185
|
+
const acp = ACPUtils.createSharing(options);
|
|
2186
|
+
return ACPUtils.sign(acp, publicClient, walletClient);
|
|
2248
2187
|
},
|
|
2249
2188
|
/**
|
|
2250
|
-
* Import and sign a shared
|
|
2189
|
+
* Import and sign a shared acp in one operation from various input formats
|
|
2251
2190
|
*/
|
|
2252
2191
|
importSharedAndSign: async (options, publicClient, walletClient) => {
|
|
2253
|
-
const
|
|
2254
|
-
return
|
|
2192
|
+
const acp = ACPUtils.importShared(options);
|
|
2193
|
+
return ACPUtils.sign(acp, publicClient, walletClient);
|
|
2255
2194
|
},
|
|
2256
2195
|
/**
|
|
2257
|
-
* Deserialize
|
|
2196
|
+
* Deserialize an ACP from serialized data
|
|
2258
2197
|
*/
|
|
2259
2198
|
deserialize: (data) => {
|
|
2260
|
-
return {
|
|
2261
|
-
...data,
|
|
2262
|
-
sealingPair: SealingKey.deserialize(data.sealingPair.privateKey, data.sealingPair.publicKey)
|
|
2263
|
-
};
|
|
2199
|
+
return { ...data };
|
|
2264
2200
|
},
|
|
2265
2201
|
/**
|
|
2266
|
-
* Serialize
|
|
2202
|
+
* Serialize an ACP for storage
|
|
2267
2203
|
*/
|
|
2268
|
-
serialize: (
|
|
2204
|
+
serialize: (acp) => {
|
|
2269
2205
|
return {
|
|
2270
|
-
hash:
|
|
2271
|
-
name:
|
|
2272
|
-
type:
|
|
2273
|
-
issuer:
|
|
2274
|
-
expiration:
|
|
2275
|
-
recipient:
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2206
|
+
hash: acp.hash,
|
|
2207
|
+
name: acp.name,
|
|
2208
|
+
type: acp.type,
|
|
2209
|
+
issuer: acp.issuer,
|
|
2210
|
+
expiration: acp.expiration,
|
|
2211
|
+
recipient: acp.recipient,
|
|
2212
|
+
revokerData: acp.revokerData,
|
|
2213
|
+
revokerContract: acp.revokerContract,
|
|
2214
|
+
scope: acp.scope,
|
|
2215
|
+
contracts: acp.contracts,
|
|
2216
|
+
handles: acp.handles,
|
|
2217
|
+
sealingKey: acp.sealingKey,
|
|
2218
|
+
issuerSignature: acp.issuerSignature,
|
|
2219
|
+
recipientSignature: acp.recipientSignature,
|
|
2220
|
+
_signedDomain: acp._signedDomain,
|
|
2221
|
+
sealingPrivateKey: acp.sealingPrivateKey
|
|
2282
2222
|
};
|
|
2283
2223
|
},
|
|
2284
2224
|
/**
|
|
2285
|
-
* Validate
|
|
2225
|
+
* Validate an ACP (schema-level validation)
|
|
2286
2226
|
*/
|
|
2287
|
-
validateSchema: (
|
|
2288
|
-
if (
|
|
2289
|
-
return
|
|
2290
|
-
} else if (
|
|
2291
|
-
return
|
|
2292
|
-
} else if (
|
|
2293
|
-
return
|
|
2227
|
+
validateSchema: (acp) => {
|
|
2228
|
+
if (acp.type === "self") {
|
|
2229
|
+
return validateSelfACP(acp);
|
|
2230
|
+
} else if (acp.type === "sharing") {
|
|
2231
|
+
return validateSharingACP(acp);
|
|
2232
|
+
} else if (acp.type === "recipient") {
|
|
2233
|
+
return validateImportACP(acp);
|
|
2294
2234
|
} else {
|
|
2295
|
-
throw new Error("Invalid
|
|
2235
|
+
throw new Error("Invalid ACP type");
|
|
2296
2236
|
}
|
|
2297
2237
|
},
|
|
2298
2238
|
/**
|
|
2299
|
-
* Validate
|
|
2239
|
+
* Validate an ACP (holistic validation).
|
|
2300
2240
|
*
|
|
2301
2241
|
* This validates:
|
|
2302
|
-
* -
|
|
2303
|
-
* -
|
|
2304
|
-
* -
|
|
2242
|
+
* - ACP schema (shape + invariants)
|
|
2243
|
+
* - ACP is signed
|
|
2244
|
+
* - ACP is not expired
|
|
2305
2245
|
*
|
|
2306
|
-
* For schema-only validation, use `validateSchema(
|
|
2246
|
+
* For schema-only validation, use `validateSchema(acp)`.
|
|
2307
2247
|
*/
|
|
2308
|
-
validate: (
|
|
2309
|
-
const validated =
|
|
2248
|
+
validate: (acp) => {
|
|
2249
|
+
const validated = ACPUtils.validateSchema(acp);
|
|
2310
2250
|
ValidationUtils.assertSignedAndNotExpired(validated);
|
|
2311
2251
|
return validated;
|
|
2312
2252
|
},
|
|
2313
2253
|
/**
|
|
2314
|
-
* Get the
|
|
2254
|
+
* Get the public component of an ACP — the signed struct sent on-chain / to the decryption backend.
|
|
2255
|
+
* Strips the private component (hash, name, type, sealing pair).
|
|
2315
2256
|
*/
|
|
2316
|
-
|
|
2257
|
+
getPublic: (acp, skipValidation = false) => {
|
|
2317
2258
|
if (!skipValidation) {
|
|
2318
|
-
|
|
2259
|
+
ACPUtils.validateSchema(acp);
|
|
2319
2260
|
}
|
|
2320
2261
|
return {
|
|
2321
|
-
issuer:
|
|
2322
|
-
expiration:
|
|
2323
|
-
recipient:
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2262
|
+
issuer: acp.issuer,
|
|
2263
|
+
expiration: acp.expiration,
|
|
2264
|
+
recipient: acp.recipient,
|
|
2265
|
+
revokerData: acp.revokerData,
|
|
2266
|
+
revokerContract: acp.revokerContract,
|
|
2267
|
+
scope: acp.scope,
|
|
2268
|
+
contracts: acp.contracts,
|
|
2269
|
+
handles: acp.handles,
|
|
2270
|
+
sealingKey: acp.sealingKey,
|
|
2271
|
+
issuerSignature: acp.issuerSignature,
|
|
2272
|
+
recipientSignature: acp.recipientSignature
|
|
2329
2273
|
};
|
|
2330
2274
|
},
|
|
2331
2275
|
/**
|
|
2332
|
-
* Get a stable hash for the
|
|
2276
|
+
* Get a stable hash for the acp (used as key in storage)
|
|
2333
2277
|
*/
|
|
2334
|
-
getHash: (
|
|
2278
|
+
getHash: (acp) => {
|
|
2335
2279
|
const data = JSON.stringify({
|
|
2336
|
-
type:
|
|
2337
|
-
issuer:
|
|
2338
|
-
expiration:
|
|
2339
|
-
recipient:
|
|
2340
|
-
|
|
2341
|
-
|
|
2280
|
+
type: acp.type,
|
|
2281
|
+
issuer: acp.issuer,
|
|
2282
|
+
expiration: acp.expiration,
|
|
2283
|
+
recipient: acp.recipient,
|
|
2284
|
+
revokerData: acp.revokerData,
|
|
2285
|
+
revokerContract: acp.revokerContract,
|
|
2286
|
+
scope: acp.scope,
|
|
2287
|
+
contracts: acp.contracts,
|
|
2288
|
+
handles: acp.handles
|
|
2342
2289
|
});
|
|
2343
2290
|
return viem.keccak256(viem.toHex(data));
|
|
2344
2291
|
},
|
|
2345
2292
|
/**
|
|
2346
|
-
* Export
|
|
2293
|
+
* Export acp data for sharing (strips the private component).
|
|
2294
|
+
* Fixed `SharedACP` shape — every field always present, aligned with
|
|
2295
|
+
* `ACPPublic` and the on-chain sharing payload.
|
|
2347
2296
|
*/
|
|
2348
|
-
export: (
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2297
|
+
export: (acp) => {
|
|
2298
|
+
if (acp.type !== "sharing") {
|
|
2299
|
+
throw new Error(
|
|
2300
|
+
`Cannot export a '${acp.type}' ACP \u2014 only 'sharing' ACPs are exportable. The export includes the issuer signature.`
|
|
2301
|
+
);
|
|
2302
|
+
}
|
|
2303
|
+
if (acp.issuerSignature === "0x") {
|
|
2304
|
+
throw new Error(
|
|
2305
|
+
"Cannot export an unsigned sharing ACP \u2014 sign it first (the recipient needs the issuer signature)."
|
|
2306
|
+
);
|
|
2307
|
+
}
|
|
2308
|
+
const shared = {
|
|
2309
|
+
name: acp.name,
|
|
2310
|
+
type: acp.type,
|
|
2311
|
+
issuer: acp.issuer,
|
|
2312
|
+
expiration: acp.expiration,
|
|
2313
|
+
recipient: acp.recipient,
|
|
2314
|
+
revokerData: acp.revokerData,
|
|
2315
|
+
revokerContract: acp.revokerContract,
|
|
2316
|
+
scope: acp.scope,
|
|
2317
|
+
contracts: acp.contracts,
|
|
2318
|
+
handles: acp.handles,
|
|
2319
|
+
issuerSignature: acp.issuerSignature
|
|
2354
2320
|
};
|
|
2355
|
-
|
|
2356
|
-
cleanedPermit.recipient = permit.recipient;
|
|
2357
|
-
if (permit.validatorId !== 0)
|
|
2358
|
-
cleanedPermit.validatorId = permit.validatorId;
|
|
2359
|
-
if (permit.validatorContract !== viem.zeroAddress)
|
|
2360
|
-
cleanedPermit.validatorContract = permit.validatorContract;
|
|
2361
|
-
if (permit.type === "sharing" && permit.issuerSignature !== "0x")
|
|
2362
|
-
cleanedPermit.issuerSignature = permit.issuerSignature;
|
|
2363
|
-
return JSON.stringify(cleanedPermit, void 0, 2);
|
|
2321
|
+
return JSON.stringify(shared, void 0, 2);
|
|
2364
2322
|
},
|
|
2365
2323
|
/**
|
|
2366
|
-
* Unseal encrypted data using the
|
|
2324
|
+
* Unseal encrypted data using the acp's sealing key
|
|
2367
2325
|
*/
|
|
2368
|
-
unseal: (
|
|
2369
|
-
return
|
|
2326
|
+
unseal: (acp, ciphertext) => {
|
|
2327
|
+
return unsealWithPrivateKey(acp.sealingPrivateKey, ciphertext);
|
|
2370
2328
|
},
|
|
2371
2329
|
/**
|
|
2372
|
-
* Check if
|
|
2330
|
+
* Check if acp is expired
|
|
2373
2331
|
*/
|
|
2374
|
-
isExpired: (
|
|
2375
|
-
return ValidationUtils.isExpired(
|
|
2332
|
+
isExpired: (acp) => {
|
|
2333
|
+
return ValidationUtils.isExpired(acp);
|
|
2376
2334
|
},
|
|
2377
2335
|
/**
|
|
2378
|
-
* Check if
|
|
2336
|
+
* Check if acp is signed
|
|
2379
2337
|
*/
|
|
2380
|
-
isSigned: (
|
|
2381
|
-
return ValidationUtils.isSigned(
|
|
2338
|
+
isSigned: (acp) => {
|
|
2339
|
+
return ValidationUtils.isSigned(acp);
|
|
2382
2340
|
},
|
|
2383
2341
|
/**
|
|
2384
|
-
* Check if
|
|
2342
|
+
* Check if acp is signed and not expired
|
|
2385
2343
|
*/
|
|
2386
|
-
isSignedAndNotExpired: (
|
|
2387
|
-
return ValidationUtils.isSignedAndNotExpired(
|
|
2344
|
+
isSignedAndNotExpired: (acp) => {
|
|
2345
|
+
return ValidationUtils.isSignedAndNotExpired(acp);
|
|
2388
2346
|
},
|
|
2389
2347
|
/**
|
|
2390
|
-
* Assert that
|
|
2348
|
+
* Assert that acp is signed and not expired
|
|
2391
2349
|
*/
|
|
2392
|
-
assertSignedAndNotExpired: (
|
|
2393
|
-
return ValidationUtils.assertSignedAndNotExpired(
|
|
2350
|
+
assertSignedAndNotExpired: (acp) => {
|
|
2351
|
+
return ValidationUtils.assertSignedAndNotExpired(acp);
|
|
2394
2352
|
},
|
|
2395
|
-
isValid: (
|
|
2396
|
-
return ValidationUtils.isValid(
|
|
2353
|
+
isValid: (acp) => {
|
|
2354
|
+
return ValidationUtils.isValid(acp);
|
|
2397
2355
|
},
|
|
2398
2356
|
/**
|
|
2399
|
-
* Update
|
|
2357
|
+
* Update acp name (returns new acp instance)
|
|
2400
2358
|
*/
|
|
2401
|
-
updateName: (
|
|
2402
|
-
return { ...
|
|
2359
|
+
updateName: (acp, name) => {
|
|
2360
|
+
return { ...acp, name };
|
|
2403
2361
|
},
|
|
2404
2362
|
/**
|
|
2405
2363
|
* Fetch EIP712 domain from the blockchain
|
|
@@ -2408,230 +2366,468 @@ var PermitUtils = {
|
|
|
2408
2366
|
return getAclEIP712Domain(publicClient);
|
|
2409
2367
|
},
|
|
2410
2368
|
/**
|
|
2411
|
-
* Check if
|
|
2369
|
+
* Check if acp's signed domain matches the provided domain
|
|
2412
2370
|
*/
|
|
2413
|
-
matchesDomain: (
|
|
2414
|
-
return
|
|
2371
|
+
matchesDomain: (acp, domain) => {
|
|
2372
|
+
return acp._signedDomain?.name === domain.name && acp._signedDomain?.version === domain.version && acp._signedDomain?.verifyingContract === domain.verifyingContract && acp._signedDomain?.chainId === domain.chainId;
|
|
2415
2373
|
},
|
|
2416
2374
|
/**
|
|
2417
|
-
* Check if
|
|
2375
|
+
* Check if acp's signed domain is valid for the current chain
|
|
2418
2376
|
*/
|
|
2419
|
-
checkSignedDomainValid: async (
|
|
2420
|
-
if (
|
|
2377
|
+
checkSignedDomainValid: async (acp, publicClient) => {
|
|
2378
|
+
if (acp._signedDomain == null)
|
|
2421
2379
|
return false;
|
|
2422
2380
|
const domain = await getAclEIP712Domain(publicClient);
|
|
2423
|
-
return
|
|
2381
|
+
return ACPUtils.matchesDomain(acp, domain);
|
|
2424
2382
|
},
|
|
2425
2383
|
/**
|
|
2426
|
-
* Check if
|
|
2384
|
+
* Check if acp passes the on-chain validation
|
|
2427
2385
|
*/
|
|
2428
|
-
checkValidityOnChain: async (
|
|
2429
|
-
const
|
|
2430
|
-
return
|
|
2386
|
+
checkValidityOnChain: async (acp, publicClient) => {
|
|
2387
|
+
const publicAcp = ACPUtils.getPublic(acp);
|
|
2388
|
+
return checkACPValidityOnChain(publicAcp, publicClient);
|
|
2431
2389
|
}
|
|
2432
2390
|
};
|
|
2433
|
-
var
|
|
2434
|
-
|
|
2435
|
-
|
|
2391
|
+
var ACP_STORE_DEFAULTS = {
|
|
2392
|
+
acps: {},
|
|
2393
|
+
activeACPHash: {}
|
|
2436
2394
|
};
|
|
2437
|
-
var
|
|
2438
|
-
|
|
2395
|
+
var ACP_STORE_VERSION = 3;
|
|
2396
|
+
var _acpStore = vanilla.createStore()(
|
|
2397
|
+
middleware.persist(() => ACP_STORE_DEFAULTS, {
|
|
2398
|
+
name: "cofhesdk-acps",
|
|
2399
|
+
version: ACP_STORE_VERSION,
|
|
2400
|
+
migrate: (persistedState, version) => {
|
|
2401
|
+
if (version < ACP_STORE_VERSION)
|
|
2402
|
+
return ACP_STORE_DEFAULTS;
|
|
2403
|
+
return persistedState;
|
|
2404
|
+
}
|
|
2405
|
+
})
|
|
2439
2406
|
);
|
|
2440
2407
|
var clearStaleStore = () => {
|
|
2441
|
-
const state =
|
|
2442
|
-
const hasExpectedStructure = state && typeof state === "object" && "
|
|
2408
|
+
const state = _acpStore.getState();
|
|
2409
|
+
const hasExpectedStructure = state && typeof state === "object" && "acps" in state && "activeACPHash" in state && typeof state.acps === "object" && typeof state.activeACPHash === "object";
|
|
2443
2410
|
if (hasExpectedStructure)
|
|
2444
2411
|
return;
|
|
2445
|
-
|
|
2412
|
+
_acpStore.setState({ acps: {}, activeACPHash: {} });
|
|
2446
2413
|
};
|
|
2447
|
-
var
|
|
2414
|
+
var getACP = (chainId, account, hash) => {
|
|
2448
2415
|
clearStaleStore();
|
|
2449
2416
|
if (chainId == null || account == null || hash == null)
|
|
2450
2417
|
return;
|
|
2451
|
-
const
|
|
2452
|
-
if (
|
|
2418
|
+
const savedACP = _acpStore.getState().acps[chainId]?.[account]?.[hash];
|
|
2419
|
+
if (savedACP == null)
|
|
2453
2420
|
return;
|
|
2454
|
-
return
|
|
2421
|
+
return ACPUtils.deserialize(savedACP);
|
|
2455
2422
|
};
|
|
2456
|
-
var
|
|
2423
|
+
var getActiveACP = (chainId, account) => {
|
|
2457
2424
|
clearStaleStore();
|
|
2458
2425
|
if (chainId == null || account == null)
|
|
2459
2426
|
return;
|
|
2460
|
-
const
|
|
2461
|
-
return
|
|
2427
|
+
const activeACPHash = _acpStore.getState().activeACPHash[chainId]?.[account];
|
|
2428
|
+
return getACP(chainId, account, activeACPHash);
|
|
2462
2429
|
};
|
|
2463
|
-
var
|
|
2430
|
+
var getACPs = (chainId, account) => {
|
|
2464
2431
|
clearStaleStore();
|
|
2465
2432
|
if (chainId == null || account == null)
|
|
2466
2433
|
return {};
|
|
2467
|
-
return Object.entries(
|
|
2468
|
-
(acc, [hash,
|
|
2469
|
-
if (
|
|
2434
|
+
return Object.entries(_acpStore.getState().acps[chainId]?.[account] ?? {}).reduce(
|
|
2435
|
+
(acc, [hash, acp]) => {
|
|
2436
|
+
if (acp == void 0)
|
|
2470
2437
|
return acc;
|
|
2471
|
-
return { ...acc, [hash]:
|
|
2438
|
+
return { ...acc, [hash]: ACPUtils.deserialize(acp) };
|
|
2472
2439
|
},
|
|
2473
2440
|
{}
|
|
2474
2441
|
);
|
|
2475
2442
|
};
|
|
2476
|
-
var
|
|
2443
|
+
var setACP = (chainId, account, acp) => {
|
|
2477
2444
|
clearStaleStore();
|
|
2478
|
-
|
|
2445
|
+
_acpStore.setState(
|
|
2479
2446
|
immer.produce((state) => {
|
|
2480
|
-
if (state.
|
|
2481
|
-
state.
|
|
2482
|
-
if (state.
|
|
2483
|
-
state.
|
|
2484
|
-
state.
|
|
2447
|
+
if (state.acps[chainId] == null)
|
|
2448
|
+
state.acps[chainId] = {};
|
|
2449
|
+
if (state.acps[chainId][account] == null)
|
|
2450
|
+
state.acps[chainId][account] = {};
|
|
2451
|
+
state.acps[chainId][account][acp.hash] = ACPUtils.serialize(acp);
|
|
2485
2452
|
})
|
|
2486
2453
|
);
|
|
2487
2454
|
};
|
|
2488
|
-
var
|
|
2455
|
+
var removeACP = (chainId, account, hash) => {
|
|
2489
2456
|
clearStaleStore();
|
|
2490
|
-
|
|
2457
|
+
_acpStore.setState(
|
|
2491
2458
|
immer.produce((state) => {
|
|
2492
|
-
if (state.
|
|
2493
|
-
state.
|
|
2494
|
-
if (state.
|
|
2495
|
-
state.
|
|
2496
|
-
const
|
|
2497
|
-
if (
|
|
2459
|
+
if (state.acps[chainId] == null)
|
|
2460
|
+
state.acps[chainId] = {};
|
|
2461
|
+
if (state.activeACPHash[chainId] == null)
|
|
2462
|
+
state.activeACPHash[chainId] = {};
|
|
2463
|
+
const accountACPs = state.acps[chainId][account];
|
|
2464
|
+
if (accountACPs == null)
|
|
2498
2465
|
return;
|
|
2499
|
-
if (
|
|
2466
|
+
if (accountACPs[hash] == null)
|
|
2500
2467
|
return;
|
|
2501
|
-
if (state.
|
|
2502
|
-
state.
|
|
2468
|
+
if (state.activeACPHash[chainId][account] === hash) {
|
|
2469
|
+
state.activeACPHash[chainId][account] = void 0;
|
|
2503
2470
|
}
|
|
2504
|
-
|
|
2471
|
+
accountACPs[hash] = void 0;
|
|
2505
2472
|
})
|
|
2506
2473
|
);
|
|
2507
2474
|
};
|
|
2508
|
-
var
|
|
2475
|
+
var getActiveACPHash = (chainId, account) => {
|
|
2509
2476
|
clearStaleStore();
|
|
2510
2477
|
if (chainId == null || account == null)
|
|
2511
2478
|
return void 0;
|
|
2512
|
-
return
|
|
2479
|
+
return _acpStore.getState().activeACPHash[chainId]?.[account];
|
|
2513
2480
|
};
|
|
2514
|
-
var
|
|
2481
|
+
var setActiveACPHash = (chainId, account, hash) => {
|
|
2515
2482
|
clearStaleStore();
|
|
2516
|
-
|
|
2483
|
+
_acpStore.setState(
|
|
2517
2484
|
immer.produce((state) => {
|
|
2518
|
-
if (state.
|
|
2519
|
-
state.
|
|
2520
|
-
state.
|
|
2485
|
+
if (state.activeACPHash[chainId] == null)
|
|
2486
|
+
state.activeACPHash[chainId] = {};
|
|
2487
|
+
state.activeACPHash[chainId][account] = hash;
|
|
2521
2488
|
})
|
|
2522
2489
|
);
|
|
2523
2490
|
};
|
|
2524
|
-
var
|
|
2491
|
+
var removeActiveACPHash = (chainId, account) => {
|
|
2525
2492
|
clearStaleStore();
|
|
2526
|
-
|
|
2493
|
+
_acpStore.setState(
|
|
2527
2494
|
immer.produce((state) => {
|
|
2528
|
-
if (state.
|
|
2529
|
-
state.
|
|
2495
|
+
if (state.activeACPHash[chainId])
|
|
2496
|
+
state.activeACPHash[chainId][account] = void 0;
|
|
2530
2497
|
})
|
|
2531
2498
|
);
|
|
2532
2499
|
};
|
|
2533
2500
|
var resetStore = () => {
|
|
2534
2501
|
clearStaleStore();
|
|
2535
|
-
|
|
2502
|
+
_acpStore.setState({ acps: {}, activeACPHash: {} });
|
|
2536
2503
|
};
|
|
2537
|
-
var
|
|
2538
|
-
store:
|
|
2539
|
-
|
|
2540
|
-
|
|
2541
|
-
|
|
2542
|
-
|
|
2543
|
-
|
|
2544
|
-
|
|
2545
|
-
|
|
2546
|
-
|
|
2504
|
+
var acpStore = {
|
|
2505
|
+
store: _acpStore,
|
|
2506
|
+
getACP,
|
|
2507
|
+
getActiveACP,
|
|
2508
|
+
getACPs,
|
|
2509
|
+
setACP,
|
|
2510
|
+
removeACP,
|
|
2511
|
+
getActiveACPHash,
|
|
2512
|
+
setActiveACPHash,
|
|
2513
|
+
removeActiveACPHash,
|
|
2547
2514
|
resetStore
|
|
2548
2515
|
};
|
|
2549
|
-
var
|
|
2516
|
+
var ACP_VALIDATOR_ABI = viem.parseAbi([
|
|
2517
|
+
"function revokeSingle(uint256 id)",
|
|
2518
|
+
"function revokeAllExisting()",
|
|
2519
|
+
"function disabled(address issuer, uint256 id) view returns (bool)"
|
|
2520
|
+
]);
|
|
2521
|
+
var storeACP = async (acp, publicClient, walletClient) => {
|
|
2550
2522
|
const chainId = await publicClient.getChainId();
|
|
2551
2523
|
const account = walletClient.account.address;
|
|
2552
|
-
|
|
2553
|
-
permitStore.setActivePermitHash(chainId, account, permit.hash);
|
|
2524
|
+
acpStore.setACP(chainId, account, acp);
|
|
2554
2525
|
};
|
|
2555
|
-
var
|
|
2556
|
-
|
|
2557
|
-
await
|
|
2558
|
-
|
|
2526
|
+
var storeActiveACP = async (acp, publicClient, walletClient) => {
|
|
2527
|
+
await storeACP(acp, publicClient, walletClient);
|
|
2528
|
+
const chainId = await publicClient.getChainId();
|
|
2529
|
+
const account = walletClient.account.address;
|
|
2530
|
+
acpStore.setActiveACPHash(chainId, account, acp.hash);
|
|
2531
|
+
};
|
|
2532
|
+
var createACPWithSign = async (options, publicClient, walletClient, acpMethod, activate = true) => {
|
|
2533
|
+
const acp = await acpMethod(options, publicClient, walletClient);
|
|
2534
|
+
if (activate) {
|
|
2535
|
+
await storeActiveACP(acp, publicClient, walletClient);
|
|
2536
|
+
} else {
|
|
2537
|
+
await storeACP(acp, publicClient, walletClient);
|
|
2538
|
+
}
|
|
2539
|
+
return acp;
|
|
2559
2540
|
};
|
|
2560
2541
|
var createSelf = async (options, publicClient, walletClient) => {
|
|
2561
|
-
return
|
|
2542
|
+
return createACPWithSign(options, publicClient, walletClient, ACPUtils.createSelfAndSign);
|
|
2562
2543
|
};
|
|
2563
2544
|
var createSharing = async (options, publicClient, walletClient) => {
|
|
2564
|
-
return
|
|
2545
|
+
return createACPWithSign(options, publicClient, walletClient, ACPUtils.createSharingAndSign, false);
|
|
2565
2546
|
};
|
|
2566
2547
|
var importShared = async (options, publicClient, walletClient) => {
|
|
2567
|
-
return
|
|
2548
|
+
return createACPWithSign(options, publicClient, walletClient, ACPUtils.importSharedAndSign);
|
|
2568
2549
|
};
|
|
2569
|
-
var getHash = (
|
|
2570
|
-
return
|
|
2550
|
+
var getHash = (acp) => {
|
|
2551
|
+
return ACPUtils.getHash(acp);
|
|
2571
2552
|
};
|
|
2572
|
-
var exportShared = (
|
|
2573
|
-
return
|
|
2553
|
+
var exportShared = (acp) => {
|
|
2554
|
+
return ACPUtils.export(acp);
|
|
2574
2555
|
};
|
|
2575
|
-
var serialize = (
|
|
2576
|
-
return
|
|
2556
|
+
var serialize = (acp) => {
|
|
2557
|
+
return ACPUtils.serialize(acp);
|
|
2577
2558
|
};
|
|
2578
2559
|
var deserialize = (serialized) => {
|
|
2579
|
-
return
|
|
2560
|
+
return ACPUtils.deserialize(serialized);
|
|
2580
2561
|
};
|
|
2581
|
-
var
|
|
2582
|
-
return
|
|
2562
|
+
var getACP2 = (chainId, account, hash) => {
|
|
2563
|
+
return acpStore.getACP(chainId, account, hash);
|
|
2583
2564
|
};
|
|
2584
|
-
var
|
|
2585
|
-
return
|
|
2565
|
+
var getACPs2 = (chainId, account) => {
|
|
2566
|
+
return acpStore.getACPs(chainId, account);
|
|
2586
2567
|
};
|
|
2587
|
-
var
|
|
2588
|
-
return
|
|
2568
|
+
var getActiveACP2 = (chainId, account) => {
|
|
2569
|
+
return acpStore.getActiveACP(chainId, account);
|
|
2589
2570
|
};
|
|
2590
|
-
var
|
|
2591
|
-
return
|
|
2571
|
+
var getActiveACPHash2 = (chainId, account) => {
|
|
2572
|
+
return acpStore.getActiveACPHash(chainId, account);
|
|
2592
2573
|
};
|
|
2593
|
-
var
|
|
2594
|
-
|
|
2574
|
+
var selectActiveACP = (chainId, account, hash) => {
|
|
2575
|
+
acpStore.setActiveACPHash(chainId, account, hash);
|
|
2595
2576
|
};
|
|
2596
|
-
var
|
|
2577
|
+
var getOrCreateSelfACP = async (publicClient, walletClient, chainId, account, options) => {
|
|
2597
2578
|
const _chainId = chainId ?? await publicClient.getChainId();
|
|
2598
2579
|
const _account = account ?? walletClient.account.address;
|
|
2599
|
-
const
|
|
2600
|
-
if (
|
|
2601
|
-
return
|
|
2580
|
+
const activeACP = await getActiveACP2(_chainId, _account);
|
|
2581
|
+
if (activeACP && activeACP.type === "self" && ACPUtils.isValid(activeACP).valid) {
|
|
2582
|
+
return activeACP;
|
|
2602
2583
|
}
|
|
2603
|
-
return createSelf(options ?? { issuer: _account, name: "Autogenerated Self
|
|
2584
|
+
return createSelf(options ?? { issuer: _account, name: "Autogenerated Self ACP" }, publicClient, walletClient);
|
|
2604
2585
|
};
|
|
2605
|
-
var
|
|
2586
|
+
var getOrCreateSharingACP = async (publicClient, walletClient, options, chainId, account) => {
|
|
2606
2587
|
const _chainId = chainId ?? await publicClient.getChainId();
|
|
2607
2588
|
const _account = account ?? walletClient.account.address;
|
|
2608
|
-
const
|
|
2609
|
-
if (
|
|
2610
|
-
return
|
|
2589
|
+
const activeACP = await getActiveACP2(_chainId, _account);
|
|
2590
|
+
if (activeACP && activeACP.type === "sharing" && ACPUtils.isValid(activeACP).valid) {
|
|
2591
|
+
return activeACP;
|
|
2611
2592
|
}
|
|
2612
2593
|
return createSharing(options, publicClient, walletClient);
|
|
2613
2594
|
};
|
|
2614
|
-
var
|
|
2615
|
-
|
|
2616
|
-
|
|
2617
|
-
|
|
2618
|
-
|
|
2595
|
+
var applyACPDefaults = (options, acpConfig, chainId) => {
|
|
2596
|
+
const result = { ...options };
|
|
2597
|
+
const defaultRevoker = acpConfig?.defaultRevoker?.[chainId];
|
|
2598
|
+
const hasValidatorOptions = options.revokerData != null || options.revokerContract != null;
|
|
2599
|
+
if (defaultRevoker != null && !hasValidatorOptions) {
|
|
2600
|
+
result.revokerContract = defaultRevoker;
|
|
2601
|
+
result.revokerData = Math.round(Date.now() / 1e3) - 60;
|
|
2602
|
+
}
|
|
2603
|
+
const defaultContracts = acpConfig?.defaultContractScopes?.[chainId];
|
|
2604
|
+
const hasScopeOptions = options.scope != null || options.contracts != null || options.handles != null;
|
|
2605
|
+
if (defaultContracts != null && defaultContracts.length > 0 && !hasScopeOptions) {
|
|
2606
|
+
result.contracts = defaultContracts;
|
|
2607
|
+
}
|
|
2608
|
+
return result;
|
|
2609
|
+
};
|
|
2610
|
+
var ACL_SERVED_ADDRESSES_ABI = viem.parseAbi([
|
|
2611
|
+
"function acl() view returns (address)",
|
|
2612
|
+
"function defaultRevokerContract() view returns (address)",
|
|
2613
|
+
"function shareRegistry() view returns (address)"
|
|
2614
|
+
]);
|
|
2615
|
+
var aclServedAddressesCache = /* @__PURE__ */ new Map();
|
|
2616
|
+
var clearAclServedAddresses = () => aclServedAddressesCache.clear();
|
|
2617
|
+
var getAclServedAddresses = async (publicClient, chainId) => {
|
|
2618
|
+
const cached = aclServedAddressesCache.get(chainId);
|
|
2619
|
+
if (cached != null)
|
|
2620
|
+
return cached;
|
|
2621
|
+
let aclAddress;
|
|
2622
|
+
try {
|
|
2623
|
+
aclAddress = await publicClient.readContract({
|
|
2624
|
+
address: TASK_MANAGER_ADDRESS,
|
|
2625
|
+
abi: ACL_SERVED_ADDRESSES_ABI,
|
|
2626
|
+
functionName: "acl"
|
|
2627
|
+
});
|
|
2628
|
+
} catch {
|
|
2629
|
+
return {};
|
|
2630
|
+
}
|
|
2631
|
+
const [defaultRevoker, shareRegistry] = await Promise.all([
|
|
2632
|
+
publicClient.readContract({ address: aclAddress, abi: ACL_SERVED_ADDRESSES_ABI, functionName: "defaultRevokerContract" }).catch(() => void 0),
|
|
2633
|
+
publicClient.readContract({ address: aclAddress, abi: ACL_SERVED_ADDRESSES_ABI, functionName: "shareRegistry" }).catch(() => void 0)
|
|
2634
|
+
]);
|
|
2635
|
+
const resolved = {
|
|
2636
|
+
defaultRevoker: defaultRevoker != null && defaultRevoker !== viem.zeroAddress ? defaultRevoker : void 0,
|
|
2637
|
+
shareRegistry: shareRegistry != null && shareRegistry !== viem.zeroAddress ? shareRegistry : void 0
|
|
2638
|
+
};
|
|
2639
|
+
aclServedAddressesCache.set(chainId, resolved);
|
|
2640
|
+
return resolved;
|
|
2641
|
+
};
|
|
2642
|
+
var applyACPDefaultsFromChain = async (options, acpConfig, publicClient, chainId) => {
|
|
2643
|
+
const hasExplicitRevoker = acpConfig?.defaultRevoker?.[chainId] != null || options.revokerData != null || options.revokerContract != null;
|
|
2644
|
+
if (hasExplicitRevoker)
|
|
2645
|
+
return applyACPDefaults(options, acpConfig, chainId);
|
|
2646
|
+
const served = await getAclServedAddresses(publicClient, chainId);
|
|
2647
|
+
const effectiveConfig = served.defaultRevoker != null ? { ...acpConfig, defaultRevoker: { ...acpConfig?.defaultRevoker, [chainId]: served.defaultRevoker } } : acpConfig;
|
|
2648
|
+
return applyACPDefaults(options, effectiveConfig, chainId);
|
|
2649
|
+
};
|
|
2650
|
+
var revokeACP = async (acp, walletClient) => {
|
|
2651
|
+
if (acp.revokerContract === viem.zeroAddress || acp.revokerData === 0) {
|
|
2652
|
+
throw new Error("ACP is not revocable: it has no revoker (revokerContract/revokerData unset)");
|
|
2653
|
+
}
|
|
2654
|
+
if (walletClient.account == null)
|
|
2655
|
+
throw new Error("Missing walletClient account");
|
|
2656
|
+
if (walletClient.account.address.toLowerCase() !== acp.issuer.toLowerCase()) {
|
|
2657
|
+
throw new Error("Only the acp issuer can revoke it");
|
|
2658
|
+
}
|
|
2659
|
+
return walletClient.writeContract({
|
|
2660
|
+
address: acp.revokerContract,
|
|
2661
|
+
abi: ACP_VALIDATOR_ABI,
|
|
2662
|
+
functionName: "revokeSingle",
|
|
2663
|
+
args: [BigInt(acp.revokerData)],
|
|
2664
|
+
account: walletClient.account,
|
|
2665
|
+
chain: walletClient.chain
|
|
2666
|
+
});
|
|
2667
|
+
};
|
|
2668
|
+
var revokeAllACPs = async (walletClient, publicClient, revokerContract) => {
|
|
2669
|
+
if (walletClient.account == null)
|
|
2670
|
+
throw new Error("Missing walletClient account");
|
|
2671
|
+
let revoker = revokerContract;
|
|
2672
|
+
if (revoker == null) {
|
|
2673
|
+
const chainId = await publicClient.getChainId();
|
|
2674
|
+
const active = getActiveACP2(chainId, walletClient.account.address);
|
|
2675
|
+
revoker = active?.revokerContract;
|
|
2676
|
+
}
|
|
2677
|
+
if (revoker == null || revoker === viem.zeroAddress) {
|
|
2678
|
+
throw new Error("No revoker contract: pass `revokerContract` or activate a revocable acp first");
|
|
2679
|
+
}
|
|
2680
|
+
return walletClient.writeContract({
|
|
2681
|
+
address: revoker,
|
|
2682
|
+
abi: ACP_VALIDATOR_ABI,
|
|
2683
|
+
functionName: "revokeAllExisting",
|
|
2684
|
+
args: [],
|
|
2685
|
+
account: walletClient.account,
|
|
2686
|
+
chain: walletClient.chain
|
|
2687
|
+
});
|
|
2688
|
+
};
|
|
2689
|
+
var isACPRevoked = async (acp, publicClient) => {
|
|
2690
|
+
if (acp.revokerContract === viem.zeroAddress || acp.revokerData === 0)
|
|
2691
|
+
return false;
|
|
2692
|
+
return publicClient.readContract({
|
|
2693
|
+
address: acp.revokerContract,
|
|
2694
|
+
abi: ACP_VALIDATOR_ABI,
|
|
2695
|
+
functionName: "disabled",
|
|
2696
|
+
args: [acp.issuer, BigInt(acp.revokerData)]
|
|
2697
|
+
});
|
|
2698
|
+
};
|
|
2699
|
+
var ACP_SHARE_REGISTRY_ABI = viem.parseAbi([
|
|
2700
|
+
"struct ACP { address issuer; uint64 expiration; address recipient; uint256 revokerData; address revokerContract; uint8 scope; address[] contracts; bytes32[] handles; bytes32 sealingKey; bytes issuerSignature; bytes recipientSignature; }",
|
|
2701
|
+
"function share(ACP calldata acp) external returns (bytes32)",
|
|
2702
|
+
"function removeShare(bytes32 shareId) external",
|
|
2703
|
+
"function sharesFor(address recipient) external view returns (ACP[] memory)",
|
|
2704
|
+
"function getShare(bytes32 shareId) external view returns (ACP memory)",
|
|
2705
|
+
"function isShareValid(bytes32 shareId) external view returns (bool)"
|
|
2706
|
+
]);
|
|
2707
|
+
var ACP_TUPLE = [
|
|
2708
|
+
{
|
|
2709
|
+
type: "tuple",
|
|
2710
|
+
components: [
|
|
2711
|
+
{ name: "issuer", type: "address" },
|
|
2712
|
+
{ name: "expiration", type: "uint64" },
|
|
2713
|
+
{ name: "recipient", type: "address" },
|
|
2714
|
+
{ name: "revokerData", type: "uint256" },
|
|
2715
|
+
{ name: "revokerContract", type: "address" },
|
|
2716
|
+
{ name: "scope", type: "uint8" },
|
|
2717
|
+
{ name: "contracts", type: "address[]" },
|
|
2718
|
+
{ name: "handles", type: "bytes32[]" },
|
|
2719
|
+
{ name: "sealingKey", type: "bytes32" },
|
|
2720
|
+
{ name: "issuerSignature", type: "bytes" },
|
|
2721
|
+
{ name: "recipientSignature", type: "bytes" }
|
|
2722
|
+
]
|
|
2723
|
+
}
|
|
2724
|
+
];
|
|
2725
|
+
var ZERO_BYTES32 = `0x${"0".repeat(64)}`;
|
|
2726
|
+
var toChainShare = (acp) => {
|
|
2727
|
+
const pub = ACPUtils.getPublic(acp, true);
|
|
2728
|
+
return {
|
|
2729
|
+
...pub,
|
|
2730
|
+
expiration: BigInt(pub.expiration),
|
|
2731
|
+
revokerData: BigInt(pub.revokerData),
|
|
2732
|
+
sealingKey: ZERO_BYTES32,
|
|
2733
|
+
recipientSignature: "0x"
|
|
2734
|
+
};
|
|
2735
|
+
};
|
|
2736
|
+
var computeShareId = (acp) => {
|
|
2737
|
+
const p = toChainShare(acp);
|
|
2738
|
+
return viem.keccak256(viem.encodeAbiParameters(ACP_TUPLE, [p]));
|
|
2739
|
+
};
|
|
2740
|
+
var shareOnChain = async (acp, walletClient, registry) => {
|
|
2741
|
+
if (acp.type !== "sharing") {
|
|
2742
|
+
throw new Error(`Cannot share a '${acp.type}' ACP on-chain \u2014 only 'sharing' ACPs are shareable.`);
|
|
2743
|
+
}
|
|
2744
|
+
if (acp.issuerSignature === "0x") {
|
|
2745
|
+
throw new Error("Cannot share an unsigned sharing ACP \u2014 sign it first.");
|
|
2746
|
+
}
|
|
2747
|
+
if (walletClient.account == null)
|
|
2748
|
+
throw new Error("Missing walletClient account");
|
|
2749
|
+
if (walletClient.account.address.toLowerCase() !== acp.issuer.toLowerCase()) {
|
|
2750
|
+
throw new Error("Only the ACP issuer can share it on-chain");
|
|
2751
|
+
}
|
|
2752
|
+
const txHash = await walletClient.writeContract({
|
|
2753
|
+
address: registry,
|
|
2754
|
+
abi: ACP_SHARE_REGISTRY_ABI,
|
|
2755
|
+
functionName: "share",
|
|
2756
|
+
args: [toChainShare(acp)],
|
|
2757
|
+
account: walletClient.account,
|
|
2758
|
+
chain: walletClient.chain ?? null
|
|
2759
|
+
});
|
|
2760
|
+
return { txHash, shareId: computeShareId(acp) };
|
|
2761
|
+
};
|
|
2762
|
+
var getIncomingShares = async (publicClient, registry, recipient) => {
|
|
2763
|
+
const raw = await publicClient.readContract({
|
|
2764
|
+
address: registry,
|
|
2765
|
+
abi: ACP_SHARE_REGISTRY_ABI,
|
|
2766
|
+
functionName: "sharesFor",
|
|
2767
|
+
args: [recipient]
|
|
2768
|
+
});
|
|
2769
|
+
return raw.map((s) => ({
|
|
2770
|
+
shareId: viem.keccak256(viem.encodeAbiParameters(ACP_TUPLE, [s])),
|
|
2771
|
+
issuer: s.issuer,
|
|
2772
|
+
expiration: Number(s.expiration),
|
|
2773
|
+
recipient: s.recipient,
|
|
2774
|
+
revokerData: Number(s.revokerData),
|
|
2775
|
+
revokerContract: s.revokerContract,
|
|
2776
|
+
scope: Number(s.scope),
|
|
2777
|
+
contracts: [...s.contracts],
|
|
2778
|
+
handles: [...s.handles],
|
|
2779
|
+
issuerSignature: s.issuerSignature
|
|
2780
|
+
}));
|
|
2781
|
+
};
|
|
2782
|
+
var importFromChain = async (share, publicClient, walletClient) => {
|
|
2783
|
+
const { shareId: _shareId, ...options } = share;
|
|
2784
|
+
return importShared({ ...options, type: "sharing" }, publicClient, walletClient);
|
|
2785
|
+
};
|
|
2786
|
+
var removeShareOnChain = async (shareId, walletClient, registry) => {
|
|
2787
|
+
if (walletClient.account == null)
|
|
2788
|
+
throw new Error("Missing walletClient account");
|
|
2789
|
+
return walletClient.writeContract({
|
|
2790
|
+
address: registry,
|
|
2791
|
+
abi: ACP_SHARE_REGISTRY_ABI,
|
|
2792
|
+
functionName: "removeShare",
|
|
2793
|
+
args: [shareId],
|
|
2794
|
+
account: walletClient.account,
|
|
2795
|
+
chain: walletClient.chain ?? null
|
|
2796
|
+
});
|
|
2797
|
+
};
|
|
2798
|
+
var removeACP2 = async (chainId, account, hash) => acpStore.removeACP(chainId, account, hash);
|
|
2799
|
+
var removeActiveACP = async (chainId, account) => acpStore.removeActiveACPHash(chainId, account);
|
|
2800
|
+
var acps = {
|
|
2801
|
+
getSnapshot: acpStore.store.getState,
|
|
2802
|
+
subscribe: acpStore.store.subscribe,
|
|
2619
2803
|
createSelf,
|
|
2620
2804
|
createSharing,
|
|
2621
2805
|
importShared,
|
|
2622
|
-
|
|
2623
|
-
|
|
2806
|
+
getOrCreateSelfACP,
|
|
2807
|
+
getOrCreateSharingACP,
|
|
2624
2808
|
getHash,
|
|
2625
2809
|
export: exportShared,
|
|
2626
2810
|
serialize,
|
|
2627
2811
|
deserialize,
|
|
2628
|
-
|
|
2629
|
-
|
|
2630
|
-
|
|
2631
|
-
|
|
2632
|
-
|
|
2633
|
-
|
|
2634
|
-
|
|
2812
|
+
getACP: getACP2,
|
|
2813
|
+
getACPs: getACPs2,
|
|
2814
|
+
getActiveACP: getActiveACP2,
|
|
2815
|
+
getActiveACPHash: getActiveACPHash2,
|
|
2816
|
+
removeACP: removeACP2,
|
|
2817
|
+
selectActiveACP,
|
|
2818
|
+
removeActiveACP,
|
|
2819
|
+
revokeACP,
|
|
2820
|
+
revokeAllACPs,
|
|
2821
|
+
isACPRevoked,
|
|
2822
|
+
shareOnChain,
|
|
2823
|
+
getIncomingShares,
|
|
2824
|
+
importFromChain,
|
|
2825
|
+
removeShareOnChain,
|
|
2826
|
+
computeShareId,
|
|
2827
|
+
applyACPDefaults,
|
|
2828
|
+
applyACPDefaultsFromChain,
|
|
2829
|
+
getAclServedAddresses,
|
|
2830
|
+
clearAclServedAddresses
|
|
2635
2831
|
};
|
|
2636
2832
|
function uint160ToAddress(uint160) {
|
|
2637
2833
|
const hexStr = uint160.toString(16).padStart(40, "0");
|
|
@@ -2694,13 +2890,16 @@ var MockThresholdNetworkAbi = [
|
|
|
2694
2890
|
{
|
|
2695
2891
|
name: "permission",
|
|
2696
2892
|
type: "tuple",
|
|
2697
|
-
internalType: "struct
|
|
2893
|
+
internalType: "struct ACPermission",
|
|
2698
2894
|
components: [
|
|
2699
2895
|
{ name: "issuer", type: "address", internalType: "address" },
|
|
2700
2896
|
{ name: "expiration", type: "uint64", internalType: "uint64" },
|
|
2701
2897
|
{ name: "recipient", type: "address", internalType: "address" },
|
|
2702
|
-
{ name: "
|
|
2703
|
-
{ name: "
|
|
2898
|
+
{ name: "revokerData", type: "uint256", internalType: "uint256" },
|
|
2899
|
+
{ name: "revokerContract", type: "address", internalType: "address" },
|
|
2900
|
+
{ name: "scope", type: "uint8", internalType: "uint8" },
|
|
2901
|
+
{ name: "contracts", type: "address[]", internalType: "address[]" },
|
|
2902
|
+
{ name: "handles", type: "bytes32[]", internalType: "bytes32[]" },
|
|
2704
2903
|
{ name: "sealingKey", type: "bytes32", internalType: "bytes32" },
|
|
2705
2904
|
{ name: "issuerSignature", type: "bytes", internalType: "bytes" },
|
|
2706
2905
|
{ name: "recipientSignature", type: "bytes", internalType: "bytes" }
|
|
@@ -2723,13 +2922,16 @@ var MockThresholdNetworkAbi = [
|
|
|
2723
2922
|
{
|
|
2724
2923
|
name: "permission",
|
|
2725
2924
|
type: "tuple",
|
|
2726
|
-
internalType: "struct
|
|
2925
|
+
internalType: "struct ACPermission",
|
|
2727
2926
|
components: [
|
|
2728
2927
|
{ name: "issuer", type: "address", internalType: "address" },
|
|
2729
2928
|
{ name: "expiration", type: "uint64", internalType: "uint64" },
|
|
2730
2929
|
{ name: "recipient", type: "address", internalType: "address" },
|
|
2731
|
-
{ name: "
|
|
2732
|
-
{ name: "
|
|
2930
|
+
{ name: "revokerData", type: "uint256", internalType: "uint256" },
|
|
2931
|
+
{ name: "revokerContract", type: "address", internalType: "address" },
|
|
2932
|
+
{ name: "scope", type: "uint8", internalType: "uint8" },
|
|
2933
|
+
{ name: "contracts", type: "address[]", internalType: "address[]" },
|
|
2934
|
+
{ name: "handles", type: "bytes32[]", internalType: "bytes32[]" },
|
|
2733
2935
|
{ name: "sealingKey", type: "bytes32", internalType: "bytes32" },
|
|
2734
2936
|
{ name: "issuerSignature", type: "bytes", internalType: "bytes" },
|
|
2735
2937
|
{ name: "recipientSignature", type: "bytes", internalType: "bytes" }
|
|
@@ -2794,19 +2996,22 @@ var MockThresholdNetworkAbi = [
|
|
|
2794
2996
|
},
|
|
2795
2997
|
{
|
|
2796
2998
|
type: "function",
|
|
2797
|
-
name: "
|
|
2999
|
+
name: "decryptForTxWithACP",
|
|
2798
3000
|
inputs: [
|
|
2799
3001
|
{ name: "ctHash", type: "uint256", internalType: "uint256" },
|
|
2800
3002
|
{
|
|
2801
3003
|
name: "permission",
|
|
2802
3004
|
type: "tuple",
|
|
2803
|
-
internalType: "struct
|
|
3005
|
+
internalType: "struct ACPermission",
|
|
2804
3006
|
components: [
|
|
2805
3007
|
{ name: "issuer", type: "address", internalType: "address" },
|
|
2806
3008
|
{ name: "expiration", type: "uint64", internalType: "uint64" },
|
|
2807
3009
|
{ name: "recipient", type: "address", internalType: "address" },
|
|
2808
|
-
{ name: "
|
|
2809
|
-
{ name: "
|
|
3010
|
+
{ name: "revokerData", type: "uint256", internalType: "uint256" },
|
|
3011
|
+
{ name: "revokerContract", type: "address", internalType: "address" },
|
|
3012
|
+
{ name: "scope", type: "uint8", internalType: "uint8" },
|
|
3013
|
+
{ name: "contracts", type: "address[]", internalType: "address[]" },
|
|
3014
|
+
{ name: "handles", type: "bytes32[]", internalType: "bytes32[]" },
|
|
2810
3015
|
{ name: "sealingKey", type: "bytes32", internalType: "bytes32" },
|
|
2811
3016
|
{ name: "issuerSignature", type: "bytes", internalType: "bytes" },
|
|
2812
3017
|
{ name: "recipientSignature", type: "bytes", internalType: "bytes" }
|
|
@@ -2822,7 +3027,7 @@ var MockThresholdNetworkAbi = [
|
|
|
2822
3027
|
},
|
|
2823
3028
|
{
|
|
2824
3029
|
type: "function",
|
|
2825
|
-
name: "
|
|
3030
|
+
name: "decryptForTxWithoutACP",
|
|
2826
3031
|
inputs: [{ name: "ctHash", type: "uint256", internalType: "uint256" }],
|
|
2827
3032
|
outputs: [
|
|
2828
3033
|
{ name: "allowed", type: "bool", internalType: "bool" },
|
|
@@ -2834,12 +3039,12 @@ var MockThresholdNetworkAbi = [
|
|
|
2834
3039
|
];
|
|
2835
3040
|
|
|
2836
3041
|
// core/decrypt/cofheMocksDecryptForView.ts
|
|
2837
|
-
async function cofheMocksDecryptForView(ctHash, utype,
|
|
2838
|
-
const
|
|
3042
|
+
async function cofheMocksDecryptForView(ctHash, utype, acp, publicClient) {
|
|
3043
|
+
const wireAcp = ACPUtils.getPublic(acp, true);
|
|
2839
3044
|
const permissionWithBigInts = {
|
|
2840
|
-
...
|
|
2841
|
-
expiration: BigInt(
|
|
2842
|
-
|
|
3045
|
+
...wireAcp,
|
|
3046
|
+
expiration: BigInt(wireAcp.expiration),
|
|
3047
|
+
revokerData: BigInt(wireAcp.revokerData)
|
|
2843
3048
|
};
|
|
2844
3049
|
const [allowed, error, result] = await publicClient.readContract({
|
|
2845
3050
|
address: MOCKS_THRESHOLD_NETWORK_ADDRESS,
|
|
@@ -2860,11 +3065,34 @@ async function cofheMocksDecryptForView(ctHash, utype, permit, publicClient) {
|
|
|
2860
3065
|
});
|
|
2861
3066
|
}
|
|
2862
3067
|
const sealedBigInt = BigInt(result);
|
|
2863
|
-
const sealingKeyBigInt = BigInt(
|
|
3068
|
+
const sealingKeyBigInt = BigInt(acp.sealingKey);
|
|
2864
3069
|
const unsealed = sealedBigInt ^ sealingKeyBigInt;
|
|
2865
3070
|
return unsealed;
|
|
2866
3071
|
}
|
|
2867
3072
|
|
|
3073
|
+
// core/debug.ts
|
|
3074
|
+
var interceptors = {};
|
|
3075
|
+
async function cofheFetch(url, init, ctx = { op: "request" }) {
|
|
3076
|
+
let finalUrl = url;
|
|
3077
|
+
let finalInit = init;
|
|
3078
|
+
if (interceptors.onRequest) {
|
|
3079
|
+
const override = await interceptors.onRequest(finalUrl, finalInit, ctx);
|
|
3080
|
+
if (override) {
|
|
3081
|
+
if (override.url !== void 0)
|
|
3082
|
+
finalUrl = override.url;
|
|
3083
|
+
if (override.init !== void 0)
|
|
3084
|
+
finalInit = override.init;
|
|
3085
|
+
}
|
|
3086
|
+
}
|
|
3087
|
+
let response = await fetch(finalUrl, finalInit);
|
|
3088
|
+
if (interceptors.onResponse) {
|
|
3089
|
+
const replaced = await interceptors.onResponse(response, ctx);
|
|
3090
|
+
if (replaced)
|
|
3091
|
+
response = replaced;
|
|
3092
|
+
}
|
|
3093
|
+
return response;
|
|
3094
|
+
}
|
|
3095
|
+
|
|
2868
3096
|
// core/decrypt/polling.ts
|
|
2869
3097
|
function computeMinuteRampPollIntervalMs(elapsedMs, params) {
|
|
2870
3098
|
const elapsedSeconds = Math.floor(elapsedMs / 1e3);
|
|
@@ -2873,11 +3101,61 @@ function computeMinuteRampPollIntervalMs(elapsedMs, params) {
|
|
|
2873
3101
|
return Math.min(params.maxIntervalMs, Math.max(params.minIntervalMs, intervalMs));
|
|
2874
3102
|
}
|
|
2875
3103
|
|
|
3104
|
+
// core/decrypt/apiError.ts
|
|
3105
|
+
var BACKEND_ERROR_CODE_TO_COFHE_ERROR_CODE = {
|
|
3106
|
+
bad_request: "BAD_REQUEST" /* BadRequest */,
|
|
3107
|
+
unknown_chain: "UNKNOWN_CHAIN" /* UnknownChain */,
|
|
3108
|
+
acp_malformed: "ACP_MALFORMED" /* ACPMalformed */,
|
|
3109
|
+
acp_denied: "ACP_DENIED" /* ACPDenied */,
|
|
3110
|
+
// ACP-era backends fold revoked into denied
|
|
3111
|
+
acp_expired: "ACP_EXPIRED" /* ACPExpired */,
|
|
3112
|
+
acp_invalid: "ACP_INVALID" /* ACPInvalid */,
|
|
3113
|
+
acp_required: "ACP_REQUIRED" /* ACPRequired */,
|
|
3114
|
+
acp_verifier_error: "ACP_VERIFIER_ERROR" /* ACPVerifierError */,
|
|
3115
|
+
acp_verifier_timeout: "ACP_VERIFIER_TIMEOUT" /* ACPVerifierTimeout */,
|
|
3116
|
+
not_publicly_allowed: "NOT_PUBLICLY_ALLOWED" /* NotPubliclyAllowed */,
|
|
3117
|
+
ct_not_found: "CT_NOT_FOUND" /* CtNotFound */,
|
|
3118
|
+
unsupported_security_zone: "UNSUPPORTED_SECURITY_ZONE" /* UnsupportedSecurityZone */,
|
|
3119
|
+
unsupported_type: "UNSUPPORTED_TYPE" /* UnsupportedType */,
|
|
3120
|
+
internal_error: "INTERNAL_ERROR" /* InternalError */,
|
|
3121
|
+
signing_failed: "SIGNING_FAILED" /* SigningFailed */,
|
|
3122
|
+
ct_source_error: "CT_SOURCE_ERROR" /* CtSourceError */,
|
|
3123
|
+
ct_source_timeout: "CT_SOURCE_TIMEOUT" /* CtSourceTimeout */,
|
|
3124
|
+
seal_failed: "SEAL_FAILED" /* SealFailed */
|
|
3125
|
+
};
|
|
3126
|
+
function isBackendApiErrorCode(value) {
|
|
3127
|
+
return value in BACKEND_ERROR_CODE_TO_COFHE_ERROR_CODE;
|
|
3128
|
+
}
|
|
3129
|
+
async function parseApiErrorResponseBody(response) {
|
|
3130
|
+
let errorMessage = `HTTP ${response.status}`;
|
|
3131
|
+
let apiErrorCode;
|
|
3132
|
+
try {
|
|
3133
|
+
const body = await response.json();
|
|
3134
|
+
if (body && typeof body === "object") {
|
|
3135
|
+
const record = body;
|
|
3136
|
+
if (typeof record.error === "string" && record.error.length > 0) {
|
|
3137
|
+
apiErrorCode = record.error;
|
|
3138
|
+
}
|
|
3139
|
+
if (typeof record.error_message === "string" && record.error_message.length > 0) {
|
|
3140
|
+
errorMessage = record.error_message;
|
|
3141
|
+
} else if (typeof record.message === "string" && record.message.length > 0) {
|
|
3142
|
+
errorMessage = record.message;
|
|
3143
|
+
}
|
|
3144
|
+
}
|
|
3145
|
+
} catch {
|
|
3146
|
+
errorMessage = response.statusText || errorMessage;
|
|
3147
|
+
}
|
|
3148
|
+
return { apiErrorCode, errorMessage };
|
|
3149
|
+
}
|
|
3150
|
+
function mapApiErrorCodeToCofheErrorCode(apiErrorCode, fallback) {
|
|
3151
|
+
if (apiErrorCode && isBackendApiErrorCode(apiErrorCode)) {
|
|
3152
|
+
return BACKEND_ERROR_CODE_TO_COFHE_ERROR_CODE[apiErrorCode];
|
|
3153
|
+
}
|
|
3154
|
+
return fallback;
|
|
3155
|
+
}
|
|
3156
|
+
|
|
2876
3157
|
// core/decrypt/submitRetry.ts
|
|
2877
3158
|
var DEFAULT_404_RETRY_TIMEOUT_MS = 1e4;
|
|
2878
|
-
function isRetryableSubmitStatus(status) {
|
|
2879
|
-
return status === 204 || status === 404;
|
|
2880
|
-
}
|
|
2881
3159
|
function normalize404RetryTimeoutMs(params) {
|
|
2882
3160
|
const { timeoutMs, operationLabel, errorCode } = params;
|
|
2883
3161
|
if (timeoutMs === void 0)
|
|
@@ -2894,32 +3172,24 @@ function normalize404RetryTimeoutMs(params) {
|
|
|
2894
3172
|
return timeoutMs;
|
|
2895
3173
|
}
|
|
2896
3174
|
async function classifySubmitResponse(params) {
|
|
2897
|
-
const { response,
|
|
2898
|
-
if (
|
|
2899
|
-
return { kind: "retryable", status:
|
|
3175
|
+
const { response, fallbackErrorCode } = params;
|
|
3176
|
+
if (response.status === 204) {
|
|
3177
|
+
return { kind: "retryable", status: 204 };
|
|
2900
3178
|
}
|
|
2901
3179
|
if (response.ok) {
|
|
2902
3180
|
return { kind: "parse-json" };
|
|
2903
3181
|
}
|
|
2904
|
-
|
|
2905
|
-
|
|
2906
|
-
|
|
2907
|
-
const maybeErrorMessage = extractErrorMessage?.(errorBody);
|
|
2908
|
-
if (typeof maybeErrorMessage === "string" && maybeErrorMessage.length > 0) {
|
|
2909
|
-
errorMessage = maybeErrorMessage;
|
|
2910
|
-
} else if (errorBody && typeof errorBody === "object") {
|
|
2911
|
-
const defaultMessage = errorBody.error_message;
|
|
2912
|
-
const fallbackMessage = errorBody.message;
|
|
2913
|
-
if (typeof defaultMessage === "string" && defaultMessage.length > 0) {
|
|
2914
|
-
errorMessage = defaultMessage;
|
|
2915
|
-
} else if (typeof fallbackMessage === "string" && fallbackMessage.length > 0) {
|
|
2916
|
-
errorMessage = fallbackMessage;
|
|
2917
|
-
}
|
|
2918
|
-
}
|
|
2919
|
-
} catch {
|
|
2920
|
-
errorMessage = response.statusText || errorMessage;
|
|
3182
|
+
const { apiErrorCode, errorMessage } = await parseApiErrorResponseBody(response);
|
|
3183
|
+
if (response.status === 404) {
|
|
3184
|
+
return { kind: "retryable", status: 404, apiErrorMessage: errorMessage };
|
|
2921
3185
|
}
|
|
2922
|
-
return {
|
|
3186
|
+
return {
|
|
3187
|
+
kind: "fatal-http",
|
|
3188
|
+
status: response.status,
|
|
3189
|
+
cofheErrorCode: mapApiErrorCodeToCofheErrorCode(apiErrorCode, fallbackErrorCode),
|
|
3190
|
+
apiErrorCode,
|
|
3191
|
+
errorMessage
|
|
3192
|
+
};
|
|
2923
3193
|
}
|
|
2924
3194
|
function throwIfSubmitRetryTimedOut(params) {
|
|
2925
3195
|
const {
|
|
@@ -2931,12 +3201,14 @@ function throwIfSubmitRetryTimedOut(params) {
|
|
|
2931
3201
|
overallTimeoutMs,
|
|
2932
3202
|
thresholdNetworkUrl,
|
|
2933
3203
|
body,
|
|
2934
|
-
attemptIndex
|
|
3204
|
+
attemptIndex,
|
|
3205
|
+
lastKnownErrorMessage
|
|
2935
3206
|
} = params;
|
|
2936
3207
|
if (status === 404 && elapsedMs > retry404TimeoutMs) {
|
|
2937
3208
|
throw new CofheError({
|
|
2938
|
-
code:
|
|
2939
|
-
|
|
3209
|
+
code: "CT_NOT_FOUND" /* CtNotFound */,
|
|
3210
|
+
apiErrorCode: "ct_not_found",
|
|
3211
|
+
message: `${operationLabel} ciphertext not found after retrying for ${retry404TimeoutMs}ms` + (lastKnownErrorMessage ? `: ${lastKnownErrorMessage}` : ""),
|
|
2940
3212
|
hint: "The ciphertext may not be indexed yet. Increase set404RetryTimeout(...) if the backend is slow to index ciphertexts.",
|
|
2941
3213
|
context: {
|
|
2942
3214
|
thresholdNetworkUrl,
|
|
@@ -2962,6 +3234,62 @@ function throwIfSubmitRetryTimedOut(params) {
|
|
|
2962
3234
|
});
|
|
2963
3235
|
}
|
|
2964
3236
|
}
|
|
3237
|
+
function normalizeTnSignature(signature) {
|
|
3238
|
+
if (typeof signature !== "string") {
|
|
3239
|
+
throw new CofheError({
|
|
3240
|
+
code: "DECRYPT_RETURNED_NULL" /* DecryptReturnedNull */,
|
|
3241
|
+
message: "decrypt response missing signature",
|
|
3242
|
+
context: {
|
|
3243
|
+
signature
|
|
3244
|
+
}
|
|
3245
|
+
});
|
|
3246
|
+
}
|
|
3247
|
+
const trimmed = signature.trim();
|
|
3248
|
+
if (trimmed.length === 0) {
|
|
3249
|
+
throw new CofheError({
|
|
3250
|
+
code: "DECRYPT_RETURNED_NULL" /* DecryptReturnedNull */,
|
|
3251
|
+
message: "decrypt response returned empty signature"
|
|
3252
|
+
});
|
|
3253
|
+
}
|
|
3254
|
+
const prefixed = trimmed.startsWith("0x") ? trimmed : `0x${trimmed}`;
|
|
3255
|
+
const parsed = viem.parseSignature(prefixed);
|
|
3256
|
+
return viem.serializeSignature(parsed);
|
|
3257
|
+
}
|
|
3258
|
+
function parseDecryptedBytesToBigInt(decrypted) {
|
|
3259
|
+
if (!Array.isArray(decrypted)) {
|
|
3260
|
+
throw new CofheError({
|
|
3261
|
+
code: "DECRYPT_RETURNED_NULL" /* DecryptReturnedNull */,
|
|
3262
|
+
message: "decrypt response field <decrypted> must be a byte array",
|
|
3263
|
+
context: {
|
|
3264
|
+
decrypted
|
|
3265
|
+
}
|
|
3266
|
+
});
|
|
3267
|
+
}
|
|
3268
|
+
if (decrypted.length === 0) {
|
|
3269
|
+
throw new CofheError({
|
|
3270
|
+
code: "DECRYPT_RETURNED_NULL" /* DecryptReturnedNull */,
|
|
3271
|
+
message: "decrypt response field <decrypted> was an empty byte array",
|
|
3272
|
+
context: {
|
|
3273
|
+
decrypted
|
|
3274
|
+
}
|
|
3275
|
+
});
|
|
3276
|
+
}
|
|
3277
|
+
let hex = "";
|
|
3278
|
+
for (const b of decrypted) {
|
|
3279
|
+
if (typeof b !== "number" || !Number.isInteger(b) || b < 0 || b > 255) {
|
|
3280
|
+
throw new CofheError({
|
|
3281
|
+
code: "DECRYPT_RETURNED_NULL" /* DecryptReturnedNull */,
|
|
3282
|
+
message: "decrypt response field <decrypted> contained a non-byte value",
|
|
3283
|
+
context: {
|
|
3284
|
+
badElement: b,
|
|
3285
|
+
decrypted
|
|
3286
|
+
}
|
|
3287
|
+
});
|
|
3288
|
+
}
|
|
3289
|
+
hex += b.toString(16).padStart(2, "0");
|
|
3290
|
+
}
|
|
3291
|
+
return BigInt(`0x${hex}`);
|
|
3292
|
+
}
|
|
2965
3293
|
|
|
2966
3294
|
// core/decrypt/tnSealOutputV2.ts
|
|
2967
3295
|
var POLL_INTERVAL_MS = 1e3;
|
|
@@ -3024,23 +3352,28 @@ function parseCompletedSealOutputResponse(params) {
|
|
|
3024
3352
|
}
|
|
3025
3353
|
return convertSealedData(sealed);
|
|
3026
3354
|
}
|
|
3027
|
-
async function submitSealOutputRequest(thresholdNetworkUrl, ctHash, chainId,
|
|
3355
|
+
async function submitSealOutputRequest(thresholdNetworkUrl, ctHash, chainId, acp, overallStartTime, retry404TimeoutMs, onPoll) {
|
|
3028
3356
|
const body = {
|
|
3029
3357
|
ct_tempkey: BigInt(ctHash).toString(16).padStart(64, "0"),
|
|
3030
3358
|
host_chain_id: chainId,
|
|
3031
|
-
|
|
3359
|
+
acp
|
|
3032
3360
|
};
|
|
3033
3361
|
let attemptIndex = 0;
|
|
3362
|
+
let last404ApiErrorMessage;
|
|
3034
3363
|
for (; ; ) {
|
|
3035
3364
|
let response;
|
|
3036
3365
|
try {
|
|
3037
|
-
response = await
|
|
3038
|
-
|
|
3039
|
-
|
|
3040
|
-
|
|
3041
|
-
|
|
3042
|
-
|
|
3043
|
-
|
|
3366
|
+
response = await cofheFetch(
|
|
3367
|
+
`${thresholdNetworkUrl}/v2/sealoutput`,
|
|
3368
|
+
/*op:sealoutput*/
|
|
3369
|
+
{
|
|
3370
|
+
method: "POST",
|
|
3371
|
+
headers: {
|
|
3372
|
+
"Content-Type": "application/json"
|
|
3373
|
+
},
|
|
3374
|
+
body: JSON.stringify(body)
|
|
3375
|
+
}
|
|
3376
|
+
);
|
|
3044
3377
|
} catch (e) {
|
|
3045
3378
|
throw new CofheError({
|
|
3046
3379
|
code: "SEAL_OUTPUT_FAILED" /* SealOutputFailed */,
|
|
@@ -3054,16 +3387,19 @@ async function submitSealOutputRequest(thresholdNetworkUrl, ctHash, chainId, per
|
|
|
3054
3387
|
}
|
|
3055
3388
|
});
|
|
3056
3389
|
}
|
|
3057
|
-
const responseClassification = await classifySubmitResponse({
|
|
3390
|
+
const responseClassification = await classifySubmitResponse({
|
|
3391
|
+
response,
|
|
3392
|
+
fallbackErrorCode: "SEAL_OUTPUT_FAILED" /* SealOutputFailed */
|
|
3393
|
+
});
|
|
3058
3394
|
if (responseClassification.kind === "fatal-http") {
|
|
3059
3395
|
throw new CofheError({
|
|
3060
|
-
code:
|
|
3396
|
+
code: responseClassification.cofheErrorCode,
|
|
3397
|
+
apiErrorCode: responseClassification.apiErrorCode,
|
|
3061
3398
|
message: `sealOutput request failed: ${responseClassification.errorMessage}`,
|
|
3062
3399
|
hint: "Check the threshold network URL and request parameters.",
|
|
3063
3400
|
context: {
|
|
3064
3401
|
thresholdNetworkUrl,
|
|
3065
|
-
status:
|
|
3066
|
-
statusText: response.statusText,
|
|
3402
|
+
status: responseClassification.status,
|
|
3067
3403
|
body,
|
|
3068
3404
|
attemptIndex
|
|
3069
3405
|
}
|
|
@@ -3109,6 +3445,9 @@ async function submitSealOutputRequest(thresholdNetworkUrl, ctHash, chainId, per
|
|
|
3109
3445
|
}
|
|
3110
3446
|
});
|
|
3111
3447
|
}
|
|
3448
|
+
if (responseClassification.status === 404) {
|
|
3449
|
+
last404ApiErrorMessage = responseClassification.apiErrorMessage;
|
|
3450
|
+
}
|
|
3112
3451
|
const elapsedMs = Date.now() - overallStartTime;
|
|
3113
3452
|
throwIfSubmitRetryTimedOut({
|
|
3114
3453
|
operationLabel: "sealOutput",
|
|
@@ -3119,7 +3458,8 @@ async function submitSealOutputRequest(thresholdNetworkUrl, ctHash, chainId, per
|
|
|
3119
3458
|
overallTimeoutMs: SEAL_OUTPUT_TIMEOUT_MS,
|
|
3120
3459
|
thresholdNetworkUrl,
|
|
3121
3460
|
body,
|
|
3122
|
-
attemptIndex
|
|
3461
|
+
attemptIndex,
|
|
3462
|
+
lastKnownErrorMessage: last404ApiErrorMessage
|
|
3123
3463
|
});
|
|
3124
3464
|
onPoll?.({
|
|
3125
3465
|
operation: "sealoutput",
|
|
@@ -3163,12 +3503,16 @@ async function pollSealOutputStatus(thresholdNetworkUrl, requestId, overallStart
|
|
|
3163
3503
|
}
|
|
3164
3504
|
let response;
|
|
3165
3505
|
try {
|
|
3166
|
-
response = await
|
|
3167
|
-
|
|
3168
|
-
|
|
3169
|
-
|
|
3506
|
+
response = await cofheFetch(
|
|
3507
|
+
`${thresholdNetworkUrl}/v2/sealoutput/${requestId}`,
|
|
3508
|
+
/*op:sealoutput-poll*/
|
|
3509
|
+
{
|
|
3510
|
+
method: "GET",
|
|
3511
|
+
headers: {
|
|
3512
|
+
"Content-Type": "application/json"
|
|
3513
|
+
}
|
|
3170
3514
|
}
|
|
3171
|
-
|
|
3515
|
+
);
|
|
3172
3516
|
} catch (e) {
|
|
3173
3517
|
throw new CofheError({
|
|
3174
3518
|
code: "SEAL_OUTPUT_FAILED" /* SealOutputFailed */,
|
|
@@ -3193,15 +3537,10 @@ async function pollSealOutputStatus(thresholdNetworkUrl, requestId, overallStart
|
|
|
3193
3537
|
});
|
|
3194
3538
|
}
|
|
3195
3539
|
if (!response.ok) {
|
|
3196
|
-
|
|
3197
|
-
try {
|
|
3198
|
-
const errorBody = await response.json();
|
|
3199
|
-
errorMessage = errorBody.error_message || errorBody.message || errorMessage;
|
|
3200
|
-
} catch {
|
|
3201
|
-
errorMessage = response.statusText || errorMessage;
|
|
3202
|
-
}
|
|
3540
|
+
const { apiErrorCode, errorMessage } = await parseApiErrorResponseBody(response);
|
|
3203
3541
|
throw new CofheError({
|
|
3204
|
-
code: "SEAL_OUTPUT_FAILED" /* SealOutputFailed
|
|
3542
|
+
code: mapApiErrorCodeToCofheErrorCode(apiErrorCode, "SEAL_OUTPUT_FAILED" /* SealOutputFailed */),
|
|
3543
|
+
apiErrorCode,
|
|
3205
3544
|
message: `sealOutput status poll failed: ${errorMessage}`,
|
|
3206
3545
|
context: {
|
|
3207
3546
|
thresholdNetworkUrl,
|
|
@@ -3245,7 +3584,7 @@ async function pollSealOutputStatus(thresholdNetworkUrl, requestId, overallStart
|
|
|
3245
3584
|
});
|
|
3246
3585
|
}
|
|
3247
3586
|
async function tnSealOutputV2(params) {
|
|
3248
|
-
const { thresholdNetworkUrl, ctHash, chainId,
|
|
3587
|
+
const { thresholdNetworkUrl, ctHash, chainId, acp, retry404TimeoutMs, onPoll } = params;
|
|
3249
3588
|
const normalized404RetryTimeoutMs = normalize404RetryTimeoutMs({
|
|
3250
3589
|
timeoutMs: retry404TimeoutMs,
|
|
3251
3590
|
operationLabel: "sealOutput",
|
|
@@ -3256,7 +3595,7 @@ async function tnSealOutputV2(params) {
|
|
|
3256
3595
|
thresholdNetworkUrl,
|
|
3257
3596
|
ctHash,
|
|
3258
3597
|
chainId,
|
|
3259
|
-
|
|
3598
|
+
acp,
|
|
3260
3599
|
overallStartTime,
|
|
3261
3600
|
normalized404RetryTimeoutMs,
|
|
3262
3601
|
onPoll
|
|
@@ -3272,8 +3611,8 @@ var DEFAULT_404_RETRY_TIMEOUT_MS2 = 1e4;
|
|
|
3272
3611
|
var DecryptForViewBuilder = class extends BaseBuilder {
|
|
3273
3612
|
ctHash;
|
|
3274
3613
|
utype;
|
|
3275
|
-
|
|
3276
|
-
|
|
3614
|
+
acpHash;
|
|
3615
|
+
acp;
|
|
3277
3616
|
pollCallback;
|
|
3278
3617
|
retry404TimeoutMs = DEFAULT_404_RETRY_TIMEOUT_MS2;
|
|
3279
3618
|
constructor(params) {
|
|
@@ -3287,11 +3626,11 @@ var DecryptForViewBuilder = class extends BaseBuilder {
|
|
|
3287
3626
|
});
|
|
3288
3627
|
this.ctHash = params.ctHash;
|
|
3289
3628
|
this.utype = params.utype;
|
|
3290
|
-
this.
|
|
3291
|
-
this.
|
|
3629
|
+
this.acpHash = params.acpHash;
|
|
3630
|
+
this.acp = params.acp;
|
|
3292
3631
|
}
|
|
3293
3632
|
/**
|
|
3294
|
-
* @param chainId - Chain to decrypt values from. Used to fetch the threshold network URL and use the correct
|
|
3633
|
+
* @param chainId - Chain to decrypt values from. Used to fetch the threshold network URL and use the correct acp.
|
|
3295
3634
|
*
|
|
3296
3635
|
* If not provided, the chainId will be fetched from the connected publicClient.
|
|
3297
3636
|
*
|
|
@@ -3312,7 +3651,7 @@ var DecryptForViewBuilder = class extends BaseBuilder {
|
|
|
3312
3651
|
return this.chainId;
|
|
3313
3652
|
}
|
|
3314
3653
|
/**
|
|
3315
|
-
* @param account - Account to decrypt values from. Used to fetch the correct
|
|
3654
|
+
* @param account - Account to decrypt values from. Used to fetch the correct acp.
|
|
3316
3655
|
*
|
|
3317
3656
|
* If not provided, the account will be fetched from the connected walletClient.
|
|
3318
3657
|
*
|
|
@@ -3349,61 +3688,61 @@ var DecryptForViewBuilder = class extends BaseBuilder {
|
|
|
3349
3688
|
this.retry404TimeoutMs = timeoutMs;
|
|
3350
3689
|
return this;
|
|
3351
3690
|
}
|
|
3352
|
-
|
|
3353
|
-
if (typeof
|
|
3354
|
-
this.
|
|
3355
|
-
this.
|
|
3356
|
-
} else if (
|
|
3357
|
-
this.
|
|
3358
|
-
this.
|
|
3691
|
+
withACP(acpOrACPHash) {
|
|
3692
|
+
if (typeof acpOrACPHash === "string") {
|
|
3693
|
+
this.acpHash = acpOrACPHash;
|
|
3694
|
+
this.acp = void 0;
|
|
3695
|
+
} else if (acpOrACPHash === void 0) {
|
|
3696
|
+
this.acpHash = void 0;
|
|
3697
|
+
this.acp = void 0;
|
|
3359
3698
|
} else {
|
|
3360
|
-
this.
|
|
3361
|
-
this.
|
|
3699
|
+
this.acp = acpOrACPHash;
|
|
3700
|
+
this.acpHash = void 0;
|
|
3362
3701
|
}
|
|
3363
3702
|
return this;
|
|
3364
3703
|
}
|
|
3365
3704
|
/**
|
|
3366
|
-
* @param
|
|
3705
|
+
* @param acpHash - ACP hash to decrypt values from. Used to fetch the correct acp.
|
|
3367
3706
|
*
|
|
3368
|
-
* If not provided, the active
|
|
3369
|
-
* If `
|
|
3707
|
+
* If not provided, the active acp for the chainId and account will be used.
|
|
3708
|
+
* If `setACP()` is called, it will be used regardless of chainId, account, or acpHash.
|
|
3370
3709
|
*
|
|
3371
3710
|
* Example:
|
|
3372
3711
|
* ```typescript
|
|
3373
3712
|
* const unsealed = await client.decryptForView(ctHash, utype)
|
|
3374
|
-
* .
|
|
3713
|
+
* .setACPHash('0x1234567890123456789012345678901234567890')
|
|
3375
3714
|
* .execute();
|
|
3376
3715
|
* ```
|
|
3377
3716
|
*
|
|
3378
3717
|
* @returns The chainable DecryptForViewBuilder instance.
|
|
3379
3718
|
*/
|
|
3380
|
-
/** @deprecated Use `
|
|
3381
|
-
|
|
3382
|
-
return this.
|
|
3719
|
+
/** @deprecated Use `withACP(acpHash)` instead. */
|
|
3720
|
+
setACPHash(acpHash) {
|
|
3721
|
+
return this.withACP(acpHash);
|
|
3383
3722
|
}
|
|
3384
|
-
|
|
3385
|
-
return this.
|
|
3723
|
+
getACPHash() {
|
|
3724
|
+
return this.acpHash;
|
|
3386
3725
|
}
|
|
3387
3726
|
/**
|
|
3388
|
-
* @param
|
|
3727
|
+
* @param acp - ACP to decrypt values with. If provided, it will be used regardless of chainId, account, or acpHash.
|
|
3389
3728
|
*
|
|
3390
|
-
* If not provided, the
|
|
3729
|
+
* If not provided, the acp will be determined by chainId, account, and acpHash.
|
|
3391
3730
|
*
|
|
3392
3731
|
* Example:
|
|
3393
3732
|
* ```typescript
|
|
3394
3733
|
* const unsealed = await client.decryptForView(ctHash, utype)
|
|
3395
|
-
* .
|
|
3734
|
+
* .setACP(acp)
|
|
3396
3735
|
* .execute();
|
|
3397
3736
|
* ```
|
|
3398
3737
|
*
|
|
3399
3738
|
* @returns The chainable DecryptForViewBuilder instance.
|
|
3400
3739
|
*/
|
|
3401
|
-
/** @deprecated Use `
|
|
3402
|
-
|
|
3403
|
-
return this.
|
|
3740
|
+
/** @deprecated Use `withACP(acp)` instead. */
|
|
3741
|
+
setACP(acp) {
|
|
3742
|
+
return this.withACP(acp);
|
|
3404
3743
|
}
|
|
3405
|
-
|
|
3406
|
-
return this.
|
|
3744
|
+
getACP() {
|
|
3745
|
+
return this.acp;
|
|
3407
3746
|
}
|
|
3408
3747
|
async getThresholdNetworkUrl() {
|
|
3409
3748
|
this.assertChainId();
|
|
@@ -3419,77 +3758,77 @@ var DecryptForViewBuilder = class extends BaseBuilder {
|
|
|
3419
3758
|
}
|
|
3420
3759
|
});
|
|
3421
3760
|
}
|
|
3422
|
-
async
|
|
3423
|
-
if (this.
|
|
3424
|
-
return this.
|
|
3761
|
+
async getResolvedACP() {
|
|
3762
|
+
if (this.acp)
|
|
3763
|
+
return this.acp;
|
|
3425
3764
|
this.assertChainId();
|
|
3426
3765
|
this.assertAccount();
|
|
3427
|
-
if (this.
|
|
3428
|
-
const
|
|
3429
|
-
if (!
|
|
3766
|
+
if (this.acpHash) {
|
|
3767
|
+
const acp2 = await acps.getACP(this.chainId, this.account, this.acpHash);
|
|
3768
|
+
if (!acp2) {
|
|
3430
3769
|
throw new CofheError({
|
|
3431
|
-
code: "
|
|
3432
|
-
message: `
|
|
3433
|
-
hint: "Ensure the
|
|
3770
|
+
code: "ACP_NOT_FOUND" /* ACPNotFound */,
|
|
3771
|
+
message: `ACP with hash <${this.acpHash}> not found for account <${this.account}> and chainId <${this.chainId}>`,
|
|
3772
|
+
hint: "Ensure the acp exists and is valid.",
|
|
3434
3773
|
context: {
|
|
3435
3774
|
chainId: this.chainId,
|
|
3436
3775
|
account: this.account,
|
|
3437
|
-
|
|
3776
|
+
acpHash: this.acpHash
|
|
3438
3777
|
}
|
|
3439
3778
|
});
|
|
3440
3779
|
}
|
|
3441
|
-
return
|
|
3780
|
+
return acp2;
|
|
3442
3781
|
}
|
|
3443
|
-
const
|
|
3444
|
-
if (!
|
|
3782
|
+
const acp = await acps.getActiveACP(this.chainId, this.account);
|
|
3783
|
+
if (!acp) {
|
|
3445
3784
|
throw new CofheError({
|
|
3446
|
-
code: "
|
|
3447
|
-
message: `Active
|
|
3448
|
-
hint: "Ensure
|
|
3785
|
+
code: "ACP_NOT_FOUND" /* ACPNotFound */,
|
|
3786
|
+
message: `Active acp not found for chainId <${this.chainId}> and account <${this.account}>`,
|
|
3787
|
+
hint: "Ensure an ACP exists for this account on this chain.",
|
|
3449
3788
|
context: {
|
|
3450
3789
|
chainId: this.chainId,
|
|
3451
3790
|
account: this.account
|
|
3452
3791
|
}
|
|
3453
3792
|
});
|
|
3454
3793
|
}
|
|
3455
|
-
return
|
|
3794
|
+
return acp;
|
|
3456
3795
|
}
|
|
3457
3796
|
/**
|
|
3458
3797
|
* On hardhat, interact with MockZkVerifier contract instead of CoFHE
|
|
3459
3798
|
*/
|
|
3460
|
-
async mocksSealOutput(
|
|
3799
|
+
async mocksSealOutput(acp) {
|
|
3461
3800
|
this.assertPublicClient();
|
|
3462
3801
|
const mocksDecryptDelay = this.config.mocks.decryptDelay;
|
|
3463
3802
|
if (mocksDecryptDelay > 0)
|
|
3464
3803
|
await sleep(mocksDecryptDelay);
|
|
3465
|
-
return cofheMocksDecryptForView(this.ctHash, this.utype,
|
|
3804
|
+
return cofheMocksDecryptForView(this.ctHash, this.utype, acp, this.publicClient);
|
|
3466
3805
|
}
|
|
3467
3806
|
/**
|
|
3468
3807
|
* In the production context, perform a true decryption with the CoFHE coprocessor.
|
|
3469
3808
|
*/
|
|
3470
|
-
async productionSealOutput(
|
|
3809
|
+
async productionSealOutput(acp) {
|
|
3471
3810
|
this.assertChainId();
|
|
3472
3811
|
this.assertPublicClient();
|
|
3473
3812
|
const thresholdNetworkUrl = await this.getThresholdNetworkUrl();
|
|
3474
|
-
const
|
|
3813
|
+
const wireAcp = ACPUtils.getPublic(acp, true);
|
|
3475
3814
|
const sealed = await tnSealOutputV2({
|
|
3476
3815
|
ctHash: this.ctHash,
|
|
3477
3816
|
chainId: this.chainId,
|
|
3478
|
-
|
|
3817
|
+
acp: wireAcp,
|
|
3479
3818
|
thresholdNetworkUrl,
|
|
3480
3819
|
retry404TimeoutMs: this.retry404TimeoutMs,
|
|
3481
3820
|
onPoll: this.pollCallback
|
|
3482
3821
|
});
|
|
3483
|
-
return
|
|
3822
|
+
return ACPUtils.unseal(acp, sealed);
|
|
3484
3823
|
}
|
|
3485
3824
|
/**
|
|
3486
3825
|
* Final step of the decryption process. MUST BE CALLED LAST IN THE CHAIN.
|
|
3487
3826
|
*
|
|
3488
3827
|
* This will:
|
|
3489
|
-
* - Use
|
|
3490
|
-
* - Check
|
|
3491
|
-
* - Call CoFHE `/sealoutput` with the
|
|
3492
|
-
* - Unseal the sealed item with the
|
|
3828
|
+
* - Use an ACP based on provided acp OR chainId + account + acpHash
|
|
3829
|
+
* - Check acp validity
|
|
3830
|
+
* - Call CoFHE `/sealoutput` with the acp, which returns a sealed (encrypted) item
|
|
3831
|
+
* - Unseal the sealed item with the acp
|
|
3493
3832
|
* - Return the unsealed item
|
|
3494
3833
|
*
|
|
3495
3834
|
* Example:
|
|
@@ -3497,7 +3836,7 @@ var DecryptForViewBuilder = class extends BaseBuilder {
|
|
|
3497
3836
|
* const unsealed = await client.decryptForView(ctHash, utype)
|
|
3498
3837
|
* .setChainId(11155111) // optional
|
|
3499
3838
|
* .setAccount('0x123...890') // optional
|
|
3500
|
-
* .
|
|
3839
|
+
* .withACP() // optional
|
|
3501
3840
|
* .execute(); // execute
|
|
3502
3841
|
* ```
|
|
3503
3842
|
*
|
|
@@ -3505,14 +3844,14 @@ var DecryptForViewBuilder = class extends BaseBuilder {
|
|
|
3505
3844
|
*/
|
|
3506
3845
|
async execute() {
|
|
3507
3846
|
this.validateUtypeOrThrow();
|
|
3508
|
-
const
|
|
3509
|
-
|
|
3510
|
-
const chainId =
|
|
3847
|
+
const acp = await this.getResolvedACP();
|
|
3848
|
+
ACPUtils.validate(acp);
|
|
3849
|
+
const chainId = acp._signedDomain.chainId;
|
|
3511
3850
|
let unsealed;
|
|
3512
3851
|
if (chainId === hardhat2.id) {
|
|
3513
|
-
unsealed = await this.mocksSealOutput(
|
|
3852
|
+
unsealed = await this.mocksSealOutput(acp);
|
|
3514
3853
|
} else {
|
|
3515
|
-
unsealed = await this.productionSealOutput(
|
|
3854
|
+
unsealed = await this.productionSealOutput(acp);
|
|
3516
3855
|
}
|
|
3517
3856
|
return convertViaUtype(this.utype, unsealed);
|
|
3518
3857
|
}
|
|
@@ -3520,28 +3859,28 @@ var DecryptForViewBuilder = class extends BaseBuilder {
|
|
|
3520
3859
|
var UINT_TYPE_MASK = 0x7fn;
|
|
3521
3860
|
var TYPE_BYTE_OFFSET = 8n;
|
|
3522
3861
|
var getEncryptionTypeFromCtHash = (ctHash) => Number(ctHash >> TYPE_BYTE_OFFSET & UINT_TYPE_MASK);
|
|
3523
|
-
async function cofheMocksDecryptForTx(ctHash, utype,
|
|
3862
|
+
async function cofheMocksDecryptForTx(ctHash, utype, acp, publicClient) {
|
|
3524
3863
|
let allowed;
|
|
3525
3864
|
let error;
|
|
3526
3865
|
let decryptedValue;
|
|
3527
|
-
if (
|
|
3528
|
-
|
|
3866
|
+
if (acp !== null) {
|
|
3867
|
+
const wireAcp = ACPUtils.getPublic(acp, true);
|
|
3529
3868
|
const permissionWithBigInts = {
|
|
3530
|
-
...
|
|
3531
|
-
expiration: BigInt(
|
|
3532
|
-
|
|
3869
|
+
...wireAcp,
|
|
3870
|
+
expiration: BigInt(wireAcp.expiration),
|
|
3871
|
+
revokerData: BigInt(wireAcp.revokerData)
|
|
3533
3872
|
};
|
|
3534
3873
|
[allowed, error, decryptedValue] = await publicClient.readContract({
|
|
3535
3874
|
address: MOCKS_THRESHOLD_NETWORK_ADDRESS,
|
|
3536
3875
|
abi: MockThresholdNetworkAbi,
|
|
3537
|
-
functionName: "
|
|
3876
|
+
functionName: "decryptForTxWithACP",
|
|
3538
3877
|
args: [BigInt(ctHash), permissionWithBigInts]
|
|
3539
3878
|
});
|
|
3540
3879
|
} else {
|
|
3541
3880
|
[allowed, error, decryptedValue] = await publicClient.readContract({
|
|
3542
3881
|
address: MOCKS_THRESHOLD_NETWORK_ADDRESS,
|
|
3543
3882
|
abi: MockThresholdNetworkAbi,
|
|
3544
|
-
functionName: "
|
|
3883
|
+
functionName: "decryptForTxWithoutACP",
|
|
3545
3884
|
args: [BigInt(ctHash)]
|
|
3546
3885
|
});
|
|
3547
3886
|
}
|
|
@@ -3576,62 +3915,6 @@ async function cofheMocksDecryptForTx(ctHash, utype, permit, publicClient) {
|
|
|
3576
3915
|
signature
|
|
3577
3916
|
};
|
|
3578
3917
|
}
|
|
3579
|
-
function normalizeTnSignature(signature) {
|
|
3580
|
-
if (typeof signature !== "string") {
|
|
3581
|
-
throw new CofheError({
|
|
3582
|
-
code: "DECRYPT_RETURNED_NULL" /* DecryptReturnedNull */,
|
|
3583
|
-
message: "decrypt response missing signature",
|
|
3584
|
-
context: {
|
|
3585
|
-
signature
|
|
3586
|
-
}
|
|
3587
|
-
});
|
|
3588
|
-
}
|
|
3589
|
-
const trimmed = signature.trim();
|
|
3590
|
-
if (trimmed.length === 0) {
|
|
3591
|
-
throw new CofheError({
|
|
3592
|
-
code: "DECRYPT_RETURNED_NULL" /* DecryptReturnedNull */,
|
|
3593
|
-
message: "decrypt response returned empty signature"
|
|
3594
|
-
});
|
|
3595
|
-
}
|
|
3596
|
-
const prefixed = trimmed.startsWith("0x") ? trimmed : `0x${trimmed}`;
|
|
3597
|
-
const parsed = viem.parseSignature(prefixed);
|
|
3598
|
-
return viem.serializeSignature(parsed);
|
|
3599
|
-
}
|
|
3600
|
-
function parseDecryptedBytesToBigInt(decrypted) {
|
|
3601
|
-
if (!Array.isArray(decrypted)) {
|
|
3602
|
-
throw new CofheError({
|
|
3603
|
-
code: "DECRYPT_RETURNED_NULL" /* DecryptReturnedNull */,
|
|
3604
|
-
message: "decrypt response field <decrypted> must be a byte array",
|
|
3605
|
-
context: {
|
|
3606
|
-
decrypted
|
|
3607
|
-
}
|
|
3608
|
-
});
|
|
3609
|
-
}
|
|
3610
|
-
if (decrypted.length === 0) {
|
|
3611
|
-
throw new CofheError({
|
|
3612
|
-
code: "DECRYPT_RETURNED_NULL" /* DecryptReturnedNull */,
|
|
3613
|
-
message: "decrypt response field <decrypted> was an empty byte array",
|
|
3614
|
-
context: {
|
|
3615
|
-
decrypted
|
|
3616
|
-
}
|
|
3617
|
-
});
|
|
3618
|
-
}
|
|
3619
|
-
let hex = "";
|
|
3620
|
-
for (const b of decrypted) {
|
|
3621
|
-
if (typeof b !== "number" || !Number.isInteger(b) || b < 0 || b > 255) {
|
|
3622
|
-
throw new CofheError({
|
|
3623
|
-
code: "DECRYPT_RETURNED_NULL" /* DecryptReturnedNull */,
|
|
3624
|
-
message: "decrypt response field <decrypted> contained a non-byte value",
|
|
3625
|
-
context: {
|
|
3626
|
-
badElement: b,
|
|
3627
|
-
decrypted
|
|
3628
|
-
}
|
|
3629
|
-
});
|
|
3630
|
-
}
|
|
3631
|
-
hex += b.toString(16).padStart(2, "0");
|
|
3632
|
-
}
|
|
3633
|
-
return BigInt(`0x${hex}`);
|
|
3634
|
-
}
|
|
3635
3918
|
|
|
3636
3919
|
// core/decrypt/tnDecryptV2.ts
|
|
3637
3920
|
var POLL_INTERVAL_MS2 = 1e3;
|
|
@@ -3753,25 +4036,30 @@ function assertDecryptStatusResponseV2(value) {
|
|
|
3753
4036
|
}
|
|
3754
4037
|
return value;
|
|
3755
4038
|
}
|
|
3756
|
-
async function submitDecryptRequestV2(thresholdNetworkUrl, ctHash, chainId,
|
|
4039
|
+
async function submitDecryptRequestV2(thresholdNetworkUrl, ctHash, chainId, acp, overallStartTime, retry404TimeoutMs, onPoll) {
|
|
3757
4040
|
const body = {
|
|
3758
4041
|
ct_tempkey: BigInt(ctHash).toString(16).padStart(64, "0"),
|
|
3759
4042
|
host_chain_id: chainId
|
|
3760
4043
|
};
|
|
3761
|
-
if (
|
|
3762
|
-
body.
|
|
4044
|
+
if (acp) {
|
|
4045
|
+
body.acp = acp;
|
|
3763
4046
|
}
|
|
3764
4047
|
let attemptIndex = 0;
|
|
4048
|
+
let last404ApiErrorMessage;
|
|
3765
4049
|
for (; ; ) {
|
|
3766
4050
|
let response;
|
|
3767
4051
|
try {
|
|
3768
|
-
response = await
|
|
3769
|
-
|
|
3770
|
-
|
|
3771
|
-
|
|
3772
|
-
|
|
3773
|
-
|
|
3774
|
-
|
|
4052
|
+
response = await cofheFetch(
|
|
4053
|
+
`${thresholdNetworkUrl}/v2/decrypt`,
|
|
4054
|
+
/*op:decrypt*/
|
|
4055
|
+
{
|
|
4056
|
+
method: "POST",
|
|
4057
|
+
headers: {
|
|
4058
|
+
"Content-Type": "application/json"
|
|
4059
|
+
},
|
|
4060
|
+
body: JSON.stringify(body)
|
|
4061
|
+
}
|
|
4062
|
+
);
|
|
3775
4063
|
} catch (e) {
|
|
3776
4064
|
throw new CofheError({
|
|
3777
4065
|
code: "DECRYPT_FAILED" /* DecryptFailed */,
|
|
@@ -3785,16 +4073,19 @@ async function submitDecryptRequestV2(thresholdNetworkUrl, ctHash, chainId, perm
|
|
|
3785
4073
|
}
|
|
3786
4074
|
});
|
|
3787
4075
|
}
|
|
3788
|
-
const responseClassification = await classifySubmitResponse({
|
|
4076
|
+
const responseClassification = await classifySubmitResponse({
|
|
4077
|
+
response,
|
|
4078
|
+
fallbackErrorCode: "DECRYPT_FAILED" /* DecryptFailed */
|
|
4079
|
+
});
|
|
3789
4080
|
if (responseClassification.kind === "fatal-http") {
|
|
3790
4081
|
throw new CofheError({
|
|
3791
|
-
code:
|
|
4082
|
+
code: responseClassification.cofheErrorCode,
|
|
4083
|
+
apiErrorCode: responseClassification.apiErrorCode,
|
|
3792
4084
|
message: `decrypt request failed: ${responseClassification.errorMessage}`,
|
|
3793
4085
|
hint: "Check the threshold network URL and request parameters.",
|
|
3794
4086
|
context: {
|
|
3795
4087
|
thresholdNetworkUrl,
|
|
3796
|
-
status:
|
|
3797
|
-
statusText: response.statusText,
|
|
4088
|
+
status: responseClassification.status,
|
|
3798
4089
|
body,
|
|
3799
4090
|
attemptIndex
|
|
3800
4091
|
}
|
|
@@ -3842,6 +4133,9 @@ async function submitDecryptRequestV2(thresholdNetworkUrl, ctHash, chainId, perm
|
|
|
3842
4133
|
}
|
|
3843
4134
|
});
|
|
3844
4135
|
}
|
|
4136
|
+
if (responseClassification.status === 404) {
|
|
4137
|
+
last404ApiErrorMessage = responseClassification.apiErrorMessage;
|
|
4138
|
+
}
|
|
3845
4139
|
const elapsedMs = Date.now() - overallStartTime;
|
|
3846
4140
|
throwIfSubmitRetryTimedOut({
|
|
3847
4141
|
operationLabel: "decrypt",
|
|
@@ -3852,7 +4146,8 @@ async function submitDecryptRequestV2(thresholdNetworkUrl, ctHash, chainId, perm
|
|
|
3852
4146
|
overallTimeoutMs: DECRYPT_TIMEOUT_MS,
|
|
3853
4147
|
thresholdNetworkUrl,
|
|
3854
4148
|
body,
|
|
3855
|
-
attemptIndex
|
|
4149
|
+
attemptIndex,
|
|
4150
|
+
lastKnownErrorMessage: last404ApiErrorMessage
|
|
3856
4151
|
});
|
|
3857
4152
|
onPoll?.({
|
|
3858
4153
|
operation: "decrypt",
|
|
@@ -3896,12 +4191,16 @@ async function pollDecryptStatusV2(thresholdNetworkUrl, requestId, overallStartT
|
|
|
3896
4191
|
}
|
|
3897
4192
|
let response;
|
|
3898
4193
|
try {
|
|
3899
|
-
response = await
|
|
3900
|
-
|
|
3901
|
-
|
|
3902
|
-
|
|
4194
|
+
response = await cofheFetch(
|
|
4195
|
+
`${thresholdNetworkUrl}/v2/decrypt/${requestId}`,
|
|
4196
|
+
/*op:decrypt-poll*/
|
|
4197
|
+
{
|
|
4198
|
+
method: "GET",
|
|
4199
|
+
headers: {
|
|
4200
|
+
"Content-Type": "application/json"
|
|
4201
|
+
}
|
|
3903
4202
|
}
|
|
3904
|
-
|
|
4203
|
+
);
|
|
3905
4204
|
} catch (e) {
|
|
3906
4205
|
throw new CofheError({
|
|
3907
4206
|
code: "DECRYPT_FAILED" /* DecryptFailed */,
|
|
@@ -3926,17 +4225,10 @@ async function pollDecryptStatusV2(thresholdNetworkUrl, requestId, overallStartT
|
|
|
3926
4225
|
});
|
|
3927
4226
|
}
|
|
3928
4227
|
if (!response.ok) {
|
|
3929
|
-
|
|
3930
|
-
try {
|
|
3931
|
-
const errorBody = await response.json();
|
|
3932
|
-
const maybeMessage = errorBody.error_message || errorBody.message;
|
|
3933
|
-
if (typeof maybeMessage === "string" && maybeMessage.length > 0)
|
|
3934
|
-
errorMessage = maybeMessage;
|
|
3935
|
-
} catch {
|
|
3936
|
-
errorMessage = response.statusText || errorMessage;
|
|
3937
|
-
}
|
|
4228
|
+
const { apiErrorCode, errorMessage } = await parseApiErrorResponseBody(response);
|
|
3938
4229
|
throw new CofheError({
|
|
3939
|
-
code: "DECRYPT_FAILED" /* DecryptFailed
|
|
4230
|
+
code: mapApiErrorCodeToCofheErrorCode(apiErrorCode, "DECRYPT_FAILED" /* DecryptFailed */),
|
|
4231
|
+
apiErrorCode,
|
|
3940
4232
|
message: `decrypt status poll failed: ${errorMessage}`,
|
|
3941
4233
|
context: {
|
|
3942
4234
|
thresholdNetworkUrl,
|
|
@@ -3981,7 +4273,7 @@ async function pollDecryptStatusV2(thresholdNetworkUrl, requestId, overallStartT
|
|
|
3981
4273
|
});
|
|
3982
4274
|
}
|
|
3983
4275
|
async function tnDecryptV2(params) {
|
|
3984
|
-
const { thresholdNetworkUrl, ctHash, chainId,
|
|
4276
|
+
const { thresholdNetworkUrl, ctHash, chainId, acp, retry404TimeoutMs, onPoll } = params;
|
|
3985
4277
|
const normalized404RetryTimeoutMs = normalize404RetryTimeoutMs({
|
|
3986
4278
|
timeoutMs: retry404TimeoutMs,
|
|
3987
4279
|
operationLabel: "decrypt",
|
|
@@ -3992,7 +4284,7 @@ async function tnDecryptV2(params) {
|
|
|
3992
4284
|
thresholdNetworkUrl,
|
|
3993
4285
|
ctHash,
|
|
3994
4286
|
chainId,
|
|
3995
|
-
|
|
4287
|
+
acp,
|
|
3996
4288
|
overallStartTime,
|
|
3997
4289
|
normalized404RetryTimeoutMs,
|
|
3998
4290
|
onPoll
|
|
@@ -4007,9 +4299,9 @@ async function tnDecryptV2(params) {
|
|
|
4007
4299
|
var DEFAULT_404_RETRY_TIMEOUT_MS3 = 1e4;
|
|
4008
4300
|
var DecryptForTxBuilder = class extends BaseBuilder {
|
|
4009
4301
|
ctHash;
|
|
4010
|
-
|
|
4011
|
-
|
|
4012
|
-
|
|
4302
|
+
acpHash;
|
|
4303
|
+
acp;
|
|
4304
|
+
acpSelection = "unset";
|
|
4013
4305
|
pollCallback;
|
|
4014
4306
|
retry404TimeoutMs = DEFAULT_404_RETRY_TIMEOUT_MS3;
|
|
4015
4307
|
constructor(params) {
|
|
@@ -4054,137 +4346,137 @@ var DecryptForTxBuilder = class extends BaseBuilder {
|
|
|
4054
4346
|
this.retry404TimeoutMs = timeoutMs;
|
|
4055
4347
|
return this;
|
|
4056
4348
|
}
|
|
4057
|
-
|
|
4058
|
-
if (this.
|
|
4349
|
+
withACP(acpOrACPHash) {
|
|
4350
|
+
if (this.acpSelection === "with-acp") {
|
|
4059
4351
|
throw new CofheError({
|
|
4060
4352
|
code: "INTERNAL_ERROR" /* InternalError */,
|
|
4061
|
-
message: "decryptForTx:
|
|
4062
|
-
hint: "Choose the
|
|
4353
|
+
message: "decryptForTx: withACP() can only be selected once.",
|
|
4354
|
+
hint: "Choose the acp mode once. If you need a different acp, start a new decryptForTx() builder chain."
|
|
4063
4355
|
});
|
|
4064
4356
|
}
|
|
4065
|
-
if (this.
|
|
4357
|
+
if (this.acpSelection === "without-acp") {
|
|
4066
4358
|
throw new CofheError({
|
|
4067
4359
|
code: "INTERNAL_ERROR" /* InternalError */,
|
|
4068
|
-
message: "decryptForTx: cannot call
|
|
4069
|
-
hint: "Choose exactly one
|
|
4360
|
+
message: "decryptForTx: cannot call withACP() after withoutACP() has been selected.",
|
|
4361
|
+
hint: "Choose exactly one acp mode: either call .withACP(...) or .withoutACP(), but not both."
|
|
4070
4362
|
});
|
|
4071
4363
|
}
|
|
4072
|
-
this.
|
|
4073
|
-
if (typeof
|
|
4074
|
-
this.
|
|
4075
|
-
this.
|
|
4076
|
-
} else if (
|
|
4077
|
-
this.
|
|
4078
|
-
this.
|
|
4364
|
+
this.acpSelection = "with-acp";
|
|
4365
|
+
if (typeof acpOrACPHash === "string") {
|
|
4366
|
+
this.acpHash = acpOrACPHash;
|
|
4367
|
+
this.acp = void 0;
|
|
4368
|
+
} else if (acpOrACPHash === void 0) {
|
|
4369
|
+
this.acpHash = void 0;
|
|
4370
|
+
this.acp = void 0;
|
|
4079
4371
|
} else {
|
|
4080
|
-
this.
|
|
4081
|
-
this.
|
|
4372
|
+
this.acp = acpOrACPHash;
|
|
4373
|
+
this.acpHash = void 0;
|
|
4082
4374
|
}
|
|
4083
4375
|
return this;
|
|
4084
4376
|
}
|
|
4085
4377
|
/**
|
|
4086
|
-
* Select "no
|
|
4378
|
+
* Select "no acp" mode.
|
|
4087
4379
|
*
|
|
4088
|
-
* This uses global allowance (no
|
|
4380
|
+
* This uses global allowance (no acp required) and sends an empty permission payload to `/decrypt`.
|
|
4089
4381
|
*/
|
|
4090
|
-
|
|
4091
|
-
if (this.
|
|
4382
|
+
withoutACP() {
|
|
4383
|
+
if (this.acpSelection === "without-acp") {
|
|
4092
4384
|
throw new CofheError({
|
|
4093
4385
|
code: "INTERNAL_ERROR" /* InternalError */,
|
|
4094
|
-
message: "decryptForTx:
|
|
4095
|
-
hint: "Choose the
|
|
4386
|
+
message: "decryptForTx: withoutACP() can only be selected once.",
|
|
4387
|
+
hint: "Choose the acp mode once. If you need a different mode, start a new decryptForTx() builder chain."
|
|
4096
4388
|
});
|
|
4097
4389
|
}
|
|
4098
|
-
if (this.
|
|
4390
|
+
if (this.acpSelection === "with-acp") {
|
|
4099
4391
|
throw new CofheError({
|
|
4100
4392
|
code: "INTERNAL_ERROR" /* InternalError */,
|
|
4101
|
-
message: "decryptForTx: cannot call
|
|
4102
|
-
hint: "Choose exactly one
|
|
4393
|
+
message: "decryptForTx: cannot call withoutACP() after withACP() has been selected.",
|
|
4394
|
+
hint: "Choose exactly one acp mode: either call .withACP(...) or .withoutACP(), but not both."
|
|
4103
4395
|
});
|
|
4104
4396
|
}
|
|
4105
|
-
this.
|
|
4106
|
-
this.
|
|
4107
|
-
this.
|
|
4397
|
+
this.acpSelection = "without-acp";
|
|
4398
|
+
this.acpHash = void 0;
|
|
4399
|
+
this.acp = void 0;
|
|
4108
4400
|
return this;
|
|
4109
4401
|
}
|
|
4110
|
-
|
|
4111
|
-
return this.
|
|
4402
|
+
getACP() {
|
|
4403
|
+
return this.acp;
|
|
4112
4404
|
}
|
|
4113
|
-
|
|
4114
|
-
return this.
|
|
4405
|
+
getACPHash() {
|
|
4406
|
+
return this.acpHash;
|
|
4115
4407
|
}
|
|
4116
4408
|
async getThresholdNetworkUrl() {
|
|
4117
4409
|
this.assertChainId();
|
|
4118
4410
|
return getThresholdNetworkUrlOrThrow(this.config, this.chainId);
|
|
4119
4411
|
}
|
|
4120
|
-
async
|
|
4121
|
-
if (this.
|
|
4412
|
+
async getResolvedACP() {
|
|
4413
|
+
if (this.acpSelection === "unset") {
|
|
4122
4414
|
throw new CofheError({
|
|
4123
4415
|
code: "INTERNAL_ERROR" /* InternalError */,
|
|
4124
|
-
message: "decryptForTx: missing
|
|
4125
|
-
hint: "Call .
|
|
4416
|
+
message: "decryptForTx: missing acp selection; call withACP(...) or withoutACP() before execute().",
|
|
4417
|
+
hint: "Call .withACP() to use the active acp, or .withoutACP() for global allowance."
|
|
4126
4418
|
});
|
|
4127
4419
|
}
|
|
4128
|
-
if (this.
|
|
4420
|
+
if (this.acpSelection === "without-acp") {
|
|
4129
4421
|
return null;
|
|
4130
4422
|
}
|
|
4131
|
-
if (this.
|
|
4132
|
-
return this.
|
|
4423
|
+
if (this.acp)
|
|
4424
|
+
return this.acp;
|
|
4133
4425
|
this.assertChainId();
|
|
4134
4426
|
this.assertAccount();
|
|
4135
|
-
if (this.
|
|
4136
|
-
const
|
|
4137
|
-
if (!
|
|
4427
|
+
if (this.acpHash) {
|
|
4428
|
+
const acp2 = await acps.getACP(this.chainId, this.account, this.acpHash);
|
|
4429
|
+
if (!acp2) {
|
|
4138
4430
|
throw new CofheError({
|
|
4139
|
-
code: "
|
|
4140
|
-
message: `
|
|
4141
|
-
hint: "Ensure the
|
|
4431
|
+
code: "ACP_NOT_FOUND" /* ACPNotFound */,
|
|
4432
|
+
message: `ACP with hash <${this.acpHash}> not found for account <${this.account}> and chainId <${this.chainId}>`,
|
|
4433
|
+
hint: "Ensure the acp exists and is valid.",
|
|
4142
4434
|
context: {
|
|
4143
4435
|
chainId: this.chainId,
|
|
4144
4436
|
account: this.account,
|
|
4145
|
-
|
|
4437
|
+
acpHash: this.acpHash
|
|
4146
4438
|
}
|
|
4147
4439
|
});
|
|
4148
4440
|
}
|
|
4149
|
-
return
|
|
4441
|
+
return acp2;
|
|
4150
4442
|
}
|
|
4151
|
-
const
|
|
4152
|
-
if (!
|
|
4443
|
+
const acp = await acps.getActiveACP(this.chainId, this.account);
|
|
4444
|
+
if (!acp) {
|
|
4153
4445
|
throw new CofheError({
|
|
4154
|
-
code: "
|
|
4155
|
-
message: `Active
|
|
4156
|
-
hint: "Create
|
|
4446
|
+
code: "ACP_NOT_FOUND" /* ACPNotFound */,
|
|
4447
|
+
message: `Active acp not found for chainId <${this.chainId}> and account <${this.account}>`,
|
|
4448
|
+
hint: "Create an ACP (e.g. client.acp.createSelf(...)) and/or set it active (client.acp.selectActiveACP(hash)).",
|
|
4157
4449
|
context: {
|
|
4158
4450
|
chainId: this.chainId,
|
|
4159
4451
|
account: this.account
|
|
4160
4452
|
}
|
|
4161
4453
|
});
|
|
4162
4454
|
}
|
|
4163
|
-
return
|
|
4455
|
+
return acp;
|
|
4164
4456
|
}
|
|
4165
4457
|
/**
|
|
4166
4458
|
* On hardhat, interact with MockThresholdNetwork contract
|
|
4167
4459
|
*/
|
|
4168
|
-
async mocksDecryptForTx(
|
|
4460
|
+
async mocksDecryptForTx(acp) {
|
|
4169
4461
|
this.assertPublicClient();
|
|
4170
4462
|
const delay = this.config.mocks.decryptDelay;
|
|
4171
4463
|
if (delay > 0)
|
|
4172
4464
|
await sleep(delay);
|
|
4173
|
-
const result = await cofheMocksDecryptForTx(this.ctHash, 0,
|
|
4465
|
+
const result = await cofheMocksDecryptForTx(this.ctHash, 0, acp, this.publicClient);
|
|
4174
4466
|
return result;
|
|
4175
4467
|
}
|
|
4176
4468
|
/**
|
|
4177
4469
|
* In the production context, perform a true decryption with the CoFHE coprocessor.
|
|
4178
4470
|
*/
|
|
4179
|
-
async productionDecryptForTx(
|
|
4471
|
+
async productionDecryptForTx(acp) {
|
|
4180
4472
|
this.assertChainId();
|
|
4181
4473
|
this.assertPublicClient();
|
|
4182
4474
|
const thresholdNetworkUrl = await this.getThresholdNetworkUrl();
|
|
4183
|
-
const
|
|
4475
|
+
const wireAcp = acp ? ACPUtils.getPublic(acp, true) : null;
|
|
4184
4476
|
const { decryptedValue, signature } = await tnDecryptV2({
|
|
4185
4477
|
ctHash: this.ctHash,
|
|
4186
4478
|
chainId: this.chainId,
|
|
4187
|
-
|
|
4479
|
+
acp: wireAcp,
|
|
4188
4480
|
thresholdNetworkUrl,
|
|
4189
4481
|
retry404TimeoutMs: this.retry404TimeoutMs,
|
|
4190
4482
|
onPoll: this.pollCallback
|
|
@@ -4198,19 +4490,19 @@ var DecryptForTxBuilder = class extends BaseBuilder {
|
|
|
4198
4490
|
/**
|
|
4199
4491
|
* Final step of the decryptForTx process. MUST BE CALLED LAST IN THE CHAIN.
|
|
4200
4492
|
*
|
|
4201
|
-
* You must explicitly choose one
|
|
4202
|
-
* - `
|
|
4203
|
-
* - `
|
|
4493
|
+
* You must explicitly choose one acp mode before calling `execute()`:
|
|
4494
|
+
* - `withACP(acp)` / `withACP(acpHash)` / `withACP()` (active acp)
|
|
4495
|
+
* - `withoutACP()` (global allowance)
|
|
4204
4496
|
*/
|
|
4205
4497
|
async execute() {
|
|
4206
|
-
const
|
|
4207
|
-
if (
|
|
4208
|
-
|
|
4209
|
-
const chainId =
|
|
4498
|
+
const acp = await this.getResolvedACP();
|
|
4499
|
+
if (acp !== null) {
|
|
4500
|
+
ACPUtils.validate(acp);
|
|
4501
|
+
const chainId = acp._signedDomain.chainId;
|
|
4210
4502
|
if (chainId === hardhat2.id) {
|
|
4211
|
-
return await this.mocksDecryptForTx(
|
|
4503
|
+
return await this.mocksDecryptForTx(acp);
|
|
4212
4504
|
} else {
|
|
4213
|
-
return await this.productionDecryptForTx(
|
|
4505
|
+
return await this.productionDecryptForTx(acp);
|
|
4214
4506
|
}
|
|
4215
4507
|
} else {
|
|
4216
4508
|
if (!this.chainId) {
|
|
@@ -4273,6 +4565,21 @@ function createCofheClientBase(opts) {
|
|
|
4273
4565
|
const updateConnectState = (partial) => {
|
|
4274
4566
|
connectStore.setState((state) => ({ ...state, ...partial }));
|
|
4275
4567
|
};
|
|
4568
|
+
const _resolveSharingRegistry = async (publicClient, chainId) => {
|
|
4569
|
+
const configured = opts.config.acp?.sharingRegistry?.[chainId];
|
|
4570
|
+
if (configured != null)
|
|
4571
|
+
return configured;
|
|
4572
|
+
const registry = (await acps.getAclServedAddresses(publicClient, chainId)).shareRegistry;
|
|
4573
|
+
if (registry == null) {
|
|
4574
|
+
throw new CofheError({
|
|
4575
|
+
code: "MISSING_CONFIG" /* MissingConfig */,
|
|
4576
|
+
message: `No ACP share registry available for chainId <${chainId}>`,
|
|
4577
|
+
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.",
|
|
4578
|
+
context: { chainId }
|
|
4579
|
+
});
|
|
4580
|
+
}
|
|
4581
|
+
return registry;
|
|
4582
|
+
};
|
|
4276
4583
|
const _requireConnected = () => {
|
|
4277
4584
|
const state = connectStore.getState();
|
|
4278
4585
|
const notConnected = !state.connected || !state.account || !state.chainId || !state.publicClient || !state.walletClient;
|
|
@@ -4395,74 +4702,147 @@ function createCofheClientBase(opts) {
|
|
|
4395
4702
|
}
|
|
4396
4703
|
return { chainId: _chainId, account: _account };
|
|
4397
4704
|
};
|
|
4398
|
-
const
|
|
4705
|
+
const clientACPs = {
|
|
4399
4706
|
// Pass through store access
|
|
4400
|
-
getSnapshot:
|
|
4401
|
-
subscribe:
|
|
4707
|
+
getSnapshot: acps.getSnapshot,
|
|
4708
|
+
subscribe: acps.subscribe,
|
|
4402
4709
|
// Creation methods (require connection)
|
|
4403
4710
|
createSelf: async (options, clients) => {
|
|
4404
4711
|
_requireConnected();
|
|
4405
4712
|
const { publicClient, walletClient } = clients ?? connectStore.getState();
|
|
4406
|
-
|
|
4713
|
+
const chainId = await publicClient.getChainId();
|
|
4714
|
+
return acps.createSelf(
|
|
4715
|
+
await acps.applyACPDefaultsFromChain(options, opts.config.acp, publicClient, chainId),
|
|
4716
|
+
publicClient,
|
|
4717
|
+
walletClient
|
|
4718
|
+
);
|
|
4407
4719
|
},
|
|
4408
4720
|
createSharing: async (options, clients) => {
|
|
4409
4721
|
_requireConnected();
|
|
4410
4722
|
const { publicClient, walletClient } = clients ?? connectStore.getState();
|
|
4411
|
-
|
|
4723
|
+
const chainId = await publicClient.getChainId();
|
|
4724
|
+
return acps.createSharing(
|
|
4725
|
+
await acps.applyACPDefaultsFromChain(options, opts.config.acp, publicClient, chainId),
|
|
4726
|
+
publicClient,
|
|
4727
|
+
walletClient
|
|
4728
|
+
);
|
|
4412
4729
|
},
|
|
4413
4730
|
importShared: async (options, clients) => {
|
|
4414
4731
|
_requireConnected();
|
|
4415
4732
|
const { publicClient, walletClient } = clients ?? connectStore.getState();
|
|
4416
|
-
return
|
|
4733
|
+
return acps.importShared(options, publicClient, walletClient);
|
|
4417
4734
|
},
|
|
4418
4735
|
// Get or create methods (require connection)
|
|
4419
|
-
|
|
4736
|
+
getOrCreateSelfACP: async (chainId, account, options) => {
|
|
4420
4737
|
_requireConnected();
|
|
4421
4738
|
const { chainId: _chainId, account: _account } = _getChainIdAndAccount(chainId, account);
|
|
4422
4739
|
const { publicClient, walletClient } = connectStore.getState();
|
|
4423
|
-
|
|
4740
|
+
const optionsWithDefaults = await acps.applyACPDefaultsFromChain(
|
|
4741
|
+
options ?? { issuer: _account, name: "Autogenerated Self ACP" },
|
|
4742
|
+
opts.config.acp,
|
|
4743
|
+
publicClient,
|
|
4744
|
+
_chainId
|
|
4745
|
+
);
|
|
4746
|
+
return acps.getOrCreateSelfACP(publicClient, walletClient, _chainId, _account, optionsWithDefaults);
|
|
4424
4747
|
},
|
|
4425
|
-
|
|
4748
|
+
getOrCreateSharingACP: async (options, chainId, account) => {
|
|
4426
4749
|
_requireConnected();
|
|
4427
4750
|
const { chainId: _chainId, account: _account } = _getChainIdAndAccount(chainId, account);
|
|
4428
4751
|
const { publicClient, walletClient } = connectStore.getState();
|
|
4429
|
-
return
|
|
4752
|
+
return acps.getOrCreateSharingACP(
|
|
4753
|
+
publicClient,
|
|
4754
|
+
walletClient,
|
|
4755
|
+
await acps.applyACPDefaultsFromChain(options, opts.config.acp, publicClient, _chainId),
|
|
4756
|
+
_chainId,
|
|
4757
|
+
_account
|
|
4758
|
+
);
|
|
4759
|
+
},
|
|
4760
|
+
// Revocation (require connection)
|
|
4761
|
+
revokeACP: async (acp) => {
|
|
4762
|
+
_requireConnected();
|
|
4763
|
+
const { walletClient } = connectStore.getState();
|
|
4764
|
+
return acps.revokeACP(acp, walletClient);
|
|
4765
|
+
},
|
|
4766
|
+
revokeAllACPs: async (revokerContract) => {
|
|
4767
|
+
_requireConnected();
|
|
4768
|
+
const { publicClient, walletClient } = connectStore.getState();
|
|
4769
|
+
return acps.revokeAllACPs(walletClient, publicClient, revokerContract);
|
|
4770
|
+
},
|
|
4771
|
+
isACPRevoked: async (acp) => {
|
|
4772
|
+
_requireConnected();
|
|
4773
|
+
const { publicClient } = connectStore.getState();
|
|
4774
|
+
return acps.isACPRevoked(acp, publicClient);
|
|
4775
|
+
},
|
|
4776
|
+
// On-chain sharing (require connection + config.acp.sharingRegistry)
|
|
4777
|
+
shareOnChain: async (acp) => {
|
|
4778
|
+
_requireConnected();
|
|
4779
|
+
const { publicClient, walletClient } = connectStore.getState();
|
|
4780
|
+
const chainId = await publicClient.getChainId();
|
|
4781
|
+
const sharingRegistry = await _resolveSharingRegistry(publicClient, chainId);
|
|
4782
|
+
return acps.shareOnChain(acp, walletClient, sharingRegistry);
|
|
4783
|
+
},
|
|
4784
|
+
getIncomingShares: async () => {
|
|
4785
|
+
_requireConnected();
|
|
4786
|
+
const { publicClient, walletClient } = connectStore.getState();
|
|
4787
|
+
const chainId = await publicClient.getChainId();
|
|
4788
|
+
const account = walletClient.account.address;
|
|
4789
|
+
const sharingRegistry = await _resolveSharingRegistry(publicClient, chainId);
|
|
4790
|
+
return acps.getIncomingShares(publicClient, sharingRegistry, account);
|
|
4791
|
+
},
|
|
4792
|
+
importFromChain: async (share) => {
|
|
4793
|
+
_requireConnected();
|
|
4794
|
+
const { publicClient, walletClient } = connectStore.getState();
|
|
4795
|
+
return acps.importFromChain(share, publicClient, walletClient);
|
|
4796
|
+
},
|
|
4797
|
+
dismissShare: async (shareId) => {
|
|
4798
|
+
_requireConnected();
|
|
4799
|
+
const { publicClient, walletClient } = connectStore.getState();
|
|
4800
|
+
const chainId = await publicClient.getChainId();
|
|
4801
|
+
const sharingRegistry = await _resolveSharingRegistry(publicClient, chainId);
|
|
4802
|
+
return acps.removeShareOnChain(shareId, walletClient, sharingRegistry);
|
|
4803
|
+
},
|
|
4804
|
+
cancelShare: async (shareId) => {
|
|
4805
|
+
_requireConnected();
|
|
4806
|
+
const { publicClient, walletClient } = connectStore.getState();
|
|
4807
|
+
const chainId = await publicClient.getChainId();
|
|
4808
|
+
const sharingRegistry = await _resolveSharingRegistry(publicClient, chainId);
|
|
4809
|
+
return acps.removeShareOnChain(shareId, walletClient, sharingRegistry);
|
|
4430
4810
|
},
|
|
4431
4811
|
// Retrieval methods (auto-fill chainId/account)
|
|
4432
|
-
|
|
4812
|
+
getACP: (hash, chainId, account) => {
|
|
4433
4813
|
const { chainId: _chainId, account: _account } = _getChainIdAndAccount(chainId, account);
|
|
4434
|
-
return
|
|
4814
|
+
return acps.getACP(_chainId, _account, hash);
|
|
4435
4815
|
},
|
|
4436
|
-
|
|
4816
|
+
getACPs: (chainId, account) => {
|
|
4437
4817
|
const { chainId: _chainId, account: _account } = _getChainIdAndAccount(chainId, account);
|
|
4438
|
-
return
|
|
4818
|
+
return acps.getACPs(_chainId, _account);
|
|
4439
4819
|
},
|
|
4440
|
-
|
|
4820
|
+
getActiveACP: (chainId, account) => {
|
|
4441
4821
|
const { chainId: _chainId, account: _account } = _getChainIdAndAccount(chainId, account);
|
|
4442
|
-
return
|
|
4822
|
+
return acps.getActiveACP(_chainId, _account);
|
|
4443
4823
|
},
|
|
4444
|
-
|
|
4824
|
+
getActiveACPHash: (chainId, account) => {
|
|
4445
4825
|
const { chainId: _chainId, account: _account } = _getChainIdAndAccount(chainId, account);
|
|
4446
|
-
return
|
|
4826
|
+
return acps.getActiveACPHash(_chainId, _account);
|
|
4447
4827
|
},
|
|
4448
4828
|
// Mutation methods (auto-fill chainId/account)
|
|
4449
|
-
|
|
4829
|
+
selectActiveACP: (hash, chainId, account) => {
|
|
4450
4830
|
const { chainId: _chainId, account: _account } = _getChainIdAndAccount(chainId, account);
|
|
4451
|
-
return
|
|
4831
|
+
return acps.selectActiveACP(_chainId, _account, hash);
|
|
4452
4832
|
},
|
|
4453
|
-
|
|
4833
|
+
removeACP: async (hash, chainId, account) => {
|
|
4454
4834
|
const { chainId: _chainId, account: _account } = _getChainIdAndAccount(chainId, account);
|
|
4455
|
-
return
|
|
4835
|
+
return acps.removeACP(_chainId, _account, hash);
|
|
4456
4836
|
},
|
|
4457
|
-
|
|
4837
|
+
removeActiveACP: async (chainId, account) => {
|
|
4458
4838
|
const { chainId: _chainId, account: _account } = _getChainIdAndAccount(chainId, account);
|
|
4459
|
-
return
|
|
4839
|
+
return acps.removeActiveACP(_chainId, _account);
|
|
4460
4840
|
},
|
|
4461
4841
|
// Utils (no context needed)
|
|
4462
|
-
getHash:
|
|
4463
|
-
export:
|
|
4464
|
-
serialize:
|
|
4465
|
-
deserialize:
|
|
4842
|
+
getHash: acps.getHash,
|
|
4843
|
+
export: acps.export,
|
|
4844
|
+
serialize: acps.serialize,
|
|
4845
|
+
deserialize: acps.deserialize
|
|
4466
4846
|
};
|
|
4467
4847
|
return {
|
|
4468
4848
|
// Zustand reactive accessors (don't export store directly to prevent mutation)
|
|
@@ -4490,7 +4870,7 @@ function createCofheClientBase(opts) {
|
|
|
4490
4870
|
decryptHandle: decryptForView,
|
|
4491
4871
|
decryptForTx,
|
|
4492
4872
|
verifyDecryptResult: verifyDecryptResult2,
|
|
4493
|
-
|
|
4873
|
+
acp: clientACPs
|
|
4494
4874
|
// Add SDK-specific methods below that require connection
|
|
4495
4875
|
// Example:
|
|
4496
4876
|
// async encryptData(data: unknown) {
|