carbon
EU/ETS compliant carbon credits token exchange
Created on 10th May 2026
•
carbon
EU/ETS compliant carbon credits token exchange
The problem carbon solves
Compliance carbon is an 800 billion euro per year market. Almost none of it trades on chain. The voluntary side tried (Toucan, Klima, MOSS, C3) and crashed in 2022 when Verra banned tokenization. Compliance carbon is 375 times bigger but it's been off limits because it needs a regulator, and permissionless DEXes don't accommodate one.
Carbon DEX is built for that. A regulator-supervised on-chain spot market for tokenized EU ETS allowances. Six contracts, all live on Sepolia, all Sourcify verified.
What's in it:
A whitelist contract. Only addresses the EU ETS Authority has verified can hold or trade the carbon credit token. Transfers revert otherwise.
A custom constant-product AMM. Same x*y=k math as Uniswap V2, written fresh in Solidity 0.8.x so the whitelist and pause hooks integrate cleanly. 0.3% fee accrues to LPs.
A regulator contract. Holds the privileged roles on the other contracts. Can mint allowances, freeze accounts, pause the DEX, audit any event. Cannot trade, take fees, or front-run. EVM atomicity means there's no in-flight intercept window anyway, which actually matches real EU ETS. Reg. 2019/1122 Art. 30 lets national administrators suspend account access; the Union Registry has no transfer reversal primitive even after the 2010-11 phishing thefts.
ENS used as the compliance registry layer. Each verified entity has text records on its subdomain: legal name, ticker, year-by-year compliance status with ESMA references when there's a violation. The regulator wallet is the controller, so attestations are signed by the authority. ESMA already publishes name-and-shame on their website; we put it on chain so it's queryable.
Three frontend roles, all wired live through viem to the deployed contracts. Company portal for the verified emitter, regulator console for the authority, public observer for anyone with no wallet required.
The demo flow. Regulator issues 1,000 EUA to Cement Mainz GmbH. Cement sells 200 surplus on the DEX. The AMM matches it against an off-stage counterparty automatically. Cement retires the remaining 800 against verified emissions for the year. The /public closing visual shows 1,000 issued, 800 retired, 200 in circulation. Supply contracted by 80% in one compliance cycle. The cap held.
Challenges we ran into
ERC-1155 to ERC-20 mid-build. Original spec had vintage as a token id under ERC-1155. The CPMM expects ERC-20 pairs natively, so we'd have needed a per-id wrapper. Rewrote CarbonCredit as ERC-20 with vintage, sector, and origin moving to event payloads. Real EU ETS treats EUAs as fungible within a Phase, so this ended up more faithful to the regulation than the original spec was.
Custom CPMM instead of forking Uniswap V2. V2 is on Solidity 0.5.16 and doesn't compose with our 0.8.x stack. Porting was higher risk than just writing the formula. About 150 lines of x*y=k math, with MIN_LIQUIDITY first-LP attack mitigation, ReentrancyGuard, and the canonical min(a,b) LP-mint formula. 12 contract tests plus a K-invariant fuzz test. 72/72 pass.
Alchemy free tier caps eth_getLogs at 10 blocks, not 10,000. We didn't notice for half a session because Promise.all on three parallel getContractEvents calls just rejected, the catch path silently substituted simulation state, and the only signal was a console.error nobody was reading. Swapped the RPC to PublicNode (no auth, no cap, 50k block scan in 0.2 seconds). Lesson: read the free tier docs before architecting around them.
Single-actor demo collapse. Original happy flow had A receive, B buy, B retire, which contracted supply by 20%. Pivoted to single-actor: A receives 1000, A sells 200 surplus, A retires 800. Bigger contraction (80%), no role switching on stage, matches what cap-and-trade actually rewards (efficient operators net-sell). Wrote a B-side bot that mirrors A's trades automatically off-stage so the demo still shows both sides.
ENS realisation late in the build. Originally treated ENS as just naming. Halfway through realised text records can store the regulator's compliance attestations year by year. ESMA already publishes name-and-shame; we just put it on chain. Wrote a script to populate 12 records (cement-mainz clean, aluminium-bratislava with a 2024 violation referencing ESMA-2024-EUR-447). Surfaced them in the regulator's compliance roster. Changed the ENS pitch from "we have subdomains" to "ENS is the registry substrate".
Stack too deep on the demo script. DemoLocal.s.sol chained four broadcasts with too many local variables. Solidity hit "stack too deep" at compile. Enabled via_ir = true in foundry.toml. Compile is twice as slow now (about 6 seconds) but it works.
Tracks Applied (3)
Network Economy
Future Society
Most Creative Use of ENS
ENS
