hardhat-deploy
Version:
Hardhat plugin for replicable smart contract deployments and easy testing across multiple EVM chains, with support for proxies, diamonds, named accounts, and deployment fixtures
258 lines • 9.55 kB
JavaScript
import { configVariable } from 'hardhat/config';
import { enhanceEnvIfNeeded } from 'rocketh';
import { loadEnvironmentFromFiles, chainByCanonicalName } from '@rocketh/node';
export function setupHardhatDeploy(extensions) {
async function loadEnvironmentFromHardhatWithExtensions(required) {
const env = await loadEnvironmentFromHardhat(required);
return enhanceEnvIfNeeded(env, extensions);
}
return {
loadEnvironmentFromHardhat: loadEnvironmentFromHardhatWithExtensions,
};
}
export async function generateForkConfig(params) {
const fork = process.env.HARDHAT_FORK;
const connection = params.connection || fork
? await params.hre.network.connect({ network: 'fork' })
: await params.hre.network.connect();
let provider = connection.provider;
let environment = connection.networkName;
let forkChainId;
if (fork) {
// if (options?.useChainIdOfForkedNetwork) {
// const forkNetworkConfig = params.hre.config.networks[fork];
// if (forkNetworkConfig.type === 'edr-simulated') {
// forkChainId = forkNetworkConfig.chainId;
// } else if (forkNetworkConfig.chainId) {
// forkChainId = forkNetworkConfig.chainId;
// } else {
// if ('url' in forkNetworkConfig) {
// const url = await forkNetworkConfig.url.getUrl();
// const response = await fetch(url, {
// method: 'POST',
// headers: {
// 'Content-Type': 'application/json',
// },
// body: JSON.stringify({
// jsonrpc: '2.0',
// id: 1,
// method: 'eth_chainId',
// params: [],
// }),
// });
// const json = (await response.json()) as {result: string};
// forkChainId = Number(json.result);
// } else {
// throw new Error(`cannot fetch chainId`);
// }
// }
// }
environment = {
fork,
};
}
if (forkChainId) {
const originalProvider = provider;
const chainId = forkChainId;
async function request(args) {
if (args.method === 'eth_chainId') {
return `0x${chainId.toString(16)}`;
}
return originalProvider.request(args);
}
provider = new Proxy(originalProvider, {
get: function (target, property, receiver) {
switch (property) {
case 'request':
return request;
default:
return originalProvider[property];
}
},
});
}
return { provider, environment, connection, isFork: !!fork };
}
export async function loadEnvironmentFromHardhat(params) {
const { connection, environment, provider, isFork } = await generateForkConfig(params);
const isEDR = connection.networkConfig.type === 'edr-simulated' ? true : undefined;
// console.log(`loading environments...`);
return loadEnvironmentFromFiles({
provider,
environment,
extra: {
connection,
},
saveDeployments: isFork ? false : undefined,
autoImpersonate: isEDR,
});
}
function getVariable(prefix, name) {
// We transform dash into underscore as dash are not supported everywhere in env var names
const variableName = (prefix + name).replaceAll('-', '_');
let uri = process.env[variableName];
if (uri === 'SECRET') {
return configVariable(`SECRET_${variableName}`);
}
else if (uri?.startsWith('SECRET:')) {
const splitted = uri.split(':');
if (splitted.length !== 2) {
throw new Error(`invalid secret uri ${uri}`);
}
return configVariable(`SECRET_${prefix + splitted[1]}`);
}
return uri;
}
export function getRPC(networkName) {
let uri = getVariable('ETH_NODE_URI_', networkName);
if (uri && uri !== '') {
return uri;
}
uri = process.env.ETH_NODE_URI;
if (uri) {
uri = uri.replace('{{networkName}}', networkName);
}
if (!uri || uri === '') {
if (networkName === 'localhost') {
return 'http://localhost:8545';
}
return uri;
// throw new Error(`no uri specified or for network ${networkName}`);
}
if (uri.indexOf('{{') >= 0) {
throw new Error(`invalid uri or network not supported by node provider : ${uri}`);
}
return uri;
}
export function getMnemonic(networkName, doNotDefault) {
if (networkName) {
const mnemonic = getVariable('MNEMONIC_', networkName);
if (mnemonic && mnemonic !== '') {
return mnemonic;
}
}
const mnemonic = process.env.MNEMONIC;
if (!mnemonic || mnemonic === '') {
if (doNotDefault) {
return undefined;
}
return 'test test test test test test test test test test test junk';
}
return mnemonic;
}
export function getAccounts(networkName, doNotDefault) {
const mnemonic = getMnemonic(networkName, doNotDefault);
if (!mnemonic) {
return undefined;
}
return { mnemonic };
}
export function addNetworksFromEnv(networks) {
const newNetworks = networks ? { ...networks } : {};
const allEnv = Object.keys(process.env);
for (const envKey of allEnv) {
if (envKey.startsWith(`ETH_NODE_URI_`) && envKey.length > `ETH_NODE_URI_`.length) {
const networkName = envKey.slice(`ETH_NODE_URI_`.length);
const url = getRPC(networkName);
if (!newNetworks[networkName]) {
if (url) {
newNetworks[networkName] = {
type: 'http',
url,
accounts: getAccounts(networkName),
};
}
else {
// TODO ?
// console.error(`no url for network ${networkName}`);
}
}
else {
console.error(`duplicated network ${networkName}`);
}
}
}
return newNetworks;
}
const listOfNetworkNamesWithTestAccountAllowed = ['hardhat', 'localhost', 'memory', 'test'];
export function addNetworksFromKnownList(networks) {
const newNetworks = networks ? { ...networks } : {};
const canonicalNames = Object.keys(chainByCanonicalName);
for (const canonicalName of canonicalNames) {
const chain = chainByCanonicalName[canonicalName];
const url = getRPC(canonicalName) || chain.rpcUrls.default.http[0];
if (!newNetworks[canonicalName]) {
if (url) {
newNetworks[canonicalName] = {
type: 'http',
url,
accounts: getAccounts(canonicalName, !listOfNetworkNamesWithTestAccountAllowed.includes(canonicalName)),
chainType: chain.chainType === 'op-stack' ? 'op' : undefined,
chainId: chain.id,
};
}
else {
console.error(`no url for chain ${canonicalName}`);
}
}
else {
// console.error(`duplicated chain ${canonicalName}`);
}
}
return newNetworks;
}
export function addForkConfiguration(networks) {
const currentNetworkName = process.env.HARDHAT_FORK;
let forkURL;
let hardhatAccounts;
if (currentNetworkName &&
currentNetworkName !== 'hardhat' &&
currentNetworkName !== 'edr' &&
currentNetworkName !== 'edr-simulated' &&
currentNetworkName !== 'memory') {
const currentNetwork = networks[currentNetworkName];
if (currentNetwork) {
if (currentNetwork.type === 'http') {
forkURL = currentNetwork.url;
if (currentNetwork.accounts &&
typeof currentNetwork.accounts === 'object' &&
'mnemonic' in currentNetwork.accounts) {
hardhatAccounts = currentNetwork.accounts;
}
// else if (currentNetwork.accounts && Array.isArray(currentNetwork.accounts)) {
// hardhatAccounts = currentNetwork.accounts.map((v) => ({
// balance: '10000000000000000000000',
// privateKey: v,
// }));
// }
}
}
if (!forkURL) {
const errorMessage = `no rpc URL found for ${currentNetworkName}, cannot fork`;
throw new Error(errorMessage);
// console.error(errorMessage);
}
const existingForkConfiguration = networks.fork && networks.fork.type === 'edr-simulated'
? networks.fork
: { type: 'edr-simulated', chainType: 'l1' };
const forkNetwork = {
...existingForkConfiguration,
...{
accounts: hardhatAccounts || existingForkConfiguration?.accounts,
forking: forkURL
? {
url: forkURL,
blockNumber: process.env.HARDHAT_FORK_NUMBER ? parseInt(process.env.HARDHAT_FORK_NUMBER) : undefined,
}
: undefined,
},
};
const newNetworks = {
...networks,
fork: forkNetwork,
};
return newNetworks;
}
return networks;
}
//# sourceMappingURL=helpers.js.map