kadenzipfel / smart-contract-vulnerabilities

A collection of smart contract vulnerabilities along with prevention methods
https://kadenzipfel.github.io/smart-contract-vulnerabilities/
1.97k stars 265 forks source link

Different Opcode Support Across Different EVM-Compatible Chains #39

Closed indeqs closed 6 months ago

indeqs commented 6 months ago

The below follows @kadenzipfel improvement proposal as outlined in issue #37

Different Opcode Support Across Different EVM-Compatible Chains

EVM-compatible chains, such as zkSync Era, BNB Chain, Polygon, Optimism, and Arbitrum, implement the Ethereum Virtual Machine (EVM) and its opcodes. However, opcode support can vary across these chains, which can lead to bugs and issues if not considered during smart contract development and deployment.

PUSH0 Opcode Compatibility

The PUSH0 opcode was introduced during the Shanghai hard fork of the Shapella upgrade (Solidity v0.8.20) and is available in certain EVM-compatible chains. However, not all chains have implemented support for this opcode yet.

Is PUSH0 supported:

  1. Ethereum: YES
  2. Arbitrum One: YES
  3. Optimism: YES
  4. ...

More chain differences and opcode supported can be found on: evmdiff.com

You can also check compatibility by running the following command assuming you have Foundry set up:

cast call --block 176 --rpc-url $ARBITRUM_RPC_URL --create 0x5f

Getting a 0x response from running the above command means the opcode is supported; an error indicates the opcode isn't supported on that chain.

Picking an older block number and getting an error means that the opcode wasn't supported at that time. Pick a recent block number to avoid confusion

CREATE and CREATE2 on zkSync Era

On zkSync Era, contract deployment uses the hash of the bytecode, and the factoryDeps field of EIP712 transactions contains the bytecode. The actual deployment occurs by providing the contract's hash to the ContractDeployer system contract.

To ensure that create and create2 functions operate correctly, the compiler must be aware of the bytecode of the deployed contract in advance. The compiler interprets the calldata arguments as incomplete input for ContractDeployer, with the remaining part filled in by the compiler internally. The Yul datasize and dataoffset instructions have been adjusted to return the constant size and bytecode hash rather than the bytecode itself.

The following code will not function correctly because the compiler is not aware of the bytecode beforehand but will work fine on Ethereum Mainnet:

function myFactory(bytes memory bytecode) public {
   assembly {
      addr := create(0, add(bytecode, 0x20), mload(bytecode))
   }
}

References

kadenzipfel commented 6 months ago

Nice! Only critique is maybe a different title would be better. I'm thinking maybe "Unsupported Opcodes", what do you think? By the way, feel free to make a PR for this.

indeqs commented 6 months ago

The suggested title is succinct. Let me make the change and make the relevant PR