我在使用 Truffle、VS Code 和 Metamask 在 Polygon 测试网上部署智能合约时遇到问题。合约在 Sepolia testnet 和 Remix IDE(Polygon) 上成功部署,但在 Polygon 上部署时抛出以下错误
"APIConsumer" hit an invalid opcode while deploying. Try:
* Verifying that your constructor params satisfy all assert conditions.
* Verifying your constructor code doesn't access an array out of bounds.
* Adding reason strings to your assert statements.
我的智能合约代码如下:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.7;
import "@chainlink/contracts/src/v0.8/ChainlinkClient.sol";
/**
* @title The APIConsumer contract
* @notice An API Consumer contract that makes GET requests to obtain 24h trading volume of ETH in USD
*/
contract APIConsumer is ChainlinkClient {
using Chainlink for Chainlink.Request;
string public volume;
address private immutable oracle;
string private jobId;
uint256 private immutable fee;
event DataFullfilled(string volume);
constructor(
) {
setChainlinkToken(0x326C977E6efc84E512bB9C30f76E30c160eD06FB);
setChainlinkOracle(0xB7E946A727090285a2d5BC5A2D837bD45539d4c8);
oracle = 0xB7E946A727090285a2d5BC5A2D837bD45539d4c8;
jobId = "70717430666f4c0d8452ca6ceec0b656";
fee = (1 * LINK_DIVISIBILITY) / 10;
}
/**
* @notice Creates a Chainlink request to retrieve API response, find the target
* data, then multiply by 1000000000000000000 (to remove decimal places from data).
*
* @return requestId - id of the request
*/
function requestVolumeData() public returns (bytes32 requestId) {
Chainlink.Request memory request = buildChainlinkRequest(
stringToBytes32(jobId),
address(this),
this.fulfill.selector
);
// Set the URL to perform the GET request on
// Multiply the result by 1000000000000000000 to remove decimals
// Sends the request
return sendChainlinkRequestTo(oracle, request, fee);
}
function stringToBytes32(
string memory source
) private pure returns (bytes32 result) {
bytes memory tempEmptyStringTest = bytes(source);
if (tempEmptyStringTest.length == 0) {
return 0x0;
}
assembly {
// solhint-disable-line no-inline-assembly
result := mload(add(source, 32))
}
}
/**
* @notice Receives the response in the form of uint256
*
* @param _requestId - id of the request
* @param _volume - response; requested 24h trading volume of ETH in USD
*/
function fulfill(
bytes32 _requestId,
string memory _volume
) public recordChainlinkFulfillment(_requestId) {
volume = _volume;
emit DataFullfilled(volume);
}
function read() public view returns (string memory) {
return volume;
}
/**
* @notice Witdraws LINK from the contract
* @dev Implement a withdraw function to avoid locking your LINK in the contract
*/
function withdrawLink() external {}
}
我的迁移文件 - 1_deploy_simple_storage.js 文件
const APIConsumer = artifacts.require("APIConsumer")
const LinkToken = artifacts.require("LinkToken")
const MockOracle = artifacts.require("MockOracle")
const web3 = require("web3")
const { networkConfig, developmentChains } = require("../helper-truffle-config")
const { fundContractWithLink } = require("../scripts/utils/fundContract")
module.exports = async function (deployer, network, accounts) {
let oracle, linkTokenAddress
if (developmentChains.includes(network)) {
const linkToken = await LinkToken.deployed()
const mockOracle = await MockOracle.deployed()
linkTokenAddress = linkToken.address
oracle = mockOracle.address
} else {
console.log(networkConfig[network])
linkTokenAddress = networkConfig[network]["linkToken"]
oracle = networkConfig[network]["oracle"]
}
const jobId = web3.utils.toHex(networkConfig[network]["jobId"])
const fee = networkConfig[network]["fee"]
await deployer.deploy(APIConsumer)
console.log("API Consumer Deployed!")
console.log("Let's fund the contract with Link...")
const apiConsumer = await APIConsumer.deployed()
await fundContractWithLink(apiConsumer.address, network)
}
helper-truffle-config
const networkConfig = {
test: {
fee: "100000000000000000",
keyHash: "0xd89b2bf150e3b9e13446986e571fb9cab24b13cea0a43ea20a6049a85cc807cc",
jobId: "29fa9aa13bf1468788b7cc4a500a45b8",
fundAmount: "1000000000000000000",
keepersUpdateInterval: "30",
ethUsdPriceFeed: "0x5f4eC3Df9cbd43714FE2740f5E3616155c5b8419",
},
ganache: {
fee: "100000000000000000",
keyHash: "0xd89b2bf150e3b9e13446986e571fb9cab24b13cea0a43ea20a6049a85cc807cc",
jobId: "29fa9aa13bf1468788b7cc4a500a45b8",
fundAmount: "1000000000000000000",
keepersUpdateInterval: "30",
ethUsdPriceFeed: "0x5f4eC3Df9cbd43714FE2740f5E3616155c5b8419",
},
goerli: {
chainId: "5",
linkToken: "0x326C977E6efc84E512bB9C30f76E30c160eD06FB",
ethUsdPriceFeed: "0xD4a33860578De61DBAbDc8BFdb98FD742fA7028e",
keyHash: "0x79d3d8832d904592c0bf9818b621522c988bb8b0c05cdc3b15aea1b6e8db0c15",
vrfCoordinator: "0x2Ca8E0C643bDe4C2E08ab1fA0da3401AdAD7734D",
oracle: "0xCC79157eb46F5624204f47AB42b3906cAA40eaB7",
jobId: "ca98366cc7314957b8c012c72f05aeeb",
fee: "11000000000000000",
fundAmount: "11000000000000000", // 0.1
keepersUpdateInterval: "30",
subId: "52", // add your subscription Id here!
},
sepolia: {
chainId: "11155111",
linkToken: "0x779877A7B0D9E8603169DdbD7836e478b4624789",
ethUsdPriceFeed: "0x694AA1769357215DE4FAC081bf1f309aDC325306",
keyHash: "0x474e34a077df58807dbe9c96d3c009b23b3c6d0cce433e59bbf5b34f823bc56c",
vrfCoordinator: "0x8103B0A8A00be2DDC778e6e7eaa21791Cd364625",
oracle: "0x3016e3b55bF31cC50E78933Fb536efD2B29ed3bE",
jobId: "c3c788ba52ba4ee387eeef344d77c793",
fee: "11000000000000000",
fundAmount: "11000000000000000", // 0.1
keepersUpdateInterval: "30",
subId: "52", // add your subscription Id here!
},
polygon: {
chainId: "80001",
linkToken: "0x326C977E6efc84E512bB9C30f76E30c160eD06FB",
ethUsdPriceFeed: "0xF9680D99D6C9589e2a93a78A04A279e509205945",
keyHash: "0x4b09e658ed251bcafeebbc69400383d49f344ace09b9576fe248bb02c003fe9f",
oracle: "0x40193c8518BB267228Fc409a613bDbD8eC5a97b3",
jobId: "7d80a6386ef543a3abb52817f6707e3b",
fee: "2500000",
fundAmount: "100000000000000000", // 0.1
keepersUpdateInterval: "30",
// subId: "52", // add your subscription Id here!
},
rinkeby: {
chainId: "4",
linkToken: "0x01be23585060835e02b77ef475b0cc51aa1e0709",
ethUsdPriceFeed: "0x8A753747A1Fa494EC906cE90E9f37563A8AF630e",
keyHash: "0xd89b2bf150e3b9e13446986e571fb9cab24b13cea0a43ea20a6049a85cc807cc",
vrfCoordinator: "0x6168499c0cFfCaCD319c818142124B7A15E857ab",
oracle: "0xc57b33452b4f7bb189bb5afae9cc4aba1f7a4fd8",
jobId: "6b88e0402e5d415eb946e528b8e0c7ba",
fee: "100000000000000000",
fundAmount: "100000000000000000", // 0.1
keepersUpdateInterval: "30",
subId: "0", // add your subscription Id here!
},
}
const developmentChains = ["test", "development", "ganache"]
module.exports = {
networkConfig,
developmentChains,
}
truffle-config.js
/**
* Use this file to configure your truffle project. It's seeded with some
* common settings for different networks and features like migrations,
* compilation, and testing. Uncomment the ones you need or modify
* them to suit your project as necessary.
*
* More information about configuration can be found at:
*
* https://trufflesuite.com/docs/truffle/reference/configuration
*
* Hands-off deployment with Infura
* --------------------------------
*
* Do you have a complex application that requires lots of transactions to deploy?
* Use this approach to make deployment a breeze 🏖️:
*
* Infura deployment needs a wallet provider (like @truffle/hdwallet-provider)
* to sign transactions before they're sent to a remote public node.
* Infura accounts are available for free at 🔍: https://infura.io/register
*
* You'll need a mnemonic - the twelve word phrase the wallet uses to generate
* public/private key pairs. You can store your secrets 🤐 in a .env file.
* In your project root, run `$ npm install dotenv`.
* Create .env (which should be .gitignored) and declare your MNEMONIC
* and Infura PROJECT_ID variables inside.
* For example, your .env file will have the following structure:
*
* MNEMONIC = <Your 12 phrase mnemonic>
* PROJECT_ID = <Your Infura project id>
*
* Deployment with Truffle Dashboard (Recommended for best security practice)
* --------------------------------------------------------------------------
*
* Are you concerned about security and minimizing rekt status 🤔?
* Use this method for best security:
*
* Truffle Dashboard lets you review transactions in detail, and leverages
* MetaMask for signing, so there's no need to copy-paste your mnemonic.
* More details can be found at 🔎:
*
* https://trufflesuite.com/docs/truffle/getting-started/using-the-truffle-dashboard/
*/
// require('dotenv').config();
// const { MNEMONIC, PROJECT_ID } = process.env;
// const HDWalletProvider = require('@truffle/hdwallet-provider');
const HDWalletProvider = require("@truffle/hdwallet-provider")
require("dotenv").config()
const privateKey = process.env.PRIVATE_KEY
// These are the keys auto-generated from running `ganache -d`
const GOERLI_RPC_URL =
process.env.GOERLI_RPC_URL || "https://eth-goerli.alchemyapi.io/v2/your-api-key"
const SEPOLIA_RPC_URL =
process.env.SEPOLIA_RPC_URL || "https://eth-goerli.alchemyapi.io/v2/your-api-key"
console.log(process.env.GOERLI_RPC_URL);
const POLYGON_RPC_URL =
process.env.POLYGON_RPC_URL
module.exports = {
/**
* Networks define how you connect to your ethereum client and let you set the
* defaults web3 uses to send transactions. If you don't specify one truffle
* will spin up a managed Ganache instance for you on port 9545 when you
* run `develop` or `test`. You can ask a truffle command to use a specific
* network from the command line, e.g
*
* $ truffle test --network <network-name>
*/
contracts_build_directory: "../client/src/contracts",
networks: {
ganache: {
// provider: () => new HDWalletProvider(ganachePrivateKeys, "http://127.0.0.1:7545"),
host: "127.0.0.1", // Localhost (default: none)
port: 7545, // Standard Ethereum port (default: none)
network_id: "5777", // Any network (default: none)
// confirmations: 0,
},
goerli: {
provider: () => new HDWalletProvider(privateKey, GOERLI_RPC_URL),
network_id: 5, // Goerli's id
confirmations: 1, // # of confirmations to wait between deployments. (default: 0)
timeoutBlocks: 20000000000, // # of blocks before a deployment times out (minimum/default: 50)
skipDryRun: true, // Skip dry run before migrations? (default: false for public nets ),
networkCheckTimeout: 100000000,
timeoutBlocks: 2000000000
},
polygon: {
provider: () => new HDWalletProvider(privateKey, POLYGON_RPC_URL),
network_id: 80001,
confirmations: 2,
timeoutBlocks: 200,
skipDryRun: true,
networkCheckTimeout: 9000000,
pollingInterval: 1800000,
disableConfirmationListener: true
},
sepolia: {
provider: () => new HDWalletProvider(privateKey, SEPOLIA_RPC_URL),
network_id: 11155111, // Goerli's id
confirmations: 1, // # of confirmations to wait between deployments. (default: 0)
timeoutBlocks: 20000000000, // # of blocks before a deployment times out (minimum/default: 50)
skipDryRun: true, // Skip dry run before migrations? (default: false for public nets ),
networkCheckTimeout: 100000000,
timeoutBlocks: 2000000000
},
// Useful for testing. The `development` name is special - truffle uses it by default
// if it's defined here and no other network is specified at the command line.
// You should run a client (like ganache, geth, or parity) in a separate terminal
// tab if you use this network and you must also set the `host`, `port` and `network_id`
// options below to some value.
//
// development: {
// host: "127.0.0.1", // Localhost (default: none)
// port: 8545, // Standard Ethereum port (default: none)
// network_id: "*", // Any network (default: none)
// },
//
// An additional network, but with some advanced options…
// advanced: {
// port: 8777, // Custom port
// network_id: 1342, // Custom network
// gas: 8500000, // Gas sent with each transaction (default: ~6700000)
// gasPrice: 20000000000, // 20 gwei (in wei) (default: 100 gwei)
// from: <address>, // Account to send transactions from (default: accounts[0])
// websocket: true // Enable EventEmitter interface for web3 (default: false)
// },
//
// Useful for deploying to a public network.
// Note: It's important to wrap the provider as a function to ensure truffle uses a new provider every time.
// goerli: {
// provider: () => new HDWalletProvider(MNEMONIC, `https://goerli.infura.io/v3/${PROJECT_ID}`),
// network_id: 5, // Goerli's id
// confirmations: 2, // # of confirmations to wait between deployments. (default: 0)
// timeoutBlocks: 200, // # of blocks before a deployment times out (minimum/default: 50)
// skipDryRun: true // Skip dry run before migrations? (default: false for public nets )
// },
//
// Useful for private networks
// private: {
// provider: () => new HDWalletProvider(MNEMONIC, `https://network.io`),
// network_id: 2111, // This network is yours, in the cloud.
// production: true // Treats this network as if it was a public net. (default: false)
// }
},
// Set default mocha options here, use special reporters, etc.
mocha: {
// timeout: 100000
},
// Configure your compilers
compilers: {
solc: {
version: "pragma", // Fetch exact version from solc-bin (default: truffle's version)
settings: {
optimizer: {
enabled: false,
runs: 200,
},
},
},
},
// Truffle DB is currently disabled by default; to enable it, change enabled:
// false to enabled: true. The default storage location can also be
// overridden by specifying the adapter settings, as shown in the commented code below.
//
// NOTE: It is not possible to migrate your contracts to truffle DB and you should
// make a backup of your artifacts to a safe location before enabling this feature.
//
// After you backed up your artifacts you can utilize db by running migrate as follows:
// $ truffle migrate --reset --compile-all
//
// db: {
// enabled: false,
// host: "127.0.0.1",
// adapter: {
// name: "indexeddb",
// settings: {
// directory: ".db"
// }
// }
// }
};
我尝试删除所有构造函数参数和断言语句,但似乎没有任何效果。任何形式的帮助表示赞赏。谢谢!