feat: pay onchain address from spending balance#1101
Draft
coreyphillips wants to merge 2 commits into
Draft
Conversation
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.
Description
Stacked on
feat/savings-swap. Targets that branch, notmaster.Sending to an onchain address with an amount larger than the Savings balance currently dead-ends on an "insufficient savings" toast, even when the Spending balance could cover it. This adds a fallback: pay the address out of Spending through a Boltz reverse swap, using the recipient's address as the swap claim address so Boltz's payout lands on them directly.
The review screen stays as it is apart from one extra item. "From" reads Spending, and
Confirming ingives way to a single Service fee cell covering Boltz's percentage plus both miner fees, mirroringSavingsConfirmScreen.SwapRepoowns the swap business logic and returnsResult<T>: quoting, bounds, and the pay-then-await-claim orchestration.TransferViewModelis left on its existing directBoltzServiceuse.boltzCreateReverseSwapprices from the Lightning invoice amount, while a send starts from what the recipient must receive, so the quote inverts Boltz's forward formula. It usesceilon the percentage fee to matchReverseFees::boltzin boltz-client, not theroundToLongthe savings quote uses. The service fee shown isinvoiceSat - recipientSat.payInvoiceis ever called.SendMethodgains aSWAPvalue. The amount screen caps input at what a swap can deliver, and the send falls back to Savings automatically once the amount fits there again.isSavingsSwapEnableddev toggle viaBoltzService.isSwapEnabled(), so nothing changes for users yet.Not in scope, worth a follow-up:
LightningActivityhas no address field. A synthetic onchain row keyed on the claim txid would never confirm, since ldk-node never sees that transaction.BoltzSwapcarries no intent field, so a send swap and a savings swap are indistinguishable in history.boltzStartSwapUpdateswas started with, so the send has no per-payment speed choice. The Fee and speed cell is not editable for a swap.QA Notes
Manual QA is mainnet only:
Env.isSwapSupportedisnetwork == Network.BITCOIN, and Boltz's mainnet reverse minimum is around 50k sat. Use a wallet with an open channel holding well above that and a near-empty onchain balance, then enable Dev Settings -> Enable Savings Swap.InsufficientSavingsToastand go nowhere. Normal in-balance onchain sends and normal Lightning sends must be unchanged.Tests: new
SwapRepoTest.ktcovers the inverse fee math (exact round-trip, ceil boundary, min/max limits, spendable balance, routing), the pre-payment abort gate, and the claimed/error/timeout outcomes.AppViewModelSendFlowTest.ktgains coverage for switching into and out of a swap send, rejecting amounts a swap cannot deliver, and dispatching the confirm toSwapRepo.Ran
./gradlew compileDevDebugKotlin testDevDebugUnitTest detekt --rerun-tasks, all green.