Skip to Content
Supra Layer 1SupraEVMBlock Timestamp in Microseconds

Reading the Block Timestamp in Microseconds

Supra blocks carry a timestamp with microsecond resolution. block.timestamp reports it in whole seconds, as it does on every EVM chain, and a precompile reports the whole value.

The precompile lives at 0x0000000000000000000000000000000053555004, and the address is frozen.

Quick start

import {LibBlockTimestamp} from "supra_contracts/libraries/LibBlockTimestamp.sol"; contract Clock { function elapsedSince(uint64 startMicros) external view returns (uint64) { return LibBlockTimestamp.microseconds() - startMicros; } }

The supra_contracts import is set up as described in Importing the Solidity libraries. The library is convenience only. Calling ISupraBlockTimestamp(0x0000000000000000000000000000000053555004).microseconds() directly gives the same behaviour.

What you get

microseconds() returns a uint64: the microseconds since the Unix epoch of the block being executed. It is the same number SupraMVM’s timestamp::now_microseconds() returns for that block, so a contract and a Move module reading in the same block read one clock.

block.timestamp is that value divided by 1,000,000. The two always agree:

assert(LibBlockTimestamp.microseconds() / 1_000_000 == block.timestamp);

There are no rules about who may read. Every call is served - STATICCALL, a nested CALL, a DELEGATECALL from a proxy, a constructor, an automated task - because the value is public header data, identical for every transaction in the block and for every frame of every transaction. There is nothing about it a caller could influence or gain by reading from one frame rather than another. A call that carries value is also served; the value sent stays at the precompile’s address, which no key controls, and cannot be recovered.

The one place a read is refused is genesis contract deployment, which runs before any block exists and so has no certified timestamp to serve. A constructor that records its own deployment microsecond works for every ordinary deployment and reverts TimestampUnavailable() if the same contract is deployed as part of genesis. Handle it, or keep the clock out of the constructor, if your contract is going into a genesis set.

The read itself costs a fixed 20 gas, the same as BLOCKHASH, on top of the cost of the call.

What it is not

Not a finer-grained ordering signal. Every transaction in a block reads the same value. To tell two transactions of one block apart, use their position in the block, not the clock.

Not a replacement for block.timestamp. Deadlines, timelocks and vesting schedules are written in seconds across the whole ecosystem, and block.timestamp stays in seconds so that code ported to Supra behaves as it did. Mixing the two units in one comparison is the mistake to avoid: store which unit a stored deadline is in, or keep every deadline in one of them.

Reverts

Every refusal except running out of gas reverts with a custom error rather than returning a sentinel, so a zero can never be mistaken for a timestamp. A frame with fewer than 20 gas fails out of gas with empty revert data.

ErrorWhen
UnknownSelector()the call data names something other than microseconds()
MalformedInput()fewer than four bytes, or microseconds() followed by extra bytes
TimestampUnavailable()there is no certified block behind the execution

TimestampUnavailable() is reachable from a transaction in one case, genesis contract deployment, as described above. Otherwise every block execution and every eth_call or eth_estimateGas supplies the block’s clock, and it is the fail-closed answer for internal node paths that have no block behind them.

Testing against it

In a Foundry test, mock the address with the vm.mockCall cheat code, the way you would any other precompile. Hardhat has no vm, so the snippet is Foundry-only:

vm.mockCall( address(0x0000000000000000000000000000000053555004), abi.encodeWithSignature("microseconds()"), abi.encode(uint64(1_700_000_000_000_000)) );

/rpc/v1/eth refuses an eth_call or eth_estimateGas that names a past height. On the wallet-integration endpoint, an eth_estimateGas at a past height runs in that block’s environment, so the precompile reports that block’s clock; an eth_call there is answered for the head whatever height is named. Neither answers what a timestamp-reading contract would have done at an earlier height. See The wallet-integration endpoint.

Last updated on