Merge remote-tracking branch 'origin/wallets_refactor' into wallets_refactor

This commit is contained in:
sneurlax 2024-01-10 16:32:03 -06:00
commit da24aaa391
12 changed files with 1425 additions and 122 deletions

View file

@ -22,6 +22,7 @@ import 'package:stackwallet/utilities/amount/amount.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart'; import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/stack_file_system.dart'; import 'package:stackwallet/utilities/stack_file_system.dart';
import 'package:stackwallet/wallets/isar/models/spark_coin.dart'; import 'package:stackwallet/wallets/isar/models/spark_coin.dart';
import 'package:stackwallet/wallets/isar/models/token_wallet_info.dart';
import 'package:stackwallet/wallets/isar/models/wallet_info.dart'; import 'package:stackwallet/wallets/isar/models/wallet_info.dart';
import 'package:stackwallet/wallets/isar/models/wallet_info_meta.dart'; import 'package:stackwallet/wallets/isar/models/wallet_info_meta.dart';
import 'package:tuple/tuple.dart'; import 'package:tuple/tuple.dart';
@ -65,6 +66,7 @@ class MainDB {
TransactionV2Schema, TransactionV2Schema,
SparkCoinSchema, SparkCoinSchema,
WalletInfoMetaSchema, WalletInfoMetaSchema,
TokenWalletInfoSchema,
], ],
directory: (await StackFileSystem.applicationIsarDirectory()).path, directory: (await StackFileSystem.applicationIsarDirectory()).path,
// inspector: kDebugMode, // inspector: kDebugMode,

View file

@ -180,10 +180,7 @@ class _EditWalletTokensViewState extends ConsumerState<EditWalletTokensView> {
tokenEntities.addAll(contracts.map((e) => AddTokenListElementData(e))); tokenEntities.addAll(contracts.map((e) => AddTokenListElementData(e)));
final walletContracts = final walletContracts = ref.read(pWalletTokenAddresses(widget.walletId));
(ref.read(pWallets).getWallet(widget.walletId) as EthereumWallet)
.info
.tokenContractAddresses;
final shouldMarkAsSelectedContracts = [ final shouldMarkAsSelectedContracts = [
...walletContracts, ...walletContracts,

View file

@ -15,14 +15,12 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/svg.dart'; import 'package:flutter_svg/svg.dart';
import 'package:stackwallet/pages/add_wallet_views/add_token_view/edit_wallet_tokens_view.dart'; import 'package:stackwallet/pages/add_wallet_views/add_token_view/edit_wallet_tokens_view.dart';
import 'package:stackwallet/pages/token_view/sub_widgets/my_tokens_list.dart'; import 'package:stackwallet/pages/token_view/sub_widgets/my_tokens_list.dart';
import 'package:stackwallet/providers/global/wallets_provider.dart';
import 'package:stackwallet/themes/stack_colors.dart'; import 'package:stackwallet/themes/stack_colors.dart';
import 'package:stackwallet/utilities/assets.dart'; import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/constants.dart'; import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/text_styles.dart'; import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/util.dart'; import 'package:stackwallet/utilities/util.dart';
import 'package:stackwallet/wallets/isar/providers/wallet_info_provider.dart'; import 'package:stackwallet/wallets/isar/providers/wallet_info_provider.dart';
import 'package:stackwallet/wallets/wallet/impl/ethereum_wallet.dart';
import 'package:stackwallet/widgets/background.dart'; import 'package:stackwallet/widgets/background.dart';
import 'package:stackwallet/widgets/conditional_parent.dart'; import 'package:stackwallet/widgets/conditional_parent.dart';
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart'; import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
@ -233,11 +231,7 @@ class _MyTokensViewState extends ConsumerState<MyTokensView> {
child: MyTokensList( child: MyTokensList(
walletId: widget.walletId, walletId: widget.walletId,
searchTerm: _searchString, searchTerm: _searchString,
tokenContracts: ref tokenContracts: ref.watch(pWalletTokenAddresses(widget.walletId)),
.watch(pWallets.select((value) =>
value.getWallet(widget.walletId) as EthereumWallet))
.info
.tokenContractAddresses,
), ),
), ),
], ],

View file

@ -13,6 +13,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:stackwallet/models/isar/models/ethereum/eth_contract.dart'; import 'package:stackwallet/models/isar/models/ethereum/eth_contract.dart';
import 'package:stackwallet/pages/token_view/token_view.dart'; import 'package:stackwallet/pages/token_view/token_view.dart';
import 'package:stackwallet/pages_desktop_specific/my_stack_view/wallet_view/desktop_token_view.dart'; import 'package:stackwallet/pages_desktop_specific/my_stack_view/wallet_view/desktop_token_view.dart';
import 'package:stackwallet/providers/db/main_db_provider.dart';
import 'package:stackwallet/providers/global/secure_store_provider.dart'; import 'package:stackwallet/providers/global/secure_store_provider.dart';
import 'package:stackwallet/providers/providers.dart'; import 'package:stackwallet/providers/providers.dart';
import 'package:stackwallet/services/ethereum/cached_eth_token_balance.dart'; import 'package:stackwallet/services/ethereum/cached_eth_token_balance.dart';
@ -25,6 +26,7 @@ import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/show_loading.dart'; import 'package:stackwallet/utilities/show_loading.dart';
import 'package:stackwallet/utilities/text_styles.dart'; import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/util.dart'; import 'package:stackwallet/utilities/util.dart';
import 'package:stackwallet/wallets/isar/providers/eth/token_balance_provider.dart';
import 'package:stackwallet/wallets/isar/providers/wallet_info_provider.dart'; import 'package:stackwallet/wallets/isar/providers/wallet_info_provider.dart';
import 'package:stackwallet/wallets/wallet/impl/ethereum_wallet.dart'; import 'package:stackwallet/wallets/wallet/impl/ethereum_wallet.dart';
import 'package:stackwallet/widgets/desktop/primary_button.dart'; import 'package:stackwallet/widgets/desktop/primary_button.dart';
@ -116,11 +118,14 @@ class _MyTokenSelectItemState extends ConsumerState<MyTokenSelectItem> {
cachedBalance = CachedEthTokenBalance(widget.walletId, widget.token); cachedBalance = CachedEthTokenBalance(widget.walletId, widget.token);
WidgetsBinding.instance.addPostFrameCallback((_) async { WidgetsBinding.instance.addPostFrameCallback((_) async {
if (mounted) {
final address = ref.read(pWalletReceivingAddress(widget.walletId)); final address = ref.read(pWalletReceivingAddress(widget.walletId));
await cachedBalance.fetchAndUpdateCachedBalance(address); await cachedBalance.fetchAndUpdateCachedBalance(
address, ref.read(mainDBProvider));
if (mounted) { if (mounted) {
setState(() {}); setState(() {});
} }
}
}); });
super.initState(); super.initState();
@ -172,7 +177,14 @@ class _MyTokenSelectItemState extends ConsumerState<MyTokenSelectItem> {
const Spacer(), const Spacer(),
Text( Text(
ref.watch(pAmountFormatter(Coin.ethereum)).format( ref.watch(pAmountFormatter(Coin.ethereum)).format(
cachedBalance.getCachedBalance().total, ref
.watch(pTokenBalance(
(
walletId: widget.walletId,
contractAddress: widget.token.address
),
))
.total,
ethContract: widget.token, ethContract: widget.token,
), ),
style: isDesktop style: isDesktop

View file

@ -25,7 +25,7 @@ import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/text_styles.dart'; import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/util.dart'; import 'package:stackwallet/utilities/util.dart';
import 'package:stackwallet/wallets/isar/models/wallet_info.dart'; import 'package:stackwallet/wallets/isar/models/wallet_info.dart';
import 'package:stackwallet/wallets/wallet/impl/ethereum_wallet.dart'; import 'package:stackwallet/wallets/isar/providers/wallet_info_provider.dart';
import 'package:stackwallet/wallets/wallet/wallet.dart'; import 'package:stackwallet/wallets/wallet/wallet.dart';
import 'package:stackwallet/widgets/background.dart'; import 'package:stackwallet/widgets/background.dart';
import 'package:stackwallet/widgets/conditional_parent.dart'; import 'package:stackwallet/widgets/conditional_parent.dart';
@ -119,9 +119,8 @@ class _EthWalletsOverviewState extends ConsumerState<WalletsOverview> {
if (widget.coin == Coin.ethereum) { if (widget.coin == Coin.ethereum) {
for (final data in walletsData) { for (final data in walletsData) {
final List<EthContract> contracts = []; final List<EthContract> contracts = [];
final wallet = ref.read(pWallets).getWallet(data.walletId);
final contractAddresses = final contractAddresses =
(wallet as EthereumWallet).info.tokenContractAddresses; ref.read(pWalletTokenAddresses(data.walletId));
// fetch each contract // fetch each contract
for (final contractAddress in contractAddresses) { for (final contractAddress in contractAddresses) {

View file

@ -8,29 +8,37 @@
* *
*/ */
import 'package:isar/isar.dart';
import 'package:stackwallet/db/isar/main_db.dart';
import 'package:stackwallet/models/balance.dart'; import 'package:stackwallet/models/balance.dart';
import 'package:stackwallet/models/isar/models/ethereum/eth_contract.dart'; import 'package:stackwallet/models/isar/models/ethereum/eth_contract.dart';
import 'package:stackwallet/services/ethereum/ethereum_api.dart'; import 'package:stackwallet/services/ethereum/ethereum_api.dart';
import 'package:stackwallet/services/mixins/eth_token_cache.dart';
import 'package:stackwallet/utilities/amount/amount.dart'; import 'package:stackwallet/utilities/amount/amount.dart';
import 'package:stackwallet/utilities/logger.dart'; import 'package:stackwallet/utilities/logger.dart';
import 'package:stackwallet/wallets/isar/models/token_wallet_info.dart';
class CachedEthTokenBalance with EthTokenCache { class CachedEthTokenBalance {
final String walletId; final String walletId;
final EthContract token; final EthContract token;
CachedEthTokenBalance(this.walletId, this.token) { CachedEthTokenBalance(this.walletId, this.token);
initCache(walletId, token);
}
Future<void> fetchAndUpdateCachedBalance(String address) async { Future<void> fetchAndUpdateCachedBalance(
String address,
MainDB mainDB,
) async {
final response = await EthereumAPI.getWalletTokenBalance( final response = await EthereumAPI.getWalletTokenBalance(
address: address, address: address,
contractAddress: token.address, contractAddress: token.address,
); );
if (response.value != null) { final info = await mainDB.isar.tokenWalletInfo
await updateCachedBalance( .where()
.walletIdTokenAddressEqualTo(walletId, token.address)
.findFirst();
if (response.value != null && info != null) {
await info.updateCachedBalance(
Balance( Balance(
total: response.value!, total: response.value!,
spendable: response.value!, spendable: response.value!,
@ -43,6 +51,7 @@ class CachedEthTokenBalance with EthTokenCache {
fractionDigits: token.decimals, fractionDigits: token.decimals,
), ),
), ),
isar: mainDB.isar,
); );
} else { } else {
Logging.instance.log( Logging.instance.log(

View file

@ -1,70 +1,70 @@
/* // /*
* This file is part of Stack Wallet. // * This file is part of Stack Wallet.
* // *
* Copyright (c) 2023 Cypher Stack // * Copyright (c) 2023 Cypher Stack
* All Rights Reserved. // * All Rights Reserved.
* The code is distributed under GPLv3 license, see LICENSE file for details. // * The code is distributed under GPLv3 license, see LICENSE file for details.
* Generated by Cypher Stack on 2023-05-26 // * Generated by Cypher Stack on 2023-05-26
* // *
*/ // */
//
import 'package:stackwallet/db/hive/db.dart'; // import 'package:stackwallet/db/hive/db.dart';
import 'package:stackwallet/models/balance.dart'; // import 'package:stackwallet/models/balance.dart';
import 'package:stackwallet/models/isar/models/ethereum/eth_contract.dart'; // import 'package:stackwallet/models/isar/models/ethereum/eth_contract.dart';
import 'package:stackwallet/utilities/amount/amount.dart'; // import 'package:stackwallet/utilities/amount/amount.dart';
//
abstract class TokenCacheKeys { // abstract class TokenCacheKeys {
static String tokenBalance(String contractAddress) { // static String tokenBalance(String contractAddress) {
return "tokenBalanceCache_$contractAddress"; // return "tokenBalanceCache_$contractAddress";
} // }
} // }
//
mixin EthTokenCache { // mixin EthTokenCache {
late final String _walletId; // late final String _walletId;
late final EthContract _token; // late final EthContract _token;
//
void initCache(String walletId, EthContract token) { // void initCache(String walletId, EthContract token) {
_walletId = walletId; // _walletId = walletId;
_token = token; // _token = token;
} // }
//
// token balance cache // // token balance cache
Balance getCachedBalance() { // Balance getCachedBalance() {
final jsonString = DB.instance.get<dynamic>( // final jsonString = DB.instance.get<dynamic>(
boxName: _walletId, // boxName: _walletId,
key: TokenCacheKeys.tokenBalance(_token.address), // key: TokenCacheKeys.tokenBalance(_token.address),
) as String?; // ) as String?;
if (jsonString == null) { // if (jsonString == null) {
return Balance( // return Balance(
total: Amount( // total: Amount(
rawValue: BigInt.zero, // rawValue: BigInt.zero,
fractionDigits: _token.decimals, // fractionDigits: _token.decimals,
), // ),
spendable: Amount( // spendable: Amount(
rawValue: BigInt.zero, // rawValue: BigInt.zero,
fractionDigits: _token.decimals, // fractionDigits: _token.decimals,
), // ),
blockedTotal: Amount( // blockedTotal: Amount(
rawValue: BigInt.zero, // rawValue: BigInt.zero,
fractionDigits: _token.decimals, // fractionDigits: _token.decimals,
), // ),
pendingSpendable: Amount( // pendingSpendable: Amount(
rawValue: BigInt.zero, // rawValue: BigInt.zero,
fractionDigits: _token.decimals, // fractionDigits: _token.decimals,
), // ),
); // );
} // }
return Balance.fromJson( // return Balance.fromJson(
jsonString, // jsonString,
_token.decimals, // _token.decimals,
); // );
} // }
//
Future<void> updateCachedBalance(Balance balance) async { // Future<void> updateCachedBalance(Balance balance) async {
await DB.instance.put<dynamic>( // await DB.instance.put<dynamic>(
boxName: _walletId, // boxName: _walletId,
key: TokenCacheKeys.tokenBalance(_token.address), // key: TokenCacheKeys.tokenBalance(_token.address),
value: balance.toJsonIgnoreCoin(), // value: balance.toJsonIgnoreCoin(),
); // );
} // }
} // }

View file

@ -0,0 +1,93 @@
import 'package:isar/isar.dart';
import 'package:stackwallet/models/balance.dart';
import 'package:stackwallet/models/isar/models/isar_models.dart';
import 'package:stackwallet/utilities/amount/amount.dart';
import 'package:stackwallet/wallets/isar/isar_id_interface.dart';
part 'token_wallet_info.g.dart';
@Collection(accessor: "tokenWalletInfo", inheritance: false)
class TokenWalletInfo implements IsarId {
@override
Id id = Isar.autoIncrement;
@Index(
unique: true,
replace: false,
composite: [
CompositeIndex("tokenAddress"),
],
)
final String walletId;
final String tokenAddress;
final int tokenFractionDigits;
final String? cachedBalanceJsonString;
TokenWalletInfo({
required this.walletId,
required this.tokenAddress,
required this.tokenFractionDigits,
this.cachedBalanceJsonString,
});
EthContract getContract(Isar isar) =>
isar.ethContracts.where().addressEqualTo(tokenAddress).findFirstSync()!;
// token balance cache
Balance getCachedBalance() {
if (cachedBalanceJsonString == null) {
return Balance(
total: Amount.zeroWith(
fractionDigits: tokenFractionDigits,
),
spendable: Amount.zeroWith(
fractionDigits: tokenFractionDigits,
),
blockedTotal: Amount.zeroWith(
fractionDigits: tokenFractionDigits,
),
pendingSpendable: Amount.zeroWith(
fractionDigits: tokenFractionDigits,
),
);
}
return Balance.fromJson(
cachedBalanceJsonString!,
tokenFractionDigits,
);
}
Future<void> updateCachedBalance(
Balance balance, {
required Isar isar,
}) async {
// // ensure we are updating using the latest entry of this in the db
final thisEntry = await isar.tokenWalletInfo
.where()
.walletIdEqualToTokenAddressNotEqualTo(walletId, tokenAddress)
.findFirst();
if (thisEntry == null) {
throw Exception(
"Attempted to update cached token balance before object was saved in db",
);
} else {
await isar.writeTxn(() async {
await isar.tokenWalletInfo.deleteByWalletIdTokenAddress(
walletId,
tokenAddress,
);
await isar.tokenWalletInfo.put(
TokenWalletInfo(
walletId: walletId,
tokenAddress: tokenAddress,
tokenFractionDigits: tokenFractionDigits,
cachedBalanceJsonString: balance.toJsonIgnoreCoin(),
),
);
});
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,40 @@
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:isar/isar.dart';
import 'package:stackwallet/models/balance.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';
final _twiProvider = ChangeNotifierProvider.family<Watcher,
({String walletId, String contractAddress})>(
(ref, data) {
final collection = ref.watch(mainDBProvider).isar.tokenWalletInfo;
final watcher = Watcher(
collection
.where()
.walletIdTokenAddressEqualTo(data.walletId, data.contractAddress)
.findFirstSync()!,
collection: collection,
);
ref.onDispose(() => watcher.dispose());
return watcher;
},
);
final pTokenWalletInfo = Provider.family<TokenWalletInfo,
({String walletId, String contractAddress})>(
(ref, data) {
return ref.watch(_twiProvider(data)).value as TokenWalletInfo;
},
);
final pTokenBalance =
Provider.family<Balance, ({String walletId, String contractAddress})>(
(ref, data) {
return ref.watch(_twiProvider(data).select(
(value) => (value.value as TokenWalletInfo).getCachedBalance()));
},
);

View file

@ -82,3 +82,10 @@ final pWalletReceivingAddress = Provider.family<String, String>(
.select((value) => (value.value as WalletInfo).cachedReceivingAddress)); .select((value) => (value.value as WalletInfo).cachedReceivingAddress));
}, },
); );
final pWalletTokenAddresses = Provider.family<List<String>, String>(
(ref, walletId) {
return ref.watch(_wiProvider(walletId)
.select((value) => (value.value as WalletInfo).tokenContractAddresses));
},
);

View file

@ -11,12 +11,11 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_svg/flutter_svg.dart';
import 'package:stackwallet/providers/providers.dart';
import 'package:stackwallet/themes/stack_colors.dart'; import 'package:stackwallet/themes/stack_colors.dart';
import 'package:stackwallet/utilities/assets.dart'; import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/constants.dart'; import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/util.dart'; import 'package:stackwallet/utilities/util.dart';
import 'package:stackwallet/wallets/wallet/impl/ethereum_wallet.dart'; import 'package:stackwallet/wallets/isar/providers/wallet_info_provider.dart';
import 'package:stackwallet/widgets/animated_widgets/rotate_icon.dart'; import 'package:stackwallet/widgets/animated_widgets/rotate_icon.dart';
import 'package:stackwallet/widgets/expandable.dart'; import 'package:stackwallet/widgets/expandable.dart';
import 'package:stackwallet/widgets/rounded_white_container.dart'; import 'package:stackwallet/widgets/rounded_white_container.dart';
@ -40,17 +39,6 @@ class MasterWalletCard extends ConsumerStatefulWidget {
class _MasterWalletCardState extends ConsumerState<MasterWalletCard> { class _MasterWalletCardState extends ConsumerState<MasterWalletCard> {
final expandableController = ExpandableController(); final expandableController = ExpandableController();
final rotateIconController = RotateIconController(); final rotateIconController = RotateIconController();
late final List<String> tokenContractAddresses;
@override
void initState() {
final ethWallet =
ref.read(pWallets).getWallet(widget.walletId) as EthereumWallet;
tokenContractAddresses = ethWallet.info.tokenContractAddresses;
super.initState();
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -130,7 +118,7 @@ class _MasterWalletCardState extends ConsumerState<MasterWalletCard> {
popPrevious: true, popPrevious: true,
), ),
), ),
...tokenContractAddresses.map( ...ref.watch(pWalletTokenAddresses(widget.walletId)).map(
(e) => Padding( (e) => Padding(
padding: const EdgeInsets.only( padding: const EdgeInsets.only(
left: 7, left: 7,