diff --git a/extension/packages/hardhat/deploy/01_deploy_se2_token.ts b/extension/packages/hardhat/deploy/01_deploy_se2_token.ts index 1e6660f..01cf6d2 100644 --- a/extension/packages/hardhat/deploy/01_deploy_se2_token.ts +++ b/extension/packages/hardhat/deploy/01_deploy_se2_token.ts @@ -1,37 +1,32 @@ -import { HardhatRuntimeEnvironment } from "hardhat/types"; -import { DeployFunction } from "hardhat-deploy/types"; +import { deployScript, artifacts } from "../rocketh/deploy.js"; /** - * Deploys a contract named "YourContract" using the deployer account and - * constructor arguments set to the deployer address + * Deploys a contract named "SE2Token" using the deployer account. * - * @param hre HardhatRuntimeEnvironment object. + * @param env Rocketh environment object. */ -const deploySe2Token: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { - /* - On localhost, the deployer account is the one that comes with Hardhat, which is already funded. +export default deployScript( + async env => { + /* + On localhost, the deployer account is the one that comes with Hardhat, which is already funded. - When deploying to live networks (e.g `yarn deploy --network sepolia`), the deployer account - should have sufficient balance to pay for the gas fees for contract creation. + When deploying to live networks (e.g `yarn deploy --network sepolia`), the deployer account + should have sufficient balance to pay for the gas fees for contract creation. - You can generate a random account with `yarn generate` which will fill DEPLOYER_PRIVATE_KEY - with a random private key in the .env file (then used on hardhat.config.ts) - You can run the `yarn account` command to check your balance in every network. - */ - const { deployer } = await hre.getNamedAccounts(); - const { deploy } = hre.deployments; + You can generate a random account with `yarn generate` or `yarn account:import` to import your + existing PK which will fill DEPLOYER_PRIVATE_KEY_ENCRYPTED in the .env file (then used on hardhat.config.ts) + You can run the `yarn account` command to check your balance in every network. + */ + const { deployer } = env.namedAccounts; - await deploy("SE2Token", { - from: deployer, - log: true, - // autoMine: can be passed to the deploy function to make the deployment process faster on local networks by - // automatically mining the contract deployment transaction. There is no effect on live networks. - autoMine: true, - }); -}; - -export default deploySe2Token; - -// Tags are useful if you have multiple deploy files and only want to run one of them. -// e.g. yarn deploy --tags SE2Token -deploySe2Token.tags = ["SE2Token"]; + await env.deploy("SE2Token", { + account: deployer, + artifact: artifacts.SE2Token, + }); + }, + { + // Tags are useful if you have multiple deploy files and only want to run some of them. + // e.g. yarn deploy --tags SE2Token + tags: ["SE2Token"], + }, +); diff --git a/extension/packages/hardhat/hardhat.config.ts.args.mjs b/extension/packages/hardhat/hardhat.config.ts.args.mjs index 3906c22..82a0cfc 100644 --- a/extension/packages/hardhat/hardhat.config.ts.args.mjs +++ b/extension/packages/hardhat/hardhat.config.ts.args.mjs @@ -1,5 +1,5 @@ export const preContent = ` -import "./tasks" +import { sayHelloTask } from "./tasks/index.js" // Custom variables // const CUSTOM_API_KEY = process.env.CUSTOM_API_KEY; @@ -20,6 +20,22 @@ export const configOverrides = { }, ], }, + // In Hardhat v3, custom block explorers are declared per-chain via top-level + // `chainDescriptors` (keyed by chainId), not a per-network `verify` block. + // The Etherscan API key stays at the top level (base config's `verify.etherscan.apiKey`). + // `hardhat verify` resolves the explorer by the live network's chainId. + chainDescriptors: { + 99999: { + name: "Custom Network", + blockExplorers: { + etherscan: { + name: "Custom Explorer", + url: "https://custom-explorer.io", + apiUrl: "https://api.custom-explorer.io", + }, + }, + }, + }, networks: { hardhat: { forking: { @@ -27,14 +43,12 @@ export const configOverrides = { } }, customNetwork: { + type: "http", url: "https://custom.network", accounts: ["$$deployerPrivateKey$$"], - verify: { - etherscan: { - apiUrl: "https://api.custom-explorer.io", - apiKey: "$$etherscanApiKey$$", - } - } + chainId: 99999, } }, + // Append the extension's task to the base `deployTasks` array. + tasks: "$$[...deployTasks, sayHelloTask]$$", }; diff --git a/extension/packages/hardhat/tasks/index.ts b/extension/packages/hardhat/tasks/index.ts index 35d9e10..6b34370 100644 --- a/extension/packages/hardhat/tasks/index.ts +++ b/extension/packages/hardhat/tasks/index.ts @@ -1,5 +1,9 @@ import { task } from "hardhat/config"; -task("sayHello", "Prints 'Hello, World!'").setAction(async () => { - console.log("Hello, World!"); -}); +// In Hardhat 3, tasks are built and registered via the `tasks` array in +// `hardhat.config.ts`. We export the built task definition and add it there. +export const sayHelloTask = task("sayHello", "Prints 'Hello, World!'") + .setInlineAction(async () => { + console.log("Hello, World!"); + }) + .build(); diff --git a/extension/packages/hardhat/test/SE2Token.ts b/extension/packages/hardhat/test/SE2Token.ts index 7ab1088..e119835 100644 --- a/extension/packages/hardhat/test/SE2Token.ts +++ b/extension/packages/hardhat/test/SE2Token.ts @@ -1,69 +1,57 @@ import { expect } from "chai"; -import { ethers } from "hardhat"; -import { SE2Token } from "../typechain-types"; -import { SignerWithAddress } from "@nomicfoundation/hardhat-ethers/signers"; +import { network } from "hardhat"; +import type { Abi_SE2Token } from "../generated/abis/SE2Token.js"; +import { loadAndExecuteDeploymentsFromFiles } from "../rocketh/environment.js"; -describe("SE2Token", function () { - let token: SE2Token; - let owner: SignerWithAddress; - let user1: SignerWithAddress; - let user2: SignerWithAddress; - - beforeEach(async function () { - // Get signers - [owner, user1, user2] = await ethers.getSigners(); +const { provider, networkHelpers, ethers } = await network.create(); - // Deploy the token - const SE2Token = await ethers.getContractFactory("SE2Token"); - token = await SE2Token.deploy(); - await token.waitForDeployment(); - }); +// We define a fixture to reuse the same setup in every test. +async function deployFixture() { + const env = await loadAndExecuteDeploymentsFromFiles({ provider }); + const { address, abi } = env.get("SE2Token"); + const [, user1, user2] = await ethers.getSigners(); + const token = await ethers.getContractAt(abi, address); + return { token, address, abi, user1, user2 }; +} +describe("SE2Token", function () { describe("Initial Setup", function () { it("should have correct name, symbol and initial supply", async function () { - const name = await token.name(); - const symbol = await token.symbol(); - const supply = await token.totalSupply(); + const { token } = await networkHelpers.loadFixture(deployFixture); - expect(name).to.equal("SE2Token"); - expect(symbol).to.equal("SE2"); - expect(supply).to.equal(0n); + expect(await token.name()).to.equal("SE2Token"); + expect(await token.symbol()).to.equal("SE2"); + expect(await token.totalSupply()).to.equal(0n); }); }); describe("Minting", function () { it("should mint tokens correctly", async function () { + const { token, user1 } = await networkHelpers.loadFixture(deployFixture); const mintAmount = 100n * 10n ** 18n; // 100 tokens - // Mint tokens to user1 - await token.connect(owner).mint(user1.address, mintAmount); - - // Check balance - const balance = await token.balanceOf(user1.address); - const totalSupply = await token.totalSupply(); + // Minting is open to anyone; the default signer mints to user1 + await token.mint(user1.address, mintAmount); - expect(balance).to.equal(mintAmount); - expect(totalSupply).to.equal(mintAmount); + expect(await token.balanceOf(user1.address)).to.equal(mintAmount); + expect(await token.totalSupply()).to.equal(mintAmount); }); }); describe("Transfer", function () { it("should transfer tokens correctly", async function () { + const { address, abi, user1, user2 } = await networkHelpers.loadFixture(deployFixture); const mintAmount = 100n * 10n ** 18n; // 100 tokens const transferAmount = 30n * 10n ** 18n; // 30 tokens - // Mint tokens to user1 - await token.connect(owner).mint(user1.address, mintAmount); - - // Transfer from user1 to user2 - await token.connect(user1).transfer(user2.address, transferAmount); + const tokenAsUser1 = await ethers.getContractAt(abi, address, user1); - // Check balances - const user1Balance = await token.balanceOf(user1.address); - const user2Balance = await token.balanceOf(user2.address); + // Mint to user1, then transfer user1 -> user2 + await tokenAsUser1.mint(user1.address, mintAmount); + await tokenAsUser1.transfer(user2.address, transferAmount); - expect(user1Balance).to.equal(mintAmount - transferAmount); - expect(user2Balance).to.equal(transferAmount); + expect(await tokenAsUser1.balanceOf(user1.address)).to.equal(mintAmount - transferAmount); + expect(await tokenAsUser1.balanceOf(user2.address)).to.equal(transferAmount); }); }); });