@cofhe/sdk 0.8.0 → 1.0.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.
Files changed (87) hide show
  1. package/CHANGELOG.md +51 -0
  2. package/acps/acp.ts +25 -6
  3. package/acps/index.ts +20 -15
  4. package/acps/onchain-utils.ts +15 -10
  5. package/acps/registry.ts +166 -8
  6. package/acps/sealing.ts +31 -5
  7. package/acps/share-metadata/appKey.ts +8 -0
  8. package/acps/share-metadata/decode.ts +120 -0
  9. package/acps/share-metadata/describe.ts +198 -0
  10. package/acps/share-metadata/encode.ts +147 -0
  11. package/acps/share-metadata/erc7984.ts +26 -0
  12. package/acps/share-metadata/index.ts +9 -0
  13. package/acps/share-metadata/layout.ts +69 -0
  14. package/acps/share-metadata/presets.ts +116 -0
  15. package/acps/share-metadata/schema.ts +47 -0
  16. package/acps/share-metadata/test/codec.test.ts +140 -0
  17. package/acps/share-metadata/test/describe.test.ts +112 -0
  18. package/acps/share-metadata/test/fixtures.ts +157 -0
  19. package/acps/share-metadata/test/live.test.ts +56 -0
  20. package/acps/share-metadata/test/presets.test.ts +119 -0
  21. package/acps/share-metadata/test/verify.test.ts +144 -0
  22. package/acps/share-metadata/verify.ts +155 -0
  23. package/acps/store.ts +48 -18
  24. package/acps/test/aclServedAddresses.test.ts +65 -0
  25. package/acps/test/acp.test.ts +102 -7
  26. package/acps/test/localstorage.test.ts +28 -1
  27. package/acps/test/registry.test.ts +144 -0
  28. package/acps/test/sealing.test.ts +74 -0
  29. package/acps/test/store.test.ts +71 -0
  30. package/acps/test/utils.test.ts +63 -0
  31. package/acps/test/validation.test.ts +0 -4
  32. package/acps/types.ts +43 -6
  33. package/acps/utils.ts +15 -2
  34. package/acps/validation.ts +3 -1
  35. package/core/acps.ts +123 -74
  36. package/core/client.ts +33 -7
  37. package/core/clientTypes.ts +25 -7
  38. package/core/config.ts +8 -0
  39. package/core/decrypt/apiError.ts +4 -1
  40. package/core/decrypt/verifyDecryptResult.ts +4 -1
  41. package/core/encrypt/cofheMocksZkVerifySign.ts +19 -2
  42. package/core/test/acpDefaults.test.ts +108 -19
  43. package/core/test/acpStoreAccountCase.test.ts +80 -0
  44. package/core/test/acps.test.ts +38 -3
  45. package/core/test/apiError.test.ts +8 -0
  46. package/core/test/appKeyConfig.test.ts +94 -0
  47. package/core/test/config.test.ts +8 -0
  48. package/core/test/decryptBuilders.test.ts +110 -2
  49. package/core/test/incomingShares.test.ts +84 -0
  50. package/core/test/mocksEncryptRange.test.ts +90 -0
  51. package/core/test/utils.test.ts +128 -0
  52. package/core/test/verifyDecryptResult.test.ts +51 -0
  53. package/core/utils.ts +16 -9
  54. package/dist/acps.cjs +796 -132
  55. package/dist/acps.d.cts +367 -35
  56. package/dist/acps.d.ts +367 -35
  57. package/dist/acps.js +2 -2
  58. package/dist/{chunk-UJYOBHYA.js → chunk-3L2IVPMI.js} +688 -203
  59. package/dist/chunk-J23FYAI2.js +174 -0
  60. package/dist/{chunk-GGI7Y6AW.js → chunk-O2ITEUEU.js} +110 -269
  61. package/dist/chunk-WJGTPW7K.js +228 -0
  62. package/dist/{clientTypes-D3aIXmUp.d.cts → clientTypes-IO6b5Um8.d.cts} +61 -21
  63. package/dist/{clientTypes-5YPPR16m.d.ts → clientTypes-b1pOxOUE.d.ts} +61 -21
  64. package/dist/core.cjs +3115 -2584
  65. package/dist/core.d.cts +6 -3
  66. package/dist/core.d.ts +6 -3
  67. package/dist/core.js +4 -3
  68. package/dist/node.cjs +3001 -2470
  69. package/dist/node.d.cts +2 -2
  70. package/dist/node.d.ts +2 -2
  71. package/dist/node.js +18 -17
  72. package/dist/{onchain-utils-CUAK8glx.d.cts → onchain-utils-CgOslubH.d.cts} +139 -15
  73. package/dist/{onchain-utils-CUAK8glx.d.ts → onchain-utils-CgOslubH.d.ts} +139 -15
  74. package/dist/web.cjs +3011 -2487
  75. package/dist/web.d.cts +2 -2
  76. package/dist/web.d.ts +2 -2
  77. package/dist/web.js +4 -10
  78. package/dist/zkProve.worker.cjs +14 -7
  79. package/dist/zkProve.worker.js +2 -8
  80. package/node/index.ts +1 -10
  81. package/node/storage.ts +18 -7
  82. package/node/test/storage-permissions.test.ts +40 -0
  83. package/package.json +4 -4
  84. package/web/index.ts +1 -10
  85. package/web/test/threadPoolBenchHelper.ts +1 -7
  86. package/web/zkProve.worker.ts +1 -10
  87. package/dist/chunk-NMKMCAI3.js +0 -14
@@ -1,5 +1,5 @@
1
- import { TASK_MANAGER_ID, COFHE_ADDRESS_BOOK_ADDRESS } from './chunk-NMKMCAI3.js';
2
- import { isAddress, getAddress, zeroAddress, isHex, parseAbi, keccak256, toHex, encodeAbiParameters, decodeErrorResult } from 'viem';
1
+ import { CofheError, TASK_MANAGER_ID, COFHE_ADDRESS_BOOK_ADDRESS } from './chunk-J23FYAI2.js';
2
+ import { isAddress, getAddress, zeroAddress, isHex, parseAbi, keccak256, padHex, numberToHex, concatHex, toFunctionSelector, size, sliceHex, toHex, encodeAbiParameters, hexToBigInt, stringToHex, hexToNumber, toEventSignature, toFunctionSignature, decodeErrorResult, toEventSelector, decodeAbiParameters } from 'viem';
3
3
  import nacl from 'tweetnacl';
4
4
  import { z } from 'zod';
5
5
  import { createStore } from 'zustand/vanilla';
@@ -7,9 +7,14 @@ import { persist } from 'zustand/middleware';
7
7
  import { produce } from 'immer';
8
8
 
9
9
  // acps/utils.ts
10
+ var HEX_BODY_REGEX = /^[0-9a-fA-F]*$/;
10
11
  var fromHexString = (hexString) => {
11
- const cleanString = hexString.length % 2 === 1 ? `0${hexString}` : hexString;
12
- const arr = cleanString.replace(/^0x/, "").match(/.{1,2}/g);
12
+ const body = hexString.replace(/^0x/, "");
13
+ if (!HEX_BODY_REGEX.test(body)) {
14
+ throw new Error("Invalid hex string: contains non-hexadecimal characters");
15
+ }
16
+ const padded = body.length % 2 === 1 ? `0${body}` : body;
17
+ const arr = padded.match(/.{1,2}/g);
13
18
  if (!arr)
14
19
  return new Uint8Array();
15
20
  return new Uint8Array(arr.map((byte) => parseInt(byte, 16)));
@@ -66,7 +71,7 @@ var GenerateSealingKey = () => {
66
71
  var seal = (value, publicKey) => {
67
72
  isString(publicKey);
68
73
  isBigIntOrNumber(value);
69
- assertKeyLength(publicKey, "Public");
74
+ assertSealingKey(publicKey, "Public");
70
75
  const ephemeralKeyPair = nacl.box.keyPair();
71
76
  const nonce = nacl.randomBytes(nacl.box.nonceLength);
72
77
  const encryptedMessage = nacl.box(toBeArray(value), nonce, fromHexString(publicKey), ephemeralKeyPair.secretKey);
@@ -77,21 +82,40 @@ var seal = (value, publicKey) => {
77
82
  };
78
83
  };
79
84
  var unsealWithPrivateKey = (privateKey, parsedData) => {
80
- assertKeyLength(privateKey, "Private");
85
+ assertSealingKey(privateKey, "Private");
81
86
  const nonce = parsedData.nonce instanceof Uint8Array ? parsedData.nonce : new Uint8Array(parsedData.nonce);
82
87
  const ephemPublicKey = parsedData.public_key instanceof Uint8Array ? parsedData.public_key : new Uint8Array(parsedData.public_key);
83
88
  const dataToDecrypt = parsedData.data instanceof Uint8Array ? parsedData.data : new Uint8Array(parsedData.data);
84
- const decryptedMessage = nacl.box.open(dataToDecrypt, nonce, ephemPublicKey, fromHexString(privateKey));
89
+ let decryptedMessage;
90
+ let cause;
91
+ try {
92
+ decryptedMessage = nacl.box.open(dataToDecrypt, nonce, ephemPublicKey, fromHexString(privateKey));
93
+ } catch (e) {
94
+ decryptedMessage = null;
95
+ cause = e instanceof Error ? e : void 0;
96
+ }
85
97
  if (!decryptedMessage) {
86
- throw new Error("Failed to decrypt message");
98
+ throw new CofheError({
99
+ code: "SEAL_OUTPUT_FAILED" /* SealOutputFailed */,
100
+ message: "Failed to unseal decrypt response",
101
+ cause,
102
+ context: {
103
+ dataLength: dataToDecrypt.length,
104
+ nonceLength: nonce.length,
105
+ publicKeyLength: ephemPublicKey.length
106
+ }
107
+ });
87
108
  }
88
109
  return toBigInt(decryptedMessage);
89
110
  };
90
- var assertKeyLength = (key, kind) => {
111
+ var assertSealingKey = (key, kind) => {
91
112
  const bare = key.startsWith("0x") ? key.slice(2) : key;
92
113
  if (bare.length !== KEY_HEX_LENGTH) {
93
114
  throw new Error(`${kind} key must be of length ${KEY_HEX_LENGTH}`);
94
115
  }
116
+ if (!/^[0-9a-fA-F]+$/.test(bare)) {
117
+ throw new Error(`${kind} key must contain only hex characters`);
118
+ }
95
119
  };
96
120
  var ACPScope = {
97
121
  Global: 0,
@@ -144,7 +168,8 @@ var zACPWithDefaults = z.object({
144
168
  contracts: contractsSchema,
145
169
  handles: handlesSchema,
146
170
  issuerSignature: bytesSchema.optional().default("0x"),
147
- recipientSignature: bytesSchema.optional().default("0x")
171
+ recipientSignature: bytesSchema.optional().default("0x"),
172
+ metadata: bytesSchema.optional()
148
173
  });
149
174
  var zACPWithSealingKeys = zACPWithDefaults.extend({
150
175
  /** X25519 private key, 0x-prefixed 32-byte hex; never leaves the client */
@@ -215,7 +240,6 @@ var ImportACPOptionsValidator = z.object({
215
240
  type: z.literal("recipient").optional().default("recipient"),
216
241
  issuer: addressNotZeroSchema,
217
242
  recipient: addressNotZeroSchema,
218
- name: z.string().optional().default("Unnamed ACP"),
219
243
  expiration: z.int(),
220
244
  revokerData: z.int().optional().default(0),
221
245
  revokerContract: addressSchema.optional().default(zeroAddress),
@@ -223,7 +247,9 @@ var ImportACPOptionsValidator = z.object({
223
247
  contracts: contractsSchema,
224
248
  handles: handlesSchema,
225
249
  issuerSignature: bytesNotEmptySchema,
226
- recipientSignature: bytesSchema.optional().default("0x")
250
+ recipientSignature: bytesSchema.optional().default("0x"),
251
+ /** The share metadata (labels blob); `0x` means none and is dropped */
252
+ metadata: bytesSchema.optional().transform((val) => val === "0x" ? void 0 : val)
227
253
  }).refine(...ExternalValidatorRefinement).transform(withDerivedScope).refine(...ScopeConsistencyRefinement);
228
254
  var ImportACPValidator = zACPWithSealingKeys.refine((data) => data.type === "recipient", {
229
255
  error: "Type must be 'recipient'"
@@ -389,168 +415,6 @@ var SignatureUtils = {
389
415
  throw new Error(`Unknown acp type: ${acpType}`);
390
416
  }
391
417
  };
392
-
393
- // core/error.ts
394
- var CofheErrorCode = /* @__PURE__ */ ((CofheErrorCode2) => {
395
- CofheErrorCode2["InternalError"] = "INTERNAL_ERROR";
396
- CofheErrorCode2["UnknownEnvironment"] = "UNKNOWN_ENVIRONMENT";
397
- CofheErrorCode2["InitTfheFailed"] = "INIT_TFHE_FAILED";
398
- CofheErrorCode2["InitViemFailed"] = "INIT_VIEM_FAILED";
399
- CofheErrorCode2["InitEthersFailed"] = "INIT_ETHERS_FAILED";
400
- CofheErrorCode2["NotConnected"] = "NOT_CONNECTED";
401
- CofheErrorCode2["MissingPublicClient"] = "MISSING_PUBLIC_CLIENT";
402
- CofheErrorCode2["MissingWalletClient"] = "MISSING_WALLET_CLIENT";
403
- CofheErrorCode2["MissingProviderParam"] = "MISSING_PROVIDER_PARAM";
404
- CofheErrorCode2["EmptySecurityZonesParam"] = "EMPTY_SECURITY_ZONES_PARAM";
405
- CofheErrorCode2["InvalidACPData"] = "INVALID_ACP_DATA";
406
- CofheErrorCode2["InvalidACPDomain"] = "INVALID_ACP_DOMAIN";
407
- CofheErrorCode2["ACPNotFound"] = "ACP_NOT_FOUND";
408
- CofheErrorCode2["CannotRemoveLastACP"] = "CANNOT_REMOVE_LAST_ACP";
409
- CofheErrorCode2["AccountUninitialized"] = "ACCOUNT_UNINITIALIZED";
410
- CofheErrorCode2["ChainIdUninitialized"] = "CHAIN_ID_UNINITIALIZED";
411
- CofheErrorCode2["ConsumingContractUninitialized"] = "CONSUMING_CONTRACT_UNINITIALIZED";
412
- CofheErrorCode2["SealOutputFailed"] = "SEAL_OUTPUT_FAILED";
413
- CofheErrorCode2["SealOutputReturnedNull"] = "SEAL_OUTPUT_RETURNED_NULL";
414
- CofheErrorCode2["InvalidUtype"] = "INVALID_UTYPE";
415
- CofheErrorCode2["DecryptFailed"] = "DECRYPT_FAILED";
416
- CofheErrorCode2["DecryptReturnedNull"] = "DECRYPT_RETURNED_NULL";
417
- CofheErrorCode2["ZkMocksInsertCtHashesFailed"] = "ZK_MOCKS_INSERT_CT_HASHES_FAILED";
418
- CofheErrorCode2["ZkMocksCalcCtHashesFailed"] = "ZK_MOCKS_CALC_CT_HASHES_FAILED";
419
- CofheErrorCode2["ZkMocksVerifySignFailed"] = "ZK_MOCKS_VERIFY_SIGN_FAILED";
420
- CofheErrorCode2["ZkMocksCreateProofSignatureFailed"] = "ZK_MOCKS_CREATE_PROOF_SIGNATURE_FAILED";
421
- CofheErrorCode2["ZkVerifyFailed"] = "ZK_VERIFY_FAILED";
422
- CofheErrorCode2["ZkPackFailed"] = "ZK_PACK_FAILED";
423
- CofheErrorCode2["ZkProveFailed"] = "ZK_PROVE_FAILED";
424
- CofheErrorCode2["EncryptRemainingInItems"] = "ENCRYPT_REMAINING_IN_ITEMS";
425
- CofheErrorCode2["ZkUninitialized"] = "ZK_UNINITIALIZED";
426
- CofheErrorCode2["ZkVerifierUrlUninitialized"] = "ZK_VERIFIER_URL_UNINITIALIZED";
427
- CofheErrorCode2["ThresholdNetworkUrlUninitialized"] = "THRESHOLD_NETWORK_URL_UNINITIALIZED";
428
- CofheErrorCode2["MissingConfig"] = "MISSING_CONFIG";
429
- CofheErrorCode2["UnsupportedChain"] = "UNSUPPORTED_CHAIN";
430
- CofheErrorCode2["MissingZkBuilderAndCrsGenerator"] = "MISSING_ZK_BUILDER_AND_CRS_GENERATOR";
431
- CofheErrorCode2["MissingTfhePublicKeyDeserializer"] = "MISSING_TFHE_PUBLIC_KEY_DESERIALIZER";
432
- CofheErrorCode2["MissingCompactPkeCrsDeserializer"] = "MISSING_COMPACT_PKE_CRS_DESERIALIZER";
433
- CofheErrorCode2["MissingFheKey"] = "MISSING_FHE_KEY";
434
- CofheErrorCode2["MissingCrs"] = "MISSING_CRS";
435
- CofheErrorCode2["FetchKeysFailed"] = "FETCH_KEYS_FAILED";
436
- CofheErrorCode2["PublicWalletGetChainIdFailed"] = "PUBLIC_WALLET_GET_CHAIN_ID_FAILED";
437
- CofheErrorCode2["PublicWalletGetAddressesFailed"] = "PUBLIC_WALLET_GET_ADDRESSES_FAILED";
438
- CofheErrorCode2["RehydrateKeysStoreFailed"] = "REHYDRATE_KEYS_STORE_FAILED";
439
- CofheErrorCode2["TaskManagerUnresolved"] = "TASK_MANAGER_UNRESOLVED";
440
- CofheErrorCode2["BadRequest"] = "BAD_REQUEST";
441
- CofheErrorCode2["UnknownChain"] = "UNKNOWN_CHAIN";
442
- CofheErrorCode2["ACPMalformed"] = "ACP_MALFORMED";
443
- CofheErrorCode2["ACPDenied"] = "ACP_DENIED";
444
- CofheErrorCode2["ACPExpired"] = "ACP_EXPIRED";
445
- CofheErrorCode2["ACPInvalid"] = "ACP_INVALID";
446
- CofheErrorCode2["ACPRevoked"] = "ACP_REVOKED";
447
- CofheErrorCode2["NotPubliclyAllowed"] = "NOT_PUBLICLY_ALLOWED";
448
- CofheErrorCode2["CtNotFound"] = "CT_NOT_FOUND";
449
- CofheErrorCode2["UnsupportedSecurityZone"] = "UNSUPPORTED_SECURITY_ZONE";
450
- CofheErrorCode2["UnsupportedType"] = "UNSUPPORTED_TYPE";
451
- CofheErrorCode2["SigningFailed"] = "SIGNING_FAILED";
452
- CofheErrorCode2["CtSourceError"] = "CT_SOURCE_ERROR";
453
- CofheErrorCode2["ACPVerifierError"] = "ACP_VERIFIER_ERROR";
454
- CofheErrorCode2["CtSourceTimeout"] = "CT_SOURCE_TIMEOUT";
455
- CofheErrorCode2["ACPVerifierTimeout"] = "ACP_VERIFIER_TIMEOUT";
456
- CofheErrorCode2["ACPRequired"] = "ACP_REQUIRED";
457
- CofheErrorCode2["SealFailed"] = "SEAL_FAILED";
458
- return CofheErrorCode2;
459
- })(CofheErrorCode || {});
460
- var CofheError = class _CofheError extends Error {
461
- code;
462
- cause;
463
- hint;
464
- context;
465
- apiErrorCode;
466
- constructor({ code, message, cause, hint, context, apiErrorCode }) {
467
- const fullMessage = cause ? `${message} | Caused by: ${cause.message}` : message;
468
- super(fullMessage);
469
- this.name = "CofheError";
470
- this.code = code;
471
- this.cause = cause;
472
- this.hint = hint;
473
- this.context = context;
474
- this.apiErrorCode = apiErrorCode;
475
- if (Error.captureStackTrace) {
476
- Error.captureStackTrace(this, _CofheError);
477
- }
478
- }
479
- /**
480
- * Creates a CofheError from an unknown error
481
- * If the error is a CofheError, it is returned unchanged, else a new CofheError is created
482
- * If a wrapperError is provided, it is used to create the new CofheError, else a default is used
483
- */
484
- static fromError(error, wrapperError) {
485
- if (isCofheError(error))
486
- return error;
487
- const cause = error instanceof Error ? error : new Error(`${error}`);
488
- return new _CofheError({
489
- code: wrapperError?.code ?? "INTERNAL_ERROR" /* InternalError */,
490
- message: wrapperError?.message ?? "An internal error occurred",
491
- hint: wrapperError?.hint,
492
- context: wrapperError?.context,
493
- apiErrorCode: wrapperError?.apiErrorCode,
494
- cause
495
- });
496
- }
497
- /**
498
- * Serializes the error to JSON string with proper handling of Error objects
499
- */
500
- serialize() {
501
- return bigintSafeJsonStringify({
502
- name: this.name,
503
- code: this.code,
504
- message: this.message,
505
- hint: this.hint,
506
- context: this.context,
507
- apiErrorCode: this.apiErrorCode,
508
- cause: this.cause ? {
509
- name: this.cause.name,
510
- message: this.cause.message,
511
- stack: this.cause.stack
512
- } : void 0,
513
- stack: this.stack
514
- });
515
- }
516
- /**
517
- * Returns a human-readable string representation of the error
518
- */
519
- toString() {
520
- const codeSuffix = this.apiErrorCode ? ` (api: ${this.apiErrorCode})` : "";
521
- const parts = [`${this.name} [${this.code}]${codeSuffix}: ${this.message}`];
522
- if (this.hint) {
523
- parts.push(`Hint: ${this.hint}`);
524
- }
525
- if (this.context && Object.keys(this.context).length > 0) {
526
- parts.push(`Context: ${bigintSafeJsonStringify(this.context)}`);
527
- }
528
- if (this.stack) {
529
- parts.push(`
530
- Stack trace:`);
531
- parts.push(this.stack);
532
- }
533
- if (this.cause) {
534
- parts.push(`
535
- Caused by: ${this.cause.name}: ${this.cause.message}`);
536
- if (this.cause.stack) {
537
- parts.push(this.cause.stack);
538
- }
539
- }
540
- return parts.join("\n");
541
- }
542
- };
543
- var bigintSafeJsonStringify = (value) => {
544
- return JSON.stringify(value, (key, value2) => {
545
- if (typeof value2 === "bigint") {
546
- return `${value2}n`;
547
- }
548
- return value2;
549
- });
550
- };
551
- var isCofheError = (error) => error instanceof CofheError;
552
-
553
- // core/taskManager.ts
554
418
  var ADDRESS_BOOK_ABI = parseAbi([
555
419
  "function getTm(uint256 id) view returns (address)",
556
420
  "error TaskManagerNotSet(uint256 id)"
@@ -857,19 +721,229 @@ var getAclServedAddresses = async (publicClient, chainId) => {
857
721
  } catch {
858
722
  return {};
859
723
  }
724
+ let transient = false;
725
+ const readServedAddress = (functionName) => publicClient.readContract({ address: aclAddress, abi: ACL_SERVED_ADDRESSES_ABI, functionName }).catch((err) => {
726
+ if (revertErrorName(err, ACL_SERVED_ADDRESSES_ABI) === void 0)
727
+ transient = true;
728
+ return void 0;
729
+ });
860
730
  const [defaultRevoker, shareRegistry] = await Promise.all([
861
- publicClient.readContract({ address: aclAddress, abi: ACL_SERVED_ADDRESSES_ABI, functionName: "defaultRevokerContract" }).catch(() => void 0),
862
- publicClient.readContract({ address: aclAddress, abi: ACL_SERVED_ADDRESSES_ABI, functionName: "shareRegistry" }).catch(() => void 0)
731
+ readServedAddress("defaultRevokerContract"),
732
+ readServedAddress("shareRegistry")
863
733
  ]);
864
734
  const resolved = {
865
735
  defaultRevoker: defaultRevoker != null && defaultRevoker !== zeroAddress ? defaultRevoker : void 0,
866
736
  shareRegistry: shareRegistry != null && shareRegistry !== zeroAddress ? shareRegistry : void 0
867
737
  };
868
- aclServedAddressesCache.set(chainId, resolved);
738
+ if (!transient)
739
+ aclServedAddressesCache.set(chainId, resolved);
869
740
  return resolved;
870
741
  };
871
742
 
743
+ // acps/share-metadata/layout.ts
744
+ var LAYOUT_VERSION = 3;
745
+ var ENTRY_KIND = {
746
+ stored: 0,
747
+ event: 1,
748
+ unlabelled: 255
749
+ };
750
+ var ARGS_CODE = {
751
+ none: 0,
752
+ issuer: 1,
753
+ explicit: 255
754
+ };
755
+ var ARG_TAG = {
756
+ word: 0,
757
+ issuer: 1
758
+ };
759
+ var APP_KEY_TAG = 1;
760
+ var BYTE_WIDTH = {
761
+ version: 1,
762
+ /** Length of a list, and a position in it: at most 255 of each per share. */
763
+ count: 1,
764
+ index: 1,
765
+ address: 20,
766
+ selector: 4,
767
+ /** uint32: 4.29 billion blocks. */
768
+ block: 4,
769
+ /** uint16: the position of a transaction within its block. */
770
+ txIndex: 2,
771
+ /** uint16: the block-wide position of a log (a busy block passes 255 logs). */
772
+ logIndex: 2,
773
+ kind: 1,
774
+ argsCode: 1,
775
+ returnWord: 1,
776
+ argTag: 1,
777
+ word: 32
778
+ };
779
+
780
+ // acps/share-metadata/encode.ts
781
+ var uint = (value, width) => numberToHex(value, { size: width });
782
+ var BYTES_FIELD = {
783
+ contract: { width: BYTE_WIDTH.address, name: "a contract address" },
784
+ selector: { width: BYTE_WIDTH.selector, name: "a selector" },
785
+ viewCallArgument: { width: BYTE_WIDTH.word, name: "a view call argument" },
786
+ appKey: { width: BYTE_WIDTH.word, name: "an app key" }
787
+ };
788
+ var bytesField = (field, value) => {
789
+ const { width, name } = BYTES_FIELD[field];
790
+ if (!isHex(value, { strict: true }) || size(value) !== width) {
791
+ throw new Error(`share metadata: ${name} must be ${width} bytes`);
792
+ }
793
+ return value.toLowerCase();
794
+ };
795
+ var listOf = (keyOf, what) => {
796
+ const items = [];
797
+ const positions = /* @__PURE__ */ new Map();
798
+ const insert = (item) => {
799
+ const key = keyOf(item);
800
+ if (positions.has(key))
801
+ return;
802
+ positions.set(key, items.length);
803
+ items.push(item);
804
+ };
805
+ const positionOf = (item) => {
806
+ const position = positions.get(keyOf(item));
807
+ if (position === void 0)
808
+ throw new Error(`share metadata: ${what} ${keyOf(item)} was not collected`);
809
+ return position;
810
+ };
811
+ return { items, insert, positionOf };
812
+ };
813
+ var keyOfContract = (contract) => contract.toLowerCase();
814
+ var keyOfSelector = (selector) => selector.toLowerCase();
815
+ var keyOfTransaction = ({ block, txIndex }) => `${block}:${txIndex}`;
816
+ var transactionOf = ({ block, txIndex }) => ({ block, txIndex });
817
+ var encodeArg = (arg) => {
818
+ if (arg.type === "issuer")
819
+ return uint(ARG_TAG.issuer, BYTE_WIDTH.argTag);
820
+ return concatHex([uint(ARG_TAG.word, BYTE_WIDTH.argTag), bytesField("viewCallArgument", arg.value)]);
821
+ };
822
+ var issuerWordOf = (issuer) => issuer == null ? void 0 : padHex(issuer, { size: BYTE_WIDTH.word }).toLowerCase();
823
+ var asIssuer = (arg, issuerWord) => arg.type === "word" && issuerWord != null && arg.value.toLowerCase() === issuerWord ? { type: "issuer" } : arg;
824
+ var encodeArgs = ({ args: given, returnWord }, issuerWord) => {
825
+ const args = given.map((arg) => asIssuer(arg, issuerWord));
826
+ if (returnWord === 0 && args.length === 0)
827
+ return uint(ARGS_CODE.none, BYTE_WIDTH.argsCode);
828
+ if (returnWord === 0 && args.length === 1 && args[0].type === "issuer") {
829
+ return uint(ARGS_CODE.issuer, BYTE_WIDTH.argsCode);
830
+ }
831
+ return concatHex([
832
+ uint(ARGS_CODE.explicit, BYTE_WIDTH.argsCode),
833
+ uint(returnWord, BYTE_WIDTH.returnWord),
834
+ uint(args.length, BYTE_WIDTH.count),
835
+ ...args.map(encodeArg)
836
+ ]);
837
+ };
838
+ var encodeShareMetadata = (labels, { issuer, appKey } = {}) => {
839
+ const issuerWord = issuerWordOf(issuer);
840
+ const contracts = listOf(keyOfContract, "contract");
841
+ const selectors = listOf(keyOfSelector, "selector");
842
+ const transactions = listOf(keyOfTransaction, "transaction");
843
+ for (const label of labels) {
844
+ if (label.kind === "unlabelled")
845
+ continue;
846
+ contracts.insert(label.contract);
847
+ selectors.insert(label.selector);
848
+ if (label.kind === "event")
849
+ transactions.insert(transactionOf(label));
850
+ }
851
+ const entries = labels.map((label) => {
852
+ switch (label.kind) {
853
+ case "stored":
854
+ return concatHex([
855
+ uint(ENTRY_KIND.stored, BYTE_WIDTH.kind),
856
+ uint(contracts.positionOf(label.contract), BYTE_WIDTH.index),
857
+ uint(selectors.positionOf(label.selector), BYTE_WIDTH.index),
858
+ uint(label.block, BYTE_WIDTH.block),
859
+ encodeArgs(label, issuerWord)
860
+ ]);
861
+ case "event":
862
+ return concatHex([
863
+ uint(ENTRY_KIND.event, BYTE_WIDTH.kind),
864
+ uint(contracts.positionOf(label.contract), BYTE_WIDTH.index),
865
+ uint(transactions.positionOf(transactionOf(label)), BYTE_WIDTH.index),
866
+ uint(label.logIndex, BYTE_WIDTH.logIndex),
867
+ uint(selectors.positionOf(label.selector), BYTE_WIDTH.index)
868
+ ]);
869
+ case "unlabelled":
870
+ return uint(ENTRY_KIND.unlabelled, BYTE_WIDTH.kind);
871
+ }
872
+ });
873
+ return concatHex([
874
+ uint(LAYOUT_VERSION, BYTE_WIDTH.version),
875
+ uint(contracts.items.length, BYTE_WIDTH.count),
876
+ ...contracts.items.map((contract) => bytesField("contract", contract)),
877
+ uint(selectors.items.length, BYTE_WIDTH.count),
878
+ ...selectors.items.map((selector) => bytesField("selector", selector)),
879
+ uint(transactions.items.length, BYTE_WIDTH.count),
880
+ ...transactions.items.map(
881
+ (tx) => concatHex([uint(tx.block, BYTE_WIDTH.block), uint(tx.txIndex, BYTE_WIDTH.txIndex)])
882
+ ),
883
+ ...entries,
884
+ ...appKey == null ? [] : [uint(APP_KEY_TAG, BYTE_WIDTH.count), bytesField("appKey", appKey)]
885
+ ]);
886
+ };
887
+ var findViewFunction = (abi, name, arity) => {
888
+ const named = abi.filter((item) => item.type === "function" && item.name === name);
889
+ if (named.length === 0)
890
+ throw new Error(`share metadata: the ABI has no function ${name}`);
891
+ const fitting = named.filter((fn2) => fn2.inputs.length === arity);
892
+ if (fitting.length !== 1) {
893
+ throw new Error(
894
+ named.length === 1 ? `share metadata: ${name} takes ${named[0].inputs.length} arguments, ${arity} given` : `share metadata: no single overload of ${name} takes ${arity} arguments`
895
+ );
896
+ }
897
+ const [fn] = fitting;
898
+ if (fn.stateMutability !== "view" && fn.stateMutability !== "pure") {
899
+ throw new Error(`share metadata: ${name} is not a view function`);
900
+ }
901
+ return fn;
902
+ };
903
+ var encodeReadArg = (fn, index, value) => {
904
+ const input = fn.inputs[index];
905
+ const word = encodeAbiParameters([input], [value]);
906
+ if (size(word) !== BYTE_WIDTH.word) {
907
+ throw new Error(
908
+ `share metadata: argument ${index} of ${fn.name} is ${input.type}; only arguments of one static 32-byte word can be labelled`
909
+ );
910
+ }
911
+ return { type: "word", value: word };
912
+ };
913
+ var viewFunctionLabel = (options) => {
914
+ const { contract, block, returnWord = 0 } = options;
915
+ const values = options.args ?? [];
916
+ const fn = findViewFunction(options.abi, options.functionName, values.length);
917
+ return {
918
+ kind: "stored",
919
+ contract,
920
+ selector: toFunctionSelector(fn),
921
+ args: values.map((value, i) => encodeReadArg(fn, i, value)),
922
+ returnWord,
923
+ block
924
+ };
925
+ };
926
+ var eventLogLabel = (log) => {
927
+ const { address, blockNumber, transactionIndex, logIndex, topics } = log;
928
+ const topic0 = topics[0];
929
+ if (blockNumber == null || transactionIndex == null || logIndex == null) {
930
+ throw new Error("share metadata: a pending log has no position to label");
931
+ }
932
+ if (topic0 == null || size(topic0) !== 32)
933
+ throw new Error("share metadata: an anonymous log has no event selector");
934
+ return {
935
+ kind: "event",
936
+ contract: address,
937
+ block: blockNumber,
938
+ txIndex: transactionIndex,
939
+ logIndex,
940
+ selector: sliceHex(topic0, 0, 4)
941
+ };
942
+ };
943
+ var UNLABELLED = { kind: "unlabelled" };
944
+
872
945
  // acps/acp.ts
946
+ var sharedByName = (issuer) => `Shared by ${issuer.slice(0, 6)}\u2026${issuer.slice(-4)}`;
873
947
  var ACPUtils = {
874
948
  /**
875
949
  * Create a self acp for personal use
@@ -891,10 +965,16 @@ var ACPUtils = {
891
965
  */
892
966
  createSharing: (options) => {
893
967
  const validation = validateSharingACPOptions(options);
968
+ const { labels, appKey } = options;
969
+ if (labels != null && labels.length !== validation.handles.length) {
970
+ throw new Error(`Cannot label a share of ${validation.handles.length} handles with ${labels.length} labels`);
971
+ }
972
+ const metadata = labels != null || appKey != null ? encodeShareMetadata(labels ?? validation.handles.map(() => UNLABELLED), { issuer: validation.issuer, appKey }) : void 0;
894
973
  const sealingPair = GenerateSealingKey();
895
974
  const acp = {
896
975
  hash: ACPUtils.getHash(validation),
897
976
  ...validation,
977
+ ...metadata != null && { metadata },
898
978
  sealingPrivateKey: sealingPair.privateKey,
899
979
  sealingKey: sealingPair.publicKey,
900
980
  _signedDomain: void 0
@@ -902,9 +982,10 @@ var ACPUtils = {
902
982
  return acp;
903
983
  },
904
984
  /**
905
- * Import a shared acp from various input formats
985
+ * Import a shared acp from various input formats. The acp is named `name`, or after its issuer
986
+ * ("Shared by 0x1234…abcd"); a `name` in the share itself is ignored.
906
987
  */
907
- importShared: (options) => {
988
+ importShared: (options, { name } = {}) => {
908
989
  let parsedOptions;
909
990
  if (typeof options === "string") {
910
991
  try {
@@ -925,6 +1006,7 @@ var ACPUtils = {
925
1006
  const acp = {
926
1007
  hash: ACPUtils.getHash(validation),
927
1008
  ...validation,
1009
+ name: name?.trim() || sharedByName(validation.issuer),
928
1010
  sealingPrivateKey: sealingPair.privateKey,
929
1011
  sealingKey: sealingPair.publicKey,
930
1012
  _signedDomain: void 0
@@ -983,8 +1065,8 @@ var ACPUtils = {
983
1065
  /**
984
1066
  * Import and sign a shared acp in one operation from various input formats
985
1067
  */
986
- importSharedAndSign: async (options, publicClient, walletClient) => {
987
- const acp = ACPUtils.importShared(options);
1068
+ importSharedAndSign: async (options, publicClient, walletClient, importOptions = {}) => {
1069
+ const acp = ACPUtils.importShared(options, importOptions);
988
1070
  return ACPUtils.sign(acp, publicClient, walletClient);
989
1071
  },
990
1072
  /**
@@ -1013,7 +1095,8 @@ var ACPUtils = {
1013
1095
  issuerSignature: acp.issuerSignature,
1014
1096
  recipientSignature: acp.recipientSignature,
1015
1097
  _signedDomain: acp._signedDomain,
1016
- sealingPrivateKey: acp.sealingPrivateKey
1098
+ sealingPrivateKey: acp.sealingPrivateKey,
1099
+ ...acp.metadata != null && { metadata: acp.metadata }
1017
1100
  };
1018
1101
  },
1019
1102
  /**
@@ -1087,7 +1170,7 @@ var ACPUtils = {
1087
1170
  /**
1088
1171
  * Export acp data for sharing (strips the private component).
1089
1172
  * Fixed `SharedACP` shape — every field always present, aligned with
1090
- * `ACPPublic` and the on-chain sharing payload.
1173
+ * `ACPPublic` and the on-chain sharing payload — plus `metadata` when the acp has labels.
1091
1174
  */
1092
1175
  export: (acp) => {
1093
1176
  if (acp.type !== "sharing") {
@@ -1101,7 +1184,6 @@ var ACPUtils = {
1101
1184
  );
1102
1185
  }
1103
1186
  const shared = {
1104
- name: acp.name,
1105
1187
  type: acp.type,
1106
1188
  issuer: acp.issuer,
1107
1189
  expiration: acp.expiration,
@@ -1111,7 +1193,8 @@ var ACPUtils = {
1111
1193
  scope: acp.scope,
1112
1194
  contracts: acp.contracts,
1113
1195
  handles: acp.handles,
1114
- issuerSignature: acp.issuerSignature
1196
+ issuerSignature: acp.issuerSignature,
1197
+ ...acp.metadata != null && acp.metadata !== "0x" && { metadata: acp.metadata }
1115
1198
  };
1116
1199
  return JSON.stringify(shared, void 0, 2);
1117
1200
  },
@@ -1197,11 +1280,14 @@ var ACP_REVOKER_ABI = parseAbi([
1197
1280
  ]);
1198
1281
  var ACP_SHARE_REGISTRY_ABI = parseAbi([
1199
1282
  "struct ACP { address issuer; uint64 expiration; address recipient; uint256 revokerData; address revokerContract; uint8 scope; address[] contracts; bytes32[] handles; bytes32 sealingKey; bytes issuerSignature; bytes recipientSignature; }",
1200
- "function share(ACP calldata acp) external returns (bytes32)",
1283
+ "struct ShareHeader { address issuer; uint64 expiration; address recipient; uint64 blockNumber; address revokerContract; uint256 revokerData; }",
1284
+ "function share(ACP calldata acp, bytes calldata metadata) external returns (bytes32)",
1201
1285
  "function removeShare(bytes32 shareId) external",
1202
- "function sharesFor(address recipient) external view returns (ACP[] memory)",
1203
- "function getShare(bytes32 shareId) external view returns (ACP memory)",
1204
- "function isShareValid(bytes32 shareId) external view returns (bool)"
1286
+ "function sharesFor(address recipient) external view returns (bytes32[] shareIds, ShareHeader[] headers)",
1287
+ "function getShare(bytes32 shareId) external view returns (ShareHeader memory)",
1288
+ "function isShareValid(bytes32 shareId) external view returns (bool)",
1289
+ "event Shared(address indexed recipient, address indexed issuer, bytes32 indexed shareId, ACP acp, bytes metadata)",
1290
+ "event ShareRemoved(address indexed recipient, address indexed issuer, bytes32 indexed shareId)"
1205
1291
  ]);
1206
1292
  var ACP_SHARE_TUPLE_ABI = [
1207
1293
  {
@@ -1237,11 +1323,393 @@ var computeShareId = (acp) => {
1237
1323
  return keccak256(encodeAbiParameters(ACP_SHARE_TUPLE_ABI, [p]));
1238
1324
  };
1239
1325
  var shareIdOfChainShare = (share) => keccak256(encodeAbiParameters(ACP_SHARE_TUPLE_ABI, [share]));
1326
+ var messageOf = (error) => error instanceof Error ? error.message : String(error);
1327
+ var readPostedShares = async (publicClient, registry, headers) => {
1328
+ const idsByBlock = /* @__PURE__ */ new Map();
1329
+ for (const { shareId, header } of headers) {
1330
+ idsByBlock.set(header.blockNumber, [...idsByBlock.get(header.blockNumber) ?? [], shareId]);
1331
+ }
1332
+ const found = /* @__PURE__ */ new Map();
1333
+ await Promise.all(
1334
+ [...idsByBlock].map(async ([block, shareIds]) => {
1335
+ let logs;
1336
+ try {
1337
+ logs = await publicClient.getContractEvents({
1338
+ address: registry,
1339
+ abi: ACP_SHARE_REGISTRY_ABI,
1340
+ eventName: "Shared",
1341
+ args: { shareId: shareIds },
1342
+ fromBlock: block,
1343
+ toBlock: block,
1344
+ strict: true
1345
+ });
1346
+ } catch (error) {
1347
+ for (const shareId of shareIds) {
1348
+ found.set(shareId.toLowerCase(), {
1349
+ reason: `the Shared events of block ${block} could not be read: ${messageOf(error)}`
1350
+ });
1351
+ }
1352
+ return;
1353
+ }
1354
+ for (const { args } of logs) {
1355
+ const hashes = shareIdOfChainShare(args.acp).toLowerCase() === args.shareId.toLowerCase();
1356
+ found.set(
1357
+ args.shareId.toLowerCase(),
1358
+ hashes ? { share: args.acp, metadata: args.metadata } : { reason: "the payload in its Shared event does not hash to its id" }
1359
+ );
1360
+ }
1361
+ })
1362
+ );
1363
+ const result = { posted: [], unreadable: [] };
1364
+ for (const { shareId, header } of headers) {
1365
+ const entry = found.get(shareId.toLowerCase());
1366
+ if (entry == null) {
1367
+ result.unreadable.push({
1368
+ shareId,
1369
+ header,
1370
+ reason: `no Shared event in block ${header.blockNumber}, where the registry points`
1371
+ });
1372
+ } else if ("reason" in entry) {
1373
+ result.unreadable.push({ shareId, header, reason: entry.reason });
1374
+ } else {
1375
+ result.posted.push({ shareId, header, ...entry });
1376
+ }
1377
+ }
1378
+ return result;
1379
+ };
1380
+ var readSharesFor = async (publicClient, registry, recipient) => {
1381
+ const [shareIds, headers] = await publicClient.readContract({
1382
+ address: registry,
1383
+ abi: ACP_SHARE_REGISTRY_ABI,
1384
+ functionName: "sharesFor",
1385
+ args: [recipient]
1386
+ });
1387
+ return readPostedShares(
1388
+ publicClient,
1389
+ registry,
1390
+ shareIds.map((shareId, i) => ({ shareId, header: headers[i] }))
1391
+ );
1392
+ };
1393
+ var readShare = async (publicClient, registry, shareId) => {
1394
+ const header = await publicClient.readContract({
1395
+ address: registry,
1396
+ abi: ACP_SHARE_REGISTRY_ABI,
1397
+ functionName: "getShare",
1398
+ args: [shareId]
1399
+ });
1400
+ if (header.issuer === "0x0000000000000000000000000000000000000000")
1401
+ return null;
1402
+ const { posted, unreadable } = await readPostedShares(publicClient, registry, [{ shareId, header }]);
1403
+ if (unreadable.length > 0)
1404
+ throw new Error(`Share ${shareId}: ${unreadable[0].reason}`);
1405
+ return posted[0];
1406
+ };
1407
+ var readerOf = (blob) => {
1408
+ let offset = 0;
1409
+ const take = (width) => {
1410
+ if (offset + width > size(blob))
1411
+ throw new Error("share metadata: the blob ends in the middle of a field");
1412
+ offset += width;
1413
+ return sliceHex(blob, offset - width, offset);
1414
+ };
1415
+ return {
1416
+ take,
1417
+ takeNumber: (width) => hexToNumber(take(width)),
1418
+ done: () => offset === size(blob),
1419
+ remaining: () => size(blob) - offset
1420
+ };
1421
+ };
1422
+ var takeList = (reader, takeItem) => Array.from({ length: reader.takeNumber(BYTE_WIDTH.count) }, takeItem);
1423
+ var takeFrom = (reader, list, what) => {
1424
+ const position = reader.takeNumber(BYTE_WIDTH.index);
1425
+ if (position >= list.length) {
1426
+ throw new Error(`share metadata: ${what} ${position} is outside the ${what} list`);
1427
+ }
1428
+ return list[position];
1429
+ };
1430
+ var decodeArg = (reader) => {
1431
+ const tag = reader.takeNumber(BYTE_WIDTH.argTag);
1432
+ if (tag === ARG_TAG.issuer)
1433
+ return { type: "issuer" };
1434
+ if (tag === ARG_TAG.word)
1435
+ return { type: "word", value: reader.take(BYTE_WIDTH.word) };
1436
+ throw new Error(`share metadata: unknown argument tag ${tag}`);
1437
+ };
1438
+ var decodeArgs = (reader) => {
1439
+ const code = reader.takeNumber(BYTE_WIDTH.argsCode);
1440
+ if (code === ARGS_CODE.none)
1441
+ return { args: [], returnWord: 0 };
1442
+ if (code === ARGS_CODE.issuer)
1443
+ return { args: [{ type: "issuer" }], returnWord: 0 };
1444
+ if (code !== ARGS_CODE.explicit)
1445
+ throw new Error(`share metadata: unknown args code ${code}`);
1446
+ const returnWord = reader.takeNumber(BYTE_WIDTH.returnWord);
1447
+ return { args: takeList(reader, () => decodeArg(reader)), returnWord };
1448
+ };
1449
+ var takeAppKey = (reader) => {
1450
+ if (reader.done())
1451
+ return void 0;
1452
+ const isKey = reader.remaining() === BYTE_WIDTH.count + BYTE_WIDTH.word && reader.takeNumber(BYTE_WIDTH.count) === APP_KEY_TAG;
1453
+ if (!isKey)
1454
+ throw new Error("share metadata: the blob has more entries than the share has ctHashes");
1455
+ return reader.take(BYTE_WIDTH.word);
1456
+ };
1457
+ var decodeShareMetadataFull = (blob, handles) => {
1458
+ const reader = readerOf(blob);
1459
+ const version = reader.takeNumber(BYTE_WIDTH.version);
1460
+ if (version !== LAYOUT_VERSION)
1461
+ throw new Error(`share metadata: unsupported version ${version}`);
1462
+ const contracts = takeList(reader, () => getAddress(reader.take(BYTE_WIDTH.address)));
1463
+ const selectors = takeList(reader, () => reader.take(BYTE_WIDTH.selector));
1464
+ const transactions = takeList(reader, () => ({
1465
+ block: hexToBigInt(reader.take(BYTE_WIDTH.block)),
1466
+ txIndex: reader.takeNumber(BYTE_WIDTH.txIndex)
1467
+ }));
1468
+ const labels = handles.map(() => {
1469
+ const kind = reader.takeNumber(BYTE_WIDTH.kind);
1470
+ switch (kind) {
1471
+ case ENTRY_KIND.stored: {
1472
+ const contract = takeFrom(reader, contracts, "contract");
1473
+ const selector = takeFrom(reader, selectors, "selector");
1474
+ const block = hexToBigInt(reader.take(BYTE_WIDTH.block));
1475
+ return { kind: "stored", contract, selector, ...decodeArgs(reader), block };
1476
+ }
1477
+ case ENTRY_KIND.event: {
1478
+ const contract = takeFrom(reader, contracts, "contract");
1479
+ const { block, txIndex } = takeFrom(reader, transactions, "transaction");
1480
+ const logIndex = reader.takeNumber(BYTE_WIDTH.logIndex);
1481
+ const selector = takeFrom(reader, selectors, "selector");
1482
+ return { kind: "event", contract, block, txIndex, logIndex, selector };
1483
+ }
1484
+ case ENTRY_KIND.unlabelled:
1485
+ return { kind: "unlabelled" };
1486
+ default:
1487
+ throw new Error(`share metadata: unknown entry kind ${kind}`);
1488
+ }
1489
+ });
1490
+ const appKey = takeAppKey(reader);
1491
+ if (!reader.done())
1492
+ throw new Error("share metadata: the blob has more entries than the share has ctHashes");
1493
+ return { labels, appKey };
1494
+ };
1495
+ var decodeShareMetadata = (blob, handles) => decodeShareMetadataFull(blob, handles).labels;
1496
+ var decodeShareAppKey = (metadata, handles) => metadata == null || metadata === "0x" ? void 0 : decodeShareMetadataFull(metadata, handles).appKey;
1497
+ var sameWord = (a, b) => a.toLowerCase() === b.toLowerCase();
1498
+ var wordsOf = (data) => {
1499
+ const count = Math.floor(size(data) / BYTE_WIDTH.word);
1500
+ return Array.from(
1501
+ { length: count },
1502
+ (_, index) => sliceHex(data, index * BYTE_WIDTH.word, (index + 1) * BYTE_WIDTH.word)
1503
+ );
1504
+ };
1505
+ var storedCallData = (label, issuer) => concatHex([
1506
+ label.selector,
1507
+ ...label.args.map((arg) => arg.type === "issuer" ? padHex(issuer, { size: BYTE_WIDTH.word }) : arg.value)
1508
+ ]);
1509
+ var checkStored = (label, handle, data) => {
1510
+ const found = data ? wordsOf(data)[label.returnWord] : void 0;
1511
+ return found && sameWord(found, handle) ? "verified" : "mismatch";
1512
+ };
1513
+ var checkEvent = (label, handle, logs) => {
1514
+ const log = logs.find((l) => l.logIndex === label.logIndex);
1515
+ if (log == null)
1516
+ return "mismatch";
1517
+ if (log.transactionIndex !== label.txIndex)
1518
+ return "mismatch";
1519
+ const topic0 = log.topics[0];
1520
+ if (topic0 == null || !topic0.toLowerCase().startsWith(label.selector.toLowerCase()))
1521
+ return "mismatch";
1522
+ const carried = [...log.topics.slice(1), ...wordsOf(log.data)].some((word) => word != null && sameWord(word, handle));
1523
+ return carried ? "verified" : "mismatch";
1524
+ };
1525
+ var callAt = async (publicClient, to, data, blockNumber) => (await publicClient.call({ to, data, blockNumber })).data;
1526
+ var logsAt = (publicClient, address, block) => publicClient.getLogs({ address, fromBlock: block, toBlock: block });
1527
+ var verifyShareLabel = async (label, handle, publicClient, issuer) => {
1528
+ try {
1529
+ switch (label.kind) {
1530
+ case "stored":
1531
+ return checkStored(
1532
+ label,
1533
+ handle,
1534
+ await callAt(publicClient, label.contract, storedCallData(label, issuer), label.block)
1535
+ );
1536
+ case "event":
1537
+ return checkEvent(label, handle, await logsAt(publicClient, label.contract, label.block));
1538
+ case "unlabelled":
1539
+ return "unverifiable";
1540
+ }
1541
+ } catch {
1542
+ return "unverifiable";
1543
+ }
1544
+ };
1545
+ var verifyShareLabels = async ({
1546
+ labels,
1547
+ handles,
1548
+ issuer,
1549
+ publicClient,
1550
+ verify = "all"
1551
+ }) => {
1552
+ if (labels.length !== handles.length) {
1553
+ throw new Error(`share metadata: ${labels.length} labels for ${handles.length} ctHashes`);
1554
+ }
1555
+ const requests = /* @__PURE__ */ new Map();
1556
+ const once = (key, request) => {
1557
+ if (!requests.has(key))
1558
+ requests.set(key, request());
1559
+ return requests.get(key);
1560
+ };
1561
+ return Promise.all(
1562
+ labels.map(async (label, i) => {
1563
+ const handle = handles[i];
1564
+ try {
1565
+ switch (label.kind) {
1566
+ case "stored": {
1567
+ if (verify !== "all")
1568
+ return "skipped";
1569
+ const data = storedCallData(label, issuer);
1570
+ const key = `call:${label.contract.toLowerCase()}:${label.block}:${data}`;
1571
+ return checkStored(
1572
+ label,
1573
+ handle,
1574
+ await once(key, () => callAt(publicClient, label.contract, data, label.block))
1575
+ );
1576
+ }
1577
+ case "event": {
1578
+ if (verify === "none")
1579
+ return "skipped";
1580
+ const key = `logs:${label.contract.toLowerCase()}:${label.block}`;
1581
+ return checkEvent(label, handle, await once(key, () => logsAt(publicClient, label.contract, label.block)));
1582
+ }
1583
+ case "unlabelled":
1584
+ return verify === "none" ? "skipped" : "unverifiable";
1585
+ }
1586
+ } catch {
1587
+ return "unverifiable";
1588
+ }
1589
+ })
1590
+ );
1591
+ };
1592
+ var abiOf = (abis, contract) => {
1593
+ const wanted = contract.toLowerCase();
1594
+ const key = Object.keys(abis).find((address) => address.toLowerCase() === wanted);
1595
+ return key ? abis[key] : void 0;
1596
+ };
1597
+ var paramOf = ({ name, type, ...rest }) => {
1598
+ const param = { type };
1599
+ if (name)
1600
+ param.name = name;
1601
+ if ("indexed" in rest && typeof rest.indexed === "boolean")
1602
+ param.indexed = rest.indexed;
1603
+ return param;
1604
+ };
1605
+ var findFunction = (abi, selector) => abi?.find(
1606
+ (item) => item.type === "function" && toFunctionSelector(item).toLowerCase() === selector.toLowerCase()
1607
+ );
1608
+ var findEvent = (abi, selector) => abi?.find(
1609
+ (item) => item.type === "event" && toEventSelector(item).toLowerCase().startsWith(selector.toLowerCase())
1610
+ );
1611
+ var decodeWord = (input, raw) => {
1612
+ if (input == null)
1613
+ return raw;
1614
+ try {
1615
+ return decodeAbiParameters([input], raw)[0];
1616
+ } catch {
1617
+ return raw;
1618
+ }
1619
+ };
1620
+ var describeStored = (base, label, abis, issuer) => {
1621
+ const fn = findFunction(abiOf(abis, label.contract), label.selector);
1622
+ const args = label.args.map((arg, i) => {
1623
+ const input = fn?.inputs[i];
1624
+ const param = input ? paramOf(input) : { type: "bytes32" };
1625
+ if (arg.type === "issuer") {
1626
+ return {
1627
+ ...param,
1628
+ isIssuer: true,
1629
+ raw: issuer ? padHex(issuer, { size: 32 }) : void 0,
1630
+ value: issuer
1631
+ };
1632
+ }
1633
+ return { ...param, isIssuer: false, raw: arg.value, value: decodeWord(input, arg.value) };
1634
+ });
1635
+ const output = fn?.outputs[label.returnWord];
1636
+ return {
1637
+ ...base,
1638
+ kind: "stored",
1639
+ label,
1640
+ contract: label.contract,
1641
+ block: label.block,
1642
+ selector: label.selector,
1643
+ function: fn ? { name: fn.name, signature: toFunctionSignature(fn), inputs: fn.inputs.map(paramOf) } : null,
1644
+ args,
1645
+ output: output ? paramOf(output) : null
1646
+ };
1647
+ };
1648
+ var describeEvent = (base, label, abis) => {
1649
+ const event = findEvent(abiOf(abis, label.contract), label.selector);
1650
+ return {
1651
+ ...base,
1652
+ kind: "event",
1653
+ label,
1654
+ contract: label.contract,
1655
+ block: label.block,
1656
+ txIndex: label.txIndex,
1657
+ logIndex: label.logIndex,
1658
+ selector: label.selector,
1659
+ event: event ? { name: event.name, signature: toEventSignature(event), inputs: event.inputs.map(paramOf) } : null
1660
+ };
1661
+ };
1662
+ var describeShareLabels = (labels, handles, abis = {}, options = {}) => labels.map((label, index) => {
1663
+ const base = { index, handle: handles[index] };
1664
+ switch (label.kind) {
1665
+ case "stored":
1666
+ return describeStored(base, label, abis, options.issuer);
1667
+ case "event":
1668
+ return describeEvent(base, label, abis);
1669
+ case "unlabelled":
1670
+ return { ...base, kind: "unlabelled", label };
1671
+ }
1672
+ });
1673
+ var describeShareMetadata = (share, abis = {}) => {
1674
+ const { issuer, handles, metadata } = share;
1675
+ if (metadata == null || metadata === "0x")
1676
+ return null;
1677
+ return describeShareLabels(decodeShareMetadata(metadata, handles), handles, abis, { issuer });
1678
+ };
1679
+
1680
+ // acps/share-metadata/erc7984.ts
1681
+ var ERC7984_ABI = [
1682
+ {
1683
+ type: "function",
1684
+ name: "confidentialBalanceOf",
1685
+ stateMutability: "view",
1686
+ inputs: [{ name: "account", type: "address", internalType: "address" }],
1687
+ outputs: [{ name: "", type: "bytes32", internalType: "euint64" }]
1688
+ },
1689
+ {
1690
+ type: "event",
1691
+ name: "ConfidentialTransfer",
1692
+ anonymous: false,
1693
+ inputs: [
1694
+ { name: "from", type: "address", indexed: true, internalType: "address" },
1695
+ { name: "to", type: "address", indexed: true, internalType: "address" },
1696
+ { name: "amount", type: "bytes32", indexed: true, internalType: "euint64" }
1697
+ ]
1698
+ }
1699
+ ];
1700
+ var shareAppKey = (app) => keccak256(stringToHex(app));
1240
1701
  var ACP_STORE_DEFAULTS = {
1241
1702
  acps: {},
1242
1703
  activeACPHash: {}
1243
1704
  };
1244
1705
  var ACP_STORE_VERSION = 3;
1706
+ var isRecord = (value) => value != null && typeof value === "object" && !Array.isArray(value);
1707
+ var resolveAccountKey = (records, account) => {
1708
+ if (records == null || account in records)
1709
+ return account;
1710
+ const lower = account.toLowerCase();
1711
+ return Object.keys(records).find((key) => key.toLowerCase() === lower) ?? account;
1712
+ };
1245
1713
  var _acpStore = createStore()(
1246
1714
  persist(() => ACP_STORE_DEFAULTS, {
1247
1715
  name: "cofhesdk-acps",
@@ -1250,12 +1718,23 @@ var _acpStore = createStore()(
1250
1718
  if (version < ACP_STORE_VERSION)
1251
1719
  return ACP_STORE_DEFAULTS;
1252
1720
  return persistedState;
1721
+ },
1722
+ // Runs on every rehydrate, including a corrupt state saved at the current version that
1723
+ // `migrate` passes through unchanged. Consumers that read the raw snapshot (e.g. the React
1724
+ // hooks) never go through `clearStaleStore`, so the shape has to be fixed here.
1725
+ merge: (persistedState, currentState) => {
1726
+ const persisted = isRecord(persistedState) ? persistedState : {};
1727
+ return {
1728
+ ...currentState,
1729
+ acps: isRecord(persisted.acps) ? persisted.acps : currentState.acps,
1730
+ activeACPHash: isRecord(persisted.activeACPHash) ? persisted.activeACPHash : currentState.activeACPHash
1731
+ };
1253
1732
  }
1254
1733
  })
1255
1734
  );
1256
1735
  var clearStaleStore = () => {
1257
1736
  const state = _acpStore.getState();
1258
- const hasExpectedStructure = state && typeof state === "object" && "acps" in state && "activeACPHash" in state && typeof state.acps === "object" && typeof state.activeACPHash === "object";
1737
+ const hasExpectedStructure = isRecord(state) && isRecord(state.acps) && isRecord(state.activeACPHash);
1259
1738
  if (hasExpectedStructure)
1260
1739
  return;
1261
1740
  _acpStore.setState({ acps: {}, activeACPHash: {} });
@@ -1264,7 +1743,8 @@ var getACP = (chainId, account, hash) => {
1264
1743
  clearStaleStore();
1265
1744
  if (chainId == null || account == null || hash == null)
1266
1745
  return;
1267
- const savedACP = _acpStore.getState().acps[chainId]?.[account]?.[hash];
1746
+ const accountACPs = _acpStore.getState().acps[chainId];
1747
+ const savedACP = accountACPs?.[resolveAccountKey(accountACPs, account)]?.[hash];
1268
1748
  if (savedACP == null)
1269
1749
  return;
1270
1750
  return ACPUtils.deserialize(savedACP);
@@ -1273,14 +1753,16 @@ var getActiveACP = (chainId, account) => {
1273
1753
  clearStaleStore();
1274
1754
  if (chainId == null || account == null)
1275
1755
  return;
1276
- const activeACPHash = _acpStore.getState().activeACPHash[chainId]?.[account];
1756
+ const activeHashes = _acpStore.getState().activeACPHash[chainId];
1757
+ const activeACPHash = activeHashes?.[resolveAccountKey(activeHashes, account)];
1277
1758
  return getACP(chainId, account, activeACPHash);
1278
1759
  };
1279
1760
  var getACPs = (chainId, account) => {
1280
1761
  clearStaleStore();
1281
1762
  if (chainId == null || account == null)
1282
1763
  return {};
1283
- return Object.entries(_acpStore.getState().acps[chainId]?.[account] ?? {}).reduce(
1764
+ const accountACPs = _acpStore.getState().acps[chainId];
1765
+ return Object.entries(accountACPs?.[resolveAccountKey(accountACPs, account)] ?? {}).reduce(
1284
1766
  (acc, [hash, acp]) => {
1285
1767
  if (acp == void 0)
1286
1768
  return acc;
@@ -1295,9 +1777,10 @@ var setACP = (chainId, account, acp) => {
1295
1777
  produce((state) => {
1296
1778
  if (state.acps[chainId] == null)
1297
1779
  state.acps[chainId] = {};
1298
- if (state.acps[chainId][account] == null)
1299
- state.acps[chainId][account] = {};
1300
- state.acps[chainId][account][acp.hash] = ACPUtils.serialize(acp);
1780
+ const key = resolveAccountKey(state.acps[chainId], account);
1781
+ if (state.acps[chainId][key] == null)
1782
+ state.acps[chainId][key] = {};
1783
+ state.acps[chainId][key][acp.hash] = ACPUtils.serialize(acp);
1301
1784
  })
1302
1785
  );
1303
1786
  };
@@ -1309,13 +1792,14 @@ var removeACP = (chainId, account, hash) => {
1309
1792
  state.acps[chainId] = {};
1310
1793
  if (state.activeACPHash[chainId] == null)
1311
1794
  state.activeACPHash[chainId] = {};
1312
- const accountACPs = state.acps[chainId][account];
1795
+ const accountACPs = state.acps[chainId][resolveAccountKey(state.acps[chainId], account)];
1313
1796
  if (accountACPs == null)
1314
1797
  return;
1315
1798
  if (accountACPs[hash] == null)
1316
1799
  return;
1317
- if (state.activeACPHash[chainId][account] === hash) {
1318
- state.activeACPHash[chainId][account] = void 0;
1800
+ const activeKey = resolveAccountKey(state.activeACPHash[chainId], account);
1801
+ if (state.activeACPHash[chainId][activeKey] === hash) {
1802
+ state.activeACPHash[chainId][activeKey] = void 0;
1319
1803
  }
1320
1804
  accountACPs[hash] = void 0;
1321
1805
  })
@@ -1325,7 +1809,8 @@ var getActiveACPHash = (chainId, account) => {
1325
1809
  clearStaleStore();
1326
1810
  if (chainId == null || account == null)
1327
1811
  return void 0;
1328
- return _acpStore.getState().activeACPHash[chainId]?.[account];
1812
+ const activeHashes = _acpStore.getState().activeACPHash[chainId];
1813
+ return activeHashes?.[resolveAccountKey(activeHashes, account)];
1329
1814
  };
1330
1815
  var setActiveACPHash = (chainId, account, hash) => {
1331
1816
  clearStaleStore();
@@ -1333,7 +1818,7 @@ var setActiveACPHash = (chainId, account, hash) => {
1333
1818
  produce((state) => {
1334
1819
  if (state.activeACPHash[chainId] == null)
1335
1820
  state.activeACPHash[chainId] = {};
1336
- state.activeACPHash[chainId][account] = hash;
1821
+ state.activeACPHash[chainId][resolveAccountKey(state.activeACPHash[chainId], account)] = hash;
1337
1822
  })
1338
1823
  );
1339
1824
  };
@@ -1342,7 +1827,7 @@ var removeActiveACPHash = (chainId, account) => {
1342
1827
  _acpStore.setState(
1343
1828
  produce((state) => {
1344
1829
  if (state.activeACPHash[chainId])
1345
- state.activeACPHash[chainId][account] = void 0;
1830
+ state.activeACPHash[chainId][resolveAccountKey(state.activeACPHash[chainId], account)] = void 0;
1346
1831
  })
1347
1832
  );
1348
1833
  };
@@ -1363,4 +1848,4 @@ var acpStore = {
1363
1848
  resetStore
1364
1849
  };
1365
1850
 
1366
- export { ACPScope, ACPUtils, ACP_REVOKER_ABI, ACP_SHARE_REGISTRY_ABI, ACP_SHARE_TUPLE_ABI, ACP_STORE_DEFAULTS, CofheError, CofheErrorCode, GenerateSealingKey, ImportACPOptionsValidator, ImportACPValidator, SelfACPOptionsValidator, SelfACPValidator, SharingACPOptionsValidator, SharingACPValidator, SignatureTypes, SignatureUtils, ValidationUtils, _acpStore, acpStore, addressNotZeroSchema, addressSchema, bytesNotEmptySchema, bytesSchema, clearAclServedAddresses, clearStaleStore, computeShareId, contractsSchema, getACP, getACPs, getAclAddress, getAclServedAddresses, getActiveACP, getActiveACPHash, getSignatureTypesAndMessage, getTaskManagerAddress, handlesSchema, isCofheError, removeACP, removeActiveACPHash, resetStore, seal, setACP, setActiveACPHash, shareIdOfChainShare, toChainShare, unsealWithPrivateKey, validateImportACP, validateImportACPOptions, validateSelfACP, validateSelfACPOptions, validateSharingACP, validateSharingACPOptions };
1851
+ export { ACPScope, ACPUtils, ACP_REVOKER_ABI, ACP_SHARE_REGISTRY_ABI, ACP_SHARE_TUPLE_ABI, ACP_STORE_DEFAULTS, ERC7984_ABI, GenerateSealingKey, ImportACPOptionsValidator, ImportACPValidator, LAYOUT_VERSION, SelfACPOptionsValidator, SelfACPValidator, SharingACPOptionsValidator, SharingACPValidator, SignatureTypes, SignatureUtils, UNLABELLED, ValidationUtils, _acpStore, acpStore, addressNotZeroSchema, addressSchema, bytesNotEmptySchema, bytesSchema, clearAclServedAddresses, clearStaleStore, computeShareId, contractsSchema, decodeShareAppKey, decodeShareMetadata, decodeShareMetadataFull, describeShareLabels, describeShareMetadata, encodeShareMetadata, eventLogLabel, getACP, getACPs, getAclAddress, getAclServedAddresses, getActiveACP, getActiveACPHash, getSignatureTypesAndMessage, getTaskManagerAddress, handlesSchema, readPostedShares, readShare, readSharesFor, removeACP, removeActiveACPHash, resetStore, seal, setACP, setActiveACPHash, shareAppKey, shareIdOfChainShare, toChainShare, unsealWithPrivateKey, validateImportACP, validateImportACPOptions, validateSelfACP, validateSelfACPOptions, validateSharingACP, validateSharingACPOptions, verifyShareLabel, verifyShareLabels, viewFunctionLabel };