Polkadot Signer Kit
This module provides the implementation of the Ledger polkadot signer of the Device Management Kit. It enables interaction with the polkadot application on a Ledger device including:
- Retrieving the polkadot address using a given derivation path
- Signing a polkadot transaction
🔹 Index
🔹 How it works
The Ledger Polkadot Signer utilizes the advanced capabilities of the Ledger device to provide secure operations for end users. It takes advantage of the interface provided by the Device Management Kit to establish communication with the Ledger device and execute various operations. The communication with the Ledger device is performed using APDU s (Application Protocol Data Units), which are encapsulated within the Command object. These commands are then organized into tasks, allowing for the execution of complex operations with one or more APDUs. The tasks are further encapsulated within DeviceAction objects to handle different real-world scenarios. Finally, the Signer exposes dedicated and independent use cases that can be directly utilized by end users.
🔹 Installation
Note: This module is not standalone; it depends on the @ledgerhq/device-management-kit package, so you need to install it first.
To install the device-signer-kit-polkadot package, run the following command:
npm install @ledgerhq/device-signer-kit-polkadot🔹 Initialisation
To initialise a Polkadot signer instance, you need a Ledger Device Management Kit instance and the ID of the session of the connected device. Use the SignerPolkadotBuilder:
const signerPolkadot = new SignerPolkadotBuilder({ dmk, sessionId }).build();🔹 Use Cases
The SignerPolkadotBuilder.build() method will return a SignerPolkadot instance that exposes 2 dedicated methods, each of which calls an independent use case. Each use case will return an object that contains an observable and a method called cancel.
Use Case 1: Get Address
This method allows users to retrieve the polkadot address based on a given derivationPath.
const { observable, cancel } = signerPolkadot.getAddress(
derivationPath,
ss58Prefix,
options,
);Parameters
-
derivationPath- Required
- Type:
string(e.g.,"m/44'/0'/0'/0/0") - The derivation path used for the polkadot address. See here for more information.
-
ss58Prefix- Required
- Type:
number(e.g.,0for Polkadot,42for Bittensor) - The SS58 address format prefix for the target network.
-
options-
Optional
-
Type:
AddressOptionstype AddressOptions = { checkOnDevice?: boolean; skipOpenApp?: boolean; }; -
checkOnDevice: An optional boolean indicating whether user confirmation on the device is required (true) or not (false). -
skipOpenApp: An optional boolean indicating whether to skip opening the polkadot app automatically (true) or not (false).
-
Returns
observableEmits DeviceActionState updates, including the following details:
type GetAddressCommandResponse = {
publicKey: Uint8Array;
chainCode?: Uint8Array;
};cancelA function to cancel the action on the Ledger device.
Use Case 2: Sign Transaction
This method allows users to sign a polkadot transaction.
const { observable, cancel } = signerPolkadot.signTransaction(
derivationPath,
blob,
metadata,
options,
);Parameters
-
derivationPath- Required
- Type:
string(e.g.,"m/44'/0'/0'/0/0") - The derivation path used for the polkadot transaction. See here for more information.
-
blob- Required
- Type:
Uint8Array - The SCALE-encoded transaction payload to be signed.
-
metadata- Required
- Type:
Uint8Array - The metadata proof used for transaction clear signing (Ledger Polkadot generic app).
-
options-
Optional
-
Type:
TransactionOptionstype TransactionOptions = { skipOpenApp?: boolean; }; -
skipOpenApp: An optional boolean indicating whether to skip opening the polkadot app automatically (true) or not (false).
-
Returns
observableEmits DeviceActionState updates, including the following details:
type Signature = Uint8Array;cancelA function to cancel the action on the Ledger device.
🔹 Observable Behavior
Each method returns an Observable emitting updates structured as DeviceActionState. These updates reflect the operation’s progress and status:
- NotStarted: The operation hasn’t started.
- Pending: The operation is in progress and may require user interaction.
- Stopped: The operation was canceled or stopped.
- Completed: The operation completed successfully, with results available.
- Error: An error occurred.
Example Observable Subscription:
observable.subscribe({
next: (state: DeviceActionState) => {
switch (state.status) {
case DeviceActionStatus.NotStarted: {
console.log("The action is not started yet.");
break;
}
case DeviceActionStatus.Pending: {
const {
intermediateValue: { requiredUserInteraction },
} = state;
// Access the intermediate value here, explained below
console.log(
"The action is pending and the intermediate value is: ",
intermediateValue,
);
break;
}
case DeviceActionStatus.Stopped: {
console.log("The action has been stopped.");
break;
}
case DeviceActionStatus.Completed: {
const { output } = state;
// Access the output of the completed action here
console.log("The action has been completed: ", output);
break;
}
case DeviceActionStatus.Error: {
const { error } = state;
// Access the error here if occurred
console.log("An error occurred during the action: ", error);
break;
}
}
},
});Intermediate Values in Pending Status:
When the status is DeviceActionStatus.Pending, the state will include an intermediateValue object that provides useful information for interaction:
const { requiredUserInteraction } = intermediateValue;
switch (requiredUserInteraction) {
case UserInteractionRequired.VerifyAddress: {
// User needs to verify the address displayed on the device
console.log("User needs to verify the address displayed on the device.");
break;
}
case UserInteractionRequired.SignTransaction: {
// User needs to sign the transaction displayed on the device
console.log("User needs to sign the transaction displayed on the device.");
break;
}
case UserInteractionRequired.SignPersonalMessage: {
// User needs to sign the message displayed on the device
console.log("User needs to sign the message displayed on the device.");
break;
}
case UserInteractionRequired.None: {
// No user action required
console.log("No user action needed.");
break;
}
case UserInteractionRequired.UnlockDevice: {
// User needs to unlock the device
console.log("The user needs to unlock the device.");
break;
}
case UserInteractionRequired.ConfirmOpenApp: {
// User needs to confirm on the device to open the app
console.log("The user needs to confirm on the device to open the app.");
break;
}
default:
// Type guard to ensure all cases are handled
const uncaughtUserInteraction: never = requiredUserInteraction;
console.error("Unhandled user interaction case:", uncaughtUserInteraction);
}🔹 Example
We encourage you to explore the Polkadot Signer by trying it out in our online sample application . Experience how it works and see its capabilities in action. Of course, you will need a Ledger device connected.