@l8d/hardhat-ledger
v1.0.0
Published
Hardhat v3 plugin for the Ledger hardware wallet
Maintainers
Readme
hardhat-ledger
Hardhat plugin for integration with a Ledger hardware wallet.
What
This plugin extends the Hardhat network connection to work with a connected Ledger wallet seamlessly.
Installation
For Hardhat v3:
npm install --save-dev @nomicfoundation/hardhat-ledgerImport and add the plugin to your hardhat.config.ts:
import { defineConfig } from "hardhat/config";
import hardhatLedger from "@nomicfoundation/hardhat-ledger";
export default defineConfig({
plugins: [hardhatLedger],
// ... rest of config
});Or in JavaScript (hardhat.config.js):
import { defineConfig } from "hardhat/config";
import hardhatLedger from "@nomicfoundation/hardhat-ledger";
export default defineConfig({
plugins: [hardhatLedger],
// ... rest of config
});For Hardhat v2:
npm install --save-dev @nomicfoundation/hardhat-ledger@1And add the following to your hardhat.config.js:
require("@nomicfoundation/hardhat-ledger");Or for TypeScript (hardhat.config.ts):
import "@nomicfoundation/hardhat-ledger";Tasks
This plugin creates no additional tasks.
Environment extensions
This plugin adds nothing to the Hardhat Runtime Environment.
How it works
In Hardhat v3, this plugin uses the network hooks system to wrap the provider with a LedgerProvider when you create a network connection. The LedgerProvider knows how to connect and interact with a Ledger wallet.
In Hardhat v2, the plugin used extendProvider to decorate the provider.
Usage
Configure the plugin through the Hardhat Config by adding ledgerAccounts to your network configuration.
For Hardhat v3 (hardhat.config.ts):
import { defineConfig } from "hardhat/config";
import hardhatLedger from "@nomicfoundation/hardhat-ledger";
export default defineConfig({
plugins: [hardhatLedger],
networks: {
sepolia: {
url: "https://sepolia.infura.io/v3/YOUR-PROJECT-ID",
ledgerAccounts: [
"0xa809931e3b38059adae9bc5455bc567d0509ab92",
"0xda6a52afdae5ff66aa786da68754a227331f56e3",
"0xbc307688a80ec5ed0edc1279c44c1b34f7746bda",
],
},
},
});For Hardhat v2 (hardhat.config.js):
require("@nomicfoundation/hardhat-ledger");
module.exports = {
networks: {
sepolia: {
url: "https://sepolia.infura.io/v3/YOUR-PROJECT-ID",
ledgerAccounts: [
"0xa809931e3b38059adae9bc5455bc567d0509ab92",
"0xda6a52afdae5ff66aa786da68754a227331f56e3",
"0xbc307688a80ec5ed0edc1279c44c1b34f7746bda",
],
},
},
};This will make those three accounts available to the LedgerProvider. If you try to send a transaction or sign something using any of those accounts, the provider will try to connect to the Ledger wallet and find a derivation path for that address. By default, the derivation paths that are tried start from m/44'/60'/0'/0'/0 and go way up to m/44'/60'/20'/0'/0.
An additional (optional) configuration is possible to specify the derivation path that should be used, allowing 'legacy' or otherwise non-standard addresses to still be used with the plugin. You can also configure connection timeouts. An example:
Hardhat v3:
export default defineConfig({
networks: {
sepolia: {
url: "https://sepolia.infura.io/v3/YOUR-PROJECT-ID",
ledgerAccounts: [
"0xa809931e3b38059adae9bc5455bc567d0509ab92",
],
ledgerOptions: {
derivationFunction: (x) => `m/44'/60'/0'/${x}`, // legacy derivation path
openTimeout: 5000, // ms to wait for ledger to open
connectionTimeout: 5000, // ms to wait for connection
},
},
},
});Hardhat v2:
module.exports = {
networks: {
sepolia: {
url: "https://sepolia.infura.io/v3/YOUR-PROJECT-ID",
ledgerAccounts: [...],
ledgerOptions: {
derivationFunction: (x) => `m/44'/60'/0'/${x}` // legacy derivation path
}
}
}
};Using with Scripts and Tasks
Hardhat v3:
import { task } from "hardhat/config";
task("sign", "Signs a message").setAction(async (_, hre) => {
const message =
"0x5417aa2a18a44da0675524453ff108c545382f0d7e26605c56bba47c21b5e979";
const account = "0xa809931e3b38059adae9bc5455bc567d0509ab92";
// Connect to the network
const connection = await hre.network.connect("sepolia");
const signature = await connection.provider.request({
method: "personal_sign",
params: [message, account],
});
console.log(
"Signed message",
message,
"for Ledger account",
account,
"and got",
signature
);
});Hardhat v2:
task("sign", "Signs a message", async (_, hre) => {
const message =
"0x5417aa2a18a44da0675524453ff108c545382f0d7e26605c56bba47c21b5e979";
const account = "0xa809931e3b38059adae9bc5455bc567d0509ab92";
const signature = await hre.network.provider.request({
method: "personal_sign",
params: [message, account],
});
console.log(
"Signed message",
message,
"for Ledger account",
account,
"and got",
signature
);
});Debugging
This plugin uses the debug package for logging. By default, all debug output is hidden. To enable it, set the DEBUG environment variable:
# Enable all hardhat-ledger debug logs
DEBUG=hardhat-ledger:* npx hardhat run script.js
# Enable only provider logs
DEBUG=hardhat-ledger:provider npx hardhat run script.js
# Enable only network hook logs
DEBUG=hardhat-ledger:network npx hardhat run script.js
# Enable all debug logs (including other packages)
DEBUG=* npx hardhat run script.jsAvailable debug namespaces:
hardhat-ledger:provider- Provider operations and transaction signinghardhat-ledger:network- Network connection and configurationhardhat-ledger:create- LedgerProvider instantiation
Errors
The package throws and exports a few errors. In case you ever need to catch and check for them, you can use the public static method present on each of them. For example:
try {
//(...)
} catch (error) {
if (DerivationPathError.isDerivationPathError(error)) {
// error is a DerivationPathError
}
}Same for the other errors, all have their corresponding .isXXXError() method.
