Skip to content

fix swap page - #549

Merged
bordalix merged 3 commits into
masterfrom
fix/swap_page
Apr 21, 2026
Merged

bordalix merged 3 commits into
masterfrom
fix/swap_page

Conversation

@bordalix

@bordalix bordalix commented Apr 21, 2026 •

Copy link
Copy Markdown
Collaborator

Fixes values in submarine swap page and direction in reverse swap.

Summary by CodeRabbit

  • Bug Fixes

    • Corrected reverse-swap label to "Reverse Swap" and improved handling of missing swap amounts so displayed values default to 0.
    • Fixed displayed amount/fee/total calculations for submarine and reverse swaps.
  • Tests

    • Expanded end-to-end coverage: added detailed assertions for swap detail fields and values, and a new on-chain (Arkade to Bitcoin) swap test.

@bordalix
bordalix requested a review from pietro909 April 21, 2026 10:17
@coderabbitai

coderabbitai Bot commented Apr 21, 2026 •

Copy link
Copy Markdown
Contributor

Walkthrough

Defaults null reverse-swap onchain amounts to 0 and renames its Kind to "Reverse Swap". For submarine swaps, uses response.expectedAmount for sent amount and derives received amount from a decoded invoice only when valid. Adds/adjusts end-to-end tests asserting updated swap detail fields and values.

Changes

Cohort / File(s) Summary
Boltz Swap UI logic
src/screens/Apps/Boltz/Swap.tsx
Reverse swap: fallback rcvdSats to 0 when response.onchainAmount is null/undefined and change Kind label to "Reverse Swap". Submarine swap: compute sentSats from response.expectedAmount; rcvdSats derived from decoded invoice only if valid.
End-to-end tests
src/test/e2e/swap.test.ts
Removed a timeout; expanded tests to navigate into specific swap details and assert visibility and exact values for multiple fields (Kind, Direction, Status, Amount, Fees, Total) for Lightning (reverse/submarine) and added a new Bitcoin (chain) swap test.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • Kukks
🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title 'fix swap page' is vague and generic, using non-descriptive language that doesn't convey the specific changes made (submarine swap value fixes and reverse swap direction corrections). Consider using a more specific title like 'Fix submarine swap values and reverse swap direction display' to clearly communicate the main changes.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/swap_page

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Apr 21, 2026 •

Copy link
Copy Markdown

Deploying wallet-bitcoin with  Cloudflare Pages  Cloudflare Pages

Latest commit: 2368aec
Status: ✅  Deploy successful!
Preview URL: https://a7fcd01e.wallet-bitcoin.pages.dev
Branch Preview URL: https://fix-swap-page.wallet-bitcoin.pages.dev

View logs

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Apr 21, 2026 •

Copy link
Copy Markdown

Deploying wallet-mutinynet with  Cloudflare Pages  Cloudflare Pages

Latest commit: 2368aec
Status: ✅  Deploy successful!
Preview URL: https://ed8e4082.arkade-wallet.pages.dev
Branch Preview URL: https://fix-swap-page.arkade-wallet.pages.dev

View logs

@ghost ghost left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Approved — UI-only display fixes, all correct

Reviewed the full diff, the complete Swap.tsx file, and the upstream boltz-swap type definitions (CreateSubmarineSwapRequest, CreateSubmarineSwapResponse, CreateReverseSwapRequest, CreateReverseSwapResponse).

Changes reviewed

1. Reverse swap kind label (line 118)
'Chain Swap' → 'Reverse Swap' — clearly a copy-paste bug from the chain swap block above. Fix is correct.

2. Nullish coalescing on reverse swap amounts (lines 113–114)
invoiceAmount ?? 0 and onchainAmount ?? 0 — both fields are typed as number (not optional) in CreateReverseSwapRequest / CreateReverseSwapResponse, so these should never be nullish in practice. Defensive coding, harmless.

3. Submarine swap sent/received swap (lines 130–131)
This is the substantive fix. For a submarine swap (Arkade → Lightning):

  • response.expectedAmount = sats the user sends to the ARK lockup address (includes Boltz fees) → correctly assigned to sentSats
  • decodeInvoice(request.invoice).amountSats = the Lightning invoice amount the recipient gets → correctly assigned to rcvdSats

The old code had these inverted, which made Fees = sentSats - rcvdSats show a negative or incorrect fee. Fix is correct.

Scope assessment

  • No protocol-critical code — purely display logic in the swap detail view
  • No cross-repo impact — no public API, type, or interface changes
  • No security concerns — no key material, signing, or transaction logic touched

LGTM. Ship it.

@ghost ghost left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Approved — UI-only display fixes, all correct

Reviewed the full diff, the complete Swap.tsx file, and the upstream boltz-swap type definitions (CreateSubmarineSwapRequest, CreateSubmarineSwapResponse, CreateReverseSwapRequest, CreateReverseSwapResponse).

Changes reviewed

1. Reverse swap kind label (line 118)
'Chain Swap' → 'Reverse Swap' — clearly a copy-paste bug from the chain swap block above. Fix is correct.

2. Nullish coalescing on reverse swap amounts (lines 113–114)
invoiceAmount ?? 0 and onchainAmount ?? 0 — both fields are typed as number (not optional) in CreateReverseSwapRequest / CreateReverseSwapResponse, so these should never be nullish in practice. Defensive coding, harmless.

3. Submarine swap sent/received swap (lines 130–131)
This is the substantive fix. For a submarine swap (Arkade → Lightning):

  • response.expectedAmount = sats the user sends to the ARK lockup address (includes Boltz fees) → correctly assigned to sentSats
  • decodeInvoice(request.invoice).amountSats = the Lightning invoice amount the recipient gets → correctly assigned to rcvdSats

The old code had these inverted, which made Fees = sentSats - rcvdSats show a negative or incorrect fee. Fix is correct.

Scope assessment

  • No protocol-critical code — purely display logic in the swap detail view
  • No cross-repo impact — no public API, type, or interface changes
  • No security concerns — no key material, signing, or transaction logic touched

LGTM. Ship it.

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Apr 21, 2026 •

Copy link
Copy Markdown

Deploying tmp-boltz-upstream-mainnet-arkade-wallet with  Cloudflare Pages  Cloudflare Pages

Latest commit: 2368aec
Status: ✅  Deploy successful!
Preview URL: https://bc48427d.tmp-boltz-upstream-mainnet-arkade-wallet.pages.dev
Branch Preview URL: https://fix-swap-page.tmp-boltz-upstream-mainnet-arkade-wallet.pages.dev

View logs

@ghost

ghost commented Apr 21, 2026

Copy link
Copy Markdown

Correction to my review above: I was wrong on point 2.

CreateReverseSwapResponse.onchainAmount is typed as onchainAmount?: number (optional) in the current boltz-swap master — so onchainAmount ?? 0 is not merely defensive, it's actually correct and necessary. CreateReverseSwapRequest.invoiceAmount remains number (required), but the response-side field can be undefined.

Sorry for the noise — the nullish coalescing on onchainAmount is the right call.

@ghost ghost left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Iterative review — new commit 78b4239d ("lint")

Reviewed the delta since my last approval. The lint commit removes two ?? 0 null-coalescing guards from sentSats assignments:

  • Reverse swap (swapInfo.request.invoiceAmount): type is non-optional number → guard was unnecessary
  • Submarine swap (swapInfo.response.expectedAmount): type is non-optional number → guard was unnecessary

onchainAmount ?? 0 is correctly retained since that field's type is optional.

All changes remain display-only (swap details page). No protocol-critical code paths affected. LGTM. ✅

@ghost ghost left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Iterative review — new commit 2368aecf ("add tests")

Reviewed the delta since my last review. The new commit adds E2E assertions for swap detail pages (reverse, submarine, chain) and a new chain swap test. Good coverage addition — this directly validates the display fixes from the earlier commits.

One concern

test.setTimeout(60000) removal — src/test/e2e/swap.test.ts:21: The "should receive funds from Lightning" test previously had a 60s timeout. This was removed but the test still does docker exec lnd lncli payinvoice + waitForPaymentReceived + the new detail page assertions. If Playwright's default timeout (30s) isn't enough, this will flake. The refund test at line ~160 still keeps its test.setTimeout(60000). Was this removal intentional?

The new "should send funds to Bitcoin" test (line ~98) also does wallet funding + chain swap + detail verification with no extended timeout — same risk.

Otherwise clean

  • Table component renders testId={title}, so getByTestId('Kind') etc. will resolve correctly. ✓
  • Hardcoded amounts (1992/8/2000 for reverse, 1000/1/1001 for submarine, 2111/164/2275 for chain) are standard for a controlled regtest Boltz environment. ✓
  • No protocol-critical changes in this commit — tests only.

LGTM. Maintaining my approval.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (3)
src/test/e2e/swap.test.ts (2)

138-148: New Bitcoin test misses Preimage and BTC Address label assertions.

The Chain Swap detail table in src/screens/Apps/Boltz/Swap.tsx (lines 99-111) renders both Preimage and BTC Address rows, but this new test only verifies a subset of labels. For parity with the other two swap-detail tests and to catch regressions on those rows, please add visibility checks for them too.

🧪 Proposed addition
   await expect(page.getByText('Direction')).toBeVisible()
   await expect(page.getByText('Date')).toBeVisible()
+  await expect(page.getByText('Preimage')).toBeVisible()
+  await expect(page.getByText('BTC Address')).toBeVisible()
   await expect(page.getByText('Status')).toBeVisible()
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/test/e2e/swap.test.ts` around lines 138 - 148, The test missing
assertions for the Chain Swap detail table should be updated to assert
visibility of the "Preimage" and "BTC Address" rows; in the existing test block
in src/test/e2e/swap.test.ts (the sequence that clicks 'Arkade to Bitcoin' and
checks labels), add two checks using page.getByText('Preimage') and
page.getByText('BTC Address') with await expect(...).toBeVisible() so the test
matches the Swap component (src/screens/Apps/Boltz/Swap.tsx) rendering of those
rows.

58-58: Inconsistent exact flag when matching the "Invoice" label.

Line 58 uses getByText('Invoice', { exact: true }) while the equivalent assertion on line 107 omits exact: true. Today the long lnbcrt… invoice string doesn't contain the word "Invoice" so both happen to resolve, but aligning the locators avoids a flaky strict-mode violation if the value rendering ever changes (e.g. truncation, tooltip text).

🧪 Proposed tweak
-  await expect(page.getByText('Invoice')).toBeVisible()
+  await expect(page.getByText('Invoice', { exact: true })).toBeVisible()

Also applies to: 107-107

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/test/e2e/swap.test.ts` at line 58, The two Playwright locators for the
"Invoice" label are inconsistent—one uses page.getByText('Invoice', { exact:
true }) while the other omits the exact flag—so update the other occurrence in
swap.test.ts to match and make both uses consistent (preferably use
page.getByText('Invoice', { exact: true }) for both) to avoid flaky strict-mode
matches; locate both calls to page.getByText('Invoice', ...) and apply the same
{ exact: true } option.
src/screens/Apps/Boltz/Swap.tsx (1)

130-131: Decode invoice once and guard against amountless invoices.

Two small concerns on the submarine branch:

  1. decodeInvoice is invoked twice for the same invoice — once inside isValidInvoice (see src/lib/bolt11.ts:31-38) and again on line 131. Decoding once avoids redundant work.
  2. If the invoice is amountless, decodeInvoice(...).amountSats can be undefined, which makes rcvdSats undefined and then sentSats - rcvdSats (line 143) evaluates to NaN, rendering a bad "Fees"/"Amount" cell. Submarine swaps are expected to always carry an amount, but a defensive fallback keeps the UI safe.
♻️ Proposed refactor
-    const sentSats = swapInfo.response.expectedAmount
-    const rcvdSats = isValidInvoice(swapInfo.request.invoice) ? decodeInvoice(swapInfo.request.invoice).amountSats : 0
+    const sentSats = swapInfo.response.expectedAmount ?? 0
+    const decoded = isValidInvoice(swapInfo.request.invoice) ? decodeInvoice(swapInfo.request.invoice) : null
+    const rcvdSats = decoded?.amountSats ?? 0
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/screens/Apps/Boltz/Swap.tsx` around lines 130 - 131, Decode the invoice
once and defensively handle amountless invoices: replace the double-call pattern
by first checking isValidInvoice(swapInfo.request.invoice) and, if valid, store
the result of decodeInvoice(swapInfo.request.invoice) in a local (e.g.,
decodedInvoice), then set rcvdSats = decodedInvoice?.amountSats ?? 0; keep
sentSats = swapInfo.response.expectedAmount. Reference: sentSats, rcvdSats,
swapInfo.request.invoice, isValidInvoice, decodeInvoice.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/screens/Apps/Boltz/Swap.tsx`:
- Around line 130-131: Decode the invoice once and defensively handle amountless
invoices: replace the double-call pattern by first checking
isValidInvoice(swapInfo.request.invoice) and, if valid, store the result of
decodeInvoice(swapInfo.request.invoice) in a local (e.g., decodedInvoice), then
set rcvdSats = decodedInvoice?.amountSats ?? 0; keep sentSats =
swapInfo.response.expectedAmount. Reference: sentSats, rcvdSats,
swapInfo.request.invoice, isValidInvoice, decodeInvoice.

In `@src/test/e2e/swap.test.ts`:
- Around line 138-148: The test missing assertions for the Chain Swap detail
table should be updated to assert visibility of the "Preimage" and "BTC Address"
rows; in the existing test block in src/test/e2e/swap.test.ts (the sequence that
clicks 'Arkade to Bitcoin' and checks labels), add two checks using
page.getByText('Preimage') and page.getByText('BTC Address') with await
expect(...).toBeVisible() so the test matches the Swap component
(src/screens/Apps/Boltz/Swap.tsx) rendering of those rows.
- Line 58: The two Playwright locators for the "Invoice" label are
inconsistent—one uses page.getByText('Invoice', { exact: true }) while the other
omits the exact flag—so update the other occurrence in swap.test.ts to match and
make both uses consistent (preferably use page.getByText('Invoice', { exact:
true }) for both) to avoid flaky strict-mode matches; locate both calls to
page.getByText('Invoice', ...) and apply the same { exact: true } option.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: a485200b-4330-4fd2-b3d0-a87aad2d8044

📥 Commits

Reviewing files that changed from the base of the PR and between 4de95fb and 2368aec.

📒 Files selected for processing (2)
  • src/screens/Apps/Boltz/Swap.tsx
  • src/test/e2e/swap.test.ts

@bordalix
bordalix merged commit ba6abab into master Apr 21, 2026
6 checks passed
bordalix added a commit that referenced this pull request Apr 22, 2026
* fix swap page

* lint

* add tests
bordalix added a commit that referenced this pull request Apr 28, 2026
* remove ionic/normalize and ion-button

* rebbit fixes

* feat(wallet): prefix fiat amounts with currency symbols (#535)

Amounts previously rendered with a trailing ISO code (`100.00 USD`, `50.00 EUR`)
now use the common Unicode currency symbol as a prefix: `$100.00`, `€50.00`,
`£25.00`, `¥1,000`. Currencies without a widely-used single-char symbol (CHF,
CNY) keep the trailing-code form, so CNY does not collide with JPY's `¥`.

The new formatters live alongside the existing generic ones:
- `prettyFiatAmount(amount, currency)` and `prettyFiatHide(value, currency)` in
  `src/lib/format.ts` take the `Fiats` enum as a typed parameter.
- The generic `prettyAmount` / `prettyHide` are intentionally left untouched so
  asset tickers (e.g. a stablecoin named `USD`) that flow through those helpers
  keep their trailing-code format.
- Decimals are derived from the currency via `fiatDecimalsFor` in
  `src/lib/fiat.ts` (JPY = 0, others = 2) — shared between `FiatContext` and
  `prettyFiatAmount` so call sites no longer need to pass `fiatDecimals()`.

Touched displays: hero balance (`Balance.tsx`), transaction list rows
(`TransactionsList.tsx`), amount picker (`Keyboard.tsx`), transaction details
(`Details.tsx`), Send/Receive/Notes success screens, and Send form available
caption. Amount input labels (`InputAmount.tsx`) also use the symbol when
available.

Side fix: `Details.tsx` previously called `prettyAmount` without passing
`fiatDecimals()`, hardcoding 2 decimals. JPY now renders as `¥174` / `¥0` in the
transaction details instead of `174.00 JPY` / `0.00 JPY`.

Hero alignment: the eye-toggle button wrapper now omits an empty unit `<Text>`
when the symbol is inlined into the balance, and the button sits inside a
`alignItems: center` flex container next to the unit text — keeping the icon
centered with the smaller ISO code in the CHF case and snug against the balance
in the `$7.30` case.

Tests:
- New coverage for `prettyFiatAmount` and `prettyFiatHide` in
  `src/test/lib/format.test.ts` (symbol + trailing-code + JPY 0-decimals).
- Existing `prettyAmount` / `prettyHide` tests unchanged — the generic helpers
  still behave the same.
- UI assertions updated to match the new rendered text:
  `src/test/screens/wallet/index.test.tsx` → `$0.00`
  `src/test/e2e/init.test.ts` → `$0.00`
  `src/test/e2e/keyboard.test.ts` → symbol-prefix regex

Co-authored-by: Sahil Chaturvedi <sahilc0@users.noreply.github.com>
Co-authored-by: João Bordalo <bordalix@users.noreply.github.com>

* Upgrade ts-sdk 0.4.16 - boltz-swap 0.3.17 (#539)

* reverse logs in csv export (#546)

* reverse logs in csv export

* fix

* avoid mutating the React state array

* Extra tests (#548)

* close copy sheet after copy

* lint

* test swaps in receive

* Prevent swap after receival (#547)

* Prevent swap after receival

* fix

* more removals

* increase viewport size in tests

* more removals

* more removals

* fixes

* fixes

* fixes

* fix

* remove ion-refresher

* Merge branch 'master' into remove_ion-button

* fix swap page (#549)

* fix swap page

* lint

* add tests

* Upgrade ts-sdk 0.4.17 - boltz-swap 0.3.18 (#551)

* fix(receive): add spacing between button row and share button (#552)

The vertical gap between the Add amount/Copy buttons and the Share
button was too tight. Changed FlexCol gap from 0 to 0.75rem.

Co-authored-by: Sahil Chaturvedi <sahilc0@users.noreply.github.com>

* Revert "Upgrade ts-sdk 0.4.17 - boltz-swap 0.3.18 (#551)" (#553)

This reverts commit 1752cc8.

* Upgrade ts-sdk 0.4.17 - boltz-swap 0.3.18 (#551)

* Revert "Upgrade ts-sdk 0.4.17 - boltz-swap 0.3.18 (#551)" (#553)

This reverts commit 1752cc8.

* Upgrade ts-sdk 0.4.18 - boltz-swap 0.3.19 (#554)

* add git commit to chatwoot custom attributes (#563)

* add lnurl tests (#564)

* lint

* add lnurl tests

Co-authored-by: Copilot <copilot@github.com>

---------

Co-authored-by: Copilot <copilot@github.com>

* fix(ui): top-align tx history rows when assets are present (#537)

* fix(ui): top-align tx history rows when assets are present

When a transaction has assets, the right column (sats + fiat + asset
info) is taller than the left column (icon + label + date). The outer
FlexRow used alignItems 'center' by default, pushing the sats amount
above the label text. Switch to alignItems 'start' so both columns
begin at the same vertical position.

Also adds explicit 'between' to the outer FlexRow and removes a dead
alignItems on the non-flex wrapper div.

* show only 2 coins in the rigth side of a tx in txs list (#550)

---------

Co-authored-by: João Bordalo <bordalix@users.noreply.github.com>

* final removal

Co-authored-by: Copilot <copilot@github.com>

* Upgrade ts-sdk 0.4.19 - boltz-swap 0.3.20 (#565)

* lint

* fix box-sizing

Co-authored-by: Copilot <copilot@github.com>

* .label styles

Co-authored-by: Copilot <copilot@github.com>

* fix branta lint error (#568)

* works on refresher

Co-authored-by: Copilot <copilot@github.com>

* fix unit tests

Co-authored-by: Copilot <copilot@github.com>

* remove ionic/react

* fix label alignment

* DRY: AssetCard

Co-authored-by: Copilot <copilot@github.com>

* fix inputAmount

---------

Co-authored-by: Sahil Chaturvedi <sahilchaturvedi@protonmail.com>
Co-authored-by: Sahil Chaturvedi <sahilc0@users.noreply.github.com>
Co-authored-by: pietro909 <2094604+pietro909@users.noreply.github.com>
Co-authored-by: Copilot <copilot@github.com>
Co-authored-by: Andrew Camilleri <evilkukka@gmail.com>
@coderabbitai coderabbitai Bot mentioned this pull request Apr 29, 2026
1 of 2 tasks
@bordalix
bordalix deleted the fix/swap_page branch May 18, 2026 16:15
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