Skip to content

@eez:on

Evaluate an expression as if the script were on another chain, and return its value: helpers, :: calls, variables and arithmetic resolve against that chain (reads only).

On-chain (@eez:on!): Reads the other chain through the proxy of its Assertions core, so the assertion runs as a transaction; one hop only, not simulable.

⚗️ Experimental — available at next.evmcrispr.com.

Returns: any

@eez:on(chain expression)
NameTypeDescription
chainchainChain to evaluate on (eezL2, a viem name or a chain id)
expressionanyExpression evaluated as if the script had switched to that chain
# From L1, read the connected account's balance on the rollup
switch eezL1
print @eez:on(eezL2 @balance(ETH @me))
  • The expression runs with the script's client switched to the target chain and the previous client restored afterwards — inside a sim:fork the fork is kept. Everything inside resolves against the target chain: @balance, @token:balance, :: reads, other modules' helpers (e.g. @eez:proxy computes the proxy on that chain), nested @eez:on.
  • Read-only by construction: helpers cannot emit actions, so exec-style writes are not possible inside the expression. Use the eez:on command to write.
  • Module config variables ($eez:registry, $std:tokenlist, …) are global, not per chain: a value set for the script's own chain is also seen while evaluating on the other one.
  • Editor completions inside the expression assume the script's current chain, not the target.
  • This is the off-chain face. The on-chain face (@eez:on!, usable inside assert for a synchronous cross-rollup read) needs the on-chain helper runtime deployed on the target rollup and is not available yet.

A synchronous cross-rollup read inside an assertion. The inner expression compiles as if the script were on the other chain (so @balance!, :: reads and every nested on-chain helper resolve there), and is evaluated at assertion time through this chain's cross-chain proxy of the Assertions core deployed over there: a static call the EEZ sequencer composes, returning the remote value inline.

switch eezL1
assert @eez:on!(eezL2 @balance!(ETH @me)) >= 1e18 "not enough on the rollup"
  • The assertion becomes a transaction. Only a transaction reaches the composer; an eth_call through a proxy always fails with a registry gate error (ExecutionNotInCurrentBlock, or ExecutionNotFound()). Inside a batch it already is one. It cannot be simulated in sim:fork, since a fork has no composer.
  • Two proxies of the Assertions core must exist first: the remote core's proxy on this chain (eez:deploy-proxy 0x67DBB438FdC614466984Dc8F68dAB812d785a2aE --chain eezL2 from L1), which the read goes out through, and this chain's core's proxy over there (the same command from L2 with --chain eezL1), which the other side sees as the reader. A static read cannot create a proxy on the way, so a missing one gets the transaction evicted rather than reverted.
  • @me compiles to the literal wallet address, so @balance!(ETH @me) on the rollup is the wallet's balance there. Anything on the far side that looks at msg.sender sees the proxy of the caller instead.
  • One hop only: the devnet composes a static read across one chain boundary, not @eez:on! inside @eez:on!.
  • A constant inner expression is folded at composition time and never crosses; @eez:on!(eezL1 …) from L1 is just the inner expression.
  • eez:on — run a block of commands on the other rollup atomically