UNPKG

defarm-sdk

Version:

DeFarm SDK - On-premise blockchain data processing and tokenization engine for agriculture supply chain

657 lines (568 loc) 17.7 kB
const crypto = require('crypto'); const { ethers } = require('ethers'); /** * Tokenization Engine for DeFarm SDK * Handles asset tokenization, NFT creation, and token management */ class TokenizationEngine { constructor(config = {}) { this.config = { tokenStandard: config.tokenStandard || 'ERC721', // ERC721, ERC1155, ERC20 metadataStandard: config.metadataStandard || 'opensea', // opensea, defarm baseUri: config.baseUri || 'https://api.defarm.io/metadata/', royaltyPercentage: config.royaltyPercentage || 2.5, maxSupply: config.maxSupply || 10000, decimals: config.decimals || 18, ...config }; this.blockchainEngine = config.blockchain; this.databaseManager = config.database; // Token registry this.tokens = new Map(); this.tokenMetadata = new Map(); this.tokenOwners = new Map(); // Token templates for different asset types this.tokenTemplates = { livestock: { name: 'DeFarm Livestock Token', symbol: 'DFLIVE', standard: 'ERC721', attributes: ['breed', 'age', 'weight', 'health_score', 'location', 'certifications'] }, crop: { name: 'DeFarm Crop Token', symbol: 'DFCROP', standard: 'ERC1155', attributes: ['crop_type', 'variety', 'quantity', 'quality_score', 'harvest_date', 'location'] }, land: { name: 'DeFarm Land Token', symbol: 'DFLAND', standard: 'ERC721', attributes: ['location', 'area', 'soil_type', 'water_rights', 'usage_rights'] }, produce: { name: 'DeFarm Produce Token', symbol: 'DFPROD', standard: 'ERC1155', attributes: ['product_type', 'quantity', 'quality', 'origin', 'certifications', 'expiry'] }, carbon_credit: { name: 'DeFarm Carbon Credit', symbol: 'DFCARB', standard: 'ERC20', attributes: ['tons_co2', 'project', 'verification', 'vintage_year'] } }; // Statistics this.stats = { totalTokensCreated: 0, totalTokensBurned: 0, totalTransfers: 0, tokensByType: {}, averageTokenValue: 0 }; } /** * Create token for asset */ async createToken(assetData, options = {}) { console.log('🪙 Creating token for asset:', assetData.asset_id); // Determine token template const template = this.tokenTemplates[assetData.asset_type] || this.getDefaultTemplate(); // Generate token ID const tokenId = await this.generateTokenId(assetData); // Prepare metadata const metadata = await this.prepareMetadata(assetData, template, options); // Store metadata (IPFS simulation) const metadataUri = await this.storeMetadata(tokenId, metadata); // Mint token on blockchain if configured let blockchainResult = null; if (this.blockchainEngine && options.blockchain !== false) { blockchainResult = await this.mintOnBlockchain( tokenId, assetData, metadataUri, options ); } // Create token object const token = { tokenId, assetId: assetData.asset_id, assetType: assetData.asset_type, standard: template.standard, owner: options.owner || assetData.owner_address || 'pending', amount: this.calculateTokenAmount(assetData, template), decimals: template.standard === 'ERC20' ? this.config.decimals : 0, metadata, metadataUri, blockchainTx: blockchainResult?.transactionHash, createdAt: Date.now(), status: 'active', history: [{ event: 'minted', from: '0x0000000000000000000000000000000000000000', to: options.owner || assetData.owner_address, timestamp: Date.now(), txHash: blockchainResult?.transactionHash }] }; // Store token this.tokens.set(tokenId, token); this.tokenMetadata.set(tokenId, metadata); // Update owner registry this.updateOwnerRegistry(token.owner, tokenId, 'add'); // Store in database if configured if (this.databaseManager) { await this.databaseManager.storeToken(token); } // Update statistics this.stats.totalTokensCreated++; this.stats.tokensByType[assetData.asset_type] = (this.stats.tokensByType[assetData.asset_type] || 0) + 1; console.log(`✅ Token created: ${tokenId}`); return token; } /** * Generate token from data */ async generateToken(data, options = {}) { // This is called by the SDK for quick token generation const tokenData = { ...data, asset_id: data.asset_id || this.generateAssetId(data), asset_type: data.asset_type || 'generic' }; return await this.createToken(tokenData, options); } /** * Transfer token */ async transferToken(tokenId, fromAddress, toAddress, amount = 1, options = {}) { const token = this.tokens.get(tokenId); if (!token) { throw new Error(`Token not found: ${tokenId}`); } // Validate ownership if (token.owner !== fromAddress) { throw new Error('Sender does not own this token'); } // For ERC1155/ERC20, check balance if (token.standard !== 'ERC721' && amount > token.amount) { throw new Error('Insufficient token balance'); } // Execute blockchain transfer if configured let blockchainResult = null; if (this.blockchainEngine && options.blockchain !== false) { blockchainResult = await this.blockchainEngine.transferAsset( token.assetId, toAddress, options ); } // Update token ownership if (token.standard === 'ERC721') { token.owner = toAddress; this.updateOwnerRegistry(fromAddress, tokenId, 'remove'); this.updateOwnerRegistry(toAddress, tokenId, 'add'); } else { // For fungible tokens, handle partial transfers // This would require more complex balance tracking token.owner = toAddress; // Simplified for now } // Add to history token.history.push({ event: 'transfer', from: fromAddress, to: toAddress, amount, timestamp: Date.now(), txHash: blockchainResult?.transactionHash }); // Update database if configured if (this.databaseManager) { await this.databaseManager.storeToken(token); } // Update statistics this.stats.totalTransfers++; console.log(`✅ Token transferred: ${tokenId} from ${fromAddress} to ${toAddress}`); return { success: true, tokenId, from: fromAddress, to: toAddress, amount, txHash: blockchainResult?.transactionHash }; } /** * Burn token */ async burnToken(tokenId, options = {}) { const token = this.tokens.get(tokenId); if (!token) { throw new Error(`Token not found: ${tokenId}`); } // Mark as burned token.status = 'burned'; token.burnedAt = Date.now(); // Add to history token.history.push({ event: 'burned', from: token.owner, to: '0x0000000000000000000000000000000000000000', timestamp: Date.now() }); // Remove from owner registry this.updateOwnerRegistry(token.owner, tokenId, 'remove'); // Update statistics this.stats.totalTokensBurned++; console.log(`🔥 Token burned: ${tokenId}`); return { success: true, tokenId, burnedAt: token.burnedAt }; } /** * Get token by ID */ getToken(tokenId) { return this.tokens.get(tokenId); } /** * Get tokens by owner */ getTokensByOwner(ownerAddress) { const tokenIds = this.tokenOwners.get(ownerAddress) || []; return tokenIds.map(id => this.tokens.get(id)).filter(Boolean); } /** * Get tokens by asset type */ getTokensByType(assetType) { const tokens = []; for (const token of this.tokens.values()) { if (token.assetType === assetType && token.status === 'active') { tokens.push(token); } } return tokens; } /** * Verify token authenticity */ async verifyToken(tokenId, data = {}) { const token = this.tokens.get(tokenId); if (!token) { return { valid: false, error: 'Token not found' }; } // Verify metadata hash const metadataHash = this.calculateHash(token.metadata); const expectedHash = this.calculateHash(this.tokenMetadata.get(tokenId)); if (metadataHash !== expectedHash) { return { valid: false, error: 'Metadata mismatch' }; } // Verify on blockchain if configured if (this.blockchainEngine && data.verifyOnChain) { const isValid = await this.blockchainEngine.verifyData( token.assetId, token.metadata ); if (!isValid) { return { valid: false, error: 'Blockchain verification failed' }; } } return { valid: true, token, verifiedAt: Date.now() }; } /** * Prepare token metadata */ async prepareMetadata(assetData, template, options = {}) { const metadata = { name: `${template.name} #${assetData.asset_id}`, description: options.description || `${template.name} representing ${assetData.asset_type}`, image: options.image || await this.generateTokenImage(assetData), external_url: `${this.config.baseUri}${assetData.asset_id}`, attributes: [] }; // Add standard attributes for (const attr of template.attributes) { if (assetData[attr] !== undefined) { metadata.attributes.push({ trait_type: this.formatTraitType(attr), value: assetData[attr], display_type: this.getDisplayType(attr, assetData[attr]) }); } } // Add custom attributes if (options.customAttributes) { for (const [key, value] of Object.entries(options.customAttributes)) { metadata.attributes.push({ trait_type: this.formatTraitType(key), value: value }); } } // Add certifications as attributes if (assetData.certifications && Array.isArray(assetData.certifications)) { for (const cert of assetData.certifications) { metadata.attributes.push({ trait_type: 'Certification', value: cert.cert_type || cert }); } } // Add sustainability metrics if (assetData.metrics) { if (assetData.metrics.sustainability_index) { metadata.attributes.push({ trait_type: 'Sustainability Index', value: assetData.metrics.sustainability_index, display_type: 'boost_percentage', max_value: 1 }); } if (assetData.metrics.carbon_footprint) { metadata.attributes.push({ trait_type: 'Carbon Footprint', value: assetData.metrics.carbon_footprint, display_type: 'number', unit: 'kg CO2' }); } } // Add blockchain properties metadata.properties = { category: assetData.asset_type, created_at: Date.now(), creator: options.creator || assetData.owner_address, royalties: { recipient: options.royaltyRecipient || assetData.owner_address, percentage: this.config.royaltyPercentage } }; // Add location if available if (assetData.location) { metadata.properties.location = { latitude: assetData.location.latitude, longitude: assetData.location.longitude, region: assetData.location.region, country: assetData.location.country }; } return metadata; } /** * Store metadata (IPFS simulation) */ async storeMetadata(tokenId, metadata) { // In production, this would upload to IPFS // For now, simulate with hash-based URI const hash = this.calculateHash(metadata); const uri = `ipfs://${hash}`; // Store locally for retrieval this.tokenMetadata.set(tokenId, metadata); return uri; } /** * Mint token on blockchain */ async mintOnBlockchain(tokenId, assetData, metadataUri, options = {}) { if (!this.blockchainEngine) { console.warn('Blockchain engine not configured'); return null; } try { const result = await this.blockchainEngine.mintToken( assetData.asset_id, options.owner || assetData.owner_address, options.amount || 1, metadataUri ); return result; } catch (error) { console.error('Failed to mint on blockchain:', error); throw error; } } /** * Calculate token amount based on asset data */ calculateTokenAmount(assetData, template) { if (template.standard === 'ERC721') { return 1; // NFTs are unique } // For fungible tokens, base on quantity if (assetData.metrics && assetData.metrics.quantity) { return Math.floor(assetData.metrics.quantity * Math.pow(10, this.config.decimals)); } // Default amount return Math.pow(10, this.config.decimals); } /** * Generate token ID */ async generateTokenId(assetData) { const input = { assetId: assetData.asset_id, assetType: assetData.asset_type, timestamp: Date.now(), nonce: crypto.randomBytes(16).toString('hex') }; const hash = this.calculateHash(input); return `TKN-${hash.substring(0, 16).toUpperCase()}`; } /** * Generate asset ID */ generateAssetId(data) { const hash = crypto.createHash('sha256'); hash.update(JSON.stringify({ type: data.asset_type, timestamp: Date.now(), random: crypto.randomBytes(8).toString('hex') })); return `ASSET-${hash.digest('hex').substring(0, 12).toUpperCase()}`; } /** * Generate token image */ async generateTokenImage(assetData) { // This would generate or retrieve an image for the token // For now, return a placeholder based on asset type const images = { livestock: 'https://api.defarm.io/images/livestock-default.png', crop: 'https://api.defarm.io/images/crop-default.png', land: 'https://api.defarm.io/images/land-default.png', produce: 'https://api.defarm.io/images/produce-default.png' }; return images[assetData.asset_type] || 'https://api.defarm.io/images/default.png'; } /** * Update owner registry */ updateOwnerRegistry(owner, tokenId, action) { if (!this.tokenOwners.has(owner)) { this.tokenOwners.set(owner, []); } const tokens = this.tokenOwners.get(owner); if (action === 'add') { if (!tokens.includes(tokenId)) { tokens.push(tokenId); } } else if (action === 'remove') { const index = tokens.indexOf(tokenId); if (index > -1) { tokens.splice(index, 1); } } } /** * Get default token template */ getDefaultTemplate() { return { name: 'DeFarm Asset Token', symbol: 'DFARM', standard: 'ERC721', attributes: ['type', 'quantity', 'quality', 'location', 'timestamp'] }; } /** * Format trait type for metadata */ formatTraitType(attribute) { return attribute .split('_') .map(word => word.charAt(0).toUpperCase() + word.slice(1)) .join(' '); } /** * Get display type for attribute */ getDisplayType(attribute, value) { if (typeof value === 'number') { if (attribute.includes('score') || attribute.includes('index')) { return 'boost_percentage'; } if (attribute.includes('date') || attribute.includes('time')) { return 'date'; } return 'number'; } return null; } /** * Calculate hash */ calculateHash(data) { const hash = crypto.createHash('sha256'); hash.update(JSON.stringify(data)); return hash.digest('hex'); } /** * Get tokenization statistics */ getStats() { return { ...this.stats, activeTokens: Array.from(this.tokens.values()).filter(t => t.status === 'active').length, totalOwners: this.tokenOwners.size, tokensPerOwner: this.tokenOwners.size > 0 ? this.stats.totalTokensCreated / this.tokenOwners.size : 0 }; } /** * Export token data */ exportTokenData(tokenId, format = 'json') { const token = this.tokens.get(tokenId); if (!token) { throw new Error(`Token not found: ${tokenId}`); } switch (format) { case 'json': return JSON.stringify(token, null, 2); case 'metadata': return JSON.stringify(token.metadata, null, 2); case 'certificate': return this.generateCertificate(token); default: throw new Error(`Unsupported format: ${format}`); } } /** * Generate certificate for token */ generateCertificate(token) { return ` ==================================== DEFARM ASSET CERTIFICATE ==================================== Token ID: ${token.tokenId} Asset ID: ${token.assetId} Asset Type: ${token.assetType} Standard: ${token.standard} Owner: ${token.owner} Created: ${new Date(token.createdAt).toISOString()} Status: ${token.status} Metadata URI: ${token.metadataUri} Blockchain TX: ${token.blockchainTx || 'N/A'} ATTRIBUTES: ${token.metadata.attributes.map(a => ` - ${a.trait_type}: ${a.value}`).join('\n')} ==================================== This certificate verifies the authenticity of the DeFarm tokenized asset. ==================================== `.trim(); } } module.exports = { TokenizationEngine };