@kadena/hardhat-chainweb
Version:
Hardhat plugin for Kadena's Chainweb network
435 lines • 20.1 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 () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__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 });
const construction_1 = require("hardhat/internal/core/providers/construction");
const config_1 = require("hardhat/config");
const chainweb_js_1 = require("./utils/chainweb.js");
const configure_js_1 = require("./utils/configure.js");
const chainweb_graph_js_1 = require("./utils/chainweb-graph.js");
const hardhat_ethers_provider_js_1 = require("@nomicfoundation/hardhat-ethers/internal/hardhat-ethers-provider.js");
const web3_1 = __importDefault(require("web3"));
const runRPCNode_js_1 = require("./server/runRPCNode.js");
const network_contracts_js_1 = require("./utils/network-contracts.js");
const picocolors_1 = __importDefault(require("picocolors"));
const pure_utils_js_1 = require("./pure-utils.js");
const minimist_1 = __importDefault(require("minimist"));
(0, config_1.extendConfig)((config, userConfig) => {
var _a, _b, _c, _d;
if (!userConfig.chainweb) {
throw new Error('hardhat_kadena plugins is imported but chainweb configuration is not presented in hardhat.config.js');
}
if (Object.keys(userConfig.chainweb).length === 0) {
throw new Error('You need to provide at least one chainweb configuration in hardhat.config.js');
}
const argv = (0, minimist_1.default)(process.argv.slice(2));
config.defaultChainweb =
(_c = (_b = (_a = argv['chainweb']) !== null && _a !== void 0 ? _a : process.env['HK_ACTIVE_CHAINWEB_NAME']) !== null && _b !== void 0 ? _b : userConfig.defaultChainweb) !== null && _c !== void 0 ? _c : 'hardhat';
const defaultChainwebChainIdOffset = 0;
const hardhatConfig = {
chains: 2,
chainwebChainIdOffset: defaultChainwebChainIdOffset,
...userConfig.chainweb.hardhat,
type: 'in-process',
};
const localhostConfig = {
chains: hardhatConfig.chains,
chainIdOffset: (_d = hardhatConfig.chainIdOffset) !== null && _d !== void 0 ? _d : 626000,
externalHostUrl: 'http://localhost:8545',
chainwebChainIdOffset: hardhatConfig.chainwebChainIdOffset,
...userConfig.chainweb['localhost'],
type: 'external',
};
const userConfigWithLocalhost = {
...userConfig.chainweb,
hardhat: hardhatConfig,
localhost: localhostConfig,
};
if (!(config.defaultChainweb in userConfigWithLocalhost)) {
throw new Error(`Default chainweb ${config.defaultChainweb} not found in hardhat.config.js`);
}
Object.entries(userConfigWithLocalhost).forEach(([name, chainwebUserConfig]) => {
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
if (chainwebUserConfig === undefined)
return;
if (!chainwebUserConfig.chains) {
throw new Error('Number of chains is not presented in hardhat.config.js');
}
let type = (_a = chainwebUserConfig.type) !== null && _a !== void 0 ? _a : 'in-process';
if (name === 'hardhat') {
type = 'in-process';
}
if (name === 'localhost') {
type = 'external';
}
const isDefaultChainweb = name === config.defaultChainweb;
if (type === 'in-process') {
const chainwebInProcessUserConfig = chainwebUserConfig;
if (chainwebInProcessUserConfig.graph) {
const graph = (_b = chainwebInProcessUserConfig.graph) !== null && _b !== void 0 ? _b : {};
if (chainwebInProcessUserConfig.chains &&
Object.keys(graph).length != chainwebInProcessUserConfig.chains) {
throw new Error('Number of chains in graph does not match the graph configuration');
}
}
const offset = (_c = chainwebInProcessUserConfig.chainwebChainIdOffset) !== null && _c !== void 0 ? _c : 0;
const graphWithOffset = (0, chainweb_graph_js_1.createGraph)(chainwebInProcessUserConfig.chains).reduce((acc, targets, source) => ({
...acc,
[source + offset]: targets.map((target) => target + offset),
}), {});
// add networks to hardhat
const chainwebConfig = {
graph: (_d = chainwebInProcessUserConfig.graph) !== null && _d !== void 0 ? _d : graphWithOffset,
logging: 'info',
type: 'in-process',
chainIdOffset: 626000,
accounts: config.networks.hardhat.accounts,
precompiles: {
chainwebChainId: network_contracts_js_1.CHAIN_ID_ADDRESS,
spvVerify: network_contracts_js_1.VERIFY_ADDRESS,
},
chainwebChainIdOffset: defaultChainwebChainIdOffset,
...chainwebInProcessUserConfig,
};
const [networkConfig, etherscanCustomChains, etherscanApiKeys] = (0, configure_js_1.getKadenaNetworks)({
availableNetworks: userConfig.networks,
hardhatNetwork: config.networks.hardhat,
networkStem: (0, pure_utils_js_1.getNetworkStem)(name),
numberOfChains: chainwebConfig.chains,
accounts: (_e = chainwebConfig.accounts) !== null && _e !== void 0 ? _e : (_f = chainwebConfig.networkOptions) === null || _f === void 0 ? void 0 : _f.accounts,
loggingEnabled: chainwebConfig.logging === 'debug',
forking: ((_h = (_g = chainwebConfig.networkOptions) === null || _g === void 0 ? void 0 : _g.forking) === null || _h === void 0 ? void 0 : _h.url)
? { enabled: true, ...chainwebConfig.networkOptions.forking }
: undefined,
networkOptions: chainwebConfig.networkOptions,
chainIdOffset: chainwebConfig.chainIdOffset,
chainwebChainIdOffset: chainwebConfig.chainwebChainIdOffset,
etherscan: isDefaultChainweb ? chainwebConfig.etherscan : undefined,
});
config.networks = {
...config.networks,
...networkConfig,
};
if (isDefaultChainweb && chainwebConfig.etherscan) {
config.etherscan = {
apiKey: etherscanApiKeys,
customChains: etherscanCustomChains,
enabled: true,
};
}
config.chainweb[name] = chainwebConfig;
}
else {
const externalUserConfig = chainwebUserConfig;
const chainwebConfig = {
type: 'external',
chainIdOffset: 626000,
externalHostUrl: 'http://localhost:8545',
accounts: 'remote',
chainwebChainIdOffset: defaultChainwebChainIdOffset,
...externalUserConfig,
precompiles: {
chainwebChainId: (_k = (_j = externalUserConfig.precompiles) === null || _j === void 0 ? void 0 : _j.chainwebChainId) !== null && _k !== void 0 ? _k : network_contracts_js_1.CHAIN_ID_ADDRESS,
spvVerify: (_m = (_l = externalUserConfig.precompiles) === null || _l === void 0 ? void 0 : _l.spvVerify) !== null && _m !== void 0 ? _m : network_contracts_js_1.VERIFY_ADDRESS,
},
};
const [networkConfig, etherscanCustomChains, etherscanApiKeys] = (0, configure_js_1.getKadenaExternalNetworks)({
availableNetworks: userConfig.networks,
networkStem: (0, pure_utils_js_1.getNetworkStem)(name),
numberOfChains: chainwebConfig.chains,
accounts: chainwebConfig.accounts,
baseUrl: chainwebConfig.externalHostUrl,
networkOptions: chainwebConfig.networkOptions,
chainIdOffset: chainwebConfig.chainIdOffset,
chainwebChainIdOffset: chainwebConfig.chainwebChainIdOffset,
etherscan: isDefaultChainweb ? chainwebConfig.etherscan : undefined,
});
// add networks to hardhat
config.networks = {
...config.networks,
...networkConfig,
};
config.chainweb[name] = chainwebConfig;
if (isDefaultChainweb && chainwebConfig.etherscan) {
config.etherscan = {
apiKey: etherscanApiKeys,
customChains: etherscanCustomChains,
enabled: true,
};
}
}
});
});
const createExternalProvider = async (hre, chainwebName) => {
const utils = await Promise.resolve().then(() => __importStar(require('./utils.js')));
const networkStem = (0, pure_utils_js_1.getNetworkStem)(chainwebName);
return {
deployContractOnChains: utils.deployContractOnChains,
getProvider: (cid) => {
const name = `${networkStem}${cid}`;
return (0, construction_1.createProvider)(hre.config, name, hre.artifacts);
},
requestSpvProof: (targetChain, origin) => utils.requestSpvProof(targetChain, origin),
switchChain: async (cid) => {
if (typeof cid === 'string') {
await hre.switchNetwork(cid);
console.log(`Switched to ${cid}`);
}
else {
console.log(`Switched to ${networkStem}${cid}`);
await hre.switchNetwork(`${networkStem}${cid}`);
}
},
getChainIds: utils.getChainIds,
callChainIdContract: utils.callChainIdContract,
createTamperedProof: (targetChain, origin) => utils.createTamperedProof(targetChain, origin),
computeOriginHash: pure_utils_js_1.computeOriginHash,
runOverChains: utils.runOverChains,
};
};
const createInternalProvider = async (hre, chainwebName, overrideForking) => {
const chainweb = hre.config.chainweb[chainwebName];
if (!chainweb || chainweb.type !== 'in-process') {
throw new Error('Chainweb configuration not found');
}
const utils = await Promise.resolve().then(() => __importStar(require('./utils.js')));
const networkStem = (0, pure_utils_js_1.getNetworkStem)(chainwebName);
const chainwebNetwork = new chainweb_js_1.ChainwebNetwork({
chainweb,
networks: hre.config.networks,
chainwebName: chainwebName,
overrideForking,
});
async function startHardhatNetwork() {
await chainwebNetwork.start();
}
let stopped = false;
async function stopHardhatNetwork() {
if (stopped)
return;
await chainwebNetwork.stop();
stopped = true;
process.exit(0);
}
let setNetworkReady;
const isNetworkReadyPromise = new Promise((resolve) => {
setNetworkReady = resolve;
});
let started = false;
const spinupChainweb = async () => {
if (started)
return;
started = true;
process.on('exit', stopHardhatNetwork);
process.on('SIGINT', stopHardhatNetwork);
process.on('SIGTERM', stopHardhatNetwork);
process.on('uncaughtException', stopHardhatNetwork);
return startHardhatNetwork()
.then(() => {
setNetworkReady();
})
.catch(() => {
process.exit(1);
});
};
const originalSwitchNetwork = hre.switchNetwork;
hre.switchNetwork = async (networkNameOrIndex) => {
await isNetworkReadyPromise;
const networkName = typeof networkNameOrIndex === 'number'
? `${networkStem}${networkNameOrIndex}`
: networkNameOrIndex;
if (networkName.startsWith(networkStem)) {
const cid = parseInt(networkName.slice(networkStem.length));
const provider = chainwebNetwork.getProvider(cid);
hre.network.name = networkName;
hre.network.config = hre.config.networks[networkName];
hre.network.provider = provider;
// update underlying library's provider data
if ('ethers' in hre) {
hre.ethers.provider = new hardhat_ethers_provider_js_1.HardhatEthersProvider(provider, networkName);
}
if ('web3' in hre) {
hre.web3 = new web3_1.default(provider);
}
console.log(`Switched to ${cid}`);
return;
}
originalSwitchNetwork(networkName);
};
spinupChainweb();
return {
deployContractOnChains: utils.deployContractOnChains,
getProvider: async (cid) => {
await isNetworkReadyPromise;
const provider = chainwebNetwork.getProvider(cid);
return provider;
},
requestSpvProof: (targetChain, origin) => utils.requestSpvProof(targetChain, origin, chainwebNetwork),
switchChain: async (cid) => {
await isNetworkReadyPromise;
if (typeof cid === 'string') {
await hre.switchNetwork(cid);
}
else {
await hre.switchNetwork(`${networkStem}${cid}`);
}
},
getChainIds: utils.getChainIds,
callChainIdContract: utils.callChainIdContract,
createTamperedProof: (targetChain, origin) => utils.createTamperedProof(targetChain, origin, chainwebNetwork),
computeOriginHash: pure_utils_js_1.computeOriginHash,
runOverChains: utils.runOverChains,
};
};
// const spinupChainweb = () =>
(0, config_1.extendEnvironment)((hre) => {
let api = undefined;
let initDone = () => { };
const init = new Promise((resolve) => {
initDone = resolve;
});
const safeCall =
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(cb) => async (...args) => {
await init;
if (api !== undefined) {
return cb()(...args);
}
throw new Error('Chainweb plugin not initialized');
};
hre.chainweb = {
initialize: async (args) => {
if (api)
return;
const chainweb = hre.config.chainweb[hre.config.defaultChainweb];
if (!chainweb) {
throw new Error('Chainweb configuration not found');
}
console.log('Chainweb:', picocolors_1.default.bgGreenBright(` ${hre.config.defaultChainweb} `), 'Chains:', picocolors_1.default.bgGreenBright(` ${chainweb.chains} `), '\n');
if (chainweb.type === 'external') {
api = await createExternalProvider(hre, hre.config.defaultChainweb);
}
else {
api = await createInternalProvider(hre, hre.config.defaultChainweb, args === null || args === void 0 ? void 0 : args.forking);
}
initDone();
},
getProvider: safeCall(() => api.getProvider),
requestSpvProof: safeCall(() => api.requestSpvProof),
switchChain: safeCall(() => api.switchChain),
getChainIds: safeCall(() => api.getChainIds),
callChainIdContract: safeCall(() => api.callChainIdContract),
deployContractOnChains: safeCall(() => api.deployContractOnChains),
createTamperedProof: safeCall(() => api.createTamperedProof),
computeOriginHash: pure_utils_js_1.computeOriginHash,
runOverChains: safeCall(() => api.runOverChains),
};
if (process.env['HK_INIT_CHAINWEB'] === 'true') {
hre.chainweb.initialize();
}
});
const chainwebSwitch = ['chainweb', 'The name of the chainweb to use'];
(0, config_1.task)('node', `Starts a JSON-RPC server on top of Default Chainweb; use ${picocolors_1.default.bgBlackBright(' --network ')} if you want to run a single network rather than a chainweb`)
.addOptionalParam(...chainwebSwitch)
.setAction(async (taskArgs, hre, runSuper) => {
var _a, _b;
const hasNetwork = process.argv.includes('--network');
if (taskArgs.chainweb && hasNetwork) {
console.error('You can only specify one of chainweb or network, not both');
return;
}
if (hasNetwork) {
return runSuper(taskArgs);
}
hre.config.defaultChainweb =
(_b = (_a = taskArgs.chainweb) !== null && _a !== void 0 ? _a : hre.config.defaultChainweb) !== null && _b !== void 0 ? _b : 'hardhat';
const config = hre.config.chainweb[hre.config.defaultChainweb];
if (!config) {
console.log(`Chainweb configuration ${hre.config.defaultChainweb} not found`);
return;
}
if (config.type === 'external') {
console.error('You can only start a node for in-process chainweb');
return;
}
let options = undefined;
if (taskArgs.fork) {
options = {
forking: {
url: taskArgs.fork,
blockNumber: taskArgs.forkBlockNumber,
},
};
}
await hre.chainweb.initialize(options);
return (0, runRPCNode_js_1.runRPCNode)(taskArgs, hre);
});
(0, config_1.task)('test', `Run mocha tests; Supports Chainweb`)
.addOptionalParam(...chainwebSwitch)
.setAction(async (taskArgs, hre, runSuper) => {
var _a, _b;
if (!hre.chainweb.initialize) {
console.error('Chainweb _initialize is not a function');
return;
}
hre.config.defaultChainweb =
(_b = (_a = taskArgs.chainweb) !== null && _a !== void 0 ? _a : hre.config.defaultChainweb) !== null && _b !== void 0 ? _b : 'hardhat';
hre.chainweb.initialize();
if (!process.argv.includes('--network')) {
const [first] = await hre.chainweb.getChainIds();
await hre.chainweb.switchChain(first);
}
return runSuper(taskArgs);
});
(0, config_1.task)('run', `Runs a user-defined script after compiling the project; Supports Chainweb`)
.addOptionalParam(...chainwebSwitch)
.setAction(async (taskArgs, hre, runSuper) => {
var _a, _b;
// Since hardhat run the script in a separate process, we need to set the following configurations
// as environment variables then Hardhat forward them to the script process
process.env['HK_ACTIVE_CHAINWEB_NAME'] = hre.config.defaultChainweb =
(_b = (_a = taskArgs.chainweb) !== null && _a !== void 0 ? _a : hre.config.defaultChainweb) !== null && _b !== void 0 ? _b : 'hardhat';
// then we know that the chainweb should run the initialization
process.env['HK_INIT_CHAINWEB'] = 'true';
return runSuper(taskArgs);
});
(0, config_1.task)('print-config', 'print the final configuration')
.addOptionalParam(...chainwebSwitch)
.setAction(async (_taskArgs, hre) => {
console.dir(hre.config, { depth: null, colors: true });
});
//# sourceMappingURL=plugin.js.map