Using hardhat-deploy with rocketh
hardhat-deploy v2 is a Hardhat plugin that runs rocketh under the hood. Your rocketh/config.ts, your extensions and your deploy scripts are the same ones described everywhere else in this documentation; hardhat-deploy adds the Hardhat wiring around them, so deployments happen through a Hardhat task and are available to Hardhat tests.
TIP
This page covers the join between the two. The dedicated hardhat-deploy documentation goes much deeper, including migration from v1, network helpers, fork testing and per-network scripts.
Configuring hardhat-deploy
Add the plugin to the plugins array in hardhat.config.ts. The helpers it ships pull network and account configuration out of environment variables, so RPC URLs and mnemonics stay out of the repository:
import {HardhatUserConfig} from 'hardhat/config';
import HardhatDeploy from 'hardhat-deploy';
import {addForkConfiguration, addNetworksFromEnv} from 'hardhat-deploy/helpers';
const config: HardhatUserConfig = {
plugins: [HardhatDeploy],
solidity: {
profiles: {
default: {version: '0.8.28'},
},
},
// adds a network for each `ETH_NODE_URI_<network>` env var found,
// reading `MNEMONIC_<network>` to populate its accounts
networks: addForkConfiguration(addNetworksFromEnv({})),
};
export default config;Everything else, named accounts, extensions and chain settings, stays in rocketh/config.ts as described in Installation and Setup. hardhat-deploy reads that file; it does not introduce a second place to configure the same things.
Running Deployments
To run your deployment scripts, use the hardhat deploy task:
npx hardhat deploy --network sepoliaYou can also run specific tags:
npx hardhat deploy --network sepolia --tags GreetingsRegistryThe unknown-signer flags described in Handling unknown signers work here too:
npx hardhat deploy --network sepolia --on-unknown-signer throwUsing Deployments in Tests
The point of running deploy scripts in tests is that you test the contracts as deployed, wired together exactly as they will be on chain, rather than a fresh set of contracts assembled by the test.
Wrap loadAndExecuteDeploymentsFromFiles (from your rocketh/environment.ts) in a fixture, so Hardhat can snapshot the chain after the deployment and restore it for each test instead of redeploying every time:
// test/utils/index.ts
import {loadAndExecuteDeploymentsFromFiles, artifacts} from '../../rocketh/environment.js';
import {EthereumProvider} from 'hardhat/types/providers';
export function setupFixtures(provider: EthereumProvider) {
return {
async deployAll() {
const env = await loadAndExecuteDeploymentsFromFiles({provider});
// Deployments are inherently untyped, since what is on chain can differ from the
// current artifacts. Type them here, assuming the artifact still matches.
const GreetingsRegistry = env.get<typeof artifacts.GreetingsRegistry.abi>('GreetingsRegistry');
return {env, GreetingsRegistry, namedAccounts: env.namedAccounts, unnamedAccounts: env.unnamedAccounts};
},
};
}The test then loads that fixture and uses the same read / execute functions your deploy scripts use:
// test/GreetingsRegistry.test.ts
import {network} from 'hardhat';
import {setupFixtures} from './utils/index.js';
const {provider, networkHelpers} = await network.connect();
const {deployAll} = setupFixtures(provider);
describe('GreetingsRegistry', function () {
it('stores a greeting', async function () {
const {env, GreetingsRegistry, unnamedAccounts} = await networkHelpers.loadFixture(deployAll);
const greeter = unnamedAccounts[0];
await env.execute(GreetingsRegistry, {
functionName: 'setMessage',
args: ['hello world'],
account: greeter,
});
expect(await env.read(GreetingsRegistry, {functionName: 'messages', args: [greeter]})).toEqual('hello world');
});
});Because the fixture runs your real deploy scripts, a script that breaks is caught by the test suite rather than at deployment time.