UNPKG

defarm-sdk

Version:

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

439 lines (377 loc) β€’ 12.5 kB
/** * Blockchain Interface for DeFarm SDK * AgnΓ³stic blockchain interface that connects to DeFarm's existing contracts * WITHOUT exposing private keys to clients */ const { StellarSDK } = require('stellar-sdk'); const { ethers } = require('ethers'); class BlockchainInterface { constructor(config = {}) { this.config = { network: config.network || 'stellar', // stellar, polygon, ethereum, etc mode: config.mode || 'relay', // relay (no keys), direct (with keys) relayUrl: config.relayUrl || process.env.DEFARM_RELAY_URL, apiKey: config.apiKey || process.env.DEFARM_API_KEY, clientId: config.clientId || process.env.DEFARM_CLIENT_ID, // Contract addresses from DeFarm backbone contracts: { stellar: { assetIssuer: process.env.STELLAR_ASSET_ISSUER, distributorAccount: process.env.STELLAR_DISTRIBUTOR }, polygon: { assetRegistry: process.env.POLYGON_ASSET_REGISTRY, tokenFactory: process.env.POLYGON_TOKEN_FACTORY, supplyChain: process.env.POLYGON_SUPPLY_CHAIN }, ethereum: { assetRegistry: process.env.ETH_ASSET_REGISTRY, tokenFactory: process.env.ETH_TOKEN_FACTORY } }, ...config }; // NO PRIVATE KEYS STORED HERE! // All signing happens on DeFarm's secure backend this.initialized = false; this.networkHandlers = new Map(); } /** * Initialize blockchain interface */ async initialize() { if (this.initialized) return; console.log(`πŸ”— Initializing Blockchain Interface (${this.config.network})...`); // Setup network-specific handlers this.setupNetworkHandlers(); // Verify relay connection if (this.config.mode === 'relay') { await this.verifyRelayConnection(); } this.initialized = true; console.log('βœ… Blockchain Interface ready (no private keys required!)'); } /** * Setup network-specific handlers */ setupNetworkHandlers() { // Stellar handler this.networkHandlers.set('stellar', { name: 'Stellar', createAsset: this.createStellarAsset.bind(this), recordEvent: this.recordStellarEvent.bind(this), verifyAsset: this.verifyStellarAsset.bind(this), getExplorer: (hash) => `https://stellar.expert/explorer/public/tx/${hash}` }); // Polygon handler this.networkHandlers.set('polygon', { name: 'Polygon', createAsset: this.createEVMAsset.bind(this, 'polygon'), recordEvent: this.recordEVMEvent.bind(this, 'polygon'), verifyAsset: this.verifyEVMAsset.bind(this, 'polygon'), getExplorer: (hash) => `https://polygonscan.com/tx/${hash}` }); // Ethereum handler this.networkHandlers.set('ethereum', { name: 'Ethereum', createAsset: this.createEVMAsset.bind(this, 'ethereum'), recordEvent: this.recordEVMEvent.bind(this, 'ethereum'), verifyAsset: this.verifyEVMAsset.bind(this, 'ethereum'), getExplorer: (hash) => `https://etherscan.io/tx/${hash}` }); } /** * Verify relay connection */ async verifyRelayConnection() { try { const response = await fetch(`${this.config.relayUrl}/health`, { headers: { 'X-API-Key': this.config.apiKey, 'X-Client-ID': this.config.clientId } }); if (!response.ok) { throw new Error('Relay connection failed'); } const data = await response.json(); console.log(` βœ“ Connected to DeFarm Relay: ${data.version}`); console.log(` βœ“ Network: ${data.network}`); console.log(` βœ“ Mode: Gas-free transactions enabled`); } catch (error) { console.warn(' ⚠ Relay not available, some features may be limited'); } } /** * Create asset on blockchain (gas-free for client) */ async createAsset(assetData, clientAddress, options = {}) { const handler = this.networkHandlers.get(this.config.network); if (!handler) { throw new Error(`Unsupported network: ${this.config.network}`); } console.log(`πŸ“ Creating asset on ${handler.name}...`); console.log(` Client: ${clientAddress} (no gas required)`); // Route through relay for gas-free transaction if (this.config.mode === 'relay') { return await this.relayTransaction({ type: 'CREATE_ASSET', network: this.config.network, data: assetData, owner: clientAddress, options }); } // Direct mode (only for DeFarm's own operations) return await handler.createAsset(assetData, clientAddress, options); } /** * Record event on blockchain (gas-free for client) */ async recordEvent(eventData, clientAddress, options = {}) { const handler = this.networkHandlers.get(this.config.network); if (!handler) { throw new Error(`Unsupported network: ${this.config.network}`); } console.log(`πŸ“ Recording event on ${handler.name}...`); // Route through relay for gas-free transaction if (this.config.mode === 'relay') { return await this.relayTransaction({ type: 'RECORD_EVENT', network: this.config.network, data: eventData, owner: clientAddress, options }); } return await handler.recordEvent(eventData, clientAddress, options); } /** * Verify asset on blockchain (read-only, no gas needed) */ async verifyAsset(assetId, expectedHash) { const handler = this.networkHandlers.get(this.config.network); if (!handler) { throw new Error(`Unsupported network: ${this.config.network}`); } // Read operations don't need gas return await handler.verifyAsset(assetId, expectedHash); } /** * Relay transaction through DeFarm backend */ async relayTransaction(request) { const payload = { id: this.generateRequestId(), timestamp: Date.now(), client: { id: this.config.clientId, address: request.owner }, transaction: { type: request.type, network: request.network, data: request.data, options: request.options }, signature: this.signRequest(request) }; try { const response = await fetch(`${this.config.relayUrl}/transaction`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-API-Key': this.config.apiKey, 'X-Client-ID': this.config.clientId }, body: JSON.stringify(payload) }); const result = await response.json(); if (result.status === 'success') { console.log('βœ… Transaction sponsored by DeFarm'); console.log(` Hash: ${result.transactionHash}`); console.log(` Gas saved: ${result.gasCost}`); return { success: true, transactionHash: result.transactionHash, blockNumber: result.blockNumber, network: result.network, gasSaved: result.gasCost, explorerUrl: this.getExplorerUrl(result.network, result.transactionHash) }; } else { throw new Error(result.error || 'Transaction failed'); } } catch (error) { console.error('Relay transaction failed:', error); throw error; } } // ===== STELLAR IMPLEMENTATIONS ===== async createStellarAsset(assetData, clientAddress, options) { // Stellar-specific implementation // This would be called by DeFarm backend, not directly by client const request = { operation: 'createAsset', assetCode: assetData.asset_code || 'DEFARM', issuer: this.config.contracts.stellar.assetIssuer, distributor: this.config.contracts.stellar.distributorAccount, destination: clientAddress, amount: assetData.amount || '1', memo: JSON.stringify({ type: assetData.asset_type, id: assetData.asset_id }) }; // This goes to DeFarm backend which has the keys return await this.sendToBackend(request); } async recordStellarEvent(eventData, clientAddress, options) { // Store event data in Stellar memo or use Soroban smart contract const request = { operation: 'recordEvent', account: clientAddress, memo: JSON.stringify({ event: eventData.event_type, asset: eventData.asset_id, timestamp: eventData.timestamp }) }; return await this.sendToBackend(request); } async verifyStellarAsset(assetId, expectedHash) { // Query Stellar Horizon API (public, no keys needed) try { const server = new StellarSDK.Server('https://horizon.stellar.org'); // Query logic here return true; } catch (error) { return false; } } // ===== EVM IMPLEMENTATIONS ===== async createEVMAsset(network, assetData, clientAddress, options) { const contracts = this.config.contracts[network]; const request = { operation: 'createAsset', contract: contracts.assetRegistry, method: 'createAsset', params: [ assetData.asset_id, assetData.asset_type, clientAddress, this.hashData(assetData) ] }; return await this.sendToBackend(request); } async recordEVMEvent(network, eventData, clientAddress, options) { const contracts = this.config.contracts[network]; const request = { operation: 'recordEvent', contract: contracts.supplyChain, method: 'recordEvent', params: [ eventData.asset_id, eventData.event_type, eventData.actor, this.hashData(eventData) ] }; return await this.sendToBackend(request); } async verifyEVMAsset(network, assetId, expectedHash) { // Read from blockchain (no gas needed) const contracts = this.config.contracts[network]; const provider = new ethers.JsonRpcProvider(this.getRpcUrl(network)); // ABI for view function const abi = ['function getAsset(bytes32) view returns (address, string, string, uint256)']; const contract = new ethers.Contract(contracts.assetRegistry, abi, provider); try { const asset = await contract.getAsset(assetId); const onChainHash = asset[2]; // Assuming hash is third return value return onChainHash === expectedHash; } catch (error) { return false; } } // ===== HELPER METHODS ===== /** * Send request to DeFarm backend (which has the keys) */ async sendToBackend(request) { // This would communicate with DeFarm's secure backend // The backend holds all private keys and signs transactions console.log('πŸ“€ Sending to DeFarm backend for signing...'); // Mock response for now return { success: true, transactionHash: '0x' + this.generateRequestId(), message: 'Transaction will be processed by DeFarm' }; } /** * Get RPC URL for network */ getRpcUrl(network) { const urls = { 'polygon': 'https://polygon-rpc.com', 'ethereum': 'https://eth.llamarpc.com', 'bsc': 'https://bsc-dataseed.binance.org' }; return urls[network]; } /** * Hash data */ hashData(data) { const crypto = require('crypto'); return '0x' + crypto.createHash('sha256') .update(JSON.stringify(data)) .digest('hex'); } /** * Sign request (for authentication, not blockchain signing) */ signRequest(request) { const crypto = require('crypto'); const hmac = crypto.createHmac('sha256', this.config.apiKey || 'secret'); hmac.update(JSON.stringify(request)); return hmac.digest('hex'); } /** * Generate request ID */ generateRequestId() { const crypto = require('crypto'); return crypto.randomBytes(16).toString('hex'); } /** * Get explorer URL */ getExplorerUrl(network, txHash) { const handler = this.networkHandlers.get(network); return handler ? handler.getExplorer(txHash) : null; } /** * Get supported networks */ getSupportedNetworks() { return Array.from(this.networkHandlers.keys()); } /** * Switch network */ async switchNetwork(network) { if (!this.networkHandlers.has(network)) { throw new Error(`Unsupported network: ${network}`); } this.config.network = network; console.log(`πŸ”„ Switched to ${network}`); // Re-verify relay for new network if (this.config.mode === 'relay') { await this.verifyRelayConnection(); } } } module.exports = { BlockchainInterface };