Smart Contract Implementation
What these contracts do. EDMA’s contracts don’t run consensus—they enforce application-layer truth. Before any mint, settlement, or access:
Independent attestors must verify the same evidence hash (quorum).
A global One-Claim Ledger must confirm the evidence hasn’t been used elsewhere.
If either fails, the call reverts.
Proof mints & conversions are gas-only on Base. Settlement uses $EDM with enforced fee/burn.
Architecture
Base (L2): all proof mints/conversions (gas-only) and marketplace flows.
Ethereum (L1): governance, fee schedule, registries, and final lineage anchoring.
PoV modules on-chain:
PoVAttestorRegistry – roles & quorum policy.
OneClaimLedger – route-agnostic
exclusivity (claimId).PoVGate – the single entrypoint for mint/settle/access (asserts quorum/equality/exclusivity).
Asset & Settlement contracts – proofs (ETT, EMT, Carbon NFTs, Energy NFT), reward (CLE), and EDM-settled actions.
Upgradeability. Registry/Gate may be upgradeable behind a timelocked multisig. OneClaimLedger is intentionally simple & immutable. Changes are published with on-chain change logs.
Access control. Role-based via AccessControl (e.g., GOVERNANCE_ROLE, MINTER_ROLE, TREASURY_ROLE). Critical ops require multisig.
Core PoV modules (interfaces / sketches)
One-Claim Ledger (exclusivity)
interface IOneClaimLedger {
function firstClaimer(bytes32 claimId) external view returns (address);
function reserve(bytes32 claimId) external returns (bool);
function finalize(bytes32 claimId) external;
function release(bytes32 claimId) external;
event Finalized(bytes32 indexed claimId, address indexed claimer);
event Released(bytes32 indexed claimId, address indexed claimer);
}PoV Gate (the gatekeeper)
contract PoVGate {
struct Params {
bytes32 batchId; bytes32 deviceId; uint64 startTs; uint64 endTs;
uint128 quantityWh; bytes32 evidenceHash; bytes32 meterUID;
bytes32[] verificationUIDs; // EAS attestations (roles include AUDITOR)
}
function assertVerified(Params calldata p) external {
// 1) compute route-agnostic claimId (deviceId,start,end,quantityWh,evidenceHash)
// 2) OneClaimLedger.reserve(claimId) -> revert ALREADY_CLAIMED on conflict
// 3) load MeterReadingBatch, check schema & equality to p.evidenceHash/window/qty
// 4) verify quorum: ≥minTotal, ≥minDistinctRoles, includes AUDITOR, all unrevoked/valid
// 5) emit PoVPassed(claimId,...); OneClaimLedger.finalize(claimId)
}
event PoVPassed(bytes32 indexed claimId, bytes32 meterUID, bytes32[] verUIDs);
}Asset contracts (proofs are non-transferable; mints are gas-only on Base)
ETT — Energy Tracking Token (proof, non-transferable)
Definition: 1 ETT per 10 kWh of verified generation.
Nature: evidence-only, non-transferable (soulbound). Stores
claimId,evidenceHash, and attestation links.Mint rule: only after
PoVGate.assertVerifiedpasses.
contract ETTProof is AccessControl {
bytes32 public constant GATE_ROLE = keccak256("GATE_ROLE");
mapping(bytes32 => address) public proofOwner; // claimId -> owner
function mintETT(address to, bytes32 claimId) external onlyRole(GATE_ROLE) {
require(proofOwner[claimId] == address(0), "ALREADY_MINTED");
proofOwner[claimId] = to;
}
// no transfer functions; proofs are non-transferable by design
}Energy NFT — 1 MWh certificate (from 100 ETT) or Carbon conversion
Mint condition: account has 100 ETT worth of the same evidenced batch (or policy-compliant grouping) OR conversion path to Carbon Credit NFTs where standards allow.
Gas-only mint. Stores provenance (batch/window/device, evidence hash, attestation refs).
contract EnergyNFT is ERC721, AccessControl {
function mintFromETT(address to, bytes32[] calldata claimIds) external onlyRole(GATE_ROLE) {
// verify claimIds sum to 1 MWh from the same evidence scope, then _safeMint(to, tokenId)
}
}Carbon Credit NFT — 1 tCO₂ (direct proof)
Mint condition: verified reduction = 1 tCO₂ with methodology metadata.
Gas-only mint. Supports on-chain retirement (non-transferable after retire).
EMT — Event/Milestone Token (proof, non-transferable)
Use: on-board / customs-cleared / delivered / assay; triggers staged releases.
Mint: gas-only, after Gate pass.
CLE — Clean Energy Coin (separate reward)
Mint rule: per verified MWh (policy ties to Energy NFT mint or batch tally).
Important: not derived from ETT; it’s a separate reward token. Tradable (ERC-20).
Settlement & Fee Router (EDM-based)
Where fees apply: trades, retirements, and milestone payout releases.
Where they don’t: all proof mints & conversions (ETT, Energy NFT, Carbon NFT, EMT) are gas-only on Base.
Energy & Carbon: 4.0% total (2% buyer + 2% seller).
Commodity milestones: 0.5% per tranche (caps: $5k ≤$1M, $12.5k $1–5M, $25k >$5M).
Split: 50% burned, 50% to treasury (rebates can reduce the treasury half only; burn is immutable).
contract FeeRouter is AccessControl {
IERC20 public immutable EDM;
address public immutable TREASURY;
function settleTrade(address payer, uint256 tradeValue, uint16 bpsTotal) external {
uint256 fee = (tradeValue * bpsTotal) / 10_000; // bpsTotal = 400 for 4%, 50 for 0.5%
uint256 burnAmt = fee / 2; uint256 treasAmt = fee - burnAmt;
EDM.transferFrom(payer, address(this), fee);
EDM.burn(burnAmt); // EDM must expose burn/burnFrom
EDM.transfer(TREASURY, treasAmt);
}
}No auto-swap: if the payer lacks EDM, settlement reverts. This keeps EDM’s utility real.
Revocation & Rectification (first-class)
If a counted verification is revoked/expired and quorum drops post-mint, dependent assets must be flagged/frozen/burned per policy until rectified.
Asset contracts expose
flagRevoked(claimId)andemit PoVFlagged / PoVRectified. Lineage is append-only; history is never overwritten.
Oracles, evidence, and storage
Attestations: EAS-compatible
(MeterReadingBatch.v1, Verification.v1, RevocationReason.v1).Evidence: canonical JSON (sorted/minified, fixed units) →
evidenceHash = SHA-256(blob).Storage: raw artifacts off-chain (S3/IPFS/partner vaults) with signed URIs; hashes on-chain.
Ingestion: Chainlink-class transports and direct operator APIs feed attestation pipelines.
Interoperability & L2 stance
Primary home is Ethereum/Base. Where regulated programs require mirroring to legacy registries, watchers reconcile external retirements/issuances on-chain without violating One-Claim. Cross-chain mirrors (with canonical “first-seen” rules) are a future extension, not a dependency.
Last updated
