当我 运行 mainnet-fork-dev 上的合约函数时交易恢复
Transaction reverts when I run a contract function on mainnet-fork-dev
为了重现错误,可以从https://github.com/Binoy-John/brownie_fund_me
中获取代码
所以我有一个合同 FundMe.sol,它有几个函数,其中之一是 getEntranceFee()。如下图:
// SPDX-License-Identifier: MIT
// Smart contract that lets anyone deposit ETH into the contract
// Only the owner of the contract can withdraw the ETH
pragma solidity ^0.8.0;
// Get the latest ETH/USD price from chainlink price feed
import "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol";
contract FundMe {
// safe math library check uint256 for integer overflows
// using SafeMathChainlink for uint256;
//mapping to store which address depositeded how much ETH
mapping(address => uint256) public addressToAmountFunded;
// array of addresses who deposited
address[] public funders;
//address of the owner (who deployed the contract)
address public owner;
//creating interface variable for use with parameterized constructor
AggregatorV3Interface public priceFeed;
// the first person to deploy the contract is
// the owner
constructor(address _priceFeed) {
owner = msg.sender;
priceFeed = AggregatorV3Interface(_priceFeed);
}
function fund() public payable {
// 18 digit number to be compared with donated amount
uint256 minimumUSD = 50 * 10**18;
//is the donated amount less than 50USD?
require(
getConversionRate(msg.value) >= minimumUSD,
"You need to spend more ETH!"
);
//if not, add to mapping and funders array
addressToAmountFunded[msg.sender] += msg.value;
funders.push(msg.sender);
}
function getEntranceFee() public view returns (uint256) {
// minimumUSD
uint256 minimumUSD = 50 * 10**18;
uint256 price = getPrice();
uint256 precision = 1 * 10**18;
return (minimumUSD * precision) / price;
}
//function to get the version of the chainlink pricefeed
function getVersion() public view returns (uint256) {
return priceFeed.version();
}
function getPrice() public view returns (uint256) {
(, int256 answer, , , ) = priceFeed.latestRoundData();
// ETH/USD rate in 18 digit
return uint256(answer * 10000000000);
}
// 1000000000
function getConversionRate(uint256 ethAmount)
public
view
returns (uint256)
{
uint256 ethPrice = getPrice();
uint256 ethAmountInUsd = (ethPrice * ethAmount) / 1000000000000000000;
// the actual ETH/USD conversation rate, after adjusting the extra 0s.
return ethAmountInUsd;
}
//modifier: https://medium.com/coinmonks/solidity-tutorial-all-about-modifiers-a86cf81c14cb
modifier onlyOwner() {
//is the message sender owner of the contract?
require(msg.sender == owner);
_;
}
// onlyOwner modifer will first check the condition inside it
// and
// if true, withdraw function will be executed
function withdraw() public payable onlyOwner {
// If you are using version eight (v0.8) of chainlink aggregator interface,
// you will need to change the code below to
payable(msg.sender).transfer(address(this).balance);
//msg.sender.transfer(address(this).balance);
//iterate through all the mappings and make them 0
//since all the deposited amount has been withdrawn
for (
uint256 funderIndex = 0;
funderIndex < funders.length;
funderIndex++
) {
address funder = funders[funderIndex];
addressToAmountFunded[funder] = 0;
}
//funders array will be initialized to 0
funders = new address[](0);
}
}
我使用一个名为 deploy.py 的脚本来部署这个合约和 运行 它的一些功能。如下图:
from brownie import FundMe, accounts, config, network, MockV3Aggregator
from scripts.helpful_scripts import (
deploy_mocks,
get_account,
LOCAL_BLOCKCHAIN_ENVIRONMENT,
)
from web3 import Web3
def deploy_fund_me():
account = get_account()
print("account used is ", account)
if network.show_active() not in LOCAL_BLOCKCHAIN_ENVIRONMENT:
price_feed_address = config["networks"][network.show_active()][
"eth_usd_priceFeed"
]
else:
deploy_mocks()
price_feed_address = MockV3Aggregator[-1].address
fund_me = FundMe.deploy(
price_feed_address,
{"from": account},
publish_source=config["networks"][network.show_active()].get("verify"),
)
print("contract deployed to ", fund_me.address)
print("entree fee is ", fund_me.getEntranceFee())
return fund_me
def main():
deploy_fund_me()
它使用另一个名为 helpful_scripts.py 的脚本来创建帐户和部署模拟。具体如下:
from brownie import config, network, accounts, MockV3Aggregator
from web3 import Web3
FORKED_LOCAL_ENVIRONMENT = ["mainnet-fork", "mainnet-fork-dev"]
LOCAL_BLOCKCHAIN_ENVIRONMENT = ["development", "ganache-local"]
DECIMALS = 8
STARTING = 2000000000000000000000
def get_account():
if (
network.show_active() in LOCAL_BLOCKCHAIN_ENVIRONMENT
or network.show_active() in FORKED_LOCAL_ENVIRONMENT
):
return accounts[0]
else:
# accounts.add(config["wallets"]["from_key"])
# return config["wallets"]["from_key"]
# print(accounts.add(config["wallets"]["from_key"]))
return accounts.add(config["wallets"]["from_key"])
def deploy_mocks():
account = get_account()
print("from deploy ")
print("the active network is ", network.show_active())
print("deploying the contract")
if len(MockV3Aggregator) <= 0:
MockV3Aggregator.deploy(DECIMALS, STARTING, {"from": account})
print("Deployed a new Mock because the length of MockV3Aggregator was 0")
# print("mock aggregator is ", mock_aggregator)
print("Mock deployed!")
# print("Mocks deployed on ", mock_aggregator)
当我 运行 deploy.py 在 rinkeby 和 ganache-cli 网络上使用
$布朗尼 运行 deploy.py --network rinkeby
要么
$布朗尼 运行 deploy.py
我得到正确的输出:
Attached to local RPC client listening at '127.0.0.1:8545'...
Running 'scripts\deploy.py::main'...
account used is 0xFF3c899C61625bF772bA029C50D27E33db52f2D9
from deploy
the active network is development
deploying the contract
Transaction sent: 0x9d6896cc8b2ea9ce24615a4bc6c069ab8b30e2ed44fb321677f5ffde30816338
Gas price: 0.0 gwei Gas limit: 12000000 Nonce: 104
MockV3Aggregator.constructor confirmed Block: 113 Gas used: 437447 (3.65%)
MockV3Aggregator deployed at: 0x3eD74772c3403fa96826c38D94Ad67685B25f2D0
Deployed a new Mock because the length of MockV3Aggregator was 0
Mock deployed!
Transaction sent: 0x3cffbacb081fd70860783e497a3dbf293c0d03e2a9cf78335d67ae021b209615
Gas price: 0.0 gwei Gas limit: 12000000 Nonce: 105
FundMe.constructor confirmed Block: 114 Gas used: 515717 (4.30%)
FundMe deployed at: 0xDeFd78dC7D60132FEaa39AD3f31E20738471cD08
contract deployed to 0xDeFd78dC7D60132FEaa39AD3f31E20738471cD08
entree fee is 2500000
但是当我 运行 使用 deploy.py
$ brownie 运行 deploy.py --network mainnet-fork-dev
它给了我以下错误:
$ brownie run deploy.py --network mainnet-fork-dev
INFO: Could not find files for the given pattern(s).
Brownie v1.16.4 - Python development framework for Ethereum
BrownieFundMeProject is the active project.
C:\Users989\AppData\Roaming\Python\Python310\site-packages\brownie\network\main.py:44: BrownieEnvironmentWarning: Development network has a block height of 114
warnings.warn(
Attached to local RPC client listening at '127.0.0.1:8545'...
Running 'scripts\deploy.py::main'...
account used is 0xFF3c899C61625bF772bA029C50D27E33db52f2D9
Transaction sent: 0xbb0c1ab97868e2851a586fddd4b64d23ec317dbba05efd35ff9fe04e03f54e3a
Gas price: 0.0 gwei Gas limit: 12000000 Nonce: 106
FundMe.constructor confirmed Block: 115 Gas used: 515717 (4.30%)
FundMe deployed at: 0x948E3BA0dE6FADCE5e1459F5432A48Ef3DC7F70b
contract deployed to 0x948E3BA0dE6FADCE5e1459F5432A48Ef3DC7F70b
File "C:\Users989\AppData\Roaming\Python\Python310\site-packages\brownie\_cli\run.py", line 49,
in main
return_value, frame = run(
File "C:\Users989\AppData\Roaming\Python\Python310\site-packages\brownie\project\scripts.py", line 103, in run
return_value = f_locals[method_name](*args, **kwargs)
File ".\scripts\deploy.py", line 32, in main
deploy_fund_me()
File ".\scripts\deploy.py", line 27, in deploy_fund_me
print("entree fee is ", fund_me.getEntranceFee())
File "C:\Users989\AppData\Roaming\Python\Python310\site-packages\brownie\network\multicall.py",
line 115, in _proxy_call
result = ContractCall.__call__(*args, **kwargs) # type: ignore
File "C:\Users989\AppData\Roaming\Python\Python310\site-packages\brownie\network\contract.py", line 1729, in __call__
return self.call(*args, block_identifier=block_identifier)
File "C:\Users989\AppData\Roaming\Python\Python310\site-packages\brownie\network\contract.py", line 1533, in call
raise VirtualMachineError(e) from None
File "C:\Users989\AppData\Roaming\Python\Python310\site-packages\brownie\exceptions.py", line 104, in __init__
raise ValueError(exc["message"]) from None
ValueError: VM Exception while processing transaction: revert
所以只有当 运行在 mainnet-fork-dev 上而不是其他地方时,我才会遇到这个问题。我对如何解决这个问题感到困惑,所以任何帮助将不胜感激!
如果您需要更多信息,请告诉我。
提前致谢!
function getPrice() public view returns (uint256) {
(, int256 answer, , , ) = priceFeed.latestRoundData();
// ETH/USD rate in 18 digit
return uint256(answer * 10000000000);
}
这是导致问题的代码。我认为 mainnet-fork-dev 的喂价合约地址不正确。这就是为什么除了这个以外的其他网络。
为了重现错误,可以从https://github.com/Binoy-John/brownie_fund_me
中获取代码所以我有一个合同 FundMe.sol,它有几个函数,其中之一是 getEntranceFee()。如下图:
// SPDX-License-Identifier: MIT
// Smart contract that lets anyone deposit ETH into the contract
// Only the owner of the contract can withdraw the ETH
pragma solidity ^0.8.0;
// Get the latest ETH/USD price from chainlink price feed
import "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol";
contract FundMe {
// safe math library check uint256 for integer overflows
// using SafeMathChainlink for uint256;
//mapping to store which address depositeded how much ETH
mapping(address => uint256) public addressToAmountFunded;
// array of addresses who deposited
address[] public funders;
//address of the owner (who deployed the contract)
address public owner;
//creating interface variable for use with parameterized constructor
AggregatorV3Interface public priceFeed;
// the first person to deploy the contract is
// the owner
constructor(address _priceFeed) {
owner = msg.sender;
priceFeed = AggregatorV3Interface(_priceFeed);
}
function fund() public payable {
// 18 digit number to be compared with donated amount
uint256 minimumUSD = 50 * 10**18;
//is the donated amount less than 50USD?
require(
getConversionRate(msg.value) >= minimumUSD,
"You need to spend more ETH!"
);
//if not, add to mapping and funders array
addressToAmountFunded[msg.sender] += msg.value;
funders.push(msg.sender);
}
function getEntranceFee() public view returns (uint256) {
// minimumUSD
uint256 minimumUSD = 50 * 10**18;
uint256 price = getPrice();
uint256 precision = 1 * 10**18;
return (minimumUSD * precision) / price;
}
//function to get the version of the chainlink pricefeed
function getVersion() public view returns (uint256) {
return priceFeed.version();
}
function getPrice() public view returns (uint256) {
(, int256 answer, , , ) = priceFeed.latestRoundData();
// ETH/USD rate in 18 digit
return uint256(answer * 10000000000);
}
// 1000000000
function getConversionRate(uint256 ethAmount)
public
view
returns (uint256)
{
uint256 ethPrice = getPrice();
uint256 ethAmountInUsd = (ethPrice * ethAmount) / 1000000000000000000;
// the actual ETH/USD conversation rate, after adjusting the extra 0s.
return ethAmountInUsd;
}
//modifier: https://medium.com/coinmonks/solidity-tutorial-all-about-modifiers-a86cf81c14cb
modifier onlyOwner() {
//is the message sender owner of the contract?
require(msg.sender == owner);
_;
}
// onlyOwner modifer will first check the condition inside it
// and
// if true, withdraw function will be executed
function withdraw() public payable onlyOwner {
// If you are using version eight (v0.8) of chainlink aggregator interface,
// you will need to change the code below to
payable(msg.sender).transfer(address(this).balance);
//msg.sender.transfer(address(this).balance);
//iterate through all the mappings and make them 0
//since all the deposited amount has been withdrawn
for (
uint256 funderIndex = 0;
funderIndex < funders.length;
funderIndex++
) {
address funder = funders[funderIndex];
addressToAmountFunded[funder] = 0;
}
//funders array will be initialized to 0
funders = new address[](0);
}
}
我使用一个名为 deploy.py 的脚本来部署这个合约和 运行 它的一些功能。如下图:
from brownie import FundMe, accounts, config, network, MockV3Aggregator
from scripts.helpful_scripts import (
deploy_mocks,
get_account,
LOCAL_BLOCKCHAIN_ENVIRONMENT,
)
from web3 import Web3
def deploy_fund_me():
account = get_account()
print("account used is ", account)
if network.show_active() not in LOCAL_BLOCKCHAIN_ENVIRONMENT:
price_feed_address = config["networks"][network.show_active()][
"eth_usd_priceFeed"
]
else:
deploy_mocks()
price_feed_address = MockV3Aggregator[-1].address
fund_me = FundMe.deploy(
price_feed_address,
{"from": account},
publish_source=config["networks"][network.show_active()].get("verify"),
)
print("contract deployed to ", fund_me.address)
print("entree fee is ", fund_me.getEntranceFee())
return fund_me
def main():
deploy_fund_me()
它使用另一个名为 helpful_scripts.py 的脚本来创建帐户和部署模拟。具体如下:
from brownie import config, network, accounts, MockV3Aggregator
from web3 import Web3
FORKED_LOCAL_ENVIRONMENT = ["mainnet-fork", "mainnet-fork-dev"]
LOCAL_BLOCKCHAIN_ENVIRONMENT = ["development", "ganache-local"]
DECIMALS = 8
STARTING = 2000000000000000000000
def get_account():
if (
network.show_active() in LOCAL_BLOCKCHAIN_ENVIRONMENT
or network.show_active() in FORKED_LOCAL_ENVIRONMENT
):
return accounts[0]
else:
# accounts.add(config["wallets"]["from_key"])
# return config["wallets"]["from_key"]
# print(accounts.add(config["wallets"]["from_key"]))
return accounts.add(config["wallets"]["from_key"])
def deploy_mocks():
account = get_account()
print("from deploy ")
print("the active network is ", network.show_active())
print("deploying the contract")
if len(MockV3Aggregator) <= 0:
MockV3Aggregator.deploy(DECIMALS, STARTING, {"from": account})
print("Deployed a new Mock because the length of MockV3Aggregator was 0")
# print("mock aggregator is ", mock_aggregator)
print("Mock deployed!")
# print("Mocks deployed on ", mock_aggregator)
当我 运行 deploy.py 在 rinkeby 和 ganache-cli 网络上使用 $布朗尼 运行 deploy.py --network rinkeby 要么 $布朗尼 运行 deploy.py 我得到正确的输出:
Attached to local RPC client listening at '127.0.0.1:8545'...
Running 'scripts\deploy.py::main'...
account used is 0xFF3c899C61625bF772bA029C50D27E33db52f2D9
from deploy
the active network is development
deploying the contract
Transaction sent: 0x9d6896cc8b2ea9ce24615a4bc6c069ab8b30e2ed44fb321677f5ffde30816338
Gas price: 0.0 gwei Gas limit: 12000000 Nonce: 104
MockV3Aggregator.constructor confirmed Block: 113 Gas used: 437447 (3.65%)
MockV3Aggregator deployed at: 0x3eD74772c3403fa96826c38D94Ad67685B25f2D0
Deployed a new Mock because the length of MockV3Aggregator was 0
Mock deployed!
Transaction sent: 0x3cffbacb081fd70860783e497a3dbf293c0d03e2a9cf78335d67ae021b209615
Gas price: 0.0 gwei Gas limit: 12000000 Nonce: 105
FundMe.constructor confirmed Block: 114 Gas used: 515717 (4.30%)
FundMe deployed at: 0xDeFd78dC7D60132FEaa39AD3f31E20738471cD08
contract deployed to 0xDeFd78dC7D60132FEaa39AD3f31E20738471cD08
entree fee is 2500000
但是当我 运行 使用 deploy.py $ brownie 运行 deploy.py --network mainnet-fork-dev 它给了我以下错误:
$ brownie run deploy.py --network mainnet-fork-dev
INFO: Could not find files for the given pattern(s).
Brownie v1.16.4 - Python development framework for Ethereum
BrownieFundMeProject is the active project.
C:\Users989\AppData\Roaming\Python\Python310\site-packages\brownie\network\main.py:44: BrownieEnvironmentWarning: Development network has a block height of 114
warnings.warn(
Attached to local RPC client listening at '127.0.0.1:8545'...
Running 'scripts\deploy.py::main'...
account used is 0xFF3c899C61625bF772bA029C50D27E33db52f2D9
Transaction sent: 0xbb0c1ab97868e2851a586fddd4b64d23ec317dbba05efd35ff9fe04e03f54e3a
Gas price: 0.0 gwei Gas limit: 12000000 Nonce: 106
FundMe.constructor confirmed Block: 115 Gas used: 515717 (4.30%)
FundMe deployed at: 0x948E3BA0dE6FADCE5e1459F5432A48Ef3DC7F70b
contract deployed to 0x948E3BA0dE6FADCE5e1459F5432A48Ef3DC7F70b
File "C:\Users989\AppData\Roaming\Python\Python310\site-packages\brownie\_cli\run.py", line 49,
in main
return_value, frame = run(
File "C:\Users989\AppData\Roaming\Python\Python310\site-packages\brownie\project\scripts.py", line 103, in run
return_value = f_locals[method_name](*args, **kwargs)
File ".\scripts\deploy.py", line 32, in main
deploy_fund_me()
File ".\scripts\deploy.py", line 27, in deploy_fund_me
print("entree fee is ", fund_me.getEntranceFee())
File "C:\Users989\AppData\Roaming\Python\Python310\site-packages\brownie\network\multicall.py",
line 115, in _proxy_call
result = ContractCall.__call__(*args, **kwargs) # type: ignore
File "C:\Users989\AppData\Roaming\Python\Python310\site-packages\brownie\network\contract.py", line 1729, in __call__
return self.call(*args, block_identifier=block_identifier)
File "C:\Users989\AppData\Roaming\Python\Python310\site-packages\brownie\network\contract.py", line 1533, in call
raise VirtualMachineError(e) from None
File "C:\Users989\AppData\Roaming\Python\Python310\site-packages\brownie\exceptions.py", line 104, in __init__
raise ValueError(exc["message"]) from None
ValueError: VM Exception while processing transaction: revert
所以只有当 运行在 mainnet-fork-dev 上而不是其他地方时,我才会遇到这个问题。我对如何解决这个问题感到困惑,所以任何帮助将不胜感激!
如果您需要更多信息,请告诉我。
提前致谢!
function getPrice() public view returns (uint256) {
(, int256 answer, , , ) = priceFeed.latestRoundData();
// ETH/USD rate in 18 digit
return uint256(answer * 10000000000);
}
这是导致问题的代码。我认为 mainnet-fork-dev 的喂价合约地址不正确。这就是为什么除了这个以外的其他网络。