Skip to main content

Someone wanted to learn this too, so Grasp built them a personal learning path.

You can keep exploring this one or create your own

Hardhat Mainnet Deployment & Secure Private Keys

Hello! Welcome to the final module of our course. You've come a long way, from writing your first line of Solidity to deploying and upgrading complex contracts on a testnet. This is where all your hard work culminates.

In our last lesson, you built a Node.js script to listen for and react to smart contract events. This was a critical skill, bridging the gap between your on-chain contracts and the off-chain services that make a dApp truly functional. Your backend development experience was a great asset there.

Now, we begin the most exciting and critical phase: preparing for launch. Today's lesson marks the first step towards taking your project live on the Ethereum mainnet. The learning outcome is to configure Hardhat for mainnet deployment with secure private key management. This is arguably one of the most important security lessons in the entire course, as we are now preparing to handle real funds.

From Testnet to Mainnet: What Changes?

So far, all our deployments have been on testnets like Sepolia. Testnets are forgiving environments where the Ether is free and mistakes have no financial consequences. The mainnet is the opposite.

  • Real Value: The Ether (ETH) used for gas fees is real money.
  • Real Impact: The contracts you deploy are live, permanent, and will interact with users' real assets.
  • Real Risk: The private key you use to deploy holds real ETH and has control over your contracts. If compromised, the financial loss can be total and irreversible.

Therefore, our primary focus is not just on how to configure for mainnet, but how to do so securely.

Part 1: Mainnet Configuration Essentials

To deploy to any network, Hardhat needs two key pieces of information in your hardhat.config.js file:

  1. A Network RPC URL: This is the address of an Ethereum node that Hardhat will send your transaction to. For mainnet, you'll get this from a node provider like Alchemy or Infura, just as you did for the Sepolia testnet.
  2. An Account Private Key: This is the secret key for the wallet you will deploy from. This wallet must be funded with real ETH to pay for the mainnet gas fees.

Here is a conceptual look at how we'll add a mainnet configuration to your Hardhat project.

Hardhat Configuration File in VS Code
This screenshot shows a typical Hardhat project structure. We will be adding a `mainnet` network configuration within the `networks` object in the `hardhat.config.js` file.

A naive and highly insecure configuration might look like this. DO NOT DO THIS!

// AVOID THIS INSECURE EXAMPLE
module.exports = {
  solidity: "0.8.20",
  networks: {
    mainnet: {
      url: "https://eth-mainnet.g.alchemy.com/v2/YOUR_API_KEY", // Your real API key
      accounts: ['0x123...your...private...key...abc'] // Your real private key
    }
  }
};

Hardcoding your private key like this is extremely dangerous. If you accidentally commit this file to a public repository like GitHub, automated bots will find your key and drain your wallet within minutes. We must manage these secrets professionally.

Part 2: Secure Key Management Strategies

Let's explore two professional methods for managing your private key and other secrets, moving from a common practice to a more robust, encrypted solution. The goal is to protect your "Secret Value" (your private key) so it is only ever used at runtime and never stored in plain text in your codebase.

How Encryption Protects Your Secrets
This diagram illustrates the core principle of secure secret management. Your private key is encrypted and stored safely. It is only decrypted in memory, using a password, when it is needed for a transaction.

Method 1: Environment Variables with .env

A very common and effective practice in backend development is to store secrets in environment variables. We can achieve this in our Hardhat project using the dotenv package. This method keeps your secrets in a local file that is never committed to version control.

To learn about this method, let's turn to a practical guide.

Deploying ERC20 Token With Hardhat: A Step-by-Step Guide

The article "Deploying ERC20 Token With Hardhat" from Semaphore provides a clear, step-by-step guide on using a .env file to manage secrets for deployment. We'll follow this pattern.

Read the sections "Configuring our Project for Deployment", "Getting your MetaMask Private Key", and "Getting Sepolia Endpoint in Infura". Although the article targets the Sepolia testnet, the process for mainnet is identical—you would simply select the mainnet endpoint from Infura/Alchemy instead. Focus on how it uses the dotenv package, creates a .env file, and accesses the variables in hardhat.config.js.

Let's summarize the steps from the article for a mainnet deployment:

  1. Install dotenv:

    npm install dotenv
    
  2. Create a .gitignore entry: Ensure your .env file is never committed. Add this line to your .gitignore file:

    .env
    
  3. Create the .env file: In the root of your project, create a file named .env and add your secrets.

    # .env
    MAINNET_RPC_URL="https://mainnet.infura.io/v3/YOUR_INFURA_PROJECT_ID"
    MAINNET_PRIVATE_KEY="YOUR_METAMASK_PRIVATE_KEY"
    
  4. Update hardhat.config.js: Modify your configuration to load and use these variables.

    require("@nomicfoundation/hardhat-toolbox");
    require("dotenv").config(); // This line loads the .env file
    
    /** @type import('hardhat/config').HardhatUserConfig */
    module.exports = {
      solidity: "0.8.20",
      networks: {
        mainnet: {
          url: process.env.MAINNET_RPC_URL,
          accounts: [process.env.MAINNET_PRIVATE_KEY],
        },
        // ... your other networks like sepolia
      },
    };
    

This is a solid approach that correctly separates configuration and secrets from your code. However, the private key is still stored in plain text on your local machine. For an even higher level of security, we can encrypt it.

Method 2: Encrypted Secrets with Hardhat Keystore

Hardhat provides a built-in, more secure way to handle secrets using the hardhat-keystore plugin (which is included with hardhat-toolbox) and configuration variables. This method encrypts your secrets with a password and stores them in a secure location outside your project directory.

The official Hardhat documentation explains this best.

Managing config values and secrets safely

The Hardhat documentation page "Managing config values and secrets safely" introduces the modern, recommended way to handle secrets using encrypted storage.

Read the sections "Using Configuration Variables" and "Managing secrets with the hardhat-keystore plugin". Pay close attention to the configVariable function and the npx hardhat keystore commands. This is the most secure method we will cover.

Here’s how to apply this superior method:

  1. Store your secrets in the keystore: Run the following commands in your terminal. Hardhat will prompt you to enter the secret value. The first time you do this, it will also ask you to create a password for your keystore. Choose a strong, unique password and save it in a password manager.

    # Store your RPC URL
    npx hardhat keystore set MAINNET_RPC_URL
    
    # Store your private key
    npx hardhat keystore set MAINNET_PRIVATE_KEY
    
  2. Update hardhat.config.js: Now, modify your configuration to use Hardhat's configVariable function. This is cleaner as it doesn't require the dotenv package.

    require("@nomicfoundation/hardhat-toolbox");
    const { configVariable } = require("hardhat/config"); // Import the function
    
    /** @type import('hardhat/config').HardhatUserConfig */
    module.exports = {
      solidity: "0.8.20",
      networks: {
        mainnet: {
          url: configVariable("MAINNET_RPC_URL"),
          accounts: [configVariable("MAINNET_PRIVATE_KEY")],
        },
        // ... your other networks
      },
    };
    

Now, whenever you run a Hardhat task that needs to connect to the mainnet (like deploying), Hardhat will automatically prompt you to enter your keystore password. This unlocks the secrets in memory just long enough to use them, providing excellent security.

Test your understanding!

A colleague suggests using a .env file for secrets, which is then added to .gitignore. Another colleague argues for using hardhat-keystore instead. What is the primary security advantage of the keystore approach over the .env approach, even when the .env file is properly ignored by Git?

Show answer

The primary advantage is encryption at rest. The hardhat-keystore method encrypts the private key on your computer's hard drive. Even if an attacker gains access to your file system, they cannot read the private key without also knowing your keystore password. The .env file, while kept out of Git, stores the private key in plain text on your hard drive, making it vulnerable if your machine is compromised.

Preparing for Deployment

With your hardhat.config.js now securely configured, you are technically ready to deploy. The command would be:

# IMPORTANT: This is an example. Do NOT run it yet!
npx hardhat run scripts/deploy.js --network mainnet

Warning: Executing this command will initiate a real transaction on the Ethereum mainnet and will cost you real ETH. This lesson is about configuration only. We will not perform the actual deployment until we have covered cost estimation and final security checks in the upcoming lessons.

Conclusion

You have now reached a major milestone in your journey as a blockchain developer. Configuring your project for a mainnet launch with a focus on security is a sign of professionalism and a crucial step toward creating a real, valuable application.

Key Takeaways:

  • Mainnet deployment carries real financial risks, making security paramount.
  • Never hardcode private keys or other secrets in your source code.
  • Using a .env file is a good security practice that separates secrets from code.
  • Using hardhat-keystore provides a higher level of security by encrypting your secrets at rest, protecting them even if your local machine is compromised.
  • The final mainnet deployment command is npx hardhat run <script> --network mainnet, but should only be run after careful preparation.

Preview of the Next Lesson:

Now that we know how to configure for mainnet, the next vital question is: how much will it cost? In the next lesson, we will dive into estimating mainnet deployment and transaction costs using gas estimation tools. This will ensure you can fund your deployer wallet appropriately and avoid any expensive surprises when you go live.

Can't find a good explanation? Sign up and we'll make it for you

Sign up