Trustless. Transparent. Immutable.
A fully decentralized platform where every carbon credit issuance, verification, trade, and retirement is permanently recorded on the Ethereum blockchain—eliminating intermediaries and ensuring complete transparency in carbon offsetting.
- Overview
- Why Blockchain is Essential
- Carbon Credit Lifecycle (On-Chain)
- Blockchain Architecture
- Blockchain Security Features
- Tech Stack
- Project Structure
- Getting Started
- Smart Contract Deep Dive
- Environment Variables
- Deployment
- API Endpoints
Veritas is a blockchain-native carbon credit marketplace where smart contracts are the source of truth. Unlike traditional carbon registries that rely on centralized databases and trusted intermediaries, Veritas leverages the Ethereum blockchain to create a trustless, transparent, and tamper-proof system for carbon credit management.
- Double Counting - Credits sold multiple times across different registries
- Opaque Verification - No public audit trail for verification decisions
- Centralized Control - Single points of failure and manipulation
- Slow Settlement - Days or weeks for cross-border transactions
- High Fees - Intermediaries extract value at every step
| Challenge | Blockchain Solution | Implementation |
|---|---|---|
| Double Counting | On-chain token ownership | ERC-1155 enforces single owner per token unit |
| Opaque Verification | Public verification events | CreditVerified event emitted on-chain |
| Centralized Control | Decentralized access control | OpenZeppelin AccessControl with multiple roles |
| Slow Settlement | Atomic swaps | Payment + transfer in single transaction |
| High Fees | Peer-to-peer trading | Direct buyer-seller trades, no middlemen |
Every credit issuance, transfer, and retirement is permanently recorded on the Ethereum blockchain. Once a transaction is confirmed, it cannot be altered or deleted—creating an unbreakable audit trail.
All smart contract code is open source and verified on Etherscan. Anyone can inspect the logic that governs credit issuance, verification rules, and retirement mechanics.
Smart contracts eliminate the need to trust intermediaries. The code automatically enforces rules:
- Only ISSUER_ROLE can mint credits
- Only VERIFIER_ROLE can approve credits
- Unverified credits cannot be listed on the marketplace
- Retired credits cannot be transferred
The marketplace smart contract ensures that payment and credit transfer happen in the same transaction—or neither happens. No risk of payment without delivery or delivery without payment.
Every credit carries its complete history on-chain:
- Who issued it and when
- Which verifier approved it
- Every transfer between wallets
- When and why it was retired
Every stage of the carbon credit lifecycle is enforced by smart contract logic and recorded permanently on the blockchain:
┌─────────────────────────────────────────────────────────────────────────────┐
│ ON-CHAIN CARBON CREDIT LIFECYCLE │
└─────────────────────────────────────────────────────────────────────────────┘
┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
│ ISSUE │ ───► │ VERIFY │ ───► │ TRADE │ ───► │ RETIRE │
│ (mint) │ │ (approve)│ │ (atomic) │ │ (burn) │
└──────────┘ └──────────┘ └──────────┘ └──────────┘
│ │ │ │
▼ ▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
│ ERC-1155 │ │ On-chain │ │ Escrow │ │ Token │
│ _mint() │ │isVerified│ │ Contract │ │ _burn() │
│ + event │ │ = true │ │ + ETH │ │ + cert │
└──────────┘ └──────────┘ └──────────┘ └──────────┘
Smart Contract Function: issueCarbonCredit()
// Only addresses with ISSUER_ROLE can call this function
function issueCarbonCredit(
address to,
uint256 quantity,
string memory projectId,
string memory vintage,
string memory serialNumber,
string memory registry,
string memory metadataURI
) external onlyRole(ISSUER_ROLE) returns (uint256)On-Chain Data Stored:
projectId— Unique project identifiervintage— Year of carbon reductionserialNumber— Registry serial numberregistry— Issuing registry (Verra, Gold Standard, etc.)metadataURI— IPFS hash for extended metadataissuer— Wallet address of issuerissuanceDate— Block timestampisVerified— Starts asfalse
Blockchain Event Emitted:
event CreditIssued(tokenId, issuer, recipient, quantity, projectId, vintage);Smart Contract Function: verifyCredit()
// Only addresses with VERIFIER_ROLE can verify credits
function verifyCredit(uint256 tokenId) external onlyRole(VERIFIER_ROLE) {
require(exists(tokenId), "Token does not exist");
require(!creditMetadata[tokenId].isVerified, "Already verified");
creditMetadata[tokenId].isVerified = true;
emit CreditVerified(tokenId, msg.sender);
}Why This Matters:
- Verification status is stored on-chain, not in a database
- The marketplace contract checks this flag before allowing listings
- Anyone can verify a credit's status by querying the blockchain
- Verification events create a public, immutable audit trail
Marketplace Verification Check:
// CarbonMarketplace.sol - prevents unverified credits from being listed
try ICarbonCredit(tokenAddress).isCreditVerified(tokenId) returns (bool verified) {
require(verified, "Credit not verified");
} catch {
revert("Token contract does not support verification check");
}Smart Contract: CarbonMarketplace.sol
The marketplace contract implements a secure escrow pattern:
┌─────────────────────────────────────────────────────────────────┐
│ ATOMIC SWAP FLOW │
├─────────────────────────────────────────────────────────────────┤
│ │
│ SELLER MARKETPLACE BUYER │
│ │ │ │ │
│ │── approve(marketplace) ──►│ │ │
│ │── createListing() ───────►│ (tokens held in escrow) │ │
│ │ │◄── buyListing() + ETH ──│ │
│ │◄────── ETH transferred ───│── tokens transferred ──►│ │
│ │ │ │ │
│ Single transaction = Payment + Delivery (atomic) │
└─────────────────────────────────────────────────────────────────┘
Security Features:
ReentrancyGuard— Prevents reentrancy attacksERC1155Holder— Safe token reception- Escrow pattern — Tokens held by contract until purchase
- Atomic execution — All-or-nothing transaction
Smart Contract Function: retireCarbonCredit()
function retireCarbonCredit(
uint256 tokenId,
uint256 quantity,
string memory reason
) external {
require(balanceOf(msg.sender, tokenId) >= quantity, "Insufficient balance");
// Permanently burn tokens - they can NEVER be recovered
_burn(msg.sender, tokenId, quantity);
// Record retirement reason on-chain
retirements[tokenId].push(RetirementEvent({
tokenId: tokenId,
quantity: quantity,
retiree: msg.sender,
reason: reason,
timestamp: block.timestamp
}));
// Mint ERC-721 certificate as proof
retirementCertificate.mint(msg.sender, tokenId, quantity, reason, certURI);
}Blockchain Guarantees:
- Burned tokens are permanently destroyed (ERC-1155
_burn) - Retirement reason is immutably recorded on-chain
isRetiredflag prevents any future transfers- ERC-721 certificate serves as on-chain proof of retirement
┌─────────────────────────────────────────────────────────────────────────────┐
│ VERITAS BLOCKCHAIN ARCHITECTURE │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │
│ │ FRONTEND │ │ BACKEND │ │ ETHEREUM │ │
│ │ (React) │◄──►│ (Express) │◄──►│ BLOCKCHAIN │ │
│ └─────────────────┘ └─────────────────┘ └─────────────────┘ │
│ │ │ │ │
│ │ │ ▼ │
│ │ │ ┌─────────────────────┐ │
│ │ │ │ SMART CONTRACTS │ │
│ │ │ │ (Solidity 0.8.20) │ │
│ │ │ └─────────────────────┘ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ ETHERS.JS v6 │ │
│ │ (Wallet Connection + Contract Calls) │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────────┐
│ SMART CONTRACT LAYER │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌───────────────────────────────────────────────────────────────────────┐ │
│ │ CarbonCredit.sol │ │
│ │ (ERC-1155) │ │
│ ├───────────────────────────────────────────────────────────────────────┤ │
│ │ INHERITS: │ │
│ │ • ERC1155 (OpenZeppelin) — Multi-token standard │ │
│ │ • AccessControl — Role-based permissions │ │
│ │ • ERC1155Supply — Track total supply per token │ │
│ │ • ERC1155Burnable — Permanent token destruction │ │
│ ├───────────────────────────────────────────────────────────────────────┤ │
│ │ ROLES: │ │
│ │ • DEFAULT_ADMIN_ROLE — Grant/revoke all roles │ │
│ │ • ISSUER_ROLE — Mint new carbon credits │ │
│ │ • VERIFIER_ROLE — Verify and approve credits │ │
│ ├───────────────────────────────────────────────────────────────────────┤ │
│ │ STORAGE: │ │
│ │ • mapping(uint256 => CreditMetadata) creditMetadata │ │
│ │ • mapping(uint256 => RetirementEvent[]) retirements │ │
│ └───────────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌───────────────────────────────────────────────────────────────────────┐ │
│ │ CarbonMarketplace.sol │ │
│ ├───────────────────────────────────────────────────────────────────────┤ │
│ │ INHERITS: │ │
│ │ • ERC1155Holder — Receive ERC-1155 tokens │ │
│ │ • ReentrancyGuard — Prevent reentrancy attacks │ │
│ ├───────────────────────────────────────────────────────────────────────┤ │
│ │ SECURITY: │ │
│ │ • Verification check before listing │ │
│ │ • Escrow pattern for safe trading │ │
│ │ • NonReentrant modifier on all state-changing functions │ │
│ └───────────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌───────────────────────────────────────────────────────────────────────┐ │
│ │ RetirementCertificate.sol │ │
│ │ (ERC-721) │ │
│ ├───────────────────────────────────────────────────────────────────────┤ │
│ │ • Non-fungible proof of retirement │ │
│ │ • Stores retirement metadata permanently │ │
│ │ • Minted automatically on credit retirement │ │
│ └───────────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
| Contract | Address | Etherscan |
|---|---|---|
| CarbonCredit | 0xc5320DF1BF249E1092b0705c49CAcf588DfBBa1A |
View ↗ |
| CarbonMarketplace | 0x5E7076A4afaB7169FE5d9AC84fFD977E4c4278AC |
View ↗ |
All critical actions emit events that are permanently recorded on the blockchain:
| Event | Data Logged | Purpose |
|---|---|---|
CreditIssued |
tokenId, issuer, recipient, quantity, projectId, vintage | Track credit creation |
CreditVerified |
tokenId, verifier | Audit verification decisions |
CreditRetired |
tokenId, retiree, quantity, reason | Permanent retirement record |
ListingCreated |
listingId, seller, tokenId, quantity, price | Marketplace activity |
ListingSold |
listingId, buyer, quantity, totalPrice | Trade execution |
Access control is enforced entirely on-chain using OpenZeppelin's AccessControl:
bytes32 public constant ISSUER_ROLE = keccak256("ISSUER_ROLE");
bytes32 public constant VERIFIER_ROLE = keccak256("VERIFIER_ROLE");
// Role hierarchy
// DEFAULT_ADMIN_ROLE
// ├── ISSUER_ROLE
// └── VERIFIER_ROLE| Role | Bytes32 Hash | Capabilities |
|---|---|---|
| Admin | 0x00...00 |
Grant/revoke all roles, upgrade contracts |
| Issuer | keccak256("ISSUER_ROLE") |
Call issueCarbonCredit() |
| Verifier | keccak256("VERIFIER_ROLE") |
Call verifyCredit(), updateMetadata() |
| User | No role required | Buy, sell, transfer, retire credits |
contract CarbonMarketplace is ReentrancyGuard {
function buyListing(uint256 listingId, uint256 quantity)
external payable nonReentrant { ... }
}function issueCarbonCredit(...) external onlyRole(ISSUER_ROLE) { ... }
function verifyCredit(...) external onlyRole(VERIFIER_ROLE) { ... }// Retired credits cannot be transferred
function safeTransferFrom(...) public override {
require(!creditMetadata[id].isRetired, "Cannot transfer retired credits");
super.safeTransferFrom(...);
}// Unverified credits cannot be listed on marketplace
require(ICarbonCredit(tokenAddress).isCreditVerified(tokenId), "Credit not verified");| Technology | Purpose |
|---|---|
| Solidity 0.8.20 | Smart contract language with latest safety features |
| OpenZeppelin Contracts | Audited implementations of ERC-1155, ERC-721, AccessControl |
| ERC-1155 | Multi-token standard for efficient batch operations |
| ERC-721 | Non-fungible tokens for retirement certificates |
| Hardhat | Development environment, testing, and deployment |
| Ethers.js v6 | TypeScript-first Ethereum library for frontend/backend |
| Sepolia Testnet | Ethereum test network for deployment |
| Technology | Purpose |
|---|---|
| React 18 | UI framework with concurrent features |
| TypeScript | Type-safe JavaScript |
| Vite | Fast build tool and dev server |
| TailwindCSS | Utility-first styling |
| Framer Motion | Smooth animations |
| MetaMask Integration | Wallet connection and signing |
| Technology | Purpose |
|---|---|
| Node.js + Express | REST API server |
| MongoDB Atlas | Off-chain metadata cache |
| JWT | User authentication |
| Blockchain Indexer | Event listener for on-chain state sync |
veritas/
├── contracts/ # Solidity smart contracts
│ ├── CarbonCredit.sol # ERC-1155 carbon credit token
│ ├── CarbonMarketplace.sol # Trading marketplace
│ └── RetirementCertificate.sol # ERC-721 certificates
├── components/ # React components
│ ├── Navbar.tsx
│ ├── Footer.tsx
│ ├── Hero.tsx
│ └── ...
├── pages/ # Page components
│ ├── Home.tsx # Landing page
│ ├── Marketplace.tsx # Credit marketplace
│ ├── Dashboard.tsx # User portfolio
│ ├── Issuer.tsx # Credit issuance
│ ├── Verifier.tsx # Verification dashboard
│ ├── Retire.tsx # Credit retirement
│ └── Admin.tsx # Admin panel
├── contexts/ # React contexts
│ ├── AuthContext.tsx # Authentication state
│ └── WalletContext.tsx # Wallet connection
├── lib/
│ └── blockchain.ts # Blockchain service layer
├── server/ # Backend server
│ ├── server.js # Express server
│ ├── models/ # MongoDB models
│ ├── routes/ # API routes
│ └── services/
│ └── indexer.js # Blockchain event indexer
├── scripts/ # Deployment scripts
└── artifacts/ # Compiled contracts
- Node.js 18+
- MongoDB Atlas account
- MetaMask wallet
- Alchemy/Infura API key for Sepolia
-
Clone the repository
git clone https://github.com/AryanSaxenaa/veritas.git cd veritas -
Install frontend dependencies
npm install
-
Install backend dependencies
cd server npm install cd ..
-
Configure environment variables (see Environment Variables)
-
Start the backend server
cd server npm run dev -
Start the frontend (new terminal)
npm run dev
-
Open in browser
http://localhost:3000
This ERC-1155 contract is the heart of the system. Every carbon credit exists as a token on this contract.
// On-chain metadata structure — permanently stored on Ethereum
struct CreditMetadata {
string projectId; // Unique project identifier
string vintage; // Year of carbon reduction
string serialNumber; // Registry serial number
string registry; // Issuing registry (Verra, Gold Standard)
string metadataURI; // IPFS hash for extended data
address issuer; // Who created this credit
uint256 issuanceDate; // Block timestamp of creation
bool isRetired; // Permanently burned?
bool isVerified; // Approved by verifier?
}Key Functions:
| Function | Access | On-Chain Action |
|---|---|---|
issueCarbonCredit() |
ISSUER_ROLE | Mints new tokens, stores metadata |
verifyCredit() |
VERIFIER_ROLE | Sets isVerified = true, emits event |
retireCarbonCredit() |
Token holder | Burns tokens, records retirement |
isCreditVerified() |
Public | Read verification status |
getCreditMetadata() |
Public | Read all on-chain metadata |
getRetirementHistory() |
Public | Read retirement events |
The marketplace enables peer-to-peer trading with built-in security:
struct Listing {
uint256 listingId; // Auto-incrementing ID
address seller; // Seller's wallet
address tokenAddress; // CarbonCredit contract
uint256 tokenId; // Which credit
uint256 quantity; // How many tonnes
uint256 pricePerUnit; // Price in wei
bool isActive; // Still available?
}Trading Flow (Single Transaction):
1. Seller approves marketplace → setApprovalForAll()
2. Seller creates listing → createListing() [tokens → escrow]
3. Buyer purchases → buyListing() [ETH → seller, tokens → buyer]
# Compile Solidity contracts
npx hardhat compile
# Deploy to Sepolia testnet
npx hardhat run scripts/deploy.cjs --network sepolia
# Verify on Etherscan (optional)
npx hardhat verify --network sepolia <CONTRACT_ADDRESS>VITE_CONTRACT_ADDRESS_SEPOLIA=0xc5320DF1BF249E1092b0705c49CAcf588DfBBa1A
VITE_MARKETPLACE_ADDRESS_SEPOLIA=0x5E7076A4afaB7169FE5d9AC84fFD977E4c4278AC
VITE_API_URL=http://localhost:5000/apiMONGODB_URI=mongodb+srv://username:password@cluster.mongodb.net/veritas
JWT_SECRET=your-secure-jwt-secret
PORT=5000
SEPOLIA_RPC_URL=https://eth-sepolia.g.alchemy.com/v2/your-api-key
CONTRACT_ADDRESS=0xc5320DF1BF249E1092b0705c49CAcf588DfBBa1A
MARKETPLACE_ADDRESS=0x5E7076A4afaB7169FE5d9AC84fFD977E4c4278AC-
Build the production bundle:
npm run build
-
Deploy the
distfolder to your hosting provider -
Set environment variables in your hosting dashboard
-
Push server folder to deployment platform
-
Set environment variables
-
Ensure MongoDB Atlas IP whitelist includes deployment IP
Contracts are already deployed on Sepolia. To redeploy:
npx hardhat run scripts/deploy.cjs --network sepoliaUpdate contract addresses in both .env files after deployment.
| Method | Endpoint | Description |
|---|---|---|
| POST | /api/auth/register |
Register new user |
| POST | /api/auth/login |
User login |
| POST | /api/auth/request-role |
Request issuer/verifier role |
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/credits |
Get all credits |
| GET | /api/credits/pending |
Get unverified credits |
| GET | /api/credits/verified |
Get verified credits |
| POST | /api/credits/update-metadata |
Update credit metadata |
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/marketplace/listings |
Get active listings |
| Role | Access |
|---|---|
| Guest | View landing page, register |
| User | Marketplace, Dashboard, Retire |
| Issuer | + Issue Credits |
| Verifier | + Verify Credits |
| Admin | + Admin Panel, Grant Roles |
This project is licensed under the MIT License.
Every transaction is permanent. Every credit is traceable. Every retirement is verifiable.
CarbonCredit on Etherscan • Marketplace on Etherscan • Report Bug
Built for a sustainable future
Blockchain-Carbon-Credit-Management-System.pptxDescription.docx
Experimental Data Performed Over Blockchain.docx
