Skip to content

Trigger on confirmed transactions#622

Merged
chedieck merged 1 commit intoPayButton:masterfrom
Fabcien:process_confirmed_txs
Feb 20, 2026
Merged

Trigger on confirmed transactions#622
chedieck merged 1 commit intoPayButton:masterfrom
Fabcien:process_confirmed_txs

Conversation

@Fabcien
Copy link
Collaborator

@Fabcien Fabcien commented Feb 20, 2026

So far only unconfirmed (aka mempool) transactions are parsed. This means that any transaction that is mined before it reaches chronik's mempool will be ignored and the payment missed, which is obviously very bad.

The transaction confirmed message is added to the list of managed chronik messages, and the success is checked before processing to avoid triggering the payment success twice.

Note 1: the filter exists in the socket.ts txsListener but this is dead code and is removed in #619.
Note 2: there should be an option for waiting for finalization but this is out of scope.

Test Plan:
yarn test

Check with a widget test, pay for it, check the success and wait for the block to be mined: the payment animation is not replayed.

Summary by CodeRabbit

  • New Features

    • Real-time handling of confirmed blockchain transactions in addition to pending notifications.
  • Bug Fixes

    • Prevent duplicate payment processing by stopping handling after a successful payment and avoiding repeated transaction callbacks when confirmations arrive.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 20, 2026

No actionable comments were generated in the recent review. 🎉


📝 Walkthrough

Walkthrough

HandleNewTransaction now skips processing if a prior payment succeeded and invokes handlePayment for any transaction with a positive amount regardless of confirmation state. WebSocket parsing now treats TX_CONFIRMED the same as TX_ADDED_TO_MEMPOOL, fetching the transaction and pushing it into the new-transaction pipeline.

Changes

Cohort / File(s) Summary
Widget transaction handling
react/lib/components/Widget/WidgetContainer.tsx
handleNewTransaction added an early return when success is true; removed tx.confirmed === false requirement so handlePayment is called for any transaction with amount > 0; guarded onTransaction to fire only for unconfirmed transactions; added success to useCallback deps.
WebSocket / chronik parsing
react/lib/util/chronik.ts
parseWebsocketMessage extended to handle TX_CONFIRMED by routing it to the same flow as TX_ADDED_TO_MEMPOOL: fetch raw tx via chronik.tx(...), convert, and push via setNewTx([...]).

Sequence Diagram(s)

sequenceDiagram
    participant WS as "WebSocket"
    participant Chronik as "chronik util"
    participant Widget as "WidgetContainer"
    WS->>Chronik: receive TX_ADDED_TO_MEMPOOL / TX_CONFIRMED msg
    Chronik->>Chronik: chronik.tx(txid) -> getTransactionFromChronikTransaction
    Chronik->>Widget: setNewTx([transaction])
    Widget->>Widget: handleNewTransaction(transaction)
    alt success already true
        Widget-->>Widget: return early (skip processing)
    else amount > 0
        Widget->>Widget: handlePayment(transaction)
        Widget->>Widget: (fire onTransaction only if unconfirmed)
    end
Loading

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Poem

🐇 I nibble code in morning light,
New txs arrive — confirmed or bright,
I skip what's done, I call what's new,
WebSocket sings and payments pursue. ✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: adding support for confirmed transaction processing, which is the primary objective of this PR.
Description check ✅ Passed The description covers the problem being solved, the solution approach, relevant notes about related code, and a clear test plan—meeting the template requirements.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

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

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

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

Inline comments:
In `@react/lib/components/Widget/WidgetContainer.tsx`:
- Around line 282-284: The onTransaction callback can fire twice for the same
txid because when shouldTriggerOnSuccess(tx) is false on mempool arrival we call
onTransaction(tx) and then call handlePayment again when TX_CONFIRMED triggers;
update handleNewTransaction/handlePayment logic to deduplicate by txid (or check
a success flag) before calling onTransaction: e.g., track seen txids for pending
transactions or check shouldTriggerOnSuccess(tx) and a stored seenTransactions
set before invoking onTransaction(tx), ensuring onTransaction is only called
once per txid (refer to functions handleNewTransaction, handlePayment,
shouldTriggerOnSuccess, onTransaction, resolveNumber, isGreaterThanZero).
- Around line 278-287: The callback that responds to transaction events
currently only early-returns on success, so shifts already completed via the
altpayment path (shiftCompleted) can trigger handlePayment and onSuccess again;
update the early-return in the useCallback that processes
TX_CONFIRMED/TX_ADDED_TO_MEMPOOL to check both success and shiftCompleted (e.g.,
if (success || shiftCompleted) return;) and add shiftCompleted to the dependency
array alongside handlePayment and success so the callback sees updates to that
flag.

Comment on lines +278 to 287
if (success) {
return;
}

if (isGreaterThanZero(resolveNumber(tx.amount))) {
handlePayment(tx);
}
},
[handlePayment],
[handlePayment, success],
);
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

shiftCompleted is not guarded — altpayment onSuccess and success sound can fire twice.

The early-return at Line 278 only checks success, but the altpayment path in handlePayment sets shiftCompleted and calls onSuccess without setting success. This means when TX_CONFIRMED arrives after TX_ADDED_TO_MEMPOOL for an already-settled shift, success is still false, the guard is bypassed, and handlePayment runs again — re-playing the sound and re-invoking onSuccess.

🐛 Proposed fix
-      if (success) {
+      if (success || shiftCompleted) {
         return;
       }

And add shiftCompleted to the useCallback dependency array:

-      [handlePayment, success],
+      [handlePayment, success, shiftCompleted],
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (success) {
return;
}
if (isGreaterThanZero(resolveNumber(tx.amount))) {
handlePayment(tx);
}
},
[handlePayment],
[handlePayment, success],
);
if (success || shiftCompleted) {
return;
}
if (isGreaterThanZero(resolveNumber(tx.amount))) {
handlePayment(tx);
}
},
[handlePayment, success, shiftCompleted],
);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@react/lib/components/Widget/WidgetContainer.tsx` around lines 278 - 287, The
callback that responds to transaction events currently only early-returns on
success, so shifts already completed via the altpayment path (shiftCompleted)
can trigger handlePayment and onSuccess again; update the early-return in the
useCallback that processes TX_CONFIRMED/TX_ADDED_TO_MEMPOOL to check both
success and shiftCompleted (e.g., if (success || shiftCompleted) return;) and
add shiftCompleted to the dependency array alongside handlePayment and success
so the callback sees updates to that flag.

So far only unconfirmed (aka mempool) transactions are parsed. This means that any transaction that is mined before it reaches chronik's mempool will be ignored and the payment missed, which is obviously very bad.

The transaction confirmed message is added to the list of managed chronik messages, and the success is checked before processing to avoid triggering the payment success twice.

Note that the filter exists in the socket.ts txsListener but this is dead code and is removed in PayButton#619.

Test Plan:
  yarn test

Check with a widget test, pay for it, check the success and wait for the block to be mined: the payment animation is not replayed.
@Fabcien Fabcien force-pushed the process_confirmed_txs branch from 822a061 to d17f9d2 Compare February 20, 2026 12:55
@Fabcien
Copy link
Collaborator Author

Fabcien commented Feb 20, 2026

As per the rabbit feedback, filter the onTransaction() callback to retain the existing behavior

@Klakurka Klakurka requested a review from chedieck February 20, 2026 15:07
@Klakurka Klakurka added the bug Something isn't working label Feb 20, 2026
@Klakurka Klakurka added this to the Phase 3 milestone Feb 20, 2026
@chedieck chedieck merged commit 4fc1aaf into PayButton:master Feb 20, 2026
3 checks passed
@Fabcien Fabcien deleted the process_confirmed_txs branch February 20, 2026 16:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

Comments