Skip to content

Use VtxoManager instance from service worker - #442

Merged
pietro909 merged 9 commits into
masterfrom
create-vtxo-manager-instance
Mar 27, 2026
Merged

pietro909 merged 9 commits into
masterfrom
create-vtxo-manager-instance

Conversation

@pietro909

@pietro909 pietro909 commented Mar 17, 2026 •

Copy link
Copy Markdown
Contributor

Use VtxoManager from the Service Worker wallet, and use that instance instead of creating a new one on every call.

Replaces #410
Depends on #450

Possible behavioral changes

Orphan VTXO recovery [preserved]

The SDK's getExpiringVtxos() covers the orphan case and more. It returns VTXOs matching any of:

  • Expiring soon (within threshold)
  • isSpendable && isExpired — this is the orphan case (unspent + expired)
  • isRecoverable — swept + unspent
  • isSubdust — below dust amount (bonus the old code didn't have)

Sentry error capture [changed, but preserved]

The old captureSettleError() was sending settle errors to Sentry with rich context (amount, dust threshold, inputs, wallet address). Updated with:

  • Startup renewal (wallet.tsx:357): captures to Sentry with tag renewVtxos:startup
  • settlePreconfirmed (wallet.tsx:453): now wraps in try/catch, captures to Sentry with tag renewVtxos:settlePreconfirmed, then re-throws so callers still see the error
  • handleRollover (Vtxos.tsx:141): added Sentry.captureException in the existing catch block with tag renewVtxos:handleRollover

Risky behavioral changes (postponed)

settlePreconfirmed

The function name says "settle" (on-chain) but now calls vtxoManager.renewVtxos() which does a virtual renewal (off-chain). Are these supposed to be the same thing, or was the on-chain settle intentional?

thresholdMs no longer passed to renewVtxos()

Wallet's threshold (wallet.thresholdMs): computed dynamically as batchLifetimeMs * 10 / 100 — i.e., 10% of the batch lifetime. For example, if the batch lifetime is 7 days, the threshold would be ~16.8 hours.

SDK's default (renewVtxos() with no args): falls through to DEFAULT_RENEWAL_CONFIG.thresholdMs = 3 days (since no renewalConfig or settlementConfig is passed by the wallet)

These don't match. The wallet was using a dynamic, much shorter threshold (10% of batch lifetime), while the SDK defaults to a static 3 days. The renewVtxos() call will renew VTXOs that are within 3 days of expiry, whereas the old code would only renew those within ~10% of the batch lifetime.

Summary by CodeRabbit

  • Refactor

    • VTXO handling now uses a dedicated manager for more consistent detection and processing of expiring/recoverable virtual coins.
  • UI / Wallet

    • Wallet context and related screens now integrate the manager, improving load guards and ensuring renewal/settlement actions run with proper context.
  • Bug Fix / Reliability

    • Improved error reporting and safer rollover/settlement flows to reduce failures and improve recoverability.

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Mar 17, 2026 •

Copy link
Copy Markdown

Deploying wallet-mutinynet with  Cloudflare Pages  Cloudflare Pages

Latest commit: 349b48a
Status: ✅  Deploy successful!
Preview URL: https://75b0862a.arkade-wallet.pages.dev
Branch Preview URL: https://create-vtxo-manager-instance.arkade-wallet.pages.dev

View logs

@pietro909 pietro909 changed the title Create vtxo manager instance Use VtxoManager instance from service worker Mar 17, 2026
@pietro909
pietro909 requested review from bordalix and louisinger and removed request for bordalix March 17, 2026 17:28
@Kukks

Kukks commented Mar 17, 2026

Copy link
Copy Markdown
Contributor

@arkanaai review

@ghost

ghost commented Mar 17, 2026

Copy link
Copy Markdown

Review

What & Why: Stops instantiating a fresh VtxoManager on every call by grabbing the single instance from the SW (svcWallet.getVtxoManager()) at wallet load time and wiring it through WalletContext. Depends on arkade-os/ts-sdk#352 (merged today) which exposes getVtxoManager() and adds renewVtxos(). Net: -75/+26, deletes vtxo.ts, removes settleVtxos/renewCoins from asp.ts.

Questions / nits:

  • Orphan VTXO handling dropped? vtxo.ts included getOrphanVtxos — vtxos that are expired, unspent, and not swept. Is this case now handled inside the SDK's VtxoManager, or silently dropped? Worth confirming.

  • Misleading error message in settlePreconfirmed (wallet.tsx:454):

    if (!vtxoManager) throw new Error('Service worker not initialized')

    Guard is on vtxoManager, not svcWallet — error text is wrong.

  • thresholdMs in renewVtxos() — settlePreconfirmed and handleRollover now call vtxoManager.renewVtxos() without passing wallet.thresholdMs. Is the threshold baked into the SW-side VtxoManager at init time via ts-sdk#352, or is it using a default?

Otherwise looks clean. Replaces #410.

@coderabbitai

coderabbitai Bot commented Mar 18, 2026 •

Copy link
Copy Markdown
Contributor

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Threads an injected IVtxoManager through VTXO settlement flows: adds IVtxoManager imports and state, removes the old vtxo helper, updates signatures for getInputsToSettle/settleVtxos/renewCoins, and updates provider and UI call sites to pass vtxoManager.

Changes

Cohort / File(s) Summary
Core settlement lib
src/lib/asp.ts
Imported IVtxoManager; updated signatures for getInputsToSettle, settleVtxos, and renewCoins to accept vtxoManager; switched to using vtxoManager for expiring VTXO retrieval and forwarded it through flows.
VTXO helper removal
src/lib/vtxo.ts
Removed getExpiringAndRecoverableVtxos and getOrphanVtxos; responsibilities replaced by IVtxoManager.
Wallet provider integration
src/providers/wallet.tsx
Added vtxoManager state and provider value; initialize from svcWallet; updated functions to supply vtxoManager to renewal/settlement calls.
UI consumers
src/screens/Settings/Vtxos.tsx, src/screens/Wallet/Transaction.tsx
Consume vtxoManager from context; updated calls to getInputsToSettle/settleVtxos; updated effect deps and guards; added Sentry error capture in Vtxos handler.
CI workflow
.github/workflows/playwright.yml
Reduced Playwright job timeout from 60 to 30 minutes (timeout-only change).

Sequence Diagram(s)

sequenceDiagram
    autonumber
    actor User
    participant UI as "Settings / Transaction UI"
    participant WP as "WalletProvider"
    participant ASP as "asp lib"
    participant VM as "IVtxoManager"

    User->>UI: trigger rollover / settle action
    UI->>WP: read wallet, svcWallet, vtxoManager
    UI->>ASP: getInputsToSettle(wallet, vtxoManager, thresholdMs)
    ASP->>VM: getExpiringVtxos(thresholdMs)
    VM-->>ASP: expiring vtxos
    ASP-->>UI: inputs to settle
    UI->>ASP: settleVtxos(wallet, vtxoManager, dustAmount, thresholdMs)
    ASP->>VM: execute settlement / queries
    VM-->>ASP: progress/result
    ASP-->>UI: settlement result
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • soft settle #171: Modifies the same settlement functions (getInputsToSettle, settleVtxos, renewCoins) and related expiring-VTXO logic.
  • add vtxo manager #168: Integrates a VTXO manager into the wallet provider and wires it into renewal/settlement flows.
  • Fix coin control #229: Changes signatures and propagation for settlement helpers; overlaps with the IVtxoManager threading in this PR.

Suggested reviewers

  • bordalix
  • louisinger
  • altafan
🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

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.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically describes the main change: propagating VtxoManager from the service worker through the codebase to replace per-call instantiation.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch create-vtxo-manager-instance

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 Mar 18, 2026 •

Copy link
Copy Markdown

Deploying wallet-bitcoin with  Cloudflare Pages  Cloudflare Pages

Latest commit: 349b48a
Status: ✅  Deploy successful!
Preview URL: https://f0aa9eb5.wallet-bitcoin.pages.dev
Branch Preview URL: https://create-vtxo-manager-instance.wallet-bitcoin.pages.dev

View logs

@ghost

ghost commented Mar 18, 2026

Copy link
Copy Markdown

Iterative review — new commits 7a44ec60, 0b865fcd

Good direction: settleVtxos/renewCoins are preserved with their original settle-on-chain semantics, thresholdMs is properly passed through, and the VtxoManager instance is reused from the service worker instead of recreated per call.

Bug: missing Sentry import in Vtxos.tsx

Sentry.captureException is called at line ~142 in handleRollover but @sentry/react is never imported in this file. This will throw a ReferenceError at runtime when a rollover fails — silently swallowing the original error and breaking the catch block.

Fix: add import * as Sentry from '@sentry/react' to the imports.

Everything else in this iteration looks clean — IVtxoManager threading through the call chain is consistent, useEffect dependency arrays are updated correctly, and the deleted vtxo.ts helper is fully replaced by the SDK's VtxoManager.getExpiringVtxos().

@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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/screens/Settings/Vtxos.tsx`:
- Around line 142-144: The file is calling Sentry.captureException in the
renewVtxos:handleRollover error handler but never imports Sentry, causing a
runtime error; add the missing import (e.g., import * as Sentry from
'@sentry/react' or import Sentry from '@sentry/react' depending on project
style) at the top of the file so the Sentry identifier used in
renewVtxos:handleRollover is defined.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: e71e9f59-ee2f-4d51-9169-fc7239fff351

📥 Commits

Reviewing files that changed from the base of the PR and between 44e0b80 and 0b865fc.

📒 Files selected for processing (5)
  • src/lib/asp.ts
  • src/lib/vtxo.ts
  • src/providers/wallet.tsx
  • src/screens/Settings/Vtxos.tsx
  • src/screens/Wallet/Transaction.tsx
💤 Files with no reviewable changes (1)
  • src/lib/vtxo.ts

Comment thread src/screens/Settings/Vtxos.tsx Resolved
@pietro909
pietro909 requested a review from altafan March 18, 2026 03:30
@ghost

ghost commented Mar 18, 2026

Copy link
Copy Markdown

🔍 Arkana — Iterative Review (commit 1f999a8)

Change: +1 line — adds the missing import * as Sentry from '@sentry/react' in Vtxos.tsx.

✅ This fixes the issue flagged in my previous review — the Sentry.captureException() call in the rollover failure handler would have thrown a ReferenceError without this import.

No other changes. PR looks good from a code review perspective — VTXO manager migration is clean, settleVtxos/renewCoins semantics preserved, threshold threading correct.

@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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/screens/Settings/Vtxos.tsx`:
- Around line 113-121: The effect currently calls getInputsToSettle as a
fire-and-forget promise and omits wallet.thresholdMs from the dependency array,
which can produce stale state and unhandled rejections; change the effect to an
async function (or async IIFE) that awaits getInputsToSettle, wraps the call in
try/catch to handle and log errors, and uses a cancellation flag or
AbortController to ignore results from stale responses before calling
setHasBoardingUtxosToSettle, setHasInputsToSettle, setHasVtxosToSettle and
setAboveDust; also include wallet.thresholdMs (or the correct threshold prop) in
the effect dependency array so the effect reruns when threshold changes.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 0eb16f6a-7250-4327-ab9e-f20b845e5b4d

📥 Commits

Reviewing files that changed from the base of the PR and between 0b865fc and 1f999a8.

📒 Files selected for processing (1)
  • src/screens/Settings/Vtxos.tsx

Comment thread src/screens/Settings/Vtxos.tsx Resolved

@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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.github/workflows/playwright.yml:
- Line 10: The workflow timeout was lowered to "timeout-minutes: 30" which will
likely cause flaky CI failures given Playwright settings (playwright.config.ts:
retries=5, workers=1, 2 projects) and long test timeouts in
src/test/e2e/swap.test.ts; fix by either restoring a safer per-job timeout
(e.g., increase timeout-minutes back to a larger value such as 60–90) or split
Mobile/Desktop into separate jobs (or a job matrix) so each job runs one
Playwright project and keep a reasonable timeout per job; update the
.github/workflows/playwright.yml entry "timeout-minutes: 30" accordingly and, if
splitting, create two jobs that invoke the same Playwright run but restrict to a
single project each.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 23094b9c-a0fa-4dfc-b41c-788cd85253cc

📥 Commits

Reviewing files that changed from the base of the PR and between 1f999a8 and 61bd1b1.

📒 Files selected for processing (1)
  • .github/workflows/playwright.yml

Comment thread .github/workflows/playwright.yml Resolved
@pietro909

Copy link
Copy Markdown
Contributor Author
image

Not sure what's wrong with the integration tests: they all fail here, but locally they are all passing.

@ghost

ghost commented Mar 18, 2026

Copy link
Copy Markdown

Followup review (commit d3b6d2d8 — error handling, cancellation, deps fix)

Three solid improvements to Vtxos.tsx:

  1. Stale response cancellation — cancelled flag in the useEffect cleanup prevents state updates after unmount or dep changes. Correct pattern.
  2. Error handling — getInputsToSettle failures are now caught and logged instead of producing unhandled promise rejections.
  3. Missing dependency — wallet.thresholdMs added to the dependency array. This was a real bug in the previous version; changes to the threshold would not have triggered a re-fetch.

All three are correctness fixes. No concerns with this commit.

@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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/screens/Settings/Vtxos.tsx`:
- Around line 113-127: When getInputsToSettle in fetchInputs throws, reset the
settlement-derived UI state so stale values don't keep the CTA enabled: inside
the catch (after the cancelled guard) call setHasBoardingUtxosToSettle(false),
setHasInputsToSettle(false), setHasVtxosToSettle(false) and setAboveDust(false)
(or setAboveDust(0 > aspInfo.dust) if you prefer a deterministic boolean) before
logging the error via consoleError(err); keep the cancelled checks and do not
alter behaviour in the success path or change getInputsToSettle itself.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 4bda0af5-7fbc-42ab-8ff1-b4fa265aa8a2

📥 Commits

Reviewing files that changed from the base of the PR and between 61bd1b1 and d3b6d2d.

📒 Files selected for processing (1)
  • src/screens/Settings/Vtxos.tsx

Comment on lines +113 to +127
if (!aspInfo || !svcWallet || !vtxoManager) return
let cancelled = false
const fetchInputs = async () => {
try {
const { boardingUtxos, inputs, vtxos } = await getInputsToSettle(svcWallet, vtxoManager, wallet.thresholdMs)
if (cancelled) return
setHasBoardingUtxosToSettle(boardingUtxos.length > 0)
setHasInputsToSettle(inputs.length > 0)
setHasVtxosToSettle(vtxos.length > 0)
const amount = inputs.reduce((a, v) => a + v.value, 0) || 0
setAboveDust(amount > aspInfo.dust)
} catch (err) {
if (cancelled) return
consoleError(err)
}

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.

⚠️ Potential issue | 🟠 Major

Reset settlement-derived UI state when getInputsToSettle fails.

On Line 124, the catch path only logs and leaves previous has*ToSettle / aboveDust values intact, which can keep the renew CTA enabled from stale data.

💡 Suggested fix
     const fetchInputs = async () => {
       try {
         const { boardingUtxos, inputs, vtxos } = await getInputsToSettle(svcWallet, vtxoManager, wallet.thresholdMs)
         if (cancelled) return
+        setError('')
         setHasBoardingUtxosToSettle(boardingUtxos.length > 0)
         setHasInputsToSettle(inputs.length > 0)
         setHasVtxosToSettle(vtxos.length > 0)
         const amount = inputs.reduce((a, v) => a + v.value, 0) || 0
         setAboveDust(amount > aspInfo.dust)
       } catch (err) {
         if (cancelled) return
         consoleError(err)
+        setHasBoardingUtxosToSettle(false)
+        setHasInputsToSettle(false)
+        setHasVtxosToSettle(false)
+        setAboveDust(false)
+        setError(extractError(err))
       }
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/screens/Settings/Vtxos.tsx` around lines 113 - 127, When
getInputsToSettle in fetchInputs throws, reset the settlement-derived UI state
so stale values don't keep the CTA enabled: inside the catch (after the
cancelled guard) call setHasBoardingUtxosToSettle(false),
setHasInputsToSettle(false), setHasVtxosToSettle(false) and setAboveDust(false)
(or setAboveDust(0 > aspInfo.dust) if you prefer a deterministic boolean) before
logging the error via consoleError(err); keep the cancelled checks and do not
alter behaviour in the success path or change getInputsToSettle itself.

@pietro909
pietro909 force-pushed the create-vtxo-manager-instance branch from d3b6d2d to 626fb98 Compare March 18, 2026 11:11
@ghost

ghost commented Mar 18, 2026

Copy link
Copy Markdown

🔍 Incremental review (rebased, new head: 626fb98)

Changes since last review look solid. Key observations:

Cancellation pattern in Vtxos.tsx — The useEffect now properly uses a cancelled flag with cleanup return, preventing stale state updates on unmount/re-render. Good fix for what was likely a source of React warnings.

vtxoManager as a loading guard — Adding !vtxoManager checks in settlePreconfirmed and the Vtxos screen loading state is the right call. Without the manager, those operations would fail anyway.

Sentry capture in handleRollover — The renewVtxos:handleRollover tag aligns with the startup and settlePreconfirmed tags. Consistent error tracking.

thresholdMs still passed correctly — Both renewCoins and settleVtxos calls pass wallet.thresholdMs, so the SDK's 3-day default never kicks in. The risk the PR description flagged is not present in this version.

One thing to watch: The wallet.thresholdMs is now in the useEffect dependency array in Vtxos.tsx (wasn't before). If wallet.thresholdMs is recomputed on every render (e.g., from a getter), this could cause unnecessary re-fetches. Should be fine if it's a stable value from context, but worth verifying.

No blocking issues.

@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.

Actionable comments posted: 3

♻️ Duplicate comments (1)
src/screens/Settings/Vtxos.tsx (1)

124-127: ⚠️ Potential issue | 🟠 Major

Reset settlement-derived flags when input fetch fails.

On failure, the effect logs but keeps prior has*ToSettle/aboveDust values, which can leave CTA state stale.

💡 Proposed fix
       } catch (err) {
         if (cancelled) return
         consoleError(err)
+        setHasBoardingUtxosToSettle(false)
+        setHasInputsToSettle(false)
+        setHasVtxosToSettle(false)
+        setAboveDust(false)
+        setError(extractError(err))
       }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/screens/Settings/Vtxos.tsx` around lines 124 - 127, The catch block that
handles input fetch failures currently only logs the error and leaves prior
settlement flags stale; inside the catch (err) { ... } for the input-fetching
effect in Vtxos.tsx, after verifying not cancelled, reset all settlement-derived
state (the has*ToSettle and aboveDust flags referenced in the component) to
their safe fallback values (e.g., false or null as your state types expect)
before calling consoleError(err) so the CTA state cannot remain stale on fetch
failure.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/lib/asp.ts`:
- Around line 249-251: Remove the truthy gate on thresholdMs so
vtxoManager.getExpiringVtxos is always invoked (pass thresholdMs even if
undefined or 0); replace the conditional expression that sets vtxos with a
direct await vtxoManager.getExpiringVtxos(thresholdMs) call and keep the
existing boardingUtxos retrieval via getConfirmedAndNotExpiredUtxos(wallet),
ensuring getExpiringVtxos handles falsy thresholds gracefully and returns an
empty array when appropriate.

In `@src/providers/wallet.tsx`:
- Around line 453-456: The settlePreconfirmed function currently awaits
settleVtxos and then calls notifyTxSettled but does not capture or tag errors;
wrap the await call in a try/catch inside settlePreconfirmed, catch any error
thrown by settleVtxos (or any subsequent step), send a telemetry/diagnostic
event with contextual tags (e.g., function: "settlePreconfirmed", svcWallet id
or descriptor, vtxoManager state, aspInfo.dust, wallet.thresholdMs, and the
error message/stack), then rethrow the caught error so callers still observe
failure; ensure references to settlePreconfirmed, settleVtxos, notifyTxSettled,
svcWallet, vtxoManager, aspInfo.dust, and wallet.thresholdMs are used when
building the telemetry payload.
- Line 357: The call to renewCoins(svcWallet, vtxoMgr, aspInfo.dust,
wallet.thresholdMs) currently swallows all errors via .catch(() => {}); change
this so startup renewal failures are surfaced: either await the promise and let
the error propagate, or replace the empty catch with a handler that logs the
error (include error details) using the existing logger and rethrows or triggers
process exit; update the call site around renewCoins so failures are not
silently ignored and include references to svcWallet, vtxoMgr, aspInfo.dust and
wallet.thresholdMs in the log context.

---

Duplicate comments:
In `@src/screens/Settings/Vtxos.tsx`:
- Around line 124-127: The catch block that handles input fetch failures
currently only logs the error and leaves prior settlement flags stale; inside
the catch (err) { ... } for the input-fetching effect in Vtxos.tsx, after
verifying not cancelled, reset all settlement-derived state (the has*ToSettle
and aboveDust flags referenced in the component) to their safe fallback values
(e.g., false or null as your state types expect) before calling
consoleError(err) so the CTA state cannot remain stale on fetch failure.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: a2fe0f89-2f50-4e05-abbc-c0969d0c55b0

📥 Commits

Reviewing files that changed from the base of the PR and between d3b6d2d and 626fb98.

📒 Files selected for processing (6)
  • .github/workflows/playwright.yml
  • src/lib/asp.ts
  • src/lib/vtxo.ts
  • src/providers/wallet.tsx
  • src/screens/Settings/Vtxos.tsx
  • src/screens/Wallet/Transaction.tsx
💤 Files with no reviewable changes (1)
  • src/lib/vtxo.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • .github/workflows/playwright.yml

Comment thread src/lib/asp.ts Resolved
Comment thread src/providers/wallet.tsx Resolved
Comment thread src/providers/wallet.tsx Resolved
@pietro909
pietro909 force-pushed the create-vtxo-manager-instance branch from 626fb98 to 8ff2f32 Compare March 20, 2026 10:26
@ghost

ghost commented Mar 20, 2026

Copy link
Copy Markdown

Followup Review — New commits since last review

New commit: 8ff2f320 — Error handling, Missing dependency, Stale response cancellation

Good improvements in this iteration:

✅ Stale response cancellation (Vtxos.tsx): The cancelled flag pattern with cleanup return is correct — prevents state updates on unmounted/re-rendered components. This was a real race condition risk with async getInputsToSettle calls.

✅ Error handling (Vtxos.tsx): try/catch around fetchInputs() with consoleError fallback + cancelled guard. Clean.

✅ Missing dependency: wallet.thresholdMs added to the useEffect dependency array — correct fix, the old code would have used a stale threshold if the wallet config changed.

✅ Sentry tag on handleRollover: renewVtxos:handleRollover tag added in the catch block — consistent with the tagging scheme described in the PR body.

One note on the overall PR: The vtxoManager dependency is correctly threaded through. The guard if (!svcWallet || !vtxoManager || loading) in Vtxos.tsx prevents rendering before the manager is available. Solid.

No blocking issues in this iteration.

@pietro909
pietro909 force-pushed the create-vtxo-manager-instance branch from 8ff2f32 to 8427eb3 Compare March 20, 2026 15:06
@ghost

ghost commented Mar 20, 2026

Copy link
Copy Markdown

🔍 Arkana Review — 8427eb3f (8 new commits since last review)

What changed: This PR now fully replaces the per-call VtxoManager instantiation pattern with a single shared IVtxoManager instance obtained from the service worker wallet. The old src/lib/vtxo.ts (manual orphan/expiry detection) is deleted entirely.

Key observations

✅ Good changes:

  • Single instance pattern — VtxoManager is created once via svcWallet.getVtxoManager() during wallet init and threaded through React context. Eliminates redundant instantiation on every getInputsToSettle call.
  • Sentry error capture preserved — Settlement errors now tagged with renewVtxos:handleRollover, renewVtxos:settlePreconfirmed, renewVtxos:startup — good for debugging.
  • Stale response cancellation — The Vtxos.tsx effect now uses a cancelled flag + cleanup return. Prevents state updates from stale async calls after unmount or dependency change.
  • Risky behavioral changes documented and deferred — PR description explicitly calls out the thresholdMs mismatch (wallet uses 10% of batch lifetime vs SDK default of 3 days) and the settle-vs-renew semantic question. Good discipline deferring these.
  • Dep bumps — ts-sdk@0.4.10 and boltz-swap@0.3.8 (aligns with recent releases).

🟡 Minor observations:

  1. vtxoManager in dependency arrays — Vtxos.tsx adds vtxoManager and wallet.thresholdMs to the useEffect dep array. Since vtxoManager is set once during init and held in state, it won't cause re-renders. But if getVtxoManager() ever returns a new reference on reconnect/re-init, this effect would re-fire. Worth keeping in mind.

  2. Guard on vtxoManager availability — Both settlePreconfirmed and the Vtxos screen now guard on !vtxoManager, which is correct. The loading screen shows until both svcWallet and vtxoManager are set. Since setVtxoManager is called right after setSvcWallet in the same await chain, there shouldn't be a visible intermediate state, but React batching guarantees this anyway (React 18+).

  3. CI timeout reduction (60→30 min) — Reasonable if tests are consistently under 30 min. If flaky E2E tests occasionally exceed this, you'll get spurious failures. Monitor after merge.

  4. The thresholdMs mismatch (flagged in PR description) — This is the most important follow-up. The wallet passing wallet.thresholdMs (dynamic, ~10% of batch lifetime ≈ 16.8h for 7-day batches) but the SDK's renewVtxos() defaulting to 3 days when no threshold is passed — these are different renewal windows. Since this PR preserves the old behavior (passing thresholdMs explicitly to getExpiringVtxos), there's no regression now, but someone should verify the SDK's getExpiringVtxos(thresholdMs) uses the passed value rather than its internal default.

Assessment: Clean refactor. No security concerns — no key handling changes, no transaction construction changes. The behavioral questions are explicitly deferred and documented. LGTM for merge.

@pietro909
pietro909 force-pushed the create-vtxo-manager-instance branch from 8427eb3 to 5c7d3e6 Compare March 23, 2026 05:09
@ghost

ghost commented Mar 23, 2026

Copy link
Copy Markdown

🔍 Review — Use VtxoManager instance from service worker

Approach: Replaces per-call VtxoManager instantiation with a shared instance from ServiceWorkerWallet.getVtxoManager(). Clean refactor that centralizes VTXO lifecycle management.

What works well

  • Single source of truth: vtxoManager is obtained once during wallet init and threaded through context — eliminates redundant manager creation and potential state divergence between callers.
  • Proper cleanup in useEffect (Vtxos.tsx:113-130): The cancelled flag pattern prevents stale state updates on unmount. The old version had a bare .then() that could set state on an unmounted component.
  • Sentry coverage improved: handleRollover now captures exceptions with tags (renewVtxos:handleRollover), matching the pattern used elsewhere.
  • vtxo.ts deletion is justified: getOrphanVtxos logic is subsumed by VtxoManager.getExpiringVtxos() which handles orphan, expiring, recoverable, and subdust cases.

Items to watch

  1. Threshold mismatch (documented in PR body, still open): The wallet computes thresholdMs = batchLifetimeMs * 10 / 100 (~16.8h for 7-day batches), but renewVtxos() without args defaults to SDK's 3-day threshold. The getInputsToSettle path still passes wallet.thresholdMs explicitly, so that path is fine. But any future caller using vtxoManager.renewVtxos() or vtxoManager.getExpiringVtxos() without the threshold param will get the SDK default. Worth a follow-up to align these — either pass the wallet threshold into the manager config at construction or document the intentional divergence.

  2. settlePreconfirmed semantic question (also in PR body): This now calls through to settleVtxos → getInputsToSettle → vtxoManager.getExpiringVtxos(thresholdMs) which does virtual renewal, not on-chain settlement. If the original intent was on-chain settle, the name is misleading. If virtual renewal is correct, the function name should probably change in a follow-up.

  3. Guard ordering in wallet.tsx:451: if (!svcWallet || !vtxoManager) — since vtxoManager is set right after svcWallet in the init flow (lines 312-313), these should always be in sync. But if setSvcWallet triggers a re-render before setVtxoManager completes, there's a brief window where svcWallet is set but vtxoManager is not. The guard catches this correctly, but batching both state updates (or using a single combined state) would eliminate the edge case.

Cross-repo context

  • Depends on wallet#450 and replaces wallet#410
  • The IVtxoManager interface and getVtxoManager() method come from ts-sdk#352 (merged 2026-03-17)
  • The getExpiringVtxos(thresholdMs?) signature should be verified against the current SDK — if the SDK expects a percentage vs milliseconds, the wallet's thresholdMs value would produce wrong results

Overall clean refactor. The threshold alignment question is the main thing to resolve before or shortly after merge.

@pietro909
pietro909 force-pushed the create-vtxo-manager-instance branch from 5c7d3e6 to 593adec Compare March 26, 2026 16:14
@ghost

ghost commented Mar 26, 2026

Copy link
Copy Markdown

🔍 Arkana — Iterative Review (rebase 593adec8)

What changed: Rebase onto current master (picks up #421, #448, #457, #460, #461, #462, #463, #464, #467, #468, #469, #470, #471, #472, #476, #479, #481). No changes to the PR-specific commits — the VtxoManager migration logic is identical to the previous review.

Rebase context: The new master includes ts-sdk 0.4.11 and boltz-swap 0.3.11 bumps (#481), which align with this PR's dependency expectations. No conflicts observed.

Previous review findings still apply — threshold alignment follow-up and settlePreconfirmed naming question remain open items for post-merge.

No new issues. LGTM.

@ghost

ghost commented Mar 26, 2026

Copy link
Copy Markdown

🔍 Arkana — Iterative Review (commit 33f0c83)

Previous feedback addressed in this iteration — nice work:

  • ✅ Sentry import added to Vtxos.tsx — ReferenceError risk eliminated
  • ✅ Threshold mismatch resolved — settlementConfig: { vtxoThreshold: Math.floor(wallet.thresholdMs / 1000) } is now passed during SW wallet init, so the VtxoManager uses the wallet's dynamic threshold instead of the SDK's 3-day default
  • ✅ useEffect cleanup in Vtxos.tsx now uses a cancelled flag — prevents state updates on unmounted components
  • ✅ Dependency arrays updated to include vtxoManager and wallet.thresholdMs

One minor observation:

  • wallet.tsx:336 — the settlementConfig is only set when wallet.thresholdMs is truthy. If thresholdMs is 0 (falsy), the config won't be passed and the SDK will fall back to its 3-day default. This is probably fine since 0 would be an invalid threshold anyway, but worth noting for clarity.

Overall this looks clean and ready. The VtxoManager singleton pattern from the service worker is properly threaded through all call sites, the old vtxo.ts helper is fully replaced, and error handling is improved with Sentry tags.

@pietro909
pietro909 force-pushed the create-vtxo-manager-instance branch from 33f0c83 to 349b48a Compare March 27, 2026 07:10
@ghost

ghost commented Mar 27, 2026

Copy link
Copy Markdown

🔍 Arkana — Iterative Review (commit 349b48ac)

Delta since last review (33f0c83): Rebase onto master + one new commit "Do not throw".

✅ Threshold mismatch — addressed

The settlementConfig: { vtxoThreshold: Math.floor(wallet.thresholdMs / 1000) } passed to ServiceWorkerWallet setup closes the gap I flagged previously. The SDK's VtxoManager will now use the wallet's dynamic threshold (10% of batch lifetime) instead of its 3-day default. Good fix.

⚠️ Silent error swallowing on startup renewal

// Before (previous iteration)
vtxoMgr.renewVtxos().catch((err) => {
  Sentry.captureException(err, {
    tags: { function: 'renewVtxos:startup' },
  })
})

// After (this commit)
vtxoMgr.renewVtxos().catch(() => {})

This removes Sentry capture for startup renewal failures. If renewVtxos() fails at boot (network issues, ASP unreachable, malformed VTXOs), the error is now fully invisible — no Sentry alert, no console log. For a fire-and-forget call that handles real user funds, silent failure makes debugging harder.

Suggestion: At minimum keep a consoleError() call so failures show up in dev tools. Even better, keep the Sentry capture:

vtxoMgr.renewVtxos().catch((err) => {
  consoleError(err, 'Startup VTXO renewal failed')
})

This is not blocking — the PR is a net improvement and the threshold fix was the critical item. But losing observability on a funds-handling path is worth reconsidering before merge.

@pietro909
pietro909 merged commit 09a19d4 into master Mar 27, 2026
5 checks passed
@pietro909
pietro909 deleted the create-vtxo-manager-instance branch March 27, 2026 08:53
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