Skip to content

fix(cash-wallet): route recipient wallet ids in intraledger send mutations - #470

Merged
islandbitcoin merged 3 commits into
mainfrom
fix/intraledger-recipient-cash-wallet-routing
Aug 8, 2026
Merged

fix(cash-wallet): route recipient wallet ids in intraledger send mutations#470
islandbitcoin merged 3 commits into
mainfrom
fix/intraledger-recipient-cash-wallet-routing

Conversation

@islandbitcoin

Copy link
Copy Markdown
Contributor

Problem

Repeated Transfer — failed events in the #flash-activity ops feed: MismatchedCurrencyForWalletError, always the same wallet pair, small USD amounts, reason: error-return.

Root cause: post-cutover, intraLedgerUsdPaymentSend routes the sender's wallet through cash-wallet routing (resolveCashWalletMutationWalletIdForAccount, legacy USD → USDT) but passes the recipient's wallet id through raw. A client that doesn't send x-flash-client-capabilities: cash-wallet-usdt-v1 (pre-0.6.0 app, scripts, or any hop that strips the header) resolves a recipient via accountDefaultWallet and gets handed the recipient's legacy USD wallet id by the compat presentation. The send then arrives as USDT sender wallet → USD recipient wallet and is rejected deterministically at send-intraledger.ts (senderWallet.currency !== recipientWallet.currency). Every intraledger send from a legacy client fails, 100% of the time.

Fix

Wire resolveCashWalletRecipientMutationWalletId — already used by lnUsdInvoiceCreateOnBehalfOfRecipient — into:

  • intraLedgerUsdPaymentSend
  • intraLedgerPaymentSend

It resolves a legacy USD recipient wallet id to the recipient account's active settlement (USDT) wallet, so funds settle where the compat read-side already points: the UsdWallet.balance and transactions resolvers alias the legacy wallet to the settlement wallet, so legacy-app recipients see the funds. Current-client ids pass through unchanged (no-op).

Tests

  • New mutation-level unit specs for both mutations (mirroring ln-usd-invoice-create-on-behalf-of-recipient.spec.ts):
    • recipient id is routed and the routed id is what reaches Payments.intraledgerPaymentSendWalletIdFor{Usd,Btc}Wallet
    • routing errors return status: failed without initiating a payment
  • yarn tsc-check clean, eslint clean, unit suites green.

🤖 Generated with Claude Code

https://claude.ai/code/session_01AP1XZs5anZjsHYgGRGVyDT

bobodread876 and others added 3 commits August 7, 2026 22:20
…tions

Post-cutover, intraLedgerUsdPaymentSend routed only the sender's wallet
through cash-wallet routing while passing the recipient wallet id raw.
A legacy client (no x-flash-client-capabilities header) resolves a
recipient via accountDefaultWallet, receives the legacy USD wallet id,
and every send then fails deterministically with
MismatchedCurrencyForWalletError (USDT sender wallet vs USD recipient
wallet) — seen repeatedly in the ops activity feed.

Wire resolveCashWalletRecipientMutationWalletId (already used by
lnUsdInvoiceCreateOnBehalfOfRecipient) into intraLedgerUsdPaymentSend
and intraLedgerPaymentSend so legacy USD recipient ids resolve to the
recipient account's active settlement (USDT) wallet, matching the
read-side aliasing of balances and history.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AP1XZs5anZjsHYgGRGVyDT
…s on routing failures

Review follow-ups:
- Add a composition test exercising the real recipient resolver through
  the real presentation/routing logic (repos stubbed): a legacy-compat
  client submitting a legacy USD recipient id post-cutover resolves to
  the USDT settlement wallet — the exact production incident scenario.
- Emit an ops-feed event when sender/recipient cash-wallet routing fails
  in the intraledger mutations, so routing failures stay visible in the
  #flash-activity feed instead of returning to the client silently.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AP1XZs5anZjsHYgGRGVyDT
Review follow-up: the BTC intraledger call mixed the raw GraphQL input
(senderWalletId: walletId) with checked/routed ids; use the validated
senderWalletId from checkedToWalletId like the rest of the call.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AP1XZs5anZjsHYgGRGVyDT
@islandbitcoin
islandbitcoin merged commit ec3af04 into main Aug 8, 2026
15 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants