Green Proofs Contracts
Last updated
Last updated
The GreenProof project by the Energy Web Foundation aims to provide a robust and flexible platform for issuing, managing, and verifying digital proofs representing clean energy certificates. To achieve this, the project employs the Diamond pattern, an innovative smart contract architecture based on the EIP-2535 standard. This document presents an overview of the Diamond pattern, its benefits for the GreenProof project, and its implementation within the core module.
A modular and upgradable smart contract architecture
The Diamond pattern allows one main contract (called the diamond) to have an unlimited number of functions by separating the contract's functions into several smaller contracts called "facets." The diamond acts as a proxy contract that routes function calls to the appropriate facets (i.e., the actual subcontracts implementing the desired logic). This architecture enables a higher degree of modularity and upgradability for smart contracts by allowing users to trigger any on-chain function of any component using a single, stable smart contract address.
By implementing the EIP-2535 standard, the GreenProof core module provides an upgradable proxied system, wherein organized groups of smart contracts (facets and libraries) encapsulate different sets of functionalities into modules that can be plugged into the main GreenProof Diamond. This architecture unlocks the ability to efficiently and granularly compose the smart contract system based on the use case, allowing for seamless integration and updating of various components, enhancing the overall flexibility and adaptability of the system.
Benefits of the Diamond Pattern for GreenProof This smart contract architecture aims to achieve the following strategic objectives :
Scalability: The modular nature of the Diamond pattern allows GreenProof to easily scale as more components and features are added or updated.
Flexibility: Facets can be added, replaced, or removed granularly, enabling GreenProof to adapt to changing requirements in the green energy certification landscape.
Maintainability: The Diamond pattern promotes a clean separation of concerns, making it easier to maintain, troubleshoot, and update the GreenProof smart contracts system.
To implement the Diamond pattern, the GreenProof smart contracts modules are composed of the following components:
Voting component : VotingFacet.sol implements the voting logic, allowing the worker nodes to achieve consensus on data to be certified.
Issuer component : IssuerFacet.sol encapsulates the issuance logic, allowing the verification and creation of green certificates. Each certificate is represented by an ERC1155 token bound to the proof of green, resulting from the workers' decentralized consensus.
Certificate Manager component: ProofManagerFacet.sol handles certificate management features, such as claiming, revocation, inspection, and verification.
Below is the detailed Github repository structure:
Additionally, the GreenProof core module uses the solidState library, which offers utility contracts implementing the EIP-2535 standard specification. The following contract libraries are particularly useful:
The DiamondCutFacet.sol
contract is a facet handling all the upgrading logic. (A cut, in the diamond’s industrie, is an action resulting in new facets creation, removal. This facet will be used to add, replace or remove granularly logic to the greenProof Diamond)
A dedicated facet is provided with the OwnershipFacet
contract, which handles the implementation of the IEP-173 standard for the Diamond ownership.
A DiamondLoupeFacet.sol
contract provides all standard loupe functions for showing what facets and functions the diamond has.
We want to estimate, for each action taking place on smart contract, the cost estimation. Smart contract transaction costs can be measured on two different metrics:
a predictable, static computation gas consumption, which measures the computational effort required on the Ethereum Virtual Machine to execute the transaction logic
a dynamic cost associated to the base fee + priority transaction fees, provided to the validator nodes. This dynamic metric is related to the offer/supply balance on the network; the more traffic occurs, the highest are the required fees to have the transaction processed in priority.
The current document provides the average gas consumption for greenProof smart contracts. It uses the Gas Reporter plugin on the hardhat development environment. This plugin is useful in estimating how much our contracts will cost to deploy to production, and how much each transaction will cost once deployed. It can be enabled during unit testing.
A separate set of unit tests has been implemented to target each function and report the gas consumption. Below are the result of this estimation.
Contracts deployed once (those contracts will be deployed only once by EnergyWeb and will act as on-chain libraries/modules for each new green proof instance)
E.g. for SAF and 24/7 app, we would deploy the facets for each of them once. We deploy all facets once, and any new projects which require those logic/feature will just have to connect to those deployed smart-contracts. For that purpose we need to keep track of the addresses of those facets in our backend system I guess, since we will have to reuse them.
Contract to deploy | Avg unit of gas consumption txfees = (amount of gas) * gasPrice) | AVG cost (in USD) | Cost percentage |
VotingFacet |
|
|
|
IssuerFacet |
|
|
|
ProofManagerFacet |
|
|
|
GreenproofInit |
|
|
|
Note about cost percentage: (For each transaction, the user provides into the gasLimit parameter the maximum amount of gas allowed to execute this transaction. So here the % is out of this gasLimit parameter.)
Contracts deployed each time we want to set a new greenProof instance
This greeproof contract will be deployed only once for each project (i.e Quintrace). This is the main contract which will never be upgraded, and which purpose is, among others, to forward / redirect each transaction to the right sub-smartcontract (i.e facet) implementing the required logic. This greenproof contract has an internal registry mapping each function to the address of the contract which should be reached to execute this function.
So anytime we want to make an upgrade, we will send a transaction to that greenproof contract in order to change the address of the logic-contract inside the mapping / registry
At the end of the day, the greenproof contract won’t be redeployed.
(Note that we may need to deploy a new logic-contract in order to get the new address to store into the greenproof main contract. But the main one is not redeployed)
Contract to deploy | Avg unit of gas consumption | AVG cost (in USD) | Cost percentage |
Greenproof (main proxy) |
|
|
|
The below methods will be called to perform actions on certificates.
Methods | Avg unit of gas consumption | AVG cost (in USD) |
requestProofIssuance |
|
|
discloseData |
|
|
safeTransferFrom |
|
|
claimProof |
|
|
claimProofFor |
|
|
revokeProof |
|
|
Voting component
The below methods will be called to perform vote related actions.
Methods | Gas consumption | AVG cost (in USD) |
addWorker |
|
|
removeWorker |
|
|
Vote |
|
|
On the smart contract level we are seeking for patterns which:
minimize readings and writings operation from and to the EVM storage, especially in loops
when possible, tightly pack data in structures
when possible convert external function calls into internal functions since calling external functions raises costs.
On the compiler level, the optimizer option can be enabled on the configuration file (hardhat.config or truffle.config). This option requires to set a runs
parameter.
From the solidity documentation:
The number of runs specifies roughly how often each opcode of the deployed code will be executed across the life-time of the contract. This means it is a trade-off parameter between code size (deploy cost) and code execution cost (cost after deployment).
Since we are using a highly modularized smart contract system (Diamond Proxy Pattern), our contracts bytecode is very reduced, which allows a high runs setting on the optimizer. (More infos on how the EIP 2535 pattern helps in code optimization).