Table of Contents
Sequencing
Scroll L2 operates a centralized sequencer that accepts transactions and generates new L2 blocks. The Sequencer exposes a JSON-RPC interface for accepting L2 transactions, and is built on a fork of Geth.
Until the Euclid upgrade (April, 2025), Scroll L2 nodes maintained Clique, a Proof-of-Authority consensus with the L2 Sequencer set as authorized signer for block production. Since then, the L2 nodes read the authorized unsafe block signer from the new SystemConfig contract on L1.
The block time is set at 3 seconds and maintained on a best-effort basis, not enforced by the protocol.
Forced transactions
Messages appended to the message queue (L1MessageQueueV2
) are expected to be included into a bundle by the centralized operator. Messages in the queue cannot be skipped or dropped, but the sequencer can choose to finalize a bundle without processing any queued messaged. Should a permissioned sequencer not process any queued messages within the SystemConfig.maxDelayMessageQueue
, anyone can include queue messages as commiting and finalizing bundles becomes permissionless.
High-level flow
To force transactions on Scroll through L1, the following steps are taken:
- The EOA sends a message to the L2 through the
sendTransaction
function on theEnforcedTxGateway
contract. - The
sendTransaction
function calls theappendEnforcedTransaction
function on theL1MessageQueue
contract, which pushes the message to the queue through themessageRollingHashes
(uint256 => bytes32, messageIndex => timestamp-rollingHash) mapping. - At each finalization (
finalizeBundlePostEuclidV2
) the number of messages processed in the bundle (totalL1MessagesPoppedOverall
) is passed as input - In the internal
_finalizeBundlePostEuclidV2
function, themessageQueueHash
is computed up to thetotalL1MessagesPoppedOverall - 1
queue index - The
messageQueueHash
is passed a public input to the verifier.
Should messages not be processed by the permissioned sequencer, the EOA waits for either:
SystemConfig.maxDelayEnterEnforcedMode
to pass since the last batch finalization, orSystemConfig.maxDelayMessageQueue
to pass since the first unfinalized message enqueue time. Then the EOA can finally call can submit a batch viacommitAndFinalizeBatch
and at the same time activate the permissionless sequencing mode (UpdateEnforcedBatchMode
).
EnforcedTxGateway
: the sendTransaction
function
This function acts as the entry point to send L1 to L2 messages from an EOA. There are two variants:
function sendTransaction(
address _target,
uint256 _value,
uint256 _gasLimit,
bytes calldata _data
)
function sendTransaction(
address _sender,
address _target,
uint256 _value,
uint256 _gasLimit,
bytes calldata _data,
uint256 _deadline,
bytes memory _signature,
address _refundAddress
)
The first variant is for direct calls, while the second allows for signed messages. Both functions validate that the caller is not paused and charge a fee based on the gas limit. For contract callers, L1-to-L2 address aliasing is applied. For EOAs and EIP-7702 delegated EOAs, the original address is used. The functions ultimately call appendEnforcedTransaction
on the L1MessageQueueV2
contract.
L1MessageQueueV2
: the appendEnforcedTransaction
function
The appendEnforcedTransaction
function can only be called by the authorized EnforcedTxGateway
contract.
function appendEnforcedTransaction(
address _sender,
address _target,
uint256 _value,
uint256 _gasLimit,
bytes calldata _data
) external
The function first validates that the gas limit is within the configured bounds in SystemConfig
. It then computes a transaction hash and stores it in the messageRollingHashes
mapping along with the current timestamp. The mapping uses a special encoding where the lower 32 bits store the timestamp and the upper 224 bits store a rolling hash of all messages.
ScrollChain
: the commitAndFinalizeBatch
function
This function allows forcing inclusion of transactions when the enforced batch mode conditions are met.
function commitAndFinalizeBatch(
uint8 version,
bytes32 parentBatchHash,
FinalizeStruct calldata finalizeStruct
) external
where FinalizeStruct
is defined as:
/// @notice The struct for permissionless batch finalization.
/// @param batchHeader The header of this batch.
/// @param totalL1MessagesPoppedOverall The number of messages processed after this bundle.
/// @param postStateRoot The state root after this batch.
/// @param withdrawRoot The withdraw trie root after this batch.
/// @param zkProof The bundle proof for this batch (single-batch bundle).
/// @dev See `BatchHeaderV7Codec` for the batch header encoding.
struct FinalizeStruct {
bytes batchHeader;
uint256 totalL1MessagesPoppedOverall;
bytes32 postStateRoot;
bytes32 withdrawRoot;
bytes zkProof;
}
The function first checks if either delay condition is met:
- No batch has been finalized for
maxDelayEnterEnforcedMode
seconds - No message has been included for
maxDelayMessageQueue
seconds
If either condition is met, it enables enforced batch mode by:
- Reverting any unfinalized batches
- Setting the enforced mode flag
- Allowing the batch to be committed and finalized with a ZK proof
Once in enforced mode, only batches with proofs can be submitted until the owner (Scroll Security Council) explicitly disables enforced mode. Moreover, the designated Sequencer can't commit or finalize batches anymore due to the whenEnforcedBatchNotEnabled
check.