@invisi-labs/primitives 0.0.0-stage → 1.2.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/dist/index.cjs ADDED
@@ -0,0 +1,2796 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod3) => __copyProps(__defProp({}, "__esModule", { value: true }), mod3);
19
+
20
+ // src/index.ts
21
+ var src_exports = {};
22
+ __export(src_exports, {
23
+ AuthorizedStatement: () => AuthorizedStatement,
24
+ CREDIT_POLICY_FIELDS: () => CREDIT_POLICY_FIELDS,
25
+ CREDIT_USAGE_FIELDS: () => CREDIT_USAGE_FIELDS,
26
+ D_CNULL: () => D_CNULL,
27
+ D_CREDIT: () => D_CREDIT,
28
+ D_CREDIT_KEY_HASH: () => D_CREDIT_KEY_HASH,
29
+ D_CREDIT_RECOVERY: () => D_CREDIT_RECOVERY,
30
+ D_CREDIT_STATE_NONCE: () => D_CREDIT_STATE_NONCE,
31
+ D_IDENT: () => D_IDENT,
32
+ D_NOTE: () => D_NOTE,
33
+ D_NULL: () => D_NULL,
34
+ DeliveryAuthenticationError: () => DeliveryAuthenticationError,
35
+ DeliveryDecryptionError: () => DeliveryDecryptionError,
36
+ Detector: () => Detector,
37
+ Disclosure: () => Disclosure,
38
+ FIELD_MODULUS: () => FIELD_MODULUS,
39
+ MAX_CIPHERTEXT_BYTES: () => MAX_CIPHERTEXT_BYTES,
40
+ MAX_MEMO_BYTES: () => MAX_MEMO_BYTES,
41
+ MAX_WALLET_BATCH_BYTES: () => MAX_WALLET_BATCH_BYTES,
42
+ MissingCapabilityError: () => MissingCapabilityError,
43
+ NativeWalletError: () => NativeWalletError,
44
+ NullifierCapability: () => NullifierCapability,
45
+ ReceivingAddress: () => ReceivingAddress,
46
+ TREE_DEPTH: () => TREE_DEPTH,
47
+ ViewingCapability: () => ViewingCapability,
48
+ WALLET_AUTHORIZATION_TYPES: () => WALLET_AUTHORIZATION_TYPES,
49
+ Wallet: () => Wallet,
50
+ acceptWalletResponse: () => acceptWalletResponse,
51
+ acceptWalletResponses: () => acceptWalletResponses,
52
+ applyCreditDrawUsage: () => applyCreditDrawUsage,
53
+ authenticatedDecryptor: () => authenticatedDecryptor,
54
+ computeCreditCommitment: () => computeCreditCommitment,
55
+ computeCreditNullifier: () => computeCreditNullifier,
56
+ computeCreditRecoveryPad: () => computeCreditRecoveryPad,
57
+ computeCreditSharedKeyHash: () => computeCreditSharedKeyHash,
58
+ computeNoteCommitment: () => computeNoteCommitment,
59
+ computeNoteIdentity: () => computeNoteIdentity,
60
+ computeNoteNullifier: () => computeNoteNullifier,
61
+ computeOwnerAddress: () => computeOwnerAddress,
62
+ computeWalletAuthorizationDigest: () => computeWalletAuthorizationDigest,
63
+ createClue: () => createClue,
64
+ createClueFromSeed: () => createClueFromSeed,
65
+ createSigningRequest: () => createSigningRequest,
66
+ creditPeriods: () => creditPeriods,
67
+ creditV1: () => creditV1,
68
+ decodeAddress: () => decodeAddress,
69
+ decodeDeliveryHeader: () => decodeDeliveryHeader,
70
+ decodeMemoText: () => decodeMemoText,
71
+ decryptCreditState: () => decryptCreditState,
72
+ decryptPrivateTransferNote: () => decryptPrivateTransferNote,
73
+ deliveryCodec: () => deliveryCodec,
74
+ deliveryHeader: () => deliveryHeader,
75
+ deriveClueKey: () => deriveClueKey,
76
+ deriveCreditState: () => deriveCreditState,
77
+ deriveCreditStateNonce: () => deriveCreditStateNonce,
78
+ deriveCreditUpdate: () => deriveCreditUpdate,
79
+ encodeMemo: () => encodeMemo,
80
+ encryptCreditState: () => encryptCreditState,
81
+ encryptPrivateTransferNote: () => encryptPrivateTransferNote,
82
+ fundsV1: () => fundsV1,
83
+ generateCreditSharedKey: () => generateCreditSharedKey,
84
+ generateNonce: () => generateNonce,
85
+ normalizeCreditDeliveryState: () => normalizeCreditDeliveryState,
86
+ normalizeCreditPolicy: () => normalizeCreditPolicy,
87
+ normalizeCreditState: () => normalizeCreditState,
88
+ normalizeCreditUsage: () => normalizeCreditUsage,
89
+ poseidonHash: () => poseidonHash,
90
+ prepareWalletRequest: () => prepareWalletRequest,
91
+ provenanceIntentHash: () => provenanceIntentHash,
92
+ recoverCreditState: () => recoverCreditState,
93
+ validateClueKey: () => validateClueKey,
94
+ validateDeliveryCiphertext: () => validateDeliveryCiphertext,
95
+ validateWalletBatch: () => validateWalletBatch,
96
+ validateWalletResponses: () => validateWalletResponses,
97
+ verifyWalletAuthorization: () => verifyWalletAuthorization,
98
+ viewingPublicKey: () => viewingPublicKey,
99
+ walletAuthorizationTypedData: () => walletAuthorizationTypedData
100
+ });
101
+ module.exports = __toCommonJS(src_exports);
102
+
103
+ // src/address/address.ts
104
+ var import_secp256k1 = require("@noble/curves/secp256k1");
105
+ var import_sha3 = require("@noble/hashes/sha3");
106
+ var import_utils2 = require("@noble/hashes/utils");
107
+ var import_ethers = require("ethers");
108
+
109
+ // src/fmd/fmd.ts
110
+ var import_blake2b2 = require("@noble/hashes/blake2b");
111
+ var import_utils = require("@noble/hashes/utils");
112
+
113
+ // src/fmd/decaf377.ts
114
+ var import_edwards = require("@noble/curves/abstract/edwards");
115
+ var import_modular = require("@noble/curves/abstract/modular");
116
+ var import_blake2b = require("@noble/hashes/blake2b");
117
+ var FQ = 8444461749428370424248824938781546531375899335154063827935233455917409239041n;
118
+ var FR = 2111115437357092606062206234695386632838870926408408195193685246394721360383n;
119
+ var field = (0, import_modular.Field)(FQ, 253, true);
120
+ var f = (n) => (0, import_modular.mod)(n, FQ);
121
+ var abs = (n) => (n & 1n) === 0n ? n : f(-n);
122
+ function secureRandomBytes(length) {
123
+ const webCrypto = globalThis.crypto;
124
+ if (!webCrypto?.getRandomValues) throw new Error("WebCrypto secure randomness is unavailable");
125
+ return webCrypto.getRandomValues(new Uint8Array(length));
126
+ }
127
+ var curve = (0, import_edwards.twistedEdwards)({
128
+ a: FQ - 1n,
129
+ d: 3021n,
130
+ Fp: field,
131
+ n: FR,
132
+ h: 4n,
133
+ Gx: 4959445789346820725352484487855828915252512307947624787834978378872129235627n,
134
+ Gy: 6060471950081851567114691557659790004756535011754163002297540472747064943288n,
135
+ hash: import_blake2b.blake2b,
136
+ randomBytes: (length = 32) => secureRandomBytes(length)
137
+ });
138
+ var generator = curve.ExtendedPoint.BASE;
139
+ function readLE(bytes3) {
140
+ let n = 0n;
141
+ for (let i = bytes3.length - 1; i >= 0; i--) n = n << 8n | BigInt(bytes3[i]);
142
+ return n;
143
+ }
144
+ function writeLE(n) {
145
+ const bytes3 = new Uint8Array(32);
146
+ for (let i = 0; i < 32; i++) {
147
+ bytes3[i] = Number(n & 255n);
148
+ n >>= 8n;
149
+ }
150
+ return bytes3;
151
+ }
152
+ function multiply(point, scalar) {
153
+ return scalar === 0n ? curve.ExtendedPoint.ZERO : point.multiply(scalar);
154
+ }
155
+ function inverseScalar(scalar) {
156
+ return (0, import_modular.Field)(FR).inv(scalar);
157
+ }
158
+ function decodePoint(bytes3) {
159
+ if (!(bytes3 instanceof Uint8Array) || bytes3.length !== 32)
160
+ throw new Error("Invalid Decaf377 encoding length");
161
+ const s = readLE(bytes3);
162
+ if (s >= FQ || (s & 1n) !== 0n || bytes3[31] >> 5 !== 0)
163
+ throw new Error("Invalid Decaf377 encoding");
164
+ const ss = f(s * s);
165
+ const u1 = f(1n - ss);
166
+ const u2 = f(u1 * u1 - 4n * 3021n * ss);
167
+ const denominator = f(u2 * u1 * u1);
168
+ if (denominator === 0n) throw new Error("Invalid Decaf377 encoding");
169
+ let v;
170
+ try {
171
+ v = field.sqrt(field.inv(denominator));
172
+ } catch {
173
+ throw new Error("Invalid Decaf377 encoding");
174
+ }
175
+ const twoSU1 = f(2n * s * u1);
176
+ if ((f(twoSU1 * v) & 1n) !== 0n) v = f(-v);
177
+ const x = f(twoSU1 * v * v * u2);
178
+ const y = f((1n + ss) * v * u1);
179
+ return curve.ExtendedPoint.fromAffine({ x, y });
180
+ }
181
+ function encodePoint(point) {
182
+ const { ex: x, ez: z, et: t } = point;
183
+ if (x === 0n) return new Uint8Array(32);
184
+ const aMinusD = -3022n;
185
+ const u1 = f((x + t) * (x - t));
186
+ const denominator = f(u1 * aMinusD * x * x);
187
+ const v = field.sqrt(field.inv(denominator));
188
+ const u2 = abs(f(v * u1));
189
+ const u3 = f(u2 * z - t);
190
+ return writeLE(abs(f(aMinusD * v * u3 * x)));
191
+ }
192
+ function isIdentity(point) {
193
+ return point.ex === 0n;
194
+ }
195
+
196
+ // src/fmd/fmd.ts
197
+ var PRECISION = 24;
198
+ function requireBytes(value, length, name) {
199
+ if (!(value instanceof Uint8Array) || value.length !== length)
200
+ throw new TypeError(`${name} must be a ${length}-byte Uint8Array`);
201
+ }
202
+ function detectionScalar(key) {
203
+ requireBytes(key, 32, "Detection key");
204
+ const scalar = readLE(key);
205
+ if (scalar === 0n || scalar >= FR)
206
+ throw new Error("Detection key must be a canonical nonzero Decaf377 scalar");
207
+ return scalar;
208
+ }
209
+ function hash(domain, ...parts) {
210
+ const personalization = new Uint8Array(16);
211
+ personalization.set((0, import_utils.utf8ToBytes)(`decaf377-fmd.${domain}`));
212
+ return (0, import_blake2b2.blake2b)((0, import_utils.concatBytes)(...parts), { dkLen: 64, personalization });
213
+ }
214
+ var hashScalar = (domain, ...parts) => readLE(hash(domain, ...parts)) % FR;
215
+ var childOffset = (clueKey, index) => hashScalar("hkd", clueKey, Uint8Array.of(index));
216
+ var hashBit = (p, shared, q) => hash("bit", p, shared, q)[0] & 1;
217
+ function deriveClueKey(detectionKey) {
218
+ return encodePoint(multiply(generator, detectionScalar(detectionKey)));
219
+ }
220
+ function validateClueKey(clueKey) {
221
+ requireBytes(clueKey, 32, "Clue key");
222
+ if (isIdentity(decodePoint(clueKey))) throw new Error("Clue key must not be the identity");
223
+ }
224
+ function createClue(clueKey) {
225
+ validateClueKey(clueKey);
226
+ return createClueFromSeed(clueKey, secureRandomBytes(32));
227
+ }
228
+ function createClueFromSeed(clueKey, seed32) {
229
+ validateClueKey(clueKey);
230
+ requireBytes(seed32, 32, "Clue seed");
231
+ const root = decodePoint(clueKey);
232
+ const r = hashScalar("rdv", clueKey, seed32);
233
+ const z = hashScalar("zdv", clueKey, seed32);
234
+ if (r === 0n) throw new Error("Degenerate clue seed");
235
+ const p = encodePoint(multiply(generator, r));
236
+ const q = encodePoint(multiply(generator, z));
237
+ const ciphertext = new Uint8Array(3);
238
+ for (let i = 0; i < PRECISION; i++) {
239
+ const child = root.add(multiply(generator, childOffset(clueKey, i)));
240
+ const shared = encodePoint(multiply(child, r));
241
+ ciphertext[i >> 3] = ciphertext[i >> 3] | (hashBit(p, shared, q) ^ 1) << (i & 7);
242
+ }
243
+ const m = hashScalar("sca", p, Uint8Array.of(PRECISION), ciphertext);
244
+ const y = (z - m + FR) * inverseScalar(r) % FR;
245
+ if (y === 0n) throw new Error("Degenerate clue seed");
246
+ return (0, import_utils.concatBytes)(p, writeLE(y), Uint8Array.of(PRECISION), ciphertext);
247
+ }
248
+
249
+ // src/address/address.ts
250
+ var ALPHABET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l";
251
+ function identityBytes(value) {
252
+ if (typeof value !== "bigint" && (typeof value !== "string" || !/^0x[0-9a-fA-F]{40}$/.test(value)))
253
+ throw new Error("invalid owner address encoding");
254
+ const n = BigInt(value);
255
+ if (n <= 0n || n >= 1n << 160n)
256
+ throw new RangeError("owner address must be a nonzero EVM owner address");
257
+ return (0, import_utils2.hexToBytes)(n.toString(16).padStart(40, "0"));
258
+ }
259
+ function receivingKeys(keys) {
260
+ const publicKey2 = keys.viewingPublicKey;
261
+ if (!(publicKey2 instanceof Uint8Array) || publicKey2.length !== 33 || publicKey2[0] !== 2 && publicKey2[0] !== 3)
262
+ throw new Error("invalid compressed viewing public key");
263
+ import_secp256k1.secp256k1.ProjectivePoint.fromHex(publicKey2).assertValidity();
264
+ validateClueKey(keys.clueKey);
265
+ return { viewingPublicKey: Uint8Array.from(publicKey2), clueKey: Uint8Array.from(keys.clueKey) };
266
+ }
267
+ function checkedText(prefix, data) {
268
+ const digest = (0, import_sha3.keccak_256)((0, import_utils2.utf8ToBytes)("Invisi.AddressCaseChecksum.v1:" + prefix + data));
269
+ return prefix + Array.from(
270
+ data,
271
+ (character, i) => digest[i >> 3] >> 7 - (i & 7) & 1 ? character.toUpperCase() : character
272
+ ).join("");
273
+ }
274
+ function encode(prefix, payload) {
275
+ let buffer2 = 0, bits = 0, data = "";
276
+ for (const byte of payload) {
277
+ buffer2 = buffer2 << 8 | byte;
278
+ bits += 8;
279
+ while (bits >= 5) {
280
+ bits -= 5;
281
+ data += ALPHABET[buffer2 >>> bits & 31];
282
+ }
283
+ buffer2 &= (1 << bits) - 1;
284
+ }
285
+ if (bits) data += ALPHABET[buffer2 << 5 - bits & 31];
286
+ return checkedText(prefix, data);
287
+ }
288
+ function encodeLongAddress(recipient) {
289
+ const identity2 = identityBytes(recipient.ownerAddress), keys = receivingKeys(recipient);
290
+ return encode("0i", (0, import_utils2.concatBytes)(identity2, keys.viewingPublicKey, keys.clueKey));
291
+ }
292
+ function encodeShortAddress(ownerAddress) {
293
+ const identity2 = identityBytes(ownerAddress);
294
+ return (0, import_ethers.getAddress)(
295
+ typeof ownerAddress === "string" ? ownerAddress : `0x${(0, import_utils2.bytesToHex)(identity2)}`
296
+ );
297
+ }
298
+ function decodeAddress(address) {
299
+ if (typeof address !== "string") throw new TypeError("address must be text");
300
+ if (address.startsWith("0x")) {
301
+ const owner2 = (0, import_ethers.getAddress)(address);
302
+ return { kind: "short", ownerAddress: `0x${(0, import_utils2.bytesToHex)(identityBytes(owner2))}` };
303
+ }
304
+ if (!address.startsWith("0i")) throw new Error("invalid Invisi address prefix");
305
+ const prefix = "0i";
306
+ if (address.length !== 138) throw new Error("invalid address length");
307
+ const data = address.slice(prefix.length).toLowerCase();
308
+ if (Array.from(data).some((character) => !ALPHABET.includes(character)))
309
+ throw new Error("invalid address alphabet");
310
+ if (checkedText(prefix, data) !== address) throw new Error("invalid address case checksum");
311
+ let buffer2 = 0, bits = 0;
312
+ const payload = [];
313
+ for (const character of data) {
314
+ buffer2 = buffer2 << 5 | ALPHABET.indexOf(character);
315
+ bits += 5;
316
+ if (bits >= 8) {
317
+ bits -= 8;
318
+ payload.push(buffer2 >>> bits & 255);
319
+ }
320
+ buffer2 &= (1 << bits) - 1;
321
+ }
322
+ if (bits !== 0 || buffer2 !== 0) throw new Error("noncanonical address padding");
323
+ const raw = Uint8Array.from(payload);
324
+ const ownerAddress = `0x${(0, import_utils2.bytesToHex)(identityBytes(`0x${(0, import_utils2.bytesToHex)(raw.subarray(0, 20))}`))}`;
325
+ return {
326
+ kind: "long",
327
+ ownerAddress,
328
+ ...receivingKeys({ viewingPublicKey: raw.slice(20, 53), clueKey: raw.slice(53) })
329
+ };
330
+ }
331
+
332
+ // src/errors.ts
333
+ var MissingCapabilityError = class extends Error {
334
+ constructor(capability) {
335
+ super(`Missing ${capability} capability`);
336
+ this.capability = capability;
337
+ this.name = "MissingCapabilityError";
338
+ }
339
+ code = "MISSING_CAPABILITY";
340
+ };
341
+
342
+ // src/address/receiving-address.ts
343
+ var ReceivingAddress = class _ReceivingAddress {
344
+ #text;
345
+ constructor(text) {
346
+ const decoded = decodeAddress(text);
347
+ this.#text = decoded.kind === "short" ? encodeShortAddress(decoded.ownerAddress) : text;
348
+ Object.freeze(this);
349
+ }
350
+ static parse(text) {
351
+ return new _ReceivingAddress(text);
352
+ }
353
+ static from(recipient) {
354
+ return new _ReceivingAddress(encodeLongAddress(recipient));
355
+ }
356
+ static fromIdentity(identity2) {
357
+ return new _ReceivingAddress(encodeShortAddress(identity2));
358
+ }
359
+ get ownerAddress() {
360
+ return decodeAddress(this.#text).ownerAddress;
361
+ }
362
+ get kind() {
363
+ return decodeAddress(this.#text).kind;
364
+ }
365
+ getReceivingKeys() {
366
+ const decoded = decodeAddress(this.#text);
367
+ if (decoded.kind === "short") {
368
+ throw new MissingCapabilityError("resolved receiving keys");
369
+ }
370
+ return { viewingPublicKey: decoded.viewingPublicKey, clueKey: decoded.clueKey };
371
+ }
372
+ async resolve(lookup) {
373
+ if (this.kind === "long") {
374
+ return this;
375
+ }
376
+ if (!lookup) {
377
+ throw new MissingCapabilityError("explicit registry lookup");
378
+ }
379
+ const keys = await lookup(this.ownerAddress);
380
+ if (!keys) {
381
+ throw new Error("owner address is not registered");
382
+ }
383
+ return _ReceivingAddress.from({
384
+ ownerAddress: this.ownerAddress,
385
+ viewingPublicKey: keys.viewingPublicKey,
386
+ clueKey: keys.clueKey
387
+ });
388
+ }
389
+ get viewingPublicKey() {
390
+ return this.getReceivingKeys().viewingPublicKey;
391
+ }
392
+ get clueKey() {
393
+ return this.getReceivingKeys().clueKey;
394
+ }
395
+ toString() {
396
+ return this.#text;
397
+ }
398
+ toJSON() {
399
+ return this.#text;
400
+ }
401
+ };
402
+
403
+ // src/crypto/domains.ts
404
+ var D_IDENT = 2n;
405
+ var D_NOTE = 3n;
406
+ var D_NULL = 4n;
407
+ var D_CREDIT = 5n;
408
+ var D_CNULL = 6n;
409
+ var D_CREDIT_KEY_HASH = 7n;
410
+ var D_CREDIT_RECOVERY = 9n;
411
+ var D_CREDIT_STATE_NONCE = 10n;
412
+ var TREE_DEPTH = 16;
413
+
414
+ // src/crypto/random.ts
415
+ var FIELD_MODULUS = 21888242871839275222246405745257275088548364400416034343698204186575808495617n;
416
+ function sampleField(nonzero) {
417
+ const bytes3 = new Uint8Array(32);
418
+ for (; ; ) {
419
+ crypto.getRandomValues(bytes3);
420
+ const candidate = BigInt(
421
+ "0x" + Array.from(bytes3, (b) => b.toString(16).padStart(2, "0")).join("")
422
+ );
423
+ if (candidate < FIELD_MODULUS && (!nonzero || candidate !== 0n)) return candidate;
424
+ }
425
+ }
426
+ function generateNonce() {
427
+ return sampleField(false);
428
+ }
429
+ function generateCreditSharedKey() {
430
+ return sampleField(true);
431
+ }
432
+
433
+ // src/crypto/poseidon-constants.ts
434
+ var INTERNAL_DIAGONAL = [
435
+ 0x10dc6e9c006ea38b04b1e03b4bd9490c0d03f98929ca1d7fb56821fd19d3b6e7n,
436
+ 0x0c28145b6a44df3e0149b3d0a30b3bb599df9756d4dd9b84a86b38cfb45a740bn,
437
+ 0x00544b8338791518b2c7645a50392798b21f75bb60e3596170067d00141cac15n,
438
+ 0x222c01175718386f2e2e82eb122789e352e105a3b8fa852613bc534433ee428bn
439
+ ];
440
+ var ROUND_CONSTANTS = [
441
+ [
442
+ 0x19b849f69450b06848da1d39bd5e4a4302bb86744edc26238b0878e269ed23e5n,
443
+ 0x265ddfe127dd51bd7239347b758f0a1320eb2cc7450acc1dad47f80c8dcf34d6n,
444
+ 0x199750ec472f1809e0f66a545e1e51624108ac845015c2aa3dfc36bab497d8aan,
445
+ 0x157ff3fe65ac7208110f06a5f74302b14d743ea25067f0ffd032f787c7f1cdf8n
446
+ ],
447
+ [
448
+ 0x2e49c43c4569dd9c5fd35ac45fca33f10b15c590692f8beefe18f4896ac94902n,
449
+ 0x0e35fb89981890520d4aef2b6d6506c3cb2f0b6973c24fa82731345ffa2d1f1en,
450
+ 0x251ad47cb15c4f1105f109ae5e944f1ba9d9e7806d667ffec6fe723002e0b996n,
451
+ 0x13da07dc64d428369873e97160234641f8beb56fdd05e5f3563fa39d9c22df4en
452
+ ],
453
+ [
454
+ 0x0c009b84e650e6d23dc00c7dccef7483a553939689d350cd46e7b89055fd4738n,
455
+ 0x011f16b1c63a854f01992e3956f42d8b04eb650c6d535eb0203dec74befdca06n,
456
+ 0x0ed69e5e383a688f209d9a561daa79612f3f78d0467ad45485df07093f367549n,
457
+ 0x04dba94a7b0ce9e221acad41472b6bbe3aec507f5eb3d33f463672264c9f789bn
458
+ ],
459
+ [
460
+ 0x0a3f2637d840f3a16eb094271c9d237b6036757d4bb50bf7ce732ff1d4fa28e8n,
461
+ 0x259a666f129eea198f8a1c502fdb38fa39b1f075569564b6e54a485d1182323fn,
462
+ 0x28bf7459c9b2f4c6d8e7d06a4ee3a47f7745d4271038e5157a32fdf7ede0d6a1n,
463
+ 0x0a1ca941f057037526ea200f489be8d4c37c85bbcce6a2aeec91bd6941432447n
464
+ ],
465
+ [0x0c6f8f958be0e93053d7fd4fc54512855535ed1539f051dcb43a26fd926361cfn, 0x0n, 0x0n, 0x0n],
466
+ [0x123106a93cd17578d426e8128ac9d90aa9e8a00708e296e084dd57e69caaf811n, 0x0n, 0x0n, 0x0n],
467
+ [0x26e1ba52ad9285d97dd3ab52f8e840085e8fa83ff1e8f1877b074867cd2dee75n, 0x0n, 0x0n, 0x0n],
468
+ [0x1cb55cad7bd133de18a64c5c47b9c97cbe4d8b7bf9e095864471537e6a4ae2c5n, 0x0n, 0x0n, 0x0n],
469
+ [0x1dcd73e46acd8f8e0e2c7ce04bde7f6d2a53043d5060a41c7143f08e6e9055d0n, 0x0n, 0x0n, 0x0n],
470
+ [0x011003e32f6d9c66f5852f05474a4def0cda294a0eb4e9b9b12b9bb4512e5574n, 0x0n, 0x0n, 0x0n],
471
+ [0x2b1e809ac1d10ab29ad5f20d03a57dfebadfe5903f58bafed7c508dd2287ae8cn, 0x0n, 0x0n, 0x0n],
472
+ [0x2539de1785b735999fb4dac35ee17ed0ef995d05ab2fc5faeaa69ae87bcec0a5n, 0x0n, 0x0n, 0x0n],
473
+ [0x0c246c5a2ef8ee0126497f222b3e0a0ef4e1c3d41c86d46e43982cb11d77951dn, 0x0n, 0x0n, 0x0n],
474
+ [0x192089c4974f68e95408148f7c0632edbb09e6a6ad1a1c2f3f0305f5d03b527bn, 0x0n, 0x0n, 0x0n],
475
+ [0x1eae0ad8ab68b2f06a0ee36eeb0d0c058529097d91096b756d8fdc2fb5a60d85n, 0x0n, 0x0n, 0x0n],
476
+ [0x179190e5d0e22179e46f8282872abc88db6e2fdc0dee99e69768bd98c5d06bfbn, 0x0n, 0x0n, 0x0n],
477
+ [0x29bb9e2c9076732576e9a81c7ac4b83214528f7db00f31bf6cafe794a9b3cd1cn, 0x0n, 0x0n, 0x0n],
478
+ [0x225d394e42207599403efd0c2464a90d52652645882aac35b10e590e6e691e08n, 0x0n, 0x0n, 0x0n],
479
+ [0x064760623c25c8cf753d238055b444532be13557451c087de09efd454b23fd59n, 0x0n, 0x0n, 0x0n],
480
+ [0x10ba3a0e01df92e87f301c4b716d8a394d67f4bf42a75c10922910a78f6b5b87n, 0x0n, 0x0n, 0x0n],
481
+ [0x0e070bf53f8451b24f9c6e96b0c2a801cb511bc0c242eb9d361b77693f21471cn, 0x0n, 0x0n, 0x0n],
482
+ [0x1b94cd61b051b04dd39755ff93821a73ccd6cb11d2491d8aa7f921014de252fbn, 0x0n, 0x0n, 0x0n],
483
+ [0x1d7cb39bafb8c744e148787a2e70230f9d4e917d5713bb050487b5aa7d74070bn, 0x0n, 0x0n, 0x0n],
484
+ [0x2ec93189bd1ab4f69117d0fe980c80ff8785c2961829f701bb74ac1f303b17dbn, 0x0n, 0x0n, 0x0n],
485
+ [0x2db366bfdd36d277a692bb825b86275beac404a19ae07a9082ea46bd83517926n, 0x0n, 0x0n, 0x0n],
486
+ [0x062100eb485db06269655cf186a68532985275428450359adc99cec6960711b8n, 0x0n, 0x0n, 0x0n],
487
+ [0x0761d33c66614aaa570e7f1e8244ca1120243f92fa59e4f900c567bf41f5a59bn, 0x0n, 0x0n, 0x0n],
488
+ [0x20fc411a114d13992c2705aa034e3f315d78608a0f7de4ccf7a72e494855ad0dn, 0x0n, 0x0n, 0x0n],
489
+ [0x25b5c004a4bdfcb5add9ec4e9ab219ba102c67e8b3effb5fc3a30f317250bc5an, 0x0n, 0x0n, 0x0n],
490
+ [0x23b1822d278ed632a494e58f6df6f5ed038b186d8474155ad87e7dff62b37f4bn, 0x0n, 0x0n, 0x0n],
491
+ [0x22734b4c5c3f9493606c4ba9012499bf0f14d13bfcfcccaa16102a29cc2f69e0n, 0x0n, 0x0n, 0x0n],
492
+ [0x26c0c8fe09eb30b7e27a74dc33492347e5bdff409aa3610254413d3fad795ce5n, 0x0n, 0x0n, 0x0n],
493
+ [0x070dd0ccb6bd7bbae88eac03fa1fbb26196be3083a809829bbd626df348ccad9n, 0x0n, 0x0n, 0x0n],
494
+ [0x12b6595bdb329b6fb043ba78bb28c3bec2c0a6de46d8c5ad6067c4ebfd4250dan, 0x0n, 0x0n, 0x0n],
495
+ [0x248d97d7f76283d63bec30e7a5876c11c06fca9b275c671c5e33d95bb7e8d729n, 0x0n, 0x0n, 0x0n],
496
+ [0x1a306d439d463b0816fc6fd64cc939318b45eb759ddde4aa106d15d9bd9baaaan, 0x0n, 0x0n, 0x0n],
497
+ [0x28a8f8372e3c38daced7c00421cb4621f4f1b54ddc27821b0d62d3d6ec7c56cfn, 0x0n, 0x0n, 0x0n],
498
+ [0x0094975717f9a8a8bb35152f24d43294071ce320c829f388bc852183e1e2ce7en, 0x0n, 0x0n, 0x0n],
499
+ [0x04d5ee4c3aa78f7d80fde60d716480d3593f74d4f653ae83f4103246db2e8d65n, 0x0n, 0x0n, 0x0n],
500
+ [0x2a6cf5e9aa03d4336349ad6fb8ed2269c7bef54b8822cc76d08495c12efde187n, 0x0n, 0x0n, 0x0n],
501
+ [0x2304d31eaab960ba9274da43e19ddeb7f792180808fd6e43baae48d7efcba3f3n, 0x0n, 0x0n, 0x0n],
502
+ [0x03fd9ac865a4b2a6d5e7009785817249bff08a7e0726fcb4e1c11d39d199f0b0n, 0x0n, 0x0n, 0x0n],
503
+ [0x00b7258ded52bbda2248404d55ee5044798afc3a209193073f7954d4d63b0b64n, 0x0n, 0x0n, 0x0n],
504
+ [0x159f81ada0771799ec38fca2d4bf65ebb13d3a74f3298db36272c5ca65e92d9an, 0x0n, 0x0n, 0x0n],
505
+ [0x1ef90e67437fbc8550237a75bc28e3bb9000130ea25f0c5471e144cf4264431fn, 0x0n, 0x0n, 0x0n],
506
+ [0x1e65f838515e5ff0196b49aa41a2d2568df739bc176b08ec95a79ed82932e30dn, 0x0n, 0x0n, 0x0n],
507
+ [0x2b1b045def3a166cec6ce768d079ba74b18c844e570e1f826575c1068c94c33fn, 0x0n, 0x0n, 0x0n],
508
+ [0x0832e5753ceb0ff6402543b1109229c165dc2d73bef715e3f1c6e07c168bb173n, 0x0n, 0x0n, 0x0n],
509
+ [0x02f614e9cedfb3dc6b762ae0a37d41bab1b841c2e8b6451bc5a8e3c390b6ad16n, 0x0n, 0x0n, 0x0n],
510
+ [0x0e2427d38bd46a60dd640b8e362cad967370ebb777bedff40f6a0be27e7ed705n, 0x0n, 0x0n, 0x0n],
511
+ [0x0493630b7c670b6deb7c84d414e7ce79049f0ec098c3c7c50768bbe29214a53an, 0x0n, 0x0n, 0x0n],
512
+ [0x22ead100e8e482674decdab17066c5a26bb1515355d5461a3dc06cc85327cea9n, 0x0n, 0x0n, 0x0n],
513
+ [0x25b3e56e655b42cdaae2626ed2554d48583f1ae35626d04de5084e0b6d2a6f16n, 0x0n, 0x0n, 0x0n],
514
+ [0x1e32752ada8836ef5837a6cde8ff13dbb599c336349e4c584b4fdc0a0cf6f9d0n, 0x0n, 0x0n, 0x0n],
515
+ [0x2fa2a871c15a387cc50f68f6f3c3455b23c00995f05078f672a9864074d412e5n, 0x0n, 0x0n, 0x0n],
516
+ [0x2f569b8a9a4424c9278e1db7311e889f54ccbf10661bab7fcd18e7c7a7d83505n, 0x0n, 0x0n, 0x0n],
517
+ [0x044cb455110a8fdd531ade530234c518a7df93f7332ffd2144165374b246b43dn, 0x0n, 0x0n, 0x0n],
518
+ [0x227808de93906d5d420246157f2e42b191fe8c90adfe118178ddc723a5319025n, 0x0n, 0x0n, 0x0n],
519
+ [0x02fcca2934e046bc623adead873579865d03781ae090ad4a8579d2e7a6800355n, 0x0n, 0x0n, 0x0n],
520
+ [0x0ef915f0ac120b876abccceb344a1d36bad3f3c5ab91a8ddcbec2e060d8befacn, 0x0n, 0x0n, 0x0n],
521
+ [
522
+ 0x1797130f4b7a3e1777eb757bc6f287f6ab0fb85f6be63b09f3b16ef2b1405d38n,
523
+ 0x0a76225dc04170ae3306c85abab59e608c7f497c20156d4d36c668555decc6e5n,
524
+ 0x1fffb9ec1992d66ba1e77a7b93209af6f8fa76d48acb664796174b5326a31a5cn,
525
+ 0x25721c4fc15a3f2853b57c338fa538d85f8fbba6c6b9c6090611889b797b9c5fn
526
+ ],
527
+ [
528
+ 0x0c817fd42d5f7a41215e3d07ba197216adb4c3790705da95eb63b982bfcaf75an,
529
+ 0x13abe3f5239915d39f7e13c2c24970b6df8cf86ce00a22002bc15866e52b5a96n,
530
+ 0x2106feea546224ea12ef7f39987a46c85c1bc3dc29bdbd7a92cd60acb4d391cen,
531
+ 0x21ca859468a746b6aaa79474a37dab49f1ca5a28c748bc7157e1b3345bb0f959n
532
+ ],
533
+ [
534
+ 0x05ccd6255c1e6f0c5cf1f0df934194c62911d14d0321662a8f1a48999e34185bn,
535
+ 0x0f0e34a64b70a626e464d846674c4c8816c4fb267fe44fe6ea28678cb09490a4n,
536
+ 0x0558531a4e25470c6157794ca36d0e9647dbfcfe350d64838f5b1a8a2de0d4bfn,
537
+ 0x09d3dca9173ed2faceea125157683d18924cadad3f655a60b72f5864961f1455n
538
+ ],
539
+ [
540
+ 0x0328cbd54e8c0913493f866ed03d218bf23f92d68aaec48617d4c722e5bd4335n,
541
+ 0x2bf07216e2aff0a223a487b1a7094e07e79e7bcc9798c648ee3347dd5329d34bn,
542
+ 0x1daf345a58006b736499c583cb76c316d6f78ed6a6dffc82111e11a63fe412dfn,
543
+ 0x176563472456aaa746b694c60e1823611ef39039b2edc7ff391e6f2293d2c404n
544
+ ]
545
+ ];
546
+
547
+ // src/crypto/poseidon.ts
548
+ var mod2 = (n) => n % FIELD_MODULUS;
549
+ var fifth = (n) => {
550
+ const x = mod2(n), x2 = mod2(x * x);
551
+ return mod2(x * x2 * x2);
552
+ };
553
+ function external(s) {
554
+ const t0 = s[0] + s[1], t1 = s[2] + s[3], t2 = 2n * s[1] + t1, t3 = 2n * s[3] + t0;
555
+ const t4 = 4n * t1 + t3, t5 = 4n * t0 + t2;
556
+ s[0] = mod2(t3 + t5);
557
+ s[1] = mod2(t5);
558
+ s[2] = mod2(t2 + t4);
559
+ s[3] = mod2(t4);
560
+ }
561
+ function permute(s) {
562
+ external(s);
563
+ for (let r = 0; r < 64; r++) {
564
+ const full = r < 4 || r >= 60, rc = ROUND_CONSTANTS[r];
565
+ if (full) {
566
+ for (let i = 0; i < 4; i++) s[i] = fifth(s[i] + rc[i]);
567
+ external(s);
568
+ } else {
569
+ s[0] = fifth(s[0] + rc[0]);
570
+ const sum = s.reduce((a, b) => a + b, 0n);
571
+ for (let i = 0; i < 4; i++) s[i] = mod2(s[i] * INTERNAL_DIAGONAL[i] + sum);
572
+ }
573
+ }
574
+ }
575
+ async function poseidonHash(inputs) {
576
+ for (let i = 0; i < inputs.length; i++)
577
+ if (inputs[i] < 0n || inputs[i] >= FIELD_MODULUS)
578
+ throw new RangeError(
579
+ `poseidonHash: input[${i}] out of BN254 field range [0, FIELD_MODULUS): ${inputs[i]}`
580
+ );
581
+ const state = [0n, 0n, 0n, BigInt(inputs.length) << 64n];
582
+ let used = 0;
583
+ for (const input of inputs) {
584
+ if (used === 3) {
585
+ permute(state);
586
+ used = 0;
587
+ }
588
+ state[used] = mod2(state[used] + input);
589
+ used++;
590
+ }
591
+ permute(state);
592
+ return `0x${state[0].toString(16).padStart(64, "0")}`;
593
+ }
594
+
595
+ // src/delivery/delivery-codec.ts
596
+ var import_ethers2 = require("ethers");
597
+
598
+ // src/delivery/note-payload.ts
599
+ var import_secp256k12 = require("@noble/curves/secp256k1");
600
+
601
+ // src/notes/noteContext.ts
602
+ function assertNoteContext(note, context2) {
603
+ if (note.chainId !== context2.chainId || note.protocolAddress !== context2.protocolAddress) {
604
+ throw new Error("note context mismatch");
605
+ }
606
+ }
607
+
608
+ // src/credit/state.ts
609
+ var CREDIT_POLICY_FIELDS = ["singleLimit", "dailyLimit", "monthlyLimit"];
610
+ var CREDIT_USAGE_FIELDS = [
611
+ "lastDay",
612
+ "dailySpent",
613
+ "lastMonth",
614
+ "monthlySpent",
615
+ "totalSpent"
616
+ ];
617
+ function normalizeCreditPolicy(value = {}) {
618
+ return Object.fromEntries(
619
+ CREDIT_POLICY_FIELDS.map((key) => {
620
+ const n = value[key] ?? 0n;
621
+ uint(n, 126, key);
622
+ return [key, n];
623
+ })
624
+ );
625
+ }
626
+ function normalizeCreditUsage(value = {}) {
627
+ const usage = Object.fromEntries(
628
+ CREDIT_USAGE_FIELDS.map((key) => {
629
+ const n = value[key] ?? 0n;
630
+ uint(n, key === "lastDay" || key === "lastMonth" ? 64 : 128, key);
631
+ return [key, n];
632
+ })
633
+ );
634
+ for (const [period, spent] of [
635
+ ["lastDay", "dailySpent"],
636
+ ["lastMonth", "monthlySpent"]
637
+ ])
638
+ if (usage[period] === 0n && usage[spent] !== 0n) throw Error("noncanonical Credit usage");
639
+ return usage;
640
+ }
641
+ function balance(value) {
642
+ uint(value.availableCredit, 128, "availableCredit");
643
+ if (typeof value.unlimited !== "boolean") throw TypeError("unlimited must be boolean");
644
+ if (value.unlimited && value.availableCredit !== 0n)
645
+ throw Error("unlimited Credit must have zero available credit");
646
+ return { availableCredit: value.availableCredit, unlimited: value.unlimited };
647
+ }
648
+ function creditPeriods(timestamp) {
649
+ if (typeof timestamp !== "bigint") throw new TypeError("Credit timestamp required");
650
+ uint(timestamp, 64, "timestamp");
651
+ const days = timestamp / 86400n, z = days + 719468n, era = z / 146097n, doe = z - era * 146097n;
652
+ const yoe = (doe - doe / 1460n + doe / 36524n - doe / 146096n) / 365n;
653
+ let year = yoe + era * 400n;
654
+ const doy = doe - (365n * yoe + yoe / 4n - yoe / 100n), mp = (5n * doy + 2n) / 153n;
655
+ const month = mp + (mp < 10n ? 3n : -9n);
656
+ if (month <= 2n) year++;
657
+ return { currentDay: days + 1n, currentMonth: (year - 1970n) * 12n + month };
658
+ }
659
+ function applyCreditDrawUsage(note, amount, timestamp) {
660
+ const policy = {
661
+ ...normalizeCreditPolicy(note),
662
+ ...normalizeCreditUsage(note),
663
+ ...balance(note)
664
+ }, periods = creditPeriods(timestamp);
665
+ uint(amount, 126, "draw amount");
666
+ if (amount === 0n) throw new Error("draw amount must be positive");
667
+ if (policy.singleLimit !== 0n && amount > policy.singleLimit)
668
+ throw new Error("single limit exceeded");
669
+ for (const [limit, last, spent, current] of [
670
+ ["dailyLimit", "lastDay", "dailySpent", "currentDay"],
671
+ ["monthlyLimit", "lastMonth", "monthlySpent", "currentMonth"]
672
+ ]) {
673
+ const period = periods[current];
674
+ if (period < policy[last]) throw new Error("Credit period rollback");
675
+ policy[spent] = (period === policy[last] ? policy[spent] : 0n) + amount;
676
+ uint(policy[spent], 128, spent);
677
+ policy[last] = period;
678
+ if (policy[limit] !== 0n && policy[spent] > policy[limit])
679
+ throw new Error(`${limit} exceeded`);
680
+ }
681
+ policy.totalSpent += amount;
682
+ uint(policy.totalSpent, 128, "totalSpent");
683
+ if (!policy.unlimited) {
684
+ if (amount > policy.availableCredit) throw Error("available credit exceeded");
685
+ policy.availableCredit -= amount;
686
+ }
687
+ return policy;
688
+ }
689
+ async function computeCreditSharedKeyHash(creditSharedKey) {
690
+ if (creditSharedKey <= 0n || creditSharedKey >= FIELD_MODULUS)
691
+ throw new RangeError("invalid Credit shared key");
692
+ return poseidonHash([D_CREDIT_KEY_HASH, creditSharedKey]);
693
+ }
694
+ function normalizeCreditState(state) {
695
+ const allowed = [
696
+ "chainId",
697
+ "protocolAddress",
698
+ "tokenAddress",
699
+ "spenderWalletIdentity",
700
+ "ownerWalletIdentity",
701
+ "revocationShard",
702
+ "revocationSlot",
703
+ "nonce",
704
+ "expiry",
705
+ "creditSharedKey",
706
+ ...CREDIT_POLICY_FIELDS,
707
+ ...CREDIT_USAGE_FIELDS,
708
+ "availableCredit",
709
+ "unlimited",
710
+ "payload"
711
+ ];
712
+ if (!state || typeof state !== "object" || Object.keys(state).some((k) => !allowed.includes(k)))
713
+ throw Error("Unsupported Credit fields");
714
+ for (const [name, bits] of [
715
+ ["chainId", 64],
716
+ ["protocolAddress", 160],
717
+ ["tokenAddress", 160],
718
+ ["revocationShard", 64],
719
+ ["revocationSlot", 16],
720
+ ["expiry", 64]
721
+ ])
722
+ uint(state[name], bits, name);
723
+ for (const id of [state.spenderWalletIdentity, state.ownerWalletIdentity])
724
+ if (!/^0x[0-9a-fA-F]{40}$/.test(id) || BigInt(id) === 0n)
725
+ throw Error("invalid Credit identity");
726
+ if (!state.protocolAddress || !state.tokenAddress || !state.revocationShard || !state.expiry)
727
+ throw Error("Credit deployment, token, shard and expiry must be positive");
728
+ field2(state.nonce, "nonce");
729
+ field2(state.creditSharedKey, "creditSharedKey");
730
+ if (!state.creditSharedKey) throw Error("Credit shared key must be positive");
731
+ if (state.payload !== void 0 && !(state.payload instanceof Uint8Array))
732
+ throw TypeError("payload must be Uint8Array");
733
+ return {
734
+ ...state,
735
+ ...normalizeCreditPolicy(state),
736
+ ...normalizeCreditUsage(state),
737
+ ...balance(state),
738
+ ...state.payload !== void 0 ? { payload: Uint8Array.from(state.payload) } : {}
739
+ };
740
+ }
741
+ async function computeCreditCommitment(state, keyHash) {
742
+ if (keyHash === void 0) state = normalizeCreditState(state);
743
+ for (const [name, bits] of [
744
+ ["chainId", 64],
745
+ ["protocolAddress", 160],
746
+ ["tokenAddress", 160],
747
+ ["revocationShard", 64],
748
+ ["revocationSlot", 16],
749
+ ["expiry", 64]
750
+ ])
751
+ uint(state[name], bits, name);
752
+ for (const identity2 of [state.spenderWalletIdentity, state.ownerWalletIdentity])
753
+ if (!/^0x[0-9a-fA-F]{40}$/.test(identity2) || BigInt(identity2) === 0n)
754
+ throw new Error("invalid Credit identity");
755
+ if (state.revocationShard === 0n || state.expiry === 0n)
756
+ throw new Error("Credit shard and expiry must be positive");
757
+ field2(state.nonce, "nonce");
758
+ const hash3 = keyHash ?? await computeCreditSharedKeyHash(state.creditSharedKey);
759
+ field2(BigInt(hash3), "Credit shared key hash");
760
+ return poseidonHash([
761
+ D_CREDIT,
762
+ state.chainId,
763
+ state.protocolAddress,
764
+ BigInt(state.spenderWalletIdentity),
765
+ BigInt(state.ownerWalletIdentity),
766
+ state.tokenAddress,
767
+ state.revocationShard,
768
+ state.revocationSlot,
769
+ state.nonce,
770
+ state.expiry,
771
+ BigInt(hash3),
772
+ ...CREDIT_POLICY_FIELDS.map((k) => normalizeCreditPolicy(state)[k]),
773
+ balance(state).availableCredit,
774
+ state.unlimited ? 1n : 0n,
775
+ ...CREDIT_USAGE_FIELDS.map((k) => normalizeCreditUsage(state)[k])
776
+ ]);
777
+ }
778
+ async function computeCreditNullifier(chainId, creditSharedKey, creditCommitment2) {
779
+ uint(chainId, 64, "chainId");
780
+ if (creditSharedKey <= 0n || creditSharedKey >= FIELD_MODULUS)
781
+ throw new RangeError("invalid Credit shared key");
782
+ if (!/^0x[0-9a-fA-F]{64}$/.test(creditCommitment2))
783
+ throw new Error("invalid Credit commitment encoding");
784
+ return poseidonHash([D_CNULL, chainId, creditSharedKey, BigInt(creditCommitment2)]);
785
+ }
786
+ async function deriveCreditStateNonce(chainId, protocolAddress, tokenAddress, creditNullifier, creditSharedKey) {
787
+ return BigInt(
788
+ await poseidonHash([
789
+ D_CREDIT_STATE_NONCE,
790
+ chainId,
791
+ protocolAddress,
792
+ tokenAddress,
793
+ creditNullifier,
794
+ creditSharedKey
795
+ ])
796
+ );
797
+ }
798
+ async function computeCreditRecoveryPad(chainId, protocolAddress, tokenAddress, creditNullifier, creditSharedKey) {
799
+ return BigInt(
800
+ await poseidonHash([
801
+ D_CREDIT_RECOVERY,
802
+ chainId,
803
+ protocolAddress,
804
+ tokenAddress,
805
+ creditNullifier,
806
+ creditSharedKey
807
+ ])
808
+ );
809
+ }
810
+ function uint(value, bits, name) {
811
+ if (typeof value !== "bigint" || value < 0n || value >= 1n << BigInt(bits))
812
+ throw new RangeError(`${name} out of uint${bits} range`);
813
+ }
814
+ function field2(value, name) {
815
+ if (typeof value !== "bigint" || value < 0n || value >= FIELD_MODULUS)
816
+ throw new RangeError(`${name} out of BN254 field range`);
817
+ }
818
+ async function recoverCreditState(context2, previous, recovery) {
819
+ assertNoteContext(previous, context2);
820
+ const nullifier = await computeCreditNullifier(
821
+ context2.chainId,
822
+ previous.creditSharedKey,
823
+ await computeCreditCommitment(previous)
824
+ );
825
+ if (nullifier.toLowerCase() !== recovery.creditNullifier.toLowerCase())
826
+ throw new Error("previous Credit nullifier mismatch");
827
+ field2(recovery.recoveryAmount, "recoveryAmount");
828
+ const args = [
829
+ context2.chainId,
830
+ context2.protocolAddress,
831
+ previous.tokenAddress,
832
+ BigInt(nullifier),
833
+ previous.creditSharedKey
834
+ ];
835
+ const nonce2 = await deriveCreditStateNonce(...args), mask = await computeCreditRecoveryPad(...args);
836
+ const debit = (recovery.recoveryAmount - mask + FIELD_MODULUS) % FIELD_MODULUS;
837
+ uint(debit, 126, "recovered debit");
838
+ if (!debit) throw new Error("invalid recovered debit");
839
+ if (nonce2 === previous.nonce) throw new Error("state nonce must change");
840
+ const { payload, ...financial } = previous;
841
+ const state = {
842
+ ...financial,
843
+ ...applyCreditDrawUsage(previous, debit, recovery.timestamp),
844
+ nonce: nonce2
845
+ };
846
+ if (await computeCreditCommitment(state) !== recovery.stateCommitment)
847
+ throw new Error("recovered Credit commitment mismatch");
848
+ return state;
849
+ }
850
+ async function deriveCreditState(previous, debit, timestamp) {
851
+ const nullifier = await computeCreditNullifier(
852
+ previous.chainId,
853
+ previous.creditSharedKey,
854
+ await computeCreditCommitment(previous)
855
+ );
856
+ const args = [
857
+ previous.chainId,
858
+ previous.protocolAddress,
859
+ previous.tokenAddress,
860
+ BigInt(nullifier),
861
+ previous.creditSharedKey
862
+ ];
863
+ const nonce2 = await deriveCreditStateNonce(...args), mask = await computeCreditRecoveryPad(...args);
864
+ if (nonce2 === previous.nonce) throw Error("state nonce must change");
865
+ const state = { ...previous, ...applyCreditDrawUsage(previous, debit, timestamp), nonce: nonce2 };
866
+ return {
867
+ state,
868
+ stateCommitment: await computeCreditCommitment(state),
869
+ nullifier,
870
+ recoveryAmount: (debit + mask) % FIELD_MODULUS
871
+ };
872
+ }
873
+ async function deriveCreditUpdate(previous, changes, deadline) {
874
+ for (const key of Object.keys(changes))
875
+ if (![
876
+ ...CREDIT_POLICY_FIELDS,
877
+ "expiry",
878
+ "unlimited",
879
+ "increaseAvailableCredit",
880
+ "decreaseAvailableCredit"
881
+ ].includes(key))
882
+ throw Error("unsupported Credit update field");
883
+ const old = normalizeCreditState(previous), increase = changes.increaseAvailableCredit === void 0 ? 0n : changes.increaseAvailableCredit, decrease = changes.decreaseAvailableCredit === void 0 ? 0n : changes.decreaseAvailableCredit;
884
+ uint(increase, 128, "increaseAvailableCredit");
885
+ uint(decrease, 128, "decreaseAvailableCredit");
886
+ if (increase !== 0n && decrease !== 0n)
887
+ throw Error("Credit increase and decrease are mutually exclusive");
888
+ const unlimited = changes.unlimited === void 0 ? old.unlimited : changes.unlimited;
889
+ if (typeof unlimited !== "boolean") throw TypeError("unlimited must be boolean");
890
+ let availableCredit = 0n;
891
+ if (unlimited) {
892
+ if (increase !== 0n || decrease !== 0n)
893
+ throw Error("unlimited update cannot adjust available credit");
894
+ } else {
895
+ if (decrease > old.availableCredit)
896
+ throw Error("available credit decrease exceeds balance");
897
+ availableCredit = old.availableCredit + increase - decrease;
898
+ uint(availableCredit, 128, "availableCredit");
899
+ }
900
+ uint(deadline, 64, "deadline");
901
+ const next = {
902
+ ...old,
903
+ ...normalizeCreditPolicy({ ...old, ...changes }),
904
+ expiry: changes.expiry ?? old.expiry,
905
+ availableCredit,
906
+ unlimited
907
+ };
908
+ if (previous.expiry < deadline || next.expiry < deadline)
909
+ throw Error("expiry before request deadline");
910
+ const nullifier = await computeCreditNullifier(
911
+ previous.chainId,
912
+ previous.creditSharedKey,
913
+ await computeCreditCommitment(previous)
914
+ );
915
+ const nonce2 = await deriveCreditStateNonce(
916
+ previous.chainId,
917
+ previous.protocolAddress,
918
+ previous.tokenAddress,
919
+ BigInt(nullifier),
920
+ previous.creditSharedKey
921
+ );
922
+ if (nonce2 === previous.nonce) throw Error("state nonce must change");
923
+ const state = { ...next, nonce: nonce2 };
924
+ return { state, nullifier, stateCommitment: await computeCreditCommitment(state) };
925
+ }
926
+
927
+ // src/delivery/note-payload.ts
928
+ function concat(...parts) {
929
+ const out = new Uint8Array(parts.reduce((length, part) => length + part.length, 0));
930
+ let offset = 0;
931
+ for (const part of parts) {
932
+ out.set(part, offset);
933
+ offset += part.length;
934
+ }
935
+ return out;
936
+ }
937
+ function uint2(value, bits, name) {
938
+ if (value < 0n || value >= 1n << BigInt(bits))
939
+ throw new RangeError(`${name} out of uint${bits} range`);
940
+ }
941
+ function encode2(value, length = 32) {
942
+ uint2(value, length * 8, "encoded value");
943
+ const out = new Uint8Array(length);
944
+ for (let i = length - 1; i >= 0; i--) {
945
+ out[i] = Number(value & 255n);
946
+ value >>= 8n;
947
+ }
948
+ return out;
949
+ }
950
+ function decode(value) {
951
+ return value.reduce((result, byte) => result << 8n | BigInt(byte), 0n);
952
+ }
953
+ function hexField(value) {
954
+ uint2(value, 160, "owner address");
955
+ return `0x${value.toString(16).padStart(40, "0")}`;
956
+ }
957
+ function receivingKeys2(keys) {
958
+ if (!keys || !(keys.viewingPublicKey instanceof Uint8Array) || !(keys.clueKey instanceof Uint8Array))
959
+ throw new Error("receiving keys required");
960
+ const viewingPublicKey2 = Uint8Array.from(keys.viewingPublicKey), clueKey = Uint8Array.from(keys.clueKey);
961
+ if (viewingPublicKey2.length !== 33 || viewingPublicKey2[0] !== 2 && viewingPublicKey2[0] !== 3)
962
+ throw new Error("invalid compressed viewing public key");
963
+ import_secp256k12.secp256k1.ProjectivePoint.fromHex(viewingPublicKey2).assertValidity();
964
+ if (clueKey.length !== 32) throw new Error("invalid clue key length");
965
+ validateClueKey(clueKey);
966
+ return { viewingPublicKey: viewingPublicKey2, clueKey };
967
+ }
968
+ function normalizeCreditDeliveryState(state) {
969
+ if (!state || typeof state !== "object" || !state.note || typeof state.note !== "object")
970
+ throw new TypeError("complete Credit delivery state required");
971
+ const allowed = [
972
+ "chainId",
973
+ "protocolAddress",
974
+ "tokenAddress",
975
+ "spenderWalletIdentity",
976
+ "ownerWalletIdentity",
977
+ "revocationShard",
978
+ "revocationSlot",
979
+ "nonce",
980
+ "expiry",
981
+ "creditSharedKey",
982
+ "singleLimit",
983
+ "dailyLimit",
984
+ "monthlyLimit",
985
+ "availableCredit",
986
+ "unlimited",
987
+ "lastDay",
988
+ "dailySpent",
989
+ "lastMonth",
990
+ "monthlySpent",
991
+ "totalSpent",
992
+ "payload"
993
+ ];
994
+ if (Object.keys(state.note).some((k) => !allowed.includes(k)))
995
+ throw Error("Unsupported Credit fields");
996
+ const note = normalizeCreditState(structuredClone(state.note));
997
+ return {
998
+ note: {
999
+ ...note,
1000
+ ...normalizeCreditPolicy(note)
1001
+ },
1002
+ ownerReceivingKeys: receivingKeys2(state.ownerReceivingKeys),
1003
+ spenderReceivingKeys: receivingKeys2(state.spenderReceivingKeys)
1004
+ };
1005
+ }
1006
+ function encodePayload(value) {
1007
+ if (value !== void 0 && !(value instanceof Uint8Array))
1008
+ throw new TypeError("payload must be Uint8Array");
1009
+ const payload = value ?? new Uint8Array();
1010
+ if (payload.length > 65535) throw new RangeError("payload length exceeds uint16");
1011
+ return concat(encode2(BigInt(payload.length), 2), payload);
1012
+ }
1013
+ function readPayload(plain, offset) {
1014
+ if (plain.length < offset + 2) throw new Error("missing payload length");
1015
+ const length = Number(decode(plain.slice(offset, offset + 2)));
1016
+ if (plain.length !== offset + 2 + length) throw new Error("invalid payload length");
1017
+ return Uint8Array.from(plain.subarray(offset + 2));
1018
+ }
1019
+ var fundsEnvelopeV1 = {
1020
+ kind: "funds",
1021
+ version: 1,
1022
+ minimum: 160,
1023
+ maximum: 4096,
1024
+ encode(note) {
1025
+ return concat(
1026
+ encode2(BigInt(note.ownerAddress)),
1027
+ encode2(note.amount, 16),
1028
+ encode2(note.nonce),
1029
+ encodePayload(note.payload)
1030
+ );
1031
+ },
1032
+ decode(plain, context2) {
1033
+ return {
1034
+ chainId: context2.chainId,
1035
+ protocolAddress: context2.protocolAddress,
1036
+ tokenAddress: context2.tokenAddress,
1037
+ ownerAddress: hexField(decode(plain.slice(0, 32))),
1038
+ amount: decode(plain.slice(32, 48)),
1039
+ nonce: decode(plain.slice(48, 80)),
1040
+ payload: readPayload(plain, 80)
1041
+ };
1042
+ }
1043
+ };
1044
+ var creditWireFields = [
1045
+ ["spenderWalletIdentity", 32],
1046
+ ["ownerWalletIdentity", 32],
1047
+ ["revocationShard", 8],
1048
+ ["revocationSlot", 2],
1049
+ ["nonce", 32],
1050
+ ["expiry", 8],
1051
+ ["creditSharedKey", 32],
1052
+ ["singleLimit", 16],
1053
+ ["dailyLimit", 16],
1054
+ ["monthlyLimit", 16],
1055
+ ["availableCredit", 16],
1056
+ ["unlimited", 1],
1057
+ ["lastDay", 8],
1058
+ ["dailySpent", 16],
1059
+ ["lastMonth", 8],
1060
+ ["monthlySpent", 16],
1061
+ ["totalSpent", 16]
1062
+ ];
1063
+ var creditEnvelopeV2 = {
1064
+ kind: "credit",
1065
+ version: 2,
1066
+ minimum: 485,
1067
+ maximum: 4096,
1068
+ encode(input) {
1069
+ const state = normalizeCreditDeliveryState(input);
1070
+ return concat(
1071
+ ...creditWireFields.map(([k, size]) => encode2(BigInt(state.note[k]), size)),
1072
+ state.ownerReceivingKeys.viewingPublicKey,
1073
+ state.ownerReceivingKeys.clueKey,
1074
+ state.spenderReceivingKeys.viewingPublicKey,
1075
+ state.spenderReceivingKeys.clueKey,
1076
+ encodePayload(state.note.payload)
1077
+ );
1078
+ },
1079
+ decode(plain, context2) {
1080
+ let offset = 0;
1081
+ const note = {
1082
+ chainId: context2.chainId,
1083
+ protocolAddress: context2.protocolAddress,
1084
+ tokenAddress: context2.tokenAddress
1085
+ };
1086
+ for (const [k, size] of creditWireFields) {
1087
+ const n = decode(plain.slice(offset, offset + size));
1088
+ if (k === "unlimited" && n !== 0n && n !== 1n)
1089
+ throw Error("invalid Credit unlimited byte");
1090
+ note[k] = k === "unlimited" ? n === 1n : k.endsWith("Identity") ? hexField(n) : n;
1091
+ offset += size;
1092
+ }
1093
+ const keys = () => {
1094
+ const k = {
1095
+ viewingPublicKey: plain.slice(offset, offset + 33),
1096
+ clueKey: plain.slice(offset + 33, offset + 65)
1097
+ };
1098
+ offset += 65;
1099
+ return k;
1100
+ };
1101
+ const ownerReceivingKeys = keys(), spenderReceivingKeys = keys();
1102
+ note.payload = readPayload(plain, offset);
1103
+ return normalizeCreditDeliveryState({
1104
+ note,
1105
+ ownerReceivingKeys,
1106
+ spenderReceivingKeys
1107
+ });
1108
+ }
1109
+ };
1110
+
1111
+ // src/delivery/delivery-codec.ts
1112
+ var MAX_CIPHERTEXT_BYTES = 4096;
1113
+ function deliveryHeader(kind) {
1114
+ if (kind !== "funds" && kind !== "credit") throw new Error("unsupported delivery kind");
1115
+ return kind === "credit" ? 130 : 1;
1116
+ }
1117
+ function decodeDeliveryHeader(header) {
1118
+ if (!Number.isInteger(header) || header < 0 || header > 255)
1119
+ throw new Error("delivery header must be one byte");
1120
+ const kind = header >>> 7 ? "credit" : "funds";
1121
+ const version = header & 127;
1122
+ if (version === 0) throw new Error("invalid envelope version");
1123
+ return { kind, version };
1124
+ }
1125
+ function deliveryCodec(header) {
1126
+ const { kind, version } = decodeDeliveryHeader(header);
1127
+ if (version !== (kind === "credit" ? 2 : 1))
1128
+ throw new Error(`unsupported envelope version ${version}`);
1129
+ const codec = kind === "funds" ? fundsEnvelopeV1 : creditEnvelopeV2;
1130
+ return {
1131
+ ...codec,
1132
+ domain: `Invisi.${kind === "funds" ? "Note" : "Credit"}Delivery.v${kind === "credit" ? 2 : 1}`
1133
+ };
1134
+ }
1135
+ function validateDeliveryCiphertext(value, kind) {
1136
+ const data = Uint8Array.from((0, import_ethers2.getBytes)(value));
1137
+ if (data.length === 0 || data.length > MAX_CIPHERTEXT_BYTES)
1138
+ throw new Error("invalid envelope length");
1139
+ const codec = deliveryCodec(data[0]);
1140
+ if (kind !== void 0 && codec.kind !== kind) throw new Error("unexpected envelope kind");
1141
+ if (data.length < codec.minimum || data.length > codec.maximum)
1142
+ throw new Error("invalid envelope length");
1143
+ return data;
1144
+ }
1145
+
1146
+ // src/delivery/errors.ts
1147
+ var DeliveryAuthenticationError = class extends Error {
1148
+ constructor(cause) {
1149
+ super("delivery authentication failed", { cause });
1150
+ this.name = "DeliveryAuthenticationError";
1151
+ }
1152
+ };
1153
+
1154
+ // src/delivery/envelope.ts
1155
+ var import_secp256k13 = require("@noble/curves/secp256k1");
1156
+
1157
+ // src/delivery/binary.ts
1158
+ var ascii = (value) => Uint8Array.from(value, (character) => character.charCodeAt(0));
1159
+ function concat2(...parts) {
1160
+ const out = new Uint8Array(parts.reduce((size, p) => size + p.length, 0));
1161
+ let offset = 0;
1162
+ for (const part of parts) {
1163
+ out.set(part, offset);
1164
+ offset += part.length;
1165
+ }
1166
+ return out;
1167
+ }
1168
+ function bytes(value, length) {
1169
+ let out;
1170
+ if (typeof value === "string") {
1171
+ if (!/^0x(?:[0-9a-fA-F]{2})*$/.test(value)) throw new Error("invalid hexadecimal bytes");
1172
+ out = Uint8Array.from(value.slice(2).match(/../g) ?? [], (b) => parseInt(b, 16));
1173
+ } else if (value instanceof Uint8Array) out = Uint8Array.from(value);
1174
+ else throw new Error("expected hexadecimal bytes or Uint8Array");
1175
+ if (out.length !== length) throw new Error(`expected ${length} bytes`);
1176
+ return out;
1177
+ }
1178
+ function uint3(value, bits, name) {
1179
+ if (value < 0n || value >= 1n << BigInt(bits))
1180
+ throw new RangeError(`${name} out of uint${bits} range`);
1181
+ }
1182
+ function field3(value, name) {
1183
+ if (value < 0n || value >= FIELD_MODULUS)
1184
+ throw new RangeError(`${name} out of BN254 field range`);
1185
+ }
1186
+ function encode3(value, length = 32) {
1187
+ uint3(value, length * 8, "encoded value");
1188
+ const out = new Uint8Array(length);
1189
+ for (let i = length - 1; i >= 0; i--) {
1190
+ out[i] = Number(value & 255n);
1191
+ value >>= 8n;
1192
+ }
1193
+ return out;
1194
+ }
1195
+
1196
+ // src/notes/commitment.ts
1197
+ async function computeNoteIdentity(ownerAddress, nonce2) {
1198
+ if (ownerAddress <= 0n || ownerAddress >= 1n << 160n)
1199
+ throw new RangeError("invalid owner address");
1200
+ return poseidonHash([D_IDENT, ownerAddress, nonce2]);
1201
+ }
1202
+ async function computeNoteCommitment(chainId, protocolAddress, identity2, tokenAddress, amount) {
1203
+ if (chainId < 0n || chainId >= 1n << 64n) throw new RangeError("invalid chainId");
1204
+ if (protocolAddress <= 0n || protocolAddress >= 1n << 160n)
1205
+ throw new RangeError("invalid protocol address");
1206
+ if (tokenAddress < 0n || tokenAddress >= 1n << 160n)
1207
+ throw new RangeError("invalid token address");
1208
+ if (amount <= 0n || amount >= 1n << 128n) throw new RangeError("invalid note amount");
1209
+ if (!/^0x[0-9a-fA-F]{64}$/.test(identity2)) throw new Error("invalid note identity encoding");
1210
+ return poseidonHash([D_NOTE, chainId, protocolAddress, BigInt(identity2), tokenAddress, amount]);
1211
+ }
1212
+
1213
+ // src/delivery/validation.ts
1214
+ function validateContext(context2, tokenAddress) {
1215
+ uint3(context2.chainId, 64, "chainId");
1216
+ uint3(context2.protocolAddress, 160, "protocolAddress");
1217
+ uint3(tokenAddress, 160, "tokenAddress");
1218
+ field3(BigInt(context2.outputCommitment), "outputCommitment");
1219
+ }
1220
+ function validateNote(note) {
1221
+ uint3(note.amount, 128, "amount");
1222
+ if (note.amount === 0n) throw new Error("amount must be positive");
1223
+ field3(note.nonce, "nonce");
1224
+ uint3(BigInt(note.ownerAddress), 160, "ownerAddress");
1225
+ if (BigInt(note.ownerAddress) === 0n) throw new Error("owner address must be positive");
1226
+ uint3(note.tokenAddress, 160, "tokenAddress");
1227
+ }
1228
+ async function noteCommitment(context2, note) {
1229
+ validateContext(context2, note.tokenAddress);
1230
+ assertNoteContext(note, context2);
1231
+ validateNote(note);
1232
+ const commitment2 = await computeNoteCommitment(
1233
+ context2.chainId,
1234
+ BigInt(context2.protocolAddress),
1235
+ await computeNoteIdentity(BigInt(note.ownerAddress), note.nonce),
1236
+ note.tokenAddress,
1237
+ note.amount
1238
+ );
1239
+ if (BigInt(commitment2) !== BigInt(context2.outputCommitment))
1240
+ throw new Error("output commitment mismatch");
1241
+ }
1242
+ async function creditCommitment(context2, note) {
1243
+ validateContext(context2, note.tokenAddress);
1244
+ assertNoteContext(note, context2);
1245
+ const commitment2 = await computeCreditCommitment(note);
1246
+ if (BigInt(commitment2) !== BigInt(context2.outputCommitment))
1247
+ throw new Error("credit commitment mismatch");
1248
+ }
1249
+
1250
+ // src/delivery/envelope.ts
1251
+ function aad(domain, context2, tokenAddress, header, role) {
1252
+ validateContext(context2, tokenAddress);
1253
+ return concat2(
1254
+ domain,
1255
+ header,
1256
+ new Uint8Array([role]),
1257
+ encode3(context2.chainId, 8),
1258
+ encode3(context2.protocolAddress, 20),
1259
+ encode3(tokenAddress, 20),
1260
+ encode3(BigInt(context2.outputCommitment))
1261
+ );
1262
+ }
1263
+ function privateKey(value) {
1264
+ const key = bytes(value, 32);
1265
+ if (!import_secp256k13.secp256k1.utils.isValidPrivateKey(key)) throw new Error("invalid viewing private key");
1266
+ return key;
1267
+ }
1268
+ function publicKey(value) {
1269
+ const key = bytes(value, 33);
1270
+ if (key[0] !== 2 && key[0] !== 3) throw new Error("expected compressed secp256k1 public key");
1271
+ import_secp256k13.secp256k1.ProjectivePoint.fromHex(key);
1272
+ return key;
1273
+ }
1274
+ function viewingPublicKey(key) {
1275
+ return import_secp256k13.secp256k1.getPublicKey(privateKey(key), true);
1276
+ }
1277
+ function random(length) {
1278
+ return crypto.getRandomValues(new Uint8Array(length));
1279
+ }
1280
+ function ephemeralKey() {
1281
+ for (; ; ) {
1282
+ const key = random(32);
1283
+ if (import_secp256k13.secp256k1.utils.isValidPrivateKey(key)) return key;
1284
+ }
1285
+ }
1286
+ var buffer = (value) => Uint8Array.from(value).buffer;
1287
+ async function derivedKey(key, peer, header, recipient, domain, extractable = false) {
1288
+ const secret = import_secp256k13.secp256k1.getSharedSecret(key, peer, true).slice(1);
1289
+ const material = await crypto.subtle.importKey("raw", buffer(secret), "HKDF", false, [
1290
+ "deriveKey"
1291
+ ]);
1292
+ try {
1293
+ return await crypto.subtle.deriveKey(
1294
+ {
1295
+ name: "HKDF",
1296
+ hash: "SHA-256",
1297
+ salt: buffer(header.slice(34, 50)),
1298
+ info: buffer(concat2(domain, header.slice(1, 34), recipient))
1299
+ },
1300
+ material,
1301
+ { name: "AES-GCM", length: 256 },
1302
+ extractable,
1303
+ ["encrypt", "decrypt"]
1304
+ );
1305
+ } finally {
1306
+ secret.fill(0);
1307
+ }
1308
+ }
1309
+ async function seal(viewingKey, plaintext, context2, tokenAddress, headerByte, role) {
1310
+ const peer = publicKey(viewingKey), key = ephemeralKey();
1311
+ const salt = random(16), iv = random(12), domain = deliveryDomain(headerByte);
1312
+ const header = concat2(
1313
+ new Uint8Array([headerByte]),
1314
+ import_secp256k13.secp256k1.getPublicKey(key, true),
1315
+ salt,
1316
+ iv
1317
+ );
1318
+ const additionalData = aad(domain, context2, tokenAddress, header, role);
1319
+ try {
1320
+ const encrypted = await crypto.subtle.encrypt(
1321
+ {
1322
+ name: "AES-GCM",
1323
+ iv: buffer(iv),
1324
+ additionalData: buffer(additionalData),
1325
+ tagLength: 128
1326
+ },
1327
+ await derivedKey(key, peer, header, peer, domain),
1328
+ buffer(plaintext)
1329
+ );
1330
+ return concat2(header, new Uint8Array(encrypted));
1331
+ } finally {
1332
+ key.fill(0);
1333
+ plaintext.fill(0);
1334
+ }
1335
+ }
1336
+ async function open(key, envelope, context2, headerByte, role, payloadKey = false) {
1337
+ const data = envelopeBytes(envelope, deliveryCodec(headerByte).kind === "credit");
1338
+ if (data[0] !== headerByte) throw new Error("unsupported envelope version");
1339
+ const domain = deliveryDomain(headerByte);
1340
+ const additionalData = aad(domain, context2, context2.tokenAddress, data.slice(0, 62), role);
1341
+ const localKey = payloadKey ? bytes(key, 32) : privateKey(key);
1342
+ try {
1343
+ const aes = payloadKey ? await crypto.subtle.importKey("raw", buffer(bytes(key, 32)), "AES-GCM", false, [
1344
+ "decrypt"
1345
+ ]) : await derivedKey(
1346
+ localKey,
1347
+ publicKey(data.slice(1, 34)),
1348
+ data.slice(0, 62),
1349
+ import_secp256k13.secp256k1.getPublicKey(localKey, true),
1350
+ domain
1351
+ );
1352
+ const decrypted = await crypto.subtle.decrypt(
1353
+ {
1354
+ name: "AES-GCM",
1355
+ iv: buffer(data.slice(50, 62)),
1356
+ additionalData: buffer(additionalData),
1357
+ tagLength: 128
1358
+ },
1359
+ aes,
1360
+ buffer(data.slice(62))
1361
+ ).catch((error) => {
1362
+ if (error instanceof Error && error.name === "OperationError")
1363
+ throw new DeliveryAuthenticationError(error);
1364
+ throw error;
1365
+ });
1366
+ return new Uint8Array(decrypted);
1367
+ } finally {
1368
+ localKey.fill(0);
1369
+ }
1370
+ }
1371
+ function deliveryDomain(headerByte) {
1372
+ return ascii(deliveryCodec(headerByte).domain);
1373
+ }
1374
+ async function exportPayloadKey(key, envelope, credit) {
1375
+ const data = envelopeBytes(envelope, credit);
1376
+ const localKey = privateKey(key);
1377
+ try {
1378
+ const aes = await derivedKey(
1379
+ localKey,
1380
+ publicKey(data.slice(1, 34)),
1381
+ data.slice(0, 62),
1382
+ viewingPublicKey(localKey),
1383
+ deliveryDomain(data[0]),
1384
+ true
1385
+ );
1386
+ return new Uint8Array(await crypto.subtle.exportKey("raw", aes));
1387
+ } finally {
1388
+ localKey.fill(0);
1389
+ }
1390
+ }
1391
+ function envelopeBytes(value, credit) {
1392
+ return validateDeliveryCiphertext(value, credit ? "credit" : "funds");
1393
+ }
1394
+
1395
+ // src/delivery/encryption.ts
1396
+ function copyPayload(value, codec) {
1397
+ if (!(value instanceof Uint8Array)) throw new Error("codec encode must return Uint8Array");
1398
+ if (value.length + 78 < codec.minimum || value.length + 78 > codec.maximum)
1399
+ throw new Error("invalid codec payload length");
1400
+ return Uint8Array.from(value);
1401
+ }
1402
+ async function encryptPrivateTransferNote(viewingKey, note, context2) {
1403
+ const scopedNote = structuredClone(note), scopedContext = { ...context2 };
1404
+ const codec = deliveryCodec(deliveryHeader("funds"));
1405
+ if (codec.kind !== "funds") throw new Error("unexpected envelope kind");
1406
+ await noteCommitment(scopedContext, scopedNote);
1407
+ const plaintext = copyPayload(codec.encode(structuredClone(scopedNote)), codec);
1408
+ return seal(
1409
+ viewingKey,
1410
+ plaintext,
1411
+ scopedContext,
1412
+ scopedNote.tokenAddress,
1413
+ deliveryHeader("funds"),
1414
+ 0
1415
+ );
1416
+ }
1417
+ async function readNote(key, envelope, context2, payloadKey = false) {
1418
+ const authenticatedContext = { ...context2 };
1419
+ const data = envelopeBytes(envelope, false);
1420
+ const headerByte = data[0];
1421
+ const plain = await open(key, data, authenticatedContext, headerByte, 0, payloadKey);
1422
+ try {
1423
+ const codec = deliveryCodec(headerByte);
1424
+ if (codec.kind !== "funds") throw new Error("unexpected envelope kind");
1425
+ const note = structuredClone(
1426
+ codec.decode(plain, Object.freeze({ ...authenticatedContext }))
1427
+ );
1428
+ if (BigInt(note.ownerAddress) !== BigInt(authenticatedContext.ownerAddress))
1429
+ throw new Error("recipient owner address mismatch");
1430
+ if (note.tokenAddress !== authenticatedContext.tokenAddress)
1431
+ throw new Error("note token context mismatch");
1432
+ await noteCommitment(authenticatedContext, note);
1433
+ return note;
1434
+ } finally {
1435
+ plain.fill(0);
1436
+ }
1437
+ }
1438
+ function creditRole(role) {
1439
+ if (role !== "owner" && role !== "spender")
1440
+ throw new Error("Credit role must be owner or spender");
1441
+ return role === "owner" ? 1 : 2;
1442
+ }
1443
+ function matchCreditRecipient(state, role, recipient) {
1444
+ const keys = role === "owner" ? state.ownerReceivingKeys : state.spenderReceivingKeys;
1445
+ if (!keys.viewingPublicKey.every((value, index) => value === recipient[index]))
1446
+ throw new Error("recipient receiving key mismatch");
1447
+ }
1448
+ async function encryptCreditState(viewingKey, state, context2) {
1449
+ const scopedState = normalizeCreditDeliveryState(state), scopedNote = scopedState.note;
1450
+ const scopedContext = { ...context2 };
1451
+ const codec = deliveryCodec(deliveryHeader("credit"));
1452
+ if (codec.kind !== "credit") throw new Error("unexpected envelope kind");
1453
+ creditRole(scopedContext.role);
1454
+ matchCreditRecipient(scopedState, scopedContext.role, publicKey(viewingKey));
1455
+ await creditCommitment(scopedContext, scopedNote);
1456
+ const plaintext = copyPayload(codec.encode(structuredClone(scopedState)), codec);
1457
+ return seal(
1458
+ viewingKey,
1459
+ plaintext,
1460
+ scopedContext,
1461
+ scopedNote.tokenAddress,
1462
+ deliveryHeader("credit"),
1463
+ creditRole(scopedContext.role)
1464
+ );
1465
+ }
1466
+ async function readCredit(key, envelope, context2, payloadKey = false) {
1467
+ const authenticatedContext = { ...context2 };
1468
+ const data = envelopeBytes(envelope, true);
1469
+ const plain = await open(
1470
+ key,
1471
+ data,
1472
+ authenticatedContext,
1473
+ data[0],
1474
+ creditRole(authenticatedContext.role),
1475
+ payloadKey
1476
+ );
1477
+ try {
1478
+ const codec = deliveryCodec(data[0]);
1479
+ if (codec.kind !== "credit") throw new Error("unexpected envelope kind");
1480
+ const state = normalizeCreditDeliveryState(
1481
+ structuredClone(codec.decode(plain, Object.freeze({ ...authenticatedContext })))
1482
+ );
1483
+ const note = state.note;
1484
+ if (BigInt(
1485
+ authenticatedContext.role === "owner" ? note.ownerWalletIdentity : note.spenderWalletIdentity
1486
+ ) !== BigInt(authenticatedContext.ownerAddress))
1487
+ throw new Error("recipient owner address mismatch");
1488
+ if (!payloadKey)
1489
+ matchCreditRecipient(state, authenticatedContext.role, viewingPublicKey(key));
1490
+ if (note.tokenAddress !== authenticatedContext.tokenAddress)
1491
+ throw new Error("Credit token context mismatch");
1492
+ await creditCommitment(authenticatedContext, note);
1493
+ return {
1494
+ ...state,
1495
+ note: { ...state.note, payload: Uint8Array.from(state.note.payload ?? []) }
1496
+ };
1497
+ } finally {
1498
+ plain.fill(0);
1499
+ }
1500
+ }
1501
+ function decryptPrivateTransferNote(key, envelope, context2) {
1502
+ return readNote(key, envelope, context2);
1503
+ }
1504
+ function decryptCreditState(key, envelope, context2) {
1505
+ return readCredit(key, envelope, context2);
1506
+ }
1507
+ function decryptNotePayload(key, envelope, context2) {
1508
+ return readNote(key, envelope, context2, true);
1509
+ }
1510
+ function decryptCreditPayload(key, envelope, context2) {
1511
+ return readCredit(key, envelope, context2, true);
1512
+ }
1513
+
1514
+ // src/disclosure/disclosure.ts
1515
+ var import_utils3 = require("@noble/hashes/utils");
1516
+ var NOTE_FIELDS = ["ownerAddress", "amount", "nonce"];
1517
+ var CREDIT_FIELDS = [
1518
+ "spenderWalletIdentity",
1519
+ "ownerWalletIdentity",
1520
+ "revocationShard",
1521
+ "revocationSlot",
1522
+ "nonce",
1523
+ "expiry",
1524
+ "creditSharedKeyHash",
1525
+ "singleLimit",
1526
+ "dailyLimit",
1527
+ "monthlyLimit",
1528
+ "availableCredit",
1529
+ "unlimited",
1530
+ "lastDay",
1531
+ "dailySpent",
1532
+ "lastMonth",
1533
+ "monthlySpent",
1534
+ "totalSpent"
1535
+ ];
1536
+ function record(value, fields) {
1537
+ if (!value || typeof value !== "object" || Array.isArray(value))
1538
+ throw new Error("invalid disclosure object");
1539
+ const keys = Object.keys(value);
1540
+ if (keys.length !== fields.length || keys.some((key) => !fields.includes(key)))
1541
+ throw new Error("unexpected disclosure fields");
1542
+ return value;
1543
+ }
1544
+ function number(value, bits, label) {
1545
+ if (typeof value !== "bigint" || value < 0n || value >= 1n << BigInt(bits))
1546
+ throw new Error(`invalid disclosure ${label}`);
1547
+ return value;
1548
+ }
1549
+ function decimal(value, bits, label) {
1550
+ if (typeof value !== "string" || !/^(0|[1-9][0-9]*)$/.test(value) || value.length > 78)
1551
+ throw new Error(`invalid disclosure ${label}`);
1552
+ return number(BigInt(value), bits, label);
1553
+ }
1554
+ function hash2(value, label, field4 = true) {
1555
+ if (typeof value !== "string" || !/^0x[0-9a-fA-F]{64}$/.test(value))
1556
+ throw new Error(`invalid disclosure ${label}`);
1557
+ if (field4 && BigInt(value) >= FIELD_MODULUS) throw new Error(`invalid disclosure ${label}`);
1558
+ return value.toLowerCase();
1559
+ }
1560
+ function owner(value, label) {
1561
+ if (typeof value !== "string" || !/^0x[0-9a-fA-F]{40}$/.test(value) || BigInt(value) === 0n)
1562
+ throw new Error(`invalid disclosure ${label}`);
1563
+ return value.toLowerCase();
1564
+ }
1565
+ function nonce(value) {
1566
+ if (value < 0n || value >= FIELD_MODULUS) throw new Error("invalid disclosure nonce");
1567
+ return value;
1568
+ }
1569
+ function context(value) {
1570
+ const result = {
1571
+ chainId: number(value.chainId, 64, "chainId"),
1572
+ protocolAddress: number(value.protocolAddress, 160, "protocolAddress"),
1573
+ tokenAddress: number(value.tokenAddress, 160, "tokenAddress")
1574
+ };
1575
+ if (result.protocolAddress === 0n || result.tokenAddress === 0n)
1576
+ throw new Error("disclosure addresses must be nonzero");
1577
+ return result;
1578
+ }
1579
+ function encode4(values) {
1580
+ return Object.fromEntries(
1581
+ Object.entries(values).map(([key, value]) => [key, value.toString()])
1582
+ );
1583
+ }
1584
+ async function creditOpening(note) {
1585
+ const copied = { ...note };
1586
+ return {
1587
+ ...context(copied),
1588
+ ...normalizeCreditPolicy(copied),
1589
+ ...normalizeCreditUsage(copied),
1590
+ availableCredit: number(copied.availableCredit, 128, "availableCredit"),
1591
+ unlimited: copied.unlimited,
1592
+ spenderWalletIdentity: owner(copied.spenderWalletIdentity, "spender identity"),
1593
+ ownerWalletIdentity: owner(copied.ownerWalletIdentity, "owner identity"),
1594
+ revocationShard: number(copied.revocationShard, 64, "revocation shard"),
1595
+ revocationSlot: number(copied.revocationSlot, 16, "revocation slot"),
1596
+ nonce: nonce(copied.nonce),
1597
+ expiry: number(copied.expiry, 64, "expiry"),
1598
+ creditSharedKeyHash: await computeCreditSharedKeyHash(copied.creditSharedKey)
1599
+ };
1600
+ }
1601
+ async function commitment(note) {
1602
+ if ("creditSharedKeyHash" in note) {
1603
+ return computeCreditCommitment({ ...note, creditSharedKey: 0n }, note.creditSharedKeyHash);
1604
+ }
1605
+ return computeNoteCommitment(
1606
+ note.chainId,
1607
+ BigInt(note.protocolAddress),
1608
+ await computeNoteIdentity(BigInt(note.ownerAddress), note.nonce),
1609
+ note.tokenAddress,
1610
+ note.amount
1611
+ );
1612
+ }
1613
+ var Disclosure = class _Disclosure {
1614
+ #material;
1615
+ constructor(material) {
1616
+ this.#material = {
1617
+ ...material,
1618
+ context: Object.freeze({ ...material.context }),
1619
+ data: Object.freeze({ ...material.data })
1620
+ };
1621
+ Object.freeze(this.#material);
1622
+ Object.freeze(this);
1623
+ }
1624
+ static async fromNote(value) {
1625
+ const copied = { ...value };
1626
+ const note = {
1627
+ ...context(copied),
1628
+ ownerAddress: owner(copied.ownerAddress, "owner address"),
1629
+ amount: number(copied.amount, 128, "amount"),
1630
+ nonce: nonce(copied.nonce)
1631
+ };
1632
+ return new _Disclosure({
1633
+ version: 1,
1634
+ kind: "note",
1635
+ method: "opening",
1636
+ context: {
1637
+ ...encode4(context(note)),
1638
+ outputCommitment: await commitment(note)
1639
+ },
1640
+ data: encode4({
1641
+ ownerAddress: note.ownerAddress,
1642
+ amount: note.amount,
1643
+ nonce: note.nonce
1644
+ })
1645
+ });
1646
+ }
1647
+ static async fromCredit(value) {
1648
+ const note = await creditOpening(value);
1649
+ const data = Object.fromEntries(
1650
+ CREDIT_FIELDS.map((key) => [
1651
+ key,
1652
+ key === "unlimited" ? note.unlimited ? "1" : "0" : note[key].toString()
1653
+ ])
1654
+ );
1655
+ return new _Disclosure({
1656
+ version: 1,
1657
+ kind: "credit",
1658
+ method: "opening",
1659
+ context: {
1660
+ ...encode4(context(note)),
1661
+ outputCommitment: await commitment(note)
1662
+ },
1663
+ data
1664
+ });
1665
+ }
1666
+ /** Low-level factory; normally use wallet.getViewer().discloseNote(). */
1667
+ static async fromEncryptedNote(key, envelope, value) {
1668
+ const scoped = { ...value };
1669
+ const copied = typeof envelope === "string" ? envelope : Uint8Array.from(envelope);
1670
+ const localKey = typeof key === "string" ? key : Uint8Array.from(key);
1671
+ try {
1672
+ await decryptPrivateTransferNote(localKey, copied, scoped);
1673
+ return await _Disclosure.fromEnvelope(localKey, copied, scoped, "funds");
1674
+ } finally {
1675
+ if (localKey instanceof Uint8Array) localKey.fill(0);
1676
+ }
1677
+ }
1678
+ /** Exposes the full selected Credit envelope, including its shared key and receiving snapshots. */
1679
+ static async fromEncryptedCredit(key, envelope, value) {
1680
+ const scoped = { ...value };
1681
+ const copied = typeof envelope === "string" ? envelope : Uint8Array.from(envelope);
1682
+ const localKey = typeof key === "string" ? key : Uint8Array.from(key);
1683
+ try {
1684
+ await decryptCreditState(localKey, copied, scoped);
1685
+ return await _Disclosure.fromEnvelope(localKey, copied, scoped, scoped.role);
1686
+ } finally {
1687
+ if (localKey instanceof Uint8Array) localKey.fill(0);
1688
+ }
1689
+ }
1690
+ static async fromEnvelope(key, envelope, scoped, role) {
1691
+ const payloadKey = await exportPayloadKey(key, envelope, role !== "funds");
1692
+ try {
1693
+ return new _Disclosure({
1694
+ version: 1,
1695
+ kind: role === "funds" ? "note" : "credit",
1696
+ method: "payload-key",
1697
+ context: {
1698
+ ...encode4(context(scoped)),
1699
+ outputCommitment: hash2(scoped.outputCommitment, "commitment")
1700
+ },
1701
+ data: {
1702
+ payloadKey: `0x${(0, import_utils3.bytesToHex)(payloadKey)}`,
1703
+ ownerAddress: owner(scoped.ownerAddress, "owner address"),
1704
+ role
1705
+ }
1706
+ });
1707
+ } finally {
1708
+ payloadKey.fill(0);
1709
+ }
1710
+ }
1711
+ /** Parse untrusted JSON material; verification against an independent output is still required. */
1712
+ static async fromExport(value) {
1713
+ const root = record(value, ["version", "kind", "method", "context", "data"]);
1714
+ if (root.version !== 1 || root.kind !== "note" && root.kind !== "credit" || root.method !== "opening" && root.method !== "payload-key")
1715
+ throw new Error("unsupported disclosure format");
1716
+ const rawContext = record(root.context, [
1717
+ "chainId",
1718
+ "protocolAddress",
1719
+ "tokenAddress",
1720
+ "outputCommitment"
1721
+ ]);
1722
+ const scoped = context({
1723
+ chainId: decimal(rawContext.chainId, 64, "chainId"),
1724
+ protocolAddress: decimal(rawContext.protocolAddress, 160, "protocolAddress"),
1725
+ tokenAddress: decimal(rawContext.tokenAddress, 160, "tokenAddress")
1726
+ });
1727
+ const outputCommitment = hash2(rawContext.outputCommitment, "commitment");
1728
+ const fields = root.method === "payload-key" ? ["payloadKey", "ownerAddress", "role"] : root.kind === "note" ? NOTE_FIELDS : CREDIT_FIELDS;
1729
+ const raw = record(root.data, fields);
1730
+ const data = {};
1731
+ for (const key of fields) {
1732
+ if (key === "role") {
1733
+ if (root.kind === "note" ? raw[key] !== "funds" : raw[key] !== "owner" && raw[key] !== "spender")
1734
+ throw new Error("invalid disclosure role");
1735
+ data[key] = raw[key];
1736
+ } else if (key === "payloadKey" || key.endsWith("Identity") || key === "ownerAddress" || key === "creditSharedKeyHash") {
1737
+ data[key] = key.endsWith("Identity") || key === "ownerAddress" ? owner(raw[key], key) : hash2(raw[key], key, key !== "payloadKey");
1738
+ } else if (key === "unlimited") {
1739
+ if (raw[key] !== "0" && raw[key] !== "1")
1740
+ throw Error("invalid disclosure unlimited");
1741
+ data[key] = raw[key];
1742
+ } else {
1743
+ const bits = key === "availableCredit" ? 128 : key.endsWith("Payout") ? 160 : key === "expiry" ? 64 : key === "amount" ? root.kind === "note" ? 128 : 126 : 254;
1744
+ const parsed = decimal(raw[key], bits, key);
1745
+ if (key === "nonce") nonce(parsed);
1746
+ data[key] = parsed.toString();
1747
+ }
1748
+ }
1749
+ const material = {
1750
+ version: 1,
1751
+ kind: root.kind,
1752
+ method: root.method,
1753
+ context: { ...encode4(scoped), outputCommitment },
1754
+ data
1755
+ };
1756
+ const result = new _Disclosure(material);
1757
+ if (material.method === "opening")
1758
+ await result.verify({
1759
+ ...scoped,
1760
+ outputCommitment,
1761
+ role: material.kind === "note" ? "funds" : "owner"
1762
+ });
1763
+ return result;
1764
+ }
1765
+ getScope() {
1766
+ const { version, kind, method } = this.#material;
1767
+ return {
1768
+ version,
1769
+ kind,
1770
+ method,
1771
+ permitsOwnerSweep: kind === "note",
1772
+ revealsNoteConsumption: kind === "note",
1773
+ revealsCreditSharedKey: kind === "credit" && method === "payload-key",
1774
+ revealsReceivingKeys: kind === "credit" && method === "payload-key",
1775
+ revealsMemo: method === "payload-key",
1776
+ revealsPayload: method === "payload-key"
1777
+ };
1778
+ }
1779
+ /** Explicitly export sensitive, JSON-compatible material for a chosen recipient. */
1780
+ export() {
1781
+ return {
1782
+ ...this.#material,
1783
+ context: { ...this.#material.context },
1784
+ data: { ...this.#material.data }
1785
+ };
1786
+ }
1787
+ async verify(value) {
1788
+ const output = {
1789
+ ...value,
1790
+ ciphertext: value.ciphertext instanceof Uint8Array ? Uint8Array.from(value.ciphertext) : value.ciphertext
1791
+ };
1792
+ const scoped = context(output);
1793
+ const material = this.#material;
1794
+ for (const field4 of ["chainId", "protocolAddress", "tokenAddress"]) {
1795
+ if (scoped[field4].toString() !== material.context[field4])
1796
+ throw new Error("disclosure deployment context mismatch");
1797
+ }
1798
+ if (hash2(output.outputCommitment, "commitment") !== material.context.outputCommitment)
1799
+ throw new Error("disclosure output commitment mismatch");
1800
+ if (material.kind === "note" ? output.role !== "funds" : output.role !== "owner" && output.role !== "spender")
1801
+ throw new Error("disclosure output role mismatch");
1802
+ const data = material.data;
1803
+ if (material.method === "payload-key") {
1804
+ if (output.role !== data.role || output.ciphertext === void 0)
1805
+ throw new Error("disclosure requires the selected role envelope");
1806
+ const decryption = {
1807
+ ...scoped,
1808
+ outputCommitment: output.outputCommitment,
1809
+ ownerAddress: data.ownerAddress
1810
+ };
1811
+ if (material.kind === "note")
1812
+ return {
1813
+ kind: "note",
1814
+ note: Object.freeze(
1815
+ await decryptNotePayload(
1816
+ data.payloadKey,
1817
+ output.ciphertext,
1818
+ decryption
1819
+ )
1820
+ )
1821
+ };
1822
+ const state = await decryptCreditPayload(
1823
+ data.payloadKey,
1824
+ output.ciphertext,
1825
+ { ...decryption, role: output.role }
1826
+ );
1827
+ return {
1828
+ kind: "credit",
1829
+ note: Object.freeze({
1830
+ ...await creditOpening(state.note),
1831
+ ...state.note.payload === void 0 ? {} : { payload: structuredClone(state.note.payload) }
1832
+ })
1833
+ };
1834
+ }
1835
+ if (material.kind === "note") {
1836
+ const note2 = {
1837
+ ...scoped,
1838
+ ownerAddress: data.ownerAddress,
1839
+ amount: BigInt(data.amount),
1840
+ nonce: BigInt(data.nonce)
1841
+ };
1842
+ if (await commitment(note2) !== material.context.outputCommitment)
1843
+ throw new Error("disclosure opening commitment mismatch");
1844
+ return { kind: "note", note: Object.freeze(note2) };
1845
+ }
1846
+ const note = {
1847
+ ...scoped,
1848
+ spenderWalletIdentity: data.spenderWalletIdentity,
1849
+ ownerWalletIdentity: data.ownerWalletIdentity,
1850
+ revocationShard: BigInt(data.revocationShard),
1851
+ revocationSlot: BigInt(data.revocationSlot),
1852
+ nonce: BigInt(data.nonce),
1853
+ expiry: BigInt(data.expiry),
1854
+ ...normalizeCreditPolicy(
1855
+ Object.fromEntries(
1856
+ ["singleLimit", "dailyLimit", "monthlyLimit"].map((k) => [k, BigInt(data[k])])
1857
+ )
1858
+ ),
1859
+ ...normalizeCreditUsage(
1860
+ Object.fromEntries(
1861
+ ["lastDay", "dailySpent", "lastMonth", "monthlySpent", "totalSpent"].map(
1862
+ (k) => [k, BigInt(data[k])]
1863
+ )
1864
+ )
1865
+ ),
1866
+ availableCredit: BigInt(data.availableCredit),
1867
+ unlimited: data.unlimited === "1",
1868
+ creditSharedKeyHash: data.creditSharedKeyHash
1869
+ };
1870
+ if (await commitment(note) !== material.context.outputCommitment)
1871
+ throw new Error("disclosure opening commitment mismatch");
1872
+ return { kind: "credit", note: Object.freeze(note) };
1873
+ }
1874
+ toJSON() {
1875
+ return { capability: "disclosure", ...this.getScope() };
1876
+ }
1877
+ [/* @__PURE__ */ Symbol.for("nodejs.util.inspect.custom")]() {
1878
+ return this.toJSON();
1879
+ }
1880
+ };
1881
+
1882
+ // src/notes/nullifier.ts
1883
+ async function computeNoteNullifier(chainId, protocolAddress, nonce2, commitment2) {
1884
+ if (chainId < 0n || chainId >= 1n << 64n) throw new RangeError("invalid chainId");
1885
+ if (protocolAddress <= 0n || protocolAddress >= 1n << 160n)
1886
+ throw new RangeError("invalid protocol address");
1887
+ if (nonce2 < 0n || nonce2 >= FIELD_MODULUS) throw new RangeError("invalid note nonce");
1888
+ if (!/^0x[0-9a-fA-F]{64}$/.test(commitment2))
1889
+ throw new Error("invalid note commitment encoding");
1890
+ return poseidonHash([D_NULL, chainId, protocolAddress, nonce2, BigInt(commitment2)]);
1891
+ }
1892
+
1893
+ // src/wallet/capabilities.ts
1894
+ var import_secp256k15 = require("@noble/curves/secp256k1");
1895
+ var import_utils5 = require("@noble/hashes/utils");
1896
+
1897
+ // src/wallet/wallet.ts
1898
+ var import_secp256k14 = require("@noble/curves/secp256k1");
1899
+ var import_hkdf = require("@noble/hashes/hkdf");
1900
+ var import_sha256 = require("@noble/hashes/sha256");
1901
+ var import_sha32 = require("@noble/hashes/sha3");
1902
+ var import_utils4 = require("@noble/hashes/utils");
1903
+ var OPERATIONS = {
1904
+ transact: "Transact",
1905
+ "create-credit": "CreateCredit",
1906
+ "credit-draw": "CreditDraw",
1907
+ "update-credit": "UpdateCredit"
1908
+ };
1909
+ var WALLET_AUTHORIZATION_TYPES = {
1910
+ WalletAuthorization: [
1911
+ { name: "operation", type: "bytes32" },
1912
+ { name: "statementHash", type: "bytes32" }
1913
+ ]
1914
+ };
1915
+ var hex = (value) => `0x${(0, import_utils4.bytesToHex)(value)}`;
1916
+ function bytes2(value, length) {
1917
+ if (typeof value === "string" && !/^0x(?:[0-9a-fA-F]{2})*$/.test(value))
1918
+ throw new Error("invalid hexadecimal bytes");
1919
+ if (typeof value !== "string" && !(value instanceof Uint8Array))
1920
+ throw new TypeError("expected bytes");
1921
+ const result = typeof value === "string" ? (0, import_utils4.hexToBytes)(value.slice(2)) : Uint8Array.from(value);
1922
+ if (length !== void 0 && result.length !== length)
1923
+ throw new Error(`expected ${length} bytes`);
1924
+ return result;
1925
+ }
1926
+ function word(value) {
1927
+ if (value < 0n || value >= 1n << 256n) throw new RangeError("value exceeds uint256");
1928
+ return (0, import_utils4.hexToBytes)(value.toString(16).padStart(64, "0"));
1929
+ }
1930
+ async function computeOwnerAddress(publicKey2) {
1931
+ const point = import_secp256k14.secp256k1.ProjectivePoint.fromHex(bytes2(publicKey2));
1932
+ point.assertValidity();
1933
+ return hex((0, import_sha32.keccak_256)(point.toRawBytes(false).slice(1)).slice(12));
1934
+ }
1935
+ function walletAuthorizationTypedData(request) {
1936
+ const { context: context2, operation, capacity, words } = request;
1937
+ if (!Object.hasOwn(OPERATIONS, operation)) throw new Error("unsupported wallet operation");
1938
+ const allowed = operation === "transact" ? [
1939
+ 1,
1940
+ 2,
1941
+ 3,
1942
+ 4,
1943
+ 5,
1944
+ 6,
1945
+ 7,
1946
+ 8,
1947
+ 9,
1948
+ 10,
1949
+ 20,
1950
+ 30,
1951
+ 40,
1952
+ 50,
1953
+ 60,
1954
+ 70,
1955
+ 80,
1956
+ 90,
1957
+ 100,
1958
+ 110,
1959
+ 120,
1960
+ 130,
1961
+ 140,
1962
+ 150,
1963
+ 160,
1964
+ 170,
1965
+ 180,
1966
+ 190
1967
+ ] : operation === "credit-draw" ? [1, 2, 3, 4, 5, 10, 20, 30, 40, 50, 60, 70, 80] : [1];
1968
+ if (!allowed.includes(capacity)) throw new Error("invalid operation capacity");
1969
+ if (!Array.isArray(words) || words.length < 2) throw new Error("public words required");
1970
+ for (const value of words)
1971
+ if (typeof value !== "bigint" || value < 0n || value >= FIELD_MODULUS)
1972
+ throw new RangeError("noncanonical public field");
1973
+ const { chainId, protocolAddress } = context2;
1974
+ if (typeof chainId !== "bigint" || chainId < 0n || chainId >= 1n << 64n)
1975
+ throw new RangeError("invalid chain id");
1976
+ if (typeof protocolAddress !== "bigint" || protocolAddress <= 0n || protocolAddress >= 1n << 160n)
1977
+ throw new RangeError("invalid protocol address");
1978
+ if (words[0] !== chainId) throw new Error("statement chain mismatch");
1979
+ if (words[1] !== protocolAddress) throw new Error("statement instance mismatch");
1980
+ const statement = (0, import_utils4.concatBytes)(
1981
+ word(BigInt(capacity)),
1982
+ word(64n),
1983
+ word(BigInt(words.length)),
1984
+ ...words.map(word)
1985
+ );
1986
+ return {
1987
+ domain: {
1988
+ name: "InvisibleProtocol",
1989
+ version: "1",
1990
+ chainId,
1991
+ verifyingContract: `0x${protocolAddress.toString(16).padStart(40, "0")}`
1992
+ },
1993
+ types: WALLET_AUTHORIZATION_TYPES,
1994
+ primaryType: "WalletAuthorization",
1995
+ message: {
1996
+ operation: hex(
1997
+ (0, import_sha32.keccak_256)(
1998
+ (0, import_utils4.utf8ToBytes)(
1999
+ `Invisi.${OPERATIONS[operation]}.${operation === "transact" ? "v1" : "v4"}`
2000
+ )
2001
+ )
2002
+ ),
2003
+ statementHash: hex((0, import_sha32.keccak_256)(statement))
2004
+ }
2005
+ };
2006
+ }
2007
+ function computeWalletAuthorizationDigest(request) {
2008
+ const data = walletAuthorizationTypedData(request), hashText = (text) => (0, import_sha32.keccak_256)((0, import_utils4.utf8ToBytes)(text));
2009
+ const domain = (0, import_sha32.keccak_256)(
2010
+ (0, import_utils4.concatBytes)(
2011
+ hashText(
2012
+ "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"
2013
+ ),
2014
+ hashText(data.domain.name),
2015
+ hashText(data.domain.version),
2016
+ word(data.domain.chainId),
2017
+ word(BigInt(data.domain.verifyingContract))
2018
+ )
2019
+ );
2020
+ const structure = (0, import_sha32.keccak_256)(
2021
+ (0, import_utils4.concatBytes)(
2022
+ hashText("WalletAuthorization(bytes32 operation,bytes32 statementHash)"),
2023
+ bytes2(data.message.operation),
2024
+ bytes2(data.message.statementHash)
2025
+ )
2026
+ );
2027
+ return hex((0, import_sha32.keccak_256)((0, import_utils4.concatBytes)(new Uint8Array([25, 1]), domain, structure)));
2028
+ }
2029
+ function signWalletAuthorization(privateKey2, request) {
2030
+ return hex(
2031
+ import_secp256k14.secp256k1.sign(bytes2(computeWalletAuthorizationDigest(request)), bytes2(privateKey2, 32), {
2032
+ lowS: true
2033
+ }).toCompactRawBytes()
2034
+ );
2035
+ }
2036
+ async function verifyWalletAuthorization(expectedIdentity, publicKey2, signature, request) {
2037
+ const raw = bytes2(signature, 64), digest = bytes2(computeWalletAuthorizationDigest(request));
2038
+ if (await computeOwnerAddress(publicKey2) !== expectedIdentity) return false;
2039
+ return import_secp256k14.secp256k1.verify(raw, digest, bytes2(publicKey2), { lowS: true });
2040
+ }
2041
+ async function buildWalletAuthorizationWitness(wallet, request) {
2042
+ const publicKey2 = import_secp256k14.secp256k1.ProjectivePoint.fromHex(bytes2(wallet.spendingPublicKey)).toRawBytes(
2043
+ false
2044
+ );
2045
+ const privateKey2 = bytes2(wallet.spendingPrivateKey, 32), identity2 = wallet.ownerAddress;
2046
+ const statement = { ...request, context: { ...request.context }, words: [...request.words] };
2047
+ try {
2048
+ const derived = import_secp256k14.secp256k1.getPublicKey(privateKey2, false);
2049
+ if (!derived.every((value, i) => value === publicKey2[i]))
2050
+ throw new Error("spending key pair mismatch");
2051
+ if (BigInt(await computeOwnerAddress(publicKey2)) !== BigInt(identity2))
2052
+ throw new Error("owner address mismatch");
2053
+ return {
2054
+ public_key_x: Array.from(publicKey2.slice(1, 33)),
2055
+ public_key_y: Array.from(publicKey2.slice(33)),
2056
+ signature: Array.from(bytes2(signWalletAuthorization(privateKey2, statement), 64))
2057
+ };
2058
+ } finally {
2059
+ privateKey2.fill(0);
2060
+ }
2061
+ }
2062
+
2063
+ // src/wallet/capabilities.ts
2064
+ var copyWitness = (witness) => ({
2065
+ public_key_x: [...witness.public_key_x],
2066
+ public_key_y: [...witness.public_key_y],
2067
+ signature: [...witness.signature]
2068
+ });
2069
+ var inspect = /* @__PURE__ */ Symbol.for("nodejs.util.inspect.custom");
2070
+ function copyBytes(value) {
2071
+ if (typeof value === "string" && !/^0x(?:[0-9a-fA-F]{2})+$/.test(value)) {
2072
+ throw new Error("invalid hexadecimal bytes");
2073
+ }
2074
+ return typeof value === "string" ? (0, import_utils5.hexToBytes)(value.slice(2)) : Uint8Array.from(value);
2075
+ }
2076
+ function identity(value) {
2077
+ return ReceivingAddress.fromIdentity(value).ownerAddress;
2078
+ }
2079
+ var ViewingCapability = class {
2080
+ #key;
2081
+ ownerAddress;
2082
+ constructor(ownerAddress, privateKey2, expectedPublicKey) {
2083
+ this.ownerAddress = identity(ownerAddress);
2084
+ this.#key = copyBytes(privateKey2);
2085
+ const publicKey2 = viewingPublicKey(this.#key);
2086
+ if (expectedPublicKey && (0, import_utils5.bytesToHex)(publicKey2) !== (0, import_utils5.bytesToHex)(expectedPublicKey)) {
2087
+ throw new Error("viewing key pair mismatch");
2088
+ }
2089
+ Object.freeze(this);
2090
+ }
2091
+ decryptNote(envelope, context2) {
2092
+ return decryptPrivateTransferNote(this.#key, envelope, {
2093
+ ...context2,
2094
+ ownerAddress: this.ownerAddress
2095
+ });
2096
+ }
2097
+ decryptCredit(envelope, context2) {
2098
+ return decryptCreditState(this.#key, envelope, {
2099
+ ...context2,
2100
+ ownerAddress: this.ownerAddress
2101
+ });
2102
+ }
2103
+ getPublicKey() {
2104
+ return viewingPublicKey(this.#key);
2105
+ }
2106
+ /** Disclose this authenticated output's payload key, without exporting the wallet viewing key. */
2107
+ discloseNote(envelope, context2) {
2108
+ return Disclosure.fromEncryptedNote(this.#key, envelope, {
2109
+ ...context2,
2110
+ ownerAddress: this.ownerAddress
2111
+ });
2112
+ }
2113
+ /** Exposes the entire Credit envelope; prefer Disclosure.fromCredit() for hash-only opening. */
2114
+ discloseCredit(envelope, context2) {
2115
+ return Disclosure.fromEncryptedCredit(this.#key, envelope, {
2116
+ ...context2,
2117
+ ownerAddress: this.ownerAddress
2118
+ });
2119
+ }
2120
+ /** Explicit sensitive export for delegating viewing. It grants note recovery and owner-only sweep permission. */
2121
+ exportKey() {
2122
+ return Uint8Array.from(this.#key);
2123
+ }
2124
+ toJSON() {
2125
+ return { capability: "viewing", ownerAddress: this.ownerAddress };
2126
+ }
2127
+ [inspect]() {
2128
+ return this.toJSON();
2129
+ }
2130
+ };
2131
+ var Detector = class {
2132
+ #key;
2133
+ #clueKey;
2134
+ constructor(key) {
2135
+ this.#key = Uint8Array.from(key);
2136
+ this.#clueKey = deriveClueKey(this.#key);
2137
+ Object.freeze(this);
2138
+ }
2139
+ getClueKey() {
2140
+ return Uint8Array.from(this.#clueKey);
2141
+ }
2142
+ /** Full detection delegation; the receiver chooses the checked prefix length. */
2143
+ exportKey() {
2144
+ return Uint8Array.from(this.#key);
2145
+ }
2146
+ toJSON() {
2147
+ return { capability: "detection" };
2148
+ }
2149
+ [inspect]() {
2150
+ return this.toJSON();
2151
+ }
2152
+ };
2153
+ var NullifierCapability = class _NullifierCapability {
2154
+ ownerAddress;
2155
+ constructor(ownerAddress) {
2156
+ this.ownerAddress = identity(ownerAddress);
2157
+ Object.freeze(this);
2158
+ }
2159
+ static fromIdentity(ownerAddress) {
2160
+ return new _NullifierCapability(ownerAddress);
2161
+ }
2162
+ compute(chainId, protocolAddress, commitment2, nonce2) {
2163
+ return computeNoteNullifier(chainId, protocolAddress, nonce2, commitment2);
2164
+ }
2165
+ toJSON() {
2166
+ return { capability: "nullifier", ownerAddress: this.ownerAddress };
2167
+ }
2168
+ [inspect]() {
2169
+ return this.toJSON();
2170
+ }
2171
+ };
2172
+ var AuthorizedStatement = class {
2173
+ #witness;
2174
+ digest;
2175
+ ownerAddress;
2176
+ constructor(ownerAddress, digest, witness) {
2177
+ this.ownerAddress = ownerAddress;
2178
+ this.digest = digest;
2179
+ this.#witness = copyWitness(witness);
2180
+ Object.freeze(this);
2181
+ }
2182
+ withWitness(assemble) {
2183
+ return assemble(copyWitness(this.#witness));
2184
+ }
2185
+ toJSON() {
2186
+ return { ownerAddress: this.ownerAddress, digest: this.digest };
2187
+ }
2188
+ [inspect]() {
2189
+ return this.toJSON();
2190
+ }
2191
+ };
2192
+ var WalletAuthorization = class _WalletAuthorization {
2193
+ #privateKey;
2194
+ #publicKey;
2195
+ ownerAddress;
2196
+ constructor(ownerAddress, privateKey2, publicKey2) {
2197
+ this.ownerAddress = ownerAddress;
2198
+ this.#privateKey = privateKey2;
2199
+ this.#publicKey = publicKey2;
2200
+ Object.freeze(this);
2201
+ }
2202
+ static async fromMaterial(ownerAddress, privateKey2) {
2203
+ const privateBytes = copyBytes(privateKey2);
2204
+ const publicBytes = import_secp256k15.secp256k1.getPublicKey(privateBytes, false);
2205
+ if (await computeOwnerAddress(publicBytes) !== identity(ownerAddress)) {
2206
+ throw new Error("owner address mismatch");
2207
+ }
2208
+ return new _WalletAuthorization(identity(ownerAddress), privateBytes, publicBytes);
2209
+ }
2210
+ getPublicKey() {
2211
+ return Uint8Array.from(this.#publicKey);
2212
+ }
2213
+ async authorize(request) {
2214
+ const frozen = { ...request, context: { ...request.context }, words: [...request.words] };
2215
+ const digest = computeWalletAuthorizationDigest(frozen);
2216
+ const witness = await buildWalletAuthorizationWitness(
2217
+ {
2218
+ ownerAddress: this.ownerAddress,
2219
+ spendingPrivateKey: this.#privateKey,
2220
+ spendingPublicKey: this.#publicKey
2221
+ },
2222
+ frozen
2223
+ );
2224
+ return new AuthorizedStatement(this.ownerAddress, digest, witness);
2225
+ }
2226
+ toJSON() {
2227
+ return { capability: "wallet-authorization", ownerAddress: this.ownerAddress };
2228
+ }
2229
+ [inspect]() {
2230
+ return this.toJSON();
2231
+ }
2232
+ };
2233
+
2234
+ // src/wallet/wallet-object.ts
2235
+ var WalletAddress = class {
2236
+ #address;
2237
+ constructor(address) {
2238
+ this.#address = address;
2239
+ }
2240
+ get ownerAddress() {
2241
+ return this.#address.ownerAddress;
2242
+ }
2243
+ get viewingPublicKey() {
2244
+ return this.#address.viewingPublicKey;
2245
+ }
2246
+ get clueKey() {
2247
+ return this.#address.clueKey;
2248
+ }
2249
+ /** Complete 0i receiving address. Owner-only wallets must first resolve their receiving keys. */
2250
+ getAddress() {
2251
+ if (this.#address.kind !== "long") {
2252
+ throw new MissingCapabilityError("resolved receiving keys");
2253
+ }
2254
+ return this.#address.toString();
2255
+ }
2256
+ getReceivingAddress() {
2257
+ return this.#address;
2258
+ }
2259
+ /** Nullifiers derive from public identity and do not grant spending authority. */
2260
+ exportNullifier() {
2261
+ return NullifierCapability.fromIdentity(this.ownerAddress);
2262
+ }
2263
+ toJSON() {
2264
+ return { ownerAddress: this.ownerAddress, address: this.#address.toString() };
2265
+ }
2266
+ [/* @__PURE__ */ Symbol.for("nodejs.util.inspect.custom")]() {
2267
+ return this.toJSON();
2268
+ }
2269
+ };
2270
+ var Wallet = class _Wallet extends WalletAddress {
2271
+ constructor(address) {
2272
+ super(address);
2273
+ Object.freeze(this);
2274
+ }
2275
+ /** Parse a complete 0i address or an EVM owner address without network access. */
2276
+ static fromAddress(address) {
2277
+ if (typeof address !== "string" && !(address instanceof ReceivingAddress)) {
2278
+ throw new TypeError("expected a validated receiving address");
2279
+ }
2280
+ return new _Wallet(typeof address === "string" ? ReceivingAddress.parse(address) : address);
2281
+ }
2282
+ };
2283
+
2284
+ // src/delivery/memo.ts
2285
+ var MAX_MEMO_BYTES = 256;
2286
+ function encodeMemo(memo) {
2287
+ if (memo === void 0) return new Uint8Array();
2288
+ let encoded;
2289
+ if (typeof memo === "string") {
2290
+ for (let i = 0; i < memo.length; i++) {
2291
+ const unit = memo.charCodeAt(i);
2292
+ if (unit >= 55296 && unit <= 56319) {
2293
+ const next = memo.charCodeAt(++i);
2294
+ if (!(next >= 56320 && next <= 57343))
2295
+ throw new Error("memo contains an invalid surrogate");
2296
+ } else if (unit >= 56320 && unit <= 57343)
2297
+ throw new Error("memo contains an invalid surrogate");
2298
+ }
2299
+ encoded = new TextEncoder().encode(memo);
2300
+ } else if (memo instanceof Uint8Array) encoded = Uint8Array.from(memo);
2301
+ else throw new TypeError("memo must be text or Uint8Array");
2302
+ if (encoded.length > MAX_MEMO_BYTES) throw new RangeError("memo exceeds 256 bytes");
2303
+ return encoded;
2304
+ }
2305
+ function decodeMemoText(memo) {
2306
+ return new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(memo);
2307
+ }
2308
+
2309
+ // src/wallet/signing-request.ts
2310
+ var import_ethers3 = require("ethers");
2311
+ function createSigningRequest(data) {
2312
+ const types = Object.fromEntries(
2313
+ Object.entries(data.types).map(([name, fields]) => [name, [...fields]])
2314
+ );
2315
+ const payload = import_ethers3.TypedDataEncoder.getPayload(data.domain, types, data.message);
2316
+ if (payload.primaryType !== data.primaryType) {
2317
+ throw new Error("signing request primary type mismatch");
2318
+ }
2319
+ delete payload.types.EIP712Domain;
2320
+ return structuredClone(payload);
2321
+ }
2322
+
2323
+ // src/delivery/payload.ts
2324
+ function encodeData(data) {
2325
+ if (!data || typeof data !== "object" || Array.isArray(data)) {
2326
+ throw new TypeError("v1 payload data must be an object");
2327
+ }
2328
+ for (const key of Object.keys(data)) {
2329
+ if (key !== "memo") throw new Error(`unsupported v1 payload field ${key}`);
2330
+ }
2331
+ const memo = encodeMemo(data.memo);
2332
+ return new Uint8Array([1, ...memo]);
2333
+ }
2334
+ function readData(value) {
2335
+ if (!(value instanceof Uint8Array) || value.length < 1) {
2336
+ throw new Error("payload version required");
2337
+ }
2338
+ if (value[0] !== 1) throw new Error("unsupported payload version");
2339
+ const memo = Uint8Array.from(value.subarray(1));
2340
+ if (memo.length > 256) throw new Error("invalid memo length");
2341
+ return memo.length === 0 ? {} : { memo };
2342
+ }
2343
+ var fundsV1 = Object.freeze({
2344
+ encode(data = {}) {
2345
+ return encodeData(data);
2346
+ },
2347
+ decode(value) {
2348
+ return readData(value);
2349
+ }
2350
+ });
2351
+ var creditV1 = Object.freeze({
2352
+ encode(data = {}) {
2353
+ return encodeData(data);
2354
+ },
2355
+ decode(value) {
2356
+ return readData(value);
2357
+ }
2358
+ });
2359
+
2360
+ // src/delivery/decryptor.ts
2361
+ var import_utils6 = require("@noble/hashes/utils");
2362
+ var DeliveryDecryptionError = class extends Error {
2363
+ constructor(cause) {
2364
+ super(cause instanceof Error ? cause.message : "delivery decryption failed", { cause });
2365
+ this.name = "DeliveryDecryptionError";
2366
+ }
2367
+ };
2368
+ function checkedPlaintext(plaintext, codec) {
2369
+ if (!(plaintext instanceof Uint8Array)) throw new Error("invalid decrypted plaintext");
2370
+ if (plaintext.length + 78 < codec.minimum || plaintext.length + 78 > codec.maximum)
2371
+ throw new Error("invalid plaintext length");
2372
+ return plaintext.slice();
2373
+ }
2374
+ function authenticatedDecryptor(wallet, decryptor) {
2375
+ const ownerAddress = wallet.ownerAddress;
2376
+ return {
2377
+ ownerAddress,
2378
+ async detectCandidates(candidates) {
2379
+ if (!decryptor.detect) return candidates.map(() => "candidate");
2380
+ const result = await decryptor.detect(structuredClone(candidates));
2381
+ if (!result || BigInt(result.ownerAddress) !== BigInt(ownerAddress) || !Array.isArray(result.results) || result.results.length !== candidates.length || result.results.some(
2382
+ (value) => !["candidate", "not-recipient", "unresolved"].includes(value)
2383
+ ))
2384
+ throw new DeliveryDecryptionError(
2385
+ new Error("invalid detection batch or wrong account")
2386
+ );
2387
+ return [...result.results];
2388
+ },
2389
+ async decryptNote(envelope, context2) {
2390
+ context2 = { ...context2 };
2391
+ try {
2392
+ const bytes3 = validateDeliveryCiphertext(envelope, "funds");
2393
+ const full = { ...context2, ownerAddress };
2394
+ const plaintext = await decryptor.decryptNote(bytes3.slice(), { ...full });
2395
+ if (plaintext === null) return null;
2396
+ if (!(plaintext instanceof Uint8Array)) {
2397
+ if (plaintext?.status === "authentication-failed")
2398
+ throw new DeliveryAuthenticationError();
2399
+ throw new Error("invalid decryption result");
2400
+ }
2401
+ const codec = deliveryCodec(bytes3[0]);
2402
+ if (codec.kind !== "funds") throw new Error("unexpected envelope kind");
2403
+ const note = codec.decode(checkedPlaintext(plaintext, codec), full);
2404
+ if (BigInt(note.ownerAddress) !== BigInt(ownerAddress) || note.tokenAddress !== context2.tokenAddress)
2405
+ throw new Error("decrypted note context mismatch");
2406
+ await noteCommitment(full, note);
2407
+ return note;
2408
+ } catch (cause) {
2409
+ if (cause instanceof DeliveryAuthenticationError) throw cause;
2410
+ throw new DeliveryDecryptionError(cause);
2411
+ }
2412
+ },
2413
+ async decryptCredit(envelope, context2) {
2414
+ context2 = { ...context2 };
2415
+ try {
2416
+ const bytes3 = validateDeliveryCiphertext(envelope, "credit");
2417
+ const full = { ...context2, ownerAddress };
2418
+ const plaintext = await decryptor.decryptCredit(bytes3.slice(), { ...full });
2419
+ if (plaintext === null) return null;
2420
+ if (!(plaintext instanceof Uint8Array)) {
2421
+ if (plaintext?.status === "authentication-failed")
2422
+ throw new DeliveryAuthenticationError();
2423
+ throw new Error("invalid decryption result");
2424
+ }
2425
+ const codec = deliveryCodec(bytes3[0]);
2426
+ if (codec.kind !== "credit") throw new Error("unexpected envelope kind");
2427
+ const state = codec.decode(checkedPlaintext(plaintext, codec), full);
2428
+ const identity2 = context2.role === "owner" ? state.note.ownerWalletIdentity : state.note.spenderWalletIdentity;
2429
+ if (BigInt(identity2) !== BigInt(ownerAddress) || state.note.tokenAddress !== context2.tokenAddress)
2430
+ throw new Error("decrypted credit context mismatch");
2431
+ if (wallet.getReceivingAddress().kind === "long") {
2432
+ const keys = context2.role === "owner" ? state.ownerReceivingKeys : state.spenderReceivingKeys;
2433
+ if ((0, import_utils6.bytesToHex)(keys.viewingPublicKey) !== (0, import_utils6.bytesToHex)(wallet.viewingPublicKey))
2434
+ throw new Error("decrypted credit receiving key mismatch");
2435
+ }
2436
+ await creditCommitment(full, state.note);
2437
+ return state;
2438
+ } catch (cause) {
2439
+ if (cause instanceof DeliveryAuthenticationError) throw cause;
2440
+ throw new DeliveryDecryptionError(cause);
2441
+ }
2442
+ }
2443
+ };
2444
+ }
2445
+
2446
+ // src/wallet/wallet-wire.ts
2447
+ var import_utils7 = require("@noble/hashes/utils");
2448
+ var walletHex = (bytes3) => `0x${(0, import_utils7.bytesToHex)(bytes3)}`;
2449
+ function walletBytes(value, length) {
2450
+ if (typeof value !== "string" || !/^0x(?:[0-9a-f]{2})*$/.test(value))
2451
+ throw new Error("invalid canonical hex bytes");
2452
+ const bytes3 = (0, import_utils7.hexToBytes)(value.slice(2));
2453
+ if (length !== void 0 && bytes3.length !== length) throw new Error("invalid byte length");
2454
+ return bytes3;
2455
+ }
2456
+ function walletInteger(value) {
2457
+ if (typeof value !== "string" || !/^(0|[1-9][0-9]*)$/.test(value) || value.length > 78)
2458
+ throw new Error("invalid canonical integer");
2459
+ return BigInt(value);
2460
+ }
2461
+ function walletAuthorizationParams(params) {
2462
+ return {
2463
+ ...params,
2464
+ words: params.words.map(walletInteger),
2465
+ context: {
2466
+ chainId: walletInteger(params.context.chainId),
2467
+ protocolAddress: walletInteger(params.context.protocolAddress)
2468
+ }
2469
+ };
2470
+ }
2471
+ function walletDecryptionContext(request) {
2472
+ const context2 = request.params.context;
2473
+ return {
2474
+ chainId: walletInteger(context2.chainId),
2475
+ protocolAddress: walletInteger(context2.protocolAddress),
2476
+ tokenAddress: walletInteger(context2.tokenAddress),
2477
+ outputCommitment: context2.outputCommitment,
2478
+ ownerAddress: request.ownerAddress,
2479
+ ...context2.role ? { role: context2.role } : {}
2480
+ };
2481
+ }
2482
+ function freeze(value) {
2483
+ if (value && typeof value === "object") {
2484
+ Object.values(value).forEach(freeze);
2485
+ Object.freeze(value);
2486
+ }
2487
+ return value;
2488
+ }
2489
+ function exactKeys(value, keys) {
2490
+ if (!value || typeof value !== "object" || Array.isArray(value) || Object.keys(value).some((key) => !keys.includes(key)) || keys.some((key) => !Object.hasOwn(value, key)))
2491
+ throw new Error("unexpected wallet fields");
2492
+ }
2493
+ function validateWalletRequest(request) {
2494
+ exactKeys(request, ["version", "id", "ownerAddress", "method", "params"]);
2495
+ if (request.version !== 1 || typeof request.id !== "string" || !/^[\x21-\x7e]{1,128}$/.test(request.id))
2496
+ throw new Error("invalid wallet request envelope");
2497
+ walletBytes(request.ownerAddress, 20);
2498
+ ReceivingAddress.fromIdentity(request.ownerAddress);
2499
+ switch (request.method) {
2500
+ case "wallet.public":
2501
+ exactKeys(request.params, []);
2502
+ break;
2503
+ case "wallet.detect": {
2504
+ exactKeys(request.params, ["clues"]);
2505
+ const { clues } = request.params;
2506
+ if (!Array.isArray(clues) || clues.length > 1024) throw new Error("invalid clues");
2507
+ clues.forEach((clue) => walletBytes(clue, 68));
2508
+ break;
2509
+ }
2510
+ case "wallet.authorize": {
2511
+ exactKeys(request.params, ["operation", "capacity", "words", "context"]);
2512
+ const params = request.params;
2513
+ exactKeys(params.context, ["chainId", "protocolAddress"]);
2514
+ if (!Array.isArray(params.words) || params.words.length > 8192)
2515
+ throw new Error("invalid authorization words");
2516
+ computeWalletAuthorizationDigest(walletAuthorizationParams(params));
2517
+ break;
2518
+ }
2519
+ case "wallet.decryptNote":
2520
+ case "wallet.decryptCredit": {
2521
+ const typed = request;
2522
+ exactKeys(typed.params, ["envelope", "context"]);
2523
+ exactKeys(typed.params.context, [
2524
+ "chainId",
2525
+ "protocolAddress",
2526
+ "tokenAddress",
2527
+ "outputCommitment",
2528
+ ...request.method === "wallet.decryptCredit" ? ["role"] : []
2529
+ ]);
2530
+ const context2 = walletDecryptionContext(typed);
2531
+ walletBytes(context2.outputCommitment, 32);
2532
+ validateContext(context2, context2.tokenAddress);
2533
+ if (request.method === "wallet.decryptCredit" && context2.role !== "owner" && context2.role !== "spender")
2534
+ throw new Error("invalid credit role");
2535
+ if (request.method === "wallet.decryptNote" && context2.role !== void 0)
2536
+ throw new Error("unexpected note role");
2537
+ walletBytes(typed.params.envelope);
2538
+ validateDeliveryCiphertext(
2539
+ typed.params.envelope,
2540
+ request.method === "wallet.decryptCredit" ? "credit" : "funds"
2541
+ );
2542
+ break;
2543
+ }
2544
+ default:
2545
+ throw new Error("unsupported wallet method");
2546
+ }
2547
+ }
2548
+ var retainedWallets = /* @__PURE__ */ new WeakMap();
2549
+ function prepareWalletRequest(wallet, id, method, params) {
2550
+ if (!(wallet instanceof Wallet)) throw new TypeError("public Wallet required");
2551
+ const request = structuredClone({
2552
+ version: 1,
2553
+ id,
2554
+ ownerAddress: wallet.ownerAddress.toLowerCase(),
2555
+ method,
2556
+ params
2557
+ });
2558
+ validateWalletRequest(request);
2559
+ const prepared = freeze({ request });
2560
+ retainedWallets.set(prepared, wallet);
2561
+ return prepared;
2562
+ }
2563
+ async function acceptWalletResponse(prepared, response) {
2564
+ return acceptResponse(prepared, response);
2565
+ }
2566
+ async function acceptResponse(prepared, response) {
2567
+ const wallet = retainedWallets.get(prepared);
2568
+ if (!wallet) throw new Error("wallet request was not prepared by this SDK instance");
2569
+ const request = prepared.request;
2570
+ validateWalletRequest(request);
2571
+ const safe = structuredClone(response);
2572
+ if (safe.version !== request.version || safe.id !== request.id || safe.ownerAddress !== request.ownerAddress || safe.method !== request.method)
2573
+ throw new Error("wallet response envelope mismatch");
2574
+ validateWalletResponses([request], [safe]);
2575
+ if ("error" in safe) throw new NativeWalletError(safe.error.code);
2576
+ const result = safe.result;
2577
+ exactKeys(
2578
+ result,
2579
+ request.method === "wallet.public" ? ["viewingPublicKey", "spendingPublicKey", "clueKey"] : request.method === "wallet.authorize" ? ["spendingPublicKey", "signature", "digest"] : request.method === "wallet.detect" ? ["matches"] : ["plaintext"]
2580
+ );
2581
+ if (request.method === "wallet.public") {
2582
+ walletBytes(result.spendingPublicKey, 65);
2583
+ walletBytes(result.viewingPublicKey, 33);
2584
+ walletBytes(result.clueKey, 32);
2585
+ if (await computeOwnerAddress(result.spendingPublicKey) !== request.ownerAddress)
2586
+ throw new Error("wallet public identity mismatch");
2587
+ if (wallet.getReceivingAddress().kind === "long" && (walletHex(wallet.viewingPublicKey) !== result.viewingPublicKey || walletHex(wallet.clueKey) !== result.clueKey))
2588
+ throw new Error("wallet receiving key mismatch");
2589
+ ReceivingAddress.from({
2590
+ ownerAddress: request.ownerAddress,
2591
+ viewingPublicKey: walletBytes(result.viewingPublicKey),
2592
+ clueKey: walletBytes(result.clueKey)
2593
+ });
2594
+ } else if (request.method === "wallet.authorize") {
2595
+ const auth = walletAuthorizationParams(request.params);
2596
+ if (result.digest !== computeWalletAuthorizationDigest(auth) || !await verifyWalletAuthorization(
2597
+ request.ownerAddress,
2598
+ result.spendingPublicKey,
2599
+ result.signature,
2600
+ auth
2601
+ ))
2602
+ throw new Error("invalid wallet authorization response");
2603
+ const publicKey2 = walletBytes(result.spendingPublicKey, 65);
2604
+ return new AuthorizedStatement(request.ownerAddress, result.digest, {
2605
+ public_key_x: Array.from(publicKey2.slice(1, 33)),
2606
+ public_key_y: Array.from(publicKey2.slice(33)),
2607
+ signature: Array.from(walletBytes(result.signature, 64))
2608
+ });
2609
+ } else if (request.method === "wallet.detect") {
2610
+ if (!Array.isArray(result.matches) || result.matches.length !== request.params.clues.length || result.matches.some((v) => typeof v !== "boolean"))
2611
+ throw new Error("invalid detection response");
2612
+ } else {
2613
+ const typed = request;
2614
+ const context2 = walletDecryptionContext(typed);
2615
+ const plaintext = walletBytes(result.plaintext);
2616
+ const viewing = authenticatedDecryptor(wallet, {
2617
+ decryptNote: async () => plaintext,
2618
+ decryptCredit: async () => plaintext
2619
+ });
2620
+ return typed.method === "wallet.decryptNote" ? viewing.decryptNote(typed.params.envelope, context2) : viewing.decryptCredit(typed.params.envelope, context2);
2621
+ }
2622
+ return result;
2623
+ }
2624
+ var NativeWalletError = class extends Error {
2625
+ constructor(code) {
2626
+ super(code);
2627
+ this.code = code;
2628
+ this.name = "NativeWalletError";
2629
+ }
2630
+ };
2631
+ var MAX_WALLET_BATCH_BYTES = 16 * 1024 * 1024;
2632
+ function boundedJson(value) {
2633
+ const json = JSON.stringify(value);
2634
+ if (typeof json !== "string" || new TextEncoder().encode(json).length > MAX_WALLET_BATCH_BYTES)
2635
+ throw new Error("wallet batch exceeds encoded size limit");
2636
+ }
2637
+ function validateWalletBatch(requests) {
2638
+ if (!Array.isArray(requests) || requests.length > 1024) throw new Error("invalid wallet batch");
2639
+ boundedJson(requests);
2640
+ const ids = /* @__PURE__ */ new Set();
2641
+ for (const request of requests) {
2642
+ validateWalletRequest(request);
2643
+ if (ids.has(request.id)) throw new Error("duplicate wallet request id");
2644
+ if (request.ownerAddress !== requests[0].ownerAddress)
2645
+ throw new Error("mixed wallet batch owners");
2646
+ ids.add(request.id);
2647
+ }
2648
+ }
2649
+ function validateWalletResponses(requests, responses) {
2650
+ validateWalletBatch(requests);
2651
+ if (!Array.isArray(responses) || responses.length !== requests.length)
2652
+ throw new Error("wallet response batch length mismatch");
2653
+ boundedJson(responses);
2654
+ const expected = new Map(requests.map((request) => [request.id, request]));
2655
+ const found = /* @__PURE__ */ new Map();
2656
+ for (const response of responses) {
2657
+ if (!response || typeof response !== "object") throw new Error("invalid wallet response");
2658
+ const request = expected.get(response.id);
2659
+ if (!request || found.has(response.id) || response.version !== 1 || response.method !== request.method || response.ownerAddress !== request.ownerAddress)
2660
+ throw new Error("wallet response envelope mismatch");
2661
+ if (Object.hasOwn(response, "error")) {
2662
+ exactKeys(response, ["version", "id", "ownerAddress", "method", "error"]);
2663
+ exactKeys(response.error, ["code"]);
2664
+ if (typeof response.error?.code !== "string" || !/^[A-Z][A-Z0-9_]{0,127}$/.test(response.error.code))
2665
+ throw new Error("invalid native wallet error");
2666
+ } else {
2667
+ exactKeys(response, ["version", "id", "ownerAddress", "method", "result"]);
2668
+ if (!response.result || typeof response.result !== "object" || Array.isArray(response.result))
2669
+ throw new Error("invalid wallet result");
2670
+ const result = response.result;
2671
+ exactKeys(
2672
+ result,
2673
+ request.method === "wallet.public" ? ["viewingPublicKey", "spendingPublicKey", "clueKey"] : request.method === "wallet.authorize" ? ["spendingPublicKey", "signature", "digest"] : request.method === "wallet.detect" ? ["matches"] : ["plaintext"]
2674
+ );
2675
+ if (request.method === "wallet.public") {
2676
+ walletBytes(result.viewingPublicKey, 33);
2677
+ walletBytes(result.spendingPublicKey, 65);
2678
+ walletBytes(result.clueKey, 32);
2679
+ } else if (request.method === "wallet.authorize") {
2680
+ walletBytes(result.spendingPublicKey, 65);
2681
+ walletBytes(result.signature, 64);
2682
+ walletBytes(result.digest, 32);
2683
+ } else if (request.method === "wallet.detect") {
2684
+ if (!Array.isArray(result.matches) || result.matches.length !== request.params.clues.length || result.matches.some((value) => typeof value !== "boolean"))
2685
+ throw new Error("invalid detection response");
2686
+ } else {
2687
+ walletBytes(result.plaintext);
2688
+ }
2689
+ }
2690
+ found.set(response.id, response);
2691
+ }
2692
+ return requests.map((request) => found.get(request.id));
2693
+ }
2694
+ async function acceptWalletResponses(prepared, responses) {
2695
+ for (const item of prepared) {
2696
+ if (!retainedWallets.has(item))
2697
+ throw new Error("wallet request was not prepared by this SDK instance");
2698
+ }
2699
+ const ordered = validateWalletResponses(
2700
+ prepared.map((item) => item.request),
2701
+ structuredClone(responses)
2702
+ );
2703
+ return Promise.all(prepared.map((item, index) => acceptWalletResponse(item, ordered[index])));
2704
+ }
2705
+
2706
+ // src/provenance/intent-hash.ts
2707
+ var import_ethers4 = require("ethers");
2708
+ function provenanceIntentHash(originalRequestHash) {
2709
+ return (0, import_ethers4.keccak256)(
2710
+ import_ethers4.AbiCoder.defaultAbiCoder().encode(
2711
+ ["string", "bytes32"],
2712
+ ["InvisiBroadcasterProvenanceIntentV1", (0, import_ethers4.toBeHex)(BigInt(originalRequestHash), 32)]
2713
+ )
2714
+ );
2715
+ }
2716
+ // Annotate the CommonJS export names for ESM import in node:
2717
+ 0 && (module.exports = {
2718
+ AuthorizedStatement,
2719
+ CREDIT_POLICY_FIELDS,
2720
+ CREDIT_USAGE_FIELDS,
2721
+ D_CNULL,
2722
+ D_CREDIT,
2723
+ D_CREDIT_KEY_HASH,
2724
+ D_CREDIT_RECOVERY,
2725
+ D_CREDIT_STATE_NONCE,
2726
+ D_IDENT,
2727
+ D_NOTE,
2728
+ D_NULL,
2729
+ DeliveryAuthenticationError,
2730
+ DeliveryDecryptionError,
2731
+ Detector,
2732
+ Disclosure,
2733
+ FIELD_MODULUS,
2734
+ MAX_CIPHERTEXT_BYTES,
2735
+ MAX_MEMO_BYTES,
2736
+ MAX_WALLET_BATCH_BYTES,
2737
+ MissingCapabilityError,
2738
+ NativeWalletError,
2739
+ NullifierCapability,
2740
+ ReceivingAddress,
2741
+ TREE_DEPTH,
2742
+ ViewingCapability,
2743
+ WALLET_AUTHORIZATION_TYPES,
2744
+ Wallet,
2745
+ acceptWalletResponse,
2746
+ acceptWalletResponses,
2747
+ applyCreditDrawUsage,
2748
+ authenticatedDecryptor,
2749
+ computeCreditCommitment,
2750
+ computeCreditNullifier,
2751
+ computeCreditRecoveryPad,
2752
+ computeCreditSharedKeyHash,
2753
+ computeNoteCommitment,
2754
+ computeNoteIdentity,
2755
+ computeNoteNullifier,
2756
+ computeOwnerAddress,
2757
+ computeWalletAuthorizationDigest,
2758
+ createClue,
2759
+ createClueFromSeed,
2760
+ createSigningRequest,
2761
+ creditPeriods,
2762
+ creditV1,
2763
+ decodeAddress,
2764
+ decodeDeliveryHeader,
2765
+ decodeMemoText,
2766
+ decryptCreditState,
2767
+ decryptPrivateTransferNote,
2768
+ deliveryCodec,
2769
+ deliveryHeader,
2770
+ deriveClueKey,
2771
+ deriveCreditState,
2772
+ deriveCreditStateNonce,
2773
+ deriveCreditUpdate,
2774
+ encodeMemo,
2775
+ encryptCreditState,
2776
+ encryptPrivateTransferNote,
2777
+ fundsV1,
2778
+ generateCreditSharedKey,
2779
+ generateNonce,
2780
+ normalizeCreditDeliveryState,
2781
+ normalizeCreditPolicy,
2782
+ normalizeCreditState,
2783
+ normalizeCreditUsage,
2784
+ poseidonHash,
2785
+ prepareWalletRequest,
2786
+ provenanceIntentHash,
2787
+ recoverCreditState,
2788
+ validateClueKey,
2789
+ validateDeliveryCiphertext,
2790
+ validateWalletBatch,
2791
+ validateWalletResponses,
2792
+ verifyWalletAuthorization,
2793
+ viewingPublicKey,
2794
+ walletAuthorizationTypedData
2795
+ });
2796
+ //# sourceMappingURL=index.cjs.map