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.
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.
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.
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.
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.
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);
}
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.
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.
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:
reportedBy to msg.sender;reportedAt to uint64(block.timestamp);correctedByIndex to zero;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.
An original snapshot MUST use correctsIndex == NO_CORRECTION.
A correction MUST:
subjectId;indicatorId, periodStart, and periodEnd as its target;correctedByIndex is zero; andWhen 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.
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.
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.
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 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 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
);
}
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.
supersedeMethodology MUST be restricted under a documented authorization
policy and MUST revert unless:
oldMethodologyHash equals the active methodology;newMethodologyHash is nonzero;newMethodologyURI is nonempty;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.
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.
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.
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.
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.
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.
int256?Measurements can be negative. Net emissions, energy savings relative to a baseline, or other indicators can move in either direction.
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.
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.
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.
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.
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.
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.
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.
Implementations should test at least:
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
Snapshots and attestations are append-only. Authorization, reporting-frequency policy, and operational monitoring are necessary to control storage costs and spam.
Copyright and related rights waived via CC0.