ERC-8329 - Subject-Linked Impact Snapshot Log

Created 2026-07-05
Status Draft
Category ERC
Type Standards Track
Authors
Requires

Abstract

This ERC defines an append-only interface for reporting quantitative impact indicator snapshots against application-defined subjects. Each snapshot records a signed value, decimal precision, unit, completed measurement period, methodology commitment and location, reporter, recording time, and correction provenance.

The core interface supports per-subject storage, per-indicator indexing, exact period lookup, and fork-free correction chains. Optional interfaces support append-only endorsement or dispute attestations and future methodology supersession with active and pending methodology discovery.

This ERC standardizes representation and lifecycle behavior. It does not define which indicators or methodologies are valid, verify reported measurements, credential reporters or attestors, prevent overlapping claims, or provide an impact score.

Motivation

Impact measurements such as emissions, carbon offsets, renewable energy, water treatment, employment, beneficiaries, biodiversity area, and diverted waste are commonly distributed through reports and application-specific data formats. On-chain systems lack a common interface for associating these values with a subject, measurement period, unit, and methodology while preserving later corrections.

A single mutable value cannot distinguish a new reporting period from a restatement of an earlier measurement. It also erases the prior assertion when updated. Generic attestations can represent individual claims but do not by themselves define per-indicator time-series indexing, exact-period uniqueness, correction chains, or methodology transitions.

This ERC provides:

The interfaces can be deployed independently of any token, registry, identity, or accounting system.

Specification

The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119 and RFC 8174.

Definitions

A subject is an application-defined entity identified by subjectId.

An indicator is an application-defined measured quantity identified by indicatorId.

A snapshot is one reported value for an indicator, subject, and measurement period.

A measurement period is the half-open interval [periodStart, periodEnd).

An original snapshot is the first snapshot occupying an exact subject, indicator, and period slot.

A correction chain is a linear sequence of snapshots connected by correctsIndex and correctedByIndex.

A terminal snapshot is a snapshot whose correctedByIndex equals zero.

A methodology is the documented process used to measure or calculate a reported value.

An attestation is an append-only endorsement or dispute attached to a specific snapshot.

Sentinel Value

Implementations MUST use:

uint256 constant NO_CORRECTION = type(uint256).max;

correctsIndex == NO_CORRECTION identifies an original snapshot. correctedByIndex == 0 identifies a snapshot with no successor correction.

Index zero is safe as the corrected-by sentinel because every correction index is greater than the snapshot it corrects and therefore cannot be zero.

Core Snapshot Interface

A compliant log MUST implement:

interface IImpactSnapshotLog {
    struct IndicatorSnapshot {
        bytes32 subjectId;
        bytes32 indicatorId;
        int256 value;
        uint8 decimals;
        bytes32 unit;
        uint64 periodStart;
        uint64 periodEnd;
        bytes32 methodologyHash;
        string methodologyURI;
        address reportedBy;
        uint64 reportedAt;
        uint256 correctsIndex;
        uint256 correctedByIndex;
    }

    event SnapshotRecorded(
        bytes32 indexed subjectId,
        bytes32 indexed indicatorId,
        uint256 indexed snapshotIndex,
        int256 value,
        uint8 decimals,
        bytes32 unit,
        uint64 periodStart,
        uint64 periodEnd,
        bytes32 methodologyHash,
        uint256 correctsIndex,
        address reportedBy
    );

    function recordSnapshot(
        bytes32 subjectId,
        bytes32 indicatorId,
        int256 value,
        uint8 decimals,
        bytes32 unit,
        uint64 periodStart,
        uint64 periodEnd,
        bytes32 methodologyHash,
        string calldata methodologyURI,
        uint256 correctsIndex
    ) external returns (uint256 snapshotIndex);

    function getSnapshot(
        bytes32 subjectId,
        uint256 snapshotIndex
    ) external view returns (IndicatorSnapshot memory);

    function snapshotCount(
        bytes32 subjectId
    ) external view returns (uint256);

    function indicatorSnapshotCount(
        bytes32 subjectId,
        bytes32 indicatorId
    ) external view returns (uint256);

    function indicatorSnapshotAt(
        bytes32 subjectId,
        bytes32 indicatorId,
        uint256 ordinal
    ) external view returns (uint256 snapshotIndex);

    function latestIndicatorSnapshot(
        bytes32 subjectId,
        bytes32 indicatorId
    ) external view returns (uint256 snapshotIndex);

    function currentSnapshotForPeriod(
        bytes32 subjectId,
        bytes32 indicatorId,
        uint64 periodStart,
        uint64 periodEnd
    ) external view returns (uint256 snapshotIndex);
}

Value, Decimal, and Unit Semantics

The represented quantity is:

value * 10^(-decimals)

value is signed because an impact measurement can be negative. This ERC does not impose a maximum decimal precision below the uint8 type limit. Consumers MUST handle the declared precision safely and MUST NOT assume the value is nonnegative.

unit SHOULD be the keccak256 hash of a documented canonical unit string. Applications SHOULD use SI or Unified Code for Units of Measure (UCUM)-compatible representations when available and MUST document exact case, spelling, pluralization, and conversion rules for custom or zero-valued unit identifiers.

This ERC does not require a nonzero subjectId, indicatorId, or unit. Applications requiring stricter namespaces MUST enforce and document them.

Measurement Periods

periodStart is inclusive and periodEnd is exclusive.

recordSnapshot MUST revert unless:

periodStart < periodEnd <= block.timestamp

Only completed measurement periods can be recorded.

An exact period slot is identified by (subjectId, indicatorId, periodStart, periodEnd). A log MUST accept at most one original snapshot for an exact slot. Once occupied, revisions to that slot MUST use correction provenance.

Different periods MAY overlap. This ERC does not determine whether overlapping measurements are additive, duplicative, or methodologically compatible.

Recording Semantics

Snapshot indices MUST be zero-based and scoped per subjectId. The returned snapshotIndex MUST equal the subject's snapshot count before the new snapshot is appended.

recordSnapshot MUST be restricted to authorized reporters. The authorization mechanism is implementation defined and MUST be documented.

For every accepted snapshot, the log MUST:

methodologyHash MUST NOT be bytes32(0), and methodologyURI MUST NOT be empty.

The methodology hash MUST be keccak256 of the exact methodology document bytes under a documented representation. Consumers MUST retrieve the document and reproduce the commitment before relying on it.

Correction Semantics

An original snapshot MUST use correctsIndex == NO_CORRECTION.

A correction MUST:

When a correction is accepted, the log MUST set the target snapshot's correctedByIndex to the new snapshot index. No other target field may change.

Each snapshot can be corrected at most once, preventing forks. A correction snapshot can itself be corrected, producing a linear chain.

The correction policy MUST NOT permit an ordinary reporter to correct another reporter's snapshot merely because both addresses can report. It MAY authorize the original reporter, a designated corrector, or an administrator.

A correction MAY change value, decimals, unit, methodology, and reporter, subject to the active methodology rules of an implementation supporting the methodology extension.

Snapshot Queries

getSnapshot MUST return the complete stored snapshot and MUST revert when snapshotIndex >= snapshotCount(subjectId).

snapshotCount MUST return the number of snapshots recorded for a subject.

indicatorSnapshotCount MUST return the number of snapshots recorded for an exact subject and indicator, including corrections.

indicatorSnapshotAt MUST return the per-subject snapshot index at the specified zero-based indicator ordinal and MUST revert when the ordinal is outside the indicator-specific index.

latestIndicatorSnapshot MUST return the most recently recorded snapshot index for the indicator and MUST revert when none exists. It describes recording order, not the greatest periodEnd, and does not necessarily identify the current value for a particular period.

currentSnapshotForPeriod MUST return the terminal snapshot index for the exact subject, indicator, periodStart, and periodEnd. It MUST revert when no snapshot occupies that period slot.

Consumers querying a specific reporting period SHOULD use currentSnapshotForPeriod rather than latestIndicatorSnapshot.

Indicator Identifiers

The following indicator identifiers are defined:

bytes32 constant CARBON_OFFSET =
    keccak256("ERC-8329:INDICATOR:CARBON_OFFSET");
bytes32 constant CARBON_EMITTED =
    keccak256("ERC-8329:INDICATOR:CARBON_EMITTED");
bytes32 constant ENERGY_GENERATED =
    keccak256("ERC-8329:INDICATOR:ENERGY_GENERATED");
bytes32 constant ENERGY_SAVED =
    keccak256("ERC-8329:INDICATOR:ENERGY_SAVED");
bytes32 constant WATER_TREATED =
    keccak256("ERC-8329:INDICATOR:WATER_TREATED");
bytes32 constant JOBS_CREATED =
    keccak256("ERC-8329:INDICATOR:JOBS_CREATED");
bytes32 constant BENEFICIARIES =
    keccak256("ERC-8329:INDICATOR:BENEFICIARIES");
bytes32 constant BIODIVERSITY_AREA =
    keccak256("ERC-8329:INDICATOR:BIODIVERSITY_AREA");
bytes32 constant WASTE_DIVERTED =
    keccak256("ERC-8329:INDICATOR:WASTE_DIVERTED");

Custom indicators SHOULD use:

keccak256("ERC-8329:INDICATOR:<NAMESPACE>:<NAME>:<VERSION>")

Applications MUST document indicator definitions, boundaries, calculation rules, and any mapping to external taxonomies. A generic identifier such as keccak256("CUSTOM") SHOULD NOT be used because it does not provide semantic separation.

Unit Identifiers

The following unit identifiers are defined:

bytes32 constant UNIT_TCO2E = keccak256("tCO2e");
bytes32 constant UNIT_KWH = keccak256("kWh");
bytes32 constant UNIT_M3 = keccak256("m3");
bytes32 constant UNIT_FTE = keccak256("FTE");
bytes32 constant UNIT_PERSONS = keccak256("persons");
bytes32 constant UNIT_HECTARES = keccak256("hectares");
bytes32 constant UNIT_TONNES = keccak256("tonnes");

UNIT_FTE uses FTE to denote full-time equivalent; the preimage string remains "FTE" for stability.

The defined indicator identifiers do not mandate one unit. Reporters and consumers MUST inspect the stored unit and methodology rather than inferring a unit from indicatorId alone.

Attestation Extension

Attestation is OPTIONAL. An implementation supporting it MUST implement:

interface IImpactAttestation {
    struct Attestation {
        address attestor;
        bool endorsed;
        bytes32 evidenceHash;
        string evidenceURI;
        uint64 attestedAt;
    }

    event SnapshotAttested(
        bytes32 indexed subjectId,
        uint256 indexed snapshotIndex,
        address indexed attestor,
        bool endorsed,
        bytes32 evidenceHash,
        uint256 attestationIndex
    );

    function attestSnapshot(
        bytes32 subjectId,
        uint256 snapshotIndex,
        bool endorsed,
        bytes32 evidenceHash,
        string calldata evidenceURI
    ) external returns (uint256 attestationIndex);

    function attestationCount(
        bytes32 subjectId,
        uint256 snapshotIndex
    ) external view returns (uint256);

    function getAttestation(
        bytes32 subjectId,
        uint256 snapshotIndex,
        uint256 attestationIndex
    ) external view returns (Attestation memory);
}

attestSnapshot MUST reject an unknown snapshot and a zero evidenceHash. evidenceURI MAY be empty.

The implementation MUST set attestor to msg.sender, set attestedAt to uint64(block.timestamp), append the attestation, emit SnapshotAttested, and return its zero-based per-snapshot index.

The reporter address stored on the snapshot MUST NOT attest that snapshot. This is address-level separation only; it does not establish organizational, affiliate, financial, or legal independence.

Attestations MUST be immutable and non-deletable. The same attestor MAY submit multiple attestations for the same snapshot, including a later assessment that differs from an earlier one. Consumers MUST evaluate the complete history.

Attestations MAY be added to corrected or nonterminal snapshots. Consumers MUST decide whether an attestation to an earlier snapshot applies to any correction. This ERC does not carry attestations forward automatically.

Attestor authorization and credentialing are implementation defined and MUST be documented.

attestationCount MUST return the number of attestations for the exact snapshot. getAttestation MUST return the requested record and MUST revert when the attestation index is outside that snapshot's history.

Methodology Versioning Extension

Methodology versioning is OPTIONAL. An implementation supporting it MUST implement:

interface IMethodologyVersioning {
    event MethodologySuperseded(
        bytes32 indexed subjectId,
        bytes32 indexed indicatorId,
        bytes32 oldMethodologyHash,
        bytes32 newMethodologyHash,
        uint256 effectiveFromOrdinal
    );

    function supersedeMethodology(
        bytes32 subjectId,
        bytes32 indicatorId,
        bytes32 oldMethodologyHash,
        bytes32 newMethodologyHash,
        string calldata newMethodologyURI,
        uint256 effectiveFromOrdinal
    ) external;

    function activeMethodology(
        bytes32 subjectId,
        bytes32 indicatorId
    ) external view returns (
        bytes32 methodologyHash,
        string memory methodologyURI
    );

    function pendingMethodology(
        bytes32 subjectId,
        bytes32 indicatorId
    ) external view returns (
        bytes32 newMethodologyHash,
        string memory newMethodologyURI,
        uint256 effectiveFromOrdinal,
        bool pending
    );
}

Methodology Initialization and Enforcement

The first snapshot for a subject and indicator MUST initialize its active methodology to the snapshot's methodologyHash and methodologyURI.

After initialization, each new snapshot for that subject and indicator MUST use the methodology hash active at its indicator-specific ordinal.

The snapshot's methodologyURI MUST remain nonempty, but this ERC does not require it to equal the URI returned by activeMethodology. Multiple locators can reference the same committed methodology bytes. Consumers MUST verify the retrieved bytes against the active hash.

activeMethodology MUST return the methodology required for the next snapshot. Before initialization, it MUST return bytes32(0) and an empty URI.

Methodology Supersession

supersedeMethodology MUST be restricted under a documented authorization policy and MUST revert unless:

If effectiveFromOrdinal equals the current indicator count, the new methodology becomes active immediately for the next snapshot.

If it is greater than the current count, the supersession is pending. Existing snapshots and snapshots before the effective ordinal continue to use the old methodology. The new methodology becomes active when the current indicator count reaches effectiveFromOrdinal.

Every successful supersession MUST emit MethodologySuperseded. Existing snapshot fields MUST NOT change.

Implementations MAY impose a documented maximum future lookahead.

Pending Methodology Discovery

While a future supersession has not reached its effective ordinal, pendingMethodology MUST return the scheduled hash, URI, ordinal, and pending == true.

When no supersession is scheduled, or once its effective ordinal has been reached, it MUST return bytes32(0), an empty URI, zero, and false.

Once the ordinal has been reached, activeMethodology MUST expose the new methodology even if no state-changing call has yet persisted an internal transition.

Historical values recalculated under a new methodology MUST be submitted as corrections. They MUST NOT mutate existing snapshots.

Interface Detection

Compliant core logs MUST implement ERC-165 and return true for type(IImpactSnapshotLog).interfaceId.

Implementations supporting optional extensions MUST also return true for the corresponding IImpactAttestation and IMethodologyVersioning interface IDs.

ERC-165 indicates interface support only. It does not establish measurement accuracy, methodology validity, reporter or attestor credentials, evidence quality, or independence.

Rationale

Why Append-Only Snapshots?

Impact data changes through new periods, corrected measurements, and revised methodologies. Append-only storage preserves each assertion and makes revisions visible instead of replacing history.

Why One Original Per Exact Period?

Allowing multiple unrelated originals for the same indicator and exact period would create competing current values. Requiring later revisions to use correction provenance provides one resolvable chain.

Why Permit Overlapping Periods?

Monthly, quarterly, annual, project-phase, and rolling measurements can legitimately overlap. The interface cannot determine whether an overlap is valid. Consumers aggregating values must apply methodology-specific rules to avoid double counting.

Why Use int256?

Measurements can be negative. Net emissions, energy savings relative to a baseline, or other indicators can move in either direction.

Why Both Latest and Period-Specific Queries?

The most recently recorded snapshot can concern an old period, such as a late correction. latestIndicatorSnapshot supports monitoring recording activity, while currentSnapshotForPeriod resolves the current value for an exact period.

Why Require Methodology Metadata?

A value and unit are insufficient without the process that produced them. Requiring both a commitment and a retrieval reference makes the declared methodology independently verifiable when the document remains available.

Why Methodology Versioning as an Extension?

Some deployments need only immutable per-snapshot methodology references. Others require a governed active methodology for future reports. Separating the extension allows the core log to remain usable without prescribing methodology governance.

Why Attestation as an Extension?

Not every deployment requires endorsement or dispute records. The optional interface permits independent assessment histories without making credential policy part of the core snapshot log.

Why Not Replace Earlier Attestations?

An attestor's changed view is itself relevant history. Appending the later assessment preserves both statements and lets consumers apply their own recency and credential policies.

Prior Art

ERC-7512 defines on-chain audit-report representation. This ERC defines quantitative, period-bounded indicator time series with corrections and methodology lifecycle semantics.

ERC-5851 defines on-chain verifiable credentials. Credentials can support reporter or attestor authorization but do not define this snapshot model.

Generic attestation systems can represent impact claims through custom schemas. This ERC defines a dedicated storage and query interface for indicator periods, correction chains, methodology transitions, and snapshot-specific assessment histories.

Impact-certificate systems can represent broad claims, evaluations, or funding relationships. This ERC is narrower: it standardizes subject-linked quantitative snapshots and their lifecycle rather than ownership of an impact claim.

Carbon-credit token systems represent issuance, transfer, and retirement of specific environmental assets. This ERC does not tokenize credits or prevent a reported measurement from being claimed elsewhere.

Backwards Compatibility

This ERC introduces new interfaces and does not modify existing token, registry, credential, attestation, or accounting standards.

An existing application can deploy a companion snapshot log and use its own application-defined subject namespace. No token contract changes are required unless the token itself records snapshots.

Implementations can adopt only the core interface or additionally expose either optional extension. Consumers use ERC-165 to detect supported interfaces.

Test Cases

Implementations should test at least:

Reference Implementation

A Solidity reference implementation, constants library, example integrations, unit tests, Medusa property tests, deployment scripts, and independent audit are linked from the official discussion thread.

The reference implementation:

Role assignments and the 1,000-ordinal lookahead are reference deployment choices rather than core interface requirements.

Security Considerations

Reporter Trust

The log proves that an authorized address reported a value. It does not verify the measurement, source data, calculation, unit, period boundaries, or methodology application. False data remains false when recorded immutably.

Methodology Integrity and Availability

A methodology commitment is useful only when consumers can obtain the exact document representation and reproduce its hash. A URI can disappear, change content, require authorization, or expose sensitive information. Deployments should use durable storage and document the hashing representation.

Methodology Governance

An authorized party can supersede a methodology with one that produces more favorable results. Events and pending-methodology queries make the transition visible but do not establish its legitimacy. Consumers must evaluate methodology governance independently.

Correction Authorization and Chain Length

A weak correction policy can let one reporter supersede another's measurement. Implementations must document correction authority.

Period lookup traverses correction pointers. Although chains are linear and acyclic, excessive corrections can make on-chain resolution expensive.

Attestor Credentials and Independence

Blocking the exact reporter address prevents only direct self-attestation. The same organization can control multiple addresses, and an attestor role does not prove professional qualification, financial independence, or legal authority. Consumers must evaluate credential and conflict policies.

Conflicting Attestations

The interface permits multiple endorsements and disputes without computing a consensus result. Counting addresses is not a reliable trust model because addresses are cheap and credentials can differ. Consumers need an external attestor-selection and weighting policy.

Double Counting and Overlapping Claims

Exact-period uniqueness applies only within one subject and indicator slot. Overlapping periods, related indicators, multiple subjects, and independent registries can represent the same underlying impact. Consumers must reconcile boundaries and methodologies before aggregation.

Privacy

Subjects, values, periods, methodology URIs, and attestation evidence can reveal commercial, personal, geographic, or site-sensitive information. Public-chain deployments should use opaque subject identifiers, redacted documents, and access-controlled evidence distribution where appropriate. Hashing low-entropy sensitive data without a secret salt does not provide meaningful privacy.

Unit and Decimal Handling

Consumers that ignore units, decimals, sign, or conversion rules can produce materially incorrect aggregates. Implementations should use checked arithmetic, explicit normalization, and methodology-aware conversion.

Timestamp Dependence

Completed-period validation uses block.timestamp, which block producers can influence within protocol bounds. Applications requiring exact wall-clock cutoffs must account for that uncertainty.

Storage Growth

Snapshots and attestations are append-only. Authorization, reporting-frequency policy, and operational monitoring are necessary to control storage costs and spam.

Copyright

Copyright and related rights waived via CC0.