diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index dd84ea78..a24ee211 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -17,22 +17,19 @@ Steps to reproduce the behavior: 3. Scroll down to '....' 4. See error -**Expected behavior** -A clear and concise description of what you expected to happen. +**Meaningful logs** +Any error or information log that would help identify the issue + +**Reproducible code** +Minimum reproducible code of the issue **Screenshots** If applicable, add screenshots to help explain your problem. -**Desktop (please complete the following information):** - - OS: [e.g. iOS] - - Browser [e.g. chrome, safari] - - Version [e.g. 22] - **Smartphone (please complete the following information):** - Device: [e.g. iPhone6] - OS: [e.g. iOS8.1] - - Browser [e.g. stock browser, safari] - - Version [e.g. 22] + - Web3Modal Version [e.g. 22] **Additional context** Add any other context about the problem here. diff --git a/CHANGELOG.md b/CHANGELOG.md index b3af0266..93fd1c9c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +## 3.3.0-beta01 + +- One-Click Auth + SIWE implementation +- Coinbase Wallet dependency update +- Bug fixes + ## 3.2.2 - Network change improvements diff --git a/example/android/gradle.properties b/example/android/gradle.properties index 9c491cd5..65662b88 100644 --- a/example/android/gradle.properties +++ b/example/android/gradle.properties @@ -1,5 +1,5 @@ org.gradle.jvmargs=-Xmx1536M android.useAndroidX=true android.enableJetifier=true -versionName=3.2.2 -versionCode=61 +versionName=3.3.0 +versionCode=62 diff --git a/example/ios/Runner.xcodeproj/project.pbxproj b/example/ios/Runner.xcodeproj/project.pbxproj index 71a9021d..e8822df8 100644 --- a/example/ios/Runner.xcodeproj/project.pbxproj +++ b/example/ios/Runner.xcodeproj/project.pbxproj @@ -470,7 +470,7 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 61; + CURRENT_PROJECT_VERSION = 62; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = W5R8AG9K22; ENABLE_BITCODE = NO; @@ -496,7 +496,7 @@ buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 61; + CURRENT_PROJECT_VERSION = 62; GENERATE_INFOPLIST_FILE = YES; MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.web3modal.flutterExample.RunnerTests; @@ -514,7 +514,7 @@ buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 61; + CURRENT_PROJECT_VERSION = 62; GENERATE_INFOPLIST_FILE = YES; MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.web3modal.flutterExample.RunnerTests; @@ -530,7 +530,7 @@ buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 61; + CURRENT_PROJECT_VERSION = 62; GENERATE_INFOPLIST_FILE = YES; MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.web3modal.flutterExample.RunnerTests; @@ -655,7 +655,7 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 61; + CURRENT_PROJECT_VERSION = 62; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = W5R8AG9K22; ENABLE_BITCODE = NO; @@ -686,7 +686,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 61; + CURRENT_PROJECT_VERSION = 62; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = W5R8AG9K22; ENABLE_BITCODE = NO; diff --git a/example/ios/Runner/Info.plist b/example/ios/Runner/Info.plist index a0ec3ffc..58fd63ff 100644 --- a/example/ios/Runner/Info.plist +++ b/example/ios/Runner/Info.plist @@ -19,7 +19,7 @@ CFBundlePackageType APPL CFBundleShortVersionString - 3.2.2 + 3.3.0 CFBundleSignature ???? CFBundleURLTypes @@ -36,7 +36,7 @@ CFBundleVersion - 61 + 62 ITSAppUsesNonExemptEncryption LSApplicationQueriesSchemes diff --git a/example/lib/home_page.dart b/example/lib/home_page.dart index f4e50e62..191d288d 100644 --- a/example/lib/home_page.dart +++ b/example/lib/home_page.dart @@ -1,14 +1,13 @@ import 'package:fl_toast/fl_toast.dart'; import 'package:flutter/material.dart'; -// ignore: unused_import -import 'package:web3modal_flutter/utils/util.dart'; import 'package:web3modal_flutter/web3modal_flutter.dart'; +import 'package:walletconnect_flutter_dapp/utils/constants.dart'; +import 'package:walletconnect_flutter_dapp/utils/crypto/siwe_service.dart'; import 'package:walletconnect_flutter_dapp/widgets/logger_widget.dart'; import 'package:walletconnect_flutter_dapp/widgets/session_widget.dart'; import 'package:walletconnect_flutter_dapp/utils/dart_defines.dart'; -import 'package:walletconnect_flutter_dapp/utils/string_constants.dart'; class MyHomePage extends StatefulWidget { const MyHomePage({ @@ -26,6 +25,7 @@ class MyHomePage extends StatefulWidget { class _MyHomePageState extends State { final overlay = OverlayController(const Duration(milliseconds: 200)); late W3MService _w3mService; + late SIWESampleWebService _siweTestService; @override void initState() { @@ -33,6 +33,7 @@ class _MyHomePageState extends State { WidgetsBinding.instance.addPostFrameCallback((_) { _toggleOverlay(); }); + _siweTestService = SIWESampleWebService(); _initializeService(); } @@ -40,19 +41,10 @@ class _MyHomePageState extends State { overlay.show(context); } - void _initializeService() async { - // See https://docs.walletconnect.com/web3modal/flutter/custom-chains - W3MChainPresets.chains.addAll(W3MChainPresets.extraChains); - W3MChainPresets.chains.addAll(W3MChainPresets.testChains); - // W3MChainPresets.chains.removeWhere((key, _) => key != '137'); - - _w3mService = W3MService( - projectId: DartDefines.projectId, - logLevel: LogLevel.error, - metadata: const PairingMetadata( + PairingMetadata get _pairingMetadata => const PairingMetadata( name: StringConstants.w3mPageTitleV3, description: StringConstants.w3mPageTitleV3, - url: 'https://walletconnect.com/appkit', + url: 'https://walletconnect.com/', icons: [ 'https://docs.walletconnect.com/assets/images/web3modalLogo-2cee77e07851ba0a710b56d03d4d09dd.png' ], @@ -60,43 +52,153 @@ class _MyHomePageState extends State { native: 'web3modalflutter://', universal: 'https://walletconnect.com/appkit', ), - ), + ); + + SIWEConfig get _siweConfig => SIWEConfig( + context: context, + getNonce: () async { + // this has to be called at the very moment of creating the pairing uri + try { + debugPrint('[SIWEConfig] getNonce()'); + final response = await _siweTestService.getNonce(); + return response['nonce'] as String; + } catch (error) { + debugPrint('[SIWEConfig] getNonce error: $error'); + // Fallback patch for testing purposes in case SIWE backend has issues + return AuthUtils.generateNonce(); + } + }, + getMessageParams: () async { + // Provide everything that is needed to construct the SIWE message + debugPrint('[SIWEConfig] getMessageParams()'); + final uri = Uri.parse(_pairingMetadata.redirect!.universal!); + return SIWEMessageArgs( + domain: uri.authority, + uri: 'https://walletconnect.com/login', + statement: 'Welcome to AppKit for Flutter.', + methods: MethodsConstants.allMethods, + ); + }, + createMessage: (SIWECreateMessageArgs args) { + // Create SIWE message to be signed. + // You can use our provided formatMessage() method of implement your own + debugPrint('[SIWEConfig] createMessage()'); + return _w3mService.formatMessage(args); + }, + verifyMessage: (SIWEVerifyMessageArgs args) async { + // Implement your verifyMessage to authenticate the user after it. + try { + debugPrint('[SIWEConfig] verifyMessage()'); + final payload = args.toJson(); + final uri = Uri.parse(_pairingMetadata.redirect!.universal!); + final result = await _siweTestService.authenticate( + payload, + domain: uri.authority, + ); + return result['token'] != null; + } catch (error) { + debugPrint('[SIWEConfig] verifyMessage error: $error'); + // Fallback patch for testing purposes in case SIWE backend has issues + final chainId = AuthSignature.getChainIdFromMessage(args.message); + final address = AuthSignature.getAddressFromMessage(args.message); + final cacaoSignature = args.cacao != null + ? args.cacao!.s + : CacaoSignature( + t: CacaoSignature.EIP191, + s: args.signature, + ); + return await AuthSignature.verifySignature( + address, + args.message, + cacaoSignature, + chainId, + DartDefines.projectId, + ); + } + }, + getSession: () async { + // Return proper session from your Web Service + try { + debugPrint('[SIWEConfig] getSession()'); + final session = await _siweTestService.getAppKitAuthSession(); + final address = session['address']!.toString(); + final chainId = session['chainId']!.toString(); + return SIWESession(address: address, chains: [chainId]); + } catch (error) { + debugPrint('[SIWEConfig] getSession error: $error'); + // Fallback patch for testing purposes in case SIWE backend has issues + final address = _w3mService.session!.address!; + final chainId = _w3mService.session!.chainId; + return SIWESession(address: address, chains: [chainId]); + } + }, + onSignIn: (SIWESession session) { + // Called after SIWE message is signed and verified + debugPrint('[SIWEConfig] onSignIn()'); + }, + signOut: () async { + // Called when user taps on disconnect button + try { + debugPrint('[SIWEConfig] signOut()'); + final _ = await _siweTestService.appKitAuthSignOut(); + return true; + } catch (error) { + debugPrint('[SIWEConfig] signOut error: $error'); + // Fallback patch for testing purposes in case SIWE backend has issues + return true; + } + }, + onSignOut: () { + // Called when disconnecting WalletConnect session was successfull + debugPrint('[SIWEConfig] onSignOut()'); + }, + // enabled: true, + // signOutOnDisconnect: true, + // signOutOnAccountChange: true, + // signOutOnNetworkChange: true, + // nonceRefetchIntervalMs: 300000, + // sessionRefetchIntervalMs: 300000, + ); + + void _initializeService() async { + // See https://docs.walletconnect.com/web3modal/flutter/custom-chains + W3MChainPresets.chains.addAll(W3MChainPresets.extraChains); + W3MChainPresets.chains.addAll(W3MChainPresets.testChains); + + _w3mService = W3MService( + projectId: DartDefines.projectId, + logLevel: LogLevel.error, + metadata: _pairingMetadata, + siweConfig: _siweConfig, enableAnalytics: true, // OPTIONAL - null by default enableEmail: true, // OPTIONAL - false by default // requiredNamespaces: {}, // optionalNamespaces: {}, + // includedWalletIds: {}, + featuredWalletIds: { + 'fd20dc426fb37566d803205b19bbc1d4096b248ac04548e3cfb6b3a38bd033aa', // Coinbase + '18450873727504ae9315a084fa7624b5297d2fe5880f0982979c17345a138277', // Kraken Wallet + 'c57ca95b47569778a828d19178114f4db188b89b763c899ba0be274e97267d96', // Metamask + '1ae92b26df02f0abca6304df07debccd18262fdf5fe82daa81593582dac9a369', // Rainbow + 'c03dfee351b6fcc421b4494ea33b9d4b92a984f87aa76d1663bb28705e95034a', // Uniswap + '38f5d18bd8522c244bdd70cb4a68e0e718865155811c043f052fb9f1c51de662', // Bitget + }, // excludedWalletIds: { - // 'c57ca95b47569778a828d19178114f4db188b89b763c899ba0be274e97267d96', // Metamask + // 'fd20dc426fb37566d803205b19bbc1d4096b248ac04548e3cfb6b3a38bd033aa', // Coinbase // }, // MORE WALLETS https://explorer.walletconnect.com/?type=wallet&chains=eip155%3A1 - // includedWalletIds: { - // 'c57ca95b47569778a828d19178114f4db188b89b763c899ba0be274e97267d96', // Metamask - // '1ae92b26df02f0abca6304df07debccd18262fdf5fe82daa81593582dac9a369', // Rainbow - // 'fd20dc426fb37566d803205b19bbc1d4096b248ac04548e3cfb6b3a38bd033aa', // Coinbase Wallet - // 'c03dfee351b6fcc421b4494ea33b9d4b92a984f87aa76d1663bb28705e95034a', // Uniswap - // '18450873727504ae9315a084fa7624b5297d2fe5880f0982979c17345a138277', // Kraken Wallet - // '38f5d18bd8522c244bdd70cb4a68e0e718865155811c043f052fb9f1c51de662', // Bitget - // '19177a98252e07ddfc9af2083ba8e07ef627cb6103467ffebb3f8f4205fd7927', // Ledger Live - // '4457c130df49fb3cb1f8b99574b97b35208bd3d0d13b8d25d2b5884ed2cad13a', // Shapeshift - // }, - // featuredWalletIds: { - // '18450873727504ae9315a084fa7624b5297d2fe5880f0982979c17345a138277', // Kraken Wallet - // '19177a98252e07ddfc9af2083ba8e07ef627cb6103467ffebb3f8f4205fd7927', // Ledger Live - // '4457c130df49fb3cb1f8b99574b97b35208bd3d0d13b8d25d2b5884ed2cad13a', // Shapeshift - // 'fd20dc426fb37566d803205b19bbc1d4096b248ac04548e3cfb6b3a38bd033aa', // Coinbase Wallet - // '38f5d18bd8522c244bdd70cb4a68e0e718865155811c043f052fb9f1c51de662', // Bitget - // }, ); - // + // modal specific subscriptions _w3mService.onModalConnect.subscribe(_onModalConnect); + _w3mService.onModalUpdate.subscribe(_onModalUpdate); _w3mService.onModalNetworkChange.subscribe(_onModalNetworkChange); _w3mService.onModalDisconnect.subscribe(_onModalDisconnect); _w3mService.onModalError.subscribe(_onModalError); - // + // session related subscriptions _w3mService.onSessionExpireEvent.subscribe(_onSessionExpired); _w3mService.onSessionUpdateEvent.subscribe(_onSessionUpdate); _w3mService.onSessionEventEvent.subscribe(_onSessionEvent); - // + // relayClient subscriptions _w3mService.web3App!.core.relayClient.onRelayClientConnect.subscribe( _onRelayClientConnect, ); @@ -125,6 +227,7 @@ class _MyHomePageState extends State { ); // _w3mService.onModalConnect.unsubscribe(_onModalConnect); + _w3mService.onModalUpdate.unsubscribe(_onModalUpdate); _w3mService.onModalNetworkChange.unsubscribe(_onModalNetworkChange); _w3mService.onModalDisconnect.unsubscribe(_onModalDisconnect); _w3mService.onModalError.unsubscribe(_onModalError); @@ -151,6 +254,10 @@ class _MyHomePageState extends State { } } + void _onModalUpdate(ModalConnect? event) { + setState(() {}); + } + void _switchToPolygonIfNeeded() { final polygon = W3MChainPresets.chains['137']!; // final approvedChains = _w3mService.getApprovedChains() ?? []; diff --git a/example/lib/main.dart b/example/lib/main.dart index d0f85200..6bb3ebae 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; import 'package:walletconnect_flutter_dapp/home_page.dart'; -import 'package:walletconnect_flutter_dapp/utils/string_constants.dart'; +import 'package:walletconnect_flutter_dapp/utils/constants.dart'; import 'package:web3modal_flutter/web3modal_flutter.dart'; void main() { diff --git a/example/lib/models/eth/ethereum_sign_message.dart b/example/lib/models/eth/ethereum_sign_message.dart deleted file mode 100644 index f5dda50c..00000000 --- a/example/lib/models/eth/ethereum_sign_message.dart +++ /dev/null @@ -1,19 +0,0 @@ -enum WCSignType { - message, - personalMessage, - typedMessageV2, - typedMessageV3, - typedMessageV4, -} - -class EthereumSignMessage { - final String data; - final String address; - final WCSignType type; - - const EthereumSignMessage({ - required this.data, - required this.address, - required this.type, - }); -} diff --git a/example/lib/models/eth/ethereum_transaction.dart b/example/lib/models/eth/ethereum_transaction.dart deleted file mode 100644 index f1cd650c..00000000 --- a/example/lib/models/eth/ethereum_transaction.dart +++ /dev/null @@ -1,69 +0,0 @@ -// import 'package:json_annotation/json_annotation.dart'; - -// part 'ethereum_transaction.g.dart'; - -// @JsonSerializable(includeIfNull: false) -class EthereumTransaction { - final String from; - final String to; - final String value; - final String? nonce; - final String? gasPrice; - final String? maxFeePerGas; - final String? maxPriorityFeePerGas; - final String? gas; - final String? gasLimit; - final String? data; - - EthereumTransaction({ - required this.from, - required this.to, - required this.value, - this.nonce, - this.gasPrice, - this.maxFeePerGas, - this.maxPriorityFeePerGas, - this.gas, - this.gasLimit, - this.data, - }); - - // factory EthereumTransaction.fromJson(Map json) { - // return EthereumTransaction(from: from, to: to, value: value,); - // } - - Map toJson() { - Map json = { - 'from': from, - 'to': to, - 'value': value, - }; - if (nonce != null) { - json['nonce'] = nonce; - } - if (gasPrice != null) { - json['gasPrice'] = gasPrice; - } - if (maxFeePerGas != null) { - json['maxFeePerGas'] = maxFeePerGas; - } - if (maxPriorityFeePerGas != null) { - json['maxPriorityFeePerGas'] = maxPriorityFeePerGas; - } - if (gas != null) { - json['gas'] = gas; - } - if (gasLimit != null) { - json['gasLimit'] = gasLimit; - } - if (data != null) { - json['data'] = data; - } - return json; - } - - @override - String toString() { - return 'WCEthereumTransaction(from: $from, to: $to, nonce: $nonce, gasPrice: $gasPrice, maxFeePerGas: $maxFeePerGas, maxPriorityFeePerGas: $maxPriorityFeePerGas, gas: $gas, gasLimit: $gasLimit, value: $value, data: $data)'; - } -} diff --git a/example/lib/utils/crypto/chain_data_wrapper.dart b/example/lib/utils/chain_data_wrapper.dart similarity index 76% rename from example/lib/utils/crypto/chain_data_wrapper.dart rename to example/lib/utils/chain_data_wrapper.dart index c6bb831f..03abd553 100644 --- a/example/lib/utils/crypto/chain_data_wrapper.dart +++ b/example/lib/utils/chain_data_wrapper.dart @@ -81,3 +81,32 @@ class ChainDataWrapper { // ), ]; } + +String getChainName(String chain) { + try { + return ChainDataWrapper.chains + .where((element) => element.w3mChainInfo.namespace == chain) + .first + .w3mChainInfo + .chainName; + } catch (e) { + debugPrint('[ExampleApp] getChainName, Invalid chain: $chain'); + } + return 'Unknown'; +} + +ChainMetadata getChainMetadataFromChain(String namespace) { + try { + return ChainDataWrapper.chains + .where((element) => element.w3mChainInfo.namespace == namespace) + .first; + } catch (_) { + return ChainMetadata( + color: Colors.blue, + type: ChainType.eip155, + w3mChainInfo: W3MChainPresets.chains.values.firstWhere( + (e) => e.namespace == namespace, + ), + ); + } +} diff --git a/example/lib/utils/constants.dart b/example/lib/utils/constants.dart index 0018d199..8f91e3e7 100644 --- a/example/lib/utils/constants.dart +++ b/example/lib/utils/constants.dart @@ -5,8 +5,6 @@ class Constants { static const String aud = 'https://walletconnect.org/login'; static const String domain = 'walletconnect.org'; - - static const String signPageTypeKey = 'signPageType'; } class StyleConstants { @@ -53,3 +51,46 @@ class StyleConstants { fontWeight: FontWeight.w600, ); } + +class StringConstants { + // General + static const String cancel = 'Cancel'; + static const String close = 'Close'; + static const String ok = 'OK'; + static const String delete = 'Delete'; + + // Main Page + static const String appTitle = 'WalletConnect v2 Flutter dApp Demo'; + static const String basicPageTitle = 'Basic'; + static const String wcmPageTitle = 'WalletConnect Modal'; + static const String w3mPageTitle = 'Web3Modal'; + static const String w3mPageTitleV3 = 'Web3Modal Flutter'; + static const String pairingsPageTitle = 'Pairings'; + static const String sessionsPageTitle = 'Sessions'; + static const String authPageTitle = 'Auth'; + static const String settingsPageTitle = 'Settings'; + static const String receivedPing = 'Received Ping'; + static const String receivedEvent = 'Received Event'; + + // Sign Page + static const String selectChains = 'Select chains:'; + static const String testnetsOnly = 'Testnets only?'; + static const String scanQrCode = 'Scan QR Code'; + static const String copiedToClipboard = 'Copied to clipboard'; + static const String bareBonesSign = 'Connect Bare Bones'; + static const String connectionEstablished = 'Session established'; + static const String connectionFailed = 'Session setup failed'; + static const String authSucceeded = 'Authentication Successful'; + static const String authFailed = 'Authentication Failed'; + + // Pairings Page + static const String pairings = 'Pairings'; + static const String deletePairing = 'Delete Pairing?'; + + // Sessions Page + static const String sessions = 'Sessions'; + static const String noSessionSelected = 'No session selected'; + static const String sessionTopic = 'Session Topic: '; + static const String methods = 'Methods'; + static const String events = 'Events'; +} diff --git a/example/lib/utils/crypto/eip155.dart b/example/lib/utils/crypto/eip155_service.dart similarity index 98% rename from example/lib/utils/crypto/eip155.dart rename to example/lib/utils/crypto/eip155_service.dart index 6a2122ce..2236beb3 100644 --- a/example/lib/utils/crypto/eip155.dart +++ b/example/lib/utils/crypto/eip155_service.dart @@ -1,13 +1,13 @@ import 'dart:convert'; import 'package:intl/intl.dart'; -import 'package:walletconnect_flutter_dapp/utils/crypto/smart_contracts/usdt_contract.dart'; +import 'package:walletconnect_flutter_dapp/utils/crypto/test_data/usdt_contract.dart'; import 'package:web3modal_flutter/web3modal_flutter.dart'; // ignore: depend_on_referenced_packages import 'package:convert/convert.dart'; -import 'package:walletconnect_flutter_dapp/utils/crypto/smart_contracts/aave_contract.dart'; -import 'package:walletconnect_flutter_dapp/utils/crypto/test_data.dart'; +import 'package:walletconnect_flutter_dapp/utils/crypto/test_data/aave_contract.dart'; +import 'package:walletconnect_flutter_dapp/utils/crypto/test_data/test_data.dart'; enum EIP155UIMethods { personalSign, diff --git a/example/lib/utils/crypto/helpers.dart b/example/lib/utils/crypto/helpers.dart deleted file mode 100644 index 064c0fb8..00000000 --- a/example/lib/utils/crypto/helpers.dart +++ /dev/null @@ -1,33 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:walletconnect_flutter_dapp/models/chain_metadata.dart'; -import 'package:walletconnect_flutter_dapp/utils/crypto/chain_data_wrapper.dart'; -import 'package:web3modal_flutter/utils/w3m_chains_presets.dart'; - -String getChainName(String chain) { - try { - return ChainDataWrapper.chains - .where((element) => element.w3mChainInfo.namespace == chain) - .first - .w3mChainInfo - .chainName; - } catch (e) { - debugPrint('[ExampleApp] getChainName, Invalid chain: $chain'); - } - return 'Unknown'; -} - -ChainMetadata getChainMetadataFromChain(String namespace) { - try { - return ChainDataWrapper.chains - .where((element) => element.w3mChainInfo.namespace == namespace) - .first; - } catch (_) { - return ChainMetadata( - color: Colors.blue, - type: ChainType.eip155, - w3mChainInfo: W3MChainPresets.chains.values.firstWhere( - (e) => e.namespace == namespace, - ), - ); - } -} diff --git a/example/lib/utils/crypto/kadena.dart b/example/lib/utils/crypto/kadena_service.dart similarity index 100% rename from example/lib/utils/crypto/kadena.dart rename to example/lib/utils/crypto/kadena_service.dart diff --git a/example/lib/utils/crypto/siwe_service.dart b/example/lib/utils/crypto/siwe_service.dart new file mode 100644 index 00000000..38354606 --- /dev/null +++ b/example/lib/utils/crypto/siwe_service.dart @@ -0,0 +1,109 @@ +import 'package:flutter/foundation.dart'; +// ignore: depend_on_referenced_packages +import 'package:http/http.dart' as http; +import 'dart:convert'; + +import 'package:walletconnect_flutter_dapp/utils/dart_defines.dart'; +import 'package:web3modal_flutter/utils/core/core_utils_singleton.dart'; + +class SIWESampleWebService { + late Map _headers; + + SIWESampleWebService() { + _headers = coreUtils.instance.getAPIHeaders( + DartDefines.appKitProjectId, + ); + } + + Future> getNonce() async { + try { + final res = await http.get( + Uri.parse('${DartDefines.authApiUrl}/auth/v1/nonce'), + headers: _headers, + ); + final nonceRes = json.decode(res.body) as Map; + final newToken = nonceRes['token'] as String; + _headers['Authorization'] = 'Bearer $newToken'; + // Persist the newToken so it can be used again with getSession() even if the user terminated the app + return nonceRes; + } catch (error) { + debugPrint('[SIWESERVICE] ⛔️ getNonce() => ${error.toString()}'); + rethrow; + } + } + + Future> getAppKitAuthSession() async { + try { + final response = await http.get( + Uri.parse('${DartDefines.authApiUrl}/auth/v1/me'), + headers: _headers, + ); + if (response.statusCode == 200) { + return json.decode(response.body); + } + throw Exception(response.statusCode.toString()); + } catch (error) { + debugPrint( + '[SIWESERVICE] ⛔️ getAppKitAuthSession() => ${error.toString()}'); + rethrow; + } + } + + Future> authenticate( + Map payload, { + required String domain, + }) async { + try { + final uri = Uri.parse('${DartDefines.authApiUrl}/auth/v1/authenticate'); + final res = await http.post( + uri.replace(queryParameters: {'domain': domain}), + headers: { + ..._headers, + 'Content-Type': 'application/json', + }, + body: jsonEncode(payload), + ); + debugPrint(jsonEncode(payload)); + debugPrint(res.request?.url.toString()); + debugPrint(jsonEncode(res.request?.headers)); + debugPrint(res.body); + final authenticateRes = jsonDecode(res.body); + final newToken = authenticateRes['token'] as String; + _headers['Authorization'] = 'Bearer $newToken'; + // Persist the newToken so it can be used again with getSession() even if the user terminated the app + return authenticateRes; + } catch (error) { + debugPrint('[SIWESERVICE] ⛔️ authenticate() => ${error.toString()}'); + rethrow; + } + } + + Future> updateUser(Map data) async { + try { + final res = await http.post( + Uri.parse('${DartDefines.authApiUrl}/auth/v1/update-user'), + headers: _headers, + body: json.encode({'metadata': data}), + ); + final updateUserRes = json.decode(res.body); + return updateUserRes; + } catch (error) { + debugPrint('[SIWESERVICE] ⛔️ updateUser() => ${error.toString()}'); + rethrow; + } + } + + Future> appKitAuthSignOut() async { + try { + final res = await http.post( + Uri.parse('${DartDefines.authApiUrl}/auth/v1/sign-out'), + headers: _headers, + ); + final signOutRes = json.decode(res.body) as Map; + return signOutRes; + } catch (error) { + debugPrint('[SIWESERVICE] ⛔️ appKitAuthSignOut() => ${error.toString()}'); + rethrow; + } + } +} diff --git a/example/lib/utils/crypto/solana_data.dart b/example/lib/utils/crypto/solana_service.dart similarity index 85% rename from example/lib/utils/crypto/solana_data.dart rename to example/lib/utils/crypto/solana_service.dart index 886dc397..17c5ee6d 100644 --- a/example/lib/utils/crypto/solana_data.dart +++ b/example/lib/utils/crypto/solana_service.dart @@ -7,6 +7,7 @@ enum SolanaEvents { none, } +// TODO to be implement when non-EVM chain support is added. class SolanaData { static final Map methods = { SolanaMethods.solanaSignTransaction: 'solana_signTransaction', diff --git a/example/lib/utils/crypto/smart_contracts/aave_contract.dart b/example/lib/utils/crypto/test_data/aave_contract.dart similarity index 100% rename from example/lib/utils/crypto/smart_contracts/aave_contract.dart rename to example/lib/utils/crypto/test_data/aave_contract.dart diff --git a/example/lib/utils/crypto/smart_contracts/test_contract.dart b/example/lib/utils/crypto/test_data/test_contract.dart similarity index 100% rename from example/lib/utils/crypto/smart_contracts/test_contract.dart rename to example/lib/utils/crypto/test_data/test_contract.dart diff --git a/example/lib/utils/crypto/test_data.dart b/example/lib/utils/crypto/test_data/test_data.dart similarity index 100% rename from example/lib/utils/crypto/test_data.dart rename to example/lib/utils/crypto/test_data/test_data.dart diff --git a/example/lib/utils/crypto/smart_contracts/usdt_contract.dart b/example/lib/utils/crypto/test_data/usdt_contract.dart similarity index 100% rename from example/lib/utils/crypto/smart_contracts/usdt_contract.dart rename to example/lib/utils/crypto/test_data/usdt_contract.dart diff --git a/example/lib/utils/dart_defines.dart b/example/lib/utils/dart_defines.dart index d70ca8fd..04d687c3 100644 --- a/example/lib/utils/dart_defines.dart +++ b/example/lib/utils/dart_defines.dart @@ -2,4 +2,13 @@ class DartDefines { static const String projectId = String.fromEnvironment( 'PROJECT_ID', ); + static const String appKitAuth = String.fromEnvironment( + 'APPKIT_AUTH', + ); + static const String appKitProjectId = String.fromEnvironment( + 'APPKIT_PROJECT_ID', + ); + static const String authApiUrl = String.fromEnvironment( + 'AUTH_SERVICE_URL', + ); } diff --git a/example/lib/utils/string_constants.dart b/example/lib/utils/string_constants.dart deleted file mode 100644 index 3ddacbf3..00000000 --- a/example/lib/utils/string_constants.dart +++ /dev/null @@ -1,42 +0,0 @@ -class StringConstants { - // General - static const String cancel = 'Cancel'; - static const String close = 'Close'; - static const String ok = 'OK'; - static const String delete = 'Delete'; - - // Main Page - static const String appTitle = 'WalletConnect v2 Flutter dApp Demo'; - static const String basicPageTitle = 'Basic'; - static const String wcmPageTitle = 'WalletConnect Modal'; - static const String w3mPageTitle = 'Web3Modal'; - static const String w3mPageTitleV3 = 'Web3Modal Flutter'; - static const String pairingsPageTitle = 'Pairings'; - static const String sessionsPageTitle = 'Sessions'; - static const String authPageTitle = 'Auth'; - static const String settingsPageTitle = 'Settings'; - static const String receivedPing = 'Received Ping'; - static const String receivedEvent = 'Received Event'; - - // Sign Page - static const String selectChains = 'Select chains:'; - static const String testnetsOnly = 'Testnets only?'; - static const String scanQrCode = 'Scan QR Code'; - static const String copiedToClipboard = 'Copied to clipboard'; - static const String bareBonesSign = 'Connect Bare Bones'; - static const String connectionEstablished = 'Session established'; - static const String connectionFailed = 'Session setup failed'; - static const String authSucceeded = 'Authentication Successful'; - static const String authFailed = 'Authentication Failed'; - - // Pairings Page - static const String pairings = 'Pairings'; - static const String deletePairing = 'Delete Pairing?'; - - // Sessions Page - static const String sessions = 'Sessions'; - static const String noSessionSelected = 'No session selected'; - static const String sessionTopic = 'Session Topic: '; - static const String methods = 'Methods'; - static const String events = 'Events'; -} diff --git a/example/lib/widgets/method_dialog.dart b/example/lib/widgets/method_dialog.dart index 5d8b1417..2c55a515 100644 --- a/example/lib/widgets/method_dialog.dart +++ b/example/lib/widgets/method_dialog.dart @@ -4,7 +4,6 @@ import 'package:fl_toast/fl_toast.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:walletconnect_flutter_dapp/utils/constants.dart'; -import 'package:walletconnect_flutter_dapp/utils/string_constants.dart'; class MethodDialog extends StatefulWidget { static Future show( diff --git a/example/lib/widgets/session_widget.dart b/example/lib/widgets/session_widget.dart index 1509489d..11d922e7 100644 --- a/example/lib/widgets/session_widget.dart +++ b/example/lib/widgets/session_widget.dart @@ -1,13 +1,17 @@ +import 'dart:convert'; + +import 'package:fl_toast/fl_toast.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:walletconnect_flutter_dapp/home_page.dart'; +import 'package:walletconnect_flutter_dapp/utils/chain_data_wrapper.dart'; +import 'package:web3modal_flutter/utils/core/core_utils_singleton.dart'; import 'package:web3modal_flutter/web3modal_flutter.dart'; import 'package:walletconnect_flutter_dapp/models/chain_metadata.dart'; import 'package:walletconnect_flutter_dapp/utils/constants.dart'; -import 'package:walletconnect_flutter_dapp/utils/crypto/eip155.dart'; -import 'package:walletconnect_flutter_dapp/utils/crypto/helpers.dart'; -import 'package:walletconnect_flutter_dapp/utils/string_constants.dart'; +import 'package:walletconnect_flutter_dapp/utils/crypto/eip155_service.dart'; import 'package:walletconnect_flutter_dapp/widgets/method_dialog.dart'; class SessionWidget extends StatefulWidget { @@ -36,7 +40,12 @@ class SessionWidgetState extends State { children: [ CircleAvatar( radius: 25.0, - backgroundImage: NetworkImage(iconImage), + backgroundImage: NetworkImage( + iconImage, + headers: coreUtils.instance.getAPIHeaders( + widget.w3mService.web3App!.core.projectId, + ), + ), ), const SizedBox(width: 10.0), ], @@ -116,6 +125,42 @@ class SessionWidgetState extends State { } catch (e) { debugPrint('[ExampleApp] ${e.toString()}'); } + children.add( + Column( + children: [ + const SizedBox.square(dimension: 8.0), + Text( + 'Stored session:', + style: + Web3ModalTheme.getDataOf(context).textStyles.small600.copyWith( + color: Web3ModalTheme.colorsOf(context).foreground100, + ), + ), + InkWell( + onTap: () => Clipboard.setData( + ClipboardData( + text: jsonEncode(widget.w3mService.session?.toMap()), + ), + ).then( + (_) => showPlatformToast( + child: const Text(StringConstants.copiedToClipboard), + context: context, + ), + ), + child: Text( + const JsonEncoder.withIndent(" ") + .convert(widget.w3mService.session?.toMap()), + style: Web3ModalTheme.getDataOf(context) + .textStyles + .small400 + .copyWith( + color: Web3ModalTheme.colorsOf(context).foreground100, + ), + ), + ), + ], + ), + ); return Padding( padding: const EdgeInsets.only(left: 16.0, right: 16.0, bottom: 16.0), diff --git a/example/pubspec.lock b/example/pubspec.lock index 7946a7b8..55d82b23 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -213,10 +213,10 @@ packages: dependency: transitive description: name: coinbase_wallet_sdk - sha256: "0e8d78359a2d8e6e7c57274342bb9d8b7257d4b42961a09399acc50d254aeae8" + sha256: e18bd5351939aacda75c0b525aa9167b945f1206040ec254f971afe96dfd98b0 url: "https://pub.dev" source: hosted - version: "1.0.7" + version: "1.0.8" collection: dependency: transitive description: @@ -1105,10 +1105,10 @@ packages: dependency: transitive description: name: walletconnect_flutter_v2 - sha256: cc6fa6a537910a66258ee64bb510edbfc0dee01485ea1138651431087c94671b + sha256: "7f6f66038ce0f559c5661c21b6485f0abfe42354605a628c2fb571a02ec1386e" url: "https://pub.dev" source: hosted - version: "2.2.3" + version: "2.3.0-beta03" watcher: dependency: transitive description: @@ -1139,7 +1139,7 @@ packages: path: ".." relative: true source: path - version: "3.2.2" + version: "3.3.0-beta01" web_socket_channel: dependency: transitive description: diff --git a/example/pubspec.yaml b/example/pubspec.yaml index 99e1c1a9..8823dfdf 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -3,7 +3,7 @@ description: A dApp showing how to use WalletConnect v2 with Flutter publish_to: "none" -version: 3.2.2 +version: 3.3.0 environment: sdk: ">=3.0.1 <4.0.0" diff --git a/lib/constants/key_constants.dart b/lib/constants/key_constants.dart index 496f9fba..dbd4635e 100644 --- a/lib/constants/key_constants.dart +++ b/lib/constants/key_constants.dart @@ -20,6 +20,7 @@ class KeyConstants { static const Key getAWalletPageKey = Key('getAWalletPageKey'); static const Key approveTransactionPage = Key('approveTransactionPage'); static const Key confirmEmailPage = Key('confirmEmailPage'); + static const Key approveSiwePageKey = Key('approveSiwePageKey'); // Buttons static const Key helpButtonKey = Key('helpButtonKey'); diff --git a/lib/constants/string_constants.dart b/lib/constants/string_constants.dart index 967ec1af..ad1afa68 100644 --- a/lib/constants/string_constants.dart +++ b/lib/constants/string_constants.dart @@ -33,11 +33,4 @@ class StringConstants { static const String connectedWalletData = 'w3m_walletData'; static const String selectedChainId = 'w3m_selectedChainId'; static const String w3mSession = 'w3m_session'; - - // Urls - static const String exploreAllWallets = - 'https://explorer.walletconnect.com/?type=wallet'; - static const String learnMoreUrl = - 'https://ethereum.org/en/developers/docs/networks/'; - static const String secureSite = 'https://secure.walletconnect.com/dashboard'; } diff --git a/lib/constants/url_constants.dart b/lib/constants/url_constants.dart new file mode 100644 index 00000000..d11a52af --- /dev/null +++ b/lib/constants/url_constants.dart @@ -0,0 +1,17 @@ +class UrlConstants { + static const String apiService = 'https://api.web3modal.org'; + static const String blockChainService = 'https://rpc.walletconnect.org'; + static const String analyticsService = 'https://pulse.walletconnect.org'; + static const String cloudService = 'https://cloud.walletconnect.com'; + static const String exploreWallets = + 'https://explorer.walletconnect.com/?type=wallet'; + static const String secureService = + 'https://secure-mobile.walletconnect.com/mobile-sdk'; + + // + static const String secureDashboard = + 'https://secure.walletconnect.com/dashboard'; + static const String learnMoreUrl = + 'https://ethereum.org/en/developers/docs/networks'; + static const String docsUrl = 'https://docs.walletconnect.com'; +} diff --git a/lib/models/listing.dart b/lib/models/listing.dart index dfeb6f10..2ae2540c 100644 --- a/lib/models/listing.dart +++ b/lib/models/listing.dart @@ -14,7 +14,7 @@ class Listing { final String? rdns; final List? injected; - Listing({ + const Listing({ required this.id, required this.name, required this.homepage, diff --git a/lib/pages/about_networks.dart b/lib/pages/about_networks.dart index 6ff54cce..2aaef234 100644 --- a/lib/pages/about_networks.dart +++ b/lib/pages/about_networks.dart @@ -2,7 +2,7 @@ import 'package:flutter/material.dart'; import 'package:url_launcher/url_launcher_string.dart'; import 'package:web3modal_flutter/constants/key_constants.dart'; -import 'package:web3modal_flutter/constants/string_constants.dart'; +import 'package:web3modal_flutter/constants/url_constants.dart'; import 'package:web3modal_flutter/web3modal_flutter.dart'; import 'package:web3modal_flutter/widgets/buttons/simple_icon_button.dart'; import 'package:web3modal_flutter/widgets/help/help_section.dart'; @@ -47,7 +47,7 @@ class AboutNetworks extends StatelessWidget { const SizedBox(height: 8), SimpleIconButton( onTap: () => launchUrlString( - StringConstants.learnMoreUrl, + UrlConstants.learnMoreUrl, mode: LaunchMode.externalApplication, ), rightIcon: 'assets/icons/arrow_top_right.svg', diff --git a/lib/pages/approve_siwe.dart b/lib/pages/approve_siwe.dart new file mode 100644 index 00000000..08a67533 --- /dev/null +++ b/lib/pages/approve_siwe.dart @@ -0,0 +1,247 @@ +import 'dart:async'; + +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/widgets.dart'; + +import 'package:web3modal_flutter/constants/key_constants.dart'; +import 'package:web3modal_flutter/services/siwe_service/siwe_service_singleton.dart'; +import 'package:web3modal_flutter/services/w3m_service/i_w3m_service.dart'; +import 'package:web3modal_flutter/theme/constants.dart'; +import 'package:web3modal_flutter/utils/toast/toast_message.dart'; +import 'package:web3modal_flutter/utils/toast/toast_utils_singleton.dart'; +import 'package:web3modal_flutter/web3modal_flutter.dart'; +import 'package:web3modal_flutter/widgets/avatars/w3m_account_avatar.dart'; +import 'package:web3modal_flutter/widgets/buttons/primary_button.dart'; +import 'package:web3modal_flutter/widgets/buttons/secondary_button.dart'; +import 'package:web3modal_flutter/widgets/miscellaneous/content_loading.dart'; +import 'package:web3modal_flutter/widgets/web3modal_provider.dart'; +import 'package:web3modal_flutter/widgets/avatars/w3m_wallet_avatar.dart'; +import 'package:web3modal_flutter/widgets/navigation/navbar.dart'; + +class ApproveSIWEPage extends StatefulWidget { + final Function(W3MSession session) onSiweFinish; + const ApproveSIWEPage({ + required this.onSiweFinish, + }) : super(key: KeyConstants.approveSiwePageKey); + + @override + State createState() => _ApproveSIWEPageState(); +} + +class _ApproveSIWEPageState extends State { + IW3MService? _service; + double _position = 0.0; + static const _duration = Duration(milliseconds: 1500); + + @override + void initState() { + super.initState(); + WidgetsBinding.instance.addPostFrameCallback((_) { + setState(() { + _position = (MediaQuery.of(context).size.width / 2) + 8.0; + _service = Web3ModalProvider.of(context).service; + Future.delayed(Duration(milliseconds: 200), () { + _animate(); + }); + }); + }); + } + + void _animate() { + if (!mounted) return; + setState(() { + if (_position == (MediaQuery.of(context).size.width / 2) - 12.0) { + _position = (MediaQuery.of(context).size.width / 2) + 8.0; + } else { + _position = (MediaQuery.of(context).size.width / 2) - 12.0; + } + }); + Future.delayed(_duration, _animate); + } + + bool _waitingSign = false; + void _signIn() async { + setState(() => _waitingSign = true); + try { + final address = _service!.session!.address!; + String chainId = _service!.selectedChain?.chainId ?? '1'; + chainId = W3MChainPresets.chains[chainId]!.namespace; + // + final message = await siweService.instance!.createMessage( + chainId: chainId, + address: address, + ); + // + _service!.launchConnectedWallet(); + final signature = await siweService.instance!.signMessageRequest( + message, + session: _service!.session!, + ); + // + final clientId = await _service!.web3App!.core.crypto.getClientId(); + await siweService.instance!.verifyMessage( + message: message, + signature: signature, + clientId: clientId, + ); + // + final siweSession = await siweService.instance!.getSession(); + final newSession = _service!.session!.copyWith(siweSession: siweSession); + // + widget.onSiweFinish(newSession); + // + } on JsonRpcError catch (e) { + _handleError(e.message); + } on W3MServiceException catch (e) { + _handleError(e.message); + } catch (e) { + _handleError(e.toString()); + } + } + + void _handleError(String? error) { + debugPrint('[$runtimeType] _handleError $error'); + setState(() => _waitingSign = false); + toastUtils.instance.show(ToastMessage( + type: ToastType.error, + text: error ?? 'Something went wrong.', + )); + } + + void _cancelSIWE() async { + _service?.closeModal(disconnectSession: true); + } + + @override + Widget build(BuildContext context) { + if (_service == null) return ContentLoading(); + + final themeData = Web3ModalTheme.getDataOf(context); + final themeColors = Web3ModalTheme.colorsOf(context); + final radiuses = Web3ModalTheme.radiusesOf(context); + return Web3ModalNavbar( + title: 'Sign In', + noClose: true, + safeAreaLeft: true, + safeAreaRight: true, + safeAreaBottom: true, + onBack: _cancelSIWE, + body: Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const SizedBox.square(dimension: kPadding12), + const SizedBox.square(dimension: kPadding8), + SizedBox( + height: 76.0, + child: Stack( + alignment: AlignmentDirectional.topCenter, + children: [ + AnimatedPositioned( + duration: _duration, + curve: Curves.easeInOut, + top: 0, + left: _position, + child: Container( + padding: const EdgeInsets.all(8.0), + decoration: BoxDecoration( + borderRadius: _service!.session!.sessionService.isMagic + ? BorderRadius.circular(60.0) + : BorderRadius.circular(radiuses.radiusM), + color: themeColors.background150, + ), + child: _service!.session!.sessionService.isMagic + ? W3MAccountAvatar( + service: _service!, + size: 60.0, + ) + : SizedBox( + width: 60.0, + height: 60.0, + child: W3MListAvatar( + imageUrl: + _service?.session?.peer?.metadata.icons.first, + borderRadius: radiuses.radiusS, + ), + ), + ), + ), + AnimatedPositioned( + duration: _duration, + curve: Curves.easeInOut, + top: 0, + right: _position, + child: Container( + padding: const EdgeInsets.all(8.0), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(36.0), + color: themeColors.background150, + ), + child: SizedBox( + width: 60.0, + height: 60.0, + child: W3MListAvatar( + imageUrl: _service?.session?.self?.metadata.icons.first, + borderRadius: 30.0, + ), + ), + ), + ), + ], + ), + ), + const SizedBox.square(dimension: kPadding12), + const SizedBox.square(dimension: kPadding8), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 60.0), + child: Text( + '${_service!.web3App!.metadata.name} needs to connect to your wallet', + textAlign: TextAlign.center, + style: themeData.textStyles.paragraph400.copyWith( + color: themeColors.foreground100, + ), + ), + ), + const SizedBox.square(dimension: kPadding12), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 30.0), + child: Text( + 'Sign this message to prove you own this wallet and proceed. Canceling will disconnect you.', + textAlign: TextAlign.center, + style: themeData.textStyles.small400.copyWith( + color: themeColors.foreground200, + ), + ), + ), + const SizedBox.square(dimension: kPadding12), + Padding( + padding: const EdgeInsets.symmetric(horizontal: kPadding12), + child: Row( + mainAxisSize: MainAxisSize.max, + children: [ + const SizedBox.square(dimension: 4.0), + Expanded( + child: SecondaryButton( + title: 'Cancel', + onTap: _cancelSIWE, + ), + ), + const SizedBox.square(dimension: kPadding8), + Expanded( + child: PrimaryButton( + title: 'Sign', + onTap: _signIn, + loading: _waitingSign, + ), + ), + const SizedBox.square(dimension: 4.0), + ], + ), + ), + const SizedBox.square(dimension: kPadding8), + ], + ), + ); + } +} diff --git a/lib/pages/connet_network_page.dart b/lib/pages/connet_network_page.dart index 3681cc56..f3056002 100644 --- a/lib/pages/connet_network_page.dart +++ b/lib/pages/connet_network_page.dart @@ -4,6 +4,7 @@ import 'package:flutter/material.dart'; import 'package:web3modal_flutter/constants/key_constants.dart'; import 'package:web3modal_flutter/services/explorer_service/explorer_service_singleton.dart'; +import 'package:web3modal_flutter/services/siwe_service/siwe_service_singleton.dart'; import 'package:web3modal_flutter/services/w3m_service/i_w3m_service.dart'; import 'package:web3modal_flutter/theme/constants.dart'; import 'package:web3modal_flutter/utils/asset_util.dart'; @@ -52,7 +53,9 @@ class _ConnectNetworkPageState extends State final chainId = widget.chainInfo.chainId; if (W3MChainPresets.chains.containsKey(chainId)) { Future.delayed(const Duration(milliseconds: 300), () { - widgetStack.instance.pop(); + if (!siweService.instance!.enabled) { + widgetStack.instance.pop(); + } }); } } catch (e) { @@ -65,7 +68,9 @@ class _ConnectNetworkPageState extends State if (state == AppLifecycleState.resumed) { if (_service?.session?.sessionService.isCoinbase == true) { if (_service?.selectedChain?.chainId == widget.chainInfo.chainId) { - widgetStack.instance.pop(); + if (!siweService.instance!.enabled) { + widgetStack.instance.pop(); + } } } } diff --git a/lib/pages/get_wallet_page.dart b/lib/pages/get_wallet_page.dart index b8b1bfdd..b59bc96a 100644 --- a/lib/pages/get_wallet_page.dart +++ b/lib/pages/get_wallet_page.dart @@ -6,7 +6,7 @@ import 'package:flutter_svg/flutter_svg.dart'; import 'package:url_launcher/url_launcher.dart'; import 'package:web3modal_flutter/constants/key_constants.dart'; -import 'package:web3modal_flutter/constants/string_constants.dart'; +import 'package:web3modal_flutter/constants/url_constants.dart'; import 'package:web3modal_flutter/models/grid_item.dart'; import 'package:web3modal_flutter/theme/constants.dart'; import 'package:web3modal_flutter/web3modal_flutter.dart'; @@ -63,7 +63,7 @@ class GetWalletPage extends StatelessWidget { AllWalletsItem( title: 'Explore all', onTap: () => urlUtils.instance.launchUrl( - Uri.parse(StringConstants.exploreAllWallets), + Uri.parse(UrlConstants.exploreWallets), mode: LaunchMode.externalApplication, ), trailing: Padding( diff --git a/lib/pages/qr_code_page.dart b/lib/pages/qr_code_page.dart index 9aabf993..a4fc6839 100644 --- a/lib/pages/qr_code_page.dart +++ b/lib/pages/qr_code_page.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; +import 'package:shimmer/shimmer.dart'; import 'package:web3modal_flutter/constants/key_constants.dart'; import 'package:web3modal_flutter/services/w3m_service/i_w3m_service.dart'; @@ -51,8 +52,14 @@ class _QRCodePageState extends State { setState(() {}); } - void _onError(EventArgs? args) { - _showUserRejection(); + void _onError(ModalError? args) { + final event = args ?? ModalError('An error occurred'); + toastUtils.instance.show( + ToastMessage( + type: ToastType.error, + text: event.message, + ), + ); } @override @@ -85,10 +92,14 @@ class _QRCodePageState extends State { child: _qrQodeWidget ?? AspectRatio( aspectRatio: 1.0, - child: Container( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(radiuses.radiusL), - color: themeColors.grayGlass005, + child: Shimmer.fromColors( + baseColor: themeColors.grayGlass100, + highlightColor: themeColors.grayGlass025, + child: Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(radiuses.radiusL), + color: themeColors.grayGlass010, + ), ), ), ), @@ -144,8 +155,4 @@ class _QRCodePageState extends State { ToastMessage(type: ToastType.success, text: 'Link copied'), ); } - - void _showUserRejection() => toastUtils.instance.show( - ToastMessage(type: ToastType.error, text: 'User rejected'), - ); } diff --git a/lib/pages/upgrade_wallet_page.dart b/lib/pages/upgrade_wallet_page.dart index fac9e7d0..ed221196 100644 --- a/lib/pages/upgrade_wallet_page.dart +++ b/lib/pages/upgrade_wallet_page.dart @@ -2,7 +2,7 @@ import 'package:flutter/material.dart'; import 'package:url_launcher/url_launcher_string.dart'; import 'package:web3modal_flutter/constants/key_constants.dart'; -import 'package:web3modal_flutter/constants/string_constants.dart'; +import 'package:web3modal_flutter/constants/url_constants.dart'; import 'package:web3modal_flutter/services/analytics_service/analytics_service_singleton.dart'; import 'package:web3modal_flutter/services/analytics_service/models/analytics_event.dart'; import 'package:web3modal_flutter/theme/constants.dart'; @@ -40,12 +40,12 @@ class UpgradeWalletPage extends StatelessWidget { onTap: () { analyticsService.instance.sendEvent(EmailUpgradeFromModal()); launchUrlString( - StringConstants.secureSite, + UrlConstants.secureDashboard, mode: LaunchMode.externalApplication, ); }, rightIcon: 'assets/icons/arrow_top_right.svg', - title: 'secure.walletconnect.com', + title: Uri.parse(UrlConstants.secureDashboard).authority, size: BaseButtonSize.small, iconSize: 12.0, fontSize: 14.0, diff --git a/lib/services/analytics_service/analytics_service.dart b/lib/services/analytics_service/analytics_service.dart index 55433a5b..3bb8aa4c 100644 --- a/lib/services/analytics_service/analytics_service.dart +++ b/lib/services/analytics_service/analytics_service.dart @@ -3,6 +3,7 @@ import 'dart:convert'; import 'package:flutter/foundation.dart'; import 'package:http/http.dart' as http; import 'package:uuid/uuid.dart'; +import 'package:web3modal_flutter/constants/url_constants.dart'; import 'package:web3modal_flutter/services/analytics_service/i_analytics_service.dart'; import 'package:web3modal_flutter/services/analytics_service/models/analytics_event.dart'; @@ -15,9 +16,11 @@ class AnalyticsService implements IAnalyticsService { static const _debugApiEndpoint = 'https://analytics-api-cf-workers-staging.walletconnect-v1-bridge.workers.dev'; static const _debugProjectId = 'e087b4b0503b860119be49d906717c12'; + // bool _isEnabled = false; late final String _bundleId; late final String _endpoint; + late final Map _headers; @override final Stream events = _eventsController.stream; @@ -31,7 +34,12 @@ class AnalyticsService implements IAnalyticsService { AnalyticsService({ required this.projectId, this.enableAnalytics, - }); + }) { + _endpoint = kDebugMode ? _debugApiEndpoint : UrlConstants.analyticsService; + _headers = kDebugMode + ? coreUtils.instance.getAPIHeaders(_debugProjectId) + : coreUtils.instance.getAPIHeaders(projectId); + } @override Future init() async { @@ -42,12 +50,9 @@ class AnalyticsService implements IAnalyticsService { _isEnabled = enableAnalytics!; } _bundleId = await WalletConnectUtils.getPackageName(); - _endpoint = kDebugMode - ? _debugApiEndpoint - : await coreUtils.instance.getAnalyticsUrl(); - loggerService.instance.p('[$runtimeType] enabled: $_isEnabled'); + loggerService.instance.d('[$runtimeType] enabled: $_isEnabled'); } catch (e, s) { - loggerService.instance.p( + loggerService.instance.d( '[$runtimeType] init error', error: e, stackTrace: s, @@ -58,17 +63,15 @@ class AnalyticsService implements IAnalyticsService { @override Future fetchAnalyticsConfig() async { try { - final apiUrl = await coreUtils.instance.getApiUrl(); - final headers = coreUtils.instance.getAPIHeaders(projectId); final response = await http.get( - Uri.parse('$apiUrl/getAnalyticsConfig'), - headers: headers, + Uri.parse('${UrlConstants.apiService}/getAnalyticsConfig'), + headers: _headers, ); final json = jsonDecode(response.body) as Map; final enabled = json['isAnalyticsEnabled'] as bool?; return enabled ?? false; } catch (e, s) { - loggerService.instance.p( + loggerService.instance.d( '[$runtimeType] fetch remote configuration error', error: e, stackTrace: s, @@ -81,10 +84,6 @@ class AnalyticsService implements IAnalyticsService { void sendEvent(AnalyticsEvent analyticsEvent) async { if (!_isEnabled) return; try { - final headers = kDebugMode - ? coreUtils.instance.getAPIHeaders(_debugProjectId) - : coreUtils.instance.getAPIHeaders(projectId); - final body = jsonEncode({ 'eventId': Uuid().v4(), 'bundleId': _bundleId, @@ -94,16 +93,16 @@ class AnalyticsService implements IAnalyticsService { final response = await http.post( Uri.parse('$_endpoint/e'), - headers: headers, + headers: _headers, body: body, ); final code = response.statusCode; if (code == 200 || code == 202) { _eventsController.sink.add(analyticsEvent.toMap()); } - loggerService.instance.p('[$runtimeType] send event $code: $body'); + loggerService.instance.d('[$runtimeType] send event $code: $body'); } catch (e, s) { - loggerService.instance.p( + loggerService.instance.d( '[$runtimeType] send event error', error: e, stackTrace: s, diff --git a/lib/services/blockchain_api_service/blockchain_api_utils.dart b/lib/services/blockchain_api_service/blockchain_api_utils.dart deleted file mode 100644 index 0edd54aa..00000000 --- a/lib/services/blockchain_api_service/blockchain_api_utils.dart +++ /dev/null @@ -1,29 +0,0 @@ -import 'dart:convert'; - -import 'package:http/http.dart' as http; -import 'package:web3modal_flutter/constants/string_constants.dart'; -import 'package:web3modal_flutter/services/blockchain_api_service/blockchain_identity.dart'; -import 'package:web3modal_flutter/services/blockchain_api_service/i_blockchain_api_utils.dart'; -import 'package:web3modal_flutter/utils/core/core_utils_singleton.dart'; - -class BlockchainApiUtils extends IBlockchainApiUtils { - // - @override - final String projectId; - - BlockchainApiUtils({required this.projectId}); - - @override - Future getIdentity(String address, int chainId) async { - final scope = '${StringConstants.namespace}:$chainId'; - final url = await coreUtils.instance.getBlockchainApiUrl(); - final endpoint = - '$url/v1/identity/$address?chainId=$scope&projectId=$projectId'; - final response = await http.get(Uri.parse(endpoint)); - if (response.statusCode == 200) { - return BlockchainIdentity.fromJson(jsonDecode(response.body)); - } else { - throw Exception('Failed to load avatar'); - } - } -} diff --git a/lib/services/blockchain_api_service/blockchain_api_utils_singleton.dart b/lib/services/blockchain_api_service/blockchain_api_utils_singleton.dart deleted file mode 100644 index 2a17a413..00000000 --- a/lib/services/blockchain_api_service/blockchain_api_utils_singleton.dart +++ /dev/null @@ -1,7 +0,0 @@ -import 'package:web3modal_flutter/services/blockchain_api_service/i_blockchain_api_utils.dart'; - -class BlockchainApiUtilsSingleton { - IBlockchainApiUtils? instance; -} - -final blockchainApiUtils = BlockchainApiUtilsSingleton(); diff --git a/lib/services/blockchain_api_service/i_blockchain_api_utils.dart b/lib/services/blockchain_api_service/i_blockchain_api_utils.dart deleted file mode 100644 index b61c4f4a..00000000 --- a/lib/services/blockchain_api_service/i_blockchain_api_utils.dart +++ /dev/null @@ -1,9 +0,0 @@ -import 'package:web3modal_flutter/services/blockchain_api_service/blockchain_identity.dart'; - -abstract class IBlockchainApiUtils { - /// The project ID used when querying the API. - String get projectId; - - /// Gets the name and avatar of a provided address on the given chain - Future getIdentity(String address, int chainId); -} diff --git a/lib/services/blockchain_api_service/blockchain_identity.freezed.dart b/lib/services/blockchain_service/blockchain_identity.freezed.dart similarity index 100% rename from lib/services/blockchain_api_service/blockchain_identity.freezed.dart rename to lib/services/blockchain_service/blockchain_identity.freezed.dart diff --git a/lib/services/blockchain_api_service/blockchain_identity.g.dart b/lib/services/blockchain_service/blockchain_identity.g.dart similarity index 100% rename from lib/services/blockchain_api_service/blockchain_identity.g.dart rename to lib/services/blockchain_service/blockchain_identity.g.dart diff --git a/lib/services/blockchain_service/blockchain_service.dart b/lib/services/blockchain_service/blockchain_service.dart new file mode 100644 index 00000000..97db7e62 --- /dev/null +++ b/lib/services/blockchain_service/blockchain_service.dart @@ -0,0 +1,89 @@ +import 'dart:convert'; + +import 'package:http/http.dart' as http; +import 'package:walletconnect_flutter_v2/walletconnect_flutter_v2.dart'; +import 'package:web3modal_flutter/constants/string_constants.dart'; +import 'package:web3modal_flutter/constants/url_constants.dart'; +import 'package:web3modal_flutter/services/blockchain_service/models/blockchain_identity.dart'; +import 'package:web3modal_flutter/services/blockchain_service/i_blockchain_service.dart'; + +class BlockChainService extends IBlockChainService { + // + @override + final String projectId; + + final IWeb3App _web3app; + + BlockChainService({ + required this.projectId, + required IWeb3App web3app, + }) : _web3app = web3app; + + @override + Future getIdentity(String address, int chainId) async { + final scope = '${StringConstants.namespace}:$chainId'; + final clientId = await _web3app.core.crypto.getClientId(); + final uri = Uri.parse( + '${UrlConstants.blockChainService}/v1/identity/$address', + ); + final queryParams = { + 'chainId': scope, + 'projectId': projectId, + 'clientId': clientId, + }; + final response = await http.get(uri.replace(queryParameters: queryParams)); + if (response.statusCode == 200) { + return BlockchainIdentity.fromJson(jsonDecode(response.body)); + } else { + throw Exception('Failed to load avatar'); + } + } + + // TODO to be implemented + // @override + // Future getBalance(String chainId, String address) async { + // // final client = Web3Client(rpcUrl, Client()); + // // final amount = await client.getBalance(EthereumAddress.fromHex(address)); + // // return amount.getValueInUnit(EtherUnit.ether); + // final scope = '${StringConstants.namespace}:$chainId'; + // final clientId = await _web3app.core.crypto.getClientId(); + // final uri = Uri.parse('${UrlConstants.blockChainService}/v1'); + // final queryParams = { + // 'chainId': scope, + // 'projectId': projectId, + // 'clientId': clientId, + // }; + // // "chainId=eip155%3A1&projectId=cad4956f31a5e40a00b62865b030c6f8&clientId=did%3Akey%3Az6MkgNA9sezpYrpiJGSkkQwUQSqoEWSDsDFZifbu6tYbu…" + // final response = await http.post( + // uri.replace(queryParameters: queryParams), + // headers: { + // ...coreUtils.instance.getAPIHeaders(projectId), + // 'Content-Type': 'application/json', + // }, + // body: jsonEncode({ + // 'jsonrpc': '2.0', + // 'method': 'eth_getBalance', + // 'params': [address, 'latest'], + // 'chainId': 1 + // }), + // ); + // print(response.body); + // if (response.statusCode == 200) { + // final result = JsonRpcResponse.fromJson(jsonDecode(response.body)); + // final bytes = hex.decode(result.result.toString().replaceFirst('0x', '')); + // // "{"jsonrpc":"2.0","id":"","result":"0x0"}" + // } else { + // throw Exception('Failed to load balance'); + // } + // } + + // @override + // Future fetchEnsName(String rpcUrl, String address) async { + // return ''; + // } + + // @override + // Future fetchEnsAvatar(String rpcUrl, String address) async { + // return ''; + // } +} diff --git a/lib/services/blockchain_service/blockchain_service_singleton.dart b/lib/services/blockchain_service/blockchain_service_singleton.dart new file mode 100644 index 00000000..f2165b01 --- /dev/null +++ b/lib/services/blockchain_service/blockchain_service_singleton.dart @@ -0,0 +1,7 @@ +import 'package:web3modal_flutter/services/blockchain_service/i_blockchain_service.dart'; + +class BlockChainServiceSingleton { + IBlockChainService? instance; +} + +final blockchainService = BlockChainServiceSingleton(); diff --git a/lib/services/blockchain_service/i_blockchain_service.dart b/lib/services/blockchain_service/i_blockchain_service.dart new file mode 100644 index 00000000..5b23527b --- /dev/null +++ b/lib/services/blockchain_service/i_blockchain_service.dart @@ -0,0 +1,15 @@ +import 'package:web3modal_flutter/services/blockchain_service/models/blockchain_identity.dart'; + +abstract class IBlockChainService { + /// The project ID used when querying the API. + String get projectId; + + /// Gets the name and avatar of a provided address on the given chain + Future getIdentity(String address, int chainId); + + // Future getBalance(String chainId, String address); + + // Future fetchEnsName(String rpcUrl, String address); + + // Future fetchEnsAvatar(String rpcUrl, String address); +} diff --git a/lib/services/blockchain_api_service/blockchain_identity.dart b/lib/services/blockchain_service/models/blockchain_identity.dart similarity index 100% rename from lib/services/blockchain_api_service/blockchain_identity.dart rename to lib/services/blockchain_service/models/blockchain_identity.dart diff --git a/lib/services/blockchain_service/models/blockchain_identity.freezed.dart b/lib/services/blockchain_service/models/blockchain_identity.freezed.dart new file mode 100644 index 00000000..1c372f97 --- /dev/null +++ b/lib/services/blockchain_service/models/blockchain_identity.freezed.dart @@ -0,0 +1,169 @@ +// coverage:ignore-file +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark + +part of 'blockchain_identity.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +T _$identity(T value) => value; + +final _privateConstructorUsedError = UnsupportedError( + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); + +BlockchainIdentity _$BlockchainIdentityFromJson(Map json) { + return _BlockchainIdentity.fromJson(json); +} + +/// @nodoc +mixin _$BlockchainIdentity { + String? get name => throw _privateConstructorUsedError; + String? get avatar => throw _privateConstructorUsedError; + + Map toJson() => throw _privateConstructorUsedError; + @JsonKey(ignore: true) + $BlockchainIdentityCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $BlockchainIdentityCopyWith<$Res> { + factory $BlockchainIdentityCopyWith( + BlockchainIdentity value, $Res Function(BlockchainIdentity) then) = + _$BlockchainIdentityCopyWithImpl<$Res, BlockchainIdentity>; + @useResult + $Res call({String? name, String? avatar}); +} + +/// @nodoc +class _$BlockchainIdentityCopyWithImpl<$Res, $Val extends BlockchainIdentity> + implements $BlockchainIdentityCopyWith<$Res> { + _$BlockchainIdentityCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? name = freezed, + Object? avatar = freezed, + }) { + return _then(_value.copyWith( + name: freezed == name + ? _value.name + : name // ignore: cast_nullable_to_non_nullable + as String?, + avatar: freezed == avatar + ? _value.avatar + : avatar // ignore: cast_nullable_to_non_nullable + as String?, + ) as $Val); + } +} + +/// @nodoc +abstract class _$$BlockchainIdentityImplCopyWith<$Res> + implements $BlockchainIdentityCopyWith<$Res> { + factory _$$BlockchainIdentityImplCopyWith(_$BlockchainIdentityImpl value, + $Res Function(_$BlockchainIdentityImpl) then) = + __$$BlockchainIdentityImplCopyWithImpl<$Res>; + @override + @useResult + $Res call({String? name, String? avatar}); +} + +/// @nodoc +class __$$BlockchainIdentityImplCopyWithImpl<$Res> + extends _$BlockchainIdentityCopyWithImpl<$Res, _$BlockchainIdentityImpl> + implements _$$BlockchainIdentityImplCopyWith<$Res> { + __$$BlockchainIdentityImplCopyWithImpl(_$BlockchainIdentityImpl _value, + $Res Function(_$BlockchainIdentityImpl) _then) + : super(_value, _then); + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? name = freezed, + Object? avatar = freezed, + }) { + return _then(_$BlockchainIdentityImpl( + name: freezed == name + ? _value.name + : name // ignore: cast_nullable_to_non_nullable + as String?, + avatar: freezed == avatar + ? _value.avatar + : avatar // ignore: cast_nullable_to_non_nullable + as String?, + )); + } +} + +/// @nodoc +@JsonSerializable() +class _$BlockchainIdentityImpl implements _BlockchainIdentity { + const _$BlockchainIdentityImpl({this.name, this.avatar}); + + factory _$BlockchainIdentityImpl.fromJson(Map json) => + _$$BlockchainIdentityImplFromJson(json); + + @override + final String? name; + @override + final String? avatar; + + @override + String toString() { + return 'BlockchainIdentity(name: $name, avatar: $avatar)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$BlockchainIdentityImpl && + (identical(other.name, name) || other.name == name) && + (identical(other.avatar, avatar) || other.avatar == avatar)); + } + + @JsonKey(ignore: true) + @override + int get hashCode => Object.hash(runtimeType, name, avatar); + + @JsonKey(ignore: true) + @override + @pragma('vm:prefer-inline') + _$$BlockchainIdentityImplCopyWith<_$BlockchainIdentityImpl> get copyWith => + __$$BlockchainIdentityImplCopyWithImpl<_$BlockchainIdentityImpl>( + this, _$identity); + + @override + Map toJson() { + return _$$BlockchainIdentityImplToJson( + this, + ); + } +} + +abstract class _BlockchainIdentity implements BlockchainIdentity { + const factory _BlockchainIdentity( + {final String? name, final String? avatar}) = _$BlockchainIdentityImpl; + + factory _BlockchainIdentity.fromJson(Map json) = + _$BlockchainIdentityImpl.fromJson; + + @override + String? get name; + @override + String? get avatar; + @override + @JsonKey(ignore: true) + _$$BlockchainIdentityImplCopyWith<_$BlockchainIdentityImpl> get copyWith => + throw _privateConstructorUsedError; +} diff --git a/lib/services/blockchain_service/models/blockchain_identity.g.dart b/lib/services/blockchain_service/models/blockchain_identity.g.dart new file mode 100644 index 00000000..b3504975 --- /dev/null +++ b/lib/services/blockchain_service/models/blockchain_identity.g.dart @@ -0,0 +1,23 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +// ignore_for_file: non_constant_identifier_names + +part of 'blockchain_identity.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +_$BlockchainIdentityImpl _$$BlockchainIdentityImplFromJson( + Map json) => + _$BlockchainIdentityImpl( + name: json['name'] as String?, + avatar: json['avatar'] as String?, + ); + +Map _$$BlockchainIdentityImplToJson( + _$BlockchainIdentityImpl instance) => + { + 'name': instance.name, + 'avatar': instance.avatar, + }; diff --git a/lib/services/coinbase_service/coinbase_service.dart b/lib/services/coinbase_service/coinbase_service.dart index f76f8926..2d53c50f 100644 --- a/lib/services/coinbase_service/coinbase_service.dart +++ b/lib/services/coinbase_service/coinbase_service.dart @@ -1,11 +1,12 @@ import 'dart:convert'; -import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; +import 'package:web3modal_flutter/models/listing.dart'; import 'package:web3modal_flutter/services/coinbase_service/i_coinbase_service.dart'; import 'package:web3modal_flutter/services/coinbase_service/models/coinbase_data.dart'; import 'package:web3modal_flutter/services/coinbase_service/models/coinbase_events.dart'; +import 'package:web3modal_flutter/services/explorer_service/explorer_service_singleton.dart'; import 'package:web3modal_flutter/web3modal_flutter.dart'; import 'package:coinbase_wallet_sdk/currency.dart'; @@ -16,11 +17,41 @@ import 'package:coinbase_wallet_sdk/eth_web3_rpc.dart'; import 'package:coinbase_wallet_sdk/request.dart'; class CoinbaseService implements ICoinbaseService { - static const coinbaseWalletId = - 'fd20dc426fb37566d803205b19bbc1d4096b248ac04548e3cfb6b3a38bd033aa'; - static const coinbaseSchema = 'cbwallet://wsegue'; static const coinbasePackageName = 'org.toshi'; - static const coinbaseWalletName = 'Coinbase Wallet'; + static const defaultWalletData = W3MWalletInfo( + listing: Listing( + id: 'fd20dc426fb37566d803205b19bbc1d4096b248ac04548e3cfb6b3a38bd033aa', + name: 'Coinbase Wallet', + homepage: 'https://www.coinbase.com/wallet/', + imageId: 'a5ebc364-8f91-4200-fcc6-be81310a0000', + order: 4110, + mobileLink: 'cbwallet://wsegue', + appStore: 'https://apps.apple.com/app/apple-store/id1278383455', + playStore: 'https://play.google.com/store/apps/details?id=org.toshi', + // rdns: 'com.coinbase.wallet', + ), + installed: false, + recent: false, + ); + + String _iconImage = ''; + + @override + ConnectionMetadata get metadata => ConnectionMetadata( + metadata: PairingMetadata( + name: _walletData.listing.name, + description: '', + url: _walletData.listing.homepage, + icons: [ + _iconImage, + ], + redirect: Redirect( + native: _walletData.listing.mobileLink, + universal: _walletData.listing.webappLink, + ), + ), + publicKey: '', + ); static const supportedMethods = [ ...MethodsConstants.requiredMethods, @@ -33,54 +64,73 @@ class CoinbaseService implements ICoinbaseService { 'wallet_watchAsset', ]; - @Deprecated('Use onModalConnect') @override Event onCoinbaseConnect = Event(); - @Deprecated('Use onModalError') @override Event onCoinbaseError = Event(); - @Deprecated('Use onModalSessionUpdate') @override Event onCoinbaseSessionUpdate = Event(); - @Deprecated('Do no use') @override Event get onCoinbaseResponse => Event(); - @protected + final IWeb3App _web3app; + late bool _enabled; + late W3MWalletInfo _walletData; + + CoinbaseService({required IWeb3App web3app, bool enabled = false}) + : _web3app = web3app, + _enabled = enabled; + @override - Future cbInit({ - required PairingMetadata metadata, - W3MWalletInfo? cbWallet, - }) async { + Future init() async { + if (!_enabled) return; // Configure SDK for each platform - final universal = metadata.redirect?.universal ?? metadata.url; - final nativeLink = metadata.redirect?.native ?? ''; - if (universal.isNotEmpty && nativeLink.isNotEmpty) { + + _walletData = (await explorerService.instance.getCoinbaseWalletObject()) ?? + defaultWalletData; + final imageId = defaultWalletData.listing.imageId; + _iconImage = explorerService.instance.getWalletImageUrl(imageId); + + final universal = _web3app.metadata.redirect?.universal ?? ''; + final nativeLink = _web3app.metadata.redirect?.native ?? ''; + final walletLink = _walletData.listing.mobileLink ?? ''; + if ((universal.isNotEmpty && nativeLink.isNotEmpty) || + walletLink.isNotEmpty) { try { final config = Configuration( ios: IOSConfiguration( - host: Uri.parse(cbWallet?.listing.mobileLink ?? coinbaseSchema), + host: Uri.parse(walletLink), callback: Uri.parse(nativeLink), ), - android: AndroidConfiguration(domain: Uri.parse(universal)), + android: AndroidConfiguration( + domain: Uri.parse(universal), + ), ); await CoinbaseWalletSDK.shared.configure(config); } catch (_) { // Silent error } } else { + _enabled = false; throw W3MCoinbaseException('Initialization error'); } } - @protected @override - Future cbGetAccount() async { + Future get ownPublicKey async => + await CoinbaseWalletSDK.shared.ownPublicKey(); + + @override + Future get peerPublicKey async => + await CoinbaseWalletSDK.shared.peerPublicKey(); + + @override + Future getAccount() async { await _checkInstalled(); try { final results = await CoinbaseWalletSDK.shared.initiateHandshake([ @@ -94,7 +144,15 @@ class CoinbaseService implements ICoinbaseService { throw W3MCoinbaseException('$errorMessage ($errorCode)'); } - final data = CoinbaseData.fromJson(result.account!.toJson()); + final data = CoinbaseData.fromJson(result.account!.toJson()).copytWith( + peer: metadata.copyWith( + publicKey: await peerPublicKey, + ), + self: ConnectionMetadata( + metadata: _web3app.metadata, + publicKey: await ownPublicKey, + ), + ); onCoinbaseConnect.broadcast(CoinbaseConnectEvent(data)); return; } on PlatformException catch (e, s) { @@ -108,9 +166,8 @@ class CoinbaseService implements ICoinbaseService { } } - @protected @override - Future cbRequest({ + Future request({ required String chainId, required SessionRequestParams request, }) async { @@ -134,7 +191,15 @@ class CoinbaseService implements ICoinbaseService { break; case 'eth_requestAccounts': final json = jsonDecode(value!); - final data = CoinbaseData.fromJson(json); + final data = CoinbaseData.fromJson(json).copytWith( + peer: metadata.copyWith( + publicKey: await peerPublicKey, + ), + self: ConnectionMetadata( + metadata: _web3app.metadata, + publicKey: await ownPublicKey, + ), + ); onCoinbaseConnect.broadcast(CoinbaseConnectEvent(data)); break; default: @@ -152,9 +217,8 @@ class CoinbaseService implements ICoinbaseService { } } - @protected @override - Future cbIsInstalled() async { + Future isInstalled() async { try { return await CoinbaseWalletSDK.shared.isAppInstalled(); } catch (e, s) { @@ -162,9 +226,8 @@ class CoinbaseService implements ICoinbaseService { } } - @protected @override - Future cbIsConnected() async { + Future isConnected() async { try { return await CoinbaseWalletSDK.shared.isConnected(); } catch (e, s) { @@ -172,9 +235,8 @@ class CoinbaseService implements ICoinbaseService { } } - @protected @override - Future cbResetSession() async { + Future resetSession() async { try { return CoinbaseWalletSDK.shared.resetSession(); } catch (e, s) { @@ -183,7 +245,7 @@ class CoinbaseService implements ICoinbaseService { } Future _checkInstalled() async { - final installed = await cbIsInstalled(); + final installed = await isInstalled(); if (!installed) { throw W3MCoinbaseNotInstalledException(); } @@ -209,25 +271,24 @@ extension on SessionRequestParams { case 'eth_requestAccounts': return RequestAccounts(); case 'eth_signTransaction': - final jsonData = _getTransactionFromParams(params); - final hexValue = jsonData['value'].toString().replaceFirst('0x', ''); - final value = int.parse(hexValue, radix: 16); - return SignTransaction( - fromAddress: jsonData['from'], - toAddress: jsonData['to'], - chainId: chainId!, - weiValue: BigInt.from(value), - data: jsonData['data'] ?? '', - ); case MethodsConstants.ethSendTransaction: + BigInt? weiValue; final jsonData = _getTransactionFromParams(params); - String? weiValue; if (jsonData.containsKey('value')) { final hexValue = jsonData['value'].toString().replaceFirst('0x', ''); final value = int.parse(hexValue, radix: 16); - weiValue = BigInt.from(value).toString(); + weiValue = BigInt.from(value); + } + final data = jsonData['data']?.toString(); + if (method == 'eth_signTransaction') { + return SignTransaction( + fromAddress: jsonData['from'].toString(), + toAddress: jsonData['to'].toString(), + chainId: chainId!, + weiValue: weiValue, + data: data, + ); } - final data = jsonData['data']?.toString() ?? ''; return SendTransaction( fromAddress: jsonData['from'].toString(), toAddress: jsonData['to'].toString(), @@ -259,9 +320,13 @@ extension on SessionRequestParams { } catch (e, s) { throw W3MCoinbaseException('Unrecognized chainId $chainId', e, s); } - // TODO [CoinbaseService] implement after Coinbase merges this PR https://github.com/MobileWalletProtocol/wallet-mobile-sdk/pull/327 - // case 'wallet_watchAsset': - // return WatchAsset(params: params); + case 'wallet_watchAsset': + final address = _getAddressFromParamsList(params); + final symbol = _getDataFromParamsList(params); + return WatchAsset( + address: address, + symbol: symbol, + ); default: throw W3MCoinbaseException('Unsupported request method $method'); } @@ -291,20 +356,3 @@ extension on SessionRequestParams { return param as Map; } } - -// class WatchAsset extends Action { -// WatchAsset({ -// required String address, -// required String symbol, -// int? decimals, -// String? image, -// }) : super( -// method: 'wallet_watchAsset', -// paramsJson: jsonEncode({ -// 'address': address, -// 'symbol': symbol, -// 'decimals': decimals ?? 18, -// if (image != null) 'image': image, -// }), -// ); -// } diff --git a/lib/services/coinbase_service/coinbase_service_singleton.dart b/lib/services/coinbase_service/coinbase_service_singleton.dart new file mode 100644 index 00000000..3685a4eb --- /dev/null +++ b/lib/services/coinbase_service/coinbase_service_singleton.dart @@ -0,0 +1,7 @@ +import 'package:web3modal_flutter/services/coinbase_service/i_coinbase_service.dart'; + +class CoinbaseServiceSingleton { + late ICoinbaseService instance; +} + +final coinbaseService = CoinbaseServiceSingleton(); diff --git a/lib/services/coinbase_service/i_coinbase_service.dart b/lib/services/coinbase_service/i_coinbase_service.dart index ac796976..deb8bd6c 100644 --- a/lib/services/coinbase_service/i_coinbase_service.dart +++ b/lib/services/coinbase_service/i_coinbase_service.dart @@ -16,23 +16,28 @@ class W3MCoinbaseNotInstalledException extends W3MCoinbaseException { W3MCoinbaseNotInstalledException() : super('App not installed'); } +class W3MCoinbaseNotEnabled extends W3MCoinbaseException { + W3MCoinbaseNotEnabled() : super('Coinbase is disabled'); +} + abstract class ICoinbaseService { - Future cbInit({required PairingMetadata metadata}); - Future cbIsConnected(); - Future cbGetAccount(); - Future cbRequest({ + Future init(); + Future isConnected(); + Future getAccount(); + Future request({ required String chainId, required SessionRequestParams request, }); - Future cbResetSession(); - Future cbIsInstalled(); + Future resetSession(); + Future isInstalled(); + + Future get ownPublicKey; + Future get peerPublicKey; + + ConnectionMetadata get metadata; - @Deprecated('Use onModalConnect') abstract final Event onCoinbaseConnect; - @Deprecated('Use onModalError') abstract final Event onCoinbaseError; - @Deprecated('Use onModalSessionUpdate') abstract final Event onCoinbaseSessionUpdate; - @Deprecated('Do no use') abstract final Event onCoinbaseResponse; } diff --git a/lib/services/coinbase_service/models/coinbase_data.dart b/lib/services/coinbase_service/models/coinbase_data.dart index c39d08b1..1e754c30 100644 --- a/lib/services/coinbase_service/models/coinbase_data.dart +++ b/lib/services/coinbase_service/models/coinbase_data.dart @@ -1,12 +1,18 @@ +import 'package:walletconnect_flutter_v2/walletconnect_flutter_v2.dart'; + class CoinbaseData { String address; String chainName; int chainId; + ConnectionMetadata? self; + ConnectionMetadata? peer; CoinbaseData({ required this.address, required this.chainName, required this.chainId, + this.self, + this.peer, }); factory CoinbaseData.fromJson(Map json) { @@ -14,6 +20,12 @@ class CoinbaseData { address: json['address'].toString(), chainName: json['chain'].toString(), chainId: int.parse(json['networkId'].toString()), + self: (json['self'] != null) + ? ConnectionMetadata.fromJson(json['self']) + : null, + peer: (json['peer'] != null) + ? ConnectionMetadata.fromJson(json['peer']) + : null, ); } @@ -22,6 +34,8 @@ class CoinbaseData { 'address': address, 'chain': chainName, 'networkId': chainId, + 'self': self?.toJson(), + 'peer': peer?.toJson(), }; } @@ -32,11 +46,15 @@ class CoinbaseData { String? address, String? chainName, int? chainId, + ConnectionMetadata? self, + ConnectionMetadata? peer, }) { return CoinbaseData( address: address ?? this.address, chainName: chainName ?? this.chainName, chainId: chainId ?? this.chainId, + self: self ?? this.self, + peer: peer ?? this.peer, ); } } diff --git a/lib/services/explorer_service/explorer_service.dart b/lib/services/explorer_service/explorer_service.dart index 410f2c33..68d0b098 100644 --- a/lib/services/explorer_service/explorer_service.dart +++ b/lib/services/explorer_service/explorer_service.dart @@ -5,6 +5,7 @@ import 'dart:io'; import 'package:collection/collection.dart'; import 'package:flutter/foundation.dart'; import 'package:http/http.dart' as http; +import 'package:web3modal_flutter/constants/url_constants.dart'; import 'package:web3modal_flutter/models/listing.dart'; import 'package:web3modal_flutter/services/coinbase_service/coinbase_service.dart'; import 'package:web3modal_flutter/services/explorer_service/models/native_app_data.dart'; @@ -26,8 +27,6 @@ import 'package:web3modal_flutter/web3modal_flutter.dart'; const int _defaultEntriesCount = 48; class ExplorerService implements IExplorerService { - String? _apiUrl; - final http.Client _client; final String _referer; @@ -106,8 +105,6 @@ class ExplorerService implements IExplorerService { return; } - _apiUrl = await coreUtils.instance.getApiUrl(); - await _setInstalledWalletIdsParam(); await _fetchInitialWallets(); @@ -157,7 +154,7 @@ class ExplorerService implements IExplorerService { sampleWallets.add(sampleWallet); } } - loggerService.instance.p( + loggerService.instance.d( '[$runtimeType] sample wallets: ${sampleWallets.length}', ); return sampleWallets; @@ -200,11 +197,10 @@ class ExplorerService implements IExplorerService { } Future> _fetchNativeAppData() async { - final apiUrl = await coreUtils.instance.getApiUrl(); final headers = coreUtils.instance.getAPIHeaders(projectId, _referer); final uri = Platform.isIOS - ? Uri.parse('$apiUrl/getIosData') - : Uri.parse('$apiUrl/getAndroidData'); + ? Uri.parse('${UrlConstants.apiService}/getIosData') + : Uri.parse('${UrlConstants.apiService}/getAndroidData'); try { final response = await _client.get(uri, headers: headers); if (response.statusCode == 200 || response.statusCode == 202) { @@ -281,10 +277,11 @@ class ExplorerService implements IExplorerService { RequestParams? params, bool updateCount = true, }) async { - final p = params?.toJson() ?? {}; - final apiUrl = await coreUtils.instance.getApiUrl(); + final queryParams = params?.toJson() ?? {}; final headers = coreUtils.instance.getAPIHeaders(projectId, _referer); - final uri = Uri.parse('$apiUrl/getWallets').replace(queryParameters: p); + final uri = Uri.parse('${UrlConstants.apiService}/getWallets').replace( + queryParameters: queryParams, + ); loggerService.instance.d('[$runtimeType] fetching $uri'); try { final response = await _client.get(uri, headers: headers); @@ -399,7 +396,7 @@ class ExplorerService implements IExplorerService { final excludedIds = (excludedWalletIds ?? {}); final exclude = excludedIds.isNotEmpty ? excludedIds.join(',') : null; - loggerService.instance.p('[$runtimeType] search $query'); + loggerService.instance.d('[$runtimeType] search $query'); _currentSearchValue = query; final newListins = await _fetchListings( params: RequestParams( @@ -432,13 +429,10 @@ class ExplorerService implements IExplorerService { if (results.isNotEmpty) { final wallet = W3MWalletInfo.fromJson(results.first.toJson()); - bool installed = await urlUtils.instance.isInstalled( - CoinbaseService.coinbaseSchema, - ); + final mobileLink = CoinbaseService.defaultWalletData.listing.mobileLink; + bool installed = await urlUtils.instance.isInstalled(mobileLink); return wallet.copyWith( - listing: wallet.listing.copyWith( - mobileLink: CoinbaseService.coinbaseSchema, - ), + listing: wallet.listing.copyWith(mobileLink: mobileLink), installed: installed, ); } @@ -453,8 +447,7 @@ class ExplorerService implements IExplorerService { if (imageId.startsWith('http')) { return imageId; } - final apiUrl = _apiUrl ?? 'https://api.web3modal.com'; - return '$apiUrl/getWalletImage/$imageId'; + return '${UrlConstants.apiService}/getWalletImage/$imageId'; } @override @@ -465,16 +458,15 @@ class ExplorerService implements IExplorerService { if (imageId.startsWith('http')) { return imageId; } - final apiUrl = _apiUrl ?? 'https://api.web3modal.com'; - return '$apiUrl/public/getAssetImage/$imageId'; + return '${UrlConstants.apiService}/public/getAssetImage/$imageId'; } @override WalletRedirect? getWalletRedirect(W3MWalletInfo? walletInfo) { if (walletInfo == null) return null; - if (walletInfo.listing.id == CoinbaseService.coinbaseWalletId) { + if (walletInfo.listing.id == CoinbaseService.defaultWalletData.listing.id) { return WalletRedirect( - mobile: CoinbaseService.coinbaseSchema, + mobile: CoinbaseService.defaultWalletData.listing.mobileLink, desktop: null, web: null, ); diff --git a/lib/services/explorer_service/models/wc_sample_wallets.dart b/lib/services/explorer_service/models/wc_sample_wallets.dart index 465bea7a..ddc99d9b 100644 --- a/lib/services/explorer_service/models/wc_sample_wallets.dart +++ b/lib/services/explorer_service/models/wc_sample_wallets.dart @@ -1,3 +1,4 @@ +import 'package:web3modal_flutter/constants/url_constants.dart'; import 'package:web3modal_flutter/models/listing.dart'; import 'package:web3modal_flutter/services/explorer_service/models/native_app_data.dart'; import 'package:web3modal_flutter/web3modal_flutter.dart'; @@ -100,5 +101,5 @@ class WCSampleWallets { } static const _walletImage = - 'https://docs.walletconnect.com/assets/images/web3walletLogo-54d3b546146931ceaf47a3500868a73a.png'; + '${UrlConstants.docsUrl}/assets/images/web3walletLogo-54d3b546146931ceaf47a3500868a73a.png'; } diff --git a/lib/services/logger_service/i_logger_service.dart b/lib/services/logger_service/i_logger_service.dart index b8f9544d..fa66f574 100644 --- a/lib/services/logger_service/i_logger_service.dart +++ b/lib/services/logger_service/i_logger_service.dart @@ -25,14 +25,6 @@ abstract class ILoggerService { StackTrace? stackTrace, }); - /// Log a message at level [Level.warning]. - void w( - dynamic message, { - DateTime? time, - Object? error, - StackTrace? stackTrace, - }); - /// Log a message at level [Level.error]. void e( dynamic message, { @@ -41,31 +33,6 @@ abstract class ILoggerService { StackTrace? stackTrace, }); - /// Log a message at level [Level.fatal]. - void f( - dynamic message, { - DateTime? time, - Object? error, - StackTrace? stackTrace, - }); - - /// Log a message with [level]. - void log( - Level level, - dynamic message, { - DateTime? time, - Object? error, - StackTrace? stackTrace, - }); - - /// Log a message at level private. - void p( - dynamic message, { - DateTime? time, - Object? error, - StackTrace? stackTrace, - }); - /// Closes the logger and releases all resources. Future close(); } diff --git a/lib/services/logger_service/logger_service.dart b/lib/services/logger_service/logger_service.dart index baa44529..21d65a41 100644 --- a/lib/services/logger_service/logger_service.dart +++ b/lib/services/logger_service/logger_service.dart @@ -6,13 +6,10 @@ import 'package:web3modal_flutter/web3modal_flutter.dart'; class LoggerService implements ILoggerService { late Logger _logger; - late String _projectId; LoggerService({ required LogLevel level, - required String projectId, - bool debugMode = true, + bool debugMode = kDebugMode, }) { - _projectId = projectId; _logger = Logger( level: level.toLevel(), printer: PrettyPrinter(methodCount: null), @@ -22,22 +19,7 @@ class LoggerService implements ILoggerService { } } - void _logListener(LogEvent event) { - debugPrint('${event.message}'); - } - - @override - void p( - message, { - DateTime? time, - Object? error, - StackTrace? stackTrace, - }) { - // TODO [LoggerService] fix this - if (_projectId == 'cad4956f31a5e40a00b62865b030c6f8') { - _logger.i(message, time: time, error: error, stackTrace: stackTrace); - } - } + void _logListener(LogEvent event) => debugPrint('${event.message}'); @override void d( @@ -59,16 +41,6 @@ class LoggerService implements ILoggerService { _logger.e(message, time: time, error: error, stackTrace: stackTrace); } - @override - void f( - message, { - DateTime? time, - Object? error, - StackTrace? stackTrace, - }) { - _logger.f(message, time: time, error: error, stackTrace: stackTrace); - } - @override void i( message, { @@ -79,18 +51,6 @@ class LoggerService implements ILoggerService { _logger.i(message, time: time, error: error, stackTrace: stackTrace); } - @override - void log( - Level level, - message, { - DateTime? time, - Object? error, - StackTrace? stackTrace, - }) { - _logger.log(level, message, - time: time, error: error, stackTrace: stackTrace); - } - @override void t( message, { @@ -101,19 +61,11 @@ class LoggerService implements ILoggerService { _logger.t(message, time: time, error: error, stackTrace: stackTrace); } - @override - void w( - message, { - DateTime? time, - Object? error, - StackTrace? stackTrace, - }) { - _logger.w(message, time: time, error: error, stackTrace: stackTrace); - } - @override Future close() async { - Logger.removeLogListener(_logListener); + try { + Logger.removeLogListener(_logListener); + } catch (_) {} return await _logger.close(); } } diff --git a/lib/services/magic_service/i_magic_service.dart b/lib/services/magic_service/i_magic_service.dart index 39531d19..b0c3a8f8 100644 --- a/lib/services/magic_service/i_magic_service.dart +++ b/lib/services/magic_service/i_magic_service.dart @@ -2,6 +2,8 @@ import 'package:web3modal_flutter/services/magic_service/models/magic_events.dar import 'package:web3modal_flutter/web3modal_flutter.dart'; abstract class IMagicService { + ConnectionMetadata get metadata; + Future init(); void setEmail(String value); diff --git a/lib/services/magic_service/magic_service.dart b/lib/services/magic_service/magic_service.dart index 6dbbb558..94cfcf72 100644 --- a/lib/services/magic_service/magic_service.dart +++ b/lib/services/magic_service/magic_service.dart @@ -6,6 +6,8 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:url_launcher/url_launcher_string.dart'; import 'package:web3modal_flutter/constants/string_constants.dart'; +import 'package:web3modal_flutter/constants/url_constants.dart'; +import 'package:web3modal_flutter/models/listing.dart'; import 'package:web3modal_flutter/services/analytics_service/analytics_service_singleton.dart'; import 'package:web3modal_flutter/services/analytics_service/models/analytics_event.dart'; import 'package:web3modal_flutter/services/logger_service/logger_service_singleton.dart'; @@ -21,10 +23,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 _url = 'secure-mobile.walletconnect.com'; static const _safeDomains = [ - _url, - 'secure.walletconnect.com', 'auth.magic.link', 'launchdarkly.com', ]; @@ -36,6 +35,31 @@ class MagicService implements IMagicService { 'wallet_switchEthereumChain', 'wallet_addEthereumChain', ]; + static const defaultWalletData = W3MWalletInfo( + listing: Listing( + id: '', + name: 'Email Wallet', + homepage: '', + imageId: '', + order: 10000, + ), + installed: false, + recent: false, + ); + + @override + ConnectionMetadata get metadata => ConnectionMetadata( + metadata: PairingMetadata( + name: defaultWalletData.listing.name, + description: '', + url: defaultWalletData.listing.homepage, + icons: [ + '', + ], + ), + publicKey: '', + ); + // final IWeb3App _web3app; Timer? _timeOutTimer; @@ -143,14 +167,15 @@ class MagicService implements IMagicService { onWebResourceError: _onWebResourceError, onPageFinished: (String url) async { _onLoadCount++; + // If bundleId/packageName is whitelisted in cloud then for some reason it enters here twice + // Like as if secure-mobile.walletconnect.com is loaded twice + // If bundleId/packageName is NOT whitelisted in cloud then it enter just once. + // This is happening only on Android devices, on iOS only once execution is done no matter what. if (_onLoadCount < 2 && Platform.isAndroid) return; - await _runJavascript(_web3app.core.projectId); - Future.delayed(Duration(milliseconds: 200)).then((_) async { - try { - _initialized.complete(true); - } catch (e) { - loggerService.instance.e('[$runtimeType] CRASH! $e'); - } + await _runJavascript(); + Future.delayed(Duration(milliseconds: 600)).then((value) { + if (_initialized.isCompleted) return; + _initialized.complete(true); }); }, ), @@ -303,7 +328,7 @@ class MagicService implements IMagicService { 'referer': _web3app.metadata.url, 'x-bundle-id': _packageName, }; - final uri = Uri.parse('https://$_url/mobile-sdk'); + final uri = Uri.parse(UrlConstants.secureService); final queryParams = { 'projectId': _web3app.core.projectId, 'bundleId': _packageName, @@ -327,7 +352,7 @@ class MagicService implements IMagicService { void _onFrameMessage(JavaScriptMessage jsMessage) async { if (Platform.isAndroid) { - loggerService.instance.p('[$runtimeType] jsMessage ${jsMessage.message}'); + loggerService.instance.d('[$runtimeType] jsMessage ${jsMessage.message}'); } try { final frameMessage = jsMessage.toFrameMessage(); @@ -429,7 +454,14 @@ class MagicService implements IMagicService { onMagicUpdate.broadcast(event); _connected.complete(isConnected.value); } else { - onMagicLoginSuccess.broadcast(MagicLoginEvent(data)); + final session = data.copytWith( + peer: metadata, + self: ConnectionMetadata( + metadata: _web3app.metadata, + publicKey: '', + ), + ); + onMagicLoginSuccess.broadcast(MagicLoginEvent(session)); } } // ****** SIGN_OUT @@ -482,7 +514,7 @@ class MagicService implements IMagicService { _error(SignOutErrorEvent()); } } catch (e, s) { - loggerService.instance.p('[$runtimeType] $jsMessage', stackTrace: s); + loggerService.instance.d('[$runtimeType] $jsMessage', stackTrace: s); } } @@ -531,24 +563,24 @@ class MagicService implements IMagicService { onMagicError.broadcast(errorEvent); } - Future _runJavascript(String projectId) async { + Future _runJavascript() async { return await _webViewController.runJavaScript(''' const iframeFL = document.getElementById('frame-mobile-sdk') - + window.addEventListener('message', ({ data, origin }) => { - console.log('w3mMessage received <=== ' + JSON.stringify({data,origin})) + console.log('[MagicService] received <=== ' + JSON.stringify({data,origin})) window.w3mWebview.postMessage(JSON.stringify({data,origin})) }) const sendW3Message = async (message) => { - console.log('w3mMessage posted =====> ' + JSON.stringify(message)) + console.log('[MagicService] posted =====> ' + JSON.stringify(message)) iframeFL.contentWindow.postMessage(message, '*') } '''); } void _onDebugConsoleReceived(JavaScriptConsoleMessage message) { - loggerService.instance.p('[$runtimeType] JS Console ${message.message}'); + loggerService.instance.d('[$runtimeType] JS Console ${message.message}'); } void _onWebResourceError(WebResourceError error) { @@ -568,7 +600,11 @@ class MagicService implements IMagicService { } bool _isAllowedDomain(String domain) { - final domains = _safeDomains.join('|'); + final domains = [ + Uri.parse(UrlConstants.secureService).authority, + Uri.parse(UrlConstants.secureDashboard).authority, + ..._safeDomains, + ].join('|'); return RegExp(r'' + domains).hasMatch(domain); } @@ -580,7 +616,7 @@ class MagicService implements IMagicService { loggerService.instance.e( '[EmailLogin] initialization timed out. Please check if your ' 'bundleId/packageName $_packageName is whitelisted in your cloud ' - 'configuration at https://cloud.walletconnect.com/ for project id ${_web3app.core.projectId}', + 'configuration at ${UrlConstants.cloudService} for project id ${_web3app.core.projectId}', ); } } diff --git a/lib/services/magic_service/models/frame_message.dart b/lib/services/magic_service/models/frame_message.dart index 473b7977..19cb8f12 100644 --- a/lib/services/magic_service/models/frame_message.dart +++ b/lib/services/magic_service/models/frame_message.dart @@ -2,12 +2,11 @@ import 'dart:convert'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; +import 'package:web3modal_flutter/constants/url_constants.dart'; import 'package:web3modal_flutter/utils/util.dart'; import 'package:web3modal_flutter/web3modal_flutter.dart'; class FrameMessage { - static const _origin = 'secure.walletconnect.com'; - final MessageData? data; final String? origin; @@ -35,7 +34,8 @@ class FrameMessage { }; bool get isValidOrigin { - return Uri.parse(origin ?? '').authority == _origin; + return Uri.parse(origin ?? '').authority == + Uri.parse(UrlConstants.secureDashboard).authority; } bool get isValidData { diff --git a/lib/services/magic_service/models/magic_data.dart b/lib/services/magic_service/models/magic_data.dart index 7765d924..43281bb6 100644 --- a/lib/services/magic_service/models/magic_data.dart +++ b/lib/services/magic_service/models/magic_data.dart @@ -1,12 +1,18 @@ +import 'package:walletconnect_flutter_v2/walletconnect_flutter_v2.dart'; + class MagicData { String email; String address; int chainId; + ConnectionMetadata? self; + ConnectionMetadata? peer; MagicData({ required this.email, required this.chainId, required this.address, + this.self, + this.peer, }); factory MagicData.fromJson(Map json) { @@ -14,6 +20,12 @@ class MagicData { email: json['email'].toString(), address: json['address'].toString(), chainId: int.parse(json['chainId'].toString()), + self: (json['self'] != null) + ? ConnectionMetadata.fromJson(json['self']) + : null, + peer: (json['peer'] != null) + ? ConnectionMetadata.fromJson(json['peer']) + : null, ); } @@ -22,6 +34,8 @@ class MagicData { 'email': email, 'address': address, 'chainId': chainId, + 'self': self?.toJson(), + 'peer': peer?.toJson(), }; } @@ -32,11 +46,15 @@ class MagicData { String? email, String? address, int? chainId, + ConnectionMetadata? self, + ConnectionMetadata? peer, }) { return MagicData( email: email ?? this.email, address: address ?? this.address, chainId: chainId ?? this.chainId, + self: self ?? this.self, + peer: peer ?? this.peer, ); } } diff --git a/lib/services/siwe_service/i_siwe_service.dart b/lib/services/siwe_service/i_siwe_service.dart new file mode 100644 index 00000000..2c5d5f54 --- /dev/null +++ b/lib/services/siwe_service/i_siwe_service.dart @@ -0,0 +1,39 @@ +import 'package:walletconnect_flutter_v2/apis/sign_api/models/auth/common_auth_models.dart'; +import 'package:web3modal_flutter/services/siwe_service/models/w3m_siwe.dart'; +import 'package:web3modal_flutter/services/w3m_service/models/w3m_session.dart'; + +abstract class ISiweService { + SIWEConfig? get config; + + bool get enabled; + bool get signOutOnDisconnect; + bool get signOutOnAccountChange; + bool get signOutOnNetworkChange; + int get nonceRefetchIntervalMs; + int get sessionRefetchIntervalMs; + + Future getNonce(); + + Future createMessage({ + required String chainId, + required String address, + }); + + Future signMessageRequest( + String message, { + required W3MSession session, + }); + + Future verifyMessage({ + required String message, + required String signature, + Cacao? cacao, + String? clientId, + }); + + Future getSession(); + + Future signOut(); + + String formatMessage(SIWECreateMessageArgs params); +} diff --git a/lib/services/siwe_service/models/w3m_siwe.dart b/lib/services/siwe_service/models/w3m_siwe.dart new file mode 100644 index 00000000..11f0e270 --- /dev/null +++ b/lib/services/siwe_service/models/w3m_siwe.dart @@ -0,0 +1,163 @@ +import 'package:flutter/material.dart'; +import 'package:freezed_annotation/freezed_annotation.dart'; + +import 'package:walletconnect_flutter_v2/walletconnect_flutter_v2.dart'; + +part 'w3m_siwe.g.dart'; +part 'w3m_siwe.freezed.dart'; + +// TODO work to do +// context parameter +// disconnect when closing modal +// siwe with email +class SIWEConfig { + final Future Function() getNonce; + final Future Function() getMessageParams; + final String Function(SIWECreateMessageArgs args) createMessage; + final Future Function(SIWEVerifyMessageArgs args) verifyMessage; + final Future Function() getSession; + final Future Function() signOut; + // Callback when user signs in + final Function(SIWESession session)? onSignIn; + // Callback when user signs out + final VoidCallback? onSignOut; + // Defaults to true + final bool enabled; + // In milliseconds, defaults to 5 minutes + final int nonceRefetchIntervalMs; + // In milliseconds, defaults to 5 minutes + final int sessionRefetchIntervalMs; + // Defaults to true + final bool signOutOnDisconnect; + // Defaults to true + final bool signOutOnAccountChange; + // Defaults to true + final bool signOutOnNetworkChange; + // + final BuildContext context; + + SIWEConfig({ + required this.context, + required this.getNonce, + required this.getMessageParams, + required this.createMessage, + required this.verifyMessage, + required this.getSession, + required this.signOut, + this.onSignIn, + this.onSignOut, + this.enabled = true, + this.signOutOnDisconnect = true, + this.signOutOnAccountChange = true, + this.signOutOnNetworkChange = true, + this.nonceRefetchIntervalMs = 300000, + this.sessionRefetchIntervalMs = 300000, + }); +} + +@freezed +class SIWECreateMessageArgs with _$SIWECreateMessageArgs { + const factory SIWECreateMessageArgs({ + required String chainId, + required String domain, + required String nonce, + required String uri, + required String address, + @Default('1') String version, + @Default(CacaoHeader(t: 'eip4361')) CacaoHeader? type, + String? nbf, + String? exp, + String? statement, + String? requestId, + List? resources, + int? expiry, + String? iat, + }) = _SIWECreateMessageArgs; + + factory SIWECreateMessageArgs.fromSIWEMessageArgs( + SIWEMessageArgs params, { + required String address, + required String chainId, + required String nonce, + required CacaoHeader type, + }) { + final now = DateTime.now(); + return SIWECreateMessageArgs( + chainId: chainId, + nonce: nonce, + address: address, + version: '1', + iat: params.iat ?? + DateTime.utc( + now.year, + now.month, + now.day, + now.hour, + now.minute, + now.second, + now.millisecond, + ).toIso8601String(), + domain: params.domain, + uri: params.uri, + type: type, + nbf: params.nbf, + exp: params.exp, + statement: params.statement, + requestId: params.requestId, + resources: params.resources, + expiry: params.expiry, + ); + } + + factory SIWECreateMessageArgs.fromJson(Map json) => + _$SIWECreateMessageArgsFromJson(json); +} + +@freezed +class SIWEMessageArgs with _$SIWEMessageArgs { + @JsonSerializable(includeIfNull: false) + const factory SIWEMessageArgs({ + required String domain, + required String uri, + @Default(CacaoHeader(t: 'eip4361')) CacaoHeader? type, + String? nbf, + String? exp, + String? statement, + String? requestId, + List? resources, + int? expiry, + String? iat, + List? methods, + }) = _SIWEMessageArgs; + + factory SIWEMessageArgs.fromJson(Map json) => + _$SIWEMessageArgsFromJson(json); +} + +@freezed +class SIWEVerifyMessageArgs with _$SIWEVerifyMessageArgs { + @JsonSerializable(includeIfNull: false) + const factory SIWEVerifyMessageArgs({ + required String message, + required String signature, + Cacao? cacao, // for One-Click Auth + String? clientId, // Not really used in mobile platforms + }) = _SIWEVerifyMessageArgs; + + factory SIWEVerifyMessageArgs.fromJson(Map json) => + _$SIWEVerifyMessageArgsFromJson(json); +} + +@freezed +class SIWESession with _$SIWESession { + const factory SIWESession({ + required String address, + required List chains, + }) = _SIWESession; + + factory SIWESession.fromJson(Map json) => + _$SIWESessionFromJson(json); + + @override + String toString() => 'SIWESession($address, $chains)'; +} diff --git a/lib/services/siwe_service/models/w3m_siwe.freezed.dart b/lib/services/siwe_service/models/w3m_siwe.freezed.dart new file mode 100644 index 00000000..5243cb04 --- /dev/null +++ b/lib/services/siwe_service/models/w3m_siwe.freezed.dart @@ -0,0 +1,1234 @@ +// coverage:ignore-file +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark + +part of 'w3m_siwe.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +T _$identity(T value) => value; + +final _privateConstructorUsedError = UnsupportedError( + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); + +SIWECreateMessageArgs _$SIWECreateMessageArgsFromJson( + Map json) { + return _SIWECreateMessageArgs.fromJson(json); +} + +/// @nodoc +mixin _$SIWECreateMessageArgs { + String get chainId => throw _privateConstructorUsedError; + String get domain => throw _privateConstructorUsedError; + String get nonce => throw _privateConstructorUsedError; + String get uri => throw _privateConstructorUsedError; + String get address => throw _privateConstructorUsedError; + String get version => throw _privateConstructorUsedError; + CacaoHeader? get type => throw _privateConstructorUsedError; + String? get nbf => throw _privateConstructorUsedError; + String? get exp => throw _privateConstructorUsedError; + String? get statement => throw _privateConstructorUsedError; + String? get requestId => throw _privateConstructorUsedError; + List? get resources => throw _privateConstructorUsedError; + int? get expiry => throw _privateConstructorUsedError; + String? get iat => throw _privateConstructorUsedError; + + Map toJson() => throw _privateConstructorUsedError; + @JsonKey(ignore: true) + $SIWECreateMessageArgsCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $SIWECreateMessageArgsCopyWith<$Res> { + factory $SIWECreateMessageArgsCopyWith(SIWECreateMessageArgs value, + $Res Function(SIWECreateMessageArgs) then) = + _$SIWECreateMessageArgsCopyWithImpl<$Res, SIWECreateMessageArgs>; + @useResult + $Res call( + {String chainId, + String domain, + String nonce, + String uri, + String address, + String version, + CacaoHeader? type, + String? nbf, + String? exp, + String? statement, + String? requestId, + List? resources, + int? expiry, + String? iat}); + + $CacaoHeaderCopyWith<$Res>? get type; +} + +/// @nodoc +class _$SIWECreateMessageArgsCopyWithImpl<$Res, + $Val extends SIWECreateMessageArgs> + implements $SIWECreateMessageArgsCopyWith<$Res> { + _$SIWECreateMessageArgsCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? chainId = null, + Object? domain = null, + Object? nonce = null, + Object? uri = null, + Object? address = null, + Object? version = null, + Object? type = freezed, + Object? nbf = freezed, + Object? exp = freezed, + Object? statement = freezed, + Object? requestId = freezed, + Object? resources = freezed, + Object? expiry = freezed, + Object? iat = freezed, + }) { + return _then(_value.copyWith( + chainId: null == chainId + ? _value.chainId + : chainId // ignore: cast_nullable_to_non_nullable + as String, + domain: null == domain + ? _value.domain + : domain // ignore: cast_nullable_to_non_nullable + as String, + nonce: null == nonce + ? _value.nonce + : nonce // ignore: cast_nullable_to_non_nullable + as String, + uri: null == uri + ? _value.uri + : uri // ignore: cast_nullable_to_non_nullable + as String, + address: null == address + ? _value.address + : address // ignore: cast_nullable_to_non_nullable + as String, + version: null == version + ? _value.version + : version // ignore: cast_nullable_to_non_nullable + as String, + type: freezed == type + ? _value.type + : type // ignore: cast_nullable_to_non_nullable + as CacaoHeader?, + nbf: freezed == nbf + ? _value.nbf + : nbf // ignore: cast_nullable_to_non_nullable + as String?, + exp: freezed == exp + ? _value.exp + : exp // ignore: cast_nullable_to_non_nullable + as String?, + statement: freezed == statement + ? _value.statement + : statement // ignore: cast_nullable_to_non_nullable + as String?, + requestId: freezed == requestId + ? _value.requestId + : requestId // ignore: cast_nullable_to_non_nullable + as String?, + resources: freezed == resources + ? _value.resources + : resources // ignore: cast_nullable_to_non_nullable + as List?, + expiry: freezed == expiry + ? _value.expiry + : expiry // ignore: cast_nullable_to_non_nullable + as int?, + iat: freezed == iat + ? _value.iat + : iat // ignore: cast_nullable_to_non_nullable + as String?, + ) as $Val); + } + + @override + @pragma('vm:prefer-inline') + $CacaoHeaderCopyWith<$Res>? get type { + if (_value.type == null) { + return null; + } + + return $CacaoHeaderCopyWith<$Res>(_value.type!, (value) { + return _then(_value.copyWith(type: value) as $Val); + }); + } +} + +/// @nodoc +abstract class _$$SIWECreateMessageArgsImplCopyWith<$Res> + implements $SIWECreateMessageArgsCopyWith<$Res> { + factory _$$SIWECreateMessageArgsImplCopyWith( + _$SIWECreateMessageArgsImpl value, + $Res Function(_$SIWECreateMessageArgsImpl) then) = + __$$SIWECreateMessageArgsImplCopyWithImpl<$Res>; + @override + @useResult + $Res call( + {String chainId, + String domain, + String nonce, + String uri, + String address, + String version, + CacaoHeader? type, + String? nbf, + String? exp, + String? statement, + String? requestId, + List? resources, + int? expiry, + String? iat}); + + @override + $CacaoHeaderCopyWith<$Res>? get type; +} + +/// @nodoc +class __$$SIWECreateMessageArgsImplCopyWithImpl<$Res> + extends _$SIWECreateMessageArgsCopyWithImpl<$Res, + _$SIWECreateMessageArgsImpl> + implements _$$SIWECreateMessageArgsImplCopyWith<$Res> { + __$$SIWECreateMessageArgsImplCopyWithImpl(_$SIWECreateMessageArgsImpl _value, + $Res Function(_$SIWECreateMessageArgsImpl) _then) + : super(_value, _then); + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? chainId = null, + Object? domain = null, + Object? nonce = null, + Object? uri = null, + Object? address = null, + Object? version = null, + Object? type = freezed, + Object? nbf = freezed, + Object? exp = freezed, + Object? statement = freezed, + Object? requestId = freezed, + Object? resources = freezed, + Object? expiry = freezed, + Object? iat = freezed, + }) { + return _then(_$SIWECreateMessageArgsImpl( + chainId: null == chainId + ? _value.chainId + : chainId // ignore: cast_nullable_to_non_nullable + as String, + domain: null == domain + ? _value.domain + : domain // ignore: cast_nullable_to_non_nullable + as String, + nonce: null == nonce + ? _value.nonce + : nonce // ignore: cast_nullable_to_non_nullable + as String, + uri: null == uri + ? _value.uri + : uri // ignore: cast_nullable_to_non_nullable + as String, + address: null == address + ? _value.address + : address // ignore: cast_nullable_to_non_nullable + as String, + version: null == version + ? _value.version + : version // ignore: cast_nullable_to_non_nullable + as String, + type: freezed == type + ? _value.type + : type // ignore: cast_nullable_to_non_nullable + as CacaoHeader?, + nbf: freezed == nbf + ? _value.nbf + : nbf // ignore: cast_nullable_to_non_nullable + as String?, + exp: freezed == exp + ? _value.exp + : exp // ignore: cast_nullable_to_non_nullable + as String?, + statement: freezed == statement + ? _value.statement + : statement // ignore: cast_nullable_to_non_nullable + as String?, + requestId: freezed == requestId + ? _value.requestId + : requestId // ignore: cast_nullable_to_non_nullable + as String?, + resources: freezed == resources + ? _value._resources + : resources // ignore: cast_nullable_to_non_nullable + as List?, + expiry: freezed == expiry + ? _value.expiry + : expiry // ignore: cast_nullable_to_non_nullable + as int?, + iat: freezed == iat + ? _value.iat + : iat // ignore: cast_nullable_to_non_nullable + as String?, + )); + } +} + +/// @nodoc +@JsonSerializable() +class _$SIWECreateMessageArgsImpl implements _SIWECreateMessageArgs { + const _$SIWECreateMessageArgsImpl( + {required this.chainId, + required this.domain, + required this.nonce, + required this.uri, + required this.address, + this.version = '1', + this.type = const CacaoHeader(t: 'eip4361'), + this.nbf, + this.exp, + this.statement, + this.requestId, + final List? resources, + this.expiry, + this.iat}) + : _resources = resources; + + factory _$SIWECreateMessageArgsImpl.fromJson(Map json) => + _$$SIWECreateMessageArgsImplFromJson(json); + + @override + final String chainId; + @override + final String domain; + @override + final String nonce; + @override + final String uri; + @override + final String address; + @override + @JsonKey() + final String version; + @override + @JsonKey() + final CacaoHeader? type; + @override + final String? nbf; + @override + final String? exp; + @override + final String? statement; + @override + final String? requestId; + final List? _resources; + @override + List? get resources { + final value = _resources; + if (value == null) return null; + if (_resources is EqualUnmodifiableListView) return _resources; + // ignore: implicit_dynamic_type + return EqualUnmodifiableListView(value); + } + + @override + final int? expiry; + @override + final String? iat; + + @override + String toString() { + return 'SIWECreateMessageArgs(chainId: $chainId, domain: $domain, nonce: $nonce, uri: $uri, address: $address, version: $version, type: $type, nbf: $nbf, exp: $exp, statement: $statement, requestId: $requestId, resources: $resources, expiry: $expiry, iat: $iat)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$SIWECreateMessageArgsImpl && + (identical(other.chainId, chainId) || other.chainId == chainId) && + (identical(other.domain, domain) || other.domain == domain) && + (identical(other.nonce, nonce) || other.nonce == nonce) && + (identical(other.uri, uri) || other.uri == uri) && + (identical(other.address, address) || other.address == address) && + (identical(other.version, version) || other.version == version) && + (identical(other.type, type) || other.type == type) && + (identical(other.nbf, nbf) || other.nbf == nbf) && + (identical(other.exp, exp) || other.exp == exp) && + (identical(other.statement, statement) || + other.statement == statement) && + (identical(other.requestId, requestId) || + other.requestId == requestId) && + const DeepCollectionEquality() + .equals(other._resources, _resources) && + (identical(other.expiry, expiry) || other.expiry == expiry) && + (identical(other.iat, iat) || other.iat == iat)); + } + + @JsonKey(ignore: true) + @override + int get hashCode => Object.hash( + runtimeType, + chainId, + domain, + nonce, + uri, + address, + version, + type, + nbf, + exp, + statement, + requestId, + const DeepCollectionEquality().hash(_resources), + expiry, + iat); + + @JsonKey(ignore: true) + @override + @pragma('vm:prefer-inline') + _$$SIWECreateMessageArgsImplCopyWith<_$SIWECreateMessageArgsImpl> + get copyWith => __$$SIWECreateMessageArgsImplCopyWithImpl< + _$SIWECreateMessageArgsImpl>(this, _$identity); + + @override + Map toJson() { + return _$$SIWECreateMessageArgsImplToJson( + this, + ); + } +} + +abstract class _SIWECreateMessageArgs implements SIWECreateMessageArgs { + const factory _SIWECreateMessageArgs( + {required final String chainId, + required final String domain, + required final String nonce, + required final String uri, + required final String address, + final String version, + final CacaoHeader? type, + final String? nbf, + final String? exp, + final String? statement, + final String? requestId, + final List? resources, + final int? expiry, + final String? iat}) = _$SIWECreateMessageArgsImpl; + + factory _SIWECreateMessageArgs.fromJson(Map json) = + _$SIWECreateMessageArgsImpl.fromJson; + + @override + String get chainId; + @override + String get domain; + @override + String get nonce; + @override + String get uri; + @override + String get address; + @override + String get version; + @override + CacaoHeader? get type; + @override + String? get nbf; + @override + String? get exp; + @override + String? get statement; + @override + String? get requestId; + @override + List? get resources; + @override + int? get expiry; + @override + String? get iat; + @override + @JsonKey(ignore: true) + _$$SIWECreateMessageArgsImplCopyWith<_$SIWECreateMessageArgsImpl> + get copyWith => throw _privateConstructorUsedError; +} + +SIWEMessageArgs _$SIWEMessageArgsFromJson(Map json) { + return _SIWEMessageArgs.fromJson(json); +} + +/// @nodoc +mixin _$SIWEMessageArgs { + String get domain => throw _privateConstructorUsedError; + String get uri => throw _privateConstructorUsedError; + CacaoHeader? get type => throw _privateConstructorUsedError; + String? get nbf => throw _privateConstructorUsedError; + String? get exp => throw _privateConstructorUsedError; + String? get statement => throw _privateConstructorUsedError; + String? get requestId => throw _privateConstructorUsedError; + List? get resources => throw _privateConstructorUsedError; + int? get expiry => throw _privateConstructorUsedError; + String? get iat => throw _privateConstructorUsedError; + List? get methods => throw _privateConstructorUsedError; + + Map toJson() => throw _privateConstructorUsedError; + @JsonKey(ignore: true) + $SIWEMessageArgsCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $SIWEMessageArgsCopyWith<$Res> { + factory $SIWEMessageArgsCopyWith( + SIWEMessageArgs value, $Res Function(SIWEMessageArgs) then) = + _$SIWEMessageArgsCopyWithImpl<$Res, SIWEMessageArgs>; + @useResult + $Res call( + {String domain, + String uri, + CacaoHeader? type, + String? nbf, + String? exp, + String? statement, + String? requestId, + List? resources, + int? expiry, + String? iat, + List? methods}); + + $CacaoHeaderCopyWith<$Res>? get type; +} + +/// @nodoc +class _$SIWEMessageArgsCopyWithImpl<$Res, $Val extends SIWEMessageArgs> + implements $SIWEMessageArgsCopyWith<$Res> { + _$SIWEMessageArgsCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? domain = null, + Object? uri = null, + Object? type = freezed, + Object? nbf = freezed, + Object? exp = freezed, + Object? statement = freezed, + Object? requestId = freezed, + Object? resources = freezed, + Object? expiry = freezed, + Object? iat = freezed, + Object? methods = freezed, + }) { + return _then(_value.copyWith( + domain: null == domain + ? _value.domain + : domain // ignore: cast_nullable_to_non_nullable + as String, + uri: null == uri + ? _value.uri + : uri // ignore: cast_nullable_to_non_nullable + as String, + type: freezed == type + ? _value.type + : type // ignore: cast_nullable_to_non_nullable + as CacaoHeader?, + nbf: freezed == nbf + ? _value.nbf + : nbf // ignore: cast_nullable_to_non_nullable + as String?, + exp: freezed == exp + ? _value.exp + : exp // ignore: cast_nullable_to_non_nullable + as String?, + statement: freezed == statement + ? _value.statement + : statement // ignore: cast_nullable_to_non_nullable + as String?, + requestId: freezed == requestId + ? _value.requestId + : requestId // ignore: cast_nullable_to_non_nullable + as String?, + resources: freezed == resources + ? _value.resources + : resources // ignore: cast_nullable_to_non_nullable + as List?, + expiry: freezed == expiry + ? _value.expiry + : expiry // ignore: cast_nullable_to_non_nullable + as int?, + iat: freezed == iat + ? _value.iat + : iat // ignore: cast_nullable_to_non_nullable + as String?, + methods: freezed == methods + ? _value.methods + : methods // ignore: cast_nullable_to_non_nullable + as List?, + ) as $Val); + } + + @override + @pragma('vm:prefer-inline') + $CacaoHeaderCopyWith<$Res>? get type { + if (_value.type == null) { + return null; + } + + return $CacaoHeaderCopyWith<$Res>(_value.type!, (value) { + return _then(_value.copyWith(type: value) as $Val); + }); + } +} + +/// @nodoc +abstract class _$$SIWEMessageArgsImplCopyWith<$Res> + implements $SIWEMessageArgsCopyWith<$Res> { + factory _$$SIWEMessageArgsImplCopyWith(_$SIWEMessageArgsImpl value, + $Res Function(_$SIWEMessageArgsImpl) then) = + __$$SIWEMessageArgsImplCopyWithImpl<$Res>; + @override + @useResult + $Res call( + {String domain, + String uri, + CacaoHeader? type, + String? nbf, + String? exp, + String? statement, + String? requestId, + List? resources, + int? expiry, + String? iat, + List? methods}); + + @override + $CacaoHeaderCopyWith<$Res>? get type; +} + +/// @nodoc +class __$$SIWEMessageArgsImplCopyWithImpl<$Res> + extends _$SIWEMessageArgsCopyWithImpl<$Res, _$SIWEMessageArgsImpl> + implements _$$SIWEMessageArgsImplCopyWith<$Res> { + __$$SIWEMessageArgsImplCopyWithImpl( + _$SIWEMessageArgsImpl _value, $Res Function(_$SIWEMessageArgsImpl) _then) + : super(_value, _then); + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? domain = null, + Object? uri = null, + Object? type = freezed, + Object? nbf = freezed, + Object? exp = freezed, + Object? statement = freezed, + Object? requestId = freezed, + Object? resources = freezed, + Object? expiry = freezed, + Object? iat = freezed, + Object? methods = freezed, + }) { + return _then(_$SIWEMessageArgsImpl( + domain: null == domain + ? _value.domain + : domain // ignore: cast_nullable_to_non_nullable + as String, + uri: null == uri + ? _value.uri + : uri // ignore: cast_nullable_to_non_nullable + as String, + type: freezed == type + ? _value.type + : type // ignore: cast_nullable_to_non_nullable + as CacaoHeader?, + nbf: freezed == nbf + ? _value.nbf + : nbf // ignore: cast_nullable_to_non_nullable + as String?, + exp: freezed == exp + ? _value.exp + : exp // ignore: cast_nullable_to_non_nullable + as String?, + statement: freezed == statement + ? _value.statement + : statement // ignore: cast_nullable_to_non_nullable + as String?, + requestId: freezed == requestId + ? _value.requestId + : requestId // ignore: cast_nullable_to_non_nullable + as String?, + resources: freezed == resources + ? _value._resources + : resources // ignore: cast_nullable_to_non_nullable + as List?, + expiry: freezed == expiry + ? _value.expiry + : expiry // ignore: cast_nullable_to_non_nullable + as int?, + iat: freezed == iat + ? _value.iat + : iat // ignore: cast_nullable_to_non_nullable + as String?, + methods: freezed == methods + ? _value._methods + : methods // ignore: cast_nullable_to_non_nullable + as List?, + )); + } +} + +/// @nodoc + +@JsonSerializable(includeIfNull: false) +class _$SIWEMessageArgsImpl implements _SIWEMessageArgs { + const _$SIWEMessageArgsImpl( + {required this.domain, + required this.uri, + this.type = const CacaoHeader(t: 'eip4361'), + this.nbf, + this.exp, + this.statement, + this.requestId, + final List? resources, + this.expiry, + this.iat, + final List? methods}) + : _resources = resources, + _methods = methods; + + factory _$SIWEMessageArgsImpl.fromJson(Map json) => + _$$SIWEMessageArgsImplFromJson(json); + + @override + final String domain; + @override + final String uri; + @override + @JsonKey() + final CacaoHeader? type; + @override + final String? nbf; + @override + final String? exp; + @override + final String? statement; + @override + final String? requestId; + final List? _resources; + @override + List? get resources { + final value = _resources; + if (value == null) return null; + if (_resources is EqualUnmodifiableListView) return _resources; + // ignore: implicit_dynamic_type + return EqualUnmodifiableListView(value); + } + + @override + final int? expiry; + @override + final String? iat; + final List? _methods; + @override + List? get methods { + final value = _methods; + if (value == null) return null; + if (_methods is EqualUnmodifiableListView) return _methods; + // ignore: implicit_dynamic_type + return EqualUnmodifiableListView(value); + } + + @override + String toString() { + return 'SIWEMessageArgs(domain: $domain, uri: $uri, type: $type, nbf: $nbf, exp: $exp, statement: $statement, requestId: $requestId, resources: $resources, expiry: $expiry, iat: $iat, methods: $methods)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$SIWEMessageArgsImpl && + (identical(other.domain, domain) || other.domain == domain) && + (identical(other.uri, uri) || other.uri == uri) && + (identical(other.type, type) || other.type == type) && + (identical(other.nbf, nbf) || other.nbf == nbf) && + (identical(other.exp, exp) || other.exp == exp) && + (identical(other.statement, statement) || + other.statement == statement) && + (identical(other.requestId, requestId) || + other.requestId == requestId) && + const DeepCollectionEquality() + .equals(other._resources, _resources) && + (identical(other.expiry, expiry) || other.expiry == expiry) && + (identical(other.iat, iat) || other.iat == iat) && + const DeepCollectionEquality().equals(other._methods, _methods)); + } + + @JsonKey(ignore: true) + @override + int get hashCode => Object.hash( + runtimeType, + domain, + uri, + type, + nbf, + exp, + statement, + requestId, + const DeepCollectionEquality().hash(_resources), + expiry, + iat, + const DeepCollectionEquality().hash(_methods)); + + @JsonKey(ignore: true) + @override + @pragma('vm:prefer-inline') + _$$SIWEMessageArgsImplCopyWith<_$SIWEMessageArgsImpl> get copyWith => + __$$SIWEMessageArgsImplCopyWithImpl<_$SIWEMessageArgsImpl>( + this, _$identity); + + @override + Map toJson() { + return _$$SIWEMessageArgsImplToJson( + this, + ); + } +} + +abstract class _SIWEMessageArgs implements SIWEMessageArgs { + const factory _SIWEMessageArgs( + {required final String domain, + required final String uri, + final CacaoHeader? type, + final String? nbf, + final String? exp, + final String? statement, + final String? requestId, + final List? resources, + final int? expiry, + final String? iat, + final List? methods}) = _$SIWEMessageArgsImpl; + + factory _SIWEMessageArgs.fromJson(Map json) = + _$SIWEMessageArgsImpl.fromJson; + + @override + String get domain; + @override + String get uri; + @override + CacaoHeader? get type; + @override + String? get nbf; + @override + String? get exp; + @override + String? get statement; + @override + String? get requestId; + @override + List? get resources; + @override + int? get expiry; + @override + String? get iat; + @override + List? get methods; + @override + @JsonKey(ignore: true) + _$$SIWEMessageArgsImplCopyWith<_$SIWEMessageArgsImpl> get copyWith => + throw _privateConstructorUsedError; +} + +SIWEVerifyMessageArgs _$SIWEVerifyMessageArgsFromJson( + Map json) { + return _SIWEVerifyMessageArgs.fromJson(json); +} + +/// @nodoc +mixin _$SIWEVerifyMessageArgs { + String get message => throw _privateConstructorUsedError; + String get signature => throw _privateConstructorUsedError; + Cacao? get cacao => throw _privateConstructorUsedError; // for One-Click Auth + String? get clientId => throw _privateConstructorUsedError; + + Map toJson() => throw _privateConstructorUsedError; + @JsonKey(ignore: true) + $SIWEVerifyMessageArgsCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $SIWEVerifyMessageArgsCopyWith<$Res> { + factory $SIWEVerifyMessageArgsCopyWith(SIWEVerifyMessageArgs value, + $Res Function(SIWEVerifyMessageArgs) then) = + _$SIWEVerifyMessageArgsCopyWithImpl<$Res, SIWEVerifyMessageArgs>; + @useResult + $Res call({String message, String signature, Cacao? cacao, String? clientId}); + + $CacaoCopyWith<$Res>? get cacao; +} + +/// @nodoc +class _$SIWEVerifyMessageArgsCopyWithImpl<$Res, + $Val extends SIWEVerifyMessageArgs> + implements $SIWEVerifyMessageArgsCopyWith<$Res> { + _$SIWEVerifyMessageArgsCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? message = null, + Object? signature = null, + Object? cacao = freezed, + Object? clientId = freezed, + }) { + return _then(_value.copyWith( + message: null == message + ? _value.message + : message // ignore: cast_nullable_to_non_nullable + as String, + signature: null == signature + ? _value.signature + : signature // ignore: cast_nullable_to_non_nullable + as String, + cacao: freezed == cacao + ? _value.cacao + : cacao // ignore: cast_nullable_to_non_nullable + as Cacao?, + clientId: freezed == clientId + ? _value.clientId + : clientId // ignore: cast_nullable_to_non_nullable + as String?, + ) as $Val); + } + + @override + @pragma('vm:prefer-inline') + $CacaoCopyWith<$Res>? get cacao { + if (_value.cacao == null) { + return null; + } + + return $CacaoCopyWith<$Res>(_value.cacao!, (value) { + return _then(_value.copyWith(cacao: value) as $Val); + }); + } +} + +/// @nodoc +abstract class _$$SIWEVerifyMessageArgsImplCopyWith<$Res> + implements $SIWEVerifyMessageArgsCopyWith<$Res> { + factory _$$SIWEVerifyMessageArgsImplCopyWith( + _$SIWEVerifyMessageArgsImpl value, + $Res Function(_$SIWEVerifyMessageArgsImpl) then) = + __$$SIWEVerifyMessageArgsImplCopyWithImpl<$Res>; + @override + @useResult + $Res call({String message, String signature, Cacao? cacao, String? clientId}); + + @override + $CacaoCopyWith<$Res>? get cacao; +} + +/// @nodoc +class __$$SIWEVerifyMessageArgsImplCopyWithImpl<$Res> + extends _$SIWEVerifyMessageArgsCopyWithImpl<$Res, + _$SIWEVerifyMessageArgsImpl> + implements _$$SIWEVerifyMessageArgsImplCopyWith<$Res> { + __$$SIWEVerifyMessageArgsImplCopyWithImpl(_$SIWEVerifyMessageArgsImpl _value, + $Res Function(_$SIWEVerifyMessageArgsImpl) _then) + : super(_value, _then); + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? message = null, + Object? signature = null, + Object? cacao = freezed, + Object? clientId = freezed, + }) { + return _then(_$SIWEVerifyMessageArgsImpl( + message: null == message + ? _value.message + : message // ignore: cast_nullable_to_non_nullable + as String, + signature: null == signature + ? _value.signature + : signature // ignore: cast_nullable_to_non_nullable + as String, + cacao: freezed == cacao + ? _value.cacao + : cacao // ignore: cast_nullable_to_non_nullable + as Cacao?, + clientId: freezed == clientId + ? _value.clientId + : clientId // ignore: cast_nullable_to_non_nullable + as String?, + )); + } +} + +/// @nodoc + +@JsonSerializable(includeIfNull: false) +class _$SIWEVerifyMessageArgsImpl implements _SIWEVerifyMessageArgs { + const _$SIWEVerifyMessageArgsImpl( + {required this.message, + required this.signature, + this.cacao, + this.clientId}); + + factory _$SIWEVerifyMessageArgsImpl.fromJson(Map json) => + _$$SIWEVerifyMessageArgsImplFromJson(json); + + @override + final String message; + @override + final String signature; + @override + final Cacao? cacao; +// for One-Click Auth + @override + final String? clientId; + + @override + String toString() { + return 'SIWEVerifyMessageArgs(message: $message, signature: $signature, cacao: $cacao, clientId: $clientId)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$SIWEVerifyMessageArgsImpl && + (identical(other.message, message) || other.message == message) && + (identical(other.signature, signature) || + other.signature == signature) && + (identical(other.cacao, cacao) || other.cacao == cacao) && + (identical(other.clientId, clientId) || + other.clientId == clientId)); + } + + @JsonKey(ignore: true) + @override + int get hashCode => + Object.hash(runtimeType, message, signature, cacao, clientId); + + @JsonKey(ignore: true) + @override + @pragma('vm:prefer-inline') + _$$SIWEVerifyMessageArgsImplCopyWith<_$SIWEVerifyMessageArgsImpl> + get copyWith => __$$SIWEVerifyMessageArgsImplCopyWithImpl< + _$SIWEVerifyMessageArgsImpl>(this, _$identity); + + @override + Map toJson() { + return _$$SIWEVerifyMessageArgsImplToJson( + this, + ); + } +} + +abstract class _SIWEVerifyMessageArgs implements SIWEVerifyMessageArgs { + const factory _SIWEVerifyMessageArgs( + {required final String message, + required final String signature, + final Cacao? cacao, + final String? clientId}) = _$SIWEVerifyMessageArgsImpl; + + factory _SIWEVerifyMessageArgs.fromJson(Map json) = + _$SIWEVerifyMessageArgsImpl.fromJson; + + @override + String get message; + @override + String get signature; + @override + Cacao? get cacao; + @override // for One-Click Auth + String? get clientId; + @override + @JsonKey(ignore: true) + _$$SIWEVerifyMessageArgsImplCopyWith<_$SIWEVerifyMessageArgsImpl> + get copyWith => throw _privateConstructorUsedError; +} + +SIWESession _$SIWESessionFromJson(Map json) { + return _SIWESession.fromJson(json); +} + +/// @nodoc +mixin _$SIWESession { + String get address => throw _privateConstructorUsedError; + List get chains => throw _privateConstructorUsedError; + + Map toJson() => throw _privateConstructorUsedError; + @JsonKey(ignore: true) + $SIWESessionCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $SIWESessionCopyWith<$Res> { + factory $SIWESessionCopyWith( + SIWESession value, $Res Function(SIWESession) then) = + _$SIWESessionCopyWithImpl<$Res, SIWESession>; + @useResult + $Res call({String address, List chains}); +} + +/// @nodoc +class _$SIWESessionCopyWithImpl<$Res, $Val extends SIWESession> + implements $SIWESessionCopyWith<$Res> { + _$SIWESessionCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? address = null, + Object? chains = null, + }) { + return _then(_value.copyWith( + address: null == address + ? _value.address + : address // ignore: cast_nullable_to_non_nullable + as String, + chains: null == chains + ? _value.chains + : chains // ignore: cast_nullable_to_non_nullable + as List, + ) as $Val); + } +} + +/// @nodoc +abstract class _$$SIWESessionImplCopyWith<$Res> + implements $SIWESessionCopyWith<$Res> { + factory _$$SIWESessionImplCopyWith( + _$SIWESessionImpl value, $Res Function(_$SIWESessionImpl) then) = + __$$SIWESessionImplCopyWithImpl<$Res>; + @override + @useResult + $Res call({String address, List chains}); +} + +/// @nodoc +class __$$SIWESessionImplCopyWithImpl<$Res> + extends _$SIWESessionCopyWithImpl<$Res, _$SIWESessionImpl> + implements _$$SIWESessionImplCopyWith<$Res> { + __$$SIWESessionImplCopyWithImpl( + _$SIWESessionImpl _value, $Res Function(_$SIWESessionImpl) _then) + : super(_value, _then); + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? address = null, + Object? chains = null, + }) { + return _then(_$SIWESessionImpl( + address: null == address + ? _value.address + : address // ignore: cast_nullable_to_non_nullable + as String, + chains: null == chains + ? _value._chains + : chains // ignore: cast_nullable_to_non_nullable + as List, + )); + } +} + +/// @nodoc +@JsonSerializable() +class _$SIWESessionImpl implements _SIWESession { + const _$SIWESessionImpl( + {required this.address, required final List chains}) + : _chains = chains; + + factory _$SIWESessionImpl.fromJson(Map json) => + _$$SIWESessionImplFromJson(json); + + @override + final String address; + final List _chains; + @override + List get chains { + if (_chains is EqualUnmodifiableListView) return _chains; + // ignore: implicit_dynamic_type + return EqualUnmodifiableListView(_chains); + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$SIWESessionImpl && + (identical(other.address, address) || other.address == address) && + const DeepCollectionEquality().equals(other._chains, _chains)); + } + + @JsonKey(ignore: true) + @override + int get hashCode => Object.hash( + runtimeType, address, const DeepCollectionEquality().hash(_chains)); + + @JsonKey(ignore: true) + @override + @pragma('vm:prefer-inline') + _$$SIWESessionImplCopyWith<_$SIWESessionImpl> get copyWith => + __$$SIWESessionImplCopyWithImpl<_$SIWESessionImpl>(this, _$identity); + + @override + Map toJson() { + return _$$SIWESessionImplToJson( + this, + ); + } +} + +abstract class _SIWESession implements SIWESession { + const factory _SIWESession( + {required final String address, + required final List chains}) = _$SIWESessionImpl; + + factory _SIWESession.fromJson(Map json) = + _$SIWESessionImpl.fromJson; + + @override + String get address; + @override + List get chains; + @override + @JsonKey(ignore: true) + _$$SIWESessionImplCopyWith<_$SIWESessionImpl> get copyWith => + throw _privateConstructorUsedError; +} diff --git a/lib/services/siwe_service/models/w3m_siwe.g.dart b/lib/services/siwe_service/models/w3m_siwe.g.dart new file mode 100644 index 00000000..af8b323e --- /dev/null +++ b/lib/services/siwe_service/models/w3m_siwe.g.dart @@ -0,0 +1,139 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +// ignore_for_file: non_constant_identifier_names + +part of 'w3m_siwe.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +_$SIWECreateMessageArgsImpl _$$SIWECreateMessageArgsImplFromJson( + Map json) => + _$SIWECreateMessageArgsImpl( + chainId: json['chainId'] as String, + domain: json['domain'] as String, + nonce: json['nonce'] as String, + uri: json['uri'] as String, + address: json['address'] as String, + version: json['version'] as String? ?? '1', + type: json['type'] == null + ? const CacaoHeader(t: 'eip4361') + : CacaoHeader.fromJson(json['type'] as Map), + nbf: json['nbf'] as String?, + exp: json['exp'] as String?, + statement: json['statement'] as String?, + requestId: json['requestId'] as String?, + resources: (json['resources'] as List?) + ?.map((e) => e as String) + .toList(), + expiry: json['expiry'] as int?, + iat: json['iat'] as String?, + ); + +Map _$$SIWECreateMessageArgsImplToJson( + _$SIWECreateMessageArgsImpl instance) => + { + 'chainId': instance.chainId, + 'domain': instance.domain, + 'nonce': instance.nonce, + 'uri': instance.uri, + 'address': instance.address, + 'version': instance.version, + 'type': instance.type?.toJson(), + 'nbf': instance.nbf, + 'exp': instance.exp, + 'statement': instance.statement, + 'requestId': instance.requestId, + 'resources': instance.resources, + 'expiry': instance.expiry, + 'iat': instance.iat, + }; + +_$SIWEMessageArgsImpl _$$SIWEMessageArgsImplFromJson( + Map json) => + _$SIWEMessageArgsImpl( + domain: json['domain'] as String, + uri: json['uri'] as String, + type: json['type'] == null + ? const CacaoHeader(t: 'eip4361') + : CacaoHeader.fromJson(json['type'] as Map), + nbf: json['nbf'] as String?, + exp: json['exp'] as String?, + statement: json['statement'] as String?, + requestId: json['requestId'] as String?, + resources: (json['resources'] as List?) + ?.map((e) => e as String) + .toList(), + expiry: json['expiry'] as int?, + iat: json['iat'] as String?, + methods: + (json['methods'] as List?)?.map((e) => e as String).toList(), + ); + +Map _$$SIWEMessageArgsImplToJson( + _$SIWEMessageArgsImpl instance) { + final val = { + 'domain': instance.domain, + 'uri': instance.uri, + }; + + void writeNotNull(String key, dynamic value) { + if (value != null) { + val[key] = value; + } + } + + writeNotNull('type', instance.type?.toJson()); + writeNotNull('nbf', instance.nbf); + writeNotNull('exp', instance.exp); + writeNotNull('statement', instance.statement); + writeNotNull('requestId', instance.requestId); + writeNotNull('resources', instance.resources); + writeNotNull('expiry', instance.expiry); + writeNotNull('iat', instance.iat); + writeNotNull('methods', instance.methods); + return val; +} + +_$SIWEVerifyMessageArgsImpl _$$SIWEVerifyMessageArgsImplFromJson( + Map json) => + _$SIWEVerifyMessageArgsImpl( + message: json['message'] as String, + signature: json['signature'] as String, + cacao: json['cacao'] == null + ? null + : Cacao.fromJson(json['cacao'] as Map), + clientId: json['clientId'] as String?, + ); + +Map _$$SIWEVerifyMessageArgsImplToJson( + _$SIWEVerifyMessageArgsImpl instance) { + final val = { + 'message': instance.message, + 'signature': instance.signature, + }; + + void writeNotNull(String key, dynamic value) { + if (value != null) { + val[key] = value; + } + } + + writeNotNull('cacao', instance.cacao?.toJson()); + writeNotNull('clientId', instance.clientId); + return val; +} + +_$SIWESessionImpl _$$SIWESessionImplFromJson(Map json) => + _$SIWESessionImpl( + address: json['address'] as String, + chains: + (json['chains'] as List).map((e) => e as String).toList(), + ); + +Map _$$SIWESessionImplToJson(_$SIWESessionImpl instance) => + { + 'address': instance.address, + 'chains': instance.chains, + }; diff --git a/lib/services/siwe_service/siwe_service.dart b/lib/services/siwe_service/siwe_service.dart new file mode 100644 index 00000000..d2bfb9aa --- /dev/null +++ b/lib/services/siwe_service/siwe_service.dart @@ -0,0 +1,176 @@ +import 'dart:convert'; + +import 'package:convert/convert.dart'; +import 'package:web3modal_flutter/services/coinbase_service/coinbase_service_singleton.dart'; +import 'package:web3modal_flutter/services/magic_service/magic_service_singleton.dart'; +import 'package:web3modal_flutter/services/siwe_service/i_siwe_service.dart'; +import 'package:web3modal_flutter/web3modal_flutter.dart'; + +class SiweService implements ISiweService { + late final SIWEConfig? _siweConfig; + final IWeb3App _web3app; + + SiweService({ + required IWeb3App web3app, + required SIWEConfig? siweConfig, + }) : _web3app = web3app, + _siweConfig = siweConfig; + + @override + SIWEConfig? get config => _siweConfig; + + @override + bool get enabled => _siweConfig?.enabled == true; + + @override + int get nonceRefetchIntervalMs => + _siweConfig?.nonceRefetchIntervalMs ?? 300000; + + @override + int get sessionRefetchIntervalMs => + _siweConfig?.sessionRefetchIntervalMs ?? 300000; + + @override + bool get signOutOnAccountChange => + _siweConfig?.signOutOnAccountChange ?? true; + + @override + bool get signOutOnDisconnect => _siweConfig?.signOutOnDisconnect ?? true; + + @override + bool get signOutOnNetworkChange => + _siweConfig?.signOutOnNetworkChange ?? true; + + @override + Future getNonce() async { + if (!enabled) throw Exception('siweConfig not enabled'); + // + return await _siweConfig!.getNonce(); + } + + @override + Future createMessage({ + required String chainId, + required String address, + }) async { + if (!enabled) throw Exception('siweConfig not enabled'); + // + final nonce = await getNonce(); + final messageParams = await _siweConfig!.getMessageParams(); + // + final createMessageArgs = SIWECreateMessageArgs.fromSIWEMessageArgs( + messageParams, + address: '$chainId:$address', + chainId: chainId, + nonce: nonce, + type: messageParams.type ?? CacaoHeader(t: 'eip4361'), + ); + + return _siweConfig!.createMessage(createMessageArgs); + } + + @override + Future signMessageRequest( + String message, { + required W3MSession session, + }) async { + if (!enabled) throw Exception('siweConfig not enabled'); + // + final chainId = AuthSignature.getChainIdFromMessage(message); + final chain = W3MChainPresets.chains[chainId]!.namespace; + final address = AuthSignature.getAddressFromMessage(message); + final bytes = utf8.encode(message); + final encoded = hex.encode(bytes); + // + if (session.sessionService.isMagic) { + return await magicService.instance.request( + chainId: chain, + request: SessionRequestParams( + method: 'personal_sign', + params: ['0x$encoded', address], + ), + ); + } + if (session.sessionService.isCoinbase) { + return await coinbaseService.instance.request( + chainId: chain, + request: SessionRequestParams( + method: 'personal_sign', + params: ['0x$encoded', address], + ), + ); + } + return await _web3app.request( + topic: session.topic!, + chainId: chain, + request: SessionRequestParams( + method: 'personal_sign', + params: ['0x$encoded', address], + ), + ); + } + + @override + Future verifyMessage({ + required String message, + required String signature, + Cacao? cacao, + String? clientId, + }) async { + if (!enabled) throw Exception('siweConfig not enabled'); + // + final verifyArgs = SIWEVerifyMessageArgs( + message: message, + signature: signature, + cacao: cacao, + clientId: clientId, + ); + final isValid = await _siweConfig!.verifyMessage(verifyArgs); + if (!isValid) { + throw W3MServiceException('Error verifying SIWE signature'); + } + return true; + } + + @override + Future getSession() async { + if (!enabled) throw Exception('siweConfig not enabled'); + // + try { + final siweSession = await _siweConfig!.getSession(); + if (siweSession == null) { + throw W3MServiceException('Error getting SIWE session'); + } + _siweConfig!.onSignIn?.call(siweSession); + + return siweSession; + } catch (e) { + rethrow; + } + } + + @override + Future signOut() async { + if (!enabled) throw Exception('siweConfig not enabled'); + + final success = await _siweConfig!.signOut(); + if (!success) { + throw W3MServiceException('signOut() from siweConfig failed'); + } + _siweConfig!.onSignOut?.call(); + } + + @override + String formatMessage(SIWECreateMessageArgs params) { + final authPayload = SessionAuthPayload.fromJson({ + ...params.toJson(), + 'chains': [params.chainId], + 'aud': params.uri, + 'type': params.type?.t, + }); + return _web3app.formatAuthMessage( + iss: 'did:pkh:${params.address}', + cacaoPayload: CacaoRequestPayload.fromSessionAuthPayload(authPayload), + ); + } +} diff --git a/lib/services/siwe_service/siwe_service_singleton.dart b/lib/services/siwe_service/siwe_service_singleton.dart new file mode 100644 index 00000000..2fc7789d --- /dev/null +++ b/lib/services/siwe_service/siwe_service_singleton.dart @@ -0,0 +1,7 @@ +import 'package:web3modal_flutter/services/siwe_service/i_siwe_service.dart'; + +class SiweServiceSingleton { + ISiweService? instance; +} + +final siweService = SiweServiceSingleton(); diff --git a/lib/services/w3m_service/events/w3m_events.dart b/lib/services/w3m_service/events/w3m_events.dart index 9956f2a5..07200ef4 100644 --- a/lib/services/w3m_service/events/w3m_events.dart +++ b/lib/services/w3m_service/events/w3m_events.dart @@ -50,5 +50,5 @@ class ErrorOpeningWallet extends ModalError { } class UserRejectedConnection extends ModalError { - UserRejectedConnection() : super('User rejected Wallet connection'); + UserRejectedConnection() : super('User rejected connection'); } diff --git a/lib/services/w3m_service/i_w3m_service.dart b/lib/services/w3m_service/i_w3m_service.dart index 2e50c068..3e01ea82 100644 --- a/lib/services/w3m_service/i_w3m_service.dart +++ b/lib/services/w3m_service/i_w3m_service.dart @@ -101,6 +101,8 @@ abstract class IW3MService with ChangeNotifier { Future loadAccountData(); + String formatMessage(SIWECreateMessageArgs params); + /// Disconnects the session and pairing, if any. /// If there is no session, this does nothing. Future disconnect({bool disconnectAllSessions = true}); @@ -108,14 +110,12 @@ abstract class IW3MService with ChangeNotifier { Future> requestReadContract({ required DeployedContract deployedContract, required String functionName, - @Deprecated('This is not needed anymore') String? rpcUrl, List parameters = const [], }); Future requestWriteContract({ required String? topic, required String chainId, - @Deprecated('This is not needed anymore') String? rpcUrl, required DeployedContract deployedContract, required String functionName, required Transaction transaction, @@ -135,7 +135,7 @@ abstract class IW3MService with ChangeNotifier { Future requestAddChain(W3MChainInfo newChain); /// Closes the modal. - void closeModal(); + void closeModal({bool disconnectSession = false}); @override Future dispose(); @@ -143,6 +143,7 @@ abstract class IW3MService with ChangeNotifier { /* EVENTS DECLARATIONS */ abstract final Event onModalConnect; + abstract final Event onModalUpdate; abstract final Event onModalNetworkChange; abstract final Event onModalDisconnect; abstract final Event onModalError; diff --git a/lib/services/w3m_service/models/w3m_session.dart b/lib/services/w3m_service/models/w3m_session.dart index 5b5dda5f..917ce11e 100644 --- a/lib/services/w3m_service/models/w3m_session.dart +++ b/lib/services/w3m_service/models/w3m_session.dart @@ -1,11 +1,9 @@ -import 'package:walletconnect_flutter_v2/walletconnect_flutter_v2.dart'; import 'package:web3modal_flutter/constants/string_constants.dart'; import 'package:web3modal_flutter/services/coinbase_service/coinbase_service.dart'; import 'package:web3modal_flutter/services/coinbase_service/models/coinbase_data.dart'; import 'package:web3modal_flutter/services/magic_service/magic_service.dart'; import 'package:web3modal_flutter/services/magic_service/models/magic_data.dart'; -import 'package:web3modal_flutter/utils/w3m_chains_presets.dart'; -import 'package:web3modal_flutter/utils/w3m_logger.dart'; +import 'package:web3modal_flutter/web3modal_flutter.dart'; enum W3MSessionService { wc, @@ -23,19 +21,24 @@ class W3MSession { SessionData? _sessionData; CoinbaseData? _coinbaseData; MagicData? _magicData; + SIWESession? _siweSession; W3MSession({ SessionData? sessionData, CoinbaseData? coinbaseData, MagicData? magicData, + SIWESession? siweSession, }) : _sessionData = sessionData, _coinbaseData = coinbaseData, - _magicData = magicData; + _magicData = magicData, + _siweSession = siweSession; - factory W3MSession.fromJson(Map json) { - final sessionDataString = json['sessionData']; - final coinbaseDataString = json['coinbaseData']; - final magicDataString = json['magicData']; + /// USED TO READ THE SESSION FROM LOCAL STORAGE + factory W3MSession.fromMap(Map map) { + final sessionDataString = map['sessionData']; + final coinbaseDataString = map['coinbaseData']; + final magicDataString = map['magicData']; + final siweSession = map['siweSession']; return W3MSession( sessionData: sessionDataString != null ? SessionData.fromJson(sessionDataString) @@ -45,6 +48,8 @@ class W3MSession { : null, magicData: magicDataString != null ? MagicData.fromJson(magicDataString) : null, + siweSession: + siweSession != null ? SIWESession.fromJson(siweSession) : null, ); } @@ -52,11 +57,13 @@ class W3MSession { SessionData? sessionData, CoinbaseData? coinbaseData, MagicData? magicData, + SIWESession? siweSession, }) { return W3MSession( sessionData: sessionData ?? _sessionData, coinbaseData: coinbaseData ?? _coinbaseData, magicData: magicData ?? _magicData, + siweSession: siweSession ?? _siweSession, ); } @@ -131,6 +138,7 @@ class W3MSession { if (!sessionService.isWC) { return [chainId]; } + final accounts = getAccounts() ?? []; final approvedChains = NamespaceUtils.getChainsFromAccounts(accounts); return approvedChains; @@ -159,22 +167,23 @@ class W3MSession { return _sessionData?.peer.metadata.redirect; } - // toJson would convert W3MSession to a WCFV2 kind of session object - Map toJson() { - return { - if (topic != null) 'topic': topic, - if (pairingTopic != null) 'pairingTopic': pairingTopic, - if (relay != null) 'relay': relay, - if (expiry != null) 'expiry': expiry, - if (acknowledged != null) 'acknowledged': acknowledged, - if (controller != null) 'controller': controller, - 'namespaces': _namespaces(), - if (requiredNamespaces != null) 'requiredNamespaces': requiredNamespaces, - if (optionalNamespaces != null) 'optionalNamespaces': optionalNamespaces, - 'self': self?.toJson(), - 'peer': peer?.toJson(), - }; - } + // toJson() would convert W3MSession to a SessionData kind of map + // no matter if Coinbase Wallet or Email Wallet is connected + Map toJson() => { + if (topic != null) 'topic': topic, + if (pairingTopic != null) 'pairingTopic': pairingTopic, + if (relay != null) 'relay': relay, + if (expiry != null) 'expiry': expiry, + if (acknowledged != null) 'acknowledged': acknowledged, + if (controller != null) 'controller': controller, + 'namespaces': _namespaces(), + if (requiredNamespaces != null) + 'requiredNamespaces': requiredNamespaces, + if (optionalNamespaces != null) + 'optionalNamespaces': optionalNamespaces, + 'self': self?.toJson(), + 'peer': peer?.toJson(), + }; } extension W3MSessionExtension on W3MSession { @@ -193,45 +202,20 @@ extension W3MSessionExtension on W3MSession { ConnectionMetadata? get self { if (sessionService.isCoinbase) { - // return ConnectionMetadata( - // metadata: _sessionData?.self.metadata, - // publicKey: '', - // ); + return _coinbaseData?.self; } if (sessionService.isMagic) { - // return ConnectionMetadata( - // metadata: _sessionData?.self.metadata, - // publicKey: '', - // ); + return _magicData?.self; } return _sessionData?.self; } ConnectionMetadata? get peer { if (sessionService.isCoinbase) { - return ConnectionMetadata( - metadata: PairingMetadata( - name: connectedWalletName!, - description: '', - url: '', - icons: [], - redirect: Redirect( - native: CoinbaseService.coinbaseSchema, - ), - ), - publicKey: '', - ); + return _coinbaseData?.peer; } if (sessionService.isMagic) { - return ConnectionMetadata( - metadata: PairingMetadata( - name: connectedWalletName!, - description: '', - url: '', - icons: [], - ), - publicKey: '', - ); + return _magicData?.peer; } return _sessionData?.peer; } @@ -255,7 +239,6 @@ extension W3MSessionExtension on W3MSession { if (accounts.isNotEmpty) { return NamespaceUtils.getAccount(accounts.first); } - W3MLoggerUtil.logger.e('[$runtimeType] no address found'); return null; } @@ -283,10 +266,10 @@ extension W3MSessionExtension on W3MSession { String? get connectedWalletName { if (sessionService.isCoinbase) { - return CoinbaseService.coinbaseWalletName; + return CoinbaseService.defaultWalletData.listing.name; } if (sessionService.isMagic) { - return 'Email Wallet'; + return MagicService.defaultWalletData.listing.name; } if (sessionService.isWC) { return peer?.metadata.name; @@ -306,6 +289,7 @@ extension W3MSessionExtension on W3MSession { if (sessionService.isCoinbase) { return { 'eip155': Namespace( + chains: ['eip155:$chainId'], accounts: ['eip155:$chainId:$address'], methods: [...CoinbaseService.supportedMethods], events: [], @@ -315,6 +299,7 @@ extension W3MSessionExtension on W3MSession { if (sessionService.isMagic) { return { 'eip155': Namespace( + chains: ['eip155:$chainId'], accounts: ['eip155:$chainId:$address'], methods: [...MagicService.supportedMethods], events: [], @@ -324,11 +309,13 @@ extension W3MSessionExtension on W3MSession { return namespaces; } + /// USED TO STORE THE SESSION IN LOCAL STORAGE Map toMap() { return { - 'sessionData': _sessionData?.toJson(), - 'coinbaseData': _coinbaseData?.toJson(), - 'magicData': _magicData?.toJson(), + if (_sessionData != null) 'sessionData': _sessionData!.toJson(), + if (_coinbaseData != null) 'coinbaseData': _coinbaseData?.toJson(), + if (_magicData != null) 'magicData': _magicData?.toJson(), + if (_siweSession != null) 'siweSession': _siweSession?.toJson(), }; } } diff --git a/lib/services/w3m_service/models/w3m_siwe.freezed.dart b/lib/services/w3m_service/models/w3m_siwe.freezed.dart new file mode 100644 index 00000000..971115af --- /dev/null +++ b/lib/services/w3m_service/models/w3m_siwe.freezed.dart @@ -0,0 +1,1077 @@ +// coverage:ignore-file +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark + +part of 'w3m_siwe.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +T _$identity(T value) => value; + +final _privateConstructorUsedError = UnsupportedError( + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); + +SIWECreateMessageArgs _$SIWECreateMessageArgsFromJson( + Map json) { + return _SIWECreateMessageArgs.fromJson(json); +} + +/// @nodoc +mixin _$SIWECreateMessageArgs { + String get chainId => throw _privateConstructorUsedError; + String get domain => throw _privateConstructorUsedError; + String get nonce => throw _privateConstructorUsedError; + String get uri => throw _privateConstructorUsedError; + String get address => throw _privateConstructorUsedError; + String get version => throw _privateConstructorUsedError; + CacaoHeader? get type => throw _privateConstructorUsedError; + String? get nbf => throw _privateConstructorUsedError; + String? get exp => throw _privateConstructorUsedError; + String? get statement => throw _privateConstructorUsedError; + String? get requestId => throw _privateConstructorUsedError; + List? get resources => throw _privateConstructorUsedError; + int? get expiry => throw _privateConstructorUsedError; + String? get iat => throw _privateConstructorUsedError; + + Map toJson() => throw _privateConstructorUsedError; + @JsonKey(ignore: true) + $SIWECreateMessageArgsCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $SIWECreateMessageArgsCopyWith<$Res> { + factory $SIWECreateMessageArgsCopyWith(SIWECreateMessageArgs value, + $Res Function(SIWECreateMessageArgs) then) = + _$SIWECreateMessageArgsCopyWithImpl<$Res, SIWECreateMessageArgs>; + @useResult + $Res call( + {String chainId, + String domain, + String nonce, + String uri, + String address, + String version, + CacaoHeader? type, + String? nbf, + String? exp, + String? statement, + String? requestId, + List? resources, + int? expiry, + String? iat}); + + $CacaoHeaderCopyWith<$Res>? get type; +} + +/// @nodoc +class _$SIWECreateMessageArgsCopyWithImpl<$Res, + $Val extends SIWECreateMessageArgs> + implements $SIWECreateMessageArgsCopyWith<$Res> { + _$SIWECreateMessageArgsCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? chainId = null, + Object? domain = null, + Object? nonce = null, + Object? uri = null, + Object? address = null, + Object? version = null, + Object? type = freezed, + Object? nbf = freezed, + Object? exp = freezed, + Object? statement = freezed, + Object? requestId = freezed, + Object? resources = freezed, + Object? expiry = freezed, + Object? iat = freezed, + }) { + return _then(_value.copyWith( + chainId: null == chainId + ? _value.chainId + : chainId // ignore: cast_nullable_to_non_nullable + as String, + domain: null == domain + ? _value.domain + : domain // ignore: cast_nullable_to_non_nullable + as String, + nonce: null == nonce + ? _value.nonce + : nonce // ignore: cast_nullable_to_non_nullable + as String, + uri: null == uri + ? _value.uri + : uri // ignore: cast_nullable_to_non_nullable + as String, + address: null == address + ? _value.address + : address // ignore: cast_nullable_to_non_nullable + as String, + version: null == version + ? _value.version + : version // ignore: cast_nullable_to_non_nullable + as String, + type: freezed == type + ? _value.type + : type // ignore: cast_nullable_to_non_nullable + as CacaoHeader?, + nbf: freezed == nbf + ? _value.nbf + : nbf // ignore: cast_nullable_to_non_nullable + as String?, + exp: freezed == exp + ? _value.exp + : exp // ignore: cast_nullable_to_non_nullable + as String?, + statement: freezed == statement + ? _value.statement + : statement // ignore: cast_nullable_to_non_nullable + as String?, + requestId: freezed == requestId + ? _value.requestId + : requestId // ignore: cast_nullable_to_non_nullable + as String?, + resources: freezed == resources + ? _value.resources + : resources // ignore: cast_nullable_to_non_nullable + as List?, + expiry: freezed == expiry + ? _value.expiry + : expiry // ignore: cast_nullable_to_non_nullable + as int?, + iat: freezed == iat + ? _value.iat + : iat // ignore: cast_nullable_to_non_nullable + as String?, + ) as $Val); + } + + @override + @pragma('vm:prefer-inline') + $CacaoHeaderCopyWith<$Res>? get type { + if (_value.type == null) { + return null; + } + + return $CacaoHeaderCopyWith<$Res>(_value.type!, (value) { + return _then(_value.copyWith(type: value) as $Val); + }); + } +} + +/// @nodoc +abstract class _$$SIWECreateMessageArgsImplCopyWith<$Res> + implements $SIWECreateMessageArgsCopyWith<$Res> { + factory _$$SIWECreateMessageArgsImplCopyWith( + _$SIWECreateMessageArgsImpl value, + $Res Function(_$SIWECreateMessageArgsImpl) then) = + __$$SIWECreateMessageArgsImplCopyWithImpl<$Res>; + @override + @useResult + $Res call( + {String chainId, + String domain, + String nonce, + String uri, + String address, + String version, + CacaoHeader? type, + String? nbf, + String? exp, + String? statement, + String? requestId, + List? resources, + int? expiry, + String? iat}); + + @override + $CacaoHeaderCopyWith<$Res>? get type; +} + +/// @nodoc +class __$$SIWECreateMessageArgsImplCopyWithImpl<$Res> + extends _$SIWECreateMessageArgsCopyWithImpl<$Res, + _$SIWECreateMessageArgsImpl> + implements _$$SIWECreateMessageArgsImplCopyWith<$Res> { + __$$SIWECreateMessageArgsImplCopyWithImpl(_$SIWECreateMessageArgsImpl _value, + $Res Function(_$SIWECreateMessageArgsImpl) _then) + : super(_value, _then); + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? chainId = null, + Object? domain = null, + Object? nonce = null, + Object? uri = null, + Object? address = null, + Object? version = null, + Object? type = freezed, + Object? nbf = freezed, + Object? exp = freezed, + Object? statement = freezed, + Object? requestId = freezed, + Object? resources = freezed, + Object? expiry = freezed, + Object? iat = freezed, + }) { + return _then(_$SIWECreateMessageArgsImpl( + chainId: null == chainId + ? _value.chainId + : chainId // ignore: cast_nullable_to_non_nullable + as String, + domain: null == domain + ? _value.domain + : domain // ignore: cast_nullable_to_non_nullable + as String, + nonce: null == nonce + ? _value.nonce + : nonce // ignore: cast_nullable_to_non_nullable + as String, + uri: null == uri + ? _value.uri + : uri // ignore: cast_nullable_to_non_nullable + as String, + address: null == address + ? _value.address + : address // ignore: cast_nullable_to_non_nullable + as String, + version: null == version + ? _value.version + : version // ignore: cast_nullable_to_non_nullable + as String, + type: freezed == type + ? _value.type + : type // ignore: cast_nullable_to_non_nullable + as CacaoHeader?, + nbf: freezed == nbf + ? _value.nbf + : nbf // ignore: cast_nullable_to_non_nullable + as String?, + exp: freezed == exp + ? _value.exp + : exp // ignore: cast_nullable_to_non_nullable + as String?, + statement: freezed == statement + ? _value.statement + : statement // ignore: cast_nullable_to_non_nullable + as String?, + requestId: freezed == requestId + ? _value.requestId + : requestId // ignore: cast_nullable_to_non_nullable + as String?, + resources: freezed == resources + ? _value._resources + : resources // ignore: cast_nullable_to_non_nullable + as List?, + expiry: freezed == expiry + ? _value.expiry + : expiry // ignore: cast_nullable_to_non_nullable + as int?, + iat: freezed == iat + ? _value.iat + : iat // ignore: cast_nullable_to_non_nullable + as String?, + )); + } +} + +/// @nodoc +@JsonSerializable() +class _$SIWECreateMessageArgsImpl implements _SIWECreateMessageArgs { + const _$SIWECreateMessageArgsImpl( + {required this.chainId, + required this.domain, + required this.nonce, + required this.uri, + required this.address, + this.version = '1', + this.type = const CacaoHeader(t: 'eip4361'), + this.nbf, + this.exp, + this.statement, + this.requestId, + final List? resources, + this.expiry, + this.iat}) + : _resources = resources; + + factory _$SIWECreateMessageArgsImpl.fromJson(Map json) => + _$$SIWECreateMessageArgsImplFromJson(json); + + @override + final String chainId; + @override + final String domain; + @override + final String nonce; + @override + final String uri; + @override + final String address; + @override + @JsonKey() + final String version; + @override + @JsonKey() + final CacaoHeader? type; + @override + final String? nbf; + @override + final String? exp; + @override + final String? statement; + @override + final String? requestId; + final List? _resources; + @override + List? get resources { + final value = _resources; + if (value == null) return null; + if (_resources is EqualUnmodifiableListView) return _resources; + // ignore: implicit_dynamic_type + return EqualUnmodifiableListView(value); + } + + @override + final int? expiry; + @override + final String? iat; + + @override + String toString() { + return 'SIWECreateMessageArgs(chainId: $chainId, domain: $domain, nonce: $nonce, uri: $uri, address: $address, version: $version, type: $type, nbf: $nbf, exp: $exp, statement: $statement, requestId: $requestId, resources: $resources, expiry: $expiry, iat: $iat)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$SIWECreateMessageArgsImpl && + (identical(other.chainId, chainId) || other.chainId == chainId) && + (identical(other.domain, domain) || other.domain == domain) && + (identical(other.nonce, nonce) || other.nonce == nonce) && + (identical(other.uri, uri) || other.uri == uri) && + (identical(other.address, address) || other.address == address) && + (identical(other.version, version) || other.version == version) && + (identical(other.type, type) || other.type == type) && + (identical(other.nbf, nbf) || other.nbf == nbf) && + (identical(other.exp, exp) || other.exp == exp) && + (identical(other.statement, statement) || + other.statement == statement) && + (identical(other.requestId, requestId) || + other.requestId == requestId) && + const DeepCollectionEquality() + .equals(other._resources, _resources) && + (identical(other.expiry, expiry) || other.expiry == expiry) && + (identical(other.iat, iat) || other.iat == iat)); + } + + @JsonKey(ignore: true) + @override + int get hashCode => Object.hash( + runtimeType, + chainId, + domain, + nonce, + uri, + address, + version, + type, + nbf, + exp, + statement, + requestId, + const DeepCollectionEquality().hash(_resources), + expiry, + iat); + + @JsonKey(ignore: true) + @override + @pragma('vm:prefer-inline') + _$$SIWECreateMessageArgsImplCopyWith<_$SIWECreateMessageArgsImpl> + get copyWith => __$$SIWECreateMessageArgsImplCopyWithImpl< + _$SIWECreateMessageArgsImpl>(this, _$identity); + + @override + Map toJson() { + return _$$SIWECreateMessageArgsImplToJson( + this, + ); + } +} + +abstract class _SIWECreateMessageArgs implements SIWECreateMessageArgs { + const factory _SIWECreateMessageArgs( + {required final String chainId, + required final String domain, + required final String nonce, + required final String uri, + required final String address, + final String version, + final CacaoHeader? type, + final String? nbf, + final String? exp, + final String? statement, + final String? requestId, + final List? resources, + final int? expiry, + final String? iat}) = _$SIWECreateMessageArgsImpl; + + factory _SIWECreateMessageArgs.fromJson(Map json) = + _$SIWECreateMessageArgsImpl.fromJson; + + @override + String get chainId; + @override + String get domain; + @override + String get nonce; + @override + String get uri; + @override + String get address; + @override + String get version; + @override + CacaoHeader? get type; + @override + String? get nbf; + @override + String? get exp; + @override + String? get statement; + @override + String? get requestId; + @override + List? get resources; + @override + int? get expiry; + @override + String? get iat; + @override + @JsonKey(ignore: true) + _$$SIWECreateMessageArgsImplCopyWith<_$SIWECreateMessageArgsImpl> + get copyWith => throw _privateConstructorUsedError; +} + +SIWEMessageArgs _$SIWEMessageArgsFromJson(Map json) { + return _SIWEMessageArgs.fromJson(json); +} + +/// @nodoc +mixin _$SIWEMessageArgs { + String get domain => throw _privateConstructorUsedError; + String get uri => throw _privateConstructorUsedError; + CacaoHeader? get type => throw _privateConstructorUsedError; + String? get nbf => throw _privateConstructorUsedError; + String? get exp => throw _privateConstructorUsedError; + String? get statement => throw _privateConstructorUsedError; + String? get requestId => throw _privateConstructorUsedError; + List? get resources => throw _privateConstructorUsedError; + int? get expiry => throw _privateConstructorUsedError; + String? get iat => throw _privateConstructorUsedError; + List? get methods => throw _privateConstructorUsedError; + + Map toJson() => throw _privateConstructorUsedError; + @JsonKey(ignore: true) + $SIWEMessageArgsCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $SIWEMessageArgsCopyWith<$Res> { + factory $SIWEMessageArgsCopyWith( + SIWEMessageArgs value, $Res Function(SIWEMessageArgs) then) = + _$SIWEMessageArgsCopyWithImpl<$Res, SIWEMessageArgs>; + @useResult + $Res call( + {String domain, + String uri, + CacaoHeader? type, + String? nbf, + String? exp, + String? statement, + String? requestId, + List? resources, + int? expiry, + String? iat, + List? methods}); + + $CacaoHeaderCopyWith<$Res>? get type; +} + +/// @nodoc +class _$SIWEMessageArgsCopyWithImpl<$Res, $Val extends SIWEMessageArgs> + implements $SIWEMessageArgsCopyWith<$Res> { + _$SIWEMessageArgsCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? domain = null, + Object? uri = null, + Object? type = freezed, + Object? nbf = freezed, + Object? exp = freezed, + Object? statement = freezed, + Object? requestId = freezed, + Object? resources = freezed, + Object? expiry = freezed, + Object? iat = freezed, + Object? methods = freezed, + }) { + return _then(_value.copyWith( + domain: null == domain + ? _value.domain + : domain // ignore: cast_nullable_to_non_nullable + as String, + uri: null == uri + ? _value.uri + : uri // ignore: cast_nullable_to_non_nullable + as String, + type: freezed == type + ? _value.type + : type // ignore: cast_nullable_to_non_nullable + as CacaoHeader?, + nbf: freezed == nbf + ? _value.nbf + : nbf // ignore: cast_nullable_to_non_nullable + as String?, + exp: freezed == exp + ? _value.exp + : exp // ignore: cast_nullable_to_non_nullable + as String?, + statement: freezed == statement + ? _value.statement + : statement // ignore: cast_nullable_to_non_nullable + as String?, + requestId: freezed == requestId + ? _value.requestId + : requestId // ignore: cast_nullable_to_non_nullable + as String?, + resources: freezed == resources + ? _value.resources + : resources // ignore: cast_nullable_to_non_nullable + as List?, + expiry: freezed == expiry + ? _value.expiry + : expiry // ignore: cast_nullable_to_non_nullable + as int?, + iat: freezed == iat + ? _value.iat + : iat // ignore: cast_nullable_to_non_nullable + as String?, + methods: freezed == methods + ? _value.methods + : methods // ignore: cast_nullable_to_non_nullable + as List?, + ) as $Val); + } + + @override + @pragma('vm:prefer-inline') + $CacaoHeaderCopyWith<$Res>? get type { + if (_value.type == null) { + return null; + } + + return $CacaoHeaderCopyWith<$Res>(_value.type!, (value) { + return _then(_value.copyWith(type: value) as $Val); + }); + } +} + +/// @nodoc +abstract class _$$SIWEMessageArgsImplCopyWith<$Res> + implements $SIWEMessageArgsCopyWith<$Res> { + factory _$$SIWEMessageArgsImplCopyWith(_$SIWEMessageArgsImpl value, + $Res Function(_$SIWEMessageArgsImpl) then) = + __$$SIWEMessageArgsImplCopyWithImpl<$Res>; + @override + @useResult + $Res call( + {String domain, + String uri, + CacaoHeader? type, + String? nbf, + String? exp, + String? statement, + String? requestId, + List? resources, + int? expiry, + String? iat, + List? methods}); + + @override + $CacaoHeaderCopyWith<$Res>? get type; +} + +/// @nodoc +class __$$SIWEMessageArgsImplCopyWithImpl<$Res> + extends _$SIWEMessageArgsCopyWithImpl<$Res, _$SIWEMessageArgsImpl> + implements _$$SIWEMessageArgsImplCopyWith<$Res> { + __$$SIWEMessageArgsImplCopyWithImpl( + _$SIWEMessageArgsImpl _value, $Res Function(_$SIWEMessageArgsImpl) _then) + : super(_value, _then); + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? domain = null, + Object? uri = null, + Object? type = freezed, + Object? nbf = freezed, + Object? exp = freezed, + Object? statement = freezed, + Object? requestId = freezed, + Object? resources = freezed, + Object? expiry = freezed, + Object? iat = freezed, + Object? methods = freezed, + }) { + return _then(_$SIWEMessageArgsImpl( + domain: null == domain + ? _value.domain + : domain // ignore: cast_nullable_to_non_nullable + as String, + uri: null == uri + ? _value.uri + : uri // ignore: cast_nullable_to_non_nullable + as String, + type: freezed == type + ? _value.type + : type // ignore: cast_nullable_to_non_nullable + as CacaoHeader?, + nbf: freezed == nbf + ? _value.nbf + : nbf // ignore: cast_nullable_to_non_nullable + as String?, + exp: freezed == exp + ? _value.exp + : exp // ignore: cast_nullable_to_non_nullable + as String?, + statement: freezed == statement + ? _value.statement + : statement // ignore: cast_nullable_to_non_nullable + as String?, + requestId: freezed == requestId + ? _value.requestId + : requestId // ignore: cast_nullable_to_non_nullable + as String?, + resources: freezed == resources + ? _value._resources + : resources // ignore: cast_nullable_to_non_nullable + as List?, + expiry: freezed == expiry + ? _value.expiry + : expiry // ignore: cast_nullable_to_non_nullable + as int?, + iat: freezed == iat + ? _value.iat + : iat // ignore: cast_nullable_to_non_nullable + as String?, + methods: freezed == methods + ? _value._methods + : methods // ignore: cast_nullable_to_non_nullable + as List?, + )); + } +} + +/// @nodoc + +@JsonSerializable(includeIfNull: false) +class _$SIWEMessageArgsImpl implements _SIWEMessageArgs { + const _$SIWEMessageArgsImpl( + {required this.domain, + required this.uri, + this.type = const CacaoHeader(t: 'eip4361'), + this.nbf, + this.exp, + this.statement, + this.requestId, + final List? resources, + this.expiry, + this.iat, + final List? methods}) + : _resources = resources, + _methods = methods; + + factory _$SIWEMessageArgsImpl.fromJson(Map json) => + _$$SIWEMessageArgsImplFromJson(json); + + @override + final String domain; + @override + final String uri; + @override + @JsonKey() + final CacaoHeader? type; + @override + final String? nbf; + @override + final String? exp; + @override + final String? statement; + @override + final String? requestId; + final List? _resources; + @override + List? get resources { + final value = _resources; + if (value == null) return null; + if (_resources is EqualUnmodifiableListView) return _resources; + // ignore: implicit_dynamic_type + return EqualUnmodifiableListView(value); + } + + @override + final int? expiry; + @override + final String? iat; + final List? _methods; + @override + List? get methods { + final value = _methods; + if (value == null) return null; + if (_methods is EqualUnmodifiableListView) return _methods; + // ignore: implicit_dynamic_type + return EqualUnmodifiableListView(value); + } + + @override + String toString() { + return 'SIWEMessageArgs(domain: $domain, uri: $uri, type: $type, nbf: $nbf, exp: $exp, statement: $statement, requestId: $requestId, resources: $resources, expiry: $expiry, iat: $iat, methods: $methods)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$SIWEMessageArgsImpl && + (identical(other.domain, domain) || other.domain == domain) && + (identical(other.uri, uri) || other.uri == uri) && + (identical(other.type, type) || other.type == type) && + (identical(other.nbf, nbf) || other.nbf == nbf) && + (identical(other.exp, exp) || other.exp == exp) && + (identical(other.statement, statement) || + other.statement == statement) && + (identical(other.requestId, requestId) || + other.requestId == requestId) && + const DeepCollectionEquality() + .equals(other._resources, _resources) && + (identical(other.expiry, expiry) || other.expiry == expiry) && + (identical(other.iat, iat) || other.iat == iat) && + const DeepCollectionEquality().equals(other._methods, _methods)); + } + + @JsonKey(ignore: true) + @override + int get hashCode => Object.hash( + runtimeType, + domain, + uri, + type, + nbf, + exp, + statement, + requestId, + const DeepCollectionEquality().hash(_resources), + expiry, + iat, + const DeepCollectionEquality().hash(_methods)); + + @JsonKey(ignore: true) + @override + @pragma('vm:prefer-inline') + _$$SIWEMessageArgsImplCopyWith<_$SIWEMessageArgsImpl> get copyWith => + __$$SIWEMessageArgsImplCopyWithImpl<_$SIWEMessageArgsImpl>( + this, _$identity); + + @override + Map toJson() { + return _$$SIWEMessageArgsImplToJson( + this, + ); + } +} + +abstract class _SIWEMessageArgs implements SIWEMessageArgs { + const factory _SIWEMessageArgs( + {required final String domain, + required final String uri, + final CacaoHeader? type, + final String? nbf, + final String? exp, + final String? statement, + final String? requestId, + final List? resources, + final int? expiry, + final String? iat, + final List? methods}) = _$SIWEMessageArgsImpl; + + factory _SIWEMessageArgs.fromJson(Map json) = + _$SIWEMessageArgsImpl.fromJson; + + @override + String get domain; + @override + String get uri; + @override + CacaoHeader? get type; + @override + String? get nbf; + @override + String? get exp; + @override + String? get statement; + @override + String? get requestId; + @override + List? get resources; + @override + int? get expiry; + @override + String? get iat; + @override + List? get methods; + @override + @JsonKey(ignore: true) + _$$SIWEMessageArgsImplCopyWith<_$SIWEMessageArgsImpl> get copyWith => + throw _privateConstructorUsedError; +} + +SIWEVerifyMessageArgs _$SIWEVerifyMessageArgsFromJson( + Map json) { + return _SIWEVerifyMessageArgs.fromJson(json); +} + +/// @nodoc +mixin _$SIWEVerifyMessageArgs { + String get message => throw _privateConstructorUsedError; + String get signature => throw _privateConstructorUsedError; + Cacao? get cacao => throw _privateConstructorUsedError; // for One-Click Auth + String? get clientId => throw _privateConstructorUsedError; + + Map toJson() => throw _privateConstructorUsedError; + @JsonKey(ignore: true) + $SIWEVerifyMessageArgsCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $SIWEVerifyMessageArgsCopyWith<$Res> { + factory $SIWEVerifyMessageArgsCopyWith(SIWEVerifyMessageArgs value, + $Res Function(SIWEVerifyMessageArgs) then) = + _$SIWEVerifyMessageArgsCopyWithImpl<$Res, SIWEVerifyMessageArgs>; + @useResult + $Res call({String message, String signature, Cacao? cacao, String? clientId}); + + $CacaoCopyWith<$Res>? get cacao; +} + +/// @nodoc +class _$SIWEVerifyMessageArgsCopyWithImpl<$Res, + $Val extends SIWEVerifyMessageArgs> + implements $SIWEVerifyMessageArgsCopyWith<$Res> { + _$SIWEVerifyMessageArgsCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? message = null, + Object? signature = null, + Object? cacao = freezed, + Object? clientId = freezed, + }) { + return _then(_value.copyWith( + message: null == message + ? _value.message + : message // ignore: cast_nullable_to_non_nullable + as String, + signature: null == signature + ? _value.signature + : signature // ignore: cast_nullable_to_non_nullable + as String, + cacao: freezed == cacao + ? _value.cacao + : cacao // ignore: cast_nullable_to_non_nullable + as Cacao?, + clientId: freezed == clientId + ? _value.clientId + : clientId // ignore: cast_nullable_to_non_nullable + as String?, + ) as $Val); + } + + @override + @pragma('vm:prefer-inline') + $CacaoCopyWith<$Res>? get cacao { + if (_value.cacao == null) { + return null; + } + + return $CacaoCopyWith<$Res>(_value.cacao!, (value) { + return _then(_value.copyWith(cacao: value) as $Val); + }); + } +} + +/// @nodoc +abstract class _$$SIWEVerifyMessageArgsImplCopyWith<$Res> + implements $SIWEVerifyMessageArgsCopyWith<$Res> { + factory _$$SIWEVerifyMessageArgsImplCopyWith( + _$SIWEVerifyMessageArgsImpl value, + $Res Function(_$SIWEVerifyMessageArgsImpl) then) = + __$$SIWEVerifyMessageArgsImplCopyWithImpl<$Res>; + @override + @useResult + $Res call({String message, String signature, Cacao? cacao, String? clientId}); + + @override + $CacaoCopyWith<$Res>? get cacao; +} + +/// @nodoc +class __$$SIWEVerifyMessageArgsImplCopyWithImpl<$Res> + extends _$SIWEVerifyMessageArgsCopyWithImpl<$Res, + _$SIWEVerifyMessageArgsImpl> + implements _$$SIWEVerifyMessageArgsImplCopyWith<$Res> { + __$$SIWEVerifyMessageArgsImplCopyWithImpl(_$SIWEVerifyMessageArgsImpl _value, + $Res Function(_$SIWEVerifyMessageArgsImpl) _then) + : super(_value, _then); + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? message = null, + Object? signature = null, + Object? cacao = freezed, + Object? clientId = freezed, + }) { + return _then(_$SIWEVerifyMessageArgsImpl( + message: null == message + ? _value.message + : message // ignore: cast_nullable_to_non_nullable + as String, + signature: null == signature + ? _value.signature + : signature // ignore: cast_nullable_to_non_nullable + as String, + cacao: freezed == cacao + ? _value.cacao + : cacao // ignore: cast_nullable_to_non_nullable + as Cacao?, + clientId: freezed == clientId + ? _value.clientId + : clientId // ignore: cast_nullable_to_non_nullable + as String?, + )); + } +} + +/// @nodoc + +@JsonSerializable(includeIfNull: false) +class _$SIWEVerifyMessageArgsImpl implements _SIWEVerifyMessageArgs { + const _$SIWEVerifyMessageArgsImpl( + {required this.message, + required this.signature, + this.cacao, + this.clientId}); + + factory _$SIWEVerifyMessageArgsImpl.fromJson(Map json) => + _$$SIWEVerifyMessageArgsImplFromJson(json); + + @override + final String message; + @override + final String signature; + @override + final Cacao? cacao; +// for One-Click Auth + @override + final String? clientId; + + @override + String toString() { + return 'SIWEVerifyMessageArgs(message: $message, signature: $signature, cacao: $cacao, clientId: $clientId)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$SIWEVerifyMessageArgsImpl && + (identical(other.message, message) || other.message == message) && + (identical(other.signature, signature) || + other.signature == signature) && + (identical(other.cacao, cacao) || other.cacao == cacao) && + (identical(other.clientId, clientId) || + other.clientId == clientId)); + } + + @JsonKey(ignore: true) + @override + int get hashCode => + Object.hash(runtimeType, message, signature, cacao, clientId); + + @JsonKey(ignore: true) + @override + @pragma('vm:prefer-inline') + _$$SIWEVerifyMessageArgsImplCopyWith<_$SIWEVerifyMessageArgsImpl> + get copyWith => __$$SIWEVerifyMessageArgsImplCopyWithImpl< + _$SIWEVerifyMessageArgsImpl>(this, _$identity); + + @override + Map toJson() { + return _$$SIWEVerifyMessageArgsImplToJson( + this, + ); + } +} + +abstract class _SIWEVerifyMessageArgs implements SIWEVerifyMessageArgs { + const factory _SIWEVerifyMessageArgs( + {required final String message, + required final String signature, + final Cacao? cacao, + final String? clientId}) = _$SIWEVerifyMessageArgsImpl; + + factory _SIWEVerifyMessageArgs.fromJson(Map json) = + _$SIWEVerifyMessageArgsImpl.fromJson; + + @override + String get message; + @override + String get signature; + @override + Cacao? get cacao; + @override // for One-Click Auth + String? get clientId; + @override + @JsonKey(ignore: true) + _$$SIWEVerifyMessageArgsImplCopyWith<_$SIWEVerifyMessageArgsImpl> + get copyWith => throw _privateConstructorUsedError; +} diff --git a/lib/services/w3m_service/models/w3m_siwe.g.dart b/lib/services/w3m_service/models/w3m_siwe.g.dart new file mode 100644 index 00000000..84bc26c3 --- /dev/null +++ b/lib/services/w3m_service/models/w3m_siwe.g.dart @@ -0,0 +1,126 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +// ignore_for_file: non_constant_identifier_names + +part of 'w3m_siwe.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +_$SIWECreateMessageArgsImpl _$$SIWECreateMessageArgsImplFromJson( + Map json) => + _$SIWECreateMessageArgsImpl( + chainId: json['chainId'] as String, + domain: json['domain'] as String, + nonce: json['nonce'] as String, + uri: json['uri'] as String, + address: json['address'] as String, + version: json['version'] as String? ?? '1', + type: json['type'] == null + ? const CacaoHeader(t: 'eip4361') + : CacaoHeader.fromJson(json['type'] as Map), + nbf: json['nbf'] as String?, + exp: json['exp'] as String?, + statement: json['statement'] as String?, + requestId: json['requestId'] as String?, + resources: (json['resources'] as List?) + ?.map((e) => e as String) + .toList(), + expiry: json['expiry'] as int?, + iat: json['iat'] as String?, + ); + +Map _$$SIWECreateMessageArgsImplToJson( + _$SIWECreateMessageArgsImpl instance) => + { + 'chainId': instance.chainId, + 'domain': instance.domain, + 'nonce': instance.nonce, + 'uri': instance.uri, + 'address': instance.address, + 'version': instance.version, + 'type': instance.type?.toJson(), + 'nbf': instance.nbf, + 'exp': instance.exp, + 'statement': instance.statement, + 'requestId': instance.requestId, + 'resources': instance.resources, + 'expiry': instance.expiry, + 'iat': instance.iat, + }; + +_$SIWEMessageArgsImpl _$$SIWEMessageArgsImplFromJson( + Map json) => + _$SIWEMessageArgsImpl( + domain: json['domain'] as String, + uri: json['uri'] as String, + type: json['type'] == null + ? const CacaoHeader(t: 'eip4361') + : CacaoHeader.fromJson(json['type'] as Map), + nbf: json['nbf'] as String?, + exp: json['exp'] as String?, + statement: json['statement'] as String?, + requestId: json['requestId'] as String?, + resources: (json['resources'] as List?) + ?.map((e) => e as String) + .toList(), + expiry: json['expiry'] as int?, + iat: json['iat'] as String?, + methods: + (json['methods'] as List?)?.map((e) => e as String).toList(), + ); + +Map _$$SIWEMessageArgsImplToJson( + _$SIWEMessageArgsImpl instance) { + final val = { + 'domain': instance.domain, + 'uri': instance.uri, + }; + + void writeNotNull(String key, dynamic value) { + if (value != null) { + val[key] = value; + } + } + + writeNotNull('type', instance.type?.toJson()); + writeNotNull('nbf', instance.nbf); + writeNotNull('exp', instance.exp); + writeNotNull('statement', instance.statement); + writeNotNull('requestId', instance.requestId); + writeNotNull('resources', instance.resources); + writeNotNull('expiry', instance.expiry); + writeNotNull('iat', instance.iat); + writeNotNull('methods', instance.methods); + return val; +} + +_$SIWEVerifyMessageArgsImpl _$$SIWEVerifyMessageArgsImplFromJson( + Map json) => + _$SIWEVerifyMessageArgsImpl( + message: json['message'] as String, + signature: json['signature'] as String, + cacao: json['cacao'] == null + ? null + : Cacao.fromJson(json['cacao'] as Map), + clientId: json['clientId'] as String?, + ); + +Map _$$SIWEVerifyMessageArgsImplToJson( + _$SIWEVerifyMessageArgsImpl instance) { + final val = { + 'message': instance.message, + 'signature': instance.signature, + }; + + void writeNotNull(String key, dynamic value) { + if (value != null) { + val[key] = value; + } + } + + writeNotNull('cacao', instance.cacao?.toJson()); + writeNotNull('clientId', instance.clientId); + return val; +} diff --git a/lib/services/w3m_service/w3m_service.dart b/lib/services/w3m_service/w3m_service.dart index e8747a87..094591d2 100644 --- a/lib/services/w3m_service/w3m_service.dart +++ b/lib/services/w3m_service/w3m_service.dart @@ -1,6 +1,7 @@ import 'dart:async'; import 'dart:convert'; import 'dart:math'; +import 'dart:developer' as dev; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; @@ -8,13 +9,16 @@ import 'package:flutter/services.dart'; import 'package:url_launcher/url_launcher.dart'; import 'package:web3modal_flutter/constants/string_constants.dart'; +import 'package:web3modal_flutter/constants/url_constants.dart'; import 'package:web3modal_flutter/pages/account_page.dart'; import 'package:web3modal_flutter/pages/approve_magic_request_page.dart'; +import 'package:web3modal_flutter/pages/approve_siwe.dart'; import 'package:web3modal_flutter/pages/select_network_page.dart'; import 'package:web3modal_flutter/services/analytics_service/analytics_service.dart'; import 'package:web3modal_flutter/services/analytics_service/analytics_service_singleton.dart'; import 'package:web3modal_flutter/services/analytics_service/models/analytics_event.dart'; import 'package:web3modal_flutter/services/coinbase_service/coinbase_service.dart'; +import 'package:web3modal_flutter/services/coinbase_service/coinbase_service_singleton.dart'; import 'package:web3modal_flutter/services/coinbase_service/i_coinbase_service.dart'; import 'package:web3modal_flutter/services/coinbase_service/models/coinbase_data.dart'; import 'package:web3modal_flutter/services/coinbase_service/models/coinbase_events.dart'; @@ -29,13 +33,15 @@ import 'package:web3modal_flutter/services/magic_service/models/magic_data.dart' import 'package:web3modal_flutter/services/magic_service/models/magic_events.dart'; import 'package:web3modal_flutter/services/logger_service/logger_service.dart'; import 'package:web3modal_flutter/services/logger_service/logger_service_singleton.dart'; +import 'package:web3modal_flutter/services/siwe_service/siwe_service.dart'; +import 'package:web3modal_flutter/services/siwe_service/siwe_service_singleton.dart'; import 'package:web3modal_flutter/utils/core/core_utils_singleton.dart'; import 'package:web3modal_flutter/utils/platform/i_platform_utils.dart'; import 'package:web3modal_flutter/utils/url/launch_url_exception.dart'; import 'package:web3modal_flutter/web3modal_flutter.dart'; import 'package:web3modal_flutter/widgets/widget_stack/widget_stack_singleton.dart'; -import 'package:web3modal_flutter/services/blockchain_api_service/blockchain_api_utils.dart'; -import 'package:web3modal_flutter/services/blockchain_api_service/blockchain_api_utils_singleton.dart'; +import 'package:web3modal_flutter/services/blockchain_service/blockchain_service.dart'; +import 'package:web3modal_flutter/services/blockchain_service/blockchain_service_singleton.dart'; import 'package:web3modal_flutter/services/network_service/network_service_singleton.dart'; import 'package:web3modal_flutter/services/storage_service/storage_service_singleton.dart'; import 'package:web3modal_flutter/services/w3m_service/i_w3m_service.dart'; @@ -47,10 +53,13 @@ import 'package:web3modal_flutter/utils/url/url_utils_singleton.dart'; /// Either a [projectId] and [metadata] must be provided or an already created [web3App]. /// optionalNamespaces is mostly not needed, if you use it, the values set here will override every optionalNamespaces set in evey chain -class W3MService with ChangeNotifier, CoinbaseService implements IW3MService { - var _projectId = ''; - +class W3MService with ChangeNotifier implements IW3MService { + String _projectId = ''; BuildContext? _context; + Map _requiredNamespaces = {}; + Map _optionalNamespaces = {}; + String? _lastChainEmitted; + bool _supportsOneClickAuth = true; W3MServiceStatus _status = W3MServiceStatus.idle; @override @@ -64,9 +73,6 @@ class W3MService with ChangeNotifier, CoinbaseService implements IW3MService { @override W3MWalletInfo? get selectedWallet => _selectedWallet; - Map _requiredNamespaces = {}; - Map _optionalNamespaces = {}; - @override bool get hasNamespaces => _requiredNamespaces.isNotEmpty || _optionalNamespaces.isNotEmpty; @@ -84,8 +90,6 @@ class W3MService with ChangeNotifier, CoinbaseService implements IW3MService { String? get avatarUrl => _avatarUrl; double? _chainBalance; - String? _lastChainEmitted; - @override String get chainBalance { return coreUtils.instance.formatChainBalance(_chainBalance); @@ -112,6 +116,7 @@ class W3MService with ChangeNotifier, CoinbaseService implements IW3MService { IWeb3App? web3App, String? projectId, PairingMetadata? metadata, + SIWEConfig? siweConfig, Map? requiredNamespaces, Map? optionalNamespaces, Set? featuredWalletIds, @@ -143,7 +148,6 @@ class W3MService with ChangeNotifier, CoinbaseService implements IW3MService { loggerService.instance = LoggerService( level: logLevel, - projectId: _projectId, debugMode: kDebugMode, ); @@ -165,14 +169,25 @@ class W3MService with ChangeNotifier, CoinbaseService implements IW3MService { excludedWalletIds: excludedWalletIds, ); - blockchainApiUtils.instance = BlockchainApiUtils( + blockchainService.instance = BlockChainService( + web3app: _web3App, projectId: _projectId, ); + siweService.instance = SiweService( + web3app: _web3App, + siweConfig: siweConfig, + ); + magicService.instance = MagicService( web3app: _web3App, enabled: enableEmail, ); + + coinbaseService.instance = CoinbaseService( + web3app: _web3App, + enabled: _initializeCoinbaseSDK, + ); } ////////* PUBLIC METHODS *///////// @@ -183,9 +198,11 @@ class W3MService with ChangeNotifier, CoinbaseService implements IW3MService { Future init() async { _serviceInitialized = false; if (!coreUtils.instance.isValidProjectID(_projectId)) { - loggerService.instance.e( - '[$runtimeType] projectId $_projectId is invalid. Please provide a valid projectId. ' - 'See https://docs.walletconnect.com/web3modal/flutter/options for details.'); + _logger.e( + '[$runtimeType] projectId $_projectId is invalid. ' + 'Please provide a valid projectId. ' + 'See ${UrlConstants.docsUrl}/appkit/flutter/core/options for details.', + ); return; } if (_status == W3MServiceStatus.initializing || @@ -200,12 +217,7 @@ class W3MService with ChangeNotifier, CoinbaseService implements IW3MService { await storageService.instance.init(); await networkService.instance.init(); await explorerService.instance.init(); - if (_initializeCoinbaseSDK) { - // final isInstalled = await cbIsInstalled(); - // Fetch Coinbase Wallet object to get updated metadata - final cbWallet = await explorerService.instance.getCoinbaseWalletObject(); - await cbInit(metadata: _web3App.metadata, cbWallet: cbWallet); - } + await coinbaseService.instance.init(); await _web3App.init(); _currentSession = await _getStoredSession(); @@ -243,12 +255,14 @@ class W3MService with ChangeNotifier, CoinbaseService implements IW3MService { // session should not outlive the pairing if (wcPairings.isEmpty) { await disconnect(); + } else { + await _checkSIWEStatus(); } } else { // Check for other type of sessions stored if (_currentSession != null) { if (_currentSession!.sessionService.isCoinbase) { - final isCbConnected = await cbIsConnected(); + final isCbConnected = await coinbaseService.instance.isConnected(); if (!isCbConnected) { await _cleanSession(); } @@ -266,20 +280,45 @@ class W3MService with ChangeNotifier, CoinbaseService implements IW3MService { // Get the chainId of the chain we are connected to. await _selectChainFromStoredId(); + onModalNetworkChange.subscribe(_onNetworkChainRequireSIWE); + _serviceInitialized = true; _status = W3MServiceStatus.initialized; - loggerService.instance.i('[$runtimeType] initialized'); + _logger.i('[$runtimeType] initialized'); _notify(); } + Future _checkSIWEStatus() async { + // check if SIWE is enabled and should still sign the message + if (siweService.instance!.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 session = _currentSession!.copyWith(siweSession: siweSession); + await _setSesionAndChainData(session); + _notify(); + } catch (_) { + final context = siweService.instance!.config!.context; + _showModalView( + context, + ApproveSIWEPage(onSiweFinish: _oneSIWEFinish), + ); + } + } + } + Future _setSesionAndChainData(W3MSession w3mSession) async { try { await _storeSession(w3mSession); - final chainId = _currentSelectedChain!.chainId; + final chainId = _currentSelectedChain?.chainId ?? w3mSession.chainId; final chainInfo = W3MChainPresets.chains[chainId]!; await _setLocalEthChain(chainInfo, logEvent: false); - } catch (e) { - _logger.e('[$runtimeType] _setSesionAndChainData error $e'); + } catch (e, s) { + _logger.e( + '[$runtimeType] _setSesionAndChainData error $e', + stackTrace: s, + ); } } @@ -290,7 +329,7 @@ class W3MService with ChangeNotifier, CoinbaseService implements IW3MService { defaultValue: '', ); if (sessionString!.isNotEmpty) { - return W3MSession.fromJson(jsonDecode(sessionString)); + return W3MSession.fromMap(jsonDecode(sessionString)); } } catch (e) { await storageService.instance.setString(StringConstants.w3mSession, ''); @@ -342,9 +381,9 @@ class W3MService with ChangeNotifier, CoinbaseService implements IW3MService { if (_currentSession?.sessionService.isMagic == true) { await magicService.instance.switchNetwork(chainId: chainInfo.chainId); - onModalNetworkChange.broadcast(ModalNetworkChange( - chainId: chainInfo.namespace, - )); + // onModalNetworkChange.broadcast(ModalNetworkChange( + // chainId: chainInfo.namespace, + // )); } else { final hasValidSession = _isConnected && _currentSession != null; if (switchChain && hasValidSession && _currentSelectedChain != null) { @@ -418,10 +457,12 @@ class W3MService with ChangeNotifier, CoinbaseService implements IW3MService { analyticsService.instance.sendEvent(SwitchNetworkEvent(network: network)); } if (_lastChainEmitted != chainInfo.namespace && _isConnected) { + if (_lastChainEmitted != null) { + onModalNetworkChange.broadcast(ModalNetworkChange( + chainId: chainInfo.namespace, + )); + } _lastChainEmitted = chainInfo.namespace; - onModalNetworkChange.broadcast(ModalNetworkChange( - chainId: _lastChainEmitted!, - )); } loadAccountData(); } @@ -496,6 +537,7 @@ class W3MService with ChangeNotifier, CoinbaseService implements IW3MService { final mqData = MediaQueryData.fromView(View.of(_context!)); final safeGap = mqData.viewPadding.bottom; final maxHeight = mqData.size.height - safeGap - 20.0; + final modalHeight = isApprovePage ? 600.0 : maxHeight; await showModalBottomSheet( backgroundColor: Colors.transparent, isDismissible: !isApprovePage, @@ -503,9 +545,7 @@ class W3MService with ChangeNotifier, CoinbaseService implements IW3MService { enableDrag: false, elevation: 0.0, useRootNavigator: true, - constraints: BoxConstraints( - maxHeight: isApprovePage ? 600.0 : maxHeight, - ), + constraints: BoxConstraints(maxHeight: modalHeight), context: _context!, builder: (_) => rootWidget, ); @@ -521,15 +561,13 @@ class W3MService with ChangeNotifier, CoinbaseService implements IW3MService { final radiuses = Web3ModalTheme.radiusesOf(_context!); final maxRadius = min(radiuses.radiusM, 36.0); final borderRadius = BorderRadius.all(Radius.circular(maxRadius)); + final constraints = BoxConstraints(maxWidth: 360, maxHeight: 600); return Dialog( backgroundColor: Web3ModalTheme.colorsOf(_context!).background125, shape: RoundedRectangleBorder(borderRadius: borderRadius), clipBehavior: Clip.hardEdge, child: ConstrainedBox( - constraints: BoxConstraints( - maxWidth: 360.0, - maxHeight: 600.0, - ), + constraints: constraints, child: rootWidget, ), ); @@ -560,18 +598,6 @@ class W3MService with ChangeNotifier, CoinbaseService implements IW3MService { name: walletName, platform: inBrowser ? AnalyticsPlatform.web : AnalyticsPlatform.mobile, ); - // if (walletRedirect?.mobileOnly == true) { - // event = SelectWalletEvent( - // name: walletName, - // platform: AnalyticsPlatform.mobile.name, - // ); - // } - // if (walletRedirect?.webOnly == true) { - // event = SelectWalletEvent( - // name: walletName, - // platform: AnalyticsPlatform.web.name, - // ); - // } analyticsService.instance.sendEvent(event); } @@ -596,7 +622,7 @@ class W3MService with ChangeNotifier, CoinbaseService implements IW3MService { } try { if (_selectedWallet!.isCoinbase) { - await cbGetAccount(); + await coinbaseService.instance.getAccount(); await explorerService.instance.storeConnectedWallet(_selectedWallet); } else { await buildConnectionUri(); @@ -625,7 +651,7 @@ class W3MService with ChangeNotifier, CoinbaseService implements IW3MService { onModalError.broadcast(WalletNotInstalled()); } else { if (_isUserRejectedError(e)) { - loggerService.instance.i('[$runtimeType] User declined connection'); + _logger.i('[$runtimeType] User declined connection'); onModalError.broadcast(UserRejectedConnection()); analyticsService.instance.sendEvent(ConnectErrorEvent( message: 'User declined connection', @@ -638,13 +664,13 @@ class W3MService with ChangeNotifier, CoinbaseService implements IW3MService { } } } else if (_isUserRejectedError(e)) { - loggerService.instance.i('[$runtimeType] User declined connection'); + _logger.i('[$runtimeType] User declined connection'); onModalError.broadcast(UserRejectedConnection()); analyticsService.instance.sendEvent(ConnectErrorEvent( message: 'User declined connection', )); } else { - loggerService.instance.e( + _logger.e( '[$runtimeType] Error connecting wallet', error: e, stackTrace: s, @@ -654,56 +680,113 @@ class W3MService with ChangeNotifier, CoinbaseService implements IW3MService { } } + @override + String formatMessage(SIWECreateMessageArgs params) { + return siweService.instance!.formatMessage( + params, + ); + } + @override Future buildConnectionUri() async { if (!_isConnected) { - loggerService.instance.i( - '[$runtimeType] Connecting to WalletConnect, ' - 'required namespaces: $_requiredNamespaces, ' - 'optional namespaces: $_optionalNamespaces', - ); - - final connectResponse = await _web3App.connect( - requiredNamespaces: _requiredNamespaces, - optionalNamespaces: _optionalNamespaces, - ); - _wcUri = connectResponse.uri?.toString() ?? ''; - _notify(); - _awaitConnectionCallback(connectResponse); + try { + if (siweService.instance!.enabled) { + final nonce = await siweService.instance!.getNonce(); + final p1 = await siweService.instance!.config!.getMessageParams(); + final chains = + W3MChainPresets.chains.values.map((e) => e.namespace).toList(); + final methods = p1.methods ?? MethodsConstants.allMethods; + final p2 = {'nonce': nonce, 'chains': chains, 'methods': methods}; + final authParams = SessionAuthRequestParams.fromJson({ + ...p1.toJson(), + ...p2, + }); + // One-Click Auth + final authResponse = await _web3App.authenticate( + params: authParams, + ); + _wcUri = authResponse.uri?.toString() ?? ''; + _notify(); + _awaitOCAuthCallback(authResponse); + } else { + // Regular Session Proposal + final connectResponse = await _web3App.connect( + requiredNamespaces: _requiredNamespaces, + optionalNamespaces: _optionalNamespaces, + ); + _wcUri = connectResponse.uri?.toString() ?? ''; + _notify(); + _awaitConnectionCallback(connectResponse); + } + } catch (e) { + _logger.e('[$runtimeType] buildConnectionUri $e'); + } } } - Future _awaitConnectionCallback(ConnectResponse connectResponse) async { + void _awaitConnectionCallback(ConnectResponse connectResponse) async { try { - final response = await connectResponse.session.future; - await explorerService.instance.storeConnectedWallet(_selectedWallet); - loggerService.instance.t( - '[$runtimeType] Connected with session ${response.toJson()}', - ); + final _ = await connectResponse.session.future; } on TimeoutException { - loggerService.instance.i( - '[$runtimeType] Rebuilding session, ending future', - ); + _logger.i('[$runtimeType] Rebuilding session, ending future'); return; - } on JsonRpcError catch (e, s) { - if (_isUserRejectedError(e)) { - loggerService.instance.i('[$runtimeType] User declined connection'); - onModalError.broadcast(UserRejectedConnection()); + } catch (e) { + await _connectionErrorHandler(e); + } + } + + Future _connectionErrorHandler(dynamic e) async { + if (_isUserRejectedError(e)) { + _logger.i('[$runtimeType] User declined connection'); + onModalError.broadcast(UserRejectedConnection()); + analyticsService.instance.sendEvent(ConnectErrorEvent( + message: 'User declined connection', + )); + } else { + if (e is JsonRpcError) { + final message = e.message ?? ''; + if (e.code == 10001 && + message.contains(MethodConstants.WC_SESSION_AUTHENTICATE)) { + // WILL HANDLE SESSION REQUEST + _supportsOneClickAuth = false; + return; + } + onModalError.broadcast(ModalError('Error connecting to wallet')); analyticsService.instance.sendEvent(ConnectErrorEvent( - message: 'User declined connection', + message: message, )); - } else { - final message = e.message ?? 'Error connecting to wallet'; - loggerService.instance.e( - '[$runtimeType] $message', - error: e, - stackTrace: s, - ); + _logger.e('[$runtimeType] $message', error: e); + } + if (e is WalletConnectError) { + onModalError.broadcast(ModalError(e.message)); analyticsService.instance.sendEvent(ConnectErrorEvent( - message: message, + message: e.message, )); + _logger.e('[$runtimeType] ${e.message}', error: e); + } + } + return await expirePreviousInactivePairings(); + } + + void _awaitOCAuthCallback(SessionAuthRequestResponse authResponse) async { + try { + final response = await authResponse.completer.future; + if (response.session != null) { + _web3App.onSessionConnect.broadcast(SessionConnect(response.session!)); + } else { + if (response.jsonRpcError != null) { + throw response.jsonRpcError!; + } + if (response.error != null) { + throw response.error!; + } } - return await expirePreviousInactivePairings(); + } on TimeoutException { + _logger.i('[$runtimeType] Rebuilding session, ending future'); + return; + } catch (e) { + await _connectionErrorHandler(e); } } @@ -766,7 +849,7 @@ class W3MService with ChangeNotifier, CoinbaseService implements IW3MService { if (_currentSession?.sessionService.isCoinbase == true) { try { - await cbResetSession(); + await coinbaseService.instance.resetSession(); } catch (_) { _status = W3MServiceStatus.initialized; _notify(); @@ -795,6 +878,11 @@ class W3MService with ChangeNotifier, CoinbaseService implements IW3MService { _currentSession?.topic, ); } + try { + if (siweService.instance!.signOutOnDisconnect) { + await siweService.instance!.signOut(); + } + } catch (_) {} analyticsService.instance.sendEvent(DisconnectSuccessEvent()); if (!(_currentSession?.sessionService.isWC == true)) { @@ -810,17 +898,23 @@ class W3MService with ChangeNotifier, CoinbaseService implements IW3MService { } @override - void closeModal() { + void closeModal({bool disconnectSession = false}) async { + if (disconnectSession) { + await disconnect(); + selectWallet(null); + } // If we aren't open, then we can't and shouldn't close _close(event: false); if (_context != null) { - Navigator.of(_context!, rootNavigator: true).pop(); - analyticsService.instance.sendEvent(ModalCloseEvent( - connected: _isConnected, - )); - } else { - _notify(); + final canPop = Navigator.of(_context!, rootNavigator: true).canPop(); + if (canPop) { + Navigator.of(_context!, rootNavigator: true).pop(); + analyticsService.instance.sendEvent(ModalCloseEvent( + connected: _isConnected, + )); + } } + _notify(); } void _close({bool event = true}) { @@ -858,7 +952,6 @@ class W3MService with ChangeNotifier, CoinbaseService implements IW3MService { Future> requestReadContract({ required DeployedContract deployedContract, required String functionName, - @Deprecated('This is not needed anymore') String? rpcUrl, List parameters = const [], }) async { try { @@ -877,7 +970,6 @@ class W3MService with ChangeNotifier, CoinbaseService implements IW3MService { Future requestWriteContract({ required String? topic, required String chainId, - @Deprecated('This is not needed anymore') String? rpcUrl, required DeployedContract deployedContract, required String functionName, required Transaction transaction, @@ -922,12 +1014,12 @@ class W3MService with ChangeNotifier, CoinbaseService implements IW3MService { if (_currentSession == null) { throw W3MServiceException('Session is null'); } - String requestChainId = chainId; + String reqChainId = chainId; final isValidChainId = NamespaceUtils.isValidChainId(chainId); if (!isValidChainId) { if (selectedChain!.namespace.contains(chainId)) { - requestChainId = selectedChain!.namespace; + reqChainId = selectedChain!.namespace; } else { throw Errors.getSdkError( Errors.UNSUPPORTED_CHAINS, @@ -937,28 +1029,30 @@ class W3MService with ChangeNotifier, CoinbaseService implements IW3MService { } // _logger.d( - '[$runtimeType] request, chainId: $requestChainId, ${jsonEncode(request.toJson())}'); + '[$runtimeType] request, chainId: $reqChainId, ' + '${jsonEncode(request.toJson())}', + ); try { if (_currentSession!.sessionService.isMagic) { return await magicService.instance.request( - chainId: requestChainId, + chainId: reqChainId, request: request, ); } if (_currentSession!.sessionService.isCoinbase) { - return await cbRequest( - chainId: switchToChainId ?? requestChainId, + return await await coinbaseService.instance.request( + chainId: switchToChainId ?? reqChainId, request: request, ); } return await _web3App.request( topic: topic!, - chainId: requestChainId, + chainId: reqChainId, request: request, ); } catch (e) { if (_isUserRejectedError(e)) { - loggerService.instance.i('[$runtimeType] User declined request'); + _logger.i('[$runtimeType] User declined request'); onModalError.broadcast(UserRejectedConnection()); if (request.method == MethodsConstants.walletSwitchEthChain || request.method == MethodsConstants.walletAddEthChain) { @@ -984,7 +1078,7 @@ class W3MService with ChangeNotifier, CoinbaseService implements IW3MService { await expirePreviousInactivePairings(); _unregisterListeners(); _status = W3MServiceStatus.idle; - loggerService.instance.d('[$runtimeType] dispose'); + _logger.d('[$runtimeType] dispose'); } super.dispose(); } @@ -992,6 +1086,9 @@ class W3MService with ChangeNotifier, CoinbaseService implements IW3MService { @override final Event onModalConnect = Event(); + @override + final Event onModalUpdate = Event(); + @override final Event onModalNetworkChange = Event(); @@ -1015,7 +1112,7 @@ class W3MService with ChangeNotifier, CoinbaseService implements IW3MService { void _notify() => notifyListeners(); bool get _initializeCoinbaseSDK { - final cbId = CoinbaseService.coinbaseWalletId; + final cbId = CoinbaseService.defaultWalletData.listing.id; final included = (explorerService.instance.includedWalletIds ?? {}); final excluded = (explorerService.instance.excludedWalletIds ?? {}); @@ -1095,14 +1192,14 @@ class W3MService with ChangeNotifier, CoinbaseService implements IW3MService { // Get the avatar, each chainId is just a number in string form. try { - final blockchainId = await blockchainApiUtils.instance!.getIdentity( + final blockchainId = await blockchainService.instance!.getIdentity( _currentSession!.address!, int.parse(_currentSelectedChain!.chainId), ); _avatarUrl = blockchainId.avatar; - loggerService.instance.i('[$runtimeType] loadAccountData'); + _logger.i('[$runtimeType] loadAccountData'); } catch (e) { - loggerService.instance.e('[$runtimeType] loadAccountData $e'); + _logger.e('[$runtimeType] loadAccountData $e'); } _notify(); } @@ -1221,6 +1318,7 @@ class W3MService with ChangeNotifier, CoinbaseService implements IW3MService { _isConnected = false; _currentSession = null; _lastChainEmitted = null; + _supportsOneClickAuth = true; _status = W3MServiceStatus.initialized; _notify(); } @@ -1243,10 +1341,17 @@ class W3MService with ChangeNotifier, CoinbaseService implements IW3MService { magicService.instance.onMagicRpcRequest.subscribe(_onMagicRequest); // // Coinbase - onCoinbaseConnect.subscribe(_onCoinbaseConnectEvent); - onCoinbaseError.subscribe(_onCoinbaseErrorEvent); - onCoinbaseSessionUpdate.subscribe(_onCoinbaseSessionUpdateEvent); + coinbaseService.instance.onCoinbaseConnect.subscribe( + _onCoinbaseConnectEvent, + ); + coinbaseService.instance.onCoinbaseError.subscribe( + _onCoinbaseErrorEvent, + ); + coinbaseService.instance.onCoinbaseSessionUpdate.subscribe( + _onCoinbaseSessionUpdateEvent, + ); // + _web3App.onSessionAuthResponse.subscribe(_onSessionAuthResponse); _web3App.onSessionConnect.subscribe(_onSessionConnect); _web3App.onSessionDelete.subscribe(_onSessionDelete); _web3App.onSessionExpire.subscribe(_onSessionExpire); @@ -1264,6 +1369,17 @@ class W3MService with ChangeNotifier, CoinbaseService implements IW3MService { ); } + void _onNetworkChainRequireSIWE(ModalNetworkChange? args) async { + try { + if (siweService.instance!.signOutOnNetworkChange) { + await siweService.instance!.signOut(); + widgetStack.instance.push(ApproveSIWEPage( + onSiweFinish: _oneSIWEFinish, + )); + } + } catch (_) {} + } + void _unregisterListeners() { // Magic magicService.instance.onMagicLoginSuccess.unsubscribe(_onMagicLoginEvent); @@ -1272,10 +1388,17 @@ class W3MService with ChangeNotifier, CoinbaseService implements IW3MService { magicService.instance.onMagicRpcRequest.unsubscribe(_onMagicRequest); // // Coinbase - onCoinbaseConnect.unsubscribe(_onCoinbaseConnectEvent); - onCoinbaseError.unsubscribe(_onCoinbaseErrorEvent); - onCoinbaseSessionUpdate.unsubscribe(_onCoinbaseSessionUpdateEvent); + coinbaseService.instance.onCoinbaseConnect.unsubscribe( + _onCoinbaseConnectEvent, + ); + coinbaseService.instance.onCoinbaseError.unsubscribe( + _onCoinbaseErrorEvent, + ); + coinbaseService.instance.onCoinbaseSessionUpdate.unsubscribe( + _onCoinbaseSessionUpdateEvent, + ); // + _web3App.onSessionAuthResponse.unsubscribe(_onSessionAuthResponse); _web3App.onSessionConnect.unsubscribe(_onSessionConnect); _web3App.onSessionDelete.unsubscribe(_onSessionDelete); _web3App.onSessionEvent.unsubscribe(_onSessionEvent); @@ -1292,7 +1415,7 @@ class W3MService with ChangeNotifier, CoinbaseService implements IW3MService { ); } - String? _savedChainId(String? defaultValue) { + String? _savedChainId(String defaultValue) { return storageService.instance.getString( StringConstants.selectedChainId, defaultValue: defaultValue, @@ -1302,30 +1425,41 @@ class W3MService with ChangeNotifier, CoinbaseService implements IW3MService { extension _W3MMagicExtension on W3MService { Future _onMagicLoginEvent(MagicLoginEvent? args) async { - _logger.p('[$runtimeType] _onMagicLoginEvent $args'); - if (args != null) { - final chainId = args.data?.chainId.toString(); - // final chainId = _savedChainId(args.data?.chainId.toString()); - final newChainId = chainId ?? '1'; - final newChain = W3MChainPresets.chains[chainId]!; + final debugString = jsonEncode(args?.data?.toJson()); + _logger.i('[$runtimeType] _onMagicLoginEvent: $debugString'); + if (args!.data != null) { + final newChainId = _savedChainId('${args.data!.chainId}')!; + final newChain = W3MChainPresets.chains[newChainId]!; _currentSelectedChain = newChain; + // final magicData = args.data?.copytWith(chainId: int.tryParse(newChainId)); final session = W3MSession(magicData: magicData); await _setSesionAndChainData(session); - // magicService.instance.switchNetwork(chainId: newChainId); onModalConnect.broadcast(ModalConnect(session)); - if (_isOpen) { - closeModal(); - } if (_selectedWallet == null) { storageService.instance.clearKey(StringConstants.recentWalletId); storageService.instance.clearKey(StringConstants.connectedWalletData); } + // + if (siweService.instance!.enabled) { + if (!_isOpen) { + await _checkSIWEStatus(); + onModalUpdate.broadcast(ModalConnect(_currentSession!)); + } else { + widgetStack.instance.push(ApproveSIWEPage( + onSiweFinish: _oneSIWEFinish, + )); + } + } else { + if (_isOpen) { + closeModal(); + } + } } } Future _onMagicSessionUpdateEvent(MagicSessionEvent? args) async { - _logger.p('[$runtimeType] _onMagicUpdateEvent: $args'); + _logger.d('[$runtimeType] _onMagicUpdateEvent: $args'); if (args != null) { try { final newEmail = args.email ?? _currentSession!.email; @@ -1333,15 +1467,22 @@ extension _W3MMagicExtension on W3MService { final chainId = args.chainId?.toString() ?? _currentSession!.chainId; final newChain = W3MChainPresets.chains[chainId]!; _currentSelectedChain = newChain; - final newData = MagicData( - email: newEmail, - address: address, - chainId: int.parse(chainId), + // + final session = _currentSession!.copyWith( + magicData: MagicData( + email: newEmail, + address: address, + chainId: int.parse(chainId), + peer: magicService.instance.metadata, + self: ConnectionMetadata( + metadata: _web3App.metadata, + publicKey: '', + ), + ), ); - final session = _currentSession!.copyWith(magicData: newData); await _setSesionAndChainData(session); } catch (e, s) { - _logger.p( + _logger.d( '[$runtimeType] _onMagicUpdateEvent: $e', stackTrace: s, ); @@ -1350,7 +1491,7 @@ extension _W3MMagicExtension on W3MService { } Future _onMagicErrorEvent(MagicErrorEvent? args) async { - _logger.p('[$runtimeType] _onMagicErrorEvent ${args?.error}'); + _logger.d('[$runtimeType] _onMagicErrorEvent ${args?.error}'); final errorMessage = args?.error ?? 'Something went wrong'; if (!errorMessage.toLowerCase().contains('user denied')) { onModalError.broadcast(ModalError(errorMessage)); @@ -1359,7 +1500,7 @@ extension _W3MMagicExtension on W3MService { } void _onMagicRequest(MagicRequestEvent? args) { - _logger.p('[$runtimeType] _onMagicRequest ${args?.toString()}'); + _logger.d('[$runtimeType] _onMagicRequest ${args?.toString()}'); if (args?.result != null) { closeModal(); } @@ -1376,40 +1517,53 @@ extension _W3MMagicExtension on W3MService { extension _W3MCoinbaseExtension on W3MService { void _onCoinbaseConnectEvent(CoinbaseConnectEvent? args) async { - _logger.p('[$runtimeType] _onCoinbaseConnectEvent $args'); - if (args != null) { - final chainId = _savedChainId(args.data?.chainId.toString()); - final newChainId = chainId ?? '1'; - // final chainId = args.data!.chainId.toString(); + final debugString = jsonEncode(args?.data?.toJson()); + _logger.i('[$runtimeType] _onCoinbaseConnectEvent: $debugString'); + if (args?.data != null) { + final newChainId = _savedChainId('${args!.data!.chainId}')!; final newChain = W3MChainPresets.chains[newChainId]!; _currentSelectedChain = newChain; - final cbData = args.data?.copytWith(chainId: int.tryParse(newChainId)); - final session = W3MSession(coinbaseData: cbData); + // + final session = W3MSession(coinbaseData: args.data!); await _setSesionAndChainData(session); onModalConnect.broadcast(ModalConnect(session)); - if (_isOpen) { - closeModal(); + // + if (siweService.instance!.enabled) { + widgetStack.instance.push(ApproveSIWEPage( + onSiweFinish: _oneSIWEFinish, + )); + } else { + if (_isOpen) { + closeModal(); + } } } } void _onCoinbaseSessionUpdateEvent(CoinbaseSessionEvent? args) async { - _logger.p('[$runtimeType] _onCoinbaseSessionUpdateEvent $args'); + _logger.d('[$runtimeType] _onCoinbaseSessionUpdateEvent $args'); if (args != null) { try { final address = args.address ?? _currentSession!.address!; final chainId = args.chainId ?? _currentSession!.chainId; final newChain = W3MChainPresets.chains[chainId]!; _currentSelectedChain = newChain; - final newData = CoinbaseData( - address: address, - chainName: newChain.chainName, - chainId: int.parse(chainId), + // + final session = _currentSession!.copyWith( + coinbaseData: CoinbaseData( + address: address, + chainName: newChain.chainName, + chainId: int.parse(chainId), + peer: coinbaseService.instance.metadata, + self: ConnectionMetadata( + metadata: _web3App.metadata, + publicKey: await coinbaseService.instance.ownPublicKey, + ), + ), ); - final session = _currentSession!.copyWith(coinbaseData: newData); await _setSesionAndChainData(session); } catch (e, s) { - _logger.p( + _logger.d( '[$runtimeType] _onCoinbaseSessionUpdateEvent: $e', stackTrace: s, ); @@ -1418,7 +1572,7 @@ extension _W3MCoinbaseExtension on W3MService { } void _onCoinbaseErrorEvent(CoinbaseErrorEvent? args) async { - _logger.p('[$runtimeType] _onCoinbaseErrorEvent ${args?.error}'); + _logger.d('[$runtimeType] _onCoinbaseErrorEvent ${args?.error}'); final errorMessage = args?.error ?? 'Something went wrong'; if (!errorMessage.toLowerCase().contains('user denied')) { onModalError.broadcast(ModalError(errorMessage)); @@ -1427,39 +1581,108 @@ extension _W3MCoinbaseExtension on W3MService { } extension _W3MServiceExtension on W3MService { - void _onSessionConnect(SessionConnect? args) async { - _logger.i('[$runtimeType] session connect $args'); + void _onSessionAuthResponse(SessionAuthResponse? args) async { + final debugString = jsonEncode(args?.toJson()); + dev.log('[$runtimeType] _onSessionAuthResponse: $debugString'); if (args != null) { - if (_currentSelectedChain == null) { - final chain = NamespaceUtils.getChainIdsFromNamespaces( - namespaces: args.session.namespaces, - ).first; - final chainId = chain.split(':').last.toString(); - _currentSelectedChain = W3MChainPresets.chains[chainId]; + if (args.session != null) { + // IF 1-CA SUPPORTED WE SHOULD CALL SIWECONGIF METHODS HERE + final session = await _settleSession(args.session!); + final namespace = args.session!.namespaces[StringConstants.namespace]!; + final chains = namespace.chains!.map((c) => c.split(':').last).toList() + ..sort(); + String chainId = _currentSelectedChain?.chainId ?? chains.first; + chainId = W3MChainPresets.chains[chainId]!.namespace; + // + try { + // Verify message with just the first cacao + final Cacao cacao = args.auths!.first; + final message = _web3App.formatAuthMessage( + iss: cacao.p.iss, + cacaoPayload: CacaoRequestPayload.fromCacaoPayload(cacao.p), + ); + final clientId = await _web3App.core.crypto.getClientId(); + await siweService.instance!.verifyMessage( + message: message, + signature: cacao.s.s, + clientId: clientId, + ); + } catch (e) { + await disconnect(); + return; + } + // + final siweSession = await siweService.instance!.getSession(); + final newSession = session.copyWith(siweSession: siweSession); + // + onModalConnect.broadcast(ModalConnect(newSession)); + await _storeSession(newSession); + _notify(); + // + if (_isOpen) { + closeModal(); + } } - final session = W3MSession(sessionData: args.session); - await _setSesionAndChainData(session); - if (_selectedWallet == null) { - analyticsService.instance.sendEvent(ConnectSuccessEvent( - name: 'WalletConnect', - method: AnalyticsPlatform.qrcode, + } + } + + void _onSessionConnect(SessionConnect? args) async { + final siweEnabled = siweService.instance!.enabled; + if (_supportsOneClickAuth && siweEnabled) return; + final debugString = jsonEncode(args?.session.toJson()); + _logger.i('[$runtimeType] _onSessionConnect: $debugString'); + 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) { + widgetStack.instance.push(ApproveSIWEPage( + onSiweFinish: _oneSIWEFinish, )); - storageService.instance.clearKey(StringConstants.recentWalletId); - storageService.instance.clearKey(StringConstants.connectedWalletData); } else { - final walletName = _selectedWallet!.listing.name; - analyticsService.instance.sendEvent(ConnectSuccessEvent( - name: walletName, - method: AnalyticsPlatform.mobile, - )); - } - onModalConnect.broadcast(ModalConnect(session)); - if (_isOpen) { - closeModal(); + if (_isOpen) { + closeModal(); + } } } } + // HAS TO BE CALLED JUST ONCE ON CONNECTION + Future _settleSession(SessionData sessionData) async { + if (_currentSelectedChain == null) { + final chains = NamespaceUtils.getChainIdsFromNamespaces( + namespaces: sessionData.namespaces, + )..sort((a, b) => a.compareTo(b)); + final chainId = chains.first.split(':').last.toString(); + _currentSelectedChain = W3MChainPresets.chains[chainId]; + } + final session = W3MSession(sessionData: sessionData); + await _setSesionAndChainData(session); + if (_selectedWallet == null) { + analyticsService.instance.sendEvent(ConnectSuccessEvent( + name: 'WalletConnect', + method: AnalyticsPlatform.qrcode, + )); + storageService.instance.clearKey(StringConstants.recentWalletId); + storageService.instance.clearKey(StringConstants.connectedWalletData); + } else { + explorerService.instance.storeConnectedWallet(_selectedWallet); + final walletName = _selectedWallet!.listing.name; + analyticsService.instance.sendEvent(ConnectSuccessEvent( + name: walletName, + method: AnalyticsPlatform.mobile, + )); + } + return session; + } + + void _oneSIWEFinish(W3MSession updatedSession) async { + await _storeSession(updatedSession); + onModalUpdate.broadcast(ModalConnect(updatedSession)); + closeModal(); + } + void _onSessionEvent(SessionEvent? args) async { _logger.i('[$runtimeType] session event $args'); onSessionEventEvent.broadcast(args); @@ -1472,6 +1695,12 @@ extension _W3MServiceExtension on W3MService { _currentSelectedChain = null; _notify(); } + } else if (args?.name == EventsConstants.accountsChanged) { + try { + if (siweService.instance!.signOutOnAccountChange) { + await siweService.instance!.signOut(); + } + } catch (_) {} } } @@ -1515,7 +1744,7 @@ extension _W3MServiceExtension on W3MService { } void _onRelayClientError(ErrorEvent? args) { - _logger.i('[$runtimeType] relay client error: $args'); + _logger.i('[$runtimeType] relay client error: ${args?.error}'); final service = _currentSession?.sessionService ?? W3MSessionService.wc; if (service.isWC) { _status = W3MServiceStatus.error; diff --git a/lib/utils/core/core_utils.dart b/lib/utils/core/core_utils.dart index e6d9d5ae..71c20297 100644 --- a/lib/utils/core/core_utils.dart +++ b/lib/utils/core/core_utils.dart @@ -1,22 +1,8 @@ -import 'package:flutter_timezone/flutter_timezone.dart'; import 'package:walletconnect_flutter_v2/walletconnect_flutter_v2.dart'; import 'package:web3modal_flutter/constants/string_constants.dart'; import 'package:web3modal_flutter/utils/core/i_core_utils.dart'; class CoreUtils extends ICoreUtils { - static const restrictedTimezone = [ - 'ASIA/SHANGHAI', - 'ASIA/URUMQI', - 'ASIA/CHONGQING', - 'ASIA/HARBIN', - 'ASIA/KASHGAR', - 'ASIA/MACAU', - 'ASIA/HONG_KONG', - 'ASIA/MACAO', - 'ASIA/BEIJING', - 'ASIA/HARBIN', - ]; - @override bool isValidProjectID(String projectId) { return RegExp(r'^[0-9a-fA-F]{32}$').hasMatch(projectId); @@ -29,44 +15,6 @@ class CoreUtils extends ICoreUtils { .hasMatch(email); } - @override - Future isRestrictedRegion() async { - try { - String tz = await FlutterTimezone.getLocalTimezone(); - tz = tz.toUpperCase(); - return restrictedTimezone.contains(tz); - } catch (e) { - return false; - } - } - - @override - Future getApiUrl() async { - final restricted = await isRestrictedRegion(); - if (restricted) { - return 'https://api.web3modal.org'; - } - return 'https://api.web3modal.com'; - } - - @override - Future getBlockchainApiUrl() async { - final restricted = await isRestrictedRegion(); - if (restricted) { - return 'https://rpc.walletconnect.org'; - } - return 'https://rpc.walletconnect.com'; - } - - @override - Future getAnalyticsUrl() async { - final restricted = await isRestrictedRegion(); - if (restricted) { - return 'https://pulse.walletconnect.org'; - } - return 'https://pulse.walletconnect.com'; - } - @override bool isHttpUrl(String url) { return url.startsWith('http://') || url.startsWith('https://'); diff --git a/lib/utils/core/i_core_utils.dart b/lib/utils/core/i_core_utils.dart index 0872ca74..b705cdb4 100644 --- a/lib/utils/core/i_core_utils.dart +++ b/lib/utils/core/i_core_utils.dart @@ -4,14 +4,6 @@ abstract class ICoreUtils { bool isValidEmail(String email); - Future isRestrictedRegion(); - - Future getApiUrl(); - - Future getBlockchainApiUrl(); - - Future getAnalyticsUrl(); - /// Returns true if the given [url] is a valid HTTP or HTTPS URL. bool isHttpUrl(String url); diff --git a/lib/utils/url/url_utils.dart b/lib/utils/url/url_utils.dart index 4ef4bf6e..b478dea8 100644 --- a/lib/utils/url/url_utils.dart +++ b/lib/utils/url/url_utils.dart @@ -64,9 +64,9 @@ class UrlUtils extends IUrlUtils { } } on FormatException catch (e) { if (id != null) { - loggerService.instance.p('[$runtimeType] $uri ($id): ${e.message}'); + loggerService.instance.d('[$runtimeType] $uri ($id): ${e.message}'); } else { - loggerService.instance.p('[$runtimeType] $uri: ${e.message}'); + loggerService.instance.d('[$runtimeType] $uri: ${e.message}'); } } catch (e) { rethrow; diff --git a/lib/utils/w3m_logger.dart b/lib/utils/w3m_logger.dart index d8dbf7e1..80251461 100644 --- a/lib/utils/w3m_logger.dart +++ b/lib/utils/w3m_logger.dart @@ -1,7 +1,7 @@ import 'package:flutter/foundation.dart'; import 'package:web3modal_flutter/web3modal_flutter.dart'; -// @Deprecated('W3MLoggerUtil is going to be deprecated soon. Don\'t use it') +@Deprecated('W3MLoggerUtil is going to be deprecated soon. Don\'t use it') class W3MLoggerUtil { static Logger logger = Logger( level: Level.off, diff --git a/lib/version.dart b/lib/version.dart index cf4332e2..ec97fd58 100644 --- a/lib/version.dart +++ b/lib/version.dart @@ -1,2 +1,2 @@ // Generated code. Do not modify. -const packageVersion = '3.2.2'; +const packageVersion = '3.3.0-beta01'; diff --git a/lib/web3modal_flutter.dart b/lib/web3modal_flutter.dart index 79b9d0db..67e37ba2 100644 --- a/lib/web3modal_flutter.dart +++ b/lib/web3modal_flutter.dart @@ -7,6 +7,7 @@ export 'package:walletconnect_flutter_v2/walletconnect_flutter_v2.dart'; /// Models export 'models/w3m_chain_info.dart'; export 'models/w3m_wallet_info.dart'; +export 'services/siwe_service/models/w3m_siwe.dart'; /// Utils export 'utils/w3m_chains_presets.dart'; diff --git a/lib/widgets/avatars/w3m_account_avatar.dart b/lib/widgets/avatars/w3m_account_avatar.dart index 08c73025..a6b1d222 100644 --- a/lib/widgets/avatars/w3m_account_avatar.dart +++ b/lib/widgets/avatars/w3m_account_avatar.dart @@ -1,6 +1,7 @@ import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; import 'package:web3modal_flutter/services/w3m_service/i_w3m_service.dart'; +import 'package:web3modal_flutter/utils/core/core_utils_singleton.dart'; import 'package:web3modal_flutter/utils/util.dart'; import 'package:web3modal_flutter/web3modal_flutter.dart'; @@ -53,6 +54,9 @@ class _W3MAccountAvatarState extends State { child: (_avatarUrl ?? '').isNotEmpty ? CachedNetworkImage( imageUrl: _avatarUrl!, + httpHeaders: coreUtils.instance.getAPIHeaders( + widget.service.web3App!.core.projectId, + ), fadeInDuration: const Duration(milliseconds: 500), fadeOutDuration: const Duration(milliseconds: 500), ) diff --git a/lib/widgets/avatars/w3m_wallet_avatar.dart b/lib/widgets/avatars/w3m_wallet_avatar.dart index f3211824..138bcbd3 100644 --- a/lib/widgets/avatars/w3m_wallet_avatar.dart +++ b/lib/widgets/avatars/w3m_wallet_avatar.dart @@ -28,6 +28,31 @@ class W3MListAvatar extends StatelessWidget { final projectId = explorerService.instance.projectId; return Stack( children: [ + AspectRatio( + aspectRatio: 1.0, + child: Container( + decoration: isNetwork + ? ShapeDecoration( + shape: StarBorder.polygon( + side: BorderSide( + color: color ?? themeColors.grayGlass010, + width: 1.0, + strokeAlign: BorderSide.strokeAlignInside, + ), + pointRounding: 0.3, + sides: 6, + ), + ) + : BoxDecoration( + borderRadius: BorderRadius.circular(radius), + border: Border.all( + color: color ?? themeColors.grayGlass010, + width: 1.0, + strokeAlign: BorderSide.strokeAlignOutside, + ), + ), + ), + ), AspectRatio( aspectRatio: 1.0, child: Container( @@ -40,7 +65,6 @@ class W3MListAvatar extends StatelessWidget { ) : BoxDecoration( borderRadius: BorderRadius.circular(radius), - color: themeColors.grayGlass005, ), clipBehavior: Clip.antiAlias, child: (imageUrl ?? '').isNotEmpty @@ -76,31 +100,6 @@ class W3MListAvatar extends StatelessWidget { ), ), ), - AspectRatio( - aspectRatio: 1.0, - child: Container( - decoration: isNetwork - ? ShapeDecoration( - shape: StarBorder.polygon( - side: BorderSide( - color: color ?? themeColors.grayGlass010, - width: 1.0, - strokeAlign: BorderSide.strokeAlignInside, - ), - pointRounding: 0.3, - sides: 6, - ), - ) - : BoxDecoration( - borderRadius: BorderRadius.circular(radius), - border: Border.all( - color: color ?? themeColors.grayGlass010, - width: 1.0, - strokeAlign: BorderSide.strokeAlignInside, - ), - ), - ), - ), ], ); } diff --git a/lib/widgets/buttons/base_button.dart b/lib/widgets/buttons/base_button.dart index 1dc805b1..c218a631 100644 --- a/lib/widgets/buttons/base_button.dart +++ b/lib/widgets/buttons/base_button.dart @@ -3,14 +3,17 @@ import 'package:web3modal_flutter/theme/w3m_theme.dart'; enum BaseButtonSize { small, - regular; + regular, + big; double get height { switch (this) { case small: return 32.0; - default: + case regular: return 40.0; + default: + return 48.0; } } @@ -18,8 +21,10 @@ enum BaseButtonSize { switch (this) { case small: return 16.0; - default: + case regular: return 20.0; + default: + return 24.0; } } } diff --git a/lib/widgets/buttons/primary_button.dart b/lib/widgets/buttons/primary_button.dart index 42dae595..09932051 100644 --- a/lib/widgets/buttons/primary_button.dart +++ b/lib/widgets/buttons/primary_button.dart @@ -5,10 +5,12 @@ import 'package:web3modal_flutter/widgets/buttons/base_button.dart'; class PrimaryButton extends StatelessWidget { final String title; final VoidCallback? onTap; + final bool loading; const PrimaryButton({ super.key, required this.title, this.onTap, + this.loading = false, }); @override @@ -16,9 +18,18 @@ class PrimaryButton extends StatelessWidget { final themeColors = Web3ModalTheme.colorsOf(context); final radiuses = Web3ModalTheme.radiusesOf(context); return BaseButton( - size: BaseButtonSize.regular, - child: Text(title), - onTap: onTap, + size: BaseButtonSize.big, + child: loading + ? SizedBox( + height: BaseButtonSize.big.height * 0.4, + width: BaseButtonSize.big.height * 0.4, + child: CircularProgressIndicator( + color: themeColors.accent100, + strokeWidth: 2.0, + ), + ) + : Text(title), + onTap: loading ? null : onTap, buttonStyle: ButtonStyle( backgroundColor: MaterialStateProperty.resolveWith( (states) { @@ -45,7 +56,7 @@ class PrimaryButton extends StatelessWidget { ), borderRadius: radiuses.isSquare() ? BorderRadius.all(Radius.zero) - : BorderRadius.circular(100.0), + : BorderRadius.circular(16.0), ); }, ), diff --git a/lib/widgets/buttons/secondary_button.dart b/lib/widgets/buttons/secondary_button.dart index 8a42c09e..75916c64 100644 --- a/lib/widgets/buttons/secondary_button.dart +++ b/lib/widgets/buttons/secondary_button.dart @@ -16,7 +16,7 @@ class SecondaryButton extends StatelessWidget { final themeColors = Web3ModalTheme.colorsOf(context); final radiuses = Web3ModalTheme.radiusesOf(context); return BaseButton( - size: BaseButtonSize.regular, + size: BaseButtonSize.big, child: Text(title), onTap: onTap, buttonStyle: ButtonStyle( @@ -35,7 +35,7 @@ class SecondaryButton extends StatelessWidget { ), borderRadius: radiuses.isSquare() ? BorderRadius.all(Radius.zero) - : BorderRadius.circular(100.0), + : BorderRadius.circular(16.0), ); }, ), diff --git a/pubspec.lock b/pubspec.lock index 326e3576..fdd12fb2 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -205,10 +205,10 @@ packages: dependency: "direct main" description: name: coinbase_wallet_sdk - sha256: "0e8d78359a2d8e6e7c57274342bb9d8b7257d4b42961a09399acc50d254aeae8" + sha256: e18bd5351939aacda75c0b525aa9167b945f1206040ec254f971afe96dfd98b0 url: "https://pub.dev" source: hosted - version: "1.0.7" + version: "1.0.8" collection: dependency: "direct main" description: @@ -1081,10 +1081,10 @@ packages: dependency: "direct main" description: name: walletconnect_flutter_v2 - sha256: cc6fa6a537910a66258ee64bb510edbfc0dee01485ea1138651431087c94671b + sha256: "7f6f66038ce0f559c5661c21b6485f0abfe42354605a628c2fb571a02ec1386e" url: "https://pub.dev" source: hosted - version: "2.2.3" + version: "2.3.0-beta03" watcher: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index d1a0da9d..1b543d31 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: web3modal_flutter description: "WalletConnect Web3Modal: Simple, intuitive wallet login. With this drop-in UI SDK, enable any wallet's users to seamlessly log in to your app and enjoy a unified experience" -version: 3.2.2 +version: 3.3.0-beta01 repository: https://github.com/WalletConnect/Web3ModalFlutter environment: @@ -9,7 +9,7 @@ environment: dependencies: appcheck: ^1.0.6 cached_network_image: ^3.3.1 - coinbase_wallet_sdk: ^1.0.7 + coinbase_wallet_sdk: ^1.0.8 collection: ^1.17.2 convert: ^3.1.1 cupertino_icons: ^1.0.2 @@ -25,7 +25,7 @@ dependencies: shimmer: ^3.0.0 url_launcher: ^6.2.5 uuid: ^4.3.3 - walletconnect_flutter_v2: ^2.2.3 + walletconnect_flutter_v2: ^2.3.0-beta03 webview_flutter: ^4.7.0 webview_flutter_android: ^3.16.0 webview_flutter_wkwebview: ^3.13.0 diff --git a/test/mock_classes.dart b/test/mock_classes.dart index 4430bda6..3086bdc7 100644 --- a/test/mock_classes.dart +++ b/test/mock_classes.dart @@ -2,7 +2,7 @@ import 'package:flutter/material.dart'; import 'package:walletconnect_flutter_v2/apis/core/relay_client/relay_client.dart'; import 'package:mockito/annotations.dart'; import 'package:http/http.dart' as http; -import 'package:web3modal_flutter/services/blockchain_api_service/blockchain_api_utils.dart'; +import 'package:web3modal_flutter/services/blockchain_service/blockchain_service.dart'; import 'package:web3modal_flutter/services/explorer_service/explorer_service.dart'; import 'package:web3modal_flutter/services/ledger_service/ledger_service.dart'; import 'package:web3modal_flutter/services/network_service/network_service.dart'; @@ -26,7 +26,7 @@ import 'mock_classes.mocks.dart'; RelayClient, http.Client, NetworkService, - BlockchainApiUtils, + BlockChainService, LedgerService, StorageService, WidgetStack, diff --git a/test/mock_classes.mocks.dart b/test/mock_classes.mocks.dart index 1b473d2e..0e7db331 100644 --- a/test/mock_classes.mocks.dart +++ b/test/mock_classes.mocks.dart @@ -4,57 +4,55 @@ // ignore_for_file: no_leading_underscores_for_library_prefixes import 'dart:async' as _i14; -import 'dart:convert' as _i27; -import 'dart:typed_data' as _i28; -import 'dart:ui' as _i17; +import 'dart:convert' as _i26; +import 'dart:typed_data' as _i27; +import 'dart:ui' as _i16; import 'package:flutter/foundation.dart' as _i2; import 'package:flutter/material.dart' as _i11; import 'package:http/http.dart' as _i9; import 'package:mockito/mockito.dart' as _i1; import 'package:mockito/src/dummies.dart' as _i13; -import 'package:url_launcher/url_launcher.dart' as _i19; +import 'package:url_launcher/url_launcher.dart' as _i18; import 'package:walletconnect_flutter_v2/apis/core/relay_client/i_message_tracker.dart' as _i7; import 'package:walletconnect_flutter_v2/apis/core/relay_client/json_rpc_2/src/peer.dart' - as _i26; -import 'package:walletconnect_flutter_v2/apis/core/relay_client/relay_client.dart' as _i25; +import 'package:walletconnect_flutter_v2/apis/core/relay_client/relay_client.dart' + as _i24; import 'package:walletconnect_flutter_v2/apis/core/relay_client/websocket/i_websocket_handler.dart' as _i8; import 'package:walletconnect_flutter_v2/apis/core/store/i_generic_store.dart' as _i4; import 'package:walletconnect_flutter_v2/apis/core/store/i_store.dart' as _i6; import 'package:walletconnect_flutter_v2/apis/sign_api/i_sessions.dart' as _i5; -import 'package:web3modal_flutter/models/grid_item.dart' as _i30; +import 'package:web3modal_flutter/models/grid_item.dart' as _i29; import 'package:web3modal_flutter/services/analytics_service/models/analytics_event.dart' - as _i35; -import 'package:web3modal_flutter/services/blockchain_api_service/blockchain_api_utils.dart' - as _i31; -import 'package:web3modal_flutter/services/blockchain_api_service/blockchain_identity.dart' + as _i34; +import 'package:web3modal_flutter/services/blockchain_service/blockchain_service.dart' + as _i30; +import 'package:web3modal_flutter/services/blockchain_service/models/blockchain_identity.dart' as _i10; -import 'package:web3modal_flutter/services/coinbase_service/models/coinbase_events.dart' - as _i16; import 'package:web3modal_flutter/services/explorer_service/explorer_service.dart' as _i12; import 'package:web3modal_flutter/services/explorer_service/models/redirect.dart' - as _i20; + as _i19; import 'package:web3modal_flutter/services/ledger_service/ledger_service.dart' - as _i32; + as _i31; import 'package:web3modal_flutter/services/network_service/network_service.dart' - as _i29; + as _i28; import 'package:web3modal_flutter/services/storage_service/storage_service.dart' - as _i33; + as _i32; import 'package:web3modal_flutter/services/w3m_service/i_w3m_service.dart' as _i15; -import 'package:web3modal_flutter/utils/platform/i_platform_utils.dart' as _i21; -import 'package:web3modal_flutter/utils/platform/platform_utils.dart' as _i22; -import 'package:web3modal_flutter/utils/toast/toast_message.dart' as _i24; -import 'package:web3modal_flutter/utils/toast/toast_utils.dart' as _i23; -import 'package:web3modal_flutter/utils/url/url_utils.dart' as _i18; +import 'package:web3modal_flutter/utils/platform/i_platform_utils.dart' as _i20; +import 'package:web3modal_flutter/utils/platform/platform_utils.dart' as _i21; +import 'package:web3modal_flutter/utils/toast/toast_message.dart' as _i23; +import 'package:web3modal_flutter/utils/toast/toast_utils.dart' as _i22; +import 'package:web3modal_flutter/utils/url/url_utils.dart' as _i17; import 'package:web3modal_flutter/web3modal_flutter.dart' as _i3; import 'package:web3modal_flutter/widgets/widget_stack/widget_stack.dart' - as _i34; + as _i33; // ignore_for_file: type=lint // ignore_for_file: avoid_redundant_argument_values @@ -185,8 +183,9 @@ class _FakeAuthRequestResponse_10 extends _i1.SmartFake ); } -class _FakeIStore_11 extends _i1.SmartFake implements _i6.IStore { - _FakeIStore_11( +class _FakeSessionAuthRequestResponse_11 extends _i1.SmartFake + implements _i3.SessionAuthRequestResponse { + _FakeSessionAuthRequestResponse_11( Object parent, Invocation parentInvocation, ) : super( @@ -195,8 +194,8 @@ class _FakeIStore_11 extends _i1.SmartFake implements _i6.IStore { ); } -class _FakeSessionData_12 extends _i1.SmartFake implements _i3.SessionData { - _FakeSessionData_12( +class _FakeIStore_12 extends _i1.SmartFake implements _i6.IStore { + _FakeIStore_12( Object parent, Invocation parentInvocation, ) : super( @@ -205,9 +204,19 @@ class _FakeSessionData_12 extends _i1.SmartFake implements _i3.SessionData { ); } -class _FakeIMessageTracker_13 extends _i1.SmartFake +class _FakeSessionData_13 extends _i1.SmartFake implements _i3.SessionData { + _FakeSessionData_13( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeIMessageTracker_14 extends _i1.SmartFake implements _i7.IMessageTracker { - _FakeIMessageTracker_13( + _FakeIMessageTracker_14( Object parent, Invocation parentInvocation, ) : super( @@ -216,9 +225,9 @@ class _FakeIMessageTracker_13 extends _i1.SmartFake ); } -class _FakeIWebSocketHandler_14 extends _i1.SmartFake +class _FakeIWebSocketHandler_15 extends _i1.SmartFake implements _i8.IWebSocketHandler { - _FakeIWebSocketHandler_14( + _FakeIWebSocketHandler_15( Object parent, Invocation parentInvocation, ) : super( @@ -227,8 +236,8 @@ class _FakeIWebSocketHandler_14 extends _i1.SmartFake ); } -class _FakeResponse_15 extends _i1.SmartFake implements _i9.Response { - _FakeResponse_15( +class _FakeResponse_16 extends _i1.SmartFake implements _i9.Response { + _FakeResponse_16( Object parent, Invocation parentInvocation, ) : super( @@ -237,9 +246,9 @@ class _FakeResponse_15 extends _i1.SmartFake implements _i9.Response { ); } -class _FakeStreamedResponse_16 extends _i1.SmartFake +class _FakeStreamedResponse_17 extends _i1.SmartFake implements _i9.StreamedResponse { - _FakeStreamedResponse_16( + _FakeStreamedResponse_17( Object parent, Invocation parentInvocation, ) : super( @@ -248,9 +257,9 @@ class _FakeStreamedResponse_16 extends _i1.SmartFake ); } -class _FakeBlockchainIdentity_17 extends _i1.SmartFake +class _FakeBlockchainIdentity_18 extends _i1.SmartFake implements _i10.BlockchainIdentity { - _FakeBlockchainIdentity_17( + _FakeBlockchainIdentity_18( Object parent, Invocation parentInvocation, ) : super( @@ -259,8 +268,8 @@ class _FakeBlockchainIdentity_17 extends _i1.SmartFake ); } -class _FakeWidget_18 extends _i1.SmartFake implements _i11.Widget { - _FakeWidget_18( +class _FakeWidget_19 extends _i1.SmartFake implements _i11.Widget { + _FakeWidget_19( Object parent, Invocation parentInvocation, ) : super( @@ -504,6 +513,14 @@ class MockW3MService extends _i1.Mock implements _i3.W3MService { ), ) as _i3.Event<_i3.ModalConnect>); @override + _i3.Event<_i3.ModalConnect> get onModalUpdate => (super.noSuchMethod( + Invocation.getter(#onModalUpdate), + returnValue: _FakeEvent_1<_i3.ModalConnect>( + this, + Invocation.getter(#onModalUpdate), + ), + ) as _i3.Event<_i3.ModalConnect>); + @override _i3.Event<_i3.ModalNetworkChange> get onModalNetworkChange => (super.noSuchMethod( Invocation.getter(#onModalNetworkChange), @@ -586,70 +603,6 @@ class MockW3MService extends _i1.Mock implements _i3.W3MService { returnValue: false, ) as bool); @override - _i3.Event<_i16.CoinbaseConnectEvent> get onCoinbaseConnect => - (super.noSuchMethod( - Invocation.getter(#onCoinbaseConnect), - returnValue: _FakeEvent_1<_i16.CoinbaseConnectEvent>( - this, - Invocation.getter(#onCoinbaseConnect), - ), - ) as _i3.Event<_i16.CoinbaseConnectEvent>); - @override - set onCoinbaseConnect( - _i3.Event<_i16.CoinbaseConnectEvent>? _onCoinbaseConnect) => - super.noSuchMethod( - Invocation.setter( - #onCoinbaseConnect, - _onCoinbaseConnect, - ), - returnValueForMissingStub: null, - ); - @override - _i3.Event<_i16.CoinbaseErrorEvent> get onCoinbaseError => (super.noSuchMethod( - Invocation.getter(#onCoinbaseError), - returnValue: _FakeEvent_1<_i16.CoinbaseErrorEvent>( - this, - Invocation.getter(#onCoinbaseError), - ), - ) as _i3.Event<_i16.CoinbaseErrorEvent>); - @override - set onCoinbaseError(_i3.Event<_i16.CoinbaseErrorEvent>? _onCoinbaseError) => - super.noSuchMethod( - Invocation.setter( - #onCoinbaseError, - _onCoinbaseError, - ), - returnValueForMissingStub: null, - ); - @override - _i3.Event<_i16.CoinbaseSessionEvent> get onCoinbaseSessionUpdate => - (super.noSuchMethod( - Invocation.getter(#onCoinbaseSessionUpdate), - returnValue: _FakeEvent_1<_i16.CoinbaseSessionEvent>( - this, - Invocation.getter(#onCoinbaseSessionUpdate), - ), - ) as _i3.Event<_i16.CoinbaseSessionEvent>); - @override - set onCoinbaseSessionUpdate( - _i3.Event<_i16.CoinbaseSessionEvent>? _onCoinbaseSessionUpdate) => - super.noSuchMethod( - Invocation.setter( - #onCoinbaseSessionUpdate, - _onCoinbaseSessionUpdate, - ), - returnValueForMissingStub: null, - ); - @override - _i3.Event<_i16.CoinbaseResponseEvent> get onCoinbaseResponse => - (super.noSuchMethod( - Invocation.getter(#onCoinbaseResponse), - returnValue: _FakeEvent_1<_i16.CoinbaseResponseEvent>( - this, - Invocation.getter(#onCoinbaseResponse), - ), - ) as _i3.Event<_i16.CoinbaseResponseEvent>); - @override _i14.Future init() => (super.noSuchMethod( Invocation.method( #init, @@ -723,6 +676,21 @@ class MockW3MService extends _i1.Mock implements _i3.W3MService { returnValueForMissingStub: _i14.Future.value(), ) as _i14.Future); @override + String formatMessage(_i3.SIWECreateMessageArgs? params) => + (super.noSuchMethod( + Invocation.method( + #formatMessage, + [params], + ), + returnValue: _i13.dummyValue( + this, + Invocation.method( + #formatMessage, + [params], + ), + ), + ) as String); + @override _i14.Future buildConnectionUri() => (super.noSuchMethod( Invocation.method( #buildConnectionUri, @@ -760,10 +728,11 @@ class MockW3MService extends _i1.Mock implements _i3.W3MService { returnValueForMissingStub: _i14.Future.value(), ) as _i14.Future); @override - void closeModal() => super.noSuchMethod( + void closeModal({bool? disconnectSession = false}) => super.noSuchMethod( Invocation.method( #closeModal, [], + {#disconnectSession: disconnectSession}, ), returnValueForMissingStub: null, ); @@ -787,7 +756,6 @@ class MockW3MService extends _i1.Mock implements _i3.W3MService { _i14.Future> requestReadContract({ required _i3.DeployedContract? deployedContract, required String? functionName, - String? rpcUrl, List? parameters = const [], }) => (super.noSuchMethod( @@ -797,7 +765,6 @@ class MockW3MService extends _i1.Mock implements _i3.W3MService { { #deployedContract: deployedContract, #functionName: functionName, - #rpcUrl: rpcUrl, #parameters: parameters, }, ), @@ -807,7 +774,6 @@ class MockW3MService extends _i1.Mock implements _i3.W3MService { _i14.Future requestWriteContract({ required String? topic, required String? chainId, - String? rpcUrl, required _i3.DeployedContract? deployedContract, required String? functionName, required _i3.Transaction? transaction, @@ -821,7 +787,6 @@ class MockW3MService extends _i1.Mock implements _i3.W3MService { { #topic: topic, #chainId: chainId, - #rpcUrl: rpcUrl, #deployedContract: deployedContract, #functionName: functionName, #transaction: transaction, @@ -890,7 +855,7 @@ class MockW3MService extends _i1.Mock implements _i3.W3MService { returnValueForMissingStub: _i14.Future.value(), ) as _i14.Future); @override - void addListener(_i17.VoidCallback? listener) => super.noSuchMethod( + void addListener(_i16.VoidCallback? listener) => super.noSuchMethod( Invocation.method( #addListener, [listener], @@ -898,7 +863,7 @@ class MockW3MService extends _i1.Mock implements _i3.W3MService { returnValueForMissingStub: null, ); @override - void removeListener(_i17.VoidCallback? listener) => super.noSuchMethod( + void removeListener(_i16.VoidCallback? listener) => super.noSuchMethod( Invocation.method( #removeListener, [listener], @@ -913,79 +878,12 @@ class MockW3MService extends _i1.Mock implements _i3.W3MService { ), returnValueForMissingStub: null, ); - @override - _i14.Future cbInit({ - required _i3.PairingMetadata? metadata, - _i3.W3MWalletInfo? cbWallet, - }) => - (super.noSuchMethod( - Invocation.method( - #cbInit, - [], - { - #metadata: metadata, - #cbWallet: cbWallet, - }, - ), - returnValue: _i14.Future.value(), - returnValueForMissingStub: _i14.Future.value(), - ) as _i14.Future); - @override - _i14.Future cbGetAccount() => (super.noSuchMethod( - Invocation.method( - #cbGetAccount, - [], - ), - returnValue: _i14.Future.value(), - returnValueForMissingStub: _i14.Future.value(), - ) as _i14.Future); - @override - _i14.Future cbRequest({ - required String? chainId, - required _i3.SessionRequestParams? request, - }) => - (super.noSuchMethod( - Invocation.method( - #cbRequest, - [], - { - #chainId: chainId, - #request: request, - }, - ), - returnValue: _i14.Future.value(), - ) as _i14.Future); - @override - _i14.Future cbIsInstalled() => (super.noSuchMethod( - Invocation.method( - #cbIsInstalled, - [], - ), - returnValue: _i14.Future.value(false), - ) as _i14.Future); - @override - _i14.Future cbIsConnected() => (super.noSuchMethod( - Invocation.method( - #cbIsConnected, - [], - ), - returnValue: _i14.Future.value(false), - ) as _i14.Future); - @override - _i14.Future cbResetSession() => (super.noSuchMethod( - Invocation.method( - #cbResetSession, - [], - ), - returnValue: _i14.Future.value(), - returnValueForMissingStub: _i14.Future.value(), - ) as _i14.Future); } /// A class which mocks [UrlUtils]. /// /// See the documentation for Mockito's code generation for more information. -class MockUrlUtils extends _i1.Mock implements _i18.UrlUtils { +class MockUrlUtils extends _i1.Mock implements _i17.UrlUtils { MockUrlUtils() { _i1.throwOnMissingStub(this); } @@ -998,17 +896,17 @@ class MockUrlUtils extends _i1.Mock implements _i18.UrlUtils { @override _i14.Future Function( Uri, { - _i19.LaunchMode? mode, + _i18.LaunchMode? mode, }) get launchUrlFunc => (super.noSuchMethod( Invocation.getter(#launchUrlFunc), returnValue: ( Uri url, { - _i19.LaunchMode? mode, + _i18.LaunchMode? mode, }) => _i14.Future.value(false), ) as _i14.Future Function( Uri, { - _i19.LaunchMode? mode, + _i18.LaunchMode? mode, })); @override _i14.Future Function(Uri) get canLaunchUrlFunc => (super.noSuchMethod( @@ -1031,7 +929,7 @@ class MockUrlUtils extends _i1.Mock implements _i18.UrlUtils { @override _i14.Future launchUrl( Uri? url, { - _i19.LaunchMode? mode, + _i18.LaunchMode? mode, }) => (super.noSuchMethod( Invocation.method( @@ -1043,9 +941,9 @@ class MockUrlUtils extends _i1.Mock implements _i18.UrlUtils { ) as _i14.Future); @override _i14.Future openRedirect( - _i20.WalletRedirect? redirect, { + _i19.WalletRedirect? redirect, { String? wcURI, - _i21.PlatformType? pType, + _i20.PlatformType? pType, }) => (super.noSuchMethod( Invocation.method( @@ -1063,27 +961,27 @@ class MockUrlUtils extends _i1.Mock implements _i18.UrlUtils { /// A class which mocks [PlatformUtils]. /// /// See the documentation for Mockito's code generation for more information. -class MockPlatformUtils extends _i1.Mock implements _i22.PlatformUtils { +class MockPlatformUtils extends _i1.Mock implements _i21.PlatformUtils { MockPlatformUtils() { _i1.throwOnMissingStub(this); } @override - _i21.PlatformExact getPlatformExact() => (super.noSuchMethod( + _i20.PlatformExact getPlatformExact() => (super.noSuchMethod( Invocation.method( #getPlatformExact, [], ), - returnValue: _i21.PlatformExact.iOS, - ) as _i21.PlatformExact); + returnValue: _i20.PlatformExact.iOS, + ) as _i20.PlatformExact); @override - _i21.PlatformType getPlatformType() => (super.noSuchMethod( + _i20.PlatformType getPlatformType() => (super.noSuchMethod( Invocation.method( #getPlatformType, [], ), - returnValue: _i21.PlatformType.mobile, - ) as _i21.PlatformType); + returnValue: _i20.PlatformType.mobile, + ) as _i20.PlatformType); @override bool canDetectInstalledApps() => (super.noSuchMethod( Invocation.method( @@ -1129,18 +1027,18 @@ class MockPlatformUtils extends _i1.Mock implements _i22.PlatformUtils { /// A class which mocks [ToastUtils]. /// /// See the documentation for Mockito's code generation for more information. -class MockToastUtils extends _i1.Mock implements _i23.ToastUtils { +class MockToastUtils extends _i1.Mock implements _i22.ToastUtils { MockToastUtils() { _i1.throwOnMissingStub(this); } @override - _i14.Stream<_i24.ToastMessage?> get toasts => (super.noSuchMethod( + _i14.Stream<_i23.ToastMessage?> get toasts => (super.noSuchMethod( Invocation.getter(#toasts), - returnValue: _i14.Stream<_i24.ToastMessage?>.empty(), - ) as _i14.Stream<_i24.ToastMessage?>); + returnValue: _i14.Stream<_i23.ToastMessage?>.empty(), + ) as _i14.Stream<_i23.ToastMessage?>); @override - void show(_i24.ToastMessage? message) => super.noSuchMethod( + void show(_i23.ToastMessage? message) => super.noSuchMethod( Invocation.method( #show, [message], @@ -1358,6 +1256,15 @@ class MockWeb3App extends _i1.Mock implements _i3.Web3App { ), ) as _i4.IGenericStore<_i3.StoredCacao>); @override + _i3.Event<_i3.SessionAuthResponse> get onSessionAuthResponse => + (super.noSuchMethod( + Invocation.getter(#onSessionAuthResponse), + returnValue: _FakeEvent_1<_i3.SessionAuthResponse>( + this, + Invocation.getter(#onSessionAuthResponse), + ), + ) as _i3.Event<_i3.SessionAuthResponse>); + @override _i14.Future init() => (super.noSuchMethod( Invocation.method( #init, @@ -1601,6 +1508,54 @@ class MockWeb3App extends _i1.Mock implements _i3.Web3App { returnValue: {}, ) as Map); @override + _i14.Future<_i3.SessionAuthRequestResponse> authenticate({ + required _i3.SessionAuthRequestParams? params, + String? pairingTopic, + List>? methods = const [ + [r'wc_sessionAuthenticate'] + ], + }) => + (super.noSuchMethod( + Invocation.method( + #authenticate, + [], + { + #params: params, + #pairingTopic: pairingTopic, + #methods: methods, + }, + ), + returnValue: _i14.Future<_i3.SessionAuthRequestResponse>.value( + _FakeSessionAuthRequestResponse_11( + this, + Invocation.method( + #authenticate, + [], + { + #params: params, + #pairingTopic: pairingTopic, + #methods: methods, + }, + ), + )), + ) as _i14.Future<_i3.SessionAuthRequestResponse>); + @override + _i14.Future validateSignedCacao({ + required _i3.Cacao? cacao, + required String? projectId, + }) => + (super.noSuchMethod( + Invocation.method( + #validateSignedCacao, + [], + { + #cacao: cacao, + #projectId: projectId, + }, + ), + returnValue: _i14.Future.value(false), + ) as _i14.Future); + @override String formatAuthMessage({ required String? iss, required _i3.CacaoRequestPayload? cacaoPayload, @@ -1655,7 +1610,7 @@ class MockSessions extends _i1.Mock implements _i3.Sessions { @override _i6.IStore get storage => (super.noSuchMethod( Invocation.getter(#storage), - returnValue: _FakeIStore_11( + returnValue: _FakeIStore_12( this, Invocation.getter(#storage), ), @@ -1711,7 +1666,7 @@ class MockSessions extends _i1.Mock implements _i3.Sessions { @override _i3.SessionData Function(dynamic) get fromJson => (super.noSuchMethod( Invocation.getter(#fromJson), - returnValue: (dynamic __p0) => _FakeSessionData_12( + returnValue: (dynamic __p0) => _FakeSessionData_13( this, Invocation.getter(#fromJson), ), @@ -1828,7 +1783,7 @@ class MockSessions extends _i1.Mock implements _i3.Sessions { /// A class which mocks [RelayClient]. /// /// See the documentation for Mockito's code generation for more information. -class MockRelayClient extends _i1.Mock implements _i25.RelayClient { +class MockRelayClient extends _i1.Mock implements _i24.RelayClient { MockRelayClient() { _i1.throwOnMissingStub(this); } @@ -1901,7 +1856,7 @@ class MockRelayClient extends _i1.Mock implements _i25.RelayClient { ), ) as _i3.Event<_i3.EventArgs>); @override - set jsonRPC(_i26.Peer? _jsonRPC) => super.noSuchMethod( + set jsonRPC(_i25.Peer? _jsonRPC) => super.noSuchMethod( Invocation.setter( #jsonRPC, _jsonRPC, @@ -1927,7 +1882,7 @@ class MockRelayClient extends _i1.Mock implements _i25.RelayClient { @override _i7.IMessageTracker get messageTracker => (super.noSuchMethod( Invocation.getter(#messageTracker), - returnValue: _FakeIMessageTracker_13( + returnValue: _FakeIMessageTracker_14( this, Invocation.getter(#messageTracker), ), @@ -1960,7 +1915,7 @@ class MockRelayClient extends _i1.Mock implements _i25.RelayClient { @override _i8.IWebSocketHandler get socketHandler => (super.noSuchMethod( Invocation.getter(#socketHandler), - returnValue: _FakeIWebSocketHandler_14( + returnValue: _FakeIWebSocketHandler_15( this, Invocation.getter(#socketHandler), ), @@ -2099,7 +2054,7 @@ class MockClient extends _i1.Mock implements _i9.Client { [url], {#headers: headers}, ), - returnValue: _i14.Future<_i9.Response>.value(_FakeResponse_15( + returnValue: _i14.Future<_i9.Response>.value(_FakeResponse_16( this, Invocation.method( #head, @@ -2119,7 +2074,7 @@ class MockClient extends _i1.Mock implements _i9.Client { [url], {#headers: headers}, ), - returnValue: _i14.Future<_i9.Response>.value(_FakeResponse_15( + returnValue: _i14.Future<_i9.Response>.value(_FakeResponse_16( this, Invocation.method( #get, @@ -2133,7 +2088,7 @@ class MockClient extends _i1.Mock implements _i9.Client { Uri? url, { Map? headers, Object? body, - _i27.Encoding? encoding, + _i26.Encoding? encoding, }) => (super.noSuchMethod( Invocation.method( @@ -2145,7 +2100,7 @@ class MockClient extends _i1.Mock implements _i9.Client { #encoding: encoding, }, ), - returnValue: _i14.Future<_i9.Response>.value(_FakeResponse_15( + returnValue: _i14.Future<_i9.Response>.value(_FakeResponse_16( this, Invocation.method( #post, @@ -2163,7 +2118,7 @@ class MockClient extends _i1.Mock implements _i9.Client { Uri? url, { Map? headers, Object? body, - _i27.Encoding? encoding, + _i26.Encoding? encoding, }) => (super.noSuchMethod( Invocation.method( @@ -2175,7 +2130,7 @@ class MockClient extends _i1.Mock implements _i9.Client { #encoding: encoding, }, ), - returnValue: _i14.Future<_i9.Response>.value(_FakeResponse_15( + returnValue: _i14.Future<_i9.Response>.value(_FakeResponse_16( this, Invocation.method( #put, @@ -2193,7 +2148,7 @@ class MockClient extends _i1.Mock implements _i9.Client { Uri? url, { Map? headers, Object? body, - _i27.Encoding? encoding, + _i26.Encoding? encoding, }) => (super.noSuchMethod( Invocation.method( @@ -2205,7 +2160,7 @@ class MockClient extends _i1.Mock implements _i9.Client { #encoding: encoding, }, ), - returnValue: _i14.Future<_i9.Response>.value(_FakeResponse_15( + returnValue: _i14.Future<_i9.Response>.value(_FakeResponse_16( this, Invocation.method( #patch, @@ -2223,7 +2178,7 @@ class MockClient extends _i1.Mock implements _i9.Client { Uri? url, { Map? headers, Object? body, - _i27.Encoding? encoding, + _i26.Encoding? encoding, }) => (super.noSuchMethod( Invocation.method( @@ -2235,7 +2190,7 @@ class MockClient extends _i1.Mock implements _i9.Client { #encoding: encoding, }, ), - returnValue: _i14.Future<_i9.Response>.value(_FakeResponse_15( + returnValue: _i14.Future<_i9.Response>.value(_FakeResponse_16( this, Invocation.method( #delete, @@ -2269,7 +2224,7 @@ class MockClient extends _i1.Mock implements _i9.Client { )), ) as _i14.Future); @override - _i14.Future<_i28.Uint8List> readBytes( + _i14.Future<_i27.Uint8List> readBytes( Uri? url, { Map? headers, }) => @@ -2279,8 +2234,8 @@ class MockClient extends _i1.Mock implements _i9.Client { [url], {#headers: headers}, ), - returnValue: _i14.Future<_i28.Uint8List>.value(_i28.Uint8List(0)), - ) as _i14.Future<_i28.Uint8List>); + returnValue: _i14.Future<_i27.Uint8List>.value(_i27.Uint8List(0)), + ) as _i14.Future<_i27.Uint8List>); @override _i14.Future<_i9.StreamedResponse> send(_i9.BaseRequest? request) => (super.noSuchMethod( @@ -2289,7 +2244,7 @@ class MockClient extends _i1.Mock implements _i9.Client { [request], ), returnValue: - _i14.Future<_i9.StreamedResponse>.value(_FakeStreamedResponse_16( + _i14.Future<_i9.StreamedResponse>.value(_FakeStreamedResponse_17( this, Invocation.method( #send, @@ -2310,7 +2265,7 @@ class MockClient extends _i1.Mock implements _i9.Client { /// A class which mocks [NetworkService]. /// /// See the documentation for Mockito's code generation for more information. -class MockNetworkService extends _i1.Mock implements _i29.NetworkService { +class MockNetworkService extends _i1.Mock implements _i28.NetworkService { MockNetworkService() { _i1.throwOnMissingStub(this); } @@ -2332,14 +2287,14 @@ class MockNetworkService extends _i1.Mock implements _i29.NetworkService { returnValueForMissingStub: null, ); @override - List<_i30.GridItem<_i3.W3MChainInfo>> get itemListComplete => + List<_i29.GridItem<_i3.W3MChainInfo>> get itemListComplete => (super.noSuchMethod( Invocation.getter(#itemListComplete), - returnValue: <_i30.GridItem<_i3.W3MChainInfo>>[], - ) as List<_i30.GridItem<_i3.W3MChainInfo>>); + returnValue: <_i29.GridItem<_i3.W3MChainInfo>>[], + ) as List<_i29.GridItem<_i3.W3MChainInfo>>); @override set itemListComplete( - List<_i30.GridItem<_i3.W3MChainInfo>>? _itemListComplete) => + List<_i29.GridItem<_i3.W3MChainInfo>>? _itemListComplete) => super.noSuchMethod( Invocation.setter( #itemListComplete, @@ -2348,18 +2303,18 @@ class MockNetworkService extends _i1.Mock implements _i29.NetworkService { returnValueForMissingStub: null, ); @override - _i2.ValueNotifier>> get itemList => + _i2.ValueNotifier>> get itemList => (super.noSuchMethod( Invocation.getter(#itemList), returnValue: - _FakeValueNotifier_0>>( + _FakeValueNotifier_0>>( this, Invocation.getter(#itemList), ), - ) as _i2.ValueNotifier>>); + ) as _i2.ValueNotifier>>); @override set itemList( - _i2.ValueNotifier>>? + _i2.ValueNotifier>>? _itemList) => super.noSuchMethod( Invocation.setter( @@ -2388,12 +2343,11 @@ class MockNetworkService extends _i1.Mock implements _i29.NetworkService { ); } -/// A class which mocks [BlockchainApiUtils]. +/// A class which mocks [BlockChainService]. /// /// See the documentation for Mockito's code generation for more information. -class MockBlockchainApiUtils extends _i1.Mock - implements _i31.BlockchainApiUtils { - MockBlockchainApiUtils() { +class MockBlockChainService extends _i1.Mock implements _i30.BlockChainService { + MockBlockChainService() { _i1.throwOnMissingStub(this); } @@ -2419,7 +2373,7 @@ class MockBlockchainApiUtils extends _i1.Mock ], ), returnValue: _i14.Future<_i10.BlockchainIdentity>.value( - _FakeBlockchainIdentity_17( + _FakeBlockchainIdentity_18( this, Invocation.method( #getIdentity, @@ -2435,7 +2389,7 @@ class MockBlockchainApiUtils extends _i1.Mock /// A class which mocks [LedgerService]. /// /// See the documentation for Mockito's code generation for more information. -class MockLedgerService extends _i1.Mock implements _i32.LedgerService { +class MockLedgerService extends _i1.Mock implements _i31.LedgerService { MockLedgerService() { _i1.throwOnMissingStub(this); } @@ -2508,7 +2462,7 @@ class MockLedgerService extends _i1.Mock implements _i32.LedgerService { /// A class which mocks [StorageService]. /// /// See the documentation for Mockito's code generation for more information. -class MockStorageService extends _i1.Mock implements _i33.StorageService { +class MockStorageService extends _i1.Mock implements _i32.StorageService { MockStorageService() { _i1.throwOnMissingStub(this); } @@ -2569,7 +2523,7 @@ class MockStorageService extends _i1.Mock implements _i33.StorageService { /// A class which mocks [WidgetStack]. /// /// See the documentation for Mockito's code generation for more information. -class MockWidgetStack extends _i1.Mock implements _i34.WidgetStack { +class MockWidgetStack extends _i1.Mock implements _i33.WidgetStack { MockWidgetStack() { _i1.throwOnMissingStub(this); } @@ -2593,7 +2547,7 @@ class MockWidgetStack extends _i1.Mock implements _i34.WidgetStack { #getCurrent, [], ), - returnValue: _FakeWidget_18( + returnValue: _FakeWidget_19( this, Invocation.method( #getCurrent, @@ -2605,7 +2559,7 @@ class MockWidgetStack extends _i1.Mock implements _i34.WidgetStack { void push( _i11.Widget? widget, { bool? renderScreen = false, - _i35.AnalyticsEvent? event, + _i34.AnalyticsEvent? event, }) => super.noSuchMethod( Invocation.method( @@ -2680,7 +2634,7 @@ class MockWidgetStack extends _i1.Mock implements _i34.WidgetStack { returnValueForMissingStub: null, ); @override - void addListener(_i17.VoidCallback? listener) => super.noSuchMethod( + void addListener(_i16.VoidCallback? listener) => super.noSuchMethod( Invocation.method( #addListener, [listener], @@ -2688,7 +2642,7 @@ class MockWidgetStack extends _i1.Mock implements _i34.WidgetStack { returnValueForMissingStub: null, ); @override - void removeListener(_i17.VoidCallback? listener) => super.noSuchMethod( + void removeListener(_i16.VoidCallback? listener) => super.noSuchMethod( Invocation.method( #removeListener, [listener], diff --git a/test/w3m_service/w3m_service_unit_test.dart b/test/w3m_service/w3m_service_unit_test.dart index 2ee79285..c3fec83d 100644 --- a/test/w3m_service/w3m_service_unit_test.dart +++ b/test/w3m_service/w3m_service_unit_test.dart @@ -5,8 +5,8 @@ import 'package:web3modal_flutter/services/explorer_service/explorer_service_sin import 'package:web3modal_flutter/services/w3m_service/i_w3m_service.dart'; import 'package:web3modal_flutter/utils/url/url_utils_singleton.dart'; -import 'package:web3modal_flutter/services/blockchain_api_service/blockchain_api_utils_singleton.dart'; -import 'package:web3modal_flutter/services/blockchain_api_service/blockchain_identity.dart'; +import 'package:web3modal_flutter/services/blockchain_service/blockchain_service_singleton.dart'; +import 'package:web3modal_flutter/services/blockchain_service/models/blockchain_identity.dart'; import 'package:web3modal_flutter/services/network_service/network_service_singleton.dart'; import 'package:web3modal_flutter/services/storage_service/storage_service_singleton.dart'; import 'package:web3modal_flutter/web3modal_flutter.dart'; @@ -24,7 +24,7 @@ void main() { late MockRelayClient mockRelayClient; late MockNetworkService mockNetworkService; late MockStorageService mockStorageService; - late MockBlockchainApiUtils mockBlockchainApiUtils; + late MockBlockChainService mockBlockChainService; late MockLedgerService mockEVMService; late MockUrlUtils mockUrlUtils; @@ -79,14 +79,14 @@ void main() { // Service mocking mockNetworkService = MockNetworkService(); mockStorageService = MockStorageService(); - mockBlockchainApiUtils = MockBlockchainApiUtils(); + mockBlockChainService = MockBlockChainService(); es = MockExplorerService(); mockEVMService = MockLedgerService(); mockUrlUtils = MockUrlUtils(); networkService.instance = mockNetworkService; storageService.instance = mockStorageService; explorerService.instance = es; - blockchainApiUtils.instance = mockBlockchainApiUtils; + blockchainService.instance = mockBlockChainService; urlUtils.instance = mockUrlUtils; // Change all chain presets to use our mock EVMService @@ -105,7 +105,7 @@ void main() { when(mockEVMService.getBalance(any, any)).thenAnswer( (_) => Future.value(1.0), ); - when(mockBlockchainApiUtils.getIdentity(any, any)).thenAnswer( + when(mockBlockChainService.getIdentity(any, any)).thenAnswer( (realInvocation) => Future.value( const BlockchainIdentity( avatar: null, @@ -133,7 +133,7 @@ void main() { metadata: metadata, ); - expect(blockchainApiUtils.instance!.projectId, 'projectId'); + expect(blockchainService.instance!.projectId, 'projectId'); }); }); @@ -193,7 +193,7 @@ void main() { ).called(1); verify(es.getAssetImageUrl('imageId')).called(1); verify(mockEVMService.getBalance(any, any)).called(1); - verify(mockBlockchainApiUtils.getIdentity(any, any)).called(1); + verify(mockBlockChainService.getIdentity(any, any)).called(1); expect(service.selectedChain, W3MChainPresets.chains['1']); expect(counter, 4); @@ -230,7 +230,7 @@ void main() { ).called(1); verify(es.getAssetImageUrl('imageId')).called(1); verify(mockEVMService.getBalance(any, any)).called(1); - verify(mockBlockchainApiUtils.getIdentity(any, any)).called(1); + verify(mockBlockChainService.getIdentity(any, any)).called(1); expect(service.selectedChain, W3MChainPresets.chains['1']); // Chain swap to polygon @@ -243,7 +243,7 @@ void main() { ).called(1); verify(es.getAssetImageUrl('imageId')).called(1); verify(mockEVMService.getBalance(any, any)).called(1); - verify(mockBlockchainApiUtils.getIdentity(any, any)).called(1); + verify(mockBlockChainService.getIdentity(any, any)).called(1); expect(service.selectedChain, W3MChainPresets.chains['137']); // Setting selected chain to null will disconnect