How to manage config values and secrets safely in Hardhat 3

One of the many interesting features in the newly launched Hardhat 3 is the encrypted secrets keystore, which this guide will focus on.
If you haven't looked it into it yet, Hardhat 3 is a complete overhaul of the product. We introduced Solidity tests, rewrote the performance critical components in Rust, added multichain support across the board, implemented OP Stack simulation, revamped the build system, modernized our TS CLI and plugin system, and a lot more.
Let's dive into config variables and secrets. Hardhat projects often need values that shouldn’t be committed to shared repositories, like private keys or API keys.
Hardhat 3 offers Config Variables to handle this in a secure way, either encrypted (using hardhat-keystore
) or not, depending on what you need.
By default, config variables are read from environment variables, but plugins can also define alternative ways to obtain their values.
This guide covers how to use Config Variables and the hardhat-keystore
plugin to securely store sensitive values.
Using config variables
A common example of a value that you don’t want to hardcode is an RPC URL that includes an API key, such as one from Alchemy or Infura:
const config = {
networks: {
sepolia: {
url: "https://eth-sepolia.g.alchemy.com/v2/ABC123...",
// ...
},
},
};
Instead of this, you can call the configVariable
function to get the value at runtime:
import { configVariable } from "hardhat/config";
const config = {
networks: {
sepolia: {
url: configVariable("SEPOLIA_RPC_URL"),
// ...
},
},
};
In the snippet above, Hardhat will look for an environment variable named SEPOLIA_RPC_URL
when it needs the value. You can define it inline when running a task:
SEPOLIA_RPC_URL='<https://eth-sepolia.g.alchemy.com/v2/ABC123>...' npx hardhat run ./my-script.ts --network sepolia
Config Variables are lazy, meaning that they’re only resolved when actually needed. This allows you to use Hardhat without having to define all of them upfront if they’re not going to be used for what you are doing.
Managing secrets with the hardhat-keystore
plugin
Resolving Config Variables from environment variables isn't safe (due to command history storage, among other things) nor convenient. The hardhat-keystore
plugin lets you store sensitive values in an encrypted file and use them as config variables, without having to type them every time or commit them to disk in plain text, where they can be stolen.
Setup
If you are using a Hardhat Toolbox or created a sample project using Hardhat 3, you already have the plugin installed.
Otherwise, check out the hardhat-keystore
documentation to install it.
Using the keystore
Use the keystore set
task to store an encrypted secret:
npx hardhat keystore set SEPOLIA_RPC_URL
When you run this task for the first time, you’ll be prompted to create a password for your keystore. After that you’ll need to enter that password to confirm the operation every time you add a new value. The secret is then stored encrypted in a file in your home directory.
Once a value is stored in the keystore, you can use it in your configuration:
import { configVariable } from "hardhat/config";
const config = {
networks: {
sepolia: {
url: configVariable("SEPOLIA_RPC_URL"),
// ...
},
},
};
Hardhat will prompt you to enter the password when the configuration variable is needed.
Managing encrypted variables
To manage your keystore, the plugin provides the following self-explanatory tasks:
npx hardhat keystore list
npx hardhat keystore get <key>
npx hardhat keystore delete <key>
npx hardhat keystore change-password
npx hardhat keystore path
Improving UX when using keystore values during the dev process
To avoid repeatedly writing the keystore password when working locally with values that aren't sensitive, you can use the Development Keystore, a separate keystore which doesn't ask for the password when the values are accessed. This allows you to keep the keystore setup when security isn't relevant during the development process. To use it just store the values adding the --dev
flag:
npx hardhat keystore set --dev <key>
Learn more about Hardhat 3 and get started by checking out the docs!
Follow @HardhatHQ on Twitter and subscribe to the Nomic Foundation blog.