diff --git a/.github/workflows/syrius_builder.yml b/.github/workflows/syrius_builder.yml index 103ace09..5ece9d21 100644 --- a/.github/workflows/syrius_builder.yml +++ b/.github/workflows/syrius_builder.yml @@ -12,6 +12,9 @@ on: jobs: build-macos: + environment: wallet-connect + env: + WALLET_CONNECT_PROJECT_ID: ${{ secrets.WC_PROJECT_ID }} runs-on: macos-12 steps: - name: Checkout @@ -23,15 +26,16 @@ jobs: - name: Checkout uses: actions/checkout@v3 - name: Setup Flutter - uses: subosito/flutter-action@v2 + uses: subosito/flutter-action@v2.10.0 with: channel: "stable" + flutter-version: "3.10.2" - name: Check flutter version run: flutter --version - name: Build syrius desktop run: | flutter config --enable-macos-desktop - flutter build macos --release + flutter build macos --dart-define=WC_PROJECT_ID=$WALLET_CONNECT_PROJECT_ID --release - name: Package into DMG run: | create-dmg --volname syrius \ @@ -53,20 +57,24 @@ jobs: name: macos-artifacts path: syrius-alphanet-macos-universal.dmg build-windows: + environment: wallet-connect + env: + WALLET_CONNECT_PROJECT_ID: ${{ secrets.WC_PROJECT_ID }} runs-on: windows-latest steps: - name: Checkout uses: actions/checkout@v3 - name: Setup Flutter - uses: subosito/flutter-action@v2 + uses: subosito/flutter-action@v2.10.0 with: channel: "stable" + flutter-version: "3.10.2" - name: Check flutter version run: flutter --version - name: Build syrius desktop run: | flutter config --enable-windows-desktop - flutter build windows --release + flutter build windows --dart-define=WC_PROJECT_ID=$env:WALLET_CONNECT_PROJECT_ID --release - name: Package into zip run: | Compress-Archive -Path build\windows\runner\Release\* -DestinationPath .\syrius-alphanet-windows-amd64.zip @@ -76,6 +84,9 @@ jobs: name: windows-artifacts path: syrius-alphanet-windows-amd64.zip build-linux: + environment: wallet-connect + env: + WALLET_CONNECT_PROJECT_ID: ${{ secrets.WC_PROJECT_ID }} runs-on: ubuntu-20.04 steps: - name: Checkout @@ -85,15 +96,21 @@ jobs: sudo apt update sudo apt install -y curl clang cmake ninja-build pkg-config libgtk-3-dev liblzma-dev unzip xz-utils zip libnotify-dev libayatana-appindicator3-dev - name: Setup Flutter - uses: subosito/flutter-action@v2 + uses: subosito/flutter-action@v2.10.0 with: channel: "stable" + flutter-version: "3.10.2" + - name: Set permissions + run: | + sudo chmod -R 777 linux/ - name: Check flutter version - run: flutter --version + run: | + which flutter + flutter --version - name: Build syrius desktop run: | flutter config --enable-linux-desktop - flutter build linux --release + flutter build linux --dart-define=WC_PROJECT_ID=$WALLET_CONNECT_PROJECT_ID --release -v - name: Package zip run: | cd build/linux/x64/release/bundle diff --git a/.gitignore b/.gitignore index 7cbd829e..6b7cc2b9 100644 --- a/.gitignore +++ b/.gitignore @@ -20,7 +20,7 @@ .classpath .project .settings/ -.vscode/ +.vscode/* # Flutter repo-specific /bin/cache/ @@ -51,7 +51,7 @@ analysis_benchmark.json .flutter-plugins-dependencies **/generated_plugin_registrant.dart .packages -.pub-cache/ +.pub-preload-cache/ .pub/ build/ flutter_*.png @@ -59,9 +59,6 @@ linked_*.ds unlinked.ds unlinked_spec.ds -# Web related -lib/generated_plugin_registrant.dart - # Android related **/android/**/gradle-wrapper.jar .gradle/ @@ -104,56 +101,21 @@ lib/generated_plugin_registrant.dart **/ios/Runner/GeneratedPluginRegistrant.* # macOS +**/Flutter/ephemeral/ +**/Pods/ **/macos/Flutter/GeneratedPluginRegistrant.swift **/macos/Flutter/ephemeral +**/xcuserdata/ -# Xcode -# -# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore - -## User settings -xcuserdata/ - -## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9) -*.xcscmblueprint -*.xccheckout - -## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4) -build/ -DerivedData/ -*.moved-aside -*.pbxuser -!default.pbxuser -*.mode1v3 -!default.mode1v3 -*.mode2v3 -!default.mode2v3 -*.perspectivev3 -!default.perspectivev3 - -# Windows thumbnail cache files -Thumbs.db -ehthumbs.db -ehthumbs_vista.db +# Windows +**/windows/flutter/generated_plugin_registrant.cc +**/windows/flutter/generated_plugin_registrant.h +**/windows/flutter/generated_plugins.cmake -# Dump file -*.stackdump - -# Folder config file -[Dd]esktop.ini - -# Recycle Bin used on file shares -$RECYCLE.BIN/ - -# Windows Installer files -*.cab -*.msi -*.msix -*.msm -*.msp - -# Windows shortcuts -*.lnk +# Linux +**/linux/flutter/generated_plugin_registrant.cc +**/linux/flutter/generated_plugin_registrant.h +**/linux/flutter/generated_plugins.cmake # Coverage coverage/ @@ -161,9 +123,6 @@ coverage/ # Symbols app.*.symbols -# Obfuscation related -app.*.map.json - # Exceptions to above rules. !**/ios/**/default.mode1v3 !**/ios/**/default.mode2v3 @@ -171,4 +130,4 @@ app.*.map.json !**/ios/**/default.perspectivev3 !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages !/dev/ci/**/Gemfile.lock -!pubspec.lock \ No newline at end of file +!.vscode/settings.json \ No newline at end of file diff --git a/.metadata b/.metadata new file mode 100644 index 00000000..0b303072 --- /dev/null +++ b/.metadata @@ -0,0 +1,30 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled. + +version: + revision: 4d9e56e694b656610ab87fcf2efbcd226e0ed8cf + channel: stable + +project_type: app + +# Tracks metadata for the flutter migrate command +migration: + platforms: + - platform: root + create_revision: 4d9e56e694b656610ab87fcf2efbcd226e0ed8cf + base_revision: 4d9e56e694b656610ab87fcf2efbcd226e0ed8cf + - platform: windows + create_revision: 4d9e56e694b656610ab87fcf2efbcd226e0ed8cf + base_revision: 4d9e56e694b656610ab87fcf2efbcd226e0ed8cf + + # User provided section + + # List of Local paths (relative to this file) that should be + # ignored by the migrate tool. + # + # Files that are not part of the templates will be ignored by default. + unmanaged_files: + - 'lib/main.dart' + - 'ios/Runner.xcodeproj/project.pbxproj' diff --git a/README.md b/README.md index 74e4e375..927269a9 100644 --- a/README.md +++ b/README.md @@ -27,18 +27,19 @@ Dependencies: Currently supported ``: `windows`, `macos`, `linux` +The [WalletConnect](https://github.com/WalletConnect) integration requires setting the `WC_PROJECT_ID` environmental variable. + ```bash git clone https://github.com/zenon-network/syrius.git flutter pub get -flutter build -flutter build --release -flutter run --release +flutter run --dart-define=WC_PROJECT_ID=walletconnect_project_id -d +flutter build --dart-define=WC_PROJECT_ID=walletconnect_project_id ``` ## Contributing -Please check CONTRIBUTING for more details. +Please check [CONTRIBUTING](./CONTRIBUTING.md) for more details. ## License -The MIT License (MIT). Please check LICENSE for more information. +The MIT License (MIT). Please check [LICENSE](./LICENSE) for more information. diff --git a/assets/svg/walletconnect-logo.svg b/assets/svg/walletconnect-logo.svg new file mode 100644 index 00000000..a2243de9 --- /dev/null +++ b/assets/svg/walletconnect-logo.svg @@ -0,0 +1,12 @@ + + + + WalletConnect + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/lib/blocs/accelerator/accelerator_balance_bloc.dart b/lib/blocs/accelerator/accelerator_balance_bloc.dart index 29a23350..e42a2440 100644 --- a/lib/blocs/accelerator/accelerator_balance_bloc.dart +++ b/lib/blocs/accelerator/accelerator_balance_bloc.dart @@ -11,7 +11,8 @@ class AcceleratorBalanceBloc extends BaseBloc { AccountInfo accountInfo = await zenon!.ledger.getAccountInfoByAddress( acceleratorAddress, ); - if (accountInfo.qsr()! > 0 || accountInfo.znn()! > 0) { + if (accountInfo.qsr()! > BigInt.zero || + accountInfo.znn()! > BigInt.zero) { addEvent(accountInfo); } else { throw 'Accelerator fund empty'; diff --git a/lib/blocs/accelerator/create_phase_bloc.dart b/lib/blocs/accelerator/create_phase_bloc.dart index 7d98d37a..905660d5 100644 --- a/lib/blocs/accelerator/create_phase_bloc.dart +++ b/lib/blocs/accelerator/create_phase_bloc.dart @@ -9,8 +9,8 @@ class CreatePhaseBloc extends BaseBloc { String name, String description, String url, - int znnFundsNeeded, - int qsrFundsNeeded, + BigInt znnFundsNeeded, + BigInt qsrFundsNeeded, ) async { try { addEvent(null); diff --git a/lib/blocs/accelerator/create_project_bloc.dart b/lib/blocs/accelerator/create_project_bloc.dart index aa00771f..f56786f9 100644 --- a/lib/blocs/accelerator/create_project_bloc.dart +++ b/lib/blocs/accelerator/create_project_bloc.dart @@ -8,8 +8,8 @@ class CreateProjectBloc extends BaseBloc { String name, String description, String url, - int znnFundsNeeded, - int qsrFundsNeeded, + BigInt znnFundsNeeded, + BigInt qsrFundsNeeded, ) { try { addEvent(null); @@ -26,7 +26,7 @@ class CreateProjectBloc extends BaseBloc { 'creating project', ).then( (block) { - AddressUtils.refreshBalance(); + ZenonAddressUtils.refreshBalance(); addEvent(block); }, ).onError( diff --git a/lib/blocs/accelerator/submit_donation_bloc.dart b/lib/blocs/accelerator/submit_donation_bloc.dart index 50198852..388c232e 100644 --- a/lib/blocs/accelerator/submit_donation_bloc.dart +++ b/lib/blocs/accelerator/submit_donation_bloc.dart @@ -1,23 +1,22 @@ import 'package:zenon_syrius_wallet_flutter/blocs/blocs.dart'; import 'package:zenon_syrius_wallet_flutter/main.dart'; import 'package:zenon_syrius_wallet_flutter/utils/account_block_utils.dart'; -import 'package:zenon_syrius_wallet_flutter/utils/extensions.dart'; import 'package:zenon_syrius_wallet_flutter/utils/zts_utils.dart'; import 'package:znn_sdk_dart/znn_sdk_dart.dart'; class SubmitDonationBloc extends BaseBloc { - Future submitDonation(num znnAmount, num qsrAmount) async { + Future submitDonation(BigInt znnAmount, BigInt qsrAmount) async { try { addEvent(null); - if (znnAmount > 0) { + if (znnAmount > BigInt.zero) { await _sendDonationBlock(zenon!.embedded.accelerator.donate( - znnAmount.extractDecimals(znnDecimals), + znnAmount, kZnnCoin.tokenStandard, )); } - if (qsrAmount > 0) { + if (qsrAmount > BigInt.zero) { await _sendDonationBlock(zenon!.embedded.accelerator.donate( - qsrAmount.extractDecimals(qsrDecimals), + qsrAmount, kQsrCoin.tokenStandard, )); } diff --git a/lib/blocs/accelerator/update_phase_bloc.dart b/lib/blocs/accelerator/update_phase_bloc.dart index e120c017..ece973a2 100644 --- a/lib/blocs/accelerator/update_phase_bloc.dart +++ b/lib/blocs/accelerator/update_phase_bloc.dart @@ -1,11 +1,11 @@ import 'package:zenon_syrius_wallet_flutter/blocs/blocs.dart'; import 'package:zenon_syrius_wallet_flutter/main.dart'; -import 'package:zenon_syrius_wallet_flutter/utils/account_block_utils.dart'; +import 'package:zenon_syrius_wallet_flutter/utils/utils.dart'; import 'package:znn_sdk_dart/znn_sdk_dart.dart'; class UpdatePhaseBloc extends BaseBloc { void updatePhase(Hash id, String name, String description, String url, - double znnFundsNeeded, double qsrFundsNeeded) { + BigInt znnFundsNeeded, BigInt qsrFundsNeeded) { try { addEvent(null); AccountBlockTemplate transactionParams = @@ -14,14 +14,8 @@ class UpdatePhaseBloc extends BaseBloc { name, description, url, - AmountUtils.extractDecimals( - znnFundsNeeded, - znnDecimals, - ), - AmountUtils.extractDecimals( - qsrFundsNeeded, - qsrDecimals, - ), + znnFundsNeeded, + qsrFundsNeeded, ); AccountBlockUtils.createAccountBlock(transactionParams, 'update phase') .then( diff --git a/lib/blocs/auto_receive_tx_worker.dart b/lib/blocs/auto_receive_tx_worker.dart index 4e54bbe5..48c109f1 100644 --- a/lib/blocs/auto_receive_tx_worker.dart +++ b/lib/blocs/auto_receive_tx_worker.dart @@ -47,6 +47,7 @@ class AutoReceiveTxWorker extends BaseBloc { ); _sendSuccessNotification(response, toAddress); } on RpcException catch (e, stackTrace) { + _sendErrorNotification(e.toString()); Logger('AutoReceiveTxWorker') .log(Level.WARNING, 'autoReceive', e, stackTrace); if (e.message.compareTo('account-block from-block already received') != @@ -74,7 +75,8 @@ class AutoReceiveTxWorker extends BaseBloc { void _sendSuccessNotification(AccountBlockTemplate block, String toAddress) { addEvent( WalletNotification( - title: 'Transaction received on ${AddressUtils.getLabel(toAddress)}', + title: + 'Transaction received on ${ZenonAddressUtils.getLabel(toAddress)}', timestamp: DateTime.now().millisecondsSinceEpoch, details: 'Transaction hash: ${block.hash}', type: NotificationType.paymentReceived, diff --git a/lib/blocs/dashboard/balance_dashboard_bloc.dart b/lib/blocs/dashboard/balance_dashboard_bloc.dart index 3ff0f364..7207a497 100644 --- a/lib/blocs/dashboard/balance_dashboard_bloc.dart +++ b/lib/blocs/dashboard/balance_dashboard_bloc.dart @@ -11,7 +11,7 @@ class BalanceDashboardBloc extends DashboardBaseBloc { AccountInfo response = await zenon!.ledger .getAccountInfoByAddress(Address.parse(kSelectedAddress!)); if (response.blockCount! > 0 && - (response.znn()! > 0 || response.qsr()! > 0)) { + (response.znn()! > BigInt.zero || response.qsr()! > BigInt.zero)) { return response; } else { throw 'Empty balance on the selected address'; diff --git a/lib/blocs/dashboard/staking_bloc.dart b/lib/blocs/dashboard/staking_bloc.dart index 46cef2b0..da039c6c 100644 --- a/lib/blocs/dashboard/staking_bloc.dart +++ b/lib/blocs/dashboard/staking_bloc.dart @@ -2,13 +2,12 @@ import 'dart:async'; import 'package:zenon_syrius_wallet_flutter/blocs/blocs.dart'; import 'package:zenon_syrius_wallet_flutter/main.dart'; -import 'package:zenon_syrius_wallet_flutter/utils/extensions.dart'; import 'package:zenon_syrius_wallet_flutter/utils/global.dart'; import 'package:znn_sdk_dart/znn_sdk_dart.dart'; class StakingStatsModel { int numActiveStakingEntries; - num totalZnnStakingAmount; + BigInt totalZnnStakingAmount; StakingStatsModel( this.numActiveStakingEntries, @@ -23,7 +22,7 @@ class StakingBloc extends DashboardBaseBloc { if (stakeList.list.isNotEmpty) { return StakingStatsModel( stakeList.list.length, - stakeList.totalAmount.addDecimals(znnDecimals), + stakeList.totalAmount, ); } else { throw 'No active staking entries'; diff --git a/lib/blocs/infinite_scroll_bloc.dart b/lib/blocs/infinite_scroll_bloc.dart index 19c19964..dc55a98d 100644 --- a/lib/blocs/infinite_scroll_bloc.dart +++ b/lib/blocs/infinite_scroll_bloc.dart @@ -5,7 +5,9 @@ import 'package:rxdart/rxdart.dart'; import 'package:zenon_syrius_wallet_flutter/blocs/blocs.dart'; abstract class InfiniteScrollBloc with RefreshBlocMixin { - InfiniteScrollBloc() { + InfiniteScrollBloc({ + this.isDataRequestPaginated = true, + }) { _onPageRequest.stream .flatMap(_fetchList) .listen(_onNewListingStateController.add) @@ -19,6 +21,8 @@ abstract class InfiniteScrollBloc with RefreshBlocMixin { listenToWsRestart(refreshResults); } + final bool isDataRequestPaginated; + List Function(List)? filterItemsFunction; Future> getData(int pageKey, int pageSize); @@ -50,6 +54,7 @@ abstract class InfiniteScrollBloc with RefreshBlocMixin { final _onRefreshResultsRequest = StreamController(); Sink get onPageRequestSink => _onPageRequest.sink; + Sink get onRefreshResultsRequest => _onRefreshResultsRequest.sink; List? get lastListingItems => _onNewListingStateController.value.itemList; @@ -58,9 +63,11 @@ abstract class InfiniteScrollBloc with RefreshBlocMixin { final lastListingState = _onNewListingStateController.value; try { final newItems = await getData(pageKey, _pageSize); - final isLastPage = newItems.length < _pageSize; + final isLastPage = newItems.length < _pageSize || !isDataRequestPaginated; final nextPageKey = isLastPage ? null : pageKey + 1; - List allItems = [...lastListingState.itemList ?? [], ...newItems]; + List allItems = isDataRequestPaginated + ? [...lastListingState.itemList ?? [], ...newItems] + : newItems; if (filterItemsFunction != null) { allItems = filterItemsFunction!(allItems); } diff --git a/lib/blocs/pillars/delegate_button_bloc.dart b/lib/blocs/pillars/delegate_button_bloc.dart index 63145e32..bb65b1d5 100644 --- a/lib/blocs/pillars/delegate_button_bloc.dart +++ b/lib/blocs/pillars/delegate_button_bloc.dart @@ -1,4 +1,3 @@ -import 'package:flutter/material.dart'; import 'package:zenon_syrius_wallet_flutter/blocs/blocs.dart'; import 'package:zenon_syrius_wallet_flutter/main.dart'; import 'package:zenon_syrius_wallet_flutter/utils/account_block_utils.dart'; @@ -6,9 +5,8 @@ import 'package:zenon_syrius_wallet_flutter/utils/constants.dart'; import 'package:znn_sdk_dart/znn_sdk_dart.dart'; class DelegateButtonBloc extends BaseBloc { - Future votePillar( + Future delegateToPillar( String? pillarName, - BuildContext context, ) async { try { addEvent(null); diff --git a/lib/blocs/pillars/disassemble_pillar_bloc.dart b/lib/blocs/pillars/disassemble_pillar_bloc.dart index a41d9640..3e10e171 100644 --- a/lib/blocs/pillars/disassemble_pillar_bloc.dart +++ b/lib/blocs/pillars/disassemble_pillar_bloc.dart @@ -21,7 +21,7 @@ class DisassemblePillarBloc extends BaseBloc { waitForRequiredPlasma: true, ).then( (response) { - AddressUtils.refreshBalance(); + ZenonAddressUtils.refreshBalance(); addEvent(response); }, ).onError( diff --git a/lib/blocs/pillars/pillar_rewards_history_bloc.dart b/lib/blocs/pillars/pillar_rewards_history_bloc.dart index 550725ab..09f303f9 100644 --- a/lib/blocs/pillars/pillar_rewards_history_bloc.dart +++ b/lib/blocs/pillars/pillar_rewards_history_bloc.dart @@ -13,7 +13,7 @@ class PillarRewardsHistoryBloc Address.parse(kSelectedAddress!), pageSize: kStandardChartNumDays.toInt(), ); - if (response.list.any((element) => element.znnAmount > 0)) { + if (response.list.any((element) => element.znnAmount > BigInt.zero)) { return response; } else { throw 'No rewards in the last week'; diff --git a/lib/blocs/pillars/pillars_deploy_bloc.dart b/lib/blocs/pillars/pillars_deploy_bloc.dart index f44777ca..1b7be9a9 100644 --- a/lib/blocs/pillars/pillars_deploy_bloc.dart +++ b/lib/blocs/pillars/pillars_deploy_bloc.dart @@ -36,7 +36,7 @@ class PillarsDeployBloc extends BaseBloc { waitForRequiredPlasma: true, ).then( (response) { - AddressUtils.refreshBalance(); + ZenonAddressUtils.refreshBalance(); addEvent(response); }, ).onError( diff --git a/lib/blocs/pillars/pillars_deposit_qsr_bloc.dart b/lib/blocs/pillars/pillars_deposit_qsr_bloc.dart index 9bfd15e7..e3cbfd35 100644 --- a/lib/blocs/pillars/pillars_deposit_qsr_bloc.dart +++ b/lib/blocs/pillars/pillars_deposit_qsr_bloc.dart @@ -5,13 +5,12 @@ import 'package:zenon_syrius_wallet_flutter/main.dart'; import 'package:zenon_syrius_wallet_flutter/utils/account_block_utils.dart'; import 'package:zenon_syrius_wallet_flutter/utils/address_utils.dart'; import 'package:zenon_syrius_wallet_flutter/utils/constants.dart'; -import 'package:zenon_syrius_wallet_flutter/utils/extensions.dart'; import 'package:zenon_syrius_wallet_flutter/utils/zts_utils.dart'; import 'package:znn_sdk_dart/znn_sdk_dart.dart'; class PillarsDepositQsrBloc extends BaseBloc { Future depositQsr( - String amount, { + BigInt amount, { bool justMarkStepCompleted = false, }) async { try { @@ -19,7 +18,7 @@ class PillarsDepositQsrBloc extends BaseBloc { if (!justMarkStepCompleted) { AccountBlockTemplate transactionParams = zenon!.embedded.pillar.depositQsr( - amount.toNum().extractDecimals(qsrDecimals), + amount, ); AccountBlockUtils.createAccountBlock( transactionParams, @@ -30,7 +29,7 @@ class PillarsDepositQsrBloc extends BaseBloc { await Future.delayed( kDelayAfterAccountBlockCreationCall, ); - AddressUtils.refreshBalance(); + ZenonAddressUtils.refreshBalance(); addEvent(response); }, ).onError( diff --git a/lib/blocs/pillars/pillars_qsr_info_bloc.dart b/lib/blocs/pillars/pillars_qsr_info_bloc.dart index e0751c7e..c9fa4dff 100644 --- a/lib/blocs/pillars/pillars_qsr_info_bloc.dart +++ b/lib/blocs/pillars/pillars_qsr_info_bloc.dart @@ -1,7 +1,6 @@ import 'package:zenon_syrius_wallet_flutter/blocs/blocs.dart'; import 'package:zenon_syrius_wallet_flutter/main.dart'; import 'package:zenon_syrius_wallet_flutter/model/model.dart'; -import 'package:zenon_syrius_wallet_flutter/utils/extensions.dart'; import 'package:zenon_syrius_wallet_flutter/widgets/widgets.dart'; import 'package:znn_sdk_dart/znn_sdk_dart.dart'; @@ -12,14 +11,10 @@ class PillarsQsrInfoBloc extends BaseBloc { ) async { try { addEvent(null); - num deposit = (await zenon!.embedded.pillar.getDepositedQsr( + BigInt deposit = (await zenon!.embedded.pillar.getDepositedQsr( Address.parse(address), - )) - .addDecimals( - qsrDecimals, - ); - num cost = (await zenon!.embedded.pillar.getQsrRegistrationCost()) - .addDecimals(qsrDecimals); + )); + BigInt cost = (await zenon!.embedded.pillar.getQsrRegistrationCost()); addEvent( PillarsQsrInfo( deposit: deposit, diff --git a/lib/blocs/pillars/pillars_withdraw_qsr_bloc.dart b/lib/blocs/pillars/pillars_withdraw_qsr_bloc.dart index d511c1d6..b4168718 100644 --- a/lib/blocs/pillars/pillars_withdraw_qsr_bloc.dart +++ b/lib/blocs/pillars/pillars_withdraw_qsr_bloc.dart @@ -21,7 +21,7 @@ class PillarsWithdrawQsrBloc extends BaseBloc { ).then( (response) async { await Future.delayed(kDelayAfterAccountBlockCreationCall); - AddressUtils.refreshBalance(); + ZenonAddressUtils.refreshBalance(); addEvent(response); }, ).onError( diff --git a/lib/blocs/plasma/cancel_plasma_bloc.dart b/lib/blocs/plasma/cancel_plasma_bloc.dart index eb1f546d..465ba8a9 100644 --- a/lib/blocs/plasma/cancel_plasma_bloc.dart +++ b/lib/blocs/plasma/cancel_plasma_bloc.dart @@ -16,7 +16,7 @@ class CancelPlasmaBloc extends BaseBloc { waitForRequiredPlasma: true) .then( (response) { - AddressUtils.refreshBalance(); + ZenonAddressUtils.refreshBalance(); addEvent(response); }, ).onError( diff --git a/lib/blocs/plasma/plasma_options_bloc.dart b/lib/blocs/plasma/plasma_options_bloc.dart index e6d31034..7ed77b93 100644 --- a/lib/blocs/plasma/plasma_options_bloc.dart +++ b/lib/blocs/plasma/plasma_options_bloc.dart @@ -2,17 +2,16 @@ import 'package:zenon_syrius_wallet_flutter/blocs/blocs.dart'; import 'package:zenon_syrius_wallet_flutter/main.dart'; import 'package:zenon_syrius_wallet_flutter/utils/account_block_utils.dart'; import 'package:zenon_syrius_wallet_flutter/utils/address_utils.dart'; -import 'package:zenon_syrius_wallet_flutter/utils/extensions.dart'; import 'package:zenon_syrius_wallet_flutter/utils/zts_utils.dart'; import 'package:znn_sdk_dart/znn_sdk_dart.dart'; class PlasmaOptionsBloc extends BaseBloc { - void generatePlasma(String beneficiaryAddress, String amount) { + void generatePlasma(String beneficiaryAddress, BigInt amount) { try { addEvent(null); AccountBlockTemplate transactionParams = zenon!.embedded.plasma.fuse( Address.parse(beneficiaryAddress), - amount.toNum().extractDecimals(qsrDecimals), + amount, ); AccountBlockUtils.createAccountBlock( transactionParams, @@ -20,7 +19,7 @@ class PlasmaOptionsBloc extends BaseBloc { waitForRequiredPlasma: true, ).then( (response) { - AddressUtils.refreshBalance(); + ZenonAddressUtils.refreshBalance(); addEvent(response); }, ).onError( diff --git a/lib/blocs/sentinels/disassemble_button_bloc.dart b/lib/blocs/sentinels/disassemble_button_bloc.dart index 29c25321..e3a7b29c 100644 --- a/lib/blocs/sentinels/disassemble_button_bloc.dart +++ b/lib/blocs/sentinels/disassemble_button_bloc.dart @@ -17,7 +17,7 @@ class DisassembleButtonBloc extends BaseBloc { waitForRequiredPlasma: true, ).then( (response) { - AddressUtils.refreshBalance(); + ZenonAddressUtils.refreshBalance(); addEvent(response); }, ).onError( diff --git a/lib/blocs/sentinels/sentinel_deposit_qsr_bloc.dart b/lib/blocs/sentinels/sentinel_deposit_qsr_bloc.dart index 5827162e..79b8e495 100644 --- a/lib/blocs/sentinels/sentinel_deposit_qsr_bloc.dart +++ b/lib/blocs/sentinels/sentinel_deposit_qsr_bloc.dart @@ -5,13 +5,12 @@ import 'package:zenon_syrius_wallet_flutter/main.dart'; import 'package:zenon_syrius_wallet_flutter/utils/account_block_utils.dart'; import 'package:zenon_syrius_wallet_flutter/utils/address_utils.dart'; import 'package:zenon_syrius_wallet_flutter/utils/constants.dart'; -import 'package:zenon_syrius_wallet_flutter/utils/extensions.dart'; import 'package:zenon_syrius_wallet_flutter/utils/zts_utils.dart'; import 'package:znn_sdk_dart/znn_sdk_dart.dart'; class SentinelsDepositQsrBloc extends BaseBloc { Future depositQsr( - String amount, { + BigInt amount, { bool justMarkStepCompleted = false, }) async { try { @@ -19,7 +18,7 @@ class SentinelsDepositQsrBloc extends BaseBloc { if (!justMarkStepCompleted) { AccountBlockTemplate transactionParams = zenon!.embedded.sentinel.depositQsr( - amount.toNum().extractDecimals(qsrDecimals), + amount, ); AccountBlockUtils.createAccountBlock( transactionParams, @@ -30,7 +29,7 @@ class SentinelsDepositQsrBloc extends BaseBloc { await Future.delayed( kDelayAfterAccountBlockCreationCall, ); - AddressUtils.refreshBalance(); + ZenonAddressUtils.refreshBalance(); addEvent(response); }, ).onError( diff --git a/lib/blocs/sentinels/sentinel_qsr_info_bloc.dart b/lib/blocs/sentinels/sentinel_qsr_info_bloc.dart index 8380be1a..62a9d871 100644 --- a/lib/blocs/sentinels/sentinel_qsr_info_bloc.dart +++ b/lib/blocs/sentinels/sentinel_qsr_info_bloc.dart @@ -1,17 +1,13 @@ import 'package:zenon_syrius_wallet_flutter/blocs/blocs.dart'; import 'package:zenon_syrius_wallet_flutter/main.dart'; -import 'package:zenon_syrius_wallet_flutter/utils/extensions.dart'; import 'package:znn_sdk_dart/znn_sdk_dart.dart'; -class SentinelsQsrInfoBloc extends BaseBloc { +class SentinelsQsrInfoBloc extends BaseBloc { Future getQsrDepositedAmount(String address) async { try { addEvent(null); - num response = (await zenon!.embedded.sentinel.getDepositedQsr( + BigInt response = await zenon!.embedded.sentinel.getDepositedQsr( Address.parse(address), - )) - .addDecimals( - qsrDecimals, ); addEvent(response); } catch (e, stackTrace) { diff --git a/lib/blocs/sentinels/sentinel_register_bloc.dart b/lib/blocs/sentinels/sentinel_register_bloc.dart index 1f2fc0f6..8c73624d 100644 --- a/lib/blocs/sentinels/sentinel_register_bloc.dart +++ b/lib/blocs/sentinels/sentinel_register_bloc.dart @@ -5,7 +5,7 @@ import 'package:zenon_syrius_wallet_flutter/utils/address_utils.dart'; import 'package:znn_sdk_dart/znn_sdk_dart.dart'; class SentinelsDeployBloc extends BaseBloc { - Future deploySentinel(String amount) async { + Future deploySentinel(BigInt amount) async { try { addEvent(null); AccountBlockTemplate transactionParams = @@ -16,7 +16,7 @@ class SentinelsDeployBloc extends BaseBloc { waitForRequiredPlasma: true, ).then( (response) { - AddressUtils.refreshBalance(); + ZenonAddressUtils.refreshBalance(); addEvent(response); }, ).onError( diff --git a/lib/blocs/sentinels/sentinel_rewards_history_bloc.dart b/lib/blocs/sentinels/sentinel_rewards_history_bloc.dart index 6ca0f6f0..5a3a25bf 100644 --- a/lib/blocs/sentinels/sentinel_rewards_history_bloc.dart +++ b/lib/blocs/sentinels/sentinel_rewards_history_bloc.dart @@ -14,7 +14,8 @@ class SentinelRewardsHistoryBloc pageSize: kStandardChartNumDays.toInt(), ); if (response.list.any( - (element) => element.qsrAmount > 0 || element.znnAmount > 0, + (element) => + element.qsrAmount > BigInt.zero || element.znnAmount > BigInt.zero, )) { return response; } else { diff --git a/lib/blocs/sentinels/sentinel_withdraw_qsr_bloc.dart b/lib/blocs/sentinels/sentinel_withdraw_qsr_bloc.dart index 1cde3f50..bc32ee4b 100644 --- a/lib/blocs/sentinels/sentinel_withdraw_qsr_bloc.dart +++ b/lib/blocs/sentinels/sentinel_withdraw_qsr_bloc.dart @@ -21,7 +21,7 @@ class SentinelsWithdrawQsrBloc extends BaseBloc { ).then( (response) async { await Future.delayed(kDelayAfterAccountBlockCreationCall); - AddressUtils.refreshBalance(); + ZenonAddressUtils.refreshBalance(); addEvent(response); }, ).onError( diff --git a/lib/blocs/staking/cancel_stake_bloc.dart b/lib/blocs/staking/cancel_stake_bloc.dart index 58e6700f..f0838fa8 100644 --- a/lib/blocs/staking/cancel_stake_bloc.dart +++ b/lib/blocs/staking/cancel_stake_bloc.dart @@ -18,7 +18,7 @@ class CancelStakeBloc extends BaseBloc { waitForRequiredPlasma: true, ).then( (response) { - AddressUtils.refreshBalance(); + ZenonAddressUtils.refreshBalance(); addEvent(response); }, ).onError( diff --git a/lib/blocs/staking/staking_options_bloc.dart b/lib/blocs/staking/staking_options_bloc.dart index 130d9e5d..50e8a417 100644 --- a/lib/blocs/staking/staking_options_bloc.dart +++ b/lib/blocs/staking/staking_options_bloc.dart @@ -2,25 +2,24 @@ import 'package:zenon_syrius_wallet_flutter/blocs/blocs.dart'; import 'package:zenon_syrius_wallet_flutter/main.dart'; import 'package:zenon_syrius_wallet_flutter/utils/account_block_utils.dart'; import 'package:zenon_syrius_wallet_flutter/utils/address_utils.dart'; -import 'package:zenon_syrius_wallet_flutter/utils/extensions.dart'; import 'package:znn_sdk_dart/znn_sdk_dart.dart'; class StakingOptionsBloc extends BaseBloc { void stakeForQsr( Duration stakeDuration, - String amount, + BigInt amount, ) { try { addEvent(null); AccountBlockTemplate transactionParams = zenon!.embedded.stake.stake( stakeDuration.inSeconds, - amount.toNum().extractDecimals(znnDecimals), + amount, ); AccountBlockUtils.createAccountBlock(transactionParams, 'create stake', waitForRequiredPlasma: true) .then( (response) { - AddressUtils.refreshBalance(); + ZenonAddressUtils.refreshBalance(); addEvent(response); }, ).onError( diff --git a/lib/blocs/staking/staking_rewards_history_bloc.dart b/lib/blocs/staking/staking_rewards_history_bloc.dart index 860875d7..34a3f14e 100644 --- a/lib/blocs/staking/staking_rewards_history_bloc.dart +++ b/lib/blocs/staking/staking_rewards_history_bloc.dart @@ -13,7 +13,7 @@ class StakingRewardsHistoryBloc Address.parse(kSelectedAddress!), pageSize: kStandardChartNumDays.toInt(), ); - if (response.list.any((element) => element.qsrAmount > 0)) { + if (response.list.any((element) => element.qsrAmount > BigInt.zero)) { return response; } else { throw 'No rewards in the last week'; diff --git a/lib/blocs/tokens/burn_token_bloc.dart b/lib/blocs/tokens/burn_token_bloc.dart index 98e22ab6..e18495bd 100644 --- a/lib/blocs/tokens/burn_token_bloc.dart +++ b/lib/blocs/tokens/burn_token_bloc.dart @@ -2,24 +2,23 @@ import 'package:zenon_syrius_wallet_flutter/blocs/blocs.dart'; import 'package:zenon_syrius_wallet_flutter/main.dart'; import 'package:zenon_syrius_wallet_flutter/utils/account_block_utils.dart'; import 'package:zenon_syrius_wallet_flutter/utils/address_utils.dart'; -import 'package:zenon_syrius_wallet_flutter/utils/extensions.dart'; import 'package:znn_sdk_dart/znn_sdk_dart.dart'; class BurnTokenBloc extends BaseBloc { void burnToken( Token token, - String amount, + BigInt amount, ) { try { AccountBlockTemplate transactionParams = zenon!.embedded.token.burnToken( token.tokenStandard, - amount.toNum().extractDecimals(token.decimals), + amount, ); AccountBlockUtils.createAccountBlock(transactionParams, 'burn token', waitForRequiredPlasma: true) .then( (response) { - AddressUtils.refreshBalance(); + ZenonAddressUtils.refreshBalance(); addEvent(response); }, ).onError( diff --git a/lib/blocs/tokens/issue_token_bloc.dart b/lib/blocs/tokens/issue_token_bloc.dart index 3ff959fa..8075106c 100644 --- a/lib/blocs/tokens/issue_token_bloc.dart +++ b/lib/blocs/tokens/issue_token_bloc.dart @@ -5,7 +5,6 @@ import 'package:zenon_syrius_wallet_flutter/model/model.dart'; import 'package:zenon_syrius_wallet_flutter/utils/account_block_utils.dart'; import 'package:zenon_syrius_wallet_flutter/utils/address_utils.dart'; import 'package:zenon_syrius_wallet_flutter/utils/constants.dart'; -import 'package:zenon_syrius_wallet_flutter/utils/extensions.dart'; import 'package:znn_sdk_dart/znn_sdk_dart.dart'; class IssueTokenBloc extends BaseBloc { @@ -15,9 +14,8 @@ class IssueTokenBloc extends BaseBloc { tokenStepperData.tokenName, tokenStepperData.tokenSymbol, tokenStepperData.tokenDomain, - tokenStepperData.totalSupply - .extractDecimals(tokenStepperData.decimals), - tokenStepperData.maxSupply.extractDecimals(tokenStepperData.decimals), + tokenStepperData.totalSupply, + tokenStepperData.maxSupply, tokenStepperData.decimals, tokenStepperData.isMintable!, tokenStepperData.isOwnerBurnOnly!, @@ -29,7 +27,7 @@ class IssueTokenBloc extends BaseBloc { ).then( (response) { Hive.box(kFavoriteTokensBox).add(response.tokenStandard.toString()); - AddressUtils.refreshBalance(); + ZenonAddressUtils.refreshBalance(); addEvent(response); }, ).onError( diff --git a/lib/blocs/tokens/mint_token_bloc.dart b/lib/blocs/tokens/mint_token_bloc.dart index 15d3d5a7..fb06d9c0 100644 --- a/lib/blocs/tokens/mint_token_bloc.dart +++ b/lib/blocs/tokens/mint_token_bloc.dart @@ -2,27 +2,26 @@ import 'package:zenon_syrius_wallet_flutter/blocs/blocs.dart'; import 'package:zenon_syrius_wallet_flutter/main.dart'; import 'package:zenon_syrius_wallet_flutter/utils/account_block_utils.dart'; import 'package:zenon_syrius_wallet_flutter/utils/address_utils.dart'; -import 'package:zenon_syrius_wallet_flutter/utils/extensions.dart'; import 'package:znn_sdk_dart/znn_sdk_dart.dart'; class MintTokenBloc extends BaseBloc { void mintToken( Token token, - String amount, + BigInt amount, Address beneficiaryAddress, ) { try { AccountBlockTemplate transactionParams = zenon!.embedded.token.mintToken( token.tokenStandard, - amount.toNum().extractDecimals(token.decimals), + amount, beneficiaryAddress, ); AccountBlockUtils.createAccountBlock(transactionParams, 'mint token', waitForRequiredPlasma: true) .then( (response) { - response.amount = amount.toNum().extractDecimals(token.decimals); - AddressUtils.refreshBalance(); + response.amount = amount; + ZenonAddressUtils.refreshBalance(); addEvent(response); }, ).onError( diff --git a/lib/blocs/transfer/send_payment_bloc.dart b/lib/blocs/transfer/send_payment_bloc.dart index f8f1914f..c656eadb 100644 --- a/lib/blocs/transfer/send_payment_bloc.dart +++ b/lib/blocs/transfer/send_payment_bloc.dart @@ -1,37 +1,47 @@ import 'package:zenon_syrius_wallet_flutter/blocs/blocs.dart'; import 'package:zenon_syrius_wallet_flutter/utils/account_block_utils.dart'; import 'package:zenon_syrius_wallet_flutter/utils/address_utils.dart'; -import 'package:zenon_syrius_wallet_flutter/utils/extensions.dart'; import 'package:zenon_syrius_wallet_flutter/utils/global.dart'; import 'package:znn_sdk_dart/znn_sdk_dart.dart'; class SendPaymentBloc extends BaseBloc { void sendTransfer({ - required String? fromAddress, - required String toAddress, - required String amount, - required List? data, - required Token token, + // TODO: make this argument non-nullable + String? fromAddress, + String? toAddress, + BigInt? amount, + List? data, + Token? token, + AccountBlockTemplate? block, }) { + assert( + block == null && + fromAddress != null && + toAddress != null && + amount != null && + token != null || + block != null && fromAddress != null, + ); try { addEvent(null); - AccountBlockTemplate transactionParams = AccountBlockTemplate.send( - Address.parse(toAddress), - token.tokenStandard, - amount.toNum().extractDecimals(token.decimals), - data, - ); + AccountBlockTemplate accountBlock = block ?? + AccountBlockTemplate.send( + Address.parse(toAddress!), + token!.tokenStandard, + amount!, + data, + ); KeyPair blockSigningKeyPair = kKeyStore!.getKeyPair( kDefaultAddressList.indexOf(fromAddress), ); AccountBlockUtils.createAccountBlock( - transactionParams, + accountBlock, 'send transaction', blockSigningKey: blockSigningKeyPair, waitForRequiredPlasma: true, ).then( (response) { - AddressUtils.refreshBalance(); + ZenonAddressUtils.refreshBalance(); addEvent(response); }, ).onError( diff --git a/lib/blocs/wallet_connect/wallet_connect_pairings_bloc.dart b/lib/blocs/wallet_connect/wallet_connect_pairings_bloc.dart new file mode 100644 index 00000000..e9cf29f3 --- /dev/null +++ b/lib/blocs/wallet_connect/wallet_connect_pairings_bloc.dart @@ -0,0 +1,14 @@ +import 'package:walletconnect_flutter_v2/walletconnect_flutter_v2.dart'; +import 'package:zenon_syrius_wallet_flutter/blocs/infinite_scroll_bloc.dart'; +import 'package:zenon_syrius_wallet_flutter/main.dart'; +import 'package:zenon_syrius_wallet_flutter/services/wallet_connect_service.dart'; + +class WalletConnectPairingsBloc extends InfiniteScrollBloc { + WalletConnectPairingsBloc() : super(isDataRequestPaginated: false); + + @override + Future> getData(int pageKey, int pageSize) => + Future.delayed(const Duration(milliseconds: 500)).then( + (value) => sl.get().pairings, + ); +} diff --git a/lib/blocs/wallet_connect/wallet_connect_sessions_bloc.dart b/lib/blocs/wallet_connect/wallet_connect_sessions_bloc.dart new file mode 100644 index 00000000..12fbb1f2 --- /dev/null +++ b/lib/blocs/wallet_connect/wallet_connect_sessions_bloc.dart @@ -0,0 +1,22 @@ +import 'package:walletconnect_flutter_v2/walletconnect_flutter_v2.dart'; +import 'package:zenon_syrius_wallet_flutter/blocs/infinite_scroll_bloc.dart'; +import 'package:zenon_syrius_wallet_flutter/main.dart'; +import 'package:zenon_syrius_wallet_flutter/services/wallet_connect_service.dart'; + +class WalletConnectSessionsBloc extends InfiniteScrollBloc { + WalletConnectSessionsBloc() : super(isDataRequestPaginated: false); + + @override + Future> getData(int pageKey, int pageSize) async { + final wcService = sl.get(); + final sessions = []; + for (var pairing in wcService.pairings) { + sessions.addAll( + wcService.getSessionsForPairing(pairing.topic).values, + ); + } + return Future.delayed(const Duration(milliseconds: 500)).then( + (value) => sessions, + ); + } +} diff --git a/lib/embedded_node/blobs/libznn.dll b/lib/embedded_node/blobs/libznn.dll index 2ecd8e6c..d70ba78b 100755 Binary files a/lib/embedded_node/blobs/libznn.dll and b/lib/embedded_node/blobs/libznn.dll differ diff --git a/lib/embedded_node/blobs/libznn.dylib b/lib/embedded_node/blobs/libznn.dylib index 980a1117..6886747d 100755 Binary files a/lib/embedded_node/blobs/libznn.dylib and b/lib/embedded_node/blobs/libznn.dylib differ diff --git a/lib/embedded_node/blobs/libznn.so b/lib/embedded_node/blobs/libznn.so index 48581586..9d8bb6bb 100755 Binary files a/lib/embedded_node/blobs/libznn.so and b/lib/embedded_node/blobs/libznn.so differ diff --git a/lib/embedded_node/embedded_node.dart b/lib/embedded_node/embedded_node.dart index 5e20532c..2cb5d5d9 100644 --- a/lib/embedded_node/embedded_node.dart +++ b/lib/embedded_node/embedded_node.dart @@ -5,6 +5,7 @@ import 'dart:isolate'; import 'dart:ui'; import 'package:ffi/ffi.dart'; +import 'package:logging/logging.dart'; import 'package:path/path.dart' as path; import 'package:znn_sdk_dart/znn_sdk_dart.dart'; @@ -63,9 +64,11 @@ class EmbeddedNode { } } - logger.info('Loading libznn from path $libraryPath'); + Logger('EmbeddedNode') + .log(Level.INFO, 'Loading libznn from path $libraryPath'); if (!found) { + Logger('EmbeddedNode').log(Level.SEVERE, 'Could not load libznn'); throw invalidZnnLibPathException; } diff --git a/lib/main.dart b/lib/main.dart index 2efb0a70..d593018a 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -11,13 +11,17 @@ import 'package:layout/layout.dart'; import 'package:local_notifier/local_notifier.dart'; import 'package:logging/logging.dart'; import 'package:overlay_support/overlay_support.dart'; +import 'package:path/path.dart' as path; import 'package:provider/provider.dart'; import 'package:tray_manager/tray_manager.dart'; import 'package:window_manager/window_manager.dart'; import 'package:zenon_syrius_wallet_flutter/blocs/blocs.dart'; +import 'package:zenon_syrius_wallet_flutter/blocs/wallet_connect/wallet_connect_pairings_bloc.dart'; +import 'package:zenon_syrius_wallet_flutter/blocs/wallet_connect/wallet_connect_sessions_bloc.dart'; import 'package:zenon_syrius_wallet_flutter/model/model.dart'; import 'package:zenon_syrius_wallet_flutter/screens/screens.dart'; import 'package:zenon_syrius_wallet_flutter/services/shared_prefs_service.dart'; +import 'package:zenon_syrius_wallet_flutter/services/wallet_connect_service.dart'; import 'package:zenon_syrius_wallet_flutter/utils/utils.dart'; import 'package:zenon_syrius_wallet_flutter/widgets/widgets.dart'; import 'package:znn_sdk_dart/znn_sdk_dart.dart'; @@ -33,19 +37,32 @@ main() async { Provider.debugCheckInvalidValueType = null; debugDefaultTargetPlatformOverride = TargetPlatform.fuchsia; + ensureDirectoriesExist(); + Hive.init(znnDefaultPaths.cache.path.toString()); + // Setup logger + Directory syriusLogDir = + Directory(path.join(znnDefaultCacheDirectory.path, 'log')); + if (!syriusLogDir.existsSync()) { + syriusLogDir.createSync(recursive: true); + } + final logFile = File( + '${syriusLogDir.path}${path.separator}syrius-${DateTime.now().millisecondsSinceEpoch}.log'); Logger.root.level = Level.ALL; - Logger.root.onRecord.listen((record) { + Logger.root.onRecord.listen((LogRecord record) { if (kDebugMode) { print( '${record.level.name} ${record.loggerName} ${record.message} ${record.time}: ' '${record.error} ${record.stackTrace}\n'); } + logFile.writeAsString( + '${record.level.name} ${record.loggerName} ${record.message} ${record.time}: ' + '${record.error} ${record.stackTrace}\n', + mode: FileMode.append, + flush: true, + ); }); - ensureDirectoriesExist(); - Hive.init(znnDefaultPaths.cache.path.toString()); - windowManager.ensureInitialized(); await windowManager.setPreventClose(true); @@ -111,9 +128,6 @@ main() async { } Future _setupTrayManager() async { - if (!Platform.isWindows) { - await trayManager.setTitle('s y r i u s'); - } await trayManager.setIcon( Platform.isWindows ? 'assets/images/tray_app_icon.ico' @@ -145,7 +159,7 @@ void setup() { zenon = sl(); sl.registerLazySingletonAsync( (() => SharedPrefsService.getInstance().then((value) => value!))); - + sl.registerLazySingleton(() => WalletConnectService()); sl.registerSingleton(AutoReceiveTxWorker.getInstance()); sl.registerSingleton(ReceivePort(), @@ -161,6 +175,12 @@ void setup() { sl.registerSingleton(NotificationsBloc()); sl.registerSingleton(AcceleratorBalanceBloc()); sl.registerSingleton(PowGeneratingStatusBloc()); + sl.registerSingleton( + WalletConnectPairingsBloc(), + ); + sl.registerSingleton( + WalletConnectSessionsBloc(), + ); } class MyApp extends StatefulWidget { diff --git a/lib/model/new_token_data.dart b/lib/model/new_token_data.dart index ca65820a..954cc1fc 100644 --- a/lib/model/new_token_data.dart +++ b/lib/model/new_token_data.dart @@ -3,9 +3,9 @@ class NewTokenData { late String tokenName; late String tokenSymbol; late String tokenDomain; - late num totalSupply; + late BigInt totalSupply; late int decimals; - late num maxSupply; + late BigInt maxSupply; bool? isMintable; bool? isOwnerBurnOnly; bool? isUtility; diff --git a/lib/model/pillars_qsr_info.dart b/lib/model/pillars_qsr_info.dart index fcad602c..4da14a40 100644 --- a/lib/model/pillars_qsr_info.dart +++ b/lib/model/pillars_qsr_info.dart @@ -1,6 +1,6 @@ class PillarsQsrInfo { - final num cost; - final num deposit; + final BigInt cost; + final BigInt deposit; PillarsQsrInfo({ required this.cost, diff --git a/lib/screens/node_management_screen.dart b/lib/screens/node_management_screen.dart index d9f04321..507f53ab 100644 --- a/lib/screens/node_management_screen.dart +++ b/lib/screens/node_management_screen.dart @@ -1,9 +1,8 @@ -import 'dart:io'; import 'dart:isolate'; import 'package:flutter/material.dart'; import 'package:hive/hive.dart'; -import 'package:wakelock/wakelock.dart'; +import 'package:wakelock_plus/wakelock_plus.dart'; import 'package:zenon_syrius_wallet_flutter/blocs/blocs.dart'; import 'package:zenon_syrius_wallet_flutter/embedded_node/embedded_node.dart'; import 'package:zenon_syrius_wallet_flutter/main.dart'; @@ -129,8 +128,8 @@ class _NodeManagementScreenState extends State { Future _onConfirmNodeButtonPressed() async { // Acquire WakeLock - if (!Platform.isLinux && !await Wakelock.enabled) { - Wakelock.enable(); + if (!await WakelockPlus.enabled) { + WakelockPlus.enable(); } try { @@ -145,8 +144,9 @@ class _NodeManagementScreenState extends State { if (!isConnectionEstablished) { // Initialize local full node await Isolate.spawn(EmbeddedNode.runNode, [''], - onExit: sl(instanceName: 'embeddedStoppedPort') - .sendPort); + onExit: + sl(instanceName: 'embeddedStoppedPort').sendPort, + debugName: 'EmbeddedNodeIsolate'); kEmbeddedNodeRunning = true; // The node needs a couple of seconds to actually start await Future.delayed(kEmbeddedConnectionDelay); diff --git a/lib/services/wallet_connect_service.dart b/lib/services/wallet_connect_service.dart new file mode 100644 index 00000000..681bb0b6 --- /dev/null +++ b/lib/services/wallet_connect_service.dart @@ -0,0 +1,644 @@ +import 'package:flutter/material.dart'; +import 'package:logging/logging.dart'; +import 'package:walletconnect_flutter_v2/walletconnect_flutter_v2.dart'; +import 'package:window_manager/window_manager.dart'; +import 'package:zenon_syrius_wallet_flutter/blocs/blocs.dart'; +import 'package:zenon_syrius_wallet_flutter/blocs/wallet_connect/wallet_connect_pairings_bloc.dart'; +import 'package:zenon_syrius_wallet_flutter/blocs/wallet_connect/wallet_connect_sessions_bloc.dart'; +import 'package:zenon_syrius_wallet_flutter/main.dart'; +import 'package:zenon_syrius_wallet_flutter/model/model.dart'; +import 'package:zenon_syrius_wallet_flutter/utils/functions.dart'; +import 'package:zenon_syrius_wallet_flutter/utils/utils.dart'; +import 'package:zenon_syrius_wallet_flutter/widgets/main_app_container.dart'; +import 'package:zenon_syrius_wallet_flutter/widgets/reusable_widgets/dialogs.dart'; +import 'package:zenon_syrius_wallet_flutter/widgets/reusable_widgets/icons/link_icon.dart'; +import 'package:znn_sdk_dart/znn_sdk_dart.dart'; + +class WalletConnectService { + WalletConnectService._internal(); + + factory WalletConnectService() => _instance; + + static final WalletConnectService _instance = + WalletConnectService._internal(); + + final List _idSessionsApproved = []; + + late Web3Wallet _wcClient; + late BuildContext _context; + + final List dAppsActiveSessions = []; + + set context(BuildContext context) => _context = context; + + final _walletLockedError = const WalletConnectError( + code: 9000, + message: 'Wallet is locked', + ); + + Future initClient() async { + if (kWcProjectId.isNotEmpty) { + _wcClient = await Web3Wallet.createInstance( + projectId: kWcProjectId, + metadata: const PairingMetadata( + name: 's y r i u s', + description: 'A wallet for interacting with Zenon Network', + url: 'https://zenon.network', + icons: [ + 'https://raw.githubusercontent.com/zenon-network/syrius/master/macos/Runner/Assets.xcassets/AppIcon.appiconset/Icon-MacOS-512x512%402x.png' + ], + ), + ).onError((e, stackTrace) { + Logger('WalletConnectService') + .log(Level.SEVERE, 'initClient onError ', e, stackTrace); + if (e != null) { + NotificationUtils.sendNotificationError( + e, 'WalletConnect initialization failed'); + } + throw 'WalletConnect init failed'; + }); + + for (var pairingInfo in pairings) { + dAppsActiveSessions + .addAll(getSessionsForPairing(pairingInfo.topic).values); + + Logger('WalletConnectService') + .log(Level.INFO, 'active pairings: $pairingInfo'); + } + Logger('WalletConnectService') + .log(Level.INFO, 'pairings num: ${pairings.length}'); + Logger('WalletConnectService') + .log(Level.INFO, 'active sessions: ${getActiveSessions()}'); + _initListeners(); + _initialChecks(); + } else { + Logger('WalletConnectService').log(Level.INFO, 'kWcProjectId missing'); + return; + } + return; + } + + Future pair(Uri uri) => _wcClient.pair(uri: uri); + + void _initListeners() { + _wcClient.onSessionProposal.subscribe(onSessionProposal); + + _wcClient.core.relayClient.onRelayClientDisconnect.subscribe((args) { + Logger('WalletConnectService').log( + Level.INFO, 'onRelayClientDisconnect triggered', args.toString()); + _wcClient.core.relayClient.connect(); + }); + + _wcClient.core.pairing.onPairingCreate.subscribe((args) { + Logger('WalletConnectService') + .log(Level.INFO, 'onPairingCreate triggered', args.toString()); + sl.get().refreshResults(); + }); + + _wcClient.core.pairing.onPairingActivate.subscribe((args) { + Logger('WalletConnectService') + .log(Level.INFO, 'onPairingActivate triggered', args.toString()); + sl.get().refreshResults(); + }); + + _wcClient.core.pairing.onPairingInvalid.subscribe((args) { + Logger('WalletConnectService') + .log(Level.INFO, 'onPairingInvalid triggered', args.toString()); + }); + + _wcClient.core.pairing.onPairingPing.subscribe((args) { + Logger('WalletConnectService') + .log(Level.INFO, 'onPairingPing triggered', args.toString()); + }); + + _wcClient.core.pairing.onPairingDelete.subscribe((args) { + Logger('WalletConnectService') + .log(Level.INFO, 'onPairingDelete triggered', args.toString()); + }); + + _wcClient.core.pairing.onPairingExpire.subscribe((args) { + Logger('WalletConnectService') + .log(Level.INFO, 'onPairingExpire triggered', args.toString()); + }); + + _wcClient.onSessionPing.subscribe((args) { + Logger('WalletConnectService') + .log(Level.INFO, 'onSessionPing triggered', args.toString()); + }); + + _wcClient.onSessionDelete.subscribe((args) { + Logger('WalletConnectService') + .log(Level.INFO, 'onSessionDelete triggered', args.toString()); + sl.get().refreshResults(); + }); + + _wcClient.onSessionProposalError.subscribe((args) { + Logger('WalletConnectService') + .log(Level.INFO, 'onSessionProposalError triggered', args.toString()); + sl.get().refreshResults(); + }); + + _wcClient.onSessionConnect.subscribe((args) { + Logger('WalletConnectService') + .log(Level.INFO, 'onSessionConnect triggered', args.toString()); + Future.delayed(const Duration(seconds: 3)).then( + (value) => sl.get().refreshResults()); + }); + + _wcClient.onSessionRequest.subscribe((SessionRequestEvent? request) async { + Logger('WalletConnectService') + .log(Level.INFO, 'onSessionRequest triggered', request.toString()); + }); + + _wcClient.onAuthRequest.subscribe((args) { + Logger('WalletConnectService') + .log(Level.INFO, 'onAuthRequest triggered', args.toString()); + }); + + _wcClient.registerRequestHandler( + chainId: 'zenon:1', + method: 'znn_info', + handler: (topic, params) async { + if (!await windowManager.isFocused() || + !await windowManager.isVisible()) { + windowManager.show(); + } + final dAppMetadata = dAppsActiveSessions + .firstWhere((element) => element.topic == topic) + .peer + .metadata; + + if (kCurrentPage != Tabs.lock) { + if (_context.mounted) { + final actionWasAccepted = await showDialogWithNoAndYesOptions( + context: _context, + isBarrierDismissible: false, + title: '${dAppMetadata.name} - Information', + content: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Text('Are you sure you want to allow ${dAppMetadata.name} to ' + 'retrieve the current address, node URL and chain identifier information?'), + kVerticalSpacing, + Image( + image: NetworkImage(dAppMetadata.icons.first), + height: 100.0, + fit: BoxFit.fitHeight, + ), + kVerticalSpacing, + Text(dAppMetadata.description), + kVerticalSpacing, + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text(dAppMetadata.url), + LinkIcon( + url: dAppMetadata.url, + ) + ], + ), + ], + ), + onYesButtonPressed: () async {}, + onNoButtonPressed: () {}, + ); + + if (actionWasAccepted) { + return { + 'address': kSelectedAddress, + 'nodeUrl': kCurrentNode, + 'chainId': getChainIdentifier(), + }; + } else { + NotificationUtils.sendNotificationError( + Errors.getSdkError(Errors.USER_REJECTED), + 'You have rejected the WalletConnect request'); + throw Errors.getSdkError(Errors.USER_REJECTED); + } + } else { + throw _walletLockedError; + } + } else { + throw _walletLockedError; + } + }, + ); + + _wcClient.onAuthRequest.subscribe((AuthRequest? args) async { + Logger('WalletConnectService') + .log(Level.INFO, 'onAuthRequest triggered', args.toString()); + }); + + _wcClient.registerRequestHandler( + chainId: 'zenon:1', + method: 'znn_sign', + handler: (topic, params) async { + if (!await windowManager.isFocused() || + !await windowManager.isVisible()) { + windowManager.show(); + } + final dAppMetadata = dAppsActiveSessions + .firstWhere((element) => element.topic == topic) + .peer + .metadata; + if (kCurrentPage != Tabs.lock) { + final message = params as String; + + if (_context.mounted) { + final actionWasAccepted = await showDialogWithNoAndYesOptions( + context: _context, + isBarrierDismissible: false, + title: '${dAppMetadata.name} - Sign Message', + content: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Text('Are you sure you want to ' + 'sign message $message ?'), + kVerticalSpacing, + Image( + image: NetworkImage(dAppMetadata.icons.first), + height: 100.0, + fit: BoxFit.fitHeight, + ), + kVerticalSpacing, + Text(dAppMetadata.description), + kVerticalSpacing, + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text(dAppMetadata.url), + LinkIcon( + url: dAppMetadata.url, + ) + ], + ), + ], + ), + onYesButtonPressed: () async {}, + onNoButtonPressed: () {}, + ); + + if (actionWasAccepted) { + return await walletSign(message.codeUnits); + } else { + NotificationUtils.sendNotificationError( + Errors.getSdkError(Errors.USER_REJECTED), + 'You have rejected the WalletConnect request'); + throw Errors.getSdkError(Errors.USER_REJECTED); + } + } else { + throw _walletLockedError; + } + } else { + throw _walletLockedError; + } + }, + ); + + _wcClient.registerRequestHandler( + chainId: 'zenon:1', + method: 'znn_send', + handler: (topic, params) async { + if (!await windowManager.isFocused() || + !await windowManager.isVisible()) { + windowManager.show(); + } + final dAppMetadata = dAppsActiveSessions + .firstWhere((element) => element.topic == topic) + .peer + .metadata; + if (kCurrentPage != Tabs.lock) { + final accountBlock = + AccountBlockTemplate.fromJson(params['accountBlock']); + + final toAddress = ZenonAddressUtils.getLabel( + accountBlock.toAddress.toString(), + ); + + final token = + await zenon!.embedded.token.getByZts(accountBlock.tokenStandard); + + final amount = accountBlock.amount.addDecimals(token!.decimals); + + final sendPaymentBloc = SendPaymentBloc(); + + if (_context.mounted) { + final wasActionAccepted = await showDialogWithNoAndYesOptions( + context: _context, + isBarrierDismissible: false, + title: '${dAppMetadata.name} - Send Payment', + content: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Text('Are you sure you want to transfer ' + '$amount ${token.symbol} to ' + '$toAddress ?'), + kVerticalSpacing, + Image( + image: NetworkImage(dAppMetadata.icons.first), + height: 100.0, + fit: BoxFit.fitHeight, + ), + kVerticalSpacing, + Text(dAppMetadata.description), + kVerticalSpacing, + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text(dAppMetadata.url), + LinkIcon( + url: dAppMetadata.url, + ) + ], + ), + ], + ), + description: 'Are you sure you want to transfer ' + '$amount ${token.symbol} to ' + '$toAddress ?', + onYesButtonPressed: () {}, + onNoButtonPressed: () {}, + ); + + if (wasActionAccepted) { + sendPaymentBloc.sendTransfer( + fromAddress: params['fromAddress'], + block: AccountBlockTemplate.fromJson(params['accountBlock']), + ); + + final result = await sendPaymentBloc.stream.firstWhere( + (element) => element != null, + ); + + return result!; + } else { + NotificationUtils.sendNotificationError( + Errors.getSdkError(Errors.USER_REJECTED), + 'You have rejected the WalletConnect request'); + throw Errors.getSdkError(Errors.USER_REJECTED); + } + } else { + throw _walletLockedError; + } + } else { + throw _walletLockedError; + } + }, + ); + } + + List get pairings => _wcClient.pairings.getAll(); + + Future approveSession( + {required int id, Map? namespaces}) async { + if (!await windowManager.isFocused() || !await windowManager.isVisible()) { + windowManager.show(); + } + namespaces = namespaces ?? + { + 'zenon': Namespace( + accounts: _getWalletAccounts(), + methods: [ + 'znn_sign', + 'znn_info', + 'znn_send', + ], + events: ['chainIdChange', 'addressChange'], + ), + }; + return _wcClient.approveSession( + id: id, + namespaces: namespaces, + ); + } + + Future rejectSession({ + required int id, + required WalletConnectError reason, + }) => + _wcClient.rejectSession(id: id, reason: reason); + + String _generateAccount(String address, int chainId) => + '$kZenonNameSpace:1:$address'; + + List _getWalletAccounts() => kAddressLabelMap.values + .map( + (address) => _generateAccount(address, getChainIdentifier()), + ) + .toList(); + + Future activatePairing({ + required String topic, + }) => + _wcClient.core.pairing.activate( + topic: topic, + ); + + Future deactivatePairing({ + required String topic, + }) async { + try { + _wcClient.core.pairing.disconnect(topic: topic); + } on WalletConnectError catch (e) { + // technically look for WalletConnectError 6 : Expired. to consider it a warning + Logger('WalletConnectService') + .log(Level.INFO, 'deactivatePairing ${e.code} : ${e.message}'); + } catch (e, s) { + // Catch anything else (not just Exceptions) and log stack + Logger('WalletConnectService').log(Level.INFO, + 'disconnectAllParings - Unexpected error: $e, topic $topic\n$s'); + } + } + + // Future disconnectSessions() async { + // IPairingStore pairingStore = getPairings(); + // pairingStore.getAll().forEach((element) async { + // await _wcClient.disconnectSession( + // topic: element.topic, + // reason: Errors.getSdkError(Errors.USER_DISCONNECTED)); + // }); + // } + + Future disconnectSession({required String topic}) async => + _wcClient.disconnectSession( + topic: topic, + reason: Errors.getSdkError(Errors.USER_DISCONNECTED), + ); + + Future _emitEventForTheDApps({ + required String changeName, + required String newValue, + }) async { + final sessionTopics = + pairings.fold>([], (previousValue, pairing) { + if (pairing.active) { + var sessionsPerPairing = getSessionsForPairing(pairing.topic).keys; + previousValue.addAll(sessionsPerPairing); + return previousValue; + } + return previousValue; + }); + + for (var sessionTopic in sessionTopics) { + _emitEventForADApp( + sessionTopic: sessionTopic, + changeName: changeName, + newValue: newValue, + ); + } + } + + Future _emitEventForADApp({ + required String sessionTopic, + required String changeName, + required String newValue, + }) => + _wcClient.emitSessionEvent( + topic: sessionTopic, + chainId: 'zenon:1', + event: SessionEventParams( + name: changeName, + data: newValue, + ), + ); + + Future emitAddressChangeEvent(String newAddress) { + return _emitEventForTheDApps( + changeName: 'addressChange', + newValue: newAddress, + ); + } + + Future emitChainIdChangeEvent(String newChainId) { + return _emitEventForTheDApps( + changeName: 'chainIdChange', + newValue: newChainId, + ); + } + + Map getActiveSessions() => _wcClient.getActiveSessions(); + + Map getSessionsForPairing(String pairingTopic) => + _wcClient.getSessionsForPairing( + pairingTopic: pairingTopic, + ); + + void _sendSuccessfullyApprovedSessionNotification( + PairingMetadata dAppMetadata) { + sl.get().addNotification( + WalletNotification( + title: 'Successfully connected to ${dAppMetadata.name}', + timestamp: DateTime.now().millisecondsSinceEpoch, + details: 'Successfully connected to ${dAppMetadata.name} ' + 'via WalletConnect', + type: NotificationType.paymentSent, + ), + ); + } + + void onSessionProposal(SessionProposalEvent? event) async { + Logger('WalletConnectService') + .log(Level.INFO, 'onSessionProposal triggered', event.toString()); + + if (event != null) { + Logger('WalletConnectService') + .log(Level.INFO, 'session proposal event', event.params.toJson()); + + final dAppMetadata = event.params.proposer.metadata; + + final actionWasAccepted = await showDialogWithNoAndYesOptions( + context: _context, + isBarrierDismissible: false, + title: 'Approve session', + content: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Text('Are you sure you want to ' + 'connect to ${dAppMetadata.name} ?'), + kVerticalSpacing, + Image( + image: NetworkImage(dAppMetadata.icons.first), + height: 100.0, + fit: BoxFit.fitHeight, + ), + kVerticalSpacing, + Text(dAppMetadata.description), + kVerticalSpacing, + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text(dAppMetadata.url), + LinkIcon( + url: dAppMetadata.url, + ) + ], + ), + ], + ), + onYesButtonPressed: () async {}, + onNoButtonPressed: () {}, + ); + + if (actionWasAccepted) { + if (!_idSessionsApproved.contains(event.id)) { + _idSessionsApproved.add(event.id); + ApproveResponse approveResponse = await _wcClient.approveSession( + id: event.id, + namespaces: { + 'zenon': Namespace( + accounts: _getWalletAccounts(), + methods: [ + 'znn_sign', + 'znn_info', + 'znn_send', + ], + events: [ + 'chainIdChange', + 'addressChange', + ], + ), + }, + ); + + _sendSuccessfullyApprovedSessionNotification(dAppMetadata); + dAppsActiveSessions.add(approveResponse.session); + } + } else { + await _wcClient.rejectSession( + id: event.id, + reason: Errors.getSdkError( + Errors.USER_REJECTED, + ), + ); + } + } + } + + void _initialChecks() { + final pendingProposals = _wcClient.getPendingSessionProposals(); + Logger('WalletConnectService').log( + Level.INFO, 'checkForPendingRequests', pendingProposals.keys.length); + if (pendingProposals.isNotEmpty) { + pendingProposals.forEach((key, value) { + _wcClient.approveSession(id: value.id, namespaces: { + 'zenon': Namespace( + accounts: _getWalletAccounts(), + methods: [ + 'znn_sign', + 'znn_info', + 'znn_send', + ], + events: [ + 'chainIdChange', + 'addressChange', + ], + ), + }); + }); + } + } +} diff --git a/lib/utils/address_utils.dart b/lib/utils/address_utils.dart index 7bdd96f9..16791496 100644 --- a/lib/utils/address_utils.dart +++ b/lib/utils/address_utils.dart @@ -17,7 +17,7 @@ class SetAddressArguments { SetAddressArguments(this.keystore, this.port); } -class AddressUtils { +class ZenonAddressUtils { static void refreshBalance() => sl.get().getBalanceForAllAddresses(); diff --git a/lib/utils/clipboard_utils.dart b/lib/utils/clipboard_utils.dart index 63b7c357..2d1d4dfb 100644 --- a/lib/utils/clipboard_utils.dart +++ b/lib/utils/clipboard_utils.dart @@ -1,11 +1,26 @@ +import 'package:clipboard_watcher/clipboard_watcher.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:zenon_syrius_wallet_flutter/blocs/blocs.dart'; import 'package:zenon_syrius_wallet_flutter/main.dart'; import 'package:zenon_syrius_wallet_flutter/model/model.dart'; -import 'package:zenon_syrius_wallet_flutter/utils/notification_utils.dart'; +import 'package:zenon_syrius_wallet_flutter/utils/utils.dart'; class ClipboardUtils { + + static void toggleClipboardWatcherStatus() { + final enableClipboardWatcher = sharedPrefsService!.get( + kEnableClipboardWatcherKey, + defaultValue: kEnableClipboardWatcherDefaultValue, + ); + if (enableClipboardWatcher) { + clipboardWatcher.start(); + } else { + kLastWalletConnectUriNotifier.value = null; + clipboardWatcher.stop(); + } + } + static void copyToClipboard(String stringValue, BuildContext context) { Clipboard.setData( ClipboardData( diff --git a/lib/utils/color_utils.dart b/lib/utils/color_utils.dart index b8567429..08c9a27d 100644 --- a/lib/utils/color_utils.dart +++ b/lib/utils/color_utils.dart @@ -1,5 +1,3 @@ -import 'dart:math'; - import 'package:flutter/material.dart'; import 'package:zenon_syrius_wallet_flutter/utils/app_colors.dart'; import 'package:zenon_syrius_wallet_flutter/utils/constants.dart'; @@ -23,12 +21,13 @@ class ColorUtils { } static Color getPlasmaColor(PlasmaInfo plasmaInfo) { - if (plasmaInfo.currentPlasma >= kPowerUsersPlasmaRequirements.reduce(max)) { + if (plasmaInfo.currentPlasma >= kPillarPlasmaAmountNeeded) { return AppColors.znnColor; - } else if (plasmaInfo.currentPlasma >= - kNormalUsersPlasmaRequirements.reduce(max)) { + } else if (plasmaInfo.currentPlasma >= kIssueTokenPlasmaAmountNeeded && + plasmaInfo.currentPlasma < kPillarPlasmaAmountNeeded) { return Colors.yellow; - } else if (plasmaInfo.currentPlasma >= minPlasmaAmount) { + } else if (plasmaInfo.currentPlasma >= minPlasmaAmount.toInt() && + plasmaInfo.currentPlasma < kIssueTokenPlasmaAmountNeeded) { return Colors.orange; } else { return AppColors.ztsColor; diff --git a/lib/utils/constants.dart b/lib/utils/constants.dart index 145249eb..d7690841 100644 --- a/lib/utils/constants.dart +++ b/lib/utils/constants.dart @@ -3,6 +3,12 @@ import 'package:zenon_syrius_wallet_flutter/utils/app_colors.dart'; import 'package:zenon_syrius_wallet_flutter/widgets/widgets.dart'; import 'package:znn_sdk_dart/znn_sdk_dart.dart'; +// WalletConnect +const String kWcProjectId = String.fromEnvironment('WC_PROJECT_ID', + defaultValue: '6106aa8c2f308b338f31465bef999a1f'); +const String kZenonNameSpace = 'zenon'; +const walletConnectDirName = 'walletconnect'; + // Dimensions const double kAmountSuffixHeight = 25.0; const double kAmountSuffixWidth = 40.0; @@ -27,7 +33,7 @@ const List kBridgeNetworks = [ ]; // Wallet version -const String kWalletVersion = '0.0.6'; +const String kWalletVersion = '0.0.7'; // Boxes constants const String kFavoriteTokensBox = 'favourite_tokens_box'; @@ -74,11 +80,8 @@ const String kWindowPositionYKey = 'window_position_y_key'; const String kWindowMaximizedKey = 'window_maximized_key'; const double kDefaultBorderOutlineWidth = 1.0; -const double kMinDelegationAmount = 1.0; const double kStandardChartNumDays = 7; -const double kMinTokenTotalMaxSupply = 1; -const int kMaxInt = 9223372036854775807; const int kAddressLabelMaxLength = 80; const int kNumOfInitialAddresses = 1; const int kSeedGridNumOfRows = 4; @@ -91,34 +94,35 @@ const int kMomentumsPerDay = kHoursPerDay * kMomentumsPerHour; const int kDaysInAWeek = 7; const int kMomentumsPerWeek = kMomentumsPerDay * kDaysInAWeek; const int kNumOfAddresses = 10; + const int kPillarPlasmaAmountNeeded = 252000; const int kSentinelPlasmaAmountNeeded = 252000; const int kStakePlasmaAmountNeeded = 105000; const int kDelegatePlasmaAmountNeeded = 84000; const int kIssueTokenPlasmaAmountNeeded = 189000; -const int kZnnProjectMaximumFunds = 5000; -const int kQsrProjectMaximumFunds = 50000; -const int kZnnProjectMinimumFunds = 10; -const int kQsrProjectMinimumFunds = 100; + const int kAmountInputMaxCharacterLength = 21; const int kSecondsPerMomentum = 10; -const List kNormalUsersPlasmaRequirements = [ +final List kNormalUsersPlasmaRequirements = [ kStakePlasmaAmountNeeded, kDelegatePlasmaAmountNeeded, ]; -const List kPowerUsersPlasmaRequirements = [ + +final List kPowerUsersPlasmaRequirements = [ kPillarPlasmaAmountNeeded, kSentinelPlasmaAmountNeeded, kIssueTokenPlasmaAmountNeeded, ]; -const String kLocalhostDefaultNodeUrl = 'ws://127.0.0.1:35998'; +const String kLocalhostDefaultNodeUrl = 'ws://127.0.0.1:$kDefaultPort'; +const int kDefaultPort = 35998; List kDefaultNodes = [ 'Embedded Node', kLocalhostDefaultNodeUrl, ]; + const List kWalletActions = [ 'pillar', 'sentinel', @@ -166,9 +170,11 @@ const String kAutoEraseNumAttemptsKey = 'auto_erase_num_attempts'; // Wallet preferences const String kLaunchAtStartupKey = 'launch_at_startup'; -const bool kLaunchAtStartupDefaultValue = false; const String kEnableDesktopNotificationsKey = 'enable_desktop_notifications'; +const String kEnableClipboardWatcherKey = 'enable_clipboard_watcher'; +const bool kLaunchAtStartupDefaultValue = false; const bool kEnableDesktopNotificationsDefaultValue = false; +const bool kEnableClipboardWatcherDefaultValue = false; /// Node management const String kChainIdKey = 'chain_id'; @@ -201,17 +207,3 @@ const List kTabsWithTextTitles = [ Tabs.plasma, Tabs.tokens, ]; - -const List kTabsWithIconTitles = [ - Tabs.bridge, - Tabs.accelerator, - Tabs.help, - Tabs.notifications, - Tabs.settings, - Tabs.resyncWallet, - Tabs.lock, -]; - -const List kDisabledTabs = [ - Tabs.resyncWallet, -]; diff --git a/lib/utils/extensions.dart b/lib/utils/extensions.dart index c0c4ff4d..d2dd34ba 100644 --- a/lib/utils/extensions.dart +++ b/lib/utils/extensions.dart @@ -1,4 +1,4 @@ -import 'dart:math' show pow; +import 'package:big_decimal/big_decimal.dart'; extension StringExtensions on String { String capitalize() { @@ -6,25 +6,27 @@ extension StringExtensions on String { } num toNum() => num.parse(this); -} -extension FixedNumDecimals on double { - String toStringFixedNumDecimals(int numDecimals) { - return '${(this * pow(10, numDecimals)).truncate() / pow(10, numDecimals)}'; + BigInt extractDecimals(int decimals) { + if (!contains('.')) { + return BigInt.parse(this + ''.padRight(decimals, '0')); + } + List parts = split('.'); + + return BigInt.parse(parts[0] + + (parts[1].length > decimals + ? parts[1].substring(0, decimals) + : parts[1].padRight(decimals, '0'))); } -} + //BigInt.parse(num.parse(this).toStringAsFixed(decimals).replaceAll('.', '')); -extension NumExtensions on num { - int extractDecimals(int decimals) => (this * pow(10, decimals)).toInt(); + String abs() => this; } -extension IntExtensions on int { - num addDecimals(int decimals) { - var numberWithDecimals = this / pow(10, decimals); - if (numberWithDecimals == numberWithDecimals.toInt()) { - return numberWithDecimals.toInt(); - } - return numberWithDecimals; +extension BigIntExtensions on BigInt { + String addDecimals(int decimals) { + return BigDecimal.createAndStripZerosForScale(this, decimals, 0) + .toPlainString(); } } @@ -49,3 +51,11 @@ extension ZipTwoLists on List { ); } } + +extension ShortString on String { + String get short { + final longString = this; + return '${longString.substring(0, 6)}...' + '${longString.substring(longString.length - 6)}'; + } +} diff --git a/lib/utils/functions.dart b/lib/utils/functions.dart new file mode 100644 index 00000000..7e620d22 --- /dev/null +++ b/lib/utils/functions.dart @@ -0,0 +1,13 @@ +import 'package:flutter/services.dart'; +import 'package:zenon_syrius_wallet_flutter/main.dart'; +import 'package:znn_sdk_dart/znn_sdk_dart.dart'; + +Future walletSign(List message) async { + List signature = await zenon!.defaultKeyPair!.sign( + Uint8List.fromList( + message, + ), + ); + + return BytesUtils.bytesToHex(signature); +} \ No newline at end of file diff --git a/lib/utils/global.dart b/lib/utils/global.dart index 53b11c17..86f221a6 100644 --- a/lib/utils/global.dart +++ b/lib/utils/global.dart @@ -1,10 +1,12 @@ import 'dart:io'; +import 'package:flutter/cupertino.dart'; import 'package:zenon_syrius_wallet_flutter/model/model.dart'; import 'package:zenon_syrius_wallet_flutter/utils/constants.dart'; import 'package:zenon_syrius_wallet_flutter/widgets/widgets.dart'; import 'package:znn_sdk_dart/znn_sdk_dart.dart'; +ValueNotifier kLastWalletConnectUriNotifier = ValueNotifier(null); String? kCurrentNode; String? kSelectedAddress; String? kKeyStorePath; @@ -38,3 +40,18 @@ WalletNotification? kLastDismissedNotification; int? kNumOfPillars; bool kEmbeddedNodeRunning = false; + +final List kTabsWithIconTitles = [ + Tabs.bridge, + if (kWcProjectId.isNotEmpty) Tabs.walletConnect, + Tabs.accelerator, + Tabs.help, + Tabs.notifications, + Tabs.settings, + Tabs.resyncWallet, + Tabs.lock, +]; + +final List kDisabledTabs = [ + Tabs.resyncWallet, +]; diff --git a/lib/utils/init_utils.dart b/lib/utils/init_utils.dart index 65d62b9c..b8996667 100644 --- a/lib/utils/init_utils.dart +++ b/lib/utils/init_utils.dart @@ -4,6 +4,7 @@ import 'package:flutter/material.dart'; import 'package:hive/hive.dart'; import 'package:zenon_syrius_wallet_flutter/main.dart'; import 'package:zenon_syrius_wallet_flutter/services/shared_prefs_service.dart'; +import 'package:zenon_syrius_wallet_flutter/services/wallet_connect_service.dart'; import 'package:zenon_syrius_wallet_flutter/utils/address_utils.dart'; import 'package:zenon_syrius_wallet_flutter/utils/constants.dart'; import 'package:zenon_syrius_wallet_flutter/utils/global.dart'; @@ -24,6 +25,9 @@ class InitUtils { await NodeUtils.setNode(); _setChainId(); await NodeUtils.loadDbNodes(); + + // Initialize WalletConnect client + sl.get().initClient(); } catch (e) { rethrow; } @@ -68,9 +72,9 @@ class InitUtils { ); static Future initWalletAfterDecryption() async { - await AddressUtils.setAddresses(kKeyStore); - await AddressUtils.setAddressLabels(); - await AddressUtils.setDefaultAddress(); + await ZenonAddressUtils.setAddresses(kKeyStore); + await ZenonAddressUtils.setAddressLabels(); + await ZenonAddressUtils.setDefaultAddress(); zenon!.defaultKeyPair = kKeyStore!.getKeyPair( kDefaultAddressList.indexOf(kSelectedAddress), ); diff --git a/lib/utils/input_validators.dart b/lib/utils/input_validators.dart index e830c43e..3d5478ea 100644 --- a/lib/utils/input_validators.dart +++ b/lib/utils/input_validators.dart @@ -1,7 +1,6 @@ import 'package:logging/logging.dart'; import 'package:validators/validators.dart'; -import 'package:zenon_syrius_wallet_flutter/utils/constants.dart'; -import 'package:zenon_syrius_wallet_flutter/utils/global.dart'; +import 'package:zenon_syrius_wallet_flutter/utils/utils.dart'; import 'package:znn_sdk_dart/znn_sdk_dart.dart'; class InputValidators { @@ -36,12 +35,14 @@ class InputValidators { static String? validateNumber(String? number) { try { - if (number == null) { - return 'Add a number'; + if (number == null || number.isEmpty) { + return 'Number required'; } int.parse(number); return null; - } catch (e) { + } catch (e, stackTrace) { + Logger('InputValidators') + .log(Level.SEVERE, 'validateNumber ', e, stackTrace); return 'Input is not a valid number'; } } @@ -52,10 +53,7 @@ class InputValidators { if (value.isEmpty) { return 'Invalid amount'; } - if (value.length > kAmountInputMaxCharacterLength) { - return 'Input max length is $kAmountInputMaxCharacterLength characters'; - } - double.parse(value); + BigInt.parse(value); return null; } catch (e, stackTrace) { Logger('InputValidators') @@ -69,15 +67,15 @@ class InputValidators { static String? correctValue( String? value, - num? maxValue, - int decimals, { - num min = 0, + BigInt maxValue, + int decimals, + BigInt minValue, { bool canBeEqualToMin = false, bool canBeBlank = false, }) { if (value != null) { try { - if (maxValue == 0) { + if (maxValue == BigInt.zero) { if (canBeEqualToMin) { return null; } @@ -90,28 +88,27 @@ class InputValidators { return 'Enter a valid amount'; } } - if (value.length > kAmountInputMaxCharacterLength) { - return 'Input max length is $kAmountInputMaxCharacterLength characters'; - } - double inputNum = double.parse(value); + + BigInt inputNum = value.extractDecimals(decimals); + if (value.contains('.') && value.split('.')[1].length > decimals) { return 'Inputted number has too many decimals'; } - if (maxValue! < min) { - return 'Your available balance must be at least $min'; + if (maxValue < minValue) { + return 'Your available balance must be at least ${minValue.addDecimals(decimals)}'; } if (canBeEqualToMin) { - return min <= inputNum && inputNum <= maxValue + return minValue <= inputNum && inputNum <= maxValue ? null - : maxValue == min - ? 'Value must be $min' - : 'Value must be between $min and $maxValue'; + : maxValue == minValue + ? 'Value must be ${minValue.addDecimals(decimals)}' + : 'Value must be between ${minValue.addDecimals(decimals)} and ${maxValue.addDecimals(decimals)}'; } - return min < inputNum && inputNum <= maxValue + return minValue < inputNum && inputNum <= maxValue ? null - : maxValue == min - ? 'Value must be $min' - : 'Value must be between $min and $maxValue'; + : maxValue == minValue + ? 'Value must be ${minValue.addDecimals(decimals)}' + : 'Value must be between ${minValue.addDecimals(decimals)} and ${maxValue.addDecimals(decimals)}'; } catch (e, stackTrace) { Logger('InputValidators') .log(Level.SEVERE, 'correctValue', e, stackTrace); @@ -174,7 +171,7 @@ class InputValidators { static String? isMaxSupplyZero(String? value) { if (value != null) { try { - if (double.parse(value) != 0) { + if (BigInt.parse(value) != BigInt.zero) { return 'Max supply must be 0 for non-mintable tokens'; } return null; diff --git a/lib/utils/math_utils.dart b/lib/utils/math_utils.dart new file mode 100644 index 00000000..1c8a0b18 --- /dev/null +++ b/lib/utils/math_utils.dart @@ -0,0 +1,15 @@ +class MathUtils { + static BigInt bigMin(BigInt a, BigInt b) { + if (a <= b) { + return a; + } + return b; + } + + static BigInt bigMax(BigInt a, BigInt b) { + if (a <= b) { + return b; + } + return a; + } +} diff --git a/lib/utils/navigation_utils.dart b/lib/utils/navigation_utils.dart index f6c3348a..404f1f9a 100644 --- a/lib/utils/navigation_utils.dart +++ b/lib/utils/navigation_utils.dart @@ -6,7 +6,7 @@ import 'package:zenon_syrius_wallet_flutter/main.dart'; import 'package:zenon_syrius_wallet_flutter/model/model.dart'; class NavigationUtils { - static Future openUrl(String url, BuildContext context) async { + static Future openUrl(String url) async { if (!RegExp(r'^http').hasMatch(url)) { url = 'http://$url'; } diff --git a/lib/utils/node_utils.dart b/lib/utils/node_utils.dart index 4a28e2c7..294540ec 100644 --- a/lib/utils/node_utils.dart +++ b/lib/utils/node_utils.dart @@ -4,7 +4,7 @@ import 'dart:isolate'; import 'package:hive/hive.dart'; import 'package:logging/logging.dart'; -import 'package:wakelock/wakelock.dart'; +import 'package:wakelock_plus/wakelock_plus.dart'; import 'package:zenon_syrius_wallet_flutter/blocs/blocs.dart'; import 'package:zenon_syrius_wallet_flutter/embedded_node/embedded_node.dart'; import 'package:zenon_syrius_wallet_flutter/main.dart'; @@ -40,8 +40,8 @@ class NodeUtils { static closeEmbeddedNode() async { // Release WakeLock - if (!Platform.isLinux && await Wakelock.enabled) { - Wakelock.disable(); + if (await WakelockPlus.enabled) { + WakelockPlus.disable(); } if (kCurrentNode == kLocalhostDefaultNodeUrl || @@ -196,8 +196,8 @@ class NodeUtils { await NodeUtils.establishConnectionToNode(kLocalhostDefaultNodeUrl); if (isConnectionEstablished == false) { // Acquire WakeLock - if (!Platform.isLinux && !await Wakelock.enabled) { - Wakelock.enable(); + if (!await WakelockPlus.enabled) { + WakelockPlus.enable(); } // Initialize local full node await Isolate.spawn(EmbeddedNode.runNode, [''], diff --git a/lib/utils/notifiers/default_address_notifier.dart b/lib/utils/notifiers/default_address_notifier.dart index dd85398b..1117ba40 100644 --- a/lib/utils/notifiers/default_address_notifier.dart +++ b/lib/utils/notifiers/default_address_notifier.dart @@ -1,9 +1,12 @@ import 'package:flutter/material.dart'; +import 'package:zenon_syrius_wallet_flutter/main.dart'; +import 'package:zenon_syrius_wallet_flutter/services/wallet_connect_service.dart'; import 'package:zenon_syrius_wallet_flutter/utils/global.dart'; class SelectedAddressNotifier extends ChangeNotifier { void changeSelectedAddress(String? newSelectedAddress) { kSelectedAddress = newSelectedAddress; + sl().emitAddressChangeEvent(newSelectedAddress!); notifyListeners(); } } diff --git a/lib/utils/widget_utils.dart b/lib/utils/widget_utils.dart index a8bf6259..6fb3c377 100644 --- a/lib/utils/widget_utils.dart +++ b/lib/utils/widget_utils.dart @@ -1,5 +1,3 @@ -import 'dart:math'; - import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:zenon_syrius_wallet_flutter/main.dart'; @@ -72,12 +70,13 @@ class WidgetUtils { } static String getPlasmaToolTipMessage(PlasmaInfo plasmaInfo) { - if (plasmaInfo.currentPlasma >= kPowerUsersPlasmaRequirements.reduce(max)) { + if (plasmaInfo.currentPlasma >= kPillarPlasmaAmountNeeded) { return 'High Plasma'; - } else if (plasmaInfo.currentPlasma >= - kNormalUsersPlasmaRequirements.reduce(max)) { + } else if (plasmaInfo.currentPlasma >= kIssueTokenPlasmaAmountNeeded && + plasmaInfo.currentPlasma < kPillarPlasmaAmountNeeded) { return 'Average Plasma'; - } else if (plasmaInfo.currentPlasma >= minPlasmaAmount) { + } else if (plasmaInfo.currentPlasma >= minPlasmaAmount.toInt() && + plasmaInfo.currentPlasma < kIssueTokenPlasmaAmountNeeded) { return 'Low Plasma'; } else { return 'Insufficient Plasma'; diff --git a/lib/utils/zts_utils.dart b/lib/utils/zts_utils.dart index 794dc687..ac3fe228 100644 --- a/lib/utils/zts_utils.dart +++ b/lib/utils/zts_utils.dart @@ -12,11 +12,11 @@ final Token kZnnCoin = Token( 'Zenon', 'ZNN', 'zenon.network', - 0, - znnDecimals, + BigInt.zero, + coinDecimals, pillarAddress, TokenStandard.parse(znnTokenStandard), - 0, + BigInt.zero, true, true, true, @@ -25,11 +25,11 @@ final Token kQsrCoin = Token( 'Quasar', 'QSR', 'zenon.network', - 0, - qsrDecimals, + BigInt.zero, + coinDecimals, stakeAddress, TokenStandard.parse(qsrTokenStandard), - 0, + BigInt.zero, true, true, true, diff --git a/lib/widgets/charts/pillar_rewards_chart.dart b/lib/widgets/charts/pillar_rewards_chart.dart index 80984a84..200fc20e 100644 --- a/lib/widgets/charts/pillar_rewards_chart.dart +++ b/lib/widgets/charts/pillar_rewards_chart.dart @@ -60,16 +60,17 @@ class PillarRewardsChartState extends State { .toList()[index] .znnAmount .addDecimals( - znnDecimals, - ); + coinDecimals, + ) + .toNum(); num _getMaxValueOfZnnRewards() { - int? max = widget.rewardsHistory!.list.first.znnAmount; + BigInt? max = widget.rewardsHistory!.list.first.znnAmount; for (var element in widget.rewardsHistory!.list) { if (element.znnAmount > max!) { max = element.znnAmount; } } - return max!.addDecimals(znnDecimals); + return max!.addDecimals(coinDecimals).toNum(); } } diff --git a/lib/widgets/charts/sentinel_rewards_chart.dart b/lib/widgets/charts/sentinel_rewards_chart.dart index 464d766c..51d42034 100644 --- a/lib/widgets/charts/sentinel_rewards_chart.dart +++ b/lib/widgets/charts/sentinel_rewards_chart.dart @@ -77,28 +77,34 @@ class _SentinelRewardsChart extends State { .toList()[index] .znnAmount .addDecimals( - znnDecimals, + coinDecimals, ) + .toNum() : widget.rewardsHistory!.list.reversed .toList()[index] .qsrAmount .addDecimals( - qsrDecimals, - ); + coinDecimals, + ) + .toNum(); } num _getMaxValueOfRewards() { - num maxZnn = _getMaxValueOfZnnRewards().addDecimals( - znnDecimals, - ); - num maxQsr = _getMaxValueOfQsrRewards().addDecimals( - qsrDecimals, - ); + num maxZnn = _getMaxValueOfZnnRewards() + .addDecimals( + coinDecimals, + ) + .toNum(); + num maxQsr = _getMaxValueOfQsrRewards() + .addDecimals( + coinDecimals, + ) + .toNum(); return max(maxQsr, maxZnn); } - int _getMaxValueOfZnnRewards() { - int max = widget.rewardsHistory!.list.first.znnAmount; + BigInt _getMaxValueOfZnnRewards() { + BigInt max = widget.rewardsHistory!.list.first.znnAmount; for (var element in widget.rewardsHistory!.list) { if (element.znnAmount > max) { max = element.znnAmount; @@ -107,8 +113,8 @@ class _SentinelRewardsChart extends State { return max; } - int _getMaxValueOfQsrRewards() { - int max = widget.rewardsHistory!.list.first.qsrAmount; + BigInt _getMaxValueOfQsrRewards() { + BigInt max = widget.rewardsHistory!.list.first.qsrAmount; for (var element in widget.rewardsHistory!.list) { if (element.qsrAmount > max) { max = element.qsrAmount; diff --git a/lib/widgets/charts/staking_rewards_chart.dart b/lib/widgets/charts/staking_rewards_chart.dart index 026de9f9..8daf8b76 100644 --- a/lib/widgets/charts/staking_rewards_chart.dart +++ b/lib/widgets/charts/staking_rewards_chart.dart @@ -60,18 +60,21 @@ class _StakingRewardsChart extends State { .toList()[index] .qsrAmount .addDecimals( - qsrDecimals, - ); + coinDecimals, + ) + .toNum(); num _getMaxValueOfQsrRewards() { - int max = widget.rewardsHistory!.list.first.qsrAmount; + BigInt max = widget.rewardsHistory!.list.first.qsrAmount; for (var element in widget.rewardsHistory!.list) { if (element.qsrAmount > max) { max = element.qsrAmount; } } - return max.addDecimals( - qsrDecimals, - ); + return max + .addDecimals( + coinDecimals, + ) + .toNum(); } } diff --git a/lib/widgets/main_app_container.dart b/lib/widgets/main_app_container.dart index ce179578..96780db4 100644 --- a/lib/widgets/main_app_container.dart +++ b/lib/widgets/main_app_container.dart @@ -1,19 +1,31 @@ import 'dart:async'; +import 'dart:io'; +import 'package:app_links/app_links.dart'; +import 'package:clipboard_watcher/clipboard_watcher.dart'; +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; +import 'package:flutter_svg/svg.dart'; import 'package:flutter_vector_icons/flutter_vector_icons.dart'; +import 'package:logging/logging.dart'; import 'package:provider/provider.dart'; +import 'package:wallet_connect_uri_validator/wallet_connect_uri_validator.dart'; +import 'package:window_manager/window_manager.dart'; import 'package:zenon_syrius_wallet_flutter/blocs/blocs.dart'; import 'package:zenon_syrius_wallet_flutter/main.dart'; import 'package:zenon_syrius_wallet_flutter/model/model.dart'; +import 'package:zenon_syrius_wallet_flutter/services/wallet_connect_service.dart'; import 'package:zenon_syrius_wallet_flutter/utils/app_colors.dart'; +import 'package:zenon_syrius_wallet_flutter/utils/clipboard_utils.dart'; import 'package:zenon_syrius_wallet_flutter/utils/constants.dart'; import 'package:zenon_syrius_wallet_flutter/utils/extensions.dart'; import 'package:zenon_syrius_wallet_flutter/utils/format_utils.dart'; import 'package:zenon_syrius_wallet_flutter/utils/global.dart'; import 'package:zenon_syrius_wallet_flutter/utils/notification_utils.dart'; import 'package:zenon_syrius_wallet_flutter/utils/notifiers/text_scaling_notifier.dart'; +import 'package:zenon_syrius_wallet_flutter/utils/zts_utils.dart'; +import 'package:zenon_syrius_wallet_flutter/widgets/tab_children_widgets/wallet_connect_tab_child.dart'; import 'package:zenon_syrius_wallet_flutter/widgets/widgets.dart'; import 'package:znn_sdk_dart/znn_sdk_dart.dart'; @@ -32,6 +44,7 @@ enum Tabs { resyncWallet, bridge, accelerator, + walletConnect, } class MainAppContainer extends StatefulWidget { @@ -49,13 +62,14 @@ class MainAppContainer extends StatefulWidget { } class _MainAppContainerState extends State - with TickerProviderStateMixin { + with TickerProviderStateMixin, ClipboardListener, WindowListener { late AnimationController _animationController; late Animation _animation; final NodeSyncStatusBloc _netSyncStatusBloc = NodeSyncStatusBloc(); late StreamSubscription _lockBlockStreamSubscription; + late StreamSubscription _incomingLinkSubscription; Timer? _navigateToLockTimer; @@ -70,9 +84,21 @@ class _MainAppContainerState extends State canRequestFocus: false, ); + bool _initialUriIsHandled = false; + + final _appLinks = AppLinks(); + @override void initState() { + sl().context = context; + + clipboardWatcher.addListener(this); + windowManager.addListener(this); + + ClipboardUtils.toggleClipboardWatcherStatus(); + _netSyncStatusBloc.getDataPeriodically(); + _transferTabChild = TransferTabChild( navigateToBridgeTab: () { _navigateTo(Tabs.bridge); @@ -87,6 +113,8 @@ class _MainAppContainerState extends State _animation = Tween(begin: 1.0, end: 3.0).animate(_animationController); kCurrentPage = kWalletInitCompleted ? Tabs.dashboard : Tabs.lock; _initLockBlock(); + _handleIncomingLinks(); + _handleInitialUri(); super.initState(); } @@ -277,6 +305,18 @@ class _MainAppContainerState extends State : Theme.of(context).iconTheme.color, ), ), + if (kWcProjectId.isNotEmpty) + Tab( + child: SvgPicture.asset( + 'assets/svg/walletconnect-logo.svg', + width: 24.0, + fit: BoxFit.fitWidth, + colorFilter: _isTabSelected(Tabs.walletConnect) + ? const ColorFilter.mode(AppColors.znnColor, BlendMode.srcIn) + : ColorFilter.mode( + Theme.of(context).iconTheme.color!, BlendMode.srcIn), + ), + ), Tab( child: Icon( MaterialCommunityIcons.rocket, @@ -431,6 +471,7 @@ class _MainAppContainerState extends State _navigateTo(Tabs.notifications), ), const BridgeTabChild(), + if (kWcProjectId.isNotEmpty) const WalletConnectTabChild(), AcceleratorTabChild( onStepperNotificationSeeMorePressed: () => _navigateTo(Tabs.notifications), @@ -458,15 +499,22 @@ class _MainAppContainerState extends State Duration(minutes: kAutoLockWalletMinutes!), (timer) => _lockBloc.addEvent(LockEvent.navigateToLock), ); - _lockBloc.addEvent(LockEvent.navigateToPreviousTab); + if (kLastWalletConnectUriNotifier.value != null) { + _tabController!.animateTo(_getTabChildIndex(Tabs.walletConnect)); + } else { + _lockBloc.addEvent(LockEvent.navigateToPreviousTab); + } } @override void dispose() { + windowManager.removeListener(this); + _animationController.dispose(); _netSyncStatusBloc.dispose(); _navigateToLockTimer?.cancel(); _lockBlockStreamSubscription.cancel(); + _incomingLinkSubscription.cancel(); _tabController?.dispose(); super.dispose(); } @@ -492,7 +540,11 @@ class _MainAppContainerState extends State ), (timer) => _lockBloc.addEvent(LockEvent.navigateToLock), ); - _lockBloc.addEvent(LockEvent.navigateToDashboard); + if (kLastWalletConnectUriNotifier.value != null) { + _tabController!.animateTo(_getTabChildIndex(Tabs.walletConnect)); + } else { + _lockBloc.addEvent(LockEvent.navigateToDashboard); + } _listenToAutoReceiveTxWorkerNotifications(); } @@ -558,8 +610,10 @@ class _MainAppContainerState extends State _transferTabChild!.sendCard = DimensionCard.small; _transferTabChild!.receiveCard = DimensionCard.large; } - kCurrentPage = page; - _tabController!.animateTo(kTabs.indexOf(page)); + if (kCurrentPage != page) { + kCurrentPage = page; + _tabController!.animateTo(kTabs.indexOf(page)); + } } void _initTabController() { @@ -630,4 +684,327 @@ class _MainAppContainerState extends State _lockBloc.addEvent(LockEvent.countDown); } } + + void _handleIncomingLinks() async { + if (!kIsWeb) { + _incomingLinkSubscription = + _appLinks.allUriLinkStream.listen((Uri? uri) async { + if (!await windowManager.isFocused() || + !await windowManager.isVisible()) { + windowManager.show(); + } + + if (uri != null) { + String uriRaw = uri.toString(); + + Logger('MainAppContainer') + .log(Level.INFO, '_handleIncomingLinks $uriRaw'); + + if (context.mounted) { + if (uriRaw.contains('wc')) { + if (Platform.isWindows) { + uriRaw = uriRaw.replaceAll('/?', '?'); + } + String wcUri = Uri.decodeFull(uriRaw.split('wc?uri=').last); + if (WalletConnectUri.tryParse(wcUri) != null) { + _updateWalletConnectUri(wcUri); + } + return; + } + + // Deep link query parameters + String queryAddress = ''; + String queryAmount = ''; // with decimals + int queryDuration = 0; // in months + String queryZTS = ''; + String queryPillarName = ''; + Token? token; + + if (uri.hasQuery) { + uri.queryParametersAll.forEach((key, value) async { + if (key == 'amount') { + queryAmount = value.first; + } else if (key == 'zts') { + queryZTS = value.first; + } else if (key == 'address') { + queryAddress = value.first; + } else if (key == 'duration') { + queryDuration = int.parse(value.first); + } else if (key == 'pillar') { + queryPillarName = value.first; + } + }); + } + + if (queryZTS.isNotEmpty) { + if (queryZTS == 'znn' || queryZTS == 'ZNN') { + token = kZnnCoin; + } else if (queryZTS == 'qsr' || queryZTS == 'QSR') { + token = kQsrCoin; + } else { + token = await zenon!.embedded.token + .getByZts(TokenStandard.parse(queryZTS)); + } + } + + final sendPaymentBloc = SendPaymentBloc(); + final stakingOptionsBloc = StakingOptionsBloc(); + final delegateButtonBloc = DelegateButtonBloc(); + final plasmaOptionsBloc = PlasmaOptionsBloc(); + + if (context.mounted) { + switch (uri.host) { + case 'transfer': + sl().addNotification( + WalletNotification( + title: 'Transfer action detected', + timestamp: DateTime.now().millisecondsSinceEpoch, + details: 'Deep link: $uriRaw', + type: NotificationType.paymentReceived, + ), + ); + + if (kCurrentPage != Tabs.lock) { + _navigateTo(Tabs.transfer); + + if (token != null) { + showDialogWithNoAndYesOptions( + context: context, + title: 'Transfer action', + isBarrierDismissible: true, + content: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Text( + 'Are you sure you want transfer $queryAmount ${token.symbol} from $kSelectedAddress to $queryAddress?'), + ], + ), + onYesButtonPressed: () { + sendPaymentBloc.sendTransfer( + fromAddress: kSelectedAddress, + toAddress: queryAddress, + amount: + queryAmount.extractDecimals(token!.decimals), + data: null, + token: token, + ); + }, + onNoButtonPressed: () {}, + ); + } + } + break; + + case 'stake': + sl().addNotification( + WalletNotification( + title: 'Stake action detected', + timestamp: DateTime.now().millisecondsSinceEpoch, + details: 'Deep link: $uriRaw', + type: NotificationType.paymentReceived, + ), + ); + + if (kCurrentPage != Tabs.lock) { + _navigateTo(Tabs.staking); + + showDialogWithNoAndYesOptions( + context: context, + title: 'Stake ${kZnnCoin.symbol} action', + isBarrierDismissible: true, + content: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Text( + 'Are you sure you want stake $queryAmount ${kZnnCoin.symbol} for $queryDuration month(s)?'), + ], + ), + onYesButtonPressed: () { + stakingOptionsBloc.stakeForQsr( + Duration(seconds: queryDuration * stakeTimeUnitSec), + queryAmount.extractDecimals(kZnnCoin.decimals)); + }, + onNoButtonPressed: () {}, + ); + } + break; + + case 'delegate': + sl().addNotification( + WalletNotification( + title: 'Delegate action detected', + timestamp: DateTime.now().millisecondsSinceEpoch, + details: 'Deep link: $uriRaw', + type: NotificationType.paymentReceived, + ), + ); + + if (kCurrentPage != Tabs.lock) { + _navigateTo(Tabs.pillars); + + showDialogWithNoAndYesOptions( + context: context, + title: 'Delegate ${kZnnCoin.symbol} action', + isBarrierDismissible: true, + content: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Text( + 'Are you sure you want delegate the ${kZnnCoin.symbol} from $kSelectedAddress to Pillar $queryPillarName?'), + ], + ), + onYesButtonPressed: () { + delegateButtonBloc.delegateToPillar(queryPillarName); + }, + onNoButtonPressed: () {}, + ); + } + break; + + case 'fuse': + sl().addNotification( + WalletNotification( + title: 'Fuse ${kQsrCoin.symbol} action detected', + timestamp: DateTime.now().millisecondsSinceEpoch, + details: 'Deep link: $uriRaw', + type: NotificationType.paymentReceived, + ), + ); + + if (kCurrentPage != Tabs.lock) { + _navigateTo(Tabs.plasma); + + showDialogWithNoAndYesOptions( + context: context, + title: 'Fuse ${kQsrCoin.symbol} action', + isBarrierDismissible: true, + content: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Text( + 'Are you sure you want fuse $queryAmount ${kQsrCoin.symbol} for address $queryAddress?'), + ], + ), + onYesButtonPressed: () { + plasmaOptionsBloc.generatePlasma(queryAddress, + queryAmount.extractDecimals(kZnnCoin.decimals)); + }, + onNoButtonPressed: () {}, + ); + } + break; + + case 'sentinel': + sl().addNotification( + WalletNotification( + title: 'Deploy Sentinel action detected', + timestamp: DateTime.now().millisecondsSinceEpoch, + details: 'Deep link: $uriRaw', + type: NotificationType.paymentReceived, + ), + ); + + if (kCurrentPage != Tabs.lock) { + _navigateTo(Tabs.sentinels); + } + break; + + case 'pillar': + sl().addNotification( + WalletNotification( + title: 'Deploy Pillar action detected', + timestamp: DateTime.now().millisecondsSinceEpoch, + details: 'Deep link: $uriRaw', + type: NotificationType.paymentReceived, + ), + ); + + if (kCurrentPage != Tabs.lock) { + _navigateTo(Tabs.pillars); + } + break; + + default: + sl().addNotification( + WalletNotification( + title: 'Incoming link detected', + timestamp: DateTime.now().millisecondsSinceEpoch, + details: 'Deep link: $uriRaw', + type: NotificationType.paymentReceived, + ), + ); + break; + } + } + return; + } + } + }, onDone: () { + Logger('MainAppContainer') + .log(Level.INFO, '_handleIncomingLinks', 'done'); + }, onError: (Object err) { + NotificationUtils.sendNotificationError( + err, 'Handle incoming link failed'); + Logger('MainAppContainer') + .log(Level.WARNING, '_handleIncomingLinks', err); + if (!mounted) return; + }); + } + } + + Future _handleInitialUri() async { + if (!_initialUriIsHandled) { + _initialUriIsHandled = true; + try { + final uri = await _appLinks.getInitialAppLink(); + if (uri != null) { + Logger('MainAppContainer').log(Level.INFO, '_handleInitialUri $uri'); + } + if (!mounted) return; + } on PlatformException catch (e, stackTrace) { + Logger('MainAppContainer').log(Level.WARNING, + '_handleInitialUri PlatformException', e, stackTrace); + } on FormatException catch (e, stackTrace) { + Logger('MainAppContainer').log( + Level.WARNING, '_handleInitialUri FormatException', e, stackTrace); + if (!mounted) return; + } + } + } + + @override + void onClipboardChanged() async { + ClipboardData? newClipboardData = + await Clipboard.getData(Clipboard.kTextPlain); + final text = newClipboardData?.text ?? ''; + if (text.isNotEmpty && WalletConnectUri.tryParse(text) != null) { + // This check is needed because onClipboardChanged is called twice sometimes + if (kLastWalletConnectUriNotifier.value != text) { + _updateWalletConnectUri(text); + } + } + } + + void _updateWalletConnectUri(String text) { + kLastWalletConnectUriNotifier.value = text; + if (!_isWalletLocked()) { + if (kCurrentPage != Tabs.walletConnect) { + sl().addNotification( + WalletNotification( + title: + 'WalletConnect link detected. Go to WalletConnect tab to connect.', + timestamp: DateTime.now().millisecondsSinceEpoch, + details: 'A WalletConnect link has been copied to clipboard. ' + 'Go to the WalletConnect tab to connect to the dApp through ${kLastWalletConnectUriNotifier.value}', + type: NotificationType.copiedToClipboard, + ), + ); + _navigateTo(Tabs.walletConnect); + } + } + } } diff --git a/lib/widgets/modular_widgets/accelerator_widgets/accelerator_donation_stepper.dart b/lib/widgets/modular_widgets/accelerator_widgets/accelerator_donation_stepper.dart index e639822d..774a4cae 100644 --- a/lib/widgets/modular_widgets/accelerator_widgets/accelerator_donation_stepper.dart +++ b/lib/widgets/modular_widgets/accelerator_widgets/accelerator_donation_stepper.dart @@ -36,8 +36,8 @@ class _AcceleratorDonationStepperState final GlobalKey _qsrAmountKey = GlobalKey(); final GlobalKey _submitButtonKey = GlobalKey(); - num _znnAmount = 0; - num _qsrAmount = 0; + BigInt _znnAmount = BigInt.zero; + BigInt _qsrAmount = BigInt.zero; @override void initState() { @@ -209,7 +209,7 @@ class _AcceleratorDonationStepperState width: 15.0, ), StepperButton( - onPressed: accountInfo.znn()! > 0 + onPressed: accountInfo.znn()! > BigInt.zero ? () { setState(() { _lastCompletedStep = @@ -230,8 +230,8 @@ class _AcceleratorDonationStepperState return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Row( - children: const [ + const Row( + children: [ Text('Total donation budget'), StandardTooltipIcon( 'Your donation matters', @@ -249,24 +249,24 @@ class _AcceleratorDonationStepperState suffixIcon: AmountSuffixWidgets( kZnnCoin, onMaxPressed: () { - num maxZnn = accountInfo.getBalanceWithDecimals( + BigInt maxZnn = accountInfo.getBalance( kZnnCoin.tokenStandard, ); if (_znnAmountController.text.isEmpty || - _znnAmountController.text.toNum() < maxZnn) { + _znnAmountController.text.extractDecimals(coinDecimals) < + maxZnn) { setState(() { - _znnAmountController.text = maxZnn.toString(); + _znnAmountController.text = + maxZnn.addDecimals(coinDecimals); }); } }, ), validator: (value) => InputValidators.correctValue( value, - AmountUtils.addDecimals( - accountInfo.znn()!, - znnDecimals, - ), - znnDecimals, + accountInfo.znn()!, + coinDecimals, + BigInt.zero, canBeEqualToMin: true, canBeBlank: true, ), @@ -288,25 +288,24 @@ class _AcceleratorDonationStepperState suffixIcon: AmountSuffixWidgets( kQsrCoin, onMaxPressed: () { - num maxQsr = accountInfo.getBalanceWithDecimals( + BigInt maxQsr = accountInfo.getBalance( kQsrCoin.tokenStandard, ); - if (_qsrAmountController.text.isEmpty || - _qsrAmountController.text.toNum() < maxQsr) { + _qsrAmountController.text.extractDecimals(coinDecimals) < + maxQsr) { setState(() { - _qsrAmountController.text = maxQsr.toString(); + _qsrAmountController.text = + maxQsr.addDecimals(coinDecimals); }); } }, ), validator: (value) => InputValidators.correctValue( value, - AmountUtils.addDecimals( - accountInfo.qsr()!, - qsrDecimals, - ), - znnDecimals, + accountInfo.qsr()!, + coinDecimals, + BigInt.zero, canBeEqualToMin: true, canBeBlank: true, ), @@ -351,38 +350,34 @@ class _AcceleratorDonationStepperState bool _ifInputValid(AccountInfo accountInfo) { try { _znnAmount = _znnAmountController.text.isNotEmpty - ? _znnAmountController.text.toNum() - : 0; + ? _znnAmountController.text.extractDecimals(coinDecimals) + : BigInt.zero; _qsrAmount = _qsrAmountController.text.isNotEmpty - ? _qsrAmountController.text.toNum() - : 0; + ? _qsrAmountController.text.extractDecimals(coinDecimals) + : BigInt.zero; } catch (_) {} return InputValidators.correctValue( _znnAmountController.text, - AmountUtils.addDecimals( - accountInfo.znn()!, - znnDecimals, - ), - znnDecimals, + accountInfo.znn()!, + coinDecimals, + BigInt.zero, canBeEqualToMin: true, canBeBlank: true, ) == null && InputValidators.correctValue( _qsrAmountController.text, - AmountUtils.addDecimals( - accountInfo.qsr()!, - qsrDecimals, - ), - qsrDecimals, + accountInfo.qsr()!, + coinDecimals, + BigInt.zero, canBeEqualToMin: true, canBeBlank: true, ) == null && (_qsrAmountController.text.isNotEmpty || _znnAmountController.text.isNotEmpty) && - (_znnAmount > 0 || _qsrAmount > 0); + (_znnAmount > BigInt.zero || _qsrAmount > BigInt.zero); } Widget _getSubmitDonationStepContent() { diff --git a/lib/widgets/modular_widgets/accelerator_widgets/accelerator_project_list_item.dart b/lib/widgets/modular_widgets/accelerator_widgets/accelerator_project_list_item.dart index 9baf5e63..d0d03a39 100644 --- a/lib/widgets/modular_widgets/accelerator_widgets/accelerator_project_list_item.dart +++ b/lib/widgets/modular_widgets/accelerator_widgets/accelerator_project_list_item.dart @@ -142,13 +142,13 @@ class _AcceleratorProjectListItemState _getProjectStatusTag(), ]; - if (widget.acceleratorProject.znnFundsNeeded > 0) { + if (widget.acceleratorProject.znnFundsNeeded > BigInt.zero) { tags.add( _getProjectZnnFundsNeededTag(context), ); } - if (widget.acceleratorProject.qsrFundsNeeded > 0) { + if (widget.acceleratorProject.qsrFundsNeeded > BigInt.zero) { tags.add( _getProjectQsrFundsNeededTag(context), ); @@ -221,7 +221,7 @@ class _AcceleratorProjectListItemState TagWidget _getProjectZnnFundsNeededTag(BuildContext context) { return TagWidget( text: - '${widget.acceleratorProject.znnFundsNeededWithDecimals} ${kZnnCoin.symbol}', + '${widget.acceleratorProject.znnFundsNeeded.addDecimals(coinDecimals)} ${kZnnCoin.symbol}', hexColorCode: Theme.of(context).colorScheme.secondary.value.toRadixString(16), ); @@ -230,7 +230,7 @@ class _AcceleratorProjectListItemState TagWidget _getProjectQsrFundsNeededTag(BuildContext context) { return TagWidget( text: - '${widget.acceleratorProject.qsrFundsNeededWithDecimals} ${kQsrCoin.symbol}', + '${widget.acceleratorProject.qsrFundsNeeded.addDecimals(coinDecimals)} ${kQsrCoin.symbol}', hexColorCode: Theme.of(context).colorScheme.secondary.value.toRadixString(16), ); @@ -252,8 +252,8 @@ class _AcceleratorProjectListItemState return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Row( - children: const [ + const Row( + children: [ Text('Voting results'), ], ), @@ -352,8 +352,7 @@ class _AcceleratorProjectListItemState ), materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, shape: const CircleBorder(), - onPressed: () => - NavigationUtils.openUrl(widget.acceleratorProject.url, context), + onPressed: () => NavigationUtils.openUrl(widget.acceleratorProject.url), child: Tooltip( message: 'Visit ${widget.acceleratorProject.url}', child: Container( diff --git a/lib/widgets/modular_widgets/accelerator_widgets/accelerator_stats.dart b/lib/widgets/modular_widgets/accelerator_widgets/accelerator_stats.dart index 6bc68252..477c7632 100644 --- a/lib/widgets/modular_widgets/accelerator_widgets/accelerator_stats.dart +++ b/lib/widgets/modular_widgets/accelerator_widgets/accelerator_stats.dart @@ -77,9 +77,11 @@ class _AcceleratorStatsState extends State { dotColor: AppColors.znnColor, mainText: 'Available', detailsWidget: FormattedAmountWithTooltip( - amount: accountInfo.getBalanceWithDecimals( - kZnnCoin.tokenStandard, - ), + amount: accountInfo + .getBalance( + kZnnCoin.tokenStandard, + ) + .addDecimals(coinDecimals), tokenSymbol: kZnnCoin.symbol, builder: (amount, tokenSymbol) => Text( '$amount $tokenSymbol', @@ -92,9 +94,11 @@ class _AcceleratorStatsState extends State { dotColor: AppColors.qsrColor, mainText: 'Available', detailsWidget: FormattedAmountWithTooltip( - amount: accountInfo.getBalanceWithDecimals( - kQsrCoin.tokenStandard, - ), + amount: accountInfo + .getBalance( + kQsrCoin.tokenStandard, + ) + .addDecimals(coinDecimals), tokenSymbol: kQsrCoin.symbol, builder: (amount, tokenSymbol) => Text( '$amount $tokenSymbol', @@ -135,10 +139,10 @@ class _AcceleratorStatsState extends State { Token token, AccountInfo accountInfo, ) { - int value = token.tokenStandard == kZnnCoin.tokenStandard + BigInt value = token.tokenStandard == kZnnCoin.tokenStandard ? accountInfo.znn()! : accountInfo.qsr()!; - int sumValues = accountInfo.znn()! + accountInfo.qsr()!; + BigInt sumValues = accountInfo.znn()! + accountInfo.qsr()!; final isTouched = token.symbol == _touchedSectionTitle; final double opacity = isTouched ? 1.0 : 0.5; diff --git a/lib/widgets/modular_widgets/accelerator_widgets/phase_creation_stepper.dart b/lib/widgets/modular_widgets/accelerator_widgets/phase_creation_stepper.dart index 638db334..95fb217f 100644 --- a/lib/widgets/modular_widgets/accelerator_widgets/phase_creation_stepper.dart +++ b/lib/widgets/modular_widgets/accelerator_widgets/phase_creation_stepper.dart @@ -4,10 +4,10 @@ import 'package:stacked/stacked.dart'; import 'package:zenon_syrius_wallet_flutter/blocs/blocs.dart'; import 'package:zenon_syrius_wallet_flutter/main.dart'; import 'package:zenon_syrius_wallet_flutter/utils/utils.dart'; -import 'package:zenon_syrius_wallet_flutter/widgets/widgets.dart'; -import 'package:znn_sdk_dart/znn_sdk_dart.dart'; import 'package:zenon_syrius_wallet_flutter/widgets/reusable_widgets/custom_material_stepper.dart' as custom_material_stepper; +import 'package:zenon_syrius_wallet_flutter/widgets/widgets.dart'; +import 'package:znn_sdk_dart/znn_sdk_dart.dart'; enum PhaseCreationStep { phaseDetails, @@ -273,11 +273,9 @@ class _PhaseCreationStepperState extends State { kZnnCoin, onMaxPressed: () { setState(() { - _phaseZnnAmountController.text = - AmountUtils.addDecimals( - widget.project.getRemainingZnnFunds(), - znnDecimals) - .toString(); + _phaseZnnAmountController.text = widget.project + .getRemainingZnnFunds() + .addDecimals(coinDecimals); }); }, ), @@ -286,9 +284,9 @@ class _PhaseCreationStepperState extends State { ), validator: (value) => InputValidators.correctValue( value, - AmountUtils.addDecimals( - widget.project.getRemainingZnnFunds(), znnDecimals), - znnDecimals, + widget.project.getRemainingZnnFunds(), + coinDecimals, + BigInt.zero, canBeEqualToMin: true, ), onChanged: (value) { @@ -317,11 +315,9 @@ class _PhaseCreationStepperState extends State { kQsrCoin, onMaxPressed: () { setState(() { - _phaseQsrAmountController.text = - AmountUtils.addDecimals( - widget.project.getRemainingQsrFunds(), - qsrDecimals) - .toString(); + _phaseQsrAmountController.text = widget.project + .getRemainingQsrFunds() + .addDecimals(coinDecimals); }); }, ), @@ -330,9 +326,9 @@ class _PhaseCreationStepperState extends State { ), validator: (value) => InputValidators.correctValue( value, - AmountUtils.addDecimals( - widget.project.getRemainingQsrFunds(), qsrDecimals), - qsrDecimals, + widget.project.getRemainingQsrFunds(), + coinDecimals, + BigInt.zero, canBeEqualToMin: true, ), onChanged: (value) { @@ -377,25 +373,23 @@ class _PhaseCreationStepperState extends State { } Widget _getSubmitPhaseStepContent() { - double remainingZnnBudget = AmountUtils.addDecimals( - widget.project.getRemainingZnnFunds(), znnDecimals) - - double.parse(_phaseZnnAmountController.text.isNotEmpty - ? _phaseZnnAmountController.text - : '0.0'); + BigInt remainingZnnBudget = widget.project.getRemainingZnnFunds() - + (_phaseZnnAmountController.text.isNotEmpty + ? _phaseZnnAmountController.text.extractDecimals(coinDecimals) + : BigInt.zero); - double remainingQsrBudget = AmountUtils.addDecimals( - widget.project.getRemainingQsrFunds(), qsrDecimals) - - double.parse(_phaseQsrAmountController.text.isNotEmpty - ? _phaseQsrAmountController.text - : '0.0'); + BigInt remainingQsrBudget = widget.project.getRemainingQsrFunds() - + (_phaseQsrAmountController.text.isNotEmpty + ? _phaseQsrAmountController.text.extractDecimals(coinDecimals) + : BigInt.zero); return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ DottedBorderInfoWidget( text: 'Remaining budget for the next phases is ' - '$remainingZnnBudget ${kZnnCoin.symbol} and ' - '$remainingQsrBudget ${kQsrCoin.symbol}', + '${remainingZnnBudget.addDecimals(coinDecimals)} ${kZnnCoin.symbol} and ' + '${remainingQsrBudget.addDecimals(coinDecimals)} ${kQsrCoin.symbol}', ), kVerticalSpacing, Row( @@ -428,12 +422,12 @@ class _PhaseCreationStepperState extends State { _phaseNameController.text, _phaseDescriptionController.text, _phaseUrlController.text, - _phaseZnnAmountController.text.toNum().extractDecimals( - znnDecimals, - ), - _phaseQsrAmountController.text.toNum().extractDecimals( - qsrDecimals, - ), + _phaseZnnAmountController.text.extractDecimals( + coinDecimals, + ), + _phaseQsrAmountController.text.extractDecimals( + coinDecimals, + ), ); }, text: 'Submit', @@ -482,17 +476,17 @@ class _PhaseCreationStepperState extends State { null && InputValidators.correctValue( _phaseZnnAmountController.text, - AmountUtils.addDecimals( - widget.project.getRemainingZnnFunds(), znnDecimals), - znnDecimals, + widget.project.getRemainingZnnFunds(), + coinDecimals, + BigInt.zero, canBeEqualToMin: true, ) == null && InputValidators.correctValue( _phaseQsrAmountController.text, - AmountUtils.addDecimals( - widget.project.getRemainingQsrFunds(), qsrDecimals), - qsrDecimals, + widget.project.getRemainingQsrFunds(), + coinDecimals, + BigInt.zero, canBeEqualToMin: true, ) == null; diff --git a/lib/widgets/modular_widgets/accelerator_widgets/project_creation_stepper.dart b/lib/widgets/modular_widgets/accelerator_widgets/project_creation_stepper.dart index 71828429..bc31c31e 100644 --- a/lib/widgets/modular_widgets/accelerator_widgets/project_creation_stepper.dart +++ b/lib/widgets/modular_widgets/accelerator_widgets/project_creation_stepper.dart @@ -203,7 +203,7 @@ class _ProjectCreationStepperState extends State { StepperUtils.getBalanceWidget(kZnnCoin, accountInfo), DottedBorderInfoWidget( text: - 'Creating a project consumes $projectCreationFeeInZnn ${kZnnCoin.symbol} that goes to the Accelerator', + 'Creating a project consumes ${projectCreationFeeInZnn.addDecimals(coinDecimals)} ${kZnnCoin.symbol} that goes to the Accelerator', ), kVerticalSpacing, Row( @@ -218,7 +218,7 @@ class _ProjectCreationStepperState extends State { width: 15.0, ), StepperButton( - onPressed: accountInfo.getBalanceWithDecimals( + onPressed: accountInfo.getBalance( kZnnCoin.tokenStandard, ) >= projectCreationFeeInZnn @@ -339,17 +339,23 @@ class _ProjectCreationStepperState extends State { suffixIcon: AmountSuffixWidgets( kZnnCoin, onMaxPressed: () { - setState(() { - _projectZnnAmountController.text = - kZnnProjectMaximumFunds.toString(); - }); + BigInt maxZnn = kZnnProjectMaximumFunds; + if (_projectZnnAmountController.text.isEmpty || + _projectZnnAmountController.text + .extractDecimals(coinDecimals) < + maxZnn) { + setState(() { + _projectZnnAmountController.text = + maxZnn.addDecimals(coinDecimals); + }); + } }, ), validator: (value) => InputValidators.correctValue( value, kZnnProjectMaximumFunds, kZnnCoin.decimals, - min: kZnnProjectMinimumFunds, + kZnnProjectMinimumFunds, canBeEqualToMin: true, ), onChanged: (value) { @@ -380,17 +386,23 @@ class _ProjectCreationStepperState extends State { suffixIcon: AmountSuffixWidgets( kQsrCoin, onMaxPressed: () { - setState(() { - _projectQsrAmountController.text = - kQsrProjectMaximumFunds.toString(); - }); + BigInt maxQsr = kQsrProjectMaximumFunds; + if (_projectQsrAmountController.text.isEmpty || + _projectQsrAmountController.text + .extractDecimals(coinDecimals) < + maxQsr) { + setState(() { + _projectQsrAmountController.text = + maxQsr.addDecimals(coinDecimals); + }); + } }, ), validator: (value) => InputValidators.correctValue( value, kQsrProjectMaximumFunds, kQsrCoin.decimals, - min: kQsrProjectMinimumFunds, + kQsrProjectMinimumFunds, canBeEqualToMin: true, ), onChanged: (value) { @@ -399,7 +411,6 @@ class _ProjectCreationStepperState extends State { ), ), ), - // Empty space so that all the right edges will align const SizedBox( width: 23.0, ), @@ -440,7 +451,7 @@ class _ProjectCreationStepperState extends State { children: [ DottedBorderInfoWidget( text: - 'Consume $projectCreationFeeInZnn ${kZnnCoin.symbol} to submit the project', + 'Consume ${projectCreationFeeInZnn.addDecimals(coinDecimals)} ${kZnnCoin.symbol} to submit the project', ), kVerticalSpacing, Row( @@ -498,12 +509,12 @@ class _ProjectCreationStepperState extends State { _projectNameController.text, _projectDescriptionController.text, _projectUrlController.text, - _projectZnnAmountController.text.toNum().extractDecimals( - znnDecimals, - ), - _projectQsrAmountController.text.toNum().extractDecimals( - qsrDecimals, - ), + _projectZnnAmountController.text.extractDecimals( + coinDecimals, + ), + _projectQsrAmountController.text.extractDecimals( + coinDecimals, + ), ); }, text: 'Submit', @@ -528,7 +539,7 @@ class _ProjectCreationStepperState extends State { _projectZnnAmountController.text, kZnnProjectMaximumFunds, kZnnCoin.decimals, - min: kZnnProjectMinimumFunds, + kZnnProjectMinimumFunds, canBeEqualToMin: true, ) == null && @@ -536,7 +547,7 @@ class _ProjectCreationStepperState extends State { _projectQsrAmountController.text, kQsrProjectMaximumFunds, kZnnCoin.decimals, - min: kQsrProjectMinimumFunds, + kQsrProjectMinimumFunds, canBeEqualToMin: true, ) == null; diff --git a/lib/widgets/modular_widgets/accelerator_widgets/projects_stats.dart b/lib/widgets/modular_widgets/accelerator_widgets/projects_stats.dart index d4b695ad..03c2a44b 100644 --- a/lib/widgets/modular_widgets/accelerator_widgets/projects_stats.dart +++ b/lib/widgets/modular_widgets/accelerator_widgets/projects_stats.dart @@ -123,13 +123,13 @@ class ProjectsStats extends StatelessWidget { return [ _getBalanceChartSection( AppColors.znnColor, - project.znnFundsNeeded == 0 + project.znnFundsNeeded == BigInt.zero ? 1 : project.getPaidZnnFunds() / project.znnFundsNeeded, ), _getBalanceChartSection( AppColors.znnColor.withOpacity(0.2), - project.znnFundsNeeded == 0 + project.znnFundsNeeded == BigInt.zero ? 0 : project.getRemainingZnnFunds() / project.znnFundsNeeded, ), @@ -140,13 +140,13 @@ class ProjectsStats extends StatelessWidget { return [ _getBalanceChartSection( AppColors.qsrColor, - project.qsrFundsNeeded == 0 + project.qsrFundsNeeded == BigInt.zero ? 1 : project.getPaidQsrFunds() / project.qsrFundsNeeded, ), _getBalanceChartSection( AppColors.qsrColor.withOpacity(0.5), - project.qsrFundsNeeded == 0 + project.qsrFundsNeeded == BigInt.zero ? 0 : project.getRemainingQsrFunds() / project.qsrFundsNeeded, ), @@ -172,10 +172,9 @@ class ProjectsStats extends StatelessWidget { children: [ ChartLegend( dotColor: AppColors.znnColor, - mainText: 'Received funds', + mainText: 'Received', detailsWidget: FormattedAmountWithTooltip( - amount: AmountUtils.addDecimals( - project.getPaidZnnFunds(), znnDecimals), + amount: project.getPaidZnnFunds().addDecimals(coinDecimals), tokenSymbol: kZnnCoin.symbol, builder: (amount, tokenSymbol) => Text( '$amount $tokenSymbol', @@ -185,10 +184,9 @@ class ProjectsStats extends StatelessWidget { ), ChartLegend( dotColor: AppColors.znnColor.withOpacity(0.2), - mainText: 'Remaining funds', + mainText: 'Remaining', detailsWidget: FormattedAmountWithTooltip( - amount: AmountUtils.addDecimals( - project.getRemainingZnnFunds(), znnDecimals), + amount: project.getRemainingZnnFunds().addDecimals(coinDecimals), tokenSymbol: kZnnCoin.symbol, builder: (amount, tokenSymbol) => Text( '$amount $tokenSymbol', @@ -198,10 +196,9 @@ class ProjectsStats extends StatelessWidget { ), ChartLegend( dotColor: AppColors.znnColor.withOpacity(0.4), - mainText: 'Total project cost', + mainText: 'Total', detailsWidget: FormattedAmountWithTooltip( - amount: AmountUtils.addDecimals( - project.getTotalZnnFunds(), znnDecimals), + amount: project.getTotalZnnFunds().addDecimals(coinDecimals), tokenSymbol: kZnnCoin.symbol, builder: (amount, tokenSymbol) => Text( '$amount $tokenSymbol', @@ -223,10 +220,9 @@ class ProjectsStats extends StatelessWidget { children: [ ChartLegend( dotColor: AppColors.qsrColor, - mainText: 'Received funds', + mainText: 'Received', detailsWidget: FormattedAmountWithTooltip( - amount: AmountUtils.addDecimals( - project.getPaidQsrFunds(), qsrDecimals), + amount: project.getPaidQsrFunds().addDecimals(coinDecimals), tokenSymbol: kQsrCoin.symbol, builder: (amount, tokenSymbol) => Text( '$amount $tokenSymbol', @@ -236,10 +232,9 @@ class ProjectsStats extends StatelessWidget { ), ChartLegend( dotColor: AppColors.qsrColor.withOpacity(0.2), - mainText: 'Remaining funds', + mainText: 'Remaining', detailsWidget: FormattedAmountWithTooltip( - amount: AmountUtils.addDecimals( - project.getRemainingQsrFunds(), qsrDecimals), + amount: project.getRemainingQsrFunds().addDecimals(coinDecimals), tokenSymbol: kQsrCoin.symbol, builder: (amount, tokenSymbol) => Text( '$amount $tokenSymbol', @@ -249,10 +244,9 @@ class ProjectsStats extends StatelessWidget { ), ChartLegend( dotColor: AppColors.qsrColor.withOpacity(0.4), - mainText: 'Total project cost', + mainText: 'Total', detailsWidget: FormattedAmountWithTooltip( - amount: AmountUtils.addDecimals( - project.getTotalQsrFunds(), qsrDecimals), + amount: project.getTotalQsrFunds().addDecimals(coinDecimals), tokenSymbol: kQsrCoin.symbol, builder: (amount, tokenSymbol) => Text( '$amount $tokenSymbol', diff --git a/lib/widgets/modular_widgets/accelerator_widgets/update_phase_stepper.dart b/lib/widgets/modular_widgets/accelerator_widgets/update_phase_stepper.dart index 8a234981..2b7b7466 100644 --- a/lib/widgets/modular_widgets/accelerator_widgets/update_phase_stepper.dart +++ b/lib/widgets/modular_widgets/accelerator_widgets/update_phase_stepper.dart @@ -4,9 +4,9 @@ import 'package:stacked/stacked.dart'; import 'package:zenon_syrius_wallet_flutter/blocs/blocs.dart'; import 'package:zenon_syrius_wallet_flutter/main.dart'; import 'package:zenon_syrius_wallet_flutter/utils/utils.dart'; -import 'package:zenon_syrius_wallet_flutter/widgets/widgets.dart'; import 'package:zenon_syrius_wallet_flutter/widgets/reusable_widgets/custom_material_stepper.dart' as custom_material_stepper; +import 'package:zenon_syrius_wallet_flutter/widgets/widgets.dart'; import 'package:znn_sdk_dart/znn_sdk_dart.dart'; enum UpdatePhaseStep { @@ -56,14 +56,10 @@ class _UpdatePhaseStepperState extends State { _phaseNameController.text = widget.phase.name; _phaseDescriptionController.text = widget.phase.description; _phaseUrlController.text = widget.phase.url; - _phaseZnnAmountController.text = AmountUtils.addDecimals( - widget.phase.znnFundsNeeded, - znnDecimals, - ).toString(); - _phaseQsrAmountController.text = AmountUtils.addDecimals( - widget.phase.qsrFundsNeeded, - qsrDecimals, - ).toString(); + _phaseZnnAmountController.text = + widget.phase.znnFundsNeeded.addDecimals(coinDecimals); + _phaseQsrAmountController.text = + widget.phase.qsrFundsNeeded.addDecimals(coinDecimals); sl.get().getBalanceForAllAddresses(); } @@ -245,9 +241,9 @@ class _UpdatePhaseStepperState extends State { kZnnCoin, onMaxPressed: () { setState(() { - _phaseZnnAmountController.text = AmountUtils.addDecimals( - widget.project.getRemainingZnnFunds(), znnDecimals) - .toString(); + _phaseZnnAmountController.text = widget.project + .getRemainingZnnFunds() + .addDecimals(coinDecimals); }); }, ), @@ -256,9 +252,9 @@ class _UpdatePhaseStepperState extends State { ), validator: (value) => InputValidators.correctValue( value, - AmountUtils.addDecimals( - widget.project.getRemainingZnnFunds(), znnDecimals), + widget.project.getRemainingZnnFunds(), kZnnCoin.decimals, + BigInt.zero, canBeEqualToMin: true, ), onChanged: (value) { @@ -277,9 +273,9 @@ class _UpdatePhaseStepperState extends State { kQsrCoin, onMaxPressed: () { setState(() { - _phaseQsrAmountController.text = AmountUtils.addDecimals( - widget.project.getRemainingQsrFunds(), qsrDecimals) - .toString(); + _phaseQsrAmountController.text = widget.project + .getRemainingQsrFunds() + .addDecimals(coinDecimals); }); }, ), @@ -288,9 +284,9 @@ class _UpdatePhaseStepperState extends State { ), validator: (value) => InputValidators.correctValue( value, - AmountUtils.addDecimals( - widget.project.getRemainingQsrFunds(), qsrDecimals), + widget.project.getRemainingQsrFunds(), kQsrCoin.decimals, + BigInt.zero, canBeEqualToMin: true, ), onChanged: (value) { @@ -364,8 +360,8 @@ class _UpdatePhaseStepperState extends State { _phaseNameController.text, _phaseDescriptionController.text, _phaseUrlController.text, - double.parse(_phaseZnnAmountController.text), - double.parse(_phaseQsrAmountController.text), + _phaseZnnAmountController.text.extractDecimals(coinDecimals), + _phaseQsrAmountController.text.extractDecimals(coinDecimals), ); }, text: 'Update', @@ -379,17 +375,17 @@ class _UpdatePhaseStepperState extends State { InputValidators.checkUrl(_phaseUrlController.text) == null && InputValidators.correctValue( _phaseZnnAmountController.text, - AmountUtils.addDecimals( - widget.project.getRemainingZnnFunds(), znnDecimals), + widget.project.getRemainingZnnFunds(), kZnnCoin.decimals, + BigInt.zero, canBeEqualToMin: true, ) == null && InputValidators.correctValue( _phaseQsrAmountController.text, - AmountUtils.addDecimals( - widget.project.getRemainingQsrFunds(), qsrDecimals), + widget.project.getRemainingQsrFunds(), kQsrCoin.decimals, + BigInt.zero, canBeEqualToMin: true, ) == null; diff --git a/lib/widgets/modular_widgets/bridge_widgets/swap_card.dart b/lib/widgets/modular_widgets/bridge_widgets/swap_card.dart index 39ef0122..08f7f8ce 100644 --- a/lib/widgets/modular_widgets/bridge_widgets/swap_card.dart +++ b/lib/widgets/modular_widgets/bridge_widgets/swap_card.dart @@ -7,6 +7,7 @@ import 'package:zenon_syrius_wallet_flutter/utils/format_utils.dart'; import 'package:zenon_syrius_wallet_flutter/utils/global.dart'; import 'package:zenon_syrius_wallet_flutter/utils/input_validators.dart'; import 'package:zenon_syrius_wallet_flutter/utils/notification_utils.dart'; +import 'package:zenon_syrius_wallet_flutter/utils/utils.dart'; import 'package:zenon_syrius_wallet_flutter/utils/zts_utils.dart'; import 'package:zenon_syrius_wallet_flutter/widgets/widgets.dart'; import 'package:znn_sdk_dart/znn_sdk_dart.dart'; @@ -159,11 +160,11 @@ class _SwapCardState extends State { ), validator: (value) => InputValidators.correctValue( value, - accountInfo.getBalanceWithDecimals( + accountInfo.getBalance( kZnnCoin.tokenStandard, ), kZnnCoin.decimals, - min: 0.99999999, + BigInt.zero, canBeEqualToMin: false, ), suffixIcon: AmountSuffixWidgets( @@ -220,20 +221,20 @@ class _SwapCardState extends State { void _onMaxPressed(AccountInfo accountInfo) => setState(() { _amountController.text = accountInfo - .getBalanceWithDecimals( + .getBalance( kZnnCoin.tokenStandard, ) - .toString(); + .addDecimals(coinDecimals); }); bool _isInputValid(AccountInfo accountInfo) => InputValidators.correctValue( _amountController.text, - accountInfo.getBalanceWithDecimals( + accountInfo.getBalance( kZnnCoin.tokenStandard, ), kZnnCoin.decimals, - min: 0.99999999, + BigInt.zero, canBeEqualToMin: false, ) == null && @@ -253,13 +254,13 @@ class _SwapCardState extends State { void _onSwapButtonPressed() { showDialogWithNoAndYesOptions( + isBarrierDismissible: true, context: context, title: 'Swap', description: 'Are you sure you want to swap ${_amountController.text} ' '${kZnnCoin.symbol} ?', onYesButtonPressed: () { _sendSwapBlock(); - Navigator.pop(context); }, ); } diff --git a/lib/widgets/modular_widgets/dashboard_widgets/balance.dart b/lib/widgets/modular_widgets/dashboard_widgets/balance.dart index 7e58bf66..c57b391c 100644 --- a/lib/widgets/modular_widgets/dashboard_widgets/balance.dart +++ b/lib/widgets/modular_widgets/dashboard_widgets/balance.dart @@ -3,11 +3,7 @@ import 'package:fl_chart/fl_chart.dart'; import 'package:flutter/material.dart'; import 'package:stacked/stacked.dart'; import 'package:zenon_syrius_wallet_flutter/blocs/blocs.dart'; -import 'package:zenon_syrius_wallet_flutter/utils/app_colors.dart'; -import 'package:zenon_syrius_wallet_flutter/utils/color_utils.dart'; -import 'package:zenon_syrius_wallet_flutter/utils/constants.dart'; -import 'package:zenon_syrius_wallet_flutter/utils/global.dart'; -import 'package:zenon_syrius_wallet_flutter/utils/zts_utils.dart'; +import 'package:zenon_syrius_wallet_flutter/utils/utils.dart'; import 'package:zenon_syrius_wallet_flutter/widgets/widgets.dart'; import 'package:znn_sdk_dart/znn_sdk_dart.dart'; @@ -157,9 +153,11 @@ class _BalanceWidgetState extends State { AccountInfo accountInfo, ) { return FormattedAmountWithTooltip( - amount: accountInfo.getBalanceWithDecimals( - coin.tokenStandard, - ), + amount: accountInfo + .getBalance( + coin.tokenStandard, + ) + .addDecimals(coin.decimals), tokenSymbol: coin.symbol, builder: (amount, tokenSymbol) => AmountInfoColumn( context: context, @@ -171,7 +169,7 @@ class _BalanceWidgetState extends State { List _getChartSection(AccountInfo accountInfo) { List sections = []; - if (accountInfo.znn()! > 0) { + if (accountInfo.znn()! > BigInt.zero) { sections.add( _getBalanceChartSection( accountInfo.findTokenByTokenStandard(kZnnCoin.tokenStandard)!, @@ -179,7 +177,7 @@ class _BalanceWidgetState extends State { ), ); } - if (accountInfo.qsr()! > 0) { + if (accountInfo.qsr()! > BigInt.zero) { sections.add( _getBalanceChartSection( accountInfo.findTokenByTokenStandard(kQsrCoin.tokenStandard)!, @@ -197,9 +195,9 @@ class _BalanceWidgetState extends State { return SizedBox( width: 120.0, child: AutoSizeText( - '${accountInfo.getBalanceWithDecimals( - tokenStandard, - )} ${_touchedTokenStandard == kZnnCoin.tokenStandard.toString() ? kZnnCoin.symbol : kQsrCoin.symbol}', + '${accountInfo.getBalance( + tokenStandard, + ).addDecimals(coinDecimals)} ${_touchedTokenStandard == kZnnCoin.tokenStandard.toString() ? kZnnCoin.symbol : kQsrCoin.symbol}', textAlign: TextAlign.center, style: Theme.of(context).textTheme.headlineMedium!.copyWith( color: ColorUtils.getTokenColor(tokenStandard), diff --git a/lib/widgets/modular_widgets/dashboard_widgets/delegation_stats.dart b/lib/widgets/modular_widgets/dashboard_widgets/delegation_stats.dart index 49a8d569..b9448b24 100644 --- a/lib/widgets/modular_widgets/dashboard_widgets/delegation_stats.dart +++ b/lib/widgets/modular_widgets/dashboard_widgets/delegation_stats.dart @@ -2,8 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_vector_icons/flutter_vector_icons.dart'; import 'package:stacked/stacked.dart'; import 'package:zenon_syrius_wallet_flutter/blocs/blocs.dart'; -import 'package:zenon_syrius_wallet_flutter/utils/app_colors.dart'; -import 'package:zenon_syrius_wallet_flutter/utils/zts_utils.dart'; +import 'package:zenon_syrius_wallet_flutter/utils/utils.dart'; import 'package:zenon_syrius_wallet_flutter/widgets/widgets.dart'; import 'package:znn_sdk_dart/znn_sdk_dart.dart'; @@ -67,7 +66,7 @@ class _DelegationStatsState extends State { style: Theme.of(context).textTheme.bodyMedium, ), Text( - '${delegationInfo.weightWithDecimals.toString()} ${kZnnCoin.symbol}', + '${delegationInfo.weight.addDecimals(coinDecimals)} ${kZnnCoin.symbol}', style: Theme.of(context).textTheme.titleMedium, ), ], diff --git a/lib/widgets/modular_widgets/dashboard_widgets/dual_coin_stats.dart b/lib/widgets/modular_widgets/dashboard_widgets/dual_coin_stats.dart index f8642177..a4f827fb 100644 --- a/lib/widgets/modular_widgets/dashboard_widgets/dual_coin_stats.dart +++ b/lib/widgets/modular_widgets/dashboard_widgets/dual_coin_stats.dart @@ -93,8 +93,8 @@ class _DualCoinStatsState extends State } List showingSections(List tokenList) { - int totalSupply = tokenList.fold( - 0, + BigInt totalSupply = tokenList.fold( + BigInt.zero, (previousValue, element) => previousValue + element!.totalSupply, ); return List.generate( diff --git a/lib/widgets/modular_widgets/dashboard_widgets/staking.dart b/lib/widgets/modular_widgets/dashboard_widgets/staking.dart index b850fe5c..ca29739b 100644 --- a/lib/widgets/modular_widgets/dashboard_widgets/staking.dart +++ b/lib/widgets/modular_widgets/dashboard_widgets/staking.dart @@ -2,9 +2,9 @@ import 'package:flutter/material.dart'; import 'package:flutter_vector_icons/flutter_vector_icons.dart'; import 'package:stacked/stacked.dart'; import 'package:zenon_syrius_wallet_flutter/blocs/blocs.dart'; -import 'package:zenon_syrius_wallet_flutter/utils/app_colors.dart'; -import 'package:zenon_syrius_wallet_flutter/utils/zts_utils.dart'; +import 'package:zenon_syrius_wallet_flutter/utils/utils.dart'; import 'package:zenon_syrius_wallet_flutter/widgets/widgets.dart'; +import 'package:znn_sdk_dart/znn_sdk_dart.dart'; const String _kWidgetTitle = 'Staking Stats'; final String _kWidgetDescription = 'This card displays the number of staking ' @@ -65,7 +65,7 @@ class _StakingState extends State { style: Theme.of(context).textTheme.headlineMedium, ), Text( - '${stake.totalZnnStakingAmount.toString()} ${kZnnCoin.symbol}', + '${stake.totalZnnStakingAmount.addDecimals(coinDecimals)} ${kZnnCoin.symbol}', style: Theme.of(context).textTheme.titleMedium, ), ], diff --git a/lib/widgets/modular_widgets/dashboard_widgets/transfer.dart b/lib/widgets/modular_widgets/dashboard_widgets/transfer.dart index b64310c4..8f502110 100644 --- a/lib/widgets/modular_widgets/dashboard_widgets/transfer.dart +++ b/lib/widgets/modular_widgets/dashboard_widgets/transfer.dart @@ -44,11 +44,8 @@ class _TransferState extends State { widget.changePage!(Tabs.transfer, redirectWithSendContainerLarge: true); }, - icon: Container( - alignment: Alignment.center, - child: const Icon( - SimpleLineIcons.arrow_up_circle, - ), + icon: const Icon( + SimpleLineIcons.arrow_up_circle, ), color: AppColors.darkHintTextColor, iconSize: 48.0, diff --git a/lib/widgets/modular_widgets/help_widgets/about_card.dart b/lib/widgets/modular_widgets/help_widgets/about_card.dart index a9ef9f4f..8522a87c 100644 --- a/lib/widgets/modular_widgets/help_widgets/about_card.dart +++ b/lib/widgets/modular_widgets/help_widgets/about_card.dart @@ -160,7 +160,7 @@ class AboutCardState extends State { IconButton( splashRadius: 16, onPressed: () async { - NavigationUtils.openUrl(url, context); + NavigationUtils.openUrl(url); }, icon: const Icon( Icons.link, diff --git a/lib/widgets/modular_widgets/help_widgets/community_card.dart b/lib/widgets/modular_widgets/help_widgets/community_card.dart index 96fb3bc6..360246b5 100644 --- a/lib/widgets/modular_widgets/help_widgets/community_card.dart +++ b/lib/widgets/modular_widgets/help_widgets/community_card.dart @@ -2,7 +2,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_vector_icons/flutter_vector_icons.dart'; import 'package:zenon_syrius_wallet_flutter/utils/app_colors.dart'; import 'package:zenon_syrius_wallet_flutter/utils/constants.dart'; -import 'package:zenon_syrius_wallet_flutter/utils/navigation_utils.dart'; import 'package:zenon_syrius_wallet_flutter/widgets/widgets.dart'; class CommunityCard extends StatelessWidget { @@ -211,29 +210,7 @@ class CommunityCard extends StatelessWidget { const SizedBox( width: 10.0, ), - RawMaterialButton( - constraints: const BoxConstraints( - minWidth: 40.0, - minHeight: 40.0, - ), - materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, - shape: const CircleBorder(), - onPressed: () => NavigationUtils.openUrl(url, context), - child: Container( - height: 25.0, - width: 25.0, - padding: const EdgeInsets.all(4.0), - decoration: const BoxDecoration( - shape: BoxShape.circle, - color: Colors.white12, - ), - child: const Icon( - SimpleLineIcons.link, - size: 10.0, - color: AppColors.znnColor, - ), - ), - ), + LinkIcon(url: url), ], ); } diff --git a/lib/widgets/modular_widgets/help_widgets/update_card.dart b/lib/widgets/modular_widgets/help_widgets/update_card.dart index cd987b98..2b46dd35 100644 --- a/lib/widgets/modular_widgets/help_widgets/update_card.dart +++ b/lib/widgets/modular_widgets/help_widgets/update_card.dart @@ -30,7 +30,7 @@ class UpdateCard extends StatelessWidget { Widget _getCheckUpdateExpandableChild(BuildContext context) { return Center( child: SettingsButton( - onPressed: () => NavigationUtils.openUrl(kGithubReleasesLink, context), + onPressed: () => NavigationUtils.openUrl(kGithubReleasesLink), text: 'Update', ), ); diff --git a/lib/widgets/modular_widgets/modular_widgets.dart b/lib/widgets/modular_widgets/modular_widgets.dart index 9d078502..6f10e0ac 100644 --- a/lib/widgets/modular_widgets/modular_widgets.dart +++ b/lib/widgets/modular_widgets/modular_widgets.dart @@ -9,3 +9,4 @@ export 'settings_widgets/settings_widgets.dart'; export 'staking_widgets/staking_widgets.dart'; export 'token_widgets/token_widgets.dart'; export 'transfer_widgets/transfer_widgets.dart'; +export 'wallet_connect_widgets/wallet_connect_widgets.dart'; \ No newline at end of file diff --git a/lib/widgets/modular_widgets/pillars_widgets/pillar_collect.dart b/lib/widgets/modular_widgets/pillars_widgets/pillar_collect.dart index db76b88e..2e186519 100644 --- a/lib/widgets/modular_widgets/pillars_widgets/pillar_collect.dart +++ b/lib/widgets/modular_widgets/pillars_widgets/pillar_collect.dart @@ -53,7 +53,7 @@ class _PillarCollectState extends State { if (snapshot.hasError) { return SyriusErrorWidget(snapshot.error!); } else if (snapshot.hasData) { - if (snapshot.data!.znnAmount > 0) { + if (snapshot.data!.znnAmount > BigInt.zero) { return _getWidgetBody(snapshot.data!); } return const SyriusErrorWidget('No rewards to collect'); @@ -68,9 +68,11 @@ class _PillarCollectState extends State { mainAxisAlignment: MainAxisAlignment.center, children: [ NumberAnimation( - end: uncollectedReward.znnAmount.addDecimals( - znnDecimals, - ), + end: uncollectedReward.znnAmount + .addDecimals( + coinDecimals, + ) + .toNum(), isInt: false, after: ' ${kZnnCoin.symbol}', style: Theme.of(context).textTheme.headlineLarge!.copyWith( @@ -80,12 +82,13 @@ class _PillarCollectState extends State { ), kVerticalSpacing, Visibility( - visible: uncollectedReward.znnAmount > 0, + visible: uncollectedReward.znnAmount > BigInt.zero, child: LoadingButton.stepper( key: _collectButtonKey, text: 'Collect', - onPressed: - uncollectedReward.znnAmount > 0 ? _onCollectPressed : null, + onPressed: uncollectedReward.znnAmount > BigInt.zero + ? _onCollectPressed + : null, ), ), ], diff --git a/lib/widgets/modular_widgets/pillars_widgets/pillars_list_widget.dart b/lib/widgets/modular_widgets/pillars_widgets/pillars_list_widget.dart index 9e529ac7..c8ef208f 100644 --- a/lib/widgets/modular_widgets/pillars_widgets/pillars_list_widget.dart +++ b/lib/widgets/modular_widgets/pillars_widgets/pillars_list_widget.dart @@ -7,7 +7,6 @@ import 'package:stacked/stacked.dart'; import 'package:zenon_syrius_wallet_flutter/blocs/blocs.dart'; import 'package:zenon_syrius_wallet_flutter/main.dart'; import 'package:zenon_syrius_wallet_flutter/utils/app_colors.dart'; -import 'package:zenon_syrius_wallet_flutter/utils/constants.dart'; import 'package:zenon_syrius_wallet_flutter/utils/extensions.dart'; import 'package:zenon_syrius_wallet_flutter/utils/global.dart'; import 'package:zenon_syrius_wallet_flutter/utils/notification_utils.dart'; @@ -369,7 +368,7 @@ class _PillarsListWidgetState extends State { setState(() { _currentlyDelegatingToPillar = pillarInfo.name; }); - model.votePillar(pillarInfo.name, context); + model.delegateToPillar(pillarInfo.name); }, text: 'DELEGATE', textStyle: Theme.of(context).textTheme.titleSmall!.copyWith( @@ -640,10 +639,7 @@ class _PillarsListWidgetState extends State { delegateButtonKey = _delegateButtonKeys[pillarInfo.name]!; return Visibility( - visible: accountInfo.znn()!.addDecimals( - znnDecimals, - ) >= - kMinDelegationAmount && + visible: accountInfo.znn()! >= kMinDelegationAmount && (_currentlyDelegatingToPillar == null ? true : _currentlyDelegatingToPillar == pillarInfo.name), diff --git a/lib/widgets/modular_widgets/pillars_widgets/pillars_stepper_container.dart b/lib/widgets/modular_widgets/pillars_widgets/pillars_stepper_container.dart index 481fe565..49aea9f4 100644 --- a/lib/widgets/modular_widgets/pillars_widgets/pillars_stepper_container.dart +++ b/lib/widgets/modular_widgets/pillars_widgets/pillars_stepper_container.dart @@ -1,5 +1,3 @@ -import 'dart:math' as math; - import 'package:fl_chart/fl_chart.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; @@ -15,6 +13,7 @@ import 'package:zenon_syrius_wallet_flutter/utils/extensions.dart'; import 'package:zenon_syrius_wallet_flutter/utils/format_utils.dart'; import 'package:zenon_syrius_wallet_flutter/utils/global.dart'; import 'package:zenon_syrius_wallet_flutter/utils/input_validators.dart'; +import 'package:zenon_syrius_wallet_flutter/utils/math_utils.dart'; import 'package:zenon_syrius_wallet_flutter/utils/navigation_utils.dart'; import 'package:zenon_syrius_wallet_flutter/utils/notification_utils.dart'; import 'package:zenon_syrius_wallet_flutter/utils/zts_utils.dart'; @@ -72,7 +71,7 @@ class _MainPillarsState extends State { final GlobalKey _withdrawButtonKey = GlobalKey(); final GlobalKey _registerButtonKey = GlobalKey(); - num? _maxQsrAmount; + BigInt _maxQsrAmount = BigInt.zero; final List> _pillarFormKeys = List.generate( 3, @@ -87,11 +86,9 @@ class _MainPillarsState extends State { @override void initState() { super.initState(); - _znnAmountController.text = pillarRegisterZnnAmount - .addDecimals( - znnDecimals, - ) - .toString(); + _znnAmountController.text = pillarRegisterZnnAmount.addDecimals( + coinDecimals, + ); _addressController.text = kSelectedAddress!; _pillarRewardAddressController.text = kSelectedAddress!; sl.get().getBalanceForAllAddresses(); @@ -131,17 +128,15 @@ class _MainPillarsState extends State { model.stream.listen( (event) { if (event != null) { - _maxQsrAmount = math.min( - accountInfo.getBalanceWithDecimals( + _maxQsrAmount = MathUtils.bigMin( + accountInfo.getBalance( kQsrCoin.tokenStandard, ), - math.max( - 0, - event.cost - (event.deposit), - ), + MathUtils.bigMax(BigInt.zero, event.cost - event.deposit), ); setState(() { - _qsrAmountController.text = _maxQsrAmount.toString(); + _qsrAmountController.text = + _maxQsrAmount.addDecimals(coinDecimals); }); } }, @@ -204,7 +199,7 @@ class _MainPillarsState extends State { accountInfo, ), Text( - '${qsrInfo.cost} ${kQsrCoin.symbol} needed for a Pillar', + '${qsrInfo.cost.addDecimals(coinDecimals)} ${kQsrCoin.symbol} required for a Pillar slot', style: Theme.of(context).inputDecorationTheme.hintStyle, ), @@ -276,7 +271,7 @@ class _MainPillarsState extends State { ), Expanded( child: Visibility( - visible: qsrInfo.deposit > 0, + visible: qsrInfo.deposit > BigInt.zero, child: Container( decoration: BoxDecoration( color: Theme.of(context).colorScheme.background, @@ -321,7 +316,7 @@ class _MainPillarsState extends State { ), ), Text( - 'Current Pillar Slot fee\n${qsrInfo.cost} ' + 'Current Pillar Slot fee\n${qsrInfo.cost.addDecimals(coinDecimals)} ' '${kQsrCoin.symbol}', style: Theme.of(context).textTheme.bodyMedium, textAlign: TextAlign.center, @@ -336,7 +331,7 @@ class _MainPillarsState extends State { SizedBox( width: 130.0, child: Text( - 'You have deposited ${qsrInfo.deposit} ' + 'You have deposited ${qsrInfo.deposit.addDecimals(coinDecimals)} ' '${kQsrCoin.symbol}', textAlign: TextAlign.center, style: Theme.of(context).textTheme.bodyLarge, @@ -399,11 +394,12 @@ class _MainPillarsState extends State { onPressed: _qsrAmountValidator(_qsrAmountController.text, qsrInfo) == null ? () => _onDepositButtonPressed(model, qsrInfo) : null, + outlineColor: AppColors.qsrColor, ); } Widget _getWithdrawQsrButtonViewModel( - num qsrDeposit, + BigInt qsrDeposit, ) { return ViewModelBuilder.reactive( onViewModelReady: (model) { @@ -436,14 +432,15 @@ class _MainPillarsState extends State { Widget _getWithdrawQsrButton( PillarsWithdrawQsrBloc model, - num qsrDeposit, + BigInt qsrDeposit, ) { return Visibility( - visible: qsrDeposit > 0, + visible: qsrDeposit > BigInt.zero, child: LoadingButton.stepper( text: 'Withdraw', - onPressed: () => _onWithdrawButtonPressed(model, qsrDeposit.toDouble()), + onPressed: () => _onWithdrawButtonPressed(model, qsrDeposit), key: _withdrawButtonKey, + outlineColor: AppColors.qsrColor, ), ); } @@ -460,7 +457,7 @@ class _MainPillarsState extends State { onStepTapped: (int index) {}, steps: [ StepperUtils.getMaterialStep( - stepTitle: 'Plasma check', + stepTitle: 'Pillar deployment: Plasma check', stepContent: _getPlasmaCheckFutureBuilder(), stepSubtitle: 'Sufficient Plasma', stepState: StepperUtils.getStepState( @@ -733,14 +730,14 @@ class _MainPillarsState extends State { if (qsrInfo.deposit >= qsrInfo.cost) { _depositQsrButtonKey.currentState?.animateForward(); model.depositQsr( - _qsrAmountController.text, + _qsrAmountController.text.extractDecimals(coinDecimals), justMarkStepCompleted: true, ); - } else if (qsrInfo.deposit + _maxQsrAmount! <= qsrInfo.cost && + } else if (qsrInfo.deposit + _maxQsrAmount <= qsrInfo.cost && _qsrFormKey.currentState!.validate() && - _qsrAmountController.text.toNum() > 0) { + _qsrAmountController.text.extractDecimals(coinDecimals) > BigInt.zero) { _depositQsrButtonKey.currentState?.animateForward(); - model.depositQsr(_qsrAmountController.text); + model.depositQsr(_qsrAmountController.text.extractDecimals(coinDecimals)); } } @@ -781,9 +778,9 @@ class _MainPillarsState extends State { void _onWithdrawButtonPressed( PillarsWithdrawQsrBloc viewModel, - double qsrDeposit, + BigInt qsrDeposit, ) { - if (qsrDeposit > 0) { + if (qsrDeposit > BigInt.zero) { _withdrawButtonKey.currentState?.animateForward(); viewModel.withdrawQsr(_addressController.text); } @@ -851,8 +848,7 @@ class _MainPillarsState extends State { ), recognizer: TapGestureRecognizer() ..onTap = () { - NavigationUtils.openUrl( - kZnnController, context); + NavigationUtils.openUrl(kZnnController); }, ), const WidgetSpan( @@ -1000,12 +996,7 @@ class _MainPillarsState extends State { } bool _hasEnoughZnn(AccountInfo accountInfo) => - accountInfo.getBalanceWithDecimals( - kZnnCoin.tokenStandard, - ) >= - pillarRegisterZnnAmount.addDecimals( - znnDecimals, - ); + accountInfo.znn()! >= pillarRegisterZnnAmount; bool _canDeployPillar() => InputValidators.notEmpty( @@ -1029,13 +1020,13 @@ class _MainPillarsState extends State { value, _maxQsrAmount, kQsrCoin.decimals, - min: 1.0, + BigInt.one, canBeEqualToMin: true, ); void _onQsrNextPressed() { setState(() { - _currentStep = PillarsStepperStep.values[_currentStep.index + 1]; + _saveProgressAndNavigateToNextStep(PillarsStepperStep.qsrManagement); }); } @@ -1060,6 +1051,13 @@ class _MainPillarsState extends State { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ + Text( + 'More Plasma is required to perform complex transactions. Please fuse enough QSR before proceeding.', + style: Theme.of(context).textTheme.headlineSmall, + ), + const SizedBox( + height: 25.0, + ), Row( mainAxisAlignment: MainAxisAlignment.start, children: [ diff --git a/lib/widgets/modular_widgets/plasma_widgets/plasma_options/plasma_options.dart b/lib/widgets/modular_widgets/plasma_widgets/plasma_options/plasma_options.dart index 7a0cd71b..32be63f7 100644 --- a/lib/widgets/modular_widgets/plasma_widgets/plasma_options/plasma_options.dart +++ b/lib/widgets/modular_widgets/plasma_widgets/plasma_options/plasma_options.dart @@ -47,7 +47,7 @@ class _PlasmaOptionsState extends State { PlasmaBeneficiaryAddressNotifier? _plasmaBeneficiaryAddress; - int? _maxQsrAmount; + BigInt _maxQsrAmount = BigInt.zero; double? _maxWidth; final double _marginWidth = 20.0; @@ -102,12 +102,11 @@ class _PlasmaOptionsState extends State { } if (snapshot.connectionState == ConnectionState.active) { if (snapshot.hasData) { - _maxQsrAmount = - snapshot.data![_addressController.text]! - .getBalanceWithDecimals( - kQsrCoin.tokenStandard, - ) - .toInt(); + _maxQsrAmount = snapshot + .data![_addressController.text]! + .getBalance( + kQsrCoin.tokenStandard, + ); return _getWidgetBody( snapshot.data![_addressController.text], ); @@ -183,7 +182,7 @@ class _PlasmaOptionsState extends State { key: _qsrAmountKey, autovalidateMode: AutovalidateMode.onUserInteraction, child: InputField( - enabled: _maxQsrAmount! > 0, + enabled: _maxQsrAmount > BigInt.zero, onChanged: (String value) { setState(() {}); }, @@ -196,9 +195,7 @@ class _PlasmaOptionsState extends State { value, _maxQsrAmount, kQsrCoin.decimals, - min: fuseMinQsrAmount.addDecimals( - qsrDecimals, - ), + fuseMinQsrAmount, canBeEqualToMin: true, ), suffixIcon: _getAmountSuffix(), @@ -240,14 +237,14 @@ class _PlasmaOptionsState extends State { return PlasmaIcon( PlasmaInfo.fromJson( { - 'currentPlasma': (_qsrAmountController.text.isNotEmpty - ? zenon!.embedded.plasma.getPlasmaByQsr( - double.parse(_qsrAmountController.text), - ) + 'currentPlasma': ((_qsrAmountController.text.isNotEmpty + ? int.parse((zenon!.embedded.plasma.getPlasmaByQsr( + _qsrAmountController.text.extractDecimals(coinDecimals), + )).addDecimals(coinDecimals)) : 0) + - _getPlasmaForCurrentBeneficiary(), + _getPlasmaForCurrentBeneficiary()), 'maxPlasma': 0, - 'qsrAmount': 0, + 'qsrAmount': '0', }, ), ); @@ -305,7 +302,7 @@ class _PlasmaOptionsState extends State { _fuseButtonKey.currentState?.animateForward(); model!.generatePlasma( _beneficiaryAddressController.text, - _qsrAmountController.text, + _qsrAmountController.text.extractDecimals(coinDecimals), ); } } @@ -319,9 +316,11 @@ class _PlasmaOptionsState extends State { void _onMaxPressed() { if (_qsrAmountController.text.isEmpty || - _qsrAmountController.text.toNum() != _maxQsrAmount) { + _qsrAmountController.text.extractDecimals(coinDecimals) != + _maxQsrAmount) { setState(() { - _qsrAmountController.text = _maxQsrAmount!.toString(); + _qsrAmountController.text = + _maxQsrAmount.addDecimals(coinDecimals).toNum().toInt().toString(); }); } } @@ -364,9 +363,7 @@ class _PlasmaOptionsState extends State { _qsrAmountController.text, _maxQsrAmount, kQsrCoin.decimals, - min: fuseMinQsrAmount.addDecimals( - qsrDecimals, - ), + fuseMinQsrAmount, canBeEqualToMin: true, ) == null; diff --git a/lib/widgets/modular_widgets/sentinel_widgets/create_sentinel.dart b/lib/widgets/modular_widgets/sentinel_widgets/create_sentinel.dart index 362aa30a..64da18ce 100644 --- a/lib/widgets/modular_widgets/sentinel_widgets/create_sentinel.dart +++ b/lib/widgets/modular_widgets/sentinel_widgets/create_sentinel.dart @@ -58,11 +58,11 @@ class _CreateSentinelState extends State { mainAxisAlignment: MainAxisAlignment.center, children: [ Text( - 'You already spawned a Sentinel on this address', + 'Sentinel detected on this address', style: Theme.of(context).textTheme.bodyLarge, ), const StandardTooltipIcon( - 'Cannot reuse address. ' + 'Cannot reuse address.\n' 'Please use another address to spawn a new Sentinel Node', Icons.help, ), diff --git a/lib/widgets/modular_widgets/sentinel_widgets/sentinel_collect.dart b/lib/widgets/modular_widgets/sentinel_widgets/sentinel_collect.dart index 5bf82495..150e80d1 100644 --- a/lib/widgets/modular_widgets/sentinel_widgets/sentinel_collect.dart +++ b/lib/widgets/modular_widgets/sentinel_widgets/sentinel_collect.dart @@ -52,7 +52,8 @@ class _SentinelCollectState extends State { if (snapshot.hasError) { return SyriusErrorWidget(snapshot.error!); } else if (snapshot.hasData) { - if (snapshot.data!.znnAmount > 0 || snapshot.data!.qsrAmount > 0) { + if (snapshot.data!.znnAmount > BigInt.zero || + snapshot.data!.qsrAmount > BigInt.zero) { return _getWidgetBody(snapshot.data!); } return const SyriusErrorWidget('No rewards to collect'); @@ -67,7 +68,7 @@ class _SentinelCollectState extends State { mainAxisAlignment: MainAxisAlignment.center, children: [ NumberAnimation( - end: uncollectedReward.znnAmount.addDecimals(znnDecimals), + end: uncollectedReward.znnAmount.addDecimals(coinDecimals).toNum(), isInt: false, after: ' ${kZnnCoin.symbol}', style: Theme.of(context).textTheme.headlineLarge!.copyWith( @@ -77,7 +78,7 @@ class _SentinelCollectState extends State { ), kVerticalSpacing, NumberAnimation( - end: uncollectedReward.qsrAmount.addDecimals(qsrDecimals), + end: uncollectedReward.qsrAmount.addDecimals(coinDecimals).toNum(), isInt: false, after: ' ${kQsrCoin.symbol}', style: Theme.of(context).textTheme.headlineLarge!.copyWith( @@ -87,13 +88,13 @@ class _SentinelCollectState extends State { ), kVerticalSpacing, Visibility( - visible: uncollectedReward.qsrAmount > 0 || - uncollectedReward.znnAmount > 0, + visible: uncollectedReward.qsrAmount > BigInt.zero || + uncollectedReward.znnAmount > BigInt.zero, child: LoadingButton.stepper( key: _collectButtonKey, text: 'Collect', - onPressed: uncollectedReward.qsrAmount > 0 || - uncollectedReward.znnAmount > 0 + onPressed: uncollectedReward.qsrAmount > BigInt.zero || + uncollectedReward.znnAmount > BigInt.zero ? _onCollectPressed : null, ), diff --git a/lib/widgets/modular_widgets/sentinel_widgets/sentinels_stepper_container.dart b/lib/widgets/modular_widgets/sentinel_widgets/sentinels_stepper_container.dart index 54898c3a..f0a10366 100644 --- a/lib/widgets/modular_widgets/sentinel_widgets/sentinels_stepper_container.dart +++ b/lib/widgets/modular_widgets/sentinel_widgets/sentinels_stepper_container.dart @@ -1,5 +1,3 @@ -import 'dart:math' as math; - import 'package:fl_chart/fl_chart.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; @@ -14,12 +12,13 @@ import 'package:zenon_syrius_wallet_flutter/utils/extensions.dart'; import 'package:zenon_syrius_wallet_flutter/utils/format_utils.dart'; import 'package:zenon_syrius_wallet_flutter/utils/global.dart'; import 'package:zenon_syrius_wallet_flutter/utils/input_validators.dart'; +import 'package:zenon_syrius_wallet_flutter/utils/math_utils.dart'; import 'package:zenon_syrius_wallet_flutter/utils/navigation_utils.dart'; import 'package:zenon_syrius_wallet_flutter/utils/notification_utils.dart'; import 'package:zenon_syrius_wallet_flutter/utils/zts_utils.dart'; -import 'package:zenon_syrius_wallet_flutter/widgets/widgets.dart'; import 'package:zenon_syrius_wallet_flutter/widgets/reusable_widgets/custom_material_stepper.dart' as custom_material_stepper; +import 'package:zenon_syrius_wallet_flutter/widgets/widgets.dart'; import 'package:znn_sdk_dart/znn_sdk_dart.dart'; enum SentinelsStepperStep { @@ -51,9 +50,9 @@ class _MainSentinelsState extends State { final GlobalKey _registerButtonKey = GlobalKey(); final GlobalKey _qsrFormKey = GlobalKey(); - num _withdrawnQSR = 0; - num? _maxQsrAmount; - late num _qsrCost; + BigInt _withdrawnQSR = BigInt.zero; + BigInt _maxQsrAmount = BigInt.zero; + BigInt _qsrCost = BigInt.zero; final int _numSteps = SentinelsStepperStep.values.length; @@ -62,13 +61,11 @@ class _MainSentinelsState extends State { @override void initState() { super.initState(); - _qsrCost = sentinelRegisterQsrAmount.addDecimals(qsrDecimals); - _qsrAmountController.text = _qsrCost.toString(); - _znnAmountController.text = sentinelRegisterZnnAmount - .addDecimals( - znnDecimals, - ) - .toString(); + _qsrCost = sentinelRegisterQsrAmount; + _qsrAmountController.text = _qsrCost.addDecimals(coinDecimals); + _znnAmountController.text = sentinelRegisterZnnAmount.addDecimals( + coinDecimals, + ); _addressController.text = kSelectedAddress!; sl.get().getBalanceForAllAddresses(); _iniStepperControllers(); @@ -102,7 +99,7 @@ class _MainSentinelsState extends State { _sentinelsQsrInfoViewModel = model; model.getQsrDepositedAmount(_addressController.text); }, - builder: (_, model, __) => StreamBuilder( + builder: (_, model, __) => StreamBuilder( stream: model.stream, builder: (_, snapshot) { if (snapshot.hasData) { @@ -127,12 +124,12 @@ class _MainSentinelsState extends State { Widget _getDepositQsrStepBody( BuildContext context, AccountInfo accountInfo, - num depositedQsr, + BigInt depositedQsr, ) { - _maxQsrAmount = math.min( - accountInfo.getBalanceWithDecimals(kQsrCoin.tokenStandard), - math.max(0, _qsrCost - (depositedQsr - _withdrawnQSR)), - ); + _maxQsrAmount = MathUtils.bigMin( + accountInfo.getBalance(kQsrCoin.tokenStandard), + MathUtils.bigMax( + BigInt.zero, _qsrCost - (depositedQsr - _withdrawnQSR))); return Column( children: [ @@ -164,8 +161,11 @@ class _MainSentinelsState extends State { PieChartSectionData( showTitle: false, radius: 7.0, - value: _qsrCost - - (depositedQsr - _withdrawnQSR) / _qsrCost, + value: (_qsrCost + .addDecimals(coinDecimals) + .toNum() - + (depositedQsr - _withdrawnQSR) / + _qsrCost), color: AppColors.qsrColor.withOpacity(0.3), ), PieChartSectionData( @@ -178,7 +178,7 @@ class _MainSentinelsState extends State { ), ), Text( - 'Sentinel Slot value\n$_qsrCost ${kQsrCoin.symbol}', + 'Sentinel Slot value\n${_qsrCost.addDecimals(coinDecimals)} ${kQsrCoin.symbol}', style: Theme.of(context).textTheme.bodyMedium, textAlign: TextAlign.center, ), @@ -238,7 +238,7 @@ class _MainSentinelsState extends State { SizedBox( width: 130.0, child: Text( - 'You have deposited $depositedQsr ${kQsrCoin.symbol}', + 'You have deposited ${depositedQsr.addDecimals(coinDecimals)} ${kQsrCoin.symbol}', textAlign: TextAlign.center, style: Theme.of(context).textTheme.bodyLarge, ), @@ -328,11 +328,12 @@ class _MainSentinelsState extends State { value, _maxQsrAmount, kQsrCoin.decimals, - min: _maxQsrAmount!, + _maxQsrAmount, canBeEqualToMin: true, ); - Widget _getDepositButtonViewModel(AccountInfo accountInfo, num depositedQsr) { + Widget _getDepositButtonViewModel( + AccountInfo accountInfo, BigInt depositedQsr) { return ViewModelBuilder.reactive( onViewModelReady: (model) { model.stream.listen( @@ -365,7 +366,7 @@ class _MainSentinelsState extends State { Widget _getDepositButton( SentinelsDepositQsrBloc model, AccountInfo accountInfo, - num depositedQsr, + BigInt depositedQsr, ) { return LoadingButton.stepper( key: _depositQsrButtonKey, @@ -373,10 +374,11 @@ class _MainSentinelsState extends State { onPressed: _qsrAmountValidator(_qsrAmountController.text) == null ? () => _onDepositButtonPressed(model, depositedQsr) : null, + outlineColor: AppColors.qsrColor, ); } - Widget _getWithdrawQsrButtonViewModel(num qsrDeposit) { + Widget _getWithdrawQsrButtonViewModel(BigInt qsrDeposit) { return ViewModelBuilder.reactive( onViewModelReady: (model) { model.stream.listen( @@ -414,15 +416,16 @@ class _MainSentinelsState extends State { } Widget _getWithdrawQsrButton( - num qsrDeposit, + BigInt qsrDeposit, SentinelsWithdrawQsrBloc model, ) { return Visibility( - visible: qsrDeposit > 0, + visible: qsrDeposit > BigInt.zero, child: LoadingButton.stepper( text: 'Withdraw', - onPressed: () => _onWithdrawButtonPressed(model, qsrDeposit.toDouble()), + onPressed: () => _onWithdrawButtonPressed(model, qsrDeposit), key: _withdrawButtonKey, + outlineColor: AppColors.qsrColor, ), ); } @@ -439,7 +442,7 @@ class _MainSentinelsState extends State { onStepTapped: (int index) {}, steps: [ StepperUtils.getMaterialStep( - stepTitle: 'Plasma check', + stepTitle: 'Sentinel deployment: Plasma check', stepContent: _getPlasmaCheckFutureBuilder(), stepSubtitle: 'Sufficient Plasma', stepState: StepperUtils.getStepState( @@ -582,19 +585,21 @@ class _MainSentinelsState extends State { } void _onDepositButtonPressed( - SentinelsDepositQsrBloc model, num depositedQsr) { + SentinelsDepositQsrBloc model, BigInt depositedQsr) { if (_lastCompletedStep == SentinelsStepperStep.checkPlasma) { if (depositedQsr >= _qsrCost) { _depositQsrButtonKey.currentState?.animateForward(); model.depositQsr( - _qsrAmountController.text, + _qsrAmountController.text.extractDecimals(coinDecimals), justMarkStepCompleted: true, ); - } else if (_maxQsrAmount! + depositedQsr >= _qsrCost && + } else if (_maxQsrAmount + depositedQsr >= _qsrCost && _qsrFormKey.currentState!.validate() && - num.parse(_qsrAmountController.text) > 0) { + _qsrAmountController.text.extractDecimals(coinDecimals) > + BigInt.zero) { _depositQsrButtonKey.currentState?.animateForward(); - model.depositQsr(_qsrAmountController.text); + model.depositQsr( + _qsrAmountController.text.extractDecimals(coinDecimals)); } } else if (_lastCompletedStep == SentinelsStepperStep.qsrManagement) { setState(() { @@ -620,15 +625,16 @@ class _MainSentinelsState extends State { void _onDeployPressed(SentinelsDeployBloc model) { if (_lastCompletedStep == SentinelsStepperStep.znnManagement) { _registerButtonKey.currentState?.animateForward(); - model.deploySentinel(_znnAmountController.text); + model.deploySentinel( + _znnAmountController.text.extractDecimals(coinDecimals)); } } void _onWithdrawButtonPressed( SentinelsWithdrawQsrBloc viewModel, - num qsrDeposit, + BigInt qsrDeposit, ) { - if (qsrDeposit > 0) { + if (qsrDeposit > BigInt.zero) { _withdrawButtonKey.currentState?.animateForward(); viewModel.withdrawQsr(_addressController.text); } @@ -696,8 +702,7 @@ class _MainSentinelsState extends State { ), recognizer: TapGestureRecognizer() ..onTap = () { - NavigationUtils.openUrl( - kZnnController, context); + NavigationUtils.openUrl(kZnnController); }, ), const WidgetSpan( @@ -818,6 +823,13 @@ class _MainSentinelsState extends State { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ + Text( + 'More Plasma is required to perform complex transactions. Please fuse enough QSR before proceeding.', + style: Theme.of(context).textTheme.headlineSmall, + ), + const SizedBox( + height: 25.0, + ), Row( mainAxisAlignment: MainAxisAlignment.start, children: [ diff --git a/lib/widgets/modular_widgets/settings_widgets/account_chain_stats.dart b/lib/widgets/modular_widgets/settings_widgets/account_chain_stats.dart index bcbb368a..3ca4fdcf 100644 --- a/lib/widgets/modular_widgets/settings_widgets/account_chain_stats.dart +++ b/lib/widgets/modular_widgets/settings_widgets/account_chain_stats.dart @@ -70,7 +70,6 @@ class _AccountChainStatsState extends State { materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, onPressed: () => NavigationUtils.openUrl( '$kExplorer/transaction/${stats.firstHash}', - context, ), child: Row( mainAxisAlignment: MainAxisAlignment.center, diff --git a/lib/widgets/modular_widgets/settings_widgets/addresses.dart b/lib/widgets/modular_widgets/settings_widgets/addresses.dart index 4852249d..dfbb91ad 100644 --- a/lib/widgets/modular_widgets/settings_widgets/addresses.dart +++ b/lib/widgets/modular_widgets/settings_widgets/addresses.dart @@ -106,13 +106,14 @@ class AddressesState extends State { InkWell( onTap: () { setState(() { - _futureGenerateNewAddress = AddressUtils.generateNewAddress( - numAddr: numberOfAddressesToAdd, - callback: () { - setState(() { - _shouldScrollToTheEnd = true; - }); - }); + _futureGenerateNewAddress = + ZenonAddressUtils.generateNewAddress( + numAddr: numberOfAddressesToAdd, + callback: () { + setState(() { + _shouldScrollToTheEnd = true; + }); + }); }); }, child: Container( diff --git a/lib/widgets/modular_widgets/settings_widgets/general.dart b/lib/widgets/modular_widgets/settings_widgets/general.dart index ca30dcdc..3f3cd382 100644 --- a/lib/widgets/modular_widgets/settings_widgets/general.dart +++ b/lib/widgets/modular_widgets/settings_widgets/general.dart @@ -150,7 +150,6 @@ class GeneralWidgetState extends State { materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, onPressed: () => NavigationUtils.openUrl( '$kExplorer/momentum/${generalStats.frontierMomentum.hash}', - context, ), child: Row( mainAxisAlignment: MainAxisAlignment.center, diff --git a/lib/widgets/modular_widgets/settings_widgets/node_management.dart b/lib/widgets/modular_widgets/settings_widgets/node_management.dart index a9349c7a..07bbc1d7 100644 --- a/lib/widgets/modular_widgets/settings_widgets/node_management.dart +++ b/lib/widgets/modular_widgets/settings_widgets/node_management.dart @@ -1,15 +1,15 @@ -import 'dart:io'; import 'dart:isolate'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_vector_icons/flutter_vector_icons.dart'; import 'package:hive/hive.dart'; -import 'package:wakelock/wakelock.dart'; +import 'package:wakelock_plus/wakelock_plus.dart'; import 'package:zenon_syrius_wallet_flutter/blocs/blocs.dart'; import 'package:zenon_syrius_wallet_flutter/embedded_node/embedded_node.dart'; import 'package:zenon_syrius_wallet_flutter/main.dart'; import 'package:zenon_syrius_wallet_flutter/model/model.dart'; +import 'package:zenon_syrius_wallet_flutter/services/wallet_connect_service.dart'; import 'package:zenon_syrius_wallet_flutter/utils/utils.dart'; import 'package:zenon_syrius_wallet_flutter/widgets/widgets.dart'; import 'package:znn_sdk_dart/znn_sdk_dart.dart'; @@ -115,8 +115,8 @@ class _NodeManagementState extends State { Future _onConfirmNodeButtonPressed() async { // Acquire WakeLock - if (!Platform.isLinux && !await Wakelock.enabled) { - Wakelock.enable(); + if (!await WakelockPlus.enabled) { + WakelockPlus.enable(); } try { @@ -368,6 +368,7 @@ class _NodeManagementState extends State { _confirmChainIdButtonKey.currentState?.animateForward(); setChainIdentifier(chainIdentifier: _newChainId); await sharedPrefsService!.put(kChainIdKey, _newChainId); + sl().emitChainIdChangeEvent(_newChainId.toString()); _sendSuccessfullyChangedChainIdNotification(_newChainId); _initCurrentChainId(); _newChainIdController = TextEditingController(); diff --git a/lib/widgets/modular_widgets/settings_widgets/security.dart b/lib/widgets/modular_widgets/settings_widgets/security.dart index 5c36b995..50071648 100644 --- a/lib/widgets/modular_widgets/settings_widgets/security.dart +++ b/lib/widgets/modular_widgets/settings_widgets/security.dart @@ -10,6 +10,7 @@ import 'package:zenon_syrius_wallet_flutter/utils/app_colors.dart'; import 'package:zenon_syrius_wallet_flutter/utils/clipboard_utils.dart'; import 'package:zenon_syrius_wallet_flutter/utils/constants.dart'; import 'package:zenon_syrius_wallet_flutter/utils/format_utils.dart'; +import 'package:zenon_syrius_wallet_flutter/utils/functions.dart'; import 'package:zenon_syrius_wallet_flutter/utils/global.dart'; import 'package:zenon_syrius_wallet_flutter/utils/navigation_utils.dart'; import 'package:zenon_syrius_wallet_flutter/utils/notification_utils.dart'; @@ -329,13 +330,11 @@ class _SecurityWidgetState extends State { Future _onSignButtonPressed() async { try { _signButtonKey.currentState?.animateForward(); - List signature = await zenon!.defaultKeyPair!.sign( - Uint8List.fromList( - _textToBeSignedController.text.codeUnits, - ), + final signedMessage = await walletSign( + _textToBeSignedController.text.codeUnits, ); setState(() { - _signedTextController.text = BytesUtils.bytesToHex(signature); + _signedTextController.text = signedMessage; }); } catch (e) { NotificationUtils.sendNotificationError(e, 'Error while signing message'); @@ -569,11 +568,11 @@ class _SecurityWidgetState extends State { File droppedFile = File( _toBeSignedFilePath!, ); - List fileSignature = await zenon!.defaultKeyPair!.sign(Crypto.digest( + final fileSignature = await walletSign(Crypto.digest( await droppedFile.readAsBytes(), )); setState(() { - _fileHashController.text = BytesUtils.bytesToHex(fileSignature); + _fileHashController.text = fileSignature; _toBeSignedFilePath = null; _signSelectFileWidgetKey.currentState!.resetMessageToUser(); }); diff --git a/lib/widgets/modular_widgets/settings_widgets/wallet_options.dart b/lib/widgets/modular_widgets/settings_widgets/wallet_options.dart index bef058cd..86284f9d 100644 --- a/lib/widgets/modular_widgets/settings_widgets/wallet_options.dart +++ b/lib/widgets/modular_widgets/settings_widgets/wallet_options.dart @@ -7,6 +7,7 @@ import 'package:zenon_syrius_wallet_flutter/blocs/blocs.dart'; import 'package:zenon_syrius_wallet_flutter/main.dart'; import 'package:zenon_syrius_wallet_flutter/model/model.dart'; import 'package:zenon_syrius_wallet_flutter/screens/screens.dart'; +import 'package:zenon_syrius_wallet_flutter/utils/clipboard_utils.dart'; import 'package:zenon_syrius_wallet_flutter/utils/constants.dart'; import 'package:zenon_syrius_wallet_flutter/utils/navigation_utils.dart'; import 'package:zenon_syrius_wallet_flutter/utils/notification_utils.dart'; @@ -24,6 +25,7 @@ class WalletOptions extends StatefulWidget { class _WalletOptionsState extends State { bool? _launchAtStartup; bool? _enableDesktopNotifications; + bool? _enabledClipboardWatcher; @override void initState() { @@ -36,6 +38,10 @@ class _WalletOptionsState extends State { kEnableDesktopNotificationsKey, defaultValue: kEnableDesktopNotificationsDefaultValue, ); + _enabledClipboardWatcher = sharedPrefsService!.get( + kEnableClipboardWatcherKey, + defaultValue: kEnableClipboardWatcherDefaultValue, + ); } @override @@ -112,6 +118,7 @@ class _WalletOptionsState extends State { children: [ _getLaunchAtStartupWidget(), _getEnableDesktopNotifications(), + _buildEnableClipboardWatcher(), ], ); } @@ -203,6 +210,32 @@ class _WalletOptionsState extends State { ); } + Widget _buildEnableClipboardWatcher() { + return Row( + children: [ + Text( + 'Enable clipboard watcher', + style: Theme.of(context).textTheme.bodyMedium, + ), + SyriusCheckbox( + onChanged: (value) { + setState(() { + _enabledClipboardWatcher = value; + _changeEnableClipboardWatcherStatus(value ?? false); + }); + }, + value: _enabledClipboardWatcher, + context: context, + ), + const StandardTooltipIcon( + 'Listens to the values passed to the clipboard and sends a ' + 'notification when a WalletConnect URI has been copied', + Icons.help, + ), + ], + ); + } + Future _changeEnableDesktopNotificationsStatus(bool enabled) async { try { await sharedPrefsService!.put(kEnableDesktopNotificationsKey, enabled); @@ -215,6 +248,19 @@ class _WalletOptionsState extends State { } } + Future _changeEnableClipboardWatcherStatus(bool enabled) async { + try { + await sharedPrefsService!.put(kEnableClipboardWatcherKey, enabled); + ClipboardUtils.toggleClipboardWatcherStatus(); + _sendEnableClipboardWatcherStatusNotification(enabled); + } on Exception catch (e) { + NotificationUtils.sendNotificationError( + e, + 'Something went wrong while changing clipboard watcher preference', + ); + } + } + void _sendEnabledDesktopNotificationsStatusNotification(bool enabled) { sl.get().addNotification( WalletNotification( @@ -226,4 +272,16 @@ class _WalletOptionsState extends State { ), ); } + + void _sendEnableClipboardWatcherStatusNotification(bool enabled) { + sl.get().addNotification( + WalletNotification( + title: 'Clipboard watcher ${enabled ? 'enabled' : 'disabled'}', + details: + 'Clipboard watcher preference was ${enabled ? 'enabled' : 'disabled'}', + timestamp: DateTime.now().millisecondsSinceEpoch, + type: NotificationType.paymentSent, + ), + ); + } } diff --git a/lib/widgets/modular_widgets/staking_widgets/stake_collect.dart b/lib/widgets/modular_widgets/staking_widgets/stake_collect.dart index 0e7da400..b77c895b 100644 --- a/lib/widgets/modular_widgets/staking_widgets/stake_collect.dart +++ b/lib/widgets/modular_widgets/staking_widgets/stake_collect.dart @@ -49,7 +49,7 @@ class _StakeCollectState extends State { if (snapshot.hasError) { return SyriusErrorWidget(snapshot.error!); } else if (snapshot.hasData) { - if (snapshot.data!.qsrAmount > 0) { + if (snapshot.data!.qsrAmount > BigInt.zero) { return _getWidgetBody(snapshot.data!); } return const SyriusErrorWidget('No rewards to collect'); @@ -64,7 +64,7 @@ class _StakeCollectState extends State { mainAxisAlignment: MainAxisAlignment.center, children: [ NumberAnimation( - end: uncollectedReward.qsrAmount.addDecimals(qsrDecimals), + end: uncollectedReward.qsrAmount.addDecimals(coinDecimals).toNum(), isInt: false, after: ' ${kQsrCoin.symbol}', style: Theme.of(context).textTheme.headlineLarge!.copyWith( @@ -74,13 +74,14 @@ class _StakeCollectState extends State { ), kVerticalSpacing, Visibility( - visible: uncollectedReward.qsrAmount > 0, + visible: uncollectedReward.qsrAmount > BigInt.zero, child: LoadingButton.stepper( key: _collectButtonKey, text: 'Collect', outlineColor: AppColors.qsrColor, - onPressed: - uncollectedReward.qsrAmount > 0 ? _onCollectPressed : null, + onPressed: uncollectedReward.qsrAmount > BigInt.zero + ? _onCollectPressed + : null, ), ), ], diff --git a/lib/widgets/modular_widgets/staking_widgets/staking_options/staking_options.dart b/lib/widgets/modular_widgets/staking_widgets/staking_options/staking_options.dart index 857c83ab..3b052030 100644 --- a/lib/widgets/modular_widgets/staking_widgets/staking_options/staking_options.dart +++ b/lib/widgets/modular_widgets/staking_widgets/staking_options/staking_options.dart @@ -37,7 +37,7 @@ class _StakingOptionsState extends State { seconds: (index + 1) * stakeTimeUnitSec, )); - num? _maxZnnAmount; + BigInt _maxZnnAmount = BigInt.zero; double? _maxWidth; @@ -74,8 +74,8 @@ class _StakingOptionsState extends State { } if (snapshot.connectionState == ConnectionState.active) { if (snapshot.hasData) { - _maxZnnAmount = snapshot.data![_addressController.text]! - .getBalanceWithDecimals( + _maxZnnAmount = + snapshot.data![_addressController.text]!.getBalance( kZnnCoin.tokenStandard, ); return _getWidgetBody( @@ -129,9 +129,7 @@ class _StakingOptionsState extends State { value, _maxZnnAmount, kZnnCoin.decimals, - min: stakeMinZnnAmount.addDecimals( - znnDecimals, - ), + stakeMinZnnAmount, canBeEqualToMin: true, ), suffixIcon: _getZnnAmountSuffix(), @@ -248,12 +246,12 @@ class _StakingOptionsState extends State { void _onStakePressed(StakingOptionsBloc? model) { if (_selectedStakeDuration != null && _znnAmountKey.currentState!.validate() && - _znnAmountController.text.toNum() >= - stakeMinZnnAmount.addDecimals(znnDecimals)) { + _znnAmountController.text.extractDecimals(coinDecimals) >= + stakeMinZnnAmount) { _stakeButtonKey.currentState?.animateForward(); model!.stakeForQsr( _selectedStakeDuration!, - _znnAmountController.text, + _znnAmountController.text.extractDecimals(coinDecimals), ); } } @@ -267,7 +265,7 @@ class _StakingOptionsState extends State { void _onMaxPressed() { setState(() { - _znnAmountController.text = _maxZnnAmount.toString(); + _znnAmountController.text = _maxZnnAmount.addDecimals(coinDecimals); }); } @@ -277,9 +275,7 @@ class _StakingOptionsState extends State { _znnAmountController.text, _maxZnnAmount, kZnnCoin.decimals, - min: stakeMinZnnAmount.addDecimals( - znnDecimals, - ), + stakeMinZnnAmount, canBeEqualToMin: true, ) == null; diff --git a/lib/widgets/modular_widgets/token_widgets/token_balance.dart b/lib/widgets/modular_widgets/token_widgets/token_balance.dart index 953f947d..b5c7f1b0 100644 --- a/lib/widgets/modular_widgets/token_widgets/token_balance.dart +++ b/lib/widgets/modular_widgets/token_widgets/token_balance.dart @@ -1,10 +1,8 @@ import 'package:flutter/material.dart'; +import 'package:marquee_widget/marquee_widget.dart'; import 'package:zenon_syrius_wallet_flutter/blocs/blocs.dart'; import 'package:zenon_syrius_wallet_flutter/main.dart'; -import 'package:zenon_syrius_wallet_flutter/utils/color_utils.dart'; -import 'package:zenon_syrius_wallet_flutter/utils/constants.dart'; -import 'package:zenon_syrius_wallet_flutter/utils/global.dart'; -import 'package:zenon_syrius_wallet_flutter/utils/zts_utils.dart'; +import 'package:zenon_syrius_wallet_flutter/utils/utils.dart'; import 'package:zenon_syrius_wallet_flutter/widgets/widgets.dart'; import 'package:znn_sdk_dart/znn_sdk_dart.dart'; @@ -102,21 +100,28 @@ class _TokenBalanceState extends State { crossAxisSpacing: 10.0, mainAxisSpacing: 10.0, ), - itemBuilder: (context, index) => FormattedAmountWithTooltip( - amount: _newTokenIds[index].balanceWithDecimals!, - tokenSymbol: _newTokenIds[index].token!.symbol, - builder: (amount, symbol) => Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text( - '● ', - style: Theme.of(context).textTheme.bodyLarge!.copyWith( - color: ColorUtils.getTokenColor( - _newTokenIds[index].token!.tokenStandard), - ), + itemBuilder: (context, index) => Padding( + padding: const EdgeInsets.all(8.0), + child: Marquee( + child: FormattedAmountWithTooltip( + amount: _newTokenIds[index] + .balance! + .addDecimals(_newTokenIds[index].token!.decimals), + tokenSymbol: _newTokenIds[index].token!.symbol, + builder: (amount, symbol) => Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + '● ', + style: Theme.of(context).textTheme.bodyLarge!.copyWith( + color: ColorUtils.getTokenColor( + _newTokenIds[index].token!.tokenStandard), + ), + ), + _getTokenStatus(amount, symbol) + ], ), - _getTokenStatus(amount, symbol) - ], + ), ), ), ); diff --git a/lib/widgets/modular_widgets/token_widgets/token_card.dart b/lib/widgets/modular_widgets/token_widgets/token_card.dart index fbfa688c..04f8dd3e 100644 --- a/lib/widgets/modular_widgets/token_widgets/token_card.dart +++ b/lib/widgets/modular_widgets/token_widgets/token_card.dart @@ -1,9 +1,8 @@ -import 'dart:math' show pow; - import 'package:fl_chart/fl_chart.dart'; import 'package:flip_card/flip_card.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; +import 'package:marquee_widget/marquee_widget.dart'; import 'package:stacked/stacked.dart'; import 'package:zenon_syrius_wallet_flutter/blocs/blocs.dart'; import 'package:zenon_syrius_wallet_flutter/main.dart'; @@ -53,8 +52,8 @@ class _TokenCardState extends State { final TextEditingController _mintAmountController = TextEditingController(); TextEditingController _newOwnerAddressController = TextEditingController(); - num? _burnMaxAmount; - num? _mintMaxAmount; + BigInt _burnMaxAmount = BigInt.zero; + BigInt _mintMaxAmount = BigInt.zero; final GlobalKey _burnButtonKey = GlobalKey(); final GlobalKey _mintButtonKey = GlobalKey(); @@ -147,9 +146,13 @@ class _TokenCardState extends State { Row( children: [ Expanded( - child: Text( - widget.token.tokenStandard.toString().toUpperCase(), - style: Theme.of(context).textTheme.titleMedium, + child: Marquee( + child: Text( + widget.token.tokenStandard + .toString() + .toUpperCase(), + style: Theme.of(context).textTheme.titleMedium, + ), ), ), CopyToClipboardIcon( @@ -261,8 +264,7 @@ class _TokenCardState extends State { ), materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, shape: const CircleBorder(), - onPressed: () => - NavigationUtils.openUrl(widget.token.domain, context), + onPressed: () => NavigationUtils.openUrl(widget.token.domain), child: Tooltip( message: 'Visit ${widget.token.domain}', child: Container( @@ -324,10 +326,9 @@ class _TokenCardState extends State { } Widget _getAnimatedChart(Token token) { - double totalSupplyWithDecimals = - token.totalSupply / pow(10, token.decimals); + BigInt totalSupply = token.totalSupply; - double maxSupplyWithDecimals = token.maxSupply / pow(10, token.decimals); + BigInt maxSupply = token.maxSupply; return Stack( alignment: Alignment.center, @@ -340,14 +341,13 @@ class _TokenCardState extends State { PieChartSectionData( showTitle: false, radius: 5.0, - value: totalSupplyWithDecimals / maxSupplyWithDecimals, + value: totalSupply / maxSupply, color: ColorUtils.getTokenColor(widget.token.tokenStandard), ), PieChartSectionData( showTitle: false, radius: 5.0, - value: (maxSupplyWithDecimals - totalSupplyWithDecimals) / - maxSupplyWithDecimals, + value: (maxSupply - totalSupply) / maxSupply, color: Colors.white12, ), ], @@ -355,13 +355,15 @@ class _TokenCardState extends State { ), SizedBox( width: 70.0, - child: FormattedAmountWithTooltip( - amount: totalSupplyWithDecimals, - tokenSymbol: token.symbol, - builder: (formattedAmount, tokenSymbol) => Text( - '$formattedAmount $tokenSymbol', - style: Theme.of(context).textTheme.bodyMedium, - textAlign: TextAlign.center, + child: Marquee( + child: FormattedAmountWithTooltip( + amount: totalSupply.addDecimals(token.decimals), + tokenSymbol: token.symbol, + builder: (formattedAmount, tokenSymbol) => Text( + '$formattedAmount $tokenSymbol', + style: Theme.of(context).textTheme.bodyMedium, + textAlign: TextAlign.center, + ), ), ), ), @@ -370,7 +372,7 @@ class _TokenCardState extends State { } Widget _getBurnBackOfCard(AccountInfo accountInfo) { - _burnMaxAmount = accountInfo.getBalanceWithDecimals( + _burnMaxAmount = accountInfo.getBalance( widget.token.tokenStandard, ); @@ -389,10 +391,7 @@ class _TokenCardState extends State { ), controller: _burnAmountController, validator: (value) => InputValidators.correctValue( - value, - _burnMaxAmount, - widget.token.decimals, - ), + value, _burnMaxAmount, widget.token.decimals, BigInt.zero), suffixIcon: _getAmountSuffix(), suffixIconConstraints: const BoxConstraints(maxWidth: 50.0), hintText: 'Amount', @@ -470,17 +469,17 @@ class _TokenCardState extends State { Widget _getBurnButton(BurnTokenBloc model) { return LoadingButton.stepper( text: 'Burn', - onPressed: _burnMaxAmount! > 0 && + onPressed: _burnMaxAmount > BigInt.zero && _burnAmountController.text.isNotEmpty && - InputValidators.correctValue( - _burnAmountController.text, - _burnMaxAmount, - widget.token.decimals, - ) == + InputValidators.correctValue(_burnAmountController.text, + _burnMaxAmount, widget.token.decimals, BigInt.zero) == null ? () { _burnButtonKey.currentState?.animateForward(); - model.burnToken(widget.token, _burnAmountController.text); + model.burnToken( + widget.token, + _burnAmountController.text + .extractDecimals(widget.token.decimals)); } : null, key: _burnButtonKey, @@ -500,23 +499,24 @@ class _TokenCardState extends State { void _onMaxPressed() { if (_burnAmountController.text.isEmpty || - _burnAmountController.text.toNum() != _burnMaxAmount || - _burnAmountController.text.toNum() != _mintMaxAmount) { + _burnAmountController.text.extractDecimals(widget.token.decimals) != + _burnMaxAmount || + _burnAmountController.text.extractDecimals(widget.token.decimals) != + _mintMaxAmount) { setState(() { if (_backOfCardVersion == TokenCardBackVersion.burn) { - _burnAmountController.text = _burnMaxAmount.toString(); + _burnAmountController.text = + _burnMaxAmount.addDecimals(widget.token.decimals); } else { - _mintAmountController.text = _mintMaxAmount.toString(); + _mintAmountController.text = + _mintMaxAmount.addDecimals(widget.token.decimals); } }); } } Widget _getMintBackOfCard(AccountInfo? accountInfo) { - _mintMaxAmount = - (widget.token.maxSupply - widget.token.totalSupply).addDecimals( - widget.token.decimals, - ); + _mintMaxAmount = (widget.token.maxSupply - widget.token.totalSupply); return ListView( shrinkWrap: true, @@ -550,10 +550,7 @@ class _TokenCardState extends State { ), controller: _mintAmountController, validator: (value) => InputValidators.correctValue( - value, - _mintMaxAmount, - widget.token.decimals, - ), + value, _mintMaxAmount, widget.token.decimals, BigInt.zero), suffixIcon: _getAmountSuffix(), suffixIconConstraints: const BoxConstraints(maxWidth: 50.0), hintText: 'Amount', @@ -630,19 +627,17 @@ class _TokenCardState extends State { Widget _getMintButton(MintTokenBloc model) { return LoadingButton.stepper( text: 'Mint', - onPressed: _mintMaxAmount! > 0 && + onPressed: _mintMaxAmount > BigInt.zero && _mintAmountController.text.isNotEmpty && - InputValidators.correctValue( - _mintAmountController.text, - _mintMaxAmount, - widget.token.decimals, - ) == + InputValidators.correctValue(_mintAmountController.text, + _mintMaxAmount, widget.token.decimals, BigInt.zero) == null ? () { _mintButtonKey.currentState!.animateForward(); model.mintToken( widget.token, - _mintAmountController.text, + _mintAmountController.text + .extractDecimals(widget.token.decimals), Address.parse(_beneficiaryAddressController.text), ); } diff --git a/lib/widgets/modular_widgets/token_widgets/token_stepper.dart b/lib/widgets/modular_widgets/token_widgets/token_stepper.dart index f1d2a000..5a1ad753 100644 --- a/lib/widgets/modular_widgets/token_widgets/token_stepper.dart +++ b/lib/widgets/modular_widgets/token_widgets/token_stepper.dart @@ -15,9 +15,9 @@ import 'package:zenon_syrius_wallet_flutter/utils/global.dart'; import 'package:zenon_syrius_wallet_flutter/utils/input_validators.dart'; import 'package:zenon_syrius_wallet_flutter/utils/notification_utils.dart'; import 'package:zenon_syrius_wallet_flutter/utils/zts_utils.dart'; -import 'package:zenon_syrius_wallet_flutter/widgets/widgets.dart'; import 'package:zenon_syrius_wallet_flutter/widgets/reusable_widgets/custom_material_stepper.dart' as custom_material_stepper; +import 'package:zenon_syrius_wallet_flutter/widgets/widgets.dart'; import 'package:znn_sdk_dart/znn_sdk_dart.dart'; enum TokenStepperStep { @@ -58,11 +58,11 @@ class _TokenStepperState extends State { GlobalKey _tokenDomainKey = GlobalKey(); final GlobalKey _createButtonKey = GlobalKey(); - double _selectedNumDecimals = 0; + int _selectedNumDecimals = 0; - bool? _isMintable = false; - bool? _isBurnable = false; - bool? _isUtility = true; + bool _isMintable = false; + bool _isBurnable = false; + bool _isUtility = true; late List _focusNodes; @@ -199,7 +199,7 @@ class _TokenStepperState extends State { onStepTapped: (int index) {}, steps: [ StepperUtils.getMaterialStep( - stepTitle: 'Plasma check', + stepTitle: 'Token creation: Plasma check', stepContent: _getPlasmaCheckFutureBuilder(), stepSubtitle: 'Sufficient Plasma', stepState: StepperUtils.getStepState( @@ -235,8 +235,8 @@ class _TokenStepperState extends State { StepperUtils.getMaterialStep( stepTitle: 'Token mintable and burnable options', stepContent: _getTokenMintableAndBurnableStepContent(), - stepSubtitle: 'Mintable: ${_isMintable! ? 'yes' : 'no'}\n' - 'Burnable: ${_isBurnable! ? 'yes' : 'no'}', + stepSubtitle: 'Mintable: ${_isMintable ? 'yes' : 'no'}\n' + 'Burnable: ${_isBurnable ? 'yes' : 'no'}', stepState: StepperUtils.getStepState( TokenStepperStep.tokenMintableBurnable.index, _lastCompletedStep?.index, @@ -260,10 +260,10 @@ class _TokenStepperState extends State { StepperUtils.getMaterialStep( stepTitle: 'Issue Token', stepContent: _getIssueTokenStepContent(context), - stepSubtitle: _isMintable! + stepSubtitle: _isMintable ? '${_totalSupplyController.text} out of ' '${_maxSupplyController.text} ${_tokenSymbolController.text}' - : _isUtility! + : _isUtility ? 'Utility Token' : '', stepState: StepperUtils.getStepState( @@ -296,6 +296,13 @@ class _TokenStepperState extends State { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ + Text( + 'More Plasma is required to perform complex transactions. Please fuse enough QSR before proceeding.', + style: Theme.of(context).textTheme.headlineSmall, + ), + const SizedBox( + height: 25.0, + ), Row( mainAxisAlignment: MainAxisAlignment.start, children: [ @@ -348,7 +355,7 @@ class _TokenStepperState extends State { value: _isUtility, onChanged: (value) { setState(() { - _isUtility = value; + _isUtility = value!; }); }, ), @@ -373,7 +380,7 @@ class _TokenStepperState extends State { child: DottedBorderInfoWidget( text: 'You will need to burn ' '${tokenZtsIssueFeeInZnn.addDecimals( - znnDecimals, + coinDecimals, )} ${kZnnCoin.symbol} ' 'to issue a token', borderColor: AppColors.ztsColor, @@ -427,16 +434,16 @@ class _TokenStepperState extends State { description: 'Number of decimals: ${_selectedNumDecimals.toInt()}', startValue: 0.0, min: 0.0, - maxValue: 16.0, + maxValue: 18.0, callback: (double value) { setState(() { - _selectedNumDecimals = value; + _selectedNumDecimals = value.toInt(); }); }, ), ), Visibility( - visible: _isMintable!, + visible: _isMintable, child: Container( margin: const EdgeInsets.only(bottom: 15.0), child: Row( @@ -455,14 +462,12 @@ class _TokenStepperState extends State { }, controller: _maxSupplyController, hintText: 'Max supply', - validator: _isMintable! + validator: _isMintable ? (String? value) => InputValidators.correctValue( value, - kMaxInt.addDecimals( - _selectedNumDecimals.toInt(), - ), + kBigP255m1, _selectedNumDecimals.toInt(), - min: kMinTokenTotalMaxSupply, + kMinTokenTotalMaxSupply, canBeEqualToMin: true, ) : (String? value) => @@ -492,17 +497,14 @@ class _TokenStepperState extends State { hintText: 'Total supply', validator: (value) => InputValidators.correctValue( value, - _isMintable! - ? double.parse(_maxSupplyController.text.isNotEmpty + _isMintable + ? _maxSupplyController.text.isNotEmpty ? _maxSupplyController.text - : '${kMaxInt.addDecimals( - _selectedNumDecimals.toInt(), - )}') - : kMaxInt.addDecimals( - _selectedNumDecimals.toInt(), - ), + .extractDecimals(_selectedNumDecimals) + : kBigP255m1 + : kBigP255m1, _selectedNumDecimals.toInt(), - min: _isMintable! ? 0 : kMinTokenTotalMaxSupply, + _isMintable ? BigInt.zero : kMinTokenTotalMaxSupply, canBeEqualToMin: true, ), ), @@ -650,7 +652,7 @@ class _TokenStepperState extends State { DottedBorderInfoWidget( text: 'You will need to burn ' '${tokenZtsIssueFeeInZnn.addDecimals( - znnDecimals, + coinDecimals, )} ${kZnnCoin.symbol} ' 'to issue a token', borderColor: AppColors.ztsColor, @@ -694,15 +696,15 @@ class _TokenStepperState extends State { } void _onTokenMetricsContinuePressed() { - if ((!_isMintable! || _maxSupplyKey.currentState!.validate()) && + if ((!_isMintable || _maxSupplyKey.currentState!.validate()) && _totalSupplyKey.currentState!.validate()) { _tokenStepperData.decimals = _selectedNumDecimals.toInt(); - _tokenStepperData.totalSupply = _totalSupplyController.text.toNum(); + _tokenStepperData.totalSupply = + _totalSupplyController.text.extractDecimals(_selectedNumDecimals); _tokenStepperData.isMintable = _isMintable; - _tokenStepperData.maxSupply = (_isMintable! - ? _maxSupplyController.text - : _totalSupplyController.text) - .toNum(); + _tokenStepperData.maxSupply = (_isMintable + ? _maxSupplyController.text.extractDecimals(_selectedNumDecimals) + : _totalSupplyController.text.extractDecimals(_selectedNumDecimals)); _tokenStepperData.isOwnerBurnOnly = _isBurnable; _saveProgressAndNavigateToNextStep(TokenStepperStep.tokenMetrics); } @@ -733,12 +735,10 @@ class _TokenStepperState extends State { Widget _getTokenCreationContinueButton(AccountInfo accountInfo) { return StepperButton( text: 'Continue', - onPressed: accountInfo.getBalanceWithDecimals( + onPressed: accountInfo.getBalance( kZnnCoin.tokenStandard, ) >= - tokenZtsIssueFeeInZnn.addDecimals( - znnDecimals, - ) + tokenZtsIssueFeeInZnn ? _onTokenCreationContinuePressed : null, ); @@ -787,27 +787,24 @@ class _TokenStepperState extends State { } bool _areTokenMetricsCorrect() => - (_isMintable! + (_isMintable ? InputValidators.correctValue( _maxSupplyController.text, - kMaxInt.addDecimals( - _selectedNumDecimals.toInt(), - ), + kBigP255m1, _selectedNumDecimals.toInt(), - min: kMinTokenTotalMaxSupply, + kMinTokenTotalMaxSupply, canBeEqualToMin: true, ) == null : true) && InputValidators.correctValue( _totalSupplyController.text, - _isMintable! - ? _maxSupplyController.text.toNum() - : kMaxInt.addDecimals( - _selectedNumDecimals.toInt(), - ), + _isMintable + ? _maxSupplyController.text + .extractDecimals(_selectedNumDecimals.toInt()) + : kBigP255m1, _selectedNumDecimals.toInt(), - min: _isMintable! ? 0 : kMinTokenTotalMaxSupply, + _isMintable ? BigInt.zero : kMinTokenTotalMaxSupply, canBeEqualToMin: true, ) == null; @@ -871,7 +868,7 @@ class _TokenStepperState extends State { value: _isBurnable, onChanged: (value) { setState(() { - _isBurnable = value; + _isBurnable = value!; }); }, ), diff --git a/lib/widgets/modular_widgets/transfer_widgets/latest_transactions/latest_transactions_transfer_widget.dart b/lib/widgets/modular_widgets/transfer_widgets/latest_transactions/latest_transactions_transfer_widget.dart index 6c875ab4..30f40160 100644 --- a/lib/widgets/modular_widgets/transfer_widgets/latest_transactions/latest_transactions_transfer_widget.dart +++ b/lib/widgets/modular_widgets/transfer_widgets/latest_transactions/latest_transactions_transfer_widget.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_vector_icons/flutter_vector_icons.dart'; +import 'package:marquee_widget/marquee_widget.dart'; import 'package:zenon_syrius_wallet_flutter/blocs/blocs.dart'; import 'package:zenon_syrius_wallet_flutter/utils/app_colors.dart'; import 'package:zenon_syrius_wallet_flutter/utils/color_utils.dart'; @@ -86,20 +87,25 @@ class _LatestTransactionsState extends State { infoBlock.hash.toShortString(), flex: 2, ), - InfiniteScrollTableCell( - FormattedAmountWithTooltip( - amount: infoBlock.amount.addDecimals( - infoBlock.token?.decimals ?? 0, - ), - tokenSymbol: infoBlock.token?.symbol ?? '', - builder: (formattedAmount, tokenSymbol) => Text( - formattedAmount, - style: Theme.of(context).textTheme.titleMedium!.copyWith( - color: AppColors.subtitleColor, - ), + InfiniteScrollTableCell(Padding( + padding: const EdgeInsets.only(right: 10), + child: Marquee( + animationDuration: const Duration(milliseconds: 1000), + backDuration: const Duration(milliseconds: 1000), + child: FormattedAmountWithTooltip( + amount: infoBlock.amount.addDecimals( + infoBlock.token?.decimals ?? 0, + ), + tokenSymbol: infoBlock.token?.symbol ?? '', + builder: (formattedAmount, tokenSymbol) => Text( + formattedAmount, + style: Theme.of(context).textTheme.titleMedium!.copyWith( + color: AppColors.subtitleColor, + ), + ), ), ), - ), + )), InfiniteScrollTableCell.withText( context, infoBlock.confirmationDetail?.momentumTimestamp == null @@ -196,19 +202,14 @@ class _LatestTransactionsState extends State { } Widget _showTokenSymbol(AccountBlock block) { - return Text( - block.token?.symbol ?? '', - style: TextStyle( - fontSize: 12.0, - color: Colors.white, - fontWeight: FontWeight.w400, - background: Paint() - ..strokeWidth = 15.0 - ..color = ColorUtils.getTokenColor(block.tokenStandard) - ..style = PaintingStyle.stroke - ..strokeJoin = StrokeJoin.round, - ), - ); + return Transform( + transform: Matrix4.identity()..scale(0.8), + alignment: Alignment.bottomCenter, + child: Chip( + backgroundColor: ColorUtils.getTokenColor(block.tokenStandard), + label: Text(block.token?.symbol ?? ''), + side: BorderSide.none, + materialTapTargetSize: MaterialTapTargetSize.shrinkWrap)); } void _onSortArrowsPressed(String columnName) { @@ -346,16 +347,23 @@ class _LatestTransactionsState extends State { ? WidgetUtils.getMarqueeAddressTableCell(infoBlock.address, context) : WidgetUtils.getTextAddressTableCell(infoBlock.address, context), InfiniteScrollTableCell( - FormattedAmountWithTooltip( - amount: infoBlock.amount.addDecimals( - infoBlock.token?.decimals ?? 0, - ), - tokenSymbol: infoBlock.token?.symbol ?? '', - builder: (formattedAmount, tokenSymbol) => Text( - formattedAmount, - style: Theme.of(context).textTheme.titleMedium!.copyWith( - color: AppColors.subtitleColor, - ), + Padding( + padding: const EdgeInsets.only(right: 10), + child: Marquee( + animationDuration: const Duration(milliseconds: 1000), + backDuration: const Duration(milliseconds: 1000), + child: FormattedAmountWithTooltip( + amount: infoBlock.amount.addDecimals( + infoBlock.token?.decimals ?? 0, + ), + tokenSymbol: infoBlock.token?.symbol ?? '', + builder: (formattedAmount, tokenSymbol) => Text( + formattedAmount, + style: Theme.of(context).textTheme.titleMedium!.copyWith( + color: AppColors.subtitleColor, + ), + ), + ), ), ), ), diff --git a/lib/widgets/modular_widgets/transfer_widgets/receive/receive_large.dart b/lib/widgets/modular_widgets/transfer_widgets/receive/receive_large.dart index cfd09f9b..8dceacb5 100644 --- a/lib/widgets/modular_widgets/transfer_widgets/receive/receive_large.dart +++ b/lib/widgets/modular_widgets/transfer_widgets/receive/receive_large.dart @@ -180,16 +180,16 @@ class _ReceiveLargeCardState extends State { } String _getQrString() { - return '${_selectedToken.symbol.toLowerCase()}:$_selectedSelfAddress?tti=' + return '${_selectedToken.symbol.toLowerCase()}:$_selectedSelfAddress?zts=' '${_selectedToken.tokenStandard}' '&amount=${_getAmount()}'; } - num _getAmount() { + BigInt _getAmount() { try { - return _amountController.text.toNum(); + return _amountController.text.extractDecimals(_selectedToken.decimals); } catch (e) { - return 0; + return BigInt.zero; } } diff --git a/lib/widgets/modular_widgets/transfer_widgets/receive/receive_medium.dart b/lib/widgets/modular_widgets/transfer_widgets/receive/receive_medium.dart index b71bb859..ddc25ee9 100644 --- a/lib/widgets/modular_widgets/transfer_widgets/receive/receive_medium.dart +++ b/lib/widgets/modular_widgets/transfer_widgets/receive/receive_medium.dart @@ -29,7 +29,7 @@ class _ReceiveMediumCardState extends State { String? _selectedSelfAddress = kSelectedAddress; - Token? _selectedToken; + Token _selectedToken = kDualCoin.first; final List _tokens = []; @@ -74,8 +74,6 @@ class _ReceiveMediumCardState extends State { } Widget _getWidgetBody(BuildContext context, List tokens) { - _selectedToken ??= kDualCoin.first; - _initTokens(tokens); return Container( @@ -99,7 +97,7 @@ class _ReceiveMediumCardState extends State { ReceiveQrImage( data: _getQrString(), size: 110.0, - tokenStandard: _selectedToken!.tokenStandard, + tokenStandard: _selectedToken.tokenStandard, context: context, ), const SizedBox( @@ -169,16 +167,16 @@ class _ReceiveMediumCardState extends State { } String _getQrString() { - return '${_selectedToken!.symbol.toLowerCase()}:' - '$_selectedSelfAddress?zts=${_selectedToken!.tokenStandard}' + return '${_selectedToken.symbol.toLowerCase()}:' + '$_selectedSelfAddress?zts=${_selectedToken.tokenStandard}' '&amount=${_getAmount()}'; } - num _getAmount() { + BigInt _getAmount() { try { - return _amountController.text.toNum(); + return _amountController.text.extractDecimals(_selectedToken.decimals); } catch (e) { - return 0; + return BigInt.zero; } } @@ -199,7 +197,7 @@ class _ReceiveMediumCardState extends State { Widget _getCoinDropdown() => CoinDropdown( _tokens.toList(), - _selectedToken!, + _selectedToken, (value) { if (_selectedToken != value) { setState( diff --git a/lib/widgets/modular_widgets/transfer_widgets/receive/receive_small.dart b/lib/widgets/modular_widgets/transfer_widgets/receive/receive_small.dart index ff1e7bf1..d1a9a507 100644 --- a/lib/widgets/modular_widgets/transfer_widgets/receive/receive_small.dart +++ b/lib/widgets/modular_widgets/transfer_widgets/receive/receive_small.dart @@ -27,10 +27,10 @@ class _ReceiveSmallCardState extends State { 15.0, ), ), - child: Column( + child: const Column( crossAxisAlignment: CrossAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center, - children: const [ + children: [ Icon( SimpleLineIcons.arrow_down_circle, size: 60.0, diff --git a/lib/widgets/modular_widgets/transfer_widgets/send/send_large.dart b/lib/widgets/modular_widgets/transfer_widgets/send/send_large.dart index 231f0cab..c963c5b9 100644 --- a/lib/widgets/modular_widgets/transfer_widgets/send/send_large.dart +++ b/lib/widgets/modular_widgets/transfer_widgets/send/send_large.dart @@ -150,12 +150,12 @@ class _SendLargeCardState extends State { ), controller: _amountController, validator: (value) => InputValidators.correctValue( - value, - accountInfo.getBalanceWithDecimals( - _selectedToken.tokenStandard, - ), - _selectedToken.decimals, - ), + value, + accountInfo.getBalance( + _selectedToken.tokenStandard, + ), + _selectedToken.decimals, + BigInt.zero), suffixIcon: _getAmountSuffix(accountInfo), hintText: 'Amount', ), @@ -224,11 +224,12 @@ class _SendLargeCardState extends State { if (_recipientKey.currentState!.validate() && _amountKey.currentState!.validate()) { showDialogWithNoAndYesOptions( + isBarrierDismissible: false, context: context, title: 'Send Payment', description: 'Are you sure you want to transfer ' '${_amountController.text} ${_selectedToken.symbol} to ' - '${AddressUtils.getLabel(_recipientController.text)} ?', + '${ZenonAddressUtils.getLabel(_recipientController.text)} ?', onYesButtonPressed: () => _sendPayment(model), ); } @@ -255,12 +256,11 @@ class _SendLargeCardState extends State { } void _sendPayment(SendPaymentBloc model) { - Navigator.pop(context); _sendPaymentButtonKey.currentState?.animateForward(); model.sendTransfer( fromAddress: _selectedSelfAddress, toAddress: _recipientController.text, - amount: _amountController.text, + amount: _amountController.text.extractDecimals(_selectedToken.decimals), data: null, token: _selectedToken, ); @@ -296,14 +296,16 @@ class _SendLargeCardState extends State { ); void _onMaxPressed(AccountInfo accountInfo) { - num maxBalance = accountInfo.getBalanceWithDecimals( + BigInt maxBalance = accountInfo.getBalance( _selectedToken.tokenStandard, ); if (_amountController.text.isEmpty || - _amountController.text.toNum() < maxBalance) { + _amountController.text.extractDecimals(_selectedToken.decimals) < + maxBalance) { setState(() { - _amountController.text = maxBalance.toString(); + _amountController.text = + maxBalance.addDecimals(_selectedToken.decimals); }); } } @@ -354,10 +356,10 @@ class _SendLargeCardState extends State { sl.get().addNotification( WalletNotification( title: 'Sent ${_amountController.text} ${_selectedToken.symbol} ' - 'to ${AddressUtils.getLabel(_recipientController.text)}', + 'to ${ZenonAddressUtils.getLabel(_recipientController.text)}', timestamp: DateTime.now().millisecondsSinceEpoch, details: 'Sent ${_amountController.text} ${_selectedToken.symbol} ' - 'from ${AddressUtils.getLabel(_selectedSelfAddress!)} to ${AddressUtils.getLabel(_recipientController.text)}', + 'from ${ZenonAddressUtils.getLabel(_selectedSelfAddress!)} to ${ZenonAddressUtils.getLabel(_recipientController.text)}', type: NotificationType.paymentSent, id: null, ), @@ -365,14 +367,14 @@ class _SendLargeCardState extends State { } bool _hasBalance(AccountInfo accountInfo) => - accountInfo.getBalanceWithDecimals( + accountInfo.getBalance( _selectedToken.tokenStandard, ) > - 0; + BigInt.zero; void _addTokensWithBalance(AccountInfo accountInfo) { for (var balanceInfo in accountInfo.balanceInfoList!) { - if (balanceInfo.balance! > 0 && + if (balanceInfo.balance! > BigInt.zero && !_tokensWithBalance.contains(balanceInfo.token)) { _tokensWithBalance.add(balanceInfo.token); } @@ -382,12 +384,12 @@ class _SendLargeCardState extends State { bool _isInputValid(AccountInfo accountInfo) => InputValidators.checkAddress(_recipientController.text) == null && InputValidators.correctValue( - _amountController.text, - accountInfo.getBalanceWithDecimals( - _selectedToken.tokenStandard, - ), - _selectedToken.decimals, - ) == + _amountController.text, + accountInfo.getBalance( + _selectedToken.tokenStandard, + ), + _selectedToken.decimals, + BigInt.one) == null; @override diff --git a/lib/widgets/modular_widgets/transfer_widgets/send/send_medium.dart b/lib/widgets/modular_widgets/transfer_widgets/send/send_medium.dart index 3628555c..e1545fd6 100644 --- a/lib/widgets/modular_widgets/transfer_widgets/send/send_medium.dart +++ b/lib/widgets/modular_widgets/transfer_widgets/send/send_medium.dart @@ -144,10 +144,11 @@ class _SendMediumCardState extends State { ), validator: (value) => InputValidators.correctValue( value, - accountInfo.getBalanceWithDecimals( + accountInfo.getBalance( _selectedToken.tokenStandard, ), _selectedToken.decimals, + BigInt.zero, ), controller: _amountController, suffixIcon: _getAmountSuffix(accountInfo), @@ -180,22 +181,22 @@ class _SendMediumCardState extends State { _amountKey.currentState!.validate()) { showDialogWithNoAndYesOptions( context: context, + isBarrierDismissible: true, title: 'Send Payment', description: 'Are you sure you want to transfer ' '${_amountController.text} ${_selectedToken.symbol} to ' - '${AddressUtils.getLabel(_recipientController.text)} ?', + '${ZenonAddressUtils.getLabel(_recipientController.text)} ?', onYesButtonPressed: () => _sendPayment(model), ); } } void _sendPayment(SendPaymentBloc model) { - Navigator.pop(context); _sendPaymentButtonKey.currentState?.animateForward(); model.sendTransfer( fromAddress: kSelectedAddress, toAddress: _recipientController.text, - amount: _amountController.text, + amount: _amountController.text.extractDecimals(_selectedToken.decimals), data: null, token: _selectedToken, ); @@ -236,14 +237,16 @@ class _SendMediumCardState extends State { ); void _onMaxPressed(AccountInfo accountInfo) { - num maxBalance = accountInfo.getBalanceWithDecimals( + BigInt maxBalance = accountInfo.getBalance( _selectedToken.tokenStandard, ); if (_amountController.text.isEmpty || - _amountController.text.toNum() < maxBalance) { + _amountController.text.extractDecimals(_selectedToken.decimals) < + maxBalance) { setState(() { - _amountController.text = maxBalance.toString(); + _amountController.text = + maxBalance.addDecimals(_selectedToken.decimals); }); } } @@ -293,10 +296,10 @@ class _SendMediumCardState extends State { sl.get().addNotification( WalletNotification( title: 'Sent ${_amountController.text} ${_selectedToken.symbol} ' - 'to ${AddressUtils.getLabel(_recipientController.text)}', + 'to ${ZenonAddressUtils.getLabel(_recipientController.text)}', timestamp: DateTime.now().millisecondsSinceEpoch, details: 'Sent ${_amountController.text} ${_selectedToken.symbol} ' - 'from ${AddressUtils.getLabel(kSelectedAddress!)} to ${AddressUtils.getLabel(_recipientController.text)}', + 'from ${ZenonAddressUtils.getLabel(kSelectedAddress!)} to ${ZenonAddressUtils.getLabel(_recipientController.text)}', type: NotificationType.paymentSent, id: null, ), @@ -307,11 +310,11 @@ class _SendMediumCardState extends State { accountInfo.getBalance( _selectedToken.tokenStandard, ) > - 0; + BigInt.zero; void _addTokensWithBalance(AccountInfo accountInfo) { for (var balanceInfo in accountInfo.balanceInfoList!) { - if (balanceInfo.balance! > 0 && + if (balanceInfo.balance! > BigInt.zero && !_tokensWithBalance.contains(balanceInfo.token)) { _tokensWithBalance.add(balanceInfo.token); } @@ -321,12 +324,12 @@ class _SendMediumCardState extends State { bool _isInputValid(AccountInfo accountInfo) => InputValidators.checkAddress(_recipientController.text) == null && InputValidators.correctValue( - _amountController.text, - accountInfo.getBalanceWithDecimals( - _selectedToken.tokenStandard, - ), - _selectedToken.decimals, - ) == + _amountController.text, + accountInfo.getBalance( + _selectedToken.tokenStandard, + ), + _selectedToken.decimals, + BigInt.one) == null; @override diff --git a/lib/widgets/modular_widgets/transfer_widgets/send/send_small.dart b/lib/widgets/modular_widgets/transfer_widgets/send/send_small.dart index fe6f3595..bac6ed87 100644 --- a/lib/widgets/modular_widgets/transfer_widgets/send/send_small.dart +++ b/lib/widgets/modular_widgets/transfer_widgets/send/send_small.dart @@ -31,10 +31,10 @@ class _SendSmallCardState extends State { padding: const EdgeInsets.all( 20.0, ), - child: Column( + child: const Column( crossAxisAlignment: CrossAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center, - children: const [ + children: [ Icon( SimpleLineIcons.arrow_up_circle, size: 60.0, diff --git a/lib/widgets/modular_widgets/wallet_connect_widgets/wallet_connect_camera_card.dart b/lib/widgets/modular_widgets/wallet_connect_widgets/wallet_connect_camera_card.dart new file mode 100644 index 00000000..202fd9b0 --- /dev/null +++ b/lib/widgets/modular_widgets/wallet_connect_widgets/wallet_connect_camera_card.dart @@ -0,0 +1,101 @@ +import 'dart:io'; + +import 'package:ai_barcode_scanner/ai_barcode_scanner.dart'; +import 'package:flutter/material.dart'; +import 'package:logging/logging.dart'; +import 'package:wallet_connect_uri_validator/wallet_connect_uri_validator.dart'; +import 'package:zenon_syrius_wallet_flutter/main.dart'; +import 'package:zenon_syrius_wallet_flutter/services/wallet_connect_service.dart'; +import 'package:zenon_syrius_wallet_flutter/utils/utils.dart'; +import 'package:zenon_syrius_wallet_flutter/widgets/widgets.dart'; + +const String _kWidgetTitle = 'Camera QR Scanner'; +const String _kWidgetDescription = + 'Scan a WalletConnect QR code using the built-in camera of this device'; + +class WalletConnectCameraCard extends StatefulWidget { + const WalletConnectCameraCard({Key? key}) : super(key: key); + + @override + State createState() => + _WalletConnectCameraCardState(); +} + +class _WalletConnectCameraCardState extends State { + @override + void initState() { + super.initState(); + } + + @override + Widget build(BuildContext context) { + return CardScaffold( + title: _kWidgetTitle, + description: _kWidgetDescription, + childBuilder: () => _getCardBody(), + ); + } + + Widget _getCardBody() { + return Padding( + padding: const EdgeInsets.all(15.0), + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + const CircleAvatar( + radius: 60.0, + backgroundColor: Colors.white12, + child: Icon( + Icons.camera_alt_outlined, + color: AppColors.znnColor, + size: 60.0, + ), + ), + Platform.isMacOS + ? MyOutlinedButton( + text: 'Scan QR', + onPressed: () async { + await Navigator.of(context).push( + MaterialPageRoute( + builder: (context) => AiBarcodeScanner( + validator: (value) { + return canParseWalletConnectUri(value); + }, + canPop: true, + onScan: (String value) async { + final wcService = sl.get(); + final pairingInfo = + await wcService.pair(Uri.parse(value)); + Logger('WalletConnectCameraCard').log(Level.INFO, + 'pairing info', pairingInfo.toJson()); + setState(() {}); + }, + onDetect: (p0) {}, + onDispose: () {}, + controller: MobileScannerController( + detectionSpeed: DetectionSpeed.noDuplicates, + ), + ), + ), + ); + }, + minimumSize: kLoadingButtonMinSize, + ) + : Text( + 'Only MacOS is supported at the moment', + style: Theme.of(context).textTheme.bodyLarge, + ), + ], + ), + ); + } + + bool canParseWalletConnectUri(String wcUri) { + WalletConnectUri? walletConnectUri; + walletConnectUri = WalletConnectUri.tryParse(wcUri); + if (walletConnectUri != null) { + return true; + } + return false; + } +} diff --git a/lib/widgets/modular_widgets/wallet_connect_widgets/wallet_connect_pairing_list_card.dart b/lib/widgets/modular_widgets/wallet_connect_widgets/wallet_connect_pairing_list_card.dart new file mode 100644 index 00000000..d1648013 --- /dev/null +++ b/lib/widgets/modular_widgets/wallet_connect_widgets/wallet_connect_pairing_list_card.dart @@ -0,0 +1,153 @@ +import 'package:flutter/material.dart'; +import 'package:intl/intl.dart'; +import 'package:walletconnect_flutter_v2/walletconnect_flutter_v2.dart'; +import 'package:zenon_syrius_wallet_flutter/blocs/blocs.dart'; +import 'package:zenon_syrius_wallet_flutter/blocs/wallet_connect/wallet_connect_pairings_bloc.dart'; +import 'package:zenon_syrius_wallet_flutter/blocs/wallet_connect/wallet_connect_sessions_bloc.dart'; +import 'package:zenon_syrius_wallet_flutter/main.dart'; +import 'package:zenon_syrius_wallet_flutter/services/wallet_connect_service.dart'; +import 'package:zenon_syrius_wallet_flutter/utils/extensions.dart'; +import 'package:zenon_syrius_wallet_flutter/widgets/reusable_widgets/icons/clear_icon.dart'; +import 'package:zenon_syrius_wallet_flutter/widgets/widgets.dart'; + +const String _kWidgetTitle = 'WalletConnect Pairing List'; +const String _kWidgetDescription = + 'A pairing refers to the initial process of establishing a secure connection between a wallet and the dApp.' + 'Once the pairing is complete, a session is established, which allows the dApp to communicate with the wallet ' + 'securely over the Internet'; + +class WalletConnectPairingsCard extends StatefulWidget { + const WalletConnectPairingsCard({Key? key}) : super(key: key); + + @override + State createState() => + _WalletConnectPairingsCardState(); +} + +class _WalletConnectPairingsCardState extends State { + final WalletConnectPairingsBloc _pairingsBloc = + sl.get(); + + @override + void initState() { + // Initialize WalletConnect client + sl.get().initClient(); + super.initState(); + } + + @override + Widget build(BuildContext context) { + return CardScaffold( + title: _kWidgetTitle, + description: _kWidgetDescription, + childBuilder: () => _getCardBody(), + onRefreshPressed: () => _pairingsBloc.refreshResults(), + ); + } + + Widget _getCardBody() { + return _buildPairingsTable(); + } + + Widget _buildPairingsTable() { + return InfiniteScrollTable( + disposeBloc: false, + bloc: _pairingsBloc, + headerColumns: const [ + InfiniteScrollTableHeaderColumn( + columnName: 'Name', + ), + InfiniteScrollTableHeaderColumn( + columnName: 'URL', + flex: 2, + ), + InfiniteScrollTableHeaderColumn( + columnName: 'Topic', + ), + InfiniteScrollTableHeaderColumn( + columnName: 'Expiration', + ), + InfiniteScrollTableHeaderColumn( + columnName: 'Active', + ), + InfiniteScrollTableHeaderColumn( + columnName: '', + ), + ], + generateRowCells: (pairingInfo, bool isSelected) { + return [ + InfiniteScrollTableCell.withText( + context, + pairingInfo.peerMetadata?.name ?? 'Empty', + ), + InfiniteScrollTableCell( + _buildTableUrlWidget(pairingInfo), + flex: 2, + ), + isSelected + ? InfiniteScrollTableCell.withMarquee( + pairingInfo.topic, + ) + : InfiniteScrollTableCell.withText( + context, + pairingInfo.topic.short, + ), + InfiniteScrollTableCell.withText( + context, + _formatExpiryDateTime(pairingInfo.expiry).toString(), + ), + InfiniteScrollTableCell.withText( + context, + pairingInfo.active ? 'Yes' : 'No', + ), + InfiniteScrollTableCell( + _buildDeactivatePairingIcon(pairingInfo), + ), + ]; + }, + ); + } + + ClearIcon _buildDeactivatePairingIcon(PairingInfo pairingInfo) { + return ClearIcon( + onPressed: () => _onDeactivatePairingIconPressed(pairingInfo), + context: context, + ); + } + + Row _buildTableUrlWidget(PairingInfo pairingInfo) { + return Row( + children: [ + Text(pairingInfo.peerMetadata?.url ?? 'Empty'), + Visibility( + visible: pairingInfo.peerMetadata?.url != null, + child: LinkIcon( + url: pairingInfo.peerMetadata?.url ?? 'Empty', + ), + ), + ], + ); + } + + String _formatExpiryDateTime(int expirySeconds) { + final expiryDateTime = + DateTime.fromMillisecondsSinceEpoch(expirySeconds * 1000); + + return DateFormat('MMM dd, y HH:mm:ss').format(expiryDateTime); + } + + Future _onDeactivatePairingIconPressed(PairingInfo pairingInfo) async { + try { + await sl().deactivatePairing( + topic: pairingInfo.topic, + ); + _pairingsBloc.refreshResults(); + sl().refreshResults(); + } catch (e) { + sl().addErrorNotification( + e, + 'Error while deactivating pair', + ); + } + } +} diff --git a/lib/widgets/modular_widgets/wallet_connect_widgets/wallet_connect_qr_card.dart b/lib/widgets/modular_widgets/wallet_connect_widgets/wallet_connect_qr_card.dart new file mode 100644 index 00000000..5ba1f628 --- /dev/null +++ b/lib/widgets/modular_widgets/wallet_connect_widgets/wallet_connect_qr_card.dart @@ -0,0 +1,198 @@ +import 'dart:io'; + +import 'package:flutter/material.dart'; +import 'package:image/image.dart' as img; +import 'package:logging/logging.dart'; +import 'package:path/path.dart' as path; +import 'package:pretty_qr_code/pretty_qr_code.dart'; +import 'package:screen_capturer/screen_capturer.dart'; +import 'package:wallet_connect_uri_validator/wallet_connect_uri_validator.dart'; +import 'package:window_manager/window_manager.dart'; +import 'package:zenon_syrius_wallet_flutter/blocs/blocs.dart'; +import 'package:zenon_syrius_wallet_flutter/main.dart'; +import 'package:zenon_syrius_wallet_flutter/model/database/notification_type.dart'; +import 'package:zenon_syrius_wallet_flutter/model/database/wallet_notification.dart'; +import 'package:zenon_syrius_wallet_flutter/services/wallet_connect_service.dart'; +import 'package:zenon_syrius_wallet_flutter/utils/utils.dart'; +import 'package:zenon_syrius_wallet_flutter/widgets/widgets.dart'; +import 'package:znn_sdk_dart/znn_sdk_dart.dart'; +import 'package:zxing2/qrcode.dart'; + +const String _kWidgetTitle = 'On-screen QR Scanner'; +const String _kWidgetDescription = + 'Scan the WalletConnect QR code using an on-screen QR scanner. ' + 'This requires the screen recording permission'; + +final screenCapturer = ScreenCapturer.instance; + +class WalletConnectQrCard extends StatefulWidget { + const WalletConnectQrCard({Key? key}) : super(key: key); + + @override + State createState() => _WalletConnectQrCardState(); +} + +class _WalletConnectQrCardState extends State { + TextEditingController _uriController = TextEditingController( + text: kLastWalletConnectUriNotifier.value, + ); + CapturedData? _lastCapturedData; + + final _uriKey = GlobalKey(); + + @override + Widget build(BuildContext context) { + return CardScaffold( + title: _kWidgetTitle, + description: _kWidgetDescription, + childBuilder: () => _getCardBody(), + ); + } + + Widget _getCardBody() { + return Padding( + padding: const EdgeInsets.all(15.0), + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Container( + padding: const EdgeInsets.all(10.0), + decoration: BoxDecoration( + color: Theme.of(context).colorScheme.background, + borderRadius: BorderRadius.circular(15.0), + ), + child: PrettyQr( + data: 'Scan the WalletConnect QR from the dApp', + size: 100.0, + elementColor: AppColors.znnColor, + image: + const AssetImage('assets/images/qr_code_child_image_znn.png'), + typeNumber: 7, + errorCorrectLevel: QrErrorCorrectLevel.M, + roundEdges: true, + ), + ), + MyOutlinedButton( + text: 'Scan QR', + onPressed: () { + checkPermissionForMacOS().then((value) { + if (value) { + windowManager.minimize().then( + (value) => _handleClickCapture(CaptureMode.region), + ); + } + }); + }, + minimumSize: kLoadingButtonMinSize, + ), + ], + ), + ); + } + + Future _pairWithDapp(Uri uri) async { + try { + final wcService = sl.get(); + final pairingInfo = await wcService.pair(uri); + Logger('WalletConnectPairingCard') + .log(Level.INFO, 'pairing info', pairingInfo.toJson()); + _uriController = TextEditingController(); + _uriKey.currentState?.reset(); + setState(() {}); + } catch (e) { + NotificationUtils.sendNotificationError(e, 'Pairing failed'); + } + } + + void _handleClickCapture(CaptureMode mode) async { + try { + Directory walletConnectDirectory = Directory( + path.join(znnDefaultPaths.cache.path, walletConnectDirName)); + + if (!walletConnectDirectory.existsSync()) { + walletConnectDirectory.createSync(recursive: true); + } + + String screenshotName = + 'screenshot-${DateTime.now().millisecondsSinceEpoch}'; + + final imagePath = await File( + '${walletConnectDirectory.absolute.path}${path.separator}$screenshotName.png') + .create(); + + _lastCapturedData = await screenCapturer.capture( + mode: mode, + imagePath: imagePath.absolute.path, + silent: true, + ); + + if (_lastCapturedData != null) { + var image = img.decodePng(imagePath.readAsBytesSync())!; + + LuminanceSource source = RGBLuminanceSource( + image.width, image.height, image.getBytes().buffer.asInt32List()); + var bitmap = BinaryBitmap(HybridBinarizer(source)); + + var reader = QRCodeReader(); + var result = reader.decode(bitmap); + + if (result.rawBytes!.isNotEmpty) { + if (result.text.isNotEmpty && + WalletConnectUri.tryParse(result.text) != null) { + windowManager.show(); + _uriController.text = result.text; + } else { + windowManager.show(); + sl().addNotification(WalletNotification( + title: 'Invalid QR code', + timestamp: DateTime.now().millisecondsSinceEpoch, + details: 'Please scan a valid WalletConnect QR code', + type: NotificationType.error)); + } + } else { + windowManager.show(); + sl().addNotification(WalletNotification( + title: 'QR code scan failed', + timestamp: DateTime.now().millisecondsSinceEpoch, + details: 'Please scan a valid WalletConnect QR code', + type: NotificationType.error)); + } + _pairWithDapp(Uri.parse(result.text)); + } else { + windowManager.show(); + sl().addErrorNotification( + 'User canceled the QR scanning operation', + 'User QR scan canceled', + ); + } + } on Exception catch (e) { + windowManager.show(); + sl() + .addErrorNotification(e, 'Invalid QR code exception'); + } + } + + Future checkPermissionForMacOS() async { + if (Platform.isMacOS) { + if (!await _requestAccessForMacOS()) { + sl().addNotification(WalletNotification( + title: 'Permission required', + timestamp: DateTime.now().millisecondsSinceEpoch, + details: + 'Screen Recording permission is required to scan and process the on-screen WalletConnect QR code', + type: NotificationType.generatingPlasma)); + return false; + } + return true; + } + return true; + } + + Future _requestAccessForMacOS() async { + bool isAccessAllowed = await ScreenCapturer.instance.isAccessAllowed(); + if (!isAccessAllowed) { + await ScreenCapturer.instance.requestAccess(); + } + return isAccessAllowed; + } +} diff --git a/lib/widgets/modular_widgets/wallet_connect_widgets/wallet_connect_session_list_card.dart b/lib/widgets/modular_widgets/wallet_connect_widgets/wallet_connect_session_list_card.dart new file mode 100644 index 00000000..44b097db --- /dev/null +++ b/lib/widgets/modular_widgets/wallet_connect_widgets/wallet_connect_session_list_card.dart @@ -0,0 +1,125 @@ +import 'package:flutter/material.dart'; +import 'package:intl/intl.dart'; +import 'package:walletconnect_flutter_v2/walletconnect_flutter_v2.dart'; +import 'package:zenon_syrius_wallet_flutter/blocs/wallet_connect/wallet_connect_sessions_bloc.dart'; +import 'package:zenon_syrius_wallet_flutter/main.dart'; +import 'package:zenon_syrius_wallet_flutter/utils/extensions.dart'; +import 'package:zenon_syrius_wallet_flutter/widgets/widgets.dart'; + +const String _kWidgetTitle = 'WalletConnect Sessions List'; +const String _kWidgetDescription = + 'A session refers to a live connection between a user\'s wallet and a dApp ' + 'A session allows the dApp to communicate with the wallet ' + 'securely over the Internet'; + +class WalletConnectSessionsCard extends StatefulWidget { + const WalletConnectSessionsCard({Key? key}) : super(key: key); + + @override + State createState() => + _WalletConnectSessionsCardState(); +} + +class _WalletConnectSessionsCardState extends State { + final WalletConnectSessionsBloc _sessionsBloc = + sl.get(); + + @override + Widget build(BuildContext context) { + return CardScaffold( + title: _kWidgetTitle, + description: _kWidgetDescription, + childBuilder: () => _getCardBody(), + onRefreshPressed: () => _sessionsBloc.refreshResults(), + ); + } + + Widget _getCardBody() { + return _buildPairingsTable(); + } + + Widget _buildPairingsTable() { + return InfiniteScrollTable( + disposeBloc: false, + bloc: _sessionsBloc, + headerColumns: const [ + InfiniteScrollTableHeaderColumn( + columnName: 'Name', + ), + InfiniteScrollTableHeaderColumn( + columnName: 'URL', + flex: 2, + ), + InfiniteScrollTableHeaderColumn( + columnName: 'Pairing Topic', + ), + InfiniteScrollTableHeaderColumn( + columnName: 'Session Topic', + ), + InfiniteScrollTableHeaderColumn( + columnName: 'Expiration', + ), + InfiniteScrollTableHeaderColumn( + columnName: 'Acknowledged', + ), + ], + generateRowCells: (sessionData, bool isSelected) { + return [ + InfiniteScrollTableCell.withText( + context, + sessionData.peer.metadata.name, + ), + InfiniteScrollTableCell( + _buildTableUrlWidget(sessionData.peer.metadata.url), + flex: 2, + ), + isSelected + ? InfiniteScrollTableCell.withMarquee( + sessionData.pairingTopic, + ) + : InfiniteScrollTableCell.withText( + context, + sessionData.pairingTopic.short, + ), + isSelected + ? InfiniteScrollTableCell.withMarquee( + sessionData.topic, + ) + : InfiniteScrollTableCell.withText( + context, + sessionData.topic.short, + ), + InfiniteScrollTableCell.withText( + context, + _formatExpiryDateTime(sessionData.expiry).toString(), + ), + InfiniteScrollTableCell.withText( + context, + sessionData.acknowledged ? 'Yes' : 'No', + ), + ]; + }, + ); + } + + Row _buildTableUrlWidget(String? peerUrl) { + return Row( + children: [ + Text(peerUrl ?? 'Empty'), + Visibility( + visible: peerUrl != null, + child: LinkIcon( + url: peerUrl!, + ), + ), + ], + ); + } + + String _formatExpiryDateTime(int expirySeconds) { + final expiryDateTime = + DateTime.fromMillisecondsSinceEpoch(expirySeconds * 1000); + + return DateFormat('MMM dd, y HH:mm:ss').format(expiryDateTime); + } +} diff --git a/lib/widgets/modular_widgets/wallet_connect_widgets/wallet_connect_uri_card.dart b/lib/widgets/modular_widgets/wallet_connect_widgets/wallet_connect_uri_card.dart new file mode 100644 index 00000000..39369c71 --- /dev/null +++ b/lib/widgets/modular_widgets/wallet_connect_widgets/wallet_connect_uri_card.dart @@ -0,0 +1,132 @@ +import 'package:flutter/material.dart'; +import 'package:logging/logging.dart'; +import 'package:wallet_connect_uri_validator/wallet_connect_uri_validator.dart'; +import 'package:zenon_syrius_wallet_flutter/main.dart'; +import 'package:zenon_syrius_wallet_flutter/services/wallet_connect_service.dart'; +import 'package:zenon_syrius_wallet_flutter/utils/utils.dart'; +import 'package:zenon_syrius_wallet_flutter/widgets/widgets.dart'; + +const String _kWidgetTitle = 'WalletConnect Link'; +const String _kWidgetDescription = 'Paste the WalletConnect link here'; + +class WalletConnectUriCard extends StatefulWidget { + const WalletConnectUriCard({Key? key}) : super(key: key); + + @override + State createState() => _WalletConnectUriCardState(); +} + +class _WalletConnectUriCardState extends State { + TextEditingController _uriController = TextEditingController( + text: kLastWalletConnectUriNotifier.value, + ); + + final _uriKey = GlobalKey(); + + @override + Widget build(BuildContext context) { + return CardScaffold( + title: _kWidgetTitle, + description: _kWidgetDescription, + childBuilder: () => _getCardBody(), + ); + } + + Widget _getCardBody() { + return Padding( + padding: const EdgeInsets.all(15.0), + child: ValueListenableBuilder( + builder: (_, value, child) { + if (value != null) { + _uriController.text = value; + kLastWalletConnectUriNotifier.value = null; + } + return Column( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + SizedBox( + height: 120.0, + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const CircleAvatar( + backgroundColor: Colors.white12, + child: Icon( + Icons.link, + color: AppColors.znnColor, + ), + ), + Form( + key: _uriKey, + autovalidateMode: AutovalidateMode.onUserInteraction, + child: InputField( + validator: (value) { + if (WalletConnectUri.tryParse(value ?? '') != null) { + return null; + } else { + return 'URI invalid'; + } + }, + onChanged: (value) { + setState(() {}); + }, + controller: _uriController, + suffixIcon: RawMaterialButton( + shape: const CircleBorder(), + onPressed: () { + ClipboardUtils.pasteToClipboard(context, + (String value) { + _uriController.text = value; + setState(() {}); + }); + }, + child: const Icon( + Icons.content_paste, + color: AppColors.darkHintTextColor, + size: 15.0, + ), + ), + suffixIconConstraints: const BoxConstraints( + maxWidth: 45.0, + maxHeight: 20.0, + ), + hintText: 'WalletConnect URI', + ), + ), + ], + ), + ), + MyOutlinedButton( + text: 'Connect', + onPressed: + WalletConnectUri.tryParse(_uriController.text) != null + ? () { + _pairWithDapp( + Uri.parse(_uriController.text), + ); + } + : null, + minimumSize: kLoadingButtonMinSize, + ), + ], + ); + }, + valueListenable: kLastWalletConnectUriNotifier, + ), + ); + } + + Future _pairWithDapp(Uri uri) async { + try { + final wcService = sl.get(); + final pairingInfo = await wcService.pair(uri); + Logger('WalletConnectPairingCard') + .log(Level.INFO, 'pairing info', pairingInfo.toJson()); + _uriController = TextEditingController(); + _uriKey.currentState?.reset(); + setState(() {}); + } catch (e) { + NotificationUtils.sendNotificationError(e, 'Pairing failed'); + } + } +} diff --git a/lib/widgets/modular_widgets/wallet_connect_widgets/wallet_connect_widgets.dart b/lib/widgets/modular_widgets/wallet_connect_widgets/wallet_connect_widgets.dart new file mode 100644 index 00000000..73d770c0 --- /dev/null +++ b/lib/widgets/modular_widgets/wallet_connect_widgets/wallet_connect_widgets.dart @@ -0,0 +1,5 @@ +export 'wallet_connect_camera_card.dart'; +export 'wallet_connect_pairing_list_card.dart'; +export 'wallet_connect_qr_card.dart'; +export 'wallet_connect_session_list_card.dart'; +export 'wallet_connect_uri_card.dart'; diff --git a/lib/widgets/reusable_widgets/available_balance.dart b/lib/widgets/reusable_widgets/available_balance.dart index 4b3f38fe..a7b32e2b 100644 --- a/lib/widgets/reusable_widgets/available_balance.dart +++ b/lib/widgets/reusable_widgets/available_balance.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:zenon_syrius_wallet_flutter/utils/utils.dart'; import 'package:znn_sdk_dart/znn_sdk_dart.dart'; class AvailableBalance extends StatelessWidget { @@ -14,9 +15,9 @@ class AvailableBalance extends StatelessWidget { @override Widget build(BuildContext context) { return Text( - '${accountInfo.getBalanceWithDecimals( - token.tokenStandard, - )} ' + '${accountInfo.getBalance( + token.tokenStandard, + ).addDecimals(token.decimals)} ' '${token.symbol} available', style: Theme.of(context).inputDecorationTheme.hintStyle, ); diff --git a/lib/widgets/reusable_widgets/chart/chart_legend.dart b/lib/widgets/reusable_widgets/chart/chart_legend.dart index 7d8b5c51..cd3e263e 100644 --- a/lib/widgets/reusable_widgets/chart/chart_legend.dart +++ b/lib/widgets/reusable_widgets/chart/chart_legend.dart @@ -25,7 +25,7 @@ class ChartLegend extends StatelessWidget { ), Text( mainText, - style: Theme.of(context).textTheme.bodyLarge, + style: Theme.of(context).textTheme.bodyMedium, ), const SizedBox( width: 5.0, diff --git a/lib/widgets/reusable_widgets/custom_table.dart b/lib/widgets/reusable_widgets/custom_table.dart index 78f4638f..fb9fda09 100644 --- a/lib/widgets/reusable_widgets/custom_table.dart +++ b/lib/widgets/reusable_widgets/custom_table.dart @@ -314,7 +314,7 @@ class CustomTableCell extends StatelessWidget { child: Tooltip( message: address.toString(), child: Text( - AddressUtils.getLabel(address.toString()), + ZenonAddressUtils.getLabel(address.toString()), textAlign: textAlign, style: textStyle ?? Theme.of(context).textTheme.titleMedium!.copyWith( diff --git a/lib/widgets/reusable_widgets/dialogs.dart b/lib/widgets/reusable_widgets/dialogs.dart index 652e86d2..59d6eb20 100644 --- a/lib/widgets/reusable_widgets/dialogs.dart +++ b/lib/widgets/reusable_widgets/dialogs.dart @@ -57,30 +57,38 @@ showWarningDialog({ showDialogWithNoAndYesOptions({ required BuildContext context, required String title, - required String description, required VoidCallback onYesButtonPressed, + required isBarrierDismissible, + VoidCallback? onNoButtonPressed, + Widget? content, + String? description, }) => showDialog( + barrierDismissible: isBarrierDismissible, context: context, builder: (context) => AlertDialog( title: Text(title), - content: Text(description), + content: content ?? Text(description!), actions: [ TextButton( + onPressed: () { + onNoButtonPressed?.call(); + Navigator.pop(context, false); + }, child: Text( 'No', style: Theme.of(context).textTheme.bodyLarge, ), - onPressed: () { - Navigator.of(context).pop(); - }, ), TextButton( - onPressed: onYesButtonPressed, style: Theme.of(context).textButtonTheme.style!.copyWith( backgroundColor: MaterialStateColor.resolveWith( (states) => AppColors.errorColor), ), + onPressed: () { + onYesButtonPressed.call(); + Navigator.pop(context, true); + }, child: Text( 'Yes', style: Theme.of(context).textTheme.bodyLarge, diff --git a/lib/widgets/reusable_widgets/formatted_amount_with_tooltip.dart b/lib/widgets/reusable_widgets/formatted_amount_with_tooltip.dart index f7316fc9..4b449cbc 100644 --- a/lib/widgets/reusable_widgets/formatted_amount_with_tooltip.dart +++ b/lib/widgets/reusable_widgets/formatted_amount_with_tooltip.dart @@ -1,8 +1,9 @@ import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; +import 'package:zenon_syrius_wallet_flutter/utils/utils.dart'; class FormattedAmountWithTooltip extends Tooltip { - final num amount; + final String amount; final String tokenSymbol; final Widget Function(String, String) builder; @@ -14,6 +15,14 @@ class FormattedAmountWithTooltip extends Tooltip { }) : super( key: key, message: '$amount $tokenSymbol', - child: builder(NumberFormat.compact().format(amount), tokenSymbol), + child: builder( + amount.toNum() == 0 + ? '0' + : amount.startsWith('0.') + ? amount + : NumberFormat.compact().format(amount.toNum()).length > 8 + ? '…' + : NumberFormat.compact().format(amount.toNum()), + tokenSymbol), ); } diff --git a/lib/widgets/reusable_widgets/icons/clear_icon.dart b/lib/widgets/reusable_widgets/icons/clear_icon.dart new file mode 100644 index 00000000..da2dd723 --- /dev/null +++ b/lib/widgets/reusable_widgets/icons/clear_icon.dart @@ -0,0 +1,19 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_vector_icons/flutter_vector_icons.dart'; + +class ClearIcon extends RawMaterialButton { + ClearIcon({ + Key? key, + required VoidCallback onPressed, + required BuildContext context, + }) : super( + key: key, + onPressed: onPressed, + shape: const CircleBorder(), + child: Icon( + SimpleLineIcons.close, + color: Theme.of(context).colorScheme.secondary, + size: 20.0, + ), + ); +} diff --git a/lib/widgets/reusable_widgets/icons/icons.dart b/lib/widgets/reusable_widgets/icons/icons.dart index f3ef375a..8f4beae2 100644 --- a/lib/widgets/reusable_widgets/icons/icons.dart +++ b/lib/widgets/reusable_widgets/icons/icons.dart @@ -1,2 +1,3 @@ export 'copy_to_clipboard_icon.dart'; export 'standard_tooltip_icon.dart'; +export 'link_icon.dart'; diff --git a/lib/widgets/reusable_widgets/icons/link_icon.dart b/lib/widgets/reusable_widgets/icons/link_icon.dart new file mode 100644 index 00000000..db565b39 --- /dev/null +++ b/lib/widgets/reusable_widgets/icons/link_icon.dart @@ -0,0 +1,33 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_vector_icons/flutter_vector_icons.dart'; +import 'package:zenon_syrius_wallet_flutter/utils/utils.dart'; + +class LinkIcon extends RawMaterialButton { + LinkIcon({ + required String url, + Key? key}) + : super( + key: key, + constraints: const BoxConstraints( + minWidth: 40.0, + minHeight: 40.0, + ), + materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, + shape: const CircleBorder(), + onPressed: () => NavigationUtils.openUrl(url), + child: Container( + height: 25.0, + width: 25.0, + padding: const EdgeInsets.all(4.0), + decoration: const BoxDecoration( + shape: BoxShape.circle, + color: Colors.white12, + ), + child: const Icon( + SimpleLineIcons.link, + size: 10.0, + color: AppColors.znnColor, + ), + ), + ); +} diff --git a/lib/widgets/reusable_widgets/infinite_scroll_table.dart b/lib/widgets/reusable_widgets/infinite_scroll_table.dart index 7206289c..438fbb5e 100644 --- a/lib/widgets/reusable_widgets/infinite_scroll_table.dart +++ b/lib/widgets/reusable_widgets/infinite_scroll_table.dart @@ -343,7 +343,7 @@ class InfiniteScrollTableCell extends StatelessWidget { child: Tooltip( message: address.toString(), child: Text( - AddressUtils.getLabel(address.toString()), + ZenonAddressUtils.getLabel(address.toString()), textAlign: textAlign, style: textStyle ?? Theme.of(context).textTheme.titleMedium!.copyWith( diff --git a/lib/widgets/reusable_widgets/layout_scaffold/card_scaffold.dart b/lib/widgets/reusable_widgets/layout_scaffold/card_scaffold.dart index fa900c2c..18850757 100644 --- a/lib/widgets/reusable_widgets/layout_scaffold/card_scaffold.dart +++ b/lib/widgets/reusable_widgets/layout_scaffold/card_scaffold.dart @@ -229,7 +229,7 @@ class _CardScaffoldState extends State> { Expanded( child: Text( title, - style: Theme.of(context).textTheme.bodyText1!.copyWith( + style: Theme.of(context).textTheme.bodyLarge!.copyWith( fontSize: widget.titleFontSize, height: 1.0, ), diff --git a/lib/widgets/reusable_widgets/settings_node.dart b/lib/widgets/reusable_widgets/settings_node.dart index b2e61aa4..34d89de2 100644 --- a/lib/widgets/reusable_widgets/settings_node.dart +++ b/lib/widgets/reusable_widgets/settings_node.dart @@ -153,6 +153,7 @@ class _SettingsNodeState extends State { child: MaterialIconButton( onPressed: () { showDialogWithNoAndYesOptions( + isBarrierDismissible: true, context: context, title: 'Node Management', description: 'Are you sure you want to delete ' @@ -305,7 +306,6 @@ class _SettingsNodeState extends State { await nodesBox.delete(nodeKey); kDbNodes.remove(node); if (!mounted) return; - Navigator.pop(context); widget.onChangedOrDeletedNode(); } catch (e) { NotificationUtils.sendNotificationError( diff --git a/lib/widgets/reusable_widgets/stepper_utils.dart b/lib/widgets/reusable_widgets/stepper_utils.dart index 97fb1777..d3cecca6 100644 --- a/lib/widgets/reusable_widgets/stepper_utils.dart +++ b/lib/widgets/reusable_widgets/stepper_utils.dart @@ -1,8 +1,8 @@ import 'package:flutter/material.dart'; import 'package:zenon_syrius_wallet_flutter/utils/app_colors.dart'; -import 'package:zenon_syrius_wallet_flutter/widgets/widgets.dart'; import 'package:zenon_syrius_wallet_flutter/widgets/reusable_widgets/custom_material_stepper.dart' as custom_material_stepper; +import 'package:zenon_syrius_wallet_flutter/widgets/widgets.dart'; import 'package:znn_sdk_dart/znn_sdk_dart.dart'; class StepperUtils { diff --git a/lib/widgets/tab_children_widgets/notifications_tab_child.dart b/lib/widgets/tab_children_widgets/notifications_tab_child.dart index 8762ea66..73e510c7 100644 --- a/lib/widgets/tab_children_widgets/notifications_tab_child.dart +++ b/lib/widgets/tab_children_widgets/notifications_tab_child.dart @@ -1,11 +1,11 @@ import 'package:expandable/expandable.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_vector_icons/flutter_vector_icons.dart'; import 'package:hive/hive.dart'; import 'package:zenon_syrius_wallet_flutter/blocs/blocs.dart'; import 'package:zenon_syrius_wallet_flutter/model/model.dart'; import 'package:zenon_syrius_wallet_flutter/utils/constants.dart'; import 'package:zenon_syrius_wallet_flutter/utils/format_utils.dart'; +import 'package:zenon_syrius_wallet_flutter/widgets/reusable_widgets/icons/clear_icon.dart'; import 'package:zenon_syrius_wallet_flutter/widgets/widgets.dart'; class NotificationsTabChild extends StatefulWidget { @@ -88,14 +88,9 @@ class _NotificationsTabChildState extends State { } RawMaterialButton _getClearIcon(WalletNotification? notification) { - return RawMaterialButton( + return ClearIcon( onPressed: () => _deleteNotification(notification!.timestamp), - shape: const CircleBorder(), - child: Icon( - SimpleLineIcons.close, - color: Theme.of(context).colorScheme.secondary, - size: 20.0, - ), + context: context, ); } diff --git a/lib/widgets/tab_children_widgets/wallet_connect_tab_child.dart b/lib/widgets/tab_children_widgets/wallet_connect_tab_child.dart new file mode 100644 index 00000000..c99d8f31 --- /dev/null +++ b/lib/widgets/tab_children_widgets/wallet_connect_tab_child.dart @@ -0,0 +1,59 @@ +import 'package:flutter/material.dart'; +import 'package:layout/layout.dart'; +import 'package:zenon_syrius_wallet_flutter/widgets/widgets.dart'; + +class WalletConnectTabChild extends StatelessWidget { + const WalletConnectTabChild({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return StandardFluidLayout( + children: [ + FluidCell( + width: context.layout.value( + xl: kStaggeredNumOfColumns ~/ 3, + lg: kStaggeredNumOfColumns ~/ 3, + md: kStaggeredNumOfColumns ~/ 3, + sm: kStaggeredNumOfColumns ~/ 3, + xs: kStaggeredNumOfColumns ~/ 2, + ), + child: const WalletConnectQrCard(), + ), + FluidCell( + width: context.layout.value( + xl: kStaggeredNumOfColumns ~/ 3, + lg: kStaggeredNumOfColumns ~/ 3, + md: kStaggeredNumOfColumns ~/ 3, + sm: kStaggeredNumOfColumns ~/ 3, + xs: kStaggeredNumOfColumns ~/ 2, + ), + child: const WalletConnectUriCard(), + ), + FluidCell( + width: context.layout.value( + xl: kStaggeredNumOfColumns ~/ 3, + lg: kStaggeredNumOfColumns ~/ 3, + md: kStaggeredNumOfColumns ~/ 3, + sm: kStaggeredNumOfColumns ~/ 3, + xs: kStaggeredNumOfColumns ~/ 2, + ), + child: const WalletConnectCameraCard(), + ), + FluidCell( + width: context.layout.value( + xs: kStaggeredNumOfColumns, + ), + height: kStaggeredNumOfColumns / 4, + child: const WalletConnectPairingsCard(), + ), + FluidCell( + width: context.layout.value( + xs: kStaggeredNumOfColumns, + ), + height: kStaggeredNumOfColumns / 4, + child: const WalletConnectSessionsCard(), + ), + ], + ); + } +} diff --git a/linux/flutter/generated_plugin_registrant.cc b/linux/flutter/generated_plugin_registrant.cc deleted file mode 100644 index d5a08dfd..00000000 --- a/linux/flutter/generated_plugin_registrant.cc +++ /dev/null @@ -1,39 +0,0 @@ -// -// Generated file. Do not edit. -// - -// clang-format off - -#include "generated_plugin_registrant.h" - -#include -#include -#include -#include -#include -#include -#include - -void fl_register_plugins(FlPluginRegistry* registry) { - g_autoptr(FlPluginRegistrar) desktop_drop_registrar = - fl_plugin_registry_get_registrar_for_plugin(registry, "DesktopDropPlugin"); - desktop_drop_plugin_register_with_registrar(desktop_drop_registrar); - g_autoptr(FlPluginRegistrar) file_selector_linux_registrar = - fl_plugin_registry_get_registrar_for_plugin(registry, "FileSelectorPlugin"); - file_selector_plugin_register_with_registrar(file_selector_linux_registrar); - g_autoptr(FlPluginRegistrar) local_notifier_registrar = - fl_plugin_registry_get_registrar_for_plugin(registry, "LocalNotifierPlugin"); - local_notifier_plugin_register_with_registrar(local_notifier_registrar); - g_autoptr(FlPluginRegistrar) screen_retriever_registrar = - fl_plugin_registry_get_registrar_for_plugin(registry, "ScreenRetrieverPlugin"); - screen_retriever_plugin_register_with_registrar(screen_retriever_registrar); - g_autoptr(FlPluginRegistrar) tray_manager_registrar = - fl_plugin_registry_get_registrar_for_plugin(registry, "TrayManagerPlugin"); - tray_manager_plugin_register_with_registrar(tray_manager_registrar); - g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar = - fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin"); - url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar); - g_autoptr(FlPluginRegistrar) window_manager_registrar = - fl_plugin_registry_get_registrar_for_plugin(registry, "WindowManagerPlugin"); - window_manager_plugin_register_with_registrar(window_manager_registrar); -} diff --git a/linux/flutter/generated_plugin_registrant.h b/linux/flutter/generated_plugin_registrant.h deleted file mode 100644 index e0f0a47b..00000000 --- a/linux/flutter/generated_plugin_registrant.h +++ /dev/null @@ -1,15 +0,0 @@ -// -// Generated file. Do not edit. -// - -// clang-format off - -#ifndef GENERATED_PLUGIN_REGISTRANT_ -#define GENERATED_PLUGIN_REGISTRANT_ - -#include - -// Registers Flutter plugins. -void fl_register_plugins(FlPluginRegistry* registry); - -#endif // GENERATED_PLUGIN_REGISTRANT_ diff --git a/linux/flutter/generated_plugins.cmake b/linux/flutter/generated_plugins.cmake deleted file mode 100644 index 5ff4a42c..00000000 --- a/linux/flutter/generated_plugins.cmake +++ /dev/null @@ -1,30 +0,0 @@ -# -# Generated file, do not edit. -# - -list(APPEND FLUTTER_PLUGIN_LIST - desktop_drop - file_selector_linux - local_notifier - screen_retriever - tray_manager - url_launcher_linux - window_manager -) - -list(APPEND FLUTTER_FFI_PLUGIN_LIST -) - -set(PLUGIN_BUNDLED_LIBRARIES) - -foreach(plugin ${FLUTTER_PLUGIN_LIST}) - add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin}) - target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) - list(APPEND PLUGIN_BUNDLED_LIBRARIES $) - list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) -endforeach(plugin) - -foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) - add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/linux plugins/${ffi_plugin}) - list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) -endforeach(ffi_plugin) diff --git a/macos/Runner.xcodeproj/project.pbxproj b/macos/Runner.xcodeproj/project.pbxproj index d23ae2d5..6fb19089 100644 --- a/macos/Runner.xcodeproj/project.pbxproj +++ b/macos/Runner.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 51; + objectVersion = 54; objects = { /* Begin PBXAggregateTarget section */ @@ -213,7 +213,7 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0920; - LastUpgradeCheck = 1400; + LastUpgradeCheck = 1300; ORGANIZATIONNAME = ""; TargetAttributes = { 33CC10EC2044A3C60003C045 = { @@ -291,6 +291,7 @@ }; 3399D490228B24CF009A79C7 /* ShellScript */ = { isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); @@ -480,7 +481,7 @@ "$(PROJECT_DIR)", ); MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)"; - MARKETING_VERSION = "v0.0.6-alphanet"; + MARKETING_VERSION = "v0.0.7-alphanet"; PRODUCT_BUNDLE_IDENTIFIER = network.zenon.syrius; PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_VERSION = 5.0; @@ -617,7 +618,7 @@ "$(PROJECT_DIR)", ); MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)"; - MARKETING_VERSION = "v0.0.6-alphanet"; + MARKETING_VERSION = "v0.0.7-alphanet"; PRODUCT_BUNDLE_IDENTIFIER = network.zenon.syrius; PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; @@ -644,7 +645,7 @@ "$(PROJECT_DIR)", ); MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)"; - MARKETING_VERSION = "v0.0.6-alphanet"; + MARKETING_VERSION = "v0.0.7-alphanet"; PRODUCT_BUNDLE_IDENTIFIER = network.zenon.syrius; PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_VERSION = 5.0; diff --git a/macos/Runner/DebugProfile.entitlements b/macos/Runner/DebugProfile.entitlements index d35e43ae..da56afa9 100644 --- a/macos/Runner/DebugProfile.entitlements +++ b/macos/Runner/DebugProfile.entitlements @@ -4,5 +4,11 @@ com.apple.security.cs.allow-jit + com.apple.security.network.client + + com.apple.security.app-sandbox + + com.apple.security.network.server + diff --git a/macos/Runner/Info.plist b/macos/Runner/Info.plist index cd4cdf19..255640f2 100644 --- a/macos/Runner/Info.plist +++ b/macos/Runner/Info.plist @@ -26,7 +26,22 @@ $(PRODUCT_COPYRIGHT) NSMainNibFile MainMenu + CFBundleURLTypes + + + CFBundleTypeRole + Editor + CFBundleURLName + + CFBundleURLSchemes + + syrius + + + NSPrincipalClass NSApplication + NSCameraUsageDescription + Allows user to scan a QR code diff --git a/macos/Runner/Release.entitlements b/macos/Runner/Release.entitlements index 0c67376e..da56afa9 100644 --- a/macos/Runner/Release.entitlements +++ b/macos/Runner/Release.entitlements @@ -1,5 +1,14 @@ - + + com.apple.security.cs.allow-jit + + com.apple.security.network.client + + com.apple.security.app-sandbox + + com.apple.security.network.server + + diff --git a/pubspec.lock b/pubspec.lock index 471bf370..a84ae396 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -5,18 +5,42 @@ packages: dependency: transitive description: name: _fe_analyzer_shared - sha256: a36ec4843dc30ea6bf652bf25e3448db6c5e8bcf4aa55f063a5d1dad216d8214 + sha256: ae92f5d747aee634b87f89d9946000c2de774be1d6ac3e58268224348cd0101a url: "https://pub.dev" source: hosted - version: "58.0.0" + version: "61.0.0" + adaptive_number: + dependency: transitive + description: + name: adaptive_number + sha256: "3a567544e9b5c9c803006f51140ad544aedc79604fd4f3f2c1380003f97c1d77" + url: "https://pub.dev" + source: hosted + version: "1.0.0" + ai_barcode_scanner: + dependency: "direct main" + description: + name: ai_barcode_scanner + sha256: e77a90d9a953ba8d797cf4ff9f705c1842133fffd596f7ccc845dd08650e0543 + url: "https://pub.dev" + source: hosted + version: "0.0.7" analyzer: dependency: transitive description: name: analyzer - sha256: cc4242565347e98424ce9945c819c192ec0838cb9d1f6aa4a97cc96becbc5b27 + sha256: ea3d8652bda62982addfd92fdc2d0214e5f82e43325104990d4f4c4a2a313562 url: "https://pub.dev" source: hosted - version: "5.10.0" + version: "5.13.0" + app_links: + dependency: "direct main" + description: + name: app_links + sha256: "16725e716afd0634a5441654b1dda2b6c5557aa230884b5e1f41a5aa546a4cb6" + url: "https://pub.dev" + source: hosted + version: "3.4.3" archive: dependency: transitive description: @@ -37,18 +61,18 @@ packages: dependency: transitive description: name: args - sha256: "4cab82a83ffef80b262ddedf47a0a8e56ee6fbf7fe21e6e768b02792034dd440" + sha256: eef6c46b622e0494a36c5a12d10d77fb4e855501a91c1b9ef9339326e58f0596 url: "https://pub.dev" source: hosted - version: "2.4.0" + version: "2.4.2" async: dependency: transitive description: name: async - sha256: bfe67ef28df125b7dddcea62755991f807aa39a2492a23e1550161692950bbe0 + sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" url: "https://pub.dev" source: hosted - version: "2.10.0" + version: "2.11.0" auto_size_text: dependency: "direct main" description: @@ -57,6 +81,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.0.0" + base_x: + dependency: transitive + description: + name: base_x + sha256: "3f1043679659f1759c651f900da6f24f0a8062c28daa6f9625e8d580002e187b" + url: "https://pub.dev" + source: hosted + version: "2.0.0" bech32: dependency: transitive description: @@ -65,6 +97,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.2.2" + big_decimal: + dependency: "direct main" + description: + name: big_decimal + sha256: "301158ec5a646d1e1a0ca7a97fbfab7be18a8df700adb7f7cb9c4149e75c8f0c" + url: "https://pub.dev" + source: hosted + version: "0.5.0" bip32: dependency: transitive description: @@ -89,6 +129,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.1" + bs58: + dependency: transitive + description: + name: bs58 + sha256: "3ed24dadf386ca749ff50af678be1131ef569a3583a6f37b87b90c032270c767" + url: "https://pub.dev" + source: hosted + version: "1.0.2" bs58check: dependency: transitive description: @@ -101,10 +149,10 @@ packages: dependency: transitive description: name: build - sha256: "3fbda25365741f8251b39f3917fb3c8e286a96fd068a5a242e11c2012d495777" + sha256: "43865b79fbb78532e4bff7c33087aa43b1d488c4fdef014eaef568af6d8016dc" url: "https://pub.dev" source: hosted - version: "2.3.1" + version: "2.4.0" build_config: dependency: transitive description: @@ -133,18 +181,18 @@ packages: dependency: "direct dev" description: name: build_runner - sha256: "7b25ba738bc74c94187cebeb9cc29d38a32e8279ce950eabd821d3b454a5f03d" + sha256: "5e1929ad37d48bd382b124266cb8e521de5548d406a45a5ae6656c13dab73e37" url: "https://pub.dev" source: hosted - version: "2.4.1" + version: "2.4.5" build_runner_core: dependency: transitive description: name: build_runner_core - sha256: "14febe0f5bac5ae474117a36099b4de6f1dbc52df6c5e55534b3da9591bf4292" + sha256: "6d6ee4276b1c5f34f21fdf39425202712d2be82019983d52f351c94aafbc2c41" url: "https://pub.dev" source: hosted - version: "7.2.7" + version: "7.2.10" built_collection: dependency: transitive description: @@ -157,26 +205,42 @@ packages: dependency: transitive description: name: built_value - sha256: "31b7c748fd4b9adf8d25d72a4c4a59ef119f12876cf414f94f8af5131d5fa2b0" + sha256: "598a2a682e2a7a90f08ba39c0aaa9374c5112340f0a2e275f61b59389543d166" url: "https://pub.dev" source: hosted - version: "8.4.4" + version: "8.6.1" characters: dependency: transitive description: name: characters - sha256: e6a326c8af69605aec75ed6c187d06b349707a27fbff8222ca9cc2cff167975c + sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" url: "https://pub.dev" source: hosted - version: "1.2.1" + version: "1.3.0" + charcode: + dependency: transitive + description: + name: charcode + sha256: fb98c0f6d12c920a02ee2d998da788bca066ca5f148492b7085ee23372b12306 + url: "https://pub.dev" + source: hosted + version: "1.3.1" checked_yaml: dependency: transitive description: name: checked_yaml - sha256: "3d1505d91afa809d177efd4eed5bb0eb65805097a1463abdd2add076effae311" + sha256: feb6bed21949061731a7a75fc5d2aa727cf160b91af9a3e464c5e3a32e28b5ff url: "https://pub.dev" source: hosted - version: "2.0.2" + version: "2.0.3" + clipboard_watcher: + dependency: "direct main" + description: + name: clipboard_watcher + sha256: "2fa9c728f46962668dbc1c07870695c9163524f8dbea25dc176277d1ef1cc2bd" + url: "https://pub.dev" + source: hosted + version: "0.2.0" clock: dependency: transitive description: @@ -189,10 +253,10 @@ packages: dependency: transitive description: name: code_builder - sha256: "0d43dd1288fd145de1ecc9a3948ad4a6d5a82f0a14c4fdd0892260787d975cbe" + sha256: "4ad01d6e56db961d29661561effde45e519939fdaeb46c351275b182eac70189" url: "https://pub.dev" source: hosted - version: "4.4.0" + version: "4.5.0" collection: dependency: transitive description: @@ -221,10 +285,10 @@ packages: dependency: transitive description: name: crypto - sha256: aa274aa7774f8964e4f4f38cc994db7b6158dd36e9187aaceaddc994b35c6c67 + sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab url: "https://pub.dev" source: hosted - version: "3.0.2" + version: "3.0.3" cryptography: dependency: transitive description: @@ -237,10 +301,18 @@ packages: dependency: transitive description: name: dart_style - sha256: "6d691edde054969f0e0f26abb1b30834b5138b963793e56f69d3a9a4435e6352" + sha256: f4f1f73ab3fd2afcbcca165ee601fe980d966af6a21b5970c6c9376955c528ad + url: "https://pub.dev" + source: hosted + version: "2.3.1" + dbus: + dependency: transitive + description: + name: dbus + sha256: "6f07cba3f7b3448d42d015bfd3d53fe12e5b36da2423f23838efc1d5fb31a263" url: "https://pub.dev" source: hosted - version: "2.3.0" + version: "0.7.8" dependency_validator: dependency: "direct dev" description: @@ -261,10 +333,10 @@ packages: dependency: "direct main" description: name: device_info_plus - sha256: "435383ca05f212760b0a70426b5a90354fe6bd65992b3a5e27ab6ede74c02f5c" + sha256: f52ab3b76b36ede4d135aab80194df8925b553686f0fa12226b4e2d658e45903 url: "https://pub.dev" source: hosted - version: "8.2.0" + version: "8.2.2" device_info_plus_platform_interface: dependency: transitive description: @@ -281,6 +353,30 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.0+3" + ed25519_edwards: + dependency: transitive + description: + name: ed25519_edwards + sha256: "6ce0112d131327ec6d42beede1e5dfd526069b18ad45dcf654f15074ad9276cd" + url: "https://pub.dev" + source: hosted + version: "0.3.1" + eip1559: + dependency: transitive + description: + name: eip1559 + sha256: c2b81ac85f3e0e71aaf558201dd9a4600f051ece7ebacd0c5d70065c9b458004 + url: "https://pub.dev" + source: hosted + version: "0.6.2" + eip55: + dependency: transitive + description: + name: eip55 + sha256: "213a9b86add87a5216328e8494b0ab836e401210c4d55eb5e521bd39e39169e1" + url: "https://pub.dev" + source: hosted + version: "1.0.2" equatable: dependency: transitive description: @@ -289,6 +385,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.5" + event: + dependency: transitive + description: + name: event + sha256: eb4814de94cbf6a10da9c4f652bc654087d7066e33566b5036822e6c0b24befb + url: "https://pub.dev" + source: hosted + version: "2.1.2" expandable: dependency: "direct main" description: @@ -309,10 +413,10 @@ packages: dependency: "direct main" description: name: ffi - sha256: a38574032c5f1dd06c4aee541789906c12ccaab8ba01446e800d9c5b79c4a978 + sha256: ed5337a5660c506388a9f012be0288fb38b49020ce2b45fe1f8b8323fe429f99 url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "2.0.2" file: dependency: transitive description: @@ -325,42 +429,42 @@ packages: dependency: "direct main" description: name: file_selector - sha256: "1ebc23f487172ac2cb9ead80fa2fab09af75e1e8239cbf8ef6d62b623991f785" + sha256: a96fa9e956c1045c1879164b09f62918d827f6757a55d0ed2aa29c4fa2370c0b url: "https://pub.dev" source: hosted - version: "0.9.2+4" + version: "0.9.3" file_selector_ios: dependency: transitive description: name: file_selector_ios - sha256: "740c60081c7d9b78a87bcdd9b60a23364af79153def78a075d5a8d3e969353b0" + sha256: "54542b6b35e3ced6246df5fae13cf0b879d14669d0fdff1a53a098f16e23328b" url: "https://pub.dev" source: hosted - version: "0.5.1+3" + version: "0.5.1+4" file_selector_linux: dependency: transitive description: name: file_selector_linux - sha256: "39eb2bec65a532ff81816123ee0f08d4993ee2a3cd82d88c7e4b64275e565b54" + sha256: d17c5e450192cdc40b718804dfb4eaf79a71bed60ee9530703900879ba50baa3 url: "https://pub.dev" source: hosted - version: "0.9.1+2" + version: "0.9.1+3" file_selector_macos: dependency: transitive description: name: file_selector_macos - sha256: "41800c76c633c99fb1c466e9a6be7ff4c8b6d45fe454267d5e4d2d0745394b7a" + sha256: "6290eec24fc4cc62535fe609e0c6714d3c1306191dc8c3b0319eaecc09423a3a" url: "https://pub.dev" source: hosted - version: "0.9.0+8" + version: "0.9.2" file_selector_platform_interface: dependency: transitive description: name: file_selector_platform_interface - sha256: "98b3f98effcc135bd951b34b575886620ce208d3ff60e3b61cbaea9e2e00f54b" + sha256: "2a7f4bbf7bd2f022ecea85bfb1754e87f7dd403a9abc17a84a4fa2ddfe2abc0a" url: "https://pub.dev" source: hosted - version: "2.4.1" + version: "2.5.1" file_selector_web: dependency: transitive description: @@ -373,10 +477,10 @@ packages: dependency: transitive description: name: file_selector_windows - sha256: "92f1d84f070405506ee4564eec848a0ad5576d7fe6388227ec237c97f5330dac" + sha256: ef246380b66d1fb9089fc65622c387bf3780bca79f533424c31d07f12c2c7fd8 url: "https://pub.dev" source: hosted - version: "0.9.1+7" + version: "0.9.2" fixnum: dependency: transitive description: @@ -389,10 +493,10 @@ packages: dependency: "direct main" description: name: fl_chart - sha256: "155556c4aba56c8474308d51b4e5446b80a07db9ab66a3cb74aff05c110df982" + sha256: "48a1b69be9544e2b03d9a8e843affd89e43f3194c9248776222efcb4206bb1ec" url: "https://pub.dev" source: hosted - version: "0.60.0" + version: "0.62.0" flip_card: dependency: "direct main" description: @@ -418,18 +522,18 @@ packages: dependency: "direct main" description: name: flutter_staggered_grid_view - sha256: a6b5139c24f292ad55cd87624f846ea8815f97977e875658f351cfc669910991 + sha256: f0b6d8c0fa7b4b444985cdde68492c0138a4fb6fc57a641b24cb234b7ee0f5c4 url: "https://pub.dev" source: hosted - version: "0.4.0" + version: "0.4.1" flutter_svg: dependency: "direct main" description: name: flutter_svg - sha256: f991fdb1533c3caeee0cdc14b04f50f0c3916f0dbcbc05237ccbe4e3c6b93f3f + sha256: "8c5d68a82add3ca76d792f058b186a0599414f279f00ece4830b9b231b570338" url: "https://pub.dev" source: hosted - version: "2.0.5" + version: "2.0.7" flutter_test: dependency: "direct dev" description: flutter @@ -448,6 +552,14 @@ packages: description: flutter source: sdk version: "0.0.0" + freezed_annotation: + dependency: transitive + description: + name: freezed_annotation + sha256: c3fd9336eb55a38cc1bbd79ab17573113a8deccd0ecbbf926cca3c62803b5c2d + url: "https://pub.dev" + source: hosted + version: "2.4.1" frontend_server_client: dependency: transitive description: @@ -460,34 +572,34 @@ packages: dependency: transitive description: name: gap - sha256: "6e35ee60d5bbc61b0bec97cc5ba7ed32f62f1f62449e281ea77677479418a15d" + sha256: db02ec4ac4511ea8d324d7f671d526959a8e7857468b4ea64113fe8a82f16a2c url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "2.0.2" get_it: dependency: "direct main" description: name: get_it - sha256: "290fde3a86072e4b37dbb03c07bec6126f0ecc28dad403c12ffe2e5a2d751ab7" + sha256: "529de303c739fca98cd7ece5fca500d8ff89649f1bb4b4e94fb20954abcd7468" url: "https://pub.dev" source: hosted - version: "7.2.0" + version: "7.6.0" glob: dependency: transitive description: name: glob - sha256: "4515b5b6ddb505ebdd242a5f2cc5d22d3d6a80013789debfbda7777f47ea308c" + sha256: "0e7014b3b7d4dac1ca4d6114f82bf1782ee86745b9b42a92c9289c23d8a0ab63" url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.2" graphs: dependency: transitive description: name: graphs - sha256: f9e130f3259f52d26f0cfc0e964513796dafed572fa52e45d2f8d6ca14db39b2 + sha256: aedc5a15e78fc65a6e23bcd927f24c64dd995062bcd1ca6eda65a3cff92a4d19 url: "https://pub.dev" source: hosted - version: "2.2.0" + version: "2.3.1" hex: dependency: "direct main" description: @@ -516,10 +628,10 @@ packages: dependency: transitive description: name: http - sha256: "6aa2946395183537c8b880962d935877325d6a09a2867c3970c05c0fed6ac482" + sha256: "759d1a329847dd0f39226c688d3e06a6b8679668e350e2891a6474f8b4bb8525" url: "https://pub.dev" source: hosted - version: "0.13.5" + version: "1.1.0" http_multi_server: dependency: transitive description: @@ -536,6 +648,14 @@ packages: url: "https://pub.dev" source: hosted version: "4.0.2" + image: + dependency: "direct main" + description: + name: image + sha256: a72242c9a0ffb65d03de1b7113bc4e189686fc07c7147b8b41811d0dd0e0d9bf + url: "https://pub.dev" + source: hosted + version: "4.0.17" infinite_scroll_pagination: dependency: "direct main" description: @@ -548,10 +668,10 @@ packages: dependency: "direct main" description: name: intl - sha256: a3715e3bc90294e971cb7dc063fbf3cd9ee0ebf8604ffeafabd9e6f16abbdbe6 + sha256: "3bc132a9dbce73a7e4a21a17d06e1878839ffbf975568bc875c60537824b0c4d" url: "https://pub.dev" source: hosted - version: "0.18.0" + version: "0.18.1" io: dependency: transitive description: @@ -572,10 +692,10 @@ packages: dependency: transitive description: name: json_annotation - sha256: c33da08e136c3df0190bd5bbe51ae1df4a7d96e7954d1d7249fea2968a72d317 + sha256: b10a7b2ff83d83c777edba3c6a0f97045ddadd56c944e1a23a3fdf43a1bf4467 url: "https://pub.dev" source: hosted - version: "4.8.0" + version: "4.8.1" json_rpc_2: dependency: "direct main" description: @@ -604,10 +724,10 @@ packages: dependency: transitive description: name: lints - sha256: "5e4a9cd06d447758280a8ac2405101e0e2094d2a1dbdd3756aec3fe7775ba593" + sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "2.1.1" local_notifier: dependency: "direct main" description: @@ -616,22 +736,30 @@ packages: url: "https://pub.dev" source: hosted version: "0.1.5" + logger: + dependency: transitive + description: + name: logger + sha256: "7ad7215c15420a102ec687bb320a7312afd449bac63bfb1c60d9787c27b9767f" + url: "https://pub.dev" + source: hosted + version: "1.4.0" logging: dependency: "direct main" description: name: logging - sha256: "04094f2eb032cbb06c6f6e8d3607edcfcb0455e2bb6cbc010cb01171dcb64e6d" + sha256: "623a88c9594aa774443aa3eb2d41807a48486b5613e67599fb4c41c0ad47c340" url: "https://pub.dev" source: hosted - version: "1.1.1" + version: "1.2.0" lottie: dependency: "direct main" description: name: lottie - sha256: "23522951540d20a57a60202ed7022e6376bed206a4eee1c347a91f58bd57eb9f" + sha256: f461105d3a35887b27089abf9c292334478dd292f7b47ecdccb6ae5c37a22c80 url: "https://pub.dev" source: hosted - version: "2.3.2" + version: "2.4.0" marquee_widget: dependency: "direct main" description: @@ -644,10 +772,10 @@ packages: dependency: transitive description: name: matcher - sha256: c94db23593b89766cda57aab9ac311e3616cf87c6fa4e9749df032f66f30dcb8 + sha256: "6501fbd55da300384b768785b83e5ce66991266cec21af89ab9ae7f5ce1c4cbb" url: "https://pub.dev" source: hosted - version: "0.12.14" + version: "0.12.15" material_color_utilities: dependency: transitive description: @@ -668,10 +796,10 @@ packages: dependency: transitive description: name: meta - sha256: "6c268b42ed578a53088d834796959e4a1814b5e9e164f147f580a386e5decf42" + sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" url: "https://pub.dev" source: hosted - version: "1.8.0" + version: "1.9.1" mime: dependency: transitive description: @@ -680,6 +808,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.4" + mobile_scanner: + dependency: transitive + description: + name: mobile_scanner + sha256: "54dd914e1bb5758b3db7aa02e56d65d80285ba0705e0e5fa5cfbb11e27344c4b" + url: "https://pub.dev" + source: hosted + version: "3.3.0" nested: dependency: transitive description: @@ -696,6 +832,22 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.7" + ocr_engine_builtin: + dependency: transitive + description: + name: ocr_engine_builtin + sha256: ced1acd5cd3dee2ea63e8dd2b77fac63deebf3ee5b4654e26b87314b917eebbb + url: "https://pub.dev" + source: hosted + version: "0.1.1" + ocr_engine_youdao: + dependency: transitive + description: + name: ocr_engine_youdao + sha256: f5c5eee137c4e816f53ea49323e2db389733da9b2a8c0ee70f1525057e05239d + url: "https://pub.dev" + source: hosted + version: "0.1.1" open_filex: dependency: "direct main" description: @@ -724,10 +876,10 @@ packages: dependency: "direct main" description: name: package_info_plus - sha256: cbff87676c352d97116af6dbea05aa28c4d65eb0f6d5677a520c11a69ca9a24d + sha256: ceb027f6bc6a60674a233b4a90a7658af1aebdea833da0b5b53c1e9821a78c7b url: "https://pub.dev" source: hosted - version: "3.1.0" + version: "4.0.2" package_info_plus_platform_interface: dependency: transitive description: @@ -772,34 +924,34 @@ packages: dependency: "direct main" description: name: path_provider - sha256: c7edf82217d4b2952b2129a61d3ad60f1075b9299e629e149a8d2e39c2e6aad4 + sha256: "3087813781ab814e4157b172f1a11c46be20179fcc9bea043e0fba36bc0acaa2" url: "https://pub.dev" source: hosted - version: "2.0.14" + version: "2.0.15" path_provider_android: dependency: transitive description: name: path_provider_android - sha256: "019f18c9c10ae370b08dce1f3e3b73bc9f58e7f087bb5e921f06529438ac0ae7" + sha256: "2cec049d282c7f13c594b4a73976b0b4f2d7a1838a6dd5aaf7bd9719196bee86" url: "https://pub.dev" source: hosted - version: "2.0.24" + version: "2.0.27" path_provider_foundation: dependency: transitive description: name: path_provider_foundation - sha256: ad4c4d011830462633f03eb34445a45345673dfd4faf1ab0b4735fbd93b19183 + sha256: "1995d88ec2948dac43edf8fe58eb434d35d22a2940ecee1a9fefcd62beee6eb3" url: "https://pub.dev" source: hosted - version: "2.2.2" + version: "2.2.3" path_provider_linux: dependency: transitive description: name: path_provider_linux - sha256: "2ae08f2216225427e64ad224a24354221c2c7907e448e6e0e8b57b1eb9f10ad1" + sha256: ffbb8cc9ed2c9ec0e4b7a541e56fd79b138e8f47d2fb86815f15358a349b3b57 url: "https://pub.dev" source: hosted - version: "2.1.10" + version: "2.1.11" path_provider_platform_interface: dependency: transitive description: @@ -812,18 +964,18 @@ packages: dependency: transitive description: name: path_provider_windows - sha256: f53720498d5a543f9607db4b0e997c4b5438884de25b0f73098cc2671a51b130 + sha256: "1cb68ba4cd3a795033de62ba1b7b4564dace301f952de6bfb3cd91b202b6ee96" url: "https://pub.dev" source: hosted - version: "2.1.5" + version: "2.1.7" petitparser: dependency: transitive description: name: petitparser - sha256: "49392a45ced973e8d94a85fdb21293fbb40ba805fc49f2965101ae748a3683b4" + sha256: cb3798bef7fc021ac45b308f4b51208a152792445cce0448c9a4ba5879dd8750 url: "https://pub.dev" source: hosted - version: "5.1.0" + version: "5.4.0" platform: dependency: transitive description: @@ -844,10 +996,10 @@ packages: dependency: transitive description: name: pointycastle - sha256: c3120a968135aead39699267f4c74bc9a08e4e909e86bc1b0af5bfd78691123c + sha256: "7c1e5f0d23c9016c5bbd8b1473d0d3fb3fc851b876046039509e18e0c7485f2c" url: "https://pub.dev" source: hosted - version: "3.7.2" + version: "3.7.3" pool: dependency: transitive description: @@ -856,6 +1008,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.5.1" + preference_list: + dependency: "direct main" + description: + name: preference_list + sha256: d3419b2ec57a6ad2156bd682a46cf3194bf162b36bb013790c7b29e6438f107a + url: "https://pub.dev" + source: hosted + version: "0.0.1" pretty_qr_code: dependency: "direct main" description: @@ -884,18 +1044,18 @@ packages: dependency: transitive description: name: pub_semver - sha256: "307de764d305289ff24ad257ad5c5793ce56d04947599ad68b3baa124105fc17" + sha256: "40d3ab1bbd474c4c2328c91e3a7df8c6dd629b79ece4c4bd04bee496a224fb0c" url: "https://pub.dev" source: hosted - version: "2.1.3" + version: "2.1.4" pubspec_parse: dependency: transitive description: name: pubspec_parse - sha256: ec85d7d55339d85f44ec2b682a82fea340071e8978257e5a43e69f79e98ef50c + sha256: c63b2876e58e194e4b0828fcb080ad0e06d051cb607a6be51a9e084f47cb9367 url: "https://pub.dev" source: hosted - version: "1.2.2" + version: "1.2.3" qr: dependency: transitive description: @@ -912,6 +1072,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.27.7" + screen_capturer: + dependency: "direct main" + description: + name: screen_capturer + sha256: "5a1b6ec7f71cc928d62f1fb120ba1f08071ffe7f52f57f6b47ecfdfa05e133a9" + url: "https://pub.dev" + source: hosted + version: "0.1.5" screen_retriever: dependency: transitive description: @@ -924,10 +1092,18 @@ packages: dependency: "direct main" description: name: screenshot - sha256: "30bb9fade6eb2578a1fc2e84f6b184141fc86883cda10988d4500ff00eb728e2" + sha256: "455284ff1f5b911d94a43c25e1385485cf6b4f288293eba68f15dad711c7b81c" url: "https://pub.dev" source: hosted - version: "1.3.0" + version: "2.1.0" + sec: + dependency: transitive + description: + name: sec + sha256: "8bbd56df884502192a441b5f5d667265498f2f8728a282beccd9db79e215f379" + url: "https://pub.dev" + source: hosted + version: "1.1.0" sha3: dependency: transitive description: @@ -940,10 +1116,10 @@ packages: dependency: "direct main" description: name: share_plus - sha256: "692261968a494e47323dcc8bc66d8d52e81bc27cb4b808e4e8d7e8079d4cc01a" + sha256: ed3fcea4f789ed95913328e629c0c53e69e80e08b6c24542f1b3576046c614e8 url: "https://pub.dev" source: hosted - version: "6.3.2" + version: "7.0.2" share_plus_platform_interface: dependency: transitive description: @@ -952,22 +1128,86 @@ packages: url: "https://pub.dev" source: hosted version: "3.2.1" + shared_preferences: + dependency: transitive + description: + name: shared_preferences + sha256: "396f85b8afc6865182610c0a2fc470853d56499f75f7499e2a73a9f0539d23d0" + url: "https://pub.dev" + source: hosted + version: "2.1.2" + shared_preferences_android: + dependency: transitive + description: + name: shared_preferences_android + sha256: "6478c6bbbecfe9aced34c483171e90d7c078f5883558b30ec3163cf18402c749" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + shared_preferences_foundation: + dependency: transitive + description: + name: shared_preferences_foundation + sha256: e014107bb79d6d3297196f4f2d0db54b5d1f85b8ea8ff63b8e8b391a02700feb + url: "https://pub.dev" + source: hosted + version: "2.2.2" + shared_preferences_linux: + dependency: transitive + description: + name: shared_preferences_linux + sha256: "9d387433ca65717bbf1be88f4d5bb18f10508917a8fa2fb02e0fd0d7479a9afa" + url: "https://pub.dev" + source: hosted + version: "2.2.0" + shared_preferences_platform_interface: + dependency: transitive + description: + name: shared_preferences_platform_interface + sha256: fb5cf25c0235df2d0640ac1b1174f6466bd311f621574997ac59018a6664548d + url: "https://pub.dev" + source: hosted + version: "2.2.0" + shared_preferences_web: + dependency: transitive + description: + name: shared_preferences_web + sha256: "74083203a8eae241e0de4a0d597dbedab3b8fef5563f33cf3c12d7e93c655ca5" + url: "https://pub.dev" + source: hosted + version: "2.1.0" + shared_preferences_windows: + dependency: transitive + description: + name: shared_preferences_windows + sha256: "5e588e2efef56916a3b229c3bfe81e6a525665a454519ca51dbcc4236a274173" + url: "https://pub.dev" + source: hosted + version: "2.2.0" shelf: dependency: transitive description: name: shelf - sha256: c24a96135a2ccd62c64b69315a14adc5c3419df63b4d7c05832a346fdb73682c + sha256: ad29c505aee705f41a4d8963641f91ac4cee3c8fad5947e033390a7bd8180fa4 url: "https://pub.dev" source: hosted - version: "1.4.0" + version: "1.4.1" shelf_web_socket: dependency: transitive description: name: shelf_web_socket - sha256: a988c0e8d8ffbdb8a28aa7ec8e449c260f3deb808781fe1284d22c5bba7156e8 + sha256: "9ca081be41c60190ebcb4766b2486a7d50261db7bd0f5d9615f2d653637a84c1" url: "https://pub.dev" source: hosted - version: "1.0.3" + version: "1.0.4" + shell_executor: + dependency: transitive + description: + name: shell_executor + sha256: f89eb17bb2660568a255bb5612f2773ba9295792d7bb6b96e3ef87985a9457fa + url: "https://pub.dev" + source: hosted + version: "0.1.4" shortid: dependency: transitive description: @@ -993,10 +1233,10 @@ packages: dependency: transitive description: name: source_gen - sha256: c2bea18c95cfa0276a366270afaa2850b09b4a76db95d546f3d003dcc7011298 + sha256: "373f96cf5a8744bc9816c1ff41cf5391bbdbe3d7a96fe98c622b6738a8a7bd33" url: "https://pub.dev" source: hosted - version: "1.2.7" + version: "1.3.2" source_helper: dependency: transitive description: @@ -1025,18 +1265,18 @@ packages: dependency: "direct main" description: name: stacked - sha256: "90604281b9e6d7edefa62b739b2ccf3895fa2a3ce589f252c41133e37ec38b5d" + sha256: f06ba899fd9cdddbd8c89ef8751dae2a9f2dac4c34ca6cf97d8819bc613ffe35 url: "https://pub.dev" source: hosted - version: "3.2.1" + version: "3.4.0" stacked_shared: dependency: transitive description: name: stacked_shared - sha256: "6021cb05b54595a64ca50e11e5649cbd09cc18131273dfcb6068c1dfa9ba664a" + sha256: "502f2b95d3857b0a0e7ebb77fef8c6ca8123c96fe67086ba3f6e66cbe695e703" url: "https://pub.dev" source: hosted - version: "1.3.1" + version: "1.3.2" stream_channel: dependency: transitive description: @@ -1073,10 +1313,10 @@ packages: dependency: transitive description: name: test_api - sha256: "6182294da5abf431177fccc1ee02401f6df30f766bc6130a0852c6b6d7ee6b2d" + sha256: eb6ac1540b26de412b3403a163d919ba86f6a973fe6cc50ae3541b80092fdcfb url: "https://pub.dev" source: hosted - version: "0.4.18" + version: "0.5.1" timing: dependency: transitive description: @@ -1097,34 +1337,50 @@ packages: dependency: transitive description: name: typed_data - sha256: "26f87ade979c47a150c9eaab93ccd2bebe70a27dc0b4b29517f2904f04eb11a5" + sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c url: "https://pub.dev" source: hosted - version: "1.3.1" + version: "1.3.2" + uni_ocr: + dependency: "direct main" + description: + name: uni_ocr + sha256: "36ad2309d59901a00e46c7e3896a3713370272543a447c3692cdb627aa4e748a" + url: "https://pub.dev" + source: hosted + version: "0.1.1" + uni_ocr_client: + dependency: transitive + description: + name: uni_ocr_client + sha256: "0508e0891008e6b240c0773a8cd73e5144f3a9d06e9db09787520b4cfb69535d" + url: "https://pub.dev" + source: hosted + version: "0.1.1" universal_io: dependency: transitive description: name: universal_io - sha256: "06866290206d196064fd61df4c7aea1ffe9a4e7c4ccaa8fcded42dd41948005d" + sha256: "1722b2dcc462b4b2f3ee7d188dad008b6eb4c40bbd03a3de451d82c78bba9aad" url: "https://pub.dev" source: hosted - version: "2.2.0" + version: "2.2.2" url_launcher: dependency: "direct main" description: name: url_launcher - sha256: "75f2846facd11168d007529d6cd8fcb2b750186bea046af9711f10b907e1587e" + sha256: eb1e00ab44303d50dd487aab67ebc575456c146c6af44422f9c13889984c00f3 url: "https://pub.dev" source: hosted - version: "6.1.10" + version: "6.1.11" url_launcher_android: dependency: transitive description: name: url_launcher_android - sha256: a52628068d282d01a07cd86e6ba99e497aa45ce8c91159015b2416907d78e411 + sha256: eed4e6a1164aa9794409325c3b707ff424d4d1c2a785e7db67f8bbda00e36e51 url: "https://pub.dev" source: hosted - version: "6.0.27" + version: "6.0.35" url_launcher_ios: dependency: transitive description: @@ -1137,10 +1393,10 @@ packages: dependency: transitive description: name: url_launcher_linux - sha256: "206fb8334a700ef7754d6a9ed119e7349bc830448098f21a69bf1b4ed038cabc" + sha256: "207f4ddda99b95b4d4868320a352d374b0b7e05eefad95a4a26f57da413443f5" url: "https://pub.dev" source: hosted - version: "3.0.4" + version: "3.0.5" url_launcher_macos: dependency: transitive description: @@ -1153,26 +1409,26 @@ packages: dependency: transitive description: name: url_launcher_platform_interface - sha256: "6c9ca697a5ae218ce56cece69d46128169a58aa8653c1b01d26fcd4aad8c4370" + sha256: bfdfa402f1f3298637d71ca8ecfe840b4696698213d5346e9d12d4ab647ee2ea url: "https://pub.dev" source: hosted - version: "2.1.2" + version: "2.1.3" url_launcher_web: dependency: transitive description: name: url_launcher_web - sha256: "81fe91b6c4f84f222d186a9d23c73157dc4c8e1c71489c4d08be1ad3b228f1aa" + sha256: "6bb1e5d7fe53daf02a8fee85352432a40b1f868a81880e99ec7440113d5cfcab" url: "https://pub.dev" source: hosted - version: "2.0.16" + version: "2.0.17" url_launcher_windows: dependency: transitive description: name: url_launcher_windows - sha256: a83ba3607a507758669cfafb03f9de09bf6e6280c14d9b9cb18f013e406dcacd + sha256: "254708f17f7c20a9c8c471f67d86d76d4a3f9c1591aad1e15292008aceb82771" url: "https://pub.dev" source: hosted - version: "3.0.5" + version: "3.0.6" uuid: dependency: transitive description: @@ -1193,26 +1449,26 @@ packages: dependency: transitive description: name: vector_graphics - sha256: ea8d3fc7b2e0f35de38a7465063ecfcf03d8217f7962aa2a6717132cb5d43a79 + sha256: "670f6e07aca990b4a2bcdc08a784193c4ccdd1932620244c3a86bb72a0eac67f" url: "https://pub.dev" source: hosted - version: "1.1.5" + version: "1.1.7" vector_graphics_codec: dependency: transitive description: name: vector_graphics_codec - sha256: a5eaa5d19e123ad4f61c3718ca1ed921c4e6254238d9145f82aa214955d9aced + sha256: "7451721781d967db9933b63f5733b1c4533022c0ba373a01bdd79d1a5457f69f" url: "https://pub.dev" source: hosted - version: "1.1.5" + version: "1.1.7" vector_graphics_compiler: dependency: transitive description: name: vector_graphics_compiler - sha256: "15edc42f7eaa478ce854eaf1fbb9062a899c0e4e56e775dd73b7f4709c97c4ca" + sha256: "80a13c613c8bde758b1464a1755a7b3a8f2b6cec61fbf0f5a53c94c30f03ba2e" url: "https://pub.dev" source: hosted - version: "1.1.5" + version: "1.1.7" vector_math: dependency: transitive description: @@ -1221,54 +1477,62 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.4" - wakelock: + wakelock_plus: dependency: "direct main" description: - name: wakelock - sha256: "769ecf42eb2d07128407b50cb93d7c10bd2ee48f0276ef0119db1d25cc2f87db" + name: wakelock_plus + sha256: aac3f3258f01781ec9212df94eecef1eb9ba9350e106728def405baa096ba413 url: "https://pub.dev" source: hosted - version: "0.6.2" - wakelock_macos: + version: "1.1.1" + wakelock_plus_platform_interface: dependency: transitive description: - name: wakelock_macos - sha256: "047c6be2f88cb6b76d02553bca5a3a3b95323b15d30867eca53a19a0a319d4cd" + name: wakelock_plus_platform_interface + sha256: "40fabed5da06caff0796dc638e1f07ee395fb18801fbff3255a2372db2d80385" url: "https://pub.dev" source: hosted - version: "0.4.0" - wakelock_platform_interface: + version: "1.1.0" + wallet: dependency: transitive description: - name: wakelock_platform_interface - sha256: "1f4aeb81fb592b863da83d2d0f7b8196067451e4df91046c26b54a403f9de621" + name: wallet + sha256: "569c91c2af13a9e1119c001f9c09218eccf3f383eb8d15ba13a5b558010c1bc0" url: "https://pub.dev" source: hosted - version: "0.3.0" - wakelock_web: - dependency: transitive + version: "0.0.12+1" + wallet_connect_uri_validator: + dependency: "direct main" description: - name: wakelock_web - sha256: "1b256b811ee3f0834888efddfe03da8d18d0819317f20f6193e2922b41a501b5" + name: wallet_connect_uri_validator + sha256: "447de085956b87f0ce0051a237ed44bbbb440db1266a7db6a27b8f3edfa13cec" url: "https://pub.dev" source: hosted - version: "0.4.0" - wakelock_windows: - dependency: transitive + version: "0.1.0" + walletconnect_flutter_v2: + dependency: "direct main" description: - name: wakelock_windows - sha256: "857f77b3fe6ae82dd045455baa626bc4b93cb9bb6c86bf3f27c182167c3a5567" + name: walletconnect_flutter_v2 + sha256: "87295fb305767bb089e6c5a78f273b52e30ade6eaa5fa89c3e103970f0ce6721" url: "https://pub.dev" source: hosted - version: "0.2.1" + version: "2.0.14" watcher: dependency: transitive description: name: watcher - sha256: "6a7f46926b01ce81bfc339da6a7f20afbe7733eff9846f6d6a5466aa4c6667c0" + sha256: "3d2ad6751b3c16cf07c7fca317a1413b3f26530319181b37e3b9039b84fc01d8" url: "https://pub.dev" source: hosted - version: "1.0.2" + version: "1.1.0" + web3dart: + dependency: transitive + description: + name: web3dart + sha256: eb4302857aec35f8eb51a54c4bcea40f0647f1160683291ec77bfeff6dae6c08 + url: "https://pub.dev" + source: hosted + version: "2.7.0" web_socket_channel: dependency: transitive description: @@ -1281,26 +1545,34 @@ packages: dependency: transitive description: name: win32 - sha256: a6f0236dbda0f63aa9a25ad1ff9a9d8a4eaaa5012da0dc59d21afdb1dc361ca4 + sha256: "5a751eddf9db89b3e5f9d50c20ab8612296e4e8db69009788d6c8b060a84191c" url: "https://pub.dev" source: hosted - version: "3.1.4" + version: "4.1.4" win32_registry: dependency: transitive description: name: win32_registry - sha256: "66e78552f17501aced68fe77425b13156998f1bd3d58f1cd8cd0af2dbe4520e3" + sha256: "1c52f994bdccb77103a6231ad4ea331a244dbcef5d1f37d8462f713143b0bfae" url: "https://pub.dev" source: hosted - version: "1.0.2" + version: "1.1.0" window_manager: dependency: "direct main" description: name: window_manager - sha256: "2b2572442b2a5178642730442dc625ac088244f5827b1f0811371b1b7485eb62" + sha256: "95096fede562cbb65f30d38b62d819a458f59ba9fe4a317f6cee669710f6676b" + url: "https://pub.dev" + source: hosted + version: "0.3.4" + x25519: + dependency: transitive + description: + name: x25519 + sha256: cec3c125f0d934dccba6c4cab48f3fbf866dc78895dcc5a1584d35b0a845005b url: "https://pub.dev" source: hosted - version: "0.3.2" + version: "0.1.1" xdg_directories: dependency: transitive description: @@ -1313,27 +1585,35 @@ packages: dependency: transitive description: name: xml - sha256: "979ee37d622dec6365e2efa4d906c37470995871fe9ae080d967e192d88286b5" + sha256: "5bc72e1e45e941d825fd7468b9b4cc3b9327942649aeb6fc5cdbf135f0a86e84" url: "https://pub.dev" source: hosted - version: "6.2.2" + version: "6.3.0" yaml: dependency: transitive description: name: yaml - sha256: "23812a9b125b48d4007117254bca50abb6c712352927eece9e155207b1db2370" + sha256: "75769501ea3489fca56601ff33454fe45507ea3bfb014161abc3b43ae25989d5" url: "https://pub.dev" source: hosted - version: "3.1.1" + version: "3.1.2" znn_sdk_dart: dependency: "direct main" description: path: "." - ref: master - resolved-ref: "3b485611c1e568dde50155562a5e855639d3417e" - url: "https://github.com/zenon-network/znn_sdk_dart.git" + ref: "2e445add298e841bf5a5bec2e3525e5c77fd0278" + resolved-ref: "2e445add298e841bf5a5bec2e3525e5c77fd0278" + url: "https://github.com/alienc0der/znn_sdk_dart.git" source: git version: "0.0.4" + zxing2: + dependency: "direct main" + description: + name: zxing2 + sha256: "1e141568c9646bc262fa75aacf739bc151ef6ad0226997c0016cc3da358a1bbc" + url: "https://pub.dev" + source: hosted + version: "0.2.0" sdks: - dart: ">=3.0.0-134.0.dev <4.0.0" - flutter: ">=3.7.0-0" + dart: ">=3.0.1 <4.0.0" + flutter: ">=3.10.0" diff --git a/pubspec.yaml b/pubspec.yaml index e05590c9..cd2b16a5 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,16 +1,16 @@ name: zenon_syrius_wallet_flutter description: Zenon cross-platform non-custodial wallet -version: 0.0.6 +version: 0.0.7 publish_to: none environment: - sdk: '>=2.14.0 <3.0.0' + sdk: '>=3.0.1' dependencies: flutter: sdk: flutter - fl_chart: ^0.60.0 + fl_chart: ^0.62.0 overlay_support: ^2.1.0 lottie: ^2.1.0 dotted_border: ^2.0.0+2 @@ -19,10 +19,10 @@ dependencies: expandable: ^5.0.1 marquee_widget: ^1.2.0 layout: ^1.0.2 - flutter_staggered_grid_view: 0.4.0 + flutter_staggered_grid_view: ^0.4.0 flip_card: ^0.7.0 auto_size_text: ^3.0.0 - window_manager: ^0.3.0 + window_manager: ^0.3.4 stacked: ^3.1.0 path_provider: ^2.0.8 rxdart: ^0.27.3 @@ -30,22 +30,22 @@ dependencies: url_launcher: ^6.0.18 provider: ^6.0.2 intl: ^0.18.0 - package_info_plus: ^3.0.2 + package_info_plus: ^4.0.2 device_info_plus: ^8.0.0 infinite_scroll_pagination: ^3.1.0 - share_plus: ^6.3.0 + share_plus: ^7.0.2 page_transition: ^2.0.4 file_selector: ^0.9.2+2 pretty_qr_code: ^2.0.3 - screenshot: ^1.3.0 + screenshot: ^2.1.0 desktop_drop: ^0.4.0 validators: ^3.0.0 - wakelock: ^0.6.1+2 + wakelock_plus: ^1.1.1 number_selector: ^1.0.5 znn_sdk_dart: git: - url: https://github.com/zenon-network/znn_sdk_dart.git - ref: master + url: https://github.com/alienc0der/znn_sdk_dart.git + ref: 2e445add298e841bf5a5bec2e3525e5c77fd0278 json_rpc_2: ^3.0.2 path: ^1.8.2 ffi: ^2.0.1 @@ -55,8 +55,19 @@ dependencies: tray_manager: ^0.2.0 open_filex: ^4.3.2 launch_at_startup: ^0.2.1 - logging: ^1.1.1 - + app_links: ^3.4.3 + logging: ^1.2.0 + walletconnect_flutter_v2: ^2.0.14 + preference_list: ^0.0.1 + screen_capturer: ^0.1.2 + uni_ocr: ^0.1.0 + zxing2: ^0.2.0 + image: ^4.0.10 + clipboard_watcher: ^0.2.0 + wallet_connect_uri_validator: ^0.1.0 + big_decimal: ^0.5.0 + ai_barcode_scanner: ^0.0.7 + dev_dependencies: flutter_test: sdk: flutter diff --git a/test/widget_test.dart b/test/widget_test.dart new file mode 100644 index 00000000..c17a98f8 --- /dev/null +++ b/test/widget_test.dart @@ -0,0 +1,30 @@ +// This is a basic Flutter widget test. +// +// To perform an interaction with a widget in your test, use the WidgetTester +// utility in the flutter_test package. For example, you can send tap and scroll +// gestures. You can also use WidgetTester to find child widgets in the widget +// tree, read text, and verify that the values of widget properties are correct. + +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import 'package:zenon_syrius_wallet_flutter/main.dart'; + +void main() { + testWidgets('Counter increments smoke test', (WidgetTester tester) async { + // Build our app and trigger a frame. + await tester.pumpWidget(const MyApp()); + + // Verify that our counter starts at 0. + expect(find.text('0'), findsOneWidget); + expect(find.text('1'), findsNothing); + + // Tap the '+' icon and trigger a frame. + await tester.tap(find.byIcon(Icons.add)); + await tester.pump(); + + // Verify that our counter has incremented. + expect(find.text('0'), findsNothing); + expect(find.text('1'), findsOneWidget); + }); +} diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc deleted file mode 100644 index bf871b3a..00000000 --- a/windows/flutter/generated_plugin_registrant.cc +++ /dev/null @@ -1,35 +0,0 @@ -// -// Generated file. Do not edit. -// - -// clang-format off - -#include "generated_plugin_registrant.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -void RegisterPlugins(flutter::PluginRegistry* registry) { - DesktopDropPluginRegisterWithRegistrar( - registry->GetRegistrarForPlugin("DesktopDropPlugin")); - FileSelectorWindowsRegisterWithRegistrar( - registry->GetRegistrarForPlugin("FileSelectorWindows")); - LocalNotifierPluginRegisterWithRegistrar( - registry->GetRegistrarForPlugin("LocalNotifierPlugin")); - ScreenRetrieverPluginRegisterWithRegistrar( - registry->GetRegistrarForPlugin("ScreenRetrieverPlugin")); - SharePlusWindowsPluginCApiRegisterWithRegistrar( - registry->GetRegistrarForPlugin("SharePlusWindowsPluginCApi")); - TrayManagerPluginRegisterWithRegistrar( - registry->GetRegistrarForPlugin("TrayManagerPlugin")); - UrlLauncherWindowsRegisterWithRegistrar( - registry->GetRegistrarForPlugin("UrlLauncherWindows")); - WindowManagerPluginRegisterWithRegistrar( - registry->GetRegistrarForPlugin("WindowManagerPlugin")); -} diff --git a/windows/flutter/generated_plugin_registrant.h b/windows/flutter/generated_plugin_registrant.h deleted file mode 100644 index dc139d85..00000000 --- a/windows/flutter/generated_plugin_registrant.h +++ /dev/null @@ -1,15 +0,0 @@ -// -// Generated file. Do not edit. -// - -// clang-format off - -#ifndef GENERATED_PLUGIN_REGISTRANT_ -#define GENERATED_PLUGIN_REGISTRANT_ - -#include - -// Registers Flutter plugins. -void RegisterPlugins(flutter::PluginRegistry* registry); - -#endif // GENERATED_PLUGIN_REGISTRANT_ diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake deleted file mode 100644 index ed4b229a..00000000 --- a/windows/flutter/generated_plugins.cmake +++ /dev/null @@ -1,31 +0,0 @@ -# -# Generated file, do not edit. -# - -list(APPEND FLUTTER_PLUGIN_LIST - desktop_drop - file_selector_windows - local_notifier - screen_retriever - share_plus - tray_manager - url_launcher_windows - window_manager -) - -list(APPEND FLUTTER_FFI_PLUGIN_LIST -) - -set(PLUGIN_BUNDLED_LIBRARIES) - -foreach(plugin ${FLUTTER_PLUGIN_LIST}) - add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/windows plugins/${plugin}) - target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) - list(APPEND PLUGIN_BUNDLED_LIBRARIES $) - list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) -endforeach(plugin) - -foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) - add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/windows plugins/${ffi_plugin}) - list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) -endforeach(ffi_plugin) diff --git a/windows/runner/Runner.rc b/windows/runner/Runner.rc index 19a2e0d8..2a4633a8 100644 --- a/windows/runner/Runner.rc +++ b/windows/runner/Runner.rc @@ -69,7 +69,7 @@ IDI_APP_ICON ICON "resources\\app_icon.ico" #ifdef FLUTTER_BUILD_NAME #define VERSION_AS_STRING #FLUTTER_BUILD_NAME #else -#define VERSION_AS_STRING "0.0.6-alphanet" +#define VERSION_AS_STRING "0.0.7-alphanet" #endif VS_VERSION_INFO VERSIONINFO @@ -90,7 +90,7 @@ BEGIN BLOCK "040904e4" BEGIN VALUE "CompanyName", "network.zenon.syrius" "\0" - VALUE "FileDescription", "s y r i u s (v0.0.6-alphanet)" "\0" + VALUE "FileDescription", "s y r i u s (v0.0.7-alphanet)" "\0" VALUE "FileVersion", VERSION_AS_STRING "\0" VALUE "InternalName", "syrius" "\0" VALUE "LegalCopyright", "(C) 2023 Zenon Network" "\0" diff --git a/windows/runner/runner.exe.manifest b/windows/runner/runner.exe.manifest index c977c4a4..a42ea768 100644 --- a/windows/runner/runner.exe.manifest +++ b/windows/runner/runner.exe.manifest @@ -7,7 +7,7 @@ - + diff --git a/windows/runner/win32_window.cpp b/windows/runner/win32_window.cpp index c10f08dc..10a458a3 100644 --- a/windows/runner/win32_window.cpp +++ b/windows/runner/win32_window.cpp @@ -4,6 +4,8 @@ #include "resource.h" +#include "app_links/app_links_plugin_c_api.h" + namespace { constexpr const wchar_t kWindowClassName[] = L"FLUTTER_RUNNER_WIN32_WINDOW"; @@ -105,6 +107,10 @@ Win32Window::~Win32Window() { bool Win32Window::CreateAndShow(const std::wstring& title, const Point& origin, const Size& size) { + if (SendAppLinkToInstance(title)) { + return false; + } + Destroy(); const wchar_t* window_class = @@ -129,6 +135,41 @@ bool Win32Window::CreateAndShow(const std::wstring& title, return OnCreate(); } +bool Win32Window::SendAppLinkToInstance(const std::wstring& title) { + // Find our exact window + HWND hwnd = ::FindWindow(kWindowClassName, title.c_str()); + + if (hwnd) { + // Dispatch new link to current window + SendAppLink(hwnd); + + // (Optional) Restore our window to front in same state + WINDOWPLACEMENT place = { sizeof(WINDOWPLACEMENT) }; + GetWindowPlacement(hwnd, &place); + + switch (place.showCmd) { + case SW_SHOWMAXIMIZED: + ShowWindow(hwnd, SW_SHOWMAXIMIZED); + break; + case SW_SHOWMINIMIZED: + ShowWindow(hwnd, SW_RESTORE); + break; + default: + ShowWindow(hwnd, SW_NORMAL); + break; + } + + SetWindowPos(0, HWND_TOP, 0, 0, 0, 0, SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE); + SetForegroundWindow(hwnd); + + // Window has been found, don't create another one. + return true; + } + + return false; +} + + // static LRESULT CALLBACK Win32Window::WndProc(HWND const window, UINT const message, diff --git a/windows/runner/win32_window.h b/windows/runner/win32_window.h index 17ba4311..9cfea10a 100644 --- a/windows/runner/win32_window.h +++ b/windows/runner/win32_window.h @@ -93,6 +93,11 @@ class Win32Window { // window handle for hosted content. HWND child_content_ = nullptr; + + // Dispatches link if any. + // This method enables our app to be with a single instance too. + // This is mandatory if you want to catch further links in same app. + bool SendAppLinkToInstance(const std::wstring& title); }; #endif // RUNNER_WIN32_WINDOW_H_