Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ npx skills add base/base-skills
|base-chain/specs/upgrades/canyon:overview
|base-chain/specs/upgrades/cobalt:eip-8130
|base-chain/specs/upgrades/delta:overview,span-batches
|base-chain/specs/upgrades/denim:200ms-native-blocks
|base-chain/specs/upgrades/ecotone:derivation,l1-attributes,overview
|base-chain/specs/upgrades/fjord:derivation,exec-engine,overview,predeploys
|base-chain/specs/upgrades/granite:derivation,exec-engine,overview
Expand Down
177 changes: 177 additions & 0 deletions docs/base-chain/specs/upgrades/denim/200ms-native-blocks.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
---
title: "200ms native blocks"
description: "Specification for Denim's canonical 200ms blocks, including BaseTime, derivation, validation, and RPC behavior."
---

<Warning>
Denim is a tentative draft published to elicit feedback. The design is mostly finalized, but this is not a production specification and may change. Denim is not active on Base Sepolia or Base Mainnet; activation times and required client versions remain undecided.
</Warning>

## Summary

Denim changes Base block production from one canonical block every two seconds to five complete canonical blocks per second. Each 200ms block has its own block number, hash, state root, receipts, forkchoice updates, and unsafe, safe, and finalized lifecycle.

Denim replaces [Flashblocks](/base-chain/flashblocks/faq), which publish incremental pending-state updates for a single block. As part of the Denim rollout, Base will stop producing Flashblocks and instead produce a canonical block every 200ms. Applications using Flashblocks must migrate to canonical block and RPC streams.

Denim keeps the Ethereum block header and its seconds-based `timestamp` unchanged. A BaseTime metadata deposit supplies the sub-second component. Together, these values identify a canonical block's full millisecond timestamp.

## Tentative activation

| Network | Activation timestamp | Required client versions |
| --- | --- | --- |
| `sepolia` | `TBD` | `TBD` |
| `mainnet` | `TBD` | `TBD` |

No Denim activation has been scheduled. A later node upgrade guide will provide release and rollout instructions.

## Execution

### Full block timestamp

Denim leaves `block.header.timestamp` as Unix time in whole seconds. For an activated block `b`, its full timestamp is:

$$
T_{ms}(b) = 1000 \times b.header.timestamp + b.tx[1].timestamp\_millis\_part
Comment thread
0x00101010 marked this conversation as resolved.
$$

The BaseTime deposit at `tx[1]` carries `timestamp_millis_part`. The only valid values are `0`, `200`, `400`, `600`, and `800`. Consecutive activated blocks satisfy:

$$
T_{ms}(child) = T_{ms}(parent) + 200
$$

Blocks cannot skip slots. The seconds header and millisecond part must come from the same scheduled timestamp; wall-clock time controls when the sequencer starts a build, not the timestamp assigned to that block. EVM `block.timestamp` remains the whole-second header value.

### BaseTime metadata deposit

After activation, every block contains the canonical BaseTime update at `tx[1]`, immediately after the L1 information deposit at `tx[0]` and before user transactions. The deposit is bound to the current block number by source-hash domain `3`.

| Field | Value |
| --- | --- |
| Transaction type | Deposit (`0x7e`) |
| Source hash | Domain `3`, bound to the current block number |
| From | `0xDeaDDEaDDeAdDeAdDEAdDEaddeAddEAdDEAd0001` |
| To | `0x4200000000000000000000000000000000000030` |
| Mint | `0` |
| Value | `0` |
| Gas limit | `1,000,000` |
| System transaction | `false` |
| Calldata | `setTimestampMillisPart(uint16)` with selector `0x86bdf394` and a 32-byte ABI-encoded millisecond part |

Before activation, blocks must not contain this metadata transaction or the Engine millisecond field. After activation, implementations validate the transaction's position, source hash, sender, recipient, mint, value, gas limit, system flag, calldata shape, and lattice value.

### BaseTime predeploy

The initial design exposes the current block's millisecond part to contracts through a predeploy.

| Property | Value |
| --- | --- |
| Proxy | `0x4200000000000000000000000000000000000030` |
| Implementation | `0xc0D3C0d3C0d3C0D3c0d3C0d3c0D3C0d3c0d30030` |
| Storage | `uint16` millisecond part in slot `0` |
| Setter | `setTimestampMillisPart(uint16)` |
| Millisecond-part getter | `timestampMillisPart()` |
| Full-timestamp getter | `timestampMs()` |

The BaseTime deposit executes before L1 user deposits, and user transactions, so all later transactions can read the updated value.

Fresh chains install the linked BaseTime predeploy in genesis. On existing chains, the reserved address already contains the canonical proxy runtime and uses the Base ProxyAdmin, but its implementation slot is unset. Calls therefore revert until activation.

At activation, before transaction execution, the protocol installs the canonical BaseTime implementation and links the existing proxy. It preserves the proxy admin and any implementation already set through governance.

### Engine payload attributes

The Engine API uses `BasePayloadAttributes`, which flattens the standard `PayloadAttributes` fields alongside Base-specific fields.

For payloads at or after Denim activation, `BasePayloadAttributes.timestampMillisPart` **MUST** be present and equal `0`, `200`, `400`, `600`, or `800`. Before activation, it **MUST NOT** be present.

`BasePayloadAttributes.transactions[1]` **MUST** contain the BaseTime metadata deposit. The deposit **MUST** be sent by the protocol depositor to the BaseTime predeploy, and its calldata **MUST** contain the canonical encoding of `setTimestampMillisPart(uint16)`. The encoded value **MUST** equal `timestampMillisPart`.

An execution client **MUST** reject malformed `forkchoiceUpdated` payload attributes with JSON-RPC `Invalid params` (`-32602`). It **MUST** report an execution payload with a missing or invalid BaseTime deposit as invalid during `newPayload` validation.

The payload ID includes `timestampMillisPart`, so builds that differ only in the millisecond part receive different IDs. When the field is absent, legacy payload-ID calculation remains unchanged.

All existing Engine timestamp fields remain seconds-based.

### Validation

When processing `forkchoiceUpdated`, the execution client performs checks that do not require reading contract state. Before Denim, `timestampMillisPart` must be absent. After Denim, it must be present and equal `0`, `200`, `400`, `600`, or `800`. The block's whole-second timestamp must not be earlier than its parent's. Because block headers do not store milliseconds, the client checks exact 200ms progression after execution.

After execution, the client **MUST** verify that the block's full timestamp is exactly 200ms after its parent's. Blocks that do not satisfy this requirement are invalid.

## Derivation

### Scheduled timestamps

After activation, the derivation pipeline computes each block's timestamp from the Denim activation schedule and absolute L2 block number. It does not read the millisecond part from batch data or derive it from the local wall clock. The sequence is:
Comment thread
0x00101010 marked this conversation as resolved.

`p (.000)` → `child (.200)` → `child (.400)` → `child (.600)` → `child (.800)` → `child (.000 in the next second)`

The pipeline splits the scheduled timestamp into the seconds-based header timestamp and the BaseTime millisecond part, then reconstructs the BaseTime deposit at `tx[1]`.

### Block lifecycle

The sequencer builds and executes a complete block for every selected 200ms slot. Each block receives its own hash, state root, receipts, Engine payload, forkchoice update, and unsafe-to-safe-to-finalized lifecycle.

The block begins with the L1 information deposit at `tx[0]` and the BaseTime metadata deposit at `tx[1]`. User transactions and other applicable transactions follow. The design intends the payload attribute, `tx[1]`, and the value written to the BaseTime predeploy to represent the same planned millisecond part.

## RPC

<Note>
The RPC behavior below is planned for Denim and is not available on production endpoints.
</Note>

### Seconds compatibility

Denim keeps the existing `timestamp` JSON-RPC field in Unix seconds. Engine timestamps, transaction-validity timestamps, `eth_call` timestamps, and EVM `block.timestamp` also remain seconds-based. RPC responses do not expose the internal `timestampMillisPart` field.

After the Denim rollout, use canonical block responses and subscriptions such as `eth_subscribe("newHeads")` for sub-second updates. Flashblocks streams will stop.

### Block and header timestamps

The following responses will add optional `timestampMs`, encoded as a JSON-RPC quantity containing the full Unix timestamp in milliseconds:

- `eth_getBlockByHash`
- `eth_getBlockByNumber`
- `eth_getHeaderByHash`
- `eth_getHeaderByNumber`
- `eth_subscribe("newHeads")`

For example, a block at 42.200 seconds has:

| Field | Value |
| --- | --- |
| `timestamp` | `0x2a` |
| `timestampMs` | `0xa4d8` |

Clients will derive `timestampMs` from authenticated BaseTime metadata rather than estimate it from the seconds field. If a historical block body or its BaseTime metadata has been pruned or is otherwise unavailable, the response will omit `timestampMs`.

### Transaction timestamps

Mined transaction objects will add optional `blockTimestampMs` for these methods:

- `eth_getTransactionByHash`
- `eth_getTransactionByBlockHashAndIndex`
- `eth_getTransactionByBlockNumberAndIndex`

Full transaction objects nested in block responses will follow the same mined-transaction behavior. Pending transactions will omit `blockTimestampMs` because they do not yet belong to a canonical block.

### Log and receipt timestamps

Mined log objects will add optional `blockTimestampMs` when returned by:

- `eth_getLogs`
- `eth_getFilterChanges`
- `eth_getFilterLogs`
- `eth_getTransactionReceipt`
- `eth_getBlockReceipts`
- `eth_subscribe("logs")`
- `eth_subscribe("transactionReceipts")`

Receipts will expose the field on their nested logs, not at the receipt's top level. A removed log will retain its original block timestamp along with its original block provenance. If the originating block's authenticated BaseTime metadata is unavailable, the log will omit the field rather than estimate it.

As with block responses, pruned or unprovenanced transaction and log data will omit `blockTimestampMs`. The fields are optional so pre-Denim history and clients without the required body data remain representable.

### Tooling

Foundry's `AnyRpcBlock` and `OtherFields` paths can preserve an unknown block-level `timestampMs`, while EVM execution continues to use seconds. Plain Alloy `AnyRpcHeader` drops unknown fields; consumers that need Denim timestamps can use `WithOtherFields<AnyRpcHeader>` or a typed Base response. Locally mined Anvil blocks are not expected to produce BaseTime metadata in the initial rollout.
6 changes: 6 additions & 0 deletions docs/docs.json
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,12 @@
{
"group": "Upgrades",
"pages": [
{
"group": "Denim",
"pages": [
"base-chain/specs/upgrades/denim/200ms-native-blocks"
]
},
{
"group": "Cobalt",
"pages": [
Expand Down
1 change: 1 addition & 0 deletions docs/llms-full.txt
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,7 @@ const client = createPublicClient({ chain: base, transport: http() })
- [B20 Native Token Standard](https://docs.base.org/base-chain/specs/upgrades/beryl/b20): B20 is Base's native token standard - designed for stablecoin issuers, real-world asset (RWA) and equity issuers, and long-tail token creators.
- [Native Account Abstraction](https://docs.base.org/base-chain/specs/upgrades/cobalt/eip-8130): Build with native account abstraction on Base. EIP-8130 smart accounts send ordinary transactions, with no bundlers or relays.
- [Span-batches](https://docs.base.org/base-chain/specs/upgrades/delta/span-batches): Specification of span batches introduced in Delta, a new batch format that compresses sequences of L2 blocks for more efficient L1 data posting.
- [200ms native blocks](https://docs.base.org/base-chain/specs/upgrades/denim/200ms-native-blocks): Specification for Denim's canonical 200ms blocks, including BaseTime, derivation, validation, and RPC behavior.
- [Derivation](https://docs.base.org/base-chain/specs/upgrades/ecotone/derivation): Derivation changes in the Ecotone upgrade, extending the retrieval stage to support EIP-4844 blobs as an additional data availability source.
- [Ecotone L1 Attributes](https://docs.base.org/base-chain/specs/upgrades/ecotone/l1-attributes): L1 attributes transaction changes in the Ecotone upgrade, updating calldata format to support the new blob-based fee calculation model.
- [Fjord L2 Chain Derivation Changes](https://docs.base.org/base-chain/specs/upgrades/fjord/derivation): Derivation changes in the Fjord upgrade, updating protocol parameters for max sequencer drift, channel bank size, and adding brotli compression support.
Expand Down
1 change: 1 addition & 0 deletions docs/llms.txt
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@
- [B20 Native Token Standard](https://docs.base.org/base-chain/specs/upgrades/beryl/b20): B20 is Base's native token standard - designed for stablecoin issuers, real-world asset (RWA) and equity issuers, and long-tail token creators.
- [Native Account Abstraction](https://docs.base.org/base-chain/specs/upgrades/cobalt/eip-8130): Build with native account abstraction on Base. EIP-8130 smart accounts send ordinary transactions, with no bundlers or relays.
- [Span-batches](https://docs.base.org/base-chain/specs/upgrades/delta/span-batches): Specification of span batches introduced in Delta, a new batch format that compresses sequences of L2 blocks for more efficient L1 data posting.
- [200ms native blocks](https://docs.base.org/base-chain/specs/upgrades/denim/200ms-native-blocks): Specification for Denim's canonical 200ms blocks, including BaseTime, derivation, validation, and RPC behavior.
- [Derivation](https://docs.base.org/base-chain/specs/upgrades/ecotone/derivation): Derivation changes in the Ecotone upgrade, extending the retrieval stage to support EIP-4844 blobs as an additional data availability source.
- [Ecotone L1 Attributes](https://docs.base.org/base-chain/specs/upgrades/ecotone/l1-attributes): L1 attributes transaction changes in the Ecotone upgrade, updating calldata format to support the new blob-based fee calculation model.
- [Fjord L2 Chain Derivation Changes](https://docs.base.org/base-chain/specs/upgrades/fjord/derivation): Derivation changes in the Fjord upgrade, updating protocol parameters for max sequencer drift, channel bank size, and adding brotli compression support.
Expand Down
Loading