This page is a placeholder. All examples on this page are currently AI-generated and are not correct. This documentation will be completed in the future with accurate, tested examples.
Overview
Opcode: 0xf5
Introduced: Constantinople (EIP-1014)
CREATE2 deploys a new contract with a deterministic address computed from creator, salt, and init code hash. Unlike CREATE (which uses nonce), the address is predictable before deployment, enabling counterfactual instantiation and state channels.
Specification
Stack Input:
value (wei to send)
offset (memory offset of init code)
length (size of init code)
salt (32-byte salt for address computation)
Stack Output:
address (deployed contract address, or 0 if failed)
Gas Cost: 32,000 + init_code_cost + keccak256_cost + memory_expansion + deployment_cost
Operation:
initCodeHash = keccak256(memory[offset:offset+length])
address = keccak256(0xff ++ sender ++ salt ++ initCodeHash)[12:]
success = deploy(address, init_code, value, gas * 63/64)
push(success ? address : 0)
Behavior
CREATE2 performs deterministic contract deployment:
- Pop 4 stack arguments: value, offset, length, salt
- Validate static context: Cannot be called in static mode (EIP-214)
- Charge gas:
- Base: 32,000 gas
- Init code: 2 gas/word (EIP-3860)
- Keccak256: 6 gas/word (for init code hash)
- Memory expansion
- Read init code from memory
- Compute deterministic address:
initCodeHash = keccak256(initCode)
preimage = 0xff ++ sender (20 bytes) ++ salt (32 bytes) ++ initCodeHash (32 bytes)
address = keccak256(preimage)[12:] // Last 20 bytes
- Check collision: Fail if account exists at computed address
- Forward gas: Up to 63/64 of remaining gas (EIP-150)
- Execute init code in new context
- Store runtime code if successful (charged 200 gas/byte)
- Push address to stack (0 if failed)
Key differences from CREATE:
- Address depends on salt and code hash, not nonce
- Additional 6 gas/word for keccak256 hashing
- Address predictable before deployment
- Enables counterfactual patterns
Examples
Basic CREATE2 Deployment
import { CREATE2 } from '@tevm/voltaire/evm/system';
import { keccak256 } from '@tevm/voltaire/crypto';
import { Address } from '@tevm/voltaire/primitives';
// Init code: returns single byte 0x42
const initCode = Bytecode([
0x60, 0x42, // PUSH1 0x42
0x60, 0x00, // PUSH1 0x00
0x52, // MSTORE
0x60, 0x01, // PUSH1 0x01 (length)
0x60, 0x1f, // PUSH1 0x1f (offset)
0xf3 // RETURN
]);
const frame = createFrame({
gasRemaining: 1000000n,
address: Address("0x1234..."),
});
// Write init code to memory
for (let i = 0; i < initCode.length; i++) {
frame.memory.set(i, initCode[i]);
}
// Compute address before deployment
const salt = 0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdefn;
const predictedAddress = predictCreate2Address(
frame.address,
salt,
initCode
);
console.log("Will deploy to:", predictedAddress);
// Stack: [value=0, offset=0, length=9, salt]
frame.stack.push(salt); // salt
frame.stack.push(9n); // length
frame.stack.push(0n); // offset
frame.stack.push(0n); // value
const err = CREATE2(frame);
console.log(frame.stack[0]); // Deployed address (matches prediction!)
Address Prediction
import { Address, Hash } from '@tevm/voltaire/primitives';
import { keccak256 } from '@tevm/voltaire/crypto';
function predictCreate2Address(
creator: Address,
salt: bigint,
initCode: Uint8Array
): Address {
// 1. Hash init code
const initCodeHash = keccak256(initCode);
// 2. Build preimage: 0xff ++ creator ++ salt ++ initCodeHash
const preimage = new Uint8Array(1 + 20 + 32 + 32);
preimage[0] = 0xff;
preimage.set(creator, 1);
// Convert salt to bytes (big-endian)
for (let i = 0; i < 32; i++) {
preimage[21 + i] = Number((salt >> BigInt(8 * (31 - i))) & 0xffn);
}
preimage.set(initCodeHash, 53);
// 3. Hash and extract address
const hash = keccak256(preimage);
return Address(hash.slice(12));
}
// Example usage
const creator = Address("0x742d35Cc6634C0532925a3b844Bc454e4438f44e");
const salt = 0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdefn;
const initCode = Bytecode();
const predicted = predictCreate2Address(creator, salt, initCode);
console.log("Contract will deploy to:", Address.toHex(predicted));
Factory Pattern with CREATE2
contract Create2Factory {
event Deployed(address indexed addr, bytes32 indexed salt);
// Deploy contract with deterministic address
function deploy(bytes memory bytecode, bytes32 salt) external returns (address) {
address addr;
assembly {
// CREATE2(value, offset, length, salt)
addr := create2(
0, // No ETH sent
add(bytecode, 0x20), // Skip length prefix
mload(bytecode), // Init code length
salt // Salt
)
if iszero(addr) {
revert(0, 0)
}
}
emit Deployed(addr, salt);
return addr;
}
// Predict deployment address
function computeAddress(
bytes memory bytecode,
bytes32 salt
) public view returns (address) {
bytes32 bytecodeHash = keccak256(bytecode);
bytes32 hash = keccak256(
abi.encodePacked(
bytes1(0xff),
address(this),
salt,
bytecodeHash
)
);
return address(uint160(uint256(hash)));
}
// Check if contract deployed at address
function isDeployed(address addr) public view returns (bool) {
uint256 size;
assembly {
size := extcodesize(addr)
}
return size > 0;
}
}
Minimal Proxy Factory (EIP-1167)
contract CloneFactory {
// Minimal proxy bytecode with CREATE2
function cloneDeterministic(
address implementation,
bytes32 salt
) external returns (address instance) {
bytes20 targetBytes = bytes20(implementation);
assembly {
let ptr := mload(0x40)
mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000)
mstore(add(ptr, 0x14), targetBytes)
mstore(add(ptr, 0x28), 0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000)
instance := create2(0, ptr, 0x37, salt)
}
require(instance != address(0), "Clone failed");
}
// Predict clone address
function predictCloneAddress(
address implementation,
bytes32 salt
) external view returns (address) {
bytes20 targetBytes = bytes20(implementation);
bytes memory bytecode = abi.encodePacked(
hex"3d602d80600a3d3981f3363d3d373d3d3d363d73",
targetBytes,
hex"5af43d82803e903d91602b57fd5bf3"
);
bytes32 hash = keccak256(
abi.encodePacked(
bytes1(0xff),
address(this),
salt,
keccak256(bytecode)
)
);
return address(uint160(uint256(hash)));
}
}
Counterfactual Instantiation
// State channel pattern
contract StateChannel {
mapping(bytes32 => bool) public deployed;
// Deploy channel only when needed (counterfactual)
function finalizeChannel(
bytes memory channelCode,
bytes32 salt,
bytes memory finalState
) external {
bytes32 channelId = keccak256(abi.encodePacked(channelCode, salt));
require(!deployed[channelId], "Already deployed");
// Predict address
address predictedAddr = computeAddress(channelCode, salt);
// Verify off-chain state was for this address
require(verifyState(predictedAddr, finalState), "Invalid state");
// Deploy actual contract
address addr;
assembly {
addr := create2(0, add(channelCode, 0x20), mload(channelCode), salt)
}
require(addr == predictedAddr, "Address mismatch");
deployed[channelId] = true;
}
// Channel can operate off-chain at predicted address
// Only deploys on-chain when dispute or finalization needed
}
Gas Cost
Total cost: 32,000 + init_code_cost + keccak256_cost + memory_expansion + deployment_cost
Base Cost: 32,000 gas
Fixed cost for CREATE2 operation (same as CREATE).
Init Code Cost (EIP-3860)
Shanghai+: 2 gas per word for init code:
init_code_cost = 2 * ceil(init_code_length / 32)
Pre-Shanghai: No init code cost.
Keccak256 Cost
CREATE2-specific: 6 gas per word for hashing init code:
keccak256_cost = 6 * ceil(init_code_length / 32)
This is the primary cost difference vs CREATE.
Memory Expansion
Dynamic cost for reading init code from memory:
words_needed = ceil((offset + length) / 32)
expansion_cost = (words_needed)² / 512 + 3 * (words_needed - current_words)
Deployment Cost
Runtime code storage: 200 gas per byte of deployed code:
deployment_cost = 200 * runtime_code_length
Gas Forwarding (EIP-150)
Same as CREATE - 63/64 rule applies.
Example Calculation
// Deploy with 100-byte init code, returns 50-byte runtime code
const initCodeLength = 100;
const runtimeCodeLength = 50;
// Base cost
const baseCost = 32000;
// Init code cost (Shanghai+): 2 gas/word
const initCodeWords = Math.ceil(initCodeLength / 32); // 4 words
const initCodeCost = 2 * initCodeWords; // 8 gas
// Keccak256 cost (CREATE2-specific): 6 gas/word
const keccak256Cost = 6 * initCodeWords; // 24 gas
// Memory expansion (assume clean memory)
const memWords = Math.ceil(initCodeLength / 32); // 4 words
const memCost = Math.floor(memWords ** 2 / 512) + 3 * memWords; // 12 gas
// Deployment cost: 200 gas/byte
const deploymentCost = 200 * runtimeCodeLength; // 10,000 gas
// Total charged to caller
const totalCost = baseCost + initCodeCost + keccak256Cost + memCost; // 32,044 gas
// Gas forwarded to init code (assume 100,000 remaining)
const remainingAfterCharge = 100000 - totalCost; // 67,956 gas
const forwardedGas = remainingAfterCharge - Math.floor(remainingAfterCharge / 64); // 66,895 gas
// Total consumed
const totalConsumed = totalCost + forwardedGas + deploymentCost; // 108,939 gas
Common Usage
Upgradeable Factory
contract UpgradeableFactory {
address public implementation;
function deployProxy(bytes32 salt) external returns (address) {
bytes memory bytecode = getProxyBytecode(implementation);
address proxy;
assembly {
proxy := create2(0, add(bytecode, 32), mload(bytecode), salt)
}
require(proxy != address(0), "Deploy failed");
return proxy;
}
function upgrade(address newImpl) external {
implementation = newImpl;
// Future deployments use new implementation
}
}
Registry Pattern
contract Registry {
mapping(address => bool) public registered;
function register(bytes memory code, bytes32 salt) external {
// Predict address
address predicted = computeAddress(code, salt);
require(!registered[predicted], "Already registered");
// Deploy
address deployed;
assembly {
deployed := create2(0, add(code, 32), mload(code), salt)
}
require(deployed == predicted, "Mismatch");
registered[deployed] = true;
}
}
Multi-signature Wallet Factory
contract WalletFactory {
function createWallet(
address[] memory owners,
uint256 threshold,
bytes32 salt
) external returns (address wallet) {
// Deterministic address based on configuration
bytes memory bytecode = abi.encodePacked(
type(MultiSigWallet).creationCode,
abi.encode(owners, threshold)
);
assembly {
wallet := create2(0, add(bytecode, 32), mload(bytecode), salt)
}
require(wallet != address(0), "Deploy failed");
}
function computeWalletAddress(
address[] memory owners,
uint256 threshold,
bytes32 salt
) external view returns (address) {
bytes memory bytecode = abi.encodePacked(
type(MultiSigWallet).creationCode,
abi.encode(owners, threshold)
);
bytes32 hash = keccak256(
abi.encodePacked(
bytes1(0xff),
address(this),
salt,
keccak256(bytecode)
)
);
return address(uint160(uint256(hash)));
}
}
Security
Address Collision Attacks
CREATE2 enables deliberate address collisions through init code manipulation:
// VULNERABLE: Pre-Cancun collision attack
contract VulnerableFactory {
function deploy(bytes memory code, bytes32 salt) external {
address addr;
assembly {
addr := create2(0, add(code, 32), mload(code), salt)
}
// Deployed contract A
}
}
// Attack (pre-EIP-6780):
// 1. Deploy Contract A with init_code_1, salt
// 2. Selfdestruct Contract A
// 3. Deploy Contract B with init_code_2, SAME salt
// Result: Different code at same address!
// Post-Cancun (EIP-6780): SELFDESTRUCT doesn't delete in same tx
// Attack mitigated: Cannot redeploy at same address
Mitigation (EIP-6780): SELFDESTRUCT only deletes if created in same transaction.
Init Code Hash Importance
Address depends on init code hash - different code = different address:
// These deploy to DIFFERENT addresses even with same salt
bytes memory code1 = type(Contract).creationCode;
bytes memory code2 = abi.encodePacked(
type(Contract).creationCode,
abi.encode(123) // Constructor argument
);
// Different init code hash = different address
address addr1 = create2(0, add(code1, 32), mload(code1), salt);
address addr2 = create2(0, add(code2, 32), mload(code2), salt);
// addr1 != addr2
Salt Reuse
Same salt with same code fails:
// VULNERABLE: Deployment fails if salt reused
function deploy(bytes memory code, bytes32 salt) external {
address addr;
assembly {
addr := create2(0, add(code, 32), mload(code), salt)
}
// Reverts if contract already exists at computed address
}
// SAFE: Include unique data in salt
function deployUnique(bytes memory code, uint256 nonce) external {
bytes32 salt = keccak256(abi.encodePacked(msg.sender, nonce));
// Address unique per user + nonce
}
Contracts that change code after deployment:
// DANGEROUS: Metamorphic pattern (pre-Cancun)
contract Metamorphic {
function morph() external {
// Change implementation
selfdestruct(payable(address(this)));
// Then redeploy different code at same address via CREATE2
}
}
// Post-Cancun: EIP-6780 prevents this
// SELFDESTRUCT doesn't delete code unless created in same tx
Frontrunning
Deterministic addresses enable frontrunning:
// VULNERABLE: Public salt + code = frontrunnabale
function deploy(bytes memory code, bytes32 salt) external {
// Attacker sees tx in mempool
// Frontruns with higher gas price
// Deploys at target address first!
address addr;
assembly {
addr := create2(0, add(code, 32), mload(code), salt)
}
}
// MITIGATION: Include msg.sender in salt
bytes32 salt = keccak256(abi.encodePacked(msg.sender, userProvidedSalt));
// Address unique per deployer
Constructor Reentrancy
Same reentrancy risks as CREATE:
contract Vulnerable {
mapping(address => bool) public trusted;
function deployAndTrust(bytes memory code, bytes32 salt) external {
address deployed;
assembly {
deployed := create2(0, add(code, 32), mload(code), salt)
}
// VULNERABLE: Constructor could re-enter here
trusted[deployed] = true;
}
}
Implementation
/**
* CREATE2 opcode (0xf5)
* Create contract at deterministic address
*/
export function create2(frame: FrameType): EvmError | null {
// EIP-214: Cannot be called in static context
if (frame.isStatic) {
return { type: "WriteProtection" };
}
// Pop 4 arguments
const value = popStack(frame);
const offset = popStack(frame);
const length = popStack(frame);
const salt = popStack(frame);
// Calculate gas cost
const words = Math.ceil(Number(length) / 32);
let gasCost = 32000n; // Base
gasCost += BigInt(words * 2); // Init code (EIP-3860)
gasCost += BigInt(words * 6); // Keccak256 for address
// Memory expansion
if (length > 0) {
const end = Number(offset) + Number(length);
gasCost += memoryExpansionCost(frame, end);
updateMemorySize(frame, end);
}
// Charge gas
consumeGas(frame, gasCost);
// Read init code
const initCode = readMemory(frame, offset, length);
// Compute deterministic address
const initCodeHash = keccak256(initCode);
const preimage = new Uint8Array(1 + 20 + 32 + 32);
preimage[0] = 0xff;
preimage.set(frame.address, 1);
preimage.set(saltToBytes(salt), 21);
preimage.set(initCodeHash, 53);
const addressHash = keccak256(preimage);
const address = addressHash.slice(12);
// Check collision
if (accountExists(address)) {
pushStack(frame, 0n); // Failure
frame.returnData = new Uint8Array(0);
return null;
}
// Calculate forwarded gas (63/64 rule)
const afterCharge = frame.gasRemaining;
const maxForward = afterCharge - afterCharge / 64n;
// Execute deployment
const result = executeCreate({
address: address,
initCode: initCode,
value: value,
gas: maxForward
});
// Refund unused gas
frame.gasRemaining += result.gasLeft;
// Set return_data
frame.returnData = result.success ? new Uint8Array(0) : result.output;
// Push address (0 if failed)
pushStack(frame, result.success ? addressToBigInt(address) : 0n);
frame.pc += 1;
return null;
}
References