diff --git a/.codespellignore b/.codespellignore
deleted file mode 100644
index 75829846c..000000000
--- a/.codespellignore
+++ /dev/null
@@ -1,3 +0,0 @@
-keypair
-astroid
-uvloop
diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json
index b03713d5c..4ba48f266 100644
--- a/.devcontainer/devcontainer.json
+++ b/.devcontainer/devcontainer.json
@@ -12,8 +12,8 @@
"python.formatting.blackPath": "/usr/local/py-utils/bin/black",
"python.formatting.yapfPath": "/usr/local/py-utils/bin/yapf",
"editor.defaultFormatter": "trunk.io",
- "cairols.sourceDir": "src",
- "cairols.cairoPath": ["src", "tests"]
+ "cairols.sourceDir": "cairo_zero",
+ "cairols.cairoPath": ["cairo_zero", "tests"]
},
// Add the IDs of extensions you want installed when the container is created.
"extensions": [
@@ -21,14 +21,15 @@
"ms-python.vscode-pylance",
"starkware.cairo",
"ericglau.cairo-ls",
- "trunk.io"
+ "trunk.io",
+ "starkware.cairo1"
]
}
},
// Use 'forwardPorts' to make a list of ports inside the container available locally.
// "forwardPorts": [],
// Use 'postCreateCommand' to run commands after the container is created.
- "postCreateCommand": "curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y && curl -L https://foundry.paradigm.xyz | bash && /home/vscode/.foundry/bin/foundryup && make setup",
+ "postCreateCommand": "curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y && curl -L https://foundry.paradigm.xyz | bash && /home/vscode/.foundry/bin/foundryup && curl -LsSf https://astral.sh/uv/install.sh | sh && . $HOME/.cargo/env && uv venv && make setup && curl --proto '=https' --tlsv1.2 -sSf https://docs.swmansion.com/scarb/install.sh | sh",
// Comment out to connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root.
"remoteUser": "vscode",
"features": {
diff --git a/.env.example b/.env.example
index 9381ce47e..80cc4030d 100644
--- a/.env.example
+++ b/.env.example
@@ -52,3 +52,7 @@ HYPOTHESIS_PROFILE=dev
VOYAGER_API_URL=
VOYAGER_API_KEY=
+
+# Private key to sign transactions for RLP scripts/compute_rlp_encoding.ts
+# Warning: Do not use this key in production systems
+PRIVATE_KEY_RLP_SCRIPT=0x6be0067ba259624aa28f796b703e7d095925a470b17786767bd09aaa3fc2ceea
diff --git a/.github/workflows/ci.yml b/.github/workflows/cairo-zero-ci.yml
similarity index 97%
rename from .github/workflows/ci.yml
rename to .github/workflows/cairo-zero-ci.yml
index 0209316b4..bf3a4b43e 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/cairo-zero-ci.yml
@@ -1,16 +1,18 @@
-name: CI
+name: cairo-zero-CI
on:
push:
branches:
- main
- pull_request: {}
+ pull_request:
+ paths:
+ - cairo_zero/**
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
concurrency:
- group: ${{ github.workflow }}-${{ github.ref }}
+ group: cairo-zero-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
permissions: read-all
@@ -32,7 +34,7 @@ jobs:
with:
filters: |
src:
- - 'src/**'
+ - 'cairo_zero/**'
kakarot_scripts:
- 'kakarot_scripts/**'
solidity:
@@ -96,7 +98,7 @@ jobs:
- name: Run tests
env:
HYPOTHESIS_PROFILE: ci
- run: make test-unit
+ run: make test-unit-cairo-zero
- name: Upload coverage report to codecov
uses: codecov/codecov-action@v3
with:
diff --git a/.github/workflows/nightly-fuzzing.yml b/.github/workflows/cairo-zero-nightly-fuzzing.yml
similarity index 92%
rename from .github/workflows/nightly-fuzzing.yml
rename to .github/workflows/cairo-zero-nightly-fuzzing.yml
index 9083bd3cd..506b9acf6 100644
--- a/.github/workflows/nightly-fuzzing.yml
+++ b/.github/workflows/cairo-zero-nightly-fuzzing.yml
@@ -1,4 +1,4 @@
-name: NIGHTLY-FUZZING
+name: cairo-zero-NIGHTLY-FUZZING
on:
schedule:
@@ -30,7 +30,7 @@ jobs:
- name: Run tests
env:
HYPOTHESIS_PROFILE: nightly
- run: make test-unit
+ run: make test-unit-cairo-zero
- name: Upload coverage report to codecov
uses: codecov/codecov-action@v3
with:
diff --git a/.github/workflows/release.yml b/.github/workflows/cairo-zero-release.yml
similarity index 99%
rename from .github/workflows/release.yml
rename to .github/workflows/cairo-zero-release.yml
index dca8189d5..bc29c36ff 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/cairo-zero-release.yml
@@ -1,5 +1,5 @@
# trunk-ignore-all(checkov/CKV2_GHA_1)
-name: Release
+name: cairo-zero-Release
on:
release:
diff --git a/.github/workflows/update-rpc.yml b/.github/workflows/cairo-zero-update-rpc.yml
similarity index 97%
rename from .github/workflows/update-rpc.yml
rename to .github/workflows/cairo-zero-update-rpc.yml
index 3a59bfb50..23c6e2621 100644
--- a/.github/workflows/update-rpc.yml
+++ b/.github/workflows/cairo-zero-update-rpc.yml
@@ -1,4 +1,4 @@
-name: Update Submodule
+name: cairo-zero Update Submodule
on:
release:
diff --git a/.github/workflows/ssj-build.yml b/.github/workflows/ssj-build.yml
new file mode 100644
index 000000000..00353104f
--- /dev/null
+++ b/.github/workflows/ssj-build.yml
@@ -0,0 +1,34 @@
+name: Reusable Build Workflow
+
+on:
+ workflow_call:
+ inputs:
+ artifact-name:
+ required: true
+ type: string
+
+permissions: read-all
+
+jobs:
+ ssj-build:
+ runs-on: ubuntu-latest
+ env:
+ CI_COMMIT_MESSAGE: CI Formatting Auto Commit
+ CI_COMMIT_AUTHOR: ${{ github.event.repository.name }} CI
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+
+ - name: Set up Scarb
+ uses: software-mansion/setup-scarb@v1
+ with:
+ tool-versions: ./cairo/kakarot-ssj/.tool-versions
+
+ - name: Build contracts
+ run: cd cairo/kakarot-ssj/ && scarb build -p contracts
+
+ - name: Upload artifacts
+ uses: actions/upload-artifact@v3
+ with:
+ name: ${{ inputs.artifact-name }}
+ path: cairo/kakarot-ssj/target/dev
diff --git a/.github/workflows/ssj-ci.yml b/.github/workflows/ssj-ci.yml
new file mode 100644
index 000000000..441ec550a
--- /dev/null
+++ b/.github/workflows/ssj-ci.yml
@@ -0,0 +1,84 @@
+name: SSJ-CI
+
+on:
+ push:
+ branches:
+ - main
+ paths:
+ - cairo/kakarot-ssj/**
+
+env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+
+concurrency:
+ group: ssj-${{ github.workflow }}-${{ github.ref }}
+ cancel-in-progress: true
+
+permissions: read-all
+
+jobs:
+ ssj-build:
+ uses: ./.github/workflows/ssj-build.yml
+ with:
+ artifact-name: ssj-build
+
+ ssj-tests-unit:
+ uses: ./.github/workflows/ssj-tests-unit.yml
+ with:
+ run-fmt-check: false
+
+ ssj-ef-tests:
+ uses: ./.github/workflows/ssj-ef-tests.yml
+ needs: [ssj-build]
+ with:
+ artifact-name: ssj-build
+
+ ssj-resources:
+ runs-on: ubuntu-latest
+ needs: [ssj-ef-tests]
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Set up Python 3.10.14
+ uses: actions/setup-python@v4
+ with:
+ python-version: 3.10.14
+
+ - name: Load cached Poetry installation
+ id: cached-poetry
+ uses: actions/cache@v4
+ with:
+ path: ~/.local
+ key: poetry-${{ runner.os }}-${{ hashFiles('**/poetry.lock') }}
+
+ - name: Install Poetry
+ if: steps.cached-poetry.outputs.cache-hit != 'true'
+ uses: snok/install-poetry@v1
+ with:
+ virtualenvs-create: true
+ virtualenvs-in-project: true
+ installer-parallel: true
+
+ - run: poetry config installer.modern-installation false
+
+ - name: Load cached venv
+ id: cached-poetry-dependencies
+ uses: actions/cache@v4
+ with:
+ path: .venv
+ key: venv-${{ runner.os }}-${{ hashFiles('**/poetry.lock') }}
+
+ - name: Install dependencies
+ if: steps.cached-poetry-dependencies.outputs.cache-hit != 'true'
+ run: make setup
+
+ - name: Load performance artifacts
+ uses: actions/download-artifact@v3
+ with:
+ path: resources
+ name: resources
+
+ - name: Check resources evolution
+ run: |
+ result=$(GITHUB_TOKEN=${{ secrets.GITHUB_TOKEN }} make check-resources 2>&1)
+ echo "$result" >> "$GITHUB_STEP_SUMMARY"
diff --git a/.github/workflows/ssj-ef-tests.yml b/.github/workflows/ssj-ef-tests.yml
new file mode 100644
index 000000000..6af3f2914
--- /dev/null
+++ b/.github/workflows/ssj-ef-tests.yml
@@ -0,0 +1,129 @@
+name: SSJ-EF-Tests
+
+on:
+ workflow_call:
+ inputs:
+ artifact-name:
+ required: true
+ type: string
+
+permissions: read-all
+
+jobs:
+ ef-tests:
+ # trunk-ignore(actionlint/runner-label)
+ runs-on: ubuntu-latest-16-cores
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+
+ - name: Cache cairo-native setup
+ id: cache-cairo-native
+ uses: actions/cache@v3
+ with:
+ path: |
+ ~/.cargo/bin/
+ ~/.cargo/registry/index/
+ ~/.cargo/registry/cache/
+ ~/.cargo/git/db/
+ cairo/kakarot-ssj/target/
+ ./libcairo_native_runtime.a
+ key:
+ ${{ runner.os }}-cairo-native-${{ hashFiles('**/Cargo.lock',
+ 'scripts/setup_cairo_native.sh') }}
+
+ - name: Make setup script executable
+ run: chmod +x ./cairo/kakarot-ssj/scripts/setup_cairo_native.sh
+
+ - name: Setup Cairo Native
+ run: |
+ if [[ "${{ steps.cache-cairo-native.outputs.cache-hit }}" == 'true' ]]; then
+ sudo ./cairo/kakarot-ssj/scripts/setup_cairo_native.sh -s
+ else
+ sudo ./cairo/kakarot-ssj/scripts/setup_cairo_native.sh
+ fi
+
+ - name: Set Environment Variables
+ run: |
+ echo "MLIR_SYS_190_PREFIX=/usr/lib/llvm-19/" >> $GITHUB_ENV
+ echo "LLVM_SYS_191_PREFIX=/usr/lib/llvm-19/" >> $GITHUB_ENV
+ echo "TABLEGEN_190_PREFIX=/usr/lib/llvm-19/" >> $GITHUB_ENV
+ echo "CAIRO_NATIVE_RUNTIME_LIBRARY=$(pwd)/libcairo_native_runtime.a" >> $GITHUB_ENV
+
+ - name: Checkout ef-tests
+ uses: actions/checkout@v4
+ with:
+ repository: kkrt-labs/ef-tests
+ ref: feat/cairo-native
+ path: ef-tests # Check out to a subdirectory to avoid cleaning the kakarot-ssj directory
+
+ - name: Checkout local skip file
+ uses: actions/checkout@v4
+ with:
+ sparse-checkout: |
+ cairo/kakarot-ssj/
+ sparse-checkout-cone-mode: false
+ path: skip-file
+
+ - name: Setup ef-tests
+ run: |
+ mv skip-file/cairo/kakarot-ssj/blockchain-tests-skip.yml ef-tests/blockchain-tests-skip.yml
+ cd ef-tests
+ mkdir -p build/common
+ make setup setup-kakarot-v0
+
+ - name: Install nextest
+ uses: taiki-e/install-action@nextest
+
+ - name: Download Kakarot-SSJ build artifacts in v1
+ uses: actions/download-artifact@v3
+ with:
+ name: ${{ inputs.artifact-name }}
+ path: ef-tests/build/v1
+
+ - name: Move Cairo1Helpers
+ run: |
+ mv ef-tests/build/v1/contracts_Cairo1Helpers.compiled_contract_class.json \
+ ef-tests/build/common/cairo1_helpers.json
+
+ - name: Set up Python
+ uses: actions/setup-python@v4
+ with:
+ python-version: 3.10.14
+
+ # Add this step to verify the file exists
+ - name: Verify libcairo_native_runtime.a
+ run: |
+ echo $CAIRO_NATIVE_RUNTIME_LIBRARY
+ ls -l $CAIRO_NATIVE_RUNTIME_LIBRARY
+
+ - name: Run tests
+ working-directory: ef-tests
+ run: |
+ set -o pipefail
+ RUST_MIN_STACK=1342177280 make ef-test-v1-native | tee test_v1.out
+ set +o pipefail
+
+ - name: Retrieve ef-tests execution resources
+ working-directory: ef-tests
+ run: python scripts/compute_resources.py
+ env:
+ KAKAROT_VERSION: v1
+
+ - name: Upload resources
+ uses: actions/upload-artifact@v3
+ with:
+ path: ef-tests/resources
+ name: resources
+
+ - name: Generate blockchain-tests-skip.yml file
+ if: github.event_name == 'workflow_dispatch'
+ working-directory: ef-tests
+ run: make generate-skip-file
+
+ - name: Upload skip file
+ if: github.event_name == 'workflow_dispatch'
+ uses: actions/upload-artifact@v3
+ with:
+ path: ef-tests/blockchain-tests-skip.yml
+ name: blockchain-tests-skip
diff --git a/.github/workflows/ssj-release.yml b/.github/workflows/ssj-release.yml
new file mode 100644
index 000000000..3e4da5724
--- /dev/null
+++ b/.github/workflows/ssj-release.yml
@@ -0,0 +1,30 @@
+# trunk-ignore-all(checkov/CKV2_GHA_1)
+name: SSJ-Release
+
+on:
+ release:
+ types: [published]
+
+jobs:
+ build-and-upload:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+ - name: Set up Scarb
+ uses: software-mansion/setup-scarb@v1
+ with:
+ tool-versions: ./cairo/kakarot-ssj/.tool-versions
+ - name: Build contracts
+ run: |
+ cd cairo/kakarot-ssj && scarb build -p contracts
+ - name: Zip dev artifacts
+ run: zip -rj kakarot-ssj-build.zip cairo/kakarot-ssj/target/dev
+ - name: Upload artifacts to release
+ uses: svenstaro/upload-release-action@v2
+ with:
+ repo_token: ${{ secrets.GITHUB_TOKEN }}
+ file: kakarot-ssj-build.zip
+ asset_name: kakarot-ssj-build.zip
+ tag: ${{ github.ref_name }}
+ overwrite: true
diff --git a/.github/workflows/ssj-test.yml b/.github/workflows/ssj-test.yml
new file mode 100644
index 000000000..b3dc169c7
--- /dev/null
+++ b/.github/workflows/ssj-test.yml
@@ -0,0 +1,29 @@
+name: SSJ-CI
+
+permissions: read-all
+
+on:
+ pull_request:
+ paths:
+ - cairo/kakarot-ssj/**
+
+concurrency:
+ group: ssj-${{ github.workflow }}-${{ github.ref }}
+ cancel-in-progress: true
+
+jobs:
+ ssj-build:
+ uses: ./.github/workflows/ssj-build.yml
+ with:
+ artifact-name: ssj-build
+
+ ssj-tests-unit:
+ uses: ./.github/workflows/ssj-tests-unit.yml
+ with:
+ run-fmt-check: true
+
+ ssj-ef-tests:
+ uses: ./.github/workflows/ssj-ef-tests.yml
+ needs: [ssj-build]
+ with:
+ artifact-name: ssj-build
diff --git a/.github/workflows/ssj-tests-unit.yml b/.github/workflows/ssj-tests-unit.yml
new file mode 100644
index 000000000..ab9068492
--- /dev/null
+++ b/.github/workflows/ssj-tests-unit.yml
@@ -0,0 +1,28 @@
+name: Reusable Unit Tests Workflow
+
+on:
+ workflow_call:
+ inputs:
+ run-fmt-check:
+ type: boolean
+ default: false
+ required: false
+
+permissions: read-all
+
+jobs:
+ unit-tests:
+ runs-on: ubuntu-latest-16-cores # trunk-ignore(actionlint/runner-label)
+ steps:
+ - uses: actions/checkout@v4
+ - uses: foundry-rs/setup-snfoundry@v3
+ - uses: software-mansion/setup-scarb@v1
+ with:
+ tool-versions: ./cairo/kakarot-ssj/.tool-versions
+
+ - name: Run format check
+ if: inputs.run-fmt-check
+ run: cd cairo/kakarot-ssj && scarb fmt --check
+
+ - name: Run tests
+ run: cd cairo/kakarot-ssj && scarb test
diff --git a/.gitignore b/.gitignore
index 9dd674473..b7d3baf47 100644
--- a/.gitignore
+++ b/.gitignore
@@ -44,3 +44,13 @@ profile*.png
*.memory
*.air_private_input.json
*.air_public_input.json
+
+.DS_Store
+.idea
+node_modules
+*.snfoundry_cache
+cairo/kakarot-ssj/target
+cairo/kakarot-ssj/cache/
+cairo/kakarot-ssj/scripts/libcairo_native_runtime.a
+__pycache__
+*.snfoundry
diff --git a/.tool-versions b/.tool-versions
index 250522192..70e7b8d55 100644
--- a/.tool-versions
+++ b/.tool-versions
@@ -1,2 +1,4 @@
scarb 0.7.0
scarb 2.6.5
+scarb 2.8.3
+starknet-foundry 0.31.0
diff --git a/.trunk/configs/.codespellrc b/.trunk/configs/.codespellrc
new file mode 100644
index 000000000..e4a1d2d22
--- /dev/null
+++ b/.trunk/configs/.codespellrc
@@ -0,0 +1,5 @@
+[codespell]
+ignore-words-list = keypair,astroid,uvloop,crate
+skip = '.git'
+check-filenames =
+check-hidden =
diff --git a/.trunk/trunk.yaml b/.trunk/trunk.yaml
index 14f8bc7c8..3a5423cb0 100644
--- a/.trunk/trunk.yaml
+++ b/.trunk/trunk.yaml
@@ -67,9 +67,14 @@ lint:
paths: [tests, docker]
- linters: [codespell]
paths: [uv.lock]
+ - linters: [cairo]
+ paths:
+ - cairo/mock_pragma
+ - cairo/token
+ - cairo/utils
+ - cairo/kakarot-ssj/crates
- linters: [ALL]
paths:
- - cairo1_contracts
- logs*
- lib*
- resources*
diff --git a/Makefile b/Makefile
index 5e21574ab..49593abcf 100644
--- a/Makefile
+++ b/Makefile
@@ -6,44 +6,49 @@ endif
.PHONY: build test coverage clean
-# 176384150 corresponds to release v0.1.13 of Kakarot SSJ.
-KKRT_SSJ_RELEASE_ID = 176384150
-# Kakarot SSJ artifacts for precompiles.
-KKRT_SSJ_BUILD_ARTIFACT_URL = $(shell curl -L https://api.github.com/repos/kkrt-labs/kakarot-ssj/releases/${KKRT_SSJ_RELEASE_ID} | jq -r '.assets[0].browser_download_url')
KATANA_VERSION = v1.0.0-alpha.14
-ROOT_DIR:=$(shell dirname $(realpath $(lastword $(MAKEFILE_LIST))))
BUILD_DIR = build
SSJ_DIR = $(BUILD_DIR)/ssj
-SSJ_ZIP = dev-artifacts.zip
-build: $(SSJ_DIR)
+build-ssj:
+ @echo "Building Kakarot SSJ"
+ @mkdir -p $(SSJ_DIR)
+ @cd cairo/kakarot-ssj && scarb build -p contracts && find target/dev -type f -name '*contracts*' | grep -vE 'test|mock|Mock' | xargs -I {} cp {} ../../$(SSJ_DIR)
+
+build: build-ssj
uv run compile
deploy: build build-sol
uv run deploy
-$(SSJ_DIR): $(SSJ_ZIP)
- rm -rf $(SSJ_DIR)
- mkdir -p $(SSJ_DIR)
- unzip -o $(SSJ_ZIP) -d $(SSJ_DIR)
- rm -f $(SSJ_ZIP)
-
-$(SSJ_ZIP):
- curl -sL -o $(SSJ_ZIP) "$(KKRT_SSJ_BUILD_ARTIFACT_URL)"
-
fetch-ef-tests:
- poetry run python ./kakarot_scripts/ef_tests/fetch.py
+ uv run ef_tests
setup:
uv sync --all-extras --dev
-test: deploy
- uv run pytest tests/src -m "not NoCI" --log-cli-level=INFO -n logical --seed 42
+test-cairo-zero: deploy
+ uv run pytest cairo_zero/tests/src -m "not NoCI" --log-cli-level=INFO -n logical --seed 42
uv run pytest tests/end_to_end --seed 42
-test-unit: build-sol
- uv run pytest tests/src -m "not NoCI" -n logical --seed 42
+test-unit-cairo-zero: build-sol
+ uv run pytest cairo_zero/tests/src -m "not NoCI" -n logical --seed 42
+
+test-unit-cairo:
+ @PACKAGE="$(word 2,$(MAKECMDGOALS))" && \
+ FILTER="$(word 3,$(MAKECMDGOALS))" && cd cairo/kakarot-ssj && \
+ if [ -z "$$PACKAGE" ] && [ -z "$$FILTER" ]; then \
+ scarb test; \
+ elif [ -n "$$PACKAGE" ] && [ -z "$$FILTER" ]; then \
+ scarb test -p $$PACKAGE; \
+ elif [ -n "$$PACKAGE" ] && [ -n "$$FILTER" ]; then \
+ uv run scripts/run_filtered_tests.py scarb test -p $$PACKAGE $$FILTER; \
+ else \
+ echo "Usage: make test-unit-cairo [PACKAGE] [FILTER]"; \
+ exit 1; \
+ fi
+
test-end-to-end: deploy
uv run pytest tests/end_to_end --seed 42
diff --git a/audits/Kakarot EVM - Zellic Audit Report.pdf b/audits/cairo_zero/Kakarot EVM - Zellic Audit Report.pdf
similarity index 100%
rename from audits/Kakarot EVM - Zellic Audit Report.pdf
rename to audits/cairo_zero/Kakarot EVM - Zellic Audit Report.pdf
diff --git a/cairo/kakarot-ssj/.all-contributorsrc b/cairo/kakarot-ssj/.all-contributorsrc
new file mode 100644
index 000000000..5ab9a148a
--- /dev/null
+++ b/cairo/kakarot-ssj/.all-contributorsrc
@@ -0,0 +1,189 @@
+{
+ "projectName": "kakarot-ssj",
+ "projectOwner": "sayajin-labs",
+ "repoType": "github",
+ "repoHost": "https://github.com",
+ "files": ["README.md"],
+ "imageSize": 100,
+ "commit": true,
+ "commitConvention": "gitmoji",
+ "contributors": [
+ {
+ "login": "abdelhamidbakhta",
+ "name": "Abdel @ StarkWare ",
+ "avatar_url": "https://avatars.githubusercontent.com/u/45264458?v=4",
+ "profile": "https://github.com/abdelhamidbakhta",
+ "contributions": ["code"]
+ },
+ {
+ "login": "jobez",
+ "name": "johann bestowrous",
+ "avatar_url": "https://avatars.githubusercontent.com/u/615197?v=4",
+ "profile": "https://github.com/jobez",
+ "contributions": ["code"]
+ },
+ {
+ "login": "Eikix",
+ "name": "Elias Tazartes",
+ "avatar_url": "https://avatars.githubusercontent.com/u/66871571?v=4",
+ "profile": "https://github.com/Eikix",
+ "contributions": ["review", "tutorial", "talk"]
+ },
+ {
+ "login": "enitrat",
+ "name": "Mathieu",
+ "avatar_url": "https://avatars.githubusercontent.com/u/60658558?v=4",
+ "profile": "https://github.com/enitrat",
+ "contributions": ["code", "test", "doc"]
+ },
+ {
+ "login": "khaeljy",
+ "name": "khaeljy",
+ "avatar_url": "https://avatars.githubusercontent.com/u/1810456?v=4",
+ "profile": "https://github.com/khaeljy",
+ "contributions": ["code"]
+ },
+ {
+ "login": "ClementWalter",
+ "name": "ClΓ©ment Walter",
+ "avatar_url": "https://avatars.githubusercontent.com/u/18620296?v=4",
+ "profile": "https://www.linkedin.com/in/clementwalter/",
+ "contributions": ["code"]
+ },
+ {
+ "login": "LucasLvy",
+ "name": "Lucas @ StarkWare",
+ "avatar_url": "https://avatars.githubusercontent.com/u/70894690?v=4",
+ "profile": "https://github.com/LucasLvy",
+ "contributions": ["code"]
+ },
+ {
+ "login": "lambda-0x",
+ "name": "lambda-0x",
+ "avatar_url": "https://avatars.githubusercontent.com/u/87354252?v=4",
+ "profile": "https://github.com/lambda-0x",
+ "contributions": ["code"]
+ },
+ {
+ "login": "danilowhk",
+ "name": "danilowhk",
+ "avatar_url": "https://avatars.githubusercontent.com/u/12735159?v=4",
+ "profile": "https://github.com/danilowhk",
+ "contributions": ["code"]
+ },
+ {
+ "login": "TAdev0",
+ "name": "Tristan",
+ "avatar_url": "https://avatars.githubusercontent.com/u/122918260?v=4",
+ "profile": "https://github.com/TAdev0",
+ "contributions": ["code"]
+ },
+ {
+ "login": "Quentash",
+ "name": "Quentash",
+ "avatar_url": "https://avatars.githubusercontent.com/u/100387965?v=4",
+ "profile": "https://github.com/Quentash",
+ "contributions": ["code"]
+ },
+ {
+ "login": "ftupas",
+ "name": "ftupas",
+ "avatar_url": "https://avatars.githubusercontent.com/u/35031356?v=4",
+ "profile": "https://github.com/ftupas",
+ "contributions": ["code"]
+ },
+ {
+ "login": "aniketpr01",
+ "name": "Aniket Prajapati",
+ "avatar_url": "https://avatars.githubusercontent.com/u/46114123?v=4",
+ "profile": "https://aniketpr01.github.io/",
+ "contributions": ["code"]
+ },
+ {
+ "login": "dbejarano820",
+ "name": "Daniel Bejarano",
+ "avatar_url": "https://avatars.githubusercontent.com/u/58019353?v=4",
+ "profile": "https://github.com/dbejarano820",
+ "contributions": ["code"]
+ },
+ {
+ "login": "Noeljarillo",
+ "name": "Noeljarillo",
+ "avatar_url": "https://avatars.githubusercontent.com/u/77942794?v=4",
+ "profile": "https://github.com/Noeljarillo",
+ "contributions": ["code"]
+ },
+ {
+ "login": "trbutler4",
+ "name": "Thomas Butler",
+ "avatar_url": "https://avatars.githubusercontent.com/u/58192340?v=4",
+ "profile": "https://github.com/trbutler4",
+ "contributions": ["code"]
+ },
+ {
+ "login": "kariy",
+ "name": "Ammar Arif",
+ "avatar_url": "https://avatars.githubusercontent.com/u/26515232?v=4",
+ "profile": "https://github.com/kariy",
+ "contributions": ["doc"]
+ },
+ {
+ "login": "greged93",
+ "name": "greged93",
+ "avatar_url": "https://avatars.githubusercontent.com/u/82421016?v=4",
+ "profile": "https://github.com/greged93",
+ "contributions": ["code"]
+ },
+ {
+ "login": "chachaleo",
+ "name": "Charlotte",
+ "avatar_url": "https://avatars.githubusercontent.com/u/49371958?v=4",
+ "profile": "https://github.com/chachaleo",
+ "contributions": ["code"]
+ },
+ {
+ "login": "akhercha",
+ "name": "akhercha",
+ "avatar_url": "https://avatars.githubusercontent.com/u/22559023?v=4",
+ "profile": "https://t.me/notaihe",
+ "contributions": ["code"]
+ },
+ {
+ "login": "alextnetto",
+ "name": "Alexandro T. Netto",
+ "avatar_url": "https://avatars.githubusercontent.com/u/56097505?v=4",
+ "profile": "https://github.com/alextnetto",
+ "contributions": ["code"]
+ },
+ {
+ "login": "edisontim",
+ "name": "tedison",
+ "avatar_url": "https://avatars.githubusercontent.com/u/76473430?v=4",
+ "profile": "https://github.com/edisontim",
+ "contributions": ["code"]
+ },
+ {
+ "login": "rkdud007",
+ "name": "Pia",
+ "avatar_url": "https://avatars.githubusercontent.com/u/76558220?v=4",
+ "profile": "https://github.com/rkdud007",
+ "contributions": ["code"]
+ },
+ {
+ "login": "glihm",
+ "name": "glihm",
+ "avatar_url": "https://avatars.githubusercontent.com/u/7962849?v=4",
+ "profile": "https://github.com/glihm",
+ "contributions": ["code"]
+ },
+ {
+ "login": "bajpai244",
+ "name": "Harsh Bajpai",
+ "avatar_url": "https://avatars.githubusercontent.com/u/41180869?v=4",
+ "profile": "https://github.com/bajpai244",
+ "contributions": ["code"]
+ }
+ ],
+ "contributorsPerLine": 7,
+ "linkToUsage": true
+}
diff --git a/cairo/kakarot-ssj/.tool-versions b/cairo/kakarot-ssj/.tool-versions
new file mode 100644
index 000000000..5248f18a0
--- /dev/null
+++ b/cairo/kakarot-ssj/.tool-versions
@@ -0,0 +1,2 @@
+scarb 2.8.3
+starknet-foundry 0.31.0
diff --git a/cairo/kakarot-ssj/README.md b/cairo/kakarot-ssj/README.md
new file mode 100644
index 000000000..196587351
--- /dev/null
+++ b/cairo/kakarot-ssj/README.md
@@ -0,0 +1,247 @@
+
+
+
+
+
+ Kakarot, the zkEVM written in Cairo.
+
+
+
+
+
+
+![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/kkrt-labs/kakarot-ssj/test.yml?branch=main)
+![GitHub](https://img.shields.io/github/license/kkrt-labs/kakarot-ssj?style=flat-square&logo=github)
+![GitHub contributors](https://img.shields.io/github/contributors/kkrt-labs/kakarot-ssj?logo=github&style=flat-square)
+![GitHub top language](https://img.shields.io/github/languages/top/kkrt-labs/kakarot-ssj?style=flat-square)
+[![Telegram](https://img.shields.io/badge/telegram-Kakarot-yellow.svg?logo=telegram)](https://t.me/KakarotZkEvm)
+![Contributions welcome](https://img.shields.io/badge/contributions-welcome-orange.svg)
+[![Read FAQ](https://img.shields.io/badge/Ask%20Question-Read%20FAQ-000000)](https://www.newton.so/view?tags=kakarot)
+![GitHub Repo stars](https://img.shields.io/github/stars/kkrt-labs/kakarot-ssj?style=social)
+[![Twitter Follow](https://img.shields.io/twitter/follow/KakarotZkEvm?style=social)](https://x.com/KakarotZkEvm)
+
+
+
+
+Table of Contents
+
+- [About](#about)
+- [Getting Started](#getting-started)
+ - [Installation](#installation)
+- [Usage](#usage)
+ - [Build](#build)
+ - [Test](#test)
+ - [Format](#format)
+- [Roadmap](#roadmap)
+- [Support](#support)
+- [Project assistance](#project-assistance)
+- [Contributing](#contributing)
+- [Authors \& contributors](#authors--contributors)
+- [Security](#security)
+- [License](#license)
+- [Acknowledgements](#acknowledgements)
+- [Contributors β¨](#contributors-)
+
+
+
+---
+
+## About
+
+Kakarot is an (zk)-Ethereum Virtual Machine implementation written in Cairo.
+Kakarot is Ethereum compatible, i.e. all existing smart contracts, developer
+tools and wallets work out-of-the-box on Kakarot. It's been open source from day
+one. Soon available on Starknet L2 and Appchains.
+
+π§ It is a work in progress, and it is not ready for production.
+
+## Getting Started
+
+This repository is a Cairo rewrite of
+[the CairoZero version of Kakarot zkEVM](https://github.com/kkrt-labs/kakarot).
+
+### Installation
+
+- Install [Scarb](https://docs.swmansion.com/scarb). To make sure your version
+ always matches the one used by Kakarot, you can install Scarb
+ [via asdf](https://docs.swmansion.com/scarb/download#install-via-asdf).
+
+- Install
+ [Starknet Foundry](https://foundry-rs.github.io/starknet-foundry/getting-started/installation.html?highlight=asdf#installation-via-asdf).
+ We also recommend installing it via asdf.
+
+- Install the
+ [Cairo Profiler](https://github.com/software-mansion/cairo-profiler) to
+ profile your Cairo code.
+
+- Install [Bun](https://bun.sh/docs/installation) to run the JavaScript scripts.
+
+- [Fork](https://docs.github.com/en/get-started/quickstart/fork-a-repo) the
+ repository and clone your fork
+ (`git clone https://github.com//kakarot-ssj`)
+
+- Run `make install` to install the git hooks.
+- Add your environment variables to `.env` (see `.env.example` for an example).
+ - Get your Github token [here](https://github.com/settings/tokens?type=beta)
+
+## Usage
+
+### Build
+
+```bash
+scarb build
+```
+
+### Test
+
+```bash
+scarb test
+```
+
+### Format
+
+The project uses [trunk](https://trunk.io/) for everything except cairo files.
+If you don't have it installed already, you can do:
+
+```bash
+curl https://get.trunk.io -fsSL | bash
+```
+
+then
+
+```bash
+trunk check --fix
+```
+
+VS Code users, don't miss the
+[VS Code trunk plugin](https://marketplace.visualstudio.com/items?itemName=Trunk.io).
+
+For cairo files, run:
+
+```bash
+scarb fmt
+```
+
+## Roadmap
+
+See the [open issues](https://github.com/kkrt-labs/kakarot-ssj/issues) for a
+list of proposed features (and known issues).
+
+- [Top Feature Requests](https://github.com/kkrt-labs/kakarot-ssj/issues?q=label%3Aenhancement+is%3Aopen+sort%3Areactions-%2B1-desc)
+ (Add your votes using the π reaction)
+- [Top Bugs](https://github.com/kkrt-labs/kakarot-ssj/issues?q=is%3Aissue+is%3Aopen+label%3Abug+sort%3Areactions-%2B1-desc)
+ (Add your votes using the π reaction)
+- [Newest Bugs](https://github.com/kkrt-labs/kakarot-ssj/issues?q=is%3Aopen+is%3Aissue+label%3Abug)
+
+## Support
+
+Reach out to the maintainer at one of the following places:
+
+- [GitHub Discussions](https://github.com/kkrt-labs/kakarot-ssj/discussions)
+- [Telegram group](https://t.me/KakarotZkEvm)
+
+## Project assistance
+
+If you want to say **thank you** or/and support active development of Kakarot:
+
+- Add a [GitHub Star](https://github.com/kkrt-labs/kakarot-ssj) to the project.
+- Tweet about [Kakarot](https://twitter.com/KakarotZkEvm).
+- Write interesting articles about the project on [Dev.to](https://dev.to/),
+ [Medium](https://medium.com/), [Mirror](https://mirror.xyz/) or your personal
+ blog.
+
+Together, we can make Kakarot **better**!
+
+## Contributing
+
+First off, thanks for taking the time to contribute! Contributions are what make
+the open-source community such an amazing place to learn, inspire, and create.
+Any contribution you make will benefit everybody else and is **greatly
+appreciated**.
+
+Please read [our contribution guidelines](docs/CONTRIBUTING.md), and thank you
+for being involved!
+
+## Authors & contributors
+
+For a full list of all authors and contributors, see
+[the contributors page](https://github.com/kkrt-labs/kakarot-ssj/contributors).
+
+## Security
+
+Kakarot follows good practices of security, but 100% security cannot be assured.
+Kakarot is provided **"as is"** without any **warranty**. Use at your own risk.
+
+_For more information and to report security issues, please refer to our
+[security documentation](docs/SECURITY.md)._
+
+## License
+
+This project is licensed under the **MIT license**.
+
+See [LICENSE](LICENSE) for more information.
+
+## Acknowledgements
+
+## Contributors β¨
+
+Thanks goes to these wonderful people
+([emoji key](https://allcontributors.org/docs/en/emoji-key)):
+
+
+
+
+
+
+
+
+
+
+
+This project follows the
+[all-contributors](https://github.com/all-contributors/all-contributors)
+specification. Contributions of any kind welcome!
diff --git a/cairo/kakarot-ssj/Scarb.lock b/cairo/kakarot-ssj/Scarb.lock
new file mode 100644
index 000000000..8a345708d
--- /dev/null
+++ b/cairo/kakarot-ssj/Scarb.lock
@@ -0,0 +1,74 @@
+# Code generated by scarb DO NOT EDIT.
+version = 1
+
+[[package]]
+name = "alexandria_data_structures"
+version = "0.1.0"
+dependencies = [
+ "snforge_std",
+]
+
+[[package]]
+name = "contracts"
+version = "0.1.0"
+dependencies = [
+ "evm",
+ "openzeppelin",
+ "snforge_std",
+ "snforge_utils",
+ "utils",
+]
+
+[[package]]
+name = "evm"
+version = "0.1.0"
+dependencies = [
+ "contracts",
+ "garaga",
+ "openzeppelin",
+ "snforge_std",
+ "snforge_utils",
+ "utils",
+]
+
+[[package]]
+name = "garaga"
+version = "0.1.0"
+source = "git+https://github.com/keep-starknet-strange/garaga.git#933784eee3811334cf1a5b83d9ffcc9cfda3be8c"
+
+[[package]]
+name = "openzeppelin"
+version = "0.1.0"
+dependencies = [
+ "snforge_std",
+]
+
+[[package]]
+name = "snforge_scarb_plugin"
+version = "0.31.0"
+source = "git+https://github.com/foundry-rs/starknet-foundry.git?tag=v0.31.0#249f1e9960b7ee07d80e12b585ac57b644f9e4c0"
+
+[[package]]
+name = "snforge_std"
+version = "0.31.0"
+source = "git+https://github.com/foundry-rs/starknet-foundry.git?tag=v0.31.0#249f1e9960b7ee07d80e12b585ac57b644f9e4c0"
+dependencies = [
+ "snforge_scarb_plugin",
+]
+
+[[package]]
+name = "snforge_utils"
+version = "0.1.0"
+dependencies = [
+ "evm",
+ "snforge_std",
+]
+
+[[package]]
+name = "utils"
+version = "0.1.0"
+dependencies = [
+ "alexandria_data_structures",
+ "evm",
+ "snforge_std",
+]
diff --git a/cairo/kakarot-ssj/Scarb.toml b/cairo/kakarot-ssj/Scarb.toml
new file mode 100644
index 000000000..b0d420965
--- /dev/null
+++ b/cairo/kakarot-ssj/Scarb.toml
@@ -0,0 +1,17 @@
+[workspace]
+members = ["crates/*"]
+
+[workspace.package]
+description = "Kakarot is an (zk)-Ethereum Virtual Machine implementation written in Cairo."
+documentation = "https://www.kakarot.org/"
+cairo-version = "2.8.2"
+version = "0.1.0"
+readme = "README.md"
+repository = "https://github.com/kkrt-labs/kakarot-ssj/"
+license-file = "LICENSE"
+
+[workspace.dependencies]
+starknet = "2.8.2"
+
+[workspace.tool.fmt]
+sort-module-level-items = true
diff --git a/cairo/kakarot-ssj/blockchain-tests-skip.yml b/cairo/kakarot-ssj/blockchain-tests-skip.yml
new file mode 100644
index 000000000..6d06e0129
--- /dev/null
+++ b/cairo/kakarot-ssj/blockchain-tests-skip.yml
@@ -0,0 +1,913 @@
+# List of test directories to completely skip
+# trunk-ignore(yamllint/empty-values)
+directories:
+
+# List of file names to be skipped
+# The first level corresponds to the directory, the second to the list of file names to ignore.
+# trunk-ignore(yamllint/empty-values)
+filename:
+
+# Keeping some skipped tests because of a probable memory leak somewhere
+# causing cargo test to just crash at some point
+regex:
+ eip4895_withdrawals:
+ - .*
+ eip4844_blobs:
+ - .*
+ eip4788_beacon_root:
+ - .*
+ stEIP4844-blobtransactions:
+ - .*
+
+testname:
+ stEIP1153-transientStorage:
+ - transStorageOK_d3g0v0_Cancun
+ - 15_tstoreCannotBeDosd_d0g0v0_Cancun
+ - 21_tstoreCannotBeDosdOOO_d0g0v0_Cancun
+ eip198_modexp_precompile:
+ - modexp__fork_Cancun_minus_blockchain_test_minus_EIP_minus_198_minus_case3_minus_raw_minus_input_minus_out_minus_of_minus_gas
+ - modexp__fork_Cancun_minus_blockchain_test_minus_EIP_minus_198_minus_case1
+ - modexp__fork_Cancun_minus_blockchain_test_minus_EIP_minus_198_minus_case2
+ - modexp__fork_Cancun_minus_blockchain_test_minus_EIP_minus_198_minus_case4_minus_extra_minus_data_07
+ - modexp__fork_Cancun_minus_blockchain_test_minus_EIP_minus_198_minus_case5_minus_raw_minus_input
+ - modexp__fork_Cancun_minus_blockchain_test_minus_ModExpInput_base_00_minus_exponent_00_minus_modulus_02_minus_ExpectedOutput_call_return_code_0x01_minus_returned_data_0x01
+ - modexp__fork_Cancun_minus_blockchain_test_minus_ModExpInput_base_01_minus_exponent_01_minus_modulus_02_minus_ExpectedOutput_call_return_code_0x01_minus_returned_data_0x01
+ - modexp__fork_Cancun_minus_blockchain_test_minus_ModExpInput_base_02_minus_exponent_01_minus_modulus_03_minus_ExpectedOutput_call_return_code_0x01_minus_returned_data_0x02
+ - modexp__fork_Cancun_minus_blockchain_test_minus_ModExpInput_base_02_minus_exponent_02_minus_modulus_05_minus_ExpectedOutput_call_return_code_0x01_minus_returned_data_0x04
+ - modexp__fork_Cancun_minus_blockchain_test_minus_ModExpInput_base__minus_exponent_01_minus_modulus_02_minus_ExpectedOutput_call_return_code_0x01_minus_returned_data_0x00
+ - modexp__fork_Cancun_minus_blockchain_test_minus_ModExpInput_base__minus_exponent__minus_modulus_0001_minus_ExpectedOutput_call_return_code_0x01_minus_returned_data_0x0000
+ - modexp__fork_Cancun_minus_blockchain_test_minus_ModExpInput_base__minus_exponent__minus_modulus_0002_minus_ExpectedOutput_call_return_code_0x01_minus_returned_data_0x0001
+ - modexp__fork_Cancun_minus_blockchain_test_minus_ModExpInput_base__minus_exponent__minus_modulus_00_minus_ExpectedOutput_call_return_code_0x01_minus_returned_data_0x00
+ - modexp__fork_Cancun_minus_blockchain_test_minus_ModExpInput_base__minus_exponent__minus_modulus_01_minus_ExpectedOutput_call_return_code_0x01_minus_returned_data_0x00
+ - modexp__fork_Cancun_minus_blockchain_test_minus_ModExpInput_base__minus_exponent__minus_modulus_02_minus_ExpectedOutput_call_return_code_0x01_minus_returned_data_0x01
+ - modexp__fork_Cancun_minus_blockchain_test_minus_ModExpInput_base__minus_exponent__minus_modulus__minus_ExpectedOutput_call_return_code_0x01_minus_returned_data_0x
+ initcode:
+ - contract_creating_tx__fork_Cancun_minus_blockchain_test_minus_over_limit_ones
+ - contract_creating_tx__fork_Cancun_minus_blockchain_test_minus_over_limit_zeros
+ stEIP3651-warmcoinbase:
+ - coinbaseWarmAccountCallGasFail_d0g0v0_Cancun
+ vmIOandFlowOperations:
+ - jump_d5g0v0_Cancun
+ - jumpi_d9g0v0_Cancun
+ vmLogTest:
+ - log0_d0g0v0_Cancun
+ - log1_d0g0v0_Cancun
+ - log2_d0g0v0_Cancun
+ - log3_d0g0v0_Cancun
+ - log4_d0g0v0_Cancun
+ vmPerformance:
+ - loopExp_d2g0v0_Cancun
+ - performanceTester_d0g0v0_Cancun
+ - performanceTester_d2g0v0_Cancun
+ - performanceTester_d3g0v0_Cancun
+ - loopExp_d10g0v0_Cancun
+ - loopExp_d11g0v0_Cancun
+ - loopExp_d12g0v0_Cancun
+ - loopExp_d13g0v0_Cancun
+ - loopExp_d14g0v0_Cancun
+ - loopExp_d8g0v0_Cancun
+ - loopExp_d9g0v0_Cancun
+ - loopMul_d0g0v0_Cancun
+ - loopMul_d1g0v0_Cancun
+ - loopMul_d2g0v0_Cancun
+ - performanceTester_d1g0v0_Cancun
+ - performanceTester_d4g0v0_Cancun
+ vmTests:
+ - blockInfo_d2g0v0_Cancun
+ stBadOpcode:
+ - invalidDiffPlaces_d34g0v0_Cancun
+ - opc0CDiffPlaces_d34g0v0_Cancun
+ - opc0DDiffPlaces_d34g0v0_Cancun
+ - opc0EDiffPlaces_d34g0v0_Cancun
+ - opc0FDiffPlaces_d34g0v0_Cancun
+ - opc1EDiffPlaces_d34g0v0_Cancun
+ - opc1FDiffPlaces_d34g0v0_Cancun
+ - opc21DiffPlaces_d34g0v0_Cancun
+ - opc22DiffPlaces_d34g0v0_Cancun
+ - opc23DiffPlaces_d34g0v0_Cancun
+ - opc24DiffPlaces_d34g0v0_Cancun
+ - opc25DiffPlaces_d34g0v0_Cancun
+ - opc26DiffPlaces_d34g0v0_Cancun
+ - opc27DiffPlaces_d34g0v0_Cancun
+ - opc28DiffPlaces_d34g0v0_Cancun
+ - opc29DiffPlaces_d34g0v0_Cancun
+ - opc2ADiffPlaces_d34g0v0_Cancun
+ - opc2BDiffPlaces_d34g0v0_Cancun
+ - opc2CDiffPlaces_d34g0v0_Cancun
+ - opc2DDiffPlaces_d34g0v0_Cancun
+ - opc2EDiffPlaces_d34g0v0_Cancun
+ - opc2FDiffPlaces_d34g0v0_Cancun
+ - opc49DiffPlaces_d34g0v0_Cancun
+ - opc4ADiffPlaces_d34g0v0_Cancun
+ - opc4BDiffPlaces_d34g0v0_Cancun
+ - opc4CDiffPlaces_d34g0v0_Cancun
+ - opc4DDiffPlaces_d34g0v0_Cancun
+ - opc4EDiffPlaces_d34g0v0_Cancun
+ - opc4FDiffPlaces_d34g0v0_Cancun
+ - opc5CDiffPlaces_d34g0v0_Cancun
+ - opc5DDiffPlaces_d34g0v0_Cancun
+ - opc5EDiffPlaces_d34g0v0_Cancun
+ - opc5FDiffPlaces_d34g0v0_Cancun
+ - opcA5DiffPlaces_d34g0v0_Cancun
+ - opcA6DiffPlaces_d34g0v0_Cancun
+ - opcA7DiffPlaces_d34g0v0_Cancun
+ - opcA8DiffPlaces_d34g0v0_Cancun
+ - opcA9DiffPlaces_d34g0v0_Cancun
+ - opcAADiffPlaces_d34g0v0_Cancun
+ - opcABDiffPlaces_d34g0v0_Cancun
+ - opcACDiffPlaces_d34g0v0_Cancun
+ - opcADDiffPlaces_d34g0v0_Cancun
+ - opcAEDiffPlaces_d34g0v0_Cancun
+ - opcAFDiffPlaces_d34g0v0_Cancun
+ - opcB0DiffPlaces_d34g0v0_Cancun
+ - opcB1DiffPlaces_d34g0v0_Cancun
+ - opcB2DiffPlaces_d34g0v0_Cancun
+ - opcB3DiffPlaces_d34g0v0_Cancun
+ - opcB4DiffPlaces_d34g0v0_Cancun
+ - opcB5DiffPlaces_d34g0v0_Cancun
+ - opcB6DiffPlaces_d34g0v0_Cancun
+ - opcB7DiffPlaces_d34g0v0_Cancun
+ - opcB8DiffPlaces_d34g0v0_Cancun
+ - opcB9DiffPlaces_d34g0v0_Cancun
+ - opcBADiffPlaces_d34g0v0_Cancun
+ - opcBBDiffPlaces_d34g0v0_Cancun
+ - opcBCDiffPlaces_d34g0v0_Cancun
+ - opcBDDiffPlaces_d34g0v0_Cancun
+ - opcBEDiffPlaces_d34g0v0_Cancun
+ - opcBFDiffPlaces_d34g0v0_Cancun
+ - opcC0DiffPlaces_d34g0v0_Cancun
+ - opcC1DiffPlaces_d34g0v0_Cancun
+ - opcC2DiffPlaces_d34g0v0_Cancun
+ - opcC3DiffPlaces_d34g0v0_Cancun
+ - opcC4DiffPlaces_d34g0v0_Cancun
+ - opcC5DiffPlaces_d34g0v0_Cancun
+ - opcC6DiffPlaces_d34g0v0_Cancun
+ - opcC7DiffPlaces_d34g0v0_Cancun
+ - opcC8DiffPlaces_d34g0v0_Cancun
+ - opcC9DiffPlaces_d34g0v0_Cancun
+ - opcCADiffPlaces_d34g0v0_Cancun
+ - opcCBDiffPlaces_d34g0v0_Cancun
+ - opcCCDiffPlaces_d34g0v0_Cancun
+ - opcCDDiffPlaces_d34g0v0_Cancun
+ - opcCEDiffPlaces_d34g0v0_Cancun
+ - opcCFDiffPlaces_d34g0v0_Cancun
+ - opcD0DiffPlaces_d34g0v0_Cancun
+ - opcD1DiffPlaces_d34g0v0_Cancun
+ - opcD2DiffPlaces_d34g0v0_Cancun
+ - opcD3DiffPlaces_d34g0v0_Cancun
+ - opcD4DiffPlaces_d34g0v0_Cancun
+ - opcD5DiffPlaces_d34g0v0_Cancun
+ - opcD6DiffPlaces_d34g0v0_Cancun
+ - opcD7DiffPlaces_d34g0v0_Cancun
+ - opcD8DiffPlaces_d34g0v0_Cancun
+ - opcD9DiffPlaces_d34g0v0_Cancun
+ - opcDADiffPlaces_d34g0v0_Cancun
+ - opcDBDiffPlaces_d34g0v0_Cancun
+ - opcDCDiffPlaces_d34g0v0_Cancun
+ - opcDDDiffPlaces_d34g0v0_Cancun
+ - opcDEDiffPlaces_d34g0v0_Cancun
+ - opcDFDiffPlaces_d34g0v0_Cancun
+ - opcE0DiffPlaces_d34g0v0_Cancun
+ - opcE1DiffPlaces_d34g0v0_Cancun
+ - opcE2DiffPlaces_d34g0v0_Cancun
+ - opcE3DiffPlaces_d34g0v0_Cancun
+ - opcE4DiffPlaces_d34g0v0_Cancun
+ - opcE5DiffPlaces_d34g0v0_Cancun
+ - opcE6DiffPlaces_d34g0v0_Cancun
+ - opcE7DiffPlaces_d34g0v0_Cancun
+ - opcE8DiffPlaces_d34g0v0_Cancun
+ - opcE9DiffPlaces_d34g0v0_Cancun
+ - opcEADiffPlaces_d34g0v0_Cancun
+ - opcEBDiffPlaces_d34g0v0_Cancun
+ - opcECDiffPlaces_d34g0v0_Cancun
+ - opcEDDiffPlaces_d34g0v0_Cancun
+ - opcEEDiffPlaces_d34g0v0_Cancun
+ - opcEFDiffPlaces_d34g0v0_Cancun
+ - opcF6DiffPlaces_d34g0v0_Cancun
+ - opcF7DiffPlaces_d34g0v0_Cancun
+ - opcF8DiffPlaces_d34g0v0_Cancun
+ - opcF9DiffPlaces_d34g0v0_Cancun
+ - opcFBDiffPlaces_d34g0v0_Cancun
+ - opcFCDiffPlaces_d34g0v0_Cancun
+ - opcFEDiffPlaces_d34g0v0_Cancun
+ - operationDiffGas_d0g0v0_Cancun
+ - operationDiffGas_d1g0v0_Cancun
+ stEIP3860-limitmeterinitcode:
+ - creationTxInitCodeSizeLimit_d1g0v0_Cancun
+ stCallCreateCallCodeTest:
+ - Call1024BalanceTooLow_d0g0v0_Cancun
+ - CallRecursiveBombPreCall_d0g0v0_Cancun
+ - Callcode1024BalanceTooLow_d0g0v0_Cancun
+ - createInitFailStackSizeLargerThan1024_d0g0v0_Cancun
+ - Call1024PreCalls_d0g0v0_Cancun
+ - Call1024PreCalls_d0g1v0_Cancun
+ stCreate2:
+ - Create2OnDepth1023_d0g0v0_Cancun
+ - Create2OnDepth1024_d0g0v0_Cancun
+ - create2callPrecompiles_d2g0v0_Cancun
+ - RevertInCreateInInitCreate2Paris_d0g0v0_Cancun
+ - create2collisionStorageParis_d0g0v0_Cancun
+ - create2collisionStorageParis_d1g0v0_Cancun
+ - create2collisionStorageParis_d2g0v0_Cancun
+ stCreateTest:
+ - CreateOOGafterMaxCodesize_d2g0v0_Cancun
+ - CreateOOGafterMaxCodesize_d3g0v0_Cancun
+ - CreateOOGafterMaxCodesize_d5g0v0_Cancun
+ stEIP150Specific:
+ - Transaction64Rule_integerBoundaries_d0g0v0_Cancun
+ - Transaction64Rule_integerBoundaries_d10g0v0_Cancun
+ - Transaction64Rule_integerBoundaries_d11g0v0_Cancun
+ - Transaction64Rule_integerBoundaries_d1g0v0_Cancun
+ - Transaction64Rule_integerBoundaries_d2g0v0_Cancun
+ - Transaction64Rule_integerBoundaries_d3g0v0_Cancun
+ - Transaction64Rule_integerBoundaries_d4g0v0_Cancun
+ - Transaction64Rule_integerBoundaries_d5g0v0_Cancun
+ - Transaction64Rule_integerBoundaries_d6g0v0_Cancun
+ - Transaction64Rule_integerBoundaries_d7g0v0_Cancun
+ - Transaction64Rule_integerBoundaries_d8g0v0_Cancun
+ - Transaction64Rule_integerBoundaries_d9g0v0_Cancun
+ stDelegatecallTestHomestead:
+ - Call1024BalanceTooLow_d0g0v0_Cancun
+ - CallRecursiveBombPreCall_d0g0v0_Cancun
+ - Delegatecall1024_d0g0v0_Cancun
+ - Call1024PreCalls_d0g0v0_Cancun
+ - Call1024PreCalls_d0g1v0_Cancun
+ - Call1024PreCalls_d0g2v0_Cancun
+ stEIP150singleCodeGasPrices:
+ - gasCostJump_d0g0v0_Cancun
+ - gasCostJump_d1g0v0_Cancun
+ - gasCostJump_d2g0v0_Cancun
+ - gasCostReturn_d0g0v0_Cancun
+ stEIP1559:
+ - baseFeeDiffPlaces_d34g0v0_Cancun
+ - gasPriceDiffPlaces_d34g0v0_Cancun
+ stExtCodeHash:
+ - extCodeHashDynamicArgument_d1g0v0_Cancun
+ - dynamicAccountOverwriteEmpty_Paris_d0g0v0_Cancun
+ stMemoryTest:
+ - stackLimitGas_1023_d0g0v0_Cancun
+ - stackLimitGas_1024_d0g0v0_Cancun
+ - stackLimitGas_1025_d0g0v0_Cancun
+ - stackLimitPush31_1023_d0g0v0_Cancun
+ - stackLimitPush31_1024_d0g0v0_Cancun
+ - stackLimitPush31_1025_d0g0v0_Cancun
+ - stackLimitPush32_1023_d0g0v0_Cancun
+ - stackLimitPush32_1024_d0g0v0_Cancun
+ - stackLimitPush32_1025_d0g0v0_Cancun
+ stPreCompiledContracts2:
+ - CALLCODERipemd160_0_d0g0v0_Cancun
+ - CALLCODERipemd160_1_d0g0v0_Cancun
+ - CALLCODERipemd160_2_d0g0v0_Cancun
+ - CALLCODERipemd160_3_d0g0v0_Cancun
+ - CALLCODERipemd160_3_postfixed0_d0g0v0_Cancun
+ - CALLCODERipemd160_3_prefixed0_d0g0v0_Cancun
+ - CALLCODERipemd160_4_d0g0v0_Cancun
+ - CALLCODERipemd160_4_gas719_d0g0v0_Cancun
+ - CallRipemd160_0_d0g0v0_Cancun
+ - CallRipemd160_1_d0g0v0_Cancun
+ - CallRipemd160_2_d0g0v0_Cancun
+ - CallRipemd160_3_d0g0v0_Cancun
+ - CallRipemd160_3_postfixed0_d0g0v0_Cancun
+ - CallRipemd160_3_prefixed0_d0g0v0_Cancun
+ - CallRipemd160_4_d0g0v0_Cancun
+ - CallRipemd160_4_gas719_d0g0v0_Cancun
+ - ecrecoverShortBuff_d0g0v0_Cancun
+ - modexpRandomInput_d2g0v0_Cancun
+ - modexpRandomInput_d2g1v0_Cancun
+ - CALLBlake2f_d9g0v0_Cancun
+ - CALLBlake2f_d9g1v0_Cancun
+ - CALLBlake2f_d9g2v0_Cancun
+ - CALLBlake2f_d9g3v0_Cancun
+ - CALLCODEBlake2f_d9g0v0_Cancun
+ - CALLCODEBlake2f_d9g1v0_Cancun
+ - CALLCODEBlake2f_d9g2v0_Cancun
+ - CALLCODEBlake2f_d9g3v0_Cancun
+ - CALLCODERipemd160_5_d0g0v0_Cancun
+ - CallEcrecover_Overflow_d0g0v0_Cancun
+ - CallEcrecover_Overflow_d4g0v0_Cancun
+ - CallEcrecover_Overflow_d5g0v0_Cancun
+ - CallRipemd160_5_d0g0v0_Cancun
+ stPreCompiledContracts:
+ - idPrecomps_d2g0v0_Cancun
+ - idPrecomps_d7g0v0_Cancun
+ - idPrecomps_d9g0v0_Cancun
+ - precompsEIP2929Cancun_d110g0v0_Cancun
+ - precompsEIP2929Cancun_d117g0v0_Cancun
+ - precompsEIP2929Cancun_d128g0v0_Cancun
+ - precompsEIP2929Cancun_d12g0v0_Cancun
+ - precompsEIP2929Cancun_d135g0v0_Cancun
+ - precompsEIP2929Cancun_d146g0v0_Cancun
+ - precompsEIP2929Cancun_d151g0v0_Cancun
+ - precompsEIP2929Cancun_d153g0v0_Cancun
+ - precompsEIP2929Cancun_d164g0v0_Cancun
+ - precompsEIP2929Cancun_d169g0v0_Cancun
+ - precompsEIP2929Cancun_d171g0v0_Cancun
+ - precompsEIP2929Cancun_d182g0v0_Cancun
+ - precompsEIP2929Cancun_d189g0v0_Cancun
+ - precompsEIP2929Cancun_d200g0v0_Cancun
+ - precompsEIP2929Cancun_d207g0v0_Cancun
+ - precompsEIP2929Cancun_d20g0v0_Cancun
+ - precompsEIP2929Cancun_d218g0v0_Cancun
+ - precompsEIP2929Cancun_d225g0v0_Cancun
+ - precompsEIP2929Cancun_d236g0v0_Cancun
+ - precompsEIP2929Cancun_d241g0v0_Cancun
+ - precompsEIP2929Cancun_d243g0v0_Cancun
+ - precompsEIP2929Cancun_d254g0v0_Cancun
+ - precompsEIP2929Cancun_d261g0v0_Cancun
+ - precompsEIP2929Cancun_d272g0v0_Cancun
+ - precompsEIP2929Cancun_d279g0v0_Cancun
+ - precompsEIP2929Cancun_d27g0v0_Cancun
+ - precompsEIP2929Cancun_d290g0v0_Cancun
+ - precompsEIP2929Cancun_d295g0v0_Cancun
+ - precompsEIP2929Cancun_d297g0v0_Cancun
+ - precompsEIP2929Cancun_d308g0v0_Cancun
+ - precompsEIP2929Cancun_d315g0v0_Cancun
+ - precompsEIP2929Cancun_d38g0v0_Cancun
+ - precompsEIP2929Cancun_d43g0v0_Cancun
+ - precompsEIP2929Cancun_d45g0v0_Cancun
+ - precompsEIP2929Cancun_d56g0v0_Cancun
+ - precompsEIP2929Cancun_d5g0v0_Cancun
+ - precompsEIP2929Cancun_d61g0v0_Cancun
+ - precompsEIP2929Cancun_d63g0v0_Cancun
+ - precompsEIP2929Cancun_d74g0v0_Cancun
+ - precompsEIP2929Cancun_d81g0v0_Cancun
+ - precompsEIP2929Cancun_d92g0v0_Cancun
+ - precompsEIP2929Cancun_d99g0v0_Cancun
+ - modexp_d11g0v0_Cancun
+ - modexp_d11g1v0_Cancun
+ - modexp_d11g2v0_Cancun
+ - modexp_d11g3v0_Cancun
+ - modexp_d14g0v0_Cancun
+ - modexp_d14g1v0_Cancun
+ - modexp_d14g2v0_Cancun
+ - modexp_d14g3v0_Cancun
+ - modexp_d16g0v0_Cancun
+ - modexp_d16g1v0_Cancun
+ - modexp_d16g2v0_Cancun
+ - modexp_d16g3v0_Cancun
+ - modexp_d17g0v0_Cancun
+ - modexp_d17g1v0_Cancun
+ - modexp_d17g2v0_Cancun
+ - modexp_d17g3v0_Cancun
+ - modexp_d25g0v0_Cancun
+ - modexp_d25g1v0_Cancun
+ - modexp_d25g2v0_Cancun
+ - modexp_d25g3v0_Cancun
+ - modexp_d26g0v0_Cancun
+ - modexp_d26g1v0_Cancun
+ - modexp_d26g2v0_Cancun
+ - modexp_d26g3v0_Cancun
+ - modexp_d27g0v0_Cancun
+ - modexp_d27g1v0_Cancun
+ - modexp_d27g2v0_Cancun
+ - modexp_d27g3v0_Cancun
+ - modexp_d29g0v0_Cancun
+ - modexp_d29g1v0_Cancun
+ - modexp_d29g2v0_Cancun
+ - modexp_d29g3v0_Cancun
+ - modexp_d30g0v0_Cancun
+ - modexp_d30g1v0_Cancun
+ - modexp_d30g2v0_Cancun
+ - modexp_d30g3v0_Cancun
+ - modexp_d33g0v0_Cancun
+ - modexp_d33g1v0_Cancun
+ - modexp_d33g2v0_Cancun
+ - modexp_d33g3v0_Cancun
+ - precompsEIP2929Cancun_d10g0v0_Cancun
+ - precompsEIP2929Cancun_d115g0v0_Cancun
+ - precompsEIP2929Cancun_d133g0v0_Cancun
+ - precompsEIP2929Cancun_d187g0v0_Cancun
+ - precompsEIP2929Cancun_d205g0v0_Cancun
+ - precompsEIP2929Cancun_d223g0v0_Cancun
+ - precompsEIP2929Cancun_d259g0v0_Cancun
+ - precompsEIP2929Cancun_d25g0v0_Cancun
+ - precompsEIP2929Cancun_d277g0v0_Cancun
+ - precompsEIP2929Cancun_d313g0v0_Cancun
+ - precompsEIP2929Cancun_d79g0v0_Cancun
+ - precompsEIP2929Cancun_d97g0v0_Cancun
+ stQuadraticComplexityTest:
+ - Call20KbytesContract50_1_d0g0v0_Cancun
+ - Call20KbytesContract50_2_d0g0v0_Cancun
+ - Call20KbytesContract50_3_d0g0v0_Cancun
+ - Call20KbytesContract50_3_d0g1v0_Cancun
+ - Call1MB1024Calldepth_d0g1v0_Cancun
+ - Call20KbytesContract50_1_d0g1v0_Cancun
+ - Call20KbytesContract50_2_d0g1v0_Cancun
+ - Call50000_d0g1v0_Cancun
+ - Call50000_ecrec_d0g1v0_Cancun
+ - Call50000_identity2_d0g1v0_Cancun
+ - Call50000_identity_d0g1v0_Cancun
+ - Call50000_rip160_d0g0v0_Cancun
+ - Call50000_rip160_d0g1v0_Cancun
+ - Call50000_sha256_d0g1v0_Cancun
+ - Callcode50000_d0g1v0_Cancun
+ - QuadraticComplexitySolidity_CallDataCopy_d0g1v0_Cancun
+ - Return50000_2_d0g0v0_Cancun
+ - Return50000_2_d0g1v0_Cancun
+ - Return50000_d0g0v0_Cancun
+ - Return50000_d0g1v0_Cancun
+ stRandom2:
+ - randomStatetest415_d0g0v0_Cancun
+ - randomStatetest418_d0g0v0_Cancun
+ - randomStatetest433_d0g0v0_Cancun
+ - randomStatetest458_d0g0v0_Cancun
+ - randomStatetest467_d0g0v0_Cancun
+ - randomStatetest469_d0g0v0_Cancun
+ - randomStatetest547_d0g0v0_Cancun
+ - randomStatetest554_d0g0v0_Cancun
+ - randomStatetest560_d0g0v0_Cancun
+ - randomStatetest583_d0g0v0_Cancun
+ - randomStatetest636_d0g0v0_Cancun
+ - randomStatetest639_d0g0v0_Cancun
+ - randomStatetest645_d0g0v0_Cancun
+ - randomStatetest645_d0g0v1_Cancun
+ - randomStatetest476_d0g0v0_Cancun
+ stRandom:
+ - randomStatetest111_d0g0v0_Cancun
+ - randomStatetest14_d0g0v0_Cancun
+ - randomStatetest150_d0g0v0_Cancun
+ - randomStatetest154_d0g0v0_Cancun
+ - randomStatetest178_d0g0v0_Cancun
+ - randomStatetest211_d0g0v0_Cancun
+ - randomStatetest260_d0g0v0_Cancun
+ - randomStatetest306_d0g0v0_Cancun
+ - randomStatetest159_d0g0v0_Cancun
+ - randomStatetest163_d0g0v0_Cancun
+ - randomStatetest177_d0g0v0_Cancun
+ - randomStatetest185_d0g0v0_Cancun
+ - randomStatetest326_d0g0v0_Cancun
+ - randomStatetest36_d0g0v0_Cancun
+ - randomStatetest384_d0g0v0_Cancun
+ - randomStatetest48_d0g0v0_Cancun
+ stReturnDataTest:
+ - revertRetDataSize_d0g0v0_Cancun
+ - revertRetDataSize_d10g0v0_Cancun
+ - revertRetDataSize_d11g0v0_Cancun
+ - revertRetDataSize_d12g0v0_Cancun
+ - revertRetDataSize_d13g0v0_Cancun
+ - revertRetDataSize_d14g0v0_Cancun
+ - revertRetDataSize_d15g0v0_Cancun
+ - revertRetDataSize_d16g0v0_Cancun
+ - revertRetDataSize_d17g0v0_Cancun
+ - revertRetDataSize_d18g0v0_Cancun
+ - revertRetDataSize_d19g0v0_Cancun
+ - revertRetDataSize_d1g0v0_Cancun
+ - revertRetDataSize_d20g0v0_Cancun
+ - revertRetDataSize_d21g0v0_Cancun
+ - revertRetDataSize_d22g0v0_Cancun
+ - revertRetDataSize_d23g0v0_Cancun
+ - revertRetDataSize_d24g0v0_Cancun
+ - revertRetDataSize_d25g0v0_Cancun
+ - revertRetDataSize_d26g0v0_Cancun
+ - revertRetDataSize_d27g0v0_Cancun
+ - revertRetDataSize_d28g0v0_Cancun
+ - revertRetDataSize_d29g0v0_Cancun
+ - revertRetDataSize_d2g0v0_Cancun
+ - revertRetDataSize_d30g0v0_Cancun
+ - revertRetDataSize_d31g0v0_Cancun
+ - revertRetDataSize_d32g0v0_Cancun
+ - revertRetDataSize_d33g0v0_Cancun
+ - revertRetDataSize_d34g0v0_Cancun
+ - revertRetDataSize_d35g0v0_Cancun
+ - revertRetDataSize_d3g0v0_Cancun
+ - revertRetDataSize_d4g0v0_Cancun
+ - revertRetDataSize_d5g0v0_Cancun
+ - revertRetDataSize_d6g0v0_Cancun
+ - revertRetDataSize_d7g0v0_Cancun
+ - revertRetDataSize_d8g0v0_Cancun
+ - revertRetDataSize_d9g0v0_Cancun
+ stRevertTest:
+ - LoopCallsDepthThenRevert2_d0g0v0_Cancun
+ - LoopCallsDepthThenRevert3_d0g0v0_Cancun
+ - LoopCallsThenRevert_d0g0v0_Cancun
+ - RevertPrecompiledTouchExactOOG_Paris_d10g1v0_Cancun
+ - RevertPrecompiledTouchExactOOG_Paris_d10g2v0_Cancun
+ - RevertPrecompiledTouchExactOOG_Paris_d15g2v0_Cancun
+ - RevertPrecompiledTouchExactOOG_Paris_d18g1v0_Cancun
+ - RevertPrecompiledTouchExactOOG_Paris_d18g2v0_Cancun
+ - RevertPrecompiledTouchExactOOG_Paris_d23g2v0_Cancun
+ - RevertPrecompiledTouchExactOOG_Paris_d26g1v0_Cancun
+ - RevertPrecompiledTouchExactOOG_Paris_d26g2v0_Cancun
+ - RevertPrecompiledTouchExactOOG_Paris_d2g1v0_Cancun
+ - RevertPrecompiledTouchExactOOG_Paris_d2g2v0_Cancun
+ - RevertPrecompiledTouchExactOOG_Paris_d31g2v0_Cancun
+ - RevertPrecompiledTouchExactOOG_Paris_d7g2v0_Cancun
+ - RevertPrecompiledTouch_Paris_d0g0v0_Cancun
+ - RevertPrecompiledTouch_Paris_d1g0v0_Cancun
+ - RevertPrecompiledTouch_Paris_d2g0v0_Cancun
+ - RevertPrecompiledTouch_Paris_d3g0v0_Cancun
+ - RevertPrecompiledTouch_nonce_d0g0v0_Cancun
+ - RevertPrecompiledTouch_nonce_d1g0v0_Cancun
+ - RevertPrecompiledTouch_nonce_d2g0v0_Cancun
+ - RevertPrecompiledTouch_nonce_d3g0v0_Cancun
+ - RevertPrecompiledTouch_noncestorage_d0g0v0_Cancun
+ - RevertPrecompiledTouch_noncestorage_d1g0v0_Cancun
+ - RevertPrecompiledTouch_noncestorage_d2g0v0_Cancun
+ - RevertPrecompiledTouch_noncestorage_d3g0v0_Cancun
+ - RevertPrecompiledTouch_storage_Paris_d0g0v0_Cancun
+ - RevertPrecompiledTouch_storage_Paris_d1g0v0_Cancun
+ - RevertPrecompiledTouch_storage_Paris_d2g0v0_Cancun
+ - RevertPrecompiledTouch_storage_Paris_d3g0v0_Cancun
+ - costRevert_d0g0v0_Cancun
+ - costRevert_d14g0v0_Cancun
+ - costRevert_d21g0v0_Cancun
+ - costRevert_d7g0v0_Cancun
+ - stateRevert_d0g0v0_Cancun
+ - RevertInCreateInInit_Paris_d0g0v0_Cancun
+ - RevertPrecompiledTouchExactOOG_Paris_d15g1v0_Cancun
+ - RevertPrecompiledTouchExactOOG_Paris_d23g1v0_Cancun
+ - RevertPrecompiledTouchExactOOG_Paris_d31g1v0_Cancun
+ - RevertPrecompiledTouchExactOOG_Paris_d7g1v0_Cancun
+ stSStoreTest:
+ - SstoreCallToSelfSubRefundBelowZero_d0g0v0_Cancun
+ - InitCollisionParis_d0g0v0_Cancun
+ - InitCollisionParis_d1g0v0_Cancun
+ - InitCollisionParis_d2g0v0_Cancun
+ - InitCollisionParis_d3g0v0_Cancun
+ stSelfBalance:
+ - diffPlaces_d34g0v0_Cancun
+ stSolidityTest:
+ - CallInfiniteLoop_d0g0v0_Cancun
+ - TestCryptographicFunctions_d0g0v0_Cancun
+ stSpecialTest:
+ - JUMPDEST_Attack_d0g0v0_Cancun
+ - JUMPDEST_AttackwithJump_d0g0v0_Cancun
+ - block504980_d0g0v0_Cancun
+ - selfdestructEIP2929_d0g0v0_Cancun
+ - sha3_deja_d0g0v0_Cancun
+ - failed_tx_xcf416c53_Paris_d0g0v0_Cancun
+ stShift:
+ - shiftCombinations_d0g0v0_Cancun
+ - shiftSignedCombinations_d0g0v0_Cancun
+ stStaticCall:
+ - static_Call1024BalanceTooLow2_d1g0v0_Cancun
+ - static_Call1024BalanceTooLow_d1g0v0_Cancun
+ - static_Call1024PreCalls2_d0g0v0_Cancun
+ - static_Call1024PreCalls2_d1g0v0_Cancun
+ - static_Call1024PreCalls3_d1g0v0_Cancun
+ - static_Call1024PreCalls_d1g0v0_Cancun
+ - static_Call50000bytesContract50_3_d0g0v0_Cancun
+ - static_Call50000bytesContract50_3_d1g0v0_Cancun
+ - static_CallEcrecover0_0input_d3g0v0_Cancun
+ - static_CallEcrecover0_0input_d8g0v0_Cancun
+ - static_CallGoesOOGOnSecondLevel2_d1g0v0_Cancun
+ - static_CallRecursiveBomb0_OOG_atMaxCallDepth_d0g0v0_Cancun
+ - static_CallRecursiveBombPreCall2_d0g0v0_Cancun
+ - static_CallRecursiveBombPreCall_d0g0v0_Cancun
+ - static_CallRipemd160_1_d0g0v0_Cancun
+ - static_CallRipemd160_2_d0g0v0_Cancun
+ - static_CallRipemd160_3_d0g0v0_Cancun
+ - static_CallRipemd160_3_postfixed0_d0g0v0_Cancun
+ - static_CallRipemd160_3_prefixed0_d0g0v0_Cancun
+ - static_CallRipemd160_4_d0g0v0_Cancun
+ - static_CallRipemd160_4_gas719_d0g0v0_Cancun
+ - static_LoopCallsDepthThenRevert2_d0g0v0_Cancun
+ - static_LoopCallsDepthThenRevert3_d0g0v0_Cancun
+ - static_callChangeRevert_d1g0v0_Cancun
+ - static_callCreate2_d2g0v0_Cancun
+ - static_callcall_00_OOGE_1_d1g0v0_Cancun
+ - static_callcall_00_OOGE_2_d1g0v0_Cancun
+ - static_callcallcall_000_OOGMAfter2_d0g0v0_Cancun
+ - static_callcallcall_000_OOGMBefore_d0g0v0_Cancun
+ - static_callcallcallcode_001_OOGE_2_d1g0v0_Cancun
+ - static_callcallcallcode_001_OOGE_d0g0v0_Cancun
+ - static_callcallcallcode_001_OOGMAfter2_d1g0v0_Cancun
+ - static_callcallcallcode_001_OOGMAfter_3_d1g0v0_Cancun
+ - static_callcallcallcode_001_OOGMAfter_d1g0v0_Cancun
+ - static_callcallcallcode_001_OOGMBefore2_d1g0v0_Cancun
+ - static_callcallcallcode_001_OOGMBefore_d0g0v0_Cancun
+ - static_callcallcodecall_010_OOGE_2_d1g0v0_Cancun
+ - static_callcallcodecall_010_OOGE_d0g0v0_Cancun
+ - static_callcallcodecall_010_OOGMAfter_2_d1g0v0_Cancun
+ - static_callcallcodecall_010_OOGMAfter_3_d1g0v0_Cancun
+ - static_callcallcodecall_010_OOGMAfter_d1g0v0_Cancun
+ - static_callcallcodecall_010_OOGMBefore2_d0g0v0_Cancun
+ - static_callcallcodecall_010_OOGMBefore_d1g0v0_Cancun
+ - static_callcodecallcall_100_OOGMAfter2_d0g0v0_Cancun
+ - static_callcodecallcall_100_OOGMAfter2_d0g0v1_Cancun
+ - static_callcodecallcall_100_OOGMAfter_2_d0g0v0_Cancun
+ - static_callcodecallcallcode_101_OOGE_d0g0v0_Cancun
+ - static_callcodecallcallcode_101_OOGMAfter_1_d0g0v0_Cancun
+ - StaticcallToPrecompileFromCalledContract_d0g0v0_Cancun
+ - StaticcallToPrecompileFromContractInitialization_d0g0v0_Cancun
+ - StaticcallToPrecompileFromTransaction_d0g0v0_Cancun
+ - static_Call1MB1024Calldepth_d1g0v0_Cancun
+ - static_Call50000_d0g0v0_Cancun
+ - static_Call50000_d1g0v0_Cancun
+ - static_Call50000_ecrec_d0g0v0_Cancun
+ - static_Call50000_ecrec_d1g0v0_Cancun
+ - static_Call50000_identity2_d0g0v0_Cancun
+ - static_Call50000_identity2_d1g0v0_Cancun
+ - static_Call50000_identity_d0g0v0_Cancun
+ - static_Call50000_identity_d1g0v0_Cancun
+ - static_Call50000_rip160_d0g0v0_Cancun
+ - static_Call50000_rip160_d1g0v0_Cancun
+ - static_Call50000bytesContract50_1_d0g0v0_Cancun
+ - static_Call50000bytesContract50_1_d1g0v0_Cancun
+ - static_Call50000bytesContract50_2_d0g0v0_Cancun
+ - static_Call50000bytesContract50_2_d1g0v0_Cancun
+ - static_CallRipemd160_5_d0g0v0_Cancun
+ - static_LoopCallsThenRevert_d0g0v0_Cancun
+ - static_LoopCallsThenRevert_d0g1v0_Cancun
+ - static_Return50000_2_d0g0v0_Cancun
+ stSystemOperationsTest:
+ - CallRecursiveBomb0_OOG_atMaxCallDepth_d0g0v0_Cancun
+ stZeroKnowledge:
+ - ecpairing_bad_length_191_d0g0v0_Cancun
+ - ecpairing_bad_length_191_d0g1v0_Cancun
+ - ecpairing_bad_length_191_d0g2v0_Cancun
+ - ecpairing_bad_length_191_d0g3v0_Cancun
+ - ecpairing_bad_length_193_d0g0v0_Cancun
+ - ecpairing_bad_length_193_d0g1v0_Cancun
+ - ecpairing_bad_length_193_d0g2v0_Cancun
+ - ecpairing_bad_length_193_d0g3v0_Cancun
+ - ecpairing_empty_data_d0g1v0_Cancun
+ - ecpairing_empty_data_d0g2v0_Cancun
+ - ecpairing_empty_data_insufficient_gas_d0g0v0_Cancun
+ - ecpairing_empty_data_insufficient_gas_d0g1v0_Cancun
+ - ecpairing_inputs_d100g0v0_Cancun
+ - ecpairing_inputs_d101g0v0_Cancun
+ - ecpairing_inputs_d102g0v0_Cancun
+ - ecpairing_inputs_d103g0v0_Cancun
+ - ecpairing_inputs_d104g0v0_Cancun
+ - ecpairing_inputs_d105g0v0_Cancun
+ - ecpairing_inputs_d106g0v0_Cancun
+ - ecpairing_inputs_d107g0v0_Cancun
+ - ecpairing_inputs_d108g0v0_Cancun
+ - ecpairing_inputs_d109g0v0_Cancun
+ - ecpairing_inputs_d110g0v0_Cancun
+ - ecpairing_inputs_d111g0v0_Cancun
+ - ecpairing_inputs_d112g0v0_Cancun
+ - ecpairing_inputs_d113g0v0_Cancun
+ - ecpairing_inputs_d114g0v0_Cancun
+ - ecpairing_inputs_d115g0v0_Cancun
+ - ecpairing_inputs_d116g0v0_Cancun
+ - ecpairing_inputs_d117g0v0_Cancun
+ - ecpairing_inputs_d118g0v0_Cancun
+ - ecpairing_inputs_d119g0v0_Cancun
+ - ecpairing_inputs_d120g0v0_Cancun
+ - ecpairing_inputs_d121g0v0_Cancun
+ - ecpairing_inputs_d122g0v0_Cancun
+ - ecpairing_inputs_d123g0v0_Cancun
+ - ecpairing_inputs_d124g0v0_Cancun
+ - ecpairing_inputs_d125g0v0_Cancun
+ - ecpairing_inputs_d126g0v0_Cancun
+ - ecpairing_inputs_d127g0v0_Cancun
+ - ecpairing_inputs_d128g0v0_Cancun
+ - ecpairing_inputs_d129g0v0_Cancun
+ - ecpairing_inputs_d130g0v0_Cancun
+ - ecpairing_inputs_d131g0v0_Cancun
+ - ecpairing_inputs_d132g0v0_Cancun
+ - ecpairing_inputs_d133g0v0_Cancun
+ - ecpairing_inputs_d134g0v0_Cancun
+ - ecpairing_inputs_d135g0v0_Cancun
+ - ecpairing_inputs_d136g0v0_Cancun
+ - ecpairing_inputs_d137g0v0_Cancun
+ - ecpairing_inputs_d138g0v0_Cancun
+ - ecpairing_inputs_d139g0v0_Cancun
+ - ecpairing_inputs_d140g0v0_Cancun
+ - ecpairing_inputs_d141g0v0_Cancun
+ - ecpairing_inputs_d142g0v0_Cancun
+ - ecpairing_inputs_d143g0v0_Cancun
+ - ecpairing_inputs_d38g0v0_Cancun
+ - ecpairing_inputs_d39g0v0_Cancun
+ - ecpairing_inputs_d40g0v0_Cancun
+ - ecpairing_inputs_d41g0v0_Cancun
+ - ecpairing_inputs_d42g0v0_Cancun
+ - ecpairing_inputs_d43g0v0_Cancun
+ - ecpairing_inputs_d44g0v0_Cancun
+ - ecpairing_inputs_d45g0v0_Cancun
+ - ecpairing_inputs_d46g0v0_Cancun
+ - ecpairing_inputs_d47g0v0_Cancun
+ - ecpairing_inputs_d48g0v0_Cancun
+ - ecpairing_inputs_d49g0v0_Cancun
+ - ecpairing_inputs_d50g0v0_Cancun
+ - ecpairing_inputs_d51g0v0_Cancun
+ - ecpairing_inputs_d52g0v0_Cancun
+ - ecpairing_inputs_d53g0v0_Cancun
+ - ecpairing_inputs_d54g0v0_Cancun
+ - ecpairing_inputs_d55g0v0_Cancun
+ - ecpairing_inputs_d56g0v0_Cancun
+ - ecpairing_inputs_d57g0v0_Cancun
+ - ecpairing_inputs_d58g0v0_Cancun
+ - ecpairing_inputs_d59g0v0_Cancun
+ - ecpairing_inputs_d60g0v0_Cancun
+ - ecpairing_inputs_d61g0v0_Cancun
+ - ecpairing_inputs_d62g0v0_Cancun
+ - ecpairing_inputs_d63g0v0_Cancun
+ - ecpairing_inputs_d64g0v0_Cancun
+ - ecpairing_inputs_d65g0v0_Cancun
+ - ecpairing_inputs_d66g0v0_Cancun
+ - ecpairing_inputs_d67g0v0_Cancun
+ - ecpairing_inputs_d68g0v0_Cancun
+ - ecpairing_inputs_d69g0v0_Cancun
+ - ecpairing_inputs_d70g0v0_Cancun
+ - ecpairing_inputs_d71g0v0_Cancun
+ - ecpairing_inputs_d72g0v0_Cancun
+ - ecpairing_inputs_d73g0v0_Cancun
+ - ecpairing_inputs_d74g0v0_Cancun
+ - ecpairing_inputs_d75g0v0_Cancun
+ - ecpairing_inputs_d76g0v0_Cancun
+ - ecpairing_inputs_d77g0v0_Cancun
+ - ecpairing_inputs_d78g0v0_Cancun
+ - ecpairing_inputs_d79g0v0_Cancun
+ - ecpairing_inputs_d80g0v0_Cancun
+ - ecpairing_inputs_d81g0v0_Cancun
+ - ecpairing_inputs_d82g0v0_Cancun
+ - ecpairing_inputs_d83g0v0_Cancun
+ - ecpairing_inputs_d84g0v0_Cancun
+ - ecpairing_inputs_d85g0v0_Cancun
+ - ecpairing_inputs_d86g0v0_Cancun
+ - ecpairing_inputs_d87g0v0_Cancun
+ - ecpairing_inputs_d88g0v0_Cancun
+ - ecpairing_inputs_d89g0v0_Cancun
+ - ecpairing_inputs_d90g0v0_Cancun
+ - ecpairing_inputs_d91g0v0_Cancun
+ - ecpairing_inputs_d92g0v0_Cancun
+ - ecpairing_inputs_d93g0v0_Cancun
+ - ecpairing_inputs_d94g0v0_Cancun
+ - ecpairing_inputs_d95g0v0_Cancun
+ - ecpairing_inputs_d96g0v0_Cancun
+ - ecpairing_inputs_d97g0v0_Cancun
+ - ecpairing_inputs_d98g0v0_Cancun
+ - ecpairing_inputs_d99g0v0_Cancun
+ - ecpairing_one_point_fail_d0g1v0_Cancun
+ - ecpairing_one_point_fail_d0g2v0_Cancun
+ - ecpairing_one_point_insufficient_gas_d0g0v0_Cancun
+ - ecpairing_one_point_insufficient_gas_d0g1v0_Cancun
+ - ecpairing_one_point_insufficient_gas_d0g2v0_Cancun
+ - ecpairing_one_point_not_in_subgroup_d0g0v0_Cancun
+ - ecpairing_one_point_not_in_subgroup_d0g1v0_Cancun
+ - ecpairing_one_point_not_in_subgroup_d0g2v0_Cancun
+ - ecpairing_one_point_not_in_subgroup_d0g3v0_Cancun
+ - ecpairing_one_point_with_g1_zero_d0g1v0_Cancun
+ - ecpairing_one_point_with_g1_zero_d0g2v0_Cancun
+ - ecpairing_one_point_with_g2_zero_and_g1_invalid_d0g0v0_Cancun
+ - ecpairing_one_point_with_g2_zero_and_g1_invalid_d0g1v0_Cancun
+ - ecpairing_one_point_with_g2_zero_and_g1_invalid_d0g2v0_Cancun
+ - ecpairing_one_point_with_g2_zero_and_g1_invalid_d0g3v0_Cancun
+ - ecpairing_one_point_with_g2_zero_d0g1v0_Cancun
+ - ecpairing_perturb_g2_by_curve_order_d0g0v0_Cancun
+ - ecpairing_perturb_g2_by_curve_order_d0g1v0_Cancun
+ - ecpairing_perturb_g2_by_curve_order_d0g2v0_Cancun
+ - ecpairing_perturb_g2_by_curve_order_d0g3v0_Cancun
+ - ecpairing_perturb_g2_by_field_modulus_again_d0g0v0_Cancun
+ - ecpairing_perturb_g2_by_field_modulus_again_d0g1v0_Cancun
+ - ecpairing_perturb_g2_by_field_modulus_again_d0g2v0_Cancun
+ - ecpairing_perturb_g2_by_field_modulus_again_d0g3v0_Cancun
+ - ecpairing_perturb_g2_by_field_modulus_d0g0v0_Cancun
+ - ecpairing_perturb_g2_by_field_modulus_d0g1v0_Cancun
+ - ecpairing_perturb_g2_by_field_modulus_d0g2v0_Cancun
+ - ecpairing_perturb_g2_by_field_modulus_d0g3v0_Cancun
+ - ecpairing_perturb_g2_by_one_d0g0v0_Cancun
+ - ecpairing_perturb_g2_by_one_d0g1v0_Cancun
+ - ecpairing_perturb_g2_by_one_d0g2v0_Cancun
+ - ecpairing_perturb_g2_by_one_d0g3v0_Cancun
+ - ecpairing_perturb_zeropoint_by_curve_order_d0g0v0_Cancun
+ - ecpairing_perturb_zeropoint_by_curve_order_d0g1v0_Cancun
+ - ecpairing_perturb_zeropoint_by_curve_order_d0g2v0_Cancun
+ - ecpairing_perturb_zeropoint_by_curve_order_d0g3v0_Cancun
+ - ecpairing_perturb_zeropoint_by_field_modulus_d0g0v0_Cancun
+ - ecpairing_perturb_zeropoint_by_field_modulus_d0g1v0_Cancun
+ - ecpairing_perturb_zeropoint_by_field_modulus_d0g2v0_Cancun
+ - ecpairing_perturb_zeropoint_by_field_modulus_d0g3v0_Cancun
+ - ecpairing_perturb_zeropoint_by_one_d0g0v0_Cancun
+ - ecpairing_perturb_zeropoint_by_one_d0g1v0_Cancun
+ - ecpairing_perturb_zeropoint_by_one_d0g2v0_Cancun
+ - ecpairing_perturb_zeropoint_by_one_d0g3v0_Cancun
+ - ecpairing_three_point_fail_1_d0g1v0_Cancun
+ - ecpairing_three_point_fail_1_d0g2v0_Cancun
+ - ecpairing_three_point_fail_1_d0g3v0_Cancun
+ - ecpairing_three_point_match_1_d0g1v0_Cancun
+ - ecpairing_three_point_match_1_d0g2v0_Cancun
+ - ecpairing_two_point_fail_1_d0g1v0_Cancun
+ - ecpairing_two_point_fail_1_d0g2v0_Cancun
+ - ecpairing_two_point_fail_2_d0g1v0_Cancun
+ - ecpairing_two_point_fail_2_d0g2v0_Cancun
+ - ecpairing_two_point_fail_2_d0g3v0_Cancun
+ - ecpairing_two_point_match_1_d0g1v0_Cancun
+ - ecpairing_two_point_match_1_d0g2v0_Cancun
+ - ecpairing_two_point_match_1_d0g3v0_Cancun
+ - ecpairing_two_point_match_2_d0g1v0_Cancun
+ - ecpairing_two_point_match_2_d0g2v0_Cancun
+ - ecpairing_two_point_match_3_d0g1v0_Cancun
+ - ecpairing_two_point_match_3_d0g2v0_Cancun
+ - ecpairing_two_point_match_4_d0g1v0_Cancun
+ - ecpairing_two_point_match_4_d0g2v0_Cancun
+ - ecpairing_two_point_match_5_d0g1v0_Cancun
+ - ecpairing_two_point_match_5_d0g2v0_Cancun
+ - ecpairing_two_point_match_5_d0g3v0_Cancun
+ - ecpairing_two_point_oog_d0g1v0_Cancun
+ - ecpairing_two_point_oog_d0g2v0_Cancun
+ - ecpairing_two_points_with_one_g2_zero_d0g1v0_Cancun
+ - ecpairing_two_points_with_one_g2_zero_d0g2v0_Cancun
+ - pairingTest_d0g1v0_Cancun
+ - pairingTest_d0g2v0_Cancun
+ - pairingTest_d0g3v0_Cancun
+ - pairingTest_d1g1v0_Cancun
+ - pairingTest_d1g2v0_Cancun
+ - pairingTest_d1g3v0_Cancun
+ - pairingTest_d2g1v0_Cancun
+ - pairingTest_d2g2v0_Cancun
+ - pairingTest_d2g3v0_Cancun
+ - pairingTest_d3g1v0_Cancun
+ - pairingTest_d3g2v0_Cancun
+ - pairingTest_d3g3v0_Cancun
+ - pairingTest_d4g1v0_Cancun
+ - pairingTest_d4g2v0_Cancun
+ - pairingTest_d4g3v0_Cancun
+ - pairingTest_d5g1v0_Cancun
+ - pairingTest_d5g2v0_Cancun
+ - pairingTest_d5g3v0_Cancun
+ - ecpairing_empty_data_d0g0v0_Cancun
+ - ecpairing_empty_data_d0g3v0_Cancun
+ - ecpairing_empty_data_insufficient_gas_d0g2v0_Cancun
+ - ecpairing_inputs_d0g0v0_Cancun
+ - ecpairing_inputs_d10g0v0_Cancun
+ - ecpairing_inputs_d11g0v0_Cancun
+ - ecpairing_inputs_d12g0v0_Cancun
+ - ecpairing_inputs_d13g0v0_Cancun
+ - ecpairing_inputs_d14g0v0_Cancun
+ - ecpairing_inputs_d15g0v0_Cancun
+ - ecpairing_inputs_d16g0v0_Cancun
+ - ecpairing_inputs_d17g0v0_Cancun
+ - ecpairing_inputs_d18g0v0_Cancun
+ - ecpairing_inputs_d19g0v0_Cancun
+ - ecpairing_inputs_d1g0v0_Cancun
+ - ecpairing_inputs_d20g0v0_Cancun
+ - ecpairing_inputs_d21g0v0_Cancun
+ - ecpairing_inputs_d22g0v0_Cancun
+ - ecpairing_inputs_d23g0v0_Cancun
+ - ecpairing_inputs_d24g0v0_Cancun
+ - ecpairing_inputs_d25g0v0_Cancun
+ - ecpairing_inputs_d26g0v0_Cancun
+ - ecpairing_inputs_d27g0v0_Cancun
+ - ecpairing_inputs_d28g0v0_Cancun
+ - ecpairing_inputs_d29g0v0_Cancun
+ - ecpairing_inputs_d2g0v0_Cancun
+ - ecpairing_inputs_d30g0v0_Cancun
+ - ecpairing_inputs_d31g0v0_Cancun
+ - ecpairing_inputs_d32g0v0_Cancun
+ - ecpairing_inputs_d33g0v0_Cancun
+ - ecpairing_inputs_d34g0v0_Cancun
+ - ecpairing_inputs_d35g0v0_Cancun
+ - ecpairing_inputs_d36g0v0_Cancun
+ - ecpairing_inputs_d37g0v0_Cancun
+ - ecpairing_inputs_d3g0v0_Cancun
+ - ecpairing_inputs_d4g0v0_Cancun
+ - ecpairing_inputs_d5g0v0_Cancun
+ - ecpairing_inputs_d6g0v0_Cancun
+ - ecpairing_inputs_d7g0v0_Cancun
+ - ecpairing_inputs_d8g0v0_Cancun
+ - ecpairing_inputs_d9g0v0_Cancun
+ - ecpairing_one_point_fail_d0g0v0_Cancun
+ - ecpairing_one_point_with_g1_zero_d0g0v0_Cancun
+ - ecpairing_one_point_with_g2_zero_d0g0v0_Cancun
+ - ecpairing_one_point_with_g2_zero_d0g2v0_Cancun
+ - ecpairing_one_point_with_g2_zero_d0g3v0_Cancun
+ - ecpairing_three_point_fail_1_d0g0v0_Cancun
+ - ecpairing_three_point_match_1_d0g0v0_Cancun
+ - ecpairing_three_point_match_1_d0g3v0_Cancun
+ - ecpairing_two_point_fail_1_d0g0v0_Cancun
+ - ecpairing_two_point_fail_2_d0g0v0_Cancun
+ - ecpairing_two_point_match_1_d0g0v0_Cancun
+ - ecpairing_two_point_match_2_d0g0v0_Cancun
+ - ecpairing_two_point_match_2_d0g3v0_Cancun
+ - ecpairing_two_point_match_3_d0g0v0_Cancun
+ - ecpairing_two_point_match_3_d0g3v0_Cancun
+ - ecpairing_two_point_match_4_d0g0v0_Cancun
+ - ecpairing_two_point_match_4_d0g3v0_Cancun
+ - ecpairing_two_point_match_5_d0g0v0_Cancun
+ - ecpairing_two_point_oog_d0g0v0_Cancun
+ - ecpairing_two_point_oog_d0g3v0_Cancun
+ - ecpairing_two_points_with_one_g2_zero_d0g0v0_Cancun
+ - ecpairing_two_points_with_one_g2_zero_d0g3v0_Cancun
+ - pairingTest_d0g0v0_Cancun
+ - pairingTest_d1g0v0_Cancun
+ - pairingTest_d2g0v0_Cancun
+ - pairingTest_d3g0v0_Cancun
+ - pairingTest_d4g0v0_Cancun
+ - pairingTest_d5g0v0_Cancun
+ tstorage:
+ - run_until_out_of_gas__fork_Cancun_minus_blockchain_test_minus_tstore
+ - run_until_out_of_gas__fork_Cancun_minus_blockchain_test_minus_tstore_tload
+ - run_until_out_of_gas__fork_Cancun_minus_blockchain_test_minus_tstore_wide_address_space
+ stAttackTest:
+ - ContractCreationSpam_d0g0v0_Cancun
+ stStaticFlagEnabled:
+ - CallWithZeroValueToPrecompileFromCalledContract_d0g0v0_Cancun
+ - CallWithZeroValueToPrecompileFromContractInitialization_d0g0v0_Cancun
+ - CallWithZeroValueToPrecompileFromTransaction_d0g0v0_Cancun
+ - CallcodeToPrecompileFromCalledContract_d0g0v0_Cancun
+ - CallcodeToPrecompileFromContractInitialization_d0g0v0_Cancun
+ - CallcodeToPrecompileFromTransaction_d0g0v0_Cancun
+ - DelegatecallToPrecompileFromCalledContract_d0g0v0_Cancun
+ - DelegatecallToPrecompileFromContractInitialization_d0g0v0_Cancun
+ - DelegatecallToPrecompileFromTransaction_d0g0v0_Cancun
+ stTimeConsuming:
+ - CALLBlake2f_MaxRounds_d0g0v0_Cancun
+ - static_Call50000_sha256_d0g0v0_Cancun
+ - static_Call50000_sha256_d1g0v0_Cancun
+ stTransactionTest:
+ - HighGasPriceParis_d0g0v0_Cancun
+ - ValueOverflowParis_d0g0v0_Cancun
diff --git a/cairo/kakarot-ssj/bun.lockb b/cairo/kakarot-ssj/bun.lockb
new file mode 100755
index 000000000..ad0e90537
Binary files /dev/null and b/cairo/kakarot-ssj/bun.lockb differ
diff --git a/cairo1_contracts/mock_pragma/.gitignore b/cairo/kakarot-ssj/crates/alexandria_data_structures/.gitignore
similarity index 100%
rename from cairo1_contracts/mock_pragma/.gitignore
rename to cairo/kakarot-ssj/crates/alexandria_data_structures/.gitignore
diff --git a/cairo/kakarot-ssj/crates/alexandria_data_structures/Scarb.toml b/cairo/kakarot-ssj/crates/alexandria_data_structures/Scarb.toml
new file mode 100644
index 000000000..d3fd53bcd
--- /dev/null
+++ b/cairo/kakarot-ssj/crates/alexandria_data_structures/Scarb.toml
@@ -0,0 +1,15 @@
+[package]
+name = "alexandria_data_structures"
+version = "0.1.0"
+
+# See more keys and their definitions at https://docs.swmansion.com/scarb/docs/reference/manifest.html
+
+[dependencies]
+
+[dev-dependencies]
+snforge_std = { git = "https://github.com/foundry-rs/starknet-foundry.git", tag = "v0.31.0" }
+assert_macros = "2.8.2"
+
+[scripts]
+test = "snforge test --max-n-steps 4294967295"
+test-profiling = "snforge test --max-n-steps 4294967295 --build-profile"
diff --git a/cairo/kakarot-ssj/crates/alexandria_data_structures/src/lib.cairo b/cairo/kakarot-ssj/crates/alexandria_data_structures/src/lib.cairo
new file mode 100644
index 000000000..63e55caa6
--- /dev/null
+++ b/cairo/kakarot-ssj/crates/alexandria_data_structures/src/lib.cairo
@@ -0,0 +1 @@
+pub mod vec;
diff --git a/cairo/kakarot-ssj/crates/alexandria_data_structures/src/vec.cairo b/cairo/kakarot-ssj/crates/alexandria_data_structures/src/vec.cairo
new file mode 100644
index 000000000..237adaff2
--- /dev/null
+++ b/cairo/kakarot-ssj/crates/alexandria_data_structures/src/vec.cairo
@@ -0,0 +1,167 @@
+use core::nullable::NullableImpl;
+use core::num::traits::WrappingAdd;
+use core::ops::index::Index;
+
+//! Vec implementation.
+//!
+//! # Example
+//! ```
+//! use alexandria::data_structures::vec::VecTrait;
+//!
+//! // Create a new vec instance.
+//! let mut vec = Felt252Vec::::new();
+//! // Push some items to the vec.
+//! vec.push(1);
+//! vec.push(2);
+//! ...
+//! ```
+
+pub trait VecTrait {
+ /// Creates a new V instance.
+ /// Returns
+ /// * V The new vec instance.
+ fn new() -> V;
+
+ /// Returns the item at the given index, or None if the index is out of bounds.
+ /// Parameters
+ /// * self The vec instance.
+ /// * index The index of the item to get.
+ /// Returns
+ /// * Option The item at the given index, or None if the index is out of bounds.
+ fn get(ref self: V, index: usize) -> Option;
+
+ /// Returns the item at the given index, or panics if the index is out of bounds.
+ /// Parameters
+ /// * self The vec instance.
+ /// * index The index of the item to get.
+ /// Returns
+ /// * T The item at the given index.
+ fn at(ref self: V, index: usize) -> T;
+
+ /// Pushes a new item to the vec.
+ /// Parameters
+ /// * self The vec instance.
+ /// * value The value to push onto the vec.
+ fn push(ref self: V, value: T);
+
+ /// Sets the item at the given index to the given value.
+ /// Panics if the index is out of bounds.
+ /// Parameters
+ /// * self The vec instance.
+ /// * index The index of the item to set.
+ /// * value The value to set the item to.
+ fn set(ref self: V, index: usize, value: T);
+
+ /// Returns the length of the vec.
+ /// Parameters
+ /// * self The vec instance.
+ /// Returns
+ /// * usize The length of the vec.
+ fn len(self: @V) -> usize;
+}
+
+impl VecIndex> of Index {
+ type Target = T;
+
+ #[inline(always)]
+ fn index(ref self: V, index: usize) -> T {
+ self.at(index)
+ }
+}
+
+pub struct Felt252Vec {
+ pub items: Felt252Dict,
+ pub len: usize,
+}
+
+impl DefaultFeltVec, +Copy, +Felt252DictValue> of Default> {
+ fn default() -> Felt252Vec {
+ Felt252VecImpl::::new()
+ }
+}
+
+impl DestructFeltVec, +Felt252DictValue> of Destruct> {
+ fn destruct(self: Felt252Vec) nopanic {
+ self.items.squash();
+ }
+}
+
+
+impl Felt252VecImpl, +Copy, +Felt252DictValue> of VecTrait, T> {
+ fn new() -> Felt252Vec {
+ Felt252Vec { items: Default::default(), len: 0 }
+ }
+
+ fn get(ref self: Felt252Vec, index: usize) -> Option {
+ if index < self.len() {
+ let item = self.items.get(index.into());
+ Option::Some(item)
+ } else {
+ Option::None
+ }
+ }
+
+ fn at(ref self: Felt252Vec, index: usize) -> T {
+ assert(index < self.len(), 'Index out of bounds');
+ let item = self.items.get(index.into());
+ item
+ }
+
+ fn push(ref self: Felt252Vec, value: T) {
+ self.items.insert(self.len.into(), value);
+ self.len = self.len.wrapping_add(1);
+ }
+
+ fn set(ref self: Felt252Vec, index: usize, value: T) {
+ assert(index < self.len(), 'Index out of bounds');
+ self.items.insert(index.into(), value);
+ }
+
+ fn len(self: @Felt252Vec) -> usize {
+ *self.len
+ }
+}
+
+pub struct NullableVec {
+ items: Felt252Dict>,
+ len: usize,
+}
+
+impl DestructNullableVec> of Destruct> {
+ fn destruct(self: NullableVec) nopanic {
+ self.items.squash();
+ }
+}
+
+impl NullableVecImpl, +Copy> of VecTrait, T> {
+ fn new() -> NullableVec {
+ NullableVec { items: Default::default(), len: 0 }
+ }
+
+ fn get(ref self: NullableVec, index: usize) -> Option {
+ if index < self.len() {
+ Option::Some(self.items.get(index.into()).deref())
+ } else {
+ Option::None
+ }
+ }
+
+ fn at(ref self: NullableVec, index: usize) -> T {
+ assert(index < self.len(), 'Index out of bounds');
+ self.items.get(index.into()).deref()
+ }
+
+ fn push(ref self: NullableVec, value: T) {
+ self.items.insert(self.len.into(), NullableImpl::new(value));
+ self.len = self.len.wrapping_add(1);
+ }
+
+ fn set(ref self: NullableVec, index: usize, value: T) {
+ assert(index < self.len(), 'Index out of bounds');
+ self.items.insert(index.into(), NullableImpl::new(value));
+ }
+
+ fn len(self: @NullableVec) -> usize {
+ *self.len
+ }
+}
diff --git a/cairo/kakarot-ssj/crates/contracts/Scarb.toml b/cairo/kakarot-ssj/crates/contracts/Scarb.toml
new file mode 100644
index 000000000..1e689c03e
--- /dev/null
+++ b/cairo/kakarot-ssj/crates/contracts/Scarb.toml
@@ -0,0 +1,32 @@
+[package]
+name = "contracts"
+version = "0.1.0"
+edition = "2024_07"
+
+# See more keys and their definitions at https://docs.swmansion.com/scarb/docs/reference/manifest.html
+
+[dependencies]
+starknet.workspace = true
+evm = { path = "../evm" }
+openzeppelin = { path = "../openzeppelin" }
+utils = { path = "../utils" }
+
+[tool]
+fmt.workspace = true
+
+[[target.starknet-contract]]
+casm = true
+casm-add-pythonic-hints = true
+build-external-contracts = ["openzeppelin::token::erc20::erc20::ERC20"]
+
+[lib]
+name = "contracts"
+
+[dev-dependencies]
+snforge_std = { git = "https://github.com/foundry-rs/starknet-foundry.git", tag = "v0.31.0" }
+assert_macros = "2.8.2"
+snforge_utils = { path = "../snforge_utils" }
+
+[scripts]
+test = "snforge test --max-n-steps 4294967295"
+test-profiling = "snforge test --max-n-steps 4294967295 --build-profile"
diff --git a/cairo/kakarot-ssj/crates/contracts/src/account_contract.cairo b/cairo/kakarot-ssj/crates/contracts/src/account_contract.cairo
new file mode 100644
index 000000000..5de99dd12
--- /dev/null
+++ b/cairo/kakarot-ssj/crates/contracts/src/account_contract.cairo
@@ -0,0 +1,278 @@
+//! The generic account that is deployed by Kakarot Core before being "specialized" into an
+//! Externally Owned Account or a Contract Account This aims at having only one class hash for all
+//! the contracts deployed by Kakarot, thus enforcing a unique and consistent address mapping Eth
+//! Address <=> Starknet Address
+
+use core::starknet::account::{Call};
+use core::starknet::{EthAddress, ClassHash, ContractAddress};
+
+#[derive(Copy, Drop, Serde, Debug)]
+pub struct OutsideExecution {
+ pub caller: ContractAddress,
+ pub nonce: u64,
+ pub execute_after: u64,
+ pub execute_before: u64,
+ pub calls: Span
+}
+
+#[starknet::interface]
+pub trait IAccount {
+ fn initialize(
+ ref self: TContractState, evm_address: EthAddress, implementation_class: ClassHash
+ );
+ fn get_implementation(self: @TContractState) -> ClassHash;
+ fn get_evm_address(self: @TContractState) -> EthAddress;
+ fn get_code_hash(self: @TContractState) -> u256;
+ fn set_code_hash(ref self: TContractState, code_hash: u256);
+ fn is_initialized(self: @TContractState) -> bool;
+
+ // EOA functions
+ fn __validate__(ref self: TContractState, calls: Array) -> felt252;
+ fn __validate_declare__(self: @TContractState, class_hash: felt252) -> felt252;
+ fn __execute__(ref self: TContractState, calls: Array) -> Array>;
+
+ // CA functions
+ fn write_bytecode(ref self: TContractState, bytecode: Span);
+ fn bytecode(self: @TContractState) -> Span;
+ fn write_storage(ref self: TContractState, key: u256, value: u256);
+ fn storage(self: @TContractState, key: u256) -> u256;
+ fn get_nonce(self: @TContractState) -> u64;
+ fn set_nonce(ref self: TContractState, nonce: u64);
+ fn execute_starknet_call(ref self: TContractState, call: Call) -> (bool, Span);
+ fn execute_from_outside(
+ ref self: TContractState, outside_execution: OutsideExecution, signature: Span,
+ ) -> Array>;
+}
+
+#[starknet::contract(account)]
+pub mod AccountContract {
+ use core::cmp::min;
+ use core::num::traits::Bounded;
+ use core::num::traits::zero::Zero;
+ use core::starknet::account::{Call};
+ use core::starknet::eth_signature::verify_eth_signature;
+ use core::starknet::storage::{
+ Map, StorageMapReadAccess, StorageMapWriteAccess, StoragePointerReadAccess,
+ StoragePointerWriteAccess
+ };
+ use core::starknet::syscalls::call_contract_syscall;
+ use core::starknet::{
+ EthAddress, ClassHash, get_caller_address, get_tx_info, get_block_timestamp
+ };
+ use crate::components::ownable::IOwnable;
+ use crate::components::ownable::ownable_component::InternalTrait;
+ use crate::components::ownable::ownable_component;
+ use crate::errors::KAKAROT_REENTRANCY;
+ use crate::kakarot_core::eth_rpc::{IEthRPCDispatcher, IEthRPCDispatcherTrait};
+ use crate::kakarot_core::interface::{IKakarotCoreDispatcher, IKakarotCoreDispatcherTrait};
+ use crate::storage::StorageBytecode;
+ use openzeppelin::token::erc20::interface::{IERC20CamelDispatcher, IERC20CamelDispatcherTrait};
+ use super::OutsideExecution;
+ use utils::eth_transaction::transaction::TransactionTrait;
+ use utils::serialization::{deserialize_signature, deserialize_bytes, serialize_bytes};
+ use utils::traits::DefaultSignature;
+
+ // Add ownable component
+ component!(path: ownable_component, storage: ownable, event: OwnableEvent);
+ #[abi(embed_v0)]
+ impl OwnableImpl = ownable_component::Ownable;
+ impl OwnableInternal = ownable_component::InternalImpl;
+
+
+ const VERSION: u32 = 000_001_000;
+
+
+ #[storage]
+ pub(crate) struct Storage {
+ pub(crate) Account_bytecode: StorageBytecode,
+ pub(crate) Account_bytecode_len: u32,
+ pub(crate) Account_storage: Map,
+ pub(crate) Account_is_initialized: bool,
+ pub(crate) Account_nonce: u64,
+ pub(crate) Account_implementation: ClassHash,
+ pub(crate) Account_evm_address: EthAddress,
+ pub(crate) Account_code_hash: u256,
+ #[substorage(v0)]
+ ownable: ownable_component::Storage
+ }
+
+ #[event]
+ #[derive(Drop, starknet::Event)]
+ pub enum Event {
+ transaction_executed: TransactionExecuted,
+ OwnableEvent: ownable_component::Event
+ }
+
+ #[derive(Drop, starknet::Event, Debug)]
+ pub struct TransactionExecuted {
+ pub response: Span,
+ pub success: bool,
+ pub gas_used: u64
+ }
+
+ #[constructor]
+ fn constructor(ref self: ContractState) {
+ panic!("Accounts cannot be created directly");
+ }
+
+ #[abi(embed_v0)]
+ impl Account of super::IAccount {
+ fn initialize(
+ ref self: ContractState, evm_address: EthAddress, implementation_class: ClassHash
+ ) {
+ assert(!self.Account_is_initialized.read(), 'Account already initialized');
+ self.Account_is_initialized.write(true);
+
+ self.Account_evm_address.write(evm_address);
+ self.Account_implementation.write(implementation_class);
+
+ let kakarot_address = self.ownable.owner();
+ let kakarot = IKakarotCoreDispatcher { contract_address: kakarot_address };
+ let native_token = kakarot.get_native_token();
+ // To internally perform value transfer of the network's native
+ // token (which conforms to the ERC20 standard), we need to give the
+ // KakarotCore contract infinite allowance
+ IERC20CamelDispatcher { contract_address: native_token }
+ .approve(kakarot_address, Bounded::::MAX);
+
+ kakarot.register_account(evm_address);
+ }
+
+ fn get_implementation(self: @ContractState) -> ClassHash {
+ self.Account_implementation.read()
+ }
+
+ fn get_evm_address(self: @ContractState) -> EthAddress {
+ self.Account_evm_address.read()
+ }
+
+ fn get_code_hash(self: @ContractState) -> u256 {
+ self.Account_code_hash.read()
+ }
+
+ fn set_code_hash(ref self: ContractState, code_hash: u256) {
+ self.ownable.assert_only_owner();
+ self.Account_code_hash.write(code_hash);
+ }
+
+ fn is_initialized(self: @ContractState) -> bool {
+ self.Account_is_initialized.read()
+ }
+
+ // EOA functions
+ fn __validate__(ref self: ContractState, calls: Array) -> felt252 {
+ panic!("EOA: __validate__ not supported")
+ }
+
+ /// Validate Declare is not used for Kakarot
+ fn __validate_declare__(self: @ContractState, class_hash: felt252) -> felt252 {
+ panic!("EOA: declare not supported")
+ }
+
+ fn __execute__(ref self: ContractState, calls: Array) -> Array> {
+ panic!("EOA: __execute__ not supported")
+ }
+
+ fn write_bytecode(ref self: ContractState, bytecode: Span) {
+ self.ownable.assert_only_owner();
+ self.Account_bytecode.write(StorageBytecode { bytecode });
+ }
+
+ fn bytecode(self: @ContractState) -> Span {
+ self.Account_bytecode.read().bytecode
+ }
+
+ fn write_storage(ref self: ContractState, key: u256, value: u256) {
+ self.ownable.assert_only_owner();
+ self.Account_storage.write(key, value);
+ }
+
+ fn storage(self: @ContractState, key: u256) -> u256 {
+ self.Account_storage.read(key)
+ }
+
+ fn get_nonce(self: @ContractState) -> u64 {
+ self.Account_nonce.read()
+ }
+
+ fn set_nonce(ref self: ContractState, nonce: u64) {
+ self.ownable.assert_only_owner();
+ self.Account_nonce.write(nonce);
+ }
+
+ /// Used to preserve caller in Cairo Precompiles
+ /// Reentrency check is done for Kakarot contract, only get_starknet_address is allowed
+ /// for Solidity contracts to be able to get the corresponding Starknet address in their
+ /// calldata.
+ fn execute_starknet_call(ref self: ContractState, call: Call) -> (bool, Span) {
+ self.ownable.assert_only_owner();
+ let kakarot_address = self.ownable.owner();
+ if call.to == kakarot_address && call.selector != selector!("get_starknet_address") {
+ return (false, KAKAROT_REENTRANCY.span());
+ }
+ let response = call_contract_syscall(call.to, call.selector, call.calldata);
+ if response.is_ok() {
+ return (true, response.unwrap().into());
+ }
+ return (false, response.unwrap_err().into());
+ }
+
+ fn execute_from_outside(
+ ref self: ContractState, outside_execution: OutsideExecution, signature: Span,
+ ) -> Array> {
+ let caller = get_caller_address();
+ let tx_info = get_tx_info();
+
+ // SNIP-9 Validation
+ if (outside_execution.caller.into() != 'ANY_CALLER') {
+ assert(caller == outside_execution.caller, 'SNIP9: Invalid caller');
+ }
+
+ let block_timestamp = get_block_timestamp();
+ assert(block_timestamp > outside_execution.execute_after, 'SNIP9: Too early call');
+ assert(block_timestamp < outside_execution.execute_before, 'SNIP9: Too late call');
+
+ // Kakarot-Specific Validation
+ assert(outside_execution.calls.len() == 1, 'KKRT: Multicall not supported');
+ assert(tx_info.version.into() >= 1_u256, 'KKRT: Deprecated tx version: 0');
+
+ // EOA Validation
+ assert(self.Account_bytecode_len.read().is_zero(), 'EOA: cannot have code');
+
+ let kakarot = IEthRPCDispatcher { contract_address: self.ownable.owner() };
+
+ let chain_id: u64 = kakarot.eth_chain_id();
+ assert(signature.len() == 5, 'EOA: Invalid signature length');
+ let signature = deserialize_signature(signature, chain_id)
+ .expect('EOA: invalid signature');
+
+ let mut encoded_tx_data = deserialize_bytes((*outside_execution.calls[0]).calldata)
+ .expect('conversion to Span failed')
+ .span();
+ let unsigned_transaction_hash = TransactionTrait::compute_hash(encoded_tx_data);
+
+ let address = self.Account_evm_address.read();
+ verify_eth_signature(unsigned_transaction_hash, signature, address);
+
+ let (success, return_data, gas_used) = kakarot
+ .eth_send_raw_unsigned_tx(encoded_tx_data);
+ let return_data = serialize_bytes(return_data).span();
+
+ // See Argent account
+ // https://github.com/argentlabs/argent-contracts-starknet/blob/1352198956f36fb35fa544c4e46a3507a3ec20e3/src/presets/user_account.cairo#L211-L213
+ // See 300 max data_len for events
+ // https://github.com/starkware-libs/blockifier/blob/9bfb3d4c8bf1b68a0c744d1249b32747c75a4d87/crates/blockifier/resources/versioned_constants.json
+ // The whole data_len should be less than 300, so it's the return_data should be less
+ // than 297 (+3 for return_data_len, success, gas_used)
+ self
+ .emit(
+ TransactionExecuted {
+ response: return_data.slice(0, min(297, return_data.len())),
+ success: success,
+ gas_used
+ }
+ );
+ array![return_data]
+ }
+ }
+}
diff --git a/cairo/kakarot-ssj/crates/contracts/src/cairo1_helpers.cairo b/cairo/kakarot-ssj/crates/contracts/src/cairo1_helpers.cairo
new file mode 100644
index 000000000..1a22b9721
--- /dev/null
+++ b/cairo/kakarot-ssj/crates/contracts/src/cairo1_helpers.cairo
@@ -0,0 +1,233 @@
+use core::starknet::{EthAddress, secp256_trait::Signature};
+
+#[starknet::interface]
+pub trait IPrecompiles {
+ /// Executes a precompiled contract at a given address with provided data.
+ ///
+ /// # Arguments
+ ///
+ /// * `self` - The instance of the current class.
+ /// * `address` - The address of the precompiled contract to be executed.
+ /// * `data` - The data to be passed to the precompiled contract.
+ ///
+ /// # Returns
+ ///
+ /// * A tuple containing:
+ /// * True if the execution was successful, false otherwise.
+ /// * The gas cost of the execution if successful, otherwise 0.
+ /// * The output data from the execution.
+ fn exec_precompile(self: @T, address: felt252, data: Span) -> (bool, u64, Span);
+}
+
+#[starknet::interface]
+pub trait IHelpers {
+ /// Gets the hash of a specific StarkNet block within the range of
+ /// [first_v0_12_0_block, current_block - 10].
+ ///
+ /// # Arguments
+ ///
+ /// * `block_number` - The block number for which to get the hash.
+ ///
+ /// # Returns
+ /// The hash of the specified block.
+ ///
+ /// # Errors
+ /// `Block number out of range` - If the block number is greater than `current_block - 10`.
+ /// `0`: The block number is inferior to `first_v0_12_0_block`.
+ fn get_block_hash(self: @T, block_number: u64) -> felt252;
+
+ /// Computes the keccak hash of the provided data.
+ ///
+ /// The data is expected to be an array of full 64-bit words.
+ /// The last u64-word to hash may be incomplete and is provided separately.
+ /// # Arguments
+ ///
+ /// * `words` - The full 64-bit words to hash.
+ /// * `last_input_word` - The last word to hash.
+ /// * `last_input_num_bytes` - The number of bytes in the last word.
+ ///
+ /// # Returns
+ /// The EVM-compatible keccak hash of the provided data.
+ fn keccak(
+ self: @T, words: Array, last_input_word: u64, last_input_num_bytes: usize
+ ) -> u256;
+
+ /// Computes the SHA-256 of the provided data.
+ ///
+ /// The data is expected to be an array of full 32-bit unsigned words.
+ /// The last u32-word to hash may be incomplete and is provided separately.
+ /// # Arguments
+ ///
+ /// * `input` - The full 32-bit unsigned words to hash.
+ /// * `last_input_word` - the last word to hash.
+ /// * `last_input_num_bytes` - the number of bytes in the last word.
+ ///
+ /// # Returns
+ /// The SHA-256 of the provided data.
+ fn compute_sha256_u32_array(
+ self: @T, input: Array, last_input_word: u32, last_input_num_bytes: u32
+ ) -> [
+ u32
+ ; 8];
+
+ // DEPRECATED
+ fn verify_eth_signature(
+ self: @T, msg_hash: u256, signature: Signature, eth_address: EthAddress
+ );
+
+ /// Recovers the Ethereum address from a message hash and a signature.
+ ///
+ /// # Arguments
+ ///
+ /// * `msg_hash` - The hash of the message.
+ /// * `signature` - The signature to recover the address from.
+ ///
+ /// # Returns
+ /// A tuple containing:
+ /// * A boolean indicating whether the recovery was successful.
+ /// * The recovered Ethereum address.
+ fn recover_eth_address(self: @T, msg_hash: u256, signature: Signature) -> (bool, EthAddress);
+
+ /// Performs signature verification in the secp256r1 elliptic curve.
+ ///
+ /// # Arguments
+ ///
+ /// * `msg_hash` - The hash of the message.
+ /// * `r` - The r component of the signature.
+ /// * `s` - The s component of the signature.
+ /// * `x` - The x coordinate of the public key.
+ /// * `y` - The y coordinate of the public key.
+ ///
+ /// # Returns
+ /// A boolean indicating whether the signature is valid.
+ fn verify_signature_secp256r1(
+ self: @T, msg_hash: u256, r: u256, s: u256, x: u256, y: u256
+ ) -> bool;
+}
+
+
+pub mod embeddable_impls {
+ use core::keccak::{cairo_keccak, keccak_u256s_be_inputs};
+ use core::num::traits::Zero;
+ use core::starknet::EthAddress;
+ use core::starknet::eth_signature::{verify_eth_signature};
+ use core::starknet::secp256_trait::{
+ Signature, recover_public_key, Secp256PointTrait, is_valid_signature
+ };
+ use core::starknet::secp256_trait::{Secp256Trait};
+ use core::starknet::secp256k1::Secp256k1Point;
+ use core::starknet::secp256r1::{Secp256r1Point};
+ use core::traits::Into;
+ use core::{starknet, starknet::SyscallResultTrait};
+ use evm::errors::EVMError;
+ use evm::precompiles::EcAdd;
+ use evm::precompiles::EcMul;
+ use evm::precompiles::Sha256;
+ use utils::traits::integer::U256Trait;
+
+
+ #[starknet::embeddable]
+ pub impl Precompiles of super::IPrecompiles {
+ fn exec_precompile(
+ self: @TContractState, address: felt252, data: Span
+ ) -> (bool, u64, Span) {
+ let result = match address {
+ 0 => Result::Err(EVMError::NotImplemented),
+ 1 => Result::Err(EVMError::NotImplemented),
+ 2 => Sha256::exec(data),
+ 3 | 4 => Result::Err(EVMError::NotImplemented),
+ 5 => Result::Err(EVMError::NotImplemented),
+ 6 => EcAdd::exec(data),
+ 7 => EcMul::exec(data),
+ _ => Result::Err(EVMError::NotImplemented),
+ };
+ match result {
+ Result::Ok((gas, output)) => (true, gas, output),
+ Result::Err(_) => (false, 0, [].span())
+ }
+ }
+ }
+
+ #[starknet::embeddable]
+ pub impl Helpers of super::IHelpers {
+ fn get_block_hash(self: @TContractState, block_number: u64) -> felt252 {
+ starknet::syscalls::get_block_hash_syscall(block_number).unwrap_syscall()
+ }
+
+ fn keccak(
+ self: @TContractState,
+ mut words: Array,
+ last_input_word: u64,
+ last_input_num_bytes: usize
+ ) -> u256 {
+ cairo_keccak(ref words, last_input_word, last_input_num_bytes).reverse_endianness()
+ }
+
+ fn compute_sha256_u32_array(
+ self: @TContractState,
+ input: Array,
+ last_input_word: u32,
+ last_input_num_bytes: u32
+ ) -> [
+ u32
+ ; 8] {
+ core::sha256::compute_sha256_u32_array(input, last_input_word, last_input_num_bytes)
+ }
+
+ // DEPRECATED
+ fn verify_eth_signature(
+ self: @TContractState, msg_hash: u256, signature: Signature, eth_address: EthAddress
+ ) {
+ verify_eth_signature(msg_hash, signature, eth_address);
+ }
+
+ fn recover_eth_address(
+ self: @TContractState, msg_hash: u256, signature: Signature
+ ) -> (bool, EthAddress) {
+ match recover_public_key::(:msg_hash, :signature) {
+ Option::Some(public_key_point) => {
+ let (x, y) = public_key_point.get_coordinates().unwrap_syscall();
+ if (x == 0 && y == 0) {
+ return (false, Zero::zero());
+ }
+ // Keccak output is little endian.
+ let point_hash_le = keccak_u256s_be_inputs([x, y].span());
+ let point_hash = u256 {
+ low: core::integer::u128_byte_reverse(point_hash_le.high),
+ high: core::integer::u128_byte_reverse(point_hash_le.low)
+ };
+
+ (true, point_hash.into())
+ },
+ Option::None => (false, Zero::zero())
+ }
+ }
+
+ fn verify_signature_secp256r1(
+ self: @TContractState, msg_hash: u256, r: u256, s: u256, x: u256, y: u256
+ ) -> bool {
+ let maybe_public_key: Option = Secp256Trait::secp256_ec_new_syscall(
+ x, y
+ )
+ .unwrap_syscall();
+ let public_key = match maybe_public_key {
+ Option::Some(public_key) => public_key,
+ Option::None => { return false; }
+ };
+
+ return is_valid_signature(msg_hash, r, s, public_key);
+ }
+ }
+}
+
+#[starknet::contract]
+pub mod Cairo1Helpers {
+ #[storage]
+ struct Storage {}
+
+ #[abi(embed_v0)]
+ pub impl Precompiles = super::embeddable_impls::Precompiles;
+
+ #[abi(embed_v0)]
+ pub impl Helpers = super::embeddable_impls::Helpers;
+}
diff --git a/cairo/kakarot-ssj/crates/contracts/src/components.cairo b/cairo/kakarot-ssj/crates/contracts/src/components.cairo
new file mode 100644
index 000000000..5ba62d80e
--- /dev/null
+++ b/cairo/kakarot-ssj/crates/contracts/src/components.cairo
@@ -0,0 +1,2 @@
+pub mod ownable;
+pub mod upgradeable;
diff --git a/cairo/kakarot-ssj/crates/contracts/src/components/ownable.cairo b/cairo/kakarot-ssj/crates/contracts/src/components/ownable.cairo
new file mode 100644
index 000000000..fc8f88db7
--- /dev/null
+++ b/cairo/kakarot-ssj/crates/contracts/src/components/ownable.cairo
@@ -0,0 +1,91 @@
+// This implementation of an Ownable is inspired by Openzeppelin's work on
+// OpenZeppelin Contracts for Cairo available here: https://github.com/OpenZeppelin/cairo-contracts
+
+use core::starknet::ContractAddress;
+pub mod Errors {
+ pub const NOT_OWNER: felt252 = 'Caller is not the owner';
+ pub const ZERO_ADDRESS_CALLER: felt252 = 'Caller is the zero address';
+ pub const ZERO_ADDRESS_OWNER: felt252 = 'New owner is the zero address';
+}
+
+#[starknet::interface]
+pub trait IOwnable {
+ fn owner(self: @TContractState) -> ContractAddress;
+ fn transfer_ownership(ref self: TContractState, new_owner: ContractAddress);
+ fn renounce_ownership(ref self: TContractState);
+}
+
+#[starknet::component]
+pub mod ownable_component {
+ use core::num::traits::Zero;
+ use core::starknet::storage::{StoragePointerReadAccess, StoragePointerWriteAccess};
+ use core::starknet::{get_caller_address, ContractAddress};
+ use super::Errors;
+
+ #[storage]
+ pub struct Storage {
+ pub Ownable_owner: ContractAddress
+ }
+
+ #[event]
+ #[derive(Drop, starknet::Event)]
+ pub enum Event {
+ OwnershipTransferred: OwnershipTransferred
+ }
+
+ #[derive(Drop, starknet::Event)]
+ pub struct OwnershipTransferred {
+ pub previous_owner: ContractAddress,
+ pub new_owner: ContractAddress,
+ }
+
+
+ #[embeddable_as(Ownable)]
+ pub impl OwnableImpl<
+ TContractState, +HasComponent
+ > of super::IOwnable> {
+ fn owner(self: @ComponentState) -> ContractAddress {
+ self.Ownable_owner.read()
+ }
+
+ fn transfer_ownership(
+ ref self: ComponentState, new_owner: ContractAddress
+ ) {
+ assert(!new_owner.is_zero(), Errors::ZERO_ADDRESS_OWNER);
+ self.assert_only_owner();
+ self._transfer_ownership(new_owner);
+ }
+
+ fn renounce_ownership(ref self: ComponentState) {
+ self.assert_only_owner();
+ self._transfer_ownership(Zero::zero());
+ }
+ }
+
+ #[generate_trait]
+ pub impl InternalImpl<
+ TContractState, +HasComponent
+ > of InternalTrait {
+ fn initializer(ref self: ComponentState, owner: ContractAddress) {
+ self._transfer_ownership(owner);
+ }
+
+ fn assert_only_owner(self: @ComponentState) {
+ let owner: ContractAddress = self.Ownable_owner.read();
+ let caller: ContractAddress = get_caller_address();
+ assert(!caller.is_zero(), Errors::ZERO_ADDRESS_CALLER);
+ assert(caller == owner, Errors::NOT_OWNER);
+ }
+
+ fn _transfer_ownership(
+ ref self: ComponentState, new_owner: ContractAddress
+ ) {
+ let previous_owner: ContractAddress = self.Ownable_owner.read();
+ self.Ownable_owner.write(new_owner);
+ self
+ .emit(
+ OwnershipTransferred { previous_owner: previous_owner, new_owner: new_owner }
+ );
+ }
+ }
+}
diff --git a/cairo/kakarot-ssj/crates/contracts/src/components/upgradeable.cairo b/cairo/kakarot-ssj/crates/contracts/src/components/upgradeable.cairo
new file mode 100644
index 000000000..67c441ef5
--- /dev/null
+++ b/cairo/kakarot-ssj/crates/contracts/src/components/upgradeable.cairo
@@ -0,0 +1,40 @@
+use core::starknet::ClassHash;
+
+#[starknet::interface]
+pub trait IUpgradeable {
+ fn upgrade_contract(ref self: TContractState, new_class_hash: ClassHash);
+}
+
+
+#[starknet::component]
+pub mod upgradeable_component {
+ use core::starknet::ClassHash;
+ use core::starknet::syscalls::{replace_class_syscall};
+
+
+ #[storage]
+ pub struct Storage {}
+
+ #[event]
+ #[derive(Drop, starknet::Event)]
+ pub enum Event {
+ ContractUpgraded: ContractUpgraded
+ }
+
+ #[derive(Drop, starknet::Event)]
+ struct ContractUpgraded {
+ new_class_hash: ClassHash
+ }
+
+ #[embeddable_as(Upgradeable)]
+ pub impl UpgradeableImpl<
+ TContractState, +HasComponent
+ > of super::IUpgradeable> {
+ fn upgrade_contract(
+ ref self: ComponentState, new_class_hash: starknet::ClassHash
+ ) {
+ replace_class_syscall(new_class_hash).expect('replace class failed');
+ self.emit(ContractUpgraded { new_class_hash: new_class_hash });
+ }
+ }
+}
diff --git a/cairo/kakarot-ssj/crates/contracts/src/errors.cairo b/cairo/kakarot-ssj/crates/contracts/src/errors.cairo
new file mode 100644
index 000000000..22b37eeee
--- /dev/null
+++ b/cairo/kakarot-ssj/crates/contracts/src/errors.cairo
@@ -0,0 +1,49 @@
+pub const BYTECODE_READ_ERROR: felt252 = 'CA: Bytecode Read Error';
+pub const BYTECODE_WRITE_ERROR: felt252 = 'CA: Bytecode Write Error';
+
+pub const STORAGE_READ_ERROR: felt252 = 'CA: Storage Read Error';
+pub const STORAGE_WRITE_ERROR: felt252 = 'CA: Storage Write Error';
+
+pub const NONCE_READ_ERROR: felt252 = 'CA: Nonce Read Error';
+pub const NONCE_WRITE_ERROR: felt252 = 'CA: Nonce Write Error';
+
+pub const KAKAROT_VALIDATION_FAILED: [
+ u8
+ ; 30] = [
+ 'K',
+ 'a',
+ 'k',
+ 'a',
+ 'r',
+ 'o',
+ 't',
+ ':',
+ ' ',
+ 'e',
+ 't',
+ 'h',
+ ' ',
+ 'v',
+ 'a',
+ 'l',
+ 'i',
+ 'd',
+ 'a',
+ 't',
+ 'i',
+ 'o',
+ 'n',
+ ' ',
+ 'f',
+ 'a',
+ 'i',
+ 'l',
+ 'e',
+ 'd'
+];
+
+pub const KAKAROT_REENTRANCY: [
+ felt252
+ ; 19] = [
+ 'K', 'a', 'k', 'a', 'r', 'o', 't', ':', ' ', 'r', 'e', 'e', 'n', 't', 'r', 'a', 'n', 'c', 'y'
+];
diff --git a/cairo/kakarot-ssj/crates/contracts/src/kakarot_core.cairo b/cairo/kakarot-ssj/crates/contracts/src/kakarot_core.cairo
new file mode 100644
index 000000000..0989f1244
--- /dev/null
+++ b/cairo/kakarot-ssj/crates/contracts/src/kakarot_core.cairo
@@ -0,0 +1,8 @@
+pub mod eth_rpc;
+pub mod interface;
+mod kakarot;
+pub use interface::{
+ IKakarotCore, IKakarotCoreDispatcher, IKakarotCoreDispatcherTrait,
+ IExtendedKakarotCoreDispatcher, IExtendedKakarotCoreDispatcherTrait
+};
+pub use kakarot::KakarotCore;
diff --git a/cairo/kakarot-ssj/crates/contracts/src/kakarot_core/eth_rpc.cairo b/cairo/kakarot-ssj/crates/contracts/src/kakarot_core/eth_rpc.cairo
new file mode 100644
index 000000000..b5259b65d
--- /dev/null
+++ b/cairo/kakarot-ssj/crates/contracts/src/kakarot_core/eth_rpc.cairo
@@ -0,0 +1,362 @@
+use core::num::traits::Zero;
+use core::starknet::get_tx_info;
+use core::starknet::{EthAddress, get_caller_address, ContractAddress};
+use crate::account_contract::{IAccountDispatcher, IAccountDispatcherTrait};
+use crate::kakarot_core::interface::IKakarotCore;
+use crate::kakarot_core::kakarot::{KakarotCore, KakarotCore::{KakarotCoreState}};
+use evm::backend::starknet_backend;
+use evm::backend::validation::validate_eth_tx;
+use evm::model::account::AccountTrait;
+use evm::model::{TransactionResult, Address};
+use evm::{EVMTrait};
+use openzeppelin::token::erc20::interface::{IERC20CamelDispatcher, IERC20CamelDispatcherTrait};
+use utils::constants::POW_2_53;
+use utils::eth_transaction::transaction::{Transaction, TransactionTrait};
+
+#[starknet::interface]
+pub trait IEthRPC {
+ /// Returns the balance of the specified address.
+ ///
+ /// This is a view-only function that doesn't modify the state.
+ ///
+ /// # Arguments
+ ///
+ /// * `address` - The Ethereum address to get the balance from
+ ///
+ /// # Returns
+ ///
+ /// The balance of the address as a u256
+ fn eth_get_balance(self: @T, address: EthAddress) -> u256;
+
+ /// Returns the number of transactions sent from the specified address.
+ ///
+ /// This is a view-only function that doesn't modify the state.
+ ///
+ /// # Arguments
+ ///
+ /// * `address` - The Ethereum address to get the transaction count from
+ ///
+ /// # Returns
+ ///
+ /// The transaction count of the address as a u64
+ fn eth_get_transaction_count(self: @T, address: EthAddress) -> u64;
+
+ /// Returns the current chain ID.
+ ///
+ /// This is a view-only function that doesn't modify the state.
+ ///
+ /// # Returns
+ ///
+ /// The chain ID as a u64
+ fn eth_chain_id(self: @T) -> u64;
+
+ /// Executes a new message call immediately without creating a transaction on the block chain.
+ ///
+ /// This is a view-only function that doesn't modify the state.
+ ///
+ /// # Arguments
+ ///
+ /// * `origin` - The address the transaction is sent from
+ /// * `tx` - The transaction object
+ ///
+ /// # Returns
+ ///
+ /// A tuple containing:
+ /// * A boolean indicating success
+ /// * The return data as a Span
+ /// * The amount of gas used as a u64
+ fn eth_call(self: @T, origin: EthAddress, tx: Transaction) -> (bool, Span, u64);
+
+ /// Generates and returns an estimate of how much gas is necessary to allow the transaction to
+ /// complete.
+ ///
+ /// This is a view-only function that doesn't modify the state.
+ ///
+ /// # Arguments
+ ///
+ /// * `origin` - The address the transaction is sent from
+ /// * `tx` - The transaction object
+ ///
+ /// # Returns
+ ///
+ /// A tuple containing:
+ /// * A boolean indicating success
+ /// * The return data as a Span
+ /// * The estimated gas as a u64
+ fn eth_estimate_gas(self: @T, origin: EthAddress, tx: Transaction) -> (bool, Span, u64);
+
+
+ /// Executes an unsigned transaction.
+ ///
+ /// This is a modified version of the eth_sendRawTransaction function.
+ /// Signature validation should be done before calling this function.
+ ///
+ /// # Arguments
+ ///
+ /// * `tx_data` - The unsigned transaction data as a Span
+ ///
+ /// # Returns
+ ///
+ /// A tuple containing:
+ /// * A boolean indicating success
+ /// * The return data as a Span
+ /// * The amount of gas used as a u64
+ fn eth_send_raw_unsigned_tx(ref self: T, tx_data: Span) -> (bool, Span, u64);
+}
+
+
+#[starknet::embeddable]
+pub impl EthRPC<
+ TContractState, impl KakarotState: KakarotCoreState, +Drop
+> of IEthRPC {
+ fn eth_get_balance(self: @TContractState, address: EthAddress) -> u256 {
+ let kakarot_state = KakarotState::get_state();
+ let starknet_address = kakarot_state.get_starknet_address(address);
+ let native_token_address = kakarot_state.get_native_token();
+ let native_token = IERC20CamelDispatcher { contract_address: native_token_address };
+ native_token.balanceOf(starknet_address)
+ }
+
+ fn eth_get_transaction_count(self: @TContractState, address: EthAddress) -> u64 {
+ let kakarot_state = KakarotState::get_state();
+ let starknet_address = kakarot_state.get_starknet_address(address);
+ let account = IAccountDispatcher { contract_address: starknet_address };
+ let nonce = account.get_nonce();
+ nonce
+ }
+
+ fn eth_chain_id(self: @TContractState) -> u64 {
+ let tx_info = get_tx_info().unbox();
+ let tx_chain_id: u64 = tx_info.chain_id.try_into().unwrap();
+ tx_chain_id % POW_2_53.try_into().unwrap()
+ }
+
+ fn eth_call(
+ self: @TContractState, origin: EthAddress, tx: Transaction
+ ) -> (bool, Span, u64) {
+ let mut kakarot_state = KakarotState::get_state();
+ if !is_view(@kakarot_state) {
+ core::panic_with_felt252('fn must be called, not invoked');
+ };
+
+ let origin = Address { evm: origin, starknet: kakarot_state.get_starknet_address(origin) };
+
+ let TransactionResult { success, return_data, gas_used, state: _state } =
+ EVMTrait::process_transaction(
+ ref kakarot_state, origin, tx, 0
+ );
+
+ (success, return_data, gas_used)
+ }
+
+ fn eth_estimate_gas(
+ self: @TContractState, origin: EthAddress, tx: Transaction
+ ) -> (bool, Span, u64) {
+ panic!("unimplemented")
+ }
+
+ //TODO: we can't really unit-test this with foundry because we can't generate the RLP-encoding
+ //in Cairo Find another way - perhaps test-data gen with python?
+ fn eth_send_raw_unsigned_tx(
+ ref self: TContractState, mut tx_data: Span
+ ) -> (bool, Span, u64) {
+ let tx = TransactionTrait::decode_enveloped(ref tx_data).expect('EOA: could not decode tx');
+ EthRPCInternal::eth_send_transaction(ref self, tx)
+ }
+}
+
+trait EthRPCInternal {
+ /// Executes a transaction and possibly modifies the state.
+ ///
+ /// This function implements the `eth_sendTransaction` method as described in the Ethereum
+ /// JSON-RPC specification.
+ /// The nonce is taken from the corresponding account contract.
+ ///
+ /// # Arguments
+ ///
+ /// * `tx` - A `Transaction` struct
+ ///
+ /// # Returns
+ ///
+ /// A tuple containing:
+ /// * A boolean indicating success (TRUE if the transaction succeeded, FALSE otherwise)
+ /// * The return data as a `Span`
+ /// * The amount of gas used by the transaction as a `u64`
+ fn eth_send_transaction(ref self: T, tx: Transaction) -> (bool, Span, u64);
+}
+
+impl EthRPCInternalImpl<
+ TContractState, impl KakarotState: KakarotCoreState, +Drop
+> of EthRPCInternal {
+ fn eth_send_transaction(ref self: TContractState, tx: Transaction) -> (bool, Span, u64) {
+ let mut kakarot_state = KakarotState::get_state();
+ let intrinsic_gas = validate_eth_tx(@kakarot_state, tx);
+
+ let starknet_caller_address = get_caller_address();
+ // panics if the caller is a spoofer of an EVM address.
+ //TODO: e2e test this! :) Send a transaction from an account that is not Kakarot's account
+ //(e.g. deploy an account but not from Kakarot)
+ let origin_evm_address = safe_get_evm_address(@self, starknet_caller_address);
+ let origin = Address { evm: origin_evm_address, starknet: starknet_caller_address };
+
+ let TransactionResult { success, return_data, gas_used, mut state } =
+ EVMTrait::process_transaction(
+ ref kakarot_state, origin, tx, intrinsic_gas
+ );
+ starknet_backend::commit(ref state).expect('Committing state failed');
+ (success, return_data, gas_used)
+ }
+}
+
+
+/// Returns the EVM address associated with a Starknet account deployed by Kakarot.
+///
+/// This function prevents cases where a Starknet account has an entrypoint `get_evm_address()`
+/// but isn't part of the Kakarot system. It also mitigates re-entrancy risk with the Cairo Interop
+/// module.
+///
+/// # Arguments
+///
+/// * `starknet_address` - The Starknet address of the account
+///
+/// # Returns
+///
+/// * `EthAddress` - The associated EVM address
+///
+/// # Panics
+///
+/// Panics if the declared corresponding EVM address (retrieved with `get_evm_address`)
+/// does not recompute into the actual caller address.
+fn safe_get_evm_address<
+ TContractState, impl KakarotState: KakarotCoreState, +Drop
+>(
+ self: @TContractState, starknet_address: ContractAddress
+) -> EthAddress {
+ let account = IAccountDispatcher { contract_address: starknet_address };
+ let evm_address = account.get_evm_address();
+ let safe_starknet_address = AccountTrait::get_starknet_address(evm_address);
+ assert!(
+ safe_starknet_address == starknet_address,
+ "Kakarot: caller contract is not a Kakarot Account"
+ );
+ evm_address
+}
+
+fn is_view(self: @KakarotCore::ContractState) -> bool {
+ let tx_info = get_tx_info().unbox();
+
+ // If the account that originated the transaction is not zero, this means we
+ // are in an invoke transaction instead of a call; therefore, `eth_call` is being
+ // wrongly called For invoke transactions, `eth_send_transaction` must be used
+ if !tx_info.account_contract_address.is_zero() {
+ return false;
+ }
+ true
+}
+
+#[cfg(test)]
+mod tests {
+ use core::ops::DerefMut;
+ use core::starknet::EthAddress;
+ use core::starknet::storage::{StoragePathEntry, StoragePointerWriteAccess};
+ use crate::kakarot_core::KakarotCore;
+ use crate::kakarot_core::eth_rpc::IEthRPC;
+ use crate::kakarot_core::interface::{IKakarotCore, IExtendedKakarotCoreDispatcherTrait};
+ use crate::test_utils::{setup_contracts_for_testing, fund_account_with_native_token};
+ use evm::test_utils::{sequencer_evm_address, evm_address, uninitialized_account};
+ use snforge_std::{
+ start_mock_call, start_cheat_chain_id_global, stop_cheat_chain_id_global, test_address
+ };
+ use super::safe_get_evm_address;
+ use utils::constants::POW_2_53;
+
+ fn set_up() -> KakarotCore::ContractState {
+ // Define the kakarot state to access contract functions
+ let kakarot_state = KakarotCore::unsafe_new_contract_state();
+
+ kakarot_state
+ }
+
+ fn tear_down() {
+ stop_cheat_chain_id_global();
+ }
+
+ #[test]
+ fn test_eth_get_transaction_count() {
+ let kakarot_state = set_up();
+ let starknet_address = kakarot_state.get_starknet_address(evm_address());
+ start_mock_call::(starknet_address, selector!("get_nonce"), 1);
+ assert_eq!(kakarot_state.eth_get_transaction_count(evm_address()), 1);
+ }
+
+ #[test]
+ fn test_eth_get_balance() {
+ let (native_token, kakarot_core) = setup_contracts_for_testing();
+ // Uninitialized accounts should return a zero balance
+ assert_eq!(kakarot_core.eth_get_balance(evm_address()), 0);
+ let sequencer_starknet_address = kakarot_core.get_starknet_address(sequencer_evm_address());
+ // Fund an initialized account and make sure the balance is correct
+ fund_account_with_native_token(sequencer_starknet_address, native_token, 0x1);
+ assert_eq!(kakarot_core.eth_get_balance(sequencer_evm_address()), 0x1);
+ }
+
+ #[test]
+ fn test_eth_chain_id_returns_input_when_less_than_pow_2_53() {
+ let kakarot_state = KakarotCore::unsafe_new_contract_state();
+ // Convert POW_2_53 - 1 to u64 since POW_2_53 is defined as u128
+ let chain_id: u64 = (POW_2_53 - 1).try_into().unwrap();
+ start_cheat_chain_id_global(chain_id.into());
+ assert_eq!(
+ kakarot_state.eth_chain_id(),
+ chain_id,
+ "Should return original chain ID when below 2^53"
+ );
+ tear_down();
+ }
+
+ #[test]
+ fn test_eth_chain_id_returns_modulo_when_greater_than_or_equal_to_pow_2_53() {
+ // Test with a value equal to 2^53
+ let kakarot_state = set_up();
+ let chain_id: u64 = POW_2_53.try_into().unwrap();
+ start_cheat_chain_id_global(chain_id.into());
+ assert_eq!(kakarot_state.eth_chain_id(), 0, "Should return 0 when chain ID is 2^53");
+
+ // Test with a value greater than 2^53
+ let chain_id: u64 = (POW_2_53 + 53).try_into().unwrap();
+ start_cheat_chain_id_global(chain_id.into());
+ assert_eq!(
+ kakarot_state.eth_chain_id(), 53, "Should return correct value after modulo operation"
+ );
+ tear_down();
+ }
+
+ #[test]
+ fn test_safe_get_evm_address_succeeds() {
+ let kakarot_state = set_up();
+ // no registry - returns the computed address
+ let starknet_address = kakarot_state.get_starknet_address(evm_address());
+ start_mock_call::<
+ EthAddress
+ >(starknet_address, selector!("get_evm_address"), evm_address());
+ let safe_evm_address = safe_get_evm_address(@kakarot_state, starknet_address);
+ assert_eq!(safe_evm_address, evm_address());
+ }
+
+ #[test]
+ #[should_panic(expected: "Kakarot: caller contract is not a Kakarot Account")]
+ fn test_safe_get_evm_address_panics_when_caller_is_not_kakarot_account() {
+ let mut kakarot_state = set_up();
+ let mut kakarot_storage = kakarot_state.deref_mut();
+
+ // Calling get_evm_address() on a fake starknet account that will return `evm_address()`.
+ // Then, when computing the deterministic starknet_address with get_starknet_address(), it
+ // will return a different address.
+ // This should fail.
+ let fake_starknet_account = 'fake_account'.try_into().unwrap();
+ start_mock_call::<
+ EthAddress
+ >(fake_starknet_account, selector!("get_evm_address"), evm_address());
+ safe_get_evm_address(@kakarot_state, fake_starknet_account);
+ }
+}
diff --git a/cairo/kakarot-ssj/crates/contracts/src/kakarot_core/interface.cairo b/cairo/kakarot-ssj/crates/contracts/src/kakarot_core/interface.cairo
new file mode 100644
index 000000000..e0fcdb1a8
--- /dev/null
+++ b/cairo/kakarot-ssj/crates/contracts/src/kakarot_core/interface.cairo
@@ -0,0 +1,127 @@
+use core::starknet::{ContractAddress, EthAddress, ClassHash};
+use utils::eth_transaction::transaction::Transaction;
+
+#[starknet::interface]
+pub trait IKakarotCore {
+ /// Sets the native token, this token will be considered the native coin in the Ethereum sense
+ fn set_native_token(ref self: TContractState, native_token: ContractAddress);
+
+ /// Gets the native token used by the Kakarot smart contract
+ fn get_native_token(self: @TContractState) -> ContractAddress;
+
+ /// Checks into KakarotCore storage if an EOA or a CA has been deployed for
+ /// a particular EVM address and. If so returns its corresponding address,
+ /// otherwise returns 0
+ fn address_registry(self: @TContractState, evm_address: EthAddress) -> ContractAddress;
+
+ /// Deploys an EOA for a particular EVM address
+ fn deploy_externally_owned_account(
+ ref self: TContractState, evm_address: EthAddress
+ ) -> ContractAddress;
+
+ /// Upgrade the KakarotCore smart contract
+ /// Using replace_class_syscall
+ fn upgrade(ref self: TContractState, new_class_hash: ClassHash);
+
+ // Setter for the Account Class Hash
+ fn set_account_contract_class_hash(ref self: TContractState, new_class_hash: ClassHash);
+ fn get_account_contract_class_hash(self: @TContractState) -> ClassHash;
+
+ // Getter for the Generic Account Class
+ fn uninitialized_account_class_hash(self: @TContractState) -> ClassHash;
+ // Setter for the Generic Account Class
+ fn set_account_class_hash(ref self: TContractState, new_class_hash: ClassHash);
+
+ fn register_account(ref self: TContractState, evm_address: EthAddress);
+
+ // Getter for the Block Gas Limit
+ fn get_block_gas_limit(self: @TContractState) -> u64;
+
+ // Getter for the Base Fee
+ fn get_base_fee(self: @TContractState) -> u64;
+ /// Setter for the base fee
+ fn set_base_fee(ref self: TContractState, base_fee: u64);
+
+ /// Returns the corresponding Starknet address for a given EVM address.
+ ///
+ /// Returns the registered address if there is one, otherwise returns the deterministic
+ /// address got when Kakarot deploys an account.
+ ///
+ /// # Arguments
+ ///
+ /// * `evm_address` - The EVM address to transform to a starknet address
+ ///
+ /// # Returns
+ ///
+ /// * `ContractAddress` - The Starknet Account Contract address
+ fn get_starknet_address(self: @TContractState, evm_address: EthAddress) -> ContractAddress;
+}
+
+#[starknet::interface]
+pub trait IExtendedKakarotCore {
+ /// Sets the native token, this token will be considered the native coin in the Ethereum sense
+ fn set_native_token(ref self: TContractState, native_token: ContractAddress);
+
+ /// Gets the native token used by the Kakarot smart contract
+ fn get_native_token(self: @TContractState) -> ContractAddress;
+
+ /// Checks into KakarotCore storage if an EOA or a CA has been deployed for
+ /// a particular EVM address and. If so returns its corresponding address,
+ /// otherwise returns 0
+ fn address_registry(self: @TContractState, evm_address: EthAddress) -> ContractAddress;
+
+ /// Deploys an EOA for a particular EVM address
+ fn deploy_externally_owned_account(
+ ref self: TContractState, evm_address: EthAddress
+ ) -> ContractAddress;
+
+ /// Returns the balance of the specified address.
+ fn eth_get_balance(self: @TContractState, address: EthAddress) -> u256;
+
+ /// View entrypoint into the EVM
+ /// Performs view calls into the blockchain
+ /// It cannot modify the state of the chain
+ fn eth_call(
+ self: @TContractState, origin: EthAddress, tx: Transaction
+ ) -> (bool, Span, u64);
+
+ /// Transaction entrypoint into the EVM
+ /// Executes an EVM transaction and possibly modifies the state
+ fn eth_send_transaction(ref self: TContractState, tx: Transaction) -> (bool, Span, u64);
+
+ fn eth_send_raw_unsigned_tx(
+ ref self: TContractState, encoded_tx_data: Span
+ ) -> (bool, Span, u64);
+
+ // Returns the transaction count (nonce) of the specified address
+ fn eth_get_transaction_count(self: @TContractState, address: EthAddress) -> u64;
+
+ /// Upgrade the KakarotCore smart contract
+ /// Using replace_class_syscall
+ fn upgrade(ref self: TContractState, new_class_hash: ClassHash);
+
+ // Setter for the Account Class Hash
+ fn set_account_contract_class_hash(ref self: TContractState, new_class_hash: ClassHash);
+ fn get_account_contract_class_hash(self: @TContractState) -> ClassHash;
+
+ // Getter for the Generic Account Class
+ fn uninitialized_account_class_hash(self: @TContractState) -> ClassHash;
+ // Setter for the Generic Account Class
+ fn set_account_class_hash(ref self: TContractState, new_class_hash: ClassHash);
+
+ fn register_account(ref self: TContractState, evm_address: EthAddress);
+
+ // Getter for the Block Gas Limit
+ fn get_block_gas_limit(self: @TContractState) -> u64;
+ // Getter for the Base Fee
+ fn get_base_fee(self: @TContractState) -> u64;
+ /// Setter for the base fee
+ fn set_base_fee(ref self: TContractState, base_fee: u64);
+
+ // Getter for the Starknet Address
+ fn get_starknet_address(self: @TContractState, evm_address: EthAddress) -> ContractAddress;
+
+ fn owner(self: @TContractState) -> ContractAddress;
+ fn transfer_ownership(ref self: TContractState, new_owner: ContractAddress);
+ fn renounce_ownership(ref self: TContractState);
+}
diff --git a/cairo/kakarot-ssj/crates/contracts/src/kakarot_core/kakarot.cairo b/cairo/kakarot-ssj/crates/contracts/src/kakarot_core/kakarot.cairo
new file mode 100644
index 000000000..2b5f14355
--- /dev/null
+++ b/cairo/kakarot-ssj/crates/contracts/src/kakarot_core/kakarot.cairo
@@ -0,0 +1,218 @@
+const INVOKE_ETH_CALL_FORBIDDEN: felt252 = 'KKT: Cannot invoke eth_call';
+
+
+#[starknet::contract]
+pub mod KakarotCore {
+ use core::num::traits::Zero;
+ use core::starknet::event::EventEmitter;
+ use core::starknet::get_caller_address;
+ use core::starknet::storage::{
+ Map, StorageMapReadAccess, StorageMapWriteAccess, StoragePointerReadAccess,
+ StoragePointerWriteAccess
+ };
+ use core::starknet::{EthAddress, ContractAddress, ClassHash, get_contract_address};
+ use crate::components::ownable::{ownable_component};
+ use crate::components::upgradeable::{IUpgradeable, upgradeable_component};
+ use crate::kakarot_core::eth_rpc;
+ use crate::kakarot_core::interface::IKakarotCore;
+ use evm::backend::starknet_backend;
+ use evm::model::account::AccountTrait;
+ use utils::helpers::compute_starknet_address;
+
+ component!(path: ownable_component, storage: ownable, event: OwnableEvent);
+ component!(path: upgradeable_component, storage: upgradeable, event: UpgradeableEvent);
+
+ /// STORAGE ///
+
+ #[storage]
+ pub struct Storage {
+ pub Kakarot_evm_to_starknet_address: Map::,
+ pub Kakarot_uninitialized_account_class_hash: ClassHash,
+ pub Kakarot_account_contract_class_hash: ClassHash,
+ pub Kakarot_native_token_address: ContractAddress,
+ pub Kakarot_coinbase: EthAddress,
+ pub Kakarot_base_fee: u64,
+ pub Kakarot_prev_randao: u256,
+ pub Kakarot_block_gas_limit: u64,
+ // Components
+ #[substorage(v0)]
+ ownable: ownable_component::Storage,
+ #[substorage(v0)]
+ upgradeable: upgradeable_component::Storage,
+ }
+
+ /// EVENTS ///
+
+ #[event]
+ #[derive(Drop, starknet::Event)]
+ pub enum Event {
+ OwnableEvent: ownable_component::Event,
+ UpgradeableEvent: upgradeable_component::Event,
+ AccountDeployed: AccountDeployed,
+ AccountClassHashChange: AccountClassHashChange,
+ EOAClassHashChange: EOAClassHashChange,
+ }
+
+ #[derive(Copy, Drop, starknet::Event)]
+ pub struct AccountDeployed {
+ #[key]
+ pub evm_address: EthAddress,
+ #[key]
+ pub starknet_address: ContractAddress,
+ }
+
+ #[derive(Copy, Drop, starknet::Event)]
+ pub struct AccountClassHashChange {
+ pub old_class_hash: ClassHash,
+ pub new_class_hash: ClassHash,
+ }
+
+
+ #[derive(Copy, Drop, starknet::Event)]
+ pub struct EOAClassHashChange {
+ pub old_class_hash: ClassHash,
+ pub new_class_hash: ClassHash,
+ }
+
+
+ /// Trait bounds allowing embedded implementations to be specific to this contract
+ pub trait KakarotCoreState {
+ fn get_state() -> ContractState;
+ }
+
+ impl _KakarotCoreState of KakarotCoreState {
+ fn get_state() -> ContractState {
+ unsafe_new_contract_state()
+ }
+ }
+
+ /// CONSTRUCTOR ///
+
+ #[constructor]
+ fn constructor(
+ ref self: ContractState,
+ owner: ContractAddress,
+ native_token: ContractAddress,
+ account_contract_class_hash: ClassHash,
+ uninitialized_account_class_hash: ClassHash,
+ coinbase: EthAddress,
+ block_gas_limit: u64,
+ mut eoas_to_deploy: Span,
+ ) {
+ self.ownable.initializer(owner);
+ self.Kakarot_native_token_address.write(native_token);
+ self.Kakarot_account_contract_class_hash.write(account_contract_class_hash);
+ self.Kakarot_uninitialized_account_class_hash.write(uninitialized_account_class_hash);
+ self.Kakarot_coinbase.write(coinbase);
+ self.Kakarot_block_gas_limit.write(block_gas_limit);
+ for eoa_address in eoas_to_deploy {
+ self.deploy_externally_owned_account(*eoa_address);
+ };
+ }
+
+ /// PUBLIC-FACING FUNCTIONS ///
+
+ // Public-facing "ownable" functions
+ #[abi(embed_v0)]
+ impl OwnableImpl = ownable_component::Ownable;
+
+ /// Public-facing "ethereum" functions
+ /// Used to make EVM-related actions through Kakarot.
+ #[abi(embed_v0)]
+ pub impl EthRPCImpl = eth_rpc::EthRPC;
+
+
+ /// Public-facing "kakarot" functions
+ /// Used to interact with the Kakarot contract outside of EVM-related actions.
+ #[abi(embed_v0)]
+ pub impl KakarotCoreImpl of IKakarotCore {
+ fn set_native_token(ref self: ContractState, native_token: ContractAddress) {
+ self.ownable.assert_only_owner();
+ self.Kakarot_native_token_address.write(native_token);
+ }
+
+ fn get_native_token(self: @ContractState) -> ContractAddress {
+ self.Kakarot_native_token_address.read()
+ }
+
+ fn address_registry(self: @ContractState, evm_address: EthAddress) -> ContractAddress {
+ self.Kakarot_evm_to_starknet_address.read(evm_address)
+ }
+
+ fn deploy_externally_owned_account(
+ ref self: ContractState, evm_address: EthAddress
+ ) -> ContractAddress {
+ starknet_backend::deploy(evm_address).expect('EOA Deployment failed').starknet
+ }
+
+ fn upgrade(ref self: ContractState, new_class_hash: ClassHash) {
+ self.ownable.assert_only_owner();
+ self.upgradeable.upgrade_contract(new_class_hash);
+ }
+
+ fn get_account_contract_class_hash(self: @ContractState) -> ClassHash {
+ self.Kakarot_account_contract_class_hash.read()
+ }
+
+ fn set_account_contract_class_hash(ref self: ContractState, new_class_hash: ClassHash) {
+ self.ownable.assert_only_owner();
+ let old_class_hash = self.Kakarot_account_contract_class_hash.read();
+ self.Kakarot_account_contract_class_hash.write(new_class_hash);
+ self.emit(EOAClassHashChange { old_class_hash, new_class_hash });
+ }
+
+ fn uninitialized_account_class_hash(self: @ContractState) -> ClassHash {
+ self.Kakarot_uninitialized_account_class_hash.read()
+ }
+
+ fn set_account_class_hash(ref self: ContractState, new_class_hash: ClassHash) {
+ self.ownable.assert_only_owner();
+ let old_class_hash = self.Kakarot_uninitialized_account_class_hash.read();
+ self.Kakarot_uninitialized_account_class_hash.write(new_class_hash);
+ self.emit(AccountClassHashChange { old_class_hash, new_class_hash });
+ }
+
+ fn register_account(ref self: ContractState, evm_address: EthAddress) {
+ let existing_address = self.Kakarot_evm_to_starknet_address.read(evm_address);
+ assert(existing_address.is_zero(), 'Account already exists');
+
+ let starknet_address = compute_starknet_address(
+ get_contract_address(),
+ evm_address,
+ self.Kakarot_uninitialized_account_class_hash.read()
+ );
+ assert!(
+ starknet_address == get_caller_address(), "Account must be registered by the caller"
+ );
+
+ self.Kakarot_evm_to_starknet_address.write(evm_address, starknet_address);
+ self.emit(AccountDeployed { evm_address, starknet_address });
+ }
+
+ fn get_block_gas_limit(self: @ContractState) -> u64 {
+ self.Kakarot_block_gas_limit.read()
+ }
+
+ fn set_base_fee(ref self: ContractState, base_fee: u64) {
+ self.ownable.assert_only_owner();
+ self.Kakarot_base_fee.write(base_fee);
+ }
+
+ fn get_base_fee(self: @ContractState) -> u64 {
+ self.Kakarot_base_fee.read()
+ }
+
+
+ fn get_starknet_address(self: @ContractState, evm_address: EthAddress) -> ContractAddress {
+ AccountTrait::get_starknet_address(evm_address)
+ }
+ }
+
+ /// INTERNAL-FACING FUNCTIONS ///
+
+ // Internal-facing "ownable" functions
+ impl OwnableInternalImpl = ownable_component::InternalImpl;
+
+ // Internal-facing "upgradeable" functions
+ impl UpgradeableImpl = upgradeable_component::Upgradeable;
+}
diff --git a/cairo/kakarot-ssj/crates/contracts/src/lib.cairo b/cairo/kakarot-ssj/crates/contracts/src/lib.cairo
new file mode 100644
index 000000000..995015b10
--- /dev/null
+++ b/cairo/kakarot-ssj/crates/contracts/src/lib.cairo
@@ -0,0 +1,37 @@
+pub mod account_contract;
+pub mod cairo1_helpers;
+pub mod components;
+
+pub mod errors;
+
+// Kakarot smart contract
+pub mod kakarot_core;
+pub mod storage;
+
+#[cfg(target: 'test')]
+pub mod test_data;
+
+#[cfg(target: 'test')]
+pub mod test_utils;
+
+// Account transparent proxy
+mod uninitialized_account;
+pub use account_contract::{AccountContract, IAccount, IAccountDispatcher, IAccountDispatcherTrait};
+pub use cairo1_helpers::{
+ Cairo1Helpers, IPrecompiles, IHelpers, IPrecompilesDispatcher, IHelpersDispatcher,
+ IPrecompilesDispatcherTrait, IHelpersDispatcherTrait
+};
+pub use kakarot_core::{
+ KakarotCore, IKakarotCore, IKakarotCoreDispatcher, IKakarotCoreDispatcherTrait,
+ IExtendedKakarotCoreDispatcher, IExtendedKakarotCoreDispatcherTrait
+};
+pub use uninitialized_account::{UninitializedAccount};
+
+//TODO: hide this behind a feature flag
+pub mod test_contracts {
+ pub mod test_upgradeable;
+}
+
+pub mod mocks {
+ pub mod cairo1_helpers_fixture;
+}
diff --git a/cairo/kakarot-ssj/crates/contracts/src/mocks/cairo1_helpers_fixture.cairo b/cairo/kakarot-ssj/crates/contracts/src/mocks/cairo1_helpers_fixture.cairo
new file mode 100644
index 000000000..1a00d76b5
--- /dev/null
+++ b/cairo/kakarot-ssj/crates/contracts/src/mocks/cairo1_helpers_fixture.cairo
@@ -0,0 +1,15 @@
+#[starknet::contract]
+pub mod Cairo1HelpersFixture {
+ use crate::cairo1_helpers::embeddable_impls;
+
+ const VERSION: felt252 = 2;
+
+ #[storage]
+ struct Storage {}
+
+ #[abi(embed_v0)]
+ impl Precompiles = embeddable_impls::Precompiles;
+
+ #[abi(embed_v0)]
+ impl Helpers = embeddable_impls::Helpers;
+}
diff --git a/cairo/kakarot-ssj/crates/contracts/src/storage.cairo b/cairo/kakarot-ssj/crates/contracts/src/storage.cairo
new file mode 100644
index 000000000..3f047b1a8
--- /dev/null
+++ b/cairo/kakarot-ssj/crates/contracts/src/storage.cairo
@@ -0,0 +1,199 @@
+use core::ops::DerefMut;
+use core::ops::SnapshotDeref;
+use core::starknet::storage::{
+ StoragePointerReadAccess, StoragePointerWriteAccess, StorageTrait, StorageTraitMut
+};
+use core::starknet::storage_access::StorageBaseAddress;
+use core::starknet::syscalls::{storage_read_syscall, storage_write_syscall};
+use core::starknet::{SyscallResult, Store, StorageAddress};
+use crate::account_contract::AccountContract::unsafe_new_contract_state as account_contract_state;
+use utils::utils::{pack_bytes, load_packed_bytes};
+
+/// A wrapper type for the bytecode storage. Packing / unpacking is done transparently inside the
+/// `read` and `write` methods of `Store`.
+#[derive(Copy, Drop)]
+pub struct StorageBytecode {
+ pub bytecode: Span
+}
+
+const BYTES_PER_FELT: NonZero = 31;
+
+/// An implementation of the `Store` trait for our specific `StorageBytecode` type.
+/// The packing-unpacking is done inside the `read` and `write` methods, thus transparent to the
+/// user.
+/// The bytecode is stored sequentially, starting from storage address 0, for compatibility purposes
+/// with KakarotZero.
+/// The bytecode length is stored in the `Account_bytecode_len` storage variable, which is accessed
+/// by the `read` and `write` methods.
+impl StoreBytecode of Store {
+ /// Side effect: reads the bytecode len from the Account_bytecode_len storage variable
+ fn read(address_domain: u32, base: StorageBaseAddress) -> SyscallResult {
+ // Read the bytecode len from the storage of the current contract
+ let state = account_contract_state();
+ let bytecode_len: u32 = state.snapshot_deref().storage().Account_bytecode_len.read();
+ let (chunks_count, _remainder) = DivRem::div_rem(bytecode_len, BYTES_PER_FELT);
+
+ // Read the bytecode from the storage of the current contract, starting from address 0.
+ //TODO(opti): unpack chunks directly instead of reading them one by one and unpacking them
+ // afterwards.
+ let base: felt252 = 0;
+ let mut packed_bytecode = array![];
+ for i in 0
+ ..chunks_count
+ + 1 {
+ let storage_address: StorageAddress = (base + i.into()).try_into().unwrap();
+ let chunk = storage_read_syscall(address_domain, storage_address).unwrap();
+ packed_bytecode.append(chunk);
+ };
+ let bytecode = load_packed_bytes(packed_bytecode.span(), bytecode_len);
+ SyscallResult::Ok(StorageBytecode { bytecode: bytecode.span() })
+ }
+
+ /// Side effect: Writes the bytecode len to the Account_bytecode_len storage variable
+ fn write(
+ address_domain: u32, base: StorageBaseAddress, value: StorageBytecode
+ ) -> SyscallResult<()> {
+ let base: felt252 = 0;
+ let mut state = account_contract_state();
+ let bytecode_len: u32 = value.bytecode.len();
+ state.deref_mut().storage_mut().Account_bytecode_len.write(bytecode_len);
+
+ let mut packed_bytecode = pack_bytes(value.bytecode);
+ let mut i = 0;
+ for chunk in packed_bytecode {
+ let storage_address: StorageAddress = (base + i.into()).try_into().unwrap();
+ storage_write_syscall(address_domain, storage_address, chunk).unwrap();
+ i += 1;
+ };
+ SyscallResult::Ok(())
+ }
+
+ fn read_at_offset(
+ address_domain: u32, base: StorageBaseAddress, offset: u8
+ ) -> SyscallResult {
+ panic!("'read_at_offset' is not implemented for StoreBytecode")
+ }
+
+ fn write_at_offset(
+ address_domain: u32, base: StorageBaseAddress, offset: u8, value: StorageBytecode
+ ) -> SyscallResult<()> {
+ panic!("'write_at_offset' is not implemented for StoreBytecode")
+ }
+
+ fn size() -> u8 {
+ panic!("'size' is not implemented for StoreBytecode")
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use core::starknet::storage::{StoragePointerReadAccess, StoragePointerWriteAccess};
+ use crate::account_contract::AccountContract::unsafe_new_contract_state as account_contract_state;
+ use starknet::storage_access::Store;
+ use starknet::storage_access::{
+ StorageBaseAddress, StorageAddress, storage_base_address_from_felt252
+ };
+ use starknet::syscalls::storage_read_syscall;
+ use super::DerefMut;
+ use super::SnapshotDeref;
+ use super::StorageBytecode;
+ use super::StorageTrait;
+ use super::StorageTraitMut;
+ use utils::utils::pack_bytes;
+
+ #[test]
+ fn test_store_bytecode_empty() {
+ let mut state = account_contract_state();
+ let bytecode = [].span();
+ // Write the bytecode to the storage
+ state.deref_mut().storage_mut().Account_bytecode.write(StorageBytecode { bytecode });
+ // Verify that the bytecode was written correctly and the len as well
+ let bytecode_len = state.snapshot_deref().storage().Account_bytecode_len.read();
+ let stored_bytecode = state.snapshot_deref().storage().Account_bytecode.read();
+ assert_eq!(bytecode_len, bytecode.len());
+ assert_eq!(stored_bytecode.bytecode, bytecode);
+ }
+
+ #[test]
+ fn test_store_bytecode_single_chunk() {
+ let mut state = account_contract_state();
+ let bytecode = [0, 1, 2, 3, 4, 5].span();
+ // Write the bytecode to the storage
+ state.deref_mut().storage_mut().Account_bytecode.write(StorageBytecode { bytecode });
+ // Verify that the bytecode was written correctly and the len as well
+ let bytecode_len = state.snapshot_deref().storage().Account_bytecode_len.read();
+ let stored_bytecode = state.snapshot_deref().storage().Account_bytecode.read();
+ assert_eq!(bytecode_len, bytecode.len());
+ assert_eq!(stored_bytecode.bytecode, bytecode);
+ }
+
+ #[test]
+ fn test_store_bytecode_multiple_chunks() {
+ let mut state = account_contract_state();
+ let mut bytecode_array = array![];
+ for i in 0..100_u8 {
+ bytecode_array.append(i);
+ };
+ let bytecode = bytecode_array.span();
+ // Write the bytecode to the storage
+ state.deref_mut().storage_mut().Account_bytecode.write(StorageBytecode { bytecode });
+ // Verify that the bytecode was written correctly and the len as well
+ let bytecode_len = state.snapshot_deref().storage().Account_bytecode_len.read();
+ let stored_bytecode = state.snapshot_deref().storage().Account_bytecode.read();
+ assert_eq!(bytecode_len, bytecode.len());
+ assert_eq!(stored_bytecode.bytecode, bytecode);
+ }
+
+ #[test]
+ fn test_store_bytecode_partial_chunk() {
+ let mut state = account_contract_state();
+ let bytecode = [
+ 1
+ ; 33].span(); // 33 bytes will require 2 chunks, with the second chunk partially filled
+ // Write the bytecode to the storage
+ state.deref_mut().storage_mut().Account_bytecode.write(StorageBytecode { bytecode });
+ // Verify that the bytecode was written correctly and the len as well
+ let bytecode_len = state.snapshot_deref().storage().Account_bytecode_len.read();
+ let stored_bytecode = state.snapshot_deref().storage().Account_bytecode.read();
+ assert_eq!(bytecode_len, bytecode.len());
+ assert_eq!(stored_bytecode.bytecode, bytecode);
+ }
+
+ #[test]
+ fn test_storage_layout_sequential_from_zero() {
+ let base_address: StorageAddress = 0.try_into().unwrap();
+ let bytecode = [0x12; 33].span();
+ let stored_bytecode = StorageBytecode { bytecode };
+ let mut state = account_contract_state();
+ state.deref_mut().storage_mut().Account_bytecode.write(stored_bytecode);
+
+ // Verify that the bytecode was packed in chunks sequential from zero
+ let chunk0 = storage_read_syscall(0, base_address).unwrap();
+ let chunk1 = storage_read_syscall(0, 1.try_into().unwrap()).unwrap();
+
+ assert_eq!(chunk0, (*pack_bytes([0x12; 31].span())[0]));
+ assert_eq!(chunk1, 0x1212);
+ }
+
+ #[test]
+ #[should_panic(expected: "'read_at_offset' is not implemented for StoreBytecode")]
+ fn test_read_at_offset_panics() {
+ let base_address: StorageBaseAddress = storage_base_address_from_felt252(0);
+ let _ = Store::::read_at_offset(0, base_address, 0);
+ }
+
+ #[test]
+ #[should_panic(expected: "'write_at_offset' is not implemented for StoreBytecode")]
+ fn test_write_at_offset_panics() {
+ let base_address: StorageBaseAddress = storage_base_address_from_felt252(0);
+ let bytecode = array![].span();
+ let stored_bytecode = StorageBytecode { bytecode };
+ let _ = Store::::write_at_offset(0, base_address, 0, stored_bytecode);
+ }
+
+ #[test]
+ #[should_panic(expected: "'size' is not implemented for StoreBytecode")]
+ fn test_size_panics() {
+ let _ = Store::::size();
+ }
+}
diff --git a/cairo/kakarot-ssj/crates/contracts/src/test_contracts/test_upgradeable.cairo b/cairo/kakarot-ssj/crates/contracts/src/test_contracts/test_upgradeable.cairo
new file mode 100644
index 000000000..22cf19587
--- /dev/null
+++ b/cairo/kakarot-ssj/crates/contracts/src/test_contracts/test_upgradeable.cairo
@@ -0,0 +1,102 @@
+use crate::components::upgradeable::{upgradeable_component};
+
+use upgradeable_component::{UpgradeableImpl};
+
+#[starknet::interface]
+pub trait IMockContractUpgradeable {
+ fn version(self: @TContractState) -> felt252;
+}
+
+#[starknet::contract]
+pub mod MockContractUpgradeableV0 {
+ use crate::components::upgradeable::{upgradeable_component};
+ use super::IMockContractUpgradeable;
+ component!(path: upgradeable_component, storage: upgradeable, event: UpgradeableEvent);
+
+ #[abi(embed_v0)]
+ impl UpgradeableImpl = upgradeable_component::Upgradeable;
+
+ #[storage]
+ struct Storage {
+ #[substorage(v0)]
+ upgradeable: upgradeable_component::Storage
+ }
+
+ #[event]
+ #[derive(Drop, starknet::Event)]
+ pub enum Event {
+ UpgradeableEvent: upgradeable_component::Event
+ }
+
+ #[abi(embed_v0)]
+ impl MockContractUpgradeableImpl of IMockContractUpgradeable {
+ fn version(self: @ContractState) -> felt252 {
+ 0
+ }
+ }
+}
+
+#[starknet::contract]
+pub mod MockContractUpgradeableV1 {
+ use crate::components::upgradeable::{upgradeable_component};
+ use super::IMockContractUpgradeable;
+ component!(path: upgradeable_component, storage: upgradeable, event: upgradeableEvent);
+
+ #[storage]
+ struct Storage {
+ #[substorage(v0)]
+ upgradeable: upgradeable_component::Storage
+ }
+
+ #[event]
+ #[derive(Drop, starknet::Event)]
+ pub enum Event {
+ upgradeableEvent: upgradeable_component::Event
+ }
+
+ #[abi(embed_v0)]
+ impl MockContractUpgradeableImpl of IMockContractUpgradeable {
+ fn version(self: @ContractState) -> felt252 {
+ 1
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use core::starknet::syscalls::{deploy_syscall};
+ use crate::components::upgradeable::{IUpgradeableDispatcher, IUpgradeableDispatcherTrait};
+ use snforge_std::{declare, DeclareResultTrait};
+ use starknet::{ClassHash};
+ use super::{IMockContractUpgradeableDispatcher, IMockContractUpgradeableDispatcherTrait};
+
+ #[test]
+ fn test_upgradeable_update_contract() {
+ let mock_contract_upgradeable_v0_class_hash = (*declare("MockContractUpgradeableV0")
+ .unwrap()
+ .contract_class()
+ .class_hash);
+ let (contract_address, _) = deploy_syscall(
+ mock_contract_upgradeable_v0_class_hash, 0, [].span(), false
+ )
+ .unwrap();
+
+ let version = IMockContractUpgradeableDispatcher { contract_address: contract_address }
+ .version();
+
+ assert(version == 0, 'version is not 0');
+
+ let mock_contract_upgradeable_v1_class_hash = (*declare("MockContractUpgradeableV1")
+ .unwrap()
+ .contract_class()
+ .class_hash);
+ let new_class_hash: ClassHash = mock_contract_upgradeable_v1_class_hash;
+
+ IUpgradeableDispatcher { contract_address: contract_address }
+ .upgrade_contract(new_class_hash);
+
+ let version = IMockContractUpgradeableDispatcher { contract_address: contract_address }
+ .version();
+ assert(version == 1, 'version is not 1');
+ }
+}
diff --git a/cairo/kakarot-ssj/crates/contracts/src/test_data.cairo b/cairo/kakarot-ssj/crates/contracts/src/test_data.cairo
new file mode 100644
index 000000000..b56a08684
--- /dev/null
+++ b/cairo/kakarot-ssj/crates/contracts/src/test_data.cairo
@@ -0,0 +1,1781 @@
+// Counter Smart Contract Bytecode:
+// 0.8.18+commit.87f61d96
+// with optimisation enabled (depth 200) (remix.ethereum.org)
+// SPDX-License-Identifier: MIT
+// pragma solidity >=0.7.0 <0.9.0;
+
+// contract Counter {
+// uint public count;
+
+// // Function to get the current count
+// function get() public view returns (uint) {
+// return count;
+// }
+
+// // Function to increment count by 1
+// function inc() public {
+// count += 1;
+// }
+
+// // Function to decrement count by 1
+// function dec() public {
+// // This function will fail if count = 0
+// count -= 1;
+// }
+// }
+
+pub fn deploy_counter_calldata() -> Span {
+ [
+ 0x60,
+ 0x80,
+ 0x60,
+ 0x40,
+ 0x52,
+ 0x34,
+ 0x80,
+ 0x15,
+ 0x61,
+ 0x00,
+ 0x0f,
+ 0x57,
+ 0x5f,
+ 0x80,
+ 0xfd,
+ 0x5b,
+ 0x50,
+ 0x61,
+ 0x01,
+ 0xd9,
+ 0x80,
+ 0x61,
+ 0x00,
+ 0x1d,
+ 0x5f,
+ 0x39,
+ 0x5f,
+ 0xf3,
+ 0xfe,
+ 0x60,
+ 0x80,
+ 0x60,
+ 0x40,
+ 0x52,
+ 0x34,
+ 0x80,
+ 0x15,
+ 0x61,
+ 0x00,
+ 0x0f,
+ 0x57,
+ 0x5f,
+ 0x80,
+ 0xfd,
+ 0x5b,
+ 0x50,
+ 0x60,
+ 0x04,
+ 0x36,
+ 0x10,
+ 0x61,
+ 0x00,
+ 0x4a,
+ 0x57,
+ 0x5f,
+ 0x35,
+ 0x60,
+ 0xe0,
+ 0x1c,
+ 0x80,
+ 0x63,
+ 0x06,
+ 0x66,
+ 0x1a,
+ 0xbd,
+ 0x14,
+ 0x61,
+ 0x00,
+ 0x4e,
+ 0x57,
+ 0x80,
+ 0x63,
+ 0x37,
+ 0x13,
+ 0x03,
+ 0xc0,
+ 0x14,
+ 0x61,
+ 0x00,
+ 0x6c,
+ 0x57,
+ 0x80,
+ 0x63,
+ 0x6d,
+ 0x4c,
+ 0xe6,
+ 0x3c,
+ 0x14,
+ 0x61,
+ 0x00,
+ 0x76,
+ 0x57,
+ 0x80,
+ 0x63,
+ 0xb3,
+ 0xbc,
+ 0xfa,
+ 0x82,
+ 0x14,
+ 0x61,
+ 0x00,
+ 0x94,
+ 0x57,
+ 0x5b,
+ 0x5f,
+ 0x80,
+ 0xfd,
+ 0x5b,
+ 0x61,
+ 0x00,
+ 0x56,
+ 0x61,
+ 0x00,
+ 0x9e,
+ 0x56,
+ 0x5b,
+ 0x60,
+ 0x40,
+ 0x51,
+ 0x61,
+ 0x00,
+ 0x63,
+ 0x91,
+ 0x90,
+ 0x61,
+ 0x00,
+ 0xf7,
+ 0x56,
+ 0x5b,
+ 0x60,
+ 0x40,
+ 0x51,
+ 0x80,
+ 0x91,
+ 0x03,
+ 0x90,
+ 0xf3,
+ 0x5b,
+ 0x61,
+ 0x00,
+ 0x74,
+ 0x61,
+ 0x00,
+ 0xa3,
+ 0x56,
+ 0x5b,
+ 0x00,
+ 0x5b,
+ 0x61,
+ 0x00,
+ 0x7e,
+ 0x61,
+ 0x00,
+ 0xbd,
+ 0x56,
+ 0x5b,
+ 0x60,
+ 0x40,
+ 0x51,
+ 0x61,
+ 0x00,
+ 0x8b,
+ 0x91,
+ 0x90,
+ 0x61,
+ 0x00,
+ 0xf7,
+ 0x56,
+ 0x5b,
+ 0x60,
+ 0x40,
+ 0x51,
+ 0x80,
+ 0x91,
+ 0x03,
+ 0x90,
+ 0xf3,
+ 0x5b,
+ 0x61,
+ 0x00,
+ 0x9c,
+ 0x61,
+ 0x00,
+ 0xc5,
+ 0x56,
+ 0x5b,
+ 0x00,
+ 0x5b,
+ 0x5f,
+ 0x54,
+ 0x81,
+ 0x56,
+ 0x5b,
+ 0x60,
+ 0x01,
+ 0x5f,
+ 0x80,
+ 0x82,
+ 0x82,
+ 0x54,
+ 0x61,
+ 0x00,
+ 0xb4,
+ 0x91,
+ 0x90,
+ 0x61,
+ 0x01,
+ 0x3d,
+ 0x56,
+ 0x5b,
+ 0x92,
+ 0x50,
+ 0x50,
+ 0x81,
+ 0x90,
+ 0x55,
+ 0x50,
+ 0x56,
+ 0x5b,
+ 0x5f,
+ 0x80,
+ 0x54,
+ 0x90,
+ 0x50,
+ 0x90,
+ 0x56,
+ 0x5b,
+ 0x60,
+ 0x01,
+ 0x5f,
+ 0x80,
+ 0x82,
+ 0x82,
+ 0x54,
+ 0x61,
+ 0x00,
+ 0xd6,
+ 0x91,
+ 0x90,
+ 0x61,
+ 0x01,
+ 0x70,
+ 0x56,
+ 0x5b,
+ 0x92,
+ 0x50,
+ 0x50,
+ 0x81,
+ 0x90,
+ 0x55,
+ 0x50,
+ 0x56,
+ 0x5b,
+ 0x5f,
+ 0x81,
+ 0x90,
+ 0x50,
+ 0x91,
+ 0x90,
+ 0x50,
+ 0x56,
+ 0x5b,
+ 0x61,
+ 0x00,
+ 0xf1,
+ 0x81,
+ 0x61,
+ 0x00,
+ 0xdf,
+ 0x56,
+ 0x5b,
+ 0x82,
+ 0x52,
+ 0x50,
+ 0x50,
+ 0x56,
+ 0x5b,
+ 0x5f,
+ 0x60,
+ 0x20,
+ 0x82,
+ 0x01,
+ 0x90,
+ 0x50,
+ 0x61,
+ 0x01,
+ 0x0a,
+ 0x5f,
+ 0x83,
+ 0x01,
+ 0x84,
+ 0x61,
+ 0x00,
+ 0xe8,
+ 0x56,
+ 0x5b,
+ 0x92,
+ 0x91,
+ 0x50,
+ 0x50,
+ 0x56,
+ 0x5b,
+ 0x7f,
+ 0x4e,
+ 0x48,
+ 0x7b,
+ 0x71,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x5f,
+ 0x52,
+ 0x60,
+ 0x11,
+ 0x60,
+ 0x04,
+ 0x52,
+ 0x60,
+ 0x24,
+ 0x5f,
+ 0xfd,
+ 0x5b,
+ 0x5f,
+ 0x61,
+ 0x01,
+ 0x47,
+ 0x82,
+ 0x61,
+ 0x00,
+ 0xdf,
+ 0x56,
+ 0x5b,
+ 0x91,
+ 0x50,
+ 0x61,
+ 0x01,
+ 0x52,
+ 0x83,
+ 0x61,
+ 0x00,
+ 0xdf,
+ 0x56,
+ 0x5b,
+ 0x92,
+ 0x50,
+ 0x82,
+ 0x82,
+ 0x01,
+ 0x90,
+ 0x50,
+ 0x80,
+ 0x82,
+ 0x11,
+ 0x15,
+ 0x61,
+ 0x01,
+ 0x6a,
+ 0x57,
+ 0x61,
+ 0x01,
+ 0x69,
+ 0x61,
+ 0x01,
+ 0x10,
+ 0x56,
+ 0x5b,
+ 0x5b,
+ 0x92,
+ 0x91,
+ 0x50,
+ 0x50,
+ 0x56,
+ 0x5b,
+ 0x5f,
+ 0x61,
+ 0x01,
+ 0x7a,
+ 0x82,
+ 0x61,
+ 0x00,
+ 0xdf,
+ 0x56,
+ 0x5b,
+ 0x91,
+ 0x50,
+ 0x61,
+ 0x01,
+ 0x85,
+ 0x83,
+ 0x61,
+ 0x00,
+ 0xdf,
+ 0x56,
+ 0x5b,
+ 0x92,
+ 0x50,
+ 0x82,
+ 0x82,
+ 0x03,
+ 0x90,
+ 0x50,
+ 0x81,
+ 0x81,
+ 0x11,
+ 0x15,
+ 0x61,
+ 0x01,
+ 0x9d,
+ 0x57,
+ 0x61,
+ 0x01,
+ 0x9c,
+ 0x61,
+ 0x01,
+ 0x10,
+ 0x56,
+ 0x5b,
+ 0x5b,
+ 0x92,
+ 0x91,
+ 0x50,
+ 0x50,
+ 0x56,
+ 0xfe,
+ 0xa2,
+ 0x64,
+ 0x69,
+ 0x70,
+ 0x66,
+ 0x73,
+ 0x58,
+ 0x22,
+ 0x12,
+ 0x20,
+ 0x7e,
+ 0x79,
+ 0x2f,
+ 0xcf,
+ 0xf2,
+ 0x8a,
+ 0x4b,
+ 0xf0,
+ 0xba,
+ 0xd8,
+ 0x67,
+ 0x5c,
+ 0x5b,
+ 0xc2,
+ 0x28,
+ 0x8b,
+ 0x07,
+ 0x83,
+ 0x5a,
+ 0xeb,
+ 0xaa,
+ 0x90,
+ 0xb8,
+ 0xdc,
+ 0x5e,
+ 0x0d,
+ 0xf1,
+ 0x91,
+ 0x83,
+ 0xfb,
+ 0x72,
+ 0xcf,
+ 0x64,
+ 0x73,
+ 0x6f,
+ 0x6c,
+ 0x63,
+ 0x43,
+ 0x00,
+ 0x08,
+ 0x16,
+ 0x00,
+ 0x33
+ ].span()
+}
+
+pub fn counter_evm_bytecode() -> Span {
+ [
+ 0x60,
+ 0x80,
+ 0x60,
+ 0x40,
+ 0x52,
+ 0x34,
+ 0x80,
+ 0x15,
+ 0x61,
+ 0x00,
+ 0x0f,
+ 0x57,
+ 0x5f,
+ 0x80,
+ 0xfd,
+ 0x5b,
+ 0x50,
+ 0x60,
+ 0x04,
+ 0x36,
+ 0x10,
+ 0x61,
+ 0x00,
+ 0x4a,
+ 0x57,
+ 0x5f,
+ 0x35,
+ 0x60,
+ 0xe0,
+ 0x1c,
+ 0x80,
+ 0x63,
+ 0x06,
+ 0x66,
+ 0x1a,
+ 0xbd,
+ 0x14,
+ 0x61,
+ 0x00,
+ 0x4e,
+ 0x57,
+ 0x80,
+ 0x63,
+ 0x37,
+ 0x13,
+ 0x03,
+ 0xc0,
+ 0x14,
+ 0x61,
+ 0x00,
+ 0x6c,
+ 0x57,
+ 0x80,
+ 0x63,
+ 0x6d,
+ 0x4c,
+ 0xe6,
+ 0x3c,
+ 0x14,
+ 0x61,
+ 0x00,
+ 0x76,
+ 0x57,
+ 0x80,
+ 0x63,
+ 0xb3,
+ 0xbc,
+ 0xfa,
+ 0x82,
+ 0x14,
+ 0x61,
+ 0x00,
+ 0x94,
+ 0x57,
+ 0x5b,
+ 0x5f,
+ 0x80,
+ 0xfd,
+ 0x5b,
+ 0x61,
+ 0x00,
+ 0x56,
+ 0x61,
+ 0x00,
+ 0x9e,
+ 0x56,
+ 0x5b,
+ 0x60,
+ 0x40,
+ 0x51,
+ 0x61,
+ 0x00,
+ 0x63,
+ 0x91,
+ 0x90,
+ 0x61,
+ 0x00,
+ 0xf7,
+ 0x56,
+ 0x5b,
+ 0x60,
+ 0x40,
+ 0x51,
+ 0x80,
+ 0x91,
+ 0x03,
+ 0x90,
+ 0xf3,
+ 0x5b,
+ 0x61,
+ 0x00,
+ 0x74,
+ 0x61,
+ 0x00,
+ 0xa3,
+ 0x56,
+ 0x5b,
+ 0x00,
+ 0x5b,
+ 0x61,
+ 0x00,
+ 0x7e,
+ 0x61,
+ 0x00,
+ 0xbd,
+ 0x56,
+ 0x5b,
+ 0x60,
+ 0x40,
+ 0x51,
+ 0x61,
+ 0x00,
+ 0x8b,
+ 0x91,
+ 0x90,
+ 0x61,
+ 0x00,
+ 0xf7,
+ 0x56,
+ 0x5b,
+ 0x60,
+ 0x40,
+ 0x51,
+ 0x80,
+ 0x91,
+ 0x03,
+ 0x90,
+ 0xf3,
+ 0x5b,
+ 0x61,
+ 0x00,
+ 0x9c,
+ 0x61,
+ 0x00,
+ 0xc5,
+ 0x56,
+ 0x5b,
+ 0x00,
+ 0x5b,
+ 0x5f,
+ 0x54,
+ 0x81,
+ 0x56,
+ 0x5b,
+ 0x60,
+ 0x01,
+ 0x5f,
+ 0x80,
+ 0x82,
+ 0x82,
+ 0x54,
+ 0x61,
+ 0x00,
+ 0xb4,
+ 0x91,
+ 0x90,
+ 0x61,
+ 0x01,
+ 0x3d,
+ 0x56,
+ 0x5b,
+ 0x92,
+ 0x50,
+ 0x50,
+ 0x81,
+ 0x90,
+ 0x55,
+ 0x50,
+ 0x56,
+ 0x5b,
+ 0x5f,
+ 0x80,
+ 0x54,
+ 0x90,
+ 0x50,
+ 0x90,
+ 0x56,
+ 0x5b,
+ 0x60,
+ 0x01,
+ 0x5f,
+ 0x80,
+ 0x82,
+ 0x82,
+ 0x54,
+ 0x61,
+ 0x00,
+ 0xd6,
+ 0x91,
+ 0x90,
+ 0x61,
+ 0x01,
+ 0x70,
+ 0x56,
+ 0x5b,
+ 0x92,
+ 0x50,
+ 0x50,
+ 0x81,
+ 0x90,
+ 0x55,
+ 0x50,
+ 0x56,
+ 0x5b,
+ 0x5f,
+ 0x81,
+ 0x90,
+ 0x50,
+ 0x91,
+ 0x90,
+ 0x50,
+ 0x56,
+ 0x5b,
+ 0x61,
+ 0x00,
+ 0xf1,
+ 0x81,
+ 0x61,
+ 0x00,
+ 0xdf,
+ 0x56,
+ 0x5b,
+ 0x82,
+ 0x52,
+ 0x50,
+ 0x50,
+ 0x56,
+ 0x5b,
+ 0x5f,
+ 0x60,
+ 0x20,
+ 0x82,
+ 0x01,
+ 0x90,
+ 0x50,
+ 0x61,
+ 0x01,
+ 0x0a,
+ 0x5f,
+ 0x83,
+ 0x01,
+ 0x84,
+ 0x61,
+ 0x00,
+ 0xe8,
+ 0x56,
+ 0x5b,
+ 0x92,
+ 0x91,
+ 0x50,
+ 0x50,
+ 0x56,
+ 0x5b,
+ 0x7f,
+ 0x4e,
+ 0x48,
+ 0x7b,
+ 0x71,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x5f,
+ 0x52,
+ 0x60,
+ 0x11,
+ 0x60,
+ 0x04,
+ 0x52,
+ 0x60,
+ 0x24,
+ 0x5f,
+ 0xfd,
+ 0x5b,
+ 0x5f,
+ 0x61,
+ 0x01,
+ 0x47,
+ 0x82,
+ 0x61,
+ 0x00,
+ 0xdf,
+ 0x56,
+ 0x5b,
+ 0x91,
+ 0x50,
+ 0x61,
+ 0x01,
+ 0x52,
+ 0x83,
+ 0x61,
+ 0x00,
+ 0xdf,
+ 0x56,
+ 0x5b,
+ 0x92,
+ 0x50,
+ 0x82,
+ 0x82,
+ 0x01,
+ 0x90,
+ 0x50,
+ 0x80,
+ 0x82,
+ 0x11,
+ 0x15,
+ 0x61,
+ 0x01,
+ 0x6a,
+ 0x57,
+ 0x61,
+ 0x01,
+ 0x69,
+ 0x61,
+ 0x01,
+ 0x10,
+ 0x56,
+ 0x5b,
+ 0x5b,
+ 0x92,
+ 0x91,
+ 0x50,
+ 0x50,
+ 0x56,
+ 0x5b,
+ 0x5f,
+ 0x61,
+ 0x01,
+ 0x7a,
+ 0x82,
+ 0x61,
+ 0x00,
+ 0xdf,
+ 0x56,
+ 0x5b,
+ 0x91,
+ 0x50,
+ 0x61,
+ 0x01,
+ 0x85,
+ 0x83,
+ 0x61,
+ 0x00,
+ 0xdf,
+ 0x56,
+ 0x5b,
+ 0x92,
+ 0x50,
+ 0x82,
+ 0x82,
+ 0x03,
+ 0x90,
+ 0x50,
+ 0x81,
+ 0x81,
+ 0x11,
+ 0x15,
+ 0x61,
+ 0x01,
+ 0x9d,
+ 0x57,
+ 0x61,
+ 0x01,
+ 0x9c,
+ 0x61,
+ 0x01,
+ 0x10,
+ 0x56,
+ 0x5b,
+ 0x5b,
+ 0x92,
+ 0x91,
+ 0x50,
+ 0x50,
+ 0x56,
+ 0xfe,
+ 0xa2,
+ 0x64,
+ 0x69,
+ 0x70,
+ 0x66,
+ 0x73,
+ 0x58,
+ 0x22,
+ 0x12,
+ 0x20,
+ 0x7e,
+ 0x79,
+ 0x2f,
+ 0xcf,
+ 0xf2,
+ 0x8a,
+ 0x4b,
+ 0xf0,
+ 0xba,
+ 0xd8,
+ 0x67,
+ 0x5c,
+ 0x5b,
+ 0xc2,
+ 0x28,
+ 0x8b,
+ 0x07,
+ 0x83,
+ 0x5a,
+ 0xeb,
+ 0xaa,
+ 0x90,
+ 0xb8,
+ 0xdc,
+ 0x5e,
+ 0x0d,
+ 0xf1,
+ 0x91,
+ 0x83,
+ 0xfb,
+ 0x72,
+ 0xcf,
+ 0x64,
+ 0x73,
+ 0x6f,
+ 0x6c,
+ 0x63,
+ 0x43,
+ 0x00,
+ 0x08,
+ 0x16,
+ 0x00,
+ 0x33
+ ].span()
+}
+
+
+// // SPDX-License-Identifier: GPL-3.0
+
+// pragma solidity >=0.8.2 <0.9.0;
+
+// /**
+// * @title Storage
+// * @dev Store & retrieve value in a variable
+// * @custom:dev-run-script ./scripts/deploy_with_ethers.ts
+// */
+// contract Storage {
+
+// uint256 number;
+
+// /**
+// * @dev Store value in variable
+// * @param num value to store
+// */
+// function store(uint256 num) public {
+// number = num;
+// }
+
+// /**
+// * @dev Return value
+// * @return value of 'number'
+// */
+// function retrieve() public view returns (uint256){
+// return number;
+// }
+// }
+// Remix compiler: 0.8.20+commit.a1b79de6
+pub fn storage_evm_initcode() -> Span {
+ [
+ 0x60,
+ 0x80,
+ 0x60,
+ 0x40,
+ 0x52,
+ 0x34,
+ 0x80,
+ 0x15,
+ 0x61,
+ 0x00,
+ 0x0f,
+ 0x57,
+ 0x5f,
+ 0x80,
+ 0xfd,
+ 0x5b,
+ 0x50,
+ 0x61,
+ 0x01,
+ 0x43,
+ 0x80,
+ 0x61,
+ 0x00,
+ 0x1d,
+ 0x5f,
+ 0x39,
+ 0x5f,
+ 0xf3,
+ 0xfe,
+ 0x60,
+ 0x80,
+ 0x60,
+ 0x40,
+ 0x52,
+ 0x34,
+ 0x80,
+ 0x15,
+ 0x61,
+ 0x00,
+ 0x0f,
+ 0x57,
+ 0x5f,
+ 0x80,
+ 0xfd,
+ 0x5b,
+ 0x50,
+ 0x60,
+ 0x04,
+ 0x36,
+ 0x10,
+ 0x61,
+ 0x00,
+ 0x34,
+ 0x57,
+ 0x5f,
+ 0x35,
+ 0x60,
+ 0xe0,
+ 0x1c,
+ 0x80,
+ 0x63,
+ 0x2e,
+ 0x64,
+ 0xce,
+ 0xc1,
+ 0x14,
+ 0x61,
+ 0x00,
+ 0x38,
+ 0x57,
+ 0x80,
+ 0x63,
+ 0x60,
+ 0x57,
+ 0x36,
+ 0x1d,
+ 0x14,
+ 0x61,
+ 0x00,
+ 0x56,
+ 0x57,
+ 0x5b,
+ 0x5f,
+ 0x80,
+ 0xfd,
+ 0x5b,
+ 0x61,
+ 0x00,
+ 0x40,
+ 0x61,
+ 0x00,
+ 0x72,
+ 0x56,
+ 0x5b,
+ 0x60,
+ 0x40,
+ 0x51,
+ 0x61,
+ 0x00,
+ 0x4d,
+ 0x91,
+ 0x90,
+ 0x61,
+ 0x00,
+ 0x9b,
+ 0x56,
+ 0x5b,
+ 0x60,
+ 0x40,
+ 0x51,
+ 0x80,
+ 0x91,
+ 0x03,
+ 0x90,
+ 0xf3,
+ 0x5b,
+ 0x61,
+ 0x00,
+ 0x70,
+ 0x60,
+ 0x04,
+ 0x80,
+ 0x36,
+ 0x03,
+ 0x81,
+ 0x01,
+ 0x90,
+ 0x61,
+ 0x00,
+ 0x6b,
+ 0x91,
+ 0x90,
+ 0x61,
+ 0x00,
+ 0xe2,
+ 0x56,
+ 0x5b,
+ 0x61,
+ 0x00,
+ 0x7a,
+ 0x56,
+ 0x5b,
+ 0x00,
+ 0x5b,
+ 0x5f,
+ 0x80,
+ 0x54,
+ 0x90,
+ 0x50,
+ 0x90,
+ 0x56,
+ 0x5b,
+ 0x80,
+ 0x5f,
+ 0x81,
+ 0x90,
+ 0x55,
+ 0x50,
+ 0x50,
+ 0x56,
+ 0x5b,
+ 0x5f,
+ 0x81,
+ 0x90,
+ 0x50,
+ 0x91,
+ 0x90,
+ 0x50,
+ 0x56,
+ 0x5b,
+ 0x61,
+ 0x00,
+ 0x95,
+ 0x81,
+ 0x61,
+ 0x00,
+ 0x83,
+ 0x56,
+ 0x5b,
+ 0x82,
+ 0x52,
+ 0x50,
+ 0x50,
+ 0x56,
+ 0x5b,
+ 0x5f,
+ 0x60,
+ 0x20,
+ 0x82,
+ 0x01,
+ 0x90,
+ 0x50,
+ 0x61,
+ 0x00,
+ 0xae,
+ 0x5f,
+ 0x83,
+ 0x01,
+ 0x84,
+ 0x61,
+ 0x00,
+ 0x8c,
+ 0x56,
+ 0x5b,
+ 0x92,
+ 0x91,
+ 0x50,
+ 0x50,
+ 0x56,
+ 0x5b,
+ 0x5f,
+ 0x80,
+ 0xfd,
+ 0x5b,
+ 0x61,
+ 0x00,
+ 0xc1,
+ 0x81,
+ 0x61,
+ 0x00,
+ 0x83,
+ 0x56,
+ 0x5b,
+ 0x81,
+ 0x14,
+ 0x61,
+ 0x00,
+ 0xcb,
+ 0x57,
+ 0x5f,
+ 0x80,
+ 0xfd,
+ 0x5b,
+ 0x50,
+ 0x56,
+ 0x5b,
+ 0x5f,
+ 0x81,
+ 0x35,
+ 0x90,
+ 0x50,
+ 0x61,
+ 0x00,
+ 0xdc,
+ 0x81,
+ 0x61,
+ 0x00,
+ 0xb8,
+ 0x56,
+ 0x5b,
+ 0x92,
+ 0x91,
+ 0x50,
+ 0x50,
+ 0x56,
+ 0x5b,
+ 0x5f,
+ 0x60,
+ 0x20,
+ 0x82,
+ 0x84,
+ 0x03,
+ 0x12,
+ 0x15,
+ 0x61,
+ 0x00,
+ 0xf7,
+ 0x57,
+ 0x61,
+ 0x00,
+ 0xf6,
+ 0x61,
+ 0x00,
+ 0xb4,
+ 0x56,
+ 0x5b,
+ 0x5b,
+ 0x5f,
+ 0x61,
+ 0x01,
+ 0x04,
+ 0x84,
+ 0x82,
+ 0x85,
+ 0x01,
+ 0x61,
+ 0x00,
+ 0xce,
+ 0x56,
+ 0x5b,
+ 0x91,
+ 0x50,
+ 0x50,
+ 0x92,
+ 0x91,
+ 0x50,
+ 0x50,
+ 0x56,
+ 0xfe,
+ 0xa2,
+ 0x64,
+ 0x69,
+ 0x70,
+ 0x66,
+ 0x73,
+ 0x58,
+ 0x22,
+ 0x12,
+ 0x20,
+ 0xb5,
+ 0xc3,
+ 0x07,
+ 0x5f,
+ 0x2f,
+ 0x20,
+ 0x34,
+ 0xd0,
+ 0x39,
+ 0xa2,
+ 0x27,
+ 0xfa,
+ 0xc6,
+ 0xdd,
+ 0x31,
+ 0x4b,
+ 0x05,
+ 0x2f,
+ 0xfb,
+ 0x2b,
+ 0x3d,
+ 0xa5,
+ 0x2c,
+ 0x7b,
+ 0x6f,
+ 0x5b,
+ 0xc3,
+ 0x74,
+ 0xd5,
+ 0x28,
+ 0xed,
+ 0x36,
+ 0x64,
+ 0x73,
+ 0x6f,
+ 0x6c,
+ 0x63,
+ 0x43,
+ 0x00,
+ 0x08,
+ 0x14,
+ 0x00,
+ 0x33
+ ].span()
+}
+
+pub fn storage_evm_bytecode() -> Span {
+ [
+ 0x60,
+ 0x80,
+ 0x60,
+ 0x40,
+ 0x52,
+ 0x34,
+ 0x80,
+ 0x15,
+ 0x61,
+ 0x00,
+ 0x0f,
+ 0x57,
+ 0x5f,
+ 0x80,
+ 0xfd,
+ 0x5b,
+ 0x50,
+ 0x60,
+ 0x04,
+ 0x36,
+ 0x10,
+ 0x61,
+ 0x00,
+ 0x34,
+ 0x57,
+ 0x5f,
+ 0x35,
+ 0x60,
+ 0xe0,
+ 0x1c,
+ 0x80,
+ 0x63,
+ 0x2e,
+ 0x64,
+ 0xce,
+ 0xc1,
+ 0x14,
+ 0x61,
+ 0x00,
+ 0x38,
+ 0x57,
+ 0x80,
+ 0x63,
+ 0x60,
+ 0x57,
+ 0x36,
+ 0x1d,
+ 0x14,
+ 0x61,
+ 0x00,
+ 0x56,
+ 0x57,
+ 0x5b,
+ 0x5f,
+ 0x80,
+ 0xfd,
+ 0x5b,
+ 0x61,
+ 0x00,
+ 0x40,
+ 0x61,
+ 0x00,
+ 0x72,
+ 0x56,
+ 0x5b,
+ 0x60,
+ 0x40,
+ 0x51,
+ 0x61,
+ 0x00,
+ 0x4d,
+ 0x91,
+ 0x90,
+ 0x61,
+ 0x00,
+ 0x9b,
+ 0x56,
+ 0x5b,
+ 0x60,
+ 0x40,
+ 0x51,
+ 0x80,
+ 0x91,
+ 0x03,
+ 0x90,
+ 0xf3,
+ 0x5b,
+ 0x61,
+ 0x00,
+ 0x70,
+ 0x60,
+ 0x04,
+ 0x80,
+ 0x36,
+ 0x03,
+ 0x81,
+ 0x01,
+ 0x90,
+ 0x61,
+ 0x00,
+ 0x6b,
+ 0x91,
+ 0x90,
+ 0x61,
+ 0x00,
+ 0xe2,
+ 0x56,
+ 0x5b,
+ 0x61,
+ 0x00,
+ 0x7a,
+ 0x56,
+ 0x5b,
+ 0x00,
+ 0x5b,
+ 0x5f,
+ 0x80,
+ 0x54,
+ 0x90,
+ 0x50,
+ 0x90,
+ 0x56,
+ 0x5b,
+ 0x80,
+ 0x5f,
+ 0x81,
+ 0x90,
+ 0x55,
+ 0x50,
+ 0x50,
+ 0x56,
+ 0x5b,
+ 0x5f,
+ 0x81,
+ 0x90,
+ 0x50,
+ 0x91,
+ 0x90,
+ 0x50,
+ 0x56,
+ 0x5b,
+ 0x61,
+ 0x00,
+ 0x95,
+ 0x81,
+ 0x61,
+ 0x00,
+ 0x83,
+ 0x56,
+ 0x5b,
+ 0x82,
+ 0x52,
+ 0x50,
+ 0x50,
+ 0x56,
+ 0x5b,
+ 0x5f,
+ 0x60,
+ 0x20,
+ 0x82,
+ 0x01,
+ 0x90,
+ 0x50,
+ 0x61,
+ 0x00,
+ 0xae,
+ 0x5f,
+ 0x83,
+ 0x01,
+ 0x84,
+ 0x61,
+ 0x00,
+ 0x8c,
+ 0x56,
+ 0x5b,
+ 0x92,
+ 0x91,
+ 0x50,
+ 0x50,
+ 0x56,
+ 0x5b,
+ 0x5f,
+ 0x80,
+ 0xfd,
+ 0x5b,
+ 0x61,
+ 0x00,
+ 0xc1,
+ 0x81,
+ 0x61,
+ 0x00,
+ 0x83,
+ 0x56,
+ 0x5b,
+ 0x81,
+ 0x14,
+ 0x61,
+ 0x00,
+ 0xcb,
+ 0x57,
+ 0x5f,
+ 0x80,
+ 0xfd,
+ 0x5b,
+ 0x50,
+ 0x56,
+ 0x5b,
+ 0x5f,
+ 0x81,
+ 0x35,
+ 0x90,
+ 0x50,
+ 0x61,
+ 0x00,
+ 0xdc,
+ 0x81,
+ 0x61,
+ 0x00,
+ 0xb8,
+ 0x56,
+ 0x5b,
+ 0x92,
+ 0x91,
+ 0x50,
+ 0x50,
+ 0x56,
+ 0x5b,
+ 0x5f,
+ 0x60,
+ 0x20,
+ 0x82,
+ 0x84,
+ 0x03,
+ 0x12,
+ 0x15,
+ 0x61,
+ 0x00,
+ 0xf7,
+ 0x57,
+ 0x61,
+ 0x00,
+ 0xf6,
+ 0x61,
+ 0x00,
+ 0xb4,
+ 0x56,
+ 0x5b,
+ 0x5b,
+ 0x5f,
+ 0x61,
+ 0x01,
+ 0x04,
+ 0x84,
+ 0x82,
+ 0x85,
+ 0x01,
+ 0x61,
+ 0x00,
+ 0xce,
+ 0x56,
+ 0x5b,
+ 0x91,
+ 0x50,
+ 0x50,
+ 0x92,
+ 0x91,
+ 0x50,
+ 0x50,
+ 0x56,
+ 0xfe,
+ 0xa2,
+ 0x64,
+ 0x69,
+ 0x70,
+ 0x66,
+ 0x73,
+ 0x58,
+ 0x22,
+ 0x12,
+ 0x20,
+ 0xb5,
+ 0xc3,
+ 0x07,
+ 0x5f,
+ 0x2f,
+ 0x20,
+ 0x34,
+ 0xd0,
+ 0x39,
+ 0xa2,
+ 0x27,
+ 0xfa,
+ 0xc6,
+ 0xdd,
+ 0x31,
+ 0x4b,
+ 0x05,
+ 0x2f,
+ 0xfb,
+ 0x2b,
+ 0x3d,
+ 0xa5,
+ 0x2c,
+ 0x7b,
+ 0x6f,
+ 0x5b,
+ 0xc3,
+ 0x74,
+ 0xd5,
+ 0x28,
+ 0xed,
+ 0x36,
+ 0x64,
+ 0x73,
+ 0x6f,
+ 0x6c,
+ 0x63,
+ 0x43,
+ 0x00,
+ 0x08,
+ 0x14,
+ 0x00,
+ 0x33
+ ].span()
+}
+
+
+// eip-2930 RLP encoded tx { unsigned }, calls the `inc` function of counter bytecode
+// format: 0x01 || rlp([chainId, nonce, gasPrice, gasLimit, to, value, data, accessList])
+// rlp decoding: [ '0x01', '0x', '0x3b9aca00', '0x1e8480',
+// '0x0000006f746865725f65766d5f61646472657373', '0x', '0x371303c0', [] ]
+pub fn eip_2930_rlp_encoded_counter_inc_tx() -> Span {
+ [
+ 1,
+ 235,
+ 132,
+ 75,
+ 75,
+ 82,
+ 84,
+ 128,
+ 132,
+ 59,
+ 154,
+ 202,
+ 0,
+ 131,
+ 30,
+ 132,
+ 128,
+ 148,
+ 0,
+ 0,
+ 0,
+ 111,
+ 116,
+ 104,
+ 101,
+ 114,
+ 95,
+ 101,
+ 118,
+ 109,
+ 95,
+ 97,
+ 100,
+ 100,
+ 114,
+ 101,
+ 115,
+ 115,
+ 128,
+ 132,
+ 55,
+ 19,
+ 3,
+ 192,
+ 192,
+ ].span()
+}
diff --git a/cairo/kakarot-ssj/crates/contracts/src/test_utils.cairo b/cairo/kakarot-ssj/crates/contracts/src/test_utils.cairo
new file mode 100644
index 000000000..e56908366
--- /dev/null
+++ b/cairo/kakarot-ssj/crates/contracts/src/test_utils.cairo
@@ -0,0 +1,147 @@
+use core::result::ResultTrait;
+use core::starknet::syscalls::deploy_syscall;
+use core::starknet::{EthAddress, ContractAddress};
+use crate::account_contract::{IAccountDispatcher, IAccountDispatcherTrait};
+use crate::kakarot_core::{
+ interface::IExtendedKakarotCoreDispatcher, interface::IExtendedKakarotCoreDispatcherTrait
+};
+use evm::model::{Address};
+
+use evm::test_utils::{other_starknet_address, sequencer_evm_address, chain_id};
+use openzeppelin::token::erc20::interface::{IERC20CamelDispatcher, IERC20CamelDispatcherTrait};
+use snforge_std::start_cheat_chain_id_global;
+use snforge_std::{
+ declare, DeclareResultTrait, start_cheat_caller_address, start_cheat_sequencer_address_global,
+ stop_cheat_caller_address, start_cheat_caller_address_global, cheat_caller_address, CheatSpan
+};
+use utils::constants::BLOCK_GAS_LIMIT;
+use utils::eth_transaction::legacy::TxLegacy;
+
+
+pub mod constants {
+ use core::starknet::{EthAddress, contract_address_const, ContractAddress};
+ pub fn ZERO() -> ContractAddress {
+ contract_address_const::<0>()
+ }
+
+ pub fn OWNER() -> ContractAddress {
+ contract_address_const::<0xabde1>()
+ }
+
+ pub fn OTHER() -> ContractAddress {
+ contract_address_const::<0xe1145>()
+ }
+
+ pub fn EVM_ADDRESS() -> EthAddress {
+ 0xc0ffee.try_into().unwrap()
+ }
+
+ pub fn ETH_BANK() -> ContractAddress {
+ contract_address_const::<0x777>()
+ }
+}
+
+pub fn deploy_native_token() -> IERC20CamelDispatcher {
+ let calldata: Array = array![
+ 'STARKNET_ETH', 'ETH', 0x00, 0xfffffffffffffffffffffffffff, constants::ETH_BANK().into()
+ ];
+ let class = declare("ERC20").unwrap().contract_class().class_hash;
+ let maybe_address = deploy_syscall(*class, 0, calldata.span(), false);
+ match maybe_address {
+ Result::Ok((contract_address, _)) => { IERC20CamelDispatcher { contract_address } },
+ Result::Err(err) => panic(err)
+ }
+}
+
+pub fn deploy_kakarot_core(
+ native_token: ContractAddress, mut eoas: Span
+) -> IExtendedKakarotCoreDispatcher {
+ let account_contract_class_hash = declare("AccountContract")
+ .unwrap()
+ .contract_class()
+ .class_hash;
+ let uninitialized_account_class_hash = declare("UninitializedAccount")
+ .unwrap()
+ .contract_class()
+ .class_hash;
+ let kakarot_core_class_hash = declare("KakarotCore").unwrap().contract_class().class_hash;
+ let mut calldata: Array = array![
+ other_starknet_address().into(),
+ native_token.into(),
+ (*account_contract_class_hash).into(),
+ (*uninitialized_account_class_hash).into(),
+ 'coinbase',
+ BLOCK_GAS_LIMIT.into(),
+ ];
+
+ Serde::serialize(@eoas, ref calldata);
+
+ let maybe_address = deploy_syscall(
+ (*kakarot_core_class_hash).into(), 0, calldata.span(), false
+ );
+
+ let kakarot_core = match maybe_address {
+ Result::Ok((
+ contract_address, _
+ )) => { IExtendedKakarotCoreDispatcher { contract_address } },
+ Result::Err(err) => panic(err)
+ };
+ cheat_caller_address(
+ kakarot_core.contract_address, other_starknet_address(), CheatSpan::TargetCalls(1)
+ );
+ kakarot_core.set_base_fee(1000);
+
+ kakarot_core
+}
+
+pub fn deploy_contract_account(
+ kakarot_core: IExtendedKakarotCoreDispatcher, evm_address: EthAddress, bytecode: Span
+) -> Address {
+ let eoa = deploy_eoa(kakarot_core, evm_address);
+ let starknet_address = eoa.contract_address;
+ start_cheat_caller_address(starknet_address, kakarot_core.contract_address);
+ IAccountDispatcher { contract_address: starknet_address }.set_nonce(1);
+ IAccountDispatcher { contract_address: starknet_address }.write_bytecode(bytecode);
+ stop_cheat_caller_address(starknet_address);
+ Address { evm: evm_address, starknet: starknet_address }
+}
+
+pub fn deploy_eoa(
+ kakarot_core: IExtendedKakarotCoreDispatcher, evm_address: EthAddress
+) -> IAccountDispatcher {
+ let starknet_address = kakarot_core.deploy_externally_owned_account(evm_address);
+ IAccountDispatcher { contract_address: starknet_address }
+}
+
+pub fn call_transaction(
+ chain_id: u64, destination: Option, input: Span
+) -> TxLegacy {
+ TxLegacy {
+ chain_id: Option::Some(chain_id),
+ nonce: 0,
+ gas_price: 0,
+ gas_limit: 500000000,
+ to: destination.into(),
+ value: 0,
+ input
+ }
+}
+
+pub fn fund_account_with_native_token(
+ contract_address: ContractAddress, native_token: IERC20CamelDispatcher, amount: u256,
+) {
+ start_cheat_caller_address(native_token.contract_address, constants::ETH_BANK());
+ native_token.transfer(contract_address, amount);
+ stop_cheat_caller_address(native_token.contract_address);
+}
+
+pub fn setup_contracts_for_testing() -> (IERC20CamelDispatcher, IExtendedKakarotCoreDispatcher) {
+ let sequencer: EthAddress = sequencer_evm_address();
+ let native_token = deploy_native_token();
+ let kakarot_core = deploy_kakarot_core(native_token.contract_address, [sequencer].span());
+
+ let sequencer_sn_address = kakarot_core.address_registry(sequencer);
+ start_cheat_sequencer_address_global(sequencer_sn_address);
+ start_cheat_chain_id_global(chain_id().into());
+ return (native_token, kakarot_core);
+}
diff --git a/cairo/kakarot-ssj/crates/contracts/src/uninitialized_account.cairo b/cairo/kakarot-ssj/crates/contracts/src/uninitialized_account.cairo
new file mode 100644
index 000000000..46c4ea9e0
--- /dev/null
+++ b/cairo/kakarot-ssj/crates/contracts/src/uninitialized_account.cairo
@@ -0,0 +1,66 @@
+//! The generic account that is deployed by Kakarot Core before being "specialized" into an
+//! Externally Owned Account or a Contract Account This aims at having only one class hash for all
+//! the contracts deployed by Kakarot, thus enforcing a unique and consistent address mapping Eth
+//! Address <=> Starknet Address
+
+use core::starknet::ClassHash;
+use core::starknet::{ContractAddress, EthAddress};
+
+#[starknet::interface]
+trait IAccount {
+ fn initialize(
+ ref self: TContractState,
+ kakarot_address: ContractAddress,
+ evm_address: EthAddress,
+ implementation_class: ClassHash
+ );
+}
+
+
+#[starknet::contract]
+pub mod UninitializedAccount {
+ use core::starknet::SyscallResultTrait;
+ use core::starknet::syscalls::{library_call_syscall, replace_class_syscall};
+ use core::starknet::{ContractAddress, get_caller_address};
+ use crate::components::ownable::ownable_component::InternalTrait;
+ use crate::components::ownable::ownable_component;
+ use crate::kakarot_core::interface::{IKakarotCoreDispatcher, IKakarotCoreDispatcherTrait};
+
+ // Add ownable component
+ component!(path: ownable_component, storage: ownable, event: OwnableEvent);
+ #[abi(embed_v0)]
+ impl OwnableImpl = ownable_component::Ownable;
+ impl OwnableInternal = ownable_component::InternalImpl;
+
+ #[storage]
+ struct Storage {
+ #[substorage(v0)]
+ ownable: ownable_component::Storage,
+ }
+
+ #[event]
+ #[derive(Drop, starknet::Event)]
+ enum Event {
+ OwnableEvent: ownable_component::Event
+ }
+
+ #[constructor]
+ fn constructor(ref self: ContractState, mut calldata: Array) {
+ let owner_address: ContractAddress = get_caller_address();
+ self.ownable.initializer(owner_address);
+ let implementation_class = IKakarotCoreDispatcher { contract_address: owner_address }
+ .get_account_contract_class_hash();
+ //TODO: Difference from KakarotZero in that the account contract takes the class
+ //implementation to write it in storage,
+ // as it is not a transparent proxy in Cairo1
+ calldata.append(implementation_class.into());
+ library_call_syscall(
+ class_hash: implementation_class,
+ function_selector: selector!("initialize"),
+ calldata: calldata.span()
+ )
+ .unwrap_syscall();
+
+ replace_class_syscall(implementation_class).unwrap_syscall();
+ }
+}
diff --git a/cairo/kakarot-ssj/crates/contracts/tests/lib.cairo b/cairo/kakarot-ssj/crates/contracts/tests/lib.cairo
new file mode 100644
index 000000000..df3d540ee
--- /dev/null
+++ b/cairo/kakarot-ssj/crates/contracts/tests/lib.cairo
@@ -0,0 +1,8 @@
+mod test_account_contract;
+mod test_cairo1_helpers;
+
+mod test_execution_from_outside;
+
+mod test_kakarot_core;
+
+mod test_ownable;
diff --git a/cairo/kakarot-ssj/crates/contracts/tests/test_account_contract.cairo b/cairo/kakarot-ssj/crates/contracts/tests/test_account_contract.cairo
new file mode 100644
index 000000000..430a5501c
--- /dev/null
+++ b/cairo/kakarot-ssj/crates/contracts/tests/test_account_contract.cairo
@@ -0,0 +1,148 @@
+use contracts::errors::KAKAROT_REENTRANCY;
+use contracts::test_data::counter_evm_bytecode;
+use contracts::test_utils::{
+ setup_contracts_for_testing, deploy_contract_account, fund_account_with_native_token, deploy_eoa
+};
+use contracts::{IAccountDispatcher, IAccountDispatcherTrait};
+use core::starknet::EthAddress;
+use core::starknet::account::{Call};
+use evm::test_utils::{ca_address, eoa_address};
+use openzeppelin::token::erc20::interface::IERC20CamelDispatcherTrait;
+use snforge_std::{start_cheat_caller_address, stop_cheat_caller_address};
+
+#[test]
+fn test_ca_deploy() {
+ let (_, kakarot_core) = setup_contracts_for_testing();
+ let ca_address = deploy_contract_account(kakarot_core, ca_address(), [].span());
+ let contract_account = IAccountDispatcher { contract_address: ca_address.starknet };
+
+ let initial_bytecode = contract_account.bytecode();
+ assert(initial_bytecode.is_empty(), 'bytecode should be empty');
+ assert(contract_account.get_evm_address() == ca_address.evm, 'wrong ca evm address');
+ assert(contract_account.get_nonce() == 1, 'wrong nonce');
+}
+
+#[test]
+fn test_ca_bytecode() {
+ let (_, kakarot_core) = setup_contracts_for_testing();
+ let bytecode = counter_evm_bytecode();
+ let ca_address = deploy_contract_account(kakarot_core, ca_address(), bytecode);
+ let contract_account = IAccountDispatcher { contract_address: ca_address.starknet };
+
+ let contract_bytecode = contract_account.bytecode();
+ assert(contract_bytecode == bytecode, 'wrong contract bytecode');
+}
+
+
+#[test]
+fn test_ca_get_nonce() {
+ let (_, kakarot_core) = setup_contracts_for_testing();
+ let ca_address = deploy_contract_account(kakarot_core, ca_address(), [].span());
+ let contract_account = IAccountDispatcher { contract_address: ca_address.starknet };
+
+ let initial_nonce = contract_account.get_nonce();
+ assert(initial_nonce == 1, 'nonce should be 1');
+
+ let expected_nonce = 100;
+ start_cheat_caller_address(ca_address.starknet, kakarot_core.contract_address);
+ contract_account.set_nonce(expected_nonce);
+ stop_cheat_caller_address(ca_address.starknet);
+
+ let nonce = contract_account.get_nonce();
+
+ assert(nonce == expected_nonce, 'wrong contract nonce');
+}
+
+#[test]
+fn test_get_evm_address() {
+ let expected_address: EthAddress = eoa_address();
+ let (_, kakarot_core) = setup_contracts_for_testing();
+
+ let eoa_contract = deploy_eoa(kakarot_core, eoa_address());
+
+ assert(eoa_contract.get_evm_address() == expected_address, 'wrong evm_address');
+}
+
+
+#[test]
+fn test_ca_storage() {
+ let (_, kakarot_core) = setup_contracts_for_testing();
+ let ca_address = deploy_contract_account(kakarot_core, ca_address(), [].span());
+ let contract_account = IAccountDispatcher { contract_address: ca_address.starknet };
+
+ let storage_slot = 0x555;
+
+ let initial_storage = contract_account.storage(storage_slot);
+ assert(initial_storage == 0, 'value should be 0');
+
+ let expected_storage = 0x444;
+ start_cheat_caller_address(ca_address.starknet, kakarot_core.contract_address);
+ contract_account.write_storage(storage_slot, expected_storage);
+ stop_cheat_caller_address(ca_address.starknet);
+
+ let storage = contract_account.storage(storage_slot);
+
+ assert(storage == expected_storage, 'wrong contract storage');
+}
+
+#[test]
+fn test_ca_external_starknet_call_native_token() {
+ let (native_token, kakarot_core) = setup_contracts_for_testing();
+ let ca_address = deploy_contract_account(kakarot_core, ca_address(), [].span());
+ let contract_account = IAccountDispatcher { contract_address: ca_address.starknet };
+ fund_account_with_native_token(ca_address.starknet, native_token, 0x1);
+
+ let call = Call {
+ to: native_token.contract_address,
+ selector: selector!("balanceOf"),
+ calldata: array![ca_address.starknet.into()].span(),
+ };
+ start_cheat_caller_address(ca_address.starknet, kakarot_core.contract_address);
+ let (success, data) = contract_account.execute_starknet_call(call);
+ stop_cheat_caller_address(ca_address.starknet);
+
+ assert(success, 'execute_starknet_call failed');
+ assert(data.len() == 2, 'wrong return data length');
+ let balance = native_token.balanceOf(ca_address.starknet);
+ assert((*data[0], *data[1]) == (balance.low.into(), balance.high.into()), 'wrong return data');
+}
+
+#[test]
+fn test_ca_external_starknet_call_kakarot_get_starknet_address() {
+ let (_, kakarot_core) = setup_contracts_for_testing();
+ let ca_address = deploy_contract_account(kakarot_core, ca_address(), [].span());
+ let contract_account = IAccountDispatcher { contract_address: ca_address.starknet };
+
+ let call = Call {
+ to: kakarot_core.contract_address, selector: selector!("get_starknet_address"), calldata: [
+ ca_address.evm.into()
+ ].span(),
+ };
+ start_cheat_caller_address(ca_address.starknet, kakarot_core.contract_address);
+ let (success, data) = contract_account.execute_starknet_call(call);
+ stop_cheat_caller_address(ca_address.starknet);
+
+ assert(success, 'execute_starknet_call failed');
+ assert(data.len() == 1, 'wrong return data length');
+ assert(*data[0] == ca_address.starknet.try_into().unwrap(), 'wrong return data');
+}
+
+#[test]
+fn test_ca_external_starknet_call_cannot_call_kakarot_other_selector() {
+ let (_, kakarot_core) = setup_contracts_for_testing();
+ let ca_address = deploy_contract_account(kakarot_core, ca_address(), [].span());
+ let contract_account = IAccountDispatcher { contract_address: ca_address.starknet };
+
+ let call = Call {
+ to: kakarot_core.contract_address,
+ selector: selector!("get_native_token"),
+ calldata: [].span(),
+ };
+ start_cheat_caller_address(ca_address.starknet, kakarot_core.contract_address);
+ let (success, data) = contract_account.execute_starknet_call(call);
+ stop_cheat_caller_address(ca_address.starknet);
+
+ assert(!success, 'execute_starknet_call failed');
+ assert(data.len() == 19, 'wrong return data length');
+ assert(data == KAKAROT_REENTRANCY.span(), 'wrong return data');
+}
diff --git a/cairo/kakarot-ssj/crates/contracts/tests/test_cairo1_helpers.cairo b/cairo/kakarot-ssj/crates/contracts/tests/test_cairo1_helpers.cairo
new file mode 100644
index 000000000..ace7677d7
--- /dev/null
+++ b/cairo/kakarot-ssj/crates/contracts/tests/test_cairo1_helpers.cairo
@@ -0,0 +1,20 @@
+use contracts::cairo1_helpers::Cairo1Helpers;
+use utils::traits::integer::BytesUsedTrait;
+
+#[test]
+fn test_keccak() {
+ // "Hello world!"
+ // where:
+ // 8031924123371070792 == int.from_bytes(b'Hello wo', 'little')
+ // 560229490 == int.from_bytes(b'rld!', 'little')
+ let input = array![8031924123371070792];
+ let last_input_word: u64 = 560229490;
+ let last_input_num_bytes = last_input_word.bytes_used();
+ let state = Cairo1Helpers::contract_state_for_testing();
+
+ let res = Cairo1Helpers::Helpers::keccak(
+ @state, input, last_input_word, last_input_num_bytes.into()
+ );
+
+ assert_eq!(res, 0xecd0e108a98e192af1d2c25055f4e3bed784b5c877204e73219a5203251feaab);
+}
diff --git a/cairo/kakarot-ssj/crates/contracts/tests/test_execution_from_outside.cairo b/cairo/kakarot-ssj/crates/contracts/tests/test_execution_from_outside.cairo
new file mode 100644
index 000000000..568565968
--- /dev/null
+++ b/cairo/kakarot-ssj/crates/contracts/tests/test_execution_from_outside.cairo
@@ -0,0 +1,514 @@
+use contracts::account_contract::{
+ AccountContract, IAccountDispatcher, IAccountDispatcherTrait, OutsideExecution
+};
+use contracts::kakarot_core::interface::{
+ IExtendedKakarotCoreDispatcher, IExtendedKakarotCoreDispatcherTrait
+};
+use contracts::test_data::{counter_evm_bytecode, eip_2930_rlp_encoded_counter_inc_tx,};
+use contracts::test_utils::{
+ setup_contracts_for_testing, deploy_contract_account, fund_account_with_native_token,
+ call_transaction
+};
+use core::num::traits::Bounded;
+use core::starknet::account::Call;
+use core::starknet::secp256_trait::Signature;
+use core::starknet::{ContractAddress, contract_address_const, EthAddress, Event};
+use evm::test_utils::chain_id;
+use evm::test_utils::other_evm_address;
+use openzeppelin::token::erc20::interface::IERC20CamelDispatcher;
+use snforge_std::{
+ start_cheat_caller_address, stop_cheat_caller_address, start_cheat_transaction_hash, spy_events,
+ EventSpyTrait, CheatSpan, cheat_caller_address, stop_cheat_block_timestamp,
+ start_cheat_block_timestamp, start_cheat_chain_id_global, stop_cheat_chain_id_global,
+ stop_cheat_caller_address_global
+};
+
+use snforge_utils::snforge_utils::EventsFilterBuilderTrait;
+use utils::eth_transaction::transaction::Transaction;
+use utils::eth_transaction::tx_type::TxType;
+use utils::helpers::u256_to_bytes_array;
+use utils::serialization::{serialize_bytes, serialize_transaction_signature};
+use utils::test_data::{legacy_rlp_encoded_tx, eip_2930_encoded_tx, eip_1559_encoded_tx};
+
+fn transaction_signer() -> EthAddress {
+ 0x6Bd85F39321B00c6d603474C5B2fddEB9c92A466.try_into().unwrap()
+}
+
+const PLACEHOLDER_SIGNATURE: [felt252; 5] = [1, 2, 3, 4, 0];
+
+const SNIP9_CALLER: felt252 = 0xaA36F24f65b5F0f2c642323f3d089A3F0f2845Bf;
+
+#[derive(Drop)]
+struct CallBuilder {
+ call: Call
+}
+
+#[generate_trait]
+impl CallBuilderImpl of CallBuilderTrait {
+ fn new(kakarot_core: ContractAddress) -> CallBuilder {
+ CallBuilder {
+ call: Call {
+ to: kakarot_core,
+ selector: selector!("eth_send_transaction"),
+ calldata: serialize_bytes(eip_2930_encoded_tx()).span()
+ }
+ }
+ }
+
+ fn with_to(mut self: CallBuilder, to: ContractAddress) -> CallBuilder {
+ self.call.to = to;
+ self
+ }
+
+ fn with_selector(mut self: CallBuilder, selector: felt252) -> CallBuilder {
+ self.call.selector = selector;
+ self
+ }
+
+ fn with_calldata(mut self: CallBuilder, calldata: Span) -> CallBuilder {
+ self.call.calldata = serialize_bytes(calldata).span();
+ self
+ }
+
+ fn build(mut self: CallBuilder) -> Call {
+ return self.call;
+ }
+}
+
+#[derive(Drop)]
+struct OutsideExecutionBuilder {
+ outside_execution: OutsideExecution
+}
+
+#[generate_trait]
+impl OutsideExecutionBuilderImpl of OutsideExecutionBuilderTrait {
+ fn new(kakarot_core: ContractAddress) -> OutsideExecutionBuilder {
+ OutsideExecutionBuilder {
+ outside_execution: OutsideExecution {
+ caller: 'ANY_CALLER'.try_into().unwrap(),
+ nonce: 0,
+ execute_after: 998,
+ execute_before: 1000,
+ calls: [
+ CallBuilderTrait::new(kakarot_core).build(),
+ ].span()
+ }
+ }
+ }
+
+ fn with_caller(
+ mut self: OutsideExecutionBuilder, caller: ContractAddress
+ ) -> OutsideExecutionBuilder {
+ self.outside_execution.caller = caller;
+ self
+ }
+
+ fn with_nonce(mut self: OutsideExecutionBuilder, nonce: u64) -> OutsideExecutionBuilder {
+ self.outside_execution.nonce = nonce;
+ self
+ }
+
+ fn with_execute_after(
+ mut self: OutsideExecutionBuilder, execute_after: u64
+ ) -> OutsideExecutionBuilder {
+ self.outside_execution.execute_after = execute_after;
+ self
+ }
+
+ fn with_execute_before(
+ mut self: OutsideExecutionBuilder, execute_before: u64
+ ) -> OutsideExecutionBuilder {
+ self.outside_execution.execute_before = execute_before;
+ self
+ }
+
+ fn with_calls(mut self: OutsideExecutionBuilder, calls: Span) -> OutsideExecutionBuilder {
+ self.outside_execution.calls = calls;
+ self
+ }
+
+ fn build(mut self: OutsideExecutionBuilder) -> OutsideExecution {
+ return self.outside_execution;
+ }
+}
+
+fn set_up() -> (IExtendedKakarotCoreDispatcher, IAccountDispatcher, IERC20CamelDispatcher) {
+ let (native_token, kakarot_core) = setup_contracts_for_testing();
+ // When we deploy the EOA, we use get_caller_address to get the address of the KakarotCore
+ // contract and set the caller address to that.
+ // Therefore, we need to stop the global caller address cheat so that the EOA is deployed
+ // by the real KakarotCore contract and not the one impersonated by the cheat
+ stop_cheat_caller_address_global();
+ let eoa = IAccountDispatcher {
+ contract_address: kakarot_core.deploy_externally_owned_account(transaction_signer())
+ };
+ start_cheat_block_timestamp(eoa.contract_address, 999);
+ start_cheat_chain_id_global(chain_id().into());
+
+ (kakarot_core, eoa, native_token)
+}
+
+fn tear_down(contract_account: IAccountDispatcher) {
+ stop_cheat_chain_id_global();
+ stop_cheat_block_timestamp(contract_account.contract_address);
+}
+
+#[test]
+#[should_panic(expected: 'SNIP9: Invalid caller')]
+fn test_execute_from_outside_invalid_caller() {
+ let (kakarot_core, contract_account, _) = set_up();
+
+ let outside_execution = OutsideExecutionBuilderTrait::new(kakarot_core.contract_address)
+ .with_caller(contract_address_const::<0xb0b>())
+ .build();
+ let signature = PLACEHOLDER_SIGNATURE.span();
+
+ let _ = contract_account.execute_from_outside(outside_execution, signature);
+
+ tear_down(contract_account);
+}
+
+#[test]
+#[should_panic(expected: 'SNIP9: Too early call')]
+fn test_execute_from_outside_too_early_call() {
+ let (kakarot_core, contract_account, _) = set_up();
+
+ let outside_execution = OutsideExecutionBuilderTrait::new(kakarot_core.contract_address)
+ .with_execute_after(999)
+ .build();
+ let signature = PLACEHOLDER_SIGNATURE.span();
+
+ let _ = contract_account.execute_from_outside(outside_execution, signature);
+
+ tear_down(contract_account);
+}
+
+#[test]
+#[should_panic(expected: 'SNIP9: Too late call')]
+fn test_execute_from_outside_too_late_call() {
+ let (kakarot_core, contract_account, _) = set_up();
+
+ let outside_execution = OutsideExecutionBuilderTrait::new(kakarot_core.contract_address)
+ .with_execute_before(999)
+ .build();
+ let signature = PLACEHOLDER_SIGNATURE.span();
+
+ let _ = contract_account.execute_from_outside(outside_execution, signature);
+
+ tear_down(contract_account);
+}
+
+#[test]
+#[should_panic(expected: 'EOA: Invalid signature length')]
+fn test_execute_from_outside_inPLACEHOLDER_SIGNATURE_length() {
+ let (kakarot_core, contract_account, _) = set_up();
+
+ let outside_execution = OutsideExecutionBuilderTrait::new(kakarot_core.contract_address)
+ .build();
+
+ let _ = contract_account.execute_from_outside(outside_execution, [].span());
+
+ tear_down(contract_account);
+}
+
+#[test]
+#[should_panic(expected: 'KKRT: Multicall not supported')]
+fn test_execute_from_outside_multicall_not_supported() {
+ let (kakarot_core, contract_account, _) = set_up();
+
+ let outside_execution = OutsideExecutionBuilderTrait::new(kakarot_core.contract_address)
+ .with_calls(
+ [
+ CallBuilderTrait::new(kakarot_core.contract_address).build(),
+ CallBuilderTrait::new(kakarot_core.contract_address).build(),
+ ].span()
+ )
+ .build();
+ let signature = PLACEHOLDER_SIGNATURE.span();
+
+ let _ = contract_account.execute_from_outside(outside_execution, signature);
+
+ tear_down(contract_account);
+}
+
+#[test]
+#[should_panic(expected: 'EOA: invalid signature')]
+fn test_execute_from_outside_invalid_signature() {
+ let (kakarot_core, contract_account, _) = set_up();
+
+ let caller = contract_address_const::();
+
+ let outside_execution = OutsideExecutionBuilderTrait::new(kakarot_core.contract_address)
+ .with_caller(caller)
+ .build();
+ let signature: Span = [1, 2, 3, 4, (chain_id() * 2 + 40).into()].span();
+
+ start_cheat_caller_address(contract_account.contract_address, caller);
+ let _ = contract_account.execute_from_outside(outside_execution, signature);
+
+ tear_down(contract_account);
+}
+
+
+#[test]
+#[should_panic(expected: 'KKRT: Multicall not supported')]
+fn test_execute_from_outside_should_fail_with_zero_calls() {
+ let (kakarot_core, contract_account, _) = set_up();
+
+ let outside_execution = OutsideExecutionBuilderTrait::new(kakarot_core.contract_address)
+ .with_calls([].span())
+ .build();
+ let signature = PLACEHOLDER_SIGNATURE.span();
+
+ let _ = contract_account.execute_from_outside(outside_execution, signature);
+
+ tear_down(contract_account);
+}
+
+#[test]
+#[should_panic(expected: 'EOA: cannot have code')]
+fn test_execute_from_outside_should_fail_account_with_code() {
+ let (kakarot_core, _, _) = set_up();
+
+ let contract_address = deploy_contract_account(
+ kakarot_core, other_evm_address(), counter_evm_bytecode()
+ )
+ .starknet;
+ let contract_account = IAccountDispatcher { contract_address };
+
+ let outside_execution = OutsideExecutionBuilderTrait::new(kakarot_core.contract_address)
+ .build();
+ let signature = PLACEHOLDER_SIGNATURE.span();
+
+ start_cheat_block_timestamp(contract_account.contract_address, 999);
+ let _ = contract_account.execute_from_outside(outside_execution, signature);
+
+ tear_down(contract_account);
+}
+
+
+#[test]
+#[should_panic(expected: 'KKRT: Multicall not supported')]
+fn test_execute_from_outside_should_fail_with_multi_calls() {
+ let (kakarot_core, eoa, _) = set_up();
+
+ let outside_execution = OutsideExecutionBuilderTrait::new(kakarot_core.contract_address)
+ .with_calls(
+ [
+ CallBuilderTrait::new(kakarot_core.contract_address)
+ .with_calldata(legacy_rlp_encoded_tx())
+ .build()
+ ; 2].span()
+ )
+ .build();
+ let signature = PLACEHOLDER_SIGNATURE.span();
+
+ let _ = eoa.execute_from_outside(outside_execution, signature);
+
+ tear_down(eoa);
+}
+
+
+#[test]
+fn test_execute_from_outside_legacy_tx() {
+ let (kakarot_core, eoa, native_token) = set_up();
+ fund_account_with_native_token(eoa.contract_address, native_token, Bounded::::MAX.into());
+
+ let caller = contract_address_const::();
+
+ let outside_execution = OutsideExecutionBuilderTrait::new(kakarot_core.contract_address)
+ .with_caller(caller)
+ .with_calls(
+ [
+ CallBuilderTrait::new(kakarot_core.contract_address)
+ .with_calldata(legacy_rlp_encoded_tx())
+ .build()
+ ].span()
+ )
+ .build();
+
+ // to reproduce locally:
+ // run: cp .env.example .env
+ // bun install & bun run scripts/compute_rlp_encoding.ts
+
+ let signature = serialize_transaction_signature(
+ Signature {
+ r: 0xf2805d01dd4fa240c79039c85a77554fc186cc73c2025d7f8c02bc8fe1a982b5,
+ s: 0x27ff351275563c1a29ab9eaeec4a3b63fbc4035d6da6b8b6af52c7993b5869ec,
+ y_parity: true
+ },
+ TxType::Legacy,
+ chain_id()
+ )
+ .span();
+
+ cheat_caller_address(eoa.contract_address, caller, CheatSpan::TargetCalls(1));
+ stop_cheat_caller_address(kakarot_core.contract_address);
+ let data = eoa.execute_from_outside(outside_execution, signature);
+
+ assert_eq!(data.len(), 1);
+ assert_eq!(*data[0], [].span());
+
+ stop_cheat_caller_address(eoa.contract_address);
+ tear_down(eoa);
+}
+
+#[test]
+fn test_execute_from_outside_eip2930_tx() {
+ let (kakarot_core, eoa, native_token) = set_up();
+ fund_account_with_native_token(eoa.contract_address, native_token, Bounded::::MAX.into());
+ let caller = contract_address_const::();
+
+ // Signature for the default eip2930 tx
+ let signature = Signature {
+ r: 0x5c4ae1ed01c8df4277f02aa3443f8183ed44627217fd7f27badaed8795906e78,
+ s: 0x4d2af576441428d47c174ffddc6e70b980527a57795b3c87a71878f97ecef274,
+ y_parity: true
+ };
+
+ // Defaults with an eip2930 tx
+ let outside_execution = OutsideExecutionBuilderTrait::new(kakarot_core.contract_address)
+ .with_caller(caller)
+ .build();
+ let signature = serialize_transaction_signature(signature, TxType::Eip2930, chain_id()).span();
+
+ cheat_caller_address(eoa.contract_address, caller, CheatSpan::TargetCalls(1));
+ stop_cheat_caller_address(kakarot_core.contract_address);
+ let data = eoa.execute_from_outside(outside_execution, signature);
+
+ assert_eq!(data.len(), 1);
+ assert_eq!(*data[0], [].span());
+
+ stop_cheat_caller_address(eoa.contract_address);
+ tear_down(eoa);
+}
+
+
+#[test]
+fn test_execute_from_outside_eip1559_tx() {
+ let (kakarot_core, eoa, native_token) = set_up();
+ fund_account_with_native_token(eoa.contract_address, native_token, Bounded::::MAX.into());
+
+ let caller = contract_address_const::();
+
+ let outside_execution = OutsideExecutionBuilderTrait::new(kakarot_core.contract_address)
+ .with_caller(caller)
+ .with_calls(
+ [
+ CallBuilderTrait::new(kakarot_core.contract_address)
+ .with_calldata(eip_1559_encoded_tx())
+ .build()
+ ].span()
+ )
+ .build();
+
+ // to reproduce locally:
+ // run: cp .env.example .env
+ // bun install & bun run scripts/compute_rlp_encoding.ts
+ let signature = Signature {
+ r: 0xb2563dbafa29dd6f126f0e6581b772d3f07063e2f07fb7bdf73aad34a04c4283,
+ s: 0x73df539e40359b81b8f260ed04431de098fc149bc5e27120e6711acabaecd067,
+ y_parity: true
+ };
+ let signature = serialize_transaction_signature(signature, TxType::Eip1559, chain_id()).span();
+
+ // Stop all cheats and only mock the EFO caller.
+ stop_cheat_caller_address_global();
+ cheat_caller_address(eoa.contract_address, caller, CheatSpan::TargetCalls(1));
+ let data = eoa.execute_from_outside(outside_execution, signature);
+
+ assert_eq!(data.len(), 1);
+ assert_eq!(*data[0], [].span());
+
+ tear_down(eoa);
+}
+
+#[test]
+fn test_execute_from_outside_eip_2930_counter_inc_tx() {
+ let (kakarot_core, eoa, native_token) = set_up();
+ fund_account_with_native_token(eoa.contract_address, native_token, Bounded::::MAX.into());
+
+ let kakarot_address = kakarot_core.contract_address;
+
+ deploy_contract_account(kakarot_core, other_evm_address(), counter_evm_bytecode());
+
+ start_cheat_caller_address(kakarot_address, eoa.contract_address);
+
+ // Then
+ // selector: function get()
+ let data_get_tx = [0x6d, 0x4c, 0xe6, 0x3c].span();
+
+ // check counter value is 0 before doing inc
+ let tx = call_transaction(chain_id(), Option::Some(other_evm_address()), data_get_tx);
+
+ let (_, return_data, _) = kakarot_core
+ .eth_call(origin: transaction_signer(), tx: Transaction::Legacy(tx),);
+
+ assert_eq!(return_data, u256_to_bytes_array(0).span());
+
+ // perform inc on the counter
+ let call = Call {
+ to: kakarot_address,
+ selector: selector!("eth_send_transaction"),
+ calldata: serialize_bytes(eip_2930_rlp_encoded_counter_inc_tx()).span()
+ };
+
+ start_cheat_transaction_hash(eoa.contract_address, selector!("transaction_hash"));
+ start_cheat_block_timestamp(eoa.contract_address, 100);
+ cheat_caller_address(
+ eoa.contract_address, contract_address_const::<0>(), CheatSpan::TargetCalls(1)
+ );
+ let mut spy = spy_events();
+ let outside_execution = OutsideExecution {
+ caller: contract_address_const::<'ANY_CALLER'>(),
+ nonce: 0,
+ execute_after: 0,
+ execute_before: 10000000,
+ calls: array![call].span()
+ };
+ let signature = Signature {
+ r: 0x8cd55583b5da62b3fd23586bf4f1ffd496046b9d248a7983ec41bd6fb673f379,
+ s: 0x09432a74ec3720a226ac040ce828f92e22350c4d8f7b188693cad035e99372ed,
+ y_parity: true
+ };
+ let signature = serialize_transaction_signature(signature, TxType::Eip2930, chain_id()).span();
+ stop_cheat_caller_address(kakarot_core.contract_address);
+ let result = eoa.execute_from_outside(outside_execution, signature);
+ assert_eq!(result.len(), 1);
+
+ let expected_event = AccountContract::Event::transaction_executed(
+ AccountContract::TransactionExecuted {
+ response: *result.span()[0], success: true, gas_used: 0
+ }
+ );
+ let mut keys = array![];
+ let mut data = array![];
+ expected_event.append_keys_and_data(ref keys, ref data);
+ let mut contract_events = EventsFilterBuilderTrait::from_events(@spy.get_events())
+ .with_contract_address(eoa.contract_address)
+ .with_keys(keys.span())
+ .build();
+
+ let mut received_keys = contract_events.events[0].keys.span();
+ let mut received_data = contract_events.events[0].data.span();
+ let deserialized_received: AccountContract::Event = Event::deserialize(
+ ref received_keys, ref received_data
+ )
+ .unwrap();
+ if let AccountContract::Event::transaction_executed(transaction_executed) =
+ deserialized_received {
+ let expected_response = *result.span()[0];
+ let expected_success = true;
+ let not_expected_gas_used = 0;
+ assert_eq!(transaction_executed.response, expected_response);
+ assert_eq!(transaction_executed.success, expected_success);
+ assert_ne!(transaction_executed.gas_used, not_expected_gas_used);
+ } else {
+ panic!("Expected transaction_executed event");
+ }
+ // check counter value has increased
+ let tx = call_transaction(chain_id(), Option::Some(other_evm_address()), data_get_tx);
+ let (_, return_data, _) = kakarot_core
+ .eth_call(origin: transaction_signer(), tx: Transaction::Legacy(tx),);
+ assert_eq!(return_data, u256_to_bytes_array(1).span());
+}
diff --git a/cairo/kakarot-ssj/crates/contracts/tests/test_kakarot_core.cairo b/cairo/kakarot-ssj/crates/contracts/tests/test_kakarot_core.cairo
new file mode 100644
index 000000000..29b2a32bf
--- /dev/null
+++ b/cairo/kakarot-ssj/crates/contracts/tests/test_kakarot_core.cairo
@@ -0,0 +1,429 @@
+use contracts::account_contract::{IAccountDispatcher, IAccountDispatcherTrait};
+use contracts::kakarot_core::interface::IExtendedKakarotCoreDispatcherTrait;
+use contracts::kakarot_core::{KakarotCore};
+use contracts::test_contracts::test_upgradeable::{
+ IMockContractUpgradeableDispatcher, IMockContractUpgradeableDispatcherTrait
+};
+use contracts::test_data::{deploy_counter_calldata, counter_evm_bytecode};
+use contracts::{test_utils as contract_utils,};
+use core::num::traits::Zero;
+use core::ops::SnapshotDeref;
+use core::option::OptionTrait;
+use core::starknet::storage::StoragePathEntry;
+use core::starknet::{contract_address_const, ContractAddress, EthAddress, ClassHash};
+
+
+use core::traits::TryInto;
+use evm::test_utils::chain_id;
+use evm::test_utils;
+use snforge_std::{
+ declare, DeclareResultTrait, start_cheat_caller_address, spy_events, EventSpyTrait,
+ cheat_caller_address, CheatSpan, store, load
+};
+use snforge_utils::snforge_utils::{EventsFilterBuilderTrait, ContractEventsTrait};
+use starknet::storage::StorageTrait;
+use utils::eth_transaction::legacy::TxLegacy;
+use utils::eth_transaction::transaction::Transaction;
+use utils::helpers::{u256_to_bytes_array};
+use utils::traits::eth_address::EthAddressExTrait;
+
+#[test]
+fn test_kakarot_core_owner() {
+ let (_, kakarot_core) = contract_utils::setup_contracts_for_testing();
+
+ assert(kakarot_core.owner() == test_utils::other_starknet_address(), 'wrong owner')
+}
+
+#[test]
+fn test_kakarot_core_transfer_ownership() {
+ let (_, kakarot_core) = contract_utils::setup_contracts_for_testing();
+
+ assert(kakarot_core.owner() == test_utils::other_starknet_address(), 'wrong owner');
+ start_cheat_caller_address(kakarot_core.contract_address, test_utils::other_starknet_address());
+ kakarot_core.transfer_ownership(test_utils::starknet_address());
+ assert(kakarot_core.owner() == test_utils::starknet_address(), 'wrong owner')
+}
+
+#[test]
+fn test_kakarot_core_renounce_ownership() {
+ let (_, kakarot_core) = contract_utils::setup_contracts_for_testing();
+
+ assert(kakarot_core.owner() == test_utils::other_starknet_address(), 'wrong owner');
+ start_cheat_caller_address(kakarot_core.contract_address, test_utils::other_starknet_address());
+ kakarot_core.renounce_ownership();
+ assert(kakarot_core.owner() == contract_address_const::<0x00>(), 'wrong owner')
+}
+
+#[test]
+fn test_kakarot_core_chain_id() {
+ contract_utils::setup_contracts_for_testing();
+
+ assert(chain_id() == test_utils::chain_id(), 'wrong chain id');
+}
+
+#[test]
+fn test_kakarot_core_set_native_token() {
+ let (native_token, kakarot_core) = contract_utils::setup_contracts_for_testing();
+
+ assert(kakarot_core.get_native_token() == native_token.contract_address, 'wrong native_token');
+
+ start_cheat_caller_address(kakarot_core.contract_address, test_utils::other_starknet_address());
+ kakarot_core.set_native_token(contract_address_const::<0xdead>());
+ assert(
+ kakarot_core.get_native_token() == contract_address_const::<0xdead>(),
+ 'wrong new native_token'
+ );
+}
+
+#[test]
+fn test_kakarot_core_deploy_eoa() {
+ let (_, kakarot_core) = contract_utils::setup_contracts_for_testing();
+ let mut spy = spy_events();
+ let eoa_starknet_address = kakarot_core
+ .deploy_externally_owned_account(test_utils::evm_address());
+
+ let expected = KakarotCore::Event::AccountDeployed(
+ KakarotCore::AccountDeployed {
+ evm_address: test_utils::evm_address(), starknet_address: eoa_starknet_address
+ }
+ );
+ let contract_events = EventsFilterBuilderTrait::from_events(@spy.get_events())
+ .with_contract_address(kakarot_core.contract_address)
+ .build();
+ contract_events.assert_emitted(@expected);
+}
+
+#[test]
+fn test_kakarot_core_eoa_mapping() {
+ // Given
+ let (_, kakarot_core) = contract_utils::setup_contracts_for_testing();
+ assert(
+ kakarot_core.address_registry(test_utils::evm_address()).is_zero(),
+ 'should be uninitialized'
+ );
+
+ let expected_eoa_starknet_address = kakarot_core
+ .deploy_externally_owned_account(test_utils::evm_address());
+
+ // When
+ let address = kakarot_core.address_registry(test_utils::evm_address());
+
+ // Then
+ assert_eq!(address, expected_eoa_starknet_address);
+
+ let another_sn_address: ContractAddress = 0xbeef.try_into().unwrap();
+
+ // Set the address registry to the another_sn_address
+ let mut kakarot_state = KakarotCore::unsafe_new_contract_state();
+ let map_entry_address = kakarot_state
+ .snapshot_deref()
+ .storage()
+ .Kakarot_evm_to_starknet_address
+ .entry(test_utils::evm_address())
+ .deref()
+ .__storage_pointer_address__;
+ store(
+ kakarot_core.contract_address, map_entry_address.into(), [another_sn_address.into()].span()
+ );
+
+ let address = kakarot_core.address_registry(test_utils::evm_address());
+ assert_eq!(address, another_sn_address)
+}
+
+#[test]
+fn test_kakarot_core_upgrade_contract() {
+ let (_, kakarot_core) = contract_utils::setup_contracts_for_testing();
+
+ let class_hash: ClassHash = (*declare("MockContractUpgradeableV1")
+ .unwrap()
+ .contract_class()
+ .class_hash);
+
+ start_cheat_caller_address(kakarot_core.contract_address, test_utils::other_starknet_address());
+ kakarot_core.upgrade(class_hash);
+
+ let version = IMockContractUpgradeableDispatcher {
+ contract_address: kakarot_core.contract_address
+ }
+ .version();
+ assert(version == 1, 'version is not 1');
+}
+
+#[test]
+fn test_kakarot_core_get_starknet_address_registered() {
+ let (_, kakarot_core) = contract_utils::setup_contracts_for_testing();
+ let mut kakarot_state = KakarotCore::unsafe_new_contract_state();
+
+ let registered_evm_address = test_utils::evm_address();
+ let registered_starknet_address = test_utils::starknet_address();
+ let registered_map_entry_address = kakarot_state
+ .snapshot_deref()
+ .storage()
+ .Kakarot_evm_to_starknet_address
+ .entry(registered_evm_address)
+ .deref()
+ .__storage_pointer_address__;
+ // store the registered address in the mapping
+ store(
+ kakarot_core.contract_address,
+ registered_map_entry_address.into(),
+ [registered_starknet_address.into()].span()
+ );
+ // when the address is registered in the mapping, it's returned
+ assert_eq!(
+ kakarot_core.get_starknet_address(registered_evm_address), registered_starknet_address
+ );
+}
+
+#[test]
+fn test_kakarot_core_get_starknet_address_unregistered() {
+ let (_, kakarot_core) = contract_utils::setup_contracts_for_testing();
+ let mut kakarot_state = KakarotCore::unsafe_new_contract_state();
+
+ let unregistered_evm_address = test_utils::other_evm_address();
+ let unregistered_map_entry_address = kakarot_state
+ .snapshot_deref()
+ .storage()
+ .Kakarot_evm_to_starknet_address
+ .entry(unregistered_evm_address)
+ .deref()
+ .__storage_pointer_address__;
+ let map_data = load(kakarot_core.contract_address, unregistered_map_entry_address.into(), 1);
+ // when the map entry is empty
+ assert_eq!(*map_data[0], 0);
+ // then an unregistered address should return the same as compute_starknet_address
+ let computed_address = utils::helpers::compute_starknet_address(
+ kakarot_core.contract_address,
+ unregistered_evm_address,
+ kakarot_core.uninitialized_account_class_hash()
+ );
+ assert_eq!(kakarot_core.get_starknet_address(unregistered_evm_address), computed_address);
+}
+
+//TODO Needs to be restored by giving the RLP encoding of the transaction to the test
+// More generally, this should _probably_ be an e2e test anyway
+#[test]
+#[ignore]
+fn test_eth_send_transaction_non_deploy_tx() {
+ // Given
+ let (native_token, kakarot_core) = contract_utils::setup_contracts_for_testing();
+
+ let evm_address = test_utils::evm_address();
+ let eoa = kakarot_core.deploy_externally_owned_account(evm_address);
+ contract_utils::fund_account_with_native_token(
+ eoa, native_token, 0xfffffffffffffffffffffffffff
+ );
+
+ let counter_address = 'counter_contract'.try_into().unwrap();
+ contract_utils::deploy_contract_account(kakarot_core, counter_address, counter_evm_bytecode());
+
+ let gas_limit = test_utils::tx_gas_limit();
+ let gas_price = test_utils::gas_price();
+ let value = 0;
+
+ // Then
+ // selector: function get()
+ let data_get_tx = [0x6d, 0x4c, 0xe6, 0x3c].span();
+
+ // check counter value is 0 before doing inc
+ let tx = contract_utils::call_transaction(
+ chain_id(), Option::Some(counter_address), data_get_tx
+ );
+ let (_, return_data, _) = kakarot_core
+ .eth_call(origin: evm_address, tx: Transaction::Legacy(tx));
+
+ assert_eq!(return_data, u256_to_bytes_array(0).span());
+
+ // selector: function inc()
+ let data_increment_counter = [0x37, 0x13, 0x03, 0xc0].span();
+
+ // When
+ start_cheat_caller_address(kakarot_core.contract_address, eoa);
+
+ let tx = TxLegacy {
+ chain_id: Option::Some(chain_id()),
+ nonce: 0,
+ to: counter_address.into(),
+ value,
+ gas_price,
+ gas_limit,
+ input: data_increment_counter
+ };
+
+ let (success, _, _) = kakarot_core.eth_send_transaction(Transaction::Legacy(tx));
+ assert!(success);
+
+ // Then
+ // selector: function get()
+ let data_get_tx = [0x6d, 0x4c, 0xe6, 0x3c].span();
+
+ // check counter value is 1
+ let tx = contract_utils::call_transaction(
+ chain_id(), Option::Some(counter_address), data_get_tx
+ );
+ let (_, return_data, _) = kakarot_core
+ .eth_call(origin: evm_address, tx: Transaction::Legacy(tx));
+
+ // Then
+ assert_eq!(return_data, u256_to_bytes_array(1).span());
+}
+
+#[test]
+fn test_eth_call() {
+ // Given
+ let (_, kakarot_core) = contract_utils::setup_contracts_for_testing();
+
+ let evm_address = test_utils::evm_address();
+ kakarot_core.deploy_externally_owned_account(evm_address);
+ let account = contract_utils::deploy_contract_account(
+ kakarot_core, test_utils::other_evm_address(), counter_evm_bytecode()
+ );
+ let counter = IAccountDispatcher { contract_address: account.starknet };
+ cheat_caller_address(
+ counter.contract_address, kakarot_core.contract_address, CheatSpan::TargetCalls(1)
+ );
+ counter.write_storage(0, 1);
+
+ let to = Option::Some(test_utils::other_evm_address());
+ // selector: function get()
+ let calldata = [0x6d, 0x4c, 0xe6, 0x3c].span();
+
+ // When
+ let tx = contract_utils::call_transaction(chain_id(), to, calldata);
+ let (success, return_data, _) = kakarot_core
+ .eth_call(origin: evm_address, tx: Transaction::Legacy(tx));
+
+ // Then
+ assert_eq!(success, true);
+ assert_eq!(return_data, u256_to_bytes_array(1).span());
+}
+
+
+//TODO Needs to be restored by giving the RLP encoding of the transaction to the test
+#[test]
+#[ignore]
+fn test_eth_send_transaction_deploy_tx() {
+ // Given
+ let (native_token, kakarot_core) = contract_utils::setup_contracts_for_testing();
+
+ let evm_address = test_utils::evm_address();
+ let eoa = kakarot_core.deploy_externally_owned_account(evm_address);
+ contract_utils::fund_account_with_native_token(
+ eoa, native_token, 0xfffffffffffffffffffffffffff
+ );
+
+ let gas_limit = test_utils::tx_gas_limit();
+ let gas_price = test_utils::gas_price();
+ let value = 0;
+
+ // When
+ // Set the contract address to the EOA address, so that the caller of the
+ // `eth_send_transaction`
+ // is an eoa
+ let tx = TxLegacy {
+ chain_id: Option::Some(chain_id()),
+ nonce: 0,
+ to: Option::None.into(),
+ value,
+ gas_price,
+ gas_limit,
+ input: deploy_counter_calldata()
+ };
+ cheat_caller_address(kakarot_core.contract_address, eoa, CheatSpan::TargetCalls(1));
+ let (_, deploy_result, _) = kakarot_core.eth_send_transaction(Transaction::Legacy(tx));
+
+ // Then
+ let expected_address: EthAddress = 0x19587b345dcadfe3120272bd0dbec24741891759
+ .try_into()
+ .unwrap();
+ assert_eq!(deploy_result, expected_address.to_bytes().span());
+
+ // Set back the contract address to Kakarot for the calculation of the deployed SN contract
+ // address, where we use a kakarot internal functions and thus must "mock" its address.
+ let computed_sn_addr = kakarot_core.get_starknet_address(expected_address);
+ let CA = IAccountDispatcher { contract_address: computed_sn_addr };
+ let bytecode = CA.bytecode();
+ assert_eq!(bytecode, counter_evm_bytecode());
+
+ // Check that the account was created and `get` returns 0.
+ let input = [0x6d, 0x4c, 0xe6, 0x3c].span();
+
+ // No need to set address back to eoa, as eth_call doesn't use the caller address.
+ let tx = TxLegacy {
+ chain_id: Option::Some(chain_id()),
+ nonce: 0,
+ to: expected_address.into(),
+ value,
+ gas_price,
+ gas_limit,
+ input
+ };
+ let (_, result, _) = kakarot_core.eth_call(origin: evm_address, tx: Transaction::Legacy(tx));
+ // Then
+ assert(result == u256_to_bytes_array(0).span(), 'wrong result');
+}
+
+
+#[test]
+fn test_account_class_hash() {
+ let (_, kakarot_core) = contract_utils::setup_contracts_for_testing();
+ let uninitialized_account_class_hash = declare("UninitializedAccount")
+ .unwrap()
+ .contract_class()
+ .class_hash;
+
+ let class_hash = kakarot_core.uninitialized_account_class_hash();
+
+ assert(class_hash == *uninitialized_account_class_hash, 'wrong class hash');
+
+ let new_class_hash: ClassHash = (*declare("MockContractUpgradeableV1")
+ .unwrap()
+ .contract_class()
+ .class_hash);
+ start_cheat_caller_address(kakarot_core.contract_address, test_utils::other_starknet_address());
+ let mut spy = spy_events();
+ kakarot_core.set_account_class_hash(new_class_hash);
+
+ assert(kakarot_core.uninitialized_account_class_hash() == new_class_hash, 'wrong class hash');
+ let expected = KakarotCore::Event::AccountClassHashChange(
+ KakarotCore::AccountClassHashChange {
+ old_class_hash: class_hash, new_class_hash: new_class_hash
+ }
+ );
+ let contract_events = EventsFilterBuilderTrait::from_events(@spy.get_events())
+ .with_contract_address(kakarot_core.contract_address)
+ .build();
+ contract_events.assert_emitted(@expected);
+}
+
+#[test]
+fn test_account_contract_class_hash() {
+ let (_, kakarot_core) = contract_utils::setup_contracts_for_testing();
+
+ let account_contract_class_hash = (*declare("AccountContract")
+ .unwrap()
+ .contract_class()
+ .class_hash);
+ let class_hash = kakarot_core.get_account_contract_class_hash();
+
+ assert(class_hash == account_contract_class_hash, 'wrong class hash');
+
+ let new_class_hash: ClassHash = (*declare("MockContractUpgradeableV1")
+ .unwrap()
+ .contract_class()
+ .class_hash);
+ start_cheat_caller_address(kakarot_core.contract_address, test_utils::other_starknet_address());
+ let mut spy = spy_events();
+ kakarot_core.set_account_contract_class_hash(new_class_hash);
+ assert(kakarot_core.get_account_contract_class_hash() == new_class_hash, 'wrong class hash');
+
+ let expected = KakarotCore::Event::EOAClassHashChange(
+ KakarotCore::EOAClassHashChange {
+ old_class_hash: class_hash, new_class_hash: new_class_hash
+ }
+ );
+ let contract_events = EventsFilterBuilderTrait::from_events(@spy.get_events())
+ .with_contract_address(kakarot_core.contract_address)
+ .build();
+ contract_events.assert_emitted(@expected);
+}
diff --git a/cairo/kakarot-ssj/crates/contracts/tests/test_ownable.cairo b/cairo/kakarot-ssj/crates/contracts/tests/test_ownable.cairo
new file mode 100644
index 000000000..d5ac7ca2a
--- /dev/null
+++ b/cairo/kakarot-ssj/crates/contracts/tests/test_ownable.cairo
@@ -0,0 +1,210 @@
+use contracts::components::ownable::{ownable_component};
+use contracts::test_utils::constants::{ZERO, OWNER, OTHER};
+use core::num::traits::Zero;
+use core::starknet::ContractAddress;
+
+
+use ownable_component::{InternalImpl, OwnableImpl};
+use snforge_std::{start_cheat_caller_address, spy_events, test_address, EventSpyTrait};
+use snforge_utils::snforge_utils::{EventsFilterBuilderTrait, ContractEventsTrait};
+
+
+#[starknet::contract]
+pub mod MockContract {
+ use contracts::components::ownable::{ownable_component};
+
+ component!(path: ownable_component, storage: ownable, event: OwnableEvent);
+
+ #[abi(embed_v0)]
+ impl OwnableImpl = ownable_component::Ownable;
+
+ impl OwnableInternalImpl = ownable_component::InternalImpl;
+
+ #[storage]
+ struct Storage {
+ #[substorage(v0)]
+ ownable: ownable_component::Storage
+ }
+
+
+ #[event]
+ #[derive(Drop, starknet::Event)]
+ pub enum Event {
+ OwnableEvent: ownable_component::Event
+ }
+}
+type TestingState = ownable_component::ComponentState;
+
+impl TestingStateDefault of Default {
+ fn default() -> TestingState {
+ ownable_component::component_state_for_testing()
+ }
+}
+
+#[generate_trait]
+impl TestingStateImpl of TestingStateTrait {
+ fn new_with(owner: ContractAddress) -> TestingState {
+ let mut ownable: TestingState = Default::default();
+ ownable.initializer(owner);
+ ownable
+ }
+}
+
+#[test]
+fn test_ownable_initializer() {
+ let mut ownable: TestingState = Default::default();
+ let test_address: ContractAddress = test_address();
+ assert(ownable.owner().is_zero(), 'owner should be zero');
+
+ let mut spy = spy_events();
+ ownable.initializer(OWNER());
+ let expected = MockContract::Event::OwnableEvent(
+ ownable_component::Event::OwnershipTransferred(
+ ownable_component::OwnershipTransferred { previous_owner: ZERO(), new_owner: OWNER() }
+ )
+ );
+ let contract_events = EventsFilterBuilderTrait::from_events(@spy.get_events())
+ .with_contract_address(test_address)
+ .build();
+ contract_events.assert_emitted(@expected);
+ assert(ownable.owner() == OWNER(), 'Owner should be set');
+}
+
+#[test]
+fn test_assert_only_owner() {
+ let mut ownable: TestingState = TestingStateTrait::new_with(OWNER());
+ let test_address: ContractAddress = test_address();
+ start_cheat_caller_address(test_address, OWNER());
+
+ ownable.assert_only_owner();
+}
+
+#[test]
+#[should_panic(expected: ('Caller is not the owner',))]
+fn test_assert_only_owner_not_owner() {
+ let mut ownable: TestingState = TestingStateTrait::new_with(OWNER());
+ let test_address: ContractAddress = test_address();
+ start_cheat_caller_address(test_address, OTHER());
+
+ ownable.assert_only_owner();
+}
+
+#[test]
+#[should_panic(expected: ('Caller is the zero address',))]
+fn test_assert_only_owner_zero() {
+ let mut ownable: TestingState = TestingStateTrait::new_with(OWNER());
+ let test_address: ContractAddress = test_address();
+ start_cheat_caller_address(test_address, ZERO());
+ ownable.assert_only_owner();
+}
+
+#[test]
+fn test__transfer_ownership() {
+ let mut ownable: TestingState = TestingStateTrait::new_with(OWNER());
+ let test_address: ContractAddress = test_address();
+
+ let mut spy = spy_events();
+ let expected = MockContract::Event::OwnableEvent(
+ ownable_component::Event::OwnershipTransferred(
+ ownable_component::OwnershipTransferred { previous_owner: OWNER(), new_owner: OTHER() }
+ )
+ );
+ ownable._transfer_ownership(OTHER());
+
+ let contract_events = EventsFilterBuilderTrait::from_events(@spy.get_events())
+ .with_contract_address(test_address)
+ .build();
+ contract_events.assert_emitted(@expected);
+ assert(ownable.owner() == OTHER(), 'Owner should be OTHER');
+}
+
+
+#[test]
+fn test_transfer_ownership() {
+ let mut ownable: TestingState = TestingStateTrait::new_with(OWNER());
+ let test_address: ContractAddress = test_address();
+ start_cheat_caller_address(test_address, OWNER());
+
+ let mut spy = spy_events();
+ ownable.transfer_ownership(OTHER());
+ let expected = MockContract::Event::OwnableEvent(
+ ownable_component::Event::OwnershipTransferred(
+ ownable_component::OwnershipTransferred { previous_owner: OWNER(), new_owner: OTHER() }
+ )
+ );
+ let contract_events = EventsFilterBuilderTrait::from_events(@spy.get_events())
+ .with_contract_address(test_address)
+ .build();
+ contract_events.assert_emitted(@expected);
+
+ assert(ownable.owner() == OTHER(), 'Should transfer ownership');
+}
+
+#[test]
+#[should_panic(expected: ('New owner is the zero address',))]
+fn test_transfer_ownership_to_zero() {
+ let mut ownable: TestingState = TestingStateTrait::new_with(OWNER());
+ let test_address: ContractAddress = test_address();
+ start_cheat_caller_address(test_address, OWNER());
+
+ ownable.transfer_ownership(ZERO());
+}
+
+
+#[test]
+#[should_panic(expected: ('Caller is the zero address',))]
+fn test_transfer_ownership_from_zero() {
+ let mut ownable: TestingState = Default::default();
+
+ ownable.transfer_ownership(OTHER());
+}
+
+
+#[test]
+#[should_panic(expected: ('Caller is not the owner',))]
+fn test_transfer_ownership_from_nonowner() {
+ let mut ownable: TestingState = TestingStateTrait::new_with(OWNER());
+ let test_address: ContractAddress = test_address();
+ start_cheat_caller_address(test_address, OTHER());
+
+ ownable.transfer_ownership(OTHER());
+}
+
+
+#[test]
+fn test_renounce_ownership() {
+ let mut ownable: TestingState = TestingStateTrait::new_with(OWNER());
+ let test_address: ContractAddress = test_address();
+
+ start_cheat_caller_address(test_address, OWNER());
+ let mut spy = spy_events();
+ ownable.renounce_ownership();
+ let expected = MockContract::Event::OwnableEvent(
+ ownable_component::Event::OwnershipTransferred(
+ ownable_component::OwnershipTransferred { previous_owner: OWNER(), new_owner: ZERO() }
+ )
+ );
+ let contract_events = EventsFilterBuilderTrait::from_events(@spy.get_events())
+ .with_contract_address(test_address)
+ .build();
+ contract_events.assert_emitted(@expected);
+
+ assert(ownable.owner().is_zero(), 'ownership not renounced');
+}
+
+#[test]
+#[should_panic(expected: ('Caller is the zero address',))]
+fn test_renounce_ownership_from_zero_address() {
+ let mut ownable: TestingState = Default::default();
+ ownable.renounce_ownership();
+}
+
+#[test]
+#[should_panic(expected: ('Caller is not the owner',))]
+fn test_renounce_ownership_from_nonowner() {
+ let mut ownable: TestingState = TestingStateTrait::new_with(OWNER());
+ let test_address: ContractAddress = test_address();
+ start_cheat_caller_address(test_address, OTHER());
+
+ ownable.renounce_ownership();
+}
diff --git a/cairo1_contracts/token/.gitignore b/cairo/kakarot-ssj/crates/evm/.gitignore
similarity index 100%
rename from cairo1_contracts/token/.gitignore
rename to cairo/kakarot-ssj/crates/evm/.gitignore
diff --git a/cairo/kakarot-ssj/crates/evm/Scarb.toml b/cairo/kakarot-ssj/crates/evm/Scarb.toml
new file mode 100644
index 000000000..1180c54a4
--- /dev/null
+++ b/cairo/kakarot-ssj/crates/evm/Scarb.toml
@@ -0,0 +1,25 @@
+[package]
+name = "evm"
+version = "0.1.0"
+edition = "2024_07"
+
+# See more keys and their definitions at https://docs.swmansion.com/scarb/docs/reference/manifest.html
+
+[dependencies]
+starknet.workspace = true
+utils = { path = "../utils" }
+contracts = { path = "../contracts" }
+openzeppelin = { path = "../openzeppelin" }
+garaga = { git = "https://github.com/keep-starknet-strange/garaga.git" }
+
+[dev-dependencies]
+snforge_std = { git = "https://github.com/foundry-rs/starknet-foundry.git", tag = "v0.31.0" }
+snforge_utils = { path = "../snforge_utils" }
+assert_macros = "2.8.2"
+
+[tool]
+fmt.workspace = true
+
+[scripts]
+test = "snforge test --max-n-steps 4294967295"
+test-profiling = "snforge test --max-n-steps 4294967295 --build-profile"
diff --git a/cairo/kakarot-ssj/crates/evm/src/backend.cairo b/cairo/kakarot-ssj/crates/evm/src/backend.cairo
new file mode 100644
index 000000000..49f713b44
--- /dev/null
+++ b/cairo/kakarot-ssj/crates/evm/src/backend.cairo
@@ -0,0 +1,2 @@
+pub mod starknet_backend;
+pub mod validation;
diff --git a/cairo/kakarot-ssj/crates/evm/src/backend/starknet_backend.cairo b/cairo/kakarot-ssj/crates/evm/src/backend/starknet_backend.cairo
new file mode 100644
index 000000000..26237c045
--- /dev/null
+++ b/cairo/kakarot-ssj/crates/evm/src/backend/starknet_backend.cairo
@@ -0,0 +1,575 @@
+use contracts::account_contract::{IAccountDispatcher, IAccountDispatcherTrait};
+use contracts::kakarot_core::eth_rpc::IEthRPC;
+use contracts::kakarot_core::{KakarotCore, KakarotCore::KakarotCoreImpl};
+use core::num::traits::zero::Zero;
+use core::ops::SnapshotDeref;
+use core::starknet::storage::StoragePointerReadAccess;
+use core::starknet::syscalls::{deploy_syscall};
+use core::starknet::syscalls::{emit_event_syscall};
+use core::starknet::{EthAddress, get_block_info, SyscallResultTrait};
+use crate::errors::{ensure, EVMError};
+use crate::model::{Address, AddressTrait, Environment, Account, AccountTrait};
+use crate::model::{Transfer};
+use crate::state::{State, StateTrait};
+use openzeppelin::token::erc20::interface::{IERC20CamelDispatcher, IERC20CamelDispatcherTrait};
+use utils::constants::BURN_ADDRESS;
+use utils::constants;
+use utils::set::SetTrait;
+
+
+/// Commits the state changes to Starknet.
+///
+/// # Arguments
+///
+/// * `state` - The state to commit.
+///
+/// # Returns
+///
+/// `Ok(())` if the commit was successful, otherwise an `EVMError`.
+pub fn commit(ref state: State) -> Result<(), EVMError> {
+ commit_accounts(ref state)?;
+ transfer_native_token(ref state)?;
+ emit_events(ref state)?;
+ commit_storage(ref state)
+}
+
+/// Deploys a new EOA contract.
+///
+/// # Arguments
+///
+/// * `evm_address` - The EVM address of the EOA to deploy.
+pub fn deploy(evm_address: EthAddress) -> Result {
+ // Unlike CAs, there is not check for the existence of an EOA prealably to calling
+ // `EOATrait::deploy` - therefore, we need to check that there is no collision.
+ let mut is_deployed = evm_address.is_deployed();
+ ensure(!is_deployed, EVMError::Collision)?;
+
+ let mut kakarot_state = KakarotCore::unsafe_new_contract_state();
+ let uninitialized_account_class_hash = kakarot_state.uninitialized_account_class_hash();
+ let calldata: Span = [1, evm_address.into()].span();
+
+ let (starknet_address, _) = deploy_syscall(
+ uninitialized_account_class_hash,
+ contract_address_salt: evm_address.into(),
+ calldata: calldata,
+ deploy_from_zero: false
+ )
+ .unwrap_syscall();
+
+ Result::Ok(Address { evm: evm_address, starknet: starknet_address })
+}
+
+pub fn get_bytecode(evm_address: EthAddress) -> Span {
+ let kakarot_state = KakarotCore::unsafe_new_contract_state();
+ let starknet_address = kakarot_state.address_registry(evm_address);
+ if starknet_address.is_non_zero() {
+ let account = IAccountDispatcher { contract_address: starknet_address };
+ account.bytecode()
+ } else {
+ [].span()
+ }
+}
+
+/// Populate an Environment with Starknet syscalls.
+pub fn get_env(origin: Address, gas_price: u128) -> Environment {
+ let kakarot_state = KakarotCore::unsafe_new_contract_state();
+ let kakarot_storage = kakarot_state.snapshot_deref();
+ let block_info = get_block_info().unbox();
+
+ // tx.gas_price and env.gas_price have the same values here
+ // - this is not always true in EVM transactions
+ Environment {
+ origin: origin,
+ gas_price,
+ chain_id: kakarot_state.eth_chain_id(),
+ prevrandao: kakarot_storage.Kakarot_prev_randao.read(),
+ block_number: block_info.block_number,
+ block_gas_limit: constants::BLOCK_GAS_LIMIT,
+ block_timestamp: block_info.block_timestamp,
+ coinbase: kakarot_storage.Kakarot_coinbase.read(),
+ base_fee: kakarot_storage.Kakarot_base_fee.read(),
+ state: Default::default(),
+ }
+}
+
+/// Fetches the value stored at the given key for the corresponding contract accounts.
+/// If the account is not deployed (in case of a create/deploy transaction), returns 0.
+/// # Arguments
+///
+/// * `account` The account to read from.
+/// * `key` The key to read.
+///
+/// # Returns
+///
+/// A `Result` containing the value stored at the given key or an `EVMError` if there was an error.
+pub fn fetch_original_storage(account: @Account, key: u256) -> u256 {
+ let is_deployed = account.evm_address().is_deployed();
+ if is_deployed {
+ return IAccountDispatcher { contract_address: account.starknet_address() }.storage(key);
+ }
+ 0
+}
+
+/// Fetches the balance of the given address.
+///
+/// # Arguments
+///
+/// * `self` - The address to fetch the balance of.
+///
+/// # Returns
+///
+/// The balance of the given address.
+pub fn fetch_balance(self: @Address) -> u256 {
+ let kakarot_state = KakarotCore::unsafe_new_contract_state();
+ kakarot_state.eth_get_balance(*self.evm)
+}
+
+
+/// Commits the account changes to Starknet.
+///
+/// # Arguments
+///
+/// * `state` - The state containing the accounts to commit.
+///
+/// # Returns
+///
+/// `Ok(())` if the commit was successful, otherwise an `EVMError`.
+fn commit_accounts(ref state: State) -> Result<(), EVMError> {
+ let mut account_keys = state.accounts.keyset.to_span();
+ for evm_address in account_keys {
+ let account = state.accounts.changes.get(*evm_address).deref();
+ commit_account(@account, ref state);
+ };
+ return Result::Ok(());
+}
+
+/// Commits the account to Starknet by updating the account state if it
+/// exists, or deploying a new account if it doesn't.
+///
+/// # Arguments
+/// * `self` - The account to commit
+/// * `state` - The state, modified in the case of selfdestruct transfers
+///
+/// # Returns
+///
+/// `Ok(())` if the commit was successful, otherwise an `EVMError`.
+fn commit_account(self: @Account, ref state: State) {
+ if self.evm_address().is_precompile() {
+ return;
+ }
+
+ // Case new account
+ if !self.evm_address().is_deployed() {
+ deploy(self.evm_address()).expect('account deployment failed');
+ }
+
+ // @dev: EIP-6780 - If selfdestruct on an account created, dont commit data
+ // and burn any leftover balance.
+ if (self.is_selfdestruct() && self.is_created()) {
+ let kakarot_state = KakarotCore::unsafe_new_contract_state();
+ let burn_starknet_address = kakarot_state
+ .get_starknet_address(BURN_ADDRESS.try_into().unwrap());
+ let burn_address = Address {
+ starknet: burn_starknet_address, evm: BURN_ADDRESS.try_into().unwrap()
+ };
+ state
+ .add_transfer(
+ Transfer { sender: self.address(), recipient: burn_address, amount: self.balance() }
+ )
+ .expect('Failed to burn on selfdestruct');
+ return;
+ }
+
+ if !self.has_code_or_nonce() {
+ // Nothing to commit
+ return;
+ }
+
+ // Write updated nonce and storage
+ //TODO: storage commits are done in the State commitment as they're not part of the account
+ //model in SSJ
+ let starknet_account = IAccountDispatcher { contract_address: self.starknet_address() };
+ starknet_account.set_nonce(*self.nonce);
+
+ //Storage is handled outside of the account and must be committed after all accounts are
+ //committed.
+ if self.is_created() {
+ starknet_account.write_bytecode(self.bytecode());
+ starknet_account.set_code_hash(self.code_hash());
+ //TODO: save valid jumpdests https://github.com/kkrt-labs/kakarot-ssj/issues/839
+ }
+ return;
+}
+
+/// Iterates through the list of pending transfer and triggers them
+fn transfer_native_token(ref self: State) -> Result<(), EVMError> {
+ let kakarot_state = KakarotCore::unsafe_new_contract_state();
+ let native_token = kakarot_state.get_native_token();
+ while let Option::Some(transfer) = self.transfers.pop_front() {
+ IERC20CamelDispatcher { contract_address: native_token }
+ .transferFrom(transfer.sender.starknet, transfer.recipient.starknet, transfer.amount);
+ };
+ Result::Ok(())
+}
+
+/// Iterates through the list of events and emits them.
+fn emit_events(ref self: State) -> Result<(), EVMError> {
+ while let Option::Some(event) = self.events.pop_front() {
+ let mut keys = Default::default();
+ let mut data = Default::default();
+ Serde::>::serialize(@event.keys, ref keys);
+ Serde::>::serialize(@event.data, ref data);
+ emit_event_syscall(keys.span(), data.span()).unwrap_syscall();
+ };
+ return Result::Ok(());
+}
+
+/// Commits storage changes to the KakarotCore contract by writing pending
+/// state changes to Starknet Storage.
+/// commit_storage MUST be called after commit_accounts.
+fn commit_storage(ref self: State) -> Result<(), EVMError> {
+ let mut storage_keys = self.accounts_storage.keyset.to_span();
+ for state_key in storage_keys {
+ let (evm_address, key, value) = self.accounts_storage.changes.get(*state_key).deref();
+ let mut account = self.get_account(evm_address);
+ // @dev: EIP-6780 - If selfdestruct on an account created, dont commit data
+ if account.is_selfdestruct() && account.is_created() {
+ continue;
+ }
+ IAccountDispatcher { contract_address: account.starknet_address() }
+ .write_storage(key, value);
+ };
+ Result::Ok(())
+}
+
+#[cfg(test)]
+mod tests {
+ use core::starknet::{ClassHash};
+ use crate::backend::starknet_backend;
+ use crate::model::account::Account;
+ use crate::model::{Address, Event};
+ use crate::state::{State, StateTrait};
+ use crate::test_utils::{
+ setup_test_environment, uninitialized_account, account_contract, register_account
+ };
+ use crate::test_utils::{evm_address};
+ use snforge_std::{
+ test_address, start_mock_call, get_class_hash, spy_events, EventSpyTrait,
+ Event as StarknetEvent
+ };
+ use snforge_utils::snforge_utils::{
+ assert_not_called, assert_called, EventsFilterBuilderTrait, ContractEventsTrait
+ };
+ use super::{commit_storage, emit_events};
+ use utils::helpers::compute_starknet_address;
+ use utils::traits::bytes::U8SpanExTrait;
+
+ // Helper function to create a test account
+ fn create_test_account(is_selfdestruct: bool, is_created: bool, id: felt252) -> Account {
+ let evm_address = (evm_address().into() + id).try_into().unwrap();
+ let starknet_address = (0x5678 + id).try_into().unwrap();
+ Account {
+ address: Address { evm: evm_address, starknet: starknet_address },
+ nonce: 0,
+ code: [].span(),
+ code_hash: 0,
+ balance: 0,
+ selfdestruct: is_selfdestruct,
+ is_created: is_created,
+ }
+ }
+
+ // Implementation to convert an `Event` into a serialized `StarknetEvent`
+ impl EventIntoStarknetEvent of Into {
+ fn into(self: Event) -> StarknetEvent {
+ let mut serialized_keys = array![];
+ let mut serialized_data = array![];
+ Serde::>::serialize(@self.keys, ref serialized_keys);
+ Serde::>::serialize(@self.data, ref serialized_data);
+ StarknetEvent { keys: serialized_keys, data: serialized_data }
+ }
+ }
+
+
+ mod test_commit_storage {
+ use snforge_std::start_mock_call;
+ use snforge_utils::snforge_utils::{assert_called_with, assert_not_called};
+ use super::{create_test_account, StateTrait, commit_storage};
+
+ #[test]
+ fn test_commit_storage_normal_case() {
+ let mut state = Default::default();
+ let account = create_test_account(false, false, 0);
+ state.set_account(account);
+
+ let key = 0x100;
+ let value = 0x200;
+ state.write_state(account.address.evm, key, value);
+
+ // Mock the write_storage call
+ start_mock_call::<()>(account.address.starknet, selector!("write_storage"), ());
+
+ commit_storage(ref state).expect('commit storage failed');
+
+ //TODO(starknet-foundry): verify call args in assert_called
+ assert_called_with::<
+ (u256, u256)
+ >(account.address.starknet, selector!("write_storage"), (key, value));
+ }
+
+ #[test]
+ fn test_commit_storage_selfdestruct_and_created() {
+ let mut state = Default::default();
+ let account = create_test_account(true, true, 0);
+ state.set_account(account);
+
+ let key = 0x100;
+ let value = 0x200;
+ state.write_state(account.address.evm, key, value);
+
+ // Mock the write_storage call
+ start_mock_call::<()>(account.address.starknet, selector!("write_storage"), ());
+
+ commit_storage(ref state).expect('commit storage failed');
+
+ // Assert that write_storage was not called
+ assert_not_called(account.address.starknet, selector!("write_storage"));
+ }
+
+ #[test]
+ fn test_commit_storage_only_selfdestruct() {
+ let mut state = Default::default();
+ let account = create_test_account(true, false, 0);
+ state.set_account(account);
+
+ let key = 0x100;
+ let value = 0x200;
+ state.write_state(account.address.evm, key, value);
+
+ // Mock the write_storage call
+ start_mock_call::<()>(account.address.starknet, selector!("write_storage"), ());
+
+ commit_storage(ref state).expect('commit storage failed');
+
+ // Assert that write_storage was called
+ assert_called_with::<
+ (u256, u256)
+ >(account.address.starknet, selector!("write_storage"), (key, value));
+ }
+
+ #[test]
+ fn test_commit_storage_multiple_accounts() {
+ let mut state = Default::default();
+
+ // Account 0: Normal
+ let account0 = create_test_account(false, false, 0);
+ state.set_account(account0);
+
+ // Account 1: Selfdestruct and created
+ let account1 = create_test_account(true, true, 1);
+ state.set_account(account1);
+
+ // Account 2: Only selfdestruct
+ let account2 = create_test_account(true, false, 2);
+ state.set_account(account2);
+
+ // Set storage for all accounts
+ let key = 0x100;
+ let value = 0x200;
+ state.write_state(account0.address.evm, key, value);
+ state.write_state(account1.address.evm, key, value);
+ state.write_state(account2.address.evm, key, value);
+
+ // Mock the write_storage calls
+ start_mock_call::<()>(account0.address.starknet, selector!("write_storage"), ());
+ start_mock_call::<()>(account1.address.starknet, selector!("write_storage"), ());
+ start_mock_call::<()>(account2.address.starknet, selector!("write_storage"), ());
+
+ commit_storage(ref state).expect('commit storage failed');
+
+ // Assert that write_storage was called for accounts 1 and 3, but not for account 2
+ assert_called_with::<
+ (u256, u256)
+ >(account0.address.starknet, selector!("write_storage"), (key, value));
+ assert_not_called(account1.address.starknet, selector!("write_storage"));
+ assert_called_with::<
+ (u256, u256)
+ >(account2.address.starknet, selector!("write_storage"), (key, value));
+ }
+ }
+
+ #[test]
+ #[ignore]
+ //TODO(starknet-fonudry): it's impossible to deploy an un-declared class, nor is it possible to
+ //mock_deploy.
+ fn test_deploy() {
+ // store the classes in the context of the local execution, to be used for deploying the
+ // account class
+ setup_test_environment();
+ let test_address = test_address();
+
+ start_mock_call::<
+ ClassHash
+ >(test_address, selector!("get_account_contract_class_hash"), account_contract());
+ start_mock_call::<()>(test_address, selector!("initialize"), ());
+ let eoa_address = starknet_backend::deploy(evm_address())
+ .expect('deployment of EOA failed');
+
+ let class_hash = get_class_hash(eoa_address.starknet);
+ assert_eq!(class_hash, account_contract());
+ }
+
+ #[test]
+ #[ignore]
+ //TODO(starknet-foundry): it's impossible to deploy an un-declared class, nor is it possible to
+ //mock_deploy.
+ fn test_account_commit_undeployed_create_should_change_set_all() {
+ setup_test_environment();
+ let test_address = test_address();
+ let evm_address = evm_address();
+ let starknet_address = compute_starknet_address(
+ test_address, evm_address, uninitialized_account()
+ );
+
+ let mut state: State = Default::default();
+
+ // When
+ let bytecode = [0x1].span();
+ let code_hash = bytecode.compute_keccak256_hash();
+ let mut account = Account {
+ address: Address { evm: evm_address, starknet: starknet_address },
+ nonce: 420,
+ code: bytecode,
+ code_hash: code_hash,
+ balance: 0,
+ selfdestruct: false,
+ is_created: true,
+ };
+ state.set_account(account);
+
+ start_mock_call::<()>(starknet_address, selector!("set_nonce"), ());
+ start_mock_call::<
+ ClassHash
+ >(test_address, selector!("get_account_contract_class_hash"), account_contract());
+ starknet_backend::commit(ref state).expect('commitment failed');
+
+ // Then
+ //TODO(starknet-foundry): we should be able to assert this has been called with specific
+ //data, to pass in mock_call
+ assert_called(starknet_address, selector!("set_nonce"));
+ assert_not_called(starknet_address, selector!("write_bytecode"));
+ }
+
+ #[test]
+ fn test_account_commit_deployed_and_created_should_write_code() {
+ setup_test_environment();
+ let test_address = test_address();
+ let evm_address = evm_address();
+ let starknet_address = compute_starknet_address(
+ test_address, evm_address, uninitialized_account()
+ );
+ register_account(evm_address, starknet_address);
+
+ let mut state: State = Default::default();
+ let bytecode = [0x1].span();
+ let code_hash = bytecode.compute_keccak256_hash();
+ let mut account = Account {
+ address: Address { evm: evm_address, starknet: starknet_address },
+ nonce: 420,
+ code: bytecode,
+ code_hash: code_hash,
+ balance: 0,
+ selfdestruct: false,
+ is_created: true,
+ };
+ state.set_account(account);
+
+ start_mock_call::<()>(starknet_address, selector!("write_bytecode"), ());
+ start_mock_call::<()>(starknet_address, selector!("set_code_hash"), ());
+ start_mock_call::<()>(starknet_address, selector!("set_nonce"), ());
+ starknet_backend::commit(ref state).expect('commitment failed');
+
+ // Then the account should have a new code.
+ //TODO(starknet-foundry): we should be able to assert this has been called with specific
+ //data, to pass in mock_call
+ assert_called(starknet_address, selector!("write_bytecode"));
+ assert_called(starknet_address, selector!("set_code_hash"));
+ assert_called(starknet_address, selector!("set_nonce"));
+ }
+
+ #[test]
+ fn test_emit_events() {
+ // Initialize the state
+ let mut state: State = Default::default();
+
+ // Prepare a list of events with different combinations of keys and data
+ let evm_events = array![
+ Event { keys: array![], data: array![] }, // Empty event
+ Event { keys: array![1.into()], data: array![2, 3] }, // Single key, multiple data
+ Event {
+ keys: array![4.into(), 5.into()], data: array![6]
+ }, // Multiple keys, single data
+ Event {
+ keys: array![7.into(), 8.into(), 9.into()], data: array![10, 11, 12, 13]
+ } // Multiple keys and data
+ ];
+
+ // Add each event to the state
+ for event in evm_events.clone() {
+ state.add_event(event);
+ };
+
+ // Emit the events and assert that no events are left in the state
+ let mut spy = spy_events();
+ emit_events(ref state).expect('emit events failed');
+ assert!(state.events.is_empty());
+
+ // Capture emitted events
+ let contract_events = EventsFilterBuilderTrait::from_events(@spy.get_events())
+ .with_contract_address(test_address())
+ .build();
+
+ // Assert that each original event was emitted as expected
+ for event in evm_events {
+ let starknet_event = EventIntoStarknetEvent::into(
+ event
+ ); // Convert to StarkNet event format
+ contract_events.assert_emitted(@starknet_event);
+ };
+ }
+}
+// #[test]
+// #[ignore]
+//TODO(starknet-foundry): it's impossible to deploy an un-declared class, nor is it possible to
+//mock_deploy.
+// fn test_exec_sstore_finalized() { // // Given
+// setup_test_environment();
+// let mut vm = VMBuilderTrait::new_with_presets().build();
+// let evm_address = vm.message().target.evm;
+// let starknet_address = compute_starknet_address(
+// test_address(), evm_address, uninitialized_account()
+// );
+// let account = Account {
+// address: Address { evm: evm_address, starknet: starknet_address },
+// code: [].span(),
+// nonce: 1,
+// balance: 0,
+// selfdestruct: false,
+// is_created: false,
+// };
+// let key: u256 = 0x100000000000000000000000000000001;
+// let value: u256 = 0xABDE1E11A5;
+// vm.stack.push(value).expect('push failed');
+// vm.stack.push(key).expect('push failed');
+
+// // When
+
+// vm.exec_sstore().expect('exec_sstore failed');
+// starknet_backend::commit(ref vm.env.state).expect('commit storage failed');
+
+// // Then
+// assert(fetch_original_storage(@account, key) == value, 'wrong committed value')
+// }
+// }
+
+
diff --git a/cairo/kakarot-ssj/crates/evm/src/backend/validation.cairo b/cairo/kakarot-ssj/crates/evm/src/backend/validation.cairo
new file mode 100644
index 000000000..30414f5ed
--- /dev/null
+++ b/cairo/kakarot-ssj/crates/evm/src/backend/validation.cairo
@@ -0,0 +1,322 @@
+use contracts::account_contract::{IAccountDispatcher, IAccountDispatcherTrait};
+use contracts::kakarot_core::KakarotCore;
+use contracts::kakarot_core::eth_rpc::IEthRPC;
+use core::num::traits::Bounded;
+use core::ops::SnapshotDeref;
+use core::starknet::storage::{StoragePointerReadAccess};
+use core::starknet::{get_caller_address};
+use crate::gas;
+use starknet::storage::StorageTrait;
+use utils::eth_transaction::check_gas_fee;
+use utils::eth_transaction::transaction::{Transaction, TransactionTrait};
+
+/// Validates the ethereum transaction by checking adherence to Ethereum rules regarding
+/// Gas logic, nonce, chainId and required balance.
+///
+/// # Returns
+///
+/// * The intrinsic gas cost of the transaction
+pub fn validate_eth_tx(kakarot_state: @KakarotCore::ContractState, tx: Transaction) -> u64 {
+ let kakarot_storage = kakarot_state.snapshot_deref().storage();
+ // Validate transaction
+
+ // Validate chain_id for post eip155
+ let tx_chain_id = tx.chain_id();
+ let kakarot_chain_id: u64 = kakarot_state.eth_chain_id();
+ if (tx_chain_id.is_some()) {
+ assert(tx_chain_id.unwrap() == kakarot_chain_id, 'Invalid chain id');
+ }
+
+ // Validate nonce
+ let starknet_caller_address = get_caller_address();
+ let account = IAccountDispatcher { contract_address: starknet_caller_address };
+ let account_nonce = account.get_nonce();
+ assert(account_nonce == tx.nonce(), 'Invalid nonce');
+ assert(account_nonce != Bounded::::MAX, 'Nonce overflow');
+
+ // Validate gas
+ let gas_limit = tx.gas_limit();
+ assert(gas_limit <= kakarot_storage.Kakarot_block_gas_limit.read(), 'Tx gas > Block gas');
+ let block_base_fee = kakarot_storage.Kakarot_base_fee.read();
+ let gas_fee_check = check_gas_fee(
+ tx.max_fee_per_gas(), tx.max_priority_fee_per_gas(), block_base_fee.into()
+ );
+ assert!(gas_fee_check.is_ok(), "{:?}", gas_fee_check.unwrap_err());
+
+ // Intrinsic Gas
+ let intrinsic_gas = gas::calculate_intrinsic_gas_cost(@tx);
+ assert(gas_limit >= intrinsic_gas, 'Intrinsic gas > gas limit');
+
+ // Validate balance
+ let balance = kakarot_state.eth_get_balance(account.get_evm_address());
+ let max_gas_fee = tx.gas_limit().into() * tx.max_fee_per_gas();
+ let tx_cost = tx.value() + max_gas_fee.into();
+ assert(tx_cost <= balance, 'Not enough ETH');
+ intrinsic_gas
+}
+
+#[cfg(test)]
+mod tests {
+ use contracts::kakarot_core::KakarotCore;
+ use core::num::traits::Bounded;
+ use core::ops::SnapshotDeref;
+ use core::starknet::storage::{StorageTrait, StoragePathEntry};
+ use core::starknet::{EthAddress, ContractAddress};
+ use evm::gas;
+ use snforge_std::cheatcodes::storage::{store_felt252};
+ use snforge_std::{
+ start_mock_call, test_address, start_cheat_chain_id_global, store,
+ start_cheat_caller_address, mock_call
+ };
+ use super::validate_eth_tx;
+ use utils::constants::BLOCK_GAS_LIMIT;
+ use utils::eth_transaction::common::TxKind;
+ use utils::eth_transaction::eip1559::TxEip1559;
+ use utils::eth_transaction::legacy::TxLegacy;
+ use utils::eth_transaction::transaction::Transaction;
+
+ fn set_up() -> (KakarotCore::ContractState, ContractAddress) {
+ // Define the addresses used in the tests, whose calls will be mocked
+ let kakarot_state = KakarotCore::unsafe_new_contract_state();
+ let kakarot_storage = kakarot_state.snapshot_deref().storage();
+ let kakarot_address = test_address();
+ let account_evm_address: EthAddress = 'account_evm_address'.try_into().unwrap();
+ let account_starknet_address = 'account_starknet_address'.try_into().unwrap();
+ let native_token_address = 'native_token_address'.try_into().unwrap();
+
+ // Set up the environment
+ start_cheat_chain_id_global(1);
+ let base_fee_storage = kakarot_storage.Kakarot_base_fee.__base_address__;
+ let block_gas_limit_storage = kakarot_storage.Kakarot_block_gas_limit.__base_address__;
+ let native_token_storage_address = kakarot_storage
+ .Kakarot_native_token_address
+ .__base_address__;
+ store_felt252(kakarot_address, base_fee_storage, 1_000_000_000); // 1 Gwei
+ store_felt252(kakarot_address, block_gas_limit_storage, BLOCK_GAS_LIMIT.into());
+ store_felt252(kakarot_address, native_token_storage_address, native_token_address.into());
+ let map_entry_address = kakarot_storage
+ .Kakarot_evm_to_starknet_address
+ .entry(account_evm_address)
+ .deref()
+ .__storage_pointer_address__;
+ store(
+ kakarot_address,
+ map_entry_address.into(),
+ array![account_starknet_address.into()].span()
+ );
+
+ // Mock the calls to the account contract and the native token contract
+ start_cheat_caller_address(kakarot_address, account_starknet_address);
+ start_mock_call(account_starknet_address, selector!("get_nonce"), 0);
+ start_mock_call(
+ account_starknet_address, selector!("get_evm_address"), account_evm_address
+ );
+ start_mock_call(
+ native_token_address, selector!("balanceOf"), Bounded::::MAX
+ ); // Min to pay for gas + value
+
+ (kakarot_state, native_token_address)
+ }
+
+ #[test]
+ fn test_validate_eth_tx_typical_case() {
+ // Setup the environment
+ let (kakarot_state, _) = set_up();
+
+ // Create a transaction object for the test
+ let tx = Transaction::Eip1559(
+ TxEip1559 {
+ chain_id: 1, // Should match the chain_id in the environment
+ nonce: 0,
+ max_priority_fee_per_gas: 1_000_000_000, // 1 Gwei
+ max_fee_per_gas: 2_000_000_000, // 2 Gwei
+ gas_limit: 21000, // Standard gas limit for a simple transfer
+ to: TxKind::Call(0x1234567890123456789012345678901234567890.try_into().unwrap()),
+ value: 1000000000000000000_u256, // 1 ETH
+ input: array![].span(),
+ access_list: array![].span(),
+ }
+ );
+
+ // Test that the function performs validation and assert expected results
+ let intrinsic_gas = validate_eth_tx(@kakarot_state, tx);
+
+ assert_eq!(intrinsic_gas, 21000); // Standard intrinsic gas for a simple transfer
+ }
+
+ #[test]
+ #[should_panic(expected: ('Invalid chain id',))]
+ fn test_validate_eth_tx_invalid_chain_id() {
+ // Setup the environment
+ let (kakarot_state, _) = set_up();
+
+ // Create a transaction object for the test
+ let tx = Transaction::Eip1559(
+ TxEip1559 {
+ chain_id: 600, // wrong chain_id
+ nonce: 0,
+ max_priority_fee_per_gas: 1_000_000_000, // 1 Gwei
+ max_fee_per_gas: 2_000_000_000, // 2 Gwei
+ gas_limit: 21000, // Standard gas limit for a simple transfer
+ to: TxKind::Call(0x1234567890123456789012345678901234567890.try_into().unwrap()),
+ value: 1000000000000000000_u256, // 1 ETH
+ input: array![].span(),
+ access_list: array![].span(),
+ }
+ );
+
+ // Test that the function performs validation and assert expected results
+ validate_eth_tx(@kakarot_state, tx);
+ }
+
+ #[test]
+ #[should_panic(expected: ('Invalid nonce',))]
+ fn test_validate_eth_tx_invalid_nonce() {
+ // Setup the environment
+ let (kakarot_state, _) = set_up();
+
+ // Create a transaction object for the test
+ let tx = Transaction::Eip1559(
+ TxEip1559 {
+ chain_id: 1, // Should match the chain_id in the environment
+ nonce: 600, //Invalid nonce
+ max_priority_fee_per_gas: 1_000_000_000, // 1 Gwei
+ max_fee_per_gas: 2_000_000_000, // 2 Gwei
+ gas_limit: 21000, // Standard gas limit for a simple transfer
+ to: TxKind::Call(0x1234567890123456789012345678901234567890.try_into().unwrap()),
+ value: 1000000000000000000_u256, // 1 ETH
+ input: array![].span(),
+ access_list: array![].span(),
+ }
+ );
+
+ // Test that the function performs validation and assert expected results
+ validate_eth_tx(@kakarot_state, tx);
+ }
+
+ #[test]
+ #[should_panic(expected: ('Tx gas > Block gas',))]
+ fn test_validate_eth_tx_gas_limit_exceeds_block_gas_limit() {
+ // Setup the environment
+ let (kakarot_state, _) = set_up();
+
+ // Create a transaction object for the test
+ let tx = Transaction::Eip1559(
+ TxEip1559 {
+ chain_id: 1, // Should match the chain_id in the environment
+ nonce: 0,
+ max_priority_fee_per_gas: 1_000_000_000, // 1 Gwei
+ max_fee_per_gas: 2_000_000_000, // 2 Gwei
+ gas_limit: BLOCK_GAS_LIMIT + 1, // Gas limit greater than block gas limit
+ to: TxKind::Call(0x1234567890123456789012345678901234567890.try_into().unwrap()),
+ value: 1000000000000000000_u256, // 1 ETH
+ input: array![].span(),
+ access_list: array![].span(),
+ }
+ );
+
+ // Test that the function performs validation and assert expected results
+ validate_eth_tx(@kakarot_state, tx);
+ }
+
+ #[test]
+ #[should_panic(expected: ('Intrinsic gas > gas limit',))]
+ fn test_validate_eth_tx_intrinsic_gas_exceeds_gas_limit() {
+ // Setup the environment
+ let (kakarot_state, _) = set_up();
+
+ // Create a transaction object for the test
+ let mut tx = Transaction::Eip1559(
+ TxEip1559 {
+ chain_id: 1, // Should match the chain_id in the environment
+ nonce: 0,
+ max_priority_fee_per_gas: 1_000_000_000, // 1 Gwei
+ max_fee_per_gas: 2_000_000_000, // 2 Gwei
+ gas_limit: 0,
+ to: TxKind::Call(0x1234567890123456789012345678901234567890.try_into().unwrap()),
+ value: 1000000000000000000_u256, // 1 ETH
+ input: array![].span(),
+ access_list: array![].span(),
+ }
+ );
+
+ // Test that the function performs validation and assert expected results
+ validate_eth_tx(@kakarot_state, tx);
+ }
+
+ #[test]
+ #[should_panic(expected: ('Not enough ETH',))]
+ fn test_validate_eth_tx_insufficient_balance() {
+ // Setup the environment
+ let (kakarot_state, native_token_address) = set_up();
+
+ // Create a transaction object for the test
+ let tx = Transaction::Eip1559(
+ TxEip1559 {
+ chain_id: 1, // Should match the chain_id in the environment
+ nonce: 0,
+ max_priority_fee_per_gas: 1_000_000_000, // 1 Gwei
+ max_fee_per_gas: 2_000_000_000, // 2 Gwei
+ gas_limit: 21000, // Standard gas limit for a simple transfer
+ to: TxKind::Call(0x1234567890123456789012345678901234567890.try_into().unwrap()),
+ value: 1000000000000000000_u256, // 1 ETH
+ input: array![].span(),
+ access_list: array![].span(),
+ }
+ );
+
+ start_mock_call(native_token_address, selector!("balanceOf"), Bounded::::MIN);
+
+ // Test that the function performs validation and assert expected results
+ validate_eth_tx(@kakarot_state, tx);
+ }
+
+ #[test]
+ fn test_validate_eth_tx_max_gas_limit() {
+ // Setup the environment
+ let (kakarot_state, _) = set_up();
+
+ // Create a transaction object for the test
+ let tx = Transaction::Eip1559(
+ TxEip1559 {
+ chain_id: 1, // Should match the chain_id in the environment
+ nonce: 0,
+ max_priority_fee_per_gas: 1_000_000_000, // 1 Gwei
+ max_fee_per_gas: 2_000_000_000, // 2 Gwei
+ gas_limit: BLOCK_GAS_LIMIT, // Gas limit = Max block gas limit
+ to: TxKind::Call(0x1234567890123456789012345678901234567890.try_into().unwrap()),
+ value: 1000000000000000000_u256, // 1 ETH
+ input: array![].span(),
+ access_list: array![].span(),
+ }
+ );
+
+ // Test that the function performs validation and assert expected results
+ let intrinsic_gas = validate_eth_tx(@kakarot_state, tx);
+ assert_eq!(intrinsic_gas, 21000); // Standard intrinsic gas for a simple transfer
+ }
+
+ #[test]
+ fn test_validate_eth_tx_pre_eip155() {
+ // Setup the environment
+ let (kakarot_state, _) = set_up();
+
+ // Create a transaction object for the test
+ let tx = Transaction::Legacy(
+ TxLegacy {
+ chain_id: Option::None,
+ nonce: 0,
+ gas_price: 120000000000000000000000000,
+ gas_limit: 21000,
+ to: TxKind::Call(0x1234567890123456789012345678901234567890.try_into().unwrap()),
+ value: 1000000000000000000_u256, // Standard gas limit for a simple transfer
+ input: array![].span(),
+ }
+ );
+
+ // Test that the function performs validation and assert expected results
+ let intrinsic_gas = validate_eth_tx(@kakarot_state, tx);
+
+ assert_eq!(intrinsic_gas, 21000); // Standard intrinsic gas for a simple transfer
+ }
+}
diff --git a/cairo/kakarot-ssj/crates/evm/src/call_helpers.cairo b/cairo/kakarot-ssj/crates/evm/src/call_helpers.cairo
new file mode 100644
index 000000000..2c38fbbf0
--- /dev/null
+++ b/cairo/kakarot-ssj/crates/evm/src/call_helpers.cairo
@@ -0,0 +1,127 @@
+use contracts::kakarot_core::KakarotCore;
+use contracts::kakarot_core::interface::IKakarotCore;
+//! CALL, CALLCODE, DELEGATECALL, STATICCALL opcode helpers
+use core::cmp::min;
+use core::starknet::EthAddress;
+
+use crate::errors::EVMError;
+use crate::interpreter::EVMTrait;
+use crate::memory::MemoryTrait;
+use crate::model::vm::{VM, VMTrait};
+use crate::model::{Address, Message, ExecutionResultStatus};
+use crate::stack::StackTrait;
+use crate::state::StateTrait;
+use utils::constants;
+use utils::set::SetTrait;
+use utils::traits::{BoolIntoNumeric, U256TryIntoResult};
+
+/// CallArgs is a subset of CallContext
+/// Created in order to simplify setting up the call opcodes
+#[derive(Drop, PartialEq)]
+pub struct CallArgs {
+ caller: Address,
+ code_address: Address,
+ to: Address,
+ gas: u128,
+ value: u256,
+ calldata: Span,
+ ret_offset: usize,
+ ret_size: usize,
+ read_only: bool,
+ should_transfer: bool,
+ max_memory_size: usize,
+}
+
+#[derive(Drop)]
+pub enum CallType {
+ Call,
+ DelegateCall,
+ CallCode,
+ StaticCall,
+}
+
+#[generate_trait]
+pub impl CallHelpersImpl of CallHelpers {
+ /// Initializes and enters into a new sub-context
+ /// The Machine will change its `current_ctx` to point to the
+ /// newly created sub-context.
+ /// Then, the EVM execution loop will start on this new execution context.
+ fn generic_call(
+ ref self: VM,
+ gas: u64,
+ value: u256,
+ caller: EthAddress,
+ to: EthAddress,
+ code_address: EthAddress,
+ should_transfer_value: bool,
+ is_staticcall: bool,
+ args_offset: usize,
+ args_size: usize,
+ ret_offset: usize,
+ ret_size: usize
+ ) -> Result<(), EVMError> {
+ self.return_data = [].span();
+ if self.message().depth >= constants::STACK_MAX_DEPTH {
+ self.gas_left += gas;
+ return self.stack.push(0);
+ }
+
+ let mut calldata = Default::default();
+ self.memory.load_n(args_size, ref calldata, args_offset);
+
+ // We enter the standard flow
+ let code_account = self.env.state.get_account(code_address);
+ let read_only = is_staticcall || self.message.read_only;
+
+ let kakarot_core = KakarotCore::unsafe_new_contract_state();
+ let to = Address { evm: to, starknet: kakarot_core.get_starknet_address(to) };
+ let caller = Address { evm: caller, starknet: kakarot_core.get_starknet_address(caller) };
+
+ let message = Message {
+ caller,
+ target: to,
+ gas_limit: gas,
+ data: calldata.span(),
+ code: code_account.code,
+ code_address: code_account.address,
+ value: value,
+ should_transfer_value: should_transfer_value,
+ depth: self.message().depth + 1,
+ read_only: read_only,
+ accessed_addresses: self.accessed_addresses.clone().spanset(),
+ accessed_storage_keys: self.accessed_storage_keys.clone().spanset(),
+ };
+
+ let result = EVMTrait::process_message(message, ref self.env);
+ self.merge_child(@result);
+
+ match result.status {
+ ExecutionResultStatus::Success => {
+ self.return_data = result.return_data;
+ self.stack.push(1)?;
+ },
+ ExecutionResultStatus::Revert => {
+ self.return_data = result.return_data;
+ self.stack.push(0)?;
+ },
+ ExecutionResultStatus::Exception => {
+ // If the call has halted exceptionally,
+ // the return_data is emptied, and nothing is stored in memory
+ self.return_data = [].span();
+ self.stack.push(0)?;
+ return Result::Ok(());
+ },
+ }
+
+ // Get the min between len(return_data) and call_ctx.ret_size.
+ let actual_returndata_len = min(result.return_data.len(), ret_size);
+
+ let actual_return_data = result.return_data.slice(0, actual_returndata_len);
+ // TODO: Check if need to pad the memory with zeroes if result.return_data.len() <
+ // call_ctx.ret_size and memory is not empty at offset call_args.ret_offset +
+ // result.return_data.len()
+ self.memory.store_n(actual_return_data, ret_offset);
+
+ Result::Ok(())
+ }
+}
diff --git a/cairo/kakarot-ssj/crates/evm/src/create_helpers.cairo b/cairo/kakarot-ssj/crates/evm/src/create_helpers.cairo
new file mode 100644
index 000000000..14c3d435c
--- /dev/null
+++ b/cairo/kakarot-ssj/crates/evm/src/create_helpers.cairo
@@ -0,0 +1,193 @@
+use core::num::traits::Bounded;
+use core::num::traits::CheckedAdd;
+use core::num::traits::Zero;
+use core::starknet::EthAddress;
+use crate::errors::{ensure, EVMError};
+use crate::gas;
+use crate::interpreter::EVMTrait;
+use crate::memory::MemoryTrait;
+use crate::model::Message;
+use crate::model::account::{Account, AccountTrait};
+use crate::model::vm::{VM, VMTrait};
+use crate::model::{ExecutionResult, ExecutionResultTrait, ExecutionResultStatus};
+use crate::stack::StackTrait;
+use crate::state::StateTrait;
+use utils::address::{compute_contract_address, compute_create2_contract_address};
+use utils::constants;
+use utils::helpers::bytes_32_words_size;
+use utils::set::SetTrait;
+use utils::traits::{
+ BoolIntoNumeric, EthAddressIntoU256, U256TryIntoResult, SpanU8TryIntoResultEthAddress
+};
+/// Helper struct to prepare CREATE and CREATE2 opcodes
+#[derive(Drop)]
+pub struct CreateArgs {
+ to: EthAddress,
+ value: u256,
+ bytecode: Span,
+}
+
+#[derive(Copy, Drop)]
+pub enum CreateType {
+ Create,
+ Create2,
+}
+
+#[generate_trait]
+pub impl CreateHelpersImpl of CreateHelpers {
+ /// Prepare the initialization of a new child or so-called sub-context
+ /// As part of the CREATE family of opcodes.
+ fn prepare_create(ref self: VM, create_type: CreateType) -> Result {
+ let value = self.stack.pop()?;
+ let offset = self.stack.pop_saturating_usize()?;
+ let size = self.stack.pop_usize()?; // Any size bigger than a usize would MemoryOOG.
+
+ let memory_expansion = gas::memory_expansion(self.memory.size(), [(offset, size)].span())?;
+ self.memory.ensure_length(memory_expansion.new_size);
+ let init_code_gas = gas::init_code_cost(size);
+ let charged_gas = match create_type {
+ CreateType::Create => gas::CREATE
+ .checked_add(memory_expansion.expansion_cost)
+ .ok_or(EVMError::OutOfGas)?
+ .checked_add(init_code_gas)
+ .ok_or(EVMError::OutOfGas)?,
+ CreateType::Create2 => {
+ let calldata_words = bytes_32_words_size(size);
+ gas::CREATE
+ .checked_add(gas::KECCAK256WORD * calldata_words.into())
+ .ok_or(EVMError::OutOfGas)?
+ .checked_add(memory_expansion.expansion_cost)
+ .ok_or(EVMError::OutOfGas)?
+ .checked_add(init_code_gas)
+ .ok_or(EVMError::OutOfGas)?
+ },
+ };
+ self.charge_gas(charged_gas)?;
+
+ let mut bytecode = Default::default();
+ self.memory.load_n(size, ref bytecode, offset);
+
+ let to = match create_type {
+ CreateType::Create => {
+ let nonce = self.env.state.get_account(self.message().target.evm).nonce();
+ compute_contract_address(self.message().target.evm, sender_nonce: nonce)
+ },
+ CreateType::Create2 => compute_create2_contract_address(
+ self.message().target.evm, salt: self.stack.pop()?, bytecode: bytecode.span()
+ )?,
+ };
+
+ Result::Ok(CreateArgs { to, value, bytecode: bytecode.span() })
+ }
+
+
+ /// Initializes and enters into a new sub-context
+ /// The Machine will change its `current_ctx` to point to the
+ /// newly created sub-context.
+ /// Then, the EVM execution loop will start on this new execution context.
+ fn generic_create(ref self: VM, create_args: CreateArgs) -> Result<(), EVMError> {
+ self.accessed_addresses.add(create_args.to);
+
+ let create_message_gas = gas::max_message_call_gas(self.gas_left);
+ self.gas_left -= create_message_gas;
+
+ ensure(!self.message().read_only, EVMError::WriteInStaticContext)?;
+ self.return_data = [].span();
+
+ // The sender in the subcontext is the message's target
+ let sender_address = self.message().target;
+ let mut sender = self.env.state.get_account(sender_address.evm);
+ let sender_current_nonce = sender.nonce();
+ if sender.balance() < create_args.value
+ || sender_current_nonce == Bounded::::MAX
+ || self.message.depth == constants::STACK_MAX_DEPTH {
+ self.gas_left += create_message_gas;
+ return self.stack.push(0);
+ }
+
+ sender
+ .set_nonce(
+ sender_current_nonce + 1
+ ); // Will not overflow because of the previous check.
+ self.env.state.set_account(sender);
+
+ let mut target_account = self.env.state.get_account(create_args.to);
+ let target_address = target_account.address();
+ // Collision happens if the target account loaded in state has code or nonce set, meaning
+ // - it's deployed on SN and is an active EVM contract
+ // - it's not deployed on SN and is an active EVM contract in the Kakarot cache
+ if target_account.has_code_or_nonce() {
+ return self.stack.push(0);
+ };
+
+ ensure(create_args.bytecode.len() <= constants::MAX_INITCODE_SIZE, EVMError::OutOfGas)?;
+
+ let child_message = Message {
+ caller: sender_address,
+ target: target_address,
+ gas_limit: create_message_gas,
+ data: [].span(),
+ code: create_args.bytecode,
+ code_address: Zero::zero(),
+ value: create_args.value,
+ should_transfer_value: true,
+ depth: self.message().depth + 1,
+ read_only: false,
+ accessed_addresses: self.accessed_addresses.clone().spanset(),
+ accessed_storage_keys: self.accessed_storage_keys.clone().spanset(),
+ };
+
+ let result = EVMTrait::process_create_message(child_message, ref self.env);
+ self.merge_child(@result);
+
+ match result.status {
+ ExecutionResultStatus::Success => {
+ self.return_data = [].span();
+ self.stack.push(target_address.evm.into())?;
+ },
+ ExecutionResultStatus::Revert => {
+ self.return_data = result.return_data;
+ self.stack.push(0)?;
+ },
+ ExecutionResultStatus::Exception => {
+ // returndata is emptied in case of exception
+ self.return_data = [].span();
+ self.stack.push(0)?;
+ },
+ }
+ Result::Ok(())
+ }
+
+ /// Finalizes the creation of an account contract by
+ /// setting its code and charging the gas for the code deposit.
+ /// Since we don't have access to the child vm anymore, we charge the gas on
+ /// the returned ExecutionResult of the childVM.
+ ///
+ /// # Arguments
+ /// * `self` - The ExecutionResult to charge the gas on.
+ /// * `account` - The Account to finalize
+ #[inline(always)]
+ fn finalize_creation(
+ ref self: ExecutionResult, mut account: Account
+ ) -> Result {
+ let code = self.return_data;
+ let contract_code_gas = code.len().into() * gas::CODEDEPOSIT;
+
+ if code.len() != 0 {
+ ensure(*code[0] != 0xEF, EVMError::InvalidCode)?;
+ }
+ self.charge_gas(contract_code_gas)?;
+
+ ensure(code.len() <= constants::MAX_CODE_SIZE, EVMError::OutOfGas)?;
+
+ account.set_code(code);
+ Result::Ok(account)
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ //TODO: test create helpers
+
+
+}
diff --git a/cairo/kakarot-ssj/crates/evm/src/errors.cairo b/cairo/kakarot-ssj/crates/evm/src/errors.cairo
new file mode 100644
index 000000000..b6d17eaa5
--- /dev/null
+++ b/cairo/kakarot-ssj/crates/evm/src/errors.cairo
@@ -0,0 +1,112 @@
+use core::fmt::{Debug, Formatter, Error, Display};
+use utils::traits::bytes::ToBytes;
+
+// STACK
+
+// INSTRUCTIONS
+pub const PC_OUT_OF_BOUNDS: felt252 = 'KKT: pc >= bytecode length';
+
+// TYPE CONVERSION
+pub const TYPE_CONVERSION_ERROR: felt252 = 'KKT: type conversion error';
+
+// NUMERIC OPERATIONS
+pub const BALANCE_OVERFLOW: felt252 = 'KKT: balance overflow';
+
+// JUMP
+pub const INVALID_DESTINATION: felt252 = 'KKT: invalid JUMP destination';
+
+// CALL
+pub const VALUE_TRANSFER_IN_STATIC_CALL: felt252 = 'KKT: transfer value in static';
+pub const ACTIVE_MACHINE_STATE_IN_CALL_FINALIZATION: felt252 = 'KKT: active state in end call';
+pub const MISSING_PARENT_CONTEXT: felt252 = 'KKT: missing parent context';
+pub const CALL_GAS_GT_GAS_LIMIT: felt252 = 'KKT: call gas gt gas limit';
+
+// EVM STATE
+
+// STARKNET_SYSCALLS
+pub const READ_SYSCALL_FAILED: felt252 = 'KKT: read syscall failed';
+pub const BLOCK_HASH_SYSCALL_FAILED: felt252 = 'KKT: block_hash syscall failed';
+pub const WRITE_SYSCALL_FAILED: felt252 = 'KKT: write syscall failed';
+pub const CONTRACT_SYSCALL_FAILED: felt252 = 'KKT: contract syscall failed';
+pub const EXECUTION_INFO_SYSCALL_FAILED: felt252 = 'KKT: exec info syscall failed';
+
+// CREATE
+pub const CONTRACT_ACCOUNT_EXISTS: felt252 = 'KKT: Contract Account exists';
+pub const EOA_EXISTS: felt252 = 'KKT: EOA already exists';
+pub const ACCOUNT_EXISTS: felt252 = 'KKT: Account already exists';
+pub const DEPLOYMENT_FAILED: felt252 = 'KKT: deployment failed';
+
+// TRANSACTION ORIGIN
+pub const CALLING_FROM_UNDEPLOYED_ACCOUNT: felt252 = 'EOA: from is undeployed EOA';
+pub const CALLING_FROM_CA: felt252 = 'EOA: from is a contract account';
+
+#[derive(Drop, Copy, PartialEq)]
+pub enum EVMError {
+ StackOverflow,
+ StackUnderflow,
+ TypeConversionError: felt252,
+ NumericOperations: felt252,
+ InsufficientBalance,
+ ReturnDataOutOfBounds,
+ InvalidJump,
+ InvalidCode,
+ NotImplemented,
+ InvalidParameter: felt252,
+ InvalidOpcode: u8,
+ WriteInStaticContext,
+ Collision,
+ OutOfGas,
+ Assertion,
+ DepthLimit,
+ MemoryLimitOOG,
+ NonceOverflow
+}
+
+#[generate_trait]
+pub impl EVMErrorImpl of EVMErrorTrait {
+ fn to_string(self: EVMError) -> felt252 {
+ match self {
+ EVMError::StackOverflow => 'stack overflow',
+ EVMError::StackUnderflow => 'stack underflow',
+ EVMError::TypeConversionError(error_message) => error_message,
+ EVMError::NumericOperations(error_message) => error_message,
+ EVMError::InsufficientBalance => 'insufficient balance',
+ EVMError::ReturnDataOutOfBounds => 'return data out of bounds',
+ EVMError::InvalidJump => 'invalid jump destination',
+ EVMError::InvalidCode => 'invalid code',
+ EVMError::NotImplemented => 'not implemented',
+ EVMError::InvalidParameter(error_message) => error_message,
+ // TODO: refactor with dynamic strings once supported
+ EVMError::InvalidOpcode => 'invalid opcode'.into(),
+ EVMError::WriteInStaticContext => 'write protection',
+ EVMError::Collision => 'create collision'.into(),
+ EVMError::OutOfGas => 'out of gas'.into(),
+ EVMError::Assertion => 'assertion failed'.into(),
+ EVMError::DepthLimit => 'max call depth exceeded'.into(),
+ EVMError::MemoryLimitOOG => 'memory limit out of gas'.into(),
+ EVMError::NonceOverflow => 'nonce overflow'.into(),
+ }
+ }
+
+ fn to_bytes(self: EVMError) -> Span {
+ let error_message: felt252 = self.to_string();
+ let error_message: u256 = error_message.into();
+ error_message.to_be_bytes()
+ }
+}
+
+pub impl DebugEVMError of Debug {
+ fn fmt(self: @EVMError, ref f: Formatter) -> Result<(), Error> {
+ let error_message = (*self).to_string();
+ Display::fmt(@error_message, ref f)
+ }
+}
+
+#[inline(always)]
+pub fn ensure(cond: bool, err: EVMError) -> Result<(), EVMError> {
+ if cond {
+ Result::Ok(())
+ } else {
+ Result::Err(err)
+ }
+}
diff --git a/cairo/kakarot-ssj/crates/evm/src/gas.cairo b/cairo/kakarot-ssj/crates/evm/src/gas.cairo
new file mode 100644
index 000000000..9454410ae
--- /dev/null
+++ b/cairo/kakarot-ssj/crates/evm/src/gas.cairo
@@ -0,0 +1,403 @@
+use core::cmp::min;
+use core::num::traits::CheckedAdd;
+use crate::errors::EVMError;
+use utils::eth_transaction::common::TxKindTrait;
+use utils::eth_transaction::eip2930::{AccessListItem};
+use utils::eth_transaction::transaction::{Transaction, TransactionTrait};
+use utils::helpers::bytes_32_words_size;
+use utils::helpers;
+
+//! Gas costs for EVM operations
+//! Code is based on alloy project
+//! Source:
+
+pub const ZERO: u64 = 0;
+pub const BASE: u64 = 2;
+pub const VERYLOW: u64 = 3;
+pub const LOW: u64 = 5;
+pub const MID: u64 = 8;
+pub const HIGH: u64 = 10;
+pub const JUMPDEST: u64 = 1;
+pub const SELFDESTRUCT: u64 = 5000;
+pub const CREATE: u64 = 32000;
+pub const CALLVALUE: u64 = 9000;
+pub const NEWACCOUNT: u64 = 25000;
+pub const EXP: u64 = 10;
+pub const EXP_GAS_PER_BYTE: u64 = 50;
+pub const MEMORY: u64 = 3;
+pub const LOG: u64 = 375;
+pub const LOGDATA: u64 = 8;
+pub const LOGTOPIC: u64 = 375;
+pub const KECCAK256: u64 = 30;
+pub const KECCAK256WORD: u64 = 6;
+pub const COPY: u64 = 3;
+pub const BLOCKHASH: u64 = 20;
+pub const CODEDEPOSIT: u64 = 200;
+
+pub const SSTORE_SET: u64 = 20000;
+pub const SSTORE_RESET: u64 = 5000;
+pub const REFUND_SSTORE_CLEARS: u64 = 4800;
+
+pub const TRANSACTION_ZERO_DATA: u64 = 4;
+pub const TRANSACTION_NON_ZERO_DATA_INIT: u64 = 16;
+pub const TRANSACTION_NON_ZERO_DATA_FRONTIER: u64 = 68;
+pub const TRANSACTION_BASE_COST: u64 = 21000;
+pub const TRANSACTION_CREATE_COST: u64 = 32000;
+
+// Berlin EIP-2929 constants
+pub const ACCESS_LIST_ADDRESS: u64 = 2400;
+pub const ACCESS_LIST_STORAGE_KEY: u64 = 1900;
+pub const COLD_SLOAD_COST: u64 = 2100;
+pub const COLD_ACCOUNT_ACCESS_COST: u64 = 2600;
+pub const WARM_ACCESS_COST: u64 = 100;
+
+/// EIP-3860 : Limit and meter initcode
+pub const INITCODE_WORD_COST: u64 = 2;
+
+pub const CALL_STIPEND: u64 = 2300;
+
+// EIP-4844
+pub const BLOB_HASH_COST: u64 = 3;
+
+/// Defines the gas cost and stipend for executing call opcodes.
+///
+/// # Struct fields
+///
+/// * `cost`: The non-refundable portion of gas reserved for executing the call opcode.
+/// * `stipend`: The portion of gas available to sub-calls that is refundable if not consumed.
+#[derive(Drop)]
+pub struct MessageCallGas {
+ pub cost: u64,
+ pub stipend: u64,
+}
+
+/// Defines the new size and the expansion cost after memory expansion.
+///
+/// # Struct fields
+///
+/// * `new_size`: The new size of the memory after extension.
+/// * `expansion_cost`: The cost of the memory extension.
+#[derive(Drop)]
+pub struct MemoryExpansion {
+ pub new_size: u32,
+ pub expansion_cost: u64,
+}
+
+/// Calculates the maximum gas that is allowed for making a message call.
+///
+/// # Arguments
+/// * `gas`: The gas available for the message call.
+///
+/// # Returns
+/// * The maximum gas allowed for the message call.
+pub fn max_message_call_gas(gas: u64) -> u64 {
+ gas - (gas / 64)
+}
+
+/// Calculates the MessageCallGas (cost and stipend) for executing call Opcodes.
+///
+/// # Parameters
+///
+/// * `value`: The amount of native token that needs to be transferred.
+/// * `gas`: The amount of gas provided to the message-call.
+/// * `gas_left`: The amount of gas left in the current frame.
+/// * `memory_cost`: The amount needed to extend the memory in the current frame.
+/// * `extra_gas`: The amount of gas needed for transferring value + creating a new account inside a
+/// message call.
+///
+/// # Returns
+///
+/// * `Result`: The calculated MessageCallGas or an error if overflow
+/// occurs.
+pub fn calculate_message_call_gas(
+ value: u256, gas: u64, gas_left: u64, memory_cost: u64, extra_gas: u64
+) -> Result {
+ let call_stipend = if value == 0 {
+ 0
+ } else {
+ CALL_STIPEND
+ };
+
+ // Check for overflow when adding extra_gas and memory_cost
+ let total_extra_cost = extra_gas.checked_add(memory_cost).ok_or(EVMError::OutOfGas)?;
+ let gas = if gas_left < total_extra_cost {
+ gas
+ } else {
+ let remaining_gas = gas_left - total_extra_cost; // Safe because of the check above
+ min(gas, max_message_call_gas(remaining_gas))
+ };
+
+ let cost = gas.checked_add(extra_gas).ok_or(EVMError::OutOfGas)?;
+ let stipend = gas.checked_add(call_stipend).ok_or(EVMError::OutOfGas)?;
+
+ Result::Ok(MessageCallGas { cost, stipend })
+}
+
+
+/// Calculates the gas cost for allocating memory
+/// to the smallest multiple of 32 bytes,
+/// such that the allocated size is at least as big as the given size.
+///
+/// To optimize computations on u128 and avoid overflows, we compute size_in_words / 512
+/// instead of size_in_words * size_in_words / 512. Then we recompute the
+/// resulting quotient: x^2 = 512q + r becomes
+/// x = 512 q0 + r0 => x^2 = 512(512 q0^2 + 2 q0 r0) + r0^2
+/// r0^2 = 512 q1 + r1
+/// x^2 = 512(512 q0^2 + 2 q0 r0 + q1) + r1
+/// q = 512 * q0 * q0 + 2 * q0 * r0 + q1
+/// # Parameters
+///
+/// * `size_in_bytes` - The size of the data in bytes.
+///
+/// # Returns
+///
+/// * `total_gas_cost` - The gas cost for storing data in memory.
+pub fn calculate_memory_gas_cost(size_in_bytes: usize) -> u64 {
+ let _512: NonZero = 512_u64.try_into().unwrap();
+ let size_in_words = bytes_32_words_size(size_in_bytes);
+ let linear_cost = size_in_words.into() * MEMORY;
+
+ let (q0, r0) = DivRem::div_rem(size_in_words.into(), _512);
+ let (q1, _) = DivRem::div_rem(r0 * r0, _512);
+ let quadratic_cost = 512 * q0 * q0 + 2 * q0 * r0 + q1;
+
+ linear_cost + quadratic_cost
+}
+
+
+/// Calculates memory expansion based on multiple memory operations.
+///
+/// # Arguments
+///
+/// * `current_size`: Current size of the memory.
+/// * `operations`: A span of tuples (offset, size) representing memory operations.
+///
+/// # Returns
+///
+/// * `MemoryExpansion`: New size and expansion cost.
+pub fn memory_expansion(
+ current_size: usize, mut operations: Span<(usize, usize)>
+) -> Result {
+ let mut current_max_size = current_size;
+
+ // Using a high-level loop because Cairo doesn't support the `for` loop syntax with breaks
+ let max_size = loop {
+ match operations.pop_front() {
+ Option::Some((
+ offset, size
+ )) => {
+ if *size != 0 {
+ match (*offset).checked_add(*size) {
+ Option::Some(end) => {
+ if end > current_max_size {
+ current_max_size = end;
+ }
+ },
+ Option::None => { break Result::Err(EVMError::MemoryLimitOOG); },
+ }
+ }
+ },
+ Option::None => { break Result::Ok((current_max_size)); },
+ }
+ }?;
+
+ let new_size = helpers::bytes_32_words_size(max_size) * 32;
+
+ if new_size <= current_size {
+ return Result::Ok(MemoryExpansion { new_size: current_size, expansion_cost: 0 });
+ }
+
+ let prev_cost = calculate_memory_gas_cost(current_size);
+ let new_cost = calculate_memory_gas_cost(new_size);
+ let expansion_cost = new_cost - prev_cost;
+ Result::Ok(MemoryExpansion { new_size, expansion_cost })
+}
+
+/// Calculates the gas to be charged for the init code in CREATE/CREATE2
+/// opcodes as well as create transactions.
+///
+/// # Arguments
+///
+/// * `code_size` - The size of the init code
+///
+/// # Returns
+///
+/// * `init_code_gas` - The gas to be charged for the init code.
+#[inline(always)]
+pub fn init_code_cost(code_size: usize) -> u64 {
+ let code_size_in_words = helpers::bytes_32_words_size(code_size);
+ code_size_in_words.into() * INITCODE_WORD_COST
+}
+
+/// Calculates the gas that is charged before execution is started.
+///
+/// The intrinsic cost of the transaction is charged before execution has
+/// begun. Functions/operations in the EVM cost money to execute so this
+/// intrinsic cost is for the operations that need to be paid for as part of
+/// the transaction. Data transfer, for example, is part of this intrinsic
+/// cost. It costs ether to send data over the wire and that ether is
+/// accounted for in the intrinsic cost calculated in this function. This
+/// intrinsic cost must be calculated and paid for before execution in order
+/// for all operations to be implemented.
+///
+/// Reference:
+/// https://github.com/ethereum/execution-specs/blob/master/src/ethereum/shanghai/fork.py#L689
+pub fn calculate_intrinsic_gas_cost(tx: @Transaction) -> u64 {
+ let mut data_cost: u64 = 0;
+
+ let mut calldata = tx.input();
+ let calldata_len: usize = calldata.len();
+
+ for data in calldata {
+ data_cost +=
+ if *data == 0 {
+ TRANSACTION_ZERO_DATA
+ } else {
+ TRANSACTION_NON_ZERO_DATA_INIT
+ };
+ };
+
+ let create_cost: u64 = if tx.kind().is_create() {
+ TRANSACTION_CREATE_COST + init_code_cost(calldata_len)
+ } else {
+ 0
+ };
+
+ let access_list_cost = if let Option::Some(mut access_list) = tx.access_list() {
+ let mut access_list_cost: u64 = 0;
+ for access_list_item in access_list {
+ let AccessListItem { ethereum_address: _, storage_keys } = *access_list_item;
+ access_list_cost += ACCESS_LIST_ADDRESS
+ + (ACCESS_LIST_STORAGE_KEY * storage_keys.len().into());
+ };
+ access_list_cost
+ } else {
+ 0
+ };
+
+ TRANSACTION_BASE_COST + data_cost + create_cost + access_list_cost
+}
+
+#[cfg(test)]
+mod tests {
+ use core::starknet::EthAddress;
+
+ use crate::gas::{
+ calculate_intrinsic_gas_cost, calculate_memory_gas_cost, ACCESS_LIST_ADDRESS,
+ ACCESS_LIST_STORAGE_KEY
+ };
+ use crate::test_utils::evm_address;
+ use utils::eth_transaction::eip2930::{AccessListItem, TxEip2930};
+ use utils::eth_transaction::legacy::TxLegacy;
+ use utils::eth_transaction::transaction::Transaction;
+ use utils::traits::bytes::ToBytes;
+
+ #[test]
+ fn test_calculate_intrinsic_gas_cost() {
+ // RLP decoded value: (https://toolkit.abdk.consulting/ethereum#rlp,transaction)
+ // ["0xc9", "0x81", "0xf7", "0x81", "0x80", "0x81", "0x84", "0x00", "0x00", "0x12"]
+ // 16 16 16 16 16 16 16 4 4 16
+ // + 21000
+ // + 0
+ // ---------------------------
+ // = 21136
+ let rlp_encoded: u256 = 0xc981f781808184000012;
+
+ let input = rlp_encoded.to_be_bytes();
+ let to: EthAddress = 'vitalik.eth'.try_into().unwrap();
+
+ let tx: Transaction = Transaction::Legacy(
+ TxLegacy {
+ to: to.into(),
+ nonce: 0,
+ gas_price: 50,
+ gas_limit: 433926,
+ value: 1,
+ input,
+ chain_id: Option::Some(0x1)
+ }
+ );
+
+ let expected_cost: u64 = 21136;
+ let out_cost: u64 = calculate_intrinsic_gas_cost(@tx);
+
+ assert_eq!(out_cost, expected_cost, "wrong cost");
+ }
+
+ #[test]
+ fn test_calculate_intrinsic_gas_cost_with_access_list() {
+ // RLP decoded value: (https://toolkit.abdk.consulting/ethereum#rlp,transaction)
+ // ["0xc9", "0x81", "0xf7", "0x81", "0x80", "0x81", "0x84", "0x00", "0x00", "0x12"]
+ // 16 16 16 16 16 16 16 4 4 16
+ // + 21000
+ // + 0
+ // ---------------------------
+ // = 21136
+ let rlp_encoded: u256 = 0xc981f781808184000012;
+
+ let input = rlp_encoded.to_be_bytes();
+ let to: EthAddress = 'vitalik.eth'.try_into().unwrap();
+
+ let access_list = [
+ AccessListItem { ethereum_address: evm_address(), storage_keys: [1, 2, 3, 4, 5].span() }
+ ].span();
+
+ let tx: Transaction = Transaction::Eip2930(
+ TxEip2930 {
+ to: to.into(),
+ nonce: 0,
+ gas_price: 50,
+ gas_limit: 433926,
+ value: 1,
+ input,
+ chain_id: 0x1,
+ access_list
+ }
+ );
+
+ let expected_cost: u64 = 21136 + ACCESS_LIST_ADDRESS + 5 * ACCESS_LIST_STORAGE_KEY;
+ let out_cost: u64 = calculate_intrinsic_gas_cost(@tx);
+
+ assert_eq!(out_cost, expected_cost, "wrong cost");
+ }
+
+
+ #[test]
+ fn test_calculate_intrinsic_gas_cost_without_destination() {
+ // RLP decoded value: (https://toolkit.abdk.consulting/ethereum#rlp,transaction)
+ // ["0xc9", "0x81", "0xf7", "0x81", "0x80", "0x81", "0x84", "0x00", "0x00", "0x12"]
+ // 16 16 16 16 16 16 16 4 4 16
+ // + 21000
+ // + (32000 + 2)
+ // ---------------------------
+ // = 53138
+ let rlp_encoded: u256 = 0xc981f781808184000012;
+
+ let input = rlp_encoded.to_be_bytes();
+
+ let tx: Transaction = Transaction::Legacy(
+ TxLegacy {
+ to: Option::None.into(),
+ nonce: 0,
+ gas_price: 50,
+ gas_limit: 433926,
+ value: 1,
+ input,
+ chain_id: Option::Some(0x1)
+ }
+ );
+
+ let expected_cost: u64 = 53138;
+ let out_cost: u64 = calculate_intrinsic_gas_cost(@tx);
+
+ assert_eq!(out_cost, expected_cost, "wrong cost");
+ }
+
+ #[test]
+ fn test_calculate_memory_allocation_cost() {
+ let size_in_bytes: usize = 10018613;
+ let expected_cost: u64 = 192385220;
+ let out_cost: u64 = calculate_memory_gas_cost(size_in_bytes);
+ assert_eq!(out_cost, expected_cost, "wrong cost");
+ }
+}
diff --git a/cairo/kakarot-ssj/crates/evm/src/instructions.cairo b/cairo/kakarot-ssj/crates/evm/src/instructions.cairo
new file mode 100644
index 000000000..e632d1169
--- /dev/null
+++ b/cairo/kakarot-ssj/crates/evm/src/instructions.cairo
@@ -0,0 +1,34 @@
+/// Sub modules.
+mod block_information;
+
+mod comparison_operations;
+
+mod duplication_operations;
+
+mod environmental_information;
+
+mod exchange_operations;
+
+mod logging_operations;
+
+mod memory_operations;
+
+mod push_operations;
+
+mod sha3;
+
+mod stop_and_arithmetic_operations;
+
+mod system_operations;
+
+pub use block_information::BlockInformationTrait;
+pub use comparison_operations::ComparisonAndBitwiseOperationsTrait;
+pub use duplication_operations::DuplicationOperationsTrait;
+pub use environmental_information::EnvironmentInformationTrait;
+pub use exchange_operations::ExchangeOperationsTrait;
+pub use logging_operations::LoggingOperationsTrait;
+pub use memory_operations::MemoryOperationTrait;
+pub use push_operations::PushOperationsTrait;
+pub use sha3::Sha3Trait;
+pub use stop_and_arithmetic_operations::StopAndArithmeticOperationsTrait;
+pub use system_operations::SystemOperationsTrait;
diff --git a/cairo/kakarot-ssj/crates/evm/src/instructions/block_information.cairo b/cairo/kakarot-ssj/crates/evm/src/instructions/block_information.cairo
new file mode 100644
index 000000000..3f834a917
--- /dev/null
+++ b/cairo/kakarot-ssj/crates/evm/src/instructions/block_information.cairo
@@ -0,0 +1,372 @@
+//! Block Information.
+
+use core::num::traits::SaturatingAdd;
+use core::starknet::SyscallResultTrait;
+use core::starknet::syscalls::get_block_hash_syscall;
+
+use crate::errors::EVMError;
+
+use crate::gas;
+use crate::model::vm::{VM, VMTrait};
+use crate::stack::StackTrait;
+use crate::state::StateTrait;
+use utils::constants::MIN_BASE_FEE_PER_BLOB_GAS;
+use utils::traits::{EthAddressTryIntoResultContractAddress, EthAddressIntoU256};
+
+#[generate_trait]
+pub impl BlockInformation of BlockInformationTrait {
+ /// 0x40 - BLOCKHASH
+ /// Get the hash of one of the 256 most recent complete blocks.
+ /// # Specification: https://www.evm.codes/#40?fork=shanghai
+ fn exec_blockhash(ref self: VM) -> Result<(), EVMError> {
+ self.charge_gas(gas::BLOCKHASH)?;
+
+ // Saturate to MAX_U64 to avoid a revert when the hash requested is too big. It should just
+ // push 0.
+ let block_number = self.stack.pop_saturating_u64()?;
+ let current_block = self.env.block_number;
+
+ // If input block number is lower than current_block - 256, return 0
+ // If input block number is higher than current_block - 10, return 0
+ // Note: in the specs, input block number can be equal - at most - to the current block
+ // number minus one.
+ // In Starknet, the `get_block_hash_syscall` is capped at current block minus ten.
+ // TODO: monitor the changes in the `get_block_hash_syscall` syscall.
+ // source:
+ // https://docs.starknet.io/documentation/architecture_and_concepts/Smart_Contracts/system-calls-cairo1/#get_block_hash
+ if block_number.saturating_add(10) > current_block
+ || block_number.saturating_add(256) < current_block {
+ return self.stack.push(0);
+ }
+
+ self.stack.push(get_block_hash_syscall(block_number).unwrap_syscall().into())
+ }
+
+ /// 0x41 - COINBASE
+ /// Get the block's beneficiary address.
+ /// # Specification: https://www.evm.codes/#41?fork=shanghai
+ fn exec_coinbase(ref self: VM) -> Result<(), EVMError> {
+ self.charge_gas(gas::BASE)?;
+
+ let coinbase = self.env.coinbase;
+ self.stack.push(coinbase.into())
+ }
+
+ /// 0x42 - TIMESTAMP
+ /// Get the blockβs timestamp
+ /// # Specification: https://www.evm.codes/#42?fork=shanghai
+ fn exec_timestamp(ref self: VM) -> Result<(), EVMError> {
+ self.charge_gas(gas::BASE)?;
+
+ self.stack.push(self.env.block_timestamp.into())
+ }
+
+ /// 0x43 - NUMBER
+ /// Get the block number.
+ /// # Specification: https://www.evm.codes/#43?fork=shanghai
+ fn exec_number(ref self: VM) -> Result<(), EVMError> {
+ self.charge_gas(gas::BASE)?;
+
+ self.stack.push(self.env.block_number.into())
+ }
+
+ /// 0x44 - PREVRANDAO
+ /// # Specification: https://www.evm.codes/#44?fork=shanghai
+ fn exec_prevrandao(ref self: VM) -> Result<(), EVMError> {
+ self.charge_gas(gas::BASE)?;
+
+ self.stack.push(self.env.prevrandao)
+ }
+
+ /// 0x45 - GASLIMIT
+ /// Get the blockβs gas limit
+ /// # Specification: https://www.evm.codes/#45?fork=shanghai
+ fn exec_gaslimit(ref self: VM) -> Result<(), EVMError> {
+ self.charge_gas(gas::BASE)?;
+
+ self.stack.push(self.env.block_gas_limit.into())
+ }
+
+ /// 0x46 - CHAINID
+ /// Get the chain ID.
+ /// # Specification: https://www.evm.codes/#46?fork=shanghai
+ fn exec_chainid(ref self: VM) -> Result<(), EVMError> {
+ self.charge_gas(gas::BASE)?;
+
+ let chain_id = self.env.chain_id;
+ self.stack.push(chain_id.into())
+ }
+
+ /// 0x47 - SELFBALANCE
+ /// Get balance of currently executing contract
+ /// # Specification: https://www.evm.codes/#47?fork=shanghai
+ fn exec_selfbalance(ref self: VM) -> Result<(), EVMError> {
+ self.charge_gas(gas::LOW)?;
+
+ let evm_address = self.message().target.evm;
+
+ let balance = self.env.state.get_account(evm_address).balance;
+
+ self.stack.push(balance)
+ }
+
+ /// 0x48 - BASEFEE
+ /// Get base fee.
+ /// # Specification: https://www.evm.codes/#48?fork=shanghai
+ fn exec_basefee(ref self: VM) -> Result<(), EVMError> {
+ self.charge_gas(gas::BASE)?;
+
+ self.stack.push(self.env.base_fee.into())
+ }
+
+ /// 0x49 - BLOBHASH
+ /// Returns the value of the blob hash of the current block
+ /// Always returns Zero in the context of Kakarot
+ /// # Specification: https://www.evm.codes/#49?fork=cancun
+ fn exec_blobhash(ref self: VM) -> Result<(), EVMError> {
+ self.charge_gas(gas::BLOB_HASH_COST)?;
+
+ self.stack.push(0)
+ }
+
+ /// 0x4A - BLOBBASEFEE
+ /// Returns the value of the blob base-fee of the current block
+ /// Always returns Zero in the context of Kakarot
+ /// # Specification: https://www.evm.codes/#4a?fork=cancun
+ fn exec_blobbasefee(ref self: VM) -> Result<(), EVMError> {
+ self.charge_gas(gas::BASE)?;
+
+ self.stack.push(MIN_BASE_FEE_PER_BLOB_GAS.into())
+ }
+}
+
+
+#[cfg(test)]
+mod tests {
+ use core::result::ResultTrait;
+ use crate::instructions::BlockInformationTrait;
+ use crate::model::account::Account;
+ use crate::model::vm::VMTrait;
+ use crate::stack::StackTrait;
+ use crate::state::StateTrait;
+ use crate::test_utils::{VMBuilderTrait, gas_price, setup_test_environment};
+ use snforge_std::{start_cheat_block_number_global, start_cheat_block_timestamp_global};
+ use utils::constants::EMPTY_KECCAK;
+ use utils::constants;
+ use utils::traits::{EthAddressIntoU256};
+
+
+ /// 0x40 - BLOCKHASH
+ #[test]
+ fn test_exec_blockhash_below_bounds() {
+ // Given
+ let mut vm = VMBuilderTrait::new_with_presets().build();
+
+ start_cheat_block_number_global(500);
+
+ // When
+ vm.stack.push(243).expect('push failed');
+ vm.exec_blockhash().unwrap();
+
+ // Then
+ assert(vm.stack.peek().unwrap() == 0, 'stack top should be 0');
+ }
+
+ #[test]
+ fn test_exec_blockhash_above_bounds() {
+ // Given
+ let mut vm = VMBuilderTrait::new_with_presets().build();
+
+ start_cheat_block_number_global(500);
+
+ // When
+ vm.stack.push(491).expect('push failed');
+ vm.exec_blockhash().unwrap();
+
+ // Then
+ assert(vm.stack.peek().unwrap() == 0, 'stack top should be 0');
+ }
+
+ // TODO: implement exec_blockhash testing for block number within bounds
+ //TODO(sn-foundry): mock the block hash
+ // https://github.com/starkware-libs/cairo/blob/77a7e7bc36aa1c317bb8dd5f6f7a7e6eef0ab4f3/crates/cairo-lang-starknet/cairo_level_tests/interoperability.cairo#L173
+ #[test]
+ #[ignore]
+ fn test_exec_blockhash_within_bounds() {
+ // If not set the default block number is 0.
+ let queried_block = 244;
+ start_cheat_block_number_global(500);
+ //TODO: restore start_cheat_block_hash_global(queried_block, 0xF);
+
+ // Given
+ let mut vm = VMBuilderTrait::new_with_presets().build();
+
+ // When
+ vm.stack.push(queried_block.into()).expect('push failed');
+ vm.exec_blockhash().expect('exec failed');
+ //TODO the CASM runner used in tests doesn't implement
+ //`get_block_hash_syscall` yet. As such, this test should fail no if the
+ //queried block is within bounds
+ // Then
+ assert(vm.stack.peek().unwrap() == 0xF, 'stack top should be 0xF');
+ }
+
+
+ #[test]
+ fn test_block_timestamp_set_to_1692873993() {
+ // 24/08/2023 12h46 33s
+ // If not set the default timestamp is 0.
+ start_cheat_block_timestamp_global(1692873993);
+
+ // Given
+ let mut vm = VMBuilderTrait::new_with_presets().build();
+
+ // When
+ vm.exec_timestamp().unwrap();
+
+ // Then
+ assert(vm.stack.len() == 1, 'stack should have one element');
+ assert(vm.stack.peek().unwrap() == 1692873993, 'stack top should be 1692873993');
+ }
+
+ #[test]
+ fn test_block_number_set_to_32() {
+ // If not set the default block number is 0.
+ start_cheat_block_number_global(32);
+
+ // Given
+ let mut vm = VMBuilderTrait::new_with_presets().build();
+
+ // When
+ vm.exec_number().unwrap();
+
+ // Then
+ assert(vm.stack.len() == 1, 'stack should have one element');
+ assert(vm.stack.peek().unwrap() == 32, 'stack top should be 32');
+ }
+
+ #[test]
+ fn test_gaslimit() {
+ // Given
+ let mut vm = VMBuilderTrait::new_with_presets().build();
+
+ // When
+ vm.exec_gaslimit().unwrap();
+
+ // Then
+ assert(vm.stack.len() == 1, 'stack should have one element');
+ // This value is set in [new_with_presets].
+ assert_eq!(vm.stack.peek().unwrap(), constants::BLOCK_GAS_LIMIT.into())
+ }
+
+ // *************************************************************************
+ // 0x47: SELFBALANCE
+ // *************************************************************************
+ #[test]
+ fn test_exec_selfbalance_should_push_balance() {
+ // Given
+ setup_test_environment();
+ let mut vm = VMBuilderTrait::new_with_presets().build();
+ let account = Account {
+ address: vm.message().target,
+ balance: 400,
+ nonce: 0,
+ code: [].span(),
+ code_hash: EMPTY_KECCAK,
+ selfdestruct: false,
+ is_created: true,
+ };
+ vm.env.state.set_account(account);
+
+ // When
+ vm.exec_selfbalance().unwrap();
+
+ // Then
+ assert_eq!(vm.stack.peek().unwrap(), 400);
+ }
+
+
+ #[test]
+ fn test_basefee_should_push_env_base_fee() {
+ // Given
+ let mut vm = VMBuilderTrait::new_with_presets().build();
+
+ // When
+ vm.exec_basefee().unwrap();
+
+ // Then
+ assert_eq!(vm.stack.len(), 1);
+ assert_eq!(vm.stack.peek().unwrap(), vm.env.base_fee.into());
+ }
+
+ #[test]
+ fn test_chainid_should_push_chain_id_to_stack() {
+ // Given
+ let mut vm = VMBuilderTrait::new_with_presets().build();
+
+ // When
+ vm.exec_chainid().unwrap();
+
+ // Then
+ let chain_id = vm.stack.peek().unwrap();
+ assert(vm.env.chain_id.into() == chain_id, 'stack should have chain id');
+ }
+
+
+ #[test]
+ fn test_randao_should_push_zero_to_stack() {
+ // Given
+ let mut vm = VMBuilderTrait::new_with_presets().build();
+
+ // When
+ vm.exec_prevrandao().unwrap();
+
+ // Then
+ let result = vm.stack.peek().unwrap();
+ assert(result == 0x00, 'stack top should be zero');
+ }
+
+ #[test]
+ fn test_blobhash_should_return_zero() {
+ // Given
+ let mut vm = VMBuilderTrait::new_with_presets().build();
+
+ // When
+ vm.exec_blobhash().unwrap();
+
+ // Then
+ assert_eq!(vm.stack.len(), 1);
+ assert_eq!(vm.stack.peek().unwrap(), 0);
+ }
+
+
+ #[test]
+ fn test_blobbasefee_should_return_one() {
+ // Given
+ let mut vm = VMBuilderTrait::new_with_presets().build();
+
+ // When
+ vm.exec_blobbasefee().unwrap();
+
+ // Then
+ assert_eq!(vm.stack.len(), 1);
+ assert_eq!(vm.stack.peek().unwrap(), 1);
+ }
+
+
+ // *************************************************************************
+ // 0x41: COINBASE
+ // *************************************************************************
+ #[test]
+ fn test_exec_coinbase() {
+ // Given
+ let mut vm = VMBuilderTrait::new_with_presets().build();
+
+ // When
+ vm.exec_coinbase().unwrap();
+
+ // Then
+ let coinbase_address = vm.stack.peek().unwrap();
+ assert(vm.env.coinbase.into() == coinbase_address, 'wrong coinbase address');
+ }
+}
diff --git a/cairo/kakarot-ssj/crates/evm/src/instructions/comparison_operations.cairo b/cairo/kakarot-ssj/crates/evm/src/instructions/comparison_operations.cairo
new file mode 100644
index 000000000..b040f21b0
--- /dev/null
+++ b/cairo/kakarot-ssj/crates/evm/src/instructions/comparison_operations.cairo
@@ -0,0 +1,1987 @@
+use core::num::traits::Bounded;
+use crate::errors::EVMError;
+use crate::gas;
+use crate::model::vm::{VM, VMTrait};
+// Internal imports
+use crate::stack::StackTrait;
+use utils::constants::{POW_2_127};
+use utils::i256::i256;
+use utils::math::{Bitshift, WrappingBitshift};
+use utils::traits::BoolIntoNumeric;
+
+#[generate_trait]
+pub impl ComparisonAndBitwiseOperations of ComparisonAndBitwiseOperationsTrait {
+ /// 0x10 - LT
+ /// # Specification: https://www.evm.codes/#10?fork=shanghai
+ fn exec_lt(ref self: VM) -> Result<(), EVMError> {
+ self.charge_gas(gas::VERYLOW)?;
+ let popped = self.stack.pop_n(2)?;
+ let a = *popped[0];
+ let b = *popped[1];
+ let result = (a < b).into();
+ self.stack.push(result)
+ }
+
+ /// 0x11 - GT
+ /// # Specification: https://www.evm.codes/#11?fork=shanghai
+ fn exec_gt(ref self: VM) -> Result<(), EVMError> {
+ self.charge_gas(gas::VERYLOW)?;
+ let popped = self.stack.pop_n(2)?;
+ let a = *popped[0];
+ let b = *popped[1];
+ let result = (a > b).into();
+ self.stack.push(result)
+ }
+
+
+ /// 0x12 - SLT
+ /// # Specification: https://www.evm.codes/#12?fork=shanghai
+ fn exec_slt(ref self: VM) -> Result<(), EVMError> {
+ self.charge_gas(gas::VERYLOW)?;
+ let a: i256 = self.stack.pop_i256()?;
+ let b: i256 = self.stack.pop_i256()?;
+ let result: u256 = (a < b).into();
+ self.stack.push(result)
+ }
+
+ /// 0x13 - SGT
+ /// # Specification: https://www.evm.codes/#13?fork=shanghai
+ fn exec_sgt(ref self: VM) -> Result<(), EVMError> {
+ self.charge_gas(gas::VERYLOW)?;
+ let a: i256 = self.stack.pop_i256()?;
+ let b: i256 = self.stack.pop_i256()?;
+ let result: u256 = (a > b).into();
+ self.stack.push(result)
+ }
+
+
+ /// 0x14 - EQ
+ /// # Specification: https://www.evm.codes/#14?fork=shanghai
+ fn exec_eq(ref self: VM) -> Result<(), EVMError> {
+ self.charge_gas(gas::VERYLOW)?;
+ let popped = self.stack.pop_n(2)?;
+ let a = *popped[0];
+ let b = *popped[1];
+ let result = (a == b).into();
+ self.stack.push(result)
+ }
+
+ /// 0x15 - ISZERO
+ /// # Specification: https://www.evm.codes/#15?fork=shanghai
+ fn exec_iszero(ref self: VM) -> Result<(), EVMError> {
+ self.charge_gas(gas::VERYLOW)?;
+ let popped = self.stack.pop()?;
+ let result: u256 = (popped == 0).into();
+ self.stack.push(result)
+ }
+
+ /// 0x16 - AND
+ /// # Specification: https://www.evm.codes/#16?fork=shanghai
+ fn exec_and(ref self: VM) -> Result<(), EVMError> {
+ self.charge_gas(gas::VERYLOW)?;
+ let popped = self.stack.pop_n(2)?;
+ let a = *popped[0];
+ let b = *popped[1];
+ let result = a & b;
+ self.stack.push(result)
+ }
+
+ /// 0x17 - OR
+ /// # Specification: https://www.evm.codes/#17?fork=shanghai
+ fn exec_or(ref self: VM) -> Result<(), EVMError> {
+ self.charge_gas(gas::VERYLOW)?;
+ let popped = self.stack.pop_n(2)?;
+ let a = *popped[0];
+ let b = *popped[1];
+ let result = a | b;
+ self.stack.push(result)
+ }
+
+ /// 0x18 - XOR operation
+ /// # Specification: https://www.evm.codes/#18?fork=shanghai
+ fn exec_xor(ref self: VM) -> Result<(), EVMError> {
+ self.charge_gas(gas::VERYLOW)?;
+ let popped = self.stack.pop_n(2)?;
+ let a = *popped[0];
+ let b = *popped[1];
+ let result = a ^ b;
+ self.stack.push(result)
+ }
+
+ /// 0x19 - NOT
+ /// Bitwise NOT operation
+ /// # Specification: https://www.evm.codes/#19?fork=shanghai
+ fn exec_not(ref self: VM) -> Result<(), EVMError> {
+ self.charge_gas(gas::VERYLOW)?;
+ let a = self.stack.pop()?;
+ let result = ~a;
+ self.stack.push(result)
+ }
+
+ /// 0x1A - BYTE
+ /// # Specification: https://www.evm.codes/#1a?fork=shanghai
+ /// Retrieve single byte located at the byte offset of value, starting from the most significant
+ /// byte.
+ fn exec_byte(ref self: VM) -> Result<(), EVMError> {
+ self.charge_gas(gas::VERYLOW)?;
+ let popped = self.stack.pop_n(2)?;
+ let i = *popped[0];
+ let x = *popped[1];
+
+ /// If the byte offset is out of range, we early return with 0.
+ if i > 31 {
+ return self.stack.push(0);
+ }
+ let i: usize = i.try_into().unwrap(); // Safe because i <= 31
+
+ // Right shift value by offset bits and then take the least significant byte.
+ let result = x.shr((31 - i) * 8) & 0xFF;
+ self.stack.push(result)
+ }
+
+ /// 0x1B - SHL
+ /// # Specification: https://www.evm.codes/#1b?fork=shanghai
+ fn exec_shl(ref self: VM) -> Result<(), EVMError> {
+ self.charge_gas(gas::VERYLOW)?;
+ let popped = self.stack.pop_n(2)?;
+ let shift = *popped[0];
+ let val = *popped[1];
+
+ // if shift is bigger than 255 return 0
+ if shift > 255 {
+ return self.stack.push(0);
+ }
+ let shift: usize = shift.try_into().unwrap(); // Safe because shift <= 255
+ let result = val.wrapping_shl(shift);
+ self.stack.push(result)
+ }
+
+ /// 0x1C - SHR
+ /// # Specification: https://www.evm.codes/#1c?fork=shanghai
+ fn exec_shr(ref self: VM) -> Result<(), EVMError> {
+ self.charge_gas(gas::VERYLOW)?;
+ let popped = self.stack.pop_n(2)?;
+ let shift = *popped[0];
+ let value = *popped[1];
+
+ // if shift is bigger than 255 return 0
+ if shift > 255 {
+ return self.stack.push(0);
+ }
+ let shift: usize = shift.try_into().unwrap(); // Safe because shift <= 255
+ let result = value.wrapping_shr(shift);
+ self.stack.push(result)
+ }
+
+ /// 0x1D - SAR
+ /// # Specification: https://www.evm.codes/#1d?fork=shanghai
+ fn exec_sar(ref self: VM) -> Result<(), EVMError> {
+ self.charge_gas(gas::VERYLOW)?;
+ let shift: u256 = self.stack.pop()?;
+ let value: i256 = self.stack.pop_i256()?;
+
+ // Checks the MSB bit sign for a 256-bit integer
+ let positive = value.value.high < POW_2_127;
+ let sign = if positive {
+ // If sign is positive, set it to 0.
+ 0
+ } else {
+ // If sign is negative, set the number to -1.
+ Bounded::::MAX
+ };
+
+ if (shift >= 256) {
+ self.stack.push(sign)
+ } else {
+ let shift: usize = shift.try_into().unwrap(); // Safe because shift <= 256
+ // XORing with sign before and after the shift propagates the sign bit of the operation
+ let result = (sign ^ value.value).shr(shift) ^ sign;
+ self.stack.push(result)
+ }
+ }
+}
+
+
+#[cfg(test)]
+mod tests {
+ use core::num::traits::Bounded;
+ use crate::instructions::ComparisonAndBitwiseOperationsTrait;
+ use crate::stack::StackTrait;
+ use crate::test_utils::VMBuilderTrait;
+
+ #[test]
+ fn test_eq_same_pair() {
+ // Given
+ let mut vm = VMBuilderTrait::new_with_presets().build();
+ vm
+ .stack
+ .push(0xFEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210)
+ .expect('push failed');
+ vm
+ .stack
+ .push(0xFEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210)
+ .expect('push failed');
+
+ // When
+ vm.exec_eq().expect('exec_eq failed');
+
+ // Then
+ assert(vm.stack.len() == 1, 'stack should have one element');
+ assert(vm.stack.peek().unwrap() == 0x01, 'stack top should be 0x01');
+ }
+
+ #[test]
+ fn test_eq_different_pair() {
+ // Given
+ let mut vm = VMBuilderTrait::new_with_presets().build();
+ vm
+ .stack
+ .push(0xAB8765432DCBA98765410F149E87610FDCBA98765432543217654DCBA93210F8)
+ .expect('push failed');
+ vm
+ .stack
+ .push(0xFEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210)
+ .expect('push failed');
+
+ // When
+ vm.exec_eq().expect('exec_eq failed');
+
+ // Then
+ assert(vm.stack.len() == 1, 'stack should have one element');
+ assert(vm.stack.peek().unwrap() == 0x00, 'stack top should be 0x00');
+ }
+
+ #[test]
+ fn test_and_zero_and_max() {
+ // Given
+ let mut vm = VMBuilderTrait::new_with_presets().build();
+ vm.stack.push(0x00).expect('push failed');
+ vm.stack.push(Bounded::::MAX).unwrap();
+
+ // When
+ vm.exec_and().expect('exec_and failed');
+
+ // Then
+ assert(vm.stack.len() == 1, 'stack should have one element');
+ assert(vm.stack.peek().unwrap() == 0x00, 'stack top should be 0x00');
+ }
+
+ #[test]
+ fn test_and_max_and_max() {
+ // Given
+ let mut vm = VMBuilderTrait::new_with_presets().build();
+ vm.stack.push(Bounded::::MAX).unwrap();
+ vm.stack.push(Bounded::::MAX).unwrap();
+
+ // When
+ vm.exec_and().expect('exec_and failed');
+
+ // Then
+ assert(vm.stack.len() == 1, 'stack should have one element');
+ assert(vm.stack.peek().unwrap() == Bounded::::MAX, 'stack top should be 0xFF...FFF');
+ }
+
+ #[test]
+ fn test_and_two_random_uint() {
+ // Given
+ let mut vm = VMBuilderTrait::new_with_presets().build();
+ vm
+ .stack
+ .push(0xAB8765432DCBA98765410F149E87610FDCBA98765432543217654DCBA93210F8)
+ .expect('push failed');
+ vm
+ .stack
+ .push(0xFEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210)
+ .expect('push failed');
+
+ // When
+ vm.exec_and().expect('exec_and failed');
+
+ // Then
+ assert(vm.stack.len() == 1, 'stack should have one element');
+ assert(
+ vm
+ .stack
+ .peek()
+ .unwrap() == 0xAA8420002440200064400A1016042000DC989810541010101644088820101010,
+ 'stack top is wrong'
+ );
+ }
+
+
+ #[test]
+ fn test_xor_different_pair() {
+ // Given
+ let mut vm = VMBuilderTrait::new_with_presets().build();
+ vm.stack.push(0b010101).expect('push failed');
+ vm.stack.push(0b101010).expect('push failed');
+
+ // When
+ vm.exec_xor().expect('exec_xor failed');
+
+ // Then
+ assert(vm.stack.len() == 1, 'stack should have one element');
+ assert(vm.stack.peek().unwrap() == 0b111111, 'stack top should be 0xFF');
+ }
+
+ #[test]
+ fn test_xor_same_pair() {
+ // Given
+ let mut vm = VMBuilderTrait::new_with_presets().build();
+ vm.stack.push(0b000111).expect('push failed');
+ vm.stack.push(0b000111).expect('push failed');
+
+ // When
+ vm.exec_xor().expect('exec_xor failed');
+
+ // Then
+ assert(vm.stack.len() == 1, 'stack should have one element');
+ assert(vm.stack.peek().unwrap() == 0x00, 'stack top should be 0x00');
+ }
+
+ #[test]
+ fn test_xor_half_same_pair() {
+ // Given
+ let mut vm = VMBuilderTrait::new_with_presets().build();
+ vm.stack.push(0b111000).expect('push failed');
+ vm.stack.push(0b000000).expect('push failed');
+
+ // When
+ vm.exec_xor().expect('exec_xor failed');
+
+ // Then
+ assert(vm.stack.len() == 1, 'stack should have one element');
+ assert(vm.stack.peek().unwrap() == 0b111000, 'stack top should be 0xFF');
+ }
+
+
+ #[test]
+ fn test_not_zero() {
+ // Given
+ let mut vm = VMBuilderTrait::new_with_presets().build();
+ vm.stack.push(0x00).expect('push failed');
+
+ // When
+ vm.exec_not().expect('exec_not failed');
+
+ // Then
+ assert(vm.stack.len() == 1, 'stack should have one element');
+ assert(vm.stack.peek().unwrap() == Bounded::::MAX, 'stack top should be 0xFFF..FFFF');
+ }
+
+ #[test]
+ fn test_not_max_uint() {
+ // Given
+ let mut vm = VMBuilderTrait::new_with_presets().build();
+ vm.stack.push(Bounded::::MAX).unwrap();
+
+ // When
+ vm.exec_not().expect('exec_not failed');
+
+ // Then
+ assert(vm.stack.len() == 1, 'stack should have one element');
+ assert(vm.stack.peek().unwrap() == 0x00, 'stack top should be 0x00');
+ }
+
+ #[test]
+ fn test_not_random_uint() {
+ // Given
+ let mut vm = VMBuilderTrait::new_with_presets().build();
+ vm
+ .stack
+ .push(0x123456789ABCDEF123456789ABCDEF123456789ABCDEF123456789ABCDEF1234)
+ .expect('push failed');
+
+ // When
+ vm.exec_not().expect('exec_not failed');
+
+ // Then
+ assert(vm.stack.len() == 1, 'stack should have one element');
+ assert(
+ vm
+ .stack
+ .peek()
+ .unwrap() == 0xEDCBA9876543210EDCBA9876543210EDCBA9876543210EDCBA9876543210EDCB,
+ 'stack top should be 0x7553'
+ );
+ }
+
+ #[test]
+ fn test_is_zero_true() {
+ // Given
+ let mut vm = VMBuilderTrait::new_with_presets().build();
+ vm.stack.push(0x00).expect('push failed');
+
+ // When
+ vm.exec_iszero().expect('exec_iszero failed');
+
+ // Then
+ assert(vm.stack.len() == 1, 'stack should have one element');
+ assert(vm.stack.peek().unwrap() == 0x01, 'stack top should be true');
+ }
+
+ #[test]
+ fn test_is_zero_false() {
+ // Given
+ let mut vm = VMBuilderTrait::new_with_presets().build();
+ vm.stack.push(0x01).expect('push failed');
+
+ // When
+ vm.exec_iszero().expect('exec_iszero failed');
+
+ // Then
+ assert(vm.stack.len() == 1, 'stack should have one element');
+ assert(vm.stack.peek().unwrap() == 0x00, 'stack top should be false');
+ }
+
+ #[test]
+ fn test_byte_random_u256() {
+ // Given
+ let mut vm = VMBuilderTrait::new_with_presets().build();
+ vm
+ .stack
+ .push(0xf7ec8b2ea4a6b7fd5f4ed41b66197fcc14c4a37d68275ea151d899bb4d7c2ae7)
+ .expect('push failed');
+ vm.stack.push(0x08).expect('push failed');
+
+ // When
+ vm.exec_byte().expect('exec_byte failed');
+
+ // Then
+ assert(vm.stack.len() == 1, 'stack should have one element');
+ assert(vm.stack.peek().unwrap() == 0x5f, 'stack top should be 0x22');
+ }
+
+ #[test]
+ fn test_byte_offset_out_of_range() {
+ // Given
+ let mut vm = VMBuilderTrait::new_with_presets().build();
+ vm
+ .stack
+ .push(0x01be893aefcfa1592f60622b80d45c2db74281d2b9e10c14b0f6ce7c8f58e209)
+ .expect('push failed');
+ vm.stack.push(32_u256).expect('push failed');
+
+ // When
+ vm.exec_byte().expect('exec_byte failed');
+
+ // Then
+ assert(vm.stack.len() == 1, 'stack should have one element');
+ assert(vm.stack.peek().unwrap() == 0x00, 'stack top should be 0x00');
+ }
+
+ #[test]
+ fn test_exec_gt_true() {
+ // Given
+ let mut vm = VMBuilderTrait::new_with_presets().build();
+ vm.stack.push(9_u256).expect('push failed');
+ vm.stack.push(10_u256).expect('push failed');
+
+ // When
+ vm.exec_gt().expect('exec_gt failed');
+
+ // Then
+ assert(vm.stack.len() == 1, 'stack should have one element');
+ assert(vm.stack.peek().unwrap() == 1, 'stack top should be 1');
+ }
+
+ #[test]
+ fn test_exec_shl() {
+ // Given
+ let mut vm = VMBuilderTrait::new_with_presets().build();
+ vm
+ .stack
+ .push(0xff00000000000000000000000000000000000000000000000000000000000000)
+ .expect('push failed');
+ vm.stack.push(4_u256).expect('push failed');
+
+ // When
+ vm.exec_shl().expect('exec_shl failed');
+
+ // Then
+ assert(vm.stack.len() == 1, 'stack should have one element');
+ assert(
+ vm
+ .stack
+ .peek()
+ .unwrap() == 0xf000000000000000000000000000000000000000000000000000000000000000,
+ 'stack top should be 0xf00000...'
+ );
+ }
+
+ #[test]
+ fn test_exec_shl_wrapping() {
+ // Given
+ let mut vm = VMBuilderTrait::new_with_presets().build();
+ vm
+ .stack
+ .push(0xff00000000000000000000000000000000000000000000000000000000000000)
+ .expect('push failed');
+ vm.stack.push(256_u256).expect('push failed');
+
+ // When
+ vm.exec_shl().expect('exec_shl failed');
+
+ // Then
+ assert(vm.stack.len() == 1, 'stack should have one element');
+ assert(vm.stack.peek().unwrap() == 0, 'if shift > 255 should return 0');
+ }
+
+ #[test]
+ fn test_exec_gt_false() {
+ // Given
+ let mut vm = VMBuilderTrait::new_with_presets().build();
+ vm.stack.push(10_u256).expect('push failed');
+ vm.stack.push(9_u256).expect('push failed');
+
+ // When
+ vm.exec_gt().expect('exec_gt failed');
+
+ // Then
+ assert(vm.stack.len() == 1, 'stack should have one element');
+ assert(vm.stack.peek().unwrap() == 0, 'stack top should be 0');
+ }
+
+ #[test]
+ fn test_exec_gt_false_equal() {
+ // Given
+ let mut vm = VMBuilderTrait::new_with_presets().build();
+ vm.stack.push(10_u256).expect('push failed');
+ vm.stack.push(10_u256).expect('push failed');
+
+ // When
+ vm.exec_gt().expect('exec_gt failed');
+
+ // Then
+ assert(vm.stack.len() == 1, 'stack should have one element');
+ assert(vm.stack.peek().unwrap() == 0, 'stack top should be 0');
+ }
+
+ #[test]
+ fn test_exec_slt() {
+ // https://github.com/ethereum/go-ethereum/blob/master/core/vm/testdata/testcases_slt.json
+ assert_slt(0x0, 0x0, 0);
+ assert_slt(0x0, 0x1, 0);
+ assert_slt(0x0, 0x5, 0);
+ assert_slt(0x0, 0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe, 0);
+ assert_slt(0x0, 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff, 0);
+ assert_slt(0x0, 0x8000000000000000000000000000000000000000000000000000000000000000, 1);
+ assert_slt(0x0, 0x8000000000000000000000000000000000000000000000000000000000000001, 1);
+ assert_slt(0x0, 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb, 1);
+ assert_slt(0x0, 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff, 1);
+ assert_slt(0x1, 0x0, 1);
+ assert_slt(0x1, 0x1, 0);
+ assert_slt(0x1, 0x5, 0);
+ assert_slt(0x1, 0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe, 0);
+ assert_slt(0x1, 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff, 0);
+ assert_slt(0x0, 0x8000000000000000000000000000000000000000000000000000000000000000, 1);
+ assert_slt(0x1, 0x8000000000000000000000000000000000000000000000000000000000000001, 1);
+ assert_slt(0x1, 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb, 1);
+ assert_slt(0x1, 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff, 1);
+ assert_slt(0x5, 0x0, 1);
+ assert_slt(0x5, 0x1, 1);
+ assert_slt(0x5, 0x5, 0);
+ assert_slt(0x5, 0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe, 0);
+ assert_slt(0x5, 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff, 0);
+ assert_slt(0x5, 0x8000000000000000000000000000000000000000000000000000000000000000, 1);
+ assert_slt(0x5, 0x8000000000000000000000000000000000000000000000000000000000000001, 1);
+ assert_slt(0x5, 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb, 1);
+ assert_slt(0x5, 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff, 1);
+ assert_slt(0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe, 0x0, 1);
+ assert_slt(0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe, 0x1, 1);
+ assert_slt(0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe, 0x5, 1);
+ assert_slt(
+ 0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe,
+ 0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe,
+ 0
+ );
+ assert_slt(
+ 0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe,
+ 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,
+ 0
+ );
+ assert_slt(
+ 0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe,
+ 0x8000000000000000000000000000000000000000000000000000000000000000,
+ 1
+ );
+ assert_slt(
+ 0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe,
+ 0x8000000000000000000000000000000000000000000000000000000000000001,
+ 1
+ );
+ assert_slt(
+ 0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe,
+ 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb,
+ 1
+ );
+ assert_slt(
+ 0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe,
+ 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,
+ 1
+ );
+ assert_slt(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff, 0x0, 1);
+ assert_slt(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff, 0x1, 1);
+ assert_slt(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff, 0x5, 1);
+ assert_slt(
+ 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,
+ 0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe,
+ 1
+ );
+ assert_slt(
+ 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,
+ 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,
+ 0
+ );
+ assert_slt(
+ 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,
+ 0x8000000000000000000000000000000000000000000000000000000000000000,
+ 1
+ );
+ assert_slt(
+ 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,
+ 0x8000000000000000000000000000000000000000000000000000000000000001,
+ 1
+ );
+ assert_slt(
+ 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,
+ 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb,
+ 1
+ );
+ assert_slt(
+ 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,
+ 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,
+ 1
+ );
+ assert_slt(0x8000000000000000000000000000000000000000000000000000000000000000, 0x0, 0);
+ assert_slt(0x8000000000000000000000000000000000000000000000000000000000000000, 0x1, 0);
+ assert_slt(0x8000000000000000000000000000000000000000000000000000000000000000, 0x5, 0);
+ assert_slt(
+ 0x8000000000000000000000000000000000000000000000000000000000000000,
+ 0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe,
+ 0
+ );
+ assert_slt(
+ 0x8000000000000000000000000000000000000000000000000000000000000000,
+ 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,
+ 0
+ );
+ assert_slt(
+ 0x8000000000000000000000000000000000000000000000000000000000000000,
+ 0x8000000000000000000000000000000000000000000000000000000000000000,
+ 0
+ );
+ assert_slt(
+ 0x8000000000000000000000000000000000000000000000000000000000000000,
+ 0x8000000000000000000000000000000000000000000000000000000000000001,
+ 0
+ );
+ assert_slt(
+ 0x8000000000000000000000000000000000000000000000000000000000000000,
+ 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb,
+ 0
+ );
+ assert_slt(
+ 0x8000000000000000000000000000000000000000000000000000000000000000,
+ 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,
+ 0
+ );
+ assert_slt(0x8000000000000000000000000000000000000000000000000000000000000001, 0x0, 0);
+ assert_slt(0x8000000000000000000000000000000000000000000000000000000000000001, 0x1, 0);
+ assert_slt(0x8000000000000000000000000000000000000000000000000000000000000001, 0x5, 0);
+ assert_slt(
+ 0x8000000000000000000000000000000000000000000000000000000000000001,
+ 0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe,
+ 0
+ );
+ assert_slt(
+ 0x8000000000000000000000000000000000000000000000000000000000000001,
+ 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,
+ 0
+ );
+ assert_slt(
+ 0x8000000000000000000000000000000000000000000000000000000000000001,
+ 0x8000000000000000000000000000000000000000000000000000000000000000,
+ 1
+ );
+ assert_slt(
+ 0x8000000000000000000000000000000000000000000000000000000000000001,
+ 0x8000000000000000000000000000000000000000000000000000000000000001,
+ 0
+ );
+ assert_slt(
+ 0x8000000000000000000000000000000000000000000000000000000000000001,
+ 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb,
+ 0
+ );
+ assert_slt(
+ 0x8000000000000000000000000000000000000000000000000000000000000001,
+ 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,
+ 0
+ );
+ assert_slt(0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb, 0x0, 0);
+ assert_slt(0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb, 0x1, 0);
+ assert_slt(0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb, 0x5, 0);
+ assert_slt(
+ 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb,
+ 0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe,
+ 0
+ );
+ assert_slt(
+ 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb,
+ 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,
+ 0
+ );
+ assert_slt(
+ 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb,
+ 0x8000000000000000000000000000000000000000000000000000000000000000,
+ 1
+ );
+ assert_slt(
+ 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb,
+ 0x8000000000000000000000000000000000000000000000000000000000000001,
+ 1
+ );
+ assert_slt(
+ 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb,
+ 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb,
+ 0
+ );
+ assert_slt(
+ 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb,
+ 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,
+ 0
+ );
+ assert_slt(0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff, 0x0, 0);
+ assert_slt(0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff, 0x1, 0);
+ assert_slt(0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff, 0x5, 0);
+ assert_slt(
+ 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,
+ 0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe,
+ 0
+ );
+ assert_slt(
+ 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,
+ 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,
+ 0
+ );
+ assert_slt(
+ 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,
+ 0x8000000000000000000000000000000000000000000000000000000000000000,
+ 1
+ );
+ assert_slt(
+ 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,
+ 0x8000000000000000000000000000000000000000000000000000000000000001,
+ 1
+ );
+ assert_slt(
+ 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,
+ 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb,
+ 1
+ );
+ assert_slt(
+ 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,
+ 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,
+ 0
+ );
+ }
+
+ fn assert_slt(b: u256, a: u256, expected: u256) {
+ // Given
+ let mut vm = VMBuilderTrait::new_with_presets().build();
+ vm.stack.push(b).expect('push failed');
+ vm.stack.push(a).expect('push failed');
+
+ // When
+ vm.exec_slt().expect('exec_slt failed');
+
+ // Then
+ assert(vm.stack.len() == 1, 'stack should have one element');
+ assert(vm.stack.peek().unwrap() == expected, 'slt failed');
+ }
+
+ #[test]
+ fn test_exec_sgt() {
+ // https://github.com/ethereum/go-ethereum/blob/master/core/vm/testdata/testcases_sgt.json
+ assert_sgt(0x0, 0x0, 0);
+ assert_sgt(0x0, 0x1, 1);
+ assert_sgt(0x0, 0x5, 1);
+ assert_sgt(0x0, 0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe, 1);
+ assert_sgt(0x0, 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff, 1);
+ assert_sgt(0x0, 0x8000000000000000000000000000000000000000000000000000000000000000, 0);
+ assert_sgt(0x0, 0x8000000000000000000000000000000000000000000000000000000000000001, 0);
+ assert_sgt(0x0, 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb, 0);
+ assert_sgt(0x0, 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff, 0);
+ assert_sgt(0x1, 0x0, 0);
+ assert_sgt(0x1, 0x1, 0);
+ assert_sgt(0x1, 0x5, 1);
+ assert_sgt(0x1, 0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe, 1);
+ assert_sgt(0x1, 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff, 1);
+ assert_sgt(0x1, 0x8000000000000000000000000000000000000000000000000000000000000000, 0);
+ assert_sgt(0x1, 0x8000000000000000000000000000000000000000000000000000000000000001, 0);
+ assert_sgt(0x1, 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb, 0);
+ assert_sgt(0x1, 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff, 0);
+ assert_sgt(0x5, 0x0, 0);
+ assert_sgt(0x5, 0x1, 0);
+ assert_sgt(0x5, 0x5, 0);
+ assert_sgt(0x5, 0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe, 1);
+ assert_sgt(0x5, 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff, 1);
+ assert_sgt(0x5, 0x8000000000000000000000000000000000000000000000000000000000000000, 0);
+ assert_sgt(0x5, 0x8000000000000000000000000000000000000000000000000000000000000001, 0);
+ assert_sgt(0x5, 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb, 0);
+ assert_sgt(0x5, 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff, 0);
+ assert_sgt(0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe, 0x0, 0);
+ assert_sgt(0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe, 0x1, 0);
+ assert_sgt(0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe, 0x5, 0);
+ assert_sgt(
+ 0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe,
+ 0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe,
+ 0
+ );
+ assert_sgt(
+ 0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe,
+ 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,
+ 1
+ );
+ assert_sgt(
+ 0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe,
+ 0x8000000000000000000000000000000000000000000000000000000000000000,
+ 0
+ );
+ assert_sgt(
+ 0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe,
+ 0x8000000000000000000000000000000000000000000000000000000000000001,
+ 0
+ );
+ assert_sgt(
+ 0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe,
+ 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb,
+ 0
+ );
+ assert_sgt(
+ 0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe,
+ 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,
+ 0
+ );
+ assert_sgt(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff, 0x0, 0);
+ assert_sgt(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff, 0x1, 0);
+ assert_sgt(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff, 0x5, 0);
+ assert_sgt(
+ 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,
+ 0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe,
+ 0
+ );
+ assert_sgt(
+ 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,
+ 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,
+ 0
+ );
+ assert_sgt(
+ 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,
+ 0x8000000000000000000000000000000000000000000000000000000000000000,
+ 0
+ );
+ assert_sgt(
+ 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,
+ 0x8000000000000000000000000000000000000000000000000000000000000001,
+ 0
+ );
+ assert_sgt(
+ 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,
+ 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb,
+ 0
+ );
+ assert_sgt(
+ 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,
+ 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,
+ 0
+ );
+ assert_sgt(0x8000000000000000000000000000000000000000000000000000000000000000, 0x0, 1);
+ assert_sgt(0x8000000000000000000000000000000000000000000000000000000000000000, 0x1, 1);
+ assert_sgt(0x8000000000000000000000000000000000000000000000000000000000000000, 0x5, 1);
+ assert_sgt(
+ 0x8000000000000000000000000000000000000000000000000000000000000000,
+ 0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe,
+ 1
+ );
+ assert_sgt(
+ 0x8000000000000000000000000000000000000000000000000000000000000000,
+ 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,
+ 1
+ );
+ assert_sgt(
+ 0x8000000000000000000000000000000000000000000000000000000000000000,
+ 0x8000000000000000000000000000000000000000000000000000000000000000,
+ 0
+ );
+ assert_sgt(
+ 0x8000000000000000000000000000000000000000000000000000000000000000,
+ 0x8000000000000000000000000000000000000000000000000000000000000001,
+ 1
+ );
+ assert_sgt(
+ 0x8000000000000000000000000000000000000000000000000000000000000000,
+ 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb,
+ 1
+ );
+ assert_sgt(
+ 0x8000000000000000000000000000000000000000000000000000000000000000,
+ 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,
+ 1
+ );
+ assert_sgt(0x8000000000000000000000000000000000000000000000000000000000000001, 0x0, 1);
+ assert_sgt(0x8000000000000000000000000000000000000000000000000000000000000001, 0x1, 1);
+ assert_sgt(0x8000000000000000000000000000000000000000000000000000000000000001, 0x5, 1);
+ assert_sgt(
+ 0x8000000000000000000000000000000000000000000000000000000000000001,
+ 0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe,
+ 1
+ );
+ assert_sgt(
+ 0x8000000000000000000000000000000000000000000000000000000000000001,
+ 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,
+ 1
+ );
+ assert_sgt(
+ 0x8000000000000000000000000000000000000000000000000000000000000001,
+ 0x8000000000000000000000000000000000000000000000000000000000000000,
+ 0
+ );
+ assert_sgt(
+ 0x8000000000000000000000000000000000000000000000000000000000000001,
+ 0x8000000000000000000000000000000000000000000000000000000000000001,
+ 0
+ );
+ assert_sgt(
+ 0x8000000000000000000000000000000000000000000000000000000000000001,
+ 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb,
+ 1
+ );
+ assert_sgt(
+ 0x8000000000000000000000000000000000000000000000000000000000000001,
+ 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,
+ 1
+ );
+ assert_sgt(0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb, 0x0, 1);
+ assert_sgt(0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb, 0x1, 1);
+ assert_sgt(0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb, 0x5, 1);
+ assert_sgt(
+ 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb,
+ 0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe,
+ 1
+ );
+ assert_sgt(
+ 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb,
+ 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,
+ 1
+ );
+ assert_sgt(
+ 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb,
+ 0x8000000000000000000000000000000000000000000000000000000000000000,
+ 0
+ );
+ assert_sgt(
+ 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb,
+ 0x8000000000000000000000000000000000000000000000000000000000000001,
+ 0
+ );
+ assert_sgt(
+ 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb,
+ 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb,
+ 0
+ );
+ assert_sgt(
+ 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb,
+ 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,
+ 1
+ );
+ assert_sgt(0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff, 0x0, 1);
+ assert_sgt(0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff, 0x1, 1);
+ assert_sgt(0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff, 0x5, 1);
+ assert_sgt(
+ 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,
+ 0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe,
+ 1
+ );
+ assert_sgt(
+ 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,
+ 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,
+ 1
+ );
+ assert_sgt(
+ 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,
+ 0x8000000000000000000000000000000000000000000000000000000000000000,
+ 0
+ );
+ assert_sgt(
+ 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,
+ 0x8000000000000000000000000000000000000000000000000000000000000001,
+ 0
+ );
+ assert_sgt(
+ 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,
+ 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb,
+ 0
+ );
+ assert_sgt(
+ 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,
+ 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,
+ 0
+ );
+ }
+
+ fn assert_sgt(b: u256, a: u256, expected: u256) {
+ // Given
+ let mut vm = VMBuilderTrait::new_with_presets().build();
+ vm.stack.push(b).expect('push failed');
+ vm.stack.push(a).expect('push failed');
+
+ // When
+ vm.exec_sgt().expect('exec_sgt failed');
+
+ // Then
+ assert(vm.stack.len() == 1, 'stack should have one element');
+ assert(vm.stack.peek().unwrap() == expected, 'sgt failed');
+ }
+
+ #[test]
+ fn test_exec_shr() {
+ // https://github.com/ethereum/go-ethereum/blob/master/core/vm/testdata/testcases_shr.json
+ assert_shr(
+ 0x0000000000000000000000000000000000000000000000000000000000000000,
+ 0x0000000000000000000000000000000000000000000000000000000000000000,
+ 0x0000000000000000000000000000000000000000000000000000000000000000
+ );
+ assert_shr(
+ 0x0000000000000000000000000000000000000000000000000000000000000000,
+ 0x0000000000000000000000000000000000000000000000000000000000000001,
+ 0x0000000000000000000000000000000000000000000000000000000000000000
+ );
+ assert_shr(
+ 0x0000000000000000000000000000000000000000000000000000000000000000,
+ 0x0000000000000000000000000000000000000000000000000000000000000005,
+ 0x0000000000000000000000000000000000000000000000000000000000000000
+ );
+ assert_shr(
+ 0x0000000000000000000000000000000000000000000000000000000000000000,
+ 0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe,
+ 0x0000000000000000000000000000000000000000000000000000000000000000
+ );
+ assert_shr(
+ 0x0000000000000000000000000000000000000000000000000000000000000000,
+ 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,
+ 0x0000000000000000000000000000000000000000000000000000000000000000
+ );
+ assert_shr(
+ 0x0000000000000000000000000000000000000000000000000000000000000000,
+ 0x8000000000000000000000000000000000000000000000000000000000000000,
+ 0x0000000000000000000000000000000000000000000000000000000000000000
+ );
+ assert_shr(
+ 0x0000000000000000000000000000000000000000000000000000000000000000,
+ 0x8000000000000000000000000000000000000000000000000000000000000001,
+ 0x0000000000000000000000000000000000000000000000000000000000000000
+ );
+ assert_shr(
+ 0x0000000000000000000000000000000000000000000000000000000000000000,
+ 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb,
+ 0x0000000000000000000000000000000000000000000000000000000000000000
+ );
+ assert_shr(
+ 0x0000000000000000000000000000000000000000000000000000000000000000,
+ 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,
+ 0x0000000000000000000000000000000000000000000000000000000000000000
+ );
+ assert_shr(
+ 0x0000000000000000000000000000000000000000000000000000000000000001,
+ 0x0000000000000000000000000000000000000000000000000000000000000000,
+ 0x0000000000000000000000000000000000000000000000000000000000000001
+ );
+ assert_shr(
+ 0x0000000000000000000000000000000000000000000000000000000000000001,
+ 0x0000000000000000000000000000000000000000000000000000000000000001,
+ 0x0000000000000000000000000000000000000000000000000000000000000000
+ );
+ assert_shr(
+ 0x0000000000000000000000000000000000000000000000000000000000000001,
+ 0x0000000000000000000000000000000000000000000000000000000000000005,
+ 0x0000000000000000000000000000000000000000000000000000000000000000
+ );
+ assert_shr(
+ 0x0000000000000000000000000000000000000000000000000000000000000001,
+ 0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe,
+ 0x0000000000000000000000000000000000000000000000000000000000000000
+ );
+ assert_shr(
+ 0x0000000000000000000000000000000000000000000000000000000000000001,
+ 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,
+ 0x0000000000000000000000000000000000000000000000000000000000000000
+ );
+ assert_shr(
+ 0x0000000000000000000000000000000000000000000000000000000000000001,
+ 0x8000000000000000000000000000000000000000000000000000000000000000,
+ 0x0000000000000000000000000000000000000000000000000000000000000000
+ );
+ assert_shr(
+ 0x0000000000000000000000000000000000000000000000000000000000000001,
+ 0x8000000000000000000000000000000000000000000000000000000000000001,
+ 0x0000000000000000000000000000000000000000000000000000000000000000
+ );
+ assert_shr(
+ 0x0000000000000000000000000000000000000000000000000000000000000001,
+ 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb,
+ 0x0000000000000000000000000000000000000000000000000000000000000000
+ );
+ assert_shr(
+ 0x0000000000000000000000000000000000000000000000000000000000000001,
+ 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,
+ 0x0000000000000000000000000000000000000000000000000000000000000000
+ );
+ assert_shr(
+ 0x0000000000000000000000000000000000000000000000000000000000000005,
+ 0x0000000000000000000000000000000000000000000000000000000000000000,
+ 0x0000000000000000000000000000000000000000000000000000000000000005
+ );
+ assert_shr(
+ 0x0000000000000000000000000000000000000000000000000000000000000005,
+ 0x0000000000000000000000000000000000000000000000000000000000000001,
+ 0x0000000000000000000000000000000000000000000000000000000000000002
+ );
+ assert_shr(
+ 0x0000000000000000000000000000000000000000000000000000000000000005,
+ 0x0000000000000000000000000000000000000000000000000000000000000005,
+ 0x0000000000000000000000000000000000000000000000000000000000000000
+ );
+ assert_shr(
+ 0x0000000000000000000000000000000000000000000000000000000000000005,
+ 0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe,
+ 0x0000000000000000000000000000000000000000000000000000000000000000
+ );
+ assert_shr(
+ 0x0000000000000000000000000000000000000000000000000000000000000005,
+ 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,
+ 0x0000000000000000000000000000000000000000000000000000000000000000
+ );
+ assert_shr(
+ 0x0000000000000000000000000000000000000000000000000000000000000005,
+ 0x8000000000000000000000000000000000000000000000000000000000000000,
+ 0x0000000000000000000000000000000000000000000000000000000000000000
+ );
+ assert_shr(
+ 0x0000000000000000000000000000000000000000000000000000000000000005,
+ 0x8000000000000000000000000000000000000000000000000000000000000001,
+ 0x0000000000000000000000000000000000000000000000000000000000000000
+ );
+ assert_shr(
+ 0x0000000000000000000000000000000000000000000000000000000000000005,
+ 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb,
+ 0x0000000000000000000000000000000000000000000000000000000000000000
+ );
+ assert_shr(
+ 0x0000000000000000000000000000000000000000000000000000000000000005,
+ 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,
+ 0x0000000000000000000000000000000000000000000000000000000000000000
+ );
+ assert_shr(
+ 0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe,
+ 0x0000000000000000000000000000000000000000000000000000000000000000,
+ 0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe
+ );
+ assert_shr(
+ 0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe,
+ 0x0000000000000000000000000000000000000000000000000000000000000001,
+ 0x3fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ );
+ assert_shr(
+ 0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe,
+ 0x0000000000000000000000000000000000000000000000000000000000000005,
+ 0x03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ );
+ assert_shr(
+ 0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe,
+ 0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe,
+ 0x0000000000000000000000000000000000000000000000000000000000000000
+ );
+ assert_shr(
+ 0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe,
+ 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,
+ 0x0000000000000000000000000000000000000000000000000000000000000000
+ );
+ assert_shr(
+ 0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe,
+ 0x8000000000000000000000000000000000000000000000000000000000000000,
+ 0x0000000000000000000000000000000000000000000000000000000000000000
+ );
+ assert_shr(
+ 0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe,
+ 0x8000000000000000000000000000000000000000000000000000000000000001,
+ 0x0000000000000000000000000000000000000000000000000000000000000000
+ );
+ assert_shr(
+ 0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe,
+ 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb,
+ 0x0000000000000000000000000000000000000000000000000000000000000000
+ );
+ assert_shr(
+ 0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe,
+ 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,
+ 0x0000000000000000000000000000000000000000000000000000000000000000
+ );
+ assert_shr(
+ 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,
+ 0x0000000000000000000000000000000000000000000000000000000000000000,
+ 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ );
+ assert_shr(
+ 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,
+ 0x0000000000000000000000000000000000000000000000000000000000000001,
+ 0x3fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ );
+ assert_shr(
+ 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,
+ 0x0000000000000000000000000000000000000000000000000000000000000005,
+ 0x03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ );
+ assert_shr(
+ 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,
+ 0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe,
+ 0x0000000000000000000000000000000000000000000000000000000000000000
+ );
+ assert_shr(
+ 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,
+ 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,
+ 0x0000000000000000000000000000000000000000000000000000000000000000
+ );
+ assert_shr(
+ 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,
+ 0x8000000000000000000000000000000000000000000000000000000000000000,
+ 0x0000000000000000000000000000000000000000000000000000000000000000
+ );
+ assert_shr(
+ 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,
+ 0x8000000000000000000000000000000000000000000000000000000000000001,
+ 0x0000000000000000000000000000000000000000000000000000000000000000
+ );
+ assert_shr(
+ 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,
+ 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb,
+ 0x0000000000000000000000000000000000000000000000000000000000000000
+ );
+ assert_shr(
+ 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,
+ 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,
+ 0x0000000000000000000000000000000000000000000000000000000000000000
+ );
+ assert_shr(
+ 0x8000000000000000000000000000000000000000000000000000000000000000,
+ 0x0000000000000000000000000000000000000000000000000000000000000000,
+ 0x8000000000000000000000000000000000000000000000000000000000000000
+ );
+ assert_shr(
+ 0x8000000000000000000000000000000000000000000000000000000000000000,
+ 0x0000000000000000000000000000000000000000000000000000000000000001,
+ 0x4000000000000000000000000000000000000000000000000000000000000000
+ );
+ assert_shr(
+ 0x8000000000000000000000000000000000000000000000000000000000000000,
+ 0x0000000000000000000000000000000000000000000000000000000000000005,
+ 0x0400000000000000000000000000000000000000000000000000000000000000
+ );
+ assert_shr(
+ 0x8000000000000000000000000000000000000000000000000000000000000000,
+ 0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe,
+ 0x0000000000000000000000000000000000000000000000000000000000000000
+ );
+ assert_shr(
+ 0x8000000000000000000000000000000000000000000000000000000000000000,
+ 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,
+ 0x0000000000000000000000000000000000000000000000000000000000000000
+ );
+ assert_shr(
+ 0x8000000000000000000000000000000000000000000000000000000000000000,
+ 0x8000000000000000000000000000000000000000000000000000000000000000,
+ 0x0000000000000000000000000000000000000000000000000000000000000000
+ );
+ assert_shr(
+ 0x8000000000000000000000000000000000000000000000000000000000000000,
+ 0x8000000000000000000000000000000000000000000000000000000000000001,
+ 0x0000000000000000000000000000000000000000000000000000000000000000
+ );
+ assert_shr(
+ 0x8000000000000000000000000000000000000000000000000000000000000000,
+ 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb,
+ 0x0000000000000000000000000000000000000000000000000000000000000000
+ );
+ assert_shr(
+ 0x8000000000000000000000000000000000000000000000000000000000000000,
+ 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,
+ 0x0000000000000000000000000000000000000000000000000000000000000000
+ );
+ assert_shr(
+ 0x8000000000000000000000000000000000000000000000000000000000000001,
+ 0x0000000000000000000000000000000000000000000000000000000000000000,
+ 0x8000000000000000000000000000000000000000000000000000000000000001
+ );
+ assert_shr(
+ 0x8000000000000000000000000000000000000000000000000000000000000001,
+ 0x0000000000000000000000000000000000000000000000000000000000000001,
+ 0x4000000000000000000000000000000000000000000000000000000000000000
+ );
+ assert_shr(
+ 0x8000000000000000000000000000000000000000000000000000000000000001,
+ 0x0000000000000000000000000000000000000000000000000000000000000005,
+ 0x0400000000000000000000000000000000000000000000000000000000000000
+ );
+ assert_shr(
+ 0x8000000000000000000000000000000000000000000000000000000000000001,
+ 0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe,
+ 0x0000000000000000000000000000000000000000000000000000000000000000
+ );
+ assert_shr(
+ 0x8000000000000000000000000000000000000000000000000000000000000001,
+ 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,
+ 0x0000000000000000000000000000000000000000000000000000000000000000
+ );
+ assert_shr(
+ 0x8000000000000000000000000000000000000000000000000000000000000001,
+ 0x8000000000000000000000000000000000000000000000000000000000000000,
+ 0x0000000000000000000000000000000000000000000000000000000000000000
+ );
+ assert_shr(
+ 0x8000000000000000000000000000000000000000000000000000000000000001,
+ 0x8000000000000000000000000000000000000000000000000000000000000001,
+ 0x0000000000000000000000000000000000000000000000000000000000000000
+ );
+ assert_shr(
+ 0x8000000000000000000000000000000000000000000000000000000000000001,
+ 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb,
+ 0x0000000000000000000000000000000000000000000000000000000000000000
+ );
+ assert_shr(
+ 0x8000000000000000000000000000000000000000000000000000000000000001,
+ 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,
+ 0x0000000000000000000000000000000000000000000000000000000000000000
+ );
+ assert_shr(
+ 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb,
+ 0x0000000000000000000000000000000000000000000000000000000000000000,
+ 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb
+ );
+ assert_shr(
+ 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb,
+ 0x0000000000000000000000000000000000000000000000000000000000000001,
+ 0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd
+ );
+ assert_shr(
+ 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb,
+ 0x0000000000000000000000000000000000000000000000000000000000000005,
+ 0x07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ );
+ assert_shr(
+ 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb,
+ 0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe,
+ 0x0000000000000000000000000000000000000000000000000000000000000000
+ );
+ assert_shr(
+ 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb,
+ 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,
+ 0x0000000000000000000000000000000000000000000000000000000000000000
+ );
+ assert_shr(
+ 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb,
+ 0x8000000000000000000000000000000000000000000000000000000000000000,
+ 0x0000000000000000000000000000000000000000000000000000000000000000
+ );
+ assert_shr(
+ 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb,
+ 0x8000000000000000000000000000000000000000000000000000000000000001,
+ 0x0000000000000000000000000000000000000000000000000000000000000000
+ );
+ assert_shr(
+ 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb,
+ 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb,
+ 0x0000000000000000000000000000000000000000000000000000000000000000
+ );
+ assert_shr(
+ 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb,
+ 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,
+ 0x0000000000000000000000000000000000000000000000000000000000000000
+ );
+ assert_shr(
+ 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,
+ 0x0000000000000000000000000000000000000000000000000000000000000000,
+ 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ );
+ assert_shr(
+ 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,
+ 0x0000000000000000000000000000000000000000000000000000000000000001,
+ 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ );
+ assert_shr(
+ 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,
+ 0x0000000000000000000000000000000000000000000000000000000000000005,
+ 0x07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ );
+ assert_shr(
+ 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,
+ 0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe,
+ 0x0000000000000000000000000000000000000000000000000000000000000000
+ );
+ assert_shr(
+ 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,
+ 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,
+ 0x0000000000000000000000000000000000000000000000000000000000000000
+ );
+ assert_shr(
+ 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,
+ 0x8000000000000000000000000000000000000000000000000000000000000000,
+ 0x0000000000000000000000000000000000000000000000000000000000000000
+ );
+ assert_shr(
+ 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,
+ 0x8000000000000000000000000000000000000000000000000000000000000001,
+ 0x0000000000000000000000000000000000000000000000000000000000000000
+ );
+ assert_shr(
+ 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,
+ 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb,
+ 0x0000000000000000000000000000000000000000000000000000000000000000
+ );
+ assert_shr(
+ 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,
+ 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,
+ 0x0000000000000000000000000000000000000000000000000000000000000000
+ );
+ }
+
+ fn assert_shr(a: u256, b: u256, expected: u256) {
+ // Given
+ let mut vm = VMBuilderTrait::new_with_presets().build();
+ vm.stack.push(a).expect('push failed');
+ vm.stack.push(b).expect('push failed');
+
+ // When
+ vm.exec_shr().expect('exec_shr failed');
+
+ // Then
+ assert(vm.stack.len() == 1, 'stack should have one element');
+ assert(vm.stack.peek().unwrap() == expected, 'shr failed');
+ }
+
+ #[test]
+ fn test_exec_sar() {
+ // https://github.com/ethereum/go-ethereum/blob/master/core/vm/testdata/testcases_sar.json
+ assert_sar(
+ 0x0000000000000000000000000000000000000000000000000000000000000000,
+ 0x0000000000000000000000000000000000000000000000000000000000000000,
+ 0x0000000000000000000000000000000000000000000000000000000000000000
+ );
+ assert_sar(
+ 0x0000000000000000000000000000000000000000000000000000000000000000,
+ 0x0000000000000000000000000000000000000000000000000000000000000001,
+ 0x0000000000000000000000000000000000000000000000000000000000000000
+ );
+ assert_sar(
+ 0x0000000000000000000000000000000000000000000000000000000000000000,
+ 0x0000000000000000000000000000000000000000000000000000000000000005,
+ 0x0000000000000000000000000000000000000000000000000000000000000000
+ );
+ assert_sar(
+ 0x0000000000000000000000000000000000000000000000000000000000000000,
+ 0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe,
+ 0x0000000000000000000000000000000000000000000000000000000000000000
+ );
+ assert_sar(
+ 0x0000000000000000000000000000000000000000000000000000000000000000,
+ 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,
+ 0x0000000000000000000000000000000000000000000000000000000000000000
+ );
+ assert_sar(
+ 0x0000000000000000000000000000000000000000000000000000000000000000,
+ 0x8000000000000000000000000000000000000000000000000000000000000000,
+ 0x0000000000000000000000000000000000000000000000000000000000000000
+ );
+ assert_sar(
+ 0x0000000000000000000000000000000000000000000000000000000000000000,
+ 0x8000000000000000000000000000000000000000000000000000000000000001,
+ 0x0000000000000000000000000000000000000000000000000000000000000000
+ );
+ assert_sar(
+ 0x0000000000000000000000000000000000000000000000000000000000000000,
+ 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb,
+ 0x0000000000000000000000000000000000000000000000000000000000000000
+ );
+ assert_sar(
+ 0x0000000000000000000000000000000000000000000000000000000000000000,
+ 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,
+ 0x0000000000000000000000000000000000000000000000000000000000000000
+ );
+ assert_sar(
+ 0x0000000000000000000000000000000000000000000000000000000000000001,
+ 0x0000000000000000000000000000000000000000000000000000000000000000,
+ 0x0000000000000000000000000000000000000000000000000000000000000001
+ );
+ assert_sar(
+ 0x0000000000000000000000000000000000000000000000000000000000000001,
+ 0x0000000000000000000000000000000000000000000000000000000000000001,
+ 0x0000000000000000000000000000000000000000000000000000000000000000
+ );
+ assert_sar(
+ 0x0000000000000000000000000000000000000000000000000000000000000001,
+ 0x0000000000000000000000000000000000000000000000000000000000000005,
+ 0x0000000000000000000000000000000000000000000000000000000000000000
+ );
+ assert_sar(
+ 0x0000000000000000000000000000000000000000000000000000000000000001,
+ 0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe,
+ 0x0000000000000000000000000000000000000000000000000000000000000000
+ );
+ assert_sar(
+ 0x0000000000000000000000000000000000000000000000000000000000000001,
+ 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,
+ 0x0000000000000000000000000000000000000000000000000000000000000000
+ );
+ assert_sar(
+ 0x0000000000000000000000000000000000000000000000000000000000000001,
+ 0x8000000000000000000000000000000000000000000000000000000000000000,
+ 0x0000000000000000000000000000000000000000000000000000000000000000
+ );
+ assert_sar(
+ 0x0000000000000000000000000000000000000000000000000000000000000001,
+ 0x8000000000000000000000000000000000000000000000000000000000000001,
+ 0x0000000000000000000000000000000000000000000000000000000000000000
+ );
+ assert_sar(
+ 0x0000000000000000000000000000000000000000000000000000000000000001,
+ 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb,
+ 0x0000000000000000000000000000000000000000000000000000000000000000
+ );
+ assert_sar(
+ 0x0000000000000000000000000000000000000000000000000000000000000001,
+ 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,
+ 0x0000000000000000000000000000000000000000000000000000000000000000
+ );
+ assert_sar(
+ 0x0000000000000000000000000000000000000000000000000000000000000005,
+ 0x0000000000000000000000000000000000000000000000000000000000000000,
+ 0x0000000000000000000000000000000000000000000000000000000000000005
+ );
+ assert_sar(
+ 0x0000000000000000000000000000000000000000000000000000000000000005,
+ 0x0000000000000000000000000000000000000000000000000000000000000001,
+ 0x0000000000000000000000000000000000000000000000000000000000000002
+ );
+ assert_sar(
+ 0x0000000000000000000000000000000000000000000000000000000000000005,
+ 0x0000000000000000000000000000000000000000000000000000000000000005,
+ 0x0000000000000000000000000000000000000000000000000000000000000000
+ );
+ assert_sar(
+ 0x0000000000000000000000000000000000000000000000000000000000000005,
+ 0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe,
+ 0x0000000000000000000000000000000000000000000000000000000000000000
+ );
+ assert_sar(
+ 0x0000000000000000000000000000000000000000000000000000000000000005,
+ 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,
+ 0x0000000000000000000000000000000000000000000000000000000000000000
+ );
+ assert_sar(
+ 0x0000000000000000000000000000000000000000000000000000000000000005,
+ 0x8000000000000000000000000000000000000000000000000000000000000000,
+ 0x0000000000000000000000000000000000000000000000000000000000000000
+ );
+ assert_sar(
+ 0x0000000000000000000000000000000000000000000000000000000000000005,
+ 0x8000000000000000000000000000000000000000000000000000000000000001,
+ 0x0000000000000000000000000000000000000000000000000000000000000000
+ );
+ assert_sar(
+ 0x0000000000000000000000000000000000000000000000000000000000000005,
+ 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb,
+ 0x0000000000000000000000000000000000000000000000000000000000000000
+ );
+ assert_sar(
+ 0x0000000000000000000000000000000000000000000000000000000000000005,
+ 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,
+ 0x0000000000000000000000000000000000000000000000000000000000000000
+ );
+ assert_sar(
+ 0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe,
+ 0x0000000000000000000000000000000000000000000000000000000000000000,
+ 0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe
+ );
+ assert_sar(
+ 0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe,
+ 0x0000000000000000000000000000000000000000000000000000000000000001,
+ 0x3fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ );
+ assert_sar(
+ 0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe,
+ 0x0000000000000000000000000000000000000000000000000000000000000005,
+ 0x03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ );
+ assert_sar(
+ 0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe,
+ 0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe,
+ 0x0000000000000000000000000000000000000000000000000000000000000000
+ );
+ assert_sar(
+ 0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe,
+ 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,
+ 0x0000000000000000000000000000000000000000000000000000000000000000
+ );
+ assert_sar(
+ 0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe,
+ 0x8000000000000000000000000000000000000000000000000000000000000000,
+ 0x0000000000000000000000000000000000000000000000000000000000000000
+ );
+ assert_sar(
+ 0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe,
+ 0x8000000000000000000000000000000000000000000000000000000000000001,
+ 0x0000000000000000000000000000000000000000000000000000000000000000
+ );
+ assert_sar(
+ 0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe,
+ 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb,
+ 0x0000000000000000000000000000000000000000000000000000000000000000
+ );
+ assert_sar(
+ 0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe,
+ 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,
+ 0x0000000000000000000000000000000000000000000000000000000000000000
+ );
+ assert_sar(
+ 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,
+ 0x0000000000000000000000000000000000000000000000000000000000000000,
+ 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ );
+ assert_sar(
+ 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,
+ 0x0000000000000000000000000000000000000000000000000000000000000001,
+ 0x3fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ );
+ assert_sar(
+ 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,
+ 0x0000000000000000000000000000000000000000000000000000000000000005,
+ 0x03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ );
+ assert_sar(
+ 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,
+ 0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe,
+ 0x0000000000000000000000000000000000000000000000000000000000000000
+ );
+ assert_sar(
+ 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,
+ 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,
+ 0x0000000000000000000000000000000000000000000000000000000000000000
+ );
+ assert_sar(
+ 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,
+ 0x8000000000000000000000000000000000000000000000000000000000000000,
+ 0x0000000000000000000000000000000000000000000000000000000000000000
+ );
+ assert_sar(
+ 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,
+ 0x8000000000000000000000000000000000000000000000000000000000000001,
+ 0x0000000000000000000000000000000000000000000000000000000000000000
+ );
+ assert_sar(
+ 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,
+ 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb,
+ 0x0000000000000000000000000000000000000000000000000000000000000000
+ );
+ assert_sar(
+ 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,
+ 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,
+ 0x0000000000000000000000000000000000000000000000000000000000000000
+ );
+ assert_sar(
+ 0x8000000000000000000000000000000000000000000000000000000000000000,
+ 0x0000000000000000000000000000000000000000000000000000000000000000,
+ 0x8000000000000000000000000000000000000000000000000000000000000000
+ );
+ assert_sar(
+ 0x8000000000000000000000000000000000000000000000000000000000000000,
+ 0x0000000000000000000000000000000000000000000000000000000000000001,
+ 0xc000000000000000000000000000000000000000000000000000000000000000
+ );
+ assert_sar(
+ 0x8000000000000000000000000000000000000000000000000000000000000000,
+ 0x0000000000000000000000000000000000000000000000000000000000000005,
+ 0xfc00000000000000000000000000000000000000000000000000000000000000
+ );
+ assert_sar(
+ 0x8000000000000000000000000000000000000000000000000000000000000000,
+ 0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe,
+ 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ );
+ assert_sar(
+ 0x8000000000000000000000000000000000000000000000000000000000000000,
+ 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,
+ 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ );
+ assert_sar(
+ 0x8000000000000000000000000000000000000000000000000000000000000000,
+ 0x8000000000000000000000000000000000000000000000000000000000000000,
+ 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ );
+ assert_sar(
+ 0x8000000000000000000000000000000000000000000000000000000000000000,
+ 0x8000000000000000000000000000000000000000000000000000000000000001,
+ 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ );
+ assert_sar(
+ 0x8000000000000000000000000000000000000000000000000000000000000000,
+ 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb,
+ 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ );
+ assert_sar(
+ 0x8000000000000000000000000000000000000000000000000000000000000000,
+ 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,
+ 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ );
+ assert_sar(
+ 0x8000000000000000000000000000000000000000000000000000000000000001,
+ 0x0000000000000000000000000000000000000000000000000000000000000000,
+ 0x8000000000000000000000000000000000000000000000000000000000000001
+ );
+ assert_sar(
+ 0x8000000000000000000000000000000000000000000000000000000000000001,
+ 0x0000000000000000000000000000000000000000000000000000000000000001,
+ 0xc000000000000000000000000000000000000000000000000000000000000000
+ );
+ assert_sar(
+ 0x8000000000000000000000000000000000000000000000000000000000000001,
+ 0x0000000000000000000000000000000000000000000000000000000000000005,
+ 0xfc00000000000000000000000000000000000000000000000000000000000000
+ );
+ assert_sar(
+ 0x8000000000000000000000000000000000000000000000000000000000000001,
+ 0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe,
+ 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ );
+ assert_sar(
+ 0x8000000000000000000000000000000000000000000000000000000000000001,
+ 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,
+ 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ );
+ assert_sar(
+ 0x8000000000000000000000000000000000000000000000000000000000000001,
+ 0x8000000000000000000000000000000000000000000000000000000000000000,
+ 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ );
+ assert_sar(
+ 0x8000000000000000000000000000000000000000000000000000000000000001,
+ 0x8000000000000000000000000000000000000000000000000000000000000001,
+ 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ );
+ assert_sar(
+ 0x8000000000000000000000000000000000000000000000000000000000000001,
+ 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb,
+ 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ );
+ assert_sar(
+ 0x8000000000000000000000000000000000000000000000000000000000000001,
+ 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,
+ 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ );
+ assert_sar(
+ 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb,
+ 0x0000000000000000000000000000000000000000000000000000000000000000,
+ 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb
+ );
+ assert_sar(
+ 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb,
+ 0x0000000000000000000000000000000000000000000000000000000000000001,
+ 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd
+ );
+ assert_sar(
+ 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb,
+ 0x0000000000000000000000000000000000000000000000000000000000000005,
+ 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ );
+ assert_sar(
+ 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb,
+ 0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe,
+ 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ );
+ assert_sar(
+ 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb,
+ 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,
+ 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ );
+ assert_sar(
+ 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb,
+ 0x8000000000000000000000000000000000000000000000000000000000000000,
+ 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ );
+ assert_sar(
+ 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb,
+ 0x8000000000000000000000000000000000000000000000000000000000000001,
+ 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ );
+ assert_sar(
+ 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb,
+ 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb,
+ 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ );
+ assert_sar(
+ 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb,
+ 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,
+ 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ );
+ assert_sar(
+ 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,
+ 0x0000000000000000000000000000000000000000000000000000000000000000,
+ 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ );
+ assert_sar(
+ 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,
+ 0x0000000000000000000000000000000000000000000000000000000000000001,
+ 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ );
+ assert_sar(
+ 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,
+ 0x0000000000000000000000000000000000000000000000000000000000000005,
+ 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ );
+ assert_sar(
+ 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,
+ 0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe,
+ 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ );
+ assert_sar(
+ 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,
+ 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,
+ 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ );
+ assert_sar(
+ 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,
+ 0x8000000000000000000000000000000000000000000000000000000000000000,
+ 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ );
+ assert_sar(
+ 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,
+ 0x8000000000000000000000000000000000000000000000000000000000000001,
+ 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ );
+ assert_sar(
+ 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,
+ 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb,
+ 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ );
+ assert_sar(
+ 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,
+ 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,
+ 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ );
+ }
+
+ fn assert_sar(a: u256, b: u256, expected: u256) {
+ // Given
+ let mut vm = VMBuilderTrait::new_with_presets().build();
+ vm.stack.push(a).expect('push failed');
+ vm.stack.push(b).expect('push failed');
+
+ // When
+
+ vm.exec_sar().expect('exec_sar failed');
+
+ // Then
+ assert(vm.stack.len() == 1, 'stack should have one element');
+ assert(vm.stack.peek().unwrap() == expected, 'sar failed');
+ }
+
+ #[test]
+ fn test_exec_or_should_pop_0_and_1_and_push_0xCD_when_0_is_0x89_and_1_is_0xC5() {
+ //Given
+ let mut vm = VMBuilderTrait::new_with_presets().build();
+ vm.stack.push(0x89).expect('push failed');
+ vm.stack.push(0xC5).expect('push failed');
+
+ //When
+ vm.exec_or().expect('exec_or failed');
+
+ // Then
+ assert(vm.stack.len() == 1, 'stack should have one element');
+ assert(vm.stack.peek().unwrap() == 0xCD, 'stack top should be 0xCD');
+ }
+
+ #[test]
+ fn test_or_true() {
+ // Given
+ let mut vm = VMBuilderTrait::new_with_presets().build();
+ vm.stack.push(0x01).expect('push failed');
+ vm.stack.push(0x00).expect('push failed');
+
+ // When
+ vm.exec_or().expect('exec_or failed');
+
+ // Then
+ assert(vm.stack.len() == 1, 'stack should have one element');
+ assert(vm.stack.peek().unwrap() == 0x01, 'stack top should be 0x01');
+ }
+
+ #[test]
+ fn test_or_false() {
+ // Given
+ let mut vm = VMBuilderTrait::new_with_presets().build();
+ vm.stack.push(0x00).expect('push failed');
+ vm.stack.push(0x00).expect('push failed');
+
+ // When
+ vm.exec_or().expect('exec_or failed');
+
+ // Then
+ assert(vm.stack.len() == 1, 'stack should have one element');
+ assert(vm.stack.peek().unwrap() == 0x00, 'stack top should be 0x00');
+ }
+
+
+ #[test]
+ fn test_exec_lt_true() {
+ // Given
+ let mut vm = VMBuilderTrait::new_with_presets().build();
+ vm.stack.push(10_u256).expect('push failed');
+ vm.stack.push(9_u256).expect('push failed');
+
+ // When
+ vm.exec_lt().expect('exec_lt failed');
+
+ // Then
+ assert(vm.stack.len() == 1, 'stack should have one element');
+ assert(vm.stack.peek().unwrap() == 0x01, 'stack top should be true');
+ }
+
+ #[test]
+ fn test_exec_lt_false() {
+ // Given
+ let mut vm = VMBuilderTrait::new_with_presets().build();
+ vm.stack.push(10_u256).expect('push failed');
+ vm.stack.push(20_u256).expect('push failed');
+
+ // When
+ vm.exec_lt().expect('exec_lt failed');
+
+ // Then
+ assert(vm.stack.len() == 1, 'stack should have one element');
+ assert(vm.stack.peek().unwrap() == 0x00, 'stack top should be false');
+ }
+
+ #[test]
+ fn test_exec_lt_false_eq() {
+ // Given
+ let mut vm = VMBuilderTrait::new_with_presets().build();
+ vm.stack.push(10_u256).expect('push failed');
+ vm.stack.push(10_u256).expect('push failed');
+
+ // When
+ vm.exec_lt().expect('exec_lt failed');
+
+ // Then
+ assert(vm.stack.len() == 1, 'stack should have one element');
+ assert(vm.stack.peek().unwrap() == 0x00, 'stack top should be false');
+ }
+}
diff --git a/cairo/kakarot-ssj/crates/evm/src/instructions/duplication_operations.cairo b/cairo/kakarot-ssj/crates/evm/src/instructions/duplication_operations.cairo
new file mode 100644
index 000000000..a69796598
--- /dev/null
+++ b/cairo/kakarot-ssj/crates/evm/src/instructions/duplication_operations.cairo
@@ -0,0 +1,557 @@
+//! Duplication Operations.
+
+// Internal imports
+use crate::errors::EVMError;
+use crate::gas;
+use crate::model::vm::{VM, VMTrait};
+use crate::stack::StackTrait;
+
+/// Generic DUP operation
+#[inline(always)]
+fn exec_dup_i(ref self: VM, i: NonZero) -> Result<(), EVMError> {
+ self.charge_gas(gas::VERYLOW)?;
+ let i: u8 = i.into();
+ let item = self.stack.peek_at((i - 1).into())?;
+ self.stack.push(item)
+}
+
+#[generate_trait]
+pub impl DuplicationOperations of DuplicationOperationsTrait {
+ /// 0x80 - DUP1 operation
+ #[inline(always)]
+ fn exec_dup1(ref self: VM) -> Result<(), EVMError> {
+ exec_dup_i(ref self, 1)
+ }
+
+ /// 0x81 - DUP2 operation
+ #[inline(always)]
+ fn exec_dup2(ref self: VM) -> Result<(), EVMError> {
+ exec_dup_i(ref self, 2)
+ }
+
+ /// 0x82 - DUP3 operation
+ #[inline(always)]
+ fn exec_dup3(ref self: VM) -> Result<(), EVMError> {
+ exec_dup_i(ref self, 3)
+ }
+
+ /// 0x83 - DUP2 operation
+ #[inline(always)]
+ fn exec_dup4(ref self: VM) -> Result<(), EVMError> {
+ exec_dup_i(ref self, 4)
+ }
+
+ /// 0x84 - DUP5 operation
+ #[inline(always)]
+ fn exec_dup5(ref self: VM) -> Result<(), EVMError> {
+ exec_dup_i(ref self, 5)
+ }
+
+ /// 0x85 - DUP6 operation
+ #[inline(always)]
+ fn exec_dup6(ref self: VM) -> Result<(), EVMError> {
+ exec_dup_i(ref self, 6)
+ }
+
+ /// 0x86 - DUP7 operation
+ #[inline(always)]
+ fn exec_dup7(ref self: VM) -> Result<(), EVMError> {
+ exec_dup_i(ref self, 7)
+ }
+
+ /// 0x87 - DUP8 operation
+ #[inline(always)]
+ fn exec_dup8(ref self: VM) -> Result<(), EVMError> {
+ exec_dup_i(ref self, 8)
+ }
+
+ /// 0x88 - DUP9 operation
+ #[inline(always)]
+ fn exec_dup9(ref self: VM) -> Result<(), EVMError> {
+ exec_dup_i(ref self, 9)
+ }
+
+ /// 0x89 - DUP10 operation
+ #[inline(always)]
+ fn exec_dup10(ref self: VM) -> Result<(), EVMError> {
+ exec_dup_i(ref self, 10)
+ }
+
+ /// 0x8A - DUP11 operation
+ #[inline(always)]
+ fn exec_dup11(ref self: VM) -> Result<(), EVMError> {
+ exec_dup_i(ref self, 11)
+ }
+
+ /// 0x8B - DUP12 operation
+ #[inline(always)]
+ fn exec_dup12(ref self: VM) -> Result<(), EVMError> {
+ exec_dup_i(ref self, 12)
+ }
+
+ /// 0x8C - DUP13 operation
+ #[inline(always)]
+ fn exec_dup13(ref self: VM) -> Result<(), EVMError> {
+ exec_dup_i(ref self, 13)
+ }
+
+ /// 0x8D - DUP14 operation
+ #[inline(always)]
+ fn exec_dup14(ref self: VM) -> Result<(), EVMError> {
+ exec_dup_i(ref self, 14)
+ }
+
+ /// 0x8E - DUP15 operation
+ #[inline(always)]
+ fn exec_dup15(ref self: VM) -> Result<(), EVMError> {
+ exec_dup_i(ref self, 15)
+ }
+
+ /// 0x8F - DUP16 operation
+ #[inline(always)]
+ fn exec_dup16(ref self: VM) -> Result<(), EVMError> {
+ exec_dup_i(ref self, 16)
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use crate::instructions::DuplicationOperationsTrait;
+ use crate::stack::Stack;
+ use crate::stack::StackTrait;
+ use crate::test_utils::VMBuilderTrait;
+
+
+ // ensures all values start from index `from` upto index `to` of stack are `0x0`
+ fn ensures_zeros(ref stack: Stack, from: u32, to: u32) {
+ if to > from {
+ return;
+ }
+
+ for idx in from..to {
+ assert(stack.peek_at(idx).unwrap() == 0x00, 'should be zero');
+ };
+ }
+
+ // push `n` number of `0x0` to the stack
+ fn push_zeros(ref stack: Stack, n: u8) {
+ for _ in 0..n {
+ stack.push(0x0).unwrap();
+ };
+ }
+
+ #[test]
+ fn test_dup1() {
+ // Given
+ let mut vm = VMBuilderTrait::new_with_presets().build();
+
+ let initial_len = vm.stack.len();
+
+ vm.stack.push(0x01).expect('push failed');
+
+ let old_stack_len = vm.stack.len();
+
+ // When
+ vm.exec_dup1().expect('exec_dup1 failed');
+
+ // Then
+ let new_stack_len = vm.stack.len();
+
+ assert(new_stack_len == old_stack_len + 1, 'len should increase by 1');
+
+ assert(vm.stack.peek_at(initial_len).unwrap() == 0x01, 'first inserted spot should be 1');
+ assert(vm.stack.peek_at(new_stack_len - 1).unwrap() == 0x01, 'top of stack should be 1');
+
+ ensures_zeros(ref vm.stack, initial_len + 1, new_stack_len - 1);
+ }
+
+ #[test]
+ fn test_dup2() {
+ // Given
+ let mut vm = VMBuilderTrait::new_with_presets().build();
+
+ let initial_len = vm.stack.len();
+
+ vm.stack.push(0x01).expect('push failed');
+ push_zeros(ref vm.stack, 1);
+
+ let old_stack_len = vm.stack.len();
+
+ // When
+ vm.exec_dup2().expect('exec_dup2 failed');
+
+ // Then
+ let new_stack_len = vm.stack.len();
+
+ assert(new_stack_len == old_stack_len + 1, 'len should increase by 1');
+
+ assert(vm.stack.peek_at(initial_len).unwrap() == 0x01, 'first inserted spot should be 1');
+ assert(vm.stack.peek_at(new_stack_len - 1).unwrap() == 0x01, 'top of stack should be 1');
+
+ ensures_zeros(ref vm.stack, initial_len + 1, new_stack_len - 1);
+ }
+
+ #[test]
+ fn test_dup3() {
+ // Given
+ let mut vm = VMBuilderTrait::new_with_presets().build();
+
+ let initial_len = vm.stack.len();
+
+ vm.stack.push(0x01).expect('push failed');
+ push_zeros(ref vm.stack, 2);
+
+ let old_stack_len = vm.stack.len();
+
+ // When
+ vm.exec_dup3().expect('exec_dup3 failed');
+
+ // Then
+ let new_stack_len = vm.stack.len();
+
+ assert(new_stack_len == old_stack_len + 1, 'len should increase by 1');
+
+ assert(vm.stack.peek_at(initial_len).unwrap() == 0x01, 'first inserted spot should be 1');
+ assert(vm.stack.peek_at(new_stack_len - 1).unwrap() == 0x01, 'top of stack should be 1');
+
+ ensures_zeros(ref vm.stack, initial_len + 1, new_stack_len - 1);
+ }
+
+ #[test]
+ fn test_dup4() {
+ // Given
+ let mut vm = VMBuilderTrait::new_with_presets().build();
+
+ let initial_len = vm.stack.len();
+
+ vm.stack.push(0x01).expect('push failed');
+ push_zeros(ref vm.stack, 3);
+
+ let old_stack_len = vm.stack.len();
+
+ // When
+ vm.exec_dup4().expect('exec_dup4 failed');
+
+ // Then
+ let new_stack_len = vm.stack.len();
+
+ assert(new_stack_len == old_stack_len + 1, 'len should increase by 1');
+
+ assert(vm.stack.peek_at(initial_len).unwrap() == 0x01, 'first inserted spot should be 1');
+ assert(vm.stack.peek_at(new_stack_len - 1).unwrap() == 0x01, 'top of stack should be 1');
+
+ ensures_zeros(ref vm.stack, initial_len + 1, new_stack_len - 1);
+ }
+
+ #[test]
+ fn test_dup5() {
+ // Given
+ let mut vm = VMBuilderTrait::new_with_presets().build();
+
+ let initial_len = vm.stack.len();
+
+ vm.stack.push(0x01).expect('push failed');
+ push_zeros(ref vm.stack, 4);
+
+ let old_stack_len = vm.stack.len();
+
+ // When
+ vm.exec_dup5().expect('exec_dup5 failed');
+
+ // Then
+ let new_stack_len = vm.stack.len();
+
+ assert(new_stack_len == old_stack_len + 1, 'len should increase by 1');
+
+ assert(vm.stack.peek_at(initial_len).unwrap() == 0x01, 'first inserted spot should be 1');
+ assert(vm.stack.peek_at(new_stack_len - 1).unwrap() == 0x01, 'top of stack should be 1');
+
+ ensures_zeros(ref vm.stack, initial_len + 1, new_stack_len - 1);
+ }
+
+ #[test]
+ fn test_dup6() {
+ // Given
+ let mut vm = VMBuilderTrait::new_with_presets().build();
+
+ let initial_len = vm.stack.len();
+
+ vm.stack.push(0x01).expect('push failed');
+ push_zeros(ref vm.stack, 5);
+
+ let old_stack_len = vm.stack.len();
+
+ // When
+ vm.exec_dup6().expect('exec_dup6 failed');
+
+ // Then
+ let new_stack_len = vm.stack.len();
+
+ assert(new_stack_len == old_stack_len + 1, 'len should increase by 1');
+
+ assert(vm.stack.peek_at(initial_len).unwrap() == 0x01, 'first inserted spot should be 1');
+ assert(vm.stack.peek_at(new_stack_len - 1).unwrap() == 0x01, 'top of stack should be 1');
+
+ ensures_zeros(ref vm.stack, initial_len + 1, new_stack_len - 1);
+ }
+
+ #[test]
+ fn test_dup7() {
+ // Given
+ let mut vm = VMBuilderTrait::new_with_presets().build();
+
+ let initial_len = vm.stack.len();
+
+ vm.stack.push(0x01).expect('push failed');
+ push_zeros(ref vm.stack, 6);
+
+ let old_stack_len = vm.stack.len();
+
+ // When
+ vm.exec_dup7().expect('exec_dup7 failed');
+
+ // Then
+ let new_stack_len = vm.stack.len();
+
+ assert(new_stack_len == old_stack_len + 1, 'len should increase by 1');
+
+ assert(vm.stack.peek_at(initial_len).unwrap() == 0x01, 'first inserted spot should be 1');
+ assert(vm.stack.peek_at(new_stack_len - 1).unwrap() == 0x01, 'top of stack should be 1');
+
+ ensures_zeros(ref vm.stack, initial_len + 1, new_stack_len - 1);
+ }
+
+ #[test]
+ fn test_dup8() {
+ // Given
+ let mut vm = VMBuilderTrait::new_with_presets().build();
+
+ let initial_len = vm.stack.len();
+
+ vm.stack.push(0x01).expect('push failed');
+ push_zeros(ref vm.stack, 7);
+
+ let old_stack_len = vm.stack.len();
+
+ // When
+ vm.exec_dup8().expect('exec_dup8 failed');
+
+ // Then
+ let new_stack_len = vm.stack.len();
+
+ assert(new_stack_len == old_stack_len + 1, 'len should increase by 1');
+
+ assert(vm.stack.peek_at(initial_len).unwrap() == 0x01, 'first inserted spot should be 1');
+ assert(vm.stack.peek_at(new_stack_len - 1).unwrap() == 0x01, 'top of stack should be 1');
+
+ ensures_zeros(ref vm.stack, initial_len + 1, new_stack_len - 1);
+ }
+
+ #[test]
+ fn test_dup9() {
+ // Given
+ let mut vm = VMBuilderTrait::new_with_presets().build();
+
+ let initial_len = vm.stack.len();
+
+ vm.stack.push(0x01).expect('push failed');
+ push_zeros(ref vm.stack, 8);
+
+ let old_stack_len = vm.stack.len();
+
+ // When
+ vm.exec_dup9().expect('exec_dup9 failed');
+
+ // Then
+ let new_stack_len = vm.stack.len();
+
+ assert(new_stack_len == old_stack_len + 1, 'len should increase by 1');
+
+ assert(vm.stack.peek_at(initial_len).unwrap() == 0x01, 'first inserted spot should be 1');
+ assert(vm.stack.peek_at(new_stack_len - 1).unwrap() == 0x01, 'top of stack should be 1');
+
+ ensures_zeros(ref vm.stack, initial_len + 1, new_stack_len - 1);
+ }
+
+ #[test]
+ fn test_dup10() {
+ // Given
+ let mut vm = VMBuilderTrait::new_with_presets().build();
+
+ let initial_len = vm.stack.len();
+
+ vm.stack.push(0x01).expect('push failed');
+ push_zeros(ref vm.stack, 9);
+
+ let old_stack_len = vm.stack.len();
+
+ // When
+ vm.exec_dup10().expect('exec_dup10 failed');
+
+ // Then
+ let new_stack_len = vm.stack.len();
+
+ assert(new_stack_len == old_stack_len + 1, 'len should increase by 1');
+
+ assert(vm.stack.peek_at(initial_len).unwrap() == 0x01, 'first inserted spot should be 1');
+ assert(vm.stack.peek_at(new_stack_len - 1).unwrap() == 0x01, 'top of stack should be 1');
+
+ ensures_zeros(ref vm.stack, initial_len + 1, new_stack_len - 1);
+ }
+
+ #[test]
+ fn test_dup11() {
+ // Given
+ let mut vm = VMBuilderTrait::new_with_presets().build();
+
+ let initial_len = vm.stack.len();
+
+ vm.stack.push(0x01).expect('push failed');
+ push_zeros(ref vm.stack, 10);
+
+ let old_stack_len = vm.stack.len();
+
+ // When
+ vm.exec_dup11().expect('exec_dup11 failed');
+
+ // Then
+ let new_stack_len = vm.stack.len();
+
+ assert(new_stack_len == old_stack_len + 1, 'len should increase by 1');
+
+ assert(vm.stack.peek_at(initial_len).unwrap() == 0x01, 'first inserted spot should be 1');
+ assert(vm.stack.peek_at(new_stack_len - 1).unwrap() == 0x01, 'top of stack should be 1');
+
+ ensures_zeros(ref vm.stack, initial_len + 1, new_stack_len - 1);
+ }
+
+ #[test]
+ fn test_dup12() {
+ // Given
+ let mut vm = VMBuilderTrait::new_with_presets().build();
+
+ let initial_len = vm.stack.len();
+
+ vm.stack.push(0x01).expect('push failed');
+ push_zeros(ref vm.stack, 11);
+
+ let old_stack_len = vm.stack.len();
+
+ // When
+ vm.exec_dup12().expect('exec_dup12 failed');
+
+ // Then
+ let new_stack_len = vm.stack.len();
+
+ assert(new_stack_len == old_stack_len + 1, 'len should increase by 1');
+
+ assert(vm.stack.peek_at(initial_len).unwrap() == 0x01, 'first inserted spot should be 1');
+ assert(vm.stack.peek_at(new_stack_len - 1).unwrap() == 0x01, 'top of stack should be 1');
+
+ ensures_zeros(ref vm.stack, initial_len + 1, new_stack_len - 1);
+ }
+
+ #[test]
+ fn test_dup13() {
+ // Given
+ let mut vm = VMBuilderTrait::new_with_presets().build();
+
+ let initial_len = vm.stack.len();
+
+ vm.stack.push(0x01).expect('push failed');
+ push_zeros(ref vm.stack, 12);
+
+ let old_stack_len = vm.stack.len();
+
+ // When
+ vm.exec_dup13().expect('exec_dup13 failed');
+
+ // Then
+ let new_stack_len = vm.stack.len();
+
+ assert(new_stack_len == old_stack_len + 1, 'len should increase by 1');
+
+ assert(vm.stack.peek_at(initial_len).unwrap() == 0x01, 'first inserted spot should be 1');
+ assert(vm.stack.peek_at(new_stack_len - 1).unwrap() == 0x01, 'top of stack should be 1');
+
+ ensures_zeros(ref vm.stack, initial_len + 1, new_stack_len - 1);
+ }
+
+ #[test]
+ fn test_dup14() {
+ // Given
+ let mut vm = VMBuilderTrait::new_with_presets().build();
+
+ let initial_len = vm.stack.len();
+
+ vm.stack.push(0x01).expect('push failed');
+ push_zeros(ref vm.stack, 13);
+
+ let old_stack_len = vm.stack.len();
+
+ // When
+ vm.exec_dup14().expect('exec_dup14 failed');
+
+ // Then
+ let new_stack_len = vm.stack.len();
+
+ assert(new_stack_len == old_stack_len + 1, 'len should increase by 1');
+
+ assert(vm.stack.peek_at(initial_len).unwrap() == 0x01, 'first inserted spot should be 1');
+ assert(vm.stack.peek_at(new_stack_len - 1).unwrap() == 0x01, 'top of stack should be 1');
+
+ ensures_zeros(ref vm.stack, initial_len + 1, new_stack_len - 1);
+ }
+
+ #[test]
+ fn test_dup15() {
+ // Given
+ let mut vm = VMBuilderTrait::new_with_presets().build();
+
+ let initial_len = vm.stack.len();
+
+ vm.stack.push(0x01).expect('push failed');
+ push_zeros(ref vm.stack, 14);
+
+ let old_stack_len = vm.stack.len();
+
+ // When
+ vm.exec_dup15().expect('exec_dup15 failed');
+
+ // Then
+ let new_stack_len = vm.stack.len();
+
+ assert(new_stack_len == old_stack_len + 1, 'len should increase by 1');
+
+ assert(vm.stack.peek_at(initial_len).unwrap() == 0x01, 'first inserted spot should be 1');
+ assert(vm.stack.peek_at(new_stack_len - 1).unwrap() == 0x01, 'top of stack should be 1');
+
+ ensures_zeros(ref vm.stack, initial_len + 1, new_stack_len - 1);
+ }
+
+ #[test]
+ fn test_dup16() {
+ // Given
+ let mut vm = VMBuilderTrait::new_with_presets().build();
+
+ let initial_len = vm.stack.len();
+
+ vm.stack.push(0x01).expect('push failed');
+ push_zeros(ref vm.stack, 15);
+
+ let old_stack_len = vm.stack.len();
+
+ // When
+ vm.exec_dup16().expect('exec_dup16 failed');
+
+ // Then
+ let new_stack_len = vm.stack.len();
+
+ assert(new_stack_len == old_stack_len + 1, 'len should increase by 1');
+
+ assert(vm.stack.peek_at(initial_len).unwrap() == 0x01, 'first inserted spot should be 1');
+ assert(vm.stack.peek_at(new_stack_len - 1).unwrap() == 0x01, 'top of stack should be 1');
+
+ ensures_zeros(ref vm.stack, initial_len + 1, new_stack_len - 1);
+ }
+}
diff --git a/cairo/kakarot-ssj/crates/evm/src/instructions/environmental_information.cairo b/cairo/kakarot-ssj/crates/evm/src/instructions/environmental_information.cairo
new file mode 100644
index 000000000..22d47d8a7
--- /dev/null
+++ b/cairo/kakarot-ssj/crates/evm/src/instructions/environmental_information.cairo
@@ -0,0 +1,1181 @@
+use core::num::traits::OverflowingAdd;
+use core::num::traits::Zero;
+use core::num::traits::{CheckedAdd, CheckedSub};
+use crate::errors::{ensure, EVMError};
+use crate::gas;
+use crate::memory::MemoryTrait;
+use crate::model::account::{AccountTrait};
+use crate::model::vm::{VM, VMTrait};
+use crate::model::{AddressTrait};
+use crate::stack::StackTrait;
+use crate::state::StateTrait;
+use utils::helpers::bytes_32_words_size;
+use utils::set::SetTrait;
+use utils::traits::bytes::FromBytes;
+use utils::traits::{EthAddressIntoU256};
+
+
+#[generate_trait]
+pub impl EnvironmentInformationImpl of EnvironmentInformationTrait {
+ /// 0x30 - ADDRESS
+ /// Get address of currently executing account.
+ /// # Specification: https://www.evm.codes/#30?fork=shanghai
+ fn exec_address(ref self: VM) -> Result<(), EVMError> {
+ self.charge_gas(gas::BASE)?;
+ self.stack.push(self.message().target.evm.into())
+ }
+
+ /// 0x31 - BALANCE opcode.
+ /// Get ETH balance of the specified address.
+ /// # Specification: https://www.evm.codes/#31?fork=shanghai
+ fn exec_balance(ref self: VM) -> Result<(), EVMError> {
+ let evm_address = self.stack.pop_eth_address()?;
+
+ // GAS
+ if self.accessed_addresses.contains(evm_address) {
+ self.charge_gas(gas::WARM_ACCESS_COST)?;
+ } else {
+ self.accessed_addresses.add(evm_address);
+ self.charge_gas(gas::COLD_ACCOUNT_ACCESS_COST)?
+ }
+
+ let balance = self.env.state.get_account(evm_address).balance();
+ self.stack.push(balance)
+ }
+
+ /// 0x32 - ORIGIN
+ /// Get execution origination address.
+ /// # Specification: https://www.evm.codes/#32?fork=shanghai
+ fn exec_origin(ref self: VM) -> Result<(), EVMError> {
+ self.charge_gas(gas::BASE)?;
+ self.stack.push(self.env.origin.evm.into())
+ }
+
+ /// 0x33 - CALLER
+ /// Get caller address.
+ /// # Specification: https://www.evm.codes/#33?fork=shanghai
+ fn exec_caller(ref self: VM) -> Result<(), EVMError> {
+ self.charge_gas(gas::BASE)?;
+ self.stack.push(self.message().caller.evm.into())
+ }
+
+ /// 0x34 - CALLVALUE
+ /// Get deposited value by the instruction/transaction responsible for this execution.
+ /// # Specification: https://www.evm.codes/#34?fork=shanghai
+ fn exec_callvalue(ref self: VM) -> Result<(), EVMError> {
+ self.charge_gas(gas::BASE)?;
+ self.stack.push(self.message().value)
+ }
+
+ /// 0x35 - CALLDATALOAD
+ /// Push a word from the calldata onto the stack.
+ /// # Specification: https://www.evm.codes/#35?fork=shanghai
+ fn exec_calldataload(ref self: VM) -> Result<(), EVMError> {
+ self.charge_gas(gas::VERYLOW)?;
+
+ // Don't error out if the offset is too big. It should just push 0.
+ let offset: usize = self.stack.pop_saturating_usize()?;
+
+ let calldata = self.message().data;
+ let calldata_len = calldata.len();
+
+ // All bytes after the end of the calldata are set to 0.
+ let bytes_len = match calldata_len.checked_sub(offset) {
+ Option::None => { return self.stack.push(0); },
+ Option::Some(remaining_len) => {
+ if remaining_len == 0 {
+ return self.stack.push(0);
+ }
+ core::cmp::min(32, remaining_len)
+ }
+ };
+
+ // Slice the calldata
+ let sliced = calldata.slice(offset, bytes_len);
+
+ let mut data_to_load: u256 = sliced
+ .from_be_bytes_partial()
+ .expect('Failed to parse calldata');
+
+ // Fill the rest of the data to load with zeros
+ // TODO: optimize once we have dw-based exponentiation
+ for _ in 0..32 - bytes_len {
+ data_to_load *= 256;
+ };
+ self.stack.push(data_to_load)
+ }
+
+ /// 0x36 - CALLDATASIZE
+ /// Get the size of return data.
+ /// # Specification: https://www.evm.codes/#36?fork=shanghai
+ fn exec_calldatasize(ref self: VM) -> Result<(), EVMError> {
+ self.charge_gas(gas::BASE)?;
+ let size: u256 = self.message().data.len().into();
+ self.stack.push(size)
+ }
+
+ /// 0x37 - CALLDATACOPY operation
+ /// Save word to memory.
+ /// # Specification: https://www.evm.codes/#37?fork=shanghai
+ fn exec_calldatacopy(ref self: VM) -> Result<(), EVMError> {
+ let dest_offset = self.stack.pop_saturating_usize()?;
+ let offset = self.stack.pop_saturating_usize()?;
+ let size = self.stack.pop_usize()?; // Any size bigger than a usize would MemoryOOG.
+
+ let words_size = bytes_32_words_size(size).into();
+ let copy_gas_cost = gas::COPY * words_size;
+ let memory_expansion = gas::memory_expansion(
+ self.memory.size(), [(dest_offset, size)].span()
+ )?;
+ self.memory.ensure_length(memory_expansion.new_size);
+
+ let total_cost = gas::VERYLOW
+ .checked_add(copy_gas_cost)
+ .ok_or(EVMError::OutOfGas)?
+ .checked_add(memory_expansion.expansion_cost)
+ .ok_or(EVMError::OutOfGas)?;
+ self.charge_gas(total_cost)?;
+
+ let calldata: Span = self.message().data;
+ copy_bytes_to_memory(ref self, calldata, dest_offset, offset, size);
+ Result::Ok(())
+ }
+
+ /// 0x38 - CODESIZE
+ /// Get size of bytecode running in current environment.
+ /// # Specification: https://www.evm.codes/#38?fork=shanghai
+ fn exec_codesize(ref self: VM) -> Result<(), EVMError> {
+ self.charge_gas(gas::BASE)?;
+ let size: u256 = self.message().code.len().into();
+ self.stack.push(size)
+ }
+
+ /// 0x39 - CODECOPY
+ /// Copies slice of bytecode to memory.
+ /// # Specification: https://www.evm.codes/#39?fork=shanghai
+ fn exec_codecopy(ref self: VM) -> Result<(), EVMError> {
+ let dest_offset = self.stack.pop_saturating_usize()?;
+ let offset = self.stack.pop_saturating_usize()?;
+ let size = self.stack.pop_usize()?; // Any size bigger than a usize would MemoryOOG.
+
+ let words_size = bytes_32_words_size(size).into();
+ let copy_gas_cost = gas::COPY * words_size;
+ let memory_expansion = gas::memory_expansion(
+ self.memory.size(), [(dest_offset, size)].span()
+ )?;
+ self.memory.ensure_length(memory_expansion.new_size);
+
+ let total_cost = gas::VERYLOW
+ .checked_add(copy_gas_cost)
+ .ok_or(EVMError::OutOfGas)?
+ .checked_add(memory_expansion.expansion_cost)
+ .ok_or(EVMError::OutOfGas)?;
+ self.charge_gas(total_cost)?;
+
+ let bytecode: Span = self.message().code;
+
+ copy_bytes_to_memory(ref self, bytecode, dest_offset, offset, size);
+ Result::Ok(())
+ }
+
+ /// 0x3A - GASPRICE
+ /// Get price of gas in current environment.
+ /// # Specification: https://www.evm.codes/#3a?fork=shanghai
+ fn exec_gasprice(ref self: VM) -> Result<(), EVMError> {
+ self.charge_gas(gas::BASE)?;
+ self.stack.push(self.env.gas_price.into())
+ }
+
+ /// 0x3B - EXTCODESIZE
+ /// Get size of an account's code.
+ /// # Specification: https://www.evm.codes/#3b?fork=shanghai
+ fn exec_extcodesize(ref self: VM) -> Result<(), EVMError> {
+ let evm_address = self.stack.pop_eth_address()?;
+
+ // GAS
+ if self.accessed_addresses.contains(evm_address) {
+ self.charge_gas(gas::WARM_ACCESS_COST)?;
+ } else {
+ self.accessed_addresses.add(evm_address);
+ self.charge_gas(gas::COLD_ACCOUNT_ACCESS_COST)?
+ }
+
+ let account = self.env.state.get_account(evm_address);
+ self.stack.push(account.code.len().into())
+ }
+
+ /// 0x3C - EXTCODECOPY
+ /// Copy an account's code to memory
+ /// # Specification: https://www.evm.codes/#3c?fork=shanghai
+ fn exec_extcodecopy(ref self: VM) -> Result<(), EVMError> {
+ let evm_address = self.stack.pop_eth_address()?;
+ let dest_offset = self.stack.pop_saturating_usize()?;
+ let offset = self.stack.pop_saturating_usize()?;
+ let size = self.stack.pop_usize()?; // Any size bigger than a usize would MemoryOOG.
+
+ // GAS
+ let words_size = bytes_32_words_size(size).into();
+ let memory_expansion = gas::memory_expansion(
+ self.memory.size(), [(dest_offset, size)].span()
+ )?;
+ self.memory.ensure_length(memory_expansion.new_size);
+ let copy_gas_cost = gas::COPY * words_size;
+ let access_gas_cost = if self.accessed_addresses.contains(evm_address) {
+ gas::WARM_ACCESS_COST
+ } else {
+ self.accessed_addresses.add(evm_address);
+ gas::COLD_ACCOUNT_ACCESS_COST
+ };
+ let total_cost = access_gas_cost
+ .checked_add(copy_gas_cost)
+ .ok_or(EVMError::OutOfGas)?
+ .checked_add(memory_expansion.expansion_cost)
+ .ok_or(EVMError::OutOfGas)?;
+ self.charge_gas(total_cost)?;
+
+ let bytecode = self.env.state.get_account(evm_address).code;
+ copy_bytes_to_memory(ref self, bytecode, dest_offset, offset, size);
+ Result::Ok(())
+ }
+
+ /// 0x3D - RETURNDATASIZE
+ /// Get the size of return data.
+ /// # Specification: https://www.evm.codes/#3d?fork=shanghai
+ fn exec_returndatasize(ref self: VM) -> Result<(), EVMError> {
+ self.charge_gas(gas::BASE)?;
+ let size = self.return_data().len();
+ self.stack.push(size.into())
+ }
+
+ /// 0x3E - RETURNDATACOPY
+ /// Save word to memory.
+ /// # Specification: https://www.evm.codes/#3e?fork=shanghai
+ fn exec_returndatacopy(ref self: VM) -> Result<(), EVMError> {
+ let dest_offset = self.stack.pop_saturating_usize()?;
+ let offset = self.stack.pop_saturating_usize()?;
+ let size = self.stack.pop_usize()?; // Any size bigger than a usize would MemoryOOG.
+ let return_data: Span = self.return_data();
+
+ let (last_returndata_index, overflow) = offset.overflowing_add(size);
+ if overflow {
+ return Result::Err(EVMError::ReturnDataOutOfBounds);
+ }
+ ensure(!(last_returndata_index > return_data.len()), EVMError::ReturnDataOutOfBounds)?;
+
+ let words_size = bytes_32_words_size(size).into();
+ let copy_gas_cost = gas::COPY * words_size;
+
+ let memory_expansion = gas::memory_expansion(
+ self.memory.size(), [(dest_offset, size)].span()
+ )?;
+ self.memory.ensure_length(memory_expansion.new_size);
+ let total_cost = gas::VERYLOW
+ .checked_add(copy_gas_cost)
+ .ok_or(EVMError::OutOfGas)?
+ .checked_add(memory_expansion.expansion_cost)
+ .ok_or(EVMError::OutOfGas)?;
+ self.charge_gas(total_cost)?;
+
+ let data_to_copy: Span = return_data.slice(offset, size);
+ self.memory.store_n(data_to_copy, dest_offset);
+
+ Result::Ok(())
+ }
+
+ /// 0x3F - EXTCODEHASH
+ /// Get hash of a contract's code.
+ // If the account has no code, return the empty hash:
+ // `0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470`
+ // If the account does not exist, is a precompile or was destroyed (SELFDESTRUCT), return 0
+ // Else return, the hash of the account's code
+ /// # Specification: https://www.evm.codes/#3f?fork=shanghai
+ fn exec_extcodehash(ref self: VM) -> Result<(), EVMError> {
+ let evm_address = self.stack.pop_eth_address()?;
+
+ // GAS
+ if self.accessed_addresses.contains(evm_address) {
+ self.charge_gas(gas::WARM_ACCESS_COST)?;
+ } else {
+ self.accessed_addresses.add(evm_address);
+ self.charge_gas(gas::COLD_ACCOUNT_ACCESS_COST)?
+ }
+
+ let account = self.env.state.get_account(evm_address);
+ // Relevant cases:
+ // https://github.com/ethereum/go-ethereum/blob/master/core/vm/instructions.go#L392
+ if account.evm_address().is_precompile()
+ || (!account.has_code_or_nonce() && account.balance.is_zero()) {
+ return self.stack.push(0);
+ }
+ self.stack.push(account.code_hash)
+ }
+}
+
+#[inline(always)]
+fn copy_bytes_to_memory(
+ ref self: VM, bytes: Span, dest_offset: usize, offset: usize, size: usize
+) {
+ let bytes_slice = match bytes.len().checked_sub(offset) {
+ Option::Some(remaining) => bytes.slice(offset, core::cmp::min(size, remaining)),
+ Option::None => [].span()
+ };
+
+ self.memory.store_padded_segment(dest_offset, size, bytes_slice);
+}
+
+
+#[cfg(test)]
+mod tests {
+ use contracts::test_data::counter_evm_bytecode;
+ use core::starknet::EthAddress;
+ use crate::errors::{EVMError, TYPE_CONVERSION_ERROR};
+ use crate::instructions::EnvironmentInformationTrait;
+ use crate::memory::{InternalMemoryTrait, MemoryTrait};
+
+ use crate::model::vm::VMTrait;
+ use crate::model::{Account, Address};
+ use crate::stack::StackTrait;
+ use crate::state::StateTrait;
+ use crate::test_utils::{VMBuilderTrait, origin, callvalue, gas_price};
+ use snforge_std::test_address;
+ use utils::constants::EMPTY_KECCAK;
+ use utils::helpers::{u256_to_bytes_array, compute_starknet_address};
+ use utils::traits::array::ArrayExtTrait;
+ use utils::traits::bytes::{U8SpanExTrait};
+ use utils::traits::{EthAddressIntoU256};
+
+
+ mod test_internals {
+ use crate::memory::MemoryTrait;
+ use crate::test_utils::VMBuilderTrait;
+ use super::super::copy_bytes_to_memory;
+
+ fn test_copy_bytes_to_memory_helper(
+ bytes: Span, dest_offset: usize, offset: usize, size: usize, expected: Span
+ ) {
+ // Given
+ let mut vm = VMBuilderTrait::new_with_presets().build();
+
+ // When
+ copy_bytes_to_memory(ref vm, bytes, dest_offset, offset, size);
+
+ // Then
+ let mut result = ArrayTrait::new();
+ vm.memory.load_n(size, ref result, dest_offset);
+ assert_eq!(result.span(), expected);
+ }
+
+ #[test]
+ fn test_copy_bytes_to_memory_normal_case() {
+ let bytes = [1, 2, 3, 4, 5].span();
+ test_copy_bytes_to_memory_helper(bytes, 0, 0, 5, bytes);
+ }
+
+ #[test]
+ fn test_copy_bytes_to_memory_with_offset() {
+ let bytes = [1, 2, 3, 4, 5].span();
+ test_copy_bytes_to_memory_helper(bytes, 0, 2, 3, [3, 4, 5].span());
+ }
+
+ #[test]
+ fn test_copy_bytes_to_memory_size_larger_than_remaining_bytes() {
+ let bytes = [1, 2, 3, 4, 5].span();
+ test_copy_bytes_to_memory_helper(bytes, 0, 3, 5, [4, 5, 0, 0, 0].span());
+ }
+
+ #[test]
+ fn test_copy_bytes_to_memory_offset_out_of_bounds() {
+ let bytes = [1, 2, 3, 4, 5].span();
+ test_copy_bytes_to_memory_helper(bytes, 0, 10, 5, [0, 0, 0, 0, 0].span());
+ }
+
+ #[test]
+ fn test_copy_bytes_to_memory_zero_size() {
+ let bytes = [1, 2, 3, 4, 5].span();
+ test_copy_bytes_to_memory_helper(bytes, 0, 0, 0, [].span());
+ }
+
+ #[test]
+ fn test_copy_bytes_to_memory_non_zero_dest_offset() {
+ let bytes = [1, 2, 3, 4, 5].span();
+ test_copy_bytes_to_memory_helper(bytes, 10, 0, 5, bytes);
+ }
+ }
+
+ // *************************************************************************
+ // 0x30: ADDRESS
+ // *************************************************************************
+
+ #[test]
+ fn test_address_basic() {
+ // Given
+ let mut vm = VMBuilderTrait::new_with_presets().build();
+
+ // When
+ vm.exec_address().expect('exec_address failed');
+
+ // Then
+ assert_eq!(vm.stack.len(), 1);
+ assert_eq!(vm.stack.pop_eth_address().unwrap(), vm.message().target.evm.into());
+ }
+
+ // *************************************************************************
+ // 0x31: BALANCE
+ // *************************************************************************
+ #[test]
+ fn test_exec_balance_eoa() {
+ // Given
+ let mut vm = VMBuilderTrait::new_with_presets().build();
+ let account = Account {
+ address: vm.message().target,
+ balance: 400,
+ nonce: 0,
+ code: [].span(),
+ code_hash: EMPTY_KECCAK,
+ selfdestruct: false,
+ is_created: true,
+ };
+ vm.env.state.set_account(account);
+ vm.stack.push(vm.message.target.evm.into()).unwrap();
+
+ // When
+ vm.exec_balance().expect('exec_balance failed');
+
+ // Then
+ assert_eq!(vm.stack.peek().unwrap(), 400);
+ }
+
+ // *************************************************************************
+ // 0x33: CALLER
+ // *************************************************************************
+ #[test]
+ fn test_caller() {
+ // Given
+ let mut vm = VMBuilderTrait::new_with_presets().build();
+
+ // When
+ vm.exec_caller().expect('exec_caller failed');
+
+ // Then
+ assert_eq!(vm.stack.len(), 1);
+ assert_eq!(vm.stack.peek().unwrap(), origin().into());
+ }
+
+
+ // *************************************************************************
+ // 0x32: ORIGIN
+ // *************************************************************************
+ #[test]
+ fn test_origin_nested_ctx() {
+ // Given
+ let mut vm = VMBuilderTrait::new_with_presets().build();
+
+ // When
+ vm.exec_origin().expect('exec_origin failed');
+
+ // Then
+ assert_eq!(vm.stack.len(), 1);
+ assert_eq!(vm.stack.peek().unwrap(), vm.env.origin.evm.into());
+ }
+
+
+ // *************************************************************************
+ // 0x34: CALLVALUE
+ // *************************************************************************
+ #[test]
+ fn test_exec_callvalue() {
+ // Given
+ let mut vm = VMBuilderTrait::new_with_presets().build();
+
+ // When
+ vm.exec_callvalue().expect('exec_callvalue failed');
+
+ // Then
+ assert_eq!(vm.stack.len(), 1);
+ assert_eq!(vm.stack.pop().unwrap(), callvalue());
+ }
+
+ // *************************************************************************
+ // 0x35: CALLDATALOAD
+ // *************************************************************************
+
+ #[test]
+ fn test_calldataload() {
+ // Given
+ let calldata = u256_to_bytes_array(
+ 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+ );
+ let mut vm = VMBuilderTrait::new_with_presets().with_calldata(calldata.span()).build();
+
+ let offset: u32 = 0;
+ vm.stack.push(offset.into()).expect('push failed');
+
+ // When
+ vm.exec_calldataload().expect('exec_calldataload failed');
+
+ // Then
+ let result: u256 = vm.stack.pop().unwrap();
+ assert_eq!(result, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF);
+ }
+
+ #[test]
+ fn test_calldataload_with_offset() {
+ // Given
+ let calldata = u256_to_bytes_array(
+ 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+ );
+
+ let mut vm = VMBuilderTrait::new_with_presets().with_calldata(calldata.span()).build();
+
+ let offset: u32 = 31;
+ vm.stack.push(offset.into()).expect('push failed');
+
+ // When
+ vm.exec_calldataload().expect('exec_calldataload failed');
+
+ // Then
+ let result: u256 = vm.stack.pop().unwrap();
+
+ assert_eq!(result, 0xFF00000000000000000000000000000000000000000000000000000000000000);
+ }
+
+ #[test]
+ fn test_calldataload_with_offset_beyond_calldata() {
+ // Given
+ let calldata = u256_to_bytes_array(
+ 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+ );
+ let mut vm = VMBuilderTrait::new_with_presets().with_calldata(calldata.span()).build();
+
+ let offset: u32 = calldata.len() + 1;
+ vm.stack.push(offset.into()).expect('push failed');
+
+ // When
+ vm.exec_calldataload().expect('exec_calldataload failed');
+
+ // Then
+ let result: u256 = vm.stack.pop().unwrap();
+ assert_eq!(result, 0);
+ }
+
+ #[test]
+ fn test_calldataload_with_function_selector() {
+ // Given
+ let calldata = array![0x6d, 0x4c, 0xe6, 0x3c];
+ let mut vm = VMBuilderTrait::new_with_presets().with_calldata(calldata.span()).build();
+
+ let offset: u32 = 0;
+ vm.stack.push(offset.into()).expect('push failed');
+
+ // When
+ vm.exec_calldataload().expect('exec_calldataload failed');
+
+ // Then
+ let result: u256 = vm.stack.pop().unwrap();
+ assert_eq!(result, 0x6d4ce63c00000000000000000000000000000000000000000000000000000000);
+ }
+
+
+ #[test]
+ fn test_calldataload_with_offset_bigger_usize_succeeds() {
+ // Given
+ let calldata = u256_to_bytes_array(
+ 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+ );
+ let mut vm = VMBuilderTrait::new_with_presets().with_calldata(calldata.span()).build();
+ let offset: u256 = 5000000000;
+ vm.stack.push(offset).expect('push failed');
+
+ // When
+ let result = vm.exec_calldataload();
+
+ // Then
+ assert!(result.is_ok());
+ assert_eq!(vm.stack.pop().unwrap(), 0);
+ }
+
+ // *************************************************************************
+ // 0x36: CALLDATASIZE
+ // *************************************************************************
+
+ #[test]
+ fn test_calldata_size() {
+ // Given
+ let mut vm = VMBuilderTrait::new_with_presets().build();
+
+ let calldata: Span = vm.message.data;
+
+ // When
+ vm.exec_calldatasize().expect('exec_calldatasize failed');
+
+ // Then
+ assert_eq!(vm.stack.len(), 1);
+ assert_eq!(vm.stack.peek().unwrap(), calldata.len().into());
+ }
+
+ // *************************************************************************
+ // 0x37: CALLDATACOPY
+ // *************************************************************************
+
+ #[test]
+ fn test_calldatacopy_type_conversion_error() {
+ // Given
+ let mut vm = VMBuilderTrait::new_with_presets().build();
+
+ vm
+ .stack
+ .push(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)
+ .expect('push failed');
+ vm
+ .stack
+ .push(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)
+ .expect('push failed');
+ vm
+ .stack
+ .push(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)
+ .expect('push failed');
+
+ // When
+ let res = vm.exec_calldatacopy();
+
+ // Then
+ assert!(res.is_err());
+ assert_eq!(res.unwrap_err(), EVMError::TypeConversionError(TYPE_CONVERSION_ERROR));
+ }
+
+ #[test]
+ fn test_calldatacopy_basic() {
+ test_calldatacopy(32, 0, 3, [4, 5, 6].span());
+ }
+
+ #[test]
+ fn test_calldatacopy_with_out_of_bound_bytes() {
+ // For out of bound bytes, 0s will be copied.
+ let mut expected = array![4, 5, 6];
+ expected.append_n(0, 5);
+
+ test_calldatacopy(32, 0, 8, expected.span());
+ }
+
+ #[test]
+ fn test_calldatacopy_with_out_of_bound_bytes_multiple_words() {
+ // For out of bound bytes, 0s will be copied.
+ let mut expected = array![4, 5, 6];
+ expected.append_n(0, 31);
+
+ test_calldatacopy(32, 0, 34, expected.span());
+ }
+
+ fn test_calldatacopy(dest_offset: u32, offset: u32, mut size: u32, expected: Span) {
+ // Given
+ let mut vm = VMBuilderTrait::new_with_presets().build();
+
+ let _calldata: Span = vm.message.data;
+
+ vm.stack.push(size.into()).expect('push failed');
+ vm.stack.push(offset.into()).expect('push failed');
+ vm.stack.push(dest_offset.into()).expect('push failed');
+
+ // Memory initialization with a value to verify that if the offset + size is out of the
+ // bound bytes, 0's have been copied.
+ // Otherwise, the memory value would be 0, and we wouldn't be able to check it.
+ for i in 0
+ ..(size / 32)
+ + 1 {
+ vm
+ .memory
+ .store(
+ 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF,
+ dest_offset + (i * 32)
+ );
+
+ let initial: u256 = vm.memory.load_internal(dest_offset + (i * 32)).into();
+
+ assert_eq!(
+ initial, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+ );
+ };
+
+ // When
+ vm.exec_calldatacopy().expect('exec_calldatacopy failed');
+
+ // Then
+ assert!(vm.stack.is_empty());
+
+ let mut results: Array = ArrayTrait::new();
+ vm.memory.load_n_internal(size, ref results, dest_offset);
+
+ assert_eq!(results.span(), expected);
+ }
+
+ // *************************************************************************
+ // 0x38: CODESIZE
+ // *************************************************************************
+
+ #[test]
+ fn test_codesize() {
+ // Given
+ let bytecode: Span = [1, 2, 3, 4, 5].span();
+
+ let mut vm = VMBuilderTrait::new_with_presets().with_bytecode(bytecode).build();
+
+ // When
+ vm.exec_codesize().expect('exec_codesize failed');
+
+ // Then
+ assert_eq!(vm.stack.len(), 1);
+ assert_eq!(vm.stack.pop().unwrap(), bytecode.len().into());
+ }
+
+ // *************************************************************************
+ // 0x39: CODECOPY
+ // *************************************************************************
+
+ #[test]
+ fn test_codecopy_type_conversion_error() {
+ // Given
+ let bytecode: Span = [1, 2, 3, 4, 5].span();
+
+ let mut vm = VMBuilderTrait::new_with_presets().with_bytecode(bytecode).build();
+
+ vm
+ .stack
+ .push(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)
+ .expect('push failed');
+ vm
+ .stack
+ .push(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)
+ .expect('push failed');
+ vm
+ .stack
+ .push(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)
+ .expect('push failed');
+
+ // When
+ let res = vm.exec_codecopy();
+
+ // Then
+ assert!(res.is_err());
+ assert_eq!(res.unwrap_err(), EVMError::TypeConversionError(TYPE_CONVERSION_ERROR));
+ }
+
+ #[test]
+ fn test_codecopy_basic() {
+ test_codecopy(32, 0, 0);
+ }
+
+ #[test]
+ fn test_codecopy_with_out_of_bound_bytes() {
+ test_codecopy(32, 0, 8);
+ }
+
+ #[test]
+ fn test_codecopy_with_out_of_bound_offset() {
+ test_codecopy(0, 0xFFFFFFFE, 2);
+ }
+
+ fn test_codecopy(dest_offset: u32, offset: u32, mut size: u32) {
+ // Given
+ let bytecode: Span = [1, 2, 3, 4, 5].span();
+
+ let mut vm = VMBuilderTrait::new_with_presets().with_bytecode(bytecode).build();
+
+ if (size == 0) {
+ size = bytecode.len() - offset;
+ }
+
+ vm.stack.push(size.into()).expect('push failed');
+ vm.stack.push(offset.into()).expect('push failed');
+ vm.stack.push(dest_offset.into()).expect('push failed');
+
+ vm
+ .memory
+ .store(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF, dest_offset);
+ let initial: u256 = vm.memory.load_internal(dest_offset).into();
+ assert_eq!(initial, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF);
+
+ // When
+ vm.exec_codecopy().expect('exec_codecopy failed');
+
+ // Then
+ assert!(vm.stack.is_empty());
+
+ let result: u256 = vm.memory.load_internal(dest_offset).into();
+ let mut results: Array = u256_to_bytes_array(result);
+
+ for i in 0
+ ..size {
+ // For out of bound bytes, 0s will be copied.
+ if (i + offset >= bytecode.len()) {
+ assert_eq!(*results[i], 0);
+ } else {
+ assert_eq!(*results[i], *bytecode[i + offset]);
+ }
+ };
+ }
+
+ // *************************************************************************
+ // 0x3A: GASPRICE
+ // *************************************************************************
+
+ #[test]
+ fn test_gasprice() {
+ // Given
+ let mut vm = VMBuilderTrait::new_with_presets().build();
+
+ // When
+ vm.exec_gasprice().expect('exec_gasprice failed');
+
+ // Then
+ assert_eq!(vm.stack.len(), 1);
+ assert_eq!(vm.stack.peek().unwrap(), gas_price().into());
+ }
+
+ // *************************************************************************
+ // 0x3B - EXTCODESIZE
+ // *************************************************************************
+ #[test]
+ fn test_exec_extcodesize_should_push_bytecode_len_0() {
+ // Given
+ let mut vm = VMBuilderTrait::new_with_presets().build();
+ let account = Account {
+ address: vm.message().target,
+ balance: 1,
+ nonce: 1,
+ code: [].span(),
+ code_hash: EMPTY_KECCAK,
+ selfdestruct: false,
+ is_created: true,
+ };
+ vm.env.state.set_account(account);
+ vm.stack.push(account.address.evm.into()).expect('push failed');
+
+ // When
+ vm.exec_extcodesize().unwrap();
+
+ // Then
+ assert_eq!(vm.stack.peek().unwrap(), 0);
+ }
+
+ #[test]
+ fn test_exec_extcodesize_should_push_bytecode_len() {
+ // Given
+ let mut vm = VMBuilderTrait::new_with_presets().build();
+ let bytecode = [0xff; 350].span();
+ let code_hash = bytecode.compute_keccak256_hash();
+ let account = Account {
+ address: vm.message().target,
+ balance: 1,
+ nonce: 1,
+ code: bytecode,
+ code_hash,
+ selfdestruct: false,
+ is_created: true,
+ };
+ vm.env.state.set_account(account);
+ vm.stack.push(account.address.evm.into()).expect('push failed');
+
+ // When
+ vm.exec_extcodesize().unwrap();
+
+ // Then
+ assert_eq!(vm.stack.peek().unwrap(), 350);
+ }
+
+ // *************************************************************************
+ // 0x3C - EXTCODECOPY
+ // *************************************************************************
+
+ #[test]
+ fn test_exec_extcodecopy_should_copy_code_of_input_account() {
+ // Given
+ let mut vm = VMBuilderTrait::new_with_presets().build();
+ let bytecode = counter_evm_bytecode();
+ let code_hash = bytecode.compute_keccak256_hash();
+ let account = Account {
+ address: vm.message().target,
+ balance: 1,
+ nonce: 1,
+ code: bytecode,
+ code_hash,
+ selfdestruct: false,
+ is_created: true,
+ };
+ vm.env.state.set_account(account);
+
+ vm.stack.push(account.address.evm.into()).expect('push failed');
+ // size
+ vm.stack.push(50).expect('push failed');
+ // offset
+ vm.stack.push(200).expect('push failed');
+ // destOffset (memory offset)
+ vm.stack.push(20).expect('push failed');
+ vm.stack.push(account.address.evm.into()).unwrap();
+
+ // When
+ vm.exec_extcodecopy().unwrap();
+
+ // Then
+ let mut bytecode_slice = array![];
+ vm.memory.load_n(50, ref bytecode_slice, 20);
+ assert_eq!(bytecode_slice.span(), account.code.slice(200, 50));
+ }
+
+ #[test]
+ fn test_exec_extcodecopy_ca_offset_out_of_bounds_should_return_zeroes() {
+ // Given
+ let mut vm = VMBuilderTrait::new_with_presets().build();
+ let bytecode = counter_evm_bytecode();
+ let code_hash = bytecode.compute_keccak256_hash();
+ let account = Account {
+ address: vm.message().target,
+ balance: 1,
+ nonce: 1,
+ code: bytecode,
+ code_hash,
+ selfdestruct: false,
+ is_created: true,
+ };
+ vm.env.state.set_account(account);
+ vm.stack.push(account.address.evm.into()).expect('push failed');
+
+ // size
+ vm.stack.push(5).expect('push failed');
+ // offset
+ vm.stack.push(5000).expect('push failed');
+ // destOffset
+ vm.stack.push(20).expect('push failed');
+ vm.stack.push(account.address.evm.into()).expect('push failed');
+
+ // When
+ vm.exec_extcodecopy().unwrap();
+
+ // Then
+ let mut bytecode_slice = array![];
+ vm.memory.load_n(5, ref bytecode_slice, 20);
+ assert_eq!(bytecode_slice.span(), [0, 0, 0, 0, 0].span());
+ }
+
+ #[test]
+ fn test_exec_returndatasize() {
+ // Given
+ let return_data: Array = array![1, 2, 3, 4, 5];
+ let size = return_data.len();
+
+ let mut vm = VMBuilderTrait::new_with_presets()
+ .with_return_data(return_data.span())
+ .build();
+
+ vm.exec_returndatasize().expect('exec_returndatasize failed');
+
+ // Then
+ assert_eq!(vm.stack.len(), 1);
+ assert_eq!(vm.stack.pop().unwrap(), size.into());
+ }
+
+ // *************************************************************************
+ // 0x3E: RETURNDATACOPY
+ // *************************************************************************
+
+ #[test]
+ fn test_returndata_copy_type_conversion_error() {
+ // Given
+ let return_data: Array = array![1, 2, 3, 4, 5];
+ let mut vm = VMBuilderTrait::new_with_presets()
+ .with_return_data(return_data.span())
+ .build();
+
+ vm
+ .stack
+ .push(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)
+ .expect('push failed');
+ vm
+ .stack
+ .push(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)
+ .expect('push failed');
+ vm
+ .stack
+ .push(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)
+ .expect('push failed');
+
+ // When
+ let res = vm.exec_returndatacopy();
+
+ // Then
+ assert_eq!(res.unwrap_err(), EVMError::TypeConversionError(TYPE_CONVERSION_ERROR));
+ }
+
+ fn test_returndatacopy_helper(
+ return_data: Span,
+ dest_offset: u32,
+ offset: u32,
+ size: u32,
+ expected_result: Result, EVMError>
+ ) {
+ // Given
+ let mut vm = VMBuilderTrait::new_with_presets().with_return_data(return_data).build();
+
+ vm.stack.push(size.into()).expect('push failed');
+ vm.stack.push(offset.into()).expect('push failed');
+ vm.stack.push(dest_offset.into()).expect('push failed');
+
+ // When
+ let res = vm.exec_returndatacopy();
+
+ // Then
+ match expected_result {
+ Result::Ok(expected) => {
+ assert!(res.is_ok());
+ let mut result = ArrayTrait::new();
+ vm
+ .memory
+ .load_n(size.try_into().unwrap(), ref result, dest_offset.try_into().unwrap());
+ assert_eq!(result.span(), expected);
+ },
+ Result::Err(expected_error) => {
+ assert!(res.is_err());
+ assert_eq!(res.unwrap_err(), expected_error);
+ }
+ }
+ }
+
+ #[test]
+ fn test_returndatacopy_basic() {
+ let return_data = array![1, 2, 3, 4, 5].span();
+ test_returndatacopy_helper(return_data, 0, 0, 5, Result::Ok(return_data));
+ }
+
+ #[test]
+ fn test_returndatacopy_with_offset() {
+ let return_data = array![1, 2, 3, 4, 5].span();
+ test_returndatacopy_helper(return_data, 0, 2, 3, Result::Ok([3, 4, 5].span()));
+ }
+
+ #[test]
+ fn test_returndatacopy_out_of_bounds() {
+ let return_data = array![1, 2, 3, 4, 5].span();
+ test_returndatacopy_helper(
+ return_data, 0, 3, 3, Result::Err(EVMError::ReturnDataOutOfBounds)
+ );
+ }
+
+ #[test]
+ fn test_returndatacopy_overflowing_add() {
+ let return_data = array![1, 2, 3, 4, 5].span();
+ test_returndatacopy_helper(
+ return_data, 0, 0xFFFFFFFF, 1, Result::Err(EVMError::ReturnDataOutOfBounds)
+ );
+ }
+
+ // *************************************************************************
+ // 0x3F: EXTCODEHASH
+ // *************************************************************************
+ #[test]
+ fn test_exec_extcodehash_precompile() {
+ // Given
+ let mut vm = VMBuilderTrait::new_with_presets().build();
+ let precompile_evm_address: EthAddress = evm::precompiles::LAST_ETHEREUM_PRECOMPILE_ADDRESS
+ .try_into()
+ .unwrap();
+ let precompile_starknet_address = compute_starknet_address(
+ test_address(), precompile_evm_address, 0.try_into().unwrap()
+ );
+ let account = Account {
+ address: Address {
+ evm: precompile_evm_address, starknet: precompile_starknet_address,
+ },
+ balance: 1,
+ nonce: 0,
+ code: [].span(),
+ code_hash: EMPTY_KECCAK,
+ selfdestruct: false,
+ is_created: true,
+ };
+ vm.env.state.set_account(account);
+ vm.stack.push(precompile_evm_address.into()).expect('push failed');
+
+ // When
+ vm.exec_extcodehash().unwrap();
+
+ // Then
+ assert_eq!(vm.stack.peek().unwrap(), 0);
+ }
+
+ #[test]
+ fn test_exec_extcodehash_empty_account() {
+ // Given
+ let mut vm = VMBuilderTrait::new_with_presets().build();
+ let account = Account {
+ address: vm.message().target,
+ balance: 0,
+ nonce: 0,
+ code: [].span(),
+ code_hash: EMPTY_KECCAK,
+ selfdestruct: false,
+ is_created: true,
+ };
+ vm.env.state.set_account(account);
+ vm.stack.push(account.address.evm.into()).expect('push failed');
+
+ // When
+ vm.exec_extcodehash().unwrap();
+
+ // Then
+ assert_eq!(vm.stack.peek().unwrap(), 0);
+ }
+
+ #[test]
+ fn test_exec_extcodehash_no_bytecode() {
+ // Given
+ let mut vm = VMBuilderTrait::new_with_presets().build();
+ let account = Account {
+ address: vm.message().target,
+ balance: 1,
+ nonce: 1,
+ code: [].span(),
+ code_hash: EMPTY_KECCAK,
+ selfdestruct: false,
+ is_created: true,
+ };
+ vm.env.state.set_account(account);
+ vm.stack.push(account.address.evm.into()).expect('push failed');
+
+ // When
+ vm.exec_extcodehash().unwrap();
+
+ // Then
+ assert_eq!(vm.stack.peek().unwrap(), EMPTY_KECCAK);
+ }
+
+ #[test]
+ fn test_exec_extcodehash_with_bytecode() {
+ // Given
+ let mut vm = VMBuilderTrait::new_with_presets().build();
+ let bytecode = counter_evm_bytecode();
+ let code_hash = bytecode.compute_keccak256_hash();
+ let account = Account {
+ address: vm.message().target,
+ balance: 1,
+ nonce: 1,
+ code: bytecode,
+ code_hash,
+ selfdestruct: false,
+ is_created: true,
+ };
+ vm.env.state.set_account(account);
+ vm.stack.push(account.address.evm.into()).expect('push failed');
+
+ // When
+ vm.exec_extcodehash().unwrap();
+
+ // Then
+ assert_eq!(
+ vm.stack.peek() // extcodehash(Counter.sol) :=
+ // 0x82abf19c13d2262cc530f54956af7e4ec1f45f637238ed35ed7400a3409fd275 (source:
+ // remix)
+ //
+ .unwrap(),
+ 0xec976f44607e73ea88910411e3da156757b63bea5547b169e1e0d733443f73b0,
+ );
+ }
+}
diff --git a/cairo/kakarot-ssj/crates/evm/src/instructions/exchange_operations.cairo b/cairo/kakarot-ssj/crates/evm/src/instructions/exchange_operations.cairo
new file mode 100644
index 000000000..349e0f3c1
--- /dev/null
+++ b/cairo/kakarot-ssj/crates/evm/src/instructions/exchange_operations.cairo
@@ -0,0 +1,439 @@
+//! Exchange Operations.
+
+use crate::errors::EVMError;
+use crate::gas;
+use crate::model::vm::{VM, VMTrait};
+use crate::stack::StackTrait;
+
+/// Place i bytes items on stack.
+#[inline(always)]
+fn exec_swap_i(ref self: VM, i: u8) -> Result<(), EVMError> {
+ self.charge_gas(gas::VERYLOW)?;
+ self.stack.swap_i(i.into())
+}
+
+#[generate_trait]
+pub impl ExchangeOperations of ExchangeOperationsTrait {
+ /// 0x90 - SWAP1 operation
+ /// Exchange 1st and 2nd stack items.
+ /// # Specification: https://www.evm.codes/#90?fork=shanghai
+
+ fn exec_swap1(ref self: VM) -> Result<(), EVMError> {
+ exec_swap_i(ref self, 1)
+ }
+
+ /// 0x91 - SWAP2 operation
+ /// Exchange 1st and 3rd stack items.
+ /// # Specification: https://www.evm.codes/#91?fork=shanghai
+ fn exec_swap2(ref self: VM) -> Result<(), EVMError> {
+ exec_swap_i(ref self, 2)
+ }
+
+ /// 0x92 - SWAP3 operation
+ /// Exchange 1st and 4th stack items.
+ /// # Specification: https://www.evm.codes/#92?fork=shanghai
+ fn exec_swap3(ref self: VM) -> Result<(), EVMError> {
+ exec_swap_i(ref self, 3)
+ }
+
+ /// 0x93 - SWAP4 operation
+ /// Exchange 1st and 5th stack items.
+ /// # Specification: https://www.evm.codes/#93?fork=shanghai
+ fn exec_swap4(ref self: VM) -> Result<(), EVMError> {
+ exec_swap_i(ref self, 4)
+ }
+
+ /// 0x94 - SWAP5 operation
+ /// Exchange 1st and 6th stack items.
+ /// # Specification: https://www.evm.codes/#94?fork=shanghai
+ fn exec_swap5(ref self: VM) -> Result<(), EVMError> {
+ exec_swap_i(ref self, 5)
+ }
+
+ /// 0x95 - SWAP6 operation
+ /// Exchange 1st and 7th stack items.
+ /// # Specification: https://www.evm.codes/#95?fork=shanghai
+ fn exec_swap6(ref self: VM) -> Result<(), EVMError> {
+ exec_swap_i(ref self, 6)
+ }
+
+ /// 0x96 - SWAP7 operation
+ /// Exchange 1st and 8th stack items.
+ /// # Specification: https://www.evm.codes/#96?fork=shanghai
+ fn exec_swap7(ref self: VM) -> Result<(), EVMError> {
+ exec_swap_i(ref self, 7)
+ }
+
+ /// 0x97 - SWAP8 operation
+ /// Exchange 1st and 9th stack items.
+ /// # Specification: https://www.evm.codes/#97?fork=shanghai
+ fn exec_swap8(ref self: VM) -> Result<(), EVMError> {
+ exec_swap_i(ref self, 8)
+ }
+
+ /// 0x98 - SWAP9 operation
+ /// Exchange 1st and 10th stack items.
+ /// # Specification: https://www.evm.codes/#98?fork=shanghai
+ fn exec_swap9(ref self: VM) -> Result<(), EVMError> {
+ exec_swap_i(ref self, 9)
+ }
+
+ /// 0x99 - SWAP10 operation
+ /// Exchange 1st and 11th stack items.
+ /// # Specification: https://www.evm.codes/#99?fork=shanghai
+ fn exec_swap10(ref self: VM) -> Result<(), EVMError> {
+ exec_swap_i(ref self, 10)
+ }
+
+ /// 0x9A - SWAP11 operation
+ /// Exchange 1st and 12th stack items.
+ /// # Specification: https://www.evm.codes/#9a?fork=shanghai
+ fn exec_swap11(ref self: VM) -> Result<(), EVMError> {
+ exec_swap_i(ref self, 11)
+ }
+
+ /// 0x9B - SWAP12 operation
+ /// Exchange 1st and 13th stack items.
+ /// # Specification: https://www.evm.codes/#9b?fork=shanghai
+ fn exec_swap12(ref self: VM) -> Result<(), EVMError> {
+ exec_swap_i(ref self, 12)
+ }
+
+ /// 0x9C - SWAP13 operation
+ /// Exchange 1st and 14th stack items.
+ /// # Specification: https://www.evm.codes/#9c?fork=shanghai
+ fn exec_swap13(ref self: VM) -> Result<(), EVMError> {
+ exec_swap_i(ref self, 13)
+ }
+
+ /// 0x9D - SWAP14 operation
+ /// Exchange 1st and 15th stack items.
+ /// # Specification: https://www.evm.codes/#9d?fork=shanghai
+ fn exec_swap14(ref self: VM) -> Result<(), EVMError> {
+ exec_swap_i(ref self, 14)
+ }
+
+ /// 0x9E - SWAP15 operation
+ /// Exchange 1st and 16th stack items.
+ /// # Specification: https://www.evm.codes/#9e?fork=shanghai
+ fn exec_swap15(ref self: VM) -> Result<(), EVMError> {
+ exec_swap_i(ref self, 15)
+ }
+
+ /// 0x9F - SWAP16 operation
+ /// Exchange 1st and 16th stack items.
+ /// # Specification: https://www.evm.codes/#9f?fork=shanghai
+ fn exec_swap16(ref self: VM) -> Result<(), EVMError> {
+ exec_swap_i(ref self, 16)
+ }
+}
+
+
+#[cfg(test)]
+mod tests {
+ use crate::instructions::exchange_operations::ExchangeOperationsTrait;
+ use crate::stack::StackTrait;
+ use crate::test_utils::VMBuilderTrait;
+
+
+ #[test]
+ fn test_exec_swap1() {
+ let mut vm = VMBuilderTrait::new_with_presets().build();
+ // given
+ vm.stack.push(0xf).expect('push failed');
+ vm.stack.push(1).expect('push failed');
+ vm.exec_swap1().expect('exec_swap1 failed');
+
+ assert(vm.stack.peek().unwrap() == 0xf, 'Top should be now 0xf');
+ assert(vm.stack.peek_at(1).unwrap() == 1, 'val at index 1 should be now 1');
+ }
+
+
+ #[test]
+ fn test_exec_swap2() {
+ let mut vm = VMBuilderTrait::new_with_presets().build();
+ // given
+ vm.stack.push(0xf).expect('push failed');
+ vm.stack.push(0).expect('push failed');
+ vm.stack.push(1).expect('push failed');
+ vm.exec_swap2().expect('exec_swap2 failed');
+ assert(vm.stack.peek().unwrap() == 0xf, 'Top should be now 0xf');
+ assert(vm.stack.peek_at(2).unwrap() == 1, 'val at index 2 should be now 1');
+ }
+
+ #[test]
+ fn test_exec_swap3() {
+ let mut vm = VMBuilderTrait::new_with_presets().build();
+ // given
+ vm.stack.push(0xf).expect('push failed');
+ vm.stack.push(0).expect('push failed');
+ vm.stack.push(0).expect('push failed');
+ vm.stack.push(1).expect('push failed');
+ vm.exec_swap3().expect('exec_swap3 failed');
+ assert(vm.stack.peek().unwrap() == 0xf, 'Top should be now 0xf');
+ assert(vm.stack.peek_at(3).unwrap() == 1, 'val at index 3 should be now 1');
+ }
+
+ #[test]
+ fn test_exec_swap4() {
+ let mut vm = VMBuilderTrait::new_with_presets().build();
+ // given
+ vm.stack.push(0xf).expect('push failed');
+ vm.stack.push(0).expect('push failed');
+ vm.stack.push(0).expect('push failed');
+ vm.stack.push(0).expect('push failed');
+ vm.stack.push(1).expect('push failed');
+ vm.exec_swap4().expect('exec_swap4 failed');
+ assert(vm.stack.peek().unwrap() == 0xf, 'Top should be now 0xf');
+ assert(vm.stack.peek_at(4).unwrap() == 1, 'val at index 4 should be now 1');
+ }
+
+
+ #[test]
+ fn test_exec_swap5() {
+ let mut vm = VMBuilderTrait::new_with_presets().build();
+ // given
+ vm.stack.push(0xf).expect('push failed');
+ vm.stack.push(0).expect('push failed');
+ vm.stack.push(0).expect('push failed');
+ vm.stack.push(0).expect('push failed');
+ vm.stack.push(0).expect('push failed');
+ vm.stack.push(1).expect('push failed');
+ vm.exec_swap5().expect('exec_swap5 failed');
+ assert(vm.stack.peek().unwrap() == 0xf, 'Top should be now 0xf');
+ assert(vm.stack.peek_at(5).unwrap() == 1, 'val at index 5 should be now 1');
+ }
+
+ #[test]
+ fn test_exec_swap6() {
+ let mut vm = VMBuilderTrait::new_with_presets().build();
+ // given
+ vm.stack.push(0xf).expect('push failed');
+ vm.stack.push(0).expect('push failed');
+ vm.stack.push(0).expect('push failed');
+ vm.stack.push(0).expect('push failed');
+ vm.stack.push(0).expect('push failed');
+ vm.stack.push(0).expect('push failed');
+ vm.stack.push(1).expect('push failed');
+ vm.exec_swap6().expect('exec_swap6 failed');
+ assert(vm.stack.peek().unwrap() == 0xf, 'Top should be now 0xf');
+ assert(vm.stack.peek_at(6).unwrap() == 1, 'val at index 6 should be now 1');
+ }
+
+
+ #[test]
+ fn test_exec_swap7() {
+ let mut vm = VMBuilderTrait::new_with_presets().build();
+ // given
+ vm.stack.push(0xf).expect('push failed');
+ vm.stack.push(0).expect('push failed');
+ vm.stack.push(0).expect('push failed');
+ vm.stack.push(0).expect('push failed');
+ vm.stack.push(0).expect('push failed');
+ vm.stack.push(0).expect('push failed');
+ vm.stack.push(0).expect('push failed');
+ vm.stack.push(1).expect('push failed');
+ vm.exec_swap7().expect('exec_swap7 failed');
+ assert(vm.stack.peek().unwrap() == 0xf, 'Top should be now 0xf');
+ assert(vm.stack.peek_at(7).unwrap() == 1, 'val at index 7 should be now 1');
+ }
+
+ #[test]
+ fn test_exec_swap8() {
+ let mut vm = VMBuilderTrait::new_with_presets().build();
+ // given
+ vm.stack.push(0xf).expect('push failed');
+ vm.stack.push(0).expect('push failed');
+ vm.stack.push(0).expect('push failed');
+ vm.stack.push(0).expect('push failed');
+ vm.stack.push(0).expect('push failed');
+ vm.stack.push(0).expect('push failed');
+ vm.stack.push(0).expect('push failed');
+ vm.stack.push(0).expect('push failed');
+ vm.stack.push(1).expect('push failed');
+ vm.exec_swap8().expect('exec_swap8 failed');
+ assert(vm.stack.peek().unwrap() == 0xf, 'Top should be now 0xf');
+ assert(vm.stack.peek_at(8).unwrap() == 1, 'val at index 8 should be now 1');
+ }
+
+
+ #[test]
+ fn test_exec_swap9() {
+ let mut vm = VMBuilderTrait::new_with_presets().build();
+ // given
+ vm.stack.push(0xf).expect('push failed');
+ vm.stack.push(0).expect('push failed');
+ vm.stack.push(0).expect('push failed');
+ vm.stack.push(0).expect('push failed');
+ vm.stack.push(0).expect('push failed');
+ vm.stack.push(0).expect('push failed');
+ vm.stack.push(0).expect('push failed');
+ vm.stack.push(0).expect('push failed');
+ vm.stack.push(0).expect('push failed');
+ vm.stack.push(1).expect('push failed');
+ vm.exec_swap9().expect('exec_swap9 failed');
+ assert(vm.stack.peek().unwrap() == 0xf, 'Top should be now 0xf');
+ assert(vm.stack.peek_at(9).unwrap() == 1, 'val at index 9 should be now 1');
+ }
+
+ #[test]
+ fn test_exec_swap10() {
+ let mut vm = VMBuilderTrait::new_with_presets().build();
+ // given
+ vm.stack.push(0xf).expect('push failed');
+ vm.stack.push(0).expect('push failed');
+ vm.stack.push(0).expect('push failed');
+ vm.stack.push(0).expect('push failed');
+ vm.stack.push(0).expect('push failed');
+ vm.stack.push(0).expect('push failed');
+ vm.stack.push(0).expect('push failed');
+ vm.stack.push(0).expect('push failed');
+ vm.stack.push(0).expect('push failed');
+ vm.stack.push(0).expect('push failed');
+ vm.stack.push(1).expect('push failed');
+ vm.exec_swap10().expect('exec_swap10 failed');
+ assert(vm.stack.peek().unwrap() == 0xf, 'Top should be now 0xf');
+ assert(vm.stack.peek_at(10).unwrap() == 1, 'val at index 10 should be now 1');
+ }
+
+ #[test]
+ fn test_exec_swap11() {
+ let mut vm = VMBuilderTrait::new_with_presets().build();
+ // given
+ vm.stack.push(0xf).expect('push failed');
+ vm.stack.push(0).expect('push failed');
+ vm.stack.push(0).expect('push failed');
+ vm.stack.push(0).expect('push failed');
+ vm.stack.push(0).expect('push failed');
+ vm.stack.push(0).expect('push failed');
+ vm.stack.push(0).expect('push failed');
+ vm.stack.push(0).expect('push failed');
+ vm.stack.push(0).expect('push failed');
+ vm.stack.push(0).expect('push failed');
+ vm.stack.push(0).expect('push failed');
+ vm.stack.push(1).expect('push failed');
+ vm.exec_swap11().expect('exec_swap11 failed');
+ assert(vm.stack.peek().unwrap() == 0xf, 'Top should be now 0xf');
+ assert(vm.stack.peek_at(11).unwrap() == 1, 'val at index 11 should be now 1');
+ }
+
+ #[test]
+ fn test_exec_swap12() {
+ let mut vm = VMBuilderTrait::new_with_presets().build();
+ // given
+ vm.stack.push(0xf).expect('push failed');
+ vm.stack.push(0).expect('push failed');
+ vm.stack.push(0).expect('push failed');
+ vm.stack.push(0).expect('push failed');
+ vm.stack.push(0).expect('push failed');
+ vm.stack.push(0).expect('push failed');
+ vm.stack.push(0).expect('push failed');
+ vm.stack.push(0).expect('push failed');
+ vm.stack.push(0).expect('push failed');
+ vm.stack.push(0).expect('push failed');
+ vm.stack.push(0).expect('push failed');
+ vm.stack.push(0).expect('push failed');
+ vm.stack.push(1).expect('push failed');
+ vm.exec_swap12().expect('exec_swap12 failed');
+ assert(vm.stack.peek().unwrap() == 0xf, 'Top should be now 0xf');
+ assert(vm.stack.peek_at(12).unwrap() == 1, 'val at index 12 should be now 1');
+ }
+
+ #[test]
+ fn test_exec_swap13() {
+ let mut vm = VMBuilderTrait::new_with_presets().build();
+ // given
+ vm.stack.push(0xf).expect('push failed');
+ vm.stack.push(0).expect('push failed');
+ vm.stack.push(0).expect('push failed');
+ vm.stack.push(0).expect('push failed');
+ vm.stack.push(0).expect('push failed');
+ vm.stack.push(0).expect('push failed');
+ vm.stack.push(0).expect('push failed');
+ vm.stack.push(0).expect('push failed');
+ vm.stack.push(0).expect('push failed');
+ vm.stack.push(0).expect('push failed');
+ vm.stack.push(0).expect('push failed');
+ vm.stack.push(0).expect('push failed');
+ vm.stack.push(0).expect('push failed');
+ vm.stack.push(1).expect('push failed');
+ vm.exec_swap13().expect('exec_swap13 failed');
+ assert(vm.stack.peek().unwrap() == 0xf, 'Top should be now 0xf');
+ assert(vm.stack.peek_at(13).unwrap() == 1, 'val at index 13 should be now 1');
+ }
+
+ #[test]
+ fn test_exec_swap14() {
+ let mut vm = VMBuilderTrait::new_with_presets().build();
+ // given
+ vm.stack.push(0xf).expect('push failed');
+ vm.stack.push(0).expect('push failed');
+ vm.stack.push(0).expect('push failed');
+ vm.stack.push(0).expect('push failed');
+ vm.stack.push(0).expect('push failed');
+ vm.stack.push(0).expect('push failed');
+ vm.stack.push(0).expect('push failed');
+ vm.stack.push(0).expect('push failed');
+ vm.stack.push(0).expect('push failed');
+ vm.stack.push(0).expect('push failed');
+ vm.stack.push(0).expect('push failed');
+ vm.stack.push(0).expect('push failed');
+ vm.stack.push(0).expect('push failed');
+ vm.stack.push(0).expect('push failed');
+ vm.stack.push(1).expect('push failed');
+ vm.exec_swap14().expect('exec_swap14 failed');
+ assert(vm.stack.peek().unwrap() == 0xf, 'Top should be now 0xf');
+ assert(vm.stack.peek_at(14).unwrap() == 1, 'val at index 14 should be now 1');
+ }
+
+ #[test]
+ fn test_exec_swap15() {
+ let mut vm = VMBuilderTrait::new_with_presets().build();
+ // given
+ vm.stack.push(0xf).expect('push failed');
+ vm.stack.push(0).expect('push failed');
+ vm.stack.push(0).expect('push failed');
+ vm.stack.push(0).expect('push failed');
+ vm.stack.push(0).expect('push failed');
+ vm.stack.push(0).expect('push failed');
+ vm.stack.push(0).expect('push failed');
+ vm.stack.push(0).expect('push failed');
+ vm.stack.push(0).expect('push failed');
+ vm.stack.push(0).expect('push failed');
+ vm.stack.push(0).expect('push failed');
+ vm.stack.push(0).expect('push failed');
+ vm.stack.push(0).expect('push failed');
+ vm.stack.push(0).expect('push failed');
+ vm.stack.push(0).expect('push failed');
+ vm.stack.push(1).expect('push failed');
+ vm.exec_swap15().expect('exec_swap15 failed');
+ assert(vm.stack.peek().unwrap() == 0xf, 'Top should be now 0xf');
+ assert(vm.stack.peek_at(15).unwrap() == 1, 'val at index 15 should be now 1');
+ }
+
+ #[test]
+ fn test_exec_swap16() {
+ let mut vm = VMBuilderTrait::new_with_presets().build();
+ // given
+ vm.stack.push(0xf).expect('push failed');
+ vm.stack.push(0).expect('push failed');
+ vm.stack.push(0).expect('push failed');
+ vm.stack.push(0).expect('push failed');
+ vm.stack.push(0).expect('push failed');
+ vm.stack.push(0).expect('push failed');
+ vm.stack.push(0).expect('push failed');
+ vm.stack.push(0).expect('push failed');
+ vm.stack.push(0).expect('push failed');
+ vm.stack.push(0).expect('push failed');
+ vm.stack.push(0).expect('push failed');
+ vm.stack.push(0).expect('push failed');
+ vm.stack.push(0).expect('push failed');
+ vm.stack.push(0).expect('push failed');
+ vm.stack.push(0).expect('push failed');
+ vm.stack.push(0).expect('push failed');
+ vm.stack.push(1).expect('push failed');
+ vm.exec_swap16().expect('exec_swap16 failed');
+ assert(vm.stack.peek().unwrap() == 0xf, 'Top should be now 0xf');
+ assert(vm.stack.peek_at(16).unwrap() == 1, 'val at index 16 should be now 1');
+ }
+}
diff --git a/cairo/kakarot-ssj/crates/evm/src/instructions/logging_operations.cairo b/cairo/kakarot-ssj/crates/evm/src/instructions/logging_operations.cairo
new file mode 100644
index 000000000..4be7153b5
--- /dev/null
+++ b/cairo/kakarot-ssj/crates/evm/src/instructions/logging_operations.cairo
@@ -0,0 +1,404 @@
+//! Logging Operations.
+
+use core::num::traits::CheckedAdd;
+use crate::errors::{EVMError, ensure};
+use crate::gas;
+use crate::memory::MemoryTrait;
+use crate::model::Event;
+use crate::model::vm::{VM, VMTrait};
+use crate::stack::StackTrait;
+use crate::state::StateTrait;
+
+
+#[generate_trait]
+pub impl LoggingOperations of LoggingOperationsTrait {
+ /// 0xA0 - LOG0 operation
+ /// Append log record with no topic.
+ /// # Specification: https://www.evm.codes/#a0?fork=shanghai
+ fn exec_log0(ref self: VM) -> Result<(), EVMError> {
+ exec_log_i(ref self, 0)
+ }
+
+ /// 0xA1 - LOG1
+ /// Append log record with one topic.
+ /// # Specification: https://www.evm.codes/#a1?fork=shanghai
+ fn exec_log1(ref self: VM) -> Result<(), EVMError> {
+ exec_log_i(ref self, 1)
+ }
+
+ /// 0xA2 - LOG2
+ /// Append log record with two topics.
+ /// # Specification: https://www.evm.codes/#a2?fork=shanghai
+ fn exec_log2(ref self: VM) -> Result<(), EVMError> {
+ exec_log_i(ref self, 2)
+ }
+
+ /// 0xA3 - LOG3
+ /// Append log record with three topics.
+ /// # Specification: https://www.evm.codes/#a3?fork=shanghai
+ fn exec_log3(ref self: VM) -> Result<(), EVMError> {
+ exec_log_i(ref self, 3)
+ }
+
+ /// 0xA4 - LOG4
+ /// Append log record with four topics.
+ /// # Specification: https://www.evm.codes/#a4?fork=shanghai
+ fn exec_log4(ref self: VM) -> Result<(), EVMError> {
+ exec_log_i(ref self, 4)
+ }
+}
+
+
+/// Store a new event in the dynamic context using topics
+/// popped from the stack and data from the memory.
+///
+/// # Arguments
+///
+/// * `self` - The context to which the event will be added
+/// * `topics_len` - The amount of topics to pop from the stack
+fn exec_log_i(ref self: VM, topics_len: u8) -> Result<(), EVMError> {
+ // Revert if the transaction is in a read only context
+ ensure(!self.message().read_only, EVMError::WriteInStaticContext)?;
+
+ // TODO(optimization): check benefits of n `pop` instead of `pop_n`
+ let offset = self.stack.pop_saturating_usize()?;
+ let size = self.stack.pop_usize()?; // Any size bigger than a usize would MemoryOOG.
+ let topics: Array = self.stack.pop_n(topics_len.into())?;
+
+ let memory_expansion = gas::memory_expansion(self.memory.size(), [(offset, size)].span())?;
+ self.memory.ensure_length(memory_expansion.new_size);
+
+ // TODO: avoid addition overflows here. We should use checked arithmetic.
+ let total_cost = gas::LOG
+ .checked_add(topics_len.into() * gas::LOGTOPIC)
+ .ok_or(EVMError::OutOfGas)?
+ .checked_add(size.into() * gas::LOGDATA)
+ .ok_or(EVMError::OutOfGas)?
+ .checked_add(memory_expansion.expansion_cost)
+ .ok_or(EVMError::OutOfGas)?;
+ self.charge_gas(total_cost)?;
+
+ let mut data: Array = Default::default();
+ self.memory.load_n(size, ref data, offset);
+
+ let event: Event = Event { keys: topics, data };
+ self.env.state.add_event(event);
+
+ Result::Ok(())
+}
+
+#[cfg(test)]
+mod tests {
+ use core::num::traits::Bounded;
+ use core::result::ResultTrait;
+ use crate::errors::{EVMError, TYPE_CONVERSION_ERROR};
+ use crate::instructions::LoggingOperationsTrait;
+ use crate::stack::StackTrait;
+ use crate::test_utils::{VMBuilderTrait, MemoryTestUtilsTrait};
+ use utils::helpers::u256_to_bytes_array;
+
+ #[test]
+ fn test_exec_log0() {
+ // Given
+ let mut vm = VMBuilderTrait::new_with_presets().build();
+
+ vm.memory.store_with_expansion(Bounded::::MAX, 0);
+
+ vm.stack.push(0x1F).expect('push failed');
+ vm.stack.push(0x00).expect('push failed');
+
+ // When
+ let result = vm.exec_log0();
+
+ // Then
+ assert!(result.is_ok());
+ assert_eq!(vm.stack.len(), 0);
+
+ let mut events = vm.env.state.events;
+ assert_eq!(events.len(), 1);
+
+ let event = events.pop_front().unwrap();
+ assert_eq!(event.keys.len(), 0);
+
+ assert_eq!(event.data.len(), 31);
+ let data_expected = u256_to_bytes_array(Bounded::::MAX).span().slice(0, 31);
+ assert_eq!(event.data.span(), data_expected);
+ }
+
+ #[test]
+ fn test_exec_log1() {
+ // Given
+ let mut vm = VMBuilderTrait::new_with_presets().build();
+
+ vm.memory.store_with_expansion(Bounded::::MAX, 0);
+
+ vm.stack.push(0x0123456789ABCDEF).expect('push failed');
+ vm.stack.push(0x20).expect('push failed');
+ vm.stack.push(0x00).expect('push failed');
+
+ // When
+ let result = vm.exec_log1();
+
+ // Then
+ assert!(result.is_ok());
+ assert_eq!(vm.stack.len(), 0);
+
+ let mut events = vm.env.state.events;
+ assert_eq!(events.len(), 1);
+
+ let event = events.pop_front().unwrap();
+ assert_eq!(event.keys.len(), 1);
+ assert_eq!(event.keys.span(), [0x0123456789ABCDEF].span());
+
+ assert_eq!(event.data.len(), 32);
+ let data_expected = u256_to_bytes_array(Bounded::::MAX).span().slice(0, 32);
+ assert_eq!(event.data.span(), data_expected);
+ }
+
+ #[test]
+ fn test_exec_log2() {
+ // Given
+ let mut vm = VMBuilderTrait::new_with_presets().build();
+
+ vm.memory.store_with_expansion(Bounded::::MAX, 0);
+
+ vm.stack.push(Bounded::::MAX).expect('push failed');
+ vm.stack.push(0x0123456789ABCDEF).expect('push failed');
+ vm.stack.push(0x05).expect('push failed');
+ vm.stack.push(0x05).expect('push failed');
+
+ // When
+ let result = vm.exec_log2();
+
+ // Then
+ assert!(result.is_ok());
+ assert_eq!(vm.stack.len(), 0);
+
+ let mut events = vm.env.state.events;
+ assert_eq!(events.len(), 1);
+
+ let event = events.pop_front().unwrap();
+ assert_eq!(event.keys.len(), 2);
+ assert_eq!(event.keys.span(), [0x0123456789ABCDEF, Bounded::::MAX].span());
+
+ assert_eq!(event.data.len(), 5);
+ let data_expected = u256_to_bytes_array(Bounded::::MAX).span().slice(0, 5);
+ assert_eq!(event.data.span(), data_expected);
+ }
+
+ #[test]
+ fn test_exec_log3() {
+ // Given
+ let mut vm = VMBuilderTrait::new_with_presets().build();
+
+ vm.memory.store_with_expansion(Bounded::::MAX, 0);
+ vm
+ .memory
+ .store_with_expansion(
+ 0x0123456789ABCDEF000000000000000000000000000000000000000000000000, 0x20
+ );
+
+ vm.stack.push(0x00).expect('push failed');
+ vm.stack.push(Bounded::::MAX).expect('push failed');
+ vm.stack.push(0x0123456789ABCDEF).expect('push failed');
+ vm.stack.push(0x28).expect('push failed');
+ vm.stack.push(0x00).expect('push failed');
+
+ // When
+ let result = vm.exec_log3();
+
+ // Then
+ assert!(result.is_ok());
+ assert_eq!(vm.stack.len(), 0);
+
+ let mut events = vm.env.state.events;
+ assert_eq!(events.len(), 1);
+
+ let event = events.pop_front().unwrap();
+ assert_eq!(event.keys.span(), [0x0123456789ABCDEF, Bounded::::MAX, 0x00].span());
+
+ assert_eq!(event.data.len(), 40);
+ let data_expected = u256_to_bytes_array(Bounded::::MAX).span();
+ assert_eq!(event.data.span().slice(0, 32), data_expected);
+ let data_expected = [0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF].span();
+ assert_eq!(event.data.span().slice(32, 8), data_expected);
+ }
+
+ #[test]
+ fn test_exec_log4() {
+ // Given
+ let mut vm = VMBuilderTrait::new_with_presets().build();
+
+ vm.memory.store_with_expansion(Bounded::