@cofhe/sdk 0.7.1 → 1.0.0-alpha.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 (123) hide show
  1. package/CHANGELOG.md +35 -0
  2. package/acps/acp.ts +35 -7
  3. package/acps/index.ts +28 -12
  4. package/acps/onchain-utils.ts +188 -49
  5. package/acps/registry.ts +231 -0
  6. package/acps/sealing.ts +9 -3
  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/acp.test.ts +102 -7
  25. package/acps/test/localstorage.test.ts +28 -1
  26. package/acps/test/onchainAccessStatus.test.ts +116 -0
  27. package/acps/test/registry.test.ts +144 -0
  28. package/acps/test/sealing.test.ts +23 -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 +59 -6
  33. package/acps/utils.ts +15 -2
  34. package/acps/validation.ts +3 -1
  35. package/chains/chains/arbitrum.ts +14 -0
  36. package/chains/chains/ethereum.ts +14 -0
  37. package/chains/index.ts +5 -1
  38. package/chains/test/chains.test.ts +24 -3
  39. package/core/acps.ts +164 -207
  40. package/core/client.ts +44 -13
  41. package/core/clientTypes.ts +50 -7
  42. package/core/config.ts +33 -1
  43. package/core/consts.ts +14 -2
  44. package/core/decrypt/apiError.ts +4 -1
  45. package/core/decrypt/verifyDecryptResult.ts +8 -4
  46. package/core/encrypt/cofheMocksZkVerifySign.ts +19 -2
  47. package/core/encrypt/encryptInputsBuilder.ts +6 -0
  48. package/core/encrypt/zkPackProveVerify.ts +7 -1
  49. package/core/error.ts +1 -0
  50. package/core/fetchKeys.ts +2 -2
  51. package/core/index.ts +9 -1
  52. package/core/keyStore.ts +23 -10
  53. package/core/taskManager.ts +48 -0
  54. package/core/test/acpDefaults.test.ts +108 -19
  55. package/core/test/acpStoreAccountCase.test.ts +80 -0
  56. package/core/test/acps.test.ts +128 -3
  57. package/core/test/apiError.test.ts +8 -0
  58. package/core/test/appKeyConfig.test.ts +94 -0
  59. package/core/test/client.test.ts +41 -0
  60. package/core/test/config.test.ts +24 -0
  61. package/core/test/decrypt.test.ts +11 -4
  62. package/core/test/decryptBuilders.test.ts +110 -2
  63. package/core/test/encryptInputsBuilder.test.ts +1 -1
  64. package/core/test/fetchKeys.test.ts +23 -2
  65. package/core/test/incomingShares.test.ts +84 -0
  66. package/core/test/keyStore.test.ts +18 -6
  67. package/core/test/mocksEncryptRange.test.ts +90 -0
  68. package/core/test/taskManager.test.ts +57 -0
  69. package/core/test/utils.test.ts +128 -0
  70. package/core/test/verifyDecryptResult.test.ts +51 -0
  71. package/core/types.ts +8 -0
  72. package/core/utils.ts +16 -9
  73. package/dist/acps.cjs +984 -64
  74. package/dist/acps.d.cts +589 -10
  75. package/dist/acps.d.ts +589 -10
  76. package/dist/acps.js +2 -2
  77. package/dist/chains.cjs +26 -0
  78. package/dist/chains.d.cts +45 -1
  79. package/dist/chains.d.ts +45 -1
  80. package/dist/chains.js +1 -1
  81. package/dist/chunk-6ZW76ZKB.js +228 -0
  82. package/dist/chunk-BQWHPIYU.js +1828 -0
  83. package/dist/chunk-KSOV5B4B.js +50 -0
  84. package/dist/{chunk-43USWPEH.js → chunk-OL3DZN7A.js} +159 -523
  85. package/dist/{chunk-N6IDQRRU.js → chunk-UKQAJK4N.js} +25 -1
  86. package/dist/chunk-XQ32JG3T.js +174 -0
  87. package/dist/{clientTypes-CYZjFznO.d.cts → clientTypes-C0A9ji5O.d.cts} +132 -29
  88. package/dist/{clientTypes-BN3nbzYM.d.ts → clientTypes-CcU5WvdV.d.ts} +132 -29
  89. package/dist/core.cjs +3269 -2626
  90. package/dist/core.d.cts +32 -8
  91. package/dist/core.d.ts +32 -8
  92. package/dist/core.js +5 -4
  93. package/dist/node.cjs +3074 -2436
  94. package/dist/node.d.cts +2 -2
  95. package/dist/node.d.ts +2 -2
  96. package/dist/node.js +20 -19
  97. package/dist/{acp-Wi6isVQI.d.cts → onchain-utils-D9Z33IP7.d.cts} +166 -12
  98. package/dist/{acp-Wi6isVQI.d.ts → onchain-utils-D9Z33IP7.d.ts} +166 -12
  99. package/dist/web.cjs +3204 -2493
  100. package/dist/web.d.cts +49 -3
  101. package/dist/web.d.ts +49 -3
  102. package/dist/web.js +52 -27
  103. package/dist/zkProve.worker.cjs +79 -18
  104. package/dist/zkProve.worker.js +24 -24
  105. package/node/index.ts +1 -10
  106. package/node/storage.ts +20 -9
  107. package/node/test/inherited.test.ts +5 -1
  108. package/node/test/storage-memory-fallback.test.ts +47 -0
  109. package/node/test/storage-permissions.test.ts +40 -0
  110. package/package.json +3 -1
  111. package/web/index.ts +101 -36
  112. package/web/test/countingZkProveWorker.ts +31 -0
  113. package/web/test/inherited.web.test.ts +5 -1
  114. package/web/test/mainThreadPool.web.test.ts +174 -0
  115. package/web/test/threadPoolBenchHelper.ts +134 -0
  116. package/web/test/threadPoolOff.web.test.ts +17 -0
  117. package/web/test/threadPoolOn.web.test.ts +80 -0
  118. package/web/test/workerInitRace.web.test.ts +79 -0
  119. package/web/tfheThreadPool.ts +140 -0
  120. package/web/workerManager.ts +4 -2
  121. package/web/zkProve.worker.ts +42 -26
  122. package/dist/chunk-ESMZCFJY.js +0 -12
  123. package/dist/chunk-Q7CBWGQX.js +0 -1029
@@ -0,0 +1,1828 @@
1
+ import { TASK_MANAGER_ID, COFHE_ADDRESS_BOOK_ADDRESS, CofheError } from './chunk-XQ32JG3T.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
+ import nacl from 'tweetnacl';
4
+ import { z } from 'zod';
5
+ import { createStore } from 'zustand/vanilla';
6
+ import { persist } from 'zustand/middleware';
7
+ import { produce } from 'immer';
8
+
9
+ // acps/utils.ts
10
+ var HEX_BODY_REGEX = /^[0-9a-fA-F]*$/;
11
+ var fromHexString = (hexString) => {
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);
18
+ if (!arr)
19
+ return new Uint8Array();
20
+ return new Uint8Array(arr.map((byte) => parseInt(byte, 16)));
21
+ };
22
+ var toHexString = (bytes) => bytes.reduce((str, byte) => str + byte.toString(16).padStart(2, "0"), "");
23
+ function toBigInt(value) {
24
+ if (typeof value === "string") {
25
+ return BigInt(value);
26
+ } else if (typeof value === "number") {
27
+ return BigInt(value);
28
+ } else if (typeof value === "object") {
29
+ return BigInt("0x" + toHexString(value));
30
+ } else {
31
+ return value;
32
+ }
33
+ }
34
+ function toBeArray(value) {
35
+ const bigIntValue = typeof value === "number" ? BigInt(value) : value;
36
+ const hex = bigIntValue.toString(16);
37
+ const paddedHex = hex.length % 2 === 0 ? hex : "0" + hex;
38
+ return fromHexString(paddedHex);
39
+ }
40
+ function isString(value) {
41
+ if (typeof value !== "string") {
42
+ throw new Error(`Expected value which is \`string\`, received value of type \`${typeof value}\`.`);
43
+ }
44
+ }
45
+ function isNumber(value) {
46
+ const is = typeof value === "number" && !Number.isNaN(value);
47
+ if (!is) {
48
+ throw new Error(`Expected value which is \`number\`, received value of type \`${typeof value}\`.`);
49
+ }
50
+ }
51
+ function isBigIntOrNumber(value) {
52
+ const is = typeof value === "bigint";
53
+ if (!is) {
54
+ try {
55
+ isNumber(value);
56
+ } catch (e) {
57
+ throw new Error(`Value ${value} is not a number or bigint: ${typeof value}`);
58
+ }
59
+ }
60
+ }
61
+
62
+ // acps/sealing.ts
63
+ var KEY_HEX_LENGTH = 64;
64
+ var GenerateSealingKey = () => {
65
+ const sodiumKeypair = nacl.box.keyPair();
66
+ return {
67
+ privateKey: `0x${toHexString(sodiumKeypair.secretKey)}`,
68
+ publicKey: `0x${toHexString(sodiumKeypair.publicKey)}`
69
+ };
70
+ };
71
+ var seal = (value, publicKey) => {
72
+ isString(publicKey);
73
+ isBigIntOrNumber(value);
74
+ assertSealingKey(publicKey, "Public");
75
+ const ephemeralKeyPair = nacl.box.keyPair();
76
+ const nonce = nacl.randomBytes(nacl.box.nonceLength);
77
+ const encryptedMessage = nacl.box(toBeArray(value), nonce, fromHexString(publicKey), ephemeralKeyPair.secretKey);
78
+ return {
79
+ data: encryptedMessage,
80
+ public_key: ephemeralKeyPair.publicKey,
81
+ nonce
82
+ };
83
+ };
84
+ var unsealWithPrivateKey = (privateKey, parsedData) => {
85
+ assertSealingKey(privateKey, "Private");
86
+ const nonce = parsedData.nonce instanceof Uint8Array ? parsedData.nonce : new Uint8Array(parsedData.nonce);
87
+ const ephemPublicKey = parsedData.public_key instanceof Uint8Array ? parsedData.public_key : new Uint8Array(parsedData.public_key);
88
+ const dataToDecrypt = parsedData.data instanceof Uint8Array ? parsedData.data : new Uint8Array(parsedData.data);
89
+ const decryptedMessage = nacl.box.open(dataToDecrypt, nonce, ephemPublicKey, fromHexString(privateKey));
90
+ if (!decryptedMessage) {
91
+ throw new Error("Failed to decrypt message");
92
+ }
93
+ return toBigInt(decryptedMessage);
94
+ };
95
+ var assertSealingKey = (key, kind) => {
96
+ const bare = key.startsWith("0x") ? key.slice(2) : key;
97
+ if (bare.length !== KEY_HEX_LENGTH) {
98
+ throw new Error(`${kind} key must be of length ${KEY_HEX_LENGTH}`);
99
+ }
100
+ if (!/^[0-9a-fA-F]+$/.test(bare)) {
101
+ throw new Error(`${kind} key must contain only hex characters`);
102
+ }
103
+ };
104
+ var ACPScope = {
105
+ Global: 0,
106
+ Contract: 1,
107
+ Handles: 2
108
+ };
109
+ var addressSchema = z.string().refine((val) => isAddress(val), {
110
+ error: "Invalid address"
111
+ }).transform((val) => getAddress(val));
112
+ var addressNotZeroSchema = addressSchema.refine((val) => val !== zeroAddress, {
113
+ error: "Must not be zeroAddress"
114
+ });
115
+ var bytesSchema = z.custom(
116
+ (val) => {
117
+ return typeof val === "string" && isHex(val);
118
+ },
119
+ {
120
+ message: "Invalid hex value"
121
+ }
122
+ );
123
+ var bytesNotEmptySchema = bytesSchema.refine((val) => val !== "0x", {
124
+ error: "Must not be empty"
125
+ });
126
+ var DEFAULT_EXPIRATION_FN = () => Math.round(Date.now() / 1e3) + 7 * 24 * 60 * 60;
127
+ var handlesSchema = z.array(z.string().regex(/^0x[0-9a-fA-F]{64}$/, "Invalid handle: expected 32-byte hex")).optional().default([]);
128
+ var contractsSchema = z.array(addressSchema).optional().default([]);
129
+ var SCOPE_GLOBAL = 0;
130
+ var SCOPE_CONTRACT = 1;
131
+ var SCOPE_HANDLES = 2;
132
+ var ScopeConsistencyRefinement = [
133
+ (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,
134
+ {
135
+ error: "ACP scope :: arrays must match the scope mode (Global: both empty; Contract: contracts only; Handles: handles only)",
136
+ path: ["scope"]
137
+ }
138
+ ];
139
+ var withDerivedScope = (data) => ({
140
+ ...data,
141
+ scope: data.scope ?? (data.contracts.length > 0 ? SCOPE_CONTRACT : data.handles.length > 0 ? SCOPE_HANDLES : SCOPE_GLOBAL)
142
+ });
143
+ var zACPWithDefaults = z.object({
144
+ name: z.string().optional().default("Unnamed ACP"),
145
+ type: z.enum(["self", "sharing", "recipient"]),
146
+ issuer: addressNotZeroSchema,
147
+ expiration: z.int().optional().default(DEFAULT_EXPIRATION_FN),
148
+ recipient: addressSchema.optional().default(zeroAddress),
149
+ revokerData: z.int().optional().default(0),
150
+ revokerContract: addressSchema.optional().default(zeroAddress),
151
+ scope: z.int().min(0).max(2).optional().default(0),
152
+ contracts: contractsSchema,
153
+ handles: handlesSchema,
154
+ issuerSignature: bytesSchema.optional().default("0x"),
155
+ recipientSignature: bytesSchema.optional().default("0x"),
156
+ metadata: bytesSchema.optional()
157
+ });
158
+ var zACPWithSealingKeys = zACPWithDefaults.extend({
159
+ /** X25519 private key, 0x-prefixed 32-byte hex; never leaves the client */
160
+ sealingPrivateKey: z.string().regex(/^0x[0-9a-fA-F]{64}$/, "Invalid sealing private key").optional(),
161
+ sealingKey: z.string().regex(/^0x[0-9a-fA-F]{64}$/, "Invalid sealing key").optional()
162
+ });
163
+ var ExternalValidatorRefinement = [
164
+ (data) => data.revokerData !== 0 && data.revokerContract !== zeroAddress || data.revokerData === 0 && data.revokerContract === zeroAddress,
165
+ {
166
+ error: "ACP external revoker :: revokerData and revokerContract must either both be set or both be unset.",
167
+ path: ["revokerData", "revokerContract"]
168
+ }
169
+ ];
170
+ var RecipientRefinement = [
171
+ (data) => data.issuer !== data.recipient,
172
+ {
173
+ error: "Sharing acp :: issuer and recipient must not be the same",
174
+ path: ["issuer", "recipient"]
175
+ }
176
+ ];
177
+ var SelfACPOptionsValidator = z.object({
178
+ type: z.literal("self").optional().default("self"),
179
+ issuer: addressNotZeroSchema,
180
+ name: z.string().optional().default("Unnamed ACP"),
181
+ expiration: z.int().optional().default(DEFAULT_EXPIRATION_FN),
182
+ recipient: addressSchema.optional().default(zeroAddress),
183
+ revokerData: z.int().optional().default(0),
184
+ revokerContract: addressSchema.optional().default(zeroAddress),
185
+ scope: z.int().min(0).max(2).optional(),
186
+ contracts: contractsSchema,
187
+ handles: handlesSchema,
188
+ issuerSignature: bytesSchema.optional().default("0x"),
189
+ recipientSignature: bytesSchema.optional().default("0x")
190
+ }).refine(...ExternalValidatorRefinement).transform(withDerivedScope).refine(...ScopeConsistencyRefinement);
191
+ var SelfACPValidator = zACPWithSealingKeys.refine((data) => data.type === "self", {
192
+ error: "Type must be 'self'"
193
+ }).refine((data) => data.recipient === zeroAddress, {
194
+ error: "Recipient must be zeroAddress"
195
+ }).refine((data) => data.issuerSignature !== "0x", {
196
+ error: "IssuerSignature must be populated"
197
+ }).refine((data) => data.recipientSignature === "0x", {
198
+ error: "RecipientSignature must be empty"
199
+ }).refine(...ExternalValidatorRefinement);
200
+ var SharingACPOptionsValidator = z.object({
201
+ type: z.literal("sharing").optional().default("sharing"),
202
+ issuer: addressNotZeroSchema,
203
+ recipient: addressNotZeroSchema,
204
+ name: z.string().optional().default("Unnamed ACP"),
205
+ expiration: z.int().optional().default(DEFAULT_EXPIRATION_FN),
206
+ revokerData: z.int().optional().default(0),
207
+ revokerContract: addressSchema.optional().default(zeroAddress),
208
+ scope: z.int().min(0).max(2).optional(),
209
+ contracts: contractsSchema,
210
+ handles: handlesSchema,
211
+ issuerSignature: bytesSchema.optional().default("0x"),
212
+ recipientSignature: bytesSchema.optional().default("0x")
213
+ }).refine(...RecipientRefinement).refine(...ExternalValidatorRefinement).transform(withDerivedScope).refine(...ScopeConsistencyRefinement);
214
+ var SharingACPValidator = zACPWithSealingKeys.refine((data) => data.type === "sharing", {
215
+ error: "Type must be 'sharing'"
216
+ }).refine((data) => data.recipient !== zeroAddress, {
217
+ error: "Recipient must not be zeroAddress"
218
+ }).refine((data) => data.issuerSignature !== "0x", {
219
+ error: "IssuerSignature must be populated"
220
+ }).refine((data) => data.recipientSignature === "0x", {
221
+ error: "RecipientSignature must be empty"
222
+ }).refine(...ExternalValidatorRefinement);
223
+ var ImportACPOptionsValidator = z.object({
224
+ type: z.literal("recipient").optional().default("recipient"),
225
+ issuer: addressNotZeroSchema,
226
+ recipient: addressNotZeroSchema,
227
+ expiration: z.int(),
228
+ revokerData: z.int().optional().default(0),
229
+ revokerContract: addressSchema.optional().default(zeroAddress),
230
+ scope: z.int().min(0).max(2).optional(),
231
+ contracts: contractsSchema,
232
+ handles: handlesSchema,
233
+ issuerSignature: bytesNotEmptySchema,
234
+ recipientSignature: bytesSchema.optional().default("0x"),
235
+ /** The share metadata (labels blob); `0x` means none and is dropped */
236
+ metadata: bytesSchema.optional().transform((val) => val === "0x" ? void 0 : val)
237
+ }).refine(...ExternalValidatorRefinement).transform(withDerivedScope).refine(...ScopeConsistencyRefinement);
238
+ var ImportACPValidator = zACPWithSealingKeys.refine((data) => data.type === "recipient", {
239
+ error: "Type must be 'recipient'"
240
+ }).refine((data) => data.recipient !== zeroAddress, {
241
+ error: "Recipient must not be zeroAddress"
242
+ }).refine((data) => data.issuerSignature !== "0x", {
243
+ error: "IssuerSignature must be populated"
244
+ }).refine((data) => data.recipientSignature !== "0x", {
245
+ error: "RecipientSignature must be populated"
246
+ }).refine(...ExternalValidatorRefinement);
247
+ var safeParseAndThrowFormatted = (schema, data, message) => {
248
+ const result = schema.safeParse(data);
249
+ if (!result.success) {
250
+ throw new Error(`${message}: ${z.prettifyError(result.error)}`, { cause: result.error });
251
+ }
252
+ return result.data;
253
+ };
254
+ var validateSelfACPOptions = (options) => {
255
+ return safeParseAndThrowFormatted(SelfACPOptionsValidator, options, "Invalid self acp options");
256
+ };
257
+ var validateSharingACPOptions = (options) => {
258
+ return safeParseAndThrowFormatted(SharingACPOptionsValidator, options, "Invalid sharing acp options");
259
+ };
260
+ var validateImportACPOptions = (options) => {
261
+ return safeParseAndThrowFormatted(ImportACPOptionsValidator, options, "Invalid import acp options");
262
+ };
263
+ var validateSelfACP = (acp) => {
264
+ return safeParseAndThrowFormatted(SelfACPValidator, acp, "Invalid self acp");
265
+ };
266
+ var validateSharingACP = (acp) => {
267
+ return safeParseAndThrowFormatted(SharingACPValidator, acp, "Invalid sharing acp");
268
+ };
269
+ var validateImportACP = (acp) => {
270
+ return safeParseAndThrowFormatted(ImportACPValidator, acp, "Invalid import acp");
271
+ };
272
+ var ValidationUtils = {
273
+ /**
274
+ * Check if acp is expired
275
+ */
276
+ isExpired: (acp) => {
277
+ return acp.expiration < Math.floor(Date.now() / 1e3);
278
+ },
279
+ /**
280
+ * Check if acp is signed by the active party
281
+ */
282
+ isSigned: (acp) => {
283
+ if (acp.type === "self" || acp.type === "sharing") {
284
+ return acp.issuerSignature !== "0x";
285
+ }
286
+ if (acp.type === "recipient") {
287
+ return acp.recipientSignature !== "0x";
288
+ }
289
+ return false;
290
+ },
291
+ /**
292
+ * Checks that an ACP is signed and not expired.
293
+ */
294
+ isSignedAndNotExpired: (acp) => {
295
+ if (ValidationUtils.isExpired(acp)) {
296
+ return { valid: false, error: "expired" };
297
+ }
298
+ if (!ValidationUtils.isSigned(acp)) {
299
+ return { valid: false, error: "not-signed" };
300
+ }
301
+ return { valid: true, error: null };
302
+ },
303
+ /**
304
+ * Asserts that an ACP is signed and not expired.
305
+ *
306
+ * Throws `Error` with message:
307
+ * - `ACP is expired`
308
+ * - `ACP is not signed`
309
+ */
310
+ assertSignedAndNotExpired: (acp) => {
311
+ const result = ValidationUtils.isSignedAndNotExpired(acp);
312
+ if (result.valid)
313
+ return;
314
+ if (result.error === "expired") {
315
+ throw new Error("ACP is expired");
316
+ }
317
+ if (result.error === "not-signed") {
318
+ throw new Error("ACP is not signed");
319
+ }
320
+ throw new Error("ACP is invalid");
321
+ },
322
+ isValid: (acp) => {
323
+ const schema = acp.type === "self" ? SelfACPValidator : acp.type === "sharing" ? SharingACPValidator : acp.type === "recipient" ? ImportACPValidator : null;
324
+ if (schema == null)
325
+ return { valid: false, error: "invalid-schema" };
326
+ const schemaResult = schema.safeParse(acp);
327
+ if (!schemaResult.success)
328
+ return { valid: false, error: "invalid-schema" };
329
+ return ValidationUtils.isSignedAndNotExpired(acp);
330
+ }
331
+ };
332
+
333
+ // acps/signature.ts
334
+ var ACPSignatureAllFields = [
335
+ { name: "issuer", type: "address" },
336
+ { name: "expiration", type: "uint64" },
337
+ { name: "recipient", type: "address" },
338
+ { name: "revokerData", type: "uint256" },
339
+ { name: "revokerContract", type: "address" },
340
+ { name: "scope", type: "uint8" },
341
+ { name: "contracts", type: "address[]" },
342
+ { name: "handles", type: "bytes32[]" },
343
+ { name: "sealingKey", type: "bytes32" },
344
+ { name: "issuerSignature", type: "bytes" }
345
+ ];
346
+ var SignatureTypes = {
347
+ ACPIssuerSelf: [
348
+ "issuer",
349
+ "expiration",
350
+ "recipient",
351
+ "revokerData",
352
+ "revokerContract",
353
+ "scope",
354
+ "contracts",
355
+ "handles",
356
+ "sealingKey"
357
+ ],
358
+ ACPIssuerShared: [
359
+ "issuer",
360
+ "expiration",
361
+ "recipient",
362
+ "revokerData",
363
+ "revokerContract",
364
+ "scope",
365
+ "contracts",
366
+ "handles"
367
+ ],
368
+ ACPRecipient: ["sealingKey", "issuerSignature"]
369
+ };
370
+ var getSignatureTypesAndMessage = (primaryType, fields, values) => {
371
+ const types = {
372
+ [primaryType]: ACPSignatureAllFields.filter((fieldType) => fields.includes(fieldType.name))
373
+ };
374
+ const message = {};
375
+ fields.forEach((field) => {
376
+ if (field in values) {
377
+ message[field] = values[field];
378
+ }
379
+ });
380
+ return { types, primaryType, message };
381
+ };
382
+ var SignatureUtils = {
383
+ /**
384
+ * Get signature parameters for an ACP
385
+ */
386
+ getSignatureParams: (acp, primaryType) => {
387
+ return getSignatureTypesAndMessage(primaryType, SignatureTypes[primaryType], acp);
388
+ },
389
+ /**
390
+ * Determine the required signature type based on acp type
391
+ */
392
+ getPrimaryType: (acpType) => {
393
+ if (acpType === "self")
394
+ return "ACPIssuerSelf";
395
+ if (acpType === "sharing")
396
+ return "ACPIssuerShared";
397
+ if (acpType === "recipient")
398
+ return "ACPRecipient";
399
+ throw new Error(`Unknown acp type: ${acpType}`);
400
+ }
401
+ };
402
+ var ADDRESS_BOOK_ABI = parseAbi([
403
+ "function getTm(uint256 id) view returns (address)",
404
+ "error TaskManagerNotSet(uint256 id)"
405
+ ]);
406
+ var taskManagerAddressCache = /* @__PURE__ */ new Map();
407
+ var getTaskManagerAddress = async (publicClient, chainId) => {
408
+ const id = chainId ?? publicClient.chain?.id ?? await publicClient.getChainId();
409
+ const cached = taskManagerAddressCache.get(id);
410
+ if (cached != null)
411
+ return cached;
412
+ const pending = publicClient.readContract({
413
+ address: COFHE_ADDRESS_BOOK_ADDRESS,
414
+ abi: ADDRESS_BOOK_ABI,
415
+ functionName: "getTm",
416
+ args: [TASK_MANAGER_ID]
417
+ }).catch((error) => {
418
+ taskManagerAddressCache.delete(id);
419
+ throw CofheError.fromError(error, {
420
+ code: "TASK_MANAGER_UNRESOLVED" /* TaskManagerUnresolved */,
421
+ message: `Failed to resolve the Task Manager from the CoFHEAddressBook on chain ${id}`,
422
+ hint: "Ensure CoFHE is deployed on this chain. On a local hardhat / anvil chain, deploy the CoFHE mocks first.",
423
+ context: { chainId: id, addressBook: COFHE_ADDRESS_BOOK_ADDRESS, taskManagerId: TASK_MANAGER_ID }
424
+ });
425
+ });
426
+ taskManagerAddressCache.set(id, pending);
427
+ return pending;
428
+ };
429
+
430
+ // acps/onchain-utils.ts
431
+ var getAclAddress = async (publicClient) => {
432
+ const ACL_IFACE = "function acl() view returns (address)";
433
+ const aclAbi = parseAbi([ACL_IFACE]);
434
+ return await publicClient.readContract({
435
+ address: await getTaskManagerAddress(publicClient),
436
+ abi: aclAbi,
437
+ functionName: "acl"
438
+ });
439
+ };
440
+ var getAclEIP712Domain = async (publicClient) => {
441
+ const aclAddress = await getAclAddress(publicClient);
442
+ const EIP712_DOMAIN_IFACE = "function eip712Domain() public view returns (bytes1 fields, string name, string version, uint256 chainId, address verifyingContract, bytes32 salt, uint256[] extensions)";
443
+ const domainAbi = parseAbi([EIP712_DOMAIN_IFACE]);
444
+ const domain = await publicClient.readContract({
445
+ address: aclAddress,
446
+ abi: domainAbi,
447
+ functionName: "eip712Domain"
448
+ });
449
+ const [_fields, name, version, chainId, verifyingContract, _salt, _extensions] = domain;
450
+ if (version !== "2") {
451
+ throw new Error(
452
+ `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.`
453
+ );
454
+ }
455
+ return {
456
+ name,
457
+ version,
458
+ chainId: Number(chainId),
459
+ verifyingContract
460
+ };
461
+ };
462
+ var toACPTuple = (acp) => ({
463
+ issuer: acp.issuer,
464
+ expiration: BigInt(acp.expiration),
465
+ recipient: acp.recipient,
466
+ revokerData: BigInt(acp.revokerData),
467
+ revokerContract: acp.revokerContract,
468
+ scope: acp.scope,
469
+ contracts: acp.contracts,
470
+ handles: acp.handles,
471
+ sealingKey: acp.sealingKey,
472
+ issuerSignature: acp.issuerSignature,
473
+ recipientSignature: acp.recipientSignature
474
+ });
475
+ function revertErrorName(err, abi) {
476
+ const walk = err?.walk;
477
+ if (typeof walk === "function") {
478
+ const revertError = walk.call(err, (e) => e?.name === "ContractFunctionRevertedError");
479
+ if (revertError)
480
+ return revertError.data?.errorName ?? "";
481
+ }
482
+ const customErrorName = extractCustomErrorFromDetails(err, abi);
483
+ if (customErrorName)
484
+ return customErrorName;
485
+ const hhDetailsData = extractReturnData(err);
486
+ if (hhDetailsData != null)
487
+ return decodeErrorResult({ abi, data: hhDetailsData }).errorName;
488
+ return void 0;
489
+ }
490
+ var checkACPValidityOnChain = async (acp, publicClient) => {
491
+ const aclAddress = await getAclAddress(publicClient);
492
+ try {
493
+ await publicClient.simulateContract({
494
+ address: aclAddress,
495
+ abi: checkACPValidityAbi,
496
+ functionName: "checkPermissionValidity",
497
+ args: [toACPTuple(acp)]
498
+ });
499
+ return true;
500
+ } catch (err) {
501
+ const errorName = revertErrorName(err, checkACPValidityAbi);
502
+ if (errorName !== void 0)
503
+ throw new Error(errorName);
504
+ throw err;
505
+ }
506
+ };
507
+ var PERMISSION_ERROR_STATUS = {
508
+ PermissionInvalid_Expired: "expired",
509
+ PermissionInvalid_Disabled: "revoked",
510
+ PermissionInvalid_IssuerSignature: "invalid-issuer-signature",
511
+ PermissionInvalid_RecipientSignature: "invalid-recipient-signature"
512
+ };
513
+ var getACPAccessStatusOnChain = async (acp, publicClient, handle) => {
514
+ const aclAddress = await getAclAddress(publicClient);
515
+ try {
516
+ if (handle === void 0) {
517
+ await publicClient.simulateContract({
518
+ address: aclAddress,
519
+ abi: acpAccessAbi,
520
+ functionName: "checkPermissionValidity",
521
+ args: [toACPTuple(acp)]
522
+ });
523
+ return "valid";
524
+ }
525
+ const ctHash = BigInt(handle);
526
+ const allowed = await publicClient.readContract({
527
+ address: aclAddress,
528
+ abi: acpAccessAbi,
529
+ functionName: "isAllowedWithPermission",
530
+ args: [toACPTuple(acp), ctHash]
531
+ });
532
+ if (allowed)
533
+ return "allowed";
534
+ const issuerAllowed = await publicClient.readContract({
535
+ address: aclAddress,
536
+ abi: acpAccessAbi,
537
+ functionName: "isAllowed",
538
+ args: [ctHash, acp.issuer]
539
+ });
540
+ return issuerAllowed ? "out-of-scope" : "issuer-not-allowed";
541
+ } catch (err) {
542
+ const status = PERMISSION_ERROR_STATUS[revertErrorName(err, acpAccessAbi) ?? ""];
543
+ if (status)
544
+ return status;
545
+ throw err;
546
+ }
547
+ };
548
+ function extractCustomErrorFromDetails(err, abi) {
549
+ const anyErr = err;
550
+ const details = anyErr?.details ?? anyErr?.cause?.details;
551
+ if (typeof details === "string") {
552
+ const customErrorMatch = details.match(/reverted with custom error '(\w+)\(\)'/);
553
+ if (customErrorMatch) {
554
+ const errorName = customErrorMatch[1];
555
+ const errorExists = abi.some((item) => item.type === "error" && item.name === errorName);
556
+ if (errorExists) {
557
+ return errorName;
558
+ }
559
+ }
560
+ }
561
+ return void 0;
562
+ }
563
+ function extractReturnData(err) {
564
+ const anyErr = err;
565
+ const s = anyErr?.details ?? anyErr?.cause?.details ?? anyErr?.shortMessage ?? anyErr?.message ?? String(err);
566
+ const match = s.match(/return data:\s*(0x[a-fA-F0-9]+)/) ?? s.match(/custom error (0x[a-fA-F0-9]{8,})/);
567
+ return match?.[1];
568
+ }
569
+ var checkACPValidityAbi = [
570
+ {
571
+ type: "function",
572
+ name: "checkPermissionValidity",
573
+ inputs: [
574
+ {
575
+ name: "acp",
576
+ type: "tuple",
577
+ internalType: "struct ACP",
578
+ components: [
579
+ {
580
+ name: "issuer",
581
+ type: "address",
582
+ internalType: "address"
583
+ },
584
+ {
585
+ name: "expiration",
586
+ type: "uint64",
587
+ internalType: "uint64"
588
+ },
589
+ {
590
+ name: "recipient",
591
+ type: "address",
592
+ internalType: "address"
593
+ },
594
+ {
595
+ name: "revokerData",
596
+ type: "uint256",
597
+ internalType: "uint256"
598
+ },
599
+ {
600
+ name: "revokerContract",
601
+ type: "address",
602
+ internalType: "address"
603
+ },
604
+ {
605
+ name: "scope",
606
+ type: "uint8",
607
+ internalType: "uint8"
608
+ },
609
+ {
610
+ name: "contracts",
611
+ type: "address[]",
612
+ internalType: "address[]"
613
+ },
614
+ {
615
+ name: "handles",
616
+ type: "bytes32[]",
617
+ internalType: "bytes32[]"
618
+ },
619
+ {
620
+ name: "sealingKey",
621
+ type: "bytes32",
622
+ internalType: "bytes32"
623
+ },
624
+ {
625
+ name: "issuerSignature",
626
+ type: "bytes",
627
+ internalType: "bytes"
628
+ },
629
+ {
630
+ name: "recipientSignature",
631
+ type: "bytes",
632
+ internalType: "bytes"
633
+ }
634
+ ]
635
+ }
636
+ ],
637
+ outputs: [
638
+ {
639
+ name: "",
640
+ type: "bool",
641
+ internalType: "bool"
642
+ }
643
+ ],
644
+ stateMutability: "view"
645
+ },
646
+ {
647
+ type: "error",
648
+ name: "PermissionInvalid_Disabled",
649
+ inputs: []
650
+ },
651
+ {
652
+ type: "error",
653
+ name: "PermissionInvalid_Expired",
654
+ inputs: []
655
+ },
656
+ {
657
+ type: "error",
658
+ name: "PermissionInvalid_IssuerSignature",
659
+ inputs: []
660
+ },
661
+ {
662
+ type: "error",
663
+ name: "PermissionInvalid_RecipientSignature",
664
+ inputs: []
665
+ }
666
+ ];
667
+ var acpAccessAbi = [
668
+ ...checkACPValidityAbi,
669
+ {
670
+ type: "function",
671
+ name: "isAllowedWithPermission",
672
+ inputs: [checkACPValidityAbi[0].inputs[0], { name: "handle", type: "uint256", internalType: "uint256" }],
673
+ outputs: [{ name: "", type: "bool", internalType: "bool" }],
674
+ stateMutability: "view"
675
+ },
676
+ {
677
+ type: "function",
678
+ name: "isAllowed",
679
+ inputs: [
680
+ { name: "handle", type: "uint256", internalType: "uint256" },
681
+ { name: "account", type: "address", internalType: "address" }
682
+ ],
683
+ outputs: [{ name: "", type: "bool", internalType: "bool" }],
684
+ stateMutability: "view"
685
+ }
686
+ ];
687
+ var ACL_SERVED_ADDRESSES_ABI = parseAbi([
688
+ "function acl() view returns (address)",
689
+ "function defaultRevokerContract() view returns (address)",
690
+ "function shareRegistry() view returns (address)"
691
+ ]);
692
+ var aclServedAddressesCache = /* @__PURE__ */ new Map();
693
+ var clearAclServedAddresses = () => aclServedAddressesCache.clear();
694
+ var getAclServedAddresses = async (publicClient, chainId) => {
695
+ const cached = aclServedAddressesCache.get(chainId);
696
+ if (cached != null)
697
+ return cached;
698
+ let aclAddress;
699
+ try {
700
+ aclAddress = await publicClient.readContract({
701
+ address: await getTaskManagerAddress(publicClient, chainId),
702
+ abi: ACL_SERVED_ADDRESSES_ABI,
703
+ functionName: "acl"
704
+ });
705
+ } catch {
706
+ return {};
707
+ }
708
+ const [defaultRevoker, shareRegistry] = await Promise.all([
709
+ publicClient.readContract({ address: aclAddress, abi: ACL_SERVED_ADDRESSES_ABI, functionName: "defaultRevokerContract" }).catch(() => void 0),
710
+ publicClient.readContract({ address: aclAddress, abi: ACL_SERVED_ADDRESSES_ABI, functionName: "shareRegistry" }).catch(() => void 0)
711
+ ]);
712
+ const resolved = {
713
+ defaultRevoker: defaultRevoker != null && defaultRevoker !== zeroAddress ? defaultRevoker : void 0,
714
+ shareRegistry: shareRegistry != null && shareRegistry !== zeroAddress ? shareRegistry : void 0
715
+ };
716
+ aclServedAddressesCache.set(chainId, resolved);
717
+ return resolved;
718
+ };
719
+
720
+ // acps/share-metadata/layout.ts
721
+ var LAYOUT_VERSION = 3;
722
+ var ENTRY_KIND = {
723
+ stored: 0,
724
+ event: 1,
725
+ unlabelled: 255
726
+ };
727
+ var ARGS_CODE = {
728
+ none: 0,
729
+ issuer: 1,
730
+ explicit: 255
731
+ };
732
+ var ARG_TAG = {
733
+ word: 0,
734
+ issuer: 1
735
+ };
736
+ var APP_KEY_TAG = 1;
737
+ var BYTE_WIDTH = {
738
+ version: 1,
739
+ /** Length of a list, and a position in it: at most 255 of each per share. */
740
+ count: 1,
741
+ index: 1,
742
+ address: 20,
743
+ selector: 4,
744
+ /** uint32: 4.29 billion blocks. */
745
+ block: 4,
746
+ /** uint16: the position of a transaction within its block. */
747
+ txIndex: 2,
748
+ /** uint16: the block-wide position of a log (a busy block passes 255 logs). */
749
+ logIndex: 2,
750
+ kind: 1,
751
+ argsCode: 1,
752
+ returnWord: 1,
753
+ argTag: 1,
754
+ word: 32
755
+ };
756
+
757
+ // acps/share-metadata/encode.ts
758
+ var uint = (value, width) => numberToHex(value, { size: width });
759
+ var BYTES_FIELD = {
760
+ contract: { width: BYTE_WIDTH.address, name: "a contract address" },
761
+ selector: { width: BYTE_WIDTH.selector, name: "a selector" },
762
+ viewCallArgument: { width: BYTE_WIDTH.word, name: "a view call argument" },
763
+ appKey: { width: BYTE_WIDTH.word, name: "an app key" }
764
+ };
765
+ var bytesField = (field, value) => {
766
+ const { width, name } = BYTES_FIELD[field];
767
+ if (!isHex(value, { strict: true }) || size(value) !== width) {
768
+ throw new Error(`share metadata: ${name} must be ${width} bytes`);
769
+ }
770
+ return value.toLowerCase();
771
+ };
772
+ var listOf = (keyOf, what) => {
773
+ const items = [];
774
+ const positions = /* @__PURE__ */ new Map();
775
+ const insert = (item) => {
776
+ const key = keyOf(item);
777
+ if (positions.has(key))
778
+ return;
779
+ positions.set(key, items.length);
780
+ items.push(item);
781
+ };
782
+ const positionOf = (item) => {
783
+ const position = positions.get(keyOf(item));
784
+ if (position === void 0)
785
+ throw new Error(`share metadata: ${what} ${keyOf(item)} was not collected`);
786
+ return position;
787
+ };
788
+ return { items, insert, positionOf };
789
+ };
790
+ var keyOfContract = (contract) => contract.toLowerCase();
791
+ var keyOfSelector = (selector) => selector.toLowerCase();
792
+ var keyOfTransaction = ({ block, txIndex }) => `${block}:${txIndex}`;
793
+ var transactionOf = ({ block, txIndex }) => ({ block, txIndex });
794
+ var encodeArg = (arg) => {
795
+ if (arg.type === "issuer")
796
+ return uint(ARG_TAG.issuer, BYTE_WIDTH.argTag);
797
+ return concatHex([uint(ARG_TAG.word, BYTE_WIDTH.argTag), bytesField("viewCallArgument", arg.value)]);
798
+ };
799
+ var issuerWordOf = (issuer) => issuer == null ? void 0 : padHex(issuer, { size: BYTE_WIDTH.word }).toLowerCase();
800
+ var asIssuer = (arg, issuerWord) => arg.type === "word" && issuerWord != null && arg.value.toLowerCase() === issuerWord ? { type: "issuer" } : arg;
801
+ var encodeArgs = ({ args: given, returnWord }, issuerWord) => {
802
+ const args = given.map((arg) => asIssuer(arg, issuerWord));
803
+ if (returnWord === 0 && args.length === 0)
804
+ return uint(ARGS_CODE.none, BYTE_WIDTH.argsCode);
805
+ if (returnWord === 0 && args.length === 1 && args[0].type === "issuer") {
806
+ return uint(ARGS_CODE.issuer, BYTE_WIDTH.argsCode);
807
+ }
808
+ return concatHex([
809
+ uint(ARGS_CODE.explicit, BYTE_WIDTH.argsCode),
810
+ uint(returnWord, BYTE_WIDTH.returnWord),
811
+ uint(args.length, BYTE_WIDTH.count),
812
+ ...args.map(encodeArg)
813
+ ]);
814
+ };
815
+ var encodeShareMetadata = (labels, { issuer, appKey } = {}) => {
816
+ const issuerWord = issuerWordOf(issuer);
817
+ const contracts = listOf(keyOfContract, "contract");
818
+ const selectors = listOf(keyOfSelector, "selector");
819
+ const transactions = listOf(keyOfTransaction, "transaction");
820
+ for (const label of labels) {
821
+ if (label.kind === "unlabelled")
822
+ continue;
823
+ contracts.insert(label.contract);
824
+ selectors.insert(label.selector);
825
+ if (label.kind === "event")
826
+ transactions.insert(transactionOf(label));
827
+ }
828
+ const entries = labels.map((label) => {
829
+ switch (label.kind) {
830
+ case "stored":
831
+ return concatHex([
832
+ uint(ENTRY_KIND.stored, BYTE_WIDTH.kind),
833
+ uint(contracts.positionOf(label.contract), BYTE_WIDTH.index),
834
+ uint(selectors.positionOf(label.selector), BYTE_WIDTH.index),
835
+ uint(label.block, BYTE_WIDTH.block),
836
+ encodeArgs(label, issuerWord)
837
+ ]);
838
+ case "event":
839
+ return concatHex([
840
+ uint(ENTRY_KIND.event, BYTE_WIDTH.kind),
841
+ uint(contracts.positionOf(label.contract), BYTE_WIDTH.index),
842
+ uint(transactions.positionOf(transactionOf(label)), BYTE_WIDTH.index),
843
+ uint(label.logIndex, BYTE_WIDTH.logIndex),
844
+ uint(selectors.positionOf(label.selector), BYTE_WIDTH.index)
845
+ ]);
846
+ case "unlabelled":
847
+ return uint(ENTRY_KIND.unlabelled, BYTE_WIDTH.kind);
848
+ }
849
+ });
850
+ return concatHex([
851
+ uint(LAYOUT_VERSION, BYTE_WIDTH.version),
852
+ uint(contracts.items.length, BYTE_WIDTH.count),
853
+ ...contracts.items.map((contract) => bytesField("contract", contract)),
854
+ uint(selectors.items.length, BYTE_WIDTH.count),
855
+ ...selectors.items.map((selector) => bytesField("selector", selector)),
856
+ uint(transactions.items.length, BYTE_WIDTH.count),
857
+ ...transactions.items.map(
858
+ (tx) => concatHex([uint(tx.block, BYTE_WIDTH.block), uint(tx.txIndex, BYTE_WIDTH.txIndex)])
859
+ ),
860
+ ...entries,
861
+ ...appKey == null ? [] : [uint(APP_KEY_TAG, BYTE_WIDTH.count), bytesField("appKey", appKey)]
862
+ ]);
863
+ };
864
+ var findViewFunction = (abi, name, arity) => {
865
+ const named = abi.filter((item) => item.type === "function" && item.name === name);
866
+ if (named.length === 0)
867
+ throw new Error(`share metadata: the ABI has no function ${name}`);
868
+ const fitting = named.filter((fn2) => fn2.inputs.length === arity);
869
+ if (fitting.length !== 1) {
870
+ throw new Error(
871
+ named.length === 1 ? `share metadata: ${name} takes ${named[0].inputs.length} arguments, ${arity} given` : `share metadata: no single overload of ${name} takes ${arity} arguments`
872
+ );
873
+ }
874
+ const [fn] = fitting;
875
+ if (fn.stateMutability !== "view" && fn.stateMutability !== "pure") {
876
+ throw new Error(`share metadata: ${name} is not a view function`);
877
+ }
878
+ return fn;
879
+ };
880
+ var encodeReadArg = (fn, index, value) => {
881
+ const input = fn.inputs[index];
882
+ const word = encodeAbiParameters([input], [value]);
883
+ if (size(word) !== BYTE_WIDTH.word) {
884
+ throw new Error(
885
+ `share metadata: argument ${index} of ${fn.name} is ${input.type}; only arguments of one static 32-byte word can be labelled`
886
+ );
887
+ }
888
+ return { type: "word", value: word };
889
+ };
890
+ var viewFunctionLabel = (options) => {
891
+ const { contract, block, returnWord = 0 } = options;
892
+ const values = options.args ?? [];
893
+ const fn = findViewFunction(options.abi, options.functionName, values.length);
894
+ return {
895
+ kind: "stored",
896
+ contract,
897
+ selector: toFunctionSelector(fn),
898
+ args: values.map((value, i) => encodeReadArg(fn, i, value)),
899
+ returnWord,
900
+ block
901
+ };
902
+ };
903
+ var eventLogLabel = (log) => {
904
+ const { address, blockNumber, transactionIndex, logIndex, topics } = log;
905
+ const topic0 = topics[0];
906
+ if (blockNumber == null || transactionIndex == null || logIndex == null) {
907
+ throw new Error("share metadata: a pending log has no position to label");
908
+ }
909
+ if (topic0 == null || size(topic0) !== 32)
910
+ throw new Error("share metadata: an anonymous log has no event selector");
911
+ return {
912
+ kind: "event",
913
+ contract: address,
914
+ block: blockNumber,
915
+ txIndex: transactionIndex,
916
+ logIndex,
917
+ selector: sliceHex(topic0, 0, 4)
918
+ };
919
+ };
920
+ var UNLABELLED = { kind: "unlabelled" };
921
+
922
+ // acps/acp.ts
923
+ var sharedByName = (issuer) => `Shared by ${issuer.slice(0, 6)}\u2026${issuer.slice(-4)}`;
924
+ var ACPUtils = {
925
+ /**
926
+ * Create a self acp for personal use
927
+ */
928
+ createSelf: (options) => {
929
+ const validation = validateSelfACPOptions(options);
930
+ const sealingPair = GenerateSealingKey();
931
+ const acp = {
932
+ hash: ACPUtils.getHash(validation),
933
+ ...validation,
934
+ sealingPrivateKey: sealingPair.privateKey,
935
+ sealingKey: sealingPair.publicKey,
936
+ _signedDomain: void 0
937
+ };
938
+ return acp;
939
+ },
940
+ /**
941
+ * Create a sharing acp to be shared with another user
942
+ */
943
+ createSharing: (options) => {
944
+ const validation = validateSharingACPOptions(options);
945
+ const { labels, appKey } = options;
946
+ if (labels != null && labels.length !== validation.handles.length) {
947
+ throw new Error(`Cannot label a share of ${validation.handles.length} handles with ${labels.length} labels`);
948
+ }
949
+ const metadata = labels != null || appKey != null ? encodeShareMetadata(labels ?? validation.handles.map(() => UNLABELLED), { issuer: validation.issuer, appKey }) : void 0;
950
+ const sealingPair = GenerateSealingKey();
951
+ const acp = {
952
+ hash: ACPUtils.getHash(validation),
953
+ ...validation,
954
+ ...metadata != null && { metadata },
955
+ sealingPrivateKey: sealingPair.privateKey,
956
+ sealingKey: sealingPair.publicKey,
957
+ _signedDomain: void 0
958
+ };
959
+ return acp;
960
+ },
961
+ /**
962
+ * Import a shared acp from various input formats. The acp is named `name`, or after its issuer
963
+ * ("Shared by 0x1234…abcd"); a `name` in the share itself is ignored.
964
+ */
965
+ importShared: (options, { name } = {}) => {
966
+ let parsedOptions;
967
+ if (typeof options === "string") {
968
+ try {
969
+ parsedOptions = JSON.parse(options);
970
+ } catch (error) {
971
+ throw new Error(`Failed to parse JSON string: ${error}`);
972
+ }
973
+ } else if (typeof options === "object" && options !== null) {
974
+ parsedOptions = options;
975
+ } else {
976
+ throw new Error("Invalid input type, expected ImportSharedACPOptions, object, or string");
977
+ }
978
+ if (parsedOptions.type != null && parsedOptions.type !== "sharing") {
979
+ throw new Error(`Invalid acp type <${parsedOptions.type}>, must be "sharing"`);
980
+ }
981
+ const validation = validateImportACPOptions({ ...parsedOptions, type: "recipient" });
982
+ const sealingPair = GenerateSealingKey();
983
+ const acp = {
984
+ hash: ACPUtils.getHash(validation),
985
+ ...validation,
986
+ name: name?.trim() || sharedByName(validation.issuer),
987
+ sealingPrivateKey: sealingPair.privateKey,
988
+ sealingKey: sealingPair.publicKey,
989
+ _signedDomain: void 0
990
+ };
991
+ return acp;
992
+ },
993
+ /**
994
+ * Sign an ACP with the provided wallet client
995
+ */
996
+ sign: async (acp, publicClient, walletClient) => {
997
+ if (walletClient == null || walletClient.account == null) {
998
+ throw new Error(
999
+ "Missing walletClient, you must pass in a `walletClient` for the connected user to create an ACP signature"
1000
+ );
1001
+ }
1002
+ const primaryType = SignatureUtils.getPrimaryType(acp.type);
1003
+ const domain = await getAclEIP712Domain(publicClient);
1004
+ const { types, message } = SignatureUtils.getSignatureParams(ACPUtils.getPublic(acp, true), primaryType);
1005
+ const signature = await walletClient.signTypedData({
1006
+ domain,
1007
+ types,
1008
+ primaryType,
1009
+ message,
1010
+ account: walletClient.account
1011
+ });
1012
+ let updatedACP;
1013
+ if (acp.type === "self" || acp.type === "sharing") {
1014
+ updatedACP = {
1015
+ ...acp,
1016
+ issuerSignature: signature,
1017
+ _signedDomain: domain
1018
+ };
1019
+ } else {
1020
+ updatedACP = {
1021
+ ...acp,
1022
+ recipientSignature: signature,
1023
+ _signedDomain: domain
1024
+ };
1025
+ }
1026
+ return updatedACP;
1027
+ },
1028
+ /**
1029
+ * Create and sign a self acp in one operation
1030
+ */
1031
+ createSelfAndSign: async (options, publicClient, walletClient) => {
1032
+ const acp = ACPUtils.createSelf(options);
1033
+ return ACPUtils.sign(acp, publicClient, walletClient);
1034
+ },
1035
+ /**
1036
+ * Create and sign a sharing acp in one operation
1037
+ */
1038
+ createSharingAndSign: async (options, publicClient, walletClient) => {
1039
+ const acp = ACPUtils.createSharing(options);
1040
+ return ACPUtils.sign(acp, publicClient, walletClient);
1041
+ },
1042
+ /**
1043
+ * Import and sign a shared acp in one operation from various input formats
1044
+ */
1045
+ importSharedAndSign: async (options, publicClient, walletClient, importOptions = {}) => {
1046
+ const acp = ACPUtils.importShared(options, importOptions);
1047
+ return ACPUtils.sign(acp, publicClient, walletClient);
1048
+ },
1049
+ /**
1050
+ * Deserialize an ACP from serialized data
1051
+ */
1052
+ deserialize: (data) => {
1053
+ return { ...data };
1054
+ },
1055
+ /**
1056
+ * Serialize an ACP for storage
1057
+ */
1058
+ serialize: (acp) => {
1059
+ return {
1060
+ hash: acp.hash,
1061
+ name: acp.name,
1062
+ type: acp.type,
1063
+ issuer: acp.issuer,
1064
+ expiration: acp.expiration,
1065
+ recipient: acp.recipient,
1066
+ revokerData: acp.revokerData,
1067
+ revokerContract: acp.revokerContract,
1068
+ scope: acp.scope,
1069
+ contracts: acp.contracts,
1070
+ handles: acp.handles,
1071
+ sealingKey: acp.sealingKey,
1072
+ issuerSignature: acp.issuerSignature,
1073
+ recipientSignature: acp.recipientSignature,
1074
+ _signedDomain: acp._signedDomain,
1075
+ sealingPrivateKey: acp.sealingPrivateKey,
1076
+ ...acp.metadata != null && { metadata: acp.metadata }
1077
+ };
1078
+ },
1079
+ /**
1080
+ * Validate an ACP (schema-level validation)
1081
+ */
1082
+ validateSchema: (acp) => {
1083
+ if (acp.type === "self") {
1084
+ return validateSelfACP(acp);
1085
+ } else if (acp.type === "sharing") {
1086
+ return validateSharingACP(acp);
1087
+ } else if (acp.type === "recipient") {
1088
+ return validateImportACP(acp);
1089
+ } else {
1090
+ throw new Error("Invalid ACP type");
1091
+ }
1092
+ },
1093
+ /**
1094
+ * Validate an ACP (holistic validation).
1095
+ *
1096
+ * This validates:
1097
+ * - ACP schema (shape + invariants)
1098
+ * - ACP is signed
1099
+ * - ACP is not expired
1100
+ *
1101
+ * For schema-only validation, use `validateSchema(acp)`.
1102
+ */
1103
+ validate: (acp) => {
1104
+ const validated = ACPUtils.validateSchema(acp);
1105
+ ValidationUtils.assertSignedAndNotExpired(validated);
1106
+ return validated;
1107
+ },
1108
+ /**
1109
+ * Get the public component of an ACP — the signed struct sent on-chain / to the decryption backend.
1110
+ * Strips the private component (hash, name, type, sealing pair).
1111
+ */
1112
+ getPublic: (acp, skipValidation = false) => {
1113
+ if (!skipValidation) {
1114
+ ACPUtils.validateSchema(acp);
1115
+ }
1116
+ return {
1117
+ issuer: acp.issuer,
1118
+ expiration: acp.expiration,
1119
+ recipient: acp.recipient,
1120
+ revokerData: acp.revokerData,
1121
+ revokerContract: acp.revokerContract,
1122
+ scope: acp.scope,
1123
+ contracts: acp.contracts,
1124
+ handles: acp.handles,
1125
+ sealingKey: acp.sealingKey,
1126
+ issuerSignature: acp.issuerSignature,
1127
+ recipientSignature: acp.recipientSignature
1128
+ };
1129
+ },
1130
+ /**
1131
+ * Get a stable hash for the acp (used as key in storage)
1132
+ */
1133
+ getHash: (acp) => {
1134
+ const data = JSON.stringify({
1135
+ type: acp.type,
1136
+ issuer: acp.issuer,
1137
+ expiration: acp.expiration,
1138
+ recipient: acp.recipient,
1139
+ revokerData: acp.revokerData,
1140
+ revokerContract: acp.revokerContract,
1141
+ scope: acp.scope,
1142
+ contracts: acp.contracts,
1143
+ handles: acp.handles
1144
+ });
1145
+ return keccak256(toHex(data));
1146
+ },
1147
+ /**
1148
+ * Export acp data for sharing (strips the private component).
1149
+ * Fixed `SharedACP` shape — every field always present, aligned with
1150
+ * `ACPPublic` and the on-chain sharing payload — plus `metadata` when the acp has labels.
1151
+ */
1152
+ export: (acp) => {
1153
+ if (acp.type !== "sharing") {
1154
+ throw new Error(
1155
+ `Cannot export a '${acp.type}' ACP \u2014 only 'sharing' ACPs are exportable. The export includes the issuer signature.`
1156
+ );
1157
+ }
1158
+ if (acp.issuerSignature === "0x") {
1159
+ throw new Error(
1160
+ "Cannot export an unsigned sharing ACP \u2014 sign it first (the recipient needs the issuer signature)."
1161
+ );
1162
+ }
1163
+ const shared = {
1164
+ type: acp.type,
1165
+ issuer: acp.issuer,
1166
+ expiration: acp.expiration,
1167
+ recipient: acp.recipient,
1168
+ revokerData: acp.revokerData,
1169
+ revokerContract: acp.revokerContract,
1170
+ scope: acp.scope,
1171
+ contracts: acp.contracts,
1172
+ handles: acp.handles,
1173
+ issuerSignature: acp.issuerSignature,
1174
+ ...acp.metadata != null && acp.metadata !== "0x" && { metadata: acp.metadata }
1175
+ };
1176
+ return JSON.stringify(shared, void 0, 2);
1177
+ },
1178
+ /**
1179
+ * Unseal encrypted data using the acp's sealing key
1180
+ */
1181
+ unseal: (acp, ciphertext) => {
1182
+ return unsealWithPrivateKey(acp.sealingPrivateKey, ciphertext);
1183
+ },
1184
+ /**
1185
+ * Check if acp is expired
1186
+ */
1187
+ isExpired: (acp) => {
1188
+ return ValidationUtils.isExpired(acp);
1189
+ },
1190
+ /**
1191
+ * Check if acp is signed
1192
+ */
1193
+ isSigned: (acp) => {
1194
+ return ValidationUtils.isSigned(acp);
1195
+ },
1196
+ /**
1197
+ * Check if acp is signed and not expired
1198
+ */
1199
+ isSignedAndNotExpired: (acp) => {
1200
+ return ValidationUtils.isSignedAndNotExpired(acp);
1201
+ },
1202
+ /**
1203
+ * Assert that acp is signed and not expired
1204
+ */
1205
+ assertSignedAndNotExpired: (acp) => {
1206
+ return ValidationUtils.assertSignedAndNotExpired(acp);
1207
+ },
1208
+ isValid: (acp) => {
1209
+ return ValidationUtils.isValid(acp);
1210
+ },
1211
+ /**
1212
+ * Update acp name (returns new acp instance)
1213
+ */
1214
+ updateName: (acp, name) => {
1215
+ return { ...acp, name };
1216
+ },
1217
+ /**
1218
+ * Fetch EIP712 domain from the blockchain
1219
+ */
1220
+ fetchEIP712Domain: async (publicClient) => {
1221
+ return getAclEIP712Domain(publicClient);
1222
+ },
1223
+ /**
1224
+ * Check if acp's signed domain matches the provided domain
1225
+ */
1226
+ matchesDomain: (acp, domain) => {
1227
+ return acp._signedDomain?.name === domain.name && acp._signedDomain?.version === domain.version && acp._signedDomain?.verifyingContract === domain.verifyingContract && acp._signedDomain?.chainId === domain.chainId;
1228
+ },
1229
+ /**
1230
+ * Check if acp's signed domain is valid for the current chain
1231
+ */
1232
+ checkSignedDomainValid: async (acp, publicClient) => {
1233
+ if (acp._signedDomain == null)
1234
+ return false;
1235
+ const domain = await getAclEIP712Domain(publicClient);
1236
+ return ACPUtils.matchesDomain(acp, domain);
1237
+ },
1238
+ /**
1239
+ * Check if acp passes the on-chain validation
1240
+ */
1241
+ checkValidityOnChain: async (acp, publicClient) => {
1242
+ const publicAcp = ACPUtils.getPublic(acp);
1243
+ return checkACPValidityOnChain(publicAcp, publicClient);
1244
+ },
1245
+ /**
1246
+ * The acp on-chain status as a value instead of a revert: 'valid' or why not, and given a
1247
+ * handle, whether the acp may read it (see `ACPAccessStatus`).
1248
+ */
1249
+ checkAccessOnChain: async (acp, publicClient, handle) => {
1250
+ return getACPAccessStatusOnChain(ACPUtils.getPublic(acp), publicClient, handle);
1251
+ }
1252
+ };
1253
+ var ACP_REVOKER_ABI = parseAbi([
1254
+ "function revokeSingle(uint256 id)",
1255
+ "function revokeAllExisting()",
1256
+ "function disabled(address issuer, uint256 id) view returns (bool)"
1257
+ ]);
1258
+ var ACP_SHARE_REGISTRY_ABI = parseAbi([
1259
+ "struct ACP { address issuer; uint64 expiration; address recipient; uint256 revokerData; address revokerContract; uint8 scope; address[] contracts; bytes32[] handles; bytes32 sealingKey; bytes issuerSignature; bytes recipientSignature; }",
1260
+ "struct ShareHeader { address issuer; uint64 expiration; address recipient; uint64 blockNumber; address revokerContract; uint256 revokerData; }",
1261
+ "function share(ACP calldata acp, bytes calldata metadata) external returns (bytes32)",
1262
+ "function removeShare(bytes32 shareId) external",
1263
+ "function sharesFor(address recipient) external view returns (bytes32[] shareIds, ShareHeader[] headers)",
1264
+ "function getShare(bytes32 shareId) external view returns (ShareHeader memory)",
1265
+ "function isShareValid(bytes32 shareId) external view returns (bool)",
1266
+ "event Shared(address indexed recipient, address indexed issuer, bytes32 indexed shareId, ACP acp, bytes metadata)",
1267
+ "event ShareRemoved(address indexed recipient, address indexed issuer, bytes32 indexed shareId)"
1268
+ ]);
1269
+ var ACP_SHARE_TUPLE_ABI = [
1270
+ {
1271
+ type: "tuple",
1272
+ components: [
1273
+ { name: "issuer", type: "address" },
1274
+ { name: "expiration", type: "uint64" },
1275
+ { name: "recipient", type: "address" },
1276
+ { name: "revokerData", type: "uint256" },
1277
+ { name: "revokerContract", type: "address" },
1278
+ { name: "scope", type: "uint8" },
1279
+ { name: "contracts", type: "address[]" },
1280
+ { name: "handles", type: "bytes32[]" },
1281
+ { name: "sealingKey", type: "bytes32" },
1282
+ { name: "issuerSignature", type: "bytes" },
1283
+ { name: "recipientSignature", type: "bytes" }
1284
+ ]
1285
+ }
1286
+ ];
1287
+ var ZERO_BYTES32 = `0x${"0".repeat(64)}`;
1288
+ var toChainShare = (acp) => {
1289
+ const pub = ACPUtils.getPublic(acp, true);
1290
+ return {
1291
+ ...pub,
1292
+ expiration: BigInt(pub.expiration),
1293
+ revokerData: BigInt(pub.revokerData),
1294
+ sealingKey: ZERO_BYTES32,
1295
+ recipientSignature: "0x"
1296
+ };
1297
+ };
1298
+ var computeShareId = (acp) => {
1299
+ const p = toChainShare(acp);
1300
+ return keccak256(encodeAbiParameters(ACP_SHARE_TUPLE_ABI, [p]));
1301
+ };
1302
+ var shareIdOfChainShare = (share) => keccak256(encodeAbiParameters(ACP_SHARE_TUPLE_ABI, [share]));
1303
+ var messageOf = (error) => error instanceof Error ? error.message : String(error);
1304
+ var readPostedShares = async (publicClient, registry, headers) => {
1305
+ const idsByBlock = /* @__PURE__ */ new Map();
1306
+ for (const { shareId, header } of headers) {
1307
+ idsByBlock.set(header.blockNumber, [...idsByBlock.get(header.blockNumber) ?? [], shareId]);
1308
+ }
1309
+ const found = /* @__PURE__ */ new Map();
1310
+ await Promise.all(
1311
+ [...idsByBlock].map(async ([block, shareIds]) => {
1312
+ let logs;
1313
+ try {
1314
+ logs = await publicClient.getContractEvents({
1315
+ address: registry,
1316
+ abi: ACP_SHARE_REGISTRY_ABI,
1317
+ eventName: "Shared",
1318
+ args: { shareId: shareIds },
1319
+ fromBlock: block,
1320
+ toBlock: block,
1321
+ strict: true
1322
+ });
1323
+ } catch (error) {
1324
+ for (const shareId of shareIds) {
1325
+ found.set(shareId.toLowerCase(), {
1326
+ reason: `the Shared events of block ${block} could not be read: ${messageOf(error)}`
1327
+ });
1328
+ }
1329
+ return;
1330
+ }
1331
+ for (const { args } of logs) {
1332
+ const hashes = shareIdOfChainShare(args.acp).toLowerCase() === args.shareId.toLowerCase();
1333
+ found.set(
1334
+ args.shareId.toLowerCase(),
1335
+ hashes ? { share: args.acp, metadata: args.metadata } : { reason: "the payload in its Shared event does not hash to its id" }
1336
+ );
1337
+ }
1338
+ })
1339
+ );
1340
+ const result = { posted: [], unreadable: [] };
1341
+ for (const { shareId, header } of headers) {
1342
+ const entry = found.get(shareId.toLowerCase());
1343
+ if (entry == null) {
1344
+ result.unreadable.push({
1345
+ shareId,
1346
+ header,
1347
+ reason: `no Shared event in block ${header.blockNumber}, where the registry points`
1348
+ });
1349
+ } else if ("reason" in entry) {
1350
+ result.unreadable.push({ shareId, header, reason: entry.reason });
1351
+ } else {
1352
+ result.posted.push({ shareId, header, ...entry });
1353
+ }
1354
+ }
1355
+ return result;
1356
+ };
1357
+ var readSharesFor = async (publicClient, registry, recipient) => {
1358
+ const [shareIds, headers] = await publicClient.readContract({
1359
+ address: registry,
1360
+ abi: ACP_SHARE_REGISTRY_ABI,
1361
+ functionName: "sharesFor",
1362
+ args: [recipient]
1363
+ });
1364
+ return readPostedShares(
1365
+ publicClient,
1366
+ registry,
1367
+ shareIds.map((shareId, i) => ({ shareId, header: headers[i] }))
1368
+ );
1369
+ };
1370
+ var readShare = async (publicClient, registry, shareId) => {
1371
+ const header = await publicClient.readContract({
1372
+ address: registry,
1373
+ abi: ACP_SHARE_REGISTRY_ABI,
1374
+ functionName: "getShare",
1375
+ args: [shareId]
1376
+ });
1377
+ if (header.issuer === "0x0000000000000000000000000000000000000000")
1378
+ return null;
1379
+ const { posted, unreadable } = await readPostedShares(publicClient, registry, [{ shareId, header }]);
1380
+ if (unreadable.length > 0)
1381
+ throw new Error(`Share ${shareId}: ${unreadable[0].reason}`);
1382
+ return posted[0];
1383
+ };
1384
+ var readerOf = (blob) => {
1385
+ let offset = 0;
1386
+ const take = (width) => {
1387
+ if (offset + width > size(blob))
1388
+ throw new Error("share metadata: the blob ends in the middle of a field");
1389
+ offset += width;
1390
+ return sliceHex(blob, offset - width, offset);
1391
+ };
1392
+ return {
1393
+ take,
1394
+ takeNumber: (width) => hexToNumber(take(width)),
1395
+ done: () => offset === size(blob),
1396
+ remaining: () => size(blob) - offset
1397
+ };
1398
+ };
1399
+ var takeList = (reader, takeItem) => Array.from({ length: reader.takeNumber(BYTE_WIDTH.count) }, takeItem);
1400
+ var takeFrom = (reader, list, what) => {
1401
+ const position = reader.takeNumber(BYTE_WIDTH.index);
1402
+ if (position >= list.length) {
1403
+ throw new Error(`share metadata: ${what} ${position} is outside the ${what} list`);
1404
+ }
1405
+ return list[position];
1406
+ };
1407
+ var decodeArg = (reader) => {
1408
+ const tag = reader.takeNumber(BYTE_WIDTH.argTag);
1409
+ if (tag === ARG_TAG.issuer)
1410
+ return { type: "issuer" };
1411
+ if (tag === ARG_TAG.word)
1412
+ return { type: "word", value: reader.take(BYTE_WIDTH.word) };
1413
+ throw new Error(`share metadata: unknown argument tag ${tag}`);
1414
+ };
1415
+ var decodeArgs = (reader) => {
1416
+ const code = reader.takeNumber(BYTE_WIDTH.argsCode);
1417
+ if (code === ARGS_CODE.none)
1418
+ return { args: [], returnWord: 0 };
1419
+ if (code === ARGS_CODE.issuer)
1420
+ return { args: [{ type: "issuer" }], returnWord: 0 };
1421
+ if (code !== ARGS_CODE.explicit)
1422
+ throw new Error(`share metadata: unknown args code ${code}`);
1423
+ const returnWord = reader.takeNumber(BYTE_WIDTH.returnWord);
1424
+ return { args: takeList(reader, () => decodeArg(reader)), returnWord };
1425
+ };
1426
+ var takeAppKey = (reader) => {
1427
+ if (reader.done())
1428
+ return void 0;
1429
+ const isKey = reader.remaining() === BYTE_WIDTH.count + BYTE_WIDTH.word && reader.takeNumber(BYTE_WIDTH.count) === APP_KEY_TAG;
1430
+ if (!isKey)
1431
+ throw new Error("share metadata: the blob has more entries than the share has ctHashes");
1432
+ return reader.take(BYTE_WIDTH.word);
1433
+ };
1434
+ var decodeShareMetadataFull = (blob, handles) => {
1435
+ const reader = readerOf(blob);
1436
+ const version = reader.takeNumber(BYTE_WIDTH.version);
1437
+ if (version !== LAYOUT_VERSION)
1438
+ throw new Error(`share metadata: unsupported version ${version}`);
1439
+ const contracts = takeList(reader, () => getAddress(reader.take(BYTE_WIDTH.address)));
1440
+ const selectors = takeList(reader, () => reader.take(BYTE_WIDTH.selector));
1441
+ const transactions = takeList(reader, () => ({
1442
+ block: hexToBigInt(reader.take(BYTE_WIDTH.block)),
1443
+ txIndex: reader.takeNumber(BYTE_WIDTH.txIndex)
1444
+ }));
1445
+ const labels = handles.map(() => {
1446
+ const kind = reader.takeNumber(BYTE_WIDTH.kind);
1447
+ switch (kind) {
1448
+ case ENTRY_KIND.stored: {
1449
+ const contract = takeFrom(reader, contracts, "contract");
1450
+ const selector = takeFrom(reader, selectors, "selector");
1451
+ const block = hexToBigInt(reader.take(BYTE_WIDTH.block));
1452
+ return { kind: "stored", contract, selector, ...decodeArgs(reader), block };
1453
+ }
1454
+ case ENTRY_KIND.event: {
1455
+ const contract = takeFrom(reader, contracts, "contract");
1456
+ const { block, txIndex } = takeFrom(reader, transactions, "transaction");
1457
+ const logIndex = reader.takeNumber(BYTE_WIDTH.logIndex);
1458
+ const selector = takeFrom(reader, selectors, "selector");
1459
+ return { kind: "event", contract, block, txIndex, logIndex, selector };
1460
+ }
1461
+ case ENTRY_KIND.unlabelled:
1462
+ return { kind: "unlabelled" };
1463
+ default:
1464
+ throw new Error(`share metadata: unknown entry kind ${kind}`);
1465
+ }
1466
+ });
1467
+ const appKey = takeAppKey(reader);
1468
+ if (!reader.done())
1469
+ throw new Error("share metadata: the blob has more entries than the share has ctHashes");
1470
+ return { labels, appKey };
1471
+ };
1472
+ var decodeShareMetadata = (blob, handles) => decodeShareMetadataFull(blob, handles).labels;
1473
+ var decodeShareAppKey = (metadata, handles) => metadata == null || metadata === "0x" ? void 0 : decodeShareMetadataFull(metadata, handles).appKey;
1474
+ var sameWord = (a, b) => a.toLowerCase() === b.toLowerCase();
1475
+ var wordsOf = (data) => {
1476
+ const count = Math.floor(size(data) / BYTE_WIDTH.word);
1477
+ return Array.from(
1478
+ { length: count },
1479
+ (_, index) => sliceHex(data, index * BYTE_WIDTH.word, (index + 1) * BYTE_WIDTH.word)
1480
+ );
1481
+ };
1482
+ var storedCallData = (label, issuer) => concatHex([
1483
+ label.selector,
1484
+ ...label.args.map((arg) => arg.type === "issuer" ? padHex(issuer, { size: BYTE_WIDTH.word }) : arg.value)
1485
+ ]);
1486
+ var checkStored = (label, handle, data) => {
1487
+ const found = data ? wordsOf(data)[label.returnWord] : void 0;
1488
+ return found && sameWord(found, handle) ? "verified" : "mismatch";
1489
+ };
1490
+ var checkEvent = (label, handle, logs) => {
1491
+ const log = logs.find((l) => l.logIndex === label.logIndex);
1492
+ if (log == null)
1493
+ return "mismatch";
1494
+ if (log.transactionIndex !== label.txIndex)
1495
+ return "mismatch";
1496
+ const topic0 = log.topics[0];
1497
+ if (topic0 == null || !topic0.toLowerCase().startsWith(label.selector.toLowerCase()))
1498
+ return "mismatch";
1499
+ const carried = [...log.topics.slice(1), ...wordsOf(log.data)].some((word) => word != null && sameWord(word, handle));
1500
+ return carried ? "verified" : "mismatch";
1501
+ };
1502
+ var callAt = async (publicClient, to, data, blockNumber) => (await publicClient.call({ to, data, blockNumber })).data;
1503
+ var logsAt = (publicClient, address, block) => publicClient.getLogs({ address, fromBlock: block, toBlock: block });
1504
+ var verifyShareLabel = async (label, handle, publicClient, issuer) => {
1505
+ try {
1506
+ switch (label.kind) {
1507
+ case "stored":
1508
+ return checkStored(
1509
+ label,
1510
+ handle,
1511
+ await callAt(publicClient, label.contract, storedCallData(label, issuer), label.block)
1512
+ );
1513
+ case "event":
1514
+ return checkEvent(label, handle, await logsAt(publicClient, label.contract, label.block));
1515
+ case "unlabelled":
1516
+ return "unverifiable";
1517
+ }
1518
+ } catch {
1519
+ return "unverifiable";
1520
+ }
1521
+ };
1522
+ var verifyShareLabels = async ({
1523
+ labels,
1524
+ handles,
1525
+ issuer,
1526
+ publicClient,
1527
+ verify = "all"
1528
+ }) => {
1529
+ if (labels.length !== handles.length) {
1530
+ throw new Error(`share metadata: ${labels.length} labels for ${handles.length} ctHashes`);
1531
+ }
1532
+ const requests = /* @__PURE__ */ new Map();
1533
+ const once = (key, request) => {
1534
+ if (!requests.has(key))
1535
+ requests.set(key, request());
1536
+ return requests.get(key);
1537
+ };
1538
+ return Promise.all(
1539
+ labels.map(async (label, i) => {
1540
+ const handle = handles[i];
1541
+ try {
1542
+ switch (label.kind) {
1543
+ case "stored": {
1544
+ if (verify !== "all")
1545
+ return "skipped";
1546
+ const data = storedCallData(label, issuer);
1547
+ const key = `call:${label.contract.toLowerCase()}:${label.block}:${data}`;
1548
+ return checkStored(
1549
+ label,
1550
+ handle,
1551
+ await once(key, () => callAt(publicClient, label.contract, data, label.block))
1552
+ );
1553
+ }
1554
+ case "event": {
1555
+ if (verify === "none")
1556
+ return "skipped";
1557
+ const key = `logs:${label.contract.toLowerCase()}:${label.block}`;
1558
+ return checkEvent(label, handle, await once(key, () => logsAt(publicClient, label.contract, label.block)));
1559
+ }
1560
+ case "unlabelled":
1561
+ return verify === "none" ? "skipped" : "unverifiable";
1562
+ }
1563
+ } catch {
1564
+ return "unverifiable";
1565
+ }
1566
+ })
1567
+ );
1568
+ };
1569
+ var abiOf = (abis, contract) => {
1570
+ const wanted = contract.toLowerCase();
1571
+ const key = Object.keys(abis).find((address) => address.toLowerCase() === wanted);
1572
+ return key ? abis[key] : void 0;
1573
+ };
1574
+ var paramOf = ({ name, type, ...rest }) => {
1575
+ const param = { type };
1576
+ if (name)
1577
+ param.name = name;
1578
+ if ("indexed" in rest && typeof rest.indexed === "boolean")
1579
+ param.indexed = rest.indexed;
1580
+ return param;
1581
+ };
1582
+ var findFunction = (abi, selector) => abi?.find(
1583
+ (item) => item.type === "function" && toFunctionSelector(item).toLowerCase() === selector.toLowerCase()
1584
+ );
1585
+ var findEvent = (abi, selector) => abi?.find(
1586
+ (item) => item.type === "event" && toEventSelector(item).toLowerCase().startsWith(selector.toLowerCase())
1587
+ );
1588
+ var decodeWord = (input, raw) => {
1589
+ if (input == null)
1590
+ return raw;
1591
+ try {
1592
+ return decodeAbiParameters([input], raw)[0];
1593
+ } catch {
1594
+ return raw;
1595
+ }
1596
+ };
1597
+ var describeStored = (base, label, abis, issuer) => {
1598
+ const fn = findFunction(abiOf(abis, label.contract), label.selector);
1599
+ const args = label.args.map((arg, i) => {
1600
+ const input = fn?.inputs[i];
1601
+ const param = input ? paramOf(input) : { type: "bytes32" };
1602
+ if (arg.type === "issuer") {
1603
+ return {
1604
+ ...param,
1605
+ isIssuer: true,
1606
+ raw: issuer ? padHex(issuer, { size: 32 }) : void 0,
1607
+ value: issuer
1608
+ };
1609
+ }
1610
+ return { ...param, isIssuer: false, raw: arg.value, value: decodeWord(input, arg.value) };
1611
+ });
1612
+ const output = fn?.outputs[label.returnWord];
1613
+ return {
1614
+ ...base,
1615
+ kind: "stored",
1616
+ label,
1617
+ contract: label.contract,
1618
+ block: label.block,
1619
+ selector: label.selector,
1620
+ function: fn ? { name: fn.name, signature: toFunctionSignature(fn), inputs: fn.inputs.map(paramOf) } : null,
1621
+ args,
1622
+ output: output ? paramOf(output) : null
1623
+ };
1624
+ };
1625
+ var describeEvent = (base, label, abis) => {
1626
+ const event = findEvent(abiOf(abis, label.contract), label.selector);
1627
+ return {
1628
+ ...base,
1629
+ kind: "event",
1630
+ label,
1631
+ contract: label.contract,
1632
+ block: label.block,
1633
+ txIndex: label.txIndex,
1634
+ logIndex: label.logIndex,
1635
+ selector: label.selector,
1636
+ event: event ? { name: event.name, signature: toEventSignature(event), inputs: event.inputs.map(paramOf) } : null
1637
+ };
1638
+ };
1639
+ var describeShareLabels = (labels, handles, abis = {}, options = {}) => labels.map((label, index) => {
1640
+ const base = { index, handle: handles[index] };
1641
+ switch (label.kind) {
1642
+ case "stored":
1643
+ return describeStored(base, label, abis, options.issuer);
1644
+ case "event":
1645
+ return describeEvent(base, label, abis);
1646
+ case "unlabelled":
1647
+ return { ...base, kind: "unlabelled", label };
1648
+ }
1649
+ });
1650
+ var describeShareMetadata = (share, abis = {}) => {
1651
+ const { issuer, handles, metadata } = share;
1652
+ if (metadata == null || metadata === "0x")
1653
+ return null;
1654
+ return describeShareLabels(decodeShareMetadata(metadata, handles), handles, abis, { issuer });
1655
+ };
1656
+
1657
+ // acps/share-metadata/erc7984.ts
1658
+ var ERC7984_ABI = [
1659
+ {
1660
+ type: "function",
1661
+ name: "confidentialBalanceOf",
1662
+ stateMutability: "view",
1663
+ inputs: [{ name: "account", type: "address", internalType: "address" }],
1664
+ outputs: [{ name: "", type: "bytes32", internalType: "euint64" }]
1665
+ },
1666
+ {
1667
+ type: "event",
1668
+ name: "ConfidentialTransfer",
1669
+ anonymous: false,
1670
+ inputs: [
1671
+ { name: "from", type: "address", indexed: true, internalType: "address" },
1672
+ { name: "to", type: "address", indexed: true, internalType: "address" },
1673
+ { name: "amount", type: "bytes32", indexed: true, internalType: "euint64" }
1674
+ ]
1675
+ }
1676
+ ];
1677
+ var shareAppKey = (app) => keccak256(stringToHex(app));
1678
+ var ACP_STORE_DEFAULTS = {
1679
+ acps: {},
1680
+ activeACPHash: {}
1681
+ };
1682
+ var ACP_STORE_VERSION = 3;
1683
+ var isRecord = (value) => value != null && typeof value === "object" && !Array.isArray(value);
1684
+ var resolveAccountKey = (records, account) => {
1685
+ if (records == null || account in records)
1686
+ return account;
1687
+ const lower = account.toLowerCase();
1688
+ return Object.keys(records).find((key) => key.toLowerCase() === lower) ?? account;
1689
+ };
1690
+ var _acpStore = createStore()(
1691
+ persist(() => ACP_STORE_DEFAULTS, {
1692
+ name: "cofhesdk-acps",
1693
+ version: ACP_STORE_VERSION,
1694
+ migrate: (persistedState, version) => {
1695
+ if (version < ACP_STORE_VERSION)
1696
+ return ACP_STORE_DEFAULTS;
1697
+ return persistedState;
1698
+ },
1699
+ // Runs on every rehydrate, including a corrupt state saved at the current version that
1700
+ // `migrate` passes through unchanged. Consumers that read the raw snapshot (e.g. the React
1701
+ // hooks) never go through `clearStaleStore`, so the shape has to be fixed here.
1702
+ merge: (persistedState, currentState) => {
1703
+ const persisted = isRecord(persistedState) ? persistedState : {};
1704
+ return {
1705
+ ...currentState,
1706
+ acps: isRecord(persisted.acps) ? persisted.acps : currentState.acps,
1707
+ activeACPHash: isRecord(persisted.activeACPHash) ? persisted.activeACPHash : currentState.activeACPHash
1708
+ };
1709
+ }
1710
+ })
1711
+ );
1712
+ var clearStaleStore = () => {
1713
+ const state = _acpStore.getState();
1714
+ const hasExpectedStructure = isRecord(state) && isRecord(state.acps) && isRecord(state.activeACPHash);
1715
+ if (hasExpectedStructure)
1716
+ return;
1717
+ _acpStore.setState({ acps: {}, activeACPHash: {} });
1718
+ };
1719
+ var getACP = (chainId, account, hash) => {
1720
+ clearStaleStore();
1721
+ if (chainId == null || account == null || hash == null)
1722
+ return;
1723
+ const accountACPs = _acpStore.getState().acps[chainId];
1724
+ const savedACP = accountACPs?.[resolveAccountKey(accountACPs, account)]?.[hash];
1725
+ if (savedACP == null)
1726
+ return;
1727
+ return ACPUtils.deserialize(savedACP);
1728
+ };
1729
+ var getActiveACP = (chainId, account) => {
1730
+ clearStaleStore();
1731
+ if (chainId == null || account == null)
1732
+ return;
1733
+ const activeHashes = _acpStore.getState().activeACPHash[chainId];
1734
+ const activeACPHash = activeHashes?.[resolveAccountKey(activeHashes, account)];
1735
+ return getACP(chainId, account, activeACPHash);
1736
+ };
1737
+ var getACPs = (chainId, account) => {
1738
+ clearStaleStore();
1739
+ if (chainId == null || account == null)
1740
+ return {};
1741
+ const accountACPs = _acpStore.getState().acps[chainId];
1742
+ return Object.entries(accountACPs?.[resolveAccountKey(accountACPs, account)] ?? {}).reduce(
1743
+ (acc, [hash, acp]) => {
1744
+ if (acp == void 0)
1745
+ return acc;
1746
+ return { ...acc, [hash]: ACPUtils.deserialize(acp) };
1747
+ },
1748
+ {}
1749
+ );
1750
+ };
1751
+ var setACP = (chainId, account, acp) => {
1752
+ clearStaleStore();
1753
+ _acpStore.setState(
1754
+ produce((state) => {
1755
+ if (state.acps[chainId] == null)
1756
+ state.acps[chainId] = {};
1757
+ const key = resolveAccountKey(state.acps[chainId], account);
1758
+ if (state.acps[chainId][key] == null)
1759
+ state.acps[chainId][key] = {};
1760
+ state.acps[chainId][key][acp.hash] = ACPUtils.serialize(acp);
1761
+ })
1762
+ );
1763
+ };
1764
+ var removeACP = (chainId, account, hash) => {
1765
+ clearStaleStore();
1766
+ _acpStore.setState(
1767
+ produce((state) => {
1768
+ if (state.acps[chainId] == null)
1769
+ state.acps[chainId] = {};
1770
+ if (state.activeACPHash[chainId] == null)
1771
+ state.activeACPHash[chainId] = {};
1772
+ const accountACPs = state.acps[chainId][resolveAccountKey(state.acps[chainId], account)];
1773
+ if (accountACPs == null)
1774
+ return;
1775
+ if (accountACPs[hash] == null)
1776
+ return;
1777
+ const activeKey = resolveAccountKey(state.activeACPHash[chainId], account);
1778
+ if (state.activeACPHash[chainId][activeKey] === hash) {
1779
+ state.activeACPHash[chainId][activeKey] = void 0;
1780
+ }
1781
+ accountACPs[hash] = void 0;
1782
+ })
1783
+ );
1784
+ };
1785
+ var getActiveACPHash = (chainId, account) => {
1786
+ clearStaleStore();
1787
+ if (chainId == null || account == null)
1788
+ return void 0;
1789
+ const activeHashes = _acpStore.getState().activeACPHash[chainId];
1790
+ return activeHashes?.[resolveAccountKey(activeHashes, account)];
1791
+ };
1792
+ var setActiveACPHash = (chainId, account, hash) => {
1793
+ clearStaleStore();
1794
+ _acpStore.setState(
1795
+ produce((state) => {
1796
+ if (state.activeACPHash[chainId] == null)
1797
+ state.activeACPHash[chainId] = {};
1798
+ state.activeACPHash[chainId][resolveAccountKey(state.activeACPHash[chainId], account)] = hash;
1799
+ })
1800
+ );
1801
+ };
1802
+ var removeActiveACPHash = (chainId, account) => {
1803
+ clearStaleStore();
1804
+ _acpStore.setState(
1805
+ produce((state) => {
1806
+ if (state.activeACPHash[chainId])
1807
+ state.activeACPHash[chainId][resolveAccountKey(state.activeACPHash[chainId], account)] = void 0;
1808
+ })
1809
+ );
1810
+ };
1811
+ var resetStore = () => {
1812
+ clearStaleStore();
1813
+ _acpStore.setState({ acps: {}, activeACPHash: {} });
1814
+ };
1815
+ var acpStore = {
1816
+ store: _acpStore,
1817
+ getACP,
1818
+ getActiveACP,
1819
+ getACPs,
1820
+ setACP,
1821
+ removeACP,
1822
+ getActiveACPHash,
1823
+ setActiveACPHash,
1824
+ removeActiveACPHash,
1825
+ resetStore
1826
+ };
1827
+
1828
+ 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 };