@invisi-labs/sdk 1.1.5 → 1.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. package/dist/broadcaster-preparation-DMxxZ_1Q.d.cts +587 -0
  2. package/dist/broadcaster-preparation-DMxxZ_1Q.d.ts +587 -0
  3. package/dist/broadcaster-preparation-RMWQFYNL.js +11 -0
  4. package/dist/broadcaster-preparation-UGOTO4SX.cjs +11 -0
  5. package/dist/broadcaster-preparation-UGOTO4SX.cjs.map +1 -0
  6. package/dist/chunk-7F7ZZW3L.cjs +374 -0
  7. package/dist/chunk-7F7ZZW3L.cjs.map +1 -0
  8. package/dist/chunk-EHP5TBC6.js +3565 -0
  9. package/dist/chunk-EHP5TBC6.js.map +1 -0
  10. package/dist/chunk-GTPUYPQL.js +128 -0
  11. package/dist/chunk-GTPUYPQL.js.map +1 -0
  12. package/dist/chunk-ITTGBIG2.js +1877 -0
  13. package/dist/chunk-ITTGBIG2.js.map +1 -0
  14. package/dist/chunk-J2JH6HIG.cjs +1877 -0
  15. package/dist/chunk-J2JH6HIG.cjs.map +1 -0
  16. package/dist/chunk-LOU7AP2B.cjs +128 -0
  17. package/dist/chunk-LOU7AP2B.cjs.map +1 -0
  18. package/dist/chunk-RHNIHI4P.cjs +3565 -0
  19. package/dist/chunk-RHNIHI4P.cjs.map +1 -0
  20. package/dist/{chunk-4HMM7ZKY.js → chunk-XHKQ2BO2.js} +19 -19
  21. package/dist/chunk-XHKQ2BO2.js.map +1 -0
  22. package/dist/ethers.cjs +11 -385
  23. package/dist/ethers.cjs.map +1 -1
  24. package/dist/ethers.d.cts +4 -5
  25. package/dist/ethers.d.ts +4 -5
  26. package/dist/ethers.js +1 -1
  27. package/dist/index.cjs +441 -6217
  28. package/dist/index.cjs.map +1 -1
  29. package/dist/index.d.cts +109 -491
  30. package/dist/index.d.ts +109 -491
  31. package/dist/index.js +518 -599
  32. package/dist/index.js.map +1 -1
  33. package/dist/receipt-logs-CvC951p7.d.cts +318 -0
  34. package/dist/receipt-logs-m0-bhaxW.d.ts +318 -0
  35. package/dist/session-support.cjs +168 -0
  36. package/dist/session-support.cjs.map +1 -0
  37. package/dist/session-support.d.cts +252 -0
  38. package/dist/session-support.d.ts +252 -0
  39. package/dist/session-support.js +168 -0
  40. package/dist/session-support.js.map +1 -0
  41. package/package.json +10 -20
  42. package/dist/chunk-4HMM7ZKY.js.map +0 -1
  43. package/dist/chunk-BE52B32Z.js +0 -5490
  44. package/dist/chunk-BE52B32Z.js.map +0 -1
  45. package/dist/indexeddb.cjs +0 -5636
  46. package/dist/indexeddb.cjs.map +0 -1
  47. package/dist/indexeddb.d.cts +0 -20
  48. package/dist/indexeddb.d.ts +0 -20
  49. package/dist/indexeddb.js +0 -70
  50. package/dist/indexeddb.js.map +0 -1
  51. package/dist/memory.cjs +0 -5644
  52. package/dist/memory.cjs.map +0 -1
  53. package/dist/memory.d.cts +0 -12
  54. package/dist/memory.d.ts +0 -12
  55. package/dist/memory.js +0 -10
  56. package/dist/operation-types-D7I405FX.d.ts +0 -275
  57. package/dist/operation-types-DIazgS2d.d.cts +0 -275
  58. package/dist/sqlite.cjs +0 -5620
  59. package/dist/sqlite.cjs.map +0 -1
  60. package/dist/sqlite.d.cts +0 -28
  61. package/dist/sqlite.d.ts +0 -28
  62. package/dist/sqlite.js +0 -54
  63. package/dist/sqlite.js.map +0 -1
  64. package/dist/state-AUnz7TG7.d.cts +0 -78
  65. package/dist/state-AUnz7TG7.d.ts +0 -78
  66. package/dist/store-BMN8kd0c.d.ts +0 -42
  67. package/dist/store-Cz75STRl.d.cts +0 -42
  68. /package/dist/{memory.js.map → broadcaster-preparation-RMWQFYNL.js.map} +0 -0
package/dist/index.js CHANGED
@@ -1,694 +1,613 @@
1
1
  import {
2
- DisclosureVerifier,
3
- InvisiError,
4
- Operation,
5
- TransactionPreparation,
6
- WalletSession,
7
- acceptFundingSignature,
8
- acceptWalletSignature,
9
- copyGasBenchmarks,
2
+ AddressRegistry,
3
+ BroadcasterClient,
4
+ BroadcasterRpcError,
5
+ Invisi,
6
+ PreparedTransaction,
7
+ TransactionOutput,
8
+ broadcasterQuoteDomain,
9
+ broadcasterQuoteTypes,
10
+ broadcasterRequestHash,
11
+ broadcasterTransactInterface,
12
+ decodeBroadcasterIntent,
10
13
  decryptCandidates,
11
14
  defineGasBenchmarks,
12
- deserializeState,
13
- encodeOperation,
14
- estimateGasReference,
15
- fundingSigningRequest,
16
- operationPreview,
17
- positiveAmount,
18
- prepareWithErrors,
19
- proveOperation,
20
- publicProof,
15
+ extractDeliveryCandidates,
21
16
  readProtocolLogs,
22
- requiresAuthorization,
23
- sameStatement,
24
- serializeState,
25
- statement,
26
- walletBalance,
27
- walletSigningRequest
28
- } from "./chunk-BE52B32Z.js";
17
+ verifyBroadcasterQuote
18
+ } from "./chunk-EHP5TBC6.js";
29
19
  import {
30
- EthersAddressResolver,
31
- EthersChainReader,
32
- EthersFundingVerifier
33
- } from "./chunk-4HMM7ZKY.js";
20
+ InvisiError,
21
+ NoteOutput,
22
+ decodePaymentNote,
23
+ decodePaymentRequest,
24
+ encodePaymentNote,
25
+ encodePaymentRequest
26
+ } from "./chunk-ITTGBIG2.js";
27
+ import {
28
+ EthersChainReader
29
+ } from "./chunk-XHKQ2BO2.js";
34
30
 
35
- // src/explicit-preparation.ts
36
- import { getAddress } from "ethers";
31
+ // src/index.ts
32
+ import {
33
+ Detector,
34
+ Disclosure,
35
+ MAX_MEMO_BYTES,
36
+ MissingCapabilityError,
37
+ NullifierCapability,
38
+ PublicWallet,
39
+ ViewingCapability,
40
+ Wallet as Wallet2,
41
+ WalletAuthorization,
42
+ computeCreditNullifier,
43
+ creditPeriods,
44
+ creditV1,
45
+ decodeDeliveryHeader,
46
+ decodeMemoText,
47
+ encodeMemo,
48
+ fundsV1
49
+ } from "@invisi-labs/core";
50
+ import { RegistrationOperation } from "@invisi-labs/protocol";
37
51
 
38
- // src/PreparedTransaction.ts
39
- import { MissingCapabilityError } from "@invisi-labs/core";
40
- import { normalizeFunding } from "@invisi-labs/protocol";
41
- function check(ok, message) {
42
- if (!ok) throw new Error(message);
52
+ // src/disclosure/DisclosureVerifier.ts
53
+ function check(value, message) {
54
+ if (!value) throw new Error(message);
43
55
  }
44
- var PreparedTransaction = class _PreparedTransaction {
45
- #runtime;
46
- #saved;
47
- #authorization;
48
- #lifecycle;
49
- status;
50
- constructor(runtime, saved, status, lifecycle, authorization) {
51
- this.#runtime = runtime;
52
- this.#saved = structuredClone(saved);
53
- this.#lifecycle = lifecycle;
54
- this.#authorization = authorization;
55
- this.status = status;
56
- Object.freeze(this);
57
- }
58
- static prepare(runtime, plan) {
59
- return new _PreparedTransaction(
60
- runtime,
61
- { plan: structuredClone(plan), generation: 0 },
62
- "prepared",
63
- { generation: 0, busy: false }
64
- );
65
- }
66
- get id() {
67
- return this.#saved.plan.id;
68
- }
69
- get kind() {
70
- return this.#saved.plan.kind;
71
- }
72
- estimateGas() {
73
- return estimateGasReference(
74
- this.#saved.plan,
75
- this.#runtime.options.protocol,
76
- this.#runtime.options.gasBenchmarks
77
- );
78
- }
79
- preview() {
80
- return operationPreview(this.#saved.plan, this.status);
81
- }
82
- current() {
56
+ function reference(value) {
57
+ check(/^0x[0-9a-fA-F]{64}$/.test(value.transactionHash), "invalid disclosure transaction hash");
58
+ check(
59
+ typeof value.treeId === "bigint" && value.treeId > 0n && value.treeId < 1n << 64n,
60
+ "invalid disclosure tree ID"
61
+ );
62
+ check(
63
+ Number.isSafeInteger(value.leafIndex) && value.leafIndex >= 0 && value.leafIndex < 65536,
64
+ "invalid disclosure leaf index"
65
+ );
66
+ check(["funds", "owner", "spender"].includes(value.role), "invalid disclosure reference role");
67
+ return {
68
+ transactionHash: value.transactionHash.toLowerCase(),
69
+ treeId: value.treeId,
70
+ leafIndex: value.leafIndex,
71
+ role: value.role
72
+ };
73
+ }
74
+ var DisclosureVerifier = class {
75
+ #protocol;
76
+ #reader;
77
+ #confirmations;
78
+ constructor(options) {
79
+ const confirmations = options.confirmations ?? 1;
83
80
  check(
84
- !this.#lifecycle.busy && this.#lifecycle.generation === this.#saved.generation,
85
- "stale transaction handle or active worker"
81
+ Number.isSafeInteger(confirmations) && confirmations >= 1,
82
+ "invalid disclosure confirmations"
86
83
  );
84
+ this.#protocol = options.protocol;
85
+ this.#reader = "provider" in options ? new EthersChainReader(options.provider) : options.reader;
86
+ this.#confirmations = BigInt(confirmations);
87
+ Object.freeze(this);
87
88
  }
88
- async stage(stage, allowed, work, signal) {
89
- signal?.throwIfAborted();
90
- this.current();
91
- check(allowed.includes(this.status), `invalid transaction stage for ${stage}`);
92
- this.#lifecycle.busy = true;
93
- const saved = structuredClone(this.#saved);
94
- try {
95
- const next = await work(saved);
96
- signal?.throwIfAborted();
97
- saved.generation++;
98
- this.#lifecycle.generation = saved.generation;
99
- return new _PreparedTransaction(
100
- this.#runtime,
101
- saved,
102
- next.status,
103
- this.#lifecycle,
104
- next.authorization ?? this.#authorization
105
- );
106
- } finally {
107
- this.#lifecycle.busy = false;
108
- }
109
- }
110
- /** Public EIP-712 request; signing transport and key custody belong to the caller. */
111
- async exportWalletAuthorization(options = {}) {
112
- options.signal?.throwIfAborted();
113
- this.current();
89
+ async barrier(block) {
90
+ await this.#protocol.validateDeployment(this.#reader);
91
+ const canonical = await this.#reader.getBlock(block.number);
114
92
  check(
115
- requiresAuthorization(this.#saved.plan),
116
- "operation does not require wallet authorization"
93
+ canonical.number === block.number && canonical.hash.toLowerCase() === block.hash.toLowerCase(),
94
+ "disclosure canonical block changed; reorg verification required"
117
95
  );
96
+ const head = await this.#reader.getLatestBlockNumber();
118
97
  check(
119
- ["prepared", "authorized"].includes(this.status),
120
- "operation is not awaiting wallet authorization"
98
+ head >= block.number && head - block.number + 1n >= this.#confirmations,
99
+ "insufficient disclosure confirmations"
121
100
  );
122
- return walletSigningRequest(this.#saved.plan);
123
- }
124
- /** Attach an external EOA signature, retaining its witness only at the private proving boundary. */
125
- async withWalletSignature(signature, options = {}) {
126
- options.signal?.throwIfAborted();
127
- this.current();
128
101
  check(
129
- requiresAuthorization(this.#saved.plan),
130
- "operation does not require wallet authorization"
102
+ await this.#reader.getChainId() === this.#protocol.context.chainId,
103
+ "disclosure chain changed during verification"
131
104
  );
105
+ return head - block.number + 1n;
106
+ }
107
+ /** Resolve the exact inserted output and its delivery from a successful canonical receipt. */
108
+ async readOutput(value) {
109
+ const selected = reference(value);
110
+ await this.#protocol.validateDeployment(this.#reader);
111
+ const received = await this.#reader.getReceipt(selected.transactionHash);
112
+ check(received, "disclosure transaction receipt is unavailable");
113
+ const receipt = structuredClone(received);
114
+ check(receipt.status === "success", "disclosure requires a successful transaction receipt");
132
115
  check(
133
- ["prepared", "authorized"].includes(this.status),
134
- "operation is not awaiting wallet authorization"
116
+ receipt.transactionHash.toLowerCase() === selected.transactionHash,
117
+ "disclosure receipt transaction mismatch"
135
118
  );
136
- const authorization = await acceptWalletSignature(this.#saved.plan, signature);
137
- return this.stage(
138
- "wallet authorization",
139
- ["prepared", "authorized"],
140
- async () => ({ status: "authorized", authorization }),
141
- options.signal
142
- );
143
- }
144
- /** Export only after proving when the funding authorization binds an actual proof hash. */
145
- async exportFundingAuthorization(options = {}) {
146
- options.signal?.throwIfAborted();
147
- this.current();
148
- check(
149
- this.status === (this.kind === "shield" ? "prepared" : "proved"),
150
- "operation is not awaiting funding authorization or proof"
151
- );
152
- const request = await fundingSigningRequest(this.#saved, this.#runtime);
153
- options.signal?.throwIfAborted();
154
- this.current();
155
- return request;
156
- }
157
- /** Verify an external signature against this handle's own frozen operation. */
158
- async withFundingSignature(signature, options = {}) {
159
- options.signal?.throwIfAborted();
160
- this.current();
161
119
  check(
162
- this.status === (this.kind === "shield" ? "prepared" : "proved"),
163
- "operation is not awaiting funding authorization or proof"
120
+ typeof receipt.block.number === "bigint" && receipt.block.number >= 0n && /^0x[0-9a-fA-F]{64}$/.test(receipt.block.hash),
121
+ "invalid disclosure receipt block"
164
122
  );
165
- const verified = await acceptFundingSignature(this.#saved, signature, this.#runtime);
166
- return this.stage(
167
- "funding authorization",
168
- this.kind === "shield" ? ["prepared"] : ["proved"],
169
- async (saved) => {
170
- saved.plan.funding.signature = verified;
171
- if (saved.plan.statement?.target !== "noir-recursive") {
172
- saved.call = await this.encode(saved);
173
- }
174
- return { status: "ready" };
175
- },
176
- options.signal
177
- );
178
- }
179
- async prove(backendOrOptions = {}, suppliedOptions = {}) {
180
- const options = { ..."prove" in backendOrOptions ? suppliedOptions : backendOrOptions };
181
- const plan = this.#saved.plan;
182
- check(plan.kind !== "shield", "shield requires no proof");
183
- if (requiresAuthorization(plan)) {
184
- check(
185
- this.#authorization,
186
- "local wallet authorization required; authorize again after recovery"
187
- );
188
- }
189
- const prover = "prove" in backendOrOptions ? backendOrOptions : this.#runtime.options.prover;
190
- if (!prover) {
191
- throw new MissingCapabilityError("prover");
192
- }
193
- return this.stage(
194
- "proof",
195
- requiresAuthorization(plan) ? ["authorized"] : ["prepared"],
196
- async (saved) => {
197
- const p = saved.plan;
198
- saved.proof = await proveOperation(
199
- this.#runtime.options.protocol,
200
- p,
201
- this.#authorization,
202
- prover,
203
- options
204
- );
205
- if (p.fundingMode === "signature") {
206
- return { status: "proved" };
207
- }
208
- if (p.statement?.target !== "noir-recursive") {
209
- saved.call = await this.encode(saved);
210
- }
211
- return { status: "ready" };
212
- },
213
- options.signal
123
+ const { context } = this.#protocol;
124
+ const logs = readProtocolLogs(receipt, context.protocolAddress);
125
+ const candidates = extractDeliveryCandidates(logs, context, receipt.block.number).filter(
126
+ (candidate2) => candidate2.treeId === selected.treeId && candidate2.leafIndex === selected.leafIndex && candidate2.role === selected.role && candidate2.tokenAddress.toLowerCase() === context.tokenAddress.toLowerCase()
214
127
  );
215
- }
216
- encode(saved) {
217
- return encodeOperation(saved);
218
- }
219
- /** Shield caller funding needs no wallet signing or proof; this step only encodes its frozen request. */
220
- async ready() {
221
128
  check(
222
- this.kind === "shield" && this.#saved.plan.fundingMode === "caller",
223
- "operation needs authorization or proof first"
129
+ candidates.length === 1,
130
+ "disclosure output has no unique canonical insertion and delivery"
224
131
  );
225
- return this.stage("encoding", ["prepared"], async (saved) => {
226
- saved.call = await this.encode(saved);
227
- return { status: "ready" };
132
+ const candidate = candidates[0];
133
+ const confirmations = await this.barrier(receipt.block);
134
+ return Object.freeze({
135
+ reference: Object.freeze(selected),
136
+ block: Object.freeze({ ...receipt.block }),
137
+ confirmations,
138
+ output: Object.freeze({
139
+ chainId: context.chainId,
140
+ protocolAddress: BigInt(context.protocolAddress),
141
+ tokenAddress: BigInt(context.tokenAddress),
142
+ outputCommitment: candidate.commitment,
143
+ role: selected.role,
144
+ ciphertext: candidate.delivery.ciphertext
145
+ })
228
146
  });
229
147
  }
230
- async exportTransaction() {
231
- this.current();
232
- check(
233
- this.#saved.plan.statement?.target !== "noir-recursive",
234
- "recursive operations must be exported as aggregate members"
235
- );
236
- check(this.status === "ready" && this.#saved.call, "transaction is not ready");
237
- return structuredClone(this.#saved.call);
238
- }
239
- exportProof() {
240
- this.current();
241
- check(this.#saved.proof, "transaction has no proof");
242
- return publicProof(this.#saved.proof);
243
- }
244
- exportAggregateMember() {
245
- this.current();
246
- const saved = this.#saved, plan = saved.plan;
247
- check(
248
- this.status === "ready" && plan.params && saved.proof && plan.statement?.target === "noir-recursive" && plan.statement.circuitName === "transact_user_slots14",
249
- "ready recursive slots14 transaction required"
250
- );
251
- sameStatement(saved.proof, statement(plan));
252
- check(
253
- saved.proof.proofHex.length === 2 + 458 * 64,
254
- "recursive member proof must have 458 fields"
255
- );
256
- return {
257
- bundle: publicProof(saved.proof),
258
- statement: structuredClone(plan.statement),
259
- context: { ...plan.context },
260
- request: {
261
- params: structuredClone(plan.params),
262
- deliveries: plan.outputs.map((output) => structuredClone(output.delivery)),
263
- funding: normalizeFunding(plan.funding)
264
- }
265
- };
148
+ async verify(disclosure, value) {
149
+ const evidence = await this.readOutput(value);
150
+ const result = await disclosure.verify(evidence.output);
151
+ const confirmations = await this.barrier(evidence.block);
152
+ return Object.freeze({
153
+ result: Object.freeze(result),
154
+ block: evidence.block,
155
+ confirmations,
156
+ output: Object.freeze({
157
+ ...evidence.reference,
158
+ commitment: evidence.output.outputCommitment
159
+ })
160
+ });
266
161
  }
267
162
  toJSON() {
268
- return { transaction: true, id: this.id, kind: this.kind, status: this.status };
163
+ return { disclosureVerifier: true, confirmations: this.#confirmations.toString() };
269
164
  }
270
165
  [/* @__PURE__ */ Symbol.for("nodejs.util.inspect.custom")]() {
271
166
  return this.toJSON();
272
167
  }
273
168
  };
274
169
 
275
- // src/explicit-preparation.ts
276
- function explicitPreparation(options, data, tokenAddress) {
277
- if (!options.chainReader) throw new Error("chain reader required");
278
- const inputs = structuredClone(data.inputs ?? []);
279
- const credits = structuredClone(data.creditInputs ?? []);
280
- const inventory = { notes: {}, credits: {}, reservations: {} };
281
- const context = options.protocol.context;
282
- function position(input) {
283
- if (typeof input.treeId !== "bigint" || input.treeId < 0n || input.treeId >= 1n << 256n || !Number.isInteger(input.leafIndex) || input.leafIndex < 0 || input.leafIndex >= 65536)
284
- throw new Error("invalid explicit input position");
285
- }
286
- for (const input of inputs) {
287
- position(input);
288
- positiveAmount(input.note.amount);
289
- if (inventory.notes[input.commitment]) throw new Error("duplicate explicit note input");
290
- if (input.note.chainId !== context.chainId || input.note.protocolAddress !== BigInt(context.protocolAddress) || input.note.walletIdentity !== options.wallet.walletIdentity)
291
- throw new Error("explicit note identity or deployment mismatch");
292
- inventory.notes[input.commitment] = { ...input, spent: "unspent" };
293
- }
294
- for (const input of credits) {
295
- position(input);
296
- if (input.role !== "owner" && input.role !== "spender")
297
- throw new Error("invalid explicit Credit role");
298
- const key = input.commitment + ":" + input.role;
299
- if (inventory.credits[key]) throw new Error("duplicate explicit Credit input");
300
- inventory.credits[key] = { ...input, spent: "unspent" };
301
- }
302
- const scope = {
303
- ...context,
304
- namespace: "explicit",
305
- walletIdentity: options.wallet.walletIdentity
306
- };
307
- const {
308
- store: _store,
309
- coinSelector: _selector,
310
- authorization: _authorization,
311
- viewing: _viewing,
312
- signer: _signer,
313
- fundingSigner: _fundingSigner,
314
- ...capabilities
315
- } = options;
316
- if (inputs.some((input) => input.note.tokenAddress !== BigInt(getAddress(tokenAddress))))
317
- throw new Error("explicit note token mismatch");
318
- const runtime = {
319
- options: { ...capabilities, protocol: options.protocol.forToken(tokenAddress) },
320
- reader: options.chainReader,
321
- scope
322
- };
323
- return new TransactionPreparation({
324
- ...runtime,
325
- explicitInputs: true,
326
- ...data.changeAddress ? { changeAddress: data.changeAddress } : {},
327
- readInventory: async () => structuredClone(inventory),
328
- complete: async (plan) => PreparedTransaction.prepare(runtime, plan)
329
- });
170
+ // src/protocol/connections.ts
171
+ var connections = /* @__PURE__ */ new WeakMap();
172
+ function rememberProtocolConnection(protocol, provider, protocolRpc) {
173
+ connections.set(protocol, { provider, protocolRpc });
174
+ }
175
+ function protocolConnection(protocol) {
176
+ return connections.get(protocol);
330
177
  }
331
178
 
332
- // src/Invisi.ts
333
- import { ProtocolRpcClient } from "@invisi-labs/rpc";
334
- import { MissingCapabilityError as MissingCapabilityError2, PublicWallet, ReceivingAddress } from "@invisi-labs/core";
335
-
336
- // src/AddressRegistry.ts
337
- import { AddressRegistry as Registry } from "@invisi-labs/protocol";
338
- var AddressRegistry = class extends Registry {
339
- prepareRegistration(options) {
340
- return prepareWithErrors("prepareRegistration", async () => {
341
- if ("provider" in options) {
342
- const { provider, ...preparation } = options;
343
- return super.prepareRegistration({
344
- ...preparation,
345
- reader: new EthersChainReader(provider)
346
- });
347
- }
348
- return super.prepareRegistration(options);
349
- });
179
+ // src/protocol/Protocol.ts
180
+ import { Protocol as BaseProtocol } from "@invisi-labs/protocol";
181
+ import { getAddress } from "ethers";
182
+ var sources = /* @__PURE__ */ new WeakMap();
183
+ var Protocol = class _Protocol extends BaseProtocol {
184
+ static async fromRpc(rpc, provider, options = {}) {
185
+ const verified = await BaseProtocol.fromRpc(rpc, provider, options);
186
+ const protocol = new _Protocol({ deployment: verified.deployment });
187
+ if (rpc.getBroadcasters)
188
+ sources.set(protocol, rpc);
189
+ if (rpc.withReader)
190
+ rememberProtocolConnection(
191
+ protocol,
192
+ provider,
193
+ rpc
194
+ );
195
+ return protocol;
196
+ }
197
+ /** Returns enabled services that answer a token fee probe; final quotes are checked by prepared. */
198
+ async getBroadcasters(options) {
199
+ const token = getAddress(options.tokenAddress);
200
+ if (!this.deployment.broadcasterRegistry)
201
+ throw new Error("broadcaster registry is not configured");
202
+ const rpc = sources.get(this);
203
+ if (!rpc) throw new Error("broadcaster discovery requires Protocol.fromRpc");
204
+ const clients = [];
205
+ let after;
206
+ let block;
207
+ const seen = /* @__PURE__ */ new Set();
208
+ do {
209
+ const page = await rpc.getBroadcasters({
210
+ enabled: true,
211
+ limit: 100,
212
+ ...after ? { after } : {},
213
+ ...block ? { block } : {}
214
+ });
215
+ block ??= page.block;
216
+ const compatible = await Promise.all(
217
+ page.broadcasters.map(async (entry) => {
218
+ if (!entry.enabled) return void 0;
219
+ try {
220
+ const client = new BroadcasterClient(entry);
221
+ await client.fee(token);
222
+ return client;
223
+ } catch {
224
+ return void 0;
225
+ }
226
+ })
227
+ );
228
+ clients.push(
229
+ ...compatible.filter((client) => client !== void 0)
230
+ );
231
+ after = page.next;
232
+ if (after && seen.has(after)) throw new Error("repeated broadcaster pagination cursor");
233
+ if (after) seen.add(after);
234
+ } while (after);
235
+ return clients;
350
236
  }
351
237
  };
352
238
 
353
- // src/Invisi.ts
354
- var Invisi = class {
239
+ // src/index.ts
240
+ import { MAX_UPDATE_BLOCKS, ProtocolRpcError } from "@invisi-labs/rpc";
241
+
242
+ // src/protocol/BroadcasterRegistry.ts
243
+ import { broadcasterRegistryAbi } from "@invisi-labs/protocol/contracts";
244
+ import { getAddress as getAddress2, Interface, keccak256 } from "ethers";
245
+ var abi = new Interface(broadcasterRegistryAbi);
246
+ function address(value) {
247
+ const normalized = getAddress2(value);
248
+ if (BigInt(normalized) === 0n) throw new Error("zero broadcaster address");
249
+ return normalized;
250
+ }
251
+ var BroadcasterRegistry = class {
252
+ address;
355
253
  #options;
356
- constructor(options = {}) {
357
- if ("ciphertextCodecs" in options || "envelopeVersion" in options) {
358
- throw new Error(
359
- "SDK envelope versions are fixed; encode application payload bytes externally"
360
- );
254
+ constructor(options) {
255
+ this.address = address(options.address);
256
+ const blockRange = options.blockRange ?? 2e3;
257
+ if (options.chainId <= 0n || !Number.isSafeInteger(options.fromBlock) || options.fromBlock < 0 || !Number.isSafeInteger(blockRange) || blockRange < 1 || blockRange > 1e5 || !/^0x[0-9a-fA-F]{64}$/.test(options.runtimeCodeHash))
258
+ throw new Error("invalid broadcaster registry configuration");
259
+ this.#options = Object.freeze({ ...options, blockRange });
260
+ }
261
+ prepareRegistration(registration) {
262
+ const { endpoint, metadataURI = "" } = registration;
263
+ if (!endpoint.startsWith("https://") || endpoint.length <= 8 || endpoint.length > 2048 || /[^\x21-\x7e]/.test(endpoint))
264
+ throw new Error("broadcaster endpoint must be a printable HTTPS URI");
265
+ const url = new URL(endpoint);
266
+ if (!url.hostname || url.username || url.password) {
267
+ throw new Error("broadcaster HTTPS endpoint must not include credentials");
361
268
  }
362
- if ("signer" in options || "fundingSigner" in options) {
363
- throw new Error(
364
- "Invisi does not accept signer defaults; export a signing request and attach the external signature"
365
- );
269
+ if (metadataURI.length > 2048 || /[^\x21-\x7e]/.test(metadataURI)) {
270
+ throw new Error("invalid broadcaster metadata URI");
366
271
  }
367
- this.#options = {
368
- ...options,
369
- ...options.gasBenchmarks ? { gasBenchmarks: copyGasBenchmarks(options.gasBenchmarks) } : {}
370
- };
371
- Object.freeze(this);
372
- }
373
- async connect(options) {
374
- return new WalletSession(await this.resolve(options));
375
- }
376
- async resolve(options) {
377
- const allowed = [
378
- "wallet",
379
- "protocol",
380
- "namespace",
381
- "viewing",
382
- "authorization",
383
- "signer",
384
- "fundingSigner"
385
- ];
386
- for (const key of Object.keys(options)) {
387
- if (!allowed.includes(key))
388
- throw new Error(
389
- `Unknown connection option: ${key}; configure infrastructure on Invisi`
390
- );
272
+ return this.call("setBroadcaster", [
273
+ endpoint,
274
+ metadataURI,
275
+ address(registration.quoteSigner),
276
+ address(registration.feeRecipient)
277
+ ]);
278
+ }
279
+ prepareAvailability(enabled) {
280
+ return this.call("setEnabled", [enabled]);
281
+ }
282
+ call(name, args) {
283
+ return { to: this.address, data: abi.encodeFunctionData(name, args), value: 0n };
284
+ }
285
+ async validate() {
286
+ const { provider, chainId, runtimeCodeHash } = this.#options;
287
+ if ((await provider.getNetwork()).chainId !== chainId) {
288
+ throw new Error("broadcaster registry chain mismatch");
391
289
  }
392
- const selected = { ...options };
393
- const combined = { ...this.#options, ...selected };
394
- const { provider, addressLookup = "protocol-rpc", ...runtime } = combined;
395
- if (addressLookup !== void 0 && addressLookup !== "chain" && addressLookup !== "protocol-rpc") {
396
- throw new Error("Invalid recipient address lookup source");
397
- }
398
- const reader = combined.chainReader ?? (provider ? new EthersChainReader(provider) : void 0);
399
- if (!reader) throw new MissingCapabilityError2("chain reader");
400
- if (provider && (await provider.getNetwork()).chainId !== selected.protocol.context.chainId)
401
- throw new Error("provider deployment chain mismatch");
402
- await selected.protocol.validateDeployment(reader);
403
- if (runtime.protocolRpc instanceof ProtocolRpcClient) {
404
- runtime.protocolRpc = runtime.protocolRpc.withReader(reader);
405
- }
406
- const registry = selected.protocol.deployment.registry;
407
- const fundingVerifier = combined.fundingVerifier ?? (provider ? new EthersFundingVerifier(provider, selected.protocol.context.chainId) : void 0);
408
- let addressResolver = combined.addressResolver;
409
- if (!addressResolver && registry) {
410
- if (addressLookup === "protocol-rpc") {
411
- const rpc = runtime.protocolRpc;
412
- addressResolver = async (identity) => {
413
- if (!(rpc instanceof ProtocolRpcClient)) {
414
- throw new MissingCapabilityError2("Protocol RPC recipient discovery");
415
- }
416
- const discovered = await rpc.getDeployment(selected.protocol.context.chainId);
417
- const expected = selected.protocol.deployment;
418
- if (discovered.protocol.address !== expected.protocol.address || discovered.protocol.runtimeCodeHash !== expected.protocol.runtimeCodeHash || discovered.protocol.fromBlock !== expected.protocol.fromBlock || discovered.registry?.address !== registry.address || discovered.registry.runtimeCodeHash !== registry.runtimeCodeHash || discovered.registry.fromBlock !== registry.fromBlock)
419
- throw new Error("Recipient registry deployment pins mismatch");
420
- const registration = await rpc.getReceivingAddress(identity);
421
- return registration ? ReceivingAddress.parse(registration.receivingAddress).getReceivingKeys() : null;
422
- };
423
- } else if (provider) {
424
- addressResolver = new EthersAddressResolver(
425
- provider,
426
- {
427
- chainId: selected.protocol.context.chainId,
428
- registryAddress: registry.address
429
- },
430
- registry.runtimeCodeHash
431
- ).lookup;
432
- }
290
+ if (keccak256(await provider.getCode(this.address)).toLowerCase() !== runtimeCodeHash.toLowerCase()) {
291
+ throw new Error("broadcaster registry runtime code mismatch");
433
292
  }
293
+ }
294
+ async get(operator) {
295
+ await this.validate();
296
+ return this.read(address(operator));
297
+ }
298
+ async read(operator) {
299
+ const result = await this.#options.provider.call(this.call("broadcasterOf", [operator]));
300
+ const [entry] = abi.decodeFunctionResult("broadcasterOf", result);
434
301
  return {
435
- ...runtime,
436
- chainReader: reader,
437
- ...fundingVerifier ? { fundingVerifier } : {},
438
- ...addressResolver ? { addressResolver } : {},
439
- protocol: selected.protocol
302
+ operator,
303
+ endpoint: entry.endpoint,
304
+ metadataURI: entry.metadataURI,
305
+ quoteSigner: address(entry.quoteSigner),
306
+ feeRecipient: address(entry.feeRecipient),
307
+ enabled: entry.enabled
440
308
  };
441
309
  }
442
- /** Discover the selected chain's registry and prepare an independent signing request. */
443
- async prepareRegistration(options) {
444
- return prepareWithErrors("prepareRegistration", async () => {
445
- for (const key of Object.keys(options)) {
446
- if (!["wallet", "update", "deadline"].includes(key)) {
447
- throw new Error(`Unknown registration option: ${key}`);
448
- }
449
- }
450
- this.assertPublicIdentity(options);
451
- const selected = { ...options };
452
- const { provider, protocolRpc } = this.#options;
453
- if (!provider) throw new MissingCapabilityError2("ethers provider");
454
- if (!(protocolRpc instanceof ProtocolRpcClient)) {
455
- throw new MissingCapabilityError2("Protocol RPC deployment discovery");
456
- }
457
- const chainId = (await provider.getNetwork()).chainId;
458
- const deployment = await protocolRpc.getDeployment(chainId);
459
- if (!deployment.registry) {
460
- throw new MissingCapabilityError2("receiving-key registry deployment");
310
+ /** Discover event operators, then read their latest entry; disabled services remain explicit. */
311
+ async discover(options = {}) {
312
+ const { provider, fromBlock, blockRange } = this.#options;
313
+ options.signal?.throwIfAborted();
314
+ await this.validate();
315
+ const toBlock = options.toBlock ?? await provider.getBlockNumber();
316
+ if (!Number.isSafeInteger(toBlock) || toBlock < fromBlock) {
317
+ throw new Error("invalid broadcaster discovery block range");
318
+ }
319
+ const operators = /* @__PURE__ */ new Set();
320
+ for (let start = fromBlock; start <= toBlock; ) {
321
+ options.signal?.throwIfAborted();
322
+ const end = Math.min(start + blockRange - 1, toBlock);
323
+ const logs = await provider.getLogs({
324
+ address: this.address,
325
+ topics: [abi.getEvent("BroadcasterSet").topicHash],
326
+ fromBlock: start,
327
+ toBlock: end
328
+ });
329
+ for (const log of logs) {
330
+ if (getAddress2(log.address) !== this.address)
331
+ throw new Error("registry event address mismatch");
332
+ const event = abi.parseLog(log);
333
+ if (!event || event.name !== "BroadcasterSet")
334
+ throw new Error("invalid registry event");
335
+ operators.add(address(event.args.operator));
461
336
  }
462
- const registry = new AddressRegistry(
463
- { chainId, registryAddress: deployment.registry.address },
464
- { expectedRuntimeCodeHash: deployment.registry.runtimeCodeHash }
465
- );
466
- return registry.prepareRegistration({ ...selected, provider });
467
- });
468
- }
469
- assertPublicIdentity(options) {
470
- const { wallet } = options;
471
- if (!(wallet instanceof PublicWallet) || ["getViewer", "getDetector", "exportAuthorization"].some((key) => key in wallet)) {
472
- throw new Error(
473
- "Independent preparation requires a PublicWallet without private capabilities"
474
- );
337
+ start = end + 1;
475
338
  }
476
- for (const key of ["authorization", "signer", "fundingSigner", "viewing"]) {
477
- if (key in options)
478
- throw new Error(`Independent preparation does not accept capability: ${key}`);
339
+ const entries = [];
340
+ for (const operator of operators) {
341
+ options.signal?.throwIfAborted();
342
+ entries.push(await this.read(operator));
479
343
  }
480
- }
481
- resolveExplicit(options) {
482
- this.assertPublicIdentity(options);
483
- return this.resolve({ wallet: options.wallet, protocol: options.protocol });
484
- }
485
- async prepareTransfer(options) {
486
- return prepareWithErrors("prepareTransfer", async () => {
487
- const { wallet, protocol, ...input } = options;
488
- const request = structuredClone(input);
489
- const runtime = await this.resolveExplicit(options);
490
- const preparation = explicitPreparation(runtime, request, request.tokenAddress);
491
- return preparation.prepareTransfer(request);
492
- });
493
- }
494
- async prepareShield(options) {
495
- return prepareWithErrors("prepareShield", async () => {
496
- const { wallet, protocol, ...input } = options;
497
- const request = structuredClone(input);
498
- const runtime = await this.resolveExplicit(options);
499
- const preparation = explicitPreparation(runtime, {}, request.tokenAddress);
500
- return preparation.prepareShield(request);
501
- });
502
- }
503
- async prepareCreateCredit(options) {
504
- return prepareWithErrors("prepareCreateCredit", async () => {
505
- const { wallet, protocol, ...input } = options;
506
- const request = structuredClone(input);
507
- const runtime = await this.resolveExplicit(options);
508
- const preparation = explicitPreparation(runtime, request, request.tokenAddress);
509
- return preparation.prepareCreateCredit(request);
510
- });
511
- }
512
- async prepareDrawCredit(options) {
513
- return prepareWithErrors("prepareDrawCredit", async () => {
514
- const { wallet, protocol, ...input } = options;
515
- const request = structuredClone(input);
516
- const runtime = await this.resolveExplicit(options);
517
- const preparation = explicitPreparation(runtime, request, request.tokenAddress);
518
- return preparation.prepareDrawCredit(request);
519
- });
520
- }
521
- async prepareTopUpCredit(options) {
522
- return prepareWithErrors("prepareTopUpCredit", async () => {
523
- const { wallet, protocol, ...input } = options;
524
- const request = structuredClone(input);
525
- const runtime = await this.resolveExplicit(options);
526
- const preparation = explicitPreparation(runtime, request, request.tokenAddress);
527
- return preparation.prepareTopUpCredit(request);
528
- });
529
- }
530
- async prepareCancelCredit(options) {
531
- return prepareWithErrors("prepareCancelCredit", async () => {
532
- const { wallet, protocol, ...input } = options;
533
- const request = structuredClone(input);
534
- const runtime = await this.resolveExplicit(options);
535
- const preparation = explicitPreparation(runtime, request, request.tokenAddress);
536
- return preparation.prepareCancelCredit(request);
537
- });
538
- }
539
- toJSON() {
540
- return { invisi: true };
541
- }
542
- [/* @__PURE__ */ Symbol.for("nodejs.util.inspect.custom")]() {
543
- return this.toJSON();
344
+ return entries;
544
345
  }
545
346
  };
546
347
 
547
- // src/index.ts
548
- import {
549
- fundsV1,
550
- creditV1,
551
- decodeDeliveryHeader,
552
- Wallet,
553
- PublicWallet as PublicWallet2,
554
- ViewingCapability,
555
- Detector,
556
- NullifierCapability,
557
- WalletAuthorization,
558
- MissingCapabilityError as MissingCapabilityError3,
559
- Disclosure,
560
- encodeMemo,
561
- decodeMemoText,
562
- MAX_MEMO_BYTES,
563
- computeCreditNullifier,
564
- creditPeriods
565
- } from "@invisi-labs/core";
566
- import { Protocol, RegistrationOperation } from "@invisi-labs/protocol";
567
- import { ProtocolRpcError } from "@invisi-labs/rpc";
568
-
569
- // src/ProtocolRpcClient.ts
348
+ // src/protocol/ProtocolRpcClient.ts
570
349
  import { ProtocolRpcClient as RpcClient } from "@invisi-labs/rpc";
571
- var ProtocolRpcClient2 = class extends RpcClient {
350
+ var ProtocolRpcClient = class extends RpcClient {
572
351
  withProvider(provider) {
573
352
  return this.withReader(new EthersChainReader(provider));
574
353
  }
575
354
  };
576
355
 
577
- // src/integrations/encrypted-codec.ts
578
- var EncryptedWalletCodec = class _EncryptedWalletCodec {
579
- #key;
580
- constructor(key) {
581
- this.#key = key;
356
+ // src/payment-note/PaymentNote.ts
357
+ import { computeNoteIdentity, computeNoteCommitment, Wallet } from "@invisi-labs/core";
358
+
359
+ // src/payment-note/verification.ts
360
+ import {
361
+ poseidonHash,
362
+ TREE_DEPTH,
363
+ computeNoteNullifier
364
+ } from "@invisi-labs/core";
365
+ import { getAddress as getAddress3 } from "ethers";
366
+ function verificationOptions(protocol, options = {}) {
367
+ const connected = protocolConnection(protocol);
368
+ const resolved = { ...connected, ...options };
369
+ if (!("protocolRpc" in resolved) || !resolved.protocolRpc || !("chainReader" in resolved) && !("provider" in resolved))
370
+ throw new Error(
371
+ "Payment note verification requires Protocol.fromRpc or explicit chain and Protocol RPC connections"
372
+ );
373
+ return resolved;
374
+ }
375
+ function sameBlock(a, b) {
376
+ return a.number === b.number && a.hash === b.hash && a.parentHash === b.parentHash && a.timestamp === b.timestamp;
377
+ }
378
+ function asNote(data) {
379
+ return {
380
+ chainId: data.chainId,
381
+ protocolAddress: BigInt(data.protocolAddress),
382
+ tokenAddress: BigInt(data.tokenAddress),
383
+ amount: data.amount,
384
+ ownerAddress: data.ownerAddress,
385
+ nonce: data.nonce
386
+ };
387
+ }
388
+ async function verifyPaymentNote(data, protocol, options, wallet) {
389
+ if (data.chainId !== protocol.context.chainId || getAddress3(data.protocolAddress) !== getAddress3(protocol.context.protocolAddress))
390
+ throw new Error("Payment note protocol context mismatch");
391
+ const confirmations = options.confirmations ?? 1;
392
+ if (!Number.isSafeInteger(confirmations) || confirmations < 1)
393
+ throw new RangeError("confirmations must be positive");
394
+ const reader = options.chainReader ?? new EthersChainReader(options.provider);
395
+ await protocol.validateDeployment(reader);
396
+ if (wallet && getAddress3(wallet.ownerAddress) !== getAddress3(data.ownerAddress))
397
+ throw new Error("Payment note owner mismatch");
398
+ if (wallet) {
399
+ if (typeof wallet.exportAuthorization !== "function")
400
+ throw new Error("Wallet spending authorization capability required");
401
+ const authorization = wallet.exportAuthorization();
402
+ if (getAddress3(authorization.ownerAddress) !== getAddress3(data.ownerAddress))
403
+ throw new Error("Wallet authorization owner mismatch");
404
+ }
405
+ const latest = await reader.getLatestBlockNumber();
406
+ const number = latest - BigInt(confirmations - 1);
407
+ if (number < protocol.deployment.protocol.fromBlock)
408
+ throw new Error("Insufficient confirmed blocks");
409
+ const rpc = options.protocolRpc.withReader(reader);
410
+ const status = await rpc.getStatus(protocol.context);
411
+ if (!status.indexedHead) throw new Error("Protocol RPC has no indexed blocks");
412
+ const indexedNumber = status.indexedHead.number < number ? status.indexedHead.number : number;
413
+ if (indexedNumber < protocol.deployment.protocol.fromBlock)
414
+ throw new Error("Protocol RPC has not reached the deployment");
415
+ const checkedAt = await reader.getBlock(indexedNumber);
416
+ if (checkedAt.number !== indexedNumber)
417
+ throw new Error("Payment note confirmation block mismatch");
418
+ const result = await rpc.getNote(protocol.context, data.commitment, checkedAt);
419
+ if (!result) throw new Error("Payment note is not indexed at the requested confirmed block");
420
+ const { output, membership } = result;
421
+ if (output.commitment !== data.commitment || getAddress3(output.tokenAddress) !== getAddress3(data.tokenAddress) || membership.leaf !== data.commitment || membership.treeId !== output.treeId || membership.leafIndex !== output.leafIndex || membership.block.number > checkedAt.number || output.block.number > checkedAt.number || membership.path.length !== TREE_DEPTH || !Number.isSafeInteger(output.leafIndex) || output.leafIndex < 0 || output.leafIndex >= 2 ** TREE_DEPTH)
422
+ throw new Error("Payment note membership metadata mismatch");
423
+ let root = BigInt(data.commitment);
424
+ for (let level = 0; level < membership.path.length; level++) {
425
+ const sibling = membership.path[level];
426
+ root = BigInt(
427
+ await poseidonHash(output.leafIndex >> level & 1 ? [sibling, root] : [root, sibling])
428
+ );
582
429
  }
583
- static fromKey(key) {
584
- if (key.type !== "secret" || key.algorithm.name !== "AES-GCM" || key.algorithm.length !== 256 || !key.usages.includes("encrypt") || !key.usages.includes("decrypt"))
585
- throw new Error("wallet record key must support AES-256-GCM encryption and decryption");
586
- return new _EncryptedWalletCodec(key);
430
+ if (root !== BigInt(membership.root) || !await reader.isKnownRoot(protocol.context, membership.treeId, membership.root, checkedAt))
431
+ throw new Error("Payment note membership root is not accepted on chain");
432
+ if (!sameBlock(output.block, await reader.getBlock(output.block.number)) || !sameBlock(membership.block, await reader.getBlock(membership.block.number)))
433
+ throw new Error("Payment note block is no longer canonical");
434
+ let spendabilityCheckedAt;
435
+ if (wallet) {
436
+ const current = await reader.getBlock(await reader.getLatestBlockNumber());
437
+ const nullifier = await computeNoteNullifier(data.chainId, data.nonce, data.commitment);
438
+ const spent = await reader.isSpent(protocol.context, [nullifier], current);
439
+ if (spent.length !== 1 || spent[0] !== false)
440
+ throw new Error("Payment note is already spent");
441
+ if (!await reader.isKnownRoot(
442
+ protocol.context,
443
+ membership.treeId,
444
+ membership.root,
445
+ current
446
+ ))
447
+ throw new Error("Payment note membership root is no longer spendable");
448
+ if (!sameBlock(current, await reader.getBlock(current.number)))
449
+ throw new Error("Spendability block reorganized");
450
+ spendabilityCheckedAt = current;
451
+ }
452
+ if (!sameBlock(checkedAt, await reader.getBlock(checkedAt.number)))
453
+ throw new Error("Payment note verification block reorganized");
454
+ return {
455
+ input: {
456
+ note: asNote(data),
457
+ commitment: data.commitment,
458
+ treeId: output.treeId,
459
+ leafIndex: output.leafIndex
460
+ },
461
+ membership,
462
+ checkedAt,
463
+ ...spendabilityCheckedAt ? { spendabilityCheckedAt } : {}
464
+ };
465
+ }
466
+
467
+ // src/payment-note/PaymentNote.ts
468
+ var verification = /* @__PURE__ */ Symbol("PaymentNote verification");
469
+ var VerifiedPaymentNote = class {
470
+ #membership;
471
+ #checkedAt;
472
+ constructor(token, membership, checkedAt) {
473
+ if (token !== verification) throw new Error("Payment note verification required");
474
+ this.#membership = structuredClone(membership);
475
+ this.#checkedAt = structuredClone(checkedAt);
476
+ }
477
+ get membership() {
478
+ return structuredClone(this.#membership);
479
+ }
480
+ get checkedAt() {
481
+ return structuredClone(this.#checkedAt);
587
482
  }
588
- static async fromBytes(key) {
589
- if (key.length !== 32) throw new Error("wallet record encryption requires 32 key bytes");
590
- const copy = Uint8Array.from(key);
591
- try {
592
- return _EncryptedWalletCodec.fromKey(
593
- await crypto.subtle.importKey("raw", copy, { name: "AES-GCM" }, false, [
594
- "encrypt",
595
- "decrypt"
596
- ])
597
- );
598
- } finally {
599
- copy.fill(0);
600
- }
483
+ };
484
+ var SpendablePaymentNote = class extends VerifiedPaymentNote {
485
+ #input;
486
+ #spendabilityCheckedAt;
487
+ constructor(token, input, membership, checkedAt, spendabilityCheckedAt) {
488
+ super(token, membership, checkedAt);
489
+ this.#input = structuredClone(input);
490
+ this.#spendabilityCheckedAt = structuredClone(spendabilityCheckedAt);
491
+ Object.freeze(this);
601
492
  }
602
- aad(context) {
603
- if (!Number.isSafeInteger(context.revision) || context.revision < 1)
604
- throw new Error("invalid encrypted record revision");
605
- return new TextEncoder().encode(
606
- `Invisi.WalletRecord.v1\0${context.revision}\0${context.key}`
607
- );
493
+ get spendabilityCheckedAt() {
494
+ return structuredClone(this.#spendabilityCheckedAt);
608
495
  }
609
- async encode(state, context) {
610
- const iv = crypto.getRandomValues(new Uint8Array(12)), plain = Uint8Array.from(serializeState(state));
611
- try {
612
- const encrypted = new Uint8Array(
613
- await crypto.subtle.encrypt(
614
- { name: "AES-GCM", iv, additionalData: this.aad(context) },
615
- this.#key,
616
- plain
617
- )
618
- );
619
- const out = new Uint8Array(13 + encrypted.length);
620
- out[0] = 1;
621
- out.set(iv, 1);
622
- out.set(encrypted, 13);
623
- return out;
624
- } finally {
625
- plain.fill(0);
626
- }
496
+ toInput() {
497
+ return structuredClone(this.#input);
627
498
  }
628
- async decode(data, context) {
629
- if (data.length < 29 || data[0] !== 1)
630
- throw new Error("wallet record authentication failed");
631
- let plain;
632
- try {
633
- plain = new Uint8Array(
634
- await crypto.subtle.decrypt(
635
- {
636
- name: "AES-GCM",
637
- iv: Uint8Array.from(data.subarray(1, 13)),
638
- additionalData: this.aad(context)
639
- },
640
- this.#key,
641
- Uint8Array.from(data.subarray(13))
642
- )
643
- );
644
- } catch {
645
- throw new Error("wallet record authentication failed");
646
- }
647
- try {
648
- return deserializeState(plain);
649
- } finally {
650
- plain.fill(0);
651
- }
499
+ };
500
+ var PaymentNote = class _PaymentNote {
501
+ chainId;
502
+ protocolAddress;
503
+ tokenAddress;
504
+ amount;
505
+ ownerAddress;
506
+ nonce;
507
+ commitment;
508
+ ipn;
509
+ constructor(data, ipn) {
510
+ this.chainId = data.chainId;
511
+ this.protocolAddress = data.protocolAddress;
512
+ this.tokenAddress = data.tokenAddress;
513
+ this.amount = data.amount;
514
+ this.ownerAddress = data.ownerAddress;
515
+ this.nonce = data.nonce;
516
+ this.commitment = data.commitment;
517
+ this.ipn = ipn;
518
+ Object.freeze(this);
652
519
  }
653
- toJSON() {
654
- return { walletRecordCodec: "AES-256-GCM" };
520
+ static async from(ipn) {
521
+ const data = decodePaymentNote(ipn);
522
+ const identity = await computeNoteIdentity(BigInt(data.ownerAddress), data.nonce);
523
+ const commitment = await computeNoteCommitment(
524
+ data.chainId,
525
+ identity,
526
+ BigInt(data.tokenAddress),
527
+ data.amount
528
+ );
529
+ if (commitment !== data.commitment) throw new Error("Payment note commitment mismatch");
530
+ return new _PaymentNote(data, encodePaymentNote(data));
531
+ }
532
+ async verify(protocol, options = {}) {
533
+ const result = await verifyPaymentNote(
534
+ this.data(),
535
+ protocol,
536
+ verificationOptions(protocol, options)
537
+ );
538
+ const verified = new VerifiedPaymentNote(verification, result.membership, result.checkedAt);
539
+ Object.freeze(verified);
540
+ return verified;
541
+ }
542
+ async verifySpendable(protocol, wallet, options = {}) {
543
+ if (!(wallet instanceof Wallet))
544
+ throw new Error("Wallet spending authorization capability required");
545
+ const result = await verifyPaymentNote(
546
+ this.data(),
547
+ protocol,
548
+ verificationOptions(protocol, options),
549
+ wallet
550
+ );
551
+ return new SpendablePaymentNote(
552
+ verification,
553
+ result.input,
554
+ result.membership,
555
+ result.checkedAt,
556
+ result.spendabilityCheckedAt
557
+ );
655
558
  }
656
- [/* @__PURE__ */ Symbol.for("nodejs.util.inspect.custom")]() {
657
- return this.toJSON();
559
+ data() {
560
+ const { chainId, protocolAddress, tokenAddress, amount, ownerAddress, nonce, commitment } = this;
561
+ return { chainId, protocolAddress, tokenAddress, amount, ownerAddress, nonce, commitment };
658
562
  }
659
563
  };
660
564
  export {
661
565
  AddressRegistry,
566
+ BroadcasterClient,
567
+ BroadcasterRegistry,
568
+ BroadcasterRpcError,
662
569
  Detector,
663
570
  Disclosure,
664
571
  DisclosureVerifier,
665
- EncryptedWalletCodec,
666
572
  Invisi,
667
573
  InvisiError,
668
574
  MAX_MEMO_BYTES,
669
- MissingCapabilityError3 as MissingCapabilityError,
575
+ MAX_UPDATE_BLOCKS,
576
+ MissingCapabilityError,
577
+ NoteOutput,
670
578
  NullifierCapability,
671
- Operation,
579
+ PaymentNote,
672
580
  PreparedTransaction,
673
581
  Protocol,
674
- ProtocolRpcClient2 as ProtocolRpcClient,
582
+ ProtocolRpcClient,
675
583
  ProtocolRpcError,
676
- PublicWallet2 as PublicWallet,
584
+ PublicWallet,
677
585
  RegistrationOperation,
586
+ SpendablePaymentNote,
587
+ TransactionOutput,
588
+ VerifiedPaymentNote,
678
589
  ViewingCapability,
679
- Wallet,
590
+ Wallet2 as Wallet,
680
591
  WalletAuthorization,
681
- WalletSession,
592
+ broadcasterQuoteDomain,
593
+ broadcasterQuoteTypes,
594
+ broadcasterRequestHash,
595
+ broadcasterTransactInterface,
682
596
  computeCreditNullifier,
683
597
  creditPeriods,
684
598
  creditV1,
599
+ decodeBroadcasterIntent,
685
600
  decodeDeliveryHeader,
686
601
  decodeMemoText,
602
+ decodePaymentNote,
603
+ decodePaymentRequest,
687
604
  decryptCandidates,
688
605
  defineGasBenchmarks,
689
606
  encodeMemo,
607
+ encodePaymentNote,
608
+ encodePaymentRequest,
690
609
  fundsV1,
691
610
  readProtocolLogs,
692
- walletBalance
611
+ verifyBroadcasterQuote
693
612
  };
694
613
  //# sourceMappingURL=index.js.map