@cloak.dev/sdk 0.2.2 → 0.2.3-staging.005e3de

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.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { PublicKey, Transaction, AddressLookupTableAccount, Connection, TransactionInstruction, Keypair, SendOptions, VersionedTransaction } from '@solana/web3.js';
1
+ import { PublicKey, Transaction, Keypair, AddressLookupTableAccount, Connection, TransactionInstruction, SendOptions, VersionedTransaction } from '@solana/web3.js';
2
2
 
3
3
  /**
4
4
  * The deployed Cloak program.
@@ -781,6 +781,199 @@ declare class LocalStorageAdapter implements StorageAdapter {
781
781
  deleteKeys(): void;
782
782
  }
783
783
 
784
+ /**
785
+ * How long the relay accepts a signed request after its `auth_issued_at`
786
+ * (`REQUEST_AUTH_MAX_AGE_SECONDS`, `api/request_auth.rs`).
787
+ *
788
+ * A FIRST-USE request past this window is rejected outright. The expiry exception the relay grants
789
+ * applies only to an exact replay of a request whose durable row already exists, which by
790
+ * definition never happens for a request that has not been accepted once.
791
+ */
792
+ declare const REQUEST_AUTH_MAX_AGE_SECONDS = 300;
793
+ /**
794
+ * How far ahead of the relay's clock a request may be stamped
795
+ * (`REQUEST_AUTH_MAX_FUTURE_SKEW_SECONDS`, `api/request_auth.rs`).
796
+ *
797
+ * `auth_issued_at` comes from `Date.now()`. On a server that is NTP-disciplined; in a browser it
798
+ * is the user's own machine clock, and a laptop more than 30 seconds fast cannot authenticate at
799
+ * all until its clock is corrected.
800
+ */
801
+ declare const REQUEST_AUTH_MAX_FUTURE_SKEW_SECONDS = 30;
802
+ /**
803
+ * The exact fields each endpoint signs. Both lists mirror the relay's `*_auth_request` builders and
804
+ * must stay in lockstep with them: adding a field on one side alone silently invalidates every
805
+ * signature, and the failure surfaces as a bare 401 with nothing pointing here.
806
+ */
807
+ declare const TRANSACT_AUTH_FIELDS: readonly ["encrypted_notes", "max_fee", "mint", "proof_bytes", "public_inputs", "recipient", "recipient_delivery_notes", "risk_quote", "sender"];
808
+ declare const TRANSACT_SWAP_AUTH_FIELDS: readonly ["close_timed_out", "dexes", "encrypted_notes", "exclude_dexes", "max_fee", "min_output_amount", "output_mint", "proof_bytes", "public_inputs", "recipient", "recipient_ata", "refund_blinding", "refund_pubkey", "retry_request_id", "risk_quote", "route_retry_attempts", "sender", "slippage_bps", "swap_max_retries"];
809
+ /**
810
+ * Serialize exactly like the relay's `canonical_json`: keys sorted bytewise, no whitespace.
811
+ *
812
+ * SCOPE. This is the SDK's half of a byte-for-byte agreement with one specific Rust function over
813
+ * one specific schema: the values reachable through {@link TRANSACT_AUTH_FIELDS} and
814
+ * {@link TRANSACT_SWAP_AUTH_FIELDS}, which are ASCII keys over strings, small unsigned integers,
815
+ * booleans, nulls, arrays and plain objects. Inside that schema the two implementations agree.
816
+ *
817
+ * Outside it they need not, so anything that could serialize differently on the two sides is
818
+ * REFUSED here rather than signed into a digest that silently fails to match:
819
+ *
820
+ * - Non-integer, non-finite and beyond-safe-integer numbers. `serde_json` renders an f64 as Rust
821
+ * does (`1e21`) where JavaScript renders `1e+21`, and `0.1 + 0.2` has no single spelling. Every
822
+ * number in both field lists is a small unsigned integer (`slippage_bps`, `route_retry_attempts`,
823
+ * `swap_max_retries`); u64 amounts already travel as decimal STRINGS for exactly this reason.
824
+ * - Functions and symbols as object VALUES. `JSON.stringify` drops such a key from the wire body
825
+ * while it stays in the signed view, so the two digests can never agree.
826
+ *
827
+ * `undefined` is NOT refused: it serializes as `null` here, `JSON.stringify` omits the key on the
828
+ * wire, and every optional field on the relay side is `Option<T>` with `#[serde(default)]`, so the
829
+ * relay sees `null` too. That is the same agreement {@link buildAuthRequest} relies on. The one
830
+ * field where it does not hold is `slippage_bps`, which is why that field is checked by name.
831
+ *
832
+ * `bigint` is accepted and rendered as a bare decimal integer, matching serde's u64/i64 output.
833
+ * Note that such a value cannot also go on the wire: `JSON.stringify` throws on a bigint. Use a
834
+ * decimal string in the body, as every SDK-built body does.
835
+ */
836
+ declare function canonicalJson(value: unknown): string;
837
+ interface RelayAuthFields {
838
+ sender: string;
839
+ auth_issued_at: string;
840
+ auth_nonce: string;
841
+ auth_signature: string;
842
+ /**
843
+ * How `auth_signature` was produced. Absent means an off-chain message, today's wire. Set to
844
+ * `"transaction"` when the wallet signed the no-op transaction envelope instead
845
+ * (`signRelayAuthPayload`). Not part of the signed view, so it never enters the digest.
846
+ */
847
+ auth_mode?: RelayAuthMode;
848
+ }
849
+ /**
850
+ * Everything an authenticated request needs EXCEPT the signature: the three fields that go on the
851
+ * wire alongside it, plus the exact bytes to sign.
852
+ *
853
+ * This exists because the holder of the pen is not always a `Keypair`. A browser wallet adapter
854
+ * exposes `signMessage(bytes): Promise<Uint8Array>` and no secret key at all, so the scheme has to
855
+ * be reachable in two halves: build the preimage here, sign it wherever the key actually lives,
856
+ * then put `sender` / `auth_issued_at` / `auth_nonce` and the base64 signature on the body.
857
+ *
858
+ * `message` is a plain ed25519 detached-signature preimage — nothing about the scheme changes
859
+ * between a local keypair and a wallet, only who signs it.
860
+ */
861
+ interface RelayAuthPreimage {
862
+ sender: string;
863
+ auth_issued_at: string;
864
+ auth_nonce: string;
865
+ message: Uint8Array;
866
+ }
867
+ /**
868
+ * Build the signed view and its preimage for one request, WITHOUT signing.
869
+ *
870
+ * `sender` is bound into the signed view and returned for the body — the relay rejects a request
871
+ * whose authenticated sender is not also present inside the signed payload. It must be the end
872
+ * user's own wallet: `sender` is the key screened for sanctions on a shield-to-shield send, so
873
+ * substituting an ephemeral or service key here moves the screening off the actual user.
874
+ *
875
+ * `nonce` and `issued_at` are generated here, once per call. Callers that re-POST a request must
876
+ * reuse the same preimage rather than rebuilding it, or the relay sees a brand-new request.
877
+ */
878
+ declare function buildRelayAuthPreimage(endpoint: string, programId: PublicKey, body: Record<string, unknown>, sender: PublicKey, nowSeconds?: number, fields?: readonly string[]): RelayAuthPreimage;
879
+ /**
880
+ * An async message signer standing in for a `Keypair`.
881
+ *
882
+ * A browser wallet adapter has no secret key to hand over — it exposes `signMessage`, and what
883
+ * this scheme needs signed is a plain ed25519 detached signature, which is exactly what that
884
+ * produces. Only the holder of the pen changes.
885
+ *
886
+ * COMPLIANCE — `walletPublicKey` becomes the request's authenticated `sender`, and `sender` is the
887
+ * key screened for sanctions on a shield-to-shield send. It MUST be the end user's own wallet.
888
+ * Putting an ephemeral, service-held or otherwise substituted key here moves the screening onto a
889
+ * key that is not the user: a compliance regression, not a shortcut.
890
+ */
891
+ interface RelayMessageSigner {
892
+ /** The end user's real wallet. Becomes the authenticated `sender`. Never an ephemeral key. */
893
+ walletPublicKey: PublicKey;
894
+ /** Wallet-adapter `signMessage`; must return the 64-byte ed25519 detached signature. */
895
+ signMessage: (message: Uint8Array) => Promise<Uint8Array>;
896
+ signAuthTransaction?: undefined;
897
+ }
898
+ /**
899
+ * A wallet that cannot sign messages at all (Ledger, Trezor) but can sign a transaction. It signs
900
+ * the deterministic no-op transaction from `buildAuthTransactionMessage` instead; the relay rebuilds
901
+ * the same bytes and verifies against them. See `signRelayAuthPayload`.
902
+ */
903
+ interface RelayTransactionSigner {
904
+ walletPublicKey: PublicKey;
905
+ /** Wallet-adapter `signTransaction`. Receives a legacy `Transaction` and returns it signed. */
906
+ signAuthTransaction: <T extends Transaction>(transaction: T) => Promise<T>;
907
+ signMessage?: undefined;
908
+ }
909
+ type RelayAuthSigner = RelayMessageSigner | RelayTransactionSigner;
910
+ /** How a relay-auth payload was signed. Mirrors `RelayAuthMode` in the relay's `auth_envelope.rs`. */
911
+ type RelayAuthMode = "message" | "transaction";
912
+ /**
913
+ * The transaction a message-less wallet signs to authenticate a relay payload.
914
+ *
915
+ * Legacy message, one instruction: `sender -> sender`, 0 lamports, System program. The 32-byte
916
+ * `recent_blockhash` slot carries `sha256(payload)`, so the signature binds the payload exactly as
917
+ * a message signature would, through a shape every hardware wallet can sign. It cannot land on
918
+ * chain: the network does not know that "blockhash". It names no Cloak program, endpoint or domain,
919
+ * so it leaks nothing if published. Byte-identical to the relay's `build_auth_transaction_message`;
920
+ * the vector is pinned on both sides.
921
+ */
922
+ declare function buildAuthTransactionMessage(sender: PublicKey, digest: Uint8Array): Transaction;
923
+ /** The exact bytes the wallet signs for `buildAuthTransactionMessage(sender, digest)`. */
924
+ declare function serializeAuthTransactionMessage(sender: PublicKey, digest: Uint8Array): Uint8Array;
925
+ /**
926
+ * Sign a relay-auth payload with whatever the caller holds: a local `Keypair` or a message signer
927
+ * signs the payload bytes; a transaction-only signer signs the no-op transaction carrying the
928
+ * payload's digest. Returns the 64-byte detached signature and the mode the relay must be told.
929
+ */
930
+ declare function signRelayAuthPayload(signer: RelayAuthSigner | Keypair, payload: Uint8Array): Promise<{
931
+ signature: Uint8Array;
932
+ mode: RelayAuthMode;
933
+ }>;
934
+ /**
935
+ * Turn a relay rejection into something the person in front of the screen can act on.
936
+ *
937
+ * Every string matched here is an `Error::Unauthorized` from `api/request_auth.rs`, and all of them
938
+ * arrive as the same bare 401. Two of them are not the caller's mistake at all: an approval that
939
+ * sat too long, and a machine clock that is simply wrong. Returns `null` for anything that is not
940
+ * an authentication rejection, so callers can append it only when there is something to add.
941
+ */
942
+ declare function explainRelayAuthRejection(responseText: string): string | null;
943
+ declare const REQUEST_AUTH_BATCH_DOMAIN = "CLOAK_RELAY_BATCH_AUTH_V1";
944
+ /**
945
+ * How long the relay accepts an item that arrives under a batch signature
946
+ * (`REQUEST_AUTH_BATCH_MAX_AGE_SECONDS`, `api/request_auth.rs`).
947
+ *
948
+ * Longer than the single-request window because one approval now stands in front of N
949
+ * submissions, each confirmed on chain in turn. Every digest is a specific proof over specific
950
+ * nullifiers and every item still burns its own nonce, so the extra time widens nothing except how
951
+ * long the SAME already-approved requests stay submittable.
952
+ */
953
+ declare const REQUEST_AUTH_BATCH_MAX_AGE_SECONDS = 600;
954
+ /**
955
+ * Most items one batch signature may cover (`REQUEST_AUTH_BATCH_MAX_ITEMS`, `api/request_auth.rs`).
956
+ *
957
+ * The practical bound is lower and comes from the chain, not the relay: every spend appends two
958
+ * roots to the on-chain ring of 100, so N items proved against one root push that root out of the
959
+ * ring by themselves before N reaches 50, and sooner with other traffic. Size a batch as
960
+ * `(100 - expected foreign inserts while it submits) / 2`, and chunk above that.
961
+ */
962
+ declare const RELAY_BATCH_AUTH_MAX_ITEMS = 64;
963
+ /** The batch envelope an item carries next to its per-request auth fields. Not part of the digest. */
964
+ interface RelayBatchAuthEnvelope {
965
+ /** Lowercase hex sha256 of every item's request-auth message, in the order the wallet signed. */
966
+ digests: string[];
967
+ }
968
+ /** Per-item wire fields under a batch signature: the single-request fields plus the envelope. */
969
+ interface RelayBatchAuthFields extends RelayAuthFields {
970
+ auth_batch: RelayBatchAuthEnvelope;
971
+ }
972
+ /** The digest an item contributes to a batch: sha256 of its request-auth message bytes, hex. */
973
+ declare function relayRequestDigestHex(preimage: RelayAuthPreimage): string;
974
+ /** Build the batch message the wallet signs. Byte-identical to `build_batch_auth_message`. */
975
+ declare function buildRelayBatchAuthMessage(programId: PublicKey, issuedAt: string, digests: readonly string[]): Uint8Array;
976
+
784
977
  interface ViewingKeyPair {
785
978
  privateKey: Uint8Array;
786
979
  publicKey: Uint8Array;
@@ -858,7 +1051,7 @@ declare const SIGN_IN_MESSAGE = "Cloak: Sign in\n\nSign this message to securely
858
1051
  * Relay stores nk and derives sk_d per note when exporting.
859
1052
  * Registration is authenticated via one-time nonce challenge.
860
1053
  */
861
- declare function registerViewingKey(relayUrl: string, userPubkey: PublicKey, nk: Uint8Array, signMessage: (message: Uint8Array) => Promise<Uint8Array>): Promise<void>;
1054
+ declare function registerViewingKey(relayUrl: string, userPubkey: PublicKey, nk: Uint8Array, signMessage: ((message: Uint8Array) => Promise<Uint8Array>) | RelayAuthSigner): Promise<void>;
862
1055
 
863
1056
  interface EncryptMetadataContext {
864
1057
  userPubkey?: PublicKey | string;
@@ -2081,7 +2274,7 @@ declare class RelayService {
2081
2274
  * );
2082
2275
  * ```
2083
2276
  */
2084
- registerViewingKey(userPubkey: string, viewingKey: string, signMessage: (message: Uint8Array) => Promise<Uint8Array>): Promise<boolean>;
2277
+ registerViewingKey(userPubkey: string, viewingKey: string, signMessage: ((message: Uint8Array) => Promise<Uint8Array>) | RelayAuthSigner): Promise<boolean>;
2085
2278
  /**
2086
2279
  * Fetch relay compliance master public key.
2087
2280
  * DEPRECATED: This endpoint is no longer used
@@ -2634,115 +2827,119 @@ declare function isBrowser(): boolean;
2634
2827
  declare function isBrowserLike(): boolean;
2635
2828
 
2636
2829
  /**
2637
- * How long the relay accepts a signed request after its `auth_issued_at`
2638
- * (`REQUEST_AUTH_MAX_AGE_SECONDS`, `api/request_auth.rs`).
2639
- *
2640
- * A FIRST-USE request past this window is rejected outright. The expiry exception the relay grants
2641
- * applies only to an exact replay of a request whose durable row already exists, which by
2642
- * definition never happens for a request that has not been accepted once.
2643
- */
2644
- declare const REQUEST_AUTH_MAX_AGE_SECONDS = 300;
2645
- /**
2646
- * How far ahead of the relay's clock a request may be stamped
2647
- * (`REQUEST_AUTH_MAX_FUTURE_SKEW_SECONDS`, `api/request_auth.rs`).
2648
- *
2649
- * `auth_issued_at` comes from `Date.now()`. On a server that is NTP-disciplined; in a browser it
2650
- * is the user's own machine clock, and a laptop more than 30 seconds fast cannot authenticate at
2651
- * all until its clock is corrected.
2652
- */
2653
- declare const REQUEST_AUTH_MAX_FUTURE_SKEW_SECONDS = 30;
2654
- /**
2655
- * The exact fields each endpoint signs. Both lists mirror the relay's `*_auth_request` builders and
2656
- * must stay in lockstep with them: adding a field on one side alone silently invalidates every
2657
- * signature, and the failure surfaces as a bare 401 with nothing pointing here.
2830
+ * One wallet approval for a batch of relay requests (CLOAK_RELAY_BATCH_AUTH_V1).
2831
+ *
2832
+ * ── The problem ───────────────────────────────────────────────────────────────────────────────
2833
+ * A payout to N recipients is N private spends, because the recipient is bound into each proof
2834
+ * through `ext_data_hash`. Every spend is its own `/transact` request, and the relay authenticates
2835
+ * every request with a wallet signature over that request's digest (`payload.ts`). So a payout of
2836
+ * forty asked the wallet forty times. The transactions are not the problem: the relay builds and
2837
+ * submits those and the user never signs them. The forty prompts were forty request signatures.
2838
+ *
2839
+ * ── The mechanism ─────────────────────────────────────────────────────────────────────────────
2840
+ * Every item runs the ordinary `transact` flow unchanged, up to the moment it would sign its
2841
+ * request. There it hands its finished body to the coordinator below and waits. When every item of
2842
+ * the batch is either waiting here or already finished, the coordinator stamps one `issued_at`,
2843
+ * builds each item's per-request preimage exactly as the single path would, takes the sha256 of
2844
+ * each preimage, and asks the wallet ONCE to sign the list of those digests. Each item then gets
2845
+ * back its own nonce and that one signature, plus the digest list the relay needs to verify it, and
2846
+ * proceeds to POST as before. The relay checks that the item's digest is in the signed list and that
2847
+ * the signature covers the list (`request_auth.rs`).
2848
+ *
2849
+ * ── What it deliberately keeps ────────────────────────────────────────────────────────────────
2850
+ * - Every item still has its own one-time nonce, so an item cannot be submitted twice.
2851
+ * - The user still signs every request they submit, as a list; nothing here can add a request the
2852
+ * wallet did not see without breaking the signature.
2853
+ * - A stale-root retry re-proves, which changes the item's digest. That item comes back here and
2854
+ * is signed in a NEW wave with whichever other items also had to re-prove: one more approval for
2855
+ * the retries, never one per row. `approvals` counts the waves so a caller can show that.
2856
+ * - Submissions after the signature are released `submitConcurrency` at a time, not all at once,
2857
+ * so the relay is not hit with N proofs in the same second while the signed window runs.
2858
+ *
2859
+ * Wave readiness is exact, not a timer: the coordinator knows how many items were registered and
2860
+ * what each one is doing, so it signs the moment nothing in the batch is still building a proof.
2861
+ *
2862
+ * Every row must therefore be IN FLIGHT at once: a row that has not started counts as building, and
2863
+ * the wave waits for it. Register every item (`item()`) BEFORE any row can fail, and always call
2864
+ * `finish()`: a declared item that never registers, or never finishes, holds the wave forever, and
2865
+ * `snapshot()` is how a caller sees that (`registered < expected`, or a row stuck in `building`). Do not run rows through a worker pool smaller than the batch (the pooled
2866
+ * rows park at the signature, the pool never frees, and nothing ever signs). Bound the expensive
2867
+ * part instead: `proofSlot()` limits how many rows prove at the same time.
2658
2868
  */
2659
- declare const TRANSACT_AUTH_FIELDS: readonly ["encrypted_notes", "max_fee", "mint", "proof_bytes", "public_inputs", "recipient", "recipient_delivery_notes", "risk_quote", "sender"];
2660
- declare const TRANSACT_SWAP_AUTH_FIELDS: readonly ["close_timed_out", "dexes", "encrypted_notes", "exclude_dexes", "max_fee", "min_output_amount", "output_mint", "proof_bytes", "public_inputs", "recipient", "recipient_ata", "refund_blinding", "refund_pubkey", "retry_request_id", "risk_quote", "route_retry_attempts", "sender", "slippage_bps", "swap_max_retries"];
2661
- /**
2662
- * Serialize exactly like the relay's `canonical_json`: keys sorted bytewise, no whitespace.
2663
- *
2664
- * SCOPE. This is the SDK's half of a byte-for-byte agreement with one specific Rust function over
2665
- * one specific schema: the values reachable through {@link TRANSACT_AUTH_FIELDS} and
2666
- * {@link TRANSACT_SWAP_AUTH_FIELDS}, which are ASCII keys over strings, small unsigned integers,
2667
- * booleans, nulls, arrays and plain objects. Inside that schema the two implementations agree.
2668
- *
2669
- * Outside it they need not, so anything that could serialize differently on the two sides is
2670
- * REFUSED here rather than signed into a digest that silently fails to match:
2671
- *
2672
- * - Non-integer, non-finite and beyond-safe-integer numbers. `serde_json` renders an f64 as Rust
2673
- * does (`1e21`) where JavaScript renders `1e+21`, and `0.1 + 0.2` has no single spelling. Every
2674
- * number in both field lists is a small unsigned integer (`slippage_bps`, `route_retry_attempts`,
2675
- * `swap_max_retries`); u64 amounts already travel as decimal STRINGS for exactly this reason.
2676
- * - Functions and symbols as object VALUES. `JSON.stringify` drops such a key from the wire body
2677
- * while it stays in the signed view, so the two digests can never agree.
2678
- *
2679
- * `undefined` is NOT refused: it serializes as `null` here, `JSON.stringify` omits the key on the
2680
- * wire, and every optional field on the relay side is `Option<T>` with `#[serde(default)]`, so the
2681
- * relay sees `null` too. That is the same agreement {@link buildAuthRequest} relies on. The one
2682
- * field where it does not hold is `slippage_bps`, which is why that field is checked by name.
2683
- *
2684
- * `bigint` is accepted and rendered as a bare decimal integer, matching serde's u64/i64 output.
2685
- * Note that such a value cannot also go on the wire: `JSON.stringify` throws on a bigint. Use a
2686
- * decimal string in the body, as every SDK-built body does.
2687
- */
2688
- declare function canonicalJson(value: unknown): string;
2869
+
2689
2870
  /**
2690
- * Everything an authenticated request needs EXCEPT the signature: the three fields that go on the
2691
- * wire alongside it, plus the exact bytes to sign.
2692
- *
2693
- * This exists because the holder of the pen is not always a `Keypair`. A browser wallet adapter
2694
- * exposes `signMessage(bytes): Promise<Uint8Array>` and no secret key at all, so the scheme has to
2695
- * be reachable in two halves: build the preimage here, sign it wherever the key actually lives,
2696
- * then put `sender` / `auth_issued_at` / `auth_nonce` and the base64 signature on the body.
2697
- *
2698
- * `message` is a plain ed25519 detached-signature preimage — nothing about the scheme changes
2699
- * between a local keypair and a wallet, only who signs it.
2871
+ * What one item of the batch holds. `submitTransactToRelay` calls `authorize` where it would
2872
+ * otherwise sign; the caller that ran the item calls `finish` when the item's flow has settled,
2873
+ * successfully or not, so the coordinator never waits on an item that is not coming.
2700
2874
  */
2701
- interface RelayAuthPreimage {
2702
- sender: string;
2703
- auth_issued_at: string;
2704
- auth_nonce: string;
2705
- message: Uint8Array;
2875
+ interface RelayBatchAuthItemHandle {
2876
+ /**
2877
+ * Wait for a proof slot. Every row of a batch is in flight at once, because all proofs must exist
2878
+ * before the one signature; this is where CPU and memory are bounded instead. Returns the release.
2879
+ */
2880
+ proofSlot(): Promise<() => void>;
2881
+ authorize(endpoint: string, programId: PublicKey, body: Record<string, unknown>, fields: readonly string[]): Promise<RelayBatchAuthFields>;
2882
+ finish(): void;
2706
2883
  }
2707
- /**
2708
- * Build the signed view and its preimage for one request, WITHOUT signing.
2709
- *
2710
- * `sender` is bound into the signed view and returned for the body the relay rejects a request
2711
- * whose authenticated sender is not also present inside the signed payload. It must be the end
2712
- * user's own wallet: `sender` is the key screened for sanctions on a shield-to-shield send, so
2713
- * substituting an ephemeral or service key here moves the screening off the actual user.
2714
- *
2715
- * `nonce` and `issued_at` are generated here, once per call. Callers that re-POST a request must
2716
- * reuse the same preimage rather than rebuilding it, or the relay sees a brand-new request.
2717
- */
2718
- declare function buildRelayAuthPreimage(endpoint: string, programId: PublicKey, body: Record<string, unknown>, sender: PublicKey, nowSeconds?: number, fields?: readonly string[]): RelayAuthPreimage;
2719
- /**
2720
- * An async message signer standing in for a `Keypair`.
2721
- *
2722
- * A browser wallet adapter has no secret key to hand over — it exposes `signMessage`, and what
2723
- * this scheme needs signed is a plain ed25519 detached signature, which is exactly what that
2724
- * produces. Only the holder of the pen changes.
2725
- *
2726
- * COMPLIANCE — `walletPublicKey` becomes the request's authenticated `sender`, and `sender` is the
2727
- * key screened for sanctions on a shield-to-shield send. It MUST be the end user's own wallet.
2728
- * Putting an ephemeral, service-held or otherwise substituted key here moves the screening onto a
2729
- * key that is not the user: a compliance regression, not a shortcut.
2730
- */
2731
- interface RelayAuthSigner {
2732
- /** The end user's real wallet. Becomes the authenticated `sender`. Never an ephemeral key. */
2733
- walletPublicKey: PublicKey;
2734
- /** Wallet-adapter `signMessage`; must return the 64-byte ed25519 detached signature. */
2735
- signMessage: (message: Uint8Array) => Promise<Uint8Array>;
2884
+ interface RelayBatchAuthCoordinatorOptions {
2885
+ /**
2886
+ * Who signs. A `Keypair` signs locally (tests, scripts, server-side callers); a wallet adapter
2887
+ * signs through `signMessage` and its `walletPublicKey` becomes every item's authenticated
2888
+ * `sender`. COMPLIANCE: that key is the one screened for sanctions on a shield-to-shield send,
2889
+ * so it must be the end user's own wallet, never a service or ephemeral key.
2890
+ */
2891
+ signer: RelayAuthSigner | Keypair;
2892
+ /** How many items will call `item()`. The wave signs only when all of them are accounted for. */
2893
+ items: number;
2894
+ /** Rows proving at the same time. Proofs are CPU-bound; default 2. */
2895
+ proofConcurrency?: number;
2896
+ /** Items released to submit at once after a signature. Default 3. */
2897
+ submitConcurrency?: number;
2898
+ onProgress?: (status: string) => void;
2736
2899
  }
2737
- /**
2738
- * Turn a relay rejection into something the person in front of the screen can act on.
2739
- *
2740
- * Every string matched here is an `Error::Unauthorized` from `api/request_auth.rs`, and all of them
2741
- * arrive as the same bare 401. Two of them are not the caller's mistake at all: an approval that
2742
- * sat too long, and a machine clock that is simply wrong. Returns `null` for anything that is not
2743
- * an authentication rejection, so callers can append it only when there is something to add.
2744
- */
2745
- declare function explainRelayAuthRejection(responseText: string): string | null;
2900
+ declare class RelayBatchAuthCoordinator {
2901
+ private readonly signer;
2902
+ private readonly sender;
2903
+ private readonly expectedItems;
2904
+ private readonly submitConcurrency;
2905
+ private readonly proofConcurrency;
2906
+ private proofSlotsInUse;
2907
+ private readonly proofQueue;
2908
+ private readonly onProgress?;
2909
+ private readonly items;
2910
+ private signing;
2911
+ private slotsInUse;
2912
+ private readonly ready;
2913
+ private waves;
2914
+ constructor(options: RelayBatchAuthCoordinatorOptions);
2915
+ /** The wallet that signs and is every item's authenticated `sender`. */
2916
+ get walletPublicKey(): PublicKey;
2917
+ /** How many times the signer has been asked so far: one per wave. */
2918
+ get approvals(): number;
2919
+ /** Where every item is right now. For progress UIs and for diagnosing a batch that never signs. */
2920
+ snapshot(): {
2921
+ building: number;
2922
+ waiting: number;
2923
+ submitting: number;
2924
+ done: number;
2925
+ proving: number;
2926
+ proofQueue: number;
2927
+ registered: number;
2928
+ expected: number;
2929
+ };
2930
+ /** Register one item. Throws past `items`: the wave condition would never be reachable. */
2931
+ item(): RelayBatchAuthItemHandle;
2932
+ private acquireProofSlot;
2933
+ private authorize;
2934
+ private finish;
2935
+ private releaseSlot;
2936
+ /** Sign when every registered item is either waiting here or finished, and nothing is building. */
2937
+ private maybeSign;
2938
+ private signWave;
2939
+ /** Release signed items to submit, `submitConcurrency` at a time, in signing order. */
2940
+ private pump;
2941
+ }
2942
+ declare function createRelayBatchAuthCoordinator(options: RelayBatchAuthCoordinatorOptions): RelayBatchAuthCoordinator;
2746
2943
 
2747
2944
  /**
2748
2945
  * The circuit artifacts this SDK build proves against.
@@ -2931,6 +3128,27 @@ interface ExternalFeePayerAdapter {
2931
3128
  */
2932
3129
  getPaymentAccountHints?: () => Promise<PublicKey[]>;
2933
3130
  }
3131
+ /**
3132
+ * Refuse a transaction whose input notes do not agree on a mint, or do not
3133
+ * agree with the mint the caller says it is spending.
3134
+ *
3135
+ * TWO SEPARATE PROPERTIES, and they catch different mistakes:
3136
+ *
3137
+ * 1. HOMOGENEITY, always enforced. Every pool is a single mint, so a set of
3138
+ * inputs spanning two mints cannot be a valid spend of either. There is no
3139
+ * legitimate call that does this, so it needs no opt-in. Zero-amount
3140
+ * padding notes are exempt: they are placeholders, not value.
3141
+ *
3142
+ * 2. INTENT, enforced when `options.expectedMint` is given. Homogeneity alone
3143
+ * cannot catch the dangerous case — ONE note of the wrong mint is a
3144
+ * perfectly homogeneous set. Only the caller knows which asset the user
3145
+ * asked for, so only the caller can supply it, and this is where it is
3146
+ * checked.
3147
+ *
3148
+ * The error names both mints, because the whole failure mode is that the two
3149
+ * were assumed to be the same.
3150
+ */
3151
+ declare function assertInputMints(inputUtxos: Utxo[], expectedMint?: PublicKey): void;
2934
3152
  /**
2935
3153
  * Options for transact operation
2936
3154
  */
@@ -2973,6 +3191,14 @@ interface TransactOptions {
2973
3191
  signTransaction?: <T extends Transaction | VersionedTransaction>(transaction: T) => Promise<T>;
2974
3192
  /** Wallet adapter signMessage for viewing-key registration auth. */
2975
3193
  signMessage?: (message: Uint8Array) => Promise<Uint8Array>;
3194
+ /**
3195
+ * Relay authentication for a wallet that cannot sign messages at all (Ledger, Trezor): the
3196
+ * wallet-adapter `signTransaction`, used to sign a deterministic no-op transaction that carries
3197
+ * the request digest in its blockhash slot instead of signing the text (`relay/payload.ts`,
3198
+ * `buildAuthTransactionMessage`). Also used for viewing-key registration. Requires
3199
+ * `walletPublicKey` (or `depositorPublicKey`). `signMessage` wins when both are present.
3200
+ */
3201
+ signAuthTransaction?: <T extends Transaction>(transaction: T) => Promise<T>;
2976
3202
  /** Public key of the depositor when using signTransaction */
2977
3203
  depositorPublicKey?: PublicKey;
2978
3204
  /** Wallet public key for relay submissions when depositor keypair isn't used. */
@@ -3184,6 +3410,38 @@ interface TransactOptions {
3184
3410
  * transaction. Omit for unchanged default behavior.
3185
3411
  */
3186
3412
  externalFeePayer?: ExternalFeePayerAdapter;
3413
+ /**
3414
+ * The mint the CALLER believes it is spending. When set, the SDK refuses the
3415
+ * transaction if the input notes say otherwise.
3416
+ *
3417
+ * ── Why this exists ────────────────────────────────────────────────────────
3418
+ * Every flow takes the pool from the notes it is handed
3419
+ * (`inputUtxos[0].mintAddress`), which is correct — the notes are the source
3420
+ * of truth for which pool they belong to. But it means the SDK cannot tell a
3421
+ * deliberate USDC withdrawal from a SOL withdrawal that was handed a USDC
3422
+ * note by mistake. Both are well-formed, both prove, and both settle.
3423
+ *
3424
+ * A consumer hit exactly that: its note selector sorted by RAW base units
3425
+ * across a store holding every mint, so with 6-decimal USDC and 9-decimal
3426
+ * SOL a stablecoin note outranked every SOL note whenever
3427
+ * `usdc > 1000 * sol`. A user sending 0.05 SOL moved ~50 USDC to the
3428
+ * recipient instead, and every layer reported SOL.
3429
+ *
3430
+ * Nothing about that is a protocol failure — the proof, the nullifier and the
3431
+ * pool accounting are all correct, and the user spent their own note. It is a
3432
+ * failure of INTENT, and intent is the one thing only the caller knows. So
3433
+ * the caller may now state it, and the SDK will hold it.
3434
+ *
3435
+ * Optional for backward compatibility, but pass it on every flow you can.
3436
+ */
3437
+ expectedMint?: PublicKey;
3438
+ /**
3439
+ * One item of a batch approval, from `createRelayBatchAuthCoordinator().item()`. When present the
3440
+ * relay request is authenticated with the batch's single wallet signature instead of a prompt of
3441
+ * its own; `transactBatch` sets it for every item it runs. Ignored on the deposit path, which the
3442
+ * relay does not authenticate this way.
3443
+ */
3444
+ relayAuthBatch?: RelayBatchAuthItemHandle;
3187
3445
  }
3188
3446
  /**
3189
3447
  * Switchboard-style response: a pre-built instruction.
@@ -3288,6 +3546,13 @@ interface SubmitTransactToRelayArgs {
3288
3546
  * or server-side key here is a compliance regression, not a shortcut.
3289
3547
  */
3290
3548
  relayAuthSigner?: RelayAuthSigner;
3549
+ /**
3550
+ * Batch alternative to both signers above: the request is authenticated by the batch's one
3551
+ * wallet signature (CLOAK_RELAY_BATCH_AUTH_V1) instead of signing here. Takes precedence over
3552
+ * `depositorKeypair` and `relayAuthSigner` when present, because the coordinator already holds
3553
+ * whichever of those the batch was created with.
3554
+ */
3555
+ relayAuthBatch?: RelayBatchAuthItemHandle;
3291
3556
  settlement: SettlementContext;
3292
3557
  /** True while the caller still has a re-prove budget for a stale root. */
3293
3558
  canRetryStaleRoot: boolean;
@@ -3738,6 +4003,32 @@ declare function cleanupStalePendingOperations(maxAgeMs?: number): {
3738
4003
  removedWithdrawals: number;
3739
4004
  };
3740
4005
 
4006
+ interface TransactBatchItem {
4007
+ params: TransactParams;
4008
+ /** Per-item overrides (recipient viewing key, chain-note salt, expected mint, ...). */
4009
+ options?: Partial<TransactOptions>;
4010
+ }
4011
+ interface TransactBatchOptions extends Omit<TransactOptions, "relayAuthBatch"> {
4012
+ /** Items proved at the same time. Proofs are CPU-bound; default 2. */
4013
+ proofConcurrency?: number;
4014
+ /** Items released to the relay at the same time after the signature. Default 3. */
4015
+ submitConcurrency?: number;
4016
+ }
4017
+ type TransactBatchItemOutcome = {
4018
+ status: "fulfilled";
4019
+ value: TransactResult;
4020
+ } | {
4021
+ status: "rejected";
4022
+ reason: unknown;
4023
+ };
4024
+ interface TransactBatchResult {
4025
+ /** One entry per item, in input order. A rejected item did not settle; the others are unaffected. */
4026
+ results: TransactBatchItemOutcome[];
4027
+ /** Wallet approvals raised: 1 for the batch, plus 1 per wave of stale-root re-proves. */
4028
+ approvals: number;
4029
+ }
4030
+ declare function transactBatch(items: readonly TransactBatchItem[], options: TransactBatchOptions): Promise<TransactBatchResult>;
4031
+
3741
4032
  /**
3742
4033
  * Recipient-addressed delivery envelope (CLKD1).
3743
4034
  *
@@ -4668,14 +4959,691 @@ declare class SimpleWallet {
4668
4959
  sync(): Promise<void>;
4669
4960
  }
4670
4961
 
4962
+ /**
4963
+ * What a quote option attests, which is NOT the same question as whether the rail worked.
4964
+ *
4965
+ * Mirrors the service's `rails::Attestation` enum field-for-field (`services/api/bridge/src/rails/
4966
+ * mod.rs`) rather than collapsing it into a boolean: 1Click signs the deposit address AND the
4967
+ * recipient, so a substituted address is detectable by the client even if the service substituted
4968
+ * it. Jupiter signs nothing, so on that rail the user is trusting Cloak — and a UI reading this
4969
+ * union is forced to say so, where a UI reading a boolean could quietly treat both the same.
4970
+ */
4971
+ type BridgeRailAttestation = {
4972
+ kind: "ed25519";
4973
+ verified: boolean;
4974
+ signer: string;
4975
+ } | {
4976
+ kind: "none";
4977
+ checks: string[];
4978
+ note: string;
4979
+ };
4980
+ interface BridgeRailQuoteOption {
4981
+ /** `"oneclick" | "jupiter"` today; typed as `string` so a new rail the service adds is not a
4982
+ * compile error here — the attestation union is what a caller must actually branch on. */
4983
+ rail: string;
4984
+ /** False means a mis-sent or under-filled transfer has NO automated recovery on this rail. */
4985
+ refunds: boolean;
4986
+ /** Whether `depositAddress` is reusable, or fresh per order and expiring. */
4987
+ addressLifetime: string;
4988
+ /** What the rail expects to deliver. NOT a promise — never display this alone. */
4989
+ amountOut: bigint;
4990
+ /** The floor the rail commits to. This is the number a UI should lead with. */
4991
+ minAmountOut: bigint;
4992
+ timeEstimateSeconds?: number;
4993
+ /** The rail's own expiry, when it returns one. Do not invent a value it attests to. */
4994
+ expiresAt?: string;
4995
+ /** Where the user sends funds on the origin chain. Absent on a dry quote (`allocate: false`),
4996
+ * where nothing has been reserved and there is nothing to send to yet. */
4997
+ depositAddress?: string;
4998
+ attestation: BridgeRailAttestation;
4999
+ }
5000
+ interface BridgeRailProblem {
5001
+ rail: string;
5002
+ reason: string;
5003
+ }
5004
+ interface BridgeRailQuoteRequest {
5005
+ /** The derived receiving address R. Base58, on Solana. */
5006
+ recipient: string;
5007
+ originChain: string;
5008
+ /** Base units on the origin chain, as a bigint — a JS `number` loses precision above 2^53 and
5009
+ * this can be an arbitrary token amount. */
5010
+ amountBaseUnits: bigint;
5011
+ /** Origin-chain address an unfillable 1Click order refunds to. Required for that rail. */
5012
+ refundTo?: string;
5013
+ rails?: string[];
5014
+ /** False (the default) sends `dry: true` to 1Click and skips GUM's allocation probes: nothing is
5015
+ * reserved and no `depositAddress` comes back. Requesting an allocation for a deposit the quote
5016
+ * gate might still refuse is the wrong order of operations — leave this false until the caller
5017
+ * has already decided to proceed. */
5018
+ allocate?: boolean;
5019
+ }
5020
+ interface BridgeRailQuoteResponse {
5021
+ options: BridgeRailQuoteOption[];
5022
+ /** Errors from rails that failed, so one rail being down does not hide the other's answer. */
5023
+ unavailable: BridgeRailProblem[];
5024
+ }
5025
+ /**
5026
+ * Mirrors the service's `api::status::DeliveryState` (`services/api/bridge/src/api/status.rs`),
5027
+ * `snake_case` on the wire and identical here. `"unknown"` is a real answer, not a parse failure:
5028
+ * Jupiter/GUM hands out permanent deposit addresses and has no per-transfer status API, so on
5029
+ * that rail the service ALWAYS answers `"unknown"` and says so in `detail` — poll the recipient's
5030
+ * on-chain balance instead. `"expired"` is derived by the service from the order deadline, not a
5031
+ * value 1Click returns.
5032
+ */
5033
+ type BridgeDeliveryState = "pending" | "delivered" | "refunded" | "expired" | "unknown";
5034
+ interface BridgeRailStatusResult {
5035
+ /** The rail that answered, echoed from the service. */
5036
+ rail: string;
5037
+ state: BridgeDeliveryState;
5038
+ /** The service's own explanation of `state`, e.g. why a `"jupiter"` query is `"unknown"`. */
5039
+ detail: string;
5040
+ }
5041
+ interface BridgeRail {
5042
+ readonly id: string;
5043
+ quote(req: BridgeRailQuoteRequest): Promise<BridgeRailQuoteResponse>;
5044
+ /**
5045
+ * `rail` is the `rail` of the quote option whose `depositAddress` this is: the service keys its
5046
+ * status lookup on (address, rail) because the two rails have unrelated status surfaces.
5047
+ */
5048
+ status(depositAddress: string, rail: string): Promise<BridgeRailStatusResult>;
5049
+ }
5050
+ /**
5051
+ * The one constructor this package exposes for talking to the bridge rails — always through
5052
+ * Cloak's own service, never through a rail's API or Kora.
5053
+ *
5054
+ * `relayUrl` is checked HERE, at construction, in addition to the check `relayFetch` repeats on
5055
+ * every call: a caller who mistypes a rail's own base URL (or Kora's) finds out immediately rather
5056
+ * than on first use, and `assertAllowedRelayOrigin` in this function's own body is what lets
5057
+ * `relay-origin-lock.test.ts` enumerate this door the same way it enumerates `RelayService`'s
5058
+ * constructor.
5059
+ */
5060
+ declare function cloakBridgeRail(relayUrl: string): BridgeRail;
5061
+
5062
+ /**
5063
+ * The paymaster client — the SOL top-up that lets a bridge receiver R (which arrives holding only
5064
+ * bridged tokens, no SOL) shield without the user's own wallet ever appearing on chain.
5065
+ *
5066
+ * Talks ONLY to Cloak's own bridge service (`api.cloak.ag/bridge/paymaster/*`), never to Kora
5067
+ * directly: under fixed pricing (docs/00-DECISION-STATE.md, PAYMASTER ECONOMICS) the Kora API key is
5068
+ * worth real money to whoever holds it, and a browser bundle is not a place that can keep a secret.
5069
+ *
5070
+ * ── The two-round-trip shape, and why it is not one call ─────────────────────────────────────
5071
+ * `prepare` returns an UNSIGNED transaction the service built and priced, plus a voucher that MACs
5072
+ * the exact message bytes. The client's receiving key R signs it — R never leaves the client, and
5073
+ * this is the only place it signs anything for the paymaster flow — and hands the bytes back
5074
+ * unchanged to `cosign`, which recognises them via the voucher and only THEN asks Kora to co-sign as
5075
+ * fee payer. The paymaster's key never leaves the server; R's key never leaves the client; neither
5076
+ * round trip needs the other's secret. See `services/api/bridge/src/api/paymaster.rs` for the
5077
+ * server half of this contract.
5078
+ *
5079
+ * ── Why the shape check below is not optional ────────────────────────────────────────────────
5080
+ * `cosign` answers exactly one question — "is this the message I built?" — and answers it by MAC,
5081
+ * not by re-deriving policy from an arbitrary transaction. That makes `prepare`'s response the ONLY
5082
+ * point where an unexpected instruction could sneak in (a compromised service, a MITM'd response, a
5083
+ * bug that built the wrong thing), because nothing downstream re-checks it. `validate
5084
+ * PaymasterTopUpTransaction` is that one check, and it runs BEFORE R signs anything: signing is the
5085
+ * one irreversible step in this flow from the client's point of view, so the check that matters has
5086
+ * to sit in front of it, not after.
5087
+ */
5088
+
5089
+ /** What `prepare`'s response is checked against before R ever signs it. */
5090
+ interface PaymasterTopUpExpectation {
5091
+ /** R — the only account this transaction may fund or spend from. */
5092
+ recipient: PublicKey;
5093
+ /** The lamports we asked for. `ix0.lamports` may come in at or under this, never over. */
5094
+ maxGrantLamports: bigint;
5095
+ feeMint: PublicKey;
5096
+ /** The fee Kora actually quoted at `prepare` time. `ix1.amount`, if `ix1` exists, must equal this
5097
+ * exactly — not "at most", because the fee is a quote the service already committed to, not a
5098
+ * ceiling. */
5099
+ feeTokenAmount: bigint;
5100
+ /** The paymaster's own token account owner, from `prepare`'s `payment_address`. `ix1`'s
5101
+ * destination must be exactly THIS address's ATA for `feeMint` — nothing else, and never an ATA
5102
+ * derived from `recipient` or from an address the caller does not already trust. */
5103
+ paymentAddress: PublicKey;
5104
+ }
5105
+ /**
5106
+ * The one check standing between `prepare`'s response and R's signature.
5107
+ *
5108
+ * Two instructions, and the shape IS the security story (`paymaster/topup.rs`'s own words, mirrored
5109
+ * here on the verifying side):
5110
+ *
5111
+ * ix0 System transfer, paymaster -> recipient, <= the lamports we asked for
5112
+ * ix1 (optional) SPL transfer, recipient's fee-mint ATA -> paymaster's fee-mint ATA, authority
5113
+ * = recipient, amount == the quoted fee
5114
+ *
5115
+ * Anything else — a third instruction, a different destination, a different authority, an amount
5116
+ * that does not match the quote — is refused. This function throws rather than returning a verdict:
5117
+ * there is no partial-trust path here, and a caller that wants to keep going after a refusal is a
5118
+ * caller papering over a corrupted or hostile response.
5119
+ */
5120
+ declare function validatePaymasterTopUpTransaction(tx: Transaction, expect: PaymasterTopUpExpectation): void;
5121
+ interface PaymasterTopUpResult {
5122
+ /** Fully signed by both R and the paymaster. The caller submits it — Kora's own
5123
+ * `signAndSendTransaction` is disabled service-side, and the broadcast should not carry the
5124
+ * service's IP any more than the deposit itself should carry R's. */
5125
+ transaction: Transaction;
5126
+ feeTokenAmount: bigint;
5127
+ feeMint: string;
5128
+ paymentAddress: string;
5129
+ }
5130
+ /**
5131
+ * The whole paymaster flow: prepare, verify the shape, sign as R, cosign, return the fully-signed
5132
+ * transaction for the caller to submit.
5133
+ *
5134
+ * `receiver` is the derived bridge receiving key (`deriveBridgeReceiver`). It signs here and only
5135
+ * here in this file's flow, and its secret never leaves this function — nothing above needs it, and
5136
+ * nothing here sends it anywhere.
5137
+ */
5138
+ declare function fundReceiverViaPaymaster(relayUrl: string, receiver: Keypair, grantLamports: bigint): Promise<PaymasterTopUpResult>;
5139
+
5140
+ /**
5141
+ * Deriving a bridge receiving address.
5142
+ *
5143
+ * One single-use address per deposit, from the wallet's own key material. Deterministic, offline,
5144
+ * and stateless — which is what lets every other function here work on a device that has never
5145
+ * seen this deposit before.
5146
+ */
5147
+
5148
+ declare const BRIDGE_ESCROW_LABEL = "cloak_bridge_escrow";
5149
+ /**
5150
+ * The index MUST be a small sequential counter (0, 1, 2, …), never a timestamp.
5151
+ *
5152
+ * This is not a style preference: discovery works by deriving indices 0…N and reading each on
5153
+ * chain. A timestamp index is unreachable by any scan, so a deposit made under one is invisible to
5154
+ * every device except the one that made it — which is precisely the failure that made a resume
5155
+ * link look necessary. The prototype harness used `Date.now()` for test hermeticity and those
5156
+ * deposits are, correctly, undiscoverable.
5157
+ */
5158
+ /**
5159
+ * The largest index discovery could plausibly reach. A scan is one round of on-chain reads per
5160
+ * index, so anything past a few thousand is not recoverable in practice — and a timestamp (~1.8e12)
5161
+ * is not recoverable even in principle. Rejecting it here is the difference between the constraint
5162
+ * being documented and it being enforced.
5163
+ */
5164
+ declare const MAX_RECEIVER_INDEX = 10000;
5165
+ declare function deriveBridgeReceiver(nk: Uint8Array | Buffer, index: number): Keypair;
5166
+
5167
+ /**
5168
+ * Finding a user's bridge deposits from their key material alone.
5169
+ *
5170
+ * THIS IS THE FUNCTION THAT MAKES THE BRIDGE AN SDK CAPABILITY RATHER THAN A WEB FEATURE.
5171
+ *
5172
+ * Derive receivers for index 0…N and read each on chain. No stored state, no link to carry, no
5173
+ * localStorage. It works on a device that has never seen the app, in a CLI, on mobile, after a
5174
+ * browser wipe. An earlier design proposed a shareable URL to carry the deposit index to a second
5175
+ * device; the index does not need carrying, it needs scanning, and the link it replaced was a
5176
+ * packaged correlation between an origin payment and a Solana address about to enter the pool.
5177
+ */
5178
+
5179
+ type BridgeDepositState =
5180
+ /** Nothing ever happened at this index. */
5181
+ "unused"
5182
+ /** Funded for a deposit, but no tokens have arrived. */
5183
+ | "awaiting"
5184
+ /** Tokens are sitting at the receiver, in the open, not yet shielded. */
5185
+ | "arrived"
5186
+ /** Shielded, and the receiver's token account has been closed. */
5187
+ | "complete"
5188
+ /** Shielded, but the token account is still open and holding its rent. */
5189
+ | "needs-cleanup"
5190
+ /**
5191
+ * An RPC read failed while checking this index. This is NOT evidence of absence — it is the
5192
+ * opposite of "unused" in every way that matters to a caller: a rate-limited or dropped read
5193
+ * used to fall back to a zero/empty default, which reported a real deposit's index as nothing-
5194
+ * here, indistinguishable from the index truly never having been touched. A user reading that
5195
+ * concludes their money is gone. `error` on the deposit explains what failed; the fix is to
5196
+ * retry, not to trust this entry's tokenBalance/lamports, which are placeholders.
5197
+ */
5198
+ | "unknown";
5199
+ interface BridgeDeposit {
5200
+ index: number;
5201
+ receiver: PublicKey;
5202
+ tokenAccount: PublicKey;
5203
+ state: BridgeDepositState;
5204
+ /** Placeholder 0n when state is "unknown" — the read that would have set this failed. */
5205
+ tokenBalance: bigint;
5206
+ /** Placeholder 0 when state is "unknown" — the read that would have set this failed. */
5207
+ lamports: number;
5208
+ /** Signature of the shield-pool transaction, when one was found. Most recent, if more than one — see indexReused. */
5209
+ shieldSignature?: string;
5210
+ /**
5211
+ * True when this receiver's own history holds more than one shield-pool transaction: this index
5212
+ * was used for more than one deposit. Two deposits at the same index share one on-chain address,
5213
+ * which links them to each other, and `shieldSignature` alone would silently pick one and hide
5214
+ * that a second one exists. A caller MUST warn the user instead of treating this as a single
5215
+ * ordinary deposit.
5216
+ */
5217
+ indexReused?: boolean;
5218
+ /** Every shield-pool signature found at this receiver, most recent first. Present only when indexReused. */
5219
+ shieldSignatures?: string[];
5220
+ /** Set only when state is "unknown": the failure that made this index's status unconfirmable. */
5221
+ error?: string;
5222
+ }
5223
+ interface DiscoverOptions {
5224
+ /**
5225
+ * How many indices to derive, starting at 0. Discovery is one to a few on-chain reads per index,
5226
+ * so this bounds the cost of a scan — it is NOT a claim about where deposits can live. An index
5227
+ * can be as large as MAX_RECEIVER_INDEX (10,000, see ./derive), and the default of 20 only covers
5228
+ * indices allocated sequentially with the stopAfterUnused gap tolerance below. A caller with
5229
+ * reason to believe a deposit landed further out (a CLI --index flag, a resumed session that
5230
+ * knows its own counter) MUST pass a larger scanDepth explicitly — anything past the configured
5231
+ * depth is simply never read, and reports as neither found nor absent, because it was not looked
5232
+ * at. Clamped to MAX_RECEIVER_INDEX + 1 (the count of valid indices, 0 through MAX_RECEIVER_INDEX
5233
+ * inclusive): deriveBridgeReceiver throws past that ceiling, and without the clamp a caller who
5234
+ * over-estimates scanDepth turns that into a mid-scan crash that discards every deposit already
5235
+ * found in the same call.
5236
+ */
5237
+ scanDepth?: number;
5238
+ /** Stop after this many consecutive unused indices. An "unknown" index (RPC failure) neither
5239
+ * counts toward nor resets this run — it carries no information about presence or absence, so
5240
+ * letting it break a real run of unused indices early would reintroduce the same false-absence
5241
+ * failure this file exists to prevent. */
5242
+ stopAfterUnused?: number;
5243
+ programId: PublicKey;
5244
+ mint: PublicKey;
5245
+ }
5246
+ /**
5247
+ * A terminal state needs TWO conjuncts, never one.
5248
+ *
5249
+ * After the rent cleanup runs, "the receiver's token balance is zero" is indistinguishable from
5250
+ * "nothing ever happened at this address". Deriving completion from balance alone overwrites a
5251
+ * real completion record with an empty one — observed live, and it crashed the view that read it.
5252
+ * Completion therefore requires the shield-pool transaction to be present in the receiver's own
5253
+ * history as well.
5254
+ */
5255
+ declare function listBridgeDeposits(conn: Connection, nk: Uint8Array | Buffer, opts: DiscoverOptions): Promise<BridgeDeposit[]>;
5256
+
5257
+ /**
5258
+ * The client half of a bridge deposit, from "tokens have arrived at R" onwards.
5259
+ *
5260
+ * This module is deliberately RAIL-AGNOSTIC. It is imported unchanged by:
5261
+ * - phase1-deposit.ts, where a seeded account plays the rail with a direct SPL transfer
5262
+ * - phase2-e2e.ts, where the rail stand-in delivers over HTTP
5263
+ * - phase3-e2e.ts, where a real Polygon fork drives the whole thing
5264
+ *
5265
+ * That import graph IS the proof of 11-E2E-PLAN.md Phase 2's pass criterion — "the client code
5266
+ * from Phase 1 runs unchanged against the stand-in" — enforced structurally rather than by
5267
+ * eyeballing two copies.
5268
+ *
5269
+ * It was NOT true when first written: phase1-deposit.ts kept its own inline copy, and so never
5270
+ * received the CLEANUP_FEE_BUDGET fix that landed here and in funder.ts. A reviewer caught both
5271
+ * the stale copy and the false claim. Keep every phase importing this file; do not fork it.
5272
+ */
5273
+
5274
+ interface DepositOutcome {
5275
+ signature: string;
5276
+ noteIndex: number;
5277
+ amount: bigint;
5278
+ txSize: number;
5279
+ rBefore: number;
5280
+ rAfter: number;
5281
+ rentExempt: boolean;
5282
+ /** The proof's two input nullifiers (zero-value padding for a pure deposit, but each one is a
5283
+ * genuine Poseidon hash over a random salt — NOT the literal zero sentinel the program treats
5284
+ * as "no PDA needed". See phase4-adversarial.ts case (c). */
5285
+ inputNullifiers: bigint[];
5286
+ /** The three program accounts the funding seam read off the built tx, and what it sent each —
5287
+ * 0 if the account already held enough. Exposed so a caller can independently verify their
5288
+ * on-chain state afterward, including after a thrown error (see `DepositError` below). */
5289
+ fundedAccounts: {
5290
+ name: string;
5291
+ address: string;
5292
+ lamportsSent: number;
5293
+ }[];
5294
+ }
5295
+ /** Thrown by `depositFromDerivedKey` in place of a bare `Error` whenever the funding seam ran
5296
+ * before the failure, so a caller can see exactly what already landed on chain despite the
5297
+ * overall deposit failing. `fundedAccounts` / `measuredSize` are `undefined` only if the seam
5298
+ * never ran at all (failure before signTransaction was called). */
5299
+ declare class DepositError extends Error {
5300
+ readonly fundedAccounts: DepositOutcome["fundedAccounts"];
5301
+ readonly measuredTxSize: number;
5302
+ constructor(message: string, fundedAccounts: DepositOutcome["fundedAccounts"], measuredTxSize: number);
5303
+ }
5304
+ /**
5305
+ * Fund R minimally, then deposit with R as the only signer.
5306
+ *
5307
+ * The funding seam is the SDK's `signTransaction`: by the time it is called the proof and the
5308
+ * relay's risk quote both exist, so the three program accounts are READ OFF the built
5309
+ * transaction rather than re-derived, and cannot drift from what the program will touch.
5310
+ *
5311
+ * `grantOverride` replaces the computed grant (rent floor + deposit fee + cleanup fee) with an
5312
+ * arbitrary lamport amount. Only phase4-adversarial.ts case (a) passes it, to reproduce the
5313
+ * "funded R with just its fee, not its floor" mistake deliberately; every other caller omits it
5314
+ * and gets the same grant this function has always sent.
5315
+ */
5316
+ declare function depositFromDerivedKey(conn: Connection, R: Keypair, funder: Keypair, amount: bigint, log: ((s: string) => void) | undefined, grantOverride: number | undefined,
5317
+ /**
5318
+ * relayUrl, programId and mint are all REQUIRED — no default, for any of the three, on any
5319
+ * environment. They used to fall back to the module-level RELAY/PROGRAM/MINT constants above,
5320
+ * each evaluated once at import time, and that is exactly how a live mainnet deposit got built
5321
+ * against the local program id while the banner said api.cloak.ag: the default filled the hole
5322
+ * silently instead of the caller having to say so. There is nothing left to fall into.
5323
+ */
5324
+ opts: {
5325
+ relayUrl: string;
5326
+ programId: PublicKey;
5327
+ mint: PublicKey;
5328
+ noteSpendKey: Uint8Array;
5329
+ }): Promise<DepositOutcome>;
5330
+
5331
+ /**
5332
+ * Recover the receiving address's token-account rent, even when someone has dusted it.
5333
+ *
5334
+ * THE PROBLEM (found by testing, docs/11-E2E-PLAN.md Phase 4): the rail creates R's token account
5335
+ * and pays its 2,039,280 rent. After the deposit that account is empty and closing it returns the
5336
+ * rent TO THE USER. But SPL Token refuses to close a non-empty account, and dust below the
5337
+ * program's 1,000,000-base-unit deposit minimum cannot be swept by re-depositing it — the program
5338
+ * correctly rejects it with DepositTooSmall. So one base unit from anyone permanently strands
5339
+ * about forty cents of somebody else's money, for free.
5340
+ *
5341
+ * THE FIX, in two parts. Dust that arrives BEFORE the deposit is not a problem at all: the deposit
5342
+ * shields R's whole balance, so it goes into the pool with everything else. The griefing only bites
5343
+ * when dust lands AFTER the deposit, and then the honest answer is to leave it.
5344
+ *
5345
+ * WHY LEAVE IT — this reverses an earlier decision, deliberately. Sweeping the dust to the user's
5346
+ * own wallet publishes `R -> user` on a public ledger, and R has just deposited into the shielded
5347
+ * pool. Anyone can join those two facts and learn that this user made that deposit. That is the
5348
+ * precise inference the pool exists to prevent, and it is the same leak that funding R from the
5349
+ * user's wallet used to cause at the other end of the flow — moved to the end, not removed. It is
5350
+ * not worth about twenty-two cents of rent. Sweeping to Cloak instead is not an option either:
5351
+ * capturing user funds off-chain is an off-chain fee, which the team rule forbids.
5352
+ *
5353
+ * So the default is privacy-first: close when empty, and when dusted, leave the account open and
5354
+ * say so. `dustDestination` lets a caller sweep anyway, with the linkage stated in its doc comment.
5355
+ * Burning stays out: dust can be up to 999,999 base units, and destroying a dollar to recover
5356
+ * twenty-two cents is a worse outcome than the griefing it answers.
5357
+ */
5358
+
5359
+ interface CleanupResult {
5360
+ closed: boolean;
5361
+ dustSwept: bigint;
5362
+ rentReturned: number;
5363
+ destination: string | null;
5364
+ signature: string | null;
5365
+ note: string;
5366
+ }
5367
+ /**
5368
+ * @param dustDestination where post-deposit dust goes, if the caller wants it swept at all.
5369
+ * LEAVE IT UNDEFINED unless the user has been told the cost: any destination they control
5370
+ * publishes `R -> them` and deanonymises the deposit R just made. It is never Cloak's, because
5371
+ * capturing user funds off-chain would be an off-chain fee, which the team rule forbids.
5372
+ * Undefined means "close if empty, otherwise leave it alone and report it".
5373
+ * @param mint the shielded asset R was funded for. Defaults to mainnet USDC so a caller written
5374
+ * before this parameter existed keeps compiling and keeps recovering the same account it always
5375
+ * has; a bridge for any other asset MUST pass its own mint, since R's token account and the
5376
+ * dust sitting in it both belong to whatever mint the rail actually delivered, not to this
5377
+ * default.
5378
+ */
5379
+ declare function cleanupReceivingAddress(conn: Connection, R: Keypair, dustDestination?: PublicKey, mint?: PublicKey): Promise<CleanupResult>;
5380
+
5381
+ /**
5382
+ * Local deposit funder for the bridge testbed.
5383
+ *
5384
+ * The bridge pays a derived address R that arrives with zero SOL, and the deposit takes its
5385
+ * account rent from R (`transact_spl/mod.rs:92` passes `payer_info` into `create_nullifier_pdas`).
5386
+ * But `create_pda_account_safe` (`utils/pda.rs:30-61`) ADOPTS an already-funded, System-owned,
5387
+ * zero-data account and transfers only the shortfall — so the rent can be paid straight to the
5388
+ * three PDAs in an earlier transaction, and R never touches it.
5389
+ *
5390
+ * That split is the whole point. Lamports parked at the nullifier and risk-nonce PDAs are
5391
+ * unrecoverable by ANYONE (no instruction in the program closes them), so flooding this funder
5392
+ * burns Cloak's SOL and earns an attacker nothing. Sending the same total to R instead would put
5393
+ * all of it in an account the requester can sweep in one instruction.
5394
+ *
5395
+ * WHAT SURVIVED THE PORT: the rent constants, the funding-target derivation, and the live rent
5396
+ * read. Everything that SUBMITTED a transaction stayed behind in the prototype — see the note at
5397
+ * the bottom of this file. The production funder is the paymaster, and it runs on a server.
5398
+ */
5399
+
5400
+ /** Rent for a 0-byte System account. `utils/pda.rs:99` -> `minimum_balance(0)`. */
5401
+ declare const MAINNET_RENT_0 = 890880;
5402
+ /** Rent for a 1-byte account. `state/nullifier.rs:18` says `SIZE = 1`. */
5403
+ declare const MAINNET_RENT_1 = 897840;
5404
+ /**
5405
+ * R must be able to pay its own transaction fee. The SDK hard-codes
5406
+ * setComputeUnitPrice(100_000) over setComputeUnitLimit(1_200_000) with no caller lever
5407
+ * (`sdk/src/flows/transact.ts:2598-2599`), so a deposit that fits costs 5,000 + 120,000.
5408
+ * Funding R with the 20,000 an earlier draft assumed leaves it 105,000 short and every
5409
+ * deposit fails under load.
5410
+ */
5411
+ declare const FEE_BUDGET = 130000;
5412
+ /**
5413
+ * A SECOND fee, for the cleanup transaction in which R closes its own token account and the
5414
+ * rail-paid 2,039,280 goes back to the user.
5415
+ *
5416
+ * MEASURED 2026-09-05: without it the close is impossible. Solana checks the fee payer's
5417
+ * rent-exemption after deducting the fee and BEFORE executing, so an R funded to exactly the
5418
+ * floor fails with "insufficient funds for rent" — even though the very transaction being
5419
+ * rejected would have credited it 2,039,280. Budgeting only the deposit's fee strands the
5420
+ * largest recoverable line in the whole flow.
5421
+ */
5422
+ declare const CLEANUP_FEE_BUDGET = 5000;
5423
+ interface DepositRef {
5424
+ programId: PublicKey;
5425
+ mint: PublicKey;
5426
+ /** The two input nullifiers from the proof's public inputs. */
5427
+ nullifiers: [Uint8Array, Uint8Array];
5428
+ /** bind0 from the relay's signed risk quote; the risk-nonce PDA is derived from it. */
5429
+ bind0: Uint8Array;
5430
+ /** R — the derived receiving address that will sign the deposit. */
5431
+ depositor: PublicKey;
5432
+ }
5433
+ /** Mirrors relay `derive_addresses` (supplemental_alt.rs) and the program's own derivations. */
5434
+ declare function deriveFundingTargets(d: DepositRef): {
5435
+ pool: PublicKey;
5436
+ nullifier0: PublicKey;
5437
+ nullifier1: PublicKey;
5438
+ riskNonce: PublicKey;
5439
+ depositorAta: PublicKey;
5440
+ };
5441
+ interface RentRates {
5442
+ zero: number;
5443
+ one: number;
5444
+ }
5445
+ /** Read rent live. Never hard-code it: a local validator's rent sysvar is not authoritative. */
5446
+ declare function readRent(conn: Connection): Promise<RentRates>;
5447
+ interface FundingPlan {
5448
+ transfers: {
5449
+ to: PublicKey;
5450
+ lamports: number;
5451
+ why: string;
5452
+ }[];
5453
+ total: number;
5454
+ alreadyFunded: string[];
5455
+ rent: RentRates;
5456
+ warnings: string[];
5457
+ }
5458
+
5459
+ declare const ONECLICK_PUBKEY_B58 = "reYaWhvwu8Jzo3WUM3zhn6VrhuMEF4eADL17qtRVifc";
5460
+ /**
5461
+ * The exact string this rail signs over, exported so a second implementation can be checked against
5462
+ * it rather than reasoned about.
5463
+ *
5464
+ * A Rust twin of `stable()` has to reproduce JavaScript's key ordering and number formatting byte
5465
+ * for byte, and the two languages do not agree by default: `Object.keys().sort()` orders by UTF-16
5466
+ * code unit while Rust's `sort()` orders by UTF-8 byte. They coincide for ASCII and diverge above
5467
+ * it. This is the same class of bug as `canonicalJson` versus Rust's `keys.sort_unstable()` in the
5468
+ * relay-auth payload, and it fails in the worst direction: a verifier that rejects HONEST quotes
5469
+ * looks like a rail outage, and teaches whoever is on call to ignore it.
5470
+ *
5471
+ * So the cross-language test compares THIS string, not just the boolean verdict. Two verifiers can
5472
+ * agree on a signature by both being wrong in the same place; they cannot agree on the bytes by
5473
+ * accident.
5474
+ */
5475
+ declare function canonicalPayloadString(resp: any): string;
5476
+ declare function verifyQuoteSignature(resp: any): {
5477
+ valid: boolean;
5478
+ reason?: string;
5479
+ };
5480
+
5481
+ /**
5482
+ * Deciding whether a bridge deposit can be honoured — BEFORE the user sends anything.
5483
+ *
5484
+ * WHY THIS EXISTS. Shielding on arrival is mandatory, not a choice: a user who bridges and then
5485
+ * sweeps unshielded has manufactured exactly the `receiver -> them` link the whole design removes.
5486
+ * But "mandatory" moves every failure from the user's account to ours. If someone sends 3 USDC and
5487
+ * the paymaster fee leaves less than the program's 1,000,000-base-unit floor, the shield CANNOT
5488
+ * happen — and under a mandatory model that is the product breaking its own promise, with the money
5489
+ * stranded unshielded at an address the user has never heard of.
5490
+ *
5491
+ * So the check belongs at quote time. The rule is the one the rail-signature check already
5492
+ * established: NEVER SHOW AN ADDRESS YOU CANNOT HONOUR. An unviable deposit is refused before the
5493
+ * deposit address is displayed, not diagnosed after the money has landed.
5494
+ *
5495
+ * All figures are worst-case, taken from the rail's guaranteed floor (`minAmountOut`), never its
5496
+ * target. A quote that only works at the target is a quote that fails on a bad day.
5497
+ */
5498
+ /** programs/shield-pool/src/constants.rs:126 — enforced at transact_spl/deposit.rs:69. */
5499
+ declare const MIN_DEPOSIT_SPL_BASE_UNITS = 1000000n;
5500
+ /**
5501
+ * Live mainnet USDC PoolConfig at deploy time: a flat fee plus a proportional part, charged on
5502
+ * WITHDRAWAL. These are the program's DEFAULTS, not a live read — quote.ts has no RPC client, so it
5503
+ * cannot see a PoolConfig an admin has since changed. Treat this pair as a fallback only: it is
5504
+ * shown to a user as what it will cost to get their money out, and if the live config has moved,
5505
+ * this UNDERSTATES that cost. A caller that has already fetched PoolConfig should pass the real
5506
+ * figures through `BridgeQuoteInput.liveWithdrawFixedFee` / `liveWithdrawFeeBps` instead.
5507
+ */
5508
+ declare const WITHDRAW_FIXED_FEE = 450000n;
5509
+ declare const WITHDRAW_FEE_BPS = 30n;
5510
+ /**
5511
+ * Below this, the fixed costs dominate: a deposit works, but most of it goes to fees.
5512
+ *
5513
+ * It is ADVISORY. `viable` is the hard gate; this only warns and asks for a second confirmation,
5514
+ * because it is the user's money and their call.
5515
+ *
5516
+ * WAS 20 USDC, derived from a 1.21 round trip when the paymaster charged $0.458 for something that
5517
+ * cost it $0.104. Fixed pricing took that to $0.150 on 2026-09-07 (docs/17-PAYMASTER-ECONOMICS.md),
5518
+ * the round trip fell to ~0.90, and leaving the constant alone would have warned people off
5519
+ * deposits that had become perfectly reasonable. If the paymaster fee moves again, recompute
5520
+ * FIXED_ROUND_TRIP rather than editing this number.
5521
+ *
5522
+ * 0.903 / a + 0.003 = 0.063 -> a = 15.05
5523
+ */
5524
+ declare const ECONOMIC_MINIMUM: bigint;
5525
+ interface BridgeQuoteInput {
5526
+ /** What the user sends on the origin chain, in the destination asset's base units. */
5527
+ sent: bigint;
5528
+ /** The rail's GUARANTEED floor. Assess against this, never `amountOut`. */
5529
+ arrivesMin: bigint;
5530
+ /** The rail's target, for display only. */
5531
+ arrivesTarget: bigint;
5532
+ /** What the paymaster will charge to fund the receiver, buffered, in the same units. */
5533
+ paymasterFee: bigint;
5534
+ /**
5535
+ * Live PoolConfig withdraw-fee figures, when the caller has already fetched them on-chain.
5536
+ * Optional: omitting either falls back to the compile-time WITHDRAW_FIXED_FEE / WITHDRAW_FEE_BPS
5537
+ * constants above, which can be stale. quote.ts never fetches these itself — no RPC call belongs
5538
+ * in a pure quote assessment — so the live figures can only arrive this way.
5539
+ */
5540
+ liveWithdrawFixedFee?: bigint;
5541
+ liveWithdrawFeeBps?: bigint;
5542
+ }
5543
+ interface BridgeQuoteAssessment extends BridgeQuoteInput {
5544
+ /** What actually lands shielded, worst case. This is the number to show the user. */
5545
+ shieldedMin: bigint;
5546
+ shieldedTarget: bigint;
5547
+ /** False means REFUSE: the shield is impossible, so the deposit must not be offered. */
5548
+ viable: boolean;
5549
+ /** False means warn: it will work, but the fixed costs dominate. */
5550
+ economic: boolean;
5551
+ /** What it would cost to take it out again, so "shielded" is not mistaken for "free to exit". */
5552
+ withdrawFee: bigint;
5553
+ /** Origin-to-recipient loss if they shielded and immediately withdrew, worst case. */
5554
+ roundTripCost: bigint;
5555
+ roundTripFraction: number;
5556
+ /** Human-readable, ordered most important first. Empty when everything is fine. */
5557
+ reasons: string[];
5558
+ }
5559
+ declare function withdrawFeeFor(amount: bigint): bigint;
5560
+ declare function assessBridgeQuote(q: BridgeQuoteInput): BridgeQuoteAssessment;
5561
+ /** The quote screen, as the user must see it BEFORE any address is shown. */
5562
+ declare function renderAssessment(a: BridgeQuoteAssessment): string;
5563
+
5564
+ /**
5565
+ * Deciding whether a failed shield attempt may be retried.
5566
+ *
5567
+ * This is the most dangerous decision in the flow and the least observable, because it only runs
5568
+ * when something has already gone wrong. Getting it wrong in either direction costs money:
5569
+ *
5570
+ * retry when the deposit actually landed -> a second proof and a second deposit fee, for a
5571
+ * deposit that already happened
5572
+ * refuse when it did not land -> the user is told their funds are unshielded and has
5573
+ * to re-run, which costs a re-run and nothing else
5574
+ *
5575
+ * The asymmetry is the whole design: an unnecessary stop is cheap, a wrongful retry is not. So
5576
+ * anything the evidence cannot explain resolves to "do not retry".
5577
+ *
5578
+ * It lives in the SDK rather than in a CLI script because the shield service will need exactly this
5579
+ * decision, and a second implementation of it would be a second way to lose track of the same money.
5580
+ * It is pure — no chain, no clock — so it can be tested exhaustively, which the CLI version never was.
5581
+ */
5582
+ /**
5583
+ * Failures where retrying only burns another proof: the deposit cannot succeed as constructed, so
5584
+ * a second attempt fails identically and costs another fee to find out.
5585
+ */
5586
+ declare const TERMINAL_FAILURE: RegExp;
5587
+ declare function isTerminalFailure(message: string): boolean;
5588
+ type PostFailureVerdict =
5589
+ /** The receiver's tokens are gone: the deposit transaction landed despite the error. */
5590
+ "landed"
5591
+ /** The balance is untouched: the deposit demonstrably did not consume it. Safe to retry. */
5592
+ | "did-not-land"
5593
+ /** The balance moved by an amount nothing here explains, or could not be read at all. */
5594
+ | "unknown";
5595
+ /**
5596
+ * What the receiver's token balance says about a deposit attempt that threw.
5597
+ *
5598
+ * The deposit transaction moves the receiver's ENTIRE `attempted` balance atomically with the rest
5599
+ * of its instructions, so the balance is a reliable witness: drained to zero means it landed,
5600
+ * unchanged means it did not. Anything else — a partial move, a fresh delivery mid-flight, an
5601
+ * unreadable account — is not something this can interpret, and guessing is exactly the mistake
5602
+ * this function exists to prevent.
5603
+ *
5604
+ * @param attempted what the receiver held when the attempt started
5605
+ * @param after what it holds now, or null if the balance could not be read
5606
+ */
5607
+ declare function classifyPostFailure(attempted: bigint, after: bigint | null): PostFailureVerdict;
5608
+ /** Whether the loop may go again, given what the evidence says. */
5609
+ declare function mayRetry(verdict: PostFailureVerdict, message: string): boolean;
5610
+ /** Linear backoff. Deliberately not exponential: the failures seen here are relay and blockhash
5611
+ * timing, which clear in seconds, and a long tail just strands the user watching a terminal. */
5612
+ declare function retryDelayMs(attempt: number): number;
5613
+ /** Raised in place of the underlying error when the evidence says the deposit landed, or says
5614
+ * nothing this can interpret. The loop stops on it without claiming the funds are unshielded. */
5615
+ declare class DoNotRetry extends Error {
5616
+ constructor(message: string);
5617
+ }
5618
+ interface RetryHooks {
5619
+ /** Called before each wait, so a CLI can say what it is doing and a service can log it. */
5620
+ onRetry?: (attempt: number, delayMs: number, lastError: string) => void;
5621
+ /** Injectable so tests do not actually wait, and a service can use its own scheduler. */
5622
+ sleep?: (ms: number) => Promise<void>;
5623
+ attempts?: number;
5624
+ }
5625
+ /**
5626
+ * Run `attempt` until it succeeds or the evidence says stop.
5627
+ *
5628
+ * The loop is separated from what it runs so it can be tested against injected failures. Its
5629
+ * previous form lived inside a CLI script wired to mainnet constants, which meant the only way to
5630
+ * exercise it was to cause a real failure during a real deposit — so it never was exercised, across
5631
+ * two rounds of changes to it.
5632
+ *
5633
+ * `attempt` is expected to throw `DoNotRetry` when it has already checked chain state and found the
5634
+ * deposit landed, or found something it cannot explain. Everything else is judged by
5635
+ * `isTerminalFailure`.
5636
+ */
5637
+ declare function withShieldRetries<T>(attempt: (attemptNo: number) => Promise<T>, hooks?: RetryHooks): Promise<T>;
5638
+
4671
5639
  /**
4672
5640
  * Cloak SDK - TypeScript SDK for Private Transactions on Solana
4673
5641
  *
4674
5642
  * @packageDocumentation
4675
5643
  */
4676
5644
 
4677
- declare const VERSION = "0.2.1";
5645
+ declare const VERSION = "0.2.3";
4678
5646
  /** True when scanner supports TransactSwap (tag 1). Check this to verify the correct SDK bundle is loaded. */
4679
5647
  declare const SCANNER_SUPPORTS_TRANSACT_SWAP = true;
4680
5648
 
4681
- export { BUILD_ALLOWS_LOCAL_ENDPOINTS, type BuildRecipientDeliveryNotesParams, CHAIN_NOTE_SALT_BITS, CLOAK_PRODUCTION_RELAY_URL, CLOAK_PROGRAM_ID, type ChainNoteTxType, type CircuitVerificationResult, type CloakConfig, CloakError, type CloakKeyPair, type CloakNote, type CommitmentEntry, type CommitmentsResponse, type CompactChainNote, type ComplianceReport, type ComplianceTxType, type ConfirmSettlementParams, DEFAULT_CIRCUITS_URL, DEFAULT_TRANSACTION_CIRCUITS_URL, DELIVERY_MEMO_TAG, DELIVERY_REGISTRY_SEED, type DeliveredNote, type DepositInstructionParams, type DepositNoteSecrets, type DepositOptions, type DepositResult, type DepositStatus, type DiscoverSwapRefundsOptions, type DiscoveredSwapRefund, EXPECTED_CIRCUIT_HASHES, type EncryptedMetadataBundle, type EncryptedNote$1 as EncryptedNote, type ErrorCategory, type ExpandedSpendKey, type ExternalFeePayerAdapter, FIXED_FEE_LAMPORTS, type Groth16Proof, InsecureRandomnessError, LAMPORTS_PER_SOL, LocalStorageAdapter, type LogLevel, type Logger, MERKLE_TREE_HEIGHT, MIN_DEPOSIT_LAMPORTS, type MasterKey, type MatchChangeNoteParams, type MatchDepositNoteParams, type MatchSwapRefundLeafParams, type MaxLengthArray, MemoryStorageAdapter, type MerkleProof, type MerkleRootResponse, MerkleTree, NATIVE_SOL_MINT, type Network, type NoteData, type OnchainMerkleProof, type ParsedDeliveryCarrier, type PendingDeposit, type PendingWithdrawal, RECIPIENT_DELIVERY_CIPHERTEXT_LEN, RECIPIENT_DELIVERY_EPHEMERAL_PK_LEN, RECIPIENT_DELIVERY_NONCE_LEN, RECIPIENT_DELIVERY_NOTE_BYTES, RECIPIENT_DELIVERY_PLAINTEXT_LEN, RECIPIENT_DELIVERY_TAG_LEN, RELAY_ORIGIN_ALLOWLIST, REQUEST_AUTH_MAX_AGE_SECONDS, REQUEST_AUTH_MAX_FUTURE_SKEW_SECONDS, type RecipientDeliveryNote, type RecoveredChangeNote, type RecoveredChangeNoteRecord, type RecoveredDepositNote, type RecoveredDepositNoteRecord, type RecoveredSwapRefund, type RelayAuthPreimage, type RelayAuthSigner, RelayInternalError, RelayService, type RelaySubmissionResult, type RiskQuoteInstructionResponse, RootNotFoundError, SCANNER_SUPPORTS_TRANSACT_SWAP, SIGN_IN_MESSAGE, SanctionsQuoteError, type ScanOptions, type ScanRecipientDeliveryOptions, type ScanResult, type ScanSummary, type ScannedTransaction, type SettlementConnection, type SettlementContext, type SettlementStatus, type SettlementVerdict, SettlementVerificationError, ShieldPoolErrors, type ShieldPoolPDAs, SimpleWallet, type SpendKey, type StorageAdapter, type SubmitTransactToRelayArgs, type SwapOptions, type SwapParams, type SwapRefundAuthorization, type SwapResult, TRANSACTION_CIRCUITS_VERSION, TRANSACT_AUTH_FIELDS, TRANSACT_SWAP_AUTH_FIELDS, type TransactOptions, type TransactParams, type TransactRequestBodyParams, type TransactResult, type TransactionMetadata, type Transfer, type TransferOptions, type TransferResult, type TxStatus, type UserFriendlyError, type Utxo, UtxoAlreadySpentError, type EncryptedNote as UtxoEncryptedNote, type UtxoKeypair, type UtxoSwapParams, type UtxoSwapResult, UtxoWallet, VARIABLE_FEE_DENOMINATOR, VARIABLE_FEE_NUMERATOR, VARIABLE_FEE_RATE, VERSION, type VerifyUtxosResult, type ViewKey, type ViewingKeyPair, type WalletAdapter, type WalletUtxo, type WithdrawOptions, type WithdrawSubmissionResult, assertDirectSubmissionLanded, assertTransactionCircuitIntegrity, bigintToBytes32$1 as bigintToBytes32, bigintToHex, buildMerkleTree, buildMerkleTreeFromChain, buildMerkleTreeFromRelay, buildRecipientDeliveryNotes, buildRelayAuthPreimage, buildTransactRequestBody, bytesToHex, calculateFee, calculateFeeBigint, calculateRelayFee, canRebuildMerkleTreeFromChain, canonicalJson, chainNoteFromBase64, chainNoteToBase64, classifyRelayError, cleanupStalePendingOperations, clearPendingDeposits, clearPendingWithdrawals, computeChainNoteHash, computeExtDataHash, computeMerkleRoot, computeProofForLatestDeposit, computeProofFromChain, computeSignature, computeSwapRefundCommitment, computeCommitment as computeUtxoCommitment, computeNullifier as computeUtxoNullifier, confirmTransactSettlement, copyNoteToClipboard, createCloakError, createDepositInstruction, createLogger, createRecoverableChangeUtxo, createRecoverableDepositUtxo, createUtxo, createZeroUtxo, decryptCompactChainNote, decryptComplianceMetadataWithMasterKey, decryptTransactionMetadata, deriveChangeNoteBlinding, deriveDepositNoteSecrets, deriveDiversifiedViewingKey, deriveDiversifier, deriveInputNullifierPdas, derivePublicKey, deriveSpendKey, deriveSwapRefundAuthorization, deriveUserCompliancePublicKey, deriveUserComplianceScalar, deriveUtxoKeypairFromSpendKey, deriveViewKey, deriveViewingKeyFromNk, deriveViewingKeyFromSpendKey, deriveViewingKeyFromUtxoPrivateKey, deserializeUtxo, detectNetworkFromRpcUrl, discoverSwapRefunds, downloadNote, encodeDeliveryCarrierMemo, encodeNoteSimple, encodeRecipientDeliveryNote, encryptCompactChainNote, encryptNoteForRecipient, encryptTransactionMetadata, encryptTransactionMetadataBundle, expandSpendKey, explainRelayAuthRejection, exportKeys, exportNote, exportWalletKeys, fetchCommitments, fetchRiskQuoteInstruction, fetchRiskQuoteIx, filterNotesByNetwork, filterWithdrawableNotes, findNoteByCommitment, formatAmount, formatComplianceCsv, formatErrorForLogging, formatSol, fullWithdraw, generateCloakKeys, generateCommitmentAsync, generateMasterSeed, generateNoteFromWallet, generateUtxoKeypair, generateViewingKeyPair, getAddressExplorerUrl, getChainNoteRegistryPDA, getCircuitsPath, getDeliveryRegistryPDA, getDistributableAmount, getExplorerUrl, getNkFromUtxoPrivateKey, getNullifierPDA, getPendingOperationsSummary, getPoolAuthorityConfigPDA, getPublicKey, getPublicViewKey, getRecipientAmount, getRpcUrlForNetwork, getShieldPoolPDAs, getSwapStatePDA, getViewKey, hasPendingOperations, hexToBigint$1 as hexToBigint, hexToBytes, importKeys, importWalletKeys, isBrowser, isBrowserLike, isDebugEnabled, isPlausibleSignature, isReactNative, isRootNotFoundError, isSubmissionOutcomeUnknownResponse, isValidHex, isValidRpcUrl, isValidSolanaAddress, isWithdrawAmountSufficient, isWithdrawable, keypairToAdapter, loadPendingDeposits, loadPendingWithdrawals, loadVerifiedCircuitArtifacts, matchChangeNote, matchDepositNote, matchSwapRefundLeaf, openRecipientDeliveryNote, parseAmount, parseDeliveryCarrierMemo, parseError, parseNote, parseRelayErrorResponse, parseRelayErrorSignature, parseTransactionError, partialWithdraw, poseidonHash, preflightCheck, preflightNullifiers, prepareEncryptedOutput, prepareEncryptedOutputForRecipient, proofToBytes, pubkeyToFieldElement, pubkeyToLimbs, randomBytes, randomChangeNoteSalt, randomDepositNoteSalt, randomFieldElement, readMerkleTreeState, recipientDeliveryNoteToBase64, registerViewingKey, removePendingDeposit, removePendingWithdrawal, resolveCircuitsBase, savePendingDeposit, savePendingWithdrawal, scanNotesForWallet, scanRecipientDeliveryNotes, scanTransactions, sdkLogger, selectUtxos, sendTransaction, serializeNote, serializeUtxo, setCircuitsPath, setDebugMode, signTransaction, splitTo2Limbs, submitTransactToRelay, sumUtxoAmounts, swapUtxo, swapWithChange, toComplianceReport, transact, transfer, truncate, tryDecryptNote, updateNoteWithDeposit, updatePendingDeposit, updatePendingWithdrawal, bigintToBytes32 as utxoBigintToBytes32, utxoEquals, hexToBigint as utxoHexToBigint, validateDepositParams, validateNote, validateOutputsSum, validateRoot, validateTransfers, validateWalletConnected, validateWithdrawableNote, verifyAllCircuits, verifyCircuitIntegrity, verifyUtxos, waitForRoot, withTiming };
5649
+ export { BRIDGE_ESCROW_LABEL, BUILD_ALLOWS_LOCAL_ENDPOINTS, type BridgeDeliveryState, type BridgeDeposit, type BridgeDepositState, type BridgeQuoteAssessment, type BridgeQuoteInput, type BridgeRail, type BridgeRailAttestation, type BridgeRailProblem, type BridgeRailQuoteOption, type BridgeRailQuoteRequest, type BridgeRailQuoteResponse, type BridgeRailStatusResult, type BuildRecipientDeliveryNotesParams, CHAIN_NOTE_SALT_BITS, CLEANUP_FEE_BUDGET, CLOAK_PRODUCTION_RELAY_URL, CLOAK_PROGRAM_ID, type ChainNoteTxType, type CircuitVerificationResult, type CleanupResult, type CloakConfig, CloakError, type CloakKeyPair, type CloakNote, type CommitmentEntry, type CommitmentsResponse, type CompactChainNote, type ComplianceReport, type ComplianceTxType, type ConfirmSettlementParams, DEFAULT_CIRCUITS_URL, DEFAULT_TRANSACTION_CIRCUITS_URL, DELIVERY_MEMO_TAG, DELIVERY_REGISTRY_SEED, type DeliveredNote, DepositError, type DepositInstructionParams, type DepositNoteSecrets, type DepositOptions, type DepositOutcome, type DepositRef, type DepositResult, type DepositStatus, type DiscoverOptions, type DiscoverSwapRefundsOptions, type DiscoveredSwapRefund, DoNotRetry, ECONOMIC_MINIMUM, EXPECTED_CIRCUIT_HASHES, type EncryptedMetadataBundle, type EncryptedNote$1 as EncryptedNote, type ErrorCategory, type ExpandedSpendKey, type ExternalFeePayerAdapter, FEE_BUDGET, FIXED_FEE_LAMPORTS, type FundingPlan, type Groth16Proof, InsecureRandomnessError, LAMPORTS_PER_SOL, LocalStorageAdapter, type LogLevel, type Logger, MAINNET_RENT_0, MAINNET_RENT_1, MAX_RECEIVER_INDEX, MERKLE_TREE_HEIGHT, MIN_DEPOSIT_LAMPORTS, MIN_DEPOSIT_SPL_BASE_UNITS, type MasterKey, type MatchChangeNoteParams, type MatchDepositNoteParams, type MatchSwapRefundLeafParams, type MaxLengthArray, MemoryStorageAdapter, type MerkleProof, type MerkleRootResponse, MerkleTree, NATIVE_SOL_MINT, type Network, type NoteData, ONECLICK_PUBKEY_B58, type OnchainMerkleProof, type ParsedDeliveryCarrier, type PaymasterTopUpExpectation, type PaymasterTopUpResult, type PendingDeposit, type PendingWithdrawal, type PostFailureVerdict, RECIPIENT_DELIVERY_CIPHERTEXT_LEN, RECIPIENT_DELIVERY_EPHEMERAL_PK_LEN, RECIPIENT_DELIVERY_NONCE_LEN, RECIPIENT_DELIVERY_NOTE_BYTES, RECIPIENT_DELIVERY_PLAINTEXT_LEN, RECIPIENT_DELIVERY_TAG_LEN, RELAY_BATCH_AUTH_MAX_ITEMS, RELAY_ORIGIN_ALLOWLIST, REQUEST_AUTH_BATCH_DOMAIN, REQUEST_AUTH_BATCH_MAX_AGE_SECONDS, REQUEST_AUTH_MAX_AGE_SECONDS, REQUEST_AUTH_MAX_FUTURE_SKEW_SECONDS, type RecipientDeliveryNote, type RecoveredChangeNote, type RecoveredChangeNoteRecord, type RecoveredDepositNote, type RecoveredDepositNoteRecord, type RecoveredSwapRefund, type RelayAuthMode, type RelayAuthPreimage, type RelayAuthSigner, RelayBatchAuthCoordinator, type RelayBatchAuthCoordinatorOptions, type RelayBatchAuthEnvelope, type RelayBatchAuthFields, type RelayBatchAuthItemHandle, RelayInternalError, type RelayMessageSigner, RelayService, type RelaySubmissionResult, type RelayTransactionSigner, type RentRates, type RetryHooks, type RiskQuoteInstructionResponse, RootNotFoundError, SCANNER_SUPPORTS_TRANSACT_SWAP, SIGN_IN_MESSAGE, SanctionsQuoteError, type ScanOptions, type ScanRecipientDeliveryOptions, type ScanResult, type ScanSummary, type ScannedTransaction, type SettlementConnection, type SettlementContext, type SettlementStatus, type SettlementVerdict, SettlementVerificationError, ShieldPoolErrors, type ShieldPoolPDAs, SimpleWallet, type SpendKey, type StorageAdapter, type SubmitTransactToRelayArgs, type SwapOptions, type SwapParams, type SwapRefundAuthorization, type SwapResult, TERMINAL_FAILURE, TRANSACTION_CIRCUITS_VERSION, TRANSACT_AUTH_FIELDS, TRANSACT_SWAP_AUTH_FIELDS, type TransactBatchItem, type TransactBatchItemOutcome, type TransactBatchOptions, type TransactBatchResult, type TransactOptions, type TransactParams, type TransactRequestBodyParams, type TransactResult, type TransactionMetadata, type Transfer, type TransferOptions, type TransferResult, type TxStatus, type UserFriendlyError, type Utxo, UtxoAlreadySpentError, type EncryptedNote as UtxoEncryptedNote, type UtxoKeypair, type UtxoSwapParams, type UtxoSwapResult, UtxoWallet, VARIABLE_FEE_DENOMINATOR, VARIABLE_FEE_NUMERATOR, VARIABLE_FEE_RATE, VERSION, type VerifyUtxosResult, type ViewKey, type ViewingKeyPair, WITHDRAW_FEE_BPS, WITHDRAW_FIXED_FEE, type WalletAdapter, type WalletUtxo, type WithdrawOptions, type WithdrawSubmissionResult, assertDirectSubmissionLanded, assertInputMints, assertTransactionCircuitIntegrity, assessBridgeQuote, bigintToBytes32$1 as bigintToBytes32, bigintToHex, buildAuthTransactionMessage, buildMerkleTree, buildMerkleTreeFromChain, buildMerkleTreeFromRelay, buildRecipientDeliveryNotes, buildRelayAuthPreimage, buildRelayBatchAuthMessage, buildTransactRequestBody, bytesToHex, calculateFee, calculateFeeBigint, calculateRelayFee, canRebuildMerkleTreeFromChain, canonicalJson, canonicalPayloadString, chainNoteFromBase64, chainNoteToBase64, classifyPostFailure, classifyRelayError, cleanupReceivingAddress, cleanupStalePendingOperations, clearPendingDeposits, clearPendingWithdrawals, cloakBridgeRail, computeChainNoteHash, computeExtDataHash, computeMerkleRoot, computeProofForLatestDeposit, computeProofFromChain, computeSignature, computeSwapRefundCommitment, computeCommitment as computeUtxoCommitment, computeNullifier as computeUtxoNullifier, confirmTransactSettlement, copyNoteToClipboard, createCloakError, createDepositInstruction, createLogger, createRecoverableChangeUtxo, createRecoverableDepositUtxo, createRelayBatchAuthCoordinator, createUtxo, createZeroUtxo, decryptCompactChainNote, decryptComplianceMetadataWithMasterKey, decryptTransactionMetadata, depositFromDerivedKey, deriveBridgeReceiver, deriveChangeNoteBlinding, deriveDepositNoteSecrets, deriveDiversifiedViewingKey, deriveDiversifier, deriveFundingTargets, deriveInputNullifierPdas, derivePublicKey, deriveSpendKey, deriveSwapRefundAuthorization, deriveUserCompliancePublicKey, deriveUserComplianceScalar, deriveUtxoKeypairFromSpendKey, deriveViewKey, deriveViewingKeyFromNk, deriveViewingKeyFromSpendKey, deriveViewingKeyFromUtxoPrivateKey, deserializeUtxo, detectNetworkFromRpcUrl, discoverSwapRefunds, downloadNote, encodeDeliveryCarrierMemo, encodeNoteSimple, encodeRecipientDeliveryNote, encryptCompactChainNote, encryptNoteForRecipient, encryptTransactionMetadata, encryptTransactionMetadataBundle, expandSpendKey, explainRelayAuthRejection, exportKeys, exportNote, exportWalletKeys, fetchCommitments, fetchRiskQuoteInstruction, fetchRiskQuoteIx, filterNotesByNetwork, filterWithdrawableNotes, findNoteByCommitment, formatAmount, formatComplianceCsv, formatErrorForLogging, formatSol, fullWithdraw, fundReceiverViaPaymaster, generateCloakKeys, generateCommitmentAsync, generateMasterSeed, generateNoteFromWallet, generateUtxoKeypair, generateViewingKeyPair, getAddressExplorerUrl, getChainNoteRegistryPDA, getCircuitsPath, getDeliveryRegistryPDA, getDistributableAmount, getExplorerUrl, getNkFromUtxoPrivateKey, getNullifierPDA, getPendingOperationsSummary, getPoolAuthorityConfigPDA, getPublicKey, getPublicViewKey, getRecipientAmount, getRpcUrlForNetwork, getShieldPoolPDAs, getSwapStatePDA, getViewKey, hasPendingOperations, hexToBigint$1 as hexToBigint, hexToBytes, importKeys, importWalletKeys, isBrowser, isBrowserLike, isDebugEnabled, isPlausibleSignature, isReactNative, isRootNotFoundError, isSubmissionOutcomeUnknownResponse, isTerminalFailure, isValidHex, isValidRpcUrl, isValidSolanaAddress, isWithdrawAmountSufficient, isWithdrawable, keypairToAdapter, listBridgeDeposits, loadPendingDeposits, loadPendingWithdrawals, loadVerifiedCircuitArtifacts, matchChangeNote, matchDepositNote, matchSwapRefundLeaf, mayRetry, openRecipientDeliveryNote, parseAmount, parseDeliveryCarrierMemo, parseError, parseNote, parseRelayErrorResponse, parseRelayErrorSignature, parseTransactionError, partialWithdraw, poseidonHash, preflightCheck, preflightNullifiers, prepareEncryptedOutput, prepareEncryptedOutputForRecipient, proofToBytes, pubkeyToFieldElement, pubkeyToLimbs, randomBytes, randomChangeNoteSalt, randomDepositNoteSalt, randomFieldElement, readMerkleTreeState, readRent, recipientDeliveryNoteToBase64, registerViewingKey, relayRequestDigestHex, removePendingDeposit, removePendingWithdrawal, renderAssessment, resolveCircuitsBase, retryDelayMs, savePendingDeposit, savePendingWithdrawal, scanNotesForWallet, scanRecipientDeliveryNotes, scanTransactions, sdkLogger, selectUtxos, sendTransaction, serializeAuthTransactionMessage, serializeNote, serializeUtxo, setCircuitsPath, setDebugMode, signRelayAuthPayload, signTransaction, splitTo2Limbs, submitTransactToRelay, sumUtxoAmounts, swapUtxo, swapWithChange, toComplianceReport, transact, transactBatch, transfer, truncate, tryDecryptNote, updateNoteWithDeposit, updatePendingDeposit, updatePendingWithdrawal, bigintToBytes32 as utxoBigintToBytes32, utxoEquals, hexToBigint as utxoHexToBigint, validateDepositParams, validateNote, validateOutputsSum, validatePaymasterTopUpTransaction, validateRoot, validateTransfers, validateWalletConnected, validateWithdrawableNote, verifyAllCircuits, verifyCircuitIntegrity, verifyQuoteSignature, verifyUtxos, waitForRoot, withShieldRetries, withTiming, withdrawFeeFor };