Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions category/execution/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,8 @@ add_library(
"ethereum/block_reward.hpp"
"ethereum/create_contract_address.cpp"
"ethereum/create_contract_address.hpp"
"ethereum/deterministic_factory_contract.cpp"
"ethereum/deterministic_factory_contract.hpp"
"ethereum/dispatch_transaction.cpp"
"ethereum/dispatch_transaction.hpp"
"ethereum/evmc_host.cpp"
Expand Down
55 changes: 55 additions & 0 deletions category/execution/ethereum/deterministic_factory_contract.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// Copyright (C) 2025-26 Category Labs, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.

#include <category/core/address.hpp>
#include <category/core/byte_string.hpp>
#include <category/core/bytes.hpp>
#include <category/core/hex.hpp>
#include <category/execution/ethereum/deterministic_factory_contract.hpp>
#include <category/vm/evm/explicit_traits.hpp>
Comment on lines +16 to +21

MONAD_ANONYMOUS_NAMESPACE_BEGIN

constexpr auto FACTORY_ADDRESS =
0x4e59b44847b379578588920cA78FbF26c0B4956C_address;

byte_string const FACTORY_CODE =
from_hex("0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
"fe03601600081602082378035828234f58015156039578182fd5b808252505050"
"6014600cf3")
.value();

MONAD_ANONYMOUS_NAMESPACE_END

MONAD_NAMESPACE_BEGIN

// EIP-7997
template <Traits traits>
void deploy_deterministic_factory_contract(State &state)
{
if constexpr (!traits::eip_7997_active()) {
return;
}

if (MONAD_UNLIKELY(!state.account_exists(FACTORY_ADDRESS))) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P1] The deployment guard should be !state.account_has_code_or_nonce(FACTORY_ADDRESS), not !state.account_exists(FACTORY_ADDRESS). account_exists returns true for any touched account (including one that only holds a balance), so if a user sends value to 0x4e59b44847b379578588920cA78FbF26c0B4956C before the MONAD_NEXT/Amsterdam activation, this check skips the factory deployment entirely and the fork silently ships without EIP-7997 code at the predeploy address. The base commit ("Add and use code-or-nonce primitive") introduced account_has_code_or_nonce specifically for this idiom — block_hash_history.cpp:57-58 uses it, and State::create_contract already preserves any existing balance per YP §7.

Suggested change
if (MONAD_UNLIKELY(!state.account_exists(FACTORY_ADDRESS))) {
if (MONAD_UNLIKELY(!state.account_has_code_or_nonce(FACTORY_ADDRESS))) {

state.create_contract(FACTORY_ADDRESS);
state.set_code(FACTORY_ADDRESS, FACTORY_CODE);
state.set_nonce(FACTORY_ADDRESS, 1);
}
Comment on lines +38 to +50
}

EXPLICIT_TRAITS(deploy_deterministic_factory_contract);

MONAD_NAMESPACE_END
25 changes: 25 additions & 0 deletions category/execution/ethereum/deterministic_factory_contract.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// Copyright (C) 2025-26 Category Labs, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.

#include <category/core/config.hpp>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] Missing #pragma once. CLAUDE.md's style section requires #pragma once on all headers, and the sibling block_hash_history.hpp:16 follows it. Without a guard, any TU that transitively includes this header more than once compiles slower and is one added definition away from an ODR breakage.

Suggested change
#include <category/core/config.hpp>
#pragma once
#include <category/core/config.hpp>

#include <category/execution/ethereum/state3/state.hpp>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P3] Prefer forward-declaring State here — the sibling block_hash_history.hpp:27 uses class State; instead of pulling in the full state3/state.hpp transitive graph, which keeps compile time down and cuts unnecessary rebuild churn when state.hpp changes.

#include <category/vm/evm/traits.hpp>
Comment on lines +16 to +18

MONAD_NAMESPACE_BEGIN

template <Traits traits>
void deploy_deterministic_factory_contract(State &);

MONAD_NAMESPACE_END
3 changes: 3 additions & 0 deletions category/execution/ethereum/execute_block_header.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include <category/core/int.hpp>
#include <category/execution/ethereum/block_hash_history.hpp>
#include <category/execution/ethereum/core/block.hpp>
#include <category/execution/ethereum/deterministic_factory_contract.hpp>
#include <category/execution/ethereum/event/exec_event_ctypes.h>
#include <category/execution/ethereum/event/exec_event_recorder.hpp>
#include <category/execution/ethereum/event/record_txn_events.hpp>
Expand Down Expand Up @@ -70,6 +71,8 @@ void execute_block_header(
deploy_block_hash_history_contract<traits>(state);
set_block_hash_history<traits>(state, header);

deploy_deterministic_factory_contract<traits>(state);

if constexpr (traits::evm_rev() >= MONAD_ETH_CANCUN) {
set_beacon_root(state, header);
}
Expand Down
11 changes: 11 additions & 0 deletions category/vm/evm/traits.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ namespace monad
{ T::eip_7939_active() } -> std::same_as<bool>;
{ T::eip_7951_active() } -> std::same_as<bool>;
{ T::eip_7981_active() } -> std::same_as<bool>;
{ T::eip_7997_active() } -> std::same_as<bool>;
{ T::mip_3_active() } -> std::same_as<bool>;
{ T::mip_8_active() } -> std::same_as<bool>;
{ T::mip_11_active() } -> std::same_as<bool>;
Expand Down Expand Up @@ -188,6 +189,11 @@ namespace monad
return Rev >= MONAD_ETH_AMSTERDAM;
}

static consteval bool eip_7997_active() noexcept
{
return Rev >= MONAD_ETH_AMSTERDAM;
}

static consteval bool mip_3_active() noexcept
{
return false;
Expand Down Expand Up @@ -367,6 +373,11 @@ namespace monad
return evm_rev() >= MONAD_ETH_AMSTERDAM;
}

static consteval bool eip_7997_active() noexcept
{
return evm_rev() >= MONAD_ETH_AMSTERDAM;
}

static consteval bool mip_3_active() noexcept
{
if constexpr (Rev >= MONAD_NINE) {
Expand Down
Loading