Overview presentation: https://docs.google.com/presentation/d/1nbxJ3_xaD1QHaRBQaa4Q8BOjAn-UAV-1NN9D3Qzx28U/edit?usp=sharing

Node

Main goal for the node is to run both an L1 node and an L2 node in the same program. This allows same latency reading/writing for both the L1 state and the L2 state.

Additionally, it needs to be possible to support multiple L2s as well. This way the node can be configured to run a full node for all selected chains, which will be very helpful for builders running beefy machines that want to validate/build cross L2 transactions.

It also allows us to be flexible about which chains nodes are required to run as these support direct state access without any additional onchain data. For now, all nodes would be required to run an L1 node and only the L2 they are interested in.

Precompiles

There is (for now) a single main precompile being introduced: XCALLOPTIONS(version: uint16, chainID: uint, sandbox: bool, txOrigin: address, msgSender: address, blockHash: bytes32, proof: bytes)

This call context modifies the behavior of the existing CALL/DELEGATECALL/STATICCALL opcodes. This allows writing standard solidity code to do these special calls instead of having to manually encode the input data and decode the return data which is very inconvenient and tedious because it’s not possible to make wrappers for these in a general way that make them as easy to use as the standard solidity code

This precompile sets additional CALL parameters in the current frame of execution which stick around until the next CALL(basically extending the CALL opcode without changing the inputs). When that call has processed, the values are reset.

tx.origin and msg.sender can be specified for sandbox calls to emulate behavior depending on those values to have specific values. In sandbox mode, msg.value can be any value, the balance check is skipped.

The gas limit is respected for the call these options will be used for, because gas is still used like usual when doing these called (well, for the stateless calls in the future a zk proof may be used, but for now that’s not the case). Specifying a fixed gas limit is also a great way to make a stateless call completely deterministic (if that would be a problem for the call being done).

Events can be emitted during these calls. However, events are conceptually linked to state changes. And so events emitted in sandboxed calls should NOT be part of the L2 transaction receipt. Only in calls that can actually modify state should the event be logged.

If blockhash == 0 and chainID is in the set of supported direct state access chains, proof can be empty and the node needs to support the generation of its own Merkle proofs for all accessed state. Otherwise, proof needs to contain proofs required for all state accesses (storage, code, accounts, blockhashes etc…)