diff --git a/docs/changelog.md b/docs/changelog.md
index 6bb2bc4be..c13da62c0 100644
--- a/docs/changelog.md
+++ b/docs/changelog.md
@@ -6,6 +6,13 @@ small.label {
}
+## Unreleased
+
+Features & improvements:
+
+- added support for Solidity 0.8.35, including the `erc7201` builtin, `ast-id` debug annotations, the experimental `@future` EVM and SSA CFG pipeline, `yulCFGJson`, and Ethdebug standard JSON interfaces [core]
+- fixed compiler checksum verification when a stable release and prerelease share a version number [core]
+
## 4.22.1 (Mar 2, 2026) { id="4.22.1" }
Fixes & improvements:
diff --git a/docs/compilation.md b/docs/compilation.md
index 195878b91..93cd21549 100644
--- a/docs/compilation.md
+++ b/docs/compilation.md
@@ -73,8 +73,8 @@ exclude_paths = ["node_modules", "venv", ".venv", "lib", "script", "test"]
## Via IR
-The compiler can can generate bytecode by converting the sources to Yul first (`Solidity -> Yul -> EVM bytecode`) instead of the traditional `Solidity -> EVM bytecode` approach.
-See the [Solidity documentation]() for more information.
+The compiler can generate bytecode by converting the sources to Yul first (`Solidity -> Yul -> EVM bytecode`) instead of the traditional `Solidity -> EVM bytecode` approach.
+See the [Solidity documentation](https://docs.soliditylang.org/en/latest/ir-breaking-changes.html) for more information.
By default, the `via_IR` config option is left unset, which leaves the decision to the compiler.
It can be enabled by setting the option to `true`:
@@ -86,9 +86,32 @@ via_IR = true
!!! note "`Stack too deep` errors"
One way to avoid `Stack too deep` errors is to enable `via_IR` and the optimizer.
+## Solidity 0.8.35 experimental features
+
+Solidity 0.8.35 introduced an explicit experimental mode, the experimental `@future` EVM target, and an experimental SSA CFG code generation pipeline. They can be enabled using:
+
+```toml title="wake.toml"
+[compiler.solc]
+target_version = "0.8.35"
+experimental = true
+evm_version = "@future"
+via_SSA_CFG = true
+```
+
+`via_SSA_CFG` implies `via_IR`. Both `via_SSA_CFG` and `@future` require experimental mode. Experimental compiler features do not provide the same backwards-compatibility guarantees as stable Solidity features.
+
+Wake also supports the following Solidity 0.8.35 standard JSON additions through `wake.compiler.solc_frontend`:
+
+- `settings.debug.debugInfo` accepts `ast-id` annotations and experimental `ethdebug` annotations;
+- `yulCFGJson` exposes the control-flow graph of the SSA-form Yul code;
+- `evm.bytecode.ethdebug` and `evm.deployedBytecode.ethdebug` expose Ethdebug programs for creation and deployed bytecode;
+- `ethdebug.resources` and `ethdebug.compilation` expose global Ethdebug resources and compilation information.
+
+Ethdebug is experimental, and its bytecode-level outputs can only be requested when compiling via IR. Requesting an Ethdebug output automatically enables the `ethdebug` debug annotation. Wake's IR also recognizes the Solidity 0.8.35 `erc7201` builtin used in storage layout expressions.
+
## Optimizer
-Wake allows setting all optimizer options supported by the Solidity compiler (see the [Solidity documentation](https://docs.soliditylang.org/en/v0.8.22/using-the-compiler.html#input-description)).
+Wake allows setting all optimizer options supported by the Solidity compiler (see the [Solidity documentation](https://docs.soliditylang.org/en/latest/using-the-compiler.html#input-description)).
By default, Wake leaves the `enabled` option unset, which leaves the decision to the compiler.
It can be enabled by setting the option to `true`:
```toml title="wake.toml"
diff --git a/docs/configuration.md b/docs/configuration.md
index 7b76e3157..63714707d 100644
--- a/docs/configuration.md
+++ b/docs/configuration.md
@@ -22,11 +22,13 @@ Wake can be configured using configuration options loaded from multiple sources
[compiler.solc]
allow_paths = []
# evm_version (unset - let the compiler decide)
+ # experimental (unset - disabled by the compiler)
exclude_paths = ["node_modules", "venv", ".venv", "lib", "script", "test"]
include_paths = ["node_modules"]
remappings = []
# target_version (unset - use the latest version)
# via_IR (unset - let the compiler decide)
+ # via_SSA_CFG (unset - let the compiler decide)
[compiler.solc.optimizer]
# enabled (unset - let the compiler decide)
@@ -166,12 +168,14 @@ Additionally, detectors and printers may use this namespace to load needed API k
| Option | Description |
|:------------------------------|:-----------------------------------------------------------------------------------------------------------------------------------------------|
| `allow_paths` | Allow paths passed to the `solc` executable |
-| `evm_version` | EVM version as specified by the [Solidity docs](https://docs.soliditylang.org/en/latest/using-the-compiler.html#target-options) |
+| `evm_version` | EVM version as specified by the [Solidity docs](https://docs.soliditylang.org/en/latest/using-the-compiler.html#target-options); `@future` requires Solidity 0.8.35+ and experimental mode |
+| `experimental` | Enable Solidity experimental mode; supported by Solidity 0.8.35 and newer |
| `exclude_paths` | Files in these paths are not compiled unless imported from other non-excluded files |
| `include_paths` | Paths (along with the current working directory) where files from non-relative imports are searched |
| `remappings` | Compiler remappings as specified by the [Solidity docs](https://docs.soliditylang.org/en/latest/path-resolution.html#import-remapping) |
| `target_version` | Target `solc` version used to compile the project |
| `via_IR` | Compile the code via the Yul intermediate language (see the [Solidity docs](https://docs.soliditylang.org/en/latest/ir-breaking-changes.html)) |
+| `via_SSA_CFG` | Compile through the experimental SSA CFG backend on Solidity 0.8.35+; implies `via_IR` and requires `experimental = true` |
!!! info
The `include_paths` option is the preferred way to handle imports of libraries. Remappings should be used only when `include_paths` cannot be used (e.g. when the import path differs from the system path of the imported file).
diff --git a/docs/wake-schema.json b/docs/wake-schema.json
index 57c7eea11..ba00ba825 100644
--- a/docs/wake-schema.json
+++ b/docs/wake-schema.json
@@ -35,8 +35,12 @@
},
"evm_version": {
"type": "string",
- "description": "Version of the EVM to compile for",
- "enum": ["homestead", "tangerineWhistle", "spuriousDragon", "byzantium", "constantinople", "petersburg", "istanbul", "berlin", "london", "paris", "shanghai", "cancun", "prague", "osaka"]
+ "description": "Version of the EVM to compile for; @future requires Solidity 0.8.35+ and experimental mode",
+ "enum": ["homestead", "tangerineWhistle", "spuriousDragon", "byzantium", "constantinople", "petersburg", "istanbul", "berlin", "london", "paris", "shanghai", "cancun", "prague", "osaka", "@future"]
+ },
+ "experimental": {
+ "type": "boolean",
+ "description": "Enable Solidity compiler experimental mode (Solidity 0.8.35+)"
},
"exclude_paths": {
"type": "array",
@@ -104,6 +108,10 @@
"type": "boolean",
"description": "Use new IR-based compiler pipeline"
},
+ "via_SSA_CFG": {
+ "type": "boolean",
+ "description": "Use the experimental SSA CFG code generation pipeline (Solidity 0.8.35+); implies via_IR and requires experimental mode"
+ },
"metadata": {
"type": "object",
"properties": {
@@ -460,8 +468,12 @@
},
"evm_version": {
"type": "string",
- "description": "Version of the EVM to compile for",
- "enum": ["homestead", "tangerineWhistle", "spuriousDragon", "byzantium", "constantinople", "petersburg", "istanbul", "berlin", "london", "paris", "shanghai", "cancun", "prague", "osaka"]
+ "description": "Version of the EVM to compile for; @future requires Solidity 0.8.35+ and experimental mode",
+ "enum": ["homestead", "tangerineWhistle", "spuriousDragon", "byzantium", "constantinople", "petersburg", "istanbul", "berlin", "london", "paris", "shanghai", "cancun", "prague", "osaka", "@future"]
+ },
+ "experimental": {
+ "type": "boolean",
+ "description": "Enable Solidity compiler experimental mode (Solidity 0.8.35+)"
},
"optimizer": {
"type": "object",
@@ -500,6 +512,10 @@
"type": "boolean",
"description": "Use new IR-based compiler pipeline"
},
+ "via_SSA_CFG": {
+ "type": "boolean",
+ "description": "Use the experimental SSA CFG code generation pipeline (Solidity 0.8.35+); implies via_IR and requires experimental mode"
+ },
"metadata": {
"type": "object",
"properties": {
@@ -515,4 +531,4 @@
}
}
}
-}
\ No newline at end of file
+}
diff --git a/tests/solidity_versions/solidity_0_8_35.sol b/tests/solidity_versions/solidity_0_8_35.sol
new file mode 100644
index 000000000..e54cea9f5
--- /dev/null
+++ b/tests/solidity_versions/solidity_0_8_35.sol
@@ -0,0 +1,12 @@
+// SPDX-License-Identifier: MIT
+pragma solidity 0.8.35;
+
+contract Solidity0835 layout at erc7201("wake.test.solidity-0.8.35") {
+ uint256 public value;
+
+ function assemblyValue() external pure returns (uint256 ret) {
+ assembly {
+ ret := 1
+ }
+ }
+}
diff --git a/tests/test_compilation.py b/tests/test_compilation.py
index 3b1605358..1f1370597 100644
--- a/tests/test_compilation.py
+++ b/tests/test_compilation.py
@@ -12,7 +12,18 @@
from wake.cli.__main__ import main
from wake.compiler import SolcOutputSelectionEnum, SolidityCompiler
+from wake.compiler.solc_frontend import (
+ SolcFrontend,
+ SolcInputDebugInfoSettingsEnum,
+ SolcInputDebugSettings,
+ SolcInputOptimizerSettings,
+ SolcInputSettings,
+)
from wake.config import WakeConfig
+from wake.core.enums import EvmVersionEnum
+from wake.core.solidity_version import SolidityVersion
+from wake.ir import ContractDefinition, FunctionCall, InlineAssembly
+from wake.ir.enums import FunctionTypeKind, GlobalSymbol, InlineAssemblyEvmVersion
from wake.utils import change_cwd
PYTEST_BUILD_PATH = Path.home() / ".tmpwake_rkDv61DDf7"
@@ -202,3 +213,106 @@ def test_compile_axelar(setup_project, config):
},
)
assert cli_result.exit_code == 0
+
+
+@pytest.mark.slow
+@pytest.mark.platform_dependent
+def test_compile_solidity_0_8_35(tmp_path, monkeypatch):
+ monkeypatch.setenv("XDG_CONFIG_HOME", str(tmp_path / "config"))
+ monkeypatch.setenv("XDG_DATA_HOME", str(tmp_path / "data"))
+ source_path = (
+ Path(__file__).parent / "solidity_versions" / "solidity_0_8_35.sol"
+ ).resolve()
+ project_root = Path(__file__).parent.parent.resolve()
+ config = WakeConfig.fromdict(
+ {
+ "compiler": {
+ "solc": {
+ "target_version": "0.8.35",
+ "experimental": True,
+ "evm_version": "@future",
+ "via_SSA_CFG": True,
+ "optimizer": {"enabled": True},
+ }
+ }
+ },
+ project_root_path=project_root,
+ )
+ compiler = SolidityCompiler(config)
+
+ build, errors = asyncio.run(
+ compiler.compile(
+ [source_path],
+ [SolcOutputSelectionEnum.ALL],
+ write_artifacts=False,
+ force_recompile=True,
+ )
+ )
+
+ assert errors == set()
+ source_unit = build.source_units[source_path]
+ inline_assembly = next(
+ node for node in source_unit if isinstance(node, InlineAssembly)
+ )
+ assert inline_assembly.evm_version == InlineAssemblyEvmVersion.FUTURE
+ contract = next(
+ declaration
+ for declaration in source_unit.declarations_iter()
+ if isinstance(declaration, ContractDefinition)
+ )
+ assert contract.storage_layout is not None
+ base_slot_expression = contract.storage_layout.base_slot_expression
+ assert isinstance(base_slot_expression, FunctionCall)
+ assert base_slot_expression.function_called == GlobalSymbol.ERC7201
+ assert base_slot_expression.expression.type.kind == FunctionTypeKind.ERC7201
+
+ frontend = SolcFrontend(config)
+ version = SolidityVersion.fromstring("0.8.35")
+ source = source_path.read_text()
+ ssa_output = asyncio.run(
+ frontend.compile(
+ {},
+ {"C.sol": source},
+ version,
+ SolcInputSettings(
+ experimental=True,
+ evm_version=EvmVersionEnum.FUTURE,
+ via_SSA_CFG=True,
+ optimizer=SolcInputOptimizerSettings(enabled=True),
+ output_selection={"*": {"*": [SolcOutputSelectionEnum.YUL_CFG_JSON]}},
+ ),
+ )
+ )
+ assert ssa_output.contracts["C.sol"]["Solidity0835"].yul_CFG_json is not None
+
+ ethdebug_output = asyncio.run(
+ frontend.compile(
+ {},
+ {"C.sol": source},
+ version,
+ SolcInputSettings(
+ experimental=True,
+ via_IR=True,
+ debug=SolcInputDebugSettings(
+ debug_info=[
+ SolcInputDebugInfoSettingsEnum.AST_ID,
+ SolcInputDebugInfoSettingsEnum.ETHDEBUG,
+ ]
+ ),
+ optimizer=SolcInputOptimizerSettings(enabled=False),
+ output_selection={
+ "*": {
+ "*": [
+ SolcOutputSelectionEnum.EVM_BYTECODE_ETHDEBUG,
+ SolcOutputSelectionEnum.EVM_DEPLOYED_BYTECODE_ETHDEBUG,
+ SolcOutputSelectionEnum.ETHDEBUG_RESOURCES,
+ SolcOutputSelectionEnum.ETHDEBUG_COMPILATION,
+ ]
+ }
+ },
+ ),
+ )
+ )
+ assert ethdebug_output.ethdebug is not None
+ assert ethdebug_output.ethdebug["resources"] is not None
+ assert ethdebug_output.ethdebug["compilation"] is not None
diff --git a/tests/test_config.py b/tests/test_config.py
index fa379144d..2de118906 100644
--- a/tests/test_config.py
+++ b/tests/test_config.py
@@ -4,6 +4,7 @@
import pydantic
import pytest
+from wake.cli.init import write_config
from wake.config import WakeConfig
from wake.config.data_model import SolcRemapping
from wake.core.enums import EvmVersionEnum
@@ -82,6 +83,34 @@ def test_config_from_dict():
assert config.compiler.solc.target_version == SolidityVersion.fromstring("0.8.12")
+def test_config_solidity_0_8_35_experimental(tmp_path):
+ config = WakeConfig.fromdict(
+ {
+ "compiler": {
+ "solc": {
+ "evm_version": "@future",
+ "experimental": True,
+ "target_version": "0.8.35",
+ "via_SSA_CFG": True,
+ }
+ }
+ },
+ project_root_path=tmp_path,
+ )
+
+ assert config.compiler.solc.evm_version == EvmVersionEnum.FUTURE
+ assert config.compiler.solc.experimental is True
+ assert config.compiler.solc.target_version == SolidityVersion.fromstring("0.8.35")
+ assert config.compiler.solc.via_SSA_CFG is True
+
+ write_config(config)
+ written_config = config.local_config_path.read_text()
+ assert 'evm_version = "@future"' in written_config
+ assert "experimental = true" in written_config
+ assert 'target_version = "0.8.35"' in written_config
+ assert "via_SSA_CFG = true" in written_config
+
+
@pytest.mark.platform_dependent
def test_config_global():
os.environ["XDG_CONFIG_HOME"] = str(sources_path / "containing_global_conf")
diff --git a/tests/test_solc_frontend.py b/tests/test_solc_frontend.py
new file mode 100644
index 000000000..14e4b73ca
--- /dev/null
+++ b/tests/test_solc_frontend.py
@@ -0,0 +1,176 @@
+import asyncio
+import json
+
+import pytest
+
+from wake.compiler import SolcOutputSelectionEnum, SolidityCompiler
+from wake.compiler.solc_frontend import (
+ SolcFrontend,
+ SolcInput,
+ SolcInputDebugInfoSettingsEnum,
+ SolcInputSettings,
+ SolcOutput,
+)
+from wake.config import WakeConfig
+from wake.core.enums import EvmVersionEnum
+from wake.core.solidity_version import SolidityVersion
+
+
+def test_solidity_0_8_35_standard_json_models():
+ settings = SolcInputSettings(
+ experimental=True,
+ evm_version=EvmVersionEnum.FUTURE,
+ via_SSA_CFG=True,
+ )
+
+ assert settings.model_dump(by_alias=True, exclude_none=True) == {
+ "experimental": True,
+ "evmVersion": "@future",
+ "viaSSACFG": True,
+ }
+
+ output = SolcOutput.model_validate(
+ {
+ "contracts": {
+ "C.sol": {
+ "C": {
+ "yulCFGJson": {},
+ "evm": {
+ "bytecode": {"ethdebug": {}},
+ "deployedBytecode": {"ethdebug": {}},
+ },
+ }
+ }
+ },
+ "ethdebug": {"resources": {}, "compilation": {}},
+ }
+ )
+
+ contract = output.contracts["C.sol"]["C"]
+ assert contract.yul_CFG_json == {}
+ assert contract.evm is not None
+ assert contract.evm.bytecode is not None
+ assert contract.evm.bytecode.ethdebug == {}
+ assert contract.evm.deployed_bytecode is not None
+ assert contract.evm.deployed_bytecode.ethdebug == {}
+ assert output.ethdebug == {"resources": {}, "compilation": {}}
+
+
+@pytest.mark.parametrize(
+ ("value", "expected", "experimental"),
+ (
+ ("ast-id", SolcInputDebugInfoSettingsEnum.AST_ID, False),
+ ("ethdebug", SolcInputDebugInfoSettingsEnum.ETHDEBUG, True),
+ ),
+)
+def test_solidity_0_8_35_debug_info_input(value, expected, experimental):
+ standard_input = SolcInput.model_validate_json(
+ json.dumps(
+ {
+ "language": "Solidity",
+ "sources": {"C.sol": {"content": "contract C {}"}},
+ "settings": {
+ "experimental": experimental,
+ "debug": {"debugInfo": [value]},
+ },
+ }
+ )
+ )
+
+ assert standard_input.settings is not None
+ assert standard_input.settings.debug is not None
+ assert standard_input.settings.debug.debug_info == [expected]
+ serialized = json.loads(
+ standard_input.model_dump_json(by_alias=True, exclude_none=True)
+ )
+ assert serialized["settings"]["debug"] == {"debugInfo": [value]}
+
+
+def test_global_experimental_outputs_remain_globally_scoped(tmp_path, monkeypatch):
+ monkeypatch.setenv("XDG_CONFIG_HOME", str(tmp_path / "config"))
+ monkeypatch.setenv("XDG_DATA_HOME", str(tmp_path / "data"))
+ config = WakeConfig(project_root_path=tmp_path)
+ compiler = SolidityCompiler(config)
+ settings = SolcInputSettings(
+ output_selection={
+ "*": {
+ "": [SolcOutputSelectionEnum.AST],
+ "*": [
+ SolcOutputSelectionEnum.ABI,
+ SolcOutputSelectionEnum.ETHDEBUG_RESOURCES,
+ SolcOutputSelectionEnum.ETHDEBUG_COMPILATION,
+ ],
+ }
+ }
+ )
+
+ optimized = compiler.optimize_build_settings(settings, {"C.sol"})
+
+ assert optimized.output_selection == {
+ "*": {
+ "": [SolcOutputSelectionEnum.AST],
+ "*": [
+ SolcOutputSelectionEnum.ETHDEBUG_RESOURCES,
+ SolcOutputSelectionEnum.ETHDEBUG_COMPILATION,
+ ],
+ },
+ "C.sol": {"*": [SolcOutputSelectionEnum.ABI]},
+ }
+
+
+def test_compiler_build_settings_include_experimental_config(tmp_path, monkeypatch):
+ monkeypatch.setenv("XDG_CONFIG_HOME", str(tmp_path / "config"))
+ monkeypatch.setenv("XDG_DATA_HOME", str(tmp_path / "data"))
+ config = WakeConfig.fromdict(
+ {
+ "compiler": {
+ "solc": {
+ "experimental": True,
+ "evm_version": "@future",
+ "via_SSA_CFG": True,
+ }
+ }
+ },
+ project_root_path=tmp_path,
+ )
+
+ settings = SolidityCompiler(config).create_build_settings([], None)
+
+ assert settings.experimental is True
+ assert settings.evm_version == EvmVersionEnum.FUTURE
+ assert settings.via_SSA_CFG is True
+
+
+def test_solidity_0_8_35_settings_are_not_forwarded_to_older_compilers(
+ tmp_path, monkeypatch
+):
+ monkeypatch.setenv("XDG_CONFIG_HOME", str(tmp_path / "config"))
+ monkeypatch.setenv("XDG_DATA_HOME", str(tmp_path / "data"))
+ config = WakeConfig(project_root_path=tmp_path)
+ captured = {}
+
+ async def run_solc(_self, target_version, standard_input):
+ captured["target_version"] = target_version
+ captured["settings"] = standard_input.settings
+ return SolcOutput()
+
+ monkeypatch.setattr(SolcFrontend, "_SolcFrontend__run_solc", run_solc)
+ frontend = SolcFrontend(config)
+
+ asyncio.run(
+ frontend.compile(
+ {},
+ {"C.sol": "pragma solidity 0.8.34; contract C {}"},
+ SolidityVersion.fromstring("0.8.34"),
+ SolcInputSettings(
+ experimental=True,
+ evm_version=EvmVersionEnum.FUTURE,
+ via_SSA_CFG=True,
+ ),
+ )
+ )
+
+ assert captured["target_version"] == SolidityVersion.fromstring("0.8.34")
+ assert captured["settings"].experimental is None
+ assert captured["settings"].via_SSA_CFG is None
+ assert captured["settings"].evm_version == EvmVersionEnum.OSAKA
diff --git a/tests/test_svm.py b/tests/test_svm.py
index 8f01fd2c2..19168e663 100644
--- a/tests/test_svm.py
+++ b/tests/test_svm.py
@@ -1,4 +1,5 @@
import asyncio
+import hashlib
import os
import shutil
import subprocess
@@ -7,10 +8,11 @@
import aiohttp
import pytest
+from Crypto.Hash import keccak
from wake.config import WakeConfig
-from wake.svm import SolcVersionManager
from wake.svm.exceptions import UnsupportedVersionError
+from wake.svm.svm import SolcBuilds, SolcVersionManager
PYTEST_WAKE_PATH = Path.home() / ".tmpwake_KVUhSovO5J"
PYTEST_WAKE_PATH2 = Path.home() / ".tmpwake2_fLtqXkHeVH"
@@ -161,3 +163,56 @@ async def test_file_executable(run_cleanup, config):
await svm.install(oldest_version)
output = subprocess.check_output([str(svm.get_path(oldest_version)), "--version"])
assert str(oldest_version).encode("utf-8") in output
+
+
+def test_stable_build_checksum_selected_when_prerelease_exists(tmp_path, monkeypatch):
+ monkeypatch.setenv("XDG_CONFIG_HOME", str(tmp_path / "config"))
+ monkeypatch.setenv("XDG_DATA_HOME", str(tmp_path / "data"))
+
+ stable_content = b"stable compiler"
+ prerelease_content = b"prerelease compiler"
+
+ def checksums(content: bytes):
+ keccak256 = keccak.new(digest_bits=256)
+ keccak256.update(content)
+ return f"0x{keccak256.hexdigest()}", f"0x{hashlib.sha256(content).hexdigest()}"
+
+ prerelease_keccak, prerelease_sha = checksums(prerelease_content)
+ stable_keccak, stable_sha = checksums(stable_content)
+ stable_filename = "solc-v0.8.35+commit.stable"
+
+ config = WakeConfig(project_root_path=tmp_path)
+ svm = SolcVersionManager(config)
+ builds = SolcBuilds.model_validate(
+ {
+ "builds": [
+ {
+ "path": "solc-v0.8.35-pre.1+commit.prerelease",
+ "version": "0.8.35",
+ "build": "commit.prerelease",
+ "longVersion": "0.8.35-pre.1+commit.prerelease",
+ "keccak256": prerelease_keccak,
+ "sha256": prerelease_sha,
+ "urls": [],
+ },
+ {
+ "path": stable_filename,
+ "version": "0.8.35",
+ "build": "commit.stable",
+ "longVersion": "0.8.35+commit.stable",
+ "keccak256": stable_keccak,
+ "sha256": stable_sha,
+ "urls": [],
+ },
+ ],
+ "releases": {"0.8.35": stable_filename},
+ "latestRelease": "0.8.35",
+ }
+ )
+ setattr(svm, "_SolcVersionManager__solc_builds", builds)
+
+ compiler_path = svm.get_path("0.8.35")
+ compiler_path.parent.mkdir(parents=True)
+ compiler_path.write_bytes(stable_content)
+
+ assert svm.installed("0.8.35")
diff --git a/wake/cli/init.py b/wake/cli/init.py
index e46945488..dbe1bc3da 100644
--- a/wake/cli/init.py
+++ b/wake/cli/init.py
@@ -61,9 +61,17 @@ def write_config(config: WakeConfig) -> None:
if config.compiler.solc.evm_version is not None:
f.write(f'evm_version = "{config.compiler.solc.evm_version}"\n')
+ if config.compiler.solc.experimental is not None:
+ f.write(
+ f"experimental = {str(config.compiler.solc.experimental).lower()}\n"
+ )
+
if config.compiler.solc.via_IR is not None:
f.write(f"via_IR = {str(config.compiler.solc.via_IR).lower()}\n")
+ if config.compiler.solc.via_SSA_CFG is not None:
+ f.write(f"via_SSA_CFG = {str(config.compiler.solc.via_SSA_CFG).lower()}\n")
+
if config.compiler.solc.target_version is not None:
f.write(f'target_version = "{config.compiler.solc.target_version}"\n')
diff --git a/wake/cli/open.py b/wake/cli/open.py
index 2ea5e3a5b..ae9486753 100644
--- a/wake/cli/open.py
+++ b/wake/cli/open.py
@@ -210,6 +210,10 @@ async def open_address(
if "viaIR" in c:
config_dict["compiler"]["solc"]["via_IR"] = c["viaIR"]
+ if "experimental" in c:
+ config_dict["compiler"]["solc"]["experimental"] = c["experimental"]
+ if "viaSSACFG" in c:
+ config_dict["compiler"]["solc"]["via_SSA_CFG"] = c["viaSSACFG"]
else:
compiler_version: str = info["CompilerVersion"]
if compiler_version.startswith("vyper"):
@@ -250,6 +254,14 @@ async def open_address(
config_dict["compiler"]["solc"][
"via_IR"
] = standard_input.settings.via_IR
+ if standard_input.settings.experimental is not None:
+ config_dict["compiler"]["solc"][
+ "experimental"
+ ] = standard_input.settings.experimental
+ if standard_input.settings.via_SSA_CFG is not None:
+ config_dict["compiler"]["solc"][
+ "via_SSA_CFG"
+ ] = standard_input.settings.via_SSA_CFG
if standard_input.settings.remappings is not None:
config_dict["compiler"]["solc"][
"remappings"
diff --git a/wake/compiler/compiler.py b/wake/compiler/compiler.py
index 8c3e0e3be..18d977d42 100644
--- a/wake/compiler/compiler.py
+++ b/wake/compiler/compiler.py
@@ -622,7 +622,9 @@ def create_build_settings(
str(remapping) for remapping in self.__config.compiler.solc.remappings
]
settings.evm_version = solc_settings.evm_version
+ settings.experimental = solc_settings.experimental
settings.via_IR = solc_settings.via_IR
+ settings.via_SSA_CFG = solc_settings.via_SSA_CFG
settings.optimizer = SolcInputOptimizerSettings(
enabled=solc_settings.optimizer.enabled,
runs=solc_settings.optimizer.runs,
@@ -679,11 +681,31 @@ def optimize_build_settings(
and "*" in settings.output_selection["*"]
):
new_selection = {}
+ global_output_types = {
+ SolcOutputSelectionEnum.ETHDEBUG_RESOURCES,
+ SolcOutputSelectionEnum.ETHDEBUG_COMPILATION,
+ }
+ selected_output_types = settings.output_selection["*"]["*"]
+ global_outputs = [
+ output_type
+ for output_type in selected_output_types
+ if output_type in global_output_types
+ ]
+ contract_outputs = [
+ output_type
+ for output_type in selected_output_types
+ if output_type not in global_output_types
+ ]
+
if "" in settings.output_selection["*"]:
new_selection["*"] = {"": settings.output_selection["*"][""]}
- for source_unit in modified_source_units:
- new_selection[source_unit] = {"*": settings.output_selection["*"]["*"]}
+ if len(global_outputs) > 0:
+ new_selection.setdefault("*", {})["*"] = global_outputs
+
+ if len(contract_outputs) > 0:
+ for source_unit in modified_source_units:
+ new_selection[source_unit] = {"*": contract_outputs}
ret = settings.model_copy()
ret.output_selection = new_selection
diff --git a/wake/compiler/solc_frontend/input_data_model.py b/wake/compiler/solc_frontend/input_data_model.py
index dd1f5afe8..d28f0b9e1 100644
--- a/wake/compiler/solc_frontend/input_data_model.py
+++ b/wake/compiler/solc_frontend/input_data_model.py
@@ -49,6 +49,8 @@ class SolcOutputSelectionEnum(StrEnum):
"""Old-style assembly format in JSON"""
EVM_BYTECODE_ALL = "evm.bytecode"
"""All bytecode subassets"""
+ EVM_BYTECODE_ETHDEBUG = "evm.bytecode.ethdebug"
+ """Experimental Ethdebug program for creation bytecode"""
EVM_BYTECODE_FUNCTION_DEBUG_DATA = "evm.bytecode.functionDebugData"
"""Debugging information at function level"""
EVM_BYTECODE_OBJECT = "evm.bytecode.object"
@@ -63,6 +65,8 @@ class SolcOutputSelectionEnum(StrEnum):
"""Sources generated by the compiler"""
EVM_DEPLOYED_BYTECODE_ALL = "evm.deployedBytecode"
"""All deployed bytecode subassets"""
+ EVM_DEPLOYED_BYTECODE_ETHDEBUG = "evm.deployedBytecode.ethdebug"
+ """Experimental Ethdebug program for deployed bytecode"""
EVM_DEPLOYED_BYTECODE_FUNCTION_DEBUG_DATA = "evm.deployedBytecode.functionDebugData"
"""Debugging information at function level"""
EVM_DEPLOYED_BYTECODE_OBJECT = "evm.deployedBytecode.object"
@@ -83,6 +87,12 @@ class SolcOutputSelectionEnum(StrEnum):
"""The list of function hashes"""
EVM_GAS_ESTIMATES = "evm.gasEstimates"
"""Function gas estimates"""
+ YUL_CFG_JSON = "yulCFGJson"
+ """Experimental control-flow graph of the SSA-form Yul code"""
+ ETHDEBUG_RESOURCES = "ethdebug.resources"
+ """Experimental global Ethdebug resources output"""
+ ETHDEBUG_COMPILATION = "ethdebug.compilation"
+ """Experimental global Ethdebug compilation output"""
EWASM_ALL = "ewasm"
"""All EWASM subassets"""
EWASM_WAST = "ewasm.wast"
@@ -200,6 +210,10 @@ class SolcInputDebugInfoSettingsEnum(StrEnum):
"""
SNIPPET = "snippet"
"""A single-line code snippet from the location indicated by `@src`. The snippet is quoted and follows the corresponding `@src` annotation."""
+ AST_ID = "ast-id"
+ """Annotations of the form `@ast-id ` for elements mapped to definitions in the Solidity AST."""
+ ETHDEBUG = "ethdebug"
+ """Experimental Ethdebug annotations."""
class SolcInputDebugSettings(SolcInputModel):
@@ -227,9 +241,11 @@ class SolcInputModelCheckerSettings(SolcInputModel):
class SolcInputSettings(SolcInputModel):
stop_after: Optional[SolcStopAfterEnum] = None
remappings: Optional[List[str]] = None
+ experimental: Optional[bool] = None
optimizer: Optional[SolcInputOptimizerSettings] = None
evm_version: Optional[EvmVersionEnum] = None
via_IR: Optional[bool] = Field(None, alias="viaIR")
+ via_SSA_CFG: Optional[bool] = Field(None, alias="viaSSACFG")
debug: Optional[SolcInputDebugSettings] = None
metadata: Optional[SolcInputMetadataSettings] = None
libraries: Optional[
diff --git a/wake/compiler/solc_frontend/output_data_model.py b/wake/compiler/solc_frontend/output_data_model.py
index a59fae174..7061686c5 100644
--- a/wake/compiler/solc_frontend/output_data_model.py
+++ b/wake/compiler/solc_frontend/output_data_model.py
@@ -1,6 +1,6 @@
from typing import Dict, List, Optional, Tuple
-from pydantic import BaseModel, ConfigDict
+from pydantic import BaseModel, ConfigDict, Field
from wake.utils import StrEnum
@@ -191,6 +191,7 @@ class SolcOutputEvmBytecodeLinkReferencesInfo(SolcOutputModel):
class SolcOutputEvmBytecodeData(SolcOutputModel):
+ ethdebug: Optional[Dict] = None
function_debug_data: Optional[
Dict[str, SolcOutputEvmBytecodeFunctionDebugData]
] = None # internal name of the function -> debug data
@@ -247,6 +248,8 @@ class SolcOutputContractInfo(SolcOutputModel):
"""See https://docs.soliditylang.org/en/latest/internals/layout_in_storage.html#json-output"""
transient_storage_layout: Optional[SolcOutputStorageLayout] = None
"""See https://docs.soliditylang.org/en/latest/internals/layout_in_storage.html#json-output"""
+ yul_CFG_json: Optional[Dict] = Field(None, alias="yulCFGJson")
+ """Experimental control-flow graph of the SSA-form Yul code"""
evm: Optional[SolcOutputEvmData] = None
"""EVM-related outputs"""
ewasm: Optional[SolcOutputEwasmData] = None
@@ -255,6 +258,7 @@ class SolcOutputContractInfo(SolcOutputModel):
class SolcOutput(SolcOutputModel):
auxiliary_input_requested: Optional[Dict] = None
+ ethdebug: Optional[Dict] = None
errors: List[SolcOutputError] = []
sources: Dict[str, SolcOutputSourceInfo] = {}
contracts: Dict[
diff --git a/wake/compiler/solc_frontend/solc_runner.py b/wake/compiler/solc_frontend/solc_runner.py
index 1e4f12cec..1f1bf6584 100644
--- a/wake/compiler/solc_frontend/solc_runner.py
+++ b/wake/compiler/solc_frontend/solc_runner.py
@@ -32,6 +32,7 @@
SolidityVersion.fromstring("0.8.24"): EvmVersionEnum.CANCUN,
SolidityVersion.fromstring("0.8.27"): EvmVersionEnum.PRAGUE,
SolidityVersion.fromstring("0.8.29"): EvmVersionEnum.OSAKA,
+ SolidityVersion.fromstring("0.8.35"): EvmVersionEnum.FUTURE,
}
@@ -87,6 +88,21 @@ async def compile(
)
standard_input.settings.via_IR = None
+ if target_version < "0.8.35":
+ if settings.experimental is not None:
+ if settings.experimental:
+ logger.warning(
+ "`experimental` is not supported for solc versions < 0.8.35. This option will be ignored."
+ )
+ standard_input.settings.experimental = None
+
+ if settings.via_SSA_CFG is not None:
+ if settings.via_SSA_CFG:
+ logger.warning(
+ "`via_SSA_CFG` is not supported for solc versions < 0.8.35. This option will be ignored."
+ )
+ standard_input.settings.via_SSA_CFG = None
+
if settings.evm_version is not None:
# find nearest <= version in MAX_SUPPORTED_EVM_VERSIONS
nearest_version = max(
diff --git a/wake/config/data_model.py b/wake/config/data_model.py
index 0c6cfac86..abd883819 100644
--- a/wake/config/data_model.py
+++ b/wake/config/data_model.py
@@ -130,6 +130,8 @@ class SolcConfig(WakeConfigModel):
"""Wake should set solc `--allow-paths` automatically. This option allows to specify additional allowed paths."""
evm_version: Optional[EvmVersionEnum] = None
"""Version of the EVM to compile for. Leave unset to let the solc decide."""
+ experimental: Optional[bool] = None
+ """Enable Solidity compiler experimental mode."""
exclude_paths: FrozenSet[PurePath] = Field(
default_factory=lambda: frozenset(
[
@@ -173,6 +175,10 @@ class SolcConfig(WakeConfigModel):
"""
Use new IR-based compiler pipeline.
"""
+ via_SSA_CFG: Optional[bool] = None
+ """
+ Use the experimental SSA CFG code generation pipeline. Implies `via_IR`.
+ """
metadata: SolcMetadataConfig = Field(default_factory=SolcMetadataConfig)
"""
Metadata config options.
@@ -191,8 +197,10 @@ class SubprojectConfig(WakeConfigModel):
paths: FrozenSet[PurePath] = frozenset()
target_version: Optional[SolidityVersion] = None
evm_version: Optional[EvmVersionEnum] = None
+ experimental: Optional[bool] = None
optimizer: SolcOptimizerConfig = Field(default_factory=SolcOptimizerConfig)
via_IR: Optional[bool] = None
+ via_SSA_CFG: Optional[bool] = None
metadata: SolcMetadataConfig = Field(default_factory=SolcMetadataConfig)
_normalize_paths = field_validator("paths", mode="before")(normalize_paths)
diff --git a/wake/config/wake_config.py b/wake/config/wake_config.py
index dd52cb61e..feb01f912 100644
--- a/wake/config/wake_config.py
+++ b/wake/config/wake_config.py
@@ -536,7 +536,7 @@ def max_solidity_version(self) -> SolidityVersion:
Returns:
Maximum supported Solidity version.
"""
- return SolidityVersion.fromstring("0.8.34")
+ return SolidityVersion.fromstring("0.8.35")
@property
def detectors(self) -> DetectorsConfig:
diff --git a/wake/core/enums.py b/wake/core/enums.py
index b609f5a9f..71e164317 100644
--- a/wake/core/enums.py
+++ b/wake/core/enums.py
@@ -16,6 +16,7 @@ class EvmVersionEnum(StrEnum):
CANCUN = "cancun"
PRAGUE = "prague"
OSAKA = "osaka"
+ FUTURE = "@future"
def __lt__(self, other: "EvmVersionEnum") -> bool:
if not isinstance(other, EvmVersionEnum):
@@ -54,4 +55,5 @@ def __ge__(self, other: "EvmVersionEnum") -> bool:
EvmVersionEnum.CANCUN,
EvmVersionEnum.PRAGUE,
EvmVersionEnum.OSAKA,
+ EvmVersionEnum.FUTURE,
]
diff --git a/wake/development/utils.py b/wake/development/utils.py
index 2b9d97353..324f9d1b5 100644
--- a/wake/development/utils.py
+++ b/wake/development/utils.py
@@ -1732,6 +1732,20 @@ def _get_storage_layout_from_explorer(
"remappings"
] = standard_input.settings.remappings
+ if standard_input.settings is not None:
+ if standard_input.settings.experimental is not None:
+ config_dict["compiler"]["solc"][
+ "experimental"
+ ] = standard_input.settings.experimental
+ if standard_input.settings.via_IR is not None:
+ config_dict["compiler"]["solc"][
+ "via_IR"
+ ] = standard_input.settings.via_IR
+ if standard_input.settings.via_SSA_CFG is not None:
+ config_dict["compiler"]["solc"][
+ "via_SSA_CFG"
+ ] = standard_input.settings.via_SSA_CFG
+
if any(
source.urls is not None for source in standard_input.sources.values()
):
diff --git a/wake/ir/enums.py b/wake/ir/enums.py
index 95f3502ba..641168db6 100644
--- a/wake/ir/enums.py
+++ b/wake/ir/enums.py
@@ -38,6 +38,7 @@ class GlobalSymbol(IntEnum):
TYPE = -27
THIS = -28
BLOBHASH = -29
+ ERC7201 = -30
BLOCK_BASEFEE = -100
BLOCK_CHAINID = -101
@@ -318,6 +319,7 @@ class InlineAssemblyEvmVersion(StrEnum):
CANCUN = "cancun"
PRAGUE = "prague"
OSAKA = "osaka"
+ FUTURE = "@future"
class InlineAssemblySuffix(StrEnum):
@@ -345,6 +347,7 @@ class FunctionTypeKind(StrEnum):
SEND = "send"
TRANSFER = "transfer"
KECCAK256 = "keccak256"
+ ERC7201 = "erc7201"
SELFDESTRUCT = "selfdestruct"
REVERT = "revert"
EC_RECOVER = "ecrecover"
diff --git a/wake/svm/svm.py b/wake/svm/svm.py
index d59c0588a..e00560c9b 100644
--- a/wake/svm/svm.py
+++ b/wake/svm/svm.py
@@ -344,10 +344,10 @@ def __fetch_list_file(
def __verify_checksums(self, version: SolidityVersion) -> bool:
assert self.__solc_builds is not None
- build_info = next(b for b in self.__solc_builds.builds if b.version == version)
+ filename = self.__solc_builds.releases[version]
+ build_info = next(b for b in self.__solc_builds.builds if b.path == filename)
local_path = self.get_path(version)
- filename = self.__solc_builds.releases[version]
if filename.endswith(".zip"):
local_path = local_path.parent / filename
if not local_path.is_file():