mirror of
https://github.com/cypherstack/stack_wallet.git
synced 2025-01-09 20:29:57 +00:00
bitcoin cash sending works for legacy and new addresses bip44
This commit is contained in:
parent
5cbaa597d3
commit
cbb3c3f241
2 changed files with 87 additions and 40 deletions
|
@ -41,6 +41,7 @@ import 'package:stackwallet/utilities/logger.dart';
|
||||||
import 'package:stackwallet/utilities/prefs.dart';
|
import 'package:stackwallet/utilities/prefs.dart';
|
||||||
import 'package:tuple/tuple.dart';
|
import 'package:tuple/tuple.dart';
|
||||||
import 'package:uuid/uuid.dart';
|
import 'package:uuid/uuid.dart';
|
||||||
|
import 'package:bitbox/bitbox.dart' as Bitbox;
|
||||||
|
|
||||||
const int MINIMUM_CONFIRMATIONS = 3;
|
const int MINIMUM_CONFIRMATIONS = 3;
|
||||||
const int DUST_LIMIT = 1000000;
|
const int DUST_LIMIT = 1000000;
|
||||||
|
@ -735,6 +736,9 @@ class BitcoinCashWallet extends CoinServiceAPI {
|
||||||
/// Refreshes display data for the wallet
|
/// Refreshes display data for the wallet
|
||||||
@override
|
@override
|
||||||
Future<void> refresh() async {
|
Future<void> refresh() async {
|
||||||
|
final bchaddr = Bitbox.Address.toCashAddress(await currentReceivingAddress);
|
||||||
|
print("bchaddr: $bchaddr ${await currentReceivingAddress}");
|
||||||
|
|
||||||
if (refreshMutex) {
|
if (refreshMutex) {
|
||||||
Logging.instance.log("$walletId $walletName refreshMutex denied",
|
Logging.instance.log("$walletId $walletName refreshMutex denied",
|
||||||
level: LogLevel.Info);
|
level: LogLevel.Info);
|
||||||
|
@ -1050,7 +1054,14 @@ class BitcoinCashWallet extends CoinServiceAPI {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool validateAddress(String address) {
|
bool validateAddress(String address) {
|
||||||
return Address.validateAddress(address, _network);
|
try {
|
||||||
|
// 0 for bitcoincash: address scheme, 1 for legacy address
|
||||||
|
final format = Bitbox.Address.detectFormat(address);
|
||||||
|
print("format $format");
|
||||||
|
return true;
|
||||||
|
} catch (e, s) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -1947,6 +1958,7 @@ class BitcoinCashWallet extends CoinServiceAPI {
|
||||||
List<Map<String, dynamic>> allTransactions = [];
|
List<Map<String, dynamic>> allTransactions = [];
|
||||||
|
|
||||||
for (final txHash in allTxHashes) {
|
for (final txHash in allTxHashes) {
|
||||||
|
Logging.instance.log("bch: $txHash", level: LogLevel.Info);
|
||||||
final tx = await cachedElectrumXClient.getTransaction(
|
final tx = await cachedElectrumXClient.getTransaction(
|
||||||
txHash: txHash["tx_hash"] as String,
|
txHash: txHash["tx_hash"] as String,
|
||||||
verbose: true,
|
verbose: true,
|
||||||
|
@ -2318,8 +2330,8 @@ class BitcoinCashWallet extends CoinServiceAPI {
|
||||||
vSize: vSizeForOneOutput,
|
vSize: vSizeForOneOutput,
|
||||||
feeRatePerKB: selectedTxFeeRate,
|
feeRatePerKB: selectedTxFeeRate,
|
||||||
);
|
);
|
||||||
if (feeForOneOutput < (vSizeForOneOutput + 1) * 1000) {
|
if (feeForOneOutput < (vSizeForOneOutput + 1)) {
|
||||||
feeForOneOutput = (vSizeForOneOutput + 1) * 1000;
|
feeForOneOutput = (vSizeForOneOutput + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
final int amount = satoshiAmountToSend - feeForOneOutput;
|
final int amount = satoshiAmountToSend - feeForOneOutput;
|
||||||
|
@ -2375,11 +2387,11 @@ class BitcoinCashWallet extends CoinServiceAPI {
|
||||||
.log("feeForTwoOutputs: $feeForTwoOutputs", level: LogLevel.Info);
|
.log("feeForTwoOutputs: $feeForTwoOutputs", level: LogLevel.Info);
|
||||||
Logging.instance
|
Logging.instance
|
||||||
.log("feeForOneOutput: $feeForOneOutput", level: LogLevel.Info);
|
.log("feeForOneOutput: $feeForOneOutput", level: LogLevel.Info);
|
||||||
if (feeForOneOutput < (vSizeForOneOutput + 1) * 1000) {
|
if (feeForOneOutput < (vSizeForOneOutput + 1)) {
|
||||||
feeForOneOutput = (vSizeForOneOutput + 1) * 1000;
|
feeForOneOutput = (vSizeForOneOutput + 1);
|
||||||
}
|
}
|
||||||
if (feeForTwoOutputs < ((vSizeForTwoOutPuts + 1) * 1000)) {
|
if (feeForTwoOutputs < ((vSizeForTwoOutPuts + 1))) {
|
||||||
feeForTwoOutputs = ((vSizeForTwoOutPuts + 1) * 1000);
|
feeForTwoOutputs = ((vSizeForTwoOutPuts + 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
Logging.instance
|
Logging.instance
|
||||||
|
@ -2679,45 +2691,76 @@ class BitcoinCashWallet extends CoinServiceAPI {
|
||||||
required List<String> recipients,
|
required List<String> recipients,
|
||||||
required List<int> satoshiAmounts,
|
required List<int> satoshiAmounts,
|
||||||
}) async {
|
}) async {
|
||||||
Logging.instance
|
final builder = Bitbox.Bitbox.transactionBuilder();
|
||||||
.log("Starting buildTransaction ----------", level: LogLevel.Info);
|
|
||||||
|
|
||||||
final txb = TransactionBuilder(network: _network);
|
// retrieve address' utxos from the rest api
|
||||||
txb.setVersion(1);
|
List<Bitbox.Utxo> _utxos =
|
||||||
|
[]; // await Bitbox.Address.utxo(address) as List<Bitbox.Utxo>;
|
||||||
|
utxosToUse.forEach((element) {
|
||||||
|
_utxos.add(Bitbox.Utxo(
|
||||||
|
element.txid,
|
||||||
|
element.vout,
|
||||||
|
Bitbox.BitcoinCash.fromSatoshi(element.value),
|
||||||
|
element.value,
|
||||||
|
0,
|
||||||
|
MINIMUM_CONFIRMATIONS + 1));
|
||||||
|
});
|
||||||
|
Logger.print("bch utxos: ${_utxos}");
|
||||||
|
|
||||||
// Add transaction inputs
|
// placeholder for input signatures
|
||||||
for (var i = 0; i < utxosToUse.length; i++) {
|
final signatures = <Map>[];
|
||||||
final txid = utxosToUse[i].txid;
|
|
||||||
txb.addInput(txid, utxosToUse[i].vout, null,
|
// placeholder for total input balance
|
||||||
utxoSigningData[txid]["output"] as Uint8List);
|
int totalBalance = 0;
|
||||||
|
|
||||||
|
// iterate through the list of address _utxos and use them as inputs for the
|
||||||
|
// withdrawal transaction
|
||||||
|
_utxos.forEach((Bitbox.Utxo utxo) {
|
||||||
|
// add the utxo as an input for the transaction
|
||||||
|
builder.addInput(utxo.txid, utxo.vout);
|
||||||
|
final ec = utxoSigningData[utxo.txid]["keyPair"] as ECPair;
|
||||||
|
|
||||||
|
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;
|
||||||
|
});
|
||||||
|
|
||||||
|
// calculate the fee based on number of inputs and one expected output
|
||||||
|
final fee =
|
||||||
|
Bitbox.BitcoinCash.getByteCount(signatures.length, recipients.length);
|
||||||
|
|
||||||
|
// 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
|
||||||
|
for (int i = 0; i < recipients.length; i++) {
|
||||||
|
String recipient = recipients[i];
|
||||||
|
int satoshiAmount = satoshiAmounts[i];
|
||||||
|
builder.addOutput(recipient, satoshiAmount);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add transaction output
|
// sign all inputs
|
||||||
for (var i = 0; i < recipients.length; i++) {
|
signatures.forEach((signature) {
|
||||||
txb.addOutput(recipients[i], satoshiAmounts[i]);
|
builder.sign(
|
||||||
}
|
signature["vin"] as int,
|
||||||
|
signature["key_pair"] as Bitbox.ECPair,
|
||||||
|
signature["original_amount"] as int);
|
||||||
|
});
|
||||||
|
|
||||||
try {
|
// build the transaction
|
||||||
// Sign the transaction accordingly
|
final tx = builder.build();
|
||||||
for (var i = 0; i < utxosToUse.length; i++) {
|
final txHex = tx.toHex();
|
||||||
final txid = utxosToUse[i].txid;
|
final vSize = tx.virtualSize();
|
||||||
txb.sign(
|
Logger.print("bch raw hex: $txHex");
|
||||||
vin: i,
|
|
||||||
keyPair: utxoSigningData[txid]["keyPair"] as ECPair,
|
|
||||||
witnessValue: utxosToUse[i].value,
|
|
||||||
redeemScript: utxoSigningData[txid]["redeemScript"] as Uint8List?,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} catch (e, s) {
|
|
||||||
Logging.instance.log("Caught exception while signing transaction: $e\n$s",
|
|
||||||
level: LogLevel.Error);
|
|
||||||
rethrow;
|
|
||||||
}
|
|
||||||
|
|
||||||
final builtTx = txb.build();
|
return {"hex": txHex, "vSize": vSize};
|
||||||
final vSize = builtTx.virtualSize();
|
|
||||||
|
|
||||||
return {"hex": builtTx.toHex(), "vSize": vSize};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
|
@ -69,6 +69,10 @@ dependencies:
|
||||||
git:
|
git:
|
||||||
url: https://github.com/cypherstack/stack-bip39.git
|
url: https://github.com/cypherstack/stack-bip39.git
|
||||||
ref: 3bef5acc21340f3cc78df0ad1dce5868a3ed68a5
|
ref: 3bef5acc21340f3cc78df0ad1dce5868a3ed68a5
|
||||||
|
bitbox:
|
||||||
|
git:
|
||||||
|
url: https://github.com/Quppy/bitbox-flutter.git
|
||||||
|
ref: ea65073efbaf395a5557e8cd7bd72f195cd7eb11
|
||||||
bip32: ^2.0.0
|
bip32: ^2.0.0
|
||||||
bech32: ^0.2.1
|
bech32: ^0.2.1
|
||||||
bs58check: ^1.0.2
|
bs58check: ^1.0.2
|
||||||
|
|
Loading…
Reference in a new issue