@fastackl/hardhat-kitty
v0.1.1
Published
Reusable deploy/initialize/verify task kit for Hardhat 3.
Readme
@fastackl/hardhat-kitty
Config-driven Hardhat 3 kit for contract deploy, initialize, and verify workflows.
It gives you:
- Hardhat tasks:
kitty:deploy,kitty:initialize,kitty:verify - Programmatic functions:
deploy,testDeploy,initialize,testInitialize,verify,testVerify - Persistent deployment metadata saved to
deployments/*.jsonfor later initialization, verification, and tests
Install
yarn add -D @fastackl/hardhat-kittyHardhat Setup
In your hardhat.config.ts:
import { defineConfig } from "hardhat/config";
import hardhatToolboxMochaEthers from "@nomicfoundation/hardhat-toolbox-mocha-ethers";
import { kittyTasks } from "@fastackl/hardhat-kitty/hardhat";
export default defineConfig({
plugins: [hardhatToolboxMochaEthers],
tasks: [...kittyTasks],
});Scripts Config File
By default, the kit looks for config in this order:
scripts/config/scriptsConfig.tsscripts/config/scriptsConfig.jsscripts.config.tsscripts.config.js
You can always override with KIT_CONFIG=<path>.
Generate a starter config file:
yarn kitty-initThis creates scripts/config/scriptsConfig.ts if it does not already exist.
You can also pass a custom output path:
yarn kitty-init ./scripts/config/myScriptsConfig.tsIf installed as a package, you can run the bundled binary directly:
yarn kitty-initExample scripts/config/scriptsConfig.ts:
import type { Config } from "@fastackl/hardhat-kitty/types";
const config: Config = {
networks: {
sepolia: {
deploy: [
// Implicit fqn_filePath + fqn
{
fqn_contractName: "HelloWorld",
args: { args: ["Hello from deploy!", 42] },
},
// Library contract deployed first
{
fqn_contractName: "VersionMath",
},
// Explicit fqn_filePath + fqn
{
fqn_contractName: "ConfigShowcase",
fqn_filePath: "contracts/ConfigShowcase.sol",
fqn: "contracts/ConfigShowcase.sol:ConfigShowcase",
args: {
args: ["HelloWorld.address", "SIGNER[0]", "sepolia config showcase", 1],
},
libraries: {
VersionMath: "VersionMath.address",
},
},
],
initialize: [
{
fqn_contractName: "HelloWorld",
function: "initialize",
args: { args: ["Hello from initialize!", 99] },
},
{
fqn_contractName: "ConfigShowcase",
function: "initialize",
args: {
args: ["HelloWorld.address", "SIGNER[0]", "sepolia initialized", 2],
},
},
],
verify: ["ALL"],
},
},
};
export default config;Config Notes
deploy[].fqn_contractNameis required;fqn_filePathandfqnare optional and auto-derived when omitted.- The included smoke fixture demonstrates both forms:
HelloWorld: implicitfqn_filePath/fqnConfigShowcase: explicitfqn_filePath/fqn
deploy[].librariessupports linked library addresses in ethers format, including references like"VersionMath.address".- When using linked libraries, deploy the library contract before contracts that consume it.
verify: ["ALL"]verifies all contracts found in deployment metadata.- Argument values support:
- direct literals
- contract address references like
"MyContract.address" - signer references like
"SIGNER[0]"
Run Tasks
Preferred CLI UX (explicit flags):
yarn hardhat kitty:deploy --network sepolia --print true
yarn hardhat kitty:initialize --network sepolia --print true --signer-index 0
yarn hardhat kitty:verify --network sepolia --print falseAlternative env-driven form:
HARDHAT_NETWORK=sepolia yarn hardhat kitty:deploy
HARDHAT_NETWORK=sepolia yarn hardhat kitty:initialize
HARDHAT_NETWORK=sepolia yarn hardhat kitty:verifyCLI options (examples):
yarn hardhat kitty:deploy --network sepolia --print true
yarn hardhat kitty:initialize --network sepolia --print true --signer-index 0
yarn hardhat kitty:verify --network sepolia --print falseAdditional task options:
--config-path <path>for all kitty tasks--signer-index <n>forkitty:deployandkitty:initialize--ethernal true|falseforkitty:deploy- Boolean options are passed as
true|falsestrings (for example--print true). - When both CLI flags and env vars are provided, CLI flags take precedence.
Optional env vars:
KIT_CONFIG=<path>SIGNERINDEX=<n>(deploy/init)ETHERNAL=true(deploy only)PRINT=true(table-style output; avoid in non-interactive CI shells)
Deployment Metadata
kitty:deploy writes one JSON file per contract under deployments/:
- path:
deployments/<ContractName>.json - includes:
contractName,sourcePath,args,libraries,abi,buildTime,network,txHash,address
kitty:initialize and kitty:verify consume this metadata automatically.
You can also read it in tests/scripts:
import { promises as fs } from "node:fs";
import path from "node:path";
import { ethers } from "hardhat";
const metadataPath = path.join(process.cwd(), "deployments", "HelloWorld.json");
const metadata = JSON.parse(await fs.readFile(metadataPath, "utf8"));
const hello = await ethers.getContractAt("HelloWorld", metadata.address);Use Programmatically in Tests
For Mocha/Chai tests, prefer testDeploy in setup (leaner and less likely to break from output/presentation behavior):
import { expect } from "chai";
import { ethers } from "hardhat";
import { testDeploy, initialize } from "@fastackl/hardhat-kitty";
describe("My flow", function () {
before(async function () {
await testDeploy(0, false, "scripts/config/scriptsConfig.ts");
await initialize({
configPath: "scripts/config/scriptsConfig.ts",
signerIndex: 0,
printTable: false,
});
});
it("reads initialized state", async function () {
// Replace with your deployed contract address/lookup.
const hello = await ethers.getContractAt("HelloWorld", "0xYourAddress");
expect(await hello.sayHello()).to.not.equal("");
});
});Optional Path Customization
If your repo uses non-default directories, you can override internal paths:
import { setScriptPaths } from "@fastackl/hardhat-kitty";
setScriptPaths({
deployments: "./custom-deployments",
sources: "./src/contracts",
});Dev (this repo)
yarn typecheck
yarn test
yarn build