EnergySDK: escritura de datos
EnergySDK se encarga de todas las operaciones de escritura on-chain: registrar organizaciones y proyectos, gestionar remitentes autorizados y enviar registros de energía. Requiere una wallet firmante y usa ethers v6 internamente.
Configuración
Hay dos formas de crear una instancia del SDK, según tu entorno.
Desde una clave privada (Node.js / IoT / backend)
import { EnergySDK } from 'energy-attestation-sdk';
const sdk = await EnergySDK.fromPrivateKey({
network: 'polygon',
privateKey: process.env.PRIVATE_KEY,
// rpcUrl: 'https://...', // optional — uses the network default if omitted
});
console.log('Signing as:', sdk.address);Desde una wallet del navegador (MetaMask / WalletConnect)
import { BrowserProvider } from 'ethers';
import { EnergySDK } from 'energy-attestation-sdk';
const provider = new BrowserProvider(window.ethereum);
const signer = await provider.getSigner();
const sdk = await EnergySDK.fromSigner({
network: 'polygon',
signer,
});Ambos métodos de fábrica validan el ID de cadena conectado contra la red especificada y lanzan un ConfigurationError si no coinciden.
Organizaciones (watchers)
Registrar una nueva organización
const { watcherId, txHash } = await sdk.watchers.createWatcher('Greenfield Energy');
console.log('Organization ID:', watcherId.toString()); // e.g. 7n
console.log('Transaction:', txHash);La wallet que hace esta llamada se convierte en la propietaria de la organización. El propietario puede autorizar remitentes, crear proyectos y transferir la propiedad.
Transferir la propiedad de la organización
await sdk.watchers.transferWatcherOwnership(watcherId, '0xNewOwnerAddress');Proyectos
Registrar un proyecto
import { EnergyType } from 'energy-attestation-sdk';
const { projectId, txHash } = await sdk.projects.createProject(
watcherId, // organization ID
'Solar Farm Alpha', // display name
EnergyType.SOLAR_PV, // energy type (or pass the numeric value directly)
);
console.log('Project ID:', projectId.toString());Valores del enum EnergyType:
| Enum | Valor | Descripción |
|---|---|---|
EnergyType.CONSUMER | 0 | Sitio consumidor (centro de datos, flota de vehículos eléctricos, etc.) |
EnergyType.SOLAR_PV | 1 | Solar fotovoltaica |
EnergyType.WIND_ONSHORE | 2 | Eólica terrestre |
EnergyType.WIND_OFFSHORE | 3 | Eólica marina |
EnergyType.HYDRO | 4 | Hidroeléctrica |
EnergyType.BIOMASS | 5 | Biomasa |
EnergyType.GEOTHERMAL | 6 | Geotérmica |
EnergyType.OCEAN_TIDAL | 7 | Oceánica / mareomotriz |
EnergyType.NUCLEAR | 8 | Nuclear |
EnergyType.NATURAL_GAS | 9 | Gas natural |
EnergyType.COAL | 10 | Carbón |
EnergyType.OIL | 11 | Petróleo |
EnergyType.STORAGE_DISCHARGE | 12 | Descarga de almacenamiento en baterías |
EnergyType.HYDROGEN_FUEL_CELL | 13 | Celda de combustible de hidrógeno |
El tipo de energía es permanente. No puedes cambiar el tipo de un proyecto después de registrarlo. Esto evita la clasificación errónea de datos de generación y consumo.
Adjuntar metadata a un proyecto
await sdk.projects.setProjectMetadataURI(
projectId,
'ipfs://QmYourMetadataCID', // or any HTTPS URL
);Consulta Metadata del proyecto para ver el formato JSON esperado.
Dar de baja un proyecto
Dar de baja un proyecto bloquea nuevos envíos, pero conserva de forma permanente todos los registros históricos:
await sdk.projects.deregisterProject(projectId);Transferir un proyecto a otra organización
await sdk.projects.transferProject(projectId, targetWatcherId);Remitentes autorizados (atestadores)
Debes autorizar al menos una wallet antes de poder enviar registros de energía para un proyecto.
Autorizar a un remitente para un proyecto específico
await sdk.attesters.addAttester(projectId, '0xDeviceWalletAddress');Autorizar a varios remitentes a la vez
await sdk.attesters.addAttesters(projectId, [
'0xDevice1Address',
'0xDevice2Address',
]);Autorizar a un remitente en todos los proyectos de una organización
Útil para operadores de respaldo o auditores manuales:
await sdk.attesters.addWatcherAttester(watcherId, '0xOperatorAddress');Quitar la autorización
await sdk.attesters.removeAttester(projectId, '0xDeviceWalletAddress');
await sdk.attesters.removeWatcherAttester(watcherId, '0xOperatorAddress');Envío de registros de energía
Los registros de energía (atestaciones) son la unidad de datos central de EnergyAS. Cada registro cubre un período de tiempo y contiene una lectura por intervalo.
Enviar un registro individual
const { uid, txHash } = await sdk.attestations.attest({
projectId: 1,
readings: [500_000n, 510_000n, 495_000n], // Wh per interval, as BigInt
readingIntervalMinutes: 60, // interval length in minutes
fromTimestamp: 1735689600, // Unix timestamp (start of first interval)
method: 'smart-meter-api', // collection method label
metadataURI: 'ipfs://QmMetadataCID', // optional — attach evidence
});
console.log('Record UID:', uid);
console.log('Transaction:', txHash);La longitud del array readings y readingIntervalMinutes definen juntos el período cubierto. Tres lecturas de 60 minutos que empiezan en fromTimestamp cubren una ventana de 3 horas. El contrato impide enviar un período que se solape con cualquier registro existente del mismo proyecto.
Todos los campos:
| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
projectId | number | bigint | Sí | ID del proyecto |
readings | bigint[] | Sí | Wh por intervalo. Usa 0n para períodos sin generación |
readingIntervalMinutes | number | Sí | Minutos por lectura (p. ej., 15, 30, 60) |
fromTimestamp | number | Sí | Timestamp Unix del inicio del primer intervalo |
method | string | Sí | Etiqueta de texto libre para el método de recolección |
metadataURI | string | No | URI que apunta a documentación de respaldo |
Enviar varios registros en una transacción
Los envíos por lotes consumen menos gas cuando tienes varios períodos que reportar:
const { uids, txHash } = await sdk.attestations.attestBatch([
{
projectId: 1,
readings: [500_000n, 510_000n],
readingIntervalMinutes: 60,
fromTimestamp: 1735689600,
method: 'smart-meter-api',
},
{
projectId: 1,
readings: [490_000n, 505_000n],
readingIntervalMinutes: 60,
fromTimestamp: 1735696800,
method: 'smart-meter-api',
},
]);
console.log('Submitted UIDs:', uids);Corregir un registro existente
Si un registro contenía un error, envía un reemplazo que haga referencia al original mediante refUID. El original se marca como reemplazado y se excluye de los totales; en su lugar entra en vigor el nuevo registro:
const { uid } = await sdk.attestations.overwriteAttestation({
refUID: '0xOriginalRecordUID', // the record being corrected
projectId: 1,
readings: [512_000n, 508_000n, 499_000n], // corrected values
readingIntervalMinutes: 60,
fromTimestamp: 1735689600,
method: 'smart-meter-api-corrected',
metadataURI: 'ipfs://QmCorrectionAuditReport',
});Nota: el contrato bloquea la revocación directa. Las correcciones siempre deben hacerse mediante overwriteAttestation, que conserva un registro de auditoría completo.
Reportar un período sin generación
Si un sitio estuvo fuera de línea o no generó durante un período, usa attestZeroPeriod. Continúa automáticamente desde el último timestamp atestado:
const { uid } = await sdk.attestations.attestZeroPeriod({
projectId: 1,
interval: 60, // Interval enum value or minutes
method: 'manual-zero-report',
metadataURI: 'ipfs://QmMaintenanceLog', // optional
});Esto equivale a llamar a attest con readings: [0n] a partir de getProjectLastTimestamp().
Estimación de gas
Cada método de escritura tiene un método estimate*Gas correspondiente para verificaciones previas:
const gas = await sdk.attestations.estimateAttestGas(params);
console.log('Estimated gas:', gas.toString());
const gas2 = await sdk.watchers.estimateCreateWatcherGas('My Org');
const gas3 = await sdk.projects.estimateCreateProjectGas(watcherId, 'Site A', EnergyType.SOLAR_PV);