mirror of
https://github.com/cake-tech/cake_wallet.git
synced 2024-12-23 12:09:43 +00:00
Add logic flow for adding erc20 tokens
This commit is contained in:
parent
4b35ad3b21
commit
55b5780958
13 changed files with 254 additions and 27 deletions
|
@ -3,6 +3,7 @@ import 'dart:math';
|
|||
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cw_ethereum/ethereum_balance.dart';
|
||||
import 'package:cw_ethereum/models/erc20_token.dart';
|
||||
import 'package:cw_ethereum/pending_ethereum_transaction.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:http/http.dart';
|
||||
|
@ -102,7 +103,10 @@ class EthereumClient {
|
|||
required int gas,
|
||||
required EthereumTransactionPriority priority,
|
||||
required CryptoCurrency currency,
|
||||
String? contractAddress,
|
||||
}) async {
|
||||
assert(currency == CryptoCurrency.eth || contractAddress != null);
|
||||
|
||||
bool _isEthereum = currency == CryptoCurrency.eth;
|
||||
|
||||
final price = await _client!.getGasPrice();
|
||||
|
@ -128,7 +132,7 @@ class EthereumClient {
|
|||
|
||||
final erc20 = Erc20(
|
||||
client: _client!,
|
||||
address: EthereumAddress.fromHex(_erc20Currencies[currency]!),
|
||||
address: EthereumAddress.fromHex(contractAddress!),
|
||||
);
|
||||
|
||||
final originalAmount = BigInt.parse(amount) / BigInt.from(pow(10, 18));
|
||||
|
@ -211,25 +215,22 @@ I/flutter ( 4474): Gas Used: 53000
|
|||
// ));
|
||||
// }
|
||||
|
||||
Future<Map<CryptoCurrency, ERC20Balance>> fetchERC20Balances(EthereumAddress userAddress) async {
|
||||
final Map<CryptoCurrency, ERC20Balance> erc20Balances = {};
|
||||
Future<ERC20Balance> fetchERC20Balances(
|
||||
EthereumAddress userAddress, String contractAddress) async {
|
||||
final erc20 = Erc20(address: EthereumAddress.fromHex(contractAddress), client: _client!);
|
||||
final balance = await erc20.balanceOf(userAddress);
|
||||
|
||||
for (var currency in _erc20Currencies.keys) {
|
||||
final contractAddress = _erc20Currencies[currency]!;
|
||||
int exponent = (await erc20.decimals()).toInt();
|
||||
|
||||
try {
|
||||
final erc20 = Erc20(address: EthereumAddress.fromHex(contractAddress), client: _client!);
|
||||
final balance = await erc20.balanceOf(userAddress);
|
||||
return ERC20Balance(balance, exponent: exponent);
|
||||
}
|
||||
|
||||
int exponent = (await erc20.decimals()).toInt();
|
||||
Future<Erc20Token> addErc20Token(String contractAddress) async {
|
||||
final erc20 = Erc20(address: EthereumAddress.fromHex(contractAddress), client: _client!);
|
||||
final name = await erc20.name();
|
||||
final symbol = await erc20.symbol();
|
||||
|
||||
erc20Balances[currency] = ERC20Balance(balance, exponent: exponent);
|
||||
} catch (e) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return erc20Balances;
|
||||
return Erc20Token(name: name, symbol: symbol, contractAddress: contractAddress);
|
||||
}
|
||||
|
||||
void stop() {
|
||||
|
|
|
@ -19,6 +19,8 @@ import 'package:cw_ethereum/ethereum_transaction_info.dart';
|
|||
import 'package:cw_ethereum/ethereum_transaction_priority.dart';
|
||||
import 'package:cw_ethereum/ethereum_wallet_addresses.dart';
|
||||
import 'package:cw_ethereum/file.dart';
|
||||
import 'package:cw_ethereum/models/erc20_token.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:hex/hex.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
import 'package:web3dart/web3dart.dart';
|
||||
|
@ -47,11 +49,17 @@ abstract class EthereumWalletBase
|
|||
{CryptoCurrency.eth: initialBalance ?? ERC20Balance(BigInt.zero)}),
|
||||
super(walletInfo) {
|
||||
this.walletInfo = walletInfo;
|
||||
|
||||
if (!Hive.isAdapterRegistered(Erc20Token.typeId)) {
|
||||
Hive.registerAdapter(Erc20TokenAdapter());
|
||||
}
|
||||
}
|
||||
|
||||
final String _mnemonic;
|
||||
final String _password;
|
||||
|
||||
late final Box<Erc20Token> erc20TokensBox;
|
||||
|
||||
late final EthPrivateKey _privateKey;
|
||||
|
||||
late EthereumClient _client;
|
||||
|
@ -71,6 +79,7 @@ abstract class EthereumWalletBase
|
|||
late ObservableMap<CryptoCurrency, ERC20Balance> balance;
|
||||
|
||||
Future<void> init() async {
|
||||
erc20TokensBox = await Hive.openBox<Erc20Token>(Erc20Token.boxName);
|
||||
await walletAddresses.init();
|
||||
_privateKey = await getPrivateKey(_mnemonic, _password);
|
||||
transactionHistory = EthereumTransactionHistory();
|
||||
|
@ -247,7 +256,15 @@ abstract class EthereumWalletBase
|
|||
|
||||
Future<void> _updateBalance() async {
|
||||
balance[currency] = await _fetchBalances();
|
||||
balance.addAll(await _client.fetchERC20Balances(_privateKey.address));
|
||||
|
||||
/// Get Erc20 Tokens balances
|
||||
for (var token in erc20TokensBox.values) {
|
||||
try {
|
||||
final currency = erc20Currencies.firstWhere((element) => element.title == token.symbol);
|
||||
balance[currency] =
|
||||
await _client.fetchERC20Balances(_privateKey.address, token.contractAddress);
|
||||
} catch (_) {}
|
||||
}
|
||||
await save();
|
||||
}
|
||||
|
||||
|
@ -270,7 +287,24 @@ abstract class EthereumWalletBase
|
|||
|
||||
Future<void>? updateBalance() => null;
|
||||
|
||||
List<CryptoCurrency> get erc20Currencies => _client.erc20Currencies.keys.toList();
|
||||
List<CryptoCurrency> get erc20Currencies => erc20TokensBox.values
|
||||
.map((token) => CryptoCurrency.all.firstWhere(
|
||||
(currency) => currency.title.toLowerCase() == token.symbol.toLowerCase(),
|
||||
orElse: () => CryptoCurrency(
|
||||
name: token.name,
|
||||
title: token.symbol,
|
||||
fullName: token.name,
|
||||
),
|
||||
))
|
||||
.toList();
|
||||
|
||||
Future<CryptoCurrency> addErc20Token(String contractAddress) async {
|
||||
final token = await _client.addErc20Token(contractAddress);
|
||||
|
||||
erc20TokensBox.add(token);
|
||||
|
||||
return CryptoCurrency(name: token.name, title: token.symbol, fullName: token.name);
|
||||
}
|
||||
|
||||
void _onNewTransaction(FilterEvent event) {
|
||||
_updateBalance();
|
||||
|
|
35
cw_ethereum/lib/models/erc20_token.dart
Normal file
35
cw_ethereum/lib/models/erc20_token.dart
Normal file
|
@ -0,0 +1,35 @@
|
|||
import 'package:cw_core/keyable.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
|
||||
part 'erc20_token.g.dart';
|
||||
|
||||
@HiveType(typeId: Erc20Token.typeId)
|
||||
class Erc20Token extends HiveObject with Keyable {
|
||||
@HiveField(0)
|
||||
final String name;
|
||||
@HiveField(1)
|
||||
final String symbol;
|
||||
@HiveField(2)
|
||||
final String contractAddress;
|
||||
|
||||
Erc20Token({required this.name, required this.symbol, required this.contractAddress});
|
||||
|
||||
static const typeId = 12;
|
||||
static const boxName = 'Erc20Tokens';
|
||||
|
||||
@override
|
||||
bool operator ==(other) =>
|
||||
other is Erc20Token &&
|
||||
(other.name == name && other.symbol == symbol && other.contractAddress == contractAddress);
|
||||
|
||||
@override
|
||||
int get hashCode => name.hashCode ^ symbol.hashCode ^ contractAddress.hashCode;
|
||||
|
||||
@override
|
||||
dynamic get keyIndex {
|
||||
_keyIndex ??= key;
|
||||
return _keyIndex;
|
||||
}
|
||||
|
||||
dynamic _keyIndex;
|
||||
}
|
|
@ -19,6 +19,7 @@ import 'package:cake_wallet/src/screens/buy/payfura_page.dart';
|
|||
import 'package:cake_wallet/src/screens/dashboard/desktop_dashboard_page.dart';
|
||||
import 'package:cake_wallet/src/screens/dashboard/desktop_widgets/desktop_sidebar_wrapper.dart';
|
||||
import 'package:cake_wallet/src/screens/dashboard/desktop_widgets/desktop_wallet_selection_dropdown.dart';
|
||||
import 'package:cake_wallet/src/screens/dashboard/home_settings_page.dart';
|
||||
import 'package:cake_wallet/src/screens/dashboard/widgets/transactions_page.dart';
|
||||
import 'package:cake_wallet/src/screens/receive/anonpay_invoice_page.dart';
|
||||
import 'package:cake_wallet/src/screens/receive/anonpay_receive_page.dart';
|
||||
|
@ -42,6 +43,7 @@ import 'package:cake_wallet/utils/responsive_layout_util.dart';
|
|||
import 'package:cake_wallet/view_model/dashboard/desktop_sidebar_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/anon_invoice_page_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/anonpay_details_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/dashboard/home_settings_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/dashboard/market_place_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/dashboard/receive_option_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/ionia/ionia_auth_view_model.dart';
|
||||
|
@ -1015,8 +1017,11 @@ Future setup({
|
|||
getIt.registerFactoryParam<AdvancedPrivacySettingsViewModel, WalletType, void>(
|
||||
(type, _) => AdvancedPrivacySettingsViewModel(type, getIt.get<SettingsStore>()));
|
||||
|
||||
getIt.registerFactory<HomeSettingsPage>(
|
||||
() => AdvancedPrivacySettingsViewModel(type, getIt.get<SettingsStore>()));
|
||||
getIt.registerFactoryParam<HomeSettingsPage, BalanceViewModel, void>((balanceViewModel, _) =>
|
||||
HomeSettingsPage(getIt.get<HomeSettingsViewModel>(param1: balanceViewModel)));
|
||||
|
||||
getIt.registerFactoryParam<HomeSettingsViewModel, BalanceViewModel, void>(
|
||||
(balanceViewModel, _) => HomeSettingsViewModel(getIt.get<SettingsStore>(), balanceViewModel));
|
||||
|
||||
_isSetupFinished = true;
|
||||
}
|
||||
|
|
|
@ -40,6 +40,7 @@ class PreferencesKey {
|
|||
static const lastAuthTimeMilliseconds = 'last_auth_time_milliseconds';
|
||||
static const lastPopupDate = 'last_popup_date';
|
||||
static const lastAppReviewDate = 'last_app_review_date';
|
||||
static const sortBalanceBy = 'sort_balance_by';
|
||||
|
||||
|
||||
|
||||
|
|
17
lib/entities/sort_balance_types.dart
Normal file
17
lib/entities/sort_balance_types.dart
Normal file
|
@ -0,0 +1,17 @@
|
|||
enum SortBalanceBy {
|
||||
FiatBalance,
|
||||
Gross,
|
||||
Alphabetical;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
switch (this) {
|
||||
case SortBalanceBy.FiatBalance:
|
||||
return "S.current.fiat_balance";
|
||||
case SortBalanceBy.Gross:
|
||||
return "S.current.gross";
|
||||
case SortBalanceBy.Alphabetical:
|
||||
return "S.current.alphabetical";
|
||||
}
|
||||
}
|
||||
}
|
|
@ -81,8 +81,14 @@ class CWEthereum extends Ethereum {
|
|||
int formatterEthereumParseAmount(String amount) => EthereumFormatter.parseEthereumAmount(amount);
|
||||
|
||||
@override
|
||||
List<CryptoCurrency> getERC20Currencies(Object wallet) {
|
||||
List<CryptoCurrency> getERC20Currencies(WalletBase wallet) {
|
||||
final ethereumWallet = wallet as EthereumWallet;
|
||||
return ethereumWallet.erc20Currencies;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<CryptoCurrency> addErc20Token(WalletBase wallet, String contractAddress) async {
|
||||
final ethereumWallet = wallet as EthereumWallet;
|
||||
return await ethereumWallet.addErc20Token(contractAddress);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ import 'package:cake_wallet/src/screens/buy/buy_webview_page.dart';
|
|||
import 'package:cake_wallet/src/screens/buy/webview_page.dart';
|
||||
import 'package:cake_wallet/src/screens/buy/payfura_page.dart';
|
||||
import 'package:cake_wallet/src/screens/buy/pre_order_page.dart';
|
||||
import 'package:cake_wallet/src/screens/dashboard/home_settings_page.dart';
|
||||
import 'package:cake_wallet/src/screens/restore/sweeping_wallet_page.dart';
|
||||
import 'package:cake_wallet/src/screens/receive/anonpay_invoice_page.dart';
|
||||
import 'package:cake_wallet/src/screens/receive/anonpay_receive_page.dart';
|
||||
|
@ -97,8 +98,6 @@ import 'package:cake_wallet/ionia/ionia_any_pay_payment_info.dart';
|
|||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cw_core/node.dart';
|
||||
|
||||
import 'buy/moonpay/moonpay_buy_provider.dart';
|
||||
|
||||
late RouteSettings currentRouteSettings;
|
||||
|
||||
Route<dynamic> createRoute(RouteSettings settings) {
|
||||
|
|
85
lib/src/screens/dashboard/home_settings_page.dart
Normal file
85
lib/src/screens/dashboard/home_settings_page.dart
Normal file
|
@ -0,0 +1,85 @@
|
|||
import 'package:cake_wallet/entities/sort_balance_types.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||
import 'package:cake_wallet/src/screens/settings/widgets/settings_picker_cell.dart';
|
||||
import 'package:cake_wallet/src/screens/settings/widgets/settings_switcher_cell.dart';
|
||||
import 'package:cake_wallet/view_model/dashboard/home_settings_view_model.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
|
||||
class HomeSettingsPage extends BasePage {
|
||||
HomeSettingsPage(this._homeSettingsViewModel);
|
||||
|
||||
final HomeSettingsViewModel _homeSettingsViewModel;
|
||||
|
||||
final TextEditingController _searchController = TextEditingController();
|
||||
|
||||
// TODO: add localization
|
||||
@override
|
||||
String? get title => "S.current.home_screen_settings";
|
||||
|
||||
@override
|
||||
Widget body(BuildContext context) {
|
||||
return SingleChildScrollView(
|
||||
child: Column(
|
||||
children: [
|
||||
Observer(
|
||||
builder: (_) => SettingsPickerCell<SortBalanceBy>(
|
||||
title: "S.current.sort_by",
|
||||
items: SortBalanceBy.values,
|
||||
selectedItem: _homeSettingsViewModel.sortBalanceBy,
|
||||
onItemSelected: _homeSettingsViewModel.setSortBalanceBy,
|
||||
),
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
TextFormField(
|
||||
controller: _searchController,
|
||||
style: TextStyle(color: Theme.of(context).primaryTextTheme.titleLarge!.color!),
|
||||
decoration: InputDecoration(
|
||||
hintText: "S.of(context).search_token",
|
||||
prefixIcon: Image.asset("assets/images/search_icon.png"),
|
||||
filled: true,
|
||||
fillColor: Theme.of(context).accentTextTheme.displaySmall!.color!,
|
||||
alignLabelWithHint: false,
|
||||
contentPadding: const EdgeInsets.symmetric(vertical: 4, horizontal: 16),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(14),
|
||||
borderSide: const BorderSide(color: Colors.transparent),
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(14),
|
||||
borderSide: const BorderSide(color: Colors.transparent),
|
||||
),
|
||||
),
|
||||
),
|
||||
IconButton(
|
||||
onPressed: () {},
|
||||
style: IconButton.styleFrom(
|
||||
shape: CircleBorder(),
|
||||
backgroundColor: Theme.of(context).accentTextTheme.bodySmall!.color!,
|
||||
),
|
||||
icon: Icon(
|
||||
Icons.add,
|
||||
color: Theme.of(context).primaryTextTheme.titleLarge!.color!,
|
||||
size: 22.0,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
ListView.builder(
|
||||
itemCount: _homeSettingsViewModel.tokens.length,
|
||||
shrinkWrap: true,
|
||||
itemBuilder: (context, index) {
|
||||
return SettingsSwitcherCell(
|
||||
title: _homeSettingsViewModel.tokens[index],
|
||||
value: false,
|
||||
onValueChange: (_, bool value) {},
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -49,9 +49,8 @@ class BalancePage extends StatelessWidget {
|
|||
),
|
||||
if (dashboardViewModel.balanceViewModel.isHomeScreenSettingsEnabled)
|
||||
InkWell(
|
||||
onTap: () {
|
||||
Navigator.pushNamed(context, Routes.homeSettings)
|
||||
},
|
||||
onTap: () => Navigator.pushNamed(context, Routes.homeSettings,
|
||||
arguments: dashboardViewModel.balanceViewModel),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Image.asset(
|
||||
|
|
|
@ -4,6 +4,7 @@ import 'package:cake_wallet/bitcoin/bitcoin.dart';
|
|||
import 'package:cake_wallet/entities/exchange_api_mode.dart';
|
||||
import 'package:cake_wallet/entities/pin_code_required_duration.dart';
|
||||
import 'package:cake_wallet/entities/preferences_key.dart';
|
||||
import 'package:cake_wallet/entities/sort_balance_types.dart';
|
||||
import 'package:cake_wallet/utils/device_info.dart';
|
||||
import 'package:cake_wallet/ethereum/ethereum.dart';
|
||||
import 'package:cw_core/transaction_priority.dart';
|
||||
|
@ -57,6 +58,7 @@ abstract class SettingsStoreBase with Store {
|
|||
required this.isBitcoinBuyEnabled,
|
||||
required this.actionlistDisplayMode,
|
||||
required this.pinTimeOutDuration,
|
||||
required this.sortBalanceBy,
|
||||
TransactionPriority? initialBitcoinTransactionPriority,
|
||||
TransactionPriority? initialMoneroTransactionPriority,
|
||||
TransactionPriority? initialHavenTransactionPriority,
|
||||
|
@ -214,6 +216,11 @@ abstract class SettingsStoreBase with Store {
|
|||
(ExchangeApiMode mode) =>
|
||||
sharedPreferences.setInt(PreferencesKey.exchangeStatusKey, mode.serialize()));
|
||||
|
||||
reaction(
|
||||
(_) => sortBalanceBy,
|
||||
(SortBalanceBy sortBalanceBy) =>
|
||||
_sharedPreferences.setInt(PreferencesKey.sortBalanceBy, sortBalanceBy.index));
|
||||
|
||||
this.nodes.observe((change) {
|
||||
if (change.newValue != null && change.key != null) {
|
||||
_saveCurrentNode(change.newValue!, change.key!);
|
||||
|
@ -293,6 +300,9 @@ abstract class SettingsStoreBase with Store {
|
|||
@observable
|
||||
ObservableMap<WalletType, TransactionPriority> priority;
|
||||
|
||||
@observable
|
||||
SortBalanceBy sortBalanceBy;
|
||||
|
||||
String appVersion;
|
||||
|
||||
String deviceName;
|
||||
|
@ -393,6 +403,8 @@ abstract class SettingsStoreBase with Store {
|
|||
final pinCodeTimeOutDuration = timeOutDuration != null
|
||||
? PinCodeRequiredDuration.deserialize(raw: timeOutDuration)
|
||||
: defaultPinCodeTimeOutDuration;
|
||||
final sortBalanceBy =
|
||||
SortBalanceBy.values[sharedPreferences.getInt(PreferencesKey.sortBalanceBy) ?? 0];
|
||||
|
||||
// If no value
|
||||
if (pinLength == null || pinLength == 0) {
|
||||
|
@ -463,6 +475,7 @@ abstract class SettingsStoreBase with Store {
|
|||
initialPinLength: pinLength,
|
||||
pinTimeOutDuration: pinCodeTimeOutDuration,
|
||||
initialLanguageCode: savedLanguageCode,
|
||||
sortBalanceBy: sortBalanceBy,
|
||||
initialMoneroTransactionPriority: moneroTransactionPriority,
|
||||
initialBitcoinTransactionPriority: bitcoinTransactionPriority,
|
||||
initialHavenTransactionPriority: havenTransactionPriority,
|
||||
|
@ -541,6 +554,8 @@ abstract class SettingsStoreBase with Store {
|
|||
languageCode = sharedPreferences.getString(PreferencesKey.currentLanguageCode) ?? languageCode;
|
||||
shouldShowYatPopup =
|
||||
sharedPreferences.getBool(PreferencesKey.shouldShowYatPopup) ?? shouldShowYatPopup;
|
||||
sortBalanceBy = SortBalanceBy
|
||||
.values[sharedPreferences.getInt(PreferencesKey.sortBalanceBy) ?? sortBalanceBy.index];
|
||||
|
||||
final nodeId = sharedPreferences.getInt(PreferencesKey.currentNodeIdKey);
|
||||
final bitcoinElectrumServerId =
|
||||
|
|
29
lib/view_model/dashboard/home_settings_view_model.dart
Normal file
29
lib/view_model/dashboard/home_settings_view_model.dart
Normal file
|
@ -0,0 +1,29 @@
|
|||
import 'package:cake_wallet/entities/sort_balance_types.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/store/settings_store.dart';
|
||||
import 'package:cake_wallet/view_model/dashboard/balance_view_model.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
|
||||
part 'home_settings_view_model.g.dart';
|
||||
|
||||
class HomeSettingsViewModel = HomeSettingsViewModelBase with _$HomeSettingsViewModel;
|
||||
|
||||
abstract class HomeSettingsViewModelBase with Store {
|
||||
HomeSettingsViewModelBase(this._settingsStore, this._balanceViewModel) {
|
||||
}
|
||||
|
||||
final SettingsStore _settingsStore;
|
||||
final BalanceViewModel _balanceViewModel;
|
||||
|
||||
@computed
|
||||
SortBalanceBy get sortBalanceBy => _settingsStore.sortBalanceBy;
|
||||
|
||||
@action
|
||||
void setSortBalanceBy(SortBalanceBy value) => _settingsStore.sortBalanceBy = value;
|
||||
|
||||
@observable
|
||||
bool pinNativeToken = false;
|
||||
|
||||
List<String> get tokens =>
|
||||
_balanceViewModel.balances.keys.map((e) => e.fullName ?? e.title).toList();
|
||||
}
|
|
@ -524,7 +524,8 @@ abstract class Ethereum {
|
|||
});
|
||||
|
||||
int formatterEthereumParseAmount(String amount);
|
||||
List<CryptoCurrency> getERC20Currencies(Object wallet);
|
||||
List<CryptoCurrency> getERC20Currencies(WalletBase wallet);
|
||||
Future<CryptoCurrency> addErc20Token(WalletBase wallet, String contractAddress);
|
||||
}
|
||||
""";
|
||||
|
||||
|
|
Loading…
Reference in a new issue