Repository: gnosisguild/enclave
Pull request #450: Enclave init simply creates a blank hardhat project based on `./examples/basic`
Author: login: ryardley
Created: 2025-05-29T00:17:13Z | Merged: 2025-05-29T10:21:05Z

- `enclave init` -> `enclave wizard` (and is hidden temporarily as we will need this functionality soon)
- Sets up a basic example that is simply a hardhat typescript project
- Makes `enclave init` setup the project in the cwd. (will add an arg path later)

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->

## Summary by CodeRabbit

- **New Features**
  - Introduced a complete example Hardhat project with a sample smart contract, deployment scripts, tests, and documentation for Ethereum development.
  - Added a new Rust package for initializing project environments, including GitHub template cloning, dependency installation, and Git setup.
  - Added a deployment wizard for interactive enclave configuration, supporting both simple and advanced setup flows.

- **Bug Fixes**
  - Improved error handling and validation during project initialization and configuration steps.

- **Documentation**
  - Added comprehensive README and configuration files for the new example project.

- **Chores**
  - Updated workspace and package configuration to include new dependencies and modules.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->


Diff:
diff --git a/examples/basic/.gitignore b/examples/basic/.gitignore
new file mode 100644
index 0000000000..e8c12ff4fe
--- /dev/null
+++ b/examples/basic/.gitignore
@@ -0,0 +1,17 @@
+node_modules
+.env
+
+# Hardhat files
+/cache
+/artifacts
+
+# TypeChain files
+/typechain
+/typechain-types
+
+# solidity-coverage files
+/coverage
+/coverage.json
+
+# Hardhat Ignition default folder for deployments against a local node
+ignition/deployments/chain-31337
diff --git a/examples/basic/README.md b/examples/basic/README.md
new file mode 100644
index 0000000000..17f8ed190d
--- /dev/null
+++ b/examples/basic/README.md
@@ -0,0 +1,13 @@
+# Sample Hardhat Project
+
+This project demonstrates a basic Hardhat use case. It comes with a sample contract, a test for that contract, and a Hardhat Ignition module that deploys that contract.
+
+Try running some of the following tasks:
+
+```shell
+npx hardhat help
+npx hardhat test
+REPORT_GAS=true npx hardhat test
+npx hardhat node
+npx hardhat ignition deploy ./ignition/modules/Lock.ts
+```
diff --git a/examples/basic/contracts/Lock.sol b/examples/basic/contracts/Lock.sol
new file mode 100644
index 0000000000..2f385f70ed
--- /dev/null
+++ b/examples/basic/contracts/Lock.sol
@@ -0,0 +1,34 @@
+// SPDX-License-Identifier: UNLICENSED
+pragma solidity ^0.8.28;
+
+// Uncomment this line to use console.log
+// import "hardhat/console.sol";
+
+contract Lock {
+    uint public unlockTime;
+    address payable public owner;
+
+    event Withdrawal(uint amount, uint when);
+
+    constructor(uint _unlockTime) payable {
+        require(
+            block.timestamp < _unlockTime,
+            "Unlock time should be in the future"
+        );
+
+        unlockTime = _unlockTime;
+        owner = payable(msg.sender);
+    }
+
+    function withdraw() public {
+        // Uncomment this line, and the import of "hardhat/console.sol", to print a log in your terminal
+        // console.log("Unlock time is %o and block timestamp is %o", unlockTime, block.timestamp);
+
+        require(block.timestamp >= unlockTime, "You can't withdraw yet");
+        require(msg.sender == owner, "You aren't the owner");
+
+        emit Withdrawal(address(this).balance, block.timestamp);
+
+        owner.transfer(address(this).balance);
+    }
+}
diff --git a/examples/basic/hardhat.config.ts b/examples/basic/hardhat.config.ts
new file mode 100644
index 0000000000..0e8962a6ef
--- /dev/null
+++ b/examples/basic/hardhat.config.ts
@@ -0,0 +1,8 @@
+import { HardhatUserConfig } from "hardhat/config";
+import "@nomicfoundation/hardhat-toolbox";
+
+const config: HardhatUserConfig = {
+  solidity: "0.8.28",
+};
+
+export default config;
diff --git a/examples/basic/ignition/modules/Lock.ts b/examples/basic/ignition/modules/Lock.ts
new file mode 100644
index 0000000000..9ee4a2f1bb
--- /dev/null
+++ b/examples/basic/ignition/modules/Lock.ts
@@ -0,0 +1,20 @@
+// This setup uses Hardhat Ignition to manage smart contract deployments.
+// Learn more about it at https://hardhat.org/ignition
+
+import { buildModule } from "@nomicfoundation/hardhat-ignition/modules";
+
+const JAN_1ST_2030 = 1893456000;
+const ONE_GWEI: bigint = 1_000_000_000n;
+
+const LockModule = buildModule("LockModule", (m) => {
+  const unlockTime = m.getParameter("unlockTime", JAN_1ST_2030);
+  const lockedAmount = m.getParameter("lockedAmount", ONE_GWEI);
+
+  const lock = m.contract("Lock", [unlockTime], {
+    value: lockedAmount,
+  });
+
+  return { lock };
+});
+
+export default LockModule;
diff --git a/examples/basic/package.json b/examples/basic/package.json
new file mode 100644
index 0000000000..e1608641f3
--- /dev/null
+++ b/examples/basic/package.json
@@ -0,0 +1,25 @@
+{
+  "name": "hardhat-project",
+  "devDependencies": {
+    "@nomicfoundation/hardhat-chai-matchers": "^2.0.0",
+    "@nomicfoundation/hardhat-ethers": "^3.0.0",
+    "@nomicfoundation/hardhat-ignition": "^0.15.0",
+    "@nomicfoundation/hardhat-ignition-ethers": "^0.15.0",
+    "@nomicfoundation/hardhat-network-helpers": "^1.0.0",
+    "@nomicfoundation/hardhat-toolbox": "^5.0.0",
+    "@nomicfoundation/hardhat-verify": "^2.0.0",
+    "@typechain/ethers-v6": "^0.5.0",
+    "@typechain/hardhat": "^9.0.0",
+    "@types/chai": "^4.2.0",
+    "@types/mocha": ">=9.1.0",
+    "@types/node": ">=18.0.0",
+    "chai": "^4.2.0",
+    "ethers": "^6.4.0",
+    "hardhat": "^2.14.0",
+    "hardhat-gas-reporter": "^1.0.8",
+    "solidity-coverage": "^0.8.0",
+    "ts-node": ">=8.0.0",
+    "typechain": "^8.3.0",
+    "typescript": ">=4.5.0"
+  }
+}
diff --git a/examples/basic/test/Lock.ts b/examples/basic/test/Lock.ts
new file mode 100644
index 0000000000..160dbfa163
--- /dev/null
+++ b/examples/basic/test/Lock.ts
@@ -0,0 +1,127 @@
+import {
+  time,
+  loadFixture,
+} from "@nomicfoundation/hardhat-toolbox/network-helpers";
+import { anyValue } from "@nomicfoundation/hardhat-chai-matchers/withArgs";
+import { expect } from "chai";
+import hre from "hardhat";
+
+describe("Lock", function () {
+  // We define a fixture to reuse the same setup in every test.
+  // We use loadFixture to run this setup once, snapshot that state,
+  // and reset Hardhat Network to that snapshot in every test.
+  async function deployOneYearLockFixture() {
+    const ONE_YEAR_IN_SECS = 365 * 24 * 60 * 60;
+    const ONE_GWEI = 1_000_000_000;
+
+    const lockedAmount = ONE_GWEI;
+    const unlockTime = (await time.latest()) + ONE_YEAR_IN_SECS;
+
+    // Contracts are deployed using the first signer/account by default
+    const [owner, otherAccount] = await hre.ethers.getSigners();
+
+    const Lock = await hre.ethers.getContractFactory("Lock");
+    const lock = await Lock.deploy(unlockTime, { value: lockedAmount });
+
+    return { lock, unlockTime, lockedAmount, owner, otherAccount };
+  }
+
+  describe("Deployment", function () {
+    it("Should set the right unlockTime", async function () {
+      const { lock, unlockTime } = await loadFixture(deployOneYearLockFixture);
+
+      expect(await lock.unlockTime()).to.equal(unlockTime);
+    });
+
+    it("Should set the right owner", async function () {
+      const { lock, owner } = await loadFixture(deployOneYearLockFixture);
+
+      expect(await lock.owner()).to.equal(owner.address);
+    });
+
+    it("Should receive and store the funds to lock", async function () {
+      const { lock, lockedAmount } = await loadFixture(
+        deployOneYearLockFixture
+      );
+
+      expect(await hre.ethers.provider.getBalance(lock.target)).to.equal(
+        lockedAmount
+      );
+    });
+
+    it("Should fail if the unlockTime is not in the future", async function () {
+      // We don't use the fixture here because we want a different deployment
+      const latestTime = await time.latest();
+      const Lock = await hre.ethers.getContractFactory("Lock");
+      await expect(Lock.deploy(latestTime, { value: 1 })).to.be.revertedWith(
+        "Unlock time should be in the future"
+      );
+    });
+  });
+
+  describe("Withdrawals", function () {
+    describe("Validations", function () {
+      it("Should revert with the right error if called too soon", async function () {
+        const { lock } = await loadFixture(deployOneYearLockFixture);
+
+        await expect(lock.withdraw()).to.be.revertedWith(
+          "You can't withdraw yet"
+        );
+      });
+
+      it("Should revert with the right error if called from another account", async function () {
+        const { lock, unlockTime, otherAccount } = await loadFixture(
+          deployOneYearLockFixture
+        );
+
+        // We can increase the time in Hardhat Network
+        await time.increaseTo(unlockTime);
+
+        // We use lock.connect() to send a transaction from another account
+        await expect(lock.connect(otherAccount).withdraw()).to.be.revertedWith(
+          "You aren't the owner"
+        );
+      });
+
+      it("Shouldn't fail if the unlockTime has arrived and the owner calls it", async function () {
+        const { lock, unlockTime } = await loadFixture(
+          deployOneYearLockFixture
+        );
+
+        // Transactions are sent using the first signer by default
+        await time.increaseTo(unlockTime);
+
+        await expect(lock.withdraw()).not.to.be.reverted;
+      });
+    });
+
+    describe("Events", function () {
+      it("Should emit an event on withdrawals", async function () {
+        const { lock, unlockTime, lockedAmount } = await loadFixture(
+          deployOneYearLockFixture
+        );
+
+        await time.increaseTo(unlockTime);
+
+        await expect(lock.withdraw())
+          .to.emit(lock, "Withdrawal")
+          .withArgs(lockedAmount, anyValue); // We accept any value as `when` arg
+      });
+    });
+
+    describe("Transfers", function () {
+      it("Should transfer the funds to the owner", async function () {
+        const { lock, unlockTime, lockedAmount, owner } = await loadFixture(
+          deployOneYearLockFixture
+        );
+
+        await time.increaseTo(unlockTime);
+
+        await expect(lock.withdraw()).to.changeEtherBalances(
+          [owner, lock],
+          [lockedAmount, -lockedAmount]
+        );
+      });
+    });
+  });
+});
diff --git a/examples/basic/tsconfig.json b/examples/basic/tsconfig.json
new file mode 100644
index 0000000000..574e785c71
--- /dev/null
+++ b/examples/basic/tsconfig.json
@@ -0,0 +1,11 @@
+{
+  "compilerOptions": {
+    "target": "es2020",
+    "module": "commonjs",
+    "esModuleInterop": true,
+    "forceConsistentCasingInFileNames": true,
+    "strict": true,
+    "skipLibCheck": true,
+    "resolveJsonModule": true
+  }
+}
diff --git a/packages/ciphernode/Cargo.lock b/packages/ciphernode/Cargo.lock
index 86e3117982..9e6e8d5aac 100644
--- a/packages/ciphernode/Cargo.lock
+++ b/packages/ciphernode/Cargo.lock
@@ -1353,6 +1353,17 @@ dependencies = [
  "pin-project-lite",
 ]
 
+[[package]]
+name = "async-recursion"
+version = "1.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.101",
+]
+
 [[package]]
 name = "async-std"
 version = "1.13.1"
@@ -2422,6 +2433,7 @@ dependencies = [
  "config",
  "crypto",
  "dialoguer",
+ "enclave-init",
  "enclave_core",
  "events",
  "hex",
@@ -2439,6 +2451,16 @@ dependencies = [
  "zeroize",
 ]
 
+[[package]]
+name = "enclave-init"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "async-recursion",
+ "git2",
+ "tokio",
+]
+
 [[package]]
 name = "enclave-sdk"
 version = "0.1.0"
@@ -3061,6 +3083,21 @@ version = "0.31.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
 
+[[package]]
+name = "git2"
+version = "0.20.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2deb07a133b1520dc1a5690e9bd08950108873d7ed5de38dcc74d3b5ebffa110"
+dependencies = [
+ "bitflags 2.9.0",
+ "libc",
+ "libgit2-sys",
+ "log",
+ "openssl-probe",
+ "openssl-sys",
+ "url",
+]
+
 [[package]]
 name = "glob"
 version = "0.3.2"
@@ -3899,6 +3936,20 @@ version = "0.2.172"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa"
 
+[[package]]
+name = "libgit2-sys"
+version = "0.18.1+1.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e1dcb20f84ffcdd825c7a311ae347cce604a6f084a767dec4a4929829645290e"
+dependencies = [
+ "cc",
+ "libc",
+ "libssh2-sys",
+ "libz-sys",
+ "openssl-sys",
+ "pkg-config",
+]
+
 [[package]]
 name = "libm"
 version = "0.2.14"
@@ -4286,6 +4337,32 @@ dependencies = [
  "libc",
 ]
 
+[[package]]
+name = "libssh2-sys"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "220e4f05ad4a218192533b300327f5150e809b54c4ec83b5a1d91833601811b9"
+dependencies = [
+ "cc",
+ "libc",
+ "libz-sys",
+ "openssl-sys",
+ "pkg-config",
+ "vcpkg",
+]
+
+[[package]]
+name = "libz-sys"
+version = "1.1.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8b70e7a7df205e92a1a4cd9aaae7898dac0aa555503cc0a649494d0d60e7651d"
+dependencies = [
+ "cc",
+ "libc",
+ "pkg-config",
+ "vcpkg",
+]
+
 [[package]]
 name = "linked-hash-map"
 version = "0.5.6"
diff --git a/packages/ciphernode/Cargo.toml b/packages/ciphernode/Cargo.toml
index 33209b252c..918e6ff68f 100644
--- a/packages/ciphernode/Cargo.toml
+++ b/packages/ciphernode/Cargo.toml
@@ -10,6 +10,7 @@ members = [
   "events",
   "evm",
   "fhe",
+  "init", 
   "keyshare",
   "logger",
   "net",
@@ -31,6 +32,7 @@ data = { path = "./data" }
 e3_request = { path = "./e3_request" }
 enclave = { path = "./enclave" }
 enclave_core = { path = "./enclave_core" }
+enclave-init = { path = "./init" }
 events = { path = "./events" }
 evm = { path = "./evm" }
 fhe = { path = "./fhe" }
@@ -56,6 +58,7 @@ argon2 = "0.5.2"
 anyhow = "1.0.86"
 async-std = { version = "1.12", features = ["attributes"] }
 async-trait = "0.1"
+async-recursion = "1.1.1"
 bincode = "1.3.3"
 bloom = "0.3.2"
 bs58 = "0.5.1"
@@ -71,6 +74,7 @@ fhe-util = { git = "https://github.com/gnosisguild/fhe.rs" }
 figment = { version = "0.10.19", features = ["yaml", "test"] }
 futures = "0.3.30"
 futures-util = "0.3"
+git2 = "0.20.2"
 hex = "0.4.3"
 lazy_static = "1.5.0"
 num = "0.4.3"
diff --git a/packages/ciphernode/enclave/Cargo.toml b/packages/ciphernode/enclave/Cargo.toml
index 1e47e8d4a6..9f6c3cb829 100644
--- a/packages/ciphernode/enclave/Cargo.toml
+++ b/packages/ciphernode/enclave/Cargo.toml
@@ -14,6 +14,7 @@ config = { workspace = true }
 crypto = { workspace = true }
 dialoguer = { workspace = true }
 enclave_core = { workspace = true }
+enclave-init = { workspace = true }
 events = { workspace = true }
 hex = { workspace = true }
 opentelemetry = { workspace = true }
diff --git a/packages/ciphernode/enclave/src/cli.rs b/packages/ciphernode/enclave/src/cli.rs
index afc70f6730..31dc59bb99 100644
--- a/packages/ciphernode/enclave/src/cli.rs
+++ b/packages/ciphernode/enclave/src/cli.rs
@@ -1,11 +1,10 @@
 use crate::helpers::telemetry::setup_tracing;
-use crate::net;
 use crate::net::NetCommands;
 use crate::nodes::{self, NodeCommands};
 use crate::password::PasswordCommands;
 use crate::start;
 use crate::wallet::WalletCommands;
-use crate::{init, password, wallet};
+use crate::{init, net, password, wallet, wizard};
 use anyhow::{bail, Result};
 use clap::{command, ArgAction, Parser, Subcommand};
 use config::validation::ValidUrl;
@@ -82,7 +81,8 @@ impl Cli {
             {
                 // Existing init branch
                 match self.command {
-                    Commands::Init {
+                    Commands::Init  => init::execute().await?,
+                    Commands::Wizard {
                         rpc_url,
                         eth_address,
                         password,
@@ -90,7 +90,7 @@ impl Cli {
                         net_keypair,
                         generate_net_keypair,
                     } => {
-                        init::execute(
+                        wizard::execute(
                             rpc_url,
                             eth_address,
                             password,
@@ -130,6 +130,9 @@ impl Cli {
             Commands::Init { .. } => {
                 bail!("Cannot run `enclave init` when a configuration exists.");
             }
+            Commands::Wizard { .. } => {
+                bail!("Cannot run `enclave wizard` when a configuration exists.");
+            }
             Commands::Nodes { command } => {
                 nodes::execute(command, &config, self.verbose, self.config).await?
             }
@@ -171,6 +174,9 @@ pub enum Commands {
         peers: Vec<String>,
     },
 
+    /// Initialize an enclave project
+    Init,
+
     /// Password management commands
     Password {
         #[command(subcommand)]
@@ -189,8 +195,9 @@ pub enum Commands {
         command: NetCommands,
     },
 
+    #[command(hide = true)]
     /// Initialize your ciphernode by setting up a configuration
-    Init {
+    Wizard {
         /// An rpc url for enclave to connect to
         #[arg(long = "rpc-url")]
         rpc_url: Option<String>,
diff --git a/packages/ciphernode/enclave/src/init.rs b/packages/ciphernode/enclave/src/init.rs
index cbdb6ae69c..cde34a1e5a 100644
--- a/packages/ciphernode/enclave/src/init.rs
+++ b/packages/ciphernode/enclave/src/init.rs
@@ -1,79 +1,5 @@
 use anyhow::Result;
-use dialoguer::{theme::ColorfulTheme, Input};
-use enclave_core::init;
-use tracing::instrument;
 
-use crate::net;
-use crate::net::{NetCommands, NetKeypairCommands};
-use crate::password;
-use crate::password::PasswordCommands;
-
-#[instrument(name = "app", skip_all)]
-pub async fn execute(
-    rpc_url: Option<String>,
-    eth_address: Option<String>,
-    password: Option<String>,
-    skip_eth: bool,
-    net_keypair: Option<String>,
-    generate_net_keypair: bool,
-) -> Result<()> {
-    let rpc_url = match rpc_url {
-        Some(url) => {
-            init::validate_rpc_url(&url)?;
-            url
-        }
-        None => Input::<String>::new()
-            .with_prompt("Enter WebSocket devnet RPC URL")
-            .default("wss://ethereum-sepolia-rpc.publicnode.com".to_string())
-            .validate_with(init::validate_rpc_url)
-            .interact_text()?,
-    };
-
-    let eth_address: Option<String> = match eth_address {
-        Some(address) => {
-            init::validate_eth_address(&address)?;
-            Some(address)
-        }
-        None => {
-            if skip_eth {
-                None
-            } else {
-                Input::with_theme(&ColorfulTheme::default())
-                    .with_prompt("Enter your Ethereum address (press Enter to skip)")
-                    .allow_empty(true)
-                    .validate_with(init::validate_eth_address)
-                    .interact()
-                    .ok()
-                    .map(|s| if s.is_empty() { None } else { Some(s) })
-                    .flatten()
-            }
-        }
-    };
-
-    let config = init::execute(rpc_url, eth_address).await?;
-
-    password::execute(PasswordCommands::Set { password }, &config).await?;
-
-    if generate_net_keypair {
-        net::execute(
-            NetCommands::Keypair {
-                command: NetKeypairCommands::Generate,
-            },
-            &config,
-        )
-        .await?;
-    } else {
-        net::execute(
-            NetCommands::Keypair {
-                command: NetKeypairCommands::Set { net_keypair },
-            },
-            &config,
-        )
-        .await?;
-    }
-
-    println!("Enclave configuration successfully created!");
-    println!("You can start your node using `enclave start`");
-
-    Ok(())
+pub async fn execute() -> Result<()> {
+    enclave_init::execute().await
 }
diff --git a/packages/ciphernode/enclave/src/main.rs b/packages/ciphernode/enclave/src/main.rs
index 4ea408655b..f0af8c9ded 100644
--- a/packages/ciphernode/enclave/src/main.rs
+++ b/packages/ciphernode/enclave/src/main.rs
@@ -24,6 +24,7 @@ mod password_set;
 mod start;
 mod wallet;
 mod wallet_set;
+mod wizard;
 
 const OWO: &str = r#"
       ___           ___           ___                         ___                         ___     
diff --git a/packages/ciphernode/enclave/src/wizard.rs b/packages/ciphernode/enclave/src/wizard.rs
new file mode 100644
index 0000000000..9e352579d5
--- /dev/null
+++ b/packages/ciphernode/enclave/src/wizard.rs
@@ -0,0 +1,79 @@
+use anyhow::Result;
+use dialoguer::{theme::ColorfulTheme, Input};
+use enclave_core::wizard;
+use tracing::instrument;
+
+use crate::net;
+use crate::net::{NetCommands, NetKeypairCommands};
+use crate::password;
+use crate::password::PasswordCommands;
+
+#[instrument(name = "app", skip_all)]
+pub async fn execute(
+    rpc_url: Option<String>,
+    eth_address: Option<String>,
+    password: Option<String>,
+    skip_eth: bool,
+    net_keypair: Option<String>,
+    generate_net_keypair: bool,
+) -> Result<()> {
+    let rpc_url = match rpc_url {
+        Some(url) => {
+            wizard::validate_rpc_url(&url)?;
+            url
+        }
+        None => Input::<String>::new()
+            .with_prompt("Enter WebSocket devnet RPC URL")
+            .default("wss://ethereum-sepolia-rpc.publicnode.com".to_string())
+            .validate_with(wizard::validate_rpc_url)
+            .interact_text()?,
+    };
+
+    let eth_address: Option<String> = match eth_address {
+        Some(address) => {
+            wizard::validate_eth_address(&address)?;
+            Some(address)
+        }
+        None => {
+            if skip_eth {
+                None
+            } else {
+                Input::with_theme(&ColorfulTheme::default())
+                    .with_prompt("Enter your Ethereum address (press Enter to skip)")
+                    .allow_empty(true)
+                    .validate_with(wizard::validate_eth_address)
+                    .interact()
+                    .ok()
+                    .map(|s| if s.is_empty() { None } else { Some(s) })
+                    .flatten()
+            }
+        }
+    };
+
+    let config = wizard::execute(rpc_url, eth_address).await?;
+
+    password::execute(PasswordCommands::Set { password }, &config).await?;
+
+    if generate_net_keypair {
+        net::execute(
+            NetCommands::Keypair {
+                command: NetKeypairCommands::Generate,
+            },
+            &config,
+        )
+        .await?;
+    } else {
+        net::execute(
+            NetCommands::Keypair {
+                command: NetKeypairCommands::Set { net_keypair },
+            },
+            &config,
+        )
+        .await?;
+    }
+
+    println!("Enclave configuration successfully created!");
+    println!("You can start your node using `enclave start`");
+
+    Ok(())
+}
diff --git a/packages/ciphernode/enclave_core/src/lib.rs b/packages/ciphernode/enclave_core/src/lib.rs
index f596700e63..cf6229f249 100644
--- a/packages/ciphernode/enclave_core/src/lib.rs
+++ b/packages/ciphernode/enclave_core/src/lib.rs
@@ -1,7 +1,7 @@
 pub mod helpers;
-pub mod init;
 pub mod net;
 pub mod nodes;
 pub mod password;
 pub mod start;
 pub mod wallet;
+pub mod wizard;
diff --git a/packages/ciphernode/enclave_core/src/init/mod.rs b/packages/ciphernode/enclave_core/src/wizard/mod.rs
similarity index 100%
rename from packages/ciphernode/enclave_core/src/init/mod.rs
rename to packages/ciphernode/enclave_core/src/wizard/mod.rs
diff --git a/packages/ciphernode/init/Cargo.toml b/packages/ciphernode/init/Cargo.toml
new file mode 100644
index 0000000000..9037b6a50e
--- /dev/null
+++ b/packages/ciphernode/init/Cargo.toml
@@ -0,0 +1,10 @@
+[package]
+name = "enclave-init"
+version.workspace = true
+edition.workspace = true
+
+[dependencies]
+anyhow.workspace = true
+tokio.workspace = true
+git2.workspace = true
+async-recursion.workspace = true 
diff --git a/packages/ciphernode/init/src/lib.rs b/packages/ciphernode/init/src/lib.rs
new file mode 100644
index 0000000000..244c99ea32
--- /dev/null
+++ b/packages/ciphernode/init/src/lib.rs
@@ -0,0 +1,180 @@
+use anyhow::{bail, Result};
+use async_recursion::async_recursion;
+use git2::{FetchOptions, IndexAddOption, Repository, Signature};
+use std::env;
+use std::path::Path;
+use tokio::process::Command as TokioCommand;
+
+pub async fn execute() -> Result<()> {
+    let github_repo = "https://github.com/gnosisguild/enclave.git";
+    let template_folder = "examples/basic";
+    let branch = "ry/389-enclave-init-crisp";
+    let temp_dir = "/tmp/enclave-basic-example";
+    let cwd = env::current_dir()?;
+    check_empty_folder(&cwd)?;
+    clone_repo(github_repo, template_folder, branch, temp_dir).await?;
+    Pnpm::run(&["install"]).await?;
+    init_git_repo_if_needed(&cwd).await?;
+    Ok(())
+}
+
+pub struct Pnpm;
+
+impl Pnpm {
+    pub async fn is_available() -> bool {
+        TokioCommand::new("pnpm")
+            .arg("--version")
+            .output()
+            .await
+            .map(|output| output.status.success())
+            .unwrap_or(false)
+    }
+
+    pub async fn run(args: &[&str]) -> Result<()> {
+        let cwd = env::current_dir()?;
+        Self::run_in_dir(&cwd, args).await
+    }
+
+    pub async fn run_in_dir<P: AsRef<Path>>(dir: P, args: &[&str]) -> Result<()> {
+        if !Self::is_available().await {
+            bail!("pnpm is not installed or not available in PATH");
+        }
+
+        let status = TokioCommand::new("pnpm")
+            .args(args)
+            .current_dir(dir)
+            .status()
+            .await?;
+
+        if status.success() {
+            Ok(())
+        } else {
+            bail!("pnpm command failed with exit code: {:?}", status.code());
+        }
+    }
+}
+
+fn is_git_repository<P: AsRef<Path>>(dir: P) -> bool {
+    Repository::open(dir.as_ref()).is_ok()
+}
+
+async fn init_git_repo_if_needed<P: AsRef<Path>>(dir: P) -> Result<()> {
+    let dir = dir.as_ref();
+
+    // Check if directory is already a git repository
+    if is_git_repository(dir) {
+        println!("Directory is already a git repository, skipping initialization.");
+        return Ok(());
+    }
+
+    println!("Initializing git repository...");
+
+    // Initialize new git repository
+    let repo = Repository::init(dir)?;
+
+    // Get the repository index
+    let mut index = repo.index()?;
+
+    // Add all files to the index
+    index.add_all(["*"].iter(), IndexAddOption::DEFAULT, None)?;
+    index.write()?;
+
+    // Create the tree from the index
+    let tree_id = index.write_tree()?;
+    let tree = repo.find_tree(tree_id)?;
+
+    // Create signature for the commit
+    let signature = Signature::now("Enclave Init", "developers@enclave.gg")?;
+
+    // Create the initial commit
+    repo.commit(
+        Some("HEAD"),
+        &signature,
+        &signature,
+        "Initial commit",
+        &tree,
+        &[],
+    )?;
+
+    println!("Git repository initialized with initial commit.");
+    Ok(())
+}
+
+pub fn check_empty_folder<P: AsRef<Path>>(path: P) -> Result<()> {
+    let path = path.as_ref();
+
+    if !path.exists() {
+        bail!("Path '{}' does not exist", path.display());
+    }
+    if !path.is_dir() {
+        bail!("Path '{}' is not a directory", path.display());
+    }
+
+    let mut entries = std::fs::read_dir(path)
+        .map_err(|e| anyhow::anyhow!("Failed to read directory '{}': {}", path.display(), e))?;
+
+    if entries.next().is_some() {
+        bail!("Directory '{}' is not empty", path.display());
+    }
+
+    Ok(())
+}
+async fn clone_repo(
+    github_repo: &str,
+    template_folder: &str,
+    branch: &str,
+    temp_dir: &str,
+) -> Result<()> {
+    if Path::new(temp_dir).exists() {
+        tokio::fs::remove_dir_all(temp_dir).await?;
+    }
+
+    println!("Cloning repository...");
+    let mut fetch_options = FetchOptions::new();
+    fetch_options.download_tags(git2::AutotagOption::None);
+
+    let mut builder = git2::build::RepoBuilder::new();
+    builder.fetch_options(fetch_options);
+    builder.branch(branch);
+    builder.clone(github_repo, Path::new(temp_dir))?;
+
+    let source_path = Path::new(temp_dir).join(template_folder);
+
+    if !source_path.exists() {
+        anyhow::bail!(
+            "Template folder '{}' not found in repository",
+            template_folder
+        );
+    }
+
+    // Get current working directory
+    let cwd = std::env::current_dir()?;
+
+    // Copy contents using async filesystem operations
+    println!("Copying template contents to current directory...");
+    copy_dir_contents_async(&source_path, &cwd).await?;
+
+    // Clean up temporary directory
+    tokio::fs::remove_dir_all(temp_dir).await?;
+
+    println!("Template copied successfully!");
+    Ok(())
+}
+
+#[async_recursion]
+async fn copy_dir_contents_async(src: &Path, dst: &Path) -> Result<()> {
+    let mut entries = tokio::fs::read_dir(src).await?;
+
+    while let Some(entry) = entries.next_entry().await? {
+        let src_path = entry.path();
+        let dst_path = dst.join(entry.file_name());
+
+        if src_path.is_dir() {
+            tokio::fs::create_dir_all(&dst_path).await?;
+            copy_dir_contents_async(&src_path, &dst_path).await?;
+        } else {
+            tokio::fs::copy(&src_path, &dst_path).await?;
+        }
+    }
+    Ok(())
+}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index eda5e903cb..3ff9477186 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -267,6 +267,69 @@ importers:
         specifier: ^3.3.0
         version: 3.4.1(vite@5.4.18(@types/node@22.7.5))
 
+  examples/basic:
+    devDependencies:
+      '@nomicfoundation/hardhat-chai-matchers':
+        specifier: ^2.0.0
+        version: 2.0.8(@nomicfoundation/hardhat-ethers@3.0.8(ethers@6.13.5(bufferutil@4.0.9)(utf-8-validate@5.0.10))(hardhat@2.22.19(bufferutil@4.0.9)(ts-node@10.9.2(@swc/core@1.11.16(@swc/helpers@0.5.17))(@types/node@22.7.5)(typescript@5.8.2))(typescript@5.8.2)(utf-8-validate@5.0.10)))(chai@4.5.0)(ethers@6.13.5(bufferutil@4.0.9)(utf-8-validate@5.0.10))(hardhat@2.22.19(bufferutil@4.0.9)(ts-node@10.9.2(@swc/core@1.11.16(@swc/helpers@0.5.17))(@types/node@22.7.5)(typescript@5.8.2))(typescript@5.8.2)(utf-8-validate@5.0.10))
+      '@nomicfoundation/hardhat-ethers':
+        specifier: ^3.0.0
+        version: 3.0.8(ethers@6.13.5(bufferutil@4.0.9)(utf-8-validate@5.0.10))(hardhat@2.22.19(bufferutil@4.0.9)(ts-node@10.9.2(@swc/core@1.11.16(@swc/helpers@0.5.17))(@types/node@22.7.5)(typescript@5.8.2))(typescript@5.8.2)(utf-8-validate@5.0.10))
+      '@nomicfoundation/hardhat-ignition':
+        specifier: ^0.15.0
+        version: 0.15.11(@nomicfoundation/hardhat-verify@2.0.13(hardhat@2.22.19(bufferutil@4.0.9)(ts-node@10.9.2(@swc/core@1.11.16(@swc/helpers@0.5.17))(@types/node@22.7.5)(typescript@5.8.2))(typescript@5.8.2)(utf-8-validate@5.0.10)))(bufferutil@4.0.9)(hardhat@2.22.19(bufferutil@4.0.9)(ts-node@10.9.2(@swc/core@1.11.16(@swc/helpers@0.5.17))(@types/node@22.7.5)(typescript@5.8.2))(typescript@5.8.2)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10)
+      '@nomicfoundation/hardhat-ignition-ethers':
+        specifier: ^0.15.0
+        version: 0.15.11(adcb435609ba3eedf4e14820144ed57f)
+      '@nomicfoundation/hardhat-network-helpers':
+        specifier: ^1.0.0
+        version: 1.0.12(hardhat@2.22.19(bufferutil@4.0.9)(ts-node@10.9.2(@swc/core@1.11.16(@swc/helpers@0.5.17))(@types/node@22.7.5)(typescript@5.8.2))(typescript@5.8.2)(utf-8-validate@5.0.10))
+      '@nomicfoundation/hardhat-toolbox':
+        specifier: ^5.0.0
+        version: 5.0.0(34b660bf6f3b32c6dc1600e0ee083f08)
+      '@nomicfoundation/hardhat-verify':
+        specifier: ^2.0.0
+        version: 2.0.13(hardhat@2.22.19(bufferutil@4.0.9)(ts-node@10.9.2(@swc/core@1.11.16(@swc/helpers@0.5.17))(@types/node@22.7.5)(typescript@5.8.2))(typescript@5.8.2)(utf-8-validate@5.0.10))
+      '@typechain/ethers-v6':
+        specifier: ^0.5.0
+        version: 0.5.1(ethers@6.13.5(bufferutil@4.0.9)(utf-8-validate@5.0.10))(typechain@8.3.2(typescript@5.8.2))(typescript@5.8.2)
+      '@typechain/hardhat':
+        specifier: ^9.0.0
+        version: 9.1.0(@typechain/ethers-v6@0.5.1(ethers@6.13.5(bufferutil@4.0.9)(utf-8-validate@5.0.10))(typechain@8.3.2(typescript@5.8.2))(typescript@5.8.2))(ethers@6.13.5(bufferutil@4.0.9)(utf-8-validate@5.0.10))(hardhat@2.22.19(bufferutil@4.0.9)(ts-node@10.9.2(@swc/core@1.11.16(@swc/helpers@0.5.17))(@types/node@22.7.5)(typescript@5.8.2))(typescript@5.8.2)(utf-8-validate@5.0.10))(typechain@8.3.2(typescript@5.8.2))
+      '@types/chai':
+        specifier: ^4.2.0
+        version: 4.3.20
+      '@types/mocha':
+        specifier: '>=9.1.0'
+        version: 10.0.10
+      '@types/node':
+        specifier: '>=18.0.0'
+        version: 22.7.5
+      chai:
+        specifier: ^4.2.0
+        version: 4.5.0
+      ethers:
+        specifier: ^6.4.0
+        version: 6.13.5(bufferutil@4.0.9)(utf-8-validate@5.0.10)
+      hardhat:
+        specifier: ^2.14.0
+        version: 2.22.19(bufferutil@4.0.9)(ts-node@10.9.2(@swc/core@1.11.16(@swc/helpers@0.5.17))(@types/node@22.7.5)(typescript@5.8.2))(typescript@5.8.2)(utf-8-validate@5.0.10)
+      hardhat-gas-reporter:
+        specifier: ^1.0.8
+        version: 1.0.10(bufferutil@4.0.9)(hardhat@2.22.19(bufferutil@4.0.9)(ts-node@10.9.2(@swc/core@1.11.16(@swc/helpers@0.5.17))(@types/node@22.7.5)(typescript@5.8.2))(typescript@5.8.2)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10)
+      solidity-coverage:
+        specifier: ^0.8.0
+        version: 0.8.14(hardhat@2.22.19(bufferutil@4.0.9)(ts-node@10.9.2(@swc/core@1.11.16(@swc/helpers@0.5.17))(@types/node@22.7.5)(typescript@5.8.2))(typescript@5.8.2)(utf-8-validate@5.0.10))
+      ts-node:
+        specifier: '>=8.0.0'
+        version: 10.9.2(@swc/core@1.11.16(@swc/helpers@0.5.17))(@types/node@22.7.5)(typescript@5.8.2)
+      typechain:
+        specifier: ^8.3.0
+        version: 8.3.2(typescript@5.8.2)
+      typescript:
+        specifier: '>=4.5.0'
+        version: 5.8.2
+
   packages/evm:
     dependencies:
       '@excubiae/contracts':
@@ -10443,6 +10506,17 @@ snapshots:
       hardhat: 2.22.19(bufferutil@4.0.9)(ts-node@10.9.2(@swc/core@1.11.16(@swc/helpers@0.5.17))(@types/node@20.17.30)(typescript@5.8.2))(typescript@5.8.2)(utf-8-validate@5.0.10)
       ordinal: 1.0.3
 
+  '@nomicfoundation/hardhat-chai-matchers@2.0.8(@nomicfoundation/hardhat-ethers@3.0.8(ethers@6.13.5(bufferutil@4.0.9)(utf-8-validate@5.0.10))(hardhat@2.22.19(bufferutil@4.0.9)(ts-node@10.9.2(@swc/core@1.11.16(@swc/helpers@0.5.17))(@types/node@22.7.5)(typescript@5.8.2))(typescript@5.8.2)(utf-8-validate@5.0.10)))(chai@4.5.0)(ethers@6.13.5(bufferutil@4.0.9)(utf-8-validate@5.0.10))(hardhat@2.22.19(bufferutil@4.0.9)(ts-node@10.9.2(@swc/core@1.11.16(@swc/helpers@0.5.17))(@types/node@22.7.5)(typescript@5.8.2))(typescript@5.8.2)(utf-8-validate@5.0.10))':
+    dependencies:
+      '@nomicfoundation/hardhat-ethers': 3.0.8(ethers@6.13.5(bufferutil@4.0.9)(utf-8-validate@5.0.10))(hardhat@2.22.19(bufferutil@4.0.9)(ts-node@10.9.2(@swc/core@1.11.16(@swc/helpers@0.5.17))(@types/node@22.7.5)(typescript@5.8.2))(typescript@5.8.2)(utf-8-validate@5.0.10))
+      '@types/chai-as-promised': 7.1.8
+      chai: 4.5.0
+      chai-as-promised: 7.1.2(chai@4.5.0)
+      deep-eql: 4.1.4
+      ethers: 6.13.5(bufferutil@4.0.9)(utf-8-validate@5.0.10)
+      hardhat: 2.22.19(bufferutil@4.0.9)(ts-node@10.9.2(@swc/core@1.11.16(@swc/helpers@0.5.17))(@types/node@22.7.5)(typescript@5.8.2))(typescript@5.8.2)(utf-8-validate@5.0.10)
+      ordinal: 1.0.3
+
   '@nomicfoundation/hardhat-ethers@3.0.8(ethers@5.8.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(hardhat@2.22.19(bufferutil@4.0.9)(ts-node@10.9.2(@swc/core@1.11.16(@swc/helpers@0.5.17))(@types/node@22.7.5)(typescript@5.8.2))(typescript@5.8.2)(utf-8-validate@5.0.10))':
     dependencies:
       debug: 4.4.0(supports-color@5.5.0)
@@ -10461,6 +10535,15 @@ snapshots:
     transitivePeerDependencies:
       - supports-color
 
+  '@nomicfoundation/hardhat-ethers@3.0.8(ethers@6.13.5(bufferutil@4.0.9)(utf-8-validate@5.0.10))(hardhat@2.22.19(bufferutil@4.0.9)(ts-node@10.9.2(@swc/core@1.11.16(@swc/helpers@0.5.17))(@types/node@22.7.5)(typescript@5.8.2))(typescript@5.8.2)(utf-8-validate@5.0.10))':
+    dependencies:
+      debug: 4.4.0(supports-color@8.1.1)
+      ethers: 6.13.5(bufferutil@4.0.9)(utf-8-validate@5.0.10)
+      hardhat: 2.22.19(bufferutil@4.0.9)(ts-node@10.9.2(@swc/core@1.11.16(@swc/helpers@0.5.17))(@types/node@22.7.5)(typescript@5.8.2))(typescript@5.8.2)(utf-8-validate@5.0.10)
+      lodash.isequal: 4.5.0
+    transitivePeerDependencies:
+      - supports-color
+
   '@nomicfoundation/hardhat-foundry@1.1.3(hardhat@2.22.19(bufferutil@4.0.9)(ts-node@10.9.2(@swc/core@1.11.16(@swc/helpers@0.5.17))(@types/node@22.7.5)(typescript@5.8.2))(typescript@5.8.2)(utf-8-validate@5.0.10))':
     dependencies:
       hardhat: 2.22.19(bufferutil@4.0.9)(ts-node@10.9.2(@swc/core@1.11.16(@swc/helpers@0.5.17))(@types/node@22.7.5)(typescript@5.8.2))(typescript@5.8.2)(utf-8-validate@5.0.10)
@@ -10474,13 +10557,21 @@ snapshots:
       ethers: 5.8.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)
       hardhat: 2.22.19(bufferutil@4.0.9)(ts-node@10.9.2(@swc/core@1.11.16(@swc/helpers@0.5.17))(@types/node@22.7.5)(typescript@5.8.2))(typescript@5.8.2)(utf-8-validate@5.0.10)
 
+  '@nomicfoundation/hardhat-ignition-ethers@0.15.11(adcb435609ba3eedf4e14820144ed57f)':
+    dependencies:
+      '@nomicfoundation/hardhat-ethers': 3.0.8(ethers@6.13.5(bufferutil@4.0.9)(utf-8-validate@5.0.10))(hardhat@2.22.19(bufferutil@4.0.9)(ts-node@10.9.2(@swc/core@1.11.16(@swc/helpers@0.5.17))(@types/node@22.7.5)(typescript@5.8.2))(typescript@5.8.2)(utf-8-validate@5.0.10))
+      '@nomicfoundation/hardhat-ignition': 0.15.11(@nomicfoundation/hardhat-verify@2.0.13(hardhat@2.22.19(bufferutil@4.0.9)(ts-node@10.9.2(@swc/core@1.11.16(@swc/helpers@0.5.17))(@types/node@22.7.5)(typescript@5.8.2))(typescript@5.8.2)(utf-8-validate@5.0.10)))(bufferutil@4.0.9)(hardhat@2.22.19(bufferutil@4.0.9)(ts-node@10.9.2(@swc/core@1.11.16(@swc/helpers@0.5.17))(@types/node@22.7.5)(typescript@5.8.2))(typescript@5.8.2)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10)
+      '@nomicfoundation/ignition-core': 0.15.11(bufferutil@4.0.9)(utf-8-validate@5.0.10)
+      ethers: 6.13.5(bufferutil@4.0.9)(utf-8-validate@5.0.10)
+      hardhat: 2.22.19(bufferutil@4.0.9)(ts-node@10.9.2(@swc/core@1.11.16(@swc/helpers@0.5.17))(@types/node@22.7.5)(typescript@5.8.2))(typescript@5.8.2)(utf-8-validate@5.0.10)
+
   '@nomicfoundation/hardhat-ignition@0.15.11(@nomicfoundation/hardhat-verify@2.0.13(hardhat@2.22.19(bufferutil@4.0.9)(ts-node@10.9.2(@swc/core@1.11.16(@swc/helpers@0.5.17))(@types/node@22.7.5)(typescript@5.8.2))(typescript@5.8.2)(utf-8-validate@5.0.10)))(bufferutil@4.0.9)(hardhat@2.22.19(bufferutil@4.0.9)(ts-node@10.9.2(@swc/core@1.11.16(@swc/helpers@0.5.17))(@types/node@22.7.5)(typescript@5.8.2))(typescript@5.8.2)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10)':
     dependencies:
       '@nomicfoundation/hardhat-verify': 2.0.13(hardhat@2.22.19(bufferutil@4.0.9)(ts-node@10.9.2(@swc/core@1.11.16(@swc/helpers@0.5.17))(@types/node@22.7.5)(typescript@5.8.2))(typescript@5.8.2)(utf-8-validate@5.0.10))
       '@nomicfoundation/ignition-core': 0.15.11(bufferutil@4.0.9)(utf-8-validate@5.0.10)
       '@nomicfoundation/ignition-ui': 0.15.11
       chalk: 4.1.2
-      debug: 4.4.0(supports-color@5.5.0)
+      debug: 4.4.0(supports-color@8.1.1)
       fs-extra: 10.1.0
       hardhat: 2.22.19(bufferutil@4.0.9)(ts-node@10.9.2(@swc/core@1.11.16(@swc/helpers@0.5.17))(@types/node@22.7.5)(typescript@5.8.2))(typescript@5.8.2)(utf-8-validate@5.0.10)
       json5: 2.2.3
@@ -10520,6 +10611,27 @@ snapshots:
       typechain: 8.3.2(typescript@5.8.2)
       typescript: 5.8.2
 
+  '@nomicfoundation/hardhat-toolbox@5.0.0(34b660bf6f3b32c6dc1600e0ee083f08)':
+    dependencies:
+      '@nomicfoundation/hardhat-chai-matchers': 2.0.8(@nomicfoundation/hardhat-ethers@3.0.8(ethers@6.13.5(bufferutil@4.0.9)(utf-8-validate@5.0.10))(hardhat@2.22.19(bufferutil@4.0.9)(ts-node@10.9.2(@swc/core@1.11.16(@swc/helpers@0.5.17))(@types/node@22.7.5)(typescript@5.8.2))(typescript@5.8.2)(utf-8-validate@5.0.10)))(chai@4.5.0)(ethers@6.13.5(bufferutil@4.0.9)(utf-8-validate@5.0.10))(hardhat@2.22.19(bufferutil@4.0.9)(ts-node@10.9.2(@swc/core@1.11.16(@swc/helpers@0.5.17))(@types/node@22.7.5)(typescript@5.8.2))(typescript@5.8.2)(utf-8-validate@5.0.10))
+      '@nomicfoundation/hardhat-ethers': 3.0.8(ethers@6.13.5(bufferutil@4.0.9)(utf-8-validate@5.0.10))(hardhat@2.22.19(bufferutil@4.0.9)(ts-node@10.9.2(@swc/core@1.11.16(@swc/helpers@0.5.17))(@types/node@22.7.5)(typescript@5.8.2))(typescript@5.8.2)(utf-8-validate@5.0.10))
+      '@nomicfoundation/hardhat-ignition-ethers': 0.15.11(adcb435609ba3eedf4e14820144ed57f)
+      '@nomicfoundation/hardhat-network-helpers': 1.0.12(hardhat@2.22.19(bufferutil@4.0.9)(ts-node@10.9.2(@swc/core@1.11.16(@swc/helpers@0.5.17))(@types/node@22.7.5)(typescript@5.8.2))(typescript@5.8.2)(utf-8-validate@5.0.10))
+      '@nomicfoundation/hardhat-verify': 2.0.13(hardhat@2.22.19(bufferutil@4.0.9)(ts-node@10.9.2(@swc/core@1.11.16(@swc/helpers@0.5.17))(@types/node@22.7.5)(typescript@5.8.2))(typescript@5.8.2)(utf-8-validate@5.0.10))
+      '@typechain/ethers-v6': 0.5.1(ethers@6.13.5(bufferutil@4.0.9)(utf-8-validate@5.0.10))(typechain@8.3.2(typescript@5.8.2))(typescript@5.8.2)
+      '@typechain/hardhat': 9.1.0(@typechain/ethers-v6@0.5.1(ethers@6.13.5(bufferutil@4.0.9)(utf-8-validate@5.0.10))(typechain@8.3.2(typescript@5.8.2))(typescript@5.8.2))(ethers@6.13.5(bufferutil@4.0.9)(utf-8-validate@5.0.10))(hardhat@2.22.19(bufferutil@4.0.9)(ts-node@10.9.2(@swc/core@1.11.16(@swc/helpers@0.5.17))(@types/node@22.7.5)(typescript@5.8.2))(typescript@5.8.2)(utf-8-validate@5.0.10))(typechain@8.3.2(typescript@5.8.2))
+      '@types/chai': 4.3.20
+      '@types/mocha': 10.0.10
+      '@types/node': 22.7.5
+      chai: 4.5.0
+      ethers: 6.13.5(bufferutil@4.0.9)(utf-8-validate@5.0.10)
+      hardhat: 2.22.19(bufferutil@4.0.9)(ts-node@10.9.2(@swc/core@1.11.16(@swc/helpers@0.5.17))(@types/node@22.7.5)(typescript@5.8.2))(typescript@5.8.2)(utf-8-validate@5.0.10)
+      hardhat-gas-reporter: 1.0.10(bufferutil@4.0.9)(hardhat@2.22.19(bufferutil@4.0.9)(ts-node@10.9.2(@swc/core@1.11.16(@swc/helpers@0.5.17))(@types/node@22.7.5)(typescript@5.8.2))(typescript@5.8.2)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10)
+      solidity-coverage: 0.8.14(hardhat@2.22.19(bufferutil@4.0.9)(ts-node@10.9.2(@swc/core@1.11.16(@swc/helpers@0.5.17))(@types/node@22.7.5)(typescript@5.8.2))(typescript@5.8.2)(utf-8-validate@5.0.10))
+      ts-node: 10.9.2(@swc/core@1.11.16(@swc/helpers@0.5.17))(@types/node@22.7.5)(typescript@5.8.2)
+      typechain: 8.3.2(typescript@5.8.2)
+      typescript: 5.8.2
+
   '@nomicfoundation/hardhat-toolbox@5.0.0(b285d134f7bebf40fffb5d0c9e92a2e9)':
     dependencies:
       '@nomicfoundation/hardhat-chai-matchers': 2.0.8(@nomicfoundation/hardhat-ethers@3.0.8(ethers@5.8.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(hardhat@2.22.19(bufferutil@4.0.9)(ts-node@10.9.2(@swc/core@1.11.16(@swc/helpers@0.5.17))(@types/node@22.7.5)(typescript@5.8.2))(typescript@5.8.2)(utf-8-validate@5.0.10)))(chai@4.5.0)(ethers@5.8.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(hardhat@2.22.19(bufferutil@4.0.9)(ts-node@10.9.2(@swc/core@1.11.16(@swc/helpers@0.5.17))(@types/node@22.7.5)(typescript@5.8.2))(typescript@5.8.2)(utf-8-validate@5.0.10))
@@ -10561,7 +10673,7 @@ snapshots:
       '@ethersproject/abi': 5.8.0
       '@ethersproject/address': 5.8.0
       cbor: 8.1.0
-      debug: 4.4.0(supports-color@5.5.0)
+      debug: 4.4.0(supports-color@8.1.1)
       hardhat: 2.22.19(bufferutil@4.0.9)(ts-node@10.9.2(@swc/core@1.11.16(@swc/helpers@0.5.17))(@types/node@22.7.5)(typescript@5.8.2))(typescript@5.8.2)(utf-8-validate@5.0.10)
       lodash.clonedeep: 4.5.0
       picocolors: 1.1.1
@@ -10576,7 +10688,7 @@ snapshots:
       '@ethersproject/address': 5.6.1
       '@nomicfoundation/solidity-analyzer': 0.1.2
       cbor: 9.0.2
-      debug: 4.4.0(supports-color@5.5.0)
+      debug: 4.4.0(supports-color@8.1.1)
       ethers: 6.13.5(bufferutil@4.0.9)(utf-8-validate@5.0.10)
       fs-extra: 10.1.0
       immer: 10.0.2
@@ -11295,6 +11407,14 @@ snapshots:
       hardhat: 2.22.19(bufferutil@4.0.9)(ts-node@10.9.2(@swc/core@1.11.16(@swc/helpers@0.5.17))(@types/node@20.17.30)(typescript@5.8.2))(typescript@5.8.2)(utf-8-validate@5.0.10)
       typechain: 8.3.2(typescript@5.8.2)
 
+  '@typechain/hardhat@9.1.0(@typechain/ethers-v6@0.5.1(ethers@6.13.5(bufferutil@4.0.9)(utf-8-validate@5.0.10))(typechain@8.3.2(typescript@5.8.2))(typescript@5.8.2))(ethers@6.13.5(bufferutil@4.0.9)(utf-8-validate@5.0.10))(hardhat@2.22.19(bufferutil@4.0.9)(ts-node@10.9.2(@swc/core@1.11.16(@swc/helpers@0.5.17))(@types/node@22.7.5)(typescript@5.8.2))(typescript@5.8.2)(utf-8-validate@5.0.10))(typechain@8.3.2(typescript@5.8.2))':
+    dependencies:
+      '@typechain/ethers-v6': 0.5.1(ethers@6.13.5(bufferutil@4.0.9)(utf-8-validate@5.0.10))(typechain@8.3.2(typescript@5.8.2))(typescript@5.8.2)
+      ethers: 6.13.5(bufferutil@4.0.9)(utf-8-validate@5.0.10)
+      fs-extra: 9.1.0
+      hardhat: 2.22.19(bufferutil@4.0.9)(ts-node@10.9.2(@swc/core@1.11.16(@swc/helpers@0.5.17))(@types/node@22.7.5)(typescript@5.8.2))(typescript@5.8.2)(utf-8-validate@5.0.10)
+      typechain: 8.3.2(typescript@5.8.2)
+
   '@types/acorn@4.0.6':
     dependencies:
       '@types/estree': 1.0.7
@@ -11322,11 +11442,11 @@ snapshots:
 
   '@types/bn.js@4.11.6':
     dependencies:
-      '@types/node': 20.10.4
+      '@types/node': 22.7.5
 
   '@types/bn.js@5.1.6':
     dependencies:
-      '@types/node': 20.10.4
+      '@types/node': 22.7.5
 
   '@types/chai-as-promised@7.1.8':
     dependencies:
@@ -11336,7 +11456,7 @@ snapshots:
 
   '@types/concat-stream@1.6.1':
     dependencies:
-      '@types/node': 20.10.4
+      '@types/node': 22.7.5
 
   '@types/d3-scale-chromatic@3.1.0': {}
 
@@ -11358,7 +11478,7 @@ snapshots:
 
   '@types/form-data@0.0.33':
     dependencies:
-      '@types/node': 20.10.4
+      '@types/node': 22.7.5
 
   '@types/fs-extra@11.0.4':
     dependencies:
@@ -11368,7 +11488,7 @@ snapshots:
   '@types/glob@7.2.0':
     dependencies:
       '@types/minimatch': 5.1.2
-      '@types/node': 20.10.4
+      '@types/node': 22.7.5
 
   '@types/hast@2.3.10':
     dependencies:
@@ -11430,7 +11550,7 @@ snapshots:
 
   '@types/pbkdf2@3.1.2':
     dependencies:
-      '@types/node': 20.10.4
+      '@types/node': 22.7.5
 
   '@types/prettier@2.7.3': {}
 
@@ -11457,7 +11577,7 @@ snapshots:
 
   '@types/secp256k1@4.0.6':
     dependencies:
-      '@types/node': 20.10.4
+      '@types/node': 22.7.5
 
   '@types/tinycolor2@1.4.6': {}
 
@@ -12053,7 +12173,7 @@ snapshots:
 
   agent-base@6.0.2:
     dependencies:
-      debug: 4.4.0(supports-color@5.5.0)
+      debug: 4.4.0(supports-color@8.1.1)
     transitivePeerDependencies:
       - supports-color
 
@@ -13899,7 +14019,7 @@ snapshots:
 
   follow-redirects@1.15.9(debug@4.4.0):
     optionalDependencies:
-      debug: 4.4.0(supports-color@5.5.0)
+      debug: 4.4.0(supports-color@8.1.1)
 
   for-each@0.3.5:
     dependencies:
@@ -14357,7 +14477,7 @@ snapshots:
       boxen: 5.1.2
       chokidar: 4.0.3
       ci-info: 2.0.0
-      debug: 4.4.0(supports-color@5.5.0)
+      debug: 4.4.0(supports-color@8.1.1)
       enquirer: 2.4.1
       env-paths: 2.2.1
       ethereum-cryptography: 1.2.0
@@ -14641,7 +14761,7 @@ snapshots:
   https-proxy-agent@5.0.1:
     dependencies:
       agent-base: 6.0.2
-      debug: 4.4.0(supports-color@5.5.0)
+      debug: 4.4.0(supports-color@8.1.1)
     transitivePeerDependencies:
       - supports-color
 
@@ -17782,7 +17902,7 @@ snapshots:
   typechain@8.3.2(typescript@5.8.2):
     dependencies:
       '@types/prettier': 2.7.3
-      debug: 4.4.0(supports-color@5.5.0)
+      debug: 4.4.0(supports-color@8.1.1)
       fs-extra: 7.0.1
       glob: 7.1.7
       js-sha3: 0.8.0


Reviews:
user: login: coderabbitai[bot]
body: **Actionable comments posted: 2**

<details>
<summary>🔭 Outside diff range comments (1)</summary><blockquote>

<details>
<summary>packages/ciphernode/enclave/src/cli.rs (1)</summary><blockquote>

`198-224`: _💡 Verification agent_

<details>
<summary>❓ Verification inconclusive</summary>

**Verify the wizard hiding strategy and documentation.**

The `Wizard` command is properly hidden and retains all the complex initialization parameters. However, consider adding a comment explaining why it's hidden and when it will be unhidden.



```diff
     #[command(hide = true)]
+    // TODO: Unhide when wizard functionality is fully ready
     /// Initialize your ciphernode by setting up a configuration
     Wizard {
```

Also, ensure that the wizard functionality is still being tested even though it's hidden from the CLI help.

---

🏁 Script executed:

```shell
#!/bin/bash
# Description: Verify that wizard functionality has test coverage
# Expected: Find test files that exercise the wizard command

fd -e rs -x grep -l "wizard\|Wizard" {} \; | grep -i test
```

Length of output: 62

---



</details>

**Add TODO comment for hidden Wizard and ensure test coverage**

- In `packages/ciphernode/enclave/src/cli.rs`, add a comment explaining why the `Wizard` subcommand is hidden and when it will be unhidden:
  ```diff
       #[command(hide = true)]
  +    // TODO: Unhide when wizard functionality is fully ready
       /// Initialize your ciphernode by setting up a configuration
       Wizard {
         …
  ```
- I did not find any tests referencing the `Wizard` command in this crate. Please add or verify test coverage for the wizard subcommand to ensure its behavior is exercised, even though it’s hidden from the CLI help.

</blockquote></details>

</blockquote></details>

<details>
<summary>🧹 Nitpick comments (6)</summary><blockquote>

<details>
<summary>examples/basic/.gitignore (1)</summary><blockquote>

`1-18`: **Excellent .gitignore coverage.**
The file excludes key Hardhat, TypeChain, coverage, and Ignition deployment artifacts.  
  
Add additional common entries such as:
```
npm-debug.log*
yarn-error.log*
*.tsbuildinfo
.env.local
```
to further clean up environment and build artifacts.

</blockquote></details>
<details>
<summary>examples/basic/package.json (1)</summary><blockquote>

`1-25`: **Consider adding NPM scripts for common Hardhat tasks.**
Currently there are no `scripts` defined. For better DX, consider adding a `"scripts"` section:
```json
"scripts": {
  "compile": "hardhat compile",
  "test": "hardhat test",
  "coverage": "hardhat coverage",
  "node": "hardhat node",
  "ignite-deploy": "hardhat ignition deploy ./ignition/modules/Lock.ts"
}
```
  
This will streamline running tasks via `npm run`.

</blockquote></details>
<details>
<summary>examples/basic/README.md (1)</summary><blockquote>

`5-13`: **Enhance README with installation step.**
Consider adding instructions to install dependencies before running tasks, for example:
```bash
npm install
``` 
and optionally specifying the required Node.js version for clarity.  
  
This ensures users can easily get started with the example.

</blockquote></details>
<details>
<summary>packages/ciphernode/init/src/lib.rs (1)</summary><blockquote>

`164-180`: **Consider using async metadata check for consistency.**

The function uses synchronous `is_dir()` check within an async context. While this works, it's better to use async operations consistently to avoid potentially blocking the async runtime.


Apply this diff to use async metadata check:

```diff
 #[async_recursion]
 async fn copy_dir_contents_async(src: &Path, dst: &Path) -> Result<()> {
     let mut entries = tokio::fs::read_dir(src).await?;
 
     while let Some(entry) = entries.next_entry().await? {
         let src_path = entry.path();
         let dst_path = dst.join(entry.file_name());
+        let metadata = entry.metadata().await?;
 
-        if src_path.is_dir() {
+        if metadata.is_dir() {
             tokio::fs::create_dir_all(&dst_path).await?;
             copy_dir_contents_async(&src_path, &dst_path).await?;
         } else {
             tokio::fs::copy(&src_path, &dst_path).await?;
         }
     }
     Ok(())
 }
```

</blockquote></details>
<details>
<summary>packages/ciphernode/enclave/src/cli.rs (2)</summary><blockquote>

`7-7`: **Improve import organization for better readability.**

Consider organizing the imports more consistently by grouping related modules together.



```diff
-use crate::{init, net, password, wallet, wizard};
+use crate::{init, password, wallet, wizard};
+use crate::net;
```

Note: The `net` import appears to be used only in the `NetCommands` type on line 2, so you might want to verify if it's still needed as a separate import.

---

`177-179`: **Consider enhancing the command description.**

The description could be more specific about what type of project initialization this performs.



```diff
-    /// Initialize an enclave project
+    /// Initialize a new enclave project in the current directory
     Init,
```

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**


<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 3d00b96a9e6830d747e64fb0be578e1b1d7f1ce8 and 069192c6f1023c9be46c25215f6bcde43abd6db0.

</details>

<details>
<summary>⛔ Files ignored due to path filters (2)</summary>

* `packages/ciphernode/Cargo.lock` is excluded by `!**/*.lock`
* `pnpm-lock.yaml` is excluded by `!**/pnpm-lock.yaml`

</details>

<details>
<summary>📒 Files selected for processing (17)</summary>

* `examples/basic/.gitignore` (1 hunks)
* `examples/basic/README.md` (1 hunks)
* `examples/basic/contracts/Lock.sol` (1 hunks)
* `examples/basic/hardhat.config.ts` (1 hunks)
* `examples/basic/ignition/modules/Lock.ts` (1 hunks)
* `examples/basic/package.json` (1 hunks)
* `examples/basic/test/Lock.ts` (1 hunks)
* `examples/basic/tsconfig.json` (1 hunks)
* `packages/ciphernode/Cargo.toml` (4 hunks)
* `packages/ciphernode/enclave/Cargo.toml` (1 hunks)
* `packages/ciphernode/enclave/src/cli.rs` (5 hunks)
* `packages/ciphernode/enclave/src/init.rs` (1 hunks)
* `packages/ciphernode/enclave/src/main.rs` (1 hunks)
* `packages/ciphernode/enclave/src/wizard.rs` (1 hunks)
* `packages/ciphernode/enclave_core/src/lib.rs` (1 hunks)
* `packages/ciphernode/init/Cargo.toml` (1 hunks)
* `packages/ciphernode/init/src/lib.rs` (1 hunks)

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>🪛 GitHub Actions: Build and Deploy Ciphernode</summary>

<details>
<summary>packages/ciphernode/init/Cargo.toml</summary>

[error] 1-1: Failed to read file: No such file or directory (os error 2). This caused the cargo build to fail due to missing dependency 'enclave-init'.

</details>

</details>

</details>

<details>
<summary>⏰ Context from checks skipped due to timeout of 90000ms (5)</summary>

* GitHub Check: ci
* GitHub Check: test-net
* GitHub Check: prebuild
* GitHub Check: e2e
* GitHub Check: ci

</details>

<details>
<summary>🔇 Additional comments (17)</summary><blockquote>

<details>
<summary>examples/basic/tsconfig.json (1)</summary>

`1-11`: **TypeScript configuration looks solid.**
The strict settings and module interoperability align well with the Hardhat example. No issues found.

</details>
<details>
<summary>examples/basic/hardhat.config.ts (1)</summary>

`1-8`: **Hardhat configuration is minimal and clear.**
Specifying Solidity 0.8.28 and including the toolbox plugin aligns perfectly with the example project's needs.

</details>
<details>
<summary>examples/basic/ignition/modules/Lock.ts (1)</summary>

`1-21`: **LGTM! Well-structured Hardhat Ignition module.**

The module correctly implements the Hardhat Ignition pattern with proper parameter handling and contract deployment configuration. The timestamp constant for January 1st, 2030 (1893456000) is accurate, and the BigInt notation for ONE_GWEI is appropriate for handling large numbers in JavaScript.

</details>
<details>
<summary>examples/basic/test/Lock.ts (1)</summary>

`1-128`: **Excellent comprehensive test suite!**

The test suite demonstrates best practices with:
- Proper use of fixtures for consistent test setup
- Complete coverage of both positive and negative scenarios  
- Validation of events, access controls, and balance changes
- Clear test organization and descriptive test names

This provides robust validation for the Lock contract functionality.

</details>
<details>
<summary>examples/basic/contracts/Lock.sol (1)</summary>

`7-34`: **Well-implemented time-locked withdrawal contract.**

The contract demonstrates solid Solidity practices with:
- Proper access control using owner checks
- Time-based validation for withdrawal timing  
- Event emission for transparency
- Clear error messages in require statements

The overall logic and structure are sound for a basic example contract.

</details>
<details>
<summary>packages/ciphernode/enclave/Cargo.toml (1)</summary>

`17-17`: **Add new workspace dependency for `enclave-init`**: This integrates the new initialization crate into the `enclave` package's dependencies correctly.

</details>
<details>
<summary>packages/ciphernode/enclave/src/main.rs (1)</summary>

`27-27`: **Register `wizard` module in `main.rs`**: Adding `mod wizard;` aligns with the new interactive setup flow. Ensure the corresponding `wizard.rs` file is present and referenced in the CLI dispatch logic.

</details>
<details>
<summary>packages/ciphernode/enclave_core/src/lib.rs (1)</summary>

`7-7`: <details>
<summary>✅ Verification successful</summary>

**Expose `wizard` in enclave_core**: Publicly exporting the `wizard` module reflects the new interactive CLI path. Make sure all references to the old `init` module have been cleaned up.  


---

🏁 Script executed:

```shell
#!/bin/bash
# Verify no remaining imports of enclave_core::init
rg -n "enclave_core::init"
```

Length of output: 26

---



</details>

**Expose `wizard` in enclave_core** – Verified removal of `init` references  
Publicly exporting the `wizard` module reflects the new interactive CLI path. A search for `enclave_core::init` returned no matches, confirming all old `init` references have been cleaned up.

</details>
<details>
<summary>packages/ciphernode/init/Cargo.toml (1)</summary>

`1-10`: **Validate `enclave-init` crate manifest**: The new `enclave-init` package is correctly defined with workspace-inherited version and dependencies.

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 GitHub Actions: Build and Deploy Ciphernode</summary>

[error] 1-1: Failed to read file: No such file or directory (os error 2). This caused the cargo build to fail due to missing dependency 'enclave-init'.

</details>

</details>

</details>
<details>
<summary>packages/ciphernode/Cargo.toml (4)</summary>

`13-13`: **Include `init` in workspace members**: Adding `"init"` to the workspace members ensures the new crate is part of the build.

---

`35-35`: **Register `enclave-init` workspace dependency**: Defining `enclave-init = { path = "./init" }` correctly links the new crate into the workspace.

---

`61-61`: **Add `async-recursion` dependency**: Introducing `async-recursion = "1.1.1"` supports the init crate's async recursion needs.

---

`77-77`: **Add `git2` dependency**: Adding `git2 = "0.20.2"` enables repository cloning functionality in the `enclave-init` crate.

</details>
<details>
<summary>packages/ciphernode/enclave/src/init.rs (1)</summary>

`3-5`: **Clean modular refactoring!**

The separation of initialization logic into the `enclave-init` crate improves modularity and follows good architectural practices.

</details>
<details>
<summary>packages/ciphernode/enclave/src/wizard.rs (1)</summary>

`11-79`: **Well-structured interactive wizard implementation!**

The wizard logic is cleanly implemented with proper validation, error handling, and user-friendly prompts. The separation of concerns between initialization and configuration wizard improves maintainability.

</details>
<details>
<summary>packages/ciphernode/enclave/src/cli.rs (2)</summary>

`84-102`: **LGTM! Clean separation of initialization logic.**

The refactoring successfully separates the simple initialization (`Init`) from the complex wizard-based setup (`Wizard`). The delegation to `init::execute().await?` is clean and follows good separation of concerns.

---

`130-135`: **Consistent error handling for existing configurations.**

The error handling properly prevents both `Init` and `Wizard` commands from running when a configuration already exists. The error messages are clear and informative.

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-29T00:21:25Z
user: login: ryardley
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-29T00:31:13Z
user: login: coderabbitai[bot]
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-29T00:31:42Z

Review comments:
diff_hunk: @@ -0,0 +1,34 @@
+// SPDX-License-Identifier: UNLICENSED
+pragma solidity ^0.8.28;
+
+// Uncomment this line to use console.log
+// import "hardhat/console.sol";
+
+contract Lock {
+    uint public unlockTime;
+    address payable public owner;
+
+    event Withdrawal(uint amount, uint when);
+
+    constructor(uint _unlockTime) payable {
+        require(
+            block.timestamp < _unlockTime,
+            "Unlock time should be in the future"
+        );
+
+        unlockTime = _unlockTime;
+        owner = payable(msg.sender);
+    }
+
+    function withdraw() public {
+        // Uncomment this line, and the import of "hardhat/console.sol", to print a log in your terminal
+        // console.log("Unlock time is %o and block timestamp is %o", unlockTime, block.timestamp);
+
+        require(block.timestamp >= unlockTime, "You can't withdraw yet");
+        require(msg.sender == owner, "You aren't the owner");
+
+        emit Withdrawal(address(this).balance, block.timestamp);
+
+        owner.transfer(address(this).balance);
path: examples/basic/contracts/Lock.sol
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**Replace `.transfer()` with `.call()` for better security.**

The `.transfer()` method has a fixed 2300 gas limit which can fail if the recipient is a contract with an expensive fallback function. Modern best practice is to use `.call{value: amount}("")` instead.



Apply this diff to improve security and compatibility:

```diff
-        owner.transfer(address(this).balance);
+        (bool success, ) = owner.call{value: address(this).balance}("");
+        require(success, "Transfer failed");
```

This change provides better gas handling and clearer error messaging while maintaining the same functionality.


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

```suggestion
        (bool success, ) = owner.call{value: address(this).balance}("");
        require(success, "Transfer failed");
```

</details>

<!-- suggestion_end -->

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
In examples/basic/contracts/Lock.sol at line 32, replace the use of .transfer()
with .call{value: amount}("") to send Ether. This involves changing
owner.transfer(address(this).balance) to a low-level call that forwards all
available gas and checks for success. After the call, verify the returned
success flag and revert the transaction if the call failed to ensure proper
error handling and improved security.
```

</details>

<!-- This is an auto-generated reply by CodeRabbit -->
created_at: 2025-05-29T00:21:24Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,180 @@
+use anyhow::{bail, Result};
+use async_recursion::async_recursion;
+use git2::{FetchOptions, IndexAddOption, Repository, Signature};
+use std::env;
+use std::path::Path;
+use tokio::process::Command as TokioCommand;
+
+pub async fn execute() -> Result<()> {
+    let github_repo = "https://github.com/gnosisguild/enclave.git";
+    let template_folder = "examples/basic";
+    let branch = "ry/389-enclave-init-crisp";
+    let temp_dir = "/tmp/enclave-basic-example";
+    let cwd = env::current_dir()?;
+    check_empty_folder(&cwd)?;
+    clone_repo(github_repo, template_folder, branch, temp_dir).await?;
+    Pnpm::run(&["install"]).await?;
+    init_git_repo_if_needed(&cwd).await?;
+    Ok(())
+}
path: packages/ciphernode/init/src/lib.rs
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**Update branch reference and use cross-platform temp directory.**

The implementation has two issues that should be addressed before merging:

1. The branch name `"ry/389-enclave-init-crisp"` appears to be a feature branch and should be updated to a stable branch like `"main"`.
2. The temp directory path `"/tmp/enclave-basic-example"` is not cross-platform (Windows doesn't have `/tmp`).


Apply this diff to fix both issues:

```diff
 pub async fn execute() -> Result<()> {
     let github_repo = "https://github.com/gnosisguild/enclave.git";
     let template_folder = "examples/basic";
-    let branch = "ry/389-enclave-init-crisp";
-    let temp_dir = "/tmp/enclave-basic-example";
+    let branch = "main";
+    let temp_dir = std::env::temp_dir().join("enclave-basic-example");
     let cwd = env::current_dir()?;
     check_empty_folder(&cwd)?;
-    clone_repo(github_repo, template_folder, branch, temp_dir).await?;
+    clone_repo(github_repo, template_folder, branch, &temp_dir.to_string_lossy()).await?;
     Pnpm::run(&["install"]).await?;
     init_git_repo_if_needed(&cwd).await?;
     Ok(())
 }
```

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
In packages/ciphernode/init/src/lib.rs around lines 8 to 19, update the branch
variable from the feature branch "ry/389-enclave-init-crisp" to the stable
branch "main". Also, replace the hardcoded temp directory path
"/tmp/enclave-basic-example" with a cross-platform solution by using a system
temp directory function or environment variable to ensure compatibility on
Windows and other OSes.
```

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-05-29T00:21:24Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,34 @@
+// SPDX-License-Identifier: UNLICENSED
+pragma solidity ^0.8.28;
+
+// Uncomment this line to use console.log
+// import "hardhat/console.sol";
+
+contract Lock {
+    uint public unlockTime;
+    address payable public owner;
+
+    event Withdrawal(uint amount, uint when);
+
+    constructor(uint _unlockTime) payable {
+        require(
+            block.timestamp < _unlockTime,
+            "Unlock time should be in the future"
+        );
+
+        unlockTime = _unlockTime;
+        owner = payable(msg.sender);
+    }
+
+    function withdraw() public {
+        // Uncomment this line, and the import of "hardhat/console.sol", to print a log in your terminal
+        // console.log("Unlock time is %o and block timestamp is %o", unlockTime, block.timestamp);
+
+        require(block.timestamp >= unlockTime, "You can't withdraw yet");
+        require(msg.sender == owner, "You aren't the owner");
+
+        emit Withdrawal(address(this).balance, block.timestamp);
+
+        owner.transfer(address(this).balance);
path: examples/basic/contracts/Lock.sol
user: login: ryardley
body: This is just a template
created_at: 2025-05-29T00:31:13Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,34 @@
+// SPDX-License-Identifier: UNLICENSED
+pragma solidity ^0.8.28;
+
+// Uncomment this line to use console.log
+// import "hardhat/console.sol";
+
+contract Lock {
+    uint public unlockTime;
+    address payable public owner;
+
+    event Withdrawal(uint amount, uint when);
+
+    constructor(uint _unlockTime) payable {
+        require(
+            block.timestamp < _unlockTime,
+            "Unlock time should be in the future"
+        );
+
+        unlockTime = _unlockTime;
+        owner = payable(msg.sender);
+    }
+
+    function withdraw() public {
+        // Uncomment this line, and the import of "hardhat/console.sol", to print a log in your terminal
+        // console.log("Unlock time is %o and block timestamp is %o", unlockTime, block.timestamp);
+
+        require(block.timestamp >= unlockTime, "You can't withdraw yet");
+        require(msg.sender == owner, "You aren't the owner");
+
+        emit Withdrawal(address(this).balance, block.timestamp);
+
+        owner.transfer(address(this).balance);
path: examples/basic/contracts/Lock.sol
user: login: coderabbitai[bot]
body: @ryardley You're absolutely right! Since this is a template file from the standard Hardhat example, it makes sense to keep it as-is so users get the familiar baseline they expect. Thanks for the clarification.

---

<details>
<summary>✏️ Learnings added</summary>

```
Learnt from: ryardley
PR: gnosisguild/enclave#450
File: examples/basic/contracts/Lock.sol:32-32
Timestamp: 2025-05-29T00:31:40.959Z
Learning: When reviewing files in examples/ directories or files that are explicitly described as templates, be more cautious about suggesting modifications unless they contain critical security vulnerabilities. Template code is often meant to remain unchanged from standard examples that users expect to recognize.
```

> Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure [review instructions](https://docs.coderabbit.ai/guides/review-instructions). You can manage existing learnings in the [app](https://app.coderabbit.ai/learnings).

</details>


<!-- <agentComponentType>chat-answer</agentComponentType> -->

<!-- <rawResChunk><answer>@ryardley You're absolutely right! Since this is a template file from the standard Hardhat example, it makes sense to keep it as-is so users get the familiar baseline they expect. Thanks for the clarification.

---

<details>
<summary>✏️ Learnings added</summary>

```
Learnt from: ryardley
PR: gnosisguild/enclave#450
File: examples/basic/contracts/Lock.sol:32-32
Timestamp: 2025-05-29T00:31:40.959Z
Learning: When reviewing files in examples/ directories or files that are explicitly described as templates, be more cautious about suggesting modifications unless they contain critical security vulnerabilities. Template code is often meant to remain unchanged from standard examples that users expect to recognize.
```

> Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure [review instructions](https://docs.coderabbit.ai/guides/review-instructions). You can manage existing learnings in the [app](https://app.coderabbit.ai/learnings).

</details>

</answer></rawResChunk> -->

<!-- This is an auto-generated reply by CodeRabbit -->
created_at: 2025-05-29T00:31:42Z
author_association: CONTRIBUTOR

PR comments:
user: login: vercel[bot]
created_at: 2025-05-29T00:17:17Z
body: [vc]: #Kjeh7BOncTiB6hmlrrmqy3xeQBBFMWw8DnCl7UzcBtE=:eyJpc01vbm9yZXBvIjp0cnVlLCJ0eXBlIjoiZ2l0aHViIiwicHJvamVjdHMiOlt7Im5hbWUiOiJlbmNsYXZlLWRvY3MiLCJyb290RGlyZWN0b3J5IjoiZG9jcyIsImxpdmVGZWVkYmFjayI6eyJyZXNvbHZlZCI6MCwidW5yZXNvbHZlZCI6MCwidG90YWwiOjAsImxpbmsiOiJlbmNsYXZlLWRvY3MtZ2l0LXJ5LTM4OS1lbmNsYXZlLWluaXQtY3Jpc3AtZ25vc2lzLWd1aWxkLnZlcmNlbC5hcHAifSwiaW5zcGVjdG9yVXJsIjoiaHR0cHM6Ly92ZXJjZWwuY29tL2dub3Npcy1ndWlsZC9lbmNsYXZlLWRvY3MvOGEyaENVZ0cxa1ZZeXlFeExpZHJ6YzZDZHVMUCIsInByZXZpZXdVcmwiOiJlbmNsYXZlLWRvY3MtZ2l0LXJ5LTM4OS1lbmNsYXZlLWluaXQtY3Jpc3AtZ25vc2lzLWd1aWxkLnZlcmNlbC5hcHAiLCJuZXh0Q29tbWl0U3RhdHVzIjoiREVQTE9ZRUQifSx7Im5hbWUiOiJjcmlzcCIsInJvb3REaXJlY3RvcnkiOiJleGFtcGxlcy9DUklTUC9hcHBzL2NsaWVudCIsImxpdmVGZWVkYmFjayI6eyJyZXNvbHZlZCI6MCwidW5yZXNvbHZlZCI6MCwidG90YWwiOjAsImxpbmsiOiJjcmlzcC1naXQtcnktMzg5LWVuY2xhdmUtaW5pdC1jcmlzcC1nbm9zaXMtZ3VpbGQudmVyY2VsLmFwcCJ9LCJpbnNwZWN0b3JVcmwiOiJodHRwczovL3ZlcmNlbC5jb20vZ25vc2lzLWd1aWxkL2NyaXNwLzlqY1JvSEhRc3pxd0g1dEFONTQ3ZWprWndpRXoiLCJwcmV2aWV3VXJsIjoiY3Jpc3AtZ2l0LXJ5LTM4OS1lbmNsYXZlLWluaXQtY3Jpc3AtZ25vc2lzLWd1aWxkLnZlcmNlbC5hcHAiLCJuZXh0Q29tbWl0U3RhdHVzIjoiREVQTE9ZRUQifV19
**The latest updates on your projects**. Learn more about [Vercel for Git ↗︎](https://vercel.link/github-learn-more)

| Name | Status | Preview | Comments | Updated (UTC) |
| :--- | :----- | :------ | :------- | :------ |
| **crisp** | ✅ Ready ([Inspect](https://vercel.com/gnosis-guild/crisp/9jcRoHHQszqwH5tAN547ejkZwiEz)) | [Visit Preview](https://crisp-git-ry-389-enclave-init-crisp-gnosis-guild.vercel.app) | 💬 [**Add feedback**](https://vercel.live/open-feedback/crisp-git-ry-389-enclave-init-crisp-gnosis-guild.vercel.app?via=pr-comment-feedback-link) | May 29, 2025 0:17am |
| **enclave-docs** | ✅ Ready ([Inspect](https://vercel.com/gnosis-guild/enclave-docs/8a2hCUgG1kVYyyExLidrzc6CduLP)) | [Visit Preview](https://enclave-docs-git-ry-389-enclave-init-crisp-gnosis-guild.vercel.app) | 💬 [**Add feedback**](https://vercel.live/open-feedback/enclave-docs-git-ry-389-enclave-init-crisp-gnosis-guild.vercel.app?via=pr-comment-feedback-link) | May 29, 2025 0:17am |



author_association: NONE
user: login: coderabbitai[bot]
created_at: 2025-05-29T00:17:20Z
body: <!-- This is an auto-generated comment: summarize by coderabbit.ai -->
<!-- walkthrough_start -->

## Walkthrough

This change introduces a new "init" crate to the workspace, providing a modularized initialization process for project setup, including cloning a template repository and configuring dependencies. The enclave CLI is refactored to separate initialization into two commands: a simple `Init` and a more interactive `Wizard`, with logic reorganized accordingly. Supporting modules and dependencies are added or updated, and a basic Hardhat example project is included with contract, tests, and deployment tooling.

## Changes

| File(s)                                                                                  | Change Summary                                                                                                                                                                                                                                                      |
|------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `examples/basic/.gitignore`, `README.md`, `contracts/Lock.sol`, `hardhat.config.ts`, ... | Added a complete Hardhat example project with contract (`Lock.sol`), tests, deployment module, configuration, and supporting files.                                                                                                                                |
| `packages/ciphernode/Cargo.toml`, `enclave/Cargo.toml`                                   | Added "init" as a workspace member and dependency; added dependencies (`async-recursion`, `git2`); updated enclave dependencies.                                                                                                                                    |
| `packages/ciphernode/enclave/src/cli.rs`                                                 | Refactored CLI: split `Init` into parameterless `Init` and interactive `Wizard`; updated command handling and error reporting; adjusted imports.                                                                             |
| `packages/ciphernode/enclave/src/init.rs`                                                | Replaced previous logic with a thin wrapper calling `enclave_init::execute()`. Removed all parameter handling and setup logic.                                                                                               |
| `packages/ciphernode/enclave/src/wizard.rs`                                              | Added new module implementing the interactive setup wizard, handling user input, validation, password, and keypair setup.                                                                                                    |
| `packages/ciphernode/enclave/src/main.rs`                                                | Declared new `wizard` module.                                                                                                                                                                                               |
| `packages/ciphernode/enclave_core/src/lib.rs`                                            | Swapped public modules: removed `init`, added `wizard`.                                                                                                                                                                      |
| `packages/ciphernode/init/Cargo.toml`, `init/src/lib.rs`                                 | Introduced new "enclave-init" crate: provides async project initialization, template cloning, dependency installation, and Git setup utilities.                                                                              |

## Sequence Diagram(s)

```mermaid
sequenceDiagram
    participant User
    participant CLI
    participant InitModule
    participant WizardModule
    participant EnclaveInitCrate

    User->>CLI: Run `enclave init`
    CLI->>InitModule: execute()
    InitModule->>EnclaveInitCrate: execute()
    EnclaveInitCrate-->>InitModule: Project initialized
    InitModule-->>CLI: Done

    User->>CLI: Run `enclave wizard`
    CLI->>WizardModule: execute(with params)
    WizardModule->>WizardModule: Validate inputs, prompt user
    WizardModule->>EnclaveInitCrate: Setup config, password, keypair
    EnclaveInitCrate-->>WizardModule: Config written
    WizardModule-->>CLI: Done
```

## Possibly related PRs

- gnosisguild/enclave#344: Implements swarm mode with multi-node commands, process management, and configuration improvements, directly related to modularization and CLI command restructuring in this PR.

## Poem

> In the garden of code, a new crate appears,  
> With wizards and init, it conquers our fears.  
> Templates are cloned, pnpm runs free,  
> Contracts are locked, as secure as can be.  
> The CLI now sparkles, commands split in two—  
> Here’s to fresh starts, and beginnings anew!  
>  
>  (\(\  
> ( -.-)  
> o_(")(")

<!-- walkthrough_end -->
<!-- internal state start -->


<!-- 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 -->

<!-- internal state end -->
<!-- finishing_touch_checkbox_start -->

<details open="true">
<summary>✨ Finishing Touches</summary>

- [ ] <!-- {"checkboxId": "7962f53c-55bc-4827-bfbf-6a18da830691"} --> 📝 Generate Docstrings

</details>

<!-- finishing_touch_checkbox_end -->
<!-- tips_start -->

---

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

<details>
<summary>❤️ Share</summary>

- [X](https://twitter.com/intent/tweet?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A&url=https%3A//coderabbit.ai)
- [Mastodon](https://mastodon.social/share?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A%20https%3A%2F%2Fcoderabbit.ai)
- [Reddit](https://www.reddit.com/submit?title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&text=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code.%20Check%20it%20out%3A%20https%3A//coderabbit.ai)
- [LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fcoderabbit.ai&mini=true&title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&summary=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code)

</details>

<details>
<summary>🪧 Tips</summary>

### Chat

There are 3 ways to chat with [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=gnosisguild/enclave&utm_content=450):

- Review comments: Directly reply to a review comment made by CodeRabbit. Example:
  - `I pushed a fix in commit <commit_id>, please review it.`
  - `Explain this complex logic.`
  - `Open a follow-up GitHub issue for this discussion.`
- Files and specific lines of code (under the "Files changed" tab): Tag `@coderabbitai` in a new review comment at the desired location with your query. Examples:
  - `@coderabbitai explain this code block.`
  -	`@coderabbitai modularize this function.`
- PR comments: Tag `@coderabbitai` in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
  - `@coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.`
  - `@coderabbitai read src/utils.ts and explain its main purpose.`
  - `@coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.`
  - `@coderabbitai help me debug CodeRabbit configuration file.`

### Support

Need help? Create a ticket on our [support page](https://www.coderabbit.ai/contact-us/support) for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

### CodeRabbit Commands (Invoked using PR comments)

- `@coderabbitai pause` to pause the reviews on a PR.
- `@coderabbitai resume` to resume the paused reviews.
- `@coderabbitai review` to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
- `@coderabbitai full review` to do a full review from scratch and review all the files again.
- `@coderabbitai summary` to regenerate the summary of the PR.
- `@coderabbitai generate docstrings` to [generate docstrings](https://docs.coderabbit.ai/finishing-touches/docstrings) for this PR.
- `@coderabbitai generate sequence diagram` to generate a sequence diagram of the changes in this PR.
- `@coderabbitai resolve` resolve all the CodeRabbit review comments.
- `@coderabbitai configuration` to show the current CodeRabbit configuration for the repository.
- `@coderabbitai help` to get help.

### Other keywords and placeholders

- Add `@coderabbitai ignore` anywhere in the PR description to prevent this PR from being reviewed.
- Add `@coderabbitai summary` to generate the high-level summary at a specific location in the PR description.
- Add `@coderabbitai` anywhere in the PR title to generate the title automatically.

### CodeRabbit Configuration File (`.coderabbit.yaml`)

- You can programmatically configure CodeRabbit by adding a `.coderabbit.yaml` file to the root of your repository.
- Please see the [configuration documentation](https://docs.coderabbit.ai/guides/configure-coderabbit) for more information.
- If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: `# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json`

### Documentation and Community

- Visit our [Documentation](https://docs.coderabbit.ai) for detailed information on how to use CodeRabbit.
- Join our [Discord Community](http://discord.gg/coderabbit) to get help, request features, and share feedback.
- Follow us on [X/Twitter](https://twitter.com/coderabbitai) for updates and announcements.

</details>

<!-- tips_end -->
author_association: CONTRIBUTOR
user: login: ryardley
created_at: 2025-05-29T00:17:33Z
body: @coderabbit review

author_association: MEMBER
user: login: coderabbitai[bot]
created_at: 2025-05-29T00:17:39Z
body: <!-- This is an auto-generated reply by CodeRabbit -->
<details>
<summary>✅ Actions performed</summary>

Review triggered.

> Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

</details>
author_association: CONTRIBUTOR