import 'package:flutter/foundation.dart'; import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin; import 'package:bitcoin_flutter/src/payments/index.dart' show PaymentData; import 'package:cake_wallet/bitcoin/bitcoin_address_record.dart'; import 'package:cake_wallet/bitcoin/bitcoin_amount_format.dart'; import 'package:cake_wallet/entities/transaction_direction.dart'; import 'package:cake_wallet/entities/transaction_info.dart'; import 'package:cake_wallet/entities/format_amount.dart'; class BitcoinTransactionInfo extends TransactionInfo { BitcoinTransactionInfo( {@required String id, @required int height, @required int amount, @required TransactionDirection direction, @required bool isPending, @required DateTime date, @required int confirmations}) { this.id = id; this.height = height; this.amount = amount; this.direction = direction; this.date = date; this.isPending = isPending; this.confirmations = confirmations; } factory BitcoinTransactionInfo.fromElectrumVerbose(Map obj, {@required List addresses, @required int height}) { final addressesSet = addresses.map((addr) => addr.address).toSet(); final id = obj['txid'] as String; final vins = obj['vin'] as List ?? []; final vout = (obj['vout'] as List ?? []); final date = obj['time'] is int ? DateTime.fromMillisecondsSinceEpoch((obj['time'] as int) * 1000) : DateTime.now(); final confirmations = obj['confirmations'] as int ?? 0; var direction = TransactionDirection.incoming; for (dynamic vin in vins) { final vout = vin['vout'] as int; final out = vin['tx']['vout'][vout] as Map; final outAddresses = (out['scriptPubKey']['addresses'] as List)?.toSet(); if (outAddresses?.intersection(addressesSet)?.isNotEmpty ?? false) { direction = TransactionDirection.outgoing; break; } } final amount = vout.fold(0, (int acc, dynamic out) { final outAddresses = out['scriptPubKey']['addresses'] as List ?? []; final ntrs = outAddresses.toSet().intersection(addressesSet); var amount = acc; if ((direction == TransactionDirection.incoming && ntrs.isNotEmpty) || (direction == TransactionDirection.outgoing && ntrs.isEmpty)) { amount += doubleToBitcoinAmount(out['value'] as double ?? 0.0); } return amount; }); return BitcoinTransactionInfo( id: id, height: height, isPending: false, direction: direction, amount: amount, date: date, confirmations: confirmations); } factory BitcoinTransactionInfo.fromHexAndHeader(String hex, {List addresses, int height, int timestamp, int confirmations}) { final tx = bitcoin.Transaction.fromHex(hex); var exist = false; var amount = 0; if (addresses != null) { tx.outs.forEach((out) { try { final p2pkh = bitcoin.P2PKH( data: PaymentData(output: out.script), network: bitcoin.bitcoin); exist = addresses.contains(p2pkh.data.address); if (exist) { amount += out.value; } } catch (_) {} }); } final date = timestamp != null ? DateTime.fromMillisecondsSinceEpoch(timestamp * 1000) : DateTime.now(); return BitcoinTransactionInfo( id: tx.getId(), height: height, isPending: false, direction: TransactionDirection.incoming, amount: amount, date: date, confirmations: confirmations); } factory BitcoinTransactionInfo.fromJson(Map data) { return BitcoinTransactionInfo( id: data['id'] as String, height: data['height'] as int, amount: data['amount'] as int, direction: parseTransactionDirectionFromInt(data['direction'] as int), date: DateTime.fromMillisecondsSinceEpoch(data['date'] as int), isPending: data['isPending'] as bool, confirmations: data['confirmations'] as int); } String _fiatAmount; @override String amountFormatted() => '${formatAmount(bitcoinAmountToString(amount: amount))} BTC'; @override String fiatAmount() => _fiatAmount ?? ''; @override void changeFiatAmount(String amount) => _fiatAmount = formatAmount(amount); Map toJson() { final m = {}; m['id'] = id; m['height'] = height; m['amount'] = amount; m['direction'] = direction.index; m['date'] = date.millisecondsSinceEpoch; m['isPending'] = isPending; m['confirmations'] = confirmations; return m; } }