hardhat
Version:
Hardhat is an extensible developer tool that helps smart contract developers increase productivity by reliably bringing together the tools they want.
189 lines (167 loc) • 5.42 kB
text/typescript
import type { RunOptions } from "./runner.js";
import type { Abi } from "../../../types/artifacts.js";
import type { ChainType } from "../../../types/network.js";
import type { SolidityTestConfig } from "../../../types/test.js";
import type {
SolidityTestRunnerConfigArgs,
PathPermission,
Artifact,
ObservabilityConfig,
} from "@nomicfoundation/edr";
import {
opGenesisState,
opLatestHardfork,
l1GenesisState,
l1HardforkLatest,
IncludeTraces,
FsAccessPermission,
CollectStackTraces,
} from "@nomicfoundation/edr";
import { hexStringToBytes } from "@nomicfoundation/hardhat-utils/hex";
import chalk from "chalk";
import { DEFAULT_VERBOSITY, OPTIMISM_CHAIN_TYPE } from "../../constants.js";
import { type Colorizer, formatArtifactId } from "./formatters.js";
interface SolidityTestConfigParams {
chainType: ChainType;
projectRoot: string;
config: SolidityTestConfig;
verbosity: number;
observability?: ObservabilityConfig;
testPattern?: string;
generateGasReport: boolean;
}
function hexStringToBuffer(hexString: string): Buffer {
return Buffer.from(hexStringToBytes(hexString));
}
export function solidityTestConfigToRunOptions(
config: SolidityTestConfig,
): RunOptions {
return config;
}
export async function solidityTestConfigToSolidityTestRunnerConfigArgs({
chainType,
projectRoot,
config,
verbosity,
observability,
testPattern,
generateGasReport,
}: SolidityTestConfigParams): Promise<SolidityTestRunnerConfigArgs> {
const fsPermissions: PathPermission[] | undefined = [
config.fsPermissions?.readWriteFile?.map((p) => ({
access: FsAccessPermission.ReadWriteFile,
path: p,
})) ?? [],
config.fsPermissions?.readFile?.map((p) => ({
access: FsAccessPermission.ReadFile,
path: p,
})) ?? [],
config.fsPermissions?.writeFile?.map((p) => ({
access: FsAccessPermission.WriteFile,
path: p,
})) ?? [],
config.fsPermissions?.dangerouslyReadWriteDirectory?.map((p) => ({
access: FsAccessPermission.DangerouslyReadWriteDirectory,
path: p,
})) ?? [],
config.fsPermissions?.readDirectory?.map((p) => ({
access: FsAccessPermission.ReadDirectory,
path: p,
})) ?? [],
config.fsPermissions?.dangerouslyWriteDirectory?.map((p) => ({
access: FsAccessPermission.DangerouslyWriteDirectory,
path: p,
})) ?? [],
].flat(1);
const sender: Buffer | undefined =
config.from === undefined ? undefined : hexStringToBuffer(config.from);
const txOrigin: Buffer | undefined =
config.txOrigin === undefined
? undefined
: hexStringToBuffer(config.txOrigin);
const blockCoinbase: Buffer | undefined =
config.coinbase === undefined
? undefined
: hexStringToBuffer(config.coinbase);
const localPredeploys =
chainType === OPTIMISM_CHAIN_TYPE
? opGenesisState(opLatestHardfork())
: l1GenesisState(l1HardforkLatest());
let includeTraces: IncludeTraces = IncludeTraces.None;
if (verbosity >= 5) {
includeTraces = IncludeTraces.All;
} else if (verbosity >= 3) {
includeTraces = IncludeTraces.Failing;
}
const blockGasLimit =
config.blockGasLimit === false ? undefined : config.blockGasLimit;
const disableBlockGasLimit = config.blockGasLimit === false;
const blockDifficulty = config.prevRandao;
let ethRpcUrl: string | undefined;
if (config.forking?.url !== undefined) {
ethRpcUrl = await config.forking.url.get();
}
const forkBlockNumber = config.forking?.blockNumber;
let rpcEndpoints: Record<string, string> | undefined;
if (config.forking?.rpcEndpoints !== undefined) {
rpcEndpoints = {};
for (const [name, configValue] of Object.entries(
config.forking.rpcEndpoints,
)) {
rpcEndpoints[name] = await configValue.get();
}
}
const shouldAlwaysCollectStackTraces = verbosity > DEFAULT_VERBOSITY;
return {
projectRoot,
...config,
fsPermissions,
localPredeploys,
sender,
txOrigin,
blockCoinbase,
observability,
testPattern,
includeTraces,
blockGasLimit,
disableBlockGasLimit,
blockDifficulty,
ethRpcUrl,
forkBlockNumber,
rpcEndpoints,
generateGasReport,
collectStackTraces: shouldAlwaysCollectStackTraces
? CollectStackTraces.Always
: CollectStackTraces.OnFailure,
};
}
export function isTestSuiteArtifact(artifact: Artifact): boolean {
const abi: Abi = JSON.parse(artifact.contract.abi);
return abi.some(({ type, name }) => {
if (type === "function" && typeof name === "string") {
return name.startsWith("test") || name.startsWith("invariant");
}
return false;
});
}
export function warnDeprecatedTestFail(
artifact: Artifact,
sourceNameToUserSourceName: Map<string, string>,
colorizer: Colorizer = chalk,
): void {
const abi: Abi = JSON.parse(artifact.contract.abi);
abi.forEach(({ type, name }) => {
if (
type === "function" &&
typeof name === "string" &&
name.startsWith("testFail")
) {
const formattedLocation = formatArtifactId(
artifact.id,
sourceNameToUserSourceName,
);
const warningMessage = `${colorizer.yellow("Warning")}: ${name} The support for the prefix \`testFail*\` has been removed. Consider using \`vm.expectRevert()\` for testing reverts in ${formattedLocation}\n`;
console.warn(warningMessage);
}
});
}