mirror of
https://github.com/cake-tech/cake_wallet.git
synced 2025-01-08 20:09:24 +00:00
add transaction creation
This commit is contained in:
parent
c563e300f8
commit
b4316581c8
13 changed files with 425 additions and 129 deletions
|
@ -19,7 +19,7 @@ class BitcoinAddressRecord {
|
|||
bool operator ==(Object o) =>
|
||||
o is BitcoinAddressRecord && address == o.address;
|
||||
|
||||
final String address;
|
||||
String address;
|
||||
final bool isHidden;
|
||||
final int index;
|
||||
bool get isUsed => _isUsed;
|
||||
|
|
|
@ -21,4 +21,8 @@ class BitcoinUnspent {
|
|||
bool isSending;
|
||||
bool isFrozen;
|
||||
String note;
|
||||
|
||||
void updateAddress(String newAddress) {
|
||||
address.address = newAddress;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ import 'dart:convert';
|
|||
import 'dart:io';
|
||||
import 'dart:math';
|
||||
import 'package:bitcoin_flutter/bitcoin_flutter.dart';
|
||||
import 'package:cw_core/pending_transaction.dart';
|
||||
import 'package:cw_core/unspent_coins_info.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
|
@ -11,6 +12,7 @@ import 'package:mobx/mobx.dart';
|
|||
import 'package:rxdart/subjects.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin;
|
||||
import 'package:bitbox/bitbox.dart';
|
||||
import 'package:cw_bitcoin/electrum_transaction_info.dart';
|
||||
import 'package:cw_core/pathForWallet.dart';
|
||||
import 'package:cw_bitcoin/address_to_output_script.dart';
|
||||
|
@ -191,7 +193,7 @@ abstract class ElectrumWalletBase
|
|||
}
|
||||
|
||||
@override
|
||||
Future<PendingBitcoinTransaction> createTransaction(Object credentials) async {
|
||||
Future<PendingTransaction> createTransaction(Object credentials) async {
|
||||
const minAmount = 546;
|
||||
final transactionCredentials = credentials as BitcoinTransactionCredentials;
|
||||
final inputs = <BitcoinUnspent>[];
|
||||
|
@ -668,7 +670,8 @@ abstract class ElectrumWalletBase
|
|||
final addresses = walletAddresses.addresses.toList();
|
||||
final balanceFutures = <Future<Map<String, dynamic>>>[];
|
||||
for (var i = 0; i < addresses.length; i++) {
|
||||
final addressRecord = addresses[i];
|
||||
// walletInfo.type != WalletType.bitcoinCash ? Address :
|
||||
final addressRecord = addresses[i] ;
|
||||
final sh = scriptHash(addressRecord.address, networkType: networkType);
|
||||
final balanceFuture = electrumClient.getBalance(sh);
|
||||
balanceFutures.add(balanceFuture);
|
||||
|
|
|
@ -66,6 +66,15 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.6"
|
||||
bitbox:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
path: "."
|
||||
ref: main
|
||||
resolved-ref: ea65073efbaf395a5557e8cd7bd72f195cd7eb11
|
||||
url: "https://github.com/Serhii-Borodenko/cw_bitbox.git"
|
||||
source: git
|
||||
version: "1.0.1"
|
||||
bitcoin_flutter:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
|
|
@ -23,6 +23,10 @@ dependencies:
|
|||
git:
|
||||
url: https://github.com/cake-tech/bitcoin_flutter.git
|
||||
ref: cake-update-v2
|
||||
bitbox:
|
||||
git:
|
||||
url: https://github.com/Serhii-Borodenko/cw_bitbox.git
|
||||
ref: main
|
||||
rxdart: ^0.27.5
|
||||
unorm_dart: ^0.2.0
|
||||
cryptography: ^2.0.5
|
||||
|
|
|
@ -2,7 +2,6 @@ export 'bitcoin_cash_balance.dart';
|
|||
export 'bitcoin_cash_client.dart';
|
||||
export 'bitcoin_cash_transaction_history.dart';
|
||||
export 'bitcoin_cash_transaction_info.dart';
|
||||
export 'bitcoin_cash_transaction_priority.dart';
|
||||
export 'bitcoin_cash_wallet.dart';
|
||||
export 'bitcoin_cash_wallet_addresses.dart';
|
||||
export 'bitcoin_cash_wallet_creation_credentials.dart';
|
||||
|
|
|
@ -1,52 +0,0 @@
|
|||
import 'package:cw_bitcoin/bitcoin_transaction_priority.dart';
|
||||
|
||||
class BitcoinCashTransactionPriority extends BitcoinTransactionPriority {
|
||||
const BitcoinCashTransactionPriority({required String title, required int raw})
|
||||
: super(title: title, raw: raw);
|
||||
|
||||
static const List<BitcoinCashTransactionPriority> all = [fast, medium, slow];
|
||||
static const BitcoinCashTransactionPriority slow =
|
||||
BitcoinCashTransactionPriority(title: 'Slow', raw: 0);
|
||||
static const BitcoinCashTransactionPriority medium =
|
||||
BitcoinCashTransactionPriority(title: 'Medium', raw: 1);
|
||||
static const BitcoinCashTransactionPriority fast =
|
||||
BitcoinCashTransactionPriority(title: 'Fast', raw: 2);
|
||||
|
||||
static BitcoinCashTransactionPriority deserialize({required int raw}) {
|
||||
switch (raw) {
|
||||
case 0:
|
||||
return slow;
|
||||
case 1:
|
||||
return medium;
|
||||
case 2:
|
||||
return fast;
|
||||
default:
|
||||
throw Exception('Unexpected token: $raw for BitcoinTransactionPriority deserialize');
|
||||
}
|
||||
}
|
||||
|
||||
String get units => 'sat';
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
var label = '';
|
||||
|
||||
switch (this) {
|
||||
case BitcoinCashTransactionPriority.slow:
|
||||
label = 'Slow ~24hrs'; // '${S.current.transaction_priority_slow} ~24hrs';
|
||||
break;
|
||||
case BitcoinCashTransactionPriority.medium:
|
||||
label = 'Medium'; // S.current.transaction_priority_medium;
|
||||
break;
|
||||
case BitcoinCashTransactionPriority.fast:
|
||||
label = 'Fast'; // S.current.transaction_priority_fast;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return label;
|
||||
}
|
||||
|
||||
String labelWithRate(int rate) => '${toString()} ($rate ${units}/byte)';
|
||||
}
|
|
@ -1,14 +1,22 @@
|
|||
import 'package:bitbox/bitbox.dart' as bitbox;
|
||||
import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin;
|
||||
import 'package:bitcoin_flutter/bitcoin_flutter.dart';
|
||||
import 'package:cw_bitcoin/bitcoin_address_record.dart';
|
||||
import 'package:cw_bitcoin/bitcoin_transaction_credentials.dart';
|
||||
import 'package:cw_bitcoin/bitcoin_transaction_no_inputs_exception.dart';
|
||||
import 'package:cw_bitcoin/bitcoin_transaction_priority.dart';
|
||||
import 'package:cw_bitcoin/bitcoin_transaction_wrong_balance_exception.dart';
|
||||
import 'package:cw_bitcoin/bitcoin_unspent.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_bitcoin/pending_bitcoin_transaction.dart';
|
||||
import 'package:cw_bitcoin_cash/src/pending_bitcoin_cash_transaction.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cw_core/pending_transaction.dart';
|
||||
import 'package:cw_core/unspent_coins_info.dart';
|
||||
import 'package:cw_core/wallet_info.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:hex/hex.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
|
||||
|
@ -91,69 +99,325 @@ abstract class BitcoinCashWalletBase extends ElectrumWallet with Store {
|
|||
}
|
||||
|
||||
@override
|
||||
Future<PendingBitcoinTransaction> createTransaction(Object credentials,
|
||||
[List<Object>? unspents, Object? wallet]) async {
|
||||
Future<PendingTransaction> createTransaction(Object credentials) async {
|
||||
final transactionCredentials = credentials as BitcoinTransactionCredentials;
|
||||
|
||||
// final utxoSigningData = await fetchBuildTxData(unspents as List<BitcoinUnspent>, wallet as BitcoinCashWalletBase);
|
||||
// final builder = bitbox.Bitbox.transactionBuilder(testnet: false);
|
||||
// final utxosToUse = unspents as List<UnspentCash>;
|
||||
// final _wallet = wallet as BitcoinCashWallet;
|
||||
// print('unspents: ${unspents.first.address}');
|
||||
//
|
||||
// List<bitbox.Utxo> _utxos = [];
|
||||
// for (var element in utxosToUse) {
|
||||
// _utxos.add(bitbox.Utxo(element.hash, element.vout,
|
||||
// bitbox.BitcoinCash.fromSatoshi(element.value), element.value, 0, 1));
|
||||
// }
|
||||
//
|
||||
// final signatures = <Map>[];
|
||||
// int totalBalance = 0;
|
||||
//
|
||||
// _utxos.forEach((bitbox.Utxo utxo) {
|
||||
// // add the utxo as an input for the transaction
|
||||
// builder.addInput(utxo.txid, utxo.vout);
|
||||
//
|
||||
// final ec = utxoSigningData.firstWhere((e) => e.utxo.hash == utxo.txid).keyPair!;
|
||||
//
|
||||
// final bitboxEC = bitbox.ECPair.fromWIF(ec.toWIF());
|
||||
//
|
||||
// // add a signature to the list to be used later
|
||||
// signatures
|
||||
// .add({"vin": signatures.length, "key_pair": bitboxEC, "original_amount": utxo.satoshis});
|
||||
//
|
||||
// totalBalance += utxo.satoshis;
|
||||
// });
|
||||
//
|
||||
// // set an address to send the remaining balance to
|
||||
// final outputAddress = "13Hvge9HRduGiXMfcJHFn6sggequmaKqsZ";
|
||||
//
|
||||
// // if there is an unspent balance, create a spending transaction
|
||||
// if (totalBalance > 0 && outputAddress != "") {
|
||||
// // calculate the fee based on number of inputs and one expected output
|
||||
// final fee = bitbox.BitcoinCash.getByteCount(signatures.length, 1);
|
||||
//
|
||||
// // calculate how much balance will be left over to spend after the fee
|
||||
// final sendAmount = totalBalance - fee;
|
||||
//
|
||||
// // add the output based on the address provided in the testing data
|
||||
// builder.addOutput(outputAddress, sendAmount);
|
||||
//
|
||||
// // sign all inputs
|
||||
// signatures.forEach((signature) {
|
||||
// builder.sign(signature["vin"], signature["key_pair"], signature["original_amount"]);
|
||||
// });
|
||||
//
|
||||
// // build the transaction
|
||||
// final tx = builder.build();
|
||||
//
|
||||
// // broadcast the transaction
|
||||
// final result = await electrumClient.broadcastTransaction(transactionRaw: tx.toHex());
|
||||
//
|
||||
// // Yatta!
|
||||
// print("Transaction broadcasted: $result");
|
||||
// }
|
||||
return PendingBitcoinTransaction(bitcoin.Transaction(), type,
|
||||
electrumClient: electrumClient, amount: 1, fee: 1);
|
||||
const minAmount = 546;
|
||||
final inputs = <BitcoinUnspent>[];
|
||||
var allInputsAmount = 0;
|
||||
final outputs = transactionCredentials.outputs;
|
||||
final hasMultiDestination = outputs.length > 1;
|
||||
|
||||
if (unspentCoins.isEmpty) {
|
||||
await updateUnspent();
|
||||
}
|
||||
|
||||
for (final utx in unspentCoins) {
|
||||
if (utx.isSending) {
|
||||
allInputsAmount += utx.value;
|
||||
inputs.add(utx);
|
||||
}
|
||||
}
|
||||
|
||||
if (inputs.isEmpty) {
|
||||
throw BitcoinTransactionNoInputsException();
|
||||
}
|
||||
|
||||
final int feeRate = transactionCredentials.feeRate != null
|
||||
? transactionCredentials.feeRate!
|
||||
: BitcoinCashFeeRates.feeRate(transactionCredentials.priority!);
|
||||
|
||||
final int allAmountFee =
|
||||
bitbox.BitcoinCash.getByteCount(inputs.length, transactionCredentials.outputs.length) *
|
||||
feeRate;
|
||||
|
||||
final allAmount = allInputsAmount - allAmountFee;
|
||||
var credentialsAmount = 0;
|
||||
var amount = 0;
|
||||
var fee = 0;
|
||||
|
||||
if (hasMultiDestination) {
|
||||
if (outputs.any((item) => item.sendAll || item.formattedCryptoAmount! <= 0)) {
|
||||
throw BitcoinTransactionWrongBalanceException(currency);
|
||||
}
|
||||
|
||||
credentialsAmount = outputs.fold(0, (acc, value) {
|
||||
return acc + value.formattedCryptoAmount!;
|
||||
});
|
||||
|
||||
print(credentialsAmount);
|
||||
|
||||
if (allAmount - credentialsAmount < minAmount) {
|
||||
throw BitcoinTransactionWrongBalanceException(currency);
|
||||
}
|
||||
|
||||
amount = credentialsAmount;
|
||||
|
||||
if (transactionCredentials.feeRate != null) {
|
||||
fee = calculateEstimatedFeeWithFeeRate(transactionCredentials.feeRate!, amount,
|
||||
outputsCount: outputs.length + 1);
|
||||
} else {
|
||||
fee = calculateEstimatedFee(transactionCredentials.priority, amount,
|
||||
outputsCount: outputs.length + 1);
|
||||
}
|
||||
} else {
|
||||
final output = outputs.first;
|
||||
credentialsAmount = !output.sendAll ? output.formattedCryptoAmount! : 0;
|
||||
|
||||
if (credentialsAmount > allAmount) {
|
||||
throw BitcoinTransactionWrongBalanceException(currency);
|
||||
}
|
||||
|
||||
amount = output.sendAll || allAmount - credentialsAmount < minAmount
|
||||
? allAmount
|
||||
: credentialsAmount;
|
||||
|
||||
if (output.sendAll || amount == allAmount) {
|
||||
fee = allAmountFee;
|
||||
} else if (transactionCredentials.feeRate != null) {
|
||||
fee = calculateEstimatedFeeWithFeeRate(transactionCredentials.feeRate!, amount);
|
||||
} else {
|
||||
fee = calculateEstimatedFee(transactionCredentials.priority, amount);
|
||||
}
|
||||
}
|
||||
|
||||
if (fee == 0) {
|
||||
throw BitcoinTransactionWrongBalanceException(currency);
|
||||
}
|
||||
|
||||
final totalAmount = amount + fee;
|
||||
|
||||
if (totalAmount > balance[currency]!.confirmed || totalAmount > allInputsAmount) {
|
||||
throw BitcoinTransactionWrongBalanceException(currency);
|
||||
}
|
||||
|
||||
// final changeAddress = await walletAddresses.getChangeAddress(); TODO: BCH: implement change address
|
||||
var leftAmount = totalAmount;
|
||||
var totalInputAmount = 0;
|
||||
|
||||
inputs.clear();
|
||||
|
||||
for (final utx in unspentCoins) {
|
||||
if (utx.isSending) {
|
||||
leftAmount = leftAmount - utx.value;
|
||||
totalInputAmount += utx.value;
|
||||
inputs.add(utx);
|
||||
|
||||
if (leftAmount <= 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (inputs.isEmpty) {
|
||||
throw BitcoinTransactionNoInputsException();
|
||||
}
|
||||
|
||||
if (amount <= 0 || totalInputAmount < totalAmount) {
|
||||
throw BitcoinTransactionWrongBalanceException(currency);
|
||||
}
|
||||
|
||||
final builder = bitbox.Bitbox.transactionBuilder(testnet: false);
|
||||
final _wallet = hd;
|
||||
|
||||
final utxoSigningData = await fetchBuildTxData(inputs, _wallet);
|
||||
|
||||
List<bitbox.Utxo> _utxos = [];
|
||||
for (var element in inputs) {
|
||||
_utxos.add(bitbox.Utxo(element.hash, element.vout,
|
||||
bitbox.BitcoinCash.fromSatoshi(element.value), element.value, 0, 1));
|
||||
}
|
||||
|
||||
final signatures = <Map>[];
|
||||
int totalBalance = 0;
|
||||
|
||||
_utxos.forEach((bitbox.Utxo utxo) {
|
||||
builder.addInput(utxo.txid, utxo.vout);
|
||||
|
||||
final ec = utxoSigningData.firstWhere((e) => e.utxo.hash == utxo.txid).keyPair!;
|
||||
|
||||
final bitboxEC = bitbox.ECPair.fromWIF(ec.toWIF());
|
||||
|
||||
signatures
|
||||
.add({"vin": signatures.length, "key_pair": bitboxEC, "original_amount": utxo.satoshis});
|
||||
|
||||
totalBalance += utxo.satoshis;
|
||||
});
|
||||
|
||||
outputs.forEach((item) {
|
||||
final outputAmount = hasMultiDestination ? item.formattedCryptoAmount : amount;
|
||||
final outputAddress = item.isParsedAddress ? item.extractedAddress! : item.address;
|
||||
builder.addOutput(outputAddress, outputAmount!);
|
||||
});
|
||||
|
||||
signatures.forEach((signature) {
|
||||
builder.sign(signature["vin"], signature["key_pair"], signature["original_amount"]);
|
||||
});
|
||||
|
||||
// build the transaction
|
||||
final tx = builder.build();
|
||||
return PendingBitcoinCashTransaction(tx, type,
|
||||
electrumClient: electrumClient, amount: amount, fee: fee)
|
||||
..addListener((transaction) async {
|
||||
transactionHistory.addOne(transaction);
|
||||
await updateBalance();
|
||||
});
|
||||
}
|
||||
|
||||
Future<List<SigningData>> fetchBuildTxData(
|
||||
List<BitcoinUnspent> utxosToUse, HDWallet wallet) async {
|
||||
// Initialize the list to store signing data
|
||||
List<SigningData> signingData = [];
|
||||
|
||||
try {
|
||||
// Iterate over UTXOs to populate the addresses and fetch transaction details
|
||||
for (var i = 0; i < utxosToUse.length; i++) {
|
||||
final txid = utxosToUse[i].hash;
|
||||
final tx = await electrumClient.getTransactionRaw(
|
||||
hash: txid); //TODO: BCH: replace with getting from local storage if possible
|
||||
|
||||
// Iterate through transaction outputs to find corresponding addresses
|
||||
for (final output in tx["vout"] as List) {
|
||||
// Handle each transaction output
|
||||
await handleTransactionOutput(output, utxosToUse[i]);
|
||||
}
|
||||
|
||||
// Determine address type and create signing data object
|
||||
signingData.add(SigningData(
|
||||
derivePathType: DerivePathType.bch44,
|
||||
utxo: utxosToUse[i])); //TODO: BCH: hardcoded DerivePathType.bch44
|
||||
|
||||
// Determine public key (pubKey) and Wallet Import Format (wif) here
|
||||
// TODO: You need to implement logic to determine pubKey and wif
|
||||
String? pubKey = wallet.pubKey;
|
||||
String? wif = wallet.wif;
|
||||
|
||||
// Then call the preparePaymentData function
|
||||
preparePaymentData(
|
||||
signingData[i], pubKey, wif, bitcoincash); //TODO: BCH: hardcoded bitcoincash
|
||||
}
|
||||
|
||||
// Return the signing data for later use
|
||||
return signingData;
|
||||
} catch (e) {
|
||||
print(e);
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
// Function to handle each transaction output
|
||||
Future<void> handleTransactionOutput(Map output, BitcoinUnspent utxo) async {
|
||||
final n = output["n"];
|
||||
if (n != null && n == utxo.vout) {
|
||||
String address = output["scriptPubKey"]?["addresses"]?[0] as String? ??
|
||||
output["scriptPubKey"]["address"] as String;
|
||||
|
||||
// Convert to Cash Address format if needed
|
||||
if (bitbox.Address.detectFormat(address) != bitbox.Address.formatCashAddr) {
|
||||
try {
|
||||
address = bitbox.Address.toCashAddress(address);
|
||||
} catch (_) {
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
// Update UTXO with the new address
|
||||
utxo.updateAddress(address); // Make sure 'address' is mutable or create a method to update it
|
||||
}
|
||||
}
|
||||
|
||||
// Function to prepare payment data
|
||||
void preparePaymentData(
|
||||
SigningData sd, String? pubKey, String? wif, bitcoin.NetworkType _network) {
|
||||
if (wif != null && pubKey != null) {
|
||||
PaymentData data; // Removed 'final' modifier
|
||||
final Uint8List? redeemScript;
|
||||
|
||||
switch (sd.derivePathType) {
|
||||
case DerivePathType.bip44:
|
||||
case DerivePathType.bch44:
|
||||
data = P2PKH(
|
||||
data: PaymentData(
|
||||
pubkey: Uint8List.fromList(HEX.decode(pubKey)),
|
||||
),
|
||||
network: _network,
|
||||
).data;
|
||||
redeemScript = null;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw Exception("DerivePathType unsupported");
|
||||
}
|
||||
|
||||
final keyPair = ECPair.fromWIF(
|
||||
wif,
|
||||
network: _network,
|
||||
);
|
||||
|
||||
sd.redeemScript = redeemScript;
|
||||
sd.output = data.output;
|
||||
sd.keyPair = keyPair;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class SigningData {
|
||||
SigningData({
|
||||
required this.derivePathType,
|
||||
required this.utxo,
|
||||
this.output,
|
||||
this.keyPair,
|
||||
this.redeemScript,
|
||||
});
|
||||
|
||||
final DerivePathType derivePathType;
|
||||
final BitcoinUnspent utxo;
|
||||
Uint8List? output;
|
||||
ECPair? keyPair;
|
||||
Uint8List? redeemScript;
|
||||
}
|
||||
|
||||
enum DerivePathType {
|
||||
bip44,
|
||||
bch44,
|
||||
bip49,
|
||||
bip84,
|
||||
eth,
|
||||
eCash44,
|
||||
}
|
||||
|
||||
// Bitcoincash Network
|
||||
final bitcoincash = bitcoin.NetworkType(
|
||||
messagePrefix: '\x18Bitcoin Signed Message:\n',
|
||||
bech32: 'bc',
|
||||
bip32: bitcoin.Bip32Type(public: 0x0488b21e, private: 0x0488ade4),
|
||||
pubKeyHash: 0x00,
|
||||
scriptHash: 0x05,
|
||||
wif: 0x80);
|
||||
|
||||
final bitcoincashtestnet = bitcoin.NetworkType(
|
||||
messagePrefix: '\x18Bitcoin Signed Message:\n',
|
||||
bech32: 'tb',
|
||||
bip32: bitcoin.Bip32Type(public: 0x043587cf, private: 0x04358394),
|
||||
pubKeyHash: 0x6f,
|
||||
scriptHash: 0xc4,
|
||||
wif: 0xef);
|
||||
|
||||
class BitcoinCashFeeRates {
|
||||
static const int highPriority = 10;
|
||||
static const int mediumPriority = 5;
|
||||
static const int lowPriority = 1;
|
||||
|
||||
static int feeRate(BitcoinTransactionPriority priority) {
|
||||
switch (priority) {
|
||||
case BitcoinTransactionPriority.fast:
|
||||
return highPriority;
|
||||
case BitcoinTransactionPriority.medium:
|
||||
return mediumPriority;
|
||||
case BitcoinTransactionPriority.slow:
|
||||
return lowPriority;
|
||||
default:
|
||||
throw Exception("Unknown priority level");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,62 @@
|
|||
import 'package:cw_bitcoin/bitcoin_commit_transaction_exception.dart';
|
||||
import 'package:bitbox/bitbox.dart' as bitbox;
|
||||
import 'package:cw_core/pending_transaction.dart';
|
||||
import 'package:cw_bitcoin/electrum.dart';
|
||||
import 'package:cw_bitcoin/bitcoin_amount_format.dart';
|
||||
import 'package:cw_bitcoin/electrum_transaction_info.dart';
|
||||
import 'package:cw_core/transaction_direction.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
|
||||
class PendingBitcoinCashTransaction with PendingTransaction {
|
||||
PendingBitcoinCashTransaction(this._tx, this.type,
|
||||
{required this.electrumClient,
|
||||
required this.amount,
|
||||
required this.fee})
|
||||
: _listeners = <void Function(ElectrumTransactionInfo transaction)>[];
|
||||
|
||||
final WalletType type;
|
||||
final bitbox.Transaction _tx;
|
||||
final ElectrumClient electrumClient;
|
||||
final int amount;
|
||||
final int fee;
|
||||
|
||||
@override
|
||||
String get id => _tx.getId();
|
||||
|
||||
@override
|
||||
String get hex => _tx.toHex();
|
||||
|
||||
@override
|
||||
String get amountFormatted => bitcoinAmountToString(amount: amount);
|
||||
|
||||
@override
|
||||
String get feeFormatted => bitcoinAmountToString(amount: fee);
|
||||
|
||||
final List<void Function(ElectrumTransactionInfo transaction)> _listeners;
|
||||
|
||||
@override
|
||||
Future<void> commit() async {
|
||||
final result =
|
||||
await electrumClient.broadcastTransaction(transactionRaw: _tx.toHex());
|
||||
|
||||
if (result.isEmpty) {
|
||||
throw BitcoinCommitTransactionException();
|
||||
}
|
||||
|
||||
_listeners?.forEach((listener) => listener(transactionInfo()));
|
||||
}
|
||||
|
||||
void addListener(
|
||||
void Function(ElectrumTransactionInfo transaction) listener) =>
|
||||
_listeners.add(listener);
|
||||
|
||||
ElectrumTransactionInfo transactionInfo() => ElectrumTransactionInfo(type,
|
||||
id: id,
|
||||
height: 0,
|
||||
amount: amount,
|
||||
direction: TransactionDirection.outgoing,
|
||||
date: DateTime.now(),
|
||||
isPending: true,
|
||||
confirmations: 0,
|
||||
fee: fee);
|
||||
}
|
|
@ -50,7 +50,7 @@ class CWBitcoin extends Bitcoin {
|
|||
|
||||
@override
|
||||
List<TransactionPriority> getBitcoinCashTransactionPriorities()
|
||||
=> BitcoinCashTransactionPriority.all;
|
||||
=> BitcoinTransactionPriority.all;
|
||||
|
||||
@override
|
||||
TransactionPriority deserializeBitcoinTransactionPriority(int raw)
|
||||
|
@ -62,7 +62,7 @@ class CWBitcoin extends Bitcoin {
|
|||
|
||||
@override
|
||||
TransactionPriority deserializeBitcoinCashTransactionPriority(int raw)
|
||||
=> BitcoinCashTransactionPriority.deserialize(raw: raw);
|
||||
=> BitcoinTransactionPriority.deserialize(raw: raw);
|
||||
|
||||
@override
|
||||
int getFeeRate(Object wallet, TransactionPriority priority) {
|
||||
|
|
|
@ -28,11 +28,11 @@ class CWBitcoinCash extends BitcoinCash {
|
|||
|
||||
@override
|
||||
TransactionPriority deserializeBitcoinCashTransactionPriority(int raw) =>
|
||||
BitcoinCashTransactionPriority.deserialize(raw: raw);
|
||||
BitcoinTransactionPriority.deserialize(raw: raw);
|
||||
|
||||
@override
|
||||
TransactionPriority getDefaultTransactionPriority() => BitcoinCashTransactionPriority.medium;
|
||||
TransactionPriority getDefaultTransactionPriority() => BitcoinTransactionPriority.medium;
|
||||
|
||||
@override
|
||||
List<TransactionPriority> getTransactionPriorities() => BitcoinCashTransactionPriority.all;
|
||||
List<TransactionPriority> getTransactionPriorities() => BitcoinTransactionPriority.all;
|
||||
}
|
||||
|
|
|
@ -87,6 +87,7 @@ class AddressValidator extends TextValidator {
|
|||
case CryptoCurrency.dash:
|
||||
case CryptoCurrency.eos:
|
||||
case CryptoCurrency.bch:
|
||||
return '[0-9a-zA-Z,:]';
|
||||
case CryptoCurrency.bnb:
|
||||
return '[0-9a-zA-Z]';
|
||||
case CryptoCurrency.ltc:
|
||||
|
@ -171,7 +172,7 @@ class AddressValidator extends TextValidator {
|
|||
case CryptoCurrency.shib:
|
||||
case CryptoCurrency.avaxc:
|
||||
case CryptoCurrency.bch:
|
||||
return [32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44]; //TODO: BCH: replace with correct length
|
||||
return [32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44,54,55,56]; //TODO: BCH: replace with correct length
|
||||
case CryptoCurrency.bnb:
|
||||
return [42];
|
||||
case CryptoCurrency.ltc:
|
||||
|
|
|
@ -81,10 +81,8 @@ abstract class OutputBase with Store {
|
|||
_amount = monero!.formatterMoneroParseAmount(amount: _cryptoAmount);
|
||||
break;
|
||||
case WalletType.bitcoin:
|
||||
_amount =
|
||||
bitcoin!.formatterStringDoubleToBitcoinAmount(_cryptoAmount);
|
||||
break;
|
||||
case WalletType.litecoin:
|
||||
case WalletType.bitcoinCash:
|
||||
_amount =
|
||||
bitcoin!.formatterStringDoubleToBitcoinAmount(_cryptoAmount);
|
||||
break;
|
||||
|
@ -116,7 +114,8 @@ abstract class OutputBase with Store {
|
|||
_settingsStore.priority[_wallet.type]!, formattedCryptoAmount);
|
||||
|
||||
if (_wallet.type == WalletType.bitcoin ||
|
||||
_wallet.type == WalletType.litecoin) {
|
||||
_wallet.type == WalletType.litecoin ||
|
||||
_wallet.type == WalletType.bitcoinCash) {
|
||||
return bitcoin!.formatterBitcoinAmountToDouble(amount: fee);
|
||||
}
|
||||
|
||||
|
@ -234,6 +233,9 @@ abstract class OutputBase with Store {
|
|||
case WalletType.litecoin:
|
||||
maximumFractionDigits = 8;
|
||||
break;
|
||||
case WalletType.bitcoinCash:
|
||||
maximumFractionDigits = 8;
|
||||
break;
|
||||
case WalletType.haven:
|
||||
maximumFractionDigits = 12;
|
||||
break;
|
||||
|
|
Loading…
Reference in a new issue