---
eip: 8298
title: SETCODEFROM Code Reuse Instruction
description: Adds an instruction that sets an account's code hash from an existing deployed contract.
author: Liyi Guo (@colinlyguo), Ben Adams (@benaadams), Carlos Perez (@CPerezz), Nicolas Consigny (@nconsigny)
discussions-to: https://ethereum-magicians.org/t/eip-8298-setcodefrom-code-reuse-instruction/28779
status: Draft
type: Standards Track
category: Core
created: 2026-06-11
requires: 3607, 6780, 7702, 7928, 8037, 8038
---

## Abstract

This EIP introduces `SETCODEFROM`, an EVM instruction that sets the current account's code hash to the code hash of a source account with existing deployed code. The new code is visible to later code execution.

## Motivation

This EIP has two primary use cases.

First, it improves code reuse and deployment economics. Deploying many contracts with identical runtime code is expensive, especially when contract deployment is repriced more closely to state growth, for example under [EIP-8037](./eip-8037.md). Initcode can initialize per-instance storage and then adopt shared deployed code, without paying code-deposit gas for identical runtime code. Existing contracts can also adopt a new shared implementation through their own upgrade logic.

Second, it provides an account migration path for disabling ECDSA-based EOA transaction authority. Migration code can store account-specific wallet state, including post-quantum (PQ) wallet state, then adopt regular wallet code. After that update, account control is through the installed wallet code rather than ECDSA transaction origination. This is because the account now has regular deployed code, not an [EIP-7702](./eip-7702.md) delegation indicator, so ECDSA-authenticated transactions remain invalid under [EIP-3607](./eip-3607.md). [EIP-7702](./eip-7702.md) authorization processing can no longer redelegate the account because the authority code check only accepts empty code or an existing delegation indicator. The account can still upgrade through upgrade logic in the installed code, for example by calling `SETCODEFROM` again or by using other methods. Protocol-level ECDSA transaction origination remains permanently disabled.

`SETCODEFROM` provides the code-adoption step for both cases after any per-instance state has been initialized.

## Specification

### Parameters

The opcode is defined as follows:

| Parameter                | Value |
| ------------------------ | ----- |
| `SETCODEFROM_OPCODE`     | `TBD` |

### `SETCODEFROM`

`SETCODEFROM(source)` takes one stack item. The low 160 bits of the stack item are interpreted as `source`.

Before execution:

```text
[..., source]
```

After execution:

```text
[..., success]
```

`success` is `1` if `source` is a valid source account, and `0` otherwise.

The instruction is address-based: its input is `source` address, not a raw code hash. It reads `source.codeHash` from a live account in consensus state.

For this instruction, the current account is the current execution-environment address, meaning the account returned by `ADDRESS` and whose storage is affected by `SSTORE`. If the executing code was loaded from another account, including through [EIP-7702](./eip-7702.md) delegated execution, `SETCODEFROM` updates the execution-environment account, not the code source account.

A source account is valid for `SETCODEFROM` if all of the following are true:

- The source account exists in the state.
- `source.codeHash != EMPTYCODEHASH`, where `EMPTYCODEHASH = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470`.
- `source.code` is valid regular deployed code under the active fork, e.g. it does not start with `0xEF`, which is reserved by [EIP-3541](./eip-3541.md) and used by [EIP-7702](./eip-7702.md) delegation indicators.
- The source account was not created in the current transaction. An account counts as created in the current transaction exactly when [EIP-6780](./eip-6780.md) would let `SELFDESTRUCT` delete it. This restriction applies to the source only: the current account may itself have been created in the current transaction, including during its own initcode.

If executed in a static context, `SETCODEFROM` causes an exceptional halt.

If `source` is not a valid source account, `SETCODEFROM` pushes `0` and makes no state change.

If `source` is a valid source account, `SETCODEFROM` sets the current account's `codeHash` to `source.codeHash` and pushes `1`. Subsequent changes to `source` must not affect the current account's `codeHash` and must not affect the availability of the bytecode referenced by that code hash.

State changes made by `SETCODEFROM` follow normal EVM revert semantics. If the frame or transaction reverts, the code update reverts.

### Deployed Code Execution

When `SETCODEFROM` succeeds while executing deployed code, it updates the code hash of the current execution-context account.

The current execution frame continues running the code that was already loaded for that frame. The updated code hash is used by later code executing operations, including later calls to the same account in the same transaction, subject to normal revert semantics.

### Contract Creation

`SETCODEFROM` is permitted during initcode execution, both in a contract-creation transaction and during `CREATE` and `CREATE2`. It behaves as specified above: the current account is the account being created, the code hash update takes effect immediately, and the creation frame continues executing the initcode already loaded for it. `CODESIZE` and `CODECOPY` in that frame continue to refer to the initcode being executed, while `EXTCODESIZE`, `EXTCODEHASH`, `EXTCODECOPY` and calls that target the account being created observe the adopted code.

When initcode execution completes successfully, contract creation installs code as follows:

- If the created account's `codeHash` is `EMPTYCODEHASH`, the initcode's return data is validated, charged code-deposit gas, and installed as the account's code.
- Otherwise the created account has adopted code through `SETCODEFROM`. The initcode's return data is treated as empty for contract-creation completion and is not validated, hashed, installed, or charged code-deposit gas. No code-deposit cost is charged as part of this creation for the adopted code, and the account's `codeHash` is left as adopted.

The account-creation costs of the creation itself are unchanged. In particular, if creation adds a new account leaf, the normal account-creation state-gas charge still applies. If the destination account already exists, no account-creation state-gas is charged, as usual.

When creation completes with code adopted through `SETCODEFROM`, no code-deposit state gas or code-deposit execution gas is charged for the adopted code. `SETCODEFROM` only updates the created account's `codeHash` to reference code that already exists.

Failure handling is unchanged: if the creation frame reverts or halts exceptionally, the code hash update reverts with every other state change in that frame.

### ECDSA Transaction Origination

After `SETCODEFROM` installs regular deployed code on an account, the account is controlled through that code. The account has nonempty regular code, so an ECDSA-authenticated transaction whose recovered sender is that account is invalid under [EIP-3607](./eip-3607.md). It also disables [EIP-7702](./eip-7702.md) redelegation, because EIP-7702 authorization processing accepts only empty code or an existing valid delegation indicator for the authority account.

### Gas Costs

The following gas parameters are used:

- `SOURCE_ACCOUNT_ACCESS_COST` is `COLD_ACCOUNT_ACCESS` if `source` is cold, otherwise `WARM_ACCESS`
- `SETCODEFROM_SOURCE_GAS` is `SOURCE_ACCOUNT_ACCESS_COST + WARM_ACCESS`
- `SETCODEFROM_CURRENT_ACCESS_GAS` is `WARM_ACCESS`
- `SETCODEFROM_WRITE_GAS` is `ACCOUNT_WRITE`
- `SETCODEFROM_STATE_GAS` is `0`

`SETCODEFROM` charges `SETCODEFROM_SOURCE_GAS` in execution gas for each execution attempt. This covers the source lookup and validation:

- carrying the `source` address = `0`, charged by ordinary calldata or deployed-code costs
- loading the source account leaf = `SOURCE_ACCOUNT_ACCESS_COST`
- validating source code = `WARM_ACCESS`, a fixed charge covering the additional code-store access required when deployed bytecode must be inspected

If `source` is valid, the instruction charges `SETCODEFROM_CURRENT_ACCESS_GAS` before comparing `source.codeHash` with the current account's `codeHash`:

- accessing the already warm current account leaf = `WARM_ACCESS`

If the two code hashes differ, the instruction charges `SETCODEFROM_WRITE_GAS` before updating the current account's code hash. Its write component follows [EIP-8038](./eip-8038.md):

- updating the current account's code hash = `ACCOUNT_WRITE`

An invalid source charges neither `SETCODEFROM_CURRENT_ACCESS_GAS` nor `SETCODEFROM_WRITE_GAS`. A valid source with the same code hash charges `SETCODEFROM_CURRENT_ACCESS_GAS` but not `SETCODEFROM_WRITE_GAS`. Under the current EIP-8038 parameters, `ACCOUNT_WRITE` is `9000`, `COLD_ACCOUNT_ACCESS` is `3000`, and `WARM_ACCESS` is `100`. The resulting execution-gas costs are:

| Result                                 | Cold source | Warm source |
| -------------------------------------- | ----------: | ----------: |
| Invalid source                         |      `3100` |       `200` |
| Valid source with unchanged code hash  |      `3200` |       `300` |
| Current account's code hash is updated |     `12200` |      `9300` |

Under [EIP-8037](./eip-8037.md), `SETCODEFROM_STATE_GAS` is `0`. `SETCODEFROM` performs no code-deposit operation. It only updates the current account leaf to reference the source's existing or pending `codeHash => code` mapping. The gas cost is not affected by source code size.

This also applies when `SETCODEFROM` executes in initcode. The enclosing contract creation may independently incur the normal state-gas charge for creating a new account leaf, but adopting code through `SETCODEFROM` incurs no code-deposit state gas and no code-deposit execution gas. In particular, neither cost depends on the size of the adopted code.

### Block-Level Access Lists

For block-level access lists (BALs) as defined by [EIP-7928](./eip-7928.md), this EIP extends `CodeChange` with an optional trailing element, `new_code_hash`. A code hash change made by `SETCODEFROM` is recorded as that hash, never as bytecode:

```python
# CodeChange: [block_access_index, new_code]             code installed by contract creation or EIP-7702
#          or [block_access_index, b"", new_code_hash]   code adopted by SETCODEFROM
CodeChange = [BlockAccessIndex, Bytecode, Optional[Hash32]]
```

If an account's code hash changes in a transaction and its value at the end of the transaction was set by `SETCODEFROM`, the change must be recorded as `[block_access_index, b"", code_hash]`. Every other code change keeps the two-element form. A BAL that records a code change in any other way is invalid.

The bytecode behind `new_code_hash` is in the pre-block state or in the `new_code` of a `code_changes` entry with a lower block access index; no entry refers to bytecode recorded at the same or a higher index.

## Rationale

Using an address rather than a raw code hash avoids depending on client-local code database contents, which may differ across nodes. A newly synced node may have a smaller local code database than a long-running node because it may not keep historical or unreferenced bytecode entries. For this reason, this EIP makes `SETCODEFROM` copy the code hash from a live source account, so the adopted code hash is confirmed by current consensus state. A valid source cannot be deleted, but it can replace its own code hash later in the same block; the bytecode it deposited at creation must then still be persisted for the accounts that adopted it.

`SETCODEFROM` requires that the source account exists, even though a nonexistent account has no code and is already excluded by the `EMPTYCODEHASH` condition in a state model where a nonexistent account reads as an empty account. Implementations that represent a nonexistent account's code hash as zero rather than as `EMPTYCODEHASH` would otherwise accept such an account as a valid source and install a code hash with no code behind it. Stating existence separately removes that reading.

A `SETCODEFROM` code change records a code hash instead of bytecode because the bytecode already exists, in the pre-block state or in the `code_changes` entry of the creation that deposited it; repeating it would price BAL bytes far below any other operation (see Security Considerations). The change still needs an entry, because consumers that read an account's code at a block access index, such as parallel executors or nodes that apply the BAL without executing, would otherwise see the old code. The hash is a separate element of `CodeChange` because a 32-byte `new_code` value is indistinguishable from 32 bytes of runtime code, and keeping it inside `CodeChange` leaves each account with one list of code changes. With no bytecode fallback, an entry's form identifies its origin, two elements for creation or an [EIP-7702](./eip-7702.md) delegation and three for adoption, and its size is fixed, so a byte-floor scheme such as [EIP-8279](./eip-8279.md) can meter the instruction when it executes.

A source created in the current transaction is rejected because its code is transient: under [EIP-6780](./eip-6780.md) the account can be deleted before the transaction ends, and it can replace its own code hash with `SETCODEFROM`. Either way an adopter keeps a code hash whose bytecode is in neither the pre-block state nor the BAL, which records end-of-transaction values only and nothing for a deleted account. The gap is transitive, since a pre-existing account can adopt from the created source, be adopted from in turn, and then adopt something else, so checking the immediate source is not enough. Rejecting created sources is: every adopted code hash was then held, at the start of the adopting transaction, by an account not created in it, so its bytecode is in the pre-block state, in the `new_code` of an earlier creation, or was itself adopted earlier, to which the same argument applies. The cost is that a template cannot be adopted in the transaction that deploys it.

The instruction is self-only during deployed-code execution to keep authority local to the account whose code is executing. It does not allow the executing code to update any other account's code hash.

Allowing `SETCODEFROM` during initcode keeps one code-installation rule for creation: creation installs either the bytes returned by initcode or the code hash adopted during it, never both. The created account's code hash at the end of initcode execution decides which, so the two paths cannot interleave. Contract creation is not the first operation whose outcome is decided by state changes made inside the creation frame: under [EIP-6780](./eip-6780.md), `SELFDESTRUCT` executed in the transaction that created an account deletes that account after creation completes, discarding the code that creation installed.

Initcode support makes per-instance initialization atomic on every creation path. Initcode writes per-instance storage, adopts shared code with `SETCODEFROM`, and returns no data. A contract-creation transaction with a nil `to` field therefore initializes and adopts in one transaction, and a factory does not need to deploy an initializer runtime and call back into it.

For example, the instruction can be used in the following patterns.

An account migration function can store account-specific wallet state, then adopt a shared wallet implementation:

```
SSTORE(pq_pubkey_slot, userPubkey)
SSTORE(recovery_slot, recoveryConfig)
if SETCODEFROM(PQ_WALLET_TEMPLATE) == 0: REVERT()
STOP
```

An [ERC-20](./eip-20.md) clone factory can deploy a tiny initializer with `CREATE2`, then call it to initialize token metadata and ownership before adopting a shared token implementation:

```
SSTORE(name_slot, "MyToken")
SSTORE(symbol_slot, "MYT")
SSTORE(owner_slot, msg.sender)
if SETCODEFROM(ERC20_TEMPLATE) == 0: REVERT()
STOP
```

## Backwards Compatibility

This EIP requires a hard fork to implement because it introduces a new instruction which did not exist previously. As a result, already deployed contracts using this instruction could change their behavior after this EIP.

This EIP also alters the [EIP-7928](./eip-7928.md) BAL encoding by adding an optional `new_code_hash` element to `CodeChange`. A client implementing only EIP-7928 will reject blocks that use it, and vice versa.

The `0xEF` source restriction also excludes three contracts whose code starts with `0xEF`. These contracts were deployed on Ethereum mainnet after [EIP-3541](./eip-3541.md)'s state survey but before its activation. Because `0xEF` is an undefined instruction that causes an exceptional halt, none of them provides executable source code. Excluding these contracts therefore has no negative effect on practical code reuse.

## Test Cases

<!-- TODO -->

## Security Considerations

`SETCODEFROM` can change the code used by later executions of an account. Contracts that expose this instruction must restrict access to trusted control paths, because a successful call can permanently change account behavior if the transaction does not revert.

`SETCODEFROM` changes account-code identity semantics. Code executing with an account's authority can replace the code identity visible to later execution and code-inspection with regular code from another account. This also departs from [EIP-7702](./eip-7702.md)'s conservative codehash-introspection design by introducing a related codehash-presentation risk through explicit code adoption, rather than through `EXTCODEHASH` following delegation.

Unlike [EIP-6913](./eip-6913.md), which forbids code replacement when the executing code differs from the account code, this EIP allows delegated execution to update the caller's account code. This preserves existing proxy upgrade patterns, which already rely on delegated code running with the caller's authority. Wallets must treat any such module as upgrade-authorized code, restrict delegatecall targets accordingly, and require explicit authorization before execution.

The source restriction requires valid regular deployed code under the active fork, e.g. code that does not use the `0xEF` prefix reserved by [EIP-3541](./eip-3541.md) and used by [EIP-7702](./eip-7702.md) delegation indicators. It also excludes empty code. Precompiles are excluded without a separate condition: a precompile has no deployed code in state, so its address either does not exist in the state or has `EMPTYCODEHASH`. This prevents `SETCODEFROM` from bypassing deployed-code validity rules or treating precompile behavior as deployed code.

Protocol-level ECDSA transaction origination is disabled once the account has regular deployed code, meaning code that is not an [EIP-7702](./eip-7702.md) delegation indicator. Contracts that verify ECDSA signatures directly through `ecRecover` may still recover that address. This affects signature-based authorization such as `permit`. A companion `ecRecover` change, such as [EIP-8151](./eip-8151.md), can reject recovered addresses whose account code is regular deployed code rather than an [EIP-7702](./eip-7702.md) delegation indicator, and return 32 zero bytes.

Because the current frame keeps executing already-loaded code, implementations must clearly separate the executing code for the current frame from the account code visible to later calls. Re-entrant calls after a successful `SETCODEFROM` observe the updated code.

An account under construction can be called before its initcode returns, for example through a callback from a contract that the initcode calls. Such a call executes the code adopted by a successful `SETCODEFROM` earlier in the same initcode, against storage that the initcode has not finished writing. Initcode should complete per-instance initialization before adopting code, or otherwise guard against re-entrancy.

Recording adopted bytecode in the BAL would let one `SETCODEFROM`, at `9300` gas for a warm source, add up to the maximum code size to the BAL, below one gas per byte, where the [EIP-7928](./eip-7928.md) `bal_items` bound assumes small items. An adoption instead adds a 32-byte hash, and because a source created in the current transaction is invalid, the hash always resolves, so no adoption adds bytecode.

## Copyright

Copyright and related rights waived via [CC0](../LICENSE.md).
