diff --git a/.gitignore b/.gitignore index 1543b3d..58ceb70 100644 --- a/.gitignore +++ b/.gitignore @@ -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/ diff --git a/README.md b/README.md index fa83f12..53c087a 100644 --- a/README.md +++ b/README.md @@ -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/.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/.json` +and the relevant API keys in `.env`. diff --git a/ignition/modules/MockToken.ts b/ignition/modules/MockToken.ts new file mode 100644 index 0000000..8d7e380 --- /dev/null +++ b/ignition/modules/MockToken.ts @@ -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 }; +}); diff --git a/ignition/modules/P2PIX.ts b/ignition/modules/P2PIX.ts new file mode 100644 index 0000000..cf19e7c --- /dev/null +++ b/ignition/modules/P2PIX.ts @@ -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( + "validSigners", + [], + ); + + const p2pix = m.contract("P2PIX", [ + defaultBlocks, + validSigners, + reputation, + [token], + [true], + ]); + + return { + p2pix, + reputation, + multicall, + token, + }; +}); diff --git a/ignition/modules/Reputation.ts b/ignition/modules/Reputation.ts new file mode 100644 index 0000000..f831225 --- /dev/null +++ b/ignition/modules/Reputation.ts @@ -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 }; +}); diff --git a/ignition/parameters/localhost.json b/ignition/parameters/localhost.json new file mode 100644 index 0000000..380fe06 --- /dev/null +++ b/ignition/parameters/localhost.json @@ -0,0 +1,9 @@ +{ + "P2PIX": { + "defaultBlocks": 10, + "validSigners": [] + }, + "MockToken": { + "supply": "20000000000000000000000000" + } +} diff --git a/package.json b/package.json index bfd0777..1e0a504 100644 --- a/package.json +++ b/package.json @@ -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\"", diff --git a/scripts/1-deploy-mockToken.ts b/scripts/1-deploy-mockToken.ts deleted file mode 100644 index 95f1776..0000000 --- a/scripts/1-deploy-mockToken.ts +++ /dev/null @@ -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); - }); diff --git a/scripts/2-deploy-p2pix.ts b/scripts/2-deploy-p2pix.ts deleted file mode 100644 index a6d1f1a..0000000 --- a/scripts/2-deploy-p2pix.ts +++ /dev/null @@ -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); - }); diff --git a/tsconfig.json b/tsconfig.json index 3b62680..0fff353 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -18,5 +18,10 @@ }, "exclude": ["node_modules"], "files": ["./hardhat.config.ts"], - "include": ["src/**/*", "test/**/*", "scripts/**/*"] + "include": [ + "src/**/*", + "test/**/*", + "scripts/**/*", + "ignition/**/*" + ] }