Skip to content

Recover Axelar transfers whose failed confirm poll shows as confirmed - #1406

Merged
ebma merged 4 commits into
stagingfrom
fix/axelar-confirm-failed-recovery
Oct 7, 2026
Merged

ebma merged 4 commits into
stagingfrom
fix/axelar-confirm-failed-recovery

Conversation

@ebma

@ebma ebma commented Oct 6, 2026

Copy link
Copy Markdown
Member

Why

On 2026-10-06 a Base -> BSC Squid GMP (0x9813…8ff7) got stuck at "failed to confirm". The validator poll (3258866) ended with NoEventsConfirmed, and Axelar never re-polls a failed poll by itself.

Axelarscan reported the transfer as status: "confirmed" with confirm_failed: true, not as "called". It records the failed poll's confirm event alongside the failure. Vortex's recovery only fired for "called", so this transfer was missed:

  • The status loop never sent a recovery ConfirmGatewayTx.
  • classifyGmpStatus mapped the transfer to relayer_pending, so the stuck monitor didn't recover it either. Its Slack alert also suggested a manual execute on a call that was never approved.

What changes

  • classifyGmpStatus returns source_confirmation_stuck for confirm_failed && !approved && !executed, whatever the status. The executed and insufficient_gas checks still run first.
  • The confirm-recovery trigger in SquidRouterPayExecutor uses the same condition. Cooldown, abort handling and the single-key axelarConfirmRecoveryAt write are unchanged.
  • The confirm-poll row in docs/security-spec/05-integrations/squid-router.md now describes the trigger.

Once Axelarscan reports approved, nothing changes: the transfer still goes to relayer_pending / approved_not_executed (#1403).

Side effect

If Axelarscan keeps confirm_failed set after a successful re-poll, we send at most one redundant recovery every 10 minutes until approval. On chain, a confirm for an already-confirmed event succeeds without changing state, so this only wastes the relayer's fee.

Tests

  • Classifier: "confirmed" + confirm_failed is stuck. Once approved it becomes relayer_pending, or approved_not_executed after the grace period. Executed and insufficient gas still take precedence.
  • Executor: the status loop triggers recovery for the "confirmed" + confirm_failed shape, with the hash and source chain, and records the cooldown. It does not trigger once approved.
  • Both new tests fail when the code changes are reverted.
  • bun test squid (api) and the shared Axelar tests pass.

ebma added 3 commits October 6, 2026 18:07
Axelarscan can report a failed validator poll as status "confirmed" because it records the poll's confirm event alongside confirm_failed. The classifier mapped that to relayer_pending, so no recovery ran and the stuck alert suggested a manual execute on a call that was never approved.
…atus

A failed poll reported as "confirmed" (2026-10-06, Base to BSC) never re-polls on its own, but the status loop only recovered status "called".
@netlify

netlify Bot commented Oct 6, 2026 •

Copy link
Copy Markdown

✅ Deploy Preview for vortex-sandbox ready!

Name Link
🔨 Latest commit 9179901
🔍 Latest deploy log https://app.netlify.com/projects/vortex-sandbox/deploys/6ac60403b644d40008dd2f93
😎 Deploy Preview https://deploy-preview-1406--vortex-sandbox.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

@netlify

netlify Bot commented Oct 6, 2026 •

Copy link
Copy Markdown

✅ Deploy Preview for vrtx-dashboard canceled.

Name Link
🔨 Latest commit 9179901
🔍 Latest deploy log https://app.netlify.com/projects/vrtx-dashboard/deploys/6ac604037bc44d0008f21add

@netlify

netlify Bot commented Oct 6, 2026 •

Copy link
Copy Markdown

✅ Deploy Preview for vortexfi canceled.

Name Link
🔨 Latest commit 9179901
🔍 Latest deploy log https://app.netlify.com/projects/vortexfi/deploys/6ac604037eb0f800084a1085

Verification only filters candidates, so a finder's misses are final. The Workflow tool also has no model option, so the sonnet directive never applied there.
@ebma
ebma merged commit 73d8a92 into staging Oct 7, 2026
6 checks passed
@ebma
ebma deleted the fix/axelar-confirm-failed-recovery branch October 7, 2026 08:39
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.

1 participant