diff --git a/lib/pages/token_view/sub_widgets/my_token_select_item.dart b/lib/pages/token_view/sub_widgets/my_token_select_item.dart index b293c2a2e..003fd515a 100644 --- a/lib/pages/token_view/sub_widgets/my_token_select_item.dart +++ b/lib/pages/token_view/sub_widgets/my_token_select_item.dart @@ -107,6 +107,7 @@ class _MyTokenSelectItemState extends ConsumerState { } if (mounted) { + unawaited(ref.read(pCurrentTokenWallet)!.refresh()); await Navigator.of(context).pushNamed( isDesktop ? DesktopTokenView.routeName : TokenView.routeName, arguments: widget.walletId, diff --git a/lib/pages/token_view/sub_widgets/token_transaction_list_widget.dart b/lib/pages/token_view/sub_widgets/token_transaction_list_widget.dart index f2037d7d3..639024995 100644 --- a/lib/pages/token_view/sub_widgets/token_transaction_list_widget.dart +++ b/lib/pages/token_view/sub_widgets/token_transaction_list_widget.dart @@ -85,6 +85,9 @@ class _TransactionsListState extends ConsumerState { .walletIdEqualTo(widget.walletId) .filter() .subTypeEqualTo(TransactionSubType.ethToken) + .and() + .contractAddressEqualTo( + ref.read(pCurrentTokenWallet)!.tokenContract.address) .sortByTimestampDesc(); _subscription = _query.watch().listen((event) { diff --git a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_wallet_summary.dart b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_wallet_summary.dart index fcf290017..77a5f0f5f 100644 --- a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_wallet_summary.dart +++ b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_wallet_summary.dart @@ -150,6 +150,9 @@ class _WDesktopWalletSummaryState extends ConsumerState { WalletRefreshButton( walletId: walletId, initialSyncStatus: widget.initialSyncStatus, + tokenContractAddress: widget.isToken + ? ref.watch(pCurrentTokenWallet)!.tokenContract.address + : null, ), if (coin == Coin.firo || coin == Coin.firoTestNet) const SizedBox( diff --git a/lib/wallets/isar/models/wallet_info.dart b/lib/wallets/isar/models/wallet_info.dart index 96c8e6416..6ffb150bc 100644 --- a/lib/wallets/isar/models/wallet_info.dart +++ b/lib/wallets/isar/models/wallet_info.dart @@ -344,18 +344,12 @@ class WalletInfo implements IsarId { required Set newContractAddresses, required Isar isar, }) async { - // only update if there were changes to the name - if (tokenContractAddresses - .toSet() - .difference(newContractAddresses) - .isNotEmpty) { - await updateOtherData( - newEntries: { - WalletInfoKeys.tokenContractAddresses: newContractAddresses.toList(), - }, - isar: isar, - ); - } + await updateOtherData( + newEntries: { + WalletInfoKeys.tokenContractAddresses: newContractAddresses.toList(), + }, + isar: isar, + ); } //============================================================================ diff --git a/lib/wallets/isar/providers/eth/token_balance_provider.dart b/lib/wallets/isar/providers/eth/token_balance_provider.dart index 70504623b..617a11b44 100644 --- a/lib/wallets/isar/providers/eth/token_balance_provider.dart +++ b/lib/wallets/isar/providers/eth/token_balance_provider.dart @@ -1,6 +1,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:isar/isar.dart'; import 'package:stackwallet/models/balance.dart'; +import 'package:stackwallet/models/isar/models/isar_models.dart'; import 'package:stackwallet/providers/db/main_db_provider.dart'; import 'package:stackwallet/wallets/isar/models/token_wallet_info.dart'; import 'package:stackwallet/wallets/isar/providers/util/watcher.dart'; @@ -8,13 +9,30 @@ import 'package:stackwallet/wallets/isar/providers/util/watcher.dart'; final _twiProvider = ChangeNotifierProvider.family( (ref, data) { - final collection = ref.watch(mainDBProvider).isar.tokenWalletInfo; + final isar = ref.watch(mainDBProvider).isar; + + final collection = isar.tokenWalletInfo; + + TokenWalletInfo? initial = collection + .where() + .walletIdTokenAddressEqualTo(data.walletId, data.contractAddress) + .findFirstSync(); + + if (initial == null) { + initial = TokenWalletInfo( + walletId: data.walletId, + tokenAddress: data.contractAddress, + tokenFractionDigits: isar.ethContracts + .getByAddressSync(data.contractAddress) + ?.decimals ?? + 2, + ); + + isar.writeTxnSync(() => isar.tokenWalletInfo.putSync(initial!)); + } final watcher = Watcher( - collection - .where() - .walletIdTokenAddressEqualTo(data.walletId, data.contractAddress) - .findFirstSync()!, + initial, collection: collection, ); @@ -27,7 +45,8 @@ final _twiProvider = ChangeNotifierProvider.family( (ref, data) { - return ref.watch(_twiProvider(data)).value as TokenWalletInfo; + return ref.watch(_twiProvider(data).select((value) => value.value)) + as TokenWalletInfo; }, ); diff --git a/lib/wallets/wallet/impl/ethereum_wallet.dart b/lib/wallets/wallet/impl/ethereum_wallet.dart index 622bac5e8..7a3f0a7b8 100644 --- a/lib/wallets/wallet/impl/ethereum_wallet.dart +++ b/lib/wallets/wallet/impl/ethereum_wallet.dart @@ -17,6 +17,7 @@ import 'package:stackwallet/services/event_bus/events/global/updated_in_backgrou import 'package:stackwallet/services/event_bus/global_event_bus.dart'; import 'package:stackwallet/utilities/amount/amount.dart'; import 'package:stackwallet/utilities/enums/coin_enum.dart'; +import 'package:stackwallet/utilities/enums/fee_rate_type_enum.dart'; import 'package:stackwallet/utilities/eth_commons.dart'; import 'package:stackwallet/utilities/logger.dart'; import 'package:stackwallet/wallets/crypto_currency/coins/ethereum.dart'; @@ -364,30 +365,124 @@ class EthereumWallet extends Bip39Wallet with PrivateKeyInterface { } @override - Future confirmSend({required TxData txData}) { - // TODO: implement confirmSend - throw UnimplementedError(); + Future prepareSend({required TxData txData}) async { + final int + rate; // TODO: use BigInt for feeObject whenever FeeObject gets redone + final feeObject = await fees; + switch (txData.feeRateType!) { + case FeeRateType.fast: + rate = feeObject.fast; + break; + case FeeRateType.average: + rate = feeObject.medium; + break; + case FeeRateType.slow: + rate = feeObject.slow; + break; + case FeeRateType.custom: + throw UnimplementedError("custom eth fees"); + } + + final feeEstimate = await estimateFeeFor(Amount.zero, rate); + + // bool isSendAll = false; + // final availableBalance = balance.spendable; + // if (satoshiAmount == availableBalance) { + // isSendAll = true; + // } + // + // if (isSendAll) { + // //Subtract fee amount from send amount + // satoshiAmount -= feeEstimate; + // } + + final client = getEthClient(); + + final myAddress = (await getCurrentReceivingAddress())!.value; + final myWeb3Address = web3.EthereumAddress.fromHex(myAddress); + + final amount = txData.recipients!.first.amount; + final address = txData.recipients!.first.address; + + // final est = await client.estimateGas( + // sender: myWeb3Address, + // to: web3.EthereumAddress.fromHex(address), + // gasPrice: web3.EtherAmount.fromUnitAndValue( + // web3.EtherUnit.wei, + // rate, + // ), + // amountOfGas: BigInt.from((cryptoCurrency as Ethereum).gasLimit), + // value: web3.EtherAmount.inWei(amount.raw), + // ); + + final nonce = txData.nonce ?? + await client.getTransactionCount(myWeb3Address, + atBlock: const web3.BlockNum.pending()); + + // final nResponse = await EthereumAPI.getAddressNonce(address: myAddress); + // print("=============================================================="); + // print("ETH client.estimateGas: $est"); + // print("ETH estimateFeeFor : $feeEstimate"); + // print("ETH nonce custom response: $nResponse"); + // print("ETH actual nonce : $nonce"); + // print("=============================================================="); + + final tx = web3.Transaction( + to: web3.EthereumAddress.fromHex(address), + gasPrice: web3.EtherAmount.fromUnitAndValue( + web3.EtherUnit.wei, + rate, + ), + maxGas: (cryptoCurrency as Ethereum).gasLimit, + value: web3.EtherAmount.inWei(amount.raw), + nonce: nonce, + ); + + return txData.copyWith( + nonce: tx.nonce, + web3dartTransaction: tx, + fee: feeEstimate, + feeInWei: BigInt.from(rate), + chainId: (await client.getChainId()), + ); } @override - Future prepareSend({required TxData txData}) { - // TODO: implement prepareSend - throw UnimplementedError(); + Future confirmSend({required TxData txData}) async { + final client = getEthClient(); + + final txid = await client.sendTransaction( + _credentials, + txData.web3dartTransaction!, + chainId: txData.chainId!.toInt(), + ); + + return txData.copyWith( + txid: txid, + txHash: txid, + ); } @override Future recover({required bool isRescan}) async { - if (isRescan) { - await mainDB.deleteWalletBlockchainData(walletId); - await _generateAndSaveAddress( - await getMnemonic(), - await getMnemonicPassphrase(), - ); - await updateBalance(); - await updateTransactions(isRescan: true); - } else { - // - } + await refreshMutex.protect(() async { + if (isRescan) { + await mainDB.deleteWalletBlockchainData(walletId); + await _generateAndSaveAddress( + await getMnemonic(), + await getMnemonicPassphrase(), + ); + await updateBalance(); + await updateTransactions(isRescan: true); + } else { + await _generateAndSaveAddress( + await getMnemonic(), + await getMnemonicPassphrase(), + ); + unawaited(updateBalance()); + unawaited(updateTransactions()); + } + }); } @override diff --git a/lib/wallets/wallet/impl/sub_wallets/eth_token_wallet.dart b/lib/wallets/wallet/impl/sub_wallets/eth_token_wallet.dart index 64b53a094..22fcd3e59 100644 --- a/lib/wallets/wallet/impl/sub_wallets/eth_token_wallet.dart +++ b/lib/wallets/wallet/impl/sub_wallets/eth_token_wallet.dart @@ -283,9 +283,15 @@ class EthTokenWallet extends Wallet { } @override - Future recover({required bool isRescan}) { - // TODO: implement recover - throw UnimplementedError(); + Future recover({required bool isRescan}) async { + try { + throw Exception(); + } catch (_, s) { + Logging.instance.log( + "Eth token wallet recover called. This should not happen. Stacktrace: $s", + level: LogLevel.Warning, + ); + } } @override @@ -401,30 +407,29 @@ class EthTokenWallet extends Wallet { for (final tuple in data) { // ignore all non Transfer events (for now) if (tuple.tx.topics[0] == kTransferEventSignature) { - final Amount amount; final Amount txFee = tuple.extra.gasUsed * tuple.extra.gasPrice; - String fromAddress, toAddress; - amount = Amount( + + final amount = Amount( rawValue: tuple.tx.data.toBigIntFromHex, fractionDigits: tokenContract.decimals, ); - fromAddress = _addressFromTopic( + final addressFrom = _addressFromTopic( tuple.tx.topics[1], ); - toAddress = _addressFromTopic( + final addressTo = _addressFromTopic( tuple.tx.topics[2], ); - bool isIncoming; - bool isSentToSelf = false; - if (fromAddress == addressString) { - isIncoming = false; - if (toAddress == addressString) { - isSentToSelf = true; + final TransactionType txType; + if (addressTo == addressString) { + if (addressFrom == addressTo) { + txType = TransactionType.sentToSelf; + } else { + txType = TransactionType.incoming; } - } else if (toAddress == addressString) { - isIncoming = true; + } else if (addressFrom == addressString) { + txType = TransactionType.outgoing; } else { // ignore for now I guess since anything here is not reflected in // balance anyways @@ -435,17 +440,6 @@ class EthTokenWallet extends Wallet { // "${tuple.item1.toString()}"); } - final TransactionType txType; - if (isIncoming) { - if (fromAddress == toAddress) { - txType = TransactionType.sentToSelf; - } else { - txType = TransactionType.incoming; - } - } else { - txType = TransactionType.outgoing; - } - final otherData = { "nonce": tuple.extra.nonce, "isCancelled": false,