diff --git a/cw_core/lib/transaction_info.dart b/cw_core/lib/transaction_info.dart index 992582ff8..7b02bf1ff 100644 --- a/cw_core/lib/transaction_info.dart +++ b/cw_core/lib/transaction_info.dart @@ -16,6 +16,7 @@ abstract class TransactionInfo extends Object with Keyable { void changeFiatAmount(String amount); String? to; String? from; + String? evmSignatureName; List? inputAddresses; List? outputAddresses; diff --git a/cw_ethereum/lib/ethereum_client.dart b/cw_ethereum/lib/ethereum_client.dart index f2b25bcdd..9d50fdd5b 100644 --- a/cw_ethereum/lib/ethereum_client.dart +++ b/cw_ethereum/lib/ethereum_client.dart @@ -29,6 +29,11 @@ class EthereumClient extends EVMChainClient { final jsonResponse = json.decode(response.body) as Map; + if (jsonResponse['result'] is String) { + log(jsonResponse['result']); + return []; + } + if (response.statusCode >= 200 && response.statusCode < 300 && jsonResponse['status'] != 0) { return (jsonResponse['result'] as List) .map((e) => EVMChainTransactionModel.fromJson(e as Map, 'ETH')) diff --git a/cw_ethereum/lib/ethereum_transaction_info.dart b/cw_ethereum/lib/ethereum_transaction_info.dart index d5d3fea8d..7a62c0b6a 100644 --- a/cw_ethereum/lib/ethereum_transaction_info.dart +++ b/cw_ethereum/lib/ethereum_transaction_info.dart @@ -14,6 +14,7 @@ class EthereumTransactionInfo extends EVMChainTransactionInfo { required super.confirmations, required super.to, required super.from, + super.evmSignatureName, super.exponent, }); @@ -31,6 +32,7 @@ class EthereumTransactionInfo extends EVMChainTransactionInfo { tokenSymbol: data['tokenSymbol'] as String, to: data['to'], from: data['from'], + evmSignatureName: data['evmSignatureName'], ); } diff --git a/cw_ethereum/lib/ethereum_wallet.dart b/cw_ethereum/lib/ethereum_wallet.dart index 4604db662..2c58cd31d 100644 --- a/cw_ethereum/lib/ethereum_wallet.dart +++ b/cw_ethereum/lib/ethereum_wallet.dart @@ -94,6 +94,7 @@ class EthereumWallet extends EVMChainWallet { tokenSymbol: transactionModel.tokenSymbol ?? "ETH", to: transactionModel.to, from: transactionModel.from, + evmSignatureName: transactionModel.evmSignatureName, ); return model; } diff --git a/cw_evm/lib/evm_chain_transaction_info.dart b/cw_evm/lib/evm_chain_transaction_info.dart index 329061db2..fb6cf6b52 100644 --- a/cw_evm/lib/evm_chain_transaction_info.dart +++ b/cw_evm/lib/evm_chain_transaction_info.dart @@ -20,6 +20,7 @@ abstract class EVMChainTransactionInfo extends TransactionInfo { required this.confirmations, required this.to, required this.from, + this.evmSignatureName, }) : amount = ethAmount.toInt(), fee = ethFee.toInt(); @@ -38,6 +39,7 @@ abstract class EVMChainTransactionInfo extends TransactionInfo { String? _fiatAmount; final String? to; final String? from; + final String? evmSignatureName; //! Getter to be overridden in child classes String get feeCurrency; @@ -73,5 +75,6 @@ abstract class EVMChainTransactionInfo extends TransactionInfo { 'tokenSymbol': tokenSymbol, 'to': to, 'from': from, + 'evmSignatureName': evmSignatureName, }; } diff --git a/cw_evm/lib/evm_chain_transaction_model.dart b/cw_evm/lib/evm_chain_transaction_model.dart index dfdeab8f5..ca4fb0864 100644 --- a/cw_evm/lib/evm_chain_transaction_model.dart +++ b/cw_evm/lib/evm_chain_transaction_model.dart @@ -12,6 +12,8 @@ class EVMChainTransactionModel { final String? tokenSymbol; final int? tokenDecimal; final bool isError; + final String input; + String? evmSignatureName; EVMChainTransactionModel({ required this.date, @@ -27,6 +29,8 @@ class EVMChainTransactionModel { required this.tokenSymbol, required this.tokenDecimal, required this.isError, + required this.input, + this.evmSignatureName, }); factory EVMChainTransactionModel.fromJson(Map json, String defaultSymbol) => @@ -44,5 +48,7 @@ class EVMChainTransactionModel { tokenSymbol: json["tokenSymbol"] ?? defaultSymbol, tokenDecimal: int.tryParse(json["tokenDecimal"] ?? ""), isError: json["isError"] == "1", + input: json["input"] ?? "", + evmSignatureName: json["evmSignatureName"], ); } diff --git a/cw_evm/lib/evm_chain_wallet.dart b/cw_evm/lib/evm_chain_wallet.dart index 56b58d400..2adb54746 100644 --- a/cw_evm/lib/evm_chain_wallet.dart +++ b/cw_evm/lib/evm_chain_wallet.dart @@ -39,6 +39,20 @@ import 'evm_erc20_balance.dart'; part 'evm_chain_wallet.g.dart'; +const Map methodSignatureToType = { + '0x095ea7b3': 'approval', + '0xa9059cbb': 'transfer', + '0x23b872dd': 'transferFrom', + '0x574da717': 'transferOut', + '0x2e1a7d4d': 'withdraw', + '0x7ff36ab5': 'swapExactETHForTokens', + '0x40c10f19': 'mint', + '0x44bc937b': 'depositWithExpiry', + '0xd0e30db0': 'deposit', + '0xe8e33700': 'addLiquidity', + '0xd505accf': 'permit', +}; + abstract class EVMChainWallet = EVMChainWalletBase with _$EVMChainWallet; abstract class EVMChainWalletBase @@ -235,7 +249,8 @@ abstract class EVMChainWalletBase String? hexOpReturnMemo; if (opReturnMemo != null) { - hexOpReturnMemo = '0x${opReturnMemo.codeUnits.map((char) => char.toRadixString(16).padLeft(2, '0')).join()}'; + hexOpReturnMemo = + '0x${opReturnMemo.codeUnits.map((char) => char.toRadixString(16).padLeft(2, '0')).join()}'; } final CryptoCurrency transactionCurrency = @@ -337,11 +352,21 @@ abstract class EVMChainWalletBase @override Future> fetchTransactions() async { + final List transactions = []; + final List>> erc20TokensTransactions = []; + final address = _evmChainPrivateKey.address.hex; - final transactions = await _client.fetchTransactions(address); + final externalTransactions = await _client.fetchTransactions(address); final internalTransactions = await _client.fetchInternalTransactions(address); - final List>> erc20TokensTransactions = []; + for (var transaction in externalTransactions) { + final evmSignatureName = analyzeTransaction(transaction.input); + + if (evmSignatureName != 'depositWithExpiry' && evmSignatureName != 'transfer') { + transaction.evmSignatureName = evmSignatureName; + transactions.add(transaction); + } + } for (var token in balance.keys) { if (token is Erc20Token) { @@ -369,6 +394,17 @@ abstract class EVMChainWalletBase return result; } + String? analyzeTransaction(String? transactionInput) { + if (transactionInput == '0x' || transactionInput == null || transactionInput.isEmpty) { + return 'simpleTransfer'; + } + + final methodSignature = + transactionInput.length >= 10 ? transactionInput.substring(0, 10) : null; + + return methodSignatureToType[methodSignature]; + } + @override Object get keys => throw UnimplementedError("keys"); @@ -482,11 +518,11 @@ abstract class EVMChainWalletBase await token.delete(); balance.remove(token); - await _removeTokenTransactionsInHistory(token); + await removeTokenTransactionsInHistory(token); _updateBalance(); } - Future _removeTokenTransactionsInHistory(Erc20Token token) async { + Future removeTokenTransactionsInHistory(Erc20Token token) async { transactionHistory.transactions.removeWhere((key, value) => value.tokenSymbol == token.title); await transactionHistory.save(); } diff --git a/lib/ethereum/cw_ethereum.dart b/lib/ethereum/cw_ethereum.dart index e72108e79..7b593d58d 100644 --- a/lib/ethereum/cw_ethereum.dart +++ b/lib/ethereum/cw_ethereum.dart @@ -137,6 +137,10 @@ class CWEthereum extends Ethereum { Future deleteErc20Token(WalletBase wallet, CryptoCurrency token) async => await (wallet as EthereumWallet).deleteErc20Token(token as Erc20Token); + @override + Future removeTokenTransactionsInHistory(WalletBase wallet, CryptoCurrency token) async => + await (wallet as EthereumWallet).removeTokenTransactionsInHistory(token as Erc20Token); + @override Future getErc20Token(WalletBase wallet, String contractAddress) async { final ethereumWallet = wallet as EthereumWallet; diff --git a/lib/polygon/cw_polygon.dart b/lib/polygon/cw_polygon.dart index 16aba284d..2dcb1b4a6 100644 --- a/lib/polygon/cw_polygon.dart +++ b/lib/polygon/cw_polygon.dart @@ -135,6 +135,10 @@ class CWPolygon extends Polygon { Future deleteErc20Token(WalletBase wallet, CryptoCurrency token) async => await (wallet as PolygonWallet).deleteErc20Token(token as Erc20Token); + @override + Future removeTokenTransactionsInHistory(WalletBase wallet, CryptoCurrency token) async => + await (wallet as PolygonWallet).removeTokenTransactionsInHistory(token as Erc20Token); + @override Future getErc20Token(WalletBase wallet, String contractAddress) async { final polygonWallet = wallet as PolygonWallet; diff --git a/lib/src/screens/dashboard/pages/transactions_page.dart b/lib/src/screens/dashboard/pages/transactions_page.dart index e5c94415f..efdd764f1 100644 --- a/lib/src/screens/dashboard/pages/transactions_page.dart +++ b/lib/src/screens/dashboard/pages/transactions_page.dart @@ -8,6 +8,7 @@ import 'package:cake_wallet/view_model/dashboard/anonpay_transaction_list_item.d import 'package:cake_wallet/view_model/dashboard/order_list_item.dart'; import 'package:cw_core/crypto_currency.dart'; import 'package:cw_core/sync_status.dart'; +import 'package:cw_core/wallet_type.dart'; import 'package:flutter/material.dart'; import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; @@ -87,6 +88,10 @@ class TransactionsPage extends StatelessWidget { } final transaction = item.transaction; + final transactionType = dashboardViewModel.type == WalletType.ethereum && + transaction.evmSignatureName == 'approval' + ? ' (${transaction.evmSignatureName})' + : ''; return Observer( builder: (_) => TransactionRow( @@ -100,7 +105,8 @@ class TransactionsPage extends StatelessWidget { ? '' : item.formattedFiatAmount, isPending: transaction.isPending, - title: item.formattedTitle + item.formattedStatus, + title: item.formattedTitle + + item.formattedStatus + ' $transactionType', ), ); } diff --git a/lib/view_model/contact_list/contact_list_view_model.dart b/lib/view_model/contact_list/contact_list_view_model.dart index 8dbd97bb9..d63f78224 100644 --- a/lib/view_model/contact_list/contact_list_view_model.dart +++ b/lib/view_model/contact_list/contact_list_view_model.dart @@ -39,7 +39,7 @@ abstract class ContactListViewModelBase with Store { )); } }); - } else if (info.addresses?.isNotEmpty == true) { + } else if (info.addresses?.isNotEmpty == true && info.addresses!.length > 1) { info.addresses!.forEach((address, label) { if (label.isEmpty) { return; diff --git a/lib/view_model/dashboard/home_settings_view_model.dart b/lib/view_model/dashboard/home_settings_view_model.dart index 9e3be746e..5778f1e19 100644 --- a/lib/view_model/dashboard/home_settings_view_model.dart +++ b/lib/view_model/dashboard/home_settings_view_model.dart @@ -144,10 +144,12 @@ abstract class HomeSettingsViewModelBase with Store { if (_balanceViewModel.wallet.type == WalletType.ethereum) { ethereum!.addErc20Token(_balanceViewModel.wallet, token as Erc20Token); + if (!value) ethereum!.removeTokenTransactionsInHistory(_balanceViewModel.wallet, token); } if (_balanceViewModel.wallet.type == WalletType.polygon) { polygon!.addErc20Token(_balanceViewModel.wallet, token as Erc20Token); + if (!value) polygon!.removeTokenTransactionsInHistory(_balanceViewModel.wallet, token); } if (_balanceViewModel.wallet.type == WalletType.solana) { diff --git a/tool/configure.dart b/tool/configure.dart index b1018a5a0..133f12e52 100644 --- a/tool/configure.dart +++ b/tool/configure.dart @@ -667,6 +667,7 @@ abstract class Ethereum { List getERC20Currencies(WalletBase wallet); Future addErc20Token(WalletBase wallet, CryptoCurrency token); Future deleteErc20Token(WalletBase wallet, CryptoCurrency token); + Future removeTokenTransactionsInHistory(WalletBase wallet, CryptoCurrency token); Future getErc20Token(WalletBase wallet, String contractAddress); CryptoCurrency assetOfTransaction(WalletBase wallet, TransactionInfo transaction); @@ -770,6 +771,7 @@ abstract class Polygon { List getERC20Currencies(WalletBase wallet); Future addErc20Token(WalletBase wallet, CryptoCurrency token); Future deleteErc20Token(WalletBase wallet, CryptoCurrency token); + Future removeTokenTransactionsInHistory(WalletBase wallet, CryptoCurrency token); Future getErc20Token(WalletBase wallet, String contractAddress); CryptoCurrency assetOfTransaction(WalletBase wallet, TransactionInfo transaction);