Verified carbon credits. Permanent retirement. Full provenance.
A decentralized carbon credit marketplace on Stellar where carbon projects mint tokenized RWAs, corporations buy and retire them on-chain, and every credit has an immutable audit trail from issuance to retirement.
- The Problem
- The Solution
- How It Works
- Architecture
- Smart Contracts
- Tech Stack
- Project Structure
- Getting Started
- Contract Deployment
- Oracle Setup
- Frontend Setup
- Running Tests
- User Roles
- Credit Lifecycle
- Roadmap
- Changelog
- Contributing
- Security
- License
Want to contribute? We've created comprehensive guides to get you started in under 30 minutes:
- 📖 New Contributor Guide - Complete overview
- 🚀 Quick Start Guide - Setup in 15-25 minutes
- ✅ Setup Checklist - Verify your environment
- 🔧 Troubleshooting - Common issues solved
- 📋 Quick Reference - One-page command reference
- 🔑 Configuration Guide - Every environment variable explained
- ♻️ Credit Lifecycle - Actors, contracts, data, and error conditions for each stage
Run this to verify your setup:
./scripts/verify-setup.sh # Linux/macOS
.\scripts\verify-setup.ps1 # WindowsThe voluntary carbon credit market moves over $2 billion annually — yet it is riddled with:
- Fraud — projects claiming credits for sequestration that never happened
- Double-counting — the same tonne of CO2 sold to multiple buyers
- Opacity — corporations have no way to verify what they actually bought
- Greenwashing — retired credits with no on-chain proof of retirement
- Inaccessibility — small projects cannot afford traditional registry fees
The result is a market where companies pay real money for carbon credits that may not represent real impact — and have no way to prove otherwise to regulators or the public.
CarbonLedger puts the entire carbon credit lifecycle on Stellar:
- Every credit is minted with a unique serial number — double counting is mathematically impossible
- Every retirement is permanently irreversible on-chain — greenwashing is eliminated
- Every credit carries full provenance — from project registration to satellite monitoring to issuance to transfer to retirement
- Every retirement generates a beautiful verifiable certificate with a permanent public URL
- The entire audit trail is publicly accessible without a wallet — regulators, journalists, and the public can verify everything
PROJECT DEVELOPER CARBONLEDGER CORPORATION
│ │ │
│── Submit project ─────►│ │
│ (methodology + │ │
│ coordinates) │ │
│ │◄── Oracle monitoring ─────│
│◄── Project verified ───│ (satellite data) │
│ │ │
│── Request issuance ───►│ │
│ (verified tonnes) │ │
│◄── Credits minted ─────│ │
│ (serial numbers │ │
│ assigned) │ │
│ │◄── Browse marketplace ────│
│ │◄── Purchase credits ──────│
│◄── USDC payment ───────│ │
│ │◄── Retire credits ────────│
│ │ (beneficiary + reason) │
│ │──► Certificate issued ────►│
│ │ (permanent on-chain) │
┌──────────────────────────────────────────────────────────────┐
│ NEXT.JS 14 FRONTEND │
│ Public Audit │ Marketplace │ Buy │ Retire │ Dashboard │
└───────────────────────────┬──────────────────────────────────┘
│ @stellar/stellar-sdk
│ @stellar/freighter-api
┌───────────────────────────▼──────────────────────────────────┐
│ SOROBAN CONTRACTS (Rust) │
│ carbon_registry │ carbon_credit │ carbon_marketplace │
│ carbon_oracle │
└───────────────────────────┬──────────────────────────────────┘
│ py-stellar-base
┌───────────────────────────▼──────────────────────────────────┐
│ ORACLE / VERIFICATION BRIDGE (Python) │
│ verification_listener │ price_oracle │ satellite_monitor │
└───────────────────────────┬──────────────────────────────────┘
│
┌───────────────────────────▼──────────────────────────────────┐
│ OFF-CHAIN LAYER (PostgreSQL + IPFS) │
│ Project docs │ Credit batches │ Retirements │ Certificates │
└──────────────────────────────────────────────────────────────┘
Key architectural decisions are documented in docs/adr/. See the ADR index for the full list.
CarbonLedger deploys 4 Soroban contracts written in Rust:
Manages carbon project registration, verification, and lifecycle status.
| Function | Description |
|---|---|
register_project() |
Submit a new carbon project for verification |
verify_project() |
Accredited verifier approves a project |
reject_project() |
Permanently reject a fraudulent project |
suspend_project() |
Halt new issuance from project under investigation |
update_project_status() |
Oracle pushes monitoring data on-chain |
get_project() |
Query full project details |
Mints, transfers, and permanently retires tokenized carbon credits.
| Function | Description |
|---|---|
mint_credits() |
Mint credits for verified projects with unique serial numbers |
retire_credits() |
Permanently and irreversibly retire credits on-chain |
transfer_credits() |
Transfer credits between accounts |
verify_serial_range() |
Detect double issuance before minting |
get_credit_batch() |
Query a credit batch by ID |
get_retirement_certificate() |
Retrieve a permanent retirement certificate |
Handles credit listings, purchases, and bulk corporate buying.
| Function | Description |
|---|---|
list_credits() |
List credits for sale with price per tonne |
delist_credits() |
Remove an active listing |
purchase_credits() |
Buy credits — USDC to seller, credits to buyer |
bulk_purchase() |
Corporations buy from multiple projects in one tx |
get_active_listings() |
Browse all available credits |
get_listings_by_vintage() |
Filter credits by vintage year |
Receives and validates off-chain monitoring and price data.
| Function | Description |
|---|---|
submit_monitoring_data() |
Verifier pushes satellite monitoring data |
update_credit_price() |
Push benchmark price per methodology and vintage |
flag_project() |
Flag a project for investigation |
is_monitoring_current() |
Returns false if no data in last 365 days |
get_benchmark_price() |
Get current market price per methodology |
pub enum CarbonError {
ProjectNotFound = 1,
ProjectNotVerified = 2,
ProjectSuspended = 3,
InsufficientCredits = 4,
AlreadyRetired = 5,
SerialNumberConflict = 6,
UnauthorizedVerifier = 7,
UnauthorizedOracle = 8,
InvalidVintageYear = 9,
ListingNotFound = 10,
InsufficientLiquidity = 11,
PriceNotSet = 12,
MonitoringDataStale = 13,
DoubleCountingDetected = 14,
RetirementIrreversible = 15,
ZeroAmountNotAllowed = 16,
ProjectAlreadyExists = 17,
InvalidSerialRange = 18,
}| Layer | Technology |
|---|---|
| Smart Contracts | Rust + Soroban SDK |
| Blockchain | Stellar Mainnet / Testnet |
| Frontend | Next.js 14 (App Router) + TypeScript |
| Wallet | Freighter (@stellar/freighter-api) |
| Stellar SDK | @stellar/stellar-sdk, soroban-client |
| Payment Token | USDC on Stellar |
| Trading | Stellar DEX (SDEX) |
| Oracle Bridge | Python + py-stellar-base |
| Satellite Data | Google Earth Engine / Planet Labs |
| Price Feeds | Xpansiv CBL + Toucan Protocol |
| Database | PostgreSQL + Prisma ORM |
| File Storage | IPFS via Pinata |
| Auth | JWT + Stellar keypair + SEP-0030 |
| Backend API | NestJS |
| Testing | Rust unit tests + Stellar Testnet |
For the full annotated reference, see docs/folder-structure.md.
carbonledger/
├── .github/ # CI/CD workflows, issue templates, and PR template
├── audit/ # Pre-audit checklist and security review artifacts
├── backend/ # NestJS REST API — auth, projects, credits, retirements, marketplace, oracle
│ └── prisma/
│ └── schema.prisma # Prisma database schema — all PostgreSQL models and relations
├── components/ # Shared UI components used outside the Next.js app directory
├── contracts/ # Soroban smart contracts written in Rust
│ └── Cargo.toml # Rust workspace manifest for all Soroban contract crates
├── docs/ # Project documentation: guides, ADRs, runbooks, API references
├── frontend/ # Next.js 14 (App Router) web application
├── hooks/ # Shared React hooks used across the monorepo
├── infra/ # Infrastructure-as-code (Terraform) for cloud provisioning
├── load-tests/ # k6 load test scripts and results for the marketplace API
├── logging/ # Observability stack configuration: Loki, Promtail, Grafana
├── oracle/ # Python oracle bridge: verification listener, price feeds, satellite monitor
├── scripts/ # Developer utility scripts: setup, deploy, test runners, DB backup
├── tests/ # Cross-contract and upgrade path integration tests (Rust)
├── .env.example # Environment variable template — copy to .env before running locally
├── docker-compose.yml # Local development stack definition — starts all services with one command
├── Stellar.toml # SEP-0001 metadata file for the Stellar network
└── README.md
Estimated setup time: 25-40 minutes
Follow these steps to set up CarbonLedger for local development and testing on a clean Ubuntu 22.04 or macOS 14 system.
Ensure you have the following installed:
- Node.js (version 18+)
- Rust (version 1.74+)
- Python (version 3.10+)
- PostgreSQL (version 14+)
- Redis (version 6+)
- Git
You can verify your installation with:
./scripts/verify-setup.sh# Clone repository
git clone https://github.com/YOUR_USERNAME/carbonledger.git
cd carbonledger
# Copy environment file
cp .env.example .env
# Edit .env and set these minimum values:
# - DATABASE_URL=postgresql://carbonledger:changeme@localhost:5432/carbonledger
# - JWT_SECRET=your-random-secret-key (use `openssl rand -hex 32` to generate)
# - REDIS_PASSWORD=changeme (optional, but recommended)# Add WebAssembly target for Soroban smart contracts
rustup target add wasm32-unknown-unknown
# Install Stellar CLI for contract deployment
cargo install --locked stellar-cli --version 21.0.0# Start PostgreSQL service if not running
# macOS: brew services start postgresql@16
# Linux (Ubuntu): sudo systemctl start postgresql
# Create database and user
createdb carbonledger
# Or if you prefer to set up a user and password:
# sudo -u postgres psql -c "CREATE USER carbonledger WITH PASSWORD 'changeme';"
# sudo -u postgres psql -c "ALTER USER carbonledger WITH SUPERUSER;"
# sudo -u postgres psql -c "GRANT ALL PRIVILEGES ON DATABASE carbonledger TO carbonledger;"
# Then update .env accordingly# Start Redis service
# macOS: brew services start redis
# Linux (Ubuntu): sudo systemctl start redis
# Alternatively, you can run Redis in the background:
# redis-server --daemonize yes
# Verify Redis is running
redis-cli ping
# Should return: PONG# Generate a funded testnet account (requires Stellar CLI)
stellar keys generate deployer --network testnet --fund
# Save the generated secret key securely - you'll need it for deployment
# Example output:
# Secret Key: SDXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
# Public Key: GAXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
# Fund your account with testnet XLM (if not already funded by the --fund flag)
# You can also friendbot: curl https://friendbot.stellar.org/?addr=GAXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX- Install the Freighter wallet extension for your browser (Chrome/Firefox).
- Create a new wallet or import an existing one.
- In Freighter settings, switch to Testnet network.
- (Optional) Fund your Freighter wallet with testnet XLM using the Stellar Discord faucet or by sending from your deployer account.
# Backend
cd backend
npm install
npx prisma generate
npx prisma migrate dev
cd ..
# Frontend
cd frontend
npm install
cd ..
# Oracle (Python)
cd oracle
pip3 install -r requirements.txt
cd ..
# Note: Contracts will be built in the next stepcd contracts
cargo build --target wasm32-unknown-unknown --release
cd ..cd contracts
# Replace 'deployer' with the alias you used in step 6, or use the secret key directly
# Example using the alias:
stellar contract deploy \
--wasm target/wasm32-unknown-unknown/release/carbon_registry.wasm \
--source deployer \
--network testnet
# Repeat for carbon_credit, carbon_marketplace, and carbon_oracle contracts
# Save the returned contract IDs to your .env file:
# CARBON_REGISTRY_CONTRACT_ID=...
# CARBON_CREDIT_CONTRACT_ID=...
# CARBON_MARKETPLACE_CONTRACT_ID=...
# CARBON_ORACLE_CONTRACT_ID=...
# USDC_CONTRACT_ID= (use the testnet USDC contract: GDTTYN6ZEVJZAINM4AA642YOZBTT5DT4JY3UDZVKWYGJEQS6S6J6Y6ZE - see Stellar.toml for the configured value)Note: The testnet USDC contract address on Stellar testnet is:
GDTTYN6ZEVJZAINM4AA642YOZBTT5DT4JY3UDZVKWYGJEQS6S6J6Y6ZE(as of the time of writing, but check Stellar.toml in the repo for the configured value).
# Start verification listener (polls every 6 hours)
cd oracle
python3 verification_listener.py &
# Example output:
# [INFO] Verification listener started
# [INFO] Listening for new verification requests on blockchain...
# [INFO] Next check in: 6 hours
# Start price oracle (runs every 12 hours)
python3 price_oracle.py &
# Example output:
# [INFO] Price oracle started
# [INFO] Fetching benchmark prices from Xpansiv CBL and Toucan Protocol...
# [INFO] Next update in: 12 hours
# Start satellite monitor (webhook receiver)
python3 satellite_monitor.py &
# Example output:
# [INFO] Satellite monitor started
# [INFO] Listening for webhooks on port 5001
# [INFO] Ready to receive satellite data from Google Earth Engine
cd ..
# Note: These services run in the background. You can stop them with `kill %1` `kill %2` `kill %3` or by closing the terminal.# Run all tests to verify your setup
./scripts/test-all.sh
# Expected output:
# ✓ Carbon Registry Tests passed
# ✓ Carbon Credit Tests passed
# ✓ Carbon Marketplace Tests passed
# ✓ Carbon Oracle Tests passed
# ✓ Backend Unit Tests passed
# ✓ Frontend Unit Tests passed
# All tests passed! ✓# Terminal 1: Backend
cd backend
npm run start:dev
# → http://localhost:3001
# Terminal 2: Frontend
cd frontend
npm run dev
# → http://localhost:3000
# Terminal 3: Oracle services (if not already started)
cd oracle
python3 verification_listener.py &
python3 price_oracle.py &
python3 satellite_monitor.py &After completing these steps, you should have:
- A running PostgreSQL database
- A running Redis instance
- Deployed contracts on Stellar testnet
- Configured Freighter wallet connected to testnet
- Oracle services running in the background
- All tests passing
You are now ready to contribute to CarbonLedger!
If you prefer Docker (still requires Rust toolchain for contract development):
# Copy environment file
cp .env.example .env
# Start all services (PostgreSQL, Redis, Backend, Frontend, Oracle)
docker-compose up --build
# Services will be available at:
# - Frontend: http://localhost:3000
# - Backend: http://localhost:3001
# - PostgreSQL: localhost:5432
# - Redis: localhost:6379Note: You still need to:
- Install Rust toolchain (for contract development)
- Generate a funded testnet account (step 6 above)
- Deploy contracts (step 10 above) using the Stellar CLI
- Start oracle services (step 11 above) if not using Docker for them (they are included in docker-compose)
cd contracts
# Build all contracts
cargo build --target wasm32-unknown-unknown --release
# Deploy carbon_registry
stellar contract deploy \
--wasm target/wasm32-unknown-unknown/release/carbon_registry.wasm \
--source ADMIN_SECRET_KEY \
--network testnet
# Deploy carbon_credit
stellar contract deploy \
--wasm target/wasm32-unknown-unknown/release/carbon_credit.wasm \
--source ADMIN_SECRET_KEY \
--network testnet
# Deploy carbon_marketplace
stellar contract deploy \
--wasm target/wasm32-unknown-unknown/release/carbon_marketplace.wasm \
--source ADMIN_SECRET_KEY \
--network testnet
# Deploy carbon_oracle
stellar contract deploy \
--wasm target/wasm32-unknown-unknown/release/carbon_oracle.wasm \
--source ADMIN_SECRET_KEY \
--network testnetSave all returned contract IDs to your .env file.
cd oracle
pip install -r requirements.txt
# Start verification listener (polls every 6 hours)
python3 verification_listener.py
# Start price oracle (runs every 12 hours)
python3 price_oracle.py
# Start satellite monitor (webhook receiver)
python3 satellite_monitor.pycd frontend
npm install
npm run devInstall Freighter wallet and switch to Testnet.
The complete development stack can be started with a single command:
docker-compose up --buildThis starts all services:
- PostgreSQL (port 5432) - Database with health checks
- Redis (port 6379) - Cache with Sentinel HA
- NestJS Backend (port 3001) - API server with health checks
- Next.js Frontend (port 3000) - Web application
- Oracle Services (port 5001) - Verification, price, and satellite monitoring
- Observability Stack - Loki, Promtail, and Grafana for logging
Copy .env.example to .env and configure:
cp .env.example .envKey variables:
POSTGRES_PASSWORD- Database passwordREDIS_PASSWORD- Redis passwordJWT_SECRET- Backend JWT secretSTELLAR_NETWORK- testnet or public- Contract IDs for
CARBON_*_CONTRACT_ID
Services start in order with health checks:
- PostgreSQL and Redis start first
- Backend waits for database and Redis to be healthy
- Frontend waits for backend to be healthy
- Oracle services connect to database and blockchain
Volume mounts enable hot reload:
- Backend:
./backend→/app(NestJS watches for changes) - Frontend:
./frontend→/app(Next.js dev mode) - Oracle:
./oracle→/app(Python auto-reload)
# All services
docker-compose logs -f
# Specific service
docker-compose logs -f backend
docker-compose logs -f frontend
docker-compose logs -f oracle_verification
# Grafana dashboards
# Open http://localhost:3200 (default: admin/admin)# Stop all services
docker-compose down
# Stop and remove volumes
docker-compose down -v# Automated test runner
./scripts/test-all.sh
# Or manually:
cd contracts && cargo test # 30 Rust tests
cd backend && npm test # Backend tests
cd frontend && npm test # Frontend tests# Rust contracts
cd contracts
cargo test # All contracts
cargo test -p carbon_registry # Specific contract
cargo test -- --nocapture # With output
# Backend (NestJS)
cd backend
npm test # All tests
npm test -- --watch # Watch mode
npm test projects.service.spec.ts # Specific file
# Frontend (Next.js)
cd frontend
npm test # All tests
npm test -- --coverage # With coverage| Contract | Tests |
|---|---|
| carbon_registry | 7 tests |
| carbon_credit | 10 tests |
| carbon_marketplace | 7 tests |
| carbon_oracle | 6 tests |
See: CONTRIBUTING.md for detailed testing guide
- Register carbon offset project with methodology and coordinates
- Submit monitoring data for credit issuance
- Track issued vs retired credits and receive USDC payments
- Browse credits by methodology, vintage year, country, and price
- Purchase single or bulk credits from multiple projects
- Retire credits and download permanent certificates for ESG reporting
- Accredited verifiers approve projects for credit issuance
- Submit on-chain attestations for monitoring periods
- Earn attestation fees per verified project
- Browse full audit trail without wallet connection
- Look up any serial number and see complete history
- Verify retirement certificates via permanent public URL
Project Registered → Verifier Approved → Oracle Monitoring →
Credits Minted (serial numbers assigned) → Listed on Marketplace →
Purchased by Corporation → Retired On-Chain (irreversible) →
Certificate Issued (permanent public URL) →
ESG Report Filed
For the full lifecycle reference — actors involved, contract functions called, on-chain and off-chain data, error conditions, and a sequence diagram — see docs/carbon-credit-lifecycle.md.
| Parameter | Value |
|---|---|
| Serial number uniqueness | Globally enforced across all batches |
| Retirement | Permanently irreversible on-chain |
| Oracle freshness | 365 days maximum for monitoring data |
| Price cache TTL | 24 hours temporary storage |
| Methodology score minimum | 70 out of 100 |
| Price deviation alert | 15% single update threshold |
| Protocol fee | 1% of each transaction |
-
carbon_registry— project registration and verification -
carbon_credit— mint, retire, transfer with serial numbers -
carbon_marketplace— list, buy, bulk purchase -
carbon_oracle— monitoring data and price feeds - 30 Rust unit tests
- Stellar Testnet deployment
- Verification listener service
- Xpansiv CBL price feed integration
- Google Earth Engine satellite webhook
- End-to-end oracle → Soroban test
- Freighter wallet integration
- Public audit explorer (no wallet required)
- Corporate bulk purchase flow
- Retirement certificate PDF generator
- Serial number lookup tool
- Smart contract security audit
- Gold Standard and Verra VCS methodology validation
- Mainnet deployment
- Regulatory compliance review
- Third party registry API integrations
We welcome contributions! Here's how to get started:
- 📖 Quick Start Guide - Setup in under 30 minutes
- 📝 Contributing Guide - Detailed setup and workflow
- 🔧 Troubleshooting - Common issues and fixes
- 🏷️ Good First Issues
# 1. Create feature branch
git checkout -b feat/your-feature-name
# 2. Make changes and test
./scripts/test-all.sh
# 3. Commit with conventional commits
git commit -m "feat: add serial number validation"
# 4. Push and create PR
git push origin feat/your-feature-name- Follow Conventional Commits
- Use
CarbonErrorenum for all contract errors - Follow checks-effects-interactions pattern in Soroban contracts
- Retirement must always be irreversible
- Avoid crypto jargon on buyer-facing pages
- Add tests for new features
- Update documentation
See: CONTRIBUTING.md for complete guidelines
See CHANGELOG.md for a full history of changes and releases.
We take security seriously. If you discover a vulnerability, please report it responsibly:
- Do not open a public GitHub issue
- Email security@carbonledger.io with details
- See SECURITY.md for our full security policy, threat model, and responsible disclosure process
MIT License — see LICENSE for details.
- Stellar Development Foundation — Soroban and RWA infrastructure
- Verra VCS — carbon methodology standards
- Gold Standard — verification framework
- Xpansiv CBL — carbon market price data
- Google Earth Engine — satellite monitoring
Built on Stellar. Built for the planet.
⭐ Star this repo if CarbonLedger matters to you
Website · Audit Explorer · Twitter · Discord