@invisi-labs/sdk 1.1.3 → 1.1.5
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/sqlite.cjs
CHANGED
|
@@ -321,8 +321,9 @@ function readProtocolLogs(receipt, protocolAddress) {
|
|
|
321
321
|
}
|
|
322
322
|
|
|
323
323
|
// src/session/discovery.ts
|
|
324
|
-
var import_ethers3 = require("ethers");
|
|
325
324
|
var import_core = require("@invisi-labs/core");
|
|
325
|
+
var import_ethers3 = require("ethers");
|
|
326
|
+
var import_core2 = require("@invisi-labs/core");
|
|
326
327
|
var import_protocol = require("@invisi-labs/protocol");
|
|
327
328
|
var EVENTS = /* @__PURE__ */ new Set([
|
|
328
329
|
"CommitmentsInserted",
|
|
@@ -416,7 +417,7 @@ function extractDeliveryCandidates(logs, context, confirmedThrough) {
|
|
|
416
417
|
check2(inserted, "delivery has no matching canonical insertion");
|
|
417
418
|
if (commitment !== void 0)
|
|
418
419
|
check2(hash(commitment) === inserted, "delivery commitment conflicts with insertion");
|
|
419
|
-
const delivery = (0, import_protocol.
|
|
420
|
+
const delivery = (0, import_protocol.normalizeOpaqueDelivery)(wire);
|
|
420
421
|
check2(
|
|
421
422
|
typeof token === "string" && (0, import_ethers3.isAddress)(token) && BigInt(token) !== 0n,
|
|
422
423
|
"invalid event token"
|
|
@@ -447,7 +448,7 @@ function extractDeliveryCandidates(logs, context, confirmedThrough) {
|
|
|
447
448
|
add(log, tree, index(leaves[0]), fields.creditCommitment, "owner", ds[0]);
|
|
448
449
|
add(log, tree, index(leaves[0]), fields.creditCommitment, "spender", ds[1]);
|
|
449
450
|
if (hasChange) add(log, tree, index(leaves[1]), change, "funds", ds[2]);
|
|
450
|
-
else (0, import_protocol.
|
|
451
|
+
else (0, import_protocol.normalizeOpaqueDelivery)(ds[2], { empty: true });
|
|
451
452
|
};
|
|
452
453
|
for (const log of relevant) {
|
|
453
454
|
const a = object(log.args);
|
|
@@ -477,7 +478,7 @@ function extractDeliveryCandidates(logs, context, confirmedThrough) {
|
|
|
477
478
|
const length = Number.parseInt(packed.slice(offset, offset + 4), 16);
|
|
478
479
|
offset += 4;
|
|
479
480
|
check2(
|
|
480
|
-
length >=
|
|
481
|
+
length >= 1 && length <= import_core.MAX_CIPHERTEXT_BYTES && offset + length * 2 + 136 <= packed.length,
|
|
481
482
|
"invalid packed delivery length"
|
|
482
483
|
);
|
|
483
484
|
const ciphertext = `0x${packed.slice(offset, offset + length * 2)}`;
|
|
@@ -499,8 +500,8 @@ function extractDeliveryCandidates(logs, context, confirmedThrough) {
|
|
|
499
500
|
cs.forEach((c, i) => {
|
|
500
501
|
if (BigInt(hash(c)) === 0n) {
|
|
501
502
|
check2(integer(leaves[i]) === 0n, "absent remainder has nonzero position");
|
|
502
|
-
(0, import_protocol.
|
|
503
|
-
(0, import_protocol.
|
|
503
|
+
(0, import_protocol.normalizeOpaqueDelivery)(ds[2 * i], { empty: true });
|
|
504
|
+
(0, import_protocol.normalizeOpaqueDelivery)(ds[2 * i + 1], { empty: true });
|
|
504
505
|
} else {
|
|
505
506
|
add(log, tree, index(leaves[i]), c, "owner", ds[2 * i]);
|
|
506
507
|
add(log, tree, index(leaves[i]), c, "spender", ds[2 * i + 1]);
|
|
@@ -512,7 +513,7 @@ function extractDeliveryCandidates(logs, context, confirmedThrough) {
|
|
|
512
513
|
}
|
|
513
514
|
async function decryptCandidates(logs, options) {
|
|
514
515
|
contextCheck(options.context);
|
|
515
|
-
(0,
|
|
516
|
+
(0, import_core2.decodeAddress)(import_core2.ReceivingAddress.fromIdentity(options.viewing.walletIdentity).toString());
|
|
516
517
|
check2(
|
|
517
518
|
!logs.some(
|
|
518
519
|
(log) => log.address.toLowerCase() === options.context.protocolAddress.toLowerCase() && log.removed
|
|
@@ -854,7 +855,7 @@ function walletBalances(state) {
|
|
|
854
855
|
}
|
|
855
856
|
|
|
856
857
|
// src/session/store.ts
|
|
857
|
-
var
|
|
858
|
+
var import_core3 = require("@invisi-labs/core");
|
|
858
859
|
var import_sha256 = require("@noble/hashes/sha256");
|
|
859
860
|
var import_utils = require("@noble/hashes/utils");
|
|
860
861
|
function scopeKey(scope) {
|
|
@@ -870,7 +871,7 @@ function scopeKey(scope) {
|
|
|
870
871
|
return (0, import_utils.bytesToHex)(
|
|
871
872
|
(0, import_sha256.sha256)(
|
|
872
873
|
(0, import_utils.utf8ToBytes)(
|
|
873
|
-
"Invisi.WalletScope.
|
|
874
|
+
"Invisi.WalletScope.v1:" + JSON.stringify([
|
|
874
875
|
scope.namespace,
|
|
875
876
|
scope.chainId.toString(),
|
|
876
877
|
scope.protocolAddress.toLowerCase(),
|
|
@@ -907,12 +908,12 @@ function validateState(state, scope, revision) {
|
|
|
907
908
|
throw new Error("invalid spent state");
|
|
908
909
|
}
|
|
909
910
|
for (const record of Object.values(state.credits)) {
|
|
910
|
-
const note = (0,
|
|
911
|
+
const note = (0, import_core3.normalizeCreditDeliveryState)(record.state).note;
|
|
911
912
|
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))
|
|
912
913
|
throw new Error("invalid stored Credit context or role");
|
|
913
914
|
}
|
|
914
915
|
for (const record of [...Object.values(state.notes), ...Object.values(state.credits)]) {
|
|
915
|
-
if (!/^0x[0-9a-fA-F]{64}$/.test(record.commitment) || BigInt(record.commitment) <= 0n || BigInt(record.commitment) >=
|
|
916
|
+
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))
|
|
916
917
|
throw new Error("invalid stored receipt position");
|
|
917
918
|
}
|
|
918
919
|
for (const [id, operation] of Object.entries(state.reservations))
|
|
@@ -973,14 +974,11 @@ var TransactionalWalletStore = class {
|
|
|
973
974
|
};
|
|
974
975
|
|
|
975
976
|
// src/session/operation-protocol.ts
|
|
976
|
-
var
|
|
977
|
+
var import_core4 = require("@invisi-labs/core");
|
|
977
978
|
var import_protocol2 = require("@invisi-labs/protocol");
|
|
978
979
|
function requiresAuthorization(plan) {
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
throw new Error("invalid owner-only sweep plan");
|
|
982
|
-
return false;
|
|
983
|
-
}
|
|
980
|
+
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);
|
|
981
|
+
if (ownerOnlyFullWithdrawal) return false;
|
|
984
982
|
return !!plan.credit || plan.inputs.length > 0;
|
|
985
983
|
}
|
|
986
984
|
function inputFacts(plan) {
|
|
@@ -1244,7 +1242,7 @@ function operationExpired(plan, now) {
|
|
|
1244
1242
|
if (credit?.creation && now >= credit.creation.expiry) return true;
|
|
1245
1243
|
if (credit?.draw) {
|
|
1246
1244
|
if (credit.draw.expiries.some((expiry) => now >= expiry)) return true;
|
|
1247
|
-
const prepared = (0,
|
|
1245
|
+
const prepared = (0, import_core4.creditPeriods)(plan.preparedAt.timestamp), current2 = (0, import_core4.creditPeriods)(now);
|
|
1248
1246
|
if (prepared.currentDay !== current2.currentDay || prepared.currentMonth !== current2.currentMonth)
|
|
1249
1247
|
return true;
|
|
1250
1248
|
}
|
|
@@ -2743,7 +2741,8 @@ function estimateGasReference(plan, protocol, profiles) {
|
|
|
2743
2741
|
|
|
2744
2742
|
// src/session/operation-execution.ts
|
|
2745
2743
|
var import_ethers6 = require("ethers");
|
|
2746
|
-
var
|
|
2744
|
+
var import_core5 = require("@invisi-labs/core");
|
|
2745
|
+
var import_protocol3 = require("@invisi-labs/protocol");
|
|
2747
2746
|
|
|
2748
2747
|
// src/session/proof-integrity.ts
|
|
2749
2748
|
var import_ethers5 = require("ethers");
|
|
@@ -2772,23 +2771,41 @@ async function authorizeOperation(plan, provider) {
|
|
|
2772
2771
|
if (provider.walletIdentity !== plan.scope.walletIdentity)
|
|
2773
2772
|
throw new Error("wallet authorization capability mismatch");
|
|
2774
2773
|
const request = authorizationRequest(plan);
|
|
2775
|
-
const digest = (0,
|
|
2774
|
+
const digest = (0, import_core5.computeWalletAuthorizationDigest)(request);
|
|
2776
2775
|
const signed = await provider.authorize(structuredClone(request));
|
|
2777
2776
|
if (signed.walletIdentity !== plan.scope.walletIdentity || signed.digest !== digest)
|
|
2778
2777
|
throw new Error("wallet authorization statement mismatch");
|
|
2779
2778
|
return signed;
|
|
2780
2779
|
}
|
|
2781
2780
|
async function proveOperation(protocol, plan, authorization, prover, options) {
|
|
2781
|
+
if (Object.hasOwn(options, "target")) {
|
|
2782
|
+
throw new Error("prove accepts execution, not a backend target");
|
|
2783
|
+
}
|
|
2784
|
+
const { execution = "single", ...proverOptions } = options;
|
|
2785
|
+
if (execution !== "single" && execution !== "aggregate") {
|
|
2786
|
+
throw new Error("execution must be single or aggregate");
|
|
2787
|
+
}
|
|
2788
|
+
const frozen = statement(plan);
|
|
2789
|
+
if (execution === "aggregate" && (plan.kind !== "transfer" || plan.capacity !== 14)) {
|
|
2790
|
+
throw new Error("aggregate execution requires a 14-slot transfer");
|
|
2791
|
+
}
|
|
2792
|
+
if (plan.kind === "transfer") {
|
|
2793
|
+
plan.statement = import_protocol3.WitnessBuilder.statement(
|
|
2794
|
+
frozen.circuitName,
|
|
2795
|
+
frozen.expectedPublicInputs,
|
|
2796
|
+
execution === "aggregate" ? "noir-recursive" : "evm"
|
|
2797
|
+
);
|
|
2798
|
+
}
|
|
2782
2799
|
const request = await witnessFor(protocol, plan, authorization);
|
|
2783
2800
|
if (JSON.stringify({ ...request, witness: void 0 }) !== JSON.stringify(plan.statement))
|
|
2784
2801
|
throw new Error("prepared proof statement changed");
|
|
2785
2802
|
try {
|
|
2786
|
-
const proof = publicProof(await prover.prove(request,
|
|
2803
|
+
const proof = publicProof(await prover.prove(request, proverOptions));
|
|
2787
2804
|
sameStatement(proof, plan.statement);
|
|
2788
2805
|
if (await prover.verify(
|
|
2789
2806
|
structuredClone(proof),
|
|
2790
2807
|
structuredClone(plan.statement),
|
|
2791
|
-
|
|
2808
|
+
proverOptions
|
|
2792
2809
|
) !== true)
|
|
2793
2810
|
throw new Error("proof verification failed");
|
|
2794
2811
|
return proof;
|
|
@@ -2803,19 +2820,88 @@ async function signOperationFunding(saved, signer, verifier) {
|
|
|
2803
2820
|
throw new Error("funding signer is not the payer");
|
|
2804
2821
|
const typed = await fundingTypedData(saved);
|
|
2805
2822
|
const signature = await signer.signTypedData(structuredClone(typed));
|
|
2823
|
+
return verifyFundingSignature(typed, payer, signature, verifier);
|
|
2824
|
+
}
|
|
2825
|
+
function walletSigningRequest(plan) {
|
|
2826
|
+
return (0, import_core5.createSigningRequest)((0, import_core5.walletAuthorizationTypedData)(authorizationRequest(plan)));
|
|
2827
|
+
}
|
|
2828
|
+
async function acceptWalletSignature(plan, signature) {
|
|
2829
|
+
const request = authorizationRequest(plan);
|
|
2830
|
+
const digest = (0, import_core5.computeWalletAuthorizationDigest)(request);
|
|
2831
|
+
try {
|
|
2832
|
+
const signed = import_ethers6.Signature.from(signature);
|
|
2833
|
+
const publicKey = (0, import_ethers6.getBytes)(import_ethers6.SigningKey.recoverPublicKey(digest, signed));
|
|
2834
|
+
const owner = await (0, import_core5.computeWalletIdentity)(publicKey);
|
|
2835
|
+
if (owner.toLowerCase() !== plan.scope.walletIdentity.toLowerCase()) {
|
|
2836
|
+
throw new Error("wallet signature identity mismatch");
|
|
2837
|
+
}
|
|
2838
|
+
return new import_core5.AuthorizedStatement(plan.scope.walletIdentity, digest, {
|
|
2839
|
+
public_key_x: Array.from(publicKey.slice(1, 33)),
|
|
2840
|
+
public_key_y: Array.from(publicKey.slice(33)),
|
|
2841
|
+
signature: Array.from((0, import_ethers6.getBytes)((0, import_ethers6.concat)([signed.r, signed.s])))
|
|
2842
|
+
});
|
|
2843
|
+
} catch {
|
|
2844
|
+
throw new Error("invalid wallet authorization signature");
|
|
2845
|
+
}
|
|
2846
|
+
}
|
|
2847
|
+
async function fundingSigningRequest(saved, runtime) {
|
|
2848
|
+
const plan = saved.plan;
|
|
2849
|
+
if (plan.fundingMode !== "signature") {
|
|
2850
|
+
throw new Error("operation does not require a funding signature");
|
|
2851
|
+
}
|
|
2852
|
+
if (plan.kind !== "shield" && !saved.proof) {
|
|
2853
|
+
throw new Error("proof must precede funding authorization");
|
|
2854
|
+
}
|
|
2855
|
+
await runtime.options.protocol.validateDeployment(runtime.reader);
|
|
2856
|
+
const block = await runtime.reader.getBlock(await runtime.reader.getLatestBlockNumber());
|
|
2857
|
+
if (block.timestamp > plan.deadline || block.timestamp > (plan.funding.deadline ?? 0n)) {
|
|
2858
|
+
throw new Error("funding authorization expired");
|
|
2859
|
+
}
|
|
2860
|
+
if (await runtime.reader.getFundingNonce(plan.context, plan.funding.payer, block) !== plan.funding.sigNonce) {
|
|
2861
|
+
throw new Error("funding nonce changed; prepare again");
|
|
2862
|
+
}
|
|
2863
|
+
const request = (0, import_core5.createSigningRequest)(await fundingTypedData(saved));
|
|
2864
|
+
if ((await runtime.reader.getBlock(block.number)).hash !== block.hash) {
|
|
2865
|
+
throw new Error("canonical funding block changed");
|
|
2866
|
+
}
|
|
2867
|
+
return request;
|
|
2868
|
+
}
|
|
2869
|
+
async function acceptFundingSignature(saved, signature, runtime) {
|
|
2870
|
+
if (!/^0x(?:[a-fA-F0-9]{2})+$/.test(signature)) {
|
|
2871
|
+
throw new Error("invalid EVM funding signature");
|
|
2872
|
+
}
|
|
2873
|
+
const request = await fundingSigningRequest(saved, runtime);
|
|
2874
|
+
return verifyFundingSignature(
|
|
2875
|
+
request,
|
|
2876
|
+
saved.plan.funding.payer,
|
|
2877
|
+
signature,
|
|
2878
|
+
runtime.options.fundingVerifier
|
|
2879
|
+
);
|
|
2880
|
+
}
|
|
2881
|
+
async function verifyFundingSignature(request, payer, signature, verifier) {
|
|
2882
|
+
if (!/^0x(?:[a-fA-F0-9]{2})+$/.test(signature)) {
|
|
2883
|
+
throw new Error("invalid EVM funding signature");
|
|
2884
|
+
}
|
|
2885
|
+
const types = Object.fromEntries(
|
|
2886
|
+
Object.entries(request.types).map(([name, fields]) => [name, [...fields]])
|
|
2887
|
+
);
|
|
2888
|
+
let validEOA = false;
|
|
2889
|
+
try {
|
|
2890
|
+
validEOA = (0, import_ethers6.verifyTypedData)(request.domain, types, request.message, signature).toLowerCase() === payer.toLowerCase();
|
|
2891
|
+
if (validEOA) {
|
|
2892
|
+
signature = import_ethers6.Signature.from(signature).serialized;
|
|
2893
|
+
}
|
|
2894
|
+
} catch {
|
|
2895
|
+
validEOA = false;
|
|
2896
|
+
}
|
|
2806
2897
|
const valid = verifier ? await verifier.verifyTypedData({
|
|
2807
|
-
...structuredClone(
|
|
2898
|
+
...structuredClone(request),
|
|
2808
2899
|
address: payer,
|
|
2809
2900
|
signature
|
|
2810
|
-
}) :
|
|
2811
|
-
|
|
2812
|
-
|
|
2813
|
-
|
|
2814
|
-
),
|
|
2815
|
-
typed.message,
|
|
2816
|
-
signature
|
|
2817
|
-
).toLowerCase() === payer.toLowerCase();
|
|
2818
|
-
if (valid !== true) throw new Error("invalid EVM funding signature");
|
|
2901
|
+
}) : validEOA;
|
|
2902
|
+
if (valid !== true) {
|
|
2903
|
+
throw new Error("invalid EVM funding signature");
|
|
2904
|
+
}
|
|
2819
2905
|
return signature;
|
|
2820
2906
|
}
|
|
2821
2907
|
|
|
@@ -2856,16 +2942,16 @@ function operationPreview(plan, status) {
|
|
|
2856
2942
|
}
|
|
2857
2943
|
|
|
2858
2944
|
// src/session/Operation.ts
|
|
2859
|
-
var
|
|
2860
|
-
var
|
|
2945
|
+
var import_core9 = require("@invisi-labs/core");
|
|
2946
|
+
var import_protocol5 = require("@invisi-labs/protocol");
|
|
2861
2947
|
|
|
2862
2948
|
// src/session/operation-confirmation.ts
|
|
2863
|
-
var
|
|
2949
|
+
var import_core8 = require("@invisi-labs/core");
|
|
2864
2950
|
|
|
2865
2951
|
// src/session/aggregate-receipt.ts
|
|
2866
2952
|
var import_ethers7 = require("ethers");
|
|
2867
2953
|
var import_contracts3 = require("@invisi-labs/protocol/contracts");
|
|
2868
|
-
var
|
|
2954
|
+
var import_protocol4 = require("@invisi-labs/protocol");
|
|
2869
2955
|
var aggregateAbi = import_contracts3.invisiProtocolAbi.filter(
|
|
2870
2956
|
(item) => item.type === "function" && item.name === "transact" && item.inputs.length === 2 && item.inputs[1]?.type === "tuple[]"
|
|
2871
2957
|
);
|
|
@@ -2893,7 +2979,7 @@ function matchesAggregateTransaction(saved, transaction) {
|
|
|
2893
2979
|
});
|
|
2894
2980
|
if (decoded.name !== "transact" || !Array.isArray(requests) || ![4, 8, 12].includes(requests.length))
|
|
2895
2981
|
return false;
|
|
2896
|
-
const canonical2 = (request) => (0,
|
|
2982
|
+
const canonical2 = (request) => (0, import_protocol4.buildTransactCalldata)({
|
|
2897
2983
|
context: { ...plan.context, tokenAddress: request.params.token },
|
|
2898
2984
|
proof: "0x00",
|
|
2899
2985
|
params: request.params,
|
|
@@ -2901,7 +2987,7 @@ function matchesAggregateTransaction(saved, transaction) {
|
|
|
2901
2987
|
funding: request.funding,
|
|
2902
2988
|
caller: transaction.from
|
|
2903
2989
|
}).data.toLowerCase();
|
|
2904
|
-
const expected = (0,
|
|
2990
|
+
const expected = (0, import_protocol4.buildTransactCalldata)({
|
|
2905
2991
|
context: plan.context,
|
|
2906
2992
|
proof: "0x00",
|
|
2907
2993
|
params: plan.params,
|
|
@@ -2916,10 +3002,10 @@ function matchesAggregateTransaction(saved, transaction) {
|
|
|
2916
3002
|
}
|
|
2917
3003
|
|
|
2918
3004
|
// src/session/credit-values.ts
|
|
2919
|
-
var
|
|
3005
|
+
var import_core6 = require("@invisi-labs/core");
|
|
2920
3006
|
async function creditLeaf(state) {
|
|
2921
3007
|
const n = state.note;
|
|
2922
|
-
return (0,
|
|
3008
|
+
return (0, import_core6.computeCreditCommitment)(
|
|
2923
3009
|
n.chainId,
|
|
2924
3010
|
n.spenderWalletIdentity,
|
|
2925
3011
|
n.ownerWalletIdentity,
|
|
@@ -2929,23 +3015,23 @@ async function creditLeaf(state) {
|
|
|
2929
3015
|
n.amount,
|
|
2930
3016
|
n.nonce,
|
|
2931
3017
|
n.expiry,
|
|
2932
|
-
await (0,
|
|
3018
|
+
await (0, import_core6.computeCreditSharedKeyHash)(n.creditSharedKey),
|
|
2933
3019
|
n
|
|
2934
3020
|
);
|
|
2935
3021
|
}
|
|
2936
3022
|
|
|
2937
3023
|
// src/session/membership.ts
|
|
2938
|
-
var
|
|
3024
|
+
var import_core7 = require("@invisi-labs/core");
|
|
2939
3025
|
async function verifyMembership(reader, context, block, input, proof) {
|
|
2940
3026
|
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))
|
|
2941
3027
|
throw new Error("membership context/root mismatch");
|
|
2942
3028
|
let root = BigInt(input.commitment);
|
|
2943
3029
|
for (let i = 0; i < 16; i++) {
|
|
2944
3030
|
const sibling = proof.path[i];
|
|
2945
|
-
if (typeof sibling !== "bigint" || sibling < 0n || sibling >=
|
|
3031
|
+
if (typeof sibling !== "bigint" || sibling < 0n || sibling >= import_core7.FIELD_MODULUS)
|
|
2946
3032
|
throw new Error("noncanonical membership path");
|
|
2947
3033
|
root = BigInt(
|
|
2948
|
-
await (0,
|
|
3034
|
+
await (0, import_core7.poseidonHash)(input.leafIndex >> i & 1 ? [sibling, root] : [root, sibling])
|
|
2949
3035
|
);
|
|
2950
3036
|
}
|
|
2951
3037
|
if (root !== BigInt(proof.root))
|
|
@@ -3117,7 +3203,7 @@ async function confirmOperation(runtime, id, generation, hash2, signal) {
|
|
|
3117
3203
|
if (credits.length) {
|
|
3118
3204
|
const nullifiers = await Promise.all(
|
|
3119
3205
|
credits.map(
|
|
3120
|
-
async (record) => (0,
|
|
3206
|
+
async (record) => (0, import_core8.computeCreditNullifier)(
|
|
3121
3207
|
plan.context.chainId,
|
|
3122
3208
|
record.state.note.creditSharedKey,
|
|
3123
3209
|
await creditLeaf(record.state)
|
|
@@ -3418,6 +3504,81 @@ var Operation = class _Operation {
|
|
|
3418
3504
|
throw error;
|
|
3419
3505
|
}
|
|
3420
3506
|
}
|
|
3507
|
+
async signingCurrent() {
|
|
3508
|
+
await this.#runtime.store.transaction(this.#runtime.scope, (state) => {
|
|
3509
|
+
check6(!state.invalidated, "wallet state requires canonical resync");
|
|
3510
|
+
this.current(state.operations[this.id]);
|
|
3511
|
+
});
|
|
3512
|
+
}
|
|
3513
|
+
/** Public EIP-712 request; signing transport and key custody belong to the caller. */
|
|
3514
|
+
async exportWalletAuthorization(options = {}) {
|
|
3515
|
+
options.signal?.throwIfAborted();
|
|
3516
|
+
await this.signingCurrent();
|
|
3517
|
+
check6(
|
|
3518
|
+
requiresAuthorization(this.#saved.plan),
|
|
3519
|
+
"operation does not require wallet authorization"
|
|
3520
|
+
);
|
|
3521
|
+
check6(
|
|
3522
|
+
["prepared", "authorized"].includes(this.status),
|
|
3523
|
+
"operation is not awaiting wallet authorization"
|
|
3524
|
+
);
|
|
3525
|
+
return walletSigningRequest(this.#saved.plan);
|
|
3526
|
+
}
|
|
3527
|
+
/** Attach an external EOA signature, retaining its witness only at the private proving boundary. */
|
|
3528
|
+
async withWalletSignature(signature, options = {}) {
|
|
3529
|
+
options.signal?.throwIfAborted();
|
|
3530
|
+
await this.signingCurrent();
|
|
3531
|
+
check6(
|
|
3532
|
+
requiresAuthorization(this.#saved.plan),
|
|
3533
|
+
"operation does not require wallet authorization"
|
|
3534
|
+
);
|
|
3535
|
+
check6(
|
|
3536
|
+
["prepared", "authorized"].includes(this.status),
|
|
3537
|
+
"operation is not awaiting wallet authorization"
|
|
3538
|
+
);
|
|
3539
|
+
const authorization = await acceptWalletSignature(this.#saved.plan, signature);
|
|
3540
|
+
return this.stage(
|
|
3541
|
+
"wallet authorization",
|
|
3542
|
+
["prepared", "authorized"],
|
|
3543
|
+
async () => ({ status: "authorized", authorization }),
|
|
3544
|
+
options.signal
|
|
3545
|
+
);
|
|
3546
|
+
}
|
|
3547
|
+
/** Export only after proving when the funding authorization binds an actual proof hash. */
|
|
3548
|
+
async exportFundingAuthorization(options = {}) {
|
|
3549
|
+
options.signal?.throwIfAborted();
|
|
3550
|
+
await this.signingCurrent();
|
|
3551
|
+
check6(
|
|
3552
|
+
this.status === (this.kind === "shield" ? "prepared" : "proved"),
|
|
3553
|
+
"operation is not awaiting funding authorization or proof"
|
|
3554
|
+
);
|
|
3555
|
+
const request = await fundingSigningRequest(this.#saved, this.#runtime);
|
|
3556
|
+
options.signal?.throwIfAborted();
|
|
3557
|
+
await this.signingCurrent();
|
|
3558
|
+
return request;
|
|
3559
|
+
}
|
|
3560
|
+
/** Verify an external signature against this handle's own frozen operation. */
|
|
3561
|
+
async withFundingSignature(signature, options = {}) {
|
|
3562
|
+
options.signal?.throwIfAborted();
|
|
3563
|
+
await this.signingCurrent();
|
|
3564
|
+
check6(
|
|
3565
|
+
this.status === (this.kind === "shield" ? "prepared" : "proved"),
|
|
3566
|
+
"operation is not awaiting funding authorization or proof"
|
|
3567
|
+
);
|
|
3568
|
+
const verified = await acceptFundingSignature(this.#saved, signature, this.#runtime);
|
|
3569
|
+
return this.stage(
|
|
3570
|
+
"funding authorization",
|
|
3571
|
+
this.kind === "shield" ? ["prepared"] : ["proved"],
|
|
3572
|
+
async (saved) => {
|
|
3573
|
+
saved.plan.funding.signature = verified;
|
|
3574
|
+
if (saved.plan.statement?.target !== "noir-recursive") {
|
|
3575
|
+
saved.call = await this.encode(saved);
|
|
3576
|
+
}
|
|
3577
|
+
return { status: "ready" };
|
|
3578
|
+
},
|
|
3579
|
+
options.signal
|
|
3580
|
+
);
|
|
3581
|
+
}
|
|
3421
3582
|
async authorize(options = {}) {
|
|
3422
3583
|
check6(
|
|
3423
3584
|
requiresAuthorization(this.#saved.plan),
|
|
@@ -3438,7 +3599,8 @@ var Operation = class _Operation {
|
|
|
3438
3599
|
options.signal
|
|
3439
3600
|
);
|
|
3440
3601
|
}
|
|
3441
|
-
async prove(
|
|
3602
|
+
async prove(suppliedOptions = {}) {
|
|
3603
|
+
const options = { ...suppliedOptions };
|
|
3442
3604
|
const plan = this.#saved.plan;
|
|
3443
3605
|
check6(plan.kind !== "shield", "shield requires no proof");
|
|
3444
3606
|
if (requiresAuthorization(plan)) {
|
|
@@ -3449,7 +3611,7 @@ var Operation = class _Operation {
|
|
|
3449
3611
|
}
|
|
3450
3612
|
const prover = this.#runtime.options.prover;
|
|
3451
3613
|
if (!prover) {
|
|
3452
|
-
throw new
|
|
3614
|
+
throw new import_core9.MissingCapabilityError("prover");
|
|
3453
3615
|
}
|
|
3454
3616
|
return this.stage(
|
|
3455
3617
|
"proof",
|
|
@@ -3483,7 +3645,7 @@ var Operation = class _Operation {
|
|
|
3483
3645
|
check6(p.kind === "shield" || this.#saved.proof, "proof must precede funding authorization");
|
|
3484
3646
|
const signer = this.#runtime.options.fundingSigner;
|
|
3485
3647
|
if (!signer) {
|
|
3486
|
-
throw new
|
|
3648
|
+
throw new import_core9.MissingCapabilityError("EVM funding signer");
|
|
3487
3649
|
}
|
|
3488
3650
|
check6(
|
|
3489
3651
|
signer.address.toLowerCase() === p.funding.payer.toLowerCase(),
|
|
@@ -3585,7 +3747,7 @@ var Operation = class _Operation {
|
|
|
3585
3747
|
request: {
|
|
3586
3748
|
params: structuredClone(plan.params),
|
|
3587
3749
|
deliveries: plan.outputs.map((output) => structuredClone(output.delivery)),
|
|
3588
|
-
funding: (0,
|
|
3750
|
+
funding: (0, import_protocol5.normalizeFunding)(plan.funding)
|
|
3589
3751
|
}
|
|
3590
3752
|
};
|
|
3591
3753
|
});
|
|
@@ -3672,18 +3834,181 @@ var Operation = class _Operation {
|
|
|
3672
3834
|
}
|
|
3673
3835
|
};
|
|
3674
3836
|
|
|
3837
|
+
// src/session/WalletSession.ts
|
|
3838
|
+
var import_core16 = require("@invisi-labs/core");
|
|
3839
|
+
|
|
3840
|
+
// src/session/history.ts
|
|
3841
|
+
function walletHistory(state, options = {}) {
|
|
3842
|
+
const token = options.tokenAddress?.toLowerCase();
|
|
3843
|
+
const outgoing = [];
|
|
3844
|
+
for (const record of Object.values(state.operations)) {
|
|
3845
|
+
const saved = record.payload;
|
|
3846
|
+
if (!saved?.plan || token && saved.plan.context.tokenAddress.toLowerCase() !== token) {
|
|
3847
|
+
continue;
|
|
3848
|
+
}
|
|
3849
|
+
const plan = saved.plan;
|
|
3850
|
+
outgoing.push({
|
|
3851
|
+
...operationPreview(plan, record.status),
|
|
3852
|
+
preparedAt: { ...plan.preparedAt },
|
|
3853
|
+
...record.transactionHash ? { transactionHash: record.transactionHash } : {},
|
|
3854
|
+
outputs: plan.outputs.map((output) => ({
|
|
3855
|
+
to: output.address,
|
|
3856
|
+
amount: output.note.amount,
|
|
3857
|
+
change: output.change,
|
|
3858
|
+
commitment: output.commitment,
|
|
3859
|
+
payload: Uint8Array.from(output.note.payload ?? [])
|
|
3860
|
+
}))
|
|
3861
|
+
});
|
|
3862
|
+
}
|
|
3863
|
+
outgoing.sort(
|
|
3864
|
+
(a, b) => a.preparedAt.number === b.preparedAt.number ? a.id.localeCompare(b.id) : a.preparedAt.number > b.preparedAt.number ? -1 : 1
|
|
3865
|
+
);
|
|
3866
|
+
const incoming = [];
|
|
3867
|
+
for (const record of Object.values(state.notes)) {
|
|
3868
|
+
const tokenAddress = `0x${record.note.tokenAddress.toString(16).padStart(40, "0")}`;
|
|
3869
|
+
if (token && tokenAddress !== token) continue;
|
|
3870
|
+
incoming.push({
|
|
3871
|
+
commitment: record.commitment,
|
|
3872
|
+
treeId: record.treeId,
|
|
3873
|
+
leafIndex: record.leafIndex,
|
|
3874
|
+
blockNumber: record.blockNumber,
|
|
3875
|
+
blockHash: record.blockHash,
|
|
3876
|
+
...record.transactionHash ? { transactionHash: record.transactionHash } : {},
|
|
3877
|
+
tokenAddress,
|
|
3878
|
+
amount: record.note.amount,
|
|
3879
|
+
payload: Uint8Array.from(record.note.payload ?? []),
|
|
3880
|
+
spent: record.spent
|
|
3881
|
+
});
|
|
3882
|
+
}
|
|
3883
|
+
incoming.sort(
|
|
3884
|
+
(a, b) => a.blockNumber === b.blockNumber ? a.commitment.localeCompare(b.commitment) : a.blockNumber > b.blockNumber ? -1 : 1
|
|
3885
|
+
);
|
|
3886
|
+
return structuredClone({
|
|
3887
|
+
revision: state.revision,
|
|
3888
|
+
checkedThrough: state.discoveryThrough ?? null,
|
|
3889
|
+
invalidated: !!state.invalidated,
|
|
3890
|
+
outgoing,
|
|
3891
|
+
incoming
|
|
3892
|
+
});
|
|
3893
|
+
}
|
|
3894
|
+
|
|
3895
|
+
// src/errors.ts
|
|
3896
|
+
var import_core10 = require("@invisi-labs/core");
|
|
3897
|
+
var InvisiError = class extends Error {
|
|
3898
|
+
code;
|
|
3899
|
+
operation;
|
|
3900
|
+
address;
|
|
3901
|
+
outputIndex;
|
|
3902
|
+
field;
|
|
3903
|
+
capability;
|
|
3904
|
+
constructor(code, message, details = {}) {
|
|
3905
|
+
super(message, { cause: details.cause });
|
|
3906
|
+
this.name = "InvisiError";
|
|
3907
|
+
this.code = code;
|
|
3908
|
+
this.operation = details.operation;
|
|
3909
|
+
this.address = details.address;
|
|
3910
|
+
this.outputIndex = details.outputIndex;
|
|
3911
|
+
this.field = details.field;
|
|
3912
|
+
this.capability = details.capability;
|
|
3913
|
+
}
|
|
3914
|
+
};
|
|
3915
|
+
async function prepareWithErrors(operation, prepare) {
|
|
3916
|
+
try {
|
|
3917
|
+
return await prepare();
|
|
3918
|
+
} catch (cause) {
|
|
3919
|
+
if (cause instanceof InvisiError) {
|
|
3920
|
+
if (cause.operation) throw cause;
|
|
3921
|
+
throw new InvisiError(cause.code, cause.message, {
|
|
3922
|
+
operation,
|
|
3923
|
+
...cause.address === void 0 ? {} : { address: cause.address },
|
|
3924
|
+
...cause.outputIndex === void 0 ? {} : { outputIndex: cause.outputIndex },
|
|
3925
|
+
...cause.field === void 0 ? {} : { field: cause.field },
|
|
3926
|
+
...cause.capability === void 0 ? {} : { capability: cause.capability },
|
|
3927
|
+
cause: cause.cause
|
|
3928
|
+
});
|
|
3929
|
+
}
|
|
3930
|
+
throw new InvisiError(
|
|
3931
|
+
cause instanceof import_core10.MissingCapabilityError ? "MISSING_CAPABILITY" : "PREPARATION_FAILED",
|
|
3932
|
+
cause instanceof Error ? cause.message : "Transaction preparation failed",
|
|
3933
|
+
{
|
|
3934
|
+
operation,
|
|
3935
|
+
cause,
|
|
3936
|
+
...cause instanceof import_core10.MissingCapabilityError ? { capability: cause.capability } : {}
|
|
3937
|
+
}
|
|
3938
|
+
);
|
|
3939
|
+
}
|
|
3940
|
+
}
|
|
3941
|
+
|
|
3675
3942
|
// src/session/WalletSession.ts
|
|
3676
3943
|
var import_ethers11 = require("ethers");
|
|
3677
|
-
var
|
|
3944
|
+
var import_core17 = require("@invisi-labs/core");
|
|
3678
3945
|
|
|
3679
3946
|
// src/session/transaction-preparation.ts
|
|
3680
3947
|
var import_ethers10 = require("ethers");
|
|
3681
|
-
var
|
|
3948
|
+
var import_protocol8 = require("@invisi-labs/protocol");
|
|
3949
|
+
|
|
3950
|
+
// src/resolve-recipient.ts
|
|
3951
|
+
var import_core11 = require("@invisi-labs/core");
|
|
3952
|
+
async function resolveRecipient(address2, lookup, details = {}) {
|
|
3953
|
+
const context = { ...details, address: address2 };
|
|
3954
|
+
let recipient;
|
|
3955
|
+
try {
|
|
3956
|
+
recipient = import_core11.ReceivingAddress.parse(address2);
|
|
3957
|
+
} catch (cause) {
|
|
3958
|
+
throw new InvisiError("INVALID_RECIPIENT", "Invalid recipient address", {
|
|
3959
|
+
...context,
|
|
3960
|
+
cause
|
|
3961
|
+
});
|
|
3962
|
+
}
|
|
3963
|
+
if (recipient.kind === "long") return recipient;
|
|
3964
|
+
if (!lookup) {
|
|
3965
|
+
throw new InvisiError("MISSING_CAPABILITY", "Missing registry lookup capability", {
|
|
3966
|
+
...context,
|
|
3967
|
+
capability: "registry lookup"
|
|
3968
|
+
});
|
|
3969
|
+
}
|
|
3970
|
+
let keys;
|
|
3971
|
+
try {
|
|
3972
|
+
keys = await lookup(recipient.walletIdentity);
|
|
3973
|
+
} catch (cause) {
|
|
3974
|
+
if (cause instanceof import_core11.MissingCapabilityError) {
|
|
3975
|
+
throw new InvisiError("MISSING_CAPABILITY", cause.message, {
|
|
3976
|
+
...context,
|
|
3977
|
+
cause,
|
|
3978
|
+
capability: cause.capability
|
|
3979
|
+
});
|
|
3980
|
+
}
|
|
3981
|
+
throw new InvisiError("RECIPIENT_LOOKUP_FAILED", "Recipient registry lookup failed", {
|
|
3982
|
+
...context,
|
|
3983
|
+
cause
|
|
3984
|
+
});
|
|
3985
|
+
}
|
|
3986
|
+
if (keys == null) {
|
|
3987
|
+
throw new InvisiError(
|
|
3988
|
+
"EVM_ADDRESS_NOT_REGISTERED",
|
|
3989
|
+
"EVM address has no registered receiving keys",
|
|
3990
|
+
context
|
|
3991
|
+
);
|
|
3992
|
+
}
|
|
3993
|
+
try {
|
|
3994
|
+
return import_core11.ReceivingAddress.from({
|
|
3995
|
+
walletIdentity: recipient.walletIdentity,
|
|
3996
|
+
viewingPublicKey: keys.viewingPublicKey,
|
|
3997
|
+
clueKey: keys.clueKey
|
|
3998
|
+
});
|
|
3999
|
+
} catch (cause) {
|
|
4000
|
+
throw new InvisiError(
|
|
4001
|
+
"RECIPIENT_LOOKUP_FAILED",
|
|
4002
|
+
"Registry returned invalid receiving keys",
|
|
4003
|
+
{ ...context, cause }
|
|
4004
|
+
);
|
|
4005
|
+
}
|
|
4006
|
+
}
|
|
3682
4007
|
|
|
3683
4008
|
// src/session/prepare-common.ts
|
|
3684
4009
|
var import_ethers8 = require("ethers");
|
|
3685
|
-
var
|
|
3686
|
-
var
|
|
4010
|
+
var import_core12 = require("@invisi-labs/core");
|
|
4011
|
+
var import_protocol6 = require("@invisi-labs/protocol");
|
|
3687
4012
|
|
|
3688
4013
|
// src/session/selection.ts
|
|
3689
4014
|
function selectNotes(notes, target, reserved) {
|
|
@@ -3716,6 +4041,27 @@ function positiveAmount(value, bits = 128) {
|
|
|
3716
4041
|
}
|
|
3717
4042
|
return value;
|
|
3718
4043
|
}
|
|
4044
|
+
function copyApplicationPayload(value) {
|
|
4045
|
+
if (value === void 0) return new Uint8Array();
|
|
4046
|
+
if (!(value instanceof Uint8Array)) throw new TypeError("payload must be Uint8Array");
|
|
4047
|
+
return Uint8Array.from(value);
|
|
4048
|
+
}
|
|
4049
|
+
function positiveOutputAmount(payment, outputIndex) {
|
|
4050
|
+
try {
|
|
4051
|
+
return positiveAmount(payment.amount);
|
|
4052
|
+
} catch (cause) {
|
|
4053
|
+
throw new InvisiError(
|
|
4054
|
+
"PREPARATION_FAILED",
|
|
4055
|
+
cause instanceof Error ? cause.message : "Invalid output amount",
|
|
4056
|
+
{
|
|
4057
|
+
address: payment.to,
|
|
4058
|
+
field: "amount",
|
|
4059
|
+
...outputIndex === void 0 ? {} : { outputIndex },
|
|
4060
|
+
cause
|
|
4061
|
+
}
|
|
4062
|
+
);
|
|
4063
|
+
}
|
|
4064
|
+
}
|
|
3719
4065
|
var Preparation = class {
|
|
3720
4066
|
constructor(runtime) {
|
|
3721
4067
|
this.runtime = runtime;
|
|
@@ -3769,7 +4115,7 @@ var Preparation = class {
|
|
|
3769
4115
|
if (mode === "caller" && (source.sigNonce || source.deadline)) {
|
|
3770
4116
|
throw new Error("caller funding uses zero nonce/deadline");
|
|
3771
4117
|
}
|
|
3772
|
-
const funding = (0,
|
|
4118
|
+
const funding = (0, import_protocol6.normalizeFunding)({
|
|
3773
4119
|
payer,
|
|
3774
4120
|
amount: source.amount,
|
|
3775
4121
|
sigNonce: mode === "caller" ? 0n : source.sigNonce ?? await this.runtime.reader.getFundingNonce(
|
|
@@ -3785,52 +4131,70 @@ var Preparation = class {
|
|
|
3785
4131
|
}
|
|
3786
4132
|
return { funding, fundingMode: mode };
|
|
3787
4133
|
}
|
|
3788
|
-
async output(payment, context, change = false) {
|
|
3789
|
-
|
|
3790
|
-
|
|
3791
|
-
|
|
3792
|
-
|
|
3793
|
-
|
|
3794
|
-
|
|
3795
|
-
|
|
3796
|
-
|
|
3797
|
-
|
|
3798
|
-
|
|
3799
|
-
|
|
3800
|
-
|
|
3801
|
-
|
|
3802
|
-
|
|
3803
|
-
|
|
3804
|
-
|
|
3805
|
-
|
|
3806
|
-
|
|
3807
|
-
|
|
3808
|
-
|
|
3809
|
-
|
|
3810
|
-
|
|
3811
|
-
|
|
3812
|
-
|
|
3813
|
-
|
|
3814
|
-
|
|
3815
|
-
|
|
3816
|
-
|
|
3817
|
-
|
|
3818
|
-
|
|
3819
|
-
|
|
3820
|
-
|
|
3821
|
-
|
|
3822
|
-
|
|
3823
|
-
|
|
3824
|
-
|
|
3825
|
-
|
|
3826
|
-
|
|
3827
|
-
|
|
3828
|
-
|
|
4134
|
+
async output(payment, context, change = false, outputIndex) {
|
|
4135
|
+
try {
|
|
4136
|
+
positiveOutputAmount(payment, outputIndex);
|
|
4137
|
+
const recipient = await resolveRecipient(
|
|
4138
|
+
payment.to,
|
|
4139
|
+
this.runtime.options.addressResolver,
|
|
4140
|
+
{
|
|
4141
|
+
field: change ? "changeAddress" : "to",
|
|
4142
|
+
...outputIndex === void 0 ? {} : { outputIndex }
|
|
4143
|
+
}
|
|
4144
|
+
);
|
|
4145
|
+
const nonce = (0, import_core12.generateNonce)();
|
|
4146
|
+
const identity = await (0, import_core12.computeNoteIdentity)(BigInt(recipient.walletIdentity), nonce);
|
|
4147
|
+
const keys = recipient.getReceivingKeys();
|
|
4148
|
+
const note = {
|
|
4149
|
+
chainId: context.chainId,
|
|
4150
|
+
protocolAddress: BigInt(context.protocolAddress),
|
|
4151
|
+
tokenAddress: BigInt(context.tokenAddress),
|
|
4152
|
+
walletIdentity: recipient.walletIdentity,
|
|
4153
|
+
amount: payment.amount,
|
|
4154
|
+
payload: copyApplicationPayload(payment.payload),
|
|
4155
|
+
nonce
|
|
4156
|
+
};
|
|
4157
|
+
const commitment = await (0, import_core12.computeNoteCommitment)(
|
|
4158
|
+
context.chainId,
|
|
4159
|
+
identity,
|
|
4160
|
+
note.tokenAddress,
|
|
4161
|
+
note.amount
|
|
4162
|
+
);
|
|
4163
|
+
return {
|
|
4164
|
+
note,
|
|
4165
|
+
commitment,
|
|
4166
|
+
identity,
|
|
4167
|
+
address: payment.to,
|
|
4168
|
+
change,
|
|
4169
|
+
delivery: {
|
|
4170
|
+
ciphertext: (0, import_ethers8.hexlify)(
|
|
4171
|
+
await (0, import_core12.encryptPrivateTransferNote)(keys.viewingPublicKey, note, {
|
|
4172
|
+
chainId: context.chainId,
|
|
4173
|
+
protocolAddress: note.protocolAddress,
|
|
4174
|
+
outputCommitment: commitment
|
|
4175
|
+
})
|
|
4176
|
+
),
|
|
4177
|
+
clue: (0, import_ethers8.hexlify)((0, import_core12.createClue)(keys.clueKey))
|
|
4178
|
+
}
|
|
4179
|
+
};
|
|
4180
|
+
} catch (cause) {
|
|
4181
|
+
if (cause instanceof InvisiError) throw cause;
|
|
4182
|
+
throw new InvisiError(
|
|
4183
|
+
"PREPARATION_FAILED",
|
|
4184
|
+
cause instanceof Error ? cause.message : "Output preparation failed",
|
|
4185
|
+
{
|
|
4186
|
+
address: payment.to,
|
|
4187
|
+
field: change ? "changeAddress" : "to",
|
|
4188
|
+
...outputIndex === void 0 ? {} : { outputIndex },
|
|
4189
|
+
cause
|
|
4190
|
+
}
|
|
4191
|
+
);
|
|
4192
|
+
}
|
|
3829
4193
|
}
|
|
3830
4194
|
async membership(input, frame) {
|
|
3831
4195
|
const { options, reader } = this.runtime;
|
|
3832
4196
|
if (!options.protocolRpc)
|
|
3833
|
-
throw new
|
|
4197
|
+
throw new import_core12.MissingCapabilityError("Protocol RPC membership provider");
|
|
3834
4198
|
const root = this.#roots.get(input.treeId);
|
|
3835
4199
|
const proof = await options.protocolRpc.getMembership(
|
|
3836
4200
|
frame.context,
|
|
@@ -3877,9 +4241,9 @@ var Preparation = class {
|
|
|
3877
4241
|
for (const input of selected) {
|
|
3878
4242
|
const note = input.note;
|
|
3879
4243
|
const c = frame.context;
|
|
3880
|
-
if (note.walletIdentity !== scope.walletIdentity || note.chainId !== c.chainId || note.protocolAddress !== BigInt(c.protocolAddress) || note.tokenAddress !== BigInt(c.tokenAddress) || await (0,
|
|
4244
|
+
if (note.walletIdentity !== scope.walletIdentity || note.chainId !== c.chainId || note.protocolAddress !== BigInt(c.protocolAddress) || note.tokenAddress !== BigInt(c.tokenAddress) || await (0, import_core12.computeNoteCommitment)(
|
|
3881
4245
|
c.chainId,
|
|
3882
|
-
await (0,
|
|
4246
|
+
await (0, import_core12.computeNoteIdentity)(BigInt(note.walletIdentity), note.nonce),
|
|
3883
4247
|
note.tokenAddress,
|
|
3884
4248
|
note.amount
|
|
3885
4249
|
) !== input.commitment) {
|
|
@@ -3931,8 +4295,8 @@ function storedCandidates(inputs) {
|
|
|
3931
4295
|
|
|
3932
4296
|
// src/session/prepare-credit.ts
|
|
3933
4297
|
var import_ethers9 = require("ethers");
|
|
3934
|
-
var
|
|
3935
|
-
var
|
|
4298
|
+
var import_core13 = require("@invisi-labs/core");
|
|
4299
|
+
var import_protocol7 = require("@invisi-labs/protocol");
|
|
3936
4300
|
var EMPTY = { ciphertext: "0x", clue: "0x" };
|
|
3937
4301
|
var CreditPreparation = class {
|
|
3938
4302
|
constructor(runtime) {
|
|
@@ -3947,7 +4311,7 @@ var CreditPreparation = class {
|
|
|
3947
4311
|
return value;
|
|
3948
4312
|
}
|
|
3949
4313
|
async seal(input, frame) {
|
|
3950
|
-
const state = (0,
|
|
4314
|
+
const state = (0, import_core13.normalizeCreditDeliveryState)(input);
|
|
3951
4315
|
const commitment = await creditLeaf(state);
|
|
3952
4316
|
const context = {
|
|
3953
4317
|
chainId: frame.context.chainId,
|
|
@@ -3959,9 +4323,9 @@ var CreditPreparation = class {
|
|
|
3959
4323
|
const keys = role === "owner" ? state.ownerReceivingKeys : state.spenderReceivingKeys;
|
|
3960
4324
|
return {
|
|
3961
4325
|
ciphertext: (0, import_ethers9.hexlify)(
|
|
3962
|
-
await (0,
|
|
4326
|
+
await (0, import_core13.encryptCreditNote)(keys.viewingPublicKey, state, { ...context, role })
|
|
3963
4327
|
),
|
|
3964
|
-
clue: (0, import_ethers9.hexlify)((0,
|
|
4328
|
+
clue: (0, import_ethers9.hexlify)((0, import_core13.createClue)(keys.clueKey))
|
|
3965
4329
|
};
|
|
3966
4330
|
})
|
|
3967
4331
|
);
|
|
@@ -3972,14 +4336,14 @@ var CreditPreparation = class {
|
|
|
3972
4336
|
if (!stored || stored.spent !== "unspent" || stored.role !== role) {
|
|
3973
4337
|
throw new Error(`confirmed unspent ${role} Credit is unavailable`);
|
|
3974
4338
|
}
|
|
3975
|
-
const state = (0,
|
|
4339
|
+
const state = (0, import_core13.normalizeCreditDeliveryState)(stored.state);
|
|
3976
4340
|
const n = state.note;
|
|
3977
4341
|
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) {
|
|
3978
4342
|
throw new Error("Credit role, commitment or deployment mismatch");
|
|
3979
4343
|
}
|
|
3980
4344
|
const proof = await this.#prepare.membership(stored, frame);
|
|
3981
4345
|
const path = [...proof.path];
|
|
3982
|
-
const nullifier = await (0,
|
|
4346
|
+
const nullifier = await (0, import_core13.computeCreditNullifier)(n.chainId, n.creditSharedKey, commitment);
|
|
3983
4347
|
await this.#prepare.assertUnspent([nullifier], frame);
|
|
3984
4348
|
return {
|
|
3985
4349
|
input: {
|
|
@@ -4016,10 +4380,16 @@ var CreditPreparation = class {
|
|
|
4016
4380
|
positiveAmount(options.amount, 126);
|
|
4017
4381
|
const frame = await this.#prepare.frame(options.deadline);
|
|
4018
4382
|
const expiry = this.expiry(options.expiry, frame);
|
|
4019
|
-
const spender = await
|
|
4020
|
-
|
|
4383
|
+
const spender = await resolveRecipient(
|
|
4384
|
+
options.spender,
|
|
4385
|
+
this.runtime.options.addressResolver,
|
|
4386
|
+
{ field: "spender" }
|
|
4387
|
+
);
|
|
4388
|
+
const owner = await resolveRecipient(
|
|
4389
|
+
this.runtime.options.wallet.getReceivingAddress().toString(),
|
|
4390
|
+
this.runtime.options.addressResolver,
|
|
4391
|
+
{ field: "wallet" }
|
|
4021
4392
|
);
|
|
4022
|
-
const owner = await this.runtime.options.wallet.getReceivingAddress().resolve(this.runtime.options.addressResolver);
|
|
4023
4393
|
const ownerPayout = (0, import_ethers9.getAddress)(owner.walletIdentity);
|
|
4024
4394
|
const spenderPayout = (0, import_ethers9.getAddress)(spender.walletIdentity);
|
|
4025
4395
|
if (BigInt(ownerPayout) === 0n || BigInt(spenderPayout) === 0n) {
|
|
@@ -4035,16 +4405,16 @@ var CreditPreparation = class {
|
|
|
4035
4405
|
spenderWalletIdentity: spender.walletIdentity,
|
|
4036
4406
|
ownerPayout: BigInt(ownerPayout),
|
|
4037
4407
|
spenderPayout: BigInt(spenderPayout),
|
|
4038
|
-
...(0,
|
|
4408
|
+
...(0, import_core13.normalizeCreditPolicy)({
|
|
4039
4409
|
singleLimit: options.singleLimit ?? 0n,
|
|
4040
4410
|
dailyLimit: options.dailyLimit ?? 0n,
|
|
4041
4411
|
monthlyLimit: options.monthlyLimit ?? 0n
|
|
4042
4412
|
}),
|
|
4043
4413
|
amount: options.amount,
|
|
4044
|
-
|
|
4414
|
+
payload: copyApplicationPayload(options.payload),
|
|
4045
4415
|
expiry,
|
|
4046
|
-
nonce: (0,
|
|
4047
|
-
creditSharedKey: (0,
|
|
4416
|
+
nonce: (0, import_core13.generateNonce)(),
|
|
4417
|
+
creditSharedKey: (0, import_core13.generateCreditSharedKey)()
|
|
4048
4418
|
},
|
|
4049
4419
|
ownerReceivingKeys: owner.getReceivingKeys(),
|
|
4050
4420
|
spenderReceivingKeys: spender.getReceivingKeys()
|
|
@@ -4086,7 +4456,7 @@ var CreditPreparation = class {
|
|
|
4086
4456
|
sources: selected.sources,
|
|
4087
4457
|
nullifiers: selected.nullifiers,
|
|
4088
4458
|
creditCommitment: output.commitment,
|
|
4089
|
-
changeCommitment: plan.outputs[0]?.commitment ?? (0,
|
|
4459
|
+
changeCommitment: plan.outputs[0]?.commitment ?? (0, import_protocol7.word)(0n),
|
|
4090
4460
|
owner: (0, import_ethers9.getAddress)((0, import_ethers9.toBeHex)(output.state.note.ownerPayout, 20)),
|
|
4091
4461
|
expiry: output.state.note.expiry,
|
|
4092
4462
|
deadline: frame.deadline
|
|
@@ -4106,14 +4476,14 @@ var CreditPreparation = class {
|
|
|
4106
4476
|
creditNullifier: previous.nullifier,
|
|
4107
4477
|
previousExpiry: previous.input.state.note.expiry
|
|
4108
4478
|
};
|
|
4109
|
-
plan.statement =
|
|
4479
|
+
plan.statement = import_protocol7.WitnessBuilder.statement(
|
|
4110
4480
|
"credit_top_up",
|
|
4111
|
-
(0,
|
|
4481
|
+
(0, import_protocol7.topUpCreditPublicInputs)(frame.context, plan.credit.topUp, deliveries, plan.funding)
|
|
4112
4482
|
);
|
|
4113
4483
|
} else {
|
|
4114
|
-
plan.statement =
|
|
4484
|
+
plan.statement = import_protocol7.WitnessBuilder.statement(
|
|
4115
4485
|
"create_credit",
|
|
4116
|
-
(0,
|
|
4486
|
+
(0, import_protocol7.createCreditPublicInputs)(frame.context, creation, deliveries, plan.funding)
|
|
4117
4487
|
);
|
|
4118
4488
|
}
|
|
4119
4489
|
await this.#prepare.assertBlock(frame);
|
|
@@ -4142,8 +4512,10 @@ var CreditPreparation = class {
|
|
|
4142
4512
|
monthlyLimit: options.monthlyLimit ?? old.note.monthlyLimit ?? 0n,
|
|
4143
4513
|
amount,
|
|
4144
4514
|
expiry,
|
|
4145
|
-
nonce: (0,
|
|
4146
|
-
|
|
4515
|
+
nonce: (0, import_core13.generateNonce)(),
|
|
4516
|
+
payload: copyApplicationPayload(
|
|
4517
|
+
options.payload === void 0 ? old.note.payload : options.payload
|
|
4518
|
+
)
|
|
4147
4519
|
}
|
|
4148
4520
|
},
|
|
4149
4521
|
frame
|
|
@@ -4159,7 +4531,7 @@ var CreditPreparation = class {
|
|
|
4159
4531
|
}
|
|
4160
4532
|
async draw(options) {
|
|
4161
4533
|
const frame = await this.#prepare.frame(options.deadline);
|
|
4162
|
-
const profile = (0,
|
|
4534
|
+
const profile = (0, import_protocol7.selectCreditDrawCircuit)(options.credits.length);
|
|
4163
4535
|
if (new Set(options.credits.map((value) => value.credit)).size !== options.credits.length) {
|
|
4164
4536
|
throw new Error("duplicate Credit input");
|
|
4165
4537
|
}
|
|
@@ -4191,11 +4563,11 @@ var CreditPreparation = class {
|
|
|
4191
4563
|
}
|
|
4192
4564
|
sources.set(member.source.treeId, member.source.merkleRoot);
|
|
4193
4565
|
amounts.push(amount);
|
|
4194
|
-
const usage = (0,
|
|
4566
|
+
const usage = (0, import_core13.applyCreditDrawUsage)(n, amount, frame.block.timestamp);
|
|
4195
4567
|
const remaining = n.amount - amount;
|
|
4196
4568
|
if (remaining === 0n) {
|
|
4197
|
-
remainders.push((0,
|
|
4198
|
-
recoveryAmounts.push((0,
|
|
4569
|
+
remainders.push((0, import_protocol7.word)(0n));
|
|
4570
|
+
recoveryAmounts.push((0, import_protocol7.word)(0n));
|
|
4199
4571
|
deliveries.push({ ...EMPTY }, { ...EMPTY });
|
|
4200
4572
|
continue;
|
|
4201
4573
|
}
|
|
@@ -4213,8 +4585,10 @@ var CreditPreparation = class {
|
|
|
4213
4585
|
...n,
|
|
4214
4586
|
...usage,
|
|
4215
4587
|
amount: remaining,
|
|
4216
|
-
|
|
4217
|
-
|
|
4588
|
+
payload: copyApplicationPayload(
|
|
4589
|
+
options.credits[i].payload === void 0 ? n.payload : options.credits[i].payload
|
|
4590
|
+
),
|
|
4591
|
+
nonce: await (0, import_core13.deriveRemainderNonce)(...args)
|
|
4218
4592
|
}
|
|
4219
4593
|
},
|
|
4220
4594
|
frame
|
|
@@ -4222,7 +4596,7 @@ var CreditPreparation = class {
|
|
|
4222
4596
|
outputs.push(output);
|
|
4223
4597
|
remainders.push(output.commitment);
|
|
4224
4598
|
recoveryAmounts.push(
|
|
4225
|
-
(0,
|
|
4599
|
+
(0, import_protocol7.word)((remaining + await (0, import_core13.computeCreditRecoveryPad)(...args)) % import_core13.FIELD_MODULUS)
|
|
4226
4600
|
);
|
|
4227
4601
|
deliveries.push(...output.deliveries);
|
|
4228
4602
|
}
|
|
@@ -4246,9 +4620,9 @@ var CreditPreparation = class {
|
|
|
4246
4620
|
draw: params,
|
|
4247
4621
|
amounts
|
|
4248
4622
|
};
|
|
4249
|
-
plan.statement =
|
|
4623
|
+
plan.statement = import_protocol7.WitnessBuilder.statement(
|
|
4250
4624
|
profile.name,
|
|
4251
|
-
(0,
|
|
4625
|
+
(0, import_protocol7.creditDrawPublicInputs)(frame.context, params, deliveries)
|
|
4252
4626
|
);
|
|
4253
4627
|
await this.#prepare.assertBlock(frame);
|
|
4254
4628
|
return this.runtime.complete(plan);
|
|
@@ -4268,9 +4642,9 @@ var CreditPreparation = class {
|
|
|
4268
4642
|
};
|
|
4269
4643
|
plan.capacity = 1;
|
|
4270
4644
|
plan.credit = { inputs: [previous.input], outputs: [], deliveries: [], cancel: params };
|
|
4271
|
-
plan.statement =
|
|
4645
|
+
plan.statement = import_protocol7.WitnessBuilder.statement(
|
|
4272
4646
|
"credit_cancel",
|
|
4273
|
-
(0,
|
|
4647
|
+
(0, import_protocol7.cancelCreditPublicInputs)(frame.context, params)
|
|
4274
4648
|
);
|
|
4275
4649
|
await this.#prepare.assertBlock(frame);
|
|
4276
4650
|
return this.runtime.complete(plan);
|
|
@@ -4291,87 +4665,48 @@ var TransactionPreparation = class {
|
|
|
4291
4665
|
return this.context.scope;
|
|
4292
4666
|
}
|
|
4293
4667
|
async prepareTransfer(options) {
|
|
4668
|
+
if (Object.hasOwn(options, "target") || Object.hasOwn(options, "execution")) {
|
|
4669
|
+
throw new Error(
|
|
4670
|
+
"prepareTransfer accepts no target or execution; choose execution in prove()"
|
|
4671
|
+
);
|
|
4672
|
+
}
|
|
4294
4673
|
const copied = structuredClone(options);
|
|
4295
|
-
const
|
|
4674
|
+
const runtime = this.runtime(copied.tokenAddress);
|
|
4675
|
+
if (copied.commitments !== void 0 && copied.commitments.length === 0)
|
|
4676
|
+
throw new Error("empty explicit input selection");
|
|
4677
|
+
const preparation = new Preparation({
|
|
4678
|
+
...runtime,
|
|
4679
|
+
...copied.commitments !== void 0 ? { inputSelection: copied.commitments } : {}
|
|
4680
|
+
});
|
|
4296
4681
|
const frame = await preparation.frame(copied.deadline);
|
|
4297
|
-
if (copied.outputs.length
|
|
4298
|
-
|
|
4682
|
+
if (copied.outputs.length > 168) throw new Error("transfer supports at most 168 outputs");
|
|
4683
|
+
let withdrawalAddress = import_protocol8.ZERO_ADDRESS;
|
|
4684
|
+
let withdrawalAmount = 0n;
|
|
4685
|
+
if (copied.withdrawal !== void 0) {
|
|
4686
|
+
withdrawalAddress = (0, import_ethers10.getAddress)(copied.withdrawal.to);
|
|
4687
|
+
positiveAmount(copied.withdrawal.amount);
|
|
4688
|
+
if (BigInt(withdrawalAddress) === 0n)
|
|
4689
|
+
throw new Error("withdrawal destination must be nonzero");
|
|
4690
|
+
withdrawalAmount = copied.withdrawal.amount;
|
|
4299
4691
|
}
|
|
4692
|
+
if (copied.outputs.length === 0 && withdrawalAmount === 0n)
|
|
4693
|
+
throw new Error("transfer requires a private output or public withdrawal");
|
|
4300
4694
|
const total = copied.outputs.reduce(
|
|
4301
|
-
(sum, output) => sum +
|
|
4302
|
-
|
|
4695
|
+
(sum, output, index2) => sum + positiveOutputAmount(output, index2),
|
|
4696
|
+
withdrawalAmount
|
|
4303
4697
|
);
|
|
4304
4698
|
const funding = await preparation.funding(copied.funding, frame);
|
|
4305
4699
|
return this.funds(
|
|
4306
|
-
"transfer",
|
|
4307
4700
|
copied.outputs,
|
|
4308
4701
|
total,
|
|
4309
|
-
|
|
4310
|
-
|
|
4702
|
+
withdrawalAddress,
|
|
4703
|
+
withdrawalAmount,
|
|
4311
4704
|
frame,
|
|
4312
4705
|
funding,
|
|
4313
|
-
copied.target,
|
|
4314
|
-
preparation
|
|
4315
|
-
);
|
|
4316
|
-
}
|
|
4317
|
-
async prepareWithdraw(options) {
|
|
4318
|
-
const copied = structuredClone(options);
|
|
4319
|
-
const preparation = new Preparation(this.runtime(copied.tokenAddress));
|
|
4320
|
-
const frame = await preparation.frame(copied.deadline);
|
|
4321
|
-
const to = (0, import_ethers10.getAddress)(copied.to);
|
|
4322
|
-
positiveAmount(copied.amount);
|
|
4323
|
-
if (BigInt(to) === 0n) {
|
|
4324
|
-
throw new Error("withdrawal destination must be nonzero");
|
|
4325
|
-
}
|
|
4326
|
-
return this.funds(
|
|
4327
|
-
"withdraw",
|
|
4328
|
-
[],
|
|
4329
|
-
copied.amount,
|
|
4330
|
-
to,
|
|
4331
|
-
copied.amount,
|
|
4332
|
-
frame,
|
|
4333
|
-
{ funding: {}, fundingMode: "none" },
|
|
4334
|
-
copied.target,
|
|
4335
|
-
preparation
|
|
4336
|
-
);
|
|
4337
|
-
}
|
|
4338
|
-
/** Full payout to the note owner; no arbitrary destination, change or spending signature. */
|
|
4339
|
-
async prepareSweep(options) {
|
|
4340
|
-
const copied = structuredClone(options);
|
|
4341
|
-
const runtime = this.runtime(copied.tokenAddress);
|
|
4342
|
-
const state = await runtime.readInventory();
|
|
4343
|
-
const available = Object.values(state.notes).filter(
|
|
4344
|
-
(record) => record.note.tokenAddress === BigInt(copied.tokenAddress) && record.spent === "unspent" && !state.reservations[record.commitment]
|
|
4345
|
-
);
|
|
4346
|
-
const selected = copied.commitments ?? available.map((record) => record.commitment);
|
|
4347
|
-
if (selected.length === 0) throw new Error("empty sweep input selection");
|
|
4348
|
-
if (new Set(selected).size !== selected.length) throw new Error("duplicate sweep inputs");
|
|
4349
|
-
const byId = new Map(available.map((record) => [record.commitment, record]));
|
|
4350
|
-
let amount = 0n;
|
|
4351
|
-
for (const id of selected) {
|
|
4352
|
-
const record = byId.get(id);
|
|
4353
|
-
if (!record) throw new Error("sweep input unavailable");
|
|
4354
|
-
amount += record.note.amount;
|
|
4355
|
-
}
|
|
4356
|
-
positiveAmount(amount);
|
|
4357
|
-
const preparation = new Preparation({
|
|
4358
|
-
...runtime,
|
|
4359
|
-
inputSelection: selected
|
|
4360
|
-
});
|
|
4361
|
-
const frame = await preparation.frame(copied.deadline);
|
|
4362
|
-
return this.funds(
|
|
4363
|
-
"sweep",
|
|
4364
|
-
[],
|
|
4365
|
-
amount,
|
|
4366
|
-
this.scope.walletIdentity,
|
|
4367
|
-
amount,
|
|
4368
|
-
frame,
|
|
4369
|
-
{ funding: {}, fundingMode: "none" },
|
|
4370
|
-
copied.target,
|
|
4371
4706
|
preparation
|
|
4372
4707
|
);
|
|
4373
4708
|
}
|
|
4374
|
-
async funds(
|
|
4709
|
+
async funds(payments, total, withdrawalAddress, withdrawalAmount, frame, funding, preparation) {
|
|
4375
4710
|
if (total >= 1n << 135n) {
|
|
4376
4711
|
throw new Error("transaction sum exceeds uint135");
|
|
4377
4712
|
}
|
|
@@ -4381,7 +4716,7 @@ var TransactionPreparation = class {
|
|
|
4381
4716
|
frame
|
|
4382
4717
|
);
|
|
4383
4718
|
const outputs = await Promise.all(
|
|
4384
|
-
payments.map((payment) => preparation.output(payment, context))
|
|
4719
|
+
payments.map((payment, index2) => preparation.output(payment, context, false, index2))
|
|
4385
4720
|
);
|
|
4386
4721
|
const change = selection.amount + (funding.funding.amount ?? 0n) - total;
|
|
4387
4722
|
if (change > 0n) {
|
|
@@ -4406,17 +4741,17 @@ var TransactionPreparation = class {
|
|
|
4406
4741
|
deadline
|
|
4407
4742
|
};
|
|
4408
4743
|
const deliveries = outputs.map((output) => output.delivery);
|
|
4409
|
-
const normalized = (0,
|
|
4410
|
-
const statement2 =
|
|
4744
|
+
const normalized = (0, import_protocol8.normalizeTransactRequest)(context, params, deliveries, funding.funding);
|
|
4745
|
+
const statement2 = import_protocol8.WitnessBuilder.statement(
|
|
4411
4746
|
`transact_user_slots${normalized.capacity}`,
|
|
4412
|
-
(0,
|
|
4413
|
-
|
|
4747
|
+
(0, import_protocol8.transactUserPublicInputs)(context, params, deliveries, funding.funding),
|
|
4748
|
+
"evm"
|
|
4414
4749
|
);
|
|
4415
4750
|
await preparation.assertBlock(frame);
|
|
4416
4751
|
return preparation.runtime.complete({
|
|
4417
4752
|
version: 1,
|
|
4418
4753
|
id: crypto.randomUUID(),
|
|
4419
|
-
kind,
|
|
4754
|
+
kind: "transfer",
|
|
4420
4755
|
context,
|
|
4421
4756
|
scope: this.scope,
|
|
4422
4757
|
preparedAt: block,
|
|
@@ -4438,7 +4773,9 @@ var TransactionPreparation = class {
|
|
|
4438
4773
|
throw new Error("shield requires 1..10 outputs");
|
|
4439
4774
|
}
|
|
4440
4775
|
const outputs = await Promise.all(
|
|
4441
|
-
copied.outputs.map(
|
|
4776
|
+
copied.outputs.map(
|
|
4777
|
+
(output, index2) => preparation.output(output, frame.context, false, index2)
|
|
4778
|
+
)
|
|
4442
4779
|
);
|
|
4443
4780
|
if (outputs.reduce((sum, output) => sum + output.note.amount, 0n) !== (funding.funding.amount ?? 0n)) {
|
|
4444
4781
|
throw new Error("shield funding must equal output total");
|
|
@@ -4543,10 +4880,10 @@ async function refreshWallet(runtime, options) {
|
|
|
4543
4880
|
}
|
|
4544
4881
|
|
|
4545
4882
|
// src/session/WalletUpdater.ts
|
|
4546
|
-
var
|
|
4883
|
+
var import_core15 = require("@invisi-labs/core");
|
|
4547
4884
|
|
|
4548
4885
|
// src/session/inventory.ts
|
|
4549
|
-
var
|
|
4886
|
+
var import_core14 = require("@invisi-labs/core");
|
|
4550
4887
|
var CanonicalConflict = class extends Error {
|
|
4551
4888
|
};
|
|
4552
4889
|
var CreditMetadataMismatch = class extends Error {
|
|
@@ -4598,10 +4935,10 @@ var InventoryProcessor = class {
|
|
|
4598
4935
|
}
|
|
4599
4936
|
async creditNullifier(state) {
|
|
4600
4937
|
const n = state.note;
|
|
4601
|
-
return (0,
|
|
4938
|
+
return (0, import_core14.computeCreditNullifier)(
|
|
4602
4939
|
n.chainId,
|
|
4603
4940
|
n.creditSharedKey,
|
|
4604
|
-
await (0,
|
|
4941
|
+
await (0, import_core14.computeCreditCommitment)(
|
|
4605
4942
|
n.chainId,
|
|
4606
4943
|
n.spenderWalletIdentity,
|
|
4607
4944
|
n.ownerWalletIdentity,
|
|
@@ -4611,7 +4948,7 @@ var InventoryProcessor = class {
|
|
|
4611
4948
|
n.amount,
|
|
4612
4949
|
n.nonce,
|
|
4613
4950
|
n.expiry,
|
|
4614
|
-
await (0,
|
|
4951
|
+
await (0, import_core14.computeCreditSharedKeyHash)(n.creditSharedKey),
|
|
4615
4952
|
n
|
|
4616
4953
|
)
|
|
4617
4954
|
);
|
|
@@ -4675,7 +5012,7 @@ var InventoryProcessor = class {
|
|
|
4675
5012
|
previous.state.note.expiry === params.expiries[index2],
|
|
4676
5013
|
"draw recovery expiry mismatch"
|
|
4677
5014
|
);
|
|
4678
|
-
const remainder = await (0,
|
|
5015
|
+
const remainder = await (0, import_core14.recoverCreditRemainder)(
|
|
4679
5016
|
context,
|
|
4680
5017
|
previous.state.note,
|
|
4681
5018
|
{
|
|
@@ -4691,7 +5028,7 @@ var InventoryProcessor = class {
|
|
|
4691
5028
|
const child = foundByKey.get(
|
|
4692
5029
|
params.remainderCommitments[index2] + ":" + previous.role
|
|
4693
5030
|
);
|
|
4694
|
-
const
|
|
5031
|
+
const payload = child?.state.note.payload;
|
|
4695
5032
|
const record = {
|
|
4696
5033
|
commitment: params.remainderCommitments[index2],
|
|
4697
5034
|
treeId: BigInt(args.outputTreeId),
|
|
@@ -4704,7 +5041,7 @@ var InventoryProcessor = class {
|
|
|
4704
5041
|
...clone(previous.state),
|
|
4705
5042
|
note: {
|
|
4706
5043
|
...remainder,
|
|
4707
|
-
...
|
|
5044
|
+
...payload === void 0 ? {} : { payload: clone(payload) }
|
|
4708
5045
|
}
|
|
4709
5046
|
},
|
|
4710
5047
|
receivingKeySource: { kind: "draw", commitment: previous.commitment },
|
|
@@ -4783,7 +5120,7 @@ var WalletUpdater = class extends InventoryProcessor {
|
|
|
4783
5120
|
}
|
|
4784
5121
|
async run(batch, options) {
|
|
4785
5122
|
const { reader, protocol, scope, store, viewing } = this.#options;
|
|
4786
|
-
const nullifier =
|
|
5123
|
+
const nullifier = import_core15.NullifierCapability.fromIdentity(scope.walletIdentity);
|
|
4787
5124
|
const { signal, reset } = options;
|
|
4788
5125
|
signal?.throwIfAborted();
|
|
4789
5126
|
const context = protocol.context;
|
|
@@ -4952,6 +5289,7 @@ var WalletUpdater = class extends InventoryProcessor {
|
|
|
4952
5289
|
draft.notes = {};
|
|
4953
5290
|
draft.credits = {};
|
|
4954
5291
|
delete draft.observedThrough;
|
|
5292
|
+
delete draft.discoveryThrough;
|
|
4955
5293
|
delete draft.invalidated;
|
|
4956
5294
|
}
|
|
4957
5295
|
mergeReceiptRecords(draft, { notes, credits, checkpoint: block });
|
|
@@ -4983,13 +5321,38 @@ var WalletUpdater = class extends InventoryProcessor {
|
|
|
4983
5321
|
var WalletSession = class {
|
|
4984
5322
|
#options;
|
|
4985
5323
|
#updater;
|
|
5324
|
+
#listeners = /* @__PURE__ */ new Set();
|
|
4986
5325
|
#sending = /* @__PURE__ */ new Set();
|
|
4987
5326
|
#refreshQueue = Promise.resolve();
|
|
4988
5327
|
scope;
|
|
4989
5328
|
protocol;
|
|
4990
5329
|
constructor(options) {
|
|
5330
|
+
if ("ciphertextCodecs" in options || "envelopeVersion" in options) {
|
|
5331
|
+
throw new Error(
|
|
5332
|
+
"SDK envelope versions are fixed; encode application payload bytes externally"
|
|
5333
|
+
);
|
|
5334
|
+
}
|
|
5335
|
+
const store = options.store;
|
|
4991
5336
|
this.#options = {
|
|
4992
5337
|
...options,
|
|
5338
|
+
...store ? {
|
|
5339
|
+
store: {
|
|
5340
|
+
persistent: store.persistent,
|
|
5341
|
+
readSnapshot: (scope) => store.readSnapshot(scope),
|
|
5342
|
+
transaction: async (scope, action) => {
|
|
5343
|
+
const result = await store.transaction(scope, action);
|
|
5344
|
+
if (scopeKey(scope) === scopeKey(this.scope)) {
|
|
5345
|
+
for (const listener of [...this.#listeners]) {
|
|
5346
|
+
try {
|
|
5347
|
+
listener();
|
|
5348
|
+
} catch {
|
|
5349
|
+
}
|
|
5350
|
+
}
|
|
5351
|
+
}
|
|
5352
|
+
return result;
|
|
5353
|
+
}
|
|
5354
|
+
}
|
|
5355
|
+
} : {},
|
|
4993
5356
|
...options.gasBenchmarks ? { gasBenchmarks: copyGasBenchmarks(options.gasBenchmarks) } : {}
|
|
4994
5357
|
};
|
|
4995
5358
|
this.protocol = options.protocol;
|
|
@@ -5004,7 +5367,7 @@ var WalletSession = class {
|
|
|
5004
5367
|
/** Delegate sending to ethers; managed operations additionally retain the resulting hash. */
|
|
5005
5368
|
async sendTransaction(input, options = {}) {
|
|
5006
5369
|
const signer = this.#options.signer;
|
|
5007
|
-
if (!signer) throw new
|
|
5370
|
+
if (!signer) throw new import_core17.MissingCapabilityError("ethers signer");
|
|
5008
5371
|
const operation = input instanceof Operation ? input : void 0;
|
|
5009
5372
|
if (operation && this.#sending.has(operation.id))
|
|
5010
5373
|
throw new Error("operation is already being sent by this session");
|
|
@@ -5012,12 +5375,12 @@ var WalletSession = class {
|
|
|
5012
5375
|
const overrides = { ...options };
|
|
5013
5376
|
const plain = operation ? void 0 : { ...input };
|
|
5014
5377
|
try {
|
|
5015
|
-
if (!signer.provider) throw new
|
|
5378
|
+
if (!signer.provider) throw new import_core17.MissingCapabilityError("connected ethers signer");
|
|
5016
5379
|
if ((await signer.provider.getNetwork()).chainId !== this.protocol.context.chainId) {
|
|
5017
5380
|
throw new Error("signer-provider session chain mismatch");
|
|
5018
5381
|
}
|
|
5019
5382
|
if (operation) {
|
|
5020
|
-
if (!this.#options.store) throw new
|
|
5383
|
+
if (!this.#options.store) throw new import_core17.MissingCapabilityError("wallet store");
|
|
5021
5384
|
const state = await this.#options.store.readSnapshot(this.scope);
|
|
5022
5385
|
if (!state.operations[operation.id])
|
|
5023
5386
|
throw new Error("operation does not belong to this wallet session");
|
|
@@ -5048,25 +5411,30 @@ var WalletSession = class {
|
|
|
5048
5411
|
runtime(tokenAddress) {
|
|
5049
5412
|
const options = this.#options;
|
|
5050
5413
|
if (!options.store) {
|
|
5051
|
-
throw new
|
|
5414
|
+
throw new import_core17.MissingCapabilityError("wallet store");
|
|
5052
5415
|
}
|
|
5053
5416
|
if (!options.chainReader) {
|
|
5054
|
-
throw new
|
|
5417
|
+
throw new import_core17.MissingCapabilityError("chain reader");
|
|
5055
5418
|
}
|
|
5056
5419
|
return {
|
|
5057
5420
|
options: { ...options, protocol: options.protocol.forToken(tokenAddress) },
|
|
5058
5421
|
scope: this.scope,
|
|
5059
5422
|
store: options.store,
|
|
5060
5423
|
reader: options.chainReader,
|
|
5061
|
-
authorization: () =>
|
|
5424
|
+
authorization: () => {
|
|
5425
|
+
if (options.authorization) return options.authorization;
|
|
5426
|
+
if (options.wallet instanceof import_core16.Wallet) return options.wallet.exportAuthorization();
|
|
5427
|
+
throw new import_core17.MissingCapabilityError("wallet authorization");
|
|
5428
|
+
}
|
|
5062
5429
|
};
|
|
5063
5430
|
}
|
|
5064
5431
|
updater() {
|
|
5065
5432
|
if (this.#updater) return this.#updater;
|
|
5066
5433
|
const options = this.#options;
|
|
5067
5434
|
if (!options.store || !options.chainReader || !options.protocolRpc)
|
|
5068
|
-
throw new
|
|
5069
|
-
const viewing = options.viewing ?? options.wallet.getViewer();
|
|
5435
|
+
throw new import_core17.MissingCapabilityError("wallet store, chain reader and Protocol RPC");
|
|
5436
|
+
const viewing = options.viewing ?? (options.wallet instanceof import_core16.Wallet ? options.wallet.getViewer() : void 0);
|
|
5437
|
+
if (!viewing) throw new import_core17.MissingCapabilityError("viewing");
|
|
5070
5438
|
const receivingAddress = options.wallet.getReceivingAddress();
|
|
5071
5439
|
const receivingKeys = receivingAddress.kind === "long" ? receivingAddress.getReceivingKeys() : void 0;
|
|
5072
5440
|
this.#updater = new WalletUpdater({
|
|
@@ -5080,10 +5448,27 @@ var WalletSession = class {
|
|
|
5080
5448
|
});
|
|
5081
5449
|
return this.#updater;
|
|
5082
5450
|
}
|
|
5451
|
+
/** Local session commits only. Host applications own cross-tab/store subscriptions. */
|
|
5452
|
+
subscribe(listener) {
|
|
5453
|
+
this.#listeners.add(listener);
|
|
5454
|
+
return () => {
|
|
5455
|
+
this.#listeners.delete(listener);
|
|
5456
|
+
};
|
|
5457
|
+
}
|
|
5458
|
+
/** Detached history for wallet UI; construction records do not imply chain success. */
|
|
5459
|
+
async getHistory(options = {}) {
|
|
5460
|
+
const tokenAddress = options.tokenAddress ? (0, import_ethers11.getAddress)(options.tokenAddress) : void 0;
|
|
5461
|
+
const store = this.#options.store;
|
|
5462
|
+
if (!store) throw new import_core17.MissingCapabilityError("wallet store");
|
|
5463
|
+
return walletHistory(
|
|
5464
|
+
await store.readSnapshot(this.scope),
|
|
5465
|
+
tokenAddress ? { tokenAddress } : {}
|
|
5466
|
+
);
|
|
5467
|
+
}
|
|
5083
5468
|
async balance(tokenAddress) {
|
|
5084
5469
|
const store = this.#options.store;
|
|
5085
5470
|
if (!store) {
|
|
5086
|
-
throw new
|
|
5471
|
+
throw new import_core17.MissingCapabilityError("wallet store");
|
|
5087
5472
|
}
|
|
5088
5473
|
return walletBalance(await store.readSnapshot(this.scope), (0, import_ethers11.getAddress)(tokenAddress));
|
|
5089
5474
|
}
|
|
@@ -5091,15 +5476,15 @@ var WalletSession = class {
|
|
|
5091
5476
|
async refresh(options) {
|
|
5092
5477
|
const provider = this.#options.protocolRpc;
|
|
5093
5478
|
const reader = this.#options.chainReader;
|
|
5094
|
-
if (!reader) throw new
|
|
5095
|
-
if (!provider) throw new
|
|
5479
|
+
if (!reader) throw new import_core17.MissingCapabilityError("chain reader");
|
|
5480
|
+
if (!provider) throw new import_core17.MissingCapabilityError("Protocol RPC");
|
|
5096
5481
|
const updater = this.updater();
|
|
5097
5482
|
const request = {
|
|
5098
5483
|
...options,
|
|
5099
5484
|
...options.detectionKey ? { detectionKey: options.detectionKey.slice() } : {}
|
|
5100
5485
|
};
|
|
5101
|
-
const task = this.#refreshQueue.then(
|
|
5102
|
-
|
|
5486
|
+
const task = this.#refreshQueue.then(async () => {
|
|
5487
|
+
const result = await refreshWallet(
|
|
5103
5488
|
{
|
|
5104
5489
|
provider,
|
|
5105
5490
|
reader,
|
|
@@ -5108,8 +5493,21 @@ var WalletSession = class {
|
|
|
5108
5493
|
apply: (batch, settings) => updater.applyUpdates(batch, settings)
|
|
5109
5494
|
},
|
|
5110
5495
|
request
|
|
5111
|
-
)
|
|
5112
|
-
|
|
5496
|
+
);
|
|
5497
|
+
await this.#options.store.transaction(this.scope, (state) => {
|
|
5498
|
+
request.signal?.throwIfAborted();
|
|
5499
|
+
if (state.invalidated) throw new Error("wallet requires canonical resync");
|
|
5500
|
+
const previous = state.discoveryThrough;
|
|
5501
|
+
const start = previous ? previous.number + 1n : this.protocol.deployment.protocol.fromBlock;
|
|
5502
|
+
if (request.fromBlock <= start && (!previous || result.block.number >= previous.number)) {
|
|
5503
|
+
state.discoveryThrough = {
|
|
5504
|
+
number: result.block.number,
|
|
5505
|
+
hash: result.block.hash
|
|
5506
|
+
};
|
|
5507
|
+
}
|
|
5508
|
+
});
|
|
5509
|
+
return result;
|
|
5510
|
+
});
|
|
5113
5511
|
this.#refreshQueue = task.catch(() => {
|
|
5114
5512
|
});
|
|
5115
5513
|
return task;
|
|
@@ -5120,38 +5518,43 @@ var WalletSession = class {
|
|
|
5120
5518
|
options: runtime.options,
|
|
5121
5519
|
scope: runtime.scope,
|
|
5122
5520
|
reader: runtime.reader,
|
|
5123
|
-
authorization: runtime.authorization,
|
|
5124
5521
|
readInventory: () => runtime.store.readSnapshot(runtime.scope),
|
|
5125
5522
|
complete: (plan) => Operation.prepare(runtime, plan)
|
|
5126
5523
|
});
|
|
5127
5524
|
}
|
|
5128
5525
|
async prepareTransfer(options) {
|
|
5129
|
-
return
|
|
5130
|
-
|
|
5131
|
-
|
|
5132
|
-
return this.preparation(options.tokenAddress).prepareWithdraw(options);
|
|
5133
|
-
}
|
|
5134
|
-
async prepareSweep(options) {
|
|
5135
|
-
return this.preparation(options.tokenAddress).prepareSweep(options);
|
|
5526
|
+
return prepareWithErrors("prepareTransfer", async () => {
|
|
5527
|
+
return this.preparation(options.tokenAddress).prepareTransfer(options);
|
|
5528
|
+
});
|
|
5136
5529
|
}
|
|
5137
5530
|
async prepareShield(options) {
|
|
5138
|
-
return
|
|
5531
|
+
return prepareWithErrors("prepareShield", async () => {
|
|
5532
|
+
return this.preparation(options.tokenAddress).prepareShield(options);
|
|
5533
|
+
});
|
|
5139
5534
|
}
|
|
5140
5535
|
async prepareCreateCredit(options) {
|
|
5141
|
-
return
|
|
5536
|
+
return prepareWithErrors("prepareCreateCredit", async () => {
|
|
5537
|
+
return this.preparation(options.tokenAddress).prepareCreateCredit(options);
|
|
5538
|
+
});
|
|
5142
5539
|
}
|
|
5143
5540
|
async prepareDrawCredit(options) {
|
|
5144
|
-
return
|
|
5541
|
+
return prepareWithErrors("prepareDrawCredit", async () => {
|
|
5542
|
+
return this.preparation(options.tokenAddress).prepareDrawCredit(options);
|
|
5543
|
+
});
|
|
5145
5544
|
}
|
|
5146
5545
|
async prepareTopUpCredit(options) {
|
|
5147
|
-
return
|
|
5546
|
+
return prepareWithErrors("prepareTopUpCredit", async () => {
|
|
5547
|
+
return this.preparation(options.tokenAddress).prepareTopUpCredit(options);
|
|
5548
|
+
});
|
|
5148
5549
|
}
|
|
5149
5550
|
async prepareCancelCredit(options) {
|
|
5150
|
-
return
|
|
5551
|
+
return prepareWithErrors("prepareCancelCredit", async () => {
|
|
5552
|
+
return this.preparation(options.tokenAddress).prepareCancelCredit(options);
|
|
5553
|
+
});
|
|
5151
5554
|
}
|
|
5152
5555
|
async resume(operationId) {
|
|
5153
5556
|
const store = this.#options.store;
|
|
5154
|
-
if (!store) throw new
|
|
5557
|
+
if (!store) throw new import_core17.MissingCapabilityError("wallet store");
|
|
5155
5558
|
const state = await store.readSnapshot(this.scope);
|
|
5156
5559
|
const saved = state.operations[operationId]?.payload;
|
|
5157
5560
|
if (!saved?.plan) throw new Error("operation not found");
|