dev
dart 125 lines 4.87 KB
Raw
1 import 'dart:async';
2 import 'dart:convert';
3 import 'dart:developer';
4 import 'dart:typed_data';
5
6 import 'package:bitbox_flutter/bitbox_manager.dart';
7 import 'package:bitcoin_base/bitcoin_base.dart';
8 import 'package:blockchain_utils/blockchain_utils.dart';
9 import 'package:cw_bitcoin/electrum_wallet.dart';
10 import 'package:cw_bitcoin/hardware/bitcoin_hardware_wallet_service.dart';
11 import 'package:cw_bitcoin/psbt/transaction_builder.dart';
12 import 'package:cw_bitcoin/psbt/v0_deserialize.dart';
13 import 'package:cw_bitcoin/psbt/v0_finalizer.dart';
14 import 'package:cw_bitcoin/utils.dart';
15 import 'package:cw_core/hardware/hardware_account_data.dart';
16 import 'package:cw_core/hardware/hardware_wallet_service.dart';
17 import 'package:ledger_bitcoin/psbt.dart';
18
19 class BitcoinBitboxService extends HardwareWalletService with BitcoinHardwareWalletService {
20 BitcoinBitboxService(this.manager);
21
22 // https://github.com/BitBoxSwiss/bitbox02-api-go/blob/ae070b1d41bf1cf00588fa6f498b4734b5ecd6fc/api/firmware/messages/btc.pb.go#L40
23 static const int bitboxCoinType = 0;
24 final BitboxManager manager;
25
26 @override
27 Future<List<HardwareAccountData>> getAvailableAccounts({int index = 0, int limit = 5}) async {
28 final accounts = <HardwareAccountData>[];
29 final indexRange = List.generate(limit, (i) => i + index);
30
31 // https://github.com/BitBoxSwiss/bitbox02-api-go/blob/ae070b1d41bf1cf00588fa6f498b4734b5ecd6fc/api/firmware/messages/btc.pb.go#L252
32 final keyType = 1; // xPUB
33 for (final i in indexRange) {
34 final derivationPath = "m/84'/0'/$i'";
35 final xPub = await manager.getBTCXPub(bitboxCoinType, derivationPath, keyType);
36 final hd = Bip32Slip10Secp256k1.fromExtendedKey(xPub).childKey(Bip32KeyIndex(0));
37 final address = generateP2WPKHAddress(hd: hd, index: 0, network: BitcoinNetwork.mainnet);
38
39 accounts.add(HardwareAccountData(
40 address: address,
41 accountIndex: i,
42 derivationPath: derivationPath,
43 xpub: xPub,
44 ));
45 }
46
47 return accounts;
48 }
49
50 @override
51 Future<Uint8List> signTransaction({required String transaction}) async {
52 final psbt = PsbtV2()..deserialize(base64Decode(transaction));
53 log(base64Encode(psbt.asPsbtV0()), name: 'PSBT');
54 final signedPsbt = await manager.signBTCPsbt(bitboxCoinType, base64Encode(psbt.asPsbtV0()));
55 log(signedPsbt, name: 'signed PSBT');
56 final transactionRes = PsbtV2()
57 ..deserializeV0(base64Decode(signedPsbt))
58 ..finalizeV0();
59 return transactionRes.extract();
60 }
61
62 @override
63 Future<Uint8List> signMessage({required Uint8List message, String? derivationPath}) =>
64 manager.signBTCMessage(bitboxCoinType, derivationPath ?? "m/84'/0'/0'/0/0", message);
65
66 @override
67 Future<Uint8List> getMasterFingerprint() => manager.getMasterFingerprint();
68 }
69
70 class LitecoinBitboxService extends HardwareWalletService
71 with BitcoinHardwareWalletService, LitecoinHardwareWalletService {
72 LitecoinBitboxService(this.manager);
73
74 final BitboxManager manager;
75
76 // https://github.com/BitBoxSwiss/bitbox02-api-go/blob/ae070b1d41bf1cf00588fa6f498b4734b5ecd6fc/api/firmware/messages/btc.pb.go#L40
77 static const int bitboxCoinType = 2;
78
79 @override
80 Future<List<HardwareAccountData>> getAvailableAccounts({int index = 0, int limit = 5}) async {
81 final accounts = <HardwareAccountData>[];
82 final indexRange = List.generate(limit, (i) => i + index);
83
84 // https://github.com/BitBoxSwiss/bitbox02-api-go/blob/ae070b1d41bf1cf00588fa6f498b4734b5ecd6fc/api/firmware/messages/btc.pb.go#L252
85 final keyType = 1; // xPUB
86 for (final i in indexRange) {
87 final derivationPath = "m/84'/2'/$i'";
88 final xPub = await manager.getBTCXPub(bitboxCoinType, derivationPath, keyType);
89 final hd = Bip32Slip10Secp256k1.fromExtendedKey(xPub).childKey(Bip32KeyIndex(0));
90 final address = generateP2WPKHAddress(hd: hd, index: 0, network: LitecoinNetwork.mainnet);
91
92 accounts.add(HardwareAccountData(
93 address: address,
94 accountIndex: i,
95 derivationPath: derivationPath,
96 xpub: xPub,
97 ));
98 }
99
100 return accounts;
101 }
102
103 @override
104 Future<Uint8List> signMessage({required Uint8List message, String? derivationPath}) =>
105 manager.signBTCMessage(bitboxCoinType, derivationPath ?? "m/84'/0'/0'/0/0", message);
106
107 @override
108 Future<String> signLitecoinTransaction({
109 required List<BitcoinBaseOutput> outputs,
110 required List<PSBTReadyUtxoWithAddress> inputs,
111 required Map<String, PublicKeyWithDerivationPath> publicKeys,
112 }) async {
113 final psbt = PSBTTransactionBuild(inputs: inputs, outputs: outputs, cwOutputs: []).psbt;
114
115 final signedPsbt = await manager.signBTCPsbt(bitboxCoinType, base64Encode(psbt.asPsbtV0()));
116 final transactionRes = PsbtV2()
117 ..deserializeV0(base64Decode(signedPsbt))
118 ..finalizeV0();
119
120 return hex.encode(transactionRes.extract());
121 }
122
123 @override
124 Future<Uint8List> getMasterFingerprint() => manager.getMasterFingerprint();
125 }