Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/fine-colts-battle.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@ledgerhq/device-signer-kit-aleo": minor
---

Add aleo-signer module with get app config command support
1 change: 1 addition & 0 deletions apps/docs/pages/docs/references/signers/_meta.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ export default {
btc: "Signer Bitcoin",
hyperliquid: "Signer Hyperliquid",
cosmos: "Signer Cosmos",
aleo: "Signer Aleo",
};
314 changes: 314 additions & 0 deletions apps/docs/pages/docs/references/signers/aleo.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,314 @@
# Aleo Signer Kit

This module provides the implementation of the Ledger aleo signer of the Device Management Kit. It enables interaction with the aleo application on a Ledger device including:

- Retrieving the aleo address using a given derivation path
- Signing a aleo transaction
- Signing a message displayed on a Ledger device
- Retrieving the app configuration

## πŸ”Ή Index

1. [How it works](#-how-it-works)
2. [Installation](#-installation)
3. [Initialisation](#-initialisation)
4. [Use Cases](#-use-cases)
- [Get App Configuration](#use-case-1-get-app-configuration)
- [Get Address](#use-case-2-get-address)
- [Sign Transaction](#use-case-3-sign-transaction)
- [Sign Message](#use-case-4-sign-message)
5. [Observable Behavior](#-observable-behavior)
6. [Example](#-example)

## πŸ”Ή How it works

The Ledger Aleo 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](https://en.wikipedia.org/wiki/Smart_card_application_protocol_data_unit)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](https://github.com/LedgerHQ/device-sdk-ts/tree/develop/packages/device-management-kit) package, so you need to install it first.

To install the `device-signer-kit-aleo` package, run the following command:

```sh
npm install @ledgerhq/device-signer-kit-aleo
```

## πŸ”Ή Initialisation

To initialise a Aleo signer instance, you need a Ledger Device Management Kit instance and the ID of the session of the connected device. Use the `SignerAleoBuilder`:

```typescript
const signerAleo = new SignerAleoBuilder({ dmk, sessionId }).build();
```

## πŸ”Ή Use Cases

The `SignerAleoBuilder.build()` method will return a `SignerAleo` instance that exposes 4 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`.

---
Comment on lines +45 to +49
Copy link

Copilot AI Feb 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This documentation states the signer exposes 4 working methods (get address / sign transaction / sign message / get config), but in this PR only get-app-config is implemented and the other commands are stubbed (they throw). Please align the docs with the actual supported surface area, or implement the missing commands before documenting them.

Copilot uses AI. Check for mistakes.

### Use Case 1: Get App Configuration

This method allows users to retrieve the app configuration from the Ledger device.

```typescript
const { observable, cancel } = signerAleo.getAppConfig();
```

#### **Returns**

- `observable` Emits DeviceActionState updates, including the following details:

```typescript
type GetAppConfigCommandResponse = {
// TODO: Define the app configuration response type
// Example:
// version: string;
// flags: number;
Comment on lines +64 to +68
Copy link

Copilot AI Feb 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the Get App Configuration docs, the return type block is still a TODO placeholder, but the code currently returns an AppConfig with a version string. Please update the documented response type so it matches the implementation.

Suggested change
type GetAppConfigCommandResponse = {
// TODO: Define the app configuration response type
// Example:
// version: string;
// flags: number;
type AppConfig = {
/**
* Application version in string format (for example, "1.0.0").
*/
version: string;

Copilot uses AI. Check for mistakes.
};
```

- `cancel` A function to cancel the action on the Ledger device.

---

### Use Case 2: Get Address

This method allows users to retrieve the aleo address based on a given `derivationPath`.

```typescript
const { observable, cancel } = signerAleo.getAddress(derivationPath, options);
```

#### **Parameters**

- `derivationPath`

- **Required**
- **Type:** `string` (e.g., `"m/44'/0'/0'/0/0"`)
- The derivation path used for the aleo address. See [here](https://www.ledger.com/blog/understanding-crypto-addresses-and-derivation-paths) for more information.

- `options`

- Optional
- Type: `AddressOptions`

```typescript
type 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 aleo app automatically (`true`) or not (`false`).

#### **Returns**

- `observable` Emits DeviceActionState updates, including the following details:

```typescript
type GetAddressCommandResponse = {
publicKey: Uint8Array;
chainCode?: Uint8Array;
};
```

- `cancel` A function to cancel the action on the Ledger device.

---

### Use Case 3: Sign Transaction

This method allows users to sign a aleo transaction.

```typescript
const { observable, cancel } = signerAleo.signTransaction(
derivationPath,
transaction,
options,
);
```

#### **Parameters**

- `derivationPath`

- **Required**
- **Type:** `string` (e.g., `"m/44'/0'/0'/0/0"`)
- The derivation path used for the aleo transaction. See [here](https://www.ledger.com/blog/understanding-crypto-addresses-and-derivation-paths) for more information.

- `transaction`

- **Required**
- **Type:** `Uint8Array`
- The serialized transaction to be signed.

- `options`

- Optional
- Type: `TransactionOptions`

```typescript
type TransactionOptions = {
skipOpenApp?: boolean;
};
```

- `skipOpenApp`: An optional boolean indicating whether to skip opening the aleo app automatically (`true`) or not (`false`).

#### **Returns**

- `observable` Emits DeviceActionState updates, including the following details:

```typescript
type Signature = {
r: string;
s: string;
v?: number;
};
```

- `cancel` A function to cancel the action on the Ledger device.

---

### Use Case 4: Sign Message

This method allows users to sign a text string that is displayed on Ledger devices.

```typescript
const { observable, cancel } = signerAleo.signMessage(derivationPath, message);
```

#### **Parameters**

- `derivationPath`

- **Required**
- **Type:** `string` (e.g., `"m/44'/0'/0'/0/0"`)
- The derivation path used for the aleo message. See [here](https://www.ledger.com/blog/understanding-crypto-addresses-and-derivation-paths) for more information.

- `message`

- **Required**
- **Type:** `string | Uint8Array`
- The message to be signed, which will be displayed on the Ledger device.

#### **Returns**

- `observable` Emits DeviceActionState updates, including the following details:

```typescript
type Signature = {
r: string;
s: string;
v?: number;
};
```

- `cancel` A function to cancel the action on the Ledger device.

---

## πŸ”Ή Observable Behavior

Each method returns an [Observable](https://rxjs.dev/guide/observable) emitting updates structured as [`DeviceActionState`](https://github.com/LedgerHQ/device-sdk-ts/blob/develop/packages/device-management-kit/src/api/device-action/model/DeviceActionState.ts). 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:**

```typescript
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:

```typescript
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 Aleo Signer by trying it out in our online [sample application](https://app.devicesdk.ledger-test.com/). Experience how it works and see its capabilities in action. Of course, you will need a Ledger device connected.
1 change: 1 addition & 0 deletions apps/sample/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
"@ledgerhq/device-management-kit-devtools-websocket-common": "workspace:^",
"@ledgerhq/device-management-kit-devtools-websocket-connector": "workspace:^",
"@ledgerhq/device-mockserver-client": "workspace:^",
"@ledgerhq/device-signer-kit-aleo": "workspace:^",
"@ledgerhq/device-signer-kit-bitcoin": "workspace:^",
"@ledgerhq/device-signer-kit-cosmos": "workspace:^",
"@ledgerhq/device-signer-kit-ethereum": "workspace:^",
Expand Down
25 changes: 14 additions & 11 deletions apps/sample/src/app/client-layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import { CalInterceptorProvider } from "@/providers/CalInterceptorProvider";
import { DmkProvider } from "@/providers/DeviceManagementKitProvider";
import { LedgerKeyringProtocolProvider } from "@/providers/LedgerKeyringProvider";
import { SettingsGate } from "@/providers/SettingsGate";
import { SignerAleoProvider } from "@/providers/SignerAleoProvider";
import { SignerCosmosProvider } from "@/providers/SignerCosmosProvider";
import { SignerEthProvider } from "@/providers/SignerEthProvider";
import { store } from "@/state/store";
Expand Down Expand Up @@ -74,17 +75,19 @@ const ClientRootLayout: React.FC<PropsWithChildren> = ({ children }) => {
<DmkProvider>
<LedgerKeyringProtocolProvider>
<SignerEthProvider>
<SignerCosmosProvider>
<CalInterceptorProvider>
<GlobalStyle />
<head>
<link rel="shortcut icon" href="../favicon.png" />
</head>
<body>
<RootApp>{children}</RootApp>
</body>
</CalInterceptorProvider>
</SignerCosmosProvider>
<SignerAleoProvider>
<SignerCosmosProvider>
<CalInterceptorProvider>
<GlobalStyle />
<head>
<link rel="shortcut icon" href="../favicon.png" />
</head>
<body>
<RootApp>{children}</RootApp>
</body>
</CalInterceptorProvider>
</SignerCosmosProvider>
</SignerAleoProvider>
</SignerEthProvider>
</LedgerKeyringProtocolProvider>
</DmkProvider>
Expand Down
11 changes: 11 additions & 0 deletions apps/sample/src/app/signers/aleo/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
"use client";
import React from "react";

import { SessionIdWrapper } from "@/components/SessionIdWrapper";
import { SignerAleoView } from "@/components/SignerAleoView";

const Signer: React.FC = () => {
return <SessionIdWrapper ChildComponent={SignerAleoView} />;
};

export default Signer;
Loading
Loading