import 'dart:async'; import 'package:hash_wallet/core/universal_address_detector.dart'; import 'package:hash_wallet/evm/evm.dart'; import 'package:hash_wallet/reactions/wallet_connect.dart'; import 'package:hash_wallet/store/app_store.dart'; import 'package:cw_core/wallet_type.dart'; import 'package:cw_core/wallet_info.dart'; import 'package:cw_core/utils/print_verbose.dart'; import 'package:cw_core/currency_for_wallet_type.dart'; import 'package:cw_core/crypto_currency.dart'; import 'package:mobx/mobx.dart'; part 'payment_view_model.g.dart'; class PaymentViewModel = PaymentViewModelBase with _$PaymentViewModel; abstract class PaymentViewModelBase with Store { PaymentViewModelBase({required this.appStore}); final AppStore appStore; @observable WalletType? detectedWalletType; @observable AddressDetectionResult? _lastDetectionResult; @computed int? get detectedChainId { if (detectedWalletType == null) return null; if (!isEVMCompatibleChain(detectedWalletType!)) return null; if (_lastDetectionResult?.chainId != null) { return _lastDetectionResult!.chainId; } // If detected wallet type is EVM-compatible, get chainId from detected currency if (_lastDetectionResult?.detectedCurrency != null) { return getChainIdByCryptoCurrency(_lastDetectionResult!.detectedCurrency!); } return evm!.getChainIdByWalletType(detectedWalletType!); } @observable bool isProcessing = false; @computed WalletType get currentWalletType => appStore.wallet!.type; @computed int? get currentChainId { if (!isEVMCompatibleChain(currentWalletType)) return null; return evm!.getSelectedChainId(appStore.wallet!); } /// Main entry point - detect address type and check compatibility @action Future processAddress(String addressData) async { try { detectedWalletType = null; isProcessing = true; // Detect address type final detectionResult = UniversalAddressDetector.detectAddress(addressData); _lastDetectionResult = detectionResult; detectedWalletType = detectionResult.detectedWalletType; if (!detectionResult.isValid || detectedWalletType == null) { return PaymentFlowResult.incompatible('Unable to detect address type'); } final currentWallet = appStore.wallet; if (currentWallet != null && currentWallet.type == detectedWalletType && !isEVMCompatibleChain(detectedWalletType!)) { return PaymentFlowResult.currentWalletCompatible(); } if (isEVMCompatibleChain(detectedWalletType!)) { return PaymentFlowResult.evmNetworkSelection(detectionResult); } final compatibleWallets = await getWalletsByType(detectedWalletType!); switch (compatibleWallets.length) { case 0: return PaymentFlowResult.noWallets(detectedWalletType!, detectionResult); case 1: return PaymentFlowResult.singleWallet(compatibleWallets.first, detectionResult); default: return PaymentFlowResult.multipleWallets(compatibleWallets, detectionResult); } } catch (e) { printV('PaymentViewModel error: $e'); return PaymentFlowResult.error('Payment processing failed: $e'); } finally { isProcessing = false; } } @action Future selectChain() async { if (detectedWalletType == null) return; final node = appStore.settingsStore.getCurrentNode(detectedWalletType!, chainId: detectedChainId); await evm!.selectChain(appStore.wallet!, detectedChainId!, node: node); } Future> getWalletsByType(WalletType walletType) async { return (await WalletInfo.getAll()).where((wallet) => wallet.type == walletType).toList(); } Future> getEVMCompatibleWallets() async { final allWallets = await WalletInfo.getAll(); return allWallets.where((wallet) => isEVMCompatibleChain(wallet.type)).toList(); } } class PaymentFlowResult { final PaymentFlowType type; final String? message; final WalletInfo? wallet; final int? chainId; final List wallets; final WalletType? walletType; final AddressDetectionResult? addressDetectionResult; PaymentFlowResult._({ required this.type, this.message, this.wallet, this.chainId, this.wallets = const [], this.walletType, this.addressDetectionResult, }); /// EVM address detected - needs network selection /// We'll also take note of the number of compatible wallets for EVM ecosystem factory PaymentFlowResult.evmNetworkSelection( AddressDetectionResult addressDetectionResult, { List? compatibleWallets, WalletInfo? wallet, }) { int? chainId = addressDetectionResult.chainId; if (chainId == null && addressDetectionResult.detectedCurrency != null) { chainId = getChainIdByCryptoCurrency(addressDetectionResult.detectedCurrency!); } if (chainId == null && addressDetectionResult.detectedWalletType != null) { chainId = evm!.getChainIdByWalletType(addressDetectionResult.detectedWalletType!); } return PaymentFlowResult._( type: PaymentFlowType.evmNetworkSelection, addressDetectionResult: addressDetectionResult, walletType: addressDetectionResult.detectedWalletType, chainId: chainId, wallets: compatibleWallets ?? [], wallet: wallet, ); } /// Current wallet is compatible factory PaymentFlowResult.currentWalletCompatible() => PaymentFlowResult._(type: PaymentFlowType.currentWalletCompatible); /// Single compatible wallet available factory PaymentFlowResult.singleWallet( WalletInfo wallet, AddressDetectionResult addressDetectionResult, ) { int? chainId = addressDetectionResult.chainId; if (chainId == null && addressDetectionResult.detectedCurrency != null) { chainId = getChainIdByCryptoCurrency(addressDetectionResult.detectedCurrency!); } if (chainId == null) { chainId = evm!.getChainIdByWalletType(wallet.type); } return PaymentFlowResult._( type: PaymentFlowType.singleWallet, wallet: wallet, walletType: wallet.type, chainId: chainId, addressDetectionResult: addressDetectionResult, ); } /// Multiple compatible wallets available factory PaymentFlowResult.multipleWallets( List wallets, AddressDetectionResult addressDetectionResult, ) { int? chainId = addressDetectionResult.chainId; if (chainId == null && addressDetectionResult.detectedCurrency != null) { chainId = getChainIdByCryptoCurrency(addressDetectionResult.detectedCurrency!); } if (chainId == null) { chainId = evm!.getChainIdByWalletType(wallets.first.type); } return PaymentFlowResult._( type: PaymentFlowType.multipleWallets, wallets: wallets, walletType: wallets.first.type, addressDetectionResult: addressDetectionResult, chainId: chainId, ); } /// No compatible wallets available factory PaymentFlowResult.noWallets( WalletType walletType, AddressDetectionResult addressDetectionResult, ) { int? chainId = addressDetectionResult.chainId; if (chainId == null && addressDetectionResult.detectedCurrency != null) { chainId = getChainIdByCryptoCurrency(addressDetectionResult.detectedCurrency!); } if (chainId == null) { chainId = evm!.getChainIdByWalletType(walletType); } return PaymentFlowResult._( type: PaymentFlowType.noWallets, walletType: walletType, addressDetectionResult: addressDetectionResult, chainId: chainId, ); } /// Error occurred factory PaymentFlowResult.error(String message) => PaymentFlowResult._(type: PaymentFlowType.error, message: message); /// Incompatible address factory PaymentFlowResult.incompatible(String message) => PaymentFlowResult._(type: PaymentFlowType.incompatible, message: message); CryptoCurrency? get detectedCurrency { if (type == PaymentFlowType.evmNetworkSelection) { return addressDetectionResult?.detectedCurrency; } if (walletType != null) { if (isEVMCompatibleChain(walletType!)) { return walletTypeToCryptoCurrency(walletType!, chainId: chainId); } return walletTypeToCryptoCurrency(walletType!); } return null; } } enum PaymentFlowType { currentWalletCompatible, singleWallet, multipleWallets, noWallets, evmNetworkSelection, error, incompatible, }