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, 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-basedtimestamp unchanged. A BaseTime metadata deposit supplies the sub-second component. Together, these values identify a canonical block’s full millisecond timestamp.
Tentative activation
No Denim activation has been scheduled. A later node upgrade guide will provide release and rollout instructions.
Execution
Full block timestamp
Denim leavesblock.header.timestamp as Unix time in whole seconds. For an activated block b, its full timestamp is:
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:
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 attx[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.
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.
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 usesBasePayloadAttributes, 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 processingforkchoiceUpdated, 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: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 attx[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
The RPC behavior below is planned for Denim and is not available on production endpoints.
Seconds compatibility
Denim keeps the existingtimestamp 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 optionaltimestampMs, encoded as a JSON-RPC quantity containing the full Unix timestamp in milliseconds:
eth_getBlockByHasheth_getBlockByNumbereth_getHeaderByHasheth_getHeaderByNumbereth_subscribe("newHeads")
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 optionalblockTimestampMs for these methods:
eth_getTransactionByHasheth_getTransactionByBlockHashAndIndexeth_getTransactionByBlockNumberAndIndex
blockTimestampMs because they do not yet belong to a canonical block.
Log and receipt timestamps
Mined log objects will add optionalblockTimestampMs when returned by:
eth_getLogseth_getFilterChangeseth_getFilterLogseth_getTransactionReceipteth_getBlockReceiptseth_subscribe("logs")eth_subscribe("transactionReceipts")
blockTimestampMs. The fields are optional so pre-Denim history and clients without the required body data remain representable.
Tooling
Foundry’sAnyRpcBlock 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.