Интеграция кошелька с Ledger
Ledger — самый распространённый hardware wallet среди пользователей DeFi и профессиональных трейдеров. Интеграция с ним открывает доступ к аудитории, которая принципиально не хранит ключи в browser extension или mobile app. Это не просто «добавить кнопку подключить» — протокол общения с устройством специфичен, и без понимания его деталей вы получите нестабильную интеграцию с плохим UX.
Как работает связь с устройством
Ledger использует несколько транспортных уровней в зависимости от среды:
- WebUSB — прямое USB-соединение в браузере (Chrome, Edge). Требует HTTPS или localhost. Не работает в Firefox из коробки.
- WebHID — более новый API, рекомендуется как основной. Поддерживает Ledger Nano S Plus и Nano X.
-
Bluetooth — только Nano X, через
@ledgerhq/hw-transport-web-ble. Нестабилен на мобильных браузерах. -
Node.js HID — для desktop приложений через
@ledgerhq/hw-transport-node-hid.
Библиотека @ledgerhq/hw-app-eth инкапсулирует APDU-протокол (Application Protocol Data Unit) — низкоуровневые команды, которыми хост общается с устройством. Вам не нужно знать APDU напрямую, но важно понимать: каждая операция — это синхронная команда/ответ, устройство обрабатывает их последовательно.
Получение адреса и подпись транзакций
Базовый флоу получения адреса:
import TransportWebHID from "@ledgerhq/hw-transport-webhid";
import Eth from "@ledgerhq/hw-app-eth";
async function getLedgerAddress(derivationPath: string): Promise<string> {
const transport = await TransportWebHID.create();
const eth = new Eth(transport);
try {
const result = await eth.getAddress(derivationPath, true); // true = display on device
return result.address;
} finally {
await transport.close();
}
}
Derivation path — критичный момент. Стандарт BIP44 для Ethereum: m/44'/60'/0'/0/0. Ledger Live использует этот путь. Старый Ledger Live использовал m/44'/60'/0' (без последних двух сегментов) — у некоторых пользователей адреса именно там. При интеграции стоит поддерживать несколько path-ов с возможностью выбора.
Подпись транзакции требует сериализации через RLP и корректной передачи chain ID для EIP-155:
async function signTransaction(tx: TransactionRequest): Promise<string> {
const transport = await TransportWebHID.create();
const eth = new Eth(transport);
// Сериализуем транзакцию без подписи
const unsignedTx = ethers.utils.serializeTransaction(tx);
const rlpEncoded = unsignedTx.slice(2); // убираем 0x
const result = await eth.signTransaction(
"m/44'/60'/0'/0/0",
rlpEncoded,
null // resolution для ERC-20 токенов
);
// Собираем подпись обратно
const signature = {
v: parseInt(result.v, 16),
r: '0x' + result.r,
s: '0x' + result.s,
};
return ethers.utils.serializeTransaction(tx, signature);
}
EIP-712 и typed data
Для подписи EIP-712 сообщений (permit, typed orders) — eth.signEIP712Message. Важно: старые прошивки Ledger не поддерживают eth.signEIP712HashedMessage с полным domain separator. Нужно проверять версию прошивки или использовать fallback на eth.signPersonalMessage.
Типичные проблемы и их решения
Устройство занято другим приложением. Ledger может быть подключён к Ledger Live или другой вкладке. Транспорт вернёт ошибку TransportError: Invalid channel. Решение: обрабатывать эту ошибку явно и показывать пользователю сообщение «Закройте Ledger Live перед использованием».
Blind signing отключён. По умолчанию Ledger требует включить «blind signing» в настройках Ethereum app на устройстве для подписи контрактных транзакций. Без этого — ошибка 0x6a80. Решение: в UI предупреждать пользователя до инициации транзакции.
Таймаут ожидания подтверждения. Пользователь не подтвердил на устройстве в течение отведённого времени. @ledgerhq/hw-transport-webhid по умолчанию не имеет таймаута — транзакция висит бесконечно. Добавляйте Promise.race с таймаутом и кнопкой отмены в UI.
Несовместимость с wagmi/viem. Если используете wagmi v2, стандартный коннектор для Ledger — через @ledgerhq/connect-kit-loader или кастомный коннектор на базе createConnector. Прямая интеграция через hw-app-eth работает, но требует ручного управления provider.
Интеграция с Ledger Connect Kit
Для web-приложений Ledger предлагает Connect Kit — универсальный способ подключения через WalletConnect v2, iframe или прямой WebHID:
import { loadConnectKit, SupportedProviders } from "@ledgerhq/connect-kit-loader";
const connectKit = await loadConnectKit();
connectKit.checkSupport({
providerType: SupportedProviders.Ethereum,
walletConnectVersion: 2,
projectId: "YOUR_WC_PROJECT_ID",
});
const provider = await connectKit.getProvider();
Это упрощает поддержку мобильных пользователей (Nano X через BLE + мобильный браузер), но добавляет зависимость от Ledger's infrastructure.
Стек и сроки
| Компонент | Библиотека |
|---|---|
| WebHID транспорт | @ledgerhq/hw-transport-webhid |
| Ethereum app | @ledgerhq/hw-app-eth |
| Bluetooth | @ledgerhq/hw-transport-web-ble |
| wagmi коннектор | кастомный или Connect Kit |
Базовая интеграция (получение адреса + подпись ETH/ERC-20 транзакций + EIP-712) — 1-2 недели. Включает обработку всех ошибочных сценариев и тестирование на реальных устройствах (Nano S, Nano S Plus, Nano X).







