hardhat
hardhat copied to clipboard
Encrypt Key Management with hardhat, add alt to the practice of placing private keys into `.env` or environment variables
As other users have mentioned, like here and here, the current recommended way to store keys isn't ideal. Right now, the convention is to put the private key as an environment variable, which many new engineers have mistakenly pushed up to github.
Other frameworks like brownie and dapptools have built in encryption for the keys, where you input your key into the command line, and it stores the key in a [keystore] (https://kb.myetherwallet.com/en/security-and-privacy/what-is-a-keystore-file/) json file, which means you can only access it with a password. This solves two issues:
- It removes accidentally putting your key in plaintext into a file in your project
- It encrypts your key so even if you do accidentally do something with it... it's encrypted.
I think the API for this would look some like as follows:
npx hardhat accounts import
And you'd be prompted for your private key, account name, and a password.
Then, when you do npx hardhat accounts list it adds the name of the account.
Then, if your hardhat.config.js, you can just pick the name of your account, or maybe the address, or something.
Thanks for the suggestions you've done lately, @PatrickAlphaC! Both of them are things that we definitely need to implement.
Working across 4 different deployment frameworks gives me context of which ones have features I value :)
This issue was marked as stale because it didn't have any activity in the last 30 days. If you think it's still relevant, please leave a comment indicating so. Otherwise, it will be closed in 7 days.
This issue was closed because it has been stalled for 7 days with no activity.
I was just thinking of creating a plugin that does that and find this issue. I wonder if you guys are doing something for it or it I should go for the plugin I have in mind...
Today I risked to push a change in the .env file and I wrote a simple wrapper that manages encrypted private keys. It works, but there are some issues with the output of the commands from npx hardat execution. If someone wants to take a look: https://github.com/secrez/hardhood
Later, I wrote a more general solution, that manages env files https://github.com/secrez/cryptoenv
I've tried to give a own solution to this case but the deploy method failed.
I've generated the keystore JSON files basically following these steps:
// instance a HDWallet from a mneumonic
const wallet = ethers.utils.HDNode.fromMnemonic(mneumonic);
// instance a Wallet from the path the first address
const wallet = hdWallet.derivePath(`m/44'/60'/0'/0/0`);
// encrypt the wallet with some "somepassword"
const keyStore = await encryptKeystore(wallet, "somepassword");
// serialize the encrypted keystore to a file
fs.writeFileSync("keystore.json", keyStore);
The full code can be found in this repository: https://github.com/fabianorodrigo/wallet-json-keystore-generator
With the keystore files created, I would use them to deploy my contracts, but I receive an error Cannot read properties of null (reading 'sendTransaction') when my Factory.deploy() is called.
export async function getSigners(password: string): Promise<Wallet[]> {
//read ADMIN the keystore file
const admin = await openKeyStoreFile(
"./scripts/keyStores/keystore_0x97b6183621504b18Ccb97D0422c33a5D3601b862.json",
password
);
//read MANAGER the keystore file
const manager = await openKeyStoreFile(
"./scripts/keyStores/keystore_0x958a7E51e39725e866440a223566229841DCFC20.json",
password
);
//read TEAMMEMBER the keystore file
const teamMember = await openKeyStoreFile(
"./scripts/keyStores/keystore_0xADA62933dEd1F05764a28DD9DFEB2E8CFAe18731.json",
password
);
return [admin, manager, teamMember];
}
const password = readlineSync.question("Inform the KeyStore JSON files password: ",
{
hideEchoBack: true,
}
);
const [admin, manager, teamMember] = await getSigners(password);
const ETHPoolFactory = await hardhatEthers.getContractFactory("ETHPool");
const ethPool = await ETHPoolFactory.deploy(manager.address);
The fullcode can be found in scripts/deploy.ts from another repository: https://github.com/fabianorodrigo/ETHPool
Is there anything I'm missing?
Hey, we needed something like this so we ended up building it into a hardhat plugin: https://github.com/edgeandnode/hardhat-secure-accounts. It's still early stages and will keep improving it but we've been using it for a bit so thought about sharing here.
The plugin uses ethers wallet encryption to store menmonics using keystore files in your project folder; when unlocking you get a prompt that lets you choose which account and password to use. We tried to build it to be flexible for most use cases, so you can unlock and get a wallet, a signer or a provider. It adds a couple tasks and extends the hardhat environment, I suggest taking a look at the README if you are interested in.
Here is a quick sample of how you could use it, first import a mnemonic into the project:
npx hardhat accounts new
Then use it on your scripts/tasks/etc:
import hre from 'hardhat'
const wallet = await hre.accounts.getWallet()
const signerWithAddress = await hre.accounts.getSigner()
const provider = await hre.accounts.getProvider()
With 1password8 you can retrieve password using CLI, and I use "@1password/op-js" package to retrieve private key stored in 1password. sample typescript code below will retrieve private key stored under item "Metamask" as type "password" with label "Deployer".
import { item } from "@1password/op-js";
const DEPLOYER: string =
item
.get("Metamask")
.fields?.filter((i) => i.type === `CONCEALED` && i.label === `Deployer`)
?.map((e) => e.value)
?.toString() || "";
Chainlink just published a package which should solve this issue.
https://www.npmjs.com/package/@chainlink/env-enc https://youtu.be/uMX7pLKf3YE
Oooo. Perhaps a PR to the docs @fvictorio?
Nice! Working on this is high in our roadmap, so we'll definitely take a look at that package, thanks for letting us know!
We just released Hardhat v2.19.0, which includes a new feature, configuration variables, that should help with this. You can read about it here.
I'm closing this for now. There are some improvements we'll likely make to this feature, but we should open new issues for them.