P2P Enerji Ticareti için Blockchain: Akıllı Sözleşmeler ve Kısıtlamalar
2025'te İtalya'nın ötesinde sayılıyor 850 Yenilenebilir Enerji Topluluğu (CER) kuruldu ve daha fazlası 199/2021 sayılı Kanun Hükmünde Kararnamenin teşvikleri ve tahsis edilen 2,2 milyar avro sayesinde planlama aşamasında 3.500 PNRR'den. Ancak mevcut teşvik modellerinin hiçbirinin tam olarak çözemediği bir sorun var: CER'ler içindeki enerji yerleşimi hala merkezi aracılar aracılığıyla gerçekleşmektedir. i için ekonomik faydayı önemli ölçüde azaltan günlük gecikmeler ve işlem maliyetleri Üreten tüketici.
New York'un Brooklyn Mikro Şebekesinden deneysel CER'lere kadar küresel olarak ortaya çıkan çözüm Almanya ve Hollanda'da ve blockchain tabanlı eşler arası enerji ticareti. Fikir şu Sadeliğiyle zarif: Fazla fotovoltaik panele sahip olan bir tüketici, fotovoltaik panellerini doğrudan satabilir. enerjisini, herhangi bir aracıya başvurmadan, yerleşimle ihtiyacı olan komşuya ulaştırıyor. Solidity akıllı sözleşmesiyle otomatik olarak yönetilir ve enerji tokenleriyle anında ödeme yapılır.
Enerji sektöründeki blockchain pazarı buna değer 2025'te 5,1 milyar dolar ve ulaşılması öngörülüyor 2035'e kadar 154,7 milyar %40,9'luk bir CAGR ile. P2P enerji ticareti, DER'lerin yaygınlaşmasıyla en hızlı büyüyen segmenti temsil ediyor (Dağıtılmış Enerji Kaynakları), üye devletlerin %42,5 yenilenebilir enerji kullanmasını gerektiren RED III direktifinden 2030 yılına kadar ve gaz maliyetlerini azaltan Katman 2 blockchain platformlarının olgunlaşması Ethereum ana ağına kıyasla %99 oranında.
Bu makalede, blockchain üzerinde baştan itibaren eksiksiz bir P2P enerji ticaret sistemi kuruyoruz: verin Enerji piyasası için sağlam akıllı sözleşmelerden akıllı sayaç verileri için oracle'a kadar ağ kısıtlamalarını yönetmek için DSO (Dağıtım Sistemi Operatörü) API'leriyle entegrasyon. Ayrıca düzenleyici çerçeveyi (Temiz Enerji Paketi, RED III, 199/2021 sayılı Kanun Hükmünde Kararname) ve bunların sonuçlarını da ele alıyoruz. Zincir içi tüketici verileri için GDPR.
Bu Makalede Neler Öğreneceksiniz?
- Blockchain üzerinde P2P enerji pazarının eksiksiz mimarisi
- Sağlamlık akıllı sözleşmeleri: EnergyToken (ERC-20), OrderBook, EnergyMarketplace, Settlement
- Enerji blockchain karşılaştırması: Ethereum L2 (Polygon, Arbitrum), Energy Web Chain, Hyperledger
- Oracle tasarımı: Şebeke fiyatlandırması için Chainlink, DLMS/COSEM akıllı sayaç verileri için özel oracle
- Tokenomik: enerji tokenleri, karbon kredileri, üreten tüketici teşvikleri
- Akıllı sayaç entegrasyonu: DLMS/COSEM protokolleri, IEC 62056, zincir üstü besleme
- Ağ kısıtlamaları yönetimi: kapasite sınırları, tıkanıklık yönetimi, DSO API
- AB düzenlemeleri: Temiz Enerji Paketi, RED III, İtalyan CER'leri
- Tasarım gereği gizlilik: GDPR uyumluluğu, tüketici verileri için sıfır bilgi kanıtları
- Hardhat ile test etme: Pazar için eksiksiz test paketi
- Örnek olay: Brooklyn Microgrid, Energy Web Foundation, İtalyan CER
EnergyTech Serisi - 10 Makale
| # | Öğe | Durum |
|---|---|---|
| 1 | Akıllı Şebeke ve Nesnelerin İnterneti: Geleceğin Elektrik Şebekesinin Mimarisi | Yayınlandı |
| 2 | DERMS Mimarisi: Milyonlarca Dağıtılmış Kaynağı Bir araya Getirme | Yayınlandı |
| 3 | Pil Yönetim Sistemi: BESS için Kontrol Algoritmaları | Yayınlandı |
| 4 | Python ve Pandapower ile Elektrik Şebekesinin Dijital İkizi | Yayınlandı |
| 5 | Yenilenebilir Enerji Tahmini: PV ve Rüzgar için ML | Yayınlandı |
| 6 | EV Yük Dengeleme: OCPP ile V2G ve Akıllı Şarj | Yayınlandı |
| 7 | Gerçek Zamanlı Enerji Telemetrisi için MQTT ve InfluxDB | Yayınlandı |
| 8 | IEC 61850: Elektrik Trafo Merkezinde İletişim | Yayınlandı |
| 9 | Karbon Muhasebe Yazılımı: Emisyonların Ölçülmesi ve Azaltılması | Yayınlandı |
| 10 | P2P Enerji Ticareti için Blockchain: Akıllı Sözleşmeler ve Kısıtlamalar (şu anda buradasınız) | Akım |
Blockchain Neden Gerçek Bir Enerji Sorununu Çözüyor?
P2P enerji ticaretinde blockchain'in değerini anlamak için öncelikle bugün nasıl çalıştığını anlamalısınız. Enerji topluluklarında enerji piyasası ve merkezi modelin neden sınırlamaları var? Üstesinden gelinmesi zor yapısal sorunlar.
Merkezi Yerleşim Sorunu
Tipik bir İtalyan CER'sinde değer akışı şu şekilde çalışır: Üreten tüketici kendi enerjisiyle enerji üretir. fotovoltaik sistem, fazla enerji şebekeye beslenir, GSE paylaşılan enerjiyi ölçer ana kabinde ve 3-6 ay sonra yaklaşık olarak eşit bir teşvik 110 Avro/MWh paylaşılan enerji (premium oran) artı faturanızda tasarruf. Anlaşma aylık veya üç aylık olup, GSE tarafından CACER portalı aracılığıyla yönetilmektedir.
Bu modelin avantajları (basitlik, kurumsal destek) var ama aynı zamanda kritik sınırlamaları da var. Dinamik P2P pazarı:
- Ödeme gecikmesi: Saniyeler yerine günler veya haftalar
- Sabit fiyat: yerel arz/talebe dayalı dinamik fiyat keşfi imkanı yok
- Sınırlı zamansal ayrıntı düzeyi: aylık ödeme ve PV'nin saatlik veya saat altı değişkenliği
- Çoklu aracılar: GSE, distribütör (e-dağıtım), kamu hizmetleri perakendecisi, her birinin kendi maliyetleri vardır
- Şeffaflık eksikliği: Üreten tüketiciler değerin nasıl dağıtıldığını gerçek zamanlı olarak göremiyor
Blockchain Neler Ekler?
Blockchain üzerindeki bir P2P platformu, GSE düzenleyici çerçevesinin yerini almaz - en azından kısa vadeli - ancak şeffaf, otomatik ve yerleşim katmanı ekleyerek onu tamamlar işlemler için neredeyse gerçek zamanlı dahili topluluğa. Somut faydalar şunlardır:
Uluslararası Pilotlarda Ölçülen Faydalar
- İşlem maliyetlerinin azaltılması: Geleneksel aracılara kıyasla -%60-80 (kaynak: Energy Web Foundation, 2024)
- Yerleşim hızı: günlerden saniyelere (neredeyse anında kesinliğe sahip L2 blockchain)
- Denetim şeffaflığı: her işlem zincir üzerinde doğrulanabilir, değişmez
- Yerel fiyat keşfi: P2P fiyatlandırması CER'deki gerçek zamanlı arz/talebi yansıtır
- Otomasyon: Mutabakat, mutabakat ve ödeme için sıfır manuel işlem
- Birlikte çalışabilirlik: Değiştirilebilir enerji tokenleri için açık standartlar (ERC-20, ERC-1155)
P2P Enerji Ticaret Sisteminin Mimarisi
Blockchain üzerindeki eksiksiz bir P2P enerji ticaret sistemi, her biri beş farklı katmana bölünmüştür. belirli sorumluluklar ve uygun teknolojilerle.
Komple Mimari Yığın
┌─────────────────────────────────────────────────────────────────────────┐
│ LAYER 5: UI / DASHBOARD │
│ React / Angular app • Prosumer wallet • Portale CER │
├─────────────────────────────────────────────────────────────────────────┤
│ LAYER 4: SMART CONTRACT │
│ EnergyToken (ERC-20) • OrderBook • EnergyMarketplace │
│ Settlement • CarbonCredit • ProducerNFT (ERC-721) │
│ Blockchain: Polygon zkEVM / Energy Web Chain / Hyperledger Besu │
├─────────────────────────────────────────────────────────────────────────┤
│ LAYER 3: ORACLE LAYER │
│ Chainlink (prezzi grid) • Smart Meter Oracle (letture DLMS) │
│ DSO Constraint Oracle (capacity limits) • Weather Oracle │
├─────────────────────────────────────────────────────────────────────────┤
│ LAYER 2: INTEGRATION MIDDLEWARE │
│ FastAPI backend • DLMS/COSEM adapter • DSO API client │
│ GSE CACER connector • MQTT broker • InfluxDB time-series │
├─────────────────────────────────────────────────────────────────────────┤
│ LAYER 1: FIELD DEVICES │
│ Smart meter (IEC 62056) • Inverter FV • BESS controller │
│ EV charger (OCPP) • CER gateway • RTU / IED │
└─────────────────────────────────────────────────────────────────────────┘
P2P İşleminin Akışı
Fiziksel ölçümden ölçüme kadar tek bir enerji ticareti işlemi şu şekilde çalışır: ödeme:
Smart Meter (Prosumer A)
│
├─► Lettura DLMS/COSEM ogni 15 minuti
│ IEC 62056-21 / OBIS codes
│
▼
Smart Meter Oracle (backend FastAPI)
│
├─► Valida dati (firma crittografica del meter)
├─► Aggrega energia prodotta nell'intervallo
├─► Chiama oracle contract: reportProduction(meterId, wh, timestamp)
│
▼
Oracle Smart Contract (on-chain)
│
├─► Verifica firma meter
├─► Aggiorna ProductionRegistry[meterId]
├─► Minta EnergyToken (EWT) = wh prodotte
│ 1 EWT = 1 Wh di energia rinnovabile certificata
│
▼
EnergyMarketplace Contract
│
├─► Prosumer A posta offerta: 50 EWT @ 0.08 EUR/EWT
├─► OrderBook matching con Consumer B: 50 EWT @ 0.09 EUR/EWT
├─► Verifica vincoli DSO: capacity disponibile sulla cabina? SI
│
▼
Settlement Contract
│
├─► Trasferisce 50 EWT da A a B
├─► Trasferisce 4 EUR (stablecoin) da B ad A
├─► Emette evento Trade(A, B, 50, 0.08, timestamp)
├─► Aggiorna CarbonCredit Registry (+50g CO2 evitata)
│
▼
DSO API (e-distribuzione / DSO locale)
│
└─► Notifica transazione per riconciliazione fisica
POST /api/v1/p2p-transactions
{ "from": "POD-IT001E...", "to": "POD-IT001E...", "wh": 50 }
Blockchain Seçimi: Enerji Kullanım Durumuna Göre Karşılaştırma
P2P enerji ticareti için tüm blok zincirleri eşit şekilde yaratılmamıştır. Sektöre özel gereksinimler - Mikro işlemlerin yüksek verimi, çok düşük gaz maliyetleri, mevzuata uygunluk, katılımcılar - hassas mimari seçimlere yol açar.
Karşılaştırma Tablosu
| Blockchain | TPS | Gaz Maliyeti (tx) | Amaç | Kimlik | CER'e uygun |
|---|---|---|---|---|---|
| Ethereum Ana Ağı | 15-30 | 2-50$ | ~12 dk | Anonim | Hayır (çok pahalı) |
| Poligon PoS | 7.000 | 0,001-0,01$ | ~2 saniye | Anonim | Evet (geliştirme/test) |
| Poligon zkEVM | 2.000+ | 0,01-0,05$ | ~1 dk (ZK kanıtı) | İsimsiz +ZK | Evet (gizlilik) |
| Arbitrum Bir | 4.000+ | 0,001-0,02$ | ~1 saniye | Anonim | Si |
| Enerji Ağ Zinciri | 3.000 | ~$0 | ~5 saniye | DID + SGK | Optimum |
| Hyperledger Besu | 1.000+ | $0 | <1 saniye | İzin verildi | Evet (kurumsal) |
| Hyperledger Kumaş | 3.500 | $0 | <1 saniye | MSP+X.509 | Evet (B2B) |
CER Italiana için öneri
Önerilen Yığın: Enerji Ağ Zinciri + Poligon zkEVM
Enerji Ağ Zinciri (EWC) ve Yetki Kanıtı'na dayanan halka açık bir blockchain, Enerji sektörü için özel olarak tasarlanmıştır. Çerçeveyi yerel olarak destekler EW-DID Merkezi olmayan üreten tüketici kimliği için (eIDAS 2.0 uyumlu), neredeyse sıfır işlem maliyetine sahiptir (doğrulayıcılar düzenlemeye tabi kamu hizmetleridir) ve halihazırda Siemens, Shell, Volkswagen ve 100'ün üzerinde enerji kuruluşu tarafından kullanılmaktadır.
Tüketici verilerinin gizliliği (GDPR gerekliliği) için kullanılır Poligon zkEVM Sıfır Bilgi Kanıtlarına sahip L2 gibi: tüketim verileri özel kalır (zincir dışı), ancak bunların geçerliliği, gerçek değerler açıklanmadan zincir üzerinde kanıtlanmıştır.
Solidity'de Akıllı Sözleşme uygulaması
P2P enerji ticareti için eksiksiz bir akıllı sözleşme sistemi uyguluyoruz. Tasarım aşağıdaki gibidir desen Elmas / Vekil yükseltilebilirlik ve desen için ERC-20 + özel ödeme Enerji jetonları için.
1. EnergyToken (ERC-20): Enerji Tokenı
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/access/AccessControl.sol";
import "@openzeppelin/contracts/security/Pausable.sol";
/**
* @title EnergyToken (EWT)
* @notice Token ERC-20 che rappresenta 1 Wh di energia rinnovabile certificata.
* Mintato dall'oracle quando lo smart meter registra produzione verificata.
* Bruciato (burned) al momento del settlement fisico con il DSO.
*/
contract EnergyToken is ERC20, AccessControl, Pausable {
bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
bytes32 public constant BURNER_ROLE = keccak256("BURNER_ROLE");
bytes32 public constant ORACLE_ROLE = keccak256("ORACLE_ROLE");
// Metadata certificazione rinnovabile
struct EnergyBatch {
uint256 timestamp;
string sourceType; // "SOLAR", "WIND", "HYDRO"
string location; // codice CER (es. "IT-CER-PG-001")
uint256 co2Avoided; // grammi CO2 evitata per Wh
}
mapping(uint256 => EnergyBatch) public batches; // batchId => metadata
mapping(address => uint256) public producerBatch; // prosumer => ultimo batchId
uint256 public batchCounter;
// Registry prosumer certificati (EW-DID o indirizzo Ethereum)
mapping(address => bool) public certifiedProducers;
mapping(address => string) public producerDID; // Decentralized Identifier
event EnergyMinted(
address indexed producer,
uint256 indexed batchId,
uint256 amount, // Wh
string sourceType,
uint256 co2Avoided
);
event ProducerCertified(address indexed producer, string did);
constructor() ERC20("EnergyWh Token", "EWT") {
_grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
_grantRole(MINTER_ROLE, msg.sender);
}
/**
* @notice Certifica un prosumer dopo verifica KYC/DID fuori catena.
* Solo admin può farlo (utility o GSE delegate).
*/
function certifyProducer(
address producer,
string calldata did
) external onlyRole(DEFAULT_ADMIN_ROLE) {
certifiedProducers[producer] = true;
producerDID[producer] = did;
emit ProducerCertified(producer, did);
}
/**
* @notice Minta EWT in base alla lettura verificata dello smart meter.
* Chiamato dall'oracle dopo validazione dati DLMS/COSEM.
* @param producer Indirizzo wallet del prosumer
* @param whAmount Energia prodotta in Wh (precision: intera, no decimali)
* @param sourceType Tipo fonte: "SOLAR", "WIND", ecc.
* @param location Codice CER di appartenenza
* @param co2PerWh Grammi CO2 evitata per Wh (dipende dalla fonte)
*/
function mintEnergy(
address producer,
uint256 whAmount,
string calldata sourceType,
string calldata location,
uint256 co2PerWh
) external onlyRole(MINTER_ROLE) whenNotPaused {
require(certifiedProducers[producer], "EWT: producer non certificato");
require(whAmount > 0, "EWT: quantità zero non valida");
batchCounter++;
batches[batchCounter] = EnergyBatch({
timestamp: block.timestamp,
sourceType: sourceType,
location: location,
co2Avoided: whAmount * co2PerWh
});
producerBatch[producer] = batchCounter;
_mint(producer, whAmount);
emit EnergyMinted(producer, batchCounter, whAmount, sourceType, whAmount * co2PerWh);
}
/**
* @notice Brucia EWT dopo il settlement fisico con il DSO.
* Garantisce che ogni token sia usato una sola volta.
*/
function burnSettled(
address holder,
uint256 amount
) external onlyRole(BURNER_ROLE) {
_burn(holder, amount);
}
function pause() external onlyRole(DEFAULT_ADMIN_ROLE) { _pause(); }
function unpause() external onlyRole(DEFAULT_ADMIN_ROLE) { _unpause(); }
}
2. OrderBook: Teklif Eşleştirme Mekanizması
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
/**
* @title EnergyOrderBook
* @notice OrderBook on-chain per offerte di vendita e acquisto di energia.
* Supporta aste continue (CDA - Continuous Double Auction).
* Ottimizzato per basse latenze su L2 (Polygon / Arbitrum).
*/
contract EnergyOrderBook {
enum OrderType { BUY, SELL }
enum OrderStatus { OPEN, FILLED, CANCELLED, PARTIAL }
struct Order {
uint256 id;
address trader;
OrderType orderType;
uint256 whAmount; // Wh offerti/richiesti
uint256 pricePerWh; // EUR in wei (usando stablecoin 18 decimali)
uint256 minFillAmount; // minimo parziale accettabile
uint256 expiresAt; // timestamp scadenza ordine
uint256 filledAmount; // Wh già eseguiti
OrderStatus status;
string cerCode; // CER di appartenenza (vincolo geografico)
bytes gridConstraintSig; // firma DSO oracle su capacity disponibile
}
mapping(uint256 => Order) public orders;
uint256 public orderCounter;
// Sorted order books (semplificato - in produzione usa RB-tree o heap)
uint256[] public sellOrderIds; // ordinati per prezzo ASC
uint256[] public buyOrderIds; // ordinati per prezzo DESC
event OrderPlaced(
uint256 indexed orderId,
address indexed trader,
OrderType orderType,
uint256 whAmount,
uint256 pricePerWh
);
event OrderMatched(
uint256 indexed sellOrderId,
uint256 indexed buyOrderId,
uint256 whAmount,
uint256 price
);
event OrderCancelled(uint256 indexed orderId);
modifier onlyOrderOwner(uint256 orderId) {
require(orders[orderId].trader == msg.sender, "OB: non sei il proprietario");
_;
}
/**
* @notice Posta un ordine di vendita o acquisto energia.
* @param orderType BUY (0) o SELL (1)
* @param whAmount Quantità in Wh
* @param pricePerWh Prezzo in wei di stablecoin per Wh (es. 0.08 EUR = 80000000000000000)
* @param minFill Quantità minima parziale accettabile (0 = fill or kill)
* @param ttl Time-to-live in secondi (max 86400 = 1 giorno)
* @param cerCode Codice CER per vincolo geografico
*/
function placeOrder(
OrderType orderType,
uint256 whAmount,
uint256 pricePerWh,
uint256 minFill,
uint256 ttl,
string calldata cerCode
) external returns (uint256 orderId) {
require(whAmount >= 100, "OB: minimo 100 Wh per ordine");
require(pricePerWh > 0, "OB: prezzo zero non valido");
require(ttl <= 86400, "OB: TTL max 24 ore");
orderId = ++orderCounter;
orders[orderId] = Order({
id: orderId,
trader: msg.sender,
orderType: orderType,
whAmount: whAmount,
pricePerWh: pricePerWh,
minFillAmount: minFill,
expiresAt: block.timestamp + ttl,
filledAmount: 0,
status: OrderStatus.OPEN,
cerCode: cerCode,
gridConstraintSig: ""
});
if (orderType == OrderType.SELL) {
_insertSellOrder(orderId);
} else {
_insertBuyOrder(orderId);
}
emit OrderPlaced(orderId, msg.sender, orderType, whAmount, pricePerWh);
}
// Inserimento ordinato (bubble sort semplificato - in produzione usa heap)
function _insertSellOrder(uint256 newId) internal {
sellOrderIds.push(newId);
uint256 n = sellOrderIds.length;
for (uint256 i = n - 1; i > 0; i--) {
if (orders[sellOrderIds[i]].pricePerWh < orders[sellOrderIds[i-1]].pricePerWh) {
(sellOrderIds[i], sellOrderIds[i-1]) = (sellOrderIds[i-1], sellOrderIds[i]);
} else {
break;
}
}
}
function _insertBuyOrder(uint256 newId) internal {
buyOrderIds.push(newId);
uint256 n = buyOrderIds.length;
for (uint256 i = n - 1; i > 0; i--) {
if (orders[buyOrderIds[i]].pricePerWh > orders[buyOrderIds[i-1]].pricePerWh) {
(buyOrderIds[i], buyOrderIds[i-1]) = (buyOrderIds[i-1], buyOrderIds[i]);
} else {
break;
}
}
}
function cancelOrder(uint256 orderId) external onlyOrderOwner(orderId) {
Order storage o = orders[orderId];
require(o.status == OrderStatus.OPEN, "OB: ordine non annullabile");
o.status = OrderStatus.CANCELLED;
emit OrderCancelled(orderId);
}
}
3. EnergyMarketplace: Ana Sözleşme
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "@openzeppelin/contracts/access/AccessControl.sol";
import "./EnergyToken.sol";
import "./EnergyOrderBook.sol";
/**
* @title EnergyMarketplace
* @notice Contratto principale per il trading P2P di energia nelle CER.
* Gestisce matching ordini, settlement EWT, verifica vincoli DSO,
* e distribuzione incentivi carbon credit.
*
* @dev Architettura:
* - EWT (EnergyToken) per i Wh di energia
* - EURC (Circle Euro stablecoin) per il pagamento in EUR
* - Escrow automatico durante il matching
* - Fee marketplace: 0.5% sul valore transazione (destinazione: CER treasury)
*/
contract EnergyMarketplace is ReentrancyGuard, AccessControl {
// =========== STATE VARIABLES ===========
EnergyToken public ewtToken;
IERC20 public stablecoin; // EURC o EURe
EnergyOrderBook public orderBook;
bytes32 public constant DSO_ORACLE_ROLE = keccak256("DSO_ORACLE_ROLE");
bytes32 public constant CER_ADMIN_ROLE = keccak256("CER_ADMIN_ROLE");
uint256 public constant FEE_BPS = 50; // 0.5% in basis points
uint256 public constant MIN_TRADE_WH = 100; // minimo 100 Wh per trade
uint256 public constant SETTLEMENT_WINDOW = 900; // 15 minuti max per settlement
// Parametri CER (impostati dall'amministratore)
struct CERConfig {
string cerCode;
address cerTreasury; // wallet CER che riceve le fee
uint256 maxCapacityWh; // capacità massima oraria cabina primaria
uint256 currentLoadWh; // carico attuale comunicato dal DSO oracle
bool active;
}
mapping(string => CERConfig) public cerConfigs;
// Trade registry per audit
struct Trade {
uint256 tradeId;
uint256 sellOrderId;
uint256 buyOrderId;
address seller;
address buyer;
uint256 whAmount;
uint256 pricePerWh;
uint256 totalValue; // in stablecoin wei
uint256 fee;
uint256 timestamp;
string cerCode;
}
mapping(uint256 => Trade) public trades;
uint256 public tradeCounter;
// Escrow: acquirente deposita stablecoin prima del matching
mapping(uint256 => uint256) public escrow; // orderId => importo bloccato
// Pending settlement per DSO reconciliation
mapping(uint256 => bool) public pendingDSOSettlement; // tradeId => settled
// =========== EVENTS ===========
event TradeExecuted(
uint256 indexed tradeId,
address indexed seller,
address indexed buyer,
uint256 whAmount,
uint256 pricePerWh,
string cerCode
);
event EscrowDeposited(uint256 indexed orderId, uint256 amount);
event EscrowReleased(uint256 indexed orderId, uint256 amount);
event DSOSettlementConfirmed(uint256 indexed tradeId);
event GridConstraintViolation(string cerCode, uint256 requestedWh, uint256 availableWh);
// =========== CONSTRUCTOR ===========
constructor(
address _ewtToken,
address _stablecoin,
address _orderBook
) {
ewtToken = EnergyToken(_ewtToken);
stablecoin = IERC20(_stablecoin);
orderBook = EnergyOrderBook(_orderBook);
_grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
}
// =========== CER MANAGEMENT ===========
function configureCER(
string calldata cerCode,
address treasury,
uint256 maxCapacityWh
) external onlyRole(CER_ADMIN_ROLE) {
cerConfigs[cerCode] = CERConfig({
cerCode: cerCode,
cerTreasury: treasury,
maxCapacityWh: maxCapacityWh,
currentLoadWh: 0,
active: true
});
}
/**
* @notice Aggiornamento carico attuale dalla rete, chiamato dall'oracle DSO.
* Previene transazioni che violerebbero i limiti di capacità.
*/
function updateGridLoad(
string calldata cerCode,
uint256 currentLoadWh
) external onlyRole(DSO_ORACLE_ROLE) {
cerConfigs[cerCode].currentLoadWh = currentLoadWh;
}
// =========== ESCROW ===========
/**
* @notice L'acquirente deposita stablecoin in escrow quando posta un BUY order.
* Garantisce la solvibilita prima del matching.
*/
function depositEscrow(
uint256 orderId,
uint256 amount
) external nonReentrant {
EnergyOrderBook.Order memory o = orderBook.orders(orderId);
require(o.trader == msg.sender, "MKT: non sei il proprietario dell'ordine");
require(o.orderType == EnergyOrderBook.OrderType.BUY, "MKT: solo per ordini BUY");
uint256 maxCost = o.whAmount * o.pricePerWh / 1e18;
require(amount >= maxCost, "MKT: escrow insufficiente");
require(stablecoin.transferFrom(msg.sender, address(this), amount), "MKT: transfer fallita");
escrow[orderId] += amount;
emit EscrowDeposited(orderId, amount);
}
// =========== MATCHING ENGINE ===========
/**
* @notice Esegue il matching tra un ordine di vendita e uno di acquisto.
* Verifica: prezzi compatibili, stessa CER, vincoli DSO, escrow sufficienti.
*
* @dev In produzione, questo viene chiamato da un keeper off-chain che monitora
* gli OrderPlaced events e trova le coppie compatibili.
*/
function matchOrders(
uint256 sellOrderId,
uint256 buyOrderId
) external nonReentrant {
EnergyOrderBook.Order memory sellOrder = orderBook.orders(sellOrderId);
EnergyOrderBook.Order memory buyOrder = orderBook.orders(buyOrderId);
// Validazioni base
require(
sellOrder.status == EnergyOrderBook.OrderStatus.OPEN &&
buyOrder.status == EnergyOrderBook.OrderStatus.OPEN,
"MKT: ordini non aperti"
);
require(
keccak256(bytes(sellOrder.cerCode)) == keccak256(bytes(buyOrder.cerCode)),
"MKT: CER diverse, trade non permesso"
);
require(
block.timestamp <= sellOrder.expiresAt &&
block.timestamp <= buyOrder.expiresAt,
"MKT: ordine/i scaduti"
);
// Verifica prezzo: sell price <= buy price (spread positivo)
require(
sellOrder.pricePerWh <= buyOrder.pricePerWh,
"MKT: prezzi incompatibili"
);
// Calcola quantità da eseguire (minimo tra i due ordini rimanenti)
uint256 sellRemaining = sellOrder.whAmount - sellOrder.filledAmount;
uint256 buyRemaining = buyOrder.whAmount - buyOrder.filledAmount;
uint256 tradeWh = sellRemaining < buyRemaining ? sellRemaining : buyRemaining;
require(tradeWh >= MIN_TRADE_WH, "MKT: quantità sotto il minimo");
// Verifica vincoli grid capacity DSO
string memory cerCode = sellOrder.cerCode;
CERConfig storage cer = cerConfigs[cerCode];
require(cer.active, "MKT: CER non attiva");
uint256 availableCapacity = cer.maxCapacityWh > cer.currentLoadWh
? cer.maxCapacityWh - cer.currentLoadWh
: 0;
if (tradeWh > availableCapacity) {
emit GridConstraintViolation(cerCode, tradeWh, availableCapacity);
revert("MKT: capacità grid insufficiente");
}
// Prezzo di esecuzione = midpoint (o prezzo sell per semplicità)
uint256 execPrice = sellOrder.pricePerWh;
// Calcola valore totale e fee
uint256 totalValue = tradeWh * execPrice / 1e18;
uint256 fee = totalValue * FEE_BPS / 10000;
uint256 sellerNet = totalValue - fee;
// Verifica EWT del venditore
require(
ewtToken.balanceOf(sellOrder.trader) >= tradeWh,
"MKT: venditore senza EWT sufficienti"
);
// Verifica escrow acquirente
require(escrow[buyOrderId] >= totalValue, "MKT: escrow acquirente insufficiente");
// ======= ESEGUI SETTLEMENT =======
// 1. Trasferisci EWT dal venditore all'acquirente
require(
ewtToken.transferFrom(sellOrder.trader, buyOrder.trader, tradeWh),
"MKT: trasferimento EWT fallito"
);
// 2. Trasferisci stablecoin da escrow al venditore (netto di fee)
escrow[buyOrderId] -= totalValue;
require(
stablecoin.transfer(sellOrder.trader, sellerNet),
"MKT: pagamento venditore fallito"
);
// 3. Fee alla treasury CER
if (fee > 0) {
require(
stablecoin.transfer(cer.cerTreasury, fee),
"MKT: fee treasury fallita"
);
}
// 4. Registra trade
tradeCounter++;
trades[tradeCounter] = Trade({
tradeId: tradeCounter,
sellOrderId: sellOrderId,
buyOrderId: buyOrderId,
seller: sellOrder.trader,
buyer: buyOrder.trader,
whAmount: tradeWh,
pricePerWh: execPrice,
totalValue: totalValue,
fee: fee,
timestamp: block.timestamp,
cerCode: cerCode
});
pendingDSOSettlement[tradeCounter] = true;
// 5. Aggiorna carico stimato CER
cer.currentLoadWh += tradeWh;
emit TradeExecuted(
tradeCounter,
sellOrder.trader,
buyOrder.trader,
tradeWh,
execPrice,
cerCode
);
}
/**
* @notice Conferma settlement fisico da parte del DSO.
* Brucia i token EWT dopo la riconciliazione fisica.
*/
function confirmDSOSettlement(
uint256 tradeId
) external onlyRole(DSO_ORACLE_ROLE) {
require(pendingDSOSettlement[tradeId], "MKT: trade già confermato");
Trade memory t = trades[tradeId];
// Brucia i token EWT dell'acquirente (energia "consumata" fisicamente)
ewtToken.burnSettled(t.buyer, t.whAmount);
pendingDSOSettlement[tradeId] = false;
emit DSOSettlementConfirmed(tradeId);
}
}
Gaz Maliyeti ve Optimizasyonlarına İlişkin Not
Örnekteki EnergyMarketplace sözleşmesi, eğitimsel netlik sağlamak amacıyla optimize edilmemiş depolamayı kullanıyor.
Üretimde depolama yazma sayısını azaltmak kritik öneme sahiptir. Teknikler: kullanım
calldata yerine memory mümkün olduğunda paket değişkenleri
birinde uint256 bit kaydırma ve her şeyden önce kullanım ile olaylar
geçmiş verileri depolamak yerine (olay günlüklerinin maliyeti depolamadan ~20 kat daha azdır).
Polygon zkEVM'de tahmini gaz maliyeti matchOrders() tam ve yaklaşık
150.000 gaz, MATIC'in mevcut fiyatıyla yaklaşık 0,03 Euro'ya eşittir.
Oracle Design: Zincir Üstü Akıllı Sayaç Verileri
Enerji bağlamında Blockchain kahininin temel sorunu ve sertifikasyonu gerçek üretim: EWT olarak basılan Wh'nin gerçekte enerjiye karşılık geldiğinden nasıl emin olunur? Sertifikalı bir akıllı sayaç tarafından üretilen ve ölçülen fizik?
Oracle Çok Katmanlı Mimari
# oracle/smart_meter_oracle.py
# Oracle Python che legge smart meter DLMS/COSEM e invia dati on-chain
import asyncio
import hashlib
import json
import time
from dataclasses import dataclass
from typing import Optional
from dlms_cosem import DlmsConnection, Obis # libreria Python DLMS
from eth_account import Account
from web3 import AsyncWeb3
from web3.middleware import SignAndSendRawMiddlewareBuilder
# ======= CONFIGURAZIONE =======
ORACLE_PRIVATE_KEY = "0x..." # in produzione: HashiCorp Vault / AWS KMS
ENERGY_TOKEN_ABI = json.load(open("abi/EnergyToken.json"))
ENERGY_TOKEN_ADDR = "0x..."
RPC_URL = "https://rpc.energyweb.org"
# Parametri DLMS per smart meter italiano
# Standard: IEC 62056-21, OBIS codes per contatori bidirezionali
METER_IP = "192.168.1.100"
METER_PORT = 4059
METER_TIMEOUT = 10 # secondi
# OBIS codes standard per energia importata/esportata
OBIS_ACTIVE_EXPORT = Obis(1, 0, 2, 8, 0, 255) # kWh esportati totali
OBIS_ACTIVE_IMPORT = Obis(1, 0, 1, 8, 0, 255) # kWh importati totali
@dataclass
class MeterReading:
meter_id: str
timestamp: int
export_kwh: float # energia esportata (produzione immessa)
import_kwh: float # energia importata (consumo dalla rete)
net_wh: int # netto in Wh (positivo = produzione eccesso)
signature: bytes # firma ECDSA del meter (se disponibile)
async def read_smart_meter(meter_id: str) -> MeterReading:
"""
Legge lo smart meter via DLMS/COSEM e restituisce dati verificati.
Il contatore italiano e-distribuzione (Enel Hera Linea Group) supporta
IEC 62056-21 mode E con HDLC framing.
"""
conn = DlmsConnection(
ip=METER_IP,
port=METER_PORT,
timeout=METER_TIMEOUT
)
try:
await conn.connect()
# Autenticazione DLMS (password HLS o LLS secondo configurazione)
await conn.authenticate(level="lls", password="00000000")
export_kwh = await conn.get(OBIS_ACTIVE_EXPORT)
import_kwh = await conn.get(OBIS_ACTIVE_IMPORT)
net_wh = int((export_kwh - import_kwh) * 1000) # converti in Wh
# Firma del meter (se il contatore supporta firma ECDSA P-256)
# In alternativa, usa l'oracle signature del backend
reading = MeterReading(
meter_id=meter_id,
timestamp=int(time.time()),
export_kwh=float(export_kwh),
import_kwh=float(import_kwh),
net_wh=max(0, net_wh), # solo produzione eccedente
signature=b""
)
return reading
finally:
await conn.disconnect()
def sign_reading(reading: MeterReading, private_key: str) -> bytes:
"""
Firma la lettura con la chiave privata dell'oracle.
Il contratto verifica questa firma per autenticare la sorgente.
"""
account = Account.from_key(private_key)
message = hashlib.sha256(
f"{reading.meter_id}:{reading.timestamp}:{reading.net_wh}".encode()
).hexdigest()
signed = account.sign_message(
message_signable={"type": "string", "message": message}
)
return signed.signature
async def submit_to_blockchain(
reading: MeterReading,
w3: AsyncWeb3,
oracle_account: Account
) -> Optional[str]:
"""
Invia la lettura verificata al contratto EnergyToken per il mint.
Usa EIP-1559 per ottimizzare i gas cost su L2.
"""
if reading.net_wh < 100:
# Non vale la pena mintare meno di 100 Wh (under il minimo del marketplace)
print(f"Skip: lettura {reading.net_wh}Wh sotto la soglia minima")
return None
contract = w3.eth.contract(
address=ENERGY_TOKEN_ADDR,
abi=ENERGY_TOKEN_ABI
)
# Verifica il wallet del prosumer associato al meter ID
producer_address = await get_producer_address(reading.meter_id)
tx = await contract.functions.mintEnergy(
producer_address,
reading.net_wh,
"SOLAR",
"IT-CER-PG-001",
400 # grammi CO2 per kWh (media mix italiano, aggiornata ISPRA 2024)
).build_transaction({
"from": oracle_account.address,
"nonce": await w3.eth.get_transaction_count(oracle_account.address),
"maxFeePerGas": w3.to_wei("30", "gwei"),
"maxPriorityFeePerGas": w3.to_wei("2", "gwei"),
})
signed_tx = oracle_account.sign_transaction(tx)
tx_hash = await w3.eth.send_raw_transaction(signed_tx.raw_transaction)
receipt = await w3.eth.wait_for_transaction_receipt(tx_hash, timeout=120)
if receipt.status == 1:
print(f"Mintati {reading.net_wh} EWT per {producer_address} | TX: {tx_hash.hex()}")
return tx_hash.hex()
else:
print(f"Errore mint: TX {tx_hash.hex()} revertita")
return None
async def oracle_loop(interval_seconds: int = 900):
"""
Loop principale dell'oracle: legge il meter ogni 15 minuti (quarter-hour interval
allineato al settlement energetico del mercato elettrico italiano - MGP/MI IPEX).
"""
w3 = AsyncWeb3(AsyncWeb3.AsyncHTTPProvider(RPC_URL))
oracle_account = Account.from_key(ORACLE_PRIVATE_KEY)
meter_ids = await load_certified_meters() # da database GSE
print(f"Oracle avviato. Monitoraggio {len(meter_ids)} smart meter ogni {interval_seconds}s")
while True:
start = time.time()
for meter_id in meter_ids:
try:
reading = await read_smart_meter(meter_id)
tx_hash = await submit_to_blockchain(reading, w3, oracle_account)
await log_reading(reading, tx_hash)
except Exception as e:
print(f"Errore meter {meter_id}: {e}")
await alert_on_call(meter_id, str(e))
elapsed = time.time() - start
sleep_for = max(0, interval_seconds - elapsed)
await asyncio.sleep(sleep_for)
if __name__ == "__main__":
asyncio.run(oracle_loop())
DSO ile Entegrasyon: Ağ Kısıtlamaları ve Izgara Operatörü API'si
Herhangi bir P2P enerji ticareti sisteminin kritik noktası, DSO (Dağıtım Sistemi Operatörü) - çoğunlukla İtalya'da e-dağıtım (Enel grubu) dağıtım ağının %85'ini yönetmektedir. DSO ile entegrasyon olmadan zincir içi ticaret yalnızca bağlantısız bir finansal soyutlamadır ağın gerçek fiziğinden.
çünkü DSO Kısıtlamaları Pazarlık Edilemez
Dağıtım Ağının Fiziksel Sınırları
- birincil kabin kapasitesi: Her bir HV/MV kabininin maksimum kapasitesi dönüşüm (tipik olarak 40-100 MVA). P2P işlemleri çevre içinde gerçekleşir tek bir birincil trafo merkezinin kullanımı - bu aynı zamanda İtalyan CER'lerin coğrafi kısıtlamasıdır.
- Tıkanıklık yönetimi: fotovoltaik saatlerin en yoğun olduğu saatlerde (11-15 saat yaz), bazı BT hatları doygun hale gelebilir. DSO, işlemleri bloke edebilmeli veya azaltabilmelidir. yerel elektrik kesintilerinden kaçının.
- Dengeleme: enerji fiziksel olarak bir POD'dan diğerine akamaz Kayıpları ve ağ dengelemeyi dikkate almadan. P2P finansal ticaret bir fiziksel yönlendirme değil, sanal ağ.
- Resmi boyut: sertifikalı akıllı sayaç (e-dağıtım veya üçüncü taraf) sertifikası) ve GSE ile düzenleyici çözüm için yasal olarak geçerli tek önlemdir.
DSO API'si için Python istemcisi
# integration/dso_client.py
# Client per l'API REST di e-distribuzione (simulata per sviluppo)
# In produzione: accesso tramite portale OpenData e-distribuzione o API B2B
import httpx
import asyncio
from dataclasses import dataclass
from typing import Optional
from datetime import datetime, timezone
DSO_API_BASE = "https://api.e-distribuzione.it/v2"
DSO_API_KEY = "..." # da variabile ambiente
@dataclass
class GridCapacity:
"""capacità grid disponibile per una cabina primaria in un intervallo temporale."""
substation_id: str # ID cabina primaria (es. "IT-RM-CP-0042")
timestamp_from: datetime
timestamp_to: datetime
total_capacity_kw: float # capacità totale MT
available_capacity_kw: float # disponibile per nuovi flussi
congestion_level: str # "LOW", "MEDIUM", "HIGH", "CRITICAL"
@dataclass
class P2PTradeNotification:
"""Notifica al DSO di un trade P2P eseguito on-chain, per riconciliazione."""
trade_id: str # ID on-chain della transazione
tx_hash: str # hash blockchain
pod_seller: str # POD del prosumer venditore (es. "IT001E12345678XX")
pod_buyer: str # POD del consumatore acquirente
wh_amount: int # Wh tradati
timestamp: datetime # timestamp del trade on-chain
cer_code: str # codice CER per riconciliazione GSE
class DSOClient:
"""
Client asincrono per comunicare con il Distribution System Operator.
Gestisce:
- Query capacità disponibile per il grid constraint oracle
- Notifica trade P2P per riconciliazione fisica
- Ricezione alert di congestion per blocco preventivo
"""
def __init__(self, api_base: str = DSO_API_BASE, api_key: str = DSO_API_KEY):
self.api_base = api_base
self.headers = {
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json",
"Accept": "application/json"
}
async def get_grid_capacity(
self,
substation_id: str,
interval_minutes: int = 15
) -> GridCapacity:
"""
Interroga il DSO per la capacità attuale della cabina primaria.
Questa informazione alimenta il DSO oracle on-chain.
"""
async with httpx.AsyncClient(timeout=10.0) as client:
response = await client.get(
f"{self.api_base}/substations/{substation_id}/capacity",
headers=self.headers,
params={
"interval_minutes": interval_minutes,
"timestamp": datetime.now(timezone.utc).isoformat()
}
)
response.raise_for_status()
data = response.json()
return GridCapacity(
substation_id=data["substation_id"],
timestamp_from=datetime.fromisoformat(data["from"]),
timestamp_to=datetime.fromisoformat(data["to"]),
total_capacity_kw=data["capacity"]["total_kw"],
available_capacity_kw=data["capacity"]["available_kw"],
congestion_level=data["congestion"]["level"]
)
async def notify_p2p_trade(self, trade: P2PTradeNotification) -> bool:
"""
Notifica il DSO di un trade P2P completato on-chain.
Il DSO usa questa informazione per:
1. Riconciliazione con le letture degli smart meter
2. Aggiornamento del database CACER (tramite interfaccia GSE)
3. Eventuale richiesta di annullamento se vincoli violati ex-post
"""
async with httpx.AsyncClient(timeout=15.0) as client:
payload = {
"trade_id": trade.trade_id,
"tx_hash": trade.tx_hash,
"seller_pod": trade.pod_seller,
"buyer_pod": trade.pod_buyer,
"energy_wh": trade.wh_amount,
"timestamp": trade.timestamp.isoformat(),
"cer_code": trade.cer_code,
"source": "blockchain_p2p_marketplace"
}
response = await client.post(
f"{self.api_base}/p2p-settlements",
headers=self.headers,
json=payload
)
if response.status_code == 201:
print(f"Trade {trade.trade_id} notificato al DSO con successo")
return True
else:
print(f"Errore DSO: {response.status_code} - {response.text}")
return False
async def subscribe_congestion_alerts(
self,
substation_id: str,
callback # async callable
):
"""
WebSocket subscription per alert real-time di congestione.
Quando il DSO segnala un blocco, il keeper aggiorna il contratto
e blocca nuovi trade per quella CER.
"""
import websockets
ws_url = f"wss://api.e-distribuzione.it/v2/ws/congestion/{substation_id}"
async with websockets.connect(
ws_url,
extra_headers={"Authorization": f"Bearer {DSO_API_KEY}"}
) as ws:
async for message in ws:
alert = json.loads(message)
await callback(alert)
Hardhat ile Test: Pazar Yeri için Eksiksiz Paket
Enerji sözleşmeleri gerçek değeri yönetir: güvenilirlik ve eleştiri. Bir test paketi ile kapsamlı Kask ve doğruluğunu garanti etmenin tek yolu Ana ağda dağıtımdan önce pazar yeri.
// test/EnergyMarketplace.test.ts
import { expect } from "chai";
import { ethers } from "hardhat";
import { loadFixture } from "@nomicfoundation/hardhat-toolbox/network-helpers";
import { SignerWithAddress } from "@nomicfoundation/hardhat-ethers/signers";
describe("EnergyMarketplace - Suite Completa", function () {
// ========= FIXTURE =========
async function deployMarketplaceFixture() {
const [admin, dsoOracle, cerAdmin, prosumerA, prosumerB, consumer1, consumer2]
= await ethers.getSigners();
// Deploy EnergyToken
const EnergyToken = await ethers.getContractFactory("EnergyToken");
const ewtToken = await EnergyToken.deploy();
// Deploy mock ERC-20 stablecoin (EURC simulato)
const MockEURC = await ethers.getContractFactory("MockERC20");
const eurc = await MockEURC.deploy("Euro Coin", "EURC", 6); // 6 decimali come USDC
// Deploy OrderBook
const OrderBook = await ethers.getContractFactory("EnergyOrderBook");
const orderBook = await OrderBook.deploy();
// Deploy Marketplace
const Marketplace = await ethers.getContractFactory("EnergyMarketplace");
const marketplace = await Marketplace.deploy(
await ewtToken.getAddress(),
await eurc.getAddress(),
await orderBook.getAddress()
);
// Setup ruoli
const MINTER_ROLE = await ewtToken.MINTER_ROLE();
await ewtToken.grantRole(MINTER_ROLE, admin.address); // oracle minta
await marketplace.grantRole(
await marketplace.DSO_ORACLE_ROLE(),
dsoOracle.address
);
await marketplace.grantRole(
await marketplace.CER_ADMIN_ROLE(),
cerAdmin.address
);
// Certifica prosumer
await ewtToken.certifyProducer(prosumerA.address, "did:ewc:it:cer:prosumer-a");
await ewtToken.certifyProducer(prosumerB.address, "did:ewc:it:cer:prosumer-b");
// Configura CER
await marketplace.connect(cerAdmin).configureCER(
"IT-CER-PG-001",
admin.address, // treasury CER
1_000_000 // 1 MWh capacità max
);
// Minta EWT per prosumer A (simulazione lettura oracle)
await ewtToken.mintEnergy(
prosumerA.address,
5_000, // 5 kWh
"SOLAR",
"IT-CER-PG-001",
400
);
// Minta EURC per consumer1 (per l'escrow)
await eurc.mint(consumer1.address, ethers.parseUnits("100", 6)); // 100 EUR
return {
admin, dsoOracle, cerAdmin, prosumerA, prosumerB, consumer1, consumer2,
ewtToken, eurc, orderBook, marketplace
};
}
// ========= TEST: TOKEN MINT =========
describe("EnergyToken - Mint e Certifica", function () {
it("deve mintare EWT solo per prosumer certificati", async function () {
const { ewtToken, consumer1 } = await loadFixture(deployMarketplaceFixture);
await expect(
ewtToken.mintEnergy(consumer1.address, 1000, "SOLAR", "IT-CER-PG-001", 400)
).to.be.revertedWith("EWT: producer non certificato");
});
it("deve registrare i metadata del batch correttamente", async function () {
const { ewtToken, prosumerA } = await loadFixture(deployMarketplaceFixture);
const batchId = await ewtToken.producerBatch(prosumerA.address);
const batch = await ewtToken.batches(batchId);
expect(batch.sourceType).to.equal("SOLAR");
expect(batch.location).to.equal("IT-CER-PG-001");
expect(batch.co2Avoided).to.equal(5_000 * 400); // 5000 Wh * 400 gCO2/Wh
});
});
// ========= TEST: ORDINI E MATCHING =========
describe("Marketplace - Matching e Settlement", function () {
it("deve eseguire un trade completo con settlement corretto", async function () {
const {
admin, dsoOracle, prosumerA, consumer1,
ewtToken, eurc, marketplace, orderBook
} = await loadFixture(deployMarketplaceFixture);
// ProsumoA approva EWT per il marketplace
await ewtToken.connect(prosumerA).approve(
await marketplace.getAddress(),
5_000
);
// Posta ordine SELL: 2000 Wh @ 0.08 EUR/Wh
const pricePerWh = ethers.parseUnits("0.08", 18); // 18 decimali per compatibilità
const sellTx = await orderBook.connect(prosumerA).placeOrder(
1, // SELL
2_000,
pricePerWh,
0, // no partial fill
3600, // TTL 1 ora
"IT-CER-PG-001"
);
const sellReceipt = await sellTx.wait();
const sellOrderId = 1n; // primo ordine
// Consumer1 approva EURC per escrow e posta BUY
const buyTotalCost = 2_000n * pricePerWh / BigInt(1e18);
await eurc.connect(consumer1).approve(await marketplace.getAddress(), buyTotalCost);
const buyTx = await orderBook.connect(consumer1).placeOrder(
0, // BUY
2_000,
pricePerWh,
0,
3600,
"IT-CER-PG-001"
);
const buyOrderId = 2n;
// Deposita escrow
await marketplace.connect(consumer1).depositEscrow(buyOrderId, buyTotalCost);
// Aggiorna grid load (DSO oracle: capacità disponibile)
await marketplace.connect(dsoOracle).updateGridLoad("IT-CER-PG-001", 0);
// Snapshot balance prima del trade
const prosumerABalanceBefore = await eurc.balanceOf(prosumerA.address);
const consumer1EWTBefore = await ewtToken.balanceOf(consumer1.address);
// Esegui matching
await marketplace.connect(admin).matchOrders(sellOrderId, buyOrderId);
// Verifica bilanci dopo trade
const prosumerABalanceAfter = await eurc.balanceOf(prosumerA.address);
const consumer1EWTAfter = await ewtToken.balanceOf(consumer1.address);
// ProsumoA ha ricevuto EURC (netto fee 0.5%)
const expectedNet = buyTotalCost - (buyTotalCost * 50n / 10000n);
expect(prosumerABalanceAfter - prosumerABalanceBefore).to.equal(expectedNet);
// Consumer1 ha ricevuto 2000 EWT
expect(consumer1EWTAfter - consumer1EWTBefore).to.equal(2_000n);
});
it("deve rifiutare trade tra CER diverse", async function () {
const { prosumerA, consumer1, orderBook, marketplace }
= await loadFixture(deployMarketplaceFixture);
await orderBook.connect(prosumerA).placeOrder(1, 1000, ethers.parseUnits("0.08", 18), 0, 3600, "IT-CER-PG-001");
await orderBook.connect(consumer1).placeOrder(0, 1000, ethers.parseUnits("0.09", 18), 0, 3600, "IT-CER-RM-002");
await expect(
marketplace.matchOrders(1n, 2n)
).to.be.revertedWith("MKT: CER diverse, trade non permesso");
});
it("deve bloccare trade quando capacità grid e insufficiente", async function () {
const { admin, dsoOracle, prosumerA, consumer1, marketplace, orderBook, ewtToken, eurc }
= await loadFixture(deployMarketplaceFixture);
// Simula grid a piena capacità
await marketplace.connect(dsoOracle).updateGridLoad(
"IT-CER-PG-001",
999_000 // 999 kWh su 1000 kWh disponibili - solo 1 kWh libero
);
await ewtToken.connect(prosumerA).approve(await marketplace.getAddress(), 5000);
await orderBook.connect(prosumerA).placeOrder(1, 2000, ethers.parseUnits("0.08", 18), 0, 3600, "IT-CER-PG-001");
const pricePerWh = ethers.parseUnits("0.08", 18);
const escrowAmt = 2000n * pricePerWh / BigInt(1e18);
await eurc.connect(consumer1).approve(await marketplace.getAddress(), escrowAmt);
await orderBook.connect(consumer1).placeOrder(0, 2000, pricePerWh, 0, 3600, "IT-CER-PG-001");
await marketplace.connect(consumer1).depositEscrow(2n, escrowAmt);
await expect(
marketplace.connect(admin).matchOrders(1n, 2n)
).to.be.revertedWith("MKT: capacità grid insufficiente");
});
});
});
Düzenleyici Çerçeve: AB ve İtalya
Blockchain üzerinde P2P enerji ticareti, mevcut iki düzenleyici çerçevenin kesişiminde faaliyet göstermektedir örtüşüyor: enerji (AB direktifleri ve İtalya'nın mevzuata aktarılması) ve piyasalar. dijital finans (MiCA, GDPR). Düzenleyici kısıtlamaları anlamak planlama için önemlidir Yasal olarak uyumlu bir sistem.
Temel Avrupa Direktifleri
Temiz Enerji Paketi (2018-2021): P2P'nin Yasal Temelleri
Il Temiz Enerji Paketi Avrupa Birliği (CEP) ve düzenleyici çerçeve Bu, AB genelinde P2P enerji ticaretini yasal olarak mümkün kıldı. İlgili sütunlar:
- Elektrik Direktifi (AB) 2019/944: Sanat. 15 - açıkça tanır "aktif üreten tüketicilerin" kendi ürettikleri enerjiyi satma hakkı, P2P anlaşmaları. Sanat. 16 - vatandaşların enerji topluluklarını (CEC) düzenler.
- RED II Direktifi (AB) 2018/2001: Sanat. 22 - tanıtır hakkına sahip bir tüzel kişilik olarak "yenilenebilir enerji topluluğu" (CER) Yenilenebilir enerji üretmek, tüketmek, depolamak ve satmak.
- RED III Direktifi (AB) 2023/2413: Kasım 2023'ten itibaren yürürlükte olan 2030 yılına kadar yenilenebilir enerjinin %42,5'ini hedeflemek. CER'lerin haklarını güçlendirmek, Yetkilendirme prosedürlerini basitleştirir, yetkilendirme kavramını tanıtır "yeniden güçlendirmeyi" kolaylaştırdı. Ulusal aktarım: Mayıs 2025'e kadar.
İtalyan mevzuatı: 199/2021 sayılı Kanun Hükmünde Kararname ve CER
| bekliyorum | Mevzuat Detayı | P2P Blockchain Üzerindeki Etki |
|---|---|---|
| CER tanımı | Tüzel kişiliğe sahip tüzel kişilik, maksimum 1 MW sistem, HV/MV birincil trafo merkezi çevresi | Zincir içi P2P ticareti aynı birincil kabindeki POD'larla sınırlıdır - sabit kısıtlama |
| GSE teşviki | Prim oranı ~110 EUR/MWh paylaşılan enerji + şebeke tarifesi tasarrufunda | Blockchain anlaşmasının CACER GSE portalı ile mutabakatı sağlanmalı |
| Ölçüm | Akıllı sayaç e-dağıtımının tek geçerli ölçüm olduğu onaylandı | Oracle, kendi raporladığı verileri değil, sertifikalı sayaçlardan gelen verileri kullanmalıdır |
| İzin verilen konular | Bireyler, KOBİ'ler, KA'lar, üçüncü sektör kuruluşları, kooperatifler (MASE Mayıs 2025 tarihli Kanun Hükmünde Kararname sonrasında) | Blockchain cüzdanları doğrulanmış yasal kimliklere (KYC) bağlı olmalıdır |
| PNRR teşvikleri | CER yatırımları için 2,2 milyar Euro (CACER çağrısı Temmuz-Kasım 2025) | Blockchain altyapısını finanse etme fırsatı + akıllı sayaç yükseltmesi |
| GSE CACER Kuralları 2025 | Geriye dönük giderlerin basitleştirilmesi, %30 peşin geri ödemesiz katkı payı | P2P sistemine ilk yatırım için daha fazla likidite |
MiCA ve Enerji Tokenları: Bunlar Güvenlik Değildir
EnergyToken'in (EWT) Yasal Sınıflandırması
Yönetmelik MiCA (Kripto Varlık Piyasaları, AB 2023/1114) tam olarak Aralık 2024'ten itibaren geçerli olmak üzere kripto varlıkları üç kategoriye ayırıyor: ART (Varlık Referanslı Tokenlar), EMT (E-Para Tokenları) ve "Diğer tokenlar".
Bu makalede açıklanan EnergyToken (EWT) şu şekilde sınıflandırılabilir: "yardımcı jeton" "Diğer jetonlar" kategorisinde: temsil eder kar beklentisi değil, fiziksel bir enerji birimi (1 EWT = 1 Wh). Bu sınıflandırma, onu ART/EMT'ye yönelik daha katı MiCA yükümlülüklerinden muaf tutar. ama yine de şunu gerektirir:
- Tokenın teknik açıklamasını içeren teknik inceleme yayınlandı
- Katılımcılar için kara para aklamayı önleme tedbirleri (AMLD6) (KYC/AML)
- Hacim eşik değerleri aşarsa ulusal yetkili makama (İtalya Bankası / Consob) bildirimde bulunulması
La EURC stabilcoini ödemeler için kullanılır ve Circle tarafından verilen bir EMT ile MiCA lisansı olup pazarın daha fazla uyumluluğunu gerektirmez.
GDPR ve Gizlilik: Zincir İçi Tüketici Verileri
Akıllı sayaçlardan gelen veriler (ne kadar kWh, ne zaman, hangi zaman profiliyle ürettiğim) kişisel veriler GDPR uyarınca (CJEU kararı C-434/16, Nowak). Bu Blockchain ile temel bir paradoks yaratıyor: zincirdeki veriler değişmez, ancak GDPR "unutulma hakkını" garanti eder (Madde 17). Nasıl çözüldü?
Tasarım Yoluyla Gizlilik: ZK Kanıtlarıyla Zincir Dışı Mimari
# privacy/zkp_meter_proof.py
# Zero-Knowledge Proof per dati smart meter: dimostra che hai prodotto X Wh
# senza rivelare il profilo dettagliato di produzione.
# Usa circom + snarkjs via Python binding
"""
Schema del circuito ZK (pseudocodice circom):
template MeterProductionProof() {
// Input privati (non rivelati on-chain)
signal private input raw_readings[96]; // 15-min interval per 24h
signal private input meter_secret; // segreto del meter (HMAC key)
// Input pubblici (on-chain verificabili)
signal input total_wh_claimed; // Wh che il prosumer dichiara
signal input meter_id_hash; // hash dell'ID meter (anonimizzato)
signal input day_timestamp; // giorno a cui si riferisce
// Output: prova che sum(raw_readings) == total_wh_claimed
signal output valid;
var sum = 0;
for (var i = 0; i < 96; i++) {
sum += raw_readings[i];
}
// Vincolo: la somma delle letture private = il totale dichiarato
sum === total_wh_claimed;
valid <== 1;
}
"""
from py_snark import Prover, Verifier
import hashlib
import json
class ZKMeterProver:
"""
Genera prove ZK per dati smart meter.
Il prosumer dimostra al contratto che ha prodotto X Wh
senza rivelare il profilo temporale delle sue letture.
"""
def __init__(self, circuit_wasm: str, zkey: str):
self.prover = Prover(circuit_wasm, zkey)
self.verifier = Verifier()
def generate_proof(
self,
raw_readings: list[float], # letture ogni 15 minuti (privati)
meter_id: str, # ID meter (privato)
day_timestamp: int # timestamp del giorno (pubblico)
) -> dict:
"""
Genera una prova ZK che attesta la produzione totale senza
rivelare le singole letture.
"""
total_wh = int(sum(raw_readings))
meter_id_hash = hashlib.sha256(meter_id.encode()).hexdigest()
# Input privati (non escono dal dispositivo del prosumer)
private_inputs = {
"raw_readings": [int(r) for r in raw_readings],
"meter_secret": int(hashlib.md5(meter_id.encode()).hexdigest(), 16) % (2**253)
}
# Input pubblici (inviati on-chain con la prova)
public_inputs = {
"total_wh_claimed": total_wh,
"meter_id_hash": int(meter_id_hash, 16) % (2**253),
"day_timestamp": day_timestamp
}
proof = self.prover.generate_proof(
private_inputs=private_inputs,
public_inputs=public_inputs
)
return {
"proof": proof.to_solidity(), # formato per verifica on-chain
"public_inputs": public_inputs,
"total_wh": total_wh
}
def verify_proof(self, proof: dict, public_inputs: dict) -> bool:
"""Verifica la prova (normalmente eseguita dal contratto Verifier on-chain)."""
return self.verifier.verify(proof["proof"], public_inputs)
# ====== CONTRATTO VERIFIER (Solidity, generato da snarkjs) ======
# Il contratto Groth16Verifier.sol e generato automaticamente da:
# $ snarkjs zkey export solidityverifier circuit_final.zkey verifier.sol
#
# Nel contratto EnergyMarketplace, il mint avviene solo se la prova e valida:
#
# function mintWithZKProof(
# uint[2] calldata _pA,
# uint[2][2] calldata _pB,
# uint[2] calldata _pC,
# uint[3] calldata _pubSignals // [total_wh, meter_hash, timestamp]
# ) external {
# require(groth16Verifier.verifyProof(_pA, _pB, _pC, _pubSignals), "ZK: prova non valida");
# uint256 totalWh = _pubSignals[0];
# address producer = meterHashToProducer[bytes32(_pubSignals[1])];
# _mintEnergy(producer, totalWh, ...);
# }
Blockchain ve GDPR'ye ilişkin EDPB 2025 Kılavuzları
14 Nisan 2025'te,Avrupa Veri Koruma Kurulu (EDPB) yayınladı Blockchain ve GDPR ile ilgili güncellenmiş yönergeler. P2P enerji ticaretinin kilit noktaları:
- Zincirdeki kişisel veriler: kimlik karmaları, gerçek kişilere bağlı cüzdan adresleri ve tüketim verilerinin tamamı kişisel verilerdir; bunlar asla zincir üzerinde açık metin olarak yazılmamalıdır
- Takma ad kullanma: Ethereum adreslerini takma ad olarak kullanmak kabul edilebilir cüzdan kimliği eşlemesi sınırlı erişimle zincir dışında tutulursa
- Unutulma hakkı: Zincir dışı veri silme + zincir içi geçersiz kılma ile uygulanabilir token sayısı (yakma + sertifikanın iptali) - blockchain işlemi değişmez ancak anlamsız kalır
- Veri denetleyicisi: Blockchain'e sahip bir CER ve onu yöneten kuruluş için Oracle ve cüzdan kimliği eşlemesi - büyük ölçekte veri işleniyorsa bir DPO atanmalıdır
Örnek Olay: Brooklyn Mikro Şebeke ve CER Italiana
Brooklyn Mikro Şebeke - LO3 Enerji (2016-2024)
Il Brooklyn Mikro ŞebekesiLO3 Energy'nin ortaklığıyla öncü bir proje Siemens ile tarihteki ilk P2P enerji ticareti pilot projesiydi. Etkinleştirildi 2016 yılında Brooklyn'in (New York) Park Slope semtinde 60'tan az üreten tüketici yer alıyordu başlangıçta yüzlerce sanal katılımcıya ulaştı.
Brooklyn Mikro Şebekesinden Öğrenilen Dersler
- Temel düzenleme sorunu: ABD'de kamu hizmetleri tekele sahip elektrik satışı yasal. Teknoloji hazır olsa bile katılımcılar yalnızca takas Yenilenebilir Enerji Sertifikaları (REC), fiziksel enerji değil. “P2P” ticareti tamamen finansaldı.
- Ekserji Platformu: LO3 izin verilen bir blockchain katmanı geliştirdi enerjiye özel. Avrupa'da Energy Web Foundation da benzer bir yaklaşım izledi Enerji Web Zinciri ile düzenlemeye tabi kullanım durumları için optimize edilmiştir.
- Akıllı sayaç entegrasyonu: asıl darboğaz blockchain değildi ancak eski akıllı sayaçlarla arayüz oluşturma. Amerikan sayaçları desteklemiyordu gerçek zamanlı okumalar - sınır Avrupa'da AB akıllı sayaç direktifiyle çözüldü.
- Kullanıcı deneyimi: Kripto cüzdanının karmaşıklığı bir engeldi teknik olmayan üreten tüketiciler için çok büyük. Çözüm: platform tarafında tutulan cüzdanlar Bir bankacılık uygulamasına benzer kullanıcı arayüzü ile.
Vaka Çalışması: Perugia'daki Pilot CER - Varsayımsal Senaryo
Blockchain üzerinde P2P ticaretine sahip bir İtalyan CER için somut bir kullanım durumu modelliyoruz. GSE 2025 çerçevesinin gerçek parametrelerine dayanmaktadır:
# simulation/cer_p2p_simulation.py
# Simulazione trading P2P per CER italiana con 50 prosumer e 200 consumer
import numpy as np
import pandas as pd
from datetime import datetime, timedelta
import matplotlib.pyplot as plt # per visualizzazione risultati
# ======= PARAMETRI CER PILOTA PERUGIA =======
CER_CONFIG = {
"name": "CER Perugia Centro - Cabina 042",
"cer_code": "IT-CER-PG-042",
"max_capacity_kw": 500, # 500 kW capacità cabina
"n_prosumer": 50, # 50 prosumer con FV
"n_consumer": 200, # 200 soli consumatori
"avg_fv_kw": 4.5, # media 4.5 kWp per prosumer (tipico residenziale)
"incentivo_gse": 0.110, # 110 EUR/MWh tariffa premio GSE
"p2p_price_range": (0.07, 0.12), # range prezzo P2P in EUR/kWh
"grid_price_sell": 0.05, # prezzo vendita a rete (in assenza P2P)
"grid_price_buy": 0.30, # prezzo acquisto da rete (consumatori)
}
def simulate_solar_production(n_prosumer: int, avg_kw: float, hours: int = 24) -> np.ndarray:
"""Simula produzione FV per un giorno estivo tipico (luglio, Perugia)."""
# Profilo solare tipico: curva gaussiana centrata alle 13:00
time_hours = np.linspace(0, 24, hours)
solar_curve = np.maximum(0, avg_kw * np.exp(-((time_hours - 13) ** 2) / (2 * 3**2)))
# Variabilità tra prosumer (orientamento, ombreggiature)
productions = np.random.normal(
loc=solar_curve,
scale=solar_curve * 0.15, # 15% variabilità
size=(n_prosumer, hours)
)
return np.maximum(0, productions)
def simulate_consumption(n_consumer: int, hours: int = 24) -> np.ndarray:
"""Simula consumo residenziale tipico (profilo ARERA 2025)."""
# Profilo bimodale: picco mattina (7-9) e sera (18-22)
time_hours = np.linspace(0, 24, hours)
base_kw = 0.8
morning_peak = 0.4 * np.exp(-((time_hours - 8) ** 2) / (2 * 1.5**2))
evening_peak = 0.6 * np.exp(-((time_hours - 20) ** 2) / (2 * 2**2))
profile = base_kw + morning_peak + evening_peak
consumptions = np.random.normal(
loc=profile,
scale=profile * 0.20,
size=(n_consumer, hours)
)
return np.maximum(0, consumptions)
def run_p2p_market_simulation() -> dict:
"""Esegui simulazione mercato P2P per 1 giorno."""
config = CER_CONFIG
hours = 24
productions = simulate_solar_production(config["n_prosumer"], config["avg_fv_kw"], hours)
consumptions = simulate_consumption(config["n_consumer"], hours)
# Surplus aggregato dei prosumer per ora
surplus_per_hour = productions.sum(axis=0) * 1000 # in Wh
# Domanda aggregata dei consumer per ora
demand_per_hour = consumptions.sum(axis=0) * 1000 # in Wh
# Simula P2P matching per ogni ora
results = []
total_p2p_wh = 0
total_p2p_value = 0
total_grid_wh = 0
for h in range(hours):
surplus = surplus_per_hour[h]
demand = demand_per_hour[h]
# Quantità P2P = minimo tra surplus e domanda
p2p_wh = min(surplus, demand)
p2p_price = np.random.uniform(*config["p2p_price_range"]) / 1000 # EUR/Wh
# Valore P2P transazione
p2p_value = p2p_wh * p2p_price
# Energia residua venduta a rete o acquistata da rete
grid_sell_wh = max(0, surplus - p2p_wh) # eccesso non assorbito P2P
grid_buy_wh = max(0, demand - p2p_wh) # deficit non coperto P2P
total_p2p_wh += p2p_wh
total_p2p_value += p2p_value
total_grid_wh += grid_buy_wh
results.append({
"hour": h,
"surplus_wh": surplus,
"demand_wh": demand,
"p2p_wh": p2p_wh,
"p2p_price": p2p_price * 1000, # EUR/kWh per output
"p2p_value_eur": p2p_value,
"grid_sell_wh": grid_sell_wh,
"grid_buy_wh": grid_buy_wh
})
# Calcola risparmio P2P vs scenario tradizionale
# Scenario tradizionale: tutto venduto a rete, tutto acquistato da rete
traditional_value_sell = total_p2p_wh * config["grid_price_sell"] / 1000
traditional_value_buy = total_p2p_wh * config["grid_price_buy"] / 1000
p2p_benefit = total_p2p_value - traditional_value_sell # venditore guadagna più
# Carbon credits: 400 gCO2/kWh mix italiano
co2_avoided_kg = total_p2p_wh * 400 / 1_000_000 # kg CO2
summary = {
"total_p2p_wh": total_p2p_wh,
"total_p2p_kwh": total_p2p_wh / 1000,
"total_p2p_value": total_p2p_value,
"average_p2p_price": total_p2p_value / total_p2p_wh * 1000 if total_p2p_wh > 0 else 0,
"n_transactions": len([r for r in results if r["p2p_wh"] > 0]),
"p2p_benefit_eur": p2p_benefit,
"co2_avoided_kg": co2_avoided_kg,
"gse_incentivo": total_p2p_wh * config["incentivo_gse"] / 1_000_000, # EUR
"hourly_data": results
}
return summary
# Esegui simulazione
results = run_p2p_market_simulation()
print(f"=== Simulazione CER Perugia - 1 giorno estivo ===")
print(f"Energia P2P tradita: {results['total_p2p_kwh']:.1f} kWh")
print(f"Valore mercato P2P: {results['total_p2p_value']:.2f} EUR")
print(f"Prezzo medio P2P: {results['average_p2p_price']:.4f} EUR/kWh")
print(f"Beneficio vs rete: {results['p2p_benefit_eur']:.2f} EUR")
print(f"CO2 evitata: {results['co2_avoided_kg']:.1f} kg")
print(f"Incentivo GSE: {results['gse_incentivo']:.2f} EUR")
print(f"Transazioni simulate: {results['n_transactions']}")
Tipik Simülasyon Sonuçları
| Metrik | P2P Blok Zinciri Senaryosu | Geleneksel Senaryo (GSE) | P2P avantajı |
|---|---|---|---|
| Paylaşılan enerji/gün | 850-1.200 kWh | 850-1.200 kWh | Aynı (fiziksel olarak) |
| Üreten tüketiciler tarafından ödenen ortalama fiyat | 90-100 Avro/MWh | 50 EUR/MWh (yalnızca GSE ödülü) | Üreten tüketiciye +40-50 EUR/MWh |
| Tüketicilerin ödediği ortalama fiyat | 90-100 Avro/MWh | 300+ EUR/MWh (perakende tarifesi) | -200 EUR/MWh tüketiciye |
| Yerleşim hızı | < 30 saniye | 30-90 gün | Anında likidite |
| Aracılık maliyetleri | %0,5 (CER hazine ücreti) | %5-15 (kamu hizmetleri + DSO) | Tasarruf %4-14 |
| Şeffaflık | Zincir üzerinde, doğrulanabilir | GSE portalı gecikti | Gerçek zamanlı denetimler |
Tokenomik: Enerji Tokenları, Karbon Kredileri ve Üreten Tüketici Teşvikleri
Sürdürülebilirliği sağlamak için iyi tasarlanmış ve temel bir tokenomik sistemi P2P pazarının ekonomisi ve tüm katılımcılar için doğru teşvikler.
Pazaryeri Token Yapısı
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
/**
* @title CarbonCreditToken (CCT)
* @notice Token che rappresenta 1 kg di CO2 evitata dalla produzione rinnovabile.
* Emesso automaticamente a ogni mint di EWT (proporzionale alla fonte).
* Vendibile su carbon credit marketplace (Toucan Protocol, Verrà Bridge).
*/
contract CarbonCreditToken is ERC20, AccessControl {
bytes32 public constant CCT_MINTER = keccak256("CCT_MINTER");
// Fattori di emissione per fonte (gCO2 evitata per Wh)
mapping(string => uint256) public co2FactorPerWh;
constructor() ERC20("Carbon Credit Token", "CCT") {
_grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
// Fattori ISPRA 2024 aggiornati
co2FactorPerWh["SOLAR"] = 400; // gCO2/kWh evitata vs mix italiano
co2FactorPerWh["WIND"] = 400;
co2FactorPerWh["HYDRO"] = 350;
co2FactorPerWh["BIOMASS"] = 100; // netta (ciclo carbonio)
}
/**
* @notice Minta CCT proporzionalmente alla produzione rinnovabile.
* 1 CCT = 1 kg CO2 evitata = 0.001 TCO2 (tonne CO2)
*
* @param recipient Prosumer che riceve il carbon credit
* @param whProduced Energia prodotta in Wh
* @param sourceType Tipo fonte per calcolo fattore emissione
*/
function mintCredits(
address recipient,
uint256 whProduced,
string calldata sourceType
) external onlyRole(CCT_MINTER) {
uint256 factor = co2FactorPerWh[sourceType];
require(factor > 0, "CCT: fonte sconosciuta");
// gCO2 / 1000 = kgCO2 = numero di CCT
uint256 cctAmount = whProduced * factor / 1_000_000; // gCO2 -> kgCO2
if (cctAmount > 0) {
_mint(recipient, cctAmount * 1e18); // 18 decimali per compatibilità ERC-20
}
}
}
// ======= TOKENOMICS SUMMARY =======
/*
Flusso token per un prosumer con 4 kWp FV che produce 20 kWh in un giorno estivo:
1. PRODUZIONE: Smart meter misura 20.000 Wh esportati
2. MINT EWT: Oracle minta 20.000 EWT (1 EWT = 1 Wh)
3. MINT CCT: 20.000 Wh * 400 gCO2/kWh / 1.000.000 = 8 CCT (8 kg CO2 evitata)
4. TRADE P2P: Vende 15.000 EWT @ 0.095 EUR/kWh sul marketplace
= 1.425 EUR gross
- fee 0.5% = 7.12 EUR alla treasury CER
= 1.417.88 EUR netti al prosumer
5. RESIDUO: 5.000 EWT venduti a rete tramite GSE
= 0.05 EUR/kWh * 5 kWh = 0.25 EUR + incentivo GSE 0.11 EUR/kWh = 0.55 EUR
6. CARBON CREDITS: 8 CCT venduti su Toucan Protocol (mercato volontario ~12 USD/tCO2)
= 8 kg * 0.012 USD/kg = 0.096 USD
TOTALE GIORNALIERO: 1.417.88 + 0.55 + 0.096 = ~1.96 EUR (vs 0.55 EUR senza P2P)
Beneficio P2P: +255% rispetto al solo incentivo GSE
Nota: prezzi illustrativi basati su stime di mercato 2025
*/
Performans ve Ölçeklendirme: Gerçek bir CER için Verim
Bir CER için saniyede kaç işlem gereklidir? Rakamlar şaşırtıcı derecede yönetilebilir: 1.000 üreten tüketiciye sahip büyük bir CER bile her 15 dakikada en fazla birkaç düzine işlem gerçekleştiriyor. Binlerce CER'ye sahip bölgesel toplayıcılardan bahsederken gerçek ölçeklenebilirlik gereksinimi ortaya çıkıyor.
Gerekli Verimin Tahmini
| Senaryo | Katılımcılar | Ticaret/15dk | TPS gerekli | Blockchain'e uygun |
|---|---|---|---|---|
| CER küçük | 50 üreten tüketici + 200 tüketici | 10-50 | < 0,1 | Herhangi bir L2 |
| CER büyük | 500 üreten tüketici + 2.000 tüketici | 100-500 | < 1 | Çokgen, Arbitrum, EWC |
| Bölgesel toplayıcı | 100 CER, ~50.000 kullanıcı | 10.000-50.000 | < 100 | EWC + Özel Katman 2 |
| Ulusal platform | 10.000 CER, 5 milyon kullanıcı | 1 milyonun üzerinde | 1.000+ | Toplama + durum kanalları |
Optimizasyon: Mikro Ticaret için Devlet Kanalları
// State Channel per trading ad alta frequenza tra due prosumer
// Solo apertura e chiusura del canale vanno on-chain (2 transazioni totali)
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
/**
* @title EnergyStateChannel
* @notice Canale bidirezionale per trading energetico ad alta frequenza.
* Apertura e chiusura on-chain, transazioni intermedie off-chain.
* Adatto per accordi bilaterali fissi (es. coppia di vicini).
*/
contract EnergyStateChannel {
address public partyA; // prosumer (venditore abituale)
address public partyB; // consumer (acquirente abituale)
uint256 public channelId;
uint256 public expiresAt;
// Bilanci depositati nel canale
uint256 public depositA_EWT; // EWT bloccati da partyA
uint256 public depositB_EUR; // EUR (stablecoin) bloccati da partyB
bool public settled;
struct ChannelState {
uint256 nonce; // sequenza crescente (anti-replay)
uint256 aBalance_EWT; // EWT spettanti ad A
uint256 bBalance_EUR; // EUR spettanti a B
bytes sigA; // firma di A
bytes sigB; // firma di B
}
event ChannelOpened(address partyA, address partyB, uint256 channelId);
event ChannelClosed(uint256 channelId, uint256 finalNonce);
/**
* @notice Chiude il canale con lo stato finale concordato off-chain.
* Entrambe le parti hanno firmato lo stato finale.
* Esegui on-chain solo al termine della sessione (giornaliero/settimanale).
*/
function closeChannel(ChannelState calldata finalState) external {
require(!settled, "SC: canale già chiuso");
require(block.timestamp <= expiresAt, "SC: canale scaduto");
// Verifica firme di entrambe le parti
bytes32 stateHash = keccak256(abi.encodePacked(
channelId,
finalState.nonce,
finalState.aBalance_EWT,
finalState.bBalance_EUR
));
require(_verifySignature(stateHash, finalState.sigA, partyA), "SC: firma A invalida");
require(_verifySignature(stateHash, finalState.sigB, partyB), "SC: firma B invalida");
settled = true;
// Distribuisci i token secondo lo stato finale concordato
// (trasferimento EWT e EUR dai deposit alle parti)
// ... implementation ...
emit ChannelClosed(channelId, finalState.nonce);
}
function _verifySignature(
bytes32 hash,
bytes memory sig,
address expectedSigner
) internal pure returns (bool) {
bytes32 prefixed = keccak256(abi.encodePacked(
"\x19Ethereum Signed Message:\n32", hash
));
(uint8 v, bytes32 r, bytes32 s) = _splitSignature(sig);
return ecrecover(prefixed, v, r, s) == expectedSigner;
}
function _splitSignature(bytes memory sig)
internal pure returns (uint8 v, bytes32 r, bytes32 s)
{
require(sig.length == 65, "SC: firma non valida");
assembly {
r := mload(add(sig, 32))
s := mload(add(sig, 64))
v := byte(0, mload(add(sig, 96)))
}
}
}
Gelecek: Enerji ve Transaktif Enerji için DeFi
Bugün tanımladığımız uygulamalar daha derin bir yakınlaşmanın sadece başlangıcıdır merkezi olmayan finans (DeFi) ile enerji altyapısı arasında. İşte daha fazla yol tarifi önümüzdeki 2-5 yıl için umut verici:
Yükselen Trendler 2025-2030
- DeFi'de Enerji Vadeli İşlemleri: Tokenize PV üretimine ilişkin vadeli işlem sözleşmeleri, Uniswap v4 veya AMM (Otomatik Piyasa Yapıcı) olarak Curve ile. Üreten tüketiciler riskten korunabilir Beklenen üretime ilişkin vadeli işlemleri satarak hava durumu riskini azaltın.
- Karbon Kredisi DeFi (ReFi): Toucan, KlimaDAO ve Flowkarbon gibi protokoller karbon kredileri için zincir üstü pazarlar inşa ediyorlar. EWT ile doğrudan entegrasyon enerji + karbon kredisinin tek bir tokenda toplanmasına olanak tanıyacak.
- İşlemsel Enerji: P2P ticaretinde bir sonraki adım - otomasyon Portföy optimizasyonunu özerk bir şekilde yöneten yapay zeka ile eksiksiz soru/cevap üreten tüketicinin enerjisi. Optimum pazar teklifi için akıllı sözleşme + makine öğrenimi.
- V2G P2P: Pazarın aktif katılımcıları olarak elektrikli araçlar enerjik. Araç, fiyat yükseldiğinde otomatik olarak aküden enerji satıyor. P2P, kullanıcı tarafından belirlenen eşiği aşıyor.
- AB Enerji Veri Alanı: Gaia-X projesi ve Avrupa Enerji Veri Alanı Birlikte çalışabilir enerji veri paylaşımına yönelik altyapıyı oluşturmak. Blockchain bir güven ve uzlaşma katmanı olarak hizmet edebilir.
Sonuçlar ve Sonraki Adımlar
Blockchain üzerinde P2P enerji ticareti en somut ve olgun uygulamalardan birini temsil ediyor saf finans sektörü dışında blockchain teknolojisinin. bu konuyla ilgili değil spekülasyon: Bu makalede gördüğümüz Sağlamlık sözleşmeleri uygulanabilir bugün Energy Web Chain veya Polygon zkEVM'de, işlem başına 0,05 Euro'nun altında gaz maliyetleriyle.
199/2021 sayılı Kanun Hükmünde Kararname ve CER'ler için 2,2 milyar PNRR ile İtalyan düzenleyici çerçevesi, bu alanda inşaat yapmak isteyen geliştiriciler için gerçek bir fırsat yaratıyor. CER'ler tanınmış tüzel kişilerdir, kamu teşviklerine ve RED III direktifine erişime sahiptirler 2025-2026'da haklarını daha da güçlendiriyor.
Kısıtlamalar devam ediyor: DSO ile entegrasyon önemsiz olmayan B2B anlaşmalarını gerektiriyor, Enerji tokenlerinin MiCA sınıflandırması yasal dikkatle ele alınmalıdır. ve GDPR, ZK kanıtlarıyla gizliliğe öncelik veren bir tasarımı zorunlu kılar. Ama bu engellerin hiçbiri ve blockchain + enerji alanı becerilerine sahip bir ekip için aşılamaz.
Bu makaleyle şu sonuca varıyoruz: EnergyTech serisi. geçtik enerji geçişinin tüm teknoloji yığını: Akıllı Şebekelerden ve IoT'den DERMS, BMS, Digital Twin, ML aracılığıyla blockchain üzerinde enerji tokenizasyonu yenilenebilir tahmin, V2G, MQTT/InfluxDB, IEC 61850 ve karbon muhasebesi.
Daha fazla bilgi edinmek için kaynaklar
- Enerji Web Vakfı: EWC ve SDK belgeleri - enerjiweb.org/teknoloji/enerji-web-zincir
- OpenZeppelin Sözleşmeleri: güvenli akıllı sözleşme kütüphanesi - docs.openzeppelin.com/contracts
- GSE CACER Portalı: İtalyan CER düzenlemeleri ve teşvikleri - gse.it/servizi-per-te/autoconsumo/comunita-energetiche-rinnovabili
- Kask Belgeleri: test ve dağıtım çerçevesi - hardhat.org/docs
- Çokgen zkEVM: Gizlilik için ZK kanıtlarına sahip L2 - polygon.teknoloji/poligon-zkevm
- Blockchain ile ilgili EDPB Yönergeleri (2025): Blockchain ile ilgili GDPR yönergeleri - edpb.europa.eu/our-work-tools/documents/our-documents
- RED III Direktifi (AB 2023/2413): resmi metin - eur-lex.europa.eu
Fedicocalo.dev'deki İlgili Seriler
- İşletmeler için MLOps: ML modellerinin üretime nasıl geçirileceği enerji tahmini ve talep yanıtı
- Veri ve Yapay Zeka İşi: enerji verileri için veri göl evi mimarisi, Enerji zaman serileri için dbt ve Airbyte ile ETL/ELT
- Yapay Zeka Mühendisliği/RAG: CER sözleşme analizi için LLM e Üreten tüketiciler için sanal asistanlar







