@invisi-labs/sdk 1.1.3 → 1.1.4
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/{chunk-FSQYZO66.js → chunk-BE52B32Z.js} +608 -196
- package/dist/chunk-BE52B32Z.js.map +1 -0
- package/dist/ethers.d.cts +2 -2
- package/dist/ethers.d.ts +2 -2
- package/dist/index.cjs +910 -421
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +240 -39
- package/dist/index.d.ts +240 -39
- package/dist/index.js +247 -131
- package/dist/index.js.map +1 -1
- package/dist/indexeddb.cjs +645 -242
- package/dist/indexeddb.cjs.map +1 -1
- package/dist/indexeddb.d.cts +2 -2
- package/dist/indexeddb.d.ts +2 -2
- package/dist/indexeddb.js +1 -1
- package/dist/memory.cjs +645 -242
- package/dist/memory.cjs.map +1 -1
- package/dist/memory.d.cts +3 -3
- package/dist/memory.d.ts +3 -3
- package/dist/memory.js +1 -1
- package/dist/{gas-estimation-BU3or4AH.d.ts → operation-types-D7I405FX.d.ts} +122 -118
- package/dist/{gas-estimation-C1OEgFFJ.d.cts → operation-types-DIazgS2d.d.cts} +122 -118
- package/dist/sqlite.cjs +645 -242
- package/dist/sqlite.cjs.map +1 -1
- package/dist/sqlite.d.cts +2 -2
- package/dist/sqlite.d.ts +2 -2
- package/dist/sqlite.js +1 -1
- package/dist/{state-DHl6j33x.d.cts → state-AUnz7TG7.d.cts} +3 -1
- package/dist/{state-DHl6j33x.d.ts → state-AUnz7TG7.d.ts} +3 -1
- package/dist/{store-Dlol1q1s.d.ts → store-BMN8kd0c.d.ts} +1 -1
- package/dist/{store-1zgwa--h.d.cts → store-Cz75STRl.d.cts} +1 -1
- package/package.json +5 -5
- package/dist/chunk-FSQYZO66.js.map +0 -1
package/dist/memory.cjs
CHANGED
|
@@ -371,8 +371,9 @@ function readProtocolLogs(receipt, protocolAddress) {
|
|
|
371
371
|
}
|
|
372
372
|
|
|
373
373
|
// src/session/discovery.ts
|
|
374
|
-
var import_ethers3 = require("ethers");
|
|
375
374
|
var import_core = require("@invisi-labs/core");
|
|
375
|
+
var import_ethers3 = require("ethers");
|
|
376
|
+
var import_core2 = require("@invisi-labs/core");
|
|
376
377
|
var import_protocol = require("@invisi-labs/protocol");
|
|
377
378
|
var EVENTS = /* @__PURE__ */ new Set([
|
|
378
379
|
"CommitmentsInserted",
|
|
@@ -466,7 +467,7 @@ function extractDeliveryCandidates(logs, context, confirmedThrough) {
|
|
|
466
467
|
check2(inserted, "delivery has no matching canonical insertion");
|
|
467
468
|
if (commitment !== void 0)
|
|
468
469
|
check2(hash(commitment) === inserted, "delivery commitment conflicts with insertion");
|
|
469
|
-
const delivery = (0, import_protocol.
|
|
470
|
+
const delivery = (0, import_protocol.normalizeOpaqueDelivery)(wire);
|
|
470
471
|
check2(
|
|
471
472
|
typeof token === "string" && (0, import_ethers3.isAddress)(token) && BigInt(token) !== 0n,
|
|
472
473
|
"invalid event token"
|
|
@@ -497,7 +498,7 @@ function extractDeliveryCandidates(logs, context, confirmedThrough) {
|
|
|
497
498
|
add(log, tree, index(leaves[0]), fields.creditCommitment, "owner", ds[0]);
|
|
498
499
|
add(log, tree, index(leaves[0]), fields.creditCommitment, "spender", ds[1]);
|
|
499
500
|
if (hasChange) add(log, tree, index(leaves[1]), change, "funds", ds[2]);
|
|
500
|
-
else (0, import_protocol.
|
|
501
|
+
else (0, import_protocol.normalizeOpaqueDelivery)(ds[2], { empty: true });
|
|
501
502
|
};
|
|
502
503
|
for (const log of relevant) {
|
|
503
504
|
const a = object(log.args);
|
|
@@ -527,7 +528,7 @@ function extractDeliveryCandidates(logs, context, confirmedThrough) {
|
|
|
527
528
|
const length = Number.parseInt(packed.slice(offset, offset + 4), 16);
|
|
528
529
|
offset += 4;
|
|
529
530
|
check2(
|
|
530
|
-
length >=
|
|
531
|
+
length >= 1 && length <= import_core.MAX_CIPHERTEXT_BYTES && offset + length * 2 + 136 <= packed.length,
|
|
531
532
|
"invalid packed delivery length"
|
|
532
533
|
);
|
|
533
534
|
const ciphertext = `0x${packed.slice(offset, offset + length * 2)}`;
|
|
@@ -549,8 +550,8 @@ function extractDeliveryCandidates(logs, context, confirmedThrough) {
|
|
|
549
550
|
cs.forEach((c, i) => {
|
|
550
551
|
if (BigInt(hash(c)) === 0n) {
|
|
551
552
|
check2(integer(leaves[i]) === 0n, "absent remainder has nonzero position");
|
|
552
|
-
(0, import_protocol.
|
|
553
|
-
(0, import_protocol.
|
|
553
|
+
(0, import_protocol.normalizeOpaqueDelivery)(ds[2 * i], { empty: true });
|
|
554
|
+
(0, import_protocol.normalizeOpaqueDelivery)(ds[2 * i + 1], { empty: true });
|
|
554
555
|
} else {
|
|
555
556
|
add(log, tree, index(leaves[i]), c, "owner", ds[2 * i]);
|
|
556
557
|
add(log, tree, index(leaves[i]), c, "spender", ds[2 * i + 1]);
|
|
@@ -562,7 +563,7 @@ function extractDeliveryCandidates(logs, context, confirmedThrough) {
|
|
|
562
563
|
}
|
|
563
564
|
async function decryptCandidates(logs, options) {
|
|
564
565
|
contextCheck(options.context);
|
|
565
|
-
(0,
|
|
566
|
+
(0, import_core2.decodeAddress)(import_core2.ReceivingAddress.fromIdentity(options.viewing.walletIdentity).toString());
|
|
566
567
|
check2(
|
|
567
568
|
!logs.some(
|
|
568
569
|
(log) => log.address.toLowerCase() === options.context.protocolAddress.toLowerCase() && log.removed
|
|
@@ -904,7 +905,7 @@ function walletBalances(state) {
|
|
|
904
905
|
}
|
|
905
906
|
|
|
906
907
|
// src/session/store.ts
|
|
907
|
-
var
|
|
908
|
+
var import_core3 = require("@invisi-labs/core");
|
|
908
909
|
var import_sha256 = require("@noble/hashes/sha256");
|
|
909
910
|
var import_utils = require("@noble/hashes/utils");
|
|
910
911
|
function scopeKey(scope) {
|
|
@@ -920,7 +921,7 @@ function scopeKey(scope) {
|
|
|
920
921
|
return (0, import_utils.bytesToHex)(
|
|
921
922
|
(0, import_sha256.sha256)(
|
|
922
923
|
(0, import_utils.utf8ToBytes)(
|
|
923
|
-
"Invisi.WalletScope.
|
|
924
|
+
"Invisi.WalletScope.v1:" + JSON.stringify([
|
|
924
925
|
scope.namespace,
|
|
925
926
|
scope.chainId.toString(),
|
|
926
927
|
scope.protocolAddress.toLowerCase(),
|
|
@@ -957,12 +958,12 @@ function validateState(state, scope, revision) {
|
|
|
957
958
|
throw new Error("invalid spent state");
|
|
958
959
|
}
|
|
959
960
|
for (const record of Object.values(state.credits)) {
|
|
960
|
-
const note = (0,
|
|
961
|
+
const note = (0, import_core3.normalizeCreditDeliveryState)(record.state).note;
|
|
961
962
|
if (note.chainId !== scope.chainId || note.protocolAddress !== BigInt(scope.protocolAddress) || !["owner", "spender"].includes(record.role) || (record.role === "owner" ? note.ownerWalletIdentity : note.spenderWalletIdentity).toLowerCase() !== scope.walletIdentity.toLowerCase() || !["unspent", "unknown", "spent"].includes(record.spent))
|
|
962
963
|
throw new Error("invalid stored Credit context or role");
|
|
963
964
|
}
|
|
964
965
|
for (const record of [...Object.values(state.notes), ...Object.values(state.credits)]) {
|
|
965
|
-
if (!/^0x[0-9a-fA-F]{64}$/.test(record.commitment) || BigInt(record.commitment) <= 0n || BigInt(record.commitment) >=
|
|
966
|
+
if (!/^0x[0-9a-fA-F]{64}$/.test(record.commitment) || BigInt(record.commitment) <= 0n || BigInt(record.commitment) >= import_core3.FIELD_MODULUS || typeof record.treeId !== "bigint" || record.treeId < 1n || record.treeId >= 1n << 64n || !Number.isInteger(record.leafIndex) || record.leafIndex < 0 || record.leafIndex >= 65536 || typeof record.blockNumber !== "bigint" || record.blockNumber < 0n || !/^0x[0-9a-fA-F]{64}$/.test(record.blockHash))
|
|
966
967
|
throw new Error("invalid stored receipt position");
|
|
967
968
|
}
|
|
968
969
|
for (const [id, operation] of Object.entries(state.reservations))
|
|
@@ -1035,14 +1036,11 @@ var MemoryAtomicRecords = class {
|
|
|
1035
1036
|
};
|
|
1036
1037
|
|
|
1037
1038
|
// src/session/operation-protocol.ts
|
|
1038
|
-
var
|
|
1039
|
+
var import_core4 = require("@invisi-labs/core");
|
|
1039
1040
|
var import_protocol2 = require("@invisi-labs/protocol");
|
|
1040
1041
|
function requiresAuthorization(plan) {
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
throw new Error("invalid owner-only sweep plan");
|
|
1044
|
-
return false;
|
|
1045
|
-
}
|
|
1042
|
+
const ownerOnlyFullWithdrawal = !plan.credit && plan.inputs.length > 0 && plan.outputs.length === 0 && plan.params !== void 0 && plan.params.outputCommitments.length === 0 && (plan.funding.amount ?? 0n) === 0n && plan.fundingMode === "none" && BigInt(plan.params.withdrawalAddress) === BigInt(plan.scope.walletIdentity) && plan.params.withdrawalAmount === plan.inputs.reduce((sum, input) => sum + input.note.amount, 0n) && plan.inputs.every((input) => input.note.walletIdentity === plan.scope.walletIdentity);
|
|
1043
|
+
if (ownerOnlyFullWithdrawal) return false;
|
|
1046
1044
|
return !!plan.credit || plan.inputs.length > 0;
|
|
1047
1045
|
}
|
|
1048
1046
|
function inputFacts(plan) {
|
|
@@ -1306,7 +1304,7 @@ function operationExpired(plan, now) {
|
|
|
1306
1304
|
if (credit?.creation && now >= credit.creation.expiry) return true;
|
|
1307
1305
|
if (credit?.draw) {
|
|
1308
1306
|
if (credit.draw.expiries.some((expiry) => now >= expiry)) return true;
|
|
1309
|
-
const prepared = (0,
|
|
1307
|
+
const prepared = (0, import_core4.creditPeriods)(plan.preparedAt.timestamp), current2 = (0, import_core4.creditPeriods)(now);
|
|
1310
1308
|
if (prepared.currentDay !== current2.currentDay || prepared.currentMonth !== current2.currentMonth)
|
|
1311
1309
|
return true;
|
|
1312
1310
|
}
|
|
@@ -2805,7 +2803,8 @@ function estimateGasReference(plan, protocol, profiles) {
|
|
|
2805
2803
|
|
|
2806
2804
|
// src/session/operation-execution.ts
|
|
2807
2805
|
var import_ethers6 = require("ethers");
|
|
2808
|
-
var
|
|
2806
|
+
var import_core5 = require("@invisi-labs/core");
|
|
2807
|
+
var import_protocol3 = require("@invisi-labs/protocol");
|
|
2809
2808
|
|
|
2810
2809
|
// src/session/proof-integrity.ts
|
|
2811
2810
|
var import_ethers5 = require("ethers");
|
|
@@ -2834,23 +2833,41 @@ async function authorizeOperation(plan, provider) {
|
|
|
2834
2833
|
if (provider.walletIdentity !== plan.scope.walletIdentity)
|
|
2835
2834
|
throw new Error("wallet authorization capability mismatch");
|
|
2836
2835
|
const request = authorizationRequest(plan);
|
|
2837
|
-
const digest = (0,
|
|
2836
|
+
const digest = (0, import_core5.computeWalletAuthorizationDigest)(request);
|
|
2838
2837
|
const signed = await provider.authorize(structuredClone(request));
|
|
2839
2838
|
if (signed.walletIdentity !== plan.scope.walletIdentity || signed.digest !== digest)
|
|
2840
2839
|
throw new Error("wallet authorization statement mismatch");
|
|
2841
2840
|
return signed;
|
|
2842
2841
|
}
|
|
2843
2842
|
async function proveOperation(protocol, plan, authorization, prover, options) {
|
|
2843
|
+
if (Object.hasOwn(options, "target")) {
|
|
2844
|
+
throw new Error("prove accepts execution, not a backend target");
|
|
2845
|
+
}
|
|
2846
|
+
const { execution = "single", ...proverOptions } = options;
|
|
2847
|
+
if (execution !== "single" && execution !== "aggregate") {
|
|
2848
|
+
throw new Error("execution must be single or aggregate");
|
|
2849
|
+
}
|
|
2850
|
+
const frozen = statement(plan);
|
|
2851
|
+
if (execution === "aggregate" && (plan.kind !== "transfer" || plan.capacity !== 14)) {
|
|
2852
|
+
throw new Error("aggregate execution requires a 14-slot transfer");
|
|
2853
|
+
}
|
|
2854
|
+
if (plan.kind === "transfer") {
|
|
2855
|
+
plan.statement = import_protocol3.WitnessBuilder.statement(
|
|
2856
|
+
frozen.circuitName,
|
|
2857
|
+
frozen.expectedPublicInputs,
|
|
2858
|
+
execution === "aggregate" ? "noir-recursive" : "evm"
|
|
2859
|
+
);
|
|
2860
|
+
}
|
|
2844
2861
|
const request = await witnessFor(protocol, plan, authorization);
|
|
2845
2862
|
if (JSON.stringify({ ...request, witness: void 0 }) !== JSON.stringify(plan.statement))
|
|
2846
2863
|
throw new Error("prepared proof statement changed");
|
|
2847
2864
|
try {
|
|
2848
|
-
const proof = publicProof(await prover.prove(request,
|
|
2865
|
+
const proof = publicProof(await prover.prove(request, proverOptions));
|
|
2849
2866
|
sameStatement(proof, plan.statement);
|
|
2850
2867
|
if (await prover.verify(
|
|
2851
2868
|
structuredClone(proof),
|
|
2852
2869
|
structuredClone(plan.statement),
|
|
2853
|
-
|
|
2870
|
+
proverOptions
|
|
2854
2871
|
) !== true)
|
|
2855
2872
|
throw new Error("proof verification failed");
|
|
2856
2873
|
return proof;
|
|
@@ -2865,19 +2882,88 @@ async function signOperationFunding(saved, signer, verifier) {
|
|
|
2865
2882
|
throw new Error("funding signer is not the payer");
|
|
2866
2883
|
const typed = await fundingTypedData(saved);
|
|
2867
2884
|
const signature = await signer.signTypedData(structuredClone(typed));
|
|
2885
|
+
return verifyFundingSignature(typed, payer, signature, verifier);
|
|
2886
|
+
}
|
|
2887
|
+
function walletSigningRequest(plan) {
|
|
2888
|
+
return (0, import_core5.createSigningRequest)((0, import_core5.walletAuthorizationTypedData)(authorizationRequest(plan)));
|
|
2889
|
+
}
|
|
2890
|
+
async function acceptWalletSignature(plan, signature) {
|
|
2891
|
+
const request = authorizationRequest(plan);
|
|
2892
|
+
const digest = (0, import_core5.computeWalletAuthorizationDigest)(request);
|
|
2893
|
+
try {
|
|
2894
|
+
const signed = import_ethers6.Signature.from(signature);
|
|
2895
|
+
const publicKey = (0, import_ethers6.getBytes)(import_ethers6.SigningKey.recoverPublicKey(digest, signed));
|
|
2896
|
+
const owner = await (0, import_core5.computeWalletIdentity)(publicKey);
|
|
2897
|
+
if (owner.toLowerCase() !== plan.scope.walletIdentity.toLowerCase()) {
|
|
2898
|
+
throw new Error("wallet signature identity mismatch");
|
|
2899
|
+
}
|
|
2900
|
+
return new import_core5.AuthorizedStatement(plan.scope.walletIdentity, digest, {
|
|
2901
|
+
public_key_x: Array.from(publicKey.slice(1, 33)),
|
|
2902
|
+
public_key_y: Array.from(publicKey.slice(33)),
|
|
2903
|
+
signature: Array.from((0, import_ethers6.getBytes)((0, import_ethers6.concat)([signed.r, signed.s])))
|
|
2904
|
+
});
|
|
2905
|
+
} catch {
|
|
2906
|
+
throw new Error("invalid wallet authorization signature");
|
|
2907
|
+
}
|
|
2908
|
+
}
|
|
2909
|
+
async function fundingSigningRequest(saved, runtime) {
|
|
2910
|
+
const plan = saved.plan;
|
|
2911
|
+
if (plan.fundingMode !== "signature") {
|
|
2912
|
+
throw new Error("operation does not require a funding signature");
|
|
2913
|
+
}
|
|
2914
|
+
if (plan.kind !== "shield" && !saved.proof) {
|
|
2915
|
+
throw new Error("proof must precede funding authorization");
|
|
2916
|
+
}
|
|
2917
|
+
await runtime.options.protocol.validateDeployment(runtime.reader);
|
|
2918
|
+
const block = await runtime.reader.getBlock(await runtime.reader.getLatestBlockNumber());
|
|
2919
|
+
if (block.timestamp > plan.deadline || block.timestamp > (plan.funding.deadline ?? 0n)) {
|
|
2920
|
+
throw new Error("funding authorization expired");
|
|
2921
|
+
}
|
|
2922
|
+
if (await runtime.reader.getFundingNonce(plan.context, plan.funding.payer, block) !== plan.funding.sigNonce) {
|
|
2923
|
+
throw new Error("funding nonce changed; prepare again");
|
|
2924
|
+
}
|
|
2925
|
+
const request = (0, import_core5.createSigningRequest)(await fundingTypedData(saved));
|
|
2926
|
+
if ((await runtime.reader.getBlock(block.number)).hash !== block.hash) {
|
|
2927
|
+
throw new Error("canonical funding block changed");
|
|
2928
|
+
}
|
|
2929
|
+
return request;
|
|
2930
|
+
}
|
|
2931
|
+
async function acceptFundingSignature(saved, signature, runtime) {
|
|
2932
|
+
if (!/^0x(?:[a-fA-F0-9]{2})+$/.test(signature)) {
|
|
2933
|
+
throw new Error("invalid EVM funding signature");
|
|
2934
|
+
}
|
|
2935
|
+
const request = await fundingSigningRequest(saved, runtime);
|
|
2936
|
+
return verifyFundingSignature(
|
|
2937
|
+
request,
|
|
2938
|
+
saved.plan.funding.payer,
|
|
2939
|
+
signature,
|
|
2940
|
+
runtime.options.fundingVerifier
|
|
2941
|
+
);
|
|
2942
|
+
}
|
|
2943
|
+
async function verifyFundingSignature(request, payer, signature, verifier) {
|
|
2944
|
+
if (!/^0x(?:[a-fA-F0-9]{2})+$/.test(signature)) {
|
|
2945
|
+
throw new Error("invalid EVM funding signature");
|
|
2946
|
+
}
|
|
2947
|
+
const types = Object.fromEntries(
|
|
2948
|
+
Object.entries(request.types).map(([name, fields]) => [name, [...fields]])
|
|
2949
|
+
);
|
|
2950
|
+
let validEOA = false;
|
|
2951
|
+
try {
|
|
2952
|
+
validEOA = (0, import_ethers6.verifyTypedData)(request.domain, types, request.message, signature).toLowerCase() === payer.toLowerCase();
|
|
2953
|
+
if (validEOA) {
|
|
2954
|
+
signature = import_ethers6.Signature.from(signature).serialized;
|
|
2955
|
+
}
|
|
2956
|
+
} catch {
|
|
2957
|
+
validEOA = false;
|
|
2958
|
+
}
|
|
2868
2959
|
const valid = verifier ? await verifier.verifyTypedData({
|
|
2869
|
-
...structuredClone(
|
|
2960
|
+
...structuredClone(request),
|
|
2870
2961
|
address: payer,
|
|
2871
2962
|
signature
|
|
2872
|
-
}) :
|
|
2873
|
-
|
|
2874
|
-
|
|
2875
|
-
|
|
2876
|
-
),
|
|
2877
|
-
typed.message,
|
|
2878
|
-
signature
|
|
2879
|
-
).toLowerCase() === payer.toLowerCase();
|
|
2880
|
-
if (valid !== true) throw new Error("invalid EVM funding signature");
|
|
2963
|
+
}) : validEOA;
|
|
2964
|
+
if (valid !== true) {
|
|
2965
|
+
throw new Error("invalid EVM funding signature");
|
|
2966
|
+
}
|
|
2881
2967
|
return signature;
|
|
2882
2968
|
}
|
|
2883
2969
|
|
|
@@ -2918,16 +3004,16 @@ function operationPreview(plan, status) {
|
|
|
2918
3004
|
}
|
|
2919
3005
|
|
|
2920
3006
|
// src/session/Operation.ts
|
|
2921
|
-
var
|
|
2922
|
-
var
|
|
3007
|
+
var import_core9 = require("@invisi-labs/core");
|
|
3008
|
+
var import_protocol5 = require("@invisi-labs/protocol");
|
|
2923
3009
|
|
|
2924
3010
|
// src/session/operation-confirmation.ts
|
|
2925
|
-
var
|
|
3011
|
+
var import_core8 = require("@invisi-labs/core");
|
|
2926
3012
|
|
|
2927
3013
|
// src/session/aggregate-receipt.ts
|
|
2928
3014
|
var import_ethers7 = require("ethers");
|
|
2929
3015
|
var import_contracts3 = require("@invisi-labs/protocol/contracts");
|
|
2930
|
-
var
|
|
3016
|
+
var import_protocol4 = require("@invisi-labs/protocol");
|
|
2931
3017
|
var aggregateAbi = import_contracts3.invisiProtocolAbi.filter(
|
|
2932
3018
|
(item) => item.type === "function" && item.name === "transact" && item.inputs.length === 2 && item.inputs[1]?.type === "tuple[]"
|
|
2933
3019
|
);
|
|
@@ -2955,7 +3041,7 @@ function matchesAggregateTransaction(saved, transaction) {
|
|
|
2955
3041
|
});
|
|
2956
3042
|
if (decoded.name !== "transact" || !Array.isArray(requests) || ![4, 8, 12].includes(requests.length))
|
|
2957
3043
|
return false;
|
|
2958
|
-
const canonical2 = (request) => (0,
|
|
3044
|
+
const canonical2 = (request) => (0, import_protocol4.buildTransactCalldata)({
|
|
2959
3045
|
context: { ...plan.context, tokenAddress: request.params.token },
|
|
2960
3046
|
proof: "0x00",
|
|
2961
3047
|
params: request.params,
|
|
@@ -2963,7 +3049,7 @@ function matchesAggregateTransaction(saved, transaction) {
|
|
|
2963
3049
|
funding: request.funding,
|
|
2964
3050
|
caller: transaction.from
|
|
2965
3051
|
}).data.toLowerCase();
|
|
2966
|
-
const expected = (0,
|
|
3052
|
+
const expected = (0, import_protocol4.buildTransactCalldata)({
|
|
2967
3053
|
context: plan.context,
|
|
2968
3054
|
proof: "0x00",
|
|
2969
3055
|
params: plan.params,
|
|
@@ -2978,10 +3064,10 @@ function matchesAggregateTransaction(saved, transaction) {
|
|
|
2978
3064
|
}
|
|
2979
3065
|
|
|
2980
3066
|
// src/session/credit-values.ts
|
|
2981
|
-
var
|
|
3067
|
+
var import_core6 = require("@invisi-labs/core");
|
|
2982
3068
|
async function creditLeaf(state) {
|
|
2983
3069
|
const n = state.note;
|
|
2984
|
-
return (0,
|
|
3070
|
+
return (0, import_core6.computeCreditCommitment)(
|
|
2985
3071
|
n.chainId,
|
|
2986
3072
|
n.spenderWalletIdentity,
|
|
2987
3073
|
n.ownerWalletIdentity,
|
|
@@ -2991,23 +3077,23 @@ async function creditLeaf(state) {
|
|
|
2991
3077
|
n.amount,
|
|
2992
3078
|
n.nonce,
|
|
2993
3079
|
n.expiry,
|
|
2994
|
-
await (0,
|
|
3080
|
+
await (0, import_core6.computeCreditSharedKeyHash)(n.creditSharedKey),
|
|
2995
3081
|
n
|
|
2996
3082
|
);
|
|
2997
3083
|
}
|
|
2998
3084
|
|
|
2999
3085
|
// src/session/membership.ts
|
|
3000
|
-
var
|
|
3086
|
+
var import_core7 = require("@invisi-labs/core");
|
|
3001
3087
|
async function verifyMembership(reader, context, block, input, proof) {
|
|
3002
3088
|
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))
|
|
3003
3089
|
throw new Error("membership context/root mismatch");
|
|
3004
3090
|
let root = BigInt(input.commitment);
|
|
3005
3091
|
for (let i = 0; i < 16; i++) {
|
|
3006
3092
|
const sibling = proof.path[i];
|
|
3007
|
-
if (typeof sibling !== "bigint" || sibling < 0n || sibling >=
|
|
3093
|
+
if (typeof sibling !== "bigint" || sibling < 0n || sibling >= import_core7.FIELD_MODULUS)
|
|
3008
3094
|
throw new Error("noncanonical membership path");
|
|
3009
3095
|
root = BigInt(
|
|
3010
|
-
await (0,
|
|
3096
|
+
await (0, import_core7.poseidonHash)(input.leafIndex >> i & 1 ? [sibling, root] : [root, sibling])
|
|
3011
3097
|
);
|
|
3012
3098
|
}
|
|
3013
3099
|
if (root !== BigInt(proof.root))
|
|
@@ -3179,7 +3265,7 @@ async function confirmOperation(runtime, id, generation, hash2, signal) {
|
|
|
3179
3265
|
if (credits.length) {
|
|
3180
3266
|
const nullifiers = await Promise.all(
|
|
3181
3267
|
credits.map(
|
|
3182
|
-
async (record) => (0,
|
|
3268
|
+
async (record) => (0, import_core8.computeCreditNullifier)(
|
|
3183
3269
|
plan.context.chainId,
|
|
3184
3270
|
record.state.note.creditSharedKey,
|
|
3185
3271
|
await creditLeaf(record.state)
|
|
@@ -3480,6 +3566,81 @@ var Operation = class _Operation {
|
|
|
3480
3566
|
throw error;
|
|
3481
3567
|
}
|
|
3482
3568
|
}
|
|
3569
|
+
async signingCurrent() {
|
|
3570
|
+
await this.#runtime.store.transaction(this.#runtime.scope, (state) => {
|
|
3571
|
+
check6(!state.invalidated, "wallet state requires canonical resync");
|
|
3572
|
+
this.current(state.operations[this.id]);
|
|
3573
|
+
});
|
|
3574
|
+
}
|
|
3575
|
+
/** Public EIP-712 request; signing transport and key custody belong to the caller. */
|
|
3576
|
+
async exportWalletAuthorization(options = {}) {
|
|
3577
|
+
options.signal?.throwIfAborted();
|
|
3578
|
+
await this.signingCurrent();
|
|
3579
|
+
check6(
|
|
3580
|
+
requiresAuthorization(this.#saved.plan),
|
|
3581
|
+
"operation does not require wallet authorization"
|
|
3582
|
+
);
|
|
3583
|
+
check6(
|
|
3584
|
+
["prepared", "authorized"].includes(this.status),
|
|
3585
|
+
"operation is not awaiting wallet authorization"
|
|
3586
|
+
);
|
|
3587
|
+
return walletSigningRequest(this.#saved.plan);
|
|
3588
|
+
}
|
|
3589
|
+
/** Attach an external EOA signature, retaining its witness only at the private proving boundary. */
|
|
3590
|
+
async withWalletSignature(signature, options = {}) {
|
|
3591
|
+
options.signal?.throwIfAborted();
|
|
3592
|
+
await this.signingCurrent();
|
|
3593
|
+
check6(
|
|
3594
|
+
requiresAuthorization(this.#saved.plan),
|
|
3595
|
+
"operation does not require wallet authorization"
|
|
3596
|
+
);
|
|
3597
|
+
check6(
|
|
3598
|
+
["prepared", "authorized"].includes(this.status),
|
|
3599
|
+
"operation is not awaiting wallet authorization"
|
|
3600
|
+
);
|
|
3601
|
+
const authorization = await acceptWalletSignature(this.#saved.plan, signature);
|
|
3602
|
+
return this.stage(
|
|
3603
|
+
"wallet authorization",
|
|
3604
|
+
["prepared", "authorized"],
|
|
3605
|
+
async () => ({ status: "authorized", authorization }),
|
|
3606
|
+
options.signal
|
|
3607
|
+
);
|
|
3608
|
+
}
|
|
3609
|
+
/** Export only after proving when the funding authorization binds an actual proof hash. */
|
|
3610
|
+
async exportFundingAuthorization(options = {}) {
|
|
3611
|
+
options.signal?.throwIfAborted();
|
|
3612
|
+
await this.signingCurrent();
|
|
3613
|
+
check6(
|
|
3614
|
+
this.status === (this.kind === "shield" ? "prepared" : "proved"),
|
|
3615
|
+
"operation is not awaiting funding authorization or proof"
|
|
3616
|
+
);
|
|
3617
|
+
const request = await fundingSigningRequest(this.#saved, this.#runtime);
|
|
3618
|
+
options.signal?.throwIfAborted();
|
|
3619
|
+
await this.signingCurrent();
|
|
3620
|
+
return request;
|
|
3621
|
+
}
|
|
3622
|
+
/** Verify an external signature against this handle's own frozen operation. */
|
|
3623
|
+
async withFundingSignature(signature, options = {}) {
|
|
3624
|
+
options.signal?.throwIfAborted();
|
|
3625
|
+
await this.signingCurrent();
|
|
3626
|
+
check6(
|
|
3627
|
+
this.status === (this.kind === "shield" ? "prepared" : "proved"),
|
|
3628
|
+
"operation is not awaiting funding authorization or proof"
|
|
3629
|
+
);
|
|
3630
|
+
const verified = await acceptFundingSignature(this.#saved, signature, this.#runtime);
|
|
3631
|
+
return this.stage(
|
|
3632
|
+
"funding authorization",
|
|
3633
|
+
this.kind === "shield" ? ["prepared"] : ["proved"],
|
|
3634
|
+
async (saved) => {
|
|
3635
|
+
saved.plan.funding.signature = verified;
|
|
3636
|
+
if (saved.plan.statement?.target !== "noir-recursive") {
|
|
3637
|
+
saved.call = await this.encode(saved);
|
|
3638
|
+
}
|
|
3639
|
+
return { status: "ready" };
|
|
3640
|
+
},
|
|
3641
|
+
options.signal
|
|
3642
|
+
);
|
|
3643
|
+
}
|
|
3483
3644
|
async authorize(options = {}) {
|
|
3484
3645
|
check6(
|
|
3485
3646
|
requiresAuthorization(this.#saved.plan),
|
|
@@ -3500,7 +3661,8 @@ var Operation = class _Operation {
|
|
|
3500
3661
|
options.signal
|
|
3501
3662
|
);
|
|
3502
3663
|
}
|
|
3503
|
-
async prove(
|
|
3664
|
+
async prove(suppliedOptions = {}) {
|
|
3665
|
+
const options = { ...suppliedOptions };
|
|
3504
3666
|
const plan = this.#saved.plan;
|
|
3505
3667
|
check6(plan.kind !== "shield", "shield requires no proof");
|
|
3506
3668
|
if (requiresAuthorization(plan)) {
|
|
@@ -3511,7 +3673,7 @@ var Operation = class _Operation {
|
|
|
3511
3673
|
}
|
|
3512
3674
|
const prover = this.#runtime.options.prover;
|
|
3513
3675
|
if (!prover) {
|
|
3514
|
-
throw new
|
|
3676
|
+
throw new import_core9.MissingCapabilityError("prover");
|
|
3515
3677
|
}
|
|
3516
3678
|
return this.stage(
|
|
3517
3679
|
"proof",
|
|
@@ -3545,7 +3707,7 @@ var Operation = class _Operation {
|
|
|
3545
3707
|
check6(p.kind === "shield" || this.#saved.proof, "proof must precede funding authorization");
|
|
3546
3708
|
const signer = this.#runtime.options.fundingSigner;
|
|
3547
3709
|
if (!signer) {
|
|
3548
|
-
throw new
|
|
3710
|
+
throw new import_core9.MissingCapabilityError("EVM funding signer");
|
|
3549
3711
|
}
|
|
3550
3712
|
check6(
|
|
3551
3713
|
signer.address.toLowerCase() === p.funding.payer.toLowerCase(),
|
|
@@ -3647,7 +3809,7 @@ var Operation = class _Operation {
|
|
|
3647
3809
|
request: {
|
|
3648
3810
|
params: structuredClone(plan.params),
|
|
3649
3811
|
deliveries: plan.outputs.map((output) => structuredClone(output.delivery)),
|
|
3650
|
-
funding: (0,
|
|
3812
|
+
funding: (0, import_protocol5.normalizeFunding)(plan.funding)
|
|
3651
3813
|
}
|
|
3652
3814
|
};
|
|
3653
3815
|
});
|
|
@@ -3734,18 +3896,181 @@ var Operation = class _Operation {
|
|
|
3734
3896
|
}
|
|
3735
3897
|
};
|
|
3736
3898
|
|
|
3899
|
+
// src/session/WalletSession.ts
|
|
3900
|
+
var import_core16 = require("@invisi-labs/core");
|
|
3901
|
+
|
|
3902
|
+
// src/session/history.ts
|
|
3903
|
+
function walletHistory(state, options = {}) {
|
|
3904
|
+
const token = options.tokenAddress?.toLowerCase();
|
|
3905
|
+
const outgoing = [];
|
|
3906
|
+
for (const record of Object.values(state.operations)) {
|
|
3907
|
+
const saved = record.payload;
|
|
3908
|
+
if (!saved?.plan || token && saved.plan.context.tokenAddress.toLowerCase() !== token) {
|
|
3909
|
+
continue;
|
|
3910
|
+
}
|
|
3911
|
+
const plan = saved.plan;
|
|
3912
|
+
outgoing.push({
|
|
3913
|
+
...operationPreview(plan, record.status),
|
|
3914
|
+
preparedAt: { ...plan.preparedAt },
|
|
3915
|
+
...record.transactionHash ? { transactionHash: record.transactionHash } : {},
|
|
3916
|
+
outputs: plan.outputs.map((output) => ({
|
|
3917
|
+
to: output.address,
|
|
3918
|
+
amount: output.note.amount,
|
|
3919
|
+
change: output.change,
|
|
3920
|
+
commitment: output.commitment,
|
|
3921
|
+
payload: Uint8Array.from(output.note.payload ?? [])
|
|
3922
|
+
}))
|
|
3923
|
+
});
|
|
3924
|
+
}
|
|
3925
|
+
outgoing.sort(
|
|
3926
|
+
(a, b) => a.preparedAt.number === b.preparedAt.number ? a.id.localeCompare(b.id) : a.preparedAt.number > b.preparedAt.number ? -1 : 1
|
|
3927
|
+
);
|
|
3928
|
+
const incoming = [];
|
|
3929
|
+
for (const record of Object.values(state.notes)) {
|
|
3930
|
+
const tokenAddress = `0x${record.note.tokenAddress.toString(16).padStart(40, "0")}`;
|
|
3931
|
+
if (token && tokenAddress !== token) continue;
|
|
3932
|
+
incoming.push({
|
|
3933
|
+
commitment: record.commitment,
|
|
3934
|
+
treeId: record.treeId,
|
|
3935
|
+
leafIndex: record.leafIndex,
|
|
3936
|
+
blockNumber: record.blockNumber,
|
|
3937
|
+
blockHash: record.blockHash,
|
|
3938
|
+
...record.transactionHash ? { transactionHash: record.transactionHash } : {},
|
|
3939
|
+
tokenAddress,
|
|
3940
|
+
amount: record.note.amount,
|
|
3941
|
+
payload: Uint8Array.from(record.note.payload ?? []),
|
|
3942
|
+
spent: record.spent
|
|
3943
|
+
});
|
|
3944
|
+
}
|
|
3945
|
+
incoming.sort(
|
|
3946
|
+
(a, b) => a.blockNumber === b.blockNumber ? a.commitment.localeCompare(b.commitment) : a.blockNumber > b.blockNumber ? -1 : 1
|
|
3947
|
+
);
|
|
3948
|
+
return structuredClone({
|
|
3949
|
+
revision: state.revision,
|
|
3950
|
+
checkedThrough: state.discoveryThrough ?? null,
|
|
3951
|
+
invalidated: !!state.invalidated,
|
|
3952
|
+
outgoing,
|
|
3953
|
+
incoming
|
|
3954
|
+
});
|
|
3955
|
+
}
|
|
3956
|
+
|
|
3957
|
+
// src/errors.ts
|
|
3958
|
+
var import_core10 = require("@invisi-labs/core");
|
|
3959
|
+
var InvisiError = class extends Error {
|
|
3960
|
+
code;
|
|
3961
|
+
operation;
|
|
3962
|
+
address;
|
|
3963
|
+
outputIndex;
|
|
3964
|
+
field;
|
|
3965
|
+
capability;
|
|
3966
|
+
constructor(code, message, details = {}) {
|
|
3967
|
+
super(message, { cause: details.cause });
|
|
3968
|
+
this.name = "InvisiError";
|
|
3969
|
+
this.code = code;
|
|
3970
|
+
this.operation = details.operation;
|
|
3971
|
+
this.address = details.address;
|
|
3972
|
+
this.outputIndex = details.outputIndex;
|
|
3973
|
+
this.field = details.field;
|
|
3974
|
+
this.capability = details.capability;
|
|
3975
|
+
}
|
|
3976
|
+
};
|
|
3977
|
+
async function prepareWithErrors(operation, prepare) {
|
|
3978
|
+
try {
|
|
3979
|
+
return await prepare();
|
|
3980
|
+
} catch (cause) {
|
|
3981
|
+
if (cause instanceof InvisiError) {
|
|
3982
|
+
if (cause.operation) throw cause;
|
|
3983
|
+
throw new InvisiError(cause.code, cause.message, {
|
|
3984
|
+
operation,
|
|
3985
|
+
...cause.address === void 0 ? {} : { address: cause.address },
|
|
3986
|
+
...cause.outputIndex === void 0 ? {} : { outputIndex: cause.outputIndex },
|
|
3987
|
+
...cause.field === void 0 ? {} : { field: cause.field },
|
|
3988
|
+
...cause.capability === void 0 ? {} : { capability: cause.capability },
|
|
3989
|
+
cause: cause.cause
|
|
3990
|
+
});
|
|
3991
|
+
}
|
|
3992
|
+
throw new InvisiError(
|
|
3993
|
+
cause instanceof import_core10.MissingCapabilityError ? "MISSING_CAPABILITY" : "PREPARATION_FAILED",
|
|
3994
|
+
cause instanceof Error ? cause.message : "Transaction preparation failed",
|
|
3995
|
+
{
|
|
3996
|
+
operation,
|
|
3997
|
+
cause,
|
|
3998
|
+
...cause instanceof import_core10.MissingCapabilityError ? { capability: cause.capability } : {}
|
|
3999
|
+
}
|
|
4000
|
+
);
|
|
4001
|
+
}
|
|
4002
|
+
}
|
|
4003
|
+
|
|
3737
4004
|
// src/session/WalletSession.ts
|
|
3738
4005
|
var import_ethers11 = require("ethers");
|
|
3739
|
-
var
|
|
4006
|
+
var import_core17 = require("@invisi-labs/core");
|
|
3740
4007
|
|
|
3741
4008
|
// src/session/transaction-preparation.ts
|
|
3742
4009
|
var import_ethers10 = require("ethers");
|
|
3743
|
-
var
|
|
4010
|
+
var import_protocol8 = require("@invisi-labs/protocol");
|
|
4011
|
+
|
|
4012
|
+
// src/resolve-recipient.ts
|
|
4013
|
+
var import_core11 = require("@invisi-labs/core");
|
|
4014
|
+
async function resolveRecipient(address2, lookup, details = {}) {
|
|
4015
|
+
const context = { ...details, address: address2 };
|
|
4016
|
+
let recipient;
|
|
4017
|
+
try {
|
|
4018
|
+
recipient = import_core11.ReceivingAddress.parse(address2);
|
|
4019
|
+
} catch (cause) {
|
|
4020
|
+
throw new InvisiError("INVALID_RECIPIENT", "Invalid recipient address", {
|
|
4021
|
+
...context,
|
|
4022
|
+
cause
|
|
4023
|
+
});
|
|
4024
|
+
}
|
|
4025
|
+
if (recipient.kind === "long") return recipient;
|
|
4026
|
+
if (!lookup) {
|
|
4027
|
+
throw new InvisiError("MISSING_CAPABILITY", "Missing registry lookup capability", {
|
|
4028
|
+
...context,
|
|
4029
|
+
capability: "registry lookup"
|
|
4030
|
+
});
|
|
4031
|
+
}
|
|
4032
|
+
let keys;
|
|
4033
|
+
try {
|
|
4034
|
+
keys = await lookup(recipient.walletIdentity);
|
|
4035
|
+
} catch (cause) {
|
|
4036
|
+
if (cause instanceof import_core11.MissingCapabilityError) {
|
|
4037
|
+
throw new InvisiError("MISSING_CAPABILITY", cause.message, {
|
|
4038
|
+
...context,
|
|
4039
|
+
cause,
|
|
4040
|
+
capability: cause.capability
|
|
4041
|
+
});
|
|
4042
|
+
}
|
|
4043
|
+
throw new InvisiError("RECIPIENT_LOOKUP_FAILED", "Recipient registry lookup failed", {
|
|
4044
|
+
...context,
|
|
4045
|
+
cause
|
|
4046
|
+
});
|
|
4047
|
+
}
|
|
4048
|
+
if (keys == null) {
|
|
4049
|
+
throw new InvisiError(
|
|
4050
|
+
"EVM_ADDRESS_NOT_REGISTERED",
|
|
4051
|
+
"EVM address has no registered receiving keys",
|
|
4052
|
+
context
|
|
4053
|
+
);
|
|
4054
|
+
}
|
|
4055
|
+
try {
|
|
4056
|
+
return import_core11.ReceivingAddress.from({
|
|
4057
|
+
walletIdentity: recipient.walletIdentity,
|
|
4058
|
+
viewingPublicKey: keys.viewingPublicKey,
|
|
4059
|
+
clueKey: keys.clueKey
|
|
4060
|
+
});
|
|
4061
|
+
} catch (cause) {
|
|
4062
|
+
throw new InvisiError(
|
|
4063
|
+
"RECIPIENT_LOOKUP_FAILED",
|
|
4064
|
+
"Registry returned invalid receiving keys",
|
|
4065
|
+
{ ...context, cause }
|
|
4066
|
+
);
|
|
4067
|
+
}
|
|
4068
|
+
}
|
|
3744
4069
|
|
|
3745
4070
|
// src/session/prepare-common.ts
|
|
3746
4071
|
var import_ethers8 = require("ethers");
|
|
3747
|
-
var
|
|
3748
|
-
var
|
|
4072
|
+
var import_core12 = require("@invisi-labs/core");
|
|
4073
|
+
var import_protocol6 = require("@invisi-labs/protocol");
|
|
3749
4074
|
|
|
3750
4075
|
// src/session/selection.ts
|
|
3751
4076
|
function selectNotes(notes, target, reserved) {
|
|
@@ -3778,6 +4103,27 @@ function positiveAmount(value, bits = 128) {
|
|
|
3778
4103
|
}
|
|
3779
4104
|
return value;
|
|
3780
4105
|
}
|
|
4106
|
+
function copyApplicationPayload(value) {
|
|
4107
|
+
if (value === void 0) return new Uint8Array();
|
|
4108
|
+
if (!(value instanceof Uint8Array)) throw new TypeError("payload must be Uint8Array");
|
|
4109
|
+
return Uint8Array.from(value);
|
|
4110
|
+
}
|
|
4111
|
+
function positiveOutputAmount(payment, outputIndex) {
|
|
4112
|
+
try {
|
|
4113
|
+
return positiveAmount(payment.amount);
|
|
4114
|
+
} catch (cause) {
|
|
4115
|
+
throw new InvisiError(
|
|
4116
|
+
"PREPARATION_FAILED",
|
|
4117
|
+
cause instanceof Error ? cause.message : "Invalid output amount",
|
|
4118
|
+
{
|
|
4119
|
+
address: payment.to,
|
|
4120
|
+
field: "amount",
|
|
4121
|
+
...outputIndex === void 0 ? {} : { outputIndex },
|
|
4122
|
+
cause
|
|
4123
|
+
}
|
|
4124
|
+
);
|
|
4125
|
+
}
|
|
4126
|
+
}
|
|
3781
4127
|
var Preparation = class {
|
|
3782
4128
|
constructor(runtime) {
|
|
3783
4129
|
this.runtime = runtime;
|
|
@@ -3831,7 +4177,7 @@ var Preparation = class {
|
|
|
3831
4177
|
if (mode === "caller" && (source.sigNonce || source.deadline)) {
|
|
3832
4178
|
throw new Error("caller funding uses zero nonce/deadline");
|
|
3833
4179
|
}
|
|
3834
|
-
const funding = (0,
|
|
4180
|
+
const funding = (0, import_protocol6.normalizeFunding)({
|
|
3835
4181
|
payer,
|
|
3836
4182
|
amount: source.amount,
|
|
3837
4183
|
sigNonce: mode === "caller" ? 0n : source.sigNonce ?? await this.runtime.reader.getFundingNonce(
|
|
@@ -3847,52 +4193,70 @@ var Preparation = class {
|
|
|
3847
4193
|
}
|
|
3848
4194
|
return { funding, fundingMode: mode };
|
|
3849
4195
|
}
|
|
3850
|
-
async output(payment, context, change = false) {
|
|
3851
|
-
|
|
3852
|
-
|
|
3853
|
-
|
|
3854
|
-
|
|
3855
|
-
|
|
3856
|
-
|
|
3857
|
-
|
|
3858
|
-
|
|
3859
|
-
|
|
3860
|
-
|
|
3861
|
-
|
|
3862
|
-
|
|
3863
|
-
|
|
3864
|
-
|
|
3865
|
-
|
|
3866
|
-
|
|
3867
|
-
|
|
3868
|
-
|
|
3869
|
-
|
|
3870
|
-
|
|
3871
|
-
|
|
3872
|
-
|
|
3873
|
-
|
|
3874
|
-
|
|
3875
|
-
|
|
3876
|
-
|
|
3877
|
-
|
|
3878
|
-
|
|
3879
|
-
|
|
3880
|
-
|
|
3881
|
-
|
|
3882
|
-
|
|
3883
|
-
|
|
3884
|
-
|
|
3885
|
-
|
|
3886
|
-
|
|
3887
|
-
|
|
3888
|
-
|
|
3889
|
-
|
|
3890
|
-
|
|
4196
|
+
async output(payment, context, change = false, outputIndex) {
|
|
4197
|
+
try {
|
|
4198
|
+
positiveOutputAmount(payment, outputIndex);
|
|
4199
|
+
const recipient = await resolveRecipient(
|
|
4200
|
+
payment.to,
|
|
4201
|
+
this.runtime.options.addressResolver,
|
|
4202
|
+
{
|
|
4203
|
+
field: change ? "changeAddress" : "to",
|
|
4204
|
+
...outputIndex === void 0 ? {} : { outputIndex }
|
|
4205
|
+
}
|
|
4206
|
+
);
|
|
4207
|
+
const nonce = (0, import_core12.generateNonce)();
|
|
4208
|
+
const identity = await (0, import_core12.computeNoteIdentity)(BigInt(recipient.walletIdentity), nonce);
|
|
4209
|
+
const keys = recipient.getReceivingKeys();
|
|
4210
|
+
const note = {
|
|
4211
|
+
chainId: context.chainId,
|
|
4212
|
+
protocolAddress: BigInt(context.protocolAddress),
|
|
4213
|
+
tokenAddress: BigInt(context.tokenAddress),
|
|
4214
|
+
walletIdentity: recipient.walletIdentity,
|
|
4215
|
+
amount: payment.amount,
|
|
4216
|
+
payload: copyApplicationPayload(payment.payload),
|
|
4217
|
+
nonce
|
|
4218
|
+
};
|
|
4219
|
+
const commitment = await (0, import_core12.computeNoteCommitment)(
|
|
4220
|
+
context.chainId,
|
|
4221
|
+
identity,
|
|
4222
|
+
note.tokenAddress,
|
|
4223
|
+
note.amount
|
|
4224
|
+
);
|
|
4225
|
+
return {
|
|
4226
|
+
note,
|
|
4227
|
+
commitment,
|
|
4228
|
+
identity,
|
|
4229
|
+
address: payment.to,
|
|
4230
|
+
change,
|
|
4231
|
+
delivery: {
|
|
4232
|
+
ciphertext: (0, import_ethers8.hexlify)(
|
|
4233
|
+
await (0, import_core12.encryptPrivateTransferNote)(keys.viewingPublicKey, note, {
|
|
4234
|
+
chainId: context.chainId,
|
|
4235
|
+
protocolAddress: note.protocolAddress,
|
|
4236
|
+
outputCommitment: commitment
|
|
4237
|
+
})
|
|
4238
|
+
),
|
|
4239
|
+
clue: (0, import_ethers8.hexlify)((0, import_core12.createClue)(keys.clueKey))
|
|
4240
|
+
}
|
|
4241
|
+
};
|
|
4242
|
+
} catch (cause) {
|
|
4243
|
+
if (cause instanceof InvisiError) throw cause;
|
|
4244
|
+
throw new InvisiError(
|
|
4245
|
+
"PREPARATION_FAILED",
|
|
4246
|
+
cause instanceof Error ? cause.message : "Output preparation failed",
|
|
4247
|
+
{
|
|
4248
|
+
address: payment.to,
|
|
4249
|
+
field: change ? "changeAddress" : "to",
|
|
4250
|
+
...outputIndex === void 0 ? {} : { outputIndex },
|
|
4251
|
+
cause
|
|
4252
|
+
}
|
|
4253
|
+
);
|
|
4254
|
+
}
|
|
3891
4255
|
}
|
|
3892
4256
|
async membership(input, frame) {
|
|
3893
4257
|
const { options, reader } = this.runtime;
|
|
3894
4258
|
if (!options.protocolRpc)
|
|
3895
|
-
throw new
|
|
4259
|
+
throw new import_core12.MissingCapabilityError("Protocol RPC membership provider");
|
|
3896
4260
|
const root = this.#roots.get(input.treeId);
|
|
3897
4261
|
const proof = await options.protocolRpc.getMembership(
|
|
3898
4262
|
frame.context,
|
|
@@ -3939,9 +4303,9 @@ var Preparation = class {
|
|
|
3939
4303
|
for (const input of selected) {
|
|
3940
4304
|
const note = input.note;
|
|
3941
4305
|
const c = frame.context;
|
|
3942
|
-
if (note.walletIdentity !== scope.walletIdentity || note.chainId !== c.chainId || note.protocolAddress !== BigInt(c.protocolAddress) || note.tokenAddress !== BigInt(c.tokenAddress) || await (0,
|
|
4306
|
+
if (note.walletIdentity !== scope.walletIdentity || note.chainId !== c.chainId || note.protocolAddress !== BigInt(c.protocolAddress) || note.tokenAddress !== BigInt(c.tokenAddress) || await (0, import_core12.computeNoteCommitment)(
|
|
3943
4307
|
c.chainId,
|
|
3944
|
-
await (0,
|
|
4308
|
+
await (0, import_core12.computeNoteIdentity)(BigInt(note.walletIdentity), note.nonce),
|
|
3945
4309
|
note.tokenAddress,
|
|
3946
4310
|
note.amount
|
|
3947
4311
|
) !== input.commitment) {
|
|
@@ -3993,8 +4357,8 @@ function storedCandidates(inputs) {
|
|
|
3993
4357
|
|
|
3994
4358
|
// src/session/prepare-credit.ts
|
|
3995
4359
|
var import_ethers9 = require("ethers");
|
|
3996
|
-
var
|
|
3997
|
-
var
|
|
4360
|
+
var import_core13 = require("@invisi-labs/core");
|
|
4361
|
+
var import_protocol7 = require("@invisi-labs/protocol");
|
|
3998
4362
|
var EMPTY = { ciphertext: "0x", clue: "0x" };
|
|
3999
4363
|
var CreditPreparation = class {
|
|
4000
4364
|
constructor(runtime) {
|
|
@@ -4009,7 +4373,7 @@ var CreditPreparation = class {
|
|
|
4009
4373
|
return value;
|
|
4010
4374
|
}
|
|
4011
4375
|
async seal(input, frame) {
|
|
4012
|
-
const state = (0,
|
|
4376
|
+
const state = (0, import_core13.normalizeCreditDeliveryState)(input);
|
|
4013
4377
|
const commitment = await creditLeaf(state);
|
|
4014
4378
|
const context = {
|
|
4015
4379
|
chainId: frame.context.chainId,
|
|
@@ -4021,9 +4385,9 @@ var CreditPreparation = class {
|
|
|
4021
4385
|
const keys = role === "owner" ? state.ownerReceivingKeys : state.spenderReceivingKeys;
|
|
4022
4386
|
return {
|
|
4023
4387
|
ciphertext: (0, import_ethers9.hexlify)(
|
|
4024
|
-
await (0,
|
|
4388
|
+
await (0, import_core13.encryptCreditNote)(keys.viewingPublicKey, state, { ...context, role })
|
|
4025
4389
|
),
|
|
4026
|
-
clue: (0, import_ethers9.hexlify)((0,
|
|
4390
|
+
clue: (0, import_ethers9.hexlify)((0, import_core13.createClue)(keys.clueKey))
|
|
4027
4391
|
};
|
|
4028
4392
|
})
|
|
4029
4393
|
);
|
|
@@ -4034,14 +4398,14 @@ var CreditPreparation = class {
|
|
|
4034
4398
|
if (!stored || stored.spent !== "unspent" || stored.role !== role) {
|
|
4035
4399
|
throw new Error(`confirmed unspent ${role} Credit is unavailable`);
|
|
4036
4400
|
}
|
|
4037
|
-
const state = (0,
|
|
4401
|
+
const state = (0, import_core13.normalizeCreditDeliveryState)(stored.state);
|
|
4038
4402
|
const n = state.note;
|
|
4039
4403
|
if ((role === "owner" ? n.ownerWalletIdentity : n.spenderWalletIdentity) !== this.runtime.scope.walletIdentity || n.chainId !== frame.context.chainId || n.protocolAddress !== BigInt(frame.context.protocolAddress) || n.tokenAddress !== BigInt(frame.context.tokenAddress) || await creditLeaf(state) !== commitment) {
|
|
4040
4404
|
throw new Error("Credit role, commitment or deployment mismatch");
|
|
4041
4405
|
}
|
|
4042
4406
|
const proof = await this.#prepare.membership(stored, frame);
|
|
4043
4407
|
const path = [...proof.path];
|
|
4044
|
-
const nullifier = await (0,
|
|
4408
|
+
const nullifier = await (0, import_core13.computeCreditNullifier)(n.chainId, n.creditSharedKey, commitment);
|
|
4045
4409
|
await this.#prepare.assertUnspent([nullifier], frame);
|
|
4046
4410
|
return {
|
|
4047
4411
|
input: {
|
|
@@ -4078,10 +4442,16 @@ var CreditPreparation = class {
|
|
|
4078
4442
|
positiveAmount(options.amount, 126);
|
|
4079
4443
|
const frame = await this.#prepare.frame(options.deadline);
|
|
4080
4444
|
const expiry = this.expiry(options.expiry, frame);
|
|
4081
|
-
const spender = await
|
|
4082
|
-
|
|
4445
|
+
const spender = await resolveRecipient(
|
|
4446
|
+
options.spender,
|
|
4447
|
+
this.runtime.options.addressResolver,
|
|
4448
|
+
{ field: "spender" }
|
|
4449
|
+
);
|
|
4450
|
+
const owner = await resolveRecipient(
|
|
4451
|
+
this.runtime.options.wallet.getReceivingAddress().toString(),
|
|
4452
|
+
this.runtime.options.addressResolver,
|
|
4453
|
+
{ field: "wallet" }
|
|
4083
4454
|
);
|
|
4084
|
-
const owner = await this.runtime.options.wallet.getReceivingAddress().resolve(this.runtime.options.addressResolver);
|
|
4085
4455
|
const ownerPayout = (0, import_ethers9.getAddress)(owner.walletIdentity);
|
|
4086
4456
|
const spenderPayout = (0, import_ethers9.getAddress)(spender.walletIdentity);
|
|
4087
4457
|
if (BigInt(ownerPayout) === 0n || BigInt(spenderPayout) === 0n) {
|
|
@@ -4097,16 +4467,16 @@ var CreditPreparation = class {
|
|
|
4097
4467
|
spenderWalletIdentity: spender.walletIdentity,
|
|
4098
4468
|
ownerPayout: BigInt(ownerPayout),
|
|
4099
4469
|
spenderPayout: BigInt(spenderPayout),
|
|
4100
|
-
...(0,
|
|
4470
|
+
...(0, import_core13.normalizeCreditPolicy)({
|
|
4101
4471
|
singleLimit: options.singleLimit ?? 0n,
|
|
4102
4472
|
dailyLimit: options.dailyLimit ?? 0n,
|
|
4103
4473
|
monthlyLimit: options.monthlyLimit ?? 0n
|
|
4104
4474
|
}),
|
|
4105
4475
|
amount: options.amount,
|
|
4106
|
-
|
|
4476
|
+
payload: copyApplicationPayload(options.payload),
|
|
4107
4477
|
expiry,
|
|
4108
|
-
nonce: (0,
|
|
4109
|
-
creditSharedKey: (0,
|
|
4478
|
+
nonce: (0, import_core13.generateNonce)(),
|
|
4479
|
+
creditSharedKey: (0, import_core13.generateCreditSharedKey)()
|
|
4110
4480
|
},
|
|
4111
4481
|
ownerReceivingKeys: owner.getReceivingKeys(),
|
|
4112
4482
|
spenderReceivingKeys: spender.getReceivingKeys()
|
|
@@ -4148,7 +4518,7 @@ var CreditPreparation = class {
|
|
|
4148
4518
|
sources: selected.sources,
|
|
4149
4519
|
nullifiers: selected.nullifiers,
|
|
4150
4520
|
creditCommitment: output.commitment,
|
|
4151
|
-
changeCommitment: plan.outputs[0]?.commitment ?? (0,
|
|
4521
|
+
changeCommitment: plan.outputs[0]?.commitment ?? (0, import_protocol7.word)(0n),
|
|
4152
4522
|
owner: (0, import_ethers9.getAddress)((0, import_ethers9.toBeHex)(output.state.note.ownerPayout, 20)),
|
|
4153
4523
|
expiry: output.state.note.expiry,
|
|
4154
4524
|
deadline: frame.deadline
|
|
@@ -4168,14 +4538,14 @@ var CreditPreparation = class {
|
|
|
4168
4538
|
creditNullifier: previous.nullifier,
|
|
4169
4539
|
previousExpiry: previous.input.state.note.expiry
|
|
4170
4540
|
};
|
|
4171
|
-
plan.statement =
|
|
4541
|
+
plan.statement = import_protocol7.WitnessBuilder.statement(
|
|
4172
4542
|
"credit_top_up",
|
|
4173
|
-
(0,
|
|
4543
|
+
(0, import_protocol7.topUpCreditPublicInputs)(frame.context, plan.credit.topUp, deliveries, plan.funding)
|
|
4174
4544
|
);
|
|
4175
4545
|
} else {
|
|
4176
|
-
plan.statement =
|
|
4546
|
+
plan.statement = import_protocol7.WitnessBuilder.statement(
|
|
4177
4547
|
"create_credit",
|
|
4178
|
-
(0,
|
|
4548
|
+
(0, import_protocol7.createCreditPublicInputs)(frame.context, creation, deliveries, plan.funding)
|
|
4179
4549
|
);
|
|
4180
4550
|
}
|
|
4181
4551
|
await this.#prepare.assertBlock(frame);
|
|
@@ -4204,8 +4574,10 @@ var CreditPreparation = class {
|
|
|
4204
4574
|
monthlyLimit: options.monthlyLimit ?? old.note.monthlyLimit ?? 0n,
|
|
4205
4575
|
amount,
|
|
4206
4576
|
expiry,
|
|
4207
|
-
nonce: (0,
|
|
4208
|
-
|
|
4577
|
+
nonce: (0, import_core13.generateNonce)(),
|
|
4578
|
+
payload: copyApplicationPayload(
|
|
4579
|
+
options.payload === void 0 ? old.note.payload : options.payload
|
|
4580
|
+
)
|
|
4209
4581
|
}
|
|
4210
4582
|
},
|
|
4211
4583
|
frame
|
|
@@ -4221,7 +4593,7 @@ var CreditPreparation = class {
|
|
|
4221
4593
|
}
|
|
4222
4594
|
async draw(options) {
|
|
4223
4595
|
const frame = await this.#prepare.frame(options.deadline);
|
|
4224
|
-
const profile = (0,
|
|
4596
|
+
const profile = (0, import_protocol7.selectCreditDrawCircuit)(options.credits.length);
|
|
4225
4597
|
if (new Set(options.credits.map((value) => value.credit)).size !== options.credits.length) {
|
|
4226
4598
|
throw new Error("duplicate Credit input");
|
|
4227
4599
|
}
|
|
@@ -4253,11 +4625,11 @@ var CreditPreparation = class {
|
|
|
4253
4625
|
}
|
|
4254
4626
|
sources.set(member.source.treeId, member.source.merkleRoot);
|
|
4255
4627
|
amounts.push(amount);
|
|
4256
|
-
const usage = (0,
|
|
4628
|
+
const usage = (0, import_core13.applyCreditDrawUsage)(n, amount, frame.block.timestamp);
|
|
4257
4629
|
const remaining = n.amount - amount;
|
|
4258
4630
|
if (remaining === 0n) {
|
|
4259
|
-
remainders.push((0,
|
|
4260
|
-
recoveryAmounts.push((0,
|
|
4631
|
+
remainders.push((0, import_protocol7.word)(0n));
|
|
4632
|
+
recoveryAmounts.push((0, import_protocol7.word)(0n));
|
|
4261
4633
|
deliveries.push({ ...EMPTY }, { ...EMPTY });
|
|
4262
4634
|
continue;
|
|
4263
4635
|
}
|
|
@@ -4275,8 +4647,10 @@ var CreditPreparation = class {
|
|
|
4275
4647
|
...n,
|
|
4276
4648
|
...usage,
|
|
4277
4649
|
amount: remaining,
|
|
4278
|
-
|
|
4279
|
-
|
|
4650
|
+
payload: copyApplicationPayload(
|
|
4651
|
+
options.credits[i].payload === void 0 ? n.payload : options.credits[i].payload
|
|
4652
|
+
),
|
|
4653
|
+
nonce: await (0, import_core13.deriveRemainderNonce)(...args)
|
|
4280
4654
|
}
|
|
4281
4655
|
},
|
|
4282
4656
|
frame
|
|
@@ -4284,7 +4658,7 @@ var CreditPreparation = class {
|
|
|
4284
4658
|
outputs.push(output);
|
|
4285
4659
|
remainders.push(output.commitment);
|
|
4286
4660
|
recoveryAmounts.push(
|
|
4287
|
-
(0,
|
|
4661
|
+
(0, import_protocol7.word)((remaining + await (0, import_core13.computeCreditRecoveryPad)(...args)) % import_core13.FIELD_MODULUS)
|
|
4288
4662
|
);
|
|
4289
4663
|
deliveries.push(...output.deliveries);
|
|
4290
4664
|
}
|
|
@@ -4308,9 +4682,9 @@ var CreditPreparation = class {
|
|
|
4308
4682
|
draw: params,
|
|
4309
4683
|
amounts
|
|
4310
4684
|
};
|
|
4311
|
-
plan.statement =
|
|
4685
|
+
plan.statement = import_protocol7.WitnessBuilder.statement(
|
|
4312
4686
|
profile.name,
|
|
4313
|
-
(0,
|
|
4687
|
+
(0, import_protocol7.creditDrawPublicInputs)(frame.context, params, deliveries)
|
|
4314
4688
|
);
|
|
4315
4689
|
await this.#prepare.assertBlock(frame);
|
|
4316
4690
|
return this.runtime.complete(plan);
|
|
@@ -4330,9 +4704,9 @@ var CreditPreparation = class {
|
|
|
4330
4704
|
};
|
|
4331
4705
|
plan.capacity = 1;
|
|
4332
4706
|
plan.credit = { inputs: [previous.input], outputs: [], deliveries: [], cancel: params };
|
|
4333
|
-
plan.statement =
|
|
4707
|
+
plan.statement = import_protocol7.WitnessBuilder.statement(
|
|
4334
4708
|
"credit_cancel",
|
|
4335
|
-
(0,
|
|
4709
|
+
(0, import_protocol7.cancelCreditPublicInputs)(frame.context, params)
|
|
4336
4710
|
);
|
|
4337
4711
|
await this.#prepare.assertBlock(frame);
|
|
4338
4712
|
return this.runtime.complete(plan);
|
|
@@ -4353,87 +4727,48 @@ var TransactionPreparation = class {
|
|
|
4353
4727
|
return this.context.scope;
|
|
4354
4728
|
}
|
|
4355
4729
|
async prepareTransfer(options) {
|
|
4730
|
+
if (Object.hasOwn(options, "target") || Object.hasOwn(options, "execution")) {
|
|
4731
|
+
throw new Error(
|
|
4732
|
+
"prepareTransfer accepts no target or execution; choose execution in prove()"
|
|
4733
|
+
);
|
|
4734
|
+
}
|
|
4356
4735
|
const copied = structuredClone(options);
|
|
4357
|
-
const
|
|
4736
|
+
const runtime = this.runtime(copied.tokenAddress);
|
|
4737
|
+
if (copied.commitments !== void 0 && copied.commitments.length === 0)
|
|
4738
|
+
throw new Error("empty explicit input selection");
|
|
4739
|
+
const preparation = new Preparation({
|
|
4740
|
+
...runtime,
|
|
4741
|
+
...copied.commitments !== void 0 ? { inputSelection: copied.commitments } : {}
|
|
4742
|
+
});
|
|
4358
4743
|
const frame = await preparation.frame(copied.deadline);
|
|
4359
|
-
if (copied.outputs.length
|
|
4360
|
-
|
|
4744
|
+
if (copied.outputs.length > 168) throw new Error("transfer supports at most 168 outputs");
|
|
4745
|
+
let withdrawalAddress = import_protocol8.ZERO_ADDRESS;
|
|
4746
|
+
let withdrawalAmount = 0n;
|
|
4747
|
+
if (copied.withdrawal !== void 0) {
|
|
4748
|
+
withdrawalAddress = (0, import_ethers10.getAddress)(copied.withdrawal.to);
|
|
4749
|
+
positiveAmount(copied.withdrawal.amount);
|
|
4750
|
+
if (BigInt(withdrawalAddress) === 0n)
|
|
4751
|
+
throw new Error("withdrawal destination must be nonzero");
|
|
4752
|
+
withdrawalAmount = copied.withdrawal.amount;
|
|
4361
4753
|
}
|
|
4754
|
+
if (copied.outputs.length === 0 && withdrawalAmount === 0n)
|
|
4755
|
+
throw new Error("transfer requires a private output or public withdrawal");
|
|
4362
4756
|
const total = copied.outputs.reduce(
|
|
4363
|
-
(sum, output) => sum +
|
|
4364
|
-
|
|
4757
|
+
(sum, output, index2) => sum + positiveOutputAmount(output, index2),
|
|
4758
|
+
withdrawalAmount
|
|
4365
4759
|
);
|
|
4366
4760
|
const funding = await preparation.funding(copied.funding, frame);
|
|
4367
4761
|
return this.funds(
|
|
4368
|
-
"transfer",
|
|
4369
4762
|
copied.outputs,
|
|
4370
4763
|
total,
|
|
4371
|
-
|
|
4372
|
-
|
|
4764
|
+
withdrawalAddress,
|
|
4765
|
+
withdrawalAmount,
|
|
4373
4766
|
frame,
|
|
4374
4767
|
funding,
|
|
4375
|
-
copied.target,
|
|
4376
|
-
preparation
|
|
4377
|
-
);
|
|
4378
|
-
}
|
|
4379
|
-
async prepareWithdraw(options) {
|
|
4380
|
-
const copied = structuredClone(options);
|
|
4381
|
-
const preparation = new Preparation(this.runtime(copied.tokenAddress));
|
|
4382
|
-
const frame = await preparation.frame(copied.deadline);
|
|
4383
|
-
const to = (0, import_ethers10.getAddress)(copied.to);
|
|
4384
|
-
positiveAmount(copied.amount);
|
|
4385
|
-
if (BigInt(to) === 0n) {
|
|
4386
|
-
throw new Error("withdrawal destination must be nonzero");
|
|
4387
|
-
}
|
|
4388
|
-
return this.funds(
|
|
4389
|
-
"withdraw",
|
|
4390
|
-
[],
|
|
4391
|
-
copied.amount,
|
|
4392
|
-
to,
|
|
4393
|
-
copied.amount,
|
|
4394
|
-
frame,
|
|
4395
|
-
{ funding: {}, fundingMode: "none" },
|
|
4396
|
-
copied.target,
|
|
4397
|
-
preparation
|
|
4398
|
-
);
|
|
4399
|
-
}
|
|
4400
|
-
/** Full payout to the note owner; no arbitrary destination, change or spending signature. */
|
|
4401
|
-
async prepareSweep(options) {
|
|
4402
|
-
const copied = structuredClone(options);
|
|
4403
|
-
const runtime = this.runtime(copied.tokenAddress);
|
|
4404
|
-
const state = await runtime.readInventory();
|
|
4405
|
-
const available = Object.values(state.notes).filter(
|
|
4406
|
-
(record) => record.note.tokenAddress === BigInt(copied.tokenAddress) && record.spent === "unspent" && !state.reservations[record.commitment]
|
|
4407
|
-
);
|
|
4408
|
-
const selected = copied.commitments ?? available.map((record) => record.commitment);
|
|
4409
|
-
if (selected.length === 0) throw new Error("empty sweep input selection");
|
|
4410
|
-
if (new Set(selected).size !== selected.length) throw new Error("duplicate sweep inputs");
|
|
4411
|
-
const byId = new Map(available.map((record) => [record.commitment, record]));
|
|
4412
|
-
let amount = 0n;
|
|
4413
|
-
for (const id of selected) {
|
|
4414
|
-
const record = byId.get(id);
|
|
4415
|
-
if (!record) throw new Error("sweep input unavailable");
|
|
4416
|
-
amount += record.note.amount;
|
|
4417
|
-
}
|
|
4418
|
-
positiveAmount(amount);
|
|
4419
|
-
const preparation = new Preparation({
|
|
4420
|
-
...runtime,
|
|
4421
|
-
inputSelection: selected
|
|
4422
|
-
});
|
|
4423
|
-
const frame = await preparation.frame(copied.deadline);
|
|
4424
|
-
return this.funds(
|
|
4425
|
-
"sweep",
|
|
4426
|
-
[],
|
|
4427
|
-
amount,
|
|
4428
|
-
this.scope.walletIdentity,
|
|
4429
|
-
amount,
|
|
4430
|
-
frame,
|
|
4431
|
-
{ funding: {}, fundingMode: "none" },
|
|
4432
|
-
copied.target,
|
|
4433
4768
|
preparation
|
|
4434
4769
|
);
|
|
4435
4770
|
}
|
|
4436
|
-
async funds(
|
|
4771
|
+
async funds(payments, total, withdrawalAddress, withdrawalAmount, frame, funding, preparation) {
|
|
4437
4772
|
if (total >= 1n << 135n) {
|
|
4438
4773
|
throw new Error("transaction sum exceeds uint135");
|
|
4439
4774
|
}
|
|
@@ -4443,7 +4778,7 @@ var TransactionPreparation = class {
|
|
|
4443
4778
|
frame
|
|
4444
4779
|
);
|
|
4445
4780
|
const outputs = await Promise.all(
|
|
4446
|
-
payments.map((payment) => preparation.output(payment, context))
|
|
4781
|
+
payments.map((payment, index2) => preparation.output(payment, context, false, index2))
|
|
4447
4782
|
);
|
|
4448
4783
|
const change = selection.amount + (funding.funding.amount ?? 0n) - total;
|
|
4449
4784
|
if (change > 0n) {
|
|
@@ -4468,17 +4803,17 @@ var TransactionPreparation = class {
|
|
|
4468
4803
|
deadline
|
|
4469
4804
|
};
|
|
4470
4805
|
const deliveries = outputs.map((output) => output.delivery);
|
|
4471
|
-
const normalized = (0,
|
|
4472
|
-
const statement2 =
|
|
4806
|
+
const normalized = (0, import_protocol8.normalizeTransactRequest)(context, params, deliveries, funding.funding);
|
|
4807
|
+
const statement2 = import_protocol8.WitnessBuilder.statement(
|
|
4473
4808
|
`transact_user_slots${normalized.capacity}`,
|
|
4474
|
-
(0,
|
|
4475
|
-
|
|
4809
|
+
(0, import_protocol8.transactUserPublicInputs)(context, params, deliveries, funding.funding),
|
|
4810
|
+
"evm"
|
|
4476
4811
|
);
|
|
4477
4812
|
await preparation.assertBlock(frame);
|
|
4478
4813
|
return preparation.runtime.complete({
|
|
4479
4814
|
version: 1,
|
|
4480
4815
|
id: crypto.randomUUID(),
|
|
4481
|
-
kind,
|
|
4816
|
+
kind: "transfer",
|
|
4482
4817
|
context,
|
|
4483
4818
|
scope: this.scope,
|
|
4484
4819
|
preparedAt: block,
|
|
@@ -4500,7 +4835,9 @@ var TransactionPreparation = class {
|
|
|
4500
4835
|
throw new Error("shield requires 1..10 outputs");
|
|
4501
4836
|
}
|
|
4502
4837
|
const outputs = await Promise.all(
|
|
4503
|
-
copied.outputs.map(
|
|
4838
|
+
copied.outputs.map(
|
|
4839
|
+
(output, index2) => preparation.output(output, frame.context, false, index2)
|
|
4840
|
+
)
|
|
4504
4841
|
);
|
|
4505
4842
|
if (outputs.reduce((sum, output) => sum + output.note.amount, 0n) !== (funding.funding.amount ?? 0n)) {
|
|
4506
4843
|
throw new Error("shield funding must equal output total");
|
|
@@ -4605,10 +4942,10 @@ async function refreshWallet(runtime, options) {
|
|
|
4605
4942
|
}
|
|
4606
4943
|
|
|
4607
4944
|
// src/session/WalletUpdater.ts
|
|
4608
|
-
var
|
|
4945
|
+
var import_core15 = require("@invisi-labs/core");
|
|
4609
4946
|
|
|
4610
4947
|
// src/session/inventory.ts
|
|
4611
|
-
var
|
|
4948
|
+
var import_core14 = require("@invisi-labs/core");
|
|
4612
4949
|
var CanonicalConflict = class extends Error {
|
|
4613
4950
|
};
|
|
4614
4951
|
var CreditMetadataMismatch = class extends Error {
|
|
@@ -4660,10 +4997,10 @@ var InventoryProcessor = class {
|
|
|
4660
4997
|
}
|
|
4661
4998
|
async creditNullifier(state) {
|
|
4662
4999
|
const n = state.note;
|
|
4663
|
-
return (0,
|
|
5000
|
+
return (0, import_core14.computeCreditNullifier)(
|
|
4664
5001
|
n.chainId,
|
|
4665
5002
|
n.creditSharedKey,
|
|
4666
|
-
await (0,
|
|
5003
|
+
await (0, import_core14.computeCreditCommitment)(
|
|
4667
5004
|
n.chainId,
|
|
4668
5005
|
n.spenderWalletIdentity,
|
|
4669
5006
|
n.ownerWalletIdentity,
|
|
@@ -4673,7 +5010,7 @@ var InventoryProcessor = class {
|
|
|
4673
5010
|
n.amount,
|
|
4674
5011
|
n.nonce,
|
|
4675
5012
|
n.expiry,
|
|
4676
|
-
await (0,
|
|
5013
|
+
await (0, import_core14.computeCreditSharedKeyHash)(n.creditSharedKey),
|
|
4677
5014
|
n
|
|
4678
5015
|
)
|
|
4679
5016
|
);
|
|
@@ -4737,7 +5074,7 @@ var InventoryProcessor = class {
|
|
|
4737
5074
|
previous.state.note.expiry === params.expiries[index2],
|
|
4738
5075
|
"draw recovery expiry mismatch"
|
|
4739
5076
|
);
|
|
4740
|
-
const remainder = await (0,
|
|
5077
|
+
const remainder = await (0, import_core14.recoverCreditRemainder)(
|
|
4741
5078
|
context,
|
|
4742
5079
|
previous.state.note,
|
|
4743
5080
|
{
|
|
@@ -4753,7 +5090,7 @@ var InventoryProcessor = class {
|
|
|
4753
5090
|
const child = foundByKey.get(
|
|
4754
5091
|
params.remainderCommitments[index2] + ":" + previous.role
|
|
4755
5092
|
);
|
|
4756
|
-
const
|
|
5093
|
+
const payload = child?.state.note.payload;
|
|
4757
5094
|
const record = {
|
|
4758
5095
|
commitment: params.remainderCommitments[index2],
|
|
4759
5096
|
treeId: BigInt(args.outputTreeId),
|
|
@@ -4766,7 +5103,7 @@ var InventoryProcessor = class {
|
|
|
4766
5103
|
...clone(previous.state),
|
|
4767
5104
|
note: {
|
|
4768
5105
|
...remainder,
|
|
4769
|
-
...
|
|
5106
|
+
...payload === void 0 ? {} : { payload: clone(payload) }
|
|
4770
5107
|
}
|
|
4771
5108
|
},
|
|
4772
5109
|
receivingKeySource: { kind: "draw", commitment: previous.commitment },
|
|
@@ -4845,7 +5182,7 @@ var WalletUpdater = class extends InventoryProcessor {
|
|
|
4845
5182
|
}
|
|
4846
5183
|
async run(batch, options) {
|
|
4847
5184
|
const { reader, protocol, scope, store, viewing } = this.#options;
|
|
4848
|
-
const nullifier =
|
|
5185
|
+
const nullifier = import_core15.NullifierCapability.fromIdentity(scope.walletIdentity);
|
|
4849
5186
|
const { signal, reset } = options;
|
|
4850
5187
|
signal?.throwIfAborted();
|
|
4851
5188
|
const context = protocol.context;
|
|
@@ -5014,6 +5351,7 @@ var WalletUpdater = class extends InventoryProcessor {
|
|
|
5014
5351
|
draft.notes = {};
|
|
5015
5352
|
draft.credits = {};
|
|
5016
5353
|
delete draft.observedThrough;
|
|
5354
|
+
delete draft.discoveryThrough;
|
|
5017
5355
|
delete draft.invalidated;
|
|
5018
5356
|
}
|
|
5019
5357
|
mergeReceiptRecords(draft, { notes, credits, checkpoint: block });
|
|
@@ -5045,13 +5383,38 @@ var WalletUpdater = class extends InventoryProcessor {
|
|
|
5045
5383
|
var WalletSession = class {
|
|
5046
5384
|
#options;
|
|
5047
5385
|
#updater;
|
|
5386
|
+
#listeners = /* @__PURE__ */ new Set();
|
|
5048
5387
|
#sending = /* @__PURE__ */ new Set();
|
|
5049
5388
|
#refreshQueue = Promise.resolve();
|
|
5050
5389
|
scope;
|
|
5051
5390
|
protocol;
|
|
5052
5391
|
constructor(options) {
|
|
5392
|
+
if ("ciphertextCodecs" in options || "envelopeVersion" in options) {
|
|
5393
|
+
throw new Error(
|
|
5394
|
+
"SDK envelope versions are fixed; encode application payload bytes externally"
|
|
5395
|
+
);
|
|
5396
|
+
}
|
|
5397
|
+
const store = options.store;
|
|
5053
5398
|
this.#options = {
|
|
5054
5399
|
...options,
|
|
5400
|
+
...store ? {
|
|
5401
|
+
store: {
|
|
5402
|
+
persistent: store.persistent,
|
|
5403
|
+
readSnapshot: (scope) => store.readSnapshot(scope),
|
|
5404
|
+
transaction: async (scope, action) => {
|
|
5405
|
+
const result = await store.transaction(scope, action);
|
|
5406
|
+
if (scopeKey(scope) === scopeKey(this.scope)) {
|
|
5407
|
+
for (const listener of [...this.#listeners]) {
|
|
5408
|
+
try {
|
|
5409
|
+
listener();
|
|
5410
|
+
} catch {
|
|
5411
|
+
}
|
|
5412
|
+
}
|
|
5413
|
+
}
|
|
5414
|
+
return result;
|
|
5415
|
+
}
|
|
5416
|
+
}
|
|
5417
|
+
} : {},
|
|
5055
5418
|
...options.gasBenchmarks ? { gasBenchmarks: copyGasBenchmarks(options.gasBenchmarks) } : {}
|
|
5056
5419
|
};
|
|
5057
5420
|
this.protocol = options.protocol;
|
|
@@ -5066,7 +5429,7 @@ var WalletSession = class {
|
|
|
5066
5429
|
/** Delegate sending to ethers; managed operations additionally retain the resulting hash. */
|
|
5067
5430
|
async sendTransaction(input, options = {}) {
|
|
5068
5431
|
const signer = this.#options.signer;
|
|
5069
|
-
if (!signer) throw new
|
|
5432
|
+
if (!signer) throw new import_core17.MissingCapabilityError("ethers signer");
|
|
5070
5433
|
const operation = input instanceof Operation ? input : void 0;
|
|
5071
5434
|
if (operation && this.#sending.has(operation.id))
|
|
5072
5435
|
throw new Error("operation is already being sent by this session");
|
|
@@ -5074,12 +5437,12 @@ var WalletSession = class {
|
|
|
5074
5437
|
const overrides = { ...options };
|
|
5075
5438
|
const plain = operation ? void 0 : { ...input };
|
|
5076
5439
|
try {
|
|
5077
|
-
if (!signer.provider) throw new
|
|
5440
|
+
if (!signer.provider) throw new import_core17.MissingCapabilityError("connected ethers signer");
|
|
5078
5441
|
if ((await signer.provider.getNetwork()).chainId !== this.protocol.context.chainId) {
|
|
5079
5442
|
throw new Error("signer-provider session chain mismatch");
|
|
5080
5443
|
}
|
|
5081
5444
|
if (operation) {
|
|
5082
|
-
if (!this.#options.store) throw new
|
|
5445
|
+
if (!this.#options.store) throw new import_core17.MissingCapabilityError("wallet store");
|
|
5083
5446
|
const state = await this.#options.store.readSnapshot(this.scope);
|
|
5084
5447
|
if (!state.operations[operation.id])
|
|
5085
5448
|
throw new Error("operation does not belong to this wallet session");
|
|
@@ -5110,25 +5473,30 @@ var WalletSession = class {
|
|
|
5110
5473
|
runtime(tokenAddress) {
|
|
5111
5474
|
const options = this.#options;
|
|
5112
5475
|
if (!options.store) {
|
|
5113
|
-
throw new
|
|
5476
|
+
throw new import_core17.MissingCapabilityError("wallet store");
|
|
5114
5477
|
}
|
|
5115
5478
|
if (!options.chainReader) {
|
|
5116
|
-
throw new
|
|
5479
|
+
throw new import_core17.MissingCapabilityError("chain reader");
|
|
5117
5480
|
}
|
|
5118
5481
|
return {
|
|
5119
5482
|
options: { ...options, protocol: options.protocol.forToken(tokenAddress) },
|
|
5120
5483
|
scope: this.scope,
|
|
5121
5484
|
store: options.store,
|
|
5122
5485
|
reader: options.chainReader,
|
|
5123
|
-
authorization: () =>
|
|
5486
|
+
authorization: () => {
|
|
5487
|
+
if (options.authorization) return options.authorization;
|
|
5488
|
+
if (options.wallet instanceof import_core16.Wallet) return options.wallet.exportAuthorization();
|
|
5489
|
+
throw new import_core17.MissingCapabilityError("wallet authorization");
|
|
5490
|
+
}
|
|
5124
5491
|
};
|
|
5125
5492
|
}
|
|
5126
5493
|
updater() {
|
|
5127
5494
|
if (this.#updater) return this.#updater;
|
|
5128
5495
|
const options = this.#options;
|
|
5129
5496
|
if (!options.store || !options.chainReader || !options.protocolRpc)
|
|
5130
|
-
throw new
|
|
5131
|
-
const viewing = options.viewing ?? options.wallet.getViewer();
|
|
5497
|
+
throw new import_core17.MissingCapabilityError("wallet store, chain reader and Protocol RPC");
|
|
5498
|
+
const viewing = options.viewing ?? (options.wallet instanceof import_core16.Wallet ? options.wallet.getViewer() : void 0);
|
|
5499
|
+
if (!viewing) throw new import_core17.MissingCapabilityError("viewing");
|
|
5132
5500
|
const receivingAddress = options.wallet.getReceivingAddress();
|
|
5133
5501
|
const receivingKeys = receivingAddress.kind === "long" ? receivingAddress.getReceivingKeys() : void 0;
|
|
5134
5502
|
this.#updater = new WalletUpdater({
|
|
@@ -5142,10 +5510,27 @@ var WalletSession = class {
|
|
|
5142
5510
|
});
|
|
5143
5511
|
return this.#updater;
|
|
5144
5512
|
}
|
|
5513
|
+
/** Local session commits only. Host applications own cross-tab/store subscriptions. */
|
|
5514
|
+
subscribe(listener) {
|
|
5515
|
+
this.#listeners.add(listener);
|
|
5516
|
+
return () => {
|
|
5517
|
+
this.#listeners.delete(listener);
|
|
5518
|
+
};
|
|
5519
|
+
}
|
|
5520
|
+
/** Detached history for wallet UI; construction records do not imply chain success. */
|
|
5521
|
+
async getHistory(options = {}) {
|
|
5522
|
+
const tokenAddress = options.tokenAddress ? (0, import_ethers11.getAddress)(options.tokenAddress) : void 0;
|
|
5523
|
+
const store = this.#options.store;
|
|
5524
|
+
if (!store) throw new import_core17.MissingCapabilityError("wallet store");
|
|
5525
|
+
return walletHistory(
|
|
5526
|
+
await store.readSnapshot(this.scope),
|
|
5527
|
+
tokenAddress ? { tokenAddress } : {}
|
|
5528
|
+
);
|
|
5529
|
+
}
|
|
5145
5530
|
async balance(tokenAddress) {
|
|
5146
5531
|
const store = this.#options.store;
|
|
5147
5532
|
if (!store) {
|
|
5148
|
-
throw new
|
|
5533
|
+
throw new import_core17.MissingCapabilityError("wallet store");
|
|
5149
5534
|
}
|
|
5150
5535
|
return walletBalance(await store.readSnapshot(this.scope), (0, import_ethers11.getAddress)(tokenAddress));
|
|
5151
5536
|
}
|
|
@@ -5153,15 +5538,15 @@ var WalletSession = class {
|
|
|
5153
5538
|
async refresh(options) {
|
|
5154
5539
|
const provider = this.#options.protocolRpc;
|
|
5155
5540
|
const reader = this.#options.chainReader;
|
|
5156
|
-
if (!reader) throw new
|
|
5157
|
-
if (!provider) throw new
|
|
5541
|
+
if (!reader) throw new import_core17.MissingCapabilityError("chain reader");
|
|
5542
|
+
if (!provider) throw new import_core17.MissingCapabilityError("Protocol RPC");
|
|
5158
5543
|
const updater = this.updater();
|
|
5159
5544
|
const request = {
|
|
5160
5545
|
...options,
|
|
5161
5546
|
...options.detectionKey ? { detectionKey: options.detectionKey.slice() } : {}
|
|
5162
5547
|
};
|
|
5163
|
-
const task = this.#refreshQueue.then(
|
|
5164
|
-
|
|
5548
|
+
const task = this.#refreshQueue.then(async () => {
|
|
5549
|
+
const result = await refreshWallet(
|
|
5165
5550
|
{
|
|
5166
5551
|
provider,
|
|
5167
5552
|
reader,
|
|
@@ -5170,8 +5555,21 @@ var WalletSession = class {
|
|
|
5170
5555
|
apply: (batch, settings) => updater.applyUpdates(batch, settings)
|
|
5171
5556
|
},
|
|
5172
5557
|
request
|
|
5173
|
-
)
|
|
5174
|
-
|
|
5558
|
+
);
|
|
5559
|
+
await this.#options.store.transaction(this.scope, (state) => {
|
|
5560
|
+
request.signal?.throwIfAborted();
|
|
5561
|
+
if (state.invalidated) throw new Error("wallet requires canonical resync");
|
|
5562
|
+
const previous = state.discoveryThrough;
|
|
5563
|
+
const start = previous ? previous.number + 1n : this.protocol.deployment.protocol.fromBlock;
|
|
5564
|
+
if (request.fromBlock <= start && (!previous || result.block.number >= previous.number)) {
|
|
5565
|
+
state.discoveryThrough = {
|
|
5566
|
+
number: result.block.number,
|
|
5567
|
+
hash: result.block.hash
|
|
5568
|
+
};
|
|
5569
|
+
}
|
|
5570
|
+
});
|
|
5571
|
+
return result;
|
|
5572
|
+
});
|
|
5175
5573
|
this.#refreshQueue = task.catch(() => {
|
|
5176
5574
|
});
|
|
5177
5575
|
return task;
|
|
@@ -5182,38 +5580,43 @@ var WalletSession = class {
|
|
|
5182
5580
|
options: runtime.options,
|
|
5183
5581
|
scope: runtime.scope,
|
|
5184
5582
|
reader: runtime.reader,
|
|
5185
|
-
authorization: runtime.authorization,
|
|
5186
5583
|
readInventory: () => runtime.store.readSnapshot(runtime.scope),
|
|
5187
5584
|
complete: (plan) => Operation.prepare(runtime, plan)
|
|
5188
5585
|
});
|
|
5189
5586
|
}
|
|
5190
5587
|
async prepareTransfer(options) {
|
|
5191
|
-
return
|
|
5192
|
-
|
|
5193
|
-
|
|
5194
|
-
return this.preparation(options.tokenAddress).prepareWithdraw(options);
|
|
5195
|
-
}
|
|
5196
|
-
async prepareSweep(options) {
|
|
5197
|
-
return this.preparation(options.tokenAddress).prepareSweep(options);
|
|
5588
|
+
return prepareWithErrors("prepareTransfer", async () => {
|
|
5589
|
+
return this.preparation(options.tokenAddress).prepareTransfer(options);
|
|
5590
|
+
});
|
|
5198
5591
|
}
|
|
5199
5592
|
async prepareShield(options) {
|
|
5200
|
-
return
|
|
5593
|
+
return prepareWithErrors("prepareShield", async () => {
|
|
5594
|
+
return this.preparation(options.tokenAddress).prepareShield(options);
|
|
5595
|
+
});
|
|
5201
5596
|
}
|
|
5202
5597
|
async prepareCreateCredit(options) {
|
|
5203
|
-
return
|
|
5598
|
+
return prepareWithErrors("prepareCreateCredit", async () => {
|
|
5599
|
+
return this.preparation(options.tokenAddress).prepareCreateCredit(options);
|
|
5600
|
+
});
|
|
5204
5601
|
}
|
|
5205
5602
|
async prepareDrawCredit(options) {
|
|
5206
|
-
return
|
|
5603
|
+
return prepareWithErrors("prepareDrawCredit", async () => {
|
|
5604
|
+
return this.preparation(options.tokenAddress).prepareDrawCredit(options);
|
|
5605
|
+
});
|
|
5207
5606
|
}
|
|
5208
5607
|
async prepareTopUpCredit(options) {
|
|
5209
|
-
return
|
|
5608
|
+
return prepareWithErrors("prepareTopUpCredit", async () => {
|
|
5609
|
+
return this.preparation(options.tokenAddress).prepareTopUpCredit(options);
|
|
5610
|
+
});
|
|
5210
5611
|
}
|
|
5211
5612
|
async prepareCancelCredit(options) {
|
|
5212
|
-
return
|
|
5613
|
+
return prepareWithErrors("prepareCancelCredit", async () => {
|
|
5614
|
+
return this.preparation(options.tokenAddress).prepareCancelCredit(options);
|
|
5615
|
+
});
|
|
5213
5616
|
}
|
|
5214
5617
|
async resume(operationId) {
|
|
5215
5618
|
const store = this.#options.store;
|
|
5216
|
-
if (!store) throw new
|
|
5619
|
+
if (!store) throw new import_core17.MissingCapabilityError("wallet store");
|
|
5217
5620
|
const state = await store.readSnapshot(this.scope);
|
|
5218
5621
|
const saved = state.operations[operationId]?.payload;
|
|
5219
5622
|
if (!saved?.plan) throw new Error("operation not found");
|