Abstract
Denim installs the BaseTime predeploy at0x4200000000000000000000000000000000000030. It exposes the millisecond component of the current block timestamp. Base Std adds the read-only IBaseTime interface and a typed handle, StdPredeploys.BASE_TIME. Call StdPredeploys.BASE_TIME.timestampMs() for the full millisecond timestamp.
Motivation
block.timestamp has second precision, but from Denim a block’s timestamp carries a sub-second component. Contracts that need the exact block time, such as auctions, rate limits, or time-weighted pricing, can’t read the millisecond component from the EVM alone.
What changed
BaseTime is a proxied predeploy: Solidity bytecode at a0x4200… address, not a native precompile. Its handle therefore lives in a new StdPredeploys library instead of StdPrecompiles.
New interface and library
IBaseTime and StdPredeploys
The depositor-only setter and its errors are protocol-internal and are not part of
IBaseTime.
Behavior
- No existing selector, event, error, or storage slot changes. The predeploy is new.
- Storage:
uint16 timestampMillisPartsits in the low-order 2 bytes of slot 0. - The protocol writes the millisecond component once per Denim block, through a depositor-only setter called by the block-scoped deposit at
tx[1]. Until that deposit executes (for example, during thetx[0]L1-info deposit),timestampMs()combines the current block’s seconds with the previous block’s millisecond component.
Example
Read the millisecond timestamp
Migration
This change is additive. No existing integration changes.- After Denim activates, read
StdPredeploys.BASE_TIME.timestampMs()for millisecond block time. - Code that runs before
tx[1]in a block, such as the L1-info deposit, sees the previous block’s millisecond component. - Before Denim, the predeploy has no implementation and calls to it revert. Don’t depend on it on a chain where Denim hasn’t activated.
Alternatives considered
- Read-only interface. Only the protocol deposit can write the value, so exposing the setter would give integrators a selector that always reverts for them.
- Separate
StdPredeployslibrary. Predeploys are EVM bytecode and precompiles are native code. Separate libraries keep that distinction visible at the call site. - Slot-0 mock layout.
MockBaseTimestores theuint16at slot 0 instead of an ERC-7201 namespace. This matches the real contract’s layout, sovm.store-based tests behave the same against either.