Skip to content

fix(tcy): surface halt state on TCY claim/stake/unstake and keep fees during halts#12398

Merged
kaladinlight merged 2 commits into
developfrom
fix/ss-5689-tcy-halt-state
May 29, 2026
Merged

fix(tcy): surface halt state on TCY claim/stake/unstake and keep fees during halts#12398
kaladinlight merged 2 commits into
developfrom
fix/ss-5689-tcy-halt-state

Conversation

@kaladinlight
Copy link
Copy Markdown
Member

@kaladinlight kaladinlight commented May 29, 2026

Description

Users were unable to confirm TCY claim and stake transactions during a THORChain halt. The confirm button was silently disabled with no reason, because:

  • inbound_addresses filters out halted chains, so useSendThorTx received no inbound address → estimateFeesArgs short-circuited → fees never estimated → the button was disabled via !estimatedFeesData with no explanation.
  • The chain-halt check used the wrong chain (thorchainChainId instead of the claim asset's own L1 chain, e.g. BTC), so a chain-specific halt (HALTBTCCHAIN) went undetected.
  • TCY-specific mimir halts (TCYCLAIMINGHALT / TCYSTAKINGHALT / TCYUNSTAKINGHALT) weren't checked at all — these can be active while the chain itself is not halted (the exact state that triggered this bug).

Changes

  • ClaimConfirm: check the claim asset's L1 chain halt (fromAssetId(claim.assetId).chainId) + TCYCLAIMINGHALT; distinct button copy (chain vs claiming halted) and error state when halted.
  • StakeInput / UnstakeInput: gate on TCYSTAKINGHALT / TCYUNSTAKINGHALT in addition to the chain halt, with distinct copy + error state.
  • useSendThorTx: opt into includeHalted inbound addresses so fees still estimate during a halt (read-only). Defense-in-depth so a halted vault can never be acted on:
    • inboundAddress (the value consumers use as an approval spender) returns undefined when halted.
    • executeTransaction bails before broadcasting when the inbound is halted.
  • thornode: add opt-in includeHalted flag to inboundAddresses with a distinct query key. The default still filters halted, so trade/swapper (swapperApiHelpers), useIsTradingActive, usePools, etc. are unaffected.
  • Add TCY.claimingHalted / TCY.stakingHalted / TCY.unstakingHalted strings (English only; other locales fall back until /translate).

Issue (if applicable)

Fixes SS-5689 — Unable to confirm TCY claim and stake transaction

Risk

High Risk PRs Require 2 approvals

Higher risk — touches the THORChain transaction execution path shared by all THORChain DeFi flows (TCY claim/stake/unstake, Savers, LP, Lending repay) via useSendThorTx.

Note: this does not introduce or change transaction construction — it only adds halt gating and lets fees estimate during a halt. Key invariants verified:

  • Default inboundAddresses() still filters halted (distinct cache key for the include-halted variant), so swapper/trade routing is unchanged.
  • No fund-moving broadcast can reach a halted vault — blocked in executeTransaction.
  • No approval can target a halted vault — inboundAddress is undefined when halted.
  • Halted inbound data is only ever used for read-only fee estimation.

Suggest the High-Risk label + 2 reviewers given the breadth of useSendThorTx consumers.

What protocols, transaction types, wallets or contract interactions might be affected by this PR?

THORChain (MsgDeposit, EvmCustomTx, UTXO Send) across TCY claim/stake/unstake, Thorchain Savers, Thorchain LP, and Lending repay.

Testing

Engineering

  • With THORChain (or a specific chain / TCY claiming) currently halted:
    • TCY claim/stake/unstake confirm screens show the fee, and the button is disabled with a clear reason ("TCY Claiming/Staking/Unstaking Halted" or "Chain Halted") rather than silently disabled.
    • Attempting to execute does not broadcast (guarded in executeTransaction).
    • Approval steps (Savers deposit, LP add) don't expose a halted vault as spender.
  • With nothing halted: claim/stake/unstake/savers/LP/repay behave exactly as before (fees estimate, buttons enable, txs broadcast normally).
  • Regression check: Thorchain swaps still route correctly (default inboundAddresses() unchanged).

Operations

  • 🏁 My feature is behind a flag and doesn't require operations testing (yet)

Functional QA: during a THORChain/TCY halt, verify TCY claim/stake/unstake show the fee + a halt reason on a disabled button; verify normal flows are unaffected when not halted.

Screenshots (if applicable)

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added user-facing messages for TCY claim, stake, and unstake halted states.
    • UI now detects TCY-specific halt flags and updates confirmation screens, button states, and loading indicators.
  • Bug Fixes

    • Blocked transaction broadcasts when relevant inbound vaults are halted while preserving fee estimation.
    • Improved handling so halted states take precedence in UI messaging and action disabling.

Review Change Stack

… during halts

Users couldn't confirm TCY claim/stake/unstake transactions during a THORChain
halt: the inbound_addresses query filters out halted chains, so useSendThorTx
got no inbound address, fee estimation never ran, and the confirm button was
silently disabled with no reason shown. The chain-halt check also used the
wrong chain (thorchainChainId instead of the claim asset's L1 chain) and didn't
account for the TCY-specific mimir halt flags.

- ClaimConfirm: check the claim asset's own L1 chain halt + TCYCLAIMINGHALT, and
  surface a distinct reason on the button (chain vs claiming halted) + error state.
- StakeInput/UnstakeInput: gate on TCYSTAKINGHALT/TCYUNSTAKINGHALT in addition to
  the chain halt, with distinct copy + error state.
- useSendThorTx: fetch halted inbound addresses (opt-in includeHalted) so fees
  still estimate during a halt (read-only). Broadcasting and the exposed inbound
  address are guarded against halted vaults as defense-in-depth, so execution is
  impossible while halted regardless of the consuming UI.
- thornode: add opt-in includeHalted to inboundAddresses with a distinct query
  key; default still filters halted so trade/swapper and all other consumers are
  unaffected.
- Add TCY.claimingHalted/stakingHalted/unstakingHalted translation strings.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@kaladinlight kaladinlight requested a review from a team as a code owner May 29, 2026 17:23
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 29, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 99e5f3a8-1979-41c2-9bbb-811e08455c83

📥 Commits

Reviewing files that changed from the base of the PR and between def2473 and cdc24b9.

📒 Files selected for processing (1)
  • src/react-queries/queries/thornode.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/react-queries/queries/thornode.ts

📝 Walkthrough

Walkthrough

This PR adds TCY claiming, staking, and unstaking halt handling: inbound address query gains an option to include halted vaults; the transaction hook guards against broadcasting to halted vaults; translation keys and Claim/Stake/Unstake UIs integrate Thorchain Mimir halt flags for messaging, disabling, and loading states.

Changes

TCY Halted State Support

Layer / File(s) Summary
Inbound address query API with halted filtering
src/react-queries/queries/thornode.ts
inboundAddresses() now accepts an optional includeHalted parameter (default false) that controls response filtering and varies the React Query cache key to avoid clobbering the default filtered cache.
Transaction layer halted vault guards
src/lib/utils/thorchain/hooks/useSendThorTx.tsx
Hook now requests unfiltered inbound addresses via inboundAddresses(true), added memo-level guard filtering out halted vault addresses from consumers, and added execution-time guard preventing transaction broadcast to halted vaults while preserving halted data for fee estimation.
TCY halted state translation strings
src/assets/translations/en/main.json
Added three new translation keys under TCY: claimingHalted, stakingHalted, and unstakingHalted for user-facing halt messages.
Claim component TCY claiming halt integration
src/pages/TCY/components/Claim/ClaimConfirm.tsx
Queries TCYCLAIMINGHALT Mimir flag, computes combined halt state from chain and TCY halt flags, treats combined halt as error condition, prioritizes halt messaging in button copy, disables confirmation on halt, and gates loading with halt fetching state.
Stake component TCY staking halt integration
src/pages/TCY/components/Stake/StakeInput.tsx
Queries TCYSTAKINGHALT Mimir flag, computes combined halt state, prioritizes halt message in button copy, changes button color to reflect halt state, and includes halt fetching in loading gate.
Unstake component TCY unstaking halt integration
src/pages/TCY/components/Unstake/UnstakeInput.tsx
Queries TCYUNSTAKINGHALT Mimir flag, computes combined halt state, shows unstaking-halted message when active, changes button color to red on halt, and includes halt fetching in loading gate.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

A rabbit nibbles on the patch,
Mimir whispers, halts dispatch,
Buttons dim and copies change,
Vaults stay safe within their range,
🐰✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically describes the main changes: fixing TCY halt state surfacing and preserving fee estimation during halts. It accurately reflects the primary objectives of the changeset.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/ss-5689-tcy-halt-state

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.

TS1355: `as const` cannot be applied to a parenthesized ternary, only to
literals directly. Move the assertion onto each array branch.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@kaladinlight kaladinlight merged commit 7af4eeb into develop May 29, 2026
4 checks passed
@kaladinlight kaladinlight deleted the fix/ss-5689-tcy-halt-state branch May 29, 2026 19:34
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