diff --git a/assets/lottie/ic_anim_live.json b/assets/lottie/ic_anim_live.json new file mode 100644 index 00000000..ab5210b4 --- /dev/null +++ b/assets/lottie/ic_anim_live.json @@ -0,0 +1 @@ +{"v":"5.7.5","fr":60,"ip":0,"op":60,"w":70,"h":70,"nm":"Live","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Ellipse 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[35,35,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[25,25],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"op","nm":"Offset Paths 1","a":{"a":0,"k":-0.5,"ix":1},"lj":1,"ml":{"a":0,"k":4,"ix":3},"ix":3,"mn":"ADBE Vector Filter - Offset","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1 Stroke","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[25,25],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.42745098039215684,1,0.403921568627451,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1 Fill","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":60,"st":-60,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Ellipse 3","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":60,"s":[100]},{"t":90,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[35,35,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":0,"s":[50,50,100]},{"t":90,"s":[150,150,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[45,45],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"op","nm":"Offset Paths 1","a":{"a":0,"k":-0.5,"ix":1},"lj":1,"ml":{"a":0,"k":4,"ix":3},"ix":3,"mn":"ADBE Vector Filter - Offset","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 2 Stroke","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[45,45],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.42745098039215684,1,0.403921568627451,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 2 Fill","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":60,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Ellipse 2","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[100]},{"t":30,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[35,35,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":-60,"s":[50,50,100]},{"t":30,"s":[150,150,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[45,45],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"op","nm":"Offset Paths 1","a":{"a":0,"k":-0.5,"ix":1},"lj":1,"ml":{"a":0,"k":4,"ix":3},"ix":3,"mn":"ADBE Vector Filter - Offset","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 2 Stroke","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[45,45],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.42745098039215684,1,0.403921568627451,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 2 Fill","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":60,"st":-60,"bm":0}],"markers":[]} \ No newline at end of file diff --git a/assets/lottie/ic_anim_plasma_generation.json b/assets/lottie/ic_anim_plasma_generation.json new file mode 100644 index 00000000..558b64f2 --- /dev/null +++ b/assets/lottie/ic_anim_plasma_generation.json @@ -0,0 +1 @@ +{"v":"5.7.1","fr":30,"ip":0,"op":80,"w":500,"h":500,"nm":"Plasma generation","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"thunder Outlines 2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[250,250,0],"ix":2},"a":{"a":0,"k":[250,250,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[95.758,-41.79],[21.469,-41.79],[22.96,-175.92],[-95.757,41.789],[-21.469,41.789],[-22.96,175.92]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"rd","nm":"Round Corners 1","r":{"a":0,"k":10,"ix":1},"ix":2,"mn":"ADBE Vector Filter - RC","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":0,"s":[100]},{"t":43,"s":[0]}],"ix":1},"e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":3,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[0.058823529412,1,0.56862745098,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":10,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[250,250],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":4,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":300,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"thunder Outlines","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[250,250,0],"ix":2},"a":{"a":0,"k":[250,250,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[95.758,-41.79],[21.469,-41.79],[22.96,-175.92],[-95.757,41.789],[-21.469,41.789],[-22.96,175.92]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"rd","nm":"Round Corners 1","r":{"a":0,"k":10,"ix":1},"ix":2,"mn":"ADBE Vector Filter - RC","hd":false},{"ty":"st","c":{"a":0,"k":[0.701960784314,0.701960784314,0.701960784314,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":2,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[250,250],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":300,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"thunder_2 Outlines","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":34,"s":[0]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":41,"s":[60]},{"t":49,"s":[100]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[250,250,0],"ix":2},"a":{"a":0,"k":[250,250,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[95.758,-41.79],[21.469,-41.79],[22.96,-175.92],[-95.757,41.789],[-21.469,41.789],[-22.96,175.92]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.058823529412,1,0.56862745098,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[250,250],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"rd","nm":"Round Corners 1","r":{"a":0,"k":10,"ix":1},"ix":2,"mn":"ADBE Vector Filter - RC","hd":false}],"ip":32,"op":322,"st":22,"bm":0}],"markers":[]} \ No newline at end of file diff --git a/lib/blocs/auto_receive_tx_worker.dart b/lib/blocs/auto_receive_tx_worker.dart index 48c109f1..74fdbf0b 100644 --- a/lib/blocs/auto_receive_tx_worker.dart +++ b/lib/blocs/auto_receive_tx_worker.dart @@ -8,13 +8,13 @@ import 'package:zenon_syrius_wallet_flutter/main.dart'; 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/global.dart'; import 'package:znn_sdk_dart/znn_sdk_dart.dart'; class AutoReceiveTxWorker extends BaseBloc { static AutoReceiveTxWorker? _instance; Queue pool = Queue(); - HashSet processedHashes = HashSet(); bool running = false; static AutoReceiveTxWorker getInstance() { @@ -22,11 +22,48 @@ class AutoReceiveTxWorker extends BaseBloc { return _instance!; } + Future autoReceiveTransactionHash(Hash currentHash) async { + if (!running) { + running = true; + try { + String toAddress = + (await zenon!.ledger.getAccountBlockByHash(currentHash))! + .toAddress + .toString(); + KeyPair keyPair = kKeyStore!.getKeyPair( + kDefaultAddressList.indexOf(toAddress), + ); + AccountBlockTemplate transactionParams = AccountBlockTemplate.receive( + currentHash, + ); + AccountBlockTemplate response = + await AccountBlockUtils.createAccountBlock( + transactionParams, + 'receive transaction', + blockSigningKey: keyPair, + waitForRequiredPlasma: true, + ); + _sendSuccessNotification(response, toAddress); + } on RpcException catch (e, stackTrace) { + _sendErrorNotification(e.toString()); + Logger('AutoReceiveTxWorker') + .log(Level.WARNING, 'autoReceive', e, stackTrace); + } + running = false; + } + } + Future autoReceive() async { + if (!sharedPrefsService!.get( + kAutoReceiveKey, + defaultValue: kAutoReceiveDefaultValue, + )) { + pool.clear(); + return; + } if (pool.isNotEmpty && !running) { running = true; Hash currentHash = pool.first; - pool.removeFirst(); try { String toAddress = (await zenon!.ledger.getAccountBlockByHash(currentHash))! @@ -46,6 +83,11 @@ class AutoReceiveTxWorker extends BaseBloc { waitForRequiredPlasma: true, ); _sendSuccessNotification(response, toAddress); + if (pool.isNotEmpty) { + pool.removeFirst(); + } + running = false; + autoReceive(); } on RpcException catch (e, stackTrace) { _sendErrorNotification(e.toString()); Logger('AutoReceiveTxWorker') @@ -59,6 +101,19 @@ class AutoReceiveTxWorker extends BaseBloc { } running = false; } + return; + } + + Future addHash(Hash hash) async { + zenon!.stats.syncInfo().then((syncInfo) { + if (!pool.contains(hash) && + (syncInfo.state == SyncState.syncDone || + (syncInfo.targetHeight > 0 && + syncInfo.currentHeight > 0 && + (syncInfo.targetHeight - syncInfo.currentHeight) < 3))) { + pool.add(hash); + } + }); } void _sendErrorNotification(String errorText) { @@ -83,19 +138,4 @@ class AutoReceiveTxWorker extends BaseBloc { ), ); } - - Future addHash(Hash hash) async { - if (!processedHashes.contains(hash)) { - zenon!.stats.syncInfo().then((syncInfo) { - if (!processedHashes.contains(hash) && - (syncInfo.state == SyncState.syncDone || - (syncInfo.targetHeight > 0 && - syncInfo.currentHeight > 0 && - (syncInfo.targetHeight - syncInfo.currentHeight) < 3))) { - pool.add(hash); - processedHashes.add(hash); - } - }); - } - } } diff --git a/lib/blocs/node_sync_status_bloc.dart b/lib/blocs/node_sync_status_bloc.dart index c012338d..b996ee91 100644 --- a/lib/blocs/node_sync_status_bloc.dart +++ b/lib/blocs/node_sync_status_bloc.dart @@ -19,9 +19,6 @@ class NodeSyncStatusBloc extends DashboardBaseBloc { (syncInfo.targetHeight - syncInfo.currentHeight) > 3))) { lastSyncState = syncInfo.state; if (syncInfo.state == SyncState.syncDone) { - NodeUtils.getUnreceivedTransactions().then((value) { - sl().autoReceive(); - }); Future.delayed(const Duration(seconds: 5)).then((value) { NodeUtils.getUnreceivedTransactions().then((value) { sl().autoReceive(); diff --git a/lib/blocs/notifications_bloc.dart b/lib/blocs/notifications_bloc.dart index cf8c3212..296ce0df 100644 --- a/lib/blocs/notifications_bloc.dart +++ b/lib/blocs/notifications_bloc.dart @@ -10,7 +10,9 @@ import 'package:zenon_syrius_wallet_flutter/utils/constants.dart'; class NotificationsBloc extends BaseBloc { Future addNotification(WalletNotification? notification) async { try { - await Hive.openBox(kNotificationsBox); + if (!Hive.box(kNotificationsBox).isOpen) { + await Hive.openBox(kNotificationsBox); + } Box notificationsBox = Hive.box(kNotificationsBox); if (notificationsBox.length >= kNotificationsEntriesLimit) { while (notificationsBox.length >= kNotificationsEntriesLimit) { diff --git a/lib/blocs/transfer/pending_transactions_bloc.dart b/lib/blocs/transfer/pending_transactions_bloc.dart new file mode 100644 index 00000000..b581d2ee --- /dev/null +++ b/lib/blocs/transfer/pending_transactions_bloc.dart @@ -0,0 +1,17 @@ +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/global.dart'; +import 'package:znn_sdk_dart/znn_sdk_dart.dart'; + +class PendingTransactionsBloc extends InfiniteScrollBloc { + @override + Future> getData(int pageKey, int pageSize) async => + (await zenon!.ledger.getUnreceivedBlocksByAddress( + Address.parse(kSelectedAddress!), + pageIndex: pageKey, + pageSize: pageSize, + )) + .list!; +} diff --git a/lib/blocs/wallet_connect/chains/i_chain.dart b/lib/blocs/wallet_connect/chains/i_chain.dart new file mode 100644 index 00000000..f3a3e36b --- /dev/null +++ b/lib/blocs/wallet_connect/chains/i_chain.dart @@ -0,0 +1,5 @@ +abstract class IChain { + String getNamespace(); + String getChainId(); + List getEvents(); +} diff --git a/lib/blocs/wallet_connect/chains/nom_service.dart b/lib/blocs/wallet_connect/chains/nom_service.dart new file mode 100644 index 00000000..186a2e6e --- /dev/null +++ b/lib/blocs/wallet_connect/chains/nom_service.dart @@ -0,0 +1,316 @@ +import 'package:flutter/material.dart'; +import 'package:walletconnect_flutter_v2/walletconnect_flutter_v2.dart'; +import 'package:window_manager/window_manager.dart'; +import 'package:zenon_syrius_wallet_flutter/blocs/transfer/send_payment_bloc.dart'; +import 'package:zenon_syrius_wallet_flutter/blocs/wallet_connect/chains/i_chain.dart'; +import 'package:zenon_syrius_wallet_flutter/main.dart'; +import 'package:zenon_syrius_wallet_flutter/services/i_web3wallet_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/extensions.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/notification_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'; + +enum NoMChainId { + mainnet, + testnet, +} + +extension NoMChainIdX on NoMChainId { + String chain() { + String name = ''; + + switch (this) { + case NoMChainId.mainnet: + name = '1'; + break; + case NoMChainId.testnet: + name = '3'; + break; + } + + return '${NoMService.namespace}:$name'; + } +} + +class NoMService extends IChain { + static const namespace = 'zenon'; + + final IWeb3WalletService _web3WalletService = sl(); + + final NoMChainId reference; + + final _walletLockedError = const WalletConnectError( + code: 9000, + message: 'Wallet is locked', + ); + + Web3Wallet? wallet; + + NoMService({ + required this.reference, + }) { + wallet = _web3WalletService.getWeb3Wallet(); + + // Register event emitters + // wallet!.registerEventEmitter(chainId: getChainId(), event: 'chainIdChange'); + // wallet!.registerEventEmitter(chainId: getChainId(), event: 'addressChange'); + + // Register request handlers + wallet!.registerRequestHandler( + chainId: getChainId(), + method: 'znn_info', + handler: _methodZnnInfo, + ); + wallet!.registerRequestHandler( + chainId: getChainId(), + method: 'znn_sign', + handler: _methodZnnSign, + ); + wallet!.registerRequestHandler( + chainId: getChainId(), + method: 'znn_send', + handler: _methodZnnSend, + ); + } + + @override + String getNamespace() { + return namespace; + } + + @override + String getChainId() { + return reference.chain(); + } + + @override + List getEvents() { + return ['chainIdChange', 'addressChange']; + } + + Future _methodZnnInfo(String topic, dynamic params) async { + if (!await windowManager.isFocused() || !await windowManager.isVisible()) { + windowManager.show(); + } + final dAppMetadata = wallet! + .getActiveSessions() + .values + .firstWhere((element) => element.topic == topic) + .peer + .metadata; + + if (kCurrentPage != Tabs.lock) { + if (globalNavigatorKey.currentContext!.mounted) { + final actionWasAccepted = await showDialogWithNoAndYesOptions( + context: globalNavigatorKey.currentContext!, + 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; + } + } + + Future _methodZnnSign(String topic, dynamic params) async { + if (!await windowManager.isFocused() || !await windowManager.isVisible()) { + windowManager.show(); + } + final dAppMetadata = wallet! + .getActiveSessions() + .values + .firstWhere((element) => element.topic == topic) + .peer + .metadata; + if (kCurrentPage != Tabs.lock) { + final message = params as String; + + if (globalNavigatorKey.currentContext!.mounted) { + final actionWasAccepted = await showDialogWithNoAndYesOptions( + context: globalNavigatorKey.currentContext!, + 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; + } + } + + Future _methodZnnSend(String topic, dynamic params) async { + if (!await windowManager.isFocused() || !await windowManager.isVisible()) { + windowManager.show(); + } + final dAppMetadata = wallet! + .getActiveSessions() + .values + .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 (globalNavigatorKey.currentContext!.mounted) { + final wasActionAccepted = await showDialogWithNoAndYesOptions( + context: globalNavigatorKey.currentContext!, + 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; + } + } +} diff --git a/lib/blocs/wallet_connect/wallet_connect_pairings_bloc.dart b/lib/blocs/wallet_connect/wallet_connect_pairings_bloc.dart index e9cf29f3..43d098a4 100644 --- a/lib/blocs/wallet_connect/wallet_connect_pairings_bloc.dart +++ b/lib/blocs/wallet_connect/wallet_connect_pairings_bloc.dart @@ -1,7 +1,7 @@ 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'; +import 'package:zenon_syrius_wallet_flutter/services/i_web3wallet_service.dart'; class WalletConnectPairingsBloc extends InfiniteScrollBloc { WalletConnectPairingsBloc() : super(isDataRequestPaginated: false); @@ -9,6 +9,6 @@ class WalletConnectPairingsBloc extends InfiniteScrollBloc { @override Future> getData(int pageKey, int pageSize) => Future.delayed(const Duration(milliseconds: 500)).then( - (value) => sl.get().pairings, + (value) => sl.get().pairings.value, ); } diff --git a/lib/blocs/wallet_connect/wallet_connect_sessions_bloc.dart b/lib/blocs/wallet_connect/wallet_connect_sessions_bloc.dart index 12fbb1f2..4d91c736 100644 --- a/lib/blocs/wallet_connect/wallet_connect_sessions_bloc.dart +++ b/lib/blocs/wallet_connect/wallet_connect_sessions_bloc.dart @@ -1,16 +1,16 @@ 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'; +import 'package:zenon_syrius_wallet_flutter/services/i_web3wallet_service.dart'; class WalletConnectSessionsBloc extends InfiniteScrollBloc { WalletConnectSessionsBloc() : super(isDataRequestPaginated: false); @override Future> getData(int pageKey, int pageSize) async { - final wcService = sl.get(); + final wcService = sl.get(); final sessions = []; - for (var pairing in wcService.pairings) { + for (var pairing in wcService.pairings.value) { sessions.addAll( wcService.getSessionsForPairing(pairing.topic).values, ); diff --git a/lib/embedded_node/blobs/libznn.dll b/lib/embedded_node/blobs/libznn.dll index d70ba78b..d3f1a204 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 6886747d..314df7db 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 9d8bb6bb..e7ffee19 100755 Binary files a/lib/embedded_node/blobs/libznn.so and b/lib/embedded_node/blobs/libznn.so differ diff --git a/lib/main.dart b/lib/main.dart index d593018a..02b22605 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -16,12 +16,15 @@ 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/chains/i_chain.dart'; +import 'package:zenon_syrius_wallet_flutter/blocs/wallet_connect/chains/nom_service.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/i_web3wallet_service.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/services/web3wallet_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'; @@ -31,6 +34,9 @@ SharedPrefsService? sharedPrefsService; final sl = GetIt.instance; +IWeb3WalletService? web3WalletService; +final globalNavigatorKey = GlobalKey(); + main() async { WidgetsFlutterBinding.ensureInitialized(); @@ -66,9 +72,14 @@ main() async { windowManager.ensureInitialized(); await windowManager.setPreventClose(true); + web3WalletService = Web3WalletService(); + web3WalletService!.create(); + // Setup services setup(); + await web3WalletService!.init(); + // Setup local_notifier await localNotifier.setup( appName: 's y r i u s', @@ -159,7 +170,14 @@ void setup() { zenon = sl(); sl.registerLazySingletonAsync( (() => SharedPrefsService.getInstance().then((value) => value!))); - sl.registerLazySingleton(() => WalletConnectService()); + + // Initialize WalletConnect service + sl.registerSingleton(web3WalletService!); + sl.registerSingleton( + NoMService(reference: NoMChainId.mainnet), + instanceName: NoMChainId.mainnet.chain(), + ); + sl.registerSingleton(AutoReceiveTxWorker.getInstance()); sl.registerSingleton(ReceivePort(), @@ -267,6 +285,7 @@ class _MyAppState extends State with WindowListener, TrayListener { child: Layout( child: MaterialApp( title: 's y r i u s', + navigatorKey: globalNavigatorKey, debugShowCheckedModeBanner: false, theme: AppTheme.lightTheme, darkTheme: AppTheme.darkTheme, diff --git a/lib/screens/node_management_screen.dart b/lib/screens/node_management_screen.dart index 507f53ab..0fd192fa 100644 --- a/lib/screens/node_management_screen.dart +++ b/lib/screens/node_management_screen.dart @@ -25,16 +25,21 @@ class NodeManagementScreen extends StatefulWidget { } class _NodeManagementScreenState extends State { - String? _selectedNode; - final GlobalKey _confirmNodeButtonKey = GlobalKey(); final GlobalKey _addNodeButtonKey = GlobalKey(); + GlobalKey _newNodeKey = GlobalKey(); TextEditingController _newNodeController = TextEditingController(); - GlobalKey _newNodeKey = GlobalKey(); + String? _selectedNode; late String _selectedNodeConfirmed; + @override + void initState() { + super.initState(); + kDefaultCommunityNodes.shuffle(); + } + @override void didChangeDependencies() { super.didChangeDependencies(); @@ -61,8 +66,11 @@ class _NodeManagementScreenState extends State { ), kVerticalSpacing, Text( - 'By default Syrius connects to its own built-in full node, which is called the Embedded Node. If you want to connect to a different node, you can add one below. Otherwise just connect and continue.', - style: Theme.of(context).textTheme.headlineMedium, + 'By default Syrius connects to its own built-in full node, which is called the Embedded Node. ' + 'It may take up to 24 hours to fully sync the network via the embedded node. ' + 'During this time, you cannot send or receive transactions.\n\n' + 'It you want to get started right away, please connect to a community node.', + style: Theme.of(context).textTheme.headlineSmall, textAlign: TextAlign.center, ), SizedBox( @@ -231,7 +239,8 @@ class _NodeManagementScreenState extends State { InputValidators.node(_newNodeController.text) == null; void _onAddNodePressed() async { - if ([...kDbNodes, ...kDefaultNodes].contains(_newNodeController.text)) { + if ([...kDbNodes, ...kDefaultCommunityNodes, ...kDefaultNodes] + .contains(_newNodeController.text)) { NotificationUtils.sendNotificationError( 'Node already exists', 'Node already exists'); } else { @@ -261,8 +270,11 @@ class _NodeManagementScreenState extends State { Widget _getNodeTiles() { return Column( - children: - [...kDefaultNodes, ...kDbNodes].map((e) => _getNodeTile(e)).toList(), + children: { + ...kDefaultNodes, + ...kDefaultCommunityNodes, + ...kDbNodes + }.toList().map((e) => _getNodeTile(e)).toList(), ); } diff --git a/lib/services/i_web3wallet_service.dart b/lib/services/i_web3wallet_service.dart new file mode 100644 index 00000000..f7756c1f --- /dev/null +++ b/lib/services/i_web3wallet_service.dart @@ -0,0 +1,26 @@ +import 'package:flutter/material.dart'; +import 'package:get_it/get_it.dart'; +import 'package:walletconnect_flutter_v2/walletconnect_flutter_v2.dart'; + +abstract class IWeb3WalletService extends Disposable { + abstract ValueNotifier> pairings; + abstract ValueNotifier> sessions; + abstract ValueNotifier> auth; + + void create(); + Future init(); + Web3Wallet getWeb3Wallet(); + Future pair(Uri uri); + Future activatePairing({ + required String topic, + }); + Future deactivatePairing({ + required String topic, + }); + Map getSessionsForPairing(String pairingTopic); + Map getActiveSessions(); + Future disconnectSessions(); + Future disconnectSession({required String topic}); + Future emitAddressChangeEvent(String newAddress); + Future emitChainIdChangeEvent(String newChainId); +} diff --git a/lib/services/wallet_connect_service.dart b/lib/services/wallet_connect_service.dart deleted file mode 100644 index 868e260c..00000000 --- a/lib/services/wallet_connect_service.dart +++ /dev/null @@ -1,661 +0,0 @@ -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 = []; - - 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) { - if (_wcClient == null) { - _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, '_wcClient already initialized'); - } - } 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()); - - if (!_wcClient!.core.relayClient.isConnected) { - _wcClient!.core.relayClient.connect(); - Logger('WalletConnectService') - .log(Level.INFO, 'relayClient reconnect', args.toString()); - } - }); - - _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()); - sl.get().refreshResults(); - }); - - _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!.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); - try { - 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); - } catch (e, stackTrace) { - NotificationUtils.sendNotificationError( - e, 'WalletConnect session approval failed'); - Logger('WalletConnectService').log( - Level.INFO, 'onSessionProposal approveResponse', e, stackTrace); - } - } - } else { - await _wcClient!.rejectSession( - id: event.id, - reason: Errors.getSdkError( - Errors.USER_REJECTED, - ), - ); - } - } - } - - void _initialChecks() { - if (!_wcClient!.core.relayClient.isConnected) { - _wcClient!.core.relayClient.connect(); - Logger('WalletConnectService').log(Level.INFO, 'relayClient connect'); - } - 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/services/web3wallet_service.dart b/lib/services/web3wallet_service.dart new file mode 100644 index 00000000..d5673a8c --- /dev/null +++ b/lib/services/web3wallet_service.dart @@ -0,0 +1,447 @@ +import 'dart:async'; + +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/notifications_bloc.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/database/notification_type.dart'; +import 'package:zenon_syrius_wallet_flutter/model/database/wallet_notification.dart'; +import 'package:zenon_syrius_wallet_flutter/services/i_web3wallet_service.dart'; +import 'package:zenon_syrius_wallet_flutter/utils/utils.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 Web3WalletService extends IWeb3WalletService { + Web3Wallet? _wcClient; + + /// The list of requests from the dapp + /// Potential types include, but aren't limited to: + /// [SessionProposalEvent], [AuthRequest] + @override + ValueNotifier> pairings = + ValueNotifier>([]); + @override + ValueNotifier> sessions = + ValueNotifier>([]); + @override + ValueNotifier> auth = ValueNotifier>([]); + + final List _idSessionsApproved = []; + + @override + void create() { + if (kWcProjectId.isNotEmpty) { + _wcClient = Web3Wallet( + core: Core( + 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' + ], + ), + ); + + // Setup our listeners + _wcClient!.core.relayClient.onRelayClientConnect + .subscribe(_onRelayClientConnect); + _wcClient!.core.relayClient.onRelayClientDisconnect + .subscribe(_onRelayClientDisconnect); + _wcClient!.core.relayClient.onRelayClientError + .subscribe(_onRelayClientError); + + _wcClient!.core.pairing.onPairingCreate.subscribe(_onPairingCreate); + _wcClient!.core.pairing.onPairingActivate.subscribe(_onPairingActivate); + _wcClient!.core.pairing.onPairingPing.subscribe(_onPairingPing); + _wcClient!.core.pairing.onPairingInvalid.subscribe(_onPairingInvalid); + _wcClient!.core.pairing.onPairingDelete.subscribe(_onPairingDelete); + + _wcClient!.pairings.onSync.subscribe(_onPairingsSync); + _wcClient!.sessions.onSync.subscribe(_onSessionsSync); + + _wcClient!.onSessionProposal.subscribe(_onSessionProposal); + _wcClient!.onSessionConnect.subscribe(_onSessionConnect); + _wcClient!.onSessionRequest.subscribe(_onSessionRequest); + _wcClient!.onSessionProposalError.subscribe(_onSessionProposalError); + _wcClient!.onSessionDelete.subscribe(_onSessionDelete); + // _wcClient!.onAuthRequest.subscribe(_onAuthRequest); + } else { + Logger('WalletConnectService').log(Level.INFO, 'kWcProjectId missing'); + } + } + + @override + Future init() async { + // Await the initialization of the web3wallet + Logger('WalletConnectService').log(Level.INFO, 'initialization'); + await _wcClient!.init(); + + pairings.value = _wcClient!.pairings.getAll(); + sessions.value = _wcClient!.sessions.getAll(); + auth.value = _wcClient!.completeRequests.getAll(); + } + + @override + FutureOr onDispose() { + _wcClient!.core.relayClient.onRelayClientConnect + .unsubscribe(_onRelayClientConnect); + _wcClient!.core.relayClient.onRelayClientDisconnect + .unsubscribe(_onRelayClientDisconnect); + _wcClient!.core.relayClient.onRelayClientError + .unsubscribe(_onRelayClientError); + + _wcClient!.core.pairing.onPairingCreate.unsubscribe(_onPairingCreate); + _wcClient!.core.pairing.onPairingActivate.unsubscribe(_onPairingActivate); + _wcClient!.core.pairing.onPairingPing.unsubscribe(_onPairingPing); + _wcClient!.core.pairing.onPairingInvalid.unsubscribe(_onPairingInvalid); + _wcClient!.core.pairing.onPairingDelete.unsubscribe(_onPairingDelete); + + _wcClient!.pairings.onSync.unsubscribe(_onPairingsSync); + _wcClient!.sessions.onSync.unsubscribe(_onSessionsSync); + + _wcClient!.onSessionProposal.unsubscribe(_onSessionProposal); + _wcClient!.onSessionConnect.unsubscribe(_onSessionConnect); + _wcClient!.onSessionRequest.unsubscribe(_onSessionRequest); + _wcClient!.onSessionProposalError.unsubscribe(_onSessionProposalError); + _wcClient!.onSessionDelete.unsubscribe(_onSessionDelete); + // _wcClient!.onAuthRequest.unsubscribe(_onAuthRequest); + + pairings.dispose(); + sessions.dispose(); + auth.dispose(); + } + + @override + Web3Wallet getWeb3Wallet() { + return _wcClient!; + } + + @override + Future pair(Uri uri) { + return _wcClient!.pair(uri: uri); + } + + @override + Future activatePairing({ + required String topic, + }) { + return _wcClient!.core.pairing.activate( + topic: topic, + ); + } + + @override + Future deactivatePairing({ + required String topic, + }) async { + try { + _wcClient!.core.pairing.disconnect(topic: topic); + _idSessionsApproved.clear(); + } 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'); + } + } + + @override + Map getSessionsForPairing(String pairingTopic) { + return _wcClient!.getSessionsForPairing( + pairingTopic: pairingTopic, + ); + } + + @override + Future emitAddressChangeEvent(String newAddress) { + return _emitEventPairedDApps( + changeName: 'addressChange', + newValue: newAddress, + ); + } + + @override + Future emitChainIdChangeEvent(String newChainId) { + return _emitEventPairedDApps( + changeName: 'chainIdChange', + newValue: newChainId, + ); + } + + @override + Future disconnectSessions() async { + Logger('WalletConnectService') + .log(Level.INFO, 'disconnectSessions triggered'); + for (int i = 0; i < pairings.value.length; i++) { + await _wcClient!.disconnectSession( + topic: pairings.value[i].topic, + reason: Errors.getSdkError(Errors.USER_DISCONNECTED)); + } + _idSessionsApproved.clear(); + } + + @override + Future disconnectSession({required String topic}) async { + Logger('WalletConnectService') + .log(Level.INFO, 'disconnectSession triggered', topic); + _wcClient!.disconnectSession( + topic: topic, + reason: Errors.getSdkError(Errors.USER_DISCONNECTED), + ); + } + + @override + Map getActiveSessions() { + Logger('WalletConnectService') + .log(Level.INFO, 'getActiveSessions triggered'); + return _wcClient!.getActiveSessions(); + } + + void _onRelayClientConnect(var args) async { + Logger('WalletConnectService') + .log(Level.INFO, '_onRelayClientConnect triggered', args.toString()); + } + + void _onRelayClientDisconnect(var args) async { + Logger('WalletConnectService') + .log(Level.INFO, '_onRelayClientDisconnect triggered', args.toString()); + } + + void _onRelayClientError(var args) async { + Logger('WalletConnectService') + .log(Level.INFO, '_onRelayClientError triggered', args.toString()); + } + + void _onSessionsSync(StoreSyncEvent? args) async { + if (args != null) { + Logger('WalletConnectService') + .log(Level.INFO, '_onSessionsSync triggered', args.toString()); + sessions.value = _wcClient!.sessions.getAll(); + } + } + + void _onPairingCreate(PairingEvent? args) async { + Logger('WalletConnectService') + .log(Level.INFO, 'onPairingCreate triggered', args.toString()); + sl.get().refreshResults(); + } + + void _onPairingActivate(PairingActivateEvent? args) async { + Logger('WalletConnectService') + .log(Level.INFO, '_onPairingActivate triggered', args.toString()); + sl.get().refreshResults(); + } + + void _onPairingPing(PairingEvent? args) async { + Logger('WalletConnectService') + .log(Level.INFO, '_onPairingPing triggered', args.toString()); + } + + void _onPairingInvalid(PairingInvalidEvent? args) { + Logger('WalletConnectService') + .log(Level.INFO, 'onPairingInvalid triggered', args.toString()); + } + + void _onPairingDelete(PairingEvent? args) async { + Logger('WalletConnectService') + .log(Level.INFO, '_onPairingDelete triggered', args.toString()); + } + + void _onPairingsSync(StoreSyncEvent? args) async { + if (args != null) { + Logger('WalletConnectService') + .log(Level.INFO, '_onPairingsSync triggered', args.toString()); + pairings.value = _wcClient!.pairings.getAll(); + } + } + + void _onSessionRequest(SessionRequestEvent? args) async { + Logger('WalletConnectService') + .log(Level.INFO, '_onSessionRequest triggered', args.toString()); + } + + void _onSessionDelete(SessionDelete? args) { + Logger('WalletConnectService') + .log(Level.INFO, '_onSessionDelete triggered', args.toString()); + sl.get().refreshResults(); + } + + void _onSessionProposalError(SessionProposalErrorEvent? args) { + Logger('WalletConnectService') + .log(Level.INFO, '_onSessionProposalError triggered', args.toString()); + sl.get().refreshResults(); + } + + void _onSessionProposal(SessionProposalEvent? event) async { + Logger('WalletConnectService') + .log(Level.INFO, '_onSessionProposal triggered', event.toString()); + + if (event != null) { + Logger('WalletConnectService') + .log(Level.INFO, '_onSessionProposal event', event.params.toJson()); + + final dAppMetadata = event.params.proposer.metadata; + + final actionWasAccepted = await showDialogWithNoAndYesOptions( + context: globalNavigatorKey.currentContext!, + 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: () async {}, + ); + + if (actionWasAccepted) { + if (!_idSessionsApproved.contains(event.id)) { + _idSessionsApproved.add(event.id); + try { + ApproveResponse approveResponse = + await _approveSession(id: event.id); + _sendSuccessfullyApprovedSessionNotification(dAppMetadata); + sessions.value.add(approveResponse.session); + } catch (e, stackTrace) { + NotificationUtils.sendNotificationError( + e, 'WalletConnect session approval failed'); + Logger('WalletConnectService').log( + Level.INFO, 'onSessionProposal approveResponse', e, stackTrace); + } + } + } else { + await _wcClient!.rejectSession( + id: event.id, + reason: Errors.getSdkError( + Errors.USER_REJECTED, + ), + ); + } + } + } + + void _onSessionConnect(SessionConnect? args) { + if (args != null) { + sessions.value.add(args.session); + } + Logger('WalletConnectService') + .log(Level.INFO, '_onSessionConnect triggered', args.toString()); + Future.delayed(const Duration(seconds: 3)) + .then((value) => sl.get().refreshResults()); + } + + 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, + ), + ); + } + + 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, + ); + } + + String _generateAccount(String address, int chainId) => + '$kZenonNameSpace:1:$address'; + + List _getWalletAccounts() => kAddressLabelMap.values + .map( + (address) => _generateAccount(address, getChainIdentifier()), + ) + .toList(); + + Future _emitEventPairedDApps({ + required String changeName, + required String newValue, + }) async { + final sessionTopics = + pairings.value.fold>([], (previousValue, pairing) { + if (pairing.active) { + previousValue.addAll(getSessionsForPairing(pairing.topic).keys); + return previousValue; + } + return previousValue; + }); + + for (String sessionTopic in sessionTopics) { + _emitDAppEvent( + sessionTopic: sessionTopic, + changeName: changeName, + newValue: newValue, + ); + } + } + + Future _emitDAppEvent({ + required String sessionTopic, + required String changeName, + required String newValue, + }) { + return _wcClient!.emitSessionEvent( + topic: sessionTopic, + chainId: 'zenon:1', + event: SessionEventParams( + name: changeName, + data: newValue, + ), + ); + } +} diff --git a/lib/utils/constants.dart b/lib/utils/constants.dart index d7690841..6b0ec4de 100644 --- a/lib/utils/constants.dart +++ b/lib/utils/constants.dart @@ -104,12 +104,12 @@ const int kIssueTokenPlasmaAmountNeeded = 189000; const int kAmountInputMaxCharacterLength = 21; const int kSecondsPerMomentum = 10; -final List kNormalUsersPlasmaRequirements = [ +const List kNormalUsersPlasmaRequirements = [ kStakePlasmaAmountNeeded, kDelegatePlasmaAmountNeeded, ]; -final List kPowerUsersPlasmaRequirements = [ +const List kPowerUsersPlasmaRequirements = [ kPillarPlasmaAmountNeeded, kSentinelPlasmaAmountNeeded, kIssueTokenPlasmaAmountNeeded, @@ -118,11 +118,6 @@ final List kPowerUsersPlasmaRequirements = [ const String kLocalhostDefaultNodeUrl = 'ws://127.0.0.1:$kDefaultPort'; const int kDefaultPort = 35998; -List kDefaultNodes = [ - 'Embedded Node', - kLocalhostDefaultNodeUrl, -]; - const List kWalletActions = [ 'pillar', 'sentinel', @@ -172,9 +167,12 @@ const String kAutoEraseNumAttemptsKey = 'auto_erase_num_attempts'; const String kLaunchAtStartupKey = 'launch_at_startup'; const String kEnableDesktopNotificationsKey = 'enable_desktop_notifications'; const String kEnableClipboardWatcherKey = 'enable_clipboard_watcher'; +const String kAutoReceiveKey = 'enable_auto_receive'; + const bool kLaunchAtStartupDefaultValue = false; const bool kEnableDesktopNotificationsDefaultValue = false; const bool kEnableClipboardWatcherDefaultValue = false; +const bool kAutoReceiveDefaultValue = true; /// Node management const String kChainIdKey = 'chain_id'; diff --git a/lib/utils/format_utils.dart b/lib/utils/format_utils.dart index 3379f589..be51662c 100644 --- a/lib/utils/format_utils.dart +++ b/lib/utils/format_utils.dart @@ -58,4 +58,24 @@ class FormatUtils { ) .millisecondsSinceEpoch; } + + static String formatData(int transactionMillis) { + int currentMillis = DateTime.now().millisecondsSinceEpoch; + if (currentMillis - transactionMillis <= + const Duration(days: 1).inMilliseconds) { + return formatDataShort(currentMillis - transactionMillis); + } + return FormatUtils.formatDate(transactionMillis, dateFormat: 'MM/dd/yyyy'); + } + + static String formatDataShort(int i) { + Duration duration = Duration(milliseconds: i); + if (duration.inHours > 0) { + return '${duration.inHours} h ago'; + } + if (duration.inMinutes > 0) { + return '${duration.inMinutes} min ago'; + } + return '${duration.inSeconds} s ago'; + } } diff --git a/lib/utils/global.dart b/lib/utils/global.dart index 86f221a6..7a592324 100644 --- a/lib/utils/global.dart +++ b/lib/utils/global.dart @@ -48,10 +48,22 @@ final List kTabsWithIconTitles = [ Tabs.help, Tabs.notifications, Tabs.settings, - Tabs.resyncWallet, + Tabs.generation, + Tabs.sync, Tabs.lock, ]; final List kDisabledTabs = [ - Tabs.resyncWallet, + Tabs.generation, + Tabs.sync, +]; + +List kDefaultNodes = [ + 'Embedded Node', + kLocalhostDefaultNodeUrl, +]; + +// Community supplied public full nodes +List kDefaultCommunityNodes = [ + 'wss://my.hc1node.com:35998', ]; diff --git a/lib/utils/init_utils.dart b/lib/utils/init_utils.dart index b8996667..5b10da5c 100644 --- a/lib/utils/init_utils.dart +++ b/lib/utils/init_utils.dart @@ -4,7 +4,6 @@ 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'; @@ -26,8 +25,9 @@ class InitUtils { _setChainId(); await NodeUtils.loadDbNodes(); - // Initialize WalletConnect client - sl.get().initClient(); + await _openFavoriteTokensBox(); + await _openNotificationsBox(); + await _openRecipientBox(); } catch (e) { rethrow; } @@ -78,9 +78,7 @@ class InitUtils { zenon!.defaultKeyPair = kKeyStore!.getKeyPair( kDefaultAddressList.indexOf(kSelectedAddress), ); - await _openFavoriteTokensBox(); - await _openNotificationsBox(); - await _openRecipientBox(); + await NodeUtils.initWebSocketClient(); await _setWalletVersion(); kWalletInitCompleted = true; diff --git a/lib/utils/node_utils.dart b/lib/utils/node_utils.dart index 294540ec..9719ad42 100644 --- a/lib/utils/node_utils.dart +++ b/lib/utils/node_utils.dart @@ -98,6 +98,7 @@ class NodeUtils { await _getSubscriptionForMomentums(); await _getSubscriptionForAllAccountEvents(); await getUnreceivedTransactions(); + sl().autoReceive(); Future.delayed(const Duration(seconds: 30)) .then((value) => NotificationUtils.sendNodeSyncingNotification()); @@ -125,7 +126,12 @@ class NodeUtils { if (unreceivedBlocks.isNotEmpty) { for (AccountBlock unreceivedBlock in unreceivedBlocks) { - sl().addHash(unreceivedBlock.hash); + if (sharedPrefsService!.get( + kAutoReceiveKey, + defaultValue: kAutoReceiveDefaultValue, + )) { + sl().addHash(unreceivedBlock.hash); + } } } } diff --git a/lib/utils/notifiers/default_address_notifier.dart b/lib/utils/notifiers/default_address_notifier.dart index 1117ba40..6300d623 100644 --- a/lib/utils/notifiers/default_address_notifier.dart +++ b/lib/utils/notifiers/default_address_notifier.dart @@ -1,12 +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/services/i_web3wallet_service.dart'; import 'package:zenon_syrius_wallet_flutter/utils/global.dart'; class SelectedAddressNotifier extends ChangeNotifier { void changeSelectedAddress(String? newSelectedAddress) { kSelectedAddress = newSelectedAddress; - sl().emitAddressChangeEvent(newSelectedAddress!); + sl().emitAddressChangeEvent(newSelectedAddress!); notifyListeners(); } } diff --git a/lib/widgets/charts/pillar_rewards_chart.dart b/lib/widgets/charts/pillar_rewards_chart.dart index 200fc20e..8fc091d3 100644 --- a/lib/widgets/charts/pillar_rewards_chart.dart +++ b/lib/widgets/charts/pillar_rewards_chart.dart @@ -50,7 +50,7 @@ class PillarRewardsChartState extends State { ); List _linesBarData() => [ - StandardLineChartBarData( + LineChartBarData( color: AppColors.znnColor, spots: _getRewardsSpots(), ), diff --git a/lib/widgets/charts/realtime_txs_chart.dart b/lib/widgets/charts/realtime_txs_chart.dart index 2d994960..2d547406 100644 --- a/lib/widgets/charts/realtime_txs_chart.dart +++ b/lib/widgets/charts/realtime_txs_chart.dart @@ -35,20 +35,23 @@ class RealtimeTxsChartState extends State { @override Widget build(BuildContext context) { - return StandardChart( - yValuesInterval: _maxTransactionsPerDay > kNumOfChartLeftSideTitles - ? _maxTransactionsPerDay / kNumOfChartLeftSideTitles - : 1, - maxY: _maxTransactionsPerDay, - lineBarsData: _linesBarData(), - lineBarDotSymbol: 'txs', - titlesReferenceDate: DateTime.now(), - convertLeftSideTitlesToInt: true, - ); + return Padding( + padding: const EdgeInsets.all(10), + child: Center( + child: StandardChart( + yValuesInterval: _maxTransactionsPerDay > kNumOfChartLeftSideTitles + ? _maxTransactionsPerDay / kNumOfChartLeftSideTitles + : 1, + maxY: _maxTransactionsPerDay, + titlesReferenceDate: DateTime.now(), + lineBarsData: _linesBarData(), + lineBarDotSymbol: 'txs', + convertLeftSideTitlesToInt: true, + ))); } double _getTransactionsByDay(TokenStandard tokenId, DateTime date) { - var transactions = []; + List transactions = []; for (var transaction in widget.transactions) { AccountBlock? pairedAccountBlock; if (transaction.blockType == 3 && @@ -80,13 +83,13 @@ class RealtimeTxsChartState extends State { List _linesBarData() { return [ - StandardLineChartBarData( + LineChartBarData( color: ColorUtils.getTokenColor(kZnnCoin.tokenStandard), - spots: _znnSpots, + spots: _znnSpots!, ), - StandardLineChartBarData( + LineChartBarData( color: ColorUtils.getTokenColor(kQsrCoin.tokenStandard), - spots: _qsrSpots, + spots: _qsrSpots!, ), ]; } diff --git a/lib/widgets/charts/sentinel_rewards_chart.dart b/lib/widgets/charts/sentinel_rewards_chart.dart index 51d42034..3f5004f0 100644 --- a/lib/widgets/charts/sentinel_rewards_chart.dart +++ b/lib/widgets/charts/sentinel_rewards_chart.dart @@ -61,11 +61,11 @@ class _SentinelRewardsChart extends State { ); List _linesBarData() => [ - StandardLineChartBarData( + LineChartBarData( color: AppColors.znnColor, spots: _getZnnRewardsSpots(), ), - StandardLineChartBarData( + LineChartBarData( color: AppColors.qsrColor, spots: _getQsrRewardsSpots(), ), diff --git a/lib/widgets/charts/staking_rewards_chart.dart b/lib/widgets/charts/staking_rewards_chart.dart index 8daf8b76..02c84e15 100644 --- a/lib/widgets/charts/staking_rewards_chart.dart +++ b/lib/widgets/charts/staking_rewards_chart.dart @@ -50,7 +50,7 @@ class _StakingRewardsChart extends State { ); _linesBarData() => [ - StandardLineChartBarData( + LineChartBarData( color: AppColors.qsrColor, spots: _getRewardsSpots(), ), diff --git a/lib/widgets/main_app_container.dart b/lib/widgets/main_app_container.dart index 96780db4..0b6f3af6 100644 --- a/lib/widgets/main_app_container.dart +++ b/lib/widgets/main_app_container.dart @@ -2,20 +2,23 @@ import 'dart:async'; import 'dart:io'; import 'package:app_links/app_links.dart'; +import 'package:badges/badges.dart' as badges; 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:hive/hive.dart'; import 'package:logging/logging.dart'; +import 'package:lottie/lottie.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/services/i_web3wallet_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'; @@ -41,7 +44,8 @@ enum Tabs { staking, plasma, tokens, - resyncWallet, + generation, + sync, bridge, accelerator, walletConnect, @@ -65,33 +69,24 @@ class _MainAppContainerState extends State with TickerProviderStateMixin, ClipboardListener, WindowListener { late AnimationController _animationController; late Animation _animation; - - final NodeSyncStatusBloc _netSyncStatusBloc = NodeSyncStatusBloc(); - late StreamSubscription _lockBlockStreamSubscription; late StreamSubscription _incomingLinkSubscription; - - Timer? _navigateToLockTimer; - late LockBloc _lockBloc; + Timer? _navigateToLockTimer; TabController? _tabController; - TransferTabChild? _transferTabChild; + bool _initialUriIsHandled = false; + final NodeSyncStatusBloc _netSyncStatusBloc = NodeSyncStatusBloc(); + final _appLinks = AppLinks(); final FocusNode _focusNode = FocusNode( skipTraversal: true, canRequestFocus: false, ); - bool _initialUriIsHandled = false; - - final _appLinks = AppLinks(); - @override void initState() { - sl().context = context; - clipboardWatcher.addListener(this); windowManager.addListener(this); @@ -115,6 +110,7 @@ class _MainAppContainerState extends State _initLockBlock(); _handleIncomingLinks(); _handleInitialUri(); + super.initState(); } @@ -307,14 +303,25 @@ class _MainAppContainerState extends State ), 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), + child: badges.Badge( + position: badges.BadgePosition.topEnd(top: -12.5, end: -12.5), + showBadge: (sl().pairings.value.isNotEmpty), + badgeContent: + Text(sl().pairings.value.length.toString()), + badgeStyle: const badges.BadgeStyle( + shape: badges.BadgeShape.circle, + badgeColor: AppColors.znnColor, + padding: EdgeInsets.all(3.5), + ), + 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( @@ -336,12 +343,22 @@ class _MainAppContainerState extends State ), ), Tab( - child: Icon( - Icons.notifications, - size: 24.0, - color: _isTabSelected(Tabs.notifications) - ? AppColors.znnColor - : Theme.of(context).iconTheme.color, + child: badges.Badge( + position: badges.BadgePosition.topEnd(top: -10, end: -10), + showBadge: (Hive.box(kNotificationsBox).length > 0), + badgeContent: Text(Hive.box(kNotificationsBox).length.toString()), + badgeStyle: const badges.BadgeStyle( + shape: badges.BadgeShape.circle, + badgeColor: AppColors.znnColor, + padding: EdgeInsets.all(3.5), + ), + child: Icon( + Icons.notifications, + size: 24.0, + color: _isTabSelected(Tabs.notifications) + ? AppColors.znnColor + : Theme.of(context).iconTheme.color, + ), ), ), Tab( @@ -354,7 +371,10 @@ class _MainAppContainerState extends State ), ), Tab( - child: _getPowGeneratingStatus(), + child: _getGenerationStatus(), + ), + Tab( + child: _getSyncStatus(), ), Tab( child: _isTabSelected(Tabs.lock) @@ -374,7 +394,7 @@ class _MainAppContainerState extends State ]; } - Widget _getWebsocketConnectionStatusStreamBuilder() { + Widget _getSyncStatus() { return StreamBuilder( stream: _netSyncStatusBloc.stream, builder: (_, snapshot) { @@ -389,8 +409,34 @@ class _MainAppContainerState extends State ); } + Widget _getGenerationStatus() { + return StreamBuilder( + stream: sl.get().stream, + builder: (_, snapshot) { + if (snapshot.hasData && snapshot.data == PowStatus.generating) { + return Tooltip( + message: 'Generating Plasma', + child: Lottie.asset( + 'assets/lottie/ic_anim_plasma_generation.json', + fit: BoxFit.contain, + width: 30.0, + repeat: true, + ), + ); + } + return Tooltip( + message: 'Plasma generation idle', + child: Icon( + MaterialCommunityIcons.lightning_bolt, + color: Theme.of(context).iconTheme.color, + ), + ); + }, + ); + } + Widget _getSyncingStatusIcon(SyncState syncState, [SyncInfo? syncInfo]) { - var message = 'Connected and synced'; + String message = 'Connected and synced'; if (syncState != SyncState.notEnoughPeers && syncState != SyncState.syncDone && @@ -401,6 +447,13 @@ class _MainAppContainerState extends State if (syncState == SyncState.unknown) { message = 'Not ready'; + return Tooltip( + message: message, + child: Icon( + Icons.sync_disabled, + size: 24.0, + color: _getSyncIconColor(syncState), + )); } else if (syncState == SyncState.syncing) { if (syncInfo != null) { if (syncInfo.targetHeight > 0 && @@ -408,12 +461,44 @@ class _MainAppContainerState extends State (syncInfo.targetHeight - syncInfo.currentHeight) < 3) { message = 'Connected and synced'; syncState = SyncState.syncDone; + return Tooltip( + message: message, + child: Lottie.asset( + 'assets/lottie/ic_anim_live.json', + fit: BoxFit.contain, + width: 25.0, + repeat: true, + )); + } else if (syncInfo.targetHeight == 0 || syncInfo.currentHeight == 0) { + message = 'Started syncing with the network, please wait'; + syncState = SyncState.syncing; + return Tooltip( + message: message, + child: Icon(Icons.sync, + size: 24.0, color: _getSyncIconColor(syncState))); } else { message = 'Sync progress: momentum ${syncInfo.currentHeight} of ${syncInfo.targetHeight}'; + return Tooltip( + message: message, + child: SizedBox( + height: 25.0, + width: 25.0, + child: Center( + child: CircularProgressIndicator( + backgroundColor: Theme.of(context).iconTheme.color, + color: _getSyncIconColor(syncState), + value: syncInfo.currentHeight / syncInfo.targetHeight, + )), + ), + ); } } else { message = 'Syncing momentums'; + return Tooltip( + message: message, + child: Icon(Icons.sync, + size: 24.0, color: _getSyncIconColor(syncState))); } } else if (syncState == SyncState.notEnoughPeers) { if (syncInfo != null) { @@ -422,17 +507,47 @@ class _MainAppContainerState extends State (syncInfo.targetHeight - syncInfo.currentHeight) < 20) { message = 'Connecting to peers'; syncState = SyncState.syncing; + return Tooltip( + message: message, + child: SizedBox( + height: 25.0, + width: 25.0, + child: Center( + child: CircularProgressIndicator( + backgroundColor: Theme.of(context).iconTheme.color, + color: _getSyncIconColor(syncState), + value: syncInfo.currentHeight / syncInfo.targetHeight, + )))); } else if (syncInfo.targetHeight == 0 || syncInfo.currentHeight == 0) { - message = 'Connecting to peers'; + message = 'Connecting to peers, please wait'; syncState = SyncState.syncing; + return Tooltip( + message: message, + child: Icon(Icons.sync, + size: 24.0, color: _getSyncIconColor(syncState))); } else { message = 'Sync progress: momentum ${syncInfo.currentHeight} of ${syncInfo.targetHeight}'; syncState = SyncState.syncing; + return Tooltip( + message: message, + child: SizedBox( + height: 25.0, + width: 25.0, + child: Center( + child: CircularProgressIndicator( + backgroundColor: Theme.of(context).iconTheme.color, + color: _getSyncIconColor(syncState), + value: syncInfo.currentHeight / syncInfo.targetHeight, + )))); } } else { message = 'Connecting to peers'; syncState = SyncState.syncing; + return Tooltip( + message: message, + child: Icon(Icons.sync_problem, + size: 24.0, color: _getSyncIconColor(syncState))); } } else { message = 'Connected and synced'; @@ -440,13 +555,13 @@ class _MainAppContainerState extends State } return Tooltip( - message: message, - child: Icon( - Icons.radio_button_unchecked, - size: 24.0, - color: _getSyncIconColor(syncState), - ), - ); + message: message, + child: Lottie.asset( + 'assets/lottie/ic_anim_live.json', + fit: BoxFit.contain, + width: 25.0, + repeat: true, + )); } Widget _getCurrentPageContainer() { @@ -480,7 +595,6 @@ class _MainAppContainerState extends State const NotificationsTabChild(), SettingsTabChild( _onChangeAutoLockTime, - _onResyncWalletPressed, onStepperNotificationSeeMorePressed: () => _navigateTo( Tabs.notifications, ), @@ -489,6 +603,7 @@ class _MainAppContainerState extends State ), ), const SizedBox(), + const SizedBox(), LockTabChild(_mainLockCallback, _afterAppInitCallback), ], ); @@ -545,6 +660,7 @@ class _MainAppContainerState extends State } else { _lockBloc.addEvent(LockEvent.navigateToDashboard); } + _listenToAutoReceiveTxWorkerNotifications(); } @@ -554,28 +670,6 @@ class _MainAppContainerState extends State }); } - void _onResyncWalletPressed() { - _navigateTo(Tabs.resyncWallet); - } - - Widget _getPowGeneratingStatus() { - return StreamBuilder( - stream: sl.get().stream, - builder: (_, snapshot) { - if (snapshot.hasData && snapshot.data == PowStatus.generating) { - return const Tooltip( - message: 'Generating Plasma', - child: SyriusLoadingWidget( - size: 20.0, - strokeWidth: 2.5, - ), - ); - } - return _getWebsocketConnectionStatusStreamBuilder(); - }, - ); - } - bool _isTabSelected(Tabs page) => _tabController!.index == kTabs.indexOf(page); diff --git a/lib/widgets/modular_widgets/bridge_widgets/swap_card.dart b/lib/widgets/modular_widgets/bridge_widgets/swap_card.dart index 08f7f8ce..6cead715 100644 --- a/lib/widgets/modular_widgets/bridge_widgets/swap_card.dart +++ b/lib/widgets/modular_widgets/bridge_widgets/swap_card.dart @@ -272,6 +272,7 @@ class _SwapCardState extends State { @override void dispose() { + _scrollController.dispose(); _amountController.dispose(); _evmAddressController.dispose(); super.dispose(); diff --git a/lib/widgets/modular_widgets/help_widgets/about_card.dart b/lib/widgets/modular_widgets/help_widgets/about_card.dart index 8522a87c..bcfcf651 100644 --- a/lib/widgets/modular_widgets/help_widgets/about_card.dart +++ b/lib/widgets/modular_widgets/help_widgets/about_card.dart @@ -2,6 +2,7 @@ import 'dart:io'; import 'package:flutter/material.dart'; import 'package:open_filex/open_filex.dart'; +import 'package:path/path.dart' as path; 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/metadata.dart'; @@ -115,6 +116,13 @@ class AboutCardState extends State { _getGenericOpenButtonExpandedChild( znnDefaultPaths.cache.absolute.path), ), + CustomExpandablePanel( + 'Syrius log path', + _getGenericOpenButtonExpandedChild( + Directory(path.join(znnDefaultCacheDirectory.path, 'log')) + .absolute + .path), + ), CustomExpandablePanel( 'Syrius wallet path', _getGenericOpenButtonExpandedChild( 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 c8ef208f..860d8d2f 100644 --- a/lib/widgets/modular_widgets/pillars_widgets/pillars_list_widget.dart +++ b/lib/widgets/modular_widgets/pillars_widgets/pillars_list_widget.dart @@ -679,6 +679,7 @@ class _PillarsListWidgetState extends State { @override void dispose() { + _scrollController.dispose(); _pillarsListBloc.dispose(); _blocListingStateSubscription.cancel(); super.dispose(); diff --git a/lib/widgets/modular_widgets/settings_widgets/node_management.dart b/lib/widgets/modular_widgets/settings_widgets/node_management.dart index 07bbc1d7..a1f42e00 100644 --- a/lib/widgets/modular_widgets/settings_widgets/node_management.dart +++ b/lib/widgets/modular_widgets/settings_widgets/node_management.dart @@ -9,7 +9,7 @@ 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/services/i_web3wallet_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'; @@ -44,6 +44,12 @@ class _NodeManagementState extends State { int get _newChainId => int.parse(_newChainIdController.text); + @override + void initState() { + super.initState(); + kDefaultCommunityNodes.shuffle(); + } + @override void didChangeDependencies() { super.didChangeDependencies(); @@ -208,7 +214,8 @@ class _NodeManagementState extends State { InputValidators.node(_newNodeController.text) == null; void _onAddNodePressed() async { - if ([...kDbNodes, ...kDefaultNodes].contains(_newNodeController.text)) { + if ([...kDbNodes, ...kDefaultCommunityNodes, ...kDefaultNodes] + .contains(_newNodeController.text)) { NotificationUtils.sendNotificationError( 'Node already exists', 'Node already exists'); } else { @@ -236,8 +243,11 @@ class _NodeManagementState extends State { Widget _getNodeTiles() { return Column( - children: - [...kDefaultNodes, ...kDbNodes].map((e) => _getNodeTile(e)).toList(), + children: { + ...kDefaultNodes, + ...kDefaultCommunityNodes, + ...kDbNodes + }.toList().map((e) => _getNodeTile(e)).toList(), ); } @@ -368,7 +378,7 @@ class _NodeManagementState extends State { _confirmChainIdButtonKey.currentState?.animateForward(); setChainIdentifier(chainIdentifier: _newChainId); await sharedPrefsService!.put(kChainIdKey, _newChainId); - sl().emitChainIdChangeEvent(_newChainId.toString()); + sl().emitChainIdChangeEvent(_newChainId.toString()); _sendSuccessfullyChangedChainIdNotification(_newChainId); _initCurrentChainId(); _newChainIdController = TextEditingController(); diff --git a/lib/widgets/modular_widgets/settings_widgets/wallet_options.dart b/lib/widgets/modular_widgets/settings_widgets/wallet_options.dart index 86284f9d..8c9ad6bc 100644 --- a/lib/widgets/modular_widgets/settings_widgets/wallet_options.dart +++ b/lib/widgets/modular_widgets/settings_widgets/wallet_options.dart @@ -2,21 +2,17 @@ import 'dart:io'; import 'package:flutter/material.dart'; import 'package:launch_at_startup/launch_at_startup.dart'; +import 'package:logging/logging.dart'; import 'package:package_info_plus/package_info_plus.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/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'; +import 'package:zenon_syrius_wallet_flutter/utils/utils.dart'; import 'package:zenon_syrius_wallet_flutter/widgets/widgets.dart'; class WalletOptions extends StatefulWidget { - final VoidCallback onResyncWalletPressed; - - const WalletOptions(this.onResyncWalletPressed, {Key? key}) : super(key: key); + const WalletOptions({Key? key}) : super(key: key); @override State createState() => _WalletOptionsState(); @@ -26,6 +22,7 @@ class _WalletOptionsState extends State { bool? _launchAtStartup; bool? _enableDesktopNotifications; bool? _enabledClipboardWatcher; + bool? _autoReceive; @override void initState() { @@ -42,6 +39,10 @@ class _WalletOptionsState extends State { kEnableClipboardWatcherKey, defaultValue: kEnableClipboardWatcherDefaultValue, ); + _autoReceive = sharedPrefsService!.get( + kAutoReceiveKey, + defaultValue: kAutoReceiveDefaultValue, + ); } @override @@ -119,6 +120,7 @@ class _WalletOptionsState extends State { _getLaunchAtStartupWidget(), _getEnableDesktopNotifications(), _buildEnableClipboardWatcher(), + _getAutoReceiveWidget() ], ); } @@ -144,6 +146,42 @@ class _WalletOptionsState extends State { ); } + Widget _getAutoReceiveWidget() { + return Row( + children: [ + Text( + 'Auto-receiver', + style: Theme.of(context).textTheme.bodyMedium, + ), + SyriusCheckbox( + onChanged: (value) async { + if (value == true) { + NodeUtils.getUnreceivedTransactions().then((value) { + sl().autoReceive(); + }).onError((error, stackTrace) { + Logger('MainAppContainer').log( + Level.WARNING, '_getAutoReceiveWidget', error, stackTrace); + }); + } else if (value == false && + sl().pool.isNotEmpty) { + sl().pool.clear(); + } + setState(() { + _autoReceive = value; + _changeAutoReceiveStatus(value ?? false); + }); + }, + value: _autoReceive, + context: context, + ), + const StandardTooltipIcon( + 'Disable the auto-receiver to protect against dusting attacks', + Icons.help, + ), + ], + ); + } + Future _setupLaunchAtStartup() async { PackageInfo packageInfo = await PackageInfo.fromPlatform(); launchAtStartup.setup( @@ -152,6 +190,37 @@ class _WalletOptionsState extends State { ); } + Future _changeAutoReceiveStatus(bool enabled) async { + try { + await _saveAutoReceiveValueToCache(enabled); + _sendAutoReceiveNotification(enabled); + } on Exception catch (e) { + NotificationUtils.sendNotificationError( + e, + 'Something went wrong while setting automatic receive preference', + ); + } + } + + Future _saveAutoReceiveValueToCache(bool enabled) async { + await sharedPrefsService!.put( + kAutoReceiveKey, + enabled, + ); + } + + void _sendAutoReceiveNotification(bool enabled) { + sl.get().addNotification( + WalletNotification( + title: 'Auto-receiver ${enabled ? 'enabled' : 'disabled'}', + details: + 'Automatic Plasma generation was ${enabled ? 'enabled' : 'disabled'}', + timestamp: DateTime.now().millisecondsSinceEpoch, + type: NotificationType.paymentSent, + ), + ); + } + Future _changeLaunchAtStartupStatus(bool enabled) async { try { await _setupLaunchAtStartup(); 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.dart similarity index 90% rename from lib/widgets/modular_widgets/transfer_widgets/latest_transactions/latest_transactions_transfer_widget.dart rename to lib/widgets/modular_widgets/transfer_widgets/latest_transactions/latest_transactions.dart index 30f40160..dc79c149 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.dart @@ -58,7 +58,7 @@ class _LatestTransactionsState extends State { List _rowCellsGenerator( AccountBlock transaction, bool isSelected, { - SentinelsListBloc? model, + LatestTransactions? model, }) => widget.version == LatestTransactionsVersion.dashboard ? _getCellsForDashboardWidget(isSelected, transaction) @@ -110,12 +110,18 @@ class _LatestTransactionsState extends State { context, infoBlock.confirmationDetail?.momentumTimestamp == null ? 'Pending' - : _formatData( + : FormatUtils.formatData( infoBlock.confirmationDetail!.momentumTimestamp * 1000), ), - InfiniteScrollTableCell(_getTransactionTypeIcon(transactionBlock)), + InfiniteScrollTableCell(Align( + alignment: Alignment.centerLeft, + child: _getTransactionTypeIcon(transactionBlock))), InfiniteScrollTableCell( - infoBlock.token != null ? _showTokenSymbol(infoBlock) : Container(), + Align( + alignment: Alignment.centerLeft, + child: infoBlock.token != null + ? _showTokenSymbol(infoBlock) + : Container()), ), ]; } @@ -148,7 +154,6 @@ class _LatestTransactionsState extends State { InfiniteScrollTableHeaderColumn( columnName: 'Type', onSortArrowsPressed: _onSortArrowsPressed, - contentAlign: MainAxisAlignment.center, ), InfiniteScrollTableHeaderColumn( columnName: 'Assets', @@ -157,26 +162,6 @@ class _LatestTransactionsState extends State { ]; } - String _formatData(int transactionMillis) { - int currentMillis = DateTime.now().millisecondsSinceEpoch; - if (currentMillis - transactionMillis <= - const Duration(days: 1).inMilliseconds) { - return _formatDataShort(currentMillis - transactionMillis); - } - return FormatUtils.formatDate(transactionMillis, dateFormat: 'MM/dd/yyyy'); - } - - String _formatDataShort(int i) { - Duration duration = Duration(milliseconds: i); - if (duration.inHours > 0) { - return '${duration.inHours} h ago'; - } - if (duration.inMinutes > 0) { - return '${duration.inMinutes} min ago'; - } - return '${duration.inSeconds} s ago'; - } - Widget _getTransactionTypeIcon(AccountBlock block) { if (BlockUtils.isSendBlock(block.blockType)) { return const Icon( @@ -196,7 +181,7 @@ class _LatestTransactionsState extends State { FormatUtils.extractNameFromEnum( BlockTypeEnum.values[block.blockType], ), - textAlign: TextAlign.center, + textAlign: TextAlign.start, style: Theme.of(context).textTheme.titleSmall, ); } @@ -324,7 +309,6 @@ class _LatestTransactionsState extends State { InfiniteScrollTableHeaderColumn( columnName: 'Type', onSortArrowsPressed: _onSortArrowsPressed, - contentAlign: MainAxisAlignment.center, ), InfiniteScrollTableHeaderColumn( columnName: 'Assets', @@ -371,13 +355,21 @@ class _LatestTransactionsState extends State { context, infoBlock.confirmationDetail?.momentumTimestamp == null ? 'Pending' - : _formatData( + : FormatUtils.formatData( infoBlock.confirmationDetail!.momentumTimestamp * 1000, ), ), - InfiniteScrollTableCell(_getTransactionTypeIcon(transactionBlock)), InfiniteScrollTableCell( - infoBlock.token != null ? _showTokenSymbol(infoBlock) : Container(), + Align( + alignment: Alignment.center, + child: _getTransactionTypeIcon(transactionBlock)), + ), + InfiniteScrollTableCell( + Align( + alignment: Alignment.centerLeft, + child: infoBlock.token != null + ? _showTokenSymbol(infoBlock) + : Container()), ) ]; } diff --git a/lib/widgets/modular_widgets/transfer_widgets/pending_transactions/pending_transactions.dart b/lib/widgets/modular_widgets/transfer_widgets/pending_transactions/pending_transactions.dart new file mode 100644 index 00000000..662cd8d3 --- /dev/null +++ b/lib/widgets/modular_widgets/transfer_widgets/pending_transactions/pending_transactions.dart @@ -0,0 +1,270 @@ +import 'package:flutter/material.dart'; +import 'package:marquee_widget/marquee_widget.dart'; +import 'package:zenon_syrius_wallet_flutter/blocs/auto_receive_tx_worker.dart'; +import 'package:zenon_syrius_wallet_flutter/blocs/transfer/pending_transactions_bloc.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/color_utils.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/widget_utils.dart'; +import 'package:zenon_syrius_wallet_flutter/widgets/widgets.dart'; +import 'package:znn_sdk_dart/znn_sdk_dart.dart'; + +class PendingTransactions extends StatefulWidget { + const PendingTransactions({ + Key? key, + }) : super(key: key); + + @override + State createState() => _PendingTransactionsState(); +} + +class _PendingTransactionsState extends State { + late PendingTransactionsBloc _bloc; + List? _transactions; + + bool _sortAscending = true; + + @override + void initState() { + super.initState(); + _bloc = PendingTransactionsBloc(); + _bloc.refreshResults(); + } + + @override + Widget build(BuildContext context) { + return CardScaffold( + title: _getWidgetTitle(), + description: 'This card displays the pending transactions (including ZTS ' + 'tokens) involving your wallet addresses', + childBuilder: () { + _bloc = PendingTransactionsBloc(); + return _getTable(); + }, + onRefreshPressed: () => _bloc.refreshResults(), + ); + } + + Widget _getTable() { + return InfiniteScrollTable( + bloc: _bloc, + generateRowCells: _rowCellsGenerator, + headerColumns: _getHeaderColumnsForTransferWidget(), + ); + } + + List _rowCellsGenerator(AccountBlock transaction, bool isSelected) => + _getCellsForPendingTransferWidget(isSelected, transaction); + + List _getCellsForPendingTransferWidget( + bool isSelected, + AccountBlock transactionBlock, + ) { + AccountBlock infoBlock = + BlockUtils.isReceiveBlock(transactionBlock.blockType) + ? transactionBlock.pairedAccountBlock! + : transactionBlock; + return [ + isSelected + ? WidgetUtils.getMarqueeAddressTableCell(infoBlock.address, context) + : WidgetUtils.getTextAddressTableCell(infoBlock.address, context), + isSelected + ? WidgetUtils.getMarqueeAddressTableCell(infoBlock.toAddress, context) + : WidgetUtils.getTextAddressTableCell(infoBlock.toAddress, context), + isSelected + ? InfiniteScrollTableCell.withMarquee(infoBlock.hash.toString(), + flex: 2) + : InfiniteScrollTableCell.withText( + context, + infoBlock.hash.toShortString(), + flex: 2, + ), + 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 + ? 'Pending' + : FormatUtils.formatData( + infoBlock.confirmationDetail!.momentumTimestamp * 1000), + ), + InfiniteScrollTableCell( + Align( + alignment: Alignment.centerLeft, + child: infoBlock.token != null + ? _showTokenSymbol(infoBlock) + : Container()), + ), + InfiniteScrollTableCell(Align( + alignment: Alignment.centerLeft, + child: _getReceiveButton(transactionBlock.hash))), + ]; + } + + List _getHeaderColumnsForTransferWidget() { + return [ + InfiniteScrollTableHeaderColumn( + columnName: 'Sender', + onSortArrowsPressed: _onSortArrowsPressed, + flex: 2, + ), + InfiniteScrollTableHeaderColumn( + columnName: 'Receiver', + onSortArrowsPressed: _onSortArrowsPressed, + flex: 2, + ), + InfiniteScrollTableHeaderColumn( + columnName: 'Hash', + onSortArrowsPressed: _onSortArrowsPressed, + flex: 2, + ), + InfiniteScrollTableHeaderColumn( + columnName: 'Amount', + onSortArrowsPressed: _onSortArrowsPressed, + ), + InfiniteScrollTableHeaderColumn( + columnName: 'Date', + onSortArrowsPressed: _onSortArrowsPressed, + ), + InfiniteScrollTableHeaderColumn( + columnName: 'Assets', + onSortArrowsPressed: _onSortArrowsPressed, + ), + const InfiniteScrollTableHeaderColumn( + columnName: '', + ), + ]; + } + + void _onSortArrowsPressed(String columnName) { + switch (columnName) { + case 'Sender': + _sortAscending + ? _transactions!.sort( + (a, b) => a.address.toString().compareTo( + b.address.toString(), + ), + ) + : _transactions!.sort( + (a, b) => b.address.toString().compareTo( + a.address.toString(), + ), + ); + break; + case 'Receiver': + _sortAscending + ? _transactions!.sort( + (a, b) => a.toAddress.toString().compareTo( + b.toAddress.toString(), + ), + ) + : _transactions!.sort((a, b) => + b.toAddress.toString().compareTo(a.toAddress.toString())); + break; + case 'Hash': + _sortAscending + ? _transactions!.sort( + (a, b) => a.hash.toString().compareTo( + b.hash.toString(), + ), + ) + : _transactions!.sort( + (a, b) => b.hash.toString().compareTo( + a.hash.toString(), + ), + ); + break; + case 'Amount': + _sortAscending + ? _transactions!.sort((a, b) => a.amount.compareTo(b.amount)) + : _transactions!.sort((a, b) => b.amount.compareTo(a.amount)); + break; + case 'Date': + _sortAscending + ? _transactions!.sort( + (a, b) => a.confirmationDetail!.momentumTimestamp.compareTo( + b.confirmationDetail!.momentumTimestamp, + )) + : _transactions!.sort( + (a, b) => b.confirmationDetail!.momentumTimestamp.compareTo( + a.confirmationDetail!.momentumTimestamp, + )); + break; + case 'Assets': + _sortAscending + ? _transactions!.sort( + (a, b) => a.token!.symbol.compareTo(b.token!.symbol), + ) + : _transactions!.sort( + (a, b) => b.token!.symbol.compareTo(a.token!.symbol), + ); + break; + default: + _sortAscending + ? _transactions!.sort( + (a, b) => a.tokenStandard.toString().compareTo( + b.tokenStandard.toString(), + ), + ) + : _transactions!.sort( + (a, b) => b.tokenStandard.toString().compareTo( + a.tokenStandard.toString(), + ), + ); + break; + } + + setState(() { + _sortAscending = !_sortAscending; + }); + } + + Widget _getReceiveButton(Hash txHash) { + return MaterialIconButton( + size: 25.0, + iconData: Icons.download_for_offline, + onPressed: () { + sl().autoReceiveTransactionHash(txHash); + setState(() {}); + }, + ); + } + + Widget _showTokenSymbol(AccountBlock block) { + 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)); + } + + @override + void dispose() { + _bloc.dispose(); + super.dispose(); + } + + String _getWidgetTitle() => 'Pending Transactions'; +} diff --git a/lib/widgets/modular_widgets/transfer_widgets/transfer_widgets.dart b/lib/widgets/modular_widgets/transfer_widgets/transfer_widgets.dart index 7e7f8887..d9b1dcb2 100644 --- a/lib/widgets/modular_widgets/transfer_widgets/transfer_widgets.dart +++ b/lib/widgets/modular_widgets/transfer_widgets/transfer_widgets.dart @@ -1,4 +1,5 @@ -export 'latest_transactions/latest_transactions_transfer_widget.dart'; +export 'latest_transactions/latest_transactions.dart'; +export 'pending_transactions/pending_transactions.dart'; export 'receive/receive_large.dart'; export 'receive/receive_medium.dart'; export 'receive/receive_small.dart'; 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 index 202fd9b0..7dab483d 100644 --- 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 @@ -5,7 +5,7 @@ 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/services/i_web3wallet_service.dart'; import 'package:zenon_syrius_wallet_flutter/utils/utils.dart'; import 'package:zenon_syrius_wallet_flutter/widgets/widgets.dart'; @@ -63,7 +63,7 @@ class _WalletConnectCameraCardState extends State { }, canPop: true, onScan: (String value) async { - final wcService = sl.get(); + final wcService = sl.get(); final pairingInfo = await wcService.pair(Uri.parse(value)); Logger('WalletConnectCameraCard').log(Level.INFO, 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 index d1648013..cd326c0b 100644 --- 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 @@ -5,7 +5,7 @@ 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/services/i_web3wallet_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'; @@ -30,8 +30,6 @@ class _WalletConnectPairingsCardState extends State { @override void initState() { - // Initialize WalletConnect client - sl.get().initClient(); super.initState(); } @@ -138,7 +136,7 @@ class _WalletConnectPairingsCardState extends State { Future _onDeactivatePairingIconPressed(PairingInfo pairingInfo) async { try { - await sl().deactivatePairing( + await sl().deactivatePairing( topic: pairingInfo.topic, ); _pairingsBloc.refreshResults(); 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 index 5ba1f628..ef379e52 100644 --- 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 @@ -12,7 +12,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/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/services/i_web3wallet_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'; @@ -92,7 +92,7 @@ class _WalletConnectQrCardState extends State { Future _pairWithDapp(Uri uri) async { try { - final wcService = sl.get(); + final wcService = sl.get(); final pairingInfo = await wcService.pair(uri); Logger('WalletConnectPairingCard') .log(Level.INFO, 'pairing info', pairingInfo.toJson()); 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 index 039eb7bc..9a6eabb5 100644 --- 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 @@ -2,7 +2,7 @@ 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/services/i_web3wallet_service.dart'; import 'package:zenon_syrius_wallet_flutter/utils/utils.dart'; import 'package:zenon_syrius_wallet_flutter/widgets/widgets.dart'; @@ -118,7 +118,7 @@ class _WalletConnectUriCardState extends State { Future _pairWithDapp(Uri uri) async { try { - final pairingInfo = await sl.get().pair(uri); + final pairingInfo = await sl.get().pair(uri); Logger('WalletConnectPairingCard') .log(Level.INFO, 'pairing info', pairingInfo.toJson()); _uriController = TextEditingController(); diff --git a/lib/widgets/reusable_widgets/buttons/material_icon_button.dart b/lib/widgets/reusable_widgets/buttons/material_icon_button.dart index 4a05ec33..5f67b3d6 100644 --- a/lib/widgets/reusable_widgets/buttons/material_icon_button.dart +++ b/lib/widgets/reusable_widgets/buttons/material_icon_button.dart @@ -8,10 +8,12 @@ class MaterialIconButton extends StatelessWidget { final MaterialTapTargetSize materialTapTargetSize; final VoidCallback onPressed; final IconData iconData; + final double size; const MaterialIconButton({ required this.onPressed, required this.iconData, + required this.size, this.iconColor = AppColors.znnColor, this.hoverColor, this.padding = 8.0, @@ -31,7 +33,7 @@ class MaterialIconButton extends StatelessWidget { child: Icon( iconData, color: iconColor, - size: 15.0, + size: size, ), ); } diff --git a/lib/widgets/reusable_widgets/infinite_scroll_table.dart b/lib/widgets/reusable_widgets/infinite_scroll_table.dart index 438fbb5e..b9b9cd8e 100644 --- a/lib/widgets/reusable_widgets/infinite_scroll_table.dart +++ b/lib/widgets/reusable_widgets/infinite_scroll_table.dart @@ -172,6 +172,7 @@ class _InfiniteScrollTableState extends State> { @override void dispose() { + _scrollController.dispose(); _pagingController.dispose(); _blocListingStateSubscription.cancel(); if (widget.disposeBloc) { diff --git a/lib/widgets/reusable_widgets/settings_address.dart b/lib/widgets/reusable_widgets/settings_address.dart index 4912b508..e44a3f44 100644 --- a/lib/widgets/reusable_widgets/settings_address.dart +++ b/lib/widgets/reusable_widgets/settings_address.dart @@ -83,6 +83,7 @@ class _SettingsAddressState extends State { width: 5.0, ), MaterialIconButton( + size: 15.0, iconData: Icons.edit, onPressed: () { setState(() { @@ -173,6 +174,7 @@ class _SettingsAddressState extends State { key: _changeButtonKey, ), MaterialIconButton( + size: 15.0, onPressed: () { setState(() { _labelController.text = kAddressLabelMap[widget.address]!; diff --git a/lib/widgets/reusable_widgets/settings_node.dart b/lib/widgets/reusable_widgets/settings_node.dart index 34d89de2..26c99e13 100644 --- a/lib/widgets/reusable_widgets/settings_node.dart +++ b/lib/widgets/reusable_widgets/settings_node.dart @@ -104,7 +104,7 @@ class _SettingsNodeState extends State { ? AppColors.znnColor : AppColors.errorColor)), const SizedBox( - width: 8.0, + width: 5.0, ), Visibility( visible: widget.node.contains('wss://'), @@ -131,14 +131,25 @@ class _SettingsNodeState extends State { 'The Embedded Node validates all network transactions\n' 'It may take several hours to fully sync with the network', MaterialCommunityIcons.clock, + iconColor: Colors.amber, + ), + ), + Visibility( + visible: kDefaultCommunityNodes.contains(widget.node), + child: const StandardTooltipIcon( + 'Hardcoded Community Node', + MaterialCommunityIcons.vector_link, + iconColor: Colors.amber, ), ), const SizedBox( width: 5.0, ), Visibility( - visible: !kDefaultNodes.contains(widget.node), + visible: !kDefaultNodes.contains(widget.node) && + !kDefaultCommunityNodes.contains(widget.node), child: MaterialIconButton( + size: 15.0, iconData: Icons.edit, onPressed: () { setState(() { @@ -149,8 +160,10 @@ class _SettingsNodeState extends State { ), ), Visibility( - visible: !kDefaultNodes.contains(widget.node), + visible: !kDefaultNodes.contains(widget.node) && + !kDefaultCommunityNodes.contains(widget.node), child: MaterialIconButton( + size: 15.0, onPressed: () { showDialogWithNoAndYesOptions( isBarrierDismissible: true, @@ -238,6 +251,7 @@ class _SettingsNodeState extends State { key: _changeButtonKey, ), MaterialIconButton( + size: 15.0, onPressed: () { setState(() { _nodeController.text = widget.node; @@ -257,7 +271,8 @@ class _SettingsNodeState extends State { _changeButtonKey.currentState!.showLoadingIndicator(true); if (_nodeController.text.isNotEmpty && _nodeController.text.length <= kAddressLabelMaxLength && - ![...kDefaultNodes, ...kDbNodes].contains(_nodeController.text)) { + ![...kDefaultNodes, ...kDefaultCommunityNodes, ...kDbNodes] + .contains(_nodeController.text)) { Box nodesBox = await Hive.openBox(kNodesBox); dynamic key = nodesBox.keys.firstWhere( (key) => nodesBox.get(key) == widget.node, diff --git a/lib/widgets/tab_children_widgets/settings_tab_child.dart b/lib/widgets/tab_children_widgets/settings_tab_child.dart index 88aee2de..f42a61b3 100644 --- a/lib/widgets/tab_children_widgets/settings_tab_child.dart +++ b/lib/widgets/tab_children_widgets/settings_tab_child.dart @@ -5,13 +5,11 @@ import 'package:zenon_syrius_wallet_flutter/widgets/widgets.dart'; class SettingsTabChild extends StatefulWidget { final VoidCallback _onChangeAutoLockTime; - final VoidCallback _onResyncWalletPressed; final VoidCallback onStepperNotificationSeeMorePressed; final VoidCallback onNodeChangedCallback; const SettingsTabChild( - this._onChangeAutoLockTime, - this._onResyncWalletPressed, { + this._onChangeAutoLockTime, { required this.onStepperNotificationSeeMorePressed, required this.onNodeChangedCallback, Key? key, @@ -75,10 +73,8 @@ class _SettingsTabChildState extends State { ), height: kStaggeredNumOfColumns / 3, ), - FluidCell( - child: WalletOptions( - widget._onResyncWalletPressed, - ), + const FluidCell( + child: WalletOptions(), height: kStaggeredNumOfColumns / 3, ), FluidCell( @@ -92,15 +88,15 @@ class _SettingsTabChildState extends State { child: NodeManagement( onNodeChangedCallback: widget.onNodeChangedCallback, ), - height: kStaggeredNumOfColumns / 6, + height: kStaggeredNumOfColumns / 3, ), const FluidCell( child: DisplayWidget(), - height: kStaggeredNumOfColumns / 6, + height: kStaggeredNumOfColumns / 3, ), const FluidCell( child: BackupWidget(), - height: kStaggeredNumOfColumns / 6, + height: kStaggeredNumOfColumns / 3, ), ], ); diff --git a/lib/widgets/tab_children_widgets/transfer_tab_child.dart b/lib/widgets/tab_children_widgets/transfer_tab_child.dart index dfc12a9a..dfbb2cdf 100644 --- a/lib/widgets/tab_children_widgets/transfer_tab_child.dart +++ b/lib/widgets/tab_children_widgets/transfer_tab_child.dart @@ -28,9 +28,14 @@ class _TransferTabChildState extends State { _getSendCard(), _getReceiveCard(), const FluidCell( - width: kStaggeredNumOfColumns, child: LatestTransactions(), - height: kStaggeredNumOfColumns / 2, + width: kStaggeredNumOfColumns, + height: kStaggeredNumOfColumns / 3, + ), + const FluidCell( + child: PendingTransactions(), + width: kStaggeredNumOfColumns, + height: kStaggeredNumOfColumns / 3, ), ], ); diff --git a/linux/create-git-metadata.sh b/linux/create-git-metadata.sh index 4711295d..af4b7bcd 100755 --- a/linux/create-git-metadata.sh +++ b/linux/create-git-metadata.sh @@ -7,8 +7,8 @@ GIT_COMMIT_FILE="../lib/utils/metadata.dart" sed --i '1,5d' $GIT_COMMIT_FILE -echo "const String gitBranchName = '$GIT_BRANCH_NAME';" >> $GIT_COMMIT_FILE -echo "const String gitCommitHash = '$GIT_COMMIT_HASH';" >> $GIT_COMMIT_FILE -echo "const String gitCommitMessage = '$GIT_COMMIT_MESSAGE';" >> $GIT_COMMIT_FILE -echo "const String gitCommitDate = '$GIT_COMMIT_DATE';" >> $GIT_COMMIT_FILE -echo "const String gitOriginUrl = '$GIT_ORIGIN_URL';" >> $GIT_COMMIT_FILE +echo "const String gitBranchName = \"${GIT_BRANCH_NAME}\";" >> $GIT_COMMIT_FILE +echo "const String gitCommitHash = \"${GIT_COMMIT_HASH}\";" >> $GIT_COMMIT_FILE +echo "const String gitCommitMessage = \"${GIT_COMMIT_MESSAGE}\";" >> $GIT_COMMIT_FILE +echo "const String gitCommitDate = \"${GIT_COMMIT_DATE}\";" >> $GIT_COMMIT_FILE +echo "const String gitOriginUrl = \"${GIT_ORIGIN_URL}\";" >> $GIT_COMMIT_FILE diff --git a/macos/Runner.xcodeproj/project.pbxproj b/macos/Runner.xcodeproj/project.pbxproj index 6fb19089..96c840c0 100644 --- a/macos/Runner.xcodeproj/project.pbxproj +++ b/macos/Runner.xcodeproj/project.pbxproj @@ -360,7 +360,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "GIT_BRANCH_NAME=$(git rev-parse --abbrev-ref HEAD)\nGIT_COMMIT_HASH=$(git rev-parse HEAD)\nGIT_COMMIT_MESSAGE=$(git log -1 --pretty=%s)\nGIT_COMMIT_DATE=$(git --no-pager log -1 --format=\"%ai\")\nGIT_ORIGIN_URL=$(git config --get remote.origin.url)\nGIT_COMMIT_FILE=\"../lib/utils/metadata.dart\"\n\nsed -i '' '1,5d' $GIT_COMMIT_FILE\necho \"const String gitBranchName = '$GIT_BRANCH_NAME';\" >> $GIT_COMMIT_FILE\necho \"const String gitCommitHash = '$GIT_COMMIT_HASH';\" >> $GIT_COMMIT_FILE\necho \"const String gitCommitMessage = '$GIT_COMMIT_MESSAGE';\" >> $GIT_COMMIT_FILE\necho \"const String gitCommitDate = '$GIT_COMMIT_DATE';\" >> $GIT_COMMIT_FILE\necho \"const String gitOriginUrl = '$GIT_ORIGIN_URL';\" >> $GIT_COMMIT_FILE\n\n\n"; + shellScript = "GIT_BRANCH_NAME=$(git rev-parse --abbrev-ref HEAD)\nGIT_COMMIT_HASH=$(git rev-parse HEAD)\nGIT_COMMIT_MESSAGE=$(git log -1 --pretty=%s)\nGIT_COMMIT_DATE=$(git --no-pager log -1 --format=\"%ai\")\nGIT_ORIGIN_URL=$(git config --get remote.origin.url)\nGIT_COMMIT_FILE=\"../lib/utils/metadata.dart\"\n\nsed -i '' '1,5d' $GIT_COMMIT_FILE\necho \"const String gitBranchName = \\\"${GIT_BRANCH_NAME}\\\";\" >> $GIT_COMMIT_FILE\necho \"const String gitCommitHash = \\\"${GIT_COMMIT_HASH}\\\";\" >> $GIT_COMMIT_FILE\necho \"const String gitCommitMessage = \\\"${GIT_COMMIT_MESSAGE}\\\";\" >> $GIT_COMMIT_FILE\necho \"const String gitCommitDate = \\\"${GIT_COMMIT_DATE}\\\";\" >> $GIT_COMMIT_FILE\necho \"const String gitOriginUrl = \\\"${GIT_ORIGIN_URL}\\\";\" >> $GIT_COMMIT_FILE\n\n\n"; }; 96DA24F0296EB70E00545E88 /* ShellScript */ = { isa = PBXShellScriptBuildPhase; diff --git a/pubspec.lock b/pubspec.lock index 2941cc2a..39107def 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -81,6 +81,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.0.0" + badges: + dependency: "direct main" + description: + name: badges + sha256: "6e7f3ec561ec08f47f912cfe349d4a1707afdc8dda271e17b046aa6d42c89e77" + url: "https://pub.dev" + source: hosted + version: "3.1.1" base_x: dependency: transitive description: @@ -333,10 +341,10 @@ packages: dependency: "direct main" description: name: device_info_plus - sha256: "2c35b6d1682b028e42d07b3aee4b98fa62996c10bc12cb651ec856a80d6a761b" + sha256: "86add5ef97215562d2e090535b0a16f197902b10c369c558a100e74ea06e8659" url: "https://pub.dev" source: hosted - version: "9.0.2" + version: "9.0.3" device_info_plus_platform_interface: dependency: transitive description: @@ -405,10 +413,10 @@ packages: dependency: "direct main" description: name: ffi - sha256: ed5337a5660c506388a9f012be0288fb38b49020ce2b45fe1f8b8323fe429f99 + sha256: "7bf0adc28a23d395f19f3f1eb21dd7cfd1dd9f8e1c50051c069122e6853bc878" url: "https://pub.dev" source: hosted - version: "2.0.2" + version: "2.1.0" file: dependency: transitive description: @@ -751,10 +759,10 @@ packages: dependency: "direct main" description: name: lottie - sha256: "0793a5866062e5cc8a8b24892fa94c3095953ea914a7fdf790f550dd7537fe60" + sha256: b8bdd54b488c54068c57d41ae85d02808da09e2bee8b8dd1f59f441e7efa60cd url: "https://pub.dev" source: hosted - version: "2.5.0" + version: "2.6.0" marquee_widget: dependency: "direct main" description: @@ -807,10 +815,10 @@ packages: dependency: transitive description: name: mobile_scanner - sha256: "54dd914e1bb5758b3db7aa02e56d65d80285ba0705e0e5fa5cfbb11e27344c4b" + sha256: "2fbc3914fe625e196c64ea8ffc4084cd36781d2be276d4d5923b11af3b5d44ff" url: "https://pub.dev" source: hosted - version: "3.3.0" + version: "3.4.1" nested: dependency: transitive description: @@ -827,22 +835,6 @@ 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: @@ -871,10 +863,10 @@ packages: dependency: "direct main" description: name: package_info_plus - sha256: ceb027f6bc6a60674a233b4a90a7658af1aebdea833da0b5b53c1e9821a78c7b + sha256: "6ff267fcd9d48cb61c8df74a82680e8b82e940231bb5f68356672fde0397334a" url: "https://pub.dev" source: hosted - version: "4.0.2" + version: "4.1.0" package_info_plus_platform_interface: dependency: transitive description: @@ -919,50 +911,50 @@ packages: dependency: "direct main" description: name: path_provider - sha256: "3087813781ab814e4157b172f1a11c46be20179fcc9bea043e0fba36bc0acaa2" + sha256: "909b84830485dbcd0308edf6f7368bc8fd76afa26a270420f34cabea2a6467a0" url: "https://pub.dev" source: hosted - version: "2.0.15" + version: "2.1.0" path_provider_android: dependency: transitive description: name: path_provider_android - sha256: "2cec049d282c7f13c594b4a73976b0b4f2d7a1838a6dd5aaf7bd9719196bee86" + sha256: "5d44fc3314d969b84816b569070d7ace0f1dea04bd94a83f74c4829615d22ad8" url: "https://pub.dev" source: hosted - version: "2.0.27" + version: "2.1.0" path_provider_foundation: dependency: transitive description: name: path_provider_foundation - sha256: "916731ccbdce44d545414dd9961f26ba5fbaa74bcbb55237d8e65a623a8c7297" + sha256: "1b744d3d774e5a879bb76d6cd1ecee2ba2c6960c03b1020cd35212f6aa267ac5" url: "https://pub.dev" source: hosted - version: "2.2.4" + version: "2.3.0" path_provider_linux: dependency: transitive description: name: path_provider_linux - sha256: ffbb8cc9ed2c9ec0e4b7a541e56fd79b138e8f47d2fb86815f15358a349b3b57 + sha256: ba2b77f0c52a33db09fc8caf85b12df691bf28d983e84cf87ff6d693cfa007b3 url: "https://pub.dev" source: hosted - version: "2.1.11" + version: "2.2.0" path_provider_platform_interface: dependency: transitive description: name: path_provider_platform_interface - sha256: "57585299a729335f1298b43245842678cb9f43a6310351b18fb577d6e33165ec" + sha256: bced5679c7df11190e1ddc35f3222c858f328fff85c3942e46e7f5589bf9eb84 url: "https://pub.dev" source: hosted - version: "2.0.6" + version: "2.1.0" path_provider_windows: dependency: transitive description: name: path_provider_windows - sha256: "1cb68ba4cd3a795033de62ba1b7b4564dace301f952de6bfb3cd91b202b6ee96" + sha256: ee0e0d164516b90ae1f970bdf29f726f1aa730d7cfc449ecc74c495378b705da url: "https://pub.dev" source: hosted - version: "2.1.7" + version: "2.2.0" petitparser: dependency: transitive description: @@ -1103,18 +1095,18 @@ packages: dependency: "direct main" description: name: share_plus - sha256: ed3fcea4f789ed95913328e629c0c53e69e80e08b6c24542f1b3576046c614e8 + sha256: "6cec740fa0943a826951223e76218df002804adb588235a8910dc3d6b0654e11" url: "https://pub.dev" source: hosted - version: "7.0.2" + version: "7.1.0" share_plus_platform_interface: dependency: transitive description: name: share_plus_platform_interface - sha256: "0c6e61471bd71b04a138b8b588fa388e66d8b005e6f2deda63371c5c505a0981" + sha256: "357412af4178d8e11d14f41723f80f12caea54cf0d5cd29af9dcdab85d58aea7" url: "https://pub.dev" source: hosted - version: "3.2.1" + version: "3.3.0" shared_preferences: dependency: transitive description: @@ -1328,22 +1320,6 @@ packages: url: "https://pub.dev" source: hosted 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: @@ -1500,10 +1476,10 @@ packages: dependency: "direct main" description: name: walletconnect_flutter_v2 - sha256: "47baee579dca5976ac9516fd860f81d951d591e4772df05cd0ae365b57d61f80" + sha256: fd82676d335550a99bd706727852289eedbb561c44a586cb20d081b5eb6086a4 url: "https://pub.dev" source: hosted - version: "2.0.15" + version: "2.1.0" watcher: dependency: transitive description: @@ -1516,10 +1492,10 @@ packages: dependency: transitive description: name: web3dart - sha256: eb4302857aec35f8eb51a54c4bcea40f0647f1160683291ec77bfeff6dae6c08 + sha256: bebbea9278723cef51d21caf65668860e7547f59114fe9f8af01b873a72ba0e6 url: "https://pub.dev" source: hosted - version: "2.7.0" + version: "2.7.1" web_socket_channel: dependency: transitive description: @@ -1564,10 +1540,10 @@ packages: dependency: transitive description: name: xdg_directories - sha256: e0b1147eec179d3911f1f19b59206448f78195ca1d20514134e10641b7d7fbff + sha256: f0c26453a2d47aa4c2570c6a033246a3fc62da2fe23c7ffdd0a7495086dc0247 url: "https://pub.dev" source: hosted - version: "1.0.1" + version: "1.0.2" xml: dependency: transitive description: @@ -1588,9 +1564,9 @@ packages: dependency: "direct main" description: path: "." - ref: a7f2598f2125f3ed95dd19116038dd377f9d321a - resolved-ref: a7f2598f2125f3ed95dd19116038dd377f9d321a - url: "https://github.com/alienc0der/znn_sdk_dart.git" + ref: master + resolved-ref: f3adff23fa6af3882b25bfb8a1d157f04f8a7b5d + url: "https://github.com/zenon-network/znn_sdk_dart.git" source: git version: "0.0.4" zxing2: diff --git a/pubspec.yaml b/pubspec.yaml index 4a5397a3..d84908c5 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -12,7 +12,7 @@ dependencies: fl_chart: ^0.63.0 overlay_support: ^2.1.0 - lottie: ^2.5.0 + lottie: ^2.6.0 dotted_border: ^2.0.0+2 flutter_svg: ^2.0.0+1 flutter_vector_icons: ^2.0.0 @@ -24,16 +24,16 @@ dependencies: auto_size_text: ^3.0.0 window_manager: ^0.3.5 stacked: ^3.4.1 - path_provider: ^2.0.8 + path_provider: ^2.1.0 rxdart: ^0.27.3 hive: ^2.0.5 url_launcher: ^6.1.12 provider: ^6.0.2 intl: ^0.18.0 - package_info_plus: ^4.0.2 - device_info_plus: ^9.0.2 + package_info_plus: ^4.1.0 + device_info_plus: ^9.0.3 infinite_scroll_pagination: ^3.1.0 - share_plus: ^7.0.2 + share_plus: ^7.1.0 page_transition: ^2.0.4 file_selector: ^1.0.0 pretty_qr_code: ^2.0.3 @@ -44,11 +44,11 @@ dependencies: number_selector: ^1.0.5 znn_sdk_dart: git: - url: https://github.com/alienc0der/znn_sdk_dart.git - ref: a7f2598f2125f3ed95dd19116038dd377f9d321a + url: https://github.com/zenon-network/znn_sdk_dart.git + ref: master json_rpc_2: ^3.0.2 path: ^1.8.2 - ffi: ^2.0.1 + ffi: ^2.1.0 get_it: ^7.2.0 hex: ^0.2.0 local_notifier: ^0.1.5 @@ -57,16 +57,16 @@ dependencies: launch_at_startup: ^0.2.1 app_links: ^3.4.3 logging: ^1.2.0 - walletconnect_flutter_v2: ^2.0.15 + walletconnect_flutter_v2: ^2.1.0 preference_list: ^0.0.1 screen_capturer: ^0.1.6 - 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 + badges: ^3.1.1 dev_dependencies: build_runner: ^2.4.6 diff --git a/windows/CreateGitMetadata.ps1 b/windows/CreateGitMetadata.ps1 index e376da88..10e49efd 100644 --- a/windows/CreateGitMetadata.ps1 +++ b/windows/CreateGitMetadata.ps1 @@ -7,8 +7,8 @@ $GIT_COMMIT_FILE = "${PSScriptRoot}\..\lib\utils\metadata.dart" Clear-Content $GIT_COMMIT_FILE -Force -Add-Content $GIT_COMMIT_FILE "const String gitBranchName = '$GIT_BRANCH_NAME';" -Add-Content $GIT_COMMIT_FILE "const String gitCommitHash = '$GIT_COMMIT_HASH';" -Add-Content $GIT_COMMIT_FILE "const String gitCommitMessage = '$GIT_COMMIT_MESSAGE';" -Add-Content $GIT_COMMIT_FILE "const String gitCommitDate = '$GIT_COMMIT_DATE';" -Add-Content $GIT_COMMIT_FILE "const String gitOriginUrl = '$GIT_ORIGIN_URL';" \ No newline at end of file +Add-Content $GIT_COMMIT_FILE "const String gitBranchName = `"${GIT_BRANCH_NAME}`";" +Add-Content $GIT_COMMIT_FILE "const String gitCommitHash = `"${GIT_COMMIT_HASH}`";" +Add-Content $GIT_COMMIT_FILE "const String gitCommitMessage = `"${GIT_COMMIT_MESSAGE}`";" +Add-Content $GIT_COMMIT_FILE "const String gitCommitDate = `"${GIT_COMMIT_DATE}`";" +Add-Content $GIT_COMMIT_FILE "const String gitOriginUrl = `"${GIT_ORIGIN_URL}`";" \ No newline at end of file