feat(payouts): automatic Stellar on-chain transaction tracking#518
Open
Petah1 wants to merge 1 commit into
Open
feat(payouts): automatic Stellar on-chain transaction tracking#518Petah1 wants to merge 1 commit into
Petah1 wants to merge 1 commit into
Conversation
Introduces a background BullMQ poller that periodically queries the Horizon API for all Stellar payouts that have been submitted but not yet confirmed on-chain, keeping payout records automatically synchronized with their actual ledger state. Changes: - Store `onChainTxHash` in `initiateStellarPayout` so the hash is persisted as soon as the signed transaction is prepared - Add `pollPendingStellarPayouts()` to PayoutsService: finds all pending/processing Stellar payouts with a non-null `onChainTxHash` and no `confirmedAt`, then calls `confirmOneStellarPayout()` for each - `confirmOneStellarPayout()` calls `StellarService.getTransactionStatus()`; on success marks the payout `completed` + sets `confirmedAt` to the timestamp returned by Horizon; on chain rejection marks `failed`; when not yet found increments `retryCount` and marks `failed` after `STELLAR_CONFIRMATION_MAX_POLLS` (default 20) exhausted - All DB writes use `updateMany` with status-filter conditions so concurrent workers cannot double-update the same record - Per-payout transient errors are caught and logged without halting the rest of the batch - Add `StellarConfirmationProcessor`: BullMQ WorkerHost (concurrency=1) that schedules a repeatable job on startup (default every 30 s, env-configurable via `STELLAR_CONFIRMATION_INTERVAL_MS`) and delegates each tick to `pollPendingStellarPayouts()` - Register `stellar-confirmation` queue and processor in PayoutsModule - Remove duplicate `FeeService` import that was present in the service - Add tests covering successful confirmations, Horizon timestamp persistence, fallback timestamp, chain rejection, retry increment, max-polls exhaustion, idempotent updateMany guards, transient-error isolation, and processor scheduling
|
@Petah1 Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits. You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀 |
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.
Closes #362
Summary
Adds an automatic background polling system that tracks Stellar payout transactions against the Horizon API, keeping
Payoutrecords synchronized with their actual on-chain state without any manual intervention.Problem
When a Stellar payout is initiated via
initiateStellarPayout, the signed transaction XDR is prepared and the hash stored, but the confirmation status is never automatically reconciled with Horizon. If the transaction is submitted externally (e.g. by the user's wallet) or is delayed on the network, the payout record stays inpendingindefinitely.Solution
initiateStellarPayoutnow writesonChainTxHashthe moment the signed transaction is built, so the hash is available for polling before the XDR ever reaches Horizon.StellarConfirmationProcessor(BullMQWorkerHost, concurrency 1) schedules a repeatable job on startup that fires every 30 s by default (STELLAR_CONFIRMATION_INTERVAL_MS).PayoutsService.pollPendingStellarPayouts(), which finds everypending/processingStellar payout that has anonChainTxHashand noconfirmedAt, then checks each one against Horizon.successful: true→completed,confirmedAtset to Horizon'screated_attimestampsuccessful: false→failedretryCount++,lastAttemptAtupdatedSTELLAR_CONFIRMATION_MAX_POLLS, default 20 ≈ 10 min) →failedupdateManywith astatus: { in: [...] }+confirmedAt: nullfilter so concurrent workers racing on the same record produce exactly one update.StellarServicecircuit breaker (5 failures → 30 s recovery) protects against Horizon outages propagating to the job.Changed Files
src/payouts/stellar-confirmation.queue.tssrc/payouts/stellar-confirmation.processor.tssrc/payouts/stellar-confirmation.processor.spec.tssrc/payouts/payouts.service.tsonChainTxHashininitiateStellarPayout; addpollPendingStellarPayouts+confirmOneStellarPayout; remove duplicateFeeServiceimportsrc/payouts/payouts.module.tsstellar-confirmationqueue andStellarConfirmationProcessorEnvironment Variables
STELLAR_CONFIRMATION_INTERVAL_MS30000STELLAR_CONFIRMATION_MAX_POLLS20failedTest Plan
stellar-confirmation.processor.spec.tsonChainTxHashaddition)onChainTxHashis stored immediately on the recordcompletedandconfirmedAtmatches Horizon'screated_atfailedafterMAX_POLLSattempts