Files
hash-wallet/cw_bitcoin/lib/hardware/bitbox_service.dart
T
Konstantin UllrichandGitHub 8b76391a2e LCW-1128-Add-support-for-Trezor (#2565)
* feat: add support for Trezor hardware wallet integration Restore Wallets

* feat: add Trezor hardware wallet support, implement RLP decoding utility, and update transaction signing logic

* feat: add bitcoin trezor signing

* chore: update Trezor Connect dependency to specific commit hash in pubspec_base.yaml

* feat: add trezor support for litecoin

* feat: add colorFilter for device manufacturer icons to align with theme styles

* refactor: optimize Trezor callback handling

* feat: add BitBox and Trezor support for Bitcoin and Litecoin, update transaction signing logic, and improve modularity in hardware wallet services

* refactor: simplify constructor assertions in `BaseBottomSheetWidget` and cleanup Trezor chainId handling logic

* feat: conditionally render device manufacturers based on platform and hide SeedSigner for now
2025-10-11 02:12:27 +03:00

125 lines
4.9 KiB
Dart

import 'dart:async';
import 'dart:convert';
import 'dart:developer';
import 'dart:typed_data';
import 'package:bitbox_flutter/bitbox_manager.dart';
import 'package:bitcoin_base/bitcoin_base.dart';
import 'package:blockchain_utils/blockchain_utils.dart';
import 'package:cw_bitcoin/electrum_wallet.dart';
import 'package:cw_bitcoin/hardware/bitcoin_hardware_wallet_service.dart';
import 'package:cw_bitcoin/psbt/transaction_builder.dart';
import 'package:cw_bitcoin/psbt/v0_deserialize.dart';
import 'package:cw_bitcoin/psbt/v0_finalizer.dart';
import 'package:cw_bitcoin/utils.dart';
import 'package:cw_core/hardware/hardware_account_data.dart';
import 'package:cw_core/hardware/hardware_wallet_service.dart';
import 'package:ledger_bitcoin/psbt.dart';
class BitcoinBitboxService extends HardwareWalletService with BitcoinHardwareWalletService {
BitcoinBitboxService(this.manager);
// https://github.com/BitBoxSwiss/bitbox02-api-go/blob/ae070b1d41bf1cf00588fa6f498b4734b5ecd6fc/api/firmware/messages/btc.pb.go#L40
static const int bitboxCoinType = 0;
final BitboxManager manager;
@override
Future<List<HardwareAccountData>> getAvailableAccounts({int index = 0, int limit = 5}) async {
final accounts = <HardwareAccountData>[];
final indexRange = List.generate(limit, (i) => i + index);
// https://github.com/BitBoxSwiss/bitbox02-api-go/blob/ae070b1d41bf1cf00588fa6f498b4734b5ecd6fc/api/firmware/messages/btc.pb.go#L252
final keyType = 1; // xPUB
for (final i in indexRange) {
final derivationPath = "m/84'/0'/$i'";
final xPub = await manager.getBTCXPub(bitboxCoinType, derivationPath, keyType);
final hd = Bip32Slip10Secp256k1.fromExtendedKey(xPub).childKey(Bip32KeyIndex(0));
final address = generateP2WPKHAddress(hd: hd, index: 0, network: BitcoinNetwork.mainnet);
accounts.add(HardwareAccountData(
address: address,
accountIndex: i,
derivationPath: derivationPath,
xpub: xPub,
));
}
return accounts;
}
@override
Future<Uint8List> signTransaction({required String transaction}) async {
final psbt = PsbtV2()..deserialize(base64Decode(transaction));
log(base64Encode(psbt.asPsbtV0()), name: 'PSBT');
final signedPsbt = await manager.signBTCPsbt(bitboxCoinType, base64Encode(psbt.asPsbtV0()));
log(signedPsbt, name: 'signed PSBT');
final transactionRes = PsbtV2()
..deserializeV0(base64Decode(signedPsbt))
..finalizeV0();
return transactionRes.extract();
}
@override
Future<Uint8List> signMessage({required Uint8List message, String? derivationPath}) =>
manager.signBTCMessage(bitboxCoinType, derivationPath ?? "m/84'/0'/0'/0/0", message);
@override
Future<Uint8List> getMasterFingerprint() => manager.getMasterFingerprint();
}
class LitecoinBitboxService extends HardwareWalletService with BitcoinHardwareWalletService, LitecoinHardwareWalletService {
LitecoinBitboxService(this.manager);
final BitboxManager manager;
// https://github.com/BitBoxSwiss/bitbox02-api-go/blob/ae070b1d41bf1cf00588fa6f498b4734b5ecd6fc/api/firmware/messages/btc.pb.go#L40
static const int bitboxCoinType = 2;
@override
Future<List<HardwareAccountData>> getAvailableAccounts({int index = 0, int limit = 5}) async {
final accounts = <HardwareAccountData>[];
final indexRange = List.generate(limit, (i) => i + index);
// https://github.com/BitBoxSwiss/bitbox02-api-go/blob/ae070b1d41bf1cf00588fa6f498b4734b5ecd6fc/api/firmware/messages/btc.pb.go#L252
final keyType = 1; // xPUB
for (final i in indexRange) {
final derivationPath = "m/84'/2'/$i'";
final xPub = await manager.getBTCXPub(bitboxCoinType, derivationPath, keyType);
final hd = Bip32Slip10Secp256k1.fromExtendedKey(xPub).childKey(Bip32KeyIndex(0));
final address = generateP2WPKHAddress(hd: hd, index: 0, network: LitecoinNetwork.mainnet);
accounts.add(HardwareAccountData(
address: address,
accountIndex: i,
derivationPath: derivationPath,
xpub: xPub,
));
}
return accounts;
}
@override
Future<Uint8List> signMessage({required Uint8List message, String? derivationPath}) =>
manager.signBTCMessage(bitboxCoinType, derivationPath ?? "m/84'/0'/0'/0/0", message);
@override
Future<String> signLitecoinTransaction({
required List<BitcoinBaseOutput> outputs,
required List<PSBTReadyUtxoWithAddress> inputs,
required Map<String, PublicKeyWithDerivationPath> publicKeys,
}) async {
final psbt = PSBTTransactionBuild(inputs: inputs, outputs: outputs, cwOutputs: []).psbt;
final signedPsbt = await manager.signBTCPsbt(bitboxCoinType, base64Encode(psbt.asPsbtV0()));
final transactionRes = PsbtV2()
..deserializeV0(base64Decode(signedPsbt))
..finalizeV0();
return hex.encode(transactionRes.extract());
}
@override
Future<Uint8List> getMasterFingerprint() => manager.getMasterFingerprint();
}