Flutter DApp中通过Web3钱包签名部署合约的实现方法咨询
问题描述
我开发了一个Flutter DApp,希望部署使用web3dart库创建的HTLC合约。当前部署合约的代码依赖私钥签名,但我不想管理私钥,希望通过MetaMask这类钱包完成交易签名后再发送。目前已通过flutter_web3实现了Web3钱包连接,请问如何修改代码实现钱包签名部署合约?
现有部署合约代码:
import 'dart:convert'; import 'dart:developer'; import 'package:flutter/services.dart'; import 'package:http/http.dart'; import 'package:web3dart/web3dart.dart'; class HTLCEthContract { final String apiUrl = 'http://127.0.0.1:7545'; Future<void> deployHTLC( String privateKey, String contractAddress, String recipientAddress, String hash, BigInt amount, { int lockTime = 720, int chainId = 1337, }) async { final privateKey = ''; // REPLACE WITH WALLET SIGNATURE final web3Client = Web3Client(apiUrl, Client()); final credentials = EthPrivateKey.fromHex(privateKey); final abiStringFile = await rootBundle.loadString('truffle/build/contracts/HTLC_ETH.json'); final jsonAbi = jsonDecode(abiStringFile); final contract = DeployedContract( jsonAbi, EthereumAddress.fromHex(contractAddress), ); final transaction = Transaction.callContract( contract: contract, function: contract.function('constructor'), parameters: [ EthereumAddress.fromHex(recipientAddress), EthereumAddress.fromHex(contractAddress), EtherAmount.fromBigInt(EtherUnit.ether, amount), hash, BigInt.from(lockTime), ], ); final transactionHash = await web3Client.sendTransaction( credentials, transaction, chainId: chainId, ); final receipt = await web3Client.getTransactionReceipt(transactionHash); log('HTLC contract deployed at ${receipt!.contractAddress!.hex}'); } }
现有钱包连接代码:
import 'package:flutter/cupertino.dart'; import 'package:flutter_web3/flutter_web3.dart'; class MetaMaskProvider extends ChangeNotifier { static const operatingChain = 4; String currentAddress = ''; String account = ''; int currentChain = -1; bool get isEnabled => ethereum != null; bool get isInOperatingChain => currentChain == operatingChain; bool get isConnected => isEnabled && currentAddress.isNotEmpty; late final Web3Provider web3Provider; MetaMaskProvider() { if (isEnabled) { web3Provider = Web3Provider.fromEthereum(ethereum!); } } Future<void> connect() async { if (isEnabled) { final accs = await ethereum!.requestAccount(); account = accs[0]; if (accs.isNotEmpty) currentAddress = accs.first; currentChain = await ethereum!.getChainId(); notifyListeners(); } } Future<void> changeChainId(int chainId) async { if (isEnabled && isConnected) { await ethereum!.walletSwitchChain(chainId); } } void clear() { currentAddress = ''; currentChain = -1; notifyListeners(); } void init() { if (isEnabled) { ethereum!.onAccountsChanged((accounts) { clear(); }); ethereum!.onChainChanged((chainId) { clear(); }); ethereum!.onDisconnect((error) { clear(); }); } } }
解决方案
要实现钱包签名合约部署,核心是通过flutter_web3的Web3Provider直接构造并发送合约创建交易,无需web3dart的私钥签名逻辑。以下是具体修改步骤:
1. 修正核心逻辑错误
你当前代码中用Transaction.callContract调用constructor是错误的——合约部署不是调用已部署合约的方法,而是发送一个无to地址、包含合约字节码+构造参数编码的交易。需要从ABI文件中提取合约字节码。
2. 整合flutter_web3实现签名部署
修改HTLCEthContract类,依赖已连接的MetaMaskProvider完成交易:
import 'dart:convert'; import 'dart:developer'; import 'package:flutter/services.dart'; import 'package:flutter_web3/flutter_web3.dart'; import 'package:web3dart/web3dart.dart'; class HTLCEthContract { final MetaMaskProvider metaMaskProvider; HTLCEthContract(this.metaMaskProvider); Future<void> deployHTLC( String recipientAddress, String hash, BigInt amount, { int lockTime = 720, }) async { // 检查钱包连接状态 if (!metaMaskProvider.isConnected) { throw Exception('请先连接MetaMask钱包'); } if (!metaMaskProvider.isInOperatingChain) { throw Exception('请切换到目标链(链ID:${MetaMaskProvider.operatingChain})'); } // 加载合约ABI和字节码 final abiStringFile = await rootBundle.loadString('truffle/build/contracts/HTLC_ETH.json'); final jsonAbi = jsonDecode(abiStringFile); final contractAbi = jsonAbi['abi']; final contractBytecode = jsonAbi['bytecode']; // 用web3dart编码构造函数参数 final web3Contract = DeployedContract( ContractAbi.fromJson(jsonEncode(contractAbi), 'HTLC_ETH'), EthereumAddress.fromHex('0x0000000000000000000000000000000000000000'), ); final constructor = web3Contract.function('constructor'); // 注意:请根据你的HTLC合约实际构造函数参数调整顺序和类型 final encodedParams = constructor.encodeCall([ EthereumAddress.fromHex(recipientAddress), hash, amount, // 直接传wei单位的金额,根据合约定义调整 BigInt.from(lockTime), ]); // 拼接字节码和编码后的参数 final transactionData = contractBytecode + encodedParams.replaceFirst('0x', ''); // 通过MetaMask发送交易(自动触发钱包签名) try { final txHash = await metaMaskProvider.web3Provider.sendTransaction({ 'from': metaMaskProvider.currentAddress, 'data': transactionData, 'value': '0x${amount.toRadixString(16)}', // 如果合约需要接收ETH,填写对应wei的十六进制 }); // 等待交易上链,获取合约地址 final receipt = await metaMaskProvider.web3Provider.getTransactionReceipt(txHash); if (receipt != null && receipt.contractAddress != null) { log('HTLC合约部署完成,地址:${receipt.contractAddress}'); } else { log('合约部署失败,未获取到合约地址'); } } catch (e) { log('交易处理失败:$e'); rethrow; } } }
3. 关键注意事项
- 构造参数校验:务必对照你的HTLC合约实际构造函数参数调整
encodeCall的参数,之前代码中传入contractAddress作为构造参数大概率是错误的。 - 金额单位:如果合约接收ETH,
value字段需要传入wei单位的十六进制字符串,避免单位错误。 - 异常处理:调用前检查钱包连接状态和链ID,处理用户拒绝签名、交易失败等异常。
4. 调用示例
在UI逻辑中,通过已连接的MetaMaskProvider实例调用部署方法:
// 假设已通过metaMaskProvider.connect()连接钱包 final htlcContract = HTLCEthContract(metaMaskProvider); try { await htlcContract.deployHTLC( '0x你的接收方地址', '0x哈希值', BigInt.from(1000000000000000000), // 1 ETH(wei单位) ); } catch (e) { // 处理错误,比如弹窗提示用户 }
内容的提问来源于stack exchange,提问作者redDwarf
相关产品推荐
相关产品推荐

