Flutter调用Ganache合约报RPCError: Invalid signature v value错误求助
问题:Flutter调用Ganache上智能合约setter报错"Invalid signature v value"
我是Flutter和Web3新手,尝试在按钮点击时调用智能合约的setter方法,将交易发送到本地区块链网络Ganache,使用Truffle管理和部署合约,但运行时出现错误:
flutter: RPCError: got code -32700 with msg "Invalid signature v value"
相关代码文件如下:
truffle-config.js
module.exports = { networks: { development: { host: "127.0.0.1", // Localhost (default: none) port: 7545, // Standard Ethereum port (default: none) network_id: "*", // Any network (default: none) } }, mocha: { // timeout: 100000 }, contracts_build_directory: "./contracts", compilers: { solc: { version: "0.8.19", settings: { optimizer: { enabled: false, runs: 200 }, } } }, db: { enabled: false } };
MyContract.sol
// SPDX-License-Identifier: MIT pragma solidity 0.8.19; contract MyContract { string public sentence = "Default Sentence Value"; function set(string memory x) public { sentence = x; } function get() public view returns (string memory) { return sentence; } }
main.dart
import 'package:flutter/material.dart'; import 'package:flutter_app/contract_provider.dart'; import 'package:flutter_app/my_app.dart'; import 'package:provider/provider.dart'; void main() { runApp(const MainApp()); } class MainApp extends StatelessWidget { const MainApp({super.key}); @override Widget build(BuildContext context) { return ChangeNotifierProvider( create: (_) => ContractProvider(context), child: MaterialApp( title: 'Flutter Practice App', theme: ThemeData( useMaterial3: true, ), home: const HomeScreen(title: 'HomeScreen Title'), ), ); } }
my_app.dart
import 'package:flutter/material.dart'; import 'package:flutter_app/contract_provider.dart'; import 'package:provider/provider.dart'; class HomeScreen extends StatefulWidget { const HomeScreen({super.key, required this.title}); final String title; @override State<HomeScreen> createState() => _HomeScreenState(); } class _HomeScreenState extends State<HomeScreen> { @override Widget build(BuildContext context) { TextEditingController inputController = TextEditingController(); final contractProvider = Provider.of<ContractProvider>(context); return Scaffold( appBar: AppBar(title: Text(widget.title)), body: Consumer<ContractProvider>(builder: (context, model, child) { return Center( child: model.isLoading ? const CircularProgressIndicator() : Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Text( 'Current value of the sentence: ${model.sentence.toString()}'), const SizedBox( height: 30, ), SizedBox( width: 200, child: TextFormField( controller: inputController, keyboardType: TextInputType.text, decoration: const InputDecoration( labelText: 'Please set a sentence!'), ), ), const SizedBox( height: 30, ), ElevatedButton( onPressed: () async { try { await contractProvider.setSentence( inputController.text.toString()); } catch (e) { print(e); } finally { inputController.clear(); } }, child: const Text('Set')) ], )); })); } }
contract_provider.dart
import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:http/http.dart'; import 'package:web3dart/web3dart.dart'; import 'package:web_socket_channel/io.dart'; class ContractProvider extends ChangeNotifier { static const String contract_name = "MyContract"; static const String ganache_host = "127.0.0.1"; static const String ganache_port = "7545"; final String _rpcURL = "http://$ganache_host:$ganache_port"; final String _wsURL = "wss://$ganache_host:$ganache_port"; final String _privateKey = "0x67e633f41624a1d742d753fe52bcdfe96b54fe3d91a87ce5260101605fced7fb"; late Web3Client _client; late Credentials _credentials; late DeployedContract _deployedContract; late ContractFunction _getSentence, _setSentence; String? sentence; bool isLoading = true; ContractProvider(context) { initialize(context); } initialize(context) async { _client = Web3Client(_rpcURL, Client(), socketConnector: () { return IOWebSocketChannel.connect(_wsURL).cast<String>(); }); final abiStringFile = await DefaultAssetBundle.of(context) .loadString('build/contracts/MyContract.json'); final abiJson = jsonDecode(abiStringFile); final abi = jsonEncode(abiJson["abi"]); final contractAddress = EthereumAddress.fromHex(abiJson["networks"]["5777"]["address"]); _credentials = EthPrivateKey.fromHex(_privateKey); _deployedContract = DeployedContract( ContractAbi.fromJson(abi, contract_name), contractAddress); _getSentence = _deployedContract.function("get"); _setSentence = _deployedContract.function("set"); getSentence(); } Future<void> getSentence() async { final result = await _client .call(contract: _deployedContract, function: _getSentence, params: []); sentence = result[0]; isLoading = false; notifyListeners(); } Future<void> setSentence(String value) async { isLoading = true; notifyListeners(); await _client.sendTransaction( _credentials, Transaction.callContract( contract: _deployedContract, function: _setSentence, parameters: [value])); getSentence(); } }
问题排查与解决
这个错误核心原因是交易签名的网络标识不匹配,或者WebSocket协议配置错误,导致Ganache拒绝无效签名。以下是具体修复步骤:
1. 修正WebSocket协议
Ganache本地节点默认不启用加密WebSocket(wss),需将协议改为ws://:
// 在contract_provider.dart中修改 final String _wsURL = "ws://$ganache_host:$ganache_port";
2. 指定交易的Chain ID
web3dart发送交易时需明确指定Ganache的Chain ID(默认是5777),确保签名使用正确的网络标识:
// 修改setSentence方法中的sendTransaction调用 await _client.sendTransaction( _credentials, Transaction.callContract( contract: _deployedContract, function: _setSentence, parameters: [value], chainId: 5777 // 添加Chain ID参数 ), fetchChainIdFromNetworkId: false, // 强制使用指定的Chain ID );
3. 验证私钥有效性
确认_privateKey是Ganache界面中生成的账户私钥,且该账户有足够ETH支付Gas费用。
4. 优化Provider初始化逻辑
原构造函数中直接调用异步方法可能导致初始化不完整,改为工厂方法+私有初始化:
// 修改contract_provider.dart中的构造函数和初始化方法 factory ContractProvider(BuildContext context) { final provider = ContractProvider._(); provider._initialize(context); return provider; } ContractProvider._(); void _initialize(BuildContext context) async { // 原initialize方法的代码 }
完成以上修改后,重新运行项目即可正常调用智能合约的setter方法。
内容的提问来源于stack exchange,提问作者mr-possible
相关产品推荐
相关产品推荐

