Comunidad energética blockchain: plataforma de intercambio de energía P2P

Una comunidad energética blockchain permite que productores y consumidores intercambien energía directamente mediante una infraestructura descentralizada. En este proyecto se muestra el desarrollo de una plataforma de intercambio de energía P2P en la que los smart contracts gestionan mercados, subastas, pagos y acuerdos entre los miembros de la comunidad.

El mercado eléctrico tradicional es unidireccional. La energía fluye de las grandes centrales al consumidor final, que actúa como un receptor pasivo, y ese modelo arrastra ineficiencias, pérdidas durante el transporte y costes elevados. Pero con la entrada de las renovables y la aparición de las microgrids aparece una figura nueva: el prosumer, un usuario que además de consumir, produce energía (típicamente con placas solares).

Si un vecino produce un excedente y el de al lado lo necesita, ¿por qué tiene que pasar ese intercambio por una comercializadora centralizada? Tomando como referencia experiencias reales como el Brooklyn Microgrid, este proyecto parte de esa pregunta y construye la infraestructura para responderla.

La idea es precisamente permitir que los miembros de una misma comunidad compren y vendan energía directamente entre ellos, sin una autoridad central que regule estas transacciones,. Pero quitar al intermediario abre un problema inmediato: si no hay una empresa que dé fe de que el trato se cumple, ¿quién garantiza que el productor entrega la energía y el consumidor paga lo acordado? Esa garantía, que tradicionalmente aporta una autoridad central, es justo lo que la tecnología blockchain sabe ofrecer sin necesidad de un tercero de confianza.

El Papel de la Blockchain

Para entender por qué blockchain se convierte en el jugador esencial de esta partida, conviene detenerse un momento en cómo funciona. Una blockchain es, en esencia, un libro de registros distribuido y replicado entre muchos nodos, sin un dueño único. Las transacciones se agrupan en bloques, y cada bloque queda encadenado al anterior mediante un hash criptográfico: alterar un solo dato del pasado rompería toda la cadena que viene después, lo que hace la información prácticamente inmutable. La validez de cada nueva transacción no la decide una empresa, sino el conjunto de la red mediante un mecanismo de consenso.

De esa mecánica nacen tres propiedades que encajan con el problema que planteamos. La descentralización elimina la autoridad central que queríamos quitar de en medio. La inmutabilidad asegura que, una vez registrado un acuerdo de compraventa de energía, nadie puede modificarlo a posteriori. Y la transparencia, combinada con la firma digital de cada usuario, garantiza que toda operación es auténtica y auditable, aportando autenticidad y no repudio. En conjunto, la blockchain ofrece exactamente lo que faltaba: confianza sin intermediario.

Pero la blockchain no solo registra; también puede ejecutar. Los contratos inteligentes son programas que viven en la cadena y se ejecutan de forma automática y verificable. Son la herramienta que permite codificar las reglas del mercado, las subastas y la custodia de los fondos.

El «reglamento» del sistema deja de estar en manos de una empresa y pasa a ser código que cualquiera puede inspeccionar. Por eso el proyecto se construye sobre una blockchain pública compatible con la EVM, que permite escribir esos contratos en Solidity, beneficiarse de la seguridad de una red con muchos nodos y, además, usar tokens estables para intercambiar energía por valor de forma segura.

Con la blockchain aportando esa confianza, el reto deja de ser conceptual y pasa a ser de ingeniería: cómo organizar el sistema para exprimir sus virtudes sin pagar sus costes. Ahí es donde entra la arquitectura.

Arquitectura en tres capas

Lo primero fue decidir qué parte del sistema vive on-chain y qué parte off-chain, porque meter todo en la blockchain es un error caro. El sistema se divide en tres capas.

Arquitectura propuesta para la comunidad energética blockchain: capa física, smart contracts y almacenamiento off-chain.

La capa física son los usuarios y los componentes tangibles: edificios, placas solares, lectores inteligentes (smart meters) y la propia interfaz con la que interactúan.

La capa blockchain contiene los contratos inteligentes con toda la lógica del mercado, las subastas, el token y los acuerdos. Es la fuente de verdad de las transacciones.

La capa de datos que tiene la misión de resolver dos problemas inherentes a la naturaleza de la Blockchain:

  • Privacidad. La blockchain es transparente por diseño, así que los datos personales no pueden vivir ahí. Van en una base de datos relacional (MySQL) detrás de una API REST.
  • Coste y escalabilidad. Consultar la cadena cuesta gas. Por eso la capa de datos replica off-chain los detalles de ofertas, subastas y acuerdos: cuando un usuario quiere consultar algo, se sirve de la copia local y solo se toca la blockchain cuando es estrictamente necesario.

La sincronización entre capas es dirigida por eventos: cuando un contrato emite un evento —por ejemplo, «esta subasta ya tiene ganador»—, la capa de datos lo captura, lo procesa y dispara las notificaciones hacia la interfaz.

El front-end es una DApp en React que habla con la blockchain mediante un provider (tipo Infura) y un signer (la wallet del usuario, p. ej. MetaMask) que firma cada transacción que cambia el estado de la cadena. Contra la API REST se comunica con Axios; contra los contratos, con ethers.js.

La capa Blockchain

La capa blockchain no es un contrato monolítico, sino una jerarquía de contratos en Solidity, cada uno con sus funcionalidad y misión dentro del sistema. Además la forma en la que se organizan las dependencias y el despliegue de estos contratos, es fundamental para no encontrarse con una limitación estructural de este sistema: los contratos tienen que tener un tamaño máximo de bytecode sino el sistema no deja desplegarlos( el motivo de esta limitación es para evitar ataques DoS y evitar que se consuman recursos excesivos en la red)

En la cima está System Management. Cuando el administrador lo despliega, él mismo despliega automáticamente al resto y guarda una referencia a cada uno:

constructor(uint48 initialDelay, uint64 minKwhPrice, address tokenAddressStablecoin) {
    tokenContractAddress = address(new TokenContract(tokenAddressStablecoin));
    tokenContract = TokenContract(tokenContractAddress);

    tradingMarketContractAddress = address(new ETradingMarket(initialDelay, minKwhPrice, tokenContractAddress));
    tradingMarketContract = ETradingMarket(tradingMarketContractAddress);
    tradingMarketContract.grantRole(keccak256("ADMIN_ROLE"), address(this));

    tradingAuctionContractAddress = address(new ETradingAuction(initialDelay, minKwhPrice, tokenContractAddress));
    tradingAuctionContract = ETradingAuction(tradingAuctionContractAddress);
    tradingAuctionContract.grantRole(keccak256("ADMIN_ROLE"), address(this));

    stableCoin = StableCoin(tokenAddressStablecoin);
}

De ahí cuelgan tres contratos principales: E-Trading Market (el mercado), Token ERC-20 (el token GTT) y E-Trading Auction (las subastas), que a su vez delega en dos auxiliares —Auction Management y Offer Auction Management— para repartir la carga. Y un sexto contrato, Model Agreement, que se genera cada vez que se cierra un trato y actúa como depósito en garantía.

Token para el intercambio de energía P2P: GTT

El GTT token está diseñado como una stablecoin anclada 1:1 a otra stablecoin de la red donde está deplegado el sistema, como por ejemplo Tether. Cada GTT token en circulación está respaldado por una un tether retenido en el contrato, lo que reduce la volatilidad que haría inviable un mercado energético de este tipo.

Al desplegarse , el contrato emite el suministro completo a su propia dirección

_mint(address(this), 1_000_000 * 10**18);

A partir de ahí, comprar GTT significa entregar el mismo número de tether, de manera que por cada GTT que está en circulación el sistema retiene un Tether.

function buyTokens(uint256 amount) public nonReentrant {
    require(_balanceContract() >= amount, "Shop doesn't have enough tokens");
    uint256 total_price = (amount * token_price) / 10**18;
    require(stableCoin.allowance(msg.sender, address(this)) >= total_price, "Money you are sending is not enough");
    stableCoin.transferFrom(msg.sender, address(this), amount);
    _transfer(address(this), msg.sender, amount);
    emit PurchaseToken(msg.sender, amount);
}

Un punto crítico es la invariante de solvencia. El contrato debe poder reembolsar en cualquier momento todos los GTT en circulación. Por eso, cuando el administrador retira las comisiones acumuladas, primero calcula cuánto debe a los usuarios y solo se lleva el sobrante:

function retrieveStableTokens() external onlyOwner nonReentrant {
    uint256 total_balance = stableCoin.balanceOf(address(this));
    uint256 tokens_for_users = (circulationTokens() * token_price) / 10**18;
    uint256 excess_stableCoin = total_balance - tokens_for_users;
    require(excess_stableCoin > 0, "There's not enough stablecoins");
    stableCoin.transfer(owner, excess_stableCoin);
}

E-Trading Market

El E-Trading Market opera en horario (de 8:00 a 22:00) y recibe ofertas de los productores durante el cierre nocturno. Para que la competencia sea justa, las ofertas tienen que permanecer ocultas hasta la apertura del mercado. La solución es un esquema commit-reveal: al enviar la oferta, el productor solo publica un hash de sus datos.

mapping(uint256 => bytes32) private offert_proposal;

offert_proposal[id_offer] = keccak256(
    abi.encodePacked(id_offer, price_kwh, energy_amount, msg.sender)
);

Cuando el mercado abre, el productor «revela» la oferta reenviando los datos en claro. El contrato recalcula el hash y, si coincide con el guardado, da por buena la oferta: nadie ha podido espiar las pujas ajenas ni cambiar la suya a última hora.

Un detalle técnico a destacar, es que cuando programamos código que vivirá y se ejecutará en una blockchain, la eficiencia se convierte en un punto fundamental. Borrar un elemento de una posición aleatoria del array cuesta gas ( es decir dinero). Una de las técnicas mas utilizadas es el swap-and-pop: mueves el último elemento a la posición donde está el elemento que quieres borrar y eliminas el último con pop(). Para que el coste sea constante, cada oferta guarda su propio índice y un mapping asocia el ID de oferta con su posición:

struct Offer {
    uint index;
    uint256 id_offer;
    address producer;
    uint64 price_kwh;
    uint256 energy_amount;
    bool is_available;
}

Offer[] public market_offers;
mapping(uint256 => uint) public idOffer_index;

function deleteOffer(uint256 id_offer) public {
    uint index = idOffer_index[id_offer];
    require(msg.sender == market_offers[index].producer || allowDeleted);
    require(market_offers[index].is_available);

    market_offers[index] = market_offers[market_offers.length - 1]; // swap
    market_offers[index].index = index;
    idOffer_index[market_offers[index].id_offer] = index;          // reindex
    market_offers.pop();                                            // pop
}

Subastas de energía mediante smart contracts

Si un consumidor no encuentra ofertas que le convenzan en el mercado, puede abrir una subasta: fija la energía que necesita, el precio máximo por kWh que está dispuesto a pagar y un plazo (entre 10 y 50 minutos). Los productores envían pujas y, al cerrar, se elige automáticamente la ganadora.

El criterio de selección es determinista y corre on-chain, de modo que la adjudicación es transparente y auditable:

  1. Se descartan las ofertas por encima del precio máximo.
  2. Entre las que cubren la demanda energética, gana la más barata por kWh.
  3. Si ninguna llega a cubrir la demanda, gana la que más energía aporte (con el precio como desempate).

Para que las funciones de los contratos auxiliares solo puedan invocarse desde el orquestador, el contrato de subastas es propietario de los dos auxiliares que crea en su despliegue, y valida la consistencia antes de delegar el registro

function sendOffer(uint256 id_request, uint256 idOffer, uint64 price_kwh, uint32 energy_amount) public nonReentrant {
    require(hasRole(PRODUCER_ROLE, msg.sender) || hasRole(PROSUMER_ROLE, msg.sender), "Not Allowed");
    (address consumer, uint64 max_price_kwh, , uint32 limit_time) = auction.getRequest(id_request);
    require(block.timestamp < limit_time, "The auction is closed");
    require(consumer != address(0), "Not allowed");
    require(price_kwh <= max_price_kwh, "Price not allowed");
    offerAuction.sendOffer(msg.sender, id_request, idOffer, price_kwh, energy_amount);
}

Smart contract para acuerdos y pagos: Model Agreement

Cuando se cierra un trato (por mercado o subasta) se genera un Model Agreement. Funciona como escrow: el consumidor deposita los GTT acordados en el contrato, y los lectores no pueden empezar a transferir energía hasta que los fondos están dentro. Si todo va bien y las lecturas de energía enviada y recibida coinciden, el contrato libera el pago al productor.

¿Y si no coinciden? Se activa la resolución de conflictos. En el diseño propuse dos mecanismos —uno cooperativo y otro basado en árbitros con aval (al estilo del slashing de PoS)— y en el prototipo implementé el cooperativo. Cualquiera de las dos partes puede abrir un incidente si hay discrepancia:

function reportIncident() public {
    require(msg.sender == consumer || msg.sender == producer, "Caller not authorized to report incident");
    require(!incidentActive, "An incident is already active");
    require(energyProvided != energyReceived, "No discrepancy in energy reported");

    uint256 discrepancy = abs(int256(totalEnergy) - int256(energyProvided));
    currentIncident = Incident(discrepancy, msg.sender);
    incidentActive = true;
    emit IncidentReported(msg.sender, discrepancy);
}

Y se resuelve solo cuando ambas partes confirman la misma cantidad. Hasta entonces, los fondos siguen bloqueados en el contrato:

function resolveIncident(uint256 energy_amount) public nonReentrant {
    require(incidentActive, "No active incident to resolve");
    require(msg.sender == consumer || msg.sender == producer, "Caller not authorized to resolve incident");

    if (msg.sender == consumer) {
        energyReceived = energy_amount;
        consumerConfirmed = true;
    } else {
        energyProvided = energy_amount;
        producerConfirmed = true;
    }

    if (consumerConfirmed && producerConfirmed && energyProvided == energyReceived) {
        incidentActive = false;
        producerConfirmed = false;
        consumerConfirmed = false;
        finalizeExchange();
    }
}

Conclusión

Los contratos, el token, el mercado, las subastas, la custodia de los fondos… ninguna de estas piezas es un fin en sí misma. Son los engranajes de una idea muy sencilla: que un vecino pueda venderle su excedente de energía a otro y que el trato se cumpla por sí solo, sin la necesidad de una autoridad centralizada. El proyecto entrega un prototipo funcional completo que demuestra que esa idea no es una utopía, sino algo construible con la tecnología actual.

Todo lo que tradicionalmente haría una autoridad central —llevar las cuentas, garantizar que el acuerdo se respeta, custodiar el dinero hasta que ambas partes cumplen, mediar cuando algo falla— en este sistema lo hace el código, ejecutándose de forma automática y verificable sobre una red sin dueño. La blockchain no es el adorno tecnológico del proyecto; es la pieza que permite quitar al intermediario sin perder la confianza que ese intermediario aportaba. Inmutabilidad para que un acuerdo registrado no pueda alterarse, transparencia para que cualquiera pueda auditarlo, y firma digital para que cada operación sea auténtica e innegable.

El consumidor deja de ser el último eslabón pasivo de una cadena que va en una sola dirección y pasa a ser un actor con voz: alguien que produce, que decide, que negocia y que comercia su energía en igualdad de condiciones con sus vecinos.

En definitiva el proyecto es una muestra de que la tecnología actual, habilita el camino hacia una soberanía energética real, donde la comunidad gestiona sus propios recursos en lugar de depender por completo de un tercero.

Detalles del proyecto

SolidityEthereumHardHat
Mateu Joan Perelló
Resumen de privacidad

Esta web utiliza cookies para que podamos ofrecerte la mejor experiencia de usuario posible. La información de las cookies se almacena en tu navegador y realiza funciones tales como reconocerte cuando vuelves a nuestra web o ayudar a nuestro equipo a comprender qué secciones de la web encuentras más interesantes y útiles.