@tenderly/hardhat-integration
Version:
Hardhat plugin for integration with Tenderly
335 lines • 18.3 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Tenderly = void 0;
const path_1 = require("path");
const fs = __importStar(require("fs-extra"));
const plugins_1 = require("hardhat/plugins");
const api_client_1 = require("@tenderly/api-client");
const constants_1 = require("@tenderly/api-client/common/constants");
const logger_1 = require("./utils/logger");
const errors_1 = require("./tenderly/errors");
const util_1 = require("./utils/util");
const constants_2 = require("./constants");
const TenderlyNetwork_1 = require("./TenderlyNetwork");
const index_1 = require("./index");
const verification_1 = require("./verification");
const errors_2 = require("./errors");
const utils_1 = require("./utils");
class Tenderly {
constructor(hre) {
this.tenderlyService = new api_client_1.TenderlyService(constants_2.PLUGIN_NAME);
logger_1.logger.debug("Creating Tenderly plugin.");
this.env = hre;
this.tenderlyNetwork = new TenderlyNetwork_1.TenderlyNetwork(hre);
this.verificationService = new verification_1.VerificationService(this.tenderlyService, this.tenderlyNetwork);
logger_1.logger.debug("Created Tenderly plugin.");
}
async verify(...contracts) {
logger_1.logger.info("Verification invoked.");
if (await this._isZkSyncNetwork(this.env)) {
for (let contract of contracts) {
contract = contract;
this.verificationService.verifyContractABI(this.env, contract.address, contract.name);
}
return;
}
// If there are proxy contracts, we can run the task without further processing.
const proxyContracts = contracts.filter((contract) => contract.name === index_1.ProxyPlaceholderName);
for (const proxyContract of proxyContracts) {
await this.env.run("verify:verify", {
address: proxyContract.address,
constructorArguments: [],
});
}
const flatContracts = contracts.reduce((accumulator, value) => {
if (value.name !== index_1.ProxyPlaceholderName) {
accumulator = accumulator.concat(value);
}
return accumulator;
}, []);
if (flatContracts === undefined ||
flatContracts === null ||
flatContracts.length === 0) {
return;
}
const verificationType = await (0, utils_1.getVerificationType)(this.env, this.tenderlyNetwork);
const platformID = verificationType === api_client_1.VERIFICATION_TYPES.FORK
? this.tenderlyNetwork.forkID
: this.tenderlyNetwork.devnetID;
const requestData = await (0, util_1.makeVerifyContractsRequest)(this.env, flatContracts, platformID);
if (requestData === null) {
logger_1.logger.error("Verification failed due to bad processing of the data in /artifacts directory.");
return;
}
if (await (0, utils_1.isVerificationOnVnet)(verificationType)) {
logger_1.logger.info(`Network parameter is set to '${this.getNetworkName()}', redirecting to ${verificationType} verification.`);
await (0, errors_2.throwIfUsernameOrProjectNotSet)(this.env);
return this.tenderlyNetwork.verify(requestData);
}
if (verificationType === api_client_1.VERIFICATION_TYPES.PRIVATE) {
logger_1.logger.info("Private verification flag is set to true, redirecting to private verification.");
await (0, errors_2.throwIfUsernameOrProjectNotSet)(this.env);
return this.tenderlyService.pushContractsMultiCompiler(requestData, this.env.config.tenderly.project, this.env.config.tenderly.username);
}
logger_1.logger.info("Publicly verifying contracts.");
return this.tenderlyService.verifyContractsMultiCompiler(requestData);
}
async verifyMultiCompilerAPI(request) {
logger_1.logger.info("Invoked verification (multi compiler version) through API.");
logger_1.logger.trace("Request data:", request);
const verificationType = await (0, utils_1.getVerificationType)(this.env, this.tenderlyNetwork);
switch (verificationType) {
case api_client_1.VERIFICATION_TYPES.FORK:
logger_1.logger.error(`Error in ${constants_2.PLUGIN_NAME}: Network parameter is set to 'tenderly' and verifyMultiCompilerAPI() is not available for fork deployments, please use verifyForkAPI().`);
break;
case api_client_1.VERIFICATION_TYPES.DEVNET:
logger_1.logger.error(`Error in ${constants_2.PLUGIN_NAME}: Network parameter is set to '${this.getNetworkName()}' and verifyMultiCompilerAPI() is not available for devnet deployments`);
break;
case api_client_1.VERIFICATION_TYPES.PRIVATE:
if (this.env.config.tenderly?.project === undefined) {
logger_1.logger.error(`Error in ${constants_2.PLUGIN_NAME}: Please provide the project field in the tenderly object in hardhat.config.js`);
return;
}
if (this.env.config.tenderly?.username === undefined) {
logger_1.logger.error(`Error in ${constants_2.PLUGIN_NAME}: Please provide the username field in the tenderly object in hardhat.config.js`);
return;
}
logger_1.logger.info("Private verification flag is set to true, redirecting to private verification.");
await this.tenderlyService.pushContractsMultiCompiler(request, this.env.config.tenderly.project, this.env.config.tenderly.username);
break;
case api_client_1.VERIFICATION_TYPES.PUBLIC:
logger_1.logger.info("Publicly verifying contracts.");
await this.tenderlyService.verifyContractsMultiCompiler(request);
break;
}
}
async _isZkSyncNetwork(hre) {
let chainId;
try {
chainId = await (0, util_1.getChainId)(hre);
}
catch (e) {
if (e instanceof errors_2.UndefinedChainIdError) { }
else
throw e;
}
return (chainId === 300 || chainId === 324 || chainId === 37111);
}
async verifyForkMultiCompilerAPI(request, tenderlyProject, username, forkID) {
logger_1.logger.info("Invoked fork verification through API request. (Multi compiler version)");
if (!(0, util_1.isTenderlyNetworkConfig)(this.env.network.config)) {
logger_1.logger.error(`Error in ${constants_2.PLUGIN_NAME}: Network parameter is not set to 'tenderly' and verifyForkAPI() is only available for tenderly fork deployments, please use --network tenderly.`);
return;
}
await (0, errors_2.throwIfUsernameOrProjectNotSet)(this.env);
await this.tenderlyNetwork.verifyMultiCompilerAPI(request, tenderlyProject, username, forkID);
}
async verifyDevnetMultiCompilerAPI(request, tenderlyProject, username, devnetID) {
logger_1.logger.info("Invoked devnet verification through API request. (Multi compiler version)");
if (!(0, util_1.isTenderlyNetworkConfig)(this.env.network.config)) {
logger_1.logger.error(`Error in ${constants_2.PLUGIN_NAME}: Network parameter is not set to 'devnet' and verifyDevnetMultiCompilerAPI() is only available for tenderly devnet deployments, please use --network devnet.`);
return;
}
await (0, errors_2.throwIfUsernameOrProjectNotSet)(this.env);
await this.tenderlyNetwork.verifyDevnetMultiCompilerAPI(request, tenderlyProject, username, devnetID);
}
network() {
return this.tenderlyNetwork;
}
getNetworkName() {
return this.env.network.name;
}
setNetwork(network) {
this.tenderlyNetwork = network;
logger_1.logger.trace("Network is set to 'tenderly'.", network);
return this.tenderlyNetwork;
}
async push(...contracts) {
return this.verify(...contracts);
}
async verifyAPI(request) {
logger_1.logger.info("Invoked public verification through API request.");
const verificationType = await (0, utils_1.getVerificationType)(this.env, this.tenderlyNetwork);
if ((0, util_1.isTenderlyNetworkConfig)(this.env.network.config)) {
if (verificationType === api_client_1.VERIFICATION_TYPES.DEVNET) {
logger_1.logger.error(`Error in ${constants_2.PLUGIN_NAME}: Network parameter is set to '${this.getNetworkName()}' and verifyAPI() is not available for devnet deployments.`);
return;
}
logger_1.logger.error(`Error in ${constants_2.PLUGIN_NAME}: Network parameter is set to 'tenderly' and verifyAPI() is not available for fork deployments, please use verifyForkAPI().`);
return;
}
await this.tenderlyService.verifyContracts(request);
}
async verifyForkAPI(request, tenderlyProject, username, forkID) {
logger_1.logger.info("Invoked fork verification through API request.");
if (this.env.network.name !== "tenderly") {
logger_1.logger.error(`Error in ${constants_2.PLUGIN_NAME}: Network parameter is not set to 'tenderly' and verifyForkAPI() is only available for tenderly fork deployments, please use --network tenderly.`);
return;
}
await this.tenderlyNetwork.verifyAPI(request, tenderlyProject, username, forkID);
}
async pushAPI(request, tenderlyProject, username) {
logger_1.logger.info("Invoked pushing contracts through API.");
const verificationType = await (0, utils_1.getVerificationType)(this.env, this.tenderlyNetwork);
if ((0, util_1.isTenderlyNetworkConfig)(this.env.network.config)) {
if (verificationType === api_client_1.VERIFICATION_TYPES.DEVNET) {
logger_1.logger.error(`Error in ${constants_2.PLUGIN_NAME}: Network parameter is set to '${this.getNetworkName()}' and pushAPI() is not available for devnet deployments.`);
return;
}
logger_1.logger.error(`Error in ${constants_2.PLUGIN_NAME}: Network parameter is set to 'tenderly' and pushAPI() is not available for fork deployments, please use verifyForkAPI().`);
return;
}
await this.tenderlyService.pushContracts(request, tenderlyProject, username);
}
async persistArtifacts(...contracts) {
logger_1.logger.info("Invoked persisting artifacts.");
if (contracts.length === 0) {
logger_1.logger.error("No contracts were provided during artifact persisting.");
return;
}
const sourcePaths = await this.env.run("compile:solidity:get-source-paths");
const sourceNames = await this.env.run("compile:solidity:get-source-names", { sourcePaths });
const data = await this.env.run("compile:solidity:get-dependency-graph", {
sourceNames,
});
if (data.length === 0) {
throw new plugins_1.HardhatPluginError(constants_2.PLUGIN_NAME, errors_1.CONTRACTS_NOT_DETECTED);
}
let contract;
data._resolvedFiles.forEach((resolvedFile, _) => {
const sourcePath = resolvedFile.sourceName;
logger_1.logger.trace("Currently processing file:", sourcePath);
const name = sourcePath.split("/").slice(-1)[0].split(".")[0];
logger_1.logger.trace("Obtained name from source file:", name);
for (contract of contracts) {
if (contract.name === name) {
logger_1.logger.trace("Found contract:", contract.name);
const network = this.env.hardhatArguments.network !== "hardhat"
? this.env.hardhatArguments.network ?? contract.network
: contract.network;
if (network === undefined) {
console.log(`Error in ${constants_2.PLUGIN_NAME}: Please provide a network via the hardhat --network argument or directly in the contract`);
continue;
}
let chainID = constants_1.NETWORK_NAME_CHAIN_ID_MAP[network.toLowerCase()];
if (this.env.config.networks[network].chainId !== undefined) {
chainID = this.env.config.networks[network].chainId.toString();
}
if (chainID === undefined) {
chainID = constants_2.DEFAULT_CHAIN_ID;
}
const deploymentsFolder = this.env.config?.tenderly?.deploymentsDir ?? "deployments";
const destPath = `${deploymentsFolder}${path_1.sep}${network.toLowerCase()}_${chainID}${path_1.sep}`;
const contractDataPath = `${this.env.config.paths.artifacts}${path_1.sep}${sourcePath}${path_1.sep}${name}.json`;
const contractData = JSON.parse(fs.readFileSync(contractDataPath).toString());
const metadata = {
defaultCompiler: {
version: (0, util_1.extractCompilerVersion)(this.env.config, sourcePath),
},
sources: {
[sourcePath]: {
content: resolvedFile.content.rawContent,
versionPragma: resolvedFile.content.versionPragmas[0],
},
},
};
const visited = {};
(0, util_1.resolveDependencies)(data, sourcePath, metadata, visited);
const artifact = {
metadata: JSON.stringify(metadata),
address: contract.address,
bytecode: contractData.bytecode,
deployedBytecode: contractData.deployedBytecode,
abi: contractData.abi,
};
logger_1.logger.trace("Processed artifact: ", artifact);
fs.outputFileSync(`${destPath}${name}.json`, JSON.stringify(artifact));
}
}
});
}
async _filterContracts(flatContracts) {
logger_1.logger.info("Processing data needed for verification.");
let contract;
let requestData;
try {
requestData = await this._getContractData(flatContracts);
logger_1.logger.silly("Processed request data:", requestData);
}
catch (e) {
logger_1.logger.error("Error caught while trying to process contracts by name: ", e);
return null;
}
for (contract of flatContracts) {
const network = this.env.hardhatArguments.network !== "hardhat"
? this.env.hardhatArguments.network ?? contract.network
: contract.network;
if (network === undefined) {
logger_1.logger.error(`Error in ${constants_2.PLUGIN_NAME}: Please provide a network via the hardhat --network argument or directly in the contract`);
return null;
}
logger_1.logger.trace("Found network is:", network);
const index = requestData.contracts.findIndex((requestContract) => requestContract.contractName === contract.name);
if (index === -1) {
logger_1.logger.error(`Contract '${contract.name}' was not found among the contracts in /artifacts.`);
continue;
}
let chainID = constants_1.NETWORK_NAME_CHAIN_ID_MAP[network.toLowerCase()];
if (this.env.config.networks[network].chainId !== undefined) {
chainID = this.env.config.networks[network].chainId.toString();
}
logger_1.logger.trace(`ChainID for network '${network}' is ${chainID}`);
if (chainID === undefined) {
logger_1.logger.error(`Error in ${constants_2.PLUGIN_NAME}: Couldn't identify network. Please provide a chainId in the network config object`);
return null;
}
requestData.contracts[index].networks = {
[chainID]: {
address: contract.address,
links: contract.libraries,
},
};
}
logger_1.logger.debug("Processed request data from _filterContracts:", requestData);
return requestData;
}
async _getContractData(flatContracts) {
const contracts = await (0, util_1.getContracts)(this.env, flatContracts);
const config = (0, util_1.getCompilerDataFromContracts)(contracts, flatContracts, this.env.config);
if (config === undefined) {
logger_1.logger.error(errors_1.NO_COMPILER_FOUND_FOR_CONTRACT_ERR_MSG);
}
return {
contracts,
config: config,
};
}
_existsProxyVerification(contracts) {
return contracts.some((contract) => contract.name === index_1.ProxyPlaceholderName);
}
}
exports.Tenderly = Tenderly;
//# sourceMappingURL=Tenderly.js.map