镜像站点 · 本页由第三方 GitHub 只读镜像提供,非 GitHub 官方站点,不接受任何登录或凭据输入。前往 github.com
Skip to content

No direct way to find a wallet that can pay an invoice #34

Closed

Description

@thebrandonlucas

What happened

The goal was "pay this invoice" (a 1 sat signet lntbs BOLT11). No profile or wallet was
given. To find a wallet, the agent had to:

  1. organizations list --json returned many organizations.
  2. wallets list --org ORG --json for each organization, in a shell loop, with python3
    to flatten names, networks, and balances.
  3. A second python3 pass read the balances[].available objects.

The loop found two wallets, both in one organization. The BTC wallet showed
{"amount": N, "negative": true}, so its balance was negative and it could not pay.
The other was a USD wallet (see 2026-10-14-sub-cent-invoice-from-usd-wallet). Learning
that no wallet could pay a 1 sat invoice took roughly ten tool calls.

Why it was confusing

  • wallets list works on one organization at a time. No command lists the wallets
    across every organization that a login can see.
  • No command answers "which wallet can pay this invoice?" The network from the invoice
    prefix (lntbs is signet or mutinynet), the currency, and a spendable balance all
    matter.
  • A negative balance is a positive amount with negative: true. A quick read of the
    amount can show a funded wallet.
  • In non-interactive use, the wallet picker on feat/cli-interactive-pickers does not
    help.
  • Cheapest fixes, from smallest: show signed balances in table output; add
    wallets list --all-orgs; give payments send --invoice without --wallet an error
    that lists suitable wallets, or says that none can pay and why.

Provenance

  • Found 2026-10-14 during a Claude Code session ("pay this invoice").
  • Branch feat/cli-human-output, commit a060712, voltage 0.1.0 (a060712).
  • Deployment: staging, a login that sees many organizations, only one of which has wallets.

Status on other branches

  • feat/cli-interactive-pickers prompts for a missing organization, environment, or
    wallet on a terminal. It does not help agents and does not filter by ability to pay.
  • feat/cli-error-hints names the next command for missing scope. It does not search
    across organizations.

Reproduction

repro.sh needs a browser login (voltage login --account me --credential-store file).
It shows the per-organization loop that an agent must write.

# docs/issues/2026-10-14-find-a-wallet-that-can-pay/repro.sh
#!/usr/bin/env bash
PIN=a060712
source "$(git rev-parse --show-toplevel)/docs/issues/lib.sh"
build
ACCOUNT=${ACCOUNT:-me}
# Today this needs a loop and a JSON parser; a fix makes it one voltage command.
orgs=$(voltage organizations list --account "$ACCOUNT" --json \
  | python3 -c 'import json,sys; [print(o["id"]) for o in json.load(sys.stdin)["data"]]')
for org in $orgs; do
  voltage wallets list --account "$ACCOUNT" --org "$org" --json 2>/dev/null | python3 -c '
import json, sys
for w in json.load(sys.stdin).get("data") or []:
    for b in w["balances"]:
        a = b["available"]
        print(sys.argv[1], w["id"], w["network"], b["currency"], ("-" if a["negative"] else "") + str(a["amount"]), a["unit"])
' "$org" || true
done
docs/issues/lib.sh
#!/usr/bin/env bash
# Shared setup for issue reproductions. Source it from a repro.sh after setting PIN.
set -euo pipefail
root=$(git rev-parse --show-toplevel)
cd "$root"
head=$(git rev-parse --short HEAD)
if [ "${PIN:-}" != "" ] && [ "$head" != "$PIN" ]; then
  echo "note: HEAD is $head; the issue was found at $PIN (git switch --detach $PIN to match)" >&2
fi
voltage() { "$root/scripts/voltage-staging" "$@"; }
need() { for v in "$@"; do [ -n "${!v:-}" ] || { echo "set $v first" >&2; exit 64; }; done; }
build() { "$root/scripts/voltage-staging" --build >/dev/null 2>&1; }
# Run a command, print its exit code, and never stop the script.
try() { echo "\$ voltage $*" >&2; set +e; voltage "$@"; echo "exit=$?" >&2; set -e; }

Run with nix develop --command ./docs/issues/2026-10-14-find-a-wallet-that-can-pay/repro.sh (see the agent friction log in AGENTS.md, #33).

Progress on master (b25c05d, 2026-10-20)

Done:

Left:

  • List wallets across every organization a login can see, for example wallets list --all-orgs.
  • Find a wallet that can pay a given invoice, from its network, currency, and spendable balance.
  • Off a terminal, payments send --invoice without --wallet lists suitable wallets, or says that none can pay and why.
  • JSON still reports a negative balance as a positive amount with negative: true.

Activity

  1. tee8z commented on Oct 20, 2026

    @tee8z
    Contributor

    Partly helped:

    Still open: nothing lists wallets across organizations (wallets list takes one --org), no command finds a wallet that can pay a given invoice, and a send without --wallet off a terminal does not suggest one.

  2. thebrandonlucas commented on Oct 21, 2026

    @thebrandonlucas
    ContributorAuthor

    Plan (2026-10-21):

    • Off a terminal, payments send --invoice without --wallet: done in the CLI. Without --wallet, the error lists the wallets in scope that match the invoice network (from its prefix) and currency and have a positive balance, or says why none qualify. The list is worded as a shortlist, not a guarantee, because only the backend knows fees, limits and line-of-credit rules.
    • Wallets across every organization (--all-orgs): on hold, waiting for voltagecloud/backend#2296. Each organization needs its own token exchange today, so the CLI would make 2N + 1 calls, and only with a browser login.
    • JSON negative balance (amount + negative: true): closed by decision. This is the API shape, which the CLI passes through, and changing it would break --json consumers. Human tables already show the - sign.
  3. thebrandonlucas commented on Oct 21, 2026

    @thebrandonlucas
    ContributorAuthor

    Update (2026-10-21): voltagecloud/backend#2296 is closed by decision. The backend will not add a cross-organization wallet endpoint, because most users belong to one organization. --all-orgs moves back to the CLI. It sends the per-organization token exchange and GET /organizations/{id}/wallets requests in parallel and merges the results. This works for browser logins only, because an API key is bound to one organization. A related follow-up: make a profile that pins an organization the stronger default, so that most commands never need a cross-organization lookup.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions