Repository: hashgraph/hedera-local-node
Pull request #738 (2024-08-15 14:49:30)

chore: Bump axios from 1.6.8 to 1.7.4 in /test/smoke
Bumps [axios](https://github.com/axios/axios) from 1.6.8 to 1.7.4.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a href="https://github.com/axios/axios/releases">axios's releases</a>.</em></p>
<blockquote>
<h2>Release v1.7.4</h2>
<h2>Release notes:</h2>
<h3>Bug Fixes</h3>
<ul>
<li><strong>sec:</strong> CVE-2024-39338 (<a href="https://redirect.github.com/axios/axios/issues/6539">#6539</a>) (<a href="https://redirect.github.com/axios/axios/issues/6543">#6543</a>) (<a href="https://github.com/axios/axios/commit/6b6b605eaf73852fb2dae033f1e786155959de3a">6b6b605</a>)</li>
<li><strong>sec:</strong> disregard protocol-relative URL to remediate SSRF (<a href="https://redirect.github.com/axios/axios/issues/6539">#6539</a>) (<a href="https://github.com/axios/axios/commit/07a661a2a6b9092c4aa640dcc7f724ec5e65bdda">07a661a</a>)</li>
</ul>
<h3>Contributors to this release</h3>
<ul>
<li><!-- raw HTML omitted --> <a href="https://github.com/levpachmanov" title="+47/-11 ([#6543](https://github.com/axios/axios/issues/6543) )">Lev Pachmanov</a></li>
<li><!-- raw HTML omitted --> <a href="https://github.com/hainenber" title="+49/-4 ([#6539](https://github.com/axios/axios/issues/6539) )">Đỗ Trọng Hải</a></li>
</ul>
<h2>Release v1.7.3</h2>
<h2>Release notes:</h2>
<h3>Bug Fixes</h3>
<ul>
<li><strong>adapter:</strong> fix progress event emitting; (<a href="https://redirect.github.com/axios/axios/issues/6518">#6518</a>) (<a href="https://github.com/axios/axios/commit/e3c76fc9bdd03aa4d98afaf211df943e2031453f">e3c76fc</a>)</li>
<li><strong>fetch:</strong> fix withCredentials request config (<a href="https://redirect.github.com/axios/axios/issues/6505">#6505</a>) (<a href="https://github.com/axios/axios/commit/85d4d0ea0aae91082f04e303dec46510d1b4e787">85d4d0e</a>)</li>
<li><strong>xhr:</strong> return original config on errors from XHR adapter (<a href="https://redirect.github.com/axios/axios/issues/6515">#6515</a>) (<a href="https://github.com/axios/axios/commit/8966ee7ea62ecbd6cfb39a905939bcdab5cf6388">8966ee7</a>)</li>
</ul>
<h3>Contributors to this release</h3>
<ul>
<li><!-- raw HTML omitted --> <a href="https://github.com/DigitalBrainJS" title="+211/-159 ([#6518](https://github.com/axios/axios/issues/6518) [#6519](https://github.com/axios/axios/issues/6519) )">Dmitriy Mozgovoy</a></li>
<li><!-- raw HTML omitted --> <a href="https://github.com/ValeraS" title="+3/-3 ([#6515](https://github.com/axios/axios/issues/6515) )">Valerii Sidorenko</a></li>
<li><!-- raw HTML omitted --> <a href="https://github.com/prianyu" title="+2/-2 ([#6505](https://github.com/axios/axios/issues/6505) )">prianYu</a></li>
</ul>
<h2>Release v1.7.2</h2>
<h2>Release notes:</h2>
<h3>Bug Fixes</h3>
<ul>
<li><strong>fetch:</strong> enhance fetch API detection; (<a href="https://redirect.github.com/axios/axios/issues/6413">#6413</a>) (<a href="https://github.com/axios/axios/commit/4f79aef81b7c4644328365bfc33acf0a9ef595bc">4f79aef</a>)</li>
</ul>
<h3>Contributors to this release</h3>
<ul>
<li><!-- raw HTML omitted --> <a href="https://github.com/DigitalBrainJS" title="+3/-3 ([#6413](https://github.com/axios/axios/issues/6413) )">Dmitriy Mozgovoy</a></li>
</ul>
<h2>Release v1.7.1</h2>
<h2>Release notes:</h2>
<h3>Bug Fixes</h3>
<ul>
<li><strong>fetch:</strong> fixed ReferenceError issue when TextEncoder is not available in the environment; (<a href="https://redirect.github.com/axios/axios/issues/6410">#6410</a>) (<a href="https://github.com/axios/axios/commit/733f15fe5bd2d67e1fadaee82e7913b70d45dc5e">733f15f</a>)</li>
</ul>
<h3>Contributors to this release</h3>
<ul>
<li><!-- raw HTML omitted --> <a href="https://github.com/DigitalBrainJS" title="+14/-9 ([#6410](https://github.com/axios/axios/issues/6410) )">Dmitriy Mozgovoy</a></li>
</ul>
<h2>Release v1.7.0</h2>
<h2>Release notes:</h2>
<h3>Features</h3>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a href="https://github.com/axios/axios/blob/v1.x/CHANGELOG.md">axios's changelog</a>.</em></p>
<blockquote>
<h2><a href="https://github.com/axios/axios/compare/v1.7.3...v1.7.4">1.7.4</a> (2024-08-13)</h2>
<h3>Bug Fixes</h3>
<ul>
<li><strong>sec:</strong> CVE-2024-39338 (<a href="https://redirect.github.com/axios/axios/issues/6539">#6539</a>) (<a href="https://redirect.github.com/axios/axios/issues/6543">#6543</a>) (<a href="https://github.com/axios/axios/commit/6b6b605eaf73852fb2dae033f1e786155959de3a">6b6b605</a>)</li>
<li><strong>sec:</strong> disregard protocol-relative URL to remediate SSRF (<a href="https://redirect.github.com/axios/axios/issues/6539">#6539</a>) (<a href="https://github.com/axios/axios/commit/07a661a2a6b9092c4aa640dcc7f724ec5e65bdda">07a661a</a>)</li>
</ul>
<h3>Contributors to this release</h3>
<ul>
<li><!-- raw HTML omitted --> <a href="https://github.com/levpachmanov" title="+47/-11 ([#6543](https://github.com/axios/axios/issues/6543) )">Lev Pachmanov</a></li>
<li><!-- raw HTML omitted --> <a href="https://github.com/hainenber" title="+49/-4 ([#6539](https://github.com/axios/axios/issues/6539) )">Đỗ Trọng Hải</a></li>
</ul>
<h2><a href="https://github.com/axios/axios/compare/v1.7.2...v1.7.3">1.7.3</a> (2024-08-01)</h2>
<h3>Bug Fixes</h3>
<ul>
<li><strong>adapter:</strong> fix progress event emitting; (<a href="https://redirect.github.com/axios/axios/issues/6518">#6518</a>) (<a href="https://github.com/axios/axios/commit/e3c76fc9bdd03aa4d98afaf211df943e2031453f">e3c76fc</a>)</li>
<li><strong>fetch:</strong> fix withCredentials request config (<a href="https://redirect.github.com/axios/axios/issues/6505">#6505</a>) (<a href="https://github.com/axios/axios/commit/85d4d0ea0aae91082f04e303dec46510d1b4e787">85d4d0e</a>)</li>
<li><strong>xhr:</strong> return original config on errors from XHR adapter (<a href="https://redirect.github.com/axios/axios/issues/6515">#6515</a>) (<a href="https://github.com/axios/axios/commit/8966ee7ea62ecbd6cfb39a905939bcdab5cf6388">8966ee7</a>)</li>
</ul>
<h3>Contributors to this release</h3>
<ul>
<li><!-- raw HTML omitted --> <a href="https://github.com/DigitalBrainJS" title="+211/-159 ([#6518](https://github.com/axios/axios/issues/6518) [#6519](https://github.com/axios/axios/issues/6519) )">Dmitriy Mozgovoy</a></li>
<li><!-- raw HTML omitted --> <a href="https://github.com/ValeraS" title="+3/-3 ([#6515](https://github.com/axios/axios/issues/6515) )">Valerii Sidorenko</a></li>
<li><!-- raw HTML omitted --> <a href="https://github.com/prianyu" title="+2/-2 ([#6505](https://github.com/axios/axios/issues/6505) )">prianYu</a></li>
</ul>
<h2><a href="https://github.com/axios/axios/compare/v1.7.1...v1.7.2">1.7.2</a> (2024-05-21)</h2>
<h3>Bug Fixes</h3>
<ul>
<li><strong>fetch:</strong> enhance fetch API detection; (<a href="https://redirect.github.com/axios/axios/issues/6413">#6413</a>) (<a href="https://github.com/axios/axios/commit/4f79aef81b7c4644328365bfc33acf0a9ef595bc">4f79aef</a>)</li>
</ul>
<h3>Contributors to this release</h3>
<ul>
<li><!-- raw HTML omitted --> <a href="https://github.com/DigitalBrainJS" title="+3/-3 ([#6413](https://github.com/axios/axios/issues/6413) )">Dmitriy Mozgovoy</a></li>
</ul>
<h2><a href="https://github.com/axios/axios/compare/v1.7.0...v1.7.1">1.7.1</a> (2024-05-20)</h2>
<h3>Bug Fixes</h3>
<ul>
<li><strong>fetch:</strong> fixed ReferenceError issue when TextEncoder is not available in the environment; (<a href="https://redirect.github.com/axios/axios/issues/6410">#6410</a>) (<a href="https://github.com/axios/axios/commit/733f15fe5bd2d67e1fadaee82e7913b70d45dc5e">733f15f</a>)</li>
</ul>
<h3>Contributors to this release</h3>
<ul>
<li><!-- raw HTML omitted --> <a href="https://github.com/DigitalBrainJS" title="+14/-9 ([#6410](https://github.com/axios/axios/issues/6410) )">Dmitriy Mozgovoy</a></li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a href="https://github.com/axios/axios/commit/abd24a7367726616e60dfc04cb394b4be37cf597"><code>abd24a7</code></a> chore(release): v1.7.4 (<a href="https://redirect.github.com/axios/axios/issues/6544">#6544</a>)</li>
<li><a href="https://github.com/axios/axios/commit/6b6b605eaf73852fb2dae033f1e786155959de3a"><code>6b6b605</code></a> fix(sec): CVE-2024-39338 (<a href="https://redirect.github.com/axios/axios/issues/6539">#6539</a>) (<a href="https://redirect.github.com/axios/axios/issues/6543">#6543</a>)</li>
<li><a href="https://github.com/axios/axios/commit/07a661a2a6b9092c4aa640dcc7f724ec5e65bdda"><code>07a661a</code></a> fix(sec): disregard protocol-relative URL to remediate SSRF (<a href="https://redirect.github.com/axios/axios/issues/6539">#6539</a>)</li>
<li><a href="https://github.com/axios/axios/commit/c6cce43cd94489f655f4488c5a50ecaf781c94f2"><code>c6cce43</code></a> chore(release): v1.7.3 (<a href="https://redirect.github.com/axios/axios/issues/6521">#6521</a>)</li>
<li><a href="https://github.com/axios/axios/commit/e3c76fc9bdd03aa4d98afaf211df943e2031453f"><code>e3c76fc</code></a> fix(adapter): fix progress event emitting; (<a href="https://redirect.github.com/axios/axios/issues/6518">#6518</a>)</li>
<li><a href="https://github.com/axios/axios/commit/85d4d0ea0aae91082f04e303dec46510d1b4e787"><code>85d4d0e</code></a> fix(fetch): fix withCredentials request config (<a href="https://redirect.github.com/axios/axios/issues/6505">#6505</a>)</li>
<li><a href="https://github.com/axios/axios/commit/92cd8ed94362f929d3d0ed85ca84296c0ac8fd6d"><code>92cd8ed</code></a> chore(github): update ISSUE_TEMPLATE.md (<a href="https://redirect.github.com/axios/axios/issues/6519">#6519</a>)</li>
<li><a href="https://github.com/axios/axios/commit/8966ee7ea62ecbd6cfb39a905939bcdab5cf6388"><code>8966ee7</code></a> fix(xhr): return original config on errors from XHR adapter (<a href="https://redirect.github.com/axios/axios/issues/6515">#6515</a>)</li>
<li><a href="https://github.com/axios/axios/commit/0e4f9fa29077ebee4499facea6be1492b42e8a26"><code>0e4f9fa</code></a> chore(release): v1.7.2 (<a href="https://redirect.github.com/axios/axios/issues/6414">#6414</a>)</li>
<li><a href="https://github.com/axios/axios/commit/4f79aef81b7c4644328365bfc33acf0a9ef595bc"><code>4f79aef</code></a> fix(fetch): enhance fetch API detection; (<a href="https://redirect.github.com/axios/axios/issues/6413">#6413</a>)</li>
<li>Additional commits viewable in <a href="https://github.com/axios/axios/compare/v1.6.8...v1.7.4">compare view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=axios&package-manager=npm_and_yarn&previous-version=1.6.8&new-version=1.7.4)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
You can disable automated security fix PRs for this repo from the [Security Alerts page](https://github.com/hashgraph/hedera-local-node/network/alerts).

</details>

sonarcloud[bot]: ## [![Quality Gate Passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-passed-20px.png 'Quality Gate Passed')](https://sonarcloud.io/dashboard?id=hashgraph_hedera-local-node&pullRequest=738) **Quality Gate passed**  
Issues  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 New issues](https://sonarcloud.io/project/issues?id=hashgraph_hedera-local-node&pullRequest=738&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/accepted-16px.png '') [0 Accepted issues](https://sonarcloud.io/project/issues?id=hashgraph_hedera-local-node&pullRequest=738&issueStatuses=ACCEPTED)

Measures  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=hashgraph_hedera-local-node&pullRequest=738&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Coverage on New Code](https://sonarcloud.io/component_measures?id=hashgraph_hedera-local-node&pullRequest=738&metric=new_coverage&view=list)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Duplication on New Code](https://sonarcloud.io/component_measures?id=hashgraph_hedera-local-node&pullRequest=738&metric=new_duplicated_lines_density&view=list)  
  
[See analysis details on SonarCloud](https://sonarcloud.io/dashboard?id=hashgraph_hedera-local-node&pullRequest=738)



Patch:
diff --git a/package-lock.json b/package-lock.json
index bc6fab1..0679886 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -2465,9 +2465,9 @@
       "dev": true
     },
     "node_modules/@types/yargs": {
-      "version": "17.0.32",
-      "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.32.tgz",
-      "integrity": "sha512-xQ67Yc/laOG5uMfX/093MRlGGCIBzZMarVa+gfNKJxWAIgykYpVGkBdbqEzGDDfCrVUj6Hiff4mTZ5BA6TmAog==",
+      "version": "17.0.33",
+      "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz",
+      "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==",
       "dev": true,
       "dependencies": {
         "@types/yargs-parser": "*"
@@ -7837,9 +7837,9 @@
       "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A=="
     },
     "node_modules/mocha": {
-      "version": "10.7.0",
-      "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.7.0.tgz",
-      "integrity": "sha512-v8/rBWr2VO5YkspYINnvu81inSz2y3ODJrhO175/Exzor1RcEZZkizgE2A+w/CAXXoESS8Kys5E62dOHGHzULA==",
+      "version": "10.7.3",
+      "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.7.3.tgz",
+      "integrity": "sha512-uQWxAu44wwiACGqjbPYmjo7Lg8sFrS3dQe7PP2FQI+woptP4vZXSMcfMyFL/e1yFEeEpV4RtyTpZROOKmxis+A==",
       "dependencies": {
         "ansi-colors": "^4.1.3",
         "browser-stdout": "^1.3.1",
diff --git a/package.json b/package.json
index 5d5d1a7..6617898 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@hashgraph/hedera-local",
-  "version": "2.29.0",
+  "version": "2.29.1",
   "description": "Developer tooling for running Local Hedera Network (Consensus + Mirror Nodes).",
   "main": "index.ts",
   "scripts": {
diff --git a/src/constants.ts b/src/constants.ts
index 5d29eff..9740cfc 100644
--- a/src/constants.ts
+++ b/src/constants.ts
@@ -36,6 +36,10 @@ export const CONTAINERS = [
     },
 ];
 
+export const SDK_ERRORS = {
+    FAILED_TO_FIND_A_HEALTHY_NODE: "failed to find a healthy working node",
+}
+
 export const NETWORK_PREFIX = 'hedera-';
 
 export const CONSENSUS_NODE_LABEL = "network-node";
@@ -76,7 +80,7 @@ export const TRACE_COLOR = '\x1b[37m'
 // check success
 export const CHECK_SUCCESS = `${INFO_COLOR}[✔︎]${COLOR_RESET}`
 // check warn
-export const CHEKC_WARN = `${WARNING_COLOR}[!]${COLOR_RESET}`
+export const CHECK_WARN = `${WARNING_COLOR}[!]${COLOR_RESET}`
 // check fail
 export const CHECK_FAIL = `${ERROR_COLOR}[✘]${COLOR_RESET}`
 // waiting
diff --git a/src/services/LoggerService.ts b/src/services/LoggerService.ts
index 65f3b5b..aa6d5b4 100644
--- a/src/services/LoggerService.ts
+++ b/src/services/LoggerService.ts
@@ -22,7 +22,7 @@ import { log, screen, Widgets } from 'blessed';
 import terminal from 'blessed-terminal';
 import {
     CHECK_FAIL,
-    CHEKC_WARN,
+    CHECK_WARN,
     COLOR_DIM,
     COLOR_RESET,
     CONSENSUS_NODE_LABEL,
@@ -33,7 +33,7 @@ import {
     MIRROR_NODE_LABEL,
     RELAY_LABEL,
     TRACE_COLOR,
-    WARNING_COLOR
+    WARNING_COLOR,
 } from '../constants';
 import { AccountCreationState } from '../state/AccountCreationState';
 import { VerboseLevel } from '../types/VerboseLevel';
@@ -233,7 +233,7 @@ export class LoggerService implements IService{
         if (this.verboseLevel < VerboseLevel.WARNING) {
             return;
         }
-        const msgToLog = LoggerService.messageCompute(`${CHEKC_WARN} ${msg}`, module, VerboseLevel.WARNING);
+        const msgToLog = LoggerService.messageCompute(`${CHECK_WARN} ${msg}`, module, VerboseLevel.WARNING);
         this.writeToLog(msgToLog, module);
     }
 
diff --git a/src/state/AccountCreationState.ts b/src/state/AccountCreationState.ts
index 43925f4..09b2cba 100644
--- a/src/state/AccountCreationState.ts
+++ b/src/state/AccountCreationState.ts
@@ -33,11 +33,18 @@ import { ClientService } from '../services/ClientService';
 import {
     privateKeysAliasECDSA,
     privateKeysECDSA,
-    privateKeysED25519
+    privateKeysED25519,
 } from '../configuration/accountConfiguration.json';
-import { ACCOUNT_CREATION_STATE_INIT_MESSAGE, CHECK_SUCCESS, EVM_ADDRESSES_BLOCKLIST_FILE_RELATIVE_PATH, LOADING } from '../constants';
+import {
+    ACCOUNT_CREATION_STATE_INIT_MESSAGE,
+    CHECK_SUCCESS,
+    EVM_ADDRESSES_BLOCKLIST_FILE_RELATIVE_PATH,
+    LOADING,
+    SDK_ERRORS,
+} from '../constants';
 import local from '../configuration/local.json';
 import { AccountUtils } from '../utils/AccountUtils';
+import { RetryUtils } from '../utils/RetryUtils';
 
 /**
  * Represents the state of account creation.
@@ -256,10 +263,14 @@ export class AccountCreationState implements IState {
         accountData.forEach((account) => {
             const { privateKey, balance } = account;
             const client = this.clientService.getClient();
-
-            const createAccountPromise: Promise<Account> = AccountUtils
-                .createAccount(privateKey.publicKey, balance, client)
-                .then((accountInfo) => {
+            const publicKey = privateKey.publicKey;
+
+            const createAccountPromise: Promise<Account> = RetryUtils.retryTask(
+                () => AccountUtils.createAccount(publicKey, balance, client),
+                {
+                    shouldRetry: error => this.shouldRetry(error),
+                    doOnRetry: error => this.doOnRetry(error)
+                }).then((accountInfo) => {
                     const address = accountInfo.accountId.toSolidityAddress();
                     return {
                         accountId: accountInfo.accountId.toString(),
@@ -305,9 +316,12 @@ export class AccountCreationState implements IState {
             const client = this.clientService.getClient();
             const aliasAccountId = privateKey.publicKey.toAccountId(0, 0);
 
-            const createAccountPromise: Promise<Account> = AccountUtils
-                .createAliasedAccount(aliasAccountId, balance, client)
-                .then((accountInfo) => {
+            const createAccountPromise: Promise<Account> = RetryUtils.retryTask(
+                () => AccountUtils.createAliasedAccount(aliasAccountId, balance, client),
+                {
+                    shouldRetry: error => this.shouldRetry(error),
+                    doOnRetry: error => this.doOnRetry(error)
+                }).then((accountInfo) => {
                     const address = privateKey.publicKey.toEvmAddress();
                     return {
                         accountId: accountInfo.accountId.toString(),
@@ -479,4 +493,12 @@ export class AccountCreationState implements IState {
             this.stateName
         );
     }
+
+    private shouldRetry = (error: unknown): boolean => {
+        return error?.toString().includes(SDK_ERRORS.FAILED_TO_FIND_A_HEALTHY_NODE) ?? false;
+    }
+
+    private doOnRetry = (error: unknown): void => {
+        this.logger.warn(`Error occurred during task execution: "${error?.toString()}"`, this.stateName);
+    }
 }
diff --git a/src/utils/RetryUtils.ts b/src/utils/RetryUtils.ts
new file mode 100644
index 0000000..336881f
--- /dev/null
+++ b/src/utils/RetryUtils.ts
@@ -0,0 +1,75 @@
+/*-
+ *
+ * Hedera Local Node
+ *
+ * Copyright (C) 2023-2024 Hedera Hashgraph, LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+export class RetryUtils {
+
+  /**
+   * Retries a task up to a specified number of times.
+   *
+   * @param {() => Promise<T>} task - The task to retry.
+   * @param {Object} options - The options for the retry.
+   * @param [options.maxAttempts=3] - The maximum number of attempts.
+   * @param [options.backOff=500] - The time to wait between attempts in milliseconds.
+   * @param [options.doOnRetry=(error, attempt) => {}] - The function to call on each retry.
+   * @returns {Promise<T>} - A promise that resolves with the result of the task.
+   * @template T
+   */
+  public static async retryTask<T>(
+    task: () => Promise<T>,
+    {
+      maxRetries = 3,
+      backOff = 500,
+      shouldRetry = (_error: unknown) => true,
+      doOnRetry = (_error: unknown) => {},
+    }: {
+      maxRetries?: number;
+      backOff?: number;
+      shouldRetry?: (error: unknown) => boolean;
+      doOnRetry?: (error: unknown) => void;
+    } = {}
+  ): Promise<T> {
+    let retries = 0;
+    while (retries < maxRetries) {
+      try {
+        // eslint-disable-next-line no-await-in-loop
+        return await task();
+      } catch (error) {
+        if (!shouldRetry(error) || retries === maxRetries - 1) {
+          throw error;
+        }
+        doOnRetry(error);
+        await this.delay(backOff);
+      }
+      retries += 1;
+    }
+    /* istanbul ignore next */
+    throw new Error('Unreachable code');
+  }
+
+  /**
+   * Delays the execution of the task.
+   * @param ms The time to wait in milliseconds.
+   * @returns {Promise<void>} A promise that resolves after the delay.
+   * @private
+   */
+  private static delay(ms: number): Promise<void> {
+    return new Promise((resolve) => setTimeout(resolve, ms));
+  }
+}


Test patch:
diff --git a/test/cypress/package-lock.json b/test/cypress/package-lock.json
index acf8dc0..6496e0a 100644
--- a/test/cypress/package-lock.json
+++ b/test/cypress/package-lock.json
@@ -1761,9 +1761,9 @@
       }
     },
     "node_modules/cypress": {
-      "version": "13.13.2",
-      "resolved": "https://registry.npmjs.org/cypress/-/cypress-13.13.2.tgz",
-      "integrity": "sha512-PvJQU33933NvS1StfzEb8/mu2kMy4dABwCF+yd5Bi7Qly1HOVf+Bufrygee/tlmty/6j5lX+KIi8j9Q3JUMbhA==",
+      "version": "13.13.3",
+      "resolved": "https://registry.npmjs.org/cypress/-/cypress-13.13.3.tgz",
+      "integrity": "sha512-hUxPrdbJXhUOTzuML+y9Av7CKoYznbD83pt8g3klgpioEha0emfx4WNIuVRx0C76r0xV2MIwAW9WYiXfVJYFQw==",
       "dev": true,
       "hasInstallScript": true,
       "dependencies": {
@@ -6092,9 +6092,9 @@
       "dev": true
     },
     "cypress": {
-      "version": "13.13.2",
-      "resolved": "https://registry.npmjs.org/cypress/-/cypress-13.13.2.tgz",
-      "integrity": "sha512-PvJQU33933NvS1StfzEb8/mu2kMy4dABwCF+yd5Bi7Qly1HOVf+Bufrygee/tlmty/6j5lX+KIi8j9Q3JUMbhA==",
+      "version": "13.13.3",
+      "resolved": "https://registry.npmjs.org/cypress/-/cypress-13.13.3.tgz",
+      "integrity": "sha512-hUxPrdbJXhUOTzuML+y9Av7CKoYznbD83pt8g3klgpioEha0emfx4WNIuVRx0C76r0xV2MIwAW9WYiXfVJYFQw==",
       "dev": true,
       "requires": {
         "@cypress/request": "^3.0.1",
diff --git a/test/smoke/package-lock.json b/test/smoke/package-lock.json
index f0d1d91..5599633 100644
--- a/test/smoke/package-lock.json
+++ b/test/smoke/package-lock.json
@@ -2277,9 +2277,9 @@
       }
     },
     "node_modules/axios": {
-      "version": "1.6.8",
-      "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.8.tgz",
-      "integrity": "sha512-v/ZHtJDU39mDpyBoFVkETcd/uNdxrWRrg3bKpOKzXFA6Bvqopts6ALSMU3y6ijYxbw2B+wPrIv46egTzJXCLGQ==",
+      "version": "1.7.4",
+      "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.4.tgz",
+      "integrity": "sha512-DukmaFRnY6AzAALSH4J2M3k6PkaC+MfaAGdEERRWcC9q3/TWQwLpHR8ZRLKTdQ3aBDL64EdluRDjJqKw+BPZEw==",
       "dev": true,
       "peer": true,
       "dependencies": {
diff --git a/test/unit/states/AccountCreationState.spec.ts b/test/unit/states/AccountCreationState.spec.ts
index 4d5c35e..3ef1a01 100644
--- a/test/unit/states/AccountCreationState.spec.ts
+++ b/test/unit/states/AccountCreationState.spec.ts
@@ -28,7 +28,8 @@ import { getTestBed } from '../testBed';
 import { SinonSandbox, SinonSpy, SinonStub, SinonStubbedInstance } from 'sinon';
 import {
   ACCOUNT_CREATION_STARTING_ASYNCHRONOUS_BLOCKLIST_MESSAGE,
-  ACCOUNT_CREATION_STATE_INIT_MESSAGE
+  ACCOUNT_CREATION_STATE_INIT_MESSAGE,
+  SDK_ERRORS,
 } from '../../../src/constants';
 import { IOBserver } from '../../../src/controller/IObserver';
 import { AccountUtils } from '../../../src/utils/AccountUtils';
@@ -66,6 +67,11 @@ describe('AccountCreationState', () => {
     testSandbox.resetHistory();
   });
 
+  afterEach(() => {
+    loggerService.info.resetHistory();
+    loggerService.warn.resetHistory();
+  });
+
   it('should initialize the Init State', async () => {
     expect(accountCreationState).to.be.instanceOf(AccountCreationState);
     testSandbox.assert.calledWith(serviceLocator, LoggerService.name);
@@ -96,54 +102,40 @@ describe('AccountCreationState', () => {
     });
 
     it('should log information when starting in synchronous mode', async () => {
-      await accountCreationState.subscribe(observer);
+      accountCreationState.subscribe(observer);
       await accountCreationState.onStart();
 
-      testSandbox.assert.calledTwice(loggerService.info);
+      commonAssertsForStart();
       testSandbox.assert.calledOnce(generateECDSAStub);
       testSandbox.assert.calledOnce(generateAliasECDSAStub);
       testSandbox.assert.calledOnce(generateED25519Stub);
-      testSandbox.assert.called(observer.update);
     });
 
     it('should log information when starting in asynchronous mode', async () => {
-      cliService.getCurrentArgv.returns({
-        async: true,
-        blocklisting: false,
-        balance: 1000,
-        accounts: 5,
-        startup: false,
-      } as any);
+      stubCliOptions({ async: true });
 
-      await accountCreationState.subscribe(observer);
+      accountCreationState.subscribe(observer);
       await accountCreationState.onStart();
 
-      testSandbox.assert.callCount(loggerService.info, 4);
+      commonAssertsForStart();
       testSandbox.assert.calledOnce(generateECDSAStub);
       testSandbox.assert.calledOnce(generateAliasECDSAStub);
       testSandbox.assert.calledOnce(generateED25519Stub);
-      testSandbox.assert.called(observer.update);
     });
 
     it('should log information when blocklisting is enabled', async () => {
       const getBlocklistedAccountsCountStub = testSandbox.stub(AccountCreationState.prototype, <any>'getBlocklistedAccountsCount').returns(1);
-      cliService.getCurrentArgv.returns({
-        async: true,
-        blocklisting: true,
-        balance: 1000,
-        accounts: 5,
-        startup: false,
-      } as any);
+      stubCliOptions({ async: true, blocklisting: true });
 
-      await accountCreationState.subscribe(observer);
+      accountCreationState.subscribe(observer);
       await accountCreationState.onStart();
 
+      commonAssertsForStart();
       testSandbox.assert.calledWith(loggerService.info, ACCOUNT_CREATION_STARTING_ASYNCHRONOUS_BLOCKLIST_MESSAGE, 'AccountCreationState');
       testSandbox.assert.called(getBlocklistedAccountsCountStub);
       testSandbox.assert.calledOnce(generateECDSAStub);
       testSandbox.assert.calledOnce(generateAliasECDSAStub);
       testSandbox.assert.calledOnce(generateED25519Stub);
-      testSandbox.assert.called(observer.update);
 
       getBlocklistedAccountsCountStub.restore();
     });
@@ -160,6 +152,10 @@ describe('AccountCreationState', () => {
     const expectedAccountId: AccountId = AccountId.fromString('0.0.1234');
     expectedAccountId.evmAddress = EvmAddress.fromString('0x1234');
     const expectedBalance: Hbar = new Hbar(1000);
+    const accountInfo: AccountInfo = {
+      accountId: expectedAccountId,
+      balance: expectedBalance
+    } as AccountInfo;
 
     let createAccountStub: SinonStub;
     let createAliasAccountStub: SinonStub;
@@ -169,12 +165,23 @@ describe('AccountCreationState', () => {
     let logAliasAccountTitleStub: SinonStub;
     let logAccountDividerStub: SinonStub;
     let logAliasAccountDividerStub: SinonStub;
+    let privateKeySpy: SinonSpy;
+
+    beforeEach(() => {
+      stubCliOptions({
+        async: false,
+        blocklisting: false,
+        balance: 1000,
+        accounts: 5,
+        startup: false,
+      });
+    });
 
     beforeEach(() => {
       createAccountStub = testSandbox.stub(AccountUtils, <any>'createAccount')
-        .resolves({ accountId: expectedAccountId, balance: expectedBalance } as AccountInfo);
+        .resolves(accountInfo);
       createAliasAccountStub = testSandbox.stub(AccountUtils, <any>'createAliasedAccount')
-        .resolves({ accountId: expectedAccountId, balance: expectedBalance } as AccountInfo);
+        .resolves(accountInfo);
       logAccountStub = testSandbox.stub(AccountCreationState.prototype, <any>'logAccount').resolves();
       logAccountTitleStub = testSandbox.stub(AccountCreationState.prototype, <any>'logAccountTitle').resolves();
       logAliasAccountStub = testSandbox.stub(AccountCreationState.prototype, <any>'logAliasAccount').resolves();
@@ -192,13 +199,13 @@ describe('AccountCreationState', () => {
       logAliasAccountTitleStub.restore();
       logAccountDividerStub.restore();
       logAliasAccountDividerStub.restore();
+      privateKeySpy.restore();
     })
 
     describe('generateECDSA', () => {
       let generateAliasECDSA: SinonStub;
       let generateED25519: SinonStub;
       let generateECDSA: SinonSpy;
-      let privateKeySpy: SinonSpy;
   
       beforeEach(() => {
         generateAliasECDSA = testSandbox.stub(AccountCreationState.prototype, <any>'generateAliasECDSA').resolves();
@@ -211,59 +218,61 @@ describe('AccountCreationState', () => {
         generateAliasECDSA.restore();
         generateED25519.restore();
         generateECDSA.restore();
-        privateKeySpy.restore();
       })
   
       it('should generate ECDSA accounts synchronously and log the title and divider', async () => {
-        cliService.getCurrentArgv.returns({
-          async: false,
-          blocklisting: false,
-          balance: 1000,
-          accounts: 5,
-          startup: false,
-        } as CLIOptions);
-
-        await accountCreationState.subscribe(observer);
+        accountCreationState.subscribe(observer);
         await accountCreationState.onStart();
 
-        testSandbox.assert.callCount(loggerService.info, 8);
         testSandbox.assert.called(generateECDSA);
         testSandbox.assert.called(generateAliasECDSA);
         testSandbox.assert.called(generateED25519);
-        testSandbox.assert.called(observer.update);
 
-        testSandbox.assert.callCount(privateKeySpy, 5);
+        assertCreatedAccounts(5, 'ECDSA');
         testSandbox.assert.callCount(createAccountStub, 5);
-        testSandbox.assert.calledOnceWithExactly(logAccountTitleStub, 'ECDSA');
-        testSandbox.assert.callCount(logAccountStub, 5);
-        testSandbox.assert.calledOnce(logAccountDividerStub);
       });
 
       it('should generate ECDSA accounts аsynchronously and log the title and divider', async () => {
-        cliService.getCurrentArgv.returns({
-          async: true,
-          blocklisting: false,
-          balance: 1000,
-          accounts: 10,
-          startup: false,
-        } as any);
-
-        await accountCreationState.subscribe(observer);
+        stubCliOptions({ async: true });
+
+        accountCreationState.subscribe(observer);
         await accountCreationState.onStart();
 
-        testSandbox.assert.callCount(loggerService.info, 10);
         testSandbox.assert.called(generateECDSA);
         testSandbox.assert.called(generateAliasECDSA);
         testSandbox.assert.called(generateED25519);
-        testSandbox.assert.called(observer.update);
 
         await generateECDSA.returnValues[0];
 
-        testSandbox.assert.callCount(privateKeySpy, 10);
-        testSandbox.assert.callCount(createAccountStub, 10);
-        testSandbox.assert.calledOnceWithExactly(logAccountTitleStub, 'ECDSA');
-        testSandbox.assert.callCount(logAccountStub, 10);
-        testSandbox.assert.calledOnce(logAccountDividerStub);
+        assertCreatedAccounts(5, 'ECDSA');
+        testSandbox.assert.callCount(createAccountStub, 5);
+      });
+
+      it('should retry the creation of ECDSA accounts if there is an error', async () => {
+        const error = new Error(SDK_ERRORS.FAILED_TO_FIND_A_HEALTHY_NODE);
+        const errorCount = 2;
+        rejectWithError(createAccountStub, error, errorCount);
+
+        accountCreationState.subscribe(observer);
+        await accountCreationState.onStart();
+
+        const accounts = cliService.getCurrentArgv().accounts;
+        assertCreatedAccounts(accounts, 'ECDSA');
+        assertRetries(createAccountStub, errorCount, error);
+      });
+
+      it('should not retry the creation of ECDSA accounts if there is an error which is not retryable', async () => {
+        // Stub the createAccount method to throw an error
+        // on the first two calls and then resolve any subsequent calls
+        const error = new Error('Some other error');
+        rejectWithError(createAccountStub, error, 2);
+
+        accountCreationState.subscribe(observer);
+        await assertThrowsError(() => accountCreationState.onStart(), error);
+
+        const accounts = cliService.getCurrentArgv().accounts;
+        assertCreatedAccounts(accounts, 'ECDSA', false);
+        assertRetries(createAccountStub, 0);
       });
     });
   
@@ -271,7 +280,6 @@ describe('AccountCreationState', () => {
       let generateECDSA: SinonStub;
       let generateED25519: SinonStub;
       let generateAliasECDSA: SinonSpy;
-      let privateKeySpy: SinonSpy;
   
       beforeEach(() => {
         generateECDSA = testSandbox.stub(AccountCreationState.prototype, <any>'generateECDSA').resolves();
@@ -284,60 +292,60 @@ describe('AccountCreationState', () => {
         generateECDSA.restore();
         generateED25519.restore();
         generateAliasECDSA.restore();
-        privateKeySpy.restore();
-        loggerService.info.resetHistory()
       })
   
       it('should generate AliasECDSA accounts synchronously and log the title and divider', async () => {
-        cliService.getCurrentArgv.returns({
-          async: false,
-          blocklisting: false,
-          balance: 1000,
-          accounts: 10,
-          startup: false,
-        } as any);
-        
-        await accountCreationState.subscribe(observer);
+        accountCreationState.subscribe(observer);
         await accountCreationState.onStart();
 
-        testSandbox.assert.callCount(loggerService.info, 12);
         testSandbox.assert.called(generateAliasECDSA);
         testSandbox.assert.called(generateECDSA);
         testSandbox.assert.called(generateED25519);
-        testSandbox.assert.called(observer.update);
 
-        testSandbox.assert.callCount(privateKeySpy, 10);
-        testSandbox.assert.callCount(createAliasAccountStub, 10);
-        testSandbox.assert.calledOnce(logAliasAccountTitleStub);
-        testSandbox.assert.callCount(logAliasAccountStub, 10);
-        testSandbox.assert.calledOnce(logAliasAccountDividerStub);
+        assertCreatedAccountAliases(5);
+        testSandbox.assert.callCount(createAliasAccountStub, 5);
       });
 
       it('should generate AliasECDSA accounts asynchronously and log the title and divider', async () => {
-        cliService.getCurrentArgv.returns({
-          async: true,
-          blocklisting: false,
-          balance: 1000,
-          accounts: 6,
-          startup: false,
-        } as any);
-
-        await accountCreationState.subscribe(observer);
+        stubCliOptions({ async: true });
+
+        accountCreationState.subscribe(observer);
         await accountCreationState.onStart();
 
-        testSandbox.assert.callCount(loggerService.info, 2);
         testSandbox.assert.called(generateAliasECDSA);
         testSandbox.assert.called(generateECDSA);
         testSandbox.assert.called(generateED25519);
-        testSandbox.assert.called(observer.update);
 
         await generateAliasECDSA.returnValues[0];
 
-        testSandbox.assert.callCount(privateKeySpy, 6);
-        testSandbox.assert.callCount(createAliasAccountStub, 6);
-        testSandbox.assert.calledOnce(logAliasAccountTitleStub);
-        testSandbox.assert.callCount(logAliasAccountStub, 6);
-        testSandbox.assert.calledOnce(logAliasAccountDividerStub);
+        assertCreatedAccountAliases(5);
+        testSandbox.assert.callCount(createAliasAccountStub, 5);
+      });
+
+
+      it('should retry the creation of AliasECDSA accounts if there is an error', async () => {
+        const error = new Error(SDK_ERRORS.FAILED_TO_FIND_A_HEALTHY_NODE);
+        const errorCount = 2;
+        rejectWithError(createAliasAccountStub, error, errorCount);
+
+        accountCreationState.subscribe(observer);
+        await accountCreationState.onStart();
+
+        const accounts = cliService.getCurrentArgv().accounts;
+        assertCreatedAccountAliases(accounts);
+        assertRetries(createAliasAccountStub, errorCount, error);
+      });
+
+      it('should not retry the creation of AliasECDSA accounts if there is an error which is not retryable', async () => {
+        const error = new Error('Some other error');
+        rejectWithError(createAliasAccountStub, error, 2);
+
+        accountCreationState.subscribe(observer);
+        await assertThrowsError(() => accountCreationState.onStart(), error);
+
+        const accounts = cliService.getCurrentArgv().accounts;
+        assertCreatedAccountAliases(accounts, false);
+        assertRetries(createAliasAccountStub, 0);
       });
     });
   
@@ -345,7 +353,6 @@ describe('AccountCreationState', () => {
       let generateAliasECDSA: SinonStub;
       let generateECDSA: SinonStub;
       let generateED25519: SinonSpy;
-      let privateKeySpy: SinonSpy;
   
       beforeEach(() => {
         generateAliasECDSA = testSandbox.stub(AccountCreationState.prototype, <any>'generateAliasECDSA').resolves();
@@ -358,61 +365,145 @@ describe('AccountCreationState', () => {
         generateAliasECDSA.restore();
         generateECDSA.restore();
         generateED25519.restore();
-        privateKeySpy.restore();
-        loggerService.info.resetHistory();
       })
   
       it('should generate ED25519 accounts synchronously and log the title and divider', async () => {
-        cliService.getCurrentArgv.returns({
-          async: false,
-          blocklisting: false,
-          balance: 1000,
-          accounts: 10,
-          startup: false,
-        } as any);
-
-        await accountCreationState.subscribe(observer);
+        accountCreationState.subscribe(observer);
         await accountCreationState.onStart();
 
-        testSandbox.assert.callCount(loggerService.info, 2);
         testSandbox.assert.called(generateAliasECDSA);
         testSandbox.assert.called(generateECDSA);
         testSandbox.assert.called(generateED25519);
-        testSandbox.assert.called(observer.update);
-
-        testSandbox.assert.callCount(privateKeySpy, 10);
-        testSandbox.assert.callCount(createAccountStub, 10);
-        testSandbox.assert.calledOnceWithExactly(logAccountTitleStub, 'ED25519');
-        testSandbox.assert.callCount(logAccountStub, 10);
-        testSandbox.assert.calledOnce(logAccountDividerStub);
+        testSandbox.assert.callCount(createAccountStub, 5);
+        assertCreatedAccounts(5, 'ED25519');
       });
 
       it('should generate ED25519 accounts asynchronously and log the title and divider', async () => {
-        cliService.getCurrentArgv.returns({
-          async: true,
-          blocklisting: false,
-          balance: 1000,
-          accounts: 6,
-          startup: false,
-        } as any);
-
-        await accountCreationState.subscribe(observer);
+        stubCliOptions({ async: true });
+
+        accountCreationState.subscribe(observer);
         await accountCreationState.onStart();
 
-        testSandbox.assert.callCount(loggerService.info, 2);
         testSandbox.assert.called(generateAliasECDSA);
         testSandbox.assert.called(generateECDSA);
         testSandbox.assert.called(generateED25519);
-        testSandbox.assert.called(observer.update);
 
         await generateED25519.returnValues[0];
 
-        testSandbox.assert.callCount(privateKeySpy, 6);
-        testSandbox.assert.callCount(createAccountStub, 6);
-        testSandbox.assert.calledOnceWithExactly(logAccountTitleStub, 'ED25519');
-        testSandbox.assert.callCount(logAccountStub, 6);
-        testSandbox.assert.calledOnce(logAccountDividerStub);
+        assertCreatedAccounts(5, 'ED25519');
+        testSandbox.assert.callCount(createAccountStub, 5);
+      });
+
+      it('should retry the creation of ED25519 accounts if there is an error', async () => {
+        const error = new Error(SDK_ERRORS.FAILED_TO_FIND_A_HEALTHY_NODE);
+        const errorCount = 2;
+        rejectWithError(createAccountStub, error, errorCount);
+
+        accountCreationState.subscribe(observer);
+        await accountCreationState.onStart();
+
+        const accounts = cliService.getCurrentArgv().accounts;
+        assertCreatedAccounts(accounts, 'ED25519');
+        assertRetries(createAccountStub, errorCount, error);
+      });
+
+      it('should not retry the creation of ED25519 accounts if there is an error which is not retryable', async () => {
+        const error = new Error('Some other error');
+        rejectWithError(createAccountStub, error, 2);
+
+        accountCreationState.subscribe(observer);
+        await assertThrowsError(() => accountCreationState.onStart(), error);
+
+        const accounts = cliService.getCurrentArgv().accounts;
+        assertCreatedAccounts(accounts, 'ED25519', false);
+        assertRetries(createAccountStub, 0);
       });
     });
-  })
+
+    /**
+     * Stubs the createAccount method to throw an error on the first two calls
+     * and then resolve any subsequent calls
+     *
+     * @param stub The stub to reset and configure
+     * @param error The error to throw
+     * @param calls The number of calls to make before resolving
+     */
+    function rejectWithError(stub: SinonStub, error: Error, calls: number) {
+      stub.reset();
+      for (let i = 0; i < calls; i++) {
+        stub.onCall(i).rejects(error);
+      }
+      stub.resolves(accountInfo);
+    }
+
+    function assertRetries(stub: SinonStub, retries: number, error?: Error) {
+      const accounts = cliService.getCurrentArgv().accounts;
+
+      // Assert that the task was retried until it succeeded
+      testSandbox.assert.callCount(stub, accounts + retries);
+
+      // Assert that the error was logged on each failure
+      if (retries > 0) {
+        testSandbox.assert.callCount(loggerService.warn, 2);
+        for (const call of loggerService.warn.getCalls()) {
+          expect(call.args[0]).to.equal(`Error occurred during task execution: "${error!.toString()}"`);
+        }
+      } else {
+        testSandbox.assert.notCalled(loggerService.warn);
+      }
+    }
+
+    function assertCreatedAccounts(accounts: number, accountType: string, success: boolean = true): void {
+      commonAssertsForStart(success);
+      testSandbox.assert.callCount(privateKeySpy, accounts);
+      if (success) {
+        testSandbox.assert.calledOnceWithExactly(logAccountTitleStub, accountType);
+        testSandbox.assert.callCount(logAccountStub, accounts);
+        testSandbox.assert.calledOnce(logAccountDividerStub);
+      } else {
+        testSandbox.assert.notCalled(logAccountTitleStub);
+        testSandbox.assert.notCalled(logAccountStub);
+        testSandbox.assert.notCalled(logAccountDividerStub);
+      }
+    }
+
+    function assertCreatedAccountAliases(count: number, success: boolean = true): void {
+      commonAssertsForStart(success);
+      testSandbox.assert.callCount(privateKeySpy, count);
+      if (success) {
+        testSandbox.assert.calledOnce(logAliasAccountTitleStub);
+        testSandbox.assert.callCount(logAliasAccountStub, count);
+        testSandbox.assert.calledOnce(logAliasAccountDividerStub);
+      } else {
+        testSandbox.assert.notCalled(logAliasAccountTitleStub);
+        testSandbox.assert.notCalled(logAliasAccountStub);
+        testSandbox.assert.notCalled(logAliasAccountDividerStub);
+      }
+    }
+  });
+
+  function stubCliOptions(mockedOptions: Partial<CLIOptions>): void {
+    cliService.getCurrentArgv.returns({
+      ...cliService.getCurrentArgv(),
+      ...mockedOptions,
+    } as CLIOptions);
+  }
+
+  function commonAssertsForStart(success: boolean = true): void {
+    testSandbox.assert.called(observer.update);
+    if (success) {
+      testSandbox.assert.calledTwice(loggerService.info);
+    } else {
+      testSandbox.assert.calledOnce(loggerService.info);
+    }
+  }
+
+  async function assertThrowsError(fn: () => Promise<void>, error: Error): Promise<void> {
+    try {
+      await fn();
+      expect.fail('Expected an exception to be thrown');
+    } catch (e) {
+      expect(e).to.equal(error);
+    }
+  }
 });
diff --git a/test/unit/utils/RetryUtils.spec.ts b/test/unit/utils/RetryUtils.spec.ts
new file mode 100644
index 0000000..d65ad5a
--- /dev/null
+++ b/test/unit/utils/RetryUtils.spec.ts
@@ -0,0 +1,135 @@
+/*-
+ *
+ * Hedera Local Node
+ *
+ * Copyright (C) 2023 Hedera Hashgraph, LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+import { createStubInstance, SinonSpy, SinonStubbedInstance, spy, stub } from 'sinon';
+import { LoggerService } from '../../../src/services/LoggerService';
+import { RetryUtils } from '../../../src/utils/RetryUtils';
+import { expect } from 'chai';
+
+describe('RetryUtils', () => {
+  const backOff = 100;
+  let doOnRetry: SinonSpy;
+  let loggerService: SinonStubbedInstance<LoggerService>;
+
+  beforeEach(() => {
+    doOnRetry = spy((e) => loggerService.error(e.message));
+    loggerService = createStubInstance(LoggerService);
+  });
+
+  afterEach(() => {
+    doOnRetry.resetHistory();
+  });
+
+  it('should retry the task until it succeeds', async () => {
+    const error = new Error('First attempt failed');
+    const task = stub()
+      .onFirstCall().rejects(error)
+      .onSecondCall().resolves('Success');
+
+    const result = await RetryUtils.retryTask(task, { doOnRetry, backOff });
+
+    expect(result).to.equal('Success');
+    expect(task.callCount).to.equal(2);
+    expect(doOnRetry.callCount).to.equal(1);
+    expect(loggerService.error.callCount).to.equal(1);
+    expect(loggerService.error.getCall(0).args[0]).to.equal(error.message);
+  });
+
+  it('should throw an error after max retries', async () => {
+    const error = new Error('Task failed');
+    const task = stub().rejects(error);
+
+    try {
+      await RetryUtils.retryTask(task, { doOnRetry, backOff, maxRetries: 5 });
+    } catch (e) {
+      expect(e).to.equal(error);
+    }
+
+    expect(task.callCount).to.equal(5);
+    expect(doOnRetry.callCount).to.equal(4);
+  });
+
+  it('should log an error message on each failure', async () => {
+    const error = new Error('Task failed');
+    const task = stub().rejects(error);
+
+    try {
+      await RetryUtils.retryTask(task, { doOnRetry, backOff });
+    } catch (e) {
+      expect(e).to.equal(error);
+    }
+
+    expect(task.callCount).to.equal(3);
+    expect(doOnRetry.callCount).to.equal(2);
+    expect(loggerService.error.callCount).to.equal(2);
+    for (let i = 0; i < 2; i++) {
+      expect(doOnRetry.getCall(i).args[0]).to.equal(error);
+      expect(loggerService.error.getCall(i).args[0]).to.equal(error.message);
+    }
+  });
+
+  it('should succeed on the first attempt without retries', async () => {
+    const task = stub().resolves('Success');
+
+    const result = await RetryUtils.retryTask(task, { doOnRetry, backOff });
+
+    expect(result).to.equal('Success');
+    expect(task.callCount).to.equal(1);
+    expect(doOnRetry.callCount).to.equal(0);
+    expect(loggerService.error.callCount).to.equal(0);
+  });
+
+  it('should not retry if shouldRetry returns false', async () => {
+    const error = new Error('Non-retryable error');
+    const task = stub().rejects(error);
+    const shouldRetry = stub().returns(false);
+
+    try {
+      await RetryUtils.retryTask(task, { doOnRetry, backOff, shouldRetry });
+    } catch (e) {
+      expect(e).to.equal(error);
+    }
+
+    expect(task.callCount).to.equal(1);
+    expect(doOnRetry.callCount).to.equal(0);
+    expect(shouldRetry.callCount).to.equal(1);
+  });
+
+  it('should retry only for retryable errors', async () => {
+    const retryableError = new Error('Retryable error');
+    const nonRetryableError = new Error('Non-retryable error');
+    const task = stub()
+      .onFirstCall().rejects(retryableError)
+      .onSecondCall().rejects(nonRetryableError);
+    const shouldRetry = stub()
+      .onFirstCall().returns(true)
+      .onSecondCall().returns(false);
+
+    try {
+      await RetryUtils.retryTask(task, { doOnRetry, backOff, shouldRetry });
+    } catch (e) {
+      expect(e).to.equal(nonRetryableError);
+    }
+
+    expect(task.callCount).to.equal(2);
+    expect(doOnRetry.callCount).to.equal(1);
+    expect(shouldRetry.callCount).to.equal(2);
+  });
+});