Skip to content

Research notes

Chainlink startedAt vs updatedAt: Read the Implementation

Two fields may expose one transmission clock or separate observation and transmission clocks. A constructed timeline shows why freshness and finality need distinct evidence.

An ivory paper ribbon passes through two ceramic apertures beside a lime bead on a dark surface.
Illustration for research notes; examples in this article are educational.

Chainlink's startedAt and updatedAt are timestamps returned by latestRoundData(), but their exact meanings depend on the aggregator. In the inspected libocr OffchainAggregator, both identify the transmission block's time. In the inspected OCR2Aggregator, startedAt identifies the offchain observations time and updatedAt the transmission block's time. Neither field proves block finality, and an application must define its own freshness policy.

Start with the implementation, then interpret the names

The generic interface describes startedAt as the round's start timestamp and updatedAt as its update timestamp. That is an interface description, rather than a guarantee that every implementation measures two distinct events. The tuple also contains roundId, answer and the deprecated answeredInRound. Chainlink's Data Feeds API reference.

Two concrete implementations show why the distinction matters. This comparison uses the libocr repository at commit 618b5bf, inspected on 28 October 2026. It establishes what those files do; it does not identify the implementation behind every deployed feed.

Inspected implementationstartedAt comes fromupdatedAt comes from
OffchainAggregatorStored transmission timestampThe same stored timestamp
OCR2AggregatorReport's observations timestampStored transmission timestamp

In the pinned OffchainAggregator implementation, a transmission stores the executing block's timestamp. The read method returns that stored value in both positions. Subtracting these fields therefore gives zero even though collecting observations, assembling the report and delivering its transaction took time.

In the pinned OCR2Aggregator implementation, the report supplies an observations timestamp, while the contract separately records the transmission block's timestamp. The two returned fields can differ. Their difference describes the interval between those recorded timestamps, rather than a measurement of every component of oracle latency.

For a real integration, establish the network and intended proxy, then identify its underlying aggregator and inspect its verified source. Chainlink documents typeAndVersion() for identifying the aggregator type; this differs from version(). Do not infer implementation behavior from the asset symbol or a remembered example. Chainlink's aggregator identification guidance.

Also check who operates the feed. Chainlink documents self-managed deployments that can expose Data Feed–style proxies while having different operators and monitoring arrangements. A familiar interface does not settle those responsibilities. Chainlink's description of self-managed feeds.

Follow one constructed report through both mappings

Consider an illustrative report with observations timestamped at 12:00:00 UTC and a successful transmission included in a block timestamped 12:00:12. These are invented times on one date, not measurements of a live feed. Assume the report is valid and no later report appears in the snapshots below.

The table applies each inspected implementation's mapping to the same teaching inputs. It follows one aggregator-local round and does not attempt to encode a proxy phase.

EventOffchainAggregator viewOCR2Aggregator view
Observations at 12:00:00Separate observation time is not exposed by these two fieldsReport carries 12:00:00
Transmission block at 12:00:12startedAt and updatedAt become 12:00:12startedAt is 12:00:00; updatedAt is 12:00:12
Read at 12:01:00Both stored fields remain 12:00:12Stored fields remain 12:00:00 and 12:00:12

At 12:01:00, time since transmission is 48 seconds in either mapping. In the OCR2 example, time since the recorded observations is 60 seconds, and the recorded observation-to-transmission interval is 12 seconds. The arithmetic reconciles: 60 equals 48 plus 12.

In the OffchainAggregator example, the timestamp difference is zero because the read exposes one stored clock twice. Concluding that observations occurred exactly at transmission would add information the returned fields do not supply.

The reverse shortcut also fails: a nonzero difference is not automatically a delay fault. Its interpretation requires the implementation and the consumer's chosen limit. Neither timestamp identifies every underlying exchange trade used in an aggregated answer.

Keep price scaling separate. A correct timestamp interpretation can accompany an incorrectly decoded answer; the feed-decimals versus token-decimals guide covers that independent validation step.

A recent timestamp does not establish finality

Suppose an illustrative offchain monitor requires an established finalized state context and a report no more than 120 seconds old at observation. Its boundary is inclusive. These are example application rules, not Chainlink settings or a recommended threshold.

The first read happens at 12:01:00 against a current head also timestamped 12:01:00 and containing the transmission. Its report age is only 48 seconds. If that state has not satisfied the monitor's finality requirement, recency passes while confidence fails.

Later, at 12:20:00, the monitor receives an old cached snapshot whose block timestamp is 12:00:36. Assume that block has been independently established as finalized. No particular finalization duration is assumed. The snapshot contains the same report updated at 12:00:12.

Constructed readAge within selected blockAge at monitor observationDecision under example rules
Current head read at 12:01:0048 s48 sWait: finality requirement unresolved
Old finalized snapshot read at 12:20:0024 s1,188 sReject: report too old for monitor

For the second row, subtracting only the selected block's time yields 24 seconds. But the monitor is looking at a snapshot 1,164 seconds behind its observation clock. Adding that lag gives the monitor-relative report age: 24 plus 1,164 equals 1,188 seconds, or 19 minutes 48 seconds.

An onchain consumer evaluates against its execution context, using that block's time. An offchain monitor additionally needs to detect a lagging endpoint or cached snapshot. These are different observation settings; silently treating a past block timestamp as the current wall clock defeats the monitor's recency requirement.

Ethereum's execution API distinguishes latest, safe and finalized block selections. A value returned from latest is not promoted to finalized by having an acceptable updatedAt. Apply each network's documented confidence model, particularly on L2s. Ethereum's block-selection reference.

Record the block hash with the tuple. The RPC batch versus block-snapshot guide explains how to preserve a common state context when collecting additional fields. A finality label attached to a different read cannot establish the context of this tuple.

answeredInRound cannot replace these decisions

Current Chainlink documentation marks answeredInRound deprecated, explaining its earlier role when computing answers could span rounds. It is not a current freshness timestamp. Chainlink's return-value definitions.

Both inspected libocr read methods return the local aggregator round identifier in the answeredInRound position. Compare the pinned OffchainAggregator return method and OCR2Aggregator return method. For these implementations, equality with roundId follows from their return expressions. It cannot demonstrate that a report is recent, that a block is finalized or that observations match a consumer's required time window.

An old tuple can continue satisfying an equality check indefinitely while its age increases. Conversely, encountering an unexpected tuple from a different adapter calls for inspecting that implementation and its documented contract. Deprecation does not justify guessing the meaning of an unfamiliar return value.

Preserve the raw field when recording evidence if useful, but give each active acceptance check a documented purpose. A copied historical checklist can look comprehensive while omitting the clock the application actually needs.

Write the acceptance contract before the subtraction

Use this worksheet for each feed integration. It separates the evidence to collect from the decision made with it.

CheckEvidence and explicit rule
IdentityNetwork, proxy, underlying implementation and intended data meaning
Timestamp mappingSource of each returned timestamp in that implementation
Usable responseSuccessful call, decoded tuple and available data
Time plausibilityRequired nonzero timestamps and future-time policy before subtraction
RecencyChosen reference clock, maximum age and boundary convention
Chain confidenceSelected block identity and required confidence stage
Offchain collectionObservation time, endpoint lag and cache policy
RejectionRecorded reason and a defined pause, review or contingency action

A missing-data response must remain distinguishable from a successful, usable read. For an asset-price consumer, validate the answer and its scale under that feed's data contract as well. The timestamp checklist alone does not validate the economic value.

Choose the reference clock explicitly. If an implementation exposes a separate observations timestamp and the application needs an observations-age limit, define that additional gate. A limit based only on transmission time cannot silently become a guarantee about observation age.

The provider's update cadence is another input. Heartbeat and deviation triggers explain when updates are initiated; the consumer still defines acceptable recency. The oracle staleness and deviation guide develops that policy choice. Chainlink assigns application data-quality checks and contingency logic to the integrating developer. Chainlink's developer responsibilities.

Keep the sequencer's status clock separate

A sequencer uptime feed uses the same familiar interface for a different purpose. Its startedAt identifies a status change, which is relevant to recovery grace checks. It is not the asset report's observation or transmission time. Chainlink also documents an Arbitrum initialization case where this field can be zero; that is not a universal initialization rule for every supported L2. Chainlink's sequencer feed guidance.

The sequencer-feed versus price-feed comparison develops the separate gates. Keep distinct records for price age, sequencer status, recovery eligibility and the selected chain state. Passing one should not overwrite a failure in another.

Before relying on the integration, exercise the written policy with small fixtures: age exactly 120 seconds, age 121 seconds, a zero timestamp, an unexpected future timestamp, a recent unfinalized report and the old finalized snapshot above. Require a specific rejection reason for each failing case. Then verify that the same timestamp tuple is interpreted according to the actual aggregator behind the intended feed.

Keep the context with the example.

Each worked example is illustrative, with its sources and assumptions in the article. Read our editorial standards for authorship, verification and corrections.