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 messages. Should a permissioned sequencer not process any queued messages within the SystemConfig.maxDelayMessageQueue, anyone can include queue messages as committing 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
sendTransactionfunction on theEnforcedTxGatewaycontract. - The
sendTransactionfunction calls theappendEnforcedTransactionfunction on theL1MessageQueuecontract, 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
_finalizeBundlePostEuclidV2function, themessageQueueHashis computed up to thetotalL1MessagesPoppedOverall - 1queue index - The
messageQueueHashis passed a public input to the verifier.
Should messages not be processed by the permissioned sequencer, the EOA waits for either:
SystemConfig.maxDelayEnterEnforcedModeto pass since the last batch finalization, orSystemConfig.maxDelayMessageQueueto pass since the first unfinalized message enqueue time. Then the EOA can finally submit a batch viacommitAndFinalizeBatchand 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
maxDelayEnterEnforcedModeseconds - No message has been included for
maxDelayMessageQueueseconds
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.