Documentation

Querying Energy Data

All EnergyAS data is public and queryable by anyone — no access controls, no registration required. This guide covers the three ways to query data, from simplest to most low-level.

ApproachBest for
SDK (EnergyQuery)Applications and scripts — typed methods, no GraphQL required
Subgraph (GraphQL)Custom queries, dashboards, analytics
Direct contract callsOn-chain integrations, auditing specific values

Using the SDK

The EnergyQuery class from the SDK is the easiest way to fetch data. It handles authentication, request construction, and response parsing for you. See the full SDK Reference for setup and all available methods.

JavaScript
import { EnergyQuery } from 'energy-attestation-sdk';

const query = new EnergyQuery({
  network: 'polygon',
  apiKey: 'YOUR_THEGRAPH_API_KEY',
});

Common queries

JavaScript
// Global protocol stats
const protocol = await query.getProtocol();
console.log(`${protocol.totalProjects} projects, ${protocol.totalGeneratedWh} Wh generated`);

// List organizations sorted by generation
const orgs = await query.getWatchers({
  orderBy: 'totalGeneratedWh',
  orderDirection: 'desc',
  first: 10,
});

// Get an organization with all its projects
const org = await query.getWatcher('1');
console.log(org.projects);

// Get energy records for a project (most recent first)
const records = await query.getAttestations({
  projectId: '5',
  replaced: false,
  orderBy: 'fromTimestamp',
  orderDirection: 'desc',
});

// Daily snapshots for a chart
const snapshots = await query.getDailySnapshots({
  projectId: '5',
  dateFrom: '2025-01-01',
  dateTo: '2025-01-31',
});

For the full method reference — including filtering, pagination, and all return types — see SDK Reference: Querying Data.


Using the Subgraph Directly

If you need custom GraphQL queries beyond what the SDK exposes, you can query the subgraph directly. The subgraph indexes all EnergyRegistry events into a queryable GraphQL API.

Endpoint

JavaScript
const SUBGRAPH_URL = 'https://gateway.thegraph.com/api/subgraphs/id/<SUBGRAPH_ID>';

async function querySubgraph(query, variables = {}) {
  const response = await fetch(SUBGRAPH_URL, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': 'Bearer YOUR_API_KEY',
    },
    body: JSON.stringify({ query, variables }),
  });
  const { data } = await response.json();
  return data;
}

Subgraph IDs by network:

NetworkSubgraph ID
Polygon Amoy (testnet)33b8nJcqxLyH96eSyyHC9vsdvHifUXejLzMEtqLeySBC
PolygonD8AgWoxUr3aDgWQCEy2hVeU8hnsrs5N3vmPSeGMphgEi
Celo9BM6kQzg7jtcbnphW7UXCGMmvJ2QtDkxnU8fqDYinUx1

Indexed entities

EntityID formatDescription
Protocol"protocol"Singleton with global counters
Resolverresolver addressAuthorization status of resolver contracts
EnergyTypeuint8 as string (e.g. "1")Energy source types with total generated
WatcherwatcherId as string (e.g. "1")Monitoring organizations
WatcherOwnershipTransfertxHash-logIndexImmutable audit trail for ownership changes
ProjectprojectId as string (e.g. "1")Individual energy sites
EnergyAttestationEAS uid (bytes32 hex)Individual records with readings array
ProjectAttesterprojectId-addressPer-project submitter whitelist entries
WatcherAttesterwatcherId-addressOrganization-wide submitter whitelist entries
DailyEnergySnapshotprojectId-YYYY-MM-DDDaily aggregates per project for charts

Example queries

Protocol stats

{
  protocol(id: "protocol") {
    totalAttestations
    totalGeneratedWh
    totalConsumedWh
    totalWatchers
    totalProjects
  }
}

List organizations

{
  watchers(
    first: 10
    orderBy: totalGeneratedWh
    orderDirection: desc
  ) {
    id
    name
    owner
    totalGeneratedWh
    totalConsumedWh
    projectCount
  }
}

Organization detail with projects

{
  watcher(id: "1") {
    id
    name
    owner
    totalGeneratedWh
    projects {
      id
      name
      energyType { id name }
      totalGeneratedWh
      attestationCount
    }
    watcherAttesters {
      attester
      active
    }
  }
}

Project records (non-replaced only)

{
  energyAttestations(
    first: 50
    orderBy: fromTimestamp
    orderDirection: desc
    where: { project: "1", replaced: false }
  ) {
    id
    energyWh
    readings
    fromTimestamp
    toTimestamp
    attester
    metadataURI
    txHash
  }
}

Replacement chain — follow corrections

When a record is corrected, the original is marked replaced: true and links to the new version:

{
  energyAttestation(id: "0xoriginaluid...") {
    id
    replaced
    replacedBy {
      id
      energyWh
      readings
    }
  }
}

Daily energy snapshots (time series)

{
  dailyEnergySnapshots(
    first: 30
    orderBy: timestamp
    orderDirection: desc
    where: { project: "1" }
  ) {
    date
    generatedWh
    consumedWh
    attestationCount
  }
}

Energy distribution by type

{
  energyTypes(
    orderBy: totalGeneratedWh
    orderDirection: desc
    where: { registered: true }
  ) {
    id
    name
    totalGeneratedWh
  }
}

Pagination

# Page 1
{ watchers(first: 10, skip: 0) { id name } }

# Page 2
{ watchers(first: 10, skip: 10) { id name } }

The Graph caps first at 1000. Use skip to page through larger result sets.

Tips

  • Entity IDs are strings. Query watcher(id: "1"), not watcher(id: 1).
  • Energy type is a relation. Query energyType { id name }, not a scalar.
  • Consumers have null energyType. Filter generators with where: { energyType_not: null }.
  • Timestamps are BigInt strings. The Graph handles ordering correctly when you sort them as strings.
  • Use replaced: false to exclude corrected records and show only the current version.

Direct Contract Calls

For on-chain integrations or when you need to read values directly from the contract, you can call the EnergyRegistry functions via ethers, viem, or any web3 library.

JavaScript
const watcher = await registry.getWatcher(watcherId);
console.log(watcher.name, watcher.owner);

const projects = await registry.getWatcherProjects(watcherId);

Energy totals

JavaScript
const generated = await registry.getTotalGeneratedEnergy(projectId);
const consumed = await registry.getTotalConsumedEnergy(projectId);
console.log(`Generated: ${generated} Wh, Consumed: ${consumed} Wh`);

// Watcher-level totals
const totalGen = await registry.getTotalGeneratedEnergyByWatcher(watcherId);
const totalCon = await registry.getTotalConsumedEnergyByWatcher(watcherId);

Check if a period has been attested

JavaScript
import { ZeroHash } from 'ethers';

const uid = await registry.getAttestedPeriodUID(projectId, fromTimestamp, toTimestamp);
const isAttested = uid !== ZeroHash;

Follow the replacement chain

JavaScript
// Get the timestamp after the last attested period
const lastTs = await registry.getProjectLastTimestamp(projectId);

// Walk the replacement chain for an attestation
let currentUid = originalUid;
let replacementUid = await registry.getReplacementUID(currentUid);
while (replacementUid !== ZeroHash) {
  currentUid = replacementUid;
  replacementUid = await registry.getReplacementUID(currentUid);
}
// currentUid is now the latest version of the record

Check submitter authorization

JavaScript
const isAuthorized =
  await registry.isProjectAttester(projectId, wallet) ||
  await registry.isWatcherAttester(watcherId, wallet);

Event indexing

If you're building your own indexer rather than using the subgraph, the key events are:

  • EnergyAttested — every new record, with full readings array
  • EnergyReplaced — every correction, with old and new energy values
  • ProjectRegistered / ProjectDeregistered — project lifecycle
  • WatcherRegistered — new organization onboarding
  • WatcherAttesterAdded / WatcherAttesterRemoved — authorization changes

See the Events reference for full parameter signatures.