--- eip: 8329 title: Subject-Linked Impact Snapshot Log description: Defines append-only subject-linked impact snapshots with correction provenance, methodology versioning, and attestations author: Chris Turner , David Hay (@david-hay), Reagan Simpson (@krumg111), Collins Musyimi (@Musyimi97) discussions-to: https://ethereum-magicians.org/t/erc-8329-subject-linked-impact-snapshot-log/28938 status: Last Call last-call-deadline: 2026-10-19 type: Standards Track category: ERC created: 2026-07-05 requires: 165 --- ## Abstract This Ethereum Request for Comments (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: - subject-linked, period-bounded quantitative snapshots; - signed values with explicit decimals and units; - one original snapshot per exact subject, indicator, and period; - additive, fork-free corrections; - per-indicator iteration and exact-period current-state lookup; - required methodology commitments and retrieval references; - optional append-only endorsement and dispute attestations; and - optional active and scheduled methodology version discovery. 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](https://www.rfc-editor.org/rfc/rfc2119) and [RFC 8174](https://www.rfc-editor.org/rfc/rfc8174). ### 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: ```solidity 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: ```solidity pragma solidity ^0.8.20; 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: ```text 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 International System of Units (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: ```text 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: - store the supplied snapshot fields; - set `reportedBy` to `msg.sender`; - set `reportedAt` to `uint64(block.timestamp)`; - initialize `correctedByIndex` to zero; - append the snapshot index to the indicator-specific index; - make the snapshot discoverable for its exact period; and - emit `SnapshotRecorded`. `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: - identify an earlier snapshot under the same `subjectId`; - use the same `indicatorId`, `periodStart`, and `periodEnd` as its target; - target a snapshot whose `correctedByIndex` is zero; and - be authorized under the implementation's documented correction policy. 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: ```solidity 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: ```text keccak256("ERC-8329:INDICATOR:::") ``` 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: ```solidity 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: ```solidity pragma solidity ^0.8.20; 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: ```solidity pragma solidity ^0.8.20; 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: - a methodology has already been initialized; - `oldMethodologyHash` equals the active methodology; - `newMethodologyHash` is nonzero; - `newMethodologyURI` is nonempty; - no future supersession is already pending; and - `effectiveFromOrdinal` is greater than or equal to the current `indicatorSnapshotCount`. 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](./eip-165.md) 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. ## 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: - zero-based per-subject indexing and subject isolation; - per-indicator counts, ordinals, and latest-recorded lookup; - completed, zero-length, and future period validation; - positive, zero, and negative values with varying decimals; - methodology hash and URI requirements; - one original per exact period; - overlapping but nonidentical periods; - correction bounds, authorization, period matching, and fork prevention; - multi-step correction chains and exact-period terminal lookup; - methodology initialization and active-methodology enforcement; - immediate and future methodology supersession; - pending methodology hash, URI, ordinal, and activation discovery; - pending-supersession exclusivity and implementation lookahead limits; - attestation evidence requirements and empty evidence URIs; - same-address self-attestation rejection; - repeated endorsement and dispute histories; - attestations to current and corrected snapshots; - unknown snapshot and attestation queries; and - positive and negative ERC-165 detection for every supported interface. ## 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: - uses reporter, attestor, and administrator roles; - allows a reporter to correct its own snapshot and an administrator-reporter to correct another reporter's snapshot; - rejects completed-period duplicates unless correction provenance is used; - implements all three interfaces; - blocks same-address self-attestation; and - limits scheduled methodology supersession to 1,000 future indicator ordinals. 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](../LICENSE.md).