import { Transaction as BsvTransaction, InternalizeActionArgs } from '@bsv/sdk'; import { StorageProvider } from '../StorageProvider'; import { AuthId, StorageInternalizeActionResult } from '../../sdk/WalletStorage.interfaces'; /** * Record of a spent-input transition this internalize call performed. * Carries enough state to roll back the exact change via * {@link restoreInputsToSpendable}: which row, and whether we touched * `spentBy` (only true for rows owned by the internalizing user). */ export interface SpentInputTransition { outputId: number; /** true if the call set spentBy; false if only spendable was flipped. */ setSpentBy: boolean; } /** * Mark every storage row at each consumed-input outpoint as spent — across * all users that track that outpoint. An on-chain spend invalidates the UTXO * for every wallet that references it, not just the wallet that called * `internalizeAction`. Returns the per-row transitions so the caller can * roll back via {@link restoreInputsToSpendable} on broadcast failure. * * Cross-user semantics: * - For rows owned by `userId` (the internalizing user) we also set * `spentBy=transactionId`. They own the corresponding transaction * record; the FK target exists in their scope. * - For rows owned by other users we set `spendable=false` and leave * `spentBy` untouched. `spentBy` references the owning user's * `transactions.transactionId`, and that user has no record of our * transaction yet — they'd need to internalize it themselves to get * one. Leaving `spentBy` undefined keeps the field consistent with * "this UTXO is spent, but my wallet didn't witness the spend". * * Mirrors what `createAction` does for wallet-originated transactions and * what `TaskUnFail.unfailReq` does when reviving a previously-failed tx. * Without this, an externally-broadcast tx that consumed user UTXOs would * leave those UTXOs as phantom spendable rows, picked up by subsequent * `createAction` fund selection. * * Idempotent: rows already `spendable=false` are skipped. Rows already * `spentBy` a different transaction are also skipped (a competing spend * has been recorded; do not overwrite). */ export declare function markUserInputsSpent(storage: StorageProvider, userId: number, tx: BsvTransaction, transactionId: number): Promise; /** * Revert the spendable=true → false transitions performed by * {@link markUserInputsSpent}. Used when an internalize call's downstream * broadcast fails non-fatally so the caller can retry with the same UTXOs. * * Only undoes what was changed: `spendable=true` always; `spentBy=undefined` * only when the original transition set it. Avoids clobbering a competing * spent-by on cross-user rows. * * The downstream `attemptToPostReqsToNetwork` path also calls * `updateTransactionStatus('failed')` on doubleSpend/invalidTx outcomes, * which independently restores same-user inputs (via * `EntityTransaction.getInputs` filtered by userId). This explicit rollback * covers cross-user rows and any path where the downstream restore did not * run, and is a no-op when the downstream restore already happened. */ export declare function restoreInputsToSpendable(storage: StorageProvider, transitions: SpentInputTransition[]): Promise; /** * Internalize Action allows a wallet to take ownership of outputs in a pre-existing transaction. * The transaction may, or may not already be known to both the storage and user. * * Two types of outputs are handled: "wallet payments" and "basket insertions". * * A "basket insertion" output is considered a custom output and has no effect on the wallet's "balance". * * A "wallet payment" adds an outputs value to the wallet's change "balance". These outputs are assigned to the "default" basket. * * Processing starts with simple validation and then checks for a pre-existing transaction. * If the transaction is already known to the user, then the outputs are reviewed against the existing outputs treatment, * and merge rules are added to the arguments passed to the storage layer. * * The existing transaction's `status` determines what the merge path does next: * - `'unproven'`, `'completed'`, or `'sending'`: outputs are merged into the existing record. The transaction status is left as-is. * The `'sending'` case covers a transaction this wallet already signed and handed to broadcast processing, but * whose proven_tx_req has not yet been advanced by the normal monitor/posting flow. * - `'nosend'`: an ambiguous case. The transaction was created with `noSend: true` and may have been externally * broadcast, may be sitting in a sendWith chain, or may be stuck mid-flight. The merge path treats the * `internalizeAction` call as explicit authorization to advance the lifecycle. Specifically: `transactions.status` * is promoted to `'completed'` (when a BUMP is included in the BEEF) or `'unproven'` (otherwise), and the * `proven_tx_req` is moved out of `'nosend'` so Monitor's standard proof-fetching flow can finalize it. * This makes the `internalizeAction` semantics consistent regardless of whether the originator shares the * same storage as the internalizer or not. * - Any other status: an error. * * When the transaction already exists, the description is updated. The isOutgoing sense is not changed. * * "basket insertion" Merge Rules: * 1. The "default" basket may not be specified as the insertion basket. * 2. A change output in the "default" basket may not be target of an insertion into a different basket. * 3. These baskets do not affect the wallet's balance and are typed "custom". * * "wallet payment" Merge Rules: * 1. Targetting an existing change "default" basket output results in a no-op. No error. No alterations made. * 2. Targetting a previously "custom" non-change output converts it into a change output. This alters the transaction's `satoshis`, and the wallet balance. */ export declare function internalizeAction(storage: StorageProvider, auth: AuthId, args: InternalizeActionArgs): Promise; //# sourceMappingURL=internalizeAction.d.ts.map