EVM Differences
LitVM is built on Arbitrum Orbit and is designed to be as compatible with Ethereum as possible. Developers experienced with Ethereum will find that little to no new knowledge is required to build on LitVM.
However, there are some important differences in how certain EVM operations behave on LitVM compared to Ethereum mainnet. This page documents these differences to help developers avoid unexpected behavior.
Quick Summary
Area
Key Difference
Block Numbers
block.number returns approximate Ethereum L1 block, not LitVM block
Block Time
Variable; blocks produced on-demand, not at fixed intervals
Gas Limit
Displayed limit appears very high, but effective cap is 32M
Randomness
blockhash() is NOT cryptographically secure
Timestamps
Set by sequencer clock, not linked to Ethereum
Solidity Differences
When deploying Solidity contracts on LitVM, most code works identically to Ethereum. However, the following operations return different results:
Operation
Behavior on LitVM
blockhash(x)
Returns a cryptographically insecure, pseudo-random hash for x within the range block.number - 256 <= x < block.number. Do not use for randomness.
block.coinbase
Returns the sequencer's designated address if the transaction was sequenced normally.
block.difficulty
Returns the constant 1.
block.prevrandao
Returns the constant 1.
block.number
Returns an approximate Ethereum L1 block number, NOT the LitVM block number. See details below.
msg.sender
Works normally for L2-to-L2 transactions. For cross-chain messages from L1, returns an aliased address.
Important: Randomness
⚠️ LitVM's block hashes should NOT be relied on as a secure source of randomness.
If your contract requires secure randomness, use a dedicated oracle service rather than blockhash().
Block Numbers
Block numbers on LitVM work differently than on Ethereum.
block.number in Solidity
block.number in SolidityWhen you access block.number in a smart contract, it returns a value close to (but not exactly) the Ethereum L1 block number at which the sequencer received the transaction.
solidity
// In a LitVM contract:
block.number // => Returns approximate Ethereum L1 block number, NOT LitVM block
This means:
The value may lag behind the actual Ethereum block by a small amount
It does NOT return the LitVM-specific block number
Multiple LitVM blocks can exist within a single Ethereum block
Getting the Actual LitVM Block Number
LitVM has its own block numbers that start at 0 at genesis and increment sequentially. To access these:
Via ArbSys Precompile (in Solidity):
solidity
interface ArbSys {
function arbBlockNumber() external view returns (uint256);
}
// Usage:
uint256 litvmBlockNumber = ArbSys(address(100)).arbBlockNumber();
Via RPC Response:
javascript
const receipt = await provider.getTransactionReceipt('0x...');
// receipt.blockNumber => LitVM block number
// receipt.l1BlockNumber => Ethereum L1 block number (approximate)
Timing Assumptions
As a general rule:
Long-term timing (hours+): Block numbers and timestamps are generally reliable
Short-term timing (minutes): Unreliable—don't depend on precise block timing
This is similar guidance to Ethereum itself, but more pronounced on L2s.
Block Timestamps
Block timestamps on LitVM are not directly linked to Ethereum block timestamps:
Set by the sequencer: Timestamps are based on the sequencer's clock
Monotonically increasing: Each block's timestamp must be ≥ the previous block
Bounded: Timestamps must be within 24 hours in the past or 1 hour in the future
For force-included transactions (bypassing the sequencer), the timestamp is the greater of:
The L1 timestamp when the transaction was submitted
The previous L2 block's timestamp
Block Time
Unlike Ethereum's ~12 second block time, LitVM block production is variable:
Blocks are produced on-demand when there are transactions to sequence
Multiple LitVM blocks can fit within a single Ethereum block
In active periods, blocks may be produced very frequently
In quiet periods, block production may be sporadic
Key implication: Don't assume fixed block intervals for time-sensitive logic.
Gas and Fees
Two-Dimensional Fee Structure
LitVM transactions pay for two components:
L2 execution cost: Gas for executing the transaction on LitVM
L1 data posting cost: Cost of posting transaction data to Ethereum
This means gas limits and costs may appear different than expected.
Block Gas Limit
When querying a block's gas limit, you may see an artificially high value (1,125,899,906,842,624). This accommodates the variable L1 posting costs.
The effective execution gas limit is 32 million per block.
To see actual gas used for execution, check the gasUsed field rather than gasLimit.
Gas Estimation
eth_estimateGasworks normally and accounts for both L2 and L1 costsEstimates may vary over time due to L1 gas price fluctuations
The gas limit shown for a transaction includes a buffer for L1 posting costs
Precompiles
LitVM supports all standard Ethereum precompiles, plus additional Arbitrum-specific precompiles:
Precompile
Address
Purpose
ArbSys
0x64 (100)
System info (block number, chain ID, etc.)
ArbInfo
0x65 (101)
Account info
ArbAddressTable
0x66 (102)
Address compression for calldata savings
ArbGasInfo
0x6c (108)
Gas pricing info
ArbRetryableTx
0x6e (110)
Retryable ticket management
ArbStatistics
0x6f (111)
Chain statistics
Using ArbSys
The ArbSys precompile is particularly useful:
solidity
interface ArbSys {
function arbBlockNumber() external view returns (uint256);
function arbBlockHash(uint256 blockNumber) external view returns (bytes32);
function arbChainID() external view returns (uint256);
}
contract MyContract {
function getLitVMBlockNumber() public view returns (uint256)
{
return ArbSys(address(100)).arbBlockNumber();
}
}
RPC Differences
Most Ethereum RPC methods work identically. Key differences:
Additional Block Fields
When calling eth_getBlockByHash or eth_getBlockByNumber:
Field
Description
l1BlockNumber
Approximate L1 block number for this L2 block
sendCount
Number of L2-to-L1 messages since genesis
sendRoot
Merkle root of the outbox tree
Additional Transaction Types
LitVM supports standard Ethereum transaction types plus Arbitrum-specific types:
Type
Code
Description
Deposit
100
ETH deposit from L1 via bridge
Unsigned
101
L1 user calling L2 contract via bridge
Contract
102
L1 contract calling L2 contract
Retry
104
Retryable ticket redemption
Submit Retryable
105
Retryable ticket submission
Internal
106
ArbOS internal transaction
Cross-Chain Messaging
When sending messages from Ethereum L1 to LitVM:
Address Aliasing
The msg.sender for L1→L2 messages is not the original L1 address. It's an "aliased" address calculated as:
L2_Alias = L1_Contract_Address + 0x1111000000000000000000000000000000001111
This prevents address collision attacks. If your contract receives cross-chain messages, account for this aliasing.
Best Practices
✅ Do
Use
ArbSys.arbBlockNumber()when you need the actual LitVM block numberUse oracles for secure randomness
Account for variable block times in time-sensitive logic
Test gas estimates under different L1 gas conditions
Use
gasUsedrather thangasLimitwhen analyzing blocks
❌ Don't
Don't use
blockhash()for randomness or securityDon't assume fixed block intervals
Don't assume
block.numberreturns the L2 block numberDon't rely on precise short-term timing (< 1 hour)
Further Reading
Have questions about EVM compatibility? Join our Telegram for developer support.
Last updated