diff --git a/basic/14-chainlink-price-feed/.env.example b/basic/14-chainlink-price-feed/.env.example index a3bb56fc8..5879a1c99 100644 --- a/basic/14-chainlink-price-feed/.env.example +++ b/basic/14-chainlink-price-feed/.env.example @@ -1,3 +1,5 @@ -PRIVATE_KEY=xxxxxxxxxxxxxxxx -INFURA_ID=yyyyyyyy -SubscriptionId=ddddd \ No newline at end of file +PRIVATE_KEY=xxxx +PUBLIC_ADDRESS=xxxx +MNEMONIC=xxxx +API_KEY=xxxx +SubscriptionId=xxxx diff --git a/basic/14-chainlink-price-feed/contracts/PriceConsumerV3.sol b/basic/14-chainlink-price-feed/contracts/PriceConsumerV3.sol index b933c29c4..12df094bf 100644 --- a/basic/14-chainlink-price-feed/contracts/PriceConsumerV3.sol +++ b/basic/14-chainlink-price-feed/contracts/PriceConsumerV3.sol @@ -1,48 +1,33 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.7; -import {AggregatorV3Interface} from '@chainlink/contracts/src/v0.8/shared/interfaces/AggregatorV3Interface.sol'; - -/** - * THIS IS AN EXAMPLE CONTRACT THAT USES HARDCODED - * VALUES FOR CLARITY. - * THIS IS AN EXAMPLE CONTRACT THAT USES UN-AUDITED CODE. - * DO NOT USE THIS CODE IN PRODUCTION. - */ - -/** - * If you are reading data feeds on L2 networks, you must - * check the latest answer from the L2 Sequencer Uptime - * Feed to ensure that the data is accurate in the event - * of an L2 sequencer outage. See the - * https://docs.chain.link/data-feeds/l2-sequencer-feeds - * page for details. - */ +import {AggregatorV3Interface} from "@chainlink/contracts/src/v0.8/shared/interfaces/AggregatorV3Interface.sol"; contract PriceConsumerV3 { - AggregatorV3Interface internal dataFeed; + + AggregatorV3Interface internal priceFeed; /** * Network: Sepolia - * Aggregator: BTC/USD - * Address: 0x1b44F3514812d835EB1BDB0acB33d3fA3351Ee43 + * Aggregator: ETH/USD + * Address: 0x694AA1769357215DE4FAC081bf1f309aDC325306 */ constructor(address _priceFeed) { - dataFeed = AggregatorV3Interface(_priceFeed); + priceFeed = AggregatorV3Interface(_priceFeed); } /** - * Returns the latest answer. + * Returns the latest price */ - function getChainlinkDataFeedLatestAnswer() public view returns (int) { - // prettier-ignore + function getLatestPrice() public view returns (int) { ( - /* uint80 roundID */, - int answer, - /*uint startedAt*/, - /*uint timeStamp*/, - /*uint80 answeredInRound*/ - ) = dataFeed.latestRoundData(); - return answer; + uint80 roundID, + int price, + uint startedAt, + uint timeStamp, + uint80 answeredInRound + ) = priceFeed.latestRoundData(); + return price; } + } diff --git a/basic/14-chainlink-price-feed/contracts/RandomNumberConsumer.sol b/basic/14-chainlink-price-feed/contracts/RandomNumberConsumer.sol index 68ed1eb9d..a64b189fd 100644 --- a/basic/14-chainlink-price-feed/contracts/RandomNumberConsumer.sol +++ b/basic/14-chainlink-price-feed/contracts/RandomNumberConsumer.sol @@ -1,6 +1,6 @@ // SPDX-License-Identifier: MIT // An example of a consumer contract that relies on a subscription for funding. -pragma solidity 0.8.19; +pragma solidity ^0.8.7; import {VRFConsumerBaseV2Plus} from '@chainlink/contracts/src/v0.8/vrf/dev/VRFConsumerBaseV2Plus.sol'; import {VRFV2PlusClient} from '@chainlink/contracts/src/v0.8/vrf/dev/libraries/VRFV2PlusClient.sol'; diff --git a/basic/14-chainlink-price-feed/hardhat.config.js b/basic/14-chainlink-price-feed/hardhat.config.js index 78920d146..3af5152cc 100644 --- a/basic/14-chainlink-price-feed/hardhat.config.js +++ b/basic/14-chainlink-price-feed/hardhat.config.js @@ -1,64 +1,32 @@ -require("@nomiclabs/hardhat-waffle") -require("@nomiclabs/hardhat-ethers") -require("@nomiclabs/hardhat-web3") -require("@nomiclabs/hardhat-truffle5") -require("@nomiclabs/hardhat-etherscan"); -require("hardhat-deploy") -const fs = require("fs"); -require('dotenv').config() - -// This is a sample Hardhat task. To learn how to create your own go to -// https://hardhat.org/guides/create-task.html -task('accounts', 'Prints the list of accounts', async () => { - const accounts = await ethers.getSigners(); - - for (const account of accounts) { - console.log(account.address); - } -}); - -function mnemonic () { - - return process.env.PRIVATE_KEY +require("@nomicfoundation/hardhat-ethers"); +require('dotenv').config({'path': './.env'}); +function mnemonic() { + return process.env.PRIVATE_KEY; } -// You need to export an object to set up your config -// Go to https://hardhat.org/config/ to learn more - -/** - * @type import('hardhat/config').HardhatUserConfig - */ - module.exports = { - defaultNetwork: "hardhat", + defaultNetwork: "sepolia", networks: { - localhost: { - url: 'http://localhost:8545', - //gasPrice: 125000000000, // you can adjust gasPrice locally to see how much it will cost on production - /* - notice no mnemonic here? it will just use account 0 of the hardhat node to deploy - (you can put in a mnemonic here to set the deployer locally) - */ + hardhat: { + // // If you want to do some forking, uncomment this + // forking: { + // url: mainnetRpcUrl + // } }, sepolia: { - url: 'https://sepolia.infura.io/v3/' + process.env.INFURA_ID, //<---- YOUR INFURA ID! (or it won't work) + url: 'https://eth-sepolia.g.alchemy.com/v2/' + process.env.API_KEY, //<---- YOUR alchemy ID! (or it won't work) accounts: [mnemonic()], + ignition: { + maxFeePerGasLimit: 10_000_000_000_000n, + maxPriorityFeePerGas: 20_000_000_000n, + } }, }, - namedAccounts: { - deployer: { - default: 0, // here this will by default take the first account as deployer - 1: 0 // similarly on mainnet it will take the first account as deployer. Note though that depending on how hardhat network are configured, the account 0 on one network can be different than on another - }, - feeCollector: { - default: 1 - } - }, solidity: { compilers: [ { - version: "0.8.19" + version: "0.8.7" } ] }, @@ -69,5 +37,3 @@ module.exports = { timeout: 6000000000000000 } } - - diff --git a/basic/14-chainlink-price-feed/package.json b/basic/14-chainlink-price-feed/package.json index 48fce1333..891fe11f7 100644 --- a/basic/14-chainlink-price-feed/package.json +++ b/basic/14-chainlink-price-feed/package.json @@ -10,20 +10,14 @@ "author": "", "license": "ISC", "devDependencies": { - "@nomiclabs/hardhat-ethers": "^2.0.2", - "@nomiclabs/hardhat-truffle5": "^2.0.0", - "@nomiclabs/hardhat-waffle": "^2.0.1", - "@nomiclabs/hardhat-web3": "^2.0.0", - "chai": "^4.3.4", - "ethereum-waffle": "^3.3.0", - "ethers": "^5.7.2", - "hardhat": "^2.22.6", - "hardhat-deploy": "^0.7.9", - "web3": "^1.3.6" + "@chainlink/contracts": "^1.3.0", + "@nomicfoundation/hardhat-ethers": "^3.0.8", + "ethers": "^6.13.4", + "hardhat": "^2.22.13", + "dotenv": "^10.0.0" }, "dependencies": { - "@chainlink/contracts": "^1.2.0", - "@nomiclabs/hardhat-etherscan": "^3.0.1", + "@chainlink/contracts": "^1.3.0", "dotenv": "^10.0.0", "moment": "^2.29.4" } diff --git a/basic/14-chainlink-price-feed/scripts/01-PriceConsumerV3Deploy.js b/basic/14-chainlink-price-feed/scripts/01-PriceConsumerV3Deploy.js index 310b4caaf..04c194cc1 100644 --- a/basic/14-chainlink-price-feed/scripts/01-PriceConsumerV3Deploy.js +++ b/basic/14-chainlink-price-feed/scripts/01-PriceConsumerV3Deploy.js @@ -1,33 +1,30 @@ const hre = require("hardhat"); -require("@nomiclabs/hardhat-web3"); +const ethers = hre.ethers; -async function main () { +async function main() { + const singer = await ethers.getSigner(process.env.PUBLIC_ADDRESS); + console.log("Deploying contracts with the account:", singer.address); - const [deployer] = await ethers.getSigners(); + // PRICE_FEED_CONTRACT 在 sepolia 上的合约地址 + const PRICE_FEED_CONTRACT = "0x694AA1769357215DE4FAC081bf1f309aDC325306"; - console.log( - "Deploying contracts with the account:", - deployer.address - ); + // deploy PriceConsumerV3 Contract + const PriceConsumerV3 = await ethers.getContractFactory("PriceConsumerV3"); + const PriceConsumerV3Ins = await PriceConsumerV3.deploy(PRICE_FEED_CONTRACT); + await PriceConsumerV3Ins.waitForDeployment(); - // PRICE_FEED_CONTRACT 在 sepolia 上的合约地址 - const PRICE_FEED_CONTRACT = "0x694AA1769357215DE4FAC081bf1f309aDC325306"; + console.log("----------------------------------------------------"); + console.log("PriceConsumerV3 address:", await PriceConsumerV3Ins.getAddress()); - // 部署 PriceConsumerV3 合约 - const priceConsumerV3 = await ethers.getContractFactory("PriceConsumerV3"); - const PriceConsumerV3Ins = await priceConsumerV3.deploy(PRICE_FEED_CONTRACT); - - console.log("----------------------------------------------------") - console.log("PriceConsumerV3 address:", PriceConsumerV3Ins.address); - - //await priceConsumerV3.deployed() - console.log("Read Price from PRICE_FEED") - - await PriceConsumerV3Ins.getChainlinkDataFeedLatestAnswer().then(function (data) { - console.log('data', data) - console.log('Current price of ETH / USD is: ', web3.utils.hexToNumber(data._hex)) - }) + // 读取价格 + console.log("Read Price from PRICE_FEED"); + const latestPrice = await PriceConsumerV3Ins.getLatestPrice(); + console.log('Current price of ETH / USD is: ', latestPrice.toString()); + // 使用 singer 获取合约 + const contract = await ethers.getContractAt("PriceConsumerV3", await PriceConsumerV3Ins.getAddress(), singer); + const latestPriceFromContract = await contract.getLatestPrice(); + console.log('Current price of ETH / USD from contract is: ', latestPriceFromContract.toString()); } // We recommend this pattern to be able to use async/await everywhere @@ -35,6 +32,6 @@ async function main () { main() .then(() => process.exit(0)) .catch(error => { - console.error(error); - process.exit(1); + console.error(error); + process.exit(1); }); diff --git a/basic/14-chainlink-price-feed/scripts/02-RandomNumberConsumerDeploy.js b/basic/14-chainlink-price-feed/scripts/02-RandomNumberConsumerDeploy.js index d5e438a0f..d48543d19 100644 --- a/basic/14-chainlink-price-feed/scripts/02-RandomNumberConsumerDeploy.js +++ b/basic/14-chainlink-price-feed/scripts/02-RandomNumberConsumerDeploy.js @@ -1,7 +1,6 @@ const hre = require('hardhat'); -require('@nomiclabs/hardhat-web3'); -require('dotenv').config(); const { saveDeployment } = require('./utils'); +const ethers = hre.ethers; async function main () { @@ -13,14 +12,15 @@ async function main () { const RandomNumberConsumer = await ethers.getContractFactory('RandomNumberConsumer'); console.log('process.env.SubscriptionId', process.env.SubscriptionId) const instance = await RandomNumberConsumer.deploy(process.env.SubscriptionId); - await instance.deployed(); + await instance.waitForDeployment(); + const RandomNumberConsumerAddress = await instance.getAddress(); console.log('----------------------------------------------------'); - console.log('RandomNumberConsumer address:', instance.address); + console.log('RandomNumberConsumer address:', RandomNumberConsumerAddress); // save contract address to file saveDeployment({ - RandomNumberConsumerAddress: instance.address, + RandomNumberConsumerAddress: RandomNumberConsumerAddress, }); } diff --git a/basic/14-chainlink-price-feed/scripts/03-RandomNumberConsumer.js b/basic/14-chainlink-price-feed/scripts/03-RandomNumberConsumer.js index c76cefad6..93bbb6b3e 100644 --- a/basic/14-chainlink-price-feed/scripts/03-RandomNumberConsumer.js +++ b/basic/14-chainlink-price-feed/scripts/03-RandomNumberConsumer.js @@ -1,11 +1,9 @@ const hre = require('hardhat'); -require('@nomiclabs/hardhat-web3'); -const { BigNumber } = require('ethers'); -require('dotenv').config(); +const ethers = hre.ethers; const { readDeployment } = require('./utils'); async function main () { - const provider = new ethers.providers.WebSocketProvider(`wss://sepolia.infura.io/ws/v3/${process.env.INFURA_ID}`); + const provider = new ethers.WebSocketProvider(`wss://eth-sepolia.g.alchemy.com/v2/${process.env.API_KEY}`); const { abi: RandomNumberConsumerABI } = require('../artifacts/contracts/RandomNumberConsumer.sol/RandomNumberConsumer.json'); const deployment = readDeployment(); console.log('deployment:', deployment); @@ -18,25 +16,25 @@ async function main () { let randomNumberConsumer, user1, iface; - randomNumberConsumer = new ethers.Contract(addr, RandomNumberConsumerABI, provider); - iface = new ethers.utils.Interface(RandomNumberConsumerABI); - [user1] = await ethers.getSigners(); + randomNumberConsumer = new ethers.BaseContract(addr, RandomNumberConsumerABI); + iface = randomNumberConsumer.interface; + user1 = await ethers.getSigner(process.env.PUBLIC_ADDRESS); let random0ID, random0Res; // 监听randomNumberConsumer 的请求随机数事件 const filterCall = { address: addr, - topics: [ethers.utils.id('RequestSent(address,uint256,uint32)')], + topics: [ethers.keccak256(ethers.toUtf8Bytes('RequestSent(address,uint256,uint32)'))], }; // 监听chainlink VRF Coordinator 的随机数回写事件 const filterRes = { address: addr, - topics: [ethers.utils.id('RequestFulfilled(uint256,uint256[])')], + topics: [ethers.keccak256(ethers.toUtf8Bytes('RequestFulfilled(uint256,uint256[])'))], }; console.log(`Listen on random number call...`); - provider.on(filterCall, (log, event) => { + await provider.on(filterCall, (log, event) => { console.log('event RequestSent(address,uint256)'); const { args } = iface.parseLog(log); if (args[0] === user1.address) { @@ -48,11 +46,11 @@ async function main () { }); console.log(`Listen on random number result...`); - provider.on(filterRes, (log, event) => { + await provider.on(filterRes, (log, event) => { console.log('event RequestFulfilled(uint256,uint256[])'); const { args } = iface.parseLog(log); console.log('random0ID', random0ID) - if (random0ID && BigNumber.from(args[0]).eq(random0ID)) { + if (random0ID && BigInt.from(args[0]).eq(random0ID)) { random0Res = args[1]; console.log('random0Res: ', random0Res.toString()); } else {