Skip to content

--ledgerLiveMode iterates the BIP-44 change index instead of the hardened account index #789

Description

@palango

Package

  • @celo/celocli 9.0.1
  • @celo/wallet-ledger 8.0.4

Both packages are up to date.

Environment

  • macOS on Apple Silicon
  • Ledger Nano X

Command

celocli account:list --useLedger --ledgerAddresses 3 --local --ledgerLiveMode

Describe the bug

--ledgerLiveMode does not discover Ledger Live accounts beyond the first account.

With the default Ethereum base path m/44'/60'/0', the command above derives:

m/44'/60'/0'/0/0
m/44'/60'/0'/1/0
m/44'/60'/0'/2/0

Ledger Live's standard BIP-44 account paths are instead:

m/44'/60'/0'/0/0
m/44'/60'/1'/0/0
m/44'/60'/2'/0/0

I confirmed this directly against a Ledger Nano X. The expected third Ledger Live account was returned for m/44'/60'/2'/0/0, but not by celocli --ledgerLiveMode --ledgerAddresses 3.

Expected behavior

--ledgerLiveMode --ledgerAddresses 3 should retrieve hardened account indexes 0, 1, and 2:

m/44'/60'/0'/0/0
m/44'/60'/1'/0/0
m/44'/60'/2'/0/0

Actual behavior

It fixes the account component at 0' and iterates the non-hardened change component. Only the first Ledger Live account is therefore discoverable. Transactions from later accounts fail because the --from address is not loaded into the Ledger wallet.

Likely cause

In packages/cli/src/base.ts, Ledger Live mode maps the requested indexes to changeIndexes:

derivationPathIndexes: isLedgerLiveMode ? [0] : indicesToIterateOver,
changeIndexes: isLedgerLiveMode ? indicesToIterateOver : [0],

In packages/sdk/wallets/wallet-ledger/src/ledger-wallet.ts, the account component is fixed by baseDerivationPath:

const [purpose, coinType, account] = this.baseDerivationPath.split('/')
for (const changeIndex of this.changeIndexes) {
  for (const addressIndex of this.derivationPathIndexes) {
    const derivationPath =
      `${purpose}/${coinType}/${account}/${changeIndex}/${addressIndex}`
  }
}

This prevents --ledgerLiveMode from iterating the hardened account component.

Ledger Live's default scheme is 44'/<coin_type>'/<account>'/<node>/<address>:

Suggested fix

Support iterating accountIndexes, producing:

`${purpose}/${coinType}/${accountIndex}'/${changeIndex}/${addressIndex}`

Ledger Live mode should effectively use:

accountIndexes: indicesToIterateOver
changeIndexes: [0]
derivationPathIndexes: [0]

Tests should include account index 1 or 2; index 0 cannot distinguish the incorrect path from the Ledger Live path.

Workaround

Set the desired account as the base path and do not use --ledgerLiveMode:

celocli config:set --derivationPath "m/44'/60'/2'"
celocli account:list --useLedger --ledgerAddresses 1 --local

This derives m/44'/60'/2'/0/0. Restore the default afterward with:

celocli config:set --derivationPath eth

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingcelocli

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions