ICarbonCredit Interface¶
Overview¶
The ICarbonCredit interface defines the standard contract for managing carbon credits associated with ERC721 tokens within the Gemforce platform. This interface enables the tokenization of environmental assets, allowing NFTs to represent carbon credits that can be tracked, managed, and retired for environmental impact purposes.
Key Features¶
- Carbon Credit Tokenization: Associate carbon credit balances with ERC721 tokens
- Credit Retirement: Permanently retire carbon credits to prevent double-counting
- Batch Operations: Efficiently manage multiple tokens simultaneously
- Status Tracking: Monitor active vs. retired carbon credit status
- Environmental Compliance: Support for carbon offset and sustainability programs
Interface Definition¶
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import { CarbonCreditStatus } from "../libraries/CarbonCreditLib.sol";
interface ICarbonCredit {
// Events
event CarbonCreditsInitialized(uint256 indexed tokenId, uint256 initialBalance);
event CarbonCreditsRetired(uint256 indexed tokenId, uint256 amount, uint256 remainingBalance);
// Core Functions
function initializeCarbonCredit(uint256 tokenId, uint256 initialBalance) external;
function retireCarbonCredits(uint256 tokenId, uint256 amount) external;
function getCarbonCreditStatus(uint256 tokenId) external view returns (CarbonCreditStatus);
function getCarbonCreditBalance(uint256 tokenId) external view returns (uint256);
// Batch Operations
function batchInitializeCarbonCredits(uint256[] calldata tokenIds, uint256[] calldata initialBalances) external;
function getAllCarbonCreditBalances(uint256[] calldata tokenIds) external view returns (uint256[] memory);
}
Core Functions¶
Carbon Credit Initialization¶
initializeCarbonCredit()¶
function initializeCarbonCredit(uint256 tokenId, uint256 initialBalance) external
Purpose: Initialize carbon credit balance for a specific ERC721 token.
Parameters:
- tokenId (uint256): The ID of the ERC721 token to associate with carbon credits
- initialBalance (uint256): The initial balance of carbon credits (must be > 0)
Requirements: - Token must exist and have a valid owner - Carbon credits must not already be initialized for this token - Initial balance must be greater than zero - Caller must have appropriate permissions
Events Emitted:
- CarbonCreditsInitialized with token ID and initial balance
Example Usage:
// Initialize carbon credits for a newly minted environmental NFT
uint256 tokenId = 1;
uint256 initialCredits = 1000; // 1000 carbon credits
ICarbonCredit(diamond).initializeCarbonCredit(tokenId, initialCredits);
console.log("Initialized", initialCredits, "carbon credits for token", tokenId);
Carbon Credit Retirement¶
retireCarbonCredits()¶
function retireCarbonCredits(uint256 tokenId, uint256 amount) external
Purpose: Permanently retire carbon credits to prevent double-counting and demonstrate environmental impact.
Parameters:
- tokenId (uint256): The ID of the ERC721 token containing carbon credits
- amount (uint256): The amount of carbon credits to retire (must be whole number)
Requirements: - Token must have sufficient carbon credit balance - Amount must be greater than zero - Amount must be less than or equal to current balance - Caller must have appropriate permissions
Events Emitted:
- CarbonCreditsRetired with token ID, retired amount, and remaining balance
Example Usage:
// Retire carbon credits to offset emissions
uint256 tokenId = 1;
uint256 retireAmount = 250; // Retire 250 carbon credits
// Check current balance first
uint256 currentBalance = ICarbonCredit(diamond).getCarbonCreditBalance(tokenId);
require(currentBalance >= retireAmount, "Insufficient carbon credits");
// Retire the credits
ICarbonCredit(diamond).retireCarbonCredits(tokenId, retireAmount);
console.log("Retired", retireAmount, "carbon credits from token", tokenId);
Status and Balance Queries¶
getCarbonCreditStatus()¶
function getCarbonCreditStatus(uint256 tokenId) external view returns (CarbonCreditStatus)
Purpose: Get the current status of carbon credits for a token.
Parameters:
- tokenId (uint256): The ID of the ERC721 token
Returns:
- CarbonCreditStatus enum value:
- ACTIVE: Token has remaining carbon credits
- RETIRED: All carbon credits have been retired
Example Usage:
// Check carbon credit status
uint256 tokenId = 1;
CarbonCreditStatus status = ICarbonCredit(diamond).getCarbonCreditStatus(tokenId);
if (status == CarbonCreditStatus.ACTIVE) {
console.log("Token", tokenId, "has active carbon credits");
} else {
console.log("Token", tokenId, "carbon credits are fully retired");
}
getCarbonCreditBalance()¶
function getCarbonCreditBalance(uint256 tokenId) external view returns (uint256)
Purpose: Get the current carbon credit balance for a specific token.
Parameters:
- tokenId (uint256): The ID of the ERC721 token
Returns: Current balance of carbon credits for the token
Example Usage:
// Check current balance
uint256 tokenId = 1;
uint256 balance = ICarbonCredit(diamond).getCarbonCreditBalance(tokenId);
console.log("Token", tokenId, "has", balance, "carbon credits remaining");
Batch Operations¶
Batch Initialization¶
batchInitializeCarbonCredits()¶
function batchInitializeCarbonCredits(uint256[] calldata tokenIds, uint256[] calldata initialBalances) external
Purpose: Initialize carbon credits for multiple tokens in a single transaction.
Parameters:
- tokenIds (uint256[]): Array of ERC721 token IDs
- initialBalances (uint256[]): Array of initial balances corresponding to token IDs
Requirements: - Arrays must have the same length - All tokens must exist and not have carbon credits initialized - All initial balances must be greater than zero - Caller must have appropriate permissions
Gas Optimization: More efficient than multiple individual calls
Example Usage:
// Batch initialize carbon credits for multiple environmental NFTs
uint256[] memory tokenIds = new uint256[](3);
tokenIds[0] = 1;
tokenIds[1] = 2;
tokenIds[2] = 3;
uint256[] memory initialBalances = new uint256[](3);
initialBalances[0] = 1000; // Forest conservation project
initialBalances[1] = 500; // Solar energy project
initialBalances[2] = 750; // Wind energy project
ICarbonCredit(diamond).batchInitializeCarbonCredits(tokenIds, initialBalances);
console.log("Batch initialized carbon credits for", tokenIds.length, "tokens");
Batch Balance Query¶
getAllCarbonCreditBalances()¶
function getAllCarbonCreditBalances(uint256[] calldata tokenIds) external view returns (uint256[] memory)
Purpose: Get carbon credit balances for multiple tokens efficiently.
Parameters:
- tokenIds (uint256[]): Array of ERC721 token IDs to query
Returns: Array of carbon credit balances corresponding to the input token IDs
Gas Optimization: Single call instead of multiple individual queries
Example Usage:
// Get balances for multiple tokens
uint256[] memory tokenIds = new uint256[](3);
tokenIds[0] = 1;
tokenIds[1] = 2;
tokenIds[2] = 3;
uint256[] memory balances = ICarbonCredit(diamond).getAllCarbonCreditBalances(tokenIds);
for (uint256 i = 0; i < tokenIds.length; i++) {
console.log("Token", tokenIds[i], "balance:", balances[i]);
}
Integration Examples¶
Environmental NFT Marketplace¶
// Integration with environmental NFT marketplace
contract EnvironmentalMarketplace {
ICarbonCredit public carbonCredit;
IERC721 public environmentalNFT;
struct EnvironmentalListing {
uint256 tokenId;
uint256 price;
uint256 carbonCredits;
address seller;
bool active;
}
mapping(uint256 => EnvironmentalListing) public listings;
event EnvironmentalNFTListed(
uint256 indexed tokenId,
uint256 price,
uint256 carbonCredits,
address indexed seller
);
event CarbonCreditsRetiredOnPurchase(
uint256 indexed tokenId,
uint256 retiredAmount,
address indexed buyer
);
constructor(address _carbonCredit, address _environmentalNFT) {
carbonCredit = ICarbonCredit(_carbonCredit);
environmentalNFT = IERC721(_environmentalNFT);
}
function listEnvironmentalNFT(
uint256 tokenId,
uint256 price,
bool retireCreditsOnSale
) external {
require(environmentalNFT.ownerOf(tokenId) == msg.sender, "Not token owner");
uint256 creditBalance = carbonCredit.getCarbonCreditBalance(tokenId);
require(creditBalance > 0, "No carbon credits associated");
listings[tokenId] = EnvironmentalListing({
tokenId: tokenId,
price: price,
carbonCredits: creditBalance,
seller: msg.sender,
active: true
});
emit EnvironmentalNFTListed(tokenId, price, creditBalance, msg.sender);
}
function purchaseEnvironmentalNFT(
uint256 tokenId,
uint256 creditsToRetire
) external payable {
EnvironmentalListing storage listing = listings[tokenId];
require(listing.active, "Listing not active");
require(msg.value >= listing.price, "Insufficient payment");
uint256 currentCredits = carbonCredit.getCarbonCreditBalance(tokenId);
require(creditsToRetire <= currentCredits, "Cannot retire more credits than available");
// Transfer NFT
environmentalNFT.safeTransferFrom(listing.seller, msg.sender, tokenId);
// Retire carbon credits if requested
if (creditsToRetire > 0) {
carbonCredit.retireCarbonCredits(tokenId, creditsToRetire);
emit CarbonCreditsRetiredOnPurchase(tokenId, creditsToRetire, msg.sender);
}
// Transfer payment
payable(listing.seller).transfer(msg.value);
// Deactivate listing
listing.active = false;
}
function getListingWithCarbonInfo(uint256 tokenId) external view returns (
EnvironmentalListing memory listing,
uint256 currentCarbonBalance,
CarbonCreditStatus status
) {
listing = listings[tokenId];
currentCarbonBalance = carbonCredit.getCarbonCreditBalance(tokenId);
status = carbonCredit.getCarbonCreditStatus(tokenId);
}
}
Carbon Offset Program¶
// Automated carbon offset program
contract CarbonOffsetProgram {
ICarbonCredit public carbonCredit;
struct OffsetProject {
string name;
string description;
uint256[] tokenIds;
uint256 totalCredits;
uint256 retiredCredits;
address projectOwner;
bool active;
}
mapping(uint256 => OffsetProject) public projects;
mapping(address => uint256[]) public userOffsets;
uint256 public nextProjectId;
event ProjectCreated(uint256 indexed projectId, string name, address indexed owner);
event CreditsRetiredForOffset(uint256 indexed projectId, uint256 amount, address indexed offsetter);
event ProjectCompleted(uint256 indexed projectId, uint256 totalRetired);
function createOffsetProject(
string memory name,
string memory description,
uint256[] memory tokenIds
) external returns (uint256 projectId) {
projectId = nextProjectId++;
uint256 totalCredits = 0;
for (uint256 i = 0; i < tokenIds.length; i++) {
uint256 balance = carbonCredit.getCarbonCreditBalance(tokenIds[i]);
require(balance > 0, "Token has no carbon credits");
totalCredits += balance;
}
projects[projectId] = OffsetProject({
name: name,
description: description,
tokenIds: tokenIds,
totalCredits: totalCredits,
retiredCredits: 0,
projectOwner: msg.sender,
active: true
});
emit ProjectCreated(projectId, name, msg.sender);
}
function offsetEmissions(
uint256 projectId,
uint256 creditsToOffset
) external payable {
OffsetProject storage project = projects[projectId];
require(project.active, "Project not active");
require(creditsToOffset > 0, "Must offset at least 1 credit");
uint256 availableCredits = project.totalCredits - project.retiredCredits;
require(creditsToOffset <= availableCredits, "Insufficient credits in project");
// Calculate payment (simplified - would integrate with pricing oracle)
uint256 costPerCredit = 10 ether; // $10 per credit in wei
uint256 totalCost = creditsToOffset * costPerCredit;
require(msg.value >= totalCost, "Insufficient payment");
// Retire credits from project tokens
uint256 creditsRemaining = creditsToOffset;
for (uint256 i = 0; i < project.tokenIds.length && creditsRemaining > 0; i++) {
uint256 tokenId = project.tokenIds[i];
uint256 tokenBalance = carbonCredit.getCarbonCreditBalance(tokenId);
if (tokenBalance > 0) {
uint256 toRetire = creditsRemaining > tokenBalance ? tokenBalance : creditsRemaining;
carbonCredit.retireCarbonCredits(tokenId, toRetire);
creditsRemaining -= toRetire;
}
}
project.retiredCredits += creditsToOffset;
userOffsets[msg.sender].push(creditsToOffset);
// Transfer payment to project owner
payable(project.projectOwner).transfer(totalCost);
emit CreditsRetiredForOffset(projectId, creditsToOffset, msg.sender);
// Check if project is completed
if (project.retiredCredits >= project.totalCredits) {
project.active = false;
emit ProjectCompleted(projectId, project.retiredCredits);
}
}
function getProjectStatus(uint256 projectId) external view returns (
string memory name,
uint256 totalCredits,
uint256 retiredCredits,
uint256 availableCredits,
bool active,
uint256[] memory tokenBalances
) {
OffsetProject memory project = projects[projectId];
name = project.name;
totalCredits = project.totalCredits;
retiredCredits = project.retiredCredits;
availableCredits = totalCredits - retiredCredits;
active = project.active;
tokenBalances = carbonCredit.getAllCarbonCreditBalances(project.tokenIds);
}
function getUserOffsetHistory(address user) external view returns (uint256[] memory) {
return userOffsets[user];
}
}
Carbon Credit Analytics Dashboard¶
// Analytics and reporting for carbon credits
contract CarbonCreditAnalytics {
ICarbonCredit public carbonCredit;
struct AnalyticsData {
uint256 totalTokensWithCredits;
uint256 totalActiveCredits;
uint256 totalRetiredCredits;
uint256 averageCreditsPerToken;
uint256 retirementRate;
}
mapping(uint256 => uint256) public tokenInitialBalances;
mapping(uint256 => uint256) public tokenRetirementHistory;
uint256[] public trackedTokens;
event AnalyticsUpdated(AnalyticsData data);
event TokenAddedToTracking(uint256 indexed tokenId, uint256 initialBalance);
function addTokenToTracking(uint256 tokenId) external {
uint256 balance = carbonCredit.getCarbonCreditBalance(tokenId);
require(balance > 0, "Token has no carbon credits");
tokenInitialBalances[tokenId] = balance;
trackedTokens.push(tokenId);
emit TokenAddedToTracking(tokenId, balance);
}
function updateAnalytics() external returns (AnalyticsData memory data) {
uint256[] memory balances = carbonCredit.getAllCarbonCreditBalances(trackedTokens);
uint256 totalActive = 0;
uint256 totalRetired = 0;
uint256 tokensWithCredits = 0;
for (uint256 i = 0; i < trackedTokens.length; i++) {
uint256 tokenId = trackedTokens[i];
uint256 currentBalance = balances[i];
uint256 initialBalance = tokenInitialBalances[tokenId];
if (currentBalance > 0) {
tokensWithCredits++;
}
totalActive += currentBalance;
totalRetired += (initialBalance - currentBalance);
}
data = AnalyticsData({
totalTokensWithCredits: tokensWithCredits,
totalActiveCredits: totalActive,
totalRetiredCredits: totalRetired,
averageCreditsPerToken: tokensWithCredits > 0 ? totalActive / tokensWithCredits : 0,
retirementRate: totalActive > 0 ? (totalRetired * 10000) / (totalRetired + totalActive) : 0 // in basis points
});
emit AnalyticsUpdated(data);
}
function getAnalytics() external view returns (AnalyticsData memory) {
uint256[] memory balances = carbonCredit.getAllCarbonCreditBalances(trackedTokens);
uint256 totalActive = 0;
uint256 totalRetired = 0;
uint256 tokensWithCredits = 0;
for (uint256 i = 0; i < trackedTokens.length; i++) {
uint256 tokenId = trackedTokens[i];
uint256 currentBalance = balances[i];
uint256 initialBalance = tokenInitialBalances[tokenId];
if (currentBalance > 0) {
tokensWithCredits++;
}
totalActive += currentBalance;
totalRetired += (initialBalance - currentBalance);
}
return AnalyticsData({
totalTokensWithCredits: tokensWithCredits,
totalActiveCredits: totalActive,
totalRetiredCredits: totalRetired,
averageCreditsPerToken: tokensWithCredits > 0 ? totalActive / tokensWithCredits : 0,
retirementRate: totalActive > 0 ? (totalRetired * 10000) / (totalRetired + totalActive) : 0 // in basis points
});
}
}
Carbon Credit Lifecycle Events¶
CarbonCreditsInitialized¶
event CarbonCreditsInitialized(uint256 indexed tokenId, uint256 initialBalance);
Emitted when carbon credits are initialized for an NFT.
CarbonCreditsRetired¶
event CarbonCreditsRetired(uint256 indexed tokenId, uint256 amount, uint256 remainingBalance);
Emitted when carbon credits are retired from an NFT.
Core Data Structures¶
CarbonCreditStatus Enum¶
enum CarbonCreditStatus {
ACTIVE,
RETIRED
}
Security Considerations¶
Access Control¶
- Authorized Initialization: Only authorized entities can initialize carbon credits
- Owner-only Retirement: Only NFT owner can retire associated carbon credits
- Token Existence Check: Prevents operations on non-existent tokens
Financial Security¶
- No Double-Spending: Retired credits cannot be reused
- Balance Validation: Ensures sufficient balance for retirement
- Overflow Prevention: Safe math for all calculations
Environmental Integrity¶
- Permanent Retirement: Ensures true carbon offsetting
- Transparent Tracking: All credit movements are on-chain
- Auditability: Full audit trail for compliance
Gas Optimization¶
Efficient Operations¶
- Batch Functions:
batchInitializeCarbonCreditsandgetAllCarbonCreditBalancesfor efficiency - Direct Storage Access: Minimize overhead for data access
Storage Optimization¶
- Packed Structs: Use tightly packed structs where feasible
- Mapping Usage: Efficient use of mappings for dynamic data
Error Handling¶
Common Errors¶
ICarbonCredit: Invalid Token ID: Provided token ID is 0 or invalidICarbonCredit: Invalid Amount: Retirement amount is 0 or exceeds balanceICarbonCredit: Already Initialized: Attempting to re-initialize credits for a tokenICarbonCredit: Not Token Owner: Caller is not the owner of the NFT when retiring
Best Practices¶
Integration Checklist¶
- Ensure proper NFT ownership and transfer mechanisms
- Validate carbon credit amounts and status before use
- Integrate with off-chain carbon registries for full traceability
- Use batch operations for efficiency in dApps
Development Guidelines¶
- Write comprehensive unit tests for all functions
- Implement robust error handling and user feedback mechanisms
- Monitor events for real-time tracking and analytics
- Follow secure coding practices for all smart contract interactions
Related Documentation¶
- Smart Contracts: Carbon Credit Facet - Reference for the CarbonCreditFacet implementation.
- Smart Contracts: ICarbonCredit Interface - Interface definition.
- Smart Contracts: Carbon Credit Lib - Supporting library functions.
- EIP-DRAFT-Carbon-Credit-Standard - The full EIP specification.
- Developer Guides: Automated Testing Setup