This EIP introduces a new EVM opcode, TCREATE, where T stands for transient, providing an official, gas-efficient, and state-aware mechanism for deploying temporary contracts.
Motivation
Many Ethereum applications do not require deployed code to persist beyond a single transaction. Examples include state-channel contracts, single-use accounts, and other ephemeral execution environments.
Today, such patterns rely on a combination of CREATE/CREATE2 and SELFDESTRUCT. However, SELFDESTRUCT has no longer provided gas refunds since EIP-3529, can only delete contracts created within the same transaction since the Cancun upgrade, and is expected to undergo further semantic restrictions in future protocol upgrades. As a result, the deploy-and-self-destruct pattern is no longer economically attractive, nor can its long-term behavior be relied upon.
Providing a dedicated EVM opcode for temporary contract deployment offers a lower-cost alternative while improving state management by preventing unnecessary long-term code accumulation. This proposal also provides a gradual migration path away from SELFDESTRUCT.
The proposal is fully compatible with account abstraction. Existing account semantics remain unchanged, while accounts may deploy transient contracts to implement extended execution logic entirely through calldata, for example, hook-based execution. Such hooks may execute dedicated logic or serve as reusable code templates that are invoked through DELEGATECALL.
TCREATE behaves identically to CREATE2 except for the following changes:
The address is computed as keccak256(0xfe || deployer_address || salt || keccak256(init_code))[12:], where deployer_address is the address of the account that executes TCREATE (the value returned by ADDRESS in the executing frame) and || denotes byte concatenation.
The nonce of the account at deployer_address is not incremented. This applies in every outcome, including an address collision and a REVERT or exceptional halt of the initcode. Nonce changes caused by the initcode follow their existing rules. The nonce limit of EIP-2681 still applies: if the nonce of the account at deployer_address is 2^64-1, TCREATE ends with 0 pushed on the stack and the target address is not accessed, as for CREATE2.
At the end of the transaction, the code and nonce that the target account has during the transaction are discarded and are not written to the state. The account balance is preserved. An account that becomes empty is removed from the state at the end of the transaction in accordance with EIP-161. The semantics are clarified further here.
Code deployment rules and collision semantics are identical to CREATE2, except for the nonce rule above and the gas rules in Gas Accounting. If the target address has a nonzero nonce or a nonzero code length, the creation fails as specified in EIP-684: the initcode is not executed, 0 is pushed on the stack, and the gas passed to the creation is consumed. The target account has a nonzero nonce from the start of its initcode execution until the end of the transaction, unless the creation fails, so a further TCREATE to the same address in that transaction fails in this way.
State transition handling
At the end of the transaction, the account state of every TCREATE account is finalized as follows:
The account nonce is 0.
The account has no code.
The account balance is preserved.
A TCREATE account has no storage, because SLOAD and SSTORE halt in its context (see SLOAD and SSTORE handling). Before the transaction, a TCREATE address has a zero nonce and no code, because otherwise the creation fails in accordance with EIP-684. The post-transaction state of the account therefore differs from its pre-transaction state only in its balance, and finalization does not delete code or storage from the state. The only state entry that finalization can remove is an empty account, in accordance with EIP-161.
TCREATE accounts
A TCREATE account is the account at an address computed by TCREATE in the current transaction. An address is a TCREATE account from the start of initcode execution at that address until the end of the transaction. If that TCREATE fails, or a frame that contains it reverts, the address is no longer a TCREATE account.
Code executes in the context of a TCREATE account when the current address, as returned by ADDRESS, is a TCREATE account. This includes the initcode and the runtime code of that account, and code that the account executes through DELEGATECALL or CALLCODE. It does not include code that executes with another current address: code of accounts that the TCREATE account calls or creates with CREATE, and code of the TCREATE account that another account executes through DELEGATECALL, CALLCODE, or an EIP-7702 delegation.
Each rule of this EIP that applies to a TCREATE account applies to all code that executes in the context of that account, and only to that code.
Opcode Behavior Changes
SLOAD and SSTORE handling
In the context of a TCREATE account, SLOAD and SSTORE MUST result in an exceptional halt of the current execution frame. The storage slot is not accessed, and no BAL entry is created for it. TLOAD and TSTORE (EIP-1153) are unchanged and remain available.
BAL handling
Because SLOAD and SSTORE halt in the context of a TCREATE account, a TCREATE account has no storage entries in the block-level Block Access List (BAL) defined by EIP-7928.
The finalization defined in State transition handling MUST be applied before the post-transaction values of the transaction are determined for the BAL. A TCREATE account therefore MUST NOT have nonce_changes or code_changes entries for the transaction, including when it executed CREATE. Its address is included in the BAL under the rules that EIP-7928 defines for CREATE2 target addresses, and its balance changes are recorded in accordance with EIP-7928.
TCREATE does not by itself cause a nonce_changes entry for the executing account. A contract created by CREATE in the context of a TCREATE account is recorded as any other deployed contract.
CREATE, CREATE2, and TCREATE handling
In the context of a TCREATE account, CREATE is permitted and follows its existing semantics. The contract it creates is not a TCREATE account and persists after the transaction.
The nonce of a TCREATE account is 1 when its initcode execution begins (EIP-161) and is reset to 0 at the end of the transaction. A CREATE executed when the account nonce is n derives its address from n. If the account is created again in a later transaction, a CREATE executed at a given nonce therefore derives the same address as a CREATE executed at that nonce in the earlier transaction. If that address has a nonzero nonce or a nonzero code length, the creation fails as specified in EIP-684, and the nonce of the TCREATE account is still incremented. A nonzero balance alone at that address does not cause the creation to fail.
In the context of a TCREATE account, CREATE2 MUST result in an exceptional halt of the current execution frame. The halt occurs regardless of the endowment, the nonce of the executing account, and the call depth: in this context CREATE2 never pushes 0, and the address of the new account is not accessed. This CREATE2 therefore adds no entry to the BAL.
In the context of a TCREATE account, TCREATE is permitted. The restriction on CREATE2 does not apply to it. The account it creates is also a TCREATE account, and all rules of this EIP apply to it.
SELFDESTRUCT handling
In the context of a TCREATE account, SELFDESTRUCT behaves as specified in EIP-6780 for an account that was not created in the current transaction: it transfers the entire balance of the account to the target and halts the current execution frame. The account is not marked for deletion and is not treated as destroyed within the transaction for the purposes of EIP-7928; its BAL entries follow BAL handling, and it is finalized only as specified in State transition handling. The change to its balance is accounted as specified in Gas Accounting, and the SELFDESTRUCT refill rules of EIP-8037 do not apply to it. All other rules of SELFDESTRUCT, including the state-gas charge when the transfer creates the target account and the transfer log of EIP-7708, are unchanged.
A contract created by CREATE in the context of a TCREATE account is not a TCREATE account. SELFDESTRUCT executed by that contract follows EIP-6780.
Gas Accounting
Each execution of TCREATE is charged BASE_OPCODE_COST together with the execution-gas costs that normally apply to CREATE2, including initcode execution, jumpdest analysis, memory expansion, copying costs, and other execution-related charges.
TCREATE does not incur the account-creation charges associated with CREATE2, such as GAS_NEW_ACCOUNT, or the code-deposit state gas of EIP-8037 (CPSB per byte of deployed code). The code-deposit hash cost of EIP-8037, 6 × ceil(L / 32) execution gas where L is the length of the deployed code, is charged as for CREATE2.
Account access costs continue to follow EIP-8038. In particular, accesses to cold accounts are charged COLD_ACCOUNT_ACCESS as appropriate.
State-gas accounting is defined separately by this EIP. Rather than charging for account creation itself, state-gas is charged only for state changes that cannot be reasonably reclaimed by the end of the transaction, and refilled when those state changes are reverted within the same transaction.
Any operation that modifies the balance of the target account of a TCREATE operation, including during contract initialization, performs state-gas accounting according to the following table.
Original balance
Current balance
New balance
Description
State-gas charges/refills
0
x
0
Balance restored to zero when the initial transaction balance was zero
STATE_BYTES_PER_NEW_ACCOUNT × CPSB refilled
0
0
x
First balance written to the account
STATE_BYTES_PER_NEW_ACCOUNT × CPSB charged
0 or x
y
z
Existing account balance updated
No state-gas adjustment
This EIP does not change the ACCOUNT_WRITE charges of EIP-8038. An operation that modifies the balance of a TCREATE account pays ACCOUNT_WRITE as for any other account, with no per-account tracking and no refund.
Rationale
Lower Deployment and Execution Costs
Today, combining CREATE with SELFDESTRUCT is the only mechanism for implementing temporary contracts. This approach is relatively expensive and no longer benefits from gas refunds, reducing incentives for efficient state management.
By making the cost of temporary contracts comparable to that of a regular CALL, this proposal encourages experimentation with transient execution models while avoiding unnecessary long-term state growth.
Address Prefix Selection
The 0xfe prefix is chosen to ensure that the address derivation scheme used by TCREATE cannot collide with that of CREATE2, which uses the 0xff prefix as specified in EIP-1014.
The TCREATE preimage has the same length as the CREATE2 preimage (85 bytes), so the prefix byte is what separates the two address spaces. As the first byte of an RLP encoding, 0xfe starts a list whose payload is at least 2^48 bytes long, so it cannot collide with CREATE addresses, whose preimages are 23 to 31 bytes long and start with a byte from 0xd6 to 0xde. The preimage fits in a single 136-byte keccak256 block.
Initcode Hash in the Address
The address includes the hash of the initcode, as for CREATE2. EOFCREATE (EIP-7620) and the creation of EIP-7873 omit it and derive the address from the sender and salt only. Their reasons do not apply to TCREATE: EOFCREATE deploys a subcontainer that is fixed in the calling code, and hashing it would prevent rewriting that container and would be repeated on every execution. TCREATE takes its initcode from memory, as CREATE2 does, so without the hash the deploying account could execute different code at the same address.
A TCREATE address is created again in later transactions and keeps its balance between them. With the hash, a party that sends value to the address can verify from the address alone which code will execute there. A deployment that needs an address independent of the code of the deployed contract can use CREATE from a TCREATE account, as described in CREATE, CREATE2, and TCREATE handling.
Temporary Nonce Semantics
Although TCREATE creates only transient account state, the account it creates follows the nonce semantics of CREATE2 during the transaction: its nonce is incremented from 0 to 1 before initcode execution, in accordance with EIP-161.
This avoids introducing additional exceptional rules for address collision handling. In particular, the target account has a nonzero nonce from the start of initcode execution, which prevents multiple initialization frames from executing simultaneously at the same address before code deposition.
Resetting the nonce of TCREATE accounts at the end of the transaction preserves the stateless nature of TCREATE while allowing implementations to reuse existing contract creation logic with minimal modifications.
Unlike CREATE2, TCREATE does not increment the nonce of the deploying account. The TCREATE address does not depend on this nonce, so an increment has no function. A permanent increment would change the deploying account on every transient deployment. An increment that is restored at the end of the transaction would cause address collisions: a CREATE executed by the deploying account after TCREATE in the same transaction would use the incremented nonce, and after the restore a CREATE in a later transaction would derive the same address again. Leaving the nonce unchanged also removes the need to record and restore it at the end of the transaction.
The nonce limit of EIP-2681 is kept, although no increment can overflow, so that TCREATE fails under the same preflight conditions as CREATE2. An account cannot reach this nonce in practice.
For an EOA delegated through EIP-7702, TCREATE executed by the delegated code does not change the nonce of the EOA, whether or not the EOA is the transaction sender. Unlike CREATE and CREATE2, TCREATE therefore cannot invalidate pending transactions of the EOA through a nonce change.
SLOAD and SSTORE handling
An earlier version of this EIP made SLOAD and SSTORE in a TCREATE account operate on storage discarded at the end of the transaction, to keep existing contract code working. Specifying that precisely requires three additional rules, each of which clients could implement differently. First, whether this storage is shared with the EIP-1153 transient storage: contracts can observe the choice, because TLOAD after SSTORE of the same key returns either the stored value or 0. Second, whether SSTORE keeps the gas stipend check of EIP-2200, which TSTORE does not have. Third, how these accesses are recorded in the BAL of EIP-7928.
Temporary contracts in current use do not use storage. Deposit forwarders, intent executors, and CREATE3 proxies keep their data in memory or read it from the deploying contract. TLOAD and TSTORE provide storage scoped to the transaction, and Solidity exposes them as transient state variables.
The prohibition keeps the account free of state other than its balance and removes the three rules above. As for CREATE2, a later upgrade can remove it, and only executions that halt under this EIP would change. Its cost is that code which reads or writes regular state variables halts in this context, including library code that keeps state in storage, and must use transient storage instead.
CREATE, CREATE2, and TCREATE handling
The first CREATE that a TCREATE account executes in a transaction derives its address from nonce 1, so that address depends only on deployer_address, the salt, and the TCREATE initcode. If the TCREATE initcode does not fix the code of the created contract, for example when the account receives the initcode of the created contract in a later call, the address does not depend on the code of the created contract. This is the property of the deployment pattern commonly called CREATE3, in which a proxy deployed with CREATE2 deploys the target contract with CREATE, and the proxy then either persists or executes SELFDESTRUCT. TCREATE provides this property without a persistent proxy and without SELFDESTRUCT. The proxy leaves no code, nonce, or storage in the state; its account remains only if it holds a balance at the end of the transaction. Because TCREATE addresses use the 0xfe prefix, the resulting addresses differ from those of existing CREATE3 deployers.
While a contract created in this way has a nonzero nonce or a nonzero code length, a repeated creation at its address fails in accordance with EIP-684. As with a self-destructing proxy, a created contract that executes SELFDESTRUCT in the transaction that created it is removed in accordance with EIP-6780, and a later transaction can deploy different code at the same address (see Security Considerations).
CREATE2 in the context of a TCREATE account adds no capability. Its address includes the hash of the initcode of the created contract, so it does not provide the property above. The deploying account can obtain an equally deterministic address by executing CREATE2 directly, with a salt that includes the TCREATE salt and initcode hash, and a TCREATE account can call a contract that executes CREATE2. Existing patterns that create a contract from a proxy removed in the same transaction use CREATE.
Address collisions are not a reason for the prohibition. A repeated CREATE2 with the same salt and initcode collides in accordance with EIP-684 from any account, in the same way as a repeated CREATE from a TCREATE account that is created again.
The prohibition is specified for forward compatibility. A later upgrade can remove it, and only executions that halt under this EIP would change. Adding the prohibition after CREATE2 has been permitted would change executions that succeed. The check uses the same execution-context condition as the SLOAD and SSTORE handling, so it adds little implementation complexity. Its cost is that code which executes CREATE2 in this context halts. This includes a Solidity new expression with a salt option, and library code that deploys a clone or a CREATE3 proxy at a deterministic address, whether that code is compiled into the contract or reached through DELEGATECALL. A compiler cannot detect this case, because it does not know whether the contract will be deployed with TCREATE.
Permitting CREATE2 was considered. It removes one exceptional case and follows the principle that an opcode is permitted unless there is a specific reason to prohibit it. It was not selected because it adds no capability and no known deployment pattern executes CREATE2 from a contract that exists for one transaction only. The prohibition can be removed if such a pattern appears.
TCREATE in the context of a TCREATE account creates another account that is removed at the end of the transaction. Creating the parent again in a later transaction and executing the same TCREATE therefore does not collide, and TCREATE is permitted.
SELFDESTRUCT handling
Without a rule, SELFDESTRUCT in the context of a TCREATE account would be subject to overlapping rules: EIP-6780 deletes an account that executes SELFDESTRUCT in the transaction that created it, EIP-8037 gives no state-gas refill in that case, EIP-7928 records the storage of such an account as reads, and this EIP refills state gas when the balance of a TCREATE account returns to an original value of 0.
The finalization of this EIP, not SELFDESTRUCT, removes the account. SELFDESTRUCT therefore keeps only the meaning that EIP-6780 gives it for accounts created in an earlier transaction and that EIP-4758 proposes for all accounts: it moves the balance without executing code at the target. The rules of this EIP apply to the balance change and the BAL, so the rules above do not overlap, and existing temporary-contract code that ends with SELFDESTRUCT runs unchanged. Prohibiting SELFDESTRUCT was considered. A value-transferring call is not an equivalent replacement, because it executes code at the target and fails if the target reverts; PAY (EIP-5920) would be.
ACCOUNT_WRITE for Balance Changes
An earlier version of this EIP charged ACCOUNT_WRITE once per TCREATE account and refunded it when the balance returned to its original value. EIP-8038 charges ACCOUNT_WRITE per operation, with no per-account tracking and no refund. A refund would also go through the refund counter of the transaction, which is capped and only credited at the end of the transaction, so a transaction would still need a gas limit that covers the full charge. This EIP therefore keeps the EIP-8038 charges unchanged and adjusts only state gas, which EIP-8037 refills directly.
State-Gas Accounting for Balance Transfers
Removing balance accounting entirely would simplify gas accounting but would reintroduce a special case that EIP-8246 sought to eliminate.
Preserving balance semantics while accounting only for temporary state changes provides a more consistent design. This decision may be revisited if native balance-burning mechanisms are introduced in the future.
Extended state accounting method
Unlike CREATE2, whose state-gas accounting is tied to permanent account creation, TCREATE accounts are ephemeral. Therefore, charging for account creation itself would overestimate the persistent state introduced by the opcode. Instead, this EIP generalizes the state-accounting model of EIP-8037 by charging only for state that remains after transaction execution and refilling charges when transient state is completely eliminated.
State creation gas is not upfront
The absence of upfront account-creation and code-deposit state-gas charges is possible because the client knows from the execution of TCREATE that the target account is temporary. Unlike CREATE2, whose resulting account may persist after the transaction, the code and nonce of a TCREATE account are guaranteed by the protocol not to be written to the state at the end of the transaction. The client therefore does not need to reserve gas in advance for persistent account state that cannot survive the transaction.
This does not make account creation or code execution free. The execution costs required to initialize and execute the account are still charged normally, including the hash of the deposited code, which clients compute at deposit to set the code hash of the account, and state-gas is charged whenever execution creates state that may persist beyond the temporary lifetime of the account. In particular, a non-zero balance transferred to a TCREATE account is charged as account state because the balance is preserved at the end of the transaction. If that balance is subsequently transferred out in full during the same transaction, the corresponding state-gas charge is refilled because the state no longer needs to be preserved.
Backwards Compatibility
Contract Execution Flow
This EIP adds a new opcode and does not change the behavior of existing contracts.
A temporary-use contract deployed with TCREATE instead of CREATE or CREATE2 followed by SELFDESTRUCT differs in the following ways:
No deployed bytecode is retained after the transaction completes. The account balance remains at the account’s address.
Its address uses the 0xfe prefix and differs from the address of an equivalent CREATE2 deployment.
TCREATE does not increment the nonce of the deploying account.
SLOAD, SSTORE, and CREATE2 in the context of the account result in an exceptional halt. Code that uses regular state variables, or that deploys with a salt (for example Solidity new C{salt: s}()), cannot run in this context. Such code can use transient storage and CREATE.
SELFDESTRUCT in the context of the account transfers the balance and halts, without deleting the account.
Applications requiring persistent code or storage should continue to use CREATE or CREATE2.
Security Considerations
Repeated Contract Deployment
A TCREATE address depends only on deployer_address, the salt, and the initcode hash. It is derived in the same way as a CREATE2 address, but with the prefix 0xfe. No account state except balance is written to the state at the end of the transaction, so the same address is a valid deployment target again in a later transaction under EIP-684.
Contracts whose runtime code depends on execution context (for example, using NUMBER, ORIGIN, or similar opcodes) may exhibit different behavior across deployments if appropriate safeguards are not implemented.
Contracts Created from TCREATE Accounts
The nonce of a TCREATE account is 1 at the start of every transaction in which it is created, so its CREATE operations derive the same sequence of addresses in each of these transactions. These addresses commit to the TCREATE initcode, but not to the initcode passed to CREATE unless the TCREATE initcode fixes it. If the TCREATE account takes that initcode from its input, the party that supplies it decides the code at these addresses. A contract that executes TCREATE for arbitrary callers should include the caller address in the salt, as current CREATE3 factories do, and a TCREATE account should accept CREATE requests only from its deploying account. Otherwise any caller can deploy arbitrary code at an address that another user expects, on every chain where the deploying contract has the same address.
If a contract created in this way executes SELFDESTRUCT in the transaction that created it, it is removed in accordance with EIP-6780. A later transaction can create the TCREATE account again and deploy different code at the same address. Existing CREATE3 deployers that remove their proxy with SELFDESTRUCT have the same property. A contract that persists beyond the transaction that created it cannot be replaced.
Authority Granted to TCREATE Accounts
The finalization at the end of the transaction does not remove state that other accounts hold about a TCREATE address, such as an owner, an allowance, or an allowlist entry. In a later transaction, the deploying account can use that authority again by creating the account again with the same salt and initcode, and that initcode can return different runtime code in each transaction. A contract created by CREATE from a TCREATE account observes that account as CALLER in its initcode. Contracts should not grant lasting authority to a TCREATE address. A contract created from one should receive privileged addresses as explicit initcode arguments.
An EIP-7702 delegation to a TCREATE address is a case of such authority. The delegation indicator remains in the code of the delegating account, but the TCREATE address has no code between transactions. In a transaction that does not create the TCREATE account, a call to the delegating account executes empty code and succeeds with no effect. The delegated code is available only after the deploying account creates the TCREATE account in the same transaction, and the initcode can return different runtime code in each transaction. This code executes in the context of the delegating account, so the rules of this EIP do not apply to it. Accounts should not delegate to a TCREATE address.