ledger-dmk-interop
Expanded the DMK signer support in ledger with a cosmos signer and viem interop
Problem Statement
This project is a TypeScript/JavaScript Ledger signer for Cosmos chains, built on top of Ledger’s Device Management Kit (DMK) instead of the older hw-app-cosmos API. It implements the low-level APDU flows defined in the Cosmos app spec (GET_ADDR / GET_PUBLIC_KEY / SIGN) and exposes them as clean, high-level commands and device actions that wallets or dApps can plug into.Concretely, it handles derivation paths (e.g. m/44'/118'/0'/0/0), HRP/bech32 prefixes (like cosmos, noble, etc.), and builds the exact binary payloads the Ledger app expects: HRP length + HRP + 5-element BIP44 path in little-endian. It then parses responses into usable structures (compressed secp256k1 pubkey, bech32 address), without relying on legacy Ledger Live helpers.For signing, the project targets Amino JSON (StdSignDoc) rather than protobuf SignDoc, matching the Ledger Cosmos TX spec. It takes a StdSignDoc, serializes it into canonical JSON (sorted keys, no spaces), splits it into APDU-sized chunks, and drives the multi-step signing flow using INS_SIGN with the correct P1 phases (init / add / last). The resulting signature is received as a DER-encoded ECDSA signature from the device, ready to be embedded into Amino or protobuf transactions. Overall, it’s a modern, DMK-based Cosmos Ledger signer that mirrors the robustness of the Ethereum/Solana integrations but tailored to the Cosmos app’s simpler, JSON-driven model.PART 2 The second part of this project is about integrating Ledger’s Device Management Kit (DMK) directly into the Ethereum tooling ecosystem via viem and wagmi, so a Ledger device can be used as a first-class signer in modern dapps. Concretely, it introduces a small functional “connector layer” that takes DMK’s device-action pattern (observables, sessions, context modules) and exposes it as a clean toAccount / WalletClient compatible signer. Under the hood, it handles: discovering and connecting to a Ledger device, opening a DMK session, building a DefaultSignerEth, and then wrapping DMK’s signTransaction, signMessage, and signTypedData flows into Promise-based methods that return the exact formats viem expects (65-byte signatures for messages/typed data, { r, s, yParity } for transactions). The goal is to let apps plug a Ledger signer into viem/wagmi with minimal boilerplate, while still benefiting from DMK’s richer device state machine and context features, without automatically broadcasting anything to the chain.
Solution
PART ONE - Cosmos DMK SignerThe Cosmos Device Signer kit implemented is based on the cosmos ledger signing ADUSPEC outlined in https://github.com/LedgerHQ/app-cosmos/blob/develop/docs/APDUSPEC.md. and the transaction-spec outlined in https://github.com/LedgerHQ/app-cosmos/blob/develop/docs/TXSPEC.md.First we declared the App-binders and the data Models to spec out the high level api for connecting to lower level interactions. We then defined the Cosmos SignDoc model based on Amino Transaction Signing, because the existing cosmos ledger app only supports Amino signing currently.The main gateway via which the DMK signer interacts with the Ledger device is the APDU which can carry up to 255 bytes of data, the implementations of GetAddress and SigTransaction following the existing structure of the eth/solana/btc signers adhering to the uniform api model and implements the APDU exchange correctly.PART TWO - Viem DMK Interop/integrationThe second part of the project is built as a thin “bridge layer” between Ledger’s Device Management Kit (DMK) / Ethereum signer stack and the viem/wagmi account model, with a strong focus on functional design and not broadcasting anything by default.It starts by wiring up DMK with web transports (WebHID/WebBLE) and the Context Module so the app can discover a Ledger device, open a session, and talk to the Ethereum app on the device. On top of that, it builds a dedicated Ethereum signer object from Ledger’sSignerEththat knows how to perform all the usual device actions: get an address, sign a transaction, sign a personal message, and sign typed data.Because DMK exposes these actions as observables (state machines that stream device states), the project adds a reusable helper that turns any device action into a single-shot Promise. This helper listens to the observable until the action reaches a “completed” status, then resolves with the result or rejects on error. Using this, all Ledger signing flows become regular async functions that can be called from viem.From there, the project defines a “Ledger connection” object that is completely functional: it groups together the DMK instance, the session ID, the Ethereum signer, the chosen derivation path, the resolved Ledger address, and a disconnect function. Nothing is hidden in globals; everything needed for signing is carried around in this connection object.Finally, this connection is adapted into a viem-compatible account. The Ledger signature format (r, s, v) is converted into the shapes viem expects: a compact 65-byte signature for messages and typed data, and an object with r, s and yParity for transactions so viem can serialize a fully signed raw transaction. The account’ssignMessage,signTransaction, andsignTypedDatamethods use the Ledger signer under the hood but always return values in viem’s standard formats. The result is that any app using viem or wagmi can treat the Ledger device as a normal account, while all the DMK complexity (device discovery, sessions, state machines) stays neatly encapsulated behind a textual, functional interface and never automatically broadcasts to the chain.
Hackathon
ETHGlobal Buenos Aires
2025
Prizes
- 🏆
[L] HARDWARE INTEGRATIONS2nd place
Ledger
Contributors
- code-z2
7 contributions