@chironbuilder/ootle-sdk 0.1.8 → 0.1.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/accountApi.d.ts +7 -2
- package/dist/accountApi.d.ts.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/wallet.d.ts +105 -10
- package/dist/wallet.d.ts.map +1 -1
- package/dist/wallet.js +333 -127
- package/package.json +12 -3
package/dist/accountApi.d.ts
CHANGED
|
@@ -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
|
package/dist/accountApi.d.ts.map
CHANGED
|
@@ -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;
|
|
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
|
package/dist/index.d.ts.map
CHANGED
|
@@ -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;
|
|
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,68 @@ 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
|
+
* Shared prepare/authorize/seal/submit path for every `StealthTransfer`-based operation
|
|
545
|
+
* (shield/unshield/withdrawStealthAndExecute/etc.): `builder` must already have every
|
|
546
|
+
* operation-specific call applied (`spendRevealedInput`, `toStealthOutput`,
|
|
547
|
+
* `spendStealthInput`, `andThen`, ...) EXCEPT the fee -- this attaches either
|
|
548
|
+
* `.payFeeFromRevealed(maxFee)` or a private fee (via `resolvePrivateFee`/`attachPrivateFee`),
|
|
549
|
+
* then authorizes with `WalletStealthAuthorizer` (passing `authorizerOpts` through, e.g.
|
|
550
|
+
* `viewSecret` when the operation itself spends stealth inputs), seals (co-signing the private
|
|
551
|
+
* fee's UTXO when there is one), and submits -- but does NOT poll to finality or record
|
|
552
|
+
* anything: callers keep their own existing `addPendingShield`/poll/`recordKnownShieldedOutput`
|
|
553
|
+
* logic for the operation's own effect, and must call `recordPrivateFeeSpend` themselves once
|
|
554
|
+
* `transactionId` actually lands (never for a dry run) using the returned `privateFee`.
|
|
555
|
+
*/
|
|
556
|
+
private prepareSignSubmit;
|
|
470
557
|
redeemStealthOutputWithPrivateFee(resourceAddress: string, commitmentHex: string, revealedAmount: bigint, followUpInstructions: Instruction[], feeResourceAddress: string, feeCommitmentHex: string, maxFee: bigint, relatedComponents?: string[]): Promise<{
|
|
471
558
|
transactionId: string;
|
|
472
559
|
feeChangeCommitment: string;
|
|
@@ -501,7 +588,7 @@ export declare class OotleAccount implements WalletAccountApi {
|
|
|
501
588
|
* other stealth payment addressed to them — spend authority is governed entirely separately, by
|
|
502
589
|
* `payTo`.
|
|
503
590
|
*/
|
|
504
|
-
htlcFund(resourceAddress: string, amount: bigint, claimantWalletAddress: string, hashLockHex: string, refundEpoch: bigint, maxFee?: bigint): Promise<{
|
|
591
|
+
htlcFund(resourceAddress: string, amount: bigint, claimantWalletAddress: string, hashLockHex: string, refundEpoch: bigint, maxFee?: bigint, feeType?: FeeType): Promise<{
|
|
505
592
|
transactionId: string;
|
|
506
593
|
conditions: object[];
|
|
507
594
|
ownCommitment: string;
|
|
@@ -534,7 +621,7 @@ export declare class OotleAccount implements WalletAccountApi {
|
|
|
534
621
|
* unblinded mask+value and the two witnesses below, so there is nothing left for the builder
|
|
535
622
|
* pipeline to add.
|
|
536
623
|
*/
|
|
537
|
-
htlcClaim(resourceAddress: string, commitmentHex: string, conditions: object[], preimageHex: string, maxFee?: bigint): Promise<{
|
|
624
|
+
htlcClaim(resourceAddress: string, commitmentHex: string, conditions: object[], preimageHex: string, maxFee?: bigint, feeType?: FeeType): Promise<{
|
|
538
625
|
transactionId: string;
|
|
539
626
|
}>;
|
|
540
627
|
/**
|
|
@@ -555,7 +642,7 @@ export declare class OotleAccount implements WalletAccountApi {
|
|
|
555
642
|
* `buildStealthTransferStatement`-in-one-call approach, same bypass of
|
|
556
643
|
* `StealthTransfer`/`WalletStealthAuthorizer`.
|
|
557
644
|
*/
|
|
558
|
-
htlcRefund(resourceAddress: string, commitmentHex: string, conditions: object[], amount: bigint, outputMaskHex: string, maxFee?: bigint): Promise<{
|
|
645
|
+
htlcRefund(resourceAddress: string, commitmentHex: string, conditions: object[], amount: bigint, outputMaskHex: string, maxFee?: bigint, feeType?: FeeType): Promise<{
|
|
559
646
|
transactionId: string;
|
|
560
647
|
}>;
|
|
561
648
|
/**
|
|
@@ -585,7 +672,7 @@ export declare class OotleAccount implements WalletAccountApi {
|
|
|
585
672
|
* grabbing "the first new stealth output" would risk recording the recipient's payment as our
|
|
586
673
|
* own. See `PendingShield.ownCommitment`'s doc comment.
|
|
587
674
|
*/
|
|
588
|
-
sendPrivately(resourceAddress: string, recipientWalletAddress: string, amount: bigint, maxFee?: bigint, memo?: string, minimumValuePromise?: bigint): Promise<{
|
|
675
|
+
sendPrivately(resourceAddress: string, recipientWalletAddress: string, amount: bigint, maxFee?: bigint, memo?: string, minimumValuePromise?: bigint, feeType?: FeeType): Promise<{
|
|
589
676
|
transactionId: string;
|
|
590
677
|
recipientCommitment: string;
|
|
591
678
|
recipientSubstateId: string;
|
|
@@ -782,6 +869,14 @@ export declare function resolveUnshieldPlan(records: ShieldedOutputRecord[], res
|
|
|
782
869
|
commitments: string[];
|
|
783
870
|
remainder: bigint;
|
|
784
871
|
};
|
|
872
|
+
/**
|
|
873
|
+
* Pure planning for `execute()`'s private-fee path: picks the smallest unspent stealth UTXO of
|
|
874
|
+
* `feeResourceAddress` that alone covers `maxFee`. Deliberately the *smallest* qualifying record
|
|
875
|
+
* (not the largest, and never several combined) -- `buildPrivateFeeInstructions` only supports a
|
|
876
|
+
* single fee input (see its own doc comment for why), so this leaves larger shielded balances
|
|
877
|
+
* untouched for whatever they were shielded for rather than reaching for them first.
|
|
878
|
+
*/
|
|
879
|
+
export declare function selectPrivateFeeUtxo(records: ShieldedOutputRecord[], feeResourceAddress: string, maxFee: bigint, exclude?: string[]): ShieldedOutputRecord;
|
|
785
880
|
/**
|
|
786
881
|
* Pure planning for `sendPrivately()`: selects enough of the caller's unspent records for this
|
|
787
882
|
* resource to cover `amount` — unlike `resolveUnshieldPlan`, sending the *entire* selected total
|
package/dist/wallet.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
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,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;
|
|
1
|
+
{"version":3,"file":"wallet.d.ts","sourceRoot":"","sources":["../src/wallet.ts"],"names":[],"mappings":"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"}
|
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
|
-
|
|
341
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
762
|
-
|
|
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,7 +895,7 @@ 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
900
|
const records = await listShieldedOutputs(accountId);
|
|
882
901
|
const { commitments, remainder } = resolveUnshieldPlan(records, resourceAddress, revealedOutAmount);
|
|
@@ -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
|
-
|
|
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
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
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
|
|
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
|
-
|
|
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,193 @@ 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 chosen = selectPrivateFeeUtxo(await listShieldedOutputs(accountId), feeType.feeResourceAddress, maxFee, exclude);
|
|
1310
|
+
const built = await this.buildPrivateFeeInstructions(feeType.feeResourceAddress, chosen.commitment, maxFee);
|
|
1311
|
+
return { ...built, spentCommitment: chosen.commitment };
|
|
1312
|
+
}
|
|
1313
|
+
/**
|
|
1314
|
+
* Shared prepare/authorize/seal/submit path for every `StealthTransfer`-based operation
|
|
1315
|
+
* (shield/unshield/withdrawStealthAndExecute/etc.): `builder` must already have every
|
|
1316
|
+
* operation-specific call applied (`spendRevealedInput`, `toStealthOutput`,
|
|
1317
|
+
* `spendStealthInput`, `andThen`, ...) EXCEPT the fee -- this attaches either
|
|
1318
|
+
* `.payFeeFromRevealed(maxFee)` or a private fee (via `resolvePrivateFee`/`attachPrivateFee`),
|
|
1319
|
+
* then authorizes with `WalletStealthAuthorizer` (passing `authorizerOpts` through, e.g.
|
|
1320
|
+
* `viewSecret` when the operation itself spends stealth inputs), seals (co-signing the private
|
|
1321
|
+
* fee's UTXO when there is one), and submits -- but does NOT poll to finality or record
|
|
1322
|
+
* anything: callers keep their own existing `addPendingShield`/poll/`recordKnownShieldedOutput`
|
|
1323
|
+
* logic for the operation's own effect, and must call `recordPrivateFeeSpend` themselves once
|
|
1324
|
+
* `transactionId` actually lands (never for a dry run) using the returned `privateFee`.
|
|
1325
|
+
*/
|
|
1326
|
+
async prepareSignSubmit(builder, provider, account, maxFee, feeType, authorizerOpts = {},
|
|
1327
|
+
/** See `resolvePrivateFee`'s own doc comment. */
|
|
1328
|
+
excludeFromFeeSelection = []) {
|
|
1329
|
+
const privateFee = await this.resolvePrivateFee(feeType, maxFee, excludeFromFeeSelection);
|
|
1330
|
+
const spec = await (privateFee ? this.attachPrivateFee(builder, privateFee) : builder.payFeeFromRevealed(maxFee)).prepare();
|
|
1331
|
+
const wallet = new OotleWallet().registerKeyProvider(account, this.signer).setDefaultSigner(account);
|
|
1332
|
+
const authorized = await WalletStealthAuthorizer.fromSpec(wallet, spec, {
|
|
1333
|
+
crypto: new WasmStealthCrypto(this.network),
|
|
1334
|
+
...authorizerOpts,
|
|
1335
|
+
}).prepare(provider);
|
|
1336
|
+
const envelope = privateFee ? await this.sealWithPrivateFee(authorized, privateFee.feeSigner) : await authorized.seal();
|
|
1337
|
+
const transactionId = await submitTransaction(provider, envelope);
|
|
1338
|
+
return { transactionId, spec, privateFee };
|
|
1339
|
+
}
|
|
1144
1340
|
async redeemStealthOutputWithPrivateFee(resourceAddress, commitmentHex, revealedAmount, followUpInstructions, feeResourceAddress, feeCommitmentHex, maxFee, relatedComponents = []) {
|
|
1145
1341
|
if (revealedAmount <= 0n)
|
|
1146
1342
|
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
1343
|
const provider = await this.getProvider();
|
|
1150
1344
|
const crypto = new WasmStealthCrypto(this.network);
|
|
1151
1345
|
const walletAddress = await this.getWalletAddress();
|
|
1152
1346
|
const viewSecret = await this.signer.getViewSecret();
|
|
1153
|
-
const
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
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})`);
|
|
1347
|
+
const commitment = fromHex(commitmentHex);
|
|
1348
|
+
const substateId = stealthUtxoSubstateId(resourceAddress, commitment);
|
|
1349
|
+
const substate = await provider.getSubstate(substateId);
|
|
1350
|
+
const decrypted = await decryptOwnedUtxo(crypto, viewSecret, substate, substateId);
|
|
1351
|
+
if (!decrypted) {
|
|
1352
|
+
throw new Error(`redeemStealthOutputWithPrivateFee: cannot decrypt ${substateId} -- it doesn't belong to this account, or is already spent.`);
|
|
1167
1353
|
}
|
|
1168
|
-
const
|
|
1169
|
-
const
|
|
1354
|
+
const redeemed = { commitmentHex, substateId, mask: decrypted.mask, value: decrypted.value };
|
|
1355
|
+
const { feeInstructions, feeInputs, feeChangeCommitment, feeSigner } = await this.buildPrivateFeeInstructions(feeResourceAddress, feeCommitmentHex, maxFee);
|
|
1356
|
+
const inputSkeleton = JSON.stringify({ inputs: [{ commitment: redeemed.commitmentHex, witness: "KeyPath" }], revealed_amount: "0" });
|
|
1170
1357
|
// Redeemed resource: 1 stealth input -> 0 stealth outputs, fully revealed into the bucket
|
|
1171
1358
|
// `followUpInstructions` consumes.
|
|
1172
|
-
const redeemedInputsStatement = new StealthInputsStatement([], 0n, inputSkeleton
|
|
1359
|
+
const redeemedInputsStatement = new StealthInputsStatement([], 0n, inputSkeleton);
|
|
1173
1360
|
const redeemedOutputsStatement = new StealthOutputsStatement(JSON.stringify({ outputs: [], revealed_output_amount: revealedAmount.toString(), agg_range_proof: "" }));
|
|
1174
1361
|
const redeemedAggInputMask = await crypto.aggregateInputMasks([redeemed.mask]);
|
|
1175
1362
|
const redeemedBalanceProof = await crypto.generateBalanceProofSignature(redeemedAggInputMask, Mask.zero(), redeemedInputsStatement.statementJson, redeemedOutputsStatement.statementJson);
|
|
1176
1363
|
const redeemedStatement = new StealthTransferStatement(redeemedInputsStatement, redeemedOutputsStatement, redeemedBalanceProof);
|
|
1177
1364
|
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
1365
|
const maxEpoch = await resolveMaxEpoch(provider);
|
|
1188
1366
|
const builder = TransactionBuilder.new(this.network, maxEpoch)
|
|
1189
1367
|
.addInstruction({
|
|
@@ -1194,20 +1372,12 @@ export class OotleAccount {
|
|
|
1194
1372
|
},
|
|
1195
1373
|
})
|
|
1196
1374
|
.addInstruction({ PutLastInstructionOutputOnWorkspace: { key: 0 } })
|
|
1197
|
-
.withInstructions(followUpInstructions)
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
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 });
|
|
1375
|
+
.withInstructions(followUpInstructions);
|
|
1376
|
+
for (const instr of feeInstructions)
|
|
1377
|
+
builder.addFeeInstruction(instr);
|
|
1378
|
+
builder.addInput({ substate_id: resourceAddress, version: null }).addInput({ substate_id: redeemed.substateId, version: null });
|
|
1379
|
+
for (const input of feeInputs)
|
|
1380
|
+
builder.addInput(input);
|
|
1211
1381
|
for (const component of relatedComponents) {
|
|
1212
1382
|
builder.addInput({ substate_id: component, version: null });
|
|
1213
1383
|
for (const vaultId of await getVaultIdsForAccount(provider, component)) {
|
|
@@ -1215,28 +1385,22 @@ export class OotleAccount {
|
|
|
1215
1385
|
}
|
|
1216
1386
|
}
|
|
1217
1387
|
const unsignedTx = await resolveTransaction(provider, builder.buildUnsignedTransaction());
|
|
1218
|
-
//
|
|
1219
|
-
//
|
|
1388
|
+
// The redeemed input's one-time key co-signs the same way the fee input's does inside
|
|
1389
|
+
// `buildPrivateFeeInstructions` -- see this method's own doc comment for why no ordinary
|
|
1220
1390
|
// account-key signature is added.
|
|
1221
|
-
const
|
|
1391
|
+
const redeemedSubstate = await provider.getSubstate(redeemed.substateId);
|
|
1392
|
+
const redeemedNonce = redeemedSubstate.substate.Utxo
|
|
1393
|
+
.output.output.public_nonce;
|
|
1394
|
+
const redeemedSigner = {
|
|
1222
1395
|
getAddress: async () => walletAddress,
|
|
1223
1396
|
getPublicKey: async () => parseOotleAddress(walletAddress).owner_key,
|
|
1224
1397
|
signTransaction: async (tx, sealPublicKey) => {
|
|
1225
1398
|
const json = serializeUnsignedTx(tx);
|
|
1226
|
-
const sig = await this.signer.addStealthSignature(json, fromHex(
|
|
1399
|
+
const sig = await this.signer.addStealthSignature(json, fromHex(redeemedNonce), sealPublicKey, { crypto });
|
|
1227
1400
|
return [sig];
|
|
1228
1401
|
},
|
|
1229
|
-
}
|
|
1230
|
-
|
|
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);
|
|
1402
|
+
};
|
|
1403
|
+
const signed = await signTransaction([redeemedSigner, feeSigner], unsignedTx);
|
|
1240
1404
|
const envelope = sealTransaction(signed);
|
|
1241
1405
|
const transactionId = await submitTransaction(provider, envelope);
|
|
1242
1406
|
const response = await withTimeout(pollTransactionResult(provider, transactionId), 60_000, "submitting the private-fee redemption transaction");
|
|
@@ -1273,7 +1437,7 @@ export class OotleAccount {
|
|
|
1273
1437
|
* other stealth payment addressed to them — spend authority is governed entirely separately, by
|
|
1274
1438
|
* `payTo`.
|
|
1275
1439
|
*/
|
|
1276
|
-
async htlcFund(resourceAddress, amount, claimantWalletAddress, hashLockHex, refundEpoch, maxFee = 50000n) {
|
|
1440
|
+
async htlcFund(resourceAddress, amount, claimantWalletAddress, hashLockHex, refundEpoch, maxFee = 50000n, feeType = { kind: "transparent" }) {
|
|
1277
1441
|
const provider = await this.getProvider();
|
|
1278
1442
|
const account = await this.getComponentAddress();
|
|
1279
1443
|
const walletAddress = await this.getWalletAddress();
|
|
@@ -1287,21 +1451,20 @@ export class OotleAccount {
|
|
|
1287
1451
|
});
|
|
1288
1452
|
// See shield()'s comment: the resource's own substate must be pinned explicitly -- prepare()
|
|
1289
1453
|
// never adds it on its own.
|
|
1290
|
-
const
|
|
1454
|
+
const builder = new StealthTransfer(provider, resourceAddress)
|
|
1291
1455
|
.withBuilder((b) => b.addInput({ substate_id: resourceAddress, version: null }))
|
|
1292
1456
|
.spendRevealedInput(account, amount)
|
|
1293
|
-
.toStealthOutput(createOutput({ destination: claimantWalletAddress, amount, resourceAddress, payTo: { Conditions: conditions } }))
|
|
1294
|
-
|
|
1295
|
-
|
|
1457
|
+
.toStealthOutput(createOutput({ destination: claimantWalletAddress, amount, resourceAddress, payTo: { Conditions: conditions } }));
|
|
1458
|
+
// No stealth inputs to unblind for this fund's own transfer (revealed-only source), so no
|
|
1459
|
+
// viewSecret needed here even when the FEE is paid privately -- same as shield().
|
|
1460
|
+
const { transactionId, spec, privateFee } = await this.prepareSignSubmit(builder, provider, account, maxFee, feeType);
|
|
1296
1461
|
const ownCommitment = extractOutputCommitment(spec, 0);
|
|
1297
1462
|
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
1463
|
const response = await withTimeout(pollTransactionResult(provider, transactionId), 60_000, "submitting the HTLC funding transaction");
|
|
1304
1464
|
await recordKnownVersions(response);
|
|
1465
|
+
if (privateFee && feeType.kind === "private") {
|
|
1466
|
+
await this.recordPrivateFeeSpend(localAccountId(this.index), feeType.feeResourceAddress, privateFee, transactionId);
|
|
1467
|
+
}
|
|
1305
1468
|
return { transactionId, conditions, ownCommitment, outputMask };
|
|
1306
1469
|
}
|
|
1307
1470
|
/**
|
|
@@ -1331,7 +1494,7 @@ export class OotleAccount {
|
|
|
1331
1494
|
* unblinded mask+value and the two witnesses below, so there is nothing left for the builder
|
|
1332
1495
|
* pipeline to add.
|
|
1333
1496
|
*/
|
|
1334
|
-
async htlcClaim(resourceAddress, commitmentHex, conditions, preimageHex, maxFee = 50000n) {
|
|
1497
|
+
async htlcClaim(resourceAddress, commitmentHex, conditions, preimageHex, maxFee = 50000n, feeType = { kind: "transparent" }) {
|
|
1335
1498
|
if (!/^[0-9a-f]{64}$/i.test(preimageHex)) {
|
|
1336
1499
|
throw new Error(`htlcClaim: preimageHex must be exactly 64 hex characters (32 bytes), got ${JSON.stringify(preimageHex)}`);
|
|
1337
1500
|
}
|
|
@@ -1359,6 +1522,7 @@ export class OotleAccount {
|
|
|
1359
1522
|
destinationWalletAddress: walletAddress,
|
|
1360
1523
|
resourceAddress,
|
|
1361
1524
|
});
|
|
1525
|
+
const privateFee = await this.resolvePrivateFee(feeType, maxFee);
|
|
1362
1526
|
const transactionId = await submitHtlcSpend({
|
|
1363
1527
|
provider,
|
|
1364
1528
|
signer: this.signer,
|
|
@@ -1369,7 +1533,11 @@ export class OotleAccount {
|
|
|
1369
1533
|
statementJson,
|
|
1370
1534
|
maxFee,
|
|
1371
1535
|
timeoutLabel: "submitting the HTLC claim transaction",
|
|
1536
|
+
privateFee,
|
|
1372
1537
|
});
|
|
1538
|
+
if (privateFee && feeType.kind === "private") {
|
|
1539
|
+
await this.recordPrivateFeeSpend(localAccountId(this.index), feeType.feeResourceAddress, privateFee, transactionId);
|
|
1540
|
+
}
|
|
1373
1541
|
return { transactionId };
|
|
1374
1542
|
}
|
|
1375
1543
|
/**
|
|
@@ -1390,7 +1558,7 @@ export class OotleAccount {
|
|
|
1390
1558
|
* `buildStealthTransferStatement`-in-one-call approach, same bypass of
|
|
1391
1559
|
* `StealthTransfer`/`WalletStealthAuthorizer`.
|
|
1392
1560
|
*/
|
|
1393
|
-
async htlcRefund(resourceAddress, commitmentHex, conditions, amount, outputMaskHex, maxFee = 50000n) {
|
|
1561
|
+
async htlcRefund(resourceAddress, commitmentHex, conditions, amount, outputMaskHex, maxFee = 50000n, feeType = { kind: "transparent" }) {
|
|
1394
1562
|
const provider = await this.getProvider();
|
|
1395
1563
|
const account = await this.getComponentAddress();
|
|
1396
1564
|
const walletAddress = await this.getWalletAddress();
|
|
@@ -1409,6 +1577,7 @@ export class OotleAccount {
|
|
|
1409
1577
|
destinationWalletAddress: walletAddress,
|
|
1410
1578
|
resourceAddress,
|
|
1411
1579
|
});
|
|
1580
|
+
const privateFee = await this.resolvePrivateFee(feeType, maxFee);
|
|
1412
1581
|
const transactionId = await submitHtlcSpend({
|
|
1413
1582
|
provider,
|
|
1414
1583
|
signer: this.signer,
|
|
@@ -1419,7 +1588,11 @@ export class OotleAccount {
|
|
|
1419
1588
|
statementJson,
|
|
1420
1589
|
maxFee,
|
|
1421
1590
|
timeoutLabel: "submitting the HTLC refund transaction",
|
|
1591
|
+
privateFee,
|
|
1422
1592
|
});
|
|
1593
|
+
if (privateFee && feeType.kind === "private") {
|
|
1594
|
+
await this.recordPrivateFeeSpend(localAccountId(this.index), feeType.feeResourceAddress, privateFee, transactionId);
|
|
1595
|
+
}
|
|
1423
1596
|
return { transactionId };
|
|
1424
1597
|
}
|
|
1425
1598
|
/**
|
|
@@ -1449,7 +1622,7 @@ export class OotleAccount {
|
|
|
1449
1622
|
* grabbing "the first new stealth output" would risk recording the recipient's payment as our
|
|
1450
1623
|
* own. See `PendingShield.ownCommitment`'s doc comment.
|
|
1451
1624
|
*/
|
|
1452
|
-
async sendPrivately(resourceAddress, recipientWalletAddress, amount, maxFee = 100000n, memo, minimumValuePromise = 0n) {
|
|
1625
|
+
async sendPrivately(resourceAddress, recipientWalletAddress, amount, maxFee = 100000n, memo, minimumValuePromise = 0n, feeType = { kind: "transparent" }) {
|
|
1453
1626
|
assertValidMinimumValuePromise(minimumValuePromise, amount);
|
|
1454
1627
|
const accountId = localAccountId(this.index);
|
|
1455
1628
|
const records = await listShieldedOutputs(accountId);
|
|
@@ -1473,18 +1646,18 @@ export class OotleAccount {
|
|
|
1473
1646
|
if (changeAmount > 0n) {
|
|
1474
1647
|
builder = builder.toStealthOutput(createOutput({ destination: ownWalletAddress, amount: changeAmount, resourceAddress }));
|
|
1475
1648
|
}
|
|
1476
|
-
|
|
1649
|
+
builder = builder.toRevealedOutput(dust);
|
|
1650
|
+
const viewSecret = await this.signer.getViewSecret();
|
|
1651
|
+
// `commitments` excluded from the fee UTXO's own selection -- see unshield()'s identical
|
|
1652
|
+
// comment on why (this and unshield are the only two methods with their own multi-UTXO
|
|
1653
|
+
// coin selection of potentially the same resource as the fee).
|
|
1654
|
+
const { transactionId, spec, privateFee } = await this.prepareSignSubmit(builder, provider, account, maxFee, feeType, { viewSecret }, commitments);
|
|
1477
1655
|
// Output 0 is the recipient's; output 1 (only present when there's change) is ours. The
|
|
1478
1656
|
// recipient has no way to discover their new output on their own (no scan-by-view-key API
|
|
1479
1657
|
// exists) -- this commitment must be handed back to the caller so it can be shared with them
|
|
1480
1658
|
// out of band; without it, the payment is invisible to them even though it succeeded on-chain.
|
|
1481
1659
|
const recipientCommitment = extractOutputCommitment(spec, 0);
|
|
1482
1660
|
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
1661
|
await addPendingShield({
|
|
1489
1662
|
transactionId,
|
|
1490
1663
|
accountId,
|
|
@@ -1501,6 +1674,9 @@ export class OotleAccount {
|
|
|
1501
1674
|
for (const commitment of commitments) {
|
|
1502
1675
|
await markShieldedOutputSpent(accountId, commitment);
|
|
1503
1676
|
}
|
|
1677
|
+
if (privateFee && feeType.kind === "private") {
|
|
1678
|
+
await this.recordPrivateFeeSpend(accountId, feeType.feeResourceAddress, privateFee, transactionId);
|
|
1679
|
+
}
|
|
1504
1680
|
}
|
|
1505
1681
|
finally {
|
|
1506
1682
|
await removePendingShield(transactionId);
|
|
@@ -1827,7 +2003,6 @@ async function submitHtlcSpend(params) {
|
|
|
1827
2003
|
const builder = TransactionBuilder.new(params.network, maxEpoch)
|
|
1828
2004
|
.addInput({ substate_id: params.substateId, version: null })
|
|
1829
2005
|
.addInput({ substate_id: params.account, version: null })
|
|
1830
|
-
.feeTransactionPayFromComponent(params.account, params.maxFee)
|
|
1831
2006
|
.addInstruction(
|
|
1832
2007
|
// `statement` is spliced as a raw JSON fragment (see buildHtlcSpendStatement), the same
|
|
1833
2008
|
// technique `@tari-project/ootle`'s own `statementAsWire` uses internally for the piecemeal
|
|
@@ -1839,11 +2014,21 @@ async function submitHtlcSpend(params) {
|
|
|
1839
2014
|
revealed_input_bucket: null,
|
|
1840
2015
|
},
|
|
1841
2016
|
});
|
|
2017
|
+
if (params.privateFee) {
|
|
2018
|
+
for (const instr of params.privateFee.feeInstructions)
|
|
2019
|
+
builder.addFeeInstruction(instr);
|
|
2020
|
+
for (const input of params.privateFee.feeInputs)
|
|
2021
|
+
builder.addInput(input);
|
|
2022
|
+
}
|
|
2023
|
+
else {
|
|
2024
|
+
builder.feeTransactionPayFromComponent(params.account, params.maxFee);
|
|
2025
|
+
}
|
|
1842
2026
|
for (const vaultId of await getVaultIdsForAccount(params.provider, params.account)) {
|
|
1843
2027
|
builder.addInput({ substate_id: vaultId, version: null });
|
|
1844
2028
|
}
|
|
1845
2029
|
const unsignedTx = await resolveTransaction(params.provider, builder.buildUnsignedTransaction());
|
|
1846
|
-
const
|
|
2030
|
+
const extraSigners = params.privateFee ? [params.privateFee.feeSigner] : [];
|
|
2031
|
+
const signed = await signTransaction([params.signer, ...extraSigners], unsignedTx);
|
|
1847
2032
|
const envelope = await sealTransaction(signed);
|
|
1848
2033
|
const transactionId = await submitTransaction(params.provider, envelope);
|
|
1849
2034
|
const response = await withTimeout(pollTransactionResult(params.provider, transactionId), 60_000, params.timeoutLabel);
|
|
@@ -2002,6 +2187,27 @@ export function resolveUnshieldPlan(records, resourceAddress, revealedOutAmount)
|
|
|
2002
2187
|
}
|
|
2003
2188
|
return { commitments: finalSelected.map((r) => r.commitment), remainder: finalTotal - revealedOutAmount };
|
|
2004
2189
|
}
|
|
2190
|
+
/**
|
|
2191
|
+
* Pure planning for `execute()`'s private-fee path: picks the smallest unspent stealth UTXO of
|
|
2192
|
+
* `feeResourceAddress` that alone covers `maxFee`. Deliberately the *smallest* qualifying record
|
|
2193
|
+
* (not the largest, and never several combined) -- `buildPrivateFeeInstructions` only supports a
|
|
2194
|
+
* single fee input (see its own doc comment for why), so this leaves larger shielded balances
|
|
2195
|
+
* untouched for whatever they were shielded for rather than reaching for them first.
|
|
2196
|
+
*/
|
|
2197
|
+
export function selectPrivateFeeUtxo(records, feeResourceAddress, maxFee, exclude = []) {
|
|
2198
|
+
const excluded = new Set(exclude);
|
|
2199
|
+
const candidates = records
|
|
2200
|
+
.filter((r) => !r.spent && !excluded.has(r.commitment) && r.resourceAddress === feeResourceAddress && BigInt(r.amount) > maxFee)
|
|
2201
|
+
.sort((a, b) => {
|
|
2202
|
+
const diff = BigInt(a.amount) - BigInt(b.amount);
|
|
2203
|
+
return diff > 0n ? 1 : diff < 0n ? -1 : 0;
|
|
2204
|
+
});
|
|
2205
|
+
const chosen = candidates[0];
|
|
2206
|
+
if (!chosen) {
|
|
2207
|
+
throw new Error(`No single shielded ${feeResourceAddress} UTXO is large enough to cover a private fee of ${maxFee}. Shield some first (see shield()).`);
|
|
2208
|
+
}
|
|
2209
|
+
return chosen;
|
|
2210
|
+
}
|
|
2005
2211
|
/**
|
|
2006
2212
|
* Pure planning for `sendPrivately()`: selects enough of the caller's unspent records for this
|
|
2007
2213
|
* 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.
|
|
3
|
+
"version": "0.1.9",
|
|
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": [
|
|
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": [
|
|
31
|
+
"files": [
|
|
32
|
+
"dist",
|
|
33
|
+
"vendor"
|
|
34
|
+
],
|
|
26
35
|
"scripts": {
|
|
27
36
|
"build": "tsc -p tsconfig.build.json",
|
|
28
37
|
"typecheck": "tsc --noEmit",
|