Solidity
布朗尼氣體估計失敗:“執行恢復”
我正在完成高級 NFT 的 freecodecamp solidity 教程,並在部署到 rinkeby 測試網時返回:
ValueError: Gas estimation failed: 'execution reverted'. This transaction will likely revert. If you wish to broadcast, you must set the gas limit manually.
當我使用 fund_with_link 代幣時,我的合約已獲得資金,但我無法呼叫 create_collectible 函式而不返回錯誤消息。我的元遮罩帳戶中確實有足夠的連結。
這是合約程式碼:
pragma solidity ^0.7.0; import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; import "@chainlink/contracts/src/v0.7/VRFConsumerbase.sol"; contract AdvancedCollectible is ERC721, VRFConsumerBase { uint256 public tokenCounter; bytes32 public keyhash; uint256 public fee; enum Breed { PUG, SHIBA_INU, ST_BERNARD } mapping(uint256 => Breed) public tokenIdToBreed; mapping(bytes32 => address) requestIdToSender; event requestedCollectible(bytes32 indexed requestId, address requester); event breedAssigned(uint256 indexed tokenId, Breed breed); constructor( address _vrfCoordinator, address _linkToken, bytes32 _keyhash, uint256 _fee ) public VRFConsumerBase(_vrfCoordinator, _linkToken) ERC721("Dogie", "DOG") { tokenCounter = 0; keyhash = _keyhash; fee = _fee; } function createCollectible(string memory tokenURI) public returns (bytes32) { bytes32 requestId = requestRandomness(keyhash, fee); requestIdToSender[requestId] = msg.sender; emit requestedCollectible(requestId, msg.sender); } function fulfillRandomness(bytes32 requestId, uint256 randomNumber) internal override { Breed breed = Breed(randomNumber % 3); uint256 newTokenId = tokenCounter; tokenIdToBreed[newTokenId] = breed; emit breedAssigned(newTokenId, breed); address owner = requestIdToSender[requestId]; _safeMint(owner, newTokenId); tokenCounter = tokenCounter + 1; } function setTokenURI(uint256 tokenId, string memory _tokenURI) public { //need tokenId for pug, shiba-inu and st-bernard //making sure that only the owner of the tokenId can update the tokenURI require( _isApprovedOrOwner(_msgSender(), tokenId), "ERC721: is not owner, not approved" ); } }
這是部署腳本:
from scripts.helpful_scripts import ( get_account, OPENSEA_URL, get_contract, fund_with_link, ) from brownie import AdvancedCollectible, config, network from web3 import Web3 from time import sleep sample_token_uri = "https://ipfs.io/ipfs/Qmd9MCGtdVz2miNumBHDbvj8bigSgTwnr4SbyH6DNnpWdt?filename=0-PUG.json" def deploy_and_create(): account = get_account() advanced_collectible = AdvancedCollectible.deploy( get_contract("vrf_coordinator"), get_contract("link_token"), config["networks"][network.show_active()]["keyhash"], config["networks"][network.show_active()]["fee"], {"from": account}, ) fund_with_link(advanced_collectible.address) create_collectible = advanced_collectible.createCollectible( sample_token_uri, {"from": account} ) def main(): deploy_and_create()
以下是有用的腳本:
from brownie import network, config, accounts, LinkToken, VRFCoordinatorMock, Contract import dotenv from web3 import Web3 OPENSEA_URL = "https://testnets.opensea.io/assets/{}/{}" LOCAL_BLOCKCHAIN_ENVIRONMENTS = ["hardhat", "development", "ganache", "mainnet-fork"] def get_account(index=None, id=None): if index: return accounts[index] if network.show_active() in LOCAL_BLOCKCHAIN_ENVIRONMENTS: return accounts[0] if id: return accounts.load(id) return accounts.add(config["wallets"]["from_key"]) contract_to_mock = {"link_token": LinkToken, "vrf_coordinator": VRFCoordinatorMock} def get_contract(contract_name): """ This function will either: - Get an address from the config - Or deploy a Mock to use for a network that doesn't have the contract Args: contract_name (string): This is the name of the contract that we will get from the config or deploy Returns: brownie.network.contract.ProjectContract: This is the most recently deployed Contract of the type specified by a dictionary. This could either be a mock or a 'real' contract on a live network. """ # link_token # LinkToken contract_type = contract_to_mock[contract_name] if network.show_active() in LOCAL_BLOCKCHAIN_ENVIRONMENTS: if len(contract_type) <= 0: deploy_mocks() contract = contract_type[-1] else: contract_address = config["networks"][network.show_active()][contract_name] contract = Contract.from_abi( contract_type._name, contract_address, contract_type.abi ) return contract def deploy_mocks(): """ Use this script if you want to deploy mocks to a testnet """ print(f"The active network is {network.show_active()}") print("Deploying mocks...") account = get_account() print("Deploying Mock LinkToken...") link_token = LinkToken.deploy({"from": account}) print(f"Link Token deployed to {link_token.address}") print("Deploying Mock VRF Coordinator...") vrf_coordinator = VRFCoordinatorMock.deploy(link_token.address, {"from": account}) print(f"VRFCoordinator deployed to {vrf_coordinator.address}") print("All done!") def fund_with_link( contract_address, account=None, link_token=None, amount=Web3.toWei(2, "ether") ): account = account if account else get_account() link_token = link_token if link_token else get_contract("link_token") funding_tx = link_token.transfer(contract_address, amount, {"from": account}) funding_tx.wait(1) print(f"Funded {contract_address}") return funding_tx
這是 brownie-config.yaml:
dotenv: .env dependencies: - OpenZeppelin/openzeppelin-contracts@3.4.0 - smartcontractkit/chainlink-brownie-contracts@0.2.1 compiler: solc: remappings: - '@openzeppelin=OpenZeppelin/openzeppelin-contracts@3.4.0' - '@chainlink=smartcontractkit/chainlink-brownie-contracts@0.2.1' wallets: from_key: ${PRIVATE_KEY} networks: development: keyhash: "0xd89b2bf150e3b9e13446986e571fb9cab24b13cea0a43ea20a6049a85cc807cc" fee: 100000000000000000 rinkeby: vrf_coordinator: "0x6168499c0cFfCaCD319c818142124B7A15E857ab" link_token: "0x01BE23585060835E02B77ef475b0Cc51aA1e0709" keyhash: "0xd89b2bf150e3b9e13446986e571fb9cab24b13cea0a43ea20a6049a85cc807cc" fee: 10000000000000000
任何幫助,將不勝感激 :)
我找到了一個解決方案,只是我很愚蠢——這並不奇怪。我只是在 brownie-config.yaml 文件中為費用寫了錯誤數量的零。我也有錯誤的 keyhash eth 地址。