From 837054872c9b6633a7ddc9af777c6085b18c5204 Mon Sep 17 00:00:00 2001 From: Beast Date: Wed, 5 Aug 2026 14:10:07 +0800 Subject: [PATCH 1/2] feat: swap task master with quersi - remove task master - add quersi - remove unused files related to task master --- mobile-app/lib/app.dart | 5 - mobile-app/lib/app_lifecycle_manager.dart | 20 - .../account_associations_providers.dart | 45 -- .../providers/currency_display_provider.dart | 2 +- .../providers/mining_rewards_provider.dart | 2 +- .../providers/opt_in_position_providers.dart | 29 - .../lib/providers/raider_quest_providers.dart | 45 -- .../lib/services/deep_link_service.dart | 5 - mobile-app/lib/services/logout_service.dart | 2 - .../lib/services/mining_rewards_service.dart | 2 +- mobile-app/lib/services/referral_service.dart | 168 ----- .../lib/services/remote_config_service.dart | 4 +- .../lib/services/wallet_creation_service.dart | 7 +- .../unit/wallet_creation_service_test.dart | 9 +- .../wallet_creation_service_test.mocks.dart | 119 ---- quantus_sdk/lib/quantus_sdk.dart | 2 +- .../lib/src/constants/app_constants.dart | 6 +- .../lib/src/services/quersi_service.dart | 131 ++++ .../lib/src/services/substrate_service.dart | 1 - .../lib/src/services/taskmaster_service.dart | 632 ------------------ 20 files changed, 140 insertions(+), 1096 deletions(-) delete mode 100644 mobile-app/lib/providers/account_associations_providers.dart delete mode 100644 mobile-app/lib/providers/opt_in_position_providers.dart delete mode 100644 mobile-app/lib/providers/raider_quest_providers.dart delete mode 100644 mobile-app/lib/services/referral_service.dart create mode 100644 quantus_sdk/lib/src/services/quersi_service.dart delete mode 100644 quantus_sdk/lib/src/services/taskmaster_service.dart diff --git a/mobile-app/lib/app.dart b/mobile-app/lib/app.dart index 51c6cd47f..8043d5672 100644 --- a/mobile-app/lib/app.dart +++ b/mobile-app/lib/app.dart @@ -6,11 +6,9 @@ import 'package:resonance_network_wallet/v2/screens/auth/auth_wrapper.dart'; import 'package:resonance_network_wallet/v2/theme/app_theme.dart'; import 'package:resonance_network_wallet/services/local_notifications_service.dart'; import 'package:resonance_network_wallet/services/notification_integration_service.dart'; -import 'package:resonance_network_wallet/services/referral_service.dart'; import 'package:resonance_network_wallet/services/telemetry_navigator_observer.dart'; import 'package:resonance_network_wallet/services/deep_link_service.dart'; import 'package:resonance_network_wallet/l10n/app_localizations.dart'; -import 'dart:io' show Platform; class ResonanceWalletApp extends ConsumerStatefulWidget { const ResonanceWalletApp({super.key}); @@ -20,7 +18,6 @@ class ResonanceWalletApp extends ConsumerStatefulWidget { } class _ResonanceWalletAppState extends ConsumerState { - final ReferralService _referralService = ReferralService(); @override void initState() { @@ -31,8 +28,6 @@ class _ResonanceWalletAppState extends ConsumerState { final localNotifications = ref.read(localNotificationsServiceProvider); localNotifications.setupNotificationsClickListener(); localNotifications.handleLaunchByNotification(); - - if (Platform.isAndroid) _referralService.checkPlayStoreReferralCode(); } @override diff --git a/mobile-app/lib/app_lifecycle_manager.dart b/mobile-app/lib/app_lifecycle_manager.dart index 7b5522f9f..20834da10 100644 --- a/mobile-app/lib/app_lifecycle_manager.dart +++ b/mobile-app/lib/app_lifecycle_manager.dart @@ -42,7 +42,6 @@ class _AppLifecycleManagerState extends ConsumerState with ref.read(appLifecycleStateProvider.notifier).state = WidgetsBinding.instance.lifecycleState ?? AppLifecycleState.resumed; - _initializeTaskmasterLogin(); _setupConnectivityListener(); localAuthNotifier.checkAuthentication(); }); @@ -104,9 +103,6 @@ class _AppLifecycleManagerState extends ConsumerState with // that briefly pause/resume the app. localAuthNotifier.checkAuthentication(); - // Initialize Taskmaster login if wallet exists - _initializeTaskmasterLogin(); - // Sync remote config on background resume unawaited(ref.read(remoteConfigProvider.notifier).syncConfig()); } @@ -132,22 +128,6 @@ class _AppLifecycleManagerState extends ConsumerState with } } - // This is merely an optimization - check our login is active. - Future _initializeTaskmasterLogin() async { - try { - final settingsService = SettingsService(); - final hasWallet = await settingsService.getHasWallet(); - - if (hasWallet) { - final taskmasterService = TaskmasterService(); - await taskmasterService.ensureIsLoggedIn(); - quantusPrint('Taskmaster login initialized'); - } - } catch (e) { - quantusPrint('Failed to initialize taskmaster login: $e'); - } - } - @override Widget build(BuildContext context) { return widget.child; diff --git a/mobile-app/lib/providers/account_associations_providers.dart b/mobile-app/lib/providers/account_associations_providers.dart deleted file mode 100644 index 2c0c2e88c..000000000 --- a/mobile-app/lib/providers/account_associations_providers.dart +++ /dev/null @@ -1,45 +0,0 @@ -import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:flutter_riverpod/legacy.dart'; -import 'package:quantus_sdk/quantus_sdk.dart'; -import 'package:resonance_network_wallet/providers/account_providers.dart'; -import 'package:resonance_network_wallet/shared/utils/print.dart'; - -class AccountAssociationsNotifier extends StateNotifier> { - final TaskmasterService _taskmasterService = TaskmasterService(); - final Account? _account; - - AccountAssociationsNotifier(this._account) : super(const AsyncValue.loading()) { - if (_account != null) { - fetchAssociations(); - } - } - - Future fetchAssociations() async { - if (_account == null) return; - - try { - final associations = await _taskmasterService.getAccountAssociations(); - if (mounted) { - state = AsyncValue.data(associations); - } - } catch (e, st) { - quantusPrint('Error fetching account associations: $e'); - quantusPrint('Stack trace: $st'); - - if (mounted) { - state = AsyncValue.error(e, st); - } - } - } - - void reset() { - state = const AsyncValue.loading(); - } -} - -final accountAssociationsProvider = StateNotifierProvider>( - (ref) { - final activeAccount = ref.watch(activeAccountProvider).value; - return AccountAssociationsNotifier(activeAccount is RegularAccount ? activeAccount.account : null); - }, -); diff --git a/mobile-app/lib/providers/currency_display_provider.dart b/mobile-app/lib/providers/currency_display_provider.dart index 222cb909b..d5c391b57 100644 --- a/mobile-app/lib/providers/currency_display_provider.dart +++ b/mobile-app/lib/providers/currency_display_provider.dart @@ -97,7 +97,7 @@ final exchangeRatesProvider = FutureProvider>((ref) async { if (cached != null) return cached; try { - final result = await TaskmasterService().getExchangeRates(); + final result = await QuersiService().getExchangeRates(); final rates = result.rates.map((k, v) => MapEntry(k, Decimal.parse(v.toString()))); await _writeRatesCache(settings, rates, result.timeNextUpdateUnix); diff --git a/mobile-app/lib/providers/mining_rewards_provider.dart b/mobile-app/lib/providers/mining_rewards_provider.dart index d23a6263a..8baed724d 100644 --- a/mobile-app/lib/providers/mining_rewards_provider.dart +++ b/mobile-app/lib/providers/mining_rewards_provider.dart @@ -28,7 +28,7 @@ final miningRewardsProvider = FutureProvider((ref) async { } final keyPair = ref.watch(hdWalletServiceProvider).deriveWormholeKeyPair(mnemonic: mnemonic); - final oldMiningAccountId = await TaskmasterService().getOldMiningAccountId(); + final oldMiningAccountId = await QuersiService().getOldMiningAccountId(); final accountsList = accounts.map((a) => a.accountId).toList(); accountsList.add(oldMiningAccountId); diff --git a/mobile-app/lib/providers/opt_in_position_providers.dart b/mobile-app/lib/providers/opt_in_position_providers.dart deleted file mode 100644 index b0b922d9a..000000000 --- a/mobile-app/lib/providers/opt_in_position_providers.dart +++ /dev/null @@ -1,29 +0,0 @@ -import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:flutter_riverpod/legacy.dart'; -import 'package:quantus_sdk/quantus_sdk.dart'; - -class OptInPositionNotifier extends StateNotifier> { - final TaskmasterService _taskmasterService = TaskmasterService(); - - OptInPositionNotifier() : super(const AsyncValue.loading()) { - fetch(); - } - - Future fetch() async { - try { - final optInPosition = await _taskmasterService.getOptInPosition(); - - state = AsyncValue.data(optInPosition); - } catch (e, st) { - state = AsyncValue.error(e, st); - } - } - - void reset() { - state = const AsyncValue.loading(); - } -} - -final optInPositionProvider = StateNotifierProvider>((ref) { - return OptInPositionNotifier(); -}); diff --git a/mobile-app/lib/providers/raider_quest_providers.dart b/mobile-app/lib/providers/raider_quest_providers.dart deleted file mode 100644 index 675cbc991..000000000 --- a/mobile-app/lib/providers/raider_quest_providers.dart +++ /dev/null @@ -1,45 +0,0 @@ -import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:flutter_riverpod/legacy.dart'; -import 'package:quantus_sdk/quantus_sdk.dart'; -import 'package:resonance_network_wallet/providers/account_providers.dart'; -import 'package:resonance_network_wallet/shared/utils/print.dart'; - -class RaiderSubmissionsNotifier extends StateNotifier> { - final TaskmasterService _taskmasterService = TaskmasterService(); - final Account? _account; - - RaiderSubmissionsNotifier(this._account) : super(const AsyncValue.loading()) { - if (_account != null) { - fetchRaiderSubmissions(); - } - } - - Future fetchRaiderSubmissions() async { - if (_account == null) return; - - try { - final submissions = await _taskmasterService.getActiveRaidRaiderSubmissions(); - if (mounted) { - state = AsyncValue.data(submissions); - } - } catch (e, st) { - quantusPrint('Error fetching raider submissions: $e'); - quantusPrint('Stack trace: $st'); - - if (mounted) { - state = AsyncValue.error(e, st); - } - } - } - - void reset() { - state = const AsyncValue.loading(); - } -} - -final raiderSubmissionsProvider = StateNotifierProvider>(( - ref, -) { - final activeAccount = ref.watch(activeAccountProvider).value; - return RaiderSubmissionsNotifier(activeAccount is RegularAccount ? activeAccount.account : null); -}); diff --git a/mobile-app/lib/services/deep_link_service.dart b/mobile-app/lib/services/deep_link_service.dart index 339b7dba8..9e6640629 100644 --- a/mobile-app/lib/services/deep_link_service.dart +++ b/mobile-app/lib/services/deep_link_service.dart @@ -2,7 +2,6 @@ import 'dart:async'; import 'package:app_links/app_links.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:resonance_network_wallet/providers/account_associations_providers.dart'; import 'package:resonance_network_wallet/providers/route_intent_providers.dart'; import 'package:resonance_network_wallet/providers/wallet_providers.dart'; import 'package:resonance_network_wallet/shared/utils/print.dart'; @@ -67,10 +66,6 @@ class DeepLinkService { quantusPrint('Missing payment parameters or invalid recipient address'); } } - - if (uri.pathSegments.isNotEmpty && uri.pathSegments.first == 'oauth') { - _ref.invalidate(accountAssociationsProvider); - } } void dispose() { diff --git a/mobile-app/lib/services/logout_service.dart b/mobile-app/lib/services/logout_service.dart index b5cd326ba..ec301e760 100644 --- a/mobile-app/lib/services/logout_service.dart +++ b/mobile-app/lib/services/logout_service.dart @@ -1,7 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:quantus_sdk/quantus_sdk.dart'; -import 'package:resonance_network_wallet/providers/account_associations_providers.dart'; import 'package:resonance_network_wallet/providers/account_providers.dart'; import 'package:resonance_network_wallet/providers/currency_display_provider.dart'; import 'package:resonance_network_wallet/providers/l10n_provider.dart'; @@ -59,7 +58,6 @@ class LogoutService { _ref.invalidate(encryptedTotalSpentProvider); _ref.read(multisigAccountsProvider.notifier).reset(); _ref.invalidate(discoveredMultisigsProvider); - _ref.read(accountAssociationsProvider.notifier).reset(); _ref.read(keystoneSignCacheProvider.notifier).reset(); await _ref.read(selectedAppLocaleProvider.notifier).reset(); await _ref.read(selectedFiatCurrencyProvider.notifier).reset(); diff --git a/mobile-app/lib/services/mining_rewards_service.dart b/mobile-app/lib/services/mining_rewards_service.dart index de2889b55..fb0f330cd 100644 --- a/mobile-app/lib/services/mining_rewards_service.dart +++ b/mobile-app/lib/services/mining_rewards_service.dart @@ -60,7 +60,7 @@ class MiningRewardsService { final schrodinger = _countBlocks('schrodinger', miners['schrodinger']!, allAccountIds); final dirac = _countBlocks('dirac', miners['dirac']!, allAccountIds); final (planckStats, redeemableRewards) = await ( - TaskmasterService().getMinerStats(), + QuersiService().getMinerStats(), wormholeUtxoService.getUnspentBalance(wormholeAddress: keyPair.address, secretHex: keyPair.secretHex), ).wait; final redeemedRewards = planckStats.totalRewards - redeemableRewards; diff --git a/mobile-app/lib/services/referral_service.dart b/mobile-app/lib/services/referral_service.dart deleted file mode 100644 index 31ff207b3..000000000 --- a/mobile-app/lib/services/referral_service.dart +++ /dev/null @@ -1,168 +0,0 @@ -import 'dart:convert'; -import 'dart:ui'; - -import 'package:http/http.dart' as http; -import 'package:play_install_referrer/play_install_referrer.dart'; -import 'package:quantus_sdk/quantus_sdk.dart'; -import 'package:resonance_network_wallet/models/referral_data.dart'; -import 'package:resonance_network_wallet/shared/utils/print.dart'; -import 'package:share_plus/share_plus.dart'; - -class ReferralService { - static final ReferralService _instance = ReferralService._internal(); - factory ReferralService() => _instance; - ReferralService._internal(); - - final SettingsService _settingsService = SettingsService(); - final HumanReadableChecksumService _checksumService = HumanReadableChecksumService(); - final TaskmasterService _taskmasterService = TaskmasterService(); - - bool? _rewardProgramParticipationCache; - bool _hasCheckedReferralData = false; - String? _referralDataCache; - - // This fetches any available referral code from the google play store and stores - // it in settings if found. - Future checkPlayStoreReferralCode() async { - // Only check once - on first launch after install - bool hasChecked = _settingsService.referralCheckCompleted(); - if (hasChecked) return; - - try { - ReferrerDetails referrerDetails = await PlayInstallReferrer.installReferrer; - String? referrerString = referrerDetails.installReferrer; - - quantusPrint('Raw Install Referrer: $referrerString'); - - if (referrerString != null && referrerString.isNotEmpty) { - Map params = _parseReferrer(referrerString); - - String? referralCode = params['referral_code']; - - if (referralCode != null && referralCode.isNotEmpty) { - SettingsService().setReferralCode(referralCode); - SettingsService().setReferralCheckCompleted(); - quantusPrint('Referral Code Found: $referralCode'); - } - } - - quantusPrint('No referral code found'); - } catch (e) { - quantusPrint('Error checking install referrer: $e'); - } - } - - Future optInRewardProgram() async { - await _taskmasterService.optInRewardProgram(); - _rewardProgramParticipationCache = true; - } - - Map _parseReferrer(String referrer) { - Map params = {}; - - Uri uri = Uri.parse('?$referrer'); - params = Map.from(uri.queryParameters); - - return params; - } - - Future getReferralData() async { - if (_hasCheckedReferralData) { - return _referralDataCache; - } - - final account = await getMainAccount(); - final getReferralByRefereeUri = Uri.parse('${AppConstants.taskMasterEndpoint}/referrals/${account.accountId}'); - - try { - final http.Response response = await http.get( - getReferralByRefereeUri, - headers: {'Content-Type': 'application/json'}, - ); - - quantusPrint('getReferralData response: ${response.body}'); - - // If account doesn't have referrer, it will return 404 code. - // Therefore we can confidently say it has been checked successfully. - // We don't have to check it anymore. - if (response.statusCode == 404) { - _hasCheckedReferralData = true; - return null; - } else if (response.statusCode != 200) { - return null; - } - - final json = jsonDecode(response.body) as Map; - final referralData = ReferralData.fromJson(json); - - final referralCode = await _checksumService.getHumanReadableName(referralData.referrerAddress); - _referralDataCache = referralCode; - _hasCheckedReferralData = true; - - return referralCode; - } catch (e) { - return null; - } - } - - void invalidateCache() { - _rewardProgramParticipationCache = null; - _hasCheckedReferralData = false; - _referralDataCache = null; - } - - Future getRewardProgramParticiation() async { - if (_rewardProgramParticipationCache != null) { - return _rewardProgramParticipationCache!; - } - - final hasOptedIn = await _taskmasterService.getRewardProgramParticipation(); - - _rewardProgramParticipationCache = hasOptedIn; - return hasOptedIn; - } - - Future submitReferralToBackend({required String referral}) async { - await _taskmasterService.submitReferral(referral); - _referralDataCache = referral; - } - - Future submitAddressToBackend() async { - await _taskmasterService.submitAddress(); - } - - String generateReferralLink(String referralCode) { - return '${AppConstants.websiteBaseUrl}/invite?referralCode=$referralCode'; - } - - Future getMainAccount() async { - final account = await _taskmasterService.getMainAccount(); - return account; - } - - Future getMyInviteCode() async { - final account = await getMainAccount(); - final referralCode = await _checksumService.getHumanReadableName(account.accountId); - - return referralCode ?? ''; - } - - Future getShareLinkParameters(Rect? positionOrigin) async { - final referralCode = await getMyInviteCode(); - - String link = generateReferralLink(referralCode); - String message = - "Most L1s aren't ready for quantum threats. This one is.\nI'm on the @QuantusNetwork testnet stacking early points for rewards.\nUse my referral link so we both earn points:\n$referralCode\n\nDownload the wallet & get in early\n\n$link"; - - return ShareParams( - text: message, - subject: 'Invite Link', - title: 'Invite Link', - sharePositionOrigin: positionOrigin, - ); - } - - String? getReferralCode() { - return _settingsService.getReferralCode(); - } -} diff --git a/mobile-app/lib/services/remote_config_service.dart b/mobile-app/lib/services/remote_config_service.dart index ab997cb06..1b2ff8c02 100644 --- a/mobile-app/lib/services/remote_config_service.dart +++ b/mobile-app/lib/services/remote_config_service.dart @@ -7,12 +7,12 @@ import 'package:resonance_network_wallet/shared/utils/print.dart'; const String remoteConfigCacheKey = 'remote_config_cache_v1'; class RemoteConfigService { - final TaskmasterService _taskmasterService = TaskmasterService(); + final QuersiService _quersiService = QuersiService(); final SettingsService _settingsService = SettingsService(); Future readRemoteConfig() async { try { - final remoteData = await _taskmasterService.getRemoteConfig(); + final remoteData = await _quersiService.getRemoteConfig(); return remoteData; } catch (error) { quantusPrint('Remote config remote read failed: $error'); diff --git a/mobile-app/lib/services/wallet_creation_service.dart b/mobile-app/lib/services/wallet_creation_service.dart index 48e2a0b5c..be8715032 100644 --- a/mobile-app/lib/services/wallet_creation_service.dart +++ b/mobile-app/lib/services/wallet_creation_service.dart @@ -1,20 +1,16 @@ import 'dart:async'; import 'package:quantus_sdk/quantus_sdk.dart'; -import 'package:resonance_network_wallet/services/referral_service.dart'; class WalletCreationService { final SettingsService _settings; final AccountsService _accounts; - final ReferralService _referral; WalletCreationService({ SettingsService? settingsService, AccountsService? accountsService, - ReferralService? referralService, }) : _settings = settingsService ?? SettingsService(), - _accounts = accountsService ?? AccountsService(), - _referral = referralService ?? ReferralService(); + _accounts = accountsService ?? AccountsService(); /// Saves [mnemonic] for [walletIndex], adds the root account when missing, /// and runs referral registration for brand-new roots. @@ -35,7 +31,6 @@ class WalletCreationService { _settings.setWalletOrigin(walletIndex, WalletOrigin.created); final account = Account(walletIndex: walletIndex, index: 0, name: name, accountId: accountId); await _accounts.addAccount(account); - unawaited(_referral.submitAddressToBackend()); return account; } diff --git a/mobile-app/test/unit/wallet_creation_service_test.dart b/mobile-app/test/unit/wallet_creation_service_test.dart index e7e84c3c7..325d296e0 100644 --- a/mobile-app/test/unit/wallet_creation_service_test.dart +++ b/mobile-app/test/unit/wallet_creation_service_test.dart @@ -2,10 +2,9 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:mockito/annotations.dart'; import 'package:mockito/mockito.dart'; import 'package:quantus_sdk/quantus_sdk.dart'; -import 'package:resonance_network_wallet/services/referral_service.dart'; import 'package:resonance_network_wallet/services/wallet_creation_service.dart'; -@GenerateNiceMocks([MockSpec(), MockSpec(), MockSpec()]) +@GenerateNiceMocks([MockSpec(), MockSpec()]) import 'wallet_creation_service_test.mocks.dart'; void main() { @@ -13,12 +12,10 @@ void main() { test('persists mnemonic, adds root account, and submits referral when no root exists', () async { final settings = MockSettingsService(); final accounts = MockAccountsService(); - final referral = MockReferralService(); final service = WalletCreationService( settingsService: settings, accountsService: accounts, - referralService: referral, ); const mnemonic = 'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about'; @@ -35,7 +32,6 @@ void main() { verify(settings.setMnemonic(mnemonic, 0)).called(1); verify(accounts.addAccount(argThat(isA().having((a) => a.accountId, 'accountId', 'abc')))).called(1); - verify(referral.submitAddressToBackend()).called(1); expect(created.accountId, accountId); expect(created.name, name); @@ -44,12 +40,10 @@ void main() { test('skips add and referral when root account already exists', () async { final settings = MockSettingsService(); final accounts = MockAccountsService(); - final referral = MockReferralService(); final service = WalletCreationService( settingsService: settings, accountsService: accounts, - referralService: referral, ); const existing = Account(walletIndex: 0, index: 0, name: 'Existing', accountId: 'existing_addr'); @@ -64,7 +58,6 @@ void main() { verify(settings.setMnemonic('word ' * 12, 0)).called(1); verifyNever(accounts.addAccount(any)); - verifyNever(referral.submitAddressToBackend()); expect(created, same(existing)); }); }); diff --git a/mobile-app/test/unit/wallet_creation_service_test.mocks.dart b/mobile-app/test/unit/wallet_creation_service_test.mocks.dart index e564696a9..a3840dc30 100644 --- a/mobile-app/test/unit/wallet_creation_service_test.mocks.dart +++ b/mobile-app/test/unit/wallet_creation_service_test.mocks.dart @@ -4,15 +4,12 @@ // ignore_for_file: no_leading_underscores_for_library_prefixes import 'dart:async' as _i5; -import 'dart:ui' as _i9; import 'package:mockito/mockito.dart' as _i1; import 'package:mockito/src/dummies.dart' as _i7; import 'package:quantus_sdk/quantus_sdk.dart' as _i4; import 'package:quantus_sdk/src/models/account.dart' as _i2; import 'package:quantus_sdk/src/models/display_account.dart' as _i6; -import 'package:resonance_network_wallet/services/referral_service.dart' as _i8; -import 'package:share_plus/share_plus.dart' as _i3; // ignore_for_file: type=lint // ignore_for_file: avoid_redundant_argument_values @@ -32,10 +29,6 @@ class _FakeAccount_0 extends _i1.SmartFake implements _i2.Account { _FakeAccount_0(Object parent, Invocation parentInvocation) : super(parent, parentInvocation); } -class _FakeShareParams_1 extends _i1.SmartFake implements _i3.ShareParams { - _FakeShareParams_1(Object parent, Invocation parentInvocation) : super(parent, parentInvocation); -} - /// A class which mocks [SettingsService]. /// /// See the documentation for Mockito's code generation for more information. @@ -534,115 +527,3 @@ class MockAccountsService extends _i1.Mock implements _i4.AccountsService { ) as _i5.Future); } - -/// A class which mocks [ReferralService]. -/// -/// See the documentation for Mockito's code generation for more information. -class MockReferralService extends _i1.Mock implements _i8.ReferralService { - @override - _i5.Future checkPlayStoreReferralCode() => - (super.noSuchMethod( - Invocation.method(#checkPlayStoreReferralCode, []), - returnValue: _i5.Future.value(), - returnValueForMissingStub: _i5.Future.value(), - ) - as _i5.Future); - - @override - _i5.Future optInRewardProgram() => - (super.noSuchMethod( - Invocation.method(#optInRewardProgram, []), - returnValue: _i5.Future.value(), - returnValueForMissingStub: _i5.Future.value(), - ) - as _i5.Future); - - @override - _i5.Future getReferralData() => - (super.noSuchMethod( - Invocation.method(#getReferralData, []), - returnValue: _i5.Future.value(), - returnValueForMissingStub: _i5.Future.value(), - ) - as _i5.Future); - - @override - void invalidateCache() => - super.noSuchMethod(Invocation.method(#invalidateCache, []), returnValueForMissingStub: null); - - @override - _i5.Future getRewardProgramParticiation() => - (super.noSuchMethod( - Invocation.method(#getRewardProgramParticiation, []), - returnValue: _i5.Future.value(false), - returnValueForMissingStub: _i5.Future.value(false), - ) - as _i5.Future); - - @override - _i5.Future submitReferralToBackend({required String? referral}) => - (super.noSuchMethod( - Invocation.method(#submitReferralToBackend, [], {#referral: referral}), - returnValue: _i5.Future.value(), - returnValueForMissingStub: _i5.Future.value(), - ) - as _i5.Future); - - @override - _i5.Future submitAddressToBackend() => - (super.noSuchMethod( - Invocation.method(#submitAddressToBackend, []), - returnValue: _i5.Future.value(), - returnValueForMissingStub: _i5.Future.value(), - ) - as _i5.Future); - - @override - String generateReferralLink(String? referralCode) => - (super.noSuchMethod( - Invocation.method(#generateReferralLink, [referralCode]), - returnValue: _i7.dummyValue(this, Invocation.method(#generateReferralLink, [referralCode])), - returnValueForMissingStub: _i7.dummyValue( - this, - Invocation.method(#generateReferralLink, [referralCode]), - ), - ) - as String); - - @override - _i5.Future<_i2.Account> getMainAccount() => - (super.noSuchMethod( - Invocation.method(#getMainAccount, []), - returnValue: _i5.Future<_i2.Account>.value(_FakeAccount_0(this, Invocation.method(#getMainAccount, []))), - returnValueForMissingStub: _i5.Future<_i2.Account>.value( - _FakeAccount_0(this, Invocation.method(#getMainAccount, [])), - ), - ) - as _i5.Future<_i2.Account>); - - @override - _i5.Future getMyInviteCode() => - (super.noSuchMethod( - Invocation.method(#getMyInviteCode, []), - returnValue: _i5.Future.value( - _i7.dummyValue(this, Invocation.method(#getMyInviteCode, [])), - ), - returnValueForMissingStub: _i5.Future.value( - _i7.dummyValue(this, Invocation.method(#getMyInviteCode, [])), - ), - ) - as _i5.Future); - - @override - _i5.Future<_i3.ShareParams> getShareLinkParameters(_i9.Rect? positionOrigin) => - (super.noSuchMethod( - Invocation.method(#getShareLinkParameters, [positionOrigin]), - returnValue: _i5.Future<_i3.ShareParams>.value( - _FakeShareParams_1(this, Invocation.method(#getShareLinkParameters, [positionOrigin])), - ), - returnValueForMissingStub: _i5.Future<_i3.ShareParams>.value( - _FakeShareParams_1(this, Invocation.method(#getShareLinkParameters, [positionOrigin])), - ), - ) - as _i5.Future<_i3.ShareParams>); -} diff --git a/quantus_sdk/lib/quantus_sdk.dart b/quantus_sdk/lib/quantus_sdk.dart index b3c92ab64..2e7fa322d 100644 --- a/quantus_sdk/lib/quantus_sdk.dart +++ b/quantus_sdk/lib/quantus_sdk.dart @@ -67,7 +67,7 @@ export 'src/services/reversible_transfers_service.dart'; export 'src/services/settings_service.dart'; export 'src/services/substrate_service.dart'; export 'src/services/swap_service.dart'; -export 'src/services/taskmaster_service.dart'; +export 'src/services/quersi_service.dart'; export 'src/services/senoti_service.dart'; export 'src/services/circuit_manager.dart'; export 'src/services/encrypted_account_service.dart'; diff --git a/quantus_sdk/lib/src/constants/app_constants.dart b/quantus_sdk/lib/src/constants/app_constants.dart index fcfdf6939..e6ff3813e 100644 --- a/quantus_sdk/lib/src/constants/app_constants.dart +++ b/quantus_sdk/lib/src/constants/app_constants.dart @@ -19,11 +19,7 @@ class AppConstants { ]; static const List graphQlEndpoints = ['https://sub2.quantus.com/v1/graphql']; - // local test android use special ip - // static const String taskMasterEndpoint = 'http://10.0.2.2:3000/api'; - // local test - // static const String taskMasterEndpoint = 'http://localhost:3000/api'; - static const String taskMasterEndpoint = 'https://quests.quantus.com/api'; + static const String quersiEndpoint = 'https://qrc-1.quantus.com/api'; static const String senotiEndpoint = 'https://snt.quantus.com/api'; diff --git a/quantus_sdk/lib/src/services/quersi_service.dart b/quantus_sdk/lib/src/services/quersi_service.dart new file mode 100644 index 000000000..facec5473 --- /dev/null +++ b/quantus_sdk/lib/src/services/quersi_service.dart @@ -0,0 +1,131 @@ +import 'dart:convert'; + +import 'package:http/http.dart' as http; +import 'package:quantus_sdk/quantus_sdk.dart'; +import 'package:quantus_sdk/src/models/exchange_rates_result.dart'; +import 'package:quantus_sdk/src/utils/print.dart'; + +// Quersi service singleton +class QuersiService { + final _remoteConfigsEndpoint = Uri.parse('${AppConstants.quersiEndpoint}/configs/wallet'); + final _exchangeRatesEndpoint = Uri.parse('${AppConstants.quersiEndpoint}/exchange-rates'); + + static final QuersiService _instance = QuersiService._internal(); + factory QuersiService() => _instance; + QuersiService._internal(); + + final SettingsService _settingsService = SettingsService(); + final HdWalletService _hd = HdWalletService(); + + Future getMiningAccountId() async { + final mnemonic = await _settingsService.getMnemonic(0); + if (mnemonic == null) { + throw Exception('Mnemonic not found.'); + } + final address = _hd.deriveWormholeKeyPair(mnemonic: mnemonic).address; + return address; + } + + // In the past in the beginnings some people mined with a non-derived account + Future getOldMiningAccountId() async { + final mnemonic = await _settingsService.getMnemonic(0); + if (mnemonic == null) { + throw Exception('Mnemonic not found.'); + } + final rawKeyPair = SubstrateService().nonHDdilithiumKeypairFromMnemonic(mnemonic); + return rawKeyPair.ss58Address; + } + + Future getRemoteConfig() async { + final http.Response response = await http.get( + _remoteConfigsEndpoint, + headers: {'Content-Type': 'application/json'}, + ); + if (response.statusCode != 200) { + throw Exception('Configs request failed with status: ${response.statusCode}. Body: ${response.body}'); + } + + final Map? responseBody = jsonDecode(response.body); + final Map? data = responseBody?['data']; + + if (data == null) { + throw Exception('Configs request failed with status: ${response.statusCode}. Body: ${response.body}'); + } + + return RemoteConfigModel.fromJson(data); + } + + Future getExchangeRates() async { + final http.Response response = await http.get( + _exchangeRatesEndpoint, + headers: {'Content-Type': 'application/json'}, + ); + if (response.statusCode != 200) { + throw Exception('Exchange rates request failed with status: ${response.statusCode}. Body: ${response.body}'); + } + + final Map? responseBody = jsonDecode(response.body); + final Map? data = responseBody?['data']; + + if (data == null) { + throw Exception('Exchange rates not found!'); + } + + return ExchangeRatesResult.fromJson(data); + } + + Future getMinerStats() async { + final String minerStatsQuery = r''' + query MinerStats($ids: [String!]!) { + minerStats: account_stats(where: {id: {_in: $ids}}) { + totalMinedBlocks: total_mined_blocks + totalRewards: total_rewards + id + } + } + '''; + + final miningAccountId = await getMiningAccountId(); + final List accountIds = [miningAccountId]; + + final Map requestBody = { + 'query': minerStatsQuery, + 'variables': {'ids': accountIds}, + }; + + try { + final http.Response response = await GraphQlEndpointService().post(body: jsonEncode(requestBody)); + + if (response.statusCode != 200) { + throw Exception('GraphQL request failed with status: ${response.statusCode}. Body: ${response.body}'); + } + + final Map responseBody = jsonDecode(response.body); + if (responseBody['errors'] != null) { + throw Exception('GraphQL errors: ${responseBody['errors']}'); + } + + final Map data = responseBody['data']; + + final List? minerStatsList = data['minerStats']; + if (minerStatsList == null || minerStatsList.isEmpty) { + return MinerStats(totalMinedBlocks: 0, totalRewards: BigInt.zero); + } + + // Aggregate stats across all accounts + int totalMinedBlocks = 0; + BigInt totalRewards = BigInt.zero; + + for (final stats in minerStatsList) { + totalMinedBlocks += int.parse(stats['totalMinedBlocks'].toString()); + totalRewards += BigInt.parse(stats['totalRewards'].toString()); + } + + return MinerStats(totalMinedBlocks: totalMinedBlocks, totalRewards: totalRewards); + } catch (e, stackTrace) { + quantusPrint('Error fetching miner stats: $e'); + quantusPrint('$stackTrace'); + rethrow; + } + } +} diff --git a/quantus_sdk/lib/src/services/substrate_service.dart b/quantus_sdk/lib/src/services/substrate_service.dart index a31d528f7..978deb82a 100644 --- a/quantus_sdk/lib/src/services/substrate_service.dart +++ b/quantus_sdk/lib/src/services/substrate_service.dart @@ -400,7 +400,6 @@ class SubstrateService { // and would otherwise leak balances into the next wallet session. await WormholeUtxoService.clearAllCaches(); await EncryptedAccountService.clearAllPersistedState(); - TaskmasterService().logout(); } Future generateMnemonic() async { diff --git a/quantus_sdk/lib/src/services/taskmaster_service.dart b/quantus_sdk/lib/src/services/taskmaster_service.dart deleted file mode 100644 index 8105ee830..000000000 --- a/quantus_sdk/lib/src/services/taskmaster_service.dart +++ /dev/null @@ -1,632 +0,0 @@ -import 'dart:convert'; - -import 'package:convert/convert.dart' as convert_hex; -import 'package:http/http.dart' as http; -import 'package:quantus_sdk/quantus_sdk.dart'; -import 'package:quantus_sdk/src/models/exchange_rates_result.dart'; -import 'package:quantus_sdk/src/models/oauth_link.dart'; -import 'package:quantus_sdk/src/utils/print.dart'; - -class TokenInfo { - final String accessToken; - final DateTime expiresAt; - final DateTime issuedAt; - - TokenInfo({required this.accessToken, required this.expiresAt, required this.issuedAt}); - - bool get isExpired => DateTime.now().isAfter(expiresAt); - bool get isNearExpiry => DateTime.now().add(const Duration(minutes: 30)).isAfter(expiresAt); - - Map toJson() => { - 'accessToken': accessToken, - 'expiresAt': expiresAt.toIso8601String(), - 'issuedAt': issuedAt.toIso8601String(), - }; - - factory TokenInfo.fromJson(Map json) => TokenInfo( - accessToken: json['accessToken'], - expiresAt: DateTime.parse(json['expiresAt']), - issuedAt: DateTime.parse(json['issuedAt']), - ); -} - -class JWTAuthenticatedHttpClient extends http.BaseClient { - final TaskmasterService _service; - final http.Client _inner = http.Client(); - - JWTAuthenticatedHttpClient(this._service); - - @override - Future send(http.BaseRequest request) async { - await _service.ensureIsLoggedIn(); - final token = _service.accessToken; - - if (token == null) throw Exception('Missing token'); - - request.headers['Authorization'] = 'Bearer $token'; - request.headers['Content-Type'] = 'application/json'; - - return _inner.send(request); - } -} - -class TaskMasterAuthClient { - final String taskMasterEndpointUrl; - final http.Client _client; - - TaskMasterAuthClient(this.taskMasterEndpointUrl, {http.Client? client}) : _client = client ?? http.Client(); - - Future> requestChallenge() async { - quantusPrint('request challenge'); - final r = await _client.post( - Uri.parse('$taskMasterEndpointUrl/auth/request-challenge'), - headers: {'content-type': 'application/json'}, - body: jsonEncode({}), - ); - if (r.statusCode != 200) { - throw Exception('request-challenge failed: ${r.statusCode} ${r.body}'); - } - final j = jsonDecode(r.body) as Map; - return {'temp_session_id': j['temp_session_id'] as String, 'challenge': j['challenge'] as String}; - } - - Future verify({ - required String tempSessionId, - required String ss58Address, - required String publicKeyHex, - required String signatureHex, - }) async { - quantusPrint('verify $tempSessionId $taskMasterEndpointUrl'); - final r = await _client.post( - Uri.parse('$taskMasterEndpointUrl/auth/verify'), - headers: {'content-type': 'application/json'}, - body: jsonEncode({ - 'temp_session_id': tempSessionId, - 'address': ss58Address, - 'public_key': publicKeyHex, - 'signature': signatureHex, - }), - ); - if (r.statusCode != 200) { - throw Exception('verify failed: ${r.statusCode}'); - } - final j = jsonDecode(r.body) as Map; - quantusPrint('verify response: ${r.body}'); - return j['access_token'] as String; - } - - Future> me(String accessToken) async { - final r = await _client.get(Uri.parse('$taskMasterEndpointUrl/auth/me'), headers: getAuthHeaders(accessToken)); - if (r.statusCode != 200) { - throw Exception('me failed: ${r.statusCode}'); - } - return jsonDecode(r.body) as Map; - } - - Future login({ - required String ss58Address, - required String publicKeyHex, - required Future Function(List messageBytes) signHex, - }) async { - final ch = await requestChallenge(); - quantusPrint('challenge: $ch'); - final msg = 'taskmaster:login:1|challenge=${ch['challenge']}|address=$ss58Address'; - quantusPrint('msg: $msg'); - final sigHex = await signHex(utf8.encode(msg)); - return verify( - tempSessionId: ch['temp_session_id']!, - ss58Address: ss58Address, - publicKeyHex: publicKeyHex, - signatureHex: sigHex, - ); - } - - Map getAuthHeaders(String? accessToken) { - return {'authorization': 'Bearer $accessToken'}; - } -} - -// Task master service singleton -class TaskmasterService { - final _referralEndpoint = Uri.parse('${AppConstants.taskMasterEndpoint}/referrals'); - final _ethAssociationsEndpoint = Uri.parse('${AppConstants.taskMasterEndpoint}/addresses/associations/eth'); - final _xAssociationsEndpoint = Uri.parse('${AppConstants.taskMasterEndpoint}/addresses/associations/x'); - final _remoteConfigsEndpoint = Uri.parse('${AppConstants.taskMasterEndpoint}/configs/wallet'); - final _exchangeRatesEndpoint = Uri.parse('${AppConstants.taskMasterEndpoint}/exchange-rates'); - - final String _minerStatsQuery = r''' - query MinerStats($ids: [String!]!) { - minerStats: account_stats(where: {id: {_in: $ids}}) { - totalMinedBlocks: total_mined_blocks - totalRewards: total_rewards - id - } - } - '''; - - static final TaskmasterService _instance = TaskmasterService._internal(); - factory TaskmasterService() => _instance; - TaskmasterService._internal(); - - final SettingsService _settingsService = SettingsService(); - final HdWalletService _hd = HdWalletService(); - TokenInfo? _tokenInfo; - String? get accessToken => _tokenInfo?.accessToken; - bool get isLoggedIn => _tokenInfo != null && !_tokenInfo!.isExpired; - - TaskMasterAuthClient get _client => TaskMasterAuthClient(AppConstants.taskMasterEndpoint); - JWTAuthenticatedHttpClient get _authenticatedHttpClient => JWTAuthenticatedHttpClient(this); - - void _clearToken() { - _tokenInfo = null; - } - - String _getEthAssociationsBody(String ethAddress) { - final Map requestBody = {'eth_address': ethAddress}; - - return jsonEncode(requestBody); - } - - String _getXAssociationsBody(String username) { - final Map requestBody = {'username': username}; - - return jsonEncode(requestBody); - } - - Future getMiningAccountId() async { - final mnemonic = await _settingsService.getMnemonic(0); - if (mnemonic == null) { - throw Exception('Mnemonic not found.'); - } - final address = _hd.deriveWormholeKeyPair(mnemonic: mnemonic).address; - return address; - } - - // In the past in the beginnings some people mined with a non-derived account - Future getOldMiningAccountId() async { - final mnemonic = await _settingsService.getMnemonic(0); - if (mnemonic == null) { - throw Exception('Mnemonic not found.'); - } - final rawKeyPair = SubstrateService().nonHDdilithiumKeypairFromMnemonic(mnemonic); - return rawKeyPair.ss58Address; - } - - Future loginWithAccount1() async { - final mnemonic = await _settingsService.getMnemonic(0); - if (mnemonic == null) { - throw Exception('Mnemonic not found.'); - } - final keypair = _hd.keyPairAtIndex(mnemonic, 0); - final ss58Address = keypair.ss58Address; - final publicKeyHex = convert_hex.hex.encode(keypair.publicKey); - - Future signHex(List messageBytes) async { - final sig = keypair.sign(messageBytes); - return convert_hex.hex.encode(sig); - } - - final accessToken = await _client.login(ss58Address: ss58Address, publicKeyHex: publicKeyHex, signHex: signHex); - - final now = DateTime.now(); - final expiresAt = now.add(const Duration(hours: 24)); - - return TokenInfo(accessToken: accessToken, expiresAt: expiresAt, issuedAt: now); - } - - Future> me(String accessToken) { - return _client.me(accessToken); - } - - Map getAuthHeaders() { - return _client.getAuthHeaders(accessToken); - } - - Future ensureIsLoggedIn() async { - quantusPrint('ensureIsLoggedIn'); - - if (_tokenInfo != null && !_tokenInfo!.isExpired) { - if (_tokenInfo!.isNearExpiry) { - try { - _tokenInfo = await loginWithAccount1(); - return true; - } catch (error) { - quantusPrint('Token refresh failed: $error'); - _clearToken(); - } - } else { - quantusPrint('is logged in by token expiry'); - return true; - } - } - - try { - _tokenInfo = await loginWithAccount1(); - return true; - } catch (error) { - quantusPrint('Login failed: $error'); - return false; - } - } - - // Submit a referral code - Future submitReferral(String referralCode) async { - quantusPrint('submitReferral $referralCode'); - final Map requestBody = {'referral_code': referralCode.toLowerCase()}; - - final http.Response response = await _authenticatedHttpClient.post( - _referralEndpoint, - body: jsonEncode(requestBody), - ); - - if (response.statusCode != 200) { - throw Exception('Referral http request failed with status: ${response.statusCode}. Body: ${response.body}'); - } - } - - Future addRaidSubmission(String replyTweetLink) async { - quantusPrint('add raid submission $replyTweetLink'); - - final raiderSubmissionsEndpoint = Uri.parse('${AppConstants.taskMasterEndpoint}/raid-quests/submissions'); - final Map requestBody = {'tweet_reply_link': replyTweetLink}; - - final http.Response response = await _authenticatedHttpClient.post( - raiderSubmissionsEndpoint, - body: jsonEncode(requestBody), - ); - - if (response.statusCode != 201) { - throw Exception('Error ${response.statusCode}: ${response.body}'); - } - } - - Future removeRaidSubmission(String id) async { - quantusPrint('Remove raid submission $id'); - - final raiderSubmissionsEndpoint = Uri.parse('${AppConstants.taskMasterEndpoint}/raid-quests/submissions/$id'); - final Map requestBody = {}; - - final http.Response response = await _authenticatedHttpClient.delete( - raiderSubmissionsEndpoint, - body: jsonEncode(requestBody), - ); - - if (response.statusCode != 204) { - throw Exception('Error ${response.statusCode}: ${response.body}'); - } - } - - Future getActiveRaidRaiderSubmissions() async { - final activeAccount = await getMainAccount(); - quantusPrint('getActiveRaidRaiderSubmissions ${activeAccount.accountId}'); - final raiderSubmissionsEndpoint = Uri.parse('${AppConstants.taskMasterEndpoint}/raid-quests/submissions/me'); - - final http.Response response = await _authenticatedHttpClient.get( - raiderSubmissionsEndpoint, - headers: {'Content-Type': 'application/json'}, - ); - - final Map responseBody = jsonDecode(response.body); - - if (response.statusCode == 404) { - final error = (responseBody['error'] as String?)?.toLowerCase(); - - if (error == 'no active raid is found') { - return const NoActiveRaid(); - } else if (error == "user doesn't have x association") { - return const NoTwitterLinked(); - } - } - - if (response.statusCode != 200) { - throw Exception( - 'Get raider submissions http request failed with status: ${response.statusCode}. Body: ${response.body}', - ); - } - - final data = responseBody['data'] as Map?; - - return RaiderSubmissionsOk( - activeRaid: RaidQuest.fromJson(data?['current_raid']), - submissions: List.from(data?['submissions']), - ); - } - - Future associateEthAddress(String ethAddress) async { - quantusPrint('associateEthAddress $ethAddress'); - - final http.Response response = await _authenticatedHttpClient.post( - _ethAssociationsEndpoint, - body: _getEthAssociationsBody(ethAddress), - ); - - if (response.statusCode != 200) { - throw Exception('Associate ETH http request failed with status: ${response.statusCode}. Body: ${response.body}'); - } - } - - Future updateAssociatedEthAddress(String ethAddress) async { - quantusPrint('updateAssociatedEthAddress $ethAddress'); - - final http.Response response = await _authenticatedHttpClient.put( - _ethAssociationsEndpoint, - body: _getEthAssociationsBody(ethAddress), - ); - - if (response.statusCode != 200) { - throw Exception('Associate ETH http request failed with status: ${response.statusCode}. Body: ${response.body}'); - } - } - - Future dissociateEthAddress() async { - quantusPrint('dissociateEthAddress'); - - final http.Response response = await _authenticatedHttpClient.delete(_ethAssociationsEndpoint); - - if (response.statusCode != 204) { - throw Exception('Dissociate ETH http request failed with status: ${response.statusCode}. Body: ${response.body}'); - } - } - - Future generateAssociateXLink() async { - quantusPrint('generateAssociateXLink'); - final xAssociationsEndpoint = Uri.parse('${AppConstants.taskMasterEndpoint}/auth/x/link'); - - final http.Response response = await _authenticatedHttpClient.get(xAssociationsEndpoint); - - if (response.statusCode != 200) { - throw Exception( - 'Generate X link http request failed with status: ${response.statusCode}. Body: ${response.body}', - ); - } - - final json = jsonDecode(response.body) as Map; - return OAuthLink.fromJson(json); - } - - Future associateXHandle(String username) async { - quantusPrint('associateXHandle $username'); - - final http.Response response = await _authenticatedHttpClient.post( - _xAssociationsEndpoint, - body: _getXAssociationsBody(username), - ); - - if (response.statusCode != 204) { - throw Exception('Associate X http request failed with status: ${response.statusCode}. Body: ${response.body}'); - } - } - - Future dissociateXAccount() async { - quantusPrint('dissociateXAccount'); - - final http.Response response = await _authenticatedHttpClient.delete(_xAssociationsEndpoint); - - if (response.statusCode != 204) { - throw Exception('Dissociate X http request failed with status: ${response.statusCode}. Body: ${response.body}'); - } - } - - Future optInRewardProgram() async { - final activeAccount = await getMainAccount(); - final rewardProgramEndpoint = Uri.parse( - '${AppConstants.taskMasterEndpoint}/addresses/${activeAccount.accountId}/reward-program', - ); - - quantusPrint('opt in reward program for ${activeAccount.name} ${activeAccount.accountId}'); - final Map requestBody = {'new_status': true}; - - final http.Response response = await _authenticatedHttpClient.put( - rewardProgramEndpoint, - body: jsonEncode(requestBody), - ); - - if (response.statusCode != 204) { - throw Exception('Referral http request failed with status: ${response.statusCode}. Body: ${response.body}'); - } - } - - Future getMainAccount() async { - final account = await _settingsService.getAccount(walletIndex: 0, index: 0); - if (account == null) { - throw Exception('No main account - this method should probably not be called when logged out'); - } - return account; - } - - Future getRewardProgramParticipation() async { - final activeAccount = await getMainAccount(); - quantusPrint('getRewardProgramParticipation ${activeAccount.accountId}'); - final rewardProgramEndpoint = Uri.parse( - '${AppConstants.taskMasterEndpoint}/addresses/${activeAccount.accountId}/reward-program', - ); - - try { - final http.Response response = await http.get( - rewardProgramEndpoint, - headers: {'Content-Type': 'application/json'}, - ); - - if (response.statusCode == 404) { - final Map responseBody = jsonDecode(response.body); - if (responseBody['error']?.toLowerCase() == 'address not found') { - quantusPrint('user not enrolled in reward program'); - return false; - } - } - - if (response.statusCode != 200) { - throw Exception( - 'Reward Program http request failed with status: ${response.statusCode}. Body: ${response.body}', - ); - } - - final Map responseBody = jsonDecode(response.body); - if (responseBody['error'] != null) { - throw Exception('HTTP error: ${responseBody['error']}'); - } - - final bool data = responseBody['data']; - - return data; - } catch (e, stackTrace) { - quantusPrint('Error fetching miner stats: $e'); - quantusPrint('$stackTrace'); - - return false; - } - } - - Future _authenticatedGet(Uri uri, T Function(Map) fromJson) async { - try { - final response = await _authenticatedHttpClient.get(uri); - - if (response.statusCode != 200) { - throw Exception('HTTP request failed with status: ${response.statusCode}. Body: ${response.body}'); - } - - final json = jsonDecode(response.body) as Map; - return fromJson(json); - } catch (e, stackTrace) { - quantusPrint('Error fetching data from $uri: $e'); - quantusPrint('$stackTrace'); - rethrow; - } - } - - Future getAccountAssociations() async { - final activeAccount = await getMainAccount(); - quantusPrint('getAccountAssociations ${activeAccount.accountId}'); - final accountAssociationsEndpoint = Uri.parse('${AppConstants.taskMasterEndpoint}/addresses/associations'); - return _authenticatedGet(accountAssociationsEndpoint, AccountAssociations.fromJson); - } - - Future submitAddress() async { - await ensureIsLoggedIn(); - } - - Future getRemoteConfig() async { - final http.Response response = await http.get( - _remoteConfigsEndpoint, - headers: {'Content-Type': 'application/json'}, - ); - if (response.statusCode != 200) { - throw Exception('Configs request failed with status: ${response.statusCode}. Body: ${response.body}'); - } - - final Map? responseBody = jsonDecode(response.body); - final Map? data = responseBody?['data']; - - if (data == null) { - throw Exception('Configs request failed with status: ${response.statusCode}. Body: ${response.body}'); - } - - return RemoteConfigModel.fromJson(data); - } - - Future getExchangeRates() async { - final http.Response response = await http.get( - _exchangeRatesEndpoint, - headers: {'Content-Type': 'application/json'}, - ); - if (response.statusCode != 200) { - throw Exception('Exchange rates request failed with status: ${response.statusCode}. Body: ${response.body}'); - } - - final Map? responseBody = jsonDecode(response.body); - final Map? data = responseBody?['data']; - - if (data == null) { - throw Exception('Exchange rates not found!'); - } - - return ExchangeRatesResult.fromJson(data); - } - - Future getMinerStats() async { - final miningAccountId = await getMiningAccountId(); - final List accountIds = [miningAccountId]; - - final Map requestBody = { - 'query': _minerStatsQuery, - 'variables': {'ids': accountIds}, - }; - - try { - final http.Response response = await GraphQlEndpointService().post(body: jsonEncode(requestBody)); - - if (response.statusCode != 200) { - throw Exception('GraphQL request failed with status: ${response.statusCode}. Body: ${response.body}'); - } - - final Map responseBody = jsonDecode(response.body); - if (responseBody['errors'] != null) { - throw Exception('GraphQL errors: ${responseBody['errors']}'); - } - - final Map data = responseBody['data']; - - final List? minerStatsList = data['minerStats']; - if (minerStatsList == null || minerStatsList.isEmpty) { - return MinerStats(totalMinedBlocks: 0, totalRewards: BigInt.zero); - } - - // Aggregate stats across all accounts - int totalMinedBlocks = 0; - BigInt totalRewards = BigInt.zero; - - for (final stats in minerStatsList) { - totalMinedBlocks += int.parse(stats['totalMinedBlocks'].toString()); - totalRewards += BigInt.parse(stats['totalRewards'].toString()); - } - - return MinerStats(totalMinedBlocks: totalMinedBlocks, totalRewards: totalRewards); - } catch (e, stackTrace) { - quantusPrint('Error fetching miner stats: $e'); - quantusPrint('$stackTrace'); - rethrow; - } - } - - Future getAccountStats() async { - final account = await getMainAccount(); - final Uri uri = Uri.parse('${AppConstants.taskMasterEndpoint}/addresses/${account.accountId}/stats'); - - try { - final http.Response response = await http.get(uri, headers: {'Content-Type': 'application/json'}); - - if (response.statusCode != 200) { - throw Exception('HTTP request failed with status: ${response.statusCode}. Body: ${response.body}'); - } - - final json = jsonDecode(response.body) as Map; - return AccountStats.fromJson(json); - } catch (e, stackTrace) { - quantusPrint('Error fetching address stats: $e'); - quantusPrint('$stackTrace'); - rethrow; - } - } - - Future getOptInPosition() async { - final Uri uri = Uri.parse('${AppConstants.taskMasterEndpoint}/addresses/my-position'); - return _authenticatedGet(uri, OptedInPosition.fromJson); - } - - void logout() { - _clearToken(); - } - - Future getReferralRank(String referralCode) async { - final Uri uri = Uri.parse('${AppConstants.taskMasterEndpoint}/addresses/leaderboard?referral_code=$referralCode'); - return _authenticatedGet(uri, ReferralRank.fromJson); - } - - Future getRaidStats(int raidId) async { - final activeAccount = await getMainAccount(); - final Uri uri = Uri.parse( - '${AppConstants.taskMasterEndpoint}/raid-quests/raiders/${activeAccount.accountId}/leaderboards/$raidId', - ); - return _authenticatedGet(uri, RaidStats.fromJson); - } -} From 2086864c201a87e48e6dddab022909e3af2d4e6d Mon Sep 17 00:00:00 2001 From: Beast Date: Wed, 5 Aug 2026 14:10:50 +0800 Subject: [PATCH 2/2] chore: formatting --- mobile-app/lib/app.dart | 1 - mobile-app/lib/services/wallet_creation_service.dart | 8 +++----- mobile-app/test/unit/wallet_creation_service_test.dart | 10 ++-------- 3 files changed, 5 insertions(+), 14 deletions(-) diff --git a/mobile-app/lib/app.dart b/mobile-app/lib/app.dart index 8043d5672..8bd6229bd 100644 --- a/mobile-app/lib/app.dart +++ b/mobile-app/lib/app.dart @@ -18,7 +18,6 @@ class ResonanceWalletApp extends ConsumerStatefulWidget { } class _ResonanceWalletAppState extends ConsumerState { - @override void initState() { super.initState(); diff --git a/mobile-app/lib/services/wallet_creation_service.dart b/mobile-app/lib/services/wallet_creation_service.dart index be8715032..51aa7a7da 100644 --- a/mobile-app/lib/services/wallet_creation_service.dart +++ b/mobile-app/lib/services/wallet_creation_service.dart @@ -6,11 +6,9 @@ class WalletCreationService { final SettingsService _settings; final AccountsService _accounts; - WalletCreationService({ - SettingsService? settingsService, - AccountsService? accountsService, - }) : _settings = settingsService ?? SettingsService(), - _accounts = accountsService ?? AccountsService(); + WalletCreationService({SettingsService? settingsService, AccountsService? accountsService}) + : _settings = settingsService ?? SettingsService(), + _accounts = accountsService ?? AccountsService(); /// Saves [mnemonic] for [walletIndex], adds the root account when missing, /// and runs referral registration for brand-new roots. diff --git a/mobile-app/test/unit/wallet_creation_service_test.dart b/mobile-app/test/unit/wallet_creation_service_test.dart index 325d296e0..b3404f00a 100644 --- a/mobile-app/test/unit/wallet_creation_service_test.dart +++ b/mobile-app/test/unit/wallet_creation_service_test.dart @@ -13,10 +13,7 @@ void main() { final settings = MockSettingsService(); final accounts = MockAccountsService(); - final service = WalletCreationService( - settingsService: settings, - accountsService: accounts, - ); + final service = WalletCreationService(settingsService: settings, accountsService: accounts); const mnemonic = 'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about'; const accountId = 'abc'; @@ -41,10 +38,7 @@ void main() { final settings = MockSettingsService(); final accounts = MockAccountsService(); - final service = WalletCreationService( - settingsService: settings, - accountsService: accounts, - ); + final service = WalletCreationService(settingsService: settings, accountsService: accounts); const existing = Account(walletIndex: 0, index: 0, name: 'Existing', accountId: 'existing_addr');