From 4823e48b01a3572ae8aafa00044a522b9bdd8578 Mon Sep 17 00:00:00 2001 From: WiseMrMusa Date: Fri, 24 Mar 2023 19:17:45 +0100 Subject: [PATCH] A script is added to verify the contract on deployment --- .env.example | 3 + .gitignore | 2 +- README.md | 9 +++ example.hardhat.config.js | 21 +++++++ package.json | 7 ++- scripts/deployAndVerify.js | 115 +++++++++++++++++++++++++++++++++++++ 6 files changed, 154 insertions(+), 3 deletions(-) create mode 100644 .env.example create mode 100644 example.hardhat.config.js create mode 100644 scripts/deployAndVerify.js diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..67c20a9 --- /dev/null +++ b/.env.example @@ -0,0 +1,3 @@ +GOERLI_RPC= +PRIVATE_KEY1= +ETHERSCAN= \ No newline at end of file diff --git a/.gitignore b/.gitignore index bf3c0b3..e8cba8b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ out artifacts node_modules -cache +cache \ No newline at end of file diff --git a/README.md b/README.md index ae5a42e..2e3893c 100644 --- a/README.md +++ b/README.md @@ -37,3 +37,12 @@ $ forge t Bonus: The [DiamondLoupefacet](contracts/facets/DiamondLoupeFacet.sol) uses an updated [LibDiamond](contracts/libraries//LibDiamond.sol) which utilises solidity custom errors to make debugging easier especially when upgrading diamonds. Take it for a spin!! Need some more clarity? message me [on twitter](https://twitter.com/Timidan_x), Or join the [EIP-2535 Diamonds Discord server](https://discord.gg/kQewPw2) + + +### Verify + +This addition to the template allows you to verify your contracts on deployment. +An example `.env: .env.example` and hardhat config `example.hardhat.config.js` has been provided +To verify, you need to include your etherscan API included. + +Enjoy \ No newline at end of file diff --git a/example.hardhat.config.js b/example.hardhat.config.js new file mode 100644 index 0000000..6b79438 --- /dev/null +++ b/example.hardhat.config.js @@ -0,0 +1,21 @@ +const dotenv = require('dotenv'); +dotenv.config(); +/** + * @type import('hardhat/config').HardhatUserConfig + */ + +require('@nomiclabs/hardhat-ethers'); +module.exports = { + solidity: '0.8.18', + defaultNetwork: "goerli", + networks: { + goerli: { + url: process.env.GOERLI_RPC, + accounts: [process.env.PRIVATE_KEY1] + } + }, + etherscan: { + apiKey: process.env.ETHERSCAN, + customChains: [], + } +} \ No newline at end of file diff --git a/package.json b/package.json index 8712008..281769c 100644 --- a/package.json +++ b/package.json @@ -2,6 +2,9 @@ "name": "hardhat-project", "dependencies": { "@nomiclabs/hardhat-ethers": "^2.0.6", - "ethers": "^5.6.9" + "@nomiclabs/hardhat-etherscan": "^3.1.7", + "dotenv": "^16.0.3", + "ethers": "^5.6.9", + "hardhat": "^2.13.0" } -} +} \ No newline at end of file diff --git a/scripts/deployAndVerify.js b/scripts/deployAndVerify.js new file mode 100644 index 0000000..ecb0a65 --- /dev/null +++ b/scripts/deployAndVerify.js @@ -0,0 +1,115 @@ +const { getSelectors, FacetCutAction } = require('./libraries/diamond.js'); +const { ethers, network, run } = require('hardhat'); +require("@nomiclabs/hardhat-etherscan"); + + +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); + } + } +}; + +async function deployAndVerifyDiamond() { + const accounts = await ethers.getSigners(); + const contractOwner = accounts[0]; + const chainId = network.config.chainId; + + // deploy DiamondCutFacet + const DiamondCutFacet = await ethers.getContractFactory('DiamondCutFacet') + const diamondCutFacet = await DiamondCutFacet.deploy() + await diamondCutFacet.deployed() + console.log('DiamondCutFacet deployed:', diamondCutFacet.address); + + // verify DiamondCutFacet + console.log(`Waiting for blocks confirmations...`); + await diamondCutFacet.deployTransaction.wait(6); + await verify(diamondCutFacet.address, []); + console.log(`Confirmed!`); + + + + // deploy Diamond + const Diamond = await ethers.getContractFactory('Diamond') + const diamond = await Diamond.deploy( + contractOwner.address, + diamondCutFacet.address, + ) + await diamond.deployed() + console.log('Diamond deployed:', diamond.address) + + // verify Diamond + console.log(`Waiting for blocks confirmations...`); + await diamond.deployTransaction.wait(6); + await verify(diamond.address, [contractOwner.address, + diamondCutFacet.address,]); + console.log(`Confirmed!`); + + // deploy DiamondInit + // DiamondInit provides a function that is called when the diamond is upgraded to initialize state variables + // Read about how the diamondCut function works here: https://eips.ethereum.org/EIPS/eip-2535#addingreplacingremoving-functions + const DiamondInit = await ethers.getContractFactory('DiamondInit'); + const diamondInit = await DiamondInit.deploy() + await diamondInit.deployed() + console.log('DiamondInit deployed:', diamondInit.address) + + // deploy facets + console.log('') + console.log('Deploying facets') + const FacetNames = ['DiamondLoupeFacet', 'OwnershipFacet'] + const cut = [] + for (const FacetName of FacetNames) { + const Facet = await ethers.getContractFactory(FacetName) + const facet = await Facet.deploy() + await facet.deployed() + console.log(`${FacetName} deployed: ${facet.address}`) + + // verify facet + console.log(`Waiting for blocks confirmations...`); + await facet.deployTransaction.wait(6); + await verify(facet.address, []); + console.log(`Confirmed!`); + cut.push({ + facetAddress: facet.address, + action: FacetCutAction.Add, + functionSelectors: getSelectors(facet), + }) + } + + // upgrade diamond with facets + console.log('') + console.log('Diamond Cut:', cut) + const diamondCut = await ethers.getContractAt('IDiamondCut', diamond.address) + let tx + let receipt + // call to init function + let functionCall = diamondInit.interface.encodeFunctionData('init') + tx = await diamondCut.diamondCut(cut, diamondInit.address, functionCall) + console.log('Diamond cut tx: ', tx.hash) + receipt = await tx.wait() + if (!receipt.status) { + throw Error(`Diamond upgrade failed: ${tx.hash}`) + } + console.log('Completed diamond cut') + return diamond.address +} + +if (require.main === module) { + deployAndVerifyDiamond() + .then(() => process.exit(0)) + .catch((error) => { + console.error(error) + process.exit(1) + }) +} + +exports.deployAndVerifyDiamond = deployAndVerifyDiamond \ No newline at end of file