> ## Documentation Index
> Fetch the complete documentation index at: https://base-a060aa97-docs-sync-code-change-740ee5d.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# BaseTime Millisecond Timestamp

> Denim adds the BaseTime predeploy so contracts can read the block timestamp at millisecond precision through a read-only interface.

## Abstract

Denim installs the BaseTime predeploy at `0x4200000000000000000000000000000000000030`. 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 a `0x4200…` address, not a native precompile. Its handle therefore lives in a new `StdPredeploys` library instead of `StdPrecompiles`.

### New interface and library

```solidity title="IBaseTime and StdPredeploys" lines expandable theme={null}
interface IBaseTime {
    function timestampMillisPart() external view returns (uint16);
    function timestampMs() external view returns (uint64);
    function version() external view returns (string memory);
}

library StdPredeploys {
    address internal constant BASE_TIME_ADDRESS = 0x4200000000000000000000000000000000000030;
    IBaseTime internal constant BASE_TIME = IBaseTime(BASE_TIME_ADDRESS);
}
```

| Function | Selector | Status | Notes |
| - | - | - | - |
| `timestampMillisPart()` | `0x7b2fea99` | New (view) | Millisecond component: `0`, `200`, `400`, `600`, or `800`. Never reverts. |
| `timestampMs()` | `0x5745a677` | New (view) | `block.timestamp * 1000 + timestampMillisPart()`. Never reverts. |
| `version()` | `0x54fd4d50` | New (view) | Returns `"1.0.0"`. |

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 timestampMillisPart` sits 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 the `tx[0]` L1-info deposit), `timestampMs()` combines the current block's seconds with the previous block's millisecond component.

### Example

```solidity title="Read the millisecond timestamp" highlight={3} theme={null}
import {StdPredeploys} from "base-std/StdPredeploys.sol";

uint64 nowMs = StdPredeploys.BASE_TIME.timestampMs();
// block.timestamp == 1_800_000_000, timestampMillisPart() == 400
// nowMs == 1_800_000_000_400
```

## 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 `StdPredeploys` library.** Predeploys are EVM bytecode and precompiles are native code. Separate libraries keep that distinction visible at the call site.
* **Slot-0 mock layout.** `MockBaseTime` stores the `uint16` at slot 0 instead of an ERC-7201 namespace. This matches the real contract's layout, so `vm.store`-based tests behave the same against either.
