Module jmwallet.utxo_selector

Interactive UTXO selector TUI.

Provides an fzf-like interface for manually selecting UTXOs with multi-select support using Tab and Enter to confirm.

Functions

def format_utxo_line(utxo: UTXOInfo, max_width: int = 80) ‑> str
Expand source code
def format_utxo_line(utxo: UTXOInfo, max_width: int = 80) -> str:
    """Format a single UTXO for display.

    Args:
        utxo: The UTXO to format
        max_width: Maximum line width

    Returns:
        Formatted string showing mixdepth, amount, confirmations, outpoint, and label
    """
    amount_str = format_amount(utxo.value)
    conf_str = f"{utxo.confirmations:>6} conf"
    md_str = f"m{utxo.mixdepth}"

    # Timelocked indicator
    lock_indicator = " [LOCKED]" if utxo.is_timelocked else ""

    # Truncate txid for display
    outpoint = f"{utxo.txid[:8]}...:{utxo.vout}"

    # Label/note for UTXO type
    label_str = f" ({utxo.label})" if utxo.label else ""

    line = f"{md_str:>3} | {amount_str:>18} | {conf_str} | {outpoint}{lock_indicator}{label_str}"

    if len(line) > max_width:
        line = line[: max_width - 3] + "..."

    return line

Format a single UTXO for display.

Args

utxo
The UTXO to format
max_width
Maximum line width

Returns

Formatted string showing mixdepth, amount, confirmations, outpoint, and label

def select_utxos_interactive(utxos: list[UTXOInfo], target_amount: int = 0) ‑> list[UTXOInfo]
Expand source code
def select_utxos_interactive(
    utxos: list[UTXOInfo],
    target_amount: int = 0,
) -> list[UTXOInfo]:
    """Display an interactive UTXO selector.

    Provides an fzf-like interface for selecting UTXOs:
    - Up/Down or j/k: Navigate
    - Tab/Space: Toggle selection
    - Enter: Confirm selection
    - q/Escape: Cancel
    - a: Select all
    - n: Deselect all
    - g/G: Go to top/bottom

    Args:
        utxos: List of available UTXOs to choose from
        target_amount: Target amount in sats (0 for sweep, used for display)

    Returns:
        List of selected UTXOs, empty if cancelled

    Raises:
        RuntimeError: If not running in a terminal
    """
    # Handle trivial cases without requiring a terminal
    if not utxos:
        return []

    # For multiple UTXOs, we need a terminal
    if not sys.stdin.isatty() or not sys.stdout.isatty():
        # If only one UTXO and no terminal, auto-select it
        if len(utxos) == 1:
            return utxos
        raise RuntimeError("Interactive UTXO selection requires a terminal")

    # Sort UTXOs by mixdepth, then by value (descending)
    sorted_utxos = sorted(utxos, key=lambda u: (u.mixdepth, -u.value))

    return curses.wrapper(_run_selector, sorted_utxos, target_amount)

Display an interactive UTXO selector.

Provides an fzf-like interface for selecting UTXOs: - Up/Down or j/k: Navigate - Tab/Space: Toggle selection - Enter: Confirm selection - q/Escape: Cancel - a: Select all - n: Deselect all - g/G: Go to top/bottom

Args

utxos
List of available UTXOs to choose from
target_amount
Target amount in sats (0 for sweep, used for display)

Returns

List of selected UTXOs, empty if cancelled

Raises

RuntimeError
If not running in a terminal