feat: migrate deployment to Hardhat Ignition

Replace the imperative deploy scripts with Hardhat Ignition modules:
- ignition/modules/{MockToken,Reputation,P2PIX}.ts orchestrate the full
  deployment graph; P2PIX.ts wires MockToken + Reputation and deploys
  P2PIX via its constructor
- ignition/parameters/localhost.json holds per-network values
  (defaultBlocks, validSigners, MockToken supply)
- swap hardhat-toolbox for the individual plugins that Ignition needs;
  add hardhat-verify (v2) and bump hardhat/hardhat-tracer accordingly
- delete scripts/1-deploy-mockToken.ts and scripts/2-deploy-p2pix.ts
- add deploy:{localhost,goerli,sepolia,mumbai} npm scripts
- include ignition/**/* in tsconfig.json
- gitignore ignition/deployments/chain-31337/ (ephemeral local state)

This branch carries the deployment-tooling migration only — the contract
is still the original constructor-based P2PIX. Proxy / UUPS deploy
support will land alongside the upgradeable contract change.
This commit is contained in:
2026-05-20 19:56:55 -03:00
parent eaf45a0288
commit 13d1d16084
10 changed files with 111 additions and 149 deletions

4
.gitignore vendored
View File

@@ -29,3 +29,7 @@ npm-debug.log*
yarn-debug.log*
yarn-error.log*
docs/lib/mock/*.md
.dagrobin/
# Hardhat Ignition local/ephemeral deployments
ignition/deployments/chain-31337/

View File

@@ -125,36 +125,52 @@ To grab deployment addresses you can just grab from deploys folder:
import localhostDeploys from "p2pix-smart-contracts/deploys/localhost.json";
```
## Deploying to local environment
## Deployment (Hardhat Ignition)
On the first teminal, use the following command and import some wallets to your Metamask, then connect to the network pointed:
Deployments are orchestrated by
[Hardhat Ignition](https://hardhat.org/ignition). Each module declares its
contracts; Ignition handles ordering, idempotency, and per-network state
under `ignition/deployments/`.
### Modules
- `ignition/modules/MockToken.ts` — ERC20 mock used in dev / testnets.
- `ignition/modules/Reputation.ts` — Reputation + Multicall.
- `ignition/modules/P2PIX.ts` — wires Reputation + MockToken and deploys
P2PIX via its constructor, returning the full set of addresses.
### Per-network parameters
Network-specific values (e.g. `defaultBlocks`, `validSigners`, MockToken
`supply`) live in `ignition/parameters/<network>.json`. Only
`localhost.json` is checked in; copy it to e.g. `goerli.json` and set the
network values before running the matching `deploy:*` script.
### Local environment
On the first terminal:
```sh
yarn hardhat node
```
On the second teminal, run the following commands:
On the second terminal:
```sh
yarn deploy1:localhost
yarn deploy2:localhost
yarn deploy:localhost
```
**_NOTE_:** The second script transfers 2M tokens to the first wallet of the node.
To use the P2Pix smart contract first transfer some of the tokens to other wallets.
Addresses for every deployed contract are written to
`ignition/deployments/chain-31337/deployed_addresses.json` (this path is
gitignored — local-only state).
## Deploying to testnets
Deploy to Ethereum's Goerli testnet:
### Testnets
```sh
yarn deploy1:goerli
yarn deploy2:goerli
yarn deploy:goerli
yarn deploy:sepolia
yarn deploy:mumbai
```
Deploy to Polygon's Mumbai testnet:
```sh
yarn deploy1:mumbai
yarn deploy2:mumbai
```
Each command requires the matching `ignition/parameters/<network>.json`
and the relevant API keys in `.env`.

View File

@@ -0,0 +1,14 @@
import { buildModule } from "@nomicfoundation/hardhat-ignition/modules";
import { ethers } from "ethers";
const DEFAULT_SUPPLY = ethers.utils
.parseEther("20000000")
.toString();
export default buildModule("MockToken", m => {
const supply = m.getParameter("supply", DEFAULT_SUPPLY);
const token = m.contract("MockToken", [supply]);
return { token };
});

32
ignition/modules/P2PIX.ts Normal file
View File

@@ -0,0 +1,32 @@
import { buildModule } from "@nomicfoundation/hardhat-ignition/modules";
import MockTokenModule from "./MockToken";
import ReputationModule from "./Reputation";
export default buildModule("P2PIX", m => {
const { token } = m.useModule(MockTokenModule);
const { reputation, multicall } = m.useModule(
ReputationModule,
);
const defaultBlocks = m.getParameter("defaultBlocks", 10);
const validSigners = m.getParameter<string[]>(
"validSigners",
[],
);
const p2pix = m.contract("P2PIX", [
defaultBlocks,
validSigners,
reputation,
[token],
[true],
]);
return {
p2pix,
reputation,
multicall,
token,
};
});

View File

@@ -0,0 +1,8 @@
import { buildModule } from "@nomicfoundation/hardhat-ignition/modules";
export default buildModule("Reputation", m => {
const reputation = m.contract("Reputation", []);
const multicall = m.contract("Multicall", []);
return { reputation, multicall };
});

View File

@@ -0,0 +1,9 @@
{
"P2PIX": {
"defaultBlocks": 10,
"validSigners": []
},
"MockToken": {
"supply": "20000000000000000000000000"
}
}

View File

@@ -13,12 +13,10 @@
"compile": "hardhat compile",
"typechain": "hardhat typechain",
"test": "hardhat test",
"deploy1:localhost": "hardhat run scripts/1-deploy-mockToken.ts --network localhost",
"deploy2:localhost": "hardhat run scripts/2-deploy-p2pix.ts --network localhost",
"deploy1:goerli": "hardhat run scripts/1-deploy-mockToken.ts --network goerli",
"deploy2:goerli": "hardhat run scripts/2-deploy-p2pix.ts --network goerli",
"deploy1:mumbai": "hardhat run scripts/1-deploy-mockToken.ts --network polygon-mumbai",
"deploy2:mumbai": "hardhat run scripts/2-deploy-p2pix.ts --network polygon-mumbai",
"deploy:localhost": "hardhat ignition deploy ignition/modules/P2PIX.ts --network localhost --parameters ignition/parameters/localhost.json",
"deploy:goerli": "hardhat ignition deploy ignition/modules/P2PIX.ts --network goerli --parameters ignition/parameters/goerli.json",
"deploy:sepolia": "hardhat ignition deploy ignition/modules/P2PIX.ts --network sepolia --parameters ignition/parameters/sepolia.json",
"deploy:mumbai": "hardhat ignition deploy ignition/modules/P2PIX.ts --network polygon-mumbai --parameters ignition/parameters/mumbai.json",
"coverage": "hardhat coverage --solcoverjs ./.solcover.js --temp artifacts --testfiles \"test/**/*.ts\" && yarn typechain",
"lint": "yarn lint:sol && yarn lint:ts && yarn prettier:check",
"lint:sol": "solhint --config ./.solhint.json --max-warnings 0 \"contracts/**/*.sol\"",

View File

@@ -1,49 +0,0 @@
import "@nomicfoundation/hardhat-ethers";
import * as fs from "fs";
import { ethers, network } from "hardhat";
import { Deploys } from "../test/utils/interfaces";
let deploysJson: Deploys;
const supply: BigInt = ethers.parseEther("20000000");
const main = async () => {
try {
const data = fs.readFileSync(
`./deploys/${network.name}.json`,
{ encoding: "utf-8" },
);
deploysJson = JSON.parse(data);
} catch (err) {
console.log("Error loading Master address: ", err);
process.exit(1);
}
const [deployer] = await ethers.getSigners();
console.log(`Deploying contracts with ${deployer.address}`);
let erc20 = await ethers.deployContract("MockToken", [supply]);
erc20 = await erc20.waitForDeployment();
deploysJson.token = await erc20.getAddress();
console.log("🚀 Mock Token Deployed:", await erc20.getAddress());
fs.writeFileSync(
`./deploys/${network.name}.json`,
JSON.stringify(deploysJson, undefined, 2),
);
/* UNCOMMENT WHEN DEPLOYING TO MAINNET/PUBLIC TESTNETS */
// verify
// await hre.run("verify:verify", {
// address: erc20.address,
// constructorArguments: [supply],
// });
};
main()
.then(() => process.exit(0))
.catch(error => {
console.log(error);
process.exit(1);
});

View File

@@ -1,75 +0,0 @@
import "@nomicfoundation/hardhat-ethers";
import * as fs from "fs";
import { ethers, network } from "hardhat";
import { Deploys } from "../test/utils/interfaces";
let deploysJson: Deploys;
const main = async () => {
try {
const data = fs.readFileSync(
`./deploys/${network.name}.json`,
{ encoding: "utf-8" },
);
deploysJson = JSON.parse(data);
} catch (err) {
console.log("Error loading Master address: ", err);
process.exit(1);
}
const [deployer] = await ethers.getSigners();
console.log(`Deploying contracts with ${deployer.address}`);
let reputation = await ethers.deployContract("Reputation");
let multicall = await ethers.deployContract("Multicall");
let p2pix = await ethers.deployContract("P2PIX", [
10,
deploysJson.signers,
reputation.target,
[deploysJson.token],
[true],
]);
reputation = await reputation.waitForDeployment();
multicall = await multicall.waitForDeployment();
p2pix = await p2pix.waitForDeployment();
deploysJson.p2pix = await p2pix.getAddress();
console.log("🚀 P2PIX Deployed:", await p2pix.getAddress());
console.log("🌠 Reputation Deployed:", await reputation.getAddress());
console.log("🛰 Multicall Deployed:", await multicall.getAddress());
fs.writeFileSync(
`./deploys/${network.name}.json`,
JSON.stringify(deploysJson, undefined, 2),
);
/* UNCOMMENT WHEN DEPLOYING TO MAINNET/PUBLIC TESTNETS */
//verify
// await hre.run("verify:verify", {
// address: p2pix.address,
// constructorArguments: [
// 10,
// deploysJson.signers,
// reputation.address,
// [deploysJson.token],
// [true],
// ],
// });
// await hre.run("verify:verify", {
// address: reputation.address,
// constructorArguments: [],
// });
// await hre.run("verify:verify", {
// address: mutlicall.address,
// constructorArguments: [],
// });
};
main()
.then(() => process.exit(0))
.catch(error => {
console.log(error);
process.exit(1);
});

View File

@@ -18,5 +18,10 @@
},
"exclude": ["node_modules"],
"files": ["./hardhat.config.ts"],
"include": ["src/**/*", "test/**/*", "scripts/**/*"]
"include": [
"src/**/*",
"test/**/*",
"scripts/**/*",
"ignition/**/*"
]
}