================================================================================ CARBONCORE DEVELOPER GUIDE #06 OTC MARKETPLACE SYSTEM ================================================================================ PRINCIPLE: Decentralized peer-to-peer carbon credit trading platform ================================================================================ MARKETPLACE ARCHITECTURE ================================================================================ Smart Contracts: OTCMarketInterface: 0xDAde447FCC8B2480c76cc8E0be23eb5772966C21 - Semantic layer over RequestManager - Order creation and management - Instant Buy functionality OTCMarketIndex: 0xd154C16A5d1fB9CE16B88F0c76593D1C28Bd046e - Order indexing and search - Filtering and sorting - RFQ to response linking Frontend Components: deals.html - Main marketplace interface deals-manager.js - State management and UI deals-contract-service.js - Contract interaction Service Dependencies: UnifiedTokenManager - Token operations MetadataService - Deal metadata TerritoryDetailsService - Territory information ================================================================================ DEAL TYPES ================================================================================ Type 1: Request for Quote (RFQ) - Buy Request Creator: Buyer Direction: Buyer seeks carbon credits Requirements: Specified territory/vintage criteria Process: Open request → Seller responses → Buyer selection Type 2: Direct Offer - Response to RFQ Creator: Seller Direction: Response to specific RFQ Requirements: Tokens deposited in escrow Process: Seller deposits → Buyer accepts → Instant exchange Type 3: Market Offer - Open Sell Order Creator: Seller Direction: Open to any buyer Variants: Standard, Instant Buy, KYC Required Process: List tokens → Buyer purchases → Exchange Type 4: Instant Buy - Pre-funded Offer Creator: Seller Direction: Immediate purchase available Requirements: Tokens in escrow, fixed price Process: Tokens locked → Buyer pays → Instant transfer ================================================================================ RFQ CREATION WORKFLOW ================================================================================ Step 1: Authentication Check Requirement: BUYER_ROLE Check: roleManager.hasRole(userTokenId, BUYER_ROLE) Step 2: Form Data Collection Fields: - carbonAmount (number of credits needed) - maxBudget (maximum willing to pay) - paymentToken (USDT/USDC/Mock USD) - territoryTypes[] (Forest, Wetland, etc.) - minVintage, maxVintage (year range) - countries[] (geographic preference) - minCO2Absorption (minimum threshold) - requiresKYC (boolean) - additionalTerms (text) Step 3: Metadata Structure { "metadataType": "RFQ", "version": "1.0", "requirements": { "territoryTypes": ["Forest", "Wetland"], "minVintage": 2020, "maxVintage": 2025, "countries": ["USA", "Canada"], "minCO2Absorption": 50000, "requiresKYC": true, "additionalTerms": "Gold Standard certification required" }, "contactPreferences": { "preferredCommunication": "in-platform", "timezone": "UTC-5", "responseTime": "24h" } } Step 4: Token Amount Parsing const carbonAmountWei = await tokenManager.parseAmount( carbonAmount, ethers.constants.AddressZero // Generic carbon token ); const maxPaymentWei = await tokenManager.parseAmount( maxBudget, paymentTokenAddress ); Step 5: Contract Call const tx = await otcMarketInterface.createBuyOrder( carbonAmountWei, maxPaymentWei, paymentTokenAddress, JSON.stringify(metadata) ); const receipt = await tx.wait(); Step 6: Extract Order ID const event = receipt.events.find(e => e.event === 'BuyOrderCreated'); const orderId = event.args.orderId.toString(); const discussionId = event.args.discussionId.toString(); ================================================================================ INSTANT OFFER CREATION WORKFLOW ================================================================================ Step 1: Token Selection Source: Token management module Data: tokenAddress, territoryId, balance Step 2: Balance Check const balanceCheck = await tokenManager.checkSufficientBalance( tokenAddress, offerAmount, userWallet ); if (!balanceCheck.sufficient) { throw new Error(`Insufficient balance: need ${balanceCheck.deficit}`); } Step 3: Token Approval const spender = window.appConfig.contractAddresses.otcMarketInterface; await tokenManager.approve( tokenAddress, spender, offerAmount ); Step 4: Metadata Creation { "metadataType": "Offer", "version": "1.0", "offerType": "instant_buy", "tokenDetails": { "territoryId": "24", "territoryName": "Amazon Conservation Alpha", "territoryType": "Forest", "location": { "country": "Brazil", "region": "Amazonas" }, "vintage": 2024, "tokenSymbol": "CCT24-2025" }, "pricingDetails": { "pricePerToken": 9.5, "instantBuy": true, "minimumPurchase": 1, "maximumPurchase": 1000 } } Step 5: Create Offer with Deposit const amountWei = await tokenManager.parseAmount(amount, tokenAddress); const priceWei = await tokenManager.parseAmount(totalPrice, paymentToken); const tx = await otcMarketInterface.createSellOrderWithDeposit( tokenAddress, amountWei, priceWei, paymentToken, JSON.stringify(metadata) ); Step 6: Confirmation Result: Order created, tokens in escrow, instant buy enabled ================================================================================ INSTANT BUY EXECUTION ================================================================================ Step 1: Verify Instant Buy Status const offer = await otcAPI.getOrder(offerId); if (!offer.carbonTokensInEscrow) { throw new Error('Tokens not in escrow - instant buy unavailable'); } Step 2: Calculate Payment const carbonAmount = ethers.utils.formatEther(offer.carbonAmount); const paymentAmount = ethers.utils.formatUnits( offer.paymentAmount, await tokenManager.getTokenInfo(offer.paymentToken).decimals ); Step 3: Check Payment Balance const balanceCheck = await tokenManager.checkSufficientBalance( offer.paymentToken, paymentAmount, buyerWallet ); Step 4: Approve Payment Token await tokenManager.approve( offer.paymentToken, otcMarketInterface, paymentAmount ); Step 5: Execute Purchase const tx = await otcMarketInterface.executeInstantBuy(offerId); const receipt = await tx.wait(); Step 6: Automatic Exchange Blockchain executes: 1. Transfer payment from buyer to seller 2. Transfer tokens from escrow to buyer 3. Update order status to COMPLETED 4. Emit InstantBuyExecuted event ================================================================================ DEAL DISCOVERY & FILTERING ================================================================================ Function: loadMarketData() Process: 1. Find maximum order ID (binary search) 2. Load orders in batches (20 per batch) 3. Filter by type (RFQ, Offer, Response) 4. Load metadata for each order 5. Enrich with territory data Filters Available: - Deal Type: RFQ, Direct Offer, Market Offer - Territory Type: Forest, Wetland, Agricultural, etc. - Vintage Year: 2020-2025 - Price Range: Min-Max per token - Geography: Countries/Regions - Payment Currency: USDT, USDC, Mock USD - Status: Active, Pending, Completed Sorting Options: - By Creation Date (Newest/Oldest) - By Price (Low to High / High to Low) - By Amount (Small to Large / Large to Small) - By Vintage Year (Recent to Old / Old to Recent) Implementation: const filtered = allOrders.filter(order => { if (filters.dealType && order.orderType !== filters.dealType) return false; if (filters.territoryType && !matchesTerritoryType(order, filters.territoryType)) return false; if (filters.minPrice && order.pricePerToken < filters.minPrice) return false; // ... additional filters return true; }); const sorted = filtered.sort((a, b) => { if (sortBy === 'price-asc') return a.pricePerToken - b.pricePerToken; if (sortBy === 'price-desc') return b.pricePerToken - a.pricePerToken; // ... additional sort options }); ================================================================================ METADATA MANAGEMENT ================================================================================ Service: MetadataService Purpose: Store and retrieve deal metadata Storage: CommentManager contract + Direct contract calls Save Metadata: await commentManager.addComment( discussionId, 0, // parentId (root comment) JSON.stringify(metadata) ); Retrieve Metadata: // Primary method: Direct contract call const metadata = await otcMarketInterface.getOrderMetadata(orderId); // Fallback: Through CommentManager const comments = await commentManager.getDiscussionComments(discussionId); const metadataComment = comments.find(c => isValidJSON(c.content)); const metadata = JSON.parse(metadataComment.content); Validation: function validateMetadata(metadata) { // Required fields if (!metadata.metadataType) return false; if (!metadata.version) return false; // Type-specific validation if (metadata.metadataType === 'RFQ') { if (!metadata.requirements) return false; } else if (metadata.metadataType === 'Offer') { if (!metadata.tokenDetails) return false; if (!metadata.pricingDetails) return false; } // Size limit (8KB) const size = new Blob([JSON.stringify(metadata)]).size; if (size > 8192) return false; return true; } ================================================================================ UI COMPONENTS ================================================================================ RFQ Card Display: