Deploying to a testnet
Testnets are blockchains run by the community that can be used to test your smart contracts in a production-like environment. As of releasing this book, the main testnet is called "Goerli" and we'll be using it to deploy to.
Here we'll cover
- The tools and services required
- Getting some testnet ethereum to pay for the depoyment and subsequent test transactions
- Configuring hardhat
- Deploying and verifying the contract
Tools and services required
We need to configure a few tools and services in order to deploy our smart contract. Prepare a new .env file in your project because we'll need to populate it in a minute.
PRIVATE_KEY=
GOERLI_URL=
ETHERSCAN_API_KEY=
REPORT_GAS=false
COIN_MARKETCAP_API_KEY=
Alchemy
We'll use the excellent Alchemy service to handle the nitty gritty of deploying smart contracts to a blockchain. Head over to Alchemy and create a free account. Create a new app on the Ethereum chain and select "Goerli" as the network.
You'll need the HTTPS key when configuring hardhat.
Save your HTTPS key to your .env file as GOERLI_URL.
Etherscan
Etherscan is a block explorer and will become more and more useful as your blockchain development skills progress. It allows you to view deployed smart contracts and explore their code, as well as view any transactions that they've made.
For our project, we want to use it so that we can verify our smart contract code so that it can be easily viewed on Etherscan. It's a common practice to do so, and while not a requirement, I would recommend doing so.
Head over to Etherscan and create a free account. Grab the API key and save it to your .env file as ETHERSCAN_API_KEY.
Metamask
You'll need a wallet to pay for transactions and interact with the blockchain. Metamask is one of the most popular browser-based wallets. If you haven't already, go ahead and install Metamask and create a new wallet.
Get used to the idea of having multiple wallets and keeping separate wallets for testing and deploying smart contracts.
Once you've decided which wallet you're going to use for your deployments, export the private key and add it to your .env file as PRIVATE_KEY. You can export it from MetaMask by clicking on Account Details -> Export Private Key.
Getting some testnet ethereum
Every transaction on the blockchain costs gas, which is paid for in ether. Testnets also require gas - but luckily there is no monetary worth of the "test ether" used to pay for these transactions.
To acquire test ether you'll need to find a "faucet", which is a website that will automatically transfer these test funds to your wallet address.
At the time of writing, we're using the "Goerli" network: Alchemy run a test faucet at https://goerlifaucet.com/ which is where you can input your wallet address to receive some funds.
Configuring Hardhat
Up until this point we've only run our contracts locally during our automated tests. Let's update our hardhat configuration, we want to include some details about the chains that we will be deploying to as well as configure the community hardhat-deploy plugin so that we can start deploying our smart contract.
We'll be adding the following 3 chains:
hardhat, a local chain that is easiest to deploy to and is useful during developmentgoerli, which is the testnet that we're goingmainnet, for when we launch to production!
Update your hardhat.config.js file to the following:
require("@nomicfoundation/hardhat-toolbox");
require('hardhat-deploy');
require("dotenv").config();
/** @type import('hardhat/config').HardhatUserConfig */
module.exports = {
solidity: "0.8.9",
namedAccounts: {
deployer: {
default: 0
},
},
defaultNetwork: "hardhat",
networks: {
hardhat: {
chainId: 31337,
blockConfirmations: 1
},
goerli: {
chainId: 5,
blockConfirmations: 6,
url: process.env.GOERLI_URL || "",
accounts:
process.env.PRIVATE_KEY !== undefined ? [process.env.PRIVATE_KEY] : [],
},
mainnet: {
chainId: 1,
blockConfirmations: 6,
url: process.env.MAINNET_URL || "",
accounts:
process.env.PRIVATE_KEY !== undefined ? [process.env.PRIVATE_KEY] : [],
},
},
etherscan: {
apiKey: process.env.ETHERSCAN_API_KEY,
},
};
You might have noticed some new dependencies up top. Let's make sure that we install them:
npm install hardhat-deploy dotenv
Let's create our deploy script. Create a new root level folder called deploy and create a new script: 01-deploy-nft-collection.js. Even though our deploy is relatively simple, the practices we'll learn here will be a useful template for deploying larger projects (with multiple contracts) in the future.
The below is a good starting template. Be sure to review it and update the args constant to be the values that you actually want to pass to the deployed contracts constructor.
const { network } = require('hardhat')
const { developmentChains } = require("../helper-hardhat-config")
const { verify } = require("../utils/verify")
module.exports = async function({ getNamedAccounts, deployments }) {
const { deploy, log } = deployments
const { deployer } = await getNamedAccounts()
// Dont' forget to update these arguments to your real-life values
const args = [
300,
10,
"ipfs://PRE_REVEAL",
]
const contract = await deploy("NFTCollection", {
from: deployer,
args,
logs: true,
waitConfirmations: network.config.blockConfirmations || 1,
})
log(`NFTCollection deployed at ${contract.address}`)
// Automatically verify the contract on Etherscan
if (
!developmentChains.includes(network.name) &&
process.env.ETHERSCAN_API_KEY
) {
await verify(contract.address, args)
}
}
module.exports.tags = ["all"]
You'll notice a couple of imports at the top of the file that reference files that we haven't created yet. Let's quickly get those going.
helper-hardhat-config.js
// The list of chain names that are used for development
const developmentChains = ["hardhat", "localhost"]
module.exports = {
developmentChains,
}
utils/verify.js
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 }
Great! We're ready to deploy our smart contract. Let's do so by using the deploy command.
npx hardhat deploy --network goerli
Running the above command will compile the contracts, deploy them to the Goerli network and verify the code on Etherscan. If any issues occur during this process, sometimes running a quick npx hardhat clean to remove all compiled contracts before re-running the above deploy command does the trick.