Attestation Schema
Every energy attestation follows a single schema registered on the EAS SchemaRegistry. The schema defines the data structure that all attestations must conform to.
Schema Definition
uint64 projectId, uint32 readingCount, uint32 readingIntervalMinutes, uint256[] readings, uint64 fromTimestamp, string method, string metadataURIField Reference
| Field | Type | Description |
|---|---|---|
| projectId | uint64 | ID of the registered energy project |
| readingCount | uint32 | Number of interval readings (must equal readings.length) |
| readingIntervalMinutes | uint32 | Duration of each interval in minutes |
| readings | uint256[] | Per-interval energy in Wh (array length must match readingCount) |
| fromTimestamp | uint64 | Start of the reporting period (Unix seconds) |
| method | string | Collection method: "manual", "iot", "estimated", etc. |
| metadataURI | string | Optional URI to supporting evidence (IPFS or HTTPS) |
The energy type is stored per-project in the registry (set at registration time), not per-attestation. The resolver reads the project's energy type from the registry during validation.
Derived Values
The resolver computes two values from the attestation data. These are not stored in the attestation itself but are used by the registry:
toTimestamp = fromTimestamp + readingCount * readingIntervalMinutes * 60
energyWh = sum(readings)ABI Encoding
Attestation data is ABI-encoded before submission to EAS:
import { AbiCoder } from 'ethers';
const data = AbiCoder.defaultAbiCoder().encode(
[
"uint64", // projectId
"uint32", // readingCount
"uint32", // readingIntervalMinutes
"uint256[]", // readings
"uint64", // fromTimestamp
"string", // method
"string" // metadataURI
],
[
1, // projectId
3, // 3 readings
60, // 60-minute intervals
[100000, 150000, 200000], // readings in Wh
1700000000, // fromTimestamp
"iot", // method
"" // metadataURI (optional)
]
);Energy Units
All energy values are in watt-hours (Wh), not kWh or MWh. This avoids floating-point precision issues on-chain.
| Unit | Wh Value | Example |
|---|---|---|
| 1 Wh | 1 | Single LED bulb for one hour |
| 1 kWh | 1,000 | Typical household hourly consumption |
| 1 MWh | 1,000,000 | Small commercial facility daily |
| 1 GWh | 1,000,000,000 | Utility-scale solar farm monthly |
Period Locking
The registry enforces two uniqueness constraints per project:
| Constraint | Key | Purpose |
|---|---|---|
| Period uniqueness | (projectId, fromTimestamp, toTimestamp) | Prevents the same time window from being attested twice |
| Start-time uniqueness | (projectId, fromTimestamp) | Prevents two attestations with the same start, even with different durations |
Linear Chain Enforcement
Attestations for a project must form a linear chain — each new attestation's fromTimestamp must equal the previous attestation's toTimestamp. This prevents gaps or overlaps in the reporting timeline.
If a project does not generate or consume energy during a given period, a zero-energy attestation must still be submitted (with all readings set to 0) to maintain the chain. Skipping a period would break the linear sequence and block all future attestations for that project.
If the energy data for a period is temporarily unavailable (e.g. meter outage, delayed data export), submit a zero-energy attestation as a placeholder to keep the chain moving. Once the actual data is available, replace the zero attestation with the correct readings using the replacement mechanism.
Corrections via Replacement
To correct an attestation, submit a replacement attestation by setting the refUID field to the original attestation's UID. The resolver validates that the replacement covers the same project and time period, then the registry records the replacement and adjusts energy accumulators by the delta. Direct revocations are blocked.