Integración de extremo a extremo
Este tutorial recorre el flujo completo de EnergyAS con el SDK, desde instalar el paquete hasta consultar los datos que enviaste. Al final tendrás un script funcional que registra una organización, crea un proyecto, autoriza una wallet de dispositivo, envía un día de lecturas de energía y las vuelve a leer.
Esta guía usa Polygon Amoy (testnet). Todo funciona igual en el mainnet de Polygon o Celo: solo cambia el valor de network.
Requisitos previos
- Node.js 18+
- Una wallet con MATIC de la testnet Amoy (consíguelo en el faucet de Polygon Amoy)
- Una API key de The Graph (gratis en thegraph.com/studio → API Keys)
1. Instala el SDK
npm install energy-attestation-sdk2. Configura las variables de entorno
Crea un archivo .env (nunca lo subas al repositorio):
PRIVATE_KEY=0x...your_operator_wallet_private_key
DEVICE_PRIVATE_KEY=0x...your_iot_device_private_key
THEGRAPH_API_KEY=your_api_key_hereUsa wallets separadas para el operador (quien registra la organización y gestiona los proyectos) y para el dispositivo (que envía las lecturas). Así limitas el alcance del daño si alguna vez se compromete la clave de un dispositivo.
3. Registra una organización
import 'dotenv/config';
import { EnergySDK } from 'energy-attestation-sdk';
const sdk = await EnergySDK.fromPrivateKey({
network: 'amoy',
privateKey: process.env.PRIVATE_KEY,
});
console.log('Operator address:', sdk.address);
// Register a new organization
const { watcherId, txHash } = await sdk.watchers.createWatcher('Greenfield Energy');
console.log('Organization ID:', watcherId.toString());
console.log('Transaction:', txHash);
// → Organization ID: 7
// → Transaction: 0xabc...Guarda el watcherId: lo necesitarás en los siguientes pasos.
4. Crea un proyecto de energía
import { EnergyType } from 'energy-attestation-sdk';
const { projectId } = await sdk.projects.createProject(
watcherId,
'Solar Farm Alpha',
EnergyType.SOLAR_PV,
);
console.log('Project ID:', projectId.toString());
// → Project ID: 12Opcionalmente, adjunta metadatos al proyecto (ubicación, certificaciones, etc.):
await sdk.projects.setProjectMetadataURI(
projectId,
'ipfs://QmYourMetadataCID',
);Consulta Metadatos del proyecto para ver el formato JSON esperado.
5. Autoriza a un dispositivo para enviar lecturas
// The device wallet address (from DEVICE_PRIVATE_KEY)
const deviceAddress = '0xYourDeviceWalletAddress';
await sdk.attesters.addAttester(projectId, deviceAddress);
console.log('Device authorized for project', projectId.toString());Ahora la wallet del dispositivo puede enviar lecturas de este proyecto. Si la clave del dispositivo se rota o se compromete, quítala y agrega la nueva sin afectar a la organización:
await sdk.attesters.removeAttester(projectId, oldDeviceAddress);
await sdk.attesters.addAttester(projectId, newDeviceAddress);6. Envía lecturas de energía
Ahora cambia a la wallet del dispositivo y envía un día de lecturas horarias. En un sistema real, esto se ejecutaría en un dispositivo IoT o en un backend automatizado:
import { EnergySDK, Interval } from 'energy-attestation-sdk';
// Device wallet — submits readings but cannot manage the organization
const deviceSdk = await EnergySDK.fromPrivateKey({
network: 'amoy',
privateKey: process.env.DEVICE_PRIVATE_KEY,
});
// 24 hourly readings for a solar farm (Wh)
// Night hours are zero, peak is around midday
const readings = [
0n, 0n, 0n, 0n, 0n, 0n,
15_000n, 45_000n, 120_000n, 280_000n, 410_000n, 520_000n,
580_000n, 590_000n, 560_000n, 480_000n, 350_000n, 200_000n,
80_000n, 20_000n, 0n, 0n, 0n, 0n,
];
// Start of the reporting period (beginning of the day in UTC)
const fromTimestamp = Math.floor(new Date('2025-01-15T00:00:00Z').getTime() / 1000);
const { uid, txHash } = await deviceSdk.attestations.attest({
projectId: Number(projectId),
readings,
readingIntervalMinutes: Interval.Hourly,
fromTimestamp,
method: 'smart-meter-api',
metadataURI: 'ipfs://QmDailyExportHash', // optional evidence
});
console.log('Record submitted. UID:', uid);
console.log('Transaction:', txHash);
// → Record submitted. UID: 0x1234...7. Consulta los datos
Una vez confirmada la transacción, el subgraph la indexa en unos pocos minutos. Léela con EnergyQuery:
import { EnergyQuery } from 'energy-attestation-sdk';
const query = new EnergyQuery({
network: 'amoy',
apiKey: process.env.THEGRAPH_API_KEY,
});
// Check organization totals
const org = await query.getWatcher(watcherId.toString());
console.log('Organization:', org.name);
console.log('Total generated:', org.totalGeneratedWh, 'Wh');
// → Organization: Greenfield Energy
// → Total generated: 4250000 Wh
// List the project's records
const records = await query.getAttestations({
projectId: projectId.toString(),
replaced: false,
orderBy: 'fromTimestamp',
orderDirection: 'desc',
});
console.log(`${records.length} record(s) found`);
for (const r of records) {
const from = new Date(Number(r.fromTimestamp) * 1000).toISOString();
const to = new Date(Number(r.toTimestamp) * 1000).toISOString();
console.log(` ${from} → ${to}: ${r.energyWh} Wh`);
}
// Daily snapshots (for charting)
const snapshots = await query.getDailySnapshots({
projectId: projectId.toString(),
dateFrom: '2025-01-15',
dateTo: '2025-01-15',
});
for (const snap of snapshots) {
console.log(`${snap.date}: generated=${snap.generatedWh} Wh`);
}8. Corrige un registro
Si más adelante descubres que algunas lecturas eran incorrectas, envía una corrección que haga referencia al original por su UID:
// Corrected readings
const correctedReadings = [
0n, 0n, 0n, 0n, 0n, 0n,
14_000n, 43_000n, 118_000n, 275_000n, 405_000n, 515_000n,
575_000n, 585_000n, 555_000n, 475_000n, 345_000n, 195_000n,
78_000n, 18_000n, 0n, 0n, 0n, 0n,
];
const { uid: correctedUid } = await deviceSdk.attestations.overwriteAttestation({
refUID: uid, // the UID of the record being corrected
projectId: Number(projectId),
readings: correctedReadings,
readingIntervalMinutes: Interval.Hourly,
fromTimestamp,
method: 'smart-meter-api-corrected',
metadataURI: 'ipfs://QmCorrectionAuditReport',
});
console.log('Correction submitted:', correctedUid);
// The original record is now marked replaced: true
// Accumulators are updated by the delta automatically9. Reporta un periodo sin generación
Si el sitio estuvo fuera de servicio durante un periodo, igualmente debes enviar un registro para mantener intacta la cadena de atestaciones; de lo contrario, los envíos futuros se bloquearán:
const { uid: zeroUid } = await deviceSdk.attestations.attestZeroPeriod({
projectId: Number(projectId),
interval: Interval.Daily,
method: 'manual-zero-report',
metadataURI: 'ipfs://QmMaintenanceLog',
});
console.log('Zero period submitted:', zeroUid);
// Automatically continues from the last attested timestampScript completo
Los pasos anteriores se pueden combinar en un solo script. Hay una implementación de referencia en el repositorio del SDK, en examples/end-to-end.ts.
Próximos pasos
- Referencia del SDK: consulta de datos: todos los métodos de
EnergyQuery - Referencia del SDK: escritura de datos: todos los métodos de
EnergySDK - Metadatos del proyecto: formato de los metadatos y anclaje en IPFS
- Schema de atestación: schema completo y detalles de codificación