@invisi-labs/sdk 1.1.6 → 1.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{NoteOutput-DRkVnwlx.d.cts → broadcaster-preparation-DMxxZ_1Q.d.cts} +309 -120
- package/dist/{NoteOutput-DpDC6T2O.d.ts → broadcaster-preparation-DMxxZ_1Q.d.ts} +309 -120
- package/dist/broadcaster-preparation-RMWQFYNL.js +11 -0
- package/dist/broadcaster-preparation-UGOTO4SX.cjs +11 -0
- package/dist/broadcaster-preparation-UGOTO4SX.cjs.map +1 -0
- package/dist/chunk-7F7ZZW3L.cjs +374 -0
- package/dist/chunk-7F7ZZW3L.cjs.map +1 -0
- package/dist/{chunk-4SD3MW6N.js → chunk-EHP5TBC6.js} +1347 -2493
- package/dist/chunk-EHP5TBC6.js.map +1 -0
- package/dist/{broadcaster-preparation-QVL62KM6.js → chunk-GTPUYPQL.js} +6 -6
- package/dist/chunk-GTPUYPQL.js.map +1 -0
- package/dist/{chunk-FL4TIGWW.js → chunk-ITTGBIG2.js} +617 -521
- package/dist/chunk-ITTGBIG2.js.map +1 -0
- package/dist/chunk-J2JH6HIG.cjs +1877 -0
- package/dist/chunk-J2JH6HIG.cjs.map +1 -0
- package/dist/chunk-LOU7AP2B.cjs +128 -0
- package/dist/chunk-LOU7AP2B.cjs.map +1 -0
- package/dist/chunk-RHNIHI4P.cjs +3565 -0
- package/dist/chunk-RHNIHI4P.cjs.map +1 -0
- package/dist/{chunk-QC6TQR3R.js → chunk-XHKQ2BO2.js} +12 -12
- package/dist/chunk-XHKQ2BO2.js.map +1 -0
- package/dist/ethers.cjs +11 -385
- package/dist/ethers.cjs.map +1 -1
- package/dist/ethers.d.cts +3 -4
- package/dist/ethers.d.ts +3 -4
- package/dist/ethers.js +1 -1
- package/dist/index.cjs +451 -7845
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +72 -702
- package/dist/index.d.ts +72 -702
- package/dist/index.js +374 -862
- package/dist/index.js.map +1 -1
- package/dist/receipt-logs-CvC951p7.d.cts +318 -0
- package/dist/receipt-logs-m0-bhaxW.d.ts +318 -0
- package/dist/session-support.cjs +168 -0
- package/dist/session-support.cjs.map +1 -0
- package/dist/session-support.d.cts +252 -0
- package/dist/session-support.d.ts +252 -0
- package/dist/session-support.js +168 -0
- package/dist/session-support.js.map +1 -0
- package/package.json +10 -20
- package/dist/broadcaster-preparation-QVL62KM6.js.map +0 -1
- package/dist/chunk-4SD3MW6N.js.map +0 -1
- 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-preparation-RMWQFYNL.js.map} +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
EthersAddressResolver
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-XHKQ2BO2.js";
|
|
4
4
|
|
|
5
5
|
// src/errors.ts
|
|
6
6
|
import { MissingCapabilityError } from "@invisi-labs/core";
|
|
@@ -49,28 +49,472 @@ async function prepareWithErrors(operation, prepare) {
|
|
|
49
49
|
}
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
-
// src/
|
|
52
|
+
// src/payment-note/codec.ts
|
|
53
|
+
import { FIELD_MODULUS } from "@invisi-labs/core";
|
|
54
|
+
import { decodeBase64, encodeBase64, getAddress, getBytes, hexlify, concat } from "ethers";
|
|
55
|
+
function uint(value, bytes) {
|
|
56
|
+
if (typeof value !== "bigint" || value < 0n || value >= 1n << BigInt(bytes * 8))
|
|
57
|
+
throw new RangeError(`Invalid uint${bytes * 8} payment note field`);
|
|
58
|
+
return getBytes(`0x${value.toString(16).padStart(bytes * 2, "0")}`);
|
|
59
|
+
}
|
|
60
|
+
function validate(value) {
|
|
61
|
+
const keys = [
|
|
62
|
+
"chainId",
|
|
63
|
+
"protocolAddress",
|
|
64
|
+
"tokenAddress",
|
|
65
|
+
"amount",
|
|
66
|
+
"ownerAddress",
|
|
67
|
+
"nonce",
|
|
68
|
+
"commitment"
|
|
69
|
+
];
|
|
70
|
+
if (!value || typeof value !== "object" || Object.keys(value).some((key) => !keys.includes(key)))
|
|
71
|
+
throw new TypeError("Invalid payment note fields");
|
|
72
|
+
uint(value.chainId, 8);
|
|
73
|
+
uint(value.amount, 16);
|
|
74
|
+
if (value.chainId === 0n || value.amount === 0n)
|
|
75
|
+
throw new RangeError("Positive chainId and amount required");
|
|
76
|
+
for (const key of ["protocolAddress", "ownerAddress", "tokenAddress"]) {
|
|
77
|
+
getAddress(value[key]);
|
|
78
|
+
if (key !== "tokenAddress" && BigInt(value[key]) === 0n)
|
|
79
|
+
throw new RangeError(`Invalid ${key}`);
|
|
80
|
+
}
|
|
81
|
+
uint(value.nonce, 32);
|
|
82
|
+
if (value.nonce >= FIELD_MODULUS) throw new RangeError("Invalid payment note nonce");
|
|
83
|
+
if (!/^0x[\da-f]{64}$/i.test(value.commitment) || BigInt(value.commitment) <= 0n || BigInt(value.commitment) >= FIELD_MODULUS)
|
|
84
|
+
throw new RangeError("Invalid payment note commitment");
|
|
85
|
+
}
|
|
86
|
+
function encodePaymentNote(value) {
|
|
87
|
+
validate(value);
|
|
88
|
+
const bytes = concat([
|
|
89
|
+
new Uint8Array([1]),
|
|
90
|
+
uint(value.chainId, 8),
|
|
91
|
+
getBytes(value.protocolAddress),
|
|
92
|
+
getBytes(value.tokenAddress),
|
|
93
|
+
uint(value.amount, 16),
|
|
94
|
+
getBytes(value.ownerAddress),
|
|
95
|
+
uint(value.nonce, 32),
|
|
96
|
+
getBytes(value.commitment)
|
|
97
|
+
]);
|
|
98
|
+
return `ipn:${encodeBase64(bytes).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "")}`;
|
|
99
|
+
}
|
|
100
|
+
function decodePaymentNote(encoded) {
|
|
101
|
+
if (typeof encoded !== "string" || encoded.length !== 203 || !/^ipn:[A-Za-z0-9_-]+$/.test(encoded))
|
|
102
|
+
throw new TypeError("Invalid payment note encoding");
|
|
103
|
+
const bytes = decodeBase64(encoded.slice(4).replace(/-/g, "+").replace(/_/g, "/") + "=");
|
|
104
|
+
if (bytes.length !== 149 || bytes[0] !== 1)
|
|
105
|
+
throw new Error("Unsupported payment note version or length");
|
|
106
|
+
let offset = 1;
|
|
107
|
+
const take = (size) => {
|
|
108
|
+
const value2 = hexlify(bytes.slice(offset, offset += size));
|
|
109
|
+
return value2;
|
|
110
|
+
};
|
|
111
|
+
const value = {
|
|
112
|
+
chainId: BigInt(take(8)),
|
|
113
|
+
protocolAddress: getAddress(take(20)),
|
|
114
|
+
tokenAddress: getAddress(take(20)),
|
|
115
|
+
amount: BigInt(take(16)),
|
|
116
|
+
ownerAddress: getAddress(take(20)).toLowerCase(),
|
|
117
|
+
nonce: BigInt(take(32)),
|
|
118
|
+
commitment: take(32)
|
|
119
|
+
};
|
|
120
|
+
if (encodePaymentNote(value) !== encoded) throw new Error("Noncanonical payment note encoding");
|
|
121
|
+
return value;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// src/transactions/signing/statement.ts
|
|
125
|
+
import { creditPeriods } from "@invisi-labs/core";
|
|
53
126
|
import {
|
|
54
|
-
|
|
55
|
-
|
|
127
|
+
WitnessBuilder,
|
|
128
|
+
cancelCreditPublicInputs,
|
|
129
|
+
createCreditPublicInputs,
|
|
130
|
+
creditDrawPublicInputs,
|
|
131
|
+
topUpCreditPublicInputs,
|
|
132
|
+
transactUserPublicInputs
|
|
133
|
+
} from "@invisi-labs/protocol";
|
|
134
|
+
function requiresAuthorization(plan) {
|
|
135
|
+
const ownerOnlyFullWithdrawal = !plan.credit && plan.inputs.length > 0 && plan.outputs.length === 0 && plan.params !== void 0 && plan.params.outputCommitments.length === 0 && (plan.params.feeAmount ?? 0n) === 0n && BigInt(plan.params.feeRecipient ?? 0n) === 0n && (plan.funding.amount ?? 0n) === 0n && plan.fundingMode === "none" && BigInt(plan.params.withdrawalAddress) === BigInt(plan.scope.ownerAddress) && plan.params.withdrawalAmount === plan.inputs.reduce((sum, input) => sum + input.note.amount, 0n) && plan.inputs.every((input) => input.note.ownerAddress === plan.scope.ownerAddress);
|
|
136
|
+
if (ownerOnlyFullWithdrawal) return false;
|
|
137
|
+
return !!plan.credit || plan.inputs.length > 0;
|
|
138
|
+
}
|
|
139
|
+
function statement(plan) {
|
|
140
|
+
if (!plan.statement) throw new Error("operation has no proof statement");
|
|
141
|
+
const credit = plan.credit;
|
|
142
|
+
let words;
|
|
143
|
+
if (credit?.topUp) {
|
|
144
|
+
words = topUpCreditPublicInputs(
|
|
145
|
+
plan.context,
|
|
146
|
+
credit.topUp,
|
|
147
|
+
credit.deliveries,
|
|
148
|
+
plan.funding
|
|
149
|
+
);
|
|
150
|
+
} else if (credit?.creation) {
|
|
151
|
+
words = createCreditPublicInputs(
|
|
152
|
+
plan.context,
|
|
153
|
+
credit.creation,
|
|
154
|
+
credit.deliveries,
|
|
155
|
+
plan.funding
|
|
156
|
+
);
|
|
157
|
+
} else if (credit?.draw) {
|
|
158
|
+
words = creditDrawPublicInputs(plan.context, credit.draw, credit.deliveries);
|
|
159
|
+
} else if (credit?.cancel) {
|
|
160
|
+
words = cancelCreditPublicInputs(plan.context, credit.cancel);
|
|
161
|
+
} else {
|
|
162
|
+
words = transactUserPublicInputs(
|
|
163
|
+
plan.context,
|
|
164
|
+
plan.params,
|
|
165
|
+
plan.outputs.map((output) => output.delivery),
|
|
166
|
+
plan.funding
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
const result = WitnessBuilder.statement(
|
|
170
|
+
plan.statement.circuitName,
|
|
171
|
+
words,
|
|
172
|
+
plan.statement.target
|
|
173
|
+
);
|
|
174
|
+
if (JSON.stringify(result) !== JSON.stringify(plan.statement)) {
|
|
175
|
+
throw new Error("frozen operation statement changed");
|
|
176
|
+
}
|
|
177
|
+
return result;
|
|
178
|
+
}
|
|
179
|
+
function authorizationRequest(plan) {
|
|
180
|
+
let operation = "transact";
|
|
181
|
+
if (plan.kind === "draw-credit") {
|
|
182
|
+
operation = "credit-draw";
|
|
183
|
+
} else if (plan.kind === "create-credit" || plan.kind === "top-up-credit" || plan.kind === "cancel-credit") {
|
|
184
|
+
operation = plan.kind;
|
|
185
|
+
}
|
|
186
|
+
return {
|
|
187
|
+
context: {
|
|
188
|
+
chainId: plan.context.chainId,
|
|
189
|
+
protocolAddress: BigInt(plan.context.protocolAddress)
|
|
190
|
+
},
|
|
191
|
+
operation,
|
|
192
|
+
capacity: plan.capacity,
|
|
193
|
+
words: statement(plan).expectedPublicInputs.slice(0, -2).map(BigInt)
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
function operationExpired(plan, now) {
|
|
197
|
+
if (now > plan.deadline) return true;
|
|
198
|
+
const credit = plan.credit;
|
|
199
|
+
if (credit?.topUp && now >= credit.topUp.previousExpiry) return true;
|
|
200
|
+
if (credit?.creation && now >= credit.creation.expiry) return true;
|
|
201
|
+
if (credit?.draw) {
|
|
202
|
+
if (credit.draw.expiries.some((expiry) => now >= expiry)) return true;
|
|
203
|
+
const prepared = creditPeriods(plan.preparedAt.timestamp), current = creditPeriods(now);
|
|
204
|
+
if (prepared.currentDay !== current.currentDay || prepared.currentMonth !== current.currentMonth)
|
|
205
|
+
return true;
|
|
206
|
+
}
|
|
207
|
+
return false;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// src/broadcaster/intent.ts
|
|
211
|
+
import { normalizeFunding } from "@invisi-labs/protocol";
|
|
212
|
+
import { invisiProtocolAbi } from "@invisi-labs/protocol/contracts";
|
|
213
|
+
import { Interface } from "ethers";
|
|
214
|
+
function exportBroadcasterIntent(plan) {
|
|
215
|
+
if (plan.kind === "draw-credit" || plan.statement && plan.statement.target !== "evm")
|
|
216
|
+
throw new Error("unsupported broadcaster operation");
|
|
217
|
+
if (plan.statement) statement(plan);
|
|
218
|
+
const feeDefaults = (params) => ({
|
|
219
|
+
...params,
|
|
220
|
+
feeRecipient: params.feeRecipient ?? "0x0000000000000000000000000000000000000000",
|
|
221
|
+
feeAmount: params.feeAmount ?? 0n
|
|
222
|
+
});
|
|
223
|
+
const funding = { ...normalizeFunding(plan.funding), signature: "0x" };
|
|
224
|
+
let method;
|
|
225
|
+
let args;
|
|
226
|
+
if (plan.params) {
|
|
227
|
+
method = "transact";
|
|
228
|
+
args = [
|
|
229
|
+
"0x",
|
|
230
|
+
{
|
|
231
|
+
params: feeDefaults(plan.params),
|
|
232
|
+
deliveries: plan.outputs.map((o) => o.delivery),
|
|
233
|
+
funding
|
|
234
|
+
}
|
|
235
|
+
];
|
|
236
|
+
} else if (plan.shieldParams) {
|
|
237
|
+
method = "shield";
|
|
238
|
+
args = [
|
|
239
|
+
feeDefaults(plan.shieldParams),
|
|
240
|
+
plan.shieldOutputs,
|
|
241
|
+
plan.outputs.map((o) => o.delivery),
|
|
242
|
+
funding
|
|
243
|
+
];
|
|
244
|
+
} else if (plan.credit?.topUp) {
|
|
245
|
+
method = "topUpCredit";
|
|
246
|
+
args = [
|
|
247
|
+
"0x",
|
|
248
|
+
{ ...plan.credit.topUp, creation: feeDefaults(plan.credit.topUp.creation) },
|
|
249
|
+
plan.credit.deliveries,
|
|
250
|
+
funding
|
|
251
|
+
];
|
|
252
|
+
} else if (plan.credit?.creation) {
|
|
253
|
+
method = "createCredit";
|
|
254
|
+
args = ["0x", feeDefaults(plan.credit.creation), plan.credit.deliveries, funding];
|
|
255
|
+
} else if (plan.credit?.cancel) {
|
|
256
|
+
method = "cancelCredit";
|
|
257
|
+
args = ["0x", feeDefaults(plan.credit.cancel)];
|
|
258
|
+
} else throw new Error("unsupported broadcaster operation");
|
|
259
|
+
const abi = invisiProtocolAbi.filter(
|
|
260
|
+
(item) => item.type === "function" && item.name === method && (method !== "transact" || item.inputs[1]?.type === "tuple")
|
|
261
|
+
);
|
|
262
|
+
return {
|
|
263
|
+
sendable: false,
|
|
264
|
+
data: new Interface(abi).encodeFunctionData(method, args)
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// src/payment-request/codec.ts
|
|
269
|
+
import { ReceivingAddress as ReceivingAddress2 } from "@invisi-labs/core";
|
|
270
|
+
import { decodeBase64 as decodeBase642, encodeBase64 as encodeBase642, getBytes as getBytes2, hexlify as hexlify2 } from "ethers";
|
|
271
|
+
|
|
272
|
+
// src/payment-request/binary.ts
|
|
273
|
+
function uint2(value, size, name) {
|
|
274
|
+
if (typeof value !== "bigint" || value < 0n || value >= 1n << BigInt(size * 8))
|
|
275
|
+
throw new RangeError(`${name} must be uint${size * 8}`);
|
|
276
|
+
const result = new Uint8Array(size);
|
|
277
|
+
for (let i = size - 1; i >= 0; i--) {
|
|
278
|
+
result[i] = Number(value & 255n);
|
|
279
|
+
value >>= 8n;
|
|
280
|
+
}
|
|
281
|
+
return result;
|
|
282
|
+
}
|
|
283
|
+
function concat2(parts) {
|
|
284
|
+
const result = new Uint8Array(parts.reduce((n, p) => n + p.length, 0));
|
|
285
|
+
let offset = 0;
|
|
286
|
+
for (const part of parts) {
|
|
287
|
+
result.set(part, offset);
|
|
288
|
+
offset += part.length;
|
|
289
|
+
}
|
|
290
|
+
return result;
|
|
291
|
+
}
|
|
292
|
+
var Reader = class {
|
|
293
|
+
constructor(data) {
|
|
294
|
+
this.data = data;
|
|
295
|
+
}
|
|
296
|
+
#offset = 0;
|
|
297
|
+
take(size) {
|
|
298
|
+
if (this.#offset + size > this.data.length) throw new Error("Truncated payment request");
|
|
299
|
+
const result = this.data.slice(this.#offset, this.#offset + size);
|
|
300
|
+
this.#offset += size;
|
|
301
|
+
return result;
|
|
302
|
+
}
|
|
303
|
+
integer(size) {
|
|
304
|
+
return this.take(size).reduce((n, b) => n << 8n | BigInt(b), 0n);
|
|
305
|
+
}
|
|
306
|
+
done() {
|
|
307
|
+
if (this.#offset !== this.data.length) throw new Error("Trailing payment request bytes");
|
|
308
|
+
}
|
|
309
|
+
};
|
|
310
|
+
|
|
311
|
+
// src/payment-request/validation.ts
|
|
312
|
+
import { FIELD_MODULUS as FIELD_MODULUS2, MAX_CIPHERTEXT_BYTES, ReceivingAddress } from "@invisi-labs/core";
|
|
313
|
+
import { getAddress as getAddress2 } from "ethers";
|
|
314
|
+
var MAX_REQUEST_PAYLOAD_BYTES = MAX_CIPHERTEXT_BYTES - 160;
|
|
315
|
+
function fields(value, allowed, name) {
|
|
316
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
317
|
+
throw new TypeError(`Invalid ${name}`);
|
|
318
|
+
for (const key of Object.keys(value))
|
|
319
|
+
if (!allowed.includes(key)) throw new TypeError(`Unsupported ${name} field ${key}`);
|
|
320
|
+
}
|
|
321
|
+
function nonce(value) {
|
|
322
|
+
uint2(value, 32, "nonce");
|
|
323
|
+
if (value >= FIELD_MODULUS2)
|
|
324
|
+
throw new RangeError("nonce must be a canonical BN254 field element");
|
|
325
|
+
return value;
|
|
326
|
+
}
|
|
327
|
+
function validatePaymentRequest(request) {
|
|
328
|
+
fields(
|
|
329
|
+
request,
|
|
330
|
+
["chainId", "protocolAddress", "tokenAddress", "amount", "recipient", "payload"],
|
|
331
|
+
"payment request"
|
|
332
|
+
);
|
|
333
|
+
uint2(request.chainId, 8, "chainId");
|
|
334
|
+
if (request.chainId === 0n) throw new RangeError("chainId must be positive");
|
|
335
|
+
const protocol = getAddress2(request.protocolAddress);
|
|
336
|
+
getAddress2(request.tokenAddress);
|
|
337
|
+
if (BigInt(protocol) === 0n) throw new RangeError("protocol address must be nonzero");
|
|
338
|
+
uint2(request.amount, 16, "amount");
|
|
339
|
+
if (request.amount === 0n) throw new RangeError("amount must be positive");
|
|
340
|
+
const recipient = request.recipient;
|
|
341
|
+
fields(
|
|
342
|
+
recipient,
|
|
343
|
+
["type", "address", "nonce", "identity", "envelope", "addressLookup"],
|
|
344
|
+
"recipient"
|
|
345
|
+
);
|
|
346
|
+
if (recipient.type === "identity") {
|
|
347
|
+
fields(recipient, ["type", "identity"], "identity recipient");
|
|
348
|
+
if (typeof recipient.identity !== "string" || !/^0x[\da-f]{64}$/i.test(recipient.identity) || BigInt(recipient.identity) <= 0n || BigInt(recipient.identity) >= FIELD_MODULUS2)
|
|
349
|
+
throw new RangeError("identity must be a nonzero canonical BN254 field element");
|
|
350
|
+
if (request.payload !== void 0)
|
|
351
|
+
throw new TypeError("payload requires an encrypted envelope");
|
|
352
|
+
return;
|
|
353
|
+
}
|
|
354
|
+
if (recipient.type !== "evm" && recipient.type !== "invisi")
|
|
355
|
+
throw new TypeError("Invalid recipient type");
|
|
356
|
+
fields(
|
|
357
|
+
recipient,
|
|
358
|
+
recipient.type === "evm" ? ["type", "address", "nonce", "envelope", "addressLookup"] : ["type", "address", "nonce"],
|
|
359
|
+
"recipient"
|
|
360
|
+
);
|
|
361
|
+
const parsed = ReceivingAddress.parse(recipient.address);
|
|
362
|
+
if (recipient.type === "evm" !== (parsed.kind === "short"))
|
|
363
|
+
throw new TypeError("recipient address type mismatch");
|
|
364
|
+
if (BigInt(parsed.ownerAddress) === 0n) throw new RangeError("owner address must be nonzero");
|
|
365
|
+
if (recipient.nonce !== void 0) nonce(recipient.nonce);
|
|
366
|
+
if (recipient.type === "evm") {
|
|
367
|
+
if (recipient.envelope !== void 0 && typeof recipient.envelope !== "boolean")
|
|
368
|
+
throw new TypeError("evm envelope must be boolean");
|
|
369
|
+
if (recipient.addressLookup !== void 0 && !["chain", "protocol-rpc"].includes(recipient.addressLookup))
|
|
370
|
+
throw new TypeError("Invalid recipient addressLookup");
|
|
371
|
+
if (recipient.envelope === false && request.payload !== void 0)
|
|
372
|
+
throw new TypeError("payload requires an encrypted envelope");
|
|
373
|
+
}
|
|
374
|
+
if (request.payload !== void 0 && !(request.payload instanceof Uint8Array))
|
|
375
|
+
throw new TypeError("payload must be Uint8Array");
|
|
376
|
+
if ((request.payload?.length ?? 0) > MAX_REQUEST_PAYLOAD_BYTES)
|
|
377
|
+
throw new RangeError("payload exceeds funds envelope limit");
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
// src/payment-request/codec.ts
|
|
381
|
+
var PREFIX = "ipr:";
|
|
382
|
+
var VERSION = 1;
|
|
383
|
+
var MAX_BINARY_BYTES = 186 + MAX_REQUEST_PAYLOAD_BYTES;
|
|
384
|
+
function base64url(data) {
|
|
385
|
+
return encodeBase642(data).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
386
|
+
}
|
|
387
|
+
function encodePaymentRequest(request) {
|
|
388
|
+
validatePaymentRequest(request);
|
|
389
|
+
const recipient = request.recipient;
|
|
390
|
+
const parts = [
|
|
391
|
+
new Uint8Array([
|
|
392
|
+
VERSION,
|
|
393
|
+
recipient.type === "evm" ? 1 : recipient.type === "invisi" ? 2 : 3
|
|
394
|
+
]),
|
|
395
|
+
uint2(request.chainId, 8, "chainId"),
|
|
396
|
+
getBytes2(request.protocolAddress),
|
|
397
|
+
getBytes2(request.tokenAddress),
|
|
398
|
+
uint2(request.amount, 16, "amount")
|
|
399
|
+
];
|
|
400
|
+
if (recipient.type === "identity") parts.push(getBytes2(recipient.identity));
|
|
401
|
+
else {
|
|
402
|
+
const parsed = ReceivingAddress2.parse(recipient.address);
|
|
403
|
+
parts.push(getBytes2(parsed.ownerAddress));
|
|
404
|
+
if (recipient.type === "invisi") {
|
|
405
|
+
const keys = parsed.getReceivingKeys();
|
|
406
|
+
parts.push(keys.viewingPublicKey, keys.clueKey);
|
|
407
|
+
} else parts.push(new Uint8Array([recipient.envelope === false ? 0 : 1]));
|
|
408
|
+
parts.push(new Uint8Array([recipient.nonce === void 0 ? 0 : 1]));
|
|
409
|
+
if (recipient.nonce !== void 0) parts.push(uint2(recipient.nonce, 32, "nonce"));
|
|
410
|
+
if (recipient.type === "invisi" || recipient.envelope !== false) {
|
|
411
|
+
const payload = request.payload ?? new Uint8Array();
|
|
412
|
+
parts.push(uint2(BigInt(payload.length), 2, "payloadLength"), payload);
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
return PREFIX + base64url(concat2(parts));
|
|
416
|
+
}
|
|
417
|
+
function decodePaymentRequest(encoded) {
|
|
418
|
+
if (typeof encoded !== "string" || !encoded.startsWith(PREFIX) || encoded.length > 4 + Math.ceil(MAX_BINARY_BYTES * 8 / 6))
|
|
419
|
+
throw new Error("Invalid payment request encoding");
|
|
420
|
+
const text = encoded.slice(PREFIX.length);
|
|
421
|
+
if (!/^[A-Za-z0-9_-]+$/.test(text) || text.length % 4 === 1)
|
|
422
|
+
throw new Error("Invalid payment request Base64URL");
|
|
423
|
+
const data = decodeBase642(
|
|
424
|
+
text.replace(/-/g, "+").replace(/_/g, "/") + "=".repeat((4 - text.length % 4) % 4)
|
|
425
|
+
);
|
|
426
|
+
if (base64url(data) !== text) throw new Error("Noncanonical payment request Base64URL");
|
|
427
|
+
const reader = new Reader(data);
|
|
428
|
+
if (reader.integer(1) !== BigInt(VERSION))
|
|
429
|
+
throw new Error("Unsupported payment request version");
|
|
430
|
+
const type = reader.integer(1);
|
|
431
|
+
if (type < 1n || type > 3n) throw new Error("Unsupported payment request recipient type");
|
|
432
|
+
const context = {
|
|
433
|
+
chainId: reader.integer(8),
|
|
434
|
+
protocolAddress: hexlify2(reader.take(20)),
|
|
435
|
+
tokenAddress: hexlify2(reader.take(20)),
|
|
436
|
+
amount: reader.integer(16)
|
|
437
|
+
};
|
|
438
|
+
let recipient;
|
|
439
|
+
let payload;
|
|
440
|
+
if (type === 3n)
|
|
441
|
+
recipient = { type: "identity", identity: hexlify2(reader.take(32)) };
|
|
442
|
+
else {
|
|
443
|
+
const ownerAddress = hexlify2(reader.take(20));
|
|
444
|
+
let address;
|
|
445
|
+
let envelope = true;
|
|
446
|
+
if (type === 2n)
|
|
447
|
+
address = ReceivingAddress2.from({
|
|
448
|
+
ownerAddress,
|
|
449
|
+
viewingPublicKey: reader.take(33),
|
|
450
|
+
clueKey: reader.take(32)
|
|
451
|
+
}).toString();
|
|
452
|
+
else {
|
|
453
|
+
address = ReceivingAddress2.fromIdentity(ownerAddress).toString();
|
|
454
|
+
const value = reader.integer(1);
|
|
455
|
+
if (value > 1n) throw new Error("Invalid envelope flag");
|
|
456
|
+
envelope = value === 1n;
|
|
457
|
+
}
|
|
458
|
+
const present = reader.integer(1);
|
|
459
|
+
if (present > 1n) throw new Error("Invalid nonce presence flag");
|
|
460
|
+
const nonce2 = present === 1n ? reader.integer(32) : void 0;
|
|
461
|
+
recipient = type === 2n ? { type: "invisi", address, ...nonce2 === void 0 ? {} : { nonce: nonce2 } } : { type: "evm", address, envelope, ...nonce2 === void 0 ? {} : { nonce: nonce2 } };
|
|
462
|
+
if (envelope) {
|
|
463
|
+
const length = Number(reader.integer(2));
|
|
464
|
+
if (length > MAX_REQUEST_PAYLOAD_BYTES)
|
|
465
|
+
throw new RangeError("payload exceeds funds envelope limit");
|
|
466
|
+
if (length !== 0) payload = reader.take(length);
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
reader.done();
|
|
470
|
+
const request = {
|
|
471
|
+
...context,
|
|
472
|
+
recipient,
|
|
473
|
+
...payload === void 0 ? {} : { payload }
|
|
474
|
+
};
|
|
475
|
+
validatePaymentRequest(request);
|
|
476
|
+
if (encodePaymentRequest(request) !== encoded) throw new Error("Noncanonical payment request");
|
|
477
|
+
return request;
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
// src/payment-note/export.ts
|
|
481
|
+
function exportPaymentNote(output) {
|
|
482
|
+
const note = output.note;
|
|
483
|
+
if (output.recipient.type === "identity" || !note)
|
|
484
|
+
throw new Error("Identity outputs cannot export an IPN");
|
|
485
|
+
return encodePaymentNote({
|
|
486
|
+
chainId: note.chainId,
|
|
487
|
+
protocolAddress: output.context.protocolAddress,
|
|
488
|
+
tokenAddress: output.context.tokenAddress,
|
|
489
|
+
amount: note.amount,
|
|
490
|
+
ownerAddress: note.ownerAddress,
|
|
491
|
+
nonce: note.nonce,
|
|
492
|
+
commitment: output.commitment
|
|
493
|
+
});
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
// src/notes/NoteOutput.ts
|
|
497
|
+
import {
|
|
498
|
+
FIELD_MODULUS as FIELD_MODULUS3,
|
|
499
|
+
ReceivingAddress as ReceivingAddress5,
|
|
56
500
|
computeNoteCommitment,
|
|
57
501
|
computeNoteIdentity,
|
|
58
502
|
createClue,
|
|
59
503
|
encryptPrivateTransferNote,
|
|
60
504
|
generateNonce
|
|
61
505
|
} from "@invisi-labs/core";
|
|
62
|
-
import { AbiCoder, getAddress, hexlify, keccak256 } from "ethers";
|
|
506
|
+
import { AbiCoder, getAddress as getAddress3, hexlify as hexlify4, keccak256 } from "ethers";
|
|
63
507
|
|
|
64
|
-
// src/resolve-recipient.ts
|
|
508
|
+
// src/notes/resolve-recipient.ts
|
|
65
509
|
import {
|
|
66
510
|
MissingCapabilityError as MissingCapabilityError2,
|
|
67
|
-
ReceivingAddress
|
|
511
|
+
ReceivingAddress as ReceivingAddress3
|
|
68
512
|
} from "@invisi-labs/core";
|
|
69
513
|
async function resolveRecipient(address, lookup, details = {}) {
|
|
70
514
|
const context = { ...details, address };
|
|
71
515
|
let recipient;
|
|
72
516
|
try {
|
|
73
|
-
recipient =
|
|
517
|
+
recipient = ReceivingAddress3.parse(address);
|
|
74
518
|
} catch (cause) {
|
|
75
519
|
throw new InvisiError("INVALID_RECIPIENT", "Invalid recipient address", {
|
|
76
520
|
...context,
|
|
@@ -108,7 +552,7 @@ async function resolveRecipient(address, lookup, details = {}) {
|
|
|
108
552
|
);
|
|
109
553
|
}
|
|
110
554
|
try {
|
|
111
|
-
return
|
|
555
|
+
return ReceivingAddress3.from({
|
|
112
556
|
ownerAddress: recipient.ownerAddress,
|
|
113
557
|
viewingPublicKey: keys.viewingPublicKey,
|
|
114
558
|
clueKey: keys.clueKey
|
|
@@ -122,14 +566,65 @@ async function resolveRecipient(address, lookup, details = {}) {
|
|
|
122
566
|
}
|
|
123
567
|
}
|
|
124
568
|
|
|
125
|
-
// src/
|
|
569
|
+
// src/payment-request/match-output.ts
|
|
570
|
+
import { ReceivingAddress as ReceivingAddress4 } from "@invisi-labs/core";
|
|
571
|
+
import { hexlify as hexlify3 } from "ethers";
|
|
572
|
+
function assertRequestMatchesOutput(ipp, output) {
|
|
573
|
+
const request = decodePaymentRequest(ipp);
|
|
574
|
+
const expected = request.recipient;
|
|
575
|
+
const actual = output.recipient;
|
|
576
|
+
if (request.chainId !== output.context.chainId || BigInt(request.protocolAddress) !== BigInt(output.context.protocolAddress) || BigInt(request.tokenAddress) !== BigInt(output.context.tokenAddress) || request.amount !== output.amount || expected.type !== actual.type)
|
|
577
|
+
throw new Error("IPR does not match output context or recipient");
|
|
578
|
+
if (expected.type === "identity") {
|
|
579
|
+
if (actual.type !== "identity" || BigInt(expected.identity) !== BigInt(actual.identity))
|
|
580
|
+
throw new Error("IPR identity mismatch");
|
|
581
|
+
return;
|
|
582
|
+
}
|
|
583
|
+
if (actual.type === "identity") throw new Error("IPR recipient type mismatch");
|
|
584
|
+
const parsed = ReceivingAddress4.parse(expected.address);
|
|
585
|
+
const resolved = ReceivingAddress4.parse(actual.address);
|
|
586
|
+
if (parsed.ownerAddress !== resolved.ownerAddress || expected.nonce !== void 0 && expected.nonce !== output.note?.nonce || hexlify3(request.payload ?? new Uint8Array()) !== hexlify3(output.note?.payload ?? new Uint8Array()))
|
|
587
|
+
throw new Error("IPR does not match output opening");
|
|
588
|
+
if (expected.type === "evm" && actual.type === "evm" && expected.envelope !== false !== (actual.envelope !== false))
|
|
589
|
+
throw new Error("IPR envelope mismatch");
|
|
590
|
+
if (expected.type === "invisi") {
|
|
591
|
+
const a = parsed.getReceivingKeys(), b = resolved.getReceivingKeys();
|
|
592
|
+
if (hexlify3(a.viewingPublicKey) !== hexlify3(b.viewingPublicKey) || hexlify3(a.clueKey) !== hexlify3(b.clueKey))
|
|
593
|
+
throw new Error("IPR receiving keys mismatch");
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
// src/payment-request/execution.ts
|
|
598
|
+
function applyExecutionOptions(request, execution) {
|
|
599
|
+
fields(execution, ["nonce", "addressLookup"], "execution options");
|
|
600
|
+
const copy = structuredClone(request);
|
|
601
|
+
const recipient = copy.recipient;
|
|
602
|
+
if (execution.nonce !== void 0) {
|
|
603
|
+
if (recipient.type === "identity")
|
|
604
|
+
throw new TypeError("identity recipient cannot specify nonce");
|
|
605
|
+
nonce(execution.nonce);
|
|
606
|
+
if (recipient.nonce !== void 0 && recipient.nonce !== execution.nonce)
|
|
607
|
+
throw new Error("nonce conflicts with the fixed payment request");
|
|
608
|
+
recipient.nonce = execution.nonce;
|
|
609
|
+
}
|
|
610
|
+
if (execution.addressLookup !== void 0) {
|
|
611
|
+
if (recipient.type !== "evm" || recipient.envelope === false)
|
|
612
|
+
throw new TypeError("addressLookup requires an encrypted EVM recipient");
|
|
613
|
+
if (!["chain", "protocol-rpc"].includes(execution.addressLookup))
|
|
614
|
+
throw new TypeError("Invalid addressLookup");
|
|
615
|
+
recipient.addressLookup = execution.addressLookup;
|
|
616
|
+
}
|
|
617
|
+
return copy;
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
// src/notes/NoteOutput.ts
|
|
126
621
|
function nonceValue(value) {
|
|
127
|
-
if (typeof value !== "bigint" || value < 0n || value >=
|
|
622
|
+
if (typeof value !== "bigint" || value < 0n || value >= FIELD_MODULUS3)
|
|
128
623
|
throw new RangeError("nonce must be a canonical BN254 field element");
|
|
129
624
|
return value;
|
|
130
625
|
}
|
|
131
626
|
function identityValue(value) {
|
|
132
|
-
if (typeof value !== "string" || !/^0x[0-9a-fA-F]{64}$/.test(value) || BigInt(value) <= 0n || BigInt(value) >=
|
|
627
|
+
if (typeof value !== "string" || !/^0x[0-9a-fA-F]{64}$/.test(value) || BigInt(value) <= 0n || BigInt(value) >= FIELD_MODULUS3)
|
|
133
628
|
throw new RangeError("identity must be a nonzero canonical BN254 field element");
|
|
134
629
|
return value.toLowerCase();
|
|
135
630
|
}
|
|
@@ -146,8 +641,8 @@ function fingerprint(delivery) {
|
|
|
146
641
|
function checkedContext(context) {
|
|
147
642
|
if (typeof context.chainId !== "bigint" || context.chainId <= 0n)
|
|
148
643
|
throw new RangeError("chainId must be positive");
|
|
149
|
-
const protocolAddress =
|
|
150
|
-
const tokenAddress =
|
|
644
|
+
const protocolAddress = getAddress3(context.protocolAddress);
|
|
645
|
+
const tokenAddress = getAddress3(context.tokenAddress);
|
|
151
646
|
if (BigInt(protocolAddress) === 0n) throw new RangeError("protocol address must be nonzero");
|
|
152
647
|
return { chainId: context.chainId, protocolAddress, tokenAddress };
|
|
153
648
|
}
|
|
@@ -169,16 +664,18 @@ function rejectLegacy(value) {
|
|
|
169
664
|
var outputCreation = /* @__PURE__ */ Symbol("NoteOutput.create");
|
|
170
665
|
var NoteOutput = class _NoteOutput {
|
|
171
666
|
to;
|
|
667
|
+
ipp;
|
|
172
668
|
amount;
|
|
173
669
|
identity;
|
|
174
670
|
commitment;
|
|
175
671
|
deliveryFingerprint;
|
|
176
672
|
/** Encrypted EVM delivery is resolved by preparation; the original offline intent stays pending. */
|
|
177
673
|
deliveryPending;
|
|
178
|
-
constructor(output, token) {
|
|
674
|
+
constructor(output, token, ipp) {
|
|
179
675
|
if (token !== outputCreation)
|
|
180
676
|
throw new TypeError("outputs must be created with NoteOutput.create");
|
|
181
677
|
const saved = structuredClone(output);
|
|
678
|
+
this.ipp = ipp;
|
|
182
679
|
this.to = saved.address;
|
|
183
680
|
this.amount = saved.amount;
|
|
184
681
|
this.identity = saved.identity;
|
|
@@ -194,8 +691,15 @@ var NoteOutput = class _NoteOutput {
|
|
|
194
691
|
});
|
|
195
692
|
Object.freeze(this);
|
|
196
693
|
}
|
|
197
|
-
|
|
198
|
-
|
|
694
|
+
/** Private offline delivery data; does not assert that the output has been mined. */
|
|
695
|
+
exportPaymentNote() {
|
|
696
|
+
return exportPaymentNote(this.preparedOutput);
|
|
697
|
+
}
|
|
698
|
+
static async create(input, execution = {}) {
|
|
699
|
+
if (typeof input !== "string") rejectLegacy(input);
|
|
700
|
+
const request = typeof input === "string" ? decodePaymentRequest(input) : structuredClone(input);
|
|
701
|
+
rejectLegacy(request);
|
|
702
|
+
const options = applyExecutionOptions(request, execution);
|
|
199
703
|
if (typeof options.amount !== "bigint" || options.amount <= 0n || options.amount >= 1n << 128n)
|
|
200
704
|
throw new RangeError("amount must be a positive uint128");
|
|
201
705
|
const context = checkedContext(options);
|
|
@@ -223,7 +727,7 @@ var NoteOutput = class _NoteOutput {
|
|
|
223
727
|
throw new TypeError("invisi recipient is always encrypted");
|
|
224
728
|
let parsed;
|
|
225
729
|
try {
|
|
226
|
-
parsed =
|
|
730
|
+
parsed = ReceivingAddress5.parse(recipient.address);
|
|
227
731
|
} catch (cause) {
|
|
228
732
|
throw new InvisiError("INVALID_RECIPIENT", "Invalid recipient address", {
|
|
229
733
|
address: recipient.address,
|
|
@@ -240,11 +744,11 @@ var NoteOutput = class _NoteOutput {
|
|
|
240
744
|
recipient.addressLookup ??= "protocol-rpc";
|
|
241
745
|
} else if ("addressLookup" in recipient)
|
|
242
746
|
throw new TypeError("addressLookup only applies to EVM recipients");
|
|
243
|
-
const
|
|
747
|
+
const nonce2 = nonceValue(
|
|
244
748
|
recipient.nonce === void 0 ? generateNonce() : recipient.nonce
|
|
245
749
|
);
|
|
246
|
-
recipient.nonce =
|
|
247
|
-
identity = await computeNoteIdentity(BigInt(resolved.ownerAddress),
|
|
750
|
+
recipient.nonce = nonce2;
|
|
751
|
+
identity = await computeNoteIdentity(BigInt(resolved.ownerAddress), nonce2);
|
|
248
752
|
address = recipient.address;
|
|
249
753
|
note = {
|
|
250
754
|
chainId: context.chainId,
|
|
@@ -252,7 +756,7 @@ var NoteOutput = class _NoteOutput {
|
|
|
252
756
|
tokenAddress: BigInt(context.tokenAddress),
|
|
253
757
|
ownerAddress: resolved.ownerAddress,
|
|
254
758
|
amount: options.amount,
|
|
255
|
-
nonce,
|
|
759
|
+
nonce: nonce2,
|
|
256
760
|
payload: payloadCopy(options.payload)
|
|
257
761
|
};
|
|
258
762
|
}
|
|
@@ -266,16 +770,17 @@ var NoteOutput = class _NoteOutput {
|
|
|
266
770
|
if (envelope && recipient.type === "invisi") {
|
|
267
771
|
const keys = resolved.getReceivingKeys();
|
|
268
772
|
delivery = {
|
|
269
|
-
ciphertext:
|
|
773
|
+
ciphertext: hexlify4(
|
|
270
774
|
await encryptPrivateTransferNote(keys.viewingPublicKey, note, {
|
|
271
775
|
chainId: context.chainId,
|
|
272
776
|
protocolAddress: BigInt(context.protocolAddress),
|
|
273
777
|
outputCommitment: commitment
|
|
274
778
|
})
|
|
275
779
|
),
|
|
276
|
-
clue:
|
|
780
|
+
clue: hexlify4(createClue(keys.clueKey))
|
|
277
781
|
};
|
|
278
782
|
}
|
|
783
|
+
const ipp = typeof input === "string" ? input : encodePaymentRequest(request);
|
|
279
784
|
return new _NoteOutput(
|
|
280
785
|
{
|
|
281
786
|
context,
|
|
@@ -289,7 +794,8 @@ var NoteOutput = class _NoteOutput {
|
|
|
289
794
|
address,
|
|
290
795
|
change: false
|
|
291
796
|
},
|
|
292
|
-
outputCreation
|
|
797
|
+
outputCreation,
|
|
798
|
+
ipp
|
|
293
799
|
);
|
|
294
800
|
}
|
|
295
801
|
assertCommitment(expectedHex) {
|
|
@@ -335,14 +841,14 @@ async function materializeNoteOutput(payment, context, lookup) {
|
|
|
335
841
|
identityValue(output.identity);
|
|
336
842
|
} else {
|
|
337
843
|
const note = output.note;
|
|
338
|
-
const parsed =
|
|
844
|
+
const parsed = ReceivingAddress5.parse(recipient.address);
|
|
339
845
|
if (recipient.type === "evm" && recipient.addressLookup !== void 0 && recipient.addressLookup !== "chain" && recipient.addressLookup !== "protocol-rpc")
|
|
340
846
|
throw new Error("invalid recipient addressLookup");
|
|
341
847
|
if (recipient.type === "invisi" && "addressLookup" in recipient)
|
|
342
848
|
throw new Error("addressLookup only applies to EVM recipients");
|
|
343
849
|
if (output.address !== recipient.address || recipient.type === "evm" && recipient.envelope !== void 0 && typeof recipient.envelope !== "boolean" || recipient.type === "invisi" && "envelope" in recipient)
|
|
344
850
|
throw new Error("recipient material mismatch");
|
|
345
|
-
if (!note || note.chainId !== context.chainId || note.protocolAddress !== BigInt(context.protocolAddress) || note.tokenAddress !== BigInt(context.tokenAddress) || note.ownerAddress !== parsed.ownerAddress || note.amount !== output.amount || note.nonce !== recipient.nonce || note.nonce !== payment.nonce ||
|
|
851
|
+
if (!note || note.chainId !== context.chainId || note.protocolAddress !== BigInt(context.protocolAddress) || note.tokenAddress !== BigInt(context.tokenAddress) || note.ownerAddress !== parsed.ownerAddress || note.amount !== output.amount || note.nonce !== recipient.nonce || note.nonce !== payment.nonce || hexlify4(payloadCopy(note.payload)) !== hexlify4(payloadCopy(payment.payload)))
|
|
346
852
|
throw new Error("note output opening does not match payment intent");
|
|
347
853
|
if (recipient.type === "evm" !== (parsed.kind === "short"))
|
|
348
854
|
throw new Error("recipient type mismatch");
|
|
@@ -356,6 +862,7 @@ async function materializeNoteOutput(payment, context, lookup) {
|
|
|
356
862
|
output.amount
|
|
357
863
|
) !== output.commitment)
|
|
358
864
|
throw new Error("note output commitment mismatch");
|
|
865
|
+
assertRequestMatchesOutput(payment.ipp, output);
|
|
359
866
|
if (pending) {
|
|
360
867
|
if (recipient.type !== "evm") throw new Error("only EVM outputs may have pending delivery");
|
|
361
868
|
const resolved = await resolveRecipient(
|
|
@@ -365,14 +872,14 @@ async function materializeNoteOutput(payment, context, lookup) {
|
|
|
365
872
|
);
|
|
366
873
|
const keys = resolved.getReceivingKeys();
|
|
367
874
|
output.delivery = {
|
|
368
|
-
ciphertext:
|
|
875
|
+
ciphertext: hexlify4(
|
|
369
876
|
await encryptPrivateTransferNote(keys.viewingPublicKey, output.note, {
|
|
370
877
|
chainId: context.chainId,
|
|
371
878
|
protocolAddress: BigInt(context.protocolAddress),
|
|
372
879
|
outputCommitment: output.commitment
|
|
373
880
|
})
|
|
374
881
|
),
|
|
375
|
-
clue:
|
|
882
|
+
clue: hexlify4(createClue(keys.clueKey))
|
|
376
883
|
};
|
|
377
884
|
delete output.deliveryPending;
|
|
378
885
|
}
|
|
@@ -388,427 +895,10 @@ function resolvedOutputSnapshot(payment, output) {
|
|
|
388
895
|
return copy;
|
|
389
896
|
}
|
|
390
897
|
|
|
391
|
-
// src/
|
|
392
|
-
function creditReservationKey(commitment) {
|
|
393
|
-
return "credit:" + commitment;
|
|
394
|
-
}
|
|
395
|
-
function walletBalance(state, tokenAddress) {
|
|
396
|
-
let confirmedKnown = 0n;
|
|
397
|
-
let reserved = 0n;
|
|
398
|
-
let unknown = !!state.invalidated;
|
|
399
|
-
const records = Object.values(state.notes).filter(
|
|
400
|
-
(r) => r.note.tokenAddress === BigInt(tokenAddress)
|
|
401
|
-
);
|
|
402
|
-
const points = records.map((record) => record.checkedThrough);
|
|
403
|
-
let checkedThrough = null;
|
|
404
|
-
if (records.length && points.every((point) => point !== void 0) && records.every((record) => record.spent !== "unknown")) {
|
|
405
|
-
checkedThrough = points.reduce(
|
|
406
|
-
(earliest, point) => point.number < earliest.number ? point : earliest
|
|
407
|
-
);
|
|
408
|
-
} else if (!records.length) {
|
|
409
|
-
checkedThrough = state.observedThrough ?? null;
|
|
410
|
-
}
|
|
411
|
-
if (!records.length && !checkedThrough) {
|
|
412
|
-
unknown = true;
|
|
413
|
-
}
|
|
414
|
-
for (const [id, record] of Object.entries(state.notes)) {
|
|
415
|
-
if (record.note.tokenAddress !== BigInt(tokenAddress)) continue;
|
|
416
|
-
if (record.spent !== "spent") {
|
|
417
|
-
confirmedKnown += record.note.amount;
|
|
418
|
-
if (record.spent === "unknown") {
|
|
419
|
-
unknown = true;
|
|
420
|
-
}
|
|
421
|
-
if (state.reservations[id]) {
|
|
422
|
-
reserved += record.note.amount;
|
|
423
|
-
}
|
|
424
|
-
}
|
|
425
|
-
}
|
|
426
|
-
return {
|
|
427
|
-
tokenAddress,
|
|
428
|
-
scope: { ...state.scope },
|
|
429
|
-
checkedThrough: state.invalidated ? null : checkedThrough ? { ...checkedThrough } : null,
|
|
430
|
-
confirmedKnown,
|
|
431
|
-
reserved,
|
|
432
|
-
availableKnown: unknown ? null : confirmedKnown - reserved,
|
|
433
|
-
invalidated: !!state.invalidated
|
|
434
|
-
};
|
|
435
|
-
}
|
|
436
|
-
function walletBalances(state) {
|
|
437
|
-
const tokens = new Set(
|
|
438
|
-
Object.values(state.notes).map(
|
|
439
|
-
(r) => `0x${r.note.tokenAddress.toString(16).padStart(40, "0")}`
|
|
440
|
-
)
|
|
441
|
-
);
|
|
442
|
-
return [...tokens].sort().map((token) => walletBalance(state, token));
|
|
443
|
-
}
|
|
444
|
-
|
|
445
|
-
// src/session/operation-protocol.ts
|
|
446
|
-
import { Interface } from "ethers";
|
|
447
|
-
import { invisiProtocolAbi } from "@invisi-labs/protocol/contracts";
|
|
448
|
-
import { creditPeriods } from "@invisi-labs/core";
|
|
449
|
-
import {
|
|
450
|
-
WitnessBuilder,
|
|
451
|
-
normalizeFunding,
|
|
452
|
-
buildCancelCreditCalldata,
|
|
453
|
-
buildCreateCreditCalldata,
|
|
454
|
-
buildCreditDrawCalldata,
|
|
455
|
-
buildShieldCalldata,
|
|
456
|
-
buildTopUpCreditCalldata,
|
|
457
|
-
buildTransactCalldata,
|
|
458
|
-
cancelCreditPublicInputs,
|
|
459
|
-
createCreditPublicInputs,
|
|
460
|
-
creditDrawPublicInputs,
|
|
461
|
-
encodeAggregateFundingTypedData,
|
|
462
|
-
encodeCreateCreditFundingTypedData,
|
|
463
|
-
encodeShieldFundingTypedData,
|
|
464
|
-
encodeTopUpCreditFundingTypedData,
|
|
465
|
-
encodeTransactFundingTypedData,
|
|
466
|
-
topUpCreditPublicInputs,
|
|
467
|
-
transactUserPublicInputs
|
|
468
|
-
} from "@invisi-labs/protocol";
|
|
469
|
-
function requiresAuthorization(plan) {
|
|
470
|
-
const ownerOnlyFullWithdrawal = !plan.credit && plan.inputs.length > 0 && plan.outputs.length === 0 && plan.params !== void 0 && plan.params.outputCommitments.length === 0 && (plan.params.feeAmount ?? 0n) === 0n && BigInt(plan.params.feeRecipient ?? 0n) === 0n && (plan.funding.amount ?? 0n) === 0n && plan.fundingMode === "none" && BigInt(plan.params.withdrawalAddress) === BigInt(plan.scope.ownerAddress) && plan.params.withdrawalAmount === plan.inputs.reduce((sum, input) => sum + input.note.amount, 0n) && plan.inputs.every((input) => input.note.ownerAddress === plan.scope.ownerAddress);
|
|
471
|
-
if (ownerOnlyFullWithdrawal) return false;
|
|
472
|
-
return !!plan.credit || plan.inputs.length > 0;
|
|
473
|
-
}
|
|
474
|
-
function inputFacts(plan) {
|
|
475
|
-
const credit = plan.credit;
|
|
476
|
-
const noteNullifiers = plan.params?.nullifiers ?? credit?.creation?.nullifiers ?? [];
|
|
477
|
-
let creditNullifiers = [];
|
|
478
|
-
if (credit?.draw) {
|
|
479
|
-
creditNullifiers = credit.draw.nullifiers;
|
|
480
|
-
} else if (credit?.topUp) {
|
|
481
|
-
creditNullifiers = [credit.topUp.creditNullifier];
|
|
482
|
-
} else if (credit?.cancel) {
|
|
483
|
-
creditNullifiers = [credit.cancel.nullifier];
|
|
484
|
-
}
|
|
485
|
-
if (noteNullifiers.length !== plan.inputs.length || creditNullifiers.length !== (credit?.inputs.length ?? 0)) {
|
|
486
|
-
throw new Error("operation input statement count mismatch");
|
|
487
|
-
}
|
|
488
|
-
return [
|
|
489
|
-
...plan.inputs.map((input, index) => ({
|
|
490
|
-
key: input.commitment,
|
|
491
|
-
reservationKey: input.commitment,
|
|
492
|
-
nullifier: noteNullifiers[index],
|
|
493
|
-
kind: "notes"
|
|
494
|
-
})),
|
|
495
|
-
...(credit?.inputs ?? []).map((input, index) => ({
|
|
496
|
-
key: input.commitment + ":" + input.role,
|
|
497
|
-
reservationKey: creditReservationKey(input.commitment),
|
|
498
|
-
nullifier: creditNullifiers[index],
|
|
499
|
-
kind: "credits"
|
|
500
|
-
}))
|
|
501
|
-
];
|
|
502
|
-
}
|
|
503
|
-
function operationSources(plan) {
|
|
504
|
-
const noteSources = plan.params?.sources;
|
|
505
|
-
if (noteSources != null) return noteSources;
|
|
506
|
-
const credit = plan.credit;
|
|
507
|
-
if (credit?.topUp) {
|
|
508
|
-
return [...credit.topUp.creation.sources, credit.topUp.creditSource];
|
|
509
|
-
}
|
|
510
|
-
if (credit?.creation) return credit.creation.sources;
|
|
511
|
-
if (credit?.draw) return credit.draw.sources;
|
|
512
|
-
if (credit?.cancel) return [credit.cancel.source];
|
|
513
|
-
return [];
|
|
514
|
-
}
|
|
515
|
-
function statement(plan) {
|
|
516
|
-
if (!plan.statement) throw new Error("operation has no proof statement");
|
|
517
|
-
const credit = plan.credit;
|
|
518
|
-
let words;
|
|
519
|
-
if (credit?.topUp) {
|
|
520
|
-
words = topUpCreditPublicInputs(
|
|
521
|
-
plan.context,
|
|
522
|
-
credit.topUp,
|
|
523
|
-
credit.deliveries,
|
|
524
|
-
plan.funding
|
|
525
|
-
);
|
|
526
|
-
} else if (credit?.creation) {
|
|
527
|
-
words = createCreditPublicInputs(
|
|
528
|
-
plan.context,
|
|
529
|
-
credit.creation,
|
|
530
|
-
credit.deliveries,
|
|
531
|
-
plan.funding
|
|
532
|
-
);
|
|
533
|
-
} else if (credit?.draw) {
|
|
534
|
-
words = creditDrawPublicInputs(plan.context, credit.draw, credit.deliveries);
|
|
535
|
-
} else if (credit?.cancel) {
|
|
536
|
-
words = cancelCreditPublicInputs(plan.context, credit.cancel);
|
|
537
|
-
} else {
|
|
538
|
-
words = transactUserPublicInputs(
|
|
539
|
-
plan.context,
|
|
540
|
-
plan.params,
|
|
541
|
-
plan.outputs.map((output) => output.delivery),
|
|
542
|
-
plan.funding
|
|
543
|
-
);
|
|
544
|
-
}
|
|
545
|
-
const result = WitnessBuilder.statement(
|
|
546
|
-
plan.statement.circuitName,
|
|
547
|
-
words,
|
|
548
|
-
plan.statement.target
|
|
549
|
-
);
|
|
550
|
-
if (JSON.stringify(result) !== JSON.stringify(plan.statement)) {
|
|
551
|
-
throw new Error("frozen operation statement changed");
|
|
552
|
-
}
|
|
553
|
-
return result;
|
|
554
|
-
}
|
|
555
|
-
function authorizationRequest(plan) {
|
|
556
|
-
let operation = "transact";
|
|
557
|
-
if (plan.kind === "draw-credit") {
|
|
558
|
-
operation = "credit-draw";
|
|
559
|
-
} else if (plan.kind === "create-credit" || plan.kind === "top-up-credit" || plan.kind === "cancel-credit") {
|
|
560
|
-
operation = plan.kind;
|
|
561
|
-
}
|
|
562
|
-
return {
|
|
563
|
-
context: {
|
|
564
|
-
chainId: plan.context.chainId,
|
|
565
|
-
protocolAddress: BigInt(plan.context.protocolAddress)
|
|
566
|
-
},
|
|
567
|
-
operation,
|
|
568
|
-
capacity: plan.capacity,
|
|
569
|
-
words: statement(plan).expectedPublicInputs.slice(0, -2).map(BigInt)
|
|
570
|
-
};
|
|
571
|
-
}
|
|
572
|
-
async function witnessFor(protocol, plan, authorization) {
|
|
573
|
-
const builder = new WitnessBuilder(protocol);
|
|
574
|
-
const credit = plan.credit;
|
|
575
|
-
if (credit) {
|
|
576
|
-
if (!authorization) throw new Error("private Credit wallet authorization required");
|
|
577
|
-
if (credit.topUp) {
|
|
578
|
-
return builder.topUpCredit({
|
|
579
|
-
params: credit.topUp,
|
|
580
|
-
deliveries: credit.deliveries,
|
|
581
|
-
funding: plan.funding,
|
|
582
|
-
inputs: plan.inputs,
|
|
583
|
-
state: credit.outputs[0].state,
|
|
584
|
-
...plan.outputs[0] ? { change: plan.outputs[0].note } : {},
|
|
585
|
-
previous: credit.inputs[0],
|
|
586
|
-
authorization
|
|
587
|
-
});
|
|
588
|
-
}
|
|
589
|
-
if (credit.creation) {
|
|
590
|
-
return builder.createCredit({
|
|
591
|
-
params: credit.creation,
|
|
592
|
-
deliveries: credit.deliveries,
|
|
593
|
-
funding: plan.funding,
|
|
594
|
-
inputs: plan.inputs,
|
|
595
|
-
state: credit.outputs[0].state,
|
|
596
|
-
...plan.outputs[0] ? { change: plan.outputs[0].note } : {},
|
|
597
|
-
authorization
|
|
598
|
-
});
|
|
599
|
-
}
|
|
600
|
-
if (credit.draw) {
|
|
601
|
-
return builder.drawCredit({
|
|
602
|
-
timestamp: plan.preparedAt.timestamp,
|
|
603
|
-
params: credit.draw,
|
|
604
|
-
deliveries: credit.deliveries,
|
|
605
|
-
inputs: credit.inputs,
|
|
606
|
-
amounts: credit.amounts,
|
|
607
|
-
authorization
|
|
608
|
-
});
|
|
609
|
-
}
|
|
610
|
-
if (credit.cancel) {
|
|
611
|
-
return builder.cancelCredit({
|
|
612
|
-
params: credit.cancel,
|
|
613
|
-
input: credit.inputs[0],
|
|
614
|
-
authorization
|
|
615
|
-
});
|
|
616
|
-
}
|
|
617
|
-
throw new Error("unsupported Credit operation");
|
|
618
|
-
}
|
|
619
|
-
return builder.transact({
|
|
620
|
-
params: plan.params,
|
|
621
|
-
deliveries: plan.outputs.map((output) => output.delivery),
|
|
622
|
-
funding: plan.funding,
|
|
623
|
-
inputs: plan.inputs,
|
|
624
|
-
outputs: plan.outputs.map((output) => ({
|
|
625
|
-
identity: output.identity,
|
|
626
|
-
amount: output.amount,
|
|
627
|
-
...output.note ? { note: output.note } : {}
|
|
628
|
-
})),
|
|
629
|
-
...authorization ? { authorization } : {},
|
|
630
|
-
target: plan.statement.target
|
|
631
|
-
});
|
|
632
|
-
}
|
|
633
|
-
async function encodeOperation(saved) {
|
|
634
|
-
const plan = saved.plan;
|
|
635
|
-
const credit = plan.credit;
|
|
636
|
-
const caller = plan.fundingMode === "caller" ? plan.funding.payer : void 0;
|
|
637
|
-
const common = { context: plan.context, proof: saved.proof?.proofHex ?? "0x" };
|
|
638
|
-
const funded = { ...common, funding: plan.funding, ...caller ? { caller } : {} };
|
|
639
|
-
let encoded;
|
|
640
|
-
if (credit?.topUp) {
|
|
641
|
-
encoded = buildTopUpCreditCalldata({
|
|
642
|
-
...funded,
|
|
643
|
-
params: credit.topUp,
|
|
644
|
-
deliveries: credit.deliveries
|
|
645
|
-
});
|
|
646
|
-
} else if (credit?.creation) {
|
|
647
|
-
encoded = buildCreateCreditCalldata({
|
|
648
|
-
...funded,
|
|
649
|
-
params: credit.creation,
|
|
650
|
-
deliveries: credit.deliveries
|
|
651
|
-
});
|
|
652
|
-
} else if (credit?.draw) {
|
|
653
|
-
encoded = buildCreditDrawCalldata({
|
|
654
|
-
...common,
|
|
655
|
-
params: credit.draw,
|
|
656
|
-
deliveries: credit.deliveries
|
|
657
|
-
});
|
|
658
|
-
} else if (credit?.cancel) {
|
|
659
|
-
encoded = buildCancelCreditCalldata({ ...common, params: credit.cancel });
|
|
660
|
-
} else if (plan.kind === "shield") {
|
|
661
|
-
encoded = await buildShieldCalldata({
|
|
662
|
-
...funded,
|
|
663
|
-
params: plan.shieldParams,
|
|
664
|
-
outputs: plan.shieldOutputs,
|
|
665
|
-
deliveries: plan.outputs.map((output) => output.delivery)
|
|
666
|
-
});
|
|
667
|
-
} else {
|
|
668
|
-
encoded = buildTransactCalldata({
|
|
669
|
-
...funded,
|
|
670
|
-
params: plan.params,
|
|
671
|
-
deliveries: plan.outputs.map((output) => output.delivery)
|
|
672
|
-
});
|
|
673
|
-
}
|
|
674
|
-
return { to: plan.context.protocolAddress, data: encoded.data, value: 0n };
|
|
675
|
-
}
|
|
676
|
-
async function fundingTypedData(saved) {
|
|
677
|
-
const plan = saved.plan;
|
|
678
|
-
const credit = plan.credit;
|
|
679
|
-
if (credit?.topUp) {
|
|
680
|
-
return encodeTopUpCreditFundingTypedData(
|
|
681
|
-
plan.context,
|
|
682
|
-
credit.topUp,
|
|
683
|
-
credit.deliveries,
|
|
684
|
-
plan.funding
|
|
685
|
-
);
|
|
686
|
-
}
|
|
687
|
-
if (credit?.creation) {
|
|
688
|
-
return encodeCreateCreditFundingTypedData(
|
|
689
|
-
plan.context,
|
|
690
|
-
credit.creation,
|
|
691
|
-
credit.deliveries,
|
|
692
|
-
plan.funding
|
|
693
|
-
);
|
|
694
|
-
}
|
|
695
|
-
if (plan.kind === "shield") {
|
|
696
|
-
return encodeShieldFundingTypedData({
|
|
697
|
-
context: plan.context,
|
|
698
|
-
params: plan.shieldParams,
|
|
699
|
-
outputs: plan.shieldOutputs,
|
|
700
|
-
deliveries: plan.outputs.map((output) => output.delivery),
|
|
701
|
-
funding: plan.funding
|
|
702
|
-
});
|
|
703
|
-
}
|
|
704
|
-
if (plan.statement?.target === "noir-recursive") {
|
|
705
|
-
return encodeAggregateFundingTypedData(plan.context, {
|
|
706
|
-
params: plan.params,
|
|
707
|
-
deliveries: plan.outputs.map((output) => output.delivery),
|
|
708
|
-
funding: plan.funding
|
|
709
|
-
});
|
|
710
|
-
}
|
|
711
|
-
return encodeTransactFundingTypedData(
|
|
712
|
-
plan.context,
|
|
713
|
-
saved.proof.proofHex,
|
|
714
|
-
plan.params,
|
|
715
|
-
plan.outputs.map((output) => output.delivery),
|
|
716
|
-
plan.funding
|
|
717
|
-
);
|
|
718
|
-
}
|
|
719
|
-
function publicProof(bundle) {
|
|
720
|
-
return {
|
|
721
|
-
formatVersion: bundle.formatVersion,
|
|
722
|
-
circuitName: bundle.circuitName,
|
|
723
|
-
target: bundle.target,
|
|
724
|
-
bbVersion: bundle.bbVersion,
|
|
725
|
-
artifactSha256: bundle.artifactSha256,
|
|
726
|
-
vkSha256: bundle.vkSha256,
|
|
727
|
-
proofHex: bundle.proofHex,
|
|
728
|
-
proofSha256: bundle.proofSha256,
|
|
729
|
-
publicInputs: [...bundle.publicInputs]
|
|
730
|
-
};
|
|
731
|
-
}
|
|
732
|
-
function operationExpired(plan, now) {
|
|
733
|
-
if (now > plan.deadline) return true;
|
|
734
|
-
const credit = plan.credit;
|
|
735
|
-
if (credit?.topUp && now >= credit.topUp.previousExpiry) return true;
|
|
736
|
-
if (credit?.creation && now >= credit.creation.expiry) return true;
|
|
737
|
-
if (credit?.draw) {
|
|
738
|
-
if (credit.draw.expiries.some((expiry) => now >= expiry)) return true;
|
|
739
|
-
const prepared = creditPeriods(plan.preparedAt.timestamp), current = creditPeriods(now);
|
|
740
|
-
if (prepared.currentDay !== current.currentDay || prepared.currentMonth !== current.currentMonth)
|
|
741
|
-
return true;
|
|
742
|
-
}
|
|
743
|
-
return false;
|
|
744
|
-
}
|
|
745
|
-
function exportBroadcasterIntent(plan) {
|
|
746
|
-
if (plan.kind === "draw-credit" || plan.statement && plan.statement.target !== "evm")
|
|
747
|
-
throw new Error("unsupported broadcaster operation");
|
|
748
|
-
if (plan.statement) statement(plan);
|
|
749
|
-
const feeDefaults = (params) => ({
|
|
750
|
-
...params,
|
|
751
|
-
feeRecipient: params.feeRecipient ?? "0x0000000000000000000000000000000000000000",
|
|
752
|
-
feeAmount: params.feeAmount ?? 0n
|
|
753
|
-
});
|
|
754
|
-
const funding = { ...normalizeFunding(plan.funding), signature: "0x" };
|
|
755
|
-
let method;
|
|
756
|
-
let args;
|
|
757
|
-
if (plan.params) {
|
|
758
|
-
method = "transact";
|
|
759
|
-
args = [
|
|
760
|
-
"0x",
|
|
761
|
-
{
|
|
762
|
-
params: feeDefaults(plan.params),
|
|
763
|
-
deliveries: plan.outputs.map((o) => o.delivery),
|
|
764
|
-
funding
|
|
765
|
-
}
|
|
766
|
-
];
|
|
767
|
-
} else if (plan.shieldParams) {
|
|
768
|
-
method = "shield";
|
|
769
|
-
args = [
|
|
770
|
-
feeDefaults(plan.shieldParams),
|
|
771
|
-
plan.shieldOutputs,
|
|
772
|
-
plan.outputs.map((o) => o.delivery),
|
|
773
|
-
funding
|
|
774
|
-
];
|
|
775
|
-
} else if (plan.credit?.topUp) {
|
|
776
|
-
method = "topUpCredit";
|
|
777
|
-
args = [
|
|
778
|
-
"0x",
|
|
779
|
-
{ ...plan.credit.topUp, creation: feeDefaults(plan.credit.topUp.creation) },
|
|
780
|
-
plan.credit.deliveries,
|
|
781
|
-
funding
|
|
782
|
-
];
|
|
783
|
-
} else if (plan.credit?.creation) {
|
|
784
|
-
method = "createCredit";
|
|
785
|
-
args = ["0x", feeDefaults(plan.credit.creation), plan.credit.deliveries, funding];
|
|
786
|
-
} else if (plan.credit?.cancel) {
|
|
787
|
-
method = "cancelCredit";
|
|
788
|
-
args = ["0x", feeDefaults(plan.credit.cancel)];
|
|
789
|
-
} else throw new Error("unsupported broadcaster operation");
|
|
790
|
-
const abi = invisiProtocolAbi.filter(
|
|
791
|
-
(item) => item.type === "function" && item.name === method && (method !== "transact" || item.inputs[1]?.type === "tuple")
|
|
792
|
-
);
|
|
793
|
-
return {
|
|
794
|
-
sendable: false,
|
|
795
|
-
data: new Interface(abi).encodeFunctionData(method, args)
|
|
796
|
-
};
|
|
797
|
-
}
|
|
798
|
-
|
|
799
|
-
// src/session/transaction-preparation.ts
|
|
800
|
-
import { getAddress as getAddress4 } from "ethers";
|
|
801
|
-
import {
|
|
802
|
-
normalizeTransactRequest as normalizeTransactRequest2,
|
|
803
|
-
transactUserPublicInputs as transactUserPublicInputs2,
|
|
804
|
-
WitnessBuilder as WitnessBuilder3,
|
|
805
|
-
ZERO_ADDRESS
|
|
806
|
-
} from "@invisi-labs/protocol";
|
|
807
|
-
|
|
808
|
-
// src/recipient-infrastructure.ts
|
|
898
|
+
// src/notes/recipient-infrastructure.ts
|
|
809
899
|
import {
|
|
810
900
|
MissingCapabilityError as MissingCapabilityError3,
|
|
811
|
-
ReceivingAddress as
|
|
901
|
+
ReceivingAddress as ReceivingAddress6
|
|
812
902
|
} from "@invisi-labs/core";
|
|
813
903
|
import { ProtocolRpcClient } from "@invisi-labs/rpc";
|
|
814
904
|
var recipientInfrastructure = /* @__PURE__ */ Symbol("SDK recipient infrastructure");
|
|
@@ -837,7 +927,7 @@ function configureRecipientInfrastructure(options) {
|
|
|
837
927
|
if (discovered.protocol.address !== deployment.protocol.address || discovered.protocol.runtimeCodeHash !== deployment.protocol.runtimeCodeHash || discovered.protocol.fromBlock !== deployment.protocol.fromBlock || discovered.registry?.address !== registry.address || discovered.registry.runtimeCodeHash !== registry.runtimeCodeHash || discovered.registry.fromBlock !== registry.fromBlock)
|
|
838
928
|
throw new Error("Recipient registry deployment pins mismatch");
|
|
839
929
|
const registration = await rpc.getReceivingAddress(ownerAddress);
|
|
840
|
-
return registration ?
|
|
930
|
+
return registration ? ReceivingAddress6.parse(registration.receivingAddress).getReceivingKeys() : null;
|
|
841
931
|
};
|
|
842
932
|
return { ...options, [recipientInfrastructure]: lookup };
|
|
843
933
|
}
|
|
@@ -849,26 +939,15 @@ function recipientLookup(options, source = "protocol-rpc") {
|
|
|
849
939
|
};
|
|
850
940
|
}
|
|
851
941
|
|
|
852
|
-
// src/
|
|
853
|
-
import {
|
|
854
|
-
import {
|
|
855
|
-
MissingCapabilityError as MissingCapabilityError4,
|
|
856
|
-
computeNoteCommitment as computeNoteCommitment2,
|
|
857
|
-
computeNoteIdentity as computeNoteIdentity2
|
|
858
|
-
} from "@invisi-labs/core";
|
|
859
|
-
import {
|
|
860
|
-
normalizeFunding as normalizeFunding2
|
|
861
|
-
} from "@invisi-labs/protocol";
|
|
862
|
-
|
|
863
|
-
// src/session/membership.ts
|
|
864
|
-
import { FIELD_MODULUS as FIELD_MODULUS2, poseidonHash } from "@invisi-labs/core";
|
|
942
|
+
// src/discovery/membership.ts
|
|
943
|
+
import { FIELD_MODULUS as FIELD_MODULUS4, poseidonHash } from "@invisi-labs/core";
|
|
865
944
|
async function verifyMembership(reader, context, block, input, proof) {
|
|
866
945
|
if (proof.block.hash !== block.hash || proof.block.number !== block.number || proof.leaf !== input.commitment || proof.treeId !== input.treeId || proof.leafIndex !== input.leafIndex || proof.path.length !== 16 || !Number.isInteger(input.leafIndex) || input.leafIndex < 0 || input.leafIndex >= 65536 || !await reader.isKnownRoot(context, input.treeId, proof.root, block))
|
|
867
946
|
throw new Error("membership context/root mismatch");
|
|
868
947
|
let root = BigInt(input.commitment);
|
|
869
948
|
for (let i = 0; i < 16; i++) {
|
|
870
949
|
const sibling = proof.path[i];
|
|
871
|
-
if (typeof sibling !== "bigint" || sibling < 0n || sibling >=
|
|
950
|
+
if (typeof sibling !== "bigint" || sibling < 0n || sibling >= FIELD_MODULUS4)
|
|
872
951
|
throw new Error("noncanonical membership path");
|
|
873
952
|
root = BigInt(
|
|
874
953
|
await poseidonHash(input.leafIndex >> i & 1 ? [sibling, root] : [root, sibling])
|
|
@@ -878,7 +957,49 @@ async function verifyMembership(reader, context, block, input, proof) {
|
|
|
878
957
|
throw new Error("membership path does not produce canonical root");
|
|
879
958
|
}
|
|
880
959
|
|
|
881
|
-
// src/
|
|
960
|
+
// src/transactions/preparation/credit-values.ts
|
|
961
|
+
import {
|
|
962
|
+
computeCreditCommitment,
|
|
963
|
+
computeCreditSharedKeyHash
|
|
964
|
+
} from "@invisi-labs/core";
|
|
965
|
+
async function creditLeaf(state) {
|
|
966
|
+
const n = state.note;
|
|
967
|
+
return computeCreditCommitment(
|
|
968
|
+
n.chainId,
|
|
969
|
+
n.spenderWalletIdentity,
|
|
970
|
+
n.ownerWalletIdentity,
|
|
971
|
+
n.spenderPayout,
|
|
972
|
+
n.ownerPayout,
|
|
973
|
+
n.tokenAddress,
|
|
974
|
+
n.amount,
|
|
975
|
+
n.nonce,
|
|
976
|
+
n.expiry,
|
|
977
|
+
await computeCreditSharedKeyHash(n.creditSharedKey),
|
|
978
|
+
n
|
|
979
|
+
);
|
|
980
|
+
}
|
|
981
|
+
|
|
982
|
+
// src/transactions/preparation/transaction-preparation.ts
|
|
983
|
+
import {
|
|
984
|
+
normalizeTransactRequest,
|
|
985
|
+
transactUserPublicInputs as transactUserPublicInputs2,
|
|
986
|
+
WitnessBuilder as WitnessBuilder3,
|
|
987
|
+
ZERO_ADDRESS
|
|
988
|
+
} from "@invisi-labs/protocol";
|
|
989
|
+
import { getAddress as getAddress6 } from "ethers";
|
|
990
|
+
|
|
991
|
+
// src/transactions/preparation/prepare-common.ts
|
|
992
|
+
import { getAddress as getAddress4 } from "ethers";
|
|
993
|
+
import {
|
|
994
|
+
MissingCapabilityError as MissingCapabilityError4,
|
|
995
|
+
computeNoteCommitment as computeNoteCommitment2,
|
|
996
|
+
computeNoteIdentity as computeNoteIdentity2
|
|
997
|
+
} from "@invisi-labs/core";
|
|
998
|
+
import {
|
|
999
|
+
normalizeFunding as normalizeFunding2
|
|
1000
|
+
} from "@invisi-labs/protocol";
|
|
1001
|
+
|
|
1002
|
+
// src/transactions/preparation/selection.ts
|
|
882
1003
|
function selectNotes(notes, target, reserved) {
|
|
883
1004
|
if (typeof target !== "bigint" || target < 0n)
|
|
884
1005
|
throw new Error("selection target must be a nonnegative amount");
|
|
@@ -902,7 +1023,7 @@ function selectNotes(notes, target, reserved) {
|
|
|
902
1023
|
throw new Error("insufficient verified available funds");
|
|
903
1024
|
}
|
|
904
1025
|
|
|
905
|
-
// src/
|
|
1026
|
+
// src/transactions/preparation/prepare-common.ts
|
|
906
1027
|
function positiveAmount(value, bits = 128) {
|
|
907
1028
|
if (typeof value !== "bigint" || value <= 0n || value >= 1n << BigInt(bits)) {
|
|
908
1029
|
throw new Error(`amount must be a positive uint${bits}`);
|
|
@@ -972,7 +1093,7 @@ var Preparation = class {
|
|
|
972
1093
|
return { funding: {}, fundingMode: "none" };
|
|
973
1094
|
}
|
|
974
1095
|
positiveAmount(source.amount);
|
|
975
|
-
const payer =
|
|
1096
|
+
const payer = getAddress4(source.payer);
|
|
976
1097
|
const mode = source.authorization ?? "signature";
|
|
977
1098
|
if (BigInt(payer) === 0n) {
|
|
978
1099
|
throw new Error("funding payer must be nonzero");
|
|
@@ -1003,7 +1124,9 @@ var Preparation = class {
|
|
|
1003
1124
|
const address = this.runtime.changeAddress ?? this.runtime.options.wallet.getReceivingAddress().toString();
|
|
1004
1125
|
try {
|
|
1005
1126
|
const output = await NoteOutput.create({
|
|
1006
|
-
|
|
1127
|
+
chainId: context.chainId,
|
|
1128
|
+
protocolAddress: context.protocolAddress,
|
|
1129
|
+
tokenAddress: context.tokenAddress,
|
|
1007
1130
|
amount,
|
|
1008
1131
|
recipient: address.startsWith("0i") ? { type: "invisi", address } : { type: "evm", address }
|
|
1009
1132
|
});
|
|
@@ -1156,11 +1279,10 @@ function storedCandidates(inputs) {
|
|
|
1156
1279
|
return structuredClone(records);
|
|
1157
1280
|
}
|
|
1158
1281
|
|
|
1159
|
-
// src/
|
|
1160
|
-
import { getAddress as
|
|
1282
|
+
// src/transactions/preparation/prepare-credit.ts
|
|
1283
|
+
import { getAddress as getAddress5, hexlify as hexlify5, toBeHex } from "ethers";
|
|
1161
1284
|
import {
|
|
1162
|
-
FIELD_MODULUS as
|
|
1163
|
-
normalizeCreditPolicy,
|
|
1285
|
+
FIELD_MODULUS as FIELD_MODULUS5,
|
|
1164
1286
|
applyCreditDrawUsage,
|
|
1165
1287
|
computeCreditNullifier,
|
|
1166
1288
|
computeCreditRecoveryPad,
|
|
@@ -1169,7 +1291,8 @@ import {
|
|
|
1169
1291
|
encryptCreditNote,
|
|
1170
1292
|
generateCreditSharedKey,
|
|
1171
1293
|
generateNonce as generateNonce2,
|
|
1172
|
-
normalizeCreditDeliveryState
|
|
1294
|
+
normalizeCreditDeliveryState,
|
|
1295
|
+
normalizeCreditPolicy
|
|
1173
1296
|
} from "@invisi-labs/core";
|
|
1174
1297
|
import {
|
|
1175
1298
|
WitnessBuilder as WitnessBuilder2,
|
|
@@ -1180,30 +1303,6 @@ import {
|
|
|
1180
1303
|
topUpCreditPublicInputs as topUpCreditPublicInputs2,
|
|
1181
1304
|
word
|
|
1182
1305
|
} from "@invisi-labs/protocol";
|
|
1183
|
-
|
|
1184
|
-
// src/session/credit-values.ts
|
|
1185
|
-
import {
|
|
1186
|
-
computeCreditCommitment,
|
|
1187
|
-
computeCreditSharedKeyHash
|
|
1188
|
-
} from "@invisi-labs/core";
|
|
1189
|
-
async function creditLeaf(state) {
|
|
1190
|
-
const n = state.note;
|
|
1191
|
-
return computeCreditCommitment(
|
|
1192
|
-
n.chainId,
|
|
1193
|
-
n.spenderWalletIdentity,
|
|
1194
|
-
n.ownerWalletIdentity,
|
|
1195
|
-
n.spenderPayout,
|
|
1196
|
-
n.ownerPayout,
|
|
1197
|
-
n.tokenAddress,
|
|
1198
|
-
n.amount,
|
|
1199
|
-
n.nonce,
|
|
1200
|
-
n.expiry,
|
|
1201
|
-
await computeCreditSharedKeyHash(n.creditSharedKey),
|
|
1202
|
-
n
|
|
1203
|
-
);
|
|
1204
|
-
}
|
|
1205
|
-
|
|
1206
|
-
// src/session/prepare-credit.ts
|
|
1207
1306
|
var EMPTY = { ciphertext: "0x", clue: "0x" };
|
|
1208
1307
|
var CreditPreparation = class {
|
|
1209
1308
|
constructor(runtime) {
|
|
@@ -1229,10 +1328,10 @@ var CreditPreparation = class {
|
|
|
1229
1328
|
["owner", "spender"].map(async (role) => {
|
|
1230
1329
|
const keys = role === "owner" ? state.ownerReceivingKeys : state.spenderReceivingKeys;
|
|
1231
1330
|
return {
|
|
1232
|
-
ciphertext:
|
|
1331
|
+
ciphertext: hexlify5(
|
|
1233
1332
|
await encryptCreditNote(keys.viewingPublicKey, state, { ...context, role })
|
|
1234
1333
|
),
|
|
1235
|
-
clue:
|
|
1334
|
+
clue: hexlify5(createClue2(keys.clueKey))
|
|
1236
1335
|
};
|
|
1237
1336
|
})
|
|
1238
1337
|
);
|
|
@@ -1297,8 +1396,8 @@ var CreditPreparation = class {
|
|
|
1297
1396
|
recipientLookup(this.runtime.options),
|
|
1298
1397
|
{ field: "wallet" }
|
|
1299
1398
|
);
|
|
1300
|
-
const ownerPayout =
|
|
1301
|
-
const spenderPayout =
|
|
1399
|
+
const ownerPayout = getAddress5(owner.ownerAddress);
|
|
1400
|
+
const spenderPayout = getAddress5(spender.ownerAddress);
|
|
1302
1401
|
if (BigInt(ownerPayout) === 0n || BigInt(spenderPayout) === 0n) {
|
|
1303
1402
|
throw new Error("Credit payouts must be nonzero");
|
|
1304
1403
|
}
|
|
@@ -1358,7 +1457,7 @@ var CreditPreparation = class {
|
|
|
1358
1457
|
nullifiers: selected.nullifiers,
|
|
1359
1458
|
creditCommitment: output.commitment,
|
|
1360
1459
|
changeCommitment: plan.outputs[0]?.commitment ?? word(0n),
|
|
1361
|
-
owner:
|
|
1460
|
+
owner: getAddress5(toBeHex(output.state.note.ownerPayout, 20)),
|
|
1362
1461
|
expiry: output.state.note.expiry,
|
|
1363
1462
|
deadline: frame.deadline
|
|
1364
1463
|
};
|
|
@@ -1498,7 +1597,7 @@ var CreditPreparation = class {
|
|
|
1498
1597
|
outputs.push(output);
|
|
1499
1598
|
remainders.push(output.commitment);
|
|
1500
1599
|
recoveryAmounts.push(
|
|
1501
|
-
word((remaining + await computeCreditRecoveryPad(...args)) %
|
|
1600
|
+
word((remaining + await computeCreditRecoveryPad(...args)) % FIELD_MODULUS5)
|
|
1502
1601
|
);
|
|
1503
1602
|
deliveries.push(...output.deliveries);
|
|
1504
1603
|
}
|
|
@@ -1510,7 +1609,7 @@ var CreditPreparation = class {
|
|
|
1510
1609
|
remainderCommitments: remainders,
|
|
1511
1610
|
expiries: members.map((member) => member.input.state.note.expiry),
|
|
1512
1611
|
recoveryAmounts,
|
|
1513
|
-
spender:
|
|
1612
|
+
spender: getAddress5(toBeHex(payout, 20)),
|
|
1514
1613
|
totalAmount: amounts.reduce((sum, amount) => sum + amount, 0n),
|
|
1515
1614
|
deadline: frame.deadline
|
|
1516
1615
|
};
|
|
@@ -1542,7 +1641,7 @@ var CreditPreparation = class {
|
|
|
1542
1641
|
token: frame.context.tokenAddress,
|
|
1543
1642
|
source: previous.source,
|
|
1544
1643
|
nullifier: previous.nullifier,
|
|
1545
|
-
owner:
|
|
1644
|
+
owner: getAddress5(toBeHex(n.ownerPayout, 20)),
|
|
1546
1645
|
amount: n.amount,
|
|
1547
1646
|
deadline: frame.deadline
|
|
1548
1647
|
};
|
|
@@ -1557,7 +1656,7 @@ var CreditPreparation = class {
|
|
|
1557
1656
|
}
|
|
1558
1657
|
};
|
|
1559
1658
|
|
|
1560
|
-
// src/
|
|
1659
|
+
// src/transactions/preparation/transaction-preparation.ts
|
|
1561
1660
|
var TransactionPreparation = class {
|
|
1562
1661
|
constructor(context) {
|
|
1563
1662
|
this.context = context;
|
|
@@ -1578,7 +1677,7 @@ var TransactionPreparation = class {
|
|
|
1578
1677
|
}
|
|
1579
1678
|
#intent;
|
|
1580
1679
|
runtime(tokenAddress) {
|
|
1581
|
-
if (
|
|
1680
|
+
if (getAddress6(tokenAddress) !== getAddress6(this.context.options.protocol.context.tokenAddress))
|
|
1582
1681
|
throw new Error("preparation token mismatch");
|
|
1583
1682
|
return this.context;
|
|
1584
1683
|
}
|
|
@@ -1605,7 +1704,7 @@ var TransactionPreparation = class {
|
|
|
1605
1704
|
let withdrawalAddress = ZERO_ADDRESS;
|
|
1606
1705
|
let withdrawalAmount = 0n;
|
|
1607
1706
|
if (copied.withdrawal !== void 0) {
|
|
1608
|
-
withdrawalAddress =
|
|
1707
|
+
withdrawalAddress = getAddress6(copied.withdrawal.to);
|
|
1609
1708
|
positiveAmount(copied.withdrawal.amount);
|
|
1610
1709
|
if (BigInt(withdrawalAddress) === 0n)
|
|
1611
1710
|
throw new Error("withdrawal destination must be nonzero");
|
|
@@ -1613,7 +1712,7 @@ var TransactionPreparation = class {
|
|
|
1613
1712
|
}
|
|
1614
1713
|
if (copied.outputs.length === 0 && withdrawalAmount === 0n)
|
|
1615
1714
|
throw new Error("transfer requires a private output or public withdrawal");
|
|
1616
|
-
const feeRecipient = copied.fee ?
|
|
1715
|
+
const feeRecipient = copied.fee ? getAddress6(copied.fee.recipient) : ZERO_ADDRESS;
|
|
1617
1716
|
const feeAmount = copied.fee?.amount ?? 0n;
|
|
1618
1717
|
if (copied.fee) {
|
|
1619
1718
|
positiveAmount(feeAmount);
|
|
@@ -1663,7 +1762,7 @@ var TransactionPreparation = class {
|
|
|
1663
1762
|
feeAmount: fee.amount
|
|
1664
1763
|
};
|
|
1665
1764
|
const deliveries = outputs.map((output) => output.delivery);
|
|
1666
|
-
const normalized =
|
|
1765
|
+
const normalized = normalizeTransactRequest(context, params, deliveries, funding.funding);
|
|
1667
1766
|
const statement2 = WitnessBuilder3.statement(
|
|
1668
1767
|
`transact_user_slots${normalized.capacity}`,
|
|
1669
1768
|
transactUserPublicInputs2(context, params, deliveries, funding.funding),
|
|
@@ -1757,25 +1856,22 @@ var TransactionPreparation = class {
|
|
|
1757
1856
|
export {
|
|
1758
1857
|
InvisiError,
|
|
1759
1858
|
prepareWithErrors,
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
walletBalance,
|
|
1764
|
-
walletBalances,
|
|
1859
|
+
encodePaymentNote,
|
|
1860
|
+
decodePaymentNote,
|
|
1861
|
+
exportPaymentNote,
|
|
1765
1862
|
requiresAuthorization,
|
|
1766
|
-
inputFacts,
|
|
1767
|
-
operationSources,
|
|
1768
1863
|
statement,
|
|
1769
1864
|
authorizationRequest,
|
|
1770
|
-
witnessFor,
|
|
1771
|
-
encodeOperation,
|
|
1772
|
-
fundingTypedData,
|
|
1773
|
-
publicProof,
|
|
1774
1865
|
operationExpired,
|
|
1775
1866
|
exportBroadcasterIntent,
|
|
1867
|
+
encodePaymentRequest,
|
|
1868
|
+
decodePaymentRequest,
|
|
1869
|
+
NoteOutput,
|
|
1870
|
+
configureRecipientInfrastructure,
|
|
1871
|
+
recipientLookup,
|
|
1776
1872
|
verifyMembership,
|
|
1777
1873
|
positiveAmount,
|
|
1778
1874
|
creditLeaf,
|
|
1779
1875
|
TransactionPreparation
|
|
1780
1876
|
};
|
|
1781
|
-
//# sourceMappingURL=chunk-
|
|
1877
|
+
//# sourceMappingURL=chunk-ITTGBIG2.js.map
|