diff --git a/mobile-app/assets/v2/axe.svg b/mobile-app/assets/v2/axe.svg
new file mode 100644
index 00000000..8c267333
--- /dev/null
+++ b/mobile-app/assets/v2/axe.svg
@@ -0,0 +1,3 @@
+
diff --git a/mobile-app/lib/features/components/get_started.dart b/mobile-app/lib/features/components/get_started.dart
index fef34d44..58e6f1ae 100644
--- a/mobile-app/lib/features/components/get_started.dart
+++ b/mobile-app/lib/features/components/get_started.dart
@@ -2,8 +2,8 @@ import 'package:flutter/material.dart';
import 'package:quantus_sdk/quantus_sdk.dart';
import 'package:resonance_network_wallet/features/styles/app_colors_theme.dart';
import 'package:resonance_network_wallet/features/styles/app_text_theme.dart';
+import 'package:resonance_network_wallet/shared/utils/open_external_url.dart';
import 'package:resonance_network_wallet/utils/url_utils.dart';
-import 'package:url_launcher/url_launcher.dart';
class GetStarted extends StatelessWidget {
const GetStarted({super.key});
@@ -33,26 +33,17 @@ class GetStarted extends StatelessWidget {
),
const SizedBox(height: 25),
GestureDetector(
- onTap: () {
- final Uri url = Uri.parse(AppConstants.tutorialsAndGuidesUrl);
- launchUrl(url);
- },
+ onTap: () => openUrl(AppConstants.tutorialsAndGuidesUrl),
child: Text('Tutorials & Guides →', style: context.themeText.smallParagraph),
),
const SizedBox(height: 25),
GestureDetector(
- onTap: () {
- final Uri url = Uri.parse(AppConstants.communityUrl);
- launchUrl(url);
- },
+ onTap: () => openUrl(AppConstants.communityUrl),
child: Text('Community →', style: context.themeText.smallParagraph),
),
const SizedBox(height: 25),
GestureDetector(
- onTap: () {
- final Uri url = Uri.parse(AppConstants.techSupportUrl);
- launchUrl(url);
- },
+ onTap: () => openUrl(AppConstants.techSupportUrl),
child: Text('Tech Support →', style: context.themeText.smallParagraph),
),
],
diff --git a/mobile-app/lib/features/components/link_text.dart b/mobile-app/lib/features/components/link_text.dart
index e9331513..567f8537 100644
--- a/mobile-app/lib/features/components/link_text.dart
+++ b/mobile-app/lib/features/components/link_text.dart
@@ -1,6 +1,6 @@
import 'package:flutter/material.dart';
import 'package:resonance_network_wallet/features/styles/app_text_theme.dart';
-import 'package:url_launcher/url_launcher.dart';
+import 'package:resonance_network_wallet/shared/utils/open_external_url.dart';
class LinkText extends StatelessWidget {
final String label;
@@ -19,7 +19,7 @@ class LinkText extends StatelessWidget {
child: Text(label, style: effectiveTextStyle),
onTap: () {
final Uri uri = Uri.parse(url);
- launchUrl(uri);
+ openUrl(uri.toString());
},
);
}
diff --git a/mobile-app/lib/features/components/reversible_transaction_action_sheet.dart b/mobile-app/lib/features/components/reversible_transaction_action_sheet.dart
index ef04518f..822e4028 100644
--- a/mobile-app/lib/features/components/reversible_transaction_action_sheet.dart
+++ b/mobile-app/lib/features/components/reversible_transaction_action_sheet.dart
@@ -16,8 +16,8 @@ import 'package:resonance_network_wallet/providers/wallet_providers.dart';
import 'package:resonance_network_wallet/services/reversible_transfer_monitoring_service.dart';
import 'package:resonance_network_wallet/shared/extensions/media_query_data_extension.dart';
import 'package:resonance_network_wallet/shared/extensions/toaster_extensions.dart';
+import 'package:resonance_network_wallet/shared/utils/open_external_url.dart';
import 'package:resonance_network_wallet/shared/utils/tx_filter_family_provider.dart';
-import 'package:url_launcher/url_launcher.dart';
enum ReversibleTransactionMode { reversible, guardianIntercept }
@@ -310,7 +310,7 @@ class _ReversibleTransactionActionSheetState extends ConsumerState((ref) => MiningRewardsService());
@@ -8,11 +9,28 @@ final miningRewardsServiceProvider = Provider((ref) => Min
final miningRewardsProvider = FutureProvider((ref) async {
final service = ref.watch(miningRewardsServiceProvider);
final accounts = ref.watch(accountsProvider).value;
+
if (accounts == null || accounts.isEmpty) {
- return const MiningRewardsData(resonanceBlocks: 0, schrodingerBlocks: 0, diracBlocks: 0, planckBlocks: 0);
+ return MiningRewardsData(
+ resonanceBlocks: 0,
+ schrodingerBlocks: 0,
+ diracBlocks: 0,
+ planckBlocks: 0,
+ planckRewards: BigInt.zero,
+ redeemedRewards: BigInt.zero,
+ redeemableRewards: BigInt.zero,
+ );
+ }
+
+ final mnemonic = await ref.watch(settingsServiceProvider).getMnemonic(0);
+ if (mnemonic == null) {
+ throw Exception('Mnemonic not found!');
}
+ final keyPair = ref.watch(hdWalletServiceProvider).deriveWormholeKeyPair(mnemonic: mnemonic);
+
final oldMiningAccountId = await TaskmasterService().getOldMiningAccountId();
final accountsList = accounts.map((a) => a.accountId).toList();
accountsList.add(oldMiningAccountId);
- return service.getMiningRewards(accountsList);
+
+ return service.getMiningRewards(ref, keyPair, accountsList);
});
diff --git a/mobile-app/lib/providers/wallet_providers.dart b/mobile-app/lib/providers/wallet_providers.dart
index 7ee0fd05..f04e57b9 100644
--- a/mobile-app/lib/providers/wallet_providers.dart
+++ b/mobile-app/lib/providers/wallet_providers.dart
@@ -53,6 +53,14 @@ final highSecurityServiceProvider = Provider((ref) {
return HighSecurityService();
});
+final hdWalletServiceProvider = Provider((ref) {
+ return HdWalletService();
+});
+
+final wormholeUtxoServiceProvider = Provider((ref) {
+ return WormholeUtxoService();
+});
+
final isHighSecurityProvider = FutureProvider.family((ref, account) async {
final highSecurityService = ref.watch(highSecurityServiceProvider);
return await highSecurityService.isHighSecurity(account);
diff --git a/mobile-app/lib/services/mining_rewards_service.dart b/mobile-app/lib/services/mining_rewards_service.dart
index 6bc6b4f1..074f17bf 100644
--- a/mobile-app/lib/services/mining_rewards_service.dart
+++ b/mobile-app/lib/services/mining_rewards_service.dart
@@ -1,7 +1,9 @@
import 'dart:convert';
import 'package:flutter/services.dart';
+import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:quantus_sdk/quantus_sdk.dart';
+import 'package:resonance_network_wallet/providers/wallet_providers.dart';
import 'package:resonance_network_wallet/utils/env_utils.dart';
class MiningRewardsData {
@@ -9,12 +11,18 @@ class MiningRewardsData {
final int schrodingerBlocks;
final int diracBlocks;
final int planckBlocks;
+ final BigInt planckRewards;
+ final BigInt redeemedRewards;
+ final BigInt redeemableRewards;
const MiningRewardsData({
required this.resonanceBlocks,
required this.schrodingerBlocks,
required this.diracBlocks,
required this.planckBlocks,
+ required this.planckRewards,
+ required this.redeemedRewards,
+ required this.redeemableRewards,
});
int get totalBlocks => resonanceBlocks + schrodingerBlocks + diracBlocks + planckBlocks;
@@ -33,8 +41,9 @@ class MiningRewardsService {
_cachedAccountIds = null;
}
- Future getMiningRewards(List currentAccountIds) async {
+ Future getMiningRewards(Ref ref, WormholeKeyPair keyPair, List currentAccountIds) async {
print('[MiningRewards] Current account IDs: $currentAccountIds');
+ final wormholeUtxoService = ref.read(wormholeUtxoServiceProvider);
final miners = >{};
for (final entry in _assets.entries) {
@@ -49,22 +58,23 @@ class MiningRewardsService {
final resonance = _countBlocks('resonance', miners['resonance']!, allAccountIds);
final schrodinger = _countBlocks('schrodinger', miners['schrodinger']!, allAccountIds);
final dirac = _countBlocks('dirac', miners['dirac']!, allAccountIds);
- final planck = await _fetchPlanckBlocks(allAccountIds);
+ final (planckStats, redeemableRewards) = await (
+ TaskmasterService().getMinerStats(),
+ wormholeUtxoService.getUnspentBalance(wormholeAddress: keyPair.address, secretHex: keyPair.secretHex),
+ ).wait;
+ final redeemedRewards = planckStats.totalRewards - redeemableRewards;
- print('[MiningRewards] Resonance: $resonance, Schrödinger: $schrodinger, Dirac: $dirac, Planck: $planck');
return MiningRewardsData(
resonanceBlocks: resonance,
schrodingerBlocks: schrodinger,
diracBlocks: dirac,
- planckBlocks: planck,
+ planckBlocks: planckStats.totalMinedBlocks,
+ planckRewards: planckStats.totalRewards,
+ redeemedRewards: redeemedRewards,
+ redeemableRewards: redeemableRewards,
);
}
- Future _fetchPlanckBlocks(Set accountIds) async {
- final minerStats = await TaskmasterService().getMinerStats();
- return minerStats.totalMinedBlocks;
- }
-
List<_MinerEntry> _parseMiners(String jsonStr) {
final decoded = jsonDecode(jsonStr);
final stats = decoded['data']['minerStats'] as List;
diff --git a/mobile-app/lib/shared/utils/open_external_url.dart b/mobile-app/lib/shared/utils/open_external_url.dart
new file mode 100644
index 00000000..6140c55a
--- /dev/null
+++ b/mobile-app/lib/shared/utils/open_external_url.dart
@@ -0,0 +1,13 @@
+import 'package:url_launcher/url_launcher.dart';
+
+Future openUrl(String urlString, {LaunchMode mode = LaunchMode.platformDefault}) async {
+ final uri = Uri.parse(urlString);
+ try {
+ final launched = await launchUrl(uri, mode: mode);
+ if (!launched) {
+ print('launchUrl returned false: $urlString');
+ }
+ } catch (e, st) {
+ print('launchUrl failed: $urlString error=$e\n$st');
+ }
+}
diff --git a/mobile-app/lib/v2/screens/accounts/account_ready_screen.dart b/mobile-app/lib/v2/screens/accounts/account_ready_screen.dart
index 2c173c72..fc4b1959 100644
--- a/mobile-app/lib/v2/screens/accounts/account_ready_screen.dart
+++ b/mobile-app/lib/v2/screens/accounts/account_ready_screen.dart
@@ -25,7 +25,6 @@ class AccountReadyScreen extends StatelessWidget {
final String checksumPhrase;
final String accountId;
- static const _galleryLargeTitle = Color(0xFFEBEBEB);
static const _successRingSize = 78.0;
static const _checkIconSize = 32.0;
static const _borderWidth = 2.0;
@@ -92,7 +91,7 @@ class AccountReadyScreen extends StatelessWidget {
Text(
headline,
textAlign: TextAlign.center,
- style: text.paragraph?.copyWith(fontSize: 32, color: _galleryLargeTitle, height: 1.0),
+ style: text.paragraph?.copyWith(fontSize: 32, color: colors.textLightGray, height: 1.0),
),
],
),
diff --git a/mobile-app/lib/v2/screens/activity/transaction_detail_sheet.dart b/mobile-app/lib/v2/screens/activity/transaction_detail_sheet.dart
index fbc0a4c8..ef5ef693 100644
--- a/mobile-app/lib/v2/screens/activity/transaction_detail_sheet.dart
+++ b/mobile-app/lib/v2/screens/activity/transaction_detail_sheet.dart
@@ -5,11 +5,11 @@ import 'package:resonance_network_wallet/features/components/dotted_border.dart'
import 'package:resonance_network_wallet/providers/currency_display_provider.dart';
import 'package:resonance_network_wallet/providers/wallet_providers.dart';
import 'package:resonance_network_wallet/shared/extensions/transaction_event_extension.dart';
+import 'package:resonance_network_wallet/shared/utils/open_external_url.dart';
import 'package:resonance_network_wallet/v2/components/amount_display_with_conversion.dart';
import 'package:resonance_network_wallet/v2/components/bottom_sheet_container.dart';
import 'package:resonance_network_wallet/v2/theme/app_colors.dart';
import 'package:resonance_network_wallet/v2/theme/app_text_styles.dart';
-import 'package:url_launcher/url_launcher.dart';
void showTransactionDetailSheet(BuildContext context, TransactionEvent tx, String activeAccountId) {
BottomSheetContainer.show(
@@ -213,6 +213,6 @@ class _ExplorerLink extends StatelessWidget {
path = '$transactionType/${tx.blockHash}';
}
- if (path != null) launchUrl(Uri.parse('${AppConstants.explorerEndpoint}/$path'));
+ if (path != null) openUrl('${AppConstants.explorerEndpoint}/$path');
}
}
diff --git a/mobile-app/lib/v2/screens/home/activity_section.dart b/mobile-app/lib/v2/screens/home/activity_section.dart
index 48033b6f..275479c0 100644
--- a/mobile-app/lib/v2/screens/home/activity_section.dart
+++ b/mobile-app/lib/v2/screens/home/activity_section.dart
@@ -7,14 +7,11 @@ import 'package:resonance_network_wallet/models/combined_transactions_list.dart'
import 'package:resonance_network_wallet/providers/active_account_transactions_provider.dart';
import 'package:resonance_network_wallet/providers/currency_display_provider.dart';
import 'package:resonance_network_wallet/services/transaction_service.dart';
-import 'package:resonance_network_wallet/utils/url_utils.dart';
import 'package:resonance_network_wallet/v2/screens/activity/activity_screen.dart';
import 'package:resonance_network_wallet/v2/screens/activity/transaction_detail_sheet.dart';
import 'package:resonance_network_wallet/v2/screens/activity/tx_item.dart';
-import 'package:resonance_network_wallet/v2/screens/settings/testnet_rewards_screen.dart';
import 'package:resonance_network_wallet/v2/theme/app_colors.dart';
import 'package:resonance_network_wallet/v2/theme/app_text_styles.dart';
-import 'package:url_launcher/url_launcher.dart';
class ActivitySection extends ConsumerStatefulWidget {
final AsyncValue txAsync;
@@ -28,8 +25,6 @@ class ActivitySection extends ConsumerStatefulWidget {
}
class _ActivitySectionState extends ConsumerState {
- bool _getStartedExpanded = true;
-
@override
Widget build(BuildContext context) {
final formatTxAmount = ref.watch(txAmountDisplayProvider);
@@ -49,13 +44,7 @@ class _ActivitySectionState extends ConsumerState {
if (all.isEmpty) {
return Column(
- children: [
- const SizedBox(height: 40),
- _header(colors, text, context),
- _emptyState(text, colors),
- const SizedBox(height: 40),
- _getStartedSection(text, colors),
- ],
+ children: [const SizedBox(height: 40), _header(colors, text, context), _emptyState(text, colors)],
);
}
@@ -130,90 +119,21 @@ class _ActivitySectionState extends ConsumerState {
padding: const EdgeInsets.symmetric(vertical: 24),
child: Column(
children: [
- Text('No Transactions Yet', style: text.smallParagraph?.copyWith(color: colors.textPrimary)),
- const SizedBox(height: 8),
- Text('Your activity will appear here', style: text.detail?.copyWith(color: colors.textSecondary)),
- ],
- ),
- );
- }
-
- Widget _getStartedSection(AppTextTheme text, AppColorsV2 colors) {
- const links = [
- ('Get Testnet Tokens', AppConstants.faucetUrl),
- ('Community', AppConstants.communityUrl),
- // ('Tech Support', AppConstants.techSupportUrl),
- ];
-
- return Column(
- children: [
- GestureDetector(
- behavior: HitTestBehavior.opaque,
- onTap: () => setState(() => _getStartedExpanded = !_getStartedExpanded),
- child: Row(
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
- children: [
- Text('Get Started', style: text.smallTitle),
- Icon(
- _getStartedExpanded ? Icons.keyboard_arrow_up : Icons.keyboard_arrow_down,
- color: colors.textSecondary,
- size: 16,
- ),
- ],
+ Text(
+ 'No Transactions Yet',
+ style: text.mediumTitle?.copyWith(color: colors.textMuted, fontWeight: FontWeight.w400),
),
- ),
- AnimatedCrossFade(
- firstChild: Padding(
- padding: const EdgeInsets.only(top: 24),
- child: Container(
- width: double.infinity,
- padding: const EdgeInsets.all(20),
- decoration: BoxDecoration(
- color: Colors.white.withValues(alpha: 0.1),
- borderRadius: BorderRadius.circular(14),
- ),
- child: Column(
- children: [
- for (var i = 0; i < links.length; i++) ...[
- GestureDetector(
- behavior: HitTestBehavior.opaque,
- onTap: () => links[i].$2 == AppConstants.faucetUrl
- ? launchXPost(links[i].$2)
- : launchUrl(Uri.parse(links[i].$2)),
- child: Row(
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
- children: [
- Text(links[i].$1, style: text.smallParagraph?.copyWith(color: colors.textPrimary)),
- Icon(Icons.arrow_outward, color: colors.textPrimary, size: 20),
- ],
- ),
- ),
- Padding(
- padding: const EdgeInsets.symmetric(vertical: 12),
- child: Divider(color: colors.separator, height: 0),
- ),
- ],
- GestureDetector(
- behavior: HitTestBehavior.opaque,
- onTap: () =>
- Navigator.push(context, MaterialPageRoute(builder: (_) => const TestnetRewardsScreen())),
- child: Row(
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
- children: [
- Text('Testnet Rewards', style: text.smallParagraph?.copyWith(color: colors.textPrimary)),
- Icon(Icons.chevron_right, color: colors.textPrimary, size: 20),
- ],
- ),
- ),
- ],
- ),
+ const SizedBox(height: 8),
+ ConstrainedBox(
+ constraints: const BoxConstraints(maxWidth: 240),
+ child: Text(
+ 'Your activity will appear here once you send or receive QUAN.',
+ textAlign: TextAlign.center,
+ style: text.smallParagraph?.copyWith(color: colors.txItemIconDefault),
),
),
- secondChild: const SizedBox.shrink(),
- crossFadeState: _getStartedExpanded ? CrossFadeState.showFirst : CrossFadeState.showSecond,
- duration: const Duration(milliseconds: 200),
- ),
- ],
+ ],
+ ),
);
}
diff --git a/mobile-app/lib/v2/screens/home/home_screen.dart b/mobile-app/lib/v2/screens/home/home_screen.dart
index 672ba9a0..0df0ed2a 100644
--- a/mobile-app/lib/v2/screens/home/home_screen.dart
+++ b/mobile-app/lib/v2/screens/home/home_screen.dart
@@ -6,10 +6,12 @@ import 'package:resonance_network_wallet/features/components/dotted_border.dart'
import 'package:resonance_network_wallet/features/components/skeleton.dart';
import 'package:resonance_network_wallet/features/components/shared_address_action_sheet.dart';
import 'package:resonance_network_wallet/providers/remote_config_provider.dart';
+import 'package:resonance_network_wallet/utils/url_utils.dart';
import 'package:resonance_network_wallet/v2/components/amount_display_with_conversion.dart';
import 'package:resonance_network_wallet/v2/components/loader.dart';
import 'package:resonance_network_wallet/v2/components/quantus_button.dart';
import 'package:resonance_network_wallet/v2/components/quantus_icon_button.dart';
+import 'package:resonance_network_wallet/v2/components/scaffold_base_bottom_content.dart';
import 'package:resonance_network_wallet/v2/screens/accounts/open_accounts_management_button.dart';
import 'package:resonance_network_wallet/v2/screens/receive/receive_screen.dart';
import 'package:resonance_network_wallet/v2/screens/send/input_amount_screen.dart';
@@ -87,6 +89,7 @@ class _HomeScreenState extends ConsumerState {
});
final isPosMode = ref.watch(posModeProvider);
+ final balanceAsync = ref.watch(balanceProvider);
final accountAsync = ref.watch(activeAccountProvider);
final txAsync = ref.watch(activeAccountTransactionsProvider(TransactionFilter.all));
final colors = context.colors;
@@ -110,6 +113,18 @@ class _HomeScreenState extends ConsumerState {
ActivitySection(txAsync: txAsync, activeAccount: active.account, onRetry: _refresh),
SizedBox(height: isPosMode ? 120 : 58),
],
+ bottomContent: balanceAsync
+ .whenData(
+ (balance) => balance == BigInt.zero
+ ? ScaffoldBaseBottomContent(
+ child: QuantusButton.simple(
+ label: 'Get Testnet Tokens ↗',
+ onTap: () => launchXPost(AppConstants.faucetUrl),
+ ),
+ )
+ : null,
+ )
+ .value,
);
},
);
diff --git a/mobile-app/lib/v2/screens/settings/about_quantus_screen.dart b/mobile-app/lib/v2/screens/settings/about_quantus_screen.dart
index c0534e64..e9c369b0 100644
--- a/mobile-app/lib/v2/screens/settings/about_quantus_screen.dart
+++ b/mobile-app/lib/v2/screens/settings/about_quantus_screen.dart
@@ -1,13 +1,13 @@
import 'package:flutter/material.dart';
import 'package:quantus_sdk/quantus_sdk.dart';
import 'package:resonance_network_wallet/generated/version.g.dart';
+import 'package:resonance_network_wallet/shared/utils/open_external_url.dart';
import 'package:resonance_network_wallet/v2/components/scaffold_base.dart';
import 'package:resonance_network_wallet/v2/components/v2_app_bar.dart';
import 'package:resonance_network_wallet/v2/screens/settings/settings_divider.dart';
import 'package:resonance_network_wallet/v2/screens/settings/settings_tappable_row.dart';
import 'package:resonance_network_wallet/v2/theme/app_colors.dart';
import 'package:resonance_network_wallet/v2/theme/app_text_styles.dart';
-import 'package:url_launcher/url_launcher.dart';
class AboutQuantusScreenV2 extends StatelessWidget {
const AboutQuantusScreenV2({super.key});
@@ -49,7 +49,7 @@ class AboutQuantusScreenV2 extends StatelessWidget {
SettingsTappableRow(
title: entry.value.title,
subtitle: entry.value.subtitle,
- onTap: () => launchUrl(_uriForAboutLink(entry.value)),
+ onTap: () => openUrl(_uriForAboutLink(entry.value).toString()),
trailing: SettingsTappableRowUtils.externalLink(colors),
),
if (entry.key < _externalLinks.length - 1) const SettingsDivider(),
diff --git a/mobile-app/lib/v2/screens/settings/help_and_support_screen.dart b/mobile-app/lib/v2/screens/settings/help_and_support_screen.dart
index 3c75102c..2dc2c32b 100644
--- a/mobile-app/lib/v2/screens/settings/help_and_support_screen.dart
+++ b/mobile-app/lib/v2/screens/settings/help_and_support_screen.dart
@@ -1,11 +1,11 @@
import 'package:flutter/material.dart';
import 'package:quantus_sdk/quantus_sdk.dart';
+import 'package:resonance_network_wallet/shared/utils/open_external_url.dart';
import 'package:resonance_network_wallet/v2/components/scaffold_base.dart';
import 'package:resonance_network_wallet/v2/components/v2_app_bar.dart';
import 'package:resonance_network_wallet/v2/screens/settings/settings_divider.dart';
import 'package:resonance_network_wallet/v2/screens/settings/settings_tappable_row.dart';
import 'package:resonance_network_wallet/v2/theme/app_colors.dart';
-import 'package:url_launcher/url_launcher.dart';
class HelpAndSupportScreenV2 extends StatelessWidget {
const HelpAndSupportScreenV2({super.key});
@@ -22,13 +22,13 @@ class HelpAndSupportScreenV2 extends StatelessWidget {
title: 'Email Support',
subtitle: AppConstants.emailSupport,
colors: colors,
- onTap: () => launchUrl(Uri.parse('mailto:${AppConstants.emailSupport}')),
+ onTap: () => openUrl('mailto:${AppConstants.emailSupport}'),
),
_contactBlock(
title: 'Telegram',
subtitle: AppConstants.telegramHandle,
colors: colors,
- onTap: () => launchUrl(Uri.parse(AppConstants.communityUrl)),
+ onTap: () => openUrl(AppConstants.communityUrl),
showBottomDivider: false,
),
],
diff --git a/mobile-app/lib/v2/screens/settings/mining_rewards_screen.dart b/mobile-app/lib/v2/screens/settings/mining_rewards_screen.dart
new file mode 100644
index 00000000..a00f8645
--- /dev/null
+++ b/mobile-app/lib/v2/screens/settings/mining_rewards_screen.dart
@@ -0,0 +1,420 @@
+import 'package:flutter/material.dart';
+import 'package:flutter_riverpod/flutter_riverpod.dart';
+import 'package:quantus_sdk/quantus_sdk.dart';
+import 'package:resonance_network_wallet/features/components/skeleton.dart';
+import 'package:resonance_network_wallet/providers/mining_rewards_provider.dart';
+import 'package:resonance_network_wallet/providers/wallet_providers.dart';
+import 'package:resonance_network_wallet/services/mining_rewards_service.dart';
+import 'package:resonance_network_wallet/shared/utils/open_external_url.dart';
+import 'package:resonance_network_wallet/v2/components/quantus_button.dart';
+import 'package:resonance_network_wallet/v2/components/scaffold_base.dart';
+import 'package:resonance_network_wallet/v2/components/scaffold_base_bottom_content.dart';
+import 'package:resonance_network_wallet/v2/components/split_card.dart';
+import 'package:resonance_network_wallet/v2/components/v2_app_bar.dart';
+import 'package:resonance_network_wallet/v2/theme/app_colors.dart';
+import 'package:resonance_network_wallet/v2/theme/app_text_styles.dart';
+
+class MiningRewardsScreen extends ConsumerWidget {
+ const MiningRewardsScreen({super.key});
+
+ @override
+ Widget build(BuildContext context, WidgetRef ref) {
+ final miningAsync = ref.watch(miningRewardsProvider);
+ final colors = context.colors;
+ final text = context.themeText;
+
+ return ScaffoldBase.refreshable(
+ appBar: const V2AppBar(title: 'Mining Rewards'),
+ onRefresh: () async => ref.invalidate(miningRewardsProvider),
+ slivers: [
+ miningAsync.when(
+ data: (data) => data.totalBlocks > 0 ? _WithRewards(data: data) : const _NoRewards(),
+ loading: () => const _NoRewards(isLoading: true),
+ error: (err, _) =>
+ _ErrorState(colors: colors, text: text, onRetry: () => ref.invalidate(miningRewardsProvider)),
+ ),
+ ],
+ bottomContent: miningAsync.when(
+ data: (data) => data.totalBlocks > 0
+ ? const ScaffoldBaseBottomContent(child: QuantusButton.simple(label: 'Redeem', onTap: null))
+ : null,
+ loading: () => null,
+ error: (err, _) => null,
+ ),
+ );
+ }
+}
+
+class _WithRewards extends ConsumerWidget {
+ final MiningRewardsData data;
+
+ const _WithRewards({required this.data});
+
+ static const _resonanceSince = 'Jul 2025';
+ static const _schrodingerSince = 'Oct 2025';
+ static const _diracSince = 'Nov 2025';
+
+ @override
+ Widget build(BuildContext context, WidgetRef ref) {
+ final numberFmt = ref.watch(numberFormattingServiceProvider);
+ final quanEarned = numberFmt.formatBalance(data.planckRewards, addSymbol: true);
+ final redeemedRewards = numberFmt.formatBalance(data.redeemedRewards, addSymbol: true);
+ final redeemableRewards = numberFmt.formatBalance(data.redeemableRewards, addSymbol: true);
+
+ final colors = context.colors;
+ final text = context.themeText;
+
+ final testnets = [
+ _TestnetEntry('Dirac', _diracSince, data.diracBlocks),
+ _TestnetEntry('Schrödinger', _schrodingerSince, data.schrodingerBlocks),
+ _TestnetEntry('Resonance', _resonanceSince, data.resonanceBlocks),
+ ];
+
+ final miningSummaryPairRows = [
+ _StatPairRow(
+ left: _MiningStatCell(label: 'TESTNET BLOCKS', value: '${data.totalBlocks}', valueColor: colors.textLightGray),
+ right: _MiningStatCell(label: 'TESTNET REWARDS', value: quanEarned, valueColor: colors.accentOrange),
+ ),
+ _StatPairRow(
+ left: _MiningStatCell(label: 'REDEEMED', value: redeemedRewards, valueColor: colors.textLightGray),
+ right: _MiningStatCell(label: 'REDEEMABLE', value: redeemableRewards, valueColor: colors.success),
+ ),
+ ];
+
+ return Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ SplitCard(
+ topChild: _CardTopSection(
+ totalBlocks: data.totalBlocks,
+ totalBlocksColor: colors.textLightGray,
+ statusLabel: 'Mining',
+ statusColor: colors.success,
+ ),
+ bottomChild: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ for (var i = 0; i < miningSummaryPairRows.length; i++) ...[
+ if (i > 0) const SizedBox(height: 24),
+ miningSummaryPairRows[i],
+ ],
+ ],
+ ),
+ ),
+ const SizedBox(height: 32),
+ for (var i = 0; i < testnets.length; i++) ...[
+ _TestnetRow(entry: testnets[i]),
+ if (i < testnets.length - 1) Divider(color: colors.toasterBackground, height: 1, thickness: 1),
+ ],
+ const SizedBox(height: 48),
+ Center(
+ child: _OrangeLinkButton(
+ label: 'View Telemetry ↗',
+ text: text,
+ onTap: () => openUrl(AppConstants.telemetryUrl),
+ ),
+ ),
+ const SizedBox(height: 24),
+ ],
+ );
+ }
+}
+
+class _NoRewards extends StatelessWidget {
+ final bool isLoading;
+
+ const _NoRewards({this.isLoading = false});
+
+ @override
+ Widget build(BuildContext context) {
+ final colors = context.colors;
+ final text = context.themeText;
+
+ return Column(
+ crossAxisAlignment: CrossAxisAlignment.center,
+ children: [
+ SplitCard(
+ topChild: _CardTopSection(
+ totalBlocks: 0,
+ totalBlocksColor: colors.textTertiary,
+ statusLabel: 'Pending',
+ statusColor: colors.textTertiary,
+ isLoading: isLoading,
+ ),
+ bottomChild: _StatColumn(
+ label: 'QUAN EARNED',
+ value: '0.00',
+ valueColor: colors.textTertiary,
+ isLoading: isLoading,
+ ),
+ ),
+ if (isLoading) ...[
+ const SizedBox(height: 32),
+ for (var i = 0; i < 4; i++) ...[
+ const Row(
+ children: [
+ Expanded(
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [Skeleton(width: 100), SizedBox(height: 8), Skeleton(width: 72)],
+ ),
+ ),
+ Spacer(),
+ Expanded(
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.end,
+ children: [Skeleton(width: 72), SizedBox(height: 8), Skeleton(width: 56)],
+ ),
+ ),
+ ],
+ ),
+ const SizedBox(height: 16),
+ if (i < 3) Divider(color: colors.toasterBackground, height: 1, thickness: 1),
+ const SizedBox(height: 24),
+ ],
+ ] else ...[
+ const SizedBox(height: 64),
+ Text(
+ 'No mining data yet',
+ style: text.mediumTitle?.copyWith(fontWeight: FontWeight.w400, color: colors.textMuted),
+ ),
+ const SizedBox(height: 8),
+ Text(
+ 'Set up a Quantus mining node to start earning rewards.',
+ textAlign: TextAlign.center,
+ style: text.smallParagraph?.copyWith(color: colors.txItemIconDefault, height: 1.35),
+ ),
+ const SizedBox(height: 64),
+ _OrangeLinkButton(
+ label: 'Mining Setup Guide ↗',
+ text: text,
+ onTap: () => openUrl(AppConstants.miningSetupGuideUrl),
+ ),
+ const SizedBox(height: 24),
+ ],
+ ],
+ );
+ }
+}
+
+class _CardTopSection extends StatelessWidget {
+ final int totalBlocks;
+ final Color totalBlocksColor;
+ final String statusLabel;
+ final Color statusColor;
+ final bool isLoading;
+
+ const _CardTopSection({
+ required this.totalBlocks,
+ required this.totalBlocksColor,
+ required this.statusLabel,
+ required this.statusColor,
+ this.isLoading = false,
+ });
+
+ @override
+ Widget build(BuildContext context) {
+ final colors = context.colors;
+ final text = context.themeText;
+
+ return Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ Text('BLOCKS MINED', style: text.receiveLabel?.copyWith(color: colors.textLabel)),
+ Row(
+ children: [
+ Container(
+ width: 4,
+ height: 4,
+ decoration: BoxDecoration(color: statusColor, shape: BoxShape.circle),
+ ),
+ const SizedBox(width: 4),
+ if (isLoading)
+ const Skeleton(width: 100, height: 24)
+ else
+ Text(statusLabel, style: text.smallParagraph?.copyWith(color: statusColor)),
+ ],
+ ),
+ ],
+ ),
+ const SizedBox(height: 8),
+ if (isLoading)
+ const Skeleton(width: 100, height: 24)
+ else
+ Text('$totalBlocks', style: text.totalMinedBlocks?.copyWith(color: totalBlocksColor)),
+ const SizedBox(height: 4),
+ Text('blocks across all testnets', style: text.detail?.copyWith(color: colors.textMuted)),
+ ],
+ );
+ }
+}
+
+class _MiningStatCell {
+ const _MiningStatCell({required this.label, required this.value, required this.valueColor});
+
+ final String label;
+ final String value;
+ final Color valueColor;
+}
+
+class _StatPairRow extends StatelessWidget {
+ const _StatPairRow({required this.left, required this.right});
+
+ final _MiningStatCell left;
+ final _MiningStatCell right;
+
+ @override
+ Widget build(BuildContext context) {
+ return Row(
+ children: [
+ Expanded(
+ child: _StatColumn(label: left.label, value: left.value, valueColor: left.valueColor),
+ ),
+ const SizedBox(width: 12),
+ Expanded(
+ child: _StatColumn(label: right.label, value: right.value, valueColor: right.valueColor),
+ ),
+ ],
+ );
+ }
+}
+
+class _StatColumn extends StatelessWidget {
+ final String label;
+ final String value;
+ final Color valueColor;
+ final bool isLoading;
+
+ const _StatColumn({required this.label, required this.value, required this.valueColor, this.isLoading = false});
+
+ @override
+ Widget build(BuildContext context) {
+ final colors = context.colors;
+ final text = context.themeText;
+
+ return Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Text(label, style: text.receiveLabel?.copyWith(color: colors.textLabel)),
+ const SizedBox(height: 8),
+ if (isLoading)
+ const Skeleton(width: 100, height: 24)
+ else
+ Text(value, style: text.sendSectionLabel?.copyWith(color: valueColor)),
+ ],
+ );
+ }
+}
+
+class _TestnetRow extends StatelessWidget {
+ final _TestnetEntry entry;
+
+ const _TestnetRow({required this.entry});
+
+ @override
+ Widget build(BuildContext context) {
+ final colors = context.colors;
+ final text = context.themeText;
+ final countColor = colors.textLightGray;
+
+ return Padding(
+ padding: const EdgeInsets.symmetric(vertical: 16),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Text(entry.name, style: text.smallTitle?.copyWith(fontWeight: FontWeight.w400)),
+ const SizedBox(height: 8),
+ Text(entry.subtitle, style: text.smallParagraph?.copyWith(color: colors.textTertiary)),
+ ],
+ ),
+ Column(
+ crossAxisAlignment: CrossAxisAlignment.center,
+ children: [
+ Text(
+ '${entry.blocks}',
+ style: text.smallTitle?.copyWith(
+ fontFamily: AppTextTheme.fontFamilySecondary,
+ fontWeight: FontWeight.w400,
+ color: countColor,
+ ),
+ ),
+ const SizedBox(height: 4),
+ Text('blocks', style: text.detail?.copyWith(color: colors.textMuted)),
+ ],
+ ),
+ ],
+ ),
+ );
+ }
+}
+
+class _OrangeLinkButton extends StatelessWidget {
+ final String label;
+ final AppTextTheme text;
+ final VoidCallback onTap;
+
+ const _OrangeLinkButton({required this.label, required this.text, required this.onTap});
+
+ @override
+ Widget build(BuildContext context) {
+ final colors = context.colors;
+
+ return GestureDetector(
+ onTap: onTap,
+ child: Container(
+ decoration: BoxDecoration(
+ border: Border(bottom: BorderSide(color: colors.accentOrange, width: 1)),
+ ),
+ padding: const EdgeInsets.only(bottom: 3),
+ child: Text(label, style: text.smallParagraph?.copyWith(color: colors.accentOrange)),
+ ),
+ );
+ }
+}
+
+class _ErrorState extends StatelessWidget {
+ final AppColorsV2 colors;
+ final AppTextTheme text;
+ final VoidCallback onRetry;
+
+ const _ErrorState({required this.colors, required this.text, required this.onRetry});
+
+ @override
+ Widget build(BuildContext context) {
+ return SizedBox(
+ height: 200,
+ child: Center(
+ child: Column(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ Text('Failed to load mining rewards', style: text.paragraph?.copyWith(color: colors.textPrimary)),
+ const SizedBox(height: 8),
+ Text('Please check your connection', style: text.detail?.copyWith(color: colors.textTertiary)),
+ const SizedBox(height: 20),
+ GestureDetector(
+ onTap: onRetry,
+ child: Text(
+ 'Try Again',
+ style: text.smallParagraph?.copyWith(color: colors.accentGreen, fontWeight: FontWeight.w600),
+ ),
+ ),
+ ],
+ ),
+ ),
+ );
+ }
+}
+
+class _TestnetEntry {
+ final String name;
+ final String subtitle;
+ final int blocks;
+
+ const _TestnetEntry(this.name, this.subtitle, this.blocks);
+}
diff --git a/mobile-app/lib/v2/screens/settings/settings_screen.dart b/mobile-app/lib/v2/screens/settings/settings_screen.dart
index 905f929e..718bf4ab 100644
--- a/mobile-app/lib/v2/screens/settings/settings_screen.dart
+++ b/mobile-app/lib/v2/screens/settings/settings_screen.dart
@@ -1,6 +1,8 @@
import 'package:flutter/material.dart';
+import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:resonance_network_wallet/generated/version.g.dart';
+import 'package:resonance_network_wallet/providers/mining_rewards_provider.dart';
import 'package:resonance_network_wallet/v2/components/scaffold_base.dart';
import 'package:resonance_network_wallet/v2/components/v2_app_bar.dart';
import 'package:resonance_network_wallet/v2/screens/settings/about_quantus_screen.dart';
@@ -9,14 +11,24 @@ import 'package:resonance_network_wallet/v2/screens/settings/help_and_support_sc
import 'package:resonance_network_wallet/v2/screens/settings/preferences_settings_screen.dart';
import 'package:resonance_network_wallet/v2/screens/settings/settings_divider.dart';
import 'package:resonance_network_wallet/v2/screens/settings/settings_tappable_row.dart';
+import 'package:resonance_network_wallet/v2/screens/settings/mining_rewards_screen.dart';
import 'package:resonance_network_wallet/v2/screens/settings/wallet_settings_screen.dart';
import 'package:resonance_network_wallet/v2/theme/app_colors.dart';
-class SettingsScreenV2 extends StatelessWidget {
+const _miningRewardsTitle = 'Mining Rewards';
+
+class SettingsScreenV2 extends ConsumerStatefulWidget {
const SettingsScreenV2({super.key});
+ @override
+ ConsumerState createState() => _SettingsScreenV2State();
+}
+
+class _SettingsScreenV2State extends ConsumerState {
@override
Widget build(BuildContext context) {
+ final miningAsync = ref.watch(miningRewardsProvider);
+
final colors = context.colors;
final trailing = SettingsTappableRowUtils.chevron(colors);
final entries = _settingsHubItems(colors);
@@ -26,19 +38,34 @@ class SettingsScreenV2 extends StatelessWidget {
mainContent: ListView(
children: [
for (final e in entries.asMap().entries) ...[
- SettingsTappableRow(
- leading: e.value.leading,
- title: e.value.title,
- subtitle: e.value.subtitle,
- onTap: () => Navigator.push(context, MaterialPageRoute(builder: (_) => e.value.page)),
- trailing: trailing,
- ),
+ if (e.value.title == _miningRewardsTitle)
+ miningAsync.when(
+ data: (data) =>
+ _buildTappableRow(e.value, subtitle: '${data.totalBlocks} blocks mined', trailing: trailing),
+ loading: () => _buildTappableRow(e.value, subtitle: 'Loading...', trailing: trailing),
+ error: (err, st) {
+ debugPrint('Error getting mining rewards: ${err.toString()}');
+ debugPrint('Stack trace: ${st.toString()}');
+
+ return _buildTappableRow(e.value, subtitle: 'Error getting mining rewards', trailing: trailing);
+ },
+ )
+ else
+ _buildTappableRow(e.value, trailing: trailing),
if (e.key < entries.length - 1) const SettingsDivider(),
],
],
),
);
}
+
+ Widget _buildTappableRow(_SettingsHubItem item, {required Widget trailing, String? subtitle}) => SettingsTappableRow(
+ leading: item.leading,
+ title: item.title,
+ subtitle: subtitle ?? item.subtitle,
+ onTap: () => Navigator.push(context, MaterialPageRoute(builder: (_) => item.page)),
+ trailing: trailing,
+ );
}
class _SettingsHubItem {
@@ -64,6 +91,12 @@ List<_SettingsHubItem> _settingsHubItems(AppColorsV2 colors) {
subtitle: 'Currency, POS mode, notifications',
page: const PreferencesSettingsScreenV2(),
),
+ _SettingsHubItem(
+ leading: _settingsHubIcon(colors, svg: SvgPicture.asset('assets/v2/axe.svg', width: 18, height: 18)),
+ title: _miningRewardsTitle,
+ subtitle: 'Loading...',
+ page: const MiningRewardsScreen(),
+ ),
_SettingsHubItem(
leading: _settingsHubIcon(colors, icon: Icons.shield_outlined),
title: 'Account Type',
diff --git a/mobile-app/lib/v2/theme/app_colors.dart b/mobile-app/lib/v2/theme/app_colors.dart
index 89cd2b90..6be68c7d 100644
--- a/mobile-app/lib/v2/theme/app_colors.dart
+++ b/mobile-app/lib/v2/theme/app_colors.dart
@@ -17,6 +17,7 @@ class AppColorsV2 extends ThemeExtension {
final Color textMuted;
final Color textError;
final Color textLabel;
+ final Color textLightGray;
// Accents
final Color accentOrange;
@@ -83,6 +84,7 @@ class AppColorsV2 extends ThemeExtension {
required this.textMuted,
required this.textError,
required this.textLabel,
+ required this.textLightGray,
required this.accentOrange,
required this.accentGreen,
required this.checksum,
@@ -132,6 +134,7 @@ class AppColorsV2 extends ThemeExtension {
textMuted: const Color(0xFF888888),
textError: const Color(0xFFC0392B),
textLabel: const Color(0xFF787878),
+ textLightGray: const Color(0xFFEBEBEB),
accentOrange: const Color(0xFFFF6B35),
accentGreen: const Color(0xFF34C759),
checksum: const Color(0xFF95A7FB),
@@ -173,6 +176,7 @@ class AppColorsV2 extends ThemeExtension {
Color? textMuted,
Color? textError,
Color? textLabel,
+ Color? textLightGray,
Color? accentOrange,
Color? accentGreen,
Color? checksum,
@@ -224,6 +228,7 @@ class AppColorsV2 extends ThemeExtension {
textMuted: textMuted ?? this.textMuted,
textError: textError ?? this.textError,
textLabel: textLabel ?? this.textLabel,
+ textLightGray: textLightGray ?? this.textLightGray,
accentOrange: accentOrange ?? this.accentOrange,
accentGreen: accentGreen ?? this.accentGreen,
checksum: checksum ?? this.checksum,
@@ -275,6 +280,7 @@ class AppColorsV2 extends ThemeExtension {
textMuted: Color.lerp(textMuted, other.textMuted, t) ?? textMuted,
textError: Color.lerp(textError, other.textError, t) ?? textError,
textLabel: Color.lerp(textLabel, other.textLabel, t) ?? textLabel,
+ textLightGray: Color.lerp(textLightGray, other.textLightGray, t) ?? textLightGray,
accentOrange: Color.lerp(accentOrange, other.accentOrange, t) ?? accentOrange,
accentGreen: Color.lerp(accentGreen, other.accentGreen, t) ?? accentGreen,
checksum: Color.lerp(checksum, other.checksum, t) ?? checksum,
diff --git a/mobile-app/lib/v2/theme/app_text_styles.dart b/mobile-app/lib/v2/theme/app_text_styles.dart
index 382d9b0f..441eb64c 100644
--- a/mobile-app/lib/v2/theme/app_text_styles.dart
+++ b/mobile-app/lib/v2/theme/app_text_styles.dart
@@ -18,6 +18,7 @@ class AppTextTheme extends ThemeExtension {
final TextStyle? timer;
final TextStyle? detail;
final TextStyle? tiny;
+ final TextStyle? totalMinedBlocks;
final TextStyle? transactionDetailAmountPrimary;
final TextStyle? transactionDetailAmountSymbol;
final TextStyle? transactionDetailRowLabel;
@@ -39,6 +40,7 @@ class AppTextTheme extends ThemeExtension {
this.timer,
this.detail,
this.tiny,
+ this.totalMinedBlocks,
this.transactionDetailAmountPrimary,
this.transactionDetailAmountSymbol,
this.transactionDetailRowLabel,
@@ -62,6 +64,12 @@ class AppTextTheme extends ThemeExtension {
timer: const TextStyle(fontSize: 28, fontWeight: FontWeight.w600, fontFamily: fontFamily),
detail: const TextStyle(fontSize: 12, fontWeight: FontWeight.w400, fontFamily: fontFamily),
tiny: const TextStyle(fontSize: 11, fontWeight: FontWeight.w400, fontFamily: fontFamily),
+ totalMinedBlocks: const TextStyle(
+ fontFamily: fontFamilySecondary,
+ fontSize: 56,
+ fontWeight: FontWeight.w400,
+ height: 1.0,
+ ),
transactionDetailAmountPrimary: const TextStyle(
fontFamily: fontFamilySecondary,
fontSize: 64,
@@ -113,6 +121,12 @@ class AppTextTheme extends ThemeExtension {
timer: const TextStyle(fontSize: 36, fontWeight: FontWeight.w600, fontFamily: fontFamily),
detail: const TextStyle(fontSize: 16, fontWeight: FontWeight.w400, fontFamily: fontFamily),
tiny: const TextStyle(fontSize: 15, fontWeight: FontWeight.w400, fontFamily: fontFamily),
+ totalMinedBlocks: const TextStyle(
+ fontFamily: fontFamilySecondary,
+ fontSize: 60,
+ fontWeight: FontWeight.w400,
+ height: 1.0,
+ ),
transactionDetailAmountPrimary: const TextStyle(
fontFamily: fontFamilySecondary,
fontSize: 80,
@@ -164,6 +178,7 @@ class AppTextTheme extends ThemeExtension {
TextStyle? timer,
TextStyle? detail,
TextStyle? tiny,
+ TextStyle? totalMinedBlocks,
TextStyle? transactionDetailAmountPrimary,
TextStyle? transactionDetailAmountSymbol,
TextStyle? transactionDetailRowLabel,
@@ -185,6 +200,7 @@ class AppTextTheme extends ThemeExtension {
timer: timer ?? this.timer,
detail: detail ?? this.detail,
tiny: tiny ?? this.tiny,
+ totalMinedBlocks: totalMinedBlocks ?? this.totalMinedBlocks,
transactionDetailAmountPrimary: transactionDetailAmountPrimary ?? this.transactionDetailAmountPrimary,
transactionDetailAmountSymbol: transactionDetailAmountSymbol ?? this.transactionDetailAmountSymbol,
transactionDetailRowLabel: transactionDetailRowLabel ?? this.transactionDetailRowLabel,
@@ -211,6 +227,7 @@ class AppTextTheme extends ThemeExtension {
timer: TextStyle.lerp(timer, other.timer, t),
detail: TextStyle.lerp(detail, other.detail, t),
tiny: TextStyle.lerp(tiny, other.tiny, t),
+ totalMinedBlocks: TextStyle.lerp(totalMinedBlocks, other.totalMinedBlocks, t),
transactionDetailAmountPrimary: TextStyle.lerp(
transactionDetailAmountPrimary,
other.transactionDetailAmountPrimary,
diff --git a/mobile-app/pubspec.yaml b/mobile-app/pubspec.yaml
index 849ff865..bf81af5e 100644
--- a/mobile-app/pubspec.yaml
+++ b/mobile-app/pubspec.yaml
@@ -107,6 +107,7 @@ flutter:
- assets/v2/action_swap.svg
- assets/v2/uppercase_q.svg
- assets/v2/uppercase_q.png
+ - assets/v2/axe.svg
- assets/v2/uppercase_q_black_bg.png
- assets/v2/swap_arrows_down_up.svg
- assets/v2/swap_clock_counter_clockwise.svg
diff --git a/quantus_sdk/lib/src/constants/app_constants.dart b/quantus_sdk/lib/src/constants/app_constants.dart
index a8a0d3df..94b75f44 100644
--- a/quantus_sdk/lib/src/constants/app_constants.dart
+++ b/quantus_sdk/lib/src/constants/app_constants.dart
@@ -39,6 +39,8 @@ class AppConstants {
static const String raidQuestsPageUrl = 'https://www.quantus.com/quests/raid';
static const String communityUrl = 'https://t.me/quantusnetwork';
static const String faucetUrl = 'https://x.com/QuantusNetwork/status/2033738875827589221';
+ static const String miningSetupGuideUrl = 'https://docs.quantus.com/guides/mining';
+ static const String telemetryUrl = 'https://telemetry.quantus.cat';
// Development accounts
static const String crystalAlice = '//Crystal Alice';
diff --git a/quantus_sdk/lib/src/services/number_formatting_service.dart b/quantus_sdk/lib/src/services/number_formatting_service.dart
index 3a3cb721..d30b6671 100644
--- a/quantus_sdk/lib/src/services/number_formatting_service.dart
+++ b/quantus_sdk/lib/src/services/number_formatting_service.dart
@@ -9,7 +9,8 @@ class NumberFormattingService {
final LocaleNumberConfig _localeConfig;
- NumberFormattingService({LocaleNumberConfig? localeConfig}) : _localeConfig = localeConfig ?? LocaleNumberConfig.fromDefaultLocale();
+ NumberFormattingService({LocaleNumberConfig? localeConfig})
+ : _localeConfig = localeConfig ?? LocaleNumberConfig.fromDefaultLocale();
/// Formats a raw BigInt balance (representing the smallest unit) into a
/// user-readable string with a specified number of decimal places.
diff --git a/quantus_sdk/lib/src/services/taskmaster_service.dart b/quantus_sdk/lib/src/services/taskmaster_service.dart
index 4bb16e54..394ac8b8 100644
--- a/quantus_sdk/lib/src/services/taskmaster_service.dart
+++ b/quantus_sdk/lib/src/services/taskmaster_service.dart
@@ -566,8 +566,6 @@ class TaskmasterService {
final Map data = responseBody['data'];
- print('data $data');
-
final List? minerStatsList = data['minerStats'];
if (minerStatsList == null || minerStatsList.isEmpty) {
return MinerStats(totalMinedBlocks: 0, totalRewards: BigInt.zero);
diff --git a/quantus_sdk/lib/src/services/wormhole_utxo_service.dart b/quantus_sdk/lib/src/services/wormhole_utxo_service.dart
index 04cb3624..376f24bd 100644
--- a/quantus_sdk/lib/src/services/wormhole_utxo_service.dart
+++ b/quantus_sdk/lib/src/services/wormhole_utxo_service.dart
@@ -198,7 +198,7 @@ class WormholeUtxoService {
query TransfersToAddress($to: String!, $limit: Int!, $offset: Int!, $afterBlock: Int) {
transfers: transfer(
where: { to: { id: {_eq: $to } }, block: { height: {_gt: $afterBlock } } }
- order_by: {block_height: asc}
+ order_by: {block: {height: asc}}
limit: $limit
offset: $offset
) {