@orbinum/sdk 1.3.1 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-A2ZRMEYW.mjs → chunk-VYKKBXOE.mjs} +51 -28
- package/dist/{index-V5Z9igEN.d.mts → index-CLpM1984.d.mts} +42 -9
- package/dist/{index-V5Z9igEN.d.ts → index-CLpM1984.d.ts} +42 -9
- package/dist/index.d.mts +399 -105
- package/dist/index.d.ts +399 -105
- package/dist/index.js +595 -338
- package/dist/index.mjs +487 -257
- package/dist/wallet/worker/index.d.mts +1 -1
- package/dist/wallet/worker/index.d.ts +1 -1
- package/dist/wallet/worker/index.js +18 -11
- package/dist/wallet/worker/index.mjs +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -29,6 +29,7 @@ import {
|
|
|
29
29
|
bytesToBjjScalar,
|
|
30
30
|
canPairWith,
|
|
31
31
|
clearKnownEphWindow,
|
|
32
|
+
collectOutgoingFacts,
|
|
32
33
|
commitmentHexOf,
|
|
33
34
|
computeNoteCommitment,
|
|
34
35
|
computeNullifier,
|
|
@@ -59,6 +60,7 @@ import {
|
|
|
59
60
|
hexToBigint,
|
|
60
61
|
hexToNumber,
|
|
61
62
|
isAbortError,
|
|
63
|
+
isHexOfLength,
|
|
62
64
|
isSpendable,
|
|
63
65
|
isValidLeafIndex,
|
|
64
66
|
leHexToBigint,
|
|
@@ -77,7 +79,7 @@ import {
|
|
|
77
79
|
tryDecryptNote,
|
|
78
80
|
tryDecryptNoteVerbose,
|
|
79
81
|
tryRecoverOutgoing
|
|
80
|
-
} from "./chunk-
|
|
82
|
+
} from "./chunk-VYKKBXOE.mjs";
|
|
81
83
|
|
|
82
84
|
// src/foundation/encoding/base64.ts
|
|
83
85
|
var ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
@@ -382,6 +384,7 @@ var SLIP_DOMAIN = new TextEncoder().encode("orbinum-payment-slip-v1");
|
|
|
382
384
|
var NONCE_PREFIX = new TextEncoder().encode("SLP1");
|
|
383
385
|
var EPH_PK_SIZE = 32;
|
|
384
386
|
var NONCE_SUFFIX_SIZE = 8;
|
|
387
|
+
var MAX_SLIP_ENVELOPE_SIZE = 4096;
|
|
385
388
|
function deriveSlipKey(sharedSecret) {
|
|
386
389
|
return hkdf(sha256, sharedSecret, void 0, SLIP_DOMAIN, 32);
|
|
387
390
|
}
|
|
@@ -416,6 +419,7 @@ function sealPaymentSlip(recipientIvkPacked, fields) {
|
|
|
416
419
|
}
|
|
417
420
|
function openPaymentSlip(recipientIvsk, envelope) {
|
|
418
421
|
if (envelope.length < EPH_PK_SIZE + NONCE_SUFFIX_SIZE + 16) return null;
|
|
422
|
+
if (envelope.length > MAX_SLIP_ENVELOPE_SIZE) return null;
|
|
419
423
|
try {
|
|
420
424
|
const ephPkPacked = envelope.subarray(0, EPH_PK_SIZE);
|
|
421
425
|
const ephPkPoint = unpackPoint(bytesToBigintLE(ephPkPacked));
|
|
@@ -427,11 +431,19 @@ function openPaymentSlip(recipientIvsk, envelope) {
|
|
|
427
431
|
const sealed = envelope.subarray(EPH_PK_SIZE + NONCE_SUFFIX_SIZE);
|
|
428
432
|
const cipher = chacha20poly1305(slipKey, buildNonce(suffix));
|
|
429
433
|
const plaintext = cipher.decrypt(sealed);
|
|
430
|
-
const
|
|
431
|
-
if (
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
return
|
|
434
|
+
const parsed = JSON.parse(new TextDecoder().decode(plaintext));
|
|
435
|
+
if (!isHexOfLength(parsed["commitmentHex"], 32)) return null;
|
|
436
|
+
if (!isHexOfLength(parsed["encryptedMemo"], ENCRYPTED_MEMO_SIZE)) return null;
|
|
437
|
+
const leafIndex = parsed["leafIndex"];
|
|
438
|
+
if (leafIndex !== void 0 && !isValidLeafIndex(leafIndex)) return null;
|
|
439
|
+
const rawTxHash = parsed["txHash"];
|
|
440
|
+
const txHash = isHexOfLength(rawTxHash, 32) ? rawTxHash : void 0;
|
|
441
|
+
return {
|
|
442
|
+
commitmentHex: parsed["commitmentHex"],
|
|
443
|
+
encryptedMemo: parsed["encryptedMemo"],
|
|
444
|
+
...leafIndex !== void 0 ? { leafIndex } : {},
|
|
445
|
+
...txHash !== void 0 ? { txHash } : {}
|
|
446
|
+
};
|
|
435
447
|
} catch {
|
|
436
448
|
return null;
|
|
437
449
|
}
|
|
@@ -1536,11 +1548,17 @@ var SubstrateClient = class _SubstrateClient {
|
|
|
1536
1548
|
|
|
1537
1549
|
// src/chain/evm/EvmClient.ts
|
|
1538
1550
|
var EvmClient = class {
|
|
1539
|
-
/**
|
|
1540
|
-
|
|
1551
|
+
/**
|
|
1552
|
+
* @param rpcUrl - HTTP URL of the EVM JSON-RPC endpoint (e.g. `"http://localhost:9933"`).
|
|
1553
|
+
* @param peerRpcUrl - Optional second endpoint, used only to tell a genuinely
|
|
1554
|
+
* pending transaction from one stranded on `rpcUrl` alone. See `waitForReceipt`.
|
|
1555
|
+
*/
|
|
1556
|
+
constructor(rpcUrl, peerRpcUrl) {
|
|
1541
1557
|
this.rpcUrl = rpcUrl;
|
|
1558
|
+
this.peerRpcUrl = peerRpcUrl;
|
|
1542
1559
|
}
|
|
1543
1560
|
rpcUrl;
|
|
1561
|
+
peerRpcUrl;
|
|
1544
1562
|
/**
|
|
1545
1563
|
* Performs a single JSON-RPC call and returns the typed result.
|
|
1546
1564
|
* Throws on HTTP errors, RPC-level errors, or a `null` result.
|
|
@@ -1598,10 +1616,18 @@ var EvmClient = class {
|
|
|
1598
1616
|
const hex = await this.request("eth_getTransactionCount", [address, "latest"]);
|
|
1599
1617
|
return hexToNumber(hex);
|
|
1600
1618
|
}
|
|
1601
|
-
/**
|
|
1602
|
-
|
|
1619
|
+
/**
|
|
1620
|
+
* Returns the current gas price in wei, padded by `bumpPercent`.
|
|
1621
|
+
*
|
|
1622
|
+
* `eth_gasPrice` reports the base fee exactly, and the base fee moves
|
|
1623
|
+
* between signing and the pool's next revalidation. A transaction priced at
|
|
1624
|
+
* the bare minimum is evicted as `GasPriceTooLow` the moment it rises, which
|
|
1625
|
+
* leaves every later nonce from that account stranded in the future queue.
|
|
1626
|
+
* The default 25% pad absorbs the usual movement.
|
|
1627
|
+
*/
|
|
1628
|
+
async getGasPrice(bumpPercent = 25) {
|
|
1603
1629
|
const hex = await this.request("eth_gasPrice", []);
|
|
1604
|
-
return hexToBigint(hex);
|
|
1630
|
+
return hexToBigint(hex) * BigInt(100 + bumpPercent) / 100n;
|
|
1605
1631
|
}
|
|
1606
1632
|
/** Submits a signed raw transaction. Returns the transaction hash. */
|
|
1607
1633
|
async sendRawTransaction(signedHex) {
|
|
@@ -1719,10 +1745,41 @@ var EvmClient = class {
|
|
|
1719
1745
|
deadline = Math.min(deadline + timeoutMs, hardDeadline);
|
|
1720
1746
|
}
|
|
1721
1747
|
}
|
|
1748
|
+
if (await this.isStrandedOnThisNode(txHash)) {
|
|
1749
|
+
throw new Error(
|
|
1750
|
+
`Transaction dropped from the tx pool (never propagated beyond the submitting node, ${Date.now() - start}ms): ${txHash}`
|
|
1751
|
+
);
|
|
1752
|
+
}
|
|
1722
1753
|
throw new Error(
|
|
1723
1754
|
`Transaction still pending after ${Date.now() - start}ms: ${txHash} \u2014 it may still confirm; check the hash on the explorer before retrying`
|
|
1724
1755
|
);
|
|
1725
1756
|
}
|
|
1757
|
+
/**
|
|
1758
|
+
* True when `rpcUrl` knows the transaction but the configured peer does not.
|
|
1759
|
+
*
|
|
1760
|
+
* Returns false without a peer configured, and on any peer error — an
|
|
1761
|
+
* unreachable peer is not evidence that a live transaction is stranded.
|
|
1762
|
+
*/
|
|
1763
|
+
async isStrandedOnThisNode(txHash) {
|
|
1764
|
+
if (!this.peerRpcUrl) return false;
|
|
1765
|
+
try {
|
|
1766
|
+
const res = await postJsonWithRetry(
|
|
1767
|
+
this.peerRpcUrl,
|
|
1768
|
+
JSON.stringify({
|
|
1769
|
+
id: 1,
|
|
1770
|
+
jsonrpc: "2.0",
|
|
1771
|
+
method: "eth_getTransactionByHash",
|
|
1772
|
+
params: [txHash]
|
|
1773
|
+
})
|
|
1774
|
+
);
|
|
1775
|
+
if (!res.ok) return false;
|
|
1776
|
+
const json = await res.json();
|
|
1777
|
+
if (json.error) return false;
|
|
1778
|
+
return json.result === null;
|
|
1779
|
+
} catch {
|
|
1780
|
+
return false;
|
|
1781
|
+
}
|
|
1782
|
+
}
|
|
1726
1783
|
};
|
|
1727
1784
|
|
|
1728
1785
|
// src/chain/evm/explorer/EvmExplorer.ts
|
|
@@ -2441,6 +2498,23 @@ function padTo32Multiple(data) {
|
|
|
2441
2498
|
padded.set(data);
|
|
2442
2499
|
return padded;
|
|
2443
2500
|
}
|
|
2501
|
+
function bytes32Slot(value) {
|
|
2502
|
+
if (value.length !== 32) {
|
|
2503
|
+
throw new Error(`encodeAbi: bytes32 needs 32 bytes, got ${value.length}`);
|
|
2504
|
+
}
|
|
2505
|
+
const slot = new Uint8Array(32);
|
|
2506
|
+
slot.set(value);
|
|
2507
|
+
return slot;
|
|
2508
|
+
}
|
|
2509
|
+
function addressSlot(address) {
|
|
2510
|
+
const clean = address.startsWith("0x") ? address.slice(2) : address;
|
|
2511
|
+
if (clean.length > 40) {
|
|
2512
|
+
throw new Error(`encodeAbi: address needs at most 20 bytes, got ${clean.length / 2}`);
|
|
2513
|
+
}
|
|
2514
|
+
const slot = new Uint8Array(32);
|
|
2515
|
+
slot.set(fromHex("0x" + clean.padStart(40, "0")), 12);
|
|
2516
|
+
return slot;
|
|
2517
|
+
}
|
|
2444
2518
|
function encodeStaticParam(param) {
|
|
2445
2519
|
const buf = new Uint8Array(32);
|
|
2446
2520
|
switch (param.type) {
|
|
@@ -2448,14 +2522,10 @@ function encodeStaticParam(param) {
|
|
|
2448
2522
|
return bigintTo32Be(param.value);
|
|
2449
2523
|
}
|
|
2450
2524
|
case "bytes32": {
|
|
2451
|
-
|
|
2452
|
-
return buf;
|
|
2525
|
+
return bytes32Slot(param.value);
|
|
2453
2526
|
}
|
|
2454
2527
|
case "address": {
|
|
2455
|
-
|
|
2456
|
-
const bytes = fromHex("0x" + clean.padStart(40, "0"));
|
|
2457
|
-
buf.set(bytes, 12);
|
|
2458
|
-
return buf;
|
|
2528
|
+
return addressSlot(param.value);
|
|
2459
2529
|
}
|
|
2460
2530
|
case "bool": {
|
|
2461
2531
|
buf[31] = param.value ? 1 : 0;
|
|
@@ -2478,25 +2548,13 @@ function encodeDynamicParam(param) {
|
|
|
2478
2548
|
return concat([bigintTo32Be(BigInt(data.length)), padTo32Multiple(data)]);
|
|
2479
2549
|
}
|
|
2480
2550
|
case "bytes32[]": {
|
|
2481
|
-
const
|
|
2482
|
-
const
|
|
2483
|
-
for (const b32 of param.value) {
|
|
2484
|
-
const slot = new Uint8Array(32);
|
|
2485
|
-
slot.set(b32.slice(0, 32));
|
|
2486
|
-
parts.push(slot);
|
|
2487
|
-
}
|
|
2551
|
+
const parts = [bigintTo32Be(BigInt(param.value.length))];
|
|
2552
|
+
for (const b32 of param.value) parts.push(bytes32Slot(b32));
|
|
2488
2553
|
return concat(parts);
|
|
2489
2554
|
}
|
|
2490
2555
|
case "address[]": {
|
|
2491
|
-
const
|
|
2492
|
-
const
|
|
2493
|
-
for (const addr of param.value) {
|
|
2494
|
-
const slot = new Uint8Array(32);
|
|
2495
|
-
const clean = addr.startsWith("0x") ? addr.slice(2) : addr;
|
|
2496
|
-
const bytes = fromHex("0x" + clean.padStart(40, "0"));
|
|
2497
|
-
slot.set(bytes, 12);
|
|
2498
|
-
parts.push(slot);
|
|
2499
|
-
}
|
|
2556
|
+
const parts = [bigintTo32Be(BigInt(param.value.length))];
|
|
2557
|
+
for (const addr of param.value) parts.push(addressSlot(addr));
|
|
2500
2558
|
return concat(parts);
|
|
2501
2559
|
}
|
|
2502
2560
|
case "bytes[]": {
|
|
@@ -2552,6 +2610,122 @@ function decodeUint(data, offset = 0) {
|
|
|
2552
2610
|
return result;
|
|
2553
2611
|
}
|
|
2554
2612
|
|
|
2613
|
+
// src/chain/evm/precompiles/shieldedPoolCalldata.ts
|
|
2614
|
+
var CLAIM_PUBLIC_SIGNALS_SIZE = 76;
|
|
2615
|
+
function bytes32(hex, field) {
|
|
2616
|
+
if (!isHexOfLength(hex, 32)) {
|
|
2617
|
+
throw new Error(`${field}: expected a 0x-prefixed 32-byte hex string, got ${hex}`);
|
|
2618
|
+
}
|
|
2619
|
+
return fromHex(hex);
|
|
2620
|
+
}
|
|
2621
|
+
function uint32(value, field) {
|
|
2622
|
+
if (!Number.isInteger(value) || value < 0 || value > 4294967295) {
|
|
2623
|
+
throw new Error(`${field}: expected a uint32 (0..4294967295), got ${value}`);
|
|
2624
|
+
}
|
|
2625
|
+
return BigInt(value);
|
|
2626
|
+
}
|
|
2627
|
+
function accountId32(address, field) {
|
|
2628
|
+
const raw = address.startsWith("0x") ? address.slice(2) : address;
|
|
2629
|
+
if (raw.length > 64) {
|
|
2630
|
+
throw new Error(`${field}: expected at most 32 bytes, got ${raw.length / 2}`);
|
|
2631
|
+
}
|
|
2632
|
+
return bytes32("0x" + raw.padEnd(64, "0"), field);
|
|
2633
|
+
}
|
|
2634
|
+
function buildShieldCalldata(params) {
|
|
2635
|
+
EncryptedMemo.validate(params.encryptedMemo, "buildShieldCalldata.encryptedMemo");
|
|
2636
|
+
const commitment = bytes32(params.commitment, "buildShieldCalldata.commitment");
|
|
2637
|
+
return encodeHex(
|
|
2638
|
+
SP_SEL.SHIELD,
|
|
2639
|
+
{ type: "uint", value: uint32(params.assetId, "buildShieldCalldata.assetId") },
|
|
2640
|
+
{ type: "bytes32", value: commitment },
|
|
2641
|
+
{ type: "bytes", value: params.encryptedMemo }
|
|
2642
|
+
);
|
|
2643
|
+
}
|
|
2644
|
+
function buildPrivateTransferCalldata(params) {
|
|
2645
|
+
const nullifiers = params.inputs.map(
|
|
2646
|
+
(input, i) => bytes32(input.nullifier, `buildPrivateTransferCalldata.inputs[${i}].nullifier`)
|
|
2647
|
+
);
|
|
2648
|
+
const commitments = params.outputs.map(
|
|
2649
|
+
(output, i) => bytes32(output.commitment, `buildPrivateTransferCalldata.outputs[${i}].commitment`)
|
|
2650
|
+
);
|
|
2651
|
+
const memos = params.outputs.map((output, i) => {
|
|
2652
|
+
EncryptedMemo.validate(
|
|
2653
|
+
output.encryptedMemo,
|
|
2654
|
+
`buildPrivateTransferCalldata.outputs[${i}].encryptedMemo`
|
|
2655
|
+
);
|
|
2656
|
+
return output.encryptedMemo;
|
|
2657
|
+
});
|
|
2658
|
+
const root = bytes32(params.merkleRoot, "buildPrivateTransferCalldata.merkleRoot");
|
|
2659
|
+
return encodeHex(
|
|
2660
|
+
SP_SEL.PRIVATE_TRANSFER,
|
|
2661
|
+
{ type: "bytes", value: params.proof },
|
|
2662
|
+
{ type: "bytes32", value: root },
|
|
2663
|
+
{ type: "bytes32[]", value: nullifiers },
|
|
2664
|
+
{ type: "bytes32[]", value: commitments },
|
|
2665
|
+
{ type: "bytes[]", value: memos },
|
|
2666
|
+
{ type: "uint", value: uint32(params.assetId, "buildPrivateTransferCalldata.assetId") },
|
|
2667
|
+
{ type: "uint", value: params.fee ?? 0n },
|
|
2668
|
+
{
|
|
2669
|
+
type: "uint",
|
|
2670
|
+
value: uint32(params.circuitVersion, "buildPrivateTransferCalldata.circuitVersion")
|
|
2671
|
+
}
|
|
2672
|
+
);
|
|
2673
|
+
}
|
|
2674
|
+
function buildUnshieldCalldata(params) {
|
|
2675
|
+
const root = bytes32(params.merkleRoot, "buildUnshieldCalldata.merkleRoot");
|
|
2676
|
+
const nullifier = bytes32(params.nullifier, "buildUnshieldCalldata.nullifier");
|
|
2677
|
+
const recipient = accountId32(
|
|
2678
|
+
params.recipientAddress,
|
|
2679
|
+
"buildUnshieldCalldata.recipientAddress"
|
|
2680
|
+
);
|
|
2681
|
+
const changeCommitment = bytes32(
|
|
2682
|
+
params.changeCommitment ?? "0x" + "00".repeat(32),
|
|
2683
|
+
"buildUnshieldCalldata.changeCommitment"
|
|
2684
|
+
);
|
|
2685
|
+
const changeEncryptedMemo = params.changeEncryptedMemo ?? new Uint8Array();
|
|
2686
|
+
return encodeHex(
|
|
2687
|
+
SP_SEL.UNSHIELD,
|
|
2688
|
+
{ type: "bytes", value: params.proof },
|
|
2689
|
+
{ type: "bytes32", value: root },
|
|
2690
|
+
{ type: "bytes32", value: nullifier },
|
|
2691
|
+
{ type: "uint", value: uint32(params.assetId, "buildUnshieldCalldata.assetId") },
|
|
2692
|
+
{ type: "uint", value: params.amount },
|
|
2693
|
+
{ type: "bytes32", value: recipient },
|
|
2694
|
+
{ type: "uint", value: params.fee ?? 0n },
|
|
2695
|
+
{ type: "bytes32", value: changeCommitment },
|
|
2696
|
+
{ type: "bytes", value: changeEncryptedMemo },
|
|
2697
|
+
{
|
|
2698
|
+
type: "uint",
|
|
2699
|
+
value: uint32(params.circuitVersion, "buildUnshieldCalldata.circuitVersion")
|
|
2700
|
+
}
|
|
2701
|
+
);
|
|
2702
|
+
}
|
|
2703
|
+
function buildClaimShieldedFeesCalldata(params) {
|
|
2704
|
+
EncryptedMemo.validate(params.encryptedMemo, "buildClaimShieldedFeesCalldata.encryptedMemo");
|
|
2705
|
+
if (params.proof.length === 0) {
|
|
2706
|
+
throw new Error("claimShieldedFees: proof must not be empty");
|
|
2707
|
+
}
|
|
2708
|
+
if (params.publicSignals.length !== CLAIM_PUBLIC_SIGNALS_SIZE) {
|
|
2709
|
+
throw new Error(
|
|
2710
|
+
`claimShieldedFees: publicSignals must be ${CLAIM_PUBLIC_SIGNALS_SIZE} bytes, got ${params.publicSignals.length}`
|
|
2711
|
+
);
|
|
2712
|
+
}
|
|
2713
|
+
const commitment = bytes32(params.commitment, "buildClaimShieldedFeesCalldata.commitment");
|
|
2714
|
+
return encodeHex(
|
|
2715
|
+
SP_SEL.CLAIM_SHIELDED_FEES,
|
|
2716
|
+
{ type: "bytes32", value: commitment },
|
|
2717
|
+
{ type: "uint", value: params.amount },
|
|
2718
|
+
{ type: "uint", value: uint32(params.assetId, "buildClaimShieldedFeesCalldata.assetId") },
|
|
2719
|
+
{ type: "bytes", value: params.encryptedMemo },
|
|
2720
|
+
{ type: "bytes", value: params.proof },
|
|
2721
|
+
{ type: "bytes", value: params.publicSignals },
|
|
2722
|
+
{
|
|
2723
|
+
type: "uint",
|
|
2724
|
+
value: uint32(params.circuitVersion, "buildClaimShieldedFeesCalldata.circuitVersion")
|
|
2725
|
+
}
|
|
2726
|
+
);
|
|
2727
|
+
}
|
|
2728
|
+
|
|
2555
2729
|
// src/chain/evm/precompiles/ShieldedPoolPrecompile.ts
|
|
2556
2730
|
var ShieldedPoolPrecompile = class {
|
|
2557
2731
|
constructor(evm) {
|
|
@@ -2559,228 +2733,111 @@ var ShieldedPoolPrecompile = class {
|
|
|
2559
2733
|
}
|
|
2560
2734
|
evm;
|
|
2561
2735
|
addr = PRECOMPILE_ADDR.SHIELDED_POOL;
|
|
2562
|
-
// ───
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
*/
|
|
2736
|
+
// ─── Calldata ────────────────────────────────────────────────────────────
|
|
2737
|
+
//
|
|
2738
|
+
// Thin delegates to `shieldedPoolCalldata`, kept because they are public
|
|
2739
|
+
// API. New code should import those functions directly: they are pure, so
|
|
2740
|
+
// using them needs no `EvmClient` to construct.
|
|
2568
2741
|
buildShieldCalldata(params) {
|
|
2569
|
-
|
|
2570
|
-
|
|
2571
|
-
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
|
|
2575
|
-
|
|
2576
|
-
);
|
|
2742
|
+
return buildShieldCalldata(params);
|
|
2743
|
+
}
|
|
2744
|
+
buildPrivateTransferCalldata(params) {
|
|
2745
|
+
return buildPrivateTransferCalldata(params);
|
|
2746
|
+
}
|
|
2747
|
+
buildUnshieldCalldata(params) {
|
|
2748
|
+
return buildUnshieldCalldata(params);
|
|
2577
2749
|
}
|
|
2750
|
+
buildClaimShieldedFeesCalldata(params) {
|
|
2751
|
+
return buildClaimShieldedFeesCalldata(params);
|
|
2752
|
+
}
|
|
2753
|
+
// ─── shield ──────────────────────────────────────────────────────────────
|
|
2578
2754
|
/**
|
|
2579
2755
|
* Deposits tokens into the shielded pool from a payable EVM transaction.
|
|
2580
2756
|
*
|
|
2581
|
-
* The
|
|
2582
|
-
* the
|
|
2583
|
-
*
|
|
2584
|
-
*
|
|
2585
|
-
* precompile → pool (via pallet transfer)
|
|
2586
|
-
* This avoids double-deduction while keeping the displayed amount accurate.
|
|
2757
|
+
* The amount rides as `msg.value` so EVM wallets show the correct figure on
|
|
2758
|
+
* the confirmation screen. The precompile then dispatches with its OWN
|
|
2759
|
+
* address as origin, so funds flow caller → precompile → pool. That avoids
|
|
2760
|
+
* a double deduction while keeping the displayed amount accurate.
|
|
2587
2761
|
*
|
|
2588
2762
|
* Extrinsic: `shieldedPool.shield(assetId, amount, commitment, encryptedMemo)`
|
|
2589
2763
|
*/
|
|
2590
2764
|
async shield(params, signer) {
|
|
2591
2765
|
return signer({
|
|
2592
2766
|
to: this.addr,
|
|
2593
|
-
data:
|
|
2767
|
+
data: buildShieldCalldata(params),
|
|
2594
2768
|
value: params.amount
|
|
2595
2769
|
});
|
|
2596
2770
|
}
|
|
2597
|
-
// ─── privateTransfer
|
|
2771
|
+
// ─── privateTransfer ─────────────────────────────────────────────────────
|
|
2598
2772
|
/**
|
|
2599
|
-
*
|
|
2600
|
-
* `privateTransfer(bytes, bytes32, bytes32[], bytes32[], bytes[], uint32, uint256, uint32)`.
|
|
2601
|
-
* The trailing `uint32` is the circuit version the input notes were created under.
|
|
2602
|
-
*/
|
|
2603
|
-
buildPrivateTransferCalldata(params) {
|
|
2604
|
-
const nullifiers = params.inputs.map((i) => fromHex(i.nullifier));
|
|
2605
|
-
const commitments = params.outputs.map((o) => fromHex(o.commitment));
|
|
2606
|
-
const memos = params.outputs.map((o, i) => {
|
|
2607
|
-
EncryptedMemo.validate(
|
|
2608
|
-
o.encryptedMemo,
|
|
2609
|
-
`buildPrivateTransferCalldata.outputs[${i}].encryptedMemo`
|
|
2610
|
-
);
|
|
2611
|
-
return o.encryptedMemo;
|
|
2612
|
-
});
|
|
2613
|
-
const root = fromHex(params.merkleRoot);
|
|
2614
|
-
return encodeHex(
|
|
2615
|
-
SP_SEL.PRIVATE_TRANSFER,
|
|
2616
|
-
{ type: "bytes", value: params.proof },
|
|
2617
|
-
{ type: "bytes32", value: root },
|
|
2618
|
-
{ type: "bytes32[]", value: nullifiers },
|
|
2619
|
-
{ type: "bytes32[]", value: commitments },
|
|
2620
|
-
{ type: "bytes[]", value: memos },
|
|
2621
|
-
{ type: "uint", value: BigInt(params.assetId) },
|
|
2622
|
-
{ type: "uint", value: params.fee ?? 0n },
|
|
2623
|
-
{ type: "uint", value: BigInt(params.circuitVersion) }
|
|
2624
|
-
);
|
|
2625
|
-
}
|
|
2626
|
-
/**
|
|
2627
|
-
* Submits a private transfer within the shielded pool from an EVM transaction.
|
|
2773
|
+
* Submits a private transfer within the shielded pool.
|
|
2628
2774
|
*
|
|
2629
|
-
* The EVM caller identity is
|
|
2630
|
-
* hidden by design
|
|
2775
|
+
* The EVM caller identity is IRRELEVANT to the ZK proof — the sender is
|
|
2776
|
+
* hidden by design, so any address (a relayer included) can submit a valid
|
|
2777
|
+
* proof.
|
|
2631
2778
|
*
|
|
2632
2779
|
* Extrinsic: `shieldedPool.privateTransfer(proof, merkleRoot, nullifiers, commitments, memos)`
|
|
2633
2780
|
*/
|
|
2634
2781
|
async privateTransfer(params, signer) {
|
|
2635
|
-
return signer({ to: this.addr, data:
|
|
2636
|
-
}
|
|
2637
|
-
// ─── unshield ──────────────────────────────────────────────────────────────
|
|
2638
|
-
/**
|
|
2639
|
-
* Params for an `unshield` call via the EVM precompile.
|
|
2640
|
-
* The `recipient` is a full 32-byte AccountId32 (Substrate account or
|
|
2641
|
-
* EeSuffix-derived: `H160 ++ [0x00; 12]`).
|
|
2642
|
-
*/
|
|
2643
|
-
buildUnshieldCalldata(params) {
|
|
2644
|
-
const proof = params.proof;
|
|
2645
|
-
const root = fromHex(params.merkleRoot);
|
|
2646
|
-
const nullifier = fromHex(params.nullifier);
|
|
2647
|
-
const recipientRaw = params.recipientAddress.startsWith("0x") ? params.recipientAddress.slice(2) : params.recipientAddress;
|
|
2648
|
-
const recipientBytes = fromHex(
|
|
2649
|
-
"0x" + (recipientRaw.length === 64 ? recipientRaw : recipientRaw.padEnd(64, "0"))
|
|
2650
|
-
);
|
|
2651
|
-
const changeCommitmentHex = params.changeCommitment ?? "0x" + "00".repeat(32);
|
|
2652
|
-
const changeCommitment = fromHex(changeCommitmentHex);
|
|
2653
|
-
const changeEncryptedMemo = params.changeEncryptedMemo ?? new Uint8Array();
|
|
2654
|
-
return encodeHex(
|
|
2655
|
-
SP_SEL.UNSHIELD,
|
|
2656
|
-
{ type: "bytes", value: proof },
|
|
2657
|
-
{ type: "bytes32", value: root },
|
|
2658
|
-
{ type: "bytes32", value: nullifier },
|
|
2659
|
-
{ type: "uint", value: BigInt(params.assetId) },
|
|
2660
|
-
{ type: "uint", value: params.amount },
|
|
2661
|
-
{ type: "bytes32", value: recipientBytes },
|
|
2662
|
-
{ type: "uint", value: params.fee ?? 0n },
|
|
2663
|
-
{ type: "bytes32", value: changeCommitment },
|
|
2664
|
-
{ type: "bytes", value: changeEncryptedMemo },
|
|
2665
|
-
{ type: "uint", value: BigInt(params.circuitVersion) }
|
|
2666
|
-
);
|
|
2782
|
+
return signer({ to: this.addr, data: buildPrivateTransferCalldata(params) });
|
|
2667
2783
|
}
|
|
2784
|
+
// ─── unshield ────────────────────────────────────────────────────────────
|
|
2668
2785
|
/**
|
|
2669
2786
|
* Withdraws tokens from the shielded pool to a recipient account.
|
|
2670
2787
|
*
|
|
2671
|
-
* `params.recipientAddress` must be a 0x-prefixed
|
|
2672
|
-
*
|
|
2673
|
-
* `@orbinum/sdk` to derive the AccountId32 first.
|
|
2788
|
+
* `params.recipientAddress` must be a 0x-prefixed AccountId32. To send to an
|
|
2789
|
+
* EVM address, derive it first with `evmToImplicitSubstrate(evmAddr)`.
|
|
2674
2790
|
*
|
|
2675
2791
|
* Extrinsic: `shieldedPool.unshield(proof, merkleRoot, nullifier, assetId, amount, recipient)`
|
|
2676
2792
|
*/
|
|
2677
2793
|
async unshield(params, signer) {
|
|
2678
|
-
return signer({ to: this.addr, data:
|
|
2794
|
+
return signer({ to: this.addr, data: buildUnshieldCalldata(params) });
|
|
2679
2795
|
}
|
|
2680
|
-
// ───
|
|
2796
|
+
// ─── claimShieldedFees ───────────────────────────────────────────────────
|
|
2681
2797
|
/**
|
|
2682
|
-
*
|
|
2683
|
-
*
|
|
2798
|
+
* Claims accrued relay fees as a private shielded note.
|
|
2799
|
+
*
|
|
2800
|
+
* For validators/relayers holding fees in `pallet-relayer` who want them
|
|
2801
|
+
* paid privately into the shielded pool rather than as a public balance
|
|
2802
|
+
* credit. The ZK `value_proof` binds `commitment` to
|
|
2803
|
+
* `(amount, assetId, ownerPk, blinding)`, so the runtime can verify the note
|
|
2804
|
+
* encodes exactly the claimed amount and a malicious relayer cannot inflate
|
|
2805
|
+
* the withdrawal.
|
|
2806
|
+
*
|
|
2807
|
+
* The `msg.sender` address is the validator identity, and must match the
|
|
2808
|
+
* one with pending fees.
|
|
2809
|
+
*
|
|
2810
|
+
* Extrinsic: `shieldedPool.claim_shielded_fees(commitment, amount, assetId, memo, proof, publicSignals)`
|
|
2684
2811
|
*/
|
|
2685
|
-
async
|
|
2686
|
-
return
|
|
2687
|
-
from,
|
|
2812
|
+
async claimShieldedFees(params, signer) {
|
|
2813
|
+
return signer({
|
|
2688
2814
|
to: this.addr,
|
|
2689
|
-
data:
|
|
2815
|
+
data: buildClaimShieldedFeesCalldata(params)
|
|
2690
2816
|
});
|
|
2691
2817
|
}
|
|
2692
|
-
|
|
2693
|
-
|
|
2694
|
-
|
|
2818
|
+
// ─── Gas estimation ──────────────────────────────────────────────────────
|
|
2819
|
+
//
|
|
2820
|
+
// `from` must be the real sender: the precompile resolves it to an
|
|
2821
|
+
// AccountId32, so estimating from a different address measures a different
|
|
2822
|
+
// call.
|
|
2823
|
+
async estimateShieldGas(params, from) {
|
|
2824
|
+
return this.evm.estimateGas({ from, to: this.addr, data: buildShieldCalldata(params) });
|
|
2825
|
+
}
|
|
2695
2826
|
async estimatePrivateTransferGas(params, from) {
|
|
2696
2827
|
return this.evm.estimateGas({
|
|
2697
2828
|
from,
|
|
2698
2829
|
to: this.addr,
|
|
2699
|
-
data:
|
|
2830
|
+
data: buildPrivateTransferCalldata(params)
|
|
2700
2831
|
});
|
|
2701
2832
|
}
|
|
2702
|
-
/**
|
|
2703
|
-
* Estimates the EVM gas for an `unshield` call.
|
|
2704
|
-
*/
|
|
2705
2833
|
async estimateUnshieldGas(params, from) {
|
|
2706
|
-
return this.evm.estimateGas({
|
|
2707
|
-
from,
|
|
2708
|
-
to: this.addr,
|
|
2709
|
-
data: this.buildUnshieldCalldata(params)
|
|
2710
|
-
});
|
|
2711
|
-
}
|
|
2712
|
-
// ─── claimShieldedFees ───────────────────────────────────────────────────────────────────
|
|
2713
|
-
/**
|
|
2714
|
-
* Returns the ABI-encoded calldata for
|
|
2715
|
-
* `claimShieldedFees(bytes32,uint256,uint32,bytes,bytes,bytes,uint32)`.
|
|
2716
|
-
*
|
|
2717
|
-
* ABI layout (params after selector):
|
|
2718
|
-
* - `commitment` — bytes32 (fixed)
|
|
2719
|
-
* - `amount` — uint256 (fixed)
|
|
2720
|
-
* - `asset_id` — uint32 (fixed, right-aligned)
|
|
2721
|
-
* - `memo` — bytes (dynamic)
|
|
2722
|
-
* - `proof` — bytes (dynamic, 128 bytes Groth16)
|
|
2723
|
-
* - `publicSignals` — bytes (dynamic, 76 bytes)
|
|
2724
|
-
* - `circuitVersion` — uint32 (fixed, right-aligned)
|
|
2725
|
-
*
|
|
2726
|
-
* The validator identity is derived from `msg.sender` in the precompile —
|
|
2727
|
-
* do NOT include it in the calldata.
|
|
2728
|
-
*/
|
|
2729
|
-
buildClaimShieldedFeesCalldata(params) {
|
|
2730
|
-
EncryptedMemo.validate(
|
|
2731
|
-
params.encryptedMemo,
|
|
2732
|
-
"buildClaimShieldedFeesCalldata.encryptedMemo"
|
|
2733
|
-
);
|
|
2734
|
-
if (params.proof.length === 0) {
|
|
2735
|
-
throw new Error("claimShieldedFees: proof must not be empty");
|
|
2736
|
-
}
|
|
2737
|
-
if (params.publicSignals.length !== 76) {
|
|
2738
|
-
throw new Error(
|
|
2739
|
-
`claimShieldedFees: publicSignals must be 76 bytes, got ${params.publicSignals.length}`
|
|
2740
|
-
);
|
|
2741
|
-
}
|
|
2742
|
-
const commitment = fromHex(params.commitment);
|
|
2743
|
-
return encodeHex(
|
|
2744
|
-
SP_SEL.CLAIM_SHIELDED_FEES,
|
|
2745
|
-
{ type: "bytes32", value: commitment },
|
|
2746
|
-
{ type: "uint", value: params.amount },
|
|
2747
|
-
{ type: "uint", value: BigInt(params.assetId) },
|
|
2748
|
-
{ type: "bytes", value: params.encryptedMemo },
|
|
2749
|
-
{ type: "bytes", value: params.proof },
|
|
2750
|
-
{ type: "bytes", value: params.publicSignals },
|
|
2751
|
-
{ type: "uint", value: BigInt(params.circuitVersion) }
|
|
2752
|
-
);
|
|
2753
|
-
}
|
|
2754
|
-
/**
|
|
2755
|
-
* Claims accumulated relay fees as a private shielded note.
|
|
2756
|
-
*
|
|
2757
|
-
* This extrinsic is for **validators/relayers** who have accrued fees in
|
|
2758
|
-
* `pallet-relayer` and want to receive them privately inside the shielded pool
|
|
2759
|
-
* instead of as a public balance credit.
|
|
2760
|
-
*
|
|
2761
|
-
* The ZK `value_proof` binds `commitment` to `(amount, assetId, ownerPk, blinding)`
|
|
2762
|
-
* so the runtime can verify the note encodes exactly the claimed fee amount,
|
|
2763
|
-
* preventing a malicious relayer from inflating the withdrawal.
|
|
2764
|
-
*
|
|
2765
|
-
* The `msg.sender` EVM address is used as the validator identity; it must match
|
|
2766
|
-
* the address that has pending relay fees in `pallet-relayer`.
|
|
2767
|
-
*
|
|
2768
|
-
* Extrinsic: `shieldedPool.claim_shielded_fees(commitment, amount, assetId, memo, proof, publicSignals)`
|
|
2769
|
-
*/
|
|
2770
|
-
async claimShieldedFees(params, signer) {
|
|
2771
|
-
return signer({
|
|
2772
|
-
to: this.addr,
|
|
2773
|
-
data: this.buildClaimShieldedFeesCalldata(params)
|
|
2774
|
-
});
|
|
2834
|
+
return this.evm.estimateGas({ from, to: this.addr, data: buildUnshieldCalldata(params) });
|
|
2775
2835
|
}
|
|
2776
|
-
/**
|
|
2777
|
-
* Estimates the EVM gas for a `claimShieldedFees` call.
|
|
2778
|
-
*/
|
|
2779
2836
|
async estimateClaimShieldedFeesGas(params, from) {
|
|
2780
2837
|
return this.evm.estimateGas({
|
|
2781
2838
|
from,
|
|
2782
2839
|
to: this.addr,
|
|
2783
|
-
data:
|
|
2840
|
+
data: buildClaimShieldedFeesCalldata(params)
|
|
2784
2841
|
});
|
|
2785
2842
|
}
|
|
2786
2843
|
};
|
|
@@ -2952,7 +3009,7 @@ var OrbinumClient = class _OrbinumClient {
|
|
|
2952
3009
|
*/
|
|
2953
3010
|
static async connect(config) {
|
|
2954
3011
|
const substrate = config.papi ? SubstrateClient.adopt(config.papi, config.substrateHttp) : await SubstrateClient.connect(config.substrateWs, config.connectTimeoutMs ?? 15e3);
|
|
2955
|
-
const evm = config.evmRpc ? new EvmClient(config.evmRpc) : null;
|
|
3012
|
+
const evm = config.evmRpc ? new EvmClient(config.evmRpc, config.evmRpcPeer) : null;
|
|
2956
3013
|
return new _OrbinumClient(substrate, evm, config.circuitsBaseUrl);
|
|
2957
3014
|
}
|
|
2958
3015
|
/**
|
|
@@ -3092,6 +3149,7 @@ var OrbinumClientProvider = class _OrbinumClientProvider {
|
|
|
3092
3149
|
connectTimeoutMs: this.connectTimeoutMs
|
|
3093
3150
|
};
|
|
3094
3151
|
if (this.config.evmRpc) connectConfig.evmRpc = this.config.evmRpc;
|
|
3152
|
+
if (this.config.evmRpcPeer) connectConfig.evmRpcPeer = this.config.evmRpcPeer;
|
|
3095
3153
|
if (this.config.circuitsBaseUrl)
|
|
3096
3154
|
connectConfig.circuitsBaseUrl = this.config.circuitsBaseUrl;
|
|
3097
3155
|
clientPromise = OrbinumClient.connect(connectConfig);
|
|
@@ -3880,6 +3938,9 @@ function methodOf(fnSig) {
|
|
|
3880
3938
|
if (fnSig.startsWith("shield(")) return "shield";
|
|
3881
3939
|
return null;
|
|
3882
3940
|
}
|
|
3941
|
+
function hasFullHead(data, slots) {
|
|
3942
|
+
return data.length >= slots * 32;
|
|
3943
|
+
}
|
|
3883
3944
|
function decodePrecompileCalldata(address, input) {
|
|
3884
3945
|
const info = KNOWN_PRECOMPILES[address.toLowerCase()];
|
|
3885
3946
|
if (!info || !input || input.length < 10) return null;
|
|
@@ -3889,6 +3950,7 @@ function decodePrecompileCalldata(address, input) {
|
|
|
3889
3950
|
if (fnSig.startsWith("shield(")) {
|
|
3890
3951
|
try {
|
|
3891
3952
|
const data = fromHex(input.slice(10));
|
|
3953
|
+
if (!hasFullHead(data, 3)) return { fnSig, method: methodOf(fnSig), args: {} };
|
|
3892
3954
|
const assetId = decodeUint(data, 0);
|
|
3893
3955
|
const commitment = toHex(data.slice(32, 64));
|
|
3894
3956
|
return { fnSig, method: methodOf(fnSig), args: { assetId, commitment } };
|
|
@@ -3899,6 +3961,7 @@ function decodePrecompileCalldata(address, input) {
|
|
|
3899
3961
|
if (fnSig.startsWith("unshield(")) {
|
|
3900
3962
|
try {
|
|
3901
3963
|
const data = fromHex(input.slice(10));
|
|
3964
|
+
if (!hasFullHead(data, 10)) return { fnSig, method: methodOf(fnSig), args: {} };
|
|
3902
3965
|
const root = toHex(data.slice(32, 64));
|
|
3903
3966
|
const nullifier = toHex(data.slice(64, 96));
|
|
3904
3967
|
const assetId = decodeUint(data, 96);
|
|
@@ -3928,18 +3991,25 @@ function decodePrecompileCalldata(address, input) {
|
|
|
3928
3991
|
if (fnSig.startsWith("privateTransfer(")) {
|
|
3929
3992
|
try {
|
|
3930
3993
|
const data = fromHex(input.slice(10));
|
|
3994
|
+
if (!hasFullHead(data, 8)) return { fnSig, method: methodOf(fnSig), args: {} };
|
|
3931
3995
|
const root = toHex(data.slice(32, 64));
|
|
3932
|
-
const nullOffset = Number(decodeUint(data, 64));
|
|
3933
|
-
const commOffset = Number(decodeUint(data, 96));
|
|
3934
|
-
const nullifiers = Number(decodeUint(data, nullOffset));
|
|
3935
|
-
const commitments = Number(decodeUint(data, commOffset));
|
|
3936
3996
|
const assetId = decodeUint(data, 160);
|
|
3937
3997
|
const fee = decodeUint(data, 192);
|
|
3938
3998
|
const circuitVersion = decodeUint(data, 224);
|
|
3999
|
+
const counts = {};
|
|
4000
|
+
for (const [name, slot] of [
|
|
4001
|
+
["nullifiers", 64],
|
|
4002
|
+
["commitments", 96]
|
|
4003
|
+
]) {
|
|
4004
|
+
const offset = decodeUint(data, slot);
|
|
4005
|
+
if (offset <= BigInt(data.length - 32)) {
|
|
4006
|
+
counts[name] = Number(decodeUint(data, Number(offset)));
|
|
4007
|
+
}
|
|
4008
|
+
}
|
|
3939
4009
|
return {
|
|
3940
4010
|
fnSig,
|
|
3941
4011
|
method: methodOf(fnSig),
|
|
3942
|
-
args: { root,
|
|
4012
|
+
args: { root, ...counts, assetId, fee, circuitVersion }
|
|
3943
4013
|
};
|
|
3944
4014
|
} catch {
|
|
3945
4015
|
return { fnSig, method: methodOf(fnSig), args: {} };
|
|
@@ -3948,6 +4018,7 @@ function decodePrecompileCalldata(address, input) {
|
|
|
3948
4018
|
if (fnSig.startsWith("claimShieldedFees(")) {
|
|
3949
4019
|
try {
|
|
3950
4020
|
const data = fromHex(input.slice(10));
|
|
4021
|
+
if (!hasFullHead(data, 7)) return { fnSig, method: methodOf(fnSig), args: {} };
|
|
3951
4022
|
const commitment = toHex(data.slice(0, 32));
|
|
3952
4023
|
const amount = decodeUint(data, 32);
|
|
3953
4024
|
const assetId = decodeUint(data, 64);
|
|
@@ -4154,7 +4225,7 @@ var MemoryVaultStorage = class {
|
|
|
4154
4225
|
};
|
|
4155
4226
|
|
|
4156
4227
|
// src/wallet/vault/storage/config.ts
|
|
4157
|
-
var VAULT_SCHEMA_VERSION =
|
|
4228
|
+
var VAULT_SCHEMA_VERSION = 5;
|
|
4158
4229
|
function normalizeChainFingerprint(chainFingerprint) {
|
|
4159
4230
|
return chainFingerprint ? chainFingerprint.toLowerCase() : void 0;
|
|
4160
4231
|
}
|
|
@@ -4188,13 +4259,29 @@ function mergeCounters(current, fallback) {
|
|
|
4188
4259
|
...parties !== void 0 ? { pairwiseCounterparties: parties } : {}
|
|
4189
4260
|
};
|
|
4190
4261
|
}
|
|
4191
|
-
var
|
|
4262
|
+
var counterOrNone = (value) => typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
4263
|
+
var higher = (a, b) => {
|
|
4264
|
+
const x = counterOrNone(a);
|
|
4265
|
+
const y = counterOrNone(b);
|
|
4266
|
+
return x === void 0 ? y : y === void 0 ? x : Math.max(x, y);
|
|
4267
|
+
};
|
|
4268
|
+
function usableEntry(entry) {
|
|
4269
|
+
if (typeof entry !== "object" || entry === null) return void 0;
|
|
4270
|
+
const { nextIndex, addedAt } = entry;
|
|
4271
|
+
const index = counterOrNone(nextIndex);
|
|
4272
|
+
if (index === void 0) return void 0;
|
|
4273
|
+
return { nextIndex: index, addedAt: counterOrNone(addedAt) ?? Date.now() };
|
|
4274
|
+
}
|
|
4192
4275
|
function mergePairwise(current, fallback) {
|
|
4193
4276
|
if (!current && !fallback) return void 0;
|
|
4194
|
-
const merged = {
|
|
4195
|
-
|
|
4196
|
-
|
|
4197
|
-
|
|
4277
|
+
const merged = {};
|
|
4278
|
+
for (const [key, entry] of Object.entries(fallback ?? {})) {
|
|
4279
|
+
const usable = usableEntry(entry);
|
|
4280
|
+
if (usable) merged[key] = usable;
|
|
4281
|
+
}
|
|
4282
|
+
for (const [key, raw] of Object.entries(current ?? {})) {
|
|
4283
|
+
const entry = usableEntry(raw);
|
|
4284
|
+
if (!entry) continue;
|
|
4198
4285
|
const prev = merged[key];
|
|
4199
4286
|
merged[key] = prev ? {
|
|
4200
4287
|
nextIndex: Math.max(prev.nextIndex, entry.nextIndex),
|
|
@@ -4205,33 +4292,42 @@ function mergePairwise(current, fallback) {
|
|
|
4205
4292
|
}
|
|
4206
4293
|
|
|
4207
4294
|
// src/wallet/vault/storage/ephemeralIndex.ts
|
|
4295
|
+
var MAX_EPH_INDEX = 4294967294;
|
|
4296
|
+
function usableCounter(value) {
|
|
4297
|
+
if (typeof value !== "number" || !Number.isInteger(value)) return null;
|
|
4298
|
+
if (value < 0 || value > MAX_EPH_INDEX) return null;
|
|
4299
|
+
return value;
|
|
4300
|
+
}
|
|
4208
4301
|
async function reserveSelfEphIndex(storage) {
|
|
4209
4302
|
const updated = await storage.updateConfig((config) => ({
|
|
4210
4303
|
...config,
|
|
4211
|
-
selfEphCounter: (config.selfEphCounter ?? 0) + 1,
|
|
4304
|
+
selfEphCounter: (usableCounter(config.selfEphCounter) ?? 0) + 1,
|
|
4212
4305
|
updatedAt: Date.now()
|
|
4213
4306
|
}));
|
|
4214
4307
|
if (!updated) throw new Error("vault not initialized");
|
|
4215
|
-
return (updated.selfEphCounter ?? 1) - 1;
|
|
4308
|
+
return (usableCounter(updated.selfEphCounter) ?? 1) - 1;
|
|
4216
4309
|
}
|
|
4217
4310
|
async function reservePairwiseIndex(storage, ivkHex) {
|
|
4218
4311
|
const key = ivkHex.toLowerCase();
|
|
4219
4312
|
const updated = await storage.updateConfig((config) => {
|
|
4220
|
-
const
|
|
4313
|
+
const stored = usableCounter(config.pairwiseCounterparties?.[key]?.nextIndex);
|
|
4314
|
+
const addedAt = config.pairwiseCounterparties?.[key]?.addedAt;
|
|
4221
4315
|
return {
|
|
4222
4316
|
...config,
|
|
4223
4317
|
pairwiseCounterparties: {
|
|
4224
4318
|
...config.pairwiseCounterparties,
|
|
4225
4319
|
[key]: {
|
|
4226
|
-
nextIndex: (
|
|
4227
|
-
addedAt:
|
|
4320
|
+
nextIndex: (stored ?? 0) + 1,
|
|
4321
|
+
addedAt: typeof addedAt === "number" ? addedAt : Date.now()
|
|
4228
4322
|
}
|
|
4229
4323
|
},
|
|
4230
4324
|
updatedAt: Date.now()
|
|
4231
4325
|
};
|
|
4232
4326
|
});
|
|
4233
4327
|
if (!updated) throw new Error("vault not initialized");
|
|
4234
|
-
|
|
4328
|
+
const nextIndex = usableCounter(updated.pairwiseCounterparties?.[key]?.nextIndex);
|
|
4329
|
+
if (nextIndex === null || nextIndex <= 1) return null;
|
|
4330
|
+
return nextIndex - 1;
|
|
4235
4331
|
}
|
|
4236
4332
|
|
|
4237
4333
|
// src/wallet/vault/notes/record.ts
|
|
@@ -4278,7 +4374,7 @@ async function noteBlindTag(blindKey, hex) {
|
|
|
4278
4374
|
|
|
4279
4375
|
// src/wallet/vault/notes/meta.ts
|
|
4280
4376
|
function noteOrigin(note) {
|
|
4281
|
-
return note.
|
|
4377
|
+
return note.sourcePk === 0n ? "shield" : "private-transfer";
|
|
4282
4378
|
}
|
|
4283
4379
|
function noteCreatedAt(note) {
|
|
4284
4380
|
return note.createdAt ?? null;
|
|
@@ -4347,9 +4443,9 @@ var NOTE_BIGINT_FIELDS = [
|
|
|
4347
4443
|
"spendingKey",
|
|
4348
4444
|
"commitment",
|
|
4349
4445
|
"nullifier",
|
|
4350
|
-
"
|
|
4446
|
+
"sourcePk"
|
|
4351
4447
|
];
|
|
4352
|
-
var ABSENT_MEANS_ZERO = /* @__PURE__ */ new Set(["
|
|
4448
|
+
var ABSENT_MEANS_ZERO = /* @__PURE__ */ new Set(["sourcePk"]);
|
|
4353
4449
|
function normalizeNote(note) {
|
|
4354
4450
|
let patch = null;
|
|
4355
4451
|
for (const field of NOTE_BIGINT_FIELDS) {
|
|
@@ -4602,12 +4698,12 @@ var VaultStore = class {
|
|
|
4602
4698
|
const { blindKey } = this.keys();
|
|
4603
4699
|
if (commitmentHexes.length === 0) return 0;
|
|
4604
4700
|
const toRemove = new Set(commitmentHexes);
|
|
4605
|
-
const
|
|
4606
|
-
if (
|
|
4607
|
-
const tags = await Promise.all(
|
|
4701
|
+
const present2 = this.deps.notes.get().filter((n) => toRemove.has(n.commitmentHex));
|
|
4702
|
+
if (present2.length === 0) return 0;
|
|
4703
|
+
const tags = await Promise.all(present2.map((n) => noteBlindTag(blindKey, n.commitmentHex)));
|
|
4608
4704
|
await this.deps.storage.deleteNotes(tags);
|
|
4609
4705
|
this.deps.notes.set(removeByCommitment(this.deps.notes.get(), toRemove));
|
|
4610
|
-
return
|
|
4706
|
+
return present2.length;
|
|
4611
4707
|
}
|
|
4612
4708
|
/**
|
|
4613
4709
|
* Stores an outgoing transaction, encrypted.
|
|
@@ -4998,7 +5094,7 @@ function gapMargin(windowSize) {
|
|
|
4998
5094
|
}
|
|
4999
5095
|
function resolveSelfEphCeiling(params) {
|
|
5000
5096
|
const { notes, spendingKey, viewingKey, scanMaxIndex } = params;
|
|
5001
|
-
const windowSize = params.windowSize
|
|
5097
|
+
const windowSize = params.windowSize !== void 0 && params.windowSize > 0 ? params.windowSize : SELF_EPH_WINDOW;
|
|
5002
5098
|
if (scanMaxIndex === null) return null;
|
|
5003
5099
|
if (scanMaxIndex < windowSize - gapMargin(windowSize)) return scanMaxIndex;
|
|
5004
5100
|
const ownEphPks = /* @__PURE__ */ new Set();
|
|
@@ -5023,9 +5119,12 @@ function resolveSelfEphCeiling(params) {
|
|
|
5023
5119
|
}
|
|
5024
5120
|
return max;
|
|
5025
5121
|
}
|
|
5122
|
+
var MAX_EPH_WINDOW = SELF_EPH_WINDOW * 64;
|
|
5026
5123
|
function windowSizeForCounter(counter) {
|
|
5027
|
-
const
|
|
5028
|
-
|
|
5124
|
+
const used = Number.isFinite(counter) ? counter : 0;
|
|
5125
|
+
const needed = used + gapMargin(SELF_EPH_WINDOW);
|
|
5126
|
+
const rounded = Math.ceil(needed / SELF_EPH_WINDOW) * SELF_EPH_WINDOW;
|
|
5127
|
+
return Math.min(MAX_EPH_WINDOW, Math.max(SELF_EPH_WINDOW, rounded));
|
|
5029
5128
|
}
|
|
5030
5129
|
|
|
5031
5130
|
// src/wallet/scanner/phases/spentStatus.ts
|
|
@@ -5195,26 +5294,147 @@ function parseFeeArg(argsJson) {
|
|
|
5195
5294
|
}
|
|
5196
5295
|
}
|
|
5197
5296
|
|
|
5297
|
+
// src/wallet/provenance/selectDescribingNote.ts
|
|
5298
|
+
function hasSourcePk(note) {
|
|
5299
|
+
return typeof note.sourcePk === "bigint" && note.sourcePk !== 0n;
|
|
5300
|
+
}
|
|
5301
|
+
function selectDescribingNote(candidates) {
|
|
5302
|
+
return candidates.find(hasSourcePk) ?? candidates[0];
|
|
5303
|
+
}
|
|
5304
|
+
function selectDescribingNoteByCommitment(commitments, noteByCommitment) {
|
|
5305
|
+
const owned = commitments.map((hex) => noteByCommitment.get(hex)).filter((note) => note !== void 0);
|
|
5306
|
+
return selectDescribingNote(owned);
|
|
5307
|
+
}
|
|
5308
|
+
|
|
5309
|
+
// src/wallet/provenance/merge.ts
|
|
5310
|
+
var SOURCE_RANK = {
|
|
5311
|
+
witnessed: 3,
|
|
5312
|
+
memo: 2,
|
|
5313
|
+
chain: 1,
|
|
5314
|
+
inferred: 0
|
|
5315
|
+
};
|
|
5316
|
+
function rankOf(source) {
|
|
5317
|
+
return SOURCE_RANK[source] ?? -1;
|
|
5318
|
+
}
|
|
5319
|
+
function outranks(a, b) {
|
|
5320
|
+
return rankOf(a) > rankOf(b);
|
|
5321
|
+
}
|
|
5322
|
+
function mergeProvenance(existing, incoming) {
|
|
5323
|
+
if (existing.id !== incoming.id) {
|
|
5324
|
+
throw new Error(
|
|
5325
|
+
`mergeProvenance: id mismatch \u2014 refusing to merge ${existing.id} with ${incoming.id}`
|
|
5326
|
+
);
|
|
5327
|
+
}
|
|
5328
|
+
const incomingWins = outranks(incoming.source, existing.source);
|
|
5329
|
+
const base = incomingWins ? incoming : existing;
|
|
5330
|
+
const other = incomingWins ? existing : incoming;
|
|
5331
|
+
const fee = base.feePlanck ?? other.feePlanck;
|
|
5332
|
+
const publicRecipient = firstPresent(base.publicRecipient, other.publicRecipient);
|
|
5333
|
+
const slip = present(base.slip?.encoded) ? base.slip : other.slip ?? base.slip;
|
|
5334
|
+
const note = base.note ?? other.note;
|
|
5335
|
+
return {
|
|
5336
|
+
// The loser is spread FIRST so a field only it carries survives. A host
|
|
5337
|
+
// stores its own record type through this — `ReconstructedTxRecord` has
|
|
5338
|
+
// `amountApproximate`, which marks an amount derived without subtracting
|
|
5339
|
+
// the fee — and dropping such a field turns an approximate figure into
|
|
5340
|
+
// one that merely looks exact. Every key the winner knows about is
|
|
5341
|
+
// overwritten below, so rank still decides every shared fact.
|
|
5342
|
+
...other,
|
|
5343
|
+
...base,
|
|
5344
|
+
// A known peer beats an unknown one even when the winner is silent:
|
|
5345
|
+
// backfilling the recipient is the whole point of re-running this.
|
|
5346
|
+
// `scope: 'none'` is "this operation has no counterparty", so it is a
|
|
5347
|
+
// gap too — treating it as known would block the backfill it exists for.
|
|
5348
|
+
peer: copyPeer(knownPeer(base.peer) ?? knownPeer(other.peer) ?? base.peer ?? other.peer),
|
|
5349
|
+
// A number that stands for "not known yet" must not win by rank. Zero is
|
|
5350
|
+
// exactly that here: `RECOVERED_TX_RESULT` and a failed submission both
|
|
5351
|
+
// report block 0, and their own comment says a caller who needs the
|
|
5352
|
+
// value must look it up rather than trust the field.
|
|
5353
|
+
blockNumber: firstPositive(base.blockNumber, other.blockNumber),
|
|
5354
|
+
timestampMs: firstPositive(base.timestampMs, other.timestampMs),
|
|
5355
|
+
// Reconstruction writes an empty hash when the extrinsic was not decoded.
|
|
5356
|
+
hash: firstPresent(base.hash, other.hash) ?? base.hash,
|
|
5357
|
+
// `exact` is a property of the FIGURE, not of the source. A `witnessed`
|
|
5358
|
+
// row whose amount was marked approximate is not better data than an
|
|
5359
|
+
// exact one from a weaker source, so rank only breaks a tie between two
|
|
5360
|
+
// figures of equal standing.
|
|
5361
|
+
amount: { ...betterAmount(base.amount, other.amount) },
|
|
5362
|
+
// The chain's outcome is not something one source knows better than
|
|
5363
|
+
// another — anyone who looks sees the same thing. Letting rank decide
|
|
5364
|
+
// would let a row written at submit time mark a transaction failed that
|
|
5365
|
+
// the chain went on to accept.
|
|
5366
|
+
status: base.status === "success" || other.status === "success" ? "success" : "failed",
|
|
5367
|
+
...fee !== void 0 && { feePlanck: fee },
|
|
5368
|
+
...publicRecipient !== void 0 && { publicRecipient },
|
|
5369
|
+
...slip !== void 0 && { slip: { ...slip } },
|
|
5370
|
+
...note !== void 0 && { note: { ...note } }
|
|
5371
|
+
};
|
|
5372
|
+
}
|
|
5373
|
+
function betterAmount(base, other) {
|
|
5374
|
+
if (base.exact === other.exact) return base;
|
|
5375
|
+
return base.exact ? base : other;
|
|
5376
|
+
}
|
|
5377
|
+
function present(value) {
|
|
5378
|
+
return value !== void 0 && value.length > 0;
|
|
5379
|
+
}
|
|
5380
|
+
function firstPresent(base, other) {
|
|
5381
|
+
return present(base) ? base : present(other) ? other : void 0;
|
|
5382
|
+
}
|
|
5383
|
+
function firstPositive(base, other) {
|
|
5384
|
+
return base > 0 ? base : other > 0 ? other : base;
|
|
5385
|
+
}
|
|
5386
|
+
function copyPeer(peer) {
|
|
5387
|
+
return peer ? { ...peer } : null;
|
|
5388
|
+
}
|
|
5389
|
+
function knownPeer(peer) {
|
|
5390
|
+
return peer && peer.scope !== "none" ? peer : null;
|
|
5391
|
+
}
|
|
5392
|
+
|
|
5393
|
+
// src/wallet/provenance/regenerateSlip.ts
|
|
5394
|
+
function regeneratePaymentSlip(facts, recipientIvkPacked, txHash) {
|
|
5395
|
+
if (!isHexOfLength(facts.commitmentHex, 32)) {
|
|
5396
|
+
throw new Error("regeneratePaymentSlip: commitmentHex must be 32 bytes of hex");
|
|
5397
|
+
}
|
|
5398
|
+
if (!isHexOfLength(facts.encryptedMemo, ENCRYPTED_MEMO_SIZE)) {
|
|
5399
|
+
throw new Error(
|
|
5400
|
+
`regeneratePaymentSlip: encryptedMemo must be ${ENCRYPTED_MEMO_SIZE} bytes of hex`
|
|
5401
|
+
);
|
|
5402
|
+
}
|
|
5403
|
+
if (facts.leafIndex !== void 0 && !isValidLeafIndex(facts.leafIndex)) {
|
|
5404
|
+
throw new Error("regeneratePaymentSlip: leafIndex must be a real tree position");
|
|
5405
|
+
}
|
|
5406
|
+
const envelope = sealPaymentSlip(recipientIvkPacked, {
|
|
5407
|
+
commitmentHex: facts.commitmentHex,
|
|
5408
|
+
encryptedMemo: facts.encryptedMemo,
|
|
5409
|
+
...facts.leafIndex !== void 0 ? { leafIndex: facts.leafIndex } : {},
|
|
5410
|
+
// Rendered as an explorer link by the recipient, where an unconstrained
|
|
5411
|
+
// string is a URL injection wearing the authority of a decrypted slip.
|
|
5412
|
+
// Dropped rather than fatal: the slip still rebuilds the note, which is
|
|
5413
|
+
// the part that matters.
|
|
5414
|
+
...isHexOfLength(txHash, 32) ? { txHash } : {}
|
|
5415
|
+
});
|
|
5416
|
+
return encodePaymentSlip(envelope);
|
|
5417
|
+
}
|
|
5418
|
+
|
|
5198
5419
|
// src/wallet/scanner/history/reconstruct.ts
|
|
5199
5420
|
var ZERO_PK = "0x" + "00".repeat(32);
|
|
5200
5421
|
function extrinsicKey(row) {
|
|
5201
5422
|
return `${row.blockNumber}:${row.extrinsicIndex ?? "null"}`;
|
|
5202
5423
|
}
|
|
5203
|
-
function toPkHex(
|
|
5204
|
-
return
|
|
5205
|
-
}
|
|
5206
|
-
function findChangeNote(commitments, noteByCommitment) {
|
|
5207
|
-
const candidates = commitments.map((h) => noteByCommitment.get(h)).filter((n) => n !== void 0);
|
|
5208
|
-
return candidates.find((n) => n.counterpartyPk != null && n.counterpartyPk !== 0n) ?? candidates[0];
|
|
5424
|
+
function toPkHex(sourcePk) {
|
|
5425
|
+
return typeof sourcePk === "bigint" && hasSourcePk({ sourcePk }) ? scalarToHex(sourcePk) : ZERO_PK;
|
|
5209
5426
|
}
|
|
5210
5427
|
async function loadExistingRecords(vault) {
|
|
5211
5428
|
try {
|
|
5212
5429
|
const records = await vault.getTxRecords();
|
|
5213
|
-
return new Map(records.map((r) => [r.
|
|
5430
|
+
return new Map(records.map((r) => [r.id, r]));
|
|
5214
5431
|
} catch {
|
|
5215
5432
|
return /* @__PURE__ */ new Map();
|
|
5216
5433
|
}
|
|
5217
5434
|
}
|
|
5435
|
+
function recordKey(transfer) {
|
|
5436
|
+
return transfer.hash ?? `${transfer.blockNumber}-${transfer.extrinsicIndex ?? 0}`;
|
|
5437
|
+
}
|
|
5218
5438
|
async function reconstructOutgoingTxRecords(deps) {
|
|
5219
5439
|
const { vault, transfers } = deps;
|
|
5220
5440
|
const now = deps.now ?? Date.now;
|
|
@@ -5231,17 +5451,17 @@ async function reconstructOutgoingTxRecords(deps) {
|
|
|
5231
5451
|
const commitmentsByExtrinsic = new Map(
|
|
5232
5452
|
commitmentTransfers.map((ct) => [extrinsicKey(ct), ct.matchedCommitments ?? []])
|
|
5233
5453
|
);
|
|
5234
|
-
const
|
|
5454
|
+
const existingByKey = await loadExistingRecords(vault);
|
|
5235
5455
|
for (const transfer of outgoingTransfers) {
|
|
5236
|
-
const existing =
|
|
5456
|
+
const existing = existingByKey.get(recordKey(transfer));
|
|
5237
5457
|
if (existing?.recipientPkHex && existing.recipientPkHex !== ZERO_PK) continue;
|
|
5238
5458
|
const inputNotes = (transfer.matchedNullifiers ?? []).map((h) => noteByNullifier.get(h)).filter((n) => n !== void 0);
|
|
5239
5459
|
if (inputNotes.length === 0) continue;
|
|
5240
|
-
const changeNote =
|
|
5460
|
+
const changeNote = selectDescribingNoteByCommitment(
|
|
5241
5461
|
commitmentsByExtrinsic.get(extrinsicKey(transfer)) ?? [],
|
|
5242
5462
|
noteByCommitment
|
|
5243
5463
|
);
|
|
5244
|
-
const recipientPkHex = toPkHex(changeNote?.
|
|
5464
|
+
const recipientPkHex = toPkHex(changeNote?.sourcePk);
|
|
5245
5465
|
if (existing) {
|
|
5246
5466
|
if (recipientPkHex === ZERO_PK) continue;
|
|
5247
5467
|
await vault.saveTxRecord({ ...existing, recipientPkHex });
|
|
@@ -5252,7 +5472,7 @@ async function reconstructOutgoingTxRecords(deps) {
|
|
|
5252
5472
|
const transferAmount = totalInputValue - (changeNote?.value ?? 0n) - (fee ?? 0n);
|
|
5253
5473
|
if (transferAmount <= 0n) continue;
|
|
5254
5474
|
const record = {
|
|
5255
|
-
id: transfer
|
|
5475
|
+
id: recordKey(transfer),
|
|
5256
5476
|
type: "private_transfer",
|
|
5257
5477
|
blockNumber: transfer.blockNumber,
|
|
5258
5478
|
hash: transfer.hash ?? "",
|
|
@@ -5283,11 +5503,13 @@ async function buildZkNote(params, deps) {
|
|
|
5283
5503
|
} else if (params.viewingPublicKey && storage && keys.viewingSecretKey) {
|
|
5284
5504
|
try {
|
|
5285
5505
|
const index = await reservePairwiseIndex(storage, toHex(params.viewingPublicKey));
|
|
5286
|
-
|
|
5287
|
-
|
|
5288
|
-
|
|
5289
|
-
|
|
5290
|
-
|
|
5506
|
+
if (index !== null) {
|
|
5507
|
+
const pairSecret = derivePairwiseSharedSecret(
|
|
5508
|
+
keys.viewingSecretKey,
|
|
5509
|
+
params.viewingPublicKey
|
|
5510
|
+
);
|
|
5511
|
+
ephSkOverride = derivePairwiseEphSk(pairSecret, index);
|
|
5512
|
+
}
|
|
5291
5513
|
} catch {
|
|
5292
5514
|
}
|
|
5293
5515
|
}
|
|
@@ -5301,7 +5523,7 @@ async function buildZkNote(params, deps) {
|
|
|
5301
5523
|
circuitVersion: params.circuitVersion,
|
|
5302
5524
|
// Omitted rather than passed as undefined: the builder distinguishes an
|
|
5303
5525
|
// absent recipient (a self note) from one explicitly set.
|
|
5304
|
-
...params.
|
|
5526
|
+
...params.sourcePk !== void 0 && { sourcePk: params.sourcePk },
|
|
5305
5527
|
...params.recipientOwnerPk !== void 0 && {
|
|
5306
5528
|
recipientOwnerPk: params.recipientOwnerPk
|
|
5307
5529
|
},
|
|
@@ -5620,7 +5842,7 @@ async function transferNotes(deps, params, onProgress) {
|
|
|
5620
5842
|
value: transferAmount,
|
|
5621
5843
|
assetId: noteA.assetId,
|
|
5622
5844
|
ownerPk: recipientPk,
|
|
5623
|
-
|
|
5845
|
+
sourcePk: effectiveSenderPk,
|
|
5624
5846
|
// Undefined → dummy memo; the recipient finds the note by scanning.
|
|
5625
5847
|
...recipientViewingPublicKey !== void 0 ? { viewingPublicKey: recipientViewingPublicKey } : {},
|
|
5626
5848
|
// With a viewing key present this activates stealth derivation.
|
|
@@ -5631,7 +5853,7 @@ async function transferNotes(deps, params, onProgress) {
|
|
|
5631
5853
|
assetId: noteA.assetId,
|
|
5632
5854
|
ownerPk: effectiveSenderPk,
|
|
5633
5855
|
spendingKey: noteA.spendingKey,
|
|
5634
|
-
|
|
5856
|
+
sourcePk: recipientNote.ownerPk,
|
|
5635
5857
|
viewingPublicKey: senderViewingPublicKey
|
|
5636
5858
|
});
|
|
5637
5859
|
onProgress?.("generating-zk");
|
|
@@ -6385,6 +6607,7 @@ export {
|
|
|
6385
6607
|
clearKnownEphWindow,
|
|
6386
6608
|
clearSession,
|
|
6387
6609
|
collectNullifiersToQuery,
|
|
6610
|
+
collectOutgoingFacts,
|
|
6388
6611
|
collectScanEntries,
|
|
6389
6612
|
commitmentHexOf,
|
|
6390
6613
|
computeNoteCommitment,
|
|
@@ -6460,6 +6683,7 @@ export {
|
|
|
6460
6683
|
getPolkadotSignerFromPjs as getSubstrateSignerFromExtension,
|
|
6461
6684
|
hasCachedSession,
|
|
6462
6685
|
hasInjectedExtensions,
|
|
6686
|
+
hasSourcePk,
|
|
6463
6687
|
hexToBigint,
|
|
6464
6688
|
hexToNumber,
|
|
6465
6689
|
implicitSubstrateToEvm,
|
|
@@ -6471,6 +6695,7 @@ export {
|
|
|
6471
6695
|
isConnectionLossError,
|
|
6472
6696
|
isEvmAddress,
|
|
6473
6697
|
isGhostNoteError,
|
|
6698
|
+
isHexOfLength,
|
|
6474
6699
|
isImplicitEvmAccount,
|
|
6475
6700
|
isNativeAsset,
|
|
6476
6701
|
isNoteSelfConsistent,
|
|
@@ -6483,6 +6708,7 @@ export {
|
|
|
6483
6708
|
mapExtrinsicArgs,
|
|
6484
6709
|
mapZkEventData,
|
|
6485
6710
|
markInputsSpent,
|
|
6711
|
+
mergeProvenance,
|
|
6486
6712
|
normalizeChainFingerprint,
|
|
6487
6713
|
normalizeEvmAddress,
|
|
6488
6714
|
normalizeNote,
|
|
@@ -6497,6 +6723,7 @@ export {
|
|
|
6497
6723
|
noteTxKind,
|
|
6498
6724
|
openOutgoingBlob,
|
|
6499
6725
|
openPaymentSlip,
|
|
6726
|
+
outranks,
|
|
6500
6727
|
pairwiseEphWindow,
|
|
6501
6728
|
palletErrorKind,
|
|
6502
6729
|
parseAmount,
|
|
@@ -6511,6 +6738,7 @@ export {
|
|
|
6511
6738
|
recoverOwnerPkPoint,
|
|
6512
6739
|
recoverSelfStealthNote,
|
|
6513
6740
|
refuseIfAlreadySpent,
|
|
6741
|
+
regeneratePaymentSlip,
|
|
6514
6742
|
removeByCommitment,
|
|
6515
6743
|
requireSessionKeys,
|
|
6516
6744
|
reservePairwiseIndex,
|
|
@@ -6524,6 +6752,8 @@ export {
|
|
|
6524
6752
|
scanAbortError,
|
|
6525
6753
|
sealOutgoingBlob,
|
|
6526
6754
|
sealPaymentSlip,
|
|
6755
|
+
selectDescribingNote,
|
|
6756
|
+
selectDescribingNoteByCommitment,
|
|
6527
6757
|
selectGhosts,
|
|
6528
6758
|
selectNotes,
|
|
6529
6759
|
selfEphWindow,
|