Web3.py调用智能合约transact方法报错:unknown account
问题:Web3.py调用StakingRewards合约transact时报错"unknown account"
我尝试用Web3.py的transact方法调用StakingRewards智能合约的setRewardsDuration方法,但执行失败,报错信息:
ValueError: {'code': -32000, 'message': 'unknown account'}
已确认Remix部署合约的Owner地址与Web3.py中使用的wal_address完全一致,有没有人遇到过相同问题?
Solidity合约代码
// SPDX-License-Identifier: MIT pragma solidity ^0.8; contract StakingRewards { IERC20 public immutable stakingToken; IERC20 public immutable rewardsToken; address public owner; uint public duration; uint public finishAt; uint public updatedAt; uint public rewardRate; uint public rewardPerTokenStored; uint public totalSupply; mapping(address => uint) public userRewardPerTokenPaid; mapping(address => uint) public rewards; mapping(address => uint) public balanceOf; constructor(address _stakingToken, address _rewardToken) { owner = msg.sender; stakingToken = IERC20(_stakingToken); rewardsToken = IERC20(_rewardToken); } modifier onlyOwner() { require(msg.sender == owner, "not authorized"); _; } modifier updateReward(address _account) { rewardPerTokenStored = rewardPerToken(); updatedAt = lastTimeRewardApplicable(); if (_account != address(0)) { rewards[_account] = earned(_account); userRewardPerTokenPaid[_account] = rewardPerTokenStored; } _; } function lastTimeRewardApplicable() public view returns (uint) { return _min(finishAt, block.timestamp); } function rewardPerToken() public view returns (uint) { if (totalSupply == 0) { return rewardPerTokenStored; } return rewardPerTokenStored + (rewardRate * (lastTimeRewardApplicable() - updatedAt) * 1e18) / totalSupply; } function stake(uint _amount) external updateReward(msg.sender) { require(_amount > 0, "amount = 0"); stakingToken.transferFrom(msg.sender, address(this), _amount); balanceOf[msg.sender] += _amount; totalSupply += _amount; } function withdraw(uint _amount) external updateReward(msg.sender) { require(_amount > 0, "amount = 0"); balanceOf[msg.sender] -= _amount; totalSupply -= _amount; stakingToken.transfer(msg.sender, _amount); } function earned(address _account) public view returns (uint) { return ((balanceOf[_account] * (rewardPerToken() - userRewardPerTokenPaid[_account])) / 1e18) + rewards[_account]; } function getReward() external updateReward(msg.sender) { uint reward = rewards[msg.sender]; if (reward > 0) { rewards[msg.sender] = 0; rewardsToken.transfer(msg.sender, reward); } } function setRewardsDuration(uint _duration) external onlyOwner { require(finishAt < block.timestamp, "reward duration not finished"); duration = _duration; } function notifyRewardAmount(uint _amount) external onlyOwner updateReward(address(0)) { if (block.timestamp >= finishAt) { rewardRate = _amount / duration; } else { uint remainingRewards = (finishAt - block.timestamp) * rewardRate; rewardRate = (_amount + remainingRewards) / duration; } require(rewardRate > 0, "reward rate = 0"); require( rewardRate * duration <= rewardsToken.balanceOf(address(this)), "reward amount > balance" ); finishAt = block.timestamp + duration; updatedAt = block.timestamp; } function _min(uint x, uint y) private pure returns (uint) { return x <= y ? x : y; } } interface IERC20 { function totalSupply() external view returns (uint); function balanceOf(address account) external view returns (uint); function transfer(address recipient, uint amount) external returns (bool); function allowance(address owner, address spender) external view returns (uint); function approve(address spender, uint amount) external returns (bool); function transferFrom( address sender, address recipient, uint amount ) external returns (bool); event Transfer(address indexed from, address indexed to, uint value); event Approval(address indexed owner, address indexed spender, uint value); }
Web3.py代码
from web3 import Web3 import json from web3.middleware import geth_poa_middleware rpc_url = 'http://...................' ## Custom testnet web3 = Web3(Web3.HTTPProvider(rpc_url)) web3.middleware_onion.inject(geth_poa_middleware, layer=0) # Inject poa middleware con_address = ".........................................." wal_address = '..........................................' # with open("/Users/creativelab/Desktop/Source/abi.json") as f: # info_json = json.load(f) abi = [ { "inputs": [], "name": "getReward", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [ { "internalType": "uint256", "name": "_amount", "type": "uint256" } ], "name": "notifyRewardAmount", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [ { "internalType": "uint256", "name": "_duration", "type": "uint256" } ], "name": "setRewardsDuration", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [ { "internalType": "uint256", "name": "_amount", "type": "uint256" } ], "name": "stake", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [ { "internalType": "address", "name": "_stakingToken", "type": "address" }, { "internalType": "address", "name": "_rewardToken", "type": "address" } ], "stateMutability": "nonpayable", "type": "constructor" }, { "inputs": [ { "internalType": "uint256", "name": "_amount", "type": "uint256" } ], "name": "withdraw", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [ { "internalType": "address", "name": "", "type": "address" } ], "name": "balanceOf", "outputs": [ { "internalType": "uint256", "name": "", "type": "uint256" } ], "stateMutability": "view", "type": "function" }, { "inputs": [], "name": "duration", "outputs": [ { "internalType": "uint256", "name": "", "type": "uint256" } ], "stateMutability": "view", "type": "function" }, { "inputs": [ { "internalType": "address", "name": "_account", "type": "address" } ], "name": "earned", "outputs": [ { "internalType": "uint256", "name": "", "type": "uint256" } ], "stateMutability": "view", "type": "function" }, { "inputs": [], "name": "finishAt", "outputs": [ { "internalType": "uint256", "name": "", "type": "uint256" } ], "stateMutability": "view", "type": "function" }, { "inputs": [], "name": "lastTimeRewardApplicable", "outputs": [ { "internalType": "uint256", "name": "", "type": "uint256" } ], "stateMutability": "view", "type": "function" }, { "inputs": [], "name": "owner", "outputs": [ { "internalType": "address", "name": "", "type": "address" } ], "stateMutability": "view", "type": "function" }, { "inputs": [], "name": "rewardPerToken", "outputs": [ { "internalType": "uint256", "name": "", "type": "uint256" } ], "stateMutability": "view", "type": "function" }, { "inputs": [], "name": "rewardPerTokenStored", "outputs": [ { "internalType": "uint256", "name": "", "type": "uint256" } ], "stateMutability": "view", "type": "function" }, { "inputs": [], "name": "rewardRate", "outputs": [ { "internalType": "uint256", "name": "", "type": "uint256" } ], "stateMutability": "view", "type": "function" }, { "inputs": [ { "internalType": "address", "name": "", "type": "address" } ], "name": "rewards", "outputs": [ { "internalType": "uint256", "name": "", "type": "uint256" } ], "stateMutability": "view", "type": "function" }, { "inputs": [], "name": "rewardsToken", "outputs": [ { "internalType": "contract IERC20", "name": "", "type": "address" } ], "stateMutability": "view", "type": "function" }, { "inputs": [], "name": "stakingToken", "outputs": [ { "internalType": "contract IERC20", "name": "", "type": "address" } ], "stateMutability": "view", "type": "function" }, { "inputs": [], "name": "totalSupply", "outputs": [ { "internalType": "uint256", "name": "", "type": "uint256" } ], "stateMutability": "view", "type": "function" }, { "inputs": [], "name": "updatedAt", "outputs": [ { "internalType": "uint256", "name": "", "type": "uint256" } ], "stateMutability": "view", "type": "function" }, { "inputs": [ { "internalType": "address", "name": "", "type": "address" } ], "name": "userRewardPerTokenPaid", "outputs": [ { "internalType": "uint256", "name": "", "type": "uint256" } ], "stateMutability": "view", "type": "function" } ] contract = web3.eth.contract(address=con_address, abi=abi) TotalSupply = contract.functions.getReward().call() contract.functions.setRewardsDuration(1).transact({'from': wal_address})
问题分析与解决方案
这个报错的核心原因是你连接的节点不识别wal_address这个账户,和合约Owner权限无关。常见解决方法如下:
解锁本地节点账户:如果使用本地Geth等节点,需要先解锁目标账户。可以通过命令行执行:
geth account unlock <你的账户地址>或者在Web3.py中调用:
web3.geth.personal.unlockAccount(wal_address, "你的账户密码")注意:仅测试环境使用,生产环境禁止硬编码密码。
本地签名交易后发送:如果连接的是远程节点(如自定义测试网的公共节点),节点不会存储你的私钥,需要本地签名交易再发送。修改Web3.py代码如下:
# 添加私钥变量 private_key = "你的账户私钥" # 构建交易参数 tx_params = { 'from': wal_address, 'nonce': web3.eth.get_transaction_count(wal_address), 'gas': 200000, 'gasPrice': web3.to_wei('5', 'gwei') } # 构建交易对象 tx = contract.functions.setRewardsDuration(1).build_transaction(tx_params) # 本地签名交易 signed_tx = web3.eth.account.sign_transaction(tx, private_key=private_key) # 发送签名后的交易 tx_hash = web3.eth.send_raw_transaction(signed_tx.rawTransaction) # 等待交易确认 tx_receipt = web3.eth.wait_for_transaction_receipt(tx_hash)校验地址格式:确认
wal_address格式正确,没有少输/多输字符,大小写一致。可以用web3.is_address(wal_address)验证地址有效性。检查节点同步状态:如果是自定义测试网,确认节点已同步到最新区块,否则可能无法识别账户的最新状态。
内容的提问来源于stack exchange,提问作者rrhlrmrr
相关产品推荐
相关产品推荐

