diff --git a/consensus/misc/eip7997.go b/consensus/misc/eip7997.go
new file mode 100644
index 0000000000..8564d5283a
--- /dev/null
+++ b/consensus/misc/eip7997.go
@@ -0,0 +1,47 @@
+// Copyright 2026 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library 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 Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+package misc
+
+import (
+ "github.com/ethereum/go-ethereum/core/tracing"
+ "github.com/ethereum/go-ethereum/core/vm"
+ "github.com/ethereum/go-ethereum/crypto"
+ "github.com/ethereum/go-ethereum/params"
+)
+
+// ApplyEIP7997 inserts the deterministic deployment factory into the state as an
+// irregular state transition, as specified by EIP-7997. The factory is a keyless
+// CREATE2 factory that, once present at the canonical address on every EVM chain,
+// allows contracts to be deployed at identical addresses across chains.
+func ApplyEIP7997(statedb vm.StateDB) {
+ // The account must hold the canonical factory runtime code. If its code hash
+ // already matches, the chain satisfies EIP-7997 and nothing needs to change.
+ wantHash := crypto.Keccak256Hash(params.DeterministicFactoryCode)
+ if statedb.GetCodeHash(params.DeterministicFactoryAddress) == wantHash {
+ return
+ }
+ if !statedb.Exist(params.DeterministicFactoryAddress) {
+ statedb.CreateAccount(params.DeterministicFactoryAddress)
+ }
+ statedb.CreateContract(params.DeterministicFactoryAddress)
+ statedb.SetCode(params.DeterministicFactoryAddress, params.DeterministicFactoryCode, tracing.CodeChangeUnspecified)
+
+ // Preserve a pre-existing nonce; only bump the default zero nonce to 1.
+ if statedb.GetNonce(params.DeterministicFactoryAddress) == 0 {
+ statedb.SetNonce(params.DeterministicFactoryAddress, 1, tracing.NonceChangeNewContract)
+ }
+}
diff --git a/core/chain_makers.go b/core/chain_makers.go
index d93ce80dca..f02e0341bb 100644
--- a/core/chain_makers.go
+++ b/core/chain_makers.go
@@ -385,6 +385,11 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, engine conse
if config.DAOForkSupport && config.DAOForkBlock != nil && config.DAOForkBlock.Cmp(b.header.Number) == 0 {
misc.ApplyDAOHardFork(statedb)
}
+ // EIP-7997: insert the deterministic deployment factory at the Amsterdam
+ // activation block via an irregular state transition.
+ if config.IsAmsterdam(b.header.Number, b.header.Time) && !config.IsAmsterdam(parent.Number(), parent.Time()) {
+ misc.ApplyEIP7997(statedb)
+ }
if config.IsPrague(b.header.Number, b.header.Time) || config.IsUBT(b.header.Number, b.header.Time) {
// EIP-2935
diff --git a/core/eip7997_test.go b/core/eip7997_test.go
new file mode 100644
index 0000000000..4c4d26507b
--- /dev/null
+++ b/core/eip7997_test.go
@@ -0,0 +1,113 @@
+// Copyright 2026 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library 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 Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+// Tests for EIP-7997: the deterministic deployment factory inserted as an
+// irregular state transition at the Amsterdam activation block.
+
+package core
+
+import (
+ "bytes"
+ "testing"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/consensus/misc"
+ "github.com/ethereum/go-ethereum/core/types"
+ "github.com/ethereum/go-ethereum/core/vm"
+ "github.com/ethereum/go-ethereum/crypto"
+ "github.com/ethereum/go-ethereum/params"
+ "github.com/holiman/uint256"
+)
+
+// TestApplyEIP7997 verifies the irregular state transition seeds the factory
+// account with the canonical code and nonce.
+func TestApplyEIP7997(t *testing.T) {
+ sdb := mkState(nil)
+ misc.ApplyEIP7997(sdb)
+
+ if got := sdb.GetCode(params.DeterministicFactoryAddress); !bytes.Equal(got, params.DeterministicFactoryCode) {
+ t.Fatalf("factory code mismatch:\n got %x\nwant %x", got, params.DeterministicFactoryCode)
+ }
+ if got := sdb.GetNonce(params.DeterministicFactoryAddress); got != 1 {
+ t.Fatalf("factory nonce = %d, want %d", got, 1)
+ }
+}
+
+// TestApplyEIP7997Existing checks that a chain which already hosts the factory
+// (for example via its keyless creation transaction) is left untouched, so the
+// transition never rewrites an existing nonce.
+func TestApplyEIP7997Existing(t *testing.T) {
+ sdb := mkState(types.GenesisAlloc{
+ params.DeterministicFactoryAddress: {Code: params.DeterministicFactoryCode, Nonce: 5},
+ })
+ misc.ApplyEIP7997(sdb)
+
+ if got := sdb.GetNonce(params.DeterministicFactoryAddress); got != 5 {
+ t.Fatalf("existing factory nonce overwritten: got %d, want 5", got)
+ }
+}
+
+// TestApplyEIP7997WrongCode checks that an account occupying the factory address
+// with the wrong code is force-overwritten with the canonical runtime code, while
+// a pre-existing non-zero nonce is preserved.
+func TestApplyEIP7997WrongCode(t *testing.T) {
+ sdb := mkState(types.GenesisAlloc{
+ params.DeterministicFactoryAddress: {Code: []byte{0x60, 0x00}, Nonce: 7},
+ })
+ misc.ApplyEIP7997(sdb)
+
+ if got := sdb.GetCode(params.DeterministicFactoryAddress); !bytes.Equal(got, params.DeterministicFactoryCode) {
+ t.Fatalf("factory code not overwritten:\n got %x\nwant %x", got, params.DeterministicFactoryCode)
+ }
+ if got := sdb.GetNonce(params.DeterministicFactoryAddress); got != 7 {
+ t.Fatalf("factory nonce = %d, want %d (existing nonce must be preserved)", got, 7)
+ }
+}
+
+// TestEIP7997FactoryDeploys exercises the inserted factory bytecode: calling it
+// with a salt followed by init code must CREATE2-deploy the contract at the
+// canonical deterministic address and return that address (20 bytes, unpadded).
+func TestEIP7997FactoryDeploys(t *testing.T) {
+ sdb := mkState(nil)
+ misc.ApplyEIP7997(sdb)
+
+ var (
+ caller = common.Address{0xca}
+ salt [32]byte
+ // initcode returning the single-byte runtime 0xfe:
+ // PUSH1 0xfe PUSH1 0x00 MSTORE8 PUSH1 0x01 PUSH1 0x00 RETURN
+ initcode = common.FromHex("60fe60005360016000f3")
+ )
+ salt[31] = 0x42
+
+ input := append(append([]byte{}, salt[:]...), initcode...)
+
+ ret, _, err := amsterdamCoreEVM(sdb).Call(caller, params.DeterministicFactoryAddress, input, vm.NewGasBudget(10_000_000, 0), new(uint256.Int))
+ if err != nil {
+ t.Fatalf("factory call failed: %v", err)
+ }
+
+ want := crypto.CreateAddress2(params.DeterministicFactoryAddress, salt, crypto.Keccak256(initcode))
+ if len(ret) != 20 {
+ t.Fatalf("factory returned %d bytes, want 20", len(ret))
+ }
+ if got := common.BytesToAddress(ret); got != want {
+ t.Fatalf("factory returned address %x, want %x", got, want)
+ }
+ if code := sdb.GetCode(want); !bytes.Equal(code, []byte{0xfe}) {
+ t.Fatalf("deployed runtime code = %x, want fe", code)
+ }
+}
diff --git a/core/genesis.go b/core/genesis.go
index 910a3fb0f5..7fad915ce2 100644
--- a/core/genesis.go
+++ b/core/genesis.go
@@ -725,6 +725,8 @@ func DeveloperGenesisBlock(gasLimit uint64, faucet *common.Address) *Genesis {
// EIP-8282 - Builder Execution Requests
params.BuilderDepositAddress: {Nonce: 1, Code: params.BuilderDepositCode, Balance: common.Big0},
params.BuilderExitAddress: {Nonce: 1, Code: params.BuilderExitCode, Balance: common.Big0},
+ // EIP-7997 - Deterministic deployment factory
+ params.DeterministicFactoryAddress: {Nonce: 1, Code: params.DeterministicFactoryCode, Balance: common.Big0},
},
}
if faucet != nil {
diff --git a/core/state_processor.go b/core/state_processor.go
index 5b81abef6f..bc565db6f9 100644
--- a/core/state_processor.go
+++ b/core/state_processor.go
@@ -81,6 +81,11 @@ func (p *StateProcessor) Process(ctx context.Context, block *types.Block, stated
if config.DAOForkSupport && config.DAOForkBlock != nil && config.DAOForkBlock.Cmp(block.Number()) == 0 {
misc.ApplyDAOHardFork(tracingStateDB)
}
+ // EIP-7997: insert the deterministic deployment factory at the Amsterdam
+ // activation block via an irregular state transition.
+ if isEIP7997Transition(config, p.chain, header) {
+ misc.ApplyEIP7997(tracingStateDB)
+ }
var (
context = NewEVMBlockContext(header, p.chain, nil)
signer = types.MakeSigner(config, header.Number, header.Time)
@@ -137,6 +142,19 @@ func (p *StateProcessor) Process(ctx context.Context, block *types.Block, stated
}, nil
}
+// isEIP7997Transition reports whether the given header belongs to the first block
+// on which the Amsterdam fork is active.
+func isEIP7997Transition(config *params.ChainConfig, chain ChainContext, header *types.Header) bool {
+ if header.Number.Sign() == 0 || !config.IsAmsterdam(header.Number, header.Time) {
+ return false
+ }
+ parent := chain.GetHeader(header.ParentHash, header.Number.Uint64()-1)
+ if parent == nil {
+ return false
+ }
+ return !config.IsAmsterdam(parent.Number, parent.Time)
+}
+
// PreExecution processes pre-execution system calls.
func PreExecution(ctx context.Context, beaconRoot *common.Hash, parent common.Hash, config *params.ChainConfig, evm *vm.EVM, number *big.Int, time uint64) *bal.ConstructionBlockAccessList {
_, _, spanEnd := telemetry.StartSpan(ctx, "core.preExecution")
diff --git a/params/protocol_params.go b/params/protocol_params.go
index a56e810074..cace632931 100644
--- a/params/protocol_params.go
+++ b/params/protocol_params.go
@@ -270,6 +270,10 @@ var (
BuilderDepositCode = common.FromHex("0x3373fffffffffffffffffffffffffffffffffffffffe146101065760115f54807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1461023457600182026001905f5b5f82111560695781019083028483029004916001019190604e565b90939004925050503660b814608957366102345734610234575f5260205ff35b8034106102345760383567ffffffffffffffff1680633b9aca001161023457633b9aca00029034031061023457600154600101600155600354806006026004015f358155600101602035815560010160403581556001016060358155600101608035815560010160a035905560b85f5f3760b85fa0600101600355005b600354600254808203806101001161011d57506101005b5f5b8181146101c3578281016006026004018160b8028154815260200181600101548152602001816002015480825260401c67ffffffffffffffff16816010018160381c81600701538160301c81600601538160281c81600501538160201c81600401538160181c81600301538160101c81600201538160081c81600101535360200181600301548152602001816004015481526020019060050154905260010161011f565b91018092146101d557906002556101e0565b90505f6002555f6003555b5f54807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff141561020d57505f5b6001546020828201116102225750505f610228565b01602090035b5f555f60015560b8025ff35b5f5ffd")
BuilderExitAddress = common.HexToAddress("0x000014574A74c805590AFF9499fc7A690f008282")
BuilderExitCode = common.FromHex("0x3373fffffffffffffffffffffffffffffffffffffffe1460cb5760115f54807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1461018857600182026001905f5b5f82111560685781019083028483029004916001019190604d565b909390049250505036603014608857366101885734610188575f5260205ff35b341061018857600154600101600155600354806003026004013381556001015f35815560010160203590553360601b5f5260305f60143760445fa0600101600355005b6003546002548082038060101160df575060105b5f5b8181146101175782810160030260040181604402815460601b8152601401816001015481526020019060020154905260010160e1565b91018092146101295790600255610134565b90505f6002555f6003555b5f54807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff141561016157505f5b6001546002828201116101765750505f61017c565b01600290035b5f555f6001556044025ff35b5f5ffd")
+
+ // EIP-7997 - Deterministic deployment factory (keyless CREATE2 factory)
+ DeterministicFactoryAddress = common.HexToAddress("0x4e59b44847b379578588920cA78FbF26c0B4956C")
+ DeterministicFactoryCode = common.FromHex("0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe03601600081602082378035828234f58015156039578182fd5b8082525050506014600cf3")
)
// System log events.