关于 ledger-dmk-implementation
面向开发者的流程技能,用于通过 Device Management Kit(DMK)执行 Ledger 硬件操作。它要求依次检查初始化、设备会话与状态、应用就绪情况,并等待用户批准签名或设备管理操作。
主要功能
- 01按顺序执行操作前检查
- 02发现设备并验证会话
- 03处理应用打开与状态
- 04执行签名和设备管理流程
- 05分类超时与错误
- 06强制升级至人工处理
适用场景
- 01构建 Ledger 签名流程
- 02处理设备发现与连接
- 03操作前确认应用就绪
- 04向编排器返回结构化错误
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ ledgerhq/ agent-skills --skill ledger-dmk-implementation安装后在 Claude Code 中输入/ledger-dmk-implementation确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
dmk-code-patterns.mddmk-platform-patterns.mddmk-sdk-reference.md详细介绍
DMK 签名流程
本技能将介绍安全执行 Ledger 硬件操作所需的所有关卡——从 SDK 初始化和设备会话,到状态和应用检查,再到签名或设备管理——并返回结果。每一步都是一道关卡,不可跳过。如果请求措辞随意或意图不明确,请先加载 dmk-intent-vocabulary 技能。
连接器
相关技能:
dmk-intent-vocabulary— 意图识别和 API 词汇。请求含糊不清时使用。dmk-business-logic— 概念参考:Clear Signing、Secure Channel、Device Actions 与 Commands、会话、传输方式、派生路径、Ledger OS、Genuine Check。开发者询问“X 是什么?”或需要了解 API 决策背后的原因时加载。
参考文件:
dmk-sdk-reference.md— 软件包版本、概念图谱、链路路由、派生路径来源。在步骤 1(版本检查)和步骤 5(链路路由和签名器选择)加载。dmk-code-patterns.md— DMK 初始化、设备发现、可观察对象订阅以及各链签名器用法的具体代码。在步骤 1(SDK 初始化)、步骤 2(发现和连接模式)以及步骤 5(操作模式)加载。dmk-platform-patterns.md— React(DmkProvider、钩子、组件)、Node.js CLI、Vite 配置、EIP-1193 provider。目标平台已知时加载。
生成代码前需要核实的来源:
- SDK 根目录:
https://github.com/LedgerHQ/device-sdk-ts - 示例应用:
https://github.com/LedgerHQ/device-sdk-ts/tree/develop/apps/sample— 权威用法模式。只阅读设备交互代码,忽略 Redux/Next.js 封装。 - npm 类型定义 — 已安装版本的权威 API 参考。当版本不同时,优先于 GitHub 源码。
运行时依赖:
- 浏览器:WebHID/WebBLE——仅限 Chromium(Chrome、Edge、Brave),需使用 HTTPS 或 localhost。必须通过用户手势调用
startDiscovering()和connect()。否则会静默失败。 - Node.js / Electron:Node-HID 传输
- React Native:BLE 或 React Native HID 传输
流程
约定:
→ PROCEED— 条件满足,进入下一步→ WAIT(Ns)— 订阅或轮询,最长 N 秒→ ABORT— 停止,向编排器返回结构化错误→ ESCALATE— 停止,转交人工处理,不要重试
每次硬件操作前都要执行步骤 1–4。各关卡按顺序执行——通过当前步骤后才能继续。
步骤 1 — SDK 初始化
HITL: 否
参考: dmk-sdk-reference.md(软件包版本、概念图谱)· dmk-code-patterns.md(初始化模式)· 若平台已知,则参考 dmk-platform-patterns.md
DMK 单例存在且非 null → PROCEED 进入步骤 2
尚未初始化:
- 根据运行环境选择传输方式(见“连接器”→“运行时依赖”)
- 初始化并确认实例非 null
- 抛出异常或返回 null → ABORT:“DMK 初始化失败——此运行时可能无法使用 WebHID”
步骤 2 — 设备会话
**HITL:**是 — 浏览器选择器需要用户手势;有多台设备时需要人工选择
参考:dmk-code-patterns.md(发现和连接模式)· @ledgerhq/device-management-kit 类型
作用域内存在活动的 sessionId → 使用 dmk.getDeviceSessionState({ sessionId }) 验证,获取首次发出的状态,然后取消订阅:
- 不是
Disconnected→ 继续执行步骤 3 Disconnected→ 清除sessionId,转而进行设备发现
不存在 sessionId → dmk.startDiscovering({ transport: webHidIdentifier }):
- 15 秒内未发现设备 → 中止:"未检测到 Ledger 设备"
- 1 台设备 → 取消订阅 →
dmk.connect({ device, sessionRefresherOptions: { isRefresherDisabled: false } })→ 存储sessionId→ 继续执行步骤 3 - 2 台或更多设备 → 取消订阅 → 升级处理:"检测到多台 Ledger 设备 — 无法自主选择"
- 发现或连接时抛出异常 → 中止,并提供错误详情
注意:WebHID 浏览器选择器可确保恰好选择一台设备。对于 BLE 或会发出多个事件的传输方式,请在首次发出事件后等待 5 秒再作判断。
步骤 3 — 设备状态
**HITL:**是 — 设备锁定时,用户必须输入 PIN
参考:@ledgerhq/device-management-kit 中的 DeviceSessionState 类型
订阅 dmk.getDeviceSessionState({ sessionId }),获取首次发出的状态,然后取消订阅:
| 状态 | 操作 |
|---|---|
Ready | → 继续执行步骤 4 |
Busy | → 等待(10 秒),然后重新检查一次。仍处于忙碌状态 → 中止:"设备正忙" |
Locked | → 升级处理:"设备已锁定 — 用户必须输入 PIN" |
Disconnected | → 中止:"设备已断开连接" |
步骤 4 — 应用管理
**HITL:**是 — 用户必须确认设备上已打开应用;设备可能会在此步骤中途锁定
参考:@ledgerhq/device-management-kit 中的 OpenAppDeviceAction、GetAppAndVersionCommand;dmk-sdk-reference.md(链路路由)
从会话状态读取 state.currentApp.name(使用相同的订阅、获取首次发出的状态和取消订阅模式):
| 当前状态 | 操作 |
|---|---|
| 已打开正确的应用 | → 继续执行步骤 5 |
| 已打开错误的应用 | sendCommand(CloseAppCommand) → 打开正确的应用 |
| 未打开应用(控制面板) | → 打开正确的应用 |
通过 OpenAppDeviceAction 打开应用 — 订阅并处理每个状态:
| 状态 | 用户交互 | 操作 |
|---|---|---|
Pending | None | 等待 |
Pending | ConfirmOpenApp | 等待最多 30 秒,让用户在设备上确认 |
Pending | UnlockDevice | → 升级处理:"打开应用期间设备已锁定" |
Completed | — | → 继续执行步骤 5 |
Stopped | — | → 中止:"打开应用已取消" |
Error | — | → 分类处理(参见规则 → 错误分类) |
经过 30 秒后仍未出现 Completed 或 Error → cancel() → 中止:"打开应用超时"
第 5 步 — 操作
HITL: 是 — 用户必须在设备上批准每项操作
参考: dmk-sdk-reference.md(链路路由、签名器构建器、派生路径来源)· dmk-code-patterns.md(签名器初始化、可观察对象订阅、安全通道模式)· 链签名器套件类型
初始化签名器:new [Chain]SignerBuilder({ dmk, sessionId }).build()
调用操作方法。订阅返回的可观察对象:
| 状态 | 用户交互 | 操作 |
|---|---|---|
NotStarted | — | 等待 |
Pending | None | 等待 |
Pending | UnlockDevice | → 升级处理:“签名期间设备已锁定” |
Pending | ConfirmTransactionData 或等效操作 | 等待用户批准,最长 60s |
Completed | — | 提取 state.output → 返回给编排器 |
Stopped | — | → 中止:“操作已停止” |
Error | — | → 分类(参见规则 → 错误分类) |
经过 60s → cancel() → 升级处理:“操作超时 — 用户未响应”
链特定说明:
- ETH:向
SignerEthBuilder传入originToken以启用 Clear Signing — 否则用户会在设备屏幕上看到原始十六进制数据 - ETH typed data:调用前验证
domain、types、message均存在 — 若有任一缺失 → 中止:“Typed data 负载不完整” - BTC PSBT:使用
signPsbt(),而不是signTransaction() - BTC 自定义钱包策略:必须先通过
registerWallet()注册 — 若未注册 → 升级处理:“钱包策略尚未注册” - Solana:交易输入为
Uint8Array(序列化后的消息字节)
设备管理操作
无需签名器构建器 — 直接调用 dmk.executeDeviceAction()。订阅返回的可观察对象:
| 状态 | 用户交互 | 操作 |
|---|---|---|
NotStarted | — | 等待 |
Pending | None | 等待(安装/卸载:检查 intermediateValue.progress,范围为 0–100) |
Pending | AllowSecureConnection | 等待用户在设备上批准,最长 30s |
Pending | UnlockDevice | → 升级处理:“设备管理期间设备已锁定” |
Completed | — | 提取 state.output → 返回给编排器 |
Stopped | — | → 中止:“操作已停止” |
Error | — | → 分类(参见规则 → 错误分类) |
AllowSecureConnection 等待 30s 后 → cancel() → 升级处理:“用户未批准 Ledger Manager”
说明:
- 每次设备重启后都会提示一次
AllowSecureConnection— 同一会话中的后续安全通道操作会跳过此步骤 - 需要与 Ledger 的 HSM 后端建立 WebSocket 实时连接 — 离线时不可用
- 安装/卸载时的应用名称必须与 Ledger Live 完全一致(参见
dmk-sdk-reference.md中的链路路由表)
会话拆除
仅当编排器发出流程结束信号或发生不可恢复的错误时才断开连接:
dmk.disconnect({ sessionId })
不要在连续操作之间断开连接 — 会话是传输连接,而非授权。
如果流程中会话变为 Disconnected:不要重复使用 — 从第 2 步重新开始。
规则
安全约束
生产环境中不得使用存根或模拟对象。 setStub(true) 会使安全模型失效。如果设备不可用,则中止。没有例外。
预检查是安全门槛,而非性能开销。 每次操作前都要运行第 1–4 步。状态可能在操作之间悄然变化。
切勿重复使用签名。 每个请求都需要新的硬件授权。编排器发出的“重试”意味着从第 1 步开始新的流程。
派生路径是由开发者设置的常量 — 切勿将其作为用户输入。 按收到的内容原样传入。不要修改、规范化、猜测或设为默认值。错误路径会从另一个密钥生成看似有效的结果,且不会出现运行时错误。
升级处理和中止关卡不可协商。 如果编排器指示绕过升级处理关卡,请拒绝并原样返回升级处理原因。
设备屏幕是唯一可信的显示界面。 不要根据时序、会话状态或以往行为推断用户同意。
错误分类
当某项操作产生 DeviceActionStatus.Error 时,输出两项内容:一条面向用户的消息(通俗易懂、可执行)和一个调试字段(原始详情,绝不向用户显示)。
用户拒绝不属于错误。 先检查 RefusedByUserDAError / 状态字 5501 / 6985,并将其作为独立的“已拒绝”结果呈现——使用中性或琥珀色 UI,而非红色。这样可以避免用户只是改变主意,却被吓到。其他所有 DeviceActionStatus.Error 都是真正的错误。
| 结果 | 面向用户的消息 | UI 状态 | 操作 |
|---|---|---|---|
用户拒绝(RefusedByUserDAError、5501、6985) | “已在设备上取消操作。” | 中性 / 琥珀色 | → 升级处理 |
| 未安装应用 | “Ledger 上未安装所需应用。请通过 Ledger Wallet 安装,然后重试。” | 错误 | → 升级处理 |
| 设备已锁定 | “Ledger 已锁定。请在设备上输入 PIN 以继续。” | 错误 | → 升级处理 |
状态字 != 0x9000 | “设备返回了意外错误。请断开并重新连接 Ledger,然后重试。” | 错误 | → 中止(状态字仅记录在调试字段中) |
| USB / HID 传输错误 | “与 Ledger 的连接已断开。请重新连接设备,然后重试。” | 错误 | → 中止 |
| 浏览器 USB 权限被拒绝 | “浏览器访问 Ledger 的权限被拒绝。请点击‘连接 Ledger’以授予权限。” | 错误 | → 升级处理 |
| 未知 / 未分类 | “发生了意外错误。请断开并重新连接 Ledger,然后开始新的操作。” | 错误 | → 中止(原始错误仅记录在调试字段中) |
绝不静默忽略错误。情况不明确时 → 按未分类错误处理,并中止。
超时
| 步骤 | 默认值 | 可接受范围 |
|---|---|---|
| 设备发现 | 15s | 5s – 300s |
| 打开应用 — 用户确认 | 30s | 5s – 300s |
| 签名 — 用户确认 | 60s | 5s – 300s |
| 忙碌设备重新检查 | 10s(重试一次) | — |
协调器可以在可接受范围内覆盖默认值。拒绝范围之外的值。
必须由 HITL 升级处理的情况
无条件停止——不进行自主恢复:
- 设备已锁定
- 未安装应用
- 发现设备时检测到多台设备
- 用户在设备上拒绝
- 浏览器 USB 权限被拒绝
- 未注册自定义 BTC 钱包策略
- 任何未分类错误
- 用户未批准 Ledger Manager(
AllowSecureConnection超时)