Repository: NomicFoundation/hardhat-ignition
Pull request #628 (2023-11-06 21:11:07)

release: v0.11.2
## 0.11.2 - 2023-11-06

### Added

- Support account values in _send_ `to` in Module API ([#618](https://github.com/NomicFoundation/hardhat-ignition/issues/618))
- Validation check for duplicate module ids ([#608](https://github.com/NomicFoundation/hardhat-ignition/issues/608))

### Fixed

- Fix `ContractAt`s being recorded to `deployed_addresses.json` ([#607](https://github.com/NomicFoundation/hardhat-ignition/issues/607))



Patch:
diff --git a/examples/complete/package.json b/examples/complete/package.json
index 15cda975..e603b185 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.1",
+  "version": "0.11.2",
   "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.1",
+    "@nomicfoundation/hardhat-ignition": "^0.11.2",
     "@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 ac876b32..7017ad28 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.1",
+  "version": "0.11.2",
   "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.1",
+    "@nomicfoundation/hardhat-ignition": "^0.11.2",
     "@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 d02fac96..5f9222b9 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.1",
+  "version": "0.11.2",
   "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.1",
+    "@nomicfoundation/hardhat-ignition": "^0.11.2",
     "@nomicfoundation/hardhat-toolbox": "3.0.0",
     "hardhat": "^2.18.0",
     "prettier-plugin-solidity": "1.1.3"
diff --git a/examples/ts-sample/package.json b/examples/ts-sample/package.json
index 62016d38..b079a8c9 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.1",
+  "version": "0.11.2",
   "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.1",
+    "@nomicfoundation/hardhat-ignition": "^0.11.2",
     "@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 549de872..899606e8 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -19,7 +19,7 @@
         "open-cli": "7.2.0",
         "prettier": "2.4.1",
         "typedoc": "0.23.28",
-        "typescript": "^4.7.4"
+        "typescript": "^5.0.2"
       },
       "workspaces": {
         "packages": [
@@ -30,9 +30,9 @@
     },
     "examples/complete": {
       "name": "@nomicfoundation/ignition-complete-example",
-      "version": "0.11.1",
+      "version": "0.11.2",
       "devDependencies": {
-        "@nomicfoundation/hardhat-ignition": "^0.11.1",
+        "@nomicfoundation/hardhat-ignition": "^0.11.2",
         "@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.1",
+      "version": "0.11.2",
       "dependencies": {
         "@ensdomains/ens-contracts": "0.0.11"
       },
       "devDependencies": {
-        "@nomicfoundation/hardhat-ignition": "^0.11.1",
+        "@nomicfoundation/hardhat-ignition": "^0.11.2",
         "@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.1",
+      "version": "0.11.2",
       "devDependencies": {
-        "@nomicfoundation/hardhat-ignition": "^0.11.1",
+        "@nomicfoundation/hardhat-ignition": "^0.11.2",
         "@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.1",
+      "version": "0.11.2",
       "devDependencies": {
-        "@nomicfoundation/hardhat-ignition": "^0.11.1",
+        "@nomicfoundation/hardhat-ignition": "^0.11.2",
         "@nomicfoundation/hardhat-toolbox": "3.0.0",
         "hardhat": "^2.18.0",
         "prettier-plugin-solidity": "1.1.3"
@@ -19991,16 +19991,16 @@
       }
     },
     "node_modules/typescript": {
-      "version": "4.9.5",
-      "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz",
-      "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==",
+      "version": "5.0.4",
+      "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.0.4.tgz",
+      "integrity": "sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw==",
       "devOptional": true,
       "bin": {
         "tsc": "bin/tsc",
         "tsserver": "bin/tsserver"
       },
       "engines": {
-        "node": ">=4.2.0"
+        "node": ">=12.20"
       }
     },
     "node_modules/typical": {
@@ -21264,7 +21264,7 @@
     },
     "packages/core": {
       "name": "@nomicfoundation/ignition-core",
-      "version": "0.11.1",
+      "version": "0.11.2",
       "license": "MIT",
       "dependencies": {
         "@ethersproject/address": "5.6.1",
@@ -21303,7 +21303,7 @@
         "prettier": "2.4.1",
         "rimraf": "3.0.2",
         "ts-node": "10.9.1",
-        "typescript": "^4.7.4"
+        "typescript": "^5.0.2"
       }
     },
     "packages/core/node_modules/eslint-import-resolver-typescript": {
@@ -21374,11 +21374,11 @@
     },
     "packages/hardhat-plugin": {
       "name": "@nomicfoundation/hardhat-ignition",
-      "version": "0.11.1",
+      "version": "0.11.2",
       "license": "MIT",
       "dependencies": {
-        "@nomicfoundation/ignition-core": "^0.11.1",
-        "@nomicfoundation/ignition-ui": "^0.11.1",
+        "@nomicfoundation/ignition-core": "^0.11.2",
+        "@nomicfoundation/ignition-ui": "^0.11.2",
         "chalk": "^4.0.0",
         "debug": "^4.3.2",
         "ethers": "^6.7.0",
@@ -21421,7 +21421,7 @@
         "rimraf": "3.0.2",
         "sinon": "^14.0.0",
         "ts-node": "10.9.1",
-        "typescript": "^4.7.4"
+        "typescript": "^5.0.2"
       },
       "peerDependencies": {
         "@nomicfoundation/hardhat-ethers": "^3.0.4",
@@ -21496,10 +21496,10 @@
     },
     "packages/ui": {
       "name": "@nomicfoundation/ignition-ui",
-      "version": "0.11.1",
+      "version": "0.11.2",
       "devDependencies": {
         "@fontsource/roboto": "^5.0.8",
-        "@nomicfoundation/ignition-core": "^0.11.1",
+        "@nomicfoundation/ignition-core": "^0.11.2",
         "@types/chai": "^4.2.22",
         "@types/chai-as-promised": "^7.1.5",
         "@types/react": "^18.0.28",
@@ -22051,19 +22051,6 @@
         "url": "https://github.com/sponsors/sindresorhus"
       }
     },
-    "packages/ui/node_modules/typescript": {
-      "version": "5.2.2",
-      "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz",
-      "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==",
-      "dev": true,
-      "bin": {
-        "tsc": "bin/tsc",
-        "tsserver": "bin/tsserver"
-      },
-      "engines": {
-        "node": ">=14.17"
-      }
-    },
     "packages/ui/node_modules/uuid": {
       "version": "9.0.1",
       "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz",
diff --git a/package.json b/package.json
index 02d4dbd0..7d28dfe4 100644
--- a/package.json
+++ b/package.json
@@ -36,6 +36,6 @@
     "open-cli": "7.2.0",
     "prettier": "2.4.1",
     "typedoc": "0.23.28",
-    "typescript": "^4.7.4"
+    "typescript": "^5.0.2"
   }
 }
diff --git a/packages/core/CHANGELOG.md b/packages/core/CHANGELOG.md
index 5df058ad..1a76b68e 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.2 - 2023-11-06
+
+### Added
+
+- Support account values in _send_ `to` in Module API ([#618](https://github.com/NomicFoundation/hardhat-ignition/issues/618))
+
+### Fixed
+
+- Fix `ContractAt`s being recorded to `deployed_addresses.json` ([#607](https://github.com/NomicFoundation/hardhat-ignition/issues/607))
+
 ## 0.11.1 - 2023-10-30
 
 ### Added
diff --git a/packages/core/package.json b/packages/core/package.json
index 46d70927..a30fb71a 100644
--- a/packages/core/package.json
+++ b/packages/core/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@nomicfoundation/ignition-core",
-  "version": "0.11.1",
+  "version": "0.11.2",
   "license": "MIT",
   "author": "Nomic Foundation",
   "homepage": "https://hardhat.org",
@@ -72,7 +72,7 @@
     "prettier": "2.4.1",
     "rimraf": "3.0.2",
     "ts-node": "10.9.1",
-    "typescript": "^4.7.4"
+    "typescript": "^5.0.2"
   },
   "dependencies": {
     "@ethersproject/address": "5.6.1",
diff --git a/packages/core/src/build-module.ts b/packages/core/src/build-module.ts
index 6963c18e..5d1a4b9b 100644
--- a/packages/core/src/build-module.ts
+++ b/packages/core/src/build-module.ts
@@ -47,5 +47,28 @@ export function buildModule<
     moduleDefintionFunction,
   });
 
+  _checkForDuplicateModuleIds(ignitionModule);
+
   return ignitionModule;
 }
+
+/**
+ * Check to ensure that there are no duplicate module ids among the root
+ * module and its submodules.
+ */
+function _checkForDuplicateModuleIds(
+  ignitionModule: IgnitionModule<string, string, IgnitionModuleResult<string>>
+): void {
+  const duplicateModuleIds = [
+    ignitionModule.id,
+    ...Array.from(ignitionModule.submodules).map((submodule) => submodule.id),
+  ].filter((id, index, array) => array.indexOf(id) !== index);
+
+  if (duplicateModuleIds.length === 0) {
+    return;
+  }
+
+  throw new IgnitionError(ERRORS.MODULE.DUPLICATE_MODULE_ID, {
+    duplicateModuleIds: duplicateModuleIds.join(", "),
+  });
+}
diff --git a/packages/core/src/errors-list.ts b/packages/core/src/errors-list.ts
index 50c317f1..6b81e088 100644
--- a/packages/core/src/errors-list.ts
+++ b/packages/core/src/errors-list.ts
@@ -129,6 +129,11 @@ export const ERRORS = {
       message:
         "The callback passed to 'buildModule' for %moduleDefinitionId% returns a Promise; async callbacks are not allowed in 'buildModule'.",
     },
+    DUPLICATE_MODULE_ID: {
+      number: 204,
+      message:
+        "The following module ids are duplicated: %duplicateModuleIds%. Please make sure all module ids are unique.",
+    },
   },
   SERIALIZATION: {
     INVALID_FUTURE_ID: {
diff --git a/packages/core/src/ignition-module-serializer.ts b/packages/core/src/ignition-module-serializer.ts
index 5e8898cb..b55c5963 100644
--- a/packages/core/src/ignition-module-serializer.ts
+++ b/packages/core/src/ignition-module-serializer.ts
@@ -21,9 +21,11 @@ import {
 } from "./internal/topological-order";
 import { replaceWithinArg } from "./internal/utils/replace-within-arg";
 import {
+  isAccountRuntimeValue,
   isAddressResolvableFuture,
   isContractFuture,
   isFuture,
+  isModuleParameterRuntimeValue,
   isRuntimeValue,
 } from "./type-guards";
 import {
@@ -319,8 +321,10 @@ export class IgnitionModuleSerializer {
           ),
           to: isFuture(future.to)
             ? this._convertFutureToFutureToken(future.to)
-            : isRuntimeValue(future.to)
+            : isModuleParameterRuntimeValue(future.to)
             ? this._serializeModuleParamterRuntimeValue(future.to)
+            : isAccountRuntimeValue(future.to)
+            ? this._serializeAccountRuntimeValue(future.to)
             : future.to,
           value: isRuntimeValue(future.value)
             ? this._serializeModuleParamterRuntimeValue(future.value)
@@ -836,6 +840,8 @@ export class IgnitionModuleDeserializer {
             ? (this._deserializeModuleParameterRuntimeValue(
                 serializedFuture.to
               ) as ModuleParameterRuntimeValue<string>) // This is unsafe, but we only serialize valid valus
+            : this._isSerializedAccountRuntimeValue(serializedFuture.to)
+            ? this._deserializeAccountRuntimeValue(serializedFuture.to)
             : serializedFuture.to,
           this._isSerializedModuleParameterRuntimeValue(serializedFuture.value)
             ? (this._deserializeModuleParameterRuntimeValue(
diff --git a/packages/core/src/internal/execution/future-processor/future-processor.ts b/packages/core/src/internal/execution/future-processor/future-processor.ts
index 40cebccb..eb1e4046 100644
--- a/packages/core/src/internal/execution/future-processor/future-processor.ts
+++ b/packages/core/src/internal/execution/future-processor/future-processor.ts
@@ -96,6 +96,8 @@ export class FutureProcessor {
         exState !== undefined,
         `Invalid initialization message for future ${future.id}: it didn't create its execution state`
       );
+
+      await this._recordDeployedAddressIfNeeded(initMessage);
     }
 
     while (!isExecutionStateComplete(exState)) {
@@ -148,6 +150,14 @@ export class FutureProcessor {
         lastAppliedMessage.futureId,
         lastAppliedMessage.result.address
       );
+    } else if (
+      lastAppliedMessage.type ===
+      JournalMessageType.CONTRACT_AT_EXECUTION_STATE_INITIALIZE
+    ) {
+      await this._deploymentLoader.recordDeployedAddress(
+        lastAppliedMessage.futureId,
+        lastAppliedMessage.contractAddress
+      );
     }
   }
 
diff --git a/packages/core/src/internal/execution/future-processor/helpers/build-initialize-message-for.ts b/packages/core/src/internal/execution/future-processor/helpers/build-initialize-message-for.ts
index 74c6f0de..9dc540f6 100644
--- a/packages/core/src/internal/execution/future-processor/helpers/build-initialize-message-for.ts
+++ b/packages/core/src/internal/execution/future-processor/helpers/build-initialize-message-for.ts
@@ -20,6 +20,7 @@ import {
   resolveFutureFrom,
   resolveLibraries,
   resolveReadEventArgumentResult,
+  resolveSendToAddress,
   resolveValue,
 } from "./future-resolvers";
 
@@ -194,10 +195,11 @@ export async function buildInitializeMessageFor(
           future,
           strategy,
           {
-            to: resolveAddressLike(
+            to: resolveSendToAddress(
               future.to,
               deploymentState,
-              deploymentParameters
+              deploymentParameters,
+              accounts
             ),
             value: resolveValue(
               future.value,
diff --git a/packages/core/src/internal/execution/future-processor/helpers/future-resolvers.ts b/packages/core/src/internal/execution/future-processor/helpers/future-resolvers.ts
index b63ae092..657150c4 100644
--- a/packages/core/src/internal/execution/future-processor/helpers/future-resolvers.ts
+++ b/packages/core/src/internal/execution/future-processor/helpers/future-resolvers.ts
@@ -1,6 +1,7 @@
 import { isAddress } from "ethers";
 
 import {
+  isAccountRuntimeValue,
   isFuture,
   isModuleParameterRuntimeValue,
 } from "../../../../type-guards";
@@ -152,6 +153,30 @@ export function resolveAddressForContractFuture(
   return findAddressForContractFuture(deploymentState, contract.id);
 }
 
+/**
+ * Resolve a SendDataFuture's "to" field to a valid ethereum address.
+ */
+export function resolveSendToAddress(
+  to:
+    | string
+    | AddressResolvableFuture
+    | ModuleParameterRuntimeValue<string>
+    | AccountRuntimeValue,
+  deploymentState: DeploymentState,
+  deploymentParameters: DeploymentParameters,
+  accounts: string[]
+): string {
+  if (typeof to === "string") {
+    return to;
+  }
+
+  if (isAccountRuntimeValue(to)) {
+    return resolveAccountRuntimeValue(to, accounts);
+  }
+
+  return resolveAddressLike(to, deploymentState, deploymentParameters);
+}
+
 /**
  * Resolve the given address like to a valid ethereum address. Futures
  * will be resolved to their result then runtime checked to ensure
diff --git a/packages/core/src/internal/execution/utils/address.ts b/packages/core/src/internal/execution/utils/address.ts
index e1ad5292..7ef0c658 100644
--- a/packages/core/src/internal/execution/utils/address.ts
+++ b/packages/core/src/internal/execution/utils/address.ts
@@ -1,7 +1,14 @@
-import { getAddress, isAddress } from "ethers";
+import { isAddress as ethersIsAddress, getAddress } from "ethers";
 
 import { assertIgnitionInvariant } from "../../utils/assertions";
 
+/**
+ * Is the string a valid ethereum address?
+ */
+export function isAddress(address: string): boolean {
+  return ethersIsAddress(address);
+}
+
 /**
  * Returns a normalized and checksumed address for the given address.
  *
diff --git a/packages/core/src/internal/module-builder.ts b/packages/core/src/internal/module-builder.ts
index ef6b7a63..611b5fa4 100644
--- a/packages/core/src/internal/module-builder.ts
+++ b/packages/core/src/internal/module-builder.ts
@@ -48,6 +48,7 @@ import {
   StaticCallOptions,
 } from "../types/module-builder";
 
+import { equalAddresses, isAddress } from "./execution/utils/address";
 import {
   AccountRuntimeValueImplementation,
   ArtifactContractAtFutureImplementation,
@@ -837,7 +838,11 @@ class IgnitionModuleBuilderImplementation<
 
   public send(
     id: string,
-    to: string | AddressResolvableFuture | ModuleParameterRuntimeValue<string>,
+    to:
+      | string
+      | AddressResolvableFuture
+      | ModuleParameterRuntimeValue<string>
+      | AccountRuntimeValue,
     value?: bigint | ModuleParameterRuntimeValue<bigint>,
     data?: string,
     options: SendDataOptions = {}
@@ -859,6 +864,7 @@ class IgnitionModuleBuilderImplementation<
     this._assertValidValue(val, this.send);
     this._assertValidData(data, this.send);
     this._assertValidFrom(options.from, this.send);
+    this._assertUniqueToAndFrom(to, options.from, this.send);
     /* validation end */
 
     const future = new SendDataFutureImplementation(
@@ -1105,15 +1111,21 @@ m.${futureConstructor.name}(..., { id: "MyUniqueId"})`,
     address:
       | string
       | AddressResolvableFuture
-      | ModuleParameterRuntimeValue<string>,
+      | ModuleParameterRuntimeValue<string>
+      | AccountRuntimeValue,
     func: (...[]: any[]) => any
   ) {
+    if (typeof address === "string" && !isAddress(address)) {
+      return this._throwErrorWithStackTrace(`Invalid address given`, func);
+    }
+
     if (
       typeof address !== "string" &&
       !isModuleParameterRuntimeValue(address) &&
+      !isAccountRuntimeValue(address) &&
       !isAddressResolvableFuture(address)
     ) {
-      this._throwErrorWithStackTrace(`Invalid address given`, func);
+      return this._throwErrorWithStackTrace(`Invalid address given`, func);
     }
   }
 
@@ -1125,4 +1137,34 @@ m.${futureConstructor.name}(..., { id: "MyUniqueId"})`,
       this._throwErrorWithStackTrace(`Invalid data given`, func);
     }
   }
+
+  private _assertUniqueToAndFrom(
+    to:
+      | string
+      | AddressResolvableFuture
+      | ModuleParameterRuntimeValue<string>
+      | AccountRuntimeValue,
+    from: string | AccountRuntimeValue | undefined,
+    func: (...[]: any[]) => any
+  ) {
+    if (
+      typeof to === "string" &&
+      typeof from === "string" &&
+      equalAddresses(to, from)
+    ) {
+      this._throwErrorWithStackTrace(
+        `The "to" and "from" addresses are the same`,
+        func
+      );
+    } else if (
+      isAccountRuntimeValue(to) &&
+      isAccountRuntimeValue(from) &&
+      to.accountIndex === from.accountIndex
+    ) {
+      this._throwErrorWithStackTrace(
+        `The "to" and "from" addresses are the same`,
+        func
+      );
+    }
+  }
 }
diff --git a/packages/core/src/internal/module.ts b/packages/core/src/internal/module.ts
index 6931cbd1..44c8ec22 100644
--- a/packages/core/src/internal/module.ts
+++ b/packages/core/src/internal/module.ts
@@ -242,7 +242,8 @@ export class SendDataFutureImplementation
     public readonly to:
       | string
       | AddressResolvableFuture
-      | ModuleParameterRuntimeValue<string>,
+      | ModuleParameterRuntimeValue<string>
+      | AccountRuntimeValue,
     public readonly value: bigint | ModuleParameterRuntimeValue<bigint>,
     public readonly data: string | undefined,
     public readonly from: string | AccountRuntimeValue | undefined
diff --git a/packages/core/src/internal/reconciliation/futures/reconcileSendData.ts b/packages/core/src/internal/reconciliation/futures/reconcileSendData.ts
index bb74949b..4586ebef 100644
--- a/packages/core/src/internal/reconciliation/futures/reconcileSendData.ts
+++ b/packages/core/src/internal/reconciliation/futures/reconcileSendData.ts
@@ -1,5 +1,5 @@
 import { SendDataFuture } from "../../../types/module";
-import { resolveAddressLike } from "../../execution/future-processor/helpers/future-resolvers";
+import { resolveSendToAddress } from "../../execution/future-processor/helpers/future-resolvers";
 import { SendDataExecutionState } from "../../execution/types/execution-state";
 import { compare } from "../helpers/compare";
 import { reconcileData } from "../helpers/reconcile-data";
@@ -12,10 +12,11 @@ export function reconcileSendData(
   executionState: SendDataExecutionState,
   context: ReconciliationContext
 ): ReconciliationFutureResult {
-  const resolvedAddress = resolveAddressLike(
+  const resolvedAddress = resolveSendToAddress(
     future.to,
     context.deploymentState,
-    context.deploymentParameters
+    context.deploymentParameters,
+    context.accounts
   );
 
   let result = compare(
diff --git a/packages/core/src/types/module-builder.ts b/packages/core/src/types/module-builder.ts
index 5555459a..55c873f6 100644
--- a/packages/core/src/types/module-builder.ts
+++ b/packages/core/src/types/module-builder.ts
@@ -206,7 +206,11 @@ export interface IgnitionModuleBuilder {
 
   send(
     id: string,
-    to: string | AddressResolvableFuture | ModuleParameterRuntimeValue<string>,
+    to:
+      | string
+      | AddressResolvableFuture
+      | ModuleParameterRuntimeValue<string>
+      | AccountRuntimeValue,
     value?: bigint | ModuleParameterRuntimeValue<bigint>,
     data?: string,
     options?: SendDataOptions
diff --git a/packages/core/src/types/module.ts b/packages/core/src/types/module.ts
index 9f0a7385..d38d2b3d 100644
--- a/packages/core/src/types/module.ts
+++ b/packages/core/src/types/module.ts
@@ -312,7 +312,11 @@ export interface SendDataFuture {
   id: string;
   module: IgnitionModule;
   dependencies: Set<Future>;
-  to: string | AddressResolvableFuture | ModuleParameterRuntimeValue<string>;
+  to:
+    | string
+    | AddressResolvableFuture
+    | ModuleParameterRuntimeValue<string>
+    | AccountRuntimeValue;
   value: bigint | ModuleParameterRuntimeValue<bigint>;
   data: string | undefined;
   from: string | AccountRuntimeValue | undefined;
diff --git a/packages/core/src/types/serialization.ts b/packages/core/src/types/serialization.ts
index 237f22b3..87087daf 100644
--- a/packages/core/src/types/serialization.ts
+++ b/packages/core/src/types/serialization.ts
@@ -201,7 +201,11 @@ export interface SerializedReadEventArgumentFuture
  */
 export interface SerializedSendDataFuture extends BaseSerializedFuture {
   type: FutureType.SEND_DATA;
-  to: string | FutureToken | SerializedModuleParameterRuntimeValue;
+  to:
+    | string
+    | FutureToken
+    | SerializedModuleParameterRuntimeValue
+    | SerializedAccountRuntimeValue;
   value: SerializedBigInt | SerializedModuleParameterRuntimeValue;
   data: string | undefined;
   from: string | SerializedAccountRuntimeValue | undefined;
diff --git a/packages/hardhat-plugin/CHANGELOG.md b/packages/hardhat-plugin/CHANGELOG.md
index f80c707c..10be2a77 100644
--- a/packages/hardhat-plugin/CHANGELOG.md
+++ b/packages/hardhat-plugin/CHANGELOG.md
@@ -4,6 +4,17 @@ 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.2 - 2023-11-06
+
+### Added
+
+- Support account values in _send_ `to` in Module API ([#618](https://github.com/NomicFoundation/hardhat-ignition/issues/618))
+- Validation check for duplicate module ids ([#608](https://github.com/NomicFoundation/hardhat-ignition/issues/608))
+
+### Fixed
+
+- Fix `ContractAt`s being recorded to `deployed_addresses.json` ([#607](https://github.com/NomicFoundation/hardhat-ignition/issues/607))
+
 ## 0.11.1 - 2023-10-30
 
 ### Added
diff --git a/packages/hardhat-plugin/package.json b/packages/hardhat-plugin/package.json
index 9098cc9c..6d2578ed 100644
--- a/packages/hardhat-plugin/package.json
+++ b/packages/hardhat-plugin/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@nomicfoundation/hardhat-ignition",
-  "version": "0.11.1",
+  "version": "0.11.2",
   "license": "MIT",
   "author": "Nomic Foundation",
   "homepage": "https://hardhat.org",
@@ -85,15 +85,15 @@
     "rimraf": "3.0.2",
     "sinon": "^14.0.0",
     "ts-node": "10.9.1",
-    "typescript": "^4.7.4"
+    "typescript": "^5.0.2"
   },
   "peerDependencies": {
     "@nomicfoundation/hardhat-ethers": "^3.0.4",
     "hardhat": "^2.18.0"
   },
   "dependencies": {
-    "@nomicfoundation/ignition-core": "^0.11.1",
-    "@nomicfoundation/ignition-ui": "^0.11.1",
+    "@nomicfoundation/ignition-core": "^0.11.2",
+    "@nomicfoundation/ignition-ui": "^0.11.2",
     "chalk": "^4.0.0",
     "debug": "^4.3.2",
     "ethers": "^6.7.0",
diff --git a/packages/hardhat-plugin/src/utils/shouldBeHardhatPluginError.ts b/packages/hardhat-plugin/src/utils/shouldBeHardhatPluginError.ts
index a6c4d8bb..c6b2cb2f 100644
--- a/packages/hardhat-plugin/src/utils/shouldBeHardhatPluginError.ts
+++ b/packages/hardhat-plugin/src/utils/shouldBeHardhatPluginError.ts
@@ -9,9 +9,9 @@ import type { IgnitionError } from "@nomicfoundation/ignition-core";
  *    - If there's an exception that doesn't fit in either category, let's discuss it and review the categories.
  */
 const whitelist = [
-  200, 201, 202, 203, 403, 404, 405, 600, 601, 602, 700, 701, 702, 703, 704,
-  705, 706, 707, 708, 709, 710, 711, 712, 713, 714, 715, 716, 717, 718, 719,
-  720, 721, 722, 723, 724, 725, 726, 800, 900,
+  200, 201, 202, 203, 204, 403, 404, 405, 600, 601, 602, 700, 701, 702, 703,
+  704, 705, 706, 707, 708, 709, 710, 711, 712, 713, 714, 715, 716, 717, 718,
+  719, 720, 721, 722, 723, 724, 725, 726, 800, 900,
 ];
 
 export function shouldBeHardhatPluginError(error: IgnitionError): boolean {
diff --git a/packages/ui/package.json b/packages/ui/package.json
index ca7f4375..0b663e81 100644
--- a/packages/ui/package.json
+++ b/packages/ui/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@nomicfoundation/ignition-ui",
-  "version": "0.11.1",
+  "version": "0.11.2",
   "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.1",
+    "@nomicfoundation/ignition-core": "^0.11.2",
     "@types/chai": "^4.2.22",
     "@types/chai-as-promised": "^7.1.5",
     "@types/react": "^18.0.28",


Test patch:
diff --git a/packages/core/test/contractAt.ts b/packages/core/test/contractAt.ts
index 73bf8c81..06b9a249 100644
--- a/packages/core/test/contractAt.ts
+++ b/packages/core/test/contractAt.ts
@@ -14,6 +14,8 @@ import {
 } from "./helpers";
 
 describe("contractAt", () => {
+  const exampleAddress = "0x1F98431c8aD98523631AE4a59f267346ea31F984";
+
   const fakeArtifact: Artifact = {
     abi: [],
     contractName: "",
@@ -23,7 +25,7 @@ describe("contractAt", () => {
 
   it("should be able to setup a contract at a given address", () => {
     const moduleWithContractFromArtifact = buildModule("Module1", (m) => {
-      const contract1 = m.contractAt("Contract1", "0xtest");
+      const contract1 = m.contractAt("Contract1", exampleAddress);
 
       return { contract1 };
     });
@@ -40,7 +42,7 @@ describe("contractAt", () => {
     // Stores the address
     assert.deepStrictEqual(
       moduleWithContractFromArtifact.results.contract1.address,
-      "0xtest"
+      exampleAddress
     );
 
     // 1 contract future
@@ -53,7 +55,7 @@ describe("contractAt", () => {
   it("should be able to pass an after dependency", () => {
     const moduleWithDependentContracts = buildModule("Module1", (m) => {
       const example = m.contract("Example");
-      const another = m.contractAt("Another", "0xtest", {
+      const another = m.contractAt("Another", exampleAddress, {
         after: [example],
       });
 
@@ -123,20 +125,12 @@ describe("contractAt", () => {
   describe("passing id", () => {
     it("should be able to deploy the same contract twice by passing an id", () => {
       const moduleWithSameContractTwice = buildModule("Module1", (m) => {
-        const sameContract1 = m.contractAt(
-          "SameContract",
-          "0x123",
-
-          { id: "first" }
-        );
-        const sameContract2 = m.contractAt(
-          "SameContract",
-          "0x123",
-
-          {
-            id: "second",
-          }
-        );
+        const sameContract1 = m.contractAt("SameContract", exampleAddress, {
+          id: "first",
+        });
+        const sameContract2 = m.contractAt("SameContract", exampleAddress, {
+          id: "second",
+        });
 
         return { sameContract1, sameContract2 };
       });
@@ -156,8 +150,8 @@ describe("contractAt", () => {
       assert.throws(
         () =>
           buildModule("Module1", (m) => {
-            const sameContract1 = m.contractAt("SameContract", "0x123");
-            const sameContract2 = m.contractAt("SameContract", "0x123");
+            const sameContract1 = m.contractAt("SameContract", exampleAddress);
+            const sameContract2 = m.contractAt("SameContract", exampleAddress);
 
             return { sameContract1, sameContract2 };
           }),
@@ -171,14 +165,9 @@ m.contractAt(..., { id: "MyUniqueId"})`
       assert.throws(
         () =>
           buildModule("Module1", (m) => {
-            const sameContract1 = m.contractAt(
-              "SameContract",
-              "0x123",
-
-              {
-                id: "same",
-              }
-            );
+            const sameContract1 = m.contractAt("SameContract", exampleAddress, {
+              id: "same",
+            });
             const sameContract2 = m.contractAt(
               "SameContract",
               "0x123",
@@ -212,7 +201,7 @@ m.contractAt(..., { id: "MyUniqueId"})`
 
     it("should not validate an invalid artifact", async () => {
       const module = buildModule("Module1", (m) => {
-        const another = m.contractAt("Another", "");
+        const another = m.contractAt("Another", exampleAddress);
 
         return { another };
       });
@@ -223,7 +212,7 @@ m.contractAt(..., { id: "MyUniqueId"})`
         await validateNamedContractAt(
           future as any,
           setupMockArtifactResolver({
-            Another: {} as any,
+            Another: { _kind: "invalid artifact" } as any,
           }),
           {},
           []
diff --git a/packages/core/test/contractAtFromArtifact.ts b/packages/core/test/contractAtFromArtifact.ts
index 64ff21b7..6e5c536f 100644
--- a/packages/core/test/contractAtFromArtifact.ts
+++ b/packages/core/test/contractAtFromArtifact.ts
@@ -14,6 +14,9 @@ import {
 } from "./helpers";
 
 describe("contractAtFromArtifact", () => {
+  const exampleAddress = "0x1F98431c8aD98523631AE4a59f267346ea31F984";
+  const differentAddress = "0xBA12222222228d8Ba445958a75a0704d566BF2C8";
+
   const fakeArtifact: Artifact = {
     abi: [],
     contractName: "",
@@ -23,7 +26,7 @@ describe("contractAtFromArtifact", () => {
 
   it("should be able to setup a contract at a given address", () => {
     const moduleWithContractFromArtifact = buildModule("Module1", (m) => {
-      const contract1 = m.contractAt("Contract1", fakeArtifact, "0xtest");
+      const contract1 = m.contractAt("Contract1", fakeArtifact, exampleAddress);
 
       return { contract1 };
     });
@@ -40,7 +43,7 @@ describe("contractAtFromArtifact", () => {
     // Stores the address
     assert.deepStrictEqual(
       moduleWithContractFromArtifact.results.contract1.address,
-      "0xtest"
+      exampleAddress
     );
 
     // 1 contract future
@@ -53,7 +56,7 @@ describe("contractAtFromArtifact", () => {
   it("should be able to pass an after dependency", () => {
     const moduleWithDependentContracts = buildModule("Module1", (m) => {
       const example = m.contract("Example");
-      const another = m.contractAt("Another", fakeArtifact, "0xtest", {
+      const another = m.contractAt("Another", fakeArtifact, exampleAddress, {
         after: [example],
       });
 
@@ -130,13 +133,13 @@ describe("contractAtFromArtifact", () => {
         const sameContract1 = m.contractAt(
           "SameContract",
           fakeArtifact,
-          "0x123",
+          exampleAddress,
           { id: "first" }
         );
         const sameContract2 = m.contractAt(
           "SameContract",
           fakeArtifact,
-          "0x123",
+          differentAddress,
           {
             id: "second",
           }
@@ -163,7 +166,7 @@ describe("contractAtFromArtifact", () => {
             const sameContract1 = m.contractAt(
               "SameContract",
               fakeArtifact,
-              "0x123"
+              exampleAddress
             );
             const sameContract2 = m.contractAt(
               "SameContract",
@@ -186,7 +189,7 @@ m.contractAt(..., { id: "MyUniqueId"})`
             const sameContract1 = m.contractAt(
               "SameContract",
               fakeArtifact,
-              "0x123",
+              exampleAddress,
               {
                 id: "same",
               }
diff --git a/packages/core/test/execution/future-processor/named-contract-at-deploy.ts b/packages/core/test/execution/future-processor/named-contract-at-deploy.ts
new file mode 100644
index 00000000..fd56b00e
--- /dev/null
+++ b/packages/core/test/execution/future-processor/named-contract-at-deploy.ts
@@ -0,0 +1,42 @@
+import { assert } from "chai";
+
+import { NamedArtifactContractAtFuture } from "../../../src";
+import { deploymentStateReducer } from "../../../src/internal/execution/reducers/deployment-state-reducer";
+import { NamedContractAtFutureImplementation } from "../../../src/internal/module";
+
+import { setupFutureProcessor } from "./utils";
+
+const exampleAddress = "0x1F98431c8aD98523631AE4a59f267346ea31F984";
+
+describe("future processor", () => {
+  const initialDeploymentState = deploymentStateReducer(undefined);
+
+  describe("deploying a named contractAt", () => {
+    it("should record the address of a contractAt future", async () => {
+      // Arrange
+      const fakeModule = {} as any;
+
+      const deploymentFuture: NamedArtifactContractAtFuture<string> =
+        new NamedContractAtFutureImplementation(
+          "MyModule:TestContract",
+          fakeModule,
+          "TestContract",
+          exampleAddress
+        );
+
+      const { processor, storedDeployedAddresses } = setupFutureProcessor(
+        (() => {}) as any,
+        {}
+      );
+
+      // Act
+      await processor.processFuture(deploymentFuture, initialDeploymentState);
+
+      // Assert
+      assert.equal(
+        storedDeployedAddresses["MyModule:TestContract"],
+        exampleAddress
+      );
+    });
+  });
+});
diff --git a/packages/core/test/ignition-module-serializer.ts b/packages/core/test/ignition-module-serializer.ts
index 6a88e6da..4ba0bfba 100644
--- a/packages/core/test/ignition-module-serializer.ts
+++ b/packages/core/test/ignition-module-serializer.ts
@@ -20,6 +20,9 @@ import {
 } from "../src/types/module";
 
 describe("stored deployment serializer", () => {
+  const exampleAddress = "0x1F98431c8aD98523631AE4a59f267346ea31F984";
+  const differentAddress = "0xBA12222222228d8Ba445958a75a0704d566BF2C8";
+
   describe("contract", () => {
     it("should serialize a contract deployment", () => {
       const module = buildModule("Module1", (m) => {
@@ -121,7 +124,7 @@ describe("stored deployment serializer", () => {
   describe("contractAt", () => {
     it("should serialize a contractAt", () => {
       const module = buildModule("Module1", (m) => {
-        const contract1 = m.contractAt("Contract1", "0x0");
+        const contract1 = m.contractAt("Contract1", exampleAddress);
 
         return { contract1 };
       });
@@ -131,7 +134,7 @@ describe("stored deployment serializer", () => {
 
     it("should serialize a contractAt with a future address", () => {
       const module = buildModule("Module1", (m) => {
-        const contract1 = m.contractAt("Contract1", "0x0");
+        const contract1 = m.contractAt("Contract1", exampleAddress);
         const call = m.staticCall(contract1, "getAddress");
         const contract2 = m.contractAt("Contract2", call);
 
@@ -143,8 +146,8 @@ describe("stored deployment serializer", () => {
 
     it("should serialize a contractAt with dependency", () => {
       const module = buildModule("Module1", (m) => {
-        const contract1 = m.contractAt("Contract1", "0x0");
-        const contract2 = m.contractAt("Contract2", "0x0", {
+        const contract1 = m.contractAt("Contract1", exampleAddress);
+        const contract2 = m.contractAt("Contract2", differentAddress, {
           after: [contract1],
         });
 
@@ -165,7 +168,11 @@ describe("stored deployment serializer", () => {
 
     it("should serialize a contractAt", () => {
       const module = buildModule("Module1", (m) => {
-        const contract1 = m.contractAt("Contract1", fakeArtifact, "0x0");
+        const contract1 = m.contractAt(
+          "Contract1",
+          fakeArtifact,
+          exampleAddress
+        );
 
         return { contract1 };
       });
@@ -175,7 +182,11 @@ describe("stored deployment serializer", () => {
 
     it("should serialize a contractAt with a future address", () => {
       const module = buildModule("Module1", (m) => {
-        const contract1 = m.contractAt("Contract1", fakeArtifact, "0x0");
+        const contract1 = m.contractAt(
+          "Contract1",
+          fakeArtifact,
+          exampleAddress
+        );
         const call = m.staticCall(contract1, "getAddress");
         const contract2 = m.contractAt("Contract2", fakeArtifact, call);
 
@@ -187,10 +198,19 @@ describe("stored deployment serializer", () => {
 
     it("should serialize a contractAt with dependency", () => {
       const module = buildModule("Module1", (m) => {
-        const contract1 = m.contractAt("Contract1", fakeArtifact, "0x0");
-        const contract2 = m.contractAt("Contract2", fakeArtifact, "0x0", {
-          after: [contract1],
-        });
+        const contract1 = m.contractAt(
+          "Contract1",
+          fakeArtifact,
+          exampleAddress
+        );
+        const contract2 = m.contractAt(
+          "Contract2",
+          fakeArtifact,
+          differentAddress,
+          {
+            after: [contract1],
+          }
+        );
 
         return { contract1, contract2 };
       });
@@ -402,6 +422,43 @@ describe("stored deployment serializer", () => {
     });
   });
 
+  describe("send", () => {
+    it("should serialize a send", () => {
+      const module = buildModule("Module1", (m) => {
+        m.send("test_send", exampleAddress, 12n, "example_data");
+
+        return {};
+      });
+
+      assertSerializableModuleIn(module);
+    });
+
+    it("should serialize a send with dependencies", () => {
+      const module = buildModule("Module1", (m) => {
+        const contract1 = m.contract("Contract1");
+        const contract2 = m.contract("Contract2");
+
+        m.send("test_send", contract1, 0n, undefined, { after: [contract2] });
+
+        return {};
+      });
+
+      assertSerializableModuleIn(module);
+    });
+
+    it("should serialize a send where the to is from an account", () => {
+      const module = buildModule("Module1", (m) => {
+        const givenAccount = m.getAccount(3);
+
+        m.send("test_send", givenAccount, 12n, "example_data");
+
+        return {};
+      });
+
+      assertSerializableModuleIn(module);
+    });
+  });
+
   describe("useModule", () => {
     it("should serialize a deployment leveraging useModule", () => {
       const submodule = buildModule("Submodule", (m) => {
diff --git a/packages/core/test/readEventArgument.ts b/packages/core/test/readEventArgument.ts
index 3d6a7e24..0a20503d 100644
--- a/packages/core/test/readEventArgument.ts
+++ b/packages/core/test/readEventArgument.ts
@@ -9,6 +9,8 @@ import { validateReadEventArgument } from "../src/internal/validation/futures/va
 import { assertValidationError, setupMockArtifactResolver } from "./helpers";
 
 describe("Read event argument", () => {
+  const exampleAddress = "0x1F98431c8aD98523631AE4a59f267346ea31F984";
+
   describe("creating modules with it", () => {
     it("should support reading arguments from all the futures that can emit them", () => {
       const fakeArtifact: Artifact = {
@@ -235,7 +237,7 @@ m.readEventArgument(..., { id: "MyUniqueId"})`
         assert.throws(
           () =>
             buildModule("Module1", (m) => {
-              const send = m.send("id", "test", 42n);
+              const send = m.send("id", exampleAddress, 42n);
 
               m.readEventArgument(send, "SomeEvent", "someArg");
 
diff --git a/packages/core/test/reconciliation/futures/reconcileSendData.ts b/packages/core/test/reconciliation/futures/reconcileSendData.ts
index 6303f365..4a304a0a 100644
--- a/packages/core/test/reconciliation/futures/reconcileSendData.ts
+++ b/packages/core/test/reconciliation/futures/reconcileSendData.ts
@@ -99,6 +99,27 @@ describe("Reconciliation - send data", () => {
     );
   });
 
+  it("should reconcile between `to` and an account value", async () => {
+    const moduleDefinition = buildModule("Module", (m) => {
+      const givenAccount = m.getAccount(2);
+
+      m.send("test_send", givenAccount, 0n, "example_data");
+
+      return {};
+    });
+
+    await assertSuccessReconciliation(
+      moduleDefinition,
+      createDeploymentState({
+        ...exampleSendState,
+        id: "Module#test_send",
+        status: ExecutionStatus.STARTED,
+        to: exampleAccounts[2],
+        data: "example_data",
+      })
+    );
+  });
+
   it("should find changes to the to address unreconciliable", async () => {
     const moduleDefinition = buildModule("Module", (m) => {
       m.send("test_send", differentAddress, 0n, "example_data");
diff --git a/packages/core/test/send.ts b/packages/core/test/send.ts
index 599bde1c..200ac0c9 100644
--- a/packages/core/test/send.ts
+++ b/packages/core/test/send.ts
@@ -18,9 +18,12 @@ import {
 } from "./helpers";
 
 describe("send", () => {
+  const exampleAddress = "0x1F98431c8aD98523631AE4a59f267346ea31F984";
+  const differentAddress = "0xBA12222222228d8Ba445958a75a0704d566BF2C8";
+
   it("should be able to setup a send", () => {
     const moduleWithASingleContract = buildModule("Module1", (m) => {
-      m.send("test_send", "0xtest", 0n, "test-data");
+      m.send("test_send", exampleAddress, 0n, "test-data");
 
       return {};
     });
@@ -77,7 +80,7 @@ describe("send", () => {
   it("should be able to pass one contract as an after dependency of a send", () => {
     const moduleWithDependentContracts = buildModule("Module1", (m) => {
       const example = m.contract("Example");
-      m.send("test_send", "0xtest", 0n, "", { after: [example] });
+      m.send("test_send", exampleAddress, 0n, "", { after: [example] });
 
       return { example };
     });
@@ -102,7 +105,7 @@ describe("send", () => {
 
   it("should be able to pass a value", () => {
     const moduleWithDependentContracts = buildModule("Module1", (m) => {
-      m.send("test_send", "0xtest", 42n, "");
+      m.send("test_send", exampleAddress, 42n, "");
 
       return {};
     });
@@ -122,7 +125,7 @@ describe("send", () => {
 
   it("Should be able to pass a ModuleParameterRuntimeValue as a value option", () => {
     const moduleWithDependentContracts = buildModule("Module1", (m) => {
-      m.send("test_send", "0xtest", m.getParameter("value"), "");
+      m.send("test_send", exampleAddress, m.getParameter("value"), "");
 
       return {};
     });
@@ -145,7 +148,7 @@ describe("send", () => {
 
   it("should be able to pass a string as from option", () => {
     const moduleWithDependentContracts = buildModule("Module1", (m) => {
-      m.send("test_send", "0xtest", 0n, "", { from: "0x2" });
+      m.send("test_send", exampleAddress, 0n, "", { from: differentAddress });
 
       return {};
     });
@@ -160,12 +163,12 @@ describe("send", () => {
       assert.fail("Not a send data future");
     }
 
-    assert.equal(sendFuture.from, "0x2");
+    assert.equal(sendFuture.from, differentAddress);
   });
 
   it("Should be able to pass an AccountRuntimeValue as from option", () => {
     const moduleWithDependentContracts = buildModule("Module1", (m) => {
-      m.send("test_send", "0xtest", 0n, "", { from: m.getAccount(1) });
+      m.send("test_send", exampleAddress, 0n, "", { from: m.getAccount(1) });
 
       return {};
     });
@@ -184,6 +187,27 @@ describe("send", () => {
     assert.equal(sendFuture.from.accountIndex, 1);
   });
 
+  it("Should be able to pass an AccountRuntimeValue as address", () => {
+    const moduleWithDependentContracts = buildModule("Module1", (m) => {
+      m.send("test_send", m.getAccount(1), 0n, "");
+
+      return {};
+    });
+
+    assert.isDefined(moduleWithDependentContracts);
+
+    const sendFuture = [...moduleWithDependentContracts.futures].find(
+      ({ id }) => id === "Module1#test_send"
+    );
+
+    if (!(sendFuture instanceof SendDataFutureImplementation)) {
+      assert.fail("Not a send data future");
+    }
+
+    assertInstanceOf(sendFuture.to, AccountRuntimeValueImplementation);
+    assert.equal(sendFuture.to.accountIndex, 1);
+  });
+
   it("Should be able to pass a module param as address", () => {
     const module = buildModule("Module", (m) => {
       const paramWithDefault = m.getParameter("addressWithDefault", "0x000000");
@@ -215,8 +239,8 @@ describe("send", () => {
   describe("passing id", () => {
     it("should be able to call the same function twice by passing an id", () => {
       const moduleWithSameCallTwice = buildModule("Module1", (m) => {
-        m.send("first", "0xtest", 0n, "test");
-        m.send("second", "0xtest", 0n, "test");
+        m.send("first", exampleAddress, 0n, "test");
+        m.send("second", exampleAddress, 0n, "test");
 
         return {};
       });
@@ -239,8 +263,8 @@ describe("send", () => {
       assert.throws(
         () =>
           buildModule("Module1", (m) => {
-            m.send("test_send", "0xtest", 0n, "test");
-            m.send("test_send", "0xtest", 0n, "test");
+            m.send("test_send", exampleAddress, 0n, "test");
+            m.send("test_send", exampleAddress, 0n, "test");
 
             return {};
           }),
@@ -252,8 +276,8 @@ describe("send", () => {
       assert.throws(
         () =>
           buildModule("Module1", (m) => {
-            m.send("first", "0xtest", 0n, "test");
-            m.send("first", "0xtest", 0n, "test");
+            m.send("first", exampleAddress, 0n, "test");
+            m.send("first", exampleAddress, 0n, "test");
             return {};
           }),
         'The future id "first" is already used, please provide a different one.'
@@ -268,7 +292,7 @@ describe("send", () => {
           () =>
             buildModule("Module1", (m) => {
               const another = m.contract("Another", []);
-              m.send("id", "test", 42 as any);
+              m.send("id", exampleAddress, 42 as any);
 
               return { another };
             }),
@@ -281,7 +305,7 @@ describe("send", () => {
           () =>
             buildModule("Module1", (m) => {
               const another = m.contract("Another", []);
-              m.send("id", "test", 0n, 42 as any);
+              m.send("id", exampleAddress, 0n, 42 as any);
 
               return { another };
             }),
@@ -316,6 +340,30 @@ describe("send", () => {
           /Invalid address given/
         );
       });
+
+      it("should not validate a `to` as a string that is not an address", () => {
+        assert.throws(
+          () =>
+            buildModule("Module1", (m) => {
+              m.send("id", "0xnot-an-address", 0n, "");
+
+              return {};
+            }),
+          /Invalid address given/
+        );
+      });
+
+      it("should not allow the from and to address to be the same", () => {
+        assert.throws(
+          () =>
+            buildModule("Module1", (m) => {
+              m.send("id", m.getAccount(1), 0n, "", { from: m.getAccount(1) });
+
+              return {};
+            }),
+          /The "to" and "from" addresses are the same/
+        );
+      });
     });
 
     it("should not validate a missing module parameter", async () => {
@@ -361,7 +409,7 @@ describe("send", () => {
     it("should not validate a module parameter of the wrong type for value", async () => {
       const module = buildModule("Module1", (m) => {
         const p = m.getParameter("p", false as unknown as bigint);
-        m.send("id", "0xasdf", p, "");
+        m.send("id", exampleAddress, p, "");
 
         return {};
       });
@@ -384,7 +432,7 @@ describe("send", () => {
     it("should validate a module parameter of the correct type for value", async () => {
       const module = buildModule("Module1", (m) => {
         const p = m.getParameter("p", 42n);
-        m.send("id", "0xasdf", p, "");
+        m.send("id", exampleAddress, p, "");
 
         return {};
       });
@@ -401,7 +449,7 @@ describe("send", () => {
     it("should not validate a negative account index", async () => {
       const module = buildModule("Module1", (m) => {
         const account = m.getAccount(-1);
-        m.send("id", "0xasdf", 0n, "", { from: account });
+        m.send("id", exampleAddress, 0n, "", { from: account });
 
         return {};
       });
@@ -424,7 +472,7 @@ describe("send", () => {
     it("should not validate an account index greater than the number of available accounts", async () => {
       const module = buildModule("Module1", (m) => {
         const account = m.getAccount(1);
-        m.send("id", "0xasdf", 0n, "", { from: account });
+        m.send("id", exampleAddress, 0n, "", { from: account });
 
         return {};
       });
diff --git a/packages/core/test/validations/id-rules.ts b/packages/core/test/validations/id-rules.ts
index c7917a7a..1cf22876 100644
--- a/packages/core/test/validations/id-rules.ts
+++ b/packages/core/test/validations/id-rules.ts
@@ -16,6 +16,22 @@ describe("id rules", () => {
         });
       }, /IGN201: The moduleId "MyModule:v2" is invalid. Module ids can only have alphanumerics and underscore, and they must start with an alphanumeric./);
     });
+
+    it("should not allow duplicate module ids", () => {
+      const MyModule = buildModule("MyModule", (m) => {
+        const myContract = m.contract("MyContract");
+
+        return { myContract };
+      });
+
+      assert.throws(() => {
+        buildModule("MyModule", (m) => {
+          const { myContract } = m.useModule(MyModule);
+
+          return { myContract };
+        });
+      }, /IGN204: The following module ids are duplicated: MyModule\. Please make sure all module ids are unique\./);
+    });
   });
 
   // Windows is not going to allow these characters in filenames
@@ -233,7 +249,7 @@ describe("id rules", () => {
           buildModule("MyModule", (m) => {
             m.contract("sourceName.sol:MyContract");
             m.contract("asd/sourceName.sol:MyContract2");
-            m.contractAt("sourceName.sol:MyContractAt", "0x1234");
+            m.contractAt("sourceName.sol:MyContractAt", exampleAddress);
             m.library("sourceName.sol:MyLibrary");
 
             return {};