mirror of
https://github.com/Such-Software/hash-wallet.git
synced 2026-10-19 05:58:50 +02:00
Cw 604 integrate bitcoin ledger (#1407)
* CW-503 Prepare Ledger integration * CW-503 Revert Tor ignore * CW-503 Add Connect Device Page * CW-503 Add createWalletFromDevice for monero * CW-503 Add Connect Device Page * CW-503 Add Connect Device Page * CW-503 Add Debug Options * CW-503 Add proper hardware wallet selection screen * CW-503 Minor design changes on connect_device_page * CW-503 Add Create Wallet from Ledger * CW-503 Spent Ledger ETH Funds * CW-503 Minor fixes * CW-503 Fix Merge conflicts * CW-503 Fix Merge conflicts * CW-503 Fix Merge conflicts * CW-503 Fix minor conflicts * CW-503 Improve Ledger BLE Communication * CW-503 Improve Ledger BLE Communication * CW-503 Rollback Monero Test code * CW-503 Fix Execution failed for task :app:checkReleaseDuplicateClasses * CW-503 Better Error-Exceptions * CW-503 Add SetPinScreen before restore from hardware-wallet * CW-503 override web3dart to use cake's git hosted version * CW-503 Implement ledger sign messages * CW-503 Implement ledger sign messages and send erc20 tokens * CW-503 Fix merge conflicts * CW-503 Fix merge conflicts * CW-503 Use dep override for ledger_flutter * CW-503 Ledger ERC20 finalisation * CW-503 More graceful error handling * CW-503 Even more graceful error handling & remove debug code * CW-503 Minor Changes for Vik * CW-503 Fix USB connection * CW-503 Maybe this overrides web3dart finally * Indicate Loading on the first 5 Wallet Accounts * Prepare Ledger Bitcoin * Fix conflicts with main * Add Bluetooth permission to iOS [skip ci] * add privacyinfo for ios [skip ci] * update PrivacyInfo.xcprivacy [scip ci] * ios shit [skip ci] * Improve bitcoin xpub * Resolve open Todos regarding iOS Support * Minor debug * Remove erc20 dependency to have more granular control over the tx UX * Create Bitcoin Wallets using xpub * Create Bitcoin Wallets using xpub * Better error handling * Improve Ledger account handling * Add Bitcoin Support for Ledger * Add Bitcoin Support for Ledger * Add Bitcoin Support for Ledger * Implement requested Changes * Implement requested Changes * Minor fix * Implement requested Changes * Implement requested Changes * Add comment to remind me * Enable RBF for Bitcoin Ledger * Fix merge conflicts * Update wallet_info.dart * Cw 503 ledger support (#1310) * CW-503 Prepare Ledger integration * CW-503 Revert Tor ignore * CW-503 Add Connect Device Page * CW-503 Add createWalletFromDevice for monero * CW-503 Add Connect Device Page * CW-503 Add Connect Device Page * CW-503 Add Debug Options * CW-503 Add proper hardware wallet selection screen * CW-503 Minor design changes on connect_device_page * CW-503 Add Create Wallet from Ledger * CW-503 Spent Ledger ETH Funds * CW-503 Minor fixes * CW-503 Fix Merge conflicts * CW-503 Fix Merge conflicts * CW-503 Fix Merge conflicts * CW-503 Fix minor conflicts * CW-503 Improve Ledger BLE Communication * CW-503 Improve Ledger BLE Communication * CW-503 Rollback Monero Test code * CW-503 Fix Execution failed for task :app:checkReleaseDuplicateClasses * CW-503 Better Error-Exceptions * CW-503 Add SetPinScreen before restore from hardware-wallet * CW-503 override web3dart to use cake's git hosted version * CW-503 Implement ledger sign messages * CW-503 Implement ledger sign messages and send erc20 tokens * CW-503 Fix merge conflicts * CW-503 Fix merge conflicts * CW-503 Use dep override for ledger_flutter * CW-503 Ledger ERC20 finalisation * CW-503 More graceful error handling * CW-503 Even more graceful error handling & remove debug code * CW-503 Minor Changes for Vik * CW-503 Fix USB connection * CW-503 Maybe this overrides web3dart finally * Indicate Loading on the first 5 Wallet Accounts * Fix conflicts with main * Add Bluetooth permission to iOS [skip ci] * add privacyinfo for ios [skip ci] * update PrivacyInfo.xcprivacy [scip ci] * ios shit [skip ci] * Resolve open Todos regarding iOS Support * Remove erc20 dependency to have more granular control over the tx UX * Better error handling * Improve Ledger account handling * Implement requested Changes * Implement requested Changes * Implement requested Changes * Implement requested Changes * Fix merge conflicts * Update wallet_info.dart --------- Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com> * Fix merge conflicts * Fix merge conflicts * Minor Fix to derivations * Update cw_bitcoin/lib/bitcoin_wallet.dart [skip ci] * Update cw_bitcoin/lib/bitcoin_wallet.dart [skip ci] * Update cw_bitcoin/lib/electrum_wallet.dart [skip ci] * Fix backward compatibility issues * Merge Tron * Fix Tron with HW changes * fix ble on iOS fix tron address validation --------- Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com>
This commit is contained in:
co-authored by
Omar Hatem
parent
043d7d7c8b
commit
5eabdcdca1
@@ -0,0 +1,43 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:bitcoin_base/bitcoin_base.dart';
|
||||
import 'package:bitcoin_flutter/bitcoin_flutter.dart';
|
||||
import 'package:cw_bitcoin/utils.dart';
|
||||
import 'package:cw_core/hardware/hardware_account_data.dart';
|
||||
import 'package:ledger_bitcoin/ledger_bitcoin.dart';
|
||||
import 'package:ledger_flutter/ledger_flutter.dart';
|
||||
|
||||
class BitcoinHardwareWalletService {
|
||||
BitcoinHardwareWalletService(this.ledger, this.device);
|
||||
|
||||
final Ledger ledger;
|
||||
final LedgerDevice device;
|
||||
|
||||
Future<List<HardwareAccountData>> getAvailableAccounts({int index = 0, int limit = 5}) async {
|
||||
final bitcoinLedgerApp = BitcoinLedgerApp(ledger);
|
||||
|
||||
final masterFp = await bitcoinLedgerApp.getMasterFingerprint(device);
|
||||
print(masterFp);
|
||||
|
||||
final accounts = <HardwareAccountData>[];
|
||||
final indexRange = List.generate(limit, (i) => i + index);
|
||||
|
||||
for (final i in indexRange) {
|
||||
final derivationPath = "m/84'/0'/$i'";
|
||||
final xpub = await bitcoinLedgerApp.getXPubKey(device, derivationPath: derivationPath);
|
||||
HDWallet hd = HDWallet.fromBase58(xpub).derive(0);
|
||||
|
||||
final address = generateP2WPKHAddress(hd: hd, index: 0, network: BitcoinNetwork.mainnet);
|
||||
|
||||
accounts.add(HardwareAccountData(
|
||||
address: address,
|
||||
accountIndex: i,
|
||||
derivationPath: derivationPath,
|
||||
masterFingerprint: masterFp,
|
||||
xpub: xpub,
|
||||
));
|
||||
}
|
||||
|
||||
return accounts;
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,13 @@
|
||||
import 'package:bitcoin_base/bitcoin_base.dart';
|
||||
import 'package:convert/convert.dart';
|
||||
|
||||
import 'package:cw_bitcoin/bitcoin_mnemonic.dart';
|
||||
import 'package:cw_bitcoin/psbt_transaction_builder.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cw_core/unspent_coins_info.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:ledger_bitcoin/ledger_bitcoin.dart';
|
||||
import 'package:ledger_flutter/ledger_flutter.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin;
|
||||
@@ -20,11 +25,12 @@ class BitcoinWallet = BitcoinWalletBase with _$BitcoinWallet;
|
||||
|
||||
abstract class BitcoinWalletBase extends ElectrumWallet with Store {
|
||||
BitcoinWalletBase({
|
||||
required String mnemonic,
|
||||
required String password,
|
||||
required WalletInfo walletInfo,
|
||||
required Box<UnspentCoinsInfo> unspentCoinsInfo,
|
||||
required Uint8List seedBytes,
|
||||
Uint8List? seedBytes,
|
||||
String? mnemonic,
|
||||
String? xpub,
|
||||
String? addressPageType,
|
||||
BasedUtxoNetwork? networkParam,
|
||||
List<BitcoinAddressRecord>? initialAddresses,
|
||||
@@ -33,24 +39,26 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
|
||||
Map<String, int>? initialChangeAddressIndex,
|
||||
String? passphrase,
|
||||
}) : super(
|
||||
mnemonic: mnemonic,
|
||||
passphrase: passphrase,
|
||||
password: password,
|
||||
walletInfo: walletInfo,
|
||||
unspentCoinsInfo: unspentCoinsInfo,
|
||||
networkType: networkParam == null
|
||||
? bitcoin.bitcoin
|
||||
: networkParam == BitcoinNetwork.mainnet
|
||||
? bitcoin.bitcoin
|
||||
: bitcoin.testnet,
|
||||
initialAddresses: initialAddresses,
|
||||
initialBalance: initialBalance,
|
||||
seedBytes: seedBytes,
|
||||
currency: CryptoCurrency.btc) {
|
||||
mnemonic: mnemonic,
|
||||
passphrase: passphrase,
|
||||
xpub: xpub,
|
||||
password: password,
|
||||
walletInfo: walletInfo,
|
||||
unspentCoinsInfo: unspentCoinsInfo,
|
||||
networkType: networkParam == null
|
||||
? bitcoin.bitcoin
|
||||
: networkParam == BitcoinNetwork.mainnet
|
||||
? bitcoin.bitcoin
|
||||
: bitcoin.testnet,
|
||||
initialAddresses: initialAddresses,
|
||||
initialBalance: initialBalance,
|
||||
seedBytes: seedBytes,
|
||||
currency: CryptoCurrency.btc) {
|
||||
// in a standard BIP44 wallet, mainHd derivation path = m/84'/0'/0'/0 (account 0, index unspecified here)
|
||||
// the sideHd derivation path = m/84'/0'/0'/1 (account 1, index unspecified here)
|
||||
String derivationPath = walletInfo.derivationInfo!.derivationPath!;
|
||||
String sideDerivationPath = derivationPath.substring(0, derivationPath.length - 1) + "1";
|
||||
// String derivationPath = walletInfo.derivationInfo!.derivationPath!;
|
||||
// String sideDerivationPath = derivationPath.substring(0, derivationPath.length - 1) + "1";
|
||||
// final hd = bitcoin.HDWallet.fromSeed(seedBytes, network: networkType);
|
||||
walletAddresses = BitcoinWalletAddresses(
|
||||
walletInfo,
|
||||
electrumClient: electrumClient,
|
||||
@@ -58,7 +66,7 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
|
||||
initialRegularAddressIndex: initialRegularAddressIndex,
|
||||
initialChangeAddressIndex: initialChangeAddressIndex,
|
||||
mainHd: hd,
|
||||
sideHd: bitcoin.HDWallet.fromSeed(seedBytes, network: networkType).derivePath(sideDerivationPath),
|
||||
sideHd: accountHD.derive(1),
|
||||
network: networkParam ?? network,
|
||||
);
|
||||
autorun((_) {
|
||||
@@ -129,23 +137,26 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
|
||||
walletInfo.derivationInfo!.derivationPath = snp.derivationPath ?? "m/0'/0";
|
||||
walletInfo.derivationInfo!.derivationType = snp.derivationType ?? DerivationType.electrum;
|
||||
|
||||
late Uint8List seedBytes;
|
||||
Uint8List? seedBytes = null;
|
||||
|
||||
switch (walletInfo.derivationInfo!.derivationType) {
|
||||
case DerivationType.electrum:
|
||||
seedBytes = await mnemonicToSeedBytes(snp.mnemonic);
|
||||
break;
|
||||
case DerivationType.bip39:
|
||||
default:
|
||||
seedBytes = await bip39.mnemonicToSeed(
|
||||
snp.mnemonic,
|
||||
passphrase: snp.passphrase ?? '',
|
||||
);
|
||||
break;
|
||||
if (snp.mnemonic != null) {
|
||||
switch (walletInfo.derivationInfo!.derivationType) {
|
||||
case DerivationType.electrum:
|
||||
seedBytes = await mnemonicToSeedBytes(snp.mnemonic!);
|
||||
break;
|
||||
case DerivationType.bip39:
|
||||
default:
|
||||
seedBytes = await bip39.mnemonicToSeed(
|
||||
snp.mnemonic!,
|
||||
passphrase: snp.passphrase ?? '',
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return BitcoinWallet(
|
||||
mnemonic: snp.mnemonic,
|
||||
xpub: snp.xpub,
|
||||
password: password,
|
||||
passphrase: snp.passphrase,
|
||||
walletInfo: walletInfo,
|
||||
@@ -159,4 +170,49 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
|
||||
networkParam: network,
|
||||
);
|
||||
}
|
||||
|
||||
Ledger? _ledger;
|
||||
LedgerDevice? _ledgerDevice;
|
||||
BitcoinLedgerApp? _bitcoinLedgerApp;
|
||||
|
||||
void setLedger(Ledger setLedger, LedgerDevice setLedgerDevice) {
|
||||
_ledger = setLedger;
|
||||
_ledgerDevice = setLedgerDevice;
|
||||
_bitcoinLedgerApp = BitcoinLedgerApp(_ledger!, derivationPath: walletInfo.derivationInfo!.derivationPath!);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<BtcTransaction> buildHardwareWalletTransaction({
|
||||
required List<BitcoinBaseOutput> outputs,
|
||||
required BigInt fee,
|
||||
required BasedUtxoNetwork network,
|
||||
required List<UtxoWithAddress> utxos,
|
||||
required Map<String, PublicKeyWithDerivationPath> publicKeys,
|
||||
String? memo,
|
||||
bool enableRBF = false,
|
||||
BitcoinOrdering inputOrdering = BitcoinOrdering.bip69,
|
||||
BitcoinOrdering outputOrdering = BitcoinOrdering.bip69,
|
||||
}) async {
|
||||
final masterFingerprint = await _bitcoinLedgerApp!.getMasterFingerprint(_ledgerDevice!);
|
||||
|
||||
final psbtReadyInputs = <PSBTReadyUtxoWithAddress>[];
|
||||
for (final utxo in utxos) {
|
||||
final rawTx = await electrumClient.getTransactionHex(hash: utxo.utxo.txHash);
|
||||
final publicKeyAndDerivationPath = publicKeys[utxo.ownerDetails.address.pubKeyHash()]!;
|
||||
|
||||
psbtReadyInputs.add(PSBTReadyUtxoWithAddress(
|
||||
utxo: utxo.utxo,
|
||||
rawTx: rawTx,
|
||||
ownerDetails: utxo.ownerDetails,
|
||||
ownerDerivationPath: publicKeyAndDerivationPath.derivationPath,
|
||||
ownerMasterFingerprint: masterFingerprint,
|
||||
ownerPublicKey: publicKeyAndDerivationPath.publicKey,
|
||||
));
|
||||
}
|
||||
|
||||
final psbt = PSBTTransactionBuild(inputs: psbtReadyInputs, outputs: outputs, enableRBF: enableRBF);
|
||||
|
||||
final rawHex = await _bitcoinLedgerApp!.signPsbt(_ledgerDevice!, psbt: psbt.psbt);
|
||||
return BtcTransaction.fromRaw(hex.encode(rawHex));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import 'package:cw_core/hardware/hardware_account_data.dart';
|
||||
import 'package:cw_core/wallet_credentials.dart';
|
||||
import 'package:cw_core/wallet_info.dart';
|
||||
|
||||
@@ -36,9 +37,22 @@ class BitcoinRestoreWalletFromSeedCredentials extends WalletCredentials {
|
||||
}
|
||||
|
||||
class BitcoinRestoreWalletFromWIFCredentials extends WalletCredentials {
|
||||
BitcoinRestoreWalletFromWIFCredentials(
|
||||
{required String name, required String password, required this.wif, WalletInfo? walletInfo})
|
||||
: super(name: name, password: password, walletInfo: walletInfo);
|
||||
BitcoinRestoreWalletFromWIFCredentials({
|
||||
required String name,
|
||||
required String password,
|
||||
required this.wif,
|
||||
WalletInfo? walletInfo,
|
||||
}) : super(name: name, password: password, walletInfo: walletInfo);
|
||||
|
||||
final String wif;
|
||||
}
|
||||
|
||||
class BitcoinRestoreWalletFromHardware extends WalletCredentials {
|
||||
BitcoinRestoreWalletFromHardware({
|
||||
required String name,
|
||||
required this.hwAccountData,
|
||||
WalletInfo? walletInfo,
|
||||
}) : super(name: name, walletInfo: walletInfo);
|
||||
|
||||
final HardwareAccountData hwAccountData;
|
||||
}
|
||||
|
||||
@@ -14,8 +14,11 @@ import 'package:hive/hive.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:bip39/bip39.dart' as bip39;
|
||||
|
||||
class BitcoinWalletService extends WalletService<BitcoinNewWalletCredentials,
|
||||
BitcoinRestoreWalletFromSeedCredentials, BitcoinRestoreWalletFromWIFCredentials> {
|
||||
class BitcoinWalletService extends WalletService<
|
||||
BitcoinNewWalletCredentials,
|
||||
BitcoinRestoreWalletFromSeedCredentials,
|
||||
BitcoinRestoreWalletFromWIFCredentials,
|
||||
BitcoinRestoreWalletFromHardware> {
|
||||
BitcoinWalletService(this.walletInfoSource, this.unspentCoinsInfoSource);
|
||||
|
||||
final Box<WalletInfo> walletInfoSource;
|
||||
@@ -99,9 +102,28 @@ class BitcoinWalletService extends WalletService<BitcoinNewWalletCredentials,
|
||||
await walletInfoSource.put(currentWalletInfo.key, newWalletInfo);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<BitcoinWallet> restoreFromHardwareWallet(BitcoinRestoreWalletFromHardware credentials,
|
||||
{bool? isTestnet}) async {
|
||||
|
||||
final network = isTestnet == true ? BitcoinNetwork.testnet : BitcoinNetwork.mainnet;
|
||||
credentials.walletInfo?.network = network.value;
|
||||
credentials.walletInfo?.derivationInfo?.derivationPath = credentials.hwAccountData.derivationPath;
|
||||
|
||||
final wallet = await BitcoinWallet(password: credentials.password!,
|
||||
xpub: credentials.hwAccountData.xpub,
|
||||
walletInfo: credentials.walletInfo!,
|
||||
unspentCoinsInfo: unspentCoinsInfoSource,
|
||||
networkParam: network,
|
||||
);
|
||||
await wallet.save();
|
||||
await wallet.init();
|
||||
return wallet;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<BitcoinWallet> restoreFromKeys(BitcoinRestoreWalletFromWIFCredentials credentials,
|
||||
{bool? isTestnet}) async =>
|
||||
{bool? isTestnet}) async =>
|
||||
throw UnimplementedError();
|
||||
|
||||
@override
|
||||
|
||||
@@ -4,7 +4,7 @@ Map<DerivationType, List<DerivationInfo>> electrum_derivations = {
|
||||
DerivationType.electrum: [
|
||||
DerivationInfo(
|
||||
derivationType: DerivationType.electrum,
|
||||
derivationPath: "m/0'/0",
|
||||
derivationPath: "m/0'",
|
||||
description: "Electrum",
|
||||
scriptType: "p2wpkh",
|
||||
),
|
||||
|
||||
@@ -4,8 +4,8 @@ import 'dart:io';
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:bitcoin_base/bitcoin_base.dart';
|
||||
import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin;
|
||||
import 'package:bitcoin_base/bitcoin_base.dart' as bitcoin_base;
|
||||
import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin;
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:cw_bitcoin/address_from_output.dart';
|
||||
import 'package:cw_bitcoin/bitcoin_address_record.dart';
|
||||
@@ -37,9 +37,9 @@ import 'package:cw_core/wallet_base.dart';
|
||||
import 'package:cw_core/wallet_info.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:mobx/mobx.dart';
|
||||
import 'package:rxdart/subjects.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
|
||||
part 'electrum_wallet.g.dart';
|
||||
|
||||
@@ -53,17 +53,16 @@ abstract class ElectrumWalletBase
|
||||
required WalletInfo walletInfo,
|
||||
required Box<UnspentCoinsInfo> unspentCoinsInfo,
|
||||
required this.networkType,
|
||||
required this.mnemonic,
|
||||
required Uint8List seedBytes,
|
||||
String? xpub,
|
||||
String? mnemonic,
|
||||
Uint8List? seedBytes,
|
||||
this.passphrase,
|
||||
List<BitcoinAddressRecord>? initialAddresses,
|
||||
ElectrumClient? electrumClient,
|
||||
ElectrumBalance? initialBalance,
|
||||
CryptoCurrency? currency})
|
||||
: hd = currency == CryptoCurrency.bch
|
||||
? bitcoinCashHDWallet(seedBytes)
|
||||
: bitcoin.HDWallet.fromSeed(seedBytes, network: networkType)
|
||||
.derivePath(walletInfo.derivationInfo?.derivationPath ?? "m/0'/0"),
|
||||
: accountHD =
|
||||
getAccountHDWallet(currency, networkType, seedBytes, xpub, walletInfo.derivationInfo),
|
||||
syncStatus = NotConnectedSyncStatus(),
|
||||
_password = password,
|
||||
_feeRates = <int>[],
|
||||
@@ -80,20 +79,44 @@ abstract class ElectrumWalletBase
|
||||
this.unspentCoinsInfo = unspentCoinsInfo,
|
||||
this.network = _getNetwork(networkType, currency),
|
||||
this.isTestnet = networkType == bitcoin.testnet,
|
||||
this._mnemonic = mnemonic,
|
||||
super(walletInfo) {
|
||||
this.electrumClient = electrumClient ?? ElectrumClient();
|
||||
this.walletInfo = walletInfo;
|
||||
transactionHistory = ElectrumTransactionHistory(walletInfo: walletInfo, password: password);
|
||||
}
|
||||
|
||||
static bitcoin.HDWallet getAccountHDWallet(
|
||||
CryptoCurrency? currency,
|
||||
bitcoin.NetworkType networkType,
|
||||
Uint8List? seedBytes,
|
||||
String? xpub,
|
||||
DerivationInfo? derivationInfo) {
|
||||
if (seedBytes == null && xpub == null) {
|
||||
throw Exception(
|
||||
"To create a Wallet you need either a seed or an xpub. This should not happen");
|
||||
}
|
||||
|
||||
if (seedBytes != null) {
|
||||
return currency == CryptoCurrency.bch
|
||||
? bitcoinCashHDWallet(seedBytes)
|
||||
: bitcoin.HDWallet.fromSeed(seedBytes, network: networkType)
|
||||
.derivePath(_hardenedDerivationPath(derivationInfo?.derivationPath ?? "m/0'"));
|
||||
}
|
||||
|
||||
return bitcoin.HDWallet.fromBase58(xpub!);
|
||||
}
|
||||
|
||||
static bitcoin.HDWallet bitcoinCashHDWallet(Uint8List seedBytes) =>
|
||||
bitcoin.HDWallet.fromSeed(seedBytes).derivePath("m/44'/145'/0'/0");
|
||||
bitcoin.HDWallet.fromSeed(seedBytes).derivePath("m/44'/145'/0'");
|
||||
|
||||
static int estimatedTransactionSize(int inputsCount, int outputsCounts) =>
|
||||
inputsCount * 68 + outputsCounts * 34 + 10;
|
||||
|
||||
final bitcoin.HDWallet hd;
|
||||
final String mnemonic;
|
||||
final bitcoin.HDWallet accountHD;
|
||||
final String? _mnemonic;
|
||||
|
||||
bitcoin.HDWallet get hd => accountHD.derive(0);
|
||||
final String? passphrase;
|
||||
|
||||
@override
|
||||
@@ -123,10 +146,10 @@ abstract class ElectrumWalletBase
|
||||
.map((addr) => scriptHash(addr.address, network: network))
|
||||
.toList();
|
||||
|
||||
String get xpub => hd.base58!;
|
||||
String get xpub => accountHD.base58!;
|
||||
|
||||
@override
|
||||
String get seed => mnemonic;
|
||||
String? get seed => _mnemonic;
|
||||
|
||||
bitcoin.NetworkType networkType;
|
||||
BasedUtxoNetwork network;
|
||||
@@ -203,7 +226,9 @@ abstract class ElectrumWalletBase
|
||||
int credentialsAmount = 0,
|
||||
}) async {
|
||||
final utxos = <UtxoWithAddress>[];
|
||||
List<ECPrivate> privateKeys = [];
|
||||
final privateKeys = <ECPrivate>[];
|
||||
final publicKeys = <String, PublicKeyWithDerivationPath>{};
|
||||
|
||||
int allInputsAmount = 0;
|
||||
|
||||
bool spendsUnconfirmedTX = false;
|
||||
@@ -217,12 +242,22 @@ abstract class ElectrumWalletBase
|
||||
allInputsAmount += utx.value;
|
||||
|
||||
final address = addressTypeFromStr(utx.address, network);
|
||||
final privkey = generateECPrivate(
|
||||
hd: utx.bitcoinAddressRecord.isHidden ? walletAddresses.sideHd : walletAddresses.mainHd,
|
||||
index: utx.bitcoinAddressRecord.index,
|
||||
network: network);
|
||||
final hd =
|
||||
utx.bitcoinAddressRecord.isHidden ? walletAddresses.sideHd : walletAddresses.mainHd;
|
||||
final derivationPath =
|
||||
"${_hardenedDerivationPath(walletInfo.derivationInfo?.derivationPath ?? "m/0'")}"
|
||||
"/${utx.bitcoinAddressRecord.isHidden ? "1" : "0"}"
|
||||
"/${utx.bitcoinAddressRecord.index}";
|
||||
final pubKeyHex = hd.derive(utx.bitcoinAddressRecord.index).pubKey!;
|
||||
|
||||
privateKeys.add(privkey);
|
||||
publicKeys[address.pubKeyHash()] = PublicKeyWithDerivationPath(pubKeyHex, derivationPath);
|
||||
|
||||
if (!walletInfo.isHardwareWallet) {
|
||||
final privkey =
|
||||
generateECPrivate(hd: hd, index: utx.bitcoinAddressRecord.index, network: network);
|
||||
|
||||
privateKeys.add(privkey);
|
||||
}
|
||||
|
||||
utxos.add(
|
||||
UtxoWithAddress(
|
||||
@@ -233,7 +268,7 @@ abstract class ElectrumWalletBase
|
||||
scriptType: _getScriptType(address),
|
||||
),
|
||||
ownerDetails: UtxoAddressDetails(
|
||||
publicKey: privkey.getPublic().toHex(),
|
||||
publicKey: pubKeyHex,
|
||||
address: address,
|
||||
),
|
||||
),
|
||||
@@ -294,6 +329,7 @@ abstract class ElectrumWalletBase
|
||||
return EstimatedTxResult(
|
||||
utxos: utxos,
|
||||
privateKeys: privateKeys,
|
||||
publicKeys: publicKeys,
|
||||
fee: fee,
|
||||
amount: amount,
|
||||
isSendAll: true,
|
||||
@@ -312,7 +348,9 @@ abstract class ElectrumWalletBase
|
||||
bool? useUnconfirmed,
|
||||
}) async {
|
||||
final utxos = <UtxoWithAddress>[];
|
||||
List<ECPrivate> privateKeys = [];
|
||||
final privateKeys = <ECPrivate>[];
|
||||
final publicKeys = <String, PublicKeyWithDerivationPath>{};
|
||||
|
||||
int allInputsAmount = 0;
|
||||
bool spendsUnconfirmedTX = false;
|
||||
|
||||
@@ -332,12 +370,23 @@ abstract class ElectrumWalletBase
|
||||
leftAmount = leftAmount - utx.value;
|
||||
|
||||
final address = addressTypeFromStr(utx.address, network);
|
||||
final privkey = generateECPrivate(
|
||||
hd: utx.bitcoinAddressRecord.isHidden ? walletAddresses.sideHd : walletAddresses.mainHd,
|
||||
index: utx.bitcoinAddressRecord.index,
|
||||
network: network);
|
||||
|
||||
privateKeys.add(privkey);
|
||||
final hd =
|
||||
utx.bitcoinAddressRecord.isHidden ? walletAddresses.sideHd : walletAddresses.mainHd;
|
||||
final derivationPath =
|
||||
"${_hardenedDerivationPath(walletInfo.derivationInfo?.derivationPath ?? "m/0'")}"
|
||||
"/${utx.bitcoinAddressRecord.isHidden ? "1" : "0"}"
|
||||
"/${utx.bitcoinAddressRecord.index}";
|
||||
final pubKeyHex = hd.derive(utx.bitcoinAddressRecord.index).pubKey!;
|
||||
|
||||
publicKeys[address.pubKeyHash()] = PublicKeyWithDerivationPath(pubKeyHex, derivationPath);
|
||||
|
||||
if (!walletInfo.isHardwareWallet) {
|
||||
final privkey =
|
||||
generateECPrivate(hd: hd, index: utx.bitcoinAddressRecord.index, network: network);
|
||||
|
||||
privateKeys.add(privkey);
|
||||
}
|
||||
|
||||
utxos.add(
|
||||
UtxoWithAddress(
|
||||
@@ -348,7 +397,7 @@ abstract class ElectrumWalletBase
|
||||
scriptType: _getScriptType(address),
|
||||
),
|
||||
ownerDetails: UtxoAddressDetails(
|
||||
publicKey: privkey.getPublic().toHex(),
|
||||
publicKey: pubKeyHex,
|
||||
address: address,
|
||||
),
|
||||
),
|
||||
@@ -490,6 +539,7 @@ abstract class ElectrumWalletBase
|
||||
return EstimatedTxResult(
|
||||
utxos: utxos,
|
||||
privateKeys: privateKeys,
|
||||
publicKeys: publicKeys,
|
||||
fee: fee,
|
||||
amount: amount,
|
||||
hasChange: true,
|
||||
@@ -557,6 +607,35 @@ abstract class ElectrumWalletBase
|
||||
);
|
||||
}
|
||||
|
||||
if (walletInfo.isHardwareWallet) {
|
||||
final transaction = await buildHardwareWalletTransaction(
|
||||
utxos: estimatedTx.utxos,
|
||||
outputs: outputs,
|
||||
publicKeys: estimatedTx.publicKeys,
|
||||
fee: BigInt.from(estimatedTx.fee),
|
||||
network: network,
|
||||
memo: estimatedTx.memo,
|
||||
outputOrdering: BitcoinOrdering.none,
|
||||
enableRBF: true,
|
||||
);
|
||||
|
||||
return PendingBitcoinTransaction(
|
||||
transaction,
|
||||
type,
|
||||
electrumClient: electrumClient,
|
||||
amount: estimatedTx.amount,
|
||||
fee: estimatedTx.fee,
|
||||
feeRate: feeRateInt.toString(),
|
||||
network: network,
|
||||
hasChange: estimatedTx.hasChange,
|
||||
isSendAll: estimatedTx.isSendAll,
|
||||
hasTaprootInputs: false, // ToDo: (Konsti) Support Taproot
|
||||
)..addListener((transaction) async {
|
||||
transactionHistory.addOne(transaction);
|
||||
await updateBalance();
|
||||
});
|
||||
}
|
||||
|
||||
BasedBitcoinTransacationBuilder txb;
|
||||
if (network is BitcoinCashNetwork) {
|
||||
txb = ForkedTransactionBuilder(
|
||||
@@ -618,8 +697,22 @@ abstract class ElectrumWalletBase
|
||||
}
|
||||
}
|
||||
|
||||
Future<BtcTransaction> buildHardwareWalletTransaction({
|
||||
required List<BitcoinBaseOutput> outputs,
|
||||
required BigInt fee,
|
||||
required BasedUtxoNetwork network,
|
||||
required List<UtxoWithAddress> utxos,
|
||||
required Map<String, PublicKeyWithDerivationPath> publicKeys,
|
||||
String? memo,
|
||||
bool enableRBF = false,
|
||||
BitcoinOrdering inputOrdering = BitcoinOrdering.bip69,
|
||||
BitcoinOrdering outputOrdering = BitcoinOrdering.bip69,
|
||||
}) async =>
|
||||
throw UnimplementedError();
|
||||
|
||||
String toJSON() => json.encode({
|
||||
'mnemonic': mnemonic,
|
||||
'mnemonic': _mnemonic,
|
||||
'xpub': xpub,
|
||||
'passphrase': passphrase ?? '',
|
||||
'account_index': walletAddresses.currentReceiveAddressIndexByType,
|
||||
'change_address_index': walletAddresses.currentChangeAddressIndexByType,
|
||||
@@ -1263,7 +1356,7 @@ abstract class ElectrumWalletBase
|
||||
void setExceptionHandler(void Function(FlutterErrorDetails) onError) => _onError = onError;
|
||||
|
||||
@override
|
||||
String signMessage(String message, {String? address = null}) {
|
||||
Future<String> signMessage(String message, {String? address = null}) async {
|
||||
final index = address != null
|
||||
? walletAddresses.allAddresses.firstWhere((element) => element.address == address).index
|
||||
: null;
|
||||
@@ -1286,6 +1379,9 @@ abstract class ElectrumWalletBase
|
||||
|
||||
return BitcoinNetwork.mainnet;
|
||||
}
|
||||
|
||||
static String _hardenedDerivationPath(String derivationPath) =>
|
||||
derivationPath.substring(0, derivationPath.lastIndexOf("'") + 1);
|
||||
}
|
||||
|
||||
class EstimateTxParams {
|
||||
@@ -1307,6 +1403,7 @@ class EstimatedTxResult {
|
||||
EstimatedTxResult({
|
||||
required this.utxos,
|
||||
required this.privateKeys,
|
||||
required this.publicKeys,
|
||||
required this.fee,
|
||||
required this.amount,
|
||||
required this.hasChange,
|
||||
@@ -1317,6 +1414,7 @@ class EstimatedTxResult {
|
||||
|
||||
final List<UtxoWithAddress> utxos;
|
||||
final List<ECPrivate> privateKeys;
|
||||
final Map<String, PublicKeyWithDerivationPath> publicKeys; // PubKey to derivationPath
|
||||
final int fee;
|
||||
final int amount;
|
||||
final bool hasChange;
|
||||
@@ -1325,6 +1423,13 @@ class EstimatedTxResult {
|
||||
final bool spendsUnconfirmedTX;
|
||||
}
|
||||
|
||||
class PublicKeyWithDerivationPath {
|
||||
const PublicKeyWithDerivationPath(this.publicKey, this.derivationPath);
|
||||
|
||||
final String derivationPath;
|
||||
final String publicKey;
|
||||
}
|
||||
|
||||
BitcoinBaseAddress addressTypeFromStr(String address, BasedUtxoNetwork network) {
|
||||
if (network is BitcoinCashNetwork) {
|
||||
if (!address.startsWith("bitcoincash:") &&
|
||||
|
||||
@@ -13,6 +13,7 @@ class ElectrumWalletSnapshot {
|
||||
required this.type,
|
||||
required this.password,
|
||||
required this.mnemonic,
|
||||
required this.xpub,
|
||||
required this.addresses,
|
||||
required this.balance,
|
||||
required this.regularAddressIndex,
|
||||
@@ -28,7 +29,8 @@ class ElectrumWalletSnapshot {
|
||||
final WalletType type;
|
||||
final String? addressPageType;
|
||||
|
||||
String mnemonic;
|
||||
String? mnemonic;
|
||||
String? xpub;
|
||||
List<BitcoinAddressRecord> addresses;
|
||||
ElectrumBalance balance;
|
||||
Map<String, int> regularAddressIndex;
|
||||
@@ -43,7 +45,8 @@ class ElectrumWalletSnapshot {
|
||||
final jsonSource = await read(path: path, password: password);
|
||||
final data = json.decode(jsonSource) as Map;
|
||||
final addressesTmp = data['addresses'] as List? ?? <Object>[];
|
||||
final mnemonic = data['mnemonic'] as String;
|
||||
final mnemonic = data['mnemonic'] as String?;
|
||||
final xpub = data['xpub'] as String?;
|
||||
final passphrase = data['passphrase'] as String? ?? '';
|
||||
final addresses = addressesTmp
|
||||
.whereType<String>()
|
||||
@@ -79,6 +82,7 @@ class ElectrumWalletSnapshot {
|
||||
password: password,
|
||||
passphrase: passphrase,
|
||||
mnemonic: mnemonic,
|
||||
xpub: xpub,
|
||||
addresses: addresses,
|
||||
balance: balance,
|
||||
regularAddressIndex: regularAddressIndexByType,
|
||||
|
||||
@@ -50,7 +50,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
|
||||
initialRegularAddressIndex: initialRegularAddressIndex,
|
||||
initialChangeAddressIndex: initialChangeAddressIndex,
|
||||
mainHd: hd,
|
||||
sideHd: bitcoin.HDWallet.fromSeed(seedBytes, network: networkType).derivePath("m/0'/1"),
|
||||
sideHd: accountHD.derive(1),
|
||||
network: network,
|
||||
);
|
||||
autorun((_) {
|
||||
@@ -106,13 +106,13 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
|
||||
final snp =
|
||||
await ElectrumWalletSnapshot.load(name, walletInfo.type, password, LitecoinNetwork.mainnet);
|
||||
return LitecoinWallet(
|
||||
mnemonic: snp.mnemonic,
|
||||
mnemonic: snp.mnemonic!,
|
||||
password: password,
|
||||
walletInfo: walletInfo,
|
||||
unspentCoinsInfo: unspentCoinsInfo,
|
||||
initialAddresses: snp.addresses,
|
||||
initialBalance: snp.balance,
|
||||
seedBytes: await mnemonicToSeedBytes(snp.mnemonic),
|
||||
seedBytes: await mnemonicToSeedBytes(snp.mnemonic!),
|
||||
initialRegularAddressIndex: snp.regularAddressIndex,
|
||||
initialChangeAddressIndex: snp.changeAddressIndex,
|
||||
addressPageType: snp.addressPageType,
|
||||
|
||||
@@ -16,7 +16,7 @@ import 'package:bip39/bip39.dart' as bip39;
|
||||
class LitecoinWalletService extends WalletService<
|
||||
BitcoinNewWalletCredentials,
|
||||
BitcoinRestoreWalletFromSeedCredentials,
|
||||
BitcoinRestoreWalletFromWIFCredentials> {
|
||||
BitcoinRestoreWalletFromWIFCredentials,BitcoinNewWalletCredentials> {
|
||||
LitecoinWalletService(this.walletInfoSource, this.unspentCoinsInfoSource);
|
||||
|
||||
final Box<WalletInfo> walletInfoSource;
|
||||
@@ -94,6 +94,11 @@ class LitecoinWalletService extends WalletService<
|
||||
await walletInfoSource.put(currentWalletInfo.key, newWalletInfo);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<LitecoinWallet> restoreFromHardwareWallet(BitcoinNewWalletCredentials credentials) {
|
||||
throw UnimplementedError("Restoring a Litecoin wallet from a hardware wallet is not yet supported!");
|
||||
}
|
||||
|
||||
@override
|
||||
Future<LitecoinWallet> restoreFromKeys(
|
||||
BitcoinRestoreWalletFromWIFCredentials credentials, {bool? isTestnet}) async =>
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:bitcoin_base/bitcoin_base.dart';
|
||||
import 'package:convert/convert.dart';
|
||||
import 'package:ledger_bitcoin/psbt.dart';
|
||||
|
||||
class PSBTTransactionBuild {
|
||||
final PsbtV2 psbt = PsbtV2();
|
||||
|
||||
PSBTTransactionBuild(
|
||||
{required List<PSBTReadyUtxoWithAddress> inputs, required List<BitcoinBaseOutput> outputs, bool enableRBF = true}) {
|
||||
psbt.setGlobalTxVersion(2);
|
||||
psbt.setGlobalInputCount(inputs.length);
|
||||
psbt.setGlobalOutputCount(outputs.length);
|
||||
|
||||
for (var i = 0; i < inputs.length; i++) {
|
||||
final input = inputs[i];
|
||||
|
||||
print(input.utxo.isP2tr());
|
||||
print(input.utxo.isSegwit());
|
||||
print(input.utxo.isP2shSegwit());
|
||||
|
||||
psbt.setInputPreviousTxId(i, Uint8List.fromList(hex.decode(input.utxo.txHash).reversed.toList()));
|
||||
psbt.setInputOutputIndex(i, input.utxo.vout);
|
||||
psbt.setInputSequence(i, enableRBF ? 0x1 : 0xffffffff);
|
||||
|
||||
|
||||
if (input.utxo.isSegwit()) {
|
||||
setInputSegwit(i, input);
|
||||
} else if (input.utxo.isP2shSegwit()) {
|
||||
setInputP2shSegwit(i, input);
|
||||
} else if (input.utxo.isP2tr()) {
|
||||
// ToDo: (Konsti) Handle Taproot Inputs
|
||||
} else {
|
||||
setInputP2pkh(i, input);
|
||||
}
|
||||
}
|
||||
|
||||
for (var i = 0; i < outputs.length; i++) {
|
||||
final output = outputs[i];
|
||||
|
||||
if (output is BitcoinOutput) {
|
||||
psbt.setOutputScript(i, Uint8List.fromList(output.address.toScriptPubKey().toBytes()));
|
||||
psbt.setOutputAmount(i, output.value.toInt());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void setInputP2pkh(int i, PSBTReadyUtxoWithAddress input) {
|
||||
psbt.setInputNonWitnessUtxo(i, Uint8List.fromList(hex.decode(input.rawTx)));
|
||||
psbt.setInputBip32Derivation(
|
||||
i,
|
||||
Uint8List.fromList(hex.decode(input.ownerPublicKey)),
|
||||
input.ownerMasterFingerprint,
|
||||
BIPPath.fromString(input.ownerDerivationPath).toPathArray());
|
||||
}
|
||||
|
||||
void setInputSegwit(int i, PSBTReadyUtxoWithAddress input) {
|
||||
psbt.setInputNonWitnessUtxo(i, Uint8List.fromList(hex.decode(input.rawTx)));
|
||||
psbt.setInputBip32Derivation(
|
||||
i,
|
||||
Uint8List.fromList(hex.decode(input.ownerPublicKey)),
|
||||
input.ownerMasterFingerprint,
|
||||
BIPPath.fromString(input.ownerDerivationPath).toPathArray());
|
||||
|
||||
psbt.setInputWitnessUtxo(i, Uint8List.fromList(bigIntToUint64LE(input.utxo.value)),
|
||||
Uint8List.fromList(input.ownerDetails.address.toScriptPubKey().toBytes()));
|
||||
}
|
||||
|
||||
void setInputP2shSegwit(int i, PSBTReadyUtxoWithAddress input) {
|
||||
psbt.setInputNonWitnessUtxo(i, Uint8List.fromList(hex.decode(input.rawTx)));
|
||||
psbt.setInputBip32Derivation(i, Uint8List.fromList(hex.decode(input.ownerPublicKey)),
|
||||
input.ownerMasterFingerprint, BIPPath.fromString(input.ownerDerivationPath).toPathArray());
|
||||
|
||||
psbt.setInputRedeemScript(
|
||||
i, Uint8List.fromList(input.ownerDetails.address.toScriptPubKey().toBytes()));
|
||||
psbt.setInputWitnessUtxo(i, Uint8List.fromList(bigIntToUint64LE(input.utxo.value)),
|
||||
Uint8List.fromList(input.ownerDetails.address.toScriptPubKey().toBytes()));
|
||||
}
|
||||
}
|
||||
|
||||
class PSBTReadyUtxoWithAddress extends UtxoWithAddress {
|
||||
final String rawTx;
|
||||
final String ownerDerivationPath;
|
||||
final Uint8List ownerMasterFingerprint;
|
||||
final String ownerPublicKey;
|
||||
|
||||
PSBTReadyUtxoWithAddress({
|
||||
required super.utxo,
|
||||
required this.rawTx,
|
||||
required super.ownerDetails,
|
||||
required this.ownerDerivationPath,
|
||||
required this.ownerMasterFingerprint,
|
||||
required this.ownerPublicKey,
|
||||
});
|
||||
}
|
||||
+74
-1
@@ -260,6 +260,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.4"
|
||||
dart_varuint_bitcoin:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: dart_varuint_bitcoin
|
||||
sha256: "4f0ccc9733fb54148b9d3688eea822b7aaabf5cc00025998f8c09a1d45b31b4b"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.3"
|
||||
encrypt:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -313,6 +321,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.1+1"
|
||||
flutter_reactive_ble:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: flutter_reactive_ble
|
||||
sha256: "247e2efa76de203d1ba11335c13754b5b9d0504b5423e5b0c93a600f016b24e0"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.3.1"
|
||||
flutter_test:
|
||||
dependency: "direct dev"
|
||||
description: flutter
|
||||
@@ -326,6 +342,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.0.0"
|
||||
functional_data:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: functional_data
|
||||
sha256: aefdec4365452283b2a7cf420a3169654d51d3e9553069a22d76680d7a9d7c3d
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.1"
|
||||
glob:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -422,6 +446,31 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.8.1"
|
||||
ledger_bitcoin:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
path: "."
|
||||
ref: HEAD
|
||||
resolved-ref: b6ed573cbeb57d5f0d39dfe4254bf9d15b620ab6
|
||||
url: "https://github.com/cake-tech/ledger-bitcoin.git"
|
||||
source: git
|
||||
version: "0.0.1"
|
||||
ledger_flutter:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: ledger_flutter
|
||||
sha256: f1680060ed6ff78f275837e0024ccaf667715a59ba7aa29fa7354bc7752e71c8
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.1"
|
||||
ledger_usb:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: ledger_usb
|
||||
sha256: "52c92d03a4cffe06c82921c8e2f79f3cdad6e1cf78e1e9ca35444196ff8f14c2"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.0"
|
||||
logging:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -582,6 +631,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.5.1"
|
||||
protobuf:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: protobuf
|
||||
sha256: "01dd9bd0fa02548bf2ceee13545d4a0ec6046459d847b6b061d8a27237108a08"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.0"
|
||||
provider:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -606,6 +663,22 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.2.3"
|
||||
reactive_ble_mobile:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: reactive_ble_mobile
|
||||
sha256: "9ec2b4c9c725e439950838d551579750060258fbccd5536d0543b4d07d225798"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.3.1"
|
||||
reactive_ble_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: reactive_ble_platform_interface
|
||||
sha256: "632c92401a2d69c9b94bd48f8fd47488a7013f3d1f9b291884350291a4a81813"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.3.1"
|
||||
rxdart:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -788,5 +861,5 @@ packages:
|
||||
source: hosted
|
||||
version: "3.1.2"
|
||||
sdks:
|
||||
dart: ">=3.0.0 <4.0.0"
|
||||
dart: ">=3.0.6 <4.0.0"
|
||||
flutter: ">=3.10.0"
|
||||
|
||||
@@ -35,6 +35,10 @@ dependencies:
|
||||
url: https://github.com/cake-tech/bitcoin_base.git
|
||||
ref: cake-update-v2
|
||||
blockchain_utils: ^2.1.1
|
||||
ledger_flutter: ^1.0.1
|
||||
ledger_bitcoin:
|
||||
git:
|
||||
url: https://github.com/cake-tech/ledger-bitcoin.git
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
|
||||
Reference in New Issue
Block a user