Skip to content

Fix Lightning swap fee estimation, auto-create wallet in 1-click invest flow#776

Merged
dangershony merged 10 commits intomainfrom
fix/mobile-port-regressions
Apr 22, 2026
Merged

Fix Lightning swap fee estimation, auto-create wallet in 1-click invest flow#776
dangershony merged 10 commits intomainfrom
fix/mobile-port-regressions

Conversation

@DavidGershony
Copy link
Copy Markdown
Collaborator

Summary

  • Fix Lightning swap fee budget: the hardcoded 250 vbyte tx size estimate caused BuildInvestmentDraft to fail with insufficient funds. Now uses 252 + (stageCount × 43) based on the deterministic investment tx structure (1 P2WPKH input, 1 P2WSH angor fee, 1 OP_RETURN, N P2TR stages, 1 P2WPKH change). StageCount is passed from the viewmodel which already knows it.
  • Auto-create wallet in 1-click invest flow: adds CreateWalletWithoutPassword(network) to IWalletAppService using the "DEFAULT" encryption key convention (same as FrictionlessSensitiveDataProvider). The invest flow now silently creates a wallet if none exists, so the user doesn't need to visit Funds first.
  • AutomationIds on invest views: 9 AutomationIds added to InvestPageView and InvestModalsView for headless integration testing (Appium-portable).
  • Full on-chain funded integration test: OneClickInvestOnChainFundedTest — navigates to project, types amount, clicks "pay invoice instead", reads address from UI, faucet sends funds, waits for success modal. All assertions via AutomationIds.
  • Fix CreateWalletModal element names: ContinueSpinner and ContinueText naming.

Test plan

  • Run OneClickInvestOnChainFundedTest against docker testnet stack
  • Verify Lightning swap creates correct invoice amount for projects with 3+ stages
  • Verify invest flow works with no pre-existing wallet (auto-creation)
  • Verify existing wallet flows (pay with wallet, Lightning) still work

🤖 Generated with Claude Code

DavidGershony and others added 4 commits April 21, 2026 12:25
The mobile port renamed these AXAML elements (ContinueSpinner -> ContinueBtnSpinner,
ContinueText -> ContinueBtnContent) breaking 6 integration tests that look up
controls by name. Restore the names expected by TestHelpers and code-behind.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…dget

The Lightning swap fee estimation used a hardcoded 250 vbyte estimate for
the investment transaction, but the actual size is ~252 + (N × 43) vbytes
where N is the stage count. For a 4-stage project at 20 sat/vB this meant
budgeting 5,000 sats when the real fee was ~8,500 — causing
BuildInvestmentDraft to fail with insufficient funds on the funding address.

Add StageCount parameter to CreateLightningSwapRequest and compute the
estimate from the deterministic transaction structure (1 P2WPKH input,
1 P2WSH angor fee, 1 OP_RETURN, N P2TR stages, 1 P2WPKH change).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…n test

Add AutomationProperties.AutomationId to key controls in InvestPageView
and InvestModalsView so headless integration tests can find them the same
way Appium would — making tests portable to E2E and resilient to
ViewModel extraction/refactoring.

New test: OneClickInvestOnChainFundedTest exercises the full 1-click
invest on-chain flow with real faucet funding. No wallet pre-funding —
the faucet sends directly to the invoice address shown in the UI,
simulating an external payer scanning the QR code. Asserts against UI
controls (AutomationIds) to verify what the user actually sees.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add CreateWalletWithoutPassword(network) to IWalletAppService — uses the
"DEFAULT" encryption key convention already established by
FrictionlessSensitiveDataProvider, to be replaced by secure storage later.

InvestPageViewModel now calls EnsureWalletExistsAsync() before generating
the receive address in both on-chain and Lightning paths. If no wallet
exists, one is created silently so the user doesn't need to visit the
Funds section before investing.

Update integration test to remove explicit wallet creation — the invest
flow handles it automatically.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
DavidGershony and others added 6 commits April 21, 2026 22:07
New test: OneClickInvestLightningFundedTest exercises the complete
Lightning invest flow end-to-end. Uses a ThunderHub GraphQL client to
pay the BOLT11 invoice from LND-2 at thunderhub.thedude.cloud:4005.

Flow: navigate to project → set amount → submit → pay invoice instead →
switch to Lightning tab → wait for Boltz swap invoice → ThunderHub pays
invoice → Boltz claims on-chain → payment detected → build investment →
success screen.

Also adds:
- ThunderHubClient helper (GraphQL login + pay mutation)
- InvestLightningTab AutomationId

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The InvoiceString TextBlock uses TextTrimming="CharacterEllipsis" which
truncates the BOLT11 invoice. The test now verifies the UI shows an
invoice (prefix check) but reads the full string from the VM's
LightningInvoice property to pass to ThunderHub for payment.

Also fixes the prefix check to match testnet invoices (lntbs prefix).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…se Thub-Auth cookie

ThunderHub uses a hashed account ID (not the display number) and returns
the JWT in a Set-Cookie: Thub-Auth header (not SSOAuth). The client now:
- Queries getServerAccounts to resolve "LND-2" → hash ID
- Extracts the Thub-Auth JWT from the login response Set-Cookie header
- Sends it as a Cookie header on subsequent requests

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Lightning test now reads THUNDERHUB_URL, THUNDERHUB_ACCOUNT, and
THUNDERHUB_PASSWORD from env vars and skips if not set. No credentials
in source.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…b auto-pay

The test now logs the full BOLT11 invoice to the console so it can be
paid manually via ThunderHub UI. The automated ThunderHub payment code
is commented out with a TODO to re-enable when env vars are configured.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
BoltzWebSocketClient only implements IAsyncDisposable, so the
ServiceProvider cannot be disposed synchronously. Check for
IAsyncDisposable first and call DisposeAsync().

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@dangershony dangershony merged commit f4ac82d into main Apr 22, 2026
3 checks passed
@dangershony dangershony deleted the fix/mobile-port-regressions branch April 22, 2026 16:51
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