feat(cli): invoice return — bulk + nametag (depends on sphere-sdk #405)#39
Merged
Conversation
added 3 commits
June 5, 2026 16:23
== Three input shapes == sphere invoice return <id> # refund every sender sphere invoice return <id> --recipient @alice # refund all to @alice sphere invoice return <id> --recipient @alice --asset 3 UCT # refund exactly Forms without --asset delegate to sphere-sdk's new AccountingModule.returnAllInvoicePayments (depends on sphere-sdk PR #404 / branch feat/accounting-return-all-invoice-payments). That method iterates getInvoiceStatus's senderBalances internally — so operators don't need to fish per-send DIRECT://… addresses out of invoice status JSON. This particularly matters for masked-predicate sends, where the on-chain sender is a one-time DIRECT://… the user CANNOT guess from their wallet identity. == --recipient resolution == All three forms now resolve @NameTag / chain pubkey / alpha1 → DIRECT:// before SDK handoff, matching the pattern `invoice deliver --to` and `invoice create --target` already use. Previously the recipient was passed verbatim, which broke with @NameTag input (the SDK uses it as a literal address-string lookup key). == Output == Form A (single explicit refund) — single-row shape: Return payment result: id : <transferId> status : pending recipient : DIRECT://... amount : 3 UCT Forms B/C (bulk) — multi-row shape: Return payment results: 2 refund(s) submitted: [0] 3 UCT → DIRECT://0000abcd... id : <transferId> status : pending [1] 4 UCT → DIRECT://0000efgh... id : <transferId> status : pending The renderer auto-detects via `Array.isArray(p.refunds)`. == Verification == - npx tsc --noEmit: clean - npx vitest run: 126/126 pass (UX static-guard now green) - npx tsup: clean ESM + CJS + DTS build - Smoke: `sphere invoice return --help` shows the new 3-form help block; the three forms each error sensibly on validation paths. == Out of scope == - --asset without --recipient is rejected with a clear error pointing to either dropping --asset (bulk refund) or adding --recipient (explicit refund). This is intentional: refunding a specific amount without specifying the recipient is ambiguous. - `--asset-index` and `--target-index` selectors stay unimplemented (single-asset single-target invoices are the only shape exercised today; file a follow-up if multi-asset invoices become real).
The bulk-return branch added in this PR's src/legacy/legacy-cli.ts calls `sphere.accounting!.returnAllInvoicePayments(...)`, a method that doesn't exist on AccountingModule before sphere-sdk #405. The prior SHA pin (d949844 = PR #402) leaves CI failing typecheck with "Property 'returnAllInvoicePayments' does not exist on type 'AccountingModule'". Bump to 0e3290a (sphere-sdk main post-#405).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds three input shapes for
sphere invoice return:The first two delegate to
sphere-sdkPR #405's newAccountingModule.returnAllInvoicePayments(invoiceId, options?). That SDK method iteratesgetInvoiceStatus().senderBalancesand refunds every attributed payment to its recorded sender — so operators don't have to fish per-send DIRECT://… addresses out of invoice status JSON. Particularly important for masked-predicate sends (the privacy default), where the on-chain sender is a one-time DIRECT://… the user cannot guess from their wallet's identity.The third (explicit) shape also gains
@nametagresolution for--recipient— same asinvoice deliver --toandinvoice create --target. Previously--recipientwas passed verbatim to the SDK, so@alicewould break withReturn amount X exceeds sender net balance 0(the SDK did a literal-string lookup against the recorded DIRECT://…).Output shape
Form A (single explicit refund) — single-row:
Forms B/C (bulk) — multi-row:
The renderer auto-detects via
Array.isArray(p.refunds). Both shapes available under--json.Dependencies
feat/accounting-return-all-invoice-payments) — addsAccountingModule.returnAllInvoicePayments. This PR's forms B and C call it.This PR is the CLI surface that exposes #405's primitive.
Verification
npx tsc --noEmit— cleannpx vitest run— 126/126 pass (UX static-guard now green)npx tsup— ESM + CJS + DTS build cleansphere invoice return --help— shows all 3 forms with examples + notes blockLive testnet end-to-end exercise of form C (no flags) in the new accounting soak (sphere-sdk PR #406): bob's bulk-refund correctly drains 3 UCT and credits alice's wallet. The token-level flow is exact.
(There's a separate sphere-sdk attribution bug #404 that prevents the SDK's invoice ledger from reflecting masked-predicate refunds — that's documented as a follow-up; it doesn't block this PR since the bug is upstream of the CLI wrapper.)
Out of scope
--assetwithout--recipientis rejected with a clear error pointing to either form. Refunding a specific amount without specifying the recipient is ambiguous.--asset-index/--target-indexselectors stay unimplemented — single-asset single-target invoices are the only shape exercised today.Related
fix/issue-36-invoice-pay-human-units) — partner PR for the human-unit--amountoninvoice pay. The accounting soak exercises both.fix/sphere-namespace-bare-help) — partner PR forsphere invoice(no sub) help.