Documentation

EnergyQuery — Reading Data

EnergyQuery is a typed client for the EnergyAS subgraph. It handles authentication, GraphQL request construction, and response parsing for you. No wallet or private key required.

Setup

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

const query = new EnergyQuery({
  network: 'polygon',       // 'amoy' | 'polygon' | 'celo'
  apiKey: 'YOUR_API_KEY',   // The Graph API key — get one at thegraph.com/studio
});

Config options:

OptionTypeDescription
networkstringNetwork to query. Must have a deployed subgraph.
apiKeystringThe Graph API key (used in the Authorization header).
subgraphUrlstringOverride the subgraph URL directly. Use this for local development or custom deployments instead of apiKey.

You need either apiKey (for the standard subgraph endpoints) or subgraphUrl (for a custom or self-hosted endpoint). Providing both uses subgraphUrl and ignores apiKey.

Protocol Stats

Get global totals across all networks:

JavaScript
const protocol = await query.getProtocol();

console.log(protocol.totalWatchers);      // number of registered organizations
console.log(protocol.totalProjects);      // number of energy sites
console.log(protocol.totalAttestations);  // total records submitted
console.log(protocol.totalGeneratedWh);   // cumulative generated energy (Wh)
console.log(protocol.totalConsumedWh);    // cumulative consumed energy (Wh)

Energy Types

JavaScript
const types = await query.getEnergyTypes();

for (const t of types) {
  console.log(t.id, t.name, t.totalGeneratedWh);
}
// e.g. "1" "Solar PV" "42000000"

Organizations (Watchers)

List all organizations

JavaScript
const watchers = await query.getWatchers({
  first: 20,
  skip: 0,
  orderBy: 'totalGeneratedWh',
  orderDirection: 'desc',
  registered: true,            // optional: filter active orgs only
});

for (const w of watchers) {
  console.log(w.id, w.name, w.owner, w.projectCount);
}

Get a single organization with full detail

JavaScript
const watcher = await query.getWatcher('1');  // pass watcherId as string

if (watcher) {
  console.log(watcher.name);
  console.log(watcher.projects);          // list of projects
  console.log(watcher.watcherAttesters);  // authorized submitters
  console.log(watcher.ownershipHistory);  // ownership transfer log
}

Filter by owner address

JavaScript
const myOrgs = await query.getWatchers({
  owner: '0xYourWalletAddress',
});

Ownership history audit trail

JavaScript
const history = await query.getWatcherOwnershipHistory('1');

for (const transfer of history) {
  console.log(transfer.previousOwner, '→', transfer.newOwner, 'at block', transfer.blockNumber);
}

Projects

List projects

JavaScript
const projects = await query.getProjects({
  first: 50,
  orderBy: 'totalGeneratedWh',
  orderDirection: 'desc',
  watcherId: '1',       // optional: filter by organization
  energyTypeId: '1',    // optional: '1' = Solar PV, '0' = Consumer, etc.
  registered: true,
});

Get a single project

JavaScript
const project = await query.getProject('5');

if (project) {
  console.log(project.name);
  console.log(project.energyType?.name);   // null for consumers
  console.log(project.totalGeneratedWh);
  console.log(project.attestationCount);
  console.log(project.metadataURI);        // link to metadata (IPFS or HTTPS)
  console.log(project.lastToTimestamp);    // end of the last attested period
}
Note

Consumers vs generators: Consumer projects have energyType: null. To filter generators only, use energyTypeId with a non-zero type ID. To get consumers only, omit energyTypeId and filter client-side.

Energy Records (Attestations)

List records for a project

JavaScript
const records = await query.getAttestations({
  projectId: '5',
  replaced: false,              // exclude corrected records
  orderBy: 'fromTimestamp',
  orderDirection: 'desc',
  first: 50,
});

for (const r of records) {
  console.log(r.fromTimestamp, r.toTimestamp, r.energyWh);
  console.log(r.readings);     // array of per-interval Wh values (as strings)
  console.log(r.attester);     // address that submitted the record
  console.log(r.txHash);       // transaction hash
}

Get a single record by UID

JavaScript
const record = await query.getAttestation('0xabcd...');

if (record) {
  console.log(record.replaced);          // true if this record was corrected
  console.log(record.replacedBy?.id);    // UID of the correction
  console.log(record.replaces?.id);      // UID of the original this corrected
}

Filter by date range

JavaScript
const startOfMonth = Math.floor(new Date('2025-01-01').getTime() / 1000).toString();
const endOfMonth = Math.floor(new Date('2025-02-01').getTime() / 1000).toString();

const records = await query.getAttestations({
  projectId: '5',
  fromTimestamp_gte: startOfMonth,
  fromTimestamp_lte: endOfMonth,
  replaced: false,
});

Daily Snapshots (Time Series)

The subgraph pre-aggregates energy data into daily buckets — the fastest way to build charts:

JavaScript
const snapshots = await query.getDailySnapshots({
  projectId: '5',
  dateFrom: '2025-01-01',   // YYYY-MM-DD (inclusive)
  dateTo: '2025-01-31',     // YYYY-MM-DD (inclusive)
  orderDirection: 'asc',
  first: 365,
});

for (const day of snapshots) {
  console.log(day.date, day.generatedWh, day.consumedWh, day.attestationCount);
}

Authorized Submitters

Project-level submitters

JavaScript
const submitters = await query.getProjectAttesters('5', { active: true });

for (const s of submitters) {
  console.log(s.attester, s.active, s.addedAt);
}

Organization-wide submitters

JavaScript
const submitters = await query.getWatcherAttesters('1', { active: true });

Pagination

All list methods accept first and skip. The subgraph caps first at 1000:

JavaScript
// Page through all projects
let skip = 0;
let all = [];
while (true) {
  const page = await query.getProjects({ first: 1000, skip });
  all = all.concat(page);
  if (page.length < 1000) break;
  skip += 1000;
}