# 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:

1. **Independent attestors** must verify the **same evidence hash** (quorum).
2. A global **One-Claim Ledger** must confirm the evidence hasn’t been used elsewhere.
3. 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)

```solidity
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)

```solidity
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.assertVerified` passes.

```solidity
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).

```solidity
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).

```solidity
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)` and `emit 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.
