From aa56f6b5caf55ccaaf53891d3e4e8beed79d3c93 Mon Sep 17 00:00:00 2001 From: Pete Date: Tue, 21 Jul 2026 11:45:57 -0500 Subject: [PATCH 1/4] docs: explain maxTimeVariation config and reorg-resistance-margin relationship Expand the Sequencer timing adjustments page with concept + reference coverage of the SequencerInbox maxTimeVariation setting: what each of the four fields (delayBlocks, futureBlocks, delaySeconds, futureSeconds) guards, how the window is recorded on the parent chain but enforced by clamping in the STF, parent-chain dependent defaults, and the impact of changing them. Add a dedicated section on the batch poster --node.batch-poster.reorg-resistance-margin flag, the =0 bypass, and its child-chain reorg risk profile. Correct the stale defaults in the parameter table. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../sequencer-timing-adjustments.mdx | 117 +++++++++++++++++- 1 file changed, 112 insertions(+), 5 deletions(-) diff --git a/docs/launch-arbitrum-chain/chain-config/sequencer/sequencer-timing-adjustments.mdx b/docs/launch-arbitrum-chain/chain-config/sequencer/sequencer-timing-adjustments.mdx index f9f5fcaaf9..3f2c8fc140 100644 --- a/docs/launch-arbitrum-chain/chain-config/sequencer/sequencer-timing-adjustments.mdx +++ b/docs/launch-arbitrum-chain/chain-config/sequencer/sequencer-timing-adjustments.mdx @@ -18,11 +18,11 @@ Most timing tweaks happen at the node level for the sequencer. Use the official ## Key Sequencer Timing parameters to adjust -| Parameter | Location | Default | How to configure | Why adjust | -| ------------------------------------------------------------------------------------ | --------------------------------------------------- | --------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ | -| Delayed sequencer finalize distance | Node (`node.delayed-sequencer`) | Higher (e.g., waits for full Parent chain finality) | CLI: `--node.delayed-sequencer.finalize-distance=1` and enable: `--node.delayed-sequencer.enable=true` and disable: `node.delayed-sequencer.use-merge-finality=false` | Lower value for near instance deposits (~seconds instead of minutes). Trade-off: ⚠️ **DANGER** ⚠️ Risk of re-org on parent chain. | -| Sequencer Inbox max time variation | Chain deployment (`sequencerInboxMaxTimeVariation`) | `delayBlocks`: 5760, `futureBlocks`: 12, `delaySeconds`: 86400, `futureSeconds`:3600 | In Chain SDK config struct or `chainConfig` JSON during deployment. | Allows sequencer minor timestamp adjustments to avoid re-orgs if batch posting lags. Rarely needs change from defaults. | -| [Timeboost](/how-arbitrum-works/timeboost/gentle-introduction.mdx) non-express delay | Node (`execution.sequencer.timeboost`) | 200ms | In node config: `"execution": { "sequencer": { "timeboost": { "non-express-delay-msec": 300 } } }` | Larger delay for more advantage/revenue for Express Lane winner. Increases latency for regular transactions. | +| Parameter | Location | Default | How to configure | Why adjust | +| ------------------------------------------------------------------------------------ | --------------------------------------------------- | --------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Delayed sequencer finalize distance | Node (`node.delayed-sequencer`) | Higher (e.g., waits for full Parent chain finality) | CLI: `--node.delayed-sequencer.finalize-distance=1` and enable: `--node.delayed-sequencer.enable=true` and disable: `node.delayed-sequencer.use-merge-finality=false` | Lower value for near instance deposits (~seconds instead of minutes). Trade-off: ⚠️ **DANGER** ⚠️ Risk of re-org on parent chain. | +| Sequencer Inbox max time variation | Chain deployment (`sequencerInboxMaxTimeVariation`) | Parent-chain dependent — see [Default values](#default-values) | In Chain SDK config struct or `chainConfig` JSON during deployment, or later via `SequencerInbox.setMaxTimeVariation`. See [Understanding `maxTimeVariation`](#understanding-maxtimevariation). | Bounds how far a message's claimed parent-chain block/time may sit from the block the batch lands in, and sets the force-inclusion wait. See the sections below before changing. | +| [Timeboost](/how-arbitrum-works/timeboost/gentle-introduction.mdx) non-express delay | Node (`execution.sequencer.timeboost`) | 200ms | In node config: `"execution": { "sequencer": { "timeboost": { "non-express-delay-msec": 300 } } }` | Larger delay for more advantage/revenue for Express Lane winner. Increases latency for regular transactions. | ## Step-by-step configuration process: @@ -63,3 +63,110 @@ You can find the latest recommended version of Nitro on the [Run a Node](/run-ar 3. **Test changes** on a devnet or testnet first—monitor TPS, state growth, posting costs, and deposit latency. For a full list of flags, run `nitro-node --help`. Refer to Arbitrum Docs sections on [Running a Sequencer Node](/run-arbitrum-node/sequencer/04-run-sequencer-node.mdx) and [How to configure your Arbitrum chain's node using the Chain SDK](/launch-arbitrum-chain/deploy/configure-node.mdx) for your exact version. If using Timeboost or advanced features, additional setup (e.g., Auction Contract) is needed. + +## Understanding `maxTimeVariation` + +`maxTimeVariation` is a four-field setting on the Sequencer Inbox contract on the parent chain. It bounds how far a sequenced message's claimed parent-chain block number and timestamp may differ from the parent-chain block in which the batch carrying that message is actually posted. + +```solidity +struct MaxTimeVariation { + uint256 delayBlocks; // max parent-chain blocks in the past a message may be received + uint256 futureBlocks; // max parent-chain blocks in the future a message may be received + uint256 delaySeconds; // max parent-chain seconds in the past a message may be received + uint256 futureSeconds; // max parent-chain seconds in the future a message may be received +} +``` + +The four fields define a **two-sided window** around the current parent-chain block and time: + +- `delayBlocks` and `delaySeconds` bound the **past** edge — how old a message's claimed block or timestamp may be. +- `futureBlocks` and `futureSeconds` bound the **future** edge — how far ahead a message's claimed block or timestamp may be. + +Blocks and seconds are tracked independently: `delayBlocks` and `futureBlocks` are measured in parent-chain block numbers (`block.number`), while `delaySeconds` and `futureSeconds` are measured in parent-chain seconds (`block.timestamp`). + +## How the time window is enforced + +Understanding what this setting guards requires knowing _where_ it is enforced, which is not where most people expect. + +When a batch is posted, the Sequencer Inbox computes the window from the **current** parent-chain block and time at the moment the batch lands: + +- Timestamp window: `[block.timestamp - delaySeconds, block.timestamp + futureSeconds]` +- Block window: `[block.number - delayBlocks, block.number + futureBlocks]` + +The contract records these bounds (they are emitted in the `SequencerBatchDelivered` event and folded into the batch's data hash), but it **does not reject** a batch whose messages fall outside them. Enforcement happens later, off the parent chain, when the batch is replayed by the node's state-transition function: each message whose claimed block or timestamp is outside the recorded window is **clamped** to the nearest bound — pulled up to the minimum if it is too far in the past, or pulled down to the maximum if it is too far in the future. + + + +Because out-of-window messages are silently clamped rather than rejected, a message can be assigned a different parent-chain block or timestamp than the Sequencer originally computed locally. When the locally executed chain and the chain derived from onchain data disagree, the result is a child-chain reorg. `maxTimeVariation` therefore does not guard by blocking bad batches; it defines the window inside which the Sequencer and batch poster must keep every message to avoid such reorgs. + + + +## What each field guards, with examples + +**Future edge (`futureBlocks` / `futureSeconds`).** These cap how far ahead of the parent chain a message may claim to be. + +For example, suppose `futureSeconds` is `3600` (one hour) and the batch lands in a parent-chain block whose timestamp is `T`. Any message in that batch claiming a timestamp later than `T + 3600` is clamped down to `T + 3600`. If `futureBlocks` is small — say `48` on a parent chain with two-second blocks, roughly 96 seconds of headroom — then a batch that is delayed, or that contains messages sequenced slightly ahead of the parent chain, can easily reach the future edge and have its messages clamped down. Raising `futureBlocks` widens this headroom. + +**Past edge (`delayBlocks` / `delaySeconds`).** These cap how old a message may be. A message claiming a timestamp earlier than `block.timestamp - delaySeconds` is clamped up to that minimum. + +For example, with `delaySeconds` set to `345600` (four days), a message may be up to four days older than the parent-chain block that carries it before it is clamped forward. + +**`delayBlocks` also sets the force-inclusion wait.** The same `delayBlocks` value determines how long a message submitted through the Delayed Inbox must wait before it can be force-included, bypassing the Sequencer. `forceInclusion` reverts with `ForceIncludeBlockTooSoon` until `delayBlocks` parent-chain blocks have elapsed since the message was submitted. Raising `delayBlocks` therefore lengthens the Sequencer's exclusive window and delays when users can force their transactions in. The optional delay-buffer feature can shorten this window under sustained delay, but never lengthen it. + +For more on force inclusion and the Delayed Inbox, see the [Sequencer deep dive](/how-arbitrum-works/deep-dives/sequencer.mdx). + +## Default values + +The Arbitrum Chain SDK does not use fixed numbers for these fields. It derives them from the parent chain's block time, holding the _time_ windows constant and converting them to block counts: + +- `delaySeconds` is `345600` (four days) and `futureSeconds` is `3600` (one hour), constant for all parent chains. +- `delayBlocks` is `delaySeconds / parentBlockTime` and `futureBlocks` is `futureSeconds / parentBlockTime`. + +`parentBlockTime` is 12 seconds for Ethereum and Arbitrum parent chains, and two seconds for OP-stack parent chains (whose `block.number` advances every two seconds). This yields different block defaults depending on where your chain settles: + +| Parent chain type | Parent block time | `delayBlocks` | `futureBlocks` | `delaySeconds` | `futureSeconds` | +| ------------------- | ----------------- | ------------- | -------------- | --------------- | --------------- | +| Ethereum / Arbitrum | 12s | 28800 | 300 | 345600 (4 days) | 3600 (1 hour) | +| OP-stack (2s block) | 2s | 172800 | 1800 | 345600 (4 days) | 3600 (1 hour) | + + + +A default `futureBlocks` can appear to be either 300 or 1800: 1800 is the default for a two-second parent chain (`3600 / 2`), while 300 is the default for a 12-second parent chain (`3600 / 12`). A much smaller value such as 48 is not a current SDK default — it is a manual override or a value from an older deployment. Restoring `futureBlocks` to the parent-appropriate default widens the future headroom (in the two-second example, from roughly 96 seconds to one hour), reducing how often messages near the future edge are clamped. + + + +## Relationship with the batch poster: `reorg-resistance-margin` + +`maxTimeVariation` defines the window; the batch poster is responsible for keeping every message it posts safely inside it. Two node-level settings do this self-policing, one per edge: + +- **Future (max) edge** — the batch poster stops adding messages that would exceed `block.number + futureBlocks` or `block.timestamp + futureSeconds`. Which parent-chain header it measures against is selected by `--node.batch-poster.l1-block-bound`, which must not be set to `ignore` for the past-edge check below to run. +- **Past (min) edge** — governed by `--node.batch-poster.reorg-resistance-margin`. + +`--node.batch-poster.reorg-resistance-margin` (a duration, default `10m0s`) tells the batch poster: do not post a batch if its oldest message is within this margin of the window's past edge (`block.timestamp - delaySeconds` or `block.number - delayBlocks`). If the oldest non-delayed message is closer to the minimum bound than the margin allows, the poster halts rather than posting. + +### Why the margin exists + +Message timestamps and block numbers are assigned by the Sequencer at sequencing time, but the onchain minimum bounds are computed from the parent-chain block and time at the **later** moment the batch actually lands. Between those two moments the parent chain advances, and it can also reorg. If a batch's oldest message is sitting right at the past edge, either a delayed landing or a parent-chain reorg can move the minimum bound _past_ that message. The state-transition function then clamps the message up to the new minimum, silently changing the child-chain block or timestamp the Sequencer's own node had already produced, which is a child-chain reorg. + +The default 10-minute margin keeps messages far enough from the past edge that ordinary parent-chain reorgs cannot push them out of the window. + +### The `reorg-resistance-margin=0` bypass and its risk profile + +Setting `--node.batch-poster.reorg-resistance-margin=0` disables the past-edge check entirely. The batch poster will then post batches whose oldest messages sit arbitrarily close to the window's past edge. + + + +With `reorg-resistance-margin=0`, a parent-chain reorg — or simply a batch landing later than expected — can move the minimum bound past messages that were near the edge. Those messages are then clamped forward by the state-transition function, diverging the onchain-derived chain from the chain the Sequencer executed locally: a child-chain reorg. Use `0` only in controlled situations, such as draining a backlog where you accept the reorg risk; it is not a safe steady-state setting. + + + +Note the interaction with `maxTimeVariation`: a wider past window (`delayBlocks` and `delaySeconds`) gives the batch poster more room before the margin is threatened, while a narrower window makes the margin bite sooner. See the [CLI flags reference](/run-arbitrum-node/nitro/cli-flags-reference.mdx) for the full batch-poster flag set. + +## Changing `maxTimeVariation` + +`maxTimeVariation` is set at deployment through the Chain SDK, and can be changed afterward by the chain owner by calling `setMaxTimeVariation` on the Sequencer Inbox contract on the parent chain. Keep two effects in mind when changing it on a live chain: + +- Raising `delayBlocks` lengthens the Sequencer's exclusive window and delays force inclusion. Very large values weaken the chain's censorship-resistance guarantee, because users must wait longer to force transactions in. +- Raising `futureBlocks` and `futureSeconds` widens the future headroom, reducing clamping of messages sequenced ahead of the parent chain; it does not affect force inclusion. + +Test any change on a testnet first, and confirm the batch poster's `l1-block-bound` and `reorg-resistance-margin` settings remain consistent with the new window. From 12f119b13392f847db1853533dff298f587b1c77 Mon Sep 17 00:00:00 2001 From: Pete Date: Tue, 21 Jul 2026 11:51:27 -0500 Subject: [PATCH 2/4] docs: tighten maxTimeVariation parent-chain default wording The SDK selects the 2s parentBlockTime only for Base and Base Sepolia by chain ID, not for OP-stack parents generically. Update the Default values table and callout to say Base / Base Sepolia -> 2s and all other parents -> 12s, matching the shipped getParentChainBlockTime logic. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../sequencer/sequencer-timing-adjustments.mdx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/launch-arbitrum-chain/chain-config/sequencer/sequencer-timing-adjustments.mdx b/docs/launch-arbitrum-chain/chain-config/sequencer/sequencer-timing-adjustments.mdx index 3f2c8fc140..879aaf1880 100644 --- a/docs/launch-arbitrum-chain/chain-config/sequencer/sequencer-timing-adjustments.mdx +++ b/docs/launch-arbitrum-chain/chain-config/sequencer/sequencer-timing-adjustments.mdx @@ -122,16 +122,16 @@ The Arbitrum Chain SDK does not use fixed numbers for these fields. It derives t - `delaySeconds` is `345600` (four days) and `futureSeconds` is `3600` (one hour), constant for all parent chains. - `delayBlocks` is `delaySeconds / parentBlockTime` and `futureBlocks` is `futureSeconds / parentBlockTime`. -`parentBlockTime` is 12 seconds for Ethereum and Arbitrum parent chains, and two seconds for OP-stack parent chains (whose `block.number` advances every two seconds). This yields different block defaults depending on where your chain settles: +`parentBlockTime` is two seconds when the parent chain is Base or Base Sepolia (whose `block.number` advances every two seconds), and 12 seconds for every other parent chain, including Ethereum and Arbitrum. The SDK selects the two-second value only for those two chain IDs, so a different OP-stack parent that the SDK does not recognize falls back to the 12-second default. This yields different block defaults depending on where your chain settles: -| Parent chain type | Parent block time | `delayBlocks` | `futureBlocks` | `delaySeconds` | `futureSeconds` | +| Parent chain | Parent block time | `delayBlocks` | `futureBlocks` | `delaySeconds` | `futureSeconds` | | ------------------- | ----------------- | ------------- | -------------- | --------------- | --------------- | -| Ethereum / Arbitrum | 12s | 28800 | 300 | 345600 (4 days) | 3600 (1 hour) | -| OP-stack (2s block) | 2s | 172800 | 1800 | 345600 (4 days) | 3600 (1 hour) | +| Base / Base Sepolia | 2s | 172800 | 1800 | 345600 (4 days) | 3600 (1 hour) | +| All other parents | 12s | 28800 | 300 | 345600 (4 days) | 3600 (1 hour) | -A default `futureBlocks` can appear to be either 300 or 1800: 1800 is the default for a two-second parent chain (`3600 / 2`), while 300 is the default for a 12-second parent chain (`3600 / 12`). A much smaller value such as 48 is not a current SDK default — it is a manual override or a value from an older deployment. Restoring `futureBlocks` to the parent-appropriate default widens the future headroom (in the two-second example, from roughly 96 seconds to one hour), reducing how often messages near the future edge are clamped. +A default `futureBlocks` can appear to be either 300 or 1800: 1800 is the default for a Base or Base Sepolia parent (`3600 / 2`), while 300 is the default for every other parent, including Ethereum and Arbitrum (`3600 / 12`). A much smaller value such as 48 is not a current SDK default — it is a manual override or a value from an older deployment. Restoring `futureBlocks` to the parent-appropriate default widens the future headroom (on a two-second parent, from roughly 96 seconds to one hour), reducing how often messages near the future edge are clamped. From 04ac86507be182a545aeefb90b93fc7a9b23c8c7 Mon Sep 17 00:00:00 2001 From: Pete Date: Tue, 21 Jul 2026 13:55:59 -0500 Subject: [PATCH 3/4] Adding maxTimeVariation settings --- .../sequencer-timing-adjustments.mdx | 37 ++++++++++--------- 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/docs/launch-arbitrum-chain/chain-config/sequencer/sequencer-timing-adjustments.mdx b/docs/launch-arbitrum-chain/chain-config/sequencer/sequencer-timing-adjustments.mdx index 879aaf1880..adea588367 100644 --- a/docs/launch-arbitrum-chain/chain-config/sequencer/sequencer-timing-adjustments.mdx +++ b/docs/launch-arbitrum-chain/chain-config/sequencer/sequencer-timing-adjustments.mdx @@ -1,30 +1,31 @@ --- title: 'Configure Sequencer timing adjustments' +sidebar_label: 'Sequencer timing adjustments' description: 'Learn how to configure timing adjustments to the Sequencer for your Arbitrum chain' +user_story: 'As a developer, I want to configure the Sequencer timing on my Arbitrum chain' content_type: how-to author: pete-vielhaber sme: jason-w123 -target_audience: 'Developers who want to configure the Sequencer timing on their Arbitrum chain' --- When launching an Arbitrum chain, the Sequencer plays a central role in ordering transactions and producing blocks. Several timing-related parameters can be adjusted in the node configuration (via the Nitro node's JSON config or command-line flags) to optimize performance, user experience, security, and cost for your specific use case. -To configure [sequencer](/how-arbitrum-works/deep-dives/sequencer.mdx) timing parameters when launching an Arbitrum chain, you'll primarily adjust settings in two places: +To configure [Sequencer](/how-arbitrum-works/deep-dives/sequencer.mdx) timing parameters when launching an Arbitrum chain, you'll primarily adjust settings in two places: -1. **Chain-level parameters**: Set during deployment via the Chain SDK, affect sequencer behavior boundaries. -2. **Node-level parameters**: Set when running your Nitro sequencer node—these control runtime behavior, such as block production speed and batch posting. +1. **Chain-level parameters**: Set during deployment via the Chain SDK; these affect Sequencer behavior boundaries. +2. **Node-level parameters**: Set when running your Nitro Sequencer node—these control runtime behavior, such as block production speed and batch posting. -Most timing tweaks happen at the node level for the sequencer. Use the official Arbitrum Chain SDK to generate a base node config JSON, then override specific fields, or pass flags directly when running the `nitro-node` Docker image. +Most timing tweaks happen at the node level for the Sequencer. Use the official Arbitrum Chain SDK to generate a base node config JSON, then override specific fields, or pass flags directly when running the `nitro-node` Docker image. -## Key Sequencer Timing parameters to adjust +## Key Sequencer timing parameters to adjust | Parameter | Location | Default | How to configure | Why adjust | | ------------------------------------------------------------------------------------ | --------------------------------------------------- | --------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| Delayed sequencer finalize distance | Node (`node.delayed-sequencer`) | Higher (e.g., waits for full Parent chain finality) | CLI: `--node.delayed-sequencer.finalize-distance=1` and enable: `--node.delayed-sequencer.enable=true` and disable: `node.delayed-sequencer.use-merge-finality=false` | Lower value for near instance deposits (~seconds instead of minutes). Trade-off: ⚠️ **DANGER** ⚠️ Risk of re-org on parent chain. | +| Delayed sequencer finalize distance | Node (`node.delayed-sequencer`) | Higher (e.g., waits for full parent chain finality) | CLI: `--node.delayed-sequencer.finalize-distance=1` and enable: `--node.delayed-sequencer.enable=true` and disable: `node.delayed-sequencer.use-merge-finality=false` | Lower value for near instant deposits (~seconds instead of minutes). Trade-off: ⚠️ **DANGER** ⚠️ Risk of reorg on parent chain. | | Sequencer Inbox max time variation | Chain deployment (`sequencerInboxMaxTimeVariation`) | Parent-chain dependent — see [Default values](#default-values) | In Chain SDK config struct or `chainConfig` JSON during deployment, or later via `SequencerInbox.setMaxTimeVariation`. See [Understanding `maxTimeVariation`](#understanding-maxtimevariation). | Bounds how far a message's claimed parent-chain block/time may sit from the block the batch lands in, and sets the force-inclusion wait. See the sections below before changing. | | [Timeboost](/how-arbitrum-works/timeboost/gentle-introduction.mdx) non-express delay | Node (`execution.sequencer.timeboost`) | 200ms | In node config: `"execution": { "sequencer": { "timeboost": { "non-express-delay-msec": 300 } } }` | Larger delay for more advantage/revenue for Express Lane winner. Increases latency for regular transactions. | -## Step-by-step configuration process: +## Step-by-step configuration process 1. **Deploy your chain first**: using the Chain SDK, this sets immutable params like `sequencerInboxMaxTimeVariation`. 2. **Generate base node config**: Use the Chain SDK: @@ -39,11 +40,11 @@ const nodeConfig = await prepareNodeConfig({ // Write nodeConfig to file: nodeConfig.json ``` -2. How to run the sequencer node: +3. **Run the Sequencer node**: - Use Docker (recommended): -```typescript +```shell docker run -v /path/to/nodeConfig.json:/config/nodeConfig.json \ offchainlabs/nitro-node: \ --conf.file=/config/nodeConfig.json \ @@ -52,15 +53,15 @@ docker run -v /path/to/nodeConfig.json:/config/nodeConfig.json \ # Add other required flags (parent-chain URL, chain info-json, keys, etc.) ``` -:::info Latest version of Nitro + You can find the latest recommended version of Nitro on the [Run a Node](/run-arbitrum-node/start-here.mdx#recommended-nitro-version) page. -::: + - or override via CLI flags for testing -3. **Test changes** on a devnet or testnet first—monitor TPS, state growth, posting costs, and deposit latency. +4. **Test changes** on a devnet or testnet first—monitor TPS, state growth, posting costs, and deposit latency. For a full list of flags, run `nitro-node --help`. Refer to Arbitrum Docs sections on [Running a Sequencer Node](/run-arbitrum-node/sequencer/04-run-sequencer-node.mdx) and [How to configure your Arbitrum chain's node using the Chain SDK](/launch-arbitrum-chain/deploy/configure-node.mdx) for your exact version. If using Timeboost or advanced features, additional setup (e.g., Auction Contract) is needed. @@ -79,8 +80,8 @@ struct MaxTimeVariation { The four fields define a **two-sided window** around the current parent-chain block and time: -- `delayBlocks` and `delaySeconds` bound the **past** edge — how old a message's claimed block or timestamp may be. -- `futureBlocks` and `futureSeconds` bound the **future** edge — how far ahead a message's claimed block or timestamp may be. +- `delayBlocks` and `delaySeconds` bound the **past** edge—how old a message's claimed block or timestamp may be. +- `futureBlocks` and `futureSeconds` bound the **future** edge—how far ahead a message's claimed block or timestamp may be. Blocks and seconds are tracked independently: `delayBlocks` and `futureBlocks` are measured in parent-chain block numbers (`block.number`), while `delaySeconds` and `futureSeconds` are measured in parent-chain seconds (`block.timestamp`). @@ -93,7 +94,7 @@ When a batch is posted, the Sequencer Inbox computes the window from the **curre - Timestamp window: `[block.timestamp - delaySeconds, block.timestamp + futureSeconds]` - Block window: `[block.number - delayBlocks, block.number + futureBlocks]` -The contract records these bounds (they are emitted in the `SequencerBatchDelivered` event and folded into the batch's data hash), but it **does not reject** a batch whose messages fall outside them. Enforcement happens later, off the parent chain, when the batch is replayed by the node's state-transition function: each message whose claimed block or timestamp is outside the recorded window is **clamped** to the nearest bound — pulled up to the minimum if it is too far in the past, or pulled down to the maximum if it is too far in the future. +The contract records these bounds (they are emitted in the `SequencerBatchDelivered` event and folded into the batch's data hash), but it **does not reject** a batch whose messages fall outside them. Enforcement happens later, off the parent chain, when the batch is replayed by the node's state-transition function: each message whose claimed block or timestamp is outside the recorded window is **clamped** to the nearest bound—pulled up to the minimum if it is too far in the past, or pulled down to the maximum if it is too far in the future. @@ -105,7 +106,7 @@ Because out-of-window messages are silently clamped rather than rejected, a mess **Future edge (`futureBlocks` / `futureSeconds`).** These cap how far ahead of the parent chain a message may claim to be. -For example, suppose `futureSeconds` is `3600` (one hour) and the batch lands in a parent-chain block whose timestamp is `T`. Any message in that batch claiming a timestamp later than `T + 3600` is clamped down to `T + 3600`. If `futureBlocks` is small — say `48` on a parent chain with two-second blocks, roughly 96 seconds of headroom — then a batch that is delayed, or that contains messages sequenced slightly ahead of the parent chain, can easily reach the future edge and have its messages clamped down. Raising `futureBlocks` widens this headroom. +For example, suppose `futureSeconds` is `3600` (one hour) and the batch lands in a parent-chain block whose timestamp is `T`. Any message in that batch claiming a timestamp later than `T + 3600` is clamped down to `T + 3600`. If `futureBlocks` is small—say `48` on a parent chain with two-second blocks, roughly 96 seconds of headroom—then a batch that is delayed, or that contains messages sequenced slightly ahead of the parent chain, can easily reach the future edge and have its messages clamped down. Raising `futureBlocks` widens this headroom. **Past edge (`delayBlocks` / `delaySeconds`).** These cap how old a message may be. A message claiming a timestamp earlier than `block.timestamp - delaySeconds` is clamped up to that minimum. @@ -131,7 +132,7 @@ The Arbitrum Chain SDK does not use fixed numbers for these fields. It derives t -A default `futureBlocks` can appear to be either 300 or 1800: 1800 is the default for a Base or Base Sepolia parent (`3600 / 2`), while 300 is the default for every other parent, including Ethereum and Arbitrum (`3600 / 12`). A much smaller value such as 48 is not a current SDK default — it is a manual override or a value from an older deployment. Restoring `futureBlocks` to the parent-appropriate default widens the future headroom (on a two-second parent, from roughly 96 seconds to one hour), reducing how often messages near the future edge are clamped. +A default `futureBlocks` can appear to be either 300 or 1800: 1800 is the default for a Base or Base Sepolia parent (`3600 / 2`), while 300 is the default for every other parent, including Ethereum and Arbitrum (`3600 / 12`). A much smaller value such as 48 is not a current SDK default—it is a manual override or a value from an older deployment. Restoring `futureBlocks` to the parent-appropriate default widens the future headroom (on a two-second parent, from roughly 96 seconds to one hour), reducing how often messages near the future edge are clamped. From 7b66c40831e5313473e7b7aa8cc40f7ca672e84b Mon Sep 17 00:00:00 2001 From: Pete Date: Wed, 22 Jul 2026 13:03:29 -0500 Subject: [PATCH 4/4] Adding suggestion from comments --- .../chain-config/validation/assertion-control.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/launch-arbitrum-chain/chain-config/validation/assertion-control.mdx b/docs/launch-arbitrum-chain/chain-config/validation/assertion-control.mdx index aa55af0cfb..1b8e8692f0 100644 --- a/docs/launch-arbitrum-chain/chain-config/validation/assertion-control.mdx +++ b/docs/launch-arbitrum-chain/chain-config/validation/assertion-control.mdx @@ -33,7 +33,7 @@ The validator assertion confirming frequency is controlled by the --node.staker. ### Reorg resistance margin -The reorganization resistance margin is configurable via the `--node.batch-poster.reorg-resistance-margin` parameter, which specifies a time duration (e.g., in minutes) during which the batch poster will avoid posting batches if their minimum L1 block number or timestamp is too close to the current L1 head. This margin serves as a safety buffer to prevent posting batches that reference recent L1 states, which are prone to reorganization. The default value is ten minutes (600 seconds), based on observed usage in deployments and error logs. +Reorg prevention interacts with batch posting through `maxTimeVariation` and reorg-resistance-margin. See the [Configure Sequencer timing adjustments](/launch-arbitrum-chain/chain-config/sequencer/sequencer-timing-adjustments.mdx) page for the full mechanism. - **Detailed Explanation of the Message**: The message "Disabling batch posting due to batch being within reorg resistance margin from Layer 1 minimum block or timestamp bounds" signifies that the batch poster's logic has detected the proposed batch's minimum required L1 block or timestamp falls within the configured margin of the current L1 chain head. For example, if the margin is ten minutes and the L1 head timestamp is `T`, the poster disables itself if the batch requires a minimum timestamp greater than (`T - 10m`) or a minimum block that is too close to the head. This message serves as a protective mechanism: L1 chains, such as Ethereum, can undergo short reorganizations (e.g., uncle blocks or brief forks), which could invalidate a posted batch if it references a block/timestamp that gets reorganized. By temporarily disabling posting, the system waits for L1 to stabilize, ensuring the batch remains valid once posted. The disablement is transient; posting resumes once the margin clears (e.g., as L1 advances). This message often appears in logs during periods of high L1 volatility or when the sequencer's clock/L1 sync is slightly off. It's normal in such cases, but it may indicate setup issues if it persists. - **Guidance on Configuration**: Set this in the `node.batch-poster` config section, e.g., `"reorg-resistance-margin": "5m"`. Lower values allow for faster posting but increase the risk; higher values enhance safety. Monitor logs for frequent disablements and adjust accordingly based on the parent chain stability (e.g., Ethereum mainnet vs. testnets).