diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..54917a3 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,15 @@ +# To get started with Dependabot version updates, you'll need to specify which +# package ecosystems to update and where the package manifests are located. +# Please see the documentation for all configuration options: +# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file + +version: 2 +updates: + - package-ecosystem: "cargo" + directory: "/" + schedule: + interval: "weekly" + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" \ No newline at end of file diff --git a/.github/workflows/build-setup.yml b/.github/workflows/build-setup.yml new file mode 100644 index 0000000..c272554 --- /dev/null +++ b/.github/workflows/build-setup.yml @@ -0,0 +1,7 @@ +# This file is used to install the Foundry toolchain and verify the installation. + +- name: Install Foundry + uses: foundry-rs/foundry-toolchain@v1 + +- name: Verify Forge installation + run: forge --version \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..c575b10 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,65 @@ +name: CI + +on: + pull_request: + branches: [ main ] + push: + branches: [ main ] + workflow_dispatch: + +concurrency: + group: rust-validation-${{ github.head_ref }} + cancel-in-progress: true + +env: + RUST_BACKTRACE: full + CARGO_TERM_COLOR: always + IN_CI: "true" + +jobs: + formatting: + name: Rustfmt + runs-on: ubuntu-latest + steps: + - name: Checkout Code + uses: actions/checkout@v2 + + - name: Install Rust + uses: actions-rs/toolchain@v1 + with: + toolchain: stable + components: rustfmt + + - name: Check Formatting + run: cargo fmt -- --check + + linting: + timeout-minutes: 120 + name: cargo clippy + runs-on: ubuntu-latest + steps: + - name: checkout code + uses: actions/checkout@v2 + + - name: Install Foundry + uses: foundry-rs/foundry-toolchain@v1 + + - name: Verify Forge installation + run: forge --version + + - name: install rust + uses: actions-rs/toolchain@v1 + with: + toolchain: stable + + - uses: swatinem/rust-cache@v2 + with: + cache-on-failure: "true" + + - name: install protobuf and gmp + run: | + sudo apt-get update + sudo apt-get install -y protobuf-compiler libprotobuf-dev libgmp-dev + + - name: Run Clippy + run: cargo clippy --tests --examples -- -D warnings diff --git a/Cargo.lock b/Cargo.lock index 6b96012..83c9cae 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1479,25 +1479,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "blueprint-build-utils" -version = "0.1.0" -source = "git+https://github.com/tangle-network/blueprint?branch=drew%2Feigensdk-bump#a67b3fc4c5eb2f01e84317dc1e4d0e0086dc171c" -dependencies = [ - "blueprint-std", -] - -[[package]] -name = "blueprint-std" -version = "0.1.0" -source = "git+https://github.com/tangle-network/blueprint?branch=drew%2Feigensdk-bump#a67b3fc4c5eb2f01e84317dc1e4d0e0086dc171c" -dependencies = [ - "colored", - "num-traits", - "rand 0.8.5", - "thiserror 2.0.12", -] - [[package]] name = "bumpalo" version = "3.17.0" @@ -1615,15 +1596,6 @@ dependencies = [ "tracing-error", ] -[[package]] -name = "colored" -version = "3.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fde0e0ec90c9dfb3b4b1a0891a7dcd0e2bffde2f7efed5fe7c9bb00e5bfb915e" -dependencies = [ - "windows-sys 0.59.0", -] - [[package]] name = "const-hex" version = "1.14.0" @@ -1947,7 +1919,8 @@ dependencies = [ [[package]] name = "eigen-crypto-bls" version = "0.5.0" -source = "git+https://github.com/Tjemmmic/eigensdk-rs.git?branch=reader-type-annotations#443b2fd99b25a4e1466f5d46ffa34eb82942c2ee" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76af95503e08dfc9500910301220c4153d4de2345127326c5b4cf407fd1a02e2" dependencies = [ "alloy", "ark-bn254", @@ -1964,7 +1937,8 @@ dependencies = [ [[package]] name = "eigen-crypto-bn254" version = "0.5.0" -source = "git+https://github.com/Tjemmmic/eigensdk-rs.git?branch=reader-type-annotations#443b2fd99b25a4e1466f5d46ffa34eb82942c2ee" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "039664a95a1c6e47fde635b9a5c07f3377901bf0463e10120f96627ac4386a8b" dependencies = [ "ark-bn254", "ark-ec", @@ -1975,7 +1949,8 @@ dependencies = [ [[package]] name = "eigen-utils" version = "0.5.0" -source = "git+https://github.com/Tjemmmic/eigensdk-rs.git?branch=reader-type-annotations#443b2fd99b25a4e1466f5d46ffa34eb82942c2ee" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0813ece83d4e9c95eddad48bf6cbfe2e1c083ffbd9eac87a40d818225be31db0" dependencies = [ "alloy", "regex", @@ -1993,7 +1968,6 @@ dependencies = [ "alloy-provider", "alloy-rpc-types-eth", "alloy-sol-types", - "blueprint-build-utils", "color-eyre", "eigensdk", "serde", @@ -2004,7 +1978,8 @@ dependencies = [ [[package]] name = "eigensdk" version = "0.5.0" -source = "git+https://github.com/Tjemmmic/eigensdk-rs.git?branch=reader-type-annotations#443b2fd99b25a4e1466f5d46ffa34eb82942c2ee" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "755d34a0a79ad84792325ca94e7c5f72d7dc5dbe92cbdd51780fa8ac0b7ba7f6" dependencies = [ "eigen-crypto-bls", "eigen-utils", diff --git a/Cargo.toml b/Cargo.toml index ac81b50..b3a60c2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -37,8 +37,7 @@ broken_intra_doc_links = "deny" [dependencies] color-eyre = { version = "0.6" } -# eigensdk = { version = "0.5.0", features = ["crypto-bls", "utils"] } -eigensdk = { git = "https://github.com/Tjemmmic/eigensdk-rs.git", branch = "reader-type-annotations", features = ["crypto-bls", "utils"] } +eigensdk = { version = "0.5.0", features = ["crypto-bls", "utils"] } serde = { version = "1.0.127", features = ["derive"] } serde_json = { version = "1.0.67", features = ["alloc"] } alloy = { version = "0.12" } @@ -49,6 +48,3 @@ alloy-contract = { version = "0.12" } alloy-provider = { version = "0.12" } alloy-primitives = { version = "0.8" } tracing = { version = "0.1.41" } - -[build-dependencies] -blueprint-build-utils = { git = "https://github.com/tangle-network/blueprint", branch = "main" } diff --git a/build.rs b/build.rs index c86cc4e..7bfcfee 100644 --- a/build.rs +++ b/build.rs @@ -1,7 +1,7 @@ -use std::fs; use std::io::{Read, Write}; -use std::path::Path; +use std::path::{Path, PathBuf}; use std::process::Command; +use std::{env, fs}; #[allow(clippy::too_many_lines, clippy::format_push_string)] fn main() { @@ -12,9 +12,9 @@ fn main() { "./dependencies/eigenlayer-middleware-0.5.4", "./contracts", ]; - blueprint_build_utils::soldeer_install(); - blueprint_build_utils::soldeer_update(); - blueprint_build_utils::build_contracts(contract_dirs); + soldeer_install(); + soldeer_update(); + build_contracts(contract_dirs); // Create bindings directory let src_dir = Path::new("src"); @@ -224,3 +224,183 @@ fn add_imports_to_file(file_path: &str, contract: &str) { file.write_all(new_contents.as_bytes()) .unwrap_or_else(|_| panic!("Failed to write to {}", file_path)); } + +/// Build the Smart contracts at the specified directories. +/// +/// This function will automatically rerun the build if changes are detected in the `src` +/// directory within any of the directories specified. Due to this, it is recommended to +/// ensure that you only pass in directories that contain the `src` directory and won't be +/// modified by anything else in the build script (otherwise, the build will always rerun). +/// +/// # Panics +/// +/// - If the Cargo Manifest directory is not found. +/// - If the `forge` executable is not found. +/// - If the `foundry.toml` file is not found in any of the specified directories +pub fn build_contracts(contract_dirs: Vec<&str>) { + // Get the project root directory + let root = workspace_or_manifest_dir(); + + // Try to find the `forge` executable dynamically + let forge_executable = find_forge_executable(); + + for dir in contract_dirs { + let full_path = root.join(dir).canonicalize().unwrap_or_else(|_| { + println!( + "Directory not found or inaccessible: {}", + root.join(dir).display() + ); + root.join(dir) + }); + + if full_path.exists() { + if full_path != root.join("./contracts") { + // Check if foundry.toml exists and add evm_version if needed + let foundry_toml_path = full_path.join("foundry.toml"); + + // We need to pin the evm_version of each foundry.toml with the same version so contracts are all consistent + if foundry_toml_path.exists() { + // Read the existing foundry.toml + let mut content = String::new(); + std::fs::File::open(&foundry_toml_path) + .expect("Failed to open foundry.toml") + .read_to_string(&mut content) + .expect("Failed to read foundry.toml"); + + // Only add evm_version if it's not already there + if !content.contains("evm_version") { + // Find the [profile.default] section + if let Some(pos) = content.find("[profile.default]") { + // Insert evm_version after the section header + let mut new_content = content.clone(); + let insert_pos = content[pos..] + .find('\n') + .map_or(content.len(), |p| p + pos + 1); + new_content.insert_str(insert_pos, " evm_version = \"shanghai\"\n"); + + // Write the modified content back + std::fs::write(&foundry_toml_path, new_content) + .expect("Failed to write to foundry.toml"); + } else { + // If [profile.default] section doesn't exist, append it + let mut file = std::fs::OpenOptions::new() + .append(true) + .open(&foundry_toml_path) + .expect("Failed to open foundry.toml for appending"); + + file.write_all(b"\n[profile.default]\nevm_version = \"shanghai\"\n") + .expect("Failed to append to foundry.toml"); + } + } + } else { + panic!("Failed to read dependency foundry.toml"); + } + } + + // Run forge build with explicit EVM version + let status = Command::new(&forge_executable) + .current_dir(&full_path) + .arg("build") + .arg("--evm-version") + .arg("shanghai") + .arg("--use") + .arg("0.8.27") + .status() + .expect("Failed to execute Forge build"); + + assert!( + status.success(), + "Forge build failed for directory: {}", + full_path.display() + ); + } else { + panic!( + "Directory not found or does not exist: {}", + full_path.display() + ); + } + } +} + +fn is_directory_empty(path: &Path) -> bool { + fs::read_dir(path) + .map(|mut i| i.next().is_none()) + .unwrap_or(true) +} + +fn workspace_or_manifest_dir() -> PathBuf { + let dir = env::var("CARGO_WORKSPACE_DIR") + .or_else(|_| env::var("CARGO_MANIFEST_DIR")) + .expect("neither CARGO_WORKSPACE_DIR nor CARGO_MANIFEST_DIR is set"); + PathBuf::from(dir) +} + +/// Run soldeer's 'install' command if the dependencies directory exists and is not empty. +/// +/// # Panics +/// - If the Cargo Manifest directory is not found. +/// - If the `forge` executable is not found. +/// - If forge's `soldeer` is not installed. +pub fn soldeer_install() { + // Get the project root directory + let root = workspace_or_manifest_dir(); + + // Check if the dependencies directory exists and is not empty + let dependencies_dir = root.join("dependencies"); + if !dependencies_dir.exists() || is_directory_empty(&dependencies_dir) { + let forge_executable = find_forge_executable(); + + println!("Populating dependencies directory"); + let status = Command::new(&forge_executable) + .current_dir(&root) + .args(["soldeer", "install"]) + .status() + .expect("Failed to execute 'forge soldeer install'"); + + assert!(status.success(), "'forge soldeer install' failed"); + } else { + println!("Dependencies directory exists or is not empty. Skipping soldeer install."); + } +} + +/// Run soldeer's `update` command to populate the `dependencies` directory. +/// +/// # Panics +/// - If the Cargo Manifest directory is not found. +/// - If the `forge` executable is not found. +/// - If forge's `soldeer` is not installed. +pub fn soldeer_update() { + // Get the project root directory + let root = workspace_or_manifest_dir(); + + // Try to find the `forge` executable dynamically + let forge_executable = find_forge_executable(); + + let status = Command::new(&forge_executable) + .current_dir(&root) + .args(["soldeer", "update", "-d"]) + .status() + .expect("Failed to execute 'forge soldeer update'"); + + assert!(status.success(), "'forge soldeer update' failed"); +} + +/// Returns a string with the path to the `forge` executable. +/// +/// # Panics +/// - If the `forge` executable is not found i.e., if Foundry is not installed. +#[must_use] +pub fn find_forge_executable() -> String { + // Try to find the `forge` executable dynamically + match Command::new("which").arg("forge").output() { + Ok(output) => { + let path = String::from_utf8_lossy(&output.stdout).trim().to_string(); + assert!( + !path.is_empty(), + "Forge executable not found. Make sure Foundry is installed." + ); + path + } + Err(e) => panic!("Failed to find `forge` executable: {e}"), + } +}