@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.
- package/LICENSE +21 -0
- package/README.md +90 -0
- package/dist/accountApi.d.ts +33 -0
- package/dist/accountApi.d.ts.map +1 -0
- package/dist/accountApi.js +1 -0
- package/dist/adapters.d.ts +13 -0
- package/dist/adapters.d.ts.map +1 -0
- package/dist/adapters.js +68 -0
- package/dist/componentAddress.d.ts +27 -0
- package/dist/componentAddress.d.ts.map +1 -0
- package/dist/componentAddress.js +68 -0
- package/dist/confidential.d.ts +50 -0
- package/dist/confidential.d.ts.map +1 -0
- package/dist/confidential.js +86 -0
- package/dist/derivation.d.ts +7 -0
- package/dist/derivation.d.ts.map +1 -0
- package/dist/derivation.js +47 -0
- package/dist/domainHash.d.ts +11 -0
- package/dist/domainHash.d.ts.map +1 -0
- package/dist/domainHash.js +44 -0
- package/dist/htlc.d.ts +26 -0
- package/dist/htlc.d.ts.map +1 -0
- package/dist/htlc.js +69 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +14 -0
- package/dist/ootleNetwork.d.ts +4 -0
- package/dist/ootleNetwork.d.ts.map +1 -0
- package/dist/ootleNetwork.js +11 -0
- package/dist/ownershipProof.d.ts +4 -0
- package/dist/ownershipProof.d.ts.map +1 -0
- package/dist/ownershipProof.js +53 -0
- package/dist/storage.d.ts +61 -0
- package/dist/storage.d.ts.map +1 -0
- package/dist/storage.js +110 -0
- package/dist/timeout.d.ts +2 -0
- package/dist/timeout.d.ts.map +1 -0
- package/dist/timeout.js +19 -0
- package/dist/vault.d.ts +12 -0
- package/dist/vault.d.ts.map +1 -0
- package/dist/vault.js +44 -0
- package/dist/wallet.d.ts +733 -0
- package/dist/wallet.d.ts.map +1 -0
- package/dist/wallet.js +1964 -0
- package/package.json +46 -0
- package/vendor/ootle-patched/README.md +67 -0
- package/vendor/ootle-patched/dist/index.d.ts +2129 -0
- package/vendor/ootle-patched/dist/index.js +1365 -0
- package/vendor/ootle-patched/package.json +23 -0
- package/vendor/ootle-wasm-patched/README.md +128 -0
- package/vendor/ootle-wasm-patched/ootle_wasm.d.ts +454 -0
- package/vendor/ootle-wasm-patched/ootle_wasm.js +9 -0
- package/vendor/ootle-wasm-patched/ootle_wasm_bg.js +1485 -0
- package/vendor/ootle-wasm-patched/ootle_wasm_bg.wasm +0 -0
- package/vendor/ootle-wasm-patched/ootle_wasm_bg.wasm.d.ts +83 -0
- package/vendor/ootle-wasm-patched/package.json +27 -0
package/dist/wallet.d.ts
ADDED
|
@@ -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
|