From f8f7f82901e562e280bc0321514e874d4c4a9369 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 9 Oct 2026 15:21:21 +0000 Subject: [PATCH] docs: sync from base-std@740ee5d --- ...3-denim-basetime-millisecond-timestamp.mdx | 69 +++++++++++++++++++ docs/docs.json | 3 +- docs/specifications/b20/changelog.mdx | 1 + 3 files changed, 72 insertions(+), 1 deletion(-) create mode 100644 docs/base-chain/specs/reference/b20/changelog/03-denim-basetime-millisecond-timestamp.mdx diff --git a/docs/base-chain/specs/reference/b20/changelog/03-denim-basetime-millisecond-timestamp.mdx b/docs/base-chain/specs/reference/b20/changelog/03-denim-basetime-millisecond-timestamp.mdx new file mode 100644 index 000000000..5b3c1fd06 --- /dev/null +++ b/docs/base-chain/specs/reference/b20/changelog/03-denim-basetime-millisecond-timestamp.mdx @@ -0,0 +1,69 @@ +--- +title: "BaseTime Millisecond Timestamp" +description: "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 +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} +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. diff --git a/docs/docs.json b/docs/docs.json index f1973f8a0..9013fa4d9 100644 --- a/docs/docs.json +++ b/docs/docs.json @@ -470,7 +470,8 @@ "upgrades/denim/migrate-from-flashblocks", "base-chain/specs/reference/b20/changelog/03-denim-b20-token-receiver", "base-chain/specs/reference/b20/changelog/03-denim-b20-transfer-executor-enforcement", - "base-chain/specs/reference/b20/changelog/03-denim-policyregistry-not-policy" + "base-chain/specs/reference/b20/changelog/03-denim-policyregistry-not-policy", + "base-chain/specs/reference/b20/changelog/03-denim-basetime-millisecond-timestamp" ] }, { diff --git a/docs/specifications/b20/changelog.mdx b/docs/specifications/b20/changelog.mdx index 0f32c7516..c4cbc780d 100644 --- a/docs/specifications/b20/changelog.mdx +++ b/docs/specifications/b20/changelog.mdx @@ -24,6 +24,7 @@ B20 method and event signatures are part of the chain's consensus surface. Exist | B20 Asset, B20 Stablecoin | (Breaking) Reject the token itself as a credit recipient | `IB20` (shared surface), `IB20Asset` (`batchMint`) | [Token receiver](/base-chain/specs/reference/b20/changelog/03-denim-b20-token-receiver) | | B20 Asset, B20 Stablecoin | (Breaking) Transfer executor policy on every transfer path | `IB20` (shared surface) | [Transfer executor enforcement](/base-chain/specs/reference/b20/changelog/03-denim-b20-transfer-executor-enforcement) | | PolicyRegistry | NOT / invert policies | `IPolicyRegistry` | [NOT policies](/base-chain/specs/reference/b20/changelog/03-denim-policyregistry-not-policy) | +| BaseTime | Millisecond block timestamp predeploy (read-only) | `IBaseTime`, `src/StdPredeploys.sol` | [Millisecond block timestamp predeploy (read-only)](/base-chain/specs/reference/b20/changelog/03-denim-basetime-millisecond-timestamp) | ## [Cobalt](/upgrades/cobalt/overview), Ordinal 02