@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.
- package/README.md +5 -0
- package/dist/broadcaster-3KTB4JO2.cjs +21 -0
- package/dist/broadcaster-3KTB4JO2.cjs.map +1 -0
- package/dist/broadcaster-7ZKKSTZQ.js +21 -0
- package/dist/broadcaster-preparation-PJ263WBL.cjs +15 -0
- package/dist/broadcaster-preparation-PJ263WBL.cjs.map +1 -0
- package/dist/broadcaster-preparation-ZQAPZAUG.js +15 -0
- package/dist/broadcaster-preparation-ZQAPZAUG.js.map +1 -0
- package/dist/chunk-2URICDPW.cjs +167 -0
- package/dist/chunk-2URICDPW.cjs.map +1 -0
- package/dist/chunk-3D24ABYR.js +86 -0
- package/dist/chunk-3D24ABYR.js.map +1 -0
- package/dist/chunk-3KTQQZOW.cjs +71 -0
- package/dist/chunk-3KTQQZOW.cjs.map +1 -0
- package/dist/chunk-4H4FARQK.js +1582 -0
- package/dist/chunk-4H4FARQK.js.map +1 -0
- package/dist/chunk-4SDP53YQ.cjs +765 -0
- package/dist/chunk-4SDP53YQ.cjs.map +1 -0
- package/dist/{chunk-QC6TQR3R.js → chunk-4VI6LYGH.js} +35 -12
- package/dist/chunk-4VI6LYGH.js.map +1 -0
- package/dist/chunk-BBSRP4MH.cjs +287 -0
- package/dist/chunk-BBSRP4MH.cjs.map +1 -0
- package/dist/chunk-DGMVGFKM.js +41 -0
- package/dist/chunk-DGMVGFKM.js.map +1 -0
- package/dist/chunk-GUD3LAUO.js +765 -0
- package/dist/chunk-GUD3LAUO.js.map +1 -0
- package/dist/chunk-HCB7RQ3S.cjs +79 -0
- package/dist/chunk-HCB7RQ3S.cjs.map +1 -0
- package/dist/{broadcaster-preparation-QVL62KM6.js → chunk-HKGJXQVN.js} +73 -34
- package/dist/chunk-HKGJXQVN.js.map +1 -0
- package/dist/chunk-HLH5JZD4.js +287 -0
- package/dist/chunk-HLH5JZD4.js.map +1 -0
- package/dist/chunk-J6TZZVAM.cjs +230 -0
- package/dist/chunk-J6TZZVAM.cjs.map +1 -0
- package/dist/chunk-LOR6PYH5.cjs +397 -0
- package/dist/chunk-LOR6PYH5.cjs.map +1 -0
- package/dist/chunk-MA2QZEF5.cjs +399 -0
- package/dist/chunk-MA2QZEF5.cjs.map +1 -0
- package/dist/chunk-MI4IP4ZD.cjs +86 -0
- package/dist/chunk-MI4IP4ZD.cjs.map +1 -0
- package/dist/chunk-NKYHYIRP.cjs +1582 -0
- package/dist/chunk-NKYHYIRP.cjs.map +1 -0
- package/dist/chunk-OCZLJKIA.cjs +41 -0
- package/dist/chunk-OCZLJKIA.cjs.map +1 -0
- package/dist/chunk-R5YAOJKB.js +71 -0
- package/dist/chunk-R5YAOJKB.js.map +1 -0
- package/dist/chunk-RY4M2CIQ.js +399 -0
- package/dist/chunk-RY4M2CIQ.js.map +1 -0
- package/dist/chunk-THMD5QO2.js +79 -0
- package/dist/chunk-THMD5QO2.js.map +1 -0
- package/dist/chunk-ZD2652WH.js +230 -0
- package/dist/chunk-ZD2652WH.js.map +1 -0
- package/dist/credit.cjs +1 -0
- package/dist/credit.cjs.map +1 -0
- package/dist/credit.d.cts +12 -0
- package/dist/credit.d.ts +12 -0
- package/dist/credit.js +1 -0
- package/dist/credit.js.map +1 -0
- package/dist/ethers.cjs +11 -385
- package/dist/ethers.cjs.map +1 -1
- package/dist/ethers.d.cts +16 -6
- package/dist/ethers.d.ts +16 -6
- package/dist/ethers.js +1 -1
- package/dist/index-CLhGwLxZ.d.cts +42 -0
- package/dist/index-Crp-ZFiM.d.ts +42 -0
- package/dist/index.cjs +479 -7847
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +87 -710
- package/dist/index.d.ts +87 -710
- package/dist/index.js +407 -869
- package/dist/index.js.map +1 -1
- package/dist/note.cjs +11 -0
- package/dist/note.cjs.map +1 -0
- package/dist/note.d.cts +29 -0
- package/dist/note.d.ts +29 -0
- package/dist/note.js +11 -0
- package/dist/note.js.map +1 -0
- package/dist/operation-bridge-CoECSxfi.d.cts +472 -0
- package/dist/operation-bridge-CoECSxfi.d.ts +472 -0
- package/dist/preparation-HUBTVCZE.js +404 -0
- package/dist/preparation-HUBTVCZE.js.map +1 -0
- package/dist/preparation-QD3ZSYV5.cjs +404 -0
- package/dist/preparation-QD3ZSYV5.cjs.map +1 -0
- package/dist/provenance-preparation-43DN2WK7.cjs +16 -0
- package/dist/provenance-preparation-43DN2WK7.cjs.map +1 -0
- package/dist/provenance-preparation-HEZE4L5H.js +16 -0
- package/dist/provenance-preparation-HEZE4L5H.js.map +1 -0
- package/dist/provenance.cjs +59 -0
- package/dist/provenance.cjs.map +1 -0
- package/dist/provenance.d.cts +34 -0
- package/dist/provenance.d.ts +34 -0
- package/dist/provenance.js +59 -0
- package/dist/provenance.js.map +1 -0
- package/dist/receipt-logs-8Msm_HZS.d.ts +322 -0
- package/dist/receipt-logs-DWG4In8N.d.cts +322 -0
- package/dist/session-support.cjs +189 -0
- package/dist/session-support.cjs.map +1 -0
- package/dist/session-support.d.cts +284 -0
- package/dist/session-support.d.ts +284 -0
- package/dist/session-support.js +189 -0
- package/dist/session-support.js.map +1 -0
- package/package.json +25 -19
- package/dist/NoteOutput-DRkVnwlx.d.cts +0 -398
- package/dist/NoteOutput-DpDC6T2O.d.ts +0 -398
- package/dist/broadcaster-preparation-QVL62KM6.js.map +0 -1
- package/dist/chunk-4SD3MW6N.js +0 -4711
- package/dist/chunk-4SD3MW6N.js.map +0 -1
- package/dist/chunk-FL4TIGWW.js +0 -1781
- package/dist/chunk-FL4TIGWW.js.map +0 -1
- package/dist/chunk-QC6TQR3R.js.map +0 -1
- package/dist/indexeddb.cjs +0 -6878
- package/dist/indexeddb.cjs.map +0 -1
- package/dist/indexeddb.d.cts +0 -20
- package/dist/indexeddb.d.ts +0 -20
- package/dist/indexeddb.js +0 -71
- package/dist/indexeddb.js.map +0 -1
- package/dist/memory.cjs +0 -6886
- package/dist/memory.cjs.map +0 -1
- package/dist/memory.d.cts +0 -12
- package/dist/memory.d.ts +0 -12
- package/dist/memory.js +0 -11
- package/dist/sqlite.cjs +0 -6862
- package/dist/sqlite.cjs.map +0 -1
- package/dist/sqlite.d.cts +0 -28
- package/dist/sqlite.d.ts +0 -28
- package/dist/sqlite.js +0 -55
- package/dist/sqlite.js.map +0 -1
- package/dist/state-C4HZFotm.d.cts +0 -78
- package/dist/state-C4HZFotm.d.ts +0 -78
- package/dist/store-8aBhnbGr.d.ts +0 -42
- package/dist/store-YrbXb6CY.d.cts +0 -42
- /package/dist/{memory.js.map → broadcaster-7ZKKSTZQ.js.map} +0 -0
package/dist/index.js
CHANGED
|
@@ -1,807 +1,188 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
acceptFundingSignature,
|
|
6
|
-
acceptWalletSignature,
|
|
7
|
-
copyGasBenchmarks,
|
|
2
|
+
AddressRegistry,
|
|
3
|
+
Invisi,
|
|
4
|
+
PreparedTransaction,
|
|
8
5
|
decryptCandidates,
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
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
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
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
|
-
|
|
37
|
-
} from "./chunk-QC6TQR3R.js";
|
|
34
|
+
EthersChainReader
|
|
35
|
+
} from "./chunk-4VI6LYGH.js";
|
|
38
36
|
|
|
39
|
-
// src/
|
|
40
|
-
import {
|
|
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/
|
|
43
|
-
|
|
44
|
-
|
|
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
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
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
|
-
|
|
170
|
-
"
|
|
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
|
-
|
|
181
|
-
|
|
182
|
-
|
|
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
|
-
|
|
186
|
-
"
|
|
97
|
+
canonical.number === block.number && canonical.hash.toLowerCase() === block.hash.toLowerCase(),
|
|
98
|
+
"disclosure canonical block changed; reorg verification required"
|
|
187
99
|
);
|
|
188
|
-
const
|
|
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
|
-
|
|
199
|
-
"
|
|
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
|
-
|
|
219
|
-
"
|
|
106
|
+
await this.#reader.getChainId() === this.#protocol.context.chainId,
|
|
107
|
+
"disclosure chain changed during verification"
|
|
220
108
|
);
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
const
|
|
230
|
-
|
|
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
|
-
|
|
263
|
-
"
|
|
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
|
-
|
|
274
|
-
"
|
|
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
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
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
|
-
|
|
294
|
-
"
|
|
133
|
+
candidates.length === 1,
|
|
134
|
+
"disclosure output has no unique canonical insertion and delivery"
|
|
295
135
|
);
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
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
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
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 {
|
|
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/
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
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
|
|
678
|
-
|
|
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
|
-
/**
|
|
201
|
+
/** Discover token support without disclosing a transaction or requesting a price. */
|
|
815
202
|
async getBroadcasters(options) {
|
|
816
|
-
|
|
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(
|
|
838
|
-
|
|
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 {
|
|
262
|
+
import { MAX_UPDATE_BLOCKS, ProtocolRpcError } from "@invisi-labs/rpc";
|
|
858
263
|
|
|
859
|
-
// src/
|
|
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
|
-
|
|
266
|
+
import { getAddress as getAddress2, Interface, keccak256 } from "ethers";
|
|
267
|
+
var abi = new Interface(broadcasterRegistryAbi);
|
|
871
268
|
function address(value) {
|
|
872
|
-
const normalized =
|
|
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 (
|
|
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 (
|
|
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/
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
this
|
|
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
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
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
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
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
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
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
|
-
|
|
1025
|
-
|
|
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
|
-
|
|
1050
|
-
return
|
|
519
|
+
toInput() {
|
|
520
|
+
return structuredClone(this.#input);
|
|
1051
521
|
}
|
|
1052
|
-
|
|
1053
|
-
|
|
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
|
-
|
|
603
|
+
MissingCapabilityError,
|
|
1070
604
|
NoteOutput,
|
|
1071
605
|
NullifierCapability,
|
|
1072
|
-
|
|
606
|
+
PaymentNote,
|
|
1073
607
|
PreparedTransaction,
|
|
1074
608
|
Protocol,
|
|
1075
|
-
|
|
609
|
+
ProtocolRpcClient,
|
|
1076
610
|
ProtocolRpcError,
|
|
1077
|
-
PublicWallet2 as PublicWallet,
|
|
1078
611
|
RegistrationOperation,
|
|
612
|
+
SpendablePaymentNote,
|
|
613
|
+
TransactionOutput,
|
|
614
|
+
VerifiedPaymentNote,
|
|
1079
615
|
ViewingCapability,
|
|
1080
|
-
Wallet,
|
|
1081
|
-
|
|
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
|