Sign a personal message

Sign a personal message


In this section, we will guide you through the creation of a web application. This application will connect to your Nano and will display the “test“ message to be signed with an Ethereum account using the personal sign message functionality introduced by EIP-191.

Web USB and Web HID applications are implemented with @ledgerhq/hw-transport-webusb and @ledgerhq/hw-transport-webhid respectively.

Use case

This tutorial will let you sign a message with your private key in order to verify and prove that you’re the owner of the address.

Tutorial Prerequisites

  • Ensure you have gone through the prerequisites.
  • Install or update your Ethereum Embedded App to the version 1.9.19 or later.


App setup

First, open a terminal and create a new folder in your usual working directory. For this tutorial, the folder will be named “example-sign-personal-message”.


mkdir example-sign-personal-message
cd example-sign-personal-message

Initialize the project by running:

npm init

Answer the questions displayed or by default press enter. There is no incidence on the execution.


mkdir src
touch src/index.html
touch src/main.js

The folder will contain these files:

Folder USB and HID


Open the folder example-sign-personal-message in a code editor.


In index.html copy and paste the following code:

<!doctype html>
<html lang="en">
   <meta charset="utf-8"/>
   <title>My First Embedded App</title>
   <script type="module" src="main.js"></script>
   <div id="main">
   <button id="hashMessage">Hash my message</button>


In main.js copy and paste the following code:


Comment out or remove the Transport package you are not using (@ledgerhq/hw-transport-webusb or @ledgerhq/hw-transport-webhid) and the corresponding const. In the example, we use webusb. Webhid is already commented in line 8 and line 23.

import 'core-js/actual';
import { listen } from "@ledgerhq/logs";
import Eth from "@ledgerhq/hw-app-eth";
// Keep this import if you want to use a Ledger Nano S/X/S Plus with the USB protocol and delete the @ledgerhq/hw-transport-webhid import
//import TransportWebUSB from "@ledgerhq/hw-transport-webusb";
// Keep this import if you want to use a Ledger Nano S/X/S Plus with the HID protocol and delete the @ledgerhq/hw-transport-webusb import
import TransportWebHID from "@ledgerhq/hw-transport-webhid";
//Display the header in the div which has the ID "main"
const initial = "<h1>Connect your Nano and open the Ethereum app. Click on “Hash my message”</h1>";
const $main = document.getElementById("main");
$main.innerHTML = initial;
document.querySelector("#hashMessage").addEventListener("click", async () => {
 $main.innerHTML = initial;
 try {
   //trying to connect to your Ledger device with USB protocol
   // const transport = await TransportWebUSB.create();
   //trying to connect to your Ledger device with HID protocol
   const transport = await TransportWebHID.create();
   //listen to the events which are sent by the Ledger packages in order to debug the app
   listen(log => console.log(log))
   //When the Ledger device connected it is trying to display the bitcoin address
   const eth = new Eth(transport);
   const signature = await eth.signPersonalMessage("44'/60'/0'/0/0", Buffer.from("test").toString("hex"));
   const signedHash = "0x" + signature.r + signature.s + signature.v.toString(16);
   //Display your bitcoin address on the screen
   const h2 = document.createElement("h2");
   h2.textContent = signedHash;
   $main.innerHTML = "<h1>Your signed message:</h1>";
   //Display the address on the Ledger device and ask to verify the address
   const { address } = await eth.getAddress("44'/60'/0'/0/0");
   const add = document.createElement("div");
   add.textContent = address
 } catch (e) {
   //Catch any error thrown and displays it on the screen
   const $err = document.createElement("code");
   $ = "#f66";
   $err.textContent = String(e.message || e);

Dependencies Installation

Install the packages


npm install --save core-js
npm install --save @ledgerhq/logs
npm install --save-dev parcel
npm install --save @ledgerhq/hw-app-eth
npm install --save eth-crypto
npm install --save-dev buffer
npm install --save-dev process
npm install --save-dev stream-browserify

Install the Transport HID or USB package

Then depending on your choice install one of the corresponding packages:

  • Install the Ledger package @ledgerhq/hw-transport-webhid which provide you with all the methods to interact with your Nano with an HID connection (make sure that you use the 6.27.1 version): npm install --save @ledgerhq/hw-transport-webhid@6.27.1

  • Install the Ledger package @ledgerhq/hw-transport-webusb which provide you with all the methods to interact with your Nano with a USB connection (make sure that you use the 6.27.1 version): npm install --save @ledgerhq/hw-transport-webusb@6.27.1


Modify “main”: “index.js” to “source”:”src/index.html”.

Ensure you have this line in your package.json:

"scripts": {
   "build": "parcel build",
   "start": "parcel"


  "alias": {
    "@ledgerhq/devices": "@ledgerhq/devices/lib-es",
    "@ledgerhq/domain-service": "@ledgerhq/domain-service/lib-es",
    "@ledgerhq/evm-tools": "@ledgerhq/evm-tools/lib-es",
    "@ledgerhq/cryptoassets": "@ledgerhq/cryptoassets/lib-es",
    "@ledgerhq/live-network": "@ledgerhq/live-network/lib-es"

Your package.json should look like this:

  "name": "example-sign-personal-message",
  "version": "1.0.0",
  "description": "",
  "source": "src/index.html",
  "scripts": {
    "build": "parcel build",
    "start": "parcel"
  "author": "",
  "license": "ISC",
  "dependencies": {
    "@ledgerhq/hw-app-eth": "^6.35.6",
    "@ledgerhq/hw-transport-webhid": "^6.28.4",
    "@ledgerhq/logs": "^6.12.0",
    "core-js": "^3.36.0",
    "eth-crypto": "^2.6.0"
  "devDependencies": {
    "buffer": "^6.0.3",
    "parcel": "^2.12.0",
    "process": "^0.11.10",
    "stream-browserify": "^3.0.0"
  "alias": {
    "@ledgerhq/devices": "@ledgerhq/devices/lib-es",
    "@ledgerhq/domain-service": "@ledgerhq/domain-service/lib-es",
    "@ledgerhq/evm-tools": "@ledgerhq/evm-tools/lib-es",
    "@ledgerhq/cryptoassets": "@ledgerhq/cryptoassets/lib-es",
    "@ledgerhq/live-network": "@ledgerhq/live-network/lib-es"


Start the development server

Now that the Setup is finished, the app has to be built to be displayed. Start the development server:

npm run start

Now the application is up and running. Open the browser and go to localhost:1234, it will display:

Hash Message

Plug your Nano

Before clicking on the “Hash my message”, connect your Nano to the USB port, unlock it and run the ethereum application. Then, click on “Hash my message”.

Go to your Nano

When you click on the “Hash my message”, a popup message will be prompt inviting you to sign a message.

Hash Message

Click the right button to see the content of the message that you are going to sign with your Ethereum address:

Hash Message

Sign the message

Click the right button to the next screens to sign the “test” message or decline it:

Hash Message

Hash Message

Once signed, the webapp will display:

  • The signed message (“hash”)
  • The Ethereum address used to sign the message

Verify on

You can verify that you are the one who signed the message by signing in on (opens in a new tab) and running the following piece of code.

  1. Adapt the code with the message you signed and the hash of the message:
const { ethers } = require("ethers");
ethers.verifyMessage("test", "0xefb42c22baa0143b322e93b24b0903a0ef47a64b716fbb77debbea55a93dec3e4417aff7dce845723240916c6e34cf17c674828b3addfb0afad966334df5b6311b");
  1. Run your code, it should now display the Ethereum address linked to the signed message (the run can take a bit of time):
Copyright © Ledger SAS. All rights reserved. Ledger, Ledger Nano S, Ledger Vault, Bolos are registered trademarks of Ledger SAS