We prepare the same synthetic contract for two networks but change a constructor argument. Under CREATE2 that changes the initialization code hash and therefore the address derivation input. If matching addresses are a requirement then fixing the salt alone does not solve the problem.

Our independent fixture builds address preimages using byte buffers and checks their inputs. It does not implement Keccak or deploy bytecode and it prints no deployment address. Repeated byte patterns stand in for synthetic deployers and hashes so the example contains no actual network configuration.

CREATE2 includes the initialization code hash

The CREATE2 preimage contains a one byte prefix and a twenty byte deploying address followed by a thirty two byte salt and a thirty two byte initialization code hash. Its total length is 85 bytes. The resulting address comes from the final twenty bytes of the Keccak256 digest.

Initialization code includes the constructor argument bytes used for deployment. Changing an argument can therefore change the address even when the Solidity source and salt stay the same. Changing the compiler output or linked library references can change the preimage for the same reason.

function create2Preimage(deployer, salt, initializationHash) {
  if (
    deployer.length !== 20 ||
    salt.length !== 32 ||
    initializationHash.length !== 32
  )
    throw new Error("byte length");
  return Buffer.concat([
    Buffer.from([255]),
    deployer,
    salt,
    initializationHash,
  ]);
}
function firstChildPreimage(proxy) {
  if (proxy.length !== 20) throw new Error("proxy length");
  return Buffer.concat([Buffer.from([214, 148]), proxy, Buffer.from([1])]);
}

The helper accepts byte buffers of exact lengths and rejects an incorrectly sized salt. A text string that looks like hex is not automatically the binary sequence expected by the formula. Encoding differences are enough to invalidate an otherwise correct address prediction.

CREATE3 is a construction using existing operations

A CREATE3 pattern first uses CREATE2 to deploy a small proxy whose initialization code is fixed. The proxy then uses CREATE to deploy the requested contract. The child address depends on the proxy address and its creation nonce instead of the requested contract's initialization code hash.

For a construction where the proxy creates its first child at nonce one the RLP preimage is 23 bytes. It contains the list prefix and address prefix followed by the proxy address and the encoded nonce. Our fixture constructs those bytes from a synthetic proxy address but does not calculate that proxy address from the first stage.

We remove the requested contract's initialization code from the address derivation only by moving it behind a fixed proxy stage because changing the proxy's own initialization code or its deployer or its salt still changes the proxy address and therefore changes the child address that the second stage produces.

CREATE3 is not a new EVM opcode in this discussion. The exact proxy construction and its nonce behavior are part of the address derivation. A different library can expose a similarly named deployment function while making different assumptions.

Matching addresses do not establish matching configuration

A deterministic address says where a successful deployment is expected to live. It does not prove that the code there has the intended constructor arguments or permissions. A deployment procedure must inspect the resulting runtime and read the configuration that initialization established.

If two networks intentionally use different dependency addresses then identical contract addresses can still hold different valid state. That is the purpose of removing constructor dependence but it also makes postdeployment verification necessary. Address equality cannot stand in for a configuration comparison.

CREATE2 preimage: 85 bytes; first child RLP preimage: 23 bytes
8 assertions passed

The tests show that changing an initialization hash changes a CREATE2 preimage. They also show that the fixed proxy stage has identical input while requested constructor data changes outside that stage. Separate tests change the deployer and salt to ensure those dependencies remain visible.

Salt authority belongs in the threat model

An unrestricted factory can let another caller consume a salt first or deploy an unexpected payload at an anticipated destination. Whether that risk applies depends on the factory's exact derivation and access policy. Salts may be namespaced by caller or protected by explicit authorization.

The fixture only checks byte structure and dependency relationships. It does not prove collision resistance or factory safety. A Solidity integration test should compare the predictor against actual CREATE and CREATE2 results and reject failed or empty deployments according to the chosen factory contract.

Address prediction is ready for an EVM integration check once its byte inputs are fixed. Deploy a tiny independent contract on two isolated local chains using different constructor values and verify both the predicted address and the intentionally different stored configuration.

Sources and examples