diff --git a/packages/hre-extender-v1/src/setup.ts b/packages/hre-extender-v1/src/setup.ts index a2571461..421f0698 100644 --- a/packages/hre-extender-v1/src/setup.ts +++ b/packages/hre-extender-v1/src/setup.ts @@ -15,7 +15,7 @@ import{ TenderlyNetwork as TenderlyNetworkInterface } from "@tenderly/api-client import { logger } from "./logger"; import { TenderlyService } from "@tenderly/api-client"; -import { Tenderly, TenderlyNetwork } from "@tenderly/hardhat-integration"; +import { Tenderly, TenderlyNetwork, VersionCompatibilityChecker } from "@tenderly/hardhat-integration"; import { extendEthers } from "./extenders/extend-ethers"; import { extendHardhatDeploy } from "./extenders/extend-hardhat-deploy"; import { isTenderlyNetworkConfig } from "./extenders/tenderly-network-resolver"; @@ -26,12 +26,12 @@ export function setup(cfg: { automaticVerifications: boolean } = { automaticVeri extendEnvironment(async (hre: HardhatRuntimeEnvironment) => { process.env.TENDERLY_AUTOMATIC_VERIFICATION = cfg.automaticVerifications ? "true": "false" process.env.AUTOMATIC_VERIFICATION_ENABLED = cfg.automaticVerifications ? "true": "false" - process.env.HARDHAT_TENDERLY_VERSION = require("../package.json").version; + const hardhatTenderlyVersion = require("../package.json").version; + process.env.HARDHAT_TENDERLY_VERSION = hardhatTenderlyVersion; hre.tenderly = lazyObject(() => new Tenderly(hre)); - const pjson = require("../package.json"); - logger.info("@tenderly/hardhat-tenderly version:", pjson.version); + logger.info("@tenderly/hardhat-tenderly version:", hardhatTenderlyVersion); logger.info("Tenderly running configuration: ", { username: hre.config.tenderly?.username, @@ -40,6 +40,23 @@ export function setup(cfg: { automaticVerifications: boolean } = { automaticVeri privateVerification: hre.config.tenderly?.privateVerification, networkName: hre.network.name, }); + + // check if the ethers and hardhat-tenderly versions are compatible + const versionCompatibilityChecker = new VersionCompatibilityChecker(); + const [areCompatible, ethersVersion] = versionCompatibilityChecker.areEthersAndHardhatTenderlyVersionsCompatible( + hre, + hardhatTenderlyVersion, + ); + if (!areCompatible) { + const compatibleHardhatTenderlyVersion = versionCompatibilityChecker.compatibleHardhatTenderlyVersionForEthersVersion( + ethersVersion, + ); + console.log( + "\x1b[31m%s%s\x1b[0m", + `Wrong '@tenderly/hardhat-tenderly' version '${hardhatTenderlyVersion}' used with ethers version '${ethersVersion}'.\n`, + `Please use the correct version of latest '@tenderly/hardhat-tenderly@${compatibleHardhatTenderlyVersion}' plugin.\n` + ); + } extendProvider(hre); populateNetworks(); diff --git a/packages/hre-extender-v2/src/setup.ts b/packages/hre-extender-v2/src/setup.ts index f58dcf11..7ab6c608 100644 --- a/packages/hre-extender-v2/src/setup.ts +++ b/packages/hre-extender-v2/src/setup.ts @@ -16,7 +16,7 @@ import{ TenderlyNetwork as TenderlyNetworkInterface } from "@tenderly/api-client import { logger } from "./logger"; import { TenderlyService } from"@tenderly/api-client"; -import { Tenderly, TenderlyNetwork } from "@tenderly/hardhat-integration"; +import { Tenderly, TenderlyNetwork, VersionCompatibilityChecker } from "@tenderly/hardhat-integration"; import { extendEthers } from "./extenders/extend-ethers"; import { extendUpgrades } from "./extenders/extend-upgrades"; import { extendHardhatDeploy } from "./extenders/extend-hardhat-deploy"; @@ -33,12 +33,13 @@ export function setup(cfg: { automaticVerifications: boolean } = { automaticVeri extendEnvironment(async (hre: HardhatRuntimeEnvironment) => { process.env.TENDERLY_AUTOMATIC_VERIFICATION = cfg.automaticVerifications ? "true": "false" process.env.AUTOMATIC_VERIFICATION_ENABLED = cfg.automaticVerifications ? "true": "false" - process.env.HARDHAT_TENDERLY_VERSION = require("../package.json").version; + + const hardhatTenderlyVersion = require("../package.json").version; + process.env.HARDHAT_TENDERLY_VERSION = hardhatTenderlyVersion; hre.tenderly = lazyObject(() => new Tenderly(hre)); - const pjson = require("../package.json"); - logger.info("@tenderly/hardhat-tenderly version:", pjson.version); + logger.info("@tenderly/hardhat-tenderly version:", hardhatTenderlyVersion); logger.info("Tenderly running configuration: ", { username: hre.config.tenderly?.username, @@ -47,6 +48,23 @@ export function setup(cfg: { automaticVerifications: boolean } = { automaticVeri privateVerification: hre.config.tenderly?.privateVerification, networkName: hre.network.name, }); + + // check if the ethers and hardhat-tenderly versions are compatible + const versionCompatibilityChecker = new VersionCompatibilityChecker(); + const [areCompatible, ethersVersion] = versionCompatibilityChecker.areEthersAndHardhatTenderlyVersionsCompatible( + hre, + hardhatTenderlyVersion, + ); + if (!areCompatible) { + const compatibleHardhatTenderlyVersion = versionCompatibilityChecker.compatibleHardhatTenderlyVersionForEthersVersion( + ethersVersion, + ); + console.log( + "\x1b[31m%s%s\x1b[0m", + `Wrong '@tenderly/hardhat-tenderly' version '${hardhatTenderlyVersion}' used with ethers version '${ethersVersion}'.\n`, + `Please use the correct version of latest '@tenderly/hardhat-tenderly@${compatibleHardhatTenderlyVersion}' plugin.\n` + ); + } extendProvider(hre); populateNetworks(); diff --git a/packages/tenderly-hardhat/src/VersionCompatibilityChecker.ts b/packages/tenderly-hardhat/src/VersionCompatibilityChecker.ts new file mode 100644 index 00000000..f1fcdfbb --- /dev/null +++ b/packages/tenderly-hardhat/src/VersionCompatibilityChecker.ts @@ -0,0 +1,61 @@ +import { HardhatRuntimeEnvironment } from "hardhat/types"; + +export class VersionCompatibilityChecker { + public areEthersAndHardhatTenderlyVersionsCompatible( + hre: HardhatRuntimeEnvironment, + hardhatTenderlyVersion: string + ): [boolean, string, string] { + // todo: there should be an interface VersionProvider from this tenderly-hardhat package. + // the hre-extender-v1 and hre-extender-v2 packages should implement that interface on their own + // for both EthersVersionProvider and TenderlyVersionProvider. + // I am casting this to `any` just for the sake of time. + // With this approach, this can easily be tested as well. + let ethersVersion = (hre as any).ethers.version as string; + ethersVersion = this._trimVersion(ethersVersion, "ethers/"); + const ethersMajorVersion = this._majorVersion(ethersVersion); + + const hardhatTenderlyMajorVersion = this._majorVersion(hardhatTenderlyVersion); + + return [this._areCompatible(ethersMajorVersion, hardhatTenderlyMajorVersion), ethersVersion, hardhatTenderlyVersion]; + } + + // todo: change this function once we have the available data of which hardhat-tenderly version is the newest. + // Then, + // the function will call the API to get the newest hardhat-tenderly version + // and we can use that to print out the command to upgrade the plugin. + public compatibleHardhatTenderlyVersionForEthersVersion(ethersVersion: string): string { + ethersVersion = this._trimVersion(ethersVersion, "ethers/"); + const ethersMajorVersion = this._majorVersion(ethersVersion); + + if (ethersMajorVersion === 5) { + return "1.x.x."; + } + if (ethersMajorVersion === 6) { + return "2.x.x"; + } + + throw new Error(`Unsupported ethers version: ${ethersVersion}`); + } + + private _trimVersion(version: string, toTrim: string): string { + if (version.startsWith(toTrim)) { + return version.slice(toTrim.length); + } + return version; + } + + private _majorVersion(version: string): number { + return parseInt(version.split(".")[0], 10); + } + + private _areCompatible(ethersMajorVersion: number, hardhatTenderlyMajorVersion: number): boolean { + if (ethersMajorVersion === 5 && hardhatTenderlyMajorVersion === 1) { + return true; + } + if (ethersMajorVersion === 6 && hardhatTenderlyMajorVersion === 2) { + return true; + } + + return false; + } +} diff --git a/packages/tenderly-hardhat/src/index.ts b/packages/tenderly-hardhat/src/index.ts index 0fa3a254..6b8d9d4a 100644 --- a/packages/tenderly-hardhat/src/index.ts +++ b/packages/tenderly-hardhat/src/index.ts @@ -4,6 +4,7 @@ import "./utils/logger"; export { Tenderly } from "./Tenderly"; export { TenderlyNetwork } from "./TenderlyNetwork"; export * from "./type-extensions"; +export { VersionCompatibilityChecker } from "./VersionCompatibilityChecker"; // ProxyPlaceholderName is used for the `name` parameter in the `tenderly.verify` method because the name is actually not important. // Beneath we use `@nomicfoundation/hardhat-verify` task in order to verify the proxy, and it doesn't need a name.