mirror of
https://github.com/cypherstack/stack_wallet.git
synced 2025-03-21 22:58:49 +00:00
move standard electrum x transaction parsing function into a mixin
This commit is contained in:
parent
8db66faecc
commit
5c66b0380b
6 changed files with 214 additions and 203 deletions
|
@ -16,13 +16,13 @@ import 'package:stackwallet/electrumx_rpc/electrumx.dart';
|
||||||
import 'package:stackwallet/models/balance.dart';
|
import 'package:stackwallet/models/balance.dart';
|
||||||
import 'package:stackwallet/models/isar/models/isar_models.dart' as isar_models;
|
import 'package:stackwallet/models/isar/models/isar_models.dart' as isar_models;
|
||||||
import 'package:stackwallet/models/paymint/fee_object_model.dart';
|
import 'package:stackwallet/models/paymint/fee_object_model.dart';
|
||||||
import 'package:stackwallet/services/coins/coin_paynym_extension.dart';
|
|
||||||
import 'package:stackwallet/services/coins/coin_service.dart';
|
import 'package:stackwallet/services/coins/coin_service.dart';
|
||||||
import 'package:stackwallet/services/event_bus/events/global/node_connection_status_changed_event.dart';
|
import 'package:stackwallet/services/event_bus/events/global/node_connection_status_changed_event.dart';
|
||||||
import 'package:stackwallet/services/event_bus/events/global/refresh_percent_changed_event.dart';
|
import 'package:stackwallet/services/event_bus/events/global/refresh_percent_changed_event.dart';
|
||||||
import 'package:stackwallet/services/event_bus/events/global/updated_in_background_event.dart';
|
import 'package:stackwallet/services/event_bus/events/global/updated_in_background_event.dart';
|
||||||
import 'package:stackwallet/services/event_bus/events/global/wallet_sync_status_changed_event.dart';
|
import 'package:stackwallet/services/event_bus/events/global/wallet_sync_status_changed_event.dart';
|
||||||
import 'package:stackwallet/services/event_bus/global_event_bus.dart';
|
import 'package:stackwallet/services/event_bus/global_event_bus.dart';
|
||||||
|
import 'package:stackwallet/services/mixins/electrum_x_parsing.dart';
|
||||||
import 'package:stackwallet/services/mixins/wallet_cache.dart';
|
import 'package:stackwallet/services/mixins/wallet_cache.dart';
|
||||||
import 'package:stackwallet/services/mixins/wallet_db.dart';
|
import 'package:stackwallet/services/mixins/wallet_db.dart';
|
||||||
import 'package:stackwallet/services/node_service.dart';
|
import 'package:stackwallet/services/node_service.dart';
|
||||||
|
@ -138,7 +138,8 @@ bip32.BIP32 getBip32RootWrapper(Tuple2<String, NetworkType> args) {
|
||||||
return getBip32Root(args.item1, args.item2);
|
return getBip32Root(args.item1, args.item2);
|
||||||
}
|
}
|
||||||
|
|
||||||
class BitcoinWallet extends CoinServiceAPI with WalletCache, WalletDB {
|
class BitcoinWallet extends CoinServiceAPI
|
||||||
|
with WalletCache, WalletDB, ElectrumXParsing {
|
||||||
static const integrationTestFlag =
|
static const integrationTestFlag =
|
||||||
bool.fromEnvironment("IS_INTEGRATION_TEST");
|
bool.fromEnvironment("IS_INTEGRATION_TEST");
|
||||||
|
|
||||||
|
|
|
@ -6,14 +6,12 @@ import 'package:bip47/src/util.dart';
|
||||||
import 'package:bitcoindart/bitcoindart.dart' as btc_dart;
|
import 'package:bitcoindart/bitcoindart.dart' as btc_dart;
|
||||||
import 'package:bitcoindart/src/utils/constants/op.dart' as op;
|
import 'package:bitcoindart/src/utils/constants/op.dart' as op;
|
||||||
import 'package:bitcoindart/src/utils/script.dart' as bscript;
|
import 'package:bitcoindart/src/utils/script.dart' as bscript;
|
||||||
import 'package:decimal/decimal.dart';
|
|
||||||
import 'package:isar/isar.dart';
|
import 'package:isar/isar.dart';
|
||||||
import 'package:pointycastle/digests/sha256.dart';
|
import 'package:pointycastle/digests/sha256.dart';
|
||||||
import 'package:stackwallet/hive/db.dart';
|
import 'package:stackwallet/hive/db.dart';
|
||||||
import 'package:stackwallet/models/isar/models/isar_models.dart';
|
import 'package:stackwallet/models/isar/models/isar_models.dart';
|
||||||
import 'package:stackwallet/services/coins/dogecoin/dogecoin_wallet.dart';
|
import 'package:stackwallet/services/coins/dogecoin/dogecoin_wallet.dart';
|
||||||
import 'package:stackwallet/utilities/address_utils.dart';
|
import 'package:stackwallet/utilities/address_utils.dart';
|
||||||
import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
|
||||||
import 'package:stackwallet/utilities/format.dart';
|
import 'package:stackwallet/utilities/format.dart';
|
||||||
import 'package:stackwallet/utilities/logger.dart';
|
import 'package:stackwallet/utilities/logger.dart';
|
||||||
import 'package:tuple/tuple.dart';
|
import 'package:tuple/tuple.dart';
|
||||||
|
@ -561,196 +559,3 @@ extension PayNym on DogecoinWallet {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<Tuple4<Transaction, List<Output>, List<Input>, Address>>
|
|
||||||
parseTransaction(
|
|
||||||
Map<String, dynamic> txData,
|
|
||||||
dynamic electrumxClient,
|
|
||||||
List<Address> myAddresses,
|
|
||||||
Coin coin,
|
|
||||||
int minConfirms,
|
|
||||||
String walletId,
|
|
||||||
) async {
|
|
||||||
Set<String> receivingAddresses = myAddresses
|
|
||||||
.where((e) => e.subType == AddressSubType.receiving)
|
|
||||||
.map((e) => e.value)
|
|
||||||
.toSet();
|
|
||||||
Set<String> changeAddresses = myAddresses
|
|
||||||
.where((e) => e.subType == AddressSubType.change)
|
|
||||||
.map((e) => e.value)
|
|
||||||
.toSet();
|
|
||||||
|
|
||||||
Set<String> inputAddresses = {};
|
|
||||||
Set<String> outputAddresses = {};
|
|
||||||
|
|
||||||
int totalInputValue = 0;
|
|
||||||
int totalOutputValue = 0;
|
|
||||||
|
|
||||||
int amountSentFromWallet = 0;
|
|
||||||
int amountReceivedInWallet = 0;
|
|
||||||
int changeAmount = 0;
|
|
||||||
|
|
||||||
// parse inputs
|
|
||||||
for (final input in txData["vin"] as List) {
|
|
||||||
final prevTxid = input["txid"] as String;
|
|
||||||
final prevOut = input["vout"] as int;
|
|
||||||
|
|
||||||
// fetch input tx to get address
|
|
||||||
final inputTx = await electrumxClient.getTransaction(
|
|
||||||
txHash: prevTxid,
|
|
||||||
coin: coin,
|
|
||||||
);
|
|
||||||
|
|
||||||
for (final output in inputTx["vout"] as List) {
|
|
||||||
// check matching output
|
|
||||||
if (prevOut == output["n"]) {
|
|
||||||
// get value
|
|
||||||
final value = Format.decimalAmountToSatoshis(
|
|
||||||
Decimal.parse(output["value"].toString()),
|
|
||||||
coin,
|
|
||||||
);
|
|
||||||
|
|
||||||
// add value to total
|
|
||||||
totalInputValue += value;
|
|
||||||
|
|
||||||
// get input(prevOut) address
|
|
||||||
final address = output["scriptPubKey"]?["addresses"]?[0] as String? ??
|
|
||||||
output["scriptPubKey"]?["address"] as String?;
|
|
||||||
|
|
||||||
if (address != null) {
|
|
||||||
inputAddresses.add(address);
|
|
||||||
|
|
||||||
// if input was from my wallet, add value to amount sent
|
|
||||||
if (receivingAddresses.contains(address) ||
|
|
||||||
changeAddresses.contains(address)) {
|
|
||||||
amountSentFromWallet += value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// parse outputs
|
|
||||||
for (final output in txData["vout"] as List) {
|
|
||||||
// get value
|
|
||||||
final value = Format.decimalAmountToSatoshis(
|
|
||||||
Decimal.parse(output["value"].toString()),
|
|
||||||
coin,
|
|
||||||
);
|
|
||||||
|
|
||||||
// add value to total
|
|
||||||
totalOutputValue += value;
|
|
||||||
|
|
||||||
// get output address
|
|
||||||
final address = output["scriptPubKey"]?["addresses"]?[0] as String? ??
|
|
||||||
output["scriptPubKey"]?["address"] as String?;
|
|
||||||
if (address != null) {
|
|
||||||
outputAddresses.add(address);
|
|
||||||
|
|
||||||
// if output was to my wallet, add value to amount received
|
|
||||||
if (receivingAddresses.contains(address)) {
|
|
||||||
amountReceivedInWallet += value;
|
|
||||||
} else if (changeAddresses.contains(address)) {
|
|
||||||
changeAmount += value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
final mySentFromAddresses = [
|
|
||||||
...receivingAddresses.intersection(inputAddresses),
|
|
||||||
...changeAddresses.intersection(inputAddresses)
|
|
||||||
];
|
|
||||||
final myReceivedOnAddresses =
|
|
||||||
receivingAddresses.intersection(outputAddresses);
|
|
||||||
final myChangeReceivedOnAddresses =
|
|
||||||
changeAddresses.intersection(outputAddresses);
|
|
||||||
|
|
||||||
final fee = totalInputValue - totalOutputValue;
|
|
||||||
|
|
||||||
// this is the address initially used to fetch the txid
|
|
||||||
Address transactionAddress = txData["address"] as Address;
|
|
||||||
|
|
||||||
TransactionType type;
|
|
||||||
int amount;
|
|
||||||
if (mySentFromAddresses.isNotEmpty && myReceivedOnAddresses.isNotEmpty) {
|
|
||||||
// tx is sent to self
|
|
||||||
type = TransactionType.sentToSelf;
|
|
||||||
|
|
||||||
// should be 0
|
|
||||||
amount = amountSentFromWallet - amountReceivedInWallet - fee - changeAmount;
|
|
||||||
} else if (mySentFromAddresses.isNotEmpty) {
|
|
||||||
// outgoing tx
|
|
||||||
type = TransactionType.outgoing;
|
|
||||||
amount = amountSentFromWallet - changeAmount - fee;
|
|
||||||
|
|
||||||
final possible =
|
|
||||||
outputAddresses.difference(myChangeReceivedOnAddresses).first;
|
|
||||||
|
|
||||||
if (transactionAddress.value != possible) {
|
|
||||||
transactionAddress = Address(
|
|
||||||
walletId: walletId,
|
|
||||||
value: possible,
|
|
||||||
derivationIndex: -1,
|
|
||||||
subType: AddressSubType.nonWallet,
|
|
||||||
type: AddressType.nonWallet,
|
|
||||||
publicKey: [],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// incoming tx
|
|
||||||
type = TransactionType.incoming;
|
|
||||||
amount = amountReceivedInWallet;
|
|
||||||
}
|
|
||||||
|
|
||||||
final tx = Transaction(
|
|
||||||
walletId: walletId,
|
|
||||||
txid: txData["txid"] as String,
|
|
||||||
timestamp: txData["blocktime"] as int? ??
|
|
||||||
(DateTime.now().millisecondsSinceEpoch ~/ 1000),
|
|
||||||
type: type,
|
|
||||||
subType: TransactionSubType.none,
|
|
||||||
amount: amount,
|
|
||||||
fee: fee,
|
|
||||||
height: txData["height"] as int?,
|
|
||||||
isCancelled: false,
|
|
||||||
isLelantus: false,
|
|
||||||
slateId: null,
|
|
||||||
otherData: null,
|
|
||||||
);
|
|
||||||
|
|
||||||
List<Output> outs = [];
|
|
||||||
List<Input> ins = [];
|
|
||||||
|
|
||||||
for (final json in txData["vin"] as List) {
|
|
||||||
bool isCoinBase = json['coinbase'] != null;
|
|
||||||
final input = Input(
|
|
||||||
walletId: walletId,
|
|
||||||
txid: json['txid'] as String,
|
|
||||||
vout: json['vout'] as int? ?? -1,
|
|
||||||
scriptSig: json['scriptSig']?['hex'] as String?,
|
|
||||||
scriptSigAsm: json['scriptSig']?['asm'] as String?,
|
|
||||||
isCoinbase: isCoinBase ? isCoinBase : json['is_coinbase'] as bool?,
|
|
||||||
sequence: json['sequence'] as int?,
|
|
||||||
innerRedeemScriptAsm: json['innerRedeemscriptAsm'] as String?,
|
|
||||||
);
|
|
||||||
ins.add(input);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (final json in txData["vout"] as List) {
|
|
||||||
final output = Output(
|
|
||||||
walletId: walletId,
|
|
||||||
scriptPubKey: json['scriptPubKey']?['hex'] as String?,
|
|
||||||
scriptPubKeyAsm: json['scriptPubKey']?['asm'] as String?,
|
|
||||||
scriptPubKeyType: json['scriptPubKey']?['type'] as String?,
|
|
||||||
scriptPubKeyAddress: json["scriptPubKey"]?["addresses"]?[0] as String? ??
|
|
||||||
json['scriptPubKey']['type'] as String,
|
|
||||||
value: Format.decimalAmountToSatoshis(
|
|
||||||
Decimal.parse(json["value"].toString()),
|
|
||||||
coin,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
outs.add(output);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Tuple4(tx, outs, ins, transactionAddress);
|
|
||||||
}
|
|
||||||
|
|
|
@ -17,13 +17,13 @@ import 'package:stackwallet/electrumx_rpc/electrumx.dart';
|
||||||
import 'package:stackwallet/models/balance.dart';
|
import 'package:stackwallet/models/balance.dart';
|
||||||
import 'package:stackwallet/models/isar/models/isar_models.dart' as isar_models;
|
import 'package:stackwallet/models/isar/models/isar_models.dart' as isar_models;
|
||||||
import 'package:stackwallet/models/paymint/fee_object_model.dart';
|
import 'package:stackwallet/models/paymint/fee_object_model.dart';
|
||||||
import 'package:stackwallet/services/coins/coin_paynym_extension.dart';
|
|
||||||
import 'package:stackwallet/services/coins/coin_service.dart';
|
import 'package:stackwallet/services/coins/coin_service.dart';
|
||||||
import 'package:stackwallet/services/event_bus/events/global/node_connection_status_changed_event.dart';
|
import 'package:stackwallet/services/event_bus/events/global/node_connection_status_changed_event.dart';
|
||||||
import 'package:stackwallet/services/event_bus/events/global/refresh_percent_changed_event.dart';
|
import 'package:stackwallet/services/event_bus/events/global/refresh_percent_changed_event.dart';
|
||||||
import 'package:stackwallet/services/event_bus/events/global/updated_in_background_event.dart';
|
import 'package:stackwallet/services/event_bus/events/global/updated_in_background_event.dart';
|
||||||
import 'package:stackwallet/services/event_bus/events/global/wallet_sync_status_changed_event.dart';
|
import 'package:stackwallet/services/event_bus/events/global/wallet_sync_status_changed_event.dart';
|
||||||
import 'package:stackwallet/services/event_bus/global_event_bus.dart';
|
import 'package:stackwallet/services/event_bus/global_event_bus.dart';
|
||||||
|
import 'package:stackwallet/services/mixins/electrum_x_parsing.dart';
|
||||||
import 'package:stackwallet/services/mixins/wallet_cache.dart';
|
import 'package:stackwallet/services/mixins/wallet_cache.dart';
|
||||||
import 'package:stackwallet/services/mixins/wallet_db.dart';
|
import 'package:stackwallet/services/mixins/wallet_db.dart';
|
||||||
import 'package:stackwallet/services/node_service.dart';
|
import 'package:stackwallet/services/node_service.dart';
|
||||||
|
@ -125,7 +125,8 @@ bip32.BIP32 getBip32RootWrapper(Tuple2<String, NetworkType> args) {
|
||||||
return getBip32Root(args.item1, args.item2);
|
return getBip32Root(args.item1, args.item2);
|
||||||
}
|
}
|
||||||
|
|
||||||
class DogecoinWallet extends CoinServiceAPI with WalletCache, WalletDB {
|
class DogecoinWallet extends CoinServiceAPI
|
||||||
|
with WalletCache, WalletDB, ElectrumXParsing {
|
||||||
static const integrationTestFlag =
|
static const integrationTestFlag =
|
||||||
bool.fromEnvironment("IS_INTEGRATION_TEST");
|
bool.fromEnvironment("IS_INTEGRATION_TEST");
|
||||||
final _prefs = Prefs.instance;
|
final _prefs = Prefs.instance;
|
||||||
|
|
|
@ -17,13 +17,13 @@ import 'package:stackwallet/electrumx_rpc/electrumx.dart';
|
||||||
import 'package:stackwallet/models/balance.dart';
|
import 'package:stackwallet/models/balance.dart';
|
||||||
import 'package:stackwallet/models/isar/models/isar_models.dart' as isar_models;
|
import 'package:stackwallet/models/isar/models/isar_models.dart' as isar_models;
|
||||||
import 'package:stackwallet/models/paymint/fee_object_model.dart';
|
import 'package:stackwallet/models/paymint/fee_object_model.dart';
|
||||||
import 'package:stackwallet/services/coins/coin_paynym_extension.dart';
|
|
||||||
import 'package:stackwallet/services/coins/coin_service.dart';
|
import 'package:stackwallet/services/coins/coin_service.dart';
|
||||||
import 'package:stackwallet/services/event_bus/events/global/node_connection_status_changed_event.dart';
|
import 'package:stackwallet/services/event_bus/events/global/node_connection_status_changed_event.dart';
|
||||||
import 'package:stackwallet/services/event_bus/events/global/refresh_percent_changed_event.dart';
|
import 'package:stackwallet/services/event_bus/events/global/refresh_percent_changed_event.dart';
|
||||||
import 'package:stackwallet/services/event_bus/events/global/updated_in_background_event.dart';
|
import 'package:stackwallet/services/event_bus/events/global/updated_in_background_event.dart';
|
||||||
import 'package:stackwallet/services/event_bus/events/global/wallet_sync_status_changed_event.dart';
|
import 'package:stackwallet/services/event_bus/events/global/wallet_sync_status_changed_event.dart';
|
||||||
import 'package:stackwallet/services/event_bus/global_event_bus.dart';
|
import 'package:stackwallet/services/event_bus/global_event_bus.dart';
|
||||||
|
import 'package:stackwallet/services/mixins/electrum_x_parsing.dart';
|
||||||
import 'package:stackwallet/services/mixins/wallet_cache.dart';
|
import 'package:stackwallet/services/mixins/wallet_cache.dart';
|
||||||
import 'package:stackwallet/services/mixins/wallet_db.dart';
|
import 'package:stackwallet/services/mixins/wallet_db.dart';
|
||||||
import 'package:stackwallet/services/node_service.dart';
|
import 'package:stackwallet/services/node_service.dart';
|
||||||
|
@ -138,7 +138,8 @@ bip32.BIP32 getBip32RootWrapper(Tuple2<String, NetworkType> args) {
|
||||||
return getBip32Root(args.item1, args.item2);
|
return getBip32Root(args.item1, args.item2);
|
||||||
}
|
}
|
||||||
|
|
||||||
class LitecoinWallet extends CoinServiceAPI with WalletCache, WalletDB {
|
class LitecoinWallet extends CoinServiceAPI
|
||||||
|
with WalletCache, WalletDB, ElectrumXParsing {
|
||||||
static const integrationTestFlag =
|
static const integrationTestFlag =
|
||||||
bool.fromEnvironment("IS_INTEGRATION_TEST");
|
bool.fromEnvironment("IS_INTEGRATION_TEST");
|
||||||
|
|
||||||
|
|
|
@ -17,13 +17,13 @@ import 'package:stackwallet/electrumx_rpc/electrumx.dart';
|
||||||
import 'package:stackwallet/models/balance.dart';
|
import 'package:stackwallet/models/balance.dart';
|
||||||
import 'package:stackwallet/models/isar/models/isar_models.dart' as isar_models;
|
import 'package:stackwallet/models/isar/models/isar_models.dart' as isar_models;
|
||||||
import 'package:stackwallet/models/paymint/fee_object_model.dart';
|
import 'package:stackwallet/models/paymint/fee_object_model.dart';
|
||||||
import 'package:stackwallet/services/coins/coin_paynym_extension.dart';
|
|
||||||
import 'package:stackwallet/services/coins/coin_service.dart';
|
import 'package:stackwallet/services/coins/coin_service.dart';
|
||||||
import 'package:stackwallet/services/event_bus/events/global/node_connection_status_changed_event.dart';
|
import 'package:stackwallet/services/event_bus/events/global/node_connection_status_changed_event.dart';
|
||||||
import 'package:stackwallet/services/event_bus/events/global/refresh_percent_changed_event.dart';
|
import 'package:stackwallet/services/event_bus/events/global/refresh_percent_changed_event.dart';
|
||||||
import 'package:stackwallet/services/event_bus/events/global/updated_in_background_event.dart';
|
import 'package:stackwallet/services/event_bus/events/global/updated_in_background_event.dart';
|
||||||
import 'package:stackwallet/services/event_bus/events/global/wallet_sync_status_changed_event.dart';
|
import 'package:stackwallet/services/event_bus/events/global/wallet_sync_status_changed_event.dart';
|
||||||
import 'package:stackwallet/services/event_bus/global_event_bus.dart';
|
import 'package:stackwallet/services/event_bus/global_event_bus.dart';
|
||||||
|
import 'package:stackwallet/services/mixins/electrum_x_parsing.dart';
|
||||||
import 'package:stackwallet/services/mixins/wallet_cache.dart';
|
import 'package:stackwallet/services/mixins/wallet_cache.dart';
|
||||||
import 'package:stackwallet/services/mixins/wallet_db.dart';
|
import 'package:stackwallet/services/mixins/wallet_db.dart';
|
||||||
import 'package:stackwallet/services/node_service.dart';
|
import 'package:stackwallet/services/node_service.dart';
|
||||||
|
@ -135,7 +135,8 @@ bip32.BIP32 getBip32RootWrapper(Tuple2<String, NetworkType> args) {
|
||||||
return getBip32Root(args.item1, args.item2);
|
return getBip32Root(args.item1, args.item2);
|
||||||
}
|
}
|
||||||
|
|
||||||
class NamecoinWallet extends CoinServiceAPI with WalletCache, WalletDB {
|
class NamecoinWallet extends CoinServiceAPI
|
||||||
|
with WalletCache, WalletDB, ElectrumXParsing {
|
||||||
static const integrationTestFlag =
|
static const integrationTestFlag =
|
||||||
bool.fromEnvironment("IS_INTEGRATION_TEST");
|
bool.fromEnvironment("IS_INTEGRATION_TEST");
|
||||||
|
|
||||||
|
|
202
lib/services/mixins/electrum_x_parsing.dart
Normal file
202
lib/services/mixins/electrum_x_parsing.dart
Normal file
|
@ -0,0 +1,202 @@
|
||||||
|
import 'package:decimal/decimal.dart';
|
||||||
|
import 'package:stackwallet/models/isar/models/isar_models.dart';
|
||||||
|
import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
||||||
|
import 'package:stackwallet/utilities/format.dart';
|
||||||
|
import 'package:tuple/tuple.dart';
|
||||||
|
|
||||||
|
mixin ElectrumXParsing {
|
||||||
|
Future<Tuple4<Transaction, List<Output>, List<Input>, Address>>
|
||||||
|
parseTransaction(
|
||||||
|
Map<String, dynamic> txData,
|
||||||
|
dynamic electrumxClient,
|
||||||
|
List<Address> myAddresses,
|
||||||
|
Coin coin,
|
||||||
|
int minConfirms,
|
||||||
|
String walletId,
|
||||||
|
) async {
|
||||||
|
Set<String> receivingAddresses = myAddresses
|
||||||
|
.where((e) => e.subType == AddressSubType.receiving)
|
||||||
|
.map((e) => e.value)
|
||||||
|
.toSet();
|
||||||
|
Set<String> changeAddresses = myAddresses
|
||||||
|
.where((e) => e.subType == AddressSubType.change)
|
||||||
|
.map((e) => e.value)
|
||||||
|
.toSet();
|
||||||
|
|
||||||
|
Set<String> inputAddresses = {};
|
||||||
|
Set<String> outputAddresses = {};
|
||||||
|
|
||||||
|
int totalInputValue = 0;
|
||||||
|
int totalOutputValue = 0;
|
||||||
|
|
||||||
|
int amountSentFromWallet = 0;
|
||||||
|
int amountReceivedInWallet = 0;
|
||||||
|
int changeAmount = 0;
|
||||||
|
|
||||||
|
// parse inputs
|
||||||
|
for (final input in txData["vin"] as List) {
|
||||||
|
final prevTxid = input["txid"] as String;
|
||||||
|
final prevOut = input["vout"] as int;
|
||||||
|
|
||||||
|
// fetch input tx to get address
|
||||||
|
final inputTx = await electrumxClient.getTransaction(
|
||||||
|
txHash: prevTxid,
|
||||||
|
coin: coin,
|
||||||
|
);
|
||||||
|
|
||||||
|
for (final output in inputTx["vout"] as List) {
|
||||||
|
// check matching output
|
||||||
|
if (prevOut == output["n"]) {
|
||||||
|
// get value
|
||||||
|
final value = Format.decimalAmountToSatoshis(
|
||||||
|
Decimal.parse(output["value"].toString()),
|
||||||
|
coin,
|
||||||
|
);
|
||||||
|
|
||||||
|
// add value to total
|
||||||
|
totalInputValue += value;
|
||||||
|
|
||||||
|
// get input(prevOut) address
|
||||||
|
final address = output["scriptPubKey"]?["addresses"]?[0] as String? ??
|
||||||
|
output["scriptPubKey"]?["address"] as String?;
|
||||||
|
|
||||||
|
if (address != null) {
|
||||||
|
inputAddresses.add(address);
|
||||||
|
|
||||||
|
// if input was from my wallet, add value to amount sent
|
||||||
|
if (receivingAddresses.contains(address) ||
|
||||||
|
changeAddresses.contains(address)) {
|
||||||
|
amountSentFromWallet += value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse outputs
|
||||||
|
for (final output in txData["vout"] as List) {
|
||||||
|
// get value
|
||||||
|
final value = Format.decimalAmountToSatoshis(
|
||||||
|
Decimal.parse(output["value"].toString()),
|
||||||
|
coin,
|
||||||
|
);
|
||||||
|
|
||||||
|
// add value to total
|
||||||
|
totalOutputValue += value;
|
||||||
|
|
||||||
|
// get output address
|
||||||
|
final address = output["scriptPubKey"]?["addresses"]?[0] as String? ??
|
||||||
|
output["scriptPubKey"]?["address"] as String?;
|
||||||
|
if (address != null) {
|
||||||
|
outputAddresses.add(address);
|
||||||
|
|
||||||
|
// if output was to my wallet, add value to amount received
|
||||||
|
if (receivingAddresses.contains(address)) {
|
||||||
|
amountReceivedInWallet += value;
|
||||||
|
} else if (changeAddresses.contains(address)) {
|
||||||
|
changeAmount += value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final mySentFromAddresses = [
|
||||||
|
...receivingAddresses.intersection(inputAddresses),
|
||||||
|
...changeAddresses.intersection(inputAddresses)
|
||||||
|
];
|
||||||
|
final myReceivedOnAddresses =
|
||||||
|
receivingAddresses.intersection(outputAddresses);
|
||||||
|
final myChangeReceivedOnAddresses =
|
||||||
|
changeAddresses.intersection(outputAddresses);
|
||||||
|
|
||||||
|
final fee = totalInputValue - totalOutputValue;
|
||||||
|
|
||||||
|
// this is the address initially used to fetch the txid
|
||||||
|
Address transactionAddress = txData["address"] as Address;
|
||||||
|
|
||||||
|
TransactionType type;
|
||||||
|
int amount;
|
||||||
|
if (mySentFromAddresses.isNotEmpty && myReceivedOnAddresses.isNotEmpty) {
|
||||||
|
// tx is sent to self
|
||||||
|
type = TransactionType.sentToSelf;
|
||||||
|
|
||||||
|
// should be 0
|
||||||
|
amount =
|
||||||
|
amountSentFromWallet - amountReceivedInWallet - fee - changeAmount;
|
||||||
|
} else if (mySentFromAddresses.isNotEmpty) {
|
||||||
|
// outgoing tx
|
||||||
|
type = TransactionType.outgoing;
|
||||||
|
amount = amountSentFromWallet - changeAmount - fee;
|
||||||
|
|
||||||
|
final possible =
|
||||||
|
outputAddresses.difference(myChangeReceivedOnAddresses).first;
|
||||||
|
|
||||||
|
if (transactionAddress.value != possible) {
|
||||||
|
transactionAddress = Address(
|
||||||
|
walletId: walletId,
|
||||||
|
value: possible,
|
||||||
|
derivationIndex: -1,
|
||||||
|
subType: AddressSubType.nonWallet,
|
||||||
|
type: AddressType.nonWallet,
|
||||||
|
publicKey: [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// incoming tx
|
||||||
|
type = TransactionType.incoming;
|
||||||
|
amount = amountReceivedInWallet;
|
||||||
|
}
|
||||||
|
|
||||||
|
final tx = Transaction(
|
||||||
|
walletId: walletId,
|
||||||
|
txid: txData["txid"] as String,
|
||||||
|
timestamp: txData["blocktime"] as int? ??
|
||||||
|
(DateTime.now().millisecondsSinceEpoch ~/ 1000),
|
||||||
|
type: type,
|
||||||
|
subType: TransactionSubType.none,
|
||||||
|
amount: amount,
|
||||||
|
fee: fee,
|
||||||
|
height: txData["height"] as int?,
|
||||||
|
isCancelled: false,
|
||||||
|
isLelantus: false,
|
||||||
|
slateId: null,
|
||||||
|
otherData: null,
|
||||||
|
);
|
||||||
|
|
||||||
|
List<Output> outs = [];
|
||||||
|
List<Input> ins = [];
|
||||||
|
|
||||||
|
for (final json in txData["vin"] as List) {
|
||||||
|
bool isCoinBase = json['coinbase'] != null;
|
||||||
|
final input = Input(
|
||||||
|
walletId: walletId,
|
||||||
|
txid: json['txid'] as String,
|
||||||
|
vout: json['vout'] as int? ?? -1,
|
||||||
|
scriptSig: json['scriptSig']?['hex'] as String?,
|
||||||
|
scriptSigAsm: json['scriptSig']?['asm'] as String?,
|
||||||
|
isCoinbase: isCoinBase ? isCoinBase : json['is_coinbase'] as bool?,
|
||||||
|
sequence: json['sequence'] as int?,
|
||||||
|
innerRedeemScriptAsm: json['innerRedeemscriptAsm'] as String?,
|
||||||
|
);
|
||||||
|
ins.add(input);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (final json in txData["vout"] as List) {
|
||||||
|
final output = Output(
|
||||||
|
walletId: walletId,
|
||||||
|
scriptPubKey: json['scriptPubKey']?['hex'] as String?,
|
||||||
|
scriptPubKeyAsm: json['scriptPubKey']?['asm'] as String?,
|
||||||
|
scriptPubKeyType: json['scriptPubKey']?['type'] as String?,
|
||||||
|
scriptPubKeyAddress:
|
||||||
|
json["scriptPubKey"]?["addresses"]?[0] as String? ??
|
||||||
|
json['scriptPubKey']['type'] as String,
|
||||||
|
value: Format.decimalAmountToSatoshis(
|
||||||
|
Decimal.parse(json["value"].toString()),
|
||||||
|
coin,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
outs.add(output);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Tuple4(tx, outs, ins, transactionAddress);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue