From 0e8a55f6cf5ae0dbb8dcf9863ef4bd71b3dcda3a Mon Sep 17 00:00:00 2001 From: Enrique Ortiz Date: Wed, 4 Oct 2023 13:51:14 +0900 Subject: [PATCH 1/8] feat: add ci/cd --- .github/scripts/install_test_binaries.sh | 89 + .github/workflows/ci.yml | 94 + .gitignore | 1 - README.md | 2 +- src/artifacts/ast/lowfidelity.rs | 217 ++ src/artifacts/ast/macros.rs | 104 + src/artifacts/ast/misc.rs | 113 + src/artifacts/ast/mod.rs | 1105 +++++++++ src/artifacts/ast/util.rs | 1 + src/artifacts/ast/visitor.rs | 1 + src/artifacts/ast/yul.rs | 192 ++ src/artifacts/bytecode.rs | 487 ++++ src/artifacts/contract.rs | 529 +++++ src/artifacts/mod.rs | 2595 ++++++++++++++++++++++ src/artifacts/output_selection.rs | 596 +++++ src/artifacts/serde_helpers.rs | 246 ++ src/report/compiler.rs | 10 +- 17 files changed, 6375 insertions(+), 7 deletions(-) create mode 100644 .github/scripts/install_test_binaries.sh create mode 100644 .github/workflows/ci.yml create mode 100644 src/artifacts/ast/lowfidelity.rs create mode 100644 src/artifacts/ast/macros.rs create mode 100644 src/artifacts/ast/misc.rs create mode 100644 src/artifacts/ast/mod.rs create mode 100644 src/artifacts/ast/util.rs create mode 100644 src/artifacts/ast/visitor.rs create mode 100644 src/artifacts/ast/yul.rs create mode 100644 src/artifacts/bytecode.rs create mode 100644 src/artifacts/contract.rs create mode 100644 src/artifacts/mod.rs create mode 100644 src/artifacts/output_selection.rs create mode 100644 src/artifacts/serde_helpers.rs diff --git a/.github/scripts/install_test_binaries.sh b/.github/scripts/install_test_binaries.sh new file mode 100644 index 00000000..7806d0c5 --- /dev/null +++ b/.github/scripts/install_test_binaries.sh @@ -0,0 +1,89 @@ +#!/usr/bin/env bash +# Installs Solc and Geth binaries +# Note: intended for use only with CI (x86_64 Ubuntu, MacOS or Windows) +set -e + +GETH_BUILD=${GETH_BUILD:-"1.11.2-73b01f40"} + +BIN_DIR=${BIN_DIR:-"$HOME/bin"} + +PLATFORM="$(uname -s | awk '{print tolower($0)}')" +if [ "$PLATFORM" != "linux" ] && [ "$PLATFORM" != "darwin" ]; then + EXT=".exe" +fi + +main() { + mkdir -p "$BIN_DIR" + cd "$BIN_DIR" + export PATH="$BIN_DIR:$PATH" + if [ "$GITHUB_PATH" ]; then + echo "$BIN_DIR" >> "$GITHUB_PATH" + fi + + install_geth & + g=$! + install_solc & + wait $g $! + + echo "" + echo "Installed Geth:" + geth version + echo "" + echo "Installed Solc:" + solc --version +} + +# Installs geth from https://geth.ethereum.org/downloads +install_geth() { + case "$PLATFORM" in + linux|darwin) + name="geth-$PLATFORM-amd64-$GETH_BUILD" + curl -s "https://gethstore.blob.core.windows.net/builds/$name.tar.gz" | tar -xzf - + mv -f "$name/geth" ./ + rm -rf "$name" + chmod +x geth + ;; + *) + name="geth-windows-amd64-$GETH_BUILD" + zip="$name.zip" + curl -so "$zip" "https://gethstore.blob.core.windows.net/builds/$zip" + unzip "$zip" + mv -f "$name/geth.exe" ./ + rm -rf "$name" "$zip" + ;; + esac +} + +# Installs solc from https://binaries.soliditylang.org (https://github.com/ethereum/solc-bin) +install_solc() { + bins_url="https://binaries.soliditylang.org" + case "$PLATFORM" in + linux) bins_url+="/linux-amd64";; + darwin) bins_url+="/macosx-amd64";; + *) bins_url+="/windows-amd64";; + esac + + list=$(curl -s "$bins_url/list.json") + # use latest version + if [ -z "$SOLC_VERSION" ]; then + SOLC_VERSION="$(echo "$list" | jq -r ".latestRelease")" + fi + bin=$(echo "$list" | jq -r ".releases[\"$SOLC_VERSION\"]") + + if [ "$bin" = "null" ]; then + echo "Invalid Solc version: $SOLC_VERSION" 1>&2 + exit 1 + fi + + # windows versions <= 0.7.1 use .zip + if [[ "$bin" = *.zip ]]; then + echo "Cannot install solc <= 0.7.1" 1>&2 + exit 1 + fi + + curl -so "$bin" "$bins_url/$bin" + mv -f "$bin" "solc$EXT" + chmod +x "solc$EXT" +} + +main \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000..b529dd4a --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,94 @@ +name: CI + +on: + push: + branches: [master] + pull_request: + +jobs: + test: + name: test ${{ matrix.flags.name }} (${{ matrix.os }}) + runs-on: ${{ matrix.os }} + timeout-minutes: 30 + strategy: + fail-fast: false + matrix: + os: ["ubuntu-latest", "macos-latest", "windows-latest"] + flags: + - name: no default features + flags: --no-default-features + - name: default features + flags: "" + - name: all features + flags: --all-features + steps: + - uses: actions/checkout@v3 + - uses: dtolnay/rust-toolchain@stable + - name: Install Anvil + uses: foundry-rs/foundry-toolchain@v1 + with: + version: nightly + - name: Install test binaries + shell: bash + run: ./.github/scripts/install_test_binaries.sh + - name: Install nextest + uses: taiki-e/install-action@nextest + - uses: Swatinem/rust-cache@v2 + - name: test ${{ matrix.flags.flags }} + shell: bash + run: | + cargo nextest run \ + ${{ matrix.flags.flags }} \ + --retries 2 + + feature-checks: + name: feature checks + runs-on: ubuntu-latest + timeout-minutes: 45 + steps: + - uses: actions/checkout@v3 + - uses: dtolnay/rust-toolchain@stable + - uses: taiki-e/install-action@cargo-hack + - uses: Swatinem/rust-cache@v2 + - name: cargo hack + run: cargo hack check --feature-powerset --depth 1 --all-targets + + clippy: + name: clippy + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - uses: actions/checkout@v3 + - uses: dtolnay/rust-toolchain@clippy + - uses: Swatinem/rust-cache@v2 + - name: clippy + run: cargo clippy --workspace --all-features --all-targets + env: + RUSTFLAGS: "-D warnings" + + docs: + name: docs + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - uses: actions/checkout@v3 + - uses: dtolnay/rust-toolchain@nightly + with: + components: rust-docs + - uses: Swatinem/rust-cache@v2 + - name: doc + run: cargo doc --workspace --all-features --no-deps --document-private-items + env: + RUSTDOCFLAGS: "--cfg docsrs -D warnings" + + fmt: + name: fmt + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - uses: actions/checkout@v3 + - uses: dtolnay/rust-toolchain@nightly + with: + components: rustfmt + - name: fmt --check + run: cargo fmt --all --check \ No newline at end of file diff --git a/.gitignore b/.gitignore index 3bca0dee..0f04d869 100644 --- a/.gitignore +++ b/.gitignore @@ -2,7 +2,6 @@ /Cargo.lock cache/ -artifacts/ .vscode /.envrc diff --git a/README.md b/README.md index 0c72c4f5..a411a586 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Utilities for working with native `solc` and compiling projects. To also compile contracts during `cargo build` (so that ethers `abigen!` can pull in updated abi automatically) you can configure a `foundry_compilers::Project` in your `build.rs` file -First add `ethers-solc` to your cargo build-dependencies. +First add `foundry-compilers` to your cargo build-dependencies. Once you compiled the project, you can configure cargo change detection with `rerun_if_sources_changed`, so that cargo will execute the `build.rs` file if a contract in the sources directory has changed diff --git a/src/artifacts/ast/lowfidelity.rs b/src/artifacts/ast/lowfidelity.rs new file mode 100644 index 00000000..738f4301 --- /dev/null +++ b/src/artifacts/ast/lowfidelity.rs @@ -0,0 +1,217 @@ +//! Bindings for solc's `ast` output field + +use crate::artifacts::serde_helpers; +use serde::{de::DeserializeOwned, Deserialize, Serialize}; +use std::{collections::BTreeMap, fmt, fmt::Write, str::FromStr}; + +/// Represents the AST field in the solc output +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct Ast { + #[serde(rename = "absolutePath")] + pub absolute_path: String, + pub id: usize, + #[serde(default, rename = "exportedSymbols")] + pub exported_symbols: BTreeMap>, + #[serde(rename = "nodeType")] + pub node_type: NodeType, + #[serde(with = "serde_helpers::display_from_str")] + pub src: SourceLocation, + #[serde(default)] + pub nodes: Vec, + + /// Node attributes that were not deserialized. + #[serde(flatten)] + pub other: BTreeMap, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct Node { + /// The node ID. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub id: Option, + + /// The node type. + #[serde(rename = "nodeType")] + pub node_type: NodeType, + + /// The location of the node in the source file. + #[serde(with = "serde_helpers::display_from_str")] + pub src: SourceLocation, + + /// Child nodes for some node types. + #[serde(default)] + pub nodes: Vec, + + /// Body node for some node types. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub body: Option>, + + /// Node attributes that were not deserialized. + #[serde(flatten)] + pub other: BTreeMap, +} + +impl Node { + /// Deserialize a serialized node attribute. + pub fn attribute(&self, key: impl AsRef) -> Option { + // TODO: Can we avoid this clone? + self.other.get(key.as_ref()).and_then(|v| serde_json::from_value(v.clone()).ok()) + } +} + +/// Represents the source location of a node: `::`. +/// +/// The `length` and `index` can be -1 which is represented as `None` +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct SourceLocation { + pub start: usize, + pub length: Option, + pub index: Option, +} + +impl FromStr for SourceLocation { + type Err = String; + + fn from_str(s: &str) -> Result { + let invalid_location = move || format!("{s} invalid source location"); + + let mut split = s.split(':'); + let start = split + .next() + .ok_or_else(invalid_location)? + .parse::() + .map_err(|_| invalid_location())?; + let length = split + .next() + .ok_or_else(invalid_location)? + .parse::() + .map_err(|_| invalid_location())?; + let index = split + .next() + .ok_or_else(invalid_location)? + .parse::() + .map_err(|_| invalid_location())?; + + let length = if length < 0 { None } else { Some(length as usize) }; + let index = if index < 0 { None } else { Some(index as usize) }; + + Ok(Self { start, length, index }) + } +} + +impl fmt::Display for SourceLocation { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.start.fmt(f)?; + f.write_char(':')?; + if let Some(length) = self.length { + length.fmt(f)?; + } else { + f.write_str("-1")?; + } + f.write_char(':')?; + if let Some(index) = self.index { + index.fmt(f)?; + } else { + f.write_str("-1")?; + } + Ok(()) + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub enum NodeType { + // Expressions + Assignment, + BinaryOperation, + Conditional, + ElementaryTypeNameExpression, + FunctionCall, + FunctionCallOptions, + Identifier, + IndexAccess, + IndexRangeAccess, + Literal, + MemberAccess, + NewExpression, + TupleExpression, + UnaryOperation, + + // Statements + Block, + Break, + Continue, + DoWhileStatement, + EmitStatement, + ExpressionStatement, + ForStatement, + IfStatement, + InlineAssembly, + PlaceholderStatement, + Return, + RevertStatement, + TryStatement, + UncheckedBlock, + VariableDeclarationStatement, + VariableDeclaration, + WhileStatement, + + // Yul statements + YulAssignment, + YulBlock, + YulBreak, + YulContinue, + YulExpressionStatement, + YulLeave, + YulForLoop, + YulFunctionDefinition, + YulIf, + YulSwitch, + YulVariableDeclaration, + + // Yul expressions + YulFunctionCall, + YulIdentifier, + YulLiteral, + + // Yul literals + YulLiteralValue, + YulHexValue, + + // Definitions + ContractDefinition, + FunctionDefinition, + EventDefinition, + ErrorDefinition, + ModifierDefinition, + StructDefinition, + EnumDefinition, + UserDefinedValueTypeDefinition, + + // Directives + PragmaDirective, + ImportDirective, + UsingForDirective, + + // Misc + SourceUnit, + InheritanceSpecifier, + ElementaryTypeName, + FunctionTypeName, + ParameterList, + TryCatchClause, + ModifierInvocation, + + /// An unknown AST node type. + Other(String), +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn can_parse_ast() { + let ast = include_str!("../../../test-data/ast/ast-erc4626.json"); + let _ast: Ast = serde_json::from_str(ast).unwrap(); + } +} diff --git a/src/artifacts/ast/macros.rs b/src/artifacts/ast/macros.rs new file mode 100644 index 00000000..a11ae480 --- /dev/null +++ b/src/artifacts/ast/macros.rs @@ -0,0 +1,104 @@ +/// Macro that expands to a struct with common AST node fields. +macro_rules! ast_node { + ( + $(#[$struct_meta:meta])* + struct $name:ident { + $( + $(#[$field_meta:meta])* + $field:ident: $ty:ty + ),* $(,)? + } + ) => { + $(#[$struct_meta])* + #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] + #[serde(rename_all = "camelCase")] + pub struct $name { + pub id: usize, + #[serde(with = "serde_helpers::display_from_str")] + pub src: SourceLocation, + $( + $(#[$field_meta])* + pub $field: $ty + ),* + } + }; +} + +/// A macro that expands to a struct with common expression node fields. +macro_rules! expr_node { + ( + $(#[$struct_meta:meta])* + struct $name:ident { + $( + $(#[$field_meta:meta])* + $field:ident: $ty:ty + ),* $(,)* + } + ) => { + ast_node!( + $(#[$struct_meta])* + struct $name { + #[serde(default, deserialize_with = "serde_helpers::default_for_null")] + argument_types: Vec, + #[serde(default)] + is_constant: bool, + #[serde(default)] + is_l_value: bool, + #[serde(default)] + is_pure: bool, + #[serde(default)] + l_value_requested: bool, + type_descriptions: TypeDescriptions, + $( + $(#[$field_meta])* + $field: $ty + ),* + } + ); + } +} + +/// A macro that expands to a struct with common statement node fields. +macro_rules! stmt_node { + ( + $(#[$struct_meta:meta])* + struct $name:ident { + $( + $(#[$field_meta:meta])* + $field:ident: $ty:ty + ),* $(,)* + } + ) => { + ast_node!( + $(#[$struct_meta])* + struct $name { + // TODO + documentation: Option, + $( + $(#[$field_meta])* + $field: $ty + ),* + } + ); + } +} + +/// A macro that expands to an enum where each variant also contains a struct of the same name. +/// +/// The inner value of each variant is boxed since AST types are inherently recursive. +macro_rules! node_group { + ($group:ident; $( $name:ident ),* $(,)*) => { + #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] + #[serde(tag = "nodeType")] + pub enum $group { + $( + $name(Box<$name>), + )* + } + }; +} + +pub(crate) use ast_node; +pub(crate) use expr_node; +pub(crate) use node_group; +pub(crate) use stmt_node; diff --git a/src/artifacts/ast/misc.rs b/src/artifacts/ast/misc.rs new file mode 100644 index 00000000..ced7f7a2 --- /dev/null +++ b/src/artifacts/ast/misc.rs @@ -0,0 +1,113 @@ +use serde::{Deserialize, Serialize}; +use std::{fmt, fmt::Write, str::FromStr}; + +/// Represents the source location of a node: `::`. +/// +/// The `start`, `length` and `index` can be -1 which is represented as `None` +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct SourceLocation { + pub start: Option, + pub length: Option, + pub index: Option, +} + +impl FromStr for SourceLocation { + type Err = String; + + fn from_str(s: &str) -> Result { + let invalid_location = move || format!("{s} invalid source location"); + + let mut split = s.split(':'); + let start = split + .next() + .ok_or_else(invalid_location)? + .parse::() + .map_err(|_| invalid_location())?; + let length = split + .next() + .ok_or_else(invalid_location)? + .parse::() + .map_err(|_| invalid_location())?; + let index = split + .next() + .ok_or_else(invalid_location)? + .parse::() + .map_err(|_| invalid_location())?; + + let start = if start < 0 { None } else { Some(start as usize) }; + let length = if length < 0 { None } else { Some(length as usize) }; + let index = if index < 0 { None } else { Some(index as usize) }; + + Ok(Self { start, length, index }) + } +} + +impl fmt::Display for SourceLocation { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if let Some(start) = self.start { + start.fmt(f)?; + } else { + f.write_str("-1")?; + } + f.write_char(':')?; + if let Some(length) = self.length { + length.fmt(f)?; + } else { + f.write_str("-1")?; + } + f.write_char(':')?; + if let Some(index) = self.index { + index.fmt(f)?; + } else { + f.write_str("-1")?; + } + Ok(()) + } +} + +/// Function mutability specifier. +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "lowercase")] +pub enum StateMutability { + Payable, + Pure, + Nonpayable, + View, +} + +/// Variable mutability specifier. +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "lowercase")] +pub enum Mutability { + Mutable, + Immutable, + Constant, +} + +/// Storage location specifier. +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "lowercase")] +pub enum StorageLocation { + Calldata, + Default, + Memory, + Storage, +} + +/// Visibility specifier. +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "lowercase")] +pub enum Visibility { + External, + Public, + Internal, + Private, +} + +/// A type description. +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct TypeDescriptions { + pub type_identifier: Option, + pub type_string: Option, +} diff --git a/src/artifacts/ast/mod.rs b/src/artifacts/ast/mod.rs new file mode 100644 index 00000000..e4e8092d --- /dev/null +++ b/src/artifacts/ast/mod.rs @@ -0,0 +1,1105 @@ +//! Bindings for the Solidity and Yul ASTs. +//! +//! The Yul AST bindings are available in the [yul] module. +//! +//! To gain an overview of the AST, it might be helpful to start at the entry point of a complete +//! Solidity AST: the [SourceUnit] node. +//! +//! # Version Support +//! +//! These types should be compatible with at least Solidity 0.5.x and above, but may also support +//! 0.4.x-0.5.x in most cases. +//! +//! The legacy Solidity AST is not supported. +mod macros; +mod misc; +pub use misc::*; +pub mod util; +pub mod visitor; + +/// A low fidelity representation of the AST. +pub(crate) mod lowfidelity; +pub use lowfidelity::{Ast, Node, NodeType, SourceLocation as LowFidelitySourceLocation}; + +/// Types for the Yul AST. +/// +/// The Yul AST is embedded into the Solidity AST for inline assembly blocks. +pub mod yul; + +use crate::artifacts::serde_helpers; +use macros::{ast_node, expr_node, node_group, stmt_node}; +use serde::{Deserialize, Serialize}; +use std::collections::BTreeMap; +use yul::YulBlock; + +ast_node!( + /// The root node of a Solidity AST. + struct SourceUnit { + #[serde(rename = "absolutePath")] + absolute_path: String, + #[serde(default, rename = "exportedSymbols")] + exported_symbols: BTreeMap>, + #[serde(default)] + license: Option, + #[serde(default, skip_serializing_if = "Vec::is_empty")] + nodes: Vec, + } +); + +node_group! { + SourceUnitPart; + + PragmaDirective, + ImportDirective, + UsingForDirective, + VariableDeclaration, + EnumDefinition, + ErrorDefinition, + FunctionDefinition, + StructDefinition, + UserDefinedValueTypeDefinition, + ContractDefinition, +} + +node_group! { + Expression; + + Assignment, + BinaryOperation, + Conditional, + ElementaryTypeNameExpression, + FunctionCall, + FunctionCallOptions, + Identifier, + IndexAccess, + IndexRangeAccess, + Literal, + MemberAccess, + NewExpression, + TupleExpression, + UnaryOperation, +} + +node_group! { + Statement; + + Block, + Break, + Continue, + DoWhileStatement, + EmitStatement, + ExpressionStatement, + ForStatement, + IfStatement, + InlineAssembly, + PlaceholderStatement, + Return, + RevertStatement, + TryStatement, + UncheckedBlock, + VariableDeclarationStatement, + WhileStatement, + +} + +node_group! { + ContractDefinitionPart; + + EnumDefinition, + ErrorDefinition, + EventDefinition, + FunctionDefinition, + ModifierDefinition, + StructDefinition, + UserDefinedValueTypeDefinition, + UsingForDirective, + VariableDeclaration, +} + +node_group! { + TypeName; + + ArrayTypeName, + ElementaryTypeName, + FunctionTypeName, + Mapping, + UserDefinedTypeName, +} + +// TODO: Better name +node_group! { + UserDefinedTypeNameOrIdentifierPath; + + UserDefinedTypeName, + IdentifierPath, +} + +// TODO: Better name +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[serde(untagged)] +pub enum BlockOrStatement { + Statement(Statement), + Block(Block), +} + +// TODO: Better name +node_group! { + ExpressionOrVariableDeclarationStatement; + + ExpressionStatement, + VariableDeclarationStatement +} + +// TODO: Better name +node_group! { + IdentifierOrIdentifierPath; + + Identifier, + IdentifierPath +} + +ast_node!( + /// A contract definition. + struct ContractDefinition { + name: String, + #[serde(default, with = "serde_helpers::display_from_str_opt")] + name_location: Option, + #[serde(default, rename = "abstract")] + is_abstract: bool, + base_contracts: Vec, + canonical_name: Option, + contract_dependencies: Vec, + #[serde(rename = "contractKind")] + kind: ContractKind, + documentation: Option, + fully_implemented: bool, + linearized_base_contracts: Vec, + nodes: Vec, + scope: usize, + #[serde(default, deserialize_with = "serde_helpers::default_for_null")] + used_errors: Vec, + #[serde(default, deserialize_with = "serde_helpers::default_for_null")] + used_events: Vec, + #[serde(default, rename = "internalFunctionIDs")] + internal_function_ids: BTreeMap, + } +); + +/// All Solidity contract kinds. +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub enum ContractKind { + /// A normal contract. + Contract, + /// An interface. + Interface, + /// A library. + Library, +} + +ast_node!( + /// An inheritance specifier. + struct InheritanceSpecifier { + #[serde(default, deserialize_with = "serde_helpers::default_for_null")] + arguments: Vec, + base_name: UserDefinedTypeNameOrIdentifierPath, + } +); + +expr_node!( + /// An assignment expression. + struct Assignment { + #[serde(rename = "leftHandSide")] + lhs: Expression, + operator: AssignmentOperator, + #[serde(rename = "rightHandSide")] + rhs: Expression, + } +); + +/// Assignment operators. +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub enum AssignmentOperator { + /// Simple assignment (`=`) + #[serde(rename = "=")] + Assign, + /// Add and assign (`+=`) + #[serde(rename = "+=")] + AddAssign, + /// Subtract and assign (`-=`) + #[serde(rename = "-=")] + SubAssign, + /// Multiply and assign (`*=`) + #[serde(rename = "*=")] + MulAssign, + /// Divide and assign (`/=`) + #[serde(rename = "/=")] + DivAssign, + /// Modulo and assign (`%=`) + #[serde(rename = "%=")] + ModAssign, + /// Bitwise or and assign (`|=`) + #[serde(rename = "|=")] + OrAssign, + /// Bitwise and and assign (`&=`) + #[serde(rename = "&=")] + AndAssign, + /// Bitwise xor and assign (`^=`) + #[serde(rename = "^=")] + XorAssign, + /// Right shift and assign (`>>=`) + #[serde(rename = ">>=")] + ShrAssign, + /// Left shift and assign (`<<=`) + #[serde(rename = "<<=")] + ShlAssign, +} + +ast_node!( + /// A binary operation. + struct BinaryOperation { + common_type: TypeDescriptions, + #[serde(rename = "leftExpression")] + lhs: Expression, + operator: BinaryOperator, + #[serde(rename = "rightExpression")] + rhs: Expression, + } +); + +/// Binary operators. +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub enum BinaryOperator { + /// Addition (`+`) + #[serde(rename = "+")] + Add, + /// Subtraction (`-`) + #[serde(rename = "-")] + Sub, + /// Multiplication (`*`) + #[serde(rename = "*")] + Mul, + /// Division (`/`) + #[serde(rename = "/")] + Div, + /// Modulo (`%`) + #[serde(rename = "%")] + Mod, + /// Exponentiation (`**`) + #[serde(rename = "**")] + Pow, + /// Logical and (`&&`) + #[serde(rename = "&&")] + And, + /// Logical or (`||`) + #[serde(rename = "||")] + Or, + /// Not equals (`!=`) + #[serde(rename = "!=")] + NotEqual, + /// Equals (`==`) + #[serde(rename = "==")] + Equal, + /// Less than (`<`) + #[serde(rename = "<")] + LessThan, + /// Less than or equal (`<=`) + #[serde(rename = "<=")] + LessThanOrEqual, + /// Greater than (`>`) + #[serde(rename = ">")] + GreaterThan, + /// Greater than or equal (`>=`) + #[serde(rename = ">=")] + GreaterThanOrEqual, + /// Bitwise xor (`^`) + #[serde(rename = "^")] + Xor, + /// Bitwise not (`~`) + #[serde(rename = "~")] + BitNot, + /// Bitwise and (`&`) + #[serde(rename = "&")] + BitAnd, + /// Bitwise or (`|`) + #[serde(rename = "|")] + BitOr, + /// Shift left (`<<`) + #[serde(rename = "<<")] + Shl, + /// Shift right (`>>`) + #[serde(rename = ">>")] + Shr, +} + +expr_node!( + /// A conditional expression. + struct Conditional { + /// The condition. + condition: Expression, + /// The expression to evaluate if falsy. + false_expression: Expression, + /// The expression to evaluate if truthy. + true_expression: Expression, + } +); + +expr_node!( + struct ElementaryTypeNameExpression { + type_name: ElementaryOrRawTypeName, + } +); + +// TODO: Better name +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[serde(untagged)] +pub enum ElementaryOrRawTypeName { + /// An [ElementaryTypeName] node that describes the type. + /// + /// This variant applies to newer compiler versions. + ElementaryTypeName(ElementaryTypeName), + /// A string representing the type name. + /// + /// This variant applies to older compiler versions. + Raw(String), +} + +ast_node!( + struct ElementaryTypeName { + type_descriptions: TypeDescriptions, + name: String, + state_mutability: Option, + } +); + +expr_node!( + /// A function call expression. + struct FunctionCall { + arguments: Vec, + expression: Expression, + kind: FunctionCallKind, + names: Vec, + #[serde(default)] + try_call: bool, + } +); + +/// Function call kinds. +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub enum FunctionCallKind { + /// A regular function call. + FunctionCall, + /// A type conversion (e.g. `bytes(x)`). + TypeConversion, + /// A struct constructor call (e.g. `MyStruct({ ... })`). + StructConstructorCall, +} + +expr_node!( + /// A function call options expression (e.g. `x.f{gas: 1}`). + struct FunctionCallOptions { + expression: Expression, + names: Vec, + options: Vec, + } +); + +ast_node!( + /// An identifier. + struct Identifier { + #[serde(default, deserialize_with = "serde_helpers::default_for_null")] + argument_types: Vec, + name: String, + overloaded_declarations: Vec, + referenced_declaration: Option, + type_descriptions: TypeDescriptions, + } +); + +expr_node!( + /// An index access. + struct IndexAccess { + base_expression: Expression, + index_expression: Option, + } +); + +expr_node!( + /// An index range access. + struct IndexRangeAccess { + base_expression: Expression, + start_expression: Option, + end_expression: Option, + } +); + +expr_node!( + /// A literal value. + struct Literal { + // TODO + hex_value: String, + kind: LiteralKind, + subdenomination: Option, // TODO + value: Option, // TODO + } +); + +/// Literal kinds. +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub enum LiteralKind { + /// A boolean. + Bool, + /// A number. + Number, + /// A string. + String, + /// A hexadecimal string. + HexString, + /// A unicode string. + UnicodeString, +} + +expr_node!( + /// Member access. + struct MemberAccess { + expression: Expression, + member_name: String, + referenced_declaration: Option, + } +); + +expr_node!( + /// A `new` expression. + struct NewExpression { + type_name: TypeName, + } +); + +ast_node!( + /// An array type name. + struct ArrayTypeName { + type_descriptions: TypeDescriptions, + base_type: TypeName, + length: Option, + } +); + +ast_node!( + /// A function type name. + struct FunctionTypeName { + type_descriptions: TypeDescriptions, + parameter_types: ParameterList, + return_parameter_types: ParameterList, + state_mutability: StateMutability, + visibility: Visibility, + } +); + +ast_node!( + /// A parameter list. + struct ParameterList { + parameters: Vec, + } +); + +ast_node!( + /// A variable declaration. + struct VariableDeclaration { + name: String, + #[serde(default, with = "serde_helpers::display_from_str_opt")] + name_location: Option, + #[serde(default, deserialize_with = "serde_helpers::default_for_null")] + base_functions: Vec, + /// Marks whether or not the variable is a constant before Solidity 0.7.x. + /// + /// After 0.7.x you must use `mutability`. For cross-version compatibility use + /// [`VariableDeclaration::mutability()`]. + #[serde(default)] + constant: bool, + /// Marks whether or not the variable is a state variable before Solidity 0.7.x. + /// + /// After 0.7.x you must use `mutability`. For cross-version compatibility use + /// [`VariableDeclaration::mutability()`]. + #[serde(default)] + state_variable: bool, + documentation: Option, + function_selector: Option, // TODO + #[serde(default)] + indexed: bool, + /// Marks the variable's mutability from Solidity 0.7.x onwards. + /// For cross-version compatibility use [`VariableDeclaration::mutability()`]. + #[serde(default)] + mutability: Option, + overrides: Option, + scope: usize, + storage_location: StorageLocation, + type_descriptions: TypeDescriptions, + type_name: Option, + value: Option, + visibility: Visibility, + } +); + +impl VariableDeclaration { + /// Returns the mutability of the variable that was declared. + /// + /// This is a helper to check variable mutability across Solidity versions. + pub fn mutability(&self) -> &Mutability { + if let Some(mutability) = &self.mutability { + mutability + } else if self.constant { + &Mutability::Constant + } else if self.state_variable { + &Mutability::Mutable + } else { + unreachable!() + } + } +} + +/// Structured documentation (NatSpec). +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[serde(untagged)] +pub enum StructuredDocumentation { + /// The documentation is provided in the form of an AST node. + Parsed { text: String }, + /// The documentation is provided in the form of a string literal. + Text(String), +} + +ast_node!( + /// An override specifier. + struct OverrideSpecifier { + overrides: Vec, + } +); + +ast_node!( + /// A user defined type name. + struct UserDefinedTypeName { + type_descriptions: TypeDescriptions, + contract_scope: Option, // TODO + name: Option, + path_node: Option, + referenced_declaration: isize, + } +); + +ast_node!( + /// An identifier path. + struct IdentifierPath { + name: String, + referenced_declaration: isize, + } +); + +ast_node!( + /// A mapping type. + struct Mapping { + type_descriptions: TypeDescriptions, + key_type: TypeName, + value_type: TypeName, + } +); + +expr_node!( + /// A tuple expression. + struct TupleExpression { + components: Vec>, + is_inline_array: bool, + } +); + +expr_node!( + /// A unary operation. + struct UnaryOperation { + operator: UnaryOperator, + /// Whether the unary operator is before or after the expression (e.g. `x++` vs. `++x`) + prefix: bool, + sub_expression: Expression, + } +); + +/// Unary operators. +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub enum UnaryOperator { + /// Increment (`++`) + #[serde(rename = "++")] + Increment, + /// Decrement (`--`) + #[serde(rename = "--")] + Decrement, + /// Negate (`-`) + #[serde(rename = "-")] + Negate, + /// Not (`!`) + #[serde(rename = "!")] + Not, + /// Bitwise not (`~`) + #[serde(rename = "~")] + BitNot, + /// `delete` + #[serde(rename = "delete")] + Delete, +} + +ast_node!( + /// An enum definition. + struct EnumDefinition { + name: String, + #[serde(default, with = "serde_helpers::display_from_str_opt")] + name_location: Option, + canonical_name: String, + members: Vec, + } +); + +ast_node!( + /// An enum value. + struct EnumValue { + name: String, + #[serde(default, with = "serde_helpers::display_from_str_opt")] + name_location: Option, + } +); + +ast_node!( + /// A custom error definition. + struct ErrorDefinition { + name: String, + #[serde(default, with = "serde_helpers::display_from_str_opt")] + name_location: Option, + documentation: Option, + error_selector: Option, // TODO + parameters: ParameterList, + } +); + +ast_node!( + /// An event definition. + struct EventDefinition { + name: String, + #[serde(default, with = "serde_helpers::display_from_str_opt")] + name_location: Option, + anonymous: bool, + event_selector: Option, // TODO + documentation: Option, + parameters: ParameterList, + } +); + +ast_node!( + /// A function definition. + struct FunctionDefinition { + name: String, + #[serde(default, with = "serde_helpers::display_from_str_opt")] + name_location: Option, + #[serde(default, deserialize_with = "serde_helpers::default_for_null")] + base_functions: Vec, + body: Option, + documentation: Option, + function_selector: Option, // TODO + implemented: bool, + modifiers: Vec, + overrides: Option, + parameters: ParameterList, + return_parameters: ParameterList, + scope: usize, + visibility: Visibility, + /// The kind of function this node defines. Only valid for Solidity versions 0.5.x and + /// above. + /// + /// For cross-version compatibility use [`FunctionDefinition::kind()`]. + kind: Option, + /// The state mutability of the function. + /// + /// Note: This was introduced in Solidity 0.5.x. For cross-version compatibility use + /// [`FunctionDefinition::state_mutability()`]. + #[serde(default)] + state_mutability: Option, + #[serde(default, rename = "virtual")] + is_virtual: bool, + /// Whether or not this function is the constructor. Only valid for Solidity versions below + /// 0.5.x. + /// + /// After 0.5.x you must use `kind`. For cross-version compatibility use + /// [`FunctionDefinition::kind()`]. + #[serde(default)] + is_constructor: bool, + /// Whether or not this function is constant (view or pure). Only valid for Solidity + /// versions below 0.5.x. + /// + /// After 0.5.x you must use `state_mutability`. For cross-version compatibility use + /// [`FunctionDefinition::state_mutability()`]. + #[serde(default)] + is_declared_const: bool, + /// Whether or not this function is payable. Only valid for Solidity versions below + /// 0.5.x. + /// + /// After 0.5.x you must use `state_mutability`. For cross-version compatibility use + /// [`FunctionDefinition::state_mutability()`]. + #[serde(default)] + is_payable: bool, + } +); + +impl FunctionDefinition { + /// The kind of function this node defines. + pub fn kind(&self) -> &FunctionKind { + if let Some(kind) = &self.kind { + kind + } else if self.is_constructor { + &FunctionKind::Constructor + } else { + &FunctionKind::Function + } + } + + /// The state mutability of the function. + /// + /// Note: Before Solidity 0.5.x, this is an approximation, as there was no distinction between + /// `view` and `pure`. + pub fn state_mutability(&self) -> &StateMutability { + if let Some(state_mutability) = &self.state_mutability { + state_mutability + } else if self.is_declared_const { + &StateMutability::View + } else if self.is_payable { + &StateMutability::Payable + } else { + &StateMutability::Nonpayable + } + } +} + +/// Function kinds. +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub enum FunctionKind { + /// A contract function. + Function, + /// A receive function. + Receive, + /// A constructor. + Constructor, + /// A fallback function. + Fallback, + /// A free-standing function. + FreeFunction, +} + +ast_node!( + /// A block of statements. + struct Block { + documentation: Option, + #[serde(default, deserialize_with = "serde_helpers::default_for_null")] + statements: Vec, + } +); + +stmt_node!( + /// The break keyword. + struct Break {} +); + +stmt_node!( + /// The continue keyword. + struct Continue {} +); + +stmt_node!( + /// A do while statement. + struct DoWhileStatement { + block: Block, + condition: Expression, + } +); + +stmt_node!( + /// An emit statement. + struct EmitStatement { + event_call: FunctionCall, + } +); + +stmt_node!( + /// An expression statement. + struct ExpressionStatement { + expression: Expression, + } +); + +stmt_node!( + /// A for statement. + struct ForStatement { + body: BlockOrStatement, + condition: Option, + initialization_expression: Option, + loop_expression: Option, + } +); + +stmt_node!( + /// A variable declaration statement. + struct VariableDeclarationStatement { + assignments: Vec>, + declarations: Vec>, + initial_value: Option, + } +); + +stmt_node!( + /// An if statement. + struct IfStatement { + condition: Expression, + false_body: Option, + true_body: BlockOrStatement, + } +); + +ast_node!( + /// A block of inline assembly. + /// + /// Refer to the [yul] module for Yul AST nodes. + struct InlineAssembly { + documentation: Option, + #[serde(rename = "AST")] + ast: YulBlock, + // TODO: We need this camel case for the AST, but pascal case other places in ethers-solc + //evm_version: EvmVersion, + external_references: Vec, + #[serde(default, deserialize_with = "serde_helpers::default_for_null")] + flags: Vec, + } +); + +/// A reference to an external variable or slot in an inline assembly block. +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct ExternalInlineAssemblyReference { + #[serde(with = "serde_helpers::display_from_str")] + pub src: SourceLocation, + pub declaration: usize, + #[serde(default)] + pub offset: bool, + #[serde(default)] + pub slot: bool, + #[serde(default)] + pub length: bool, + pub value_size: usize, + pub suffix: Option, +} + +/// An assembly reference suffix. +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub enum AssemblyReferenceSuffix { + /// The reference refers to a storage slot. + Slot, + /// The reference refers to an offset. + Offset, + /// The reference refers to a length. + Length, +} + +/// Inline assembly flags. +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub enum InlineAssemblyFlag { + MemorySafe, +} + +stmt_node!( + /// A placeholder statement (`_`) + struct PlaceholderStatement {} +); + +stmt_node!( + /// A return statement. + struct Return { + expression: Option, + function_return_parameters: usize, + } +); + +stmt_node!( + /// A revert statement. + struct RevertStatement { + error_call: FunctionCall, + } +); + +stmt_node!( + /// A try/catch statement. + struct TryStatement { + clauses: Vec, + external_call: FunctionCall, + } +); + +ast_node!( + /// A try/catch clause. + struct TryCatchClause { + block: Block, + error_name: String, + #[serde(default, deserialize_with = "serde_helpers::default_for_null")] + parameters: Vec, + } +); + +stmt_node!( + /// An unchecked block. + struct UncheckedBlock { + statements: Vec, + } +); + +stmt_node!( + /// A while statement. + struct WhileStatement { + body: BlockOrStatement, + condition: Expression, + } +); + +ast_node!( + /// A modifier or base constructor invocation. + struct ModifierInvocation { + #[serde(default, deserialize_with = "serde_helpers::default_for_null")] + arguments: Vec, + kind: Option, + modifier_name: IdentifierOrIdentifierPath, + } +); + +/// Modifier invocation kinds. +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub enum ModifierInvocationKind { + /// A regular modifier invocation. + ModifierInvocation, + /// A base constructor invocation. + BaseConstructorSpecifier, +} + +ast_node!( + /// A modifier definition. + struct ModifierDefinition { + name: String, + #[serde(default, with = "serde_helpers::display_from_str_opt")] + name_location: Option, + #[serde(default, deserialize_with = "serde_helpers::default_for_null")] + base_modifiers: Vec, + body: Block, + documentation: Option, + overrides: Option, + parameters: ParameterList, + #[serde(default, rename = "virtual")] + is_virtual: bool, + visibility: Visibility, + } +); + +ast_node!( + /// A struct definition. + struct StructDefinition { + name: String, + #[serde(default, with = "serde_helpers::display_from_str_opt")] + name_location: Option, + canonical_name: String, + members: Vec, + scope: usize, + visibility: Visibility, + } +); + +ast_node!( + /// A user defined value type definition. + struct UserDefinedValueTypeDefinition { + name: String, + #[serde(default, with = "serde_helpers::display_from_str_opt")] + name_location: Option, + canonical_name: Option, + underlying_type: TypeName, + } +); + +ast_node!( + /// A using for directive. + struct UsingForDirective { + #[serde(default, deserialize_with = "serde_helpers::default_for_null")] + function_list: Vec, + #[serde(default)] + global: bool, + library_name: Option, + type_name: Option, + } +); + +/// A wrapper around [IdentifierPath] for the [UsingForDirective]. +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct FunctionIdentifierPath { + pub function: IdentifierPath, +} + +ast_node!( + /// An import directive. + struct ImportDirective { + absolute_path: String, + file: String, + #[serde(default, with = "serde_helpers::display_from_str_opt")] + name_location: Option, + scope: usize, + source_unit: usize, + symbol_aliases: Vec, + unit_alias: String, + } +); + +/// A symbol alias. +/// +/// Symbol aliases can be defined using the [ImportDirective]. +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct SymbolAlias { + pub foreign: Identifier, + pub local: Option, + #[serde(default, with = "serde_helpers::display_from_str_opt")] + pub name_location: Option, +} + +ast_node!( + /// A pragma directive. + struct PragmaDirective { + literals: Vec, + } +); + +#[cfg(test)] +mod tests { + use super::*; + use std::{fs, path::PathBuf}; + + #[test] + fn can_parse_ast() { + fs::read_dir(PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("test-data").join("ast")) + .unwrap() + .for_each(|path| { + let path = path.unwrap().path(); + let path_str = path.to_string_lossy(); + + let input = fs::read_to_string(&path).unwrap(); + let deserializer = &mut serde_json::Deserializer::from_str(&input); + let result: Result = serde_path_to_error::deserialize(deserializer); + match result { + Err(e) => { + println!("... {path_str} fail: {e}"); + panic!(); + } + Ok(_) => { + println!("... {path_str} ok"); + } + } + }) + } +} diff --git a/src/artifacts/ast/util.rs b/src/artifacts/ast/util.rs new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/src/artifacts/ast/util.rs @@ -0,0 +1 @@ + diff --git a/src/artifacts/ast/visitor.rs b/src/artifacts/ast/visitor.rs new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/src/artifacts/ast/visitor.rs @@ -0,0 +1 @@ + diff --git a/src/artifacts/ast/yul.rs b/src/artifacts/ast/yul.rs new file mode 100644 index 00000000..556083cb --- /dev/null +++ b/src/artifacts/ast/yul.rs @@ -0,0 +1,192 @@ +use super::{macros::node_group, misc::SourceLocation}; +use crate::artifacts::serde_helpers; +use serde::{Deserialize, Serialize}; + +node_group! { + YulStatement; + + YulAssignment, + YulBlock, + YulBreak, + YulContinue, + YulExpressionStatement, + YulLeave, + YulForLoop, + YulFunctionDefinition, + YulIf, + YulSwitch, + YulVariableDeclaration, +} + +node_group! { + YulExpression; + + YulFunctionCall, + YulIdentifier, + YulLiteral, +} + +/// A Yul block. +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct YulBlock { + #[serde(with = "serde_helpers::display_from_str")] + pub src: SourceLocation, + pub statements: Vec, +} + +/// A Yul assignment statement. +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct YulAssignment { + #[serde(with = "serde_helpers::display_from_str")] + pub src: SourceLocation, + pub value: YulExpression, + pub variable_names: Vec, +} + +/// A Yul function call. +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct YulFunctionCall { + #[serde(with = "serde_helpers::display_from_str")] + pub src: SourceLocation, + pub arguments: Vec, + pub function_name: YulIdentifier, +} + +/// A Yul identifier. +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct YulIdentifier { + #[serde(with = "serde_helpers::display_from_str")] + pub src: SourceLocation, + pub name: String, +} + +/// A literal Yul value. +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct YulLiteral { + #[serde(with = "serde_helpers::display_from_str")] + pub src: SourceLocation, + pub hex_value: Option, // TODO + pub value: Option, // TODO + pub kind: YulLiteralKind, + pub type_name: Option, // TODO +} + +/// Yul literal value kinds. +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub enum YulLiteralKind { + /// A number literal. + Number, + /// A string literal. + String, + /// A boolean literal. + Bool, +} + +/// A Yul keyword. +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct YulKeyword { + #[serde(with = "serde_helpers::display_from_str")] + pub src: SourceLocation, +} + +/// The Yul break keyword. +pub type YulBreak = YulKeyword; +/// The Yul continue keyword. +pub type YulContinue = YulKeyword; +/// The Yul leave keyword. +pub type YulLeave = YulKeyword; + +/// A Yul expression statement. +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct YulExpressionStatement { + #[serde(with = "serde_helpers::display_from_str")] + pub src: SourceLocation, + pub expression: YulExpression, +} + +/// A Yul for loop. +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct YulForLoop { + #[serde(with = "serde_helpers::display_from_str")] + pub src: SourceLocation, + pub body: YulBlock, + pub condition: YulExpression, + pub post: YulBlock, + pub pre: YulBlock, +} + +/// A Yul function definition. +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct YulFunctionDefinition { + #[serde(with = "serde_helpers::display_from_str")] + pub src: SourceLocation, + pub body: YulBlock, + pub name: String, + #[serde(default)] + pub parameters: Vec, + #[serde(default)] + pub return_variables: Vec, +} + +/// A Yul type name. +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct YulTypedName { + #[serde(with = "serde_helpers::display_from_str")] + pub src: SourceLocation, + pub name: String, + #[serde(rename = "type")] + pub type_name: String, // TODO +} + +/// A Yul if statement. +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct YulIf { + #[serde(with = "serde_helpers::display_from_str")] + pub src: SourceLocation, + pub body: YulBlock, + pub condition: YulExpression, +} + +/// A Yul switch statement. +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct YulSwitch { + #[serde(with = "serde_helpers::display_from_str")] + pub src: SourceLocation, + pub cases: Vec, + pub expression: YulExpression, +} + +/// A Yul switch statement case. +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct YulCase { + #[serde(with = "serde_helpers::display_from_str")] + pub src: SourceLocation, + pub body: YulBlock, + pub value: YulCaseValue, +} + +/// A Yul switch case value. +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[serde(untagged)] +pub enum YulCaseValue { + /// A case defined by a literal value. + YulLiteral(YulLiteral), + /// The default case + // TODO: How do we make this only match "default"? + Default(String), +} + +/// A Yul variable declaration. +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct YulVariableDeclaration { + #[serde(with = "serde_helpers::display_from_str")] + pub src: SourceLocation, + pub value: Option, + pub variables: Vec, +} diff --git a/src/artifacts/bytecode.rs b/src/artifacts/bytecode.rs new file mode 100644 index 00000000..635aba3c --- /dev/null +++ b/src/artifacts/bytecode.rs @@ -0,0 +1,487 @@ +//! Bytecode related types + +use crate::{ + artifacts::{serde_helpers, FunctionDebugData, GeneratedSource, Offsets}, + sourcemap::{self, SourceMap, SyntaxError}, + utils, +}; +use ethers_core::{abi::Address, types::Bytes}; +use serde::{Deserialize, Serialize, Serializer}; +use std::collections::BTreeMap; + +#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)] +#[serde(rename_all = "camelCase")] +pub struct Bytecode { + /// Debugging information at function level + #[serde(default, skip_serializing_if = "::std::collections::BTreeMap::is_empty")] + pub function_debug_data: BTreeMap, + /// The bytecode as a hex string. + #[serde(serialize_with = "serialize_bytecode_without_prefix")] + pub object: BytecodeObject, + /// Opcodes list (string) + #[serde(default, skip_serializing_if = "Option::is_none")] + pub opcodes: Option, + /// The source mapping as a string. See the source mapping definition. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub source_map: Option, + /// Array of sources generated by the compiler. Currently only contains a + /// single Yul file. + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub generated_sources: Vec, + /// If given, this is an unlinked object. + #[serde(default)] + pub link_references: BTreeMap>>, +} + +#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)] +#[serde(rename_all = "camelCase")] +pub struct CompactBytecode { + /// The bytecode as a hex string. + pub object: BytecodeObject, + /// The source mapping as a string. See the source mapping definition. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub source_map: Option, + /// If given, this is an unlinked object. + #[serde(default)] + pub link_references: BTreeMap>>, +} + +impl CompactBytecode { + /// Returns a new `CompactBytecode` object that contains nothing, as it's the case for + /// interfaces and standalone solidity files that don't contain any contract definitions + pub fn empty() -> Self { + Self { object: Default::default(), source_map: None, link_references: Default::default() } + } + /// Returns the parsed source map + /// + /// See also + pub fn source_map(&self) -> Option> { + self.source_map.as_ref().map(|map| sourcemap::parse(map)) + } + + /// Tries to link the bytecode object with the `file` and `library` name. + /// Replaces all library placeholders with the given address. + /// + /// Returns true if the bytecode object is fully linked, false otherwise + /// This is a noop if the bytecode object is already fully linked. + pub fn link( + &mut self, + file: impl AsRef, + library: impl AsRef, + address: Address, + ) -> bool { + if !self.object.is_unlinked() { + return true + } + + let file = file.as_ref(); + let library = library.as_ref(); + if let Some((key, mut contracts)) = self.link_references.remove_entry(file) { + if contracts.remove(library).is_some() { + self.object.link(file, library, address); + } + if !contracts.is_empty() { + self.link_references.insert(key, contracts); + } + if self.link_references.is_empty() { + return self.object.resolve().is_some() + } + } + false + } +} + +impl From for CompactBytecode { + fn from(bcode: Bytecode) -> CompactBytecode { + CompactBytecode { + object: bcode.object, + source_map: bcode.source_map, + link_references: bcode.link_references, + } + } +} + +impl From for Bytecode { + fn from(bcode: CompactBytecode) -> Bytecode { + Bytecode { + object: bcode.object, + source_map: bcode.source_map, + link_references: bcode.link_references, + function_debug_data: Default::default(), + opcodes: Default::default(), + generated_sources: Default::default(), + } + } +} + +impl From for Bytecode { + fn from(object: BytecodeObject) -> Bytecode { + Bytecode { + object, + function_debug_data: Default::default(), + opcodes: Default::default(), + source_map: Default::default(), + generated_sources: Default::default(), + link_references: Default::default(), + } + } +} + +impl Bytecode { + /// Returns the parsed source map + /// + /// See also + pub fn source_map(&self) -> Option> { + self.source_map.as_ref().map(|map| sourcemap::parse(map)) + } + + /// Same as `Bytecode::link` but with fully qualified name (`file.sol:Math`) + pub fn link_fully_qualified(&mut self, name: impl AsRef, addr: Address) -> bool { + if let Some((file, lib)) = name.as_ref().split_once(':') { + self.link(file, lib, addr) + } else { + false + } + } + + /// Tries to link the bytecode object with the `file` and `library` name. + /// Replaces all library placeholders with the given address. + /// + /// Returns true if the bytecode object is fully linked, false otherwise + /// This is a noop if the bytecode object is already fully linked. + pub fn link( + &mut self, + file: impl AsRef, + library: impl AsRef, + address: Address, + ) -> bool { + if !self.object.is_unlinked() { + return true + } + + let file = file.as_ref(); + let library = library.as_ref(); + if let Some((key, mut contracts)) = self.link_references.remove_entry(file) { + if contracts.remove(library).is_some() { + self.object.link(file, library, address); + } + if !contracts.is_empty() { + self.link_references.insert(key, contracts); + } + if self.link_references.is_empty() { + return self.object.resolve().is_some() + } + } + false + } + + /// Links the bytecode object with all provided `(file, lib, addr)` + pub fn link_all(&mut self, libs: I) -> bool + where + I: IntoIterator, + S: AsRef, + T: AsRef, + { + for (file, lib, addr) in libs.into_iter() { + if self.link(file, lib, addr) { + return true + } + } + false + } + + /// Links the bytecode object with all provided `(fully_qualified, addr)` + pub fn link_all_fully_qualified(&mut self, libs: I) -> bool + where + I: IntoIterator, + S: AsRef, + { + for (name, addr) in libs.into_iter() { + if self.link_fully_qualified(name, addr) { + return true + } + } + false + } +} + +/// Represents the bytecode of a contracts that might be not fully linked yet. +#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)] +#[serde(untagged)] +pub enum BytecodeObject { + /// Fully linked bytecode object + #[serde(deserialize_with = "serde_helpers::deserialize_bytes")] + Bytecode(Bytes), + /// Bytecode as hex string that's not fully linked yet and contains library placeholders + #[serde(with = "serde_helpers::string_bytes")] + Unlinked(String), +} + +impl BytecodeObject { + /// Returns the underlying `Bytes` if the object is a valid bytecode, and not empty + pub fn into_bytes(self) -> Option { + match self { + BytecodeObject::Bytecode(bytes) => Some(bytes), + BytecodeObject::Unlinked(_) => None, + } + } + + /// Returns a reference to the underlying `Bytes` if the object is a valid bytecode, and not + /// empty + pub fn as_bytes(&self) -> Option<&Bytes> { + match self { + BytecodeObject::Bytecode(bytes) => Some(bytes), + BytecodeObject::Unlinked(_) => None, + } + } + + /// Returns the number of bytes of the fully linked bytecode + /// + /// Returns `0` if this object is unlinked. + pub fn bytes_len(&self) -> usize { + self.as_bytes().map(|b| b.as_ref().len()).unwrap_or_default() + } + + /// Returns a reference to the underlying `String` if the object is unlinked + pub fn as_str(&self) -> Option<&str> { + match self { + BytecodeObject::Bytecode(_) => None, + BytecodeObject::Unlinked(s) => Some(s.as_str()), + } + } + + /// Returns the unlinked `String` if the object is unlinked or empty + pub fn into_unlinked(self) -> Option { + match self { + BytecodeObject::Bytecode(_) => None, + BytecodeObject::Unlinked(code) => Some(code), + } + } + + /// Whether this object is still unlinked + pub fn is_unlinked(&self) -> bool { + matches!(self, BytecodeObject::Unlinked(_)) + } + + /// Whether this object a valid bytecode + pub fn is_bytecode(&self) -> bool { + matches!(self, BytecodeObject::Bytecode(_)) + } + + /// Returns `true` if the object is a valid bytecode and not empty. + /// Returns false the object is a valid but empty bytecode or unlinked. + pub fn is_non_empty_bytecode(&self) -> bool { + self.as_bytes().map(|c| !c.0.is_empty()).unwrap_or_default() + } + + /// Tries to resolve the unlinked string object a valid bytecode object in place + /// + /// Returns the string if it is a valid + pub fn resolve(&mut self) -> Option<&Bytes> { + if let BytecodeObject::Unlinked(unlinked) = self { + if let Ok(linked) = hex::decode(unlinked) { + *self = BytecodeObject::Bytecode(linked.into()); + } + } + self.as_bytes() + } + + /// Link using the fully qualified name of a library + /// The fully qualified library name is the path of its source file and the library name + /// separated by `:` like `file.sol:Math` + /// + /// This will replace all occurrences of the library placeholder with the given address. + /// + /// See also: + pub fn link_fully_qualified(&mut self, name: impl AsRef, addr: Address) -> &mut Self { + if let BytecodeObject::Unlinked(ref mut unlinked) = self { + let name = name.as_ref(); + let place_holder = utils::library_hash_placeholder(name); + // the address as hex without prefix + let hex_addr = hex::encode(addr); + + // the library placeholder used to be the fully qualified name of the library instead of + // the hash. This is also still supported by `solc` so we handle this as well + let fully_qualified_placeholder = utils::library_fully_qualified_placeholder(name); + + *unlinked = unlinked + .replace(&format!("__{fully_qualified_placeholder}__"), &hex_addr) + .replace(&format!("__{place_holder}__"), &hex_addr) + } + self + } + + /// Link using the `file` and `library` names as fully qualified name `:` + /// See `BytecodeObject::link_fully_qualified` + pub fn link( + &mut self, + file: impl AsRef, + library: impl AsRef, + addr: Address, + ) -> &mut Self { + self.link_fully_qualified(format!("{}:{}", file.as_ref(), library.as_ref(),), addr) + } + + /// Links the bytecode object with all provided `(file, lib, addr)` + pub fn link_all(&mut self, libs: I) -> &mut Self + where + I: IntoIterator, + S: AsRef, + T: AsRef, + { + for (file, lib, addr) in libs.into_iter() { + self.link(file, lib, addr); + } + self + } + + /// Whether the bytecode contains a matching placeholder using the qualified name + pub fn contains_fully_qualified_placeholder(&self, name: impl AsRef) -> bool { + if let BytecodeObject::Unlinked(unlinked) = self { + let name = name.as_ref(); + unlinked.contains(&utils::library_hash_placeholder(name)) || + unlinked.contains(&utils::library_fully_qualified_placeholder(name)) + } else { + false + } + } + + /// Whether the bytecode contains a matching placeholder + pub fn contains_placeholder(&self, file: impl AsRef, library: impl AsRef) -> bool { + self.contains_fully_qualified_placeholder(format!("{}:{}", file.as_ref(), library.as_ref())) + } +} + +// Returns an empty bytecode object +impl Default for BytecodeObject { + fn default() -> Self { + BytecodeObject::Bytecode(Default::default()) + } +} + +impl AsRef<[u8]> for BytecodeObject { + fn as_ref(&self) -> &[u8] { + match self { + BytecodeObject::Bytecode(code) => code.as_ref(), + BytecodeObject::Unlinked(code) => code.as_bytes(), + } + } +} + +/// This will serialize the bytecode data without a `0x` prefix, which the `ethers::types::Bytes` +/// adds by default. +/// +/// This ensures that we serialize bytecode data in the same way as solc does, See also +pub fn serialize_bytecode_without_prefix( + bytecode: &BytecodeObject, + s: S, +) -> Result +where + S: Serializer, +{ + match bytecode { + BytecodeObject::Bytecode(code) => s.serialize_str(&hex::encode(code)), + BytecodeObject::Unlinked(code) => { + s.serialize_str(code.strip_prefix("0x").unwrap_or(code.as_str())) + } + } +} + +#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)] +pub struct DeployedBytecode { + #[serde(flatten)] + pub bytecode: Option, + #[serde( + default, + rename = "immutableReferences", + skip_serializing_if = "::std::collections::BTreeMap::is_empty" + )] + pub immutable_references: BTreeMap>, +} + +impl DeployedBytecode { + /// Returns the underlying `Bytes` if the object is a valid bytecode, and not empty + pub fn into_bytes(self) -> Option { + self.bytecode?.object.into_bytes() + } +} + +impl From for DeployedBytecode { + fn from(bcode: Bytecode) -> DeployedBytecode { + DeployedBytecode { bytecode: Some(bcode), immutable_references: Default::default() } + } +} + +#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)] +#[serde(rename_all = "camelCase")] +pub struct CompactDeployedBytecode { + #[serde(flatten)] + pub bytecode: Option, + #[serde( + default, + rename = "immutableReferences", + skip_serializing_if = "::std::collections::BTreeMap::is_empty" + )] + pub immutable_references: BTreeMap>, +} + +impl CompactDeployedBytecode { + /// Returns a new `CompactDeployedBytecode` object that contains nothing, as it's the case for + /// interfaces and standalone solidity files that don't contain any contract definitions + pub fn empty() -> Self { + Self { bytecode: Some(CompactBytecode::empty()), immutable_references: Default::default() } + } + + /// Returns the parsed source map + /// + /// See also + pub fn source_map(&self) -> Option> { + self.bytecode.as_ref().and_then(|bytecode| bytecode.source_map()) + } +} + +impl From for CompactDeployedBytecode { + fn from(bcode: DeployedBytecode) -> CompactDeployedBytecode { + CompactDeployedBytecode { + bytecode: bcode.bytecode.map(|d_bcode| d_bcode.into()), + immutable_references: bcode.immutable_references, + } + } +} + +impl From for DeployedBytecode { + fn from(bcode: CompactDeployedBytecode) -> DeployedBytecode { + DeployedBytecode { + bytecode: bcode.bytecode.map(|d_bcode| d_bcode.into()), + immutable_references: bcode.immutable_references, + } + } +} + +#[cfg(test)] +mod tests { + use crate::{artifacts::ContractBytecode, ConfigurableContractArtifact}; + + #[test] + fn test_empty_bytecode() { + let empty = r#" + { + "abi": [], + "bytecode": { + "object": "0x", + "linkReferences": {} + }, + "deployedBytecode": { + "object": "0x", + "linkReferences": {} + } + } + "#; + + let artifact: ConfigurableContractArtifact = serde_json::from_str(empty).unwrap(); + let contract = artifact.into_contract_bytecode(); + let bytecode: ContractBytecode = contract.into(); + let bytecode = bytecode.unwrap(); + assert!(!bytecode.bytecode.object.is_unlinked()); + } +} diff --git a/src/artifacts/contract.rs b/src/artifacts/contract.rs new file mode 100644 index 00000000..63acbc47 --- /dev/null +++ b/src/artifacts/contract.rs @@ -0,0 +1,529 @@ +//! Contract related types + +use crate::artifacts::{ + bytecode::{ + Bytecode, BytecodeObject, CompactBytecode, CompactDeployedBytecode, DeployedBytecode, + }, + serde_helpers, DevDoc, Evm, Ewasm, LosslessAbi, LosslessMetadata, Offsets, StorageLayout, + UserDoc, +}; +use ethers_core::{abi::Contract as Abi, types::Bytes}; +use serde::{Deserialize, Serialize}; +use std::{borrow::Cow, collections::BTreeMap, convert::TryFrom}; + +/// Represents a compiled solidity contract +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] +#[serde(rename_all = "camelCase")] +pub struct Contract { + /// The Ethereum Contract Metadata. + /// See + pub abi: Option, + #[serde( + default, + skip_serializing_if = "Option::is_none", + with = "serde_helpers::json_string_opt" + )] + pub metadata: Option, + #[serde(default)] + pub userdoc: UserDoc, + #[serde(default)] + pub devdoc: DevDoc, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub ir: Option, + #[serde(default, skip_serializing_if = "StorageLayout::is_empty")] + pub storage_layout: StorageLayout, + /// EVM-related outputs + #[serde(default, skip_serializing_if = "Option::is_none")] + pub evm: Option, + /// Ewasm related outputs + #[serde(default, skip_serializing_if = "Option::is_none")] + pub ewasm: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub ir_optimized: Option, +} + +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.deployed_bytecode.clone().map(Into::into).map(Cow::Owned), + ) + } else { + (None, None) + }; + CompactContractBytecodeCow { + abi: artifact.abi.as_ref().map(|abi| Cow::Borrowed(&abi.abi)), + bytecode, + deployed_bytecode, + } + } +} + +/// Minimal representation of a contract with a present abi and bytecode. +/// +/// Unlike `CompactContractSome` which contains the `BytecodeObject`, this holds the whole +/// `Bytecode` object. +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] +pub struct ContractBytecode { + /// The Ethereum Contract ABI. If empty, it is represented as an empty + /// array. See + pub abi: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub bytecode: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub deployed_bytecode: Option, +} + +impl ContractBytecode { + /// Returns the `ContractBytecodeSome` if all fields are `Some` + /// + /// # Panics + /// + /// Panics if any of the fields equal `None` + /// + /// # Example + /// + /// ``` + /// use foundry_compilers::Project; + /// use foundry_compilers::artifacts::*; + /// # fn demo(project: Project) { + /// let mut output = project.compile().unwrap().output(); + /// let contract: ContractBytecode = output.remove_first("Greeter").unwrap().into(); + /// let contract = contract.unwrap(); + /// # } + /// ``` + pub fn unwrap(self) -> ContractBytecodeSome { + ContractBytecodeSome { + abi: self.abi.unwrap(), + bytecode: self.bytecode.unwrap(), + deployed_bytecode: self.deployed_bytecode.unwrap(), + } + } + + /// Looks for all link references in deployment and runtime bytecodes + pub fn all_link_references(&self) -> BTreeMap>> { + let mut links = BTreeMap::new(); + if let Some(bcode) = &self.bytecode { + links.extend(bcode.link_references.clone()); + } + + if let Some(d_bcode) = &self.deployed_bytecode { + if let Some(bcode) = &d_bcode.bytecode { + links.extend(bcode.link_references.clone()); + } + } + links + } +} + +impl From for ContractBytecode { + fn from(c: Contract) -> Self { + let (bytecode, deployed_bytecode) = if let Some(evm) = c.evm { + (evm.bytecode, evm.deployed_bytecode) + } else { + (None, None) + }; + + Self { abi: c.abi.map(Into::into), bytecode, deployed_bytecode } + } +} + +/// Minimal representation of a contract with a present abi and bytecode. +/// +/// Unlike `CompactContractSome` which contains the `BytecodeObject`, this holds the whole +/// `Bytecode` object. +#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq)] +#[serde(rename_all = "camelCase")] +pub struct CompactContractBytecode { + /// The Ethereum Contract ABI. If empty, it is represented as an empty + /// array. See + pub abi: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub bytecode: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub deployed_bytecode: Option, +} + +impl CompactContractBytecode { + /// Looks for all link references in deployment and runtime bytecodes + pub fn all_link_references(&self) -> BTreeMap>> { + let mut links = BTreeMap::new(); + if let Some(bcode) = &self.bytecode { + links.extend(bcode.link_references.clone()); + } + + if let Some(d_bcode) = &self.deployed_bytecode { + if let Some(bcode) = &d_bcode.bytecode { + links.extend(bcode.link_references.clone()); + } + } + links + } +} + +impl<'a> From<&'a CompactContractBytecode> for CompactContractBytecodeCow<'a> { + fn from(artifact: &'a CompactContractBytecode) -> Self { + CompactContractBytecodeCow { + abi: artifact.abi.as_ref().map(Cow::Borrowed), + bytecode: artifact.bytecode.as_ref().map(Cow::Borrowed), + deployed_bytecode: artifact.deployed_bytecode.as_ref().map(Cow::Borrowed), + } + } +} + +impl From for CompactContractBytecode { + fn from(c: Contract) -> Self { + let (bytecode, deployed_bytecode) = if let Some(evm) = c.evm { + let evm = evm.into_compact(); + (evm.bytecode, evm.deployed_bytecode) + } else { + (None, None) + }; + + Self { abi: c.abi.map(Into::into), bytecode, deployed_bytecode } + } +} + +impl From for CompactContractBytecode { + fn from(c: ContractBytecode) -> Self { + let (maybe_bcode, maybe_runtime) = match (c.bytecode, c.deployed_bytecode) { + (Some(bcode), Some(dbcode)) => (Some(bcode.into()), Some(dbcode.into())), + (None, Some(dbcode)) => (None, Some(dbcode.into())), + (Some(bcode), None) => (Some(bcode.into()), None), + (None, None) => (None, None), + }; + Self { abi: c.abi, bytecode: maybe_bcode, deployed_bytecode: maybe_runtime } + } +} + +impl From for ContractBytecode { + fn from(c: CompactContractBytecode) -> Self { + let (maybe_bcode, maybe_runtime) = match (c.bytecode, c.deployed_bytecode) { + (Some(bcode), Some(dbcode)) => (Some(bcode.into()), Some(dbcode.into())), + (None, Some(dbcode)) => (None, Some(dbcode.into())), + (Some(bcode), None) => (Some(bcode.into()), None), + (None, None) => (None, None), + }; + Self { abi: c.abi, bytecode: maybe_bcode, deployed_bytecode: maybe_runtime } + } +} + +/// A [CompactContractBytecode] that is either owns or borrows its content +#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq)] +#[serde(rename_all = "camelCase")] +pub struct CompactContractBytecodeCow<'a> { + pub abi: Option>, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub bytecode: Option>, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub deployed_bytecode: Option>, +} + +/// Minimal representation of a contract with a present abi and bytecode. +/// +/// Unlike `CompactContractSome` which contains the `BytecodeObject`, this holds the whole +/// `Bytecode` object. +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] +pub struct ContractBytecodeSome { + pub abi: Abi, + pub bytecode: Bytecode, + pub deployed_bytecode: DeployedBytecode, +} + +impl TryFrom for ContractBytecodeSome { + type Error = ContractBytecode; + + fn try_from(value: ContractBytecode) -> Result { + if value.abi.is_none() || value.bytecode.is_none() || value.deployed_bytecode.is_none() { + return Err(value) + } + Ok(value.unwrap()) + } +} + +/// Minimal representation of a contract's artifact with a present abi and bytecode. +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Default)] +pub struct CompactContractSome { + /// The Ethereum Contract ABI. If empty, it is represented as an empty + /// array. See + pub abi: Abi, + pub bin: BytecodeObject, + #[serde(rename = "bin-runtime")] + pub bin_runtime: BytecodeObject, +} + +impl TryFrom for CompactContractSome { + type Error = CompactContract; + + fn try_from(value: CompactContract) -> Result { + if value.abi.is_none() || value.bin.is_none() || value.bin_runtime.is_none() { + return Err(value) + } + Ok(value.unwrap()) + } +} + +/// The general purpose minimal representation of a contract's abi with bytecode +/// Unlike `CompactContractSome` all fields are optional so that every possible compiler output can +/// be represented by it +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Default)] +pub struct CompactContract { + /// The Ethereum Contract ABI. If empty, it is represented as an empty + /// array. See + pub abi: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub bin: Option, + #[serde(default, rename = "bin-runtime", skip_serializing_if = "Option::is_none")] + pub bin_runtime: Option, +} + +impl CompactContract { + /// Returns the contents of this type as a single tuple of abi, bytecode and deployed bytecode + pub fn into_parts(self) -> (Option, Option, Option) { + ( + self.abi, + self.bin.and_then(|bin| bin.into_bytes()), + self.bin_runtime.and_then(|bin| bin.into_bytes()), + ) + } + + /// Returns the individual parts of this contract. + /// + /// If the values are `None`, then `Default` is returned. + pub fn into_parts_or_default(self) -> (Abi, Bytes, Bytes) { + ( + self.abi.unwrap_or_default(), + self.bin.and_then(|bin| bin.into_bytes()).unwrap_or_default(), + self.bin_runtime.and_then(|bin| bin.into_bytes()).unwrap_or_default(), + ) + } + + /// Returns the `CompactContractSome` if all fields are `Some` + /// + /// # Panics + /// + /// Panics if any of the fields euqal `None` + /// + /// # Example + /// + /// ``` + /// use foundry_compilers::Project; + /// use foundry_compilers::artifacts::*; + /// # fn demo(project: Project) { + /// let mut output = project.compile().unwrap().output(); + /// let contract: CompactContract = output.remove_first("Greeter").unwrap().into(); + /// let contract = contract.unwrap(); + /// # } + /// ``` + pub fn unwrap(self) -> CompactContractSome { + CompactContractSome { + abi: self.abi.unwrap(), + bin: self.bin.unwrap(), + bin_runtime: self.bin_runtime.unwrap(), + } + } + + /// Returns the `CompactContractSome` if any if the field equals `None` the `Default` value is + /// returned + /// + /// Unlike `unwrap`, this function does _not_ panic + pub fn unwrap_or_default(self) -> CompactContractSome { + CompactContractSome { + abi: self.abi.unwrap_or_default(), + bin: self.bin.unwrap_or_default(), + bin_runtime: self.bin_runtime.unwrap_or_default(), + } + } +} + +impl From for CompactContract { + fn from(mut val: serde_json::Value) -> Self { + if let Some(map) = val.as_object_mut() { + let abi = map.remove("abi").and_then(|val| serde_json::from_value(val).ok()); + let bin = map.remove("bin").and_then(|val| serde_json::from_value(val).ok()); + let bin_runtime = + map.remove("bin-runtime").and_then(|val| serde_json::from_value(val).ok()); + Self { abi, bin, bin_runtime } + } else { + CompactContract::default() + } + } +} + +impl<'a> From<&'a serde_json::Value> for CompactContractBytecodeCow<'a> { + fn from(artifact: &'a serde_json::Value) -> Self { + let c = CompactContractBytecode::from(artifact.clone()); + CompactContractBytecodeCow { + abi: c.abi.map(Cow::Owned), + bytecode: c.bytecode.map(Cow::Owned), + deployed_bytecode: c.deployed_bytecode.map(Cow::Owned), + } + } +} + +impl From for CompactContractBytecode { + fn from(val: serde_json::Value) -> Self { + serde_json::from_value(val).unwrap_or_default() + } +} + +impl From for CompactContract { + fn from(c: ContractBytecode) -> Self { + let ContractBytecode { abi, bytecode, deployed_bytecode } = c; + Self { + abi, + bin: bytecode.map(|c| c.object), + bin_runtime: deployed_bytecode + .and_then(|deployed| deployed.bytecode.map(|code| code.object)), + } + } +} + +impl From for CompactContract { + fn from(c: CompactContractBytecode) -> Self { + let c: ContractBytecode = c.into(); + c.into() + } +} + +impl From for CompactContract { + fn from(c: ContractBytecodeSome) -> Self { + Self { + abi: Some(c.abi), + bin: Some(c.bytecode.object), + bin_runtime: c.deployed_bytecode.bytecode.map(|code| code.object), + } + } +} + +impl From for CompactContract { + fn from(c: Contract) -> Self { + ContractBytecode::from(c).into() + } +} + +impl From for CompactContract { + fn from(c: CompactContractSome) -> Self { + Self { abi: Some(c.abi), bin: Some(c.bin), bin_runtime: Some(c.bin_runtime) } + } +} + +impl<'a> From> for CompactContract { + fn from(c: CompactContractRef<'a>) -> Self { + Self { abi: c.abi.cloned(), bin: c.bin.cloned(), bin_runtime: c.bin_runtime.cloned() } + } +} + +impl<'a> From> for CompactContract { + fn from(c: CompactContractRefSome<'a>) -> Self { + Self { + abi: Some(c.abi.clone()), + bin: Some(c.bin.clone()), + bin_runtime: Some(c.bin_runtime.clone()), + } + } +} + +/// Minimal representation of a contract with a present abi and bytecode that borrows. +#[derive(Copy, Clone, Debug, Serialize)] +pub struct CompactContractRefSome<'a> { + pub abi: &'a Abi, + pub bin: &'a BytecodeObject, + #[serde(rename = "bin-runtime")] + pub bin_runtime: &'a BytecodeObject, +} + +impl<'a> CompactContractRefSome<'a> { + /// Returns the individual parts of this contract. + /// + /// If the values are `None`, then `Default` is returned. + pub fn into_parts(self) -> (Abi, Bytes, Bytes) { + CompactContract::from(self).into_parts_or_default() + } +} + +impl<'a> TryFrom> for CompactContractRefSome<'a> { + type Error = CompactContractRef<'a>; + + fn try_from(value: CompactContractRef<'a>) -> Result { + if value.abi.is_none() || value.bin.is_none() || value.bin_runtime.is_none() { + return Err(value) + } + Ok(value.unwrap()) + } +} + +/// Helper type to serialize while borrowing from `Contract` +#[derive(Copy, Clone, Debug, Serialize)] +pub struct CompactContractRef<'a> { + pub abi: Option<&'a Abi>, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub bin: Option<&'a BytecodeObject>, + #[serde(default, rename = "bin-runtime", skip_serializing_if = "Option::is_none")] + pub bin_runtime: Option<&'a BytecodeObject>, +} + +impl<'a> CompactContractRef<'a> { + /// Clones the referenced values and returns as tuples + pub fn into_parts(self) -> (Option, Option, Option) { + CompactContract::from(self).into_parts() + } + + /// Returns the individual parts of this contract. + /// + /// If the values are `None`, then `Default` is returned. + pub fn into_parts_or_default(self) -> (Abi, Bytes, Bytes) { + CompactContract::from(self).into_parts_or_default() + } + + pub fn bytecode(&self) -> Option<&Bytes> { + self.bin.as_ref().and_then(|bin| bin.as_bytes()) + } + + pub fn runtime_bytecode(&self) -> Option<&Bytes> { + self.bin_runtime.as_ref().and_then(|bin| bin.as_bytes()) + } + + /// Returns the `CompactContractRefSome` if all fields are `Some` + /// + /// # Panics + /// + /// Panics if any of the fields equal `None` + /// + /// # Example + /// + /// ``` + /// use foundry_compilers::Project; + /// use foundry_compilers::artifacts::*; + /// # fn demo(project: Project) { + /// let output = project.compile().unwrap().output(); + /// let contract = output.find_first("Greeter").unwrap(); + /// let contract = contract.unwrap(); + /// # } + /// ``` + pub fn unwrap(self) -> CompactContractRefSome<'a> { + CompactContractRefSome { + abi: self.abi.unwrap(), + bin: self.bin.unwrap(), + bin_runtime: self.bin_runtime.unwrap(), + } + } +} + +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.deployed_bytecode + .as_ref() + .and_then(|deployed| deployed.bytecode.as_ref().map(|evm| &evm.object)), + ) + } else { + (None, None) + }; + + Self { abi: c.abi.as_ref().map(|abi| &abi.abi), bin, bin_runtime } + } +} diff --git a/src/artifacts/mod.rs b/src/artifacts/mod.rs new file mode 100644 index 00000000..fd8f5902 --- /dev/null +++ b/src/artifacts/mod.rs @@ -0,0 +1,2595 @@ +//! Solc artifact types +use crate::{ + compile::*, error::SolcIoError, remappings::Remapping, utils, ProjectPathsConfig, SolcError, +}; +use ethers_core::abi::Abi; +use md5::Digest; +use semver::Version; +use serde::{de::Visitor, Deserialize, Deserializer, Serialize, Serializer}; +use std::{ + collections::{BTreeMap, HashSet}, + fmt, fs, + ops::Range, + path::{Path, PathBuf}, + str::FromStr, + sync::Arc, +}; +use tracing::warn; +use yansi::{Color, Paint, Style}; + +pub mod ast; +pub use ast::*; +pub mod bytecode; +pub mod contract; +pub mod output_selection; +pub mod serde_helpers; +use crate::{ + artifacts::output_selection::{ContractOutputSelection, OutputSelection}, + filter::FilteredSources, +}; +pub use bytecode::*; +pub use contract::*; +pub use serde_helpers::{deserialize_bytes, deserialize_opt_bytes}; + +/// Solidity files are made up of multiple `source units`, a solidity contract is such a `source +/// unit`, therefore a solidity file can contain multiple contracts: (1-N*) relationship. +/// +/// This types represents this mapping as `file name -> (contract name -> T)`, where the generic is +/// intended to represent contract specific information, like [`Contract`] itself, See [`Contracts`] +pub type FileToContractsMap = BTreeMap>; + +/// file -> (contract name -> Contract) +pub type Contracts = FileToContractsMap; + +/// An ordered list of files and their source +pub type Sources = BTreeMap; + +/// A set of different Solc installations with their version and the sources to be compiled +pub(crate) type VersionedSources = BTreeMap; + +/// A set of different Solc installations with their version and the sources to be compiled +pub(crate) type VersionedFilteredSources = BTreeMap; + +const SOLIDITY: &str = "Solidity"; +const YUL: &str = "Yul"; + +/// Input type `solc` expects +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct CompilerInput { + pub language: String, + pub sources: Sources, + pub settings: Settings, +} + +impl CompilerInput { + /// Reads all contracts found under the path + pub fn new(path: impl AsRef) -> Result, SolcIoError> { + Source::read_all_from(path.as_ref()).map(Self::with_sources) + } + + /// Creates a new [CompilerInput]s with default settings and the given sources + /// + /// A [CompilerInput] expects a language setting, supported by solc are solidity or yul. + /// In case the `sources` is a mix of solidity and yul files, 2 CompilerInputs are returned + pub fn with_sources(sources: Sources) -> Vec { + let mut solidity_sources = BTreeMap::new(); + let mut yul_sources = BTreeMap::new(); + for (path, source) in sources { + if path.extension() == Some(std::ffi::OsStr::new("yul")) { + yul_sources.insert(path, source); + } else { + solidity_sources.insert(path, source); + } + } + let mut res = Vec::new(); + if !solidity_sources.is_empty() { + res.push(Self { + language: SOLIDITY.to_string(), + sources: solidity_sources, + settings: Default::default(), + }); + } + if !yul_sources.is_empty() { + res.push(Self { + language: YUL.to_string(), + sources: yul_sources, + settings: Default::default(), + }); + } + res + } + + /// This will remove/adjust values in the `CompilerInput` that are not compatible with this + /// version + pub fn sanitize(&mut self, version: &Version) { + self.settings.sanitize(version) + } + + /// Consumes the type and returns a [CompilerInput::sanitized] version + pub fn sanitized(mut self, version: &Version) -> Self { + self.settings.sanitize(version); + self + } + + /// Sets the settings for compilation + #[must_use] + pub fn settings(mut self, mut settings: Settings) -> Self { + if self.is_yul() { + if !settings.remappings.is_empty() { + warn!("omitting remappings supplied for the yul sources"); + settings.remappings = vec![]; + } + if let Some(debug) = settings.debug.as_mut() { + if debug.revert_strings.is_some() { + warn!("omitting revertStrings supplied for the yul sources"); + debug.revert_strings = None; + } + } + } + self.settings = settings; + self + } + + /// Sets the EVM version for compilation + #[must_use] + pub fn evm_version(mut self, version: EvmVersion) -> Self { + self.settings.evm_version = Some(version); + self + } + + /// Sets the optimizer runs (default = 200) + #[must_use] + pub fn optimizer(mut self, runs: usize) -> Self { + self.settings.optimizer.runs(runs); + self + } + + /// Normalizes the EVM version used in the settings to be up to the latest one + /// supported by the provided compiler version. + #[must_use] + pub fn normalize_evm_version(mut self, version: &Version) -> Self { + if let Some(evm_version) = &mut self.settings.evm_version { + self.settings.evm_version = evm_version.normalize_version(version); + } + self + } + + #[must_use] + pub fn with_remappings(mut self, remappings: Vec) -> Self { + if self.is_yul() { + warn!("omitting remappings supplied for the yul sources"); + } else { + self.settings.remappings = remappings; + } + self + } + + /// Sets the path of the source files to `root` adjoined to the existing path + #[must_use] + pub fn join_path(mut self, root: impl AsRef) -> Self { + let root = root.as_ref(); + self.sources = self.sources.into_iter().map(|(path, s)| (root.join(path), s)).collect(); + self + } + + /// Removes the `base` path from all source files + pub fn strip_prefix(mut self, base: impl AsRef) -> Self { + let base = base.as_ref(); + self.sources = self + .sources + .into_iter() + .map(|(path, s)| (path.strip_prefix(base).map(Into::into).unwrap_or(path), s)) + .collect(); + self + } + + /// Similar to `Self::strip_prefix()`. Remove a base path from all + /// sources _and_ all paths in solc settings such as remappings + /// + /// See also `solc --base-path` + pub fn with_base_path(mut self, base: impl AsRef) -> Self { + let base = base.as_ref(); + self.settings = self.settings.with_base_path(base); + self.strip_prefix(base) + } + + /// The flag indicating whether the current [CompilerInput] is + /// constructed for the yul sources + pub fn is_yul(&self) -> bool { + self.language == YUL + } +} + +/// A `CompilerInput` representation used for verify +/// +/// This type is an alternative `CompilerInput` but uses non-alphabetic ordering of the `sources` +/// and instead emits the (Path -> Source) path in the same order as the pairs in the `sources` +/// `Vec`. This is used over a map, so we can determine the order in which etherscan will display +/// the verified contracts +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct StandardJsonCompilerInput { + pub language: String, + #[serde(with = "serde_helpers::tuple_vec_map")] + pub sources: Vec<(PathBuf, Source)>, + pub settings: Settings, +} + +// === impl StandardJsonCompilerInput === + +impl StandardJsonCompilerInput { + pub fn new(sources: Vec<(PathBuf, Source)>, settings: Settings) -> Self { + Self { language: SOLIDITY.to_string(), sources, settings } + } + + /// Normalizes the EVM version used in the settings to be up to the latest one + /// supported by the provided compiler version. + #[must_use] + pub fn normalize_evm_version(mut self, version: &Version) -> Self { + if let Some(evm_version) = &mut self.settings.evm_version { + self.settings.evm_version = evm_version.normalize_version(version); + } + self + } +} + +impl From for CompilerInput { + fn from(input: StandardJsonCompilerInput) -> Self { + let StandardJsonCompilerInput { language, sources, settings } = input; + CompilerInput { language, sources: sources.into_iter().collect(), settings } + } +} + +impl From for StandardJsonCompilerInput { + fn from(input: CompilerInput) -> Self { + let CompilerInput { language, sources, settings } = input; + StandardJsonCompilerInput { language, sources: sources.into_iter().collect(), settings } + } +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct Settings { + /// Stop compilation after the given stage. + /// since 0.8.11: only "parsing" is valid here + #[serde(default, skip_serializing_if = "Option::is_none")] + pub stop_after: Option, + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub remappings: Vec, + pub optimizer: Optimizer, + /// Model Checker options. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub model_checker: Option, + /// Metadata settings + #[serde(default, skip_serializing_if = "Option::is_none")] + pub metadata: Option, + /// This field can be used to select desired outputs based + /// on file and contract names. + /// If this field is omitted, then the compiler loads and does type + /// checking, but will not generate any outputs apart from errors. + #[serde(default)] + pub output_selection: OutputSelection, + #[serde( + default, + with = "serde_helpers::display_from_str_opt", + skip_serializing_if = "Option::is_none" + )] + pub evm_version: Option, + /// Change compilation pipeline to go through the Yul intermediate representation. This is + /// false by default. + #[serde(rename = "viaIR", default, skip_serializing_if = "Option::is_none")] + pub via_ir: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub debug: Option, + /// Addresses of the libraries. If not all libraries are given here, + /// it can result in unlinked objects whose output data is different. + /// + /// The top level key is the name of the source file where the library is used. + /// If remappings are used, this source file should match the global path + /// after remappings were applied. + /// If this key is an empty string, that refers to a global level. + #[serde(default)] + pub libraries: Libraries, +} + +impl Settings { + /// Creates a new `Settings` instance with the given `output_selection` + pub fn new(output_selection: impl Into) -> Self { + Self { output_selection: output_selection.into(), ..Default::default() } + } + + /// Consumes the type and returns a [Settings::sanitize] version + pub fn sanitized(mut self, version: &Version) -> Self { + self.sanitize(version); + self + } + + /// This will remove/adjust values in the settings that are not compatible with this version. + pub fn sanitize(&mut self, version: &Version) { + const V0_6_0: Version = Version::new(0, 6, 0); + if *version < V0_6_0 { + if let Some(meta) = &mut self.metadata { + // introduced in + // missing in + meta.bytecode_hash = None; + } + // introduced in + self.debug = None; + } + + const V0_7_5: Version = Version::new(0, 7, 5); + if *version < V0_7_5 { + // introduced in 0.7.5 + self.via_ir = None; + } + + const V0_8_7: Version = Version::new(0, 8, 7); + if *version < V0_8_7 { + // lower the disable version from 0.8.10 to 0.8.7, due to `divModNoSlacks`, + // `showUnproved` and `solvers` are implemented + // introduced in + self.model_checker = None; + } + + const V0_8_10: Version = Version::new(0, 8, 10); + if *version < V0_8_10 { + if let Some(debug) = &mut self.debug { + // introduced in + // + debug.debug_info.clear(); + } + + if let Some(model_checker) = &mut self.model_checker { + // introduced in + model_checker.invariants = None; + } + } + + const V0_8_18: Version = Version::new(0, 8, 18); + if *version < V0_8_18 { + // introduced in 0.8.18 + if let Some(meta) = &mut self.metadata { + meta.cbor_metadata = None; + } + + if let Some(model_checker) = &mut self.model_checker { + if let Some(solvers) = &mut model_checker.solvers { + // elf solver introduced in 0.8.18 + solvers.retain(|solver| *solver != ModelCheckerSolver::Eld); + } + } + } + + if *version < SHANGHAI_SOLC { + // introduced in 0.8.20 + if let Some(model_checker) = &mut self.model_checker { + model_checker.show_proved_safe = None; + model_checker.show_unsupported = None; + } + } + } + + /// Inserts a set of `ContractOutputSelection` + pub fn push_all(&mut self, settings: impl IntoIterator) { + for value in settings { + self.push_output_selection(value) + } + } + + /// Inserts a set of `ContractOutputSelection` + #[must_use] + pub fn with_extra_output( + mut self, + settings: impl IntoIterator, + ) -> Self { + for value in settings { + self.push_output_selection(value) + } + self + } + + /// Inserts the value for all files and contracts + /// + /// ``` + /// use foundry_compilers::artifacts::output_selection::ContractOutputSelection; + /// use foundry_compilers::artifacts::Settings; + /// let mut selection = Settings::default(); + /// selection.push_output_selection(ContractOutputSelection::Metadata); + /// ``` + pub fn push_output_selection(&mut self, value: impl ToString) { + self.push_contract_output_selection("*", value) + } + + /// Inserts the `key` `value` pair to the `output_selection` for all files + /// + /// If the `key` already exists, then the value is added to the existing list + pub fn push_contract_output_selection( + &mut self, + contracts: impl Into, + value: impl ToString, + ) { + let value = value.to_string(); + let values = self + .output_selection + .as_mut() + .entry("*".to_string()) + .or_default() + .entry(contracts.into()) + .or_default(); + if !values.contains(&value) { + values.push(value) + } + } + + /// Sets the value for all files and contracts + pub fn set_output_selection(&mut self, values: impl IntoIterator) { + self.set_contract_output_selection("*", values) + } + + /// Sets the `key` to the `values` pair to the `output_selection` for all files + /// + /// This will replace the existing values for `key` if they're present + pub fn set_contract_output_selection( + &mut self, + key: impl Into, + values: impl IntoIterator, + ) { + self.output_selection + .as_mut() + .entry("*".to_string()) + .or_default() + .insert(key.into(), values.into_iter().map(|s| s.to_string()).collect()); + } + + /// Sets the ``viaIR` valu + #[must_use] + pub fn set_via_ir(mut self, via_ir: bool) -> Self { + self.via_ir = Some(via_ir); + self + } + + /// Enables `viaIR` + #[must_use] + pub fn with_via_ir(self) -> Self { + self.set_via_ir(true) + } + + /// Enable `viaIR` and use the minimum optimization settings + /// + /// This is useful in the following scenarios: + /// - When compiling for test coverage, this can resolve the "stack too deep" error while still + /// giving a relatively accurate source mapping + /// - When compiling for test, this can reduce the compilation time + pub fn with_via_ir_minimum_optimization(mut self) -> Self { + // https://github.com/foundry-rs/foundry/pull/5349 + // https://github.com/ethereum/solidity/issues/12533#issuecomment-1013073350 + self.via_ir = Some(true); + self.optimizer.details = Some(OptimizerDetails { + peephole: Some(false), + inliner: Some(false), + jumpdest_remover: Some(false), + order_literals: Some(false), + deduplicate: Some(false), + cse: Some(false), + constant_optimizer: Some(false), + yul: Some(true), // enable yul optimizer + yul_details: Some(YulDetails { + stack_allocation: Some(true), + // with only unused prunner step + optimizer_steps: Some("u".to_string()), + }), + }); + self + } + + /// Adds `ast` to output + #[must_use] + pub fn with_ast(mut self) -> Self { + let output = + self.output_selection.as_mut().entry("*".to_string()).or_insert_with(BTreeMap::default); + output.insert("".to_string(), vec!["ast".to_string()]); + self + } + + /// Strips `base` from all paths + pub fn with_base_path(mut self, base: impl AsRef) -> Self { + let base = base.as_ref(); + self.remappings.iter_mut().for_each(|r| { + r.strip_prefix(base); + }); + + self.libraries.libs = self + .libraries + .libs + .into_iter() + .map(|(file, libs)| (file.strip_prefix(base).map(Into::into).unwrap_or(file), libs)) + .collect(); + + self.output_selection = OutputSelection( + self.output_selection + .0 + .into_iter() + .map(|(file, selection)| { + ( + Path::new(&file) + .strip_prefix(base) + .map(|p| format!("{}", p.display())) + .unwrap_or(file), + selection, + ) + }) + .collect(), + ); + + if let Some(mut model_checker) = self.model_checker.take() { + model_checker.contracts = model_checker + .contracts + .into_iter() + .map(|(path, contracts)| { + ( + Path::new(&path) + .strip_prefix(base) + .map(|p| format!("{}", p.display())) + .unwrap_or(path), + contracts, + ) + }) + .collect(); + self.model_checker = Some(model_checker); + } + + self + } +} + +impl Default for Settings { + fn default() -> Self { + Self { + stop_after: None, + optimizer: Default::default(), + metadata: None, + output_selection: OutputSelection::default_output_selection(), + evm_version: Some(EvmVersion::default()), + via_ir: None, + debug: None, + libraries: Default::default(), + remappings: Default::default(), + model_checker: None, + } + .with_ast() + } +} + +/// A wrapper type for all libraries in the form of `::` +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)] +#[serde(transparent)] +pub struct Libraries { + /// All libraries, `(file path -> (Lib name -> Address)) + pub libs: BTreeMap>, +} + +// === impl Libraries === + +impl Libraries { + /// Parses all libraries in the form of + /// `::` + /// + /// # Example + /// + /// ``` + /// use foundry_compilers::artifacts::Libraries; + /// let libs = Libraries::parse(&[ + /// "src/DssSpell.sol:DssExecLib:0xfD88CeE74f7D78697775aBDAE53f9Da1559728E4".to_string(), + /// ]) + /// .unwrap(); + /// ``` + pub fn parse(libs: &[String]) -> Result { + let mut libraries = BTreeMap::default(); + for lib in libs { + let mut items = lib.split(':'); + let file = items.next().ok_or_else(|| { + SolcError::msg(format!("failed to parse path to library file: {lib}")) + })?; + let lib = items + .next() + .ok_or_else(|| SolcError::msg(format!("failed to parse library name: {lib}")))?; + let addr = items + .next() + .ok_or_else(|| SolcError::msg(format!("failed to parse library address: {lib}")))?; + if items.next().is_some() { + return Err(SolcError::msg(format!( + "failed to parse, too many arguments passed: {lib}" + ))) + } + libraries + .entry(file.into()) + .or_insert_with(BTreeMap::default) + .insert(lib.to_string(), addr.to_string()); + } + Ok(Self { libs: libraries }) + } + + pub fn is_empty(&self) -> bool { + self.libs.is_empty() + } + + pub fn len(&self) -> usize { + self.libs.len() + } + + /// Solc expects the lib paths to match the global path after remappings were applied + /// + /// See also [ProjectPathsConfig::resolve_import] + pub fn with_applied_remappings(mut self, config: &ProjectPathsConfig) -> Self { + self.libs = self + .libs + .into_iter() + .map(|(file, target)| { + let file = config.resolve_import(&config.root, &file).unwrap_or_else(|err| { + warn!(target: "libs", "Failed to resolve library `{}` for linking: {:?}", file.display(), err); + file + }); + (file, target) + }) + .collect(); + self + } +} + +impl From>> for Libraries { + fn from(libs: BTreeMap>) -> Self { + Self { libs } + } +} + +impl AsRef>> for Libraries { + fn as_ref(&self) -> &BTreeMap> { + &self.libs + } +} + +impl AsMut>> for Libraries { + fn as_mut(&mut self) -> &mut BTreeMap> { + &mut self.libs + } +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct Optimizer { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub enabled: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub runs: Option, + /// Switch optimizer components on or off in detail. + /// The "enabled" switch above provides two defaults which can be + /// tweaked here. If "details" is given, "enabled" can be omitted. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub details: Option, +} + +impl Optimizer { + pub fn runs(&mut self, runs: usize) { + self.runs = Some(runs); + } + + pub fn disable(&mut self) { + self.enabled.take(); + } + + pub fn enable(&mut self) { + self.enabled = Some(true) + } +} + +impl Default for Optimizer { + fn default() -> Self { + Self { enabled: Some(false), runs: Some(200), details: None } + } +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, Default)] +#[serde(rename_all = "camelCase")] +pub struct OptimizerDetails { + /// The peephole optimizer is always on if no details are given, + /// use details to switch it off. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub peephole: Option, + /// The inliner is always on if no details are given, + /// use details to switch it off. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub inliner: Option, + /// The unused jumpdest remover is always on if no details are given, + /// use details to switch it off. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub jumpdest_remover: Option, + /// Sometimes re-orders literals in commutative operations. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub order_literals: Option, + /// Removes duplicate code blocks + #[serde(default, skip_serializing_if = "Option::is_none")] + pub deduplicate: Option, + /// Common subexpression elimination, this is the most complicated step but + /// can also provide the largest gain. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub cse: Option, + /// Optimize representation of literal numbers and strings in code. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub constant_optimizer: Option, + /// The new Yul optimizer. Mostly operates on the code of ABI coder v2 + /// and inline assembly. + /// It is activated together with the global optimizer setting + /// and can be deactivated here. + /// Before Solidity 0.6.0 it had to be activated through this switch. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub yul: Option, + /// Tuning options for the Yul optimizer. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub yul_details: Option, +} + +// === impl OptimizerDetails === + +impl OptimizerDetails { + /// Returns true if no settings are set. + pub fn is_empty(&self) -> bool { + self.peephole.is_none() && + self.inliner.is_none() && + self.jumpdest_remover.is_none() && + self.order_literals.is_none() && + self.deduplicate.is_none() && + self.cse.is_none() && + self.constant_optimizer.is_none() && + self.yul.is_none() && + self.yul_details.as_ref().map(|yul| yul.is_empty()).unwrap_or(true) + } +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, Default)] +#[serde(rename_all = "camelCase")] +pub struct YulDetails { + /// Improve allocation of stack slots for variables, can free up stack slots early. + /// Activated by default if the Yul optimizer is activated. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub stack_allocation: Option, + /// Select optimization steps to be applied. + /// Optional, the optimizer will use the default sequence if omitted. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub optimizer_steps: Option, +} + +// === impl YulDetails === + +impl YulDetails { + /// Returns true if no settings are set. + pub fn is_empty(&self) -> bool { + self.stack_allocation.is_none() && self.optimizer_steps.is_none() + } +} + +/// EVM versions. +/// +/// Kept in sync with: +#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] +pub enum EvmVersion { + Homestead, + TangerineWhistle, + SpuriousDragon, + Byzantium, + Constantinople, + Petersburg, + Istanbul, + Berlin, + London, + Paris, + #[default] + Shanghai, +} + +impl EvmVersion { + /// Normalizes this EVM version by checking against the given Solc [`Version`]. + pub fn normalize_version(self, version: &Version) -> Option { + // The EVM version flag was only added in 0.4.21; we work our way backwards + if *version >= BYZANTIUM_SOLC { + // If the Solc version is at least at Shanghai, it supports all EVM versions. + // For all other cases, cap at the at-the-time highest possible fork. + let normalized = if *version >= SHANGHAI_SOLC { + self + } else if self >= Self::Paris && *version >= PARIS_SOLC { + Self::Paris + } else if self >= Self::London && *version >= LONDON_SOLC { + Self::London + } else if self >= Self::Berlin && *version >= BERLIN_SOLC { + Self::Berlin + } else if self >= Self::Istanbul && *version >= ISTANBUL_SOLC { + Self::Istanbul + } else if self >= Self::Petersburg && *version >= PETERSBURG_SOLC { + Self::Petersburg + } else if self >= Self::Constantinople && *version >= CONSTANTINOPLE_SOLC { + Self::Constantinople + } else if self >= Self::Byzantium { + Self::Byzantium + } else { + self + }; + Some(normalized) + } else { + None + } + } + + /// Returns the EVM version as a string. + pub const fn as_str(&self) -> &'static str { + match self { + Self::Homestead => "homestead", + Self::TangerineWhistle => "tangerineWhistle", + Self::SpuriousDragon => "spuriousDragon", + Self::Byzantium => "byzantium", + Self::Constantinople => "constantinople", + Self::Petersburg => "petersburg", + Self::Istanbul => "istanbul", + Self::Berlin => "berlin", + Self::London => "london", + Self::Paris => "paris", + Self::Shanghai => "shanghai", + } + } + + /// Has the `RETURNDATACOPY` and `RETURNDATASIZE` opcodes. + pub fn supports_returndata(&self) -> bool { + *self >= Self::Byzantium + } + + pub fn has_static_call(&self) -> bool { + *self >= Self::Byzantium + } + + pub fn has_bitwise_shifting(&self) -> bool { + *self >= Self::Constantinople + } + + pub fn has_create2(&self) -> bool { + *self >= Self::Constantinople + } + + pub fn has_ext_code_hash(&self) -> bool { + *self >= Self::Constantinople + } + + pub fn has_chain_id(&self) -> bool { + *self >= Self::Istanbul + } + + pub fn has_self_balance(&self) -> bool { + *self >= Self::Istanbul + } + + pub fn has_base_fee(&self) -> bool { + *self >= Self::London + } + + pub fn has_prevrandao(&self) -> bool { + *self >= Self::Paris + } + + pub fn has_push0(&self) -> bool { + *self >= Self::Shanghai + } +} + +impl fmt::Display for EvmVersion { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(self.as_str()) + } +} + +impl FromStr for EvmVersion { + type Err = String; + + fn from_str(s: &str) -> Result { + match s { + "homestead" => Ok(Self::Homestead), + "tangerineWhistle" => Ok(Self::TangerineWhistle), + "spuriousDragon" => Ok(Self::SpuriousDragon), + "byzantium" => Ok(Self::Byzantium), + "constantinople" => Ok(Self::Constantinople), + "petersburg" => Ok(Self::Petersburg), + "istanbul" => Ok(Self::Istanbul), + "berlin" => Ok(Self::Berlin), + "london" => Ok(Self::London), + "paris" => Ok(Self::Paris), + "shanghai" => Ok(Self::Shanghai), + s => Err(format!("Unknown evm version: {s}")), + } + } +} + +/// Debugging settings for solc +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct DebuggingSettings { + #[serde( + default, + with = "serde_helpers::display_from_str_opt", + skip_serializing_if = "Option::is_none" + )] + pub revert_strings: Option, + ///How much extra debug information to include in comments in the produced EVM assembly and + /// Yul code. + /// Available components are: + // - `location`: Annotations of the form `@src ::` indicating the location of + // the corresponding element in the original Solidity file, where: + // - `` is the file index matching the `@use-src` annotation, + // - `` is the index of the first byte at that location, + // - `` is the index of the first byte after that location. + // - `snippet`: A single-line code snippet from the location indicated by `@src`. The snippet is + // quoted and follows the corresponding `@src` annotation. + // - `*`: Wildcard value that can be used to request everything. + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub debug_info: Vec, +} + +/// How to treat revert (and require) reason strings. +#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] +pub enum RevertStrings { + /// "default" does not inject compiler-generated revert strings and keeps user-supplied ones. + #[default] + Default, + /// "strip" removes all revert strings (if possible, i.e. if literals are used) keeping + /// side-effects + Strip, + /// "debug" injects strings for compiler-generated internal reverts, implemented for ABI + /// encoders V1 and V2 for now. + Debug, + /// "verboseDebug" even appends further information to user-supplied revert strings (not yet + /// implemented) + VerboseDebug, +} + +impl fmt::Display for RevertStrings { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let string = match self { + RevertStrings::Default => "default", + RevertStrings::Strip => "strip", + RevertStrings::Debug => "debug", + RevertStrings::VerboseDebug => "verboseDebug", + }; + write!(f, "{string}") + } +} + +impl FromStr for RevertStrings { + type Err = String; + + fn from_str(s: &str) -> Result { + match s { + "default" => Ok(RevertStrings::Default), + "strip" => Ok(RevertStrings::Strip), + "debug" => Ok(RevertStrings::Debug), + "verboseDebug" | "verbosedebug" => Ok(RevertStrings::VerboseDebug), + s => Err(format!("Unknown evm version: {s}")), + } + } +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct SettingsMetadata { + /// Use only literal content and not URLs (false by default) + #[serde(default, rename = "useLiteralContent", skip_serializing_if = "Option::is_none")] + pub use_literal_content: Option, + /// Use the given hash method for the metadata hash that is appended to the bytecode. + /// The metadata hash can be removed from the bytecode via option "none". + /// The other options are "ipfs" and "bzzr1". + /// If the option is omitted, "ipfs" is used by default. + #[serde( + default, + rename = "bytecodeHash", + skip_serializing_if = "Option::is_none", + with = "serde_helpers::display_from_str_opt" + )] + pub bytecode_hash: Option, + #[serde(default, rename = "appendCBOR", skip_serializing_if = "Option::is_none")] + pub cbor_metadata: Option, +} + +impl SettingsMetadata { + pub fn new(hash: BytecodeHash, cbor: bool) -> Self { + Self { use_literal_content: None, bytecode_hash: Some(hash), cbor_metadata: Some(cbor) } + } +} + +impl From for SettingsMetadata { + fn from(hash: BytecodeHash) -> Self { + Self { use_literal_content: None, bytecode_hash: Some(hash), cbor_metadata: None } + } +} + +/// Determines the hash method for the metadata hash that is appended to the bytecode. +/// +/// Solc's default is `Ipfs`, see . +#[derive(Clone, Debug, Default, Copy, PartialEq, Eq, Serialize, Deserialize)] +pub enum BytecodeHash { + #[default] + Ipfs, + None, + Bzzr1, +} + +impl FromStr for BytecodeHash { + type Err = String; + + fn from_str(s: &str) -> Result { + match s { + "none" => Ok(BytecodeHash::None), + "ipfs" => Ok(BytecodeHash::Ipfs), + "bzzr1" => Ok(BytecodeHash::Bzzr1), + s => Err(format!("Unknown bytecode hash: {s}")), + } + } +} + +impl fmt::Display for BytecodeHash { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let s = match self { + BytecodeHash::Ipfs => "ipfs", + BytecodeHash::None => "none", + BytecodeHash::Bzzr1 => "bzzr1", + }; + f.write_str(s) + } +} + +/// Bindings for [`solc` contract metadata](https://docs.soliditylang.org/en/latest/metadata.html) +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct Metadata { + pub compiler: Compiler, + pub language: String, + pub output: Output, + pub settings: MetadataSettings, + pub sources: MetadataSources, + pub version: i64, +} + +/// A helper type that ensures lossless (de)serialisation so we can preserve the exact String +/// metadata value that's being hashed by solc +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct LosslessMetadata { + /// The complete abi as json value + pub raw_metadata: String, + /// The deserialised metadata of `raw_metadata` + pub metadata: Metadata, +} + +// === impl LosslessMetadata === + +impl LosslessMetadata { + /// Returns the whole string raw metadata as `serde_json::Value` + pub fn raw_json(&self) -> serde_json::Result { + serde_json::from_str(&self.raw_metadata) + } +} + +impl Serialize for LosslessMetadata { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_str(&self.raw_metadata) + } +} + +impl<'de> Deserialize<'de> for LosslessMetadata { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct LosslessMetadataVisitor; + + impl<'de> Visitor<'de> for LosslessMetadataVisitor { + type Value = LosslessMetadata; + + fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(formatter, "metadata string") + } + + fn visit_str(self, value: &str) -> Result + where + E: serde::de::Error, + { + let metadata = serde_json::from_str(value).map_err(serde::de::Error::custom)?; + let raw_metadata = value.to_string(); + Ok(LosslessMetadata { raw_metadata, metadata }) + } + } + deserializer.deserialize_str(LosslessMetadataVisitor) + } +} + +/// Compiler settings +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct MetadataSettings { + #[serde(default)] + pub remappings: Vec, + pub optimizer: Optimizer, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub metadata: Option, + /// Required for Solidity: File and name of the contract or library this metadata is created + /// for. + #[serde(default, rename = "compilationTarget")] + pub compilation_target: BTreeMap, + /// Metadata settings + /// + /// Note: this differs from `Libraries` and does not require another mapping for file name + /// since metadata is per file + #[serde(default)] + pub libraries: BTreeMap, + /// Change compilation pipeline to go through the Yul intermediate representation. This is + /// false by default. + #[serde(rename = "viaIR", default, skip_serializing_if = "Option::is_none")] + pub via_ir: Option, +} + +/// Compilation source files/source units, keys are file names +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct MetadataSources { + #[serde(flatten)] + pub inner: BTreeMap, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct MetadataSource { + /// Required: keccak256 hash of the source file + pub keccak256: String, + /// Required (unless "content" is used, see below): Sorted URL(s) + /// to the source file, protocol is more or less arbitrary, but a + /// Swarm URL is recommended + #[serde(default)] + pub urls: Vec, + /// Required (unless "url" is used): literal contents of the source file + #[serde(default, skip_serializing_if = "Option::is_none")] + pub content: Option, + /// Optional: SPDX license identifier as given in the source file + pub license: Option, +} + +/// Model checker settings for solc +#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct ModelCheckerSettings { + #[serde(default, skip_serializing_if = "BTreeMap::is_empty")] + pub contracts: BTreeMap>, + #[serde( + default, + with = "serde_helpers::display_from_str_opt", + skip_serializing_if = "Option::is_none" + )] + pub engine: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub timeout: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub targets: Option>, + #[serde(skip_serializing_if = "Option::is_none")] + pub invariants: Option>, + #[serde(skip_serializing_if = "Option::is_none")] + pub show_unproved: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub div_mod_with_slacks: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub solvers: Option>, + #[serde(skip_serializing_if = "Option::is_none")] + pub show_unsupported: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub show_proved_safe: Option, +} + +/// Which model checker engine to run. +#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)] +pub enum ModelCheckerEngine { + #[default] + Default, + All, + BMC, + CHC, +} + +impl fmt::Display for ModelCheckerEngine { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let string = match self { + ModelCheckerEngine::Default => "none", + ModelCheckerEngine::All => "all", + ModelCheckerEngine::BMC => "bmc", + ModelCheckerEngine::CHC => "chc", + }; + write!(f, "{string}") + } +} + +impl FromStr for ModelCheckerEngine { + type Err = String; + + fn from_str(s: &str) -> Result { + match s { + "none" => Ok(ModelCheckerEngine::Default), + "all" => Ok(ModelCheckerEngine::All), + "bmc" => Ok(ModelCheckerEngine::BMC), + "chc" => Ok(ModelCheckerEngine::CHC), + s => Err(format!("Unknown model checker engine: {s}")), + } + } +} + +/// Which model checker targets to check. +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub enum ModelCheckerTarget { + Assert, + Underflow, + Overflow, + DivByZero, + ConstantCondition, + PopEmptyArray, + OutOfBounds, + Balance, +} + +impl fmt::Display for ModelCheckerTarget { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let string = match self { + ModelCheckerTarget::Assert => "assert", + ModelCheckerTarget::Underflow => "underflow", + ModelCheckerTarget::Overflow => "overflow", + ModelCheckerTarget::DivByZero => "divByZero", + ModelCheckerTarget::ConstantCondition => "constantCondition", + ModelCheckerTarget::PopEmptyArray => "popEmptyArray", + ModelCheckerTarget::OutOfBounds => "outOfBounds", + ModelCheckerTarget::Balance => "balance", + }; + write!(f, "{string}") + } +} + +impl FromStr for ModelCheckerTarget { + type Err = String; + + fn from_str(s: &str) -> Result { + match s { + "assert" => Ok(ModelCheckerTarget::Assert), + "underflow" => Ok(ModelCheckerTarget::Underflow), + "overflow" => Ok(ModelCheckerTarget::Overflow), + "divByZero" => Ok(ModelCheckerTarget::DivByZero), + "constantCondition" => Ok(ModelCheckerTarget::ConstantCondition), + "popEmptyArray" => Ok(ModelCheckerTarget::PopEmptyArray), + "outOfBounds" => Ok(ModelCheckerTarget::OutOfBounds), + "balance" => Ok(ModelCheckerTarget::Balance), + s => Err(format!("Unknown model checker target: {s}")), + } + } +} + +/// Which model checker invariants to check. +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub enum ModelCheckerInvariant { + Contract, + Reentrancy, +} + +impl fmt::Display for ModelCheckerInvariant { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let string = match self { + ModelCheckerInvariant::Contract => "contract", + ModelCheckerInvariant::Reentrancy => "reentrancy", + }; + write!(f, "{string}") + } +} + +impl FromStr for ModelCheckerInvariant { + type Err = String; + + fn from_str(s: &str) -> Result { + match s { + "contract" => Ok(ModelCheckerInvariant::Contract), + "reentrancy" => Ok(ModelCheckerInvariant::Reentrancy), + s => Err(format!("Unknown model checker invariant: {s}")), + } + } +} + +/// Which model checker solvers to check. +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub enum ModelCheckerSolver { + Cvc4, + Eld, + Smtlib2, + Z3, +} + +impl fmt::Display for ModelCheckerSolver { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let string = match self { + ModelCheckerSolver::Cvc4 => "cvc4", + ModelCheckerSolver::Eld => "eld", + ModelCheckerSolver::Smtlib2 => "smtlib2", + ModelCheckerSolver::Z3 => "z3", + }; + write!(f, "{string}") + } +} + +impl FromStr for ModelCheckerSolver { + type Err = String; + + fn from_str(s: &str) -> Result { + match s { + "cvc4" => Ok(ModelCheckerSolver::Cvc4), + "eld" => Ok(ModelCheckerSolver::Cvc4), + "smtlib2" => Ok(ModelCheckerSolver::Smtlib2), + "z3" => Ok(ModelCheckerSolver::Z3), + s => Err(format!("Unknown model checker invariant: {s}")), + } + } +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct Compiler { + pub version: String, +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct Output { + pub abi: Vec, + pub devdoc: Option, + pub userdoc: Option, +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct SolcAbi { + #[serde(default)] + pub inputs: Vec, + #[serde(rename = "stateMutability", skip_serializing_if = "Option::is_none")] + pub state_mutability: Option, + #[serde(rename = "type")] + pub abi_type: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub name: Option, + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub outputs: Vec, + // required to satisfy solidity events + #[serde(default, skip_serializing_if = "Option::is_none")] + pub anonymous: Option, +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct Item { + #[serde(rename = "internalType")] + pub internal_type: Option, + pub name: String, + #[serde(rename = "type")] + pub put_type: String, + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub components: Vec, + /// Indexed flag. for solidity events + #[serde(default, skip_serializing_if = "Option::is_none")] + pub indexed: Option, +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct Doc { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub kind: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub methods: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub version: Option, +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, Default)] +pub struct DocLibraries { + #[serde(flatten)] + pub libs: BTreeMap, +} + +/// Content of a solidity file +/// +/// This contains the actual source code of a file +#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)] +pub struct Source { + /// Content of the file + /// + /// This is an `Arc` because it may be cloned. If the [Graph](crate::resolver::Graph) of the + /// project contains multiple conflicting versions then the same [Source] may be required by + /// conflicting versions and needs to be duplicated. + pub content: Arc, +} + +impl Source { + /// Creates a new instance of [Source] with the given content. + pub fn new(content: impl Into) -> Self { + Self { content: Arc::new(content.into()) } + } + + /// Reads the file's content + pub fn read(file: impl AsRef) -> Result { + let file = file.as_ref(); + Ok(Self::new(fs::read_to_string(file).map_err(|err| SolcIoError::new(err, file))?)) + } + + /// Recursively finds all source files under the given dir path and reads them all + pub fn read_all_from(dir: impl AsRef) -> Result { + Self::read_all_files(utils::source_files(dir)) + } + + /// Reads all source files of the given vec + /// + /// Depending on the len of the vec it will try to read the files in parallel + pub fn read_all_files(files: Vec) -> Result { + Self::read_all(files) + } + + /// Reads all files + pub fn read_all(files: I) -> Result + where + I: IntoIterator, + T: Into, + { + files + .into_iter() + .map(Into::into) + .map(|file| Self::read(&file).map(|source| (file, source))) + .collect() + } + + /// Parallelized version of `Self::read_all` that reads all files using a parallel iterator + /// + /// NOTE: this is only expected to be faster than `Self::read_all` if the given iterator + /// contains at least several paths or the files are rather large. + pub fn par_read_all(files: I) -> Result + where + I: IntoIterator, + ::IntoIter: Send, + T: Into + Send, + { + use rayon::{iter::ParallelBridge, prelude::ParallelIterator}; + files + .into_iter() + .par_bridge() + .map(Into::into) + .map(|file| Self::read(&file).map(|source| (file, source))) + .collect() + } + + /// Generate a non-cryptographically secure checksum of the file's content + pub fn content_hash(&self) -> String { + let mut hasher = md5::Md5::new(); + hasher.update(self); + let result = hasher.finalize(); + hex::encode(result) + } + + /// Returns all import statements of the file + pub fn parse_imports(&self) -> Vec<&str> { + utils::find_import_paths(self.as_ref()).map(|m| m.as_str()).collect() + } +} + +#[cfg(feature = "async")] +impl Source { + /// async version of `Self::read` + pub async fn async_read(file: impl AsRef) -> Result { + let file = file.as_ref(); + Ok(Self::new( + tokio::fs::read_to_string(file).await.map_err(|err| SolcIoError::new(err, file))?, + )) + } + + /// Finds all source files under the given dir path and reads them all + pub async fn async_read_all_from(dir: impl AsRef) -> Result { + Self::async_read_all(utils::source_files(dir.as_ref())).await + } + + /// async version of `Self::read_all` + pub async fn async_read_all(files: I) -> Result + where + I: IntoIterator, + T: Into, + { + futures_util::future::join_all( + files + .into_iter() + .map(Into::into) + .map(|file| async { Self::async_read(&file).await.map(|source| (file, source)) }), + ) + .await + .into_iter() + .collect() + } +} + +impl AsRef for Source { + fn as_ref(&self) -> &str { + &self.content + } +} + +impl AsRef<[u8]> for Source { + fn as_ref(&self) -> &[u8] { + self.content.as_bytes() + } +} + +/// Output type `solc` produces +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Default)] +pub struct CompilerOutput { + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub errors: Vec, + #[serde(default)] + pub sources: BTreeMap, + #[serde(default)] + pub contracts: Contracts, +} + +impl CompilerOutput { + /// Whether the output contains a compiler error + pub fn has_error(&self) -> bool { + self.errors.iter().any(|err| err.severity.is_error()) + } + + /// Whether the output contains a compiler warning + pub fn has_warning(&self, ignored_error_codes: &[u64]) -> bool { + self.errors.iter().any(|err| { + if err.severity.is_warning() { + err.error_code.as_ref().map_or(false, |code| !ignored_error_codes.contains(code)) + } else { + false + } + }) + } + + /// Finds the _first_ contract with the given name + pub fn find(&self, contract: impl AsRef) -> Option { + let contract_name = contract.as_ref(); + self.contracts_iter().find_map(|(name, contract)| { + (name == contract_name).then(|| CompactContractRef::from(contract)) + }) + } + + /// Finds the first contract with the given name and removes it from the set + pub fn remove(&mut self, contract: impl AsRef) -> Option { + let contract_name = contract.as_ref(); + self.contracts.values_mut().find_map(|c| c.remove(contract_name)) + } + + /// Iterate over all contracts and their names + pub fn contracts_iter(&self) -> impl Iterator { + self.contracts.values().flatten() + } + + /// Iterate over all contracts and their names + pub fn contracts_into_iter(self) -> impl Iterator { + self.contracts.into_values().flatten() + } + + /// Given the contract file's path and the contract's name, tries to return the contract's + /// bytecode, runtime bytecode, and abi + pub fn get(&self, path: &str, contract: &str) -> Option { + self.contracts + .get(path) + .and_then(|contracts| contracts.get(contract)) + .map(CompactContractRef::from) + } + + /// Returns the output's source files and contracts separately, wrapped in helper types that + /// provide several helper methods + pub fn split(self) -> (SourceFiles, OutputContracts) { + (SourceFiles(self.sources), OutputContracts(self.contracts)) + } + + /// Retains only those files the given iterator yields + /// + /// In other words, removes all contracts for files not included in the iterator + pub fn retain_files<'a, I>(&mut self, files: I) + where + I: IntoIterator, + { + // Note: use `to_lowercase` here because solc not necessarily emits the exact file name, + // e.g. `src/utils/upgradeProxy.sol` is emitted as `src/utils/UpgradeProxy.sol` + let files: HashSet<_> = files.into_iter().map(|s| s.to_lowercase()).collect(); + self.contracts.retain(|f, _| files.contains(f.to_lowercase().as_str())); + self.sources.retain(|f, _| files.contains(f.to_lowercase().as_str())); + } + + pub fn merge(&mut self, other: CompilerOutput) { + self.errors.extend(other.errors); + self.contracts.extend(other.contracts); + self.sources.extend(other.sources); + } +} + +/// A wrapper helper type for the `Contracts` type alias +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] +pub struct OutputContracts(pub Contracts); + +impl OutputContracts { + /// Returns an iterator over all contracts and their source names. + pub fn into_contracts(self) -> impl Iterator { + self.0.into_values().flatten() + } + + /// Iterate over all contracts and their names + pub fn contracts_iter(&self) -> impl Iterator { + self.0.values().flatten() + } + + /// Finds the _first_ contract with the given name + pub fn find(&self, contract: impl AsRef) -> Option { + let contract_name = contract.as_ref(); + self.contracts_iter().find_map(|(name, contract)| { + (name == contract_name).then(|| CompactContractRef::from(contract)) + }) + } + + /// Finds the first contract with the given name and removes it from the set + pub fn remove(&mut self, contract: impl AsRef) -> Option { + let contract_name = contract.as_ref(); + self.0.values_mut().find_map(|c| c.remove(contract_name)) + } +} + +/// A helper type that ensures lossless (de)serialisation unlike [`ethers_core::abi::Abi`] which +/// omits some information of (nested) components in a serde roundtrip. This is a problem for +/// abienconderv2 structs because [`ethers_core::abi::Contract`]'s representation of those are +/// [`ethers_core::abi::Param`] and the `kind` field of type [`ethers_core::abi::ParamType`] does +/// not support deeply nested components as it's the case for structs. This is not easily fixable in +/// ethabi as it would require a redesign of the overall `Param` and `ParamType` types. Instead, +/// this type keeps a copy of the [`serde_json::Value`] when deserialized from the `solc` json +/// compiler output and uses it to serialize the `abi` without loss. +#[derive(Clone, Debug, PartialEq)] +pub struct LosslessAbi { + /// The complete abi as json value + pub abi_value: serde_json::Value, + /// The deserialised version of `abi_value` + pub abi: Abi, +} + +impl Default for LosslessAbi { + fn default() -> Self { + LosslessAbi { abi_value: serde_json::json!([]), abi: Default::default() } + } +} + +impl From for Abi { + fn from(abi: LosslessAbi) -> Self { + abi.abi + } +} + +impl Serialize for LosslessAbi { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + self.abi_value.serialize(serializer) + } +} + +impl<'de> Deserialize<'de> for LosslessAbi { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let abi_value = serde_json::Value::deserialize(deserializer)?; + let abi = serde_json::from_value(abi_value.clone()).map_err(serde::de::Error::custom)?; + Ok(Self { abi_value, abi }) + } +} + +#[derive(Clone, Debug, Default, Serialize, Deserialize, Eq, PartialEq)] +pub struct UserDoc { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub version: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub kind: Option, + #[serde(default, skip_serializing_if = "::std::collections::BTreeMap::is_empty")] + pub methods: BTreeMap, + #[serde(default, skip_serializing_if = "::std::collections::BTreeMap::is_empty")] + pub events: BTreeMap, + #[serde(default, skip_serializing_if = "::std::collections::BTreeMap::is_empty")] + pub errors: BTreeMap>, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub notice: Option, +} + +#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)] +#[serde(untagged)] +pub enum UserDocNotice { + // NOTE: this a variant used for constructors on older solc versions + Constructor(String), + Notice { notice: String }, +} + +#[derive(Clone, Debug, Default, Serialize, Deserialize, Eq, PartialEq)] +pub struct DevDoc { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub version: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub kind: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub author: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub details: Option, + #[serde(default, rename = "custom:experimental", skip_serializing_if = "Option::is_none")] + pub custom_experimental: Option, + #[serde(default, skip_serializing_if = "::std::collections::BTreeMap::is_empty")] + pub methods: BTreeMap, + #[serde(default, skip_serializing_if = "::std::collections::BTreeMap::is_empty")] + pub events: BTreeMap, + #[serde(default, skip_serializing_if = "::std::collections::BTreeMap::is_empty")] + pub errors: BTreeMap>, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub title: Option, +} + +#[derive(Clone, Debug, Default, Serialize, Deserialize, Eq, PartialEq)] +pub struct MethodDoc { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub details: Option, + #[serde(default, skip_serializing_if = "::std::collections::BTreeMap::is_empty")] + pub params: BTreeMap, + #[serde(default, skip_serializing_if = "::std::collections::BTreeMap::is_empty")] + pub returns: BTreeMap, +} + +#[derive(Clone, Debug, Default, Serialize, Deserialize, Eq, PartialEq)] +pub struct EventDoc { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub details: Option, + #[serde(default, skip_serializing_if = "::std::collections::BTreeMap::is_empty")] + pub params: BTreeMap, +} + +#[derive(Clone, Debug, Default, Serialize, Deserialize, Eq, PartialEq)] +pub struct ErrorDoc { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub details: Option, + #[serde(default, skip_serializing_if = "::std::collections::BTreeMap::is_empty")] + pub params: BTreeMap, +} + +#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)] +#[serde(rename_all = "camelCase")] +pub struct Evm { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub assembly: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub legacy_assembly: Option, + pub bytecode: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub deployed_bytecode: Option, + /// The list of function hashes + #[serde(default, skip_serializing_if = "::std::collections::BTreeMap::is_empty")] + pub method_identifiers: BTreeMap, + /// Function gas estimates + #[serde(default, skip_serializing_if = "Option::is_none")] + pub gas_estimates: Option, +} + +impl Evm { + /// Crate internal helper do transform the underlying bytecode artifacts into a more convenient + /// structure + pub(crate) fn into_compact(self) -> CompactEvm { + let Evm { + assembly, + legacy_assembly, + bytecode, + deployed_bytecode, + method_identifiers, + gas_estimates, + } = self; + + let (bytecode, deployed_bytecode) = match (bytecode, deployed_bytecode) { + (Some(bcode), Some(dbcode)) => (Some(bcode.into()), Some(dbcode.into())), + (None, Some(dbcode)) => (None, Some(dbcode.into())), + (Some(bcode), None) => (Some(bcode.into()), None), + (None, None) => (None, None), + }; + + CompactEvm { + assembly, + legacy_assembly, + bytecode, + deployed_bytecode, + method_identifiers, + gas_estimates, + } + } +} + +#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)] +#[serde(rename_all = "camelCase")] +pub(crate) struct CompactEvm { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub assembly: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub legacy_assembly: Option, + pub bytecode: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub deployed_bytecode: Option, + /// The list of function hashes + #[serde(default, skip_serializing_if = "::std::collections::BTreeMap::is_empty")] + pub method_identifiers: BTreeMap, + /// Function gas estimates + #[serde(default, skip_serializing_if = "Option::is_none")] + pub gas_estimates: Option, +} + +#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)] +#[serde(rename_all = "camelCase")] +pub struct FunctionDebugData { + pub entry_point: Option, + pub id: Option, + pub parameter_slots: Option, + pub return_slots: Option, +} + +#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)] +pub struct GeneratedSource { + pub ast: serde_json::Value, + pub contents: String, + pub id: u32, + pub language: String, + pub name: String, +} + +/// Byte offsets into the bytecode. +/// Linking replaces the 20 bytes located there. +#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)] +pub struct Offsets { + pub start: u32, + pub length: u32, +} + +#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)] +pub struct GasEstimates { + pub creation: Creation, + #[serde(default)] + pub external: BTreeMap, + #[serde(default)] + pub internal: BTreeMap, +} + +#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)] +#[serde(rename_all = "camelCase")] +pub struct Creation { + pub code_deposit_cost: String, + pub execution_cost: String, + pub total_cost: String, +} + +#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)] +pub struct Ewasm { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub wast: Option, + pub wasm: String, +} + +/// Represents the `storage-layout` section of the `CompilerOutput` if selected. +#[derive(Clone, Debug, Default, Serialize, Deserialize, Eq, PartialEq)] +pub struct StorageLayout { + pub storage: Vec, + #[serde(default, deserialize_with = "serde_helpers::default_for_null")] + pub types: BTreeMap, +} + +impl StorageLayout { + fn is_empty(&self) -> bool { + self.storage.is_empty() && self.types.is_empty() + } +} + +#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)] +pub struct Storage { + #[serde(rename = "astId")] + pub ast_id: u64, + pub contract: String, + pub label: String, + pub offset: i64, + pub slot: String, + #[serde(rename = "type")] + pub storage_type: String, +} + +#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)] +pub struct StorageType { + pub encoding: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub key: Option, + pub label: String, + #[serde(rename = "numberOfBytes")] + pub number_of_bytes: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub value: Option, + /// additional fields + #[serde(flatten)] + pub other: BTreeMap, +} + +#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq, Hash)] +#[serde(rename_all = "camelCase")] +pub struct Error { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub source_location: Option, + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub secondary_source_locations: Vec, + pub r#type: String, + pub component: String, + pub severity: Severity, + #[serde(default, with = "serde_helpers::display_from_str_opt")] + pub error_code: Option, + pub message: String, + pub formatted_message: Option, +} + +/// Tries to mimic Solidity's own error formatting. +/// +/// +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if !Paint::is_enabled() { + let msg = self.formatted_message.as_ref().unwrap_or(&self.message); + self.fmt_severity(f)?; + f.write_str(": ")?; + return f.write_str(msg) + } + + // Error (XXXX): Error Message + styled(f, self.severity.color().style().bold(), |f| self.fmt_severity(f))?; + fmt_msg(f, &self.message)?; + + if let Some(msg) = &self.formatted_message { + let mut lines = msg.lines(); + + // skip first line, it should be similar to the error message we wrote above + lines.next(); + + // format the main source location + fmt_source_location(f, &mut lines)?; + + // format remaining lines as secondary locations + while let Some(line) = lines.next() { + f.write_str("\n")?; + + if let Some((note, msg)) = line.split_once(':') { + styled(f, Self::secondary_style(), |f| f.write_str(note))?; + fmt_msg(f, msg)?; + } else { + f.write_str(line)?; + } + + fmt_source_location(f, &mut lines)?; + } + } + + Ok(()) + } +} + +impl Error { + /// The style of the diagnostic severity. + pub fn error_style(&self) -> Style { + self.severity.color().style().bold() + } + + /// The style of the diagnostic message. + pub fn message_style() -> Style { + Color::White.style().bold() + } + + /// The style of the secondary source location. + pub fn secondary_style() -> Style { + Color::Cyan.style().bold() + } + + /// The style of the source location highlight. + pub fn highlight_style() -> Style { + Color::Yellow.style() + } + + /// The style of the diagnostics. + pub fn diag_style() -> Style { + Color::Yellow.style().bold() + } + + /// The style of the source location frame. + pub fn frame_style() -> Style { + Color::Blue.style() + } + + /// Formats the diagnostic severity: + /// + /// ```text + /// Error (XXXX) + /// ``` + fn fmt_severity(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_str(self.severity.as_str())?; + if let Some(code) = self.error_code { + write!(f, " ({code})")?; + } + Ok(()) + } +} + +/// Calls `fun` in between [`Style::fmt_prefix`] and [`Style::fmt_suffix`]. +fn styled(f: &mut fmt::Formatter, style: Style, fun: F) -> fmt::Result +where + F: FnOnce(&mut fmt::Formatter) -> fmt::Result, +{ + style.fmt_prefix(f)?; + fun(f)?; + style.fmt_suffix(f) +} + +/// Formats the diagnostic message. +fn fmt_msg(f: &mut fmt::Formatter, msg: &str) -> fmt::Result { + styled(f, Error::message_style(), |f| { + f.write_str(": ")?; + f.write_str(msg.trim_start()) + }) +} + +/// Colors a Solidity source location: +/// +/// ```text +/// --> /home/user/contract.sol:420:69: +/// | +/// 420 | bad_code() +/// | ^ +/// ``` +fn fmt_source_location(f: &mut fmt::Formatter, lines: &mut std::str::Lines) -> fmt::Result { + // --> source + if let Some(line) = lines.next() { + f.write_str("\n")?; + + let arrow = "-->"; + if let Some((left, loc)) = line.split_once(arrow) { + f.write_str(left)?; + styled(f, Error::frame_style(), |f| f.write_str(arrow))?; + f.write_str(loc)?; + } else { + f.write_str(line)?; + } + } + + // get the next 3 lines + // FIXME: Somehow do this without allocating + let next_3 = lines.take(3).collect::>(); + let [line1, line2, line3] = next_3[..] else { + for line in next_3 { + f.write_str("\n")?; + f.write_str(line)?; + } + return Ok(()) + }; + + // line 1, just a frame + fmt_framed_location(f, line1, None)?; + + // line 2, frame and code; highlight the text based on line 3's carets + let hl_start = line3.find('^'); + let highlight = hl_start.map(|start| { + let end = if line3.contains("^ (") { + // highlight the entire line because of "spans across multiple lines" diagnostic + line2.len() + } else if let Some(carets) = line3[start..].find(|c: char| c != '^') { + // highlight the text that the carets point to + start + carets + } else { + // the carets span the entire third line + line3.len() + } + // bound in case carets span longer than the code they point to + .min(line2.len()); + (start.min(end)..end, Error::highlight_style()) + }); + fmt_framed_location(f, line2, highlight)?; + + // line 3, frame and maybe highlight, this time till the end unconditionally + let highlight = hl_start.map(|i| (i..line3.len(), Error::diag_style())); + fmt_framed_location(f, line3, highlight) +} + +/// Colors a single Solidity framed source location line. Part of [`fmt_source_location`]. +fn fmt_framed_location( + f: &mut fmt::Formatter, + line: &str, + highlight: Option<(Range, Style)>, +) -> fmt::Result { + f.write_str("\n")?; + + if let Some((space_or_line_number, rest)) = line.split_once('|') { + // if the potential frame is not just whitespace or numbers, don't color it + if !space_or_line_number.chars().all(|c| c.is_whitespace() || c.is_numeric()) { + return f.write_str(line) + } + + styled(f, Error::frame_style(), |f| { + f.write_str(space_or_line_number)?; + f.write_str("|") + })?; + + if let Some((range, style)) = highlight { + let Range { start, end } = range; + let rest_start = line.len() - rest.len(); + f.write_str(&line[rest_start..start])?; + styled(f, style, |f| f.write_str(&line[range]))?; + f.write_str(&line[end..]) + } else { + f.write_str(rest) + } + } else { + f.write_str(line) + } +} + +#[derive( + Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize, +)] +#[serde(rename_all = "lowercase")] +pub enum Severity { + #[default] + Error, + Warning, + Info, +} + +impl fmt::Display for Severity { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(self.as_str()) + } +} + +impl FromStr for Severity { + type Err = String; + + fn from_str(s: &str) -> Result { + match s { + "Error" | "error" => Ok(Self::Error), + "Warning" | "warning" => Ok(Self::Warning), + "Info" | "info" => Ok(Self::Info), + s => Err(format!("Invalid severity: {s}")), + } + } +} + +impl Severity { + /// Returns `true` if the severity is `Error`. + pub const fn is_error(&self) -> bool { + matches!(self, Self::Error) + } + + /// Returns `true` if the severity is `Warning`. + pub const fn is_warning(&self) -> bool { + matches!(self, Self::Warning) + } + + /// Returns `true` if the severity is `Info`. + pub const fn is_info(&self) -> bool { + matches!(self, Self::Info) + } + + /// Returns the string representation of the severity. + pub const fn as_str(&self) -> &'static str { + match self { + Self::Error => "Error", + Self::Warning => "Warning", + Self::Info => "Info", + } + } + + /// Returns the color to format the severity with. + pub const fn color(&self) -> Color { + match self { + Self::Error => Color::Red, + Self::Warning => Color::Yellow, + Self::Info => Color::White, + } + } +} + +#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq, Hash)] +pub struct SourceLocation { + pub file: String, + pub start: i32, + pub end: i32, +} + +#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq, Hash)] +pub struct SecondarySourceLocation { + pub file: Option, + pub start: Option, + pub end: Option, + pub message: Option, +} + +#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)] +pub struct SourceFile { + pub id: u32, + #[serde(default, with = "serde_helpers::empty_json_object_opt")] + pub ast: Option, +} + +// === impl SourceFile === + +impl SourceFile { + /// Returns `true` if the source file contains at least 1 `ContractDefinition` such as + /// `contract`, `abstract contract`, `interface` or `library` + pub fn contains_contract_definition(&self) -> bool { + if let Some(ref ast) = self.ast { + // contract definitions are only allowed at the source-unit level + return ast.nodes.iter().any(|node| node.node_type == NodeType::ContractDefinition) + // abstract contract, interfaces: ContractDefinition + } + + false + } +} + +/// A wrapper type for a list of source files +/// `path -> SourceFile` +#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)] +pub struct SourceFiles(pub BTreeMap); + +impl SourceFiles { + /// Returns an iterator over the source files' ids and path + /// + /// ``` + /// use std::collections::BTreeMap; + /// use foundry_compilers::artifacts::SourceFiles; + /// # fn demo(files: SourceFiles) { + /// let sources: BTreeMap = files.into_ids().collect(); + /// # } + /// ``` + pub fn into_ids(self) -> impl Iterator { + self.0.into_iter().map(|(k, v)| (v.id, k)) + } + + /// Returns an iterator over the source files' paths and ids + /// + /// ``` + /// use std::collections::BTreeMap; + /// use foundry_compilers::artifacts::SourceFiles; + /// # fn demo(files: SourceFiles) { + /// let sources :BTreeMap = files.into_paths().collect(); + /// # } + /// ``` + pub fn into_paths(self) -> impl Iterator { + self.0.into_iter().map(|(k, v)| (k, v.id)) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::AggregatedCompilerOutput; + use ethers_core::types::Address; + use std::{fs, path::PathBuf}; + + #[test] + fn can_parse_declaration_error() { + let s = r#"{ + "errors": [ + { + "component": "general", + "errorCode": "7576", + "formattedMessage": "DeclarationError: Undeclared identifier. Did you mean \"revert\"?\n --> /Users/src/utils/UpgradeProxy.sol:35:17:\n |\n35 | refert(\"Transparent ERC1967 proxies do not have upgradeable implementations\");\n | ^^^^^^\n\n", + "message": "Undeclared identifier. Did you mean \"revert\"?", + "severity": "error", + "sourceLocation": { + "end": 1623, + "file": "/Users/src/utils/UpgradeProxy.sol", + "start": 1617 + }, + "type": "DeclarationError" + } + ], + "sources": { } +}"#; + + let out: CompilerOutput = serde_json::from_str(s).unwrap(); + assert_eq!(out.errors.len(), 1); + + let mut aggregated = AggregatedCompilerOutput::default(); + aggregated.extend("0.8.12".parse().unwrap(), out); + assert!(!aggregated.is_unchanged()); + } + + #[test] + fn can_link_bytecode() { + // test cases taken from + + #[derive(Serialize, Deserialize)] + struct Mockject { + object: BytecodeObject, + } + fn parse_bytecode(bytecode: &str) -> BytecodeObject { + let object: Mockject = + serde_json::from_value(serde_json::json!({ "object": bytecode })).unwrap(); + object.object + } + + let bytecode = "6060604052341561000f57600080fd5b60f48061001d6000396000f300606060405260043610603e5763ffffffff7c010000000000000000000000000000000000000000000000000000000060003504166326121ff081146043575b600080fd5b3415604d57600080fd5b60536055565b005b73__lib2.sol:L____________________________6326121ff06040518163ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040160006040518083038186803b151560b357600080fd5b6102c65a03f4151560c357600080fd5b5050505600a165627a7a723058207979b30bd4a07c77b02774a511f2a1dd04d7e5d65b5c2735b5fc96ad61d43ae40029"; + + let mut object = parse_bytecode(bytecode); + assert!(object.is_unlinked()); + assert!(object.contains_placeholder("lib2.sol", "L")); + assert!(object.contains_fully_qualified_placeholder("lib2.sol:L")); + assert!(object.link("lib2.sol", "L", Address::random()).resolve().is_some()); + assert!(!object.is_unlinked()); + + let mut code = Bytecode { + function_debug_data: Default::default(), + object: parse_bytecode(bytecode), + opcodes: None, + source_map: None, + generated_sources: vec![], + link_references: BTreeMap::from([( + "lib2.sol".to_string(), + BTreeMap::from([("L".to_string(), vec![])]), + )]), + }; + + assert!(!code.link("lib2.sol", "Y", Address::random())); + assert!(code.link("lib2.sol", "L", Address::random())); + assert!(code.link("lib2.sol", "L", Address::random())); + + let hashed_placeholder = "6060604052341561000f57600080fd5b60f48061001d6000396000f300606060405260043610603e5763ffffffff7c010000000000000000000000000000000000000000000000000000000060003504166326121ff081146043575b600080fd5b3415604d57600080fd5b60536055565b005b73__$cb901161e812ceb78cfe30ca65050c4337$__6326121ff06040518163ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040160006040518083038186803b151560b357600080fd5b6102c65a03f4151560c357600080fd5b5050505600a165627a7a723058207979b30bd4a07c77b02774a511f2a1dd04d7e5d65b5c2735b5fc96ad61d43ae40029"; + let mut object = parse_bytecode(hashed_placeholder); + assert!(object.is_unlinked()); + assert!(object.contains_placeholder("lib2.sol", "L")); + assert!(object.contains_fully_qualified_placeholder("lib2.sol:L")); + assert!(object.link("lib2.sol", "L", Address::default()).resolve().is_some()); + assert!(!object.is_unlinked()); + } + + #[test] + fn can_parse_compiler_output() { + let mut dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")); + dir.push("test-data/out"); + + for path in fs::read_dir(dir).unwrap() { + let path = path.unwrap().path(); + let compiler_output = fs::read_to_string(&path).unwrap(); + serde_json::from_str::(&compiler_output).unwrap_or_else(|err| { + panic!("Failed to read compiler output of {} {}", path.display(), err) + }); + } + } + + #[test] + fn can_parse_compiler_input() { + let mut dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")); + dir.push("test-data/in"); + + for path in fs::read_dir(dir).unwrap() { + let path = path.unwrap().path(); + let compiler_input = fs::read_to_string(&path).unwrap(); + serde_json::from_str::(&compiler_input).unwrap_or_else(|err| { + panic!("Failed to read compiler input of {} {}", path.display(), err) + }); + } + } + + #[test] + fn can_parse_standard_json_compiler_input() { + let mut dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")); + dir.push("test-data/in"); + + for path in fs::read_dir(dir).unwrap() { + let path = path.unwrap().path(); + let compiler_input = fs::read_to_string(&path).unwrap(); + let val = serde_json::from_str::(&compiler_input) + .unwrap_or_else(|err| { + panic!("Failed to read compiler output of {} {}", path.display(), err) + }); + + let pretty = serde_json::to_string_pretty(&val).unwrap(); + serde_json::from_str::(&pretty).unwrap_or_else(|err| { + panic!("Failed to read converted compiler input of {} {}", path.display(), err) + }); + } + } + + #[test] + fn test_evm_version_normalization() { + for (solc_version, evm_version, expected) in &[ + // Everything before 0.4.21 should always return None + ("0.4.20", EvmVersion::Homestead, None), + // Byzantium clipping + ("0.4.21", EvmVersion::Homestead, Some(EvmVersion::Homestead)), + ("0.4.21", EvmVersion::Constantinople, Some(EvmVersion::Byzantium)), + ("0.4.21", EvmVersion::London, Some(EvmVersion::Byzantium)), + // Constantinople bug fix + ("0.4.22", EvmVersion::Homestead, Some(EvmVersion::Homestead)), + ("0.4.22", EvmVersion::Constantinople, Some(EvmVersion::Constantinople)), + ("0.4.22", EvmVersion::London, Some(EvmVersion::Constantinople)), + // Petersburg + ("0.5.5", EvmVersion::Homestead, Some(EvmVersion::Homestead)), + ("0.5.5", EvmVersion::Petersburg, Some(EvmVersion::Petersburg)), + ("0.5.5", EvmVersion::London, Some(EvmVersion::Petersburg)), + // Istanbul + ("0.5.14", EvmVersion::Homestead, Some(EvmVersion::Homestead)), + ("0.5.14", EvmVersion::Istanbul, Some(EvmVersion::Istanbul)), + ("0.5.14", EvmVersion::London, Some(EvmVersion::Istanbul)), + // Berlin + ("0.8.5", EvmVersion::Homestead, Some(EvmVersion::Homestead)), + ("0.8.5", EvmVersion::Berlin, Some(EvmVersion::Berlin)), + ("0.8.5", EvmVersion::London, Some(EvmVersion::Berlin)), + // London + ("0.8.7", EvmVersion::Homestead, Some(EvmVersion::Homestead)), + ("0.8.7", EvmVersion::London, Some(EvmVersion::London)), + ("0.8.7", EvmVersion::Paris, Some(EvmVersion::London)), + // Paris + ("0.8.18", EvmVersion::Homestead, Some(EvmVersion::Homestead)), + ("0.8.18", EvmVersion::Paris, Some(EvmVersion::Paris)), + ("0.8.18", EvmVersion::Shanghai, Some(EvmVersion::Paris)), + // Shanghai + ("0.8.20", EvmVersion::Homestead, Some(EvmVersion::Homestead)), + ("0.8.20", EvmVersion::Paris, Some(EvmVersion::Paris)), + ("0.8.20", EvmVersion::Shanghai, Some(EvmVersion::Shanghai)), + ] { + let version = Version::from_str(solc_version).unwrap(); + assert_eq!( + &evm_version.normalize_version(&version), + expected, + "({version}, {evm_version:?})" + ) + } + } + + #[test] + fn can_sanitize_byte_code_hash() { + let version: Version = "0.6.0".parse().unwrap(); + + let settings = Settings { metadata: Some(BytecodeHash::Ipfs.into()), ..Default::default() }; + + let input = CompilerInput { + language: "Solidity".to_string(), + sources: Default::default(), + settings, + }; + + let i = input.clone().sanitized(&version); + assert_eq!(i.settings.metadata.unwrap().bytecode_hash, Some(BytecodeHash::Ipfs)); + + let version: Version = "0.5.17".parse().unwrap(); + let i = input.sanitized(&version); + assert!(i.settings.metadata.unwrap().bytecode_hash.is_none()); + } + + #[test] + fn can_sanitize_cbor_metadata() { + let version: Version = "0.8.18".parse().unwrap(); + + let settings = Settings { + metadata: Some(SettingsMetadata::new(BytecodeHash::Ipfs, true)), + ..Default::default() + }; + + let input = CompilerInput { + language: "Solidity".to_string(), + sources: Default::default(), + settings, + }; + + let i = input.clone().sanitized(&version); + assert_eq!(i.settings.metadata.unwrap().cbor_metadata, Some(true)); + + let i = input.sanitized(&Version::new(0, 8, 0)); + assert!(i.settings.metadata.unwrap().cbor_metadata.is_none()); + } + + #[test] + fn can_parse_libraries() { + let libraries = ["./src/lib/LibraryContract.sol:Library:0xaddress".to_string()]; + + let libs = Libraries::parse(&libraries[..]).unwrap().libs; + + assert_eq!( + libs, + BTreeMap::from([( + PathBuf::from("./src/lib/LibraryContract.sol"), + BTreeMap::from([("Library".to_string(), "0xaddress".to_string())]) + )]) + ); + } + + #[test] + fn can_parse_many_libraries() { + let libraries= [ + "./src/SizeAuctionDiscount.sol:Chainlink:0xffedba5e171c4f15abaaabc86e8bd01f9b54dae5".to_string(), + "./src/SizeAuction.sol:ChainlinkTWAP:0xffedba5e171c4f15abaaabc86e8bd01f9b54dae5".to_string(), + "./src/SizeAuction.sol:Math:0x902f6cf364b8d9470d5793a9b2b2e86bddd21e0c".to_string(), + "./src/test/ChainlinkTWAP.t.sol:ChainlinkTWAP:0xffedba5e171c4f15abaaabc86e8bd01f9b54dae5".to_string(), + "./src/SizeAuctionDiscount.sol:Math:0x902f6cf364b8d9470d5793a9b2b2e86bddd21e0c".to_string(), + ]; + + let libs = Libraries::parse(&libraries[..]).unwrap().libs; + + pretty_assertions::assert_eq!( + libs, + BTreeMap::from([ + ( + PathBuf::from("./src/SizeAuctionDiscount.sol"), + BTreeMap::from([ + ( + "Chainlink".to_string(), + "0xffedba5e171c4f15abaaabc86e8bd01f9b54dae5".to_string() + ), + ( + "Math".to_string(), + "0x902f6cf364b8d9470d5793a9b2b2e86bddd21e0c".to_string() + ) + ]) + ), + ( + PathBuf::from("./src/SizeAuction.sol"), + BTreeMap::from([ + ( + "ChainlinkTWAP".to_string(), + "0xffedba5e171c4f15abaaabc86e8bd01f9b54dae5".to_string() + ), + ( + "Math".to_string(), + "0x902f6cf364b8d9470d5793a9b2b2e86bddd21e0c".to_string() + ) + ]) + ), + ( + PathBuf::from("./src/test/ChainlinkTWAP.t.sol"), + BTreeMap::from([( + "ChainlinkTWAP".to_string(), + "0xffedba5e171c4f15abaaabc86e8bd01f9b54dae5".to_string() + )]) + ), + ]) + ); + } + + #[test] + fn test_lossless_metadata() { + #[derive(Debug, Serialize, Deserialize)] + #[serde(rename_all = "camelCase")] + pub struct Contract { + #[serde( + default, + skip_serializing_if = "Option::is_none", + with = "serde_helpers::json_string_opt" + )] + pub metadata: Option, + } + + let s = r#"{"metadata":"{\"compiler\":{\"version\":\"0.4.18+commit.9cf6e910\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"constant\":true,\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"}],\"devdoc\":{\"methods\":{\"transferOwnership(address)\":{\"details\":\"Allows the current owner to transfer control of the contract to a newOwner.\",\"params\":{\"newOwner\":\"The address to transfer ownership to.\"}}},\"title\":\"Ownable\"},\"userdoc\":{\"methods\":{}}},\"settings\":{\"compilationTarget\":{\"src/Contract.sol\":\"Ownable\"},\"libraries\":{},\"optimizer\":{\"enabled\":true,\"runs\":1000000},\"remappings\":[\":src/=src/\"]},\"sources\":{\"src/Contract.sol\":{\"keccak256\":\"0x3e0d611f53491f313ae035797ed7ecfd1dfd8db8fef8f82737e6f0cd86d71de7\",\"urls\":[\"bzzr://9c33025fa9d1b8389e4c7c9534a1d70fad91c6c2ad70eb5e4b7dc3a701a5f892\"]}},\"version\":1}"}"#; + + let value: serde_json::Value = serde_json::from_str(s).unwrap(); + let c: Contract = serde_json::from_value(value).unwrap(); + assert_eq!(c.metadata.as_ref().unwrap().raw_metadata, "{\"compiler\":{\"version\":\"0.4.18+commit.9cf6e910\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"constant\":true,\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"}],\"devdoc\":{\"methods\":{\"transferOwnership(address)\":{\"details\":\"Allows the current owner to transfer control of the contract to a newOwner.\",\"params\":{\"newOwner\":\"The address to transfer ownership to.\"}}},\"title\":\"Ownable\"},\"userdoc\":{\"methods\":{}}},\"settings\":{\"compilationTarget\":{\"src/Contract.sol\":\"Ownable\"},\"libraries\":{},\"optimizer\":{\"enabled\":true,\"runs\":1000000},\"remappings\":[\":src/=src/\"]},\"sources\":{\"src/Contract.sol\":{\"keccak256\":\"0x3e0d611f53491f313ae035797ed7ecfd1dfd8db8fef8f82737e6f0cd86d71de7\",\"urls\":[\"bzzr://9c33025fa9d1b8389e4c7c9534a1d70fad91c6c2ad70eb5e4b7dc3a701a5f892\"]}},\"version\":1}"); + + let value = serde_json::to_string(&c).unwrap(); + pretty_assertions::assert_eq!(s, value); + } + + #[test] + fn test_lossless_storage_layout() { + let input = include_str!("../../test-data/foundryissue2462.json").trim(); + let layout: StorageLayout = serde_json::from_str(input).unwrap(); + pretty_assertions::assert_eq!(input, &serde_json::to_string(&layout).unwrap()); + } + + // + #[test] + fn can_parse_compiler_output_spells_0_6_12() { + let path = + PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("test-data/0.6.12-with-libs.json"); + let content = fs::read_to_string(path).unwrap(); + let _output: CompilerOutput = serde_json::from_str(&content).unwrap(); + } +} diff --git a/src/artifacts/output_selection.rs b/src/artifacts/output_selection.rs new file mode 100644 index 00000000..769b4d4b --- /dev/null +++ b/src/artifacts/output_selection.rs @@ -0,0 +1,596 @@ +//! bindings for standard json output selection + +use serde::{ser::SerializeMap, Deserialize, Deserializer, Serialize, Serializer}; +use std::{collections::BTreeMap, fmt, str::FromStr}; + +/// Represents the desired outputs based on a File `(file -> (contract -> [outputs]))` +pub type FileOutputSelection = BTreeMap>; + +/// Represents the selected output of files and contracts +/// The first level key is the file name and the second level key is the +/// contract name. An empty contract name is used for outputs that are +/// not tied to a contract but to the whole source file like the AST. +/// A star as contract name refers to all contracts in the file. +/// Similarly, a star as a file name matches all files. +/// To select all outputs the compiler can possibly generate, use +/// "outputSelection: { "*": { "*": [ "*" ], "": [ "*" ] } }" +/// but note that this might slow down the compilation process needlessly. +/// +/// The available output types are as follows: +/// +/// File level (needs empty string as contract name): +/// ast - AST of all source files +/// +/// Contract level (needs the contract name or "*"): +/// abi - ABI +/// devdoc - Developer documentation (natspec) +/// userdoc - User documentation (natspec) +/// metadata - Metadata +/// ir - Yul intermediate representation of the code before optimization +/// irOptimized - Intermediate representation after optimization +/// storageLayout - Slots, offsets and types of the contract's state +/// variables. +/// evm.assembly - New assembly format +/// evm.legacyAssembly - Old-style assembly format in JSON +/// evm.bytecode.functionDebugData - Debugging information at function level +/// evm.bytecode.object - Bytecode object +/// evm.bytecode.opcodes - Opcodes list +/// evm.bytecode.sourceMap - Source mapping (useful for debugging) +/// evm.bytecode.linkReferences - Link references (if unlinked object) +/// evm.bytecode.generatedSources - Sources generated by the compiler +/// evm.deployedBytecode* - Deployed bytecode (has all the options that +/// evm.bytecode has) +/// evm.deployedBytecode.immutableReferences - Map from AST ids to +/// bytecode ranges that reference immutables +/// evm.methodIdentifiers - The list of function hashes +/// evm.gasEstimates - Function gas estimates +/// ewasm.wast - Ewasm in WebAssembly S-expressions format +/// ewasm.wasm - Ewasm in WebAssembly binary format +/// +/// Note that using a using `evm`, `evm.bytecode`, `ewasm`, etc. will select +/// every target part of that output. Additionally, `*` can be used as a +/// wildcard to request everything. +/// +/// The default output selection is +/// +/// ```json +/// { +/// "*": { +/// "*": [ +/// "abi", +/// "evm.bytecode", +/// "evm.deployedBytecode", +/// "evm.methodIdentifiers" +/// ], +/// "": [ +/// "ast" +/// ] +/// } +/// } +/// ``` +#[derive(Debug, Clone, Eq, PartialEq, Default, Deserialize)] +#[serde(transparent)] +pub struct OutputSelection(pub BTreeMap); + +impl OutputSelection { + /// select all outputs the compiler can possibly generate, use + /// `{ "*": { "*": [ "*" ], "": [ "*" ] } }` + /// but note that this might slow down the compilation process needlessly. + pub fn complete_output_selection() -> Self { + BTreeMap::from([( + "*".to_string(), + BTreeMap::from([ + ("*".to_string(), vec!["*".to_string()]), + ("".to_string(), vec!["*".to_string()]), + ]), + )]) + .into() + } + + /// Default output selection for compiler output: + /// + /// `{ "*": { "*": [ "*" ], "": [ + /// "abi","evm.bytecode","evm.deployedBytecode","evm.methodIdentifiers"] } }` + /// + /// Which enables it for all files and all their contracts ("*" wildcard) + pub fn default_output_selection() -> Self { + BTreeMap::from([("*".to_string(), Self::default_file_output_selection())]).into() + } + + /// Default output selection for a single file: + /// + /// `{ "*": [ "*" ], "": [ + /// "abi","evm.bytecode","evm.deployedBytecode","evm.methodIdentifiers"] }` + /// + /// Which enables it for all the contracts in the file ("*" wildcard) + pub fn default_file_output_selection() -> FileOutputSelection { + BTreeMap::from([( + "*".to_string(), + vec![ + "abi".to_string(), + "evm.bytecode".to_string(), + "evm.deployedBytecode".to_string(), + "evm.methodIdentifiers".to_string(), + ], + )]) + } + + /// Returns an empty output selection which corresponds to an empty map `{}` + pub fn empty_file_output_select() -> FileOutputSelection { + Default::default() + } +} + +// this will make sure that if the `FileOutputSelection` for a certain file is empty will be +// serializes as `"*" : []` because +// > Contract level (needs the contract name or "*") +impl Serialize for OutputSelection { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + struct EmptyFileOutput; + + impl Serialize for EmptyFileOutput { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut map = serializer.serialize_map(Some(1))?; + map.serialize_entry("*", &[] as &[String])?; + map.end() + } + } + + let mut map = serializer.serialize_map(Some(self.0.len()))?; + for (file, selection) in self.0.iter() { + if selection.is_empty() { + map.serialize_entry(file, &EmptyFileOutput {})?; + } else { + map.serialize_entry(file, selection)?; + } + } + map.end() + } +} + +impl AsRef> for OutputSelection { + fn as_ref(&self) -> &BTreeMap { + &self.0 + } +} + +impl AsMut> for OutputSelection { + fn as_mut(&mut self) -> &mut BTreeMap { + &mut self.0 + } +} + +impl From> for OutputSelection { + fn from(s: BTreeMap) -> Self { + OutputSelection(s) + } +} + +/// Contract level output selection +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub enum ContractOutputSelection { + Abi, + DevDoc, + UserDoc, + Metadata, + Ir, + IrOptimized, + StorageLayout, + Evm(EvmOutputSelection), + Ewasm(EwasmOutputSelection), +} + +impl ContractOutputSelection { + /// Returns the basic set of contract level settings that should be included in the `Contract` + /// that solc emits: + /// - "abi" + /// - "evm.bytecode" + /// - "evm.deployedBytecode" + /// - "evm.methodIdentifiers" + pub fn basic() -> Vec { + vec![ + ContractOutputSelection::Abi, + BytecodeOutputSelection::All.into(), + DeployedBytecodeOutputSelection::All.into(), + EvmOutputSelection::MethodIdentifiers.into(), + ] + } +} + +impl Serialize for ContractOutputSelection { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.collect_str(self) + } +} + +impl<'de> Deserialize<'de> for ContractOutputSelection { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + String::deserialize(deserializer)?.parse().map_err(serde::de::Error::custom) + } +} + +impl fmt::Display for ContractOutputSelection { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + ContractOutputSelection::Abi => f.write_str("abi"), + ContractOutputSelection::DevDoc => f.write_str("devdoc"), + ContractOutputSelection::UserDoc => f.write_str("userdoc"), + ContractOutputSelection::Metadata => f.write_str("metadata"), + ContractOutputSelection::Ir => f.write_str("ir"), + ContractOutputSelection::IrOptimized => f.write_str("irOptimized"), + ContractOutputSelection::StorageLayout => f.write_str("storageLayout"), + ContractOutputSelection::Evm(e) => e.fmt(f), + ContractOutputSelection::Ewasm(e) => e.fmt(f), + } + } +} + +impl FromStr for ContractOutputSelection { + type Err = String; + + fn from_str(s: &str) -> Result { + match s { + "abi" => Ok(ContractOutputSelection::Abi), + "devdoc" => Ok(ContractOutputSelection::DevDoc), + "userdoc" => Ok(ContractOutputSelection::UserDoc), + "metadata" => Ok(ContractOutputSelection::Metadata), + "ir" => Ok(ContractOutputSelection::Ir), + "ir-optimized" | "irOptimized" | "iroptimized" => { + Ok(ContractOutputSelection::IrOptimized) + } + "storage-layout" | "storagelayout" | "storageLayout" => { + Ok(ContractOutputSelection::StorageLayout) + } + s => EvmOutputSelection::from_str(s) + .map(ContractOutputSelection::Evm) + .or_else(|_| EwasmOutputSelection::from_str(s).map(ContractOutputSelection::Ewasm)) + .map_err(|_| format!("Invalid contract output selection: {s}")), + } + } +} + +impl> From for ContractOutputSelection { + fn from(evm: T) -> Self { + ContractOutputSelection::Evm(evm.into()) + } +} + +impl From for ContractOutputSelection { + fn from(ewasm: EwasmOutputSelection) -> Self { + ContractOutputSelection::Ewasm(ewasm) + } +} + +/// Contract level output selection for `evm` +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub enum EvmOutputSelection { + All, + Assembly, + LegacyAssembly, + MethodIdentifiers, + GasEstimates, + ByteCode(BytecodeOutputSelection), + DeployedByteCode(DeployedBytecodeOutputSelection), +} + +impl From for EvmOutputSelection { + fn from(b: BytecodeOutputSelection) -> Self { + EvmOutputSelection::ByteCode(b) + } +} + +impl From for EvmOutputSelection { + fn from(b: DeployedBytecodeOutputSelection) -> Self { + EvmOutputSelection::DeployedByteCode(b) + } +} + +impl Serialize for EvmOutputSelection { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.collect_str(self) + } +} + +impl<'de> Deserialize<'de> for EvmOutputSelection { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + String::deserialize(deserializer)?.parse().map_err(serde::de::Error::custom) + } +} + +impl fmt::Display for EvmOutputSelection { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + EvmOutputSelection::All => f.write_str("evm"), + EvmOutputSelection::Assembly => f.write_str("evm.assembly"), + EvmOutputSelection::LegacyAssembly => f.write_str("evm.legacyAssembly"), + EvmOutputSelection::MethodIdentifiers => f.write_str("evm.methodIdentifiers"), + EvmOutputSelection::GasEstimates => f.write_str("evm.gasEstimates"), + EvmOutputSelection::ByteCode(b) => b.fmt(f), + EvmOutputSelection::DeployedByteCode(b) => b.fmt(f), + } + } +} + +impl FromStr for EvmOutputSelection { + type Err = String; + + fn from_str(s: &str) -> Result { + match s { + "evm" => Ok(EvmOutputSelection::All), + "asm" | "evm.assembly" => Ok(EvmOutputSelection::Assembly), + "evm.legacyAssembly" => Ok(EvmOutputSelection::LegacyAssembly), + "methodidentifiers" | "evm.methodIdentifiers" | "evm.methodidentifiers" => { + Ok(EvmOutputSelection::MethodIdentifiers) + } + "gas" | "evm.gasEstimates" | "evm.gasestimates" => Ok(EvmOutputSelection::GasEstimates), + s => BytecodeOutputSelection::from_str(s) + .map(EvmOutputSelection::ByteCode) + .or_else(|_| { + DeployedBytecodeOutputSelection::from_str(s) + .map(EvmOutputSelection::DeployedByteCode) + }) + .map_err(|_| format!("Invalid evm selection: {s}")), + } + } +} + +/// Contract level output selection for `evm.bytecode` +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub enum BytecodeOutputSelection { + All, + FunctionDebugData, + Object, + Opcodes, + SourceMap, + LinkReferences, + GeneratedSources, +} + +impl Serialize for BytecodeOutputSelection { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.collect_str(self) + } +} + +impl<'de> Deserialize<'de> for BytecodeOutputSelection { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + String::deserialize(deserializer)?.parse().map_err(serde::de::Error::custom) + } +} + +impl fmt::Display for BytecodeOutputSelection { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + BytecodeOutputSelection::All => f.write_str("evm.bytecode"), + BytecodeOutputSelection::FunctionDebugData => { + f.write_str("evm.bytecode.functionDebugData") + } + BytecodeOutputSelection::Object => f.write_str("evm.bytecode.object"), + BytecodeOutputSelection::Opcodes => f.write_str("evm.bytecode.opcodes"), + BytecodeOutputSelection::SourceMap => f.write_str("evm.bytecode.sourceMap"), + BytecodeOutputSelection::LinkReferences => f.write_str("evm.bytecode.linkReferences"), + BytecodeOutputSelection::GeneratedSources => { + f.write_str("evm.bytecode.generatedSources") + } + } + } +} + +impl FromStr for BytecodeOutputSelection { + type Err = String; + + fn from_str(s: &str) -> Result { + match s { + "evm.bytecode" => Ok(BytecodeOutputSelection::All), + "evm.bytecode.functionDebugData" => Ok(BytecodeOutputSelection::FunctionDebugData), + "code" | "bin" | "evm.bytecode.object" => Ok(BytecodeOutputSelection::Object), + "evm.bytecode.opcodes" => Ok(BytecodeOutputSelection::Opcodes), + "evm.bytecode.sourceMap" => Ok(BytecodeOutputSelection::SourceMap), + "evm.bytecode.linkReferences" => Ok(BytecodeOutputSelection::LinkReferences), + "evm.bytecode.generatedSources" => Ok(BytecodeOutputSelection::GeneratedSources), + s => Err(format!("Invalid bytecode selection: {s}")), + } + } +} + +/// Contract level output selection for `evm.deployedBytecode` +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub enum DeployedBytecodeOutputSelection { + All, + FunctionDebugData, + Object, + Opcodes, + SourceMap, + LinkReferences, + GeneratedSources, + ImmutableReferences, +} + +impl Serialize for DeployedBytecodeOutputSelection { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.collect_str(self) + } +} + +impl<'de> Deserialize<'de> for DeployedBytecodeOutputSelection { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + String::deserialize(deserializer)?.parse().map_err(serde::de::Error::custom) + } +} + +impl fmt::Display for DeployedBytecodeOutputSelection { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + DeployedBytecodeOutputSelection::All => f.write_str("evm.deployedBytecode"), + DeployedBytecodeOutputSelection::FunctionDebugData => { + f.write_str("evm.deployedBytecode.functionDebugData") + } + DeployedBytecodeOutputSelection::Object => f.write_str("evm.deployedBytecode.object"), + DeployedBytecodeOutputSelection::Opcodes => f.write_str("evm.deployedBytecode.opcodes"), + DeployedBytecodeOutputSelection::SourceMap => { + f.write_str("evm.deployedBytecode.sourceMap") + } + DeployedBytecodeOutputSelection::LinkReferences => { + f.write_str("evm.deployedBytecode.linkReferences") + } + DeployedBytecodeOutputSelection::GeneratedSources => { + f.write_str("evm.deployedBytecode.generatedSources") + } + DeployedBytecodeOutputSelection::ImmutableReferences => { + f.write_str("evm.deployedBytecode.immutableReferences") + } + } + } +} + +impl FromStr for DeployedBytecodeOutputSelection { + type Err = String; + + fn from_str(s: &str) -> Result { + match s { + "evm.deployedBytecode" => Ok(DeployedBytecodeOutputSelection::All), + "evm.deployedBytecode.functionDebugData" => { + Ok(DeployedBytecodeOutputSelection::FunctionDebugData) + } + "deployed-code" | + "deployed-bin" | + "runtime-code" | + "runtime-bin" | + "evm.deployedBytecode.object" => Ok(DeployedBytecodeOutputSelection::Object), + "evm.deployedBytecode.opcodes" => Ok(DeployedBytecodeOutputSelection::Opcodes), + "evm.deployedBytecode.sourceMap" => Ok(DeployedBytecodeOutputSelection::SourceMap), + "evm.deployedBytecode.linkReferences" => { + Ok(DeployedBytecodeOutputSelection::LinkReferences) + } + "evm.deployedBytecode.generatedSources" => { + Ok(DeployedBytecodeOutputSelection::GeneratedSources) + } + "evm.deployedBytecode.immutableReferences" => { + Ok(DeployedBytecodeOutputSelection::ImmutableReferences) + } + s => Err(format!("Invalid deployedBytecode selection: {s}")), + } + } +} + +/// Contract level output selection for `evm.ewasm` +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub enum EwasmOutputSelection { + All, + Wast, + Wasm, +} + +impl Serialize for EwasmOutputSelection { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.collect_str(self) + } +} + +impl<'de> Deserialize<'de> for EwasmOutputSelection { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + String::deserialize(deserializer)?.parse().map_err(serde::de::Error::custom) + } +} + +impl fmt::Display for EwasmOutputSelection { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + EwasmOutputSelection::All => f.write_str("ewasm"), + EwasmOutputSelection::Wast => f.write_str("ewasm.wast"), + EwasmOutputSelection::Wasm => f.write_str("ewasm.wasm"), + } + } +} + +impl FromStr for EwasmOutputSelection { + type Err = String; + + fn from_str(s: &str) -> Result { + match s { + "ewasm" => Ok(EwasmOutputSelection::All), + "ewasm.wast" => Ok(EwasmOutputSelection::Wast), + "ewasm.wasm" => Ok(EwasmOutputSelection::Wasm), + s => Err(format!("Invalid ewasm selection: {s}")), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use std::collections::BTreeMap; + + #[test] + fn outputselection_serde_works() { + let mut output = BTreeMap::default(); + output.insert( + "*".to_string(), + vec![ + "abi".to_string(), + "evm.bytecode".to_string(), + "evm.deployedBytecode".to_string(), + "evm.methodIdentifiers".to_string(), + ], + ); + + let json = serde_json::to_string(&output).unwrap(); + let deserde_selection: BTreeMap> = + serde_json::from_str(&json).unwrap(); + + assert_eq!(json, serde_json::to_string(&deserde_selection).unwrap()); + } + + #[test] + fn empty_outputselection_serde_works() { + let mut empty = OutputSelection::default(); + empty.0.insert("contract.sol".to_string(), OutputSelection::empty_file_output_select()); + let s = serde_json::to_string(&empty).unwrap(); + assert_eq!(s, r#"{"contract.sol":{"*":[]}}"#); + } + + #[test] + fn deployed_bytecode_from_str() { + assert_eq!( + DeployedBytecodeOutputSelection::from_str("evm.deployedBytecode.immutableReferences") + .unwrap(), + DeployedBytecodeOutputSelection::ImmutableReferences + ) + } +} diff --git a/src/artifacts/serde_helpers.rs b/src/artifacts/serde_helpers.rs new file mode 100644 index 00000000..a6e4197f --- /dev/null +++ b/src/artifacts/serde_helpers.rs @@ -0,0 +1,246 @@ +//! serde helpers + +use ethers_core::types::Bytes; +use serde::{Deserialize, Deserializer}; + +pub fn deserialize_bytes<'de, D>(d: D) -> Result +where + D: Deserializer<'de>, +{ + String::deserialize(d)?.parse::().map_err(serde::de::Error::custom) +} + +pub fn deserialize_opt_bytes<'de, D>(d: D) -> Result, D::Error> +where + D: Deserializer<'de>, +{ + let value = Option::::deserialize(d)?; + value.as_deref().map(str::parse).transpose().map_err(serde::de::Error::custom) +} + +pub fn default_for_null<'de, D, T>(deserializer: D) -> Result +where + D: Deserializer<'de>, + T: Deserialize<'de> + Default, +{ + Ok(Option::::deserialize(deserializer)?.unwrap_or_default()) +} + +pub mod json_string_opt { + use serde::{ + de::{self, DeserializeOwned}, + Deserialize, Deserializer, Serialize, Serializer, + }; + + pub fn serialize(value: &Option, serializer: S) -> Result + where + S: Serializer, + T: Serialize, + { + if let Some(value) = value { + value.serialize(serializer) + } else { + serializer.serialize_none() + } + } + + pub fn deserialize<'de, T, D>(deserializer: D) -> Result, D::Error> + where + D: Deserializer<'de>, + T: DeserializeOwned, + { + if let Some(s) = Option::::deserialize(deserializer)? { + if s.is_empty() { + return Ok(None) + } + let value = serde_json::Value::String(s); + serde_json::from_value(value).map_err(de::Error::custom).map(Some) + } else { + Ok(None) + } + } +} + +/// deserializes empty json object `{}` as `None` +pub mod empty_json_object_opt { + use serde::{ + de::{self, DeserializeOwned}, + Deserialize, Deserializer, Serialize, Serializer, + }; + + pub fn serialize(value: &Option, serializer: S) -> Result + where + S: Serializer, + T: Serialize, + { + if let Some(value) = value { + value.serialize(serializer) + } else { + let empty = serde_json::Value::Object(Default::default()); + serde_json::Value::serialize(&empty, serializer) + } + } + + pub fn deserialize<'de, T, D>(deserializer: D) -> Result, D::Error> + where + D: Deserializer<'de>, + T: DeserializeOwned, + { + let json = serde_json::Value::deserialize(deserializer)?; + if json.is_null() { + return Ok(None) + } + if json.as_object().map(|obj| obj.is_empty()).unwrap_or_default() { + return Ok(None) + } + serde_json::from_value(json).map_err(de::Error::custom).map(Some) + } +} + +/// serde support for string +pub mod string_bytes { + use serde::{Deserialize, Deserializer, Serializer}; + + pub fn serialize(value: &String, serializer: S) -> Result + where + S: Serializer, + { + if value.starts_with("0x") { + serializer.serialize_str(value.as_str()) + } else { + serializer.serialize_str(&format!("0x{value}")) + } + } + + pub fn deserialize<'de, D>(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let value = String::deserialize(deserializer)?; + if let Some(rem) = value.strip_prefix("0x") { + Ok(rem.to_string()) + } else { + Ok(value) + } + } +} + +pub mod display_from_str_opt { + use serde::{de, Deserialize, Deserializer, Serializer}; + use std::{fmt, str::FromStr}; + + pub fn serialize(value: &Option, serializer: S) -> Result + where + T: fmt::Display, + S: Serializer, + { + if let Some(value) = value { + serializer.collect_str(value) + } else { + serializer.serialize_none() + } + } + + pub fn deserialize<'de, T, D>(deserializer: D) -> Result, D::Error> + where + D: Deserializer<'de>, + T: FromStr, + T::Err: fmt::Display, + { + if let Some(s) = Option::::deserialize(deserializer)? { + s.parse().map_err(de::Error::custom).map(Some) + } else { + Ok(None) + } + } +} + +pub mod display_from_str { + use serde::{de, Deserialize, Deserializer, Serializer}; + use std::{fmt, str::FromStr}; + + pub fn serialize(value: &T, serializer: S) -> Result + where + T: fmt::Display, + S: Serializer, + { + serializer.collect_str(value) + } + + pub fn deserialize<'de, T, D>(deserializer: D) -> Result + where + D: Deserializer<'de>, + T: FromStr, + T::Err: fmt::Display, + { + String::deserialize(deserializer)?.parse().map_err(de::Error::custom) + } +} + +/// (De)serialize vec of tuples as map +pub mod tuple_vec_map { + use serde::{de::DeserializeOwned, Deserialize, Deserializer, Serialize, Serializer}; + + pub fn serialize(data: &[(K, V)], serializer: S) -> Result + where + S: Serializer, + K: Serialize, + V: Serialize, + { + serializer.collect_map(data.iter().map(|x| (&x.0, &x.1))) + } + + pub fn deserialize<'de, K, V, D>(deserializer: D) -> Result, D::Error> + where + D: Deserializer<'de>, + K: DeserializeOwned, + V: DeserializeOwned, + { + use serde::de::{MapAccess, Visitor}; + use std::{fmt, marker::PhantomData}; + + struct TupleVecMapVisitor { + marker: PhantomData>, + } + + impl TupleVecMapVisitor { + pub fn new() -> Self { + TupleVecMapVisitor { marker: PhantomData } + } + } + + impl<'de, K, V> Visitor<'de> for TupleVecMapVisitor + where + K: Deserialize<'de>, + V: Deserialize<'de>, + { + type Value = Vec<(K, V)>; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("a map") + } + + #[inline] + fn visit_unit(self) -> Result, E> { + Ok(Vec::new()) + } + + #[inline] + fn visit_map(self, mut access: T) -> Result, T::Error> + where + T: MapAccess<'de>, + { + let mut values = + Vec::with_capacity(std::cmp::min(access.size_hint().unwrap_or(0), 4096)); + + while let Some((key, value)) = access.next_entry()? { + values.push((key, value)); + } + + Ok(values) + } + } + + deserializer.deserialize_map(TupleVecMapVisitor::new()) + } +} diff --git a/src/report/compiler.rs b/src/report/compiler.rs index 8ad0bac3..4b2f9c60 100644 --- a/src/report/compiler.rs +++ b/src/report/compiler.rs @@ -14,9 +14,9 @@ use std::{env, path::PathBuf, str::FromStr}; /// /// # Example /// -/// If `foundry_compilers_LOG=in=in.json,out=out.json` is then the reporter will be configured to write -/// the compiler input as pretty formatted json to `in.{solc version}.json` and the compiler output -/// to `out.{solc version}.json` +/// If `foundry_compilers_LOG=in=in.json,out=out.json` is then the reporter will be configured to +/// write the compiler input as pretty formatted json to `in.{solc version}.json` and the compiler +/// output to `out.{solc version}.json` /// /// ```no_run /// use foundry_compilers::report::SolcCompilerIoReporter; @@ -42,8 +42,8 @@ impl SolcCompilerIoReporter { /// [`SolcCompilerIOLayer::from_default_env`]: #method.from_default_env pub const DEFAULT_ENV: &'static str = "foundry_compilers_LOG"; - /// Returns a new `SolcCompilerIOLayer` from the value of the `foundry_compilers_LOG` environment - /// variable, ignoring any invalid filter directives. + /// Returns a new `SolcCompilerIOLayer` from the value of the `foundry_compilers_LOG` + /// environment variable, ignoring any invalid filter directives. pub fn from_default_env() -> Self { Self::from_env(Self::DEFAULT_ENV) } From 6aa3da9e5b52e93e6dd1c95d14dfd11a7b2805a0 Mon Sep 17 00:00:00 2001 From: Enrique Ortiz Date: Wed, 4 Oct 2023 14:05:10 +0900 Subject: [PATCH 2/8] chore: adding execute permissions --- .github/scripts/install_test_binaries.sh | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 .github/scripts/install_test_binaries.sh diff --git a/.github/scripts/install_test_binaries.sh b/.github/scripts/install_test_binaries.sh old mode 100644 new mode 100755 From 866d95541084eceb26e18d55661b64064ef61853 Mon Sep 17 00:00:00 2001 From: Enrique Ortiz Date: Wed, 4 Oct 2023 14:20:33 +0900 Subject: [PATCH 3/8] chore: missing deps --- .github/scripts/install_test_binaries.sh | 0 Cargo.toml | 16 +++++++++++++++- 2 files changed, 15 insertions(+), 1 deletion(-) mode change 100755 => 100644 .github/scripts/install_test_binaries.sh diff --git a/.github/scripts/install_test_binaries.sh b/.github/scripts/install_test_binaries.sh old mode 100755 new mode 100644 diff --git a/Cargo.toml b/Cargo.toml index 9d81a48c..94493c32 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,12 +25,14 @@ serde_json = "1.0" # tracing tracing = "0.1.37" +tracing-subscriber = { version = "0.3.17", default-features = false } +tracing-futures = "0.2.5" tiny-keccak = { version = "2.0.2", default-features = false } sha2 = { version = "0.10.7", default-features = false, optional = true } md-5 = "0.10.5" -semver = { version = "1", features = ["serde"] } +semver = { version = "1.0", features = ["serde"] } walkdir = "2.3" once_cell = "1.18" regex = "1.9" @@ -59,6 +61,18 @@ svm = { package = "svm-rs", version = "0.3", default-features = false, features ], optional = true } svm-builds = { package = "svm-rs-builds", version = "0.2", optional = true } tokio = { version = "1.32", features = ["rt-multi-thread"] } +tokio-tungstenite = { version = "0.20", default-features = false } +futures = { version = "0.3.28", default-features = false, features = ["std"] } +futures-core = "0.3.28" +futures-util = "0.3.28" +futures-executor = "0.3.28" +futures-channel = "0.3.28" +futures-locks = { version = "0.7.1", default-features = false } +futures-timer = { version = "3.0.2", default-features = false, features = ["wasm-bindgen"] } +pin-project = "1.1" +reqwest = { version = "0.11.19", default-features = false } +url = { version = "2.4", default-features = false } + [dev-dependencies] criterion = { version = "0.5", features = ["async_tokio"] } From 7b2893009c43326e764782c4ea99d0cc8f9b4d16 Mon Sep 17 00:00:00 2001 From: Enrique Ortiz Date: Wed, 4 Oct 2023 14:28:22 +0900 Subject: [PATCH 4/8] permissions --- .github/scripts/install_test_binaries.sh | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 .github/scripts/install_test_binaries.sh diff --git a/.github/scripts/install_test_binaries.sh b/.github/scripts/install_test_binaries.sh old mode 100644 new mode 100755 From d579925e4f9ad2799362804f5bf7fb17bbbfbb7e Mon Sep 17 00:00:00 2001 From: Enrique Ortiz Date: Wed, 4 Oct 2023 14:31:09 +0900 Subject: [PATCH 5/8] add msrv --- .clippy.toml | 1 + 1 file changed, 1 insertion(+) create mode 100644 .clippy.toml diff --git a/.clippy.toml b/.clippy.toml new file mode 100644 index 00000000..9a72cedb --- /dev/null +++ b/.clippy.toml @@ -0,0 +1 @@ +msrv = "1.65" \ No newline at end of file From 4a7aaf4d45531ce1f502196442c54d2f4e8a0506 Mon Sep 17 00:00:00 2001 From: Enrique Ortiz Date: Wed, 4 Oct 2023 14:43:41 +0900 Subject: [PATCH 6/8] fix: push missing node_modules folder --- .../node_modules/hardhat/console.sol | 1532 +++++++++++++++++ 1 file changed, 1532 insertions(+) create mode 100644 test-data/hardhat-sample/node_modules/hardhat/console.sol diff --git a/test-data/hardhat-sample/node_modules/hardhat/console.sol b/test-data/hardhat-sample/node_modules/hardhat/console.sol new file mode 100644 index 00000000..d65e3b41 --- /dev/null +++ b/test-data/hardhat-sample/node_modules/hardhat/console.sol @@ -0,0 +1,1532 @@ +// SPDX-License-Identifier: MIT +pragma solidity >= 0.4.22 <0.9.0; + +library console { + address constant CONSOLE_ADDRESS = address(0x000000000000000000636F6e736F6c652e6c6f67); + + function _sendLogPayload(bytes memory payload) private view { + uint256 payloadLength = payload.length; + address consoleAddress = CONSOLE_ADDRESS; + assembly { + let payloadStart := add(payload, 32) + let r := staticcall(gas(), consoleAddress, payloadStart, payloadLength, 0, 0) + } + } + + function log() internal view { + _sendLogPayload(abi.encodeWithSignature("log()")); + } + + function logInt(int p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(int)", p0)); + } + + function logUint(uint p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint)", p0)); + } + + function logString(string memory p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string)", p0)); + } + + function logBool(bool p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool)", p0)); + } + + function logAddress(address p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address)", p0)); + } + + function logBytes(bytes memory p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes)", p0)); + } + + function logBytes1(bytes1 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes1)", p0)); + } + + function logBytes2(bytes2 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes2)", p0)); + } + + function logBytes3(bytes3 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes3)", p0)); + } + + function logBytes4(bytes4 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes4)", p0)); + } + + function logBytes5(bytes5 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes5)", p0)); + } + + function logBytes6(bytes6 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes6)", p0)); + } + + function logBytes7(bytes7 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes7)", p0)); + } + + function logBytes8(bytes8 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes8)", p0)); + } + + function logBytes9(bytes9 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes9)", p0)); + } + + function logBytes10(bytes10 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes10)", p0)); + } + + function logBytes11(bytes11 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes11)", p0)); + } + + function logBytes12(bytes12 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes12)", p0)); + } + + function logBytes13(bytes13 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes13)", p0)); + } + + function logBytes14(bytes14 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes14)", p0)); + } + + function logBytes15(bytes15 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes15)", p0)); + } + + function logBytes16(bytes16 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes16)", p0)); + } + + function logBytes17(bytes17 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes17)", p0)); + } + + function logBytes18(bytes18 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes18)", p0)); + } + + function logBytes19(bytes19 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes19)", p0)); + } + + function logBytes20(bytes20 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes20)", p0)); + } + + function logBytes21(bytes21 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes21)", p0)); + } + + function logBytes22(bytes22 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes22)", p0)); + } + + function logBytes23(bytes23 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes23)", p0)); + } + + function logBytes24(bytes24 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes24)", p0)); + } + + function logBytes25(bytes25 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes25)", p0)); + } + + function logBytes26(bytes26 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes26)", p0)); + } + + function logBytes27(bytes27 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes27)", p0)); + } + + function logBytes28(bytes28 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes28)", p0)); + } + + function logBytes29(bytes29 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes29)", p0)); + } + + function logBytes30(bytes30 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes30)", p0)); + } + + function logBytes31(bytes31 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes31)", p0)); + } + + function logBytes32(bytes32 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes32)", p0)); + } + + function log(uint p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint)", p0)); + } + + function log(string memory p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string)", p0)); + } + + function log(bool p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool)", p0)); + } + + function log(address p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address)", p0)); + } + + function log(uint p0, uint p1) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,uint)", p0, p1)); + } + + function log(uint p0, string memory p1) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,string)", p0, p1)); + } + + function log(uint p0, bool p1) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,bool)", p0, p1)); + } + + function log(uint p0, address p1) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,address)", p0, p1)); + } + + function log(string memory p0, uint p1) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint)", p0, p1)); + } + + function log(string memory p0, string memory p1) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string)", p0, p1)); + } + + function log(string memory p0, bool p1) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool)", p0, p1)); + } + + function log(string memory p0, address p1) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address)", p0, p1)); + } + + function log(bool p0, uint p1) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint)", p0, p1)); + } + + function log(bool p0, string memory p1) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string)", p0, p1)); + } + + function log(bool p0, bool p1) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool)", p0, p1)); + } + + function log(bool p0, address p1) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address)", p0, p1)); + } + + function log(address p0, uint p1) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint)", p0, p1)); + } + + function log(address p0, string memory p1) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string)", p0, p1)); + } + + function log(address p0, bool p1) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool)", p0, p1)); + } + + function log(address p0, address p1) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address)", p0, p1)); + } + + function log(uint p0, uint p1, uint p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,uint,uint)", p0, p1, p2)); + } + + function log(uint p0, uint p1, string memory p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,uint,string)", p0, p1, p2)); + } + + function log(uint p0, uint p1, bool p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,uint,bool)", p0, p1, p2)); + } + + function log(uint p0, uint p1, address p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,uint,address)", p0, p1, p2)); + } + + function log(uint p0, string memory p1, uint p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,string,uint)", p0, p1, p2)); + } + + function log(uint p0, string memory p1, string memory p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,string,string)", p0, p1, p2)); + } + + function log(uint p0, string memory p1, bool p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,string,bool)", p0, p1, p2)); + } + + function log(uint p0, string memory p1, address p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,string,address)", p0, p1, p2)); + } + + function log(uint p0, bool p1, uint p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,bool,uint)", p0, p1, p2)); + } + + function log(uint p0, bool p1, string memory p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,bool,string)", p0, p1, p2)); + } + + function log(uint p0, bool p1, bool p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,bool,bool)", p0, p1, p2)); + } + + function log(uint p0, bool p1, address p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,bool,address)", p0, p1, p2)); + } + + function log(uint p0, address p1, uint p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,address,uint)", p0, p1, p2)); + } + + function log(uint p0, address p1, string memory p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,address,string)", p0, p1, p2)); + } + + function log(uint p0, address p1, bool p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,address,bool)", p0, p1, p2)); + } + + function log(uint p0, address p1, address p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,address,address)", p0, p1, p2)); + } + + function log(string memory p0, uint p1, uint p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint,uint)", p0, p1, p2)); + } + + function log(string memory p0, uint p1, string memory p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint,string)", p0, p1, p2)); + } + + function log(string memory p0, uint p1, bool p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint,bool)", p0, p1, p2)); + } + + function log(string memory p0, uint p1, address p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint,address)", p0, p1, p2)); + } + + function log(string memory p0, string memory p1, uint p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,uint)", p0, p1, p2)); + } + + function log(string memory p0, string memory p1, string memory p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,string)", p0, p1, p2)); + } + + function log(string memory p0, string memory p1, bool p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,bool)", p0, p1, p2)); + } + + function log(string memory p0, string memory p1, address p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,address)", p0, p1, p2)); + } + + function log(string memory p0, bool p1, uint p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint)", p0, p1, p2)); + } + + function log(string memory p0, bool p1, string memory p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,string)", p0, p1, p2)); + } + + function log(string memory p0, bool p1, bool p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool)", p0, p1, p2)); + } + + function log(string memory p0, bool p1, address p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,address)", p0, p1, p2)); + } + + function log(string memory p0, address p1, uint p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,uint)", p0, p1, p2)); + } + + function log(string memory p0, address p1, string memory p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,string)", p0, p1, p2)); + } + + function log(string memory p0, address p1, bool p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,bool)", p0, p1, p2)); + } + + function log(string memory p0, address p1, address p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,address)", p0, p1, p2)); + } + + function log(bool p0, uint p1, uint p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint,uint)", p0, p1, p2)); + } + + function log(bool p0, uint p1, string memory p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint,string)", p0, p1, p2)); + } + + function log(bool p0, uint p1, bool p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint,bool)", p0, p1, p2)); + } + + function log(bool p0, uint p1, address p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint,address)", p0, p1, p2)); + } + + function log(bool p0, string memory p1, uint p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint)", p0, p1, p2)); + } + + function log(bool p0, string memory p1, string memory p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,string)", p0, p1, p2)); + } + + function log(bool p0, string memory p1, bool p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool)", p0, p1, p2)); + } + + function log(bool p0, string memory p1, address p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,address)", p0, p1, p2)); + } + + function log(bool p0, bool p1, uint p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint)", p0, p1, p2)); + } + + function log(bool p0, bool p1, string memory p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string)", p0, p1, p2)); + } + + function log(bool p0, bool p1, bool p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool)", p0, p1, p2)); + } + + function log(bool p0, bool p1, address p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address)", p0, p1, p2)); + } + + function log(bool p0, address p1, uint p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint)", p0, p1, p2)); + } + + function log(bool p0, address p1, string memory p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,string)", p0, p1, p2)); + } + + function log(bool p0, address p1, bool p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool)", p0, p1, p2)); + } + + function log(bool p0, address p1, address p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,address)", p0, p1, p2)); + } + + function log(address p0, uint p1, uint p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint,uint)", p0, p1, p2)); + } + + function log(address p0, uint p1, string memory p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint,string)", p0, p1, p2)); + } + + function log(address p0, uint p1, bool p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint,bool)", p0, p1, p2)); + } + + function log(address p0, uint p1, address p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint,address)", p0, p1, p2)); + } + + function log(address p0, string memory p1, uint p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,uint)", p0, p1, p2)); + } + + function log(address p0, string memory p1, string memory p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,string)", p0, p1, p2)); + } + + function log(address p0, string memory p1, bool p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,bool)", p0, p1, p2)); + } + + function log(address p0, string memory p1, address p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,address)", p0, p1, p2)); + } + + function log(address p0, bool p1, uint p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint)", p0, p1, p2)); + } + + function log(address p0, bool p1, string memory p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,string)", p0, p1, p2)); + } + + function log(address p0, bool p1, bool p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool)", p0, p1, p2)); + } + + function log(address p0, bool p1, address p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,address)", p0, p1, p2)); + } + + function log(address p0, address p1, uint p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,uint)", p0, p1, p2)); + } + + function log(address p0, address p1, string memory p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,string)", p0, p1, p2)); + } + + function log(address p0, address p1, bool p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,bool)", p0, p1, p2)); + } + + function log(address p0, address p1, address p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,address)", p0, p1, p2)); + } + + function log(uint p0, uint p1, uint p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,uint,uint,uint)", p0, p1, p2, p3)); + } + + function log(uint p0, uint p1, uint p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,uint,uint,string)", p0, p1, p2, p3)); + } + + function log(uint p0, uint p1, uint p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,uint,uint,bool)", p0, p1, p2, p3)); + } + + function log(uint p0, uint p1, uint p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,uint,uint,address)", p0, p1, p2, p3)); + } + + function log(uint p0, uint p1, string memory p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,uint,string,uint)", p0, p1, p2, p3)); + } + + function log(uint p0, uint p1, string memory p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,uint,string,string)", p0, p1, p2, p3)); + } + + function log(uint p0, uint p1, string memory p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,uint,string,bool)", p0, p1, p2, p3)); + } + + function log(uint p0, uint p1, string memory p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,uint,string,address)", p0, p1, p2, p3)); + } + + function log(uint p0, uint p1, bool p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,uint,bool,uint)", p0, p1, p2, p3)); + } + + function log(uint p0, uint p1, bool p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,uint,bool,string)", p0, p1, p2, p3)); + } + + function log(uint p0, uint p1, bool p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,uint,bool,bool)", p0, p1, p2, p3)); + } + + function log(uint p0, uint p1, bool p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,uint,bool,address)", p0, p1, p2, p3)); + } + + function log(uint p0, uint p1, address p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,uint,address,uint)", p0, p1, p2, p3)); + } + + function log(uint p0, uint p1, address p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,uint,address,string)", p0, p1, p2, p3)); + } + + function log(uint p0, uint p1, address p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,uint,address,bool)", p0, p1, p2, p3)); + } + + function log(uint p0, uint p1, address p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,uint,address,address)", p0, p1, p2, p3)); + } + + function log(uint p0, string memory p1, uint p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,string,uint,uint)", p0, p1, p2, p3)); + } + + function log(uint p0, string memory p1, uint p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,string,uint,string)", p0, p1, p2, p3)); + } + + function log(uint p0, string memory p1, uint p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,string,uint,bool)", p0, p1, p2, p3)); + } + + function log(uint p0, string memory p1, uint p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,string,uint,address)", p0, p1, p2, p3)); + } + + function log(uint p0, string memory p1, string memory p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,string,string,uint)", p0, p1, p2, p3)); + } + + function log(uint p0, string memory p1, string memory p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,string,string,string)", p0, p1, p2, p3)); + } + + function log(uint p0, string memory p1, string memory p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,string,string,bool)", p0, p1, p2, p3)); + } + + function log(uint p0, string memory p1, string memory p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,string,string,address)", p0, p1, p2, p3)); + } + + function log(uint p0, string memory p1, bool p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,string,bool,uint)", p0, p1, p2, p3)); + } + + function log(uint p0, string memory p1, bool p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,string,bool,string)", p0, p1, p2, p3)); + } + + function log(uint p0, string memory p1, bool p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,string,bool,bool)", p0, p1, p2, p3)); + } + + function log(uint p0, string memory p1, bool p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,string,bool,address)", p0, p1, p2, p3)); + } + + function log(uint p0, string memory p1, address p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,string,address,uint)", p0, p1, p2, p3)); + } + + function log(uint p0, string memory p1, address p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,string,address,string)", p0, p1, p2, p3)); + } + + function log(uint p0, string memory p1, address p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,string,address,bool)", p0, p1, p2, p3)); + } + + function log(uint p0, string memory p1, address p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,string,address,address)", p0, p1, p2, p3)); + } + + function log(uint p0, bool p1, uint p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,bool,uint,uint)", p0, p1, p2, p3)); + } + + function log(uint p0, bool p1, uint p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,bool,uint,string)", p0, p1, p2, p3)); + } + + function log(uint p0, bool p1, uint p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,bool,uint,bool)", p0, p1, p2, p3)); + } + + function log(uint p0, bool p1, uint p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,bool,uint,address)", p0, p1, p2, p3)); + } + + function log(uint p0, bool p1, string memory p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,bool,string,uint)", p0, p1, p2, p3)); + } + + function log(uint p0, bool p1, string memory p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,bool,string,string)", p0, p1, p2, p3)); + } + + function log(uint p0, bool p1, string memory p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,bool,string,bool)", p0, p1, p2, p3)); + } + + function log(uint p0, bool p1, string memory p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,bool,string,address)", p0, p1, p2, p3)); + } + + function log(uint p0, bool p1, bool p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,bool,bool,uint)", p0, p1, p2, p3)); + } + + function log(uint p0, bool p1, bool p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,bool,bool,string)", p0, p1, p2, p3)); + } + + function log(uint p0, bool p1, bool p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,bool,bool,bool)", p0, p1, p2, p3)); + } + + function log(uint p0, bool p1, bool p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,bool,bool,address)", p0, p1, p2, p3)); + } + + function log(uint p0, bool p1, address p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,bool,address,uint)", p0, p1, p2, p3)); + } + + function log(uint p0, bool p1, address p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,bool,address,string)", p0, p1, p2, p3)); + } + + function log(uint p0, bool p1, address p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,bool,address,bool)", p0, p1, p2, p3)); + } + + function log(uint p0, bool p1, address p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,bool,address,address)", p0, p1, p2, p3)); + } + + function log(uint p0, address p1, uint p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,address,uint,uint)", p0, p1, p2, p3)); + } + + function log(uint p0, address p1, uint p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,address,uint,string)", p0, p1, p2, p3)); + } + + function log(uint p0, address p1, uint p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,address,uint,bool)", p0, p1, p2, p3)); + } + + function log(uint p0, address p1, uint p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,address,uint,address)", p0, p1, p2, p3)); + } + + function log(uint p0, address p1, string memory p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,address,string,uint)", p0, p1, p2, p3)); + } + + function log(uint p0, address p1, string memory p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,address,string,string)", p0, p1, p2, p3)); + } + + function log(uint p0, address p1, string memory p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,address,string,bool)", p0, p1, p2, p3)); + } + + function log(uint p0, address p1, string memory p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,address,string,address)", p0, p1, p2, p3)); + } + + function log(uint p0, address p1, bool p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,address,bool,uint)", p0, p1, p2, p3)); + } + + function log(uint p0, address p1, bool p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,address,bool,string)", p0, p1, p2, p3)); + } + + function log(uint p0, address p1, bool p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,address,bool,bool)", p0, p1, p2, p3)); + } + + function log(uint p0, address p1, bool p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,address,bool,address)", p0, p1, p2, p3)); + } + + function log(uint p0, address p1, address p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,address,address,uint)", p0, p1, p2, p3)); + } + + function log(uint p0, address p1, address p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,address,address,string)", p0, p1, p2, p3)); + } + + function log(uint p0, address p1, address p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,address,address,bool)", p0, p1, p2, p3)); + } + + function log(uint p0, address p1, address p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,address,address,address)", p0, p1, p2, p3)); + } + + function log(string memory p0, uint p1, uint p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint,uint,uint)", p0, p1, p2, p3)); + } + + function log(string memory p0, uint p1, uint p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint,uint,string)", p0, p1, p2, p3)); + } + + function log(string memory p0, uint p1, uint p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint,uint,bool)", p0, p1, p2, p3)); + } + + function log(string memory p0, uint p1, uint p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint,uint,address)", p0, p1, p2, p3)); + } + + function log(string memory p0, uint p1, string memory p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint,string,uint)", p0, p1, p2, p3)); + } + + function log(string memory p0, uint p1, string memory p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint,string,string)", p0, p1, p2, p3)); + } + + function log(string memory p0, uint p1, string memory p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint,string,bool)", p0, p1, p2, p3)); + } + + function log(string memory p0, uint p1, string memory p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint,string,address)", p0, p1, p2, p3)); + } + + function log(string memory p0, uint p1, bool p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint,bool,uint)", p0, p1, p2, p3)); + } + + function log(string memory p0, uint p1, bool p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint,bool,string)", p0, p1, p2, p3)); + } + + function log(string memory p0, uint p1, bool p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint,bool,bool)", p0, p1, p2, p3)); + } + + function log(string memory p0, uint p1, bool p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint,bool,address)", p0, p1, p2, p3)); + } + + function log(string memory p0, uint p1, address p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint,address,uint)", p0, p1, p2, p3)); + } + + function log(string memory p0, uint p1, address p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint,address,string)", p0, p1, p2, p3)); + } + + function log(string memory p0, uint p1, address p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint,address,bool)", p0, p1, p2, p3)); + } + + function log(string memory p0, uint p1, address p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint,address,address)", p0, p1, p2, p3)); + } + + function log(string memory p0, string memory p1, uint p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,uint,uint)", p0, p1, p2, p3)); + } + + function log(string memory p0, string memory p1, uint p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,uint,string)", p0, p1, p2, p3)); + } + + function log(string memory p0, string memory p1, uint p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,uint,bool)", p0, p1, p2, p3)); + } + + function log(string memory p0, string memory p1, uint p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,uint,address)", p0, p1, p2, p3)); + } + + function log(string memory p0, string memory p1, string memory p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,string,uint)", p0, p1, p2, p3)); + } + + function log(string memory p0, string memory p1, string memory p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,string,string)", p0, p1, p2, p3)); + } + + function log(string memory p0, string memory p1, string memory p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,string,bool)", p0, p1, p2, p3)); + } + + function log(string memory p0, string memory p1, string memory p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,string,address)", p0, p1, p2, p3)); + } + + function log(string memory p0, string memory p1, bool p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,bool,uint)", p0, p1, p2, p3)); + } + + function log(string memory p0, string memory p1, bool p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,bool,string)", p0, p1, p2, p3)); + } + + function log(string memory p0, string memory p1, bool p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,bool,bool)", p0, p1, p2, p3)); + } + + function log(string memory p0, string memory p1, bool p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,bool,address)", p0, p1, p2, p3)); + } + + function log(string memory p0, string memory p1, address p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,address,uint)", p0, p1, p2, p3)); + } + + function log(string memory p0, string memory p1, address p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,address,string)", p0, p1, p2, p3)); + } + + function log(string memory p0, string memory p1, address p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,address,bool)", p0, p1, p2, p3)); + } + + function log(string memory p0, string memory p1, address p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,address,address)", p0, p1, p2, p3)); + } + + function log(string memory p0, bool p1, uint p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint,uint)", p0, p1, p2, p3)); + } + + function log(string memory p0, bool p1, uint p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint,string)", p0, p1, p2, p3)); + } + + function log(string memory p0, bool p1, uint p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint,bool)", p0, p1, p2, p3)); + } + + function log(string memory p0, bool p1, uint p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint,address)", p0, p1, p2, p3)); + } + + function log(string memory p0, bool p1, string memory p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,string,uint)", p0, p1, p2, p3)); + } + + function log(string memory p0, bool p1, string memory p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,string,string)", p0, p1, p2, p3)); + } + + function log(string memory p0, bool p1, string memory p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,string,bool)", p0, p1, p2, p3)); + } + + function log(string memory p0, bool p1, string memory p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,string,address)", p0, p1, p2, p3)); + } + + function log(string memory p0, bool p1, bool p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,uint)", p0, p1, p2, p3)); + } + + function log(string memory p0, bool p1, bool p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,string)", p0, p1, p2, p3)); + } + + function log(string memory p0, bool p1, bool p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,bool)", p0, p1, p2, p3)); + } + + function log(string memory p0, bool p1, bool p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,address)", p0, p1, p2, p3)); + } + + function log(string memory p0, bool p1, address p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,address,uint)", p0, p1, p2, p3)); + } + + function log(string memory p0, bool p1, address p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,address,string)", p0, p1, p2, p3)); + } + + function log(string memory p0, bool p1, address p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,address,bool)", p0, p1, p2, p3)); + } + + function log(string memory p0, bool p1, address p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,address,address)", p0, p1, p2, p3)); + } + + function log(string memory p0, address p1, uint p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,uint,uint)", p0, p1, p2, p3)); + } + + function log(string memory p0, address p1, uint p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,uint,string)", p0, p1, p2, p3)); + } + + function log(string memory p0, address p1, uint p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,uint,bool)", p0, p1, p2, p3)); + } + + function log(string memory p0, address p1, uint p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,uint,address)", p0, p1, p2, p3)); + } + + function log(string memory p0, address p1, string memory p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,string,uint)", p0, p1, p2, p3)); + } + + function log(string memory p0, address p1, string memory p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,string,string)", p0, p1, p2, p3)); + } + + function log(string memory p0, address p1, string memory p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,string,bool)", p0, p1, p2, p3)); + } + + function log(string memory p0, address p1, string memory p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,string,address)", p0, p1, p2, p3)); + } + + function log(string memory p0, address p1, bool p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,bool,uint)", p0, p1, p2, p3)); + } + + function log(string memory p0, address p1, bool p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,bool,string)", p0, p1, p2, p3)); + } + + function log(string memory p0, address p1, bool p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,bool,bool)", p0, p1, p2, p3)); + } + + function log(string memory p0, address p1, bool p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,bool,address)", p0, p1, p2, p3)); + } + + function log(string memory p0, address p1, address p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,address,uint)", p0, p1, p2, p3)); + } + + function log(string memory p0, address p1, address p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,address,string)", p0, p1, p2, p3)); + } + + function log(string memory p0, address p1, address p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,address,bool)", p0, p1, p2, p3)); + } + + function log(string memory p0, address p1, address p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,address,address)", p0, p1, p2, p3)); + } + + function log(bool p0, uint p1, uint p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint,uint,uint)", p0, p1, p2, p3)); + } + + function log(bool p0, uint p1, uint p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint,uint,string)", p0, p1, p2, p3)); + } + + function log(bool p0, uint p1, uint p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint,uint,bool)", p0, p1, p2, p3)); + } + + function log(bool p0, uint p1, uint p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint,uint,address)", p0, p1, p2, p3)); + } + + function log(bool p0, uint p1, string memory p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint,string,uint)", p0, p1, p2, p3)); + } + + function log(bool p0, uint p1, string memory p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint,string,string)", p0, p1, p2, p3)); + } + + function log(bool p0, uint p1, string memory p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint,string,bool)", p0, p1, p2, p3)); + } + + function log(bool p0, uint p1, string memory p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint,string,address)", p0, p1, p2, p3)); + } + + function log(bool p0, uint p1, bool p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint,bool,uint)", p0, p1, p2, p3)); + } + + function log(bool p0, uint p1, bool p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint,bool,string)", p0, p1, p2, p3)); + } + + function log(bool p0, uint p1, bool p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint,bool,bool)", p0, p1, p2, p3)); + } + + function log(bool p0, uint p1, bool p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint,bool,address)", p0, p1, p2, p3)); + } + + function log(bool p0, uint p1, address p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint,address,uint)", p0, p1, p2, p3)); + } + + function log(bool p0, uint p1, address p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint,address,string)", p0, p1, p2, p3)); + } + + function log(bool p0, uint p1, address p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint,address,bool)", p0, p1, p2, p3)); + } + + function log(bool p0, uint p1, address p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint,address,address)", p0, p1, p2, p3)); + } + + function log(bool p0, string memory p1, uint p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint,uint)", p0, p1, p2, p3)); + } + + function log(bool p0, string memory p1, uint p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint,string)", p0, p1, p2, p3)); + } + + function log(bool p0, string memory p1, uint p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint,bool)", p0, p1, p2, p3)); + } + + function log(bool p0, string memory p1, uint p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint,address)", p0, p1, p2, p3)); + } + + function log(bool p0, string memory p1, string memory p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,string,uint)", p0, p1, p2, p3)); + } + + function log(bool p0, string memory p1, string memory p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,string,string)", p0, p1, p2, p3)); + } + + function log(bool p0, string memory p1, string memory p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,string,bool)", p0, p1, p2, p3)); + } + + function log(bool p0, string memory p1, string memory p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,string,address)", p0, p1, p2, p3)); + } + + function log(bool p0, string memory p1, bool p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,uint)", p0, p1, p2, p3)); + } + + function log(bool p0, string memory p1, bool p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,string)", p0, p1, p2, p3)); + } + + function log(bool p0, string memory p1, bool p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,bool)", p0, p1, p2, p3)); + } + + function log(bool p0, string memory p1, bool p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,address)", p0, p1, p2, p3)); + } + + function log(bool p0, string memory p1, address p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,address,uint)", p0, p1, p2, p3)); + } + + function log(bool p0, string memory p1, address p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,address,string)", p0, p1, p2, p3)); + } + + function log(bool p0, string memory p1, address p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,address,bool)", p0, p1, p2, p3)); + } + + function log(bool p0, string memory p1, address p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,address,address)", p0, p1, p2, p3)); + } + + function log(bool p0, bool p1, uint p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint,uint)", p0, p1, p2, p3)); + } + + function log(bool p0, bool p1, uint p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint,string)", p0, p1, p2, p3)); + } + + function log(bool p0, bool p1, uint p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint,bool)", p0, p1, p2, p3)); + } + + function log(bool p0, bool p1, uint p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint,address)", p0, p1, p2, p3)); + } + + function log(bool p0, bool p1, string memory p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,uint)", p0, p1, p2, p3)); + } + + function log(bool p0, bool p1, string memory p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,string)", p0, p1, p2, p3)); + } + + function log(bool p0, bool p1, string memory p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,bool)", p0, p1, p2, p3)); + } + + function log(bool p0, bool p1, string memory p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,address)", p0, p1, p2, p3)); + } + + function log(bool p0, bool p1, bool p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,uint)", p0, p1, p2, p3)); + } + + function log(bool p0, bool p1, bool p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,string)", p0, p1, p2, p3)); + } + + function log(bool p0, bool p1, bool p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,bool)", p0, p1, p2, p3)); + } + + function log(bool p0, bool p1, bool p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,address)", p0, p1, p2, p3)); + } + + function log(bool p0, bool p1, address p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,uint)", p0, p1, p2, p3)); + } + + function log(bool p0, bool p1, address p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,string)", p0, p1, p2, p3)); + } + + function log(bool p0, bool p1, address p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,bool)", p0, p1, p2, p3)); + } + + function log(bool p0, bool p1, address p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,address)", p0, p1, p2, p3)); + } + + function log(bool p0, address p1, uint p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint,uint)", p0, p1, p2, p3)); + } + + function log(bool p0, address p1, uint p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint,string)", p0, p1, p2, p3)); + } + + function log(bool p0, address p1, uint p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint,bool)", p0, p1, p2, p3)); + } + + function log(bool p0, address p1, uint p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint,address)", p0, p1, p2, p3)); + } + + function log(bool p0, address p1, string memory p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,string,uint)", p0, p1, p2, p3)); + } + + function log(bool p0, address p1, string memory p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,string,string)", p0, p1, p2, p3)); + } + + function log(bool p0, address p1, string memory p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,string,bool)", p0, p1, p2, p3)); + } + + function log(bool p0, address p1, string memory p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,string,address)", p0, p1, p2, p3)); + } + + function log(bool p0, address p1, bool p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,uint)", p0, p1, p2, p3)); + } + + function log(bool p0, address p1, bool p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,string)", p0, p1, p2, p3)); + } + + function log(bool p0, address p1, bool p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,bool)", p0, p1, p2, p3)); + } + + function log(bool p0, address p1, bool p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,address)", p0, p1, p2, p3)); + } + + function log(bool p0, address p1, address p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,address,uint)", p0, p1, p2, p3)); + } + + function log(bool p0, address p1, address p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,address,string)", p0, p1, p2, p3)); + } + + function log(bool p0, address p1, address p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,address,bool)", p0, p1, p2, p3)); + } + + function log(bool p0, address p1, address p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,address,address)", p0, p1, p2, p3)); + } + + function log(address p0, uint p1, uint p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint,uint,uint)", p0, p1, p2, p3)); + } + + function log(address p0, uint p1, uint p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint,uint,string)", p0, p1, p2, p3)); + } + + function log(address p0, uint p1, uint p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint,uint,bool)", p0, p1, p2, p3)); + } + + function log(address p0, uint p1, uint p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint,uint,address)", p0, p1, p2, p3)); + } + + function log(address p0, uint p1, string memory p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint,string,uint)", p0, p1, p2, p3)); + } + + function log(address p0, uint p1, string memory p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint,string,string)", p0, p1, p2, p3)); + } + + function log(address p0, uint p1, string memory p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint,string,bool)", p0, p1, p2, p3)); + } + + function log(address p0, uint p1, string memory p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint,string,address)", p0, p1, p2, p3)); + } + + function log(address p0, uint p1, bool p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint,bool,uint)", p0, p1, p2, p3)); + } + + function log(address p0, uint p1, bool p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint,bool,string)", p0, p1, p2, p3)); + } + + function log(address p0, uint p1, bool p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint,bool,bool)", p0, p1, p2, p3)); + } + + function log(address p0, uint p1, bool p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint,bool,address)", p0, p1, p2, p3)); + } + + function log(address p0, uint p1, address p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint,address,uint)", p0, p1, p2, p3)); + } + + function log(address p0, uint p1, address p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint,address,string)", p0, p1, p2, p3)); + } + + function log(address p0, uint p1, address p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint,address,bool)", p0, p1, p2, p3)); + } + + function log(address p0, uint p1, address p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint,address,address)", p0, p1, p2, p3)); + } + + function log(address p0, string memory p1, uint p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,uint,uint)", p0, p1, p2, p3)); + } + + function log(address p0, string memory p1, uint p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,uint,string)", p0, p1, p2, p3)); + } + + function log(address p0, string memory p1, uint p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,uint,bool)", p0, p1, p2, p3)); + } + + function log(address p0, string memory p1, uint p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,uint,address)", p0, p1, p2, p3)); + } + + function log(address p0, string memory p1, string memory p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,string,uint)", p0, p1, p2, p3)); + } + + function log(address p0, string memory p1, string memory p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,string,string)", p0, p1, p2, p3)); + } + + function log(address p0, string memory p1, string memory p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,string,bool)", p0, p1, p2, p3)); + } + + function log(address p0, string memory p1, string memory p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,string,address)", p0, p1, p2, p3)); + } + + function log(address p0, string memory p1, bool p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,bool,uint)", p0, p1, p2, p3)); + } + + function log(address p0, string memory p1, bool p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,bool,string)", p0, p1, p2, p3)); + } + + function log(address p0, string memory p1, bool p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,bool,bool)", p0, p1, p2, p3)); + } + + function log(address p0, string memory p1, bool p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,bool,address)", p0, p1, p2, p3)); + } + + function log(address p0, string memory p1, address p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,address,uint)", p0, p1, p2, p3)); + } + + function log(address p0, string memory p1, address p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,address,string)", p0, p1, p2, p3)); + } + + function log(address p0, string memory p1, address p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,address,bool)", p0, p1, p2, p3)); + } + + function log(address p0, string memory p1, address p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,address,address)", p0, p1, p2, p3)); + } + + function log(address p0, bool p1, uint p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint,uint)", p0, p1, p2, p3)); + } + + function log(address p0, bool p1, uint p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint,string)", p0, p1, p2, p3)); + } + + function log(address p0, bool p1, uint p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint,bool)", p0, p1, p2, p3)); + } + + function log(address p0, bool p1, uint p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint,address)", p0, p1, p2, p3)); + } + + function log(address p0, bool p1, string memory p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,string,uint)", p0, p1, p2, p3)); + } + + function log(address p0, bool p1, string memory p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,string,string)", p0, p1, p2, p3)); + } + + function log(address p0, bool p1, string memory p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,string,bool)", p0, p1, p2, p3)); + } + + function log(address p0, bool p1, string memory p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,string,address)", p0, p1, p2, p3)); + } + + function log(address p0, bool p1, bool p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,uint)", p0, p1, p2, p3)); + } + + function log(address p0, bool p1, bool p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,string)", p0, p1, p2, p3)); + } + + function log(address p0, bool p1, bool p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,bool)", p0, p1, p2, p3)); + } + + function log(address p0, bool p1, bool p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,address)", p0, p1, p2, p3)); + } + + function log(address p0, bool p1, address p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,address,uint)", p0, p1, p2, p3)); + } + + function log(address p0, bool p1, address p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,address,string)", p0, p1, p2, p3)); + } + + function log(address p0, bool p1, address p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,address,bool)", p0, p1, p2, p3)); + } + + function log(address p0, bool p1, address p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,address,address)", p0, p1, p2, p3)); + } + + function log(address p0, address p1, uint p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,uint,uint)", p0, p1, p2, p3)); + } + + function log(address p0, address p1, uint p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,uint,string)", p0, p1, p2, p3)); + } + + function log(address p0, address p1, uint p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,uint,bool)", p0, p1, p2, p3)); + } + + function log(address p0, address p1, uint p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,uint,address)", p0, p1, p2, p3)); + } + + function log(address p0, address p1, string memory p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,string,uint)", p0, p1, p2, p3)); + } + + function log(address p0, address p1, string memory p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,string,string)", p0, p1, p2, p3)); + } + + function log(address p0, address p1, string memory p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,string,bool)", p0, p1, p2, p3)); + } + + function log(address p0, address p1, string memory p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,string,address)", p0, p1, p2, p3)); + } + + function log(address p0, address p1, bool p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,bool,uint)", p0, p1, p2, p3)); + } + + function log(address p0, address p1, bool p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,bool,string)", p0, p1, p2, p3)); + } + + function log(address p0, address p1, bool p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,bool,bool)", p0, p1, p2, p3)); + } + + function log(address p0, address p1, bool p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,bool,address)", p0, p1, p2, p3)); + } + + function log(address p0, address p1, address p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,address,uint)", p0, p1, p2, p3)); + } + + function log(address p0, address p1, address p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,address,string)", p0, p1, p2, p3)); + } + + function log(address p0, address p1, address p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,address,bool)", p0, p1, p2, p3)); + } + + function log(address p0, address p1, address p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,address,address)", p0, p1, p2, p3)); + } + +} From b1fca810f06e017a4d0210978f4ba5d7c6271238 Mon Sep 17 00:00:00 2001 From: Enrique Ortiz Date: Wed, 4 Oct 2023 15:09:20 +0900 Subject: [PATCH 7/8] chore: do not run anvil --- .github/workflows/ci.yml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b529dd4a..edf2ebb7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,10 +24,6 @@ jobs: steps: - uses: actions/checkout@v3 - uses: dtolnay/rust-toolchain@stable - - name: Install Anvil - uses: foundry-rs/foundry-toolchain@v1 - with: - version: nightly - name: Install test binaries shell: bash run: ./.github/scripts/install_test_binaries.sh @@ -39,6 +35,7 @@ jobs: run: | cargo nextest run \ ${{ matrix.flags.flags }} \ + -E "!(deps(ethers-etherscan) & kind(test))" \ --retries 2 feature-checks: From 79efa667d9612350c7d3372e940736c52854998a Mon Sep 17 00:00:00 2001 From: Enrique Ortiz Date: Wed, 4 Oct 2023 15:11:41 +0900 Subject: [PATCH 8/8] chore: ignore it tests --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index edf2ebb7..9533aa3a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -35,7 +35,7 @@ jobs: run: | cargo nextest run \ ${{ matrix.flags.flags }} \ - -E "!(deps(ethers-etherscan) & kind(test))" \ + -E "!(kind(test))" \ --retries 2 feature-checks: