ERC-6372 Contract clock disclosure interface (Contract clock)

Review · Other · created 2023-01-25

An interface that lets a contract expose, via two standard functions, whether it internally measures time by block number or by timestamp.

Why it is needed

Ethereum contracts frequently measure time. Timelock contracts enforce a certain delay before executing an operation. DAOs have voting periods during which proposals can be voted for or against, and voting tokens store changes in voting power as point-in-time snapshots. These methods of measuring time vary. Some contracts use timestamp, some use block number, and some use more unusual functions. Until now, however, there was no standard way for outsiders to determine which clock a given contract uses. As a result, other contracts or apps that wanted to integrate had to make risky assumptions, severely limiting interoperability. This standard solves this problem by defining a common interface through which a contract discloses, on its own, which clock it uses.

What to implement

In depth

Requirements for the clock function

A contract conforming to the standard MUST implement both the clock and CLOCK_MODE functions. clock() is a view function that returns, as a uint48, the current timepoint according to the mode in which the contract operates. This value MUST be a non-decreasing function with respect to the chain, with block.timestamp or block.number given as examples. It takes no input and its output is a single uint48 named timepoint.

CLOCK_MODE string format

CLOCK_MODE() returns, as a machine readable string, which clock the contract uses. This string MUST be in URL query string (application/x-www-form-urlencoded) format and MUST be interpretable in JavaScript as new URLSearchParams(CLOCK_MODE). In block number mode, if the contract uses the block number from the NUMBER opcode (0x43), it MUST return mode=blocknumber&from=default; if it uses a different block number, it MUST return mode=blocknumber&from=<CAIP-2-ID>, where <CAIP-2-ID> is a CAIP-2 blockchain ID such as eip155:1. In timestamp mode it MUST return mode=timestamp, and for any other mode it SHOULD place a unique identifier in the mode field.

Why uint48

The reason the return type is set to uint48 is that it is sufficient to hold realistic values. In timestamp mode it can hold values up to the year 8921556, and even in the extreme case of block number mode producing 10,000 blocks per second, it is sufficient until the year 2861. Using a type smaller than uint256 allows the timepoint value to be packed into the same storage slot as related values, substantially reducing storage and read costs. By contrast, the original text explains that a smaller type such as uint32 could become a problem fairly soon given developments in blockchains such as layer 2s, and a type larger than uint48 would be wasteful.

uint32 recommended for storing durations

There are also cases where, in addition to a timepoint, one needs to store a duration, that is, the difference between two timepoints, or a delay. Generally this is expected to be represented with the same uint48 as a timepoint, but the original text considers uint32 a good alternative in most cases. This is because, when the clock runs in seconds, uint32 can represent more than 136 years. So uint48 is recommended for storing timepoints and uint32 for storing durations. This recommendation applies to reasonably ranged durations such as timelock delays, voting periods, and vesting periods, and to operation with timestamps or with block numbers whose interval is longer than 1 second.

Expected properties and proposal status

The only expected property the original text specifies is that the clock() function must not decrease (MUST). The Security Considerations section states only that there are no known security issues. Meanwhile, the status of this document is Review. Since this proposal has not yet been finalized, it is advisable to check before implementation whether the interface or requirements may change going forward.

Original ERC · ← ERCs