--- 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` | `0xA7` | ### `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, i.e. 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 the current account is a system contract, `SETCODEFROM` causes an exceptional halt. A system contract is an account that the protocol calls from `SYSTEM_ADDRESS` (`0xfffffffffffffffffffffffffffffffffffffffe`) during block processing; the [EIP-6110](./eip-6110.md) deposit contract, whose logs are read but which is never called, is not one. Both halts happen before `SETCODEFROM_SOURCE_GAS` is charged, so `source` is not accessed. 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. `CODESIZE` and `CODECOPY` in that frame continue to refer to that code, while `EXTCODESIZE`, `EXTCODEHASH` and `EXTCODECOPY` of the current account observe the updated code hash. 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. In the second case, the code the creation deploys is the code behind the created account's `codeHash` when initcode execution completes, i.e. the code of the last successful `SETCODEFROM` of the creation that was not reverted. Clients that report a creation's deployed code, for example in execution traces or `eth_call` results, report that code. As for every creation, a successful `CREATE` or `CREATE2` still leaves the caller's return data buffer empty ([EIP-211](./eip-211.md)). 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. If the account held an [EIP-7702](./eip-7702.md) delegation indicator, the adopted code replaces it. 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 Once `SETCODEFROM_SOURCE_GAS` is charged, `source` is added to `accessed_addresses` ([EIP-2929](./eip-2929.md)) whether or not it is valid. Validation reads `source`'s own code and never resolves an [EIP-7702](./eip-7702.md) delegation, so the delegation target of a delegated `source` is not accessed, charged or warmed. 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. `SETCODEFROM` accesses `source` once `SETCODEFROM_SOURCE_GAS` has been charged, whether or not `source` is valid. From then on `source` must be included in the BAL, with empty change lists if it has no other changes. If that charge fails, `source` is not accessed and must not be included. In [EIP-7928](./eip-7928.md)'s gas validation before state access, the pre-state validation cost of `SETCODEFROM` is `SETCODEFROM_SOURCE_GAS`. ### Public Mempool These rules apply only to public mempool validation and do not affect consensus. An EOA's ECDSA-signed transactions stay valid while its code is empty or an [EIP-7702](./eip-7702.md) delegation indicator. Once it adopts regular code, [EIP-3607](./eip-3607.md) makes them invalid, including those already pending, so nodes should evict them when processing a new block and reject new ones. This retires the account's key, not the account: the account keeps transacting as the sender of [EIP-8141](./eip-8141.md) frame transactions that its code approves, which the public mempool accepts under EIP-8141's rules and the ones below, and keeps acting when other transactions call it. An EOA adopts code only by running EIP-7702 delegated code, so an adoption invalidates no more pending transactions than delegated code already can, and EIP-7702's recommendation of one pending transaction per delegated EOA applies as is. [ERC-7562](./eip-7562.md) validation of user operations should block `SETCODEFROM`, as it blocks `SELFDESTRUCT`: executed during validation, it changes code that the validation of other pending operations can depend on. For EIP-8141 frame transactions: - `SETCODEFROM` should be banned during validation-prefix execution. - A contract reached through `CALL*` or `EXTCODE*` during the validation prefix whose code contains a `SETCODEFROM` instruction, excluding `PUSH` data and other immediates, should be treated like an account with an [EIP-7702](./eip-7702.md) delegation indicator, i.e. as having mutable code. The regular code stored at the sender's address, running as the resolved target of a validation-prefix frame, is not treated as mutable merely because it contains `SETCODEFROM`; executing the instruction there is still covered by the first rule. - If such a contract's code instead contains `DELEGATECALL` or `CALLCODE`, it can still reach `SETCODEFROM` through delegated code. Nodes should track its code hash as a validation dependency and limit how many pending transactions may depend on it, so that one code update cannot invalidate an unbounded number of transactions. This keeps proxy-based accounts usable. ## 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 ## 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` copies whatever code hash the source holds when it executes, and a valid source can replace its own code hash, including between the time an adoption is authorized and the time it executes. An adoption authorized in advance, by a signature, a proposal or any other commitment, should therefore name the expected code hash and not only a source address, and the adopting code should compare `EXTCODEHASH(source)` with that hash immediately before `SETCODEFROM`, with no intervening external calls, so that a source whose code has changed in the meantime is rejected rather than adopted. `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. An [EIP-7702](./eip-7702.md) delegation executes its target's current code, so delegating to an account whose code can reach `SETCODEFROM` trusts every code that account may adopt. This now includes EOAs. A delegation to an EOA used to execute nothing, since an EOA's code is empty or a delegation indicator, which delegations do not follow. Once the EOA adopts code, every account delegated to it executes that code. 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 empty output with `success = 1`, as for a failed recovery. An EOA that adopts regular code retires its key and cannot undo the adoption: it can no longer originate ECDSA transactions, [EIP-7702](./eip-7702.md) can no longer redelegate it, and no instruction returns it to empty code. The adopted code is then the only way for the account to act; with [EIP-8151](./eip-8151.md), `ecRecover` no longer returns its address either, so code that authorizes through `ecRecover` against the account's own address fails permanently, and a fallback to the account's own key when no other key is set locks the account out at adoption. If authorization depends entirely on uninitialized state and no separately authenticated initialization path exists, the account may become unusable. Initialization should therefore be completed in the adopting transaction; initialization left open to anyone can be front-run. Adoption also changes how the account receives: a plain ETH transfer sent by `CALL`, or a token transfer with a receiver hook, now executes the adopted code and fails where the code rejects it or lacks the hook. This EIP does not require [EIP-8141](./eip-8141.md). On a chain without it, [EIP-3607](./eip-3607.md) leaves the account unable to originate transactions, so the account acts only when called. Adopted code that offers no relay path of its own, for example through an [ERC-4337](./eip-4337.md) entry point, is then stranded. If the chain later adds EIP-8141, the account can act again only if the adopted code supports EIP-8141 validation and approval. The EVM gives the code no reliable way to learn which EIPs are active on the chain. 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. Mempool rules that assume deployed code is immutable must account for `SETCODEFROM`; see [Public Mempool](#public-mempool). ## Copyright Copyright and related rights waived via [CC0](../LICENSE.md).