From 2b7d2f32a9f74159bd172368b8e32b97fcf19cf2 Mon Sep 17 00:00:00 2001 From: Alfreedom <00tango.bromine@icloud.com> Date: Wed, 9 Oct 2024 15:07:27 +0200 Subject: [PATCH 01/17] chain agnosticity --- .../reown_appkit/example/base/lib/main.dart | 24 +- .../base/lib/models/chain_metadata.dart | 51 ---- .../example/base/lib/pages/connect_page.dart | 266 +++++++++-------- .../base/lib/utils/crypto/chain_data.dart | 199 ------------- .../example/base/lib/utils/crypto/eip155.dart | 12 +- .../base/lib/utils/crypto/helpers.dart | 46 +-- .../example/base/lib/utils/crypto/solana.dart | 5 +- .../base/lib/widgets/chain_button.dart | 13 +- .../base/lib/widgets/session_widget.dart | 56 ++-- .../android/app/src/main/AndroidManifest.xml | 2 +- .../modal/ios/Runner/AppDelegate.swift | 26 ++ .../example/modal/lib/home_page.dart | 76 ++++- ...p155_service.dart => methods_service.dart} | 137 +++++---- .../modal/lib/widgets/session_widget.dart | 98 +++++-- packages/reown_appkit/lib/appkit_modal.dart | 4 +- .../lib/modal/appkit_modal_impl.dart | 273 ++++++++++-------- .../lib/modal/constants/string_constants.dart | 1 - .../lib/modal/i_appkit_modal_impl.dart | 9 +- .../models/public/appkit_modal_session.dart | 96 +++--- .../lib/modal/pages/approve_siwe.dart | 8 +- .../lib/modal/pages/connet_network_page.dart | 6 +- .../appkit_modal_select_network_page.dart | 13 +- .../blockchain_service.dart | 189 +++++++----- .../i_blockchain_service.dart | 13 +- .../coinbase_service/coinbase_service.dart | 8 +- .../explorer_service/explorer_service.dart | 11 +- .../models/request_params.dart | 8 + .../magic_service/models/frame_message.dart | 10 +- .../network_service/network_service.dart | 23 +- .../network_service_singleton.dart | 5 +- .../services/siwe_service/siwe_service.dart | 22 +- .../public/appkit_modal_default_networks.dart | 268 +++++++++-------- .../public/appkit_modal_networks_utils.dart | 25 ++ .../utils/public/appkit_modal_utils.dart | 2 - .../modal/widgets/avatars/account_avatar.dart | 10 +- .../modal/widgets/avatars/wallet_avatar.dart | 13 +- .../modal/widgets/buttons/address_button.dart | 10 +- .../widgets/buttons/address_copy_button.dart | 12 +- .../lists/grid_items/wallet_grid_item.dart | 4 +- .../public/appkit_modal_account_button.dart | 12 +- .../modal/widgets/text/appkit_address.dart | 6 +- .../network_service_items_listener.dart | 45 +-- 42 files changed, 1144 insertions(+), 973 deletions(-) delete mode 100644 packages/reown_appkit/example/base/lib/models/chain_metadata.dart delete mode 100644 packages/reown_appkit/example/base/lib/utils/crypto/chain_data.dart rename packages/reown_appkit/example/modal/lib/services/{eip155_service.dart => methods_service.dart} (76%) create mode 100644 packages/reown_appkit/lib/modal/utils/public/appkit_modal_networks_utils.dart delete mode 100644 packages/reown_appkit/lib/modal/utils/public/appkit_modal_utils.dart diff --git a/packages/reown_appkit/example/base/lib/main.dart b/packages/reown_appkit/example/base/lib/main.dart index 1a4c7f2..bd28e18 100644 --- a/packages/reown_appkit/example/base/lib/main.dart +++ b/packages/reown_appkit/example/base/lib/main.dart @@ -4,14 +4,11 @@ import 'dart:developer'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:reown_appkit/reown_appkit.dart'; - -import 'package:reown_appkit_dapp/models/chain_metadata.dart'; import 'package:reown_appkit_dapp/models/page_data.dart'; import 'package:reown_appkit_dapp/pages/connect_page.dart'; import 'package:reown_appkit_dapp/pages/pairings_page.dart'; import 'package:reown_appkit_dapp/pages/sessions_page.dart'; import 'package:reown_appkit_dapp/utils/constants.dart'; -import 'package:reown_appkit_dapp/utils/crypto/chain_data.dart'; import 'package:reown_appkit_dapp/utils/crypto/helpers.dart'; import 'package:reown_appkit_dapp/utils/dart_defines.dart'; import 'package:reown_appkit_dapp/utils/deep_link_handler.dart'; @@ -172,10 +169,14 @@ class _MyHomePageState extends State { DeepLinkHandler.init(_appKit!); DeepLinkHandler.checkInitialLink(); + final allChains = ReownAppKitModalNetworks.getAllSupportedNetworks(); // Loop through all the chain data - for (final ChainMetadata chain in ChainData.allChains) { + for (final chain in allChains) { // Loop through the events for that chain - for (final event in getChainEvents(chain.type)) { + final namespace = ReownAppKitModalNetworks.getNamespaceForChainId( + chain.chainId, + ); + for (final event in getChainEvents(namespace)) { _appKit!.registerEventHandler( chainId: chain.chainId, event: event, @@ -215,9 +216,13 @@ class _MyHomePageState extends State { } // Loop through all the chain data - for (final ChainMetadata chain in ChainData.allChains) { + final allChains = ReownAppKitModalNetworks.getAllSupportedNetworks(); + for (final chain in allChains) { // Loop through the events for that chain - for (final event in getChainEvents(chain.type)) { + final namespace = ReownAppKitModalNetworks.getNamespaceForChainId( + chain.chainId, + ); + for (final event in getChainEvents(namespace)) { _appKit!.registerEventHandler( chainId: chain.chainId, event: event, @@ -467,8 +472,11 @@ class _MyHomePageState extends State { }, getSession: () async { // Return proper session from your Web Service - final address = _appKitModal!.session!.address!; final chainId = _appKitModal!.session!.chainId; + final namespace = ReownAppKitModalNetworks.getNamespaceForChainId( + chainId, + ); + final address = _appKitModal!.session!.getAddress(namespace)!; return SIWESession(address: address, chains: [chainId]); }, onSignIn: (SIWESession session) { diff --git a/packages/reown_appkit/example/base/lib/models/chain_metadata.dart b/packages/reown_appkit/example/base/lib/models/chain_metadata.dart deleted file mode 100644 index 834a0ba..0000000 --- a/packages/reown_appkit/example/base/lib/models/chain_metadata.dart +++ /dev/null @@ -1,51 +0,0 @@ -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; - -enum ChainType { - eip155, - solana, - kadena, - cosmos, - polkadot, -} - -class ChainMetadata { - final String chainId; - final String name; - final String logo; - final bool isTestnet; - final Color color; - final ChainType type; - final List rpc; - - const ChainMetadata({ - required this.chainId, - required this.name, - required this.logo, - this.isTestnet = false, - required this.color, - required this.type, - required this.rpc, - }); - - @override - bool operator ==(Object other) { - if (identical(this, other)) return true; - - return other is ChainMetadata && - other.chainId == chainId && - other.name == name && - other.logo == logo && - other.isTestnet == isTestnet && - listEquals(other.rpc, rpc); - } - - @override - int get hashCode { - return chainId.hashCode ^ - name.hashCode ^ - logo.hashCode ^ - rpc.hashCode ^ - isTestnet.hashCode; - } -} diff --git a/packages/reown_appkit/example/base/lib/pages/connect_page.dart b/packages/reown_appkit/example/base/lib/pages/connect_page.dart index 7090342..c7337fc 100644 --- a/packages/reown_appkit/example/base/lib/pages/connect_page.dart +++ b/packages/reown_appkit/example/base/lib/pages/connect_page.dart @@ -1,4 +1,5 @@ import 'dart:async'; +import 'dart:convert'; import 'package:fl_toast/fl_toast.dart'; import 'package:flutter/foundation.dart'; @@ -7,9 +8,7 @@ import 'package:flutter/services.dart'; import 'package:package_info_plus/package_info_plus.dart'; import 'package:qr_flutter/qr_flutter.dart'; import 'package:reown_appkit/reown_appkit.dart'; -import 'package:reown_appkit_dapp/models/chain_metadata.dart'; import 'package:reown_appkit_dapp/utils/constants.dart'; -import 'package:reown_appkit_dapp/utils/crypto/chain_data.dart'; import 'package:reown_appkit_dapp/utils/crypto/eip155.dart'; import 'package:reown_appkit_dapp/utils/crypto/polkadot.dart'; import 'package:reown_appkit_dapp/utils/crypto/solana.dart'; @@ -31,8 +30,9 @@ class ConnectPage extends StatefulWidget { } class ConnectPageState extends State { - final List _selectedChains = []; + final List _selectedChains = []; bool _shouldDismissQrCode = true; + bool _linkMode = false; @override void initState() { @@ -73,7 +73,7 @@ class ConnectPageState extends State { super.dispose(); } - void _selectChain(ChainMetadata chain) { + void _selectChain(ReownAppKitModalNetworkInfo chain) { setState(() { if (_selectedChains.contains(chain)) { _selectedChains.remove(chain); @@ -90,61 +90,60 @@ class ConnectPageState extends State { void _updateNamespaces() { optionalNamespaces = {}; - final evmChains = - _selectedChains.where((e) => e.type == ChainType.eip155).toList(); + final evmChains = _selectedChains.where((c) { + final ns = ReownAppKitModalNetworks.getNamespaceForChainId(c.chainId); + return ns == 'eip155'; + }).toList(); if (evmChains.isNotEmpty) { optionalNamespaces['eip155'] = RequiredNamespace( - chains: evmChains.map((c) => c.chainId).toList(), + chains: evmChains.map((c) => 'eip155:${c.chainId}').toList(), methods: EIP155.methods.values.toList(), events: EIP155.events.values.toList(), ); } - final solanaChains = - _selectedChains.where((e) => e.type == ChainType.solana).toList(); + final solanaChains = _selectedChains.where((c) { + final ns = ReownAppKitModalNetworks.getNamespaceForChainId(c.chainId); + return ns == 'solana'; + }).toList(); if (solanaChains.isNotEmpty) { optionalNamespaces['solana'] = RequiredNamespace( - chains: solanaChains.map((c) => c.chainId).toList(), + chains: solanaChains.map((c) => 'solana:${c.chainId}').toList(), methods: Solana.methods.values.toList(), events: Solana.events.values.toList(), ); } - final polkadotChains = - _selectedChains.where((e) => e.type == ChainType.polkadot).toList(); + final polkadotChains = _selectedChains.where((c) { + final ns = ReownAppKitModalNetworks.getNamespaceForChainId(c.chainId); + return ns == 'polkadot'; + }).toList(); if (polkadotChains.isNotEmpty) { optionalNamespaces['polkadot'] = RequiredNamespace( - chains: polkadotChains.map((c) => c.chainId).toList(), + chains: polkadotChains.map((c) => 'polkadot:${c.chainId}').toList(), methods: Polkadot.methods.values.toList(), events: Polkadot.events.values.toList(), ); } - if (optionalNamespaces.isEmpty) { - requiredNamespaces = {}; - } else { - // WalletConnectModal still requires to have requiredNamespaces - // this has to be changed in that SDK - requiredNamespaces = { - 'eip155': const RequiredNamespace( - chains: ['eip155:1'], - methods: ['personal_sign', 'eth_signTransaction'], - events: ['chainChanged'], - ), - }; - } + debugPrint( + '[$runtimeType] optionalNamespaces ${jsonEncode(optionalNamespaces)}', + ); } @override Widget build(BuildContext context) { // Build the list of chain buttons, clear if the textnet changed - final testChains = ChainData.allChains.where((e) => e.isTestnet).toList(); - final mainChains = ChainData.allChains.where((e) => !e.isTestnet).toList(); + final allChains = ReownAppKitModalNetworks.getAllSupportedNetworks( + namespace: _linkMode ? 'eip155' : null, + ); + final mainChains = allChains.where((e) => !e.isTestNetwork).toList(); + final testChains = allChains.where((e) => e.isTestNetwork).toList(); final List chainButtons = []; final List testButtons = []; - for (final ChainMetadata chain in mainChains) { + for (final chain in mainChains) { // Build the button chainButtons.add( ChainButton( @@ -154,7 +153,7 @@ class ConnectPageState extends State { ), ); } - for (final ChainMetadata chain in testChains) { + for (final chain in testChains) { // Build the button testButtons.add( ChainButton( @@ -166,28 +165,23 @@ class ConnectPageState extends State { } return ListView( - padding: const EdgeInsets.symmetric(horizontal: StyleConstants.linear8), + padding: const EdgeInsets.symmetric( + horizontal: StyleConstants.linear8, + ), children: [ Text( widget.appKitModal.appKit!.metadata.name, style: StyleConstants.subtitleText, textAlign: TextAlign.center, ), - const SizedBox(height: StyleConstants.linear8), - const Divider(), + const SizedBox(height: StyleConstants.linear16), + const Divider(height: 1.0), + const SizedBox(height: StyleConstants.linear16), const Text( - 'Connect With AppKit Modal and Link Mode:', + 'Connect With AppKit Modal', style: StyleConstants.buttonText, textAlign: TextAlign.center, ), - Text( - 'Only EVM chains', - style: TextStyle( - color: Colors.black.withOpacity(0.7), - fontSize: 12.0, - ), - textAlign: TextAlign.center, - ), const SizedBox(height: StyleConstants.linear8), Row( mainAxisAlignment: MainAxisAlignment.center, @@ -222,7 +216,7 @@ class ConnectPageState extends State { Row( children: [ Expanded( - child: const Divider(), + child: const Divider(height: 1.0), ), const Text( ' Or ', @@ -230,17 +224,32 @@ class ConnectPageState extends State { textAlign: TextAlign.center, ), Expanded( - child: const Divider(), + child: const Divider(height: 1.0), ), ], ), - const SizedBox(height: StyleConstants.linear8), + const SizedBox(height: StyleConstants.linear16), const Text( - 'Connect With AppKit multichain', + 'Custom connection', style: StyleConstants.buttonText, textAlign: TextAlign.center, ), - const SizedBox(height: StyleConstants.linear8), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text('Link Mode'), + Switch( + value: _linkMode, + onChanged: (value) { + requiredNamespaces = {}; + optionalNamespaces = {}; + _selectedChains.clear(); + setState(() => _linkMode = value); + }, + ), + ], + ), + const SizedBox(height: StyleConstants.linear16), Wrap( spacing: 10.0, children: chainButtons, @@ -253,10 +262,8 @@ class ConnectPageState extends State { ), const SizedBox(height: StyleConstants.linear16), // const Divider(), - Row( - children: [ - Expanded( - child: Column( + !_linkMode + ? Column( crossAxisAlignment: CrossAxisAlignment.center, children: [ const Text( @@ -298,11 +305,8 @@ class ConnectPageState extends State { }).toList(), ), ], - ), - ), - const SizedBox.square(dimension: 8.0), - Expanded( - child: Column( + ) + : Column( crossAxisAlignment: CrossAxisAlignment.center, children: [ const Text( @@ -347,81 +351,13 @@ class ConnectPageState extends State { ), ], ), - ), - ], - ), const SizedBox(height: StyleConstants.linear16), const Divider(height: 1.0), + const SizedBox(height: StyleConstants.linear8), + _FooterWidget(appKitModal: widget.appKitModal), ], ), ), - const SizedBox(height: StyleConstants.linear16), - const Text( - 'Redirect:', - style: TextStyle(fontWeight: FontWeight.bold), - ), - Row( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const Text('Native: '), - Expanded( - child: Text( - '${widget.appKitModal.appKit!.metadata.redirect?.native}', - style: const TextStyle(fontWeight: FontWeight.bold), - ), - ), - ], - ), - Row( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const Text('Universal: '), - Expanded( - child: Text( - '${widget.appKitModal.appKit!.metadata.redirect?.universal}', - style: const TextStyle(fontWeight: FontWeight.bold), - ), - ), - ], - ), - Row( - children: [ - const Text('Link Mode: '), - Text( - '${widget.appKitModal.appKit!.metadata.redirect?.linkMode}', - style: const TextStyle(fontWeight: FontWeight.bold), - ), - ], - ), - const SizedBox(height: StyleConstants.linear8), - FutureBuilder( - future: PackageInfo.fromPlatform(), - builder: (context, snapshot) { - if (!snapshot.hasData) { - return const SizedBox.shrink(); - } - final v = snapshot.data!.version; - final b = snapshot.data!.buildNumber; - const f = String.fromEnvironment('FLUTTER_APP_FLAVOR'); - // return Text('App Version: $v-$f ($b) - SDK v$packageVersion'); - return Row( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const Text('App Version: '), - Expanded( - child: Text( - '$v-$f ($b) - SDK v$packageVersion', - style: const TextStyle(fontWeight: FontWeight.bold), - ), - ), - ], - ); - }, - ), - const SizedBox(height: StyleConstants.linear16), ], ); } @@ -635,7 +571,7 @@ class ConnectPageState extends State { if (states.contains(MaterialState.disabled)) { return StyleConstants.grayColor; } - return StyleConstants.primaryColor; + return Colors.blue; }, ), textStyle: MaterialStateProperty.resolveWith( @@ -661,6 +597,82 @@ class ConnectPageState extends State { ); } +class _FooterWidget extends StatefulWidget { + const _FooterWidget({required this.appKitModal}); + final ReownAppKitModal appKitModal; + + @override + State<_FooterWidget> createState() => __FooterWidgetState(); +} + +class __FooterWidgetState extends State<_FooterWidget> { + @override + Widget build(BuildContext context) { + final textStyle = TextStyle(fontSize: 12.0); + final textStyleBold = textStyle.copyWith(fontWeight: FontWeight.bold); + final redirect = widget.appKitModal.appKit!.metadata.redirect; + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const SizedBox(height: StyleConstants.linear8), + Text('Redirect:', style: textStyleBold), + Row( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('Native: ', style: textStyle), + Expanded( + child: Text('${redirect?.native}', style: textStyleBold), + ), + ], + ), + Row( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('Universal: ', style: textStyle), + Expanded( + child: Text('${redirect?.universal}', style: textStyleBold), + ), + ], + ), + Row( + children: [ + Text('Link Mode: ', style: textStyle), + Text('${redirect?.linkMode}', style: textStyleBold), + ], + ), + FutureBuilder( + future: PackageInfo.fromPlatform(), + builder: (context, snapshot) { + if (!snapshot.hasData) { + return const SizedBox.shrink(); + } + final v = snapshot.data!.version; + final b = snapshot.data!.buildNumber; + const f = String.fromEnvironment('FLUTTER_APP_FLAVOR'); + // return Text('App Version: $v-$f ($b) - SDK v$packageVersion'); + return Row( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('App Version: ', style: textStyle), + Expanded( + child: Text( + '$v-$f ($b) - SDK v$packageVersion', + style: textStyleBold, + ), + ), + ], + ); + }, + ), + const SizedBox(height: StyleConstants.linear8), + ], + ); + } +} + class QRCodeScreen extends StatefulWidget { const QRCodeScreen({ super.key, diff --git a/packages/reown_appkit/example/base/lib/utils/crypto/chain_data.dart b/packages/reown_appkit/example/base/lib/utils/crypto/chain_data.dart deleted file mode 100644 index b6b4396..0000000 --- a/packages/reown_appkit/example/base/lib/utils/crypto/chain_data.dart +++ /dev/null @@ -1,199 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:reown_appkit_dapp/models/chain_metadata.dart'; - -class ChainData { - static final List eip155Chains = [ - ChainMetadata( - type: ChainType.eip155, - chainId: 'eip155:1', - name: 'Ethereum', - logo: '/chain-logos/eip155-1.png', - color: Colors.blue.shade300, - rpc: ['https://eth.drpc.org'], - ), - ChainMetadata( - type: ChainType.eip155, - chainId: 'eip155:137', - name: 'Polygon', - logo: '/chain-logos/eip155-137.png', - color: Colors.purple.shade300, - rpc: ['https://polygon-rpc.com/'], - ), - const ChainMetadata( - type: ChainType.eip155, - chainId: 'eip155:42161', - name: 'Arbitrum', - logo: '/chain-logos/eip155-42161.png', - color: Colors.blue, - rpc: ['https://arbitrum.blockpi.network/v1/rpc/public'], - ), - const ChainMetadata( - type: ChainType.eip155, - chainId: 'eip155:10', - name: 'OP Mainnet', - logo: '/chain-logos/eip155-10.png', - color: Colors.red, - rpc: ['https://mainnet.optimism.io/'], - ), - const ChainMetadata( - type: ChainType.eip155, - chainId: 'eip155:43114', - name: 'Avalanche', - logo: '/chain-logos/eip155-43114.png', - color: Colors.orange, - rpc: ['https://api.avax.network/ext/bc/C/rpc'], - ), - const ChainMetadata( - type: ChainType.eip155, - chainId: 'eip155:56', - name: 'BNB Smart Chain Mainnet', - logo: '/chain-logos/eip155-56.png', - color: Colors.orange, - rpc: ['https://bsc-dataseed1.bnbchain.org'], - ), - const ChainMetadata( - type: ChainType.eip155, - chainId: 'eip155:42220', - name: 'Celo', - logo: '/chain-logos/eip155-42220.png', - color: Colors.green, - rpc: ['https://forno.celo.org/'], - ), - const ChainMetadata( - type: ChainType.eip155, - chainId: 'eip155:100', - name: 'Gnosis', - logo: '/chain-logos/eip155-100.png', - color: Colors.greenAccent, - rpc: ['https://rpc.gnosischain.com/'], - ), - const ChainMetadata( - type: ChainType.eip155, - chainId: 'eip155:324', - name: 'zkSync', - logo: '/chain-logos/eip155-324.png', - color: Colors.black, - rpc: ['https://mainnet.era.zksync.io'], - ), - ChainMetadata( - type: ChainType.eip155, - chainId: 'eip155:11155111', - name: 'Sepolia', - logo: '/chain-logos/eip155-1.png', - color: Colors.blue.shade300, - isTestnet: true, - rpc: ['https://ethereum-sepolia.publicnode.com'], - ), - ChainMetadata( - type: ChainType.eip155, - chainId: 'eip155:80001', - name: 'Polygon Mumbai', - logo: '/chain-logos/eip155-137.png', - color: Colors.purple.shade300, - isTestnet: true, - rpc: ['https://matic-mumbai.chainstacklabs.com'], - ), - ]; - - static final List solanaChains = [ - const ChainMetadata( - type: ChainType.solana, - chainId: 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp', - name: 'Solana Mainnet', - logo: '/chain-logos/solana.png', - color: Color.fromARGB(255, 247, 0, 255), - rpc: ['https://api.mainnet-beta.solana.com'], - ), - const ChainMetadata( - type: ChainType.solana, - chainId: 'solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1', - name: 'Solana Devnet', - logo: '/chain-logos/solana.png', - color: Color.fromARGB(255, 247, 0, 255), - rpc: ['https://api.devnet.solana.com'], - ), - const ChainMetadata( - type: ChainType.solana, - chainId: 'solana:4uhcVJyU9pJkvQyS88uRDiswHXSCkY3z', - name: 'Solana Testnet', - logo: '/chain-logos/solana.png', - color: Colors.black, - isTestnet: true, - rpc: ['https://api.testnet.solana.com'], - ), - ]; - - static final List cosmosChains = [ - // TODO TO BE SUPPORTED - const ChainMetadata( - type: ChainType.cosmos, - chainId: 'cosmos:cosmoshub-4', - name: 'Cosmos Mainnet', - logo: '/chain-logos/cosmos.png', - color: Colors.purple, - rpc: [ - 'https://cosmos-rpc.polkachu.com:443', - 'https://rpc-cosmoshub-ia.cosmosia.notional.ventures:443', - 'https://rpc.cosmos.network:443', - ], - ), - ]; - - static final List kadenaChains = [ - // TODO TO BE SUPPORTED - const ChainMetadata( - type: ChainType.kadena, - chainId: 'kadena:mainnet01', - name: 'Kadena Mainnet', - logo: '/chain-logos/kadena.png', - color: Colors.green, - rpc: [ - 'https://api.chainweb.com', - ], - ), - const ChainMetadata( - type: ChainType.kadena, - chainId: 'kadena:testnet04', - name: 'Kadena Testnet', - logo: '/chain-logos/kadena.png', - color: Colors.green, - isTestnet: true, - rpc: [ - 'https://api.chainweb.com', - ], - ), - ]; - - static final List polkadotChains = [ - const ChainMetadata( - type: ChainType.polkadot, - chainId: 'polkadot:91b171bb158e2d3848fa23a9f1c25182', - name: 'Polkadot Mainnet', - logo: '/chain-logos/polkadot.png', - color: Color.fromARGB(255, 174, 57, 220), - rpc: [ - 'wss://rpc.polkadot.io', - // 'wss://rpc.matrix.canary.enjin.io' - ], - ), - const ChainMetadata( - type: ChainType.polkadot, - chainId: 'polkadot:e143f23803ac50e8f6f8e62695d1ce9e', - name: 'Polkadot Testnet (Westend)', - logo: '/chain-logos/polkadot.png', - color: Color.fromARGB(255, 174, 57, 220), - isTestnet: true, - rpc: [ - 'wss://westend-rpc.polkadot.io', - ], - ), - ]; - - static final List allChains = [ - ...eip155Chains, - ...solanaChains, - ...polkadotChains, - // ...kadenaChains, - // ...cosmosChains, - ]; -} diff --git a/packages/reown_appkit/example/base/lib/utils/crypto/eip155.dart b/packages/reown_appkit/example/base/lib/utils/crypto/eip155.dart index 5cfde85..0bf6ae3 100644 --- a/packages/reown_appkit/example/base/lib/utils/crypto/eip155.dart +++ b/packages/reown_appkit/example/base/lib/utils/crypto/eip155.dart @@ -3,8 +3,6 @@ import 'dart:convert'; import 'package:eth_sig_util/util/utils.dart'; import 'package:intl/intl.dart'; import 'package:reown_appkit/reown_appkit.dart'; -import 'package:reown_appkit_dapp/models/chain_metadata.dart'; -import 'package:reown_appkit_dapp/utils/crypto/chain_data.dart'; import 'package:reown_appkit_dapp/utils/smart_contracts.dart'; import 'package:reown_appkit_dapp/utils/test_data.dart'; @@ -39,7 +37,7 @@ class EIP155 { required IReownAppKit appKit, required String topic, required String method, - required ChainMetadata chainData, + required ReownAppKitModalNetworkInfo chainData, required String address, }) { switch (method) { @@ -96,14 +94,16 @@ class EIP155 { EthereumAddress.fromHex(SepoliaTestContract.contractAddress), ); - final sepolia = - ChainData.allChains.firstWhere((e) => e.chainId == 'eip155:11155111'); + final sepolia = ReownAppKitModalNetworks.getNetworkById( + 'eip155', + '11155111', + )!; switch (action) { case 'read': return readSmartContract( appKit: appKit, - rpcUrl: sepolia.rpc.first, + rpcUrl: sepolia.rpcUrl, contract: deployedContract, address: address, ); diff --git a/packages/reown_appkit/example/base/lib/utils/crypto/helpers.dart b/packages/reown_appkit/example/base/lib/utils/crypto/helpers.dart index 6ead377..afb871c 100644 --- a/packages/reown_appkit/example/base/lib/utils/crypto/helpers.dart +++ b/packages/reown_appkit/example/base/lib/utils/crypto/helpers.dart @@ -1,53 +1,27 @@ -import 'package:flutter/material.dart'; -import 'package:reown_appkit_dapp/models/chain_metadata.dart'; -import 'package:reown_appkit_dapp/utils/crypto/chain_data.dart'; import 'package:reown_appkit_dapp/utils/crypto/eip155.dart'; import 'package:reown_appkit_dapp/utils/crypto/polkadot.dart'; import 'package:reown_appkit_dapp/utils/crypto/solana.dart'; -String getChainName(String chain) { - try { - return ChainData.allChains - .where((element) => element.chainId == chain) - .first - .name; - } catch (e) { - debugPrint('[SampleDapp] Invalid chain'); - } - return 'Unknown'; -} - -ChainMetadata getChainMetadataFromChain(String chain) { - try { - return ChainData.allChains - .where((element) => element.chainId == chain) - .first; - } catch (e) { - debugPrint('[SampleDapp] Invalid chain'); - } - return ChainData.eip155Chains[0]; -} - -List getChainMethods(ChainType value) { - switch (value) { - case ChainType.eip155: +List getChainMethods(String namespace) { + switch (namespace) { + case 'eip155': return EIP155.methods.values.toList(); - case ChainType.solana: + case 'solana': return Solana.methods.values.toList(); - case ChainType.polkadot: + case 'polkadot': return Polkadot.methods.values.toList(); default: return []; } } -List getChainEvents(ChainType value) { - switch (value) { - case ChainType.eip155: +List getChainEvents(String namespace) { + switch (namespace) { + case 'eip155': return EIP155.events.values.toList(); - case ChainType.solana: + case 'solana': return Solana.events.values.toList(); - case ChainType.polkadot: + case 'polkadot': return Polkadot.events.values.toList(); default: return []; diff --git a/packages/reown_appkit/example/base/lib/utils/crypto/solana.dart b/packages/reown_appkit/example/base/lib/utils/crypto/solana.dart index f060d5b..defd03f 100644 --- a/packages/reown_appkit/example/base/lib/utils/crypto/solana.dart +++ b/packages/reown_appkit/example/base/lib/utils/crypto/solana.dart @@ -2,7 +2,6 @@ import 'dart:convert'; import 'package:bs58/bs58.dart'; import 'package:solana_web3/solana_web3.dart' as solana; import 'package:reown_appkit/reown_appkit.dart'; -import 'package:reown_appkit_dapp/models/chain_metadata.dart'; enum SolanaMethods { solanaSignTransaction, @@ -25,7 +24,7 @@ class Solana { required IReownAppKit appKit, required String topic, required String method, - required ChainMetadata chainData, + required ReownAppKitModalNetworkInfo chainData, required String address, }) async { switch (method) { @@ -48,7 +47,7 @@ class Solana { case 'solana_signTransaction': // Create a connection to the devnet cluster. final cluster = solana.Cluster.https( - Uri.parse(chainData.rpc.first).authority, + Uri.parse(chainData.rpcUrl).authority, ); // final cluster = solana.Cluster.devnet; final connection = solana.Connection(cluster); diff --git a/packages/reown_appkit/example/base/lib/widgets/chain_button.dart b/packages/reown_appkit/example/base/lib/widgets/chain_button.dart index b923c1e..2184019 100644 --- a/packages/reown_appkit/example/base/lib/widgets/chain_button.dart +++ b/packages/reown_appkit/example/base/lib/widgets/chain_button.dart @@ -1,7 +1,7 @@ import 'dart:math'; import 'package:flutter/material.dart'; -import 'package:reown_appkit_dapp/models/chain_metadata.dart'; +import 'package:reown_appkit/reown_appkit.dart'; import 'package:reown_appkit_dapp/utils/constants.dart'; class ChainButton extends StatelessWidget { @@ -12,7 +12,7 @@ class ChainButton extends StatelessWidget { this.selected = false, }); - final ChainMetadata chain; + final ReownAppKitModalNetworkInfo chain; final VoidCallback onPressed; final bool selected; @@ -24,19 +24,20 @@ class ChainButton extends StatelessWidget { 2) - 14.0, height: StyleConstants.linear48, - margin: const EdgeInsets.symmetric( - vertical: StyleConstants.linear8, + margin: const EdgeInsets.only( + bottom: StyleConstants.linear8, ), child: ElevatedButton( onPressed: onPressed, style: ButtonStyle( + elevation: MaterialStateProperty.all(0.0), backgroundColor: MaterialStateProperty.all( - selected ? Colors.grey.shade400 : Colors.white, + selected ? Colors.white : Colors.grey.shade300, ), shape: MaterialStateProperty.all( RoundedRectangleBorder( side: BorderSide( - color: selected ? Colors.grey.shade400 : chain.color, + color: selected ? Colors.blue : Colors.grey.shade300, width: selected ? 4 : 2, ), borderRadius: BorderRadius.circular( diff --git a/packages/reown_appkit/example/base/lib/widgets/session_widget.dart b/packages/reown_appkit/example/base/lib/widgets/session_widget.dart index 88b5399..c41977c 100644 --- a/packages/reown_appkit/example/base/lib/widgets/session_widget.dart +++ b/packages/reown_appkit/example/base/lib/widgets/session_widget.dart @@ -1,8 +1,6 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:reown_appkit/reown_appkit.dart'; - -import 'package:reown_appkit_dapp/models/chain_metadata.dart'; import 'package:reown_appkit_dapp/utils/constants.dart'; import 'package:reown_appkit_dapp/utils/crypto/eip155.dart'; import 'package:reown_appkit_dapp/utils/crypto/helpers.dart'; @@ -100,11 +98,17 @@ class SessionWidgetState extends State { Widget _buildAccountWidget(String namespaceAccount) { final chainId = NamespaceUtils.getChainFromAccount(namespaceAccount); final account = NamespaceUtils.getAccount(namespaceAccount); - final chainMetadata = getChainMetadataFromChain(chainId); + final namespace = NamespaceUtils.getNamespaceFromChain( + chainId, + ); + final chainData = ReownAppKitModalNetworks.getNetworkById( + namespace, + chainId.split(':').last, + ); final List children = [ Text( - chainMetadata.name, + chainData!.name, style: StyleConstants.subtitleText, ), const SizedBox( @@ -123,7 +127,7 @@ class SessionWidgetState extends State { ), ]; - children.addAll(_buildChainMethodButtons(chainMetadata, account)); + children.addAll(_buildChainMethodButtons(chainData, account)); children.add(const Divider()); @@ -141,11 +145,7 @@ class SessionWidgetState extends State { style: StyleConstants.subtitleText, ), ]); - children.addAll( - _buildChainEventsTiles( - chainMetadata, - ), - ); + children.addAll(_buildChainEventsTiles(chainData)); // final ChainMetadata return Container( @@ -159,7 +159,7 @@ class SessionWidgetState extends State { ), decoration: BoxDecoration( border: Border.all( - color: chainMetadata.color, + color: Colors.blue, ), borderRadius: const BorderRadius.all( Radius.circular( @@ -174,13 +174,16 @@ class SessionWidgetState extends State { } List _buildChainMethodButtons( - ChainMetadata chainMetadata, + ReownAppKitModalNetworkInfo chainMetadata, String address, ) { final List buttons = []; // Add Methods - for (final String method in getChainMethods(chainMetadata.type)) { - final namespaces = _session.namespaces[chainMetadata.type.name]; + final namespace = ReownAppKitModalNetworks.getNamespaceForChainId( + chainMetadata.chainId, + ); + for (final String method in getChainMethods(namespace)) { + final namespaces = widget.session.namespaces[namespace]; final supported = namespaces?.methods.contains(method) ?? false; buttons.add( Container( @@ -205,7 +208,7 @@ class SessionWidgetState extends State { backgroundColor: MaterialStateProperty.resolveWith( (states) => states.contains(MaterialState.disabled) ? Colors.grey - : chainMetadata.color, + : Colors.blue, ), shape: MaterialStateProperty.all( RoundedRectangleBorder( @@ -230,11 +233,14 @@ class SessionWidgetState extends State { Future callChainMethod( String method, - ChainMetadata chainMetadata, + ReownAppKitModalNetworkInfo chainMetadata, String address, ) { - switch (chainMetadata.type) { - case ChainType.eip155: + final namespace = ReownAppKitModalNetworks.getNamespaceForChainId( + chainMetadata.chainId, + ); + switch (namespace) { + case 'eip155': return EIP155.callMethod( appKit: _appKit, topic: _session.topic, @@ -242,7 +248,7 @@ class SessionWidgetState extends State { chainData: chainMetadata, address: address, ); - case ChainType.polkadot: + case 'polkadot': return Polkadot.callMethod( appKit: _appKit, topic: _session.topic, @@ -250,7 +256,7 @@ class SessionWidgetState extends State { chainData: chainMetadata, address: address, ); - case ChainType.solana: + case 'solana': return Solana.callMethod( appKit: _appKit, topic: _session.topic, @@ -371,10 +377,12 @@ class SessionWidgetState extends State { return buttons; } - List _buildChainEventsTiles(ChainMetadata chainMetadata) { + List _buildChainEventsTiles(ReownAppKitModalNetworkInfo chainData) { + final namespace = ReownAppKitModalNetworks.getNamespaceForChainId( + chainData.chainId, + ); final List values = []; - - for (final String event in getChainEvents(chainMetadata.type)) { + for (final String event in getChainEvents(namespace)) { values.add( Container( width: double.infinity, @@ -384,7 +392,7 @@ class SessionWidgetState extends State { ), decoration: BoxDecoration( border: Border.all( - color: chainMetadata.color, + color: Colors.blue, ), borderRadius: const BorderRadius.all( Radius.circular( diff --git a/packages/reown_appkit/example/modal/android/app/src/main/AndroidManifest.xml b/packages/reown_appkit/example/modal/android/app/src/main/AndroidManifest.xml index 09087a0..dade05d 100644 --- a/packages/reown_appkit/example/modal/android/app/src/main/AndroidManifest.xml +++ b/packages/reown_appkit/example/modal/android/app/src/main/AndroidManifest.xml @@ -111,4 +111,4 @@ android:name="flutterEmbedding" android:value="2" /> - + \ No newline at end of file diff --git a/packages/reown_appkit/example/modal/ios/Runner/AppDelegate.swift b/packages/reown_appkit/example/modal/ios/Runner/AppDelegate.swift index 242bf15..7c561a2 100644 --- a/packages/reown_appkit/example/modal/ios/Runner/AppDelegate.swift +++ b/packages/reown_appkit/example/modal/ios/Runner/AppDelegate.swift @@ -89,6 +89,32 @@ import CoinbaseWalletSDK } } +class LinkStreamHandler: NSObject, FlutterStreamHandler { + var eventSink: FlutterEventSink? + var queuedLinks = [String]() + + func onListen(withArguments arguments: Any?, eventSink events: @escaping FlutterEventSink) -> FlutterError? { + self.eventSink = events + queuedLinks.forEach({ events($0) }) + queuedLinks.removeAll() + return nil + } + + func onCancel(withArguments arguments: Any?) -> FlutterError? { + self.eventSink = nil + return nil + } + + func handleLink(_ link: String) -> Bool { + guard let eventSink = eventSink else { + queuedLinks.append(link) + return false + } + eventSink(link) + return true + } +} + class LinkStreamHandler: NSObject, FlutterStreamHandler { var eventSink: FlutterEventSink? var queuedLinks = [String]() diff --git a/packages/reown_appkit/example/modal/lib/home_page.dart b/packages/reown_appkit/example/modal/lib/home_page.dart index a2a4490..fc7f89e 100644 --- a/packages/reown_appkit/example/modal/lib/home_page.dart +++ b/packages/reown_appkit/example/modal/lib/home_page.dart @@ -6,6 +6,7 @@ import 'package:flutter/material.dart'; import 'package:reown_appkit_example/services/deep_link_handler.dart'; import 'package:shared_preferences/shared_preferences.dart'; +import 'package:reown_appkit/modal/services/blockchain_service/blockchain_service_singleton.dart'; import 'package:reown_appkit/reown_appkit.dart'; import 'package:reown_appkit_example/widgets/debug_drawer.dart'; @@ -168,8 +169,11 @@ class _MyHomePageState extends State { } catch (error) { debugPrint('[SIWEConfig] getSession error: $error'); // Fallback patch for testing purposes in case SIWE backend has issues - final address = _appKitModal.session!.address!; final chainId = _appKitModal.session!.chainId; + final namespace = ReownAppKitModalNetworks.getNamespaceForChainId( + chainId, + ); + final address = _appKitModal.session!.getAddress(namespace)!; return SIWESession(address: address, chains: [chainId]); } }, @@ -207,8 +211,28 @@ class _MyHomePageState extends State { final siweAuthValue = prefs.getBool('appkit_siwe_auth') ?? true; // See https://docs.reown.com/appkit/flutter/core/custom-chains - final testNetworks = ReownAppKitModalNetworks.test['eip155'] ?? []; - ReownAppKitModalNetworks.addNetworks('eip155', testNetworks); + // final extraChains = ReownAppKitModalNetworks.extra['eip155']!; + // ReownAppKitModalNetworks.addSupportedNetworks('eip155', extraChains); + // ReownAppKitModalNetworks.removeSupportedNetworks('eip155'); + // ReownAppKitModalNetworks.removeTestNetworks(); + ReownAppKitModalNetworks.addSupportedNetworks('polkadot', [ + ReownAppKitModalNetworkInfo( + name: 'Polkadot', + chainId: '91b171bb158e2d3848fa23a9f1c25182', + chainIcon: 'https://cryptologos.cc/logos/polkadot-new-dot-logo.png', + currency: 'DOT', + rpcUrl: 'https://rpc.polkadot.io', + explorerUrl: 'https://polkadot.subscan.io', + ), + ReownAppKitModalNetworkInfo( + name: 'Westend', + chainId: 'e143f23803ac50e8f6f8e62695d1ce9e', + currency: 'DOT', + rpcUrl: 'https://westend-rpc.polkadot.io', + explorerUrl: 'https://westend.subscan.io', + isTestNetwork: true, + ), + ]); try { _appKitModal = ReownAppKitModal( @@ -239,10 +263,50 @@ class _MyHomePageState extends State { 'c03dfee351b6fcc421b4494ea33b9d4b92a984f87aa76d1663bb28705e95034a', // Uniswap '38f5d18bd8522c244bdd70cb4a68e0e718865155811c043f052fb9f1c51de662', // Bitget }, - // excludedWalletIds: { - // 'fd20dc426fb37566d803205b19bbc1d4096b248ac04548e3cfb6b3a38bd033aa', // Coinbase - // }, + // excludedWalletIds: {}, // MORE WALLETS https://explorer.walletconnect.com/?type=wallet&chains=eip155%3A1 + getBalance: () async { + try { + final namespace = ReownAppKitModalNetworks.getNamespaceForChainId( + _appKitModal.selectedChain!.chainId, + ); + return await blockchainService.instance.getBalance( + address: _appKitModal.session!.getAddress(namespace)!, + namespace: namespace, + chainId: _appKitModal.selectedChain!.chainId, + ); + } catch (e) { + debugPrint('[$runtimeType] getBalance $e'); + return 0.0; + } + }, + optionalNamespaces: { + 'eip155': RequiredNamespace.fromJson({ + 'chains': ReownAppKitModalNetworks.getAllSupportedNetworks( + namespace: 'eip155', + ).map((chain) => 'eip155:${chain.chainId}').toList(), + 'methods': NetworkUtils.defaultNetworkMethods['eip155']!.toList(), + 'events': NetworkUtils.defaultNetworkEvents['eip155']!.toList(), + }), + 'solana': RequiredNamespace.fromJson({ + 'chains': ReownAppKitModalNetworks.getAllSupportedNetworks( + namespace: 'solana', + ).map((chain) => 'solana:${chain.chainId}').toList(), + 'methods': NetworkUtils.defaultNetworkMethods['solana']!.toList(), + 'events': [], + }), + 'polkadot': RequiredNamespace.fromJson({ + 'chains': [ + 'polkadot:91b171bb158e2d3848fa23a9f1c25182', + 'polkadot:e143f23803ac50e8f6f8e62695d1ce9e' + ], + 'methods': [ + 'polkadot_signMessage', + 'polkadot_signTransaction', + ], + 'events': [] + }), + }, ); setState(() => _initialized = true); } on ReownAppKitModalException catch (e) { diff --git a/packages/reown_appkit/example/modal/lib/services/eip155_service.dart b/packages/reown_appkit/example/modal/lib/services/methods_service.dart similarity index 76% rename from packages/reown_appkit/example/modal/lib/services/eip155_service.dart rename to packages/reown_appkit/example/modal/lib/services/methods_service.dart index bcc1a3a..eecce3c 100644 --- a/packages/reown_appkit/example/modal/lib/services/eip155_service.dart +++ b/packages/reown_appkit/example/modal/lib/services/methods_service.dart @@ -9,7 +9,7 @@ import 'package:convert/convert.dart'; import 'package:reown_appkit_example/services/contracts/aave_contract.dart'; import 'package:reown_appkit_example/services/contracts/test_data.dart'; -enum EIP155UIMethods { +enum SupportedMethods { personalSign, ethSendTransaction, requestAccounts, @@ -17,7 +17,9 @@ enum EIP155UIMethods { ethSignTypedDataV3, ethSignTypedDataV4, ethSignTransaction, - walletWatchAsset; + walletWatchAsset, + solanaSignMessage, + solanaSignTransaction; String get name { switch (this) { @@ -37,69 +39,73 @@ enum EIP155UIMethods { return 'eth_signTransaction'; case walletWatchAsset: return 'wallet_watchAsset'; + case solanaSignMessage: + return 'solana_signMessage'; + case solanaSignTransaction: + return 'solana_signTransaction'; } } } -class EIP155 { - static EIP155UIMethods methodFromName(String name) { +class MethodsService { + static SupportedMethods methodFromName(String name) { switch (name) { case 'personal_sign': - return EIP155UIMethods.personalSign; + return SupportedMethods.personalSign; case 'eth_signTypedData_v4': - return EIP155UIMethods.ethSignTypedDataV4; + return SupportedMethods.ethSignTypedDataV4; case 'eth_sendTransaction': - return EIP155UIMethods.ethSendTransaction; + return SupportedMethods.ethSendTransaction; case 'eth_requestAccounts': - return EIP155UIMethods.requestAccounts; + return SupportedMethods.requestAccounts; case 'eth_signTypedData_v3': - return EIP155UIMethods.ethSignTypedDataV3; + return SupportedMethods.ethSignTypedDataV3; case 'eth_signTypedData': - return EIP155UIMethods.ethSignTypedData; + return SupportedMethods.ethSignTypedData; case 'eth_signTransaction': - return EIP155UIMethods.ethSignTransaction; + return SupportedMethods.ethSignTransaction; case 'wallet_watchAsset': - return EIP155UIMethods.walletWatchAsset; + return SupportedMethods.walletWatchAsset; default: - throw Exception('Unrecognized method'); + throw Exception('Method not implemented'); } } static Future callMethod({ required ReownAppKitModal appKitModal, required String topic, - required EIP155UIMethods method, + required SupportedMethods method, required String chainId, required String address, }) { final cid = int.parse(chainId); switch (method) { - case EIP155UIMethods.requestAccounts: + case SupportedMethods.requestAccounts: return requestAccounts( appKitModal: appKitModal, ); - case EIP155UIMethods.personalSign: + case SupportedMethods.personalSign: return personalSign( appKitModal: appKitModal, message: testSignData, ); - case EIP155UIMethods.ethSignTypedDataV3: + case SupportedMethods.ethSignTypedDataV3: return ethSignTypedDataV3( appKitModal: appKitModal, data: jsonEncode(typeDataV3(cid)), ); - case EIP155UIMethods.ethSignTypedData: + case SupportedMethods.ethSignTypedData: return ethSignTypedData( appKitModal: appKitModal, data: jsonEncode(typedData()), ); - case EIP155UIMethods.ethSignTypedDataV4: + case SupportedMethods.ethSignTypedDataV4: return ethSignTypedDataV4( appKitModal: appKitModal, data: jsonEncode(typeDataV4(cid)), ); - case EIP155UIMethods.ethSignTransaction: - case EIP155UIMethods.ethSendTransaction: + case SupportedMethods.ethSignTransaction: + case SupportedMethods.ethSendTransaction: return ethSendOrSignTransaction( appKitModal: appKitModal, method: method, @@ -112,10 +118,14 @@ class EIP155 { data: utf8.encode('0x'), // to make it work with some wallets ), ); - case EIP155UIMethods.walletWatchAsset: + case SupportedMethods.walletWatchAsset: return walletWatchAsset( appKitModal: appKitModal, ); + case SupportedMethods.solanaSignMessage: + throw Exception('Method not implemented'); + case SupportedMethods.solanaSignTransaction: + throw Exception('Method not implemented'); } } @@ -126,7 +136,7 @@ class EIP155 { topic: appKitModal.session!.topic, chainId: appKitModal.selectedChain!.chainId, request: SessionRequestParams( - method: EIP155UIMethods.requestAccounts.name, + method: SupportedMethods.requestAccounts.name, params: [], ), ); @@ -138,15 +148,18 @@ class EIP155 { }) async { final bytes = utf8.encode(message); final encoded = hex.encode(bytes); + final namespace = ReownAppKitModalNetworks.getNamespaceForChainId( + appKit.selectedChain!.chainId, + ); return await appKitModal.request( topic: appKitModal.session!.topic, chainId: appKitModal.selectedChain!.chainId, request: SessionRequestParams( - method: EIP155UIMethods.personalSign.name, + method: SupportedMethods.personalSign.name, params: [ '0x$encoded', - appKitModal.session!.address!, + appKit.session!.getAddress(namespace)!, ], ), ); @@ -156,14 +169,17 @@ class EIP155 { required ReownAppKitModal appKitModal, required String data, }) async { - return await appKitModal.request( - topic: appKitModal.session!.topic, - chainId: appKitModal.selectedChain!.chainId, + final namespace = ReownAppKitModalNetworks.getNamespaceForChainId( + appKit.selectedChain!.chainId, + ); + return await appKit.request( + topic: appKit.session!.topic, + chainId: appKit.selectedChain!.chainId, request: SessionRequestParams( - method: EIP155UIMethods.ethSignTypedData.name, + method: SupportedMethods.ethSignTypedData.name, params: [ data, - appKitModal.session!.address!, + appKit.session!.getAddress(namespace)!, ], ), ); @@ -173,14 +189,17 @@ class EIP155 { required ReownAppKitModal appKitModal, required String data, }) async { - return await appKitModal.request( - topic: appKitModal.session!.topic, - chainId: appKitModal.selectedChain!.chainId, + final namespace = ReownAppKitModalNetworks.getNamespaceForChainId( + appKit.selectedChain!.chainId, + ); + return await appKit.request( + topic: appKit.session!.topic, + chainId: appKit.selectedChain!.chainId, request: SessionRequestParams( - method: EIP155UIMethods.ethSignTypedDataV3.name, + method: SupportedMethods.ethSignTypedDataV3.name, params: [ data, - appKitModal.session!.address!, + appKit.session!.getAddress(namespace)!, ], ), ); @@ -190,14 +209,17 @@ class EIP155 { required ReownAppKitModal appKitModal, required String data, }) async { - return await appKitModal.request( - topic: appKitModal.session!.topic, - chainId: appKitModal.selectedChain!.chainId, + final namespace = ReownAppKitModalNetworks.getNamespaceForChainId( + appKit.selectedChain!.chainId, + ); + return await appKit.request( + topic: appKit.session!.topic, + chainId: appKit.selectedChain!.chainId, request: SessionRequestParams( - method: EIP155UIMethods.ethSignTypedDataV4.name, + method: SupportedMethods.ethSignTypedDataV4.name, params: [ data, - appKitModal.session!.address!, + appKit.session!.getAddress(namespace)!, ], ), ); @@ -206,7 +228,7 @@ class EIP155 { static Future ethSendOrSignTransaction({ required ReownAppKitModal appKitModal, required Transaction transaction, - required EIP155UIMethods method, + required SupportedMethods method, }) async { return await appKitModal.request( topic: appKitModal.session!.topic, @@ -227,7 +249,7 @@ class EIP155 { topic: appKitModal.session!.topic, chainId: appKitModal.selectedChain!.chainId, request: SessionRequestParams( - method: EIP155UIMethods.walletWatchAsset.name, + method: SupportedMethods.walletWatchAsset.name, params: { 'type': 'ERC20', 'options': { @@ -285,13 +307,18 @@ class EIP155 { final d = (decimals.first as BigInt); final requestValue = _formatValue(0.01, decimals: d); // now we call `transfer` write function with the parsed value. - return appKitModal.requestWriteContract( - topic: appKitModal.session!.topic, - chainId: appKitModal.selectedChain!.chainId, + final namespace = ReownAppKitModalNetworks.getNamespaceForChainId( + appKit.selectedChain!.chainId, + ); + return appKit.requestWriteContract( + topic: appKit.session!.topic, + chainId: appKit.selectedChain!.chainId, deployedContract: deployedContract, functionName: 'transfer', transaction: Transaction( - from: EthereumAddress.fromHex(appKitModal.session!.address!), + from: EthereumAddress.fromHex( + appKit.session!.getAddress(namespace)!, + ), ), parameters: [ EthereumAddress.fromHex( @@ -357,13 +384,18 @@ class EIP155 { final d = (decimals.first as BigInt); final requestValue = _formatValue(0.23, decimals: d); // now we call `transfer` write function with the parsed value. - return appKitModal.requestWriteContract( - topic: appKitModal.session!.topic, - chainId: appKitModal.selectedChain!.chainId, + final namespace = ReownAppKitModalNetworks.getNamespaceForChainId( + appKit.selectedChain!.chainId, + ); + return appKit.requestWriteContract( + topic: appKit.session!.topic, + chainId: appKit.selectedChain!.chainId, deployedContract: deployedContract, functionName: 'transfer', transaction: Transaction( - from: EthereumAddress.fromHex(appKitModal.session!.address!), + from: EthereumAddress.fromHex( + appKit.session!.getAddress(namespace)!, + ), ), parameters: [ EthereumAddress.fromHex( @@ -381,6 +413,9 @@ class EIP155 { required ReownAppKitModal appKitModal, required DeployedContract contract, }) async { + final namespace = ReownAppKitModalNetworks.getNamespaceForChainId( + appKit.selectedChain!.chainId, + ); final results = await Future.wait([ // results[0] appKitModal.requestReadContract( @@ -403,7 +438,7 @@ class EIP155 { deployedContract: contract, functionName: 'balanceOf', parameters: [ - EthereumAddress.fromHex(appKitModal.session!.address!), + EthereumAddress.fromHex(appKit.session!.getAddress(namespace)!), ], ), // results[4] diff --git a/packages/reown_appkit/example/modal/lib/widgets/session_widget.dart b/packages/reown_appkit/example/modal/lib/widgets/session_widget.dart index 10728f2..ced8d01 100644 --- a/packages/reown_appkit/example/modal/lib/widgets/session_widget.dart +++ b/packages/reown_appkit/example/modal/lib/widgets/session_widget.dart @@ -3,13 +3,14 @@ import 'dart:convert'; import 'package:fl_toast/fl_toast.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; +import 'package:reown_appkit/modal/utils/core_utils.dart'; import 'package:reown_appkit_example/utils/styles.dart'; import 'package:reown_appkit/reown_appkit.dart'; import 'package:reown_appkit_example/utils/constants.dart'; -import 'package:reown_appkit_example/services/eip155_service.dart'; +import 'package:reown_appkit_example/services/methods_service.dart'; import 'package:reown_appkit_example/widgets/method_dialog.dart'; class SessionWidget extends StatefulWidget { @@ -38,7 +39,12 @@ class SessionWidgetState extends State { children: [ CircleAvatar( radius: 18.0, - backgroundImage: NetworkImage(iconImage), + backgroundImage: NetworkImage( + iconImage, + headers: CoreUtils.getAPIHeaders( + widget.appKit.appKit!.core.projectId, + ), + ), ), const SizedBox(width: 8.0), ], @@ -102,15 +108,17 @@ class SessionWidgetState extends State { ), ), Column( - children: _buildSupportedChainsWidget(), + children: _buildSupportedChainsWidget( + widget.appKit.selectedChain!.chainId, + ), ), const SizedBox(height: StyleConstants.linear8), ]; // Get current active account - final accounts = session.getAccounts() ?? []; final chainId = widget.appKit.selectedChain?.chainId ?? ''; final namespace = ReownAppKitModalNetworks.getNamespaceForChainId(chainId); + final accounts = session.getAccounts(namespace: namespace) ?? []; final chainsNamespaces = NamespaceUtils.getChainsFromAccounts(accounts); if (chainsNamespaces.contains('$namespace:$chainId')) { final account = accounts.firstWhere( @@ -165,6 +173,12 @@ class SessionWidgetState extends State { } Widget _buildAccountWidget(String account) { + final chainId = NamespaceUtils.getChainFromAccount(account); + // final chainMetadata = ChainDataWrapper.getChainMetadataFromChain( + // chainId.split(':').first, + // chainId.split(':').last, + // ); + final List children = [ Text( widget.appKit.selectedChain?.name ?? 'Unsupported chain', @@ -213,7 +227,7 @@ class SessionWidgetState extends State { ), ), ]); - children.add(_buildChainEventsTiles()); + children.add(_buildChainEventsTiles(chainId)); return Container( padding: const EdgeInsets.all(StyleConstants.linear8), @@ -232,12 +246,14 @@ class SessionWidgetState extends State { ); } - List _buildChainMethodButtons( - String address, - ) { + List _buildChainMethodButtons(String address) { // Add Methods - final approvedMethods = widget.appKit.getApprovedMethods() ?? []; - if (approvedMethods.isEmpty) { + final chainId = NamespaceUtils.getChainFromAccount(address); + final namespace = NamespaceUtils.getNamespaceFromChain(chainId); + final approvedMethods = widget.appKit.getApprovedMethods( + namespace: namespace, + ); + if ((approvedMethods ?? []).isEmpty) { return [ Text( 'No methods approved', @@ -250,10 +266,10 @@ class SessionWidgetState extends State { ) ]; } - final usableMethods = EIP155UIMethods.values.map((e) => e.name).toList(); + final usableMethods = SupportedMethods.values.map((e) => e.name).toList(); // final List children = []; - for (final method in approvedMethods) { + for (final method in (approvedMethods ?? [])) { final implemented = usableMethods.contains(method); children.add( Container( @@ -264,7 +280,8 @@ class SessionWidgetState extends State { onPressed: implemented ? () async { widget.appKit.launchConnectedWallet(); - final future = callChainMethod(EIP155.methodFromName( + final future = + callChainMethod(MethodsService.methodFromName( method, )); MethodDialog.show(context, method, future); @@ -277,6 +294,16 @@ class SessionWidgetState extends State { ); } + if (namespace == 'eip155') { + children.addAll(_addSmartContractButtons()); + } + + return children; + } + + List _addSmartContractButtons() { + final List children = []; + children.add(const Divider()); final onSepolia = widget.appKit.selectedChain?.chainId == '11155111'; if (!onSepolia) { @@ -304,8 +331,8 @@ class SessionWidgetState extends State { child: ElevatedButton( onPressed: onSepolia ? () async { - final future = EIP155.callTestSmartContract( - appKitModal: widget.appKit, + final future = MethodsService.callTestSmartContract( + appKit: widget.appKit, action: 'read', ); MethodDialog.show( @@ -316,8 +343,8 @@ class SessionWidgetState extends State { } : onMainnet ? () async { - final future = EIP155.callUSDTSmartContract( - appKitModal: widget.appKit, + final future = MethodsService.callUSDTSmartContract( + appKit: widget.appKit, action: 'read', ); MethodDialog.show( @@ -341,8 +368,8 @@ class SessionWidgetState extends State { onPressed: onSepolia ? () async { widget.appKit.launchConnectedWallet(); - final future = EIP155.callTestSmartContract( - appKitModal: widget.appKit, + final future = MethodsService.callTestSmartContract( + appKit: widget.appKit, action: 'write', ); MethodDialog.show(context, 'Test Contract (Write)', future); @@ -350,8 +377,8 @@ class SessionWidgetState extends State { : onMainnet ? () async { widget.appKit.launchConnectedWallet(); - final future = EIP155.callUSDTSmartContract( - appKitModal: widget.appKit, + final future = MethodsService.callUSDTSmartContract( + appKit: widget.appKit, action: 'write', ); MethodDialog.show( @@ -369,7 +396,7 @@ class SessionWidgetState extends State { return children; } - List _buildSupportedChainsWidget() { + List _buildSupportedChainsWidget(String chainId) { List children = []; children.addAll( [ @@ -385,10 +412,11 @@ class SessionWidgetState extends State { ), ], ); - final approvedChains = widget.appKit.getApprovedChains() ?? []; + final ns = ReownAppKitModalNetworks.getNamespaceForChainId(chainId); + final approvedChains = widget.appKit.getApprovedChains(namespace: ns); children.add( Text( - approvedChains.join(', '), + (approvedChains ?? []).join(', '), style: ReownAppKitModalTheme.getDataOf(context) .textStyles .small400 @@ -400,10 +428,13 @@ class SessionWidgetState extends State { return children; } - Widget _buildChainEventsTiles() { + Widget _buildChainEventsTiles(String chainId) { // Add Events - final approvedEvents = widget.appKit.getApprovedEvents() ?? []; - if (approvedEvents.isEmpty) { + final namespace = NamespaceUtils.getNamespaceFromChain(chainId); + final approvedEvents = widget.appKit.getApprovedEvents( + namespace: namespace, + ); + if ((approvedEvents ?? []).isEmpty) { return Text( 'No events approved', style: ReownAppKitModalTheme.getDataOf(context) @@ -415,7 +446,7 @@ class SessionWidgetState extends State { ); } final List children = []; - for (final event in approvedEvents) { + for (final event in (approvedEvents ?? [])) { children.add( Container( margin: const EdgeInsets.symmetric( @@ -447,14 +478,17 @@ class SessionWidgetState extends State { ); } - Future callChainMethod(EIP155UIMethods method) { + Future callChainMethod(SupportedMethods method) { final session = widget.appKit.session!; - return EIP155.callMethod( - appKitModal: widget.appKit, + final chainId = widget.appKit.selectedChain!.chainId; + final namespace = ReownAppKitModalNetworks.getNamespaceForChainId(chainId); + final address = session.getAddress(namespace)!; + return MethodsService.callMethod( + appKit: widget.appKit, topic: session.topic ?? '', method: method, chainId: widget.appKit.selectedChain!.chainId, - address: session.address!, + address: address, ); } } diff --git a/packages/reown_appkit/lib/appkit_modal.dart b/packages/reown_appkit/lib/appkit_modal.dart index b9f37fb..a58eeb6 100644 --- a/packages/reown_appkit/lib/appkit_modal.dart +++ b/packages/reown_appkit/lib/appkit_modal.dart @@ -11,8 +11,8 @@ export 'modal/models/public/appkit_modal_models.dart'; // Theme export 'modal/theme/public/appkit_modal_theme.dart'; -// Utils -export 'modal/utils/public/appkit_modal_utils.dart'; +/// Utils +export 'modal/utils/public/appkit_modal_networks_utils.dart'; // Widgets export 'modal/widgets/public/appkit_modal_widgets.dart'; diff --git a/packages/reown_appkit/lib/modal/appkit_modal_impl.dart b/packages/reown_appkit/lib/modal/appkit_modal_impl.dart index 8669975..28ca7a2 100644 --- a/packages/reown_appkit/lib/modal/appkit_modal_impl.dart +++ b/packages/reown_appkit/lib/modal/appkit_modal_impl.dart @@ -2,7 +2,6 @@ import 'dart:async'; import 'dart:convert'; import 'dart:math'; -import 'package:collection/collection.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:get_it/get_it.dart'; @@ -10,6 +9,7 @@ import 'package:reown_appkit/modal/services/magic_service/i_magic_service.dart'; import 'package:reown_core/store/i_store.dart'; import 'package:reown_appkit/reown_appkit.dart'; +import 'package:reown_appkit/modal/services/network_service/network_service.dart'; import 'package:reown_appkit/modal/services/uri_service/launch_url_exception.dart'; import 'package:reown_appkit/modal/services/uri_service/url_utils.dart'; import 'package:reown_appkit/modal/services/uri_service/url_utils_singleton.dart'; @@ -64,8 +64,11 @@ class ReownAppKitModal with ChangeNotifier implements IReownAppKitModal { @override ReownAppKitModalNetworkInfo? get selectedChain { if (_currentSelectedChainId != null) { + final namespace = ReownAppKitModalNetworks.getNamespaceForChainId( + _currentSelectedChainId!, + ); return ReownAppKitModalNetworks.getNetworkById( - CoreConstants.namespace, + namespace, _currentSelectedChainId!, ); } @@ -129,6 +132,8 @@ class ReownAppKitModal with ChangeNotifier implements IReownAppKitModal { late final FeaturesConfig featuresConfig; /// + late final Future Function()? _getBalance; + ReownAppKitModal({ required BuildContext context, IReownAppKit? appKit, @@ -142,6 +147,9 @@ class ReownAppKitModal with ChangeNotifier implements IReownAppKitModal { Set? featuredWalletIds, Set? includedWalletIds, Set? excludedWalletIds, + bool? enableAnalytics, + bool enableEmail = false, + Future Function()? getBalance, LogLevel logLevel = LogLevel.nothing, }) { if (appKit == null) { @@ -160,10 +168,14 @@ class ReownAppKitModal with ChangeNotifier implements IReownAppKitModal { this.featuresConfig = featuresConfig ?? FeaturesConfig(email: false); _context = context; + _getBalance = getBalance; _appKit = appKit ?? ReownAppKit( - core: ReownCore(projectId: projectId!), + core: ReownCore( + projectId: projectId!, + logLevel: logLevel, + ), metadata: metadata!, ); _projectId = _appKit.core.projectId; @@ -188,8 +200,14 @@ class ReownAppKitModal with ChangeNotifier implements IReownAppKitModal { featuredWalletIds: featuredWalletIds, includedWalletIds: includedWalletIds, excludedWalletIds: excludedWalletIds, + namespaces: { + ..._requiredNamespaces, + ..._optionalNamespaces, + }, ); + networkService.instance = NetworkService(); + blockchainService.instance = BlockChainService( core: _appKit.core, ); @@ -211,6 +229,10 @@ class ReownAppKitModal with ChangeNotifier implements IReownAppKitModal { siweService.instance = SiweService( appKit: _appKit, siweConfig: siweConfig, + namespaces: { + ..._requiredNamespaces, + ..._optionalNamespaces, + }, ); } @@ -237,18 +259,25 @@ class ReownAppKitModal with ChangeNotifier implements IReownAppKitModal { Completer _awaitRelayOnce = Completer(); + @override + Future dispatchEnvelope(String url) async { + final envelope = ReownCoreUtils.getSearchParamFromURL(url, 'wc_ev'); + if (envelope.isNotEmpty) { + await _appKit.dispatchEnvelope(url); + return true; + } + + return false; + } + @override Future init() async { _relayConnected = false; _awaitRelayOnce = Completer(); if (!CoreUtils.isValidProjectID(_projectId)) { - _appKit.core.logger.e( - '[$runtimeType] projectId $_projectId is invalid. ' - 'Please provide a valid projectId. ' - 'See ${UrlConstants.docsUrl}/appkit/flutter/core/options for details.', - ); - return; + throw 'Please provide a valid projectId ($_projectId).' + 'See ${UrlConstants.docsUrl}/appkit/flutter/core/usage for details.'; } if (_status == ReownAppKitModalStatus.initializing || _status == ReownAppKitModalStatus.initialized) { @@ -285,13 +314,17 @@ class ReownAppKitModal with ChangeNotifier implements IReownAppKitModal { final wcSessions = _appKit.sessions.getAll(); // Loop through all the chain data - final allNetworks = ReownAppKitModalNetworks.getNetworks( - CoreConstants.namespace, - ); + final allNetworks = ReownAppKitModalNetworks.getAllSupportedNetworks(); for (final chain in allNetworks) { - for (final event in EventsConstants.allEvents) { + final namespace = ReownAppKitModalNetworks.getNamespaceForChainId( + chain.chainId, + ); + final requiredEvents = _requiredNamespaces[namespace]?.events ?? []; + final optionalEvents = _optionalNamespaces[namespace]?.events ?? []; + final events = [...requiredEvents, ...optionalEvents]; + for (final event in events) { _appKit.registerEventHandler( - chainId: '${CoreConstants.namespace}:${chain.chainId}', + chainId: '$namespace:${chain.chainId}', event: event, ); } @@ -299,11 +332,9 @@ class ReownAppKitModal with ChangeNotifier implements IReownAppKitModal { // There's a session stored if (wcSessions.isNotEmpty) { - await _storeSession( - ReownAppKitModalSession( - sessionData: wcSessions.first, - ), - ); + await _storeSession(ReownAppKitModalSession( + sessionData: wcSessions.first, + )); // session should not outlive the pairing if (wcPairings.isEmpty) { await disconnect(); @@ -370,10 +401,10 @@ class ReownAppKitModal with ChangeNotifier implements IReownAppKitModal { } } - Future _setSesionAndChainData(ReownAppKitModalSession sData) async { + Future _setSesionAndChainData(ReownAppKitModalSession mSession) async { try { - await _storeSession(sData); - _currentSelectedChainId = _currentSelectedChainId ?? sData.chainId; + await _storeSession(mSession); + _currentSelectedChainId = _currentSelectedChainId ?? mSession.chainId; await _setLocalEthChain(_currentSelectedChainId!, logEvent: false); } catch (e, s) { _appKit.core.logger.d( @@ -421,8 +452,11 @@ class ReownAppKitModal with ChangeNotifier implements IReownAppKitModal { if (_currentSession != null) { final chainId = _getStoredChainId(null); if (chainId != null) { + final namespace = ReownAppKitModalNetworks.getNamespaceForChainId( + chainId, + ); final chain = ReownAppKitModalNetworks.getNetworkById( - CoreConstants.namespace, + namespace, chainId, ); if (chain != null) { @@ -462,10 +496,16 @@ class ReownAppKitModal with ChangeNotifier implements IReownAppKitModal { } else { final hasValidSession = _isConnected && _currentSession != null; if (switchChain && hasValidSession && _currentSelectedChainId != null) { - final approvedChains = _currentSession!.getApprovedChains() ?? []; - final hasChainAlready = approvedChains.contains( - '${CoreConstants.namespace}:${chainInfo.chainId}', + final namespace = ReownAppKitModalNetworks.getNamespaceForChainId( + chainInfo.chainId, + ); + final approvedChains = _currentSession!.getApprovedChains( + namespace: namespace, ); + final caip2chain = ReownAppKitModalNetworks.getCaip2Chain( + chainInfo.chainId, + ); + final hasChainAlready = (approvedChains ?? []).contains(caip2chain); if (!hasChainAlready) { requestSwitchToChain(chainInfo); final hasSwitchMethod = _currentSession!.hasSwitchMethod(); @@ -485,41 +525,51 @@ class ReownAppKitModal with ChangeNotifier implements IReownAppKitModal { @override List? getAvailableChains() { // if there's no session or if supportsAddChain method then every chain can be used - if (_currentSession == null || _currentSession!.hasSwitchMethod()) { + final evmHasSwitch = _currentSession?.hasSwitchMethod() ?? false; + if (_currentSession == null || evmHasSwitch) { return null; } - return getApprovedChains(); + final namespace = ReownAppKitModalNetworks.getNamespaceForChainId( + _currentSelectedChainId!, + ); + return getApprovedChains(namespace: namespace); } /// Will get the list of already approved chains by the wallet (to switch to) @override - List? getApprovedChains() { + List? getApprovedChains({required String? namespace}) { if (_currentSession == null) { return null; } - return _currentSession!.getApprovedChains(); + return _currentSession!.getApprovedChains( + namespace: null, + ); } @override - List? getApprovedMethods() { + List? getApprovedMethods({required String? namespace}) { if (_currentSession == null) { return null; } - return _currentSession!.getApprovedMethods(); + return _currentSession!.getApprovedMethods( + namespace: namespace, + ); } @override - List? getApprovedEvents() { + List? getApprovedEvents({required String? namespace}) { if (_currentSession == null) { return null; } - return _currentSession!.getApprovedEvents(); + return _currentSession!.getApprovedEvents( + namespace: namespace, + ); } Future _setLocalEthChain(String chainId, {bool? logEvent}) async { _currentSelectedChainId = chainId; - final caip2Chain = '${CoreConstants.namespace}:$_currentSelectedChainId'; - _currentSelectedChainId = chainId; + final caip2Chain = ReownAppKitModalNetworks.getCaip2Chain(chainId); + _logger.i('[$runtimeType] set local chain $caip2Chain'); _notify(); try { if (isConnected) { @@ -809,14 +859,13 @@ class ReownAppKitModal with ChangeNotifier implements IReownAppKitModal { ); final nonce = await siweService.instance!.getNonce(); final p1 = await siweService.instance!.config!.getMessageParams(); - final methods = p1.methods ?? MethodsConstants.allMethods; + final methods = + p1.methods ?? NetworkUtils.defaultNetworkMethods['eip155']; // - final supportedNetworks = ReownAppKitModalNetworks.getNetworks( - CoreConstants.namespace, - ); - final chains = supportedNetworks - .map((e) => '${CoreConstants.namespace}:${e.chainId}') - .toList(); + final networks = ReownAppKitModalNetworks.getAllSupportedNetworks(); + final chains = networks.map((chain) { + return ReownAppKitModalNetworks.getCaip2Chain(chain.chainId); + }).toList(); final p2 = {'nonce': nonce, 'chains': chains, 'methods': methods}; final authParams = SessionAuthRequestParams.fromJson({ ...p1.toJson(), @@ -1079,7 +1128,10 @@ class ReownAppKitModal with ChangeNotifier implements IReownAppKitModal { void launchBlockExplorer() async { if ((selectedChain?.explorerUrl ?? '').isNotEmpty) { final blockExplorer = selectedChain!.explorerUrl; - final address = _currentSession?.address ?? ''; + final namespace = ReownAppKitModalNetworks.getNamespaceForChainId( + selectedChain!.chainId, + ); + final address = _currentSession?.getAddress(namespace) ?? ''; final explorerUrl = '$blockExplorer/address/$address'; await ReownCoreUtils.openURL(explorerUrl); } @@ -1101,7 +1153,7 @@ class ReownAppKitModal with ChangeNotifier implements IReownAppKitModal { final isValidChainId = NamespaceUtils.isValidChainId(chainId); if (!isValidChainId) { if (selectedChain!.chainId == chainId) { - reqChainId = '${CoreConstants.namespace}:$chainId'; + reqChainId = ReownAppKitModalNetworks.getCaip2Chain(chainId); } else { throw Errors.getSdkError( Errors.UNSUPPORTED_CHAINS, @@ -1153,7 +1205,7 @@ class ReownAppKitModal with ChangeNotifier implements IReownAppKitModal { final isValidChainId = NamespaceUtils.isValidChainId(chainId); if (!isValidChainId) { if (selectedChain!.chainId == chainId) { - reqChainId = '${CoreConstants.namespace}:$chainId'; + reqChainId = ReownAppKitModalNetworks.getCaip2Chain(chainId); } else { throw Errors.getSdkError( Errors.UNSUPPORTED_CHAINS, @@ -1194,7 +1246,7 @@ class ReownAppKitModal with ChangeNotifier implements IReownAppKitModal { final isValidChainId = NamespaceUtils.isValidChainId(chainId); if (!isValidChainId) { if (selectedChain!.chainId == chainId) { - reqChainId = '${CoreConstants.namespace}:$chainId'; + reqChainId = ReownAppKitModalNetworks.getCaip2Chain(chainId); } else { throw Errors.getSdkError( Errors.UNSUPPORTED_CHAINS, @@ -1316,13 +1368,8 @@ class ReownAppKitModal with ChangeNotifier implements IReownAppKitModal { // Set the required namespaces to everything in our chain presets _requiredNamespaces = {}; } - - final wrongNamespace = _requiredNamespaces.keys.firstWhereOrNull( - (k) => k != CoreConstants.namespace, - ); - if (wrongNamespace != null) { - throw ReownAppKitModalException('Only eip155 blockains are supported'); - } + dev.log( + '[$runtimeType] required namespaces ${jsonEncode(_requiredNamespaces)}'); } void _setOptionalNamespaces(Map? optionalNSpaces) { @@ -1340,25 +1387,21 @@ class ReownAppKitModal with ChangeNotifier implements IReownAppKitModal { ); } else { // Set the optional namespaces to everything in our chain presets - final namespaces = ReownAppKitModalNetworks.supported.keys; + final namespaces = ReownAppKitModalNetworks.getAllSupportedNamespaces(); for (var ns in namespaces) { - final chains = ReownAppKitModalNetworks.supported[ns] ?? []; + final networks = ReownAppKitModalNetworks.getAllSupportedNetworks( + namespace: ns, + ); _optionalNamespaces[ns] = RequiredNamespace( - chains: chains.map((e) => '$ns:${e.chainId}').toList(), - methods: MethodsConstants.allMethods.toSet().toList(), - events: EventsConstants.allEvents.toSet().toList(), + chains: networks.map((e) => '$ns:${e.chainId}').toList(), + methods: NetworkUtils.defaultNetworkMethods[ns] ?? [], + events: NetworkUtils.defaultNetworkEvents[ns] ?? [], ); } } - final wrongNamespace = _optionalNamespaces.keys.firstWhereOrNull( - (k) => k != CoreConstants.namespace, - ); - if (wrongNamespace != null) { - throw ReownAppKitModalException( - 'Only ${CoreConstants.namespace} networks are supported', - ); - } + dev.log( + '[$runtimeType] optional namespaces ${jsonEncode(_optionalNamespaces)}'); } /// Loads account balance and avatar. @@ -1366,42 +1409,38 @@ class ReownAppKitModal with ChangeNotifier implements IReownAppKitModal { @override Future loadAccountData() async { // If there is no selected chain or session, stop. No account to load in. - if (_currentSelectedChainId == null || - _currentSession == null || - _currentSession?.address == null) { + if (_currentSelectedChainId == null || _currentSession == null) { return; } // Get the chain balance. + final namespace = ReownAppKitModalNetworks.getNamespaceForChainId( + _currentSelectedChainId!, + ); + try { - _chainBalance = await blockchainService.instance.rpcRequest( - chainId: '${CoreConstants.namespace}:$_currentSelectedChainId', - request: SessionRequestParams( - method: 'eth_getBalance', - params: [_currentSession!.address!, 'latest'], - ), - ); + _chainBalance = await (_getBalance?.call() ?? + blockchainService.instance.getBalance( + address: _currentSession!.getAddress(namespace)!, + namespace: namespace, + chainId: _currentSelectedChainId!, + )); final tokenName = selectedChain?.currency ?? ''; - balanceNotifier.value = - '${CoreUtils.formatChainBalance(_chainBalance)} $tokenName'; - } catch (e, s) { - _appKit.core.logger.d( - '[$runtimeType] loadAccountData error: $e', - stackTrace: s, - ); + balanceNotifier.value = '$_chainBalance $tokenName'; + } catch (e) { + _logger.e('[$runtimeType] getBalance $e'); } - // Get the avatar, each chainId is just a number in string form. - try { - final blockchainId = await blockchainService.instance.getIdentity( - _currentSession!.address!, - ); - _avatarUrl = blockchainId.avatar; - } catch (e, s) { - _appKit.core.logger.d( - '[$runtimeType] loadAccountData error: $e', - stackTrace: s, - ); + if (namespace == NetworkUtils.eip155) { + // Get the avatar, each chainId is just a number in string form. + try { + final blockchainId = await blockchainService.instance.getIdentity( + _currentSession!.getAddress(namespace)!, + ); + _avatarUrl = blockchainId.avatar; + } catch (e) { + _logger.e('[$runtimeType] getIdentity $e'); + } } _notify(); } @@ -1413,10 +1452,11 @@ class ReownAppKitModal with ChangeNotifier implements IReownAppKitModal { await selectChain(newChain); return; } - final currentChain = '${CoreConstants.namespace}:$_currentSelectedChainId'; - final newChainId = '${CoreConstants.namespace}:${newChain.chainId}'; - _appKit.core.logger - .i('[$runtimeType] requesting switch to chain $newChainId'); + final currentChain = ReownAppKitModalNetworks.getCaip2Chain( + _currentSelectedChainId!, + ); + final newChainId = ReownAppKitModalNetworks.getCaip2Chain(newChain.chainId); + _logger.i('[$runtimeType] requesting switch to chain $newChainId'); try { await request( topic: _currentSession?.topic ?? '', @@ -1456,13 +1496,15 @@ class ReownAppKitModal with ChangeNotifier implements IReownAppKitModal { @override Future requestAddChain(ReownAppKitModalNetworkInfo newChain) async { final topic = _currentSession?.topic ?? ''; - final chainId = '${CoreConstants.namespace}:$_currentSelectedChainId'; - final newChainId = '${CoreConstants.namespace}:${newChain.chainId}'; - _appKit.core.logger.i('[$runtimeType] requesting add chain $newChainId'); + final currentChain = ReownAppKitModalNetworks.getCaip2Chain( + _currentSelectedChainId!, + ); + final newChainId = ReownAppKitModalNetworks.getCaip2Chain(newChain.chainId); + _logger.i('[$runtimeType] requesting switch to add chain $newChainId'); try { await request( topic: topic, - chainId: chainId, + chainId: currentChain, switchToChainId: newChainId, request: SessionRequestParams( method: MethodsConstants.walletAddEthChain, @@ -1777,18 +1819,15 @@ extension _CoinbaseConnectorExtension on ReownAppKitModal { void _onCoinbaseSessionUpdateEvent(CoinbaseSessionEvent? args) async { if (args != null) { try { - final address = args.address ?? _currentSession!.address!; final chainId = args.chainId ?? _currentSession!.chainId; + final ns = ReownAppKitModalNetworks.getNamespaceForChainId(chainId); + final address = args.address ?? _currentSession!.getAddress(ns)!; + final chainInfo = ReownAppKitModalNetworks.getNetworkById(ns, chainId); _currentSelectedChainId = chainId; - // - final chain = ReownAppKitModalNetworks.getNetworkById( - CoreConstants.namespace, - chainId, - ); final session = _currentSession!.copyWith( coinbaseData: CoinbaseData( address: address, - chainName: chain?.name ?? '', + chainName: chainInfo?.name ?? '', chainId: int.parse(chainId), peer: coinbaseService.instance.metadata, self: ConnectionMetadata( @@ -1818,6 +1857,8 @@ extension _CoinbaseConnectorExtension on ReownAppKitModal { extension _AppKitModalExtension on ReownAppKitModal { void _onSessionAuthResponse(SessionAuthResponse? args) async { + final debugString = jsonEncode(args?.toJson()); + _logger.d('[$runtimeType] _onSessionAuthResponse: $debugString'); if (args?.session != null) { // IF 1-CA SUPPORTED WE SHOULD CALL SIWECONGIF METHODS HERE final session = await _settleSession(args!.session!); @@ -1855,6 +1896,8 @@ extension _AppKitModalExtension on ReownAppKitModal { } void _onSessionConnect(SessionConnect? args) async { + final debugString = jsonEncode(args?.session.toJson()); + _logger.d('[$runtimeType] _onSessionConnect: $debugString'); final siweEnabled = siweService.instance!.enabled; if (_supportsOneClickAuth && siweEnabled) return; if (args != null) { @@ -1876,17 +1919,15 @@ extension _AppKitModalExtension on ReownAppKitModal { } // HAS TO BE CALLED JUST ONCE ON CONNECTION - Future _settleSession( - SessionData sessionData, - ) async { + Future _settleSession(SessionData mSession) async { if (_currentSelectedChainId == null) { final chains = NamespaceUtils.getChainIdsFromNamespaces( - namespaces: sessionData.namespaces, - )..sort((a, b) => a.compareTo(b)); + namespaces: mSession.namespaces, + ); final chainId = chains.first.split(':').last.toString(); _currentSelectedChainId = chainId; } - final session = ReownAppKitModalSession(sessionData: sessionData); + final session = ReownAppKitModalSession(sessionData: mSession); await _setSesionAndChainData(session); if (_selectedWallet == null) { analyticsService.instance.sendEvent(ConnectSuccessEvent( diff --git a/packages/reown_appkit/lib/modal/constants/string_constants.dart b/packages/reown_appkit/lib/modal/constants/string_constants.dart index aee69eb..1d4e286 100644 --- a/packages/reown_appkit/lib/modal/constants/string_constants.dart +++ b/packages/reown_appkit/lib/modal/constants/string_constants.dart @@ -8,7 +8,6 @@ class CoreConstants { static const X_SDK_TYPE = 'appkit'; static const X_SDK_VERSION = packageVersion; static const X_CORE_SDK_VERSION = 'flutter_${reown_sign.packageVersion}'; - static const String namespace = 'eip155'; } class UIConstants { diff --git a/packages/reown_appkit/lib/modal/i_appkit_modal_impl.dart b/packages/reown_appkit/lib/modal/i_appkit_modal_impl.dart index 63e1602..6e081bf 100644 --- a/packages/reown_appkit/lib/modal/i_appkit_modal_impl.dart +++ b/packages/reown_appkit/lib/modal/i_appkit_modal_impl.dart @@ -103,14 +103,17 @@ abstract class IReownAppKitModal with ChangeNotifier { /// List of available chains to be added in connected wallet List? getAvailableChains(); + // TODO NON-EVM Support: make namespace non nullable /// List of approved chains by connected wallet - List? getApprovedChains(); + List? getApprovedChains({required String? namespace}); + // TODO NON-EVM Support: make namespace non nullable /// List of approved methods by connected wallet - List? getApprovedMethods(); + List? getApprovedMethods({required String? namespace}); + // TODO NON-EVM Support: make namespace non nullable /// List of approved events by connected wallet - List? getApprovedEvents(); + List? getApprovedEvents({required String? namespace}); /// Loads/Refresh account balance and identity Future loadAccountData(); diff --git a/packages/reown_appkit/lib/modal/models/public/appkit_modal_session.dart b/packages/reown_appkit/lib/modal/models/public/appkit_modal_session.dart index 1cf2336..c455532 100644 --- a/packages/reown_appkit/lib/modal/models/public/appkit_modal_session.dart +++ b/packages/reown_appkit/lib/modal/models/public/appkit_modal_session.dart @@ -97,15 +97,17 @@ class ReownAppKitModalSession { return true; } - final nsMethods = getApprovedMethods() ?? []; + final nsMethods = getApprovedMethods(namespace: 'eip155') ?? []; final supportsAddChain = nsMethods.contains( MethodsConstants.walletAddEthChain, ); return supportsAddChain; } - /// Get the approved methods by the connected peer - List? getApprovedMethods() { + // TODO NON-EVM Support: make namespace non nullable + List? getApprovedMethods({required String? namespace}) { + final methodsList = []; + if (sessionService.noSession) { return null; } @@ -117,31 +119,45 @@ class ReownAppKitModalSession { } final sessionNamespaces = _sessionData!.namespaces; - final namespace = sessionNamespaces[CoreConstants.namespace]; - final methodsList = namespace?.methods.toSet().toList(); - return methodsList ?? []; + if ((namespace ?? '').isEmpty) { + for (var namespace in sessionNamespaces.keys) { + final events = sessionNamespaces[namespace]?.methods ?? []; + methodsList.addAll(events); + } + + return methodsList; + } + + return sessionNamespaces[namespace]?.methods ?? []; } - /// Get the approved events by the connected peer - List? getApprovedEvents() { + List? getApprovedEvents({required String? namespace}) { + final eventsList = []; + if (sessionService.noSession) { return null; } if (sessionService.isCoinbase) { - return []; + return eventsList; } if (sessionService.isMagic) { - return []; + return eventsList; } final sessionNamespaces = _sessionData!.namespaces; - final namespace = sessionNamespaces[CoreConstants.namespace]; - final eventsList = namespace?.events.toSet().toList(); - return eventsList ?? []; + if ((namespace ?? '').isEmpty) { + for (var namespace in sessionNamespaces.keys) { + final events = sessionNamespaces[namespace]?.events ?? []; + eventsList.addAll(events); + } + + return eventsList; + } + + return sessionNamespaces[namespace]?.events ?? []; } - /// Get the approved chains by the connected peer - List? getApprovedChains() { + List? getApprovedChains({required String? namespace}) { if (sessionService.noSession) { return null; } @@ -150,25 +166,34 @@ class ReownAppKitModalSession { return [chainId]; } - final accounts = getAccounts() ?? []; - final approvedChains = NamespaceUtils.getChainsFromAccounts(accounts); - return approvedChains; + final accounts = getAccounts(namespace: namespace) ?? []; + return NamespaceUtils.getChainsFromAccounts(accounts); } - /// Get the approved accounts by the connected peer - List? getAccounts() { + List? getAccounts({required String? namespace}) { + final accountList = []; + if (sessionService.noSession) { return null; } if (sessionService.isCoinbase) { - return ['${CoreConstants.namespace}:$chainId:$address']; + return ['$namespace:$chainId:${getAddress(namespace ?? 'eip155')}']; } if (sessionService.isMagic) { - return ['${CoreConstants.namespace}:$chainId:$address']; + return ['$namespace:$chainId:${getAddress(namespace ?? 'eip155')}']; } final sessionNamespaces = _sessionData!.namespaces; - return sessionNamespaces[CoreConstants.namespace]?.accounts ?? []; + if ((namespace ?? '').isEmpty) { + for (var namespace in sessionNamespaces.keys) { + final accounts = sessionNamespaces[namespace]?.accounts ?? []; + accountList.addAll(accounts); + } + + return accountList; + } + + return sessionNamespaces[namespace]?.accounts ?? []; } Redirect? getSessionRedirect() { @@ -257,7 +282,7 @@ extension ReownAppKitModalSessionExtension on ReownAppKitModalSession { AppKitSocialOption? get socialProvider => _magicData?.provider; // - String? get address { + String? getAddress(String namespace) { if (sessionService.noSession) { return null; } @@ -267,8 +292,8 @@ extension ReownAppKitModalSessionExtension on ReownAppKitModalSession { if (sessionService.isMagic) { return _magicData!.address; } - final namespace = namespaces?[CoreConstants.namespace]; - final accounts = namespace?.accounts ?? []; + final ns = namespaces?[namespace]; + final accounts = ns?.accounts ?? []; if (accounts.isNotEmpty) { return NamespaceUtils.getAccount(accounts.first); } @@ -312,22 +337,27 @@ extension ReownAppKitModalSessionExtension on ReownAppKitModalSession { } Map? _namespaces() { + final eip155 = NetworkUtils.eip155; + // TODO check if namespaces are needed when connected to Coinbase or Magic if (sessionService.isCoinbase) { + // Coinbase only supports eip155 chains return { - CoreConstants.namespace: Namespace( - chains: ['${CoreConstants.namespace}:$chainId'], - accounts: ['${CoreConstants.namespace}:$chainId:$address'], + eip155: Namespace( + chains: ['$eip155:$chainId'], + accounts: ['$eip155:$chainId:${getAddress(eip155)}'], methods: [...CoinbaseService.supportedMethods], + // Coinbase does not have events as it doesn't use WC protocol events: [], ), }; } if (sessionService.isMagic) { return { - CoreConstants.namespace: Namespace( - chains: ['${CoreConstants.namespace}:$chainId'], - accounts: ['${CoreConstants.namespace}:$chainId:$address'], - methods: [...GetIt.I().supportedMethods], + eip155: Namespace( + chains: ['$eip155:$chainId'], + accounts: ['$eip155:$chainId:${getAddress(eip155)}'], + methods: [...MagicService.supportedMethods], + // Magic does not have events as it doesn't use WC protocol events: [], ), }; diff --git a/packages/reown_appkit/lib/modal/pages/approve_siwe.dart b/packages/reown_appkit/lib/modal/pages/approve_siwe.dart index 8ad66ba..b0837ed 100644 --- a/packages/reown_appkit/lib/modal/pages/approve_siwe.dart +++ b/packages/reown_appkit/lib/modal/pages/approve_siwe.dart @@ -5,7 +5,6 @@ import 'package:flutter/material.dart'; import 'package:flutter/widgets.dart'; import 'package:reown_appkit/modal/constants/key_constants.dart'; -import 'package:reown_appkit/modal/constants/string_constants.dart'; import 'package:reown_appkit/modal/services/analytics_service/analytics_service_singleton.dart'; import 'package:reown_appkit/modal/services/analytics_service/models/analytics_event.dart'; import 'package:reown_appkit/modal/services/siwe_service/siwe_service_singleton.dart'; @@ -68,12 +67,15 @@ class _ApproveSIWEPageState extends State { void _signIn() async { setState(() => _waitingSign = true); try { - final address = _appKitModal!.session!.address!; String chainId = _appKitModal!.selectedChain?.chainId ?? '1'; + final namespace = ReownAppKitModalNetworks.getNamespaceForChainId( + chainId, + ); + final address = _appKitModal!.session!.getAddress(namespace)!; analyticsService.instance.sendEvent(ClickSignSiweMessage( network: chainId, )); - chainId = '${CoreConstants.namespace}:$chainId'; + chainId = ReownAppKitModalNetworks.getCaip2Chain(chainId); // final message = await siweService.instance!.createMessage( chainId: chainId, diff --git a/packages/reown_appkit/lib/modal/pages/connet_network_page.dart b/packages/reown_appkit/lib/modal/pages/connet_network_page.dart index aaad1bc..08bb8b7 100644 --- a/packages/reown_appkit/lib/modal/pages/connet_network_page.dart +++ b/packages/reown_appkit/lib/modal/pages/connet_network_page.dart @@ -3,7 +3,6 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:reown_appkit/modal/constants/key_constants.dart'; -import 'package:reown_appkit/modal/constants/string_constants.dart'; import 'package:reown_appkit/modal/services/explorer_service/explorer_service_singleton.dart'; import 'package:reown_appkit/modal/services/siwe_service/siwe_service_singleton.dart'; import 'package:reown_appkit/modal/i_appkit_modal_impl.dart'; @@ -51,8 +50,11 @@ class _ConnectNetworkPageState extends State try { await _service!.requestSwitchToChain(widget.chainInfo); final chainId = widget.chainInfo.chainId; + final namespace = ReownAppKitModalNetworks.getNamespaceForChainId( + chainId, + ); final chainInfo = ReownAppKitModalNetworks.getNetworkById( - CoreConstants.namespace, + namespace, chainId, ); if (chainInfo != null) { diff --git a/packages/reown_appkit/lib/modal/pages/public/appkit_modal_select_network_page.dart b/packages/reown_appkit/lib/modal/pages/public/appkit_modal_select_network_page.dart index e3c0dd8..8670851 100644 --- a/packages/reown_appkit/lib/modal/pages/public/appkit_modal_select_network_page.dart +++ b/packages/reown_appkit/lib/modal/pages/public/appkit_modal_select_network_page.dart @@ -2,7 +2,6 @@ import 'dart:math'; import 'package:flutter/material.dart'; import 'package:reown_appkit/modal/constants/key_constants.dart'; -import 'package:reown_appkit/modal/constants/string_constants.dart'; import 'package:reown_appkit/modal/pages/about_networks.dart'; import 'package:reown_appkit/modal/pages/connet_network_page.dart'; import 'package:reown_appkit/modal/services/analytics_service/models/analytics_event.dart'; @@ -30,9 +29,15 @@ class ReownAppKitModalSelectNetworkPage extends StatelessWidget { ) async { final service = ModalProvider.of(context).instance; if (service.isConnected) { - final approvedChains = service.session!.getApprovedChains() ?? []; - final caip2Chain = '${CoreConstants.namespace}:${chainInfo.chainId}'; - final isChainApproved = approvedChains.contains(caip2Chain); + final chainId = chainInfo.chainId; + final caip2Chain = ReownAppKitModalNetworks.getCaip2Chain(chainId); + final namespace = ReownAppKitModalNetworks.getNamespaceForChainId( + chainId, + ); + final approvedChains = service.session!.getApprovedChains( + namespace: namespace, + ); + final isChainApproved = (approvedChains ?? []).contains(caip2Chain); if (chainInfo.chainId == service.selectedChain?.chainId) { if (widgetStack.instance.canPop()) { widgetStack.instance.pop(); diff --git a/packages/reown_appkit/lib/modal/services/blockchain_service/blockchain_service.dart b/packages/reown_appkit/lib/modal/services/blockchain_service/blockchain_service.dart index f1193c0..f75a974 100644 --- a/packages/reown_appkit/lib/modal/services/blockchain_service/blockchain_service.dart +++ b/packages/reown_appkit/lib/modal/services/blockchain_service/blockchain_service.dart @@ -35,76 +35,65 @@ class BlockChainService implements IBlockChainService { try { final uri = Uri.parse('$_baseUrl/identity/$address'); final queryParams = {..._requiredParams}; - // if (queryParams['clientId'] == null) { - // queryParams['clientId'] = await _core.crypto.getClientId(); - // } final response = await http.get( uri.replace(queryParameters: queryParams), headers: _requiredHeaders, ); - if (response.statusCode == 200) { + if (response.statusCode == 200 && response.body.isNotEmpty) { return BlockchainIdentity.fromJson(jsonDecode(response.body)); + } + if (response.statusCode == 400) { + final errorData = jsonDecode(response.body) as Map; + final reasons = errorData['reasons'] as List; + final reason = reasons.isNotEmpty + ? reasons.first['description'] ?? '' + : response.body; + throw Exception(reason); } else { throw Exception('Failed to load avatar'); } } catch (e) { - _core.logger.e('[$runtimeType] getIdentity: $e'); rethrow; } } - int _retries = 1; @override - Future rpcRequest({ - // required String? topic, + Future getBalance({ + required String address, + required String namespace, required String chainId, - required SessionRequestParams request, }) async { - final bool isChainId = NamespaceUtils.isValidChainId(chainId); - if (!isChainId) { - throw Errors.getSdkError( - Errors.UNSUPPORTED_CHAINS, - context: '[$runtimeType] chain should be CAIP-2 valid', - ); - } final uri = Uri.parse(_baseUrl); - final queryParams = {..._requiredParams, 'chainId': chainId}; + final queryParams = {..._requiredParams, 'chainId': '$namespace:$chainId'}; final response = await http.post( uri.replace(queryParameters: queryParams), - headers: { - ..._requiredHeaders, - 'Content-Type': 'application/json', - }, + headers: {..._requiredHeaders, 'Content-Type': 'application/json'}, body: jsonEncode({ 'id': 1, 'jsonrpc': '2.0', - 'method': request.method, - 'params': request.params, + 'method': _balanceMetod(namespace), + 'params': [ + address, + if (namespace == NetworkUtils.eip155) 'latest', + ], }), ); if (response.statusCode == 200 && response.body.isNotEmpty) { - _retries = 1; try { - final result = _parseRpcResultAs(response.body); - final amount = EtherAmount.fromBigInt(EtherUnit.wei, hexToInt(result)); - return amount.getValueInUnit(EtherUnit.ether); + return _parseBalanceResult(namespace, response.body); } catch (e) { - _core.logger.e( - '[$runtimeType] Failed to get parse ${request.toJson()}. ' - 'Response: ${response.body}, Status code: ${response.statusCode}', - ); - rethrow; + throw Exception('Failed to load balance. $e'); } + } + if (response.statusCode == 400) { + final errorData = jsonDecode(response.body) as Map; + final reasons = errorData['reasons'] as List; + final reason = reasons.isNotEmpty + ? reasons.first['description'] ?? '' + : response.body; + throw Exception(reason); } else { - if (response.body.isEmpty && _retries > 0) { - _core.logger.i('[$runtimeType] Empty body'); - _retries -= 1; - await rpcRequest(chainId: chainId, request: request); - } else { - _core.logger.e( - '[$runtimeType] Failed to get request ${request.toJson()}. Response: ${response.body}, Status code: ${response.statusCode}', - ); - } + throw Exception('Failed to load balance'); } } @@ -129,49 +118,97 @@ class BlockChainService implements IBlockChainService { } } + String _balanceMetod(String namespace) { + if (namespace == NetworkUtils.eip155) { + return 'eth_getBalance'; + } else if (namespace == NetworkUtils.solana) { + return 'getBalance'; + } + return ''; + } + + double _parseBalanceResult(String namespace, String balanceResult) { + if (namespace == NetworkUtils.solana) { + final result = _parseRpcResultAs>(balanceResult); + final value = result['value'] as int; + return value / 1000000000.0; + } else if (namespace == NetworkUtils.eip155) { + final result = _parseRpcResultAs(balanceResult); + final amount = EtherAmount.fromBigInt( + EtherUnit.wei, + hexToInt(result), + ); + return amount.getValueInUnit(EtherUnit.ether); + } + return 0.0; + } + + // int _retries = 1; // @override - // Future getBalance( - // String address, - // String currency, { - // String? chain, - // String? forceUpdate, + // Future rpcRequest({ + // // required String? topic, + // required String chainId, + // required SessionRequestParams request, // }) async { - // final uri = Uri.parse('$_baseUrl/account/$address/balance'); - // final queryParams = { - // ..._requiredParams, - // 'currency': currency, - // if (chain != null) 'chainId': chain, - // if (forceUpdate != null) 'forceUpdate': forceUpdate, - // }; - // final response = await http.get( + // final bool isChainId = NamespaceUtils.isValidChainId(chainId); + // if (!isChainId) { + // throw Errors.getSdkError( + // Errors.UNSUPPORTED_CHAINS, + // context: '[$runtimeType] chain should be CAIP-2 valid', + // ); + // } + // final namespace = NamespaceUtils.getNamespaceFromChain(chainId); + // String method = request.method; + // // TODO NON-EVM Support: handle this better + // if (namespace == NetworkUtils.eip155) { + // method = 'eth_$method'; + // } + // final uri = Uri.parse(_baseUrl); + // final queryParams = {..._requiredParams, 'chainId': chainId}; + // final response = await http.post( // uri.replace(queryParameters: queryParams), // headers: { // ..._requiredHeaders, - // // 'chain': chainId, - // // 'forceUpdate': string - // // 'Content-Type': 'application/json', + // 'Content-Type': 'application/json', // }, - // // body: jsonEncode({ - // // 'jsonrpc': '2.0', - // // 'method': 'eth_getBalance', - // // 'params': [address, 'latest'], - // // 'chainId': 1 - // // }), + // body: jsonEncode({ + // 'id': 1, + // 'jsonrpc': '2.0', + // 'method': method, + // 'params': request.params, + // }), // ); - // _core.logger.i('[$runtimeType] getBalance $address: ${response.body}'); - // if (response.statusCode == 200) { + // if (response.statusCode == 200 && response.body.isNotEmpty) { + // _retries = 1; + // try { + // // TODO NON-EVM Support: handle this better + // if (namespace == NetworkUtils.solana) { + // final result = _parseRpcResultAs>(response.body); + // final value = result['value'] as int; + // return value / 1000000000.0; + // } + // if (namespace == NetworkUtils.eip155) { + // final result = _parseRpcResultAs(response.body); + // final amount = EtherAmount.fromBigInt( + // EtherUnit.wei, + // hexToInt(result), + // ); + // return amount.getValueInUnit(EtherUnit.ether); + // } + // } catch (e) { + // rethrow; + // } // } else { - // throw Exception('Failed to load balance'); + // if (response.body.isEmpty && _retries > 0) { + // _core.logger.i('[$runtimeType] Empty body'); + // _retries -= 1; + // await rpcRequest(chainId: chainId, request: request); + // } else { + // _core.logger.i( + // '[$runtimeType] Failed to get request ${request.toJson()}. ' + // 'Response: ${response.body}, Status code: ${response.statusCode}', + // ); + // } // } // } - - // @override - // Future fetchEnsName(String rpcUrl, String address) async { - // return ''; - // } - - // @override - // Future fetchEnsAvatar(String rpcUrl, String address) async { - // return ''; - // } } diff --git a/packages/reown_appkit/lib/modal/services/blockchain_service/i_blockchain_service.dart b/packages/reown_appkit/lib/modal/services/blockchain_service/i_blockchain_service.dart index cdc0e8a..91c4db6 100644 --- a/packages/reown_appkit/lib/modal/services/blockchain_service/i_blockchain_service.dart +++ b/packages/reown_appkit/lib/modal/services/blockchain_service/i_blockchain_service.dart @@ -1,5 +1,5 @@ import 'package:reown_appkit/modal/services/blockchain_service/models/blockchain_identity.dart'; -import 'package:reown_appkit/reown_appkit.dart'; +// import 'package:reown_appkit/reown_appkit.dart'; abstract class IBlockChainService { Future init(); @@ -7,9 +7,14 @@ abstract class IBlockChainService { /// Gets the name and avatar of a provided address on the given chain Future getIdentity(String address); - Future rpcRequest({ - // required String? topic, + Future getBalance({ + required String address, + required String namespace, required String chainId, - required SessionRequestParams request, }); + + // Future rpcRequest({ + // required String chainId, + // required SessionRequestParams request, + // }); } diff --git a/packages/reown_appkit/lib/modal/services/coinbase_service/coinbase_service.dart b/packages/reown_appkit/lib/modal/services/coinbase_service/coinbase_service.dart index 0db48ce..d67d7d8 100644 --- a/packages/reown_appkit/lib/modal/services/coinbase_service/coinbase_service.dart +++ b/packages/reown_appkit/lib/modal/services/coinbase_service/coinbase_service.dart @@ -1,7 +1,6 @@ import 'dart:convert'; import 'package:flutter/services.dart'; -import 'package:reown_appkit/modal/constants/string_constants.dart'; import 'package:reown_appkit/modal/services/coinbase_service/i_coinbase_service.dart'; import 'package:reown_appkit/modal/services/coinbase_service/models/coinbase_data.dart'; @@ -315,9 +314,12 @@ extension on SessionRequestParams { case MethodsConstants.walletSwitchEthChain: case MethodsConstants.walletAddEthChain: try { - final chainInfo = ReownAppKitModalNetworks.getNetworkById( - CoreConstants.namespace, + final namespace = ReownAppKitModalNetworks.getNamespaceForChainId( chainId!, + ); + final chainInfo = ReownAppKitModalNetworks.getNetworkById( + namespace, + chainId, )!; return AddEthereumChain( chainId: chainInfo.chainId, diff --git a/packages/reown_appkit/lib/modal/services/explorer_service/explorer_service.dart b/packages/reown_appkit/lib/modal/services/explorer_service/explorer_service.dart index 79a13f5..34800c6 100644 --- a/packages/reown_appkit/lib/modal/services/explorer_service/explorer_service.dart +++ b/packages/reown_appkit/lib/modal/services/explorer_service/explorer_service.dart @@ -87,6 +87,8 @@ class ExplorerService implements IExplorerService { bool get canPaginate => _canPaginate; late final String _bundleId; + late final Set _chains; + late final Map namespaces; ExplorerService({ required IReownCore core, @@ -94,6 +96,7 @@ class ExplorerService implements IExplorerService { this.featuredWalletIds, this.includedWalletIds, this.excludedWalletIds, + this.namespaces = const {}, }) : _core = core, _referer = referer, _client = http.Client(); @@ -105,6 +108,10 @@ class ExplorerService implements IExplorerService { } _bundleId = await ReownCoreUtils.getPackageName(); + _chains = NamespaceUtils.getChainIdsFromRequiredNamespaces( + requiredNamespaces: namespaces, + ).map((chainId) => NamespaceUtils.getNamespaceFromChain(chainId)).toSet(); + // TODO ideally we should call this at every opening to be able to detect newly installed wallets. final nativeData = await _fetchNativeAppData(); final installed = await nativeData.getInstalledApps(); @@ -277,14 +284,14 @@ class ExplorerService implements IExplorerService { RequestParams? params, bool updateCount = true, }) async { - final queryParams = params?.toJson() ?? {}; + params = params?.copyWith(chains: _chains.join(',')); final headers = CoreUtils.getAPIHeaders( _core.projectId, _referer, _bundleId, ); final uri = Uri.parse('${UrlConstants.apiService}/getWallets').replace( - queryParameters: queryParams, + queryParameters: params?.toJson(), ); try { final response = await _client.get(uri, headers: headers); diff --git a/packages/reown_appkit/lib/modal/services/explorer_service/models/request_params.dart b/packages/reown_appkit/lib/modal/services/explorer_service/models/request_params.dart index a495855..cd48d98 100644 --- a/packages/reown_appkit/lib/modal/services/explorer_service/models/request_params.dart +++ b/packages/reown_appkit/lib/modal/services/explorer_service/models/request_params.dart @@ -5,6 +5,7 @@ class RequestParams { final String? include; // eg. id1,id2,id3 final String? exclude; // eg. id1,id2,id3 final String? platform; // 'ios' | 'android' + final String? chains; // eip155,solana const RequestParams({ required this.page, @@ -13,6 +14,7 @@ class RequestParams { this.include, this.exclude, this.platform, + this.chains, }); Map toJson({bool short = false}) { @@ -32,6 +34,9 @@ class RequestParams { if ((platform ?? '').isNotEmpty) { params['platform'] = platform; } + if ((chains ?? '').isNotEmpty) { + params['chains'] = chains; + } return params; } @@ -43,6 +48,7 @@ class RequestParams { String? include, String? exclude, String? platform, + String? chains, }) => RequestParams( page: page ?? this.page, @@ -51,6 +57,7 @@ class RequestParams { include: include ?? this.include, exclude: exclude ?? this.exclude, platform: platform ?? this.platform, + chains: chains ?? this.chains, ); RequestParams nextPage() => RequestParams( @@ -60,5 +67,6 @@ class RequestParams { include: include, exclude: exclude, platform: platform, + chains: chains, ); } diff --git a/packages/reown_appkit/lib/modal/services/magic_service/models/frame_message.dart b/packages/reown_appkit/lib/modal/services/magic_service/models/frame_message.dart index 35d7d4e..e2f9035 100644 --- a/packages/reown_appkit/lib/modal/services/magic_service/models/frame_message.dart +++ b/packages/reown_appkit/lib/modal/services/magic_service/models/frame_message.dart @@ -343,11 +343,11 @@ class SyncAppData extends MessageData { @override String toString() { final v = 'verified: true'; - final p1 = 'projectId:"$projectId"'; - final p2 = 'sdkVersion:"$sdkVersion"'; - final m1 = 'name:"${metadata.name}"'; - final m2 = 'description:"${metadata.description}"'; - final m3 = 'url:"${metadata.url}"'; + final p1 = 'projectId:\'$projectId\''; + final p2 = 'sdkVersion:\'${sdkVersion.replaceAll("'", "\\'")}\''; + final m1 = 'name:\'${metadata.name.replaceAll("'", "\\'")}\''; + final m2 = 'description:\'${metadata.description.replaceAll("'", "\\'")}\''; + final m3 = 'url:\'${metadata.url}\''; final m4 = 'icons:["${metadata.icons.first}"]'; final r1 = 'native:"${metadata.redirect?.native}"'; final r2 = 'universal:"${metadata.redirect?.universal}"'; diff --git a/packages/reown_appkit/lib/modal/services/network_service/network_service.dart b/packages/reown_appkit/lib/modal/services/network_service/network_service.dart index 3a51c51..7794e95 100644 --- a/packages/reown_appkit/lib/modal/services/network_service/network_service.dart +++ b/packages/reown_appkit/lib/modal/services/network_service/network_service.dart @@ -1,5 +1,4 @@ import 'package:flutter/material.dart'; -import 'package:reown_appkit/modal/constants/string_constants.dart'; import 'package:reown_appkit/modal/models/grid_item.dart'; import 'package:reown_appkit/modal/services/explorer_service/explorer_service_singleton.dart'; @@ -18,11 +17,15 @@ class NetworkService implements INetworkService { ValueNotifier>>([]); String _getImageUrl(ReownAppKitModalNetworkInfo chainInfo) { + if (chainInfo.isTestNetwork) { + return ''; + } if (chainInfo.chainIcon != null && chainInfo.chainIcon!.contains('http')) { return chainInfo.chainIcon!; } - final imageId = - ReownAppKitModalNetworks.getNetworkIconId(chainInfo.chainId); + final imageId = ReownAppKitModalNetworks.getNetworkIconId( + chainInfo.chainId, + ); return explorerService.instance.getAssetImageUrl(imageId); } @@ -32,17 +35,15 @@ class NetworkService implements INetworkService { return; } - final networks = ReownAppKitModalNetworks.getNetworks( - CoreConstants.namespace, - ); - for (var chain in networks) { - final imageUrl = _getImageUrl(chain); + final networks = ReownAppKitModalNetworks.getAllSupportedNetworks(); + for (var network in networks) { + final imageUrl = _getImageUrl(network); itemListComplete.add( GridItem( image: imageUrl, - id: chain.chainId, - title: RenderUtils.shorten(chain.name), - data: chain, + id: network.chainId, + title: RenderUtils.shorten(network.name), + data: network, ), ); } diff --git a/packages/reown_appkit/lib/modal/services/network_service/network_service_singleton.dart b/packages/reown_appkit/lib/modal/services/network_service/network_service_singleton.dart index 7bf9bf8..ecdfa30 100644 --- a/packages/reown_appkit/lib/modal/services/network_service/network_service_singleton.dart +++ b/packages/reown_appkit/lib/modal/services/network_service/network_service_singleton.dart @@ -1,10 +1,7 @@ import 'package:reown_appkit/modal/services/network_service/i_network_service.dart'; -import 'package:reown_appkit/modal/services/network_service/network_service.dart'; class NetworkServiceSingleton { - INetworkService instance; - - NetworkServiceSingleton() : instance = NetworkService(); + late INetworkService instance; } final networkService = NetworkServiceSingleton(); diff --git a/packages/reown_appkit/lib/modal/services/siwe_service/siwe_service.dart b/packages/reown_appkit/lib/modal/services/siwe_service/siwe_service.dart index 402c23d..43007ea 100644 --- a/packages/reown_appkit/lib/modal/services/siwe_service/siwe_service.dart +++ b/packages/reown_appkit/lib/modal/services/siwe_service/siwe_service.dart @@ -1,5 +1,6 @@ import 'dart:convert'; +import 'package:collection/collection.dart'; import 'package:convert/convert.dart'; import 'package:get_it/get_it.dart'; import 'package:reown_appkit/modal/constants/string_constants.dart'; @@ -12,19 +13,28 @@ class SiweService implements ISiweService { IMagicService get _magicService => GetIt.I(); late final SIWEConfig? _siweConfig; - final IReownAppKit _appKit; + late final IReownAppKit _appKit; + late final Map _namespaces; SiweService({ required IReownAppKit appKit, required SIWEConfig? siweConfig, + required Map namespaces, }) : _appKit = appKit, - _siweConfig = siweConfig; + _siweConfig = siweConfig, + _namespaces = namespaces; @override SIWEConfig? get config => _siweConfig; @override - bool get enabled => _siweConfig?.enabled == true; + bool get enabled { + // TODO check this logic + final nonEVM = _namespaces.keys.firstWhereOrNull( + (k) => k != NetworkUtils.eip155, + ); + return _siweConfig?.enabled == true && nonEVM == null; + } @override int get nonceRefetchIntervalMs => @@ -81,11 +91,7 @@ class SiweService implements ISiweService { if (!enabled) throw Exception('siweConfig not enabled'); // final chainId = AuthSignature.getChainIdFromMessage(message); - final chainInfo = ReownAppKitModalNetworks.getNetworkById( - CoreConstants.namespace, - chainId, - )!; - final caip2Chain = '${CoreConstants.namespace}:${chainInfo.chainId}'; + final caip2Chain = ReownAppKitModalNetworks.getCaip2Chain(chainId); final address = AuthSignature.getAddressFromMessage(message); final bytes = utf8.encode(message); final encoded = hex.encode(bytes); diff --git a/packages/reown_appkit/lib/modal/utils/public/appkit_modal_default_networks.dart b/packages/reown_appkit/lib/modal/utils/public/appkit_modal_default_networks.dart index 14a13d0..8801cad 100644 --- a/packages/reown_appkit/lib/modal/utils/public/appkit_modal_default_networks.dart +++ b/packages/reown_appkit/lib/modal/utils/public/appkit_modal_default_networks.dart @@ -1,14 +1,15 @@ import 'package:collection/collection.dart'; import 'package:reown_appkit/modal/models/public/appkit_network_info.dart'; +import 'package:reown_appkit/modal/utils/public/appkit_modal_networks_utils.dart'; class ReownAppKitModalNetworks { // https://github.com/WalletConnect/blockchain-api/blob/master/SUPPORTED_CHAINS.md - static Map> supported = { - 'eip155': [ + static final Map> _mainnets = { + NetworkUtils.eip155: [ ReownAppKitModalNetworkInfo( name: 'Ethereum', chainId: '1', - chainIcon: chainImagesId['1'], + chainIcon: _networkIcons['1'], currency: 'ETH', rpcUrl: 'https://ethereum-rpc.publicnode.com', explorerUrl: 'https://etherscan.io', @@ -16,7 +17,7 @@ class ReownAppKitModalNetworks { ReownAppKitModalNetworkInfo( name: 'Optimism', chainId: '10', - chainIcon: chainImagesId['10'], + chainIcon: _networkIcons['10'], currency: 'ETH', rpcUrl: 'https://mainnet.optimism.io/', explorerUrl: 'https://optimistic.etherscan.io', @@ -24,7 +25,7 @@ class ReownAppKitModalNetworks { ReownAppKitModalNetworkInfo( name: 'Binance Smart Chain', chainId: '56', - chainIcon: chainImagesId['56'], + chainIcon: _networkIcons['56'], currency: 'BNB', rpcUrl: 'https://bsc-dataseed.binance.org/', explorerUrl: 'https://bscscan.com', @@ -32,7 +33,7 @@ class ReownAppKitModalNetworks { ReownAppKitModalNetworkInfo( name: 'Gnosis Chain', chainId: '100', - chainIcon: chainImagesId['100'], + chainIcon: _networkIcons['100'], currency: 'xDAI', rpcUrl: 'https://rpc.gnosischain.com', explorerUrl: 'https://gnosis.blockscout.com', @@ -40,7 +41,7 @@ class ReownAppKitModalNetworks { ReownAppKitModalNetworkInfo( name: 'Polygon', chainId: '137', - chainIcon: chainImagesId['137'], + chainIcon: _networkIcons['137'], currency: 'MATIC', rpcUrl: 'https://polygon.drpc.org', explorerUrl: 'https://polygonscan.com', @@ -48,7 +49,7 @@ class ReownAppKitModalNetworks { ReownAppKitModalNetworkInfo( name: 'zkSync Era', chainId: '324', - chainIcon: chainImagesId['324'], + chainIcon: _networkIcons['324'], currency: 'ETH', rpcUrl: 'https://mainnet.era.zksync.io', explorerUrl: 'https://explorer.zksync.io', @@ -56,7 +57,7 @@ class ReownAppKitModalNetworks { ReownAppKitModalNetworkInfo( name: 'Polygon zkEVM', chainId: '1101', - chainIcon: chainImagesId['1101'], + chainIcon: _networkIcons['1101'], currency: 'ETH', rpcUrl: 'https://rpc-mainnet.matic.network', explorerUrl: 'https://explorer-evm.polygon.technology', @@ -64,7 +65,7 @@ class ReownAppKitModalNetworks { ReownAppKitModalNetworkInfo( name: 'Mantle', chainId: '5000', - chainIcon: chainImagesId['5000'], + chainIcon: _networkIcons['5000'], currency: 'BIT', rpcUrl: 'https://rpc.mantlenetwork.io', explorerUrl: 'https://explorer.mantlenetwork.io', @@ -72,7 +73,7 @@ class ReownAppKitModalNetworks { ReownAppKitModalNetworkInfo( name: 'Klaytn Mainnet', chainId: '8217', - chainIcon: chainImagesId['8217'], + chainIcon: _networkIcons['8217'], currency: 'KLAY', rpcUrl: 'https://public-node-api.klaytnapi.com/v1/cypress', explorerUrl: 'https://scope.klaytn.com', @@ -80,7 +81,7 @@ class ReownAppKitModalNetworks { ReownAppKitModalNetworkInfo( name: 'Base', chainId: '8453', - chainIcon: chainImagesId['8453'], + chainIcon: _networkIcons['8453'], currency: 'ETH', rpcUrl: 'https://mainnet.base.org', explorerUrl: 'https://basescan.org', @@ -88,7 +89,7 @@ class ReownAppKitModalNetworks { ReownAppKitModalNetworkInfo( name: 'Arbitrum', chainId: '42161', - chainIcon: chainImagesId['42161'], + chainIcon: _networkIcons['42161'], currency: 'ETH', rpcUrl: 'https://arbitrum.blockpi.network/v1/rpc/public', explorerUrl: 'https://arbiscan.io/', @@ -96,7 +97,7 @@ class ReownAppKitModalNetworks { ReownAppKitModalNetworkInfo( name: 'Celo', chainId: '42220', - chainIcon: chainImagesId['42220'], + chainIcon: _networkIcons['42220'], currency: 'CELO', rpcUrl: 'https://forno.celo.org', explorerUrl: 'https://explorer.celo.org/mainnet', @@ -104,7 +105,7 @@ class ReownAppKitModalNetworks { ReownAppKitModalNetworkInfo( name: 'Avalanche', chainId: '43114', - chainIcon: chainImagesId['43114'], + chainIcon: _networkIcons['43114'], currency: 'AVAX', rpcUrl: 'https://api.avax.network/ext/bc/C/rpc', explorerUrl: 'https://snowtrace.io', @@ -112,7 +113,7 @@ class ReownAppKitModalNetworks { ReownAppKitModalNetworkInfo( name: 'Linea', chainId: '59144', - chainIcon: chainImagesId['59144'], + chainIcon: _networkIcons['59144'], currency: 'ETH', rpcUrl: 'https://rpc.linea.build', explorerUrl: 'https://explorer.linea.build', @@ -120,7 +121,7 @@ class ReownAppKitModalNetworks { ReownAppKitModalNetworkInfo( name: 'Zora', chainId: '7777777', - chainIcon: chainImagesId['7777777'], + chainIcon: _networkIcons['7777777'], currency: 'ETH', rpcUrl: 'https://rpc.zora.energy', explorerUrl: 'https://explorer.zora.energy', @@ -128,53 +129,26 @@ class ReownAppKitModalNetworks { ReownAppKitModalNetworkInfo( name: 'Aurora', chainId: '1313161554', - chainIcon: chainImagesId['1313161554'], + chainIcon: _networkIcons['1313161554'], currency: 'ETH', rpcUrl: 'https://mainnet.aurora.dev', explorerUrl: 'https://explorer.aurora.dev', ), ], - }; - - static Map> extra = { - 'eip155': [ - ReownAppKitModalNetworkInfo( - name: 'Fantom', - chainId: '250', - chainIcon: chainImagesId['250'], - currency: 'FTM', - rpcUrl: 'https://rpc.ftm.tools/', - explorerUrl: 'https://ftmscan.com', - ), + NetworkUtils.solana: [ ReownAppKitModalNetworkInfo( - name: 'EVMos', - chainId: '9001', - chainIcon: chainImagesId['9001'], - currency: 'EVMOS', - rpcUrl: 'https://evmos-evm.publicnode.com', - explorerUrl: '', + name: 'Solana', + chainId: '5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp', + chainIcon: _networkIcons['5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp'], + currency: 'SOL', + rpcUrl: 'https://api.mainnet-beta.solana.com', + explorerUrl: 'https://solscan.io', ), - ReownAppKitModalNetworkInfo( - name: 'Iotx', - chainId: '4689', - chainIcon: chainImagesId['4689'], - currency: 'IOTX', - rpcUrl: 'https://rpc.ankr.com/iotex', - explorerUrl: 'https://iotexscan.io', - ), - ReownAppKitModalNetworkInfo( - name: 'Metis', - chainId: '1088', - chainIcon: chainImagesId['1088'], - currency: 'METIS', - rpcUrl: 'https://metis-mainnet.public.blastapi.io', - explorerUrl: 'https://andromeda-explorer.metis.io', - ), - ] + ], }; - static Map> test = { - 'eip155': [ + static final Map> _testnets = { + NetworkUtils.eip155: [ ReownAppKitModalNetworkInfo( name: 'Sepolia', chainId: '11155111', @@ -186,7 +160,6 @@ class ReownAppKitModalNetworks { ReownAppKitModalNetworkInfo( name: 'Holesky', chainId: '17000', - chainIcon: chainImagesId['17000'], currency: 'ETH', rpcUrl: 'https://rpc.holesky.test', explorerUrl: 'https://explorer.holesky.test', @@ -210,7 +183,6 @@ class ReownAppKitModalNetworks { chainId: '80002', currency: 'MATIC', rpcUrl: 'https://rpc-amoy.polygon.technology/', - extraRpcUrls: [], explorerUrl: 'https://amoy.polygonscan.com', isTestNetwork: true, ), @@ -223,9 +195,130 @@ class ReownAppKitModalNetworks { isTestNetwork: true, ), ], + NetworkUtils.solana: [ + ReownAppKitModalNetworkInfo( + name: 'Solana Testnet', + chainId: '4uhcVJyU9pJkvQyS88uRDiswHXSCkY3z', + currency: 'SOL', + rpcUrl: 'https://api.testnet.solana.com', + explorerUrl: 'https://explorer.solana.com/?cluster=testnet', + isTestNetwork: true, + ), + ReownAppKitModalNetworkInfo( + name: 'Solana Devnet', + chainId: 'EtWTRABZaYq6iMfeYKouRu166VU2xqa1', + currency: 'SOL', + rpcUrl: 'https://api.devnet.solana.com', + explorerUrl: 'https://explorer.solana.com/?cluster=devnet', + isTestNetwork: true, + ), + ], }; - static Map chainImagesId = { + static ReownAppKitModalNetworkInfo? getNetworkById( + String namespace, + String chainId, + ) { + return getAllSupportedNetworks(namespace: namespace).firstWhere( + (e) => e.chainId == chainId, + ); + } + + static void removeSupportedNetworks( + String namespace, { + List chainIds = const [], + bool includeTestnets = true, + }) { + _mainnets[namespace]!.removeWhere((chain) { + if (chainIds.isEmpty || chainIds.contains(chain.chainId)) { + return true; + } + return false; + }); + if (includeTestnets) { + _testnets[namespace]!.removeWhere((chain) { + if (chainIds.isEmpty || chainIds.contains(chain.chainId)) { + return true; + } + return false; + }); + } + } + + static void removeTestNetworks() { + for (var key in _testnets.keys) { + _testnets[key]!.clear(); + } + } + + static void addSupportedNetworks( + String namespace, + List chains, + ) { + final List mainnets = [ + ...List.from(_mainnets[namespace] ?? []), + ...(chains.where((e) => e.isTestNetwork == false)), + ]; + _mainnets[namespace] = mainnets; + + final List testnets = [ + ...List.from(_testnets[namespace] ?? []), + ...(chains.where((e) => e.isTestNetwork == true)), + ]; + _testnets[namespace] = testnets; + } + + static List getAllSupportedNamespaces() { + final mainNamespaces = + _mainnets.keys.where((key) => _mainnets[key]!.isNotEmpty).toList(); + final testNamespaces = + _testnets.keys.where((key) => _mainnets[key]!.isNotEmpty).toList(); + return {...mainNamespaces, ...testNamespaces}.toList(); + } + + static List getAllSupportedNetworks({ + String? namespace, + }) { + final allMainnets = namespace != null + ? (_mainnets[namespace] ?? []) + : _mainnets.values.expand((e) => e); + final mainnets = allMainnets.where((e) { + return !e.isTestNetwork; + }).toList(); + final allTestnets = namespace != null + ? (_testnets[namespace] ?? []) + : _testnets.values.expand((e) => e); + final testnets = allTestnets.where((e) { + return e.isTestNetwork; + }).toList(); + return [...mainnets, ...testnets].toList(); + } + + static String getNamespaceForChainId(String chainId) { + String? namespace; + final namespaces = getAllSupportedNamespaces(); + for (var ns in namespaces) { + final found = getAllSupportedNetworks(namespace: ns).firstWhereOrNull( + (e) => e.chainId == chainId, + ); + if (found != null) { + namespace = ns; + break; + } + } + return namespace ?? ''; + } + + static String getNetworkIconId(String chainId) { + return _networkIcons[chainId] ?? ''; + } + + static String getCaip2Chain(String chainId) { + final namespace = getNamespaceForChainId(chainId); + return '$namespace:$chainId'; + } + + static final Map _networkIcons = { // Ethereum '1': 'ba0ba0cd-17c6-4806-ad93-f9d174f17900', // Optimism @@ -272,60 +365,9 @@ class ReownAppKitModalNetworks { '7777777': '845c60df-d429-4991-e687-91ae45791600', // Aurora '1313161554': '3ff73439-a619-4894-9262-4470c773a100', + // Solana + '5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp': 'a1b58899-f671-4276-6a5e-56ca5bd59700', + // Near + 'mainnet': 'ab3d4dee-e416-4d8d-226c-4c131b8b7600', }; - - static ReownAppKitModalNetworkInfo? getNetworkById( - String namespace, - String chainId, - ) { - return supported[namespace]?.firstWhere((e) => e.chainId == chainId); - } - - static void removeNetworks( - String namespace, - List chainIds, - ) { - supported[namespace]!.removeWhere((chain) { - if (chainIds.contains(chain.chainId)) { - return true; - } - return false; - }); - } - - static void addNetworks( - String namespace, - List chains, - ) { - final List networks = [ - ...List.from(supported[namespace] ?? []), - ...chains, - ]; - - supported[namespace] = networks; - } - - static List getNetworks(String namespace) { - return supported[namespace] ?? []; - } - - static String? getNamespaceForChainId(String chainId) { - // final allChains = supported.values.expand((e) => e).toList(); - String? namespace; - final namespaces = supported.keys.toList(); - for (var ns in namespaces) { - final found = supported[ns]!.firstWhereOrNull( - (e) => e.chainId == chainId, - ); - if (found != null) { - namespace = ns; - break; - } - } - return namespace; - } - - static String getNetworkIconId(String chainId) { - return chainImagesId[chainId] ?? ''; - } } diff --git a/packages/reown_appkit/lib/modal/utils/public/appkit_modal_networks_utils.dart b/packages/reown_appkit/lib/modal/utils/public/appkit_modal_networks_utils.dart new file mode 100644 index 0000000..f1baaa1 --- /dev/null +++ b/packages/reown_appkit/lib/modal/utils/public/appkit_modal_networks_utils.dart @@ -0,0 +1,25 @@ +import 'package:reown_core/reown_core.dart'; + +export 'appkit_modal_default_networks.dart'; +export 'appkit_modal_siwe_utils.dart'; + +class NetworkUtils { + static const eip155 = 'eip155'; + static const solana = 'solana'; + + static Map> defaultNetworkMethods = { + eip155: MethodsConstants.allMethods.toSet().toList(), + solana: [ + 'solana_getAccounts', + 'solana_signMessage', + 'solana_signTransaction', + 'solana_signAllTransactions', + 'solana_signAndSendTransaction', + ], + }; + + static Map> defaultNetworkEvents = { + eip155: EventsConstants.allEvents.toSet().toList(), + solana: [], + }; +} diff --git a/packages/reown_appkit/lib/modal/utils/public/appkit_modal_utils.dart b/packages/reown_appkit/lib/modal/utils/public/appkit_modal_utils.dart deleted file mode 100644 index 9193060..0000000 --- a/packages/reown_appkit/lib/modal/utils/public/appkit_modal_utils.dart +++ /dev/null @@ -1,2 +0,0 @@ -export 'appkit_modal_default_networks.dart'; -export 'appkit_modal_siwe_utils.dart'; diff --git a/packages/reown_appkit/lib/modal/widgets/avatars/account_avatar.dart b/packages/reown_appkit/lib/modal/widgets/avatars/account_avatar.dart index 51cee2b..211c368 100644 --- a/packages/reown_appkit/lib/modal/widgets/avatars/account_avatar.dart +++ b/packages/reown_appkit/lib/modal/widgets/avatars/account_avatar.dart @@ -68,8 +68,14 @@ class _AccountAvatarState extends State { void _modalNotifyListener() { setState(() { - _avatarUrl = widget.appKit.avatarUrl; - _address = widget.appKit.session?.address; + try { + _avatarUrl = widget.appKit.avatarUrl; + final chainId = widget.appKit.selectedChain?.chainId ?? ''; + final namespace = ReownAppKitModalNetworks.getNamespaceForChainId( + chainId, + ); + _address = widget.appKit.session?.getAddress(namespace); + } catch (_) {} }); } } diff --git a/packages/reown_appkit/lib/modal/widgets/avatars/wallet_avatar.dart b/packages/reown_appkit/lib/modal/widgets/avatars/wallet_avatar.dart index 90a2873..022be03 100644 --- a/packages/reown_appkit/lib/modal/widgets/avatars/wallet_avatar.dart +++ b/packages/reown_appkit/lib/modal/widgets/avatars/wallet_avatar.dart @@ -26,6 +26,7 @@ class ListAvatar extends StatelessWidget { final radiuses = ReownAppKitModalTheme.radiusesOf(context); final radius = borderRadius ?? radiuses.radiusM; final projectId = explorerService.instance.projectId; + final validImage = (imageUrl ?? '').isNotEmpty && !disabled; return Stack( children: [ AspectRatio( @@ -67,11 +68,11 @@ class ListAvatar extends StatelessWidget { borderRadius: BorderRadius.circular(radius), ), clipBehavior: Clip.antiAlias, - child: (imageUrl ?? '').isNotEmpty + child: validImage ? ColorFiltered( colorFilter: ColorFilter.mode( - disabled ? Colors.grey : Colors.transparent, - BlendMode.saturation, + disabled ? Colors.white : Colors.transparent, + disabled ? BlendMode.saturation : BlendMode.saturation, ), child: CachedNetworkImage( imageUrl: imageUrl!, @@ -90,8 +91,10 @@ class ListAvatar extends StatelessWidget { 'lib/modal/assets/icons/network.svg', package: 'reown_appkit', colorFilter: ColorFilter.mode( - themeColors.grayGlass030, - BlendMode.srcIn, + disabled + ? Colors.black12 + : themeColors.grayGlass030, + disabled ? BlendMode.srcIn : BlendMode.srcIn, ), ), ) diff --git a/packages/reown_appkit/lib/modal/widgets/buttons/address_button.dart b/packages/reown_appkit/lib/modal/widgets/buttons/address_button.dart index bb806fb..5ea4e1d 100644 --- a/packages/reown_appkit/lib/modal/widgets/buttons/address_button.dart +++ b/packages/reown_appkit/lib/modal/widgets/buttons/address_button.dart @@ -38,7 +38,15 @@ class _AddressButtonState extends State { void _modalNotifyListener() { setState(() { - _address = widget.service.session?.address; + try { + final chainId = widget.service.selectedChain!.chainId; + final namespace = ReownAppKitModalNetworks.getNamespaceForChainId( + chainId, + ); + _address = widget.service.session?.getAddress(namespace); + } catch (e) { + widget.service.appKit!.core.logger.e('[$runtimeType] $e'); + } }); } diff --git a/packages/reown_appkit/lib/modal/widgets/buttons/address_copy_button.dart b/packages/reown_appkit/lib/modal/widgets/buttons/address_copy_button.dart index 2c25388..47c7cee 100644 --- a/packages/reown_appkit/lib/modal/widgets/buttons/address_copy_button.dart +++ b/packages/reown_appkit/lib/modal/widgets/buttons/address_copy_button.dart @@ -19,10 +19,16 @@ class AddressCopyButton extends StatelessWidget { final themeColors = ReownAppKitModalTheme.colorsOf(context); return GestureDetector( onTap: () async { - await Clipboard.setData(ClipboardData(text: service.session!.address!)); - toastService.instance.show( - ToastMessage(type: ToastType.success, text: 'Address copied'), + final chainId = service.selectedChain!.chainId; + final namespace = ReownAppKitModalNetworks.getNamespaceForChainId( + chainId, ); + final address = service.session!.getAddress(namespace)!; + await Clipboard.setData(ClipboardData(text: address)); + toastService.instance.show(ToastMessage( + type: ToastType.success, + text: 'Address copied', + )); }, child: Row( mainAxisAlignment: MainAxisAlignment.center, diff --git a/packages/reown_appkit/lib/modal/widgets/lists/grid_items/wallet_grid_item.dart b/packages/reown_appkit/lib/modal/widgets/lists/grid_items/wallet_grid_item.dart index 8cc65f7..b8917cb 100644 --- a/packages/reown_appkit/lib/modal/widgets/lists/grid_items/wallet_grid_item.dart +++ b/packages/reown_appkit/lib/modal/widgets/lists/grid_items/wallet_grid_item.dart @@ -95,7 +95,9 @@ class WalletGridItem extends StatelessWidget { style: themeData.textStyles.tiny500.copyWith( color: isSelected ? themeColors.accent100 - : themeColors.foreground100, + : (isNetwork && onTap == null) + ? themeColors.background300 + : themeColors.foreground100, height: 1.0, ), ), diff --git a/packages/reown_appkit/lib/modal/widgets/public/appkit_modal_account_button.dart b/packages/reown_appkit/lib/modal/widgets/public/appkit_modal_account_button.dart index a21f5c4..d7eed91 100644 --- a/packages/reown_appkit/lib/modal/widgets/public/appkit_modal_account_button.dart +++ b/packages/reown_appkit/lib/modal/widgets/public/appkit_modal_account_button.dart @@ -58,7 +58,17 @@ class _AppKitModalAccountButtonState extends State { } void _modalNotifyListener() { - setState(() => _address = widget.appKit.session?.address ?? ''); + setState(() { + final chainId = widget.appKit.selectedChain?.chainId ?? ''; + final namespace = ReownAppKitModalNetworks.getNamespaceForChainId( + chainId, + ); + _address = widget.appKit.session?.getAddress(namespace) ?? ''; + final imageId = ReownAppKitModalNetworks.getNetworkIconId(chainId); + _tokenImage = explorerService.instance.getAssetImageUrl(imageId); + _balance = widget.appKit.chainBalance; + _tokenName = widget.appKit.selectedChain?.currency; + }); } void _onTap() { diff --git a/packages/reown_appkit/lib/modal/widgets/text/appkit_address.dart b/packages/reown_appkit/lib/modal/widgets/text/appkit_address.dart index 814cc3e..9add272 100644 --- a/packages/reown_appkit/lib/modal/widgets/text/appkit_address.dart +++ b/packages/reown_appkit/lib/modal/widgets/text/appkit_address.dart @@ -48,7 +48,11 @@ class _AddressState extends State
{ void _modalNotifyListener() { setState(() { - _address = widget.service.session?.address; + final chainId = widget.service.selectedChain?.chainId ?? ''; + final namespace = ReownAppKitModalNetworks.getNamespaceForChainId( + chainId, + ); + _address = widget.service.session?.getAddress(namespace); }); } } diff --git a/packages/reown_appkit/lib/modal/widgets/value_listenable_builders/network_service_items_listener.dart b/packages/reown_appkit/lib/modal/widgets/value_listenable_builders/network_service_items_listener.dart index a18de2f..1c2e19c 100644 --- a/packages/reown_appkit/lib/modal/widgets/value_listenable_builders/network_service_items_listener.dart +++ b/packages/reown_appkit/lib/modal/widgets/value_listenable_builders/network_service_items_listener.dart @@ -1,9 +1,9 @@ import 'package:flutter/material.dart'; -import 'package:reown_appkit/modal/constants/string_constants.dart'; import 'package:reown_appkit/modal/models/grid_item.dart'; import 'package:reown_appkit/modal/models/public/appkit_network_info.dart'; import 'package:reown_appkit/modal/services/network_service/network_service_singleton.dart'; +import 'package:reown_appkit/modal/utils/public/appkit_modal_default_networks.dart'; import 'package:reown_appkit/modal/widgets/modal_provider.dart'; class NetworkServiceItemsListener extends StatelessWidget { @@ -28,22 +28,7 @@ class NetworkServiceItemsListener extends StatelessWidget { return ValueListenableBuilder( valueListenable: networkService.instance.itemList, builder: (context, items, _) { - final service = ModalProvider.of(context).instance; - final availableChains = service.getAvailableChains(); - final parsedItems = availableChains == null - ? items - : items.map((e) { - final caip2Chain = - '${CoreConstants.namespace}:${e.data.chainId}'; - return e.copyWith( - disabled: !availableChains.contains(caip2Chain), - ); - }).toList() - ..sort((a, b) { - final disabledA = a.disabled ? 0 : 1; - final disabledB = b.disabled ? 0 : 1; - return disabledB.compareTo(disabledA); - }); + final parsedItems = items.parseItems(context); return builder(context, initialised, parsedItems); }, ); @@ -51,3 +36,29 @@ class NetworkServiceItemsListener extends StatelessWidget { ); } } + +extension on List> { + List> parseItems(BuildContext context) { + final service = ModalProvider.of(context).instance; + final supportedChains = service.getAvailableChains(); + if (supportedChains == null) { + return this + ..sort((a, b) { + final disabledA = a.disabled ? 0 : 1; + final disabledB = b.disabled ? 0 : 1; + return disabledB.compareTo(disabledA); + }); + } + return map((item) { + final caip2Chain = ReownAppKitModalNetworks.getCaip2Chain( + item.data.chainId, + ); + return item.copyWith(disabled: !supportedChains.contains(caip2Chain)); + }).toList() + ..sort((a, b) { + final disabledA = a.disabled ? 0 : 1; + final disabledB = b.disabled ? 0 : 1; + return disabledB.compareTo(disabledA); + }); + } +} From 9b486c213d7214cc36342ea4f2786c00bceb4577 Mon Sep 17 00:00:00 2001 From: Alfreedom <00tango.bromine@icloud.com> Date: Tue, 15 Oct 2024 09:37:56 +0200 Subject: [PATCH 02/17] Merge and fix conflicts --- .../example/base/lib/pages/connect_page.dart | 6 +- .../base/lib/utils/crypto/polkadot.dart | 3 +- .../base/lib/widgets/session_widget.dart | 2 +- .../modal/ios/Runner/AppDelegate.swift | 25 -------- .../modal/lib/services/methods_service.dart | 58 +++++++++---------- .../modal/lib/widgets/session_widget.dart | 10 ++-- .../appkit_modal_select_network_page.dart | 17 +++--- .../magic_service/models/frame_message.dart | 2 +- .../magic_service/models/magic_events.dart | 8 ++- .../services/siwe_service/siwe_service.dart | 1 - .../public/appkit_modal_account_button.dart | 4 -- .../public/appkit_modal_address_button.dart | 14 ++++- .../network_service_items_listener.dart | 6 +- 13 files changed, 69 insertions(+), 87 deletions(-) diff --git a/packages/reown_appkit/example/base/lib/pages/connect_page.dart b/packages/reown_appkit/example/base/lib/pages/connect_page.dart index c7337fc..f169937 100644 --- a/packages/reown_appkit/example/base/lib/pages/connect_page.dart +++ b/packages/reown_appkit/example/base/lib/pages/connect_page.dart @@ -363,10 +363,12 @@ class ConnectPageState extends State { } List _buildRequestButtons() { - return widget.appKitModal.getApprovedMethods()?.map((method) { + final chainId = widget.appKitModal.selectedChain!.chainId; + final ns = ReownAppKitModalNetworks.getNamespaceForChainId(chainId); + return widget.appKitModal.getApprovedMethods(namespace: ns)?.map((method) { final topic = widget.appKitModal.session!.topic ?? ''; final chainId = widget.appKitModal.selectedChain!.chainId; - final address = widget.appKitModal.session!.address!; + final address = widget.appKitModal.session!.getAddress(ns)!; final requestParams = EIP155.getParams(method, address); final enabled = requestParams != null; return Container( diff --git a/packages/reown_appkit/example/base/lib/utils/crypto/polkadot.dart b/packages/reown_appkit/example/base/lib/utils/crypto/polkadot.dart index 02d770b..194aacb 100644 --- a/packages/reown_appkit/example/base/lib/utils/crypto/polkadot.dart +++ b/packages/reown_appkit/example/base/lib/utils/crypto/polkadot.dart @@ -1,5 +1,4 @@ import 'package:reown_appkit/reown_appkit.dart'; -import 'package:reown_appkit_dapp/models/chain_metadata.dart'; enum PolkadotMethods { polkadotSignTransaction, @@ -22,7 +21,7 @@ class Polkadot { required IReownAppKit appKit, required String topic, required String method, - required ChainMetadata chainData, + required ReownAppKitModalNetworkInfo chainData, required String address, }) { switch (method) { diff --git a/packages/reown_appkit/example/base/lib/widgets/session_widget.dart b/packages/reown_appkit/example/base/lib/widgets/session_widget.dart index c41977c..86c7b32 100644 --- a/packages/reown_appkit/example/base/lib/widgets/session_widget.dart +++ b/packages/reown_appkit/example/base/lib/widgets/session_widget.dart @@ -183,7 +183,7 @@ class SessionWidgetState extends State { chainMetadata.chainId, ); for (final String method in getChainMethods(namespace)) { - final namespaces = widget.session.namespaces[namespace]; + final namespaces = _session.namespaces[namespace]; final supported = namespaces?.methods.contains(method) ?? false; buttons.add( Container( diff --git a/packages/reown_appkit/example/modal/ios/Runner/AppDelegate.swift b/packages/reown_appkit/example/modal/ios/Runner/AppDelegate.swift index 7c561a2..9002210 100644 --- a/packages/reown_appkit/example/modal/ios/Runner/AppDelegate.swift +++ b/packages/reown_appkit/example/modal/ios/Runner/AppDelegate.swift @@ -115,28 +115,3 @@ class LinkStreamHandler: NSObject, FlutterStreamHandler { } } -class LinkStreamHandler: NSObject, FlutterStreamHandler { - var eventSink: FlutterEventSink? - var queuedLinks = [String]() - - func onListen(withArguments arguments: Any?, eventSink events: @escaping FlutterEventSink) -> FlutterError? { - self.eventSink = events - queuedLinks.forEach({ events($0) }) - queuedLinks.removeAll() - return nil - } - - func onCancel(withArguments arguments: Any?) -> FlutterError? { - self.eventSink = nil - return nil - } - - func handleLink(_ link: String) -> Bool { - guard let eventSink = eventSink else { - queuedLinks.append(link) - return false - } - eventSink(link) - return true - } -} \ No newline at end of file diff --git a/packages/reown_appkit/example/modal/lib/services/methods_service.dart b/packages/reown_appkit/example/modal/lib/services/methods_service.dart index eecce3c..8a0c6c5 100644 --- a/packages/reown_appkit/example/modal/lib/services/methods_service.dart +++ b/packages/reown_appkit/example/modal/lib/services/methods_service.dart @@ -149,7 +149,7 @@ class MethodsService { final bytes = utf8.encode(message); final encoded = hex.encode(bytes); final namespace = ReownAppKitModalNetworks.getNamespaceForChainId( - appKit.selectedChain!.chainId, + appKitModal.selectedChain!.chainId, ); return await appKitModal.request( @@ -159,7 +159,7 @@ class MethodsService { method: SupportedMethods.personalSign.name, params: [ '0x$encoded', - appKit.session!.getAddress(namespace)!, + appKitModal.session!.getAddress(namespace)!, ], ), ); @@ -170,16 +170,16 @@ class MethodsService { required String data, }) async { final namespace = ReownAppKitModalNetworks.getNamespaceForChainId( - appKit.selectedChain!.chainId, + appKitModal.selectedChain!.chainId, ); - return await appKit.request( - topic: appKit.session!.topic, - chainId: appKit.selectedChain!.chainId, + return await appKitModal.request( + topic: appKitModal.session!.topic, + chainId: appKitModal.selectedChain!.chainId, request: SessionRequestParams( method: SupportedMethods.ethSignTypedData.name, params: [ data, - appKit.session!.getAddress(namespace)!, + appKitModal.session!.getAddress(namespace)!, ], ), ); @@ -190,16 +190,16 @@ class MethodsService { required String data, }) async { final namespace = ReownAppKitModalNetworks.getNamespaceForChainId( - appKit.selectedChain!.chainId, + appKitModal.selectedChain!.chainId, ); - return await appKit.request( - topic: appKit.session!.topic, - chainId: appKit.selectedChain!.chainId, + return await appKitModal.request( + topic: appKitModal.session!.topic, + chainId: appKitModal.selectedChain!.chainId, request: SessionRequestParams( method: SupportedMethods.ethSignTypedDataV3.name, params: [ data, - appKit.session!.getAddress(namespace)!, + appKitModal.session!.getAddress(namespace)!, ], ), ); @@ -210,16 +210,16 @@ class MethodsService { required String data, }) async { final namespace = ReownAppKitModalNetworks.getNamespaceForChainId( - appKit.selectedChain!.chainId, + appKitModal.selectedChain!.chainId, ); - return await appKit.request( - topic: appKit.session!.topic, - chainId: appKit.selectedChain!.chainId, + return await appKitModal.request( + topic: appKitModal.session!.topic, + chainId: appKitModal.selectedChain!.chainId, request: SessionRequestParams( method: SupportedMethods.ethSignTypedDataV4.name, params: [ data, - appKit.session!.getAddress(namespace)!, + appKitModal.session!.getAddress(namespace)!, ], ), ); @@ -308,16 +308,16 @@ class MethodsService { final requestValue = _formatValue(0.01, decimals: d); // now we call `transfer` write function with the parsed value. final namespace = ReownAppKitModalNetworks.getNamespaceForChainId( - appKit.selectedChain!.chainId, + appKitModal.selectedChain!.chainId, ); - return appKit.requestWriteContract( - topic: appKit.session!.topic, - chainId: appKit.selectedChain!.chainId, + return appKitModal.requestWriteContract( + topic: appKitModal.session!.topic, + chainId: appKitModal.selectedChain!.chainId, deployedContract: deployedContract, functionName: 'transfer', transaction: Transaction( from: EthereumAddress.fromHex( - appKit.session!.getAddress(namespace)!, + appKitModal.session!.getAddress(namespace)!, ), ), parameters: [ @@ -385,16 +385,16 @@ class MethodsService { final requestValue = _formatValue(0.23, decimals: d); // now we call `transfer` write function with the parsed value. final namespace = ReownAppKitModalNetworks.getNamespaceForChainId( - appKit.selectedChain!.chainId, + appKitModal.selectedChain!.chainId, ); - return appKit.requestWriteContract( - topic: appKit.session!.topic, - chainId: appKit.selectedChain!.chainId, + return appKitModal.requestWriteContract( + topic: appKitModal.session!.topic, + chainId: appKitModal.selectedChain!.chainId, deployedContract: deployedContract, functionName: 'transfer', transaction: Transaction( from: EthereumAddress.fromHex( - appKit.session!.getAddress(namespace)!, + appKitModal.session!.getAddress(namespace)!, ), ), parameters: [ @@ -414,7 +414,7 @@ class MethodsService { required DeployedContract contract, }) async { final namespace = ReownAppKitModalNetworks.getNamespaceForChainId( - appKit.selectedChain!.chainId, + appKitModal.selectedChain!.chainId, ); final results = await Future.wait([ // results[0] @@ -438,7 +438,7 @@ class MethodsService { deployedContract: contract, functionName: 'balanceOf', parameters: [ - EthereumAddress.fromHex(appKit.session!.getAddress(namespace)!), + EthereumAddress.fromHex(appKitModal.session!.getAddress(namespace)!), ], ), // results[4] diff --git a/packages/reown_appkit/example/modal/lib/widgets/session_widget.dart b/packages/reown_appkit/example/modal/lib/widgets/session_widget.dart index ced8d01..7c25c73 100644 --- a/packages/reown_appkit/example/modal/lib/widgets/session_widget.dart +++ b/packages/reown_appkit/example/modal/lib/widgets/session_widget.dart @@ -332,7 +332,7 @@ class SessionWidgetState extends State { onPressed: onSepolia ? () async { final future = MethodsService.callTestSmartContract( - appKit: widget.appKit, + appKitModal: widget.appKit, action: 'read', ); MethodDialog.show( @@ -344,7 +344,7 @@ class SessionWidgetState extends State { : onMainnet ? () async { final future = MethodsService.callUSDTSmartContract( - appKit: widget.appKit, + appKitModal: widget.appKit, action: 'read', ); MethodDialog.show( @@ -369,7 +369,7 @@ class SessionWidgetState extends State { ? () async { widget.appKit.launchConnectedWallet(); final future = MethodsService.callTestSmartContract( - appKit: widget.appKit, + appKitModal: widget.appKit, action: 'write', ); MethodDialog.show(context, 'Test Contract (Write)', future); @@ -378,7 +378,7 @@ class SessionWidgetState extends State { ? () async { widget.appKit.launchConnectedWallet(); final future = MethodsService.callUSDTSmartContract( - appKit: widget.appKit, + appKitModal: widget.appKit, action: 'write', ); MethodDialog.show( @@ -484,7 +484,7 @@ class SessionWidgetState extends State { final namespace = ReownAppKitModalNetworks.getNamespaceForChainId(chainId); final address = session.getAddress(namespace)!; return MethodsService.callMethod( - appKit: widget.appKit, + appKitModal: widget.appKit, topic: session.topic ?? '', method: method, chainId: widget.appKit.selectedChain!.chainId, diff --git a/packages/reown_appkit/lib/modal/pages/public/appkit_modal_select_network_page.dart b/packages/reown_appkit/lib/modal/pages/public/appkit_modal_select_network_page.dart index 8670851..eb71547 100644 --- a/packages/reown_appkit/lib/modal/pages/public/appkit_modal_select_network_page.dart +++ b/packages/reown_appkit/lib/modal/pages/public/appkit_modal_select_network_page.dart @@ -27,29 +27,30 @@ class ReownAppKitModalSelectNetworkPage extends StatelessWidget { BuildContext context, ReownAppKitModalNetworkInfo chainInfo, ) async { - final service = ModalProvider.of(context).instance; - if (service.isConnected) { + final appKitModal = ModalProvider.of(context).instance; + if (appKitModal.isConnected) { final chainId = chainInfo.chainId; final caip2Chain = ReownAppKitModalNetworks.getCaip2Chain(chainId); final namespace = ReownAppKitModalNetworks.getNamespaceForChainId( chainId, ); - final approvedChains = service.session!.getApprovedChains( + final approvedChains = appKitModal.session!.getApprovedChains( namespace: namespace, ); final isChainApproved = (approvedChains ?? []).contains(caip2Chain); - if (chainInfo.chainId == service.selectedChain?.chainId) { + if (chainInfo.chainId == appKitModal.selectedChain?.chainId) { if (widgetStack.instance.canPop()) { widgetStack.instance.pop(); } else { - service.closeModal(); + appKitModal.closeModal(); } - } else if (isChainApproved || service.session!.sessionService.isMagic) { - await service.selectChain(chainInfo, switchChain: true); + } else if (isChainApproved || + appKitModal.session!.sessionService.isMagic) { + await appKitModal.selectChain(chainInfo, switchChain: true); if (widgetStack.instance.canPop()) { widgetStack.instance.pop(); } else { - service.closeModal(); + appKitModal.closeModal(); } } else { widgetStack.instance.push(ConnectNetworkPage(chainInfo: chainInfo)); diff --git a/packages/reown_appkit/lib/modal/services/magic_service/models/frame_message.dart b/packages/reown_appkit/lib/modal/services/magic_service/models/frame_message.dart index e2f9035..50a67db 100644 --- a/packages/reown_appkit/lib/modal/services/magic_service/models/frame_message.dart +++ b/packages/reown_appkit/lib/modal/services/magic_service/models/frame_message.dart @@ -137,7 +137,7 @@ class SwitchNetwork extends MessageData { }) : super(type: '@w3m-app/SWITCH_NETWORK'); @override - String toString() => '{type:"${super.type}",payload:{chainId:$chainId}}'; + String toString() => '{type:"${super.type}",payload:{chainId:"$chainId"}}'; } class GetSocialRedirectUri extends MessageData { diff --git a/packages/reown_appkit/lib/modal/services/magic_service/models/magic_events.dart b/packages/reown_appkit/lib/modal/services/magic_service/models/magic_events.dart index ff8e4f6..d67600b 100644 --- a/packages/reown_appkit/lib/modal/services/magic_service/models/magic_events.dart +++ b/packages/reown_appkit/lib/modal/services/magic_service/models/magic_events.dart @@ -14,7 +14,7 @@ class MagicSessionEvent implements EventArgs { String? userName; AppKitSocialOption? provider; String? address; - int? chainId; + String? chainId; MagicSessionEvent({ this.email, @@ -121,7 +121,11 @@ class GetUserErrorEvent extends MagicErrorEvent { } class SwitchNetworkErrorEvent extends MagicErrorEvent { - SwitchNetworkErrorEvent() : super('Error switching network'); + final String? message; + SwitchNetworkErrorEvent({this.message}) + : super( + message ?? 'Error switching network', + ); } class SignOutErrorEvent extends MagicErrorEvent { diff --git a/packages/reown_appkit/lib/modal/services/siwe_service/siwe_service.dart b/packages/reown_appkit/lib/modal/services/siwe_service/siwe_service.dart index 43007ea..807aa0a 100644 --- a/packages/reown_appkit/lib/modal/services/siwe_service/siwe_service.dart +++ b/packages/reown_appkit/lib/modal/services/siwe_service/siwe_service.dart @@ -3,7 +3,6 @@ import 'dart:convert'; import 'package:collection/collection.dart'; import 'package:convert/convert.dart'; import 'package:get_it/get_it.dart'; -import 'package:reown_appkit/modal/constants/string_constants.dart'; import 'package:reown_appkit/modal/services/coinbase_service/coinbase_service_singleton.dart'; import 'package:reown_appkit/modal/services/magic_service/i_magic_service.dart'; import 'package:reown_appkit/modal/services/siwe_service/i_siwe_service.dart'; diff --git a/packages/reown_appkit/lib/modal/widgets/public/appkit_modal_account_button.dart b/packages/reown_appkit/lib/modal/widgets/public/appkit_modal_account_button.dart index d7eed91..0200e57 100644 --- a/packages/reown_appkit/lib/modal/widgets/public/appkit_modal_account_button.dart +++ b/packages/reown_appkit/lib/modal/widgets/public/appkit_modal_account_button.dart @@ -64,10 +64,6 @@ class _AppKitModalAccountButtonState extends State { chainId, ); _address = widget.appKit.session?.getAddress(namespace) ?? ''; - final imageId = ReownAppKitModalNetworks.getNetworkIconId(chainId); - _tokenImage = explorerService.instance.getAssetImageUrl(imageId); - _balance = widget.appKit.chainBalance; - _tokenName = widget.appKit.selectedChain?.currency; }); } diff --git a/packages/reown_appkit/lib/modal/widgets/public/appkit_modal_address_button.dart b/packages/reown_appkit/lib/modal/widgets/public/appkit_modal_address_button.dart index 8a0d395..dbf0539 100644 --- a/packages/reown_appkit/lib/modal/widgets/public/appkit_modal_address_button.dart +++ b/packages/reown_appkit/lib/modal/widgets/public/appkit_modal_address_button.dart @@ -18,10 +18,18 @@ class AppKitModalAddressButton extends StatelessWidget { @override Widget build(BuildContext context) { - if ((appKitModal.session?.address ?? '').isEmpty) { + if (appKitModal.session == null) { + return SizedBox.shrink(); + } + final chainId = appKitModal.selectedChain?.chainId ?? ''; + if (chainId.isEmpty) { + return SizedBox.shrink(); + } + final namespace = ReownAppKitModalNetworks.getNamespaceForChainId(chainId); + final address = appKitModal.session!.getAddress(namespace); + if ((address ?? '').isEmpty) { return SizedBox.shrink(); } - final address = appKitModal.session!.address!; final themeData = ReownAppKitModalTheme.getDataOf(context); final textStyle = size == BaseButtonSize.small ? themeData.textStyles.small600 @@ -98,7 +106,7 @@ class AppKitModalAddressButton extends StatelessWidget { const SizedBox.square(dimension: 4.0), Text( RenderUtils.truncate( - address, + address!, length: size == BaseButtonSize.small ? 2 : 4, ), style: textStyle, diff --git a/packages/reown_appkit/lib/modal/widgets/value_listenable_builders/network_service_items_listener.dart b/packages/reown_appkit/lib/modal/widgets/value_listenable_builders/network_service_items_listener.dart index 1c2e19c..18ee35d 100644 --- a/packages/reown_appkit/lib/modal/widgets/value_listenable_builders/network_service_items_listener.dart +++ b/packages/reown_appkit/lib/modal/widgets/value_listenable_builders/network_service_items_listener.dart @@ -3,7 +3,6 @@ import 'package:reown_appkit/modal/models/grid_item.dart'; import 'package:reown_appkit/modal/models/public/appkit_network_info.dart'; import 'package:reown_appkit/modal/services/network_service/network_service_singleton.dart'; -import 'package:reown_appkit/modal/utils/public/appkit_modal_default_networks.dart'; import 'package:reown_appkit/modal/widgets/modal_provider.dart'; class NetworkServiceItemsListener extends StatelessWidget { @@ -50,10 +49,9 @@ extension on List> { }); } return map((item) { - final caip2Chain = ReownAppKitModalNetworks.getCaip2Chain( - item.data.chainId, + return item.copyWith( + disabled: !supportedChains.contains(item.data.chainId), ); - return item.copyWith(disabled: !supportedChains.contains(caip2Chain)); }).toList() ..sort((a, b) { final disabledA = a.disabled ? 0 : 1; From c75a06b4afc056904a0ec97a0347f729d4d16030 Mon Sep 17 00:00:00 2001 From: Alfreedom <00tango.bromine@icloud.com> Date: Wed, 16 Oct 2024 09:13:34 +0200 Subject: [PATCH 03/17] bug fixes and enabling solana with social login --- .../reown_appkit/example/base/lib/main.dart | 6 +- .../example/base/lib/pages/connect_page.dart | 44 +- .../example/base/lib/utils/crypto/eip155.dart | 63 +- .../base/lib/utils/crypto/helpers.dart | 126 ++++ .../example/base/lib/utils/crypto/solana.dart | 77 +- packages/reown_appkit/example/example.md | 37 +- .../example/modal/lib/home_page.dart | 19 +- .../lib/modal/appkit_modal_impl.dart | 196 ++--- .../lib/modal/constants/string_constants.dart | 6 +- .../models/public/appkit_modal_session.dart | 64 +- .../lib/modal/pages/account_page.dart | 5 +- .../lib/modal/pages/confirm_email_page.dart | 4 - .../lib/modal/pages/connet_network_page.dart | 74 +- .../lib/modal/pages/edit_email_page.dart | 6 - .../modal/pages/farcaster_qrcode_page.dart | 23 - .../public/appkit_modal_qrcode_page.dart | 32 +- .../appkit_modal_select_network_page.dart | 19 +- .../lib/modal/pages/social_login_page.dart | 26 +- .../blockchain_service.dart | 2 +- .../magic_service/i_magic_service.dart | 10 +- .../services/magic_service/magic_service.dart | 706 ++++++++++-------- .../magic_service/models/frame_message.dart | 16 +- .../magic_service/models/magic_data.dart | 17 +- .../lib/modal/utils/core_utils.dart | 6 +- .../widgets/miscellaneous/input_email.dart | 8 +- .../network_service_items_listener.dart | 6 +- 26 files changed, 897 insertions(+), 701 deletions(-) diff --git a/packages/reown_appkit/example/base/lib/main.dart b/packages/reown_appkit/example/base/lib/main.dart index bd28e18..ffc0178 100644 --- a/packages/reown_appkit/example/base/lib/main.dart +++ b/packages/reown_appkit/example/base/lib/main.dart @@ -122,8 +122,8 @@ class _MyHomePageState extends State { _appKit!.onSessionAuthResponse.subscribe(_onSessionAuthResponse); // See https://docs.reown.com/appkit/flutter/core/custom-chains - final testNetworks = ReownAppKitModalNetworks.test['eip155'] ?? []; - ReownAppKitModalNetworks.addNetworks('eip155', testNetworks); + // final testNetworks = ReownAppKitModalNetworks.test['eip155'] ?? []; + // ReownAppKitModalNetworks.addNetworks('eip155', testNetworks); _appKitModal = ReownAppKitModal( context: context, @@ -472,7 +472,7 @@ class _MyHomePageState extends State { }, getSession: () async { // Return proper session from your Web Service - final chainId = _appKitModal!.session!.chainId; + final chainId = _appKitModal!.selectedChain?.chainId ?? '1'; final namespace = ReownAppKitModalNetworks.getNamespaceForChainId( chainId, ); diff --git a/packages/reown_appkit/example/base/lib/pages/connect_page.dart b/packages/reown_appkit/example/base/lib/pages/connect_page.dart index f169937..17ddbfb 100644 --- a/packages/reown_appkit/example/base/lib/pages/connect_page.dart +++ b/packages/reown_appkit/example/base/lib/pages/connect_page.dart @@ -10,6 +10,7 @@ import 'package:qr_flutter/qr_flutter.dart'; import 'package:reown_appkit/reown_appkit.dart'; import 'package:reown_appkit_dapp/utils/constants.dart'; import 'package:reown_appkit_dapp/utils/crypto/eip155.dart'; +import 'package:reown_appkit_dapp/utils/crypto/helpers.dart'; import 'package:reown_appkit_dapp/utils/crypto/polkadot.dart'; import 'package:reown_appkit_dapp/utils/crypto/solana.dart'; import 'package:reown_appkit_dapp/utils/sample_wallets.dart'; @@ -363,34 +364,43 @@ class ConnectPageState extends State { } List _buildRequestButtons() { - final chainId = widget.appKitModal.selectedChain!.chainId; + final chainId = widget.appKitModal.selectedChain?.chainId ?? '1'; final ns = ReownAppKitModalNetworks.getNamespaceForChainId(chainId); return widget.appKitModal.getApprovedMethods(namespace: ns)?.map((method) { final topic = widget.appKitModal.session!.topic ?? ''; final chainId = widget.appKitModal.selectedChain!.chainId; final address = widget.appKitModal.session!.getAddress(ns)!; - final requestParams = EIP155.getParams(method, address); - final enabled = requestParams != null; + final chainInfo = ReownAppKitModalNetworks.getNetworkById( + ns, + chainId, + ); + // final requestParams = await getParams(method, address); + // final enabled = requestParams != null; return Container( height: 40.0, width: double.infinity, margin: const EdgeInsets.symmetric( vertical: StyleConstants.linear8, ), - child: ElevatedButton( - onPressed: enabled - ? () { - widget.appKitModal.launchConnectedWallet(); - final future = widget.appKitModal.request( - topic: topic, - chainId: chainId, - request: requestParams, - ); - MethodDialog.show(context, method, future); - } - : null, - child: Text(method), - ), + child: FutureBuilder( + future: getParams(method, address, rpcUrl: chainInfo?.rpcUrl), + builder: (_, snapshot) { + final enabled = snapshot.data != null; + return ElevatedButton( + onPressed: enabled + ? () { + widget.appKitModal.launchConnectedWallet(); + final future = widget.appKitModal.request( + topic: topic, + chainId: chainId, + request: snapshot.data!, + ); + MethodDialog.show(context, method, future); + } + : null, + child: Text(method), + ); + }), ); }).toList() ?? []; diff --git a/packages/reown_appkit/example/base/lib/utils/crypto/eip155.dart b/packages/reown_appkit/example/base/lib/utils/crypto/eip155.dart index 0bf6ae3..70aac59 100644 --- a/packages/reown_appkit/example/base/lib/utils/crypto/eip155.dart +++ b/packages/reown_appkit/example/base/lib/utils/crypto/eip155.dart @@ -1,10 +1,9 @@ import 'dart:convert'; -import 'package:eth_sig_util/util/utils.dart'; import 'package:intl/intl.dart'; import 'package:reown_appkit/reown_appkit.dart'; +import 'package:reown_appkit_dapp/utils/crypto/helpers.dart'; import 'package:reown_appkit_dapp/utils/smart_contracts.dart'; -import 'package:reown_appkit_dapp/utils/test_data.dart'; enum EIP155Methods { personalSign, @@ -139,7 +138,7 @@ class EIP155 { return await appKit.request( topic: topic, chainId: chainId, - request: getParams('personal_sign', address)!, + request: (await getParams('personal_sign', address))!, ); } @@ -152,7 +151,7 @@ class EIP155 { return await appKit.request( topic: topic, chainId: chainId, - request: getParams('eth_sign', address)!, + request: (await getParams('eth_sign', address))!, ); } @@ -165,7 +164,7 @@ class EIP155 { return await appKit.request( topic: topic, chainId: chainId, - request: getParams('eth_signTypedData', address)!, + request: (await getParams('eth_signTypedData', address))!, ); } @@ -177,7 +176,7 @@ class EIP155 { return await appKit.request( topic: topic, chainId: chainId, - request: getParams('eth_signTransaction', '')!, + request: (await getParams('eth_signTransaction', ''))!, ); } @@ -189,7 +188,7 @@ class EIP155 { return await appKit.request( topic: topic, chainId: chainId, - request: getParams('eth_sendTransaction', '')!, + request: (await getParams('eth_sendTransaction', ''))!, ); } @@ -234,54 +233,4 @@ class EIP155 { 'balance': oCcy.format(balance), }; } - - static SessionRequestParams? getParams(String method, String address) { - switch (method) { - case 'personal_sign': - final bytes = utf8.encode(testSignData); - final encoded = bytesToHex(bytes, include0x: true); - return SessionRequestParams( - method: methods[EIP155Methods.personalSign]!, - params: [encoded, address], - ); - case 'eth_sign': - return SessionRequestParams( - method: methods[EIP155Methods.ethSign]!, - params: [address, testSignData], - ); - case 'eth_signTypedData': - return SessionRequestParams( - method: methods[EIP155Methods.ethSignTypedData]!, - params: [address, typedData], - ); - case 'eth_signTransaction': - return SessionRequestParams( - method: methods[EIP155Methods.ethSignTransaction]!, - params: [ - Transaction( - from: EthereumAddress.fromHex(address), - to: EthereumAddress.fromHex( - '0x59e2f66C0E96803206B6486cDb39029abAE834c0', - ), - value: EtherAmount.fromInt(EtherUnit.finney, 12), // == 0.012 - ).toJson(), - ], - ); - case 'eth_sendTransaction': - return SessionRequestParams( - method: methods[EIP155Methods.ethSendTransaction]!, - params: [ - Transaction( - from: EthereumAddress.fromHex(address), - to: EthereumAddress.fromHex( - '0x59e2f66C0E96803206B6486cDb39029abAE834c0', - ), - value: EtherAmount.fromInt(EtherUnit.finney, 12), // == 0.012 - ).toJson(), - ], - ); - default: - return null; - } - } } diff --git a/packages/reown_appkit/example/base/lib/utils/crypto/helpers.dart b/packages/reown_appkit/example/base/lib/utils/crypto/helpers.dart index afb871c..5caf4b9 100644 --- a/packages/reown_appkit/example/base/lib/utils/crypto/helpers.dart +++ b/packages/reown_appkit/example/base/lib/utils/crypto/helpers.dart @@ -1,6 +1,14 @@ +import 'dart:convert'; + +import 'package:bs58/bs58.dart'; +import 'package:eth_sig_util/util/utils.dart'; +import 'package:reown_appkit/reown_appkit.dart'; import 'package:reown_appkit_dapp/utils/crypto/eip155.dart'; import 'package:reown_appkit_dapp/utils/crypto/polkadot.dart'; import 'package:reown_appkit_dapp/utils/crypto/solana.dart'; +import 'package:reown_appkit_dapp/utils/test_data.dart'; + +import 'package:solana_web3/solana_web3.dart' as solana; List getChainMethods(String namespace) { switch (namespace) { @@ -27,3 +35,121 @@ List getChainEvents(String namespace) { return []; } } + +Future getParams( + String method, + String address, { + String? rpcUrl, +}) async { + switch (method) { + case 'personal_sign': + final bytes = utf8.encode(testSignData); + final encoded = bytesToHex(bytes, include0x: true); + return SessionRequestParams( + method: method, + params: [encoded, address], + ); + case 'eth_sign': + return SessionRequestParams( + method: method, + params: [address, testSignData], + ); + case 'eth_signTypedData': + return SessionRequestParams( + method: method, + params: [address, typedData], + ); + case 'eth_signTransaction': + return SessionRequestParams( + method: method, + params: [ + Transaction( + from: EthereumAddress.fromHex(address), + to: EthereumAddress.fromHex( + '0x59e2f66C0E96803206B6486cDb39029abAE834c0', + ), + value: EtherAmount.fromInt(EtherUnit.finney, 12), // == 0.012 + ).toJson(), + ], + ); + case 'eth_sendTransaction': + return SessionRequestParams( + method: method, + params: [ + Transaction( + from: EthereumAddress.fromHex(address), + to: EthereumAddress.fromHex( + '0x59e2f66C0E96803206B6486cDb39029abAE834c0', + ), + value: EtherAmount.fromInt(EtherUnit.finney, 12), // == 0.012 + ).toJson(), + ], + ); + case 'solana_signMessage': + final bytes = utf8.encode(testSignData); + final message = base58.encode(bytes); + return SessionRequestParams( + method: method, + params: {'pubkey': address, 'message': message}, + ); + case 'solana_signTransaction': + // Create a connection to the devnet cluster. + final cluster = solana.Cluster.https( + Uri.parse(rpcUrl!).authority, + ); + // final cluster = solana.Cluster.devnet; + final connection = solana.Connection(cluster); + + // Fetch the latest blockhash. + final blockhash = await connection.getLatestBlockhash(); + + // Create a System Program instruction to transfer 0.5 SOL from [address1] to [address2]. + final transactionv0 = solana.Transaction.v0( + payer: solana.Pubkey.fromBase58(address), + recentBlockhash: blockhash.blockhash, + instructions: [ + solana.TransactionInstruction.fromJson({ + 'programId': '11111111111111111111111111111111', + 'data': [2, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0], + 'keys': [ + { + 'isSigner': true, + 'isWritable': true, + 'pubkey': address, + }, + { + 'isSigner': false, + 'isWritable': true, + 'pubkey': '8vCyX7oB6Pc3pbWMGYYZF5pbSnAdQ7Gyr32JqxqCy8ZR' + } + ] + }), + // SystemProgram.transfer( + // fromPubkey: solana.Pubkey.fromBase58(address), + // toPubkey: solana.Pubkey.fromBase58( + // '8vCyX7oB6Pc3pbWMGYYZF5pbSnAdQ7Gyr32JqxqCy8ZR', + // ), + // lamports: solana.solToLamports(0.5), + // ), + ], + ); + + const config = solana.TransactionSerializableConfig( + verifySignatures: false, + ); + final bytes = transactionv0.serialize(config).asUint8List(); + final encodedV0Trx = base64.encode(bytes); + + return SessionRequestParams( + method: method, + params: { + 'transaction': encodedV0Trx, + // 'pubkey': address, + // 'feePayer': address, + // ...transactionv0.message.toJson(), + }, + ); + default: + return null; + } +} diff --git a/packages/reown_appkit/example/base/lib/utils/crypto/solana.dart b/packages/reown_appkit/example/base/lib/utils/crypto/solana.dart index defd03f..57266e1 100644 --- a/packages/reown_appkit/example/base/lib/utils/crypto/solana.dart +++ b/packages/reown_appkit/example/base/lib/utils/crypto/solana.dart @@ -1,6 +1,4 @@ -import 'dart:convert'; -import 'package:bs58/bs58.dart'; -import 'package:solana_web3/solana_web3.dart' as solana; +import 'package:reown_appkit_dapp/utils/crypto/helpers.dart'; import 'package:reown_appkit/reown_appkit.dart'; enum SolanaMethods { @@ -29,81 +27,20 @@ class Solana { }) async { switch (method) { case 'solana_signMessage': - final bytes = utf8.encode( - 'This is an example message to be signed - ${DateTime.now()}', - ); - final message = base58.encode(bytes); return appKit.request( topic: topic, chainId: chainData.chainId, - request: SessionRequestParams( - method: method, - params: { - 'pubkey': address, - 'message': message, - }, - ), + request: (await getParams(method, address))!, ); case 'solana_signTransaction': - // Create a connection to the devnet cluster. - final cluster = solana.Cluster.https( - Uri.parse(chainData.rpcUrl).authority, - ); - // final cluster = solana.Cluster.devnet; - final connection = solana.Connection(cluster); - - // Fetch the latest blockhash. - final blockhash = await connection.getLatestBlockhash(); - - // Create a System Program instruction to transfer 0.5 SOL from [address1] to [address2]. - final transactionv0 = solana.Transaction.v0( - payer: solana.Pubkey.fromBase58(address), - recentBlockhash: blockhash.blockhash, - instructions: [ - solana.TransactionInstruction.fromJson({ - 'programId': '11111111111111111111111111111111', - 'data': [2, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0], - 'keys': [ - { - 'isSigner': true, - 'isWritable': true, - 'pubkey': address, - }, - { - 'isSigner': false, - 'isWritable': true, - 'pubkey': '8vCyX7oB6Pc3pbWMGYYZF5pbSnAdQ7Gyr32JqxqCy8ZR' - } - ] - }), - // SystemProgram.transfer( - // fromPubkey: solana.Pubkey.fromBase58(address), - // toPubkey: solana.Pubkey.fromBase58( - // '8vCyX7oB6Pc3pbWMGYYZF5pbSnAdQ7Gyr32JqxqCy8ZR', - // ), - // lamports: solana.solToLamports(0.5), - // ), - ], - ); - - const config = solana.TransactionSerializableConfig( - verifySignatures: false, - ); - final bytes = transactionv0.serialize(config).asUint8List(); - final encodedV0Trx = base64.encode(bytes); - return appKit.request( topic: topic, chainId: chainData.chainId, - request: SessionRequestParams( - method: method, - params: { - 'transaction': encodedV0Trx, - 'pubkey': address, - 'feePayer': address, - ...transactionv0.message.toJson(), - }, - ), + request: (await getParams( + method, + address, + rpcUrl: chainData.rpcUrl, + ))!, ); default: throw 'Method unimplemented'; diff --git a/packages/reown_appkit/example/example.md b/packages/reown_appkit/example/example.md index 2abf4a8..e998461 100644 --- a/packages/reown_appkit/example/example.md +++ b/packages/reown_appkit/example/example.md @@ -38,7 +38,7 @@ class _MyHomePageState extends State { super.initState(); _appKitModal = ReownAppKitModal( context: context, - projectId: '07429........', + projectId: '074.....', metadata: const PairingMetadata( name: 'Example App', description: 'Example app description', @@ -51,7 +51,7 @@ class _MyHomePageState extends State { ), ); - _appKitModal.init(); + _appKitModal.init().then((value) => setState(() {})); } @override @@ -61,25 +61,26 @@ class _MyHomePageState extends State { backgroundColor: Theme.of(context).colorScheme.inversePrimary, title: Text(widget.title), ), - body: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - AppKitModalNetworkSelectButton( - appKit: _appKitModal, - context: context, - ), - AppKitModalConnectButton( - appKit: _appKitModal, - context: context, - ), - Visibility( - visible: _appKitModal.isConnected, - child: AppKitModalAccountButton( + body: Center( + child: Column( + children: [ + AppKitModalNetworkSelectButton( appKit: _appKitModal, context: context, ), - ) - ], + AppKitModalConnectButton( + appKit: _appKitModal, + context: context, + ), + Visibility( + visible: _appKitModal.isConnected, + child: AppKitModalAccountButton( + appKit: _appKitModal, + context: context, + ), + ) + ], + ), ), ); } diff --git a/packages/reown_appkit/example/modal/lib/home_page.dart b/packages/reown_appkit/example/modal/lib/home_page.dart index fc7f89e..410a880 100644 --- a/packages/reown_appkit/example/modal/lib/home_page.dart +++ b/packages/reown_appkit/example/modal/lib/home_page.dart @@ -1,3 +1,4 @@ +import 'dart:convert'; import 'dart:developer'; import 'package:fl_toast/fl_toast.dart'; @@ -169,7 +170,7 @@ class _MyHomePageState extends State { } catch (error) { debugPrint('[SIWEConfig] getSession error: $error'); // Fallback patch for testing purposes in case SIWE backend has issues - final chainId = _appKitModal.session!.chainId; + final chainId = _appKitModal.selectedChain?.chainId ?? '1'; final namespace = ReownAppKitModalNetworks.getNamespaceForChainId( chainId, ); @@ -267,13 +268,15 @@ class _MyHomePageState extends State { // MORE WALLETS https://explorer.walletconnect.com/?type=wallet&chains=eip155%3A1 getBalance: () async { try { + final chainId = _appKitModal.selectedChain!.chainId; final namespace = ReownAppKitModalNetworks.getNamespaceForChainId( _appKitModal.selectedChain!.chainId, ); + final address = _appKitModal.session!.getAddress(namespace)!; return await blockchainService.instance.getBalance( - address: _appKitModal.session!.getAddress(namespace)!, + address: address, namespace: namespace, - chainId: _appKitModal.selectedChain!.chainId, + chainId: chainId, ); } catch (e) { debugPrint('[$runtimeType] getBalance $e'); @@ -382,7 +385,7 @@ class _MyHomePageState extends State { void _onModalConnect(ModalConnect? event) async { setState(() {}); - debugPrint('[ExampleApp] _onModalConnect ${event?.session.toJson()}'); + log('[ExampleApp] _onModalConnect ${jsonEncode(event?.session.toJson())}'); } void _onModalUpdate(ModalConnect? event) { @@ -390,17 +393,17 @@ class _MyHomePageState extends State { } void _onModalNetworkChange(ModalNetworkChange? event) { - debugPrint('[ExampleApp] _onModalNetworkChange ${event?.toString()}'); + log('[ExampleApp] _onModalNetworkChange ${event?.toString()}'); setState(() {}); } void _onModalDisconnect(ModalDisconnect? event) { - debugPrint('[ExampleApp] _onModalDisconnect ${event?.toString()}'); + log('[ExampleApp] _onModalDisconnect ${event?.toString()}'); setState(() {}); } void _onModalError(ModalError? event) { - debugPrint('[ExampleApp] _onModalError ${event?.toString()}'); + log('[ExampleApp] _onModalError ${event?.toString()}'); // When user connected to Coinbase Wallet but Coinbase Wallet does not have a session anymore // (for instance if user disconnected the dapp directly within Coinbase Wallet) // Then Coinbase Wallet won't emit any event @@ -583,7 +586,7 @@ class _ConnectedView extends StatelessWidget { children: [ AppKitModalBalanceButton( appKitModal: appKit, - onTap: appKit.openModalView, + onTap: appKit.openNetworksView, ), const SizedBox.square(dimension: 8.0), AppKitModalAddressButton( diff --git a/packages/reown_appkit/lib/modal/appkit_modal_impl.dart b/packages/reown_appkit/lib/modal/appkit_modal_impl.dart index 28ca7a2..2b50754 100644 --- a/packages/reown_appkit/lib/modal/appkit_modal_impl.dart +++ b/packages/reown_appkit/lib/modal/appkit_modal_impl.dart @@ -6,6 +6,7 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:get_it/get_it.dart'; import 'package:reown_appkit/modal/services/magic_service/i_magic_service.dart'; +import 'package:reown_appkit/modal/services/toast_service/models/toast_message.dart'; import 'package:reown_core/store/i_store.dart'; import 'package:reown_appkit/reown_appkit.dart'; @@ -147,8 +148,6 @@ class ReownAppKitModal with ChangeNotifier implements IReownAppKitModal { Set? featuredWalletIds, Set? includedWalletIds, Set? excludedWalletIds, - bool? enableAnalytics, - bool enableEmail = false, Future Function()? getBalance, LogLevel logLevel = LogLevel.nothing, }) { @@ -259,17 +258,6 @@ class ReownAppKitModal with ChangeNotifier implements IReownAppKitModal { Completer _awaitRelayOnce = Completer(); - @override - Future dispatchEnvelope(String url) async { - final envelope = ReownCoreUtils.getSearchParamFromURL(url, 'wc_ev'); - if (envelope.isNotEmpty) { - await _appKit.dispatchEnvelope(url); - return true; - } - - return false; - } - @override Future init() async { _relayConnected = false; @@ -302,7 +290,10 @@ class ReownAppKitModal with ChangeNotifier implements IReownAppKitModal { _currentSelectedChainId ??= _currentSession!.chainId; await _setSesionAndChainData(_currentSession!); if (isMagic) { - await _magicService.init(); + final caip2Chain = ReownAppKitModalNetworks.getCaip2Chain( + _currentSelectedChainId!, + ); + await _magicService.init(chainId: caip2Chain); } } else { _magicService.init(); @@ -488,36 +479,29 @@ class ReownAppKitModal with ChangeNotifier implements IReownAppKitModal { _chainBalance = null; - if (_currentSession?.sessionService.isMagic == true) { - await _magicService.switchNetwork(chainId: chainInfo.chainId); - // onModalNetworkChange.broadcast(ModalNetworkChange( - // chainId: chainInfo.namespace, - // )); - } else { - final hasValidSession = _isConnected && _currentSession != null; - if (switchChain && hasValidSession && _currentSelectedChainId != null) { - final namespace = ReownAppKitModalNetworks.getNamespaceForChainId( - chainInfo.chainId, - ); - final approvedChains = _currentSession!.getApprovedChains( - namespace: namespace, - ); - final caip2chain = ReownAppKitModalNetworks.getCaip2Chain( - chainInfo.chainId, - ); - final hasChainAlready = (approvedChains ?? []).contains(caip2chain); - if (!hasChainAlready) { - requestSwitchToChain(chainInfo); - final hasSwitchMethod = _currentSession!.hasSwitchMethod(); - if (hasSwitchMethod) { - launchConnectedWallet(); - } - } else { - await _setLocalEthChain(chainInfo.chainId, logEvent: logEvent); + final hasValidSession = _isConnected && _currentSession != null; + if (switchChain && hasValidSession && _currentSelectedChainId != null) { + final namespace = ReownAppKitModalNetworks.getNamespaceForChainId( + chainInfo.chainId, + ); + final approvedChains = _currentSession!.getApprovedChains( + namespace: namespace, + ); + final newCaip2Chain = ReownAppKitModalNetworks.getCaip2Chain( + chainInfo.chainId, + ); + final hasChainAlready = (approvedChains ?? []).contains(newCaip2Chain); + if (!hasChainAlready) { + requestSwitchToChain(chainInfo); + final hasSwitchMethod = _currentSession!.hasSwitchMethod(); + if (hasSwitchMethod) { + launchConnectedWallet(); } } else { await _setLocalEthChain(chainInfo.chainId, logEvent: logEvent); } + } else { + await _setLocalEthChain(chainInfo.chainId, logEvent: logEvent); } } @@ -526,7 +510,8 @@ class ReownAppKitModal with ChangeNotifier implements IReownAppKitModal { List? getAvailableChains() { // if there's no session or if supportsAddChain method then every chain can be used final evmHasSwitch = _currentSession?.hasSwitchMethod() ?? false; - if (_currentSession == null || evmHasSwitch) { + if ((_currentSession == null || + (evmHasSwitch && _currentSession!.sessionService.isWC))) { return null; } final namespace = ReownAppKitModalNetworks.getNamespaceForChainId( @@ -542,7 +527,7 @@ class ReownAppKitModal with ChangeNotifier implements IReownAppKitModal { return null; } return _currentSession!.getApprovedChains( - namespace: null, + namespace: namespace, ); } @@ -569,7 +554,7 @@ class ReownAppKitModal with ChangeNotifier implements IReownAppKitModal { Future _setLocalEthChain(String chainId, {bool? logEvent}) async { _currentSelectedChainId = chainId; final caip2Chain = ReownAppKitModalNetworks.getCaip2Chain(chainId); - _logger.i('[$runtimeType] set local chain $caip2Chain'); + _appKit.core.logger.i('[$runtimeType] set local chain $caip2Chain'); _notify(); try { if (isConnected) { @@ -1368,8 +1353,9 @@ class ReownAppKitModal with ChangeNotifier implements IReownAppKitModal { // Set the required namespaces to everything in our chain presets _requiredNamespaces = {}; } - dev.log( - '[$runtimeType] required namespaces ${jsonEncode(_requiredNamespaces)}'); + _appKit.core.logger.i( + '[$runtimeType] required namespaces ${jsonEncode(_requiredNamespaces)}', + ); } void _setOptionalNamespaces(Map? optionalNSpaces) { @@ -1400,8 +1386,9 @@ class ReownAppKitModal with ChangeNotifier implements IReownAppKitModal { } } - dev.log( - '[$runtimeType] optional namespaces ${jsonEncode(_optionalNamespaces)}'); + _appKit.core.logger.i( + '[$runtimeType] optional namespaces ${jsonEncode(_optionalNamespaces)}', + ); } /// Loads account balance and avatar. @@ -1426,10 +1413,9 @@ class ReownAppKitModal with ChangeNotifier implements IReownAppKitModal { chainId: _currentSelectedChainId!, )); final tokenName = selectedChain?.currency ?? ''; - balanceNotifier.value = '$_chainBalance $tokenName'; - } catch (e) { - _logger.e('[$runtimeType] getBalance $e'); - } + final formattedBalance = CoreUtils.formatChainBalance(_chainBalance); + balanceNotifier.value = '$formattedBalance $tokenName'; + } catch (_) {} if (namespace == NetworkUtils.eip155) { // Get the avatar, each chainId is just a number in string form. @@ -1438,30 +1424,33 @@ class ReownAppKitModal with ChangeNotifier implements IReownAppKitModal { _currentSession!.getAddress(namespace)!, ); _avatarUrl = blockchainId.avatar; - } catch (e) { - _logger.e('[$runtimeType] getIdentity $e'); - } + } catch (_) {} } _notify(); } @override Future requestSwitchToChain( - ReownAppKitModalNetworkInfo newChain) async { + ReownAppKitModalNetworkInfo newChain, + ) async { if (_currentSession?.sessionService.isMagic == true) { await selectChain(newChain); return; } - final currentChain = ReownAppKitModalNetworks.getCaip2Chain( + final currentCaip2Chain = ReownAppKitModalNetworks.getCaip2Chain( _currentSelectedChainId!, ); - final newChainId = ReownAppKitModalNetworks.getCaip2Chain(newChain.chainId); - _logger.i('[$runtimeType] requesting switch to chain $newChainId'); + final newCaip2Chain = ReownAppKitModalNetworks.getCaip2Chain( + newChain.chainId, + ); + _appKit.core.logger.i( + '[$runtimeType] requesting switch to chain $newCaip2Chain', + ); try { await request( topic: _currentSession?.topic ?? '', - chainId: currentChain, - switchToChainId: newChainId, + chainId: currentCaip2Chain, + switchToChainId: newCaip2Chain, request: SessionRequestParams( method: MethodsConstants.walletSwitchEthChain, params: [ @@ -1496,16 +1485,20 @@ class ReownAppKitModal with ChangeNotifier implements IReownAppKitModal { @override Future requestAddChain(ReownAppKitModalNetworkInfo newChain) async { final topic = _currentSession?.topic ?? ''; - final currentChain = ReownAppKitModalNetworks.getCaip2Chain( + final currentCaip2Chain = ReownAppKitModalNetworks.getCaip2Chain( _currentSelectedChainId!, ); - final newChainId = ReownAppKitModalNetworks.getCaip2Chain(newChain.chainId); - _logger.i('[$runtimeType] requesting switch to add chain $newChainId'); + final newCaip2Chain = ReownAppKitModalNetworks.getCaip2Chain( + newChain.chainId, + ); + _appKit.core.logger.i( + '[$runtimeType] requesting switch to add chain $newCaip2Chain', + ); try { await request( topic: topic, - chainId: currentChain, - switchToChainId: newChainId, + chainId: currentCaip2Chain, + switchToChainId: newCaip2Chain, request: SessionRequestParams( method: MethodsConstants.walletAddEthChain, params: [newChain.toJson()], @@ -1576,6 +1569,30 @@ class ReownAppKitModal with ChangeNotifier implements IReownAppKitModal { _notify(); } + void _onModalError(ModalError? event) { + toastService.instance.show(ToastMessage( + type: ToastType.error, + text: event?.message ?? 'An error occurred.', + )); + } + + void _onNetworkChainRequireSIWE(ModalNetworkChange? args) async { + try { + if (siweService.instance!.signOutOnNetworkChange) { + await siweService.instance!.signOut(); + _disconnectOnClose = true; + widgetStack.instance.push(ApproveSIWEPage( + onSiweFinish: _oneSIWEFinish, + )); + } + } catch (e, s) { + _appKit.core.logger.d( + '[$runtimeType] _onNetworkChainRequireSIWE error: $e', + stackTrace: s, + ); + } + } + void _checkInitialized() { if (_status != ReownAppKitModalStatus.initialized && _status != ReownAppKitModalStatus.initializing) { @@ -1586,6 +1603,7 @@ class ReownAppKitModal with ChangeNotifier implements IReownAppKitModal { } void _registerListeners() { + onModalError.subscribe(_onModalError); // Magic _magicService.onMagicConnect.subscribe(_onMagicConnectEvent); _magicService.onMagicLoginSuccess.subscribe(_onMagicLoginEvent); @@ -1622,24 +1640,9 @@ class ReownAppKitModal with ChangeNotifier implements IReownAppKitModal { ); } - void _onNetworkChainRequireSIWE(ModalNetworkChange? args) async { - try { - if (siweService.instance!.signOutOnNetworkChange) { - await siweService.instance!.signOut(); - _disconnectOnClose = true; - widgetStack.instance.push(ApproveSIWEPage( - onSiweFinish: _oneSIWEFinish, - )); - } - } catch (e, s) { - _appKit.core.logger.d( - '[$runtimeType] _onNetworkChainRequireSIWE error: $e', - stackTrace: s, - ); - } - } - void _unregisterListeners() { + onModalError.unsubscribe(_onModalError); + // Magic _magicService.onMagicLoginSuccess.unsubscribe(_onMagicLoginEvent); _magicService.onMagicError.unsubscribe(_onMagicErrorEvent); @@ -1677,7 +1680,6 @@ class ReownAppKitModal with ChangeNotifier implements IReownAppKitModal { String? _getStoredChainId(String? defaultValue) { if (_storage.has(StorageConstants.selectedChainId)) { final storedChain = _storage.get(StorageConstants.selectedChainId); - debugPrint('storedChain $storedChain'); return storedChain?['chainId'] as String? ?? defaultValue; } return defaultValue; @@ -1688,14 +1690,17 @@ extension _EmailConnectorExtension on ReownAppKitModal { // Login event should be treated like Connect event for regular wallets Future _onMagicLoginEvent(MagicLoginEvent? args) async { if (args!.data != null) { - final newChainId = _getStoredChainId('${args.data!.chainId}')!; + _appKit.core.logger.d( + '[$runtimeType] _onMagicLoginEvent ${args.data?.toJson()}', + ); + final newChainId = _getStoredChainId(args.data!.chainId)!; _currentSelectedChainId = newChainId; // final email = args.data?.email ?? _currentSession?.toRawJson()['email']; final userName = args.data?.userName ?? _currentSession?.toRawJson()['userName']; final magicData = args.data?.copytWith( - chainId: int.tryParse(newChainId), + chainId: newChainId, email: email, userName: userName, ); @@ -1730,14 +1735,18 @@ extension _EmailConnectorExtension on ReownAppKitModal { Future _onMagicSessionUpdateEvent(MagicSessionEvent? args) async { if (args != null) { + _appKit.core.logger.d( + '[$runtimeType] _onMagicSessionUpdateEvent ${args.toJson()}', + ); try { final currentUsername = _currentSession?.userName; final currentEmail = _currentSession?.email; final newEmail = args.email ?? currentEmail ?? currentUsername; final newUsername = args.userName ?? currentUsername; final newProvider = args.provider ?? _currentSession?.socialProvider; - final newAddress = args.address ?? _currentSession!.address!; final newChainId = args.chainId?.toString() ?? _currentSession!.chainId; + final ns = ReownAppKitModalNetworks.getNamespaceForChainId(newChainId); + final newAddress = args.address ?? _currentSession!.getAddress(ns)!; _currentSelectedChainId = newChainId; // final magicData = MagicData( @@ -1745,7 +1754,7 @@ extension _EmailConnectorExtension on ReownAppKitModal { address: newAddress, userName: newUsername, provider: newProvider, - chainId: int.parse(newChainId), + chainId: newChainId, ); final session = (_currentSession != null) ? _currentSession!.copyWith( @@ -1795,6 +1804,7 @@ extension _EmailConnectorExtension on ReownAppKitModal { extension _CoinbaseConnectorExtension on ReownAppKitModal { void _onCoinbaseConnectEvent(CoinbaseConnectEvent? args) async { + _appKit.core.logger.d('[$runtimeType] _onCoinbaseConnectEvent: $args'); if (args?.data != null) { final newChainId = _getStoredChainId('${args!.data!.chainId}')!; _currentSelectedChainId = newChainId; @@ -1817,6 +1827,8 @@ extension _CoinbaseConnectorExtension on ReownAppKitModal { } void _onCoinbaseSessionUpdateEvent(CoinbaseSessionEvent? args) async { + _appKit.core.logger + .d('[$runtimeType] _onCoinbaseSessionUpdateEvent: $args'); if (args != null) { try { final chainId = args.chainId ?? _currentSession!.chainId; @@ -1848,6 +1860,7 @@ extension _CoinbaseConnectorExtension on ReownAppKitModal { } void _onCoinbaseErrorEvent(CoinbaseErrorEvent? args) async { + _appKit.core.logger.d('[$runtimeType] _onCoinbaseErrorEvent: $args'); final errorMessage = args?.error ?? 'Something went wrong'; if (!errorMessage.toLowerCase().contains('user denied')) { onModalError.broadcast(ModalError(errorMessage)); @@ -1858,7 +1871,9 @@ extension _CoinbaseConnectorExtension on ReownAppKitModal { extension _AppKitModalExtension on ReownAppKitModal { void _onSessionAuthResponse(SessionAuthResponse? args) async { final debugString = jsonEncode(args?.toJson()); - _logger.d('[$runtimeType] _onSessionAuthResponse: $debugString'); + _appKit.core.logger.d( + '[$runtimeType] _onSessionAuthResponse: $debugString', + ); if (args?.session != null) { // IF 1-CA SUPPORTED WE SHOULD CALL SIWECONGIF METHODS HERE final session = await _settleSession(args!.session!); @@ -1897,7 +1912,7 @@ extension _AppKitModalExtension on ReownAppKitModal { void _onSessionConnect(SessionConnect? args) async { final debugString = jsonEncode(args?.session.toJson()); - _logger.d('[$runtimeType] _onSessionConnect: $debugString'); + _appKit.core.logger.d('[$runtimeType] _onSessionConnect: $debugString'); final siweEnabled = siweService.instance!.enabled; if (_supportsOneClickAuth && siweEnabled) return; if (args != null) { @@ -1948,6 +1963,7 @@ extension _AppKitModalExtension on ReownAppKitModal { } void _oneSIWEFinish(ReownAppKitModalSession updatedSession) async { + _appKit.core.logger.d('[$runtimeType] _oneSIWEFinish $updatedSession'); await _storeSession(updatedSession); try { await _storage.set( @@ -1968,6 +1984,7 @@ extension _AppKitModalExtension on ReownAppKitModal { } void _onSessionEvent(SessionEvent? args) async { + _appKit.core.logger.d('[$runtimeType] _onSessionEvent $args'); onSessionEventEvent.broadcast(args); if (args?.name == EventsConstants.chainChanged) { _currentSelectedChainId = args?.data?.toString(); @@ -1988,6 +2005,7 @@ extension _AppKitModalExtension on ReownAppKitModal { } void _onSessionUpdate(SessionUpdate? args) async { + _appKit.core.logger.d('[$runtimeType] _onSessionUpdate $args'); if (args != null) { final wcSessions = _appKit.sessions.getAll(); if (wcSessions.isEmpty) return; diff --git a/packages/reown_appkit/lib/modal/constants/string_constants.dart b/packages/reown_appkit/lib/modal/constants/string_constants.dart index 1d4e286..38fd9b8 100644 --- a/packages/reown_appkit/lib/modal/constants/string_constants.dart +++ b/packages/reown_appkit/lib/modal/constants/string_constants.dart @@ -1,13 +1,15 @@ // ignore_for_file: public_member_api_docs import 'package:reown_appkit/reown_appkit.dart'; +import 'package:reown_core/version.dart' as reown_core; import 'package:reown_sign/version.dart' as reown_sign; class CoreConstants { // Request Headers static const X_SDK_TYPE = 'appkit'; - static const X_SDK_VERSION = packageVersion; - static const X_CORE_SDK_VERSION = 'flutter_${reown_sign.packageVersion}'; + static const X_SDK_VERSION = 'flutter-$packageVersion'; + static const X_CORE_SDK_VERSION = 'core-${reown_core.packageVersion}'; + static const X_SIGN_SDK_VERSION = 'sign-${reown_sign.packageVersion}'; } class UIConstants { diff --git a/packages/reown_appkit/lib/modal/models/public/appkit_modal_session.dart b/packages/reown_appkit/lib/modal/models/public/appkit_modal_session.dart index c455532..97e4371 100644 --- a/packages/reown_appkit/lib/modal/models/public/appkit_modal_session.dart +++ b/packages/reown_appkit/lib/modal/models/public/appkit_modal_session.dart @@ -1,5 +1,4 @@ import 'package:get_it/get_it.dart'; -import 'package:reown_appkit/modal/constants/string_constants.dart'; import 'package:reown_appkit/modal/services/coinbase_service/coinbase_service.dart'; import 'package:reown_appkit/modal/services/coinbase_service/models/coinbase_data.dart'; import 'package:reown_appkit/modal/services/magic_service/i_magic_service.dart'; @@ -97,7 +96,7 @@ class ReownAppKitModalSession { return true; } - final nsMethods = getApprovedMethods(namespace: 'eip155') ?? []; + final nsMethods = getApprovedMethods(namespace: NetworkUtils.eip155) ?? []; final supportsAddChain = nsMethods.contains( MethodsConstants.walletAddEthChain, ); @@ -115,7 +114,8 @@ class ReownAppKitModalSession { return CoinbaseService.supportedMethods; } if (sessionService.isMagic) { - return GetIt.I().supportedMethods; + final ns = namespace ?? NetworkUtils.eip155; + return GetIt.I().supportedMethods[ns]; } final sessionNamespaces = _sessionData!.namespaces; @@ -161,9 +161,23 @@ class ReownAppKitModalSession { if (sessionService.noSession) { return null; } - // We can not know which chains are approved from Coinbase or Magic - if (!sessionService.isWC) { - return [chainId]; + // Coinbase only support EIP155 but since we can not know which chains are actually approved... + // Magic only support EIP155 and Solana but since we can not know which chains are actually approved... + + final allEIP155 = ReownAppKitModalNetworks.getAllSupportedNetworks( + namespace: NetworkUtils.eip155, + ).map((e) => '${NetworkUtils.eip155}:${e.chainId}').toList(); + + if (sessionService.isCoinbase) { + return [...allEIP155]; + } + + final allSolana = ReownAppKitModalNetworks.getAllSupportedNetworks( + namespace: NetworkUtils.solana, + ).map((e) => '${NetworkUtils.solana}:${e.chainId}').toList(); + + if (sessionService.isMagic) { + return [...allEIP155, ...allSolana]; } final accounts = getAccounts(namespace: namespace) ?? []; @@ -176,11 +190,19 @@ class ReownAppKitModalSession { if (sessionService.noSession) { return null; } + if (sessionService.isCoinbase) { - return ['$namespace:$chainId:${getAddress(namespace ?? 'eip155')}']; + final ns = NetworkUtils.eip155; + return ReownAppKitModalNetworks.getAllSupportedNetworks(namespace: ns) + .map((e) => '$ns:${e.chainId}:${getAddress(ns)}') + .toList(); } + if (sessionService.isMagic) { - return ['$namespace:$chainId:${getAddress(namespace ?? 'eip155')}']; + final ns = namespace ?? NetworkUtils.eip155; + return ReownAppKitModalNetworks.getAllSupportedNetworks(namespace: ns) + .map((e) => '$ns:${e.chainId}:${getAddress(ns)}') + .toList(); } final sessionNamespaces = _sessionData!.namespaces; @@ -337,31 +359,41 @@ extension ReownAppKitModalSessionExtension on ReownAppKitModalSession { } Map? _namespaces() { - final eip155 = NetworkUtils.eip155; - // TODO check if namespaces are needed when connected to Coinbase or Magic if (sessionService.isCoinbase) { // Coinbase only supports eip155 chains + final eip155 = NetworkUtils.eip155; + final allEIP155 = ReownAppKitModalNetworks.getAllSupportedNetworks( + namespace: eip155, + ).map((e) => '$eip155:${e.chainId}').toList(); return { eip155: Namespace( - chains: ['$eip155:$chainId'], - accounts: ['$eip155:$chainId:${getAddress(eip155)}'], + chains: [...allEIP155], + accounts: [...getAccounts(namespace: eip155)!], methods: [...CoinbaseService.supportedMethods], // Coinbase does not have events as it doesn't use WC protocol events: [], ), }; } + if (sessionService.isMagic) { + final ns = ReownAppKitModalNetworks.getNamespaceForChainId( + _magicData!.chainId, + ); + final allChains = ReownAppKitModalNetworks.getAllSupportedNetworks( + namespace: ns, + ).map((e) => '$ns:${e.chainId}').toList(); return { - eip155: Namespace( - chains: ['$eip155:$chainId'], - accounts: ['$eip155:$chainId:${getAddress(eip155)}'], - methods: [...MagicService.supportedMethods], + ns: Namespace( + chains: [...allChains], + accounts: [...getAccounts(namespace: ns)!], + methods: [...NetworkUtils.defaultNetworkMethods[ns]!], // Magic does not have events as it doesn't use WC protocol events: [], ), }; } + return namespaces; } diff --git a/packages/reown_appkit/lib/modal/pages/account_page.dart b/packages/reown_appkit/lib/modal/pages/account_page.dart index 0247507..d1b4c32 100644 --- a/packages/reown_appkit/lib/modal/pages/account_page.dart +++ b/packages/reown_appkit/lib/modal/pages/account_page.dart @@ -208,9 +208,8 @@ class _EmailLoginButton extends StatelessWidget { final provider = AppKitSocialOption.values.firstWhereOrNull( (e) => e.name == service.session!.peer?.metadata.name, ); - final title = service.session!.email.isNotEmpty - ? service.session!.email - : service.session!.userName; + final title = + provider != null ? service.session!.userName : service.session!.email; return Column( children: [ const SizedBox.square(dimension: kPadding8), diff --git a/packages/reown_appkit/lib/modal/pages/confirm_email_page.dart b/packages/reown_appkit/lib/modal/pages/confirm_email_page.dart index e9de578..53ef12a 100644 --- a/packages/reown_appkit/lib/modal/pages/confirm_email_page.dart +++ b/packages/reown_appkit/lib/modal/pages/confirm_email_page.dart @@ -38,10 +38,6 @@ class _ConfirmEmailPageState extends State { } void _onMagicErrorEvent(MagicErrorEvent? event) { - toastService.instance.show(ToastMessage( - type: ToastType.error, - text: event?.error ?? 'Something went wrong.', - )); if (event is ConnectEmailErrorEvent) { _goBack(); } else { diff --git a/packages/reown_appkit/lib/modal/pages/connet_network_page.dart b/packages/reown_appkit/lib/modal/pages/connet_network_page.dart index 08bb8b7..e3bb0f1 100644 --- a/packages/reown_appkit/lib/modal/pages/connet_network_page.dart +++ b/packages/reown_appkit/lib/modal/pages/connet_network_page.dart @@ -1,9 +1,11 @@ import 'dart:async'; import 'package:flutter/material.dart'; +import 'package:get_it/get_it.dart'; import 'package:reown_appkit/modal/constants/key_constants.dart'; import 'package:reown_appkit/modal/services/explorer_service/explorer_service_singleton.dart'; +import 'package:reown_appkit/modal/services/magic_service/i_magic_service.dart'; import 'package:reown_appkit/modal/services/siwe_service/siwe_service_singleton.dart'; import 'package:reown_appkit/modal/i_appkit_modal_impl.dart'; import 'package:reown_appkit/modal/constants/style_constants.dart'; @@ -18,18 +20,21 @@ import 'package:reown_appkit/modal/widgets/navigation/navbar.dart'; import 'package:reown_appkit/reown_appkit.dart'; class ConnectNetworkPage extends StatefulWidget { - final ReownAppKitModalNetworkInfo chainInfo; const ConnectNetworkPage({ required this.chainInfo, + this.isMagic = false, }) : super(key: KeyConstants.connecNetworkPageKey); + final ReownAppKitModalNetworkInfo chainInfo; + final bool isMagic; + @override State createState() => _ConnectNetworkPageState(); } class _ConnectNetworkPageState extends State with WidgetsBindingObserver { - IReownAppKitModal? _service; + IReownAppKitModal? _appKitModal; ModalError? errorEvent; @override @@ -37,43 +42,52 @@ class _ConnectNetworkPageState extends State super.initState(); WidgetsBinding.instance.addObserver(this); WidgetsBinding.instance.addPostFrameCallback((_) { - _service = ModalProvider.of(context).instance; - _service?.onModalError.subscribe(_errorListener); + _appKitModal = ModalProvider.of(context).instance; + _appKitModal?.onModalError.subscribe(_errorListener); setState(() {}); Future.delayed(const Duration(milliseconds: 300), () => _connect()); }); } + IMagicService get _magicService => GetIt.I(); + void _connect() async { errorEvent = null; - _service!.launchConnectedWallet(); - try { - await _service!.requestSwitchToChain(widget.chainInfo); - final chainId = widget.chainInfo.chainId; - final namespace = ReownAppKitModalNetworks.getNamespaceForChainId( - chainId, - ); - final chainInfo = ReownAppKitModalNetworks.getNetworkById( - namespace, - chainId, + if (widget.isMagic) { + final newCaip2Chain = ReownAppKitModalNetworks.getCaip2Chain( + widget.chainInfo.chainId, ); - if (chainInfo != null) { - Future.delayed(const Duration(milliseconds: 300), () { - if (!siweService.instance!.enabled) { - widgetStack.instance.pop(); - } - }); + await _magicService.switchNetwork(chainId: newCaip2Chain); + } else { + _appKitModal!.launchConnectedWallet(); + try { + await _appKitModal!.requestSwitchToChain(widget.chainInfo); + final chainId = widget.chainInfo.chainId; + final namespace = ReownAppKitModalNetworks.getNamespaceForChainId( + chainId, + ); + final chainInfo = ReownAppKitModalNetworks.getNetworkById( + namespace, + chainId, + ); + if (chainInfo != null) { + Future.delayed(const Duration(milliseconds: 300), () { + if (!siweService.instance!.enabled) { + widgetStack.instance.pop(); + } + }); + } + } catch (e) { + setState(() {}); } - } catch (e) { - setState(() {}); } } @override void didChangeAppLifecycleState(AppLifecycleState state) { if (state == AppLifecycleState.resumed) { - if (_service?.session?.sessionService.isCoinbase == true) { - if (_service?.selectedChain?.chainId == widget.chainInfo.chainId) { + if (_appKitModal?.session?.sessionService.isCoinbase == true) { + if (_appKitModal?.selectedChain?.chainId == widget.chainInfo.chainId) { if (!siweService.instance!.enabled) { widgetStack.instance.pop(); } @@ -86,14 +100,14 @@ class _ConnectNetworkPageState extends State @override void dispose() { - _service?.onModalError.unsubscribe(_errorListener); + _appKitModal?.onModalError.unsubscribe(_errorListener); WidgetsBinding.instance.removeObserver(this); super.dispose(); } @override Widget build(BuildContext context) { - if (_service == null) { + if (_appKitModal == null) { return ContentLoading(); } final themeData = ReownAppKitModalTheme.getDataOf(context); @@ -147,7 +161,9 @@ class _ConnectNetworkPageState extends State ), ) : Text( - 'Continue in ${_service?.session?.peer?.metadata.name ?? 'wallet'}', + widget.isMagic + ? 'Switching to ${widget.chainInfo.name}' + : 'Continue in ${_appKitModal?.session?.peer?.metadata.name ?? 'wallet'}', textAlign: TextAlign.center, style: themeData.textStyles.paragraph500.copyWith( color: themeColors.foreground100, @@ -163,7 +179,9 @@ class _ConnectNetworkPageState extends State ), ) : Text( - 'Accept switch request in your wallet', + widget.isMagic + ? 'Wait until it\'s completed' + : 'Accept switch request in your wallet', textAlign: TextAlign.center, style: themeData.textStyles.small500.copyWith( color: themeColors.foreground200, diff --git a/packages/reown_appkit/lib/modal/pages/edit_email_page.dart b/packages/reown_appkit/lib/modal/pages/edit_email_page.dart index 8d54bea..4204001 100644 --- a/packages/reown_appkit/lib/modal/pages/edit_email_page.dart +++ b/packages/reown_appkit/lib/modal/pages/edit_email_page.dart @@ -7,8 +7,6 @@ import 'package:reown_appkit/modal/services/magic_service/models/email_login_ste import 'package:reown_appkit/modal/services/magic_service/models/magic_events.dart'; import 'package:reown_appkit/modal/constants/style_constants.dart'; import 'package:reown_appkit/modal/utils/core_utils.dart'; -import 'package:reown_appkit/modal/services/toast_service/models/toast_message.dart'; -import 'package:reown_appkit/modal/services/toast_service/toast_service_singleton.dart'; import 'package:reown_appkit/modal/widgets/buttons/primary_button.dart'; import 'package:reown_appkit/modal/widgets/buttons/secondary_button.dart'; import 'package:reown_appkit/modal/widgets/miscellaneous/content_loading.dart'; @@ -48,10 +46,6 @@ class _EditEmailPageState extends State { } void _onMagicErrorEvent(MagicErrorEvent? event) { - toastService.instance.show(ToastMessage( - type: ToastType.error, - text: event?.error ?? 'An error occurred.', - )); setState(() {}); } diff --git a/packages/reown_appkit/lib/modal/pages/farcaster_qrcode_page.dart b/packages/reown_appkit/lib/modal/pages/farcaster_qrcode_page.dart index 1e20b52..b59dde5 100644 --- a/packages/reown_appkit/lib/modal/pages/farcaster_qrcode_page.dart +++ b/packages/reown_appkit/lib/modal/pages/farcaster_qrcode_page.dart @@ -5,14 +5,10 @@ import 'package:reown_appkit/modal/widgets/widget_stack/widget_stack_singleton.d import 'package:reown_appkit/reown_appkit.dart'; import 'package:reown_appkit/modal/constants/key_constants.dart'; -import 'package:reown_appkit/modal/i_appkit_modal_impl.dart'; import 'package:reown_appkit/modal/constants/style_constants.dart'; import 'package:reown_appkit/modal/widgets/qr_code_view.dart'; import 'package:reown_appkit/modal/widgets/miscellaneous/responsive_container.dart'; -import 'package:reown_appkit/modal/widgets/modal_provider.dart'; import 'package:reown_appkit/modal/widgets/navigation/navbar.dart'; -import 'package:reown_appkit/modal/services/toast_service/models/toast_message.dart'; -import 'package:reown_appkit/modal/services/toast_service/toast_service_singleton.dart'; import 'package:shimmer/shimmer.dart'; class FarcasterQRCodePage extends StatefulWidget { @@ -28,15 +24,12 @@ class FarcasterQRCodePage extends StatefulWidget { } class _FarcasterQRCodePageState extends State { - IReownAppKitModal? _service; Widget? _qrQodeWidget; @override void initState() { super.initState(); WidgetsBinding.instance.addPostFrameCallback((_) async { - _service = ModalProvider.of(context).instance; - _service?.onModalError.subscribe(_onError); _qrQodeWidget = QRCodeView( uri: widget.farcasterUri, logoPath: 'lib/modal/assets/png/farcaster.png', @@ -54,22 +47,6 @@ class _FarcasterQRCodePageState extends State { }); } - void _onError(ModalError? args) { - final event = args ?? ModalError('An error occurred'); - toastService.instance.show( - ToastMessage( - type: ToastType.error, - text: event.message, - ), - ); - } - - @override - void dispose() async { - _service?.onModalError.unsubscribe(_onError); - super.dispose(); - } - @override Widget build(BuildContext context) { final themeData = ReownAppKitModalTheme.getDataOf(context); diff --git a/packages/reown_appkit/lib/modal/pages/public/appkit_modal_qrcode_page.dart b/packages/reown_appkit/lib/modal/pages/public/appkit_modal_qrcode_page.dart index 5ddf1ca..4f6a3b7 100644 --- a/packages/reown_appkit/lib/modal/pages/public/appkit_modal_qrcode_page.dart +++ b/packages/reown_appkit/lib/modal/pages/public/appkit_modal_qrcode_page.dart @@ -23,7 +23,7 @@ class ReownAppKitModalQRCodePage extends StatefulWidget { } class _AppKitModalQRCodePageState extends State { - IReownAppKitModal? _service; + IReownAppKitModal? _appKitModal; Widget? _qrQodeWidget; // @@ -31,46 +31,34 @@ class _AppKitModalQRCodePageState extends State { void initState() { super.initState(); WidgetsBinding.instance.addPostFrameCallback((_) async { - _service = ModalProvider.of(context).instance; - _service!.addListener(_buildWidget); - _service!.appKit!.core.pairing.onPairingExpire.subscribe( + _appKitModal = ModalProvider.of(context).instance; + _appKitModal!.addListener(_buildWidget); + _appKitModal!.appKit!.core.pairing.onPairingExpire.subscribe( _onPairingExpire, ); - _service?.onModalError.subscribe(_onError); - await _service!.buildConnectionUri(); + await _appKitModal!.buildConnectionUri(); }); } void _buildWidget() => setState(() { _qrQodeWidget = QRCodeView( - uri: _service!.wcUri!, + uri: _appKitModal!.wcUri!, logoPath: 'lib/modal/assets/png/logo_wc.png', ); }); void _onPairingExpire(EventArgs? args) async { - await _service!.buildConnectionUri(); + await _appKitModal!.buildConnectionUri(); setState(() {}); } - void _onError(ModalError? args) { - final event = args ?? ModalError('An error occurred'); - toastService.instance.show( - ToastMessage( - type: ToastType.error, - text: event.message, - ), - ); - } - @override void dispose() async { - _service?.onModalError.unsubscribe(_onError); - _service!.appKit!.core.pairing.onPairingExpire.unsubscribe( + _appKitModal!.appKit!.core.pairing.onPairingExpire.unsubscribe( _onPairingExpire, ); - _service!.removeListener(_buildWidget); - _service!.expirePreviousInactivePairings(); + _appKitModal!.removeListener(_buildWidget); + _appKitModal!.expirePreviousInactivePairings(); super.dispose(); } diff --git a/packages/reown_appkit/lib/modal/pages/public/appkit_modal_select_network_page.dart b/packages/reown_appkit/lib/modal/pages/public/appkit_modal_select_network_page.dart index eb71547..95c989e 100644 --- a/packages/reown_appkit/lib/modal/pages/public/appkit_modal_select_network_page.dart +++ b/packages/reown_appkit/lib/modal/pages/public/appkit_modal_select_network_page.dart @@ -37,6 +37,7 @@ class ReownAppKitModalSelectNetworkPage extends StatelessWidget { final approvedChains = appKitModal.session!.getApprovedChains( namespace: namespace, ); + final isMagic = appKitModal.session!.sessionService.isMagic; final isChainApproved = (approvedChains ?? []).contains(caip2Chain); if (chainInfo.chainId == appKitModal.selectedChain?.chainId) { if (widgetStack.instance.canPop()) { @@ -44,13 +45,19 @@ class ReownAppKitModalSelectNetworkPage extends StatelessWidget { } else { appKitModal.closeModal(); } - } else if (isChainApproved || - appKitModal.session!.sessionService.isMagic) { - await appKitModal.selectChain(chainInfo, switchChain: true); - if (widgetStack.instance.canPop()) { - widgetStack.instance.pop(); + } else if (isChainApproved || isMagic) { + if (isMagic) { + widgetStack.instance.push(ConnectNetworkPage( + chainInfo: chainInfo, + isMagic: true, + )); } else { - appKitModal.closeModal(); + await appKitModal.selectChain(chainInfo, switchChain: true); + if (widgetStack.instance.canPop()) { + widgetStack.instance.pop(); + } else { + appKitModal.closeModal(); + } } } else { widgetStack.instance.push(ConnectNetworkPage(chainInfo: chainInfo)); diff --git a/packages/reown_appkit/lib/modal/pages/social_login_page.dart b/packages/reown_appkit/lib/modal/pages/social_login_page.dart index c89cdf1..1550475 100644 --- a/packages/reown_appkit/lib/modal/pages/social_login_page.dart +++ b/packages/reown_appkit/lib/modal/pages/social_login_page.dart @@ -7,7 +7,6 @@ import 'package:flutter_svg/flutter_svg.dart'; import 'package:get_it/get_it.dart'; import 'package:reown_appkit/modal/constants/key_constants.dart'; import 'package:reown_appkit/modal/constants/string_constants.dart'; - import 'package:reown_appkit/modal/i_appkit_modal_impl.dart'; import 'package:reown_appkit/modal/constants/style_constants.dart'; import 'package:reown_appkit/modal/pages/farcaster_qrcode_page.dart'; @@ -15,8 +14,6 @@ import 'package:reown_appkit/modal/services/analytics_service/analytics_service_ import 'package:reown_appkit/modal/services/analytics_service/models/analytics_event.dart'; import 'package:reown_appkit/modal/services/magic_service/i_magic_service.dart'; import 'package:reown_appkit/modal/services/magic_service/models/magic_events.dart'; -import 'package:reown_appkit/modal/services/toast_service/models/toast_message.dart'; -import 'package:reown_appkit/modal/services/toast_service/toast_service_singleton.dart'; import 'package:reown_appkit/modal/utils/asset_util.dart'; import 'package:reown_appkit/modal/utils/platform_utils.dart'; import 'package:reown_appkit/modal/widgets/buttons/simple_icon_button.dart'; @@ -76,10 +73,6 @@ class _SocialLoginPageState extends State { } void _errorListener(ModalError? event) { - toastService.instance.show(ToastMessage( - type: ToastType.error, - text: event?.message ?? 'Something went wrong.', - )); setState(() => errorEvent = event); } @@ -186,7 +179,10 @@ class _SocialLoginPageState extends State { uri: '?${Uri.parse(url).query}', ); if (success == true) { - await _magicService.getUser(); + final caip2Chain = ReownAppKitModalNetworks.getCaip2Chain( + _service?.selectedChain?.chainId ?? '1', + ); + await _magicService.getUser(chainId: caip2Chain); analyticsService.instance.sendEvent(SocialLoginSuccess( provider: widget.socialOption.name.toLowerCase(), )); @@ -218,7 +214,10 @@ class _SocialLoginPageState extends State { _cancelSocialLogin(); } else { setState(() => _retrievingData = true); - await _magicService.getUser(); + final caip2Chain = ReownAppKitModalNetworks.getCaip2Chain( + _service?.selectedChain?.chainId ?? '1', + ); + await _magicService.getUser(chainId: caip2Chain); } } @@ -476,6 +475,15 @@ class __WebViewLoginWidgetState extends State<_WebViewLoginWidget> { asset: 'lib/modal/assets/icons/close.svg', action: widget.onCancel, ), + leftAction: NavbarActionButton( + asset: 'lib/modal/assets/icons/disconnect.svg', + action: () async { + await _clearCookies(); + await _webViewController.clearCache(); + await _webViewController.clearLocalStorage(); + await _webViewController.reload(); + }, + ), body: WebViewWidget(controller: _webViewController), ); } diff --git a/packages/reown_appkit/lib/modal/services/blockchain_service/blockchain_service.dart b/packages/reown_appkit/lib/modal/services/blockchain_service/blockchain_service.dart index f75a974..f5d8598 100644 --- a/packages/reown_appkit/lib/modal/services/blockchain_service/blockchain_service.dart +++ b/packages/reown_appkit/lib/modal/services/blockchain_service/blockchain_service.dart @@ -22,7 +22,7 @@ class BlockChainService implements IBlockChainService { Map get _requiredHeaders => { 'x-sdk-type': CoreConstants.X_SDK_TYPE, - 'x-sdk-version': 'flutter-${CoreConstants.X_SDK_VERSION}', + 'x-sdk-version': CoreConstants.X_SDK_VERSION, }; @override diff --git a/packages/reown_appkit/lib/modal/services/magic_service/i_magic_service.dart b/packages/reown_appkit/lib/modal/services/magic_service/i_magic_service.dart index 51656f1..c333f1c 100644 --- a/packages/reown_appkit/lib/modal/services/magic_service/i_magic_service.dart +++ b/packages/reown_appkit/lib/modal/services/magic_service/i_magic_service.dart @@ -5,7 +5,7 @@ import 'package:reown_appkit/reown_appkit.dart'; import 'package:webview_flutter/webview_flutter.dart'; abstract class IMagicService { - List get supportedMethods; + Map> get supportedMethods; List get socials; ValueNotifier get isReady; @@ -19,7 +19,7 @@ abstract class IMagicService { WebViewWidget get webview; - Future init(); + Future init({String? chainId}); void setEmail(String value); void setNewEmail(String value); @@ -40,10 +40,10 @@ abstract class IMagicService { Future updateEmailPrimaryOtp({required String otp}); Future updateEmailSecondaryOtp({required String otp}); Future connectOtp({required String otp}); - Future getChainId(); + Future getChainId(); Future syncTheme(ReownAppKitModalTheme? theme); - Future getUser({String? chainId}); - Future switchNetwork({required String chainId}); + Future getUser({required String? chainId}); + Future switchNetwork({required String chainId}); Future request({ String? chainId, required SessionRequestParams request, diff --git a/packages/reown_appkit/lib/modal/services/magic_service/magic_service.dart b/packages/reown_appkit/lib/modal/services/magic_service/magic_service.dart index af8ecdf..b3ee5fa 100644 --- a/packages/reown_appkit/lib/modal/services/magic_service/magic_service.dart +++ b/packages/reown_appkit/lib/modal/services/magic_service/magic_service.dart @@ -4,6 +4,7 @@ import 'dart:io'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; +import 'package:reown_appkit/modal/utils/core_utils.dart'; import 'package:reown_appkit/reown_appkit.dart'; import 'package:reown_appkit/modal/constants/string_constants.dart'; @@ -33,10 +34,12 @@ class MagicService implements IMagicService { // TODO export this ConnectionMetadata get _peerMetadata => ConnectionMetadata( metadata: PairingMetadata( - name: 'Email Wallet', + name: _socialProvider != null + ? '$_socialProvider Wallet' + : 'Email Wallet', description: '', url: '', - icons: [''], // TODO set the icon here depending on the login type + icons: [''], ), publicKey: '', ); @@ -47,26 +50,26 @@ class MagicService implements IMagicService { int _onLoadCount = 0; String _packageName = ''; AppKitSocialOption? _socialProvider; + String? _socialUsername; - late Completer _initialized; - late Completer _connected; - late Completer _response; - late Completer _disconnect; late final IReownCore _core; late final PairingMetadata _metadata; late final FeaturesConfig _features; late final WebViewController _webViewController; late final WebViewWidget _webview; - // Logger get _logger => _core.logger; @override - final List supportedMethods = [ - 'personal_sign', - 'eth_sendTransaction', - 'eth_accounts', - 'eth_sendRawTransaction', - 'eth_signTypedData_v4', - ]; + final Map> supportedMethods = { + NetworkUtils.eip155: [ + 'personal_sign', + 'eth_sendTransaction', + 'eth_accounts', + 'eth_sendRawTransaction', + 'eth_signTypedData_v4', + ], + NetworkUtils.solana: + NetworkUtils.defaultNetworkMethods[NetworkUtils.solana]!, + }; @override WebViewWidget get webview => _webview; @@ -136,28 +139,24 @@ class MagicService implements IMagicService { } } + Completer _initializedCompleter = Completer(); @override - Future init() async { + Future init({String? chainId}) async { + _connectionChainId = chainId; + _initializedCompleter = Completer(); if (!isEmailEnabled.value && !isSocialEnabled.value) { - _initialized = Completer(); - _initialized.complete(false); - _connected = Completer(); - _connected.complete(false); + _initializedCompleter.complete(false); + _isConnectedCompleter.complete(false); return; } _packageName = await ReownCoreUtils.getPackageName(); await _init(); - await _initialized.future; await _isConnected(); - await _connected.future; + await _syncAppData(); isReady.value = true; - _syncDappData(); - return; } - Future _init() async { - _initialized = Completer(); - + Future _init() async { await _webViewController.setBackgroundColor(Colors.transparent); await _webViewController.setJavaScriptMode(JavaScriptMode.unrestricted); await _webViewController.enableZoom(false); @@ -168,13 +167,20 @@ class MagicService implements IMagicService { await _webViewController.setNavigationDelegate( NavigationDelegate( onNavigationRequest: (NavigationRequest request) async { - if (_isAllowedDomain(request.url)) { - await _fitToScreen(); - return NavigationDecision.navigate; - } - if (isReady.value) { - ReownCoreUtils.openURL(request.url); - } + try { + final uri = Uri.parse(request.url); + if (uri.host.isEmpty || uri.authority.isEmpty) { + return NavigationDecision.prevent; + } + if (_isAllowedDomain(uri.toString())) { + await _fitToScreen(); + return NavigationDecision.navigate; + } + if (isReady.value) { + ReownCoreUtils.openURL(uri.toString()); + return NavigationDecision.prevent; + } + } catch (_) {} return NavigationDecision.prevent; }, onWebResourceError: _onWebResourceError, @@ -188,14 +194,35 @@ class MagicService implements IMagicService { await _runJavascript(); await _fitToScreen(); Future.delayed(Duration(milliseconds: 600)).then((value) { - if (_initialized.isCompleted) return; - _initialized.complete(true); + if (_initializedCompleter.isCompleted) return; + _initializedCompleter.complete(true); }); }, ), ); await _setDebugMode(); await _loadRequest(); + return await _initializedCompleter.future; + } + + Completer _isConnectedCompleter = Completer(); + Future _isConnected() async { + _isConnectedCompleter = Completer(); + final message = IsConnected().toString(); + await _webViewController.runJavaScript('sendMessage($message)'); + return await _isConnectedCompleter.future; + } + + Completer _syncAppDataCompleter = Completer(); + Future _syncAppData() async { + _syncAppDataCompleter = Completer(); + final message = SyncAppData( + metadata: _metadata, + projectId: _core.projectId, + sdkVersion: CoreUtils.getUserAgent(), + ).toString(); + await _webViewController.runJavaScript('sendMessage($message)'); + return await _syncAppDataCompleter.future; } @override @@ -229,7 +256,9 @@ class MagicService implements IMagicService { String? schema, String? chainId, }) async { - if (_socialsNotReady) return null; + if (_socialsNotReady) { + throw Exception('Service is not ready'); + } // _getSocialRedirectUri = Completer(); _connectionChainId = chainId ?? _connectionChainId; @@ -245,7 +274,9 @@ class MagicService implements IMagicService { Completer _connectSocial = Completer(); @override Future connectSocial({required String uri}) async { - if (_socialsNotReady) return null; + if (_socialsNotReady) { + throw Exception('Service is not ready'); + } // _connectSocial = Completer(); final message = ConnectSocial(uri: uri).toString(); @@ -261,7 +292,9 @@ class MagicService implements IMagicService { Completer _getFarcasterUri = Completer(); @override Future getFarcasterUri({String? chainId}) async { - if (_socialsNotReady) return null; + if (_socialsNotReady) { + throw Exception('Service is not ready'); + } if (_getFarcasterUri.isCompleted) { return await _getFarcasterUri.future; } @@ -277,7 +310,9 @@ class MagicService implements IMagicService { Completer _connectFarcaster = Completer(); @override Future awaitFarcasterResponse() async { - if (_socialsNotReady) return false; + if (_socialsNotReady) { + throw Exception('Service is not ready'); + } // _connectFarcaster = Completer(); // final message = ConnectFarcaster().toString(); @@ -289,7 +324,9 @@ class MagicService implements IMagicService { @override Future connectEmail({required String value, String? chainId}) async { - if (_emailNotReady) return; + if (_emailNotReady) { + throw Exception('Service is not ready'); + } // _socialProvider = null; _connectionChainId = chainId ?? _connectionChainId; @@ -299,7 +336,9 @@ class MagicService implements IMagicService { @override Future updateEmail({required String value}) async { - if (_emailNotReady) return; + if (_emailNotReady) { + throw Exception('Service is not ready'); + } // step.value = EmailLoginStep.loading; final message = UpdateEmail(email: value).toString(); @@ -308,7 +347,9 @@ class MagicService implements IMagicService { @override Future updateEmailPrimaryOtp({required String otp}) async { - if (_emailNotReady) return; + if (_emailNotReady) { + throw Exception('Service is not ready'); + } // step.value = EmailLoginStep.loading; final message = UpdateEmailPrimaryOtp(otp: otp).toString(); @@ -317,7 +358,9 @@ class MagicService implements IMagicService { @override Future updateEmailSecondaryOtp({required String otp}) async { - if (_emailNotReady) return; + if (_emailNotReady) { + throw Exception('Service is not ready'); + } // step.value = EmailLoginStep.loading; final message = UpdateEmailSecondaryOtp(otp: otp).toString(); @@ -326,7 +369,9 @@ class MagicService implements IMagicService { @override Future connectOtp({required String otp}) async { - if (_emailNotReady) return; + if (_emailNotReady) { + throw Exception('Service is not ready'); + } // step.value = EmailLoginStep.loading; final message = ConnectOtp(otp: otp).toString(); @@ -337,36 +382,37 @@ class MagicService implements IMagicService { @override Future syncTheme(ReownAppKitModalTheme? theme) async { - if (_serviceNotReady) return; + if (_serviceNotReady) { + throw Exception('Service is not ready'); + } // final message = SyncTheme(theme: theme).toString(); await _webViewController.runJavaScript('sendMessage($message)'); } - void _syncDappData() async { - if (_serviceNotReady) return; - // - final message = SyncAppData( - metadata: _metadata, - projectId: _core.projectId, - sdkVersion: 'flutter-${CoreConstants.X_SDK_VERSION}', - ).toString(); - await _webViewController.runJavaScript('sendMessage($message)'); - } - + Completer _getChainIdCompleter = Completer(); @override - Future getChainId() async { - if (_serviceNotReady) return; + Future getChainId() async { + if (_serviceNotReady) { + throw Exception('Service is not ready'); + } // + _getChainIdCompleter = Completer(); final message = GetChainId().toString(); await _webViewController.runJavaScript('sendMessage($message)'); + return await _getChainIdCompleter.future; } + Completer _getUserCompleter = Completer(); @override - Future getUser({String? chainId}) async { - if (_serviceNotReady) return; + Future getUser({required String? chainId}) async { + if (_serviceNotReady) { + throw Exception('Service is not ready'); + } // - return await _getUser(chainId); + _getUserCompleter = Completer(); + await _getUser(chainId); + return await _getUserCompleter.future; } Future _getUser(String? chainId) async { @@ -374,30 +420,61 @@ class MagicService implements IMagicService { return await _webViewController.runJavaScript('sendMessage($message)'); } + Completer _switchNetworkCompleter = Completer(); @override - Future switchNetwork({required String chainId}) async { - if (_serviceNotReady) return; + Future switchNetwork({required String chainId}) async { + if (_serviceNotReady) { + throw Exception('Service is not ready'); + } // + await _awaitReadyness.future; + _switchNetworkCompleter = Completer(); + await _switchNetwork(chainId); + return await _switchNetworkCompleter.future; + } + + Future _switchNetwork(String chainId) async { + if (!isConnected.value) { + _isConnectedCompleter = Completer(); + if (_socialProvider != null) { + onMagicLoginRequest.broadcast(MagicSessionEvent( + provider: _socialProvider, + )); + } else { + onMagicLoginRequest.broadcast(MagicSessionEvent( + email: email.value, + )); + await connectEmail(value: email.value); + } + final success = await _isConnectedCompleter.future; + if (!success) return; + } final message = SwitchNetwork(chainId: chainId).toString(); await _webViewController.runJavaScript('sendMessage($message)'); } + Completer _requestCompleter = Completer(); @override Future request({ String? chainId, required SessionRequestParams request, }) async { - if (_serviceNotReady) return; + if (_serviceNotReady) { + throw Exception('Service is not ready'); + } // await _awaitReadyness.future; + _requestCompleter = Completer(); + // if (chainId != _connectionChainId && chainId != null) { + // await switchNetwork(chainId: chainId); + // } await _rpcRequest(request.toJson()); - return await _response.future; + return await _requestCompleter.future; } Future _rpcRequest(Map parameters) async { - _response = Completer(); if (!isConnected.value) { - _connected = Completer(); + _isConnectedCompleter = Completer(); if (_socialProvider != null) { onMagicLoginRequest.broadcast(MagicSessionEvent( provider: _socialProvider, @@ -408,29 +485,32 @@ class MagicService implements IMagicService { )); await connectEmail(value: email.value); } - final success = await _connected.future; + final success = await _isConnectedCompleter.future; if (!success) return; } onMagicRpcRequest.broadcast(MagicRequestEvent(request: parameters)); final method = parameters['method']; - final params = parameters['params'] as List; + final params = parameters['params']; final message = RpcRequest(method: method, params: params).toString(); await _webViewController.runJavaScript('sendMessage($message)'); } + Completer _disconnectCompleter = Completer(); @override Future disconnect() async { - if (_serviceNotReady) return false; + if (_serviceNotReady) { + throw Exception('Service is not ready'); + } // - _disconnect = Completer(); + _disconnectCompleter = Completer(); if (!isConnected.value) { _resetTimeOut(); - _disconnect.complete(true); - return (await _disconnect.future); + _disconnectCompleter.complete(true); + return true; } final message = SignOut().toString(); await _webViewController.runJavaScript('sendMessage($message)'); - return (await _disconnect.future); + return await _disconnectCompleter.future; } // ****** Private Methods ******* // @@ -446,26 +526,17 @@ class MagicService implements IMagicService { final queryParams = { 'projectId': _core.projectId, 'bundleId': _packageName, + if (_connectionChainId != null) 'chainId': _connectionChainId, }; - await _webViewController.loadRequest( - uri.replace(queryParameters: queryParams), - headers: headers, - ); + final requestUri = uri.replace(queryParameters: queryParams); + await _webViewController.loadRequest(requestUri, headers: headers); // in case connection message or even the request itself hangs there's no other way to continue the flow than timing it out. _timeOutTimer ??= Timer.periodic(Duration(seconds: 1), _timeOut); } catch (e) { - _initialized.complete(false); + _initializedCompleter.complete(false); } } - Future _isConnected() async { - _connected = Completer(); - final message = IsConnected().toString(); - await _webViewController.runJavaScript('sendMessage($message)'); - } - - String? _socialUsername; - void _onFrameMessage(JavaScriptMessage jsMessage) async { if (Platform.isAndroid) { _core.logger.d('[$runtimeType] JS Console: $jsMessage'); @@ -475,232 +546,272 @@ class MagicService implements IMagicService { if (!frameMessage.isValidOrigin || !frameMessage.isValidData) { return; } - final messageData = frameMessage.data!; - if (messageData.syncDataSuccess) { - _resetTimeOut(); - } - // ****** IS_CONNECTED - if (messageData.isConnectSuccess) { - _resetTimeOut(); - isConnected.value = messageData.getPayloadMapKey('isConnected'); - if (!_connected.isCompleted) { - _connected.complete(isConnected.value); - } - onMagicConnect.broadcast(MagicConnectEvent(isConnected.value)); - if (isConnected.value) { - await _getUser(_connectionChainId); - } - } - if (messageData.getSocialRedirectUriSuccess) { - final uri = messageData.getPayloadMapKey('uri'); - _getSocialRedirectUri.complete(uri); - } - // ****** CONNECT_SOCIAL_SUCCESS - if (messageData.connectSocialSuccess) { - _socialUsername = messageData.getPayloadMapKey('userName'); - debugPrint('[$runtimeType] connectSocialSuccess $_socialUsername'); - _connectSocial.complete(true); - } - // ****** GET_FARCASTER_URI_SUCCESS - if (messageData.getFarcasterUriSuccess) { - final url = messageData.getPayloadMapKey('url'); - _getFarcasterUri.complete(url); - } - // ****** CONNECT_FARCASTER_SUCCESS - if (messageData.connectFarcasterSuccess) { - _socialUsername = messageData.getPayloadMapKey('userName'); - debugPrint('[$runtimeType] connectFarcasterSuccess $_socialUsername'); - _connectFarcaster.complete(true); - } - // ****** CONNECT_EMAIL - if (messageData.connectEmailSuccess) { - if (step.value != EmailLoginStep.loading) { - final action = messageData.getPayloadMapKey('action'); - final value = action.toString().toUpperCase(); - final newStep = EmailLoginStep.fromAction(value); - if (newStep == EmailLoginStep.verifyOtp) { - if (step.value == EmailLoginStep.verifyDevice) { - analyticsService.instance.sendEvent(DeviceRegisteredForEmail()); - } - analyticsService.instance.sendEvent(EmailVerificationCodeSent()); - } - step.value = newStep; - } + _core.logger.i('[$runtimeType] ${frameMessage.data!.toRawJson()}'); + _successMessageHandler(frameMessage.data!); // await? + _errorMessageHandler(frameMessage.data!); + } catch (e, s) { + _core.logger.d('[$runtimeType] $jsMessage $e', stackTrace: s); + } + } + + Future _successMessageHandler(MessageData messageData) async { + if (messageData.syncDataSuccess) { + _resetTimeOut(); + _syncAppDataCompleter.complete(true); + } + // ****** IS_CONNECTED_SUCCESS + if (messageData.isConnectSuccess) { + _resetTimeOut(); + isConnected.value = messageData.getPayloadMapKey('isConnected'); + if (!_isConnectedCompleter.isCompleted) { + _isConnectedCompleter.complete(isConnected.value); } - // ****** CONNECT_OTP - if (messageData.connectOtpSuccess) { - analyticsService.instance.sendEvent(EmailVerificationCodePass()); - step.value = EmailLoginStep.idle; + onMagicConnect.broadcast(MagicConnectEvent(isConnected.value)); + if (isConnected.value) { await _getUser(_connectionChainId); } - // ****** UPDAET_EMAIL - if (messageData.updateEmailSuccess) { + } + if (messageData.getSocialRedirectUriSuccess) { + final uri = messageData.getPayloadMapKey('uri'); + _getSocialRedirectUri.complete(uri); + } + // ****** CONNECT_SOCIAL_SUCCESS + if (messageData.connectSocialSuccess) { + _socialUsername = messageData.getPayloadMapKey('userName'); + _connectSocial.complete(true); + } + // ****** GET_FARCASTER_URI_SUCCESS + if (messageData.getFarcasterUriSuccess) { + final url = messageData.getPayloadMapKey('url'); + _getFarcasterUri.complete(url); + } + // ****** CONNECT_FARCASTER_SUCCESS + if (messageData.connectFarcasterSuccess) { + _socialUsername = messageData.getPayloadMapKey('userName'); + _connectFarcaster.complete(true); + } + // ****** CONNECT_EMAIL + if (messageData.connectEmailSuccess) { + if (step.value != EmailLoginStep.loading) { final action = messageData.getPayloadMapKey('action'); - if (action == 'VERIFY_SECONDARY_OTP') { - step.value = EmailLoginStep.verifyOtp2; - } else { - step.value = EmailLoginStep.verifyOtp; + final value = action.toString().toUpperCase(); + final newStep = EmailLoginStep.fromAction(value); + if (newStep == EmailLoginStep.verifyOtp) { + if (step.value == EmailLoginStep.verifyDevice) { + analyticsService.instance.sendEvent(DeviceRegisteredForEmail()); + } + analyticsService.instance.sendEvent(EmailVerificationCodeSent()); } - analyticsService.instance.sendEvent(EmailEdit()); + step.value = newStep; } - // ****** UPDATE_EMAIL_PRIMARY_OTP - if (messageData.updateEmailPrimarySuccess) { + } + // ****** CONNECT_OTP + if (messageData.connectOtpSuccess) { + analyticsService.instance.sendEvent(EmailVerificationCodePass()); + step.value = EmailLoginStep.idle; + await _getUser(_connectionChainId); + } + // ****** UPDAET_EMAIL + if (messageData.updateEmailSuccess) { + final action = messageData.getPayloadMapKey('action'); + if (action == 'VERIFY_SECONDARY_OTP') { step.value = EmailLoginStep.verifyOtp2; + } else { + step.value = EmailLoginStep.verifyOtp; } - // ****** UPDATE_EMAIL_SECONDARY_OTP - if (messageData.updateEmailSecondarySuccess) { - analyticsService.instance.sendEvent(EmailEditComplete()); - step.value = EmailLoginStep.idle; - setEmail(newEmail.value); - setNewEmail(''); - await _getUser(_connectionChainId); - } - // ****** SWITCH_NETWORK - if (messageData.switchNetworkSuccess) { - final chainId = messageData.getPayloadMapKey('chainId'); - onMagicUpdate.broadcast(MagicSessionEvent(chainId: chainId)); - } - // ****** GET_CHAIN_ID - if (messageData.getChainIdSuccess) { - final chainId = messageData.getPayloadMapKey('chainId'); - onMagicUpdate.broadcast(MagicSessionEvent(chainId: chainId)); + analyticsService.instance.sendEvent(EmailEdit()); + } + // ****** UPDATE_EMAIL_PRIMARY_OTP + if (messageData.updateEmailPrimarySuccess) { + step.value = EmailLoginStep.verifyOtp2; + } + // ****** UPDATE_EMAIL_SECONDARY_OTP + if (messageData.updateEmailSecondarySuccess) { + analyticsService.instance.sendEvent(EmailEditComplete()); + step.value = EmailLoginStep.idle; + setEmail(newEmail.value); + setNewEmail(''); + await _getUser(_connectionChainId); + } + // ****** SWITCH_NETWORK_SUCCESS + if (messageData.switchNetworkSuccess) { + final chainId = messageData.getPayloadMapKey('chainId'); + if (chainId is String && chainId.contains(':')) { + final cid = chainId.split(':').last; + onMagicUpdate.broadcast(MagicSessionEvent(chainId: cid.toString())); + _connectionChainId = cid.toString(); + } else { + onMagicUpdate.broadcast(MagicSessionEvent( + chainId: chainId?.toString(), + )); _connectionChainId = chainId?.toString(); } - // ****** RPC_REQUEST - if (messageData.rpcRequestSuccess) { - final hash = messageData.payload as String?; - _response.complete(hash); - onMagicRpcRequest.broadcast( - MagicRequestEvent( - request: null, - result: hash, - success: true, - ), - ); + final newCaip2Chain = ReownAppKitModalNetworks.getCaip2Chain( + _connectionChainId ?? '1', + ); + final success = await getUser(chainId: newCaip2Chain); + _switchNetworkCompleter.complete(success); + } + // ****** GET_CHAIN_ID + if (messageData.getChainIdSuccess) { + final chainId = messageData.getPayloadMapKey('chainId'); + if (chainId is String && chainId.contains(':')) { + final cid = chainId.split(':').last; + _connectionChainId = cid.toString(); + } else { + _connectionChainId = chainId?.toString(); } - // ****** GET_USER - if (messageData.getUserSuccess) { - isConnected.value = true; - debugPrint('[$runtimeType] getUserSuccess ${messageData.payload}'); - final magicData = MagicData.fromJson(messageData.payload!).copytWith( - userName: _socialUsername, + _getChainIdCompleter.complete(_connectionChainId); + } + // ****** RPC_REQUEST_SUCCESS + if (messageData.rpcRequestSuccess) { + final hash = messageData.payload; + _requestCompleter.complete(hash); + onMagicRpcRequest.broadcast( + MagicRequestEvent( + request: null, + result: hash, + success: true, + ), + ); + } + // ****** GET_USER_SUCCESS + if (messageData.getUserSuccess) { + isConnected.value = true; + final magicData = MagicData.fromJson(messageData.payload!).copytWith( + userName: _socialUsername, + ); + if (!_isConnectedCompleter.isCompleted) { + final event = MagicSessionEvent( + email: magicData.email, + userName: magicData.userName, + address: magicData.address, + chainId: magicData.chainId, + provider: magicData.provider, ); - _socialUsername = null; - if (!_connected.isCompleted) { - final event = MagicSessionEvent( - email: magicData.email, - userName: magicData.userName, - address: magicData.address, - chainId: magicData.chainId, - provider: magicData.provider, - ); - onMagicUpdate.broadcast(event); - _connected.complete(isConnected.value); - } else { - final session = magicData.copytWith( - peer: _peerMetadata.copyWith( - metadata: _peerMetadata.metadata.copyWith( - name: _socialProvider?.name ?? 'Email Wallet', - ), + onMagicUpdate.broadcast(event); + _isConnectedCompleter.complete(isConnected.value); + } else { + final session = magicData.copytWith( + peer: _peerMetadata.copyWith( + metadata: _peerMetadata.metadata.copyWith( + name: _socialProvider?.name ?? 'Email Wallet', ), - self: _selfMetadata, - provider: _socialProvider, - ); - onMagicLoginSuccess.broadcast(MagicLoginEvent(session)); - } - } - // ****** SIGN_OUT_SUCCESS - if (messageData.signOutSuccess) { - _resetTimeOut(); - _disconnect.complete(true); - } - if (messageData.sessionUpdate) { - // onMagicUpdate.broadcast(MagicSessionEvent(...)); - } - if (messageData.isConnectError) { - _error(IsConnectedErrorEvent()); - } - if (messageData.connectEmailError) { - String? message = messageData.getPayloadMapKey('message'); - if (message?.toLowerCase() == 'invalid params') { - message = 'Wrong email format'; - } - _error(ConnectEmailErrorEvent(message: message)); - } - if (messageData.updateEmailError) { - final message = messageData.getPayloadMapKey('message'); - _error(UpdateEmailErrorEvent(message: message)); - } - if (messageData.updateEmailPrimaryOtpError) { - final message = messageData.getPayloadMapKey('message'); - _error(UpdateEmailPrimaryOtpErrorEvent(message: message)); - } - if (messageData.updateEmailSecondaryOtpError) { - final message = messageData.getPayloadMapKey('message'); - _error(UpdateEmailSecondaryOtpErrorEvent(message: message)); - } - if (messageData.connectOtpError) { - analyticsService.instance.sendEvent(EmailVerificationCodeFail()); - final message = messageData.getPayloadMapKey('message'); - _error(ConnectOtpErrorEvent(message: message)); - } - if (messageData.getSocialRedirectUriError) { - String? message = messageData.getPayloadMapKey('message'); - message = message?.replaceFirst( - 'Error: Magic RPC Error: [-32600] ', - '', - ); - _error(MagicErrorEvent(message)); - _getSocialRedirectUri.complete(null); - } - if (messageData.connectSocialError) { - String? message = messageData.getPayloadMapKey('message'); - message = message?.replaceFirst( - 'Error: Magic RPC Error: [-32600] ', - '', - ); - _error(MagicErrorEvent(message)); - _connectSocial.complete(false); - } - if (messageData.getFarcasterUriError) { - String? message = messageData.getPayloadMapKey('message'); - message = message?.replaceFirst( - 'Error: Magic RPC Error: [-32600] ', - '', - ); - _error(MagicErrorEvent(message)); - _getFarcasterUri.complete(null); - } - if (messageData.connectFarcasterError) { - String? message = messageData.getPayloadMapKey('message'); - message = message?.replaceFirst( - 'Error: Magic RPC Error: [-32600] ', - '', + ), + self: _selfMetadata, + provider: _socialProvider, ); - _error(MagicErrorEvent(message)); - _connectFarcaster.complete(false); - } - if (messageData.getUserError) { - _error(GetUserErrorEvent()); - } - if (messageData.switchNetworkError) { - _error(SwitchNetworkErrorEvent()); + onMagicLoginSuccess.broadcast(MagicLoginEvent(session)); } - if (messageData.rpcRequestError) { - final message = messageData.getPayloadMapKey('message'); - _error(RpcRequestErrorEvent(message)); - } - if (messageData.signOutError) { - _error(SignOutErrorEvent()); + _getUserCompleter.complete(true); + } + // ****** SIGN_OUT_SUCCESS + if (messageData.signOutSuccess) { + _socialUsername = null; + _resetTimeOut(); + _disconnectCompleter.complete(true); + } + } + + void _errorMessageHandler(MessageData messageData) { + if (messageData.syncDataError) { + _resetTimeOut(); + _syncAppDataCompleter.complete(false); + } + if (messageData.isConnectError) { + _error(IsConnectedErrorEvent()); + } + if (messageData.connectEmailError) { + String? message = messageData.getPayloadMapKey('message'); + if (message?.toLowerCase() == 'invalid params') { + message = 'Wrong email format'; } - } catch (e, s) { - _core.logger.d('[$runtimeType] $jsMessage $e', stackTrace: s); + _error(ConnectEmailErrorEvent(message: message)); + } + if (messageData.updateEmailError) { + final message = messageData.getPayloadMapKey('message'); + _error(UpdateEmailErrorEvent(message: message)); + } + if (messageData.updateEmailPrimaryOtpError) { + final message = messageData.getPayloadMapKey('message'); + _error(UpdateEmailPrimaryOtpErrorEvent(message: message)); + } + if (messageData.updateEmailSecondaryOtpError) { + final message = messageData.getPayloadMapKey('message'); + _error(UpdateEmailSecondaryOtpErrorEvent(message: message)); + } + if (messageData.connectOtpError) { + analyticsService.instance.sendEvent(EmailVerificationCodeFail()); + final message = messageData.getPayloadMapKey('message'); + _error(ConnectOtpErrorEvent(message: message)); + } + if (messageData.getSocialRedirectUriError) { + String? message = messageData.getPayloadMapKey('message'); + message = message?.replaceFirst( + 'Error: Magic RPC Error: [-32600] ', + '', + ); + _error(MagicErrorEvent(message)); + _getSocialRedirectUri.complete(null); + } + if (messageData.connectSocialError) { + String? message = messageData.getPayloadMapKey('message'); + message = message?.replaceFirst( + 'Error: Magic RPC Error: [-32600] ', + '', + ); + _error(MagicErrorEvent(message)); + _connectSocial.complete(false); + } + if (messageData.getFarcasterUriError) { + String? message = messageData.getPayloadMapKey('message'); + message = message?.replaceFirst( + 'Error: Magic RPC Error: [-32600] ', + '', + ); + _error(MagicErrorEvent(message)); + _getFarcasterUri.complete(null); + } + if (messageData.connectFarcasterError) { + String? message = messageData.getPayloadMapKey('message'); + message = message?.replaceFirst( + 'Error: Magic RPC Error: [-32600] ', + '', + ); + _error(MagicErrorEvent(message)); + _connectFarcaster.complete(false); + } + // GET_USER_ERROR + if (messageData.getUserError) { + // final message = messageData.getPayloadMapKey('message'); + _error(GetUserErrorEvent()); + _getUserCompleter.complete(false); + } + if (messageData.switchNetworkError) { + final message = messageData.getPayloadMapKey('message'); + _error(SwitchNetworkErrorEvent(message: message)); + _switchNetworkCompleter.complete(false); + } + if (messageData.getChainIdError) { + final message = messageData.getPayloadMapKey('message'); + _error(SwitchNetworkErrorEvent(message: message)); + _getChainIdCompleter.complete(null); + } + if (messageData.rpcRequestError) { + final message = messageData.getPayloadMapKey('message'); + _error(RpcRequestErrorEvent(message)); + } + if (messageData.signOutError) { + _error(SignOutErrorEvent()); } } void _error(MagicErrorEvent errorEvent) { if (errorEvent is RpcRequestErrorEvent) { - _response.completeError(JsonRpcError(code: 0, message: errorEvent.error)); + _requestCompleter.completeError(JsonRpcError( + code: 0, + message: errorEvent.error, + )); onMagicRpcRequest.broadcast( MagicRequestEvent( request: null, @@ -735,10 +846,10 @@ class MagicService implements IMagicService { } if (errorEvent is SignOutErrorEvent) { isConnected.value = true; - _disconnect.complete(false); + _disconnectCompleter.complete(false); } - if (!_connected.isCompleted) { - _connected.complete(isConnected.value); + if (!_isConnectedCompleter.isCompleted) { + _isConnectedCompleter.complete(isConnected.value); } onMagicError.broadcast(errorEvent); } @@ -759,20 +870,19 @@ class MagicService implements IMagicService { Future _runJavascript() async { return await _webViewController.runJavaScript(''' window.addEventListener('message', ({ data, origin }) => { - console.log('[MagicService] received <=== ' + JSON.stringify({data,origin})) window.w3mWebview.postMessage(JSON.stringify({data,origin})) }) const sendMessage = async (message) => { const iframeFL = document.getElementById('frame-mobile-sdk') - console.log('[MagicService] posted =====> ' + JSON.stringify(message)) + console.log('postMessage(' + JSON.stringify(message) + ')') iframeFL.contentWindow.postMessage(message, '*') } '''); } void _onDebugConsoleReceived(JavaScriptConsoleMessage message) { - _core.logger.d('[$runtimeType] JS Console: ${message.message}'); + _core.logger.i('[$runtimeType] ${message.message}'); } void _onWebResourceError(WebResourceError error) { diff --git a/packages/reown_appkit/lib/modal/services/magic_service/models/frame_message.dart b/packages/reown_appkit/lib/modal/services/magic_service/models/frame_message.dart index 50a67db..b30be2d 100644 --- a/packages/reown_appkit/lib/modal/services/magic_service/models/frame_message.dart +++ b/packages/reown_appkit/lib/modal/services/magic_service/models/frame_message.dart @@ -69,7 +69,7 @@ class MessageData { Map toJson() => { 'type': type, - 'payload': payload?.toJson(), + if (payload != null) 'payload': payload, }; T getPayloadMapKey(String key) { @@ -79,7 +79,9 @@ class MessageData { // @w3m-frame events bool get syncThemeSuccess => type == '@w3m-frame/SYNC_THEME_SUCCESS'; + bool get syncThemeError => type == '@w3m-frame/SYNC_THEME_ERROR'; bool get syncDataSuccess => type == '@w3m-frame/SYNC_DAPP_DATA_SUCCESS'; + bool get syncDataError => type == '@w3m-frame/SYNC_DAPP_DATA_ERROR'; bool get getSocialRedirectUriSuccess => type == '@w3m-frame/GET_SOCIAL_REDIRECT_URI_SUCCESS'; bool get getSocialRedirectUriError => @@ -235,7 +237,7 @@ class GetUser extends MessageData { @override String toString() { if ((chainId ?? '').isNotEmpty) { - return '{type:"${super.type}",payload:{chainId:$chainId}}'; + return '{type:"${super.type}",payload:{chainId:"$chainId"}}'; } return '{type:"${super.type}"}'; } @@ -257,7 +259,7 @@ class GetChainId extends MessageData { class RpcRequest extends MessageData { final String method; - final List params; + final dynamic params; RpcRequest({ required this.method, @@ -268,6 +270,14 @@ class RpcRequest extends MessageData { String toString() { final m = 'method:"$method"'; final t = 'type:"${super.type}"'; + + if (params is Map) { + List ps = + (params as Map).entries.map((e) => '${e.key}:"${e.value}"').toList(); + final pString = ps.join(','); + return '{$t,payload:{$m,params:{$pString}}}'; + } + final p = params.map((i) => '$i').toList(); if (method == 'personal_sign') { diff --git a/packages/reown_appkit/lib/modal/services/magic_service/models/magic_data.dart b/packages/reown_appkit/lib/modal/services/magic_service/models/magic_data.dart index 8acd35d..c2d858d 100644 --- a/packages/reown_appkit/lib/modal/services/magic_service/models/magic_data.dart +++ b/packages/reown_appkit/lib/modal/services/magic_service/models/magic_data.dart @@ -3,7 +3,7 @@ import 'package:reown_appkit/reown_appkit.dart'; class MagicData { String? email; String address; - int chainId; + String chainId; String? userName; bool? smartAccountDeployed; String? preferredAccountType; @@ -27,7 +27,7 @@ class MagicData { return MagicData( email: json['email']?.toString(), address: json['address'].toString(), - chainId: int.parse(json['chainId'].toString()), + chainId: _parseChainId(json['chainId'].toString()), userName: json['userName']?.toString(), smartAccountDeployed: json['smartAccountDeployed'] as bool?, preferredAccountType: json['preferredAccountType']?.toString(), @@ -43,6 +43,14 @@ class MagicData { ); } + static String _parseChainId(String value) { + String chainId = value.toString(); + if (chainId.contains(':')) { + return chainId.split(':').last; + } + return chainId; + } + Map toJson() { return { 'email': email, @@ -63,7 +71,7 @@ class MagicData { MagicData copytWith({ String? email, String? address, - int? chainId, + String? chainId, String? userName, bool? smartAccountDeployed, String? preferredAccountType, @@ -71,6 +79,9 @@ class MagicData { ConnectionMetadata? peer, AppKitSocialOption? provider, }) { + if (chainId != null) { + chainId = _parseChainId(chainId); + } return MagicData( email: email ?? this.email, address: address ?? this.address, diff --git a/packages/reown_appkit/lib/modal/utils/core_utils.dart b/packages/reown_appkit/lib/modal/utils/core_utils.dart index 0673849..a7c1453 100644 --- a/packages/reown_appkit/lib/modal/utils/core_utils.dart +++ b/packages/reown_appkit/lib/modal/utils/core_utils.dart @@ -94,11 +94,11 @@ class CoreUtils { } static String getUserAgent() { - String userAgent = '${CoreConstants.X_SDK_TYPE}' - '-flutter-' + String userAgent = '${CoreConstants.X_SDK_TYPE}/' '${CoreConstants.X_SDK_VERSION}/' '${CoreConstants.X_CORE_SDK_VERSION}/' '${ReownCoreUtils.getOS()}'; + // return userAgent; } @@ -110,7 +110,7 @@ class CoreUtils { return { 'x-project-id': projectId, 'x-sdk-type': CoreConstants.X_SDK_TYPE, - 'x-sdk-version': 'flutter-${CoreConstants.X_SDK_VERSION}', + 'x-sdk-version': CoreConstants.X_SDK_VERSION, 'user-agent': getUserAgent(), if (referer != null) 'referer': referer, if (origin != null) 'origin': origin, diff --git a/packages/reown_appkit/lib/modal/widgets/miscellaneous/input_email.dart b/packages/reown_appkit/lib/modal/widgets/miscellaneous/input_email.dart index 016e9a9..d1cf1e0 100644 --- a/packages/reown_appkit/lib/modal/widgets/miscellaneous/input_email.dart +++ b/packages/reown_appkit/lib/modal/widgets/miscellaneous/input_email.dart @@ -4,8 +4,6 @@ import 'package:get_it/get_it.dart'; import 'package:reown_appkit/modal/constants/style_constants.dart'; import 'package:reown_appkit/modal/services/magic_service/i_magic_service.dart'; import 'package:reown_appkit/modal/services/magic_service/models/magic_events.dart'; -import 'package:reown_appkit/modal/services/toast_service/models/toast_message.dart'; -import 'package:reown_appkit/modal/services/toast_service/toast_service_singleton.dart'; import 'package:reown_appkit/modal/utils/core_utils.dart'; import 'package:reown_appkit/modal/widgets/circular_loader.dart'; import 'package:reown_appkit/modal/widgets/miscellaneous/searchbar.dart'; @@ -51,6 +49,7 @@ class _InputEmailWidgetState extends State { } void _updateStatus() { + if (!mounted) return; setState(() { _ready = _magicService.isReady.value; _timedOut = _magicService.isTimeout.value; @@ -58,10 +57,7 @@ class _InputEmailWidgetState extends State { } void _onMagicErrorEvent(MagicErrorEvent? event) { - toastService.instance.show(ToastMessage( - type: ToastType.error, - text: event?.error ?? 'An error occurred.', - )); + if (!mounted) return; _submitted = false; setState(() {}); } diff --git a/packages/reown_appkit/lib/modal/widgets/value_listenable_builders/network_service_items_listener.dart b/packages/reown_appkit/lib/modal/widgets/value_listenable_builders/network_service_items_listener.dart index 18ee35d..6ec0714 100644 --- a/packages/reown_appkit/lib/modal/widgets/value_listenable_builders/network_service_items_listener.dart +++ b/packages/reown_appkit/lib/modal/widgets/value_listenable_builders/network_service_items_listener.dart @@ -3,6 +3,7 @@ import 'package:reown_appkit/modal/models/grid_item.dart'; import 'package:reown_appkit/modal/models/public/appkit_network_info.dart'; import 'package:reown_appkit/modal/services/network_service/network_service_singleton.dart'; +import 'package:reown_appkit/modal/utils/public/appkit_modal_default_networks.dart'; import 'package:reown_appkit/modal/widgets/modal_provider.dart'; class NetworkServiceItemsListener extends StatelessWidget { @@ -49,8 +50,11 @@ extension on List> { }); } return map((item) { + final caip2Chain = ReownAppKitModalNetworks.getCaip2Chain( + item.data.chainId, + ); return item.copyWith( - disabled: !supportedChains.contains(item.data.chainId), + disabled: !supportedChains.contains(caip2Chain), ); }).toList() ..sort((a, b) { From 13668df20ea239b139310c983723d1b34d613f15 Mon Sep 17 00:00:00 2001 From: Alfreedom <00tango.bromine@icloud.com> Date: Wed, 16 Oct 2024 13:50:41 +0200 Subject: [PATCH 04/17] Change every service singleton to use GetIt --- .../example/modal/lib/home_page.dart | 33 +-- .../modal/lib/widgets/logger_widget.dart | 25 +- .../lib/modal/appkit_modal_impl.dart | 255 +++++++++--------- .../lib/modal/pages/account_page.dart | 5 +- .../lib/modal/pages/approve_siwe.dart | 26 +- .../lib/modal/pages/confirm_email_page.dart | 6 +- .../lib/modal/pages/connect_wallet_page.dart | 23 +- .../lib/modal/pages/connet_network_page.dart | 12 +- .../public/appkit_modal_all_wallets_page.dart | 12 +- .../appkit_modal_main_wallets_page.dart | 5 +- .../public/appkit_modal_qrcode_page.dart | 10 +- .../lib/modal/pages/social_login_page.dart | 45 ++-- .../lib/modal/pages/upgrade_wallet_page.dart | 5 +- .../analytics_service_singleton.dart | 7 - .../blockchain_service_singleton.dart | 7 - .../i_blockchain_service.dart | 6 - .../coinbase_service/coinbase_service.dart | 11 +- .../coinbase_service_singleton.dart | 7 - .../explorer_service/explorer_service.dart | 11 +- .../explorer_service_singleton.dart | 7 - .../magic_service/i_magic_service.dart | 6 +- .../services/magic_service/magic_service.dart | 21 +- .../network_service/network_service.dart | 7 +- .../network_service_singleton.dart | 7 - .../services/siwe_service/siwe_service.dart | 5 +- .../siwe_service/siwe_service_singleton.dart | 7 - .../toast_service_singleton.dart | 10 - .../uri_service/url_utils_singleton.dart | 7 - .../utils/public/appkit_modal_siwe_utils.dart | 7 +- .../modal/widgets/avatars/wallet_avatar.dart | 5 +- .../widgets/buttons/address_copy_button.dart | 5 +- .../buttons/email_login_input_field.dart | 7 +- .../modal/widgets/buttons/network_button.dart | 10 +- .../lib/modal/widgets/icons/rounded_icon.dart | 5 +- .../miscellaneous/all_wallets_header.dart | 7 +- .../miscellaneous/verify_otp_view.dart | 7 +- .../lib/modal/widgets/navigation/navbar.dart | 8 +- .../navigation/navbar_action_button.dart | 6 +- .../public/appkit_modal_account_button.dart | 4 +- .../public/appkit_modal_balance_button.dart | 5 +- .../appkit_modal_network_select_button.dart | 5 +- .../lib/modal/widgets/toast/toast.dart | 5 +- .../modal/widgets/toast/toast_presenter.dart | 5 +- .../explorer_service_items_listener.dart | 12 +- .../network_service_items_listener.dart | 8 +- .../widgets/widget_stack/widget_stack.dart | 5 +- 46 files changed, 350 insertions(+), 354 deletions(-) delete mode 100644 packages/reown_appkit/lib/modal/services/analytics_service/analytics_service_singleton.dart delete mode 100644 packages/reown_appkit/lib/modal/services/blockchain_service/blockchain_service_singleton.dart delete mode 100644 packages/reown_appkit/lib/modal/services/coinbase_service/coinbase_service_singleton.dart delete mode 100644 packages/reown_appkit/lib/modal/services/explorer_service/explorer_service_singleton.dart delete mode 100644 packages/reown_appkit/lib/modal/services/network_service/network_service_singleton.dart delete mode 100644 packages/reown_appkit/lib/modal/services/siwe_service/siwe_service_singleton.dart delete mode 100644 packages/reown_appkit/lib/modal/services/toast_service/toast_service_singleton.dart delete mode 100644 packages/reown_appkit/lib/modal/services/uri_service/url_utils_singleton.dart diff --git a/packages/reown_appkit/example/modal/lib/home_page.dart b/packages/reown_appkit/example/modal/lib/home_page.dart index 410a880..620bca8 100644 --- a/packages/reown_appkit/example/modal/lib/home_page.dart +++ b/packages/reown_appkit/example/modal/lib/home_page.dart @@ -7,9 +7,7 @@ import 'package:flutter/material.dart'; import 'package:reown_appkit_example/services/deep_link_handler.dart'; import 'package:shared_preferences/shared_preferences.dart'; -import 'package:reown_appkit/modal/services/blockchain_service/blockchain_service_singleton.dart'; import 'package:reown_appkit/reown_appkit.dart'; - import 'package:reown_appkit_example/widgets/debug_drawer.dart'; import 'package:reown_appkit_example/utils/constants.dart'; import 'package:reown_appkit_example/services/siwe_service.dart'; @@ -36,7 +34,7 @@ class MyHomePage extends StatefulWidget { } class _MyHomePageState extends State { - final overlay = OverlayController(const Duration(milliseconds: 200)); + late OverlayController overlay; late ReownAppKitModal _appKitModal; late SIWESampleWebService _siweTestService; bool _initialized = false; @@ -46,7 +44,7 @@ class _MyHomePageState extends State { super.initState(); _siweTestService = SIWESampleWebService(); WidgetsBinding.instance.addPostFrameCallback((_) { - _toggleOverlay(); + // _toggleOverlay(); _initializeService(widget.prefs); }); } @@ -251,7 +249,7 @@ class _MyHomePageState extends State { AppKitSocialOption.Apple, AppKitSocialOption.Discord, ], - showMainWallets: true, // OPTIONAL - true by default + showMainWallets: false, // OPTIONAL - true by default ), // requiredNamespaces: {}, // optionalNamespaces: {}, @@ -266,23 +264,9 @@ class _MyHomePageState extends State { }, // excludedWalletIds: {}, // MORE WALLETS https://explorer.walletconnect.com/?type=wallet&chains=eip155%3A1 - getBalance: () async { - try { - final chainId = _appKitModal.selectedChain!.chainId; - final namespace = ReownAppKitModalNetworks.getNamespaceForChainId( - _appKitModal.selectedChain!.chainId, - ); - final address = _appKitModal.session!.getAddress(namespace)!; - return await blockchainService.instance.getBalance( - address: address, - namespace: namespace, - chainId: chainId, - ); - } catch (e) { - debugPrint('[$runtimeType] getBalance $e'); - return 0.0; - } - }, + // getBalance: () async { + // // Your own balance function + // }, optionalNamespaces: { 'eip155': RequiredNamespace.fromJson({ 'chains': ReownAppKitModalNetworks.getAllSupportedNetworks( @@ -311,6 +295,11 @@ class _MyHomePageState extends State { }), }, ); + overlay = OverlayController( + const Duration(milliseconds: 200), + appKitModal: _appKitModal, + ); + _toggleOverlay(); setState(() => _initialized = true); } on ReownAppKitModalException catch (e) { debugPrint('⛔️ ${e.message}'); diff --git a/packages/reown_appkit/example/modal/lib/widgets/logger_widget.dart b/packages/reown_appkit/example/modal/lib/widgets/logger_widget.dart index 8bb0855..cee506e 100644 --- a/packages/reown_appkit/example/modal/lib/widgets/logger_widget.dart +++ b/packages/reown_appkit/example/modal/lib/widgets/logger_widget.dart @@ -1,9 +1,10 @@ import 'package:flutter/material.dart'; import 'package:flutter/scheduler.dart'; -import 'package:reown_appkit/modal/services/analytics_service/analytics_service_singleton.dart'; +import 'package:reown_appkit/reown_appkit.dart'; class DraggableCard extends StatefulWidget { final OverlayController overlayController; + // const DraggableCard({ super.key, required this.overlayController, @@ -19,14 +20,19 @@ class _DraggableCardState extends State { @override void initState() { super.initState(); - analyticsService.instance.events.listen(_eventsListener); + widget.overlayController.appKitModal.appKit!.core.addLogListener( + _eventsListener, + ); } void _eventsListener(event) { if (!mounted) return; + if (!event.message.toString().contains('[AnalyticsService]')) return; + String message = '${event.message}'.replaceAll('[AnalyticsService] ', ''); + message = message.replaceAll('send event 202: ', ''); _logs.add( Text( - '=> $event', + '=> $message', style: const TextStyle( color: Colors.white, fontSize: 12.0, @@ -38,6 +44,9 @@ class _DraggableCardState extends State { @override void dispose() { + widget.overlayController.appKitModal.appKit!.core.removeLogListener( + _eventsListener, + ); widget.overlayController.remove(); super.dispose(); } @@ -55,12 +64,12 @@ class _DraggableCardState extends State { children: [ SizedBox( width: MediaQuery.of(context).size.width, - height: 200.0, + height: 300.0, child: Row( children: [ Expanded( child: SizedBox( - height: 200.0, + height: 300.0, child: ListView( reverse: true, padding: const EdgeInsets.all(6.0), @@ -99,7 +108,11 @@ class _DraggableCardState extends State { } class OverlayController extends AnimatedOverlay { - OverlayController(super.duration); + OverlayController( + super.duration, { + required this.appKitModal, + }); + final ReownAppKitModal appKitModal; OverlayEntry? _entry; final _defaultAlign = const Alignment(0.0, -30.0); Alignment align = const Alignment(0.0, -30.0); diff --git a/packages/reown_appkit/lib/modal/appkit_modal_impl.dart b/packages/reown_appkit/lib/modal/appkit_modal_impl.dart index 2b50754..8f6648f 100644 --- a/packages/reown_appkit/lib/modal/appkit_modal_impl.dart +++ b/packages/reown_appkit/lib/modal/appkit_modal_impl.dart @@ -5,15 +5,22 @@ import 'dart:math'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:get_it/get_it.dart'; -import 'package:reown_appkit/modal/services/magic_service/i_magic_service.dart'; -import 'package:reown_appkit/modal/services/toast_service/models/toast_message.dart'; -import 'package:reown_core/store/i_store.dart'; +import 'package:reown_appkit/modal/services/analytics_service/i_analytics_service.dart'; +import 'package:reown_appkit/modal/services/explorer_service/i_explorer_service.dart'; +import 'package:reown_appkit/modal/services/network_service/i_network_service.dart'; +import 'package:reown_appkit/modal/services/siwe_service/i_siwe_service.dart'; +import 'package:reown_appkit/modal/services/toast_service/i_toast_service.dart'; +import 'package:reown_appkit/modal/services/toast_service/toast_service.dart'; +import 'package:reown_appkit/modal/services/uri_service/i_url_utils.dart'; +import 'package:reown_core/store/i_store.dart'; import 'package:reown_appkit/reown_appkit.dart'; +import 'package:reown_appkit/modal/services/blockchain_service/i_blockchain_service.dart'; +import 'package:reown_appkit/modal/services/magic_service/i_magic_service.dart'; +import 'package:reown_appkit/modal/services/toast_service/models/toast_message.dart'; import 'package:reown_appkit/modal/services/network_service/network_service.dart'; import 'package:reown_appkit/modal/services/uri_service/launch_url_exception.dart'; import 'package:reown_appkit/modal/services/uri_service/url_utils.dart'; -import 'package:reown_appkit/modal/services/uri_service/url_utils_singleton.dart'; import 'package:reown_appkit/modal/utils/core_utils.dart'; import 'package:reown_appkit/modal/utils/platform_utils.dart'; import 'package:reown_appkit/modal/constants/key_constants.dart'; @@ -22,29 +29,22 @@ import 'package:reown_appkit/modal/pages/account_page.dart'; import 'package:reown_appkit/modal/pages/approve_magic_request_page.dart'; import 'package:reown_appkit/modal/pages/approve_siwe.dart'; import 'package:reown_appkit/modal/services/analytics_service/analytics_service.dart'; -import 'package:reown_appkit/modal/services/analytics_service/analytics_service_singleton.dart'; import 'package:reown_appkit/modal/services/analytics_service/models/analytics_event.dart'; import 'package:reown_appkit/modal/services/coinbase_service/coinbase_service.dart'; -import 'package:reown_appkit/modal/services/coinbase_service/coinbase_service_singleton.dart'; import 'package:reown_appkit/modal/services/coinbase_service/i_coinbase_service.dart'; import 'package:reown_appkit/modal/services/coinbase_service/models/coinbase_data.dart'; import 'package:reown_appkit/modal/services/coinbase_service/models/coinbase_events.dart'; import 'package:reown_appkit/modal/services/explorer_service/explorer_service.dart'; -import 'package:reown_appkit/modal/services/explorer_service/explorer_service_singleton.dart'; import 'package:reown_appkit/modal/services/explorer_service/models/redirect.dart'; import 'package:reown_appkit/modal/services/magic_service/magic_service.dart'; import 'package:reown_appkit/modal/services/magic_service/models/magic_data.dart'; import 'package:reown_appkit/modal/services/magic_service/models/magic_events.dart'; import 'package:reown_appkit/modal/services/siwe_service/siwe_service.dart'; -import 'package:reown_appkit/modal/services/siwe_service/siwe_service_singleton.dart'; import 'package:reown_appkit/modal/widgets/widget_stack/widget_stack_singleton.dart'; import 'package:reown_appkit/modal/services/blockchain_service/blockchain_service.dart'; -import 'package:reown_appkit/modal/services/blockchain_service/blockchain_service_singleton.dart'; -import 'package:reown_appkit/modal/services/network_service/network_service_singleton.dart'; import 'package:reown_appkit/modal/i_appkit_modal_impl.dart'; import 'package:reown_appkit/modal/widgets/modal_container.dart'; import 'package:reown_appkit/modal/widgets/modal_provider.dart'; -import 'package:reown_appkit/modal/services/toast_service/toast_service_singleton.dart'; /// Either a [projectId] and [metadata] must be provided or an already created [appKit]. /// optionalNamespaces is mostly not needed, if you use it, the values set here will override every optionalNamespaces set in evey chain @@ -182,33 +182,36 @@ class ReownAppKitModal with ChangeNotifier implements IReownAppKitModal { _setRequiredNamespaces(requiredNamespaces); _setOptionalNamespaces(optionalNamespaces); - uriService.instance = UriService( - core: _appKit.core, + GetIt.I.registerSingleton( + UriService( + core: _appKit.core, + ), ); - - analyticsService.instance = AnalyticsService( - core: _appKit.core, - enableAnalytics: enableAnalytics, - )..init().then((_) { - analyticsService.instance.sendEvent(ModalLoadedEvent()); - }); - - explorerService.instance = ExplorerService( - core: _appKit.core, - referer: _appKit.metadata.name.replaceAll(' ', ''), - featuredWalletIds: featuredWalletIds, - includedWalletIds: includedWalletIds, - excludedWalletIds: excludedWalletIds, - namespaces: { - ..._requiredNamespaces, - ..._optionalNamespaces, - }, + GetIt.I.registerSingletonAsync(() async { + final analyticsService = AnalyticsService( + core: _appKit.core, + enableAnalytics: enableAnalytics, + ); + await analyticsService.init(); + analyticsService.sendEvent(ModalLoadedEvent()); + return analyticsService; + }); + GetIt.I.registerSingleton( + ExplorerService( + core: _appKit.core, + referer: _appKit.metadata.name.replaceAll(' ', ''), + featuredWalletIds: featuredWalletIds, + includedWalletIds: includedWalletIds, + excludedWalletIds: excludedWalletIds, + namespaces: {..._requiredNamespaces, ..._optionalNamespaces}, + ), ); - - networkService.instance = NetworkService(); - - blockchainService.instance = BlockChainService( - core: _appKit.core, + GetIt.I.registerSingleton(NetworkService()); + GetIt.I.registerSingleton(ToastService()); + GetIt.I.registerSingleton( + BlockChainService( + core: _appKit.core, + ), ); GetIt.I.registerSingletonIfAbsent( @@ -218,24 +221,31 @@ class ReownAppKitModal with ChangeNotifier implements IReownAppKitModal { featuresConfig: this.featuresConfig, ), ); - - coinbaseService.instance = CoinbaseService( - core: _appKit.core, - metadata: _appKit.metadata, - enabled: _initializeCoinbaseSDK, + GetIt.I.registerSingleton( + CoinbaseService( + core: _appKit.core, + metadata: _appKit.metadata, + enabled: _initializeCoinbaseSDK, + ), ); - - siweService.instance = SiweService( - appKit: _appKit, - siweConfig: siweConfig, - namespaces: { - ..._requiredNamespaces, - ..._optionalNamespaces, - }, + GetIt.I.registerSingleton( + SiweService( + appKit: _appKit, + siweConfig: siweConfig, + namespaces: {..._requiredNamespaces, ..._optionalNamespaces}, + ), ); } + IUriService get _uriService => GetIt.I(); + IToastService get _toastService => GetIt.I(); + IAnalyticsService get _analyticsService => GetIt.I(); + IExplorerService get _explorerService => GetIt.I(); + INetworkService get _networkService => GetIt.I(); IMagicService get _magicService => GetIt.I(); + IBlockChainService get _blockchainService => GetIt.I(); + ICoinbaseService get _coinbaseService => GetIt.I(); + ISiweService get _siweService => GetIt.I(); ////////* PUBLIC METHODS *///////// @@ -277,10 +287,10 @@ class ReownAppKitModal with ChangeNotifier implements IReownAppKitModal { _registerListeners(); await _appKit.init(); - await networkService.instance.init(); - await explorerService.instance.init(); - await coinbaseService.instance.init(); - await blockchainService.instance.init(); + await _networkService.init(); + await _explorerService.init(); + await _coinbaseService.init(); + await _blockchainService.init(); _currentSession = await _getStoredSession(); _currentSelectedChainId = _getStoredChainId(null); @@ -336,7 +346,7 @@ class ReownAppKitModal with ChangeNotifier implements IReownAppKitModal { // Check for other type of sessions stored if (_currentSession != null) { if (_currentSession!.sessionService.isCoinbase) { - final isCbConnected = await coinbaseService.instance.isConnected(); + final isCbConnected = await _coinbaseService.isConnected(); if (!isCbConnected) { await _cleanSession(); } @@ -375,11 +385,11 @@ class ReownAppKitModal with ChangeNotifier implements IReownAppKitModal { Future _checkSIWEStatus() async { // check if SIWE is enabled and should still sign the message - if (siweService.instance!.enabled) { + if (_siweService.enabled) { try { // If getSession() throws it means message has not been signed and // we should present modal with ApproveSIWEPage - final siweSession = await siweService.instance!.getSession(); + final siweSession = await _siweService.getSession(); final session = _currentSession!.copyWith(siweSession: siweSession); await _setSesionAndChainData(session); _notify(); @@ -570,7 +580,7 @@ class ReownAppKitModal with ChangeNotifier implements IReownAppKitModal { ); } if (_isConnected && logEvent == true) { - analyticsService.instance.sendEvent(SwitchNetworkEvent( + _analyticsService.sendEvent(SwitchNetworkEvent( network: _currentSelectedChainId!, )); } @@ -656,12 +666,12 @@ class ReownAppKitModal with ChangeNotifier implements IReownAppKitModal { return; } - analyticsService.instance.sendEvent(ModalOpenEvent( + _analyticsService.sendEvent(ModalOpenEvent( connected: _isConnected, )); // Reset the explorer - explorerService.instance.search(query: null); + _explorerService.search(query: null); widgetStack.instance.clear(); final isBottomSheet = PlatformUtils.isBottomSheet(); @@ -755,14 +765,14 @@ class ReownAppKitModal with ChangeNotifier implements IReownAppKitModal { name: walletName, platform: inBrowser ? AnalyticsPlatform.web : AnalyticsPlatform.mobile, ); - analyticsService.instance.sendEvent(event); + _analyticsService.sendEvent(event); } @override Future connectSelectedWallet({bool inBrowser = false}) async { _checkInitialized(); - final walletRedirect = explorerService.instance.getWalletRedirect( + final walletRedirect = _explorerService.getWalletRedirect( selectedWallet, ); @@ -779,11 +789,11 @@ class ReownAppKitModal with ChangeNotifier implements IReownAppKitModal { } try { if (_selectedWallet!.isCoinbase) { - await coinbaseService.instance.getAccount(); - await explorerService.instance.storeConnectedWallet(_selectedWallet); + await _coinbaseService.getAccount(); + await _explorerService.storeConnectedWallet(_selectedWallet); } else { await buildConnectionUri(); - await uriService.instance.openRedirect( + await _uriService.openRedirect( walletRedirect, wcURI: wcUri!, pType: pType, @@ -809,19 +819,19 @@ class ReownAppKitModal with ChangeNotifier implements IReownAppKitModal { } else { if (_isUserRejectedError(e)) { onModalError.broadcast(UserRejectedConnection()); - analyticsService.instance.sendEvent(ConnectErrorEvent( + _analyticsService.sendEvent(ConnectErrorEvent( message: 'User declined connection', )); } else { onModalError.broadcast(ErrorOpeningWallet()); - analyticsService.instance.sendEvent(ConnectErrorEvent( + _analyticsService.sendEvent(ConnectErrorEvent( message: e.message, )); } } } else if (_isUserRejectedError(e)) { onModalError.broadcast(UserRejectedConnection()); - analyticsService.instance.sendEvent(ConnectErrorEvent( + _analyticsService.sendEvent(ConnectErrorEvent( message: 'User declined connection', )); } else { @@ -838,12 +848,12 @@ class ReownAppKitModal with ChangeNotifier implements IReownAppKitModal { Future buildConnectionUri() async { if (!_isConnected) { try { - if (siweService.instance!.enabled) { - final walletRedirect = explorerService.instance.getWalletRedirect( + if (_siweService.enabled) { + final walletRedirect = _explorerService.getWalletRedirect( selectedWallet, ); - final nonce = await siweService.instance!.getNonce(); - final p1 = await siweService.instance!.config!.getMessageParams(); + final nonce = await _siweService.getNonce(); + final p1 = await _siweService.config!.getMessageParams(); final methods = p1.methods ?? NetworkUtils.defaultNetworkMethods['eip155']; // @@ -923,7 +933,7 @@ class ReownAppKitModal with ChangeNotifier implements IReownAppKitModal { Future _connectionErrorHandler(dynamic e) async { if (_isUserRejectedError(e)) { onModalError.broadcast(UserRejectedConnection()); - analyticsService.instance.sendEvent(ConnectErrorEvent( + _analyticsService.sendEvent(ConnectErrorEvent( message: 'User declined connection', )); } else { @@ -936,13 +946,13 @@ class ReownAppKitModal with ChangeNotifier implements IReownAppKitModal { return; } onModalError.broadcast(ModalError('Error connecting to wallet')); - analyticsService.instance.sendEvent(ConnectErrorEvent( + _analyticsService.sendEvent(ConnectErrorEvent( message: message, )); } if (e is ReownSignError || e is ReownCoreError) { onModalError.broadcast(ModalError(e.message)); - analyticsService.instance.sendEvent(ConnectErrorEvent( + _analyticsService.sendEvent(ConnectErrorEvent( message: e.message, )); } @@ -954,7 +964,7 @@ class ReownAppKitModal with ChangeNotifier implements IReownAppKitModal { void launchConnectedWallet() async { _checkInitialized(); - final walletInfo = explorerService.instance.getConnectedWallet(); + final walletInfo = _explorerService.getConnectedWallet(); if (walletInfo == null) { // if walletInfo is null could mean that either // 1. There's no wallet connected (shouldn't happen) @@ -984,7 +994,7 @@ class ReownAppKitModal with ChangeNotifier implements IReownAppKitModal { return; } - final walletRedirect = explorerService.instance.getWalletRedirect( + final walletRedirect = _explorerService.getWalletRedirect( walletInfo, ); @@ -994,7 +1004,7 @@ class ReownAppKitModal with ChangeNotifier implements IReownAppKitModal { try { final link = metadataRedirect?.native ?? metadataRedirect?.universal; - uriService.instance.openRedirect( + _uriService.openRedirect( walletRedirect.copyWith(mobile: link), pType: PlatformUtils.getPlatformType(), ); @@ -1019,7 +1029,7 @@ class ReownAppKitModal with ChangeNotifier implements IReownAppKitModal { if (_currentSession?.sessionService.isCoinbase == true) { try { - await coinbaseService.instance.resetSession(); + await _coinbaseService.resetSession(); } catch (_) { _status = ReownAppKitModalStatus.initialized; _notify(); @@ -1050,19 +1060,19 @@ class ReownAppKitModal with ChangeNotifier implements IReownAppKitModal { ); } try { - if (siweService.instance!.signOutOnDisconnect) { - await siweService.instance!.signOut(); + if (_siweService.signOutOnDisconnect) { + await _siweService.signOut(); } } catch (_) {} - analyticsService.instance.sendEvent(DisconnectSuccessEvent()); + _analyticsService.sendEvent(DisconnectSuccessEvent()); if (!(_currentSession?.sessionService.isWC == true)) { // if sessionService.isWC then _cleanSession() is being called on sessionDelete event return await _cleanSession(); } return; } catch (e) { - analyticsService.instance.sendEvent(DisconnectErrorEvent()); + _analyticsService.sendEvent(DisconnectErrorEvent()); _status = ReownAppKitModalStatus.initialized; _notify(); } @@ -1091,15 +1101,15 @@ class ReownAppKitModal with ChangeNotifier implements IReownAppKitModal { if (_disconnectOnClose) { _disconnectOnClose = false; if (currentKey == KeyConstants.approveSiwePageKey) { - analyticsService.instance.sendEvent(ClickCancelSiwe( + _analyticsService.sendEvent(ClickCancelSiwe( network: _currentSelectedChainId ?? '', )); } await disconnect(); selectWallet(null); } - toastService.instance.clear(); - analyticsService.instance.sendEvent(ModalCloseEvent( + _toastService.clear(); + _analyticsService.sendEvent(ModalCloseEvent( connected: _isConnected, )); } @@ -1252,7 +1262,7 @@ class ReownAppKitModal with ChangeNotifier implements IReownAppKitModal { ); } if (_currentSession!.sessionService.isCoinbase) { - return await await coinbaseService.instance.request( + return await await _coinbaseService.request( chainId: switchToChainId ?? reqChainId, request: request, ); @@ -1323,8 +1333,8 @@ class ReownAppKitModal with ChangeNotifier implements IReownAppKitModal { bool get _initializeCoinbaseSDK { final cbId = CoinbaseService.defaultWalletData.listing.id; - final included = (explorerService.instance.includedWalletIds ?? {}); - final excluded = (explorerService.instance.excludedWalletIds ?? {}); + final included = (_explorerService.includedWalletIds ?? {}); + final excluded = (_explorerService.excludedWalletIds ?? {}); if (included.isNotEmpty) { return included.contains(cbId); @@ -1407,7 +1417,7 @@ class ReownAppKitModal with ChangeNotifier implements IReownAppKitModal { try { _chainBalance = await (_getBalance?.call() ?? - blockchainService.instance.getBalance( + _blockchainService.getBalance( address: _currentSession!.getAddress(namespace)!, namespace: namespace, chainId: _currentSelectedChainId!, @@ -1420,7 +1430,7 @@ class ReownAppKitModal with ChangeNotifier implements IReownAppKitModal { if (namespace == NetworkUtils.eip155) { // Get the avatar, each chainId is just a number in string form. try { - final blockchainId = await blockchainService.instance.getIdentity( + final blockchainId = await _blockchainService.getIdentity( _currentSession!.getAddress(namespace)!, ); _avatarUrl = blockchainId.avatar; @@ -1550,7 +1560,7 @@ class ReownAppKitModal with ChangeNotifier implements IReownAppKitModal { final storedWalletId = _storage.get(StorageConstants.recentWalletId); final walletId = storedWalletId?['walletId']; await _storage.deleteAll(); - await explorerService.instance.storeRecentWalletId(walletId); + await _explorerService.storeRecentWalletId(walletId); } catch (_) { await _storage.deleteAll(); } @@ -1570,7 +1580,7 @@ class ReownAppKitModal with ChangeNotifier implements IReownAppKitModal { } void _onModalError(ModalError? event) { - toastService.instance.show(ToastMessage( + _toastService.show(ToastMessage( type: ToastType.error, text: event?.message ?? 'An error occurred.', )); @@ -1578,8 +1588,8 @@ class ReownAppKitModal with ChangeNotifier implements IReownAppKitModal { void _onNetworkChainRequireSIWE(ModalNetworkChange? args) async { try { - if (siweService.instance!.signOutOnNetworkChange) { - await siweService.instance!.signOut(); + if (_siweService.signOutOnNetworkChange) { + await _siweService.signOut(); _disconnectOnClose = true; widgetStack.instance.push(ApproveSIWEPage( onSiweFinish: _oneSIWEFinish, @@ -1612,13 +1622,9 @@ class ReownAppKitModal with ChangeNotifier implements IReownAppKitModal { _magicService.onMagicRpcRequest.subscribe(_onMagicRequest); // // Coinbase - coinbaseService.instance.onCoinbaseConnect.subscribe( - _onCoinbaseConnectEvent, - ); - coinbaseService.instance.onCoinbaseError.subscribe( - _onCoinbaseErrorEvent, - ); - coinbaseService.instance.onCoinbaseSessionUpdate.subscribe( + _coinbaseService.onCoinbaseConnect.subscribe(_onCoinbaseConnectEvent); + _coinbaseService.onCoinbaseError.subscribe(_onCoinbaseErrorEvent); + _coinbaseService.onCoinbaseSessionUpdate.subscribe( _onCoinbaseSessionUpdateEvent, ); // @@ -1650,13 +1656,9 @@ class ReownAppKitModal with ChangeNotifier implements IReownAppKitModal { _magicService.onMagicRpcRequest.unsubscribe(_onMagicRequest); // // Coinbase - coinbaseService.instance.onCoinbaseConnect.unsubscribe( - _onCoinbaseConnectEvent, - ); - coinbaseService.instance.onCoinbaseError.unsubscribe( - _onCoinbaseErrorEvent, - ); - coinbaseService.instance.onCoinbaseSessionUpdate.unsubscribe( + _coinbaseService.onCoinbaseConnect.unsubscribe(_onCoinbaseConnectEvent); + _coinbaseService.onCoinbaseError.unsubscribe(_onCoinbaseErrorEvent); + _coinbaseService.onCoinbaseSessionUpdate.unsubscribe( _onCoinbaseSessionUpdateEvent, ); // @@ -1713,7 +1715,7 @@ extension _EmailConnectorExtension on ReownAppKitModal { await _storage.delete(StorageConstants.connectedWalletData); } // - if (siweService.instance!.enabled) { + if (_siweService.enabled) { if (!_isOpen) { await _checkSIWEStatus(); onModalUpdate.broadcast(ModalConnect(_currentSession!)); @@ -1813,7 +1815,7 @@ extension _CoinbaseConnectorExtension on ReownAppKitModal { await _setSesionAndChainData(session); onModalConnect.broadcast(ModalConnect(session)); // - if (siweService.instance!.enabled) { + if (_siweService.enabled) { _disconnectOnClose = true; widgetStack.instance.push(ApproveSIWEPage( onSiweFinish: _oneSIWEFinish, @@ -1841,10 +1843,10 @@ extension _CoinbaseConnectorExtension on ReownAppKitModal { address: address, chainName: chainInfo?.name ?? '', chainId: int.parse(chainId), - peer: coinbaseService.instance.metadata, + peer: _coinbaseService.metadata, self: ConnectionMetadata( metadata: _appKit.metadata, - publicKey: await coinbaseService.instance.ownPublicKey, + publicKey: await _coinbaseService.ownPublicKey, ), ), ); @@ -1886,19 +1888,21 @@ extension _AppKitModalExtension on ReownAppKitModal { cacaoPayload: CacaoRequestPayload.fromCacaoPayload(cacao.p), ); final clientId = await _appKit.core.crypto.getClientId(); - await siweService.instance!.verifyMessage( + await _siweService.verifyMessage( message: message, signature: cacao.s.s, clientId: clientId, ); } catch (e, s) { - _appKit.core.logger - .e('[$runtimeType] onSessionAuthResponse $e', stackTrace: s); + _appKit.core.logger.e( + '[$runtimeType] onSessionAuthResponse $e', + stackTrace: s, + ); await disconnect(); return; } // - final siweSession = await siweService.instance!.getSession(); + final siweSession = await _siweService.getSession(); final newSession = session.copyWith(siweSession: siweSession); // await _storeSession(newSession); @@ -1913,14 +1917,15 @@ extension _AppKitModalExtension on ReownAppKitModal { void _onSessionConnect(SessionConnect? args) async { final debugString = jsonEncode(args?.session.toJson()); _appKit.core.logger.d('[$runtimeType] _onSessionConnect: $debugString'); - final siweEnabled = siweService.instance!.enabled; - if (_supportsOneClickAuth && siweEnabled) return; + if (_supportsOneClickAuth && _siweService.enabled) { + return; + } if (args != null) { // IF SIWE CALLBACK (1-CA NOT SUPPORTED) SIWECONGIF METHODS ARE CALLED ON ApproveSIWEPage final session = await _settleSession(args.session); onModalConnect.broadcast(ModalConnect(session)); // - if (siweService.instance!.enabled) { + if (_siweService.enabled) { _disconnectOnClose = true; widgetStack.instance.push(ApproveSIWEPage( onSiweFinish: _oneSIWEFinish, @@ -1945,16 +1950,16 @@ extension _AppKitModalExtension on ReownAppKitModal { final session = ReownAppKitModalSession(sessionData: mSession); await _setSesionAndChainData(session); if (_selectedWallet == null) { - analyticsService.instance.sendEvent(ConnectSuccessEvent( + _analyticsService.sendEvent(ConnectSuccessEvent( name: 'WalletConnect', method: AnalyticsPlatform.qrcode, )); await _storage.delete(StorageConstants.recentWalletId); await _storage.delete(StorageConstants.connectedWalletData); } else { - explorerService.instance.storeConnectedWallet(_selectedWallet); + _explorerService.storeConnectedWallet(_selectedWallet); final walletName = _selectedWallet!.listing.name; - analyticsService.instance.sendEvent(ConnectSuccessEvent( + _analyticsService.sendEvent(ConnectSuccessEvent( name: walletName, method: AnalyticsPlatform.mobile, )); @@ -1978,7 +1983,7 @@ extension _AppKitModalExtension on ReownAppKitModal { } onModalUpdate.broadcast(ModalConnect(updatedSession)); closeModal(); - analyticsService.instance.sendEvent(SiweAuthSuccess( + _analyticsService.sendEvent(SiweAuthSuccess( network: _currentSelectedChainId!, )); } @@ -1991,8 +1996,8 @@ extension _AppKitModalExtension on ReownAppKitModal { } else if (args?.name == EventsConstants.accountsChanged) { try { // TODO implement account change - if (siweService.instance!.signOutOnAccountChange) { - await siweService.instance!.signOut(); + if (_siweService.signOutOnAccountChange) { + await _siweService.signOut(); } } catch (e, s) { _appKit.core.logger.d( diff --git a/packages/reown_appkit/lib/modal/pages/account_page.dart b/packages/reown_appkit/lib/modal/pages/account_page.dart index d1b4c32..2d9c12a 100644 --- a/packages/reown_appkit/lib/modal/pages/account_page.dart +++ b/packages/reown_appkit/lib/modal/pages/account_page.dart @@ -1,14 +1,15 @@ import 'package:collection/collection.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; +import 'package:get_it/get_it.dart'; import 'package:reown_appkit/modal/constants/key_constants.dart'; import 'package:reown_appkit/modal/constants/style_constants.dart'; import 'package:reown_appkit/modal/pages/edit_email_page.dart'; import 'package:reown_appkit/modal/pages/upgrade_wallet_page.dart'; import 'package:reown_appkit/modal/services/analytics_service/models/analytics_event.dart'; -import 'package:reown_appkit/modal/services/explorer_service/explorer_service_singleton.dart'; import 'package:reown_appkit/modal/i_appkit_modal_impl.dart'; +import 'package:reown_appkit/modal/services/explorer_service/i_explorer_service.dart'; import 'package:reown_appkit/modal/utils/asset_util.dart'; import 'package:reown_appkit/modal/widgets/circular_loader.dart'; import 'package:reown_appkit/modal/widgets/miscellaneous/content_loading.dart'; @@ -259,7 +260,7 @@ class _SelectNetworkButton extends StatelessWidget { final themeColors = ReownAppKitModalTheme.colorsOf(context); final chainId = service.selectedChain?.chainId ?? ''; final imageId = ReownAppKitModalNetworks.getNetworkIconId(chainId); - final tokenImage = explorerService.instance.getAssetImageUrl(imageId); + final tokenImage = GetIt.I().getAssetImageUrl(imageId); final radiuses = ReownAppKitModalTheme.radiusesOf(context); return AccountListItem( iconWidget: Padding( diff --git a/packages/reown_appkit/lib/modal/pages/approve_siwe.dart b/packages/reown_appkit/lib/modal/pages/approve_siwe.dart index b0837ed..e9ac032 100644 --- a/packages/reown_appkit/lib/modal/pages/approve_siwe.dart +++ b/packages/reown_appkit/lib/modal/pages/approve_siwe.dart @@ -3,15 +3,16 @@ import 'dart:async'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter/widgets.dart'; +import 'package:get_it/get_it.dart'; import 'package:reown_appkit/modal/constants/key_constants.dart'; -import 'package:reown_appkit/modal/services/analytics_service/analytics_service_singleton.dart'; +import 'package:reown_appkit/modal/services/analytics_service/i_analytics_service.dart'; import 'package:reown_appkit/modal/services/analytics_service/models/analytics_event.dart'; -import 'package:reown_appkit/modal/services/siwe_service/siwe_service_singleton.dart'; import 'package:reown_appkit/modal/i_appkit_modal_impl.dart'; import 'package:reown_appkit/modal/constants/style_constants.dart'; +import 'package:reown_appkit/modal/services/siwe_service/i_siwe_service.dart'; +import 'package:reown_appkit/modal/services/toast_service/i_toast_service.dart'; import 'package:reown_appkit/modal/services/toast_service/models/toast_message.dart'; -import 'package:reown_appkit/modal/services/toast_service/toast_service_singleton.dart'; import 'package:reown_appkit/modal/widgets/avatars/account_avatar.dart'; import 'package:reown_appkit/modal/widgets/buttons/primary_button.dart'; import 'package:reown_appkit/modal/widgets/buttons/secondary_button.dart'; @@ -33,6 +34,9 @@ class ApproveSIWEPage extends StatefulWidget { } class _ApproveSIWEPageState extends State { + ISiweService get _siweService => GetIt.I(); + IAnalyticsService get _analyticsService => GetIt.I(); + IReownAppKitModal? _appKitModal; double _position = 0.0; static const _duration = Duration(milliseconds: 1500); @@ -72,30 +76,28 @@ class _ApproveSIWEPageState extends State { chainId, ); final address = _appKitModal!.session!.getAddress(namespace)!; - analyticsService.instance.sendEvent(ClickSignSiweMessage( - network: chainId, - )); + _analyticsService.sendEvent(ClickSignSiweMessage(network: chainId)); chainId = ReownAppKitModalNetworks.getCaip2Chain(chainId); // - final message = await siweService.instance!.createMessage( + final message = await _siweService.createMessage( chainId: chainId, address: address, ); // _appKitModal!.launchConnectedWallet(); - final signature = await siweService.instance!.signMessageRequest( + final signature = await _siweService.signMessageRequest( message, session: _appKitModal!.session!, ); // final clientId = await _appKitModal!.appKit!.core.crypto.getClientId(); - await siweService.instance!.verifyMessage( + await _siweService.verifyMessage( message: message, signature: signature, clientId: clientId, ); // - final siweSession = await siweService.instance!.getSession(); + final siweSession = await _siweService.getSession(); final newSession = _appKitModal!.session!.copyWith(siweSession: siweSession); // @@ -113,8 +115,8 @@ class _ApproveSIWEPageState extends State { void _handleError(String? error) { debugPrint('[$runtimeType] _handleError $error'); String chainId = _appKitModal!.selectedChain?.chainId ?? '1'; - analyticsService.instance.sendEvent(SiweAuthError(network: chainId)); - toastService.instance.show(ToastMessage( + _analyticsService.sendEvent(SiweAuthError(network: chainId)); + GetIt.I().show(ToastMessage( type: ToastType.error, text: error ?? 'Something went wrong.', )); diff --git a/packages/reown_appkit/lib/modal/pages/confirm_email_page.dart b/packages/reown_appkit/lib/modal/pages/confirm_email_page.dart index 53ef12a..79b27c7 100644 --- a/packages/reown_appkit/lib/modal/pages/confirm_email_page.dart +++ b/packages/reown_appkit/lib/modal/pages/confirm_email_page.dart @@ -5,9 +5,9 @@ import 'package:reown_appkit/modal/services/magic_service/i_magic_service.dart'; import 'package:reown_appkit/modal/services/magic_service/models/email_login_step.dart'; import 'package:reown_appkit/modal/services/magic_service/models/magic_events.dart'; import 'package:reown_appkit/modal/constants/style_constants.dart'; +import 'package:reown_appkit/modal/services/toast_service/i_toast_service.dart'; import 'package:reown_appkit/modal/theme/public/appkit_modal_theme.dart'; import 'package:reown_appkit/modal/services/toast_service/models/toast_message.dart'; -import 'package:reown_appkit/modal/services/toast_service/toast_service_singleton.dart'; import 'package:reown_appkit/modal/widgets/icons/rounded_icon.dart'; import 'package:reown_appkit/modal/widgets/miscellaneous/content_loading.dart'; import 'package:reown_appkit/modal/widgets/miscellaneous/verify_otp_view.dart'; @@ -104,7 +104,7 @@ class __VerifyDeviceViewState extends State<_VerifyDeviceView> { void _resendEmail() async { final diff = DateTime.now().difference(_resendEnabledAt).inSeconds; if (diff < 0) { - toastService.instance.show(ToastMessage( + GetIt.I().show(ToastMessage( type: ToastType.error, text: 'Try again after ${diff.abs()} seconds', )); @@ -112,7 +112,7 @@ class __VerifyDeviceViewState extends State<_VerifyDeviceView> { final email = _magicService.email.value; await _magicService.connectEmail(value: email); _resendEnabledAt = DateTime.now().add(Duration(seconds: 30)); - toastService.instance.show(ToastMessage( + GetIt.I().show(ToastMessage( type: ToastType.success, text: 'Link email resent', )); diff --git a/packages/reown_appkit/lib/modal/pages/connect_wallet_page.dart b/packages/reown_appkit/lib/modal/pages/connect_wallet_page.dart index eb5a6bf..526a510 100644 --- a/packages/reown_appkit/lib/modal/pages/connect_wallet_page.dart +++ b/packages/reown_appkit/lib/modal/pages/connect_wallet_page.dart @@ -2,14 +2,15 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; +import 'package:get_it/get_it.dart'; import 'package:reown_appkit/modal/constants/key_constants.dart'; -import 'package:reown_appkit/modal/services/explorer_service/explorer_service_singleton.dart'; -import 'package:reown_appkit/modal/services/siwe_service/siwe_service_singleton.dart'; +import 'package:reown_appkit/modal/services/explorer_service/i_explorer_service.dart'; import 'package:reown_appkit/modal/i_appkit_modal_impl.dart'; import 'package:reown_appkit/modal/constants/style_constants.dart'; +import 'package:reown_appkit/modal/services/siwe_service/i_siwe_service.dart'; +import 'package:reown_appkit/modal/services/toast_service/i_toast_service.dart'; import 'package:reown_appkit/modal/services/toast_service/models/toast_message.dart'; -import 'package:reown_appkit/modal/services/toast_service/toast_service_singleton.dart'; import 'package:reown_appkit/modal/widgets/icons/rounded_icon.dart'; import 'package:reown_appkit/modal/widgets/miscellaneous/content_loading.dart'; import 'package:reown_appkit/modal/widgets/miscellaneous/segmented_control.dart'; @@ -32,6 +33,9 @@ class ConnectWalletPage extends StatefulWidget { class _ConnectWalletPageState extends State with WidgetsBindingObserver { + IExplorerService get _explorerService => GetIt.I(); + ISiweService get _siweService => GetIt.I(); + IReownAppKitModal? _service; SegmentOption _selectedSegment = SegmentOption.mobile; ModalError? errorEvent; @@ -56,7 +60,7 @@ class _ConnectWalletPageState extends State if (state == AppLifecycleState.resumed) { final isOpen = _service?.isOpen ?? false; final isConnected = _service?.isConnected ?? false; - if (isOpen && isConnected && !siweService.instance!.enabled) { + if (isOpen && isConnected && !_siweService.enabled) { Future.delayed(Duration(seconds: 1), () { if (!mounted) return; _service?.closeModal(); @@ -88,7 +92,7 @@ class _ConnectWalletPageState extends State kNavbarHeight - (kPadding16 * 2); // - final walletRedirect = explorerService.instance.getWalletRedirect( + final walletRedirect = _explorerService.getWalletRedirect( _service!.selectedWallet, ); final webOnlyWallet = walletRedirect?.webOnly == true; @@ -97,7 +101,7 @@ class _ConnectWalletPageState extends State final selectedWallet = _service!.selectedWallet; final walletName = selectedWallet?.listing.name ?? 'Wallet'; final imageId = selectedWallet?.listing.imageId ?? ''; - final imageUrl = explorerService.instance.getWalletImageUrl(imageId); + final imageUrl = _explorerService.getWalletImageUrl(imageId); // return ModalNavbar( title: walletName, @@ -297,9 +301,10 @@ class _ConnectWalletPageState extends State Future _copyToClipboard(BuildContext context) async { final service = ModalProvider.of(context).instance; await Clipboard.setData(ClipboardData(text: service.wcUri!)); - toastService.instance.show( - ToastMessage(type: ToastType.success, text: 'Link copied'), - ); + GetIt.I().show(ToastMessage( + type: ToastType.success, + text: 'Link copied', + )); } } diff --git a/packages/reown_appkit/lib/modal/pages/connet_network_page.dart b/packages/reown_appkit/lib/modal/pages/connet_network_page.dart index e3bb0f1..2ca5274 100644 --- a/packages/reown_appkit/lib/modal/pages/connet_network_page.dart +++ b/packages/reown_appkit/lib/modal/pages/connet_network_page.dart @@ -4,11 +4,11 @@ import 'package:flutter/material.dart'; import 'package:get_it/get_it.dart'; import 'package:reown_appkit/modal/constants/key_constants.dart'; -import 'package:reown_appkit/modal/services/explorer_service/explorer_service_singleton.dart'; +import 'package:reown_appkit/modal/services/explorer_service/i_explorer_service.dart'; import 'package:reown_appkit/modal/services/magic_service/i_magic_service.dart'; -import 'package:reown_appkit/modal/services/siwe_service/siwe_service_singleton.dart'; import 'package:reown_appkit/modal/i_appkit_modal_impl.dart'; import 'package:reown_appkit/modal/constants/style_constants.dart'; +import 'package:reown_appkit/modal/services/siwe_service/i_siwe_service.dart'; import 'package:reown_appkit/modal/widgets/icons/rounded_icon.dart'; import 'package:reown_appkit/modal/widgets/miscellaneous/content_loading.dart'; import 'package:reown_appkit/modal/widgets/widget_stack/widget_stack_singleton.dart'; @@ -50,6 +50,8 @@ class _ConnectNetworkPageState extends State } IMagicService get _magicService => GetIt.I(); + IExplorerService get _explorerService => GetIt.I(); + ISiweService get _siweService => GetIt.I(); void _connect() async { errorEvent = null; @@ -72,7 +74,7 @@ class _ConnectNetworkPageState extends State ); if (chainInfo != null) { Future.delayed(const Duration(milliseconds: 300), () { - if (!siweService.instance!.enabled) { + if (!_siweService.enabled) { widgetStack.instance.pop(); } }); @@ -88,7 +90,7 @@ class _ConnectNetworkPageState extends State if (state == AppLifecycleState.resumed) { if (_appKitModal?.session?.sessionService.isCoinbase == true) { if (_appKitModal?.selectedChain?.chainId == widget.chainInfo.chainId) { - if (!siweService.instance!.enabled) { + if (!_siweService.enabled) { widgetStack.instance.pop(); } } @@ -121,7 +123,7 @@ class _ConnectNetworkPageState extends State // final chainId = widget.chainInfo.chainId; final imageId = ReownAppKitModalNetworks.getNetworkIconId(chainId); - final imageUrl = explorerService.instance.getAssetImageUrl(imageId); + final imageUrl = _explorerService.getAssetImageUrl(imageId); // return ModalNavbar( title: widget.chainInfo.name, diff --git a/packages/reown_appkit/lib/modal/pages/public/appkit_modal_all_wallets_page.dart b/packages/reown_appkit/lib/modal/pages/public/appkit_modal_all_wallets_page.dart index 0c953f9..832fd61 100644 --- a/packages/reown_appkit/lib/modal/pages/public/appkit_modal_all_wallets_page.dart +++ b/packages/reown_appkit/lib/modal/pages/public/appkit_modal_all_wallets_page.dart @@ -1,11 +1,12 @@ import 'dart:math'; import 'package:flutter/material.dart'; +import 'package:get_it/get_it.dart'; import 'package:reown_appkit/modal/constants/key_constants.dart'; import 'package:reown_appkit/modal/pages/connect_wallet_page.dart'; -import 'package:reown_appkit/modal/services/explorer_service/explorer_service_singleton.dart'; import 'package:reown_appkit/modal/constants/style_constants.dart'; +import 'package:reown_appkit/modal/services/explorer_service/i_explorer_service.dart'; import 'package:reown_appkit/modal/widgets/widget_stack/widget_stack_singleton.dart'; import 'package:reown_appkit/modal/widgets/miscellaneous/responsive_container.dart'; import 'package:reown_appkit/modal/widgets/modal_provider.dart'; @@ -25,6 +26,7 @@ class ReownAppKitModalAllWalletsPage extends StatefulWidget { class _AppKitModalAllWalletsPageState extends State { + IExplorerService get _explorerService => GetIt.I(); bool _paginating = false; final _controller = ScrollController(); @@ -47,14 +49,14 @@ class _AppKitModalAllWalletsPageState } Future _paginate() { - setState(() => _paginating = explorerService.instance.canPaginate); - return explorerService.instance.paginate(); + setState(() => _paginating = _explorerService.canPaginate); + return _explorerService.paginate(); } @override Widget build(BuildContext context) { final service = ModalProvider.of(context).instance; - final totalListings = explorerService.instance.totalListings.value; + final totalListings = _explorerService.totalListings.value; final rows = (totalListings / 4.0).ceil(); final isSearchAvailable = totalListings >= kShortWalletListCount; final maxHeight = (rows * kGridItemHeight) + @@ -70,7 +72,7 @@ class _AppKitModalAllWalletsPageState ), onBack: () { FocusManager.instance.primaryFocus?.unfocus(); - explorerService.instance.search(query: null); + _explorerService.search(query: null); widgetStack.instance.pop(); }, safeAreaBottom: false, diff --git a/packages/reown_appkit/lib/modal/pages/public/appkit_modal_main_wallets_page.dart b/packages/reown_appkit/lib/modal/pages/public/appkit_modal_main_wallets_page.dart index e1eed70..326ab7d 100644 --- a/packages/reown_appkit/lib/modal/pages/public/appkit_modal_main_wallets_page.dart +++ b/packages/reown_appkit/lib/modal/pages/public/appkit_modal_main_wallets_page.dart @@ -6,7 +6,7 @@ import 'package:get_it/get_it.dart'; import 'package:reown_appkit/modal/pages/about_wallets.dart'; import 'package:reown_appkit/modal/pages/connect_wallet_page.dart'; import 'package:reown_appkit/modal/services/analytics_service/models/analytics_event.dart'; -import 'package:reown_appkit/modal/services/explorer_service/explorer_service_singleton.dart'; +import 'package:reown_appkit/modal/services/explorer_service/i_explorer_service.dart'; import 'package:reown_appkit/modal/services/magic_service/i_magic_service.dart'; import 'package:reown_appkit/modal/constants/key_constants.dart'; import 'package:reown_appkit/modal/constants/style_constants.dart'; @@ -36,6 +36,7 @@ class ReownAppKitModalMainWalletsPage extends StatefulWidget { class _AppKitModalMainWalletsPageState extends State { IMagicService get _magicService => GetIt.I(); + IExplorerService get _explorerService => GetIt.I(); @override void initState() { @@ -170,7 +171,7 @@ class _AppKitModalMainWalletsPageState ? null : ValueListenableBuilder( valueListenable: - explorerService.instance.totalListings, + _explorerService.totalListings, builder: (context, value, _) { return WalletItemChip( value: value.lazyCount, diff --git a/packages/reown_appkit/lib/modal/pages/public/appkit_modal_qrcode_page.dart b/packages/reown_appkit/lib/modal/pages/public/appkit_modal_qrcode_page.dart index 4f6a3b7..6b154bc 100644 --- a/packages/reown_appkit/lib/modal/pages/public/appkit_modal_qrcode_page.dart +++ b/packages/reown_appkit/lib/modal/pages/public/appkit_modal_qrcode_page.dart @@ -1,5 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; +import 'package:get_it/get_it.dart'; +import 'package:reown_appkit/modal/services/toast_service/i_toast_service.dart'; import 'package:reown_appkit/reown_appkit.dart'; import 'package:shimmer/shimmer.dart'; @@ -12,7 +14,6 @@ import 'package:reown_appkit/modal/widgets/miscellaneous/responsive_container.da import 'package:reown_appkit/modal/widgets/modal_provider.dart'; import 'package:reown_appkit/modal/widgets/navigation/navbar.dart'; import 'package:reown_appkit/modal/services/toast_service/models/toast_message.dart'; -import 'package:reown_appkit/modal/services/toast_service/toast_service_singleton.dart'; class ReownAppKitModalQRCodePage extends StatefulWidget { const ReownAppKitModalQRCodePage() : super(key: KeyConstants.qrCodePageKey); @@ -140,8 +141,9 @@ class _AppKitModalQRCodePageState extends State { Future _copyToClipboard(BuildContext context) async { final service = ModalProvider.of(context).instance; await Clipboard.setData(ClipboardData(text: service.wcUri!)); - toastService.instance.show( - ToastMessage(type: ToastType.success, text: 'Link copied'), - ); + GetIt.I().show(ToastMessage( + type: ToastType.success, + text: 'Link copied', + )); } } diff --git a/packages/reown_appkit/lib/modal/pages/social_login_page.dart b/packages/reown_appkit/lib/modal/pages/social_login_page.dart index 1550475..2ec9ede 100644 --- a/packages/reown_appkit/lib/modal/pages/social_login_page.dart +++ b/packages/reown_appkit/lib/modal/pages/social_login_page.dart @@ -10,7 +10,7 @@ import 'package:reown_appkit/modal/constants/string_constants.dart'; import 'package:reown_appkit/modal/i_appkit_modal_impl.dart'; import 'package:reown_appkit/modal/constants/style_constants.dart'; import 'package:reown_appkit/modal/pages/farcaster_qrcode_page.dart'; -import 'package:reown_appkit/modal/services/analytics_service/analytics_service_singleton.dart'; +import 'package:reown_appkit/modal/services/analytics_service/i_analytics_service.dart'; import 'package:reown_appkit/modal/services/analytics_service/models/analytics_event.dart'; import 'package:reown_appkit/modal/services/magic_service/i_magic_service.dart'; import 'package:reown_appkit/modal/services/magic_service/models/magic_events.dart'; @@ -44,6 +44,7 @@ class SocialLoginPage extends StatefulWidget { class _SocialLoginPageState extends State { IMagicService get _magicService => GetIt.I(); + IAnalyticsService get _analyticsService => GetIt.I(); IReownAppKitModal? _service; ModalError? errorEvent; @@ -79,7 +80,7 @@ class _SocialLoginPageState extends State { Future _initSocialLogin(AppKitSocialOption option) async { try { setState(() => errorEvent = null); - analyticsService.instance.sendEvent(SocialLoginStarted( + _analyticsService.sendEvent(SocialLoginStarted( provider: widget.socialOption.name.toLowerCase(), )); if (option == AppKitSocialOption.Farcaster) { @@ -183,7 +184,7 @@ class _SocialLoginPageState extends State { _service?.selectedChain?.chainId ?? '1', ); await _magicService.getUser(chainId: caip2Chain); - analyticsService.instance.sendEvent(SocialLoginSuccess( + _analyticsService.sendEvent(SocialLoginSuccess( provider: widget.socialOption.name.toLowerCase(), )); _magicService.onCompleteSocialLogin.unsubscribe( @@ -225,7 +226,7 @@ class _SocialLoginPageState extends State { debugPrint('[$runtimeType] _cancelSocialLogin'); errorEvent = ModalError('User canceled'); setState(() => _retrievingData = false); - analyticsService.instance.sendEvent(SocialLoginError( + _analyticsService.sendEvent(SocialLoginError( provider: widget.socialOption.name.toLowerCase(), )); } @@ -451,9 +452,10 @@ class __WebViewLoginWidgetState extends State<_WebViewLoginWidget> { final params = uri.queryParameters; final secureOrigin1 = UrlConstants.secureOrigin1; final secureOrigin2 = UrlConstants.secureOrigin2; - if ((uri.authority == secureOrigin1 || - uri.authority == secureOrigin2) && - params.containsKey('state')) { + final origin1 = uri.authority == secureOrigin1; + final origin2 = uri.authority == secureOrigin2; + final hasState = params.containsKey('state'); + if ((origin1 || origin2) && hasState) { Future.delayed(Duration(milliseconds: 500), () { Navigator.of(context).pop(request.url); }); @@ -471,19 +473,22 @@ class __WebViewLoginWidgetState extends State<_WebViewLoginWidget> { title: '', noBack: true, noClose: true, - rightAction: NavbarActionButton( - asset: 'lib/modal/assets/icons/close.svg', - action: widget.onCancel, - ), - leftAction: NavbarActionButton( - asset: 'lib/modal/assets/icons/disconnect.svg', - action: () async { - await _clearCookies(); - await _webViewController.clearCache(); - await _webViewController.clearLocalStorage(); - await _webViewController.reload(); - }, - ), + rightActions: [ + NavbarActionButton( + asset: 'lib/modal/assets/icons/disconnect.svg', + dimension: 48.0, + action: () async { + await _clearCookies(); + await _webViewController.clearCache(); + await _webViewController.clearLocalStorage(); + await _webViewController.reload(); + }, + ), + NavbarActionButton( + asset: 'lib/modal/assets/icons/close.svg', + action: widget.onCancel, + ), + ], body: WebViewWidget(controller: _webViewController), ); } diff --git a/packages/reown_appkit/lib/modal/pages/upgrade_wallet_page.dart b/packages/reown_appkit/lib/modal/pages/upgrade_wallet_page.dart index 754c256..9178feb 100644 --- a/packages/reown_appkit/lib/modal/pages/upgrade_wallet_page.dart +++ b/packages/reown_appkit/lib/modal/pages/upgrade_wallet_page.dart @@ -1,7 +1,8 @@ import 'package:flutter/material.dart'; +import 'package:get_it/get_it.dart'; +import 'package:reown_appkit/modal/services/analytics_service/i_analytics_service.dart'; import 'package:reown_appkit/reown_appkit.dart'; import 'package:reown_appkit/modal/constants/key_constants.dart'; -import 'package:reown_appkit/modal/services/analytics_service/analytics_service_singleton.dart'; import 'package:reown_appkit/modal/services/analytics_service/models/analytics_event.dart'; import 'package:reown_appkit/modal/constants/style_constants.dart'; import 'package:reown_appkit/modal/constants/string_constants.dart'; @@ -36,7 +37,7 @@ class UpgradeWalletPage extends StatelessWidget { SimpleIconButton( leftIcon: 'lib/modal/assets/icons/wc.svg', onTap: () { - analyticsService.instance.sendEvent(EmailUpgradeFromModal()); + GetIt.I().sendEvent(EmailUpgradeFromModal()); ReownCoreUtils.openURL(UrlConstants.secureDashboard); }, rightIcon: 'lib/modal/assets/icons/arrow_top_right.svg', diff --git a/packages/reown_appkit/lib/modal/services/analytics_service/analytics_service_singleton.dart b/packages/reown_appkit/lib/modal/services/analytics_service/analytics_service_singleton.dart deleted file mode 100644 index 83ccb30..0000000 --- a/packages/reown_appkit/lib/modal/services/analytics_service/analytics_service_singleton.dart +++ /dev/null @@ -1,7 +0,0 @@ -import 'package:reown_appkit/modal/services/analytics_service/i_analytics_service.dart'; - -class AnalyticsServiceSingleton { - late IAnalyticsService instance; -} - -final analyticsService = AnalyticsServiceSingleton(); diff --git a/packages/reown_appkit/lib/modal/services/blockchain_service/blockchain_service_singleton.dart b/packages/reown_appkit/lib/modal/services/blockchain_service/blockchain_service_singleton.dart deleted file mode 100644 index 3ea620c..0000000 --- a/packages/reown_appkit/lib/modal/services/blockchain_service/blockchain_service_singleton.dart +++ /dev/null @@ -1,7 +0,0 @@ -import 'package:reown_appkit/modal/services/blockchain_service/i_blockchain_service.dart'; - -class BlockChainServiceSingleton { - late IBlockChainService instance; -} - -final blockchainService = BlockChainServiceSingleton(); diff --git a/packages/reown_appkit/lib/modal/services/blockchain_service/i_blockchain_service.dart b/packages/reown_appkit/lib/modal/services/blockchain_service/i_blockchain_service.dart index 91c4db6..7744360 100644 --- a/packages/reown_appkit/lib/modal/services/blockchain_service/i_blockchain_service.dart +++ b/packages/reown_appkit/lib/modal/services/blockchain_service/i_blockchain_service.dart @@ -1,5 +1,4 @@ import 'package:reown_appkit/modal/services/blockchain_service/models/blockchain_identity.dart'; -// import 'package:reown_appkit/reown_appkit.dart'; abstract class IBlockChainService { Future init(); @@ -12,9 +11,4 @@ abstract class IBlockChainService { required String namespace, required String chainId, }); - - // Future rpcRequest({ - // required String chainId, - // required SessionRequestParams request, - // }); } diff --git a/packages/reown_appkit/lib/modal/services/coinbase_service/coinbase_service.dart b/packages/reown_appkit/lib/modal/services/coinbase_service/coinbase_service.dart index d67d7d8..c9eb558 100644 --- a/packages/reown_appkit/lib/modal/services/coinbase_service/coinbase_service.dart +++ b/packages/reown_appkit/lib/modal/services/coinbase_service/coinbase_service.dart @@ -1,11 +1,11 @@ import 'dart:convert'; import 'package:flutter/services.dart'; +import 'package:get_it/get_it.dart'; import 'package:reown_appkit/modal/services/coinbase_service/i_coinbase_service.dart'; import 'package:reown_appkit/modal/services/coinbase_service/models/coinbase_data.dart'; import 'package:reown_appkit/modal/services/coinbase_service/models/coinbase_events.dart'; -import 'package:reown_appkit/modal/services/explorer_service/explorer_service_singleton.dart'; import 'package:coinbase_wallet_sdk/currency.dart'; import 'package:coinbase_wallet_sdk/action.dart'; @@ -13,6 +13,7 @@ import 'package:coinbase_wallet_sdk/coinbase_wallet_sdk.dart'; import 'package:coinbase_wallet_sdk/configuration.dart'; import 'package:coinbase_wallet_sdk/eth_web3_rpc.dart'; import 'package:coinbase_wallet_sdk/request.dart'; +import 'package:reown_appkit/modal/services/explorer_service/i_explorer_service.dart'; import 'package:reown_appkit/reown_appkit.dart'; class CoinbaseService implements ICoinbaseService { @@ -82,6 +83,8 @@ class CoinbaseService implements ICoinbaseService { late ReownAppKitModalWalletInfo _walletData; late final IReownCore _core; + IExplorerService get _explorerService => GetIt.I(); + CoinbaseService({ required PairingMetadata metadata, required IReownCore core, @@ -95,10 +98,10 @@ class CoinbaseService implements ICoinbaseService { if (!_enabled) return; // Configure SDK for each platform - _walletData = (await explorerService.instance.getCoinbaseWalletObject()) ?? - defaultWalletData; + _walletData = + (await _explorerService.getCoinbaseWalletObject()) ?? defaultWalletData; final imageId = defaultWalletData.listing.imageId; - _iconImage = explorerService.instance.getWalletImageUrl(imageId); + _iconImage = _explorerService.getWalletImageUrl(imageId); final walletLink = _walletData.listing.mobileLink ?? ''; final redirect = _metadata.redirect; diff --git a/packages/reown_appkit/lib/modal/services/coinbase_service/coinbase_service_singleton.dart b/packages/reown_appkit/lib/modal/services/coinbase_service/coinbase_service_singleton.dart deleted file mode 100644 index bc8a08b..0000000 --- a/packages/reown_appkit/lib/modal/services/coinbase_service/coinbase_service_singleton.dart +++ /dev/null @@ -1,7 +0,0 @@ -import 'package:reown_appkit/modal/services/coinbase_service/i_coinbase_service.dart'; - -class CoinbaseServiceSingleton { - late ICoinbaseService instance; -} - -final coinbaseService = CoinbaseServiceSingleton(); diff --git a/packages/reown_appkit/lib/modal/services/explorer_service/explorer_service.dart b/packages/reown_appkit/lib/modal/services/explorer_service/explorer_service.dart index 34800c6..3d2b00a 100644 --- a/packages/reown_appkit/lib/modal/services/explorer_service/explorer_service.dart +++ b/packages/reown_appkit/lib/modal/services/explorer_service/explorer_service.dart @@ -4,6 +4,7 @@ import 'dart:io'; import 'package:collection/collection.dart'; import 'package:flutter/foundation.dart'; +import 'package:get_it/get_it.dart'; import 'package:http/http.dart' as http; import 'package:reown_appkit/modal/constants/string_constants.dart'; import 'package:reown_appkit/modal/services/coinbase_service/coinbase_service.dart'; @@ -11,7 +12,7 @@ import 'package:reown_appkit/modal/services/explorer_service/models/native_app_d import 'package:reown_appkit/modal/services/explorer_service/models/redirect.dart'; import 'package:reown_appkit/modal/services/explorer_service/models/request_params.dart'; import 'package:reown_appkit/modal/services/explorer_service/models/wc_sample_wallets.dart'; -import 'package:reown_appkit/modal/services/uri_service/url_utils_singleton.dart'; +import 'package:reown_appkit/modal/services/uri_service/i_url_utils.dart'; import 'package:reown_appkit/modal/utils/core_utils.dart'; import 'package:reown_appkit/modal/utils/debouncer.dart'; import 'package:reown_appkit/modal/utils/platform_utils.dart'; @@ -22,6 +23,8 @@ import 'package:reown_appkit/reown_appkit.dart'; const int _defaultEntriesCount = 48; class ExplorerService implements IExplorerService { + IUriService get _uriService => GetIt.I(); + final http.Client _client; final String _referer; @@ -156,7 +159,7 @@ class ExplorerService implements IExplorerService { final schema = WCSampleWallets.getSampleWalletScheme( sampleWallet.listing.id, ); - final installed = await uriService.instance.isInstalled(schema); + final installed = await _uriService.isInstalled(schema); if (installed) { sampleWallet = sampleWallet.copyWith(installed: true); sampleWallets.add(sampleWallet); @@ -436,7 +439,7 @@ class ExplorerService implements IExplorerService { final wallet = ReownAppKitModalWalletInfo.fromJson(results.first.toJson()); final mobileLink = CoinbaseService.defaultWalletData.listing.mobileLink; - bool installed = await uriService.instance.isInstalled(mobileLink); + bool installed = await _uriService.isInstalled(mobileLink); return wallet.copyWith( listing: wallet.listing.copyWith(mobileLink: mobileLink), installed: installed, @@ -542,7 +545,7 @@ extension on List { Future> getInstalledApps() async { final installedApps = []; for (var appData in this) { - bool installed = await uriService.instance.isInstalled( + bool installed = await GetIt.I().isInstalled( appData.schema, id: appData.id, ); diff --git a/packages/reown_appkit/lib/modal/services/explorer_service/explorer_service_singleton.dart b/packages/reown_appkit/lib/modal/services/explorer_service/explorer_service_singleton.dart deleted file mode 100644 index cbb5c09..0000000 --- a/packages/reown_appkit/lib/modal/services/explorer_service/explorer_service_singleton.dart +++ /dev/null @@ -1,7 +0,0 @@ -import 'package:reown_appkit/modal/services/explorer_service/i_explorer_service.dart'; - -class ExplorerServiceSingleton { - late IExplorerService instance; -} - -final explorerService = ExplorerServiceSingleton(); diff --git a/packages/reown_appkit/lib/modal/services/magic_service/i_magic_service.dart b/packages/reown_appkit/lib/modal/services/magic_service/i_magic_service.dart index c333f1c..052f4e5 100644 --- a/packages/reown_appkit/lib/modal/services/magic_service/i_magic_service.dart +++ b/packages/reown_appkit/lib/modal/services/magic_service/i_magic_service.dart @@ -8,6 +8,7 @@ abstract class IMagicService { Map> get supportedMethods; List get socials; + WebViewWidget get webview; ValueNotifier get isReady; ValueNotifier get isConnected; ValueNotifier get isTimeout; @@ -17,8 +18,6 @@ abstract class IMagicService { ValueNotifier get newEmail; ValueNotifier get step; - WebViewWidget get webview; - Future init({String? chainId}); void setEmail(String value); @@ -34,14 +33,13 @@ abstract class IMagicService { void completeSocialLogin({required String url}); Future getFarcasterUri({String? chainId}); Future awaitFarcasterResponse(); - Future connectEmail({required String value, String? chainId}); Future updateEmail({required String value}); Future updateEmailPrimaryOtp({required String otp}); Future updateEmailSecondaryOtp({required String otp}); Future connectOtp({required String otp}); - Future getChainId(); Future syncTheme(ReownAppKitModalTheme? theme); + Future getChainId(); Future getUser({required String? chainId}); Future switchNetwork({required String chainId}); Future request({ diff --git a/packages/reown_appkit/lib/modal/services/magic_service/magic_service.dart b/packages/reown_appkit/lib/modal/services/magic_service/magic_service.dart index b3ee5fa..977203b 100644 --- a/packages/reown_appkit/lib/modal/services/magic_service/magic_service.dart +++ b/packages/reown_appkit/lib/modal/services/magic_service/magic_service.dart @@ -4,11 +4,12 @@ import 'dart:io'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; +import 'package:get_it/get_it.dart'; +import 'package:reown_appkit/modal/services/analytics_service/i_analytics_service.dart'; import 'package:reown_appkit/modal/utils/core_utils.dart'; import 'package:reown_appkit/reown_appkit.dart'; import 'package:reown_appkit/modal/constants/string_constants.dart'; -import 'package:reown_appkit/modal/services/analytics_service/analytics_service_singleton.dart'; import 'package:reown_appkit/modal/services/analytics_service/models/analytics_event.dart'; import 'package:reown_appkit/modal/services/magic_service/models/email_login_step.dart'; import 'package:reown_appkit/modal/services/magic_service/i_magic_service.dart'; @@ -21,7 +22,7 @@ import 'package:webview_flutter_wkwebview/webview_flutter_wkwebview.dart'; import 'package:webview_flutter_android/webview_flutter_android.dart'; class MagicService implements IMagicService { - static const _thirdSafeDomains = [ + static const _thirdPartySafeDomains = [ 'auth.magic.link', 'launchdarkly.com', ]; @@ -115,6 +116,8 @@ class MagicService implements IMagicService { Event onCompleteSocialLogin = Event(); + IAnalyticsService get _analyticsService => GetIt.I(); + MagicService({ required IReownCore core, required PairingMetadata metadata, @@ -598,16 +601,16 @@ class MagicService implements IMagicService { final newStep = EmailLoginStep.fromAction(value); if (newStep == EmailLoginStep.verifyOtp) { if (step.value == EmailLoginStep.verifyDevice) { - analyticsService.instance.sendEvent(DeviceRegisteredForEmail()); + _analyticsService.sendEvent(DeviceRegisteredForEmail()); } - analyticsService.instance.sendEvent(EmailVerificationCodeSent()); + _analyticsService.sendEvent(EmailVerificationCodeSent()); } step.value = newStep; } } // ****** CONNECT_OTP if (messageData.connectOtpSuccess) { - analyticsService.instance.sendEvent(EmailVerificationCodePass()); + _analyticsService.sendEvent(EmailVerificationCodePass()); step.value = EmailLoginStep.idle; await _getUser(_connectionChainId); } @@ -619,7 +622,7 @@ class MagicService implements IMagicService { } else { step.value = EmailLoginStep.verifyOtp; } - analyticsService.instance.sendEvent(EmailEdit()); + _analyticsService.sendEvent(EmailEdit()); } // ****** UPDATE_EMAIL_PRIMARY_OTP if (messageData.updateEmailPrimarySuccess) { @@ -627,7 +630,7 @@ class MagicService implements IMagicService { } // ****** UPDATE_EMAIL_SECONDARY_OTP if (messageData.updateEmailSecondarySuccess) { - analyticsService.instance.sendEvent(EmailEditComplete()); + _analyticsService.sendEvent(EmailEditComplete()); step.value = EmailLoginStep.idle; setEmail(newEmail.value); setNewEmail(''); @@ -741,7 +744,7 @@ class MagicService implements IMagicService { _error(UpdateEmailSecondaryOtpErrorEvent(message: message)); } if (messageData.connectOtpError) { - analyticsService.instance.sendEvent(EmailVerificationCodeFail()); + _analyticsService.sendEvent(EmailVerificationCodeFail()); final message = messageData.getPayloadMapKey('message'); _error(ConnectOtpErrorEvent(message: message)); } @@ -905,7 +908,7 @@ class MagicService implements IMagicService { final domains = [ UrlConstants.secureOrigin1, UrlConstants.secureOrigin2, - ..._thirdSafeDomains, + ..._thirdPartySafeDomains, ].join('|'); return RegExp(r'' + domains).hasMatch(domain); } diff --git a/packages/reown_appkit/lib/modal/services/network_service/network_service.dart b/packages/reown_appkit/lib/modal/services/network_service/network_service.dart index 7794e95..d2bf315 100644 --- a/packages/reown_appkit/lib/modal/services/network_service/network_service.dart +++ b/packages/reown_appkit/lib/modal/services/network_service/network_service.dart @@ -1,12 +1,15 @@ import 'package:flutter/material.dart'; +import 'package:get_it/get_it.dart'; import 'package:reown_appkit/modal/models/grid_item.dart'; -import 'package:reown_appkit/modal/services/explorer_service/explorer_service_singleton.dart'; +import 'package:reown_appkit/modal/services/explorer_service/i_explorer_service.dart'; import 'package:reown_appkit/modal/services/network_service/i_network_service.dart'; import 'package:reown_appkit/modal/utils/render_utils.dart'; import 'package:reown_appkit/reown_appkit.dart'; class NetworkService implements INetworkService { + IExplorerService get _explorerService => GetIt.I(); + @override ValueNotifier initialized = ValueNotifier(false); @@ -26,7 +29,7 @@ class NetworkService implements INetworkService { final imageId = ReownAppKitModalNetworks.getNetworkIconId( chainInfo.chainId, ); - return explorerService.instance.getAssetImageUrl(imageId); + return _explorerService.getAssetImageUrl(imageId); } @override diff --git a/packages/reown_appkit/lib/modal/services/network_service/network_service_singleton.dart b/packages/reown_appkit/lib/modal/services/network_service/network_service_singleton.dart deleted file mode 100644 index ecdfa30..0000000 --- a/packages/reown_appkit/lib/modal/services/network_service/network_service_singleton.dart +++ /dev/null @@ -1,7 +0,0 @@ -import 'package:reown_appkit/modal/services/network_service/i_network_service.dart'; - -class NetworkServiceSingleton { - late INetworkService instance; -} - -final networkService = NetworkServiceSingleton(); diff --git a/packages/reown_appkit/lib/modal/services/siwe_service/siwe_service.dart b/packages/reown_appkit/lib/modal/services/siwe_service/siwe_service.dart index 807aa0a..7b91ee3 100644 --- a/packages/reown_appkit/lib/modal/services/siwe_service/siwe_service.dart +++ b/packages/reown_appkit/lib/modal/services/siwe_service/siwe_service.dart @@ -3,13 +3,14 @@ import 'dart:convert'; import 'package:collection/collection.dart'; import 'package:convert/convert.dart'; import 'package:get_it/get_it.dart'; -import 'package:reown_appkit/modal/services/coinbase_service/coinbase_service_singleton.dart'; +import 'package:reown_appkit/modal/services/coinbase_service/i_coinbase_service.dart'; import 'package:reown_appkit/modal/services/magic_service/i_magic_service.dart'; import 'package:reown_appkit/modal/services/siwe_service/i_siwe_service.dart'; import 'package:reown_appkit/reown_appkit.dart'; class SiweService implements ISiweService { IMagicService get _magicService => GetIt.I(); + ICoinbaseService get _coinbaseService => GetIt.I(); late final SIWEConfig? _siweConfig; late final IReownAppKit _appKit; @@ -105,7 +106,7 @@ class SiweService implements ISiweService { ); } if (session.sessionService.isCoinbase) { - return await coinbaseService.instance.request( + return await _coinbaseService.request( chainId: caip2Chain, request: SessionRequestParams( method: 'personal_sign', diff --git a/packages/reown_appkit/lib/modal/services/siwe_service/siwe_service_singleton.dart b/packages/reown_appkit/lib/modal/services/siwe_service/siwe_service_singleton.dart deleted file mode 100644 index 5a109ad..0000000 --- a/packages/reown_appkit/lib/modal/services/siwe_service/siwe_service_singleton.dart +++ /dev/null @@ -1,7 +0,0 @@ -import 'package:reown_appkit/modal/services/siwe_service/i_siwe_service.dart'; - -class SiweServiceSingleton { - ISiweService? instance; -} - -final siweService = SiweServiceSingleton(); diff --git a/packages/reown_appkit/lib/modal/services/toast_service/toast_service_singleton.dart b/packages/reown_appkit/lib/modal/services/toast_service/toast_service_singleton.dart deleted file mode 100644 index 64a6838..0000000 --- a/packages/reown_appkit/lib/modal/services/toast_service/toast_service_singleton.dart +++ /dev/null @@ -1,10 +0,0 @@ -import 'package:reown_appkit/modal/services/toast_service/i_toast_service.dart'; -import 'package:reown_appkit/modal/services/toast_service/toast_service.dart'; - -class ToastServiceSingleton { - IToastService instance; - - ToastServiceSingleton() : instance = ToastService(); -} - -final toastService = ToastServiceSingleton(); diff --git a/packages/reown_appkit/lib/modal/services/uri_service/url_utils_singleton.dart b/packages/reown_appkit/lib/modal/services/uri_service/url_utils_singleton.dart deleted file mode 100644 index af7eaa8..0000000 --- a/packages/reown_appkit/lib/modal/services/uri_service/url_utils_singleton.dart +++ /dev/null @@ -1,7 +0,0 @@ -import 'package:reown_appkit/modal/services/uri_service/i_url_utils.dart'; - -class UriServiceSingleton { - late IUriService instance; -} - -final uriService = UriServiceSingleton(); diff --git a/packages/reown_appkit/lib/modal/utils/public/appkit_modal_siwe_utils.dart b/packages/reown_appkit/lib/modal/utils/public/appkit_modal_siwe_utils.dart index a514c01..4c9a92d 100644 --- a/packages/reown_appkit/lib/modal/utils/public/appkit_modal_siwe_utils.dart +++ b/packages/reown_appkit/lib/modal/utils/public/appkit_modal_siwe_utils.dart @@ -1,12 +1,11 @@ -import 'package:reown_appkit/modal/services/siwe_service/siwe_service_singleton.dart'; +import 'package:get_it/get_it.dart'; +import 'package:reown_appkit/modal/services/siwe_service/i_siwe_service.dart'; import 'package:reown_appkit/reown_appkit.dart'; class SIWEUtils { /// Given SIWECreateMessageArgs will format message according to EIP-4361 https://docs.login.xyz/general-information/siwe-overview/eip-4361 static String formatMessage(SIWECreateMessageArgs params) { - return siweService.instance!.formatMessage( - params, - ); + return GetIt.I().formatMessage(params); } static String getAddressFromMessage(String message) { diff --git a/packages/reown_appkit/lib/modal/widgets/avatars/wallet_avatar.dart b/packages/reown_appkit/lib/modal/widgets/avatars/wallet_avatar.dart index 022be03..4bc9e53 100644 --- a/packages/reown_appkit/lib/modal/widgets/avatars/wallet_avatar.dart +++ b/packages/reown_appkit/lib/modal/widgets/avatars/wallet_avatar.dart @@ -1,9 +1,9 @@ import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; -import 'package:reown_appkit/modal/services/explorer_service/explorer_service_singleton.dart'; import 'package:reown_appkit/modal/theme/public/appkit_modal_theme.dart'; import 'package:reown_appkit/modal/utils/core_utils.dart'; +import 'package:reown_appkit/modal/widgets/modal_provider.dart'; class ListAvatar extends StatelessWidget { const ListAvatar({ @@ -22,10 +22,11 @@ class ListAvatar extends StatelessWidget { @override Widget build(BuildContext context) { + final appKitModal = ModalProvider.of(context).instance; final themeColors = ReownAppKitModalTheme.colorsOf(context); final radiuses = ReownAppKitModalTheme.radiusesOf(context); final radius = borderRadius ?? radiuses.radiusM; - final projectId = explorerService.instance.projectId; + final projectId = appKitModal.appKit!.core.projectId; final validImage = (imageUrl ?? '').isNotEmpty && !disabled; return Stack( children: [ diff --git a/packages/reown_appkit/lib/modal/widgets/buttons/address_copy_button.dart b/packages/reown_appkit/lib/modal/widgets/buttons/address_copy_button.dart index 47c7cee..21c4099 100644 --- a/packages/reown_appkit/lib/modal/widgets/buttons/address_copy_button.dart +++ b/packages/reown_appkit/lib/modal/widgets/buttons/address_copy_button.dart @@ -1,12 +1,13 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_svg/flutter_svg.dart'; +import 'package:get_it/get_it.dart'; +import 'package:reown_appkit/modal/services/toast_service/i_toast_service.dart'; import 'package:reown_appkit/modal/widgets/modal_provider.dart'; import 'package:reown_appkit/modal/widgets/text/appkit_address.dart'; import 'package:reown_appkit/modal/services/toast_service/models/toast_message.dart'; -import 'package:reown_appkit/modal/services/toast_service/toast_service_singleton.dart'; import 'package:reown_appkit/reown_appkit.dart'; class AddressCopyButton extends StatelessWidget { @@ -25,7 +26,7 @@ class AddressCopyButton extends StatelessWidget { ); final address = service.session!.getAddress(namespace)!; await Clipboard.setData(ClipboardData(text: address)); - toastService.instance.show(ToastMessage( + GetIt.I().show(ToastMessage( type: ToastType.success, text: 'Address copied', )); diff --git a/packages/reown_appkit/lib/modal/widgets/buttons/email_login_input_field.dart b/packages/reown_appkit/lib/modal/widgets/buttons/email_login_input_field.dart index ebed263..c3ace1b 100644 --- a/packages/reown_appkit/lib/modal/widgets/buttons/email_login_input_field.dart +++ b/packages/reown_appkit/lib/modal/widgets/buttons/email_login_input_field.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:get_it/get_it.dart'; import 'package:reown_appkit/modal/pages/confirm_email_page.dart'; -import 'package:reown_appkit/modal/services/analytics_service/analytics_service_singleton.dart'; +import 'package:reown_appkit/modal/services/analytics_service/i_analytics_service.dart'; import 'package:reown_appkit/modal/services/analytics_service/models/analytics_event.dart'; import 'package:reown_appkit/modal/services/magic_service/i_magic_service.dart'; import 'package:reown_appkit/modal/services/magic_service/models/email_login_step.dart'; @@ -18,6 +18,7 @@ class EmailLoginInputField extends StatefulWidget { class _EmailLoginInputFieldState extends State { IMagicService get _magicService => GetIt.I(); + IAnalyticsService get _analyticsService => GetIt.I(); bool _submitted = false; @override @@ -53,7 +54,7 @@ class _EmailLoginInputFieldState extends State { return InputEmailWidget( onFocus: (value) { if (value) { - analyticsService.instance.sendEvent(EmailLoginSelected()); + _analyticsService.sendEvent(EmailLoginSelected()); } }, onValueChange: (value) { @@ -63,7 +64,7 @@ class _EmailLoginInputFieldState extends State { setState(() => _submitted = true); final service = ModalProvider.of(context).instance; final chainId = service.selectedChain?.chainId; - analyticsService.instance.sendEvent(EmailSubmitted()); + _analyticsService.sendEvent(EmailSubmitted()); _magicService.connectEmail( value: value, chainId: chainId, diff --git a/packages/reown_appkit/lib/modal/widgets/buttons/network_button.dart b/packages/reown_appkit/lib/modal/widgets/buttons/network_button.dart index 6266cbf..1d1afe6 100644 --- a/packages/reown_appkit/lib/modal/widgets/buttons/network_button.dart +++ b/packages/reown_appkit/lib/modal/widgets/buttons/network_button.dart @@ -1,10 +1,11 @@ import 'package:flutter/material.dart'; +import 'package:get_it/get_it.dart'; import 'package:reown_appkit/modal/constants/string_constants.dart'; import 'package:reown_appkit/modal/models/public/appkit_network_info.dart'; -import 'package:reown_appkit/modal/services/explorer_service/explorer_service_singleton.dart'; import 'package:reown_appkit/modal/i_appkit_modal_impl.dart'; import 'package:reown_appkit/modal/constants/style_constants.dart'; +import 'package:reown_appkit/modal/services/explorer_service/i_explorer_service.dart'; import 'package:reown_appkit/modal/theme/public/appkit_modal_theme.dart'; import 'package:reown_appkit/modal/utils/public/appkit_modal_default_networks.dart'; import 'package:reown_appkit/modal/widgets/buttons/base_button.dart'; @@ -28,9 +29,10 @@ class NetworkButton extends StatelessWidget { if (chainInfo.chainIcon != null && chainInfo.chainIcon!.contains('http')) { return chainInfo.chainIcon!; } - final imageId = - ReownAppKitModalNetworks.getNetworkIconId(chainInfo.chainId); - return explorerService.instance.getAssetImageUrl(imageId); + final imageId = ReownAppKitModalNetworks.getNetworkIconId( + chainInfo.chainId, + ); + return GetIt.I().getAssetImageUrl(imageId); } @override diff --git a/packages/reown_appkit/lib/modal/widgets/icons/rounded_icon.dart b/packages/reown_appkit/lib/modal/widgets/icons/rounded_icon.dart index 27944d6..1651b4d 100644 --- a/packages/reown_appkit/lib/modal/widgets/icons/rounded_icon.dart +++ b/packages/reown_appkit/lib/modal/widgets/icons/rounded_icon.dart @@ -1,7 +1,8 @@ import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; -import 'package:reown_appkit/modal/services/explorer_service/explorer_service_singleton.dart'; +import 'package:get_it/get_it.dart'; +import 'package:reown_appkit/modal/services/explorer_service/i_explorer_service.dart'; import 'package:reown_appkit/modal/theme/public/appkit_modal_theme.dart'; import 'package:reown_appkit/modal/utils/core_utils.dart'; @@ -25,7 +26,7 @@ class RoundedIcon extends StatelessWidget { @override Widget build(BuildContext context) { final themeColors = ReownAppKitModalTheme.colorsOf(context); - final projectId = explorerService.instance.projectId; + final projectId = GetIt.I().projectId; final radius = borderRadius ?? size; return Container( width: size, diff --git a/packages/reown_appkit/lib/modal/widgets/miscellaneous/all_wallets_header.dart b/packages/reown_appkit/lib/modal/widgets/miscellaneous/all_wallets_header.dart index 2d6e5f2..d76ef46 100644 --- a/packages/reown_appkit/lib/modal/widgets/miscellaneous/all_wallets_header.dart +++ b/packages/reown_appkit/lib/modal/widgets/miscellaneous/all_wallets_header.dart @@ -1,8 +1,9 @@ import 'package:flutter/material.dart'; +import 'package:get_it/get_it.dart'; import 'package:reown_appkit/modal/pages/public/appkit_modal_qrcode_page.dart'; import 'package:reown_appkit/modal/services/analytics_service/models/analytics_event.dart'; -import 'package:reown_appkit/modal/services/explorer_service/explorer_service_singleton.dart'; import 'package:reown_appkit/modal/constants/style_constants.dart'; +import 'package:reown_appkit/modal/services/explorer_service/i_explorer_service.dart'; import 'package:reown_appkit/modal/widgets/icons/themed_icon.dart'; import 'package:reown_appkit/modal/widgets/miscellaneous/searchbar.dart'; import 'package:reown_appkit/modal/widgets/widget_stack/widget_stack_singleton.dart'; @@ -21,12 +22,12 @@ class AllWalletsHeader extends StatelessWidget { child: ModalSearchBar( hint: 'Search wallet', onTextChanged: (value) { - explorerService.instance.search(query: value); + GetIt.I().search(query: value); }, onDismissKeyboard: (clear) { FocusManager.instance.primaryFocus?.unfocus(); if (clear) { - explorerService.instance.search(query: null); + GetIt.I().search(query: null); } }, ), diff --git a/packages/reown_appkit/lib/modal/widgets/miscellaneous/verify_otp_view.dart b/packages/reown_appkit/lib/modal/widgets/miscellaneous/verify_otp_view.dart index d02c099..d76fe03 100644 --- a/packages/reown_appkit/lib/modal/widgets/miscellaneous/verify_otp_view.dart +++ b/packages/reown_appkit/lib/modal/widgets/miscellaneous/verify_otp_view.dart @@ -1,9 +1,10 @@ import 'package:collection/collection.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; +import 'package:get_it/get_it.dart'; import 'package:reown_appkit/modal/constants/style_constants.dart'; +import 'package:reown_appkit/modal/services/toast_service/i_toast_service.dart'; import 'package:reown_appkit/modal/services/toast_service/models/toast_message.dart'; -import 'package:reown_appkit/modal/services/toast_service/toast_service_singleton.dart'; import 'package:reown_appkit/modal/widgets/miscellaneous/searchbar.dart'; import 'package:reown_appkit/reown_appkit.dart'; @@ -68,7 +69,7 @@ class _VerifyOtpViewState extends State void _resendEmail() async { final diff = DateTime.now().difference(_resendEnabledAt).inSeconds; if (diff < 0) { - toastService.instance.show(ToastMessage( + GetIt.I().show(ToastMessage( type: ToastType.error, text: 'Try again after ${diff.abs()} seconds', )); @@ -76,7 +77,7 @@ class _VerifyOtpViewState extends State final email = widget.currentEmail; widget.resendEmail(value: email); _resendEnabledAt = DateTime.now().add(Duration(seconds: 30)); - toastService.instance.show(ToastMessage( + GetIt.I().show(ToastMessage( type: ToastType.success, text: 'Code email resent', )); diff --git a/packages/reown_appkit/lib/modal/widgets/navigation/navbar.dart b/packages/reown_appkit/lib/modal/widgets/navigation/navbar.dart index 817f58f..8f5c6d3 100644 --- a/packages/reown_appkit/lib/modal/widgets/navigation/navbar.dart +++ b/packages/reown_appkit/lib/modal/widgets/navigation/navbar.dart @@ -14,7 +14,7 @@ class ModalNavbar extends StatelessWidget { required this.body, required this.title, this.leftAction, - this.rightAction, + this.rightActions = const [], this.safeAreaLeft = false, this.safeAreaRight = false, this.safeAreaBottom = true, @@ -28,7 +28,7 @@ class ModalNavbar extends StatelessWidget { final Widget body; final String title; final NavbarActionButton? leftAction; - final NavbarActionButton? rightAction; + final List rightActions; final bool safeAreaLeft, safeAreaRight, safeAreaBottom, @@ -90,7 +90,9 @@ class ModalNavbar extends StatelessWidget { ModalProvider.of(context).instance.closeModal(); }, ), - rightAction ?? SizedBox.shrink(), + Row( + children: rightActions, + ), ], ); }, diff --git a/packages/reown_appkit/lib/modal/widgets/navigation/navbar_action_button.dart b/packages/reown_appkit/lib/modal/widgets/navigation/navbar_action_button.dart index 7c0f998..d06fd82 100644 --- a/packages/reown_appkit/lib/modal/widgets/navigation/navbar_action_button.dart +++ b/packages/reown_appkit/lib/modal/widgets/navigation/navbar_action_button.dart @@ -9,18 +9,22 @@ class NavbarActionButton extends StatelessWidget { required this.asset, required this.action, this.color, + this.dimension = kNavbarHeight, }); final String asset; final VoidCallback action; final Color? color; + final double dimension; @override Widget build(BuildContext context) { final themeColors = ReownAppKitModalTheme.colorsOf(context); return SizedBox.square( - dimension: kNavbarHeight, + dimension: dimension, child: IconButton( onPressed: action, + padding: const EdgeInsets.all(0.0), + visualDensity: VisualDensity.compact, icon: SvgPicture.asset( asset, package: 'reown_appkit', diff --git a/packages/reown_appkit/lib/modal/widgets/public/appkit_modal_account_button.dart b/packages/reown_appkit/lib/modal/widgets/public/appkit_modal_account_button.dart index 0200e57..dbe9452 100644 --- a/packages/reown_appkit/lib/modal/widgets/public/appkit_modal_account_button.dart +++ b/packages/reown_appkit/lib/modal/widgets/public/appkit_modal_account_button.dart @@ -3,7 +3,7 @@ import 'package:get_it/get_it.dart'; import 'package:reown_appkit/modal/pages/approve_magic_request_page.dart'; import 'package:reown_appkit/modal/pages/confirm_email_page.dart'; import 'package:reown_appkit/modal/pages/social_login_page.dart'; -import 'package:reown_appkit/modal/services/explorer_service/explorer_service_singleton.dart'; +import 'package:reown_appkit/modal/services/explorer_service/i_explorer_service.dart'; import 'package:reown_appkit/modal/services/magic_service/i_magic_service.dart'; import 'package:reown_appkit/modal/services/magic_service/models/magic_events.dart'; import 'package:reown_appkit/modal/i_appkit_modal_impl.dart'; @@ -196,7 +196,7 @@ class _BalanceButton extends StatelessWidget { : themeData.textStyles.paragraph600; final chainId = appKit.selectedChain?.chainId ?? ''; final imageId = ReownAppKitModalNetworks.getNetworkIconId(chainId); - final tokenImage = explorerService.instance.getAssetImageUrl(imageId); + final tokenImage = GetIt.I().getAssetImageUrl(imageId); return BaseButton( size: BaseButtonSize.small, onTap: onTap, diff --git a/packages/reown_appkit/lib/modal/widgets/public/appkit_modal_balance_button.dart b/packages/reown_appkit/lib/modal/widgets/public/appkit_modal_balance_button.dart index 70acba5..aeae759 100644 --- a/packages/reown_appkit/lib/modal/widgets/public/appkit_modal_balance_button.dart +++ b/packages/reown_appkit/lib/modal/widgets/public/appkit_modal_balance_button.dart @@ -1,7 +1,8 @@ import 'package:flutter/material.dart'; +import 'package:get_it/get_it.dart'; import 'package:reown_appkit/modal/constants/style_constants.dart'; -import 'package:reown_appkit/modal/services/explorer_service/explorer_service_singleton.dart'; import 'package:reown_appkit/modal/i_appkit_modal_impl.dart'; +import 'package:reown_appkit/modal/services/explorer_service/i_explorer_service.dart'; import 'package:reown_appkit/modal/theme/public/appkit_modal_theme.dart'; import 'package:reown_appkit/modal/utils/public/appkit_modal_default_networks.dart'; import 'package:reown_appkit/modal/widgets/buttons/base_button.dart'; @@ -45,7 +46,7 @@ class _AppKitModalBalanceButtonState extends State { setState(() { final chainId = widget.appKitModal.selectedChain?.chainId ?? '1'; final imageId = ReownAppKitModalNetworks.getNetworkIconId(chainId); - _tokenImage = explorerService.instance.getAssetImageUrl(imageId); + _tokenImage = GetIt.I().getAssetImageUrl(imageId); }); } diff --git a/packages/reown_appkit/lib/modal/widgets/public/appkit_modal_network_select_button.dart b/packages/reown_appkit/lib/modal/widgets/public/appkit_modal_network_select_button.dart index 8aeae6e..069ff8b 100644 --- a/packages/reown_appkit/lib/modal/widgets/public/appkit_modal_network_select_button.dart +++ b/packages/reown_appkit/lib/modal/widgets/public/appkit_modal_network_select_button.dart @@ -1,8 +1,9 @@ import 'package:flutter/material.dart'; +import 'package:get_it/get_it.dart'; import 'package:reown_appkit/modal/models/public/appkit_network_info.dart'; import 'package:reown_appkit/modal/pages/public/appkit_modal_select_network_page.dart'; -import 'package:reown_appkit/modal/services/analytics_service/analytics_service_singleton.dart'; +import 'package:reown_appkit/modal/services/analytics_service/i_analytics_service.dart'; import 'package:reown_appkit/modal/services/analytics_service/models/analytics_event.dart'; import 'package:reown_appkit/modal/i_appkit_modal_impl.dart'; import 'package:reown_appkit/modal/widgets/widget_stack/widget_stack_singleton.dart'; @@ -57,7 +58,7 @@ class _AppKitModalNetworkSelectButtonState } void _onConnectPressed() { - analyticsService.instance.sendEvent(ClickNetworksEvent()); + GetIt.I().sendEvent(ClickNetworksEvent()); widget.appKit.openModalView( ReownAppKitModalSelectNetworkPage( onTapNetwork: (info) { diff --git a/packages/reown_appkit/lib/modal/widgets/toast/toast.dart b/packages/reown_appkit/lib/modal/widgets/toast/toast.dart index ed3f6f7..cdbe8d5 100644 --- a/packages/reown_appkit/lib/modal/widgets/toast/toast.dart +++ b/packages/reown_appkit/lib/modal/widgets/toast/toast.dart @@ -1,7 +1,8 @@ import 'package:flutter/material.dart'; +import 'package:get_it/get_it.dart'; +import 'package:reown_appkit/modal/services/toast_service/i_toast_service.dart'; import 'package:reown_appkit/modal/theme/public/appkit_modal_theme.dart'; import 'package:reown_appkit/modal/services/toast_service/models/toast_message.dart'; -import 'package:reown_appkit/modal/services/toast_service/toast_service_singleton.dart'; class ToastWidget extends StatefulWidget { const ToastWidget({ @@ -37,7 +38,7 @@ class _ToastWidgetState extends State if (!mounted) { return; } - _controller.reverse().then((value) => toastService.instance.clear()); + _controller.reverse().then((value) => GetIt.I().clear()); }); }); } diff --git a/packages/reown_appkit/lib/modal/widgets/toast/toast_presenter.dart b/packages/reown_appkit/lib/modal/widgets/toast/toast_presenter.dart index fe25e5a..93daccf 100644 --- a/packages/reown_appkit/lib/modal/widgets/toast/toast_presenter.dart +++ b/packages/reown_appkit/lib/modal/widgets/toast/toast_presenter.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; +import 'package:get_it/get_it.dart'; +import 'package:reown_appkit/modal/services/toast_service/i_toast_service.dart'; import 'package:reown_appkit/modal/services/toast_service/models/toast_message.dart'; -import 'package:reown_appkit/modal/services/toast_service/toast_service_singleton.dart'; import 'package:reown_appkit/modal/widgets/toast/toast.dart'; class ToastPresenter extends StatelessWidget { @@ -9,7 +10,7 @@ class ToastPresenter extends StatelessWidget { @override Widget build(BuildContext context) { return StreamBuilder( - stream: toastService.instance.toasts, + stream: GetIt.I().toasts, builder: (context, snapshot) { if (snapshot.hasData && snapshot.data != null) { return ToastWidget(message: snapshot.data!); diff --git a/packages/reown_appkit/lib/modal/widgets/value_listenable_builders/explorer_service_items_listener.dart b/packages/reown_appkit/lib/modal/widgets/value_listenable_builders/explorer_service_items_listener.dart index cd64279..4ba4569 100644 --- a/packages/reown_appkit/lib/modal/widgets/value_listenable_builders/explorer_service_items_listener.dart +++ b/packages/reown_appkit/lib/modal/widgets/value_listenable_builders/explorer_service_items_listener.dart @@ -1,7 +1,8 @@ import 'package:flutter/material.dart'; +import 'package:get_it/get_it.dart'; import 'package:reown_appkit/modal/models/grid_item.dart'; -import 'package:reown_appkit/modal/services/explorer_service/explorer_service_singleton.dart'; +import 'package:reown_appkit/modal/services/explorer_service/i_explorer_service.dart'; import 'package:reown_appkit/reown_appkit.dart'; class ExplorerServiceItemsListener extends StatefulWidget { @@ -26,23 +27,24 @@ class ExplorerServiceItemsListener extends StatefulWidget { class _ExplorerServiceItemsListenerState extends State { List> _items = []; + IExplorerService get _explorerService => GetIt.I(); @override Widget build(BuildContext context) { return ValueListenableBuilder( - valueListenable: explorerService.instance.initialized, + valueListenable: _explorerService.initialized, builder: (context, initialised, _) { if (!initialised) { return widget.builder(context, initialised, [], false); } return ValueListenableBuilder( - valueListenable: explorerService.instance.isSearching, + valueListenable: _explorerService.isSearching, builder: (context, searching, _) { if (searching) { return widget.builder(context, initialised, _items, searching); } return ValueListenableBuilder>( - valueListenable: explorerService.instance.listings, + valueListenable: _explorerService.listings, builder: (context, items, _) { if (widget.listen) { _items = items.toGridItems(); @@ -65,7 +67,7 @@ extension on List { GridItem( title: item.listing.name, id: item.listing.id, - image: explorerService.instance.getWalletImageUrl( + image: GetIt.I().getWalletImageUrl( item.listing.imageId, ), data: item, diff --git a/packages/reown_appkit/lib/modal/widgets/value_listenable_builders/network_service_items_listener.dart b/packages/reown_appkit/lib/modal/widgets/value_listenable_builders/network_service_items_listener.dart index 6ec0714..1d2eacd 100644 --- a/packages/reown_appkit/lib/modal/widgets/value_listenable_builders/network_service_items_listener.dart +++ b/packages/reown_appkit/lib/modal/widgets/value_listenable_builders/network_service_items_listener.dart @@ -1,12 +1,14 @@ import 'package:flutter/material.dart'; +import 'package:get_it/get_it.dart'; import 'package:reown_appkit/modal/models/grid_item.dart'; import 'package:reown_appkit/modal/models/public/appkit_network_info.dart'; -import 'package:reown_appkit/modal/services/network_service/network_service_singleton.dart'; +import 'package:reown_appkit/modal/services/network_service/i_network_service.dart'; import 'package:reown_appkit/modal/utils/public/appkit_modal_default_networks.dart'; import 'package:reown_appkit/modal/widgets/modal_provider.dart'; class NetworkServiceItemsListener extends StatelessWidget { + INetworkService get _networkService => GetIt.I(); const NetworkServiceItemsListener({ super.key, required this.builder, @@ -20,13 +22,13 @@ class NetworkServiceItemsListener extends StatelessWidget { @override Widget build(BuildContext context) { return ValueListenableBuilder( - valueListenable: networkService.instance.initialized, + valueListenable: _networkService.initialized, builder: (context, bool initialised, _) { if (!initialised) { return builder(context, initialised, []); } return ValueListenableBuilder( - valueListenable: networkService.instance.itemList, + valueListenable: _networkService.itemList, builder: (context, items, _) { final parsedItems = items.parseItems(context); return builder(context, initialised, parsedItems); diff --git a/packages/reown_appkit/lib/modal/widgets/widget_stack/widget_stack.dart b/packages/reown_appkit/lib/modal/widgets/widget_stack/widget_stack.dart index eae8f53..6cec8d0 100644 --- a/packages/reown_appkit/lib/modal/widgets/widget_stack/widget_stack.dart +++ b/packages/reown_appkit/lib/modal/widgets/widget_stack/widget_stack.dart @@ -1,7 +1,8 @@ import 'package:flutter/material.dart'; +import 'package:get_it/get_it.dart'; import 'package:reown_appkit/modal/pages/public/appkit_modal_main_wallets_page.dart'; -import 'package:reown_appkit/modal/services/analytics_service/analytics_service_singleton.dart'; +import 'package:reown_appkit/modal/services/analytics_service/i_analytics_service.dart'; import 'package:reown_appkit/modal/services/analytics_service/models/analytics_event.dart'; import 'package:reown_appkit/modal/utils/platform_utils.dart'; import 'package:reown_appkit/modal/widgets/widget_stack/i_widget_stack.dart'; @@ -23,7 +24,7 @@ class WidgetStack extends IWidgetStack { AnalyticsEvent? event, }) { if (event != null) { - analyticsService.instance.sendEvent(event); + GetIt.I().sendEvent(event); } onRenderScreen.value = renderScreen; if (replace) { From 3d52a53a6b80ea2673943630f3ac87d3ba41f918 Mon Sep 17 00:00:00 2001 From: Alfreedom <00tango.bromine@icloud.com> Date: Tue, 22 Oct 2024 12:45:24 +0100 Subject: [PATCH 05/17] bug fixes --- packages/reown_appkit/CHANGELOG.md | 4 +- .../reown_appkit/example/base/lib/main.dart | 229 +++++++++---- .../example/base/lib/pages/connect_page.dart | 311 ++++++++++-------- .../example/base/lib/utils/crypto/eip155.dart | 12 +- .../example/modal/lib/home_page.dart | 49 ++- .../modal/lib/services/methods_service.dart | 43 ++- .../modal/lib/widgets/logger_widget.dart | 7 +- .../lib/modal/appkit_modal_impl.dart | 201 ++++++----- .../lib/modal/i_appkit_modal_impl.dart | 10 +- .../models/public/appkit_modal_session.dart | 9 +- .../lib/modal/pages/account_page.dart | 63 +++- .../appkit_modal_select_network_page.dart | 5 + .../analytics_service/analytics_service.dart | 9 +- .../blockchain_service.dart | 82 +---- .../coinbase_service/coinbase_service.dart | 14 + .../explorer_service/explorer_service.dart | 9 +- .../services/magic_service/magic_service.dart | 19 +- .../public/appkit_modal_default_networks.dart | 9 +- .../modal/widgets/buttons/network_button.dart | 9 +- .../lists/list_items/account_list_item.dart | 28 +- .../public/appkit_modal_account_button.dart | 40 ++- .../public/appkit_modal_balance_button.dart | 5 + .../network_service_items_listener.dart | 2 +- packages/reown_appkit/lib/version.dart | 2 +- packages/reown_appkit/pubspec.yaml | 14 +- .../chain_services/evm_service.dart | 54 +++ .../chain_services/kadena_service.dart | 2 +- .../chain_services/solana_service.dart | 52 +++ .../chain_services/solana_service_2.dart | 52 +++ .../dependencies/key_service/key_service.dart | 8 +- .../lib/dependencies/walletkit_service.dart | 2 +- .../reown_walletkit/example/lib/main.dart | 10 +- .../example/lib/models/chain_data.dart | 2 +- .../example/lib/pages/settings_page.dart | 108 +++++- 34 files changed, 954 insertions(+), 521 deletions(-) diff --git a/packages/reown_appkit/CHANGELOG.md b/packages/reown_appkit/CHANGELOG.md index 7301ae1..636eed5 100644 --- a/packages/reown_appkit/CHANGELOG.md +++ b/packages/reown_appkit/CHANGELOG.md @@ -1,6 +1,6 @@ -## 1.1.0-beta03 +## 1.2.0-beta01 -- Social Logins +- Non-EVM Chains support ## 1.0.1 diff --git a/packages/reown_appkit/example/base/lib/main.dart b/packages/reown_appkit/example/base/lib/main.dart index ffc0178..4188b0a 100644 --- a/packages/reown_appkit/example/base/lib/main.dart +++ b/packages/reown_appkit/example/base/lib/main.dart @@ -1,5 +1,5 @@ import 'dart:convert'; -import 'dart:developer'; +import 'dart:io'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; @@ -7,13 +7,14 @@ import 'package:reown_appkit/reown_appkit.dart'; import 'package:reown_appkit_dapp/models/page_data.dart'; import 'package:reown_appkit_dapp/pages/connect_page.dart'; import 'package:reown_appkit_dapp/pages/pairings_page.dart'; -import 'package:reown_appkit_dapp/pages/sessions_page.dart'; import 'package:reown_appkit_dapp/utils/constants.dart'; import 'package:reown_appkit_dapp/utils/crypto/helpers.dart'; import 'package:reown_appkit_dapp/utils/dart_defines.dart'; import 'package:reown_appkit_dapp/utils/deep_link_handler.dart'; import 'package:reown_appkit_dapp/utils/string_constants.dart'; import 'package:reown_appkit_dapp/widgets/event_widget.dart'; +// ignore: depend_on_referenced_packages +import 'package:shared_preferences/shared_preferences.dart'; void main() { WidgetsFlutterBinding.ensureInitialized(); @@ -21,18 +22,55 @@ void main() { runApp(const MyApp()); } -class MyApp extends StatelessWidget { +class MyApp extends StatefulWidget { const MyApp({super.key}); - // This widget is the root of your application. + @override + State createState() => _MyAppState(); +} + +class _MyAppState extends State with WidgetsBindingObserver { + bool _isDarkMode = false; + + @override + void initState() { + super.initState(); + WidgetsBinding.instance.addObserver(this); + WidgetsBinding.instance.addPostFrameCallback((_) { + setState(() { + final platformDispatcher = View.of(context).platformDispatcher; + final platformBrightness = platformDispatcher.platformBrightness; + _isDarkMode = platformBrightness == Brightness.dark; + }); + }); + } + + @override + void dispose() { + WidgetsBinding.instance.removeObserver(this); + super.dispose(); + } + + @override + void didChangePlatformBrightness() { + if (mounted) { + setState(() { + final platformDispatcher = View.of(context).platformDispatcher; + final platformBrightness = platformDispatcher.platformBrightness; + _isDarkMode = platformBrightness == Brightness.dark; + }); + } + super.didChangePlatformBrightness(); + } + @override Widget build(BuildContext context) { - return MaterialApp( - title: StringConstants.appTitle, - theme: ThemeData( - primarySwatch: Colors.blue, + return ReownAppKitModalTheme( + isDarkMode: _isDarkMode, + child: MaterialApp( + title: StringConstants.appTitle, + home: const MyHomePage(), ), - home: const MyHomePage(), ); } } @@ -45,8 +83,6 @@ class MyHomePage extends StatefulWidget { } class _MyHomePageState extends State { - bool _initializing = true; - ReownAppKit? _appKit; ReownAppKitModal? _appKitModal; @@ -98,13 +134,11 @@ class _MyHomePageState extends State { _appKit = ReownAppKit( core: ReownCore( projectId: DartDefines.projectId, - logLevel: LogLevel.error, + logLevel: LogLevel.debug, ), metadata: _pairingMetadata(), ); - _appKit!.core.addLogListener(_logListener); - // Register event handlers _appKit!.core.relayClient.onRelayClientError.subscribe( _relayClientError, @@ -122,13 +156,40 @@ class _MyHomePageState extends State { _appKit!.onSessionAuthResponse.subscribe(_onSessionAuthResponse); // See https://docs.reown.com/appkit/flutter/core/custom-chains - // final testNetworks = ReownAppKitModalNetworks.test['eip155'] ?? []; - // ReownAppKitModalNetworks.addNetworks('eip155', testNetworks); + // final extraChains = ReownAppKitModalNetworks.extra['eip155']!; + // ReownAppKitModalNetworks.addSupportedNetworks('eip155', extraChains); + // ReownAppKitModalNetworks.removeSupportedNetworks('solana'); + // ReownAppKitModalNetworks.removeTestNetworks(); + + final prefs = await SharedPreferences.getInstance(); + final linkMode = prefs.getBool('appkit_sample_linkmode') ?? false; + if (!linkMode) { + ReownAppKitModalNetworks.addSupportedNetworks('polkadot', [ + ReownAppKitModalNetworkInfo( + name: 'Polkadot', + chainId: '91b171bb158e2d3848fa23a9f1c25182', + chainIcon: 'https://cryptologos.cc/logos/polkadot-new-dot-logo.png', + currency: 'DOT', + rpcUrl: 'https://rpc.polkadot.io', + explorerUrl: 'https://polkadot.subscan.io', + ), + ReownAppKitModalNetworkInfo( + name: 'Westend', + chainId: 'e143f23803ac50e8f6f8e62695d1ce9e', + currency: 'DOT', + rpcUrl: 'https://westend-rpc.polkadot.io', + explorerUrl: 'https://westend.subscan.io', + isTestNetwork: true, + ), + ]); + } else { + ReownAppKitModalNetworks.removeSupportedNetworks('solana'); + } _appKitModal = ReownAppKitModal( context: context, appKit: _appKit, - siweConfig: _siweConfig(), + siweConfig: linkMode ? _siweConfig() : null, enableAnalytics: true, featuresConfig: FeaturesConfig( email: true, @@ -138,7 +199,7 @@ class _MyHomePageState extends State { AppKitSocialOption.Apple, AppKitSocialOption.Discord, ], - showMainWallets: false, + showMainWallets: false, // OPTIONAL - true by default ), // requiredNamespaces: {}, // optionalNamespaces: {}, @@ -155,6 +216,38 @@ class _MyHomePageState extends State { // 'fd20dc426fb37566d803205b19bbc1d4096b248ac04548e3cfb6b3a38bd033aa', // Coinbase // }, // MORE WALLETS https://explorer.walletconnect.com/?type=wallet&chains=eip155%3A1 + optionalNamespaces: !linkMode + ? { + // This is needed if more chains besides EVM and Solana are supported + // mostly because we can not define internally every possible method for every possible chain + 'eip155': RequiredNamespace.fromJson({ + 'chains': ReownAppKitModalNetworks.getAllSupportedNetworks( + namespace: 'eip155', + ).map((chain) => 'eip155:${chain.chainId}').toList(), + 'methods': + NetworkUtils.defaultNetworkMethods['eip155']!.toList(), + 'events': NetworkUtils.defaultNetworkEvents['eip155']!.toList(), + }), + 'solana': RequiredNamespace.fromJson({ + 'chains': ReownAppKitModalNetworks.getAllSupportedNetworks( + namespace: 'solana', + ).map((chain) => 'solana:${chain.chainId}').toList(), + 'methods': + NetworkUtils.defaultNetworkMethods['solana']!.toList(), + 'events': [], + }), + 'polkadot': RequiredNamespace.fromJson({ + 'chains': ReownAppKitModalNetworks.getAllSupportedNetworks( + namespace: 'polkadot', + ).map((chain) => 'polkadot:${chain.chainId}').toList(), + 'methods': [ + 'polkadot_signMessage', + 'polkadot_signTransaction', + ], + 'events': [] + }), + } + : null, ); _appKitModal!.onModalConnect.subscribe(_onModalConnect); @@ -163,6 +256,53 @@ class _MyHomePageState extends State { _appKitModal!.onModalDisconnect.subscribe(_onModalDisconnect); _appKitModal!.onModalError.subscribe(_onModalError); + _pageDatas = [ + PageData( + page: ConnectPage( + appKitModal: _appKitModal!, + linkMode: linkMode, + reinitialize: (bool linkMode) async { + final result = await showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + content: Text('App will be closed to apply changes'), + actions: [ + TextButton( + onPressed: () => Navigator.of(context).pop(false), + child: Text('Cancel'), + ), + TextButton( + onPressed: () => Navigator.of(context).pop(true), + child: Text('Ok'), + ), + ], + ); + }, + ); + if (result == true) { + await prefs.setBool('appkit_sample_linkmode', linkMode); + if (!kDebugMode) { + exit(0); + } + } + }, + ), + title: StringConstants.connectPageTitle, + icon: Icons.home, + ), + PageData( + page: PairingsPage(appKitModal: _appKitModal!), + title: StringConstants.pairingsPageTitle, + icon: Icons.vertical_align_center_rounded, + ), + // PageData( + // page: SessionsPage(appKitModal: _appKitModal!), + // title: StringConstants.sessionsPageTitle, + // icon: Icons.workspaces_filled, + // ), + ]; + await _appKitModal!.init(); await _registerEventHandlers(); @@ -183,28 +323,6 @@ class _MyHomePageState extends State { ); } } - - setState(() { - _pageDatas = [ - PageData( - page: ConnectPage(appKitModal: _appKitModal!), - title: StringConstants.connectPageTitle, - icon: Icons.home, - ), - PageData( - page: PairingsPage(appKitModal: _appKitModal!), - title: StringConstants.pairingsPageTitle, - icon: Icons.vertical_align_center_rounded, - ), - PageData( - page: SessionsPage(appKitModal: _appKitModal!), - title: StringConstants.sessionsPageTitle, - icon: Icons.workspaces_filled, - ), - ]; - - _initializing = false; - }); } Future _registerEventHandlers() async { @@ -233,21 +351,13 @@ class _MyHomePageState extends State { void _onSessionConnect(SessionConnect? event) { debugPrint('[SampleDapp] _onSessionConnect $event'); - Future.delayed(const Duration(milliseconds: 500), () { - setState(() => _selectedIndex = 2); - }); } void _onSessionAuthResponse(SessionAuthResponse? response) { debugPrint('[SampleDapp] _onSessionAuthResponse $response'); - if (response?.session != null) { - Future.delayed(const Duration(milliseconds: 500), () { - setState(() => _selectedIndex = 2); - }); - } } - void _setState(dynamic args) => setState(() {}); + void _setState(_) => setState(() {}); void _relayClientError(ErrorEvent? event) { debugPrint('[SampleDapp] _relayClientError ${event?.error}'); @@ -257,8 +367,6 @@ class _MyHomePageState extends State { @override void dispose() { // Unregister event handlers - _appKit!.core.removeLogListener(_logListener); - _appKit!.core.relayClient.onRelayClientError.unsubscribe( _relayClientError, ); @@ -283,26 +391,11 @@ class _MyHomePageState extends State { super.dispose(); } - void _logListener(event) { - if ('${event.level}' == 'Level.debug' || - '${event.level}' == 'Level.error') { - // TODO send to mixpanel - log('${event.message}'); - } else { - debugPrint('${event.message}'); - } - } - @override Widget build(BuildContext context) { - if (_initializing) { - return const Center( - child: CircularProgressIndicator( - color: StyleConstants.primaryColor, - ), - ); + if (_pageDatas.isEmpty) { + return Center(child: CircularProgressIndicator()); } - final List navRail = []; if (MediaQuery.of(context).size.width >= Constants.smallScreen) { navRail.add(_buildNavigationRail()); diff --git a/packages/reown_appkit/example/base/lib/pages/connect_page.dart b/packages/reown_appkit/example/base/lib/pages/connect_page.dart index 17ddbfb..9ffd002 100644 --- a/packages/reown_appkit/example/base/lib/pages/connect_page.dart +++ b/packages/reown_appkit/example/base/lib/pages/connect_page.dart @@ -13,7 +13,7 @@ import 'package:reown_appkit_dapp/utils/crypto/eip155.dart'; import 'package:reown_appkit_dapp/utils/crypto/helpers.dart'; import 'package:reown_appkit_dapp/utils/crypto/polkadot.dart'; import 'package:reown_appkit_dapp/utils/crypto/solana.dart'; -import 'package:reown_appkit_dapp/utils/sample_wallets.dart'; +// import 'package:reown_appkit_dapp/utils/sample_wallets.dart'; import 'package:reown_appkit_dapp/utils/string_constants.dart'; import 'package:reown_appkit_dapp/widgets/chain_button.dart'; import 'package:reown_appkit_dapp/widgets/method_dialog.dart'; @@ -22,9 +22,13 @@ class ConnectPage extends StatefulWidget { const ConnectPage({ super.key, required this.appKitModal, + required this.reinitialize, + this.linkMode = false, }); final ReownAppKitModal appKitModal; + final Function(bool linkMode) reinitialize; + final bool linkMode; @override ConnectPageState createState() => ConnectPageState(); @@ -33,7 +37,6 @@ class ConnectPage extends StatefulWidget { class ConnectPageState extends State { final List _selectedChains = []; bool _shouldDismissQrCode = true; - bool _linkMode = false; @override void initState() { @@ -135,9 +138,7 @@ class ConnectPageState extends State { @override Widget build(BuildContext context) { // Build the list of chain buttons, clear if the textnet changed - final allChains = ReownAppKitModalNetworks.getAllSupportedNetworks( - namespace: _linkMode ? 'eip155' : null, - ); + final allChains = ReownAppKitModalNetworks.getAllSupportedNetworks(); final mainChains = allChains.where((e) => !e.isTestNetwork).toList(); final testChains = allChains.where((e) => e.isTestNetwork).toList(); @@ -202,7 +203,22 @@ class ConnectPageState extends State { child: Column( children: [ AppKitModalAccountButton( - appKit: widget.appKitModal, + appKitModal: widget.appKitModal, + ), + const SizedBox.square(dimension: 8.0), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + AppKitModalBalanceButton( + appKitModal: widget.appKitModal, + onTap: widget.appKitModal.openNetworksView, + ), + const SizedBox.square(dimension: 8.0), + AppKitModalAddressButton( + appKitModal: widget.appKitModal, + onTap: widget.appKitModal.openModalView, + ), + ], ), const SizedBox.square(dimension: 8.0), ...(_buildRequestButtons()), @@ -214,151 +230,163 @@ class ConnectPageState extends State { visible: !widget.appKitModal.isConnected, child: Column( children: [ - Row( - children: [ - Expanded( - child: const Divider(height: 1.0), - ), - const Text( - ' Or ', - style: StyleConstants.buttonText, - textAlign: TextAlign.center, - ), - Expanded( - child: const Divider(height: 1.0), - ), - ], - ), - const SizedBox(height: StyleConstants.linear16), - const Text( - 'Custom connection', - style: StyleConstants.buttonText, - textAlign: TextAlign.center, - ), + // Row( + // children: [ + // Expanded( + // child: const Divider(height: 1.0), + // ), + // const Text( + // ' Or ', + // style: StyleConstants.buttonText, + // textAlign: TextAlign.center, + // ), + // Expanded( + // child: const Divider(height: 1.0), + // ), + // ], + // ), + // const SizedBox(height: StyleConstants.linear16), + // const Text( + // 'Custom connection', + // style: StyleConstants.buttonText, + // textAlign: TextAlign.center, + // ), Row( mainAxisAlignment: MainAxisAlignment.center, children: [ - Text('Link Mode'), + Text( + 'non-EVM', + style: TextStyle( + fontWeight: !widget.linkMode + ? FontWeight.bold + : FontWeight.normal, + ), + ), Switch( - value: _linkMode, + value: widget.linkMode, onChanged: (value) { - requiredNamespaces = {}; - optionalNamespaces = {}; - _selectedChains.clear(); - setState(() => _linkMode = value); + widget.reinitialize(value); }, ), + Text( + 'Link Mode', + style: TextStyle( + fontWeight: + widget.linkMode ? FontWeight.bold : FontWeight.normal, + ), + ), ], ), - const SizedBox(height: StyleConstants.linear16), - Wrap( - spacing: 10.0, - children: chainButtons, - ), - // const Divider(), - const Text('Test chains'), - Wrap( - spacing: 10.0, - children: testButtons, - ), - const SizedBox(height: StyleConstants.linear16), - // const Divider(), - !_linkMode - ? Column( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - const Text( - 'Session Propose:', - style: StyleConstants.buttonText, - ), - const SizedBox(height: StyleConstants.linear8), - Column( - children: - WCSampleWallets.getSampleWallets().map((wallet) { - return Padding( - padding: const EdgeInsets.only(bottom: 8.0), - child: ElevatedButton( - style: _buttonStyle, - onPressed: _selectedChains.isEmpty - ? null - : () { - _onConnect( - nativeLink: '${wallet['schema']}', - closeModal: () { - if (Navigator.canPop(context)) { - Navigator.of(context).pop(); - } - }, - showToast: (m) async { - showPlatformToast( - child: Text(m), - context: context, - ); - }, - ); - }, - child: Text( - '${wallet['name']}', - style: StyleConstants.buttonText, - ), - ), - ); - }).toList(), - ), - ], - ) - : Column( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - const Text( - 'Link Mode:', - style: StyleConstants.buttonText, - ), - const SizedBox(height: StyleConstants.linear8), - Column( - children: - WCSampleWallets.getSampleWallets().map((wallet) { - return Padding( - padding: const EdgeInsets.only(bottom: 8.0), - child: ElevatedButton( - style: _buttonStyle, - onPressed: _selectedChains.isEmpty - ? null - : () { - _sessionAuthenticate( - nativeLink: '${wallet['schema']}', - universalLink: - '${wallet['universal']}', - closeModal: () { - if (Navigator.canPop(context)) { - Navigator.of(context).pop(); - } - }, - showToast: (message) { - showPlatformToast( - child: Text(message), - context: context, - ); - }, - ); - }, - child: Text( - '${wallet['name']}', - style: StyleConstants.buttonText, - ), - ), - ); - }).toList(), - ), - ], - ), - const SizedBox(height: StyleConstants.linear16), - const Divider(height: 1.0), - const SizedBox(height: StyleConstants.linear8), - _FooterWidget(appKitModal: widget.appKitModal), + // const SizedBox(height: StyleConstants.linear16), + // Wrap( + // spacing: 10.0, + // children: chainButtons, + // ), + // // const Divider(), + // const Text('Test chains'), + // Wrap( + // spacing: 10.0, + // children: testButtons, + // ), + // const SizedBox(height: StyleConstants.linear16), + // // const Divider(), + // !_linkMode + // ? Column( + // crossAxisAlignment: CrossAxisAlignment.center, + // children: [ + // const Text( + // 'Session Propose:', + // style: StyleConstants.buttonText, + // ), + // const SizedBox(height: StyleConstants.linear8), + // Column( + // children: + // WCSampleWallets.getSampleWallets().map((wallet) { + // return Padding( + // padding: const EdgeInsets.only(bottom: 8.0), + // child: ElevatedButton( + // style: _buttonStyle, + // onPressed: _selectedChains.isEmpty + // ? null + // : () { + // _onConnect( + // nativeLink: '${wallet['schema']}', + // closeModal: () { + // if (Navigator.canPop(context)) { + // Navigator.of(context).pop(); + // } + // }, + // showToast: (m) async { + // showPlatformToast( + // child: Text(m), + // context: context, + // ); + // }, + // ); + // }, + // child: Text( + // '${wallet['name']}', + // style: StyleConstants.buttonText, + // ), + // ), + // ); + // }).toList(), + // ), + // ], + // ) + // : Column( + // crossAxisAlignment: CrossAxisAlignment.center, + // children: [ + // const Text( + // 'Link Mode:', + // style: StyleConstants.buttonText, + // ), + // const SizedBox(height: StyleConstants.linear8), + // Column( + // children: + // WCSampleWallets.getSampleWallets().map((wallet) { + // return Padding( + // padding: const EdgeInsets.only(bottom: 8.0), + // child: ElevatedButton( + // style: _buttonStyle, + // onPressed: _selectedChains.isEmpty + // ? null + // : () { + // _sessionAuthenticate( + // nativeLink: '${wallet['schema']}', + // universalLink: + // '${wallet['universal']}', + // closeModal: () { + // if (Navigator.canPop(context)) { + // Navigator.of(context).pop(); + // } + // }, + // showToast: (message) { + // showPlatformToast( + // child: Text(message), + // context: context, + // ); + // }, + // ); + // }, + // child: Text( + // '${wallet['name']}', + // style: StyleConstants.buttonText, + // ), + // ), + // ); + // }).toList(), + // ), + // ], + // ), ], ), ), + const SizedBox(height: StyleConstants.linear16), + const Divider(height: 1.0), + const SizedBox(height: StyleConstants.linear8), + _FooterWidget(appKitModal: widget.appKitModal), + const SizedBox(height: StyleConstants.linear8), ], ); } @@ -444,12 +472,13 @@ class ConnectPageState extends State { Function(String message)? showToast, }) async { debugPrint( - '[SampleDapp] Creating authenticate with $nativeLink, $universalLink'); + '[SampleDapp] Creating authentication with $nativeLink, $universalLink', + ); final methods1 = requiredNamespaces['eip155']?.methods ?? []; final methods2 = optionalNamespaces['eip155']?.methods ?? []; final authResponse = await widget.appKitModal.appKit!.authenticate( params: SessionAuthRequestParams( - chains: _selectedChains.map((e) => e.chainId).toList(), + chains: _selectedChains.map((e) => 'eip155:${e.chainId}').toList(), domain: Uri.parse(widget.appKitModal.appKit!.metadata.url).authority, nonce: AuthUtils.generateNonce(), uri: widget.appKitModal.appKit!.metadata.url, diff --git a/packages/reown_appkit/example/base/lib/utils/crypto/eip155.dart b/packages/reown_appkit/example/base/lib/utils/crypto/eip155.dart index 70aac59..594bb28 100644 --- a/packages/reown_appkit/example/base/lib/utils/crypto/eip155.dart +++ b/packages/reown_appkit/example/base/lib/utils/crypto/eip155.dart @@ -109,7 +109,7 @@ class EIP155 { case 'write': return appKit.requestWriteContract( topic: topic, - chainId: sepolia.chainId, + chainId: 'eip155:${sepolia.chainId}', deployedContract: deployedContract, functionName: 'transfer', transaction: Transaction( @@ -137,7 +137,7 @@ class EIP155 { }) async { return await appKit.request( topic: topic, - chainId: chainId, + chainId: 'eip155:$chainId', request: (await getParams('personal_sign', address))!, ); } @@ -150,7 +150,7 @@ class EIP155 { }) async { return await appKit.request( topic: topic, - chainId: chainId, + chainId: 'eip155:$chainId', request: (await getParams('eth_sign', address))!, ); } @@ -163,7 +163,7 @@ class EIP155 { }) async { return await appKit.request( topic: topic, - chainId: chainId, + chainId: 'eip155:$chainId', request: (await getParams('eth_signTypedData', address))!, ); } @@ -175,7 +175,7 @@ class EIP155 { }) async { return await appKit.request( topic: topic, - chainId: chainId, + chainId: 'eip155:$chainId', request: (await getParams('eth_signTransaction', ''))!, ); } @@ -187,7 +187,7 @@ class EIP155 { }) async { return await appKit.request( topic: topic, - chainId: chainId, + chainId: 'eip155:$chainId', request: (await getParams('eth_sendTransaction', ''))!, ); } diff --git a/packages/reown_appkit/example/modal/lib/home_page.dart b/packages/reown_appkit/example/modal/lib/home_page.dart index 620bca8..d4d9652 100644 --- a/packages/reown_appkit/example/modal/lib/home_page.dart +++ b/packages/reown_appkit/example/modal/lib/home_page.dart @@ -44,7 +44,6 @@ class _MyHomePageState extends State { super.initState(); _siweTestService = SIWESampleWebService(); WidgetsBinding.instance.addPostFrameCallback((_) { - // _toggleOverlay(); _initializeService(widget.prefs); }); } @@ -54,8 +53,6 @@ class _MyHomePageState extends State { } String get _flavor { - // String flavor = '-${const String.fromEnvironment('FLUTTER_APP_FLAVOR')}'; - // return flavor.replaceAll('-production', ''); final internal = widget.bundleId.endsWith('.internal'); final debug = widget.bundleId.endsWith('.debug'); if (internal || debug || kDebugMode) { @@ -237,20 +234,22 @@ class _MyHomePageState extends State { _appKitModal = ReownAppKitModal( context: context, projectId: DartDefines.projectId, - logLevel: LogLevel.error, + logLevel: LogLevel.all, metadata: _pairingMetadata(), siweConfig: _siweConfig(siweAuthValue), enableAnalytics: analyticsValue, // OPTIONAL - null by default - featuresConfig: FeaturesConfig( - email: emailWalletValue, - socials: [ - AppKitSocialOption.Farcaster, - AppKitSocialOption.X, - AppKitSocialOption.Apple, - AppKitSocialOption.Discord, - ], - showMainWallets: false, // OPTIONAL - true by default - ), + featuresConfig: emailWalletValue + ? FeaturesConfig( + email: true, + socials: [ + AppKitSocialOption.Farcaster, + AppKitSocialOption.X, + AppKitSocialOption.Apple, + AppKitSocialOption.Discord, + ], + showMainWallets: false, // OPTIONAL - true by default + ) + : null, // requiredNamespaces: {}, // optionalNamespaces: {}, // includedWalletIds: {}, @@ -264,8 +263,10 @@ class _MyHomePageState extends State { }, // excludedWalletIds: {}, // MORE WALLETS https://explorer.walletconnect.com/?type=wallet&chains=eip155%3A1 - // getBalance: () async { - // // Your own balance function + // getBalanceFallback: () async { + // // This method will be triggered if getting the balance from our blockchain API fails + // // You could place here your own getBalance method + // return 0.123; // }, optionalNamespaces: { 'eip155': RequiredNamespace.fromJson({ @@ -325,7 +326,6 @@ class _MyHomePageState extends State { _appKitModal.appKit!.core.relayClient.onRelayClientDisconnect.subscribe( _onRelayClientDisconnect, ); - _appKitModal.appKit!.core.addLogListener(_logListener); // await _appKitModal.init(); @@ -335,20 +335,9 @@ class _MyHomePageState extends State { setState(() {}); } - void _logListener(event) { - if ('${event.level}' == 'Level.debug' || - '${event.level}' == 'Level.error') { - // TODO send to mixpanel - log('${event.message}'); - } else { - debugPrint('${event.message}'); - } - } - @override void dispose() { // - _appKitModal.appKit!.core.removeLogListener(_logListener); _appKitModal.appKit!.core.relayClient.onRelayClientConnect.unsubscribe( _onRelayClientConnect, ); @@ -422,7 +411,7 @@ class _MyHomePageState extends State { showTextToast(text: 'Relay connected', context: context); } - void _onRelayClientError(EventArgs? event) { + void _onRelayClientError(ErrorEvent? event) { setState(() {}); showTextToast(text: 'Relay disconnected', context: context); } @@ -557,7 +546,7 @@ class _ConnectedView extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.center, children: [ AppKitModalAccountButton( - appKit: appKit, + appKitModal: appKit, // custom: ValueListenableBuilder( // valueListenable: appKit.balanceNotifier, // builder: (_, balance, __) { diff --git a/packages/reown_appkit/example/modal/lib/services/methods_service.dart b/packages/reown_appkit/example/modal/lib/services/methods_service.dart index 8a0c6c5..5728772 100644 --- a/packages/reown_appkit/example/modal/lib/services/methods_service.dart +++ b/packages/reown_appkit/example/modal/lib/services/methods_service.dart @@ -5,6 +5,8 @@ import 'package:reown_appkit/reown_appkit.dart'; // ignore: depend_on_referenced_packages import 'package:convert/convert.dart'; +// ignore: depend_on_referenced_packages +import 'package:bs58/bs58.dart'; import 'package:reown_appkit_example/services/contracts/aave_contract.dart'; import 'package:reown_appkit_example/services/contracts/test_data.dart'; @@ -18,8 +20,7 @@ enum SupportedMethods { ethSignTypedDataV4, ethSignTransaction, walletWatchAsset, - solanaSignMessage, - solanaSignTransaction; + solanaSignMessage; String get name { switch (this) { @@ -41,8 +42,6 @@ enum SupportedMethods { return 'wallet_watchAsset'; case solanaSignMessage: return 'solana_signMessage'; - case solanaSignTransaction: - return 'solana_signTransaction'; } } } @@ -66,6 +65,8 @@ class MethodsService { return SupportedMethods.ethSignTransaction; case 'wallet_watchAsset': return SupportedMethods.walletWatchAsset; + case 'solana_signMessage': + return SupportedMethods.solanaSignMessage; default: throw Exception('Method not implemented'); } @@ -78,7 +79,7 @@ class MethodsService { required String chainId, required String address, }) { - final cid = int.parse(chainId); + // final cid = int.parse(chainId); switch (method) { case SupportedMethods.requestAccounts: return requestAccounts( @@ -92,7 +93,7 @@ class MethodsService { case SupportedMethods.ethSignTypedDataV3: return ethSignTypedDataV3( appKitModal: appKitModal, - data: jsonEncode(typeDataV3(cid)), + data: jsonEncode(typeDataV3(int.parse(chainId))), ); case SupportedMethods.ethSignTypedData: return ethSignTypedData( @@ -102,7 +103,7 @@ class MethodsService { case SupportedMethods.ethSignTypedDataV4: return ethSignTypedDataV4( appKitModal: appKitModal, - data: jsonEncode(typeDataV4(cid)), + data: jsonEncode(typeDataV4(int.parse(chainId))), ); case SupportedMethods.ethSignTransaction: case SupportedMethods.ethSendTransaction: @@ -123,9 +124,10 @@ class MethodsService { appKitModal: appKitModal, ); case SupportedMethods.solanaSignMessage: - throw Exception('Method not implemented'); - case SupportedMethods.solanaSignTransaction: - throw Exception('Method not implemented'); + return solanaSignMessage( + appKitModal: appKitModal, + message: testSignData, + ); } } @@ -165,6 +167,27 @@ class MethodsService { ); } + static Future solanaSignMessage({ + required ReownAppKitModal appKitModal, + required String message, + }) async { + final bytes = utf8.encode(testSignData); + final message = base58.encode(bytes); + final namespace = ReownAppKitModalNetworks.getNamespaceForChainId( + appKitModal.selectedChain!.chainId, + ); + final address = appKitModal.session!.getAddress(namespace)!; + + return await appKitModal.request( + topic: appKitModal.session!.topic, + chainId: appKitModal.selectedChain!.chainId, + request: SessionRequestParams( + method: SupportedMethods.solanaSignMessage.name, + params: {'pubkey': address, 'message': message}, + ), + ); + } + static Future ethSignTypedData({ required ReownAppKitModal appKitModal, required String data, diff --git a/packages/reown_appkit/example/modal/lib/widgets/logger_widget.dart b/packages/reown_appkit/example/modal/lib/widgets/logger_widget.dart index cee506e..cdbd8f5 100644 --- a/packages/reown_appkit/example/modal/lib/widgets/logger_widget.dart +++ b/packages/reown_appkit/example/modal/lib/widgets/logger_widget.dart @@ -25,11 +25,12 @@ class _DraggableCardState extends State { ); } - void _eventsListener(event) { + void _eventsListener(String event) { if (!mounted) return; - if (!event.message.toString().contains('[AnalyticsService]')) return; - String message = '${event.message}'.replaceAll('[AnalyticsService] ', ''); + if (!event.toString().contains('[AnalyticsService]')) return; + String message = event.replaceAll('[AnalyticsService] ', ''); message = message.replaceAll('send event 202: ', ''); + message = message.replaceAll('info: ', ''); _logs.add( Text( '=> $message', diff --git a/packages/reown_appkit/lib/modal/appkit_modal_impl.dart b/packages/reown_appkit/lib/modal/appkit_modal_impl.dart index 8f6648f..ddce504 100644 --- a/packages/reown_appkit/lib/modal/appkit_modal_impl.dart +++ b/packages/reown_appkit/lib/modal/appkit_modal_impl.dart @@ -134,6 +134,7 @@ class ReownAppKitModal with ChangeNotifier implements IReownAppKitModal { /// late final Future Function()? _getBalance; + Completer _awaitRelayOnce = Completer(); ReownAppKitModal({ required BuildContext context, @@ -148,7 +149,7 @@ class ReownAppKitModal with ChangeNotifier implements IReownAppKitModal { Set? featuredWalletIds, Set? includedWalletIds, Set? excludedWalletIds, - Future Function()? getBalance, + Future Function()? getBalanceFallback, LogLevel logLevel = LogLevel.nothing, }) { if (appKit == null) { @@ -167,7 +168,7 @@ class ReownAppKitModal with ChangeNotifier implements IReownAppKitModal { this.featuresConfig = featuresConfig ?? FeaturesConfig(email: false); _context = context; - _getBalance = getBalance; + _getBalance = getBalanceFallback; _appKit = appKit ?? ReownAppKit( @@ -179,25 +180,25 @@ class ReownAppKitModal with ChangeNotifier implements IReownAppKitModal { ); _projectId = _appKit.core.projectId; + // TODO should be moved to init() _setRequiredNamespaces(requiredNamespaces); _setOptionalNamespaces(optionalNamespaces); - GetIt.I.registerSingleton( - UriService( - core: _appKit.core, - ), + GetIt.I.registerSingletonIfAbsent( + () => UriService(core: _appKit.core), ); - GetIt.I.registerSingletonAsync(() async { - final analyticsService = AnalyticsService( + GetIt.I.registerSingletonIfAbsent( + () => AnalyticsService( core: _appKit.core, enableAnalytics: enableAnalytics, - ); - await analyticsService.init(); - analyticsService.sendEvent(ModalLoadedEvent()); - return analyticsService; - }); - GetIt.I.registerSingleton( - ExplorerService( + ), + ); + // TODO should be moved to init() + _analyticsService.init().then( + (_) => _analyticsService.sendEvent(ModalLoadedEvent()), + ); + GetIt.I.registerSingletonIfAbsent( + () => ExplorerService( core: _appKit.core, referer: _appKit.metadata.name.replaceAll(' ', ''), featuredWalletIds: featuredWalletIds, @@ -206,14 +207,13 @@ class ReownAppKitModal with ChangeNotifier implements IReownAppKitModal { namespaces: {..._requiredNamespaces, ..._optionalNamespaces}, ), ); - GetIt.I.registerSingleton(NetworkService()); - GetIt.I.registerSingleton(ToastService()); - GetIt.I.registerSingleton( - BlockChainService( + GetIt.I.registerSingletonIfAbsent(() => NetworkService()); + GetIt.I.registerSingletonIfAbsent(() => ToastService()); + GetIt.I.registerSingletonIfAbsent( + () => BlockChainService( core: _appKit.core, ), ); - GetIt.I.registerSingletonIfAbsent( () => MagicService( core: _appKit.core, @@ -221,15 +221,15 @@ class ReownAppKitModal with ChangeNotifier implements IReownAppKitModal { featuresConfig: this.featuresConfig, ), ); - GetIt.I.registerSingleton( - CoinbaseService( + GetIt.I.registerSingletonIfAbsent( + () => CoinbaseService( core: _appKit.core, metadata: _appKit.metadata, enabled: _initializeCoinbaseSDK, ), ); - GetIt.I.registerSingleton( - SiweService( + GetIt.I.registerSingletonIfAbsent( + () => SiweService( appKit: _appKit, siweConfig: siweConfig, namespaces: {..._requiredNamespaces, ..._optionalNamespaces}, @@ -266,8 +266,6 @@ class ReownAppKitModal with ChangeNotifier implements IReownAppKitModal { return false; } - Completer _awaitRelayOnce = Completer(); - @override Future init() async { _relayConnected = false; @@ -367,6 +365,7 @@ class ReownAppKitModal with ChangeNotifier implements IReownAppKitModal { await _selectChainFromStoredId(); onModalNetworkChange.subscribe(_onNetworkChainRequireSIWE); + onModalConnect.subscribe(_loadAccountData); _relayConnected = _appKit.core.relayClient.isConnected; if (!_relayConnected) { @@ -407,11 +406,8 @@ class ReownAppKitModal with ChangeNotifier implements IReownAppKitModal { await _storeSession(mSession); _currentSelectedChainId = _currentSelectedChainId ?? mSession.chainId; await _setLocalEthChain(_currentSelectedChainId!, logEvent: false); - } catch (e, s) { - _appKit.core.logger.d( - '[$runtimeType] _setSesionAndChainData error $e', - stackTrace: s, - ); + } catch (e) { + _appKit.core.logger.e('[$runtimeType] _setSesionAndChainData error $e'); } } @@ -423,11 +419,8 @@ class ReownAppKitModal with ChangeNotifier implements IReownAppKitModal { return ReownAppKitModalSession.fromMap(storedSession); } } - } catch (e, s) { - _appKit.core.logger.d( - '[$runtimeType] _getStoredSession error: $e', - stackTrace: s, - ); + } catch (e) { + _appKit.core.logger.e('[$runtimeType] _getStoredSession error: $e'); } return null; } @@ -439,11 +432,8 @@ class ReownAppKitModal with ChangeNotifier implements IReownAppKitModal { StorageConstants.modalSession, _currentSession!.toMap(), ); - } catch (e, s) { - _appKit.core.logger.d( - '[$runtimeType] _storeSession error: $e', - stackTrace: s, - ); + } catch (e) { + _appKit.core.logger.e('[$runtimeType] _storeSession error: $e'); } // _isConnected shoudl probably go at the very end of the connection _isConnected = true; @@ -488,6 +478,9 @@ class ReownAppKitModal with ChangeNotifier implements IReownAppKitModal { } _chainBalance = null; + final tokenName = chainInfo.currency; + final formattedBalance = CoreUtils.formatChainBalance(_chainBalance); + balanceNotifier.value = '$formattedBalance $tokenName'; final hasValidSession = _isConnected && _currentSession != null; if (switchChain && hasValidSession && _currentSelectedChainId != null) { @@ -516,55 +509,41 @@ class ReownAppKitModal with ChangeNotifier implements IReownAppKitModal { } /// Will get the list of available chains to add + @Deprecated('User getApprovedChains()') @override List? getAvailableChains() { - // if there's no session or if supportsAddChain method then every chain can be used - final evmHasSwitch = _currentSession?.hasSwitchMethod() ?? false; - if ((_currentSession == null || - (evmHasSwitch && _currentSession!.sessionService.isWC))) { - return null; - } - final namespace = ReownAppKitModalNetworks.getNamespaceForChainId( - _currentSelectedChainId!, - ); - return getApprovedChains(namespace: namespace); + return getApprovedChains(); } /// Will get the list of already approved chains by the wallet (to switch to) @override - List? getApprovedChains({required String? namespace}) { + List? getApprovedChains({String? namespace}) { if (_currentSession == null) { return null; } - return _currentSession!.getApprovedChains( - namespace: namespace, - ); + return _currentSession!.getApprovedChains(namespace: namespace); } @override - List? getApprovedMethods({required String? namespace}) { + List? getApprovedMethods({String? namespace}) { if (_currentSession == null) { return null; } - return _currentSession!.getApprovedMethods( - namespace: namespace, - ); + return _currentSession!.getApprovedMethods(namespace: namespace); } @override - List? getApprovedEvents({required String? namespace}) { + List? getApprovedEvents({String? namespace}) { if (_currentSession == null) { return null; } - return _currentSession!.getApprovedEvents( - namespace: namespace, - ); + return _currentSession!.getApprovedEvents(namespace: namespace); } Future _setLocalEthChain(String chainId, {bool? logEvent}) async { _currentSelectedChainId = chainId; final caip2Chain = ReownAppKitModalNetworks.getCaip2Chain(chainId); - _appKit.core.logger.i('[$runtimeType] set local chain $caip2Chain'); + _appKit.core.logger.d('[$runtimeType] _setLocalEthChain $caip2Chain'); _notify(); try { if (isConnected) { @@ -573,11 +552,8 @@ class ReownAppKitModal with ChangeNotifier implements IReownAppKitModal { {'chainId': _currentSelectedChainId!}, ); } - } catch (e, s) { - _appKit.core.logger.d( - '[$runtimeType] _setLocalEthChain error: $e', - stackTrace: s, - ); + } catch (e) { + _appKit.core.logger.e('[$runtimeType] _setLocalEthChain error: $e'); } if (_isConnected && logEvent == true) { _analyticsService.sendEvent(SwitchNetworkEvent( @@ -592,7 +568,7 @@ class ReownAppKitModal with ChangeNotifier implements IReownAppKitModal { } _lastChainEmitted = caip2Chain; } - loadAccountData(); + // loadAccountData(); } @override @@ -884,11 +860,8 @@ class ReownAppKitModal with ChangeNotifier implements IReownAppKitModal { _notify(); _awaitConnectionCallback(connectResponse); } - } catch (e, s) { - _appKit.core.logger.d( - '[$runtimeType] buildConnectionUri error: $e', - stackTrace: s, - ); + } catch (e) { + _appKit.core.logger.e('[$runtimeType] buildConnectionUri error: $e'); } } } @@ -1157,8 +1130,9 @@ class ReownAppKitModal with ChangeNotifier implements IReownAppKitModal { } } // - _appKit.core.logger - .t('[$runtimeType] requestWriteContract, chainId: $reqChainId'); + _appKit.core.logger.i( + '[$runtimeType] requestReadContract, chainId: $reqChainId', + ); final networkInfo = ReownAppKitModalNetworks.getNetworkById( reqChainId.split(':').first, @@ -1209,8 +1183,9 @@ class ReownAppKitModal with ChangeNotifier implements IReownAppKitModal { } } // - _appKit.core.logger - .t('[$runtimeType] requestWriteContract, chainId: $reqChainId'); + _appKit.core.logger.i( + '[$runtimeType] requestWriteContract, chainId: $reqChainId', + ); try { return await _appKit.requestWriteContract( @@ -1250,7 +1225,7 @@ class ReownAppKitModal with ChangeNotifier implements IReownAppKitModal { } } // - _appKit.core.logger.t( + _appKit.core.logger.d( '[$runtimeType] request, chainId: $reqChainId, ' '${jsonEncode(request.toJson())}', ); @@ -1295,10 +1270,21 @@ class ReownAppKitModal with ChangeNotifier implements IReownAppKitModal { @override Future dispose() async { if (_status == ReownAppKitModalStatus.initialized) { - await disconnect(); - await expirePreviousInactivePairings(); _unregisterListeners(); + await expirePreviousInactivePairings(); + await disconnect(); + await _appKit.core.relayClient.disconnect(); + _relayConnected = false; + _isConnected = false; + _currentSelectedChainId = null; + _requiredNamespaces = {}; + _optionalNamespaces = {}; + _lastChainEmitted = null; + _supportsOneClickAuth = false; + _relayConnected = false; _status = ReownAppKitModalStatus.idle; + await Future.delayed(Duration(milliseconds: 500)); + _notify(); } super.dispose(); } @@ -1363,7 +1349,7 @@ class ReownAppKitModal with ChangeNotifier implements IReownAppKitModal { // Set the required namespaces to everything in our chain presets _requiredNamespaces = {}; } - _appKit.core.logger.i( + _appKit.core.logger.d( '[$runtimeType] required namespaces ${jsonEncode(_requiredNamespaces)}', ); } @@ -1396,7 +1382,7 @@ class ReownAppKitModal with ChangeNotifier implements IReownAppKitModal { } } - _appKit.core.logger.i( + _appKit.core.logger.d( '[$runtimeType] optional namespaces ${jsonEncode(_optionalNamespaces)}', ); } @@ -1416,16 +1402,21 @@ class ReownAppKitModal with ChangeNotifier implements IReownAppKitModal { ); try { - _chainBalance = await (_getBalance?.call() ?? - _blockchainService.getBalance( - address: _currentSession!.getAddress(namespace)!, - namespace: namespace, - chainId: _currentSelectedChainId!, - )); + _chainBalance = await _blockchainService.getBalance( + address: _currentSession!.getAddress(namespace)!, + namespace: namespace, + chainId: _currentSelectedChainId!, + ); + final tokenName = selectedChain?.currency ?? ''; + final formattedBalance = CoreUtils.formatChainBalance(_chainBalance); + balanceNotifier.value = '$formattedBalance $tokenName'; + } catch (_) { + // Calling getBalanceFallback defined by user + _chainBalance = await _getBalance?.call(); final tokenName = selectedChain?.currency ?? ''; final formattedBalance = CoreUtils.formatChainBalance(_chainBalance); balanceNotifier.value = '$formattedBalance $tokenName'; - } catch (_) {} + } if (namespace == NetworkUtils.eip155) { // Get the avatar, each chainId is just a number in string form. @@ -1482,7 +1473,7 @@ class ReownAppKitModal with ChangeNotifier implements IReownAppKitModal { // Otherwise it meas chain has to be added. return await requestAddChain(newChain); } catch (e, s) { - _appKit.core.logger.d( + _appKit.core.logger.e( '[$runtimeType] requestSwitchToChain error: $e', stackTrace: s, ); @@ -1501,9 +1492,7 @@ class ReownAppKitModal with ChangeNotifier implements IReownAppKitModal { final newCaip2Chain = ReownAppKitModalNetworks.getCaip2Chain( newChain.chainId, ); - _appKit.core.logger.i( - '[$runtimeType] requesting switch to add chain $newCaip2Chain', - ); + _appKit.core.logger.i('[$runtimeType] requesting add chain $newCaip2Chain'); try { await request( topic: topic, @@ -1518,7 +1507,7 @@ class ReownAppKitModal with ChangeNotifier implements IReownAppKitModal { await _setSesionAndChainData(_currentSession!); return; } catch (e, s) { - _appKit.core.logger.d( + _appKit.core.logger.e( '[$runtimeType] requestAddChain error: $e', stackTrace: s, ); @@ -1587,6 +1576,7 @@ class ReownAppKitModal with ChangeNotifier implements IReownAppKitModal { } void _onNetworkChainRequireSIWE(ModalNetworkChange? args) async { + if (_siweService.config?.enabled != true) return; try { if (_siweService.signOutOnNetworkChange) { await _siweService.signOut(); @@ -1596,13 +1586,17 @@ class ReownAppKitModal with ChangeNotifier implements IReownAppKitModal { )); } } catch (e, s) { - _appKit.core.logger.d( + _appKit.core.logger.e( '[$runtimeType] _onNetworkChainRequireSIWE error: $e', stackTrace: s, ); } } + void _loadAccountData(ModalConnect? event) { + loadAccountData(); + } + void _checkInitialized() { if (_status != ReownAppKitModalStatus.initialized && _status != ReownAppKitModalStatus.initializing) { @@ -1768,7 +1762,7 @@ extension _EmailConnectorExtension on ReownAppKitModal { await _setSesionAndChainData(session); onModalUpdate.broadcast(ModalConnect(session)); } catch (e, s) { - _appKit.core.logger.d( + _appKit.core.logger.e( '[$runtimeType] _onMagicUpdateEvent error: $e', stackTrace: s, ); @@ -1829,8 +1823,9 @@ extension _CoinbaseConnectorExtension on ReownAppKitModal { } void _onCoinbaseSessionUpdateEvent(CoinbaseSessionEvent? args) async { - _appKit.core.logger - .d('[$runtimeType] _onCoinbaseSessionUpdateEvent: $args'); + _appKit.core.logger.d( + '[$runtimeType] _onCoinbaseSessionUpdateEvent: $args', + ); if (args != null) { try { final chainId = args.chainId ?? _currentSession!.chainId; @@ -1853,7 +1848,7 @@ extension _CoinbaseConnectorExtension on ReownAppKitModal { await _setSesionAndChainData(session); onModalUpdate.broadcast(ModalConnect(session)); } catch (e, s) { - _appKit.core.logger.d( + _appKit.core.logger.e( '[$runtimeType] _onCoinbaseSessionUpdateEvent error: $e', stackTrace: s, ); @@ -1976,7 +1971,7 @@ extension _AppKitModalExtension on ReownAppKitModal { {'chainId': _currentSelectedChainId!}, ); } catch (e, s) { - _appKit.core.logger.d( + _appKit.core.logger.e( '[$runtimeType] _oneSIWEFinish error: $e', stackTrace: s, ); @@ -2000,7 +1995,7 @@ extension _AppKitModalExtension on ReownAppKitModal { await _siweService.signOut(); } } catch (e, s) { - _appKit.core.logger.d( + _appKit.core.logger.e( '[$runtimeType] _onSessionEvent error: $e', stackTrace: s, ); diff --git a/packages/reown_appkit/lib/modal/i_appkit_modal_impl.dart b/packages/reown_appkit/lib/modal/i_appkit_modal_impl.dart index 6e081bf..cc54c89 100644 --- a/packages/reown_appkit/lib/modal/i_appkit_modal_impl.dart +++ b/packages/reown_appkit/lib/modal/i_appkit_modal_impl.dart @@ -101,19 +101,17 @@ abstract class IReownAppKitModal with ChangeNotifier { void launchConnectedWallet(); /// List of available chains to be added in connected wallet + @Deprecated('User getApprovedChains()') List? getAvailableChains(); - // TODO NON-EVM Support: make namespace non nullable /// List of approved chains by connected wallet - List? getApprovedChains({required String? namespace}); + List? getApprovedChains({String? namespace}); - // TODO NON-EVM Support: make namespace non nullable /// List of approved methods by connected wallet - List? getApprovedMethods({required String? namespace}); + List? getApprovedMethods({String? namespace}); - // TODO NON-EVM Support: make namespace non nullable /// List of approved events by connected wallet - List? getApprovedEvents({required String? namespace}); + List? getApprovedEvents({String? namespace}); /// Loads/Refresh account balance and identity Future loadAccountData(); diff --git a/packages/reown_appkit/lib/modal/models/public/appkit_modal_session.dart b/packages/reown_appkit/lib/modal/models/public/appkit_modal_session.dart index 97e4371..c9a95e7 100644 --- a/packages/reown_appkit/lib/modal/models/public/appkit_modal_session.dart +++ b/packages/reown_appkit/lib/modal/models/public/appkit_modal_session.dart @@ -103,8 +103,7 @@ class ReownAppKitModalSession { return supportsAddChain; } - // TODO NON-EVM Support: make namespace non nullable - List? getApprovedMethods({required String? namespace}) { + List? getApprovedMethods({String? namespace}) { final methodsList = []; if (sessionService.noSession) { @@ -131,7 +130,7 @@ class ReownAppKitModalSession { return sessionNamespaces[namespace]?.methods ?? []; } - List? getApprovedEvents({required String? namespace}) { + List? getApprovedEvents({String? namespace}) { final eventsList = []; if (sessionService.noSession) { @@ -157,7 +156,7 @@ class ReownAppKitModalSession { return sessionNamespaces[namespace]?.events ?? []; } - List? getApprovedChains({required String? namespace}) { + List? getApprovedChains({String? namespace}) { if (sessionService.noSession) { return null; } @@ -184,7 +183,7 @@ class ReownAppKitModalSession { return NamespaceUtils.getChainsFromAccounts(accounts); } - List? getAccounts({required String? namespace}) { + List? getAccounts({String? namespace}) { final accountList = []; if (sessionService.noSession) { diff --git a/packages/reown_appkit/lib/modal/pages/account_page.dart b/packages/reown_appkit/lib/modal/pages/account_page.dart index 2d9c12a..2baeb3b 100644 --- a/packages/reown_appkit/lib/modal/pages/account_page.dart +++ b/packages/reown_appkit/lib/modal/pages/account_page.dart @@ -1,3 +1,4 @@ +import 'package:cached_network_image/cached_network_image.dart'; import 'package:collection/collection.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; @@ -128,8 +129,12 @@ class _DefaultAccountView extends StatelessWidget { ), Visibility( visible: isEmailLogin, - child: _EmailLoginButton(), + child: _EmailAndSocialLoginButton(), ), + // Visibility( + // visible: !isEmailLogin, + // child: _ConnectedWalletButton(), + // ), const SizedBox.square(dimension: kPadding8), _SelectNetworkButton(), const SizedBox.square(dimension: kPadding8), @@ -199,7 +204,7 @@ class _UpgradeWalletButton extends StatelessWidget { } } -class _EmailLoginButton extends StatelessWidget { +class _EmailAndSocialLoginButton extends StatelessWidget { @override Widget build(BuildContext context) { final service = ModalProvider.of(context).instance; @@ -252,6 +257,60 @@ class _EmailLoginButton extends StatelessWidget { } } +class _ConnectedWalletButton extends StatelessWidget { + @override + Widget build(BuildContext context) { + final service = ModalProvider.of(context).instance; + final themeData = ReownAppKitModalTheme.getDataOf(context); + final themeColors = ReownAppKitModalTheme.colorsOf(context); + final radiuses = ReownAppKitModalTheme.radiusesOf(context); + String iconImage = ''; + if ((service.session!.peer?.metadata.icons ?? []).isNotEmpty) { + iconImage = service.session!.peer?.metadata.icons.first ?? ''; + } + final walletInfo = GetIt.I().getConnectedWallet(); + return Column( + children: [ + const SizedBox.square(dimension: kPadding8), + AccountListItem( + iconWidget: Padding( + padding: const EdgeInsets.symmetric(horizontal: 4.0), + child: iconImage.isEmpty + ? RoundedIcon( + assetPath: 'lib/modal/assets/icons/wallet.svg', + assetColor: themeColors.inverse100, + borderRadius: radiuses.isSquare() ? 0.0 : null, + ) + : ClipRRect( + borderRadius: radiuses.isSquare() + ? BorderRadius.zero + : BorderRadius.circular(34), + child: CachedNetworkImage( + imageUrl: iconImage, + height: 34, + width: 34, + errorWidget: (context, url, error) { + return RoundedIcon( + assetPath: 'lib/modal/assets/icons/wallet.svg', + assetColor: themeColors.inverse100, + borderRadius: radiuses.isSquare() ? 0.0 : null, + ); + }, + ), + ), + ), + title: service.session!.peer?.metadata.name ?? 'Connected Wallet', + titleStyle: themeData.textStyles.paragraph500.copyWith( + color: themeColors.foreground100, + ), + onTap: + walletInfo != null ? () => service.launchConnectedWallet() : null, + ), + ], + ); + } +} + class _SelectNetworkButton extends StatelessWidget { @override Widget build(BuildContext context) { diff --git a/packages/reown_appkit/lib/modal/pages/public/appkit_modal_select_network_page.dart b/packages/reown_appkit/lib/modal/pages/public/appkit_modal_select_network_page.dart index 95c989e..b29468b 100644 --- a/packages/reown_appkit/lib/modal/pages/public/appkit_modal_select_network_page.dart +++ b/packages/reown_appkit/lib/modal/pages/public/appkit_modal_select_network_page.dart @@ -6,6 +6,7 @@ import 'package:reown_appkit/modal/pages/about_networks.dart'; import 'package:reown_appkit/modal/pages/connet_network_page.dart'; import 'package:reown_appkit/modal/services/analytics_service/models/analytics_event.dart'; import 'package:reown_appkit/modal/constants/style_constants.dart'; +import 'package:reown_appkit/modal/utils/core_utils.dart'; import 'package:reown_appkit/modal/widgets/miscellaneous/responsive_container.dart'; import 'package:reown_appkit/modal/widgets/widget_stack/widget_stack_singleton.dart'; import 'package:reown_appkit/modal/widgets/buttons/simple_icon_button.dart'; @@ -29,6 +30,10 @@ class ReownAppKitModalSelectNetworkPage extends StatelessWidget { ) async { final appKitModal = ModalProvider.of(context).instance; if (appKitModal.isConnected) { + final tokenName = chainInfo.currency; + final formattedBalance = CoreUtils.formatChainBalance(null); + appKitModal.balanceNotifier.value = '$formattedBalance $tokenName'; + final chainId = chainInfo.chainId; final caip2Chain = ReownAppKitModalNetworks.getCaip2Chain(chainId); final namespace = ReownAppKitModalNetworks.getNamespaceForChainId( diff --git a/packages/reown_appkit/lib/modal/services/analytics_service/analytics_service.dart b/packages/reown_appkit/lib/modal/services/analytics_service/analytics_service.dart index e10afa9..6ac2c0f 100644 --- a/packages/reown_appkit/lib/modal/services/analytics_service/analytics_service.dart +++ b/packages/reown_appkit/lib/modal/services/analytics_service/analytics_service.dart @@ -48,7 +48,7 @@ class AnalyticsService implements IAnalyticsService { _isEnabled = enableAnalytics!; } _bundleId = await ReownCoreUtils.getPackageName(); - _core.logger.t('[$runtimeType] enabled: $_isEnabled'); + _core.logger.i('[$runtimeType] enabled: $_isEnabled'); } catch (e, _) { _core.logger.e('[$runtimeType] init error', error: e); } @@ -63,9 +63,10 @@ class AnalyticsService implements IAnalyticsService { ); final json = jsonDecode(response.body) as Map; final enabled = json['isAnalyticsEnabled'] as bool?; + _core.logger.i('[$runtimeType] fetch result $enabled'); return enabled ?? false; - } catch (e, _) { - _core.logger.e('[$runtimeType] fetch error', error: e); + } catch (e, s) { + _core.logger.e('[$runtimeType] fetch error', error: e, stackTrace: s); return false; } } @@ -90,7 +91,7 @@ class AnalyticsService implements IAnalyticsService { if (code == 200 || code == 202) { _eventsController.sink.add(analyticsEvent.toMap()); } - _core.logger.t('[$runtimeType] send event $code: $body'); + _core.logger.i('[$runtimeType] send event $code: $body'); } catch (e, _) { _core.logger.e('[$runtimeType] send event error', error: e); } diff --git a/packages/reown_appkit/lib/modal/services/blockchain_service/blockchain_service.dart b/packages/reown_appkit/lib/modal/services/blockchain_service/blockchain_service.dart index f5d8598..60079da 100644 --- a/packages/reown_appkit/lib/modal/services/blockchain_service/blockchain_service.dart +++ b/packages/reown_appkit/lib/modal/services/blockchain_service/blockchain_service.dart @@ -39,6 +39,7 @@ class BlockChainService implements IBlockChainService { uri.replace(queryParameters: queryParams), headers: _requiredHeaders, ); + _core.logger.i('[$runtimeType] getIdentity $address => ${response.body}'); if (response.statusCode == 200 && response.body.isNotEmpty) { return BlockchainIdentity.fromJson(jsonDecode(response.body)); } @@ -53,6 +54,7 @@ class BlockChainService implements IBlockChainService { throw Exception('Failed to load avatar'); } } catch (e) { + _core.logger.e('[$runtimeType] getIdentity $address error => $e'); rethrow; } } @@ -78,22 +80,27 @@ class BlockChainService implements IBlockChainService { ], }), ); + _core.logger.i( + '[$runtimeType] getBalance $namespace, $chainId, $address => ${response.body}', + ); if (response.statusCode == 200 && response.body.isNotEmpty) { try { return _parseBalanceResult(namespace, response.body); } catch (e) { + _core.logger.e('[$runtimeType] getBalance, parse result error => $e'); throw Exception('Failed to load balance. $e'); } } - if (response.statusCode == 400) { + try { final errorData = jsonDecode(response.body) as Map; final reasons = errorData['reasons'] as List; final reason = reasons.isNotEmpty ? reasons.first['description'] ?? '' : response.body; throw Exception(reason); - } else { - throw Exception('Failed to load balance'); + } catch (e) { + _core.logger.e('[$runtimeType] getBalance, decode result error => $e'); + rethrow; } } @@ -142,73 +149,4 @@ class BlockChainService implements IBlockChainService { } return 0.0; } - - // int _retries = 1; - // @override - // Future rpcRequest({ - // // required String? topic, - // required String chainId, - // required SessionRequestParams request, - // }) async { - // final bool isChainId = NamespaceUtils.isValidChainId(chainId); - // if (!isChainId) { - // throw Errors.getSdkError( - // Errors.UNSUPPORTED_CHAINS, - // context: '[$runtimeType] chain should be CAIP-2 valid', - // ); - // } - // final namespace = NamespaceUtils.getNamespaceFromChain(chainId); - // String method = request.method; - // // TODO NON-EVM Support: handle this better - // if (namespace == NetworkUtils.eip155) { - // method = 'eth_$method'; - // } - // final uri = Uri.parse(_baseUrl); - // final queryParams = {..._requiredParams, 'chainId': chainId}; - // final response = await http.post( - // uri.replace(queryParameters: queryParams), - // headers: { - // ..._requiredHeaders, - // 'Content-Type': 'application/json', - // }, - // body: jsonEncode({ - // 'id': 1, - // 'jsonrpc': '2.0', - // 'method': method, - // 'params': request.params, - // }), - // ); - // if (response.statusCode == 200 && response.body.isNotEmpty) { - // _retries = 1; - // try { - // // TODO NON-EVM Support: handle this better - // if (namespace == NetworkUtils.solana) { - // final result = _parseRpcResultAs>(response.body); - // final value = result['value'] as int; - // return value / 1000000000.0; - // } - // if (namespace == NetworkUtils.eip155) { - // final result = _parseRpcResultAs(response.body); - // final amount = EtherAmount.fromBigInt( - // EtherUnit.wei, - // hexToInt(result), - // ); - // return amount.getValueInUnit(EtherUnit.ether); - // } - // } catch (e) { - // rethrow; - // } - // } else { - // if (response.body.isEmpty && _retries > 0) { - // _core.logger.i('[$runtimeType] Empty body'); - // _retries -= 1; - // await rpcRequest(chainId: chainId, request: request); - // } else { - // _core.logger.i( - // '[$runtimeType] Failed to get request ${request.toJson()}. ' - // 'Response: ${response.body}, Status code: ${response.statusCode}', - // ); - // } - // } - // } } diff --git a/packages/reown_appkit/lib/modal/services/coinbase_service/coinbase_service.dart b/packages/reown_appkit/lib/modal/services/coinbase_service/coinbase_service.dart index c9eb558..8f4f41b 100644 --- a/packages/reown_appkit/lib/modal/services/coinbase_service/coinbase_service.dart +++ b/packages/reown_appkit/lib/modal/services/coinbase_service/coinbase_service.dart @@ -106,6 +106,9 @@ class CoinbaseService implements ICoinbaseService { final walletLink = _walletData.listing.mobileLink ?? ''; final redirect = _metadata.redirect; final callback = redirect?.universal ?? redirect?.native ?? ''; + _core.logger.i( + '[$runtimeType] init with $walletLink, $redirect, $callback', + ); if (callback.isNotEmpty || walletLink.isNotEmpty) { try { final config = Configuration( @@ -123,6 +126,7 @@ class CoinbaseService implements ICoinbaseService { } } else { _enabled = false; + _core.logger.e('[$runtimeType] Initialization error'); throw CoinbaseServiceException('Initialization error'); } } @@ -172,13 +176,16 @@ class CoinbaseService implements ICoinbaseService { ), ); onCoinbaseConnect.broadcast(CoinbaseConnectEvent(data)); + _core.logger.i('[$runtimeType] getAccount ${data.toJson()}'); return; } on PlatformException catch (e, s) { + _core.logger.e('[$runtimeType] getAccount PlatformException $e'); // Currently Coinbase SDK is not differentiate between User rejection or any other kind of error in iOS final errorMessage = (e.message ?? '').toLowerCase(); onCoinbaseError.broadcast(CoinbaseErrorEvent(errorMessage)); throw CoinbaseServiceException(errorMessage, e, s); } catch (e, s) { + _core.logger.e('[$runtimeType] getAccount $e'); onCoinbaseError.broadcast(CoinbaseErrorEvent('Initial handshake error')); throw CoinbaseServiceException('Initial handshake error', e, s); } @@ -191,6 +198,7 @@ class CoinbaseService implements ICoinbaseService { }) async { await _checkInstalled(); final cid = chainId.contains(':') ? chainId.split(':').last : chainId; + _core.logger.i('[$runtimeType] request $chainId, ${request.toJson()}'); try { final req = Request(actions: [request.toCoinbaseRequest(cid)]); final result = (await CoinbaseWalletSDK.shared.makeRequest(req)).first; @@ -224,11 +232,14 @@ class CoinbaseService implements ICoinbaseService { onCoinbaseResponse.broadcast(CoinbaseResponseEvent(data: value)); break; } + _core.logger.i('[$runtimeType] request result $value'); return value; } on CoinbaseServiceException catch (e) { + _core.logger.e('[$runtimeType] request CoinbaseServiceException $e'); onCoinbaseError.broadcast(CoinbaseErrorEvent(e.message)); rethrow; } on PlatformException catch (e, s) { + _core.logger.e('[$runtimeType] request PlatformException $e'); final message = 'Coinbase Wallet Error: (${e.code}) ${e.message}'; onCoinbaseError.broadcast(CoinbaseErrorEvent(message)); throw CoinbaseServiceException(message, e, s); @@ -240,6 +251,7 @@ class CoinbaseService implements ICoinbaseService { try { return await CoinbaseWalletSDK.shared.isAppInstalled(); } catch (e, s) { + _core.logger.e('[$runtimeType] isInstalled $e'); throw CoinbaseServiceException('Check is installed error', e, s); } } @@ -249,6 +261,7 @@ class CoinbaseService implements ICoinbaseService { try { return await CoinbaseWalletSDK.shared.isConnected(); } catch (e, s) { + _core.logger.e('[$runtimeType] isConnected $e'); throw CoinbaseServiceException('Check is connected error', e, s); } } @@ -258,6 +271,7 @@ class CoinbaseService implements ICoinbaseService { try { return CoinbaseWalletSDK.shared.resetSession(); } catch (e, s) { + _core.logger.e('[$runtimeType] resetSession $e'); throw CoinbaseServiceException('Reset session error', e, s); } } diff --git a/packages/reown_appkit/lib/modal/services/explorer_service/explorer_service.dart b/packages/reown_appkit/lib/modal/services/explorer_service/explorer_service.dart index 3d2b00a..2366368 100644 --- a/packages/reown_appkit/lib/modal/services/explorer_service/explorer_service.dart +++ b/packages/reown_appkit/lib/modal/services/explorer_service/explorer_service.dart @@ -253,7 +253,7 @@ class ExplorerService implements IExplorerService { ); // this query gives me a count of installedWalletsParam.length final installedWallets = await _fetchListings(params: params); - _core.logger.t( + _core.logger.d( '[$runtimeType] ${installedWallets.length} installed wallets', ); return installedWallets.setInstalledFlag(); @@ -381,8 +381,11 @@ class ExplorerService implements IExplorerService { } _listings = currentListings; listings.value = _listings; - } catch (e) { - _core.logger.e('[$runtimeType] error updating recent wallet: $e'); + } catch (e, s) { + _core.logger.e( + '[$runtimeType] error updating recent wallet: $e', + stackTrace: s, + ); } } diff --git a/packages/reown_appkit/lib/modal/services/magic_service/magic_service.dart b/packages/reown_appkit/lib/modal/services/magic_service/magic_service.dart index 977203b..6996b41 100644 --- a/packages/reown_appkit/lib/modal/services/magic_service/magic_service.dart +++ b/packages/reown_appkit/lib/modal/services/magic_service/magic_service.dart @@ -52,6 +52,7 @@ class MagicService implements IMagicService { String _packageName = ''; AppKitSocialOption? _socialProvider; String? _socialUsername; + ReownAppKitModalTheme? _appTheme; late final IReownCore _core; late final PairingMetadata _metadata; @@ -147,6 +148,7 @@ class MagicService implements IMagicService { Future init({String? chainId}) async { _connectionChainId = chainId; _initializedCompleter = Completer(); + _isConnectedCompleter = Completer(); if (!isEmailEnabled.value && !isSocialEnabled.value) { _initializedCompleter.complete(false); _isConnectedCompleter.complete(false); @@ -157,6 +159,7 @@ class MagicService implements IMagicService { await _isConnected(); await _syncAppData(); isReady.value = true; + await syncTheme(_appTheme); } Future _init() async { @@ -381,15 +384,12 @@ class MagicService implements IMagicService { await _webViewController.runJavaScript('sendMessage($message)'); } - // SHARED METHODS - @override Future syncTheme(ReownAppKitModalTheme? theme) async { - if (_serviceNotReady) { - throw Exception('Service is not ready'); - } + _appTheme = theme; + if (_serviceNotReady || _appTheme == null) return; // - final message = SyncTheme(theme: theme).toString(); + final message = SyncTheme(theme: _appTheme).toString(); await _webViewController.runJavaScript('sendMessage($message)'); } @@ -549,15 +549,16 @@ class MagicService implements IMagicService { if (!frameMessage.isValidOrigin || !frameMessage.isValidData) { return; } - _core.logger.i('[$runtimeType] ${frameMessage.data!.toRawJson()}'); + _core.logger.d('[$runtimeType] ${frameMessage.data!.toRawJson()}'); _successMessageHandler(frameMessage.data!); // await? _errorMessageHandler(frameMessage.data!); } catch (e, s) { - _core.logger.d('[$runtimeType] $jsMessage $e', stackTrace: s); + _core.logger.e('[$runtimeType] $jsMessage $e', stackTrace: s); } } Future _successMessageHandler(MessageData messageData) async { + // ******* SYNC_DAPP_DATA_SUCCESS if (messageData.syncDataSuccess) { _resetTimeOut(); _syncAppDataCompleter.complete(true); @@ -885,7 +886,7 @@ class MagicService implements IMagicService { } void _onDebugConsoleReceived(JavaScriptConsoleMessage message) { - _core.logger.i('[$runtimeType] ${message.message}'); + _core.logger.d('[$runtimeType] ${message.message}'); } void _onWebResourceError(WebResourceError error) { diff --git a/packages/reown_appkit/lib/modal/utils/public/appkit_modal_default_networks.dart b/packages/reown_appkit/lib/modal/utils/public/appkit_modal_default_networks.dart index 8801cad..4c42f28 100644 --- a/packages/reown_appkit/lib/modal/utils/public/appkit_modal_default_networks.dart +++ b/packages/reown_appkit/lib/modal/utils/public/appkit_modal_default_networks.dart @@ -229,14 +229,14 @@ class ReownAppKitModalNetworks { List chainIds = const [], bool includeTestnets = true, }) { - _mainnets[namespace]!.removeWhere((chain) { + _mainnets[namespace]?.removeWhere((chain) { if (chainIds.isEmpty || chainIds.contains(chain.chainId)) { return true; } return false; }); if (includeTestnets) { - _testnets[namespace]!.removeWhere((chain) { + _testnets[namespace]?.removeWhere((chain) { if (chainIds.isEmpty || chainIds.contains(chain.chainId)) { return true; } @@ -310,6 +310,11 @@ class ReownAppKitModalNetworks { } static String getNetworkIconId(String chainId) { + final namespace = getNamespaceForChainId(chainId); + final network = getNetworkById(namespace, chainId); + if ((network?.chainIcon ?? '').isNotEmpty) { + return network!.chainIcon!; + } return _networkIcons[chainId] ?? ''; } diff --git a/packages/reown_appkit/lib/modal/widgets/buttons/network_button.dart b/packages/reown_appkit/lib/modal/widgets/buttons/network_button.dart index 1d1afe6..d4fab66 100644 --- a/packages/reown_appkit/lib/modal/widgets/buttons/network_button.dart +++ b/packages/reown_appkit/lib/modal/widgets/buttons/network_button.dart @@ -25,7 +25,9 @@ class NetworkButton extends StatelessWidget { final ReownAppKitModalStatus serviceStatus; final VoidCallback? onTap; - String _getImageUrl(ReownAppKitModalNetworkInfo chainInfo) { + String _getImageUrl(ReownAppKitModalNetworkInfo? chainInfo) { + if (chainInfo == null) return ''; + if (chainInfo.chainIcon != null && chainInfo.chainIcon!.contains('http')) { return chainInfo.chainIcon!; } @@ -38,10 +40,7 @@ class NetworkButton extends StatelessWidget { @override Widget build(BuildContext context) { final themeColors = ReownAppKitModalTheme.colorsOf(context); - String imageUrl = ''; - if (chainInfo != null && (chainInfo!.chainIcon ?? '').isNotEmpty) { - imageUrl = _getImageUrl(chainInfo!); - } + final imageUrl = _getImageUrl(chainInfo); final radiuses = ReownAppKitModalTheme.radiusesOf(context); final borderRadius = radiuses.isSquare() ? 0.0 : size.height / 2; return BaseButton( diff --git a/packages/reown_appkit/lib/modal/widgets/lists/list_items/account_list_item.dart b/packages/reown_appkit/lib/modal/widgets/lists/list_items/account_list_item.dart index 8636bb0..ad7028a 100644 --- a/packages/reown_appkit/lib/modal/widgets/lists/list_items/account_list_item.dart +++ b/packages/reown_appkit/lib/modal/widgets/lists/list_items/account_list_item.dart @@ -89,19 +89,21 @@ class AccountListItem extends StatelessWidget { ), ), trailing ?? - Padding( - padding: const EdgeInsets.only(right: 8.0), - child: SvgPicture.asset( - 'lib/modal/assets/icons/chevron_right.svg', - package: 'reown_appkit', - colorFilter: ColorFilter.mode( - themeColors.foreground200, - BlendMode.srcIn, - ), - width: 18.0, - height: 18.0, - ), - ), + (onTap != null + ? Padding( + padding: const EdgeInsets.only(right: 8.0), + child: SvgPicture.asset( + 'lib/modal/assets/icons/chevron_right.svg', + package: 'reown_appkit', + colorFilter: ColorFilter.mode( + themeColors.foreground200, + BlendMode.srcIn, + ), + width: 18.0, + height: 18.0, + ), + ) + : const SizedBox.shrink()), ], ), ); diff --git a/packages/reown_appkit/lib/modal/widgets/public/appkit_modal_account_button.dart b/packages/reown_appkit/lib/modal/widgets/public/appkit_modal_account_button.dart index dbe9452..eb79629 100644 --- a/packages/reown_appkit/lib/modal/widgets/public/appkit_modal_account_button.dart +++ b/packages/reown_appkit/lib/modal/widgets/public/appkit_modal_account_button.dart @@ -17,14 +17,17 @@ import 'package:reown_appkit/reown_appkit.dart'; class AppKitModalAccountButton extends StatefulWidget { const AppKitModalAccountButton({ super.key, - required this.appKit, + @Deprecated('Use appKitModal parameter') this.appKit, + required this.appKitModal, this.size = BaseButtonSize.regular, this.avatar, this.context, this.custom, }); - final IReownAppKitModal appKit; + @Deprecated('Use appKitModal parameter') + final IReownAppKitModal? appKit; + final IReownAppKitModal appKitModal; final BaseButtonSize size; final String? avatar; final BuildContext? context; @@ -43,7 +46,7 @@ class _AppKitModalAccountButtonState extends State { void initState() { super.initState(); _modalNotifyListener(); - widget.appKit.addListener(_modalNotifyListener); + widget.appKitModal.addListener(_modalNotifyListener); // TODO [AppKitModalAccountButton] this should go in ReownAppKitModal but for that, init() method of ReownAppKitModal should receive a BuildContext, which would be a breaking change _magicService.onMagicRpcRequest.subscribe(_approveSign); _magicService.onMagicLoginRequest.subscribe(_loginRequested); @@ -51,7 +54,7 @@ class _AppKitModalAccountButtonState extends State { @override void dispose() { - widget.appKit.removeListener(_modalNotifyListener); + widget.appKitModal.removeListener(_modalNotifyListener); _magicService.onMagicRpcRequest.unsubscribe(_approveSign); _magicService.onMagicLoginRequest.unsubscribe(_loginRequested); super.dispose(); @@ -59,24 +62,24 @@ class _AppKitModalAccountButtonState extends State { void _modalNotifyListener() { setState(() { - final chainId = widget.appKit.selectedChain?.chainId ?? ''; + final chainId = widget.appKitModal.selectedChain?.chainId ?? ''; final namespace = ReownAppKitModalNetworks.getNamespaceForChainId( chainId, ); - _address = widget.appKit.session?.getAddress(namespace) ?? ''; + _address = widget.appKitModal.session?.getAddress(namespace) ?? ''; }); } void _onTap() { - widget.appKit.openModalView(); + widget.appKitModal.openModalView(); } void _approveSign(MagicRequestEvent? args) async { if (args?.request != null) { - if (widget.appKit.isOpen) { + if (widget.appKitModal.isOpen) { widgetStack.instance.push(ApproveTransactionPage()); } else { - widget.appKit.openModalView(ApproveTransactionPage()); + widget.appKitModal.openModalView(ApproveTransactionPage()); } } } @@ -84,7 +87,7 @@ class _AppKitModalAccountButtonState extends State { void _loginRequested(MagicSessionEvent? args) { if (args == null) return; final provider = args.provider; - final isOpen = widget.appKit.isOpen; + final isOpen = widget.appKitModal.isOpen; if (isOpen) { if (provider != null) { widgetStack.instance.popAllAndPush(SocialLoginPage( @@ -95,11 +98,11 @@ class _AppKitModalAccountButtonState extends State { } } else { if (provider != null) { - widget.appKit.openModalView(SocialLoginPage( + widget.appKitModal.openModalView(SocialLoginPage( socialOption: provider, )); } else { - widget.appKit.openModalView(ConfirmEmailPage()); + widget.appKitModal.openModalView(ConfirmEmailPage()); } } } @@ -112,7 +115,8 @@ class _AppKitModalAccountButtonState extends State { final themeColors = ReownAppKitModalTheme.colorsOf(context); final radiuses = ReownAppKitModalTheme.radiusesOf(context); final borderRadius = radiuses.isSquare() ? 0.0 : widget.size.height / 2; - final enabled = _address.isNotEmpty && widget.appKit.status.isInitialized; + final enabled = + _address.isNotEmpty && widget.appKitModal.status.isInitialized; // TODO [AppKitModalAccountButton] this button should be able to be disable by passing a null onTap action // I should decouple an AccountButton from AppKitModalAccountButton like on ConnectButton and NetworkButton return Stack( @@ -156,7 +160,7 @@ class _AppKitModalAccountButtonState extends State { mainAxisSize: MainAxisSize.min, children: [ _BalanceButton( - appKit: widget.appKit, + appKit: widget.appKitModal, buttonSize: widget.size, onTap: enabled ? _onTap : null, ), @@ -165,7 +169,7 @@ class _AppKitModalAccountButtonState extends State { padding: const EdgeInsets.symmetric(vertical: 4.0), child: AppKitModalAddressButton( size: widget.size, - appKitModal: widget.appKit, + appKitModal: widget.appKitModal, onTap: enabled ? _onTap : null, ), ), @@ -196,7 +200,11 @@ class _BalanceButton extends StatelessWidget { : themeData.textStyles.paragraph600; final chainId = appKit.selectedChain?.chainId ?? ''; final imageId = ReownAppKitModalNetworks.getNetworkIconId(chainId); - final tokenImage = GetIt.I().getAssetImageUrl(imageId); + String tokenImage = GetIt.I().getAssetImageUrl(imageId); + final balance = appKit.balanceNotifier.value; + if (balance.contains(AppKitModalBalanceButton.balanceDefault)) { + tokenImage = ''; + } return BaseButton( size: BaseButtonSize.small, onTap: onTap, diff --git a/packages/reown_appkit/lib/modal/widgets/public/appkit_modal_balance_button.dart b/packages/reown_appkit/lib/modal/widgets/public/appkit_modal_balance_button.dart index aeae759..0013f91 100644 --- a/packages/reown_appkit/lib/modal/widgets/public/appkit_modal_balance_button.dart +++ b/packages/reown_appkit/lib/modal/widgets/public/appkit_modal_balance_button.dart @@ -47,6 +47,11 @@ class _AppKitModalBalanceButtonState extends State { final chainId = widget.appKitModal.selectedChain?.chainId ?? '1'; final imageId = ReownAppKitModalNetworks.getNetworkIconId(chainId); _tokenImage = GetIt.I().getAssetImageUrl(imageId); + final balance = widget.appKitModal.balanceNotifier.value; + debugPrint('[$runtimeType] $balance'); + if (balance.contains(AppKitModalBalanceButton.balanceDefault)) { + _tokenImage = ''; + } }); } diff --git a/packages/reown_appkit/lib/modal/widgets/value_listenable_builders/network_service_items_listener.dart b/packages/reown_appkit/lib/modal/widgets/value_listenable_builders/network_service_items_listener.dart index 1d2eacd..e843c18 100644 --- a/packages/reown_appkit/lib/modal/widgets/value_listenable_builders/network_service_items_listener.dart +++ b/packages/reown_appkit/lib/modal/widgets/value_listenable_builders/network_service_items_listener.dart @@ -42,7 +42,7 @@ class NetworkServiceItemsListener extends StatelessWidget { extension on List> { List> parseItems(BuildContext context) { final service = ModalProvider.of(context).instance; - final supportedChains = service.getAvailableChains(); + final supportedChains = service.getApprovedChains(); if (supportedChains == null) { return this ..sort((a, b) { diff --git a/packages/reown_appkit/lib/version.dart b/packages/reown_appkit/lib/version.dart index 67367f8..1ee4245 100644 --- a/packages/reown_appkit/lib/version.dart +++ b/packages/reown_appkit/lib/version.dart @@ -1,2 +1,2 @@ // Generated code. Do not modify. -const packageVersion = '1.1.0-beta03'; +const packageVersion = '1.2.0-beta01'; diff --git a/packages/reown_appkit/pubspec.yaml b/packages/reown_appkit/pubspec.yaml index 163d322..d147ddf 100644 --- a/packages/reown_appkit/pubspec.yaml +++ b/packages/reown_appkit/pubspec.yaml @@ -1,6 +1,6 @@ name: reown_appkit description: "Reown is the onchain UX platform that provides toolkits built on top of the WalletConnect Network" -version: 1.1.0-beta03 +version: 1.2.0-beta01 homepage: https://github.com/reown-com/reown_flutter repository: https://github.com/reown-com/reown_flutter/tree/master/packages/reown_appkit documentation: https://docs.reown.com/appkit/flutter/core/installation @@ -26,8 +26,12 @@ dependencies: json_annotation: ^4.8.1 plugin_platform_interface: ^2.1.8 qr_flutter_wc: ^0.0.3 - reown_core: ^1.0.1 - reown_sign: ^1.0.1 + # reown_core: ^1.0.1 + reown_core: + path: ../reown_core/ + # reown_sign: ^1.0.1 + reown_sign: + path: ../reown_sign/ shimmer: ^3.0.0 uuid: ^4.5.1 webview_flutter: ^4.8.0 @@ -46,7 +50,9 @@ dev_dependencies: logger: ^2.2.0 mockito: ^5.4.3 package_info_plus: ^8.0.2 - reown_walletkit: ^1.0.1 + # reown_walletkit: ^1.0.1 + reown_walletkit: + path: ../reown_walletkit/ # For information on the generic Dart part of this file, see the # following page: https://dart.dev/tools/pub/pubspec diff --git a/packages/reown_walletkit/example/lib/dependencies/chain_services/evm_service.dart b/packages/reown_walletkit/example/lib/dependencies/chain_services/evm_service.dart index 9b3821a..cf9f575 100644 --- a/packages/reown_walletkit/example/lib/dependencies/chain_services/evm_service.dart +++ b/packages/reown_walletkit/example/lib/dependencies/chain_services/evm_service.dart @@ -590,4 +590,58 @@ class EVMService { return false; } } + + Future getBalance({required String address}) async { + final uri = Uri.parse('https://rpc.walletconnect.org/v1'); + final queryParams = { + 'projectId': _walletKit.core.projectId, + 'chainId': chainSupported.chainId + }; + final response = await http.post( + uri.replace(queryParameters: queryParams), + headers: {'Content-Type': 'application/json'}, + body: jsonEncode({ + 'id': 1, + 'jsonrpc': '2.0', + 'method': 'eth_getBalance', + 'params': [address, 'latest'], + }), + ); + if (response.statusCode == 200 && response.body.isNotEmpty) { + try { + final result = _parseRpcResultAs(response.body); + final amount = EtherAmount.fromBigInt( + EtherUnit.wei, + hexToInt(result), + ); + return amount.getValueInUnit(EtherUnit.ether); + } catch (e) { + throw Exception('Failed to load balance. $e'); + } + } + try { + final errorData = jsonDecode(response.body) as Map; + final reasons = errorData['reasons'] as List; + final reason = reasons.isNotEmpty + ? reasons.first['description'] ?? '' + : response.body; + throw Exception(reason); + } catch (e) { + rethrow; + } + } + + T _parseRpcResultAs(String body) { + try { + final result = Map.from({...jsonDecode(body), 'id': 1}); + final jsonResponse = JsonRpcResponse.fromJson(result); + if (jsonResponse.result != null) { + return jsonResponse.result; + } else { + throw jsonResponse.error ?? 'Error parsing result'; + } + } catch (e) { + rethrow; + } + } } diff --git a/packages/reown_walletkit/example/lib/dependencies/chain_services/kadena_service.dart b/packages/reown_walletkit/example/lib/dependencies/chain_services/kadena_service.dart index f964fca..af238e7 100644 --- a/packages/reown_walletkit/example/lib/dependencies/chain_services/kadena_service.dart +++ b/packages/reown_walletkit/example/lib/dependencies/chain_services/kadena_service.dart @@ -112,7 +112,7 @@ class KadenaService { ); try { - final chain = ChainData.kadenaChains.firstWhere( + final chain = ChainsDataList.kadenaChains.firstWhere( (c) => c.chainId == chainSupported.chainId, ); final uri = Uri.parse(chain.rpc.first); diff --git a/packages/reown_walletkit/example/lib/dependencies/chain_services/solana_service.dart b/packages/reown_walletkit/example/lib/dependencies/chain_services/solana_service.dart index 6b68adf..621592a 100644 --- a/packages/reown_walletkit/example/lib/dependencies/chain_services/solana_service.dart +++ b/packages/reown_walletkit/example/lib/dependencies/chain_services/solana_service.dart @@ -1,5 +1,6 @@ import 'dart:convert'; import 'dart:typed_data'; +import 'package:http/http.dart' as http; import 'package:flutter/material.dart'; import 'package:get_it/get_it.dart'; @@ -215,6 +216,57 @@ class SolanaService { ); } } + + Future getBalance({required String address}) async { + final uri = Uri.parse('https://rpc.walletconnect.org/v1'); + final queryParams = { + 'projectId': _walletKit.core.projectId, + 'chainId': chainSupported.chainId + }; + final response = await http.post( + uri.replace(queryParameters: queryParams), + headers: {'Content-Type': 'application/json'}, + body: jsonEncode({ + 'id': 1, + 'jsonrpc': '2.0', + 'method': 'getBalance', + 'params': [address] + }), + ); + if (response.statusCode == 200 && response.body.isNotEmpty) { + try { + final result = _parseRpcResultAs>(response.body); + final value = result['value'] as int; + return value / 1000000000.0; + } catch (e) { + throw Exception('Failed to load balance. $e'); + } + } + try { + final errorData = jsonDecode(response.body) as Map; + final reasons = errorData['reasons'] as List; + final reason = reasons.isNotEmpty + ? reasons.first['description'] ?? '' + : response.body; + throw Exception(reason); + } catch (e) { + rethrow; + } + } + + T _parseRpcResultAs(String body) { + try { + final result = Map.from({...jsonDecode(body), 'id': 1}); + final jsonResponse = JsonRpcResponse.fromJson(result); + if (jsonResponse.result != null) { + return jsonResponse.result; + } else { + throw jsonResponse.error ?? 'Error parsing result'; + } + } catch (e) { + rethrow; + } + } } extension on String { diff --git a/packages/reown_walletkit/example/lib/dependencies/chain_services/solana_service_2.dart b/packages/reown_walletkit/example/lib/dependencies/chain_services/solana_service_2.dart index 77cabf9..58ecce1 100644 --- a/packages/reown_walletkit/example/lib/dependencies/chain_services/solana_service_2.dart +++ b/packages/reown_walletkit/example/lib/dependencies/chain_services/solana_service_2.dart @@ -1,5 +1,6 @@ import 'dart:convert'; import 'dart:typed_data'; +import 'package:http/http.dart' as http; import 'package:flutter/material.dart'; import 'package:get_it/get_it.dart'; @@ -219,6 +220,57 @@ class SolanaService2 { ); } } + + Future getBalance({required String address}) async { + final uri = Uri.parse('https://rpc.walletconnect.org/v1'); + final queryParams = { + 'projectId': _walletKit.core.projectId, + 'chainId': chainSupported.chainId + }; + final response = await http.post( + uri.replace(queryParameters: queryParams), + headers: {'Content-Type': 'application/json'}, + body: jsonEncode({ + 'id': 1, + 'jsonrpc': '2.0', + 'method': 'getBalance', + 'params': [address], + }), + ); + if (response.statusCode == 200 && response.body.isNotEmpty) { + try { + final result = _parseRpcResultAs>(response.body); + final value = result['value'] as int; + return value / 1000000000.0; + } catch (e) { + throw Exception('Failed to load balance. $e'); + } + } + try { + final errorData = jsonDecode(response.body) as Map; + final reasons = errorData['reasons'] as List; + final reason = reasons.isNotEmpty + ? reasons.first['description'] ?? '' + : response.body; + throw Exception(reason); + } catch (e) { + rethrow; + } + } + + T _parseRpcResultAs(String body) { + try { + final result = Map.from({...jsonDecode(body), 'id': 1}); + final jsonResponse = JsonRpcResponse.fromJson(result); + if (jsonResponse.result != null) { + return jsonResponse.result; + } else { + throw jsonResponse.error ?? 'Error parsing result'; + } + } catch (e) { + rethrow; + } + } } extension on Map { diff --git a/packages/reown_walletkit/example/lib/dependencies/key_service/key_service.dart b/packages/reown_walletkit/example/lib/dependencies/key_service/key_service.dart index 50ee3e7..96c23cf 100644 --- a/packages/reown_walletkit/example/lib/dependencies/key_service/key_service.dart +++ b/packages/reown_walletkit/example/lib/dependencies/key_service/key_service.dart @@ -148,7 +148,7 @@ class KeyService extends IKeyService { final private = EthPrivateKey.fromHex(keyPair.privateKey); final address = private.address.hex; final evmChainKey = ChainKey( - chains: ChainData.eip155Chains.map((e) => e.chainId).toList(), + chains: ChainsDataList.eip155Chains.map((e) => e.chainId).toList(), privateKey: keyPair.privateKey, publicKey: keyPair.publicKey, address: address, @@ -174,7 +174,7 @@ class KeyService extends IKeyService { ChainKey _kadenaChainKey() { return ChainKey( - chains: ChainData.kadenaChains.map((e) => e.chainId).toList(), + chains: ChainsDataList.kadenaChains.map((e) => e.chainId).toList(), privateKey: DartDefines.kadenaSecretKey, publicKey: DartDefines.kadenaAddress, address: DartDefines.kadenaAddress, @@ -183,7 +183,7 @@ class KeyService extends IKeyService { ChainKey _polkadotChainKey() { return ChainKey( - chains: ChainData.polkadotChains.map((e) => e.chainId).toList(), + chains: ChainsDataList.polkadotChains.map((e) => e.chainId).toList(), privateKey: DartDefines.polkadotMnemonic, publicKey: '', address: DartDefines.polkadotAddress, @@ -192,7 +192,7 @@ class KeyService extends IKeyService { ChainKey _solanaChainKey() { return ChainKey( - chains: ChainData.solanaChains.map((e) => e.chainId).toList(), + chains: ChainsDataList.solanaChains.map((e) => e.chainId).toList(), privateKey: DartDefines.solanaSecretKey, publicKey: DartDefines.solanaAddress, address: DartDefines.solanaAddress, diff --git a/packages/reown_walletkit/example/lib/dependencies/walletkit_service.dart b/packages/reown_walletkit/example/lib/dependencies/walletkit_service.dart index 75caa5e..c702dfa 100644 --- a/packages/reown_walletkit/example/lib/dependencies/walletkit_service.dart +++ b/packages/reown_walletkit/example/lib/dependencies/walletkit_service.dart @@ -288,7 +288,7 @@ class WalletKitService extends IWalletKitService { final SessionAuthPayload authPayload = args.authPayload; final jsonPyaload = jsonEncode(authPayload.toJson()); debugPrint('[SampleWallet] _onSessionAuthRequest $jsonPyaload'); - final supportedChains = ChainData.eip155Chains.map((e) => e.chainId); + final supportedChains = ChainsDataList.eip155Chains.map((e) => e.chainId); final supportedMethods = SupportedEVMMethods.values.map((e) => e.name); final newAuthPayload = AuthSignature.populateAuthPayload( authPayload: authPayload, diff --git a/packages/reown_walletkit/example/lib/main.dart b/packages/reown_walletkit/example/lib/main.dart index adf9c2f..670546d 100644 --- a/packages/reown_walletkit/example/lib/main.dart +++ b/packages/reown_walletkit/example/lib/main.dart @@ -70,7 +70,7 @@ class _MyHomePageState extends State with GetItStateMixin { GetIt.I.registerSingleton(walletKitService); // Support EVM Chains - for (final chainData in ChainData.eip155Chains) { + for (final chainData in ChainsDataList.eip155Chains) { GetIt.I.registerSingleton( EVMService(chainSupported: chainData), instanceName: chainData.chainId, @@ -78,7 +78,7 @@ class _MyHomePageState extends State with GetItStateMixin { } // Support Kadena Chains - for (final chainData in ChainData.kadenaChains) { + for (final chainData in ChainsDataList.kadenaChains) { GetIt.I.registerSingleton( KadenaService(chainSupported: chainData), instanceName: chainData.chainId, @@ -86,7 +86,7 @@ class _MyHomePageState extends State with GetItStateMixin { } // Support Polkadot Chains - for (final chainData in ChainData.polkadotChains) { + for (final chainData in ChainsDataList.polkadotChains) { GetIt.I.registerSingleton( PolkadotService(chainSupported: chainData), instanceName: chainData.chainId, @@ -95,7 +95,7 @@ class _MyHomePageState extends State with GetItStateMixin { // Support Solana Chains // Change SolanaService2 to SolanaService to switch between solana_web3: ^0.1.3 to solana: ^0.30.4 - for (final chainData in ChainData.solanaChains) { + for (final chainData in ChainsDataList.solanaChains) { GetIt.I.registerSingleton( SolanaService2(chainSupported: chainData), instanceName: chainData.chainId, @@ -103,7 +103,7 @@ class _MyHomePageState extends State with GetItStateMixin { } // Support Cosmos Chains - for (final chainData in ChainData.cosmosChains) { + for (final chainData in ChainsDataList.cosmosChains) { GetIt.I.registerSingleton( CosmosService(chainSupported: chainData), instanceName: chainData.chainId, diff --git a/packages/reown_walletkit/example/lib/models/chain_data.dart b/packages/reown_walletkit/example/lib/models/chain_data.dart index e85a2e7..249594c 100644 --- a/packages/reown_walletkit/example/lib/models/chain_data.dart +++ b/packages/reown_walletkit/example/lib/models/chain_data.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:reown_walletkit_wallet/models/chain_metadata.dart'; -class ChainData { +class ChainsDataList { static final List eip155Chains = [ ChainMetadata( type: ChainType.eip155, diff --git a/packages/reown_walletkit/example/lib/pages/settings_page.dart b/packages/reown_walletkit/example/lib/pages/settings_page.dart index 03cfb4c..abb1156 100644 --- a/packages/reown_walletkit/example/lib/pages/settings_page.dart +++ b/packages/reown_walletkit/example/lib/pages/settings_page.dart @@ -8,8 +8,12 @@ import 'package:get_it/get_it.dart'; import 'package:package_info_plus/package_info_plus.dart'; import 'package:reown_walletkit/reown_walletkit.dart'; import 'package:reown_walletkit_wallet/dependencies/bottom_sheet/i_bottom_sheet_service.dart'; +import 'package:reown_walletkit_wallet/dependencies/chain_services/evm_service.dart'; +import 'package:reown_walletkit_wallet/dependencies/chain_services/solana_service_2.dart'; import 'package:reown_walletkit_wallet/dependencies/i_walletkit_service.dart'; import 'package:reown_walletkit_wallet/dependencies/key_service/i_key_service.dart'; +import 'package:reown_walletkit_wallet/models/chain_data.dart'; +import 'package:reown_walletkit_wallet/models/chain_metadata.dart'; import 'package:reown_walletkit_wallet/utils/constants.dart'; import 'package:reown_walletkit_wallet/widgets/custom_button.dart'; import 'package:reown_walletkit_wallet/widgets/recover_from_seed.dart'; @@ -162,11 +166,20 @@ class _EVMAccounts extends StatefulWidget { class _EVMAccountsState extends State<_EVMAccounts> { int _currentPage = 0; late final PageController _pageController; + ChainMetadata? _selectedChain; + double _balance = 0.0; @override void initState() { super.initState(); + _selectedChain = ChainsDataList.eip155Chains.first; _pageController = PageController(); + final keysService = GetIt.I(); + final chainKeys = keysService.getKeysForChain('eip155'); + GetIt.I + .get(instanceName: _selectedChain!.chainId) + .getBalance(address: chainKeys[_currentPage].address) + .then((value) => setState(() => _balance = value)); } @override @@ -180,7 +193,6 @@ class _EVMAccountsState extends State<_EVMAccounts> { padding: const EdgeInsets.symmetric(horizontal: 12.0), child: Row( children: [ - const SizedBox.square(dimension: 8.0), Expanded( child: Text( 'EVM Accounts (${_currentPage + 1}/${chainKeys.length})', @@ -242,6 +254,41 @@ class _EVMAccountsState extends State<_EVMAccounts> { ], ), ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 12.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Expanded( + child: Text( + '${_balance.toStringAsFixed(3)} ETH', + style: TextStyle( + fontSize: 15.0, + fontWeight: FontWeight.bold, + ), + ), + ), + DropdownButton( + value: _selectedChain, + items: ChainsDataList.eip155Chains.map((e) { + return DropdownMenuItem( + value: e, + child: Text(e.name), + ); + }).toList(), + onChanged: (ChainMetadata? chain) { + setState(() => _selectedChain = chain); + final chainKey = chainKeys[_currentPage]; + GetIt.I + .get(instanceName: chain?.chainId) + .getBalance(address: chainKey.address) + .then((value) => setState(() => _balance = value)); + }, + ), + ], + ), + ), SizedBox( height: 300.0, child: PageView.builder( @@ -261,7 +308,7 @@ class _EVMAccountsState extends State<_EVMAccounts> { const SizedBox(height: 12.0), _DataContainer( title: 'CAIP-10', - data: 'eip155:1:${chainKey.address}', + data: '${_selectedChain?.chainId}:${chainKey.address}', height: 84.0, ), const SizedBox(height: 12.0), @@ -324,7 +371,27 @@ class _EVMAccountsState extends State<_EVMAccounts> { } } -class _SolanaAccounts extends StatelessWidget { +class _SolanaAccounts extends StatefulWidget { + @override + State<_SolanaAccounts> createState() => _SolanaAccountsState(); +} + +class _SolanaAccountsState extends State<_SolanaAccounts> { + ChainMetadata? _selectedChain; + double _balance = 0.0; + + @override + void initState() { + super.initState(); + _selectedChain = ChainsDataList.solanaChains.first; + final keysService = GetIt.I(); + final chainKeys = keysService.getKeysForChain('solana'); + GetIt.I + .get(instanceName: _selectedChain!.chainId) + .getBalance(address: chainKeys.first.address) + .then((value) => setState(() => _balance = value)); + } + @override Widget build(BuildContext context) { final keysService = GetIt.I(); @@ -350,6 +417,41 @@ class _SolanaAccounts extends StatelessWidget { ], ), ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 12.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Expanded( + child: Text( + '${_balance.toStringAsFixed(3)} SOL', + style: TextStyle( + fontSize: 15.0, + fontWeight: FontWeight.bold, + ), + ), + ), + DropdownButton( + value: _selectedChain, + items: ChainsDataList.solanaChains.map((e) { + return DropdownMenuItem( + value: e, + child: Text(e.name), + ); + }).toList(), + onChanged: (ChainMetadata? chain) { + setState(() => _selectedChain = chain); + final chainKey = chainKeys.first; + GetIt.I + .get(instanceName: chain?.chainId) + .getBalance(address: chainKey.address) + .then((value) => setState(() => _balance = value)); + }, + ), + ], + ), + ), Padding( padding: const EdgeInsets.symmetric(horizontal: 12.0), child: Column( From ee056dd4891c742a9552fc641a366b37e5020232 Mon Sep 17 00:00:00 2001 From: Alfreedom <00tango.bromine@icloud.com> Date: Tue, 22 Oct 2024 16:39:48 +0100 Subject: [PATCH 06/17] minor changes and updates --- packages/reown_appkit/CHANGELOG.md | 2 +- packages/reown_appkit/example/base/lib/main.dart | 2 +- .../reown_appkit/lib/modal/appkit_modal_impl.dart | 2 +- .../public/appkit_modal_main_wallets_page.dart | 2 +- packages/reown_appkit/lib/version.dart | 2 +- packages/reown_appkit/pubspec.yaml | 14 ++++---------- 6 files changed, 9 insertions(+), 15 deletions(-) diff --git a/packages/reown_appkit/CHANGELOG.md b/packages/reown_appkit/CHANGELOG.md index 636eed5..80d0d80 100644 --- a/packages/reown_appkit/CHANGELOG.md +++ b/packages/reown_appkit/CHANGELOG.md @@ -1,4 +1,4 @@ -## 1.2.0-beta01 +## 1.2.0-alpha01 - Non-EVM Chains support diff --git a/packages/reown_appkit/example/base/lib/main.dart b/packages/reown_appkit/example/base/lib/main.dart index 4188b0a..e9af78c 100644 --- a/packages/reown_appkit/example/base/lib/main.dart +++ b/packages/reown_appkit/example/base/lib/main.dart @@ -134,7 +134,7 @@ class _MyHomePageState extends State { _appKit = ReownAppKit( core: ReownCore( projectId: DartDefines.projectId, - logLevel: LogLevel.debug, + logLevel: LogLevel.info, ), metadata: _pairingMetadata(), ); diff --git a/packages/reown_appkit/lib/modal/appkit_modal_impl.dart b/packages/reown_appkit/lib/modal/appkit_modal_impl.dart index ddce504..3b27493 100644 --- a/packages/reown_appkit/lib/modal/appkit_modal_impl.dart +++ b/packages/reown_appkit/lib/modal/appkit_modal_impl.dart @@ -568,7 +568,7 @@ class ReownAppKitModal with ChangeNotifier implements IReownAppKitModal { } _lastChainEmitted = caip2Chain; } - // loadAccountData(); + loadAccountData(); } @override diff --git a/packages/reown_appkit/lib/modal/pages/public/appkit_modal_main_wallets_page.dart b/packages/reown_appkit/lib/modal/pages/public/appkit_modal_main_wallets_page.dart index 326ab7d..06bc6f4 100644 --- a/packages/reown_appkit/lib/modal/pages/public/appkit_modal_main_wallets_page.dart +++ b/packages/reown_appkit/lib/modal/pages/public/appkit_modal_main_wallets_page.dart @@ -149,7 +149,7 @@ class _AppKitModalMainWalletsPageState child: (!modalInstance.featuresConfig.showMainWallets && (emailEnabled || socials.isNotEmpty)) ? AllWalletsItem( - title: 'Connect wallet', + title: 'Continue with a wallet', titleAlign: TextAlign.center, leading: RoundedIcon( padding: 10.0, diff --git a/packages/reown_appkit/lib/version.dart b/packages/reown_appkit/lib/version.dart index 1ee4245..21005a6 100644 --- a/packages/reown_appkit/lib/version.dart +++ b/packages/reown_appkit/lib/version.dart @@ -1,2 +1,2 @@ // Generated code. Do not modify. -const packageVersion = '1.2.0-beta01'; +const packageVersion = '1.2.0-alpha01'; diff --git a/packages/reown_appkit/pubspec.yaml b/packages/reown_appkit/pubspec.yaml index d147ddf..3d2a506 100644 --- a/packages/reown_appkit/pubspec.yaml +++ b/packages/reown_appkit/pubspec.yaml @@ -1,6 +1,6 @@ name: reown_appkit description: "Reown is the onchain UX platform that provides toolkits built on top of the WalletConnect Network" -version: 1.2.0-beta01 +version: 1.2.0-alpha01 homepage: https://github.com/reown-com/reown_flutter repository: https://github.com/reown-com/reown_flutter/tree/master/packages/reown_appkit documentation: https://docs.reown.com/appkit/flutter/core/installation @@ -26,12 +26,8 @@ dependencies: json_annotation: ^4.8.1 plugin_platform_interface: ^2.1.8 qr_flutter_wc: ^0.0.3 - # reown_core: ^1.0.1 - reown_core: - path: ../reown_core/ - # reown_sign: ^1.0.1 - reown_sign: - path: ../reown_sign/ + reown_core: ^1.0.2 + reown_sign: ^1.0.2 shimmer: ^3.0.0 uuid: ^4.5.1 webview_flutter: ^4.8.0 @@ -50,9 +46,7 @@ dev_dependencies: logger: ^2.2.0 mockito: ^5.4.3 package_info_plus: ^8.0.2 - # reown_walletkit: ^1.0.1 - reown_walletkit: - path: ../reown_walletkit/ + reown_walletkit: ^1.0.2 # For information on the generic Dart part of this file, see the # following page: https://dart.dev/tools/pub/pubspec From 003ea76d6dab0c5f89e2cee53fa7954f374ec301 Mon Sep 17 00:00:00 2001 From: Alfreedom <00tango.bromine@icloud.com> Date: Mon, 28 Oct 2024 14:00:12 +0100 Subject: [PATCH 07/17] minor change --- packages/reown_appkit/example/base/lib/main.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/reown_appkit/example/base/lib/main.dart b/packages/reown_appkit/example/base/lib/main.dart index e9af78c..5d31dc8 100644 --- a/packages/reown_appkit/example/base/lib/main.dart +++ b/packages/reown_appkit/example/base/lib/main.dart @@ -1,4 +1,5 @@ import 'dart:convert'; +import 'dart:developer'; import 'dart:io'; import 'package:flutter/foundation.dart'; @@ -350,7 +351,7 @@ class _MyHomePageState extends State { } void _onSessionConnect(SessionConnect? event) { - debugPrint('[SampleDapp] _onSessionConnect $event'); + log('[SampleDapp] _onSessionConnect ${jsonEncode(event?.session.toJson())}'); } void _onSessionAuthResponse(SessionAuthResponse? response) { From 07599c2ff0e4cc5371903a551f18d8c8f0cbe52e Mon Sep 17 00:00:00 2001 From: Alfreedom <00tango.bromine@icloud.com> Date: Tue, 29 Oct 2024 15:01:53 +0100 Subject: [PATCH 08/17] Minor fixes --- .../base/ios/Runner/Info-internal.plist | 47 +++++- .../example/base/ios/Runner/Info.plist | 47 +++++- .../base/lib/utils/sample_wallets.dart | 4 +- .../example/modal/ios/Runner/Info.plist | 2 +- .../example/modal/lib/home_page.dart | 8 +- .../modal/lib/widgets/debug_drawer.dart | 6 +- .../lib/modal/appkit_modal_impl.dart | 141 ++++++++++++------ .../lib/modal/constants/string_constants.dart | 13 +- .../lib/modal/i_appkit_modal_impl.dart | 1 - .../models/public/appkit_modal_session.dart | 4 + .../lib/modal/pages/connet_network_page.dart | 14 +- .../appkit_modal_main_wallets_page.dart | 5 +- .../models/wc_sample_wallets.dart | 4 +- .../public/appkit_modal_balance_button.dart | 3 +- .../network_service_items_listener.dart | 2 +- packages/reown_appkit/pubspec.yaml | 6 + 16 files changed, 235 insertions(+), 72 deletions(-) diff --git a/packages/reown_appkit/example/base/ios/Runner/Info-internal.plist b/packages/reown_appkit/example/base/ios/Runner/Info-internal.plist index dbaa42e..0327438 100644 --- a/packages/reown_appkit/example/base/ios/Runner/Info-internal.plist +++ b/packages/reown_appkit/example/base/ios/Runner/Info-internal.plist @@ -41,8 +41,53 @@ LSApplicationQueriesSchemes - wcflutterwallet-internal + wirexwallet + stasis + omni + strikex + bitcoincom + bnc + kryptogo + roninwallet + moonstake + ripio + frontier + qubic + dropp + safepalwallet + bee + shido + foxwallet + exodus + coolwallet + shinobi-wallet + halowallet + spotonchain + rainbow + obvious + robinhood-wallet + cbwallet + okto + bitkeep + bitizen + ape + uniswap + zerion + oasys-wallet + coinstats + ledgerlive + safe + okex + trust + thorwallet + krakenwallet + coinwallet + mewwallet + metamask + avacus walletapp + wcflutterwallet-internal + rn-web3wallet-internal ITSAppUsesNonExemptEncryption diff --git a/packages/reown_appkit/example/base/ios/Runner/Info.plist b/packages/reown_appkit/example/base/ios/Runner/Info.plist index f70379f..43ad88f 100644 --- a/packages/reown_appkit/example/base/ios/Runner/Info.plist +++ b/packages/reown_appkit/example/base/ios/Runner/Info.plist @@ -43,8 +43,53 @@ LSApplicationQueriesSchemes - wcflutterwallet + wirexwallet + stasis + omni + strikex + bitcoincom + bnc + kryptogo + roninwallet + moonstake + ripio + frontier + qubic + dropp + safepalwallet + bee + shido + foxwallet + exodus + coolwallet + shinobi-wallet + halowallet + spotonchain + rainbow + obvious + robinhood-wallet + cbwallet + okto + bitkeep + bitizen + ape + uniswap + zerion + oasys-wallet + coinstats + ledgerlive + safe + okex + trust + thorwallet + krakenwallet + coinwallet + mewwallet + metamask + avacus walletapp + wcflutterwallet + rn-web3wallet LSRequiresIPhoneOS diff --git a/packages/reown_appkit/example/base/lib/utils/sample_wallets.dart b/packages/reown_appkit/example/base/lib/utils/sample_wallets.dart index 24b43c3..cc3a800 100644 --- a/packages/reown_appkit/example/base/lib/utils/sample_wallets.dart +++ b/packages/reown_appkit/example/base/lib/utils/sample_wallets.dart @@ -23,7 +23,7 @@ class WCSampleWallets { 'name': 'RN Wallet (internal)', 'platform': ['ios', 'android'], 'id': '1234567890123456789012345678922', - 'schema': 'rn-web3wallet://wc', + 'schema': 'rn-web3wallet-internal://', 'bundleId': 'com.walletconnect.web3wallet.rnsample.internal', 'universal': 'https://appkit-lab.reown.com/rn_walletkit_internal', }, @@ -58,7 +58,7 @@ class WCSampleWallets { 'name': 'RN Wallet', 'platform': ['ios', 'android'], 'id': '123456789012345678901234567892', - 'schema': 'rn-web3wallet://wc', + 'schema': 'rn-web3wallet://', 'bundleId': 'com.walletconnect.web3wallet.rnsample', 'universal': 'https://appkit-lab.reown.com/rn_walletkit', }, diff --git a/packages/reown_appkit/example/modal/ios/Runner/Info.plist b/packages/reown_appkit/example/modal/ios/Runner/Info.plist index 119d42e..a97c4c3 100644 --- a/packages/reown_appkit/example/modal/ios/Runner/Info.plist +++ b/packages/reown_appkit/example/modal/ios/Runner/Info.plist @@ -69,7 +69,6 @@ cbwallet okto bitkeep - hyperPay bitizen ape uniswap @@ -90,6 +89,7 @@ wcflutterwallet wcflutterwallet-internal rn-web3wallet + rn-web3wallet-internal LSRequiresIPhoneOS diff --git a/packages/reown_appkit/example/modal/lib/home_page.dart b/packages/reown_appkit/example/modal/lib/home_page.dart index d4d9652..2c1676f 100644 --- a/packages/reown_appkit/example/modal/lib/home_page.dart +++ b/packages/reown_appkit/example/modal/lib/home_page.dart @@ -207,10 +207,14 @@ class _MyHomePageState extends State { final siweAuthValue = prefs.getBool('appkit_siwe_auth') ?? true; // See https://docs.reown.com/appkit/flutter/core/custom-chains + // Add extra chains // final extraChains = ReownAppKitModalNetworks.extra['eip155']!; // ReownAppKitModalNetworks.addSupportedNetworks('eip155', extraChains); - // ReownAppKitModalNetworks.removeSupportedNetworks('eip155'); + // Remove Solana support + // ReownAppKitModalNetworks.removeSupportedNetworks('solana'); + // Remove every test network // ReownAppKitModalNetworks.removeTestNetworks(); + // Add custom chains ReownAppKitModalNetworks.addSupportedNetworks('polkadot', [ ReownAppKitModalNetworkInfo( name: 'Polkadot', @@ -247,7 +251,7 @@ class _MyHomePageState extends State { AppKitSocialOption.Apple, AppKitSocialOption.Discord, ], - showMainWallets: false, // OPTIONAL - true by default + // showMainWallets: false, // OPTIONAL - true by default ) : null, // requiredNamespaces: {}, diff --git a/packages/reown_appkit/example/modal/lib/widgets/debug_drawer.dart b/packages/reown_appkit/example/modal/lib/widgets/debug_drawer.dart index 7db646e..c74f71a 100644 --- a/packages/reown_appkit/example/modal/lib/widgets/debug_drawer.dart +++ b/packages/reown_appkit/example/modal/lib/widgets/debug_drawer.dart @@ -182,7 +182,7 @@ class _DebugDrawerState extends State with WidgetsBindingObserver { color: ReownAppKitModalTheme.colorsOf(context).foreground100, ), - title: const Text('Analytics On'), + title: const Text('Analytics'), titleTextStyle: TextStyle( color: ReownAppKitModalTheme.colorsOf(context).foreground100, @@ -203,7 +203,7 @@ class _DebugDrawerState extends State with WidgetsBindingObserver { color: ReownAppKitModalTheme.colorsOf(context).foreground100, ), - title: const Text('Email Wallet On'), + title: const Text('Email & Socials'), titleTextStyle: TextStyle( color: ReownAppKitModalTheme.colorsOf(context).foreground100, @@ -224,7 +224,7 @@ class _DebugDrawerState extends State with WidgetsBindingObserver { color: ReownAppKitModalTheme.colorsOf(context).foreground100, ), - title: const Text('1-CA + SIWE On'), + title: const Text('1-CA + SIWE'), titleTextStyle: TextStyle( color: ReownAppKitModalTheme.colorsOf(context).foreground100, diff --git a/packages/reown_appkit/lib/modal/appkit_modal_impl.dart b/packages/reown_appkit/lib/modal/appkit_modal_impl.dart index 3b27493..e935a94 100644 --- a/packages/reown_appkit/lib/modal/appkit_modal_impl.dart +++ b/packages/reown_appkit/lib/modal/appkit_modal_impl.dart @@ -329,6 +329,10 @@ class ReownAppKitModal with ChangeNotifier implements IReownAppKitModal { } } + _appKit.core.logger.d('[$runtimeType] wcSessions ${wcSessions.length}'); + _appKit.core.logger + .d('[$runtimeType] _currentSession ${_currentSession?.toJson()}'); + // There's a session stored if (wcSessions.isNotEmpty) { await _storeSession(ReownAppKitModalSession( @@ -477,42 +481,71 @@ class ReownAppKitModal with ChangeNotifier implements IReownAppKitModal { return; } - _chainBalance = null; - final tokenName = chainInfo.currency; - final formattedBalance = CoreUtils.formatChainBalance(_chainBalance); - balanceNotifier.value = '$formattedBalance $tokenName'; + try { + _chainBalance = null; + final tokenName = chainInfo.currency; + final formattedBalance = CoreUtils.formatChainBalance(_chainBalance); + balanceNotifier.value = '$formattedBalance $tokenName'; - final hasValidSession = _isConnected && _currentSession != null; - if (switchChain && hasValidSession && _currentSelectedChainId != null) { - final namespace = ReownAppKitModalNetworks.getNamespaceForChainId( - chainInfo.chainId, - ); - final approvedChains = _currentSession!.getApprovedChains( - namespace: namespace, - ); - final newCaip2Chain = ReownAppKitModalNetworks.getCaip2Chain( - chainInfo.chainId, - ); - final hasChainAlready = (approvedChains ?? []).contains(newCaip2Chain); - if (!hasChainAlready) { - requestSwitchToChain(chainInfo); - final hasSwitchMethod = _currentSession!.hasSwitchMethod(); - if (hasSwitchMethod) { - launchConnectedWallet(); + final hasValidSession = _isConnected && _currentSession != null; + if (switchChain && hasValidSession && _currentSelectedChainId != null) { + final namespace = ReownAppKitModalNetworks.getNamespaceForChainId( + chainInfo.chainId, + ); + final approvedChains = _currentSession!.getApprovedChains( + namespace: namespace, + ); + final newCaip2Chain = ReownAppKitModalNetworks.getCaip2Chain( + chainInfo.chainId, + ); + final hasChainAlready = (approvedChains ?? []).contains(newCaip2Chain); + if (!hasChainAlready) { + requestSwitchToChain(chainInfo); + final hasSwitchMethod = _currentSession!.hasSwitchMethod(); + if (hasSwitchMethod) { + launchConnectedWallet(); + } + } else { + await _setLocalEthChain(chainInfo.chainId, logEvent: logEvent); } } else { await _setLocalEthChain(chainInfo.chainId, logEvent: logEvent); } - } else { - await _setLocalEthChain(chainInfo.chainId, logEvent: logEvent); + } on JsonRpcError catch (e) { + onModalError.broadcast(ModalError(e.message ?? 'An error occurred')); + } on ReownAppKitModalException catch (e) { + onModalError.broadcast(ModalError(e.message)); + } catch (e) { + onModalError.broadcast(ModalError('An error occurred')); } } /// Will get the list of available chains to add - @Deprecated('User getApprovedChains()') @override List? getAvailableChains() { - return getApprovedChains(); + // if there's no session or if supportsAddChain method then every chain can be used + if (_currentSession == null) { + // meaning all chains in the list are available + return null; + } + // Valid only for EVM chains + final hasSwitchMethod = _currentSession!.hasSwitchMethod(); + if (!hasSwitchMethod) { + return getApprovedChains(); + } + + List availableChains = []; + final namespaces = ReownAppKitModalNetworks.getAllSupportedNamespaces(); + for (var ns in namespaces) { + final chains = + ReownAppKitModalNetworks.getAllSupportedNetworks(namespace: ns) + .map( + (e) => '$ns:${e.chainId}', + ) + .toList(); + availableChains.addAll(chains); + } + return availableChains; } /// Will get the list of already approved chains by the wallet (to switch to) @@ -549,7 +582,7 @@ class ReownAppKitModal with ChangeNotifier implements IReownAppKitModal { if (isConnected) { await _storage.set( StorageConstants.selectedChainId, - {'chainId': _currentSelectedChainId!}, + {'chainId': _currentSelectedChainId ?? '1'}, ); } } catch (e) { @@ -823,6 +856,8 @@ class ReownAppKitModal with ChangeNotifier implements IReownAppKitModal { @override Future buildConnectionUri() async { if (!_isConnected) { + /// TODO Qs: How do I handle SIWE if non-EVM chains are included? + /// TODO Qs: How do I handle switch to Solana from EVM chain? try { if (_siweService.enabled) { final walletRedirect = _explorerService.getWalletRedirect( @@ -1281,7 +1316,6 @@ class ReownAppKitModal with ChangeNotifier implements IReownAppKitModal { _optionalNamespaces = {}; _lastChainEmitted = null; _supportsOneClickAuth = false; - _relayConnected = false; _status = ReownAppKitModalStatus.idle; await Future.delayed(Duration(milliseconds: 500)); _notify(); @@ -1434,6 +1468,14 @@ class ReownAppKitModal with ChangeNotifier implements IReownAppKitModal { Future requestSwitchToChain( ReownAppKitModalNetworkInfo newChain, ) async { + final namespace = ReownAppKitModalNetworks.getNamespaceForChainId( + newChain.chainId, + ); + if (namespace != NetworkUtils.eip155) { + // If chain is not EVM then there's no need to request a switch since it doesn't exist such method for non-EVM chains + // Therefor at this point the selected non-EVM chain is either already approved, invalidating the need of a switch call, or not approved, failing with the following error. + throw ReownAppKitModalException('Unsupported Chain'); + } if (_currentSession?.sessionService.isMagic == true) { await selectChain(newChain); return; @@ -1472,9 +1514,20 @@ class ReownAppKitModal with ChangeNotifier implements IReownAppKitModal { try { // Otherwise it meas chain has to be added. return await requestAddChain(newChain); + } on JsonRpcError catch (e) { + _appKit.core.logger.e( + '[$runtimeType] Switch to chain error: ${e.toJson()}', + ); + rethrow; + } on ReownAppKitModalException catch (e) { + _appKit.core.logger.e( + '[$runtimeType] Switch to chain error: ${e.message}', + stackTrace: e.stackTrace, + ); + rethrow; } catch (e, s) { _appKit.core.logger.e( - '[$runtimeType] requestSwitchToChain error: $e', + '[$runtimeType] Switch to chain error: ${e.toString()}', stackTrace: s, ); rethrow; @@ -1506,13 +1559,12 @@ class ReownAppKitModal with ChangeNotifier implements IReownAppKitModal { _currentSelectedChainId = newChain.chainId; await _setSesionAndChainData(_currentSession!); return; + } on JsonRpcError { + await _setLocalEthChain(_currentSelectedChainId!); + rethrow; } catch (e, s) { - _appKit.core.logger.e( - '[$runtimeType] requestAddChain error: $e', - stackTrace: s, - ); await _setLocalEthChain(_currentSelectedChainId!); - throw JsonRpcError(code: 5002, message: 'User rejected methods.'); + throw ReownAppKitModalException(e.toString(), s); } } @@ -1550,12 +1602,13 @@ class ReownAppKitModal with ChangeNotifier implements IReownAppKitModal { final walletId = storedWalletId?['walletId']; await _storage.deleteAll(); await _explorerService.storeRecentWalletId(walletId); - } catch (_) { + } catch (e, s) { + _appKit.core.logger.e('[$runtimeType] _cleanSession $e', stackTrace: s); await _storage.deleteAll(); } if (event) { onModalDisconnect.broadcast(ModalDisconnect( - topic: args?.topic, + topic: args?.topic ?? _currentSession?.topic, id: args?.id, )); } @@ -1988,17 +2041,17 @@ extension _AppKitModalExtension on ReownAppKitModal { onSessionEventEvent.broadcast(args); if (args?.name == EventsConstants.chainChanged) { _currentSelectedChainId = args?.data?.toString(); - } else if (args?.name == EventsConstants.accountsChanged) { - try { - // TODO implement account change - if (_siweService.signOutOnAccountChange) { + } + if (args?.name == EventsConstants.accountsChanged) { + if (_siweService.enabled && _siweService.signOutOnAccountChange) { + try { await _siweService.signOut(); + } catch (e, s) { + _appKit.core.logger.e( + '[$runtimeType] _onSessionEvent error: $e', + stackTrace: s, + ); } - } catch (e, s) { - _appKit.core.logger.e( - '[$runtimeType] _onSessionEvent error: $e', - stackTrace: s, - ); } } _notify(); diff --git a/packages/reown_appkit/lib/modal/constants/string_constants.dart b/packages/reown_appkit/lib/modal/constants/string_constants.dart index 38fd9b8..9d3a426 100644 --- a/packages/reown_appkit/lib/modal/constants/string_constants.dart +++ b/packages/reown_appkit/lib/modal/constants/string_constants.dart @@ -33,13 +33,12 @@ class UIConstants { class StorageConstants { // Storage - static const String recentWalletId = - '${CoreConstants.X_SDK_TYPE}_recentWallet'; - static const String connectedWalletData = - '${CoreConstants.X_SDK_TYPE}_walletData'; - static const String selectedChainId = - '${CoreConstants.X_SDK_TYPE}_selectedChainId'; - static const String modalSession = '${CoreConstants.X_SDK_TYPE}_session'; + static const _prefix = '${CoreConstants.X_SDK_TYPE}:$packageVersion//'; + + static const String recentWalletId = '${_prefix}recentWallet'; + static const String connectedWalletData = '${_prefix}walletData'; + static const String selectedChainId = '${_prefix}selectedChainId'; + static const String modalSession = '${_prefix}session'; } class UrlConstants { diff --git a/packages/reown_appkit/lib/modal/i_appkit_modal_impl.dart b/packages/reown_appkit/lib/modal/i_appkit_modal_impl.dart index cc54c89..d1db8e7 100644 --- a/packages/reown_appkit/lib/modal/i_appkit_modal_impl.dart +++ b/packages/reown_appkit/lib/modal/i_appkit_modal_impl.dart @@ -101,7 +101,6 @@ abstract class IReownAppKitModal with ChangeNotifier { void launchConnectedWallet(); /// List of available chains to be added in connected wallet - @Deprecated('User getApprovedChains()') List? getAvailableChains(); /// List of approved chains by connected wallet diff --git a/packages/reown_appkit/lib/modal/models/public/appkit_modal_session.dart b/packages/reown_appkit/lib/modal/models/public/appkit_modal_session.dart index c9a95e7..6cdcfbe 100644 --- a/packages/reown_appkit/lib/modal/models/public/appkit_modal_session.dart +++ b/packages/reown_appkit/lib/modal/models/public/appkit_modal_session.dart @@ -228,6 +228,10 @@ class ReownAppKitModalSession { // toJson() would convert ReownAppKitModalSession to a SessionData kind of map // no matter if Coinbase Wallet or Email Wallet is connected Map toJson() { + if (_sessionData != null) { + return _sessionData!.toJson(); + } + final sessionData = SessionData( topic: topic ?? '', pairingTopic: pairingTopic ?? '', diff --git a/packages/reown_appkit/lib/modal/pages/connet_network_page.dart b/packages/reown_appkit/lib/modal/pages/connet_network_page.dart index 2ca5274..e85bcc2 100644 --- a/packages/reown_appkit/lib/modal/pages/connet_network_page.dart +++ b/packages/reown_appkit/lib/modal/pages/connet_network_page.dart @@ -61,8 +61,8 @@ class _ConnectNetworkPageState extends State ); await _magicService.switchNetwork(chainId: newCaip2Chain); } else { - _appKitModal!.launchConnectedWallet(); try { + _appKitModal!.launchConnectedWallet(); await _appKitModal!.requestSwitchToChain(widget.chainInfo); final chainId = widget.chainInfo.chainId; final namespace = ReownAppKitModalNetworks.getNamespaceForChainId( @@ -79,8 +79,14 @@ class _ConnectNetworkPageState extends State } }); } + } on JsonRpcError catch (e) { + setState( + () => errorEvent = ModalError(e.message ?? 'An error occurred'), + ); + } on ReownAppKitModalException catch (e) { + setState(() => errorEvent = ModalError(e.message)); } catch (e) { - setState(() {}); + setState(() => errorEvent = ModalError('An error occurred')); } } } @@ -156,7 +162,7 @@ class _ConnectNetworkPageState extends State const SizedBox.square(dimension: 20.0), errorEvent != null ? Text( - 'Switch declined', + errorEvent?.message ?? 'Switch declined', textAlign: TextAlign.center, style: themeData.textStyles.paragraph500.copyWith( color: themeColors.error100, @@ -174,7 +180,7 @@ class _ConnectNetworkPageState extends State const SizedBox.square(dimension: 8.0), errorEvent != null ? Text( - 'Switch can be declined by the user or if a previous request is still active', + 'Switch can be declined by the user or if the wallet doesn\'t support the selected chain.', textAlign: TextAlign.center, style: themeData.textStyles.small500.copyWith( color: themeColors.foreground200, diff --git a/packages/reown_appkit/lib/modal/pages/public/appkit_modal_main_wallets_page.dart b/packages/reown_appkit/lib/modal/pages/public/appkit_modal_main_wallets_page.dart index 06bc6f4..6b33996 100644 --- a/packages/reown_appkit/lib/modal/pages/public/appkit_modal_main_wallets_page.dart +++ b/packages/reown_appkit/lib/modal/pages/public/appkit_modal_main_wallets_page.dart @@ -65,8 +65,11 @@ class _AppKitModalMainWalletsPageState double maxHeight = isPortrait ? (kListItemHeight * 6) : ResponsiveData.maxHeightOf(context); + + final isSignIn = _magicService.isEmailEnabled.value || + _magicService.isSocialEnabled.value; return ModalNavbar( - title: 'Connect wallet', + title: isSignIn ? 'Sign in' : 'Connect wallet', leftAction: NavbarActionButton( asset: 'lib/modal/assets/icons/help.svg', action: () { diff --git a/packages/reown_appkit/lib/modal/services/explorer_service/models/wc_sample_wallets.dart b/packages/reown_appkit/lib/modal/services/explorer_service/models/wc_sample_wallets.dart index 1d40759..ccfa0b3 100644 --- a/packages/reown_appkit/lib/modal/services/explorer_service/models/wc_sample_wallets.dart +++ b/packages/reown_appkit/lib/modal/services/explorer_service/models/wc_sample_wallets.dart @@ -25,7 +25,7 @@ class WCSampleWallets { 'name': 'RN Wallet (internal)', 'platform': ['ios', 'android'], 'id': '1234567890123456789012345678922', - 'schema': 'rn-web3wallet://wc', + 'schema': 'rn-web3wallet-internal://', 'bundleId': 'com.walletconnect.web3wallet.rnsample.internal', 'universal': 'https://appkit-lab.reown.com/rn_walletkit_internal', }, @@ -60,7 +60,7 @@ class WCSampleWallets { 'name': 'RN Wallet', 'platform': ['ios', 'android'], 'id': '123456789012345678901234567892', - 'schema': 'rn-web3wallet://wc', + 'schema': 'rn-web3wallet://', 'bundleId': 'com.walletconnect.web3wallet.rnsample', 'universal': 'https://appkit-lab.reown.com/rn_walletkit', }, diff --git a/packages/reown_appkit/lib/modal/widgets/public/appkit_modal_balance_button.dart b/packages/reown_appkit/lib/modal/widgets/public/appkit_modal_balance_button.dart index 0013f91..3b67c81 100644 --- a/packages/reown_appkit/lib/modal/widgets/public/appkit_modal_balance_button.dart +++ b/packages/reown_appkit/lib/modal/widgets/public/appkit_modal_balance_button.dart @@ -48,7 +48,6 @@ class _AppKitModalBalanceButtonState extends State { final imageId = ReownAppKitModalNetworks.getNetworkIconId(chainId); _tokenImage = GetIt.I().getAssetImageUrl(imageId); final balance = widget.appKitModal.balanceNotifier.value; - debugPrint('[$runtimeType] $balance'); if (balance.contains(AppKitModalBalanceButton.balanceDefault)) { _tokenImage = ''; } @@ -118,7 +117,7 @@ class _AppKitModalBalanceButtonState extends State { ) : RoundedIcon( imageUrl: _tokenImage!, - size: widget.size.height + 2.0, + size: widget.size.height * 0.55, ), const SizedBox.square(dimension: 4.0), ValueListenableBuilder( diff --git a/packages/reown_appkit/lib/modal/widgets/value_listenable_builders/network_service_items_listener.dart b/packages/reown_appkit/lib/modal/widgets/value_listenable_builders/network_service_items_listener.dart index e843c18..1d2eacd 100644 --- a/packages/reown_appkit/lib/modal/widgets/value_listenable_builders/network_service_items_listener.dart +++ b/packages/reown_appkit/lib/modal/widgets/value_listenable_builders/network_service_items_listener.dart @@ -42,7 +42,7 @@ class NetworkServiceItemsListener extends StatelessWidget { extension on List> { List> parseItems(BuildContext context) { final service = ModalProvider.of(context).instance; - final supportedChains = service.getApprovedChains(); + final supportedChains = service.getAvailableChains(); if (supportedChains == null) { return this ..sort((a, b) { diff --git a/packages/reown_appkit/pubspec.yaml b/packages/reown_appkit/pubspec.yaml index 3d2a506..ec6a7c6 100644 --- a/packages/reown_appkit/pubspec.yaml +++ b/packages/reown_appkit/pubspec.yaml @@ -27,7 +27,11 @@ dependencies: plugin_platform_interface: ^2.1.8 qr_flutter_wc: ^0.0.3 reown_core: ^1.0.2 + # reown_core: + # path: ../reown_core/ reown_sign: ^1.0.2 + # reown_sign: + # path: ../reown_sign/ shimmer: ^3.0.0 uuid: ^4.5.1 webview_flutter: ^4.8.0 @@ -47,6 +51,8 @@ dev_dependencies: mockito: ^5.4.3 package_info_plus: ^8.0.2 reown_walletkit: ^1.0.2 + # reown_walletkit: + # path: ../reown_walletkit/ # For information on the generic Dart part of this file, see the # following page: https://dart.dev/tools/pub/pubspec From 71d2cea92a2989dd022bf3900f97972a0f9aef78 Mon Sep 17 00:00:00 2001 From: Alfreedom <00tango.bromine@icloud.com> Date: Wed, 30 Oct 2024 15:18:08 +0100 Subject: [PATCH 09/17] Minor changes --- .../reown_appkit/example/base/lib/main.dart | 2 +- .../example/base/lib/pages/connect_page.dart | 155 +++--------------- .../base/lib/utils/deep_link_handler.dart | 9 +- .../example/modal/lib/home_page.dart | 92 +++++------ .../lib/modal/appkit_modal_impl.dart | 31 +++- .../lib/modal/constants/string_constants.dart | 10 +- .../models/public/appkit_modal_session.dart | 5 +- .../lib/modal/pages/account_page.dart | 1 + .../coinbase_service/coinbase_service.dart | 21 +-- .../coinbase_service/i_coinbase_service.dart | 2 + .../services/magic_service/magic_service.dart | 22 +-- .../test/shared/shared_test_utils.mocks.dart | 7 +- .../lib/dependencies/deep_link_handler.dart | 7 +- .../lib/dependencies/walletkit_service.dart | 2 +- .../test/shared/shared_test_utils.mocks.dart | 7 +- 15 files changed, 139 insertions(+), 234 deletions(-) diff --git a/packages/reown_appkit/example/base/lib/main.dart b/packages/reown_appkit/example/base/lib/main.dart index 5d31dc8..d1a37c7 100644 --- a/packages/reown_appkit/example/base/lib/main.dart +++ b/packages/reown_appkit/example/base/lib/main.dart @@ -135,7 +135,7 @@ class _MyHomePageState extends State { _appKit = ReownAppKit( core: ReownCore( projectId: DartDefines.projectId, - logLevel: LogLevel.info, + logLevel: LogLevel.all, ), metadata: _pairingMetadata(), ); diff --git a/packages/reown_appkit/example/base/lib/pages/connect_page.dart b/packages/reown_appkit/example/base/lib/pages/connect_page.dart index 9ffd002..978b8c0 100644 --- a/packages/reown_appkit/example/base/lib/pages/connect_page.dart +++ b/packages/reown_appkit/example/base/lib/pages/connect_page.dart @@ -13,7 +13,6 @@ import 'package:reown_appkit_dapp/utils/crypto/eip155.dart'; import 'package:reown_appkit_dapp/utils/crypto/helpers.dart'; import 'package:reown_appkit_dapp/utils/crypto/polkadot.dart'; import 'package:reown_appkit_dapp/utils/crypto/solana.dart'; -// import 'package:reown_appkit_dapp/utils/sample_wallets.dart'; import 'package:reown_appkit_dapp/utils/string_constants.dart'; import 'package:reown_appkit_dapp/widgets/chain_button.dart'; import 'package:reown_appkit_dapp/widgets/method_dialog.dart'; @@ -230,36 +229,18 @@ class ConnectPageState extends State { visible: !widget.appKitModal.isConnected, child: Column( children: [ - // Row( - // children: [ - // Expanded( - // child: const Divider(height: 1.0), - // ), - // const Text( - // ' Or ', - // style: StyleConstants.buttonText, - // textAlign: TextAlign.center, - // ), - // Expanded( - // child: const Divider(height: 1.0), - // ), - // ], - // ), - // const SizedBox(height: StyleConstants.linear16), - // const Text( - // 'Custom connection', - // style: StyleConstants.buttonText, - // textAlign: TextAlign.center, - // ), Row( mainAxisAlignment: MainAxisAlignment.center, children: [ - Text( - 'non-EVM', - style: TextStyle( - fontWeight: !widget.linkMode - ? FontWeight.bold - : FontWeight.normal, + Expanded( + child: Text( + 'non-EVM\nSession Proposal', + textAlign: TextAlign.end, + style: TextStyle( + fontWeight: !widget.linkMode + ? FontWeight.bold + : FontWeight.normal, + ), ), ), Switch( @@ -268,117 +249,18 @@ class ConnectPageState extends State { widget.reinitialize(value); }, ), - Text( - 'Link Mode', - style: TextStyle( - fontWeight: - widget.linkMode ? FontWeight.bold : FontWeight.normal, + Expanded( + child: Text( + 'only EVM\nLink Mode', + style: TextStyle( + fontWeight: widget.linkMode + ? FontWeight.bold + : FontWeight.normal, + ), ), ), ], ), - // const SizedBox(height: StyleConstants.linear16), - // Wrap( - // spacing: 10.0, - // children: chainButtons, - // ), - // // const Divider(), - // const Text('Test chains'), - // Wrap( - // spacing: 10.0, - // children: testButtons, - // ), - // const SizedBox(height: StyleConstants.linear16), - // // const Divider(), - // !_linkMode - // ? Column( - // crossAxisAlignment: CrossAxisAlignment.center, - // children: [ - // const Text( - // 'Session Propose:', - // style: StyleConstants.buttonText, - // ), - // const SizedBox(height: StyleConstants.linear8), - // Column( - // children: - // WCSampleWallets.getSampleWallets().map((wallet) { - // return Padding( - // padding: const EdgeInsets.only(bottom: 8.0), - // child: ElevatedButton( - // style: _buttonStyle, - // onPressed: _selectedChains.isEmpty - // ? null - // : () { - // _onConnect( - // nativeLink: '${wallet['schema']}', - // closeModal: () { - // if (Navigator.canPop(context)) { - // Navigator.of(context).pop(); - // } - // }, - // showToast: (m) async { - // showPlatformToast( - // child: Text(m), - // context: context, - // ); - // }, - // ); - // }, - // child: Text( - // '${wallet['name']}', - // style: StyleConstants.buttonText, - // ), - // ), - // ); - // }).toList(), - // ), - // ], - // ) - // : Column( - // crossAxisAlignment: CrossAxisAlignment.center, - // children: [ - // const Text( - // 'Link Mode:', - // style: StyleConstants.buttonText, - // ), - // const SizedBox(height: StyleConstants.linear8), - // Column( - // children: - // WCSampleWallets.getSampleWallets().map((wallet) { - // return Padding( - // padding: const EdgeInsets.only(bottom: 8.0), - // child: ElevatedButton( - // style: _buttonStyle, - // onPressed: _selectedChains.isEmpty - // ? null - // : () { - // _sessionAuthenticate( - // nativeLink: '${wallet['schema']}', - // universalLink: - // '${wallet['universal']}', - // closeModal: () { - // if (Navigator.canPop(context)) { - // Navigator.of(context).pop(); - // } - // }, - // showToast: (message) { - // showPlatformToast( - // child: Text(message), - // context: context, - // ); - // }, - // ); - // }, - // child: Text( - // '${wallet['name']}', - // style: StyleConstants.buttonText, - // ), - // ), - // ); - // }).toList(), - // ), - // ], - // ), ], ), ), @@ -434,6 +316,7 @@ class ConnectPageState extends State { []; } + // ignore: unused_element Future _onConnect({ required String nativeLink, VoidCallback? closeModal, @@ -465,6 +348,7 @@ class ConnectPageState extends State { closeModal?.call(); } + // ignore: unused_element void _sessionAuthenticate({ required String nativeLink, required String universalLink, @@ -606,6 +490,7 @@ class ConnectPageState extends State { setState(() {}); } + // ignore: unused_element ButtonStyle get _buttonStyle => ButtonStyle( backgroundColor: MaterialStateProperty.resolveWith( (states) { diff --git a/packages/reown_appkit/example/base/lib/utils/deep_link_handler.dart b/packages/reown_appkit/example/base/lib/utils/deep_link_handler.dart index ccf15e9..f150e39 100644 --- a/packages/reown_appkit/example/base/lib/utils/deep_link_handler.dart +++ b/packages/reown_appkit/example/base/lib/utils/deep_link_handler.dart @@ -45,11 +45,12 @@ class DeepLinkHandler { static String get host => universalUri.host; static void _onLink(dynamic link) async { + debugPrint('[SampleDapp] _onLink $link'); if (link == null) return; - final envelope = ReownCoreUtils.getSearchParamFromURL(link, 'wc_ev'); - if (envelope.isNotEmpty) { - debugPrint('[SampleDapp] is linkMode $link'); - await _appKit.dispatchEnvelope(link); + try { + return await _appKit.dispatchEnvelope(link); + } catch (e) { + debugPrint('[SampleDapp] _onLink error $e'); } } diff --git a/packages/reown_appkit/example/modal/lib/home_page.dart b/packages/reown_appkit/example/modal/lib/home_page.dart index 2c1676f..18a86ed 100644 --- a/packages/reown_appkit/example/modal/lib/home_page.dart +++ b/packages/reown_appkit/example/modal/lib/home_page.dart @@ -211,28 +211,28 @@ class _MyHomePageState extends State { // final extraChains = ReownAppKitModalNetworks.extra['eip155']!; // ReownAppKitModalNetworks.addSupportedNetworks('eip155', extraChains); // Remove Solana support - // ReownAppKitModalNetworks.removeSupportedNetworks('solana'); + ReownAppKitModalNetworks.removeSupportedNetworks('solana'); // Remove every test network // ReownAppKitModalNetworks.removeTestNetworks(); // Add custom chains - ReownAppKitModalNetworks.addSupportedNetworks('polkadot', [ - ReownAppKitModalNetworkInfo( - name: 'Polkadot', - chainId: '91b171bb158e2d3848fa23a9f1c25182', - chainIcon: 'https://cryptologos.cc/logos/polkadot-new-dot-logo.png', - currency: 'DOT', - rpcUrl: 'https://rpc.polkadot.io', - explorerUrl: 'https://polkadot.subscan.io', - ), - ReownAppKitModalNetworkInfo( - name: 'Westend', - chainId: 'e143f23803ac50e8f6f8e62695d1ce9e', - currency: 'DOT', - rpcUrl: 'https://westend-rpc.polkadot.io', - explorerUrl: 'https://westend.subscan.io', - isTestNetwork: true, - ), - ]); + // ReownAppKitModalNetworks.addSupportedNetworks('polkadot', [ + // ReownAppKitModalNetworkInfo( + // name: 'Polkadot', + // chainId: '91b171bb158e2d3848fa23a9f1c25182', + // chainIcon: 'https://cryptologos.cc/logos/polkadot-new-dot-logo.png', + // currency: 'DOT', + // rpcUrl: 'https://rpc.polkadot.io', + // explorerUrl: 'https://polkadot.subscan.io', + // ), + // ReownAppKitModalNetworkInfo( + // name: 'Westend', + // chainId: 'e143f23803ac50e8f6f8e62695d1ce9e', + // currency: 'DOT', + // rpcUrl: 'https://westend-rpc.polkadot.io', + // explorerUrl: 'https://westend.subscan.io', + // isTestNetwork: true, + // ), + // ]); try { _appKitModal = ReownAppKitModal( @@ -272,33 +272,33 @@ class _MyHomePageState extends State { // // You could place here your own getBalance method // return 0.123; // }, - optionalNamespaces: { - 'eip155': RequiredNamespace.fromJson({ - 'chains': ReownAppKitModalNetworks.getAllSupportedNetworks( - namespace: 'eip155', - ).map((chain) => 'eip155:${chain.chainId}').toList(), - 'methods': NetworkUtils.defaultNetworkMethods['eip155']!.toList(), - 'events': NetworkUtils.defaultNetworkEvents['eip155']!.toList(), - }), - 'solana': RequiredNamespace.fromJson({ - 'chains': ReownAppKitModalNetworks.getAllSupportedNetworks( - namespace: 'solana', - ).map((chain) => 'solana:${chain.chainId}').toList(), - 'methods': NetworkUtils.defaultNetworkMethods['solana']!.toList(), - 'events': [], - }), - 'polkadot': RequiredNamespace.fromJson({ - 'chains': [ - 'polkadot:91b171bb158e2d3848fa23a9f1c25182', - 'polkadot:e143f23803ac50e8f6f8e62695d1ce9e' - ], - 'methods': [ - 'polkadot_signMessage', - 'polkadot_signTransaction', - ], - 'events': [] - }), - }, + // optionalNamespaces: { + // 'eip155': RequiredNamespace.fromJson({ + // 'chains': ReownAppKitModalNetworks.getAllSupportedNetworks( + // namespace: 'eip155', + // ).map((chain) => 'eip155:${chain.chainId}').toList(), + // 'methods': NetworkUtils.defaultNetworkMethods['eip155']!.toList(), + // 'events': NetworkUtils.defaultNetworkEvents['eip155']!.toList(), + // }), + // 'solana': RequiredNamespace.fromJson({ + // 'chains': ReownAppKitModalNetworks.getAllSupportedNetworks( + // namespace: 'solana', + // ).map((chain) => 'solana:${chain.chainId}').toList(), + // 'methods': NetworkUtils.defaultNetworkMethods['solana']!.toList(), + // 'events': [], + // }), + // 'polkadot': RequiredNamespace.fromJson({ + // 'chains': [ + // 'polkadot:91b171bb158e2d3848fa23a9f1c25182', + // 'polkadot:e143f23803ac50e8f6f8e62695d1ce9e' + // ], + // 'methods': [ + // 'polkadot_signMessage', + // 'polkadot_signTransaction', + // ], + // 'events': [] + // }), + // }, ); overlay = OverlayController( const Duration(milliseconds: 200), diff --git a/packages/reown_appkit/lib/modal/appkit_modal_impl.dart b/packages/reown_appkit/lib/modal/appkit_modal_impl.dart index e935a94..6c0a67d 100644 --- a/packages/reown_appkit/lib/modal/appkit_modal_impl.dart +++ b/packages/reown_appkit/lib/modal/appkit_modal_impl.dart @@ -483,9 +483,8 @@ class ReownAppKitModal with ChangeNotifier implements IReownAppKitModal { try { _chainBalance = null; - final tokenName = chainInfo.currency; final formattedBalance = CoreUtils.formatChainBalance(_chainBalance); - balanceNotifier.value = '$formattedBalance $tokenName'; + balanceNotifier.value = '$formattedBalance ${chainInfo.currency}'; final hasValidSession = _isConnected && _currentSession != null; if (switchChain && hasValidSession && _currentSelectedChainId != null) { @@ -534,17 +533,25 @@ class ReownAppKitModal with ChangeNotifier implements IReownAppKitModal { return getApprovedChains(); } + final isMagic = _currentSession!.sessionService.isMagic; + final isCoinbase = _currentSession!.sessionService.isCoinbase; + if (isMagic || isCoinbase) { + return getApprovedChains(); + } + List availableChains = []; final namespaces = ReownAppKitModalNetworks.getAllSupportedNamespaces(); for (var ns in namespaces) { final chains = ReownAppKitModalNetworks.getAllSupportedNetworks(namespace: ns) - .map( - (e) => '$ns:${e.chainId}', - ) + .map((e) => '$ns:${e.chainId}') .toList(); availableChains.addAll(chains); } + if (availableChains.isEmpty) { + return getApprovedChains(); + } + return availableChains; } @@ -1474,7 +1481,7 @@ class ReownAppKitModal with ChangeNotifier implements IReownAppKitModal { if (namespace != NetworkUtils.eip155) { // If chain is not EVM then there's no need to request a switch since it doesn't exist such method for non-EVM chains // Therefor at this point the selected non-EVM chain is either already approved, invalidating the need of a switch call, or not approved, failing with the following error. - throw ReownAppKitModalException('Unsupported Chain'); + throw ReownAppKitModalException('Chain namespace is not supported'); } if (_currentSession?.sessionService.isMagic == true) { await selectChain(newChain); @@ -1596,15 +1603,23 @@ class ReownAppKitModal with ChangeNotifier implements IReownAppKitModal { } } + Future _deleteStorage() async { + for (var key in _storage.keys) { + if (key.startsWith(StorageConstants.prefix)) { + await _storage.delete(key); + } + } + } + Future _cleanSession({SessionDelete? args, bool event = true}) async { try { final storedWalletId = _storage.get(StorageConstants.recentWalletId); final walletId = storedWalletId?['walletId']; - await _storage.deleteAll(); + await _deleteStorage(); await _explorerService.storeRecentWalletId(walletId); } catch (e, s) { _appKit.core.logger.e('[$runtimeType] _cleanSession $e', stackTrace: s); - await _storage.deleteAll(); + await _deleteStorage(); } if (event) { onModalDisconnect.broadcast(ModalDisconnect( diff --git a/packages/reown_appkit/lib/modal/constants/string_constants.dart b/packages/reown_appkit/lib/modal/constants/string_constants.dart index 9d3a426..e29b447 100644 --- a/packages/reown_appkit/lib/modal/constants/string_constants.dart +++ b/packages/reown_appkit/lib/modal/constants/string_constants.dart @@ -33,12 +33,12 @@ class UIConstants { class StorageConstants { // Storage - static const _prefix = '${CoreConstants.X_SDK_TYPE}:$packageVersion//'; + static const prefix = '${CoreConstants.X_SDK_TYPE}:$packageVersion//'; - static const String recentWalletId = '${_prefix}recentWallet'; - static const String connectedWalletData = '${_prefix}walletData'; - static const String selectedChainId = '${_prefix}selectedChainId'; - static const String modalSession = '${_prefix}session'; + static const String recentWalletId = '${prefix}recentWallet'; + static const String connectedWalletData = '${prefix}walletData'; + static const String selectedChainId = '${prefix}selectedChainId'; + static const String modalSession = '${prefix}session'; } class UrlConstants { diff --git a/packages/reown_appkit/lib/modal/models/public/appkit_modal_session.dart b/packages/reown_appkit/lib/modal/models/public/appkit_modal_session.dart index 6cdcfbe..3a6e496 100644 --- a/packages/reown_appkit/lib/modal/models/public/appkit_modal_session.dart +++ b/packages/reown_appkit/lib/modal/models/public/appkit_modal_session.dart @@ -1,5 +1,6 @@ import 'package:get_it/get_it.dart'; import 'package:reown_appkit/modal/services/coinbase_service/coinbase_service.dart'; +import 'package:reown_appkit/modal/services/coinbase_service/i_coinbase_service.dart'; import 'package:reown_appkit/modal/services/coinbase_service/models/coinbase_data.dart'; import 'package:reown_appkit/modal/services/magic_service/i_magic_service.dart'; import 'package:reown_appkit/modal/services/magic_service/models/magic_data.dart'; @@ -110,7 +111,7 @@ class ReownAppKitModalSession { return null; } if (sessionService.isCoinbase) { - return CoinbaseService.supportedMethods; + return GetIt.I().supportedMethods; } if (sessionService.isMagic) { final ns = namespace ?? NetworkUtils.eip155; @@ -372,7 +373,7 @@ extension ReownAppKitModalSessionExtension on ReownAppKitModalSession { eip155: Namespace( chains: [...allEIP155], accounts: [...getAccounts(namespace: eip155)!], - methods: [...CoinbaseService.supportedMethods], + methods: [...GetIt.I().supportedMethods], // Coinbase does not have events as it doesn't use WC protocol events: [], ), diff --git a/packages/reown_appkit/lib/modal/pages/account_page.dart b/packages/reown_appkit/lib/modal/pages/account_page.dart index 2baeb3b..b1cf3e7 100644 --- a/packages/reown_appkit/lib/modal/pages/account_page.dart +++ b/packages/reown_appkit/lib/modal/pages/account_page.dart @@ -257,6 +257,7 @@ class _EmailAndSocialLoginButton extends StatelessWidget { } } +// ignore: unused_element class _ConnectedWalletButton extends StatelessWidget { @override Widget build(BuildContext context) { diff --git a/packages/reown_appkit/lib/modal/services/coinbase_service/coinbase_service.dart b/packages/reown_appkit/lib/modal/services/coinbase_service/coinbase_service.dart index 8f4f41b..c091ba1 100644 --- a/packages/reown_appkit/lib/modal/services/coinbase_service/coinbase_service.dart +++ b/packages/reown_appkit/lib/modal/services/coinbase_service/coinbase_service.dart @@ -53,16 +53,17 @@ class CoinbaseService implements ICoinbaseService { publicKey: '', ); - static const supportedMethods = [ - ...MethodsConstants.requiredMethods, - 'eth_requestAccounts', - 'eth_signTypedData_v3', - 'eth_signTypedData_v4', - 'eth_signTransaction', - MethodsConstants.walletSwitchEthChain, - MethodsConstants.walletAddEthChain, - 'wallet_watchAsset', - ]; + @override + List get supportedMethods => [ + ...MethodsConstants.requiredMethods, + 'eth_requestAccounts', + 'eth_signTypedData_v3', + 'eth_signTypedData_v4', + 'eth_signTransaction', + MethodsConstants.walletSwitchEthChain, + MethodsConstants.walletAddEthChain, + 'wallet_watchAsset', + ]; @override Event onCoinbaseConnect = Event(); diff --git a/packages/reown_appkit/lib/modal/services/coinbase_service/i_coinbase_service.dart b/packages/reown_appkit/lib/modal/services/coinbase_service/i_coinbase_service.dart index 7a93b8c..22fe3c5 100644 --- a/packages/reown_appkit/lib/modal/services/coinbase_service/i_coinbase_service.dart +++ b/packages/reown_appkit/lib/modal/services/coinbase_service/i_coinbase_service.dart @@ -21,6 +21,8 @@ class CoinbaseNotEnabledException extends CoinbaseServiceException { } abstract class ICoinbaseService { + List get supportedMethods; + Future init(); Future isConnected(); Future getAccount(); diff --git a/packages/reown_appkit/lib/modal/services/magic_service/magic_service.dart b/packages/reown_appkit/lib/modal/services/magic_service/magic_service.dart index 6996b41..6828b1c 100644 --- a/packages/reown_appkit/lib/modal/services/magic_service/magic_service.dart +++ b/packages/reown_appkit/lib/modal/services/magic_service/magic_service.dart @@ -61,17 +61,17 @@ class MagicService implements IMagicService { late final WebViewWidget _webview; @override - final Map> supportedMethods = { - NetworkUtils.eip155: [ - 'personal_sign', - 'eth_sendTransaction', - 'eth_accounts', - 'eth_sendRawTransaction', - 'eth_signTypedData_v4', - ], - NetworkUtils.solana: - NetworkUtils.defaultNetworkMethods[NetworkUtils.solana]!, - }; + Map> get supportedMethods => { + NetworkUtils.eip155: [ + 'personal_sign', + 'eth_sendTransaction', + 'eth_accounts', + 'eth_sendRawTransaction', + 'eth_signTypedData_v4', + ], + NetworkUtils.solana: + NetworkUtils.defaultNetworkMethods[NetworkUtils.solana]!, + }; @override WebViewWidget get webview => _webview; diff --git a/packages/reown_appkit/test/shared/shared_test_utils.mocks.dart b/packages/reown_appkit/test/shared/shared_test_utils.mocks.dart index a9cff43..fe4428e 100644 --- a/packages/reown_appkit/test/shared/shared_test_utils.mocks.dart +++ b/packages/reown_appkit/test/shared/shared_test_utils.mocks.dart @@ -1483,15 +1483,14 @@ class MockReownCore extends _i1.Mock implements _i28.ReownCore { ) as _i23.Future); @override - _i23.Future addLinkModeSupportedApp(String? universalLink) => + _i23.Future addLinkModeSupportedApp(String? universalLink) => (super.noSuchMethod( Invocation.method( #addLinkModeSupportedApp, [universalLink], ), - returnValue: _i23.Future.value(), - returnValueForMissingStub: _i23.Future.value(), - ) as _i23.Future); + returnValue: _i23.Future.value(false), + ) as _i23.Future); @override List getLinkModeSupportedApps() => (super.noSuchMethod( diff --git a/packages/reown_walletkit/example/lib/dependencies/deep_link_handler.dart b/packages/reown_walletkit/example/lib/dependencies/deep_link_handler.dart index e429ab2..f15c23b 100644 --- a/packages/reown_walletkit/example/lib/dependencies/deep_link_handler.dart +++ b/packages/reown_walletkit/example/lib/dependencies/deep_link_handler.dart @@ -42,11 +42,12 @@ class DeepLinkHandler { Uri.parse(_walletKit.metadata.redirect?.universal ?? ''); static String get host => universalUri.host; - static void _onLink(Object? event) async { + static void _onLink(dynamic link) async { + debugPrint('[SampleWallet] _onLink $link'); try { - return await _walletKit.dispatchEnvelope('$event'); + return await _walletKit.dispatchEnvelope('$link'); } catch (e) { - final decodedUri = Uri.parse(Uri.decodeFull(event.toString())); + final decodedUri = Uri.parse(Uri.decodeFull(link.toString())); if (decodedUri.isScheme('wc')) { debugPrint('[SampleWallet] is legacy uri $decodedUri'); waiting.value = true; diff --git a/packages/reown_walletkit/example/lib/dependencies/walletkit_service.dart b/packages/reown_walletkit/example/lib/dependencies/walletkit_service.dart index c702dfa..806be58 100644 --- a/packages/reown_walletkit/example/lib/dependencies/walletkit_service.dart +++ b/packages/reown_walletkit/example/lib/dependencies/walletkit_service.dart @@ -53,7 +53,7 @@ class WalletKitService extends IWalletKitService { _walletKit = ReownWalletKit( core: ReownCore( projectId: DartDefines.projectId, - logLevel: LogLevel.info, + logLevel: LogLevel.all, ), metadata: PairingMetadata( name: 'FL WalletKit Sample', diff --git a/packages/reown_walletkit/test/shared/shared_test_utils.mocks.dart b/packages/reown_walletkit/test/shared/shared_test_utils.mocks.dart index e36a4d0..7f5c83e 100644 --- a/packages/reown_walletkit/test/shared/shared_test_utils.mocks.dart +++ b/packages/reown_walletkit/test/shared/shared_test_utils.mocks.dart @@ -1483,15 +1483,14 @@ class MockReownCore extends _i1.Mock implements _i28.ReownCore { ) as _i23.Future); @override - _i23.Future addLinkModeSupportedApp(String? universalLink) => + _i23.Future addLinkModeSupportedApp(String? universalLink) => (super.noSuchMethod( Invocation.method( #addLinkModeSupportedApp, [universalLink], ), - returnValue: _i23.Future.value(), - returnValueForMissingStub: _i23.Future.value(), - ) as _i23.Future); + returnValue: _i23.Future.value(false), + ) as _i23.Future); @override List getLinkModeSupportedApps() => (super.noSuchMethod( From 5201c4496495fbcb34818e5e1be32dbb82821734 Mon Sep 17 00:00:00 2001 From: Alfreedom <00tango.bromine@icloud.com> Date: Wed, 30 Oct 2024 15:59:48 +0100 Subject: [PATCH 10/17] Added logs --- .../example/modal/lib/home_page.dart | 24 +++++++++---------- .../lib/modal/appkit_modal_impl.dart | 10 ++++---- packages/reown_appkit/pubspec.yaml | 18 +++++++------- packages/reown_sign/lib/sign_engine.dart | 2 +- packages/reown_walletkit/pubspec.yaml | 18 +++++++------- 5 files changed, 36 insertions(+), 36 deletions(-) diff --git a/packages/reown_appkit/example/modal/lib/home_page.dart b/packages/reown_appkit/example/modal/lib/home_page.dart index 18a86ed..1391713 100644 --- a/packages/reown_appkit/example/modal/lib/home_page.dart +++ b/packages/reown_appkit/example/modal/lib/home_page.dart @@ -242,18 +242,18 @@ class _MyHomePageState extends State { metadata: _pairingMetadata(), siweConfig: _siweConfig(siweAuthValue), enableAnalytics: analyticsValue, // OPTIONAL - null by default - featuresConfig: emailWalletValue - ? FeaturesConfig( - email: true, - socials: [ - AppKitSocialOption.Farcaster, - AppKitSocialOption.X, - AppKitSocialOption.Apple, - AppKitSocialOption.Discord, - ], - // showMainWallets: false, // OPTIONAL - true by default - ) - : null, + // featuresConfig: emailWalletValue + // ? FeaturesConfig( + // email: true, + // socials: [ + // AppKitSocialOption.Farcaster, + // AppKitSocialOption.X, + // AppKitSocialOption.Apple, + // AppKitSocialOption.Discord, + // ], + // // showMainWallets: false, // OPTIONAL - true by default + // ) + // : null, // requiredNamespaces: {}, // optionalNamespaces: {}, // includedWalletIds: {}, diff --git a/packages/reown_appkit/lib/modal/appkit_modal_impl.dart b/packages/reown_appkit/lib/modal/appkit_modal_impl.dart index 6c0a67d..9055be0 100644 --- a/packages/reown_appkit/lib/modal/appkit_modal_impl.dart +++ b/packages/reown_appkit/lib/modal/appkit_modal_impl.dart @@ -809,11 +809,11 @@ class ReownAppKitModal with ChangeNotifier implements IReownAppKitModal { await _explorerService.storeConnectedWallet(_selectedWallet); } else { await buildConnectionUri(); - await _uriService.openRedirect( - walletRedirect, - wcURI: wcUri!, - pType: pType, - ); + // await _uriService.openRedirect( + // walletRedirect, + // wcURI: wcUri!, + // pType: pType, + // ); } } on LaunchUrlException catch (e) { if (e is CanNotLaunchUrl) { diff --git a/packages/reown_appkit/pubspec.yaml b/packages/reown_appkit/pubspec.yaml index ec6a7c6..4b95d87 100644 --- a/packages/reown_appkit/pubspec.yaml +++ b/packages/reown_appkit/pubspec.yaml @@ -26,12 +26,12 @@ dependencies: json_annotation: ^4.8.1 plugin_platform_interface: ^2.1.8 qr_flutter_wc: ^0.0.3 - reown_core: ^1.0.2 - # reown_core: - # path: ../reown_core/ - reown_sign: ^1.0.2 - # reown_sign: - # path: ../reown_sign/ + # reown_core: ^1.0.3 + reown_core: + path: ../reown_core/ + # reown_sign: ^1.0.3 + reown_sign: + path: ../reown_sign/ shimmer: ^3.0.0 uuid: ^4.5.1 webview_flutter: ^4.8.0 @@ -50,9 +50,9 @@ dev_dependencies: logger: ^2.2.0 mockito: ^5.4.3 package_info_plus: ^8.0.2 - reown_walletkit: ^1.0.2 - # reown_walletkit: - # path: ../reown_walletkit/ + # reown_walletkit: ^1.0.3 + reown_walletkit: + path: ../reown_walletkit/ # For information on the generic Dart part of this file, see the # following page: https://dart.dev/tools/pub/pubspec diff --git a/packages/reown_sign/lib/sign_engine.dart b/packages/reown_sign/lib/sign_engine.dart index 0435a38..552e6e1 100644 --- a/packages/reown_sign/lib/sign_engine.dart +++ b/packages/reown_sign/lib/sign_engine.dart @@ -2283,7 +2283,7 @@ class ReownSign implements IReownSign { ttl: ttl, appLink: isLinkMode ? walletUniversalLink : null, // We don't want to open the appLink in this case as it will be opened by the host app - openUrl: false, + // openUrl: false, ); result = WcSessionAuthRequestResult.fromJson(response); } catch (error) { diff --git a/packages/reown_walletkit/pubspec.yaml b/packages/reown_walletkit/pubspec.yaml index 60d46b7..799f14c 100644 --- a/packages/reown_walletkit/pubspec.yaml +++ b/packages/reown_walletkit/pubspec.yaml @@ -12,12 +12,12 @@ dependencies: event: ^2.1.2 flutter: sdk: flutter - reown_core: ^1.0.3 - # reown_core: - # path: ../reown_core/ - reown_sign: ^1.0.3 - # reown_sign: - # path: ../reown_sign/ + # reown_core: ^1.0.3 + reown_core: + path: ../reown_core/ + # reown_sign: ^1.0.3 + reown_sign: + path: ../reown_sign/ dev_dependencies: build_runner: ^2.4.7 @@ -30,9 +30,9 @@ dev_dependencies: logger: ^2.2.0 mockito: ^5.4.3 package_info_plus: ^8.0.2 - reown_appkit: ^1.0.2 - # reown_appkit: - # path: ../reown_appkit/ + # reown_appkit: ^1.0.2 + reown_appkit: + path: ../reown_appkit/ platforms: android: From ffbe5915534d893f79fe7d80b2479fcd1c04fa9d Mon Sep 17 00:00:00 2001 From: Alfreedom <00tango.bromine@icloud.com> Date: Thu, 31 Oct 2024 13:04:10 +0100 Subject: [PATCH 11/17] fix on link mode --- .../example/base/lib/pages/connect_page.dart | 24 +++++++++++++++++++ .../example/modal/lib/home_page.dart | 24 +++++++++---------- .../lib/modal/appkit_modal_impl.dart | 18 ++++++++++---- packages/reown_appkit/pubspec.yaml | 12 +++++----- .../test/shared/shared_test_utils.mocks.dart | 7 +++--- packages/reown_sign/lib/sign_engine.dart | 2 +- .../test/shared/shared_test_utils.mocks.dart | 7 +++--- .../lib/dependencies/deep_link_handler.dart | 2 +- .../example/lib/pages/settings_page.dart | 23 ++++++++++++++++++ packages/reown_walletkit/pubspec.yaml | 18 +++++++------- .../test/shared/shared_test_utils.mocks.dart | 7 +++--- 11 files changed, 101 insertions(+), 43 deletions(-) diff --git a/packages/reown_appkit/example/base/lib/pages/connect_page.dart b/packages/reown_appkit/example/base/lib/pages/connect_page.dart index 978b8c0..163af43 100644 --- a/packages/reown_appkit/example/base/lib/pages/connect_page.dart +++ b/packages/reown_appkit/example/base/lib/pages/connect_page.dart @@ -594,6 +594,30 @@ class __FooterWidgetState extends State<_FooterWidget> { }, ), const SizedBox(height: StyleConstants.linear8), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Visibility( + visible: !widget.appKitModal.isConnected, + child: SizedBox( + height: 30.0, + child: ElevatedButton( + onPressed: () async { + await widget.appKitModal.appKit!.core.storage.deleteAll(); + ScaffoldMessenger.of(context).showSnackBar(SnackBar( + content: Text('Storage cleared'), + duration: Duration(seconds: 1), + )); + }, + child: Text( + 'CLEAR STORAGE', + style: TextStyle(fontSize: 10.0), + ), + ), + ), + ), + ], + ), ], ); } diff --git a/packages/reown_appkit/example/modal/lib/home_page.dart b/packages/reown_appkit/example/modal/lib/home_page.dart index 1391713..18a86ed 100644 --- a/packages/reown_appkit/example/modal/lib/home_page.dart +++ b/packages/reown_appkit/example/modal/lib/home_page.dart @@ -242,18 +242,18 @@ class _MyHomePageState extends State { metadata: _pairingMetadata(), siweConfig: _siweConfig(siweAuthValue), enableAnalytics: analyticsValue, // OPTIONAL - null by default - // featuresConfig: emailWalletValue - // ? FeaturesConfig( - // email: true, - // socials: [ - // AppKitSocialOption.Farcaster, - // AppKitSocialOption.X, - // AppKitSocialOption.Apple, - // AppKitSocialOption.Discord, - // ], - // // showMainWallets: false, // OPTIONAL - true by default - // ) - // : null, + featuresConfig: emailWalletValue + ? FeaturesConfig( + email: true, + socials: [ + AppKitSocialOption.Farcaster, + AppKitSocialOption.X, + AppKitSocialOption.Apple, + AppKitSocialOption.Discord, + ], + // showMainWallets: false, // OPTIONAL - true by default + ) + : null, // requiredNamespaces: {}, // optionalNamespaces: {}, // includedWalletIds: {}, diff --git a/packages/reown_appkit/lib/modal/appkit_modal_impl.dart b/packages/reown_appkit/lib/modal/appkit_modal_impl.dart index 9055be0..e456e56 100644 --- a/packages/reown_appkit/lib/modal/appkit_modal_impl.dart +++ b/packages/reown_appkit/lib/modal/appkit_modal_impl.dart @@ -809,11 +809,16 @@ class ReownAppKitModal with ChangeNotifier implements IReownAppKitModal { await _explorerService.storeConnectedWallet(_selectedWallet); } else { await buildConnectionUri(); - // await _uriService.openRedirect( - // walletRedirect, - // wcURI: wcUri!, - // pType: pType, - // ); + final linkMode = walletRedirect.linkMode ?? ''; + if (linkMode.isNotEmpty && _wcUri.startsWith(linkMode)) { + await ReownCoreUtils.openURL(_wcUri); + } else { + await _uriService.openRedirect( + walletRedirect, + wcURI: _wcUri, + pType: pType, + ); + } } } on LaunchUrlException catch (e) { if (e is CanNotLaunchUrl) { @@ -885,6 +890,9 @@ class ReownAppKitModal with ChangeNotifier implements IReownAppKitModal { ...p2, }); // One-Click Auth + _appKit.core.logger.d( + '[$runtimeType] authenticate ${jsonEncode(authParams.toJson())}, ${walletRedirect?.linkMode}', + ); final authResponse = await _appKit.authenticate( params: authParams, walletUniversalLink: walletRedirect?.linkMode, diff --git a/packages/reown_appkit/pubspec.yaml b/packages/reown_appkit/pubspec.yaml index 4b95d87..0bd66ae 100644 --- a/packages/reown_appkit/pubspec.yaml +++ b/packages/reown_appkit/pubspec.yaml @@ -26,12 +26,12 @@ dependencies: json_annotation: ^4.8.1 plugin_platform_interface: ^2.1.8 qr_flutter_wc: ^0.0.3 - # reown_core: ^1.0.3 - reown_core: - path: ../reown_core/ - # reown_sign: ^1.0.3 - reown_sign: - path: ../reown_sign/ + reown_core: ^1.0.3 + # reown_core: + # path: ../reown_core/ + reown_sign: ^1.0.3 + # reown_sign: + # path: ../reown_sign/ shimmer: ^3.0.0 uuid: ^4.5.1 webview_flutter: ^4.8.0 diff --git a/packages/reown_appkit/test/shared/shared_test_utils.mocks.dart b/packages/reown_appkit/test/shared/shared_test_utils.mocks.dart index fe4428e..a9cff43 100644 --- a/packages/reown_appkit/test/shared/shared_test_utils.mocks.dart +++ b/packages/reown_appkit/test/shared/shared_test_utils.mocks.dart @@ -1483,14 +1483,15 @@ class MockReownCore extends _i1.Mock implements _i28.ReownCore { ) as _i23.Future); @override - _i23.Future addLinkModeSupportedApp(String? universalLink) => + _i23.Future addLinkModeSupportedApp(String? universalLink) => (super.noSuchMethod( Invocation.method( #addLinkModeSupportedApp, [universalLink], ), - returnValue: _i23.Future.value(false), - ) as _i23.Future); + returnValue: _i23.Future.value(), + returnValueForMissingStub: _i23.Future.value(), + ) as _i23.Future); @override List getLinkModeSupportedApps() => (super.noSuchMethod( diff --git a/packages/reown_sign/lib/sign_engine.dart b/packages/reown_sign/lib/sign_engine.dart index 552e6e1..0435a38 100644 --- a/packages/reown_sign/lib/sign_engine.dart +++ b/packages/reown_sign/lib/sign_engine.dart @@ -2283,7 +2283,7 @@ class ReownSign implements IReownSign { ttl: ttl, appLink: isLinkMode ? walletUniversalLink : null, // We don't want to open the appLink in this case as it will be opened by the host app - // openUrl: false, + openUrl: false, ); result = WcSessionAuthRequestResult.fromJson(response); } catch (error) { diff --git a/packages/reown_sign/test/shared/shared_test_utils.mocks.dart b/packages/reown_sign/test/shared/shared_test_utils.mocks.dart index 234edd8..300e3f7 100644 --- a/packages/reown_sign/test/shared/shared_test_utils.mocks.dart +++ b/packages/reown_sign/test/shared/shared_test_utils.mocks.dart @@ -1483,14 +1483,15 @@ class MockReownCore extends _i1.Mock implements _i28.ReownCore { ) as _i23.Future); @override - _i23.Future addLinkModeSupportedApp(String? universalLink) => + _i23.Future addLinkModeSupportedApp(String? universalLink) => (super.noSuchMethod( Invocation.method( #addLinkModeSupportedApp, [universalLink], ), - returnValue: _i23.Future.value(false), - ) as _i23.Future); + returnValue: _i23.Future.value(), + returnValueForMissingStub: _i23.Future.value(), + ) as _i23.Future); @override List getLinkModeSupportedApps() => (super.noSuchMethod( diff --git a/packages/reown_walletkit/example/lib/dependencies/deep_link_handler.dart b/packages/reown_walletkit/example/lib/dependencies/deep_link_handler.dart index f15c23b..7bcddd9 100644 --- a/packages/reown_walletkit/example/lib/dependencies/deep_link_handler.dart +++ b/packages/reown_walletkit/example/lib/dependencies/deep_link_handler.dart @@ -47,7 +47,7 @@ class DeepLinkHandler { try { return await _walletKit.dispatchEnvelope('$link'); } catch (e) { - final decodedUri = Uri.parse(Uri.decodeFull(link.toString())); + final decodedUri = Uri.parse(Uri.decodeFull('$link')); if (decodedUri.isScheme('wc')) { debugPrint('[SampleWallet] is legacy uri $decodedUri'); waiting.value = true; diff --git a/packages/reown_walletkit/example/lib/pages/settings_page.dart b/packages/reown_walletkit/example/lib/pages/settings_page.dart index abb1156..114b40a 100644 --- a/packages/reown_walletkit/example/lib/pages/settings_page.dart +++ b/packages/reown_walletkit/example/lib/pages/settings_page.dart @@ -81,6 +81,14 @@ class _SettingsPageState extends State { const SizedBox(height: 20.0), const Divider(height: 1.0), _Buttons( + onDeleteData: () async { + final walletKit = GetIt.I().walletKit; + await walletKit.core.storage.deleteAll(); + ScaffoldMessenger.of(context).showSnackBar(SnackBar( + content: Text('Storage cleared'), + duration: Duration(seconds: 1), + )); + }, onRestoreFromSeed: () async { final mnemonic = await GetIt.I().queueBottomSheet( @@ -624,9 +632,11 @@ class _DeviceData extends StatelessWidget { class _Buttons extends StatelessWidget { final VoidCallback onRestoreFromSeed; final VoidCallback onRestoreDefault; + final VoidCallback onDeleteData; const _Buttons({ required this.onRestoreFromSeed, required this.onRestoreDefault, + required this.onDeleteData, }); @override @@ -636,6 +646,19 @@ class _Buttons extends StatelessWidget { child: Column( children: [ const SizedBox(height: 8.0), + SizedBox( + width: double.infinity, + child: ElevatedButton( + onPressed: onDeleteData, + child: Text( + 'Clear local storage', + style: TextStyle( + fontWeight: FontWeight.bold, + ), + ), + ), + ), + const SizedBox(height: 12.0), Row( children: [ CustomButton( diff --git a/packages/reown_walletkit/pubspec.yaml b/packages/reown_walletkit/pubspec.yaml index 799f14c..60d46b7 100644 --- a/packages/reown_walletkit/pubspec.yaml +++ b/packages/reown_walletkit/pubspec.yaml @@ -12,12 +12,12 @@ dependencies: event: ^2.1.2 flutter: sdk: flutter - # reown_core: ^1.0.3 - reown_core: - path: ../reown_core/ - # reown_sign: ^1.0.3 - reown_sign: - path: ../reown_sign/ + reown_core: ^1.0.3 + # reown_core: + # path: ../reown_core/ + reown_sign: ^1.0.3 + # reown_sign: + # path: ../reown_sign/ dev_dependencies: build_runner: ^2.4.7 @@ -30,9 +30,9 @@ dev_dependencies: logger: ^2.2.0 mockito: ^5.4.3 package_info_plus: ^8.0.2 - # reown_appkit: ^1.0.2 - reown_appkit: - path: ../reown_appkit/ + reown_appkit: ^1.0.2 + # reown_appkit: + # path: ../reown_appkit/ platforms: android: diff --git a/packages/reown_walletkit/test/shared/shared_test_utils.mocks.dart b/packages/reown_walletkit/test/shared/shared_test_utils.mocks.dart index 7f5c83e..e36a4d0 100644 --- a/packages/reown_walletkit/test/shared/shared_test_utils.mocks.dart +++ b/packages/reown_walletkit/test/shared/shared_test_utils.mocks.dart @@ -1483,14 +1483,15 @@ class MockReownCore extends _i1.Mock implements _i28.ReownCore { ) as _i23.Future); @override - _i23.Future addLinkModeSupportedApp(String? universalLink) => + _i23.Future addLinkModeSupportedApp(String? universalLink) => (super.noSuchMethod( Invocation.method( #addLinkModeSupportedApp, [universalLink], ), - returnValue: _i23.Future.value(false), - ) as _i23.Future); + returnValue: _i23.Future.value(), + returnValueForMissingStub: _i23.Future.value(), + ) as _i23.Future); @override List getLinkModeSupportedApps() => (super.noSuchMethod( From 9eb551345d79eca80c7d81749d3479613ff1afc7 Mon Sep 17 00:00:00 2001 From: Alfreedom <00tango.bromine@icloud.com> Date: Thu, 31 Oct 2024 15:35:18 +0100 Subject: [PATCH 12/17] final changes in example --- .../example/modal/lib/home_page.dart | 104 ++++++++++-------- .../modal/lib/services/methods_service.dart | 9 +- .../modal/lib/widgets/session_widget.dart | 9 +- packages/reown_appkit/pubspec.yaml | 4 +- .../test/shared/shared_test_utils.mocks.dart | 7 +- .../test/shared/shared_test_utils.mocks.dart | 7 +- 6 files changed, 72 insertions(+), 68 deletions(-) diff --git a/packages/reown_appkit/example/modal/lib/home_page.dart b/packages/reown_appkit/example/modal/lib/home_page.dart index 18a86ed..f48418b 100644 --- a/packages/reown_appkit/example/modal/lib/home_page.dart +++ b/packages/reown_appkit/example/modal/lib/home_page.dart @@ -210,29 +210,32 @@ class _MyHomePageState extends State { // Add extra chains // final extraChains = ReownAppKitModalNetworks.extra['eip155']!; // ReownAppKitModalNetworks.addSupportedNetworks('eip155', extraChains); - // Remove Solana support - ReownAppKitModalNetworks.removeSupportedNetworks('solana'); // Remove every test network // ReownAppKitModalNetworks.removeTestNetworks(); - // Add custom chains - // ReownAppKitModalNetworks.addSupportedNetworks('polkadot', [ - // ReownAppKitModalNetworkInfo( - // name: 'Polkadot', - // chainId: '91b171bb158e2d3848fa23a9f1c25182', - // chainIcon: 'https://cryptologos.cc/logos/polkadot-new-dot-logo.png', - // currency: 'DOT', - // rpcUrl: 'https://rpc.polkadot.io', - // explorerUrl: 'https://polkadot.subscan.io', - // ), - // ReownAppKitModalNetworkInfo( - // name: 'Westend', - // chainId: 'e143f23803ac50e8f6f8e62695d1ce9e', - // currency: 'DOT', - // rpcUrl: 'https://westend-rpc.polkadot.io', - // explorerUrl: 'https://westend.subscan.io', - // isTestNetwork: true, - // ), - // ]); + if (siweAuthValue) { + // Remove Solana support + ReownAppKitModalNetworks.removeSupportedNetworks('solana'); + } else { + // Add custom chains + ReownAppKitModalNetworks.addSupportedNetworks('polkadot', [ + ReownAppKitModalNetworkInfo( + name: 'Polkadot', + chainId: '91b171bb158e2d3848fa23a9f1c25182', + chainIcon: 'https://cryptologos.cc/logos/polkadot-new-dot-logo.png', + currency: 'DOT', + rpcUrl: 'https://rpc.polkadot.io', + explorerUrl: 'https://polkadot.subscan.io', + ), + ReownAppKitModalNetworkInfo( + name: 'Westend', + chainId: 'e143f23803ac50e8f6f8e62695d1ce9e', + currency: 'DOT', + rpcUrl: 'https://westend-rpc.polkadot.io', + explorerUrl: 'https://westend.subscan.io', + isTestNetwork: true, + ), + ]); + } try { _appKitModal = ReownAppKitModal( @@ -272,33 +275,38 @@ class _MyHomePageState extends State { // // You could place here your own getBalance method // return 0.123; // }, - // optionalNamespaces: { - // 'eip155': RequiredNamespace.fromJson({ - // 'chains': ReownAppKitModalNetworks.getAllSupportedNetworks( - // namespace: 'eip155', - // ).map((chain) => 'eip155:${chain.chainId}').toList(), - // 'methods': NetworkUtils.defaultNetworkMethods['eip155']!.toList(), - // 'events': NetworkUtils.defaultNetworkEvents['eip155']!.toList(), - // }), - // 'solana': RequiredNamespace.fromJson({ - // 'chains': ReownAppKitModalNetworks.getAllSupportedNetworks( - // namespace: 'solana', - // ).map((chain) => 'solana:${chain.chainId}').toList(), - // 'methods': NetworkUtils.defaultNetworkMethods['solana']!.toList(), - // 'events': [], - // }), - // 'polkadot': RequiredNamespace.fromJson({ - // 'chains': [ - // 'polkadot:91b171bb158e2d3848fa23a9f1c25182', - // 'polkadot:e143f23803ac50e8f6f8e62695d1ce9e' - // ], - // 'methods': [ - // 'polkadot_signMessage', - // 'polkadot_signTransaction', - // ], - // 'events': [] - // }), - // }, + optionalNamespaces: siweAuthValue + ? null + : { + 'eip155': RequiredNamespace.fromJson({ + 'chains': ReownAppKitModalNetworks.getAllSupportedNetworks( + namespace: 'eip155', + ).map((chain) => 'eip155:${chain.chainId}').toList(), + 'methods': + NetworkUtils.defaultNetworkMethods['eip155']!.toList(), + 'events': + NetworkUtils.defaultNetworkEvents['eip155']!.toList(), + }), + 'solana': RequiredNamespace.fromJson({ + 'chains': ReownAppKitModalNetworks.getAllSupportedNetworks( + namespace: 'solana', + ).map((chain) => 'solana:${chain.chainId}').toList(), + 'methods': + NetworkUtils.defaultNetworkMethods['solana']!.toList(), + 'events': [], + }), + 'polkadot': RequiredNamespace.fromJson({ + 'chains': [ + 'polkadot:91b171bb158e2d3848fa23a9f1c25182', + 'polkadot:e143f23803ac50e8f6f8e62695d1ce9e' + ], + 'methods': [ + 'polkadot_signMessage', + 'polkadot_signTransaction', + ], + 'events': [] + }), + }, ); overlay = OverlayController( const Duration(milliseconds: 200), diff --git a/packages/reown_appkit/example/modal/lib/services/methods_service.dart b/packages/reown_appkit/example/modal/lib/services/methods_service.dart index 5728772..2dab1fe 100644 --- a/packages/reown_appkit/example/modal/lib/services/methods_service.dart +++ b/packages/reown_appkit/example/modal/lib/services/methods_service.dart @@ -75,12 +75,13 @@ class MethodsService { static Future callMethod({ required ReownAppKitModal appKitModal, required String topic, - required SupportedMethods method, + required String method, required String chainId, required String address, }) { // final cid = int.parse(chainId); - switch (method) { + final supportedMethod = MethodsService.methodFromName(method); + switch (supportedMethod) { case SupportedMethods.requestAccounts: return requestAccounts( appKitModal: appKitModal, @@ -251,13 +252,13 @@ class MethodsService { static Future ethSendOrSignTransaction({ required ReownAppKitModal appKitModal, required Transaction transaction, - required SupportedMethods method, + required String method, }) async { return await appKitModal.request( topic: appKitModal.session!.topic, chainId: appKitModal.selectedChain!.chainId, request: SessionRequestParams( - method: method.name, + method: method, params: [ transaction.toJson(), ], diff --git a/packages/reown_appkit/example/modal/lib/widgets/session_widget.dart b/packages/reown_appkit/example/modal/lib/widgets/session_widget.dart index 7c25c73..c1b5a4d 100644 --- a/packages/reown_appkit/example/modal/lib/widgets/session_widget.dart +++ b/packages/reown_appkit/example/modal/lib/widgets/session_widget.dart @@ -269,7 +269,7 @@ class SessionWidgetState extends State { final usableMethods = SupportedMethods.values.map((e) => e.name).toList(); // final List children = []; - for (final method in (approvedMethods ?? [])) { + for (final method in (approvedMethods ?? [])) { final implemented = usableMethods.contains(method); children.add( Container( @@ -280,10 +280,7 @@ class SessionWidgetState extends State { onPressed: implemented ? () async { widget.appKit.launchConnectedWallet(); - final future = - callChainMethod(MethodsService.methodFromName( - method, - )); + final future = callChainMethod(method); MethodDialog.show(context, method, future); } : null, @@ -478,7 +475,7 @@ class SessionWidgetState extends State { ); } - Future callChainMethod(SupportedMethods method) { + Future callChainMethod(String method) { final session = widget.appKit.session!; final chainId = widget.appKit.selectedChain!.chainId; final namespace = ReownAppKitModalNetworks.getNamespaceForChainId(chainId); diff --git a/packages/reown_appkit/pubspec.yaml b/packages/reown_appkit/pubspec.yaml index 0bd66ae..4f92420 100644 --- a/packages/reown_appkit/pubspec.yaml +++ b/packages/reown_appkit/pubspec.yaml @@ -26,10 +26,10 @@ dependencies: json_annotation: ^4.8.1 plugin_platform_interface: ^2.1.8 qr_flutter_wc: ^0.0.3 - reown_core: ^1.0.3 + reown_core: ^1.0.4 # reown_core: # path: ../reown_core/ - reown_sign: ^1.0.3 + reown_sign: ^1.0.4 # reown_sign: # path: ../reown_sign/ shimmer: ^3.0.0 diff --git a/packages/reown_appkit/test/shared/shared_test_utils.mocks.dart b/packages/reown_appkit/test/shared/shared_test_utils.mocks.dart index a9cff43..fe4428e 100644 --- a/packages/reown_appkit/test/shared/shared_test_utils.mocks.dart +++ b/packages/reown_appkit/test/shared/shared_test_utils.mocks.dart @@ -1483,15 +1483,14 @@ class MockReownCore extends _i1.Mock implements _i28.ReownCore { ) as _i23.Future); @override - _i23.Future addLinkModeSupportedApp(String? universalLink) => + _i23.Future addLinkModeSupportedApp(String? universalLink) => (super.noSuchMethod( Invocation.method( #addLinkModeSupportedApp, [universalLink], ), - returnValue: _i23.Future.value(), - returnValueForMissingStub: _i23.Future.value(), - ) as _i23.Future); + returnValue: _i23.Future.value(false), + ) as _i23.Future); @override List getLinkModeSupportedApps() => (super.noSuchMethod( diff --git a/packages/reown_sign/test/shared/shared_test_utils.mocks.dart b/packages/reown_sign/test/shared/shared_test_utils.mocks.dart index 300e3f7..234edd8 100644 --- a/packages/reown_sign/test/shared/shared_test_utils.mocks.dart +++ b/packages/reown_sign/test/shared/shared_test_utils.mocks.dart @@ -1483,15 +1483,14 @@ class MockReownCore extends _i1.Mock implements _i28.ReownCore { ) as _i23.Future); @override - _i23.Future addLinkModeSupportedApp(String? universalLink) => + _i23.Future addLinkModeSupportedApp(String? universalLink) => (super.noSuchMethod( Invocation.method( #addLinkModeSupportedApp, [universalLink], ), - returnValue: _i23.Future.value(), - returnValueForMissingStub: _i23.Future.value(), - ) as _i23.Future); + returnValue: _i23.Future.value(false), + ) as _i23.Future); @override List getLinkModeSupportedApps() => (super.noSuchMethod( From 91467b7699efa0980e45e9da17bf53baec54fadf Mon Sep 17 00:00:00 2001 From: Alfreedom <00tango.bromine@icloud.com> Date: Thu, 31 Oct 2024 16:07:19 +0100 Subject: [PATCH 13/17] version update --- packages/reown_appkit/CHANGELOG.md | 3 ++- packages/reown_appkit/lib/version.dart | 2 +- packages/reown_appkit/pubspec.yaml | 8 ++++---- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/packages/reown_appkit/CHANGELOG.md b/packages/reown_appkit/CHANGELOG.md index 80d0d80..2129816 100644 --- a/packages/reown_appkit/CHANGELOG.md +++ b/packages/reown_appkit/CHANGELOG.md @@ -1,6 +1,7 @@ -## 1.2.0-alpha01 +## 1.2.0-beta04 - Non-EVM Chains support +- Social Logins ## 1.0.1 diff --git a/packages/reown_appkit/lib/version.dart b/packages/reown_appkit/lib/version.dart index 21005a6..d53993e 100644 --- a/packages/reown_appkit/lib/version.dart +++ b/packages/reown_appkit/lib/version.dart @@ -1,2 +1,2 @@ // Generated code. Do not modify. -const packageVersion = '1.2.0-alpha01'; +const packageVersion = '1.2.0-beta04'; diff --git a/packages/reown_appkit/pubspec.yaml b/packages/reown_appkit/pubspec.yaml index 4f92420..a65681e 100644 --- a/packages/reown_appkit/pubspec.yaml +++ b/packages/reown_appkit/pubspec.yaml @@ -1,6 +1,6 @@ name: reown_appkit description: "Reown is the onchain UX platform that provides toolkits built on top of the WalletConnect Network" -version: 1.2.0-alpha01 +version: 1.2.0-beta04 homepage: https://github.com/reown-com/reown_flutter repository: https://github.com/reown-com/reown_flutter/tree/master/packages/reown_appkit documentation: https://docs.reown.com/appkit/flutter/core/installation @@ -50,9 +50,9 @@ dev_dependencies: logger: ^2.2.0 mockito: ^5.4.3 package_info_plus: ^8.0.2 - # reown_walletkit: ^1.0.3 - reown_walletkit: - path: ../reown_walletkit/ + reown_walletkit: ^1.0.3 + # reown_walletkit: + # path: ../reown_walletkit/ # For information on the generic Dart part of this file, see the # following page: https://dart.dev/tools/pub/pubspec From 7a3da9fa48b266850a9deb7d0f53b7cddc1980f4 Mon Sep 17 00:00:00 2001 From: Alfreedom <00tango.bromine@icloud.com> Date: Thu, 31 Oct 2024 18:30:54 +0100 Subject: [PATCH 14/17] minor fix --- .../lib/modal/appkit_modal_impl.dart | 21 +++++++++++-------- .../services/magic_service/magic_service.dart | 2 +- packages/reown_walletkit/pubspec.yaml | 4 ++-- .../test/shared/shared_test_utils.mocks.dart | 7 +++---- 4 files changed, 18 insertions(+), 16 deletions(-) diff --git a/packages/reown_appkit/lib/modal/appkit_modal_impl.dart b/packages/reown_appkit/lib/modal/appkit_modal_impl.dart index e456e56..882e278 100644 --- a/packages/reown_appkit/lib/modal/appkit_modal_impl.dart +++ b/packages/reown_appkit/lib/modal/appkit_modal_impl.dart @@ -419,6 +419,9 @@ class ReownAppKitModal with ChangeNotifier implements IReownAppKitModal { try { if (_storage.has(StorageConstants.modalSession)) { final storedSession = _storage.get(StorageConstants.modalSession); + _appKit.core.logger.i( + '[$runtimeType] _getStoredSession, storedSession: $storedSession, key: ${StorageConstants.modalSession}', + ); if (storedSession != null) { return ReownAppKitModalSession.fromMap(storedSession); } @@ -1053,16 +1056,19 @@ class ReownAppKitModal with ChangeNotifier implements IReownAppKitModal { if (_currentSession?.sessionService.isCoinbase == true) { try { await _coinbaseService.resetSession(); - } catch (_) { + } catch (e) { + _appKit.core.logger.d('[$runtimeType] disconnect coinbase $e'); _status = ReownAppKitModalStatus.initialized; _notify(); return; } } if (_currentSession?.sessionService.isMagic == true) { - await Future.delayed(Duration(milliseconds: 300)); - final disconnected = await _magicService.disconnect(); - if (!disconnected) { + try { + await Future.delayed(Duration(milliseconds: 300)); + await _magicService.disconnect(); + } catch (e) { + _appKit.core.logger.d('[$runtimeType] disconnect magic $e'); _status = ReownAppKitModalStatus.initialized; _notify(); return; @@ -1612,11 +1618,8 @@ class ReownAppKitModal with ChangeNotifier implements IReownAppKitModal { } Future _deleteStorage() async { - for (var key in _storage.keys) { - if (key.startsWith(StorageConstants.prefix)) { - await _storage.delete(key); - } - } + await _storage.delete(StorageConstants.selectedChainId); + await _storage.delete(StorageConstants.modalSession); } Future _cleanSession({SessionDelete? args, bool event = true}) async { diff --git a/packages/reown_appkit/lib/modal/services/magic_service/magic_service.dart b/packages/reown_appkit/lib/modal/services/magic_service/magic_service.dart index 6828b1c..8bbd92a 100644 --- a/packages/reown_appkit/lib/modal/services/magic_service/magic_service.dart +++ b/packages/reown_appkit/lib/modal/services/magic_service/magic_service.dart @@ -509,7 +509,7 @@ class MagicService implements IMagicService { if (!isConnected.value) { _resetTimeOut(); _disconnectCompleter.complete(true); - return true; + return await _disconnectCompleter.future; } final message = SignOut().toString(); await _webViewController.runJavaScript('sendMessage($message)'); diff --git a/packages/reown_walletkit/pubspec.yaml b/packages/reown_walletkit/pubspec.yaml index 60d46b7..2bafe84 100644 --- a/packages/reown_walletkit/pubspec.yaml +++ b/packages/reown_walletkit/pubspec.yaml @@ -12,10 +12,10 @@ dependencies: event: ^2.1.2 flutter: sdk: flutter - reown_core: ^1.0.3 + reown_core: ^1.0.4 # reown_core: # path: ../reown_core/ - reown_sign: ^1.0.3 + reown_sign: ^1.0.4 # reown_sign: # path: ../reown_sign/ diff --git a/packages/reown_walletkit/test/shared/shared_test_utils.mocks.dart b/packages/reown_walletkit/test/shared/shared_test_utils.mocks.dart index e36a4d0..7f5c83e 100644 --- a/packages/reown_walletkit/test/shared/shared_test_utils.mocks.dart +++ b/packages/reown_walletkit/test/shared/shared_test_utils.mocks.dart @@ -1483,15 +1483,14 @@ class MockReownCore extends _i1.Mock implements _i28.ReownCore { ) as _i23.Future); @override - _i23.Future addLinkModeSupportedApp(String? universalLink) => + _i23.Future addLinkModeSupportedApp(String? universalLink) => (super.noSuchMethod( Invocation.method( #addLinkModeSupportedApp, [universalLink], ), - returnValue: _i23.Future.value(), - returnValueForMissingStub: _i23.Future.value(), - ) as _i23.Future); + returnValue: _i23.Future.value(false), + ) as _i23.Future); @override List getLinkModeSupportedApps() => (super.noSuchMethod( From 0fb963dda62696fc977004cd2cc6f6144bb51f9b Mon Sep 17 00:00:00 2001 From: Alfreedom <00tango.bromine@icloud.com> Date: Fri, 1 Nov 2024 15:56:33 +0100 Subject: [PATCH 15/17] Minor change --- .../release_dapp_android_internal.yml | 5 ++ .../release_wallet_android_internal.yml | 5 ++ .../public/appkit_modal_default_networks.dart | 52 +++++++++++++++---- 3 files changed, 53 insertions(+), 9 deletions(-) diff --git a/.github/workflows/release_dapp_android_internal.yml b/.github/workflows/release_dapp_android_internal.yml index e3ade37..a3d80d0 100644 --- a/.github/workflows/release_dapp_android_internal.yml +++ b/.github/workflows/release_dapp_android_internal.yml @@ -6,6 +6,11 @@ on: types: - closed +# inputs: +# working-directory +# flavor +# BUNDLE_ID + jobs: build: if: (github.event.pull_request.merged == true && github.event.pull_request.base.ref == 'develop') || github.event_name == 'workflow_dispatch' diff --git a/.github/workflows/release_wallet_android_internal.yml b/.github/workflows/release_wallet_android_internal.yml index 19e803d..95b91b6 100644 --- a/.github/workflows/release_wallet_android_internal.yml +++ b/.github/workflows/release_wallet_android_internal.yml @@ -6,6 +6,11 @@ on: types: - closed +# inputs: +# working-directory +# flavor +# BUNDLE_ID + jobs: build: if: (github.event.pull_request.merged == true && github.event.pull_request.base.ref == 'develop') || github.event_name == 'workflow_dispatch' diff --git a/packages/reown_appkit/lib/modal/utils/public/appkit_modal_default_networks.dart b/packages/reown_appkit/lib/modal/utils/public/appkit_modal_default_networks.dart index 4c42f28..7bc4871 100644 --- a/packages/reown_appkit/lib/modal/utils/public/appkit_modal_default_networks.dart +++ b/packages/reown_appkit/lib/modal/utils/public/appkit_modal_default_networks.dart @@ -1,6 +1,5 @@ import 'package:collection/collection.dart'; -import 'package:reown_appkit/modal/models/public/appkit_network_info.dart'; -import 'package:reown_appkit/modal/utils/public/appkit_modal_networks_utils.dart'; +import 'package:reown_appkit/reown_appkit.dart'; class ReownAppKitModalNetworks { // https://github.com/WalletConnect/blockchain-api/blob/master/SUPPORTED_CHAINS.md @@ -219,6 +218,18 @@ class ReownAppKitModalNetworks { String namespace, String chainId, ) { + if (namespace.isEmpty) { + throw ReownAppKitModalException('`namespace` can not be empty'); + } + if (chainId.isEmpty) { + throw ReownAppKitModalException('`chainId` can not be empty'); + } + if (chainId.contains(':')) { + return getAllSupportedNetworks(namespace: chainId.split(':').first) + .firstWhere( + (e) => e.chainId == chainId.split(':').last, + ); + } return getAllSupportedNetworks(namespace: namespace).firstWhere( (e) => e.chainId == chainId, ); @@ -229,6 +240,9 @@ class ReownAppKitModalNetworks { List chainIds = const [], bool includeTestnets = true, }) { + if (namespace.isEmpty) { + throw ReownAppKitModalException('`namespace` can not be empty'); + } _mainnets[namespace]?.removeWhere((chain) { if (chainIds.isEmpty || chainIds.contains(chain.chainId)) { return true; @@ -255,6 +269,10 @@ class ReownAppKitModalNetworks { String namespace, List chains, ) { + if (namespace.isEmpty) { + throw ReownAppKitModalException('`namespace` can not be empty'); + } + final List mainnets = [ ...List.from(_mainnets[namespace] ?? []), ...(chains.where((e) => e.isTestNetwork == false)), @@ -279,22 +297,31 @@ class ReownAppKitModalNetworks { static List getAllSupportedNetworks({ String? namespace, }) { - final allMainnets = namespace != null + final allMainnets = (namespace ?? '').isNotEmpty ? (_mainnets[namespace] ?? []) : _mainnets.values.expand((e) => e); final mainnets = allMainnets.where((e) { return !e.isTestNetwork; }).toList(); - final allTestnets = namespace != null + // + final allTestnets = (namespace ?? '').isNotEmpty ? (_testnets[namespace] ?? []) : _testnets.values.expand((e) => e); final testnets = allTestnets.where((e) { return e.isTestNetwork; }).toList(); + // return [...mainnets, ...testnets].toList(); } static String getNamespaceForChainId(String chainId) { + if (chainId.isEmpty) { + throw ReownAppKitModalException('`chainId` can not be empty'); + } + if (NamespaceUtils.isValidChainId(chainId)) { + return chainId.split(':').first; + } + String? namespace; final namespaces = getAllSupportedNamespaces(); for (var ns in namespaces) { @@ -310,15 +337,22 @@ class ReownAppKitModalNetworks { } static String getNetworkIconId(String chainId) { - final namespace = getNamespaceForChainId(chainId); - final network = getNetworkById(namespace, chainId); - if ((network?.chainIcon ?? '').isNotEmpty) { - return network!.chainIcon!; + try { + final namespace = getNamespaceForChainId(chainId); + final network = getNetworkById(namespace, chainId); + if ((network?.chainIcon ?? '').isNotEmpty) { + return network!.chainIcon!; + } + return _networkIcons[chainId]!; + } catch (e) { + return ''; } - return _networkIcons[chainId] ?? ''; } static String getCaip2Chain(String chainId) { + if (NamespaceUtils.isValidChainId(chainId)) { + return chainId; + } final namespace = getNamespaceForChainId(chainId); return '$namespace:$chainId'; } From d9326413c16e5d6b6bdd8bdf507bd22125a8920b Mon Sep 17 00:00:00 2001 From: Alfreedom <00tango.bromine@icloud.com> Date: Fri, 1 Nov 2024 16:53:00 +0100 Subject: [PATCH 16/17] fix coinbase --- .../example/base/ios/Runner/AppDelegate.swift | 18 ++++++++++++++++++ .../reown_appkit/example/base/lib/main.dart | 8 ++++---- .../base/lib/utils/deep_link_handler.dart | 19 +++++++++---------- .../lib/modal/appkit_modal_impl.dart | 5 ++++- .../coinbase_service/coinbase_service.dart | 2 +- .../public/appkit_modal_account_button.dart | 7 ++++--- 6 files changed, 40 insertions(+), 19 deletions(-) diff --git a/packages/reown_appkit/example/base/ios/Runner/AppDelegate.swift b/packages/reown_appkit/example/base/ios/Runner/AppDelegate.swift index 805d27d..53e1dfa 100644 --- a/packages/reown_appkit/example/base/ios/Runner/AppDelegate.swift +++ b/packages/reown_appkit/example/base/ios/Runner/AppDelegate.swift @@ -1,5 +1,6 @@ import UIKit import Flutter +import CoinbaseWalletSDK @UIApplicationMain @objc class AppDelegate: FlutterAppDelegate { @@ -48,14 +49,31 @@ import Flutter } override func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool { + if #available(iOS 13.0, *) { + if (CoinbaseWalletSDK.isConfigured == true) { + if (try? CoinbaseWalletSDK.shared.handleResponse(url)) == true { + return true + } + } + } + return linkStreamHandler.handleLink(url.absoluteString) } override func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool { + if #available(iOS 13.0, *) { + if (CoinbaseWalletSDK.isConfigured == true) { + if let url = userActivity.webpageURL, (try? CoinbaseWalletSDK.shared.handleResponse(url)) == true { + return true + } + } + } + if userActivity.activityType == NSUserActivityTypeBrowsingWeb { handleIncomingUniversalLink(userActivity: userActivity) return true } + return false } diff --git a/packages/reown_appkit/example/base/lib/main.dart b/packages/reown_appkit/example/base/lib/main.dart index d1a37c7..fe38282 100644 --- a/packages/reown_appkit/example/base/lib/main.dart +++ b/packages/reown_appkit/example/base/lib/main.dart @@ -190,7 +190,7 @@ class _MyHomePageState extends State { _appKitModal = ReownAppKitModal( context: context, appKit: _appKit, - siweConfig: linkMode ? _siweConfig() : null, + siweConfig: _siweConfig(linkMode), enableAnalytics: true, featuresConfig: FeaturesConfig( email: true, @@ -307,7 +307,7 @@ class _MyHomePageState extends State { await _appKitModal!.init(); await _registerEventHandlers(); - DeepLinkHandler.init(_appKit!); + DeepLinkHandler.init(_appKitModal!); DeepLinkHandler.checkInitialLink(); final allChains = ReownAppKitModalNetworks.getAllSupportedNetworks(); @@ -522,7 +522,7 @@ class _MyHomePageState extends State { } } - SIWEConfig _siweConfig() => SIWEConfig( + SIWEConfig _siweConfig(bool enabled) => SIWEConfig( getNonce: () async { // this has to be called at the very moment of creating the pairing uri return SIWEUtils.generateNonce(); @@ -585,7 +585,7 @@ class _MyHomePageState extends State { // Called when disconnecting WalletConnect session was successfull debugPrint('[SIWEConfig] onSignOut()'); }, - enabled: true, + enabled: enabled, signOutOnDisconnect: true, signOutOnAccountChange: true, signOutOnNetworkChange: false, diff --git a/packages/reown_appkit/example/base/lib/utils/deep_link_handler.dart b/packages/reown_appkit/example/base/lib/utils/deep_link_handler.dart index f150e39..10eedaa 100644 --- a/packages/reown_appkit/example/base/lib/utils/deep_link_handler.dart +++ b/packages/reown_appkit/example/base/lib/utils/deep_link_handler.dart @@ -1,6 +1,6 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; -import 'package:reown_appkit/reown_appkit.dart'; +import 'package:reown_appkit/modal/i_appkit_modal_impl.dart'; class DeepLinkHandler { static const _methodChannel = MethodChannel( @@ -10,7 +10,7 @@ class DeepLinkHandler { 'com.walletconnect.flutterdapp/events', ); static final waiting = ValueNotifier(false); - static late IReownAppKit _appKit; + static late IReownAppKitModal _appKitModal; static void initListener() { if (kIsWeb) return; @@ -24,9 +24,9 @@ class DeepLinkHandler { } } - static void init(IReownAppKit appKit) { + static void init(IReownAppKitModal appKitModal) { if (kIsWeb) return; - _appKit = appKit; + _appKitModal = appKitModal; } static void checkInitialLink() async { @@ -39,18 +39,17 @@ class DeepLinkHandler { } static Uri get nativeUri => - Uri.parse(_appKit.metadata.redirect?.native ?? ''); + Uri.parse(_appKitModal.appKit!.metadata.redirect?.native ?? ''); static Uri get universalUri => - Uri.parse(_appKit.metadata.redirect?.universal ?? ''); + Uri.parse(_appKitModal.appKit!.metadata.redirect?.universal ?? ''); static String get host => universalUri.host; static void _onLink(dynamic link) async { debugPrint('[SampleDapp] _onLink $link'); if (link == null) return; - try { - return await _appKit.dispatchEnvelope(link); - } catch (e) { - debugPrint('[SampleDapp] _onLink error $e'); + final handled = await _appKitModal.dispatchEnvelope(link); + if (!handled) { + debugPrint('[SampleDapp] _onLink not handled by AppKit'); } } diff --git a/packages/reown_appkit/lib/modal/appkit_modal_impl.dart b/packages/reown_appkit/lib/modal/appkit_modal_impl.dart index 882e278..2d36bbb 100644 --- a/packages/reown_appkit/lib/modal/appkit_modal_impl.dart +++ b/packages/reown_appkit/lib/modal/appkit_modal_impl.dart @@ -251,6 +251,7 @@ class ReownAppKitModal with ChangeNotifier implements IReownAppKitModal { @override Future dispatchEnvelope(String url) async { + _appKit.core.logger.d('[$runtimeType] dispatchEnvelope $url'); final envelope = ReownCoreUtils.getSearchParamFromURL(url, 'wc_ev'); if (envelope.isNotEmpty) { await _appKit.dispatchEnvelope(url); @@ -2042,7 +2043,9 @@ extension _AppKitModalExtension on ReownAppKitModal { } void _oneSIWEFinish(ReownAppKitModalSession updatedSession) async { - _appKit.core.logger.d('[$runtimeType] _oneSIWEFinish $updatedSession'); + _appKit.core.logger.d( + '[$runtimeType] _oneSIWEFinish ${updatedSession.toJson()}', + ); await _storeSession(updatedSession); try { await _storage.set( diff --git a/packages/reown_appkit/lib/modal/services/coinbase_service/coinbase_service.dart b/packages/reown_appkit/lib/modal/services/coinbase_service/coinbase_service.dart index c091ba1..286bbe1 100644 --- a/packages/reown_appkit/lib/modal/services/coinbase_service/coinbase_service.dart +++ b/packages/reown_appkit/lib/modal/services/coinbase_service/coinbase_service.dart @@ -108,7 +108,7 @@ class CoinbaseService implements ICoinbaseService { final redirect = _metadata.redirect; final callback = redirect?.universal ?? redirect?.native ?? ''; _core.logger.i( - '[$runtimeType] init with $walletLink, $redirect, $callback', + '[$runtimeType] init with host: ${Uri.parse(walletLink)}, callback: ${Uri.parse(callback)}', ); if (callback.isNotEmpty || walletLink.isNotEmpty) { try { diff --git a/packages/reown_appkit/lib/modal/widgets/public/appkit_modal_account_button.dart b/packages/reown_appkit/lib/modal/widgets/public/appkit_modal_account_button.dart index eb79629..4fc83d3 100644 --- a/packages/reown_appkit/lib/modal/widgets/public/appkit_modal_account_button.dart +++ b/packages/reown_appkit/lib/modal/widgets/public/appkit_modal_account_button.dart @@ -61,13 +61,14 @@ class _AppKitModalAccountButtonState extends State { } void _modalNotifyListener() { - setState(() { - final chainId = widget.appKitModal.selectedChain?.chainId ?? ''; + final chainId = widget.appKitModal.selectedChain?.chainId ?? ''; + if (chainId.isNotEmpty) { final namespace = ReownAppKitModalNetworks.getNamespaceForChainId( chainId, ); _address = widget.appKitModal.session?.getAddress(namespace) ?? ''; - }); + } + setState(() {}); } void _onTap() { From 481a1d58443f0829f8c46f46ea7dc5cad7c3f893 Mon Sep 17 00:00:00 2001 From: Alfreedom <00tango.bromine@icloud.com> Date: Tue, 5 Nov 2024 09:28:09 +0100 Subject: [PATCH 17/17] version update --- packages/reown_appkit/CHANGELOG.md | 2 +- packages/reown_appkit/lib/version.dart | 2 +- packages/reown_appkit/pubspec.yaml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/reown_appkit/CHANGELOG.md b/packages/reown_appkit/CHANGELOG.md index 2129816..d87c71e 100644 --- a/packages/reown_appkit/CHANGELOG.md +++ b/packages/reown_appkit/CHANGELOG.md @@ -1,4 +1,4 @@ -## 1.2.0-beta04 +## 1.2.0-beta01 - Non-EVM Chains support - Social Logins diff --git a/packages/reown_appkit/lib/version.dart b/packages/reown_appkit/lib/version.dart index d53993e..1ee4245 100644 --- a/packages/reown_appkit/lib/version.dart +++ b/packages/reown_appkit/lib/version.dart @@ -1,2 +1,2 @@ // Generated code. Do not modify. -const packageVersion = '1.2.0-beta04'; +const packageVersion = '1.2.0-beta01'; diff --git a/packages/reown_appkit/pubspec.yaml b/packages/reown_appkit/pubspec.yaml index a65681e..bd7ec5d 100644 --- a/packages/reown_appkit/pubspec.yaml +++ b/packages/reown_appkit/pubspec.yaml @@ -1,6 +1,6 @@ name: reown_appkit description: "Reown is the onchain UX platform that provides toolkits built on top of the WalletConnect Network" -version: 1.2.0-beta04 +version: 1.2.0-beta01 homepage: https://github.com/reown-com/reown_flutter repository: https://github.com/reown-com/reown_flutter/tree/master/packages/reown_appkit documentation: https://docs.reown.com/appkit/flutter/core/installation