Done auto select price feed based on network & verify on testnet

This commit is contained in:
hoelee 2024-08-05 21:30:14 +08:00
parent 39c2dd760b
commit ee8912613c
12 changed files with 331 additions and 35 deletions

View File

@ -17,6 +17,9 @@ This project mainly to keep as a reference for future Web 3.0 Developments.
0xc2022b56eBC140B5FebCf9FBaB14c17db4C315C4
https://sepolia.etherscan.io/address/0xc2022b56eBC140B5FebCf9FBaB14c17db4C315C4#code
Via deploy.js
https://sepolia.etherscan.io/address/0x3a827C119e1D746bb3C7bcbbf95c55246C8CcBdd#code
Via yarn hardhat deploy --network sepolia
### Public Reported Hacked Code References:

View File

@ -6,6 +6,7 @@ const {
DECIMALS,
INITIAL_ANSWER,
} = require("../helper-hardhat-config");
const { verify } = require("../utils/verify");
module.exports = async ({ getNamedAccounts, deployments }) => {
const { deploy, log } = deployments;

View File

@ -8,6 +8,7 @@ const {
const helperConfig = require("../helper-hardhat-config");
const networkConfig = helperConfig.networkConfig;
*/
const { verify } = require("../utils/verify");
/*
// main function that auto run via hardhat deploy
@ -19,6 +20,14 @@ function deployFunc() {
module.exports.default = deployFunc;
*/
/*
module.exports = async () => {
let contractAddress = "0x3a827C119e1D746bb3C7bcbbf95c55246C8CcBdd";
let priceFeedAddress = "0x694AA1769357215DE4FAC081bf1f309aDC325306";
await verify(contractAddress, [priceFeedAddress]);
};
*/
module.exports = async ({ getNamedAccounts, deployments }) => {
const { deploy, log } = deployments;
const { deployer } = await getNamedAccounts(); // Take from hardhat.config.js namedAccounts
@ -46,20 +55,23 @@ module.exports = async ({ getNamedAccounts, deployments }) => {
log("----------------------------------------------------");
log("Deploying FundMe Contract and waiting for confirmations...");
const argsPriceFeed = [ethUsdPriceFeedAddress];
const fundMe = await deploy("FundMe", {
from: deployer,
args: [ethUsdPriceFeedAddress],
args: argsPriceFeed,
log: true,
// we need to wait if on a live network so we can verify properly
waitConfirmations: network.config.blockConfirmations || 1,
});
log(`FundMe deployed at ${fundMe.address}`);
// If on testnet, then verify smart contract
if (
!developmentChains.includes(network.name) &&
process.env.ETHERSCAN_API_KEY
) {
await verify(fundMe.address, [ethUsdPriceFeedAddress]);
// Verify Code
await verify(fundMe.address, argsPriceFeed);
}
};

View File

@ -1,5 +1,5 @@
{
"address": "0xa513E6E4b8f2a923D98304ec87F64353C4D5C853",
"address": "0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512",
"abi": [
{
"inputs": [
@ -117,24 +117,24 @@
"type": "receive"
}
],
"transactionHash": "0xeabedc2721e71ca9ce643a60e7a60b0534335a0bff995dd4360e134d0ed41e1b",
"transactionHash": "0xee5f527d8a195ad5909a2ec516483cb1f62c73dc93c8cc7edf1316380411394c",
"receipt": {
"to": null,
"from": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266",
"contractAddress": "0xa513E6E4b8f2a923D98304ec87F64353C4D5C853",
"contractAddress": "0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512",
"transactionIndex": 0,
"gasUsed": "855613",
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"blockHash": "0x0c9cb3c9850d403be7b5d7342e9eac86f2ff991f7197d0214eaa0e1a877b9d2f",
"transactionHash": "0xeabedc2721e71ca9ce643a60e7a60b0534335a0bff995dd4360e134d0ed41e1b",
"blockHash": "0xb4e85c54d81b89dd58a2c11e02935d04b8b3d7d536a33d346ae0d0d207c1f95d",
"transactionHash": "0xee5f527d8a195ad5909a2ec516483cb1f62c73dc93c8cc7edf1316380411394c",
"logs": [],
"blockNumber": 8,
"blockNumber": 2,
"cumulativeGasUsed": "855613",
"status": 1,
"byzantium": true
},
"args": [
"0x0165878A594ca255338adfa4d48449f69242Eb8F"
"0x5FbDB2315678afecb367f032d93F642f64180aa3"
],
"numDeployments": 1,
"solcInputHash": "2addf7ea1ebec6fe07221842a0f5af20",

View File

@ -1,5 +1,5 @@
{
"address": "0x0165878A594ca255338adfa4d48449f69242Eb8F",
"address": "0x5FbDB2315678afecb367f032d93F642f64180aa3",
"abi": [
{
"inputs": [
@ -297,25 +297,25 @@
"type": "function"
}
],
"transactionHash": "0x4e36cc692c0b599533e0ff9149034e753f47f7d986873acb110df44c3dbf67e6",
"transactionHash": "0x7e29e8ee3c67b18648f3fe15adcc0ea408e835cac0fee09a37c8a5c296b9ec68",
"receipt": {
"to": null,
"from": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266",
"contractAddress": "0x0165878A594ca255338adfa4d48449f69242Eb8F",
"contractAddress": "0x5FbDB2315678afecb367f032d93F642f64180aa3",
"transactionIndex": 0,
"gasUsed": "569759",
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"blockHash": "0x14f9c2a5f82fda6c4401839b55d9a1aa8854b8e0d866e6ce8119befa946aee83",
"transactionHash": "0x4e36cc692c0b599533e0ff9149034e753f47f7d986873acb110df44c3dbf67e6",
"blockHash": "0xbc933db6a5f8e02afe32f6771521a3365ffc45e6361fc7ec758ababbdb9725ca",
"transactionHash": "0x7e29e8ee3c67b18648f3fe15adcc0ea408e835cac0fee09a37c8a5c296b9ec68",
"logs": [],
"blockNumber": 7,
"blockNumber": 1,
"cumulativeGasUsed": "569759",
"status": 1,
"byzantium": true
},
"args": [
"8",
"200000000000"
8,
200000000000
],
"numDeployments": 1,
"solcInputHash": "5eac77ba83b4d63fff115dd8269de44a",

View File

@ -0,0 +1 @@
11155111

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,41 @@
{
"language": "Solidity",
"sources": {
"@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol": {
"content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface AggregatorV3Interface {\n function decimals() external view returns (uint8);\n\n function description() external view returns (string memory);\n\n function version() external view returns (uint256);\n\n // getRoundData and latestRoundData should both raise \"No data present\"\n // if they do not have data to report, instead of returning unset values\n // which could be misinterpreted as actual reported values.\n function getRoundData(uint80 _roundId)\n external\n view\n returns (\n uint80 roundId,\n int256 answer,\n uint256 startedAt,\n uint256 updatedAt,\n uint80 answeredInRound\n );\n\n function latestRoundData()\n external\n view\n returns (\n uint80 roundId,\n int256 answer,\n uint256 startedAt,\n uint256 updatedAt,\n uint80 answeredInRound\n );\n}\n"
},
"contracts/FundMe.sol": {
"content": "// SPDX-License-Identifier: MIT\n/*\n Get funds from users\n Withdraw funds\n Set a minimum funding value in USD\n*/\npragma solidity ^0.8.0;\n\nimport \"./PriceConverter.sol\";\n// Save Gas by using constant, immutable for variables\n\nerror NotOwner();\n\n// 816914\ncontract FundMe {\n /*\n Transaction Fields\n Nonce\n Gas Price\n Gas Limit - Max Gas that this TX can use\n To - Address\n Value - Amount of wei to send\n Data - What to send to the Address (Can Empty)\n v, r, s - components of TX signature\n */\n\n using PriceConverter for uint256;\n uint256 public constant MINIMUM_USD = 50 * 1e18;\n\n address[] public funders;\n mapping(address => uint256) public addressToAmountFunded;\n\n // immutable only can declare 1 time at contructor\n address public immutable i_owner;\n\n AggregatorV3Interface public priceFeed;\n\n constructor(address priceFeedAddress) {\n i_owner = msg.sender;\n priceFeed = AggregatorV3Interface(priceFeedAddress);\n }\n\n function fund() public payable {\n /*\n Want to be able to set a minimum fund amount in USD\n 1. How do we send ETH to this contract\n */\n //require (msg.value >= 1e18, \"Didn't send enough\"); // 1e18 = 1 * 10 * 18 = 1000000000000000000\n require(\n msg.value.getConversionRate(priceFeed) >= MINIMUM_USD,\n \"Didn't send enough\"\n );\n funders.push(msg.sender);\n addressToAmountFunded[msg.sender] = msg.value;\n }\n\n // Owner of this contract only can withdraw\n function withdraw() public onlyOwner {\n // Can use modifier to replace\n // require(msg.sender == owner, \"Sender is not owner\");\n\n /* starting index, eding index, step amount */\n for (\n uint256 funderIndex = 0;\n funderIndex < funders.length;\n funderIndex++\n ) {\n address funder = funders[funderIndex];\n addressToAmountFunded[funder] = 0;\n }\n // reset the array\n funders = new address[](0);\n\n /*\n // transfer - over 2300 gas will cause exception\n payable(msg.sender).transfer(address(this).balance);\n // send - over 2300 gas will not cause exception, only will return true or false\n bool sendSuccess = payable(msg.sender).send(address(this).balance);\n require(sendSuccess, \"Send failed\");\n */\n\n // call - lower level\n (bool callSuccess, ) = payable(msg.sender).call{\n value: address(this).balance\n }(\"\");\n require(callSuccess, \"Call failed\");\n }\n\n modifier onlyOwner() {\n // require(msg.sender == i_owner, \"Sender is not owner!\");\n if (msg.sender != i_owner) {\n revert NotOwner();\n }\n\n _; // doing the rest of the code\n }\n\n // What happens if someone send this contract ETH without calling the fund fucntion\n /*\n receive - people send money / empty money into this contract\n fallback - cannot identify which function to run\n */\n receive() external payable {\n fund();\n }\n\n fallback() external payable {\n fund();\n }\n}\n"
},
"contracts/PriceConverter.sol": {
"content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.8;\n\n//import \"@chainlink/contracts/src/v0.8/shared/interfaces/AggregatorV3Interface.sol\";\nimport \"@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol\";\n\n// Why is this a library and not abstract?\n// Why not an interface?\nlibrary PriceConverter {\n // We could make this public, but then we'd have to deploy it\n function getPrice(\n AggregatorV3Interface priceFeed\n ) internal view returns (uint256) {\n (, int256 answer, , , ) = priceFeed.latestRoundData();\n // ETH/USD rate in 18 digit\n return uint256(answer * 10000000000);\n // or (Both will do the same thing)\n // return uint256(answer * 1e10); // 1* 10 ** 10 == 10000000000\n }\n\n // 1000000000\n function getConversionRate(\n uint256 ethAmount,\n AggregatorV3Interface priceFeed\n ) internal view returns (uint256) {\n uint256 ethPrice = getPrice(priceFeed);\n uint256 ethAmountInUsd = (ethPrice * ethAmount) / 1000000000000000000;\n // or (Both will do the same thing)\n // uint256 ethAmountInUsd = (ethPrice * ethAmount) / 1e18; // 1 * 10 ** 18 == 1000000000000000000\n // the actual ETH/USD conversion rate, after adjusting the extra 0s.\n return ethAmountInUsd;\n }\n}\n"
}
},
"settings": {
"optimizer": {
"enabled": false,
"runs": 200
},
"outputSelection": {
"*": {
"*": [
"abi",
"evm.bytecode",
"evm.deployedBytecode",
"evm.methodIdentifiers",
"metadata",
"devdoc",
"userdoc",
"storageLayout",
"evm.gasEstimates"
],
"": [
"ast"
]
}
},
"metadata": {
"useLiteralContent": true
}
}
}

View File

@ -1,13 +1,15 @@
require("@nomicfoundation/hardhat-toolbox");
//// Deprecated, below import combined in toolbox import already
require("@nomiclabs/hardhat-ethers");
//require("@nomiclabs/hardhat-etherscan");
//require("@nomicfoundation/hardhat-verify");
require("dotenv").config();
require("./tasks/block-number");
require("@nomiclabs/hardhat-waffle");
require("hardhat-gas-reporter");
require("@nomiclabs/hardhat-etherscan");
require("dotenv").config();
require("solidity-coverage");
require("hardhat-deploy");
require("./tasks/block-number");
//require("@nomicfoundation/hardhat-toolbox");
//// Deprecated, below import combined in toolbox import already
//require("@nomiclabs/hardhat-ethers");
//require("@nomicfoundation/hardhat-verify");
const RPC_URL_SEPOLIA = process.env.SEPOLIA_RPC_URL || "https://eth-sepolia";
const PRIVATE_KEY_SEPOLIA = process.env.SEPOLIA_PRIVATE_KEY || "0xkey";
@ -29,17 +31,20 @@ module.exports = {
url: RPC_URL_SEPOLIA,
accounts: [PRIVATE_KEY_SEPOLIA],
chainId: 11155111,
blockConfirmation: 6,
},
ganache: {
url: RPC_URL_GANACHE,
accounts: [PRIVATE_KEY_GANACHE],
chainId: 5777,
blockConfirmation: 1,
},
localhost: {
// Hardhost similar like ganache, can debug the transaction
// Start in terminal - yarn hardhat node
url: "http://127.0.0.1:8545/",
chainId: 31337,
blockConfirmation: 1,
},
},
solidity: {
@ -54,6 +59,7 @@ module.exports = {
},
etherscan: {
apiKey: ETHERSCAN_API_KEY,
customChains: [],
},
sourcify: {
enabled: false,
@ -81,11 +87,3 @@ module.exports = {
},
},
};
task("accounts", "Prints the list of accounts", async (taskArgs, hre) => {
const accounts = await hre.ethers.getSigners();
for (const account of accounts) {
console.log(account.address);
}
});

View File

@ -5,7 +5,7 @@ const { exit } = require("process");
// async main
async function main() {
if (false) {
await runVerifyOnly("0xF90F7edF515001CD4293255bfaF7D17ffa415e91");
await runVerifyOnly("0x3a827C119e1D746bb3C7bcbbf95c55246C8CcBdd");
}
const simpleStorageFactory =
@ -78,7 +78,8 @@ async function runVerifyOnly(address) {
console.log("Currently on Sepolia Testnet");
if (ETHERSCAN_API_KEY_VERIFY !== undefined) {
console.log("Verifying contract...");
await verify(address, []);
let tmp = await verify(address, []);
console.log(tmp);
console.log("Verification complete");
} else {
console.log("ETHERSCAN_API_KEY is undefined");

View File

@ -1,4 +1,4 @@
const { tast, task } = require("hardhat/config");
const { task } = require("hardhat/config");
task("block-number", "Prints current block number").setAction(
async (taskArgs, hre) => {
@ -6,3 +6,11 @@ task("block-number", "Prints current block number").setAction(
console.log("Current block number: " + blockNumber);
},
);
task("accounts", "Prints the list of accounts", async (taskArgs, hre) => {
const accounts = await hre.ethers.getSigners();
for (const account of accounts) {
console.log(account.address);
}
});

19
utils/verify.js Normal file
View File

@ -0,0 +1,19 @@
const { run } = require("hardhat")
const verify = async (contractAddress, args) => {
console.log("Verifying contract...")
try {
await run("verify:verify", {
address: contractAddress,
constructorArguments: args,
})
} catch (e) {
if (e.message.toLowerCase().includes("already verified")) {
console.log("Already verified!")
} else {
console.log(e)
}
}
}
module.exports = { verify }