@cofhe/sdk 0.6.1 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +43 -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
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import { describe, it, expect, vi } from 'vitest';
|
|
2
|
+
import { classifySubmitResponse, throwIfSubmitRetryTimedOut } from '../decrypt/submitRetry.js';
|
|
3
|
+
import { CofheError, CofheErrorCode } from '../error.js';
|
|
4
|
+
|
|
5
|
+
const makeResponse = (opts: {
|
|
6
|
+
ok: boolean;
|
|
7
|
+
status: number;
|
|
8
|
+
statusText?: string;
|
|
9
|
+
json: () => Promise<unknown>;
|
|
10
|
+
}): Response => {
|
|
11
|
+
return {
|
|
12
|
+
ok: opts.ok,
|
|
13
|
+
status: opts.status,
|
|
14
|
+
statusText: opts.statusText ?? '',
|
|
15
|
+
json: opts.json,
|
|
16
|
+
} as unknown as Response;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
describe('classifySubmitResponse', () => {
|
|
20
|
+
it('classifies 204 as retryable without reading the body', async () => {
|
|
21
|
+
const json = vi.fn(async () => {
|
|
22
|
+
throw new Error('json() should not be called for 204');
|
|
23
|
+
});
|
|
24
|
+
const response = makeResponse({ ok: true, status: 204, json });
|
|
25
|
+
|
|
26
|
+
const result = await classifySubmitResponse({ response, fallbackErrorCode: CofheErrorCode.DecryptFailed });
|
|
27
|
+
|
|
28
|
+
expect(result).toEqual({ kind: 'retryable', status: 204 });
|
|
29
|
+
expect(json).not.toHaveBeenCalled();
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it('classifies 200 as parse-json', async () => {
|
|
33
|
+
const response = makeResponse({ ok: true, status: 200, json: async () => ({}) });
|
|
34
|
+
|
|
35
|
+
const result = await classifySubmitResponse({ response, fallbackErrorCode: CofheErrorCode.DecryptFailed });
|
|
36
|
+
|
|
37
|
+
expect(result).toEqual({ kind: 'parse-json' });
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it('classifies 404 with error=ct_not_found as retryable', async () => {
|
|
41
|
+
const response = makeResponse({
|
|
42
|
+
ok: false,
|
|
43
|
+
status: 404,
|
|
44
|
+
json: async () => ({ error: 'ct_not_found', error_message: 'ciphertext not indexed yet' }),
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
const result = await classifySubmitResponse({ response, fallbackErrorCode: CofheErrorCode.DecryptFailed });
|
|
48
|
+
|
|
49
|
+
expect(result).toEqual({ kind: 'retryable', status: 404, apiErrorMessage: 'ciphertext not indexed yet' });
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('classifies 404 with a different error code as retryable', async () => {
|
|
53
|
+
const response = makeResponse({
|
|
54
|
+
ok: false,
|
|
55
|
+
status: 404,
|
|
56
|
+
json: async () => ({ error: 'acp_denied', error_message: 'acp was rejected' }),
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
const result = await classifySubmitResponse({ response, fallbackErrorCode: CofheErrorCode.DecryptFailed });
|
|
60
|
+
|
|
61
|
+
expect(result).toEqual({
|
|
62
|
+
kind: 'retryable',
|
|
63
|
+
status: 404,
|
|
64
|
+
apiErrorMessage: 'acp was rejected',
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it('classifies 404 with an unparsable body as retryable', async () => {
|
|
69
|
+
const response = makeResponse({
|
|
70
|
+
ok: false,
|
|
71
|
+
status: 404,
|
|
72
|
+
statusText: 'Not Found',
|
|
73
|
+
json: async () => {
|
|
74
|
+
throw new Error('not json');
|
|
75
|
+
},
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
const result = await classifySubmitResponse({ response, fallbackErrorCode: CofheErrorCode.DecryptFailed });
|
|
79
|
+
|
|
80
|
+
expect(result).toEqual({
|
|
81
|
+
kind: 'retryable',
|
|
82
|
+
status: 404,
|
|
83
|
+
apiErrorMessage: 'Not Found',
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it('classifies other non-ok statuses as fatal with the mapped error code', async () => {
|
|
88
|
+
const response = makeResponse({
|
|
89
|
+
ok: false,
|
|
90
|
+
status: 500,
|
|
91
|
+
json: async () => ({ error: 'internal_error', error_message: 'decrypt task panicked' }),
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
const result = await classifySubmitResponse({ response, fallbackErrorCode: CofheErrorCode.SealOutputFailed });
|
|
95
|
+
|
|
96
|
+
expect(result).toEqual({
|
|
97
|
+
kind: 'fatal-http',
|
|
98
|
+
status: 500,
|
|
99
|
+
cofheErrorCode: CofheErrorCode.InternalError,
|
|
100
|
+
apiErrorCode: 'internal_error',
|
|
101
|
+
errorMessage: 'decrypt task panicked',
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
describe('throwIfSubmitRetryTimedOut', () => {
|
|
107
|
+
const baseParams = {
|
|
108
|
+
operationLabel: 'decrypt',
|
|
109
|
+
errorCode: CofheErrorCode.DecryptFailed,
|
|
110
|
+
thresholdNetworkUrl: 'http://threshold.local',
|
|
111
|
+
body: { some: 'body' },
|
|
112
|
+
attemptIndex: 3,
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
it('does not throw when under both budgets', () => {
|
|
116
|
+
expect(() =>
|
|
117
|
+
throwIfSubmitRetryTimedOut({
|
|
118
|
+
...baseParams,
|
|
119
|
+
status: 204,
|
|
120
|
+
elapsedMs: 1000,
|
|
121
|
+
retry404TimeoutMs: 10_000,
|
|
122
|
+
overallTimeoutMs: 300_000,
|
|
123
|
+
})
|
|
124
|
+
).not.toThrow();
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
it('throws CtNotFound with the last-known message once the 404 retry budget elapses', () => {
|
|
128
|
+
try {
|
|
129
|
+
throwIfSubmitRetryTimedOut({
|
|
130
|
+
...baseParams,
|
|
131
|
+
status: 404,
|
|
132
|
+
elapsedMs: 10_001,
|
|
133
|
+
retry404TimeoutMs: 10_000,
|
|
134
|
+
overallTimeoutMs: 300_000,
|
|
135
|
+
lastKnownErrorMessage: 'ciphertext not indexed yet',
|
|
136
|
+
});
|
|
137
|
+
expect.fail('expected throwIfSubmitRetryTimedOut to throw');
|
|
138
|
+
} catch (error) {
|
|
139
|
+
expect(error).toBeInstanceOf(CofheError);
|
|
140
|
+
const cofheError = error as CofheError;
|
|
141
|
+
expect(cofheError.code).toBe(CofheErrorCode.CtNotFound);
|
|
142
|
+
expect(cofheError.apiErrorCode).toBe('ct_not_found');
|
|
143
|
+
expect(cofheError.message).toBe(
|
|
144
|
+
'decrypt ciphertext not found after retrying for 10000ms: ciphertext not indexed yet'
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
it('omits the trailing message when no lastKnownErrorMessage is available', () => {
|
|
150
|
+
try {
|
|
151
|
+
throwIfSubmitRetryTimedOut({
|
|
152
|
+
...baseParams,
|
|
153
|
+
status: 404,
|
|
154
|
+
elapsedMs: 10_001,
|
|
155
|
+
retry404TimeoutMs: 10_000,
|
|
156
|
+
overallTimeoutMs: 300_000,
|
|
157
|
+
});
|
|
158
|
+
expect.fail('expected throwIfSubmitRetryTimedOut to throw');
|
|
159
|
+
} catch (error) {
|
|
160
|
+
const cofheError = error as CofheError;
|
|
161
|
+
expect(cofheError.message).toBe('decrypt ciphertext not found after retrying for 10000ms');
|
|
162
|
+
}
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
it('throws the generic operation error once the overall timeout elapses (204)', () => {
|
|
166
|
+
try {
|
|
167
|
+
throwIfSubmitRetryTimedOut({
|
|
168
|
+
...baseParams,
|
|
169
|
+
status: 204,
|
|
170
|
+
elapsedMs: 300_001,
|
|
171
|
+
retry404TimeoutMs: 10_000,
|
|
172
|
+
overallTimeoutMs: 300_000,
|
|
173
|
+
});
|
|
174
|
+
expect.fail('expected throwIfSubmitRetryTimedOut to throw');
|
|
175
|
+
} catch (error) {
|
|
176
|
+
const cofheError = error as CofheError;
|
|
177
|
+
expect(cofheError.code).toBe(CofheErrorCode.DecryptFailed);
|
|
178
|
+
expect(cofheError.apiErrorCode).toBeUndefined();
|
|
179
|
+
expect(cofheError.message).toBe('decrypt submit retried without receiving request_id for 300000ms');
|
|
180
|
+
}
|
|
181
|
+
});
|
|
182
|
+
});
|
package/core/types.ts
CHANGED
|
@@ -111,43 +111,6 @@ export type EncryptedNumber = {
|
|
|
111
111
|
securityZone: number;
|
|
112
112
|
};
|
|
113
113
|
|
|
114
|
-
export type EncryptedItemInput = {
|
|
115
|
-
ctHash: bigint;
|
|
116
|
-
securityZone: number;
|
|
117
|
-
utype: FheTypes;
|
|
118
|
-
signature: `0x${string}`;
|
|
119
|
-
};
|
|
120
|
-
|
|
121
|
-
export function assertCorrectEncryptedItemInput(input: EncryptedItemInput): asserts input is EncryptedItemInput {
|
|
122
|
-
if (!input.signature.startsWith('0x')) throw new Error('Signature must be a hex string starting with 0x');
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
export type EncryptedBoolInput = EncryptedItemInput & {
|
|
126
|
-
utype: FheTypes.Bool;
|
|
127
|
-
};
|
|
128
|
-
export type EncryptedUint8Input = EncryptedItemInput & {
|
|
129
|
-
utype: FheTypes.Uint8;
|
|
130
|
-
};
|
|
131
|
-
export type EncryptedUint16Input = EncryptedItemInput & {
|
|
132
|
-
utype: FheTypes.Uint16;
|
|
133
|
-
};
|
|
134
|
-
export type EncryptedUint32Input = EncryptedItemInput & {
|
|
135
|
-
utype: FheTypes.Uint32;
|
|
136
|
-
};
|
|
137
|
-
export type EncryptedUint64Input = EncryptedItemInput & {
|
|
138
|
-
utype: FheTypes.Uint64;
|
|
139
|
-
};
|
|
140
|
-
export type EncryptedUint128Input = EncryptedItemInput & {
|
|
141
|
-
utype: FheTypes.Uint128;
|
|
142
|
-
};
|
|
143
|
-
// [U256-DISABLED]
|
|
144
|
-
// export type EncryptedUint256Input = EncryptedItemInput & {
|
|
145
|
-
// utype: FheTypes.Uint256;
|
|
146
|
-
// };
|
|
147
|
-
export type EncryptedAddressInput = EncryptedItemInput & {
|
|
148
|
-
utype: FheTypes.Uint160;
|
|
149
|
-
};
|
|
150
|
-
|
|
151
114
|
export type EncryptableBase<U extends FheTypes, D> = {
|
|
152
115
|
data: D;
|
|
153
116
|
securityZone: number;
|
|
@@ -321,34 +284,6 @@ export type EncryptableItemByFheType<T extends FheTypes> = T extends FheTypes.Bo
|
|
|
321
284
|
? EncryptableAddress
|
|
322
285
|
: never;
|
|
323
286
|
|
|
324
|
-
// COFHE Encrypt
|
|
325
|
-
export type EncryptableToEncryptedItemInputMap<E extends EncryptableItem> = E extends EncryptableBool
|
|
326
|
-
? EncryptedBoolInput
|
|
327
|
-
: E extends EncryptableUint8
|
|
328
|
-
? EncryptedUint8Input
|
|
329
|
-
: E extends EncryptableUint16
|
|
330
|
-
? EncryptedUint16Input
|
|
331
|
-
: E extends EncryptableUint32
|
|
332
|
-
? EncryptedUint32Input
|
|
333
|
-
: E extends EncryptableUint64
|
|
334
|
-
? EncryptedUint64Input
|
|
335
|
-
: E extends EncryptableUint128
|
|
336
|
-
? EncryptedUint128Input
|
|
337
|
-
: // [U256-DISABLED]
|
|
338
|
-
// : E extends EncryptableUint256
|
|
339
|
-
// ? EncryptedUint256Input
|
|
340
|
-
E extends EncryptableAddress
|
|
341
|
-
? EncryptedAddressInput
|
|
342
|
-
: never;
|
|
343
|
-
|
|
344
|
-
export type EncryptedItemInputs<T> = T extends Primitive
|
|
345
|
-
? LiteralToPrimitive<T>
|
|
346
|
-
: T extends EncryptableItem
|
|
347
|
-
? EncryptableToEncryptedItemInputMap<T>
|
|
348
|
-
: {
|
|
349
|
-
[K in keyof T]: EncryptedItemInputs<T[K]>;
|
|
350
|
-
};
|
|
351
|
-
|
|
352
287
|
export function isEncryptableItem(value: unknown): value is EncryptableItem {
|
|
353
288
|
return (
|
|
354
289
|
// Is object and exists
|
|
@@ -399,7 +334,12 @@ export type ExternalUint64Hash = `0x${string}` & { readonly utype: FheTypes.Uint
|
|
|
399
334
|
export type ExternalUint128Hash = `0x${string}` & { readonly utype: FheTypes.Uint128 };
|
|
400
335
|
export type ExternalAddressHash = `0x${string}` & { readonly utype: FheTypes.Uint160 };
|
|
401
336
|
|
|
402
|
-
/**
|
|
337
|
+
/**
|
|
338
|
+
* Branded batch signature (Solidity: bytes memory signature).
|
|
339
|
+
* A single ECDSA signature (65-byte r||s||v, hex-encoded) authenticating an entire batch of
|
|
340
|
+
* ciphertexts at once — it covers keccak256(h_0 || h_1 || ... || h_n), where each h_i is the
|
|
341
|
+
* per-ciphertext message hash for the corresponding ExternalItemHashes entry, in order.
|
|
342
|
+
*/
|
|
403
343
|
export type ExternalHashProof = `0x${string}` & { readonly _kind: 'ExternalHashProof' };
|
|
404
344
|
|
|
405
345
|
/** Union of all External*Hash types — useful for utilities that operate on any hash without caring about the specific FHE type. */
|
|
@@ -414,7 +354,6 @@ export type AnyExternalHash =
|
|
|
414
354
|
|
|
415
355
|
/**
|
|
416
356
|
* Maps a single EncryptableItem to its corresponding External*Hash type.
|
|
417
|
-
* Mirrors EncryptableToEncryptedItemInputMap.
|
|
418
357
|
*/
|
|
419
358
|
export type EncryptableToExternalHashMap<E extends EncryptableItem> = E extends EncryptableBool
|
|
420
359
|
? ExternalBoolHash
|
|
@@ -441,8 +380,9 @@ export type ExternalItemHashes<T extends EncryptableItem[]> = {
|
|
|
441
380
|
};
|
|
442
381
|
|
|
443
382
|
/**
|
|
444
|
-
* Return type of EncryptInputsBuilder.execute()
|
|
445
|
-
* Tuple of per-input hashes in input order, followed by
|
|
383
|
+
* Return type of EncryptInputsBuilder.execute().
|
|
384
|
+
* Tuple of per-input hashes in input order, followed by the single batch signature that
|
|
385
|
+
* authenticates all of them together.
|
|
446
386
|
* e.g. [Encryptable.bool(true), Encryptable.uint32(5)] → [ExternalBoolHash, ExternalUint32Hash, ExternalHashProof]
|
|
447
387
|
*/
|
|
448
388
|
export type HashPlusProofResult<T extends EncryptableItem[]> = [...ExternalItemHashes<T>, ExternalHashProof];
|
|
@@ -0,0 +1,407 @@
|
|
|
1
|
+
import { Hex, PublicClient, WalletClient } from 'viem';
|
|
2
|
+
|
|
3
|
+
type EthEncryptedData = {
|
|
4
|
+
data: Uint8Array;
|
|
5
|
+
public_key: Uint8Array;
|
|
6
|
+
nonce: Uint8Array;
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* An X25519 sealing keypair, both halves 0x-prefixed 32-byte hex.
|
|
10
|
+
* The public key travels in the ACP (`sealingKey`); the private key never
|
|
11
|
+
* leaves the client (`sealingPrivateKey`).
|
|
12
|
+
*/
|
|
13
|
+
type SealingKeyPair = {
|
|
14
|
+
privateKey: Hex;
|
|
15
|
+
publicKey: Hex;
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* Generates a new sealing keypair. A sealing key is used to encrypt data such
|
|
19
|
+
* that it can only be unsealed (decrypted) by the owner of the corresponding
|
|
20
|
+
* private key.
|
|
21
|
+
*/
|
|
22
|
+
declare const GenerateSealingKey: () => SealingKeyPair;
|
|
23
|
+
/**
|
|
24
|
+
* Seals (encrypts) the provided message for a receiver with the specified public key.
|
|
25
|
+
*
|
|
26
|
+
* @param {bigint | number} value - The message to be encrypted.
|
|
27
|
+
* @param {string} publicKey - The public key of the intended recipient (with or without 0x prefix).
|
|
28
|
+
* @returns {EthEncryptedData} - The encrypted message.
|
|
29
|
+
* @throws Will throw if the provided publicKey or value do not meet defined preconditions.
|
|
30
|
+
*/
|
|
31
|
+
declare const seal: (value: bigint | number, publicKey: string) => EthEncryptedData;
|
|
32
|
+
/**
|
|
33
|
+
* Unseal (decrypt) data with a sealing private key (with or without 0x prefix).
|
|
34
|
+
* The ephemeral public key travels inside the payload, so the private key alone suffices.
|
|
35
|
+
*/
|
|
36
|
+
declare const unsealWithPrivateKey: (privateKey: string, parsedData: EthEncryptedData) => bigint;
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* EIP712 related types
|
|
40
|
+
*/
|
|
41
|
+
type EIP712Type = {
|
|
42
|
+
name: string;
|
|
43
|
+
type: string;
|
|
44
|
+
};
|
|
45
|
+
type EIP712Types = Record<string, EIP712Type[]>;
|
|
46
|
+
type EIP712Message = Record<string, string>;
|
|
47
|
+
type EIP712Domain = {
|
|
48
|
+
chainId: number;
|
|
49
|
+
name: string;
|
|
50
|
+
verifyingContract: Hex;
|
|
51
|
+
version: string;
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* The client-side-only component of an ACP — never leaves the client.
|
|
56
|
+
*/
|
|
57
|
+
interface ACPPrivate {
|
|
58
|
+
/**
|
|
59
|
+
* Stable hash of relevant acp data, used as key in storage
|
|
60
|
+
*/
|
|
61
|
+
hash: string;
|
|
62
|
+
/**
|
|
63
|
+
* Name for this acp, for organization and UI usage, not included in signature.
|
|
64
|
+
*/
|
|
65
|
+
name: string;
|
|
66
|
+
/**
|
|
67
|
+
* The type of the ACP (self / sharing / recipient)
|
|
68
|
+
* (self) ACP that will be signed and used by the issuer
|
|
69
|
+
* (sharing) ACP that is signed by the issuer, but intended to be shared with recipient
|
|
70
|
+
* (recipient) ACP that has been received, and signed by the recipient
|
|
71
|
+
*/
|
|
72
|
+
type: 'self' | 'sharing' | 'recipient';
|
|
73
|
+
/**
|
|
74
|
+
* The private half of the sealing keypair — used to unseal returned data.
|
|
75
|
+
* `sealingKey` (in the public component) is the corresponding public key.
|
|
76
|
+
* Never leaves the client.
|
|
77
|
+
*/
|
|
78
|
+
sealingPrivateKey: Hex;
|
|
79
|
+
/**
|
|
80
|
+
* EIP712 domain used to sign this acp.
|
|
81
|
+
* Should not be set manually, included in metadata as part of serialization flows.
|
|
82
|
+
*/
|
|
83
|
+
_signedDomain?: EIP712Domain;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* The public component of an ACP — the signed struct passed on-chain / to the
|
|
87
|
+
* decryption backend to grant encrypted data access. Produced by `getPublic()`.
|
|
88
|
+
*/
|
|
89
|
+
interface ACPPublic {
|
|
90
|
+
/**
|
|
91
|
+
* (base) User that initially created the permission, target of data fetching
|
|
92
|
+
*/
|
|
93
|
+
issuer: Hex;
|
|
94
|
+
/**
|
|
95
|
+
* (base) Expiration timestamp
|
|
96
|
+
*/
|
|
97
|
+
expiration: number;
|
|
98
|
+
/**
|
|
99
|
+
* (sharing) The user that this permission will be shared with
|
|
100
|
+
* ** optional, use `address(0)` to disable **
|
|
101
|
+
*/
|
|
102
|
+
recipient: Hex;
|
|
103
|
+
/**
|
|
104
|
+
* (revocation) Opaque data interpreted by `revokerContract` — the default
|
|
105
|
+
* revoker interprets it as the acp's creation timestamp.
|
|
106
|
+
* ** optional, use `0` to disable **
|
|
107
|
+
*/
|
|
108
|
+
revokerData: number;
|
|
109
|
+
/**
|
|
110
|
+
* (revocation) The contract to query to determine permission validity
|
|
111
|
+
* ** optional, use `address(0)` to disable **
|
|
112
|
+
*/
|
|
113
|
+
revokerContract: Hex;
|
|
114
|
+
/**
|
|
115
|
+
* (scope) Scope discriminator — exactly one scope mode per ACP (see ACPScope).
|
|
116
|
+
* Global: all of `issuer`s values (arrays empty). Contract: values readable by
|
|
117
|
+
* `contracts`. Handles: the listed `handles` only.
|
|
118
|
+
*/
|
|
119
|
+
scope: number;
|
|
120
|
+
/**
|
|
121
|
+
* (scope) Grants access to `issuer`s values readable by any of these contracts.
|
|
122
|
+
* Checked on-chain as an intersection with the ACL's persisted allowances.
|
|
123
|
+
*/
|
|
124
|
+
contracts: Hex[];
|
|
125
|
+
/**
|
|
126
|
+
* (scope) Grants access to these specific ciphertext handles (bytes32 hex)
|
|
127
|
+
*/
|
|
128
|
+
handles: Hex[];
|
|
129
|
+
/**
|
|
130
|
+
* (base) The public half of the sealing keypair — used to re-encrypt `issuer`s confidential data
|
|
131
|
+
* (non-sharing) Populated by `issuer`
|
|
132
|
+
* (sharing) Populated by `recipient`
|
|
133
|
+
*/
|
|
134
|
+
sealingKey: Hex;
|
|
135
|
+
/**
|
|
136
|
+
* (base) `signTypedData` signature created by `issuer`.
|
|
137
|
+
* (base) Shared- and Self- permissions differ in signature format: (`sealingKey` absent in shared signature)
|
|
138
|
+
*/
|
|
139
|
+
issuerSignature: Hex;
|
|
140
|
+
/**
|
|
141
|
+
* (sharing) `signTypedData` signature created by `recipient`
|
|
142
|
+
* ** required for shared acps **
|
|
143
|
+
*/
|
|
144
|
+
recipientSignature: Hex;
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Core ACP (Access Control Permission) type — immutable design for React compatibility.
|
|
148
|
+
* The union of the private (client-only) and public (signed) components.
|
|
149
|
+
*/
|
|
150
|
+
type ACP = Expand<ACPPrivate & ACPPublic>;
|
|
151
|
+
/**
|
|
152
|
+
* ACP discriminant helpers
|
|
153
|
+
*/
|
|
154
|
+
type ACPType = ACP['type'];
|
|
155
|
+
/**
|
|
156
|
+
* Utility type to narrow an ACP to a specific discriminant.
|
|
157
|
+
*
|
|
158
|
+
* Note: this only narrows the `type` field. Runtime/validation constraints
|
|
159
|
+
* (e.g. recipient == zeroAddress for self acps) are enforced elsewhere.
|
|
160
|
+
*/
|
|
161
|
+
type ACPOf<T extends ACPType> = Expand<Omit<ACP, 'type'> & {
|
|
162
|
+
type: T;
|
|
163
|
+
}>;
|
|
164
|
+
type SelfACP = ACPOf<'self'>;
|
|
165
|
+
type SharingACP = ACPOf<'sharing'>;
|
|
166
|
+
type RecipientACP = ACPOf<'recipient'>;
|
|
167
|
+
/**
|
|
168
|
+
* Optional additional metadata of an ACP
|
|
169
|
+
* Can be passed into the constructor, but not necessary
|
|
170
|
+
* Useful for deserialization
|
|
171
|
+
*/
|
|
172
|
+
interface ACPMetadata {
|
|
173
|
+
/**
|
|
174
|
+
* EIP712 domain used to sign this acp.
|
|
175
|
+
* Should not be set manually, included in metadata as part of serialization flows.
|
|
176
|
+
*/
|
|
177
|
+
_signedDomain?: EIP712Domain;
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Utility types for acp creation
|
|
181
|
+
*/
|
|
182
|
+
/**
|
|
183
|
+
* Scope fields shared by all acp creation options.
|
|
184
|
+
* When any scope array is populated and `global` is not explicitly set,
|
|
185
|
+
* `global` defaults to false (narrowest matching scope); with no scope
|
|
186
|
+
* arrays it defaults to true (V2 behavior).
|
|
187
|
+
*/
|
|
188
|
+
declare const ACPScope: {
|
|
189
|
+
readonly Global: 0;
|
|
190
|
+
readonly Contract: 1;
|
|
191
|
+
readonly Handles: 2;
|
|
192
|
+
};
|
|
193
|
+
type ACPScopeOptions = {
|
|
194
|
+
scope?: number;
|
|
195
|
+
contracts?: Hex[];
|
|
196
|
+
handles?: Hex[];
|
|
197
|
+
};
|
|
198
|
+
type CreateSelfACPOptions = ACPScopeOptions & {
|
|
199
|
+
type?: 'self';
|
|
200
|
+
issuer: string;
|
|
201
|
+
name?: string;
|
|
202
|
+
expiration?: number;
|
|
203
|
+
revokerData?: number;
|
|
204
|
+
revokerContract?: string;
|
|
205
|
+
};
|
|
206
|
+
type CreateSharingACPOptions = ACPScopeOptions & {
|
|
207
|
+
type?: 'sharing';
|
|
208
|
+
issuer: string;
|
|
209
|
+
recipient: string;
|
|
210
|
+
name?: string;
|
|
211
|
+
expiration?: number;
|
|
212
|
+
revokerData?: number;
|
|
213
|
+
revokerContract?: string;
|
|
214
|
+
};
|
|
215
|
+
type ImportSharedACPOptions = ACPScopeOptions & {
|
|
216
|
+
type?: 'sharing';
|
|
217
|
+
issuer: string;
|
|
218
|
+
recipient: string;
|
|
219
|
+
issuerSignature: string;
|
|
220
|
+
name?: string;
|
|
221
|
+
expiration: number;
|
|
222
|
+
revokerData?: number;
|
|
223
|
+
revokerContract?: string;
|
|
224
|
+
};
|
|
225
|
+
/** An ACP is plain JSON-serializable data — the serialized form is the ACP itself. */
|
|
226
|
+
type SerializedACP = ACP;
|
|
227
|
+
/**
|
|
228
|
+
* The share payload produced by `ACPUtils.export()` — the full public component
|
|
229
|
+
* with `sealingKey`/`recipientSignature` left for the recipient to fill, plus
|
|
230
|
+
* the display name and acp type. Fixed shape: every field is always present
|
|
231
|
+
* (zero-values instead of omissions), so importers can parse a single schema.
|
|
232
|
+
* Mirrors the on-chain sharing payload struct field-for-field.
|
|
233
|
+
*/
|
|
234
|
+
type SharedACP = Expand<Omit<ACPPublic, 'sealingKey' | 'recipientSignature'> & {
|
|
235
|
+
name: string;
|
|
236
|
+
type: 'sharing';
|
|
237
|
+
}>;
|
|
238
|
+
/**
|
|
239
|
+
* A share read back from the on-chain ACPShareRegistry: the posted payload
|
|
240
|
+
* (SharedACP minus the client-side name/type) plus its registry id.
|
|
241
|
+
*/
|
|
242
|
+
type IncomingShare = Expand<Omit<SharedACP, 'name' | 'type'> & {
|
|
243
|
+
shareId: Hex;
|
|
244
|
+
}>;
|
|
245
|
+
/**
|
|
246
|
+
* A type representing the acp fields that are used to generate the hash
|
|
247
|
+
*/
|
|
248
|
+
type ACPHashFields = Pick<ACP, 'type' | 'issuer' | 'expiration' | 'recipient' | 'revokerData' | 'revokerContract' | 'scope' | 'contracts' | 'handles'>;
|
|
249
|
+
/**
|
|
250
|
+
* Validation result type
|
|
251
|
+
*/
|
|
252
|
+
interface ValidationResult {
|
|
253
|
+
valid: boolean;
|
|
254
|
+
error: 'invalid-schema' | 'expired' | 'not-signed' | null;
|
|
255
|
+
}
|
|
256
|
+
/**
|
|
257
|
+
* Signature types for EIP712 signing
|
|
258
|
+
*/
|
|
259
|
+
type ACPSignaturePrimaryType = 'ACPIssuerSelf' | 'ACPIssuerShared' | 'ACPRecipient';
|
|
260
|
+
type Expand<T> = T extends infer O ? {
|
|
261
|
+
[K in keyof O]: O[K];
|
|
262
|
+
} : never;
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Main ACP utilities - functional approach for React compatibility
|
|
266
|
+
*/
|
|
267
|
+
declare const ACPUtils: {
|
|
268
|
+
/**
|
|
269
|
+
* Create a self acp for personal use
|
|
270
|
+
*/
|
|
271
|
+
createSelf: (options: CreateSelfACPOptions) => SelfACP;
|
|
272
|
+
/**
|
|
273
|
+
* Create a sharing acp to be shared with another user
|
|
274
|
+
*/
|
|
275
|
+
createSharing: (options: CreateSharingACPOptions) => SharingACP;
|
|
276
|
+
/**
|
|
277
|
+
* Import a shared acp from various input formats
|
|
278
|
+
*/
|
|
279
|
+
importShared: (options: ImportSharedACPOptions | string) => RecipientACP;
|
|
280
|
+
/**
|
|
281
|
+
* Sign an ACP with the provided wallet client
|
|
282
|
+
*/
|
|
283
|
+
sign: <T extends ACP>(acp: T, publicClient: PublicClient, walletClient: WalletClient) => Promise<T>;
|
|
284
|
+
/**
|
|
285
|
+
* Create and sign a self acp in one operation
|
|
286
|
+
*/
|
|
287
|
+
createSelfAndSign: (options: CreateSelfACPOptions, publicClient: PublicClient, walletClient: WalletClient) => Promise<SelfACP>;
|
|
288
|
+
/**
|
|
289
|
+
* Create and sign a sharing acp in one operation
|
|
290
|
+
*/
|
|
291
|
+
createSharingAndSign: (options: CreateSharingACPOptions, publicClient: PublicClient, walletClient: WalletClient) => Promise<SharingACP>;
|
|
292
|
+
/**
|
|
293
|
+
* Import and sign a shared acp in one operation from various input formats
|
|
294
|
+
*/
|
|
295
|
+
importSharedAndSign: (options: ImportSharedACPOptions | string, publicClient: PublicClient, walletClient: WalletClient) => Promise<RecipientACP>;
|
|
296
|
+
/**
|
|
297
|
+
* Deserialize an ACP from serialized data
|
|
298
|
+
*/
|
|
299
|
+
deserialize: (data: SerializedACP) => ACP;
|
|
300
|
+
/**
|
|
301
|
+
* Serialize an ACP for storage
|
|
302
|
+
*/
|
|
303
|
+
serialize: (acp: ACP) => SerializedACP;
|
|
304
|
+
/**
|
|
305
|
+
* Validate an ACP (schema-level validation)
|
|
306
|
+
*/
|
|
307
|
+
validateSchema: (acp: ACP) => {
|
|
308
|
+
name: string;
|
|
309
|
+
type: "self" | "sharing" | "recipient";
|
|
310
|
+
issuer: `0x${string}`;
|
|
311
|
+
expiration: number;
|
|
312
|
+
recipient: `0x${string}`;
|
|
313
|
+
revokerData: number;
|
|
314
|
+
revokerContract: `0x${string}`;
|
|
315
|
+
scope: number;
|
|
316
|
+
contracts: `0x${string}`[];
|
|
317
|
+
handles: `0x${string}`[];
|
|
318
|
+
issuerSignature: `0x${string}`;
|
|
319
|
+
recipientSignature: `0x${string}`;
|
|
320
|
+
sealingPrivateKey?: string | undefined;
|
|
321
|
+
sealingKey?: string | undefined;
|
|
322
|
+
};
|
|
323
|
+
/**
|
|
324
|
+
* Validate an ACP (holistic validation).
|
|
325
|
+
*
|
|
326
|
+
* This validates:
|
|
327
|
+
* - ACP schema (shape + invariants)
|
|
328
|
+
* - ACP is signed
|
|
329
|
+
* - ACP is not expired
|
|
330
|
+
*
|
|
331
|
+
* For schema-only validation, use `validateSchema(acp)`.
|
|
332
|
+
*/
|
|
333
|
+
validate: (acp: ACP) => {
|
|
334
|
+
name: string;
|
|
335
|
+
type: "self" | "sharing" | "recipient";
|
|
336
|
+
issuer: `0x${string}`;
|
|
337
|
+
expiration: number;
|
|
338
|
+
recipient: `0x${string}`;
|
|
339
|
+
revokerData: number;
|
|
340
|
+
revokerContract: `0x${string}`;
|
|
341
|
+
scope: number;
|
|
342
|
+
contracts: `0x${string}`[];
|
|
343
|
+
handles: `0x${string}`[];
|
|
344
|
+
issuerSignature: `0x${string}`;
|
|
345
|
+
recipientSignature: `0x${string}`;
|
|
346
|
+
sealingPrivateKey?: string | undefined;
|
|
347
|
+
sealingKey?: string | undefined;
|
|
348
|
+
};
|
|
349
|
+
/**
|
|
350
|
+
* Get the public component of an ACP — the signed struct sent on-chain / to the decryption backend.
|
|
351
|
+
* Strips the private component (hash, name, type, sealing pair).
|
|
352
|
+
*/
|
|
353
|
+
getPublic: (acp: ACP, skipValidation?: boolean) => ACPPublic;
|
|
354
|
+
/**
|
|
355
|
+
* Get a stable hash for the acp (used as key in storage)
|
|
356
|
+
*/
|
|
357
|
+
getHash: (acp: ACPHashFields) => string;
|
|
358
|
+
/**
|
|
359
|
+
* Export acp data for sharing (strips the private component).
|
|
360
|
+
* Fixed `SharedACP` shape — every field always present, aligned with
|
|
361
|
+
* `ACPPublic` and the on-chain sharing payload.
|
|
362
|
+
*/
|
|
363
|
+
export: (acp: ACP) => string;
|
|
364
|
+
/**
|
|
365
|
+
* Unseal encrypted data using the acp's sealing key
|
|
366
|
+
*/
|
|
367
|
+
unseal: (acp: ACP, ciphertext: EthEncryptedData) => bigint;
|
|
368
|
+
/**
|
|
369
|
+
* Check if acp is expired
|
|
370
|
+
*/
|
|
371
|
+
isExpired: (acp: ACP) => boolean;
|
|
372
|
+
/**
|
|
373
|
+
* Check if acp is signed
|
|
374
|
+
*/
|
|
375
|
+
isSigned: (acp: ACP) => boolean;
|
|
376
|
+
/**
|
|
377
|
+
* Check if acp is signed and not expired
|
|
378
|
+
*/
|
|
379
|
+
isSignedAndNotExpired: (acp: ACP) => ValidationResult;
|
|
380
|
+
/**
|
|
381
|
+
* Assert that acp is signed and not expired
|
|
382
|
+
*/
|
|
383
|
+
assertSignedAndNotExpired: (acp: ACP) => void;
|
|
384
|
+
isValid: (acp: ACP) => ValidationResult;
|
|
385
|
+
/**
|
|
386
|
+
* Update acp name (returns new acp instance)
|
|
387
|
+
*/
|
|
388
|
+
updateName: (acp: ACP, name: string) => ACP;
|
|
389
|
+
/**
|
|
390
|
+
* Fetch EIP712 domain from the blockchain
|
|
391
|
+
*/
|
|
392
|
+
fetchEIP712Domain: (publicClient: PublicClient) => Promise<EIP712Domain>;
|
|
393
|
+
/**
|
|
394
|
+
* Check if acp's signed domain matches the provided domain
|
|
395
|
+
*/
|
|
396
|
+
matchesDomain: (acp: ACP, domain: EIP712Domain) => boolean;
|
|
397
|
+
/**
|
|
398
|
+
* Check if acp's signed domain is valid for the current chain
|
|
399
|
+
*/
|
|
400
|
+
checkSignedDomainValid: (acp: ACP, publicClient: PublicClient) => Promise<boolean>;
|
|
401
|
+
/**
|
|
402
|
+
* Check if acp passes the on-chain validation
|
|
403
|
+
*/
|
|
404
|
+
checkValidityOnChain: (acp: ACP, publicClient: PublicClient) => Promise<boolean>;
|
|
405
|
+
};
|
|
406
|
+
|
|
407
|
+
export { type ACP as A, type CreateSelfACPOptions as C, type EIP712Types as E, GenerateSealingKey as G, type ImportSharedACPOptions as I, type RecipientACP as R, type SerializedACP as S, type ValidationResult as V, type ACPPublic as a, type ACPSignaturePrimaryType as b, type EIP712Message as c, type CreateSharingACPOptions as d, type ACPMetadata as e, type SharedACP as f, type IncomingShare as g, type EIP712Domain as h, ACPUtils as i, type SealingKeyPair as j, type EthEncryptedData as k, type EIP712Type as l, type ACPPrivate as m, type ACPType as n, type ACPOf as o, type SelfACP as p, type SharingACP as q, ACPScope as r, seal as s, type ACPScopeOptions as t, unsealWithPrivateKey as u, type ACPHashFields as v, type Expand as w };
|