Skip to content

Commit

Permalink
feat: add zksolc support
Browse files Browse the repository at this point in the history
  • Loading branch information
elfedy authored and Karrq committed Oct 4, 2024
1 parent 5de3d53 commit ff9173a
Show file tree
Hide file tree
Showing 31 changed files with 4,123 additions and 4 deletions.
1 change: 1 addition & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ on:
push:
branches: [main]
pull_request:
branches: [main]

env:
CARGO_TERM_COLOR: always
Expand Down
74 changes: 74 additions & 0 deletions .github/workflows/zksync-ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
name: zksync-ci

on:
push:
branches:
- 'zksync-v**'
pull_request:
branches:
- 'zksync-v**'
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
cancel-in-progress: true

env:
CARGO_TERM_COLOR: always

jobs:
doctests:
name: doc tests
runs-on: ubuntu-latest
timeout-minutes: 60
steps:
- uses: actions/checkout@v4
- uses: dtolnay/[email protected]
- uses: Swatinem/rust-cache@v2
with:
cache-on-failure: true
- name: cargo test
run: cargo test --doc
env:
RUST_TEST_THREADS: 2

clippy:
name: clippy
runs-on: ubuntu-latest
timeout-minutes: 60
steps:
- uses: actions/checkout@v4
- uses: dtolnay/[email protected]
with:
components: clippy
- run: cargo clippy --workspace --all-targets --all-features
env:
RUSTFLAGS: -Dwarnings

fmt:
name: fmt
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Install Rust
uses: dtolnay/[email protected]
with:
components: rustfmt

- name: Run rustfmt
run: cargo fmt -- --check

cargo-test:
name: cargo-test
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
ref: ${{ github.event.pull_request.head.sha }}
- uses: dtolnay/[email protected]
- name: Run tests
run: cargo test zk
env:
RUST_BACKTRACE: full
4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -64,3 +64,7 @@ futures-util = "0.3"
tokio = { version = "1.35", features = ["rt-multi-thread"] }

snapbox = "0.6.9"

# zksync
foundry-compilers-artifacts-zksolc = { path = "crates/artifacts/zksolc", version = "0.11.1" }
globset = "0.4"
1 change: 1 addition & 0 deletions crates/artifacts/artifacts/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ workspace = true
[dependencies]
foundry-compilers-artifacts-solc.workspace = true
foundry-compilers-artifacts-vyper.workspace = true
foundry-compilers-artifacts-zksolc.workspace = true

[features]
async = ["foundry-compilers-artifacts-solc/async"]
1 change: 1 addition & 0 deletions crates/artifacts/artifacts/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@

pub use foundry_compilers_artifacts_solc as solc;
pub use foundry_compilers_artifacts_vyper as vyper;
pub use foundry_compilers_artifacts_zksolc as zksolc;
pub use solc::*;
2 changes: 1 addition & 1 deletion crates/artifacts/solc/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1766,7 +1766,7 @@ pub struct StorageLayout {
}

impl StorageLayout {
fn is_empty(&self) -> bool {
pub fn is_empty(&self) -> bool {
self.storage.is_empty() && self.types.is_empty()
}
}
Expand Down
45 changes: 45 additions & 0 deletions crates/artifacts/zksolc/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
[package]
name = "foundry-compilers-artifacts-zksolc"
description = "Rust bindings for ZkSolc JSON artifacts"

version.workspace = true
edition.workspace = true
rust-version.workspace = true
authors.workspace = true
license.workspace = true
homepage.workspace = true
repository.workspace = true
exclude.workspace = true

[lints]
workspace = true

[dependencies]
foundry-compilers-core.workspace = true
foundry-compilers-artifacts-solc.workspace = true

serde.workspace = true
semver.workspace = true
serde_json.workspace = true
tracing.workspace = true
alloy-primitives.workspace = true
alloy-json-abi.workspace = true
rayon.workspace = true
thiserror.workspace = true
md-5.workspace = true
yansi.workspace = true
futures-util = { workspace = true, optional = true }
tokio = { workspace = true, optional = true }

walkdir = "2.4"

[target.'cfg(windows)'.dependencies]
path-slash.workspace = true

[dev-dependencies]
serde_path_to_error = "0.1"
similar-asserts.workspace = true
foundry-compilers-core = { workspace = true, features = ["test-utils"] }

[features]
async = ["dep:tokio", "futures-util", "tokio/fs"]
28 changes: 28 additions & 0 deletions crates/artifacts/zksolc/src/bytecode.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
use std::collections::BTreeMap;

use foundry_compilers_artifacts_solc::{
bytecode::{serialize_bytecode_without_prefix, BytecodeObject},
CompactBytecode, CompactDeployedBytecode,
};
use serde::{Deserialize, Serialize};

#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct Bytecode {
#[serde(serialize_with = "serialize_bytecode_without_prefix")]
pub object: BytecodeObject,
}

// NOTE: distinction between bytecode and deployed bytecode make no sense of zkEvm, but
// we implement these conversions in order to be able to use the Artifacts trait.
impl From<Bytecode> for CompactBytecode {
fn from(bcode: Bytecode) -> Self {
Self { object: bcode.object, source_map: None, link_references: BTreeMap::default() }
}
}

impl From<Bytecode> for CompactDeployedBytecode {
fn from(bcode: Bytecode) -> Self {
Self { bytecode: Some(bcode.into()), immutable_references: BTreeMap::default() }
}
}
88 changes: 88 additions & 0 deletions crates/artifacts/zksolc/src/contract.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
//! Contract related types.
use crate::Evm;
use alloy_json_abi::JsonAbi;
use foundry_compilers_artifacts_solc::{
CompactContractBytecode, CompactContractBytecodeCow, CompactContractRef, DevDoc, StorageLayout,
UserDoc,
};
use serde::{Deserialize, Serialize};
use std::{borrow::Cow, collections::BTreeMap};

/// Represents a compiled solidity contract
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct Contract {
pub abi: Option<JsonAbi>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub metadata: Option<serde_json::Value>,
#[serde(default)]
pub userdoc: UserDoc,
#[serde(default)]
pub devdoc: DevDoc,
/// The contract optimized IR code.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub ir_optimized: Option<String>,
/// The contract storage layout.
#[serde(default, skip_serializing_if = "StorageLayout::is_empty")]
pub storage_layout: StorageLayout,
/// The contract EraVM bytecode hash.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub hash: Option<String>,
/// The contract factory dependencies.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub factory_dependencies: Option<BTreeMap<String, String>>,
/// The contract missing libraries.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub missing_libraries: Option<Vec<String>>,
/// EVM-related outputs
#[serde(default, skip_serializing_if = "Option::is_none")]
pub evm: Option<Evm>,
}

// CompactContract variants
// TODO: for zkEvm, the distinction between bytecode and deployed_bytecode makes little sense,
// and there some fields that the ouptut doesn't provide (e.g: source_map)
// However, we implement these because we get the Artifact trait and can reuse lots of
// the crate's helpers without needing to duplicate everything. Maybe there's a way
// we can get all these without having to add the same bytecode twice on each struct.
// Ideally the Artifacts trait would not be coupled to a specific Contract type
impl<'a> From<&'a Contract> for CompactContractBytecodeCow<'a> {
fn from(artifact: &'a Contract) -> Self {
let (bytecode, deployed_bytecode) = if let Some(ref evm) = artifact.evm {
(
evm.bytecode.clone().map(Into::into).map(Cow::Owned),
evm.bytecode.clone().map(Into::into).map(Cow::Owned),
)
} else {
(None, None)
};
CompactContractBytecodeCow {
abi: artifact.abi.as_ref().map(Cow::Borrowed),
bytecode,
deployed_bytecode,
}
}
}

impl From<Contract> for CompactContractBytecode {
fn from(c: Contract) -> Self {
let bytecode = if let Some(evm) = c.evm { evm.bytecode } else { None };
Self {
abi: c.abi.map(Into::into),
deployed_bytecode: bytecode.clone().map(|b| b.into()),
bytecode: bytecode.clone().map(|b| b.into()),
}
}
}

impl<'a> From<&'a Contract> for CompactContractRef<'a> {
fn from(c: &'a Contract) -> Self {
let (bin, bin_runtime) = if let Some(ref evm) = c.evm {
(evm.bytecode.as_ref().map(|c| &c.object), evm.bytecode.as_ref().map(|c| &c.object))
} else {
(None, None)
};

Self { abi: c.abi.as_ref(), bin, bin_runtime }
}
}
Loading

0 comments on commit ff9173a

Please sign in to comment.