@orbinum/sdk 1.4.0 → 2.1.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/index.d.mts CHANGED
@@ -1,5 +1,5 @@
1
- import { Z as ZkNote, D as DecryptedMemo, N as NoteInput, S as ScanCommitment, O as OutgoingNoteRecord, a as DecryptPool, b as ScanKeys } from './index-V5Z9igEN.mjs';
2
- export { C as CURRENT_CIRCUIT_VERSION, c as DECRYPT_YIELD_EVERY, d as DecryptBatchResult, e as DecryptRequest, E as EMPTY_BATCH_RESULT, K as KnownEphEntry, f as KnownEphWindow, M as MAX_WORKERS, g as MatchSource, h as MerkleTreeInfo, P as PAIRWISE_EPH_WINDOW, i as SELF_EPH_WINDOW, W as WORKER_CRASHED, j as WorkerFactory, k as WorkerLike, l as WorkerMessage, m as clearKnownEphWindow, n as createDecryptPool, o as createMainThreadPool, p as createWorkerPool, q as decryptHintBatch, r as getKnownEphWindow } from './index-V5Z9igEN.mjs';
1
+ import { Z as ZkNote, D as DecryptedMemo, N as NoteInput, S as ScanCommitment, a as NoteFacts, O as OutgoingNoteRecord, b as DecryptPool, c as ScanKeys } from './index-CLpM1984.mjs';
2
+ export { C as CURRENT_CIRCUIT_VERSION, d as DECRYPT_YIELD_EVERY, e as DecryptBatchResult, f as DecryptRequest, E as EMPTY_BATCH_RESULT, K as KnownEphEntry, g as KnownEphWindow, M as MAX_WORKERS, h as MatchSource, i as MerkleTreeInfo, P as PAIRWISE_EPH_WINDOW, j as SELF_EPH_WINDOW, W as WORKER_CRASHED, k as WorkerFactory, l as WorkerLike, m as WorkerMessage, n as clearKnownEphWindow, o as createDecryptPool, p as createMainThreadPool, q as createWorkerPool, r as decryptHintBatch, s as getKnownEphWindow } from './index-CLpM1984.mjs';
3
3
  import { ArtifactProvider, ProofResult, CircuitType } from '@orbinum/proof-generator';
4
4
  export { ArtifactProvider, CircuitType, ProofResult, WebArtifactProvider, shouldProveSingleThreaded } from '@orbinum/proof-generator';
5
5
  import * as polkadot_api from 'polkadot-api';
@@ -188,8 +188,22 @@ declare class MemoryVaultStorage implements VaultStorage {
188
188
  * carry every ephemeral counter forward. See `buildConfig`.
189
189
  */
190
190
 
191
- /** The schema version this build writes and expects to read back. */
192
- declare const VAULT_SCHEMA_VERSION = 4;
191
+ /**
192
+ * The schema version this build writes and expects to read back.
193
+ *
194
+ * v5: `counterpartyPk` became `sourcePk` on the persisted note. A v4 record
195
+ * carries the old key, and reading it back does NOT throw — `sourcePk` is in
196
+ * `ABSENT_MEANS_ZERO`, so its absence reads as a legitimate zero. The note
197
+ * loads fine, spends fine, and silently loses the other party's key: the only
198
+ * copy of the payee a sender can still open. The bump exists because that
199
+ * failure is quiet, not because the note becomes unusable.
200
+ *
201
+ * No data migration — wipe-and-rescan IS this repo's migration mechanism, and
202
+ * nothing is lost by it: notes live on chain and come back from a scan, while
203
+ * the ephemeral counters (the one piece that cannot be rebuilt) are carried
204
+ * forward by `mergeCounters` rather than wiped.
205
+ */
206
+ declare const VAULT_SCHEMA_VERSION = 5;
193
207
  /**
194
208
  * Lowercases a chain fingerprint, or returns undefined when there is none —
195
209
  * so callers can chain `??` without distinguishing empty from absent.
@@ -333,7 +347,7 @@ type NoteWithMeta = ZkNote & {
333
347
  txKind?: TxKind;
334
348
  };
335
349
  /** Shield deposit vs PrivateTransfer output (received or change) — the memo's
336
- * counterpartyPk is zero only for shield/unshield-built notes. */
350
+ * sourcePk is zero only for shield/unshield-built notes. */
337
351
  declare function noteOrigin(note: ZkNote): 'shield' | 'private-transfer';
338
352
  declare function noteCreatedAt(note: ZkNote): number | null;
339
353
  declare function stampCreatedAt(note: ZkNote, createdAt: number | null): ZkNote;
@@ -431,7 +445,7 @@ declare function removeByCommitment(notes: ZkNote[], commitmentHexes: Set<string
431
445
  * already exists.
432
446
  *
433
447
  * The field list belongs here because it is a fact about `ZkNote`, and a host
434
- * enumerating it independently would miss `counterpartyPk` — the one that is
448
+ * enumerating it independently would miss `sourcePk` — the one that is
435
449
  * optional on the way in and easy to overlook.
436
450
  */
437
451
 
@@ -442,7 +456,7 @@ declare function removeByCommitment(notes: ZkNote[], commitmentHexes: Set<string
442
456
  * without listing it here is a type error rather than a note that silently
443
457
  * normalises incompletely.
444
458
  */
445
- declare const NOTE_BIGINT_FIELDS: readonly ["value", "assetId", "ownerPk", "blinding", "spendingKey", "commitment", "nullifier", "counterpartyPk"];
459
+ declare const NOTE_BIGINT_FIELDS: readonly ["value", "assetId", "ownerPk", "blinding", "spendingKey", "commitment", "nullifier", "sourcePk"];
446
460
  /**
447
461
  * Coerces a note's scalars to `bigint`, or throws naming the field that cannot
448
462
  * be repaired.
@@ -622,6 +636,20 @@ declare class VaultStore {
622
636
  */
623
637
  declare function detectCommitmentMismatch(notes: ZkNote[], validateCommitment?: (commitmentHex: string) => Promise<boolean>): Promise<boolean>;
624
638
 
639
+ /**
640
+ * Is `value` a 0x-prefixed hex string of exactly `byteLen` bytes?
641
+ *
642
+ * A total predicate on `unknown`, for validating values that crossed a trust
643
+ * boundary — a decoded payment slip, an indexer response, a pasted string.
644
+ * Those arrive as `unknown` or as a `string` the type system already believes,
645
+ * and a bare `typeof === 'string'` check admits script tags, URLs and megabyte
646
+ * payloads into fields the wallet later stores and renders.
647
+ *
648
+ * Returns a boolean rather than throwing: callers at a boundary usually want to
649
+ * drop the value or reject the message, not unwind. Use `fromHex` when a throw
650
+ * is the right answer.
651
+ */
652
+ declare function isHexOfLength(value: unknown, byteLen: number): value is string;
625
653
  /**
626
654
  * Converts a Uint8Array or number[] to a 0x-prefixed lowercase hex string.
627
655
  */
@@ -658,6 +686,8 @@ declare function scalarToHex(value: bigint): string;
658
686
 
659
687
  /**
660
688
  * Serialises a bigint as a 32-byte little-endian Uint8Array.
689
+ *
690
+ * Throws on a negative value or one ≥ 2^256 — see `assert32ByteRange`.
661
691
  */
662
692
  declare function bigintTo32Le(n: bigint): Uint8Array;
663
693
  /**
@@ -666,11 +696,17 @@ declare function bigintTo32Le(n: bigint): Uint8Array;
666
696
  declare function bytesToBigintLE(bytes: Uint8Array): bigint;
667
697
  /**
668
698
  * Serialises a bigint as a 32-byte big-endian Uint8Array.
699
+ *
700
+ * Throws on a negative value or one ≥ 2^256 — see `assert32ByteRange`.
669
701
  */
670
702
  declare function bigintTo32Be(n: bigint): Uint8Array;
671
703
  /**
672
704
  * Serialises a bigint as a 32-element little-endian number[].
673
705
  * Useful when building SCALE-encoded arguments via polkadot-api.
706
+ *
707
+ * Throws on a negative value or one ≥ 2^256 — see `assert32ByteRange`. This is
708
+ * the encoder that feeds commitments straight into extrinsic arguments, so a
709
+ * silently-wrong value here lands on chain.
674
710
  */
675
711
  declare function bigintTo32LeArr(n: bigint): number[];
676
712
  /**
@@ -1005,7 +1041,7 @@ declare function addressToFieldElement(address: string): bigint;
1005
1041
  * tagActivationLeaf.
1006
1042
  *
1007
1043
  * Plaintext layout (120 bytes):
1008
- * value_lo(8 LE) || value_hi(8 LE) || owner_pk(32) || blinding(32) || asset_id(4 LE) || counterparty_pk(32) || circuit_version(4 LE)
1044
+ * value_lo(8 LE) || value_hi(8 LE) || owner_pk(32) || blinding(32) || asset_id(4 LE) || source_pk(32) || circuit_version(4 LE)
1009
1045
  *
1010
1046
  * value is stored as a 128-bit LE unsigned integer (two uint64 words), supporting
1011
1047
  * amounts up to ~3.4 × 10^38 planck — well above any realistic token supply.
@@ -1038,12 +1074,12 @@ declare const EncryptedMemo: {
1038
1074
  * (from PrivacyKeyManager.getViewingPublicKeyPacked() or
1039
1075
  * decoded from a privacy address).
1040
1076
  * Pass `new Uint8Array(32)` (all zeros) for a publicly-readable memo.
1041
- * @param counterpartyPk 32-byte counterparty BJJ Ax. Default: all zeros.
1077
+ * @param sourcePk 32-byte counterparty BJJ Ax. Default: all zeros.
1042
1078
  * @param circuitVersion ZK circuit version the note is spent under. Default: 0.
1043
1079
  * @param ephSkOverride 32-byte ephemeral secret key (stealth coordination). Optional.
1044
1080
  * @returns 180-byte encrypted memo: nonce(12) || ciphertext+MAC(136) || ephPk(32).
1045
1081
  */
1046
- encrypt(value: bigint, ownerPk: Uint8Array, blinding: Uint8Array, assetId: number, commitment: Uint8Array, recipientIvkPacked: Uint8Array, counterpartyPk?: Uint8Array, circuitVersion?: number, ephSkOverride?: Uint8Array): Uint8Array;
1082
+ encrypt(value: bigint, ownerPk: Uint8Array, blinding: Uint8Array, assetId: number, commitment: Uint8Array, recipientIvkPacked: Uint8Array, sourcePk?: Uint8Array, circuitVersion?: number, ephSkOverride?: Uint8Array): Uint8Array;
1047
1083
  /**
1048
1084
  * Returns a 180-byte public memo encrypted with a zero viewing key.
1049
1085
  * Decryptable by anyone with `decrypt(memo, commitment, new Uint8Array(32))`.
@@ -1108,7 +1144,16 @@ declare const EncryptedMemo: {
1108
1144
  _decrypt(memoBytes: Uint8Array, commitment: Uint8Array, viewingSecretKey: Uint8Array): DecryptedMemo | null;
1109
1145
  };
1110
1146
 
1111
- declare function serializeMemo(value: bigint, ownerPk: Uint8Array, blinding: Uint8Array, assetId: number, counterpartyPk: Uint8Array, circuitVersion: number): Uint8Array;
1147
+ /**
1148
+ * Serialises the 120-byte memo plaintext.
1149
+ *
1150
+ * The BYTE OFFSETS are the frozen contract, not the parameter names — a golden
1151
+ * vector pins them, and any other implementation has to agree with those
1152
+ * offsets to interoperate. `sourcePk` occupies [84,116); older material calls
1153
+ * that field `counterparty_pk`, which is the same 32 bytes under a name that
1154
+ * wrongly suggested it identifies the sender.
1155
+ */
1156
+ declare function serializeMemo(value: bigint, ownerPk: Uint8Array, blinding: Uint8Array, assetId: number, sourcePk: Uint8Array, circuitVersion: number): Uint8Array;
1112
1157
  /**
1113
1158
  * Derive the 1-byte view tag (Monero-style fast-scan filter) from the ECDH
1114
1159
  * shared secret:
@@ -1221,8 +1266,16 @@ interface PaymentSlipFields {
1221
1266
  */
1222
1267
  declare function sealPaymentSlip(recipientIvkPacked: Uint8Array, fields: PaymentSlipFields): Uint8Array;
1223
1268
  /**
1224
- * Open a payment slip with the recipient's viewing secret key. Returns the fields
1225
- * or null (not ours / corrupt). Never throws — safe in import loops.
1269
+ * Open a payment slip with the recipient's viewing secret key.
1270
+ *
1271
+ * Returns the fields, or null when the slip is not ours, is corrupt, or carries
1272
+ * a field that cannot be what it claims. Never throws: the input is a string a
1273
+ * user pasted, and an exception here takes down the paste handler rather than
1274
+ * one import.
1275
+ *
1276
+ * The result is REBUILT field by field rather than returned from `JSON.parse` —
1277
+ * see the note on the MAC at the top of this file for why a decrypted slip is
1278
+ * still untrusted input.
1226
1279
  */
1227
1280
  declare function openPaymentSlip(recipientIvsk: Uint8Array, envelope: Uint8Array): PaymentSlipFields | null;
1228
1281
  /** URI scheme prefix. The version is in the name, so a v2 reader can refuse a v1. */
@@ -1344,10 +1397,10 @@ declare class NoteBuilder {
1344
1397
  * @param note The ZkNote whose fields populate the plaintext.
1345
1398
  * @param recipientIvkPacked 32-byte LE packed BJJ viewing public key of the recipient.
1346
1399
  * Pass `new Uint8Array(32)` (default) for a public/dummy memo.
1347
- * @param counterpartyPk 32-byte counterparty BabyJubJub Ax.
1400
+ * @param sourcePk 32-byte counterparty BabyJubJub Ax.
1348
1401
  * Pass `new Uint8Array(32)` (default) for no counterparty.
1349
1402
  */
1350
- static buildMemo(note: ZkNote, recipientIvkPacked?: Uint8Array, counterpartyPk?: Uint8Array): Uint8Array;
1403
+ static buildMemo(note: ZkNote, recipientIvkPacked?: Uint8Array, sourcePk?: Uint8Array): Uint8Array;
1351
1404
  }
1352
1405
 
1353
1406
  /**
@@ -1471,6 +1524,24 @@ type OutgoingHint = ScanCommitment & {
1471
1524
  declare function tryRecoverOutgoing(hint: OutgoingHint, ovk: Uint8Array, opts?: {
1472
1525
  viewTagActivationLeaf?: number;
1473
1526
  }): OutgoingNoteRecord | null;
1527
+ /**
1528
+ * Collect what a sender can still say about a note they sent.
1529
+ *
1530
+ * There is no decryption on this path and no key involved. The memo travels
1531
+ * verbatim, exactly as published — the point is to FORWARD it to the recipient
1532
+ * inside a fresh payment slip, not to read it. The recipient opens it with
1533
+ * their own viewing key as they always would.
1534
+ *
1535
+ * That is what makes a slip recoverable after a lost device: re-issuing one
1536
+ * needs the commitment, the memo, and the leaf index, all of them public.
1537
+ *
1538
+ * What is NOT recoverable this way is the amount and the recipient, which live
1539
+ * inside the sealed memo. A sender restoring from a seed alone gets working
1540
+ * slips, not their outgoing history.
1541
+ *
1542
+ * Never throws (runs in recovery loops).
1543
+ */
1544
+ declare function collectOutgoingFacts(hint: ScanCommitment): NoteFacts | null;
1474
1545
 
1475
1546
  /**
1476
1547
  * Proving what ONE note holds, without granting any power to spend it.
@@ -2144,6 +2215,18 @@ interface UnshieldProofResult extends ProofResult {
2144
2215
  */
2145
2216
  declare function generateUnshieldProof(inputs: UnshieldProofInputs, options?: ProofOptions): Promise<UnshieldProofResult>;
2146
2217
 
2218
+ /**
2219
+ * Witness assembly for the fee-claim circuit — accrued relayer fees minted
2220
+ * straight into a shielded note.
2221
+ *
2222
+ * No input note and no merkle path: this proves knowledge of a commitment's
2223
+ * preimage, so the chain can check the note encodes exactly the amount being
2224
+ * claimed and a relayer cannot inflate its own payout.
2225
+ *
2226
+ * The public signals are a fixed 76-byte buffer the runtime parses by offset,
2227
+ * so their layout is a wire contract rather than an implementation detail.
2228
+ */
2229
+
2147
2230
  /**
2148
2231
  * Inputs required to generate a fee-claim proof.
2149
2232
  *
@@ -2601,8 +2684,13 @@ declare class CircuitVersionResolver {
2601
2684
  */
2602
2685
  declare class EvmClient {
2603
2686
  private readonly rpcUrl;
2604
- /** @param rpcUrl - HTTP URL of the EVM JSON-RPC endpoint (e.g. `"http://localhost:9933"`). */
2605
- constructor(rpcUrl: string);
2687
+ private readonly peerRpcUrl?;
2688
+ /**
2689
+ * @param rpcUrl - HTTP URL of the EVM JSON-RPC endpoint (e.g. `"http://localhost:9933"`).
2690
+ * @param peerRpcUrl - Optional second endpoint, used only to tell a genuinely
2691
+ * pending transaction from one stranded on `rpcUrl` alone. See `waitForReceipt`.
2692
+ */
2693
+ constructor(rpcUrl: string, peerRpcUrl?: string | undefined);
2606
2694
  /**
2607
2695
  * Performs a single JSON-RPC call and returns the typed result.
2608
2696
  * Throws on HTTP errors, RPC-level errors, or a `null` result.
@@ -2624,8 +2712,16 @@ declare class EvmClient {
2624
2712
  getChainId(): Promise<number>;
2625
2713
  /** Returns the transaction count (nonce) for an EVM address. */
2626
2714
  getTransactionCount(address: string): Promise<number>;
2627
- /** Returns the current gas price in wei. */
2628
- getGasPrice(): Promise<bigint>;
2715
+ /**
2716
+ * Returns the current gas price in wei, padded by `bumpPercent`.
2717
+ *
2718
+ * `eth_gasPrice` reports the base fee exactly, and the base fee moves
2719
+ * between signing and the pool's next revalidation. A transaction priced at
2720
+ * the bare minimum is evicted as `GasPriceTooLow` the moment it rises, which
2721
+ * leaves every later nonce from that account stranded in the future queue.
2722
+ * The default 25% pad absorbs the usual movement.
2723
+ */
2724
+ getGasPrice(bumpPercent?: number): Promise<bigint>;
2629
2725
  /** Submits a signed raw transaction. Returns the transaction hash. */
2630
2726
  sendRawTransaction(signedHex: string): Promise<string>;
2631
2727
  /** Executes a read-only call without creating a transaction. Returns the raw ABI-encoded response. */
@@ -2660,6 +2756,13 @@ declare class EvmClient {
2660
2756
  * @throws If the transaction dropped, is still pending after the grace window, or reverted (`status == 0x0`).
2661
2757
  */
2662
2758
  waitForReceipt(txHash: string, intervalMs?: number, timeoutMs?: number): Promise<Record<string, unknown>>;
2759
+ /**
2760
+ * True when `rpcUrl` knows the transaction but the configured peer does not.
2761
+ *
2762
+ * Returns false without a peer configured, and on any peer error — an
2763
+ * unreachable peer is not evidence that a live transaction is stranded.
2764
+ */
2765
+ private isStrandedOnThisNode;
2663
2766
  }
2664
2767
 
2665
2768
  /** Enriched EVM transaction model for explorer UIs. */
@@ -2871,6 +2974,12 @@ declare class EvmExplorer {
2871
2974
  type OrbinumClientCommon = {
2872
2975
  /** HTTP URL of the EVM JSON-RPC endpoint (e.g. `"http://localhost:9933"`). Omit to disable EVM support. */
2873
2976
  evmRpc?: string;
2977
+ /**
2978
+ * HTTP URL of a second, independent EVM endpoint. Never used for submission —
2979
+ * only to tell a genuinely pending transaction from one stranded on `evmRpc`
2980
+ * alone, which reports as pending forever but can never mine.
2981
+ */
2982
+ evmRpcPeer?: string;
2874
2983
  /**
2875
2984
  * Base URL of a circuits-artifact mirror serving `manifest.json` and the
2876
2985
  * artifacts beside it. Omit for the default npm CDN (unpkg).
@@ -2983,6 +3092,23 @@ type ShieldParams = {
2983
3092
  /** Encrypted memo bytes (180 bytes). Required — notes without valid memos are irrecoverable. */
2984
3093
  encryptedMemo: Uint8Array;
2985
3094
  };
3095
+ /**
3096
+ * How the relay fee recipient is decided.
3097
+ *
3098
+ * It is not a parameter anywhere in this module: the chain reads it from the
3099
+ * dispatch origin, which calldata cannot influence. A `relayer` field would be an
3100
+ * unauthenticated claim — anyone could take a propagated proof, resubmit it
3101
+ * naming themselves, and collect a fee they never paid for.
3102
+ *
3103
+ * | Submitted via | Credited |
3104
+ * |---|---|
3105
+ * | EVM precompile | whoever signed that EVM transaction and paid its gas |
3106
+ * | Signed extrinsic | the signer's registered EVM address |
3107
+ * | Unsigned extrinsic | the block author |
3108
+ *
3109
+ * `fee` below still matters: it is a ZK public input, so it cannot be altered
3110
+ * without regenerating the proof. Only the *recipient* moved to the origin.
3111
+ */
2986
3112
  /** Parameters for shieldedPool.unshield — withdraws from the pool to a clear address. */
2987
3113
  type UnshieldParams = {
2988
3114
  /** ZK proof bytes */
@@ -3090,7 +3216,9 @@ type ClaimShieldedFeesParams = {
3090
3216
  * which means the Orbinum node must be reachable on first use.
3091
3217
  * Signing is delegated to a SubstrateSigner (see polkadot-api/signer).
3092
3218
  *
3093
- * Parameter order matches the Orbinum runtime extrinsics exactly.
3219
+ * Arguments are passed BY NAME through PAPI, so the field names not the
3220
+ * order — have to match the runtime's. A renamed parameter fails at
3221
+ * encoding rather than silently shifting a value into the wrong slot.
3094
3222
  */
3095
3223
  declare class ShieldedPoolModule {
3096
3224
  private readonly substrate;
@@ -3107,14 +3235,22 @@ declare class ShieldedPoolModule {
3107
3235
  * Withdraws tokens from the shielded pool to a public address.
3108
3236
  * Submits as an UNSIGNED (gasless) transaction — fee is embedded in the ZK proof.
3109
3237
  * Pass a `signer` to fall back to signed submission (e.g. for testing).
3110
- * Extrinsic: shieldedPool.unshield(proof, merkleRoot, nullifier, assetId, amount, recipient, fee, changeCommitment, changeEncryptedMemo, relayer, circuitVersion)
3238
+ * Extrinsic: shieldedPool.unshield(proof, merkleRoot, nullifier, assetId, amount, recipient, fee, changeCommitment, changeEncryptedMemo, circuitVersion)
3239
+ *
3240
+ * The relay fee recipient is NOT a parameter: the chain takes it from the
3241
+ * dispatch origin. Submitting unsigned credits the block author; submitting
3242
+ * through the EVM precompile credits whoever signed that transaction.
3111
3243
  */
3112
3244
  unshield(params: UnshieldParams, signer?: PolkadotSigner, options?: SubmitOptions): Promise<TxResult>;
3113
3245
  /**
3114
3246
  * Performs a private (shielded) transfer between two notes.
3115
3247
  * Submits as an UNSIGNED (gasless) transaction — fee is embedded in the ZK proof.
3116
3248
  * Pass a `signer` to fall back to signed submission (e.g. for testing).
3117
- * Extrinsic: shieldedPool.privateTransfer(proof, merkleRoot, nullifiers, commitments, memos, assetId, fee, relayer, circuitVersion)
3249
+ * Extrinsic: shieldedPool.privateTransfer(proof, merkleRoot, nullifiers, commitments, memos, assetId, fee, circuitVersion)
3250
+ *
3251
+ * The relay fee recipient is NOT a parameter: the chain takes it from the
3252
+ * dispatch origin. Submitting unsigned credits the block author; submitting
3253
+ * through the EVM precompile credits whoever signed that transaction.
3118
3254
  */
3119
3255
  privateTransfer(params: PrivateTransferParams, signer?: PolkadotSigner, options?: SubmitOptions): Promise<TxResult>;
3120
3256
  /**
@@ -3239,6 +3375,12 @@ type EvmTxRequest = {
3239
3375
  to: string;
3240
3376
  data: string;
3241
3377
  value?: bigint;
3378
+ /**
3379
+ * Explicit gas price in wei. Omit to let the wallet pick, which prices the
3380
+ * transaction at the bare base fee — enough to be evicted as `GasPriceTooLow`
3381
+ * the moment the base fee rises, stranding every later nonce from the account.
3382
+ */
3383
+ gasPrice?: bigint;
3242
3384
  };
3243
3385
  /** Callback that signs and submits an EVM transaction, returning the tx hash. */
3244
3386
  type EvmSigner = (tx: EvmTxRequest) => Promise<string>;
@@ -3255,121 +3397,84 @@ interface KnownPrecompileInfo {
3255
3397
  *
3256
3398
  * This precompile wraps `pallet-shielded-pool` extrinsics and dispatches them
3257
3399
  * on behalf of the EVM caller (resolved to an AccountId32 via
3258
- * `EeSuffixAddressMapping`). No Substrate signer is required — an EVM wallet
3259
- * is sufficient.
3260
- *
3261
- * ### Key benefit for apps
3262
- * EVM-only users (MetaMask, Phantom bridge via chain links, etc.) can shield,
3263
- * transfer, and unshield without ever needing a Polkadot extension.
3400
+ * `EeSuffixAddressMapping`). No Substrate signer is required — an EVM wallet is
3401
+ * sufficient, so EVM-only users can shield, transfer and unshield without ever
3402
+ * installing a Polkadot extension.
3264
3403
  *
3265
- * All write methods accept an `EvmSigner` callback so the module stays
3266
- * transport-agnostic. See `buildShieldCalldata` etc. if you only need the
3267
- * raw calldata for custom signing flows.
3404
+ * This class is TRANSPORT only: the precompile address, the signer callback and
3405
+ * gas estimation. The calldata itself is built by `shieldedPoolCalldata`, which
3406
+ * needs no chain connection — import those functions directly for custom
3407
+ * signing flows rather than constructing a client you never call.
3268
3408
  */
3409
+
3269
3410
  declare class ShieldedPoolPrecompile {
3270
3411
  private readonly evm;
3271
3412
  private readonly addr;
3272
3413
  constructor(evm: EvmClient);
3273
- /**
3274
- * Returns the ABI-encoded calldata for `shield(uint32, bytes32, bytes)`.
3275
- * The token amount must be sent as `msg.value` (the `value` field of the EVM
3276
- * transaction) — this is what MetaMask and other wallets display to the user.
3277
- */
3278
3414
  buildShieldCalldata(params: ShieldParams): string;
3415
+ buildPrivateTransferCalldata(params: PrivateTransferParams): string;
3416
+ buildUnshieldCalldata(params: UnshieldParams): string;
3417
+ buildClaimShieldedFeesCalldata(params: ClaimShieldedFeesParams): string;
3279
3418
  /**
3280
3419
  * Deposits tokens into the shielded pool from a payable EVM transaction.
3281
3420
  *
3282
- * The token amount is sent as `msg.value` so EVM wallets (MetaMask, etc.) display
3283
- * the correct amount on the confirmation screen. The precompile dispatches
3284
- * `shieldedPool.shield` with its own address as origin, so the funds flow:
3285
- * caller precompile (via msg.value, handled by EVM)
3286
- * precompile → pool (via pallet transfer)
3287
- * This avoids double-deduction while keeping the displayed amount accurate.
3421
+ * The amount rides as `msg.value` so EVM wallets show the correct figure on
3422
+ * the confirmation screen. The precompile then dispatches with its OWN
3423
+ * address as origin, so funds flow caller → precompile → pool. That avoids
3424
+ * a double deduction while keeping the displayed amount accurate.
3288
3425
  *
3289
3426
  * Extrinsic: `shieldedPool.shield(assetId, amount, commitment, encryptedMemo)`
3290
3427
  */
3291
3428
  shield(params: ShieldParams, signer: EvmSigner): Promise<string>;
3292
3429
  /**
3293
- * Returns the ABI-encoded calldata for
3294
- * `privateTransfer(bytes, bytes32, bytes32[], bytes32[], bytes[], uint32, uint256, uint32)`.
3295
- * The trailing `uint32` is the circuit version the input notes were created under.
3296
- */
3297
- buildPrivateTransferCalldata(params: PrivateTransferParams): string;
3298
- /**
3299
- * Submits a private transfer within the shielded pool from an EVM transaction.
3430
+ * Submits a private transfer within the shielded pool.
3300
3431
  *
3301
- * The EVM caller identity is **irrelevant to the ZK proof** — the sender is
3302
- * hidden by design. Any EVM address (including a relayer) can submit a valid proof.
3432
+ * The EVM caller identity is IRRELEVANT to the ZK proof — the sender is
3433
+ * hidden by design, so any address (a relayer included) can submit a valid
3434
+ * proof.
3303
3435
  *
3304
- * Extrinsic: `shieldedPool.privateTransfer(proof, merkleRoot, nullifiers, commitments, memos)`
3436
+ * Extrinsic: `shieldedPool.privateTransfer(proof, merkleRoot, nullifiers,
3437
+ * commitments, memos, assetId, fee, circuitVersion)` — eight arguments; see
3438
+ * `buildPrivateTransferCalldata` for the encoding order.
3305
3439
  */
3306
3440
  privateTransfer(params: PrivateTransferParams, signer: EvmSigner): Promise<string>;
3307
- /**
3308
- * Params for an `unshield` call via the EVM precompile.
3309
- * The `recipient` is a full 32-byte AccountId32 (Substrate account or
3310
- * EeSuffix-derived: `H160 ++ [0x00; 12]`).
3311
- */
3312
- buildUnshieldCalldata(params: UnshieldParams): string;
3313
3441
  /**
3314
3442
  * Withdraws tokens from the shielded pool to a recipient account.
3315
3443
  *
3316
- * `params.recipientAddress` must be a 0x-prefixed 64-hex-char AccountId32.
3317
- * To send to an EVM address, use `evmToImplicitSubstrate(evmAddr)` from
3318
- * `@orbinum/sdk` to derive the AccountId32 first.
3444
+ * `params.recipientAddress` must be a 0x-prefixed AccountId32. To send to an
3445
+ * EVM address, derive it first with `evmToImplicitSubstrate(evmAddr)`.
3319
3446
  *
3320
- * Extrinsic: `shieldedPool.unshield(proof, merkleRoot, nullifier, assetId, amount, recipient)`
3321
- */
3322
- unshield(params: UnshieldParams, signer: EvmSigner): Promise<string>;
3323
- /**
3324
- * Estimates the EVM gas for a `shield` call without submitting.
3325
- * Requires `from` to be set to the actual sender address.
3326
- */
3327
- estimateShieldGas(params: ShieldParams, from: string): Promise<bigint>;
3328
- /**
3329
- * Estimates the EVM gas for a `privateTransfer` call.
3330
- */
3331
- estimatePrivateTransferGas(params: PrivateTransferParams, from: string): Promise<bigint>;
3332
- /**
3333
- * Estimates the EVM gas for an `unshield` call.
3334
- */
3335
- estimateUnshieldGas(params: UnshieldParams, from: string): Promise<bigint>;
3336
- /**
3337
- * Returns the ABI-encoded calldata for
3338
- * `claimShieldedFees(bytes32,uint256,uint32,bytes,bytes,bytes,uint32)`.
3447
+ * Extrinsic: `shieldedPool.unshield(proof, merkleRoot, nullifier, assetId,
3448
+ * amount, recipient, fee, changeCommitment, changeEncryptedMemo,
3449
+ * circuitVersion)` ten arguments; see `buildUnshieldCalldata`.
3339
3450
  *
3340
- * ABI layout (params after selector):
3341
- * - `commitment` — bytes32 (fixed)
3342
- * - `amount` uint256 (fixed)
3343
- * - `asset_id` — uint32 (fixed, right-aligned)
3344
- * - `memo` — bytes (dynamic)
3345
- * - `proof` — bytes (dynamic, 128 bytes Groth16)
3346
- * - `publicSignals` — bytes (dynamic, 76 bytes)
3347
- * - `circuitVersion` — uint32 (fixed, right-aligned)
3348
- *
3349
- * The validator identity is derived from `msg.sender` in the precompile —
3350
- * do NOT include it in the calldata.
3451
+ * **The relay fee goes to whoever `signer` is.** The chain takes the recipient
3452
+ * from `msg.sender`, not from calldata, so the account behind this signer is
3453
+ * the one credited and it is also the one paying gas. Relaying on someone
3454
+ * else's behalf and being paid for it is the same act here.
3351
3455
  */
3352
- buildClaimShieldedFeesCalldata(params: ClaimShieldedFeesParams): string;
3456
+ unshield(params: UnshieldParams, signer: EvmSigner): Promise<string>;
3353
3457
  /**
3354
- * Claims accumulated relay fees as a private shielded note.
3355
- *
3356
- * This extrinsic is for **validators/relayers** who have accrued fees in
3357
- * `pallet-relayer` and want to receive them privately inside the shielded pool
3358
- * instead of as a public balance credit.
3458
+ * Claims accrued relay fees as a private shielded note.
3359
3459
  *
3360
- * The ZK `value_proof` binds `commitment` to `(amount, assetId, ownerPk, blinding)`
3361
- * so the runtime can verify the note encodes exactly the claimed fee amount,
3362
- * preventing a malicious relayer from inflating the withdrawal.
3460
+ * For validators/relayers holding fees in `pallet-relayer` who want them
3461
+ * paid privately into the shielded pool rather than as a public balance
3462
+ * credit. The ZK `value_proof` binds `commitment` to
3463
+ * `(amount, assetId, ownerPk, blinding)`, so the runtime can verify the note
3464
+ * encodes exactly the claimed amount and a malicious relayer cannot inflate
3465
+ * the withdrawal.
3363
3466
  *
3364
- * The `msg.sender` EVM address is used as the validator identity; it must match
3365
- * the address that has pending relay fees in `pallet-relayer`.
3467
+ * The `msg.sender` address is the validator identity, and must match the
3468
+ * one with pending fees.
3366
3469
  *
3367
- * Extrinsic: `shieldedPool.claim_shielded_fees(commitment, amount, assetId, memo, proof, publicSignals)`
3470
+ * Extrinsic: `shieldedPool.claim_shielded_fees(commitment, amount, assetId,
3471
+ * memo, proof, publicSignals, circuitVersion)` — seven arguments; see
3472
+ * `buildClaimShieldedFeesCalldata`.
3368
3473
  */
3369
3474
  claimShieldedFees(params: ClaimShieldedFeesParams, signer: EvmSigner): Promise<string>;
3370
- /**
3371
- * Estimates the EVM gas for a `claimShieldedFees` call.
3372
- */
3475
+ estimateShieldGas(params: ShieldParams, from: string): Promise<bigint>;
3476
+ estimatePrivateTransferGas(params: PrivateTransferParams, from: string): Promise<bigint>;
3477
+ estimateUnshieldGas(params: UnshieldParams, from: string): Promise<bigint>;
3373
3478
  estimateClaimShieldedFeesGas(params: ClaimShieldedFeesParams, from: string): Promise<bigint>;
3374
3479
  }
3375
3480
 
@@ -3562,6 +3667,8 @@ interface ClientProviderConfig {
3562
3667
  substrateWs: string;
3563
3668
  /** HTTP URL of the EVM JSON-RPC endpoint (e.g. `"http://localhost:9933"`). Omit to disable EVM support. */
3564
3669
  evmRpc?: string;
3670
+ /** HTTP URL of a second EVM endpoint, used only to detect transactions stranded on `evmRpc`. */
3671
+ evmRpcPeer?: string;
3565
3672
  /** Base URL of a circuits-artifact mirror (manifest.json + artifacts). Omit to use the default npm CDN. */
3566
3673
  circuitsBaseUrl?: string;
3567
3674
  /** Timeout for the initial WebSocket handshake in milliseconds. Default: `8_000`. */
@@ -3847,13 +3954,18 @@ declare function isGhostNoteError(rawMessage: string): boolean;
3847
3954
  * `fee`, and without this a host is left reverse-engineering the number from a
3848
3955
  * failed extrinsic.
3849
3956
  *
3850
- * A runtime upgrade can change these. They are exported as the current chain's
3851
- * values, which is what every caller needs today; a chain that has moved on
3852
- * should pass its own.
3957
+ * These are the current chain's values, which is what every caller needs today.
3958
+ * A chain that has moved on should pass its own — and note that not all of them
3959
+ * need a runtime upgrade to move: see `MIN_GASLESS_FEE`.
3853
3960
  */
3854
3961
  /**
3855
3962
  * Smallest fee an unsigned (gasless) shielded-pool extrinsic may carry, in
3856
- * planck. Mirrors the runtime's `MinGaslessFee`.
3963
+ * planck the default of `pallet-relayer`'s `MinRelayFee`.
3964
+ *
3965
+ * MUTABLE STORAGE, not a runtime constant: governance moves it with
3966
+ * `set_min_relay_fee`, no upgrade required. A wallet that hardcodes this value
3967
+ * starts failing with `FeeTooLow` the moment the floor rises, so read
3968
+ * `min_relay_fee()` when the answer has to be current.
3857
3969
  *
3858
3970
  * Below it the pallet rejects with `FeeTooLow`. The fee is paid to the block
3859
3971
  * author by the runtime, which is what lets a user with no public balance spend
@@ -4260,10 +4372,13 @@ declare function getPrecompileLabel(address: string | null | undefined): string
4260
4372
  * `unshield` — a classifier that checks in the wrong order reports every
4261
4373
  * unshield as a shield, silently, and a new pallet method breaks it again.
4262
4374
  */
4263
- type PrecompileMethod = 'shield' | 'unshield' | 'privateTransfer' | 'shieldBatch';
4375
+ type PrecompileMethod = 'shield' | 'unshield' | 'privateTransfer' | 'shieldBatch' | 'claimShieldedFees';
4264
4376
  type DecodedPrecompile = {
4265
4377
  fnSig: string;
4266
- /** The operation, from the selector. Null for a known precompile's unmapped call. */
4378
+ /**
4379
+ * The operation, from the selector. Null only when the args below could not
4380
+ * be decoded either — every signature this file decodes maps to a name.
4381
+ */
4267
4382
  method: PrecompileMethod | null;
4268
4383
  args: Record<string, unknown>;
4269
4384
  };
@@ -4879,7 +4994,7 @@ interface BuildNoteParams {
4879
4994
  /** Packed viewing public key of the RECIPIENT, from their privacy address. */
4880
4995
  viewingPublicKey?: Uint8Array | undefined;
4881
4996
  /** Counterparty ownerPk. Zero for shield/unshield notes. */
4882
- counterpartyPk?: bigint | undefined;
4997
+ sourcePk?: bigint | undefined;
4883
4998
  /** Recipient's global ownerPk. With `viewingPublicKey`, enables stealth. */
4884
4999
  recipientOwnerPk?: bigint | undefined;
4885
5000
  /**
@@ -5197,7 +5312,7 @@ interface TransferDeps {
5197
5312
  assetId: bigint;
5198
5313
  ownerPk: bigint;
5199
5314
  spendingKey?: bigint;
5200
- counterpartyPk: bigint;
5315
+ sourcePk: bigint;
5201
5316
  viewingPublicKey?: Uint8Array;
5202
5317
  recipientOwnerPk?: bigint;
5203
5318
  }) => Promise<ZkNote>;
@@ -5300,7 +5415,9 @@ interface FeeClaimDeps {
5300
5415
  buildNote: (params: {
5301
5416
  value: bigint;
5302
5417
  assetId: bigint;
5303
- }) => Promise<ZkNote>;
5418
+ }) => Promise<{
5419
+ note: ZkNote;
5420
+ }>;
5304
5421
  resolver: Pick<CircuitVersionResolver, 'resolve'>;
5305
5422
  pool: Pick<ShieldedPoolModule, 'claimShieldedFees'>;
5306
5423
  /** Where the minted note lands once the claim finalizes. */
@@ -5554,6 +5671,218 @@ declare function getInjectedExtensions(): string[];
5554
5671
  */
5555
5672
  declare function connectInjectedExtension(name: string, origin?: string): Promise<InjectedExtension>;
5556
5673
 
5674
+ /**
5675
+ * NoteProvenance — the single vocabulary for "where did this note come from,
5676
+ * and where did it go".
5677
+ *
5678
+ * Several mechanisms answer that question, and before this module they had
5679
+ * separate vocabularies and no contract between them:
5680
+ *
5681
+ * - the memo's `sourcePk` field, readable by whoever can decrypt the note
5682
+ * (the recipient always; the sender only for the change note they kept);
5683
+ * - a lookup by commitment, which returns only what is already public — no
5684
+ * amount, no recipient, but enough to re-issue a payment slip.
5685
+ *
5686
+ * They are not competing designs. They are providers of the same fact, and
5687
+ * `ProvenanceSource` records which one spoke. Nothing here derives a key: this
5688
+ * layer only holds data already recovered.
5689
+ *
5690
+ * A sender cannot reopen a memo sealed toward someone else, so the amount and
5691
+ * recipient of an outgoing transfer are NOT recoverable from a seed alone. What
5692
+ * survives is the ability to hand the recipient a working slip again.
5693
+ */
5694
+
5695
+ /**
5696
+ * Which operation produced a note.
5697
+ *
5698
+ * Replaces reading intent out of `sourcePk === 0n`, which had come to mean
5699
+ * three unrelated things at once: "shield or unshield", "an older record that
5700
+ * omitted the field", and "recipient not yet known". A note whose origin is
5701
+ * genuinely unknown says so.
5702
+ */
5703
+ type NoteOrigin = 'shield' | 'transfer-in' | 'transfer-change' | 'unshield-change' | 'fee-claim' | 'unknown';
5704
+ /**
5705
+ * Who wrote this record, and therefore how much to trust it.
5706
+ *
5707
+ * `witnessed` is the wallet's own account of a transfer it submitted — the
5708
+ * strongest, since nothing was recovered or guessed. `memo` is a decrypted
5709
+ * fact. `chain` is a lookup by commitment: trustworthy but thin, carrying only
5710
+ * public fields. `inferred` is arithmetic over the notes an extrinsic touched,
5711
+ * and is the only one that can be wrong about the amount.
5712
+ */
5713
+ type ProvenanceSource = 'witnessed' | 'memo' | 'chain' | 'inferred';
5714
+ /**
5715
+ * What kind of public key `peer.pk` is.
5716
+ *
5717
+ * Orbinum stamps ONE-TIME stealth keys in memos on purpose — a stable
5718
+ * identifier in the recipient's note would link every payment from the same
5719
+ * sender forever. The UI needs to know which it holds: a global pk is an
5720
+ * address a user can act on, a stealth pk is a per-transfer artifact that
5721
+ * happens to look identical in hex.
5722
+ */
5723
+ type PkScope = 'global' | 'stealth' | 'none';
5724
+ /** The other party to a transfer, with the nature of the key made explicit. */
5725
+ type ProvenancePeer = {
5726
+ /** BabyJubJub Ax coordinate. */
5727
+ pk: bigint;
5728
+ scope: PkScope;
5729
+ };
5730
+ /**
5731
+ * The amount moved, and whether that figure is exact.
5732
+ *
5733
+ * `exact` is a property of the FIGURE, not of the source: an `inferred` record
5734
+ * whose fee resolved is exact too. Keeping them separate is what lets the UI
5735
+ * mark an approximation without pretending to know where it came from.
5736
+ */
5737
+ type ProvenanceAmount = {
5738
+ /** Amount in planck. */
5739
+ value: bigint;
5740
+ /** False when the figure was derived and something in the derivation was unknown. */
5741
+ exact: boolean;
5742
+ };
5743
+ /**
5744
+ * One entry in the wallet's private history. Subsumes what used to be three
5745
+ * separate shapes: the app's `LocalTxRecord`, the scanner's
5746
+ * `ReconstructedTxRecord`, and the per-note facts a lookup returns.
5747
+ */
5748
+ type NoteProvenanceRecord = {
5749
+ /** Primary key — the tx hash, or `{block}-{index}` when it was not decoded. */
5750
+ id: string;
5751
+ /** 0x-prefixed extrinsic hash. Empty when the extrinsic could not be resolved. */
5752
+ hash: string;
5753
+ blockNumber: number;
5754
+ /** Unix ms. On-chain block time where known, local wall-clock otherwise. */
5755
+ timestampMs: number;
5756
+ /** Explicit direction — replaces `isIncoming`, `outgoing/incoming` and the rest. */
5757
+ direction: 'in' | 'out';
5758
+ kind: 'private_transfer' | 'unshield' | 'shield' | 'fee_claim';
5759
+ origin: NoteOrigin;
5760
+ source: ProvenanceSource;
5761
+ /** The other party, or null when this operation has none (shield, fee claim)
5762
+ * or when no one-time key was available to record. */
5763
+ peer: ProvenancePeer | null;
5764
+ amount: ProvenanceAmount;
5765
+ assetId: bigint;
5766
+ status: 'success' | 'failed';
5767
+ /** Relay fee actually paid, when it could be read from the extrinsic. */
5768
+ feePlanck?: bigint;
5769
+ /** SS58 address that received the funds — unshield only. */
5770
+ publicRecipient?: string;
5771
+ /** The `orbslip1:` payment slip, when one was sealed or regenerated. */
5772
+ slip?: {
5773
+ encoded: string;
5774
+ };
5775
+ /** The recovered note itself, when a decryption path produced one. */
5776
+ note?: NoteFacts;
5777
+ };
5778
+
5779
+ /**
5780
+ * Which of an extrinsic's notes describes the transfer.
5781
+ *
5782
+ * One extrinsic can insert more than one note we own — a self-transfer produces
5783
+ * both the recipient note and the change. Taking whichever comes first would
5784
+ * report the change amount as the transfer amount, and an arbitrary one at
5785
+ * that, since vault order is insertion order.
5786
+ *
5787
+ * The rule: a note carrying a stamped `sourcePk` names the other party, so that
5788
+ * is the one that describes the transfer. In a self-transfer either note
5789
+ * qualifies, since both identify us.
5790
+ *
5791
+ * This was implemented twice — `findChangeNote` in the scanner and
5792
+ * `resolveIncomingTransferMeta` in the app, whose comment already admitted it
5793
+ * was copying the SDK. One rule, one place.
5794
+ */
5795
+
5796
+ /**
5797
+ * True when the note carries a `sourcePk` — i.e. it is not a shield/unshield
5798
+ * output, and not a transfer whose spent note had no one-time key to stamp.
5799
+ *
5800
+ * The type is checked, not just the value. Notes come back from encrypted
5801
+ * storage, and `normalizeNote` is what turns their scalars into bigints — but
5802
+ * this is public API and takes any object with the field, so a record that
5803
+ * skipped normalisation arrives with a string. `'0' != null && '0' !== 0n` is
5804
+ * true, so an unnormalised zero would read as a stamped key and pick the wrong
5805
+ * note: the CHANGE reported as the transfer amount, silently.
5806
+ */
5807
+ declare function hasSourcePk(note: Pick<ZkNote, 'sourcePk'>): boolean;
5808
+ /**
5809
+ * Pick the note that describes the transfer, from the notes of ONE extrinsic
5810
+ * that this wallet owns. Returns undefined when it owns none of them.
5811
+ */
5812
+ declare function selectDescribingNote<T extends Pick<ZkNote, 'sourcePk'>>(candidates: T[]): T | undefined;
5813
+ /**
5814
+ * Same rule, resolving commitment hexes against a lookup first — the shape the
5815
+ * scanner has on hand. Commitments we do not own are skipped.
5816
+ */
5817
+ declare function selectDescribingNoteByCommitment<T extends Pick<ZkNote, 'sourcePk'>>(commitments: string[], noteByCommitment: Map<string, T>): T | undefined;
5818
+
5819
+ /**
5820
+ * Merging what a rescan learned into what the wallet already knew.
5821
+ *
5822
+ * The hazard this exists to remove: reconstruction runs after every scan, over
5823
+ * records the wallet may have written itself at submit time. Those local
5824
+ * records hold what a recovery path cannot: the amount and the recipient, which
5825
+ * live inside a memo sealed toward someone else. Overwriting one loses them.
5826
+ *
5827
+ * The payment slip itself is not at risk — it can be re-issued from public
5828
+ * fields — but a thin `chain` record must never replace a rich local one.
5829
+ *
5830
+ * Before this, the protection was a single spread expression in the
5831
+ * reconstruction loop. The rule is now explicit and testable on its own.
5832
+ */
5833
+
5834
+ declare function outranks(a: ProvenanceSource, b: ProvenanceSource): boolean;
5835
+ /**
5836
+ * Merge an incoming record into an existing one.
5837
+ *
5838
+ * Two rules, in order:
5839
+ *
5840
+ * 1. **A weaker source never overwrites a stronger one's facts.** An
5841
+ * `inferred` backfill cannot replace the amount a `witnessed` record
5842
+ * recorded at submit time.
5843
+ * 2. **Absence never overwrites presence.** Whatever the incoming record does
5844
+ * not carry — a slip, a fee, a public recipient — is kept from the
5845
+ * existing one regardless of rank, because "not recovered" is not "not
5846
+ * there".
5847
+ *
5848
+ * Absence is not only `undefined`. Several fields spell "not known yet" with a
5849
+ * value: block and timestamp use `0`, `hash` and a slip's `encoded` use the
5850
+ * empty string, and a peer uses `scope: 'none'`. Those are gaps too, and
5851
+ * treating them as data let a placeholder win by rank — which is how a record
5852
+ * written before the chain confirmed could erase a block number already
5853
+ * resolved.
5854
+ *
5855
+ * Two fields sit outside the ranking entirely, because rank is the wrong
5856
+ * question for them: `status` is the chain's own outcome, and `amount.exact`
5857
+ * describes the figure rather than its source.
5858
+ */
5859
+ declare function mergeProvenance(existing: NoteProvenanceRecord, incoming: NoteProvenanceRecord): NoteProvenanceRecord;
5860
+
5861
+ /**
5862
+ * Seal a fresh `orbslip1:` slip for a transfer recovered from history.
5863
+ *
5864
+ * ## Why the facts are checked here
5865
+ *
5866
+ * They were looked up by commitment, so they crossed a trust boundary, and
5867
+ * sealing them produces an AUTHENTICATED envelope. A valid MAC proves the
5868
+ * sender knew the recipient's viewing key — not that they are honest, and not
5869
+ * that whatever server answered the lookup was. The recipient's wallet then
5870
+ * renders those fields with the authority of a decrypted slip, on a device
5871
+ * where nothing explains which server supplied them.
5872
+ *
5873
+ * So the check belongs on THIS side of the wire: a value that slipped through
5874
+ * would fail on the recipient's device, or worse, not fail at all.
5875
+ *
5876
+ * Throws on a malformed commitment, memo or leaf index — those are the note's
5877
+ * identity, and a slip carrying a wrong one is not a degraded slip but a broken
5878
+ * one. `txHash` is informational, so it is DROPPED rather than fatal.
5879
+ *
5880
+ * @param facts public facts of the sent note, looked up by commitment
5881
+ * @param recipientIvkPacked 32-byte packed viewing key from the recipient's privacy address
5882
+ * @param txHash the transfer's hash, when known — informational, shown as proof of payment
5883
+ */
5884
+ declare function regeneratePaymentSlip(facts: NoteFacts, recipientIvkPacked: Uint8Array, txHash?: string): string;
5885
+
5557
5886
  /**
5558
5887
  * `OrbinumWallet` — the assembled wallet: keys, vault, scanner, note building.
5559
5888
  *
@@ -5720,4 +6049,4 @@ declare class OrbinumWallet {
5720
6049
  private requireKey;
5721
6050
  }
5722
6051
 
5723
- export { type AssetRegisteredEvent, type AssetUnverifiedEvent, type AssetVerifiedEvent, BABYJUB_SUBORDER, BN254_R, type BackupImportKeys, type BlockInfo, type BuildNoteDeps, type BuildNoteParams, type Bytes32, CachedNullifier, type ChainInfo, ChainModule, type ChunkInfo, CircuitId, CircuitVersionResolver, type ClaimShieldedFeesParams, type ClientProviderConfig, type CoinSelection, type CollectScanEntriesParams, type CommitmentsInsertedEvent, type ConnectionStatus, type CryptoKey$1 as CryptoKey, CryptoPrecompiles, type DecodedPrecompile, DecryptPool, DecryptedMemo, type DynamicBuilder, ENCRYPTED_MEMO_SIZE, EncryptedMemo, EncryptedNoteRecord, EncryptedTxRecord, type EventData, type EventPhase, type EventRecord, type EvmAddressInfo, type EvmBlock, EvmClient, EvmExplorer, type EvmLog, type EvmSigner, type EvmTransaction, type EvmTxRequest, type EvmTxSummary, type ExtrinsicDecoder, type ExtrinsicFacts, type ExtrinsicRecord, type FeeClaimDeps, type FeeClaimParams, type FeeClaimProofInputs, type FeeClaimProofOutput, type FeeClaimStep, type FormatOptions, KNOWN_PALLET_ERRORS, KNOWN_PRECOMPILES, type KnownPrecompileInfo, LEAVES_PER_TREE, MIN_GASLESS_FEE, MIN_SIGNATURE_BYTES, MemoryVaultStorage, type MerkleRootUpdatedEvent, type MutableWalletSession, NATIVE_ASSET_ID, NOTE_BACKUP_VERSION, NOTE_BIGINT_FIELDS, NOTE_TRANSFER_URI_SCHEME, type NoteBackup, type NoteBackupEntry, type NoteBuildKeys, NoteBuilder, type NoteDisclosure, NoteInput, NoteStatusUpdate, NoteStorage, type NoteTransferEntry, type NoteTransferPayload, type NoteWithMeta, type NotesCache, NullifierCache, type NullifierChunkBody, type NullifierManifest, type NullifierSource, NullifierSyncMeta, type NullifierTail, type NullifiersSpentEvent, OVK_BLOB_SIZE, type ObservableNotesCache, OrbinumClient, type OrbinumClientConfig, OrbinumClientProvider, OrbinumWallet, type OrbinumWalletConfig, type OutgoingHint, OutgoingNoteRecord, PAGE_SIZE, PAYMENT_SLIP_SCHEME, PRECOMPILE_ADDR, type PairwiseEphWindowEntry, type PalletErrorKind, type PaymentSlipFields, type PersistParams, type PrecompileMethod, PrivacyKeyManager, type PrivacyMerkleProof, PrivacyModule, type PrivateTransferArgs, type PrivateTransferInput, type PrivateTransferOutput, type PrivateTransferParams, type PrivateTransferProofInputs, type ProofOptions, type ProviderFactory, QR_PAGE_MAX_CHARS, RECOVERED_TX_RESULT, type RawBlock, type RawBlockHeader, type RawTransferInput, type RawTransferOutput, type ReconstructDeps, type ReconstructedTxRecord, type RegisterAssetArgs, type RelayerInfo, RelayerStatusModule, type ResolveSpentSetParams, type ResolvedProverVersion, type ResolvedSpendVersion, type RpcV2MerkleProof, RpcV2Module, type RpcV2NullifierStatus, type RpcV2PoolAssetBalance, type RpcV2PoolStats, type RunScanParams, SPENDING_KEY_CANONICAL_ORIGIN, SPENDING_KEY_VERIFYING_CONTRACT, SPENDING_KEY_WARNING, type ScanChunkManifest, ScanCommitment, type ScanHint, type ScanHintPage, type ScanHintSource, ScanKeys, type ScanOptions, type ScanOutcome, type ScanProgress, type ScanResult, SecretStore, type SelfEphWindowEntry, type SelfStealthKeys, type SessionCacheDeps, type ShieldArgs, type ShieldBatchArgs, type ShieldBatchItem, type ShieldBatchParams, type ShieldOperation, type ShieldParams, type ShieldedEvent, type ShieldedPoolCall, type ShieldedPoolEvent, ShieldedPoolModule, ShieldedPoolPrecompile, type SlipImportKeys, SpendDetails, type SpendPlanProblem, type SpendPrivacyReads, type SpendVault, type SpendableInputsCheck, type SpendingKeyTypedData, type StatusChangeEvent, type StatusListener, SubstrateClient, type SystemHealth, TRANSFER_INPUTS, TRANSFER_OUTPUTS, type TokenInfo, type TokenTransfer, type TransferDeps, type TransferFactsRow, type TransferFactsSource, type TransferInputNote, type TransferOutputNote, type TransferParams, type TransferPlan, type TransferResult, type TransferStep, type TransferSubmitRequest, type TxFactsSource, TxHistoryStore, type TxKind, type TxLandingPollOptions, type TxResult, type UnsafeTxOptions, type UnshieldArgs, type UnshieldDeps, type UnshieldNoteParams, type UnshieldParams, type UnshieldPlan, type UnshieldProofInputs, type UnshieldProofResult, type UnshieldStep, type UnshieldSubmitRequest, type UnshieldedEvent, type UnverifyAssetArgs, VAULT_SCHEMA_VERSION, VaultConfigRecord, VaultLockedError, VaultStorage, VaultStore, type VaultStoreDeps, type VaultUnlockOptions, type VerifyAssetArgs, type VersionedArtifactProvider, type WalletScanKeys, type WalletSession, ZkNote, type ZkVerifierCircuitVersionInfo, type ZkVerifierHistoricalVersion, ZkVerifierModule, type ZkVerifierVersionStats, type ZkVerifierVkHash, accountIdHexToSs58, addressToAccountIdHex, addressToFieldElement, applyBatch, applyNoteStatus, assembleNoteTransfer, base64UrlDecode, base64UrlEncode, bigintTo32Be, bigintTo32Le, bigintTo32LeArr, blindTag, buildConfig, buildDummyTransferInput, buildShieldBatchOperations, buildShieldParams, buildZkNote, bytesToBigintLE, bytesToBjjScalar, cacheSession, canPairWith, canonicalAccountId, chainActiveCircuitVersion, checkSpendableInputs, claimFees, classifyChainError, clearSession, collectNullifiersToQuery, collectScanEntries, commitmentHexOf, computeNoteCommitment, computeNullifier, computePathIndices, connectInjectedExtension, createNoteDisclosureKey, createNotesCache, createWalletSession, decodeNoteBackup, decodeNoteDisclosureKey, decodeNoteTransferPage, decodePaymentSlip, decodePrecompileCalldata, decryptJson, decryptNoteRecord, deriveMasterKeyBytes, deriveOutgoingCipherKey, deriveOutgoingViewingKey, deriveOwnerPk, derivePairwiseEphSk, derivePairwiseSharedSecret, deriveSelfEphSk, deriveSpendingKeyFromMaster, deriveSpendingKeyFromSignature, deriveSpendingKeyMessageV2, deriveSpendingKeyTypedData, deriveStealthOwnerPk, deriveStealthSk, deriveVaultBlindKey, deriveVaultKey, deriveViewTag, deriveViewingPublicKey, deriveViewingSecretKey, detectCommitmentMismatch, encodeNoteBackup, encodeNoteTransferPages, encodePaymentSlip, encryptJson, encryptNote, ensureCreatedAt, ensureHexPrefix, evmAddressToAccountId, evmToImplicitSubstrate, evmToMappedAccountHex, evmToSubstrate, extractPalletError, failed, fastMulBase, fastMulPoint, fetchExtrinsicFacts, formatAmountPlain, formatBalance, formatORB, fromBase64, fromHex, gapMargin, generateFeeClaimProof, generateTransferProof, generateUnshieldProof, getInjectedExtensions, getPrecompileLabel, hasCachedSession, hasInjectedExtensions, hexToBigint, hexToNumber, implicitSubstrateToEvm, importNotesFromBackup, importPaymentSlip, isAbortError, isAlreadySpentError, isConnectionLossError, isEvmAddress, isGhostNoteError, isImplicitEvmAccount, isNativeAsset, isNoteSelfConsistent, isSpendable, isSs58, isSubstrateAddress, isUnifiedAddress, isValidLeafIndex, leHexToBigint, mapExtrinsicArgs, mapZkEventData, markInputsSpent, normalizeChainFingerprint, normalizeEvmAddress, normalizeNote, normalizeNotes, noteBlindTag, noteCreatedAt, noteCreatedTxHash, noteMatchesCommitment, noteOrigin, noteSpentTxHash, noteToTransferEntry, noteTxKind, openOutgoingBlob, openPaymentSlip, pairwiseEphWindow, palletErrorKind, parseAmount, parseEvmAddress, persistCursor, persistScanResults, planTransfer, planUnshield, randomBlinding, randomOutgoingBlob, reconstructOutgoingTxRecords, recoverOwnerPkPoint, recoverSelfStealthNote, refuseIfAlreadySpent, removeByCommitment, requireSessionKeys, reservePairwiseIndex, reserveSelfEphIndex, resolveSelfEphCeiling, resolveSpentSet, resolveSpentStatus, restoreSession, runScan, scalarToHex, scanAbortError, sealOutgoingBlob, sealPaymentSlip, selectGhosts, selectNotes, selfEphWindow, serializeMemo, sessionCacheKey, shortHash, signAndSubmitTx, spendableBalance, stampCreatedAt, stampCreatedTxHash, stampSpentTxHash, substrateSs58ToAccountIdHex, substrateToEvm, toBase64, toHex, toTxResult, transferNotes, treeIdOf, treeOf, truncateMiddle, tryDecryptNote, tryDecryptNoteVerbose, tryRecoverOutgoing, txLandedAfterError, unshieldNote, upsertNote, vaultReplacer, vaultReviver, vaultStorageName, windowSizeForCounter };
6052
+ export { type AssetRegisteredEvent, type AssetUnverifiedEvent, type AssetVerifiedEvent, BABYJUB_SUBORDER, BN254_R, type BackupImportKeys, type BlockInfo, type BuildNoteDeps, type BuildNoteParams, type Bytes32, CachedNullifier, type ChainInfo, ChainModule, type ChunkInfo, CircuitId, CircuitVersionResolver, type ClaimShieldedFeesParams, type ClientProviderConfig, type CoinSelection, type CollectScanEntriesParams, type CommitmentsInsertedEvent, type ConnectionStatus, type CryptoKey$1 as CryptoKey, CryptoPrecompiles, type DecodedPrecompile, DecryptPool, DecryptedMemo, type DynamicBuilder, ENCRYPTED_MEMO_SIZE, EncryptedMemo, EncryptedNoteRecord, EncryptedTxRecord, type EventData, type EventPhase, type EventRecord, type EvmAddressInfo, type EvmBlock, EvmClient, EvmExplorer, type EvmLog, type EvmSigner, type EvmTransaction, type EvmTxRequest, type EvmTxSummary, type ExtrinsicDecoder, type ExtrinsicFacts, type ExtrinsicRecord, type FeeClaimDeps, type FeeClaimParams, type FeeClaimProofInputs, type FeeClaimProofOutput, type FeeClaimStep, type FormatOptions, KNOWN_PALLET_ERRORS, KNOWN_PRECOMPILES, type KnownPrecompileInfo, LEAVES_PER_TREE, MIN_GASLESS_FEE, MIN_SIGNATURE_BYTES, MemoryVaultStorage, type MerkleRootUpdatedEvent, type MutableWalletSession, NATIVE_ASSET_ID, NOTE_BACKUP_VERSION, NOTE_BIGINT_FIELDS, NOTE_TRANSFER_URI_SCHEME, type NoteBackup, type NoteBackupEntry, type NoteBuildKeys, NoteBuilder, type NoteDisclosure, NoteFacts, NoteInput, type NoteOrigin, type NoteProvenanceRecord, NoteStatusUpdate, NoteStorage, type NoteTransferEntry, type NoteTransferPayload, type NoteWithMeta, type NotesCache, NullifierCache, type NullifierChunkBody, type NullifierManifest, type NullifierSource, NullifierSyncMeta, type NullifierTail, type NullifiersSpentEvent, OVK_BLOB_SIZE, type ObservableNotesCache, OrbinumClient, type OrbinumClientConfig, OrbinumClientProvider, OrbinumWallet, type OrbinumWalletConfig, type OutgoingHint, OutgoingNoteRecord, PAGE_SIZE, PAYMENT_SLIP_SCHEME, PRECOMPILE_ADDR, type PairwiseEphWindowEntry, type PalletErrorKind, type PaymentSlipFields, type PersistParams, type PkScope, type PrecompileMethod, PrivacyKeyManager, type PrivacyMerkleProof, PrivacyModule, type PrivateTransferArgs, type PrivateTransferInput, type PrivateTransferOutput, type PrivateTransferParams, type PrivateTransferProofInputs, type ProofOptions, type ProvenanceAmount, type ProvenancePeer, type ProvenanceSource, type ProviderFactory, QR_PAGE_MAX_CHARS, RECOVERED_TX_RESULT, type RawBlock, type RawBlockHeader, type RawTransferInput, type RawTransferOutput, type ReconstructDeps, type ReconstructedTxRecord, type RegisterAssetArgs, type RelayerInfo, RelayerStatusModule, type ResolveSpentSetParams, type ResolvedProverVersion, type ResolvedSpendVersion, type RpcV2MerkleProof, RpcV2Module, type RpcV2NullifierStatus, type RpcV2PoolAssetBalance, type RpcV2PoolStats, type RunScanParams, SPENDING_KEY_CANONICAL_ORIGIN, SPENDING_KEY_VERIFYING_CONTRACT, SPENDING_KEY_WARNING, type ScanChunkManifest, ScanCommitment, type ScanHint, type ScanHintPage, type ScanHintSource, ScanKeys, type ScanOptions, type ScanOutcome, type ScanProgress, type ScanResult, SecretStore, type SelfEphWindowEntry, type SelfStealthKeys, type SessionCacheDeps, type ShieldArgs, type ShieldBatchArgs, type ShieldBatchItem, type ShieldBatchParams, type ShieldOperation, type ShieldParams, type ShieldedEvent, type ShieldedPoolCall, type ShieldedPoolEvent, ShieldedPoolModule, ShieldedPoolPrecompile, type SlipImportKeys, SpendDetails, type SpendPlanProblem, type SpendPrivacyReads, type SpendVault, type SpendableInputsCheck, type SpendingKeyTypedData, type StatusChangeEvent, type StatusListener, SubstrateClient, type SystemHealth, TRANSFER_INPUTS, TRANSFER_OUTPUTS, type TokenInfo, type TokenTransfer, type TransferDeps, type TransferFactsRow, type TransferFactsSource, type TransferInputNote, type TransferOutputNote, type TransferParams, type TransferPlan, type TransferResult, type TransferStep, type TransferSubmitRequest, type TxFactsSource, TxHistoryStore, type TxKind, type TxLandingPollOptions, type TxResult, type UnsafeTxOptions, type UnshieldArgs, type UnshieldDeps, type UnshieldNoteParams, type UnshieldParams, type UnshieldPlan, type UnshieldProofInputs, type UnshieldProofResult, type UnshieldStep, type UnshieldSubmitRequest, type UnshieldedEvent, type UnverifyAssetArgs, VAULT_SCHEMA_VERSION, VaultConfigRecord, VaultLockedError, VaultStorage, VaultStore, type VaultStoreDeps, type VaultUnlockOptions, type VerifyAssetArgs, type VersionedArtifactProvider, type WalletScanKeys, type WalletSession, ZkNote, type ZkVerifierCircuitVersionInfo, type ZkVerifierHistoricalVersion, ZkVerifierModule, type ZkVerifierVersionStats, type ZkVerifierVkHash, accountIdHexToSs58, addressToAccountIdHex, addressToFieldElement, applyBatch, applyNoteStatus, assembleNoteTransfer, base64UrlDecode, base64UrlEncode, bigintTo32Be, bigintTo32Le, bigintTo32LeArr, blindTag, buildConfig, buildDummyTransferInput, buildShieldBatchOperations, buildShieldParams, buildZkNote, bytesToBigintLE, bytesToBjjScalar, cacheSession, canPairWith, canonicalAccountId, chainActiveCircuitVersion, checkSpendableInputs, claimFees, classifyChainError, clearSession, collectNullifiersToQuery, collectOutgoingFacts, collectScanEntries, commitmentHexOf, computeNoteCommitment, computeNullifier, computePathIndices, connectInjectedExtension, createNoteDisclosureKey, createNotesCache, createWalletSession, decodeNoteBackup, decodeNoteDisclosureKey, decodeNoteTransferPage, decodePaymentSlip, decodePrecompileCalldata, decryptJson, decryptNoteRecord, deriveMasterKeyBytes, deriveOutgoingCipherKey, deriveOutgoingViewingKey, deriveOwnerPk, derivePairwiseEphSk, derivePairwiseSharedSecret, deriveSelfEphSk, deriveSpendingKeyFromMaster, deriveSpendingKeyFromSignature, deriveSpendingKeyMessageV2, deriveSpendingKeyTypedData, deriveStealthOwnerPk, deriveStealthSk, deriveVaultBlindKey, deriveVaultKey, deriveViewTag, deriveViewingPublicKey, deriveViewingSecretKey, detectCommitmentMismatch, encodeNoteBackup, encodeNoteTransferPages, encodePaymentSlip, encryptJson, encryptNote, ensureCreatedAt, ensureHexPrefix, evmAddressToAccountId, evmToImplicitSubstrate, evmToMappedAccountHex, evmToSubstrate, extractPalletError, failed, fastMulBase, fastMulPoint, fetchExtrinsicFacts, formatAmountPlain, formatBalance, formatORB, fromBase64, fromHex, gapMargin, generateFeeClaimProof, generateTransferProof, generateUnshieldProof, getInjectedExtensions, getPrecompileLabel, hasCachedSession, hasInjectedExtensions, hasSourcePk, hexToBigint, hexToNumber, implicitSubstrateToEvm, importNotesFromBackup, importPaymentSlip, isAbortError, isAlreadySpentError, isConnectionLossError, isEvmAddress, isGhostNoteError, isHexOfLength, isImplicitEvmAccount, isNativeAsset, isNoteSelfConsistent, isSpendable, isSs58, isSubstrateAddress, isUnifiedAddress, isValidLeafIndex, leHexToBigint, mapExtrinsicArgs, mapZkEventData, markInputsSpent, mergeProvenance, normalizeChainFingerprint, normalizeEvmAddress, normalizeNote, normalizeNotes, noteBlindTag, noteCreatedAt, noteCreatedTxHash, noteMatchesCommitment, noteOrigin, noteSpentTxHash, noteToTransferEntry, noteTxKind, openOutgoingBlob, openPaymentSlip, outranks, pairwiseEphWindow, palletErrorKind, parseAmount, parseEvmAddress, persistCursor, persistScanResults, planTransfer, planUnshield, randomBlinding, randomOutgoingBlob, reconstructOutgoingTxRecords, recoverOwnerPkPoint, recoverSelfStealthNote, refuseIfAlreadySpent, regeneratePaymentSlip, removeByCommitment, requireSessionKeys, reservePairwiseIndex, reserveSelfEphIndex, resolveSelfEphCeiling, resolveSpentSet, resolveSpentStatus, restoreSession, runScan, scalarToHex, scanAbortError, sealOutgoingBlob, sealPaymentSlip, selectDescribingNote, selectDescribingNoteByCommitment, selectGhosts, selectNotes, selfEphWindow, serializeMemo, sessionCacheKey, shortHash, signAndSubmitTx, spendableBalance, stampCreatedAt, stampCreatedTxHash, stampSpentTxHash, substrateSs58ToAccountIdHex, substrateToEvm, toBase64, toHex, toTxResult, transferNotes, treeIdOf, treeOf, truncateMiddle, tryDecryptNote, tryDecryptNoteVerbose, tryRecoverOutgoing, txLandedAfterError, unshieldNote, upsertNote, vaultReplacer, vaultReviver, vaultStorageName, windowSizeForCounter };