cake_wallet/cw_bitcoin_cash/lib/src/bitcoin_cash_wallet.dart
Rafael 96b9b60f50
Cw 453 (#1306)
* feat: rebase btc-addr-types, migrate to bitcoin_base

* feat: allow scanning elect-rs using get_tweaks

* feat: scanning and adding addresses working with getTweaks, add btc SP address type

* chore: pubspec.lock

* chore: pubspec.lock

* fix: scan when switching, fix multiple unspents in same tx

* fix: initial scan

* fix: initial scan

* fix: scanning issues

* fix: sync, storing silent unspents

* chore: deps

* fix: label issues, clear spent utxo

* chore: deps

* fix: build

* fix: missing types

* feat: new electrs API & changes, fixes for last block scanning

* feat: Scan Silent Payments homepage toggle

* chore: build configure

* feat: generic fixes, testnet UI improvements, useSSL on bitcoin nodes

* fix: invalid Object in sendData

* feat: improve addresses page & address book displays

* feat: silent payments labeled addresses disclaimer

* fix: missing i18n

* chore: print

* feat: single block scan, rescan by date working for btc mainnet

* feat: new cake features page replace market page, move sp scan toggle, auto switch node pop up alert

* feat: delete silent addresses

* fix: red dot in non ssl nodes

* fix: inconsistent connection states, fix tx history

* fix: tx & balance displays, cpfp sending

* feat: new rust lib

* chore: node path

* fix: check node based on network

* fix: missing txcount from addresses

* style: padding in feature page cards

* fix: restore not getting all wallet addresses by type

* fix: auto switch node broken

* fix: silent payment txs not being restored

* feat: change scanning to subscription model, sync improvements

* fix: scan re-subscription

* fix: default nodes

* fix: improve scanning by date, fix single block scan

* refactor: common function for input tx selection

* fix: nodes & build

* fix: send all with multiple outs

* refactor: unchanged file

* Update pr_test_build.yml

* chore: upgrade

* chore: merge changes

* refactor: unchanged files [skip ci]

* fix: scan fixes, add date, allow sending while scanning

* feat: sync fixes, sp settings

* feat: fix resyncing

* fix: date from height logic, status disconnected & chain tip get

* fix: params

* feat: electrum migration if using cake electrum

* fix nodes
update versions

* re-enable tron

* update sp_scanner to work on iOS [skip ci]

* fix: wrong socket for old electrum nodes

* Fix unchecked wallet type call

* fix: double balance

* feat: node domain

* fix: menu name

* fix: update tip on set scanning

* fix: connection switching back and forth

* feat: check if node is electrs, and supports sp

* chore: fix build

* minor enhancements

* fixes and enhancements

* solve conflicts with main

* fix: status toggle

* minor enhancement

* Monero.com fixes

* update sp_scanner to include windows and linux

---------

Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com>
2024-05-29 17:43:48 +03:00

177 lines
6 KiB
Dart

import 'dart:convert';
import 'package:bitbox/bitbox.dart' as bitbox;
import 'package:bitcoin_base/bitcoin_base.dart';
import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin;
import 'package:cw_bitcoin/bitcoin_address_record.dart';
import 'package:cw_bitcoin/bitcoin_transaction_priority.dart';
import 'package:cw_bitcoin/electrum_balance.dart';
import 'package:cw_bitcoin/electrum_wallet.dart';
import 'package:cw_bitcoin/electrum_wallet_snapshot.dart';
import 'package:cw_core/crypto_currency.dart';
import 'package:cw_core/transaction_priority.dart';
import 'package:cw_core/unspent_coins_info.dart';
import 'package:cw_core/wallet_info.dart';
import 'package:flutter/foundation.dart';
import 'package:hive/hive.dart';
import 'package:mobx/mobx.dart';
import 'bitcoin_cash_base.dart';
part 'bitcoin_cash_wallet.g.dart';
class BitcoinCashWallet = BitcoinCashWalletBase with _$BitcoinCashWallet;
abstract class BitcoinCashWalletBase extends ElectrumWallet with Store {
BitcoinCashWalletBase({
required String mnemonic,
required String password,
required WalletInfo walletInfo,
required Box<UnspentCoinsInfo> unspentCoinsInfo,
required Uint8List seedBytes,
BitcoinAddressType? addressPageType,
List<BitcoinAddressRecord>? initialAddresses,
ElectrumBalance? initialBalance,
Map<String, int>? initialRegularAddressIndex,
Map<String, int>? initialChangeAddressIndex,
}) : super(
mnemonic: mnemonic,
password: password,
walletInfo: walletInfo,
unspentCoinsInfo: unspentCoinsInfo,
networkType: bitcoin.bitcoin,
initialAddresses: initialAddresses,
initialBalance: initialBalance,
seedBytes: seedBytes,
currency: CryptoCurrency.bch) {
walletAddresses = BitcoinCashWalletAddresses(
walletInfo,
initialAddresses: initialAddresses,
initialRegularAddressIndex: initialRegularAddressIndex,
initialChangeAddressIndex: initialChangeAddressIndex,
mainHd: hd,
sideHd: accountHD.derive(1),
network: network,
initialAddressPageType: addressPageType,
);
autorun((_) {
this.walletAddresses.isEnabledAutoGenerateSubaddress = this.isEnabledAutoGenerateSubaddress;
});
}
static Future<BitcoinCashWallet> create(
{required String mnemonic,
required String password,
required WalletInfo walletInfo,
required Box<UnspentCoinsInfo> unspentCoinsInfo,
String? addressPageType,
List<BitcoinAddressRecord>? initialAddresses,
ElectrumBalance? initialBalance,
Map<String, int>? initialRegularAddressIndex,
Map<String, int>? initialChangeAddressIndex}) async {
return BitcoinCashWallet(
mnemonic: mnemonic,
password: password,
walletInfo: walletInfo,
unspentCoinsInfo: unspentCoinsInfo,
initialAddresses: initialAddresses,
initialBalance: initialBalance,
seedBytes: await Mnemonic.toSeed(mnemonic),
initialRegularAddressIndex: initialRegularAddressIndex,
initialChangeAddressIndex: initialChangeAddressIndex,
addressPageType: P2pkhAddressType.p2pkh,
);
}
static Future<BitcoinCashWallet> open({
required String name,
required WalletInfo walletInfo,
required Box<UnspentCoinsInfo> unspentCoinsInfo,
required String password,
}) async {
final snp = await ElectrumWalletSnapshot.load(
name, walletInfo.type, password, BitcoinCashNetwork.mainnet);
return BitcoinCashWallet(
mnemonic: snp.mnemonic!,
password: password,
walletInfo: walletInfo,
unspentCoinsInfo: unspentCoinsInfo,
initialAddresses: snp.addresses.map((addr) {
try {
BitcoinCashAddress(addr.address);
return BitcoinAddressRecord(
addr.address,
index: addr.index,
isHidden: addr.isHidden,
type: P2pkhAddressType.p2pkh,
network: BitcoinCashNetwork.mainnet,
);
} catch (_) {
return BitcoinAddressRecord(
AddressUtils.getCashAddrFormat(addr.address),
index: addr.index,
isHidden: addr.isHidden,
type: P2pkhAddressType.p2pkh,
network: BitcoinCashNetwork.mainnet,
);
}
}).toList(),
initialBalance: snp.balance,
seedBytes: await Mnemonic.toSeed(snp.mnemonic!),
initialRegularAddressIndex: snp.regularAddressIndex,
initialChangeAddressIndex: snp.changeAddressIndex,
addressPageType: P2pkhAddressType.p2pkh,
);
}
bitbox.ECPair generateKeyPair({required bitcoin.HDWallet hd, required int index}) =>
bitbox.ECPair.fromWIF(hd.derive(index).wif!);
int calculateEstimatedFeeWithFeeRate(int feeRate, int? amount, {int? outputsCount, int? size}) {
int inputsCount = 0;
int totalValue = 0;
for (final input in unspentCoins) {
if (input.isSending) {
inputsCount++;
totalValue += input.value;
}
if (amount != null && totalValue >= amount) {
break;
}
}
if (amount != null && totalValue < amount) return 0;
final _outputsCount = outputsCount ?? (amount != null ? 2 : 1);
return feeAmountWithFeeRate(feeRate, inputsCount, _outputsCount);
}
@override
int feeRate(TransactionPriority priority) {
if (priority is BitcoinCashTransactionPriority) {
switch (priority) {
case BitcoinCashTransactionPriority.slow:
return 1;
case BitcoinCashTransactionPriority.medium:
return 5;
case BitcoinCashTransactionPriority.fast:
return 10;
}
}
return 0;
}
@override
Future<String> signMessage(String message, {String? address = null}) async {
final index = address != null
? walletAddresses.allAddresses
.firstWhere((element) => element.address == AddressUtils.toLegacyAddress(address))
.index
: null;
final HD = index == null ? hd : hd.derive(index);
return base64Encode(HD.signMessage(message));
}
}