| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151 |
- import 'dart:async';
- import 'package:reown_appkit/modal/i_appkit_modal_impl.dart';
- import 'package:reown_appkit/reown_appkit.dart';
- import 'evm_recharge.dart';
- import '../../data/models/asset/recharge_order.dart';
- /// 使用 Reown AppKit 连接钱包并向订单收款地址发起转账(与 Web `useWalletConnectDeposit` 对齐)。
- class WalletConnectRechargeHelper {
- WalletConnectRechargeHelper._();
- static const _erc20Abi = '''
- [{"constant":false,"inputs":[{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transfer","outputs":[{"name":"","type":"bool"}],"type":"function"}]
- ''';
- static BigInt _parseUnits(String amount, int decimals) {
- final s = amount.trim();
- if (s.isEmpty) {
- throw FormatException('empty amount');
- }
- var normalized = s.replaceAll(',', '');
- final neg = normalized.startsWith('-');
- if (neg) {
- normalized = normalized.substring(1);
- }
- final parts = normalized.split('.');
- final intPart = parts[0].isEmpty ? '0' : parts[0];
- var frac = parts.length > 1 ? parts[1] : '';
- if (frac.length > decimals) {
- frac = frac.substring(0, decimals);
- } else {
- frac = frac.padRight(decimals, '0');
- }
- final combined = intPart + frac;
- var bi = BigInt.parse(combined);
- if (neg) {
- bi = -bi;
- }
- return bi;
- }
- static Future<void> ensureConnected(IReownAppKitModal modal) async {
- if (modal.isConnected) {
- return;
- }
- final completer = Completer<void>();
- void onConnect(ModalConnect _) {
- if (!completer.isCompleted) {
- completer.complete();
- }
- }
- modal.onModalConnect.subscribe(onConnect);
- try {
- // 与「先 await openModalView 再 await connect」不同:用户关掉弹窗未连接时,
- // openModalView 会结束而 onConnect 永不触发,会导致外层 loading 卡死到超时。
- final modalClosed = modal.openModalView();
- await Future.any<void>([
- completer.future,
- modalClosed,
- ]).timeout(
- const Duration(minutes: 3),
- onTimeout: () {
- throw TimeoutException('WalletConnect 连接超时');
- },
- );
- if (!modal.isConnected) {
- throw StateError('WalletConnect 已取消或未连接');
- }
- } finally {
- modal.onModalConnect.unsubscribe(onConnect);
- }
- }
- static Future<void> ensureEvmChain(
- IReownAppKitModal modal,
- int chainId,
- ) async {
- final net = ReownAppKitModalNetworks.getNetworkInfo(
- 'eip155',
- chainIdToCaip2(chainId),
- );
- if (net == null) {
- throw StateError('不支持的链 chainId=$chainId');
- }
- await modal.selectChain(net, switchChain: true);
- }
- /// 返回交易 hash(0x…)。
- static Future<String> connectAndPay({
- required IReownAppKitModal appKitModal,
- required RechargeFlatNetworkOption network,
- required RechargeOrderDetail order,
- }) async {
- final chainId = resolveEvmChainId(network.protocol, network.networkName);
- if (chainId == null) {
- throw StateError('当前网络不支持 WalletConnect,请使用钱包手动转账');
- }
- await ensureConnected(appKitModal);
- await ensureEvmChain(appKitModal, chainId);
- final session = appKitModal.session;
- if (session == null) {
- throw StateError('会话无效');
- }
- final fromRaw = session.getAddress('eip155');
- if (fromRaw == null || fromRaw.isEmpty) {
- throw StateError('未获取到钱包地址');
- }
- final from = EthereumAddress.fromHex(fromRaw);
- final to = EthereumAddress.fromHex(order.rechargeAddress.trim());
- final caip2 = chainIdToCaip2(chainId);
- final topic = session.topic;
- final amountStr = order.amount.trim();
- if (network.hasTokenContract) {
- final token = EthereumAddress.fromHex(network.contractAddress.trim());
- final decimals = erc20DecimalsForChain(chainId);
- final amountWei = _parseUnits(amountStr, decimals);
- final abi = ContractAbi.fromJson(_erc20Abi, 'ERC20');
- final deployed = DeployedContract(abi, token);
- final hash = await appKitModal.requestWriteContract(
- topic: topic,
- chainId: caip2,
- deployedContract: deployed,
- functionName: 'transfer',
- transaction: Transaction(from: from),
- parameters: [to, amountWei],
- );
- return hash.toString();
- } else {
- final valueWei = _parseUnits(amountStr, 18);
- final trx = Transaction(
- from: from,
- to: to,
- value: EtherAmount.fromBigInt(EtherUnit.wei, valueWei),
- );
- final hash = await appKitModal.request(
- topic: topic,
- chainId: caip2,
- request: SessionRequestParams(
- method: MethodsConstants.ethSendTransaction,
- params: [trx.toJson()],
- ),
- );
- return hash.toString();
- }
- }
- }
|