Repository: NomicFoundation/hardhat-ignition
Pull request #609 (2023-10-30 12:04:54)

release: 0.11.1
## 0.11.1 - 2023-10-30

### Added

- Give visual indication that there was a gas bump in `deploy` task ([#587](https://github.com/NomicFoundation/hardhat-ignition/issues/587))

### Changed

- When displaying an Ethereum Address at the cli, show in checksum format ([#600](https://github.com/NomicFoundation/hardhat-ignition/issues/600))
- Show a better message when no futures were executed during a rerun ([#586](https://github.com/NomicFoundation/hardhat-ignition/issues/586))
- Less confusing message for no modules folder error ([#602](https://github.com/NomicFoundation/hardhat-ignition/issues/602))




Patch:
diff --git a/examples/complete/package.json b/examples/complete/package.json
index cc99373b..15cda975 100644
--- a/examples/complete/package.json
+++ b/examples/complete/package.json
@@ -1,7 +1,7 @@
 {
   "name": "@nomicfoundation/ignition-complete-example",
   "private": true,
-  "version": "0.11.0",
+  "version": "0.11.1",
   "scripts": {
     "test": "hardhat test",
     "lint": "npm run prettier -- --check && npm run eslint",
@@ -10,7 +10,7 @@
     "prettier": "prettier \"*.{js,md,json}\" \"ignition/modules/*.{js,md,json}\" \"test/*.{js,md,json}\" \"contracts/**/*.sol\""
   },
   "devDependencies": {
-    "@nomicfoundation/hardhat-ignition": "^0.11.0",
+    "@nomicfoundation/hardhat-ignition": "^0.11.1",
     "@nomicfoundation/hardhat-toolbox": "3.0.0",
     "hardhat": "^2.18.0",
     "prettier-plugin-solidity": "1.1.3"
diff --git a/examples/ens/package.json b/examples/ens/package.json
index dfaaa3f3..ac876b32 100644
--- a/examples/ens/package.json
+++ b/examples/ens/package.json
@@ -1,7 +1,7 @@
 {
   "name": "@nomicfoundation/ignition-ens-example",
   "private": true,
-  "version": "0.11.0",
+  "version": "0.11.1",
   "scripts": {
     "test": "hardhat test",
     "lint": "npm run prettier -- --check && npm run eslint",
@@ -10,7 +10,7 @@
     "prettier": "prettier \"*.{js,md,json}\" \"ignition/modules/*.{js,md,json}\" \"test/*.{js,md,json}\" \"contracts/**/*.sol\""
   },
   "devDependencies": {
-    "@nomicfoundation/hardhat-ignition": "^0.11.0",
+    "@nomicfoundation/hardhat-ignition": "^0.11.1",
     "@nomicfoundation/hardhat-toolbox": "3.0.0",
     "hardhat": "^2.18.0",
     "prettier-plugin-solidity": "1.1.3"
diff --git a/examples/sample/package.json b/examples/sample/package.json
index efa0fb33..d02fac96 100644
--- a/examples/sample/package.json
+++ b/examples/sample/package.json
@@ -1,7 +1,7 @@
 {
   "name": "@nomicfoundation/ignition-sample-example",
   "private": true,
-  "version": "0.11.0",
+  "version": "0.11.1",
   "scripts": {
     "test": "hardhat test",
     "lint": "npm run prettier -- --check && npm run eslint",
@@ -10,7 +10,7 @@
     "prettier": "prettier \"*.{js,md,json}\" \"ignition/modules/*.{js,md,json}\" \"test/*.{js,md,json}\" \"contracts/**/*.sol\""
   },
   "devDependencies": {
-    "@nomicfoundation/hardhat-ignition": "^0.11.0",
+    "@nomicfoundation/hardhat-ignition": "^0.11.1",
     "@nomicfoundation/hardhat-toolbox": "3.0.0",
     "hardhat": "^2.18.0",
     "prettier-plugin-solidity": "1.1.3"
diff --git a/examples/sample/scripts/bump.js b/examples/sample/scripts/bump.js
new file mode 100644
index 00000000..31478773
--- /dev/null
+++ b/examples/sample/scripts/bump.js
@@ -0,0 +1,118 @@
+const {
+  setNextBlockBaseFeePerGas,
+  mine,
+} = require("@nomicfoundation/hardhat-network-helpers");
+
+/**
+ * This is a script that can be used to manually test the fee bumping mechanism in Hardhat Ignition.
+ *
+ * Steps to use:
+ * 1. Edit hardhat.config.js to adjust Hardhat Ignition's configuration, depending on what you want to test.
+ *    Ex:
+ *    ```
+        networks: {
+          hardhat: {
+            mining: {
+              auto: false,
+            },
+          },
+        },
+        ignition: {
+          maxFeeBumps: 3,
+          timeBeforeBumpingFees: 50,
+          blockPollingInterval: 100,
+        },
+      ```
+ *  2. Run `npx hardhat node` in one terminal.
+ *  3. Run `npx hardhat run ./scripts/bump.js --network localhost` in another terminal.
+ *  4. Wait for the script to ask you to start the deployment.
+ *  5. Run `npx hardhat ignition deploy ./ignition/modules/LockModule.js --network localhost` in another terminal.
+ */
+async function main() {
+  await ethers.provider.send("evm_setAutomine", [false]);
+
+  // eslint-disable-next-line no-console
+  console.log(
+    "Script has started. Please open another terminal and start the deployment now..."
+  );
+
+  // wait for one tx to be added to the mempool
+  await waitForPendingTxs(1);
+
+  // eslint-disable-next-line no-console
+  console.log(
+    "Transaction detected, increasing block fee and mining a block..."
+  );
+
+  // bump the fee of the next block
+  await setNextBlockBaseFeePerGas(10_000_000_000n);
+
+  // mine the block
+  await mine(1);
+
+  // wait
+  await sleep(1000);
+
+  // bump the fee of the next block
+  await setNextBlockBaseFeePerGas(100_000_000_000n);
+
+  // mine the block
+  await mine(1);
+
+  // wait
+  await sleep(1000);
+
+  // bump the fee of the next block
+  await setNextBlockBaseFeePerGas(1_000_000_000_000n);
+
+  // mine the block
+  await mine(1);
+
+  // wait
+  await sleep(1000);
+
+  // bump the fee of the next block
+  await setNextBlockBaseFeePerGas(10_000_000_000_000n);
+
+  // mine the block
+  await mine(1);
+
+  // wait
+  await sleep(1000);
+
+  // bump the fee of the next block
+  await setNextBlockBaseFeePerGas(100_000_000_000_000n);
+
+  // mine the block
+  await mine(1);
+
+  // wait
+  await sleep(1000);
+
+  // eslint-disable-next-line no-console
+  console.log("Script finished");
+}
+
+/* Helpers */
+
+const sleep = (timeout) => new Promise((res) => setTimeout(res, timeout));
+
+/**
+ * Wait until there are at least `expectedCount` transactions in the mempool
+ */
+async function waitForPendingTxs(expectedCount) {
+  while (true) {
+    const pendingBlock = await network.provider.send("eth_getBlockByNumber", [
+      "pending",
+      false,
+    ]);
+
+    if (pendingBlock.transactions.length >= expectedCount) {
+      return;
+    }
+
+    await sleep(50);
+  }
+}
+
+main();
diff --git a/examples/ts-sample/package.json b/examples/ts-sample/package.json
index f24836d2..62016d38 100644
--- a/examples/ts-sample/package.json
+++ b/examples/ts-sample/package.json
@@ -1,7 +1,7 @@
 {
   "name": "@nomicfoundation/ignition-ts-sample-example",
   "private": true,
-  "version": "0.11.0",
+  "version": "0.11.1",
   "scripts": {
     "test": "hardhat test",
     "lint": "npm run prettier -- --check && npm run eslint",
@@ -10,7 +10,7 @@
     "prettier": "prettier \"*.{js,ts,md,json}\" \"ignition/modules/*.{js,ts,md,json}\" \"test/*.{js,ts,md,json}\" \"contracts/**/*.sol\""
   },
   "devDependencies": {
-    "@nomicfoundation/hardhat-ignition": "^0.11.0",
+    "@nomicfoundation/hardhat-ignition": "^0.11.1",
     "@nomicfoundation/hardhat-toolbox": "3.0.0",
     "hardhat": "^2.18.0",
     "prettier-plugin-solidity": "1.1.3"
diff --git a/package-lock.json b/package-lock.json
index af7c665a..549de872 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -30,9 +30,9 @@
     },
     "examples/complete": {
       "name": "@nomicfoundation/ignition-complete-example",
-      "version": "0.11.0",
+      "version": "0.11.1",
       "devDependencies": {
-        "@nomicfoundation/hardhat-ignition": "^0.11.0",
+        "@nomicfoundation/hardhat-ignition": "^0.11.1",
         "@nomicfoundation/hardhat-toolbox": "3.0.0",
         "hardhat": "^2.18.0",
         "prettier-plugin-solidity": "1.1.3"
@@ -40,12 +40,12 @@
     },
     "examples/ens": {
       "name": "@nomicfoundation/ignition-ens-example",
-      "version": "0.11.0",
+      "version": "0.11.1",
       "dependencies": {
         "@ensdomains/ens-contracts": "0.0.11"
       },
       "devDependencies": {
-        "@nomicfoundation/hardhat-ignition": "^0.11.0",
+        "@nomicfoundation/hardhat-ignition": "^0.11.1",
         "@nomicfoundation/hardhat-toolbox": "3.0.0",
         "hardhat": "^2.18.0",
         "prettier-plugin-solidity": "1.1.3"
@@ -53,9 +53,9 @@
     },
     "examples/sample": {
       "name": "@nomicfoundation/ignition-sample-example",
-      "version": "0.11.0",
+      "version": "0.11.1",
       "devDependencies": {
-        "@nomicfoundation/hardhat-ignition": "^0.11.0",
+        "@nomicfoundation/hardhat-ignition": "^0.11.1",
         "@nomicfoundation/hardhat-toolbox": "3.0.0",
         "hardhat": "^2.18.0",
         "prettier-plugin-solidity": "1.1.3"
@@ -63,9 +63,9 @@
     },
     "examples/ts-sample": {
       "name": "@nomicfoundation/ignition-ts-sample-example",
-      "version": "0.11.0",
+      "version": "0.11.1",
       "devDependencies": {
-        "@nomicfoundation/hardhat-ignition": "^0.11.0",
+        "@nomicfoundation/hardhat-ignition": "^0.11.1",
         "@nomicfoundation/hardhat-toolbox": "3.0.0",
         "hardhat": "^2.18.0",
         "prettier-plugin-solidity": "1.1.3"
@@ -21264,7 +21264,7 @@
     },
     "packages/core": {
       "name": "@nomicfoundation/ignition-core",
-      "version": "0.11.0",
+      "version": "0.11.1",
       "license": "MIT",
       "dependencies": {
         "@ethersproject/address": "5.6.1",
@@ -21374,11 +21374,11 @@
     },
     "packages/hardhat-plugin": {
       "name": "@nomicfoundation/hardhat-ignition",
-      "version": "0.11.0",
+      "version": "0.11.1",
       "license": "MIT",
       "dependencies": {
-        "@nomicfoundation/ignition-core": "^0.11.0",
-        "@nomicfoundation/ignition-ui": "^0.11.0",
+        "@nomicfoundation/ignition-core": "^0.11.1",
+        "@nomicfoundation/ignition-ui": "^0.11.1",
         "chalk": "^4.0.0",
         "debug": "^4.3.2",
         "ethers": "^6.7.0",
@@ -21496,10 +21496,10 @@
     },
     "packages/ui": {
       "name": "@nomicfoundation/ignition-ui",
-      "version": "0.11.0",
+      "version": "0.11.1",
       "devDependencies": {
         "@fontsource/roboto": "^5.0.8",
-        "@nomicfoundation/ignition-core": "^0.11.0",
+        "@nomicfoundation/ignition-core": "^0.11.1",
         "@types/chai": "^4.2.22",
         "@types/chai-as-promised": "^7.1.5",
         "@types/react": "^18.0.28",
diff --git a/packages/core/CHANGELOG.md b/packages/core/CHANGELOG.md
index 8b4f80f0..5df058ad 100644
--- a/packages/core/CHANGELOG.md
+++ b/packages/core/CHANGELOG.md
@@ -4,6 +4,16 @@ All notable changes to this project will be documented in this file.
 
 The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
 
+## 0.11.1 - 2023-10-30
+
+### Added
+
+- Give visual indication that there was a gas bump in `deploy` task ([#587](https://github.com/NomicFoundation/hardhat-ignition/issues/587))
+
+### Changed
+
+- When displaying an Ethereum Address at the cli, show in checksum format ([#600](https://github.com/NomicFoundation/hardhat-ignition/issues/600))
+
 ## 0.11.0 - 2023-10-23
 
 First public launch 🚀
diff --git a/packages/core/package.json b/packages/core/package.json
index a3987e8c..46d70927 100644
--- a/packages/core/package.json
+++ b/packages/core/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@nomicfoundation/ignition-core",
-  "version": "0.11.0",
+  "version": "0.11.1",
   "license": "MIT",
   "author": "Nomic Foundation",
   "homepage": "https://hardhat.org",
diff --git a/packages/core/src/internal/deployer.ts b/packages/core/src/internal/deployer.ts
index 65aa4327..371536c6 100644
--- a/packages/core/src/internal/deployer.ts
+++ b/packages/core/src/internal/deployer.ts
@@ -84,7 +84,8 @@ export class Deployer {
     this._emitDeploymentStartEvent(
       ignitionModule.id,
       this._deploymentDir,
-      isResumed
+      isResumed,
+      this._config.maxFeeBumps
     );
 
     const contracts =
@@ -268,7 +269,8 @@ export class Deployer {
   private _emitDeploymentStartEvent(
     moduleId: string,
     deploymentDir: string | undefined,
-    isResumed: boolean
+    isResumed: boolean,
+    maxFeeBumps: number
   ): void {
     if (this._executionEventListener === undefined) {
       return;
@@ -279,6 +281,7 @@ export class Deployer {
       moduleName: moduleId,
       deploymentDir: deploymentDir ?? undefined,
       isResumed,
+      maxFeeBumps,
     });
   }
 
diff --git a/packages/core/src/internal/execution/abi.ts b/packages/core/src/internal/execution/abi.ts
index 05ec30aa..b85659fe 100644
--- a/packages/core/src/internal/execution/abi.ts
+++ b/packages/core/src/internal/execution/abi.ts
@@ -27,6 +27,7 @@ import {
   SuccessfulEvmExecutionResult,
 } from "./types/evm-execution";
 import { TransactionReceipt } from "./types/jsonrpc";
+import { equalAddresses } from "./utils/address";
 
 const REVERT_REASON_SIGNATURE = "0x08c379a0";
 const PANIC_CODE_SIGNATURE = "0x4e487b71";
@@ -362,7 +363,9 @@ export function getEventArgumentFromReceipt(
   eventIndex: number,
   nameOrIndex: string | number
 ): EvmValue {
-  const emitterLogs = receipt.logs.filter((l) => l.address === emitterAddress);
+  const emitterLogs = receipt.logs.filter((l) =>
+    equalAddresses(l.address, emitterAddress)
+  );
 
   const { ethers } = require("ethers") as typeof import("ethers");
   const iface = new ethers.Interface(emitterArtifact.abi);
diff --git a/packages/core/src/internal/execution/jsonrpc-client.ts b/packages/core/src/internal/execution/jsonrpc-client.ts
index a7e8ca77..630a904a 100644
--- a/packages/core/src/internal/execution/jsonrpc-client.ts
+++ b/packages/core/src/internal/execution/jsonrpc-client.ts
@@ -10,6 +10,7 @@ import {
   TransactionReceipt,
   TransactionReceiptStatus,
 } from "./types/jsonrpc";
+import { toChecksumFormat } from "./utils/address";
 
 /**
  * The params to make an `eth_call`.
@@ -537,7 +538,10 @@ export class EIP1193JsonRpcClient implements JsonRpcClient {
     return {
       blockHash: response.blockHash,
       blockNumber: jsonRpcQuantityToNumber(response.blockNumber),
-      contractAddress,
+      contractAddress:
+        contractAddress === undefined
+          ? undefined
+          : toChecksumFormat(contractAddress),
       status,
       logs: formatReceiptLogs(method, response),
     };
@@ -611,6 +615,7 @@ function assertResponseType(
     });
   }
 }
+
 function formatReceiptLogs(method: string, response: object): TransactionLog[] {
   const formattedLogs: TransactionLog[] = [];
 
@@ -660,7 +665,7 @@ function formatReceiptLogs(method: string, response: object): TransactionLog[] {
     );
 
     formattedLogs.push({
-      address: rawLog.address,
+      address: toChecksumFormat(rawLog.address),
       logIndex: jsonRpcQuantityToNumber(rawLog.logIndex),
       data: rawLog.data,
       topics: rawLog.topics,
diff --git a/packages/core/src/internal/execution/utils/address.ts b/packages/core/src/internal/execution/utils/address.ts
new file mode 100644
index 00000000..e1ad5292
--- /dev/null
+++ b/packages/core/src/internal/execution/utils/address.ts
@@ -0,0 +1,29 @@
+import { getAddress, isAddress } from "ethers";
+
+import { assertIgnitionInvariant } from "../../utils/assertions";
+
+/**
+ * Returns a normalized and checksumed address for the given address.
+ *
+ * @param address - the address to reformat
+ * @returns checksumed address
+ */
+export function toChecksumFormat(address: string) {
+  assertIgnitionInvariant(
+    isAddress(address),
+    `Expected ${address} to be an address`
+  );
+
+  return getAddress(address);
+}
+
+/**
+ * Determine if two addresses are equal ignoring case (which is a consideration
+ * because of checksumming).
+ */
+export function equalAddresses(
+  leftAddress: string,
+  rightAddress: string
+): boolean {
+  return toChecksumFormat(leftAddress) === toChecksumFormat(rightAddress);
+}
diff --git a/packages/core/src/types/execution-events.ts b/packages/core/src/types/execution-events.ts
index 64111807..0bf6cbb2 100644
--- a/packages/core/src/types/execution-events.ts
+++ b/packages/core/src/types/execution-events.ts
@@ -78,6 +78,7 @@ export interface DeploymentStartEvent {
   moduleName: string;
   deploymentDir: string | undefined;
   isResumed: boolean;
+  maxFeeBumps: number;
 }
 
 /**
diff --git a/packages/hardhat-plugin/CHANGELOG.md b/packages/hardhat-plugin/CHANGELOG.md
index db353ba6..f80c707c 100644
--- a/packages/hardhat-plugin/CHANGELOG.md
+++ b/packages/hardhat-plugin/CHANGELOG.md
@@ -4,6 +4,18 @@ All notable changes to this project will be documented in this file.
 
 The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
 
+## 0.11.1 - 2023-10-30
+
+### Added
+
+- Give visual indication that there was a gas bump in `deploy` task ([#587](https://github.com/NomicFoundation/hardhat-ignition/issues/587))
+
+### Changed
+
+- When displaying an Ethereum Address at the cli, show in checksum format ([#600](https://github.com/NomicFoundation/hardhat-ignition/issues/600))
+- Show a better message when no futures were executed during a rerun ([#586](https://github.com/NomicFoundation/hardhat-ignition/issues/586))
+- Less confusing message for no modules folder error ([#602](https://github.com/NomicFoundation/hardhat-ignition/issues/602))
+
 ## 0.11.0 - 2023-10-23
 
 First public launch 🚀
diff --git a/packages/hardhat-plugin/package.json b/packages/hardhat-plugin/package.json
index 70c3c554..9098cc9c 100644
--- a/packages/hardhat-plugin/package.json
+++ b/packages/hardhat-plugin/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@nomicfoundation/hardhat-ignition",
-  "version": "0.11.0",
+  "version": "0.11.1",
   "license": "MIT",
   "author": "Nomic Foundation",
   "homepage": "https://hardhat.org",
@@ -92,8 +92,8 @@
     "hardhat": "^2.18.0"
   },
   "dependencies": {
-    "@nomicfoundation/ignition-core": "^0.11.0",
-    "@nomicfoundation/ignition-ui": "^0.11.0",
+    "@nomicfoundation/ignition-core": "^0.11.1",
+    "@nomicfoundation/ignition-ui": "^0.11.1",
     "chalk": "^4.0.0",
     "debug": "^4.3.2",
     "ethers": "^6.7.0",
diff --git a/packages/hardhat-plugin/src/index.ts b/packages/hardhat-plugin/src/index.ts
index 67f48388..05cc917e 100644
--- a/packages/hardhat-plugin/src/index.ts
+++ b/packages/hardhat-plugin/src/index.ts
@@ -55,7 +55,7 @@ ignitionScope
   .addPositionalParam("modulePath", "The path to the module file to deploy")
   .addOptionalParam(
     "parameters",
-    "A relative path to a JSON file to use for the module parameters,"
+    "A relative path to a JSON file to use for the module parameters"
   )
   .addOptionalParam("deploymentId", "Set the id of the deployment")
   .setDescription("Deploy a module to the specified network")
diff --git a/packages/hardhat-plugin/src/load-module.ts b/packages/hardhat-plugin/src/load-module.ts
index 16f9a05e..49ade76a 100644
--- a/packages/hardhat-plugin/src/load-module.ts
+++ b/packages/hardhat-plugin/src/load-module.ts
@@ -1,6 +1,6 @@
 import { IgnitionError, IgnitionModule } from "@nomicfoundation/ignition-core";
 import setupDebug from "debug";
-import { existsSync, pathExistsSync } from "fs-extra";
+import { pathExistsSync } from "fs-extra";
 import {
   HardhatPluginError,
   NomicLabsHardhatPluginError,
@@ -27,20 +27,6 @@ export function loadModule(
     MODULES_FOLDER
   );
 
-  if (!existsSync(ignitionDirectory)) {
-    throw new HardhatPluginError(
-      "hardhat-ignition",
-      `Ignition directory ${ignitionDirectory} not found.`
-    );
-  }
-
-  if (!existsSync(fullModulesDirectoryName)) {
-    throw new HardhatPluginError(
-      "hardhat-ignition",
-      `Ignition modules directory ${shortModulesDirectoryName} not found.`
-    );
-  }
-
   debug(`Loading user modules from '${fullModulesDirectoryName}'`);
 
   const fullpathToModule = path.resolve(modulePath);
diff --git a/packages/hardhat-plugin/src/ui/helpers/calculate-batch-display.ts b/packages/hardhat-plugin/src/ui/helpers/calculate-batch-display.ts
index fa3f578b..9d4b5520 100644
--- a/packages/hardhat-plugin/src/ui/helpers/calculate-batch-display.ts
+++ b/packages/hardhat-plugin/src/ui/helpers/calculate-batch-display.ts
@@ -11,7 +11,7 @@ export function calculateBatchDisplay(state: UiState): {
 
   text += batch
     .sort((a, b) => a.futureId.localeCompare(b.futureId))
-    .map(_futureStatus)
+    .map((v) => _futureStatus(v, state.gasBumps, state.maxFeeBumps))
     .join("\n");
 
   text += "\n";
@@ -19,16 +19,25 @@ export function calculateBatchDisplay(state: UiState): {
   return { text, height };
 }
 
-function _futureStatus(future: UiFuture): string {
+function _futureStatus(
+  future: UiFuture,
+  gasBumps: Record<string, number>,
+  maxFeeBumps: number
+): string {
   switch (future.status.type) {
     case UiFutureStatusType.UNSTARTED: {
-      return `  Executing ${future.futureId}...`;
+      const gas = gasBumps[future.futureId];
+      return `  Executing ${future.futureId}${
+        gas !== undefined
+          ? ` - bumping gas fee (${gas}/${maxFeeBumps})...`
+          : "..."
+      }`;
     }
     case UiFutureStatusType.SUCCESS: {
       return `  Executed ${future.futureId}`;
     }
     case UiFutureStatusType.TIMEDOUT: {
-      return `  Pending ${future.futureId}`;
+      return `  Timed out ${future.futureId}`;
     }
     case UiFutureStatusType.ERRORED: {
       return `  Failed ${future.futureId}`;
diff --git a/packages/hardhat-plugin/src/ui/helpers/calculate-deployment-complete-display.ts b/packages/hardhat-plugin/src/ui/helpers/calculate-deployment-complete-display.ts
index fb4e40e6..dfb457d6 100644
--- a/packages/hardhat-plugin/src/ui/helpers/calculate-deployment-complete-display.ts
+++ b/packages/hardhat-plugin/src/ui/helpers/calculate-deployment-complete-display.ts
@@ -9,15 +9,31 @@ import {
 } from "@nomicfoundation/ignition-core";
 import chalk from "chalk";
 
+import { UiState } from "../types";
+
+import { pathFromCwd } from "./cwd-relative-path";
+import { wasAnythingExecuted } from "./was-anything-executed";
+
 export function calculateDeploymentCompleteDisplay(
   event: DeploymentCompleteEvent,
-  { moduleName: givenModuleName }: { moduleName: string | null }
+  uiState: Pick<
+    UiState,
+    "moduleName" | "isResumed" | "batches" | "deploymentDir"
+  >
 ): string {
-  const moduleName = givenModuleName ?? "unknown";
+  const moduleName = uiState.moduleName ?? "unknown";
+  const isResumed = uiState.isResumed ?? false;
 
   switch (event.result.type) {
     case DeploymentResultType.SUCCESSFUL_DEPLOYMENT: {
-      return _displaySuccessfulDeployment(event.result, { moduleName });
+      const isRerunWithNoChanges: boolean =
+        isResumed && !wasAnythingExecuted(uiState);
+
+      return _displaySuccessfulDeployment(event.result, {
+        moduleName,
+        isRerunWithNoChanges,
+        deploymentDir: uiState.deploymentDir,
+      });
     }
     case DeploymentResultType.VALIDATION_ERROR: {
       return _displayValidationErrors(event.result, { moduleName });
@@ -36,9 +52,23 @@ export function calculateDeploymentCompleteDisplay(
 
 function _displaySuccessfulDeployment(
   result: SuccessfulDeploymentResult,
-  { moduleName }: { moduleName: string }
+  {
+    moduleName,
+    isRerunWithNoChanges,
+    deploymentDir,
+  }: {
+    moduleName: string;
+    isRerunWithNoChanges: boolean;
+    deploymentDir: string | null | undefined;
+  }
 ): string {
-  let text = `[ ${moduleName} ] successfully deployed 🚀
+  const fillerText = isRerunWithNoChanges
+    ? `Nothing new to deploy based on previous execution stored in ${pathFromCwd(
+        deploymentDir ?? "Not provided"
+      )}`
+    : `successfully deployed 🚀`;
+
+  let text = `[ ${moduleName} ] ${fillerText}
 
 ${chalk.bold("Deployed Addresses")}
 
@@ -133,14 +163,12 @@ function _displayExecutionErrors(
   let text = `[ ${moduleName} ] failed ⛔\n\n`;
 
   if (result.timedOut.length > 0) {
-    let timedOutSection = `Futures with transactions unconfirmed after maximum fee bumps:\n`;
+    let timedOutSection = `Futures timed out with transactions unconfirmed after maximum fee bumps:\n`;
 
     timedOutSection += Object.values(result.timedOut)
       .map(({ futureId }) => ` - ${futureId}`)
       .join("\n");
 
-    timedOutSection += "\n\nConsider increasing the fee in your config.";
-
     sections.push(timedOutSection);
   }
 
diff --git a/packages/hardhat-plugin/src/ui/helpers/calculate-deployment-status-display.ts b/packages/hardhat-plugin/src/ui/helpers/calculate-deployment-status-display.ts
index 41fe7709..f0edec0a 100644
--- a/packages/hardhat-plugin/src/ui/helpers/calculate-deployment-status-display.ts
+++ b/packages/hardhat-plugin/src/ui/helpers/calculate-deployment-status-display.ts
@@ -57,14 +57,12 @@ function _calculateFailed(deploymentId: string, statusResult: StatusResult) {
   const sections: string[] = [];
 
   if (statusResult.timedOut.length > 0) {
-    let timedOutSection = `\nFutures with transactions unconfirmed after maximum fee bumps:\n`;
+    let timedOutSection = `\nFutures timed out with transactions unconfirmed after maximum fee bumps:\n`;
 
     timedOutSection += Object.values(statusResult.timedOut)
       .map(({ futureId }) => ` - ${futureId}`)
       .join("\n");
 
-    timedOutSection += "\n\nConsider increasing the fee in your config.";
-
     sections.push(timedOutSection);
   }
 
diff --git a/packages/hardhat-plugin/src/ui/helpers/was-anything-executed.ts b/packages/hardhat-plugin/src/ui/helpers/was-anything-executed.ts
new file mode 100644
index 00000000..f8191911
--- /dev/null
+++ b/packages/hardhat-plugin/src/ui/helpers/was-anything-executed.ts
@@ -0,0 +1,11 @@
+import { UiState } from "../types";
+
+/**
+ * Was anything executed during the deployment. We determine this based
+ * on whether the batcher indicates that there was at least one batch.
+ */
+export function wasAnythingExecuted({
+  batches,
+}: Pick<UiState, "batches">): boolean {
+  return batches.length > 0;
+}
diff --git a/packages/hardhat-plugin/src/ui/pretty-event-handler.ts b/packages/hardhat-plugin/src/ui/pretty-event-handler.ts
index 36421f5a..6def3977 100644
--- a/packages/hardhat-plugin/src/ui/pretty-event-handler.ts
+++ b/packages/hardhat-plugin/src/ui/pretty-event-handler.ts
@@ -38,6 +38,7 @@ import { calculateBatchDisplay } from "./helpers/calculate-batch-display";
 import { calculateDeployingModulePanel } from "./helpers/calculate-deploying-module-panel";
 import { calculateDeploymentCompleteDisplay } from "./helpers/calculate-deployment-complete-display";
 import { calculateStartingMessage } from "./helpers/calculate-starting-message";
+import { wasAnythingExecuted } from "./helpers/was-anything-executed";
 import {
   UiBatches,
   UiFuture,
@@ -61,6 +62,8 @@ export class PrettyEventHandler implements ExecutionEventListener {
     result: null,
     warnings: [],
     isResumed: null,
+    maxFeeBumps: 0,
+    gasBumps: {},
   };
 
   constructor(private _deploymentParams: DeploymentParameters = {}) {}
@@ -80,6 +83,7 @@ export class PrettyEventHandler implements ExecutionEventListener {
       moduleName: event.moduleName,
       deploymentDir: event.deploymentDir,
       isResumed: event.isResumed,
+      maxFeeBumps: event.maxFeeBumps,
     };
 
     process.stdout.write(calculateStartingMessage(this.state));
@@ -238,8 +242,16 @@ export class PrettyEventHandler implements ExecutionEventListener {
   public staticCallComplete(_event: StaticCallCompleteEvent): void {}
 
   public onchainInteractionBumpFees(
-    _event: OnchainInteractionBumpFeesEvent
-  ): void {}
+    event: OnchainInteractionBumpFeesEvent
+  ): void {
+    if (this._uiState.gasBumps[event.futureId] === undefined) {
+      this._uiState.gasBumps[event.futureId] = 0;
+    }
+
+    this._uiState.gasBumps[event.futureId] += 1;
+
+    this._redisplayCurrentBatch();
+  }
 
   public onchainInteractionDropped(
     _event: OnchainInteractionDroppedEvent
@@ -262,7 +274,7 @@ export class PrettyEventHandler implements ExecutionEventListener {
     };
 
     // If batches where executed, rerender the last batch
-    if (this.state.currentBatch > 0) {
+    if (wasAnythingExecuted(this.state)) {
       this._redisplayCurrentBatch();
     } else {
       // Otherwise only the completion panel will be shown so clear
diff --git a/packages/hardhat-plugin/src/ui/types.ts b/packages/hardhat-plugin/src/ui/types.ts
index ea7ba79e..6fabc5c2 100644
--- a/packages/hardhat-plugin/src/ui/types.ts
+++ b/packages/hardhat-plugin/src/ui/types.ts
@@ -62,6 +62,8 @@ export interface UiState {
   result: DeploymentResult | null;
   warnings: string[];
   isResumed: boolean | null;
+  maxFeeBumps: number;
+  gasBumps: Record<string, number>;
 }
 
 export interface AddressMap {
diff --git a/packages/ui/package.json b/packages/ui/package.json
index b0d9809c..ca7f4375 100644
--- a/packages/ui/package.json
+++ b/packages/ui/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@nomicfoundation/ignition-ui",
-  "version": "0.11.0",
+  "version": "0.11.1",
   "type": "module",
   "scripts": {
     "predev": "npm run regenerate-deployment-example",
@@ -16,7 +16,7 @@
   "dependencies": {},
   "devDependencies": {
     "@fontsource/roboto": "^5.0.8",
-    "@nomicfoundation/ignition-core": "^0.11.0",
+    "@nomicfoundation/ignition-core": "^0.11.1",
     "@types/chai": "^4.2.22",
     "@types/chai-as-promised": "^7.1.5",
     "@types/react": "^18.0.28",


Test patch:
diff --git a/packages/hardhat-plugin/test/load-module.ts b/packages/hardhat-plugin/test/load-module.ts
index eadd00bd..d684b95e 100644
--- a/packages/hardhat-plugin/test/load-module.ts
+++ b/packages/hardhat-plugin/test/load-module.ts
@@ -8,13 +8,6 @@ import { useEphemeralIgnitionProject } from "./use-ignition-project";
 describe("loadModule", function () {
   useEphemeralIgnitionProject("user-modules");
 
-  it("should throw if given a user module directory that does not exist", async () => {
-    assert.throws(
-      () => loadModule("/fake", "AFile.js"),
-      `Ignition directory /fake not found.`
-    );
-  });
-
   it("should throw if the full path to the module does not exist", () => {
     assert.throws(
       () => loadModule("ignition", "./ignition/modules/Fake.js"),
diff --git a/packages/hardhat-plugin/test/status.ts b/packages/hardhat-plugin/test/status.ts
index 0eb77b7c..efc95887 100644
--- a/packages/hardhat-plugin/test/status.ts
+++ b/packages/hardhat-plugin/test/status.ts
@@ -48,7 +48,7 @@ describe("status", function () {
           "FooModule#Foo": {
             id: "FooModule#Foo",
             contractName: "Foo",
-            address: "0x5fbdb2315678afecb367f032d93f642f64180aa3",
+            address: "0x5FbDB2315678afecb367f032d93F642f64180aa3",
           },
         },
       });
@@ -87,7 +87,7 @@ describe("status", function () {
           "FooModule#Foo": {
             id: "FooModule#Foo",
             contractName: "Foo",
-            address: "0x5fbdb2315678afecb367f032d93f642f64180aa3",
+            address: "0x5FbDB2315678afecb367f032d93F642f64180aa3",
           },
         },
       });
diff --git a/packages/hardhat-plugin/test/ui/helpers/calculate-batch-display.ts b/packages/hardhat-plugin/test/ui/helpers/calculate-batch-display.ts
index 25f3bf80..e5e80573 100644
--- a/packages/hardhat-plugin/test/ui/helpers/calculate-batch-display.ts
+++ b/packages/hardhat-plugin/test/ui/helpers/calculate-batch-display.ts
@@ -20,6 +20,8 @@ const exampleState: UiState = {
   result: null,
   warnings: [],
   isResumed: false,
+  maxFeeBumps: 0,
+  gasBumps: {},
 };
 
 describe("ui - calculate batch display", () => {
@@ -84,6 +86,26 @@ describe("ui - calculate batch display", () => {
     );
   });
 
+  it("should render a batch with a single timed out future", () => {
+    const expectedText = testFormat(`
+      Batch #1
+        Timed out ExampleModule#Token
+    `);
+
+    assertBatchText(
+      [
+        {
+          status: {
+            type: UiFutureStatusType.TIMEDOUT,
+          },
+          futureId: "ExampleModule#Token",
+        },
+      ],
+      3,
+      expectedText
+    );
+  });
+
   it("should render a batch with a single held future", () => {
     const expectedText = testFormat(`
       Batch #1
@@ -111,6 +133,7 @@ describe("ui - calculate batch display", () => {
       Batch #1
         Failed ExampleModule#Dex
         Held ExampleModule#ENS
+        Timed out ExampleModule#Registry
         Executed ExampleModule#Router
         Executing ExampleModule#Token...
       `);
@@ -136,6 +159,12 @@ describe("ui - calculate batch display", () => {
           },
           futureId: "ExampleModule#Router",
         },
+        {
+          status: {
+            type: UiFutureStatusType.TIMEDOUT,
+          },
+          futureId: "ExampleModule#Registry",
+        },
         {
           status: {
             type: UiFutureStatusType.HELD,
@@ -145,7 +174,7 @@ describe("ui - calculate batch display", () => {
           futureId: "ExampleModule#ENS",
         },
       ],
-      6,
+      7,
       expectedText
     );
   });
diff --git a/packages/hardhat-plugin/test/ui/helpers/calculate-deploying-module-panel-display.ts b/packages/hardhat-plugin/test/ui/helpers/calculate-deploying-module-panel-display.ts
index c0afbe2d..b5c4cf0b 100644
--- a/packages/hardhat-plugin/test/ui/helpers/calculate-deploying-module-panel-display.ts
+++ b/packages/hardhat-plugin/test/ui/helpers/calculate-deploying-module-panel-display.ts
@@ -18,6 +18,8 @@ describe("ui - calculate starting message display", () => {
     result: null,
     warnings: [],
     isResumed: null,
+    maxFeeBumps: 0,
+    gasBumps: {},
   };
 
   it("should display the deploying module message", () => {
diff --git a/packages/hardhat-plugin/test/ui/helpers/calculate-deployment-complete-display.ts b/packages/hardhat-plugin/test/ui/helpers/calculate-deployment-complete-display.ts
index 38c5df8c..5e59cc92 100644
--- a/packages/hardhat-plugin/test/ui/helpers/calculate-deployment-complete-display.ts
+++ b/packages/hardhat-plugin/test/ui/helpers/calculate-deployment-complete-display.ts
@@ -11,6 +11,7 @@ import { assert } from "chai";
 import chalk from "chalk";
 
 import { calculateDeploymentCompleteDisplay } from "../../../src/ui/helpers/calculate-deployment-complete-display";
+import { UiBatches, UiFutureStatusType } from "../../../src/ui/types";
 
 import { testFormat } from "./test-format";
 
@@ -18,6 +19,20 @@ describe("ui - calculate deployment complete display", () => {
   const exampleAddress = "0x1F98431c8aD98523631AE4a59f267346ea31F984";
   const differentAddress = "0x0011223344556677889900112233445566778899";
 
+  const exampleMultipleBatches: UiBatches = [
+    [
+      {
+        status: {
+          type: UiFutureStatusType.SUCCESS,
+          result: "0x0",
+        },
+        futureId: "MyModule#MyContract1",
+      },
+    ],
+  ];
+
+  const exampleNoBatches: UiBatches = [];
+
   describe("successful deployment", () => {
     it("should render a sucessful deployment", () => {
       const expectedText = testFormat(`
@@ -49,6 +64,9 @@ describe("ui - calculate deployment complete display", () => {
 
       const actualText = calculateDeploymentCompleteDisplay(event, {
         moduleName: "MyModule",
+        isResumed: false,
+        batches: exampleMultipleBatches,
+        deploymentDir: "",
       });
 
       assert.equal(actualText, expectedText);
@@ -72,6 +90,35 @@ describe("ui - calculate deployment complete display", () => {
 
       const actualText = calculateDeploymentCompleteDisplay(event, {
         moduleName: "MyModule",
+        isResumed: false,
+        batches: exampleMultipleBatches,
+        deploymentDir: "",
+      });
+
+      assert.equal(actualText, expectedText);
+    });
+
+    it("should render a resumed deployment with no new deployments", () => {
+      const expectedText = testFormat(`
+        [ MyModule ] Nothing new to deploy based on previous execution stored in test
+
+        ${chalk.bold("Deployed Addresses")}
+
+        ${chalk.italic("No contracts were deployed")}`);
+
+      const event: DeploymentCompleteEvent = {
+        type: ExecutionEventType.DEPLOYMENT_COMPLETE,
+        result: {
+          type: DeploymentResultType.SUCCESSFUL_DEPLOYMENT,
+          contracts: {},
+        },
+      };
+
+      const actualText = calculateDeploymentCompleteDisplay(event, {
+        moduleName: "MyModule",
+        isResumed: true,
+        batches: exampleNoBatches,
+        deploymentDir: "test",
       });
 
       assert.equal(actualText, expectedText);
@@ -112,6 +159,9 @@ describe("ui - calculate deployment complete display", () => {
 
       const actualText = calculateDeploymentCompleteDisplay(event, {
         moduleName: "MyModule",
+        isResumed: false,
+        batches: exampleMultipleBatches,
+        deploymentDir: "",
       });
 
       assert.equal(actualText, expectedText);
@@ -152,6 +202,9 @@ describe("ui - calculate deployment complete display", () => {
 
       const actualText = calculateDeploymentCompleteDisplay(event, {
         moduleName: "MyModule",
+        isResumed: false,
+        batches: exampleMultipleBatches,
+        deploymentDir: "",
       });
 
       assert.equal(actualText, expectedText);
@@ -184,6 +237,9 @@ describe("ui - calculate deployment complete display", () => {
 
       const actualText = calculateDeploymentCompleteDisplay(event, {
         moduleName: "MyModule",
+        isResumed: false,
+        batches: exampleMultipleBatches,
+        deploymentDir: "",
       });
 
       assert.equal(actualText, expectedText);
@@ -195,12 +251,10 @@ describe("ui - calculate deployment complete display", () => {
       const expectedText = testFormat(`
         [ MyModule ] failed ⛔
 
-        Futures with transactions unconfirmed after maximum fee bumps:
+        Futures timed out with transactions unconfirmed after maximum fee bumps:
          - MyModule#MyContract1
          - MyModule#AnotherContract1
 
-        Consider increasing the fee in your config.
-
         Futures failed during execution:
          - MyModule#MyContract3: Reverted with reason x
          - MyModule#AnotherContract3: Reverted with reason y
@@ -252,6 +306,9 @@ describe("ui - calculate deployment complete display", () => {
 
       const actualText = calculateDeploymentCompleteDisplay(event, {
         moduleName: "MyModule",
+        isResumed: false,
+        batches: exampleMultipleBatches,
+        deploymentDir: "",
       });
 
       assert.equal(actualText, expectedText);
diff --git a/packages/hardhat-plugin/test/ui/helpers/calculate-deployment-status-display.ts b/packages/hardhat-plugin/test/ui/helpers/calculate-deployment-status-display.ts
index 259a8492..b226bbb6 100644
--- a/packages/hardhat-plugin/test/ui/helpers/calculate-deployment-status-display.ts
+++ b/packages/hardhat-plugin/test/ui/helpers/calculate-deployment-status-display.ts
@@ -80,12 +80,10 @@ describe("ui - calculate deployment status display", () => {
       const expectedText = testFormat(`
         Deployment deployment-01 failed
 
-        Futures with transactions unconfirmed after maximum fee bumps:
+        Futures timed out with transactions unconfirmed after maximum fee bumps:
          - MyModule:MyContract1
          - MyModule:AnotherContract1
 
-        Consider increasing the fee in your config.
-
         Futures failed during execution:
          - MyModule:MyContract3: Reverted with reason x
          - MyModule:AnotherContract3: Reverted with reason y