@hwlt/era-connect
v0.9.0
Published
ERA hardware wallet SDK: air-gapped UR/QR account linking and transaction signing for EVM, Bitcoin (+ LTC/DOGE/DASH/BCH), Solana, Tron, TON, Cardano, Sui, Cosmos and XRP
Maintainers
Readme
@hwlt/era-connect
Air-gapped ERA hardware wallet integration for software wallets: account linking and transaction signing over animated QR codes (BC-UR / Keystone-compatible registry), for every chain the device ships — EVM (all networks), Bitcoin (+ Litecoin, Dogecoin, Dash, Bitcoin Cash), Solana, Tron, TON, Cardano, Sui, Cosmos (~35 zones) and XRP.
- Headless. You render the QR and own the camera; the SDK owns every byte of the protocol. Works in React Native, Expo, browsers, extensions and Node.
- Every address the export carries. All four Bitcoin script types including taproot, the Bitcoin-like altcoins, the three EVM derivation schemes, all three Solana schemes, 33 Cosmos zones with Ethermint, Cardano base addresses and TON wallet addresses — computed locally from the linked account.
- Zero I/O. No network calls, ever. No Node built-ins, no
Buffer—Uint8Arrayend-to-end. - Verified against the device. Golden vectors are generated by the
reference implementation and replayed byte-for-byte in CI; the fountain
encoder reproduces the canonical BCR-2020-005 frames bit-for-bit and
round-trips against
@ngraveio/bc-ur. - Hardened where it matters. The scanner refuses hostile QR frames
(stream binding needs a second distinct fragment), every reply must echo the
request id byte-for-byte, gzip replies inflate under a hard ceiling, and a
verifymodule proves the device signed exactly what you sent.
Install
npm install @hwlt/era-connectReact Native needs a CSPRNG for request ids — install
react-native-get-random-values
and import it once at app start (or inject your own via randomBytes).
60 seconds to a signature
import { EraConnect, EvmChain, WALLET_UR_TYPES } from '@hwlt/era-connect';
const era = new EraConnect({ origin: 'MyWallet' }); // shown on the device
// 1. LINK — the user shows the device's "connect" QR; you scan it.
// WALLET_UR_TYPES is every UR type a device links with — pin all of them, not
// one literal: TON, for instance, links as a standalone `crypto-hdkey`.
const scanner = era.scanner({ expectedTypes: WALLET_UR_TYPES });
// feed camera frames: scanner.receivePart(text) → { kind: 'complete', ur } when done
const accounts = era.parseAccounts(scanner.result());
const evm = accounts.evm()!;
console.log(evm.deriveAddress(0)); // 0x... — derived locally, device not needed
// 2. SIGN — build the request, render it, scan the reply.
const request = era.evm.generateSignRequest({
signData: rlpBytes, // your unsigned tx, RLP-encoded for signing
dataType: EvmChain.DataType.transaction,
path: evm.pathFor(0),
xfp: evm.xfp,
chainId: 1,
address: evm.deriveAddress(0),
});
const animated = request.toAnimated(); // drive nextFrame() at ~8 fps
// render animated.nextFrame() in your QR component every 125 ms
const reply = request.scanner(); // pre-pinned to the right reply types
// feed camera frames: reply.receivePart(text)
const signature = reply.parse(); // typed; request-id echo enforced
// 3. VERIFY (recommended) + broadcast with your own stack.
import { verifyEvmSignature } from '@hwlt/era-connect/verify';
verifyEvmSignature({ signData: rlpBytes, dataType: 1,
signature: signature.signature, address: evm.deriveAddress(0) });Chain support
The ERA device is multichain, and the SDK ships a dedicated module for every chain family the current firmware supports.
Dedicated modules:
| Chain | Sign transaction | Sign message | Subpath |
|---|---|---|---|
| EVM (all chains) | eth-sign-request | personal_sign / EIP-712 | @hwlt/era-connect/evm |
| Bitcoin | crypto-psbt (PSBT v0) | BIP-44/49/84 on firmware 2.1.0+ (Taproot refused); legacy P2PKH on older | @hwlt/era-connect/btc |
| Solana | sol-sign-request | off-chain messages | @hwlt/era-connect/solana |
| Tron | structured envelope (any contract via rawData) | UTF-8 in rawData | @hwlt/era-connect/tron |
| TON | ton-sign-request (BoC root-hash signing) | TON Connect proof | @hwlt/era-connect/ton |
| Cardano | cardano-sign-request (witness-set replies, soft-derived vkey binding) | — | @hwlt/era-connect/cardano |
| Sui | sui-sign-request / hash variant | — | @hwlt/era-connect/sui |
| Cosmos (~35 networks incl. Ethermint) | cosmos-sign-request / evm-sign-request | ADR-036 | @hwlt/era-connect/cosmos |
| XRP | ur:bytes (XRP Toolkit convention) | — | @hwlt/era-connect/xrp |
| Litecoin, Dogecoin, Dash | crypto-psbt-extend (same flow as Bitcoin) | — | @hwlt/era-connect/btc |
| Bitcoin Cash | structured envelope (FORKID signing, CashAddr) | — | @hwlt/era-connect/bch |
Every chain the current device firmware ships now has a dedicated module.
Two things keep future additions workable: linking already surfaces every account
the device exports (unknown chains still carry their path, fingerprint and
public key), and the scanner/fountain layer is chain-agnostic — era.raw plus
era.scanner({ expectedTypes }) speak any UR type the device learns next.
Documentation
The full guide lives in docs/:
Per-chain guides, QR tuning, the WalletConnect forwarding model and a
migration note from @keystonehq/keystone-sdk are under
docs/chains and
docs/advanced.
The wire specification — what to implement if you cannot use the SDK — is
docs/protocol;
it walks through EVM, Bitcoin, Solana and Tron in full, and its reference
tables cover every UR type the device speaks.
