hardhat-deploy
Version:
Hardhat Plugin For Replicable Deployments And Tests
552 lines • 23.8 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;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.recode = exports.mergeABIs = exports.filterABI = exports.getDeployPaths = exports.getNetworkName = exports.traverse = exports.traverseMultipleDirectory = exports.processNamedAccounts = exports.addDeployments = exports.deleteDeployments = exports.loadAllDeployments = exports.getExtendedArtifactFromFolders = exports.getArtifactFromFolders = void 0;
/* eslint-disable @typescript-eslint/no-explicit-any */
const fs = __importStar(require("fs-extra"));
const path = __importStar(require("path"));
const wallet_1 = require("@ethersproject/wallet");
const address_1 = require("@ethersproject/address");
const abi_1 = require("@ethersproject/abi");
const bignumber_1 = require("@ethersproject/bignumber");
const artifacts_1 = require("hardhat/internal/artifacts");
const murmur_128_1 = __importDefault(require("murmur-128"));
const globalStore_1 = require("./globalStore");
const errors_list_1 = require("hardhat/internal/core/errors-list");
function getOldArtifactSync(name, folderPath) {
const oldArtifactPath = path.join(folderPath, name + '.json');
let artifact;
if (fs.existsSync(oldArtifactPath)) {
try {
artifact = JSON.parse(fs.readFileSync(oldArtifactPath).toString());
}
catch (e) {
console.error(e);
}
}
return artifact;
}
async function getArtifactFromFolders(name, folderPaths) {
for (const onepath of folderPaths) {
const artifacts = new artifacts_1.Artifacts(onepath);
let artifact = getOldArtifactSync(name, onepath);
if (!artifact) {
try {
artifact = artifacts.readArtifactSync(name);
}
catch (e) {
const hardhatError = e;
if (hardhatError.number &&
hardhatError.number == errors_list_1.ERRORS.ARTIFACTS.MULTIPLE_FOUND.number) {
throw e;
}
}
}
if (artifact) {
return artifact;
}
}
}
exports.getArtifactFromFolders = getArtifactFromFolders;
// TODO
// const solcInputMetadataCache: Record<string,
// const buildInfoCache
// const hashCache: Record<string, string> = {};
async function getExtendedArtifactFromFolders(name, folderPaths) {
for (const folderPath of folderPaths) {
const artifacts = new artifacts_1.Artifacts(folderPath);
let artifact = getOldArtifactSync(name, folderPath);
if (!artifact &&
(await artifacts.artifactExists(name).catch(() => false))) {
const hardhatArtifact = await artifacts.readArtifact(name);
// check if name is already a fullyQualifiedName
let fullyQualifiedName = name;
let contractName = name;
if (!fullyQualifiedName.includes(':')) {
fullyQualifiedName = `${hardhatArtifact.sourceName}:${name}`;
}
else {
contractName = fullyQualifiedName.split(':')[1];
}
// TODO try catch ? in case debug file is missing
const buildInfo = await artifacts.getBuildInfo(fullyQualifiedName);
if (buildInfo) {
const solcInput = JSON.stringify(buildInfo.input, null, ' ');
const solcInputHash = Buffer.from((0, murmur_128_1.default)(solcInput)).toString('hex');
artifact = Object.assign(Object.assign(Object.assign({}, hardhatArtifact), buildInfo.output.contracts[hardhatArtifact.sourceName][contractName]), { solcInput,
solcInputHash });
}
else {
artifact = Object.assign({}, hardhatArtifact);
}
}
if (artifact) {
return artifact;
}
}
}
exports.getExtendedArtifactFromFolders = getExtendedArtifactFromFolders;
function loadAllDeployments(hre, deploymentsPath, onlyABIAndAddress, externalDeployments) {
const networksFound = {};
const all = {}; // TODO any is chainConfig
fs.readdirSync(deploymentsPath).forEach((fileName) => {
const fPath = path.resolve(deploymentsPath, fileName);
const stats = fs.statSync(fPath);
if (stats.isDirectory()) {
let chainIdFound;
const chainIdFilepath = path.join(fPath, '.chainId');
if (fs.existsSync(chainIdFilepath)) {
chainIdFound = fs.readFileSync(chainIdFilepath).toString().trim();
}
else {
throw new Error(`with hardhat-deploy >= 0.6 you need to rename network folder without appended chainId
You also need to create a '.chainId' file in the folder with the chainId`);
}
if (!all[chainIdFound]) {
all[chainIdFound] = [];
}
const contracts = loadDeployments(deploymentsPath, fileName, onlyABIAndAddress);
const network = {
name: fileName,
chainId: chainIdFound,
contracts,
};
networksFound[fileName] = network;
all[chainIdFound].push(network);
}
});
if (externalDeployments) {
for (const networkName of Object.keys(externalDeployments)) {
for (const folderPath of externalDeployments[networkName]) {
const networkConfig = hre.config.networks[networkName];
if (networkConfig && networkConfig.chainId) {
const networkChainId = networkConfig.chainId.toString();
const contracts = loadDeployments(folderPath, '', onlyABIAndAddress, undefined, networkChainId);
const networkExist = networksFound[networkName];
if (networkExist) {
if (networkChainId !== networkExist.chainId) {
throw new Error(`mismatch between external deployment network ${networkName} chainId: ${networkChainId} vs existing chainId: ${networkExist.chainId}`);
}
networkExist.contracts = Object.assign(Object.assign({}, contracts), networkExist.contracts);
}
else {
const network = {
name: networkName,
chainId: networkChainId,
contracts,
};
networksFound[networkName] = network;
all[networkChainId].push(network);
}
}
else {
console.warn(`export-all limitation: attempting to load external deployments from ${folderPath} without chainId info. Please set the chainId in the network config for ${networkName}`);
}
}
}
}
return all;
}
exports.loadAllDeployments = loadAllDeployments;
function deleteDeployments(deploymentsPath, subPath) {
const deployPath = path.join(deploymentsPath, subPath);
fs.removeSync(deployPath);
}
exports.deleteDeployments = deleteDeployments;
function loadDeployments(deploymentsPath, subPath, onlyABIAndAddress, expectedChainId, truffleChainId) {
const deploymentsFound = {};
const deployPath = path.join(deploymentsPath, subPath);
let filesStats;
try {
filesStats = (0, exports.traverse)(deployPath, undefined, undefined, (name) => !name.startsWith('.') && name !== 'solcInputs');
}
catch (e) {
// console.log('no folder at ' + deployPath);
return {};
}
if (filesStats.length > 0) {
if (expectedChainId) {
const chainIdFilepath = path.join(deployPath, '.chainId');
if (fs.existsSync(chainIdFilepath)) {
const chainIdFound = fs.readFileSync(chainIdFilepath).toString().trim();
if (expectedChainId !== chainIdFound) {
throw new Error(`Loading deployment in folder '${deployPath}' (with chainId: ${chainIdFound}) for a different chainId (${expectedChainId})`);
}
}
else {
throw new Error(`with hardhat-deploy >= 0.6 you are expected to create a '.chainId' file in the deployment folder`);
}
}
}
let fileNames = filesStats.map((a) => a.relativePath);
fileNames = fileNames.sort((a, b) => {
if (a < b) {
return -1;
}
if (a > b) {
return 1;
}
return 0;
});
for (const fileName of fileNames) {
if (fileName.substr(fileName.length - 5) === '.json') {
const deploymentFileName = path.join(deployPath, fileName);
let deployment = JSON.parse(fs.readFileSync(deploymentFileName).toString());
if (!deployment.address && deployment.networks) {
if (truffleChainId && deployment.networks[truffleChainId]) {
// TRUFFLE support
const truffleDeployment = deployment; // TruffleDeployment;
deployment.address =
truffleDeployment.networks[truffleChainId].address;
deployment.transactionHash =
truffleDeployment.networks[truffleChainId].transactionHash;
}
}
if (onlyABIAndAddress) {
deployment = {
address: deployment.address,
abi: deployment.abi,
linkedData: deployment.linkedData,
};
}
const name = fileName.slice(0, fileName.length - 5);
// console.log('fetching ' + deploymentFileName + ' for ' + name);
deploymentsFound[name] = deployment;
}
}
return deploymentsFound;
}
function addDeployments(
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
db, deploymentsPath, subPath, expectedChainId, truffleChainId) {
const contracts = loadDeployments(deploymentsPath, subPath, false, expectedChainId, truffleChainId);
for (const key of Object.keys(contracts)) {
db.deployments[key] = contracts[key];
// TODO ABIS
// db.abis[contracts[key].address] = mergeABI(
// db.abis[contracts[key].address],
// contracts[key].abi
// );
}
}
exports.addDeployments = addDeployments;
function transformNamedAccounts(configNamedAccounts, chainIdGiven, accounts, networkConfigName) {
const addressesToProtocol = {};
const unknownAccountsDict = {};
const knownAccountsDict = {};
for (const account of accounts) {
knownAccountsDict[account.toLowerCase()] = true;
}
const namedAccounts = {};
const usedAccounts = {};
// TODO transform into checksum address
if (configNamedAccounts) {
const accountNames = Object.keys(configNamedAccounts);
// eslint-disable-next-line no-inner-declarations
function parseSpec(spec) {
let address;
switch (typeof spec) {
case 'string':
// eslint-disable-next-line no-case-declarations
const protocolSplit = spec.split('://');
if (protocolSplit.length > 1) {
if (protocolSplit[0].toLowerCase() === 'external') {
address = protocolSplit[1];
addressesToProtocol[address.toLowerCase()] =
protocolSplit[0].toLowerCase();
// knownAccountsDict[address.toLowerCase()] = true; // TODO ? this would prevent auto impersonation in fork/test
}
else if (protocolSplit[0].toLowerCase() === 'trezor') {
address = protocolSplit[1];
addressesToProtocol[address.toLowerCase()] =
protocolSplit[0].toLowerCase();
}
else if (protocolSplit[0].toLowerCase() === 'ledger') {
const addressSplit = protocolSplit[1].split(':');
if (addressSplit.length > 1) {
address = addressSplit[1];
addressesToProtocol[address.toLowerCase()] = `ledger://${addressSplit[0]}`;
}
else {
address = protocolSplit[1];
addressesToProtocol[address.toLowerCase()] =
"ledger://m/44'/60'/0'/0/0";
}
// knownAccountsDict[address.toLowerCase()] = true; // TODO ? this would prevent auto impersonation in fork/test
}
else if (protocolSplit[0].toLowerCase() === 'privatekey') {
address = new wallet_1.Wallet(protocolSplit[1]).address;
addressesToProtocol[address.toLowerCase()] =
'privatekey://' + protocolSplit[1];
}
else {
throw new Error(`unsupported protocol ${protocolSplit[0]}:// for named accounts`);
}
}
else {
if (spec.slice(0, 2).toLowerCase() === '0x') {
if (!(0, address_1.isAddress)(spec)) {
throw new Error(`"${spec}" is not a valid address, if you used to put privateKey there, use the "privatekey://" prefix instead`);
}
address = spec;
}
else {
address = parseSpec(configNamedAccounts[spec]);
}
}
break;
case 'number':
if (accounts) {
address = accounts[spec];
}
break;
case 'undefined':
break;
case 'object':
if (spec) {
if (spec.type === 'object') {
address = spec;
}
else {
const newSpec = chainConfig(spec, chainIdGiven, networkConfigName);
if (typeof newSpec !== 'undefined') {
address = parseSpec(newSpec);
}
}
}
break;
}
if (address) {
if (typeof address === 'string') {
address = (0, address_1.getAddress)(address);
}
}
return address;
}
for (const accountName of accountNames) {
const spec = configNamedAccounts[accountName];
const address = parseSpec(spec);
if (address) {
namedAccounts[accountName] = address;
usedAccounts[address.toLowerCase()] = true;
if (!knownAccountsDict[address.toLowerCase()]) {
unknownAccountsDict[address.toLowerCase()] = true;
}
}
}
}
const unnamedAccounts = [];
for (const address of accounts) {
if (!usedAccounts[address.toLowerCase()]) {
unnamedAccounts.push((0, address_1.getAddress)(address));
}
}
return {
namedAccounts,
unnamedAccounts,
unknownAccounts: Object.keys(unknownAccountsDict).map(address_1.getAddress),
addressesToProtocol,
};
}
function chainConfig(object, chainIdGiven, networkConfigName) {
// TODO utility function:
let chainIdDecimal;
if (typeof chainIdGiven === 'number') {
chainIdDecimal = '' + chainIdGiven;
}
else {
if (chainIdGiven.startsWith('0x')) {
chainIdDecimal = '' + parseInt(chainIdGiven.slice(2), 16);
}
else {
chainIdDecimal = chainIdGiven;
}
}
if (typeof object[networkConfigName] !== 'undefined') {
return object[networkConfigName];
}
else if (typeof object[chainIdGiven] !== 'undefined') {
return object[chainIdGiven];
}
else if (typeof object[chainIdDecimal] !== 'undefined') {
return object[chainIdDecimal];
}
else {
return object.default;
}
}
function processNamedAccounts(network, namedAccounts, accounts, chainIdGiven) {
if (namedAccounts) {
return transformNamedAccounts(namedAccounts, chainIdGiven, accounts, process.env.HARDHAT_DEPLOY_ACCOUNTS_NETWORK || getNetworkName(network));
}
else {
return {
namedAccounts: {},
unnamedAccounts: accounts,
unknownAccounts: [],
addressesToProtocol: {},
};
}
}
exports.processNamedAccounts = processNamedAccounts;
function traverseMultipleDirectory(dirs) {
const filepaths = [];
for (const dir of dirs) {
let filesStats = (0, exports.traverse)(dir);
filesStats = filesStats.filter((v) => !v.directory);
for (const filestat of filesStats) {
filepaths.push(path.join(dir, filestat.relativePath));
}
}
return filepaths;
}
exports.traverseMultipleDirectory = traverseMultipleDirectory;
const traverse = function (dir, result = [], topDir, filter // TODO any is Stats
) {
fs.readdirSync(dir).forEach((name) => {
const fPath = path.resolve(dir, name);
const stats = fs.statSync(fPath);
if ((!filter && !name.startsWith('.')) || (filter && filter(name, stats))) {
const fileStats = {
name,
path: fPath,
relativePath: path.relative(topDir || dir, fPath),
mtimeMs: stats.mtimeMs,
directory: stats.isDirectory(),
};
if (fileStats.directory) {
result.push(fileStats);
return (0, exports.traverse)(fPath, result, topDir || dir, filter);
}
result.push(fileStats);
}
});
return result;
};
exports.traverse = traverse;
function getNetworkName(network) {
var _a, _b;
if (process.env['HARDHAT_DEPLOY_FORK']) {
return process.env['HARDHAT_DEPLOY_FORK'];
}
if ('forking' in network.config && ((_a = network.config.forking) === null || _a === void 0 ? void 0 : _a.network)) {
return (_b = network.config.forking) === null || _b === void 0 ? void 0 : _b.network;
}
return network.name;
}
exports.getNetworkName = getNetworkName;
function getDeployPaths(network) {
var _a, _b;
const networkName = getNetworkName(network);
if (networkName === network.name) {
return network.deploy || ((_a = globalStore_1.store.networks[networkName]) === null || _a === void 0 ? void 0 : _a.deploy); // fallback to global store
}
else {
return (_b = globalStore_1.store.networks[networkName]) === null || _b === void 0 ? void 0 : _b.deploy; // skip network.deploy
}
}
exports.getDeployPaths = getDeployPaths;
function filterABI(abi, excludeSighashes) {
return abi.filter(fragment => fragment.type !== 'function' || !excludeSighashes.has(abi_1.Interface.getSighash(abi_1.Fragment.from(fragment))));
}
exports.filterABI = filterABI;
function mergeABIs(abis, options) {
if (abis.length === 0) {
return [];
}
const result = JSON.parse(JSON.stringify(abis[0]));
for (let i = 1; i < abis.length; i++) {
const abi = abis[i];
for (const fragment of abi) {
const newEthersFragment = abi_1.Fragment.from(fragment);
// TODO constructor special handling ?
const foundSameSig = result.find((v) => {
const existingEthersFragment = abi_1.Fragment.from(v);
if (v.type !== fragment.type) {
return false;
}
if (!existingEthersFragment) {
return v.name === fragment.name; // TODO fallback and receive hanlding
}
if (existingEthersFragment.type === 'constructor' ||
newEthersFragment.type === 'constructor') {
return existingEthersFragment.name === newEthersFragment.name;
}
if (newEthersFragment.type === 'function') {
return (abi_1.Interface.getSighash(existingEthersFragment) ===
abi_1.Interface.getSighash(newEthersFragment));
}
else if (newEthersFragment.type === 'event') {
return existingEthersFragment.format() === newEthersFragment.format();
}
else {
return v.name === fragment.name; // TODO fallback and receive hanlding
}
});
if (foundSameSig) {
if (options.check &&
!(options.skipSupportsInterface &&
fragment.name === 'supportsInterface')) {
if (fragment.type === 'function') {
throw new Error(`function "${fragment.name}" will shadow "${foundSameSig.name}". Please update code to avoid conflict.`);
}
}
}
else {
result.push(fragment);
}
}
}
return result;
}
exports.mergeABIs = mergeABIs;
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
function recode(decoded) {
return {
from: decoded.from,
gasPrice: decoded.gasPrice ? bignumber_1.BigNumber.from(decoded.gasPrice) : undefined,
maxFeePerGas: decoded.maxFeePerGas
? bignumber_1.BigNumber.from(decoded.maxFeePerGas)
: undefined,
maxPriorityFeePerGas: decoded.maxPriorityFeePerGas
? bignumber_1.BigNumber.from(decoded.maxPriorityFeePerGas)
: undefined,
gasLimit: bignumber_1.BigNumber.from(decoded.gasLimit),
to: decoded.to,
value: bignumber_1.BigNumber.from(decoded.value),
nonce: decoded.nonce,
data: decoded.data,
r: decoded.r,
s: decoded.s,
v: decoded.v,
// creates: tx.creates, // TODO test
chainId: decoded.chainId,
};
}
exports.recode = recode;
//# sourceMappingURL=utils.js.map