@invisi-labs/sdk 1.1.5 → 1.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{operation-types-DIazgS2d.d.cts → NoteOutput-DRkVnwlx.d.cts} +140 -17
- package/dist/{operation-types-D7I405FX.d.ts → NoteOutput-DpDC6T2O.d.ts} +140 -17
- package/dist/broadcaster-preparation-QVL62KM6.js +128 -0
- package/dist/broadcaster-preparation-QVL62KM6.js.map +1 -0
- package/dist/{chunk-BE52B32Z.js → chunk-4SD3MW6N.js} +846 -1625
- package/dist/chunk-4SD3MW6N.js.map +1 -0
- package/dist/chunk-FL4TIGWW.js +1781 -0
- package/dist/chunk-FL4TIGWW.js.map +1 -0
- package/dist/{chunk-4HMM7ZKY.js → chunk-QC6TQR3R.js} +8 -8
- package/dist/chunk-QC6TQR3R.js.map +1 -0
- package/dist/ethers.cjs +7 -7
- package/dist/ethers.cjs.map +1 -1
- package/dist/ethers.d.cts +3 -3
- package/dist/ethers.d.ts +3 -3
- package/dist/ethers.js +1 -1
- package/dist/index.cjs +4520 -2902
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +273 -25
- package/dist/index.d.ts +273 -25
- package/dist/index.js +459 -52
- package/dist/index.js.map +1 -1
- package/dist/indexeddb.cjs +5812 -4570
- package/dist/indexeddb.cjs.map +1 -1
- package/dist/indexeddb.d.cts +2 -2
- package/dist/indexeddb.d.ts +2 -2
- package/dist/indexeddb.js +3 -2
- package/dist/indexeddb.js.map +1 -1
- package/dist/memory.cjs +5868 -4626
- package/dist/memory.cjs.map +1 -1
- package/dist/memory.d.cts +3 -3
- package/dist/memory.d.ts +3 -3
- package/dist/memory.js +3 -2
- package/dist/sqlite.cjs +5812 -4570
- package/dist/sqlite.cjs.map +1 -1
- package/dist/sqlite.d.cts +2 -2
- package/dist/sqlite.d.ts +2 -2
- package/dist/sqlite.js +3 -2
- package/dist/sqlite.js.map +1 -1
- package/dist/{state-AUnz7TG7.d.cts → state-C4HZFotm.d.cts} +1 -1
- package/dist/{state-AUnz7TG7.d.ts → state-C4HZFotm.d.ts} +1 -1
- package/dist/{store-BMN8kd0c.d.ts → store-8aBhnbGr.d.ts} +1 -1
- package/dist/{store-Cz75STRl.d.cts → store-YrbXb6CY.d.cts} +1 -1
- package/package.json +5 -5
- package/dist/chunk-4HMM7ZKY.js.map +0 -1
- package/dist/chunk-BE52B32Z.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
DisclosureVerifier,
|
|
3
|
-
InvisiError,
|
|
4
3
|
Operation,
|
|
5
|
-
TransactionPreparation,
|
|
6
4
|
WalletSession,
|
|
7
5
|
acceptFundingSignature,
|
|
8
6
|
acceptWalletSignature,
|
|
@@ -10,27 +8,33 @@ import {
|
|
|
10
8
|
decryptCandidates,
|
|
11
9
|
defineGasBenchmarks,
|
|
12
10
|
deserializeState,
|
|
13
|
-
encodeOperation,
|
|
14
11
|
estimateGasReference,
|
|
15
12
|
fundingSigningRequest,
|
|
16
13
|
operationPreview,
|
|
17
|
-
positiveAmount,
|
|
18
|
-
prepareWithErrors,
|
|
19
14
|
proveOperation,
|
|
20
|
-
publicProof,
|
|
21
15
|
readProtocolLogs,
|
|
22
|
-
requiresAuthorization,
|
|
23
16
|
sameStatement,
|
|
24
17
|
serializeState,
|
|
25
|
-
statement,
|
|
26
|
-
walletBalance,
|
|
27
18
|
walletSigningRequest
|
|
28
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-4SD3MW6N.js";
|
|
20
|
+
import {
|
|
21
|
+
InvisiError,
|
|
22
|
+
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";
|
|
29
34
|
import {
|
|
30
|
-
EthersAddressResolver,
|
|
31
35
|
EthersChainReader,
|
|
32
36
|
EthersFundingVerifier
|
|
33
|
-
} from "./chunk-
|
|
37
|
+
} from "./chunk-QC6TQR3R.js";
|
|
34
38
|
|
|
35
39
|
// src/explicit-preparation.ts
|
|
36
40
|
import { getAddress } from "ethers";
|
|
@@ -63,6 +67,29 @@ var PreparedTransaction = class _PreparedTransaction {
|
|
|
63
67
|
{ generation: 0, busy: false }
|
|
64
68
|
);
|
|
65
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
|
+
}
|
|
66
93
|
get id() {
|
|
67
94
|
return this.#saved.plan.id;
|
|
68
95
|
}
|
|
@@ -79,6 +106,15 @@ var PreparedTransaction = class _PreparedTransaction {
|
|
|
79
106
|
preview() {
|
|
80
107
|
return operationPreview(this.#saved.plan, this.status);
|
|
81
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
|
+
}
|
|
82
118
|
current() {
|
|
83
119
|
check(
|
|
84
120
|
!this.#lifecycle.busy && this.#lifecycle.generation === this.#saved.generation,
|
|
@@ -178,6 +214,10 @@ var PreparedTransaction = class _PreparedTransaction {
|
|
|
178
214
|
}
|
|
179
215
|
async prove(backendOrOptions = {}, suppliedOptions = {}) {
|
|
180
216
|
const options = { ..."prove" in backendOrOptions ? suppliedOptions : backendOrOptions };
|
|
217
|
+
check(
|
|
218
|
+
!this.#saved.broadcasterQuote || options.execution !== "aggregate",
|
|
219
|
+
"a broadcaster quote requires direct execution"
|
|
220
|
+
);
|
|
181
221
|
const plan = this.#saved.plan;
|
|
182
222
|
check(plan.kind !== "shield", "shield requires no proof");
|
|
183
223
|
if (requiresAuthorization(plan)) {
|
|
@@ -287,7 +327,7 @@ function explicitPreparation(options, data, tokenAddress) {
|
|
|
287
327
|
position(input);
|
|
288
328
|
positiveAmount(input.note.amount);
|
|
289
329
|
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.
|
|
330
|
+
if (input.note.chainId !== context.chainId || input.note.protocolAddress !== BigInt(context.protocolAddress) || input.note.ownerAddress !== options.wallet.ownerAddress)
|
|
291
331
|
throw new Error("explicit note identity or deployment mismatch");
|
|
292
332
|
inventory.notes[input.commitment] = { ...input, spent: "unspent" };
|
|
293
333
|
}
|
|
@@ -302,7 +342,7 @@ function explicitPreparation(options, data, tokenAddress) {
|
|
|
302
342
|
const scope = {
|
|
303
343
|
...context,
|
|
304
344
|
namespace: "explicit",
|
|
305
|
-
|
|
345
|
+
ownerAddress: options.wallet.ownerAddress
|
|
306
346
|
};
|
|
307
347
|
const {
|
|
308
348
|
store: _store,
|
|
@@ -320,18 +360,30 @@ function explicitPreparation(options, data, tokenAddress) {
|
|
|
320
360
|
reader: options.chainReader,
|
|
321
361
|
scope
|
|
322
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
|
+
);
|
|
323
375
|
return new TransactionPreparation({
|
|
324
376
|
...runtime,
|
|
325
377
|
explicitInputs: true,
|
|
326
378
|
...data.changeAddress ? { changeAddress: data.changeAddress } : {},
|
|
327
379
|
readInventory: async () => structuredClone(inventory),
|
|
328
|
-
complete: async (plan) => PreparedTransaction.prepare(runtime, plan)
|
|
380
|
+
complete: async (plan) => PreparedTransaction.prepare({ ...runtime, reprepare }, plan)
|
|
329
381
|
});
|
|
330
382
|
}
|
|
331
383
|
|
|
332
384
|
// src/Invisi.ts
|
|
333
385
|
import { ProtocolRpcClient } from "@invisi-labs/rpc";
|
|
334
|
-
import { MissingCapabilityError as MissingCapabilityError2, PublicWallet
|
|
386
|
+
import { MissingCapabilityError as MissingCapabilityError2, PublicWallet } from "@invisi-labs/core";
|
|
335
387
|
|
|
336
388
|
// src/AddressRegistry.ts
|
|
337
389
|
import { AddressRegistry as Registry } from "@invisi-labs/protocol";
|
|
@@ -354,6 +406,10 @@ var AddressRegistry = class extends Registry {
|
|
|
354
406
|
var Invisi = class {
|
|
355
407
|
#options;
|
|
356
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
|
+
);
|
|
357
413
|
if ("ciphertextCodecs" in options || "envelopeVersion" in options) {
|
|
358
414
|
throw new Error(
|
|
359
415
|
"SDK envelope versions are fixed; encode application payload bytes externally"
|
|
@@ -391,10 +447,7 @@ var Invisi = class {
|
|
|
391
447
|
}
|
|
392
448
|
const selected = { ...options };
|
|
393
449
|
const combined = { ...this.#options, ...selected };
|
|
394
|
-
const { provider,
|
|
395
|
-
if (addressLookup !== void 0 && addressLookup !== "chain" && addressLookup !== "protocol-rpc") {
|
|
396
|
-
throw new Error("Invalid recipient address lookup source");
|
|
397
|
-
}
|
|
450
|
+
const { provider, ...runtime } = combined;
|
|
398
451
|
const reader = combined.chainReader ?? (provider ? new EthersChainReader(provider) : void 0);
|
|
399
452
|
if (!reader) throw new MissingCapabilityError2("chain reader");
|
|
400
453
|
if (provider && (await provider.getNetwork()).chainId !== selected.protocol.context.chainId)
|
|
@@ -403,41 +456,14 @@ var Invisi = class {
|
|
|
403
456
|
if (runtime.protocolRpc instanceof ProtocolRpcClient) {
|
|
404
457
|
runtime.protocolRpc = runtime.protocolRpc.withReader(reader);
|
|
405
458
|
}
|
|
406
|
-
const registry = selected.protocol.deployment.registry;
|
|
407
459
|
const fundingVerifier = combined.fundingVerifier ?? (provider ? new EthersFundingVerifier(provider, selected.protocol.context.chainId) : void 0);
|
|
408
|
-
|
|
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
|
-
}
|
|
433
|
-
}
|
|
434
|
-
return {
|
|
460
|
+
return configureRecipientInfrastructure({
|
|
435
461
|
...runtime,
|
|
462
|
+
...provider ? { provider } : {},
|
|
436
463
|
chainReader: reader,
|
|
437
464
|
...fundingVerifier ? { fundingVerifier } : {},
|
|
438
|
-
...addressResolver ? { addressResolver } : {},
|
|
439
465
|
protocol: selected.protocol
|
|
440
|
-
};
|
|
466
|
+
});
|
|
441
467
|
}
|
|
442
468
|
/** Discover the selected chain's registry and prepare an independent signing request. */
|
|
443
469
|
async prepareRegistration(options) {
|
|
@@ -563,8 +589,272 @@ import {
|
|
|
563
589
|
computeCreditNullifier,
|
|
564
590
|
creditPeriods
|
|
565
591
|
} from "@invisi-labs/core";
|
|
566
|
-
import {
|
|
567
|
-
|
|
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;
|
|
676
|
+
}
|
|
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;
|
|
682
|
+
}
|
|
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
|
+
|
|
804
|
+
// src/Protocol.ts
|
|
805
|
+
var sources = /* @__PURE__ */ new WeakMap();
|
|
806
|
+
var Protocol = class _Protocol extends BaseProtocol {
|
|
807
|
+
static async fromRpc(rpc, provider, options = {}) {
|
|
808
|
+
const verified = await BaseProtocol.fromRpc(rpc, provider, options);
|
|
809
|
+
const protocol = new _Protocol({ deployment: verified.deployment });
|
|
810
|
+
if (rpc.getBroadcasters)
|
|
811
|
+
sources.set(protocol, rpc);
|
|
812
|
+
return protocol;
|
|
813
|
+
}
|
|
814
|
+
/** Returns enabled services that answer a token fee probe; final quotes are checked by prepared. */
|
|
815
|
+
async getBroadcasters(options) {
|
|
816
|
+
const token = getAddress3(options.tokenAddress);
|
|
817
|
+
if (!this.deployment.broadcasterRegistry)
|
|
818
|
+
throw new Error("broadcaster registry is not configured");
|
|
819
|
+
const rpc = sources.get(this);
|
|
820
|
+
if (!rpc) throw new Error("broadcaster discovery requires Protocol.fromRpc");
|
|
821
|
+
const clients = [];
|
|
822
|
+
let after;
|
|
823
|
+
let block;
|
|
824
|
+
const seen = /* @__PURE__ */ new Set();
|
|
825
|
+
do {
|
|
826
|
+
const page = await rpc.getBroadcasters({
|
|
827
|
+
enabled: true,
|
|
828
|
+
limit: 100,
|
|
829
|
+
...after ? { after } : {},
|
|
830
|
+
...block ? { block } : {}
|
|
831
|
+
});
|
|
832
|
+
block ??= page.block;
|
|
833
|
+
const compatible = await Promise.all(
|
|
834
|
+
page.broadcasters.map(async (entry) => {
|
|
835
|
+
if (!entry.enabled) return void 0;
|
|
836
|
+
try {
|
|
837
|
+
const client = new BroadcasterClient(entry);
|
|
838
|
+
await client.fee(token);
|
|
839
|
+
return client;
|
|
840
|
+
} catch {
|
|
841
|
+
return void 0;
|
|
842
|
+
}
|
|
843
|
+
})
|
|
844
|
+
);
|
|
845
|
+
clients.push(
|
|
846
|
+
...compatible.filter((client) => client !== void 0)
|
|
847
|
+
);
|
|
848
|
+
after = page.next;
|
|
849
|
+
if (after && seen.has(after)) throw new Error("repeated broadcaster pagination cursor");
|
|
850
|
+
if (after) seen.add(after);
|
|
851
|
+
} while (after);
|
|
852
|
+
return clients;
|
|
853
|
+
}
|
|
854
|
+
};
|
|
855
|
+
|
|
856
|
+
// src/index.ts
|
|
857
|
+
import { ProtocolRpcError, MAX_UPDATE_BLOCKS } from "@invisi-labs/rpc";
|
|
568
858
|
|
|
569
859
|
// src/ProtocolRpcClient.ts
|
|
570
860
|
import { ProtocolRpcClient as RpcClient } from "@invisi-labs/rpc";
|
|
@@ -574,6 +864,112 @@ var ProtocolRpcClient2 = class extends RpcClient {
|
|
|
574
864
|
}
|
|
575
865
|
};
|
|
576
866
|
|
|
867
|
+
// src/BroadcasterRegistry.ts
|
|
868
|
+
import { getAddress as getAddress4, Interface as Interface2, keccak256 as keccak2562 } from "ethers";
|
|
869
|
+
import { broadcasterRegistryAbi } from "@invisi-labs/protocol/contracts";
|
|
870
|
+
var abi = new Interface2(broadcasterRegistryAbi);
|
|
871
|
+
function address(value) {
|
|
872
|
+
const normalized = getAddress4(value);
|
|
873
|
+
if (BigInt(normalized) === 0n) throw new Error("zero broadcaster address");
|
|
874
|
+
return normalized;
|
|
875
|
+
}
|
|
876
|
+
var BroadcasterRegistry = class {
|
|
877
|
+
address;
|
|
878
|
+
#options;
|
|
879
|
+
constructor(options) {
|
|
880
|
+
this.address = address(options.address);
|
|
881
|
+
const blockRange = options.blockRange ?? 2e3;
|
|
882
|
+
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))
|
|
883
|
+
throw new Error("invalid broadcaster registry configuration");
|
|
884
|
+
this.#options = Object.freeze({ ...options, blockRange });
|
|
885
|
+
}
|
|
886
|
+
prepareRegistration(registration) {
|
|
887
|
+
const { endpoint, metadataURI = "" } = registration;
|
|
888
|
+
if (!endpoint.startsWith("https://") || endpoint.length <= 8 || endpoint.length > 2048 || /[^\x21-\x7e]/.test(endpoint))
|
|
889
|
+
throw new Error("broadcaster endpoint must be a printable HTTPS URI");
|
|
890
|
+
const url = new URL(endpoint);
|
|
891
|
+
if (!url.hostname || url.username || url.password) {
|
|
892
|
+
throw new Error("broadcaster HTTPS endpoint must not include credentials");
|
|
893
|
+
}
|
|
894
|
+
if (metadataURI.length > 2048 || /[^\x21-\x7e]/.test(metadataURI)) {
|
|
895
|
+
throw new Error("invalid broadcaster metadata URI");
|
|
896
|
+
}
|
|
897
|
+
return this.call("setBroadcaster", [
|
|
898
|
+
endpoint,
|
|
899
|
+
metadataURI,
|
|
900
|
+
address(registration.quoteSigner),
|
|
901
|
+
address(registration.feeRecipient)
|
|
902
|
+
]);
|
|
903
|
+
}
|
|
904
|
+
prepareAvailability(enabled) {
|
|
905
|
+
return this.call("setEnabled", [enabled]);
|
|
906
|
+
}
|
|
907
|
+
call(name, args) {
|
|
908
|
+
return { to: this.address, data: abi.encodeFunctionData(name, args), value: 0n };
|
|
909
|
+
}
|
|
910
|
+
async validate() {
|
|
911
|
+
const { provider, chainId, runtimeCodeHash } = this.#options;
|
|
912
|
+
if ((await provider.getNetwork()).chainId !== chainId) {
|
|
913
|
+
throw new Error("broadcaster registry chain mismatch");
|
|
914
|
+
}
|
|
915
|
+
if (keccak2562(await provider.getCode(this.address)).toLowerCase() !== runtimeCodeHash.toLowerCase()) {
|
|
916
|
+
throw new Error("broadcaster registry runtime code mismatch");
|
|
917
|
+
}
|
|
918
|
+
}
|
|
919
|
+
async get(operator) {
|
|
920
|
+
await this.validate();
|
|
921
|
+
return this.read(address(operator));
|
|
922
|
+
}
|
|
923
|
+
async read(operator) {
|
|
924
|
+
const result = await this.#options.provider.call(this.call("broadcasterOf", [operator]));
|
|
925
|
+
const [entry] = abi.decodeFunctionResult("broadcasterOf", result);
|
|
926
|
+
return {
|
|
927
|
+
operator,
|
|
928
|
+
endpoint: entry.endpoint,
|
|
929
|
+
metadataURI: entry.metadataURI,
|
|
930
|
+
quoteSigner: address(entry.quoteSigner),
|
|
931
|
+
feeRecipient: address(entry.feeRecipient),
|
|
932
|
+
enabled: entry.enabled
|
|
933
|
+
};
|
|
934
|
+
}
|
|
935
|
+
/** Discover event operators, then read their latest entry; disabled services remain explicit. */
|
|
936
|
+
async discover(options = {}) {
|
|
937
|
+
const { provider, fromBlock, blockRange } = this.#options;
|
|
938
|
+
options.signal?.throwIfAborted();
|
|
939
|
+
await this.validate();
|
|
940
|
+
const toBlock = options.toBlock ?? await provider.getBlockNumber();
|
|
941
|
+
if (!Number.isSafeInteger(toBlock) || toBlock < fromBlock) {
|
|
942
|
+
throw new Error("invalid broadcaster discovery block range");
|
|
943
|
+
}
|
|
944
|
+
const operators = /* @__PURE__ */ new Set();
|
|
945
|
+
for (let start = fromBlock; start <= toBlock; ) {
|
|
946
|
+
options.signal?.throwIfAborted();
|
|
947
|
+
const end = Math.min(start + blockRange - 1, toBlock);
|
|
948
|
+
const logs = await provider.getLogs({
|
|
949
|
+
address: this.address,
|
|
950
|
+
topics: [abi.getEvent("BroadcasterSet").topicHash],
|
|
951
|
+
fromBlock: start,
|
|
952
|
+
toBlock: end
|
|
953
|
+
});
|
|
954
|
+
for (const log of logs) {
|
|
955
|
+
if (getAddress4(log.address) !== this.address)
|
|
956
|
+
throw new Error("registry event address mismatch");
|
|
957
|
+
const event = abi.parseLog(log);
|
|
958
|
+
if (!event || event.name !== "BroadcasterSet")
|
|
959
|
+
throw new Error("invalid registry event");
|
|
960
|
+
operators.add(address(event.args.operator));
|
|
961
|
+
}
|
|
962
|
+
start = end + 1;
|
|
963
|
+
}
|
|
964
|
+
const entries = [];
|
|
965
|
+
for (const operator of operators) {
|
|
966
|
+
options.signal?.throwIfAborted();
|
|
967
|
+
entries.push(await this.read(operator));
|
|
968
|
+
}
|
|
969
|
+
return entries;
|
|
970
|
+
}
|
|
971
|
+
};
|
|
972
|
+
|
|
577
973
|
// src/integrations/encrypted-codec.ts
|
|
578
974
|
var EncryptedWalletCodec = class _EncryptedWalletCodec {
|
|
579
975
|
#key;
|
|
@@ -659,6 +1055,9 @@ var EncryptedWalletCodec = class _EncryptedWalletCodec {
|
|
|
659
1055
|
};
|
|
660
1056
|
export {
|
|
661
1057
|
AddressRegistry,
|
|
1058
|
+
BroadcasterClient,
|
|
1059
|
+
BroadcasterRegistry,
|
|
1060
|
+
BroadcasterRpcError,
|
|
662
1061
|
Detector,
|
|
663
1062
|
Disclosure,
|
|
664
1063
|
DisclosureVerifier,
|
|
@@ -666,7 +1065,9 @@ export {
|
|
|
666
1065
|
Invisi,
|
|
667
1066
|
InvisiError,
|
|
668
1067
|
MAX_MEMO_BYTES,
|
|
1068
|
+
MAX_UPDATE_BLOCKS,
|
|
669
1069
|
MissingCapabilityError3 as MissingCapabilityError,
|
|
1070
|
+
NoteOutput,
|
|
670
1071
|
NullifierCapability,
|
|
671
1072
|
Operation,
|
|
672
1073
|
PreparedTransaction,
|
|
@@ -679,9 +1080,14 @@ export {
|
|
|
679
1080
|
Wallet,
|
|
680
1081
|
WalletAuthorization,
|
|
681
1082
|
WalletSession,
|
|
1083
|
+
broadcasterQuoteDomain,
|
|
1084
|
+
broadcasterQuoteTypes,
|
|
1085
|
+
broadcasterRequestHash,
|
|
1086
|
+
broadcasterTransactInterface,
|
|
682
1087
|
computeCreditNullifier,
|
|
683
1088
|
creditPeriods,
|
|
684
1089
|
creditV1,
|
|
1090
|
+
decodeBroadcasterIntent,
|
|
685
1091
|
decodeDeliveryHeader,
|
|
686
1092
|
decodeMemoText,
|
|
687
1093
|
decryptCandidates,
|
|
@@ -689,6 +1095,7 @@ export {
|
|
|
689
1095
|
encodeMemo,
|
|
690
1096
|
fundsV1,
|
|
691
1097
|
readProtocolLogs,
|
|
1098
|
+
verifyBroadcasterQuote,
|
|
692
1099
|
walletBalance
|
|
693
1100
|
};
|
|
694
1101
|
//# sourceMappingURL=index.js.map
|