@nomicfoundation/hardhat-ignition-viem
Version:
The Viem extension to Hardhat Ignition. Hardhat Ignition is a declarative system for deploying smart contracts on Ethereum. It enables you to define smart contract instances you want to deploy, and any operation you want to run on them. By taking over the
182 lines • 8.57 kB
JavaScript
var _a;
import path from "node:path";
import { assertHardhatInvariant, HardhatError, } from "@nomicfoundation/hardhat-errors";
import { HardhatArtifactResolver, PrettyEventHandler, errorDeploymentResultToExceptionMessage, getUserInterruptionsHandlers, readDeploymentParameters, resolveDeploymentId, } from "@nomicfoundation/hardhat-ignition/helpers";
import { DeploymentResultType, FutureType, deploy, isContractFuture, } from "@nomicfoundation/ignition-core";
import { getContract } from "viem";
export class ViemIgnitionHelperImpl {
type = "viem";
#hardhatConfig;
#artifactsManager;
#connection;
#userInterruptions;
#hooks;
#config;
#provider;
#mutex = false;
constructor(hardhatConfig, artifactsManager, connection, userInterruptions, hooks, config, provider) {
this.#hardhatConfig = hardhatConfig;
this.#artifactsManager = artifactsManager;
this.#connection = connection;
this.#userInterruptions = userInterruptions;
this.#hooks = hooks;
this.#config = config;
this.#provider = provider ?? this.#connection.provider;
}
/**
* Deploys the given Ignition module and returns the results of the module
* as Viem contract instances.
*
* @param ignitionModule - The Ignition module to deploy.
* @param options - The options to use for the deployment.
* @returns Viem contract instances for each contract returned by the module.
*/
async deploy(ignitionModule, { parameters = {}, config: perDeployConfig = {}, defaultSender = undefined, strategy, strategyConfig, deploymentId: givenDeploymentId = undefined, displayUi = false, } = {
parameters: {},
config: {},
defaultSender: undefined,
strategy: undefined,
strategyConfig: undefined,
deploymentId: undefined,
displayUi: undefined,
}) {
if (this.#mutex) {
throw new HardhatError(HardhatError.ERRORS.IGNITION.DEPLOY.ALREADY_IN_PROGRESS);
}
this.#mutex = true;
let userInterruptionsHandlers;
try {
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- eth_accounts returns a string array
const accounts = (await this.#connection.provider.request({
method: "eth_accounts",
}));
const artifactResolver = new HardhatArtifactResolver(this.#artifactsManager);
const resolvedConfig = this.getResolvedConfig(perDeployConfig);
const resolvedStrategyConfig = _a.#resolveStrategyConfig(this.#hardhatConfig, strategy, strategyConfig);
const chainId = Number(await this.#connection.provider.request({
method: "eth_chainId",
}));
const deploymentId = resolveDeploymentId(givenDeploymentId, chainId);
const deploymentDir = this.#connection.networkConfig.type === "edr-simulated"
? undefined
: path.join(this.#hardhatConfig.paths.ignition, "deployments", deploymentId);
const executionEventListener = displayUi
? new PrettyEventHandler(this.#userInterruptions)
: undefined;
if (executionEventListener !== undefined) {
userInterruptionsHandlers = getUserInterruptionsHandlers();
this.#hooks.registerHandlers("userInterruptions", userInterruptionsHandlers);
}
let deploymentParameters;
if (typeof parameters === "string") {
deploymentParameters = await readDeploymentParameters(parameters);
}
else {
deploymentParameters = parameters;
}
if (resolvedConfig.maxRetries === undefined &&
this.#connection.networkConfig.ignition.maxRetries !== undefined) {
resolvedConfig.maxRetries =
this.#connection.networkConfig.ignition.maxRetries;
}
if (resolvedConfig.retryInterval === undefined &&
this.#connection.networkConfig.ignition.retryInterval !== undefined) {
resolvedConfig.retryInterval =
this.#connection.networkConfig.ignition.retryInterval;
}
const result = await deploy({
config: resolvedConfig,
provider: this.#provider,
deploymentDir,
executionEventListener,
artifactResolver,
ignitionModule,
deploymentParameters,
accounts,
defaultSender,
strategy,
strategyConfig: resolvedStrategyConfig,
maxFeePerGasLimit: this.#connection.networkConfig?.ignition.maxFeePerGasLimit,
maxPriorityFeePerGas: this.#connection.networkConfig?.ignition.maxPriorityFeePerGas,
});
if (result.type !== DeploymentResultType.SUCCESSFUL_DEPLOYMENT) {
const message = errorDeploymentResultToExceptionMessage(result);
throw new HardhatError(HardhatError.ERRORS.IGNITION.INTERNAL.DEPLOYMENT_ERROR, {
message,
});
}
return await this.#toViemContracts(this.#connection, ignitionModule, result);
}
finally {
if (userInterruptionsHandlers !== undefined) {
this.#hooks.unregisterHandlers("userInterruptions", userInterruptionsHandlers);
}
this.#mutex = false;
}
}
getResolvedConfig(perDeployConfig) {
return {
...this.#config,
...perDeployConfig,
};
}
async #toViemContracts(connection, ignitionModule, result) {
return Object.fromEntries(await Promise.all(Object.entries(ignitionModule.results).map(async ([name, contractFuture]) => [
name,
await this.#getContract(connection, contractFuture, result.contracts[contractFuture.id]),
])));
}
async #getContract(connection, future, deployedContract) {
assertHardhatInvariant(isContractFuture(future), `Expected contract future but got ${future.id} with type ${future.type} instead`);
return await this.#convertContractFutureToViemContract(connection, future, deployedContract);
}
async #convertContractFutureToViemContract(connection, future, deployedContract) {
switch (future.type) {
case FutureType.NAMED_ARTIFACT_CONTRACT_DEPLOYMENT:
case FutureType.NAMED_ARTIFACT_LIBRARY_DEPLOYMENT:
case FutureType.NAMED_ARTIFACT_CONTRACT_AT:
return await this.#convertHardhatContractToViemContract(connection, future, deployedContract);
case FutureType.CONTRACT_DEPLOYMENT:
case FutureType.LIBRARY_DEPLOYMENT:
case FutureType.CONTRACT_AT:
return await this.#convertArtifactToViemContract(connection, future, deployedContract);
}
}
#convertHardhatContractToViemContract(connection, future, deployedContract) {
return connection.viem.getContractAt(future.contractName, _a.#ensureAddressFormat(deployedContract.address));
}
async #convertArtifactToViemContract(connection, future, deployedContract) {
const publicClient = await connection.viem.getPublicClient();
const [walletClient] = await connection.viem.getWalletClients();
if (walletClient === undefined) {
throw new HardhatError(HardhatError.ERRORS.IGNITION.INTERNAL.NO_DEFAULT_VIEM_WALLET_CLIENT);
}
const contract = getContract({
address: _a.#ensureAddressFormat(deployedContract.address),
abi: future.artifact.abi,
client: {
public: publicClient,
wallet: walletClient,
},
});
return contract;
}
static #ensureAddressFormat(address) {
if (!address.startsWith("0x")) {
return `0x${address}`;
}
return `0x${address.slice(2)}`;
}
static #resolveStrategyConfig(hardhatConfig, strategyName, strategyConfig) {
if (strategyName === undefined) {
return undefined;
}
if (strategyConfig === undefined) {
const fromHardhatConfig = hardhatConfig.ignition?.strategyConfig?.[strategyName];
return fromHardhatConfig;
}
return strategyConfig;
}
}
_a = ViemIgnitionHelperImpl;
//# sourceMappingURL=viem-ignition-helper.js.map