core: ethereumPath takes the address index

The BIP-44 path is the same for every address under an account, so the
caller that wants one names it, rather than rebuilding the path around the
one this hard-coded.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rvc3HbiWBTqbAvRT45G5oX
This commit is contained in:
Alain Brenzikofer
2026-09-10 15:19:45 +00:00
co-authored by Claude Opus 5
parent a23da20fb5
commit 2298704687
2 changed files with 7 additions and 5 deletions
+5 -3
View File
@@ -108,9 +108,11 @@ parseAddress s
letters = filter (not . isDigit) bodyC
mixedCase = any isUpper letters && any isLower letters
-- | BIP-44 path for Ethereum account @i@: @m\/44'\/60'\/i'\/0\/0@.
ethereumPath :: Word32 -> [Word32]
ethereumPath account = [hardened 44, hardened 60, hardened account, 0, 0]
-- | BIP-44 path for Ethereum account @i@, address @k@: @m\/44'\/60'\/i'\/0\/k@.
-- The account must be below 'hardenedOffset', as 'hardened' returns anything
-- at or above it unchanged and the path would be another account's.
ethereumPath :: Word32 -> Word32 -> [Word32]
ethereumPath account address = [hardened 44, hardened 60, hardened account, 0, address]
-- Hex via memory's Base16, which this package already depends on and which
-- Crypto.Secp256k1 already uses. Base16 emits lowercase, which is what EIP-55
+2 -2
View File
@@ -179,7 +179,7 @@ bip32Tests = do
it "renders a path" $
B32.renderPath [hardened' 44, hardened' 60, hardened' 0, 0, 0] `shouldBe` "m/44'/60'/0'/0/0"
it "round-trips render and parse" $
B32.parsePath (B32.renderPath (ethereumPath 7)) `shouldBe` Right (ethereumPath 7)
B32.parsePath (B32.renderPath (ethereumPath 7 3)) `shouldBe` Right (ethereumPath 7 3)
it "rejects a non-numeric component" $
B32.parsePath "m/44x/60" `shouldSatisfy` isLeft
it "rejects an index at the hardened boundary" $
@@ -239,7 +239,7 @@ derivationTests = do
m = right $ B39.parseMnemonic canonicalPhrase
seed = B39.mnemonicToSeed m ""
master = right $ B32.masterKey seed
addrAt i = addressFromPrivateKey . B32.xkKey . right $ B32.derivePath master (ethereumPath i)
addrAt i = addressFromPrivateKey . B32.xkKey . right $ B32.derivePath master (ethereumPath i 0)
dedup a as = if a `elem` as then as else a : as
eip55Tests :: Spec