ERC-8330: Subject-Linked NAV Snapshot Oracle

Thanks again to @SergeevDmitry for identifying this issue.

The resulting change has now been merged in ethereum/ERCs#2116, returning ERC-8330 to Review. The open-source Solidity reference implementation has been updated to the same fresh-quorum model and is available in KulaDao/titled-asset-standards#42 and the UI Suite linked above.

This goes further than our October 14 response in post 10. That response would have made aggregate isPublishStale true when any eligible submission was publication-stale, while still leaving those submissions in the median. On further review, a flag was not a strong enough guarantee, because stale inputs would still have moved the result.

The revised aggregate uses only provider submissions whose publishedAt is no more than the configured maxSubmissionAge before block.timestamp. maxSubmissionAge is independent of the stream heartbeat. aggregatedNAV is available only when the latest aggregation timestamp has at least the configured quorum of those fresh submissions. Otherwise the call reverts. It does not fall back to an older valuation timestamp that still has a fresh quorum.

The merged interface changes are:

  • isPublishStale has been removed from the aggregatedNAV return values.

  • The return tuple is now (nav, decimals, navBasis, valuationTimestamp, freshProviderCount, isValuationStale).

  • freshProviderSubmissionCount(subjectId, currency) returns the number of currently fresh contributors at the latest aggregation timestamp.

  • freshProviderSubmissionAt(subjectId, currency, index) exposes those fresh submissions.

  • setAggregationConfig and AggregationConfigUpdated now include maxSubmissionAge, and maxSubmissionAge(subjectId, currency) exposes the configured window.

latestNAVStatus() still reports stream-level publication staleness from heartbeat. A successful aggregatedNAV does not mean that flag would be false.

maxSubmissionAge should exceed the expected maximum publication spread between the providers required for quorum. If the spread is greater than the window, an early submission can age out before the final required provider publishes. The valuation timestamp can then have a historical quorum without a fresh quorum at that moment.

The result is intentionally time-dependent. Freshness is evaluated at query time using block.timestamp. As individual submissions age beyond maxSubmissionAge, the contributor set can shrink, the lower median can change while fresh quorum remains, and aggregatedNAV reverts once fresh quorum is lost.

This is independent of valuation staleness. An aggregate can have a fresh quorum while its shared valuation timestamp is economically old, so an available result can still return isValuationStale = true.

The revision also changes the INAVAggregation ERC-165 interface ID. aggregatedNAV(bytes32,bytes32) keeps its function selector, but the return tuple is ABI-incompatible with the earlier draft. A decoder from that draft can read the old providerCount as freshProviderCount and the old isPublishStale as isValuationStale. Integrations need to verify support for the revised interface ID before decoding the response. The setAggregationConfig selector changed as well.

1 Like