era_connect
Put the ERA air-gapped hardware wallet behind a Dart or Flutter wallet. The device never touches a cable or a radio: sign requests leave your app as animated QR frames, signatures come back the same way.
Account linking and transaction signing for eleven chain families — EVM (all networks), Bitcoin, Litecoin, Dogecoin, Dash, Bitcoin Cash, Solana, Tron, TON, Cardano, Sui, Cosmos and XRP.
- Headless. You render the QR and own the camera; this package owns every byte of the protocol — BC-UR fountain encoding, the Keystone-compatible registry, the CBOR layouts, the reply checks.
- Pure Dart. No plugins, no
dart:ioin the library. Flutter on every target, plus CLI and web. - Zero I/O. No network calls, ever. Nothing leaves the process.
- No keys, ever. The device signs; this package builds requests, reads replies and derives public addresses. There is no signer here to misuse.
- Hardened where it matters. A scanned QR is attacker-controlled input, so it is treated that way: bounds precede allocations, the scanner refuses hostile frames, every reply must echo its request id, and compressed replies inflate under a hard ceiling.
Install
dart pub add era_connect
Sixty seconds to a signature
import 'package:era_connect/era_connect.dart';
final era = EraConnect(EraConnectConfig(origin: 'MyWallet')); // shown on device
// 1. LINK — scan the device's "connect" QR once.
// walletUrTypes pins every shape a wallet export can arrive in.
final scanner = era.scanner(
UrScannerOptions(expectedTypes: walletUrTypes.toList()));
// feed camera frames: scanner.receivePart(text) until scanner.isComplete
final accounts = era.parseAccounts(scanner.result());
final evm = accounts.evm()!;
evm.deriveAddress(0); // 0x… — derived locally, no device round-trip
// 2. SIGN — build the transaction with your chain tooling, hand over the bytes.
final request = era.evm.generateSignRequest(EvmSignRequestProps(
signData: rawRlpBytes,
dataType: EvmDataType.transaction,
path: evm.pathFor(0),
xfp: evm.xfp,
chainId: 1,
));
// 3. DISPLAY — animate the request as QR frames.
final animated = request.toAnimated();
// render animated.nextFrame() on a timer (~125 ms per frame)
// 4. SCAN the reply. The request-id echo is enforced for you.
final replyScanner = request.scanner();
// feed camera frames …
final signature = replyScanner.parse();
// 5. VERIFY before broadcasting — see package:era_connect/verify.dart
Full walkthrough: doc/getting-started.
Chain support
Ten modules cover eleven families — Litecoin, Dogecoin and Dash ride the Bitcoin module, since the device signs them through the same PSBT flow.
| Chain | Signing | Library |
|---|---|---|
| EVM (all networks) | transactions, personal_sign, EIP-712 |
package:era_connect/evm.dart |
| Bitcoin | PSBT v0; messages (script types depend on firmware) | package:era_connect/btc.dart |
| Litecoin, Dogecoin, Dash | the Bitcoin PSBT flow with a coin id | package:era_connect/btc.dart |
| Bitcoin Cash | structured envelope, FORKID sighash, CashAddr | package:era_connect/bch.dart |
| Solana | transactions (incl. versioned), off-chain messages | package:era_connect/solana.dart |
| Tron | any contract — the raw raw_data is what gets signed |
package:era_connect/tron.dart |
| TON | BoC root-hash signing, TON Connect proofs | package:era_connect/ton.dart |
| Cardano | witness sets, soft-derived vkey binding | package:era_connect/cardano.dart |
| Sui | intent-message signing, local address derivation | package:era_connect/sui.dart |
| Cosmos (~35 zones incl. Ethermint) | Amino, Direct, ADR-036 | package:era_connect/cosmos.dart |
| XRP | the XRP Toolkit ur:bytes convention |
package:era_connect/xrp.dart |
package:era_connect/era_connect.dart re-exports all of it behind the
EraConnect facade. The per-chain libraries exist so an app that signs one
chain does not import ten.
Verify before you broadcast
The reply's request-id echo proves which request was answered.
package:era_connect/verify.dart proves what was signed — it recomputes the
digest, checks the signature against the key the request named, and compares
the returned transaction with the one you sent.
import 'package:era_connect/verify.dart';
final check = verifyEvmSignature(/* … */);
if (!check.ok) throw StateError(check.reason!);
On two paths there is no request id at all — Bitcoin PSBT and XRP — so the content check is the only binding you have. Verification is mandatory there, not advisory. Details in doc/advanced/verification.md.
Documentation
- Getting started — five pages, install to verified signature
- Chain guides — one per family: props, replies, verification, broadcasting
- Verification · QR tuning · Flutter integration · Key-derivation calls · WalletConnect
- API reference: the dartdoc on pub.dev
Security
Found a flaw? Please report it privately — see SECURITY.md.
License
Libraries
- bch
- Subpath library:
package:era_connect/bch.dart— the bch chain module plus the shared request/scan plumbing, without the rest of the SDK. - btc
- Subpath library:
package:era_connect/btc.dart— the btc chain module plus the shared request/scan plumbing, without the rest of the SDK. - cardano
- Subpath library:
package:era_connect/cardano.dart— the cardano chain module plus the shared request/scan plumbing, without the rest of the SDK. - cosmos
- Subpath library:
package:era_connect/cosmos.dart— the cosmos chain module plus the shared request/scan plumbing, without the rest of the SDK. - era_connect
- Air-gapped ERA hardware wallet SDK: account linking and transaction signing over animated QR codes (BC-UR / Keystone-compatible registry).
- evm
- Subpath library:
package:era_connect/evm.dart— the evm chain module plus the shared request/scan plumbing, without the rest of the SDK. - solana
- Subpath library:
package:era_connect/solana.dart— the solana chain module plus the shared request/scan plumbing, without the rest of the SDK. - sui
- Subpath library:
package:era_connect/sui.dart— the sui chain module plus the shared request/scan plumbing, without the rest of the SDK. - ton
- Subpath library:
package:era_connect/ton.dart— the ton chain module plus the shared request/scan plumbing, without the rest of the SDK. - tron
- Subpath library:
package:era_connect/tron.dart— the tron chain module plus the shared request/scan plumbing, without the rest of the SDK. - verify
- Subpath library:
package:era_connect/verify.dart. - xrp
- Subpath library:
package:era_connect/xrp.dart— the xrp chain module plus the shared request/scan plumbing, without the rest of the SDK.