@cofhe/sdk 0.6.1 → 0.7.1
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 +45 -0
- package/acps/acp.ts +411 -0
- package/acps/index.ts +70 -0
- package/{permits → acps}/onchain-utils.ts +49 -24
- package/acps/sealing.ts +90 -0
- package/acps/signature.ts +89 -0
- package/acps/store.ts +172 -0
- package/acps/test/acp.test.ts +615 -0
- package/acps/test/localstorage.test.ts +105 -0
- package/acps/test/sealing.test.ts +77 -0
- package/acps/test/store.test.ts +88 -0
- package/acps/test/validation.test.ts +361 -0
- package/acps/test-utils.ts +32 -0
- package/acps/types.ts +252 -0
- package/acps/validation.ts +392 -0
- package/adapters/test/ethers5.test.ts +4 -1
- package/adapters/test/ethers6.test.ts +4 -1
- package/adapters/test/wagmi.test.ts +5 -2
- package/chains/chains/stagingCofhe.ts +21 -0
- package/chains/index.ts +3 -1
- package/chains/test/chains.test.ts +2 -1
- package/core/acps.ts +625 -0
- package/core/client.ts +136 -39
- package/core/clientTypes.ts +52 -41
- package/core/config.ts +66 -5
- package/core/decrypt/MockThresholdNetworkAbi.ts +20 -11
- package/core/decrypt/apiError.ts +104 -0
- package/core/decrypt/cofheMocksDecryptForTx.ts +11 -11
- package/core/decrypt/cofheMocksDecryptForView.ts +7 -7
- package/core/decrypt/decryptForTxBuilder.ts +102 -102
- package/core/decrypt/decryptForViewBuilder.ts +90 -90
- package/core/decrypt/submitRetry.ts +38 -30
- package/core/decrypt/tnDecryptV1.ts +5 -5
- package/core/decrypt/tnDecryptV2.ts +25 -21
- package/core/decrypt/tnSealOutputV1.ts +4 -3
- package/core/decrypt/tnSealOutputV2.ts +24 -18
- package/core/encrypt/cofheMocksZkVerifySign.ts +59 -74
- package/core/encrypt/encryptInputsBuilder.ts +86 -52
- package/core/encrypt/zkPackProveVerify.ts +25 -18
- package/core/error.ts +34 -6
- package/core/index.ts +4 -14
- package/core/test/acpDefaults.test.ts +52 -0
- package/core/test/acps.test.ts +596 -0
- package/core/test/apiError.test.ts +130 -0
- package/core/test/client.test.ts +22 -19
- package/core/test/config.test.ts +25 -5
- package/core/test/decrypt.test.ts +40 -35
- package/core/test/decryptBuilders.test.ts +68 -68
- package/core/test/decryptErrorCodes.test.ts +217 -0
- package/core/test/encryptInputsBuilder.test.ts +72 -41
- package/core/test/pollCallbacks.test.ts +89 -18
- package/core/test/stagingRedirect.ts +18 -0
- package/core/test/submitRetry.test.ts +182 -0
- package/core/types.ts +9 -69
- package/dist/acp-Wi6isVQI.d.cts +407 -0
- package/dist/acp-Wi6isVQI.d.ts +407 -0
- package/dist/acps.cjs +1081 -0
- package/dist/acps.d.cts +480 -0
- package/dist/acps.d.ts +480 -0
- package/dist/acps.js +2 -0
- package/dist/chains.cjs +14 -1
- package/dist/chains.d.cts +30 -1
- package/dist/chains.d.ts +30 -1
- package/dist/chains.js +1 -1
- package/dist/{chunk-NOC3PYB7.js → chunk-43USWPEH.js} +930 -580
- package/dist/{chunk-MTRAXQXC.js → chunk-N6IDQRRU.js} +14 -2
- package/dist/chunk-Q7CBWGQX.js +1029 -0
- package/dist/{clientTypes-CyUvRRzA.d.ts → clientTypes-BN3nbzYM.d.ts} +342 -165
- package/dist/{clientTypes-BDy1qIBu.d.cts → clientTypes-CYZjFznO.d.cts} +342 -165
- package/dist/core.cjs +1358 -1002
- package/dist/core.d.cts +31 -9
- package/dist/core.d.ts +31 -9
- package/dist/core.js +3 -3
- package/dist/node.cjs +1293 -952
- package/dist/node.d.cts +2 -2
- package/dist/node.d.ts +2 -2
- package/dist/node.js +3 -3
- package/dist/web.cjs +1293 -952
- package/dist/web.d.cts +2 -2
- package/dist/web.d.ts +2 -2
- package/dist/web.js +3 -3
- package/node/test/inherited.test.ts +75 -65
- package/node/test/tfheinit.test.ts +23 -8
- package/package.json +6 -6
- package/web/test/client.web.test.ts +5 -1
- package/web/test/inherited.web.test.ts +75 -65
- package/web/test/tfheinit.web.test.ts +14 -5
- package/web/test/worker.config.web.test.ts +38 -23
- package/web/test/worker.output.web.test.ts +25 -24
- package/core/encrypt/encryptUtils.ts +0 -67
- package/core/permits.ts +0 -216
- package/core/test/permits.test.ts +0 -596
- package/dist/chunk-VB62WYPL.js +0 -978
- package/dist/permit-DnVMDT5h.d.cts +0 -376
- package/dist/permit-DnVMDT5h.d.ts +0 -376
- package/dist/permits.cjs +0 -1026
- package/dist/permits.d.cts +0 -353
- package/dist/permits.d.ts +0 -353
- package/dist/permits.js +0 -2
- package/permits/index.ts +0 -68
- package/permits/permit.ts +0 -385
- package/permits/sealing.ts +0 -131
- package/permits/signature.ts +0 -79
- package/permits/store.ts +0 -157
- package/permits/test/localstorage.test.ts +0 -113
- package/permits/test/permit.test.ts +0 -557
- package/permits/test/sealing.test.ts +0 -84
- package/permits/test/store.test.ts +0 -88
- package/permits/test/validation.test.ts +0 -361
- package/permits/test-utils.ts +0 -28
- package/permits/types.ts +0 -204
- package/permits/validation.ts +0 -327
- /package/{permits → acps}/utils.ts +0 -0
package/dist/acps.cjs
ADDED
|
@@ -0,0 +1,1081 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var viem = require('viem');
|
|
4
|
+
var nacl = require('tweetnacl');
|
|
5
|
+
var zod = require('zod');
|
|
6
|
+
var vanilla = require('zustand/vanilla');
|
|
7
|
+
var middleware = require('zustand/middleware');
|
|
8
|
+
var immer = require('immer');
|
|
9
|
+
|
|
10
|
+
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
11
|
+
|
|
12
|
+
var nacl__default = /*#__PURE__*/_interopDefault(nacl);
|
|
13
|
+
|
|
14
|
+
// acps/acp.ts
|
|
15
|
+
|
|
16
|
+
// acps/utils.ts
|
|
17
|
+
var fromHexString = (hexString) => {
|
|
18
|
+
const cleanString = hexString.length % 2 === 1 ? `0${hexString}` : hexString;
|
|
19
|
+
const arr = cleanString.replace(/^0x/, "").match(/.{1,2}/g);
|
|
20
|
+
if (!arr)
|
|
21
|
+
return new Uint8Array();
|
|
22
|
+
return new Uint8Array(arr.map((byte) => parseInt(byte, 16)));
|
|
23
|
+
};
|
|
24
|
+
var toHexString = (bytes) => bytes.reduce((str, byte) => str + byte.toString(16).padStart(2, "0"), "");
|
|
25
|
+
function toBigInt(value) {
|
|
26
|
+
if (typeof value === "string") {
|
|
27
|
+
return BigInt(value);
|
|
28
|
+
} else if (typeof value === "number") {
|
|
29
|
+
return BigInt(value);
|
|
30
|
+
} else if (typeof value === "object") {
|
|
31
|
+
return BigInt("0x" + toHexString(value));
|
|
32
|
+
} else {
|
|
33
|
+
return value;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
function toBeArray(value) {
|
|
37
|
+
const bigIntValue = typeof value === "number" ? BigInt(value) : value;
|
|
38
|
+
const hex = bigIntValue.toString(16);
|
|
39
|
+
const paddedHex = hex.length % 2 === 0 ? hex : "0" + hex;
|
|
40
|
+
return fromHexString(paddedHex);
|
|
41
|
+
}
|
|
42
|
+
function isString(value) {
|
|
43
|
+
if (typeof value !== "string") {
|
|
44
|
+
throw new Error(`Expected value which is \`string\`, received value of type \`${typeof value}\`.`);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
function isNumber(value) {
|
|
48
|
+
const is = typeof value === "number" && !Number.isNaN(value);
|
|
49
|
+
if (!is) {
|
|
50
|
+
throw new Error(`Expected value which is \`number\`, received value of type \`${typeof value}\`.`);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
function isBigIntOrNumber(value) {
|
|
54
|
+
const is = typeof value === "bigint";
|
|
55
|
+
if (!is) {
|
|
56
|
+
try {
|
|
57
|
+
isNumber(value);
|
|
58
|
+
} catch (e) {
|
|
59
|
+
throw new Error(`Value ${value} is not a number or bigint: ${typeof value}`);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// acps/sealing.ts
|
|
65
|
+
var KEY_HEX_LENGTH = 64;
|
|
66
|
+
var GenerateSealingKey = () => {
|
|
67
|
+
const sodiumKeypair = nacl__default.default.box.keyPair();
|
|
68
|
+
return {
|
|
69
|
+
privateKey: `0x${toHexString(sodiumKeypair.secretKey)}`,
|
|
70
|
+
publicKey: `0x${toHexString(sodiumKeypair.publicKey)}`
|
|
71
|
+
};
|
|
72
|
+
};
|
|
73
|
+
var seal = (value, publicKey) => {
|
|
74
|
+
isString(publicKey);
|
|
75
|
+
isBigIntOrNumber(value);
|
|
76
|
+
assertKeyLength(publicKey, "Public");
|
|
77
|
+
const ephemeralKeyPair = nacl__default.default.box.keyPair();
|
|
78
|
+
const nonce = nacl__default.default.randomBytes(nacl__default.default.box.nonceLength);
|
|
79
|
+
const encryptedMessage = nacl__default.default.box(toBeArray(value), nonce, fromHexString(publicKey), ephemeralKeyPair.secretKey);
|
|
80
|
+
return {
|
|
81
|
+
data: encryptedMessage,
|
|
82
|
+
public_key: ephemeralKeyPair.publicKey,
|
|
83
|
+
nonce
|
|
84
|
+
};
|
|
85
|
+
};
|
|
86
|
+
var unsealWithPrivateKey = (privateKey, parsedData) => {
|
|
87
|
+
assertKeyLength(privateKey, "Private");
|
|
88
|
+
const nonce = parsedData.nonce instanceof Uint8Array ? parsedData.nonce : new Uint8Array(parsedData.nonce);
|
|
89
|
+
const ephemPublicKey = parsedData.public_key instanceof Uint8Array ? parsedData.public_key : new Uint8Array(parsedData.public_key);
|
|
90
|
+
const dataToDecrypt = parsedData.data instanceof Uint8Array ? parsedData.data : new Uint8Array(parsedData.data);
|
|
91
|
+
const decryptedMessage = nacl__default.default.box.open(dataToDecrypt, nonce, ephemPublicKey, fromHexString(privateKey));
|
|
92
|
+
if (!decryptedMessage) {
|
|
93
|
+
throw new Error("Failed to decrypt message");
|
|
94
|
+
}
|
|
95
|
+
return toBigInt(decryptedMessage);
|
|
96
|
+
};
|
|
97
|
+
var assertKeyLength = (key, kind) => {
|
|
98
|
+
const bare = key.startsWith("0x") ? key.slice(2) : key;
|
|
99
|
+
if (bare.length !== KEY_HEX_LENGTH) {
|
|
100
|
+
throw new Error(`${kind} key must be of length ${KEY_HEX_LENGTH}`);
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
var ACPScope = {
|
|
104
|
+
Global: 0,
|
|
105
|
+
Contract: 1,
|
|
106
|
+
Handles: 2
|
|
107
|
+
};
|
|
108
|
+
var addressSchema = zod.z.string().refine((val) => viem.isAddress(val), {
|
|
109
|
+
error: "Invalid address"
|
|
110
|
+
}).transform((val) => viem.getAddress(val));
|
|
111
|
+
var addressNotZeroSchema = addressSchema.refine((val) => val !== viem.zeroAddress, {
|
|
112
|
+
error: "Must not be zeroAddress"
|
|
113
|
+
});
|
|
114
|
+
var bytesSchema = zod.z.custom(
|
|
115
|
+
(val) => {
|
|
116
|
+
return typeof val === "string" && viem.isHex(val);
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
message: "Invalid hex value"
|
|
120
|
+
}
|
|
121
|
+
);
|
|
122
|
+
var bytesNotEmptySchema = bytesSchema.refine((val) => val !== "0x", {
|
|
123
|
+
error: "Must not be empty"
|
|
124
|
+
});
|
|
125
|
+
var DEFAULT_EXPIRATION_FN = () => Math.round(Date.now() / 1e3) + 7 * 24 * 60 * 60;
|
|
126
|
+
var handlesSchema = zod.z.array(zod.z.string().regex(/^0x[0-9a-fA-F]{64}$/, "Invalid handle: expected 32-byte hex")).optional().default([]);
|
|
127
|
+
var contractsSchema = zod.z.array(addressSchema).optional().default([]);
|
|
128
|
+
var SCOPE_GLOBAL = 0;
|
|
129
|
+
var SCOPE_CONTRACT = 1;
|
|
130
|
+
var SCOPE_HANDLES = 2;
|
|
131
|
+
var ScopeConsistencyRefinement = [
|
|
132
|
+
(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,
|
|
133
|
+
{
|
|
134
|
+
error: "ACP scope :: arrays must match the scope mode (Global: both empty; Contract: contracts only; Handles: handles only)",
|
|
135
|
+
path: ["scope"]
|
|
136
|
+
}
|
|
137
|
+
];
|
|
138
|
+
var withDerivedScope = (data) => ({
|
|
139
|
+
...data,
|
|
140
|
+
scope: data.scope ?? (data.contracts.length > 0 ? SCOPE_CONTRACT : data.handles.length > 0 ? SCOPE_HANDLES : SCOPE_GLOBAL)
|
|
141
|
+
});
|
|
142
|
+
var zACPWithDefaults = zod.z.object({
|
|
143
|
+
name: zod.z.string().optional().default("Unnamed ACP"),
|
|
144
|
+
type: zod.z.enum(["self", "sharing", "recipient"]),
|
|
145
|
+
issuer: addressNotZeroSchema,
|
|
146
|
+
expiration: zod.z.int().optional().default(DEFAULT_EXPIRATION_FN),
|
|
147
|
+
recipient: addressSchema.optional().default(viem.zeroAddress),
|
|
148
|
+
revokerData: zod.z.int().optional().default(0),
|
|
149
|
+
revokerContract: addressSchema.optional().default(viem.zeroAddress),
|
|
150
|
+
scope: zod.z.int().min(0).max(2).optional().default(0),
|
|
151
|
+
contracts: contractsSchema,
|
|
152
|
+
handles: handlesSchema,
|
|
153
|
+
issuerSignature: bytesSchema.optional().default("0x"),
|
|
154
|
+
recipientSignature: bytesSchema.optional().default("0x")
|
|
155
|
+
});
|
|
156
|
+
var zACPWithSealingKeys = zACPWithDefaults.extend({
|
|
157
|
+
/** X25519 private key, 0x-prefixed 32-byte hex; never leaves the client */
|
|
158
|
+
sealingPrivateKey: zod.z.string().regex(/^0x[0-9a-fA-F]{64}$/, "Invalid sealing private key").optional(),
|
|
159
|
+
sealingKey: zod.z.string().regex(/^0x[0-9a-fA-F]{64}$/, "Invalid sealing key").optional()
|
|
160
|
+
});
|
|
161
|
+
var ExternalValidatorRefinement = [
|
|
162
|
+
(data) => data.revokerData !== 0 && data.revokerContract !== viem.zeroAddress || data.revokerData === 0 && data.revokerContract === viem.zeroAddress,
|
|
163
|
+
{
|
|
164
|
+
error: "ACP external revoker :: revokerData and revokerContract must either both be set or both be unset.",
|
|
165
|
+
path: ["revokerData", "revokerContract"]
|
|
166
|
+
}
|
|
167
|
+
];
|
|
168
|
+
var RecipientRefinement = [
|
|
169
|
+
(data) => data.issuer !== data.recipient,
|
|
170
|
+
{
|
|
171
|
+
error: "Sharing acp :: issuer and recipient must not be the same",
|
|
172
|
+
path: ["issuer", "recipient"]
|
|
173
|
+
}
|
|
174
|
+
];
|
|
175
|
+
var SelfACPOptionsValidator = zod.z.object({
|
|
176
|
+
type: zod.z.literal("self").optional().default("self"),
|
|
177
|
+
issuer: addressNotZeroSchema,
|
|
178
|
+
name: zod.z.string().optional().default("Unnamed ACP"),
|
|
179
|
+
expiration: zod.z.int().optional().default(DEFAULT_EXPIRATION_FN),
|
|
180
|
+
recipient: addressSchema.optional().default(viem.zeroAddress),
|
|
181
|
+
revokerData: zod.z.int().optional().default(0),
|
|
182
|
+
revokerContract: addressSchema.optional().default(viem.zeroAddress),
|
|
183
|
+
scope: zod.z.int().min(0).max(2).optional(),
|
|
184
|
+
contracts: contractsSchema,
|
|
185
|
+
handles: handlesSchema,
|
|
186
|
+
issuerSignature: bytesSchema.optional().default("0x"),
|
|
187
|
+
recipientSignature: bytesSchema.optional().default("0x")
|
|
188
|
+
}).refine(...ExternalValidatorRefinement).transform(withDerivedScope).refine(...ScopeConsistencyRefinement);
|
|
189
|
+
var SelfACPValidator = zACPWithSealingKeys.refine((data) => data.type === "self", {
|
|
190
|
+
error: "Type must be 'self'"
|
|
191
|
+
}).refine((data) => data.recipient === viem.zeroAddress, {
|
|
192
|
+
error: "Recipient must be zeroAddress"
|
|
193
|
+
}).refine((data) => data.issuerSignature !== "0x", {
|
|
194
|
+
error: "IssuerSignature must be populated"
|
|
195
|
+
}).refine((data) => data.recipientSignature === "0x", {
|
|
196
|
+
error: "RecipientSignature must be empty"
|
|
197
|
+
}).refine(...ExternalValidatorRefinement);
|
|
198
|
+
var SharingACPOptionsValidator = zod.z.object({
|
|
199
|
+
type: zod.z.literal("sharing").optional().default("sharing"),
|
|
200
|
+
issuer: addressNotZeroSchema,
|
|
201
|
+
recipient: addressNotZeroSchema,
|
|
202
|
+
name: zod.z.string().optional().default("Unnamed ACP"),
|
|
203
|
+
expiration: zod.z.int().optional().default(DEFAULT_EXPIRATION_FN),
|
|
204
|
+
revokerData: zod.z.int().optional().default(0),
|
|
205
|
+
revokerContract: addressSchema.optional().default(viem.zeroAddress),
|
|
206
|
+
scope: zod.z.int().min(0).max(2).optional(),
|
|
207
|
+
contracts: contractsSchema,
|
|
208
|
+
handles: handlesSchema,
|
|
209
|
+
issuerSignature: bytesSchema.optional().default("0x"),
|
|
210
|
+
recipientSignature: bytesSchema.optional().default("0x")
|
|
211
|
+
}).refine(...RecipientRefinement).refine(...ExternalValidatorRefinement).transform(withDerivedScope).refine(...ScopeConsistencyRefinement);
|
|
212
|
+
var SharingACPValidator = zACPWithSealingKeys.refine((data) => data.type === "sharing", {
|
|
213
|
+
error: "Type must be 'sharing'"
|
|
214
|
+
}).refine((data) => data.recipient !== viem.zeroAddress, {
|
|
215
|
+
error: "Recipient must not be zeroAddress"
|
|
216
|
+
}).refine((data) => data.issuerSignature !== "0x", {
|
|
217
|
+
error: "IssuerSignature must be populated"
|
|
218
|
+
}).refine((data) => data.recipientSignature === "0x", {
|
|
219
|
+
error: "RecipientSignature must be empty"
|
|
220
|
+
}).refine(...ExternalValidatorRefinement);
|
|
221
|
+
var ImportACPOptionsValidator = zod.z.object({
|
|
222
|
+
type: zod.z.literal("recipient").optional().default("recipient"),
|
|
223
|
+
issuer: addressNotZeroSchema,
|
|
224
|
+
recipient: addressNotZeroSchema,
|
|
225
|
+
name: zod.z.string().optional().default("Unnamed ACP"),
|
|
226
|
+
expiration: zod.z.int(),
|
|
227
|
+
revokerData: zod.z.int().optional().default(0),
|
|
228
|
+
revokerContract: addressSchema.optional().default(viem.zeroAddress),
|
|
229
|
+
scope: zod.z.int().min(0).max(2).optional(),
|
|
230
|
+
contracts: contractsSchema,
|
|
231
|
+
handles: handlesSchema,
|
|
232
|
+
issuerSignature: bytesNotEmptySchema,
|
|
233
|
+
recipientSignature: bytesSchema.optional().default("0x")
|
|
234
|
+
}).refine(...ExternalValidatorRefinement).transform(withDerivedScope).refine(...ScopeConsistencyRefinement);
|
|
235
|
+
var ImportACPValidator = zACPWithSealingKeys.refine((data) => data.type === "recipient", {
|
|
236
|
+
error: "Type must be 'recipient'"
|
|
237
|
+
}).refine((data) => data.recipient !== viem.zeroAddress, {
|
|
238
|
+
error: "Recipient must not be zeroAddress"
|
|
239
|
+
}).refine((data) => data.issuerSignature !== "0x", {
|
|
240
|
+
error: "IssuerSignature must be populated"
|
|
241
|
+
}).refine((data) => data.recipientSignature !== "0x", {
|
|
242
|
+
error: "RecipientSignature must be populated"
|
|
243
|
+
}).refine(...ExternalValidatorRefinement);
|
|
244
|
+
var safeParseAndThrowFormatted = (schema, data, message) => {
|
|
245
|
+
const result = schema.safeParse(data);
|
|
246
|
+
if (!result.success) {
|
|
247
|
+
throw new Error(`${message}: ${zod.z.prettifyError(result.error)}`, { cause: result.error });
|
|
248
|
+
}
|
|
249
|
+
return result.data;
|
|
250
|
+
};
|
|
251
|
+
var validateSelfACPOptions = (options) => {
|
|
252
|
+
return safeParseAndThrowFormatted(SelfACPOptionsValidator, options, "Invalid self acp options");
|
|
253
|
+
};
|
|
254
|
+
var validateSharingACPOptions = (options) => {
|
|
255
|
+
return safeParseAndThrowFormatted(SharingACPOptionsValidator, options, "Invalid sharing acp options");
|
|
256
|
+
};
|
|
257
|
+
var validateImportACPOptions = (options) => {
|
|
258
|
+
return safeParseAndThrowFormatted(ImportACPOptionsValidator, options, "Invalid import acp options");
|
|
259
|
+
};
|
|
260
|
+
var validateSelfACP = (acp) => {
|
|
261
|
+
return safeParseAndThrowFormatted(SelfACPValidator, acp, "Invalid self acp");
|
|
262
|
+
};
|
|
263
|
+
var validateSharingACP = (acp) => {
|
|
264
|
+
return safeParseAndThrowFormatted(SharingACPValidator, acp, "Invalid sharing acp");
|
|
265
|
+
};
|
|
266
|
+
var validateImportACP = (acp) => {
|
|
267
|
+
return safeParseAndThrowFormatted(ImportACPValidator, acp, "Invalid import acp");
|
|
268
|
+
};
|
|
269
|
+
var ValidationUtils = {
|
|
270
|
+
/**
|
|
271
|
+
* Check if acp is expired
|
|
272
|
+
*/
|
|
273
|
+
isExpired: (acp) => {
|
|
274
|
+
return acp.expiration < Math.floor(Date.now() / 1e3);
|
|
275
|
+
},
|
|
276
|
+
/**
|
|
277
|
+
* Check if acp is signed by the active party
|
|
278
|
+
*/
|
|
279
|
+
isSigned: (acp) => {
|
|
280
|
+
if (acp.type === "self" || acp.type === "sharing") {
|
|
281
|
+
return acp.issuerSignature !== "0x";
|
|
282
|
+
}
|
|
283
|
+
if (acp.type === "recipient") {
|
|
284
|
+
return acp.recipientSignature !== "0x";
|
|
285
|
+
}
|
|
286
|
+
return false;
|
|
287
|
+
},
|
|
288
|
+
/**
|
|
289
|
+
* Checks that an ACP is signed and not expired.
|
|
290
|
+
*/
|
|
291
|
+
isSignedAndNotExpired: (acp) => {
|
|
292
|
+
if (ValidationUtils.isExpired(acp)) {
|
|
293
|
+
return { valid: false, error: "expired" };
|
|
294
|
+
}
|
|
295
|
+
if (!ValidationUtils.isSigned(acp)) {
|
|
296
|
+
return { valid: false, error: "not-signed" };
|
|
297
|
+
}
|
|
298
|
+
return { valid: true, error: null };
|
|
299
|
+
},
|
|
300
|
+
/**
|
|
301
|
+
* Asserts that an ACP is signed and not expired.
|
|
302
|
+
*
|
|
303
|
+
* Throws `Error` with message:
|
|
304
|
+
* - `ACP is expired`
|
|
305
|
+
* - `ACP is not signed`
|
|
306
|
+
*/
|
|
307
|
+
assertSignedAndNotExpired: (acp) => {
|
|
308
|
+
const result = ValidationUtils.isSignedAndNotExpired(acp);
|
|
309
|
+
if (result.valid)
|
|
310
|
+
return;
|
|
311
|
+
if (result.error === "expired") {
|
|
312
|
+
throw new Error("ACP is expired");
|
|
313
|
+
}
|
|
314
|
+
if (result.error === "not-signed") {
|
|
315
|
+
throw new Error("ACP is not signed");
|
|
316
|
+
}
|
|
317
|
+
throw new Error("ACP is invalid");
|
|
318
|
+
},
|
|
319
|
+
isValid: (acp) => {
|
|
320
|
+
const schema = acp.type === "self" ? SelfACPValidator : acp.type === "sharing" ? SharingACPValidator : acp.type === "recipient" ? ImportACPValidator : null;
|
|
321
|
+
if (schema == null)
|
|
322
|
+
return { valid: false, error: "invalid-schema" };
|
|
323
|
+
const schemaResult = schema.safeParse(acp);
|
|
324
|
+
if (!schemaResult.success)
|
|
325
|
+
return { valid: false, error: "invalid-schema" };
|
|
326
|
+
return ValidationUtils.isSignedAndNotExpired(acp);
|
|
327
|
+
}
|
|
328
|
+
};
|
|
329
|
+
|
|
330
|
+
// acps/signature.ts
|
|
331
|
+
var ACPSignatureAllFields = [
|
|
332
|
+
{ name: "issuer", type: "address" },
|
|
333
|
+
{ name: "expiration", type: "uint64" },
|
|
334
|
+
{ name: "recipient", type: "address" },
|
|
335
|
+
{ name: "revokerData", type: "uint256" },
|
|
336
|
+
{ name: "revokerContract", type: "address" },
|
|
337
|
+
{ name: "scope", type: "uint8" },
|
|
338
|
+
{ name: "contracts", type: "address[]" },
|
|
339
|
+
{ name: "handles", type: "bytes32[]" },
|
|
340
|
+
{ name: "sealingKey", type: "bytes32" },
|
|
341
|
+
{ name: "issuerSignature", type: "bytes" }
|
|
342
|
+
];
|
|
343
|
+
var SignatureTypes = {
|
|
344
|
+
ACPIssuerSelf: [
|
|
345
|
+
"issuer",
|
|
346
|
+
"expiration",
|
|
347
|
+
"recipient",
|
|
348
|
+
"revokerData",
|
|
349
|
+
"revokerContract",
|
|
350
|
+
"scope",
|
|
351
|
+
"contracts",
|
|
352
|
+
"handles",
|
|
353
|
+
"sealingKey"
|
|
354
|
+
],
|
|
355
|
+
ACPIssuerShared: [
|
|
356
|
+
"issuer",
|
|
357
|
+
"expiration",
|
|
358
|
+
"recipient",
|
|
359
|
+
"revokerData",
|
|
360
|
+
"revokerContract",
|
|
361
|
+
"scope",
|
|
362
|
+
"contracts",
|
|
363
|
+
"handles"
|
|
364
|
+
],
|
|
365
|
+
ACPRecipient: ["sealingKey", "issuerSignature"]
|
|
366
|
+
};
|
|
367
|
+
var getSignatureTypesAndMessage = (primaryType, fields, values) => {
|
|
368
|
+
const types = {
|
|
369
|
+
[primaryType]: ACPSignatureAllFields.filter((fieldType) => fields.includes(fieldType.name))
|
|
370
|
+
};
|
|
371
|
+
const message = {};
|
|
372
|
+
fields.forEach((field) => {
|
|
373
|
+
if (field in values) {
|
|
374
|
+
message[field] = values[field];
|
|
375
|
+
}
|
|
376
|
+
});
|
|
377
|
+
return { types, primaryType, message };
|
|
378
|
+
};
|
|
379
|
+
var SignatureUtils = {
|
|
380
|
+
/**
|
|
381
|
+
* Get signature parameters for an ACP
|
|
382
|
+
*/
|
|
383
|
+
getSignatureParams: (acp, primaryType) => {
|
|
384
|
+
return getSignatureTypesAndMessage(primaryType, SignatureTypes[primaryType], acp);
|
|
385
|
+
},
|
|
386
|
+
/**
|
|
387
|
+
* Determine the required signature type based on acp type
|
|
388
|
+
*/
|
|
389
|
+
getPrimaryType: (acpType) => {
|
|
390
|
+
if (acpType === "self")
|
|
391
|
+
return "ACPIssuerSelf";
|
|
392
|
+
if (acpType === "sharing")
|
|
393
|
+
return "ACPIssuerShared";
|
|
394
|
+
if (acpType === "recipient")
|
|
395
|
+
return "ACPRecipient";
|
|
396
|
+
throw new Error(`Unknown acp type: ${acpType}`);
|
|
397
|
+
}
|
|
398
|
+
};
|
|
399
|
+
|
|
400
|
+
// core/consts.ts
|
|
401
|
+
var TASK_MANAGER_ADDRESS = "0xeA30c4B8b44078Bbf8a6ef5b9f1eC1626C7848D9";
|
|
402
|
+
BigInt(1 << 30);
|
|
403
|
+
|
|
404
|
+
// acps/onchain-utils.ts
|
|
405
|
+
var getAclAddress = async (publicClient) => {
|
|
406
|
+
const ACL_IFACE = "function acl() view returns (address)";
|
|
407
|
+
const aclAbi = viem.parseAbi([ACL_IFACE]);
|
|
408
|
+
return await publicClient.readContract({
|
|
409
|
+
address: TASK_MANAGER_ADDRESS,
|
|
410
|
+
abi: aclAbi,
|
|
411
|
+
functionName: "acl"
|
|
412
|
+
});
|
|
413
|
+
};
|
|
414
|
+
var getAclEIP712Domain = async (publicClient) => {
|
|
415
|
+
const aclAddress = await getAclAddress(publicClient);
|
|
416
|
+
const EIP712_DOMAIN_IFACE = "function eip712Domain() public view returns (bytes1 fields, string name, string version, uint256 chainId, address verifyingContract, bytes32 salt, uint256[] extensions)";
|
|
417
|
+
const domainAbi = viem.parseAbi([EIP712_DOMAIN_IFACE]);
|
|
418
|
+
const domain = await publicClient.readContract({
|
|
419
|
+
address: aclAddress,
|
|
420
|
+
abi: domainAbi,
|
|
421
|
+
functionName: "eip712Domain"
|
|
422
|
+
});
|
|
423
|
+
const [_fields, name, version, chainId, verifyingContract, _salt, _extensions] = domain;
|
|
424
|
+
if (version !== "2") {
|
|
425
|
+
throw new Error(
|
|
426
|
+
`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.`
|
|
427
|
+
);
|
|
428
|
+
}
|
|
429
|
+
return {
|
|
430
|
+
name,
|
|
431
|
+
version,
|
|
432
|
+
chainId: Number(chainId),
|
|
433
|
+
verifyingContract
|
|
434
|
+
};
|
|
435
|
+
};
|
|
436
|
+
var checkACPValidityOnChain = async (acp, publicClient) => {
|
|
437
|
+
const aclAddress = await getAclAddress(publicClient);
|
|
438
|
+
try {
|
|
439
|
+
await publicClient.simulateContract({
|
|
440
|
+
address: aclAddress,
|
|
441
|
+
abi: checkACPValidityAbi,
|
|
442
|
+
functionName: "checkPermissionValidity",
|
|
443
|
+
args: [
|
|
444
|
+
{
|
|
445
|
+
issuer: acp.issuer,
|
|
446
|
+
expiration: BigInt(acp.expiration),
|
|
447
|
+
recipient: acp.recipient,
|
|
448
|
+
revokerData: BigInt(acp.revokerData),
|
|
449
|
+
revokerContract: acp.revokerContract,
|
|
450
|
+
scope: acp.scope,
|
|
451
|
+
contracts: acp.contracts,
|
|
452
|
+
handles: acp.handles,
|
|
453
|
+
sealingKey: acp.sealingKey,
|
|
454
|
+
issuerSignature: acp.issuerSignature,
|
|
455
|
+
recipientSignature: acp.recipientSignature
|
|
456
|
+
}
|
|
457
|
+
]
|
|
458
|
+
});
|
|
459
|
+
return true;
|
|
460
|
+
} catch (err) {
|
|
461
|
+
if (err instanceof viem.BaseError) {
|
|
462
|
+
const revertError = err.walk((err2) => err2 instanceof viem.ContractFunctionRevertedError);
|
|
463
|
+
if (revertError instanceof viem.ContractFunctionRevertedError) {
|
|
464
|
+
const errorName = revertError.data?.errorName ?? "";
|
|
465
|
+
throw new Error(errorName);
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
const customErrorName = extractCustomErrorFromDetails(err, checkACPValidityAbi);
|
|
469
|
+
if (customErrorName) {
|
|
470
|
+
throw new Error(customErrorName);
|
|
471
|
+
}
|
|
472
|
+
const hhDetailsData = extractReturnData(err);
|
|
473
|
+
if (hhDetailsData != null) {
|
|
474
|
+
const decoded = viem.decodeErrorResult({
|
|
475
|
+
abi: checkACPValidityAbi,
|
|
476
|
+
data: hhDetailsData
|
|
477
|
+
});
|
|
478
|
+
throw new Error(decoded.errorName);
|
|
479
|
+
}
|
|
480
|
+
throw err;
|
|
481
|
+
}
|
|
482
|
+
};
|
|
483
|
+
function extractCustomErrorFromDetails(err, abi) {
|
|
484
|
+
const anyErr = err;
|
|
485
|
+
const details = anyErr?.details ?? anyErr?.cause?.details;
|
|
486
|
+
if (typeof details === "string") {
|
|
487
|
+
const customErrorMatch = details.match(/reverted with custom error '(\w+)\(\)'/);
|
|
488
|
+
if (customErrorMatch) {
|
|
489
|
+
const errorName = customErrorMatch[1];
|
|
490
|
+
const errorExists = abi.some((item) => item.type === "error" && item.name === errorName);
|
|
491
|
+
if (errorExists) {
|
|
492
|
+
return errorName;
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
return void 0;
|
|
497
|
+
}
|
|
498
|
+
function extractReturnData(err) {
|
|
499
|
+
const anyErr = err;
|
|
500
|
+
const s = anyErr?.details ?? anyErr?.cause?.details ?? anyErr?.shortMessage ?? anyErr?.message ?? String(err);
|
|
501
|
+
return s.match(/return data:\s*(0x[a-fA-F0-9]+)/)?.[1];
|
|
502
|
+
}
|
|
503
|
+
var checkACPValidityAbi = [
|
|
504
|
+
{
|
|
505
|
+
type: "function",
|
|
506
|
+
name: "checkPermissionValidity",
|
|
507
|
+
inputs: [
|
|
508
|
+
{
|
|
509
|
+
name: "acp",
|
|
510
|
+
type: "tuple",
|
|
511
|
+
internalType: "struct ACP",
|
|
512
|
+
components: [
|
|
513
|
+
{
|
|
514
|
+
name: "issuer",
|
|
515
|
+
type: "address",
|
|
516
|
+
internalType: "address"
|
|
517
|
+
},
|
|
518
|
+
{
|
|
519
|
+
name: "expiration",
|
|
520
|
+
type: "uint64",
|
|
521
|
+
internalType: "uint64"
|
|
522
|
+
},
|
|
523
|
+
{
|
|
524
|
+
name: "recipient",
|
|
525
|
+
type: "address",
|
|
526
|
+
internalType: "address"
|
|
527
|
+
},
|
|
528
|
+
{
|
|
529
|
+
name: "revokerData",
|
|
530
|
+
type: "uint256",
|
|
531
|
+
internalType: "uint256"
|
|
532
|
+
},
|
|
533
|
+
{
|
|
534
|
+
name: "revokerContract",
|
|
535
|
+
type: "address",
|
|
536
|
+
internalType: "address"
|
|
537
|
+
},
|
|
538
|
+
{
|
|
539
|
+
name: "scope",
|
|
540
|
+
type: "uint8",
|
|
541
|
+
internalType: "uint8"
|
|
542
|
+
},
|
|
543
|
+
{
|
|
544
|
+
name: "contracts",
|
|
545
|
+
type: "address[]",
|
|
546
|
+
internalType: "address[]"
|
|
547
|
+
},
|
|
548
|
+
{
|
|
549
|
+
name: "handles",
|
|
550
|
+
type: "bytes32[]",
|
|
551
|
+
internalType: "bytes32[]"
|
|
552
|
+
},
|
|
553
|
+
{
|
|
554
|
+
name: "sealingKey",
|
|
555
|
+
type: "bytes32",
|
|
556
|
+
internalType: "bytes32"
|
|
557
|
+
},
|
|
558
|
+
{
|
|
559
|
+
name: "issuerSignature",
|
|
560
|
+
type: "bytes",
|
|
561
|
+
internalType: "bytes"
|
|
562
|
+
},
|
|
563
|
+
{
|
|
564
|
+
name: "recipientSignature",
|
|
565
|
+
type: "bytes",
|
|
566
|
+
internalType: "bytes"
|
|
567
|
+
}
|
|
568
|
+
]
|
|
569
|
+
}
|
|
570
|
+
],
|
|
571
|
+
outputs: [
|
|
572
|
+
{
|
|
573
|
+
name: "",
|
|
574
|
+
type: "bool",
|
|
575
|
+
internalType: "bool"
|
|
576
|
+
}
|
|
577
|
+
],
|
|
578
|
+
stateMutability: "view"
|
|
579
|
+
},
|
|
580
|
+
{
|
|
581
|
+
type: "error",
|
|
582
|
+
name: "PermissionInvalid_Disabled",
|
|
583
|
+
inputs: []
|
|
584
|
+
},
|
|
585
|
+
{
|
|
586
|
+
type: "error",
|
|
587
|
+
name: "PermissionInvalid_Expired",
|
|
588
|
+
inputs: []
|
|
589
|
+
},
|
|
590
|
+
{
|
|
591
|
+
type: "error",
|
|
592
|
+
name: "PermissionInvalid_IssuerSignature",
|
|
593
|
+
inputs: []
|
|
594
|
+
},
|
|
595
|
+
{
|
|
596
|
+
type: "error",
|
|
597
|
+
name: "PermissionInvalid_RecipientSignature",
|
|
598
|
+
inputs: []
|
|
599
|
+
}
|
|
600
|
+
];
|
|
601
|
+
|
|
602
|
+
// acps/acp.ts
|
|
603
|
+
var ACPUtils = {
|
|
604
|
+
/**
|
|
605
|
+
* Create a self acp for personal use
|
|
606
|
+
*/
|
|
607
|
+
createSelf: (options) => {
|
|
608
|
+
const validation = validateSelfACPOptions(options);
|
|
609
|
+
const sealingPair = GenerateSealingKey();
|
|
610
|
+
const acp = {
|
|
611
|
+
hash: ACPUtils.getHash(validation),
|
|
612
|
+
...validation,
|
|
613
|
+
sealingPrivateKey: sealingPair.privateKey,
|
|
614
|
+
sealingKey: sealingPair.publicKey,
|
|
615
|
+
_signedDomain: void 0
|
|
616
|
+
};
|
|
617
|
+
return acp;
|
|
618
|
+
},
|
|
619
|
+
/**
|
|
620
|
+
* Create a sharing acp to be shared with another user
|
|
621
|
+
*/
|
|
622
|
+
createSharing: (options) => {
|
|
623
|
+
const validation = validateSharingACPOptions(options);
|
|
624
|
+
const sealingPair = GenerateSealingKey();
|
|
625
|
+
const acp = {
|
|
626
|
+
hash: ACPUtils.getHash(validation),
|
|
627
|
+
...validation,
|
|
628
|
+
sealingPrivateKey: sealingPair.privateKey,
|
|
629
|
+
sealingKey: sealingPair.publicKey,
|
|
630
|
+
_signedDomain: void 0
|
|
631
|
+
};
|
|
632
|
+
return acp;
|
|
633
|
+
},
|
|
634
|
+
/**
|
|
635
|
+
* Import a shared acp from various input formats
|
|
636
|
+
*/
|
|
637
|
+
importShared: (options) => {
|
|
638
|
+
let parsedOptions;
|
|
639
|
+
if (typeof options === "string") {
|
|
640
|
+
try {
|
|
641
|
+
parsedOptions = JSON.parse(options);
|
|
642
|
+
} catch (error) {
|
|
643
|
+
throw new Error(`Failed to parse JSON string: ${error}`);
|
|
644
|
+
}
|
|
645
|
+
} else if (typeof options === "object" && options !== null) {
|
|
646
|
+
parsedOptions = options;
|
|
647
|
+
} else {
|
|
648
|
+
throw new Error("Invalid input type, expected ImportSharedACPOptions, object, or string");
|
|
649
|
+
}
|
|
650
|
+
if (parsedOptions.type != null && parsedOptions.type !== "sharing") {
|
|
651
|
+
throw new Error(`Invalid acp type <${parsedOptions.type}>, must be "sharing"`);
|
|
652
|
+
}
|
|
653
|
+
const validation = validateImportACPOptions({ ...parsedOptions, type: "recipient" });
|
|
654
|
+
const sealingPair = GenerateSealingKey();
|
|
655
|
+
const acp = {
|
|
656
|
+
hash: ACPUtils.getHash(validation),
|
|
657
|
+
...validation,
|
|
658
|
+
sealingPrivateKey: sealingPair.privateKey,
|
|
659
|
+
sealingKey: sealingPair.publicKey,
|
|
660
|
+
_signedDomain: void 0
|
|
661
|
+
};
|
|
662
|
+
return acp;
|
|
663
|
+
},
|
|
664
|
+
/**
|
|
665
|
+
* Sign an ACP with the provided wallet client
|
|
666
|
+
*/
|
|
667
|
+
sign: async (acp, publicClient, walletClient) => {
|
|
668
|
+
if (walletClient == null || walletClient.account == null) {
|
|
669
|
+
throw new Error(
|
|
670
|
+
"Missing walletClient, you must pass in a `walletClient` for the connected user to create an ACP signature"
|
|
671
|
+
);
|
|
672
|
+
}
|
|
673
|
+
const primaryType = SignatureUtils.getPrimaryType(acp.type);
|
|
674
|
+
const domain = await getAclEIP712Domain(publicClient);
|
|
675
|
+
const { types, message } = SignatureUtils.getSignatureParams(ACPUtils.getPublic(acp, true), primaryType);
|
|
676
|
+
const signature = await walletClient.signTypedData({
|
|
677
|
+
domain,
|
|
678
|
+
types,
|
|
679
|
+
primaryType,
|
|
680
|
+
message,
|
|
681
|
+
account: walletClient.account
|
|
682
|
+
});
|
|
683
|
+
let updatedACP;
|
|
684
|
+
if (acp.type === "self" || acp.type === "sharing") {
|
|
685
|
+
updatedACP = {
|
|
686
|
+
...acp,
|
|
687
|
+
issuerSignature: signature,
|
|
688
|
+
_signedDomain: domain
|
|
689
|
+
};
|
|
690
|
+
} else {
|
|
691
|
+
updatedACP = {
|
|
692
|
+
...acp,
|
|
693
|
+
recipientSignature: signature,
|
|
694
|
+
_signedDomain: domain
|
|
695
|
+
};
|
|
696
|
+
}
|
|
697
|
+
return updatedACP;
|
|
698
|
+
},
|
|
699
|
+
/**
|
|
700
|
+
* Create and sign a self acp in one operation
|
|
701
|
+
*/
|
|
702
|
+
createSelfAndSign: async (options, publicClient, walletClient) => {
|
|
703
|
+
const acp = ACPUtils.createSelf(options);
|
|
704
|
+
return ACPUtils.sign(acp, publicClient, walletClient);
|
|
705
|
+
},
|
|
706
|
+
/**
|
|
707
|
+
* Create and sign a sharing acp in one operation
|
|
708
|
+
*/
|
|
709
|
+
createSharingAndSign: async (options, publicClient, walletClient) => {
|
|
710
|
+
const acp = ACPUtils.createSharing(options);
|
|
711
|
+
return ACPUtils.sign(acp, publicClient, walletClient);
|
|
712
|
+
},
|
|
713
|
+
/**
|
|
714
|
+
* Import and sign a shared acp in one operation from various input formats
|
|
715
|
+
*/
|
|
716
|
+
importSharedAndSign: async (options, publicClient, walletClient) => {
|
|
717
|
+
const acp = ACPUtils.importShared(options);
|
|
718
|
+
return ACPUtils.sign(acp, publicClient, walletClient);
|
|
719
|
+
},
|
|
720
|
+
/**
|
|
721
|
+
* Deserialize an ACP from serialized data
|
|
722
|
+
*/
|
|
723
|
+
deserialize: (data) => {
|
|
724
|
+
return { ...data };
|
|
725
|
+
},
|
|
726
|
+
/**
|
|
727
|
+
* Serialize an ACP for storage
|
|
728
|
+
*/
|
|
729
|
+
serialize: (acp) => {
|
|
730
|
+
return {
|
|
731
|
+
hash: acp.hash,
|
|
732
|
+
name: acp.name,
|
|
733
|
+
type: acp.type,
|
|
734
|
+
issuer: acp.issuer,
|
|
735
|
+
expiration: acp.expiration,
|
|
736
|
+
recipient: acp.recipient,
|
|
737
|
+
revokerData: acp.revokerData,
|
|
738
|
+
revokerContract: acp.revokerContract,
|
|
739
|
+
scope: acp.scope,
|
|
740
|
+
contracts: acp.contracts,
|
|
741
|
+
handles: acp.handles,
|
|
742
|
+
sealingKey: acp.sealingKey,
|
|
743
|
+
issuerSignature: acp.issuerSignature,
|
|
744
|
+
recipientSignature: acp.recipientSignature,
|
|
745
|
+
_signedDomain: acp._signedDomain,
|
|
746
|
+
sealingPrivateKey: acp.sealingPrivateKey
|
|
747
|
+
};
|
|
748
|
+
},
|
|
749
|
+
/**
|
|
750
|
+
* Validate an ACP (schema-level validation)
|
|
751
|
+
*/
|
|
752
|
+
validateSchema: (acp) => {
|
|
753
|
+
if (acp.type === "self") {
|
|
754
|
+
return validateSelfACP(acp);
|
|
755
|
+
} else if (acp.type === "sharing") {
|
|
756
|
+
return validateSharingACP(acp);
|
|
757
|
+
} else if (acp.type === "recipient") {
|
|
758
|
+
return validateImportACP(acp);
|
|
759
|
+
} else {
|
|
760
|
+
throw new Error("Invalid ACP type");
|
|
761
|
+
}
|
|
762
|
+
},
|
|
763
|
+
/**
|
|
764
|
+
* Validate an ACP (holistic validation).
|
|
765
|
+
*
|
|
766
|
+
* This validates:
|
|
767
|
+
* - ACP schema (shape + invariants)
|
|
768
|
+
* - ACP is signed
|
|
769
|
+
* - ACP is not expired
|
|
770
|
+
*
|
|
771
|
+
* For schema-only validation, use `validateSchema(acp)`.
|
|
772
|
+
*/
|
|
773
|
+
validate: (acp) => {
|
|
774
|
+
const validated = ACPUtils.validateSchema(acp);
|
|
775
|
+
ValidationUtils.assertSignedAndNotExpired(validated);
|
|
776
|
+
return validated;
|
|
777
|
+
},
|
|
778
|
+
/**
|
|
779
|
+
* Get the public component of an ACP — the signed struct sent on-chain / to the decryption backend.
|
|
780
|
+
* Strips the private component (hash, name, type, sealing pair).
|
|
781
|
+
*/
|
|
782
|
+
getPublic: (acp, skipValidation = false) => {
|
|
783
|
+
if (!skipValidation) {
|
|
784
|
+
ACPUtils.validateSchema(acp);
|
|
785
|
+
}
|
|
786
|
+
return {
|
|
787
|
+
issuer: acp.issuer,
|
|
788
|
+
expiration: acp.expiration,
|
|
789
|
+
recipient: acp.recipient,
|
|
790
|
+
revokerData: acp.revokerData,
|
|
791
|
+
revokerContract: acp.revokerContract,
|
|
792
|
+
scope: acp.scope,
|
|
793
|
+
contracts: acp.contracts,
|
|
794
|
+
handles: acp.handles,
|
|
795
|
+
sealingKey: acp.sealingKey,
|
|
796
|
+
issuerSignature: acp.issuerSignature,
|
|
797
|
+
recipientSignature: acp.recipientSignature
|
|
798
|
+
};
|
|
799
|
+
},
|
|
800
|
+
/**
|
|
801
|
+
* Get a stable hash for the acp (used as key in storage)
|
|
802
|
+
*/
|
|
803
|
+
getHash: (acp) => {
|
|
804
|
+
const data = JSON.stringify({
|
|
805
|
+
type: acp.type,
|
|
806
|
+
issuer: acp.issuer,
|
|
807
|
+
expiration: acp.expiration,
|
|
808
|
+
recipient: acp.recipient,
|
|
809
|
+
revokerData: acp.revokerData,
|
|
810
|
+
revokerContract: acp.revokerContract,
|
|
811
|
+
scope: acp.scope,
|
|
812
|
+
contracts: acp.contracts,
|
|
813
|
+
handles: acp.handles
|
|
814
|
+
});
|
|
815
|
+
return viem.keccak256(viem.toHex(data));
|
|
816
|
+
},
|
|
817
|
+
/**
|
|
818
|
+
* Export acp data for sharing (strips the private component).
|
|
819
|
+
* Fixed `SharedACP` shape — every field always present, aligned with
|
|
820
|
+
* `ACPPublic` and the on-chain sharing payload.
|
|
821
|
+
*/
|
|
822
|
+
export: (acp) => {
|
|
823
|
+
if (acp.type !== "sharing") {
|
|
824
|
+
throw new Error(
|
|
825
|
+
`Cannot export a '${acp.type}' ACP \u2014 only 'sharing' ACPs are exportable. The export includes the issuer signature.`
|
|
826
|
+
);
|
|
827
|
+
}
|
|
828
|
+
if (acp.issuerSignature === "0x") {
|
|
829
|
+
throw new Error(
|
|
830
|
+
"Cannot export an unsigned sharing ACP \u2014 sign it first (the recipient needs the issuer signature)."
|
|
831
|
+
);
|
|
832
|
+
}
|
|
833
|
+
const shared = {
|
|
834
|
+
name: acp.name,
|
|
835
|
+
type: acp.type,
|
|
836
|
+
issuer: acp.issuer,
|
|
837
|
+
expiration: acp.expiration,
|
|
838
|
+
recipient: acp.recipient,
|
|
839
|
+
revokerData: acp.revokerData,
|
|
840
|
+
revokerContract: acp.revokerContract,
|
|
841
|
+
scope: acp.scope,
|
|
842
|
+
contracts: acp.contracts,
|
|
843
|
+
handles: acp.handles,
|
|
844
|
+
issuerSignature: acp.issuerSignature
|
|
845
|
+
};
|
|
846
|
+
return JSON.stringify(shared, void 0, 2);
|
|
847
|
+
},
|
|
848
|
+
/**
|
|
849
|
+
* Unseal encrypted data using the acp's sealing key
|
|
850
|
+
*/
|
|
851
|
+
unseal: (acp, ciphertext) => {
|
|
852
|
+
return unsealWithPrivateKey(acp.sealingPrivateKey, ciphertext);
|
|
853
|
+
},
|
|
854
|
+
/**
|
|
855
|
+
* Check if acp is expired
|
|
856
|
+
*/
|
|
857
|
+
isExpired: (acp) => {
|
|
858
|
+
return ValidationUtils.isExpired(acp);
|
|
859
|
+
},
|
|
860
|
+
/**
|
|
861
|
+
* Check if acp is signed
|
|
862
|
+
*/
|
|
863
|
+
isSigned: (acp) => {
|
|
864
|
+
return ValidationUtils.isSigned(acp);
|
|
865
|
+
},
|
|
866
|
+
/**
|
|
867
|
+
* Check if acp is signed and not expired
|
|
868
|
+
*/
|
|
869
|
+
isSignedAndNotExpired: (acp) => {
|
|
870
|
+
return ValidationUtils.isSignedAndNotExpired(acp);
|
|
871
|
+
},
|
|
872
|
+
/**
|
|
873
|
+
* Assert that acp is signed and not expired
|
|
874
|
+
*/
|
|
875
|
+
assertSignedAndNotExpired: (acp) => {
|
|
876
|
+
return ValidationUtils.assertSignedAndNotExpired(acp);
|
|
877
|
+
},
|
|
878
|
+
isValid: (acp) => {
|
|
879
|
+
return ValidationUtils.isValid(acp);
|
|
880
|
+
},
|
|
881
|
+
/**
|
|
882
|
+
* Update acp name (returns new acp instance)
|
|
883
|
+
*/
|
|
884
|
+
updateName: (acp, name) => {
|
|
885
|
+
return { ...acp, name };
|
|
886
|
+
},
|
|
887
|
+
/**
|
|
888
|
+
* Fetch EIP712 domain from the blockchain
|
|
889
|
+
*/
|
|
890
|
+
fetchEIP712Domain: async (publicClient) => {
|
|
891
|
+
return getAclEIP712Domain(publicClient);
|
|
892
|
+
},
|
|
893
|
+
/**
|
|
894
|
+
* Check if acp's signed domain matches the provided domain
|
|
895
|
+
*/
|
|
896
|
+
matchesDomain: (acp, domain) => {
|
|
897
|
+
return acp._signedDomain?.name === domain.name && acp._signedDomain?.version === domain.version && acp._signedDomain?.verifyingContract === domain.verifyingContract && acp._signedDomain?.chainId === domain.chainId;
|
|
898
|
+
},
|
|
899
|
+
/**
|
|
900
|
+
* Check if acp's signed domain is valid for the current chain
|
|
901
|
+
*/
|
|
902
|
+
checkSignedDomainValid: async (acp, publicClient) => {
|
|
903
|
+
if (acp._signedDomain == null)
|
|
904
|
+
return false;
|
|
905
|
+
const domain = await getAclEIP712Domain(publicClient);
|
|
906
|
+
return ACPUtils.matchesDomain(acp, domain);
|
|
907
|
+
},
|
|
908
|
+
/**
|
|
909
|
+
* Check if acp passes the on-chain validation
|
|
910
|
+
*/
|
|
911
|
+
checkValidityOnChain: async (acp, publicClient) => {
|
|
912
|
+
const publicAcp = ACPUtils.getPublic(acp);
|
|
913
|
+
return checkACPValidityOnChain(publicAcp, publicClient);
|
|
914
|
+
}
|
|
915
|
+
};
|
|
916
|
+
var ACP_STORE_DEFAULTS = {
|
|
917
|
+
acps: {},
|
|
918
|
+
activeACPHash: {}
|
|
919
|
+
};
|
|
920
|
+
var ACP_STORE_VERSION = 3;
|
|
921
|
+
var _acpStore = vanilla.createStore()(
|
|
922
|
+
middleware.persist(() => ACP_STORE_DEFAULTS, {
|
|
923
|
+
name: "cofhesdk-acps",
|
|
924
|
+
version: ACP_STORE_VERSION,
|
|
925
|
+
migrate: (persistedState, version) => {
|
|
926
|
+
if (version < ACP_STORE_VERSION)
|
|
927
|
+
return ACP_STORE_DEFAULTS;
|
|
928
|
+
return persistedState;
|
|
929
|
+
}
|
|
930
|
+
})
|
|
931
|
+
);
|
|
932
|
+
var clearStaleStore = () => {
|
|
933
|
+
const state = _acpStore.getState();
|
|
934
|
+
const hasExpectedStructure = state && typeof state === "object" && "acps" in state && "activeACPHash" in state && typeof state.acps === "object" && typeof state.activeACPHash === "object";
|
|
935
|
+
if (hasExpectedStructure)
|
|
936
|
+
return;
|
|
937
|
+
_acpStore.setState({ acps: {}, activeACPHash: {} });
|
|
938
|
+
};
|
|
939
|
+
var getACP = (chainId, account, hash) => {
|
|
940
|
+
clearStaleStore();
|
|
941
|
+
if (chainId == null || account == null || hash == null)
|
|
942
|
+
return;
|
|
943
|
+
const savedACP = _acpStore.getState().acps[chainId]?.[account]?.[hash];
|
|
944
|
+
if (savedACP == null)
|
|
945
|
+
return;
|
|
946
|
+
return ACPUtils.deserialize(savedACP);
|
|
947
|
+
};
|
|
948
|
+
var getActiveACP = (chainId, account) => {
|
|
949
|
+
clearStaleStore();
|
|
950
|
+
if (chainId == null || account == null)
|
|
951
|
+
return;
|
|
952
|
+
const activeACPHash = _acpStore.getState().activeACPHash[chainId]?.[account];
|
|
953
|
+
return getACP(chainId, account, activeACPHash);
|
|
954
|
+
};
|
|
955
|
+
var getACPs = (chainId, account) => {
|
|
956
|
+
clearStaleStore();
|
|
957
|
+
if (chainId == null || account == null)
|
|
958
|
+
return {};
|
|
959
|
+
return Object.entries(_acpStore.getState().acps[chainId]?.[account] ?? {}).reduce(
|
|
960
|
+
(acc, [hash, acp]) => {
|
|
961
|
+
if (acp == void 0)
|
|
962
|
+
return acc;
|
|
963
|
+
return { ...acc, [hash]: ACPUtils.deserialize(acp) };
|
|
964
|
+
},
|
|
965
|
+
{}
|
|
966
|
+
);
|
|
967
|
+
};
|
|
968
|
+
var setACP = (chainId, account, acp) => {
|
|
969
|
+
clearStaleStore();
|
|
970
|
+
_acpStore.setState(
|
|
971
|
+
immer.produce((state) => {
|
|
972
|
+
if (state.acps[chainId] == null)
|
|
973
|
+
state.acps[chainId] = {};
|
|
974
|
+
if (state.acps[chainId][account] == null)
|
|
975
|
+
state.acps[chainId][account] = {};
|
|
976
|
+
state.acps[chainId][account][acp.hash] = ACPUtils.serialize(acp);
|
|
977
|
+
})
|
|
978
|
+
);
|
|
979
|
+
};
|
|
980
|
+
var removeACP = (chainId, account, hash) => {
|
|
981
|
+
clearStaleStore();
|
|
982
|
+
_acpStore.setState(
|
|
983
|
+
immer.produce((state) => {
|
|
984
|
+
if (state.acps[chainId] == null)
|
|
985
|
+
state.acps[chainId] = {};
|
|
986
|
+
if (state.activeACPHash[chainId] == null)
|
|
987
|
+
state.activeACPHash[chainId] = {};
|
|
988
|
+
const accountACPs = state.acps[chainId][account];
|
|
989
|
+
if (accountACPs == null)
|
|
990
|
+
return;
|
|
991
|
+
if (accountACPs[hash] == null)
|
|
992
|
+
return;
|
|
993
|
+
if (state.activeACPHash[chainId][account] === hash) {
|
|
994
|
+
state.activeACPHash[chainId][account] = void 0;
|
|
995
|
+
}
|
|
996
|
+
accountACPs[hash] = void 0;
|
|
997
|
+
})
|
|
998
|
+
);
|
|
999
|
+
};
|
|
1000
|
+
var getActiveACPHash = (chainId, account) => {
|
|
1001
|
+
clearStaleStore();
|
|
1002
|
+
if (chainId == null || account == null)
|
|
1003
|
+
return void 0;
|
|
1004
|
+
return _acpStore.getState().activeACPHash[chainId]?.[account];
|
|
1005
|
+
};
|
|
1006
|
+
var setActiveACPHash = (chainId, account, hash) => {
|
|
1007
|
+
clearStaleStore();
|
|
1008
|
+
_acpStore.setState(
|
|
1009
|
+
immer.produce((state) => {
|
|
1010
|
+
if (state.activeACPHash[chainId] == null)
|
|
1011
|
+
state.activeACPHash[chainId] = {};
|
|
1012
|
+
state.activeACPHash[chainId][account] = hash;
|
|
1013
|
+
})
|
|
1014
|
+
);
|
|
1015
|
+
};
|
|
1016
|
+
var removeActiveACPHash = (chainId, account) => {
|
|
1017
|
+
clearStaleStore();
|
|
1018
|
+
_acpStore.setState(
|
|
1019
|
+
immer.produce((state) => {
|
|
1020
|
+
if (state.activeACPHash[chainId])
|
|
1021
|
+
state.activeACPHash[chainId][account] = void 0;
|
|
1022
|
+
})
|
|
1023
|
+
);
|
|
1024
|
+
};
|
|
1025
|
+
var resetStore = () => {
|
|
1026
|
+
clearStaleStore();
|
|
1027
|
+
_acpStore.setState({ acps: {}, activeACPHash: {} });
|
|
1028
|
+
};
|
|
1029
|
+
var acpStore = {
|
|
1030
|
+
store: _acpStore,
|
|
1031
|
+
getACP,
|
|
1032
|
+
getActiveACP,
|
|
1033
|
+
getACPs,
|
|
1034
|
+
setACP,
|
|
1035
|
+
removeACP,
|
|
1036
|
+
getActiveACPHash,
|
|
1037
|
+
setActiveACPHash,
|
|
1038
|
+
removeActiveACPHash,
|
|
1039
|
+
resetStore
|
|
1040
|
+
};
|
|
1041
|
+
|
|
1042
|
+
exports.ACPScope = ACPScope;
|
|
1043
|
+
exports.ACPUtils = ACPUtils;
|
|
1044
|
+
exports.ACP_STORE_DEFAULTS = ACP_STORE_DEFAULTS;
|
|
1045
|
+
exports.GenerateSealingKey = GenerateSealingKey;
|
|
1046
|
+
exports.ImportACPOptionsValidator = ImportACPOptionsValidator;
|
|
1047
|
+
exports.ImportACPValidator = ImportACPValidator;
|
|
1048
|
+
exports.SelfACPOptionsValidator = SelfACPOptionsValidator;
|
|
1049
|
+
exports.SelfACPValidator = SelfACPValidator;
|
|
1050
|
+
exports.SharingACPOptionsValidator = SharingACPOptionsValidator;
|
|
1051
|
+
exports.SharingACPValidator = SharingACPValidator;
|
|
1052
|
+
exports.SignatureTypes = SignatureTypes;
|
|
1053
|
+
exports.SignatureUtils = SignatureUtils;
|
|
1054
|
+
exports.ValidationUtils = ValidationUtils;
|
|
1055
|
+
exports._acpStore = _acpStore;
|
|
1056
|
+
exports.acpStore = acpStore;
|
|
1057
|
+
exports.addressNotZeroSchema = addressNotZeroSchema;
|
|
1058
|
+
exports.addressSchema = addressSchema;
|
|
1059
|
+
exports.bytesNotEmptySchema = bytesNotEmptySchema;
|
|
1060
|
+
exports.bytesSchema = bytesSchema;
|
|
1061
|
+
exports.clearStaleStore = clearStaleStore;
|
|
1062
|
+
exports.contractsSchema = contractsSchema;
|
|
1063
|
+
exports.getACP = getACP;
|
|
1064
|
+
exports.getACPs = getACPs;
|
|
1065
|
+
exports.getActiveACP = getActiveACP;
|
|
1066
|
+
exports.getActiveACPHash = getActiveACPHash;
|
|
1067
|
+
exports.getSignatureTypesAndMessage = getSignatureTypesAndMessage;
|
|
1068
|
+
exports.handlesSchema = handlesSchema;
|
|
1069
|
+
exports.removeACP = removeACP;
|
|
1070
|
+
exports.removeActiveACPHash = removeActiveACPHash;
|
|
1071
|
+
exports.resetStore = resetStore;
|
|
1072
|
+
exports.seal = seal;
|
|
1073
|
+
exports.setACP = setACP;
|
|
1074
|
+
exports.setActiveACPHash = setActiveACPHash;
|
|
1075
|
+
exports.unsealWithPrivateKey = unsealWithPrivateKey;
|
|
1076
|
+
exports.validateImportACP = validateImportACP;
|
|
1077
|
+
exports.validateImportACPOptions = validateImportACPOptions;
|
|
1078
|
+
exports.validateSelfACP = validateSelfACP;
|
|
1079
|
+
exports.validateSelfACPOptions = validateSelfACPOptions;
|
|
1080
|
+
exports.validateSharingACP = validateSharingACP;
|
|
1081
|
+
exports.validateSharingACPOptions = validateSharingACPOptions;
|