@openzeppelin/hardhat-upgrades
Version:
[](https://docs.openzeppelin.com/upgrades-plugins/hardhat-upgrades) [](https://www.npmjs.org/package/@openzeppelin/h
177 lines • 7.93 kB
JavaScript
import { getChainId, hasCode, isDeploymentCompleted, UpgradesError, } from '@openzeppelin/upgrades-core';
import { fromChainId } from '@openzeppelin/defender-sdk-base-client';
import debug from '../utils/debug.js';
import { promisify } from 'util';
import { getDeployClient, getNetworkClient } from './client.js';
const sleep = promisify(setTimeout);
export function getDefenderApiKey(hre) {
const cfg = hre.config.defender;
if (!cfg || !cfg.apiKey || !cfg.apiSecret) {
const sampleConfig = JSON.stringify({ apiKey: 'YOUR_API_KEY', apiSecret: 'YOUR_API_SECRET' }, null, 2);
throw new Error(`Missing OpenZeppelin Defender API key and secret in hardhat config. Add the following to your hardhat.config.js configuration:\ndefender: ${sampleConfig}\n`);
}
return cfg;
}
export async function getNetwork(hre, connection) {
const { networkConfig, ethers } = connection;
const provider = ethers.provider;
const chainId = networkConfig.chainId ?? (await getChainId(provider));
const networkNames = await getNetworkNames(chainId, hre);
const userConfigNetwork = hre.config.defender?.network;
if (networkNames.length === 0) {
throw new UpgradesError(`The current network with chainId ${chainId} is not supported by OpenZeppelin Defender`, () => `If this is a private or forked network, add it in Defender from the Manage tab.`);
}
else if (networkNames.length === 1) {
const network = networkNames[0];
if (userConfigNetwork !== undefined && network !== userConfigNetwork) {
throw new UpgradesError(`Detected network ${network} does not match specified network: ${userConfigNetwork}`, () => `The current chainId ${chainId} is detected as ${network} on OpenZeppelin Defender, but the hardhat config's 'defender' section specifies network: ${userConfigNetwork}.\nEnsure you are connected to the correct network.`);
}
return network;
}
else {
if (userConfigNetwork === undefined) {
throw new UpgradesError(`Detected multiple networks with the same chainId ${chainId} on OpenZeppelin Defender: ${Array.from(networkNames).join(', ')}`, () => `Specify the network that you want to use in your hardhat config file as follows:\ndefender: { network: 'networkName' }`);
}
else if (!networkNames.includes(userConfigNetwork)) {
throw new UpgradesError(`Specified network ${userConfigNetwork} does not match any of the detected networks for chainId ${chainId}: ${Array.from(networkNames).join(', ')}`, () => `Ensure you are connected to the correct network, or specify one of the detected networks in your hardhat config file.`);
}
return userConfigNetwork;
}
}
async function getNetworkNames(chainId, hre) {
const matchingNetworks = [];
const knownNetwork = fromChainId(chainId);
if (knownNetwork !== undefined) {
matchingNetworks.push(knownNetwork);
}
const networkClient = getNetworkClient(hre);
const forkedNetworks = await networkClient.listForkedNetworks();
for (const network of forkedNetworks) {
if (network.chainId === chainId) {
matchingNetworks.push(network.name);
}
}
const privateNetworks = await networkClient.listPrivateNetworks();
for (const network of privateNetworks) {
if (network.chainId === chainId) {
matchingNetworks.push(network.name);
}
}
return matchingNetworks;
}
export function enableDefender(hre, defenderModule, opts) {
if ((hre.config.defender?.useDefenderDeploy || defenderModule) && opts.useDefenderDeploy === undefined) {
return {
...opts,
useDefenderDeploy: true,
};
}
else {
return opts;
}
}
/**
* Disables Defender for a function that does not support it.
* If opts.useDefenderDeploy or defenderModule is true, throws an error.
* If hre.config.defender.useDefenderDeploy is true, logs a debug message and passes (to allow fallback to Hardhat signer).
*
* @param hre The Hardhat runtime environment
* @param defenderModule Whether the function was called from the defender module
* @param opts The options passed to the function
* @param unsupportedFunction The name of the function that does not support Defender
*/
export function disableDefender(hre, defenderModule, opts, unsupportedFunction) {
if (opts.useDefenderDeploy) {
throw new UpgradesError(`The function ${unsupportedFunction} is not supported with the \`useDefenderDeploy\` option.`);
}
else if (defenderModule) {
throw new UpgradesError(`The function ${unsupportedFunction} is not supported with the \`defender\` module.`, () => `Call the function as upgrades.${unsupportedFunction} to use the Hardhat signer.`);
}
else if (hre.config.defender?.useDefenderDeploy) {
debug(`The function ${unsupportedFunction} is not supported with the \`defender.useDefenderDeploy\` configuration option. Using the Hardhat signer instead.`);
}
}
/**
* Gets the remote deployment response for the given id.
*
* @param hre The Hardhat runtime environment
* @param remoteDeploymentId The deployment id.
* @returns The remote deployment response, or undefined if the deployment is not found.
* @throws Error if the deployment response could not be retrieved.
*/
export async function getRemoteDeployment(hre, remoteDeploymentId) {
const client = getDeployClient(hre);
try {
return (await client.getDeployedContract(remoteDeploymentId));
}
catch (e) {
const message = e.response?.data?.message;
if (message?.match(/deployment with id .* not found\./)) {
return undefined;
}
throw e;
}
}
/**
* Waits indefinitely for the deployment until it is completed or failed.
* Returns the last known transaction hash seen from the remote deployment, or undefined if the remote deployment was not retrieved.
*/
export async function waitForDeployment(hre, opts, address, remoteDeploymentId) {
const pollInterval = opts.pollingInterval ?? 5e3;
let lastKnownTxHash;
const connection = await hre.network.create();
const { ethers } = connection;
// eslint-disable-next-line no-constant-condition
while (true) {
if (await hasCode(ethers.provider, address)) {
debug('code in target address found', address);
break;
}
const response = await getRemoteDeployment(hre, remoteDeploymentId);
lastKnownTxHash = response?.txHash;
const completed = await isDeploymentCompleted(address, remoteDeploymentId, response);
if (completed) {
break;
}
else {
await sleep(pollInterval);
}
}
return lastKnownTxHash;
}
export function parseTxOverrides(overrides) {
if (!overrides) {
return undefined;
}
if (typeof overrides.gasLimit === 'bigint' ||
typeof overrides.gasPrice === 'bigint' ||
typeof overrides.maxFeePerGas === 'bigint' ||
typeof overrides.maxPriorityFeePerGas === 'bigint') {
throw new Error('bigint not yet supported');
}
return {
gasLimit: parseNumberOrUndefined(overrides.gasLimit),
gasPrice: parseHexOrUndefined(overrides.gasPrice),
maxFeePerGas: parseHexOrUndefined(overrides.maxFeePerGas),
maxPriorityFeePerGas: parseHexOrUndefined(overrides.maxPriorityFeePerGas),
};
}
function parseHexOrUndefined(value) {
if (typeof value === 'string' && !!value) {
return value;
}
if (typeof value === 'number') {
return `0x${value.toString(16)}`;
}
return undefined;
}
function parseNumberOrUndefined(value) {
if (typeof value === 'string' && !!value) {
return Number(value);
}
if (typeof value === 'number') {
return value;
}
return undefined;
}
//# sourceMappingURL=utils.js.map