@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.
- package/CHANGELOG.md +35 -0
- package/acps/acp.ts +35 -7
- package/acps/index.ts +28 -12
- package/acps/onchain-utils.ts +188 -49
- package/acps/registry.ts +231 -0
- package/acps/sealing.ts +9 -3
- package/acps/share-metadata/appKey.ts +8 -0
- package/acps/share-metadata/decode.ts +120 -0
- package/acps/share-metadata/describe.ts +198 -0
- package/acps/share-metadata/encode.ts +147 -0
- package/acps/share-metadata/erc7984.ts +26 -0
- package/acps/share-metadata/index.ts +9 -0
- package/acps/share-metadata/layout.ts +69 -0
- package/acps/share-metadata/presets.ts +116 -0
- package/acps/share-metadata/schema.ts +47 -0
- package/acps/share-metadata/test/codec.test.ts +140 -0
- package/acps/share-metadata/test/describe.test.ts +112 -0
- package/acps/share-metadata/test/fixtures.ts +157 -0
- package/acps/share-metadata/test/live.test.ts +56 -0
- package/acps/share-metadata/test/presets.test.ts +119 -0
- package/acps/share-metadata/test/verify.test.ts +144 -0
- package/acps/share-metadata/verify.ts +155 -0
- package/acps/store.ts +48 -18
- package/acps/test/acp.test.ts +102 -7
- package/acps/test/localstorage.test.ts +28 -1
- package/acps/test/onchainAccessStatus.test.ts +116 -0
- package/acps/test/registry.test.ts +144 -0
- package/acps/test/sealing.test.ts +23 -0
- package/acps/test/store.test.ts +71 -0
- package/acps/test/utils.test.ts +63 -0
- package/acps/test/validation.test.ts +0 -4
- package/acps/types.ts +59 -6
- package/acps/utils.ts +15 -2
- package/acps/validation.ts +3 -1
- package/chains/chains/arbitrum.ts +14 -0
- package/chains/chains/ethereum.ts +14 -0
- package/chains/index.ts +5 -1
- package/chains/test/chains.test.ts +24 -3
- package/core/acps.ts +164 -207
- package/core/client.ts +44 -13
- package/core/clientTypes.ts +50 -7
- package/core/config.ts +33 -1
- package/core/consts.ts +14 -2
- package/core/decrypt/apiError.ts +4 -1
- package/core/decrypt/verifyDecryptResult.ts +8 -4
- package/core/encrypt/cofheMocksZkVerifySign.ts +19 -2
- package/core/encrypt/encryptInputsBuilder.ts +6 -0
- package/core/encrypt/zkPackProveVerify.ts +7 -1
- package/core/error.ts +1 -0
- package/core/fetchKeys.ts +2 -2
- package/core/index.ts +9 -1
- package/core/keyStore.ts +23 -10
- package/core/taskManager.ts +48 -0
- package/core/test/acpDefaults.test.ts +108 -19
- package/core/test/acpStoreAccountCase.test.ts +80 -0
- package/core/test/acps.test.ts +128 -3
- package/core/test/apiError.test.ts +8 -0
- package/core/test/appKeyConfig.test.ts +94 -0
- package/core/test/client.test.ts +41 -0
- package/core/test/config.test.ts +24 -0
- package/core/test/decrypt.test.ts +11 -4
- package/core/test/decryptBuilders.test.ts +110 -2
- package/core/test/encryptInputsBuilder.test.ts +1 -1
- package/core/test/fetchKeys.test.ts +23 -2
- package/core/test/incomingShares.test.ts +84 -0
- package/core/test/keyStore.test.ts +18 -6
- package/core/test/mocksEncryptRange.test.ts +90 -0
- package/core/test/taskManager.test.ts +57 -0
- package/core/test/utils.test.ts +128 -0
- package/core/test/verifyDecryptResult.test.ts +51 -0
- package/core/types.ts +8 -0
- package/core/utils.ts +16 -9
- package/dist/acps.cjs +984 -64
- package/dist/acps.d.cts +589 -10
- package/dist/acps.d.ts +589 -10
- package/dist/acps.js +2 -2
- package/dist/chains.cjs +26 -0
- package/dist/chains.d.cts +45 -1
- package/dist/chains.d.ts +45 -1
- package/dist/chains.js +1 -1
- package/dist/chunk-6ZW76ZKB.js +228 -0
- package/dist/chunk-BQWHPIYU.js +1828 -0
- package/dist/chunk-KSOV5B4B.js +50 -0
- package/dist/{chunk-43USWPEH.js → chunk-OL3DZN7A.js} +159 -523
- package/dist/{chunk-N6IDQRRU.js → chunk-UKQAJK4N.js} +25 -1
- package/dist/chunk-XQ32JG3T.js +174 -0
- package/dist/{clientTypes-CYZjFznO.d.cts → clientTypes-C0A9ji5O.d.cts} +132 -29
- package/dist/{clientTypes-BN3nbzYM.d.ts → clientTypes-CcU5WvdV.d.ts} +132 -29
- package/dist/core.cjs +3269 -2626
- package/dist/core.d.cts +32 -8
- package/dist/core.d.ts +32 -8
- package/dist/core.js +5 -4
- package/dist/node.cjs +3074 -2436
- package/dist/node.d.cts +2 -2
- package/dist/node.d.ts +2 -2
- package/dist/node.js +20 -19
- package/dist/{acp-Wi6isVQI.d.cts → onchain-utils-D9Z33IP7.d.cts} +166 -12
- package/dist/{acp-Wi6isVQI.d.ts → onchain-utils-D9Z33IP7.d.ts} +166 -12
- package/dist/web.cjs +3204 -2493
- package/dist/web.d.cts +49 -3
- package/dist/web.d.ts +49 -3
- package/dist/web.js +52 -27
- package/dist/zkProve.worker.cjs +79 -18
- package/dist/zkProve.worker.js +24 -24
- package/node/index.ts +1 -10
- package/node/storage.ts +20 -9
- package/node/test/inherited.test.ts +5 -1
- package/node/test/storage-memory-fallback.test.ts +47 -0
- package/node/test/storage-permissions.test.ts +40 -0
- package/package.json +3 -1
- package/web/index.ts +101 -36
- package/web/test/countingZkProveWorker.ts +31 -0
- package/web/test/inherited.web.test.ts +5 -1
- package/web/test/mainThreadPool.web.test.ts +174 -0
- package/web/test/threadPoolBenchHelper.ts +134 -0
- package/web/test/threadPoolOff.web.test.ts +17 -0
- package/web/test/threadPoolOn.web.test.ts +80 -0
- package/web/test/workerInitRace.web.test.ts +79 -0
- package/web/tfheThreadPool.ts +140 -0
- package/web/workerManager.ts +4 -2
- package/web/zkProve.worker.ts +42 -26
- package/dist/chunk-ESMZCFJY.js +0 -12
- 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 };
|