@invisi-labs/sdk 1.1.6 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (132) hide show
  1. package/README.md +5 -0
  2. package/dist/broadcaster-3KTB4JO2.cjs +21 -0
  3. package/dist/broadcaster-3KTB4JO2.cjs.map +1 -0
  4. package/dist/broadcaster-7ZKKSTZQ.js +21 -0
  5. package/dist/broadcaster-preparation-PJ263WBL.cjs +15 -0
  6. package/dist/broadcaster-preparation-PJ263WBL.cjs.map +1 -0
  7. package/dist/broadcaster-preparation-ZQAPZAUG.js +15 -0
  8. package/dist/broadcaster-preparation-ZQAPZAUG.js.map +1 -0
  9. package/dist/chunk-2URICDPW.cjs +167 -0
  10. package/dist/chunk-2URICDPW.cjs.map +1 -0
  11. package/dist/chunk-3D24ABYR.js +86 -0
  12. package/dist/chunk-3D24ABYR.js.map +1 -0
  13. package/dist/chunk-3KTQQZOW.cjs +71 -0
  14. package/dist/chunk-3KTQQZOW.cjs.map +1 -0
  15. package/dist/chunk-4H4FARQK.js +1582 -0
  16. package/dist/chunk-4H4FARQK.js.map +1 -0
  17. package/dist/chunk-4SDP53YQ.cjs +765 -0
  18. package/dist/chunk-4SDP53YQ.cjs.map +1 -0
  19. package/dist/{chunk-QC6TQR3R.js → chunk-4VI6LYGH.js} +35 -12
  20. package/dist/chunk-4VI6LYGH.js.map +1 -0
  21. package/dist/chunk-BBSRP4MH.cjs +287 -0
  22. package/dist/chunk-BBSRP4MH.cjs.map +1 -0
  23. package/dist/chunk-DGMVGFKM.js +41 -0
  24. package/dist/chunk-DGMVGFKM.js.map +1 -0
  25. package/dist/chunk-GUD3LAUO.js +765 -0
  26. package/dist/chunk-GUD3LAUO.js.map +1 -0
  27. package/dist/chunk-HCB7RQ3S.cjs +79 -0
  28. package/dist/chunk-HCB7RQ3S.cjs.map +1 -0
  29. package/dist/{broadcaster-preparation-QVL62KM6.js → chunk-HKGJXQVN.js} +73 -34
  30. package/dist/chunk-HKGJXQVN.js.map +1 -0
  31. package/dist/chunk-HLH5JZD4.js +287 -0
  32. package/dist/chunk-HLH5JZD4.js.map +1 -0
  33. package/dist/chunk-J6TZZVAM.cjs +230 -0
  34. package/dist/chunk-J6TZZVAM.cjs.map +1 -0
  35. package/dist/chunk-LOR6PYH5.cjs +397 -0
  36. package/dist/chunk-LOR6PYH5.cjs.map +1 -0
  37. package/dist/chunk-MA2QZEF5.cjs +399 -0
  38. package/dist/chunk-MA2QZEF5.cjs.map +1 -0
  39. package/dist/chunk-MI4IP4ZD.cjs +86 -0
  40. package/dist/chunk-MI4IP4ZD.cjs.map +1 -0
  41. package/dist/chunk-NKYHYIRP.cjs +1582 -0
  42. package/dist/chunk-NKYHYIRP.cjs.map +1 -0
  43. package/dist/chunk-OCZLJKIA.cjs +41 -0
  44. package/dist/chunk-OCZLJKIA.cjs.map +1 -0
  45. package/dist/chunk-R5YAOJKB.js +71 -0
  46. package/dist/chunk-R5YAOJKB.js.map +1 -0
  47. package/dist/chunk-RY4M2CIQ.js +399 -0
  48. package/dist/chunk-RY4M2CIQ.js.map +1 -0
  49. package/dist/chunk-THMD5QO2.js +79 -0
  50. package/dist/chunk-THMD5QO2.js.map +1 -0
  51. package/dist/chunk-ZD2652WH.js +230 -0
  52. package/dist/chunk-ZD2652WH.js.map +1 -0
  53. package/dist/credit.cjs +1 -0
  54. package/dist/credit.cjs.map +1 -0
  55. package/dist/credit.d.cts +12 -0
  56. package/dist/credit.d.ts +12 -0
  57. package/dist/credit.js +1 -0
  58. package/dist/credit.js.map +1 -0
  59. package/dist/ethers.cjs +11 -385
  60. package/dist/ethers.cjs.map +1 -1
  61. package/dist/ethers.d.cts +16 -6
  62. package/dist/ethers.d.ts +16 -6
  63. package/dist/ethers.js +1 -1
  64. package/dist/index-CLhGwLxZ.d.cts +42 -0
  65. package/dist/index-Crp-ZFiM.d.ts +42 -0
  66. package/dist/index.cjs +479 -7847
  67. package/dist/index.cjs.map +1 -1
  68. package/dist/index.d.cts +87 -710
  69. package/dist/index.d.ts +87 -710
  70. package/dist/index.js +407 -869
  71. package/dist/index.js.map +1 -1
  72. package/dist/note.cjs +11 -0
  73. package/dist/note.cjs.map +1 -0
  74. package/dist/note.d.cts +29 -0
  75. package/dist/note.d.ts +29 -0
  76. package/dist/note.js +11 -0
  77. package/dist/note.js.map +1 -0
  78. package/dist/operation-bridge-CoECSxfi.d.cts +472 -0
  79. package/dist/operation-bridge-CoECSxfi.d.ts +472 -0
  80. package/dist/preparation-HUBTVCZE.js +404 -0
  81. package/dist/preparation-HUBTVCZE.js.map +1 -0
  82. package/dist/preparation-QD3ZSYV5.cjs +404 -0
  83. package/dist/preparation-QD3ZSYV5.cjs.map +1 -0
  84. package/dist/provenance-preparation-43DN2WK7.cjs +16 -0
  85. package/dist/provenance-preparation-43DN2WK7.cjs.map +1 -0
  86. package/dist/provenance-preparation-HEZE4L5H.js +16 -0
  87. package/dist/provenance-preparation-HEZE4L5H.js.map +1 -0
  88. package/dist/provenance.cjs +59 -0
  89. package/dist/provenance.cjs.map +1 -0
  90. package/dist/provenance.d.cts +34 -0
  91. package/dist/provenance.d.ts +34 -0
  92. package/dist/provenance.js +59 -0
  93. package/dist/provenance.js.map +1 -0
  94. package/dist/receipt-logs-8Msm_HZS.d.ts +322 -0
  95. package/dist/receipt-logs-DWG4In8N.d.cts +322 -0
  96. package/dist/session-support.cjs +189 -0
  97. package/dist/session-support.cjs.map +1 -0
  98. package/dist/session-support.d.cts +284 -0
  99. package/dist/session-support.d.ts +284 -0
  100. package/dist/session-support.js +189 -0
  101. package/dist/session-support.js.map +1 -0
  102. package/package.json +25 -19
  103. package/dist/NoteOutput-DRkVnwlx.d.cts +0 -398
  104. package/dist/NoteOutput-DpDC6T2O.d.ts +0 -398
  105. package/dist/broadcaster-preparation-QVL62KM6.js.map +0 -1
  106. package/dist/chunk-4SD3MW6N.js +0 -4711
  107. package/dist/chunk-4SD3MW6N.js.map +0 -1
  108. package/dist/chunk-FL4TIGWW.js +0 -1781
  109. package/dist/chunk-FL4TIGWW.js.map +0 -1
  110. package/dist/chunk-QC6TQR3R.js.map +0 -1
  111. package/dist/indexeddb.cjs +0 -6878
  112. package/dist/indexeddb.cjs.map +0 -1
  113. package/dist/indexeddb.d.cts +0 -20
  114. package/dist/indexeddb.d.ts +0 -20
  115. package/dist/indexeddb.js +0 -71
  116. package/dist/indexeddb.js.map +0 -1
  117. package/dist/memory.cjs +0 -6886
  118. package/dist/memory.cjs.map +0 -1
  119. package/dist/memory.d.cts +0 -12
  120. package/dist/memory.d.ts +0 -12
  121. package/dist/memory.js +0 -11
  122. package/dist/sqlite.cjs +0 -6862
  123. package/dist/sqlite.cjs.map +0 -1
  124. package/dist/sqlite.d.cts +0 -28
  125. package/dist/sqlite.d.ts +0 -28
  126. package/dist/sqlite.js +0 -55
  127. package/dist/sqlite.js.map +0 -1
  128. package/dist/state-C4HZFotm.d.cts +0 -78
  129. package/dist/state-C4HZFotm.d.ts +0 -78
  130. package/dist/store-8aBhnbGr.d.ts +0 -42
  131. package/dist/store-YrbXb6CY.d.cts +0 -42
  132. /package/dist/{memory.js.map → broadcaster-7ZKKSTZQ.js.map} +0 -0
package/dist/index.js CHANGED
@@ -1,807 +1,188 @@
1
1
  import {
2
- DisclosureVerifier,
3
- Operation,
4
- WalletSession,
5
- acceptFundingSignature,
6
- acceptWalletSignature,
7
- copyGasBenchmarks,
2
+ AddressRegistry,
3
+ Invisi,
4
+ PreparedTransaction,
8
5
  decryptCandidates,
9
- defineGasBenchmarks,
10
- deserializeState,
11
- estimateGasReference,
12
- fundingSigningRequest,
13
- operationPreview,
14
- proveOperation,
15
- readProtocolLogs,
16
- sameStatement,
17
- serializeState,
18
- walletSigningRequest
19
- } from "./chunk-4SD3MW6N.js";
6
+ extractDeliveryCandidates,
7
+ readProtocolLogs
8
+ } from "./chunk-4H4FARQK.js";
9
+ import "./chunk-HLH5JZD4.js";
10
+ import "./chunk-THMD5QO2.js";
11
+ import {
12
+ TransactionOutput
13
+ } from "./chunk-DGMVGFKM.js";
14
+ import {
15
+ BroadcasterClient,
16
+ BroadcasterRpcError,
17
+ broadcasterQuoteDomain,
18
+ broadcasterQuoteTypes,
19
+ broadcasterRequestHash,
20
+ broadcasterTransactInterface,
21
+ decodeBroadcasterIntent,
22
+ verifyBroadcasterQuote
23
+ } from "./chunk-R5YAOJKB.js";
24
+ import "./chunk-RY4M2CIQ.js";
20
25
  import {
21
26
  InvisiError,
22
27
  NoteOutput,
23
- TransactionPreparation,
24
- configureRecipientInfrastructure,
25
- encodeOperation,
26
- exportBroadcasterIntent,
27
- positiveAmount,
28
- prepareWithErrors,
29
- publicProof,
30
- requiresAuthorization,
31
- statement,
32
- walletBalance
33
- } from "./chunk-FL4TIGWW.js";
28
+ decodePaymentNote,
29
+ decodePaymentRequest,
30
+ encodePaymentNote,
31
+ encodePaymentRequest
32
+ } from "./chunk-GUD3LAUO.js";
34
33
  import {
35
- EthersChainReader,
36
- EthersFundingVerifier
37
- } from "./chunk-QC6TQR3R.js";
34
+ EthersChainReader
35
+ } from "./chunk-4VI6LYGH.js";
38
36
 
39
- // src/explicit-preparation.ts
40
- import { getAddress } from "ethers";
37
+ // src/index.ts
38
+ import {
39
+ Detector,
40
+ Disclosure,
41
+ MAX_MEMO_BYTES,
42
+ MissingCapabilityError,
43
+ NullifierCapability,
44
+ ViewingCapability,
45
+ Wallet as Wallet2,
46
+ computeCreditNullifier,
47
+ creditPeriods,
48
+ creditV1,
49
+ decodeDeliveryHeader,
50
+ decodeMemoText,
51
+ encodeMemo,
52
+ fundsV1
53
+ } from "@invisi-labs/primitives";
54
+ import { RegistrationOperation } from "@invisi-labs/rpc";
41
55
 
42
- // src/PreparedTransaction.ts
43
- import { MissingCapabilityError } from "@invisi-labs/core";
44
- import { normalizeFunding } from "@invisi-labs/protocol";
45
- function check(ok, message) {
46
- if (!ok) throw new Error(message);
56
+ // src/disclosure/DisclosureVerifier.ts
57
+ function check(value, message) {
58
+ if (!value) throw new Error(message);
47
59
  }
48
- var PreparedTransaction = class _PreparedTransaction {
49
- #runtime;
50
- #saved;
51
- #authorization;
52
- #lifecycle;
53
- status;
54
- constructor(runtime, saved, status, lifecycle, authorization) {
55
- this.#runtime = runtime;
56
- this.#saved = structuredClone(saved);
57
- this.#lifecycle = lifecycle;
58
- this.#authorization = authorization;
59
- this.status = status;
60
- Object.freeze(this);
61
- }
62
- static prepare(runtime, plan) {
63
- return new _PreparedTransaction(
64
- runtime,
65
- { plan: structuredClone(plan), generation: 0 },
66
- "prepared",
67
- { generation: 0, busy: false }
68
- );
69
- }
70
- broadcasterQuote() {
71
- return this.#saved.broadcasterQuote ? structuredClone(this.#saved.broadcasterQuote) : void 0;
72
- }
73
- async withBroadcaster(client, options) {
74
- this.current();
75
- check(
76
- this.status === "prepared" && this.#runtime.reprepare,
77
- "broadcaster must attach to an unsigned prepared transaction"
78
- );
79
- const { prepareBroadcasterPlan } = await import("./broadcaster-preparation-QVL62KM6.js");
80
- const selected = await prepareBroadcasterPlan(
81
- this.#saved.plan,
82
- this.#runtime.reprepare,
83
- client,
84
- options
85
- );
86
- return new _PreparedTransaction(
87
- this.#runtime,
88
- { plan: selected.plan, broadcasterQuote: selected.quote, generation: 0 },
89
- "prepared",
90
- { generation: 0, busy: false }
91
- );
92
- }
93
- get id() {
94
- return this.#saved.plan.id;
95
- }
96
- get kind() {
97
- return this.#saved.plan.kind;
98
- }
99
- estimateGas() {
100
- return estimateGasReference(
101
- this.#saved.plan,
102
- this.#runtime.options.protocol,
103
- this.#runtime.options.gasBenchmarks
104
- );
105
- }
106
- preview() {
107
- return operationPreview(this.#saved.plan, this.status);
108
- }
109
- /** Obtain a firm quote before signing. This intent contains no private inputs or witness. */
110
- async exportBroadcasterIntent() {
111
- this.current();
112
- check(
113
- ["prepared", "authorized"].includes(this.status),
114
- "quote intent requires a prepared transaction"
115
- );
116
- return exportBroadcasterIntent(this.#saved.plan);
117
- }
118
- current() {
119
- check(
120
- !this.#lifecycle.busy && this.#lifecycle.generation === this.#saved.generation,
121
- "stale transaction handle or active worker"
122
- );
123
- }
124
- async stage(stage, allowed, work, signal) {
125
- signal?.throwIfAborted();
126
- this.current();
127
- check(allowed.includes(this.status), `invalid transaction stage for ${stage}`);
128
- this.#lifecycle.busy = true;
129
- const saved = structuredClone(this.#saved);
130
- try {
131
- const next = await work(saved);
132
- signal?.throwIfAborted();
133
- saved.generation++;
134
- this.#lifecycle.generation = saved.generation;
135
- return new _PreparedTransaction(
136
- this.#runtime,
137
- saved,
138
- next.status,
139
- this.#lifecycle,
140
- next.authorization ?? this.#authorization
141
- );
142
- } finally {
143
- this.#lifecycle.busy = false;
144
- }
145
- }
146
- /** Public EIP-712 request; signing transport and key custody belong to the caller. */
147
- async exportWalletAuthorization(options = {}) {
148
- options.signal?.throwIfAborted();
149
- this.current();
150
- check(
151
- requiresAuthorization(this.#saved.plan),
152
- "operation does not require wallet authorization"
153
- );
154
- check(
155
- ["prepared", "authorized"].includes(this.status),
156
- "operation is not awaiting wallet authorization"
157
- );
158
- return walletSigningRequest(this.#saved.plan);
159
- }
160
- /** Attach an external EOA signature, retaining its witness only at the private proving boundary. */
161
- async withWalletSignature(signature, options = {}) {
162
- options.signal?.throwIfAborted();
163
- this.current();
164
- check(
165
- requiresAuthorization(this.#saved.plan),
166
- "operation does not require wallet authorization"
167
- );
60
+ function reference(value) {
61
+ check(/^0x[0-9a-fA-F]{64}$/.test(value.transactionHash), "invalid disclosure transaction hash");
62
+ check(
63
+ typeof value.treeId === "bigint" && value.treeId > 0n && value.treeId < 1n << 64n,
64
+ "invalid disclosure tree ID"
65
+ );
66
+ check(
67
+ Number.isSafeInteger(value.leafIndex) && value.leafIndex >= 0 && value.leafIndex < 65536,
68
+ "invalid disclosure leaf index"
69
+ );
70
+ check(["funds", "owner", "spender"].includes(value.role), "invalid disclosure reference role");
71
+ return {
72
+ transactionHash: value.transactionHash.toLowerCase(),
73
+ treeId: value.treeId,
74
+ leafIndex: value.leafIndex,
75
+ role: value.role
76
+ };
77
+ }
78
+ var DisclosureVerifier = class {
79
+ #protocol;
80
+ #reader;
81
+ #confirmations;
82
+ constructor(options) {
83
+ const confirmations = options.confirmations ?? 1;
168
84
  check(
169
- ["prepared", "authorized"].includes(this.status),
170
- "operation is not awaiting wallet authorization"
171
- );
172
- const authorization = await acceptWalletSignature(this.#saved.plan, signature);
173
- return this.stage(
174
- "wallet authorization",
175
- ["prepared", "authorized"],
176
- async () => ({ status: "authorized", authorization }),
177
- options.signal
85
+ Number.isSafeInteger(confirmations) && confirmations >= 1,
86
+ "invalid disclosure confirmations"
178
87
  );
88
+ this.#protocol = options.protocol;
89
+ this.#reader = "provider" in options ? new EthersChainReader(options.provider) : options.reader;
90
+ this.#confirmations = BigInt(confirmations);
91
+ Object.freeze(this);
179
92
  }
180
- /** Export only after proving when the funding authorization binds an actual proof hash. */
181
- async exportFundingAuthorization(options = {}) {
182
- options.signal?.throwIfAborted();
183
- this.current();
93
+ async barrier(block) {
94
+ await this.#protocol.validateDeployment(this.#reader);
95
+ const canonical = await this.#reader.getBlock(block.number);
184
96
  check(
185
- this.status === (this.kind === "shield" ? "prepared" : "proved"),
186
- "operation is not awaiting funding authorization or proof"
97
+ canonical.number === block.number && canonical.hash.toLowerCase() === block.hash.toLowerCase(),
98
+ "disclosure canonical block changed; reorg verification required"
187
99
  );
188
- const request = await fundingSigningRequest(this.#saved, this.#runtime);
189
- options.signal?.throwIfAborted();
190
- this.current();
191
- return request;
192
- }
193
- /** Verify an external signature against this handle's own frozen operation. */
194
- async withFundingSignature(signature, options = {}) {
195
- options.signal?.throwIfAborted();
196
- this.current();
100
+ const head = await this.#reader.getLatestBlockNumber();
197
101
  check(
198
- this.status === (this.kind === "shield" ? "prepared" : "proved"),
199
- "operation is not awaiting funding authorization or proof"
200
- );
201
- const verified = await acceptFundingSignature(this.#saved, signature, this.#runtime);
202
- return this.stage(
203
- "funding authorization",
204
- this.kind === "shield" ? ["prepared"] : ["proved"],
205
- async (saved) => {
206
- saved.plan.funding.signature = verified;
207
- if (saved.plan.statement?.target !== "noir-recursive") {
208
- saved.call = await this.encode(saved);
209
- }
210
- return { status: "ready" };
211
- },
212
- options.signal
102
+ head >= block.number && head - block.number + 1n >= this.#confirmations,
103
+ "insufficient disclosure confirmations"
213
104
  );
214
- }
215
- async prove(backendOrOptions = {}, suppliedOptions = {}) {
216
- const options = { ..."prove" in backendOrOptions ? suppliedOptions : backendOrOptions };
217
105
  check(
218
- !this.#saved.broadcasterQuote || options.execution !== "aggregate",
219
- "a broadcaster quote requires direct execution"
106
+ await this.#reader.getChainId() === this.#protocol.context.chainId,
107
+ "disclosure chain changed during verification"
220
108
  );
221
- const plan = this.#saved.plan;
222
- check(plan.kind !== "shield", "shield requires no proof");
223
- if (requiresAuthorization(plan)) {
224
- check(
225
- this.#authorization,
226
- "local wallet authorization required; authorize again after recovery"
227
- );
228
- }
229
- const prover = "prove" in backendOrOptions ? backendOrOptions : this.#runtime.options.prover;
230
- if (!prover) {
231
- throw new MissingCapabilityError("prover");
232
- }
233
- return this.stage(
234
- "proof",
235
- requiresAuthorization(plan) ? ["authorized"] : ["prepared"],
236
- async (saved) => {
237
- const p = saved.plan;
238
- saved.proof = await proveOperation(
239
- this.#runtime.options.protocol,
240
- p,
241
- this.#authorization,
242
- prover,
243
- options
244
- );
245
- if (p.fundingMode === "signature") {
246
- return { status: "proved" };
247
- }
248
- if (p.statement?.target !== "noir-recursive") {
249
- saved.call = await this.encode(saved);
250
- }
251
- return { status: "ready" };
252
- },
253
- options.signal
254
- );
255
- }
256
- encode(saved) {
257
- return encodeOperation(saved);
258
- }
259
- /** Shield caller funding needs no wallet signing or proof; this step only encodes its frozen request. */
260
- async ready() {
109
+ return head - block.number + 1n;
110
+ }
111
+ /** Resolve the exact inserted output and its delivery from a successful canonical receipt. */
112
+ async readOutput(value) {
113
+ const selected = reference(value);
114
+ await this.#protocol.validateDeployment(this.#reader);
115
+ const received = await this.#reader.getReceipt(selected.transactionHash);
116
+ check(received, "disclosure transaction receipt is unavailable");
117
+ const receipt = structuredClone(received);
118
+ check(receipt.status === "success", "disclosure requires a successful transaction receipt");
261
119
  check(
262
- this.kind === "shield" && this.#saved.plan.fundingMode === "caller",
263
- "operation needs authorization or proof first"
120
+ receipt.transactionHash.toLowerCase() === selected.transactionHash,
121
+ "disclosure receipt transaction mismatch"
264
122
  );
265
- return this.stage("encoding", ["prepared"], async (saved) => {
266
- saved.call = await this.encode(saved);
267
- return { status: "ready" };
268
- });
269
- }
270
- async exportTransaction() {
271
- this.current();
272
123
  check(
273
- this.#saved.plan.statement?.target !== "noir-recursive",
274
- "recursive operations must be exported as aggregate members"
124
+ typeof receipt.block.number === "bigint" && receipt.block.number >= 0n && /^0x[0-9a-fA-F]{64}$/.test(receipt.block.hash),
125
+ "invalid disclosure receipt block"
275
126
  );
276
- check(this.status === "ready" && this.#saved.call, "transaction is not ready");
277
- return structuredClone(this.#saved.call);
278
- }
279
- exportProof() {
280
- this.current();
281
- check(this.#saved.proof, "transaction has no proof");
282
- return publicProof(this.#saved.proof);
283
- }
284
- exportAggregateMember() {
285
- this.current();
286
- const saved = this.#saved, plan = saved.plan;
287
- check(
288
- this.status === "ready" && plan.params && saved.proof && plan.statement?.target === "noir-recursive" && plan.statement.circuitName === "transact_user_slots14",
289
- "ready recursive slots14 transaction required"
127
+ const { context } = this.#protocol;
128
+ const logs = readProtocolLogs(receipt, context.protocolAddress);
129
+ const candidates = extractDeliveryCandidates(logs, context, receipt.block.number).filter(
130
+ (candidate2) => candidate2.treeId === selected.treeId && candidate2.leafIndex === selected.leafIndex && candidate2.role === selected.role && candidate2.tokenAddress.toLowerCase() === context.tokenAddress.toLowerCase()
290
131
  );
291
- sameStatement(saved.proof, statement(plan));
292
132
  check(
293
- saved.proof.proofHex.length === 2 + 458 * 64,
294
- "recursive member proof must have 458 fields"
133
+ candidates.length === 1,
134
+ "disclosure output has no unique canonical insertion and delivery"
295
135
  );
296
- return {
297
- bundle: publicProof(saved.proof),
298
- statement: structuredClone(plan.statement),
299
- context: { ...plan.context },
300
- request: {
301
- params: structuredClone(plan.params),
302
- deliveries: plan.outputs.map((output) => structuredClone(output.delivery)),
303
- funding: normalizeFunding(plan.funding)
304
- }
305
- };
306
- }
307
- toJSON() {
308
- return { transaction: true, id: this.id, kind: this.kind, status: this.status };
309
- }
310
- [/* @__PURE__ */ Symbol.for("nodejs.util.inspect.custom")]() {
311
- return this.toJSON();
312
- }
313
- };
314
-
315
- // src/explicit-preparation.ts
316
- function explicitPreparation(options, data, tokenAddress) {
317
- if (!options.chainReader) throw new Error("chain reader required");
318
- const inputs = structuredClone(data.inputs ?? []);
319
- const credits = structuredClone(data.creditInputs ?? []);
320
- const inventory = { notes: {}, credits: {}, reservations: {} };
321
- const context = options.protocol.context;
322
- function position(input) {
323
- if (typeof input.treeId !== "bigint" || input.treeId < 0n || input.treeId >= 1n << 256n || !Number.isInteger(input.leafIndex) || input.leafIndex < 0 || input.leafIndex >= 65536)
324
- throw new Error("invalid explicit input position");
325
- }
326
- for (const input of inputs) {
327
- position(input);
328
- positiveAmount(input.note.amount);
329
- if (inventory.notes[input.commitment]) throw new Error("duplicate explicit note input");
330
- if (input.note.chainId !== context.chainId || input.note.protocolAddress !== BigInt(context.protocolAddress) || input.note.ownerAddress !== options.wallet.ownerAddress)
331
- throw new Error("explicit note identity or deployment mismatch");
332
- inventory.notes[input.commitment] = { ...input, spent: "unspent" };
333
- }
334
- for (const input of credits) {
335
- position(input);
336
- if (input.role !== "owner" && input.role !== "spender")
337
- throw new Error("invalid explicit Credit role");
338
- const key = input.commitment + ":" + input.role;
339
- if (inventory.credits[key]) throw new Error("duplicate explicit Credit input");
340
- inventory.credits[key] = { ...input, spent: "unspent" };
341
- }
342
- const scope = {
343
- ...context,
344
- namespace: "explicit",
345
- ownerAddress: options.wallet.ownerAddress
346
- };
347
- const {
348
- store: _store,
349
- coinSelector: _selector,
350
- authorization: _authorization,
351
- viewing: _viewing,
352
- signer: _signer,
353
- fundingSigner: _fundingSigner,
354
- ...capabilities
355
- } = options;
356
- if (inputs.some((input) => input.note.tokenAddress !== BigInt(getAddress(tokenAddress))))
357
- throw new Error("explicit note token mismatch");
358
- const runtime = {
359
- options: { ...capabilities, protocol: options.protocol.forToken(tokenAddress) },
360
- reader: options.chainReader,
361
- scope
362
- };
363
- const reprepare = (intent) => import("./broadcaster-preparation-QVL62KM6.js").then(
364
- ({ prepareIntent }) => prepareIntent(
365
- {
366
- ...runtime,
367
- explicitInputs: true,
368
- ...data.changeAddress ? { changeAddress: data.changeAddress } : {},
369
- readInventory: async () => structuredClone(inventory),
370
- complete: async (plan) => plan
371
- },
372
- intent
373
- )
374
- );
375
- return new TransactionPreparation({
376
- ...runtime,
377
- explicitInputs: true,
378
- ...data.changeAddress ? { changeAddress: data.changeAddress } : {},
379
- readInventory: async () => structuredClone(inventory),
380
- complete: async (plan) => PreparedTransaction.prepare({ ...runtime, reprepare }, plan)
381
- });
382
- }
383
-
384
- // src/Invisi.ts
385
- import { ProtocolRpcClient } from "@invisi-labs/rpc";
386
- import { MissingCapabilityError as MissingCapabilityError2, PublicWallet } from "@invisi-labs/core";
387
-
388
- // src/AddressRegistry.ts
389
- import { AddressRegistry as Registry } from "@invisi-labs/protocol";
390
- var AddressRegistry = class extends Registry {
391
- prepareRegistration(options) {
392
- return prepareWithErrors("prepareRegistration", async () => {
393
- if ("provider" in options) {
394
- const { provider, ...preparation } = options;
395
- return super.prepareRegistration({
396
- ...preparation,
397
- reader: new EthersChainReader(provider)
398
- });
399
- }
400
- return super.prepareRegistration(options);
136
+ const candidate = candidates[0];
137
+ const confirmations = await this.barrier(receipt.block);
138
+ return Object.freeze({
139
+ reference: Object.freeze(selected),
140
+ block: Object.freeze({ ...receipt.block }),
141
+ confirmations,
142
+ output: Object.freeze({
143
+ chainId: context.chainId,
144
+ protocolAddress: BigInt(context.protocolAddress),
145
+ tokenAddress: BigInt(context.tokenAddress),
146
+ outputCommitment: candidate.commitment,
147
+ role: selected.role,
148
+ ciphertext: candidate.delivery.ciphertext
149
+ })
401
150
  });
402
151
  }
403
- };
404
-
405
- // src/Invisi.ts
406
- var Invisi = class {
407
- #options;
408
- constructor(options = {}) {
409
- if ("addressLookup" in options || "addressResolver" in options)
410
- throw new TypeError(
411
- "Recipient lookup belongs to each EVM output; caller resolver injection is not supported"
412
- );
413
- if ("ciphertextCodecs" in options || "envelopeVersion" in options) {
414
- throw new Error(
415
- "SDK envelope versions are fixed; encode application payload bytes externally"
416
- );
417
- }
418
- if ("signer" in options || "fundingSigner" in options) {
419
- throw new Error(
420
- "Invisi does not accept signer defaults; export a signing request and attach the external signature"
421
- );
422
- }
423
- this.#options = {
424
- ...options,
425
- ...options.gasBenchmarks ? { gasBenchmarks: copyGasBenchmarks(options.gasBenchmarks) } : {}
426
- };
427
- Object.freeze(this);
428
- }
429
- async connect(options) {
430
- return new WalletSession(await this.resolve(options));
431
- }
432
- async resolve(options) {
433
- const allowed = [
434
- "wallet",
435
- "protocol",
436
- "namespace",
437
- "viewing",
438
- "authorization",
439
- "signer",
440
- "fundingSigner"
441
- ];
442
- for (const key of Object.keys(options)) {
443
- if (!allowed.includes(key))
444
- throw new Error(
445
- `Unknown connection option: ${key}; configure infrastructure on Invisi`
446
- );
447
- }
448
- const selected = { ...options };
449
- const combined = { ...this.#options, ...selected };
450
- const { provider, ...runtime } = combined;
451
- const reader = combined.chainReader ?? (provider ? new EthersChainReader(provider) : void 0);
452
- if (!reader) throw new MissingCapabilityError2("chain reader");
453
- if (provider && (await provider.getNetwork()).chainId !== selected.protocol.context.chainId)
454
- throw new Error("provider deployment chain mismatch");
455
- await selected.protocol.validateDeployment(reader);
456
- if (runtime.protocolRpc instanceof ProtocolRpcClient) {
457
- runtime.protocolRpc = runtime.protocolRpc.withReader(reader);
458
- }
459
- const fundingVerifier = combined.fundingVerifier ?? (provider ? new EthersFundingVerifier(provider, selected.protocol.context.chainId) : void 0);
460
- return configureRecipientInfrastructure({
461
- ...runtime,
462
- ...provider ? { provider } : {},
463
- chainReader: reader,
464
- ...fundingVerifier ? { fundingVerifier } : {},
465
- protocol: selected.protocol
466
- });
467
- }
468
- /** Discover the selected chain's registry and prepare an independent signing request. */
469
- async prepareRegistration(options) {
470
- return prepareWithErrors("prepareRegistration", async () => {
471
- for (const key of Object.keys(options)) {
472
- if (!["wallet", "update", "deadline"].includes(key)) {
473
- throw new Error(`Unknown registration option: ${key}`);
474
- }
475
- }
476
- this.assertPublicIdentity(options);
477
- const selected = { ...options };
478
- const { provider, protocolRpc } = this.#options;
479
- if (!provider) throw new MissingCapabilityError2("ethers provider");
480
- if (!(protocolRpc instanceof ProtocolRpcClient)) {
481
- throw new MissingCapabilityError2("Protocol RPC deployment discovery");
482
- }
483
- const chainId = (await provider.getNetwork()).chainId;
484
- const deployment = await protocolRpc.getDeployment(chainId);
485
- if (!deployment.registry) {
486
- throw new MissingCapabilityError2("receiving-key registry deployment");
487
- }
488
- const registry = new AddressRegistry(
489
- { chainId, registryAddress: deployment.registry.address },
490
- { expectedRuntimeCodeHash: deployment.registry.runtimeCodeHash }
491
- );
492
- return registry.prepareRegistration({ ...selected, provider });
493
- });
494
- }
495
- assertPublicIdentity(options) {
496
- const { wallet } = options;
497
- if (!(wallet instanceof PublicWallet) || ["getViewer", "getDetector", "exportAuthorization"].some((key) => key in wallet)) {
498
- throw new Error(
499
- "Independent preparation requires a PublicWallet without private capabilities"
500
- );
501
- }
502
- for (const key of ["authorization", "signer", "fundingSigner", "viewing"]) {
503
- if (key in options)
504
- throw new Error(`Independent preparation does not accept capability: ${key}`);
505
- }
506
- }
507
- resolveExplicit(options) {
508
- this.assertPublicIdentity(options);
509
- return this.resolve({ wallet: options.wallet, protocol: options.protocol });
510
- }
511
- async prepareTransfer(options) {
512
- return prepareWithErrors("prepareTransfer", async () => {
513
- const { wallet, protocol, ...input } = options;
514
- const request = structuredClone(input);
515
- const runtime = await this.resolveExplicit(options);
516
- const preparation = explicitPreparation(runtime, request, request.tokenAddress);
517
- return preparation.prepareTransfer(request);
518
- });
519
- }
520
- async prepareShield(options) {
521
- return prepareWithErrors("prepareShield", async () => {
522
- const { wallet, protocol, ...input } = options;
523
- const request = structuredClone(input);
524
- const runtime = await this.resolveExplicit(options);
525
- const preparation = explicitPreparation(runtime, {}, request.tokenAddress);
526
- return preparation.prepareShield(request);
527
- });
528
- }
529
- async prepareCreateCredit(options) {
530
- return prepareWithErrors("prepareCreateCredit", async () => {
531
- const { wallet, protocol, ...input } = options;
532
- const request = structuredClone(input);
533
- const runtime = await this.resolveExplicit(options);
534
- const preparation = explicitPreparation(runtime, request, request.tokenAddress);
535
- return preparation.prepareCreateCredit(request);
536
- });
537
- }
538
- async prepareDrawCredit(options) {
539
- return prepareWithErrors("prepareDrawCredit", async () => {
540
- const { wallet, protocol, ...input } = options;
541
- const request = structuredClone(input);
542
- const runtime = await this.resolveExplicit(options);
543
- const preparation = explicitPreparation(runtime, request, request.tokenAddress);
544
- return preparation.prepareDrawCredit(request);
545
- });
546
- }
547
- async prepareTopUpCredit(options) {
548
- return prepareWithErrors("prepareTopUpCredit", async () => {
549
- const { wallet, protocol, ...input } = options;
550
- const request = structuredClone(input);
551
- const runtime = await this.resolveExplicit(options);
552
- const preparation = explicitPreparation(runtime, request, request.tokenAddress);
553
- return preparation.prepareTopUpCredit(request);
554
- });
555
- }
556
- async prepareCancelCredit(options) {
557
- return prepareWithErrors("prepareCancelCredit", async () => {
558
- const { wallet, protocol, ...input } = options;
559
- const request = structuredClone(input);
560
- const runtime = await this.resolveExplicit(options);
561
- const preparation = explicitPreparation(runtime, request, request.tokenAddress);
562
- return preparation.prepareCancelCredit(request);
152
+ async verify(disclosure, value) {
153
+ const evidence = await this.readOutput(value);
154
+ const result = await disclosure.verify(evidence.output);
155
+ const confirmations = await this.barrier(evidence.block);
156
+ return Object.freeze({
157
+ result: Object.freeze(result),
158
+ block: evidence.block,
159
+ confirmations,
160
+ output: Object.freeze({
161
+ ...evidence.reference,
162
+ commitment: evidence.output.outputCommitment
163
+ })
563
164
  });
564
165
  }
565
166
  toJSON() {
566
- return { invisi: true };
167
+ return { disclosureVerifier: true, confirmations: this.#confirmations.toString() };
567
168
  }
568
169
  [/* @__PURE__ */ Symbol.for("nodejs.util.inspect.custom")]() {
569
170
  return this.toJSON();
570
171
  }
571
172
  };
572
173
 
573
- // src/index.ts
574
- import {
575
- fundsV1,
576
- creditV1,
577
- decodeDeliveryHeader,
578
- Wallet,
579
- PublicWallet as PublicWallet2,
580
- ViewingCapability,
581
- Detector,
582
- NullifierCapability,
583
- WalletAuthorization,
584
- MissingCapabilityError as MissingCapabilityError3,
585
- Disclosure,
586
- encodeMemo,
587
- decodeMemoText,
588
- MAX_MEMO_BYTES,
589
- computeCreditNullifier,
590
- creditPeriods
591
- } from "@invisi-labs/core";
592
- import { RegistrationOperation } from "@invisi-labs/protocol";
593
-
594
- // src/Protocol.ts
595
- import { Protocol as BaseProtocol } from "@invisi-labs/protocol";
596
- import { getAddress as getAddress3 } from "ethers";
597
-
598
- // src/broadcaster/index.ts
599
- import { AbiCoder, getAddress as getAddress2, Interface, keccak256, verifyTypedData } from "ethers";
600
- import { invisiProtocolAbi } from "@invisi-labs/protocol/contracts";
601
- var broadcasterQuoteTypes = {
602
- Quote: [
603
- { name: "token", type: "address" },
604
- { name: "feeRecipient", type: "address" },
605
- { name: "feeAmount", type: "uint256" },
606
- { name: "deadline", type: "uint256" },
607
- { name: "maxGas", type: "uint256" },
608
- { name: "requestHash", type: "bytes32" }
609
- ]
610
- };
611
- function broadcasterQuoteDomain(trust) {
612
- return {
613
- name: "InvisiBroadcaster",
614
- version: "1",
615
- chainId: trust.chainId,
616
- verifyingContract: getAddress2(trust.protocol)
617
- };
618
- }
619
- var supportedMethods = ["transact", "shield", "createCredit", "topUpCredit", "cancelCredit"];
620
- var intentAbi = invisiProtocolAbi.filter(
621
- (item) => item.type === "function" && supportedMethods.includes(item.name) && (item.name !== "transact" || item.inputs[1]?.type === "tuple")
622
- );
623
- var broadcasterTransactInterface = new Interface(intentAbi);
624
- function decodeBroadcasterIntent(data) {
625
- const parsed = broadcasterTransactInterface.parseTransaction({ data });
626
- if (!parsed || !supportedMethods.includes(parsed.name))
627
- throw new Error("unsupported broadcaster method");
628
- const fragment = parsed.fragment;
629
- const decoded = parsed.args;
630
- if (broadcasterTransactInterface.encodeFunctionData(fragment, decoded).toLowerCase() !== data.toLowerCase())
631
- throw new Error("noncanonical broadcaster calldata");
632
- const method = parsed.name;
633
- const raw = method === "transact" ? decoded[1] : void 0;
634
- const params = raw?.params ?? (method === "shield" ? decoded[0] : method === "topUpCredit" ? decoded[1].creation : decoded[1]);
635
- const funding = raw?.funding ?? (method === "shield" ? decoded[3] : ["createCredit", "topUpCredit"].includes(method) ? decoded[3] : void 0);
636
- const deliveries = raw?.deliveries ?? (method === "shield" ? decoded[2] : ["createCredit", "topUpCredit"].includes(method) ? decoded[2] : []);
637
- return {
638
- fragment,
639
- method,
640
- args: decoded,
641
- proof: method === "shield" ? "0x" : decoded[0],
642
- request: { params, funding, deliveries }
643
- };
644
- }
645
- function broadcasterRequestHash(data) {
646
- const { fragment, method, args } = decodeBroadcasterIntent(data);
647
- const copy = (value) => Array.isArray(value) ? value.map(copy) : value;
648
- const canonical = copy(args);
649
- if (method !== "shield") canonical[0] = "0x";
650
- if (method === "transact") canonical[1][2][4] = "0x";
651
- else if (method !== "cancelCredit") canonical[3][4] = "0x";
652
- const calldata = broadcasterTransactInterface.encodeFunctionData(fragment, canonical);
653
- return keccak256(
654
- AbiCoder.defaultAbiCoder().encode(
655
- ["string", "string", "bytes"],
656
- ["InvisiBroadcasterIntentV1", method, calldata]
657
- )
658
- );
659
- }
660
- function verifyBroadcasterQuote(signed, trust, data, now = BigInt(Math.floor(Date.now() / 1e3))) {
661
- const { quote } = signed;
662
- const { request } = decodeBroadcasterIntent(data);
663
- const p = request.params;
664
- if (quote.requestHash !== broadcasterRequestHash(data) || getAddress2(quote.token) !== getAddress2(p.token) || getAddress2(quote.feeRecipient) !== getAddress2(p.feeRecipient) || BigInt(quote.feeAmount) !== p.feeAmount || BigInt(quote.deadline) > p.deadline || BigInt(quote.deadline) < now || BigInt(quote.maxGas) <= 0n || BigInt(quote.feeAmount) <= 0n || BigInt(quote.feeAmount) >= 1n << 128n)
665
- throw new Error("quote does not match final intent or bounds");
666
- if (getAddress2(
667
- verifyTypedData(
668
- broadcasterQuoteDomain(trust),
669
- broadcasterQuoteTypes,
670
- quote,
671
- signed.signature
672
- )
673
- ) !== getAddress2(trust.signer))
674
- throw new Error("untrusted quote signature");
675
- return quote;
174
+ // src/protocol/connections.ts
175
+ var connections = /* @__PURE__ */ new WeakMap();
176
+ function rememberProtocolConnection(protocol, provider, protocolRpc) {
177
+ connections.set(protocol, { provider, protocolRpc });
676
178
  }
677
- function checkedStatus(value, id) {
678
- const status = value;
679
- if (!status || status.id !== id || !["pending", "submitted", "confirmed", "reverted", "uncertain"].includes(status.state) || status.transactionHash !== void 0 && !/^0x[0-9a-fA-F]{64}$/.test(status.transactionHash))
680
- throw new Error("invalid broadcaster status");
681
- return status;
179
+ function protocolConnection(protocol) {
180
+ return connections.get(protocol);
682
181
  }
683
- var BroadcasterClient = class {
684
- configuration;
685
- url;
686
- transport;
687
- requestId = 0;
688
- constructor(configuration, transport) {
689
- this.configuration = Object.freeze({
690
- endpoint: configuration.endpoint,
691
- quoteSigner: getAddress2(configuration.quoteSigner),
692
- feeRecipient: getAddress2(configuration.feeRecipient)
693
- });
694
- if (BigInt(this.configuration.quoteSigner) === 0n || BigInt(this.configuration.feeRecipient) === 0n)
695
- throw new Error("invalid broadcaster configuration");
696
- this.url = configuration.endpoint;
697
- const base = new URL(this.url);
698
- if (base.protocol !== "https:" || base.username || base.password)
699
- throw new Error("broadcaster requires HTTPS");
700
- this.transport = transport ?? (async (request) => {
701
- const response = await fetch(base, {
702
- redirect: "error",
703
- method: "POST",
704
- body: JSON.stringify(request),
705
- headers: { "content-type": "application/json" },
706
- signal: AbortSignal.timeout(3e4)
707
- });
708
- if (!response.ok) throw new Error(`Broadcaster HTTP ${response.status}`);
709
- if (!response.body) throw new Error("empty broadcaster response");
710
- const reader = response.body.getReader();
711
- const chunks = [];
712
- let size = 0;
713
- try {
714
- while (true) {
715
- const result = await reader.read();
716
- if (result.done) break;
717
- size += result.value.length;
718
- if (size > 65536) throw new Error("broadcaster response too large");
719
- chunks.push(result.value);
720
- }
721
- } finally {
722
- await reader.cancel();
723
- }
724
- const bytes = new Uint8Array(size);
725
- let offset = 0;
726
- for (const chunk of chunks) {
727
- bytes.set(chunk, offset);
728
- offset += chunk.length;
729
- }
730
- return JSON.parse(new TextDecoder().decode(bytes));
731
- });
732
- }
733
- async call(method, params) {
734
- const id = ++this.requestId;
735
- const response = await this.transport({
736
- jsonrpc: "2.0",
737
- id,
738
- method,
739
- params: [params]
740
- });
741
- if (!response || response.jsonrpc !== "2.0" || response.id !== id || Object.hasOwn(response, "result") === Object.hasOwn(response, "error"))
742
- throw new Error("invalid broadcaster JSON-RPC response");
743
- if (response.error) {
744
- if (!Number.isInteger(response.error.code) || typeof response.error.message !== "string")
745
- throw new Error("invalid broadcaster JSON-RPC error");
746
- throw new BroadcasterRpcError(response.error.code, response.error.message);
747
- }
748
- return response.result;
749
- }
750
- async fee(token, data) {
751
- const fee = await this.call("broadcaster_getFee", {
752
- token: getAddress2(token),
753
- ...data ? { data } : {}
754
- });
755
- if (!fee || getAddress2(fee.feeRecipient) !== this.configuration.feeRecipient || !/^\d+$/.test(fee.feeAmount) || BigInt(fee.feeAmount) <= 0n || BigInt(fee.feeAmount) >= 1n << 128n || !/^\d+$/.test(fee.maxGas) || BigInt(fee.maxGas) <= 0n)
756
- throw new Error("invalid broadcaster fee");
757
- return fee;
758
- }
759
- async quote(data, context) {
760
- const signed = await this.call("broadcaster_getQuote", {
761
- data
762
- });
763
- verifyBroadcasterQuote(
764
- signed,
765
- { ...context, signer: this.configuration.quoteSigner },
766
- data
767
- );
768
- if (getAddress2(signed.quote.feeRecipient) !== this.configuration.feeRecipient)
769
- throw new Error("unregistered fee recipient");
770
- return { quote: signed.quote, signature: signed.signature, context: { ...context } };
771
- }
772
- async submit(data, signed, idempotencyKey) {
773
- if (!signed.context) throw new Error("missing verified quote context");
774
- verifyBroadcasterQuote(
775
- signed,
776
- { ...signed.context, signer: this.configuration.quoteSigner },
777
- data,
778
- 0n
779
- );
780
- if (getAddress2(signed.quote.feeRecipient) !== this.configuration.feeRecipient)
781
- throw new Error("unregistered fee recipient");
782
- return checkedStatus(
783
- await this.call("broadcaster_submitTransaction", {
784
- data,
785
- quote: signed.quote,
786
- signature: signed.signature,
787
- idempotencyKey
788
- }),
789
- idempotencyKey
790
- );
791
- }
792
- async status(id) {
793
- return checkedStatus(await this.call("broadcaster_getTransactionStatus", { id }), id);
794
- }
795
- };
796
- var BroadcasterRpcError = class extends Error {
797
- constructor(code, message) {
798
- super(message);
799
- this.code = code;
800
- this.name = "BroadcasterRpcError";
801
- }
802
- };
803
182
 
804
- // src/Protocol.ts
183
+ // src/protocol/Protocol.ts
184
+ import { Protocol as BaseProtocol } from "@invisi-labs/rpc";
185
+ import { getAddress } from "ethers";
805
186
  var sources = /* @__PURE__ */ new WeakMap();
806
187
  var Protocol = class _Protocol extends BaseProtocol {
807
188
  static async fromRpc(rpc, provider, options = {}) {
@@ -809,11 +190,19 @@ var Protocol = class _Protocol extends BaseProtocol {
809
190
  const protocol = new _Protocol({ deployment: verified.deployment });
810
191
  if (rpc.getBroadcasters)
811
192
  sources.set(protocol, rpc);
193
+ if (rpc.withReader)
194
+ rememberProtocolConnection(
195
+ protocol,
196
+ provider,
197
+ rpc
198
+ );
812
199
  return protocol;
813
200
  }
814
- /** Returns enabled services that answer a token fee probe; final quotes are checked by prepared. */
201
+ /** Discover token support without disclosing a transaction or requesting a price. */
815
202
  async getBroadcasters(options) {
816
- const token = getAddress3(options.tokenAddress);
203
+ options.signal?.throwIfAborted();
204
+ const token = getAddress(options.tokenAddress);
205
+ if (BigInt(token) === 0n) throw Error("Invalid Broadcaster token address");
817
206
  if (!this.deployment.broadcasterRegistry)
818
207
  throw new Error("broadcaster registry is not configured");
819
208
  const rpc = sources.get(this);
@@ -823,9 +212,11 @@ var Protocol = class _Protocol extends BaseProtocol {
823
212
  let block;
824
213
  const seen = /* @__PURE__ */ new Set();
825
214
  do {
215
+ options.signal?.throwIfAborted();
826
216
  const page = await rpc.getBroadcasters({
827
217
  enabled: true,
828
218
  limit: 100,
219
+ ...options.signal ? { signal: options.signal } : {},
829
220
  ...after ? { after } : {},
830
221
  ...block ? { block } : {}
831
222
  });
@@ -834,14 +225,28 @@ var Protocol = class _Protocol extends BaseProtocol {
834
225
  page.broadcasters.map(async (entry) => {
835
226
  if (!entry.enabled) return void 0;
836
227
  try {
837
- const client = new BroadcasterClient(entry);
838
- await client.fee(token);
228
+ const client = new BroadcasterClient({
229
+ endpoint: entry.endpoint,
230
+ quoteSigner: entry.quoteSigner,
231
+ feeRecipient: entry.feeRecipient
232
+ });
233
+ if (!await client.supportsToken(
234
+ token,
235
+ {
236
+ chainId: this.context.chainId,
237
+ protocol: this.context.protocolAddress
238
+ },
239
+ options
240
+ ))
241
+ return void 0;
839
242
  return client;
840
243
  } catch {
244
+ options.signal?.throwIfAborted();
841
245
  return void 0;
842
246
  }
843
247
  })
844
248
  );
249
+ options.signal?.throwIfAborted();
845
250
  clients.push(
846
251
  ...compatible.filter((client) => client !== void 0)
847
252
  );
@@ -854,22 +259,14 @@ var Protocol = class _Protocol extends BaseProtocol {
854
259
  };
855
260
 
856
261
  // src/index.ts
857
- import { ProtocolRpcError, MAX_UPDATE_BLOCKS } from "@invisi-labs/rpc";
262
+ import { MAX_UPDATE_BLOCKS, ProtocolRpcError } from "@invisi-labs/rpc";
858
263
 
859
- // src/ProtocolRpcClient.ts
860
- import { ProtocolRpcClient as RpcClient } from "@invisi-labs/rpc";
861
- var ProtocolRpcClient2 = class extends RpcClient {
862
- withProvider(provider) {
863
- return this.withReader(new EthersChainReader(provider));
864
- }
865
- };
866
-
867
- // src/BroadcasterRegistry.ts
868
- import { getAddress as getAddress4, Interface as Interface2, keccak256 as keccak2562 } from "ethers";
264
+ // src/protocol/BroadcasterRegistry.ts
869
265
  import { broadcasterRegistryAbi } from "@invisi-labs/protocol/contracts";
870
- var abi = new Interface2(broadcasterRegistryAbi);
266
+ import { getAddress as getAddress2, Interface, keccak256 } from "ethers";
267
+ var abi = new Interface(broadcasterRegistryAbi);
871
268
  function address(value) {
872
- const normalized = getAddress4(value);
269
+ const normalized = getAddress2(value);
873
270
  if (BigInt(normalized) === 0n) throw new Error("zero broadcaster address");
874
271
  return normalized;
875
272
  }
@@ -912,7 +309,7 @@ var BroadcasterRegistry = class {
912
309
  if ((await provider.getNetwork()).chainId !== chainId) {
913
310
  throw new Error("broadcaster registry chain mismatch");
914
311
  }
915
- if (keccak2562(await provider.getCode(this.address)).toLowerCase() !== runtimeCodeHash.toLowerCase()) {
312
+ if (keccak256(await provider.getCode(this.address)).toLowerCase() !== runtimeCodeHash.toLowerCase()) {
916
313
  throw new Error("broadcaster registry runtime code mismatch");
917
314
  }
918
315
  }
@@ -952,7 +349,7 @@ var BroadcasterRegistry = class {
952
349
  toBlock: end
953
350
  });
954
351
  for (const log of logs) {
955
- if (getAddress4(log.address) !== this.address)
352
+ if (getAddress2(log.address) !== this.address)
956
353
  throw new Error("registry event address mismatch");
957
354
  const event = abi.parseLog(log);
958
355
  if (!event || event.name !== "BroadcasterSet")
@@ -970,116 +367,254 @@ var BroadcasterRegistry = class {
970
367
  }
971
368
  };
972
369
 
973
- // src/integrations/encrypted-codec.ts
974
- var EncryptedWalletCodec = class _EncryptedWalletCodec {
975
- #key;
976
- constructor(key) {
977
- this.#key = key;
978
- }
979
- static fromKey(key) {
980
- if (key.type !== "secret" || key.algorithm.name !== "AES-GCM" || key.algorithm.length !== 256 || !key.usages.includes("encrypt") || !key.usages.includes("decrypt"))
981
- throw new Error("wallet record key must support AES-256-GCM encryption and decryption");
982
- return new _EncryptedWalletCodec(key);
370
+ // src/protocol/ProtocolRpcClient.ts
371
+ import { ProtocolRpcClient as RpcClient } from "@invisi-labs/rpc";
372
+ var ProtocolRpcClient = class extends RpcClient {
373
+ withProvider(provider) {
374
+ return this.withReader(new EthersChainReader(provider));
983
375
  }
984
- static async fromBytes(key) {
985
- if (key.length !== 32) throw new Error("wallet record encryption requires 32 key bytes");
986
- const copy = Uint8Array.from(key);
987
- try {
988
- return _EncryptedWalletCodec.fromKey(
989
- await crypto.subtle.importKey("raw", copy, { name: "AES-GCM" }, false, [
990
- "encrypt",
991
- "decrypt"
992
- ])
993
- );
994
- } finally {
995
- copy.fill(0);
996
- }
376
+ };
377
+
378
+ // src/index.ts
379
+ import { getDocumentedGasReference } from "@invisi-labs/protocol";
380
+
381
+ // src/payment-note/PaymentNote.ts
382
+ import { computeNoteIdentity, computeNoteCommitment, Wallet } from "@invisi-labs/primitives";
383
+
384
+ // src/payment-note/verification.ts
385
+ import {
386
+ poseidonHash,
387
+ TREE_DEPTH,
388
+ computeNoteNullifier
389
+ } from "@invisi-labs/primitives";
390
+ import { getAddress as getAddress3 } from "ethers";
391
+ function verificationOptions(protocol, options = {}) {
392
+ const connected = protocolConnection(protocol);
393
+ const resolved = { ...connected, ...options };
394
+ if (!("protocolRpc" in resolved) || !resolved.protocolRpc || !("chainReader" in resolved) && !("provider" in resolved))
395
+ throw new Error(
396
+ "Payment note verification requires Protocol.fromRpc or explicit chain and Protocol RPC connections"
397
+ );
398
+ return resolved;
399
+ }
400
+ function sameBlock(a, b) {
401
+ return a.number === b.number && a.hash === b.hash && a.parentHash === b.parentHash && a.timestamp === b.timestamp;
402
+ }
403
+ function asNote(data) {
404
+ return {
405
+ chainId: data.chainId,
406
+ protocolAddress: BigInt(data.protocolAddress),
407
+ tokenAddress: BigInt(data.tokenAddress),
408
+ amount: data.amount,
409
+ ownerAddress: data.ownerAddress,
410
+ nonce: data.nonce
411
+ };
412
+ }
413
+ async function verifyPaymentNote(data, protocol, options, wallet) {
414
+ if (data.chainId !== protocol.context.chainId || getAddress3(data.protocolAddress) !== getAddress3(protocol.context.protocolAddress))
415
+ throw new Error("Payment note protocol context mismatch");
416
+ const confirmations = options.confirmations ?? 1;
417
+ if (!Number.isSafeInteger(confirmations) || confirmations < 1)
418
+ throw new RangeError("confirmations must be positive");
419
+ const reader = options.chainReader ?? new EthersChainReader(options.provider);
420
+ await protocol.validateDeployment(reader);
421
+ if (wallet && getAddress3(wallet.ownerAddress) !== getAddress3(data.ownerAddress))
422
+ throw new Error("Payment note owner mismatch");
423
+ const latest = await reader.getLatestBlockNumber();
424
+ const number = latest - BigInt(confirmations - 1);
425
+ if (number < protocol.deployment.protocol.fromBlock)
426
+ throw new Error("Insufficient confirmed blocks");
427
+ const rpc = options.protocolRpc.withReader(reader);
428
+ const status = await rpc.getStatus(protocol.context);
429
+ if (!status.indexedHead) throw new Error("Protocol RPC has no indexed blocks");
430
+ const indexedNumber = status.indexedHead.number < number ? status.indexedHead.number : number;
431
+ if (indexedNumber < protocol.deployment.protocol.fromBlock)
432
+ throw new Error("Protocol RPC has not reached the deployment");
433
+ const checkedAt = await reader.getBlock(indexedNumber);
434
+ if (checkedAt.number !== indexedNumber)
435
+ throw new Error("Payment note confirmation block mismatch");
436
+ const result = await rpc.getNote(protocol.context, data.commitment, checkedAt);
437
+ if (!result) throw new Error("Payment note is not indexed at the requested confirmed block");
438
+ const { output, membership } = result;
439
+ 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)
440
+ throw new Error("Payment note membership metadata mismatch");
441
+ let root = BigInt(data.commitment);
442
+ for (let level = 0; level < membership.path.length; level++) {
443
+ const sibling = membership.path[level];
444
+ root = BigInt(
445
+ await poseidonHash(output.leafIndex >> level & 1 ? [sibling, root] : [root, sibling])
446
+ );
997
447
  }
998
- aad(context) {
999
- if (!Number.isSafeInteger(context.revision) || context.revision < 1)
1000
- throw new Error("invalid encrypted record revision");
1001
- return new TextEncoder().encode(
1002
- `Invisi.WalletRecord.v1\0${context.revision}\0${context.key}`
448
+ if (root !== BigInt(membership.root) || !await reader.isKnownRoot(protocol.context, membership.treeId, membership.root, checkedAt))
449
+ throw new Error("Payment note membership root is not accepted on chain");
450
+ if (!sameBlock(output.block, await reader.getBlock(output.block.number)) || !sameBlock(membership.block, await reader.getBlock(membership.block.number)))
451
+ throw new Error("Payment note block is no longer canonical");
452
+ let spendabilityCheckedAt;
453
+ if (wallet) {
454
+ const current = await reader.getBlock(await reader.getLatestBlockNumber());
455
+ const nullifier = await computeNoteNullifier(
456
+ data.chainId,
457
+ BigInt(data.protocolAddress),
458
+ data.nonce,
459
+ data.commitment
1003
460
  );
461
+ const spent = await reader.isSpent(protocol.context, [nullifier], current);
462
+ if (spent.length !== 1 || spent[0] !== false)
463
+ throw new Error("Payment note is already spent");
464
+ if (!await reader.isKnownRoot(
465
+ protocol.context,
466
+ membership.treeId,
467
+ membership.root,
468
+ current
469
+ ))
470
+ throw new Error("Payment note membership root is no longer spendable");
471
+ if (!sameBlock(current, await reader.getBlock(current.number)))
472
+ throw new Error("Spendability block reorganized");
473
+ spendabilityCheckedAt = current;
474
+ }
475
+ if (!sameBlock(checkedAt, await reader.getBlock(checkedAt.number)))
476
+ throw new Error("Payment note verification block reorganized");
477
+ return {
478
+ input: {
479
+ note: asNote(data),
480
+ commitment: data.commitment,
481
+ treeId: output.treeId,
482
+ leafIndex: output.leafIndex
483
+ },
484
+ membership,
485
+ checkedAt,
486
+ ...spendabilityCheckedAt ? { spendabilityCheckedAt } : {}
487
+ };
488
+ }
489
+
490
+ // src/payment-note/PaymentNote.ts
491
+ var verification = /* @__PURE__ */ Symbol("PaymentNote verification");
492
+ var VerifiedPaymentNote = class {
493
+ #membership;
494
+ #checkedAt;
495
+ constructor(token, membership, checkedAt) {
496
+ if (token !== verification) throw new Error("Payment note verification required");
497
+ this.#membership = structuredClone(membership);
498
+ this.#checkedAt = structuredClone(checkedAt);
499
+ }
500
+ get membership() {
501
+ return structuredClone(this.#membership);
502
+ }
503
+ get checkedAt() {
504
+ return structuredClone(this.#checkedAt);
1004
505
  }
1005
- async encode(state, context) {
1006
- const iv = crypto.getRandomValues(new Uint8Array(12)), plain = Uint8Array.from(serializeState(state));
1007
- try {
1008
- const encrypted = new Uint8Array(
1009
- await crypto.subtle.encrypt(
1010
- { name: "AES-GCM", iv, additionalData: this.aad(context) },
1011
- this.#key,
1012
- plain
1013
- )
1014
- );
1015
- const out = new Uint8Array(13 + encrypted.length);
1016
- out[0] = 1;
1017
- out.set(iv, 1);
1018
- out.set(encrypted, 13);
1019
- return out;
1020
- } finally {
1021
- plain.fill(0);
1022
- }
506
+ };
507
+ var SpendablePaymentNote = class extends VerifiedPaymentNote {
508
+ #input;
509
+ #spendabilityCheckedAt;
510
+ constructor(token, input, membership, checkedAt, spendabilityCheckedAt) {
511
+ super(token, membership, checkedAt);
512
+ this.#input = structuredClone(input);
513
+ this.#spendabilityCheckedAt = structuredClone(spendabilityCheckedAt);
514
+ Object.freeze(this);
1023
515
  }
1024
- async decode(data, context) {
1025
- if (data.length < 29 || data[0] !== 1)
1026
- throw new Error("wallet record authentication failed");
1027
- let plain;
1028
- try {
1029
- plain = new Uint8Array(
1030
- await crypto.subtle.decrypt(
1031
- {
1032
- name: "AES-GCM",
1033
- iv: Uint8Array.from(data.subarray(1, 13)),
1034
- additionalData: this.aad(context)
1035
- },
1036
- this.#key,
1037
- Uint8Array.from(data.subarray(13))
1038
- )
1039
- );
1040
- } catch {
1041
- throw new Error("wallet record authentication failed");
1042
- }
1043
- try {
1044
- return deserializeState(plain);
1045
- } finally {
1046
- plain.fill(0);
1047
- }
516
+ get spendabilityCheckedAt() {
517
+ return structuredClone(this.#spendabilityCheckedAt);
1048
518
  }
1049
- toJSON() {
1050
- return { walletRecordCodec: "AES-256-GCM" };
519
+ toInput() {
520
+ return structuredClone(this.#input);
1051
521
  }
1052
- [/* @__PURE__ */ Symbol.for("nodejs.util.inspect.custom")]() {
1053
- return this.toJSON();
522
+ };
523
+ var PaymentNote = class _PaymentNote {
524
+ chainId;
525
+ protocolAddress;
526
+ tokenAddress;
527
+ amount;
528
+ ownerAddress;
529
+ nonce;
530
+ commitment;
531
+ ipn;
532
+ constructor(data, ipn) {
533
+ this.chainId = data.chainId;
534
+ this.protocolAddress = data.protocolAddress;
535
+ this.tokenAddress = data.tokenAddress;
536
+ this.amount = data.amount;
537
+ this.ownerAddress = data.ownerAddress;
538
+ this.nonce = data.nonce;
539
+ this.commitment = data.commitment;
540
+ this.ipn = ipn;
541
+ Object.freeze(this);
542
+ }
543
+ static async from(ipn) {
544
+ const data = decodePaymentNote(ipn);
545
+ const identity = await computeNoteIdentity(BigInt(data.ownerAddress), data.nonce);
546
+ const commitment = await computeNoteCommitment(
547
+ data.chainId,
548
+ BigInt(data.protocolAddress),
549
+ identity,
550
+ BigInt(data.tokenAddress),
551
+ data.amount
552
+ );
553
+ if (commitment !== data.commitment) throw new Error("Payment note commitment mismatch");
554
+ return new _PaymentNote(data, encodePaymentNote(data));
555
+ }
556
+ async verify(protocol, options = {}) {
557
+ const result = await verifyPaymentNote(
558
+ this.data(),
559
+ protocol,
560
+ verificationOptions(protocol, options)
561
+ );
562
+ const verified = new VerifiedPaymentNote(verification, result.membership, result.checkedAt);
563
+ Object.freeze(verified);
564
+ return verified;
565
+ }
566
+ async verifySpendable(protocol, wallet, options = {}) {
567
+ if (!(wallet instanceof Wallet)) throw new Error("A public Wallet is required");
568
+ const result = await verifyPaymentNote(
569
+ this.data(),
570
+ protocol,
571
+ verificationOptions(protocol, options),
572
+ wallet
573
+ );
574
+ return new SpendablePaymentNote(
575
+ verification,
576
+ result.input,
577
+ result.membership,
578
+ result.checkedAt,
579
+ result.spendabilityCheckedAt
580
+ );
581
+ }
582
+ data() {
583
+ const { chainId, protocolAddress, tokenAddress, amount, ownerAddress, nonce, commitment } = this;
584
+ return { chainId, protocolAddress, tokenAddress, amount, ownerAddress, nonce, commitment };
1054
585
  }
1055
586
  };
587
+
588
+ // src/index.ts
589
+ import { authenticatedDecryptor, DeliveryDecryptionError } from "@invisi-labs/primitives";
1056
590
  export {
1057
591
  AddressRegistry,
1058
592
  BroadcasterClient,
1059
593
  BroadcasterRegistry,
1060
594
  BroadcasterRpcError,
595
+ DeliveryDecryptionError,
1061
596
  Detector,
1062
597
  Disclosure,
1063
598
  DisclosureVerifier,
1064
- EncryptedWalletCodec,
1065
599
  Invisi,
1066
600
  InvisiError,
1067
601
  MAX_MEMO_BYTES,
1068
602
  MAX_UPDATE_BLOCKS,
1069
- MissingCapabilityError3 as MissingCapabilityError,
603
+ MissingCapabilityError,
1070
604
  NoteOutput,
1071
605
  NullifierCapability,
1072
- Operation,
606
+ PaymentNote,
1073
607
  PreparedTransaction,
1074
608
  Protocol,
1075
- ProtocolRpcClient2 as ProtocolRpcClient,
609
+ ProtocolRpcClient,
1076
610
  ProtocolRpcError,
1077
- PublicWallet2 as PublicWallet,
1078
611
  RegistrationOperation,
612
+ SpendablePaymentNote,
613
+ TransactionOutput,
614
+ VerifiedPaymentNote,
1079
615
  ViewingCapability,
1080
- Wallet,
1081
- WalletAuthorization,
1082
- WalletSession,
616
+ Wallet2 as Wallet,
617
+ authenticatedDecryptor,
1083
618
  broadcasterQuoteDomain,
1084
619
  broadcasterQuoteTypes,
1085
620
  broadcasterRequestHash,
@@ -1090,12 +625,15 @@ export {
1090
625
  decodeBroadcasterIntent,
1091
626
  decodeDeliveryHeader,
1092
627
  decodeMemoText,
628
+ decodePaymentNote,
629
+ decodePaymentRequest,
1093
630
  decryptCandidates,
1094
- defineGasBenchmarks,
1095
631
  encodeMemo,
632
+ encodePaymentNote,
633
+ encodePaymentRequest,
1096
634
  fundsV1,
635
+ getDocumentedGasReference,
1097
636
  readProtocolLogs,
1098
- verifyBroadcasterQuote,
1099
- walletBalance
637
+ verifyBroadcasterQuote
1100
638
  };
1101
639
  //# sourceMappingURL=index.js.map