Skip to content

Commit

Permalink
Prevent losing typechain types when compiling a subset of contracts
Browse files Browse the repository at this point in the history
  • Loading branch information
antico5 committed Mar 10, 2025
1 parent db98630 commit f7e49db
Show file tree
Hide file tree
Showing 3 changed files with 100 additions and 16 deletions.
12 changes: 11 additions & 1 deletion v-next/hardhat-typechain/src/internal/hook-handlers/solidity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,20 @@ export default async (): Promise<Partial<SolidityHooks>> => {
artifacts: Map<CompilationJob, ReadonlyMap<string, string[]>>,
) => Promise<void>,
) {
const artifactsPaths = Array.from(artifacts.values()).flatMap(
const currentArtifactsPaths = Array.from(artifacts.values()).flatMap(
(innerMap) => Array.from(innerMap.values()).flat(),
);

const existingArtifactsPaths = await Promise.all(
Array.from(await context.artifacts.getAllFullyQualifiedNames()).map(
(name) => context.artifacts.getArtifactPath(name),
),
);

const artifactsPaths = Array.from(
new Set([...currentArtifactsPaths, ...existingArtifactsPaths]),
);

await generateTypes(
context.config.paths.root,
context.config.typechain,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract B {
function getMessage() external pure returns (string memory) {
return "Hello from B contract!";
}
}
96 changes: 81 additions & 15 deletions v-next/hardhat-typechain/test/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import type { HardhatRuntimeEnvironment } from "hardhat/types/hre";

import assert from "node:assert/strict";
import path from "node:path";
import { before, describe, it } from "node:test";
import { before, beforeEach, describe, it } from "node:test";

import { useFixtureProject } from "@nomicfoundation/hardhat-test-utils";
import {
Expand All @@ -10,30 +12,61 @@ import {
} from "@nomicfoundation/hardhat-utils/fs";
import { createHardhatRuntimeEnvironment } from "hardhat/hre";

// Read the contract factory from the generated types
async function readContractFactory(contractName: string) {
const potentialPaths = [
`${process.cwd()}/types/ethers-contracts/factories/${contractName}__factory.ts`,
`${process.cwd()}/types/ethers-contracts/factories/${contractName}.sol/${contractName}__factory.ts`,
];
for (const potentialPath of potentialPaths) {
if (await exists(potentialPath)) {
return readUtf8File(potentialPath);
}
}
return undefined;
}

// Check the contract is typed in hardhat.d.ts
function isContractTyped(typeFileContents: string, contractName: string) {
const lookupStrings = [
`getContractFactory(name: '${contractName}'`,
`getContractAt(name: '${contractName}'`,
`deployContract(name: '${contractName}'`,
];
for (const lookupString of lookupStrings) {
if (!typeFileContents.includes(lookupString)) {
return false;
}
}
return true;
}

describe("hardhat-typechain", () => {
describe("check that types are generated correctly", () => {
const projectFolder = "generate-types";
let hre: HardhatRuntimeEnvironment;
let hardhatConfig: any;

useFixtureProject(projectFolder);

before(async () => {
beforeEach(async () => {
await remove(`${process.cwd()}/types`);

const hardhatConfig = await import(
hardhatConfig = await import(
// eslint-disable-next-line import/no-relative-packages -- allow for fixture projects
`./fixture-projects/${projectFolder}/hardhat.config.js`
);

const hre = await createHardhatRuntimeEnvironment(hardhatConfig.default);
hre = await createHardhatRuntimeEnvironment(hardhatConfig.default);

assert.equal(await exists(`${process.cwd()}/types`), false);

await hre.tasks.getTask("clean").run();

await hre.tasks.getTask("compile").run();
});

it("should generate the types for the `hardhat.d.ts` file", async () => {
await hre.tasks.getTask("compile").run();

// Check that the types are generated with the expected addition of the "/index.js" extensions
// and the v3 modules

Expand Down Expand Up @@ -63,18 +96,24 @@ describe("hardhat-typechain", () => {

// The import from a npm package should have ".js" extensions
assert.equal(content.includes(`import { ethers } from 'ethers'`), true);

for (const contractName of ["A", "B"]) {
assert.ok(
(await readContractFactory(contractName)) !== undefined,
`Factory for ${contractName} doesnt exist`,
);
assert.equal(isContractTyped(content, contractName), true);
}
});

it("should generated types for the contracts and add the support for the `attach` method", async () => {
const content = await readUtf8File(
path.join(
process.cwd(),
"types",
"ethers-contracts",
"factories",
"A__factory.ts",
),
);
await hre.tasks.getTask("compile").run();

const content = await readContractFactory("A");

if (content === undefined) {
throw new Error("Factory for A.sol not found");
}

// The "Addressable" type should be imported
assert.equal(
Expand All @@ -88,6 +127,33 @@ describe("hardhat-typechain", () => {
true,
);
});

it("doesnt lose types when compiling a subset of the contracts", async () => {
// First: compile only A.sol. Only A should be typed
await hre.tasks.getTask("compile").run({ files: ["contracts/A.sol"] });

assert.notEqual(await readContractFactory("A"), undefined);
assert.equal(await readContractFactory("B"), undefined);

let content = await readUtf8File(
path.join(process.cwd(), "types", "ethers-contracts", "hardhat.d.ts"),
);

assert.equal(isContractTyped(content, "A"), true);
assert.equal(isContractTyped(content, "B"), false);

// Second: compile only B.sol. Both A and B should be typed
await hre.tasks.getTask("compile").run({ files: ["contracts/B.sol"] });

assert.notEqual(await readContractFactory("A"), undefined);
assert.notEqual(await readContractFactory("B"), undefined);

content = await readUtf8File(
path.join(process.cwd(), "types", "ethers-contracts", "hardhat.d.ts"),
);
assert.equal(isContractTyped(content, "A"), true);
assert.equal(isContractTyped(content, "B"), true);
});
});

describe("typechain should not generate types during compilation", () => {
Expand Down

0 comments on commit f7e49db

Please sign in to comment.