From 928676e2cdeed68a67324d5a4fbfb7b0513f51a2 Mon Sep 17 00:00:00 2001 From: Julian Date: Sun, 3 Aug 2025 11:51:22 -0600 Subject: [PATCH 1/5] dialog safe area fix --- lib/main.dart | 2 +- lib/widgets/stack_dialog.dart | 128 +++++++++++++++------------------- 2 files changed, 57 insertions(+), 73 deletions(-) diff --git a/lib/main.dart b/lib/main.dart index 0eb4c224c..6a042070d 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -217,7 +217,7 @@ void main(List args) async { await CampfireMigration.init(); } - if (kDebugMode) { + if (kDebugMode && !Platform.isIOS) { unawaited( MwebdService.instance .logsStream(CryptoCurrencyNetwork.main) diff --git a/lib/widgets/stack_dialog.dart b/lib/widgets/stack_dialog.dart index ff921d24c..f601330b6 100644 --- a/lib/widgets/stack_dialog.dart +++ b/lib/widgets/stack_dialog.dart @@ -28,39 +28,37 @@ class StackDialogBase extends StatelessWidget { @override Widget build(BuildContext context) { - return Padding( - padding: EdgeInsets.only( - top: 16, - left: 16, - right: 16, - bottom: 16 + keyboardPaddingAmount, - ), - child: Column( - mainAxisAlignment: - !Util.isDesktop ? MainAxisAlignment.end : MainAxisAlignment.center, - children: [ - Flexible( - child: SingleChildScrollView( - child: Material( - borderRadius: BorderRadius.circular( - 20, - ), - child: Container( - decoration: BoxDecoration( - color: Theme.of(context).extension()!.popupBG, - borderRadius: BorderRadius.circular( - 20, + return SafeArea( + child: Padding( + padding: EdgeInsets.only( + top: 16, + left: 16, + right: 16, + bottom: 16 + keyboardPaddingAmount, + ), + child: Column( + mainAxisAlignment: + !Util.isDesktop + ? MainAxisAlignment.end + : MainAxisAlignment.center, + children: [ + Flexible( + child: SingleChildScrollView( + child: Material( + borderRadius: BorderRadius.circular(20), + child: Container( + decoration: BoxDecoration( + color: + Theme.of(context).extension()!.popupBG, + borderRadius: BorderRadius.circular(20), ), - ), - child: Padding( - padding: padding, - child: child, + child: Padding(padding: padding, child: child), ), ), ), ), - ), - ], + ], + ), ), ); } @@ -102,10 +100,7 @@ class StackDialog extends StatelessWidget { icon != null ? icon! : Container(), ], ), - if (message != null) - const SizedBox( - height: 8, - ), + if (message != null) const SizedBox(height: 8), if (message != null) Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -117,18 +112,14 @@ class StackDialog extends StatelessWidget { ], ), if (leftButton != null || rightButton != null) - const SizedBox( - height: 20, - ), + const SizedBox(height: 20), if (leftButton != null || rightButton != null) Row( children: [ leftButton == null ? const Spacer() : Expanded(child: leftButton!), - const SizedBox( - width: 8, - ), + const SizedBox(width: 8), rightButton == null ? const Spacer() : Expanded(child: rightButton!), @@ -174,23 +165,18 @@ class StackOkDialog extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Flexible( - child: Text( - title, - style: STextStyles.pageTitleH2(context), - ), + child: Text(title, style: STextStyles.pageTitleH2(context)), ), icon != null ? icon! : Container(), ], ), ), - if (message != null) - const SizedBox( - height: 8, - ), + if (message != null) const SizedBox(height: 8), if (message != null) ConstrainedBox( - constraints: - BoxConstraints(maxWidth: maxWidth ?? double.infinity), + constraints: BoxConstraints( + maxWidth: maxWidth ?? double.infinity, + ), child: Row( children: [ Flexible( @@ -202,9 +188,7 @@ class StackOkDialog extends StatelessWidget { ], ), ), - const SizedBox( - height: 20, - ), + const SizedBox(height: 20), ConstrainedBox( constraints: BoxConstraints(maxWidth: maxWidth ?? double.infinity), child: Row( @@ -212,33 +196,33 @@ class StackOkDialog extends StatelessWidget { leftButton == null ? const Spacer() : Expanded(child: leftButton!), - const SizedBox( - width: 8, - ), + const SizedBox(width: 8), Expanded( child: TextButton( - onPressed: !Util.isDesktop - ? () { - Navigator.of(context).pop(); - onOkPressed?.call("OK"); - } - : () { - if (desktopPopRootNavigator) { - Navigator.of(context, rootNavigator: true).pop(); - } else { - int count = 0; - Navigator.of(context) - .popUntil((_) => count++ >= 2); - // onOkPressed?.call("OK"); + onPressed: + !Util.isDesktop + ? () { + Navigator.of(context).pop(); + onOkPressed?.call("OK"); } - }, + : () { + if (desktopPopRootNavigator) { + Navigator.of( + context, + rootNavigator: true, + ).pop(); + } else { + int count = 0; + Navigator.of( + context, + ).popUntil((_) => count++ >= 2); + // onOkPressed?.call("OK"); + } + }, style: Theme.of(context) .extension()! .getPrimaryEnabledButtonStyle(context), - child: Text( - "Ok", - style: STextStyles.button(context), - ), + child: Text("Ok", style: STextStyles.button(context)), ), ), ], From f01ce8095f2cad83cc6637f0fdf5f161e304a9f2 Mon Sep 17 00:00:00 2001 From: Julian Date: Mon, 4 Aug 2025 09:52:49 -0600 Subject: [PATCH 2/5] eth related navigation fixes --- lib/main.dart | 9 +- .../verify_recovery_phrase_view.dart | 20 +- lib/pages/wallets_view/wallets_overview.dart | 213 +++++++++--------- .../global/global_nav_key_provider.dart | 4 + lib/providers/providers.dart | 1 + 5 files changed, 122 insertions(+), 125 deletions(-) create mode 100644 lib/providers/global/global_nav_key_provider.dart diff --git a/lib/main.dart b/lib/main.dart index 6a042070d..c75000950 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -344,7 +344,6 @@ class MaterialAppWithTheme extends ConsumerStatefulWidget { class _MaterialAppWithThemeState extends ConsumerState with WidgetsBindingObserver { static const platform = MethodChannel("STACK_WALLET_RESTORE"); - final GlobalKey navigatorKey = GlobalKey(); // late final Wallets _wallets; // late final Prefs _prefs; @@ -659,10 +658,10 @@ class _MaterialAppWithThemeState extends ConsumerState Future goToRestoreSWB(String encrypted) async { if (!ref.read(prefsChangeNotifierProvider).hasPin) { await Navigator.of( - navigatorKey.currentContext!, + ref.read(pNavKey).currentContext!, ).pushNamed(CreatePinView.routeName, arguments: true).then((value) { if (value is! bool || value == false) { - Navigator.of(navigatorKey.currentContext!).pushNamed( + Navigator.of(ref.read(pNavKey).currentContext!).pushNamed( RestoreFromEncryptedStringView.routeName, arguments: encrypted, ); @@ -671,7 +670,7 @@ class _MaterialAppWithThemeState extends ConsumerState } else { unawaited( Navigator.push( - navigatorKey.currentContext!, + ref.read(pNavKey).currentContext!, RouteGenerator.getRoute( shouldUseMaterialRoute: RouteGenerator.useMaterialPageRoute, builder: @@ -711,7 +710,7 @@ class _MaterialAppWithThemeState extends ConsumerState return MaterialApp( key: GlobalKey(), - navigatorKey: navigatorKey, + navigatorKey: ref.read(pNavKey), title: AppConfig.appName, onGenerateRoute: RouteGenerator.generateRoute, theme: ThemeData( diff --git a/lib/pages/add_wallet_views/verify_recovery_phrase_view/verify_recovery_phrase_view.dart b/lib/pages/add_wallet_views/verify_recovery_phrase_view/verify_recovery_phrase_view.dart index c562ec88e..9d79a315e 100644 --- a/lib/pages/add_wallet_views/verify_recovery_phrase_view/verify_recovery_phrase_view.dart +++ b/lib/pages/add_wallet_views/verify_recovery_phrase_view/verify_recovery_phrase_view.dart @@ -95,11 +95,6 @@ class _VerifyRecoveryPhraseViewState super.initState(); } - @override - dispose() { - super.dispose(); - } - Future _verifyMnemonicPassphrase() async { final result = await showDialog( context: context, @@ -391,12 +386,15 @@ class _VerifyRecoveryPhraseViewState ).pushNamedAndRemoveUntil(HomeView.routeName, (route) => false), ); if (_coin is Ethereum) { - unawaited( - Navigator.of(context).pushNamed( - EditWalletTokensView.routeName, - arguments: widget.wallet.walletId, - ), - ); + WidgetsBinding.instance.addPostFrameCallback((_) { + ref + .read(pNavKey) + .currentState + ?.pushNamed( + EditWalletTokensView.routeName, + arguments: widget.wallet.walletId, + ); + }); } } } diff --git a/lib/pages/wallets_view/wallets_overview.dart b/lib/pages/wallets_view/wallets_overview.dart index 95e38b07d..7ba524713 100644 --- a/lib/pages/wallets_view/wallets_overview.dart +++ b/lib/pages/wallets_view/wallets_overview.dart @@ -17,7 +17,6 @@ import '../../app_config.dart'; import '../../models/add_wallet_list_entity/sub_classes/coin_entity.dart'; import '../../models/isar/models/ethereum/eth_contract.dart'; import '../../pages_desktop_specific/my_stack_view/dialogs/desktop_expanding_wallet_card.dart'; -import '../../providers/db/main_db_provider.dart'; import '../../providers/providers.dart'; import '../../services/event_bus/events/wallet_added_event.dart'; import '../../services/event_bus/global_event_bus.dart'; @@ -28,6 +27,7 @@ import '../../utilities/text_styles.dart'; import '../../utilities/util.dart'; import '../../wallets/crypto_currency/crypto_currency.dart'; import '../../wallets/isar/models/wallet_info.dart'; +import '../../wallets/isar/providers/all_wallets_info_provider.dart'; import '../../wallets/isar/providers/wallet_info_provider.dart'; import '../../wallets/wallet/wallet.dart'; import '../../widgets/background.dart'; @@ -73,13 +73,14 @@ class _EthWalletsOverviewState extends ConsumerState { List _filter(String searchTerm) { // clean out deleted wallets - final existingWalletIds = ref - .read(mainDBProvider) - .isar - .walletInfo - .where() - .walletIdProperty() - .findAllSync(); + final existingWalletIds = + ref + .read(mainDBProvider) + .isar + .walletInfo + .where() + .walletIdProperty() + .findAllSync(); wallets.removeWhere((k, v) => !existingWalletIds.contains(k)); if (searchTerm.isEmpty) { @@ -127,25 +128,22 @@ class _EthWalletsOverviewState extends ConsumerState { } void updateWallets() { - final walletsData = - ref.read(mainDBProvider).isar.walletInfo.where().findAllSync(); + final walletsData = ref.read(pAllWalletsInfo); + walletsData.removeWhere((e) => e.coin != widget.coin); if (widget.coin is Ethereum) { for (final data in walletsData) { final List contracts = []; - final contractAddresses = - ref.read(pWalletTokenAddresses(data.walletId)); + final contractAddresses = ref.read( + pWalletTokenAddresses(data.walletId), + ); // fetch each contract for (final contractAddress in contractAddresses) { final contract = ref - .read( - mainDBProvider, - ) - .getEthContractSync( - contractAddress, - ); + .read(mainDBProvider) + .getEthContractSync(contractAddress); // add it to list if it exists in DB if (contract != null) { @@ -155,9 +153,7 @@ class _EthWalletsOverviewState extends ConsumerState { // add tuple to list wallets[data.walletId] = ( - wallet: ref.read(pWallets).getWallet( - data.walletId, - ), + wallet: ref.read(pWallets).getWallet(data.walletId), contracts: contracts, ); } @@ -167,9 +163,7 @@ class _EthWalletsOverviewState extends ConsumerState { // desktop single coin apps may cause issues so lets just ignore the error and move on try { wallets[data.walletId] = ( - wallet: ref.read(pWallets).getWallet( - data.walletId, - ), + wallet: ref.read(pWallets).getWallet(data.walletId), contracts: [], ); } catch (_) { @@ -211,46 +205,45 @@ class _EthWalletsOverviewState extends ConsumerState { Widget build(BuildContext context) { return ConditionalParent( condition: !isDesktop && !AppConfig.isSingleCoinApp, - builder: (child) => Background( - child: Scaffold( - backgroundColor: - Theme.of(context).extension()!.background, - appBar: AppBar( - leading: const AppBarBackButton(), - title: Text( - "${widget.coin.prettyName} (${widget.coin.ticker}) wallets", - style: STextStyles.navBarTitle(context), - ), - actions: [ - AspectRatio( - aspectRatio: 1, - child: AppBarIconButton( - icon: SvgPicture.asset( - Assets.svg.plus, - width: 18, - height: 18, - color: Theme.of(context) - .extension()! - .topNavIconPrimary, - ), - onPressed: () { - Navigator.of(context).pushNamed( - CreateOrRestoreWalletView.routeName, - arguments: CoinEntity(widget.coin), - ); - }, + builder: + (child) => Background( + child: Scaffold( + backgroundColor: + Theme.of(context).extension()!.background, + appBar: AppBar( + leading: const AppBarBackButton(), + title: Text( + "${widget.coin.prettyName} (${widget.coin.ticker}) wallets", + style: STextStyles.navBarTitle(context), ), + actions: [ + AspectRatio( + aspectRatio: 1, + child: AppBarIconButton( + icon: SvgPicture.asset( + Assets.svg.plus, + width: 18, + height: 18, + color: + Theme.of( + context, + ).extension()!.topNavIconPrimary, + ), + onPressed: () { + Navigator.of(context).pushNamed( + CreateOrRestoreWalletView.routeName, + arguments: CoinEntity(widget.coin), + ); + }, + ), + ), + ], + ), + body: SafeArea( + child: Padding(padding: const EdgeInsets.all(16), child: child), ), - ], - ), - body: SafeArea( - child: Padding( - padding: const EdgeInsets.all(16), - child: child, ), ), - ), - ), child: Column( children: [ ClipRRect( @@ -267,14 +260,16 @@ class _EthWalletsOverviewState extends ConsumerState { _searchString = value; }); }, - style: isDesktop - ? STextStyles.desktopTextExtraSmall(context).copyWith( - color: Theme.of(context) - .extension()! - .textFieldActiveText, - height: 1.8, - ) - : STextStyles.field(context), + style: + isDesktop + ? STextStyles.desktopTextExtraSmall(context).copyWith( + color: + Theme.of( + context, + ).extension()!.textFieldActiveText, + height: 1.8, + ) + : STextStyles.field(context), decoration: standardInputDecoration( "Search...", searchFieldFocusNode, @@ -292,32 +287,31 @@ class _EthWalletsOverviewState extends ConsumerState { height: isDesktop ? 20 : 16, ), ), - suffixIcon: _searchController.text.isNotEmpty - ? Padding( - padding: const EdgeInsets.only(right: 0), - child: UnconstrainedBox( - child: Row( - children: [ - TextFieldIconButton( - child: const XIcon(), - onTap: () async { - setState(() { - _searchController.text = ""; - _searchString = ""; - }); - }, - ), - ], + suffixIcon: + _searchController.text.isNotEmpty + ? Padding( + padding: const EdgeInsets.only(right: 0), + child: UnconstrainedBox( + child: Row( + children: [ + TextFieldIconButton( + child: const XIcon(), + onTap: () async { + setState(() { + _searchController.text = ""; + _searchString = ""; + }); + }, + ), + ], + ), ), - ), - ) - : null, + ) + : null, ), ), ), - const SizedBox( - height: 16, - ), + const SizedBox(height: 16), Expanded( child: Builder( builder: (context) { @@ -346,33 +340,34 @@ class _EthWalletsOverviewState extends ConsumerState { return ConditionalParent( key: Key(wallet.walletId), condition: isDesktop, - builder: (child) => RoundedWhiteContainer( - padding: const EdgeInsets.symmetric( - vertical: 14, - horizontal: 20, - ), - borderColor: Theme.of(context) - .extension()! - .backgroundAppBar, - child: child, - ), + builder: + (child) => RoundedWhiteContainer( + padding: const EdgeInsets.symmetric( + vertical: 14, + horizontal: 20, + ), + borderColor: + Theme.of( + context, + ).extension()!.backgroundAppBar, + child: child, + ), child: SimpleWalletCard( walletId: wallet.walletId, - popPrevious: widget - .overrideSimpleWalletCardPopPreviousValueWith == - null - ? isDesktop - : widget - .overrideSimpleWalletCardPopPreviousValueWith!, + popPrevious: + widget.overrideSimpleWalletCardPopPreviousValueWith == + null + ? isDesktop + : widget + .overrideSimpleWalletCardPopPreviousValueWith!, desktopNavigatorState: isDesktop ? widget.navigatorState : null, ), ); } }, - separatorBuilder: (_, __) => SizedBox( - height: isDesktop ? 10 : 8, - ), + separatorBuilder: + (_, __) => SizedBox(height: isDesktop ? 10 : 8), itemCount: data.length, ); }, diff --git a/lib/providers/global/global_nav_key_provider.dart b/lib/providers/global/global_nav_key_provider.dart new file mode 100644 index 000000000..1cf0ad7ba --- /dev/null +++ b/lib/providers/global/global_nav_key_provider.dart @@ -0,0 +1,4 @@ +import 'package:flutter/widgets.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +final pNavKey = Provider((_) => GlobalKey()); diff --git a/lib/providers/providers.dart b/lib/providers/providers.dart index d86e4de3b..598f3a3d0 100644 --- a/lib/providers/providers.dart +++ b/lib/providers/providers.dart @@ -27,6 +27,7 @@ export './global/mweb_service_provider.dart'; export './global/node_service_provider.dart'; export './global/notifications_provider.dart'; export './global/prefs_provider.dart'; +export './global/global_nav_key_provider.dart'; export './global/price_provider.dart'; export './global/should_show_lockscreen_on_resume_state_provider.dart'; export './global/wallets_provider.dart'; From fed6ff39ae2a7b424f2c5b4d4bcc9377d3045023 Mon Sep 17 00:00:00 2001 From: julian Date: Tue, 5 Aug 2025 07:26:09 -0600 Subject: [PATCH 3/5] mweb fee calc bandaid and rounding fix --- .../wallet/wallet_mixin_interfaces/mweb_interface.dart | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/wallets/wallet/wallet_mixin_interfaces/mweb_interface.dart b/lib/wallets/wallet/wallet_mixin_interfaces/mweb_interface.dart index 4480ef81b..c7fded386 100644 --- a/lib/wallets/wallet/wallet_mixin_interfaces/mweb_interface.dart +++ b/lib/wallets/wallet/wallet_mixin_interfaces/mweb_interface.dart @@ -952,13 +952,14 @@ mixin MwebInterface BigInt feeIncrease = posOutputSum - expectedPegin; if (expectedPegin > BigInt.zero) { - feeIncrease += BigInt.from( - (txData.feeRateAmount! / BigInt.from(1000) * 41).ceil(), - ); + feeIncrease += + BigInt.from((txData.feeRateAmount! / BigInt.from(1000)).ceil()) * + BigInt.from(41); } + // bandaid: add one to account for a rounding error that happens sometimes return Amount( - rawValue: fee + feeIncrease, + rawValue: fee + feeIncrease + BigInt.one, fractionDigits: cryptoCurrency.fractionDigits, ); } From 6c7231cc09dc1ae4f20d0544d607609e0dd30cdd Mon Sep 17 00:00:00 2001 From: julian Date: Tue, 5 Aug 2025 08:04:32 -0600 Subject: [PATCH 4/5] mweb pegout send all error fix --- .../wallet/wallet_mixin_interfaces/electrumx_interface.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/wallets/wallet/wallet_mixin_interfaces/electrumx_interface.dart b/lib/wallets/wallet/wallet_mixin_interfaces/electrumx_interface.dart index e978a0bd7..fd9b67ccb 100644 --- a/lib/wallets/wallet/wallet_mixin_interfaces/electrumx_interface.dart +++ b/lib/wallets/wallet/wallet_mixin_interfaces/electrumx_interface.dart @@ -276,7 +276,7 @@ mixin ElectrumXInterface // hack check if (!(txData.type == TxType.mwebPegIn || - (txData.type == TxType.mweb && overrideFeeAmount != null))) { + (txData.type.isMweb() && overrideFeeAmount != null))) { throw Exception( "Something happened that should never actually happen. " "Please report this error to the developers.", From d13acd62645c8cfec9142bc68874cb4c3f21eb9b Mon Sep 17 00:00:00 2001 From: julian Date: Tue, 5 Aug 2025 11:39:58 -0600 Subject: [PATCH 5/5] Silly hack(?) to temporarily show mweb spends. Note: Unless wallet is open when the transaction confirms the tx details view will show the wrong transaction height. --- lib/wallets/wallet/impl/litecoin_wallet.dart | 4 ++ .../electrumx_interface.dart | 17 ++++++- .../mweb_interface.dart | 50 +++++++++++++++++++ 3 files changed, 70 insertions(+), 1 deletion(-) diff --git a/lib/wallets/wallet/impl/litecoin_wallet.dart b/lib/wallets/wallet/impl/litecoin_wallet.dart index 76205c252..740710d1d 100644 --- a/lib/wallets/wallet/impl/litecoin_wallet.dart +++ b/lib/wallets/wallet/impl/litecoin_wallet.dart @@ -361,6 +361,10 @@ class LitecoinWallet } await mainDB.updateOrPutTransactionV2s(txns); + + if (info.isMwebEnabled) { + await checkMwebSpends(); + } } @override diff --git a/lib/wallets/wallet/wallet_mixin_interfaces/electrumx_interface.dart b/lib/wallets/wallet/wallet_mixin_interfaces/electrumx_interface.dart index fd9b67ccb..dbfc8d45f 100644 --- a/lib/wallets/wallet/wallet_mixin_interfaces/electrumx_interface.dart +++ b/lib/wallets/wallet/wallet_mixin_interfaces/electrumx_interface.dart @@ -907,8 +907,23 @@ mixin ElectrumXInterface // dirty shortcut for peercoin's weirdness vSize: this is PeercoinWallet ? clTx.size : clTx.vSize(), tempTx: - txData.type.isMweb() + txData.type == TxType.mwebPegIn ? null + : txData.type.isMweb() + ? TransactionV2( + walletId: walletId, + blockHash: null, + hash: clTx.hashHex, + txid: clTx.txid, + height: null, + timestamp: DateTime.timestamp().millisecondsSinceEpoch ~/ 1000, + inputs: List.unmodifiable(tempInputs), + outputs: List.unmodifiable(tempOutputs), + version: clTx.version, + type: TransactionType.outgoing, + subType: TransactionSubType.mweb, + otherData: null, + ) : TransactionV2( walletId: walletId, blockHash: null, diff --git a/lib/wallets/wallet/wallet_mixin_interfaces/mweb_interface.dart b/lib/wallets/wallet/wallet_mixin_interfaces/mweb_interface.dart index c7fded386..1f7babc79 100644 --- a/lib/wallets/wallet/wallet_mixin_interfaces/mweb_interface.dart +++ b/lib/wallets/wallet/wallet_mixin_interfaces/mweb_interface.dart @@ -358,6 +358,56 @@ mixin MwebInterface ); } + Future checkMwebSpends() async { + final pending = + await mainDB.isar.transactionV2s + .where() + .walletIdEqualTo(walletId) + .filter() + .heightIsNull() + .and() + .blockHashIsNull() + .and() + .subTypeEqualTo(TransactionSubType.mweb) + .and() + .typeEqualTo(TransactionType.outgoing) + .findAll(); + + Logging.instance.f(pending); + + final client = await _client; + for (final tx in pending) { + for (final input in tx.inputs) { + if (input.addresses.length == 1) { + final address = await mainDB.getAddress( + walletId, + input.addresses.first, + ); + if (address?.type == AddressType.mweb) { + final response = await client.spent( + SpentRequest(outputId: [input.outpoint!.txid]), + ); + if (response.outputId.contains(input.outpoint!.txid)) { + // dummy to show tx as confirmed. Need a better way to handle this as its kind of stupid, resulting in terrible UX + final dummyHeight = await chainHeight; + + TransactionV2? transaction = + await mainDB.isar.transactionV2s + .where() + .txidWalletIdEqualTo(tx.txid, walletId) + .findFirst(); + + if (transaction == null || transaction.height == null) { + transaction = (transaction ?? tx).copyWith(height: dummyHeight); + await mainDB.updateOrPutTransactionV2s([transaction]); + } + } + } + } + } + } + } + Future processMwebTransaction(TxData txData) async { final client = await _client; final response = await client.create(