| 1 | import "package:cake_wallet/core/anypay/anypay_models.dart"; |
| 2 | import "package:cake_wallet/core/anypay/anypay_parser.dart"; |
| 3 | import "package:cake_wallet/core/anypay/anypay_resolver.dart"; |
| 4 | import "package:cake_wallet/core/anypay/anypay_router.dart"; |
| 5 | import "package:cake_wallet/evm/evm.dart"; |
| 6 | import "package:cake_wallet/new-ui/services/wallet_switch_service.dart"; |
| 7 | import "package:cake_wallet/reactions/wallet_connect.dart"; |
| 8 | import "package:cake_wallet/solana/solana.dart"; |
| 9 | import "package:cake_wallet/store/app_store.dart"; |
| 10 | import "package:cake_wallet/tron/tron.dart"; |
| 11 | import "package:cake_wallet/utils/payment_request.dart"; |
| 12 | import "package:cw_core/amount/money.dart"; |
| 13 | import "package:cw_core/crypto_currency.dart"; |
| 14 | import "package:cw_core/currency_for_wallet_type.dart"; |
| 15 | import "package:cw_core/utils/print_verbose.dart"; |
| 16 | import "package:cw_core/wallet_info.dart"; |
| 17 | import "package:cw_core/wallet_type.dart"; |
| 18 | |
| 19 | class AnyPayService { |
| 20 | AnyPayService({ |
| 21 | required this.appStore, |
| 22 | required this.walletSwitchService, |
| 23 | AnyPayResolver? resolver, |
| 24 | }) : resolver = resolver ?? AnyPayResolver(); |
| 25 | |
| 26 | final AppStore appStore; |
| 27 | final WalletSwitchService walletSwitchService; |
| 28 | final AnyPayResolver resolver; |
| 29 | |
| 30 | Future<AnyPayEvaluation> evaluateRawInput(String input) => _evaluate(AnyPayParser.fromRaw(input)); |
| 31 | |
| 32 | Future<AnyPayEvaluation> evaluatePaymentRequest(PaymentRequest request) => |
| 33 | _evaluate(AnyPayParser.fromPaymentRequest(request)); |
| 34 | |
| 35 | Future<AnyPayEvaluation> evaluateForEvmChain(AnyPayRequest request, int chainId) => _evaluate( |
| 36 | AnyPayRequest( |
| 37 | rawInput: request.rawInput, |
| 38 | paymentRequest: request.paymentRequest, |
| 39 | detection: request.detection, |
| 40 | chainBinding: ExplicitEvmChain(chainId), |
| 41 | ), |
| 42 | ); |
| 43 | |
| 44 | Future<AnyPayEvaluation> _evaluate(AnyPayRequest request) async { |
| 45 | try { |
| 46 | final snapshot = await _buildWalletSnapshot(); |
| 47 | final resolution = await resolver.resolve(request, snapshot); |
| 48 | |
| 49 | return AnyPayEvaluation( |
| 50 | request: request, |
| 51 | decision: AnyPayRouter.route(request, snapshot, resolution), |
| 52 | ); |
| 53 | } catch (e) { |
| 54 | printV("anypay evaluation failed: $e"); |
| 55 | return AnyPayEvaluation( |
| 56 | request: request, |
| 57 | decision: const AnyPayApplyToCurrentWallet(), |
| 58 | ); |
| 59 | } |
| 60 | } |
| 61 | |
| 62 | Future<WalletSnapshot> _buildWalletSnapshot() async { |
| 63 | final wallet = appStore.wallet!; |
| 64 | final currentWalletIsEvm = isEVMCompatibleChain(wallet.type); |
| 65 | |
| 66 | final supportedEvmChains = <int, WalletType>{}; |
| 67 | if (evm != null) { |
| 68 | for (final chain in evm!.getAllChains()) { |
| 69 | final walletType = evm!.getWalletTypeByChainId(chain.chainId); |
| 70 | if (walletType != null) { |
| 71 | supportedEvmChains[chain.chainId] = walletType; |
| 72 | } |
| 73 | } |
| 74 | } |
| 75 | |
| 76 | return WalletSnapshot( |
| 77 | type: wallet.type, |
| 78 | currentWalletIsEvm: currentWalletIsEvm, |
| 79 | currentChainId: currentWalletIsEvm && evm != null |
| 80 | ? evm!.getSelectedChainId(wallet) ?? evm!.getChainIdByWalletType(wallet.type) |
| 81 | : null, |
| 82 | wallets: await WalletInfo.getAll(), |
| 83 | supportedEvmChains: supportedEvmChains, |
| 84 | hasEvmProxy: evm != null, |
| 85 | hasSolanaProxy: solana != null, |
| 86 | hasTronProxy: tron != null, |
| 87 | ); |
| 88 | } |
| 89 | |
| 90 | AnyPaySwapIntent buildSwapIntent( |
| 91 | AnyPayRequest request, { |
| 92 | required WalletType targetWalletType, |
| 93 | int? targetChainId, |
| 94 | CryptoCurrency? token, |
| 95 | }) { |
| 96 | final receiveCurrency = token ?? |
| 97 | (targetChainId != null |
| 98 | ? walletTypeToCryptoCurrency(targetWalletType, chainId: targetChainId) |
| 99 | : request.detection.detectedCurrency ?? walletTypeToCryptoCurrency(targetWalletType)); |
| 100 | |
| 101 | final amountValue = token != null |
| 102 | ? request.paymentRequest.resolveTokenAmount(token) |
| 103 | : (request.amount.isNotEmpty ? request.amount : null); |
| 104 | |
| 105 | return AnyPaySwapIntent( |
| 106 | request: request, |
| 107 | receiveCurrency: receiveCurrency, |
| 108 | targetWalletType: targetWalletType, |
| 109 | receiveAmount: amountValue != null ? Money.tryParse(amountValue, receiveCurrency) : null, |
| 110 | ); |
| 111 | } |
| 112 | |
| 113 | Future<bool> switchWalletForPayment(WalletInfo walletInfo, {int? chainId}) async { |
| 114 | try { |
| 115 | await walletSwitchService.switchToWallet(walletInfo); |
| 116 | } catch (e) { |
| 117 | printV("wallet switch failed: $e"); |
| 118 | return false; |
| 119 | } |
| 120 | |
| 121 | final wallet = appStore.wallet!; |
| 122 | if (chainId != null && evm != null && isEVMCompatibleChain(wallet.type)) { |
| 123 | try { |
| 124 | final node = appStore.settingsStore.getCurrentNode(wallet.type, chainId: chainId); |
| 125 | await evm!.selectChain(wallet, chainId, node: node); |
| 126 | } catch (e, s) { |
| 127 | printV("switchWalletForPayment chain select failed: $e\n$s"); |
| 128 | return false; |
| 129 | } |
| 130 | } |
| 131 | |
| 132 | try { |
| 133 | await wallet.updateBalance(); |
| 134 | } catch (e) { |
| 135 | printV("balance refresh after wallet switch failed: $e"); |
| 136 | } |
| 137 | |
| 138 | return true; |
| 139 | } |
| 140 | } |