Skip to content

Derive Avalanche C-Chain addresses at coin type 60 - #1057

Open
j0ntz wants to merge 2 commits into
masterfrom
jon/fix-avax-evm-derivation
Open

j0ntz wants to merge 2 commits into
masterfrom
jon/fix-avax-evm-derivation

Conversation

@j0ntz

@j0ntz j0ntz commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

CHANGELOG

Does this branch warrant an entry to the CHANGELOG?

  • Yes
  • No

Dependencies

none

Description

Avalanche C-Chain is EVM-compatible, and standard wallets (Exodus, MetaMask, Trust) derive its address at Ethereum's BIP-44 coin type 60. Edge derived Avalanche at coin type 9000 (the SLIP-44 X/P-Chain type), so a 12-word seed imported from one of those wallets produced an AVAX receive address that did not match the source wallet, the bug reported on the Asana task (ETH/POL matched, AVAX did not).

This switches avalancheInfo.hdPathCoinType from 9000 to 60, bringing AVAX in line with every other EVM network in this repo.

Existing wallets keep their old address. The plugin now saves the BIP-44 path it derived from into the wallet keys (derivationPath, the same shape TronTools already uses), and derivePublicKey derives from that saved path when it is present. Keys with no saved path predate the correction, so they fall back to the new EthereumNetworkInfo.legacyHdPathCoinType, set to 9000 on avalanche only. Without that fallback an existing wallet re-derived on a new device would have displayed a coin-type-60 address while its stored avalancheKey still signed for the coin-type-9000 one, leaving anything sent to the displayed address unspendable in the app. This is the incomplete-fix case raised in review.

Verification

  • test/ethereum/avalancheDerivation.test.ts covers three things: ethereum, polygon and avalanche all derive 0x21D45Fd06e291C49AbFa135460DE827b6579Cef5 from one imported seed (driven through importPrivateKey, so the saved path is what gets exercised); a path-less avalanche keys object still derives the legacy 0xc0Ee5411B61513Bea1853692463e28D6c32A0b50; and a keys object holding only the private key resolves to the coin-type-60 address.
  • tsc, eslint and the full test suite pass (verify-repo.sh).
  • The coin-type-60 fix was driven end to end in the iOS simulator on an earlier segment: importing the affected seed into a fresh AVAX wallet showed 0x21D4...Cef5 on the receive scene, against 0xc0Ee...0b50 on a pre-fix build.

Asana: https://app.asana.com/0/1215088146871429/1209918996092096


Note

Medium Risk
Changes HD derivation for new/imported Avalanche wallets; existing wallets keep cached keys per PR description, but any code path that re-derives without cache could diverge from old 9000 addresses.

Overview
Fixes Avalanche C-Chain receive addresses for new/imported wallets by changing BIP-44 derivation from coin type 9000 to 60 in avalancheInfo, matching Exodus, MetaMask, Trust and other EVM wallets.

Documents the fix in CHANGELOG and adds test/ethereum/avalancheDerivation.test.ts, which asserts derivePublicKey for ethereum, polygon, and avalanche all yield the same known coin-type-60 address from a fixed 12-word seed.

Reviewed by Cursor Bugbot for commit 92a67d2. Bugbot is set up for automated code reviews on this repo. Configure here.

Test evidence

7119116
Derive Avalanche C-Chain addresses at coin type 60

AVAX receive PRE FIX derives coinType 9000 address does not match Exodus

imported seed fixed coin type 60 0x21D45Fd06e291C49AbFa135460DE827b6579Cef5

same seed pre fix coin type 9000 0xc0Ee5411B61513Bea1853692463e28D6c32A0b50

@j0ntz

j0ntz commented Jun 12, 2026

Copy link
Copy Markdown
Contributor Author

Corrected test evidence

This replaces the earlier proof screenshots, which were iOS springboard (home screen) frames captured from a simulator the maestro daemon was not actually driving. The fix itself is unchanged.

Genuine re-test on the iOS simulator (slot UDID 44B54874-D8A9-4A36-8EAB-1900D96FD722): imported the affected seed room soda device label bicycle hill fork nest lion knee purpose hen into a fresh AVAX wallet with this PR's coin-type-60 plugin loaded (DEBUG_ACCOUNTBASED=true + the fix branch served on :8082). The receive scene shows:

0x21D45Fd06e291C49AbFa135460DE827b6579Cef5

That is the coin-type-60 address Exodus, MetaMask, and Trust derive, and the value asserted by this PR's avalancheDerivation.test.ts.

Control on the same seed under the pre-fix coin-type-9000 build: 0xc0Ee5411B61513Bea1853692463e28D6c32A0b50. Offline derivation confirms 60 yields 0x21D4..Cef5 and 9000 yields 0xc0Ee..0b50, so the two on-screen addresses are exactly the buggy vs fixed derivations of one seed.

Screenshots below were captured via simctl io against the exact simulator UDID that was driven.

Comment thread src/ethereum/info/avalancheInfo.ts
AVAX C-Chain is EVM-compatible and every standard wallet (Exodus, MetaMask,
Trust) derives it at Ethereum's coin type 60. Edge used coin type 9000, so a
seed imported from those wallets produced a non-matching AVAX receive address.

Switch avalanche hdPathCoinType to 60 so imported seeds yield a matching
address. Existing wallets keep their cached public key (no checkPublicKey hook),
so their addresses and funds are unaffected; only newly created or imported
avalanche wallets adopt the corrected path.

Lock the behavior with a parity test that asserts avalanche, ethereum and
polygon all derive the same known EVM address from one imported seed.
@j0ntz
j0ntz force-pushed the jon/fix-avax-evm-derivation branch from 92a67d2 to 097f5ea Compare September 3, 2026 01:18
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.

2 participants