You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
#4263 extended the fail-closed designate-before-broadcast boundary to all non-EVM pay-in send strategies (and #4249 did the same for EVM). When a send fails ambiguously at or after the broadcast, the pay-in is left in SENDING and the processStrandedSendingPayIns cron escalates it to SEND_UNCERTAIN after the grace window. This is correct and fund-safe: the transaction may be in flight, so re-broadcasting would double-pay the customer.
Problem
There is currently no recovery path for a SEND_UNCERTAIN pay-in:
No cron re-reads SEND_UNCERTAIN (checkOutputConfirmations / checkReturnConfirmations only touch FORWARDED / RETURNED).
The pay-in controller has no retry endpoint.
returnPayIn / triggerBuyFiatReturn reject an in-flight-send status, so the customer's funds are blocked — neither forwarded/returned nor retryable — until someone edits the database by hand.
The payout side already solved the equivalent problem: #4231 added a verification-gated admin retry for PAYOUT_UNCERTAIN orders. The pay-in side has no equivalent, and #4263 widened this exposure from EVM-only to all non-EVM chains.
Expected behavior
A verification-gated admin endpoint (mirroring the payout retry from #4231) that returns a confirmed-not-broadcast SEND_UNCERTAIN pay-in to the send flow:
Admin-guarded (same guards as the existing admin pay-in/payout routes).
Gated on an explicit operator attestation that the transaction was not broadcast (checked strictly, e.g. noBroadcastVerified === true), because the whole point of SEND_UNCERTAIN is that the broadcast outcome is unknown — a blind reset would re-introduce the exact double-send fix(payin): fail closed on ambiguous non-EVM pay-in sends #4263 prevents.
Reject the reset if the pay-in already carries an outTxId / returnTxId (it did broadcast) — reconcile that separately, never re-send.
On success, an atomic conditional transition (compare-and-set on status = SEND_UNCERTAIN) back to the status the send cron re-selects (ACKNOWLEDGED/PREPARED for forward, TO_RETURN/PREPARED for return), so the next cron run re-runs it under the full designate-before-broadcast protection.
Fail closed if the lookup/verification cannot be completed — deny the reset, never proceed.
Acceptance criteria
Admin endpoint resets a verified-not-broadcast SEND_UNCERTAIN pay-in to a send-cron-selectable status via an atomic conditional update; a pay-in that changed state concurrently is not resurrected.
A pay-in that has an outTxId/returnTxId set is rejected (no reset).
The operator attestation is required and strictly checked; the request fails closed if verification cannot complete.
Authz matches the existing admin routes.
Regression test: a reset pay-in re-enters the send flow and is not double-sent; a non-attested / already-broadcast pay-in is rejected.
Context
#4263 extended the fail-closed designate-before-broadcast boundary to all non-EVM pay-in send strategies (and #4249 did the same for EVM). When a send fails ambiguously at or after the broadcast, the pay-in is left in
SENDINGand theprocessStrandedSendingPayInscron escalates it toSEND_UNCERTAINafter the grace window. This is correct and fund-safe: the transaction may be in flight, so re-broadcasting would double-pay the customer.Problem
There is currently no recovery path for a
SEND_UNCERTAINpay-in:SEND_UNCERTAIN(checkOutputConfirmations/checkReturnConfirmationsonly touchFORWARDED/RETURNED).returnPayIn/triggerBuyFiatReturnreject an in-flight-send status, so the customer's funds are blocked — neither forwarded/returned nor retryable — until someone edits the database by hand.The payout side already solved the equivalent problem: #4231 added a verification-gated admin retry for
PAYOUT_UNCERTAINorders. The pay-in side has no equivalent, and #4263 widened this exposure from EVM-only to all non-EVM chains.Expected behavior
A verification-gated admin endpoint (mirroring the payout retry from #4231) that returns a confirmed-not-broadcast
SEND_UNCERTAINpay-in to the send flow:noBroadcastVerified === true), because the whole point ofSEND_UNCERTAINis that the broadcast outcome is unknown — a blind reset would re-introduce the exact double-send fix(payin): fail closed on ambiguous non-EVM pay-in sends #4263 prevents.outTxId/returnTxId(it did broadcast) — reconcile that separately, never re-send.status = SEND_UNCERTAIN) back to the status the send cron re-selects (ACKNOWLEDGED/PREPAREDfor forward,TO_RETURN/PREPAREDfor return), so the next cron run re-runs it under the full designate-before-broadcast protection.Acceptance criteria
SEND_UNCERTAINpay-in to a send-cron-selectable status via an atomic conditional update; a pay-in that changed state concurrently is not resurrected.outTxId/returnTxIdset is rejected (no reset).References
PAYOUT_UNCERTAIN)