token transaction display fixes

This commit is contained in:
julian 2024-01-11 12:12:31 -06:00
parent 90cc8ced9c
commit 46d454fad1
6 changed files with 115 additions and 55 deletions

View file

@ -6,7 +6,6 @@ import 'package:stackwallet/models/isar/models/blockchain_data/transaction.dart'
import 'package:stackwallet/models/isar/models/blockchain_data/v2/input_v2.dart';
import 'package:stackwallet/models/isar/models/blockchain_data/v2/output_v2.dart';
import 'package:stackwallet/utilities/amount/amount.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/extensions/extensions.dart';
import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/spark_interface.dart';
@ -79,7 +78,7 @@ class TransactionV2 {
return confirmations >= minimumConfirms;
}
Amount getFee({required Coin coin}) {
Amount getFee({required int fractionDigits}) {
// check for override fee
final fee = _getOverrideFee();
if (fee != null) {
@ -92,44 +91,47 @@ class TransactionV2 {
.map((e) => e.value)
.reduce((value, element) => value += element);
return Amount(rawValue: inSum - outSum, fractionDigits: coin.decimals);
return Amount(rawValue: inSum - outSum, fractionDigits: fractionDigits);
}
Amount getAmountReceivedInThisWallet({required Coin coin}) {
Amount getAmountReceivedInThisWallet({required int fractionDigits}) {
final outSum = outputs
.where((e) => e.walletOwns)
.fold(BigInt.zero, (p, e) => p + e.value);
return Amount(rawValue: outSum, fractionDigits: coin.decimals);
return Amount(rawValue: outSum, fractionDigits: fractionDigits);
}
Amount getAmountSparkSelfMinted({required Coin coin}) {
Amount getAmountSparkSelfMinted({required int fractionDigits}) {
final outSum = outputs.where((e) {
final op = e.scriptPubKeyHex.substring(0, 2).toUint8ListFromHex.first;
return e.walletOwns && (op == OP_SPARKMINT);
}).fold(BigInt.zero, (p, e) => p + e.value);
return Amount(rawValue: outSum, fractionDigits: coin.decimals);
return Amount(rawValue: outSum, fractionDigits: fractionDigits);
}
Amount getAmountSentFromThisWallet({required Coin coin}) {
Amount getAmountSentFromThisWallet({required int fractionDigits}) {
final inSum = inputs
.where((e) => e.walletOwns)
.fold(BigInt.zero, (p, e) => p + e.value);
final amount = Amount(
Amount amount = Amount(
rawValue: inSum,
fractionDigits: coin.decimals,
fractionDigits: fractionDigits,
) -
getAmountReceivedInThisWallet(
coin: coin,
) -
getFee(coin: coin);
fractionDigits: fractionDigits,
);
if (subType != TransactionSubType.ethToken) {
amount = amount - getFee(fractionDigits: fractionDigits);
}
// negative amounts are likely an error or can happen with coins such as eth
// that don't use the btc style inputs/outputs
if (amount.raw < BigInt.zero) {
return Amount.zeroWith(fractionDigits: coin.decimals);
return Amount.zeroWith(fractionDigits: fractionDigits);
}
return amount;

View file

@ -843,6 +843,9 @@ class _DesktopTransactionCardRowState
late final TransactionV2 _transaction;
late final String walletId;
late final int minConfirms;
late final EthContract? ethContract;
bool get isTokenTx => ethContract != null;
String whatIsIt(TransactionV2 tx, int height) => tx.statusLabel(
currentChainHeight: height,
@ -858,6 +861,15 @@ class _DesktopTransactionCardRowState
.cryptoCurrency
.minConfirms;
_transaction = widget.transaction;
if (_transaction.subType == TransactionSubType.ethToken) {
ethContract = ref
.read(mainDBProvider)
.getEthContractSync(_transaction.contractAddress!);
} else {
ethContract == null;
}
super.initState();
}
@ -892,19 +904,22 @@ class _DesktopTransactionCardRowState
final currentHeight = ref.watch(pWalletChainHeight(walletId));
final Amount amount;
final fractionDigits = ethContract?.decimals ?? coin.decimals;
if (_transaction.subType == TransactionSubType.cashFusion) {
amount = _transaction.getAmountReceivedInThisWallet(coin: coin);
amount = _transaction.getAmountReceivedInThisWallet(
fractionDigits: fractionDigits);
} else {
switch (_transaction.type) {
case TransactionType.outgoing:
amount = _transaction.getAmountSentFromThisWallet(coin: coin);
amount = _transaction.getAmountSentFromThisWallet(
fractionDigits: fractionDigits);
break;
case TransactionType.incoming:
case TransactionType.sentToSelf:
if (_transaction.subType == TransactionSubType.sparkMint) {
amount = _transaction.getAmountSparkSelfMinted(coin: coin);
amount = _transaction.getAmountSparkSelfMinted(
fractionDigits: fractionDigits);
} else if (_transaction.subType == TransactionSubType.sparkSpend) {
final changeAddress =
(ref.watch(pWallets).getWallet(walletId) as SparkInterface)
@ -917,12 +932,14 @@ class _DesktopTransactionCardRowState
fractionDigits: coin.decimals,
);
} else {
amount = _transaction.getAmountReceivedInThisWallet(coin: coin);
amount = _transaction.getAmountReceivedInThisWallet(
fractionDigits: fractionDigits);
}
break;
case TransactionType.unknown:
amount = _transaction.getAmountSentFromThisWallet(coin: coin);
amount = _transaction.getAmountSentFromThisWallet(
fractionDigits: fractionDigits);
break;
}
}
@ -1005,7 +1022,7 @@ class _DesktopTransactionCardRowState
Expanded(
flex: 6,
child: Text(
"$prefix${ref.watch(pAmountFormatter(coin)).format(amount)}",
"$prefix${ref.watch(pAmountFormatter(coin)).format(amount, ethContract: ethContract)}",
style:
STextStyles.desktopTextExtraExtraSmall(context).copyWith(
color: Theme.of(context).extension<StackColors>()!.textDark,

View file

@ -112,18 +112,23 @@ class _TransactionCardStateV2 extends ConsumerState<TransactionCardV2> {
final Amount amount;
final fractionDigits = tokenContract?.decimals ?? coin.decimals;
if (_transaction.subType == TransactionSubType.cashFusion) {
amount = _transaction.getAmountReceivedInThisWallet(coin: coin);
amount = _transaction.getAmountReceivedInThisWallet(
fractionDigits: fractionDigits);
} else {
switch (_transaction.type) {
case TransactionType.outgoing:
amount = _transaction.getAmountSentFromThisWallet(coin: coin);
amount = _transaction.getAmountSentFromThisWallet(
fractionDigits: fractionDigits);
break;
case TransactionType.incoming:
case TransactionType.sentToSelf:
if (_transaction.subType == TransactionSubType.sparkMint) {
amount = _transaction.getAmountSparkSelfMinted(coin: coin);
amount = _transaction.getAmountSparkSelfMinted(
fractionDigits: fractionDigits);
} else if (_transaction.subType == TransactionSubType.sparkSpend) {
final changeAddress =
(ref.watch(pWallets).getWallet(walletId) as SparkInterface)
@ -136,12 +141,14 @@ class _TransactionCardStateV2 extends ConsumerState<TransactionCardV2> {
fractionDigits: coin.decimals,
);
} else {
amount = _transaction.getAmountReceivedInThisWallet(coin: coin);
amount = _transaction.getAmountReceivedInThisWallet(
fractionDigits: fractionDigits);
}
break;
case TransactionType.unknown:
amount = _transaction.getAmountSentFromThisWallet(coin: coin);
amount = _transaction.getAmountSentFromThisWallet(
fractionDigits: fractionDigits);
break;
}
}

View file

@ -17,11 +17,13 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:stackwallet/models/isar/models/blockchain_data/transaction.dart';
import 'package:stackwallet/models/isar/models/blockchain_data/v2/transaction_v2.dart';
import 'package:stackwallet/models/isar/models/ethereum/eth_contract.dart';
import 'package:stackwallet/notifications/show_flush_bar.dart';
import 'package:stackwallet/pages/wallet_view/sub_widgets/tx_icon.dart';
import 'package:stackwallet/pages/wallet_view/transaction_views/dialogs/cancelling_transaction_progress_dialog.dart';
import 'package:stackwallet/pages/wallet_view/transaction_views/edit_note_view.dart';
import 'package:stackwallet/pages/wallet_view/wallet_view.dart';
import 'package:stackwallet/providers/db/main_db_provider.dart';
import 'package:stackwallet/providers/global/address_book_service_provider.dart';
import 'package:stackwallet/providers/providers.dart';
import 'package:stackwallet/themes/stack_colors.dart';
@ -84,6 +86,9 @@ class _TransactionV2DetailsViewState
late final String amountPrefix;
late final String unit;
late final int minConfirms;
late final EthContract? ethContract;
bool get isTokenTx => ethContract != null;
late final List<({List<String> addresses, Amount amount})> data;
@ -96,10 +101,24 @@ class _TransactionV2DetailsViewState
walletId = widget.walletId;
coin = widget.coin;
if (_transaction.subType == TransactionSubType.ethToken) {
ethContract = ref
.read(mainDBProvider)
.getEthContractSync(_transaction.contractAddress!);
unit = ethContract!.symbol;
} else {
ethContract == null;
unit = coin.ticker;
}
minConfirms =
ref.read(pWallets).getWallet(walletId).cryptoCurrency.minConfirms;
fee = _transaction.getFee(coin: coin);
final fractionDigits = ethContract?.decimals ?? coin.decimals;
fee = _transaction.getFee(fractionDigits: fractionDigits);
if (_transaction.subType == TransactionSubType.cashFusion ||
_transaction.type == TransactionType.sentToSelf) {
@ -108,18 +127,18 @@ class _TransactionV2DetailsViewState
amountPrefix = _transaction.type == TransactionType.outgoing ? "-" : "+";
}
unit = coin.ticker;
if (_transaction.isEpiccashTransaction) {
switch (_transaction.type) {
case TransactionType.outgoing:
case TransactionType.unknown:
amount = _transaction.getAmountSentFromThisWallet(coin: coin);
amount = _transaction.getAmountSentFromThisWallet(
fractionDigits: fractionDigits);
break;
case TransactionType.incoming:
case TransactionType.sentToSelf:
amount = _transaction.getAmountReceivedInThisWallet(coin: coin);
amount = _transaction.getAmountReceivedInThisWallet(
fractionDigits: fractionDigits);
break;
}
data = _transaction.outputs
@ -129,7 +148,8 @@ class _TransactionV2DetailsViewState
))
.toList();
} else if (_transaction.subType == TransactionSubType.cashFusion) {
amount = _transaction.getAmountReceivedInThisWallet(coin: coin);
amount = _transaction.getAmountReceivedInThisWallet(
fractionDigits: fractionDigits);
data = _transaction.outputs
.where((e) => e.walletOwns)
.map((e) => (
@ -140,7 +160,8 @@ class _TransactionV2DetailsViewState
} else {
switch (_transaction.type) {
case TransactionType.outgoing:
amount = _transaction.getAmountSentFromThisWallet(coin: coin);
amount = _transaction.getAmountSentFromThisWallet(
fractionDigits: fractionDigits);
data = _transaction.outputs
.where((e) => !e.walletOwns)
.map((e) => (
@ -154,7 +175,8 @@ class _TransactionV2DetailsViewState
case TransactionType.incoming:
case TransactionType.sentToSelf:
if (_transaction.subType == TransactionSubType.sparkMint) {
amount = _transaction.getAmountSparkSelfMinted(coin: coin);
amount = _transaction.getAmountSparkSelfMinted(
fractionDigits: fractionDigits);
} else if (_transaction.subType == TransactionSubType.sparkSpend) {
final changeAddress =
(ref.read(pWallets).getWallet(walletId) as SparkInterface)
@ -167,7 +189,8 @@ class _TransactionV2DetailsViewState
fractionDigits: coin.decimals,
);
} else {
amount = _transaction.getAmountReceivedInThisWallet(coin: coin);
amount = _transaction.getAmountReceivedInThisWallet(
fractionDigits: fractionDigits);
}
data = _transaction.outputs
.where((e) => e.walletOwns)
@ -180,7 +203,8 @@ class _TransactionV2DetailsViewState
break;
case TransactionType.unknown:
amount = _transaction.getAmountSentFromThisWallet(coin: coin);
amount = _transaction.getAmountSentFromThisWallet(
fractionDigits: fractionDigits);
data = _transaction.inputs
.where((e) => e.walletOwns)
.map((e) => (
@ -515,7 +539,7 @@ class _TransactionV2DetailsViewState
: CrossAxisAlignment.start,
children: [
SelectableText(
"$amountPrefix${ref.watch(pAmountFormatter(coin)).format(amount)}",
"$amountPrefix${ref.watch(pAmountFormatter(coin)).format(amount, ethContract: ethContract)}",
style: isDesktop
? STextStyles
.desktopTextExtraExtraSmall(

View file

@ -105,21 +105,6 @@ class EthereumWallet extends Bip39Wallet with PrivateKeyInterface {
await mainDB.updateOrPutAddresses([address]);
}
// delete
@override
Future<Address> getCurrentReceivingAddress() async {
return Address(
walletId: walletId,
value:
checksumEthereumAddress("0x6Cc3006944070B32D80107D51d843a66EaC00686"),
publicKey: [], // maybe store address bytes here? seems a waste of space though
derivationIndex: 0,
derivationPath: DerivationPath()..value = "$hdPathEthereum/0",
type: AddressType.ethereum,
subType: AddressSubType.receiving,
);
}
// ==================== Overrides ============================================
@override

View file

@ -407,13 +407,17 @@ class EthTokenWallet extends Wallet {
for (final tuple in data) {
// ignore all non Transfer events (for now)
if (tuple.tx.topics[0] == kTransferEventSignature) {
final Amount txFee = tuple.extra.gasUsed * tuple.extra.gasPrice;
final amount = Amount(
rawValue: tuple.tx.data.toBigIntFromHex,
fractionDigits: tokenContract.decimals,
);
if (amount.raw == BigInt.zero) {
// probably don't need to show this
continue;
}
final Amount txFee = tuple.extra.gasUsed * tuple.extra.gasPrice;
final addressFrom = _addressFromTopic(
tuple.tx.topics[1],
);
@ -451,7 +455,28 @@ class EthTokenWallet extends Wallet {
final List<OutputV2> outputs = [];
final List<InputV2> inputs = [];
// TODO: ins outs
OutputV2 output = OutputV2.isarCantDoRequiredInDefaultConstructor(
scriptPubKeyHex: "00",
valueStringSats: amount.raw.toString(),
addresses: [
addressTo,
],
walletOwns: addressTo == addressString,
);
InputV2 input = InputV2.isarCantDoRequiredInDefaultConstructor(
scriptSigHex: null,
sequence: null,
outpoint: null,
addresses: [addressFrom],
valueStringSats: amount.raw.toString(),
witness: null,
innerRedeemScriptAsm: null,
coinbase: null,
walletOwns: addressFrom == addressString,
);
outputs.add(output);
inputs.add(input);
final txn = TransactionV2(
walletId: walletId,