Ledger signer

The Ledger Signer implements the Signer interface of Taquito, allowing you to sign operation from a Ledger Nano device.

note

You need to have the Tezos Wallet app installed and opened on your Ledger device when using the Ledger Signer.

You first need to import the desired transport from the LedgerJs library. The Ledger Signer has currently been tested with @ledgerhq/hw-transport-node-hid for Node based application and with @ledgerhq/hw-transport-u2f for web applications. You will need to pass an instance of the transport of your choice to your Ledger Signer as follows:

import TransportU2F from "@ledgerhq/hw-transport-u2f";
import { LedgerSigner } from '@taquito/ledger-signer';
const transport = await TransportU2F.create();
const ledgerSigner = new LedgerSigner(transport);

The constructor of the LedgerSigner class can take three other parameters. If none are specified, the default values are used.

  • path: default value is "44'/1729'/0'/0'"
    You can use as a parameter the HDPathTemplate which refers to 44'/1729'/${account}'/0'. You will only have to specify what is the index of the account you want to use. Or you can also use a complete path as a parameter.
    More details about paths below
  • prompt: default is true
    If true, you will be asked, on your Ledger device, for validation to send your public key. Note that confirmation is required when using @ledgerhq/hw-transport-u2f, so you should not set this parameter to false if you are using this transport.
  • derivationType: default is DerivationType.tz1
    It can be DerivationType.tz1, DerivationType.tz2 or DerivationType.tz3.
import { LedgerSigner, DerivationType, HDPathTemplate } from '@taquito/ledger-signer';
import { Tezos } from '@taquito/taquito';
const ledgerSigner = new LedgerSigner(
transport, //required
HDPathTemplate(1), // path optional (equivalent to "44'/1729'/1'/0'")
true, // prompt optional
DerivationType.tz1 // derivationType optional
);

Usage

import { LedgerSigner } from '@taquito/ledger-signer';
import { Tezos } from '@taquito/taquito';
import TransportNodeHid from "@ledgerhq/hw-transport-node-hid";
const transport = await TransportNodeHid.create();
const ledgerSigner = new LedgerSigner(transport);
Tezos.setProvider({ signer: ledgerSigner });
//Get the public key and the public key hash from the Ledger
const publicKey = await Tezos.signer.publicKey();
const publicKeyHash = await Tezos.signer.publicKeyHash();

You are all set to sign operation with your Ledger device. You can use your configured ledger signer with both the Contract API or the Wallet API as usual. If you try the following example, you will be asked on your Ledger device to confirm the transaction before sending it.

const amount = 0.5;
const address = 'tz1h3rQ8wBxFd8L9B3d7Jhaawu6Z568XU3xY';
console.log(`Transfering ${amount} ꜩ to ${address}...`);
Tezos.contract.transfer({ to: address, amount: amount })
.then(op => {
console.log(`Waiting for ${op.hash} to be confirmed...`);
return op.confirmation(1).then(() => op.hash);
})
.then(hash => console.log(`Operation injected: https://carthagenet.tzstats.com/${hash}`))
.catch(error => console.log(`Error: ${error} ${JSON.stringify(error, null, 2)}`));

Derivation paths, HD wallet & BIP Standards

Derivation paths are related to Hierarchical Deterministic Wallet (HD wallet). HD wallet is a system allowing to derive addresses from a mnemonic phrase combined with a derivation path. Changing one index of the path will allow accessing a different account. We can access an unlimited number of addresses with HD wallet.

Here is the technical specification for the most commonly used HD wallets :

According to BIP44, path is described as follow: purpose' / coin_type' / account' / change / address_index. Where purpose is a constant set to 44' and coin_type is set to 1729' for Tezos.

Different Tezos HD Paths

The path always begins with 44'/1729' and we see some difference for the three other indexes across the Tezos ecosystem. We can notice that changing any number for the three last indexes of the path (account' / change / address_index) will lead to different accounts. But, to ensure consistency, it is important trying to follow the same convention regarding the structure of the path and which index to increase to access the next address.

In Tezos, we generally see a slight difference in the path compared to the BIP44 specification. It is common to see path made of 4 indexes instead of 5 (default path being 44'/1729'/0'/0' instead of 44'/1729'/0'/0'/0'). For example, the default path used by tezos-client is 44'/1729'/0'/0'. Based on what is done by the Tezos-client, the default path used by Taquito in the LedgerSigner is also 44'/1729'/0'/0'. Taquito offers a template for the path called HDPathTemplate. This template uses four indexes and suggests doing the iteration on the account index.
For example, you can use HDPathTemplate(0) (equivalent to 44'/1729'/0'/0') to access the first address, HDPathTemplate(1) equivalent to 44'/1729'/1'/0') to access the second address, HDPathTemplate(2) (equivalent to 44'/1729'/2'/0') to access the third address... In order to meet the needs of each user, this template is not imposed by Taquito.

We can see other implementations that adhere to BIP44 and use 44'/1729'/0'/0'/0', where the next address is accessed by incrementing account or address_index.

Quick summary of different default paths used:

WalletPath
Tezbox"44'/1729'/{account}'/0'" or "44'/1729'/0'/{account}'"
Galleon"44'/1729'/{account}'/0'/0'" or "44'/1729'/0'/0'/{account}'"

Some considerations about paths

According to BIP44, "Software should prevent a creation of an account if a previous account does not have a transaction history (meaning none of its addresses have been used before)." When building an app using the LedgerSigner, you must be careful not to allow users to access an account with a path structure that does not follow any convention. Otherwise, users could have difficulties using their accounts with other wallets that are not compatible with their paths. As stated before, HD wallets allow you to get an infinity of address. According to BIP44, wallets should follow an Account discovery algorithm meaning that it is possible that the wallet won't found an account created with an unconventional path. We can think about how hard it would be for a user who had created an account with a no common path and forgot it to find it back.

More about derivation path here

https://ethereum.stackexchange.com/questions/70017/can-someone-explain-the-meaning-of-derivation-path-in-wallet-in-plain-english-s

https://github.com/LedgerHQ/ledger-live-desktop/issues/2559

https://github.com/obsidiansystems/ledger-app-tezos/#importing-the-key-from-the-ledger-device

https://github.com/MyCryptoHQ/MyCrypto/issues/2070

https://medium.com/mycrypto/wtf-is-a-derivation-path-c3493ca2eb52

Paths scanning

Having your Ledger device connected to your computer and the Tezos Wallet App opened, you can run the following code example. It will scan your Ledger from path 44'/1729'/0'/0' to 44'/1729'/9'/0' to get public key hashes and the balance for revealed accounts. Confirmations will be asked on your Ledger to send the public keys. This example is not intended to be a complete example of paths scanning but only a rough outline of what is possible to do.

Live Editor
Result

An example of path scanning using @ledgerhq/hw-transport-node-hid can be found here. This example directly retrieves the public keys from the Ledger without asking for confirmation on the device.