EVM Compatibility
OXN runs a fully compatible EVM inside its enclave. In practice, nearly every contract that runs on Ethereum will also run on OXN unmodified. This page documents the small number of places where behavior differs, so you can spot them before they cause a surprise.
Compiler settings that matter
Pin evmVersion to "paris". This is the single most important setting on OXN and the most common source of "invalid code" errors.
Solidity 0.8.20 and later default to evmVersion: "shanghai", which emits the PUSH0 opcode (0x5F). OXN's runtime does not yet implement PUSH0. Contracts compiled with the Shanghai default will deploy successfully but revert at runtime with invalid code on any execution path that reaches a PUSH0 — and there will be many of them in typical Solidity output.
| Toolchain | Setting |
|---|---|
| Hardhat | solidity.settings.evmVersion: "paris" in hardhat.config.js |
| Foundry | evm_version = "paris" in foundry.toml |
| solc-js | settings.evmVersion: "paris" |
Diagnostic: if your bytecode.length grew significantly after upgrading solc, and you now see invalid code errors, PUSH0 is likely the cause. paris-compiled ERC-20 is typically ~5.8 KB; shanghai-compiled is ~6.2 KB with ~180 PUSH0 instructions.
Opcode support matrix
| EVM version | Opcodes introduced | OXN support |
|---|---|---|
| Frontier | Base instruction set | Full |
| Homestead | DELEGATECALL | Full |
| Byzantium | RETURNDATASIZE, RETURNDATACOPY, REVERT, STATICCALL | Full |
| Constantinople | SHL, SHR, SAR, CREATE2, EXTCODEHASH | Full |
| Istanbul | CHAINID, SELFBALANCE, EXTCODEHASH gas changes | Full |
| Berlin | EIP-2929 gas repricing | Full |
| London | EIP-1559 (base fee), BASEFEE opcode | Partial (BASEFEE returns 0 or fixed value; no EIP-1559 fee auction) |
| Paris | The Merge (DIFFICULTY → PREVRANDAO) | Full |
| Shanghai | PUSH0, warm COINBASE, EIP-3651 | Not supported |
| Cancun | Transient storage (TLOAD, TSTORE), MCOPY, BLOBHASH | Not supported |
Set evmVersion: "paris" to stay inside the supported set.
Standard EVM precompiles
Ethereum's standard precompiles at addresses 0x01 through 0x0a are supported:
| Address | Function | OXN support |
|---|---|---|
0x01 | ecrecover | Full |
0x02 | sha256 | Full |
0x03 | ripemd160 | Full |
0x04 | identity | Full |
0x05 | modexp | Full |
0x06 | ecAdd (bn128) | Full |
0x07 | ecMul (bn128) | Full |
0x08 | ecPairing (bn128) | Full |
0x09 | blake2f | Full |
0x0a | pointEvaluation (KZG) | Not supported |
OpenZeppelin, Solmate, and most standard libraries rely only on 0x01–0x09, which are all present. pointEvaluation (used in EIP-4844 blob validation) is not needed on OXN because OXN does not have blob transactions.
Behavior differences from Ethereum
Block environment.
| Opcode / global | Ethereum | OXN |
|---|---|---|
block.number | Ethereum block height | OXN block height |
block.timestamp | Unix timestamp of the block | Same |
blockhash(n) | Hash of block n (last 256) | Same |
block.chainid | 1 (mainnet) | 186 (OXN testnet) |
block.coinbase | Miner / proposer address | Zero address or fixed sentinel |
block.difficulty / PREVRANDAO | Randomness beacon | Fixed sentinel — do not use for randomness |
block.basefee | EIP-1559 base fee | Zero or fixed — legacy fee model in use |
Randomness. block.difficulty / PREVRANDAO, block.timestamp, and block.hash are all publicly predictable. Do not use them for randomness in confidential contracts. Use the Sapphire.randomBytes precompile — see Precompiles: Randomness.
Gas estimation. Set gasLimit explicitly rather than relying on eth_estimateGas. See Gas and Fees.
Event log visibility. Standard on Ethereum: any observer can decode all events. On OXN: encrypted, and only the caller who emitted them can decode. See Confidentiality Model.
Revert reason visibility. Standard on Ethereum: any observer can read the revert reason. On OXN: encrypted, decodable only by the caller. Debugging from outside is impossible without the caller's session key.
Testing your contract for OXN compatibility
A quick checklist before shipping:
-
evmVersion: "paris"in your compiler config - No use of
PUSH0(implicit — comes from evmVersion) - No reliance on
block.difficulty/PREVRANDAOfor randomness - No reliance on EIP-1559 fee parameters (
maxFeePerGas,maxPriorityFeePerGas) - No use of transient storage (
TLOAD,TSTORE) - Explicit
gasLimiton every transaction - Events designed with the OXN encryption model in mind (if global indexing is required, add a public event or use a relay)
Run integration tests against OXN Testnet, not just a local Hardhat network. Local emulators do not model confidentiality or the OXN-specific opcode support.
Migration effort from Ethereum
For most contracts, migration is a two-line change: evmVersion: "paris" in the compiler config, and explicit gasLimit in the deployment script.
Contracts that use encryption features (Sapphire.encrypt, signed queries, etc.) are OXN-native and won't compile against a public EVM chain — that's expected. If your migration goal is to write a contract that works on both Ethereum and OXN, keep the OXN-specific code paths behind a flag or a separate contract.
See Migrating an Ethereum dApp to OXN for a step-by-step walkthrough.
Next steps
- Precompiles Overview — OXN's additional built-in primitives
- Confidentiality Model — what changes about your contract's observable behavior
- Gas and Fees — the practical implications of the fee model