@chironbuilder/ootle-sdk 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +90 -0
  3. package/dist/accountApi.d.ts +33 -0
  4. package/dist/accountApi.d.ts.map +1 -0
  5. package/dist/accountApi.js +1 -0
  6. package/dist/adapters.d.ts +13 -0
  7. package/dist/adapters.d.ts.map +1 -0
  8. package/dist/adapters.js +68 -0
  9. package/dist/componentAddress.d.ts +27 -0
  10. package/dist/componentAddress.d.ts.map +1 -0
  11. package/dist/componentAddress.js +68 -0
  12. package/dist/confidential.d.ts +50 -0
  13. package/dist/confidential.d.ts.map +1 -0
  14. package/dist/confidential.js +86 -0
  15. package/dist/derivation.d.ts +7 -0
  16. package/dist/derivation.d.ts.map +1 -0
  17. package/dist/derivation.js +47 -0
  18. package/dist/domainHash.d.ts +11 -0
  19. package/dist/domainHash.d.ts.map +1 -0
  20. package/dist/domainHash.js +44 -0
  21. package/dist/htlc.d.ts +26 -0
  22. package/dist/htlc.d.ts.map +1 -0
  23. package/dist/htlc.js +69 -0
  24. package/dist/index.d.ts +21 -0
  25. package/dist/index.d.ts.map +1 -0
  26. package/dist/index.js +14 -0
  27. package/dist/ootleNetwork.d.ts +4 -0
  28. package/dist/ootleNetwork.d.ts.map +1 -0
  29. package/dist/ootleNetwork.js +11 -0
  30. package/dist/ownershipProof.d.ts +4 -0
  31. package/dist/ownershipProof.d.ts.map +1 -0
  32. package/dist/ownershipProof.js +53 -0
  33. package/dist/storage.d.ts +61 -0
  34. package/dist/storage.d.ts.map +1 -0
  35. package/dist/storage.js +110 -0
  36. package/dist/timeout.d.ts +2 -0
  37. package/dist/timeout.d.ts.map +1 -0
  38. package/dist/timeout.js +19 -0
  39. package/dist/vault.d.ts +12 -0
  40. package/dist/vault.d.ts.map +1 -0
  41. package/dist/vault.js +44 -0
  42. package/dist/wallet.d.ts +733 -0
  43. package/dist/wallet.d.ts.map +1 -0
  44. package/dist/wallet.js +1964 -0
  45. package/package.json +46 -0
  46. package/vendor/ootle-patched/README.md +67 -0
  47. package/vendor/ootle-patched/dist/index.d.ts +2129 -0
  48. package/vendor/ootle-patched/dist/index.js +1365 -0
  49. package/vendor/ootle-patched/package.json +23 -0
  50. package/vendor/ootle-wasm-patched/README.md +128 -0
  51. package/vendor/ootle-wasm-patched/ootle_wasm.d.ts +454 -0
  52. package/vendor/ootle-wasm-patched/ootle_wasm.js +9 -0
  53. package/vendor/ootle-wasm-patched/ootle_wasm_bg.js +1485 -0
  54. package/vendor/ootle-wasm-patched/ootle_wasm_bg.wasm +0 -0
  55. package/vendor/ootle-wasm-patched/ootle_wasm_bg.wasm.d.ts +83 -0
  56. package/vendor/ootle-wasm-patched/package.json +27 -0
@@ -0,0 +1,733 @@
1
+ import { Network } from "@tari-project/ootle";
2
+ import type { ExecuteResult, IndexerGetTransactionResultResponse, Instruction, SubstateRequirement, TransactionId } from "@tari-project/ootle-ts-bindings";
3
+ import { IndexerProvider } from "@tari-project/ootle-indexer";
4
+ import { SecretKeyWallet } from "@tari-project/ootle-secret-key-wallet";
5
+ import type { WalletAccountApi } from "./accountApi";
6
+ import type { ScannedStealthOutput } from "./confidential";
7
+ import { type NetworkName } from "./ootleNetwork";
8
+ import type { ShieldedOutputRecord } from "./storage";
9
+ export interface TokenBalance {
10
+ resourceAddress: string;
11
+ kind: string;
12
+ /** The plain, public (revealed) amount — what this vault shows on-chain to anyone. */
13
+ amount: bigint;
14
+ /** Value hidden inside this vault's Pedersen commitments, decrypted with this account's own
15
+ * view key (see confidential.ts) — 0n for every kind except Confidential, and for a
16
+ * Confidential vault with no commitments. Never requires a network call beyond what
17
+ * getBalances() already fetches. */
18
+ confidentialAmount: bigint;
19
+ /** Commitments that failed to decrypt against this account's view key. Expected to be 0 for a
20
+ * vault's own commitments map (every entry there should be ours) — nonzero is worth surfacing,
21
+ * not silently swallowing. */
22
+ confidentialDecryptFailures: number;
23
+ /** The resource's on-chain decimal precision (e.g. 6 for XTR, 8 for a typical DemoToken) —
24
+ * a real `Resource.divisibility` field, not a guessed convention. */
25
+ divisibility: number;
26
+ /** The resource's `metadata.SYMBOL`, if it set one — null for a resource with no such metadata
27
+ * key, in which case a caller should fall back to the address. */
28
+ symbol: string | null;
29
+ /** The resource's `metadata.name` (a longer display name, distinct from the ticker-style
30
+ * `symbol`), if it set one — null otherwise. */
31
+ name: string | null;
32
+ /** Token IDs held in this vault, for a `NonFungible` resource only — null for every other kind.
33
+ * A NonFungible vault's container has no `.amount`/`.revealed_amount` field at all (only
34
+ * `token_ids`/`locked_token_ids`), so `amount` above is synthesized as this array's length. */
35
+ nonFungibleTokenIds: string[] | null;
36
+ }
37
+ /** One resource's total unspent *stealth* holding -- `OotleAccount.getPrivateBalances()`'s element
38
+ * type. Deliberately separate from `TokenBalance`: that describes a vault (public amount plus any
39
+ * decrypted commitments), this describes a set of freestanding stealth UTXOs, and a resource can
40
+ * have either, both, or only the latter. */
41
+ export interface PrivateBalance {
42
+ resourceAddress: string;
43
+ /** Sum of the unspent outputs' amounts, raw resource-native units (same convention as
44
+ * `TokenBalance.amount`). */
45
+ amount: bigint;
46
+ /** How many unspent outputs make up `amount`. Each is spent whole, so this bounds what a single
47
+ * spend can be covered by -- see `resolveUnshieldPlan`'s coin selection. */
48
+ outputCount: number;
49
+ divisibility: number;
50
+ symbol: string | null;
51
+ name: string | null;
52
+ }
53
+ /**
54
+ * A stealth output's **minimum value promise**: a public claim, committed into the output's own
55
+ * range proof, that it is worth at least this much.
56
+ *
57
+ * A confidential output normally carries a range proof for `0 <= v < 2^64`, hiding `v` completely.
58
+ * Set a promise `m` and the proof instead attests `m <= v < 2^64`, with `m` stored in the clear on
59
+ * the output (`UnspentOutput.minimum_value_promise`). Anyone reading the output on-chain learns
60
+ * "worth at least `m`" and nothing more precise -- which is exactly a proof of funds: a permanent,
61
+ * non-interactive, publicly verifiable artifact that needs no cooperation from this wallet to check
62
+ * (fetch the `utxo_{resource}_{commitment}` substate, read the field, confirm it is still unspent).
63
+ *
64
+ * Two things follow that callers must get right, which is why this is validated rather than passed
65
+ * straight through:
66
+ *
67
+ * 1. **A promise above the real value is unprovable.** The bulletproof asserting `m <= v` cannot be
68
+ * generated when `m > v`, so this fails here with a message that says so, rather than deeper in
69
+ * the wasm with something opaque -- or, worse, producing a proof the network then rejects after
70
+ * the fee half of the transaction has already been committed.
71
+ * 2. **It is a permanent, irreversible privacy disclosure.** Shielding is how value stops being
72
+ * publicly visible; a promise puts a floor back on public view, for the life of the output, for
73
+ * everyone -- not just whoever the proof was made for. That is the deliberate trade being made,
74
+ * and it is why every path reaching this asks the user first.
75
+ */
76
+ export declare function assertValidMinimumValuePromise(promise: bigint, amount: bigint): void;
77
+ /**
78
+ * One derived Ootle account: a signer (owner + view keypair) plus the network connection needed
79
+ * to read its state and submit transactions, entirely independent of the wallet daemon.
80
+ */
81
+ export declare class OotleAccount implements WalletAccountApi {
82
+ readonly index: number;
83
+ readonly network: Network;
84
+ readonly signer: SecretKeyWallet;
85
+ private provider;
86
+ private readonly ownerSecret;
87
+ private constructor();
88
+ /** `entropy` is the 16-byte CipherSeed entropy (see cipherSeed.ts), not a raw 32-byte seed. */
89
+ static fromSeed(entropy: Uint8Array, index: number, networkName: NetworkName): OotleAccount;
90
+ getProvider(): Promise<IndexerProvider>;
91
+ /**
92
+ * The bech32m "otl_..." wallet address, for display / receiving funds. Confirmed (empirically,
93
+ * against `generateOotleAddress`) to be what `Signer.getAddress()` actually returns for a
94
+ * `SecretKeyWallet` — despite the base `Signer` interface's JSDoc calling it "the component
95
+ * address", it is NOT the on-chain account component address. See `getComponentAddress()`.
96
+ */
97
+ getWalletAddress(): Promise<string>;
98
+ getPublicKey(): Promise<Uint8Array>;
99
+ /**
100
+ * The account's on-chain component address — what instructions use as the `account` argument
101
+ * (deposit/withdraw/pay_fee/etc.) and what `getVaultIdsForAccount` needs to find balances.
102
+ *
103
+ * Computed client-side via `deriveAccountComponentAddress` (`componentAddress.ts`), which
104
+ * reproduces Ootle's domain-separated Blake2b hash of (ACCOUNT_TEMPLATE_ADDRESS, owner_public_key)
105
+ * byte-for-byte — verified against all three committed golden vectors from
106
+ * `tari-ootle`'s `crates/ootle_sdk_core/fixtures/address_derive/`.
107
+ */
108
+ getComponentAddress(): Promise<string>;
109
+ getBalances(): Promise<TokenBalance[]>;
110
+ /**
111
+ * Builds, signs (with this account's key) and submits/dry-runs a transaction. `opts.inputs` lets
112
+ * a caller pre-pin substates it already knows are needed; beyond that, this indexer requires
113
+ * *every* substate an instruction touches to be listed as an input — even ones referenced only
114
+ * by address in a `CallMethod` on an already-existing component (confirmed empirically, first
115
+ * with the faucet, and again with this account's own existing component when calling into it for
116
+ * a `pay_fee`/`deposit`/`withdraw` on a second use) — and there's no want-derivation pass in this
117
+ * TS SDK to discover that dependency graph up front the way the Rust `ootle_sdk_core` can.
118
+ *
119
+ * Rather than hand-tracing each call's full component/vault graph, this retries on the specific
120
+ * rejection the engine itself gives for a missing input — "At instruction #N: <address> not
121
+ * found" — extracting that address, resolving its current version, adding it to the pinned
122
+ * inputs, and resubmitting. Each retry can surface a *different* missing address (a component's
123
+ * vaults are only discoverable after the component itself is known), so this loops until either
124
+ * success or the same address reappears (nothing new left to resolve). The version-race retry
125
+ * `claimTestnetXtr()` needs for a heavily-contended shared substate is folded in too.
126
+ */
127
+ execute(instructions: Instruction[], opts?: {
128
+ maxFee?: bigint;
129
+ dryRun?: boolean;
130
+ inputs?: SubstateRequirement[];
131
+ maxRetries?: number;
132
+ }): Promise<{
133
+ transaction_id: TransactionId;
134
+ result: ExecuteResult;
135
+ } | IndexerGetTransactionResultResponse>;
136
+ /**
137
+ * A working replacement for the SDK's exported `sendDryRun()`, which is broken against this
138
+ * indexer: it builds the identical signed/sealed envelope but POSTs it to the regular
139
+ * `transactions` endpoint, which rejects dry-run envelopes with "Dry-run transactions must be
140
+ * submitted to the /transactions/dry-run endpoint" (confirmed empirically). This reproduces
141
+ * `sendDryRun`'s own pipeline — set `dry_run`, `resolveTransaction`, `signTransaction`,
142
+ * `sealTransaction` (all exported, same functions it uses internally) — but posts the sealed
143
+ * envelope to the correct path via the indexer client's transport directly. Dry-run responses
144
+ * come back synchronously with the full result already attached (no `transactions/{id}/result`
145
+ * polling needed, unlike a real submission).
146
+ *
147
+ * The response shape (`{transaction_id, result: ExecuteResult}`) is the indexer's own
148
+ * `SubmitTransactionDryRunResponse` (`applications/tari_indexer/src/rest_api/handlers/
149
+ * transactions.rs`'s `submit_transaction_dry_run` handler) — confirmed directly against that
150
+ * Rust source since `@tari-project/ootle-ts-bindings` doesn't export a matching type for it
151
+ * (only the real-submission response, which no longer carries a `result` field at all as of
152
+ * the 0.39.0 protocol upgrade).
153
+ */
154
+ private submitDryRun;
155
+ /**
156
+ * A working replacement for the SDK's exported `sendTransaction()`. That function still submits
157
+ * correctly, but its own polling (`ct()`/`st()` internally) only surfaces a generic
158
+ * `abort_details` summary on rejection — not the full structured `RejectReason`/`SubstateDiff`
159
+ * this class's auto-resolve retry (see `execute()`) needs to find a missing substate's address
160
+ * in. Confirmed empirically: a real submission that hit "SubstateNotFound: vault_... not found"
161
+ * came back from `sendTransaction()` with no address in the thrown message at all, so the retry
162
+ * loop had nothing to extract and gave up on the first attempt — the exact failure this method
163
+ * exists to fix. Submission itself (`provider.submitTransaction`) is unchanged and already
164
+ * proven correct; only the result-polling and error-message construction are reimplemented, in
165
+ * the same detailed shape `submitDryRun()` above already produces.
166
+ */
167
+ private submitReal;
168
+ /**
169
+ * Transfers `amount` (raw, resource-native units) of `resourceAddress` from this account to
170
+ * `recipientAddress` — a plain `withdraw` off this account's own vault, handed straight to
171
+ * `deposit` on the recipient's account component via a workspace bucket, in one transaction.
172
+ * Works for any resource this account holds, including XTR; `execute()`'s auto-resolve retry
173
+ * (see its own doc comment) discovers and pins whichever vaults/components aren't already
174
+ * known, exactly as it does for every other instruction this class builds by hand.
175
+ *
176
+ * `recipientAddress` accepts either form (see `WalletAccountApi.send`'s doc comment): the
177
+ * recipient's bech32m wallet address, or a bare `component_...` address. `CallMethod.call` is a
178
+ * `SubstateId`, so it must end up being the latter either way — handing an "otl_…" straight
179
+ * through is not a friendly error: `SubstateId::from_str` splits on the first underscore, does
180
+ * not recognise the `otl` prefix, and the instruction then fails to deserialize, surfacing from
181
+ * the wasm as "data did not match any variant of untagged enum TransactionInput", naming neither
182
+ * the field nor the reason.
183
+ *
184
+ * `deposit` is a method call on an *existing* component, and `CreateAccount` is create-or-conflict
185
+ * (against an account that already exists it rejects with "Substate <component>:0 is already UP
186
+ * and conflicts with an existing output" -- Tari's own walletd reads that exact string as "this
187
+ * already exists" in its faucet handler), so a brand-new recipient's account is created only when
188
+ * it is genuinely missing, and only when a wallet address was given -- a bare component address
189
+ * never reveals the owner key `CreateAccount` needs, so that form can only pay an account that
190
+ * already exists.
191
+ */
192
+ send(recipientAddress: string, resourceAddress: string, amount: bigint, maxFee?: bigint): Promise<{
193
+ transaction_id: TransactionId;
194
+ result: ExecuteResult;
195
+ } | IndexerGetTransactionResultResponse>;
196
+ /**
197
+ * Moves `amount` of this account's own REVEALED balance into a Confidential-type vault for the
198
+ * same resource -- the "Confidential" `ResourceType`'s equivalent of `shield()`, a different
199
+ * privacy mechanism from Stealth (vault-based, ElGamal-encrypted to a resource view key, rather
200
+ * than freestanding one-time-key UTXOs -- see the resource-types write-up in the integration
201
+ * docs). Only meaningful for a resource actually created as `ResourceType::Confidential`; calling
202
+ * this against a Fungible or Stealth resource's vault fails on-chain ("resource type mismatch" or
203
+ * similar), not client-side -- this class has no way to know a resource's type up front without
204
+ * an extra substate fetch, so the failure is left to the engine.
205
+ *
206
+ * Builds a `ConfidentialWithdrawProof` client-side (no wallet-daemon round trip) via the vendored
207
+ * `createConfidentialWithdrawProofLiteral` -- see that WASM export's own doc comment for why the
208
+ * `tari_bor` encoding happens in Rust rather than being hand-rolled here: the official
209
+ * `@tari-project/ootle` SDK's own `feeTransactionPayFromComponentConfidential` is unimplemented
210
+ * for exactly this reason ("a ConfidentialWithdrawProof Literal must be tari_bor-CBOR-encoded,
211
+ * which the TS SDK does not yet support").
212
+ *
213
+ * The new confidential output is addressed to this account's own owner/view keys -- the same
214
+ * one-time-output-witness construction `createStealthOutputWitness` already builds for Stealth
215
+ * outputs, reused here since `ConfidentialOutputStatement.output` and a `StealthOutputWitness`'s
216
+ * `witness` share the exact same shape (mask, sender_public_nonce, encrypted_data). Only the
217
+ * `witness` half of that result is used -- the `auth`/`tag` halves are Stealth-specific
218
+ * (`SpendAuthorization`/`UtxoTag`); a Confidential output has neither, ownership being purely
219
+ * "which vault holds it," not a spend-authorization key.
220
+ *
221
+ * Two instructions, mirroring `send()`'s withdraw-then-deposit shape: `withdraw_confidential`
222
+ * (drawing `amount` from the vault's revealed side, per the proof's `input_revealed_amount`)
223
+ * produces a bucket holding the one new confidential output, which `deposit` puts back in the
224
+ * same vault.
225
+ */
226
+ depositConfidential(resourceAddress: string, amount: bigint, maxFee?: bigint): Promise<{
227
+ transaction_id: TransactionId;
228
+ result: ExecuteResult;
229
+ } | IndexerGetTransactionResultResponse>;
230
+ /**
231
+ * Claims free testnet XTR from the network's builtin faucet (esmeralda/igor only — there is no
232
+ * such faucet on mainnet). Mirrors both `build_faucet_claim_with_wants` in tari-ootle's
233
+ * `crates/ootle_sdk_core/src/faucet.rs` and (confirmed against its actual pinned-input list)
234
+ * `handle_create_free_test_coins` in `applications/tari_walletd/src/handlers/accounts.rs` — the
235
+ * handler backing the `accounts.create_free_test_coins` walletd RPC that tari-dex's swap-ui
236
+ * calls. Self-funding, so it works even for a brand-new account that doesn't exist on-chain yet:
237
+ * the fee phase itself creates the account, funds it from the faucet
238
+ * (`XTR_FAUCET_COMPONENT_ADDRESS.take(account)`), then pays its own fee out of what it just
239
+ * received.
240
+ *
241
+ * Unlike every other component this class calls, none of the faucet's substates (component,
242
+ * vault, claim resource) get auto-resolved from the instructions by this indexer — each has to
243
+ * be pinned as an explicit transaction input (confirmed empirically: submitting with only the
244
+ * claim resource pinned rejected with "component_...0000 not found" at the `take` call).
245
+ * `provider.resolveInputs()` fills in each one's current version.
246
+ *
247
+ * The faucet vault is a single shared substate every claimant on the testnet contends for, so
248
+ * the version pinned by `resolveInputs()` routinely goes stale between resolution and consensus
249
+ * (confirmed empirically: "Lock failure: Substate vault_...:8 is DOWN" — someone else's claim
250
+ * landed first). This isn't a bug to fix, just contention to ride out: catch that specific
251
+ * rejection and retry with freshly-resolved versions and a new transaction id.
252
+ *
253
+ * Retries back off (`retryDelayMs * (attempt + 1)`, so 300ms, 600ms, 900ms, ...) rather than
254
+ * resubmitting back-to-back: confirmed empirically that hammering `resolveInputs()` immediately
255
+ * after a rejection can keep handing back the same already-stale version every time (the
256
+ * indexer's own view of a shared, heavily-contended substate can lag behind consensus by more
257
+ * than one round trip takes) — a short, growing pause gives that view time to catch up instead
258
+ * of burning the whole retry budget re-observing the same stale state.
259
+ */
260
+ claimTestnetXtr(maxFee?: bigint, retries?: number, retryDelayMs?: number): Promise<IndexerGetTransactionResultResponse>;
261
+ /**
262
+ * Shields (moves from revealed to private/confidential) `amount` of `resourceAddress`, held as
263
+ * a new stealth output owned by this same account. Uses an entirely separate submission
264
+ * pipeline from `execute()`: the SDK's `StealthTransfer` builder produces its own
265
+ * `TransactionEnvelope` directly (via `WalletStealthAuthorizer`/`AuthorizedTransfer`), not
266
+ * through `TransactionBuilder`/`resolveTransaction`/`signTransaction`/`sealTransaction`.
267
+ *
268
+ * The one genuinely new failure mode this adds beyond `send()`: if the extension's service
269
+ * worker is killed *after* the transaction finalizes on-chain but *before* the resulting
270
+ * commitment is written to storage, the shield succeeds financially but the wallet loses its
271
+ * only lead back to that output — worse than a failed `send()`, whose destination is always a
272
+ * known component address. `pendingShieldTransactionIds` (written before polling, cleared only
273
+ * after the commitment record is stored) narrows that window to "killed between finalization
274
+ * and one storage write" and gives `recoverPendingShields()` (background/index.ts) something
275
+ * to reconcile on next launch.
276
+ *
277
+ * `maxFee` defaults far higher than `send()`'s 5000 -- confirmed live that 5000 gets
278
+ * "AcceptFeeRejectRest" with `ExecutionFailure: "Insufficient fees to fund native
279
+ * verification"` (the stealth balance proof / range proof verification costs real compute
280
+ * points beyond the engine's free grace allowance). Unused fee is refunded on success, so
281
+ * erring high just wastes nothing; erring low burns the small fee-intent cost with nothing to
282
+ * show for it, since only the fee half of the transaction gets committed.
283
+ */
284
+ shield(resourceAddress: string, amount: bigint, maxFee?: bigint, memo?: string, minimumValuePromise?: bigint): Promise<{
285
+ transactionId: string;
286
+ commitment: string;
287
+ substateId: string;
288
+ minimumValuePromise: string;
289
+ }>;
290
+ /**
291
+ * Proves this account currently controls the stealth output at `substateId` -- e.g. a
292
+ * `minimumValuePromise` proof-of-funds output this account created earlier -- by Schnorr-signing
293
+ * `challenge` with the output's one-time spend key. Spends nothing and reveals nothing about the
294
+ * output beyond what `tari_getSubstate` already shows anyone.
295
+ *
296
+ * `challenge` is signed under a domain tag (`ownershipProof.ts`) disjoint from real transaction
297
+ * signing, so the resulting signature can never be replayed as spend authorization for this or
298
+ * any other output -- this is the entire reason a caller cannot just ask for a raw signature over
299
+ * an arbitrary message.
300
+ *
301
+ * Throws if the substate isn't a live, key-authorized stealth output, or if it isn't actually
302
+ * addressed to this account (checked by re-deriving the expected one-time public key and
303
+ * comparing it to the substate's own `auth.Key` -- the same check `scan_stealth_output` does
304
+ * receiver-side, just run here against a specific caller-supplied id instead of a scan).
305
+ */
306
+ signOwnershipProof(resourceAddress: string, substateId: string, challenge: string): Promise<{
307
+ publicKey: string;
308
+ publicNonce: string;
309
+ signature: string;
310
+ }>;
311
+ /**
312
+ * Proves this account holds a specific `otl_…` wallet address by Schnorr-signing `challenge`
313
+ * with the account's own persistent owner key -- not a per-output derived one, so unlike
314
+ * `signOwnershipProof` this needs no substate lookup and isn't tied to any particular output.
315
+ *
316
+ * Signs under a domain tag (`ownershipProof.ts`) disjoint from real transaction signing, for the
317
+ * same reason `signOwnershipProof` does: this exact key also signs real transactions
318
+ * (`SecretKeyWallet.signTransaction`), so a raw signature over caller-supplied bytes would risk
319
+ * being replayable as spend authorization.
320
+ *
321
+ * A verifier checks the result against the owner key decoded from the wallet address *they*
322
+ * already have in mind (`parseOotleAddress`) -- never against a value this call's caller reports
323
+ * about themselves.
324
+ */
325
+ signWalletOwnership(challenge: string): Promise<{
326
+ walletAddress: string;
327
+ publicNonce: string;
328
+ signature: string;
329
+ }>;
330
+ /**
331
+ * Unshields (moves from private/confidential back to revealed) `revealedOutAmount` of this
332
+ * account's shielded balance for `resourceAddress` — see `ShieldedOutputRecord`'s doc comment
333
+ * for why a known local record is the only way to spend one (no client-side scan-by-view-key
334
+ * API exists for stealth UTXOs). Which specific output(s) to spend is decided internally by
335
+ * `resolveUnshieldPlan`'s coin selection (largest-first, spending more than one in the same
336
+ * transaction via repeated `spendStealthInput()` calls if a single output isn't enough) — the
337
+ * caller only supplies an amount, not a commitment.
338
+ *
339
+ * Two `StealthTransfer` builder requirements, confirmed directly against its `validate()`/
340
+ * `emitInstructions()` source (not assumed from the docs alone), shape this method:
341
+ *
342
+ * 1. `toRevealedOutput`/`payFeeFromRevealed` both require a revealed source account already
343
+ * registered via `spendRevealedInput`, which itself requires an amount `> 0` — there is no
344
+ * way to register just the account without withdrawing something. This method withdraws a
345
+ * trivial `1n`-unit "dust" amount for that sole purpose and folds it straight back into the
346
+ * revealed output (`revealedOutAmount + 1n`), so it costs nothing beyond the tx fee.
347
+ * 2. The builder always requires at least one stealth output — a "pure" 100%-revealed spend
348
+ * with zero private remainder cannot be constructed in one step. This method always creates
349
+ * a new stealth output for the selected inputs' total minus `revealedOutAmount` back to this
350
+ * same account, so `resolveUnshieldPlan` guarantees that remainder is always `> 0`.
351
+ *
352
+ * Shares `shield()`'s mid-flight crash safety: the pending-shield ledger entry carries
353
+ * `spentCommitments` so `recoverPendingShields()` can both record the new change output *and*
354
+ * mark the spent ones, even if the service worker dies between finalization and the storage
355
+ * writes.
356
+ */
357
+ unshield(resourceAddress: string, revealedOutAmount: bigint, maxFee?: bigint, memo?: string): Promise<{
358
+ transactionId: string;
359
+ }>;
360
+ /**
361
+ * Withdraws `amount` of a Stealth-typed resource (e.g. XTR) from this account's revealed
362
+ * balance and feeds it, as a `Bucket` left on the workspace under `workspaceVarName`, into
363
+ * `followUpInstructions` — all in one signed transaction.
364
+ *
365
+ * This is the *only* way to move Stealth-typed funds into an arbitrary contract call. A plain
366
+ * `CallMethod withdraw` on a Stealth vault is not a standalone-valid instruction: confirmed
367
+ * empirically (isolated to a bare `withdraw` immediately followed by `deposit`, zero other
368
+ * instructions) that `account.execute()` fails it client-side, before ever reaching the
369
+ * network, with a generic `JSON deserialization failed: ... untagged enum TransactionInput`
370
+ * error. Moving Stealth funds anywhere always requires the native `StealthTransfer`
371
+ * instruction, signed via `WalletStealthAuthorizer` — which `execute()`/`TransactionBuilder`
372
+ * never invokes (see this file's own `execute()` doc comment, and the
373
+ * `isStealthTransferInstruction` guard + comment in `background/index.ts`). This method uses
374
+ * the *same* `StealthTransfer` builder + `WalletStealthAuthorizer` pipeline `shield()`/
375
+ * `unshield()` already use, extended with `toRevealedOutputAsBucket`/`andThen` (see
376
+ * `vendor/ootle-patched/README.md`) to leave the revealed output on the workspace instead of
377
+ * auto-depositing it back to this account, and to append the caller's own instructions after
378
+ * it in the same transaction.
379
+ *
380
+ * @param relatedComponents Every *other* component `followUpInstructions` touches (e.g. a
381
+ * DAO contract being called). The `StealthTransfer` builder auto-registers this account's
382
+ * own vaults as tx inputs, but has no way to know what the caller's own follow-up
383
+ * instructions reference — the engine rejects a `CallMethod` touching an unregistered
384
+ * substate with `SubstateNotFound`. Each is registered by address, plus every vault its own
385
+ * state references (the same vault-discovery this account's own address already gets).
386
+ */
387
+ withdrawStealthAndExecute(resourceAddress: string, amount: bigint, workspaceVarName: string, followUpInstructions: Instruction[], relatedComponents?: string[], maxFee?: bigint): Promise<{
388
+ transactionId: string;
389
+ }>;
390
+ /**
391
+ * Funds an HTLC (hashed timelock contract): creates a stealth output for `amount` of
392
+ * `resourceAddress`, gated not by a normal one-time stealth key but by a two-leaf TIP-0006
393
+ * condition tree (`htlcConditions` in `./htlc.ts`) — a claim path admissible only to
394
+ * `claimantWalletAddress` (revealing the SHA-256 preimage of `hashLockHex`, before
395
+ * `refundEpoch`), and a refund path admissible only to this account (at/after `refundEpoch`).
396
+ * `htlcConditions`'s own doc comment covers the exact leaf shapes.
397
+ *
398
+ * This account never needs (and must never be given) the actual preimage — only its hash. The
399
+ * caller resolves `refundEpoch` from a desired wall-clock deadline via the provider/network's
400
+ * current epoch, the same as any other epoch-relative on-chain deadline.
401
+ *
402
+ * Returns the full `conditions` tree alongside the transaction id and this output's own
403
+ * commitment: only the tree's **root** is committed on-chain, so the claimant needs the exact
404
+ * leaves — out of band, via whatever swap protocol coordinates the two sides — to later reveal
405
+ * the claim leaf.
406
+ *
407
+ * Also returns `outputMask` (hex): this account funded the output, so it's the one place the
408
+ * blinding mask is ever available to it without decryption — `encrypted_data` is encrypted for
409
+ * `claimantWalletAddress`'s view key, not this account's, so if the claim path is never taken
410
+ * and this account later calls `htlcRefund`, it cannot decrypt the output the normal way (the
411
+ * way `htlcClaim`'s claimant can, since the output is addressed to *them*). Retaining this mask
412
+ * now — alongside the known `amount` — is what makes an undecryptable refund possible at all.
413
+ * `htlcRefund`'s own doc comment covers the rest.
414
+ *
415
+ * `destination` is set to the claimant's own wallet address (not this account's) so the
416
+ * claimant can independently decrypt and verify the funded amount themselves, the same as any
417
+ * other stealth payment addressed to them — spend authority is governed entirely separately, by
418
+ * `payTo`.
419
+ */
420
+ htlcFund(resourceAddress: string, amount: bigint, claimantWalletAddress: string, hashLockHex: string, refundEpoch: bigint, maxFee?: bigint): Promise<{
421
+ transactionId: string;
422
+ conditions: object[];
423
+ ownCommitment: string;
424
+ outputMask: string;
425
+ }>;
426
+ /**
427
+ * Claims a funded HTLC (the counterpart to `htlcFund`): reveals the claim leaf's SHA-256
428
+ * preimage to spend the script-path-locked output, moving `amount` into a brand-new, normal
429
+ * (freely key-spendable) stealth output owned by this account.
430
+ *
431
+ * This account must be the intended claimant — `htlcFund` addressed the output to this
432
+ * account's own wallet address, so it can unblind it the normal way: fetch the `utxo_...`
433
+ * substate and decrypt with this account's own view secret, exactly like `claimPrivatePayment`
434
+ * does for any other incoming stealth payment. `conditions` must be the *exact* two-leaf tree
435
+ * `htlcFund` returned (or was handed out of band) — the wrong tree produces a script-path
436
+ * witness whose condition root won't match the output's committed `SpendAuthorization::Script`
437
+ * root, and `buildStealthTransferStatement`/the network both reject the spend.
438
+ *
439
+ * Fees are paid from this account's own existing revealed balance via `pay_fee` (same as any
440
+ * other transaction this wallet submits), not from the claimed HTLC funds — a transaction's fee
441
+ * instructions execute in a separate phase *before* its main instructions, so the claim's own
442
+ * `StealthTransfer` instruction (a main instruction) can't fund its own fee even in principle.
443
+ *
444
+ * Bypasses `StealthTransfer`/`WalletStealthAuthorizer` entirely — that builder pipeline only
445
+ * ever produces key-path stealth inputs (a one-time DH signature per input), which doesn't
446
+ * apply here: this spend's authorization is the revealed script-path leaf itself (its
447
+ * `AccessRule` atom, satisfied by this account's normal transaction signature), not a stealth
448
+ * spend key. `buildStealthTransferStatement` (tari-project/tari-ootle#2431) produces the
449
+ * complete statement — inputs/outputs/balance proof/covenant claims — in one call from the
450
+ * unblinded mask+value and the two witnesses below, so there is nothing left for the builder
451
+ * pipeline to add.
452
+ */
453
+ htlcClaim(resourceAddress: string, commitmentHex: string, conditions: object[], preimageHex: string, maxFee?: bigint): Promise<{
454
+ transactionId: string;
455
+ }>;
456
+ /**
457
+ * Refunds an HTLC this account itself funded (via `htlcFund`), once `refundEpoch` has passed:
458
+ * reveals the refund leaf to spend the output back into a brand-new, normal (freely
459
+ * key-spendable) stealth output owned by this account.
460
+ *
461
+ * Unlike `htlcClaim`, this account **cannot** decrypt the output on-chain — `htlcFund` addressed
462
+ * it to the claimant's wallet, so `encrypted_data` is encrypted for the claimant's view key, not
463
+ * this account's. `amount` and `outputMaskHex` must be exactly what `htlcFund` returned (or
464
+ * otherwise already known — this account chose `amount` itself when funding, and `outputMaskHex`
465
+ * is `htlcFund`'s own return value for exactly this purpose): reconstructing the commitment from
466
+ * a wrong mask or amount fails cleanly (the resulting input just doesn't match the on-chain
467
+ * commitment, so the network rejects the spend) rather than silently, but there is no recovery
468
+ * path if this account discarded the mask — see `htlcFund`'s doc comment.
469
+ *
470
+ * Otherwise identical to `htlcClaim`: same fee-from-own-balance reasoning, same
471
+ * `buildStealthTransferStatement`-in-one-call approach, same bypass of
472
+ * `StealthTransfer`/`WalletStealthAuthorizer`.
473
+ */
474
+ htlcRefund(resourceAddress: string, commitmentHex: string, conditions: object[], amount: bigint, outputMaskHex: string, maxFee?: bigint): Promise<{
475
+ transactionId: string;
476
+ }>;
477
+ /**
478
+ * Sends `amount` of this account's shielded balance for `resourceAddress` directly to
479
+ * `recipientWalletAddress` — a private-to-private transfer, unlike `shield()` (which always
480
+ * targets this same account's own wallet address). The recipient never appears on-chain in the
481
+ * clear; only they (holding the matching view key) can discover the payment, and only by being
482
+ * told the resulting commitment out of band — same "no scan API" caveat as everywhere else
483
+ * stealth outputs are spent. Which specific output(s) to spend is decided internally by
484
+ * `resolveSendPrivatelyPlan`'s coin selection (largest-first, spending more than one in the same
485
+ * transaction via repeated `spendStealthInput()` calls if a single output isn't enough) — the
486
+ * caller only supplies an amount, not a commitment.
487
+ *
488
+ * Unlike `unshield()`, sending the *entire* selected total with no remainder is possible in one
489
+ * step here: the builder's "at least one stealth output" requirement is trivially satisfied by
490
+ * the recipient's own output, with no need for a same-account dust/change output to satisfy it.
491
+ * Change (if `amount` is less than the selected inputs' total) is a second stealth output back
492
+ * to this account.
493
+ *
494
+ * The fee-paying "dust" trick from `unshield()` still applies for the same reason: pay_fee and
495
+ * (when there's change) the *concept* of "this account received a new output" both need a
496
+ * revealed source account registered via `spendRevealedInput`, which requires amount `> 0`.
497
+ *
498
+ * Crash-safety: this account's own change commitment (if any) is read directly from the
499
+ * locally-built outputs statement, *not* inferred from the finalized transaction's
500
+ * `up_substates` — those also contain the recipient's brand-new stealth output, and naively
501
+ * grabbing "the first new stealth output" would risk recording the recipient's payment as our
502
+ * own. See `PendingShield.ownCommitment`'s doc comment.
503
+ */
504
+ sendPrivately(resourceAddress: string, recipientWalletAddress: string, amount: bigint, maxFee?: bigint, memo?: string, minimumValuePromise?: bigint): Promise<{
505
+ transactionId: string;
506
+ recipientCommitment: string;
507
+ recipientSubstateId: string;
508
+ minimumValuePromise: string;
509
+ }>;
510
+ /**
511
+ * The recipient-side counterpart to `sendPrivately()`'s commitment hand-off: given a commitment
512
+ * shared out of band, fetches the corresponding `utxo_{resource}_{commitment}` substate and
513
+ * tries to decrypt it with this account's own view secret. `decryptOwnedUtxo` swallows the
514
+ * "not ours" throw and returns `null` instead, so a wrong/foreign commitment fails cleanly here
515
+ * rather than looking like a network error. Success both *proves* ownership and recovers the
516
+ * amount in one step (no need to be told the amount separately) -- recorded exactly like a
517
+ * self-shield's output, so it then just shows up in this account's private balance and
518
+ * shielded-outputs picker.
519
+ */
520
+ claimPrivatePayment(resourceAddress: string, commitmentHex: string): Promise<{
521
+ amount: bigint;
522
+ memo?: string;
523
+ }>;
524
+ /**
525
+ * This account's unspent stealth outputs, newest first, optionally filtered to one resource.
526
+ *
527
+ * This local ledger *is* the account's private position -- see `ShieldedOutputRecord`'s doc
528
+ * comment for why it has to be: a stealth output is a freestanding
529
+ * `utxo_{resource}_{commitment}` substate, not an entry in any vault, and there is no
530
+ * scan-by-view-key API that could rediscover one from the chain alone given only its commitment.
531
+ * Anything not in here is, as far as this wallet is concerned, not spendable.
532
+ */
533
+ listUnspentShieldedOutputs(resourceAddress?: string): Promise<ShieldedOutputRecord[]>;
534
+ /**
535
+ * Per-resource totals over `listUnspentShieldedOutputs()`, with each resource's real on-chain
536
+ * `divisibility`/symbol/name folded in (the same lookup `getBalances()` does, via the shared
537
+ * `fetchResourceMetadata` helper -- a private balance has to be renderable on its own, without a
538
+ * caller cross-referencing `getBalances()` for the decimals).
539
+ *
540
+ * Distinct from `getBalances()`'s `confidentialAmount` in two ways worth being precise about:
541
+ * it counts only these freestanding stealth outputs (not a Confidential *vault*'s own
542
+ * commitments map, which `getBalances()` also decrypts and adds in), and it reports a resource
543
+ * whose only holding is private, which `getBalances()` only surfaces via its
544
+ * `synthesizeShieldedOnlyBalances` path. For "what can I spend privately right now", this is the
545
+ * authoritative number, because it is exactly what `resolveUnshieldPlan`/
546
+ * `resolveSendPrivatelyPlan` select from.
547
+ *
548
+ * One network round trip (the resource metadata batch); the amounts themselves are local.
549
+ */
550
+ getPrivateBalances(): Promise<PrivateBalance[]>;
551
+ /**
552
+ * Automatic counterpart to `claimPrivatePayment()`: instead of requiring the recipient be told a
553
+ * commitment out of band, walks the indexer's recent-transaction history looking for
554
+ * `StealthTransfer` outputs this account can decrypt with its own view key (see
555
+ * `scanTransactionsForOwnedOutputs`'s doc comment for why the data needed to do this is present
556
+ * even in the *pruned* form `listRecentTransactions` returns).
557
+ *
558
+ * Cursor-based (`privatePaymentScanCursors[accountId]`, the newest transaction id seen last time)
559
+ * so this can run cheaply and opportunistically on every `popup-get-status` — see
560
+ * `buildStatus()`'s `recoverPendingShields()` call for the same pattern. Walks backward in pages
561
+ * of `pageSize` (oldest-paging via `last_id`, per the indexer's own "recent" convention) up to
562
+ * `maxPages`, stopping early once it reaches the transaction id it left off at last time. A very
563
+ * active chain producing more than `maxPages * pageSize` new transactions between scans will
564
+ * only be caught up partway; the next scan resumes from the same cursor and continues.
565
+ */
566
+ scanForPrivatePayments(maxPages?: number, pageSize?: number): Promise<{
567
+ claimed: number;
568
+ found: ScannedStealthOutput[];
569
+ }>;
570
+ }
571
+ /**
572
+ * Reads the commitment of the output at `outputIndex` (in the order `toStealthOutput()` was
573
+ * called) directly from the outputs statement `StealthTransfer.prepare()` already built —
574
+ * generated client-side, so it's known *before* submission, not inferred afterward by scanning
575
+ * the finalized transaction's `up_substates`. That scan-based approach is unambiguous when a
576
+ * transfer only ever creates one new stealth output belonging to this account (shield, unshield),
577
+ * but breaks down the moment a transfer also creates a *different* account's new stealth output
578
+ * in the same transaction (a private send's recipient output) — grabbing "the first new stealth
579
+ * output" from `up_substates` would then risk attributing someone else's payment to us.
580
+ */
581
+ /**
582
+ * Builds a complete `StealthTransferStatement` (as its final compact JSON string) that spends one
583
+ * script-path-locked HTLC UTXO into one brand-new, normal (key-spendable) stealth output — the
584
+ * shared core of `htlcClaim`/`htlcRefund`, which differ only in which leaf they reveal and how
585
+ * they come by the input's `mask`/`value` (on-chain decryption for a claim; retained from funding
586
+ * time for a refund, since the funder can't decrypt an output addressed to the claimant).
587
+ *
588
+ * Every JSON fragment here is spliced from the raw strings `buildScriptPathWitness`/
589
+ * `createStealthOutputWitness` themselves return, never `JSON.parse`d and rebuilt — both carry
590
+ * u64 fields (amounts, epochs) that can exceed `Number.MAX_SAFE_INTEGER`, and a parse/restringify
591
+ * round-trip through a JS number would silently corrupt them. `value` is spliced in from `params`
592
+ * via template-literal interpolation of a `bigint`, which stringifies to its exact decimal digits
593
+ * with no such risk.
594
+ */
595
+ export declare function buildHtlcSpendStatement(params: {
596
+ network: Network;
597
+ conditions: object[];
598
+ leaf: object;
599
+ /** The revealed leaf's witness data -- the preimage for a claim, empty for a refund (the
600
+ * refund leaf has no data-consuming `HashLock` atom). */
601
+ data: Uint8Array;
602
+ mask: string;
603
+ value: bigint;
604
+ destinationWalletAddress: string;
605
+ resourceAddress: string;
606
+ }): string;
607
+ /**
608
+ * Synthesizes a `TokenBalance` entry for each resource whose only balance is a shielded output
609
+ * with no on-chain vault at all (e.g. redeemed via the "Advanced" unshield flow from someone
610
+ * else's shared commitment) -- these never appear in `getBalances()`'s vault-derived `parsed`
611
+ * list, since they're freestanding `utxo_{resource}_{commitment}` substates, not vault entries.
612
+ * Skips any resource already covered by `parsedResources` (that one gets its shielded amount
613
+ * folded into its existing entry instead, in `getBalances()` itself).
614
+ */
615
+ export declare function synthesizeShieldedOnlyBalances(parsedResources: Set<string>, shieldedByResource: Map<string, bigint>, divisibilityByResource: Map<string, number>, symbolByResource: Map<string, string | null>, nameByResource: Map<string, string | null>): TokenBalance[];
616
+ /**
617
+ * Coin selection for spending shielded outputs: picks this resource's unspent records
618
+ * largest-first until their sum covers `targetAmount`, so a spend needing more than any single
619
+ * output holds is satisfied by combining several in one transaction (the builder's
620
+ * `spendStealthInput()` can be called once per input UTXO) rather than requiring the caller to
621
+ * pick one record themselves. Largest-first minimizes the number of inputs spent (each one adds
622
+ * real signature/proof-verification fee cost), rather than needlessly consolidating dust.
623
+ *
624
+ * Returns the unselected remainder too (still sorted largest-first) so callers that need "at
625
+ * least one more unit available" (see `resolveUnshieldPlan`) can pull in exactly one more output
626
+ * without re-deriving the candidate list.
627
+ */
628
+ /**
629
+ * The unspent subset of a shielded-output ledger, newest first, optionally narrowed to one
630
+ * resource. Newest-first is a display order, deliberately *not* the spend order: coin selection is
631
+ * `selectShieldedUtxosForAmount`'s largest-first job, and the two must not be conflated.
632
+ *
633
+ * Split out of `OotleAccount.listUnspentShieldedOutputs` (which is just this over the account's own
634
+ * stored records) so the filtering rule -- what counts as spendable, and what a dApp with view
635
+ * access is allowed to see -- is testable on its own.
636
+ */
637
+ export declare function selectUnspentShieldedOutputs(records: ShieldedOutputRecord[], resourceAddress?: string): ShieldedOutputRecord[];
638
+ /**
639
+ * Totals a set of already-unspent shielded outputs per resource. `outputCount` is carried
640
+ * alongside the sum rather than left to be recomputed, because it is not cosmetic: each output is
641
+ * spent whole, so the count is what bounds whether a given amount can actually be covered (see
642
+ * `resolveUnshieldPlan`) -- a balance of 100 across one output and across fifty are very different
643
+ * things to a caller planning a spend.
644
+ *
645
+ * Insertion-ordered by first appearance (Map iteration order), so a caller's rendering doesn't
646
+ * reshuffle between calls for no reason.
647
+ */
648
+ export declare function summarizePrivateHoldings(records: ShieldedOutputRecord[]): {
649
+ resourceAddress: string;
650
+ amount: bigint;
651
+ outputCount: number;
652
+ }[];
653
+ export declare function selectShieldedUtxosForAmount(records: ShieldedOutputRecord[], resourceAddress: string, targetAmount: bigint): {
654
+ selected: ShieldedOutputRecord[];
655
+ total: bigint;
656
+ unselected: ShieldedOutputRecord[];
657
+ };
658
+ /**
659
+ * Pure planning for `unshield()`: selects enough of the caller's unspent records for this
660
+ * resource (via `selectShieldedUtxosForAmount`) to cover `revealedOutAmount`, and checks the
661
+ * total leaves at least 1 unit as private remainder (see `unshield()`'s doc comment for why the
662
+ * upper bound is strict, not `<=`). Extracted so this decision logic is unit-testable without a
663
+ * live provider/WASM crypto, same reasoning as `pollTransactionResult`'s extraction.
664
+ *
665
+ * If the minimal selection's total lands *exactly* on `revealedOutAmount` (zero remainder), and
666
+ * another unspent record for this resource exists, one more (the smallest available) is pulled
667
+ * in purely to create a nonzero remainder — spending an extra UTXO is preferable to failing a
668
+ * reveal that the account's total private balance could otherwise satisfy.
669
+ */
670
+ export declare function resolveUnshieldPlan(records: ShieldedOutputRecord[], resourceAddress: string, revealedOutAmount: bigint): {
671
+ commitments: string[];
672
+ remainder: bigint;
673
+ };
674
+ /**
675
+ * Pure planning for `sendPrivately()`: selects enough of the caller's unspent records for this
676
+ * resource to cover `amount` — unlike `resolveUnshieldPlan`, sending the *entire* selected total
677
+ * with zero change is allowed, since the recipient's own stealth output already satisfies the
678
+ * builder's "at least one stealth output" requirement (no same-account dust output is needed to
679
+ * satisfy it, unlike unshield's revealed destination). Extracted for the same unit-testability
680
+ * reasons as `resolveUnshieldPlan`.
681
+ */
682
+ export declare function resolveSendPrivatelyPlan(records: ShieldedOutputRecord[], resourceAddress: string, amount: bigint): {
683
+ commitments: string[];
684
+ changeAmount: bigint;
685
+ };
686
+ /**
687
+ * Reconciles shields whose `pollTransactionResult` never got to finish (the extension's service
688
+ * worker was killed between submission and the storage write) — see `PendingShield`'s doc
689
+ * comment. Checks each pending shield's current status *once* (not a blocking poll-to-finalize
690
+ * loop — if it's still genuinely pending, this leaves it for the next check rather than stalling
691
+ * whatever triggered recovery, typically `popup-get-status`) and either completes the storage
692
+ * write (finalized successfully), drops it (rejected — no stealth output was ever created, so
693
+ * there's nothing to recover), or leaves it alone (still pending). One provider is reused across
694
+ * every pending shield, whichever local account they belong to — this is a read-only connection,
695
+ * not account/signing-specific.
696
+ */
697
+ export declare function recoverPendingShields(provider: IndexerProvider): Promise<void>;
698
+ /**
699
+ * Forgets what we thought we knew about these substates' versions, after the chain has told us a
700
+ * remembered one is unusable. Keeping it would make `applyKnownVersions` reassert it over every
701
+ * future resolve, turning one bad write into a permanently stuck wallet.
702
+ */
703
+ export declare function forgetKnownVersions(substateIds: string[]): Promise<void>;
704
+ /** Drops the in-memory known-versions cache. Call this on wallet reset so a freshly-created wallet
705
+ * can't inherit the previous wallet's cached version numbers — the persisted side of that wipe is
706
+ * the caller's own responsibility (a serialized clear of whatever `KeyValueStore` backs `storage.ts`). */
707
+ export declare function resetKnownVersions(): void;
708
+ /**
709
+ * Polls a submitted transaction's result to finalization (or rejection/timeout). Extracted from
710
+ * `OotleAccount.submitReal()` so `shield()`/`unshield()` — which submit via a completely
711
+ * different pipeline (`StealthTransfer`/`WalletStealthAuthorizer`/`submitTransaction`, not
712
+ * `TransactionBuilder`) — share the exact same hardened Reject/AcceptFeeRejectRest/timeout
713
+ * handling instead of a second, potentially-drifting copy of it.
714
+ */
715
+ export declare function pollTransactionResult(provider: IndexerProvider, transactionId: string, timeoutMs?: number): Promise<IndexerGetTransactionResultResponse>;
716
+ export declare function resolveInputsWithRetry(provider: IndexerProvider, requirements: SubstateRequirement[], retries?: number, retryDelayMs?: number): Promise<SubstateRequirement[]>;
717
+ /**
718
+ * Checks whether `substateId` already exists on-chain, tolerating a couple of retries in case it
719
+ * just hasn't finalized on the indexer yet (a much smaller budget than `resolveInputsWithRetry`'s
720
+ * default — this is used to *decide* whether to create something, not to wait out a known producer,
721
+ * so a persistent 404 should read as "doesn't exist" quickly rather than stall the caller). Only
722
+ * `resolveInputsWithRetry`'s specific "not yet finalized" 404 is treated as "missing" — any other
723
+ * failure (network error, indexer down) propagates rather than being silently read as nonexistence,
724
+ * since that misread would make `send()` prepend a `CreateAccount` for an account that actually
725
+ * exists, which the engine rejects outright ("is already UP and conflicts with an existing output").
726
+ */
727
+ export declare function substateExists(provider: IndexerProvider, substateId: string): Promise<boolean>;
728
+ export declare function extractMissingSubstateAddress(message: string): string | null;
729
+ export declare function extractStaleLockVersion(message: string): {
730
+ substateId: string;
731
+ version: number;
732
+ } | null;
733
+ //# sourceMappingURL=wallet.d.ts.map