Erc20 token transactions are displaying incorrectly (#1493)
* evm signature name * hide depositWithExpiry and transfer transactions * Update contact_list_view_model.dart * remove erc20 token history when disabled
Serhii committed
Jun 20, 2024 at 23:13 UTC
1690f6af1edc26c025cf07e5f21eb5af1725de99
13 files changed
+79
-7
cw_core/lib/transaction_info.dart
+1
@@ -16,6 +16,7 @@ abstract class TransactionInfo extends Object with Keyable {
16
void changeFiatAmount(String amount);
17
String? to;
18
String? from;
19
+ String? evmSignatureName;
20
List<String>? inputAddresses;
21
List<String>? outputAddresses;
22
cw_ethereum/lib/ethereum_client.dart
+5
@@ -29,6 +29,11 @@ class EthereumClient extends EVMChainClient {
29
30
final jsonResponse = json.decode(response.body) as Map<String, dynamic>;
31
32
+ if (jsonResponse['result'] is String) {
33
+ log(jsonResponse['result']);
34
+ return [];
35
+ }
36
+
37
if (response.statusCode >= 200 && response.statusCode < 300 && jsonResponse['status'] != 0) {
38
return (jsonResponse['result'] as List)
39
.map((e) => EVMChainTransactionModel.fromJson(e as Map<String, dynamic>, 'ETH'))
cw_ethereum/lib/ethereum_transaction_info.dart
+2
@@ -14,6 +14,7 @@ class EthereumTransactionInfo extends EVMChainTransactionInfo {
14
required super.confirmations,
15
required super.to,
16
required super.from,
17
+ super.evmSignatureName,
18
super.exponent,
19
});
20
@@ -31,6 +32,7 @@ class EthereumTransactionInfo extends EVMChainTransactionInfo {
32
tokenSymbol: data['tokenSymbol'] as String,
33
to: data['to'],
34
from: data['from'],
35
+ evmSignatureName: data['evmSignatureName'],
36
);
37
}
38
cw_ethereum/lib/ethereum_wallet.dart
+1
@@ -94,6 +94,7 @@ class EthereumWallet extends EVMChainWallet {
94
tokenSymbol: transactionModel.tokenSymbol ?? "ETH",
95
to: transactionModel.to,
96
from: transactionModel.from,
97
+ evmSignatureName: transactionModel.evmSignatureName,
98
);
99
return model;
100
}
cw_evm/lib/evm_chain_transaction_info.dart
+3
@@ -20,6 +20,7 @@ abstract class EVMChainTransactionInfo extends TransactionInfo {
20
required this.confirmations,
21
required this.to,
22
required this.from,
23
+ this.evmSignatureName,
24
}) : amount = ethAmount.toInt(),
25
fee = ethFee.toInt();
26
@@ -38,6 +39,7 @@ abstract class EVMChainTransactionInfo extends TransactionInfo {
39
String? _fiatAmount;
40
final String? to;
41
final String? from;
42
+ final String? evmSignatureName;
43
44
//! Getter to be overridden in child classes
45
String get feeCurrency;
@@ -73,5 +75,6 @@ abstract class EVMChainTransactionInfo extends TransactionInfo {
75
'tokenSymbol': tokenSymbol,
76
'to': to,
77
'from': from,
78
+ 'evmSignatureName': evmSignatureName,
79
};
80
}
cw_evm/lib/evm_chain_transaction_model.dart
+6
@@ -12,6 +12,8 @@ class EVMChainTransactionModel {
12
final String? tokenSymbol;
13
final int? tokenDecimal;
14
final bool isError;
15
+ final String input;
16
+ String? evmSignatureName;
17
18
EVMChainTransactionModel({
19
required this.date,
@@ -27,6 +29,8 @@ class EVMChainTransactionModel {
29
required this.tokenSymbol,
30
required this.tokenDecimal,
31
required this.isError,
32
+ required this.input,
33
+ this.evmSignatureName,
34
});
35
36
factory EVMChainTransactionModel.fromJson(Map<String, dynamic> json, String defaultSymbol) =>
@@ -44,5 +48,7 @@ class EVMChainTransactionModel {
48
tokenSymbol: json["tokenSymbol"] ?? defaultSymbol,
49
tokenDecimal: int.tryParse(json["tokenDecimal"] ?? ""),
50
isError: json["isError"] == "1",
51
+ input: json["input"] ?? "",
52
+ evmSignatureName: json["evmSignatureName"],
53
);
54
}
cw_evm/lib/evm_chain_wallet.dart
+41
-5
@@ -39,6 +39,20 @@ import 'evm_erc20_balance.dart';
39
40
part 'evm_chain_wallet.g.dart';
41
42
+const Map<String, String> methodSignatureToType = {
43
+ '0x095ea7b3': 'approval',
44
+ '0xa9059cbb': 'transfer',
45
+ '0x23b872dd': 'transferFrom',
46
+ '0x574da717': 'transferOut',
47
+ '0x2e1a7d4d': 'withdraw',
48
+ '0x7ff36ab5': 'swapExactETHForTokens',
49
+ '0x40c10f19': 'mint',
50
+ '0x44bc937b': 'depositWithExpiry',
51
+ '0xd0e30db0': 'deposit',
52
+ '0xe8e33700': 'addLiquidity',
53
+ '0xd505accf': 'permit',
54
+};
55
+
56
abstract class EVMChainWallet = EVMChainWalletBase with _$EVMChainWallet;
57
58
abstract class EVMChainWalletBase
@@ -235,7 +249,8 @@ abstract class EVMChainWalletBase
249
250
String? hexOpReturnMemo;
251
if (opReturnMemo != null) {
238
- hexOpReturnMemo = '0x${opReturnMemo.codeUnits.map((char) => char.toRadixString(16).padLeft(2, '0')).join()}';
252
+ hexOpReturnMemo =
253
+ '0x${opReturnMemo.codeUnits.map((char) => char.toRadixString(16).padLeft(2, '0')).join()}';
254
}
255
256
final CryptoCurrency transactionCurrency =
@@ -337,11 +352,21 @@ abstract class EVMChainWalletBase
352
353
@override
354
Future<Map<String, EVMChainTransactionInfo>> fetchTransactions() async {
355
+ final List<EVMChainTransactionModel> transactions = [];
356
+ final List<Future<List<EVMChainTransactionModel>>> erc20TokensTransactions = [];
357
+
358
final address = _evmChainPrivateKey.address.hex;
341
- final transactions = await _client.fetchTransactions(address);
359
+ final externalTransactions = await _client.fetchTransactions(address);
360
final internalTransactions = await _client.fetchInternalTransactions(address);
361
344
- final List<Future<List<EVMChainTransactionModel>>> erc20TokensTransactions = [];
362
+ for (var transaction in externalTransactions) {
363
+ final evmSignatureName = analyzeTransaction(transaction.input);
364
+
365
+ if (evmSignatureName != 'depositWithExpiry' && evmSignatureName != 'transfer') {
366
+ transaction.evmSignatureName = evmSignatureName;
367
+ transactions.add(transaction);
368
+ }
369
+ }
370
371
for (var token in balance.keys) {
372
if (token is Erc20Token) {
@@ -369,6 +394,17 @@ abstract class EVMChainWalletBase
394
return result;
395
}
396
397
+ String? analyzeTransaction(String? transactionInput) {
398
+ if (transactionInput == '0x' || transactionInput == null || transactionInput.isEmpty) {
399
+ return 'simpleTransfer';
400
+ }
401
+
402
+ final methodSignature =
403
+ transactionInput.length >= 10 ? transactionInput.substring(0, 10) : null;
404
+
405
+ return methodSignatureToType[methodSignature];
406
+ }
407
+
408
@override
409
Object get keys => throw UnimplementedError("keys");
410
@@ -482,11 +518,11 @@ abstract class EVMChainWalletBase
518
await token.delete();
519
520
balance.remove(token);
485
- await _removeTokenTransactionsInHistory(token);
521
+ await removeTokenTransactionsInHistory(token);
522
_updateBalance();
523
}
524
489
- Future<void> _removeTokenTransactionsInHistory(Erc20Token token) async {
525
+ Future<void> removeTokenTransactionsInHistory(Erc20Token token) async {
526
transactionHistory.transactions.removeWhere((key, value) => value.tokenSymbol == token.title);
527
await transactionHistory.save();
528
}
lib/ethereum/cw_ethereum.dart
+4
@@ -137,6 +137,10 @@ class CWEthereum extends Ethereum {
137
Future<void> deleteErc20Token(WalletBase wallet, CryptoCurrency token) async =>
138
await (wallet as EthereumWallet).deleteErc20Token(token as Erc20Token);
139
140
+ @override
141
+ Future<void> removeTokenTransactionsInHistory(WalletBase wallet, CryptoCurrency token) async =>
142
+ await (wallet as EthereumWallet).removeTokenTransactionsInHistory(token as Erc20Token);
143
+
144
@override
145
Future<Erc20Token?> getErc20Token(WalletBase wallet, String contractAddress) async {
146
final ethereumWallet = wallet as EthereumWallet;
lib/polygon/cw_polygon.dart
+4
@@ -135,6 +135,10 @@ class CWPolygon extends Polygon {
135
Future<void> deleteErc20Token(WalletBase wallet, CryptoCurrency token) async =>
136
await (wallet as PolygonWallet).deleteErc20Token(token as Erc20Token);
137
138
+ @override
139
+ Future<void> removeTokenTransactionsInHistory(WalletBase wallet, CryptoCurrency token) async =>
140
+ await (wallet as PolygonWallet).removeTokenTransactionsInHistory(token as Erc20Token);
141
+
142
@override
143
Future<Erc20Token?> getErc20Token(WalletBase wallet, String contractAddress) async {
144
final polygonWallet = wallet as PolygonWallet;
lib/src/screens/dashboard/pages/transactions_page.dart
+7
-1
@@ -8,6 +8,7 @@ import 'package:cake_wallet/view_model/dashboard/anonpay_transaction_list_item.d
8
import 'package:cake_wallet/view_model/dashboard/order_list_item.dart';
9
import 'package:cw_core/crypto_currency.dart';
10
import 'package:cw_core/sync_status.dart';
11
+import 'package:cw_core/wallet_type.dart';
12
import 'package:flutter/material.dart';
13
import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart';
14
import 'package:flutter_mobx/flutter_mobx.dart';
@@ -87,6 +88,10 @@ class TransactionsPage extends StatelessWidget {
88
}
89
90
final transaction = item.transaction;
91
+ final transactionType = dashboardViewModel.type == WalletType.ethereum &&
92
+ transaction.evmSignatureName == 'approval'
93
+ ? ' (${transaction.evmSignatureName})'
94
+ : '';
95
96
return Observer(
97
builder: (_) => TransactionRow(
@@ -100,7 +105,8 @@ class TransactionsPage extends StatelessWidget {
105
? ''
106
: item.formattedFiatAmount,
107
isPending: transaction.isPending,
103
- title: item.formattedTitle + item.formattedStatus,
108
+ title: item.formattedTitle +
109
+ item.formattedStatus + ' $transactionType',
110
),
111
);
112
}
lib/view_model/contact_list/contact_list_view_model.dart
+1
-1
@@ -39,7 +39,7 @@ abstract class ContactListViewModelBase with Store {
39
));
40
}
41
});
42
- } else if (info.addresses?.isNotEmpty == true) {
42
+ } else if (info.addresses?.isNotEmpty == true && info.addresses!.length > 1) {
43
info.addresses!.forEach((address, label) {
44
if (label.isEmpty) {
45
return;
lib/view_model/dashboard/home_settings_view_model.dart
+2
@@ -144,10 +144,12 @@ abstract class HomeSettingsViewModelBase with Store {
144
145
if (_balanceViewModel.wallet.type == WalletType.ethereum) {
146
ethereum!.addErc20Token(_balanceViewModel.wallet, token as Erc20Token);
147
+ if (!value) ethereum!.removeTokenTransactionsInHistory(_balanceViewModel.wallet, token);
148
}
149
150
if (_balanceViewModel.wallet.type == WalletType.polygon) {
151
polygon!.addErc20Token(_balanceViewModel.wallet, token as Erc20Token);
152
+ if (!value) polygon!.removeTokenTransactionsInHistory(_balanceViewModel.wallet, token);
153
}
154
155
if (_balanceViewModel.wallet.type == WalletType.solana) {
tool/configure.dart
+2
@@ -667,6 +667,7 @@ abstract class Ethereum {
667
List<Erc20Token> getERC20Currencies(WalletBase wallet);
668
Future<void> addErc20Token(WalletBase wallet, CryptoCurrency token);
669
Future<void> deleteErc20Token(WalletBase wallet, CryptoCurrency token);
670
+ Future<void> removeTokenTransactionsInHistory(WalletBase wallet, CryptoCurrency token);
671
Future<Erc20Token?> getErc20Token(WalletBase wallet, String contractAddress);
672
673
CryptoCurrency assetOfTransaction(WalletBase wallet, TransactionInfo transaction);
@@ -770,6 +771,7 @@ abstract class Polygon {
771
List<Erc20Token> getERC20Currencies(WalletBase wallet);
772
Future<void> addErc20Token(WalletBase wallet, CryptoCurrency token);
773
Future<void> deleteErc20Token(WalletBase wallet, CryptoCurrency token);
774
+ Future<void> removeTokenTransactionsInHistory(WalletBase wallet, CryptoCurrency token);
775
Future<Erc20Token?> getErc20Token(WalletBase wallet, String contractAddress);
776
777
CryptoCurrency assetOfTransaction(WalletBase wallet, TransactionInfo transaction);