Skip to content

fix(thorchain): clear-sign deposits on every EVM chain, not just mainnet#309

Open
BitHighlander wants to merge 1 commit into
developfrom
fix/thorchain-clearsign-per-chain-router
Open

fix(thorchain): clear-sign deposits on every EVM chain, not just mainnet#309
BitHighlander wants to merge 1 commit into
developfrom
fix/thorchain-clearsign-per-chain-router

Conversation

@BitHighlander

Copy link
Copy Markdown
Owner

What & why

A THORChain AVAX→ETH swap on the emulator hit the AdvancedMode blind-sign gate and returned a bare ActionCancelled instead of clear-signing. Root cause: thor_isThorchainTx pins the router to the Ethereum-mainnet address (d37bbe57..) with no chain awareness, so a depositWithExpiry on Avalanche — whose router is 00dc6100.. — can never match. ethereum_contractHandled returns false, the signed-metadata escape isn't wired, and the tx falls into the hard blind-sign block (ethereum.c:850-859). THORChain EVM deposits were therefore clear-signable on Ethereum and structurally impossible on every other chain.

Changes

  1. Chain-scoped router pin. thor_router_for_chain(msg) resolves the THORChain router by chain_id (Ethereum + Avalanche today); thor_isThorchainTx pins on (address, chain_id) together. EthereumSignTx already carries chain_id, so no plumbing was needed. The confirm screen labels the AVAX router as "Thorchain router" too.

  2. Memo-length read (latent security fix on the same path). thor_confirm_deposit_tx read a hardcoded 64 memo bytes instead of the ABI length word. That truncated a longer memo (the live 67-byte memo drops its trailing affiliate-fee field) and, with a crafted length word, let the router execute a longer memo than the device displayed (display-vs-execute divergence). Now reads the real length word with canonical high-byte + full-presence checks, and relaxes the min_chunk floor (which was itself derived from the same 64) to the true structural minimum.

Scope note

BSC (56) and Base (8453) THORChain routers exist on-chain but are intentionally omitted until verified against a live node: the shipped host catalog lists a stale AVAX router (8f66c4ae.. vs the live 00dc6100..), so its BSC/Base entries can't be trusted, and Pioneer routes those pairs via Relay today — no THORChain deposit reaches the device. Each drops in as a one-line addition to thor_router_for_chain once verified. The Avalanche address here was verified live via a Pioneer quote (/inbound_addresses).

Verification

  • New unit tests (7): chain-scoped match (ETH@1, AVAX@43114), reject router-on-wrong-chain, reject unpinned chain, reject unpinned address, 67-byte memo decodes fully, over-declared memo length rejected.
  • Full firmware unit suite: 254/254 pass (firmware-unit, emulator cache).
  • clang-format-20 clean on .c/.h.
  • The ConfirmThorTxLongMemoDecodesFully test drives the exact live AVAX calldata (67-char memo, AVAX router, chain 43114) through the real thor_confirmThorTx confirm path.

Remaining

On-device / emulator Gate-3 OLED capture of the AVAX deposit clear-sign screens is the human-gated approval step (emulator dylib rebuilt from this branch + restart).

The vault-side clear-sign predictor mirror (calldata-decoder.ts firmwareClearSigns) is updated in lockstep in a separate vault change so the REST/WalletConnect overlay predicts AVAX clear-signability correctly.

thor_isThorchainTx pinned the router to the Ethereum-mainnet address with no
chain awareness, so a THORChain depositWithExpiry on Avalanche (or any
non-mainnet chain) never matched and fell into the AdvancedMode blind-sign
gate — the device returned a bare ActionCancelled instead of clear-signing.
Scope the pin to (address, chain_id): add the live-verified Avalanche router
and look it up by chain. EthereumSignTx already carries chain_id, so no
plumbing is needed.

Also fix a latent memo bug on the same path: thor_confirm_deposit_tx read a
hardcoded 64 memo bytes instead of the ABI length word. That truncated a
longer memo (dropping trailing fields such as the affiliate fee bps) and —
with a crafted length word — let the router execute a longer memo than the
device displayed. Read the real length word with canonical high-byte and
presence checks, and relax the min-chunk floor (which was derived from the
same 64) to the true structural minimum.

BSC and Base routers exist on-chain but are omitted until verified against a
live node: the shipped host catalog lists a stale AVAX router, and those pairs
route via Relay today, so no such tx reaches the device.

Tests: 7 new (chain-scoped match/reject, unpinned chain/address reject,
67-byte memo decodes fully, over-declared memo length rejected). Full firmware
unit suite 254/254.
@BitHighlander BitHighlander force-pushed the fix/thorchain-clearsign-per-chain-router branch from 86fa168 to 715c173 Compare July 16, 2026 03:02
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