@chironbuilder/ootle-sdk 0.1.8 → 0.1.10

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.
@@ -1,11 +1,16 @@
1
1
  import type { IndexerProvider } from "@tari-project/ootle-indexer";
2
2
  import type { Instruction, SubstateRequirement } from "@tari-project/ootle-ts-bindings";
3
- import type { TokenBalance } from "./wallet.js";
3
+ import type { FeeType, TokenBalance } from "./wallet.js";
4
4
  export interface TransactionExecuteOpts {
5
5
  maxFee?: bigint;
6
6
  dryRun?: boolean;
7
7
  inputs?: SubstateRequirement[];
8
8
  maxRetries?: number;
9
+ /** See `FeeType`'s own doc comment. `OotleAccount` honors this; a `DaemonAccount`
10
+ * (walletd-relayed) implementation of this interface must implement its own support or
11
+ * explicitly reject `{ kind: "private" }` -- there is no default private-fee behavior a shared
12
+ * interface can provide for a backend this package doesn't talk to. */
13
+ feeType?: FeeType;
9
14
  }
10
15
  /**
11
16
  * Shared surface both `OotleAccount` (this extension's own seed-derived signer) and `DaemonAccount`
@@ -27,7 +32,7 @@ export interface WalletAccountApi {
27
32
  * requires the owner public key the wallet address encodes; a bare component address never
28
33
  * reveals it, so that form can only pay an account that already exists). Prefer asking senders
29
34
  * for the wallet address — it's the one form that always works. */
30
- send(recipientAddress: string, resourceAddress: string, amount: bigint, maxFee?: bigint): Promise<unknown>;
35
+ send(recipientAddress: string, resourceAddress: string, amount: bigint, maxFee?: bigint, feeType?: FeeType): Promise<unknown>;
31
36
  claimTestnetXtr(): Promise<unknown>;
32
37
  }
33
38
  //# sourceMappingURL=accountApi.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"accountApi.d.ts","sourceRoot":"","sources":["../src/accountApi.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AACnE,OAAO,KAAK,EAAE,WAAW,EAAE,mBAAmB,EAAE,MAAM,iCAAiC,CAAC;AACxF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD,MAAM,WAAW,sBAAsB;IACrC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,mBAAmB,EAAE,CAAC;IAC/B,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;;;GAKG;AACH,MAAM,WAAW,gBAAgB;IAC/B,mBAAmB,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;IACvC,gBAAgB,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;IACpC,WAAW,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC,CAAC;IACvC;2FACuF;IACvF,WAAW,IAAI,OAAO,CAAC,eAAe,CAAC,CAAC;IACxC,OAAO,CAAC,YAAY,EAAE,WAAW,EAAE,EAAE,IAAI,CAAC,EAAE,sBAAsB,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACtF;;;;;uEAKmE;IACnE,IAAI,CAAC,gBAAgB,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC3G,eAAe,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;CACrC"}
1
+ {"version":3,"file":"accountApi.d.ts","sourceRoot":"","sources":["../src/accountApi.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AACnE,OAAO,KAAK,EAAE,WAAW,EAAE,mBAAmB,EAAE,MAAM,iCAAiC,CAAC;AACxF,OAAO,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEzD,MAAM,WAAW,sBAAsB;IACrC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,mBAAmB,EAAE,CAAC;IAC/B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;2EAGuE;IACvE,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED;;;;;GAKG;AACH,MAAM,WAAW,gBAAgB;IAC/B,mBAAmB,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;IACvC,gBAAgB,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;IACpC,WAAW,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC,CAAC;IACvC;2FACuF;IACvF,WAAW,IAAI,OAAO,CAAC,eAAe,CAAC,CAAC;IACxC,OAAO,CAAC,YAAY,EAAE,WAAW,EAAE,EAAE,IAAI,CAAC,EAAE,sBAAsB,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACtF;;;;;uEAKmE;IACnE,IAAI,CAAC,gBAAgB,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC9H,eAAe,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;CACrC"}
package/dist/index.d.ts CHANGED
@@ -16,6 +16,6 @@ export { withTimeout } from "./timeout.js";
16
16
  export type { KeyValueStore, PendingShield, ShieldedOutputRecord } from "./storage.js";
17
17
  export { addPendingShield, addShieldedOutput, configureOotleStorage, getKnownVersions, getPrivatePaymentScanCursor, listPendingShields, listShieldedOutputs, localAccountId, markShieldedOutputSpent, removePendingShield, serialized, setKnownVersions, setPrivatePaymentScanCursor, wipeOotleState, } from "./storage.js";
18
18
  export { chromeStorageAdapter, inMemoryAdapter, localStorageAdapter } from "./adapters.js";
19
- export type { PrivateBalance, TokenBalance } from "./wallet.js";
20
- export { OotleAccount, assertValidMinimumValuePromise, buildHtlcSpendStatement, extractMissingSubstateAddress, extractStaleLockVersion, forgetKnownVersions, pollTransactionResult, recoverPendingShields, resetKnownVersions, resolveInputsWithRetry, resolveSendPrivatelyPlan, resolveUnshieldPlan, selectShieldedUtxosForAmount, selectUnspentShieldedOutputs, substateExists, summarizePrivateHoldings, synthesizeShieldedOnlyBalances, } from "./wallet.js";
19
+ export type { FeeType, PrivateBalance, TokenBalance } from "./wallet.js";
20
+ export { OotleAccount, assertValidMinimumValuePromise, buildHtlcSpendStatement, extractMissingSubstateAddress, extractStaleLockVersion, forgetKnownVersions, pollTransactionResult, recoverPendingShields, resetKnownVersions, resolveInputsWithRetry, resolveSendPrivatelyPlan, resolveUnshieldPlan, selectPrivateFeeUtxo, selectShieldedUtxosForAmount, selectUnspentShieldedOutputs, substateExists, summarizePrivateHoldings, synthesizeShieldedOnlyBalances, } from "./wallet.js";
21
21
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,YAAY,EAAE,sBAAsB,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAEhF,OAAO,EACL,wBAAwB,EACxB,iCAAiC,EACjC,6BAA6B,EAC7B,sBAAsB,GACvB,MAAM,uBAAuB,CAAC;AAE/B,YAAY,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AACrF,OAAO,EAAE,+BAA+B,EAAE,0BAA0B,EAAE,MAAM,mBAAmB,CAAC;AAEhG,YAAY,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAC1D,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAEpD,OAAO,EAAE,qBAAqB,EAAE,kBAAkB,EAAE,0BAA0B,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAC;AAEhI,YAAY,EAAE,oBAAoB,EAAE,MAAM,WAAW,CAAC;AACtD,OAAO,EAAE,4BAA4B,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAEzE,YAAY,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAEnD,OAAO,EAAE,oCAAoC,EAAE,0BAA0B,EAAE,2BAA2B,EAAE,MAAM,qBAAqB,CAAC;AAEpI,YAAY,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AACjD,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAExE,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAE3C,YAAY,EAAE,aAAa,EAAE,aAAa,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AACvF,OAAO,EACL,gBAAgB,EAChB,iBAAiB,EACjB,qBAAqB,EACrB,gBAAgB,EAChB,2BAA2B,EAC3B,kBAAkB,EAClB,mBAAmB,EACnB,cAAc,EACd,uBAAuB,EACvB,mBAAmB,EACnB,UAAU,EACV,gBAAgB,EAChB,2BAA2B,EAC3B,cAAc,GACf,MAAM,cAAc,CAAC;AAEtB,OAAO,EAAE,oBAAoB,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AAE3F,YAAY,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAChE,OAAO,EACL,YAAY,EACZ,8BAA8B,EAC9B,uBAAuB,EACvB,6BAA6B,EAC7B,uBAAuB,EACvB,mBAAmB,EACnB,qBAAqB,EACrB,qBAAqB,EACrB,kBAAkB,EAClB,sBAAsB,EACtB,wBAAwB,EACxB,mBAAmB,EACnB,4BAA4B,EAC5B,4BAA4B,EAC5B,cAAc,EACd,wBAAwB,EACxB,8BAA8B,GAC/B,MAAM,aAAa,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,YAAY,EAAE,sBAAsB,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAEhF,OAAO,EACL,wBAAwB,EACxB,iCAAiC,EACjC,6BAA6B,EAC7B,sBAAsB,GACvB,MAAM,uBAAuB,CAAC;AAE/B,YAAY,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AACrF,OAAO,EAAE,+BAA+B,EAAE,0BAA0B,EAAE,MAAM,mBAAmB,CAAC;AAEhG,YAAY,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAC1D,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAEpD,OAAO,EAAE,qBAAqB,EAAE,kBAAkB,EAAE,0BAA0B,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAC;AAEhI,YAAY,EAAE,oBAAoB,EAAE,MAAM,WAAW,CAAC;AACtD,OAAO,EAAE,4BAA4B,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAEzE,YAAY,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAEnD,OAAO,EAAE,oCAAoC,EAAE,0BAA0B,EAAE,2BAA2B,EAAE,MAAM,qBAAqB,CAAC;AAEpI,YAAY,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AACjD,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAExE,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAE3C,YAAY,EAAE,aAAa,EAAE,aAAa,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AACvF,OAAO,EACL,gBAAgB,EAChB,iBAAiB,EACjB,qBAAqB,EACrB,gBAAgB,EAChB,2BAA2B,EAC3B,kBAAkB,EAClB,mBAAmB,EACnB,cAAc,EACd,uBAAuB,EACvB,mBAAmB,EACnB,UAAU,EACV,gBAAgB,EAChB,2BAA2B,EAC3B,cAAc,GACf,MAAM,cAAc,CAAC;AAEtB,OAAO,EAAE,oBAAoB,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AAE3F,YAAY,EAAE,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AACzE,OAAO,EACL,YAAY,EACZ,8BAA8B,EAC9B,uBAAuB,EACvB,6BAA6B,EAC7B,uBAAuB,EACvB,mBAAmB,EACnB,qBAAqB,EACrB,qBAAqB,EACrB,kBAAkB,EAClB,sBAAsB,EACtB,wBAAwB,EACxB,mBAAmB,EACnB,oBAAoB,EACpB,4BAA4B,EAC5B,4BAA4B,EAC5B,cAAc,EACd,wBAAwB,EACxB,8BAA8B,GAC/B,MAAM,aAAa,CAAC"}
package/dist/index.js CHANGED
@@ -11,4 +11,4 @@ export { decryptVault, encryptVault, fromHex, toHex } from "./vault.js";
11
11
  export { withTimeout } from "./timeout.js";
12
12
  export { addPendingShield, addShieldedOutput, configureOotleStorage, getKnownVersions, getPrivatePaymentScanCursor, listPendingShields, listShieldedOutputs, localAccountId, markShieldedOutputSpent, removePendingShield, serialized, setKnownVersions, setPrivatePaymentScanCursor, wipeOotleState, } from "./storage.js";
13
13
  export { chromeStorageAdapter, inMemoryAdapter, localStorageAdapter } from "./adapters.js";
14
- export { OotleAccount, assertValidMinimumValuePromise, buildHtlcSpendStatement, extractMissingSubstateAddress, extractStaleLockVersion, forgetKnownVersions, pollTransactionResult, recoverPendingShields, resetKnownVersions, resolveInputsWithRetry, resolveSendPrivatelyPlan, resolveUnshieldPlan, selectShieldedUtxosForAmount, selectUnspentShieldedOutputs, substateExists, summarizePrivateHoldings, synthesizeShieldedOnlyBalances, } from "./wallet.js";
14
+ export { OotleAccount, assertValidMinimumValuePromise, buildHtlcSpendStatement, extractMissingSubstateAddress, extractStaleLockVersion, forgetKnownVersions, pollTransactionResult, recoverPendingShields, resetKnownVersions, resolveInputsWithRetry, resolveSendPrivatelyPlan, resolveUnshieldPlan, selectPrivateFeeUtxo, selectShieldedUtxosForAmount, selectUnspentShieldedOutputs, substateExists, summarizePrivateHoldings, synthesizeShieldedOnlyBalances, } from "./wallet.js";
package/dist/wallet.d.ts CHANGED
@@ -6,6 +6,16 @@ import type { WalletAccountApi } from "./accountApi.js";
6
6
  import type { ScannedStealthOutput } from "./confidential.js";
7
7
  import { type NetworkName } from "./ootleNetwork.js";
8
8
  import type { ShieldedOutputRecord } from "./storage.js";
9
+ /** How a transaction's fee is paid. Defaults to `transparent` everywhere (unchanged behavior:
10
+ * revealed balance, reveals the paying account on-chain). `private` instead spends a stealth
11
+ * UTXO of `feeResourceAddress` -- see `OotleAccount.buildPrivateFeeInstructions`'s doc comment
12
+ * for why this is single-UTXO only, and `selectPrivateFeeUtxo` for how one is chosen. */
13
+ export type FeeType = {
14
+ kind: "transparent";
15
+ } | {
16
+ kind: "private";
17
+ feeResourceAddress: string;
18
+ };
9
19
  export interface TokenBalance {
10
20
  resourceAddress: string;
11
21
  kind: string;
@@ -129,10 +139,21 @@ export declare class OotleAccount implements WalletAccountApi {
129
139
  dryRun?: boolean;
130
140
  inputs?: SubstateRequirement[];
131
141
  maxRetries?: number;
142
+ /**
143
+ * Defaults to `{ kind: "transparent" }` (unchanged behavior: fee paid from the account's
144
+ * revealed balance, which reveals `account` on-chain). `{ kind: "private" }` instead pays
145
+ * from a shielded UTXO of `feeResourceAddress`, auto-selected via `selectPrivateFeeUtxo`
146
+ * (smallest unspent record that alone covers `maxFee` -- shield some first if none
147
+ * qualifies) -- see `buildPrivateFeeInstructions`'s doc comment for why this is
148
+ * single-UTXO only. `dryRun` never touches local storage even when private.
149
+ */
150
+ feeType?: FeeType;
132
151
  }): Promise<{
133
152
  transaction_id: TransactionId;
134
153
  result: ExecuteResult;
135
154
  } | (IndexerGetTransactionResultResponse & {
155
+ transaction_id: TransactionId;
156
+ } & {
136
157
  transactionId?: string;
137
158
  })>;
138
159
  /**
@@ -191,10 +212,12 @@ export declare class OotleAccount implements WalletAccountApi {
191
212
  * never reveals the owner key `CreateAccount` needs, so that form can only pay an account that
192
213
  * already exists.
193
214
  */
194
- send(recipientAddress: string, resourceAddress: string, amount: bigint, maxFee?: bigint): Promise<{
215
+ send(recipientAddress: string, resourceAddress: string, amount: bigint, maxFee?: bigint, feeType?: FeeType): Promise<{
195
216
  transaction_id: TransactionId;
196
217
  result: ExecuteResult;
197
218
  } | (IndexerGetTransactionResultResponse & {
219
+ transaction_id: TransactionId;
220
+ } & {
198
221
  transactionId?: string;
199
222
  })>;
200
223
  /**
@@ -227,10 +250,12 @@ export declare class OotleAccount implements WalletAccountApi {
227
250
  * produces a bucket holding the one new confidential output, which `deposit` puts back in the
228
251
  * same vault.
229
252
  */
230
- depositConfidential(resourceAddress: string, amount: bigint, maxFee?: bigint): Promise<{
253
+ depositConfidential(resourceAddress: string, amount: bigint, maxFee?: bigint, feeType?: FeeType): Promise<{
231
254
  transaction_id: TransactionId;
232
255
  result: ExecuteResult;
233
256
  } | (IndexerGetTransactionResultResponse & {
257
+ transaction_id: TransactionId;
258
+ } & {
234
259
  transactionId?: string;
235
260
  })>;
236
261
  /**
@@ -287,7 +312,7 @@ export declare class OotleAccount implements WalletAccountApi {
287
312
  * erring high just wastes nothing; erring low burns the small fee-intent cost with nothing to
288
313
  * show for it, since only the fee half of the transaction gets committed.
289
314
  */
290
- shield(resourceAddress: string, amount: bigint, maxFee?: bigint, memo?: string, minimumValuePromise?: bigint): Promise<{
315
+ shield(resourceAddress: string, amount: bigint, maxFee?: bigint, memo?: string, minimumValuePromise?: bigint, feeType?: FeeType): Promise<{
291
316
  transactionId: string;
292
317
  commitment: string;
293
318
  substateId: string;
@@ -360,7 +385,7 @@ export declare class OotleAccount implements WalletAccountApi {
360
385
  * mark the spent ones, even if the service worker dies between finalization and the storage
361
386
  * writes.
362
387
  */
363
- unshield(resourceAddress: string, revealedOutAmount: bigint, maxFee?: bigint, memo?: string): Promise<{
388
+ unshield(resourceAddress: string, revealedOutAmount: bigint, maxFee?: bigint, memo?: string, feeType?: FeeType): Promise<{
364
389
  transactionId: string;
365
390
  }>;
366
391
  /**
@@ -390,7 +415,7 @@ export declare class OotleAccount implements WalletAccountApi {
390
415
  * substate with `SubstateNotFound`. Each is registered by address, plus every vault its own
391
416
  * state references (the same vault-discovery this account's own address already gets).
392
417
  */
393
- withdrawStealthAndExecute(resourceAddress: string, amount: bigint, workspaceVarName: string, followUpInstructions: Instruction[], relatedComponents?: string[], maxFee?: bigint): Promise<{
418
+ withdrawStealthAndExecute(resourceAddress: string, amount: bigint, workspaceVarName: string, followUpInstructions: Instruction[], relatedComponents?: string[], maxFee?: bigint, feeType?: FeeType): Promise<{
394
419
  transactionId: string;
395
420
  }>;
396
421
  /**
@@ -421,7 +446,7 @@ export declare class OotleAccount implements WalletAccountApi {
421
446
  * @param relatedComponents Every *other* component `followUpInstructions` touches — same
422
447
  * reasoning as `withdrawStealthAndExecute`'s own parameter of the same name.
423
448
  */
424
- redeemStealthOutputAndExecute(resourceAddress: string, commitmentHex: string, revealedAmount: bigint, followUpInstructions: Instruction[], relatedComponents?: string[], maxFee?: bigint): Promise<{
449
+ redeemStealthOutputAndExecute(resourceAddress: string, commitmentHex: string, revealedAmount: bigint, followUpInstructions: Instruction[], relatedComponents?: string[], maxFee?: bigint, feeType?: FeeType): Promise<{
425
450
  transactionId: string;
426
451
  }>;
427
452
  /**
@@ -467,6 +492,87 @@ export declare class OotleAccount implements WalletAccountApi {
467
492
  * (which records its own outputs locally) — callers doing several of these in sequence must
468
493
  * thread this value into the next call's `feeCommitmentHex` themselves.
469
494
  */
495
+ /**
496
+ * Builds the fee-payment instructions/inputs for spending a single stealth UTXO of
497
+ * `feeResourceAddress` to cover `maxFee`, plus the one-time `Signer` that must co-sign the
498
+ * resulting `StealthTransfer` fee spend (an ordinary account signature can't authorize a
499
+ * stealth spend -- see `redeemStealthOutputWithPrivateFee`'s doc comment, which this method's
500
+ * fee-half logic was extracted from verbatim). The unspent remainder above `maxFee` becomes a
501
+ * new stealth output back to this account (`feeChangeCommitment`); this only builds
502
+ * instructions, it doesn't touch local storage -- the caller records the change output (e.g.
503
+ * via `recordKnownShieldedOutput`) and marks `feeCommitmentHex` spent once the transaction
504
+ * actually lands, exactly as `redeemStealthOutputWithPrivateFee` itself does inline.
505
+ *
506
+ * Deliberately single-UTXO only, like the method this was extracted from -- the only shape
507
+ * confirmed live. Aggregating several stealth inputs into one `StealthTransfer` fee spend
508
+ * isn't proven here: `unshield()`'s multi-commitment coin selection uses a different,
509
+ * higher-level signing pipeline (`WalletStealthAuthorizer`) that isn't available inside this
510
+ * raw `TransactionBuilder`/`resolveTransaction`/`signTransaction` pipeline. Callers needing
511
+ * more than one stealth input's worth of fee should pick the smallest single covering UTXO,
512
+ * not sum several.
513
+ */
514
+ private buildPrivateFeeInstructions;
515
+ /**
516
+ * Wires a `buildPrivateFeeInstructions()` result into a `StealthTransfer` builder in place of
517
+ * `.payFeeFromRevealed(maxFee)` -- via the builder's `withBuilder` escape hatch, since the
518
+ * class has no first-class "pay fee from a stealth UTXO" method of its own.
519
+ */
520
+ private attachPrivateFee;
521
+ /**
522
+ * Completes signing for a `StealthTransfer`-based operation whose fee was attached via
523
+ * `attachPrivateFee`: adds the fee UTXO's one-time authorization as an extra signature
524
+ * (`AuthorizedTransfer.addSignature`) alongside whatever `WalletStealthAuthorizer` already
525
+ * produced for the operation's own stealth inputs, then seals. Bound to the same seal public
526
+ * key `authorized` itself uses, so every signature verifies against the same tx hash (see
527
+ * `AuthorizedTransfer.seal`'s doc comment).
528
+ */
529
+ private sealWithPrivateFee;
530
+ /**
531
+ * Shared bookkeeping after ANY operation (`execute()` included) whose fee was paid privately
532
+ * lands on-chain: records the fee UTXO's change output and marks the spent one, mirroring
533
+ * `shield()`/`unshield()`'s existing pattern for their own outputs. Never call this for a dry
534
+ * run or a failed submission.
535
+ */
536
+ private recordPrivateFeeSpend;
537
+ /**
538
+ * Selects and builds everything needed to pay a transaction's fee privately, or returns `null`
539
+ * for `{ kind: "transparent" }` -- the single entry point every fee-paying method (`execute()`
540
+ * and every `StealthTransfer`-based one below) uses to interpret a `FeeType` the same way.
541
+ */
542
+ private resolvePrivateFee;
543
+ /**
544
+ * Filters out unspent local records for `resourceAddress` that can't actually be read right now
545
+ * (checked largest-first, stopping once verified-readable records cover `targetAmount` -- the
546
+ * same greedy order `selectShieldedUtxosForAmount` itself uses, so this does the minimum
547
+ * verification needed rather than checking every record up front) -- used by `sendPrivately()`
548
+ * and `unshield()` before their own coin-selection plan runs, so a record that's actually
549
+ * already spent (confirmed live: the indexer can 500 instead of cleanly saying so, e.g. for one
550
+ * spent through another device/session sharing this seed) gets skipped as a *candidate* instead
551
+ * of failing the whole transaction deep inside the `StealthTransfer` builder with no way to
552
+ * retry excluding just the bad one.
553
+ *
554
+ * Deliberately never marks a failing record spent in local storage -- same reasoning as
555
+ * `resolvePrivateFee`: an unreadable-right-now record could just as easily be a transient
556
+ * indexer error as a genuinely spent output, and this wallet's only handle back to a real
557
+ * stealth output is this local record (see `ShieldedOutputRecord`'s own doc comment), so wrongly
558
+ * deleting one is unrecoverable. A record skipped here is simply not offered as a candidate for
559
+ * *this* attempt; it stays in local storage to be tried again later.
560
+ */
561
+ private filterReadableShieldedOutputs;
562
+ /**
563
+ * Shared prepare/authorize/seal/submit path for every `StealthTransfer`-based operation
564
+ * (shield/unshield/withdrawStealthAndExecute/etc.): `builder` must already have every
565
+ * operation-specific call applied (`spendRevealedInput`, `toStealthOutput`,
566
+ * `spendStealthInput`, `andThen`, ...) EXCEPT the fee -- this attaches either
567
+ * `.payFeeFromRevealed(maxFee)` or a private fee (via `resolvePrivateFee`/`attachPrivateFee`),
568
+ * then authorizes with `WalletStealthAuthorizer` (passing `authorizerOpts` through, e.g.
569
+ * `viewSecret` when the operation itself spends stealth inputs), seals (co-signing the private
570
+ * fee's UTXO when there is one), and submits -- but does NOT poll to finality or record
571
+ * anything: callers keep their own existing `addPendingShield`/poll/`recordKnownShieldedOutput`
572
+ * logic for the operation's own effect, and must call `recordPrivateFeeSpend` themselves once
573
+ * `transactionId` actually lands (never for a dry run) using the returned `privateFee`.
574
+ */
575
+ private prepareSignSubmit;
470
576
  redeemStealthOutputWithPrivateFee(resourceAddress: string, commitmentHex: string, revealedAmount: bigint, followUpInstructions: Instruction[], feeResourceAddress: string, feeCommitmentHex: string, maxFee: bigint, relatedComponents?: string[]): Promise<{
471
577
  transactionId: string;
472
578
  feeChangeCommitment: string;
@@ -501,7 +607,7 @@ export declare class OotleAccount implements WalletAccountApi {
501
607
  * other stealth payment addressed to them — spend authority is governed entirely separately, by
502
608
  * `payTo`.
503
609
  */
504
- htlcFund(resourceAddress: string, amount: bigint, claimantWalletAddress: string, hashLockHex: string, refundEpoch: bigint, maxFee?: bigint): Promise<{
610
+ htlcFund(resourceAddress: string, amount: bigint, claimantWalletAddress: string, hashLockHex: string, refundEpoch: bigint, maxFee?: bigint, feeType?: FeeType): Promise<{
505
611
  transactionId: string;
506
612
  conditions: object[];
507
613
  ownCommitment: string;
@@ -534,7 +640,7 @@ export declare class OotleAccount implements WalletAccountApi {
534
640
  * unblinded mask+value and the two witnesses below, so there is nothing left for the builder
535
641
  * pipeline to add.
536
642
  */
537
- htlcClaim(resourceAddress: string, commitmentHex: string, conditions: object[], preimageHex: string, maxFee?: bigint): Promise<{
643
+ htlcClaim(resourceAddress: string, commitmentHex: string, conditions: object[], preimageHex: string, maxFee?: bigint, feeType?: FeeType): Promise<{
538
644
  transactionId: string;
539
645
  }>;
540
646
  /**
@@ -555,7 +661,7 @@ export declare class OotleAccount implements WalletAccountApi {
555
661
  * `buildStealthTransferStatement`-in-one-call approach, same bypass of
556
662
  * `StealthTransfer`/`WalletStealthAuthorizer`.
557
663
  */
558
- htlcRefund(resourceAddress: string, commitmentHex: string, conditions: object[], amount: bigint, outputMaskHex: string, maxFee?: bigint): Promise<{
664
+ htlcRefund(resourceAddress: string, commitmentHex: string, conditions: object[], amount: bigint, outputMaskHex: string, maxFee?: bigint, feeType?: FeeType): Promise<{
559
665
  transactionId: string;
560
666
  }>;
561
667
  /**
@@ -585,7 +691,7 @@ export declare class OotleAccount implements WalletAccountApi {
585
691
  * grabbing "the first new stealth output" would risk recording the recipient's payment as our
586
692
  * own. See `PendingShield.ownCommitment`'s doc comment.
587
693
  */
588
- sendPrivately(resourceAddress: string, recipientWalletAddress: string, amount: bigint, maxFee?: bigint, memo?: string, minimumValuePromise?: bigint): Promise<{
694
+ sendPrivately(resourceAddress: string, recipientWalletAddress: string, amount: bigint, maxFee?: bigint, memo?: string, minimumValuePromise?: bigint, feeType?: FeeType): Promise<{
589
695
  transactionId: string;
590
696
  recipientCommitment: string;
591
697
  recipientSubstateId: string;
@@ -782,6 +888,14 @@ export declare function resolveUnshieldPlan(records: ShieldedOutputRecord[], res
782
888
  commitments: string[];
783
889
  remainder: bigint;
784
890
  };
891
+ /**
892
+ * Pure planning for `execute()`'s private-fee path: picks the smallest unspent stealth UTXO of
893
+ * `feeResourceAddress` that alone covers `maxFee`. Deliberately the *smallest* qualifying record
894
+ * (not the largest, and never several combined) -- `buildPrivateFeeInstructions` only supports a
895
+ * single fee input (see its own doc comment for why), so this leaves larger shielded balances
896
+ * untouched for whatever they were shielded for rather than reaching for them first.
897
+ */
898
+ export declare function selectPrivateFeeUtxo(records: ShieldedOutputRecord[], feeResourceAddress: string, maxFee: bigint, exclude?: string[]): ShieldedOutputRecord;
785
899
  /**
786
900
  * Pure planning for `sendPrivately()`: selects enough of the caller's unspent records for this
787
901
  * resource to cover `amount` — unlike `resolveUnshieldPlan`, sending the *entire* selected total
@@ -1 +1 @@
1
- {"version":3,"file":"wallet.d.ts","sourceRoot":"","sources":["../src/wallet.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"wallet.d.ts","sourceRoot":"","sources":["../src/wallet.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,OAAO,EA2BR,MAAM,qBAAqB,CAAC;AAE7B,OAAO,KAAK,EACV,aAAa,EACb,mCAAmC,EACnC,WAAW,EAIX,mBAAmB,EACnB,aAAa,EACd,MAAM,iCAAiC,CAAC;AACzC,OAAO,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAC9D,OAAO,EAAE,eAAe,EAAE,MAAM,uCAAuC,CAAC;AAYxE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAGxD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AAI9D,OAAO,EAAE,KAAK,WAAW,EAAkB,MAAM,mBAAmB,CAAC;AAcrE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AAIzD;;;yFAGyF;AACzF,MAAM,MAAM,OAAO,GAAG;IAAE,IAAI,EAAE,aAAa,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,kBAAkB,EAAE,MAAM,CAAA;CAAE,CAAC;AAahG,MAAM,WAAW,YAAY;IAC3B,eAAe,EAAE,MAAM,CAAC;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,sFAAsF;IACtF,MAAM,EAAE,MAAM,CAAC;IACf;;;wCAGoC;IACpC,kBAAkB,EAAE,MAAM,CAAC;IAC3B;;kCAE8B;IAC9B,2BAA2B,EAAE,MAAM,CAAC;IACpC;yEACqE;IACrE,YAAY,EAAE,MAAM,CAAC;IACrB;sEACkE;IAClE,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB;oDACgD;IAChD,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB;;mGAE+F;IAC/F,mBAAmB,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;CACtC;AAED;;;4CAG4C;AAC5C,MAAM,WAAW,cAAc;IAC7B,eAAe,EAAE,MAAM,CAAC;IACxB;iCAC6B;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf;gFAC4E;IAC5E,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;CACrB;AAmCD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAIH,wBAAgB,8BAA8B,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CASpF;AAoCD;;;GAGG;AACH,qBAAa,YAAa,YAAW,gBAAgB;IACnD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,eAAe,CAAC;IACjC,OAAO,CAAC,QAAQ,CAAgC;IAKhD,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAa;IAEzC,OAAO;IAOP,+FAA+F;IAC/F,MAAM,CAAC,QAAQ,CAAC,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,WAAW,GAAG,YAAY;IAOrF,WAAW,IAAI,OAAO,CAAC,eAAe,CAAC;IAqB7C;;;;;OAKG;IACG,gBAAgB,IAAI,OAAO,CAAC,MAAM,CAAC;IAInC,YAAY,IAAI,OAAO,CAAC,UAAU,CAAC;IAIzC;;;;;;;;OAQG;IACG,mBAAmB,IAAI,OAAO,CAAC,MAAM,CAAC;IAKtC,WAAW,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC;IA+I5C;;;;;;;;;;;;;;;;OAgBG;IACG,OAAO,CACX,YAAY,EAAE,WAAW,EAAE,EAC3B,IAAI,GAAE;QACJ,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,MAAM,CAAC,EAAE,OAAO,CAAC;QACjB,MAAM,CAAC,EAAE,mBAAmB,EAAE,CAAC;QAC/B,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB;;;;;;;WAOG;QACH,OAAO,CAAC,EAAE,OAAO,CAAC;KACd;wBA8IqB,aAAa;gBAAU,aAAa;;wBA4DE,aAAa;;wBAksE4B,MAAM;;IApxElH;;;;;;;;;;;;;;;;;OAiBG;YACW,YAAY;IAgD1B;;;;;;;;;;;OAWG;YACW,UAAU;IAoCxB;;;;;;;;;;;;;;;;;;;;;;;OAuBG;IACG,IAAI,CAAC,gBAAgB,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,SAAQ,EAAE,OAAO,GAAE,OAAiC;wBApH3G,aAAa;gBAAU,aAAa;;wBA4DE,aAAa;;wBAksE4B,MAAM;;IAxmElH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA6BG;IACG,mBAAmB,CAAC,eAAe,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,SAAS,EAAE,OAAO,GAAE,OAAiC;wBApLjG,aAAa;gBAAU,aAAa;;wBA4DE,aAAa;;wBAksE4B,MAAM;;IA7hElH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA6BG;IACG,eAAe,CAAC,MAAM,SAAQ,EAAE,OAAO,SAAK,EAAE,YAAY,SAAM;IA0EtE;;;;;;;;;;;;;;;;;;;;;;OAsBG;IACG,MAAM,CACV,eAAe,EAAE,MAAM,EACvB,MAAM,EAAE,MAAM,EACd,MAAM,SAAS,EACf,IAAI,CAAC,EAAE,MAAM,EACb,mBAAmB,SAAK,EACxB,OAAO,GAAE,OAAiC,GACzC,OAAO,CAAC;QAAE,aAAa,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAC;QAAC,mBAAmB,EAAE,MAAM,CAAA;KAAE,CAAC;IAkD1G;;;;;;;;;;;;;;;OAeG;IACG,kBAAkB,CACtB,eAAe,EAAE,MAAM,EACvB,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC;IA4BzE;;;;;;;;;;;;;OAaG;IACG,mBAAmB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,aAAa,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC;IAOxH;;;;;;;;;;;;;;;;;;;;;;;;;;OA0BG;IACG,QAAQ,CACZ,eAAe,EAAE,MAAM,EACvB,iBAAiB,EAAE,MAAM,EACzB,MAAM,SAAU,EAChB,IAAI,CAAC,EAAE,MAAM,EACb,OAAO,GAAE,OAAiC,GACzC,OAAO,CAAC;QAAE,aAAa,EAAE,MAAM,CAAA;KAAE,CAAC;IAqErC;;;;;;;;;;;;;;;;;;;;;;;;;;OA0BG;IACG,yBAAyB,CAC7B,eAAe,EAAE,MAAM,EACvB,MAAM,EAAE,MAAM,EACd,gBAAgB,EAAE,MAAM,EACxB,oBAAoB,EAAE,WAAW,EAAE,EACnC,iBAAiB,GAAE,MAAM,EAAO,EAChC,MAAM,SAAU,EAChB,OAAO,GAAE,OAAiC,GACzC,OAAO,CAAC;QAAE,aAAa,EAAE,MAAM,CAAA;KAAE,CAAC;IAsCrC;;;;;;;;;;;;;;;;;;;;;;;;;;;OA2BG;IACG,6BAA6B,CACjC,eAAe,EAAE,MAAM,EACvB,aAAa,EAAE,MAAM,EACrB,cAAc,EAAE,MAAM,EACtB,oBAAoB,EAAE,WAAW,EAAE,EACnC,iBAAiB,GAAE,MAAM,EAAO,EAChC,MAAM,SAAU,EAChB,OAAO,GAAE,OAAiC,GACzC,OAAO,CAAC;QAAE,aAAa,EAAE,MAAM,CAAA;KAAE,CAAC;IAmGrC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA0CG;IACH;;;;;;;;;;;;;;;;;;OAkBG;YACW,2BAA2B;IAgFzC;;;;OAIG;IACH,OAAO,CAAC,gBAAgB;IAWxB;;;;;;;OAOG;YACW,kBAAkB;IAOhC;;;;;OAKG;YACW,qBAAqB;IAKnC;;;;OAIG;YACW,iBAAiB;IAmD/B;;;;;;;;;;;;;;;;;OAiBG;YACW,6BAA6B;IAkC3C;;;;;;;;;;;;OAYG;YACW,iBAAiB;IAsBzB,iCAAiC,CACrC,eAAe,EAAE,MAAM,EACvB,aAAa,EAAE,MAAM,EACrB,cAAc,EAAE,MAAM,EACtB,oBAAoB,EAAE,WAAW,EAAE,EACnC,kBAAkB,EAAE,MAAM,EAC1B,gBAAgB,EAAE,MAAM,EACxB,MAAM,EAAE,MAAM,EACd,iBAAiB,GAAE,MAAM,EAAO,GAC/B,OAAO,CAAC;QAAE,aAAa,EAAE,MAAM,CAAC;QAAC,mBAAmB,EAAE,MAAM,CAAA;KAAE,CAAC;IAsFlE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA6BG;IACG,QAAQ,CACZ,eAAe,EAAE,MAAM,EACvB,MAAM,EAAE,MAAM,EACd,qBAAqB,EAAE,MAAM,EAC7B,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,MAAM,EACnB,MAAM,SAAS,EACf,OAAO,GAAE,OAAiC,GACzC,OAAO,CAAC;QAAE,aAAa,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,EAAE,CAAC;QAAC,aAAa,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,CAAC;IAwCtG;;;;;;;;;;;;;;;;;;;;;;;;;;OA0BG;IACG,SAAS,CACb,eAAe,EAAE,MAAM,EACvB,aAAa,EAAE,MAAM,EACrB,UAAU,EAAE,MAAM,EAAE,EACpB,WAAW,EAAE,MAAM,EACnB,MAAM,SAAS,EACf,OAAO,GAAE,OAAiC,GACzC,OAAO,CAAC;QAAE,aAAa,EAAE,MAAM,CAAA;KAAE,CAAC;IAmDrC;;;;;;;;;;;;;;;;;OAiBG;IACG,UAAU,CACd,eAAe,EAAE,MAAM,EACvB,aAAa,EAAE,MAAM,EACrB,UAAU,EAAE,MAAM,EAAE,EACpB,MAAM,EAAE,MAAM,EACd,aAAa,EAAE,MAAM,EACrB,MAAM,SAAS,EACf,OAAO,GAAE,OAAiC,GACzC,OAAO,CAAC;QAAE,aAAa,EAAE,MAAM,CAAA;KAAE,CAAC;IAuCrC;;;;;;;;;;;;;;;;;;;;;;;;;;OA0BG;IACG,aAAa,CACjB,eAAe,EAAE,MAAM,EACvB,sBAAsB,EAAE,MAAM,EAC9B,MAAM,EAAE,MAAM,EACd,MAAM,SAAU,EAChB,IAAI,CAAC,EAAE,MAAM,EACb,mBAAmB,SAAK,EACxB,OAAO,GAAE,OAAiC,GACzC,OAAO,CAAC;QAAE,aAAa,EAAE,MAAM,CAAC;QAAC,mBAAmB,EAAE,MAAM,CAAC;QAAC,mBAAmB,EAAE,MAAM,CAAC;QAAC,mBAAmB,EAAE,MAAM,CAAA;KAAE,CAAC;IA+E5H;;;;;;;;;OASG;IACG,mBAAmB,CAAC,eAAe,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAqBrH;;;;;;;;OAQG;IACG,0BAA0B,CAAC,eAAe,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,EAAE,CAAC;IAI3F;;;;;;;;;;;;;;;OAeG;IACG,kBAAkB,IAAI,OAAO,CAAC,cAAc,EAAE,CAAC;IAiBrD;;;;;;;;;;;;;;OAcG;IACG,sBAAsB,CAAC,QAAQ,CAAC,EAAE,MAAM,EAAE,QAAQ,SAAK,GAAG,OAAO,CAAC;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,oBAAoB,EAAE,CAAA;KAAE,CAAC;IA4D3H;;;;;;;;;;;;;;;;;;;;OAoBG;IACG,oBAAoB,CACxB,eAAe,EAAE,MAAM,EACvB,IAAI,GAAE;QAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,cAAc,CAAC,EAAE,MAAM,EAAE,CAAA;KAAO,GAC7F,OAAO,CAAC,oBAAoB,EAAE,CAAC;CAoFnC;AAED;;;;;;;;;GASG;AACH;;;;;;;;;;;;;GAaG;AAIH,wBAAgB,uBAAuB,CAAC,MAAM,EAAE;IAC9C,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;IACb;6DACyD;IACzD,IAAI,EAAE,UAAU,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,wBAAwB,EAAE,MAAM,CAAC;IACjC,eAAe,EAAE,MAAM,CAAC;CACzB,GAAG,MAAM,CA2BT;AAuED;;;;;;;GAOG;AACH,wBAAgB,8BAA8B,CAC5C,eAAe,EAAE,GAAG,CAAC,MAAM,CAAC,EAC5B,kBAAkB,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EACvC,sBAAsB,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EAC3C,gBAAgB,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC,EAC5C,cAAc,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC,GACzC,YAAY,EAAE,CAiBhB;AAoBD;;;;;;;;;;;GAWG;AACH;;;;;;;;GAQG;AACH,wBAAgB,4BAA4B,CAAC,OAAO,EAAE,oBAAoB,EAAE,EAAE,eAAe,CAAC,EAAE,MAAM,GAAG,oBAAoB,EAAE,CAI9H;AAED;;;;;;;;;GASG;AACH,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,oBAAoB,EAAE,GAC9B;IAAE,eAAe,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,EAAE,CASpE;AAED,wBAAgB,4BAA4B,CAC1C,OAAO,EAAE,oBAAoB,EAAE,EAC/B,eAAe,EAAE,MAAM,EACvB,YAAY,EAAE,MAAM,GACnB;IAAE,QAAQ,EAAE,oBAAoB,EAAE,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,oBAAoB,EAAE,CAAA;CAAE,CAiBzF;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,oBAAoB,EAAE,EAC/B,eAAe,EAAE,MAAM,EACvB,iBAAiB,EAAE,MAAM,GACxB;IAAE,WAAW,EAAE,MAAM,EAAE,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAmB9C;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,oBAAoB,EAAE,EAC/B,kBAAkB,EAAE,MAAM,EAC1B,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,MAAM,EAAO,GACrB,oBAAoB,CAetB;AAED;;;;;;;GAOG;AACH,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,oBAAoB,EAAE,EAC/B,eAAe,EAAE,MAAM,EACvB,MAAM,EAAE,MAAM,GACb;IAAE,WAAW,EAAE,MAAM,EAAE,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,CAOjD;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,qBAAqB,CAAC,QAAQ,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CAgCpF;AAyGD;;;;GAIG;AACH,wBAAsB,mBAAmB,CAAC,WAAW,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAM9E;AAED;;0GAE0G;AAC1G,wBAAgB,kBAAkB,IAAI,IAAI,CAEzC;AAED;;;;;;GAMG;AACH,wBAAsB,qBAAqB,CACzC,QAAQ,EAAE,eAAe,EACzB,aAAa,EAAE,MAAM,EACrB,SAAS,SAAS,GACjB,OAAO,CAAC,mCAAmC,CAAC,CAwB9C;AAmBD,wBAAsB,sBAAsB,CAC1C,QAAQ,EAAE,eAAe,EACzB,YAAY,EAAE,mBAAmB,EAAE,EACnC,OAAO,SAAI,EACX,YAAY,SAAM,GACjB,OAAO,CAAC,mBAAmB,EAAE,CAAC,CAUhC;AAED;;;;;;;;;GASG;AACH,wBAAsB,cAAc,CAAC,QAAQ,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAQpG;AAmBD,wBAAgB,6BAA6B,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAM5E;AAOD,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,MAAM,GAAG;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAGvG"}
package/dist/wallet.js CHANGED
@@ -320,6 +320,7 @@ export class OotleAccount {
320
320
  const provider = await this.getProvider();
321
321
  const account = await this.getComponentAddress();
322
322
  const maxFee = opts.maxFee ?? 5000n;
323
+ const feeType = opts.feeType ?? { kind: "transparent" };
323
324
  // Resolved once, outside the retry loop below: every transaction now carries a mandatory
324
325
  // `max_epoch`, and a fresh chain-tip lookup on every retry would buy nothing (the loop's
325
326
  // whole retry budget runs in well under an epoch in practice) at the cost of an extra
@@ -335,18 +336,34 @@ export class OotleAccount {
335
336
  const maxRetries = opts.maxRetries ?? 30;
336
337
  const seenAddresses = new Set();
337
338
  let inputs = await applyKnownVersions(opts.inputs ? [...opts.inputs] : []);
339
+ // Built once, outside the retry loop, like `maxEpoch` above: the fee stealth proof doesn't
340
+ // depend on which main-instruction substates a given attempt discovers, so redoing the
341
+ // indexer round trip + proof generation on every retry would buy nothing.
342
+ const accountId = localAccountId(this.index);
343
+ const privateFee = await this.resolvePrivateFee(feeType, maxFee);
338
344
  for (let attempt = 0;; attempt++) {
339
- const builder = TransactionBuilder.new(this.network, maxEpoch)
340
- .withInstructions(instructions)
341
- .feeTransactionPayFromComponent(account, maxFee);
345
+ const builder = TransactionBuilder.new(this.network, maxEpoch).withInstructions(instructions);
346
+ if (privateFee) {
347
+ for (const instr of privateFee.feeInstructions)
348
+ builder.addFeeInstruction(instr);
349
+ }
350
+ else {
351
+ builder.feeTransactionPayFromComponent(account, maxFee);
352
+ }
342
353
  if (inputs.length)
343
354
  builder.withInputs(inputs);
355
+ if (privateFee)
356
+ builder.withInputs(privateFee.feeInputs);
344
357
  const unsignedTx = builder.buildUnsignedTransaction();
345
358
  try {
359
+ const extraSigners = privateFee ? [privateFee.feeSigner] : [];
346
360
  if (opts.dryRun)
347
- return await withTimeout(this.submitDryRun(provider, unsignedTx), 30_000, "submitting the transaction");
348
- const result = await withTimeout(this.submitReal(provider, unsignedTx), 60_000, "submitting the transaction");
361
+ return await withTimeout(this.submitDryRun(provider, unsignedTx, extraSigners), 30_000, "submitting the transaction");
362
+ const result = await withTimeout(this.submitReal(provider, unsignedTx, extraSigners), 60_000, "submitting the transaction");
349
363
  await recordKnownVersions(result);
364
+ if (privateFee && feeType.kind === "private") {
365
+ await this.recordPrivateFeeSpend(accountId, feeType.feeResourceAddress, privateFee, String(result.transaction_id));
366
+ }
350
367
  return withTransactionId(result);
351
368
  }
352
369
  catch (e) {
@@ -431,9 +448,9 @@ export class OotleAccount {
431
448
  * (only the real-submission response, which no longer carries a `result` field at all as of
432
449
  * the 0.39.0 protocol upgrade).
433
450
  */
434
- async submitDryRun(provider, unsignedTx) {
451
+ async submitDryRun(provider, unsignedTx, extraSigners = []) {
435
452
  const resolved = await resolveTransaction(provider, { ...unsignedTx, dry_run: true });
436
- const signed = await signTransaction([this.signer], resolved);
453
+ const signed = await signTransaction([this.signer, ...extraSigners], resolved);
437
454
  const envelope = sealTransaction(signed);
438
455
  const res = await fetch(`${defaultIndexerUrl(this.network)}/transactions/dry-run`, {
439
456
  method: "POST",
@@ -486,7 +503,7 @@ export class OotleAccount {
486
503
  * proven correct; only the result-polling and error-message construction are reimplemented, in
487
504
  * the same detailed shape `submitDryRun()` above already produces.
488
505
  */
489
- async submitReal(provider, unsignedTx) {
506
+ async submitReal(provider, unsignedTx, extraSigners = []) {
490
507
  const resolved = await resolveTransaction(provider, unsignedTx);
491
508
  // Both of these parse the transaction inside the wasm as an untagged enum
492
509
  // (`ootle_wasm/core/src/transaction.rs`), so a single bad field anywhere collapses into "data
@@ -497,7 +514,7 @@ export class OotleAccount {
497
514
  // is attached here rather than guessed at afterwards.
498
515
  let signed;
499
516
  try {
500
- signed = await signTransaction([this.signer], resolved);
517
+ signed = await signTransaction([this.signer, ...extraSigners], resolved);
501
518
  }
502
519
  catch (e) {
503
520
  throw new Error(`${e instanceof Error ? e.message : String(e)} — at signTransaction; resolved tx: ${describeResolvedTx(resolved)}`, { cause: e });
@@ -510,7 +527,8 @@ export class OotleAccount {
510
527
  throw new Error(`${e instanceof Error ? e.message : String(e)} — at sealTransaction; signed: ${describeSignedTx(signed)}`, { cause: e });
511
528
  }
512
529
  const { transaction_id } = await provider.submitTransaction(envelope);
513
- return pollTransactionResult(provider, transaction_id);
530
+ const result = await pollTransactionResult(provider, transaction_id);
531
+ return { ...result, transaction_id };
514
532
  }
515
533
  /**
516
534
  * Transfers `amount` (raw, resource-native units) of `resourceAddress` from this account to
@@ -536,7 +554,7 @@ export class OotleAccount {
536
554
  * never reveals the owner key `CreateAccount` needs, so that form can only pay an account that
537
555
  * already exists.
538
556
  */
539
- async send(recipientAddress, resourceAddress, amount, maxFee = 5000n) {
557
+ async send(recipientAddress, resourceAddress, amount, maxFee = 5000n, feeType = { kind: "transparent" }) {
540
558
  const account = await this.getComponentAddress();
541
559
  const isWalletAddress = recipientAddress.startsWith("otl");
542
560
  const recipient = isWalletAddress ? componentAddressFromWalletAddress(recipientAddress) : recipientAddress;
@@ -562,7 +580,7 @@ export class OotleAccount {
562
580
  }
563
581
  }
564
582
  instructions.push({ CallMethod: { call: { Address: account }, method: "withdraw", args: [resourceAddressLiteral(resourceAddress), amountLiteral(amount)] } }, { PutLastInstructionOutputOnWorkspace: { key: 0 } }, { CallMethod: { call: { Address: recipient }, method: "deposit", args: [{ Workspace: { id: 0, offset: null } }] } });
565
- return this.execute(instructions, { maxFee });
583
+ return this.execute(instructions, { maxFee, feeType });
566
584
  }
567
585
  /**
568
586
  * Moves `amount` of this account's own REVEALED balance into a Confidential-type vault for the
@@ -594,7 +612,7 @@ export class OotleAccount {
594
612
  * produces a bucket holding the one new confidential output, which `deposit` puts back in the
595
613
  * same vault.
596
614
  */
597
- async depositConfidential(resourceAddress, amount, maxFee = 50000n) {
615
+ async depositConfidential(resourceAddress, amount, maxFee = 50000n, feeType = { kind: "transparent" }) {
598
616
  if (amount <= 0n)
599
617
  throw new Error(`depositConfidential: amount must be greater than zero, got ${amount}`);
600
618
  const account = await this.getComponentAddress();
@@ -621,7 +639,7 @@ export class OotleAccount {
621
639
  { PutLastInstructionOutputOnWorkspace: { key: 0 } },
622
640
  { CallMethod: { call: { Address: account }, method: "deposit", args: [{ Workspace: { id: 0, offset: null } }] } },
623
641
  ];
624
- return this.execute(instructions, { maxFee });
642
+ return this.execute(instructions, { maxFee, feeType });
625
643
  }
626
644
  /**
627
645
  * Claims free testnet XTR from the network's builtin faucet (esmeralda/igor only — there is no
@@ -739,7 +757,7 @@ export class OotleAccount {
739
757
  * erring high just wastes nothing; erring low burns the small fee-intent cost with nothing to
740
758
  * show for it, since only the fee half of the transaction gets committed.
741
759
  */
742
- async shield(resourceAddress, amount, maxFee = 50000n, memo, minimumValuePromise = 0n) {
760
+ async shield(resourceAddress, amount, maxFee = 50000n, memo, minimumValuePromise = 0n, feeType = { kind: "transparent" }) {
743
761
  assertValidMinimumValuePromise(minimumValuePromise, amount);
744
762
  const accountId = localAccountId(this.index);
745
763
  const provider = await this.getProvider();
@@ -748,7 +766,7 @@ export class OotleAccount {
748
766
  // on-chain component address -- passing `account` here throws "Bech32 decode error" (confirmed
749
767
  // live). See getWalletAddress()'s doc comment for why these two addresses are easy to conflate.
750
768
  const walletAddress = await this.getWalletAddress();
751
- const spec = await new StealthTransfer(provider, resourceAddress)
769
+ const builder = new StealthTransfer(provider, resourceAddress)
752
770
  // StealthTransfer.prepare() auto-adds the revealed account's own substate and any vault
753
771
  // addresses embedded in its on-chain state, but never the resource's own substate -- fine
754
772
  // for XTR (resource_0101...0101 is engine-special-cased and needs no lock), but any other
@@ -757,24 +775,25 @@ export class OotleAccount {
757
775
  // source (node_modules/@tari-project/ootle/dist/index.js) -- not assumed from the docs.
758
776
  .withBuilder((b) => b.addInput({ substate_id: resourceAddress, version: null }))
759
777
  .spendRevealedInput(account, amount)
760
- .toStealthOutput(createOutput({ destination: walletAddress, amount, resourceAddress, memo: toMemo(memo), minimumValuePromise }))
761
- .payFeeFromRevealed(maxFee)
762
- .prepare();
778
+ .toStealthOutput(createOutput({ destination: walletAddress, amount, resourceAddress, memo: toMemo(memo), minimumValuePromise }));
779
+ // No stealth inputs to unblind for a shield's own transfer (revealed-only source), so no
780
+ // viewSecret needed here even when the FEE is paid privately -- `prepareSignSubmit` handles
781
+ // that UTXO's own decryption internally. `fromSpec`'s own default crypto is
782
+ // `new WasmStealthCrypto()` -- Network.LocalNet, NOT this account's real network -- which
783
+ // produced an "Invalid transaction signature" server-side rejection (confirmed live) since
784
+ // the balance proof it computes is network-domain-separated. Must match the network
785
+ // StealthTransfer itself used when it built the outputs statement (`prepareSignSubmit`
786
+ // already passes `this.network` for exactly this reason).
787
+ const { transactionId, spec, privateFee } = await this.prepareSignSubmit(builder, provider, account, maxFee, feeType);
763
788
  const ownCommitment = extractOutputCommitment(spec, 0);
764
- const wallet = new OotleWallet().registerKeyProvider(account, this.signer).setDefaultSigner(account);
765
- // No stealth inputs to unblind for a shield (revealed-only source), so no viewSecret needed.
766
- // `fromSpec`'s own default crypto is `new WasmStealthCrypto()` -- Network.LocalNet, NOT this
767
- // account's real network -- which produced an "Invalid transaction signature" server-side
768
- // rejection (confirmed live) since the balance proof it computes is network-domain-separated.
769
- // Must match the network StealthTransfer itself used when it built the outputs statement.
770
- const authorized = await WalletStealthAuthorizer.fromSpec(wallet, spec, { crypto: new WasmStealthCrypto(this.network) }).prepare(provider);
771
- const envelope = await authorized.seal();
772
- const transactionId = await submitTransaction(provider, envelope);
773
789
  await addPendingShield({ transactionId, accountId, resourceAddress, amount: amount.toString(), ownCommitment, memo });
774
790
  try {
775
791
  const response = await withTimeout(pollTransactionResult(provider, transactionId), 60_000, "submitting the shield transaction");
776
792
  await recordKnownVersions(response);
777
793
  await recordKnownShieldedOutput(accountId, resourceAddress, ownCommitment, amount, transactionId, memo);
794
+ if (privateFee && feeType.kind === "private") {
795
+ await this.recordPrivateFeeSpend(accountId, feeType.feeResourceAddress, privateFee, transactionId);
796
+ }
778
797
  }
779
798
  finally {
780
799
  await removePendingShield(transactionId);
@@ -876,9 +895,9 @@ export class OotleAccount {
876
895
  * mark the spent ones, even if the service worker dies between finalization and the storage
877
896
  * writes.
878
897
  */
879
- async unshield(resourceAddress, revealedOutAmount, maxFee = 100000n, memo) {
898
+ async unshield(resourceAddress, revealedOutAmount, maxFee = 100000n, memo, feeType = { kind: "transparent" }) {
880
899
  const accountId = localAccountId(this.index);
881
- const records = await listShieldedOutputs(accountId);
900
+ const records = await this.filterReadableShieldedOutputs(await listShieldedOutputs(accountId), resourceAddress, revealedOutAmount);
882
901
  const { commitments, remainder } = resolveUnshieldPlan(records, resourceAddress, revealedOutAmount);
883
902
  const dust = 1n;
884
903
  const provider = await this.getProvider();
@@ -894,18 +913,16 @@ export class OotleAccount {
894
913
  for (const commitment of commitments) {
895
914
  builder = builder.spendStealthInput(account, fromHex(commitment));
896
915
  }
897
- const spec = await builder
916
+ builder = builder
898
917
  .toStealthOutput(createOutput({ destination: walletAddress, amount: remainder, resourceAddress, memo: toMemo(memo) }))
899
- .toRevealedOutput(revealedOutAmount + dust)
900
- .payFeeFromRevealed(maxFee)
901
- .prepare();
902
- const ownCommitment = extractOutputCommitment(spec, 0);
903
- const wallet = new OotleWallet().registerKeyProvider(account, this.signer).setDefaultSigner(account);
918
+ .toRevealedOutput(revealedOutAmount + dust);
904
919
  const viewSecret = await this.signer.getViewSecret();
905
- // See shield()'s comment: must pass this account's real network, not fromSpec's LocalNet default.
906
- const authorized = await WalletStealthAuthorizer.fromSpec(wallet, spec, { viewSecret, crypto: new WasmStealthCrypto(this.network) }).prepare(provider);
907
- const envelope = await authorized.seal();
908
- const transactionId = await submitTransaction(provider, envelope);
920
+ // See shield()'s comment: must pass this account's real network, not fromSpec's LocalNet
921
+ // default. `commitments` is excluded from the fee UTXO's own selection so the same shielded
922
+ // record can never be spent twice in one transaction (as both a main input here and the fee
923
+ // input) when `resourceAddress` and the fee's resource happen to be the same currency.
924
+ const { transactionId, spec, privateFee } = await this.prepareSignSubmit(builder, provider, account, maxFee, feeType, { viewSecret }, commitments);
925
+ const ownCommitment = extractOutputCommitment(spec, 0);
909
926
  await addPendingShield({
910
927
  transactionId,
911
928
  accountId,
@@ -922,6 +939,9 @@ export class OotleAccount {
922
939
  for (const commitment of commitments) {
923
940
  await markShieldedOutputSpent(accountId, commitment);
924
941
  }
942
+ if (privateFee && feeType.kind === "private") {
943
+ await this.recordPrivateFeeSpend(accountId, feeType.feeResourceAddress, privateFee, transactionId);
944
+ }
925
945
  }
926
946
  finally {
927
947
  await removePendingShield(transactionId);
@@ -955,7 +975,7 @@ export class OotleAccount {
955
975
  * substate with `SubstateNotFound`. Each is registered by address, plus every vault its own
956
976
  * state references (the same vault-discovery this account's own address already gets).
957
977
  */
958
- async withdrawStealthAndExecute(resourceAddress, amount, workspaceVarName, followUpInstructions, relatedComponents = [], maxFee = 100000n) {
978
+ async withdrawStealthAndExecute(resourceAddress, amount, workspaceVarName, followUpInstructions, relatedComponents = [], maxFee = 100000n, feeType = { kind: "transparent" }) {
959
979
  if (amount <= 0n)
960
980
  throw new Error(`withdrawStealthAndExecute amount must be > 0, got ${amount}`);
961
981
  const provider = await this.getProvider();
@@ -983,14 +1003,13 @@ export class OotleAccount {
983
1003
  builder = builder.withBuilder((b) => b.addInput({ substate_id: vaultId, version: null }));
984
1004
  }
985
1005
  }
986
- const spec = await builder.payFeeFromRevealed(maxFee).prepare();
987
- const wallet = new OotleWallet().registerKeyProvider(account, this.signer).setDefaultSigner(account);
988
1006
  const viewSecret = await this.signer.getViewSecret();
989
- const authorized = await WalletStealthAuthorizer.fromSpec(wallet, spec, { viewSecret, crypto: new WasmStealthCrypto(this.network) }).prepare(provider);
990
- const envelope = await authorized.seal();
991
- const transactionId = await submitTransaction(provider, envelope);
1007
+ const { transactionId, privateFee } = await this.prepareSignSubmit(builder, provider, account, maxFee, feeType, { viewSecret });
992
1008
  const response = await withTimeout(pollTransactionResult(provider, transactionId), 60_000, "submitting the transaction");
993
1009
  await recordKnownVersions(response);
1010
+ if (privateFee && feeType.kind === "private") {
1011
+ await this.recordPrivateFeeSpend(localAccountId(this.index), feeType.feeResourceAddress, privateFee, transactionId);
1012
+ }
994
1013
  return { transactionId };
995
1014
  }
996
1015
  /**
@@ -1021,7 +1040,7 @@ export class OotleAccount {
1021
1040
  * @param relatedComponents Every *other* component `followUpInstructions` touches — same
1022
1041
  * reasoning as `withdrawStealthAndExecute`'s own parameter of the same name.
1023
1042
  */
1024
- async redeemStealthOutputAndExecute(resourceAddress, commitmentHex, revealedAmount, followUpInstructions, relatedComponents = [], maxFee = 100000n) {
1043
+ async redeemStealthOutputAndExecute(resourceAddress, commitmentHex, revealedAmount, followUpInstructions, relatedComponents = [], maxFee = 100000n, feeType = { kind: "transparent" }) {
1025
1044
  if (revealedAmount <= 0n) {
1026
1045
  throw new Error(`redeemStealthOutputAndExecute: revealedAmount must be > 0, got ${revealedAmount}`);
1027
1046
  }
@@ -1046,6 +1065,7 @@ export class OotleAccount {
1046
1065
  agg_range_proof: "",
1047
1066
  });
1048
1067
  const statement = new StealthTransferStatement(new StealthInputsStatement([], 0n, inputsJson), new StealthOutputsStatement(outputsJson));
1068
+ const privateFee = await this.resolvePrivateFee(feeType, maxFee);
1049
1069
  const maxEpoch = await resolveMaxEpoch(provider);
1050
1070
  const builder = TransactionBuilder.new(this.network, maxEpoch)
1051
1071
  .addInstruction({
@@ -1060,11 +1080,22 @@ export class OotleAccount {
1060
1080
  // (name resolution only applies to instructions built through *this* same chain; raw
1061
1081
  // pre-built instructions bypass it, same constraint `withdrawStealthAndExecute` documents).
1062
1082
  .saveVar("redeemed")
1063
- .withInstructions(followUpInstructions)
1064
- .feeTransactionPayFromComponent(account, maxFee)
1083
+ .withInstructions(followUpInstructions);
1084
+ if (privateFee) {
1085
+ for (const instr of privateFee.feeInstructions)
1086
+ builder.addFeeInstruction(instr);
1087
+ }
1088
+ else {
1089
+ builder.feeTransactionPayFromComponent(account, maxFee);
1090
+ }
1091
+ builder
1065
1092
  .addInput({ substate_id: resourceAddress, version: null })
1066
1093
  .addInput({ substate_id: stealthUtxoSubstateId(resourceAddress, commitment), version: null })
1067
1094
  .addInput({ substate_id: account, version: null });
1095
+ if (privateFee) {
1096
+ for (const input of privateFee.feeInputs)
1097
+ builder.addInput(input);
1098
+ }
1068
1099
  for (const vaultId of await getVaultIdsForAccount(provider, account)) {
1069
1100
  builder.addInput({ substate_id: vaultId, version: null });
1070
1101
  }
@@ -1092,10 +1123,13 @@ export class OotleAccount {
1092
1123
  const wallet = new OotleWallet().registerKeyProvider(account, this.signer).setDefaultSigner(account);
1093
1124
  const viewSecret = await this.signer.getViewSecret();
1094
1125
  const authorized = await WalletStealthAuthorizer.fromSpec(wallet, spec, { viewSecret, crypto: new WasmStealthCrypto(this.network) }).prepare(provider);
1095
- const envelope = await authorized.seal();
1126
+ const envelope = privateFee ? await this.sealWithPrivateFee(authorized, privateFee.feeSigner) : await authorized.seal();
1096
1127
  const transactionId = await submitTransaction(provider, envelope);
1097
1128
  const response = await withTimeout(pollTransactionResult(provider, transactionId), 60_000, "submitting the redemption transaction");
1098
1129
  await recordKnownVersions(response);
1130
+ if (privateFee && feeType.kind === "private") {
1131
+ await this.recordPrivateFeeSpend(localAccountId(this.index), feeType.feeResourceAddress, privateFee, transactionId);
1132
+ }
1099
1133
  return { transactionId };
1100
1134
  }
1101
1135
  /**
@@ -1141,49 +1175,275 @@ export class OotleAccount {
1141
1175
  * (which records its own outputs locally) — callers doing several of these in sequence must
1142
1176
  * thread this value into the next call's `feeCommitmentHex` themselves.
1143
1177
  */
1178
+ /**
1179
+ * Builds the fee-payment instructions/inputs for spending a single stealth UTXO of
1180
+ * `feeResourceAddress` to cover `maxFee`, plus the one-time `Signer` that must co-sign the
1181
+ * resulting `StealthTransfer` fee spend (an ordinary account signature can't authorize a
1182
+ * stealth spend -- see `redeemStealthOutputWithPrivateFee`'s doc comment, which this method's
1183
+ * fee-half logic was extracted from verbatim). The unspent remainder above `maxFee` becomes a
1184
+ * new stealth output back to this account (`feeChangeCommitment`); this only builds
1185
+ * instructions, it doesn't touch local storage -- the caller records the change output (e.g.
1186
+ * via `recordKnownShieldedOutput`) and marks `feeCommitmentHex` spent once the transaction
1187
+ * actually lands, exactly as `redeemStealthOutputWithPrivateFee` itself does inline.
1188
+ *
1189
+ * Deliberately single-UTXO only, like the method this was extracted from -- the only shape
1190
+ * confirmed live. Aggregating several stealth inputs into one `StealthTransfer` fee spend
1191
+ * isn't proven here: `unshield()`'s multi-commitment coin selection uses a different,
1192
+ * higher-level signing pipeline (`WalletStealthAuthorizer`) that isn't available inside this
1193
+ * raw `TransactionBuilder`/`resolveTransaction`/`signTransaction` pipeline. Callers needing
1194
+ * more than one stealth input's worth of fee should pick the smallest single covering UTXO,
1195
+ * not sum several.
1196
+ */
1197
+ async buildPrivateFeeInstructions(feeResourceAddress, feeCommitmentHex, maxFee) {
1198
+ if (maxFee <= 0n)
1199
+ throw new Error(`buildPrivateFeeInstructions: maxFee must be > 0, got ${maxFee}`);
1200
+ const provider = await this.getProvider();
1201
+ const crypto = new WasmStealthCrypto(this.network);
1202
+ const walletAddress = await this.getWalletAddress();
1203
+ const viewSecret = await this.signer.getViewSecret();
1204
+ const commitment = fromHex(feeCommitmentHex);
1205
+ const substateId = stealthUtxoSubstateId(feeResourceAddress, commitment);
1206
+ const substate = await provider.getSubstate(substateId);
1207
+ const decrypted = await decryptOwnedUtxo(crypto, viewSecret, substate, substateId);
1208
+ if (!decrypted) {
1209
+ throw new Error(`buildPrivateFeeInstructions: cannot decrypt ${substateId} -- it doesn't belong to this account, or is already spent.`);
1210
+ }
1211
+ if (decrypted.value <= maxFee) {
1212
+ throw new Error(`buildPrivateFeeInstructions: fee UTXO (${decrypted.value}) is too small to cover maxFee (${maxFee})`);
1213
+ }
1214
+ const feeChange = decrypted.value - maxFee;
1215
+ const inputSkeleton = JSON.stringify({ inputs: [{ commitment: feeCommitmentHex, witness: "KeyPath" }], revealed_amount: "0" });
1216
+ const feeChangeOutput = createOutput({ destination: walletAddress, amount: feeChange, resourceAddress: feeResourceAddress });
1217
+ const { statement: feeOutputsStatement, outputMask: feeOutputMask } = await crypto.generateOutputsStatement([feeChangeOutput], maxFee);
1218
+ const feeInputsStatement = new StealthInputsStatement([], 0n, inputSkeleton);
1219
+ const feeAggInputMask = await crypto.aggregateInputMasks([decrypted.mask]);
1220
+ const feeBalanceProof = await crypto.generateBalanceProofSignature(feeAggInputMask, feeOutputMask, feeInputsStatement.statementJson, feeOutputsStatement.statementJson);
1221
+ const feeStatement = new StealthTransferStatement(feeInputsStatement, feeOutputsStatement, feeBalanceProof);
1222
+ await crypto.validateTransfer(feeStatement);
1223
+ const feeChangeCommitment = feeOutputsStatement.parsed().outputs[0].output.commitment;
1224
+ const feeInstructions = [
1225
+ {
1226
+ StealthTransfer: {
1227
+ resource_address_ref: { Address: feeResourceAddress },
1228
+ statement: { __ootleRawJson: feeStatement.toCompactJson() },
1229
+ revealed_input_bucket: null,
1230
+ },
1231
+ },
1232
+ { PutLastInstructionOutputOnWorkspace: { key: 0 } },
1233
+ { PayFeeFromBucket: { bucket: { id: 0, offset: null } } },
1234
+ ];
1235
+ const feeInputs = [
1236
+ { substate_id: feeResourceAddress, version: null },
1237
+ { substate_id: substateId, version: null },
1238
+ ];
1239
+ // The fee input's own sender_public_nonce lives on its UTXO substate -- fetched again here
1240
+ // (decryptOwnedUtxo above didn't return it) rather than parsed out of the substate response
1241
+ // type, whose shape `decryptOwnedUtxo` already validated once. Same convention
1242
+ // `redeemStealthOutputWithPrivateFee` used before this was extracted.
1243
+ const feeSubstate = await provider.getSubstate(substateId);
1244
+ const feeNonce = feeSubstate.substate.Utxo.output
1245
+ .output.public_nonce;
1246
+ const feeSigner = {
1247
+ getAddress: async () => walletAddress,
1248
+ getPublicKey: async () => parseOotleAddress(walletAddress).owner_key,
1249
+ signTransaction: async (tx, sealPublicKey) => {
1250
+ const json = serializeUnsignedTx(tx);
1251
+ const sig = await this.signer.addStealthSignature(json, fromHex(feeNonce), sealPublicKey, { crypto });
1252
+ return [sig];
1253
+ },
1254
+ };
1255
+ return { feeInstructions, feeInputs, feeChangeCommitment, feeChangeAmount: feeChange, feeSigner };
1256
+ }
1257
+ /**
1258
+ * Wires a `buildPrivateFeeInstructions()` result into a `StealthTransfer` builder in place of
1259
+ * `.payFeeFromRevealed(maxFee)` -- via the builder's `withBuilder` escape hatch, since the
1260
+ * class has no first-class "pay fee from a stealth UTXO" method of its own.
1261
+ */
1262
+ attachPrivateFee(builder, fee) {
1263
+ return builder.withBuilder((b) => {
1264
+ for (const instr of fee.feeInstructions)
1265
+ b.addFeeInstruction(instr);
1266
+ for (const input of fee.feeInputs)
1267
+ b.addInput(input);
1268
+ return b;
1269
+ });
1270
+ }
1271
+ /**
1272
+ * Completes signing for a `StealthTransfer`-based operation whose fee was attached via
1273
+ * `attachPrivateFee`: adds the fee UTXO's one-time authorization as an extra signature
1274
+ * (`AuthorizedTransfer.addSignature`) alongside whatever `WalletStealthAuthorizer` already
1275
+ * produced for the operation's own stealth inputs, then seals. Bound to the same seal public
1276
+ * key `authorized` itself uses, so every signature verifies against the same tx hash (see
1277
+ * `AuthorizedTransfer.seal`'s doc comment).
1278
+ */
1279
+ async sealWithPrivateFee(authorized, feeSigner) {
1280
+ const sealPublicKey = authorized.getSealPublicKey();
1281
+ const signatures = await feeSigner.signTransaction(authorized.getSpec().unsignedTx, sealPublicKey);
1282
+ for (const signature of signatures)
1283
+ authorized.addSignature(signature);
1284
+ return authorized.seal();
1285
+ }
1286
+ /**
1287
+ * Shared bookkeeping after ANY operation (`execute()` included) whose fee was paid privately
1288
+ * lands on-chain: records the fee UTXO's change output and marks the spent one, mirroring
1289
+ * `shield()`/`unshield()`'s existing pattern for their own outputs. Never call this for a dry
1290
+ * run or a failed submission.
1291
+ */
1292
+ async recordPrivateFeeSpend(accountId, feeResourceAddress, fee, transactionId) {
1293
+ await recordKnownShieldedOutput(accountId, feeResourceAddress, fee.feeChangeCommitment, fee.feeChangeAmount, transactionId);
1294
+ await markShieldedOutputSpent(accountId, fee.spentCommitment);
1295
+ }
1296
+ /**
1297
+ * Selects and builds everything needed to pay a transaction's fee privately, or returns `null`
1298
+ * for `{ kind: "transparent" }` -- the single entry point every fee-paying method (`execute()`
1299
+ * and every `StealthTransfer`-based one below) uses to interpret a `FeeType` the same way.
1300
+ */
1301
+ async resolvePrivateFee(feeType, maxFee,
1302
+ /** Commitments already claimed by the operation's own main effect (e.g. `unshield()`'s or
1303
+ * `sendPrivately()`'s own multi-UTXO coin selection) -- excluded so the same commitment can
1304
+ * never be selected as both a main input and the fee input in one transaction. */
1305
+ exclude = []) {
1306
+ if (feeType.kind !== "private")
1307
+ return null;
1308
+ const accountId = localAccountId(this.index);
1309
+ const records = await listShieldedOutputs(accountId);
1310
+ // Retries with the next-smallest qualifying UTXO if one fails to read -- confirmed live that a
1311
+ // record this wallet still has as "unspent" can 500 on the indexer (consistent with it having
1312
+ // actually been spent already, e.g. from another device/session sharing this seed, hitting an
1313
+ // indexer bug serving a since-spent substate) rather than a clean not-found. Deliberately does
1314
+ // NOT mark a failing candidate spent here: a 500 doesn't distinguish "genuinely spent" from "the
1315
+ // indexer had a bad moment," and this wallet's only handle back to a real stealth output is this
1316
+ // local record (see `ShieldedOutputRecord`'s own doc comment) -- wrongly deleting one on an
1317
+ // ambiguous error is unrecoverable, so a failing candidate is only skipped for *this* attempt,
1318
+ // tried again next time. Bounded by the exclude set strictly growing each iteration.
1319
+ const tried = new Set(exclude);
1320
+ let lastReadError;
1321
+ for (;;) {
1322
+ let chosen;
1323
+ try {
1324
+ chosen = selectPrivateFeeUtxo(records, feeType.feeResourceAddress, maxFee, [...tried]);
1325
+ }
1326
+ catch (selectError) {
1327
+ // Distinguishes "never had a large-enough candidate" (selectPrivateFeeUtxo's own clear
1328
+ // error) from "had one or more, but every single one failed to read" -- the latter needs
1329
+ // its own message naming the last read failure, or it looks identical to simply not having
1330
+ // enough shielded balance.
1331
+ if (lastReadError) {
1332
+ throw new Error(`Every shielded ${feeType.feeResourceAddress} UTXO large enough to cover a private fee of ${maxFee} failed to read from the network (last error: ${lastReadError instanceof Error ? lastReadError.message : String(lastReadError)}) -- one or more may already be spent, e.g. from another device or session sharing this seed.`, { cause: lastReadError });
1333
+ }
1334
+ throw selectError;
1335
+ }
1336
+ try {
1337
+ const built = await this.buildPrivateFeeInstructions(feeType.feeResourceAddress, chosen.commitment, maxFee);
1338
+ return { ...built, spentCommitment: chosen.commitment };
1339
+ }
1340
+ catch (e) {
1341
+ lastReadError = e;
1342
+ tried.add(chosen.commitment);
1343
+ }
1344
+ }
1345
+ }
1346
+ /**
1347
+ * Filters out unspent local records for `resourceAddress` that can't actually be read right now
1348
+ * (checked largest-first, stopping once verified-readable records cover `targetAmount` -- the
1349
+ * same greedy order `selectShieldedUtxosForAmount` itself uses, so this does the minimum
1350
+ * verification needed rather than checking every record up front) -- used by `sendPrivately()`
1351
+ * and `unshield()` before their own coin-selection plan runs, so a record that's actually
1352
+ * already spent (confirmed live: the indexer can 500 instead of cleanly saying so, e.g. for one
1353
+ * spent through another device/session sharing this seed) gets skipped as a *candidate* instead
1354
+ * of failing the whole transaction deep inside the `StealthTransfer` builder with no way to
1355
+ * retry excluding just the bad one.
1356
+ *
1357
+ * Deliberately never marks a failing record spent in local storage -- same reasoning as
1358
+ * `resolvePrivateFee`: an unreadable-right-now record could just as easily be a transient
1359
+ * indexer error as a genuinely spent output, and this wallet's only handle back to a real
1360
+ * stealth output is this local record (see `ShieldedOutputRecord`'s own doc comment), so wrongly
1361
+ * deleting one is unrecoverable. A record skipped here is simply not offered as a candidate for
1362
+ * *this* attempt; it stays in local storage to be tried again later.
1363
+ */
1364
+ async filterReadableShieldedOutputs(records, resourceAddress, targetAmount) {
1365
+ const provider = await this.getProvider();
1366
+ const crypto = new WasmStealthCrypto(this.network);
1367
+ const viewSecret = await this.signer.getViewSecret();
1368
+ const candidates = records
1369
+ .filter((r) => r.resourceAddress === resourceAddress && !r.spent)
1370
+ .sort((a, b) => {
1371
+ const diff = BigInt(b.amount) - BigInt(a.amount);
1372
+ return diff > 0n ? 1 : diff < 0n ? -1 : 0;
1373
+ });
1374
+ const unreadable = new Set();
1375
+ let verifiedTotal = 0n;
1376
+ for (const record of candidates) {
1377
+ if (verifiedTotal >= targetAmount)
1378
+ break;
1379
+ try {
1380
+ const substateId = stealthUtxoSubstateId(resourceAddress, fromHex(record.commitment));
1381
+ const substate = await provider.getSubstate(substateId);
1382
+ const decrypted = await decryptOwnedUtxo(crypto, viewSecret, substate, substateId);
1383
+ if (!decrypted) {
1384
+ unreadable.add(record.commitment); // doesn't decrypt as ours -- don't offer it either
1385
+ continue;
1386
+ }
1387
+ verifiedTotal += BigInt(record.amount);
1388
+ }
1389
+ catch {
1390
+ unreadable.add(record.commitment);
1391
+ }
1392
+ }
1393
+ return records.filter((r) => !unreadable.has(r.commitment));
1394
+ }
1395
+ /**
1396
+ * Shared prepare/authorize/seal/submit path for every `StealthTransfer`-based operation
1397
+ * (shield/unshield/withdrawStealthAndExecute/etc.): `builder` must already have every
1398
+ * operation-specific call applied (`spendRevealedInput`, `toStealthOutput`,
1399
+ * `spendStealthInput`, `andThen`, ...) EXCEPT the fee -- this attaches either
1400
+ * `.payFeeFromRevealed(maxFee)` or a private fee (via `resolvePrivateFee`/`attachPrivateFee`),
1401
+ * then authorizes with `WalletStealthAuthorizer` (passing `authorizerOpts` through, e.g.
1402
+ * `viewSecret` when the operation itself spends stealth inputs), seals (co-signing the private
1403
+ * fee's UTXO when there is one), and submits -- but does NOT poll to finality or record
1404
+ * anything: callers keep their own existing `addPendingShield`/poll/`recordKnownShieldedOutput`
1405
+ * logic for the operation's own effect, and must call `recordPrivateFeeSpend` themselves once
1406
+ * `transactionId` actually lands (never for a dry run) using the returned `privateFee`.
1407
+ */
1408
+ async prepareSignSubmit(builder, provider, account, maxFee, feeType, authorizerOpts = {},
1409
+ /** See `resolvePrivateFee`'s own doc comment. */
1410
+ excludeFromFeeSelection = []) {
1411
+ const privateFee = await this.resolvePrivateFee(feeType, maxFee, excludeFromFeeSelection);
1412
+ const spec = await (privateFee ? this.attachPrivateFee(builder, privateFee) : builder.payFeeFromRevealed(maxFee)).prepare();
1413
+ const wallet = new OotleWallet().registerKeyProvider(account, this.signer).setDefaultSigner(account);
1414
+ const authorized = await WalletStealthAuthorizer.fromSpec(wallet, spec, {
1415
+ crypto: new WasmStealthCrypto(this.network),
1416
+ ...authorizerOpts,
1417
+ }).prepare(provider);
1418
+ const envelope = privateFee ? await this.sealWithPrivateFee(authorized, privateFee.feeSigner) : await authorized.seal();
1419
+ const transactionId = await submitTransaction(provider, envelope);
1420
+ return { transactionId, spec, privateFee };
1421
+ }
1144
1422
  async redeemStealthOutputWithPrivateFee(resourceAddress, commitmentHex, revealedAmount, followUpInstructions, feeResourceAddress, feeCommitmentHex, maxFee, relatedComponents = []) {
1145
1423
  if (revealedAmount <= 0n)
1146
1424
  throw new Error(`redeemStealthOutputWithPrivateFee: revealedAmount must be > 0, got ${revealedAmount}`);
1147
- if (maxFee <= 0n)
1148
- throw new Error(`redeemStealthOutputWithPrivateFee: maxFee must be > 0, got ${maxFee}`);
1149
1425
  const provider = await this.getProvider();
1150
1426
  const crypto = new WasmStealthCrypto(this.network);
1151
1427
  const walletAddress = await this.getWalletAddress();
1152
1428
  const viewSecret = await this.signer.getViewSecret();
1153
- const decryptStealthInput = async (resource, commitmentHexIn) => {
1154
- const commitment = fromHex(commitmentHexIn);
1155
- const substateId = stealthUtxoSubstateId(resource, commitment);
1156
- const substate = await provider.getSubstate(substateId);
1157
- const decrypted = await decryptOwnedUtxo(crypto, viewSecret, substate, substateId);
1158
- if (!decrypted) {
1159
- throw new Error(`redeemStealthOutputWithPrivateFee: cannot decrypt ${substateId} -- it doesn't belong to this account, or is already spent.`);
1160
- }
1161
- return { commitmentHex: commitmentHexIn, substateId, mask: decrypted.mask, value: decrypted.value };
1162
- };
1163
- const redeemed = await decryptStealthInput(resourceAddress, commitmentHex);
1164
- const feeInput = await decryptStealthInput(feeResourceAddress, feeCommitmentHex);
1165
- if (feeInput.value <= maxFee) {
1166
- throw new Error(`redeemStealthOutputWithPrivateFee: fee UTXO (${feeInput.value}) is too small to cover maxFee (${maxFee})`);
1429
+ const commitment = fromHex(commitmentHex);
1430
+ const substateId = stealthUtxoSubstateId(resourceAddress, commitment);
1431
+ const substate = await provider.getSubstate(substateId);
1432
+ const decrypted = await decryptOwnedUtxo(crypto, viewSecret, substate, substateId);
1433
+ if (!decrypted) {
1434
+ throw new Error(`redeemStealthOutputWithPrivateFee: cannot decrypt ${substateId} -- it doesn't belong to this account, or is already spent.`);
1167
1435
  }
1168
- const feeChange = feeInput.value - maxFee;
1169
- const inputSkeleton = (hex) => JSON.stringify({ inputs: [{ commitment: hex, witness: "KeyPath" }], revealed_amount: "0" });
1436
+ const redeemed = { commitmentHex, substateId, mask: decrypted.mask, value: decrypted.value };
1437
+ const { feeInstructions, feeInputs, feeChangeCommitment, feeSigner } = await this.buildPrivateFeeInstructions(feeResourceAddress, feeCommitmentHex, maxFee);
1438
+ const inputSkeleton = JSON.stringify({ inputs: [{ commitment: redeemed.commitmentHex, witness: "KeyPath" }], revealed_amount: "0" });
1170
1439
  // Redeemed resource: 1 stealth input -> 0 stealth outputs, fully revealed into the bucket
1171
1440
  // `followUpInstructions` consumes.
1172
- const redeemedInputsStatement = new StealthInputsStatement([], 0n, inputSkeleton(redeemed.commitmentHex));
1441
+ const redeemedInputsStatement = new StealthInputsStatement([], 0n, inputSkeleton);
1173
1442
  const redeemedOutputsStatement = new StealthOutputsStatement(JSON.stringify({ outputs: [], revealed_output_amount: revealedAmount.toString(), agg_range_proof: "" }));
1174
1443
  const redeemedAggInputMask = await crypto.aggregateInputMasks([redeemed.mask]);
1175
1444
  const redeemedBalanceProof = await crypto.generateBalanceProofSignature(redeemedAggInputMask, Mask.zero(), redeemedInputsStatement.statementJson, redeemedOutputsStatement.statementJson);
1176
1445
  const redeemedStatement = new StealthTransferStatement(redeemedInputsStatement, redeemedOutputsStatement, redeemedBalanceProof);
1177
1446
  await crypto.validateTransfer(redeemedStatement);
1178
- // Fee resource: 1 stealth input -> 1 stealth change output + revealed maxFee.
1179
- const feeChangeOutput = createOutput({ destination: walletAddress, amount: feeChange, resourceAddress: feeResourceAddress });
1180
- const { statement: feeOutputsStatement, outputMask: feeOutputMask } = await crypto.generateOutputsStatement([feeChangeOutput], maxFee);
1181
- const feeInputsStatement = new StealthInputsStatement([], 0n, inputSkeleton(feeInput.commitmentHex));
1182
- const feeAggInputMask = await crypto.aggregateInputMasks([feeInput.mask]);
1183
- const feeBalanceProof = await crypto.generateBalanceProofSignature(feeAggInputMask, feeOutputMask, feeInputsStatement.statementJson, feeOutputsStatement.statementJson);
1184
- const feeStatement = new StealthTransferStatement(feeInputsStatement, feeOutputsStatement, feeBalanceProof);
1185
- await crypto.validateTransfer(feeStatement);
1186
- const feeChangeCommitment = feeOutputsStatement.parsed().outputs[0].output.commitment;
1187
1447
  const maxEpoch = await resolveMaxEpoch(provider);
1188
1448
  const builder = TransactionBuilder.new(this.network, maxEpoch)
1189
1449
  .addInstruction({
@@ -1194,20 +1454,12 @@ export class OotleAccount {
1194
1454
  },
1195
1455
  })
1196
1456
  .addInstruction({ PutLastInstructionOutputOnWorkspace: { key: 0 } })
1197
- .withInstructions(followUpInstructions)
1198
- .addFeeInstruction({
1199
- StealthTransfer: {
1200
- resource_address_ref: { Address: feeResourceAddress },
1201
- statement: { __ootleRawJson: feeStatement.toCompactJson() },
1202
- revealed_input_bucket: null,
1203
- },
1204
- })
1205
- .addFeeInstruction({ PutLastInstructionOutputOnWorkspace: { key: 0 } })
1206
- .addFeeInstruction({ PayFeeFromBucket: { bucket: { id: 0, offset: null } } })
1207
- .addInput({ substate_id: resourceAddress, version: null })
1208
- .addInput({ substate_id: redeemed.substateId, version: null })
1209
- .addInput({ substate_id: feeResourceAddress, version: null })
1210
- .addInput({ substate_id: feeInput.substateId, version: null });
1457
+ .withInstructions(followUpInstructions);
1458
+ for (const instr of feeInstructions)
1459
+ builder.addFeeInstruction(instr);
1460
+ builder.addInput({ substate_id: resourceAddress, version: null }).addInput({ substate_id: redeemed.substateId, version: null });
1461
+ for (const input of feeInputs)
1462
+ builder.addInput(input);
1211
1463
  for (const component of relatedComponents) {
1212
1464
  builder.addInput({ substate_id: component, version: null });
1213
1465
  for (const vaultId of await getVaultIdsForAccount(provider, component)) {
@@ -1215,28 +1467,22 @@ export class OotleAccount {
1215
1467
  }
1216
1468
  }
1217
1469
  const unsignedTx = await resolveTransaction(provider, builder.buildUnsignedTransaction());
1218
- // One addStealthSignature per stealth input, bound to a shared (arbitrary) seal key that
1219
- // signTransaction generates -- see this method's own doc comment for why no ordinary
1470
+ // The redeemed input's one-time key co-signs the same way the fee input's does inside
1471
+ // `buildPrivateFeeInstructions` -- see this method's own doc comment for why no ordinary
1220
1472
  // account-key signature is added.
1221
- const oneTimeSigner = (publicNonceHex) => ({
1473
+ const redeemedSubstate = await provider.getSubstate(redeemed.substateId);
1474
+ const redeemedNonce = redeemedSubstate.substate.Utxo
1475
+ .output.output.public_nonce;
1476
+ const redeemedSigner = {
1222
1477
  getAddress: async () => walletAddress,
1223
1478
  getPublicKey: async () => parseOotleAddress(walletAddress).owner_key,
1224
1479
  signTransaction: async (tx, sealPublicKey) => {
1225
1480
  const json = serializeUnsignedTx(tx);
1226
- const sig = await this.signer.addStealthSignature(json, fromHex(publicNonceHex), sealPublicKey, { crypto });
1481
+ const sig = await this.signer.addStealthSignature(json, fromHex(redeemedNonce), sealPublicKey, { crypto });
1227
1482
  return [sig];
1228
1483
  },
1229
- });
1230
- // Each stealth input's own sender_public_nonce lives on its UTXO substate -- fetched again
1231
- // here (decryptOwnedUtxo above didn't return it) rather than parsed out of the substate
1232
- // response type, whose shape `decryptOwnedUtxo` already validated once.
1233
- const redeemedSubstate = await provider.getSubstate(redeemed.substateId);
1234
- const feeInputSubstate = await provider.getSubstate(feeInput.substateId);
1235
- const redeemedNonce = redeemedSubstate.substate.Utxo
1236
- .output.output.public_nonce;
1237
- const feeNonce = feeInputSubstate.substate.Utxo.output
1238
- .output.public_nonce;
1239
- const signed = await signTransaction([oneTimeSigner(redeemedNonce), oneTimeSigner(feeNonce)], unsignedTx);
1484
+ };
1485
+ const signed = await signTransaction([redeemedSigner, feeSigner], unsignedTx);
1240
1486
  const envelope = sealTransaction(signed);
1241
1487
  const transactionId = await submitTransaction(provider, envelope);
1242
1488
  const response = await withTimeout(pollTransactionResult(provider, transactionId), 60_000, "submitting the private-fee redemption transaction");
@@ -1273,7 +1519,7 @@ export class OotleAccount {
1273
1519
  * other stealth payment addressed to them — spend authority is governed entirely separately, by
1274
1520
  * `payTo`.
1275
1521
  */
1276
- async htlcFund(resourceAddress, amount, claimantWalletAddress, hashLockHex, refundEpoch, maxFee = 50000n) {
1522
+ async htlcFund(resourceAddress, amount, claimantWalletAddress, hashLockHex, refundEpoch, maxFee = 50000n, feeType = { kind: "transparent" }) {
1277
1523
  const provider = await this.getProvider();
1278
1524
  const account = await this.getComponentAddress();
1279
1525
  const walletAddress = await this.getWalletAddress();
@@ -1287,21 +1533,20 @@ export class OotleAccount {
1287
1533
  });
1288
1534
  // See shield()'s comment: the resource's own substate must be pinned explicitly -- prepare()
1289
1535
  // never adds it on its own.
1290
- const spec = await new StealthTransfer(provider, resourceAddress)
1536
+ const builder = new StealthTransfer(provider, resourceAddress)
1291
1537
  .withBuilder((b) => b.addInput({ substate_id: resourceAddress, version: null }))
1292
1538
  .spendRevealedInput(account, amount)
1293
- .toStealthOutput(createOutput({ destination: claimantWalletAddress, amount, resourceAddress, payTo: { Conditions: conditions } }))
1294
- .payFeeFromRevealed(maxFee)
1295
- .prepare();
1539
+ .toStealthOutput(createOutput({ destination: claimantWalletAddress, amount, resourceAddress, payTo: { Conditions: conditions } }));
1540
+ // No stealth inputs to unblind for this fund's own transfer (revealed-only source), so no
1541
+ // viewSecret needed here even when the FEE is paid privately -- same as shield().
1542
+ const { transactionId, spec, privateFee } = await this.prepareSignSubmit(builder, provider, account, maxFee, feeType);
1296
1543
  const ownCommitment = extractOutputCommitment(spec, 0);
1297
1544
  const outputMask = spec.outputMask.toHex();
1298
- const wallet = new OotleWallet().registerKeyProvider(account, this.signer).setDefaultSigner(account);
1299
- // No stealth inputs to unblind (revealed-only source), so no viewSecret needed -- same as shield().
1300
- const authorized = await WalletStealthAuthorizer.fromSpec(wallet, spec, { crypto: new WasmStealthCrypto(this.network) }).prepare(provider);
1301
- const envelope = await authorized.seal();
1302
- const transactionId = await submitTransaction(provider, envelope);
1303
1545
  const response = await withTimeout(pollTransactionResult(provider, transactionId), 60_000, "submitting the HTLC funding transaction");
1304
1546
  await recordKnownVersions(response);
1547
+ if (privateFee && feeType.kind === "private") {
1548
+ await this.recordPrivateFeeSpend(localAccountId(this.index), feeType.feeResourceAddress, privateFee, transactionId);
1549
+ }
1305
1550
  return { transactionId, conditions, ownCommitment, outputMask };
1306
1551
  }
1307
1552
  /**
@@ -1331,7 +1576,7 @@ export class OotleAccount {
1331
1576
  * unblinded mask+value and the two witnesses below, so there is nothing left for the builder
1332
1577
  * pipeline to add.
1333
1578
  */
1334
- async htlcClaim(resourceAddress, commitmentHex, conditions, preimageHex, maxFee = 50000n) {
1579
+ async htlcClaim(resourceAddress, commitmentHex, conditions, preimageHex, maxFee = 50000n, feeType = { kind: "transparent" }) {
1335
1580
  if (!/^[0-9a-f]{64}$/i.test(preimageHex)) {
1336
1581
  throw new Error(`htlcClaim: preimageHex must be exactly 64 hex characters (32 bytes), got ${JSON.stringify(preimageHex)}`);
1337
1582
  }
@@ -1359,6 +1604,7 @@ export class OotleAccount {
1359
1604
  destinationWalletAddress: walletAddress,
1360
1605
  resourceAddress,
1361
1606
  });
1607
+ const privateFee = await this.resolvePrivateFee(feeType, maxFee);
1362
1608
  const transactionId = await submitHtlcSpend({
1363
1609
  provider,
1364
1610
  signer: this.signer,
@@ -1369,7 +1615,11 @@ export class OotleAccount {
1369
1615
  statementJson,
1370
1616
  maxFee,
1371
1617
  timeoutLabel: "submitting the HTLC claim transaction",
1618
+ privateFee,
1372
1619
  });
1620
+ if (privateFee && feeType.kind === "private") {
1621
+ await this.recordPrivateFeeSpend(localAccountId(this.index), feeType.feeResourceAddress, privateFee, transactionId);
1622
+ }
1373
1623
  return { transactionId };
1374
1624
  }
1375
1625
  /**
@@ -1390,7 +1640,7 @@ export class OotleAccount {
1390
1640
  * `buildStealthTransferStatement`-in-one-call approach, same bypass of
1391
1641
  * `StealthTransfer`/`WalletStealthAuthorizer`.
1392
1642
  */
1393
- async htlcRefund(resourceAddress, commitmentHex, conditions, amount, outputMaskHex, maxFee = 50000n) {
1643
+ async htlcRefund(resourceAddress, commitmentHex, conditions, amount, outputMaskHex, maxFee = 50000n, feeType = { kind: "transparent" }) {
1394
1644
  const provider = await this.getProvider();
1395
1645
  const account = await this.getComponentAddress();
1396
1646
  const walletAddress = await this.getWalletAddress();
@@ -1409,6 +1659,7 @@ export class OotleAccount {
1409
1659
  destinationWalletAddress: walletAddress,
1410
1660
  resourceAddress,
1411
1661
  });
1662
+ const privateFee = await this.resolvePrivateFee(feeType, maxFee);
1412
1663
  const transactionId = await submitHtlcSpend({
1413
1664
  provider,
1414
1665
  signer: this.signer,
@@ -1419,7 +1670,11 @@ export class OotleAccount {
1419
1670
  statementJson,
1420
1671
  maxFee,
1421
1672
  timeoutLabel: "submitting the HTLC refund transaction",
1673
+ privateFee,
1422
1674
  });
1675
+ if (privateFee && feeType.kind === "private") {
1676
+ await this.recordPrivateFeeSpend(localAccountId(this.index), feeType.feeResourceAddress, privateFee, transactionId);
1677
+ }
1423
1678
  return { transactionId };
1424
1679
  }
1425
1680
  /**
@@ -1449,10 +1704,10 @@ export class OotleAccount {
1449
1704
  * grabbing "the first new stealth output" would risk recording the recipient's payment as our
1450
1705
  * own. See `PendingShield.ownCommitment`'s doc comment.
1451
1706
  */
1452
- async sendPrivately(resourceAddress, recipientWalletAddress, amount, maxFee = 100000n, memo, minimumValuePromise = 0n) {
1707
+ async sendPrivately(resourceAddress, recipientWalletAddress, amount, maxFee = 100000n, memo, minimumValuePromise = 0n, feeType = { kind: "transparent" }) {
1453
1708
  assertValidMinimumValuePromise(minimumValuePromise, amount);
1454
1709
  const accountId = localAccountId(this.index);
1455
- const records = await listShieldedOutputs(accountId);
1710
+ const records = await this.filterReadableShieldedOutputs(await listShieldedOutputs(accountId), resourceAddress, amount);
1456
1711
  const { commitments, changeAmount } = resolveSendPrivatelyPlan(records, resourceAddress, amount);
1457
1712
  const dust = 1n;
1458
1713
  const provider = await this.getProvider();
@@ -1473,18 +1728,18 @@ export class OotleAccount {
1473
1728
  if (changeAmount > 0n) {
1474
1729
  builder = builder.toStealthOutput(createOutput({ destination: ownWalletAddress, amount: changeAmount, resourceAddress }));
1475
1730
  }
1476
- const spec = await builder.toRevealedOutput(dust).payFeeFromRevealed(maxFee).prepare();
1731
+ builder = builder.toRevealedOutput(dust);
1732
+ const viewSecret = await this.signer.getViewSecret();
1733
+ // `commitments` excluded from the fee UTXO's own selection -- see unshield()'s identical
1734
+ // comment on why (this and unshield are the only two methods with their own multi-UTXO
1735
+ // coin selection of potentially the same resource as the fee).
1736
+ const { transactionId, spec, privateFee } = await this.prepareSignSubmit(builder, provider, account, maxFee, feeType, { viewSecret }, commitments);
1477
1737
  // Output 0 is the recipient's; output 1 (only present when there's change) is ours. The
1478
1738
  // recipient has no way to discover their new output on their own (no scan-by-view-key API
1479
1739
  // exists) -- this commitment must be handed back to the caller so it can be shared with them
1480
1740
  // out of band; without it, the payment is invisible to them even though it succeeded on-chain.
1481
1741
  const recipientCommitment = extractOutputCommitment(spec, 0);
1482
1742
  const ownCommitment = changeAmount > 0n ? extractOutputCommitment(spec, 1) : undefined;
1483
- const wallet = new OotleWallet().registerKeyProvider(account, this.signer).setDefaultSigner(account);
1484
- const viewSecret = await this.signer.getViewSecret();
1485
- const authorized = await WalletStealthAuthorizer.fromSpec(wallet, spec, { viewSecret, crypto: new WasmStealthCrypto(this.network) }).prepare(provider);
1486
- const envelope = await authorized.seal();
1487
- const transactionId = await submitTransaction(provider, envelope);
1488
1743
  await addPendingShield({
1489
1744
  transactionId,
1490
1745
  accountId,
@@ -1501,6 +1756,9 @@ export class OotleAccount {
1501
1756
  for (const commitment of commitments) {
1502
1757
  await markShieldedOutputSpent(accountId, commitment);
1503
1758
  }
1759
+ if (privateFee && feeType.kind === "private") {
1760
+ await this.recordPrivateFeeSpend(accountId, feeType.feeResourceAddress, privateFee, transactionId);
1761
+ }
1504
1762
  }
1505
1763
  finally {
1506
1764
  await removePendingShield(transactionId);
@@ -1827,7 +2085,6 @@ async function submitHtlcSpend(params) {
1827
2085
  const builder = TransactionBuilder.new(params.network, maxEpoch)
1828
2086
  .addInput({ substate_id: params.substateId, version: null })
1829
2087
  .addInput({ substate_id: params.account, version: null })
1830
- .feeTransactionPayFromComponent(params.account, params.maxFee)
1831
2088
  .addInstruction(
1832
2089
  // `statement` is spliced as a raw JSON fragment (see buildHtlcSpendStatement), the same
1833
2090
  // technique `@tari-project/ootle`'s own `statementAsWire` uses internally for the piecemeal
@@ -1839,11 +2096,21 @@ async function submitHtlcSpend(params) {
1839
2096
  revealed_input_bucket: null,
1840
2097
  },
1841
2098
  });
2099
+ if (params.privateFee) {
2100
+ for (const instr of params.privateFee.feeInstructions)
2101
+ builder.addFeeInstruction(instr);
2102
+ for (const input of params.privateFee.feeInputs)
2103
+ builder.addInput(input);
2104
+ }
2105
+ else {
2106
+ builder.feeTransactionPayFromComponent(params.account, params.maxFee);
2107
+ }
1842
2108
  for (const vaultId of await getVaultIdsForAccount(params.provider, params.account)) {
1843
2109
  builder.addInput({ substate_id: vaultId, version: null });
1844
2110
  }
1845
2111
  const unsignedTx = await resolveTransaction(params.provider, builder.buildUnsignedTransaction());
1846
- const signed = await signTransaction([params.signer], unsignedTx);
2112
+ const extraSigners = params.privateFee ? [params.privateFee.feeSigner] : [];
2113
+ const signed = await signTransaction([params.signer, ...extraSigners], unsignedTx);
1847
2114
  const envelope = await sealTransaction(signed);
1848
2115
  const transactionId = await submitTransaction(params.provider, envelope);
1849
2116
  const response = await withTimeout(pollTransactionResult(params.provider, transactionId), 60_000, params.timeoutLabel);
@@ -2002,6 +2269,27 @@ export function resolveUnshieldPlan(records, resourceAddress, revealedOutAmount)
2002
2269
  }
2003
2270
  return { commitments: finalSelected.map((r) => r.commitment), remainder: finalTotal - revealedOutAmount };
2004
2271
  }
2272
+ /**
2273
+ * Pure planning for `execute()`'s private-fee path: picks the smallest unspent stealth UTXO of
2274
+ * `feeResourceAddress` that alone covers `maxFee`. Deliberately the *smallest* qualifying record
2275
+ * (not the largest, and never several combined) -- `buildPrivateFeeInstructions` only supports a
2276
+ * single fee input (see its own doc comment for why), so this leaves larger shielded balances
2277
+ * untouched for whatever they were shielded for rather than reaching for them first.
2278
+ */
2279
+ export function selectPrivateFeeUtxo(records, feeResourceAddress, maxFee, exclude = []) {
2280
+ const excluded = new Set(exclude);
2281
+ const candidates = records
2282
+ .filter((r) => !r.spent && !excluded.has(r.commitment) && r.resourceAddress === feeResourceAddress && BigInt(r.amount) > maxFee)
2283
+ .sort((a, b) => {
2284
+ const diff = BigInt(a.amount) - BigInt(b.amount);
2285
+ return diff > 0n ? 1 : diff < 0n ? -1 : 0;
2286
+ });
2287
+ const chosen = candidates[0];
2288
+ if (!chosen) {
2289
+ throw new Error(`No single shielded ${feeResourceAddress} UTXO is large enough to cover a private fee of ${maxFee}. Shield some first (see shield()).`);
2290
+ }
2291
+ return chosen;
2292
+ }
2005
2293
  /**
2006
2294
  * Pure planning for `sendPrivately()`: selects enough of the caller's unspent records for this
2007
2295
  * resource to cover `amount` — unlike `resolveUnshieldPlan`, sending the *entire* selected total
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chironbuilder/ootle-sdk",
3
- "version": "0.1.8",
3
+ "version": "0.1.10",
4
4
  "description": "Shared TypeScript client for Tari Ootle (L2): account derivation, balances, transactions, HTLCs, and stealth/confidential transfers — the account core behind Sapient and the Tari L1 web wallet's Ootle support.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -9,7 +9,13 @@
9
9
  "url": "git+https://github.com/chironbuilds/ootle-sdk-ts.git"
10
10
  },
11
11
  "homepage": "https://github.com/chironbuilds/ootle-sdk-ts",
12
- "keywords": ["tari", "ootle", "wallet", "blockchain", "web3"],
12
+ "keywords": [
13
+ "tari",
14
+ "ootle",
15
+ "wallet",
16
+ "blockchain",
17
+ "web3"
18
+ ],
13
19
  "main": "./dist/index.js",
14
20
  "types": "./dist/index.d.ts",
15
21
  "exports": {
@@ -22,7 +28,10 @@
22
28
  "default": "./dist/adapters.js"
23
29
  }
24
30
  },
25
- "files": ["dist", "vendor"],
31
+ "files": [
32
+ "dist",
33
+ "vendor"
34
+ ],
26
35
  "scripts": {
27
36
  "build": "tsc -p tsconfig.build.json",
28
37
  "typecheck": "tsc --noEmit",