taler-typescript-core

Wallet core logic and WebUIs for various components
Log | Files | Refs | Submodules | README | LICENSE

commit b8a1443edcbb68eafd6f16de428a2905af829600
parent b14adbb9d95c074897fac80cd9e252996785f46f
Author: Florian Dold <dold@taler.net>
Date:   Sun,  9 Aug 2026 23:51:10 +0200

wallet-cli: consolidate deposit command

Diffstat:
Mpackages/taler-wallet-cli/src/index.ts | 140++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------
1 file changed, 118 insertions(+), 22 deletions(-)

diff --git a/packages/taler-wallet-cli/src/index.ts b/packages/taler-wallet-cli/src/index.ts @@ -1445,6 +1445,98 @@ async function waitForCreatedTx( } /** + * States at which a deposit no longer needs the CLI to keep running. A + * deposit can take a long time to become final while its wire transfer is + * being tracked, so finalizing is sufficient here. KYC and suspended states + * need action outside the wallet task loop and therefore also end the wait. + */ +const DEPOSIT_FINAL_STATE_PATTERNS: TransactionStatePattern[] = [ + { major: TransactionMajorState.Done, minor: "*", working: "*" }, + { major: TransactionMajorState.Failed, minor: "*", working: "*" }, + { major: TransactionMajorState.Aborted, minor: "*", working: "*" }, + { major: TransactionMajorState.Expired, minor: "*", working: "*" }, + { major: TransactionMajorState.Deleted, minor: "*", working: "*" }, +]; + +const DEPOSIT_USER_ACTION_STATE_PATTERNS: TransactionStatePattern[] = [ + { + major: TransactionMajorState.SuspendedFinalizing, + minor: "*", + working: "*", + }, + { major: TransactionMajorState.Suspended, minor: "*", working: "*" }, + { + major: TransactionMajorState.SuspendedAborting, + minor: "*", + working: "*", + }, + { major: TransactionMajorState.Dialog, minor: "*", working: "*" }, + { major: "*", minor: TransactionMinorState.KycRequired, working: "*" }, + { + major: "*", + minor: TransactionMinorState.KycAuthRequired, + working: "*", + }, +]; + +const DEPOSIT_WAIT_STATE_PATTERNS: TransactionStatePattern[] = [ + ...DEPOSIT_FINAL_STATE_PATTERNS, + { major: TransactionMajorState.Finalizing, minor: "*", working: "*" }, + ...DEPOSIT_USER_ACTION_STATE_PATTERNS, +]; + +interface DepositWaitArgs { + noWait: boolean; + timeout?: string; +} + +/** + * Wait for a newly created deposit until it is final, finalizing, or needs + * action from the user. Returns the exit code for the command. + */ +async function waitForCreatedDepositTx( + ctx: WalletContext, + transactionId: TransactionIdStr, + a: DepositWaitArgs, +): Promise<number> { + if (a.noWait) { + return 0; + } + const timeout = a.timeout != null ? parseTimeoutSpec(a.timeout) : "forever"; + const outcome = await waitForTxState(ctx, { + transactionId, + txState: DEPOSIT_WAIT_STATE_PATTERNS, + timeoutMs: timeout === "forever" ? undefined : timeout, + }); + if (outcome.result === "timeout") { + console.error(`timeout: transaction ${transactionId} did not finish`); + return EXIT_TIMEOUT; + } + console.log( + `transaction ${transactionId} reached state ${formatTxState( + outcome.txState, + )}`, + ); + const unsuccessful = UNSUCCESSFUL_STATE_PATTERNS.some((pat) => + matchTransactionState(outcome.txState, pat), + ); + if (unsuccessful) { + return EXIT_TX_UNSUCCESSFUL; + } + const requiresUserAction = DEPOSIT_USER_ACTION_STATE_PATTERNS.some((pat) => + matchTransactionState(outcome.txState, pat), + ); + if (requiresUserAction) { + const tx = await lookupTxOrNull(ctx, transactionId); + if (tx?.kycUrl != null) { + console.log(`kyc required: ${tx.kycUrl}`); + } + return EXIT_INPUT_REQUIRED; + } + return 0; +} + +/** * Answers that a continuation may need, supplied on the command line * so that it also works without a terminal. */ @@ -2736,55 +2828,59 @@ bankAccountsCli.subcommand("listArgs", "list").action(async (args) => { }); const depositCli = walletCli.subcommand("depositArgs", "deposit", { - help: "Subcommands for depositing money to payto:// accounts", + help: "Deposit money to a payto:// account.", }); depositCli - .subcommand("createDepositArgs", "create") .requiredArgument("amount", clk.AMOUNT) .requiredArgument("targetPayto", clk.STRING) - .flag("wait", ["--wait"], { - help: "Wait until the transaction is in a final state.", + .flag("check", ["--check"], { + help: "Check the deposit without creating it.", + }) + .flag("noWait", ["--no-wait"], { + help: "Exit after creating the deposit instead of waiting for its result.", }) .maybeOption("timeout", ["--timeout"], clk.STRING, { help: "Give up waiting after this duration (e.g. '30s', '5m').", }) .action(async (args) => { + if (args.depositArgs.check) { + await runCliAction(() => + withWallet(args, { lazyTaskLoop: true }, async (wallet) => { + const resp = await wallet.client.call( + WalletApiOperation.CheckDeposit, + { + amount: args.depositArgs.amount, + depositPaytoUri: args.depositArgs.targetPayto, + }, + ); + console.log(`Check deposit result: ${j2s(resp)}`); + }), + ); + return; + } + await runCliAction(() => withWallet(args, { lazyTaskLoop: true }, async (wallet) => { const resp = await wallet.client.call( WalletApiOperation.CreateDepositGroup, { - amount: args.createDepositArgs.amount, - depositPaytoUri: args.createDepositArgs.targetPayto, + amount: args.depositArgs.amount, + depositPaytoUri: args.depositArgs.targetPayto, }, ); console.log(`Created deposit ${resp.depositGroupId}`); // The transaction ID is what the other commands take. console.log(`transaction ${resp.transactionId}`); - return await waitForCreatedTx( + return await waitForCreatedDepositTx( wallet, resp.transactionId, - args.createDepositArgs, + args.depositArgs, ); }), ); }); -depositCli - .subcommand("checkDepositArgs", "check") - .requiredArgument("amount", clk.AMOUNT) - .requiredArgument("targetPayto", clk.STRING) - .action(async (args) => { - await withWallet(args, { lazyTaskLoop: true }, async (wallet) => { - const resp = await wallet.client.call(WalletApiOperation.CheckDeposit, { - amount: args.checkDepositArgs.amount, - depositPaytoUri: args.checkDepositArgs.targetPayto, - }); - console.log(`Check deposit result: ${j2s(resp)}`); - }); - }); - const peerCli = walletCli.subcommand("peerArgs", "p2p", { help: "Subcommands for peer-to-peer payments.", mark: "legacy",