Flutter前端无法连接Election智能合约,遇RPCError执行回滚求助
选举DApp Flutter连接智能合约执行回滚问题解决
问题描述
开发的小型选举DApp在Remix IDE中部署测试完全正常,但通过Flutter应用连接智能合约时无法正常工作。已尝试重新部署合约并同步更新ABI与合约地址,问题仍存在,仅Remix内测试可正常运行,触发的错误为执行回滚。
错误信息
call function has error ::::: RPCError: got code -32000 with msg "execution reverted".
相关代码
智能合约代码
//SPDX-License-Identifier:UNLICENSED pragma solidity ^0.8.0; contract Election{ struct Candidate{ string name; uint numvotes; } struct Voter{ string name; bool authorised; uint whom; bool voted; } modifier ownerOnly(){ require(msg.sender == owner); _; } address public owner; string public ElectionName; mapping(address => Voter) public Voters; Candidate[] public candidates; uint public totalvotes=0; function startElection(string memory _ElectionName)public{ owner = msg.sender; ElectionName = _ElectionName; } function addCandidate(string memory _candidatename) ownerOnly public{ candidates.push(Candidate(_candidatename,0)); } function authoriseVoter(address _voteradress)ownerOnly public{ require(!Voters[msg.sender].voted); Voters[_voteradress].authorised = true; } function getNumcandidates()public view returns(uint){ return candidates.length; } function Vote(uint CandidateIndex)public { require(!Voters[msg.sender].voted); require(Voters[msg.sender].authorised = true); Voters[msg.sender].whom = CandidateIndex; Voters[msg.sender].voted = true; candidates[CandidateIndex].numvotes++; totalvotes++; } function candidateInfo(uint index) public view returns(Candidate memory){ return candidates[index]; } function getTotalVotes()public view returns(uint) { return totalvotes; } }
前端函数代码
Future<DeployedContract> loadContract() async { try{ String abi = await rootBundle.loadString('assets/abi.json'); String contractAddress = contractAdressConst; final contract = DeployedContract(ContractAbi.fromJson(abi, 'Election'), EthereumAddress.fromHex(contractAddress)); return contract; }catch(e){ print('load contract failed ::::: $e'); print('{{{{{{{{{{{{{{{[[{{{{{'); String abi = await rootBundle.loadString('assets/abi.json'); String contractAddress = contractAdressConst; final contract = DeployedContract(ContractAbi.fromJson(abi, 'Election'), EthereumAddress.fromHex(contractAddress)); return contract; } } Future<String> callFunction(String funcname, List<dynamic> args, Web3Client ethClient, String privateKey) async { try{ EthPrivateKey credentials = EthPrivateKey.fromHex(privateKey); DeployedContract contract = await loadContract(); final ethFunction = contract.function(funcname); final result = await ethClient.sendTransaction( credentials, Transaction.callContract( contract: contract, function: ethFunction, parameters: args, ), chainId: null, fetchChainIdFromNetworkId: true); return result; }catch(e){ print('call function has error ::::: $e'); return e.toString(); } } Future<String> startElection(String name, Web3Client ethClient) async { try{ var response = await callFunction('startElection', [name], ethClient, owner_private_key); print('Election started successfully'); return response; }catch(e){ print("election not started : : : $e"); return e.toString(); } } Future<String> addCandidate(String name, Web3Client ethClient) async { try{ var response = await callFunction('addCandidate', [name], ethClient, owner_private_key); print('Candidate added successfully'); return response; }catch(e){ print(" candidate not added : : :$e"); return e.toString(); } }
home.dart代码
import 'package:election/utils/Constants.dart'; import 'package:flutter/material.dart'; import 'package:http/http.dart'; import 'package:web3dart/web3dart.dart'; import '../services/functions.dart'; import 'Electioninfo.dart'; class Home extends StatefulWidget { const Home({Key? key}) : super(key: key); @override _HomeState createState() => _HomeState(); } class _HomeState extends State<Home> { Client? httpClient; Web3Client? ethClient; TextEditingController controller = TextEditingController(); @override void initState() { httpClient = Client(); ethClient = Web3Client(infura_url, httpClient!); super.initState(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('Start Election'), ), body: Container( padding: EdgeInsets.all(14), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ TextField( controller: controller, decoration: InputDecoration( filled: true, hintText: 'Enter election name'), ), SizedBox( height: 10, ), Container( width: double.infinity, height: 45, child: ElevatedButton( onPressed: () async { if (controller.text.length > 0) { await startElection(controller.text, ethClient!); Navigator.push( context, MaterialPageRoute( builder: (context) => ElectionInfo( ethClient: ethClient!, electionName: controller.text))); } }, child: Text('Start Election'))) ], ), ), ); } }
解决方案
1. 修复智能合约语法错误
在Vote函数中存在致命的赋值错误,将判断条件误写为赋值操作,会导致逻辑异常:
// 错误写法 require(Voters[msg.sender].authorised = true); // 正确写法(二选一) require(Voters[msg.sender].authorised == true); require(Voters[msg.sender].authorised);
2. 明确指定交易Chain ID
Flutter代码中sendTransaction依赖自动获取Chain ID可能出现偏差,需手动指定对应网络的Chain ID(如Sepolia为11155111,Goerli为5):
final result = await ethClient.sendTransaction( credentials, Transaction.callContract( contract: contract, function: ethFunction, parameters: args, ), chainId: 11155111, // 替换为你使用的网络Chain ID fetchChainIdFromNetworkId: false);
3. 验证调用者与合约Owner一致性
- 确认
owner_private_key对应的地址,是调用startElection时设置的合约Owner地址;若多次调用startElection,Owner会被更新,后续addCandidate需使用最新Owner的私钥。 - 调用
addCandidate前,通过合约owner()方法查询当前Owner,确保与Flutter使用的私钥地址匹配。
4. 确保ABI文件正确性
重新编译合约并导出最新ABI,替换assets/abi.json,避免手动编辑导致的签名或参数类型不匹配问题。
5. 增强错误日志信息
在Flutter错误捕获中添加上下文信息,帮助定位问题:
catch(e){ print('call function has error ::::: $e'); print('Function: $funcname, Args: $args, Sender: ${credentials.address}'); return e.toString(); }
内容的提问来源于stack exchange,提问作者Abhiram
相关产品推荐
相关产品推荐

