UNPKG

@iota-big3/sdk-blockchain

Version:

Comprehensive blockchain integration platform with multi-chain support, smart contracts, DeFi protocols, NFT infrastructure, Bitcoin support, and seamless SDK ecosystem integration for IOTA Big3

543 lines 23.1 kB
"use strict"; /** * @iota-big3/sdk-blockchain - Smart Contract Manager * Smart contract deployment and interaction */ Object.defineProperty(exports, "__esModule", { value: true }); exports.SmartContractManager = void 0; const ethers_1 = require("ethers"); const events_1 = require("events"); const provider_factory_1 = require("../chains/provider-factory"); // Feature flag for real contract operations const USE_REAL_CONTRACTS = process.env.BLOCKCHAIN_USE_REAL_CONTRACTS === 'true'; class SmartContractManager extends events_1.EventEmitter { constructor(config = {}) { super(); this.isEnabled = true; this.contracts = new Map(); this.eventListeners = new Map(); this.contractInstances = new Map(); // eslint-disable-next-line @typescript-eslint/no-explicit-any this.provider = null; this.isEnabled = config.enabled ?? true; } async initialize() { if (USE_REAL_CONTRACTS) { try { // Get provider from environment or create default const rpcUrl = process.env.BLOCKCHAIN_RPC_URL || 'https://mainnet.infura.io/v3/'; this.provider = await provider_factory_1.ProviderFactory.createProvider(1, rpcUrl, { timeout: 10000, fallbackToMock: true }); } catch (error) { console.warn('Failed to initialize real provider, using mock:', error); this.provider = null; } } this.emit('initialized', { type: 'initialized', data: { mode: USE_REAL_CONTRACTS ? 'real' : 'mock' }, timestamp: new Date() }); } async deployContract(deployment) { try { // Validate bytecode if (!deployment.bytecode || !deployment.bytecode.startsWith('0x')) { throw new Error('Invalid bytecode'); } let contract; if (USE_REAL_CONTRACTS && this.provider && provider_factory_1.ProviderFactory.isRealProvider(this.provider)) { // Real deployment using ethers.js const contractFactory = new ethers_1.ethers.ContractFactory(deployment.abi, deployment.bytecode, await this.provider.getSigner()); const deploymentArgs = deployment.constructorArgs || []; const deployTx = await contractFactory.deploy(...deploymentArgs, { gasLimit: deployment.gasLimit }); const receipt = await deployTx.waitForDeployment(); const address = await receipt.getAddress(); contract = { address, abi: deployment.abi, deploymentTransaction: { hash: deployTx.deploymentTransaction()?.hash || '', blockNumber: receipt.deploymentTransaction()?.blockNumber || 0 }, createdAt: new Date(), name: 'Contract', symbol: 'N/A', // eslint-disable-next-line @typescript-eslint/no-explicit-any owner: await this.provider.getSigner().then((s) => s.getAddress()), verified: false }; // Store contract instance this.contractInstances.set(address, receipt); } else { // Mock deployment const address = `0x${Array(40).fill(0).map(() => Math.floor(Math.random() * 16).toString(16)).join('')}`; contract = { address, abi: deployment.abi, deploymentTransaction: { hash: `0x${'a'.repeat(64)}`, blockNumber: Math.floor(Math.random() * 1000000) + 15000000 }, createdAt: new Date(), name: 'Mock Contract', symbol: 'MOCK', owner: '0x0000000000000000000000000000000000000000', verified: false }; } this.contracts.set(contract.address, contract); this.emit('contract:deployed', { type: 'contract:deployed', data: contract, timestamp: new Date() }); return contract; } catch (error) { this.emit('error', { type: 'error', data: { error: error instanceof Error ? error.message : 'Deployment failed' }, timestamp: new Date() }); throw error; } } async callContract(address, method, // eslint-disable-next-line @typescript-eslint/no-explicit-any args = []) { const contract = this.contracts.get(address); if (!contract) { throw new Error(`Contract ${address} not found`); } try { if (USE_REAL_CONTRACTS && this.provider && provider_factory_1.ProviderFactory.isRealProvider(this.provider)) { // Real contract call let contractInstance = this.contractInstances.get(address); if (!contractInstance) { contractInstance = new ethers_1.ethers.Contract(address, contract.abi, this.provider); this.contractInstances.set(address, contractInstance); } // Use interface for type-safe calls const iface = contractInstance.interface; const fragment = iface.getFunction(method); if (!fragment) { throw new Error(`Method ${method} not found in contract ABI`); } // Encode the function call const data = iface.encodeFunctionData(method, args); // Make the call const result = await this.provider.call({ to: address, data }); // Decode the result return iface.decodeFunctionResult(method, result); } else { // Mock implementation - return sample data based on method const mockResults = { name: 'Mock Token', symbol: 'MOCK', decimals: 18, totalSupply: '1000000000000000000', balanceOf: '1000000000000000000', owner: '0x0000000000000000000000000000000000000000', paused: false }; return mockResults[method] || `Mock result for ${method}`; } } catch (error) { this.emit('error', { type: 'error', data: { error: error instanceof Error ? error.message : 'Call failed', contract: address, method }, timestamp: new Date() }); throw error; } } async sendTransaction(address, method, // eslint-disable-next-line @typescript-eslint/no-explicit-any args = [], options = {}) { const contract = this.contracts.get(address); if (!contract) { throw new Error(`Contract ${address} not found`); } let receipt; if (USE_REAL_CONTRACTS && this.provider && provider_factory_1.ProviderFactory.isRealProvider(this.provider)) { // Real transaction let contractInstance = this.contractInstances.get(address); if (!contractInstance) { const signer = await this.provider.getSigner(); contractInstance = new ethers_1.ethers.Contract(address, contract.abi, signer); this.contractInstances.set(address, contractInstance); } // Use interface to encode the transaction const iface = contractInstance.interface; const fragment = iface.getFunction(method); if (!fragment) { throw new Error(`Method ${method} not found in contract ABI`); } // Encode function data const data = iface.encodeFunctionData(method, args); // Send transaction through signer const signer = await this.provider.getSigner(); const tx = await signer.sendTransaction({ to: address, data, gasLimit: options.gasLimit, value: options.value }); const txReceipt = await tx.wait(); if (!txReceipt) { throw new Error('Transaction receipt not received'); } receipt = { transactionHash: txReceipt.hash, blockNumber: txReceipt.blockNumber, blockHash: txReceipt.blockHash, gasUsed: BigInt(txReceipt.gasUsed.toString()), effectiveGasPrice: BigInt(txReceipt.gasPrice?.toString() || '0'), status: txReceipt.status === 1 ? 1 : 0, from: txReceipt.from, to: txReceipt.to || undefined, // eslint-disable-next-line @typescript-eslint/no-explicit-any logs: txReceipt.logs, contractAddress: undefined }; } else { // Mock transaction receipt receipt = { transactionHash: `0x${'b'.repeat(64)}`, blockNumber: Math.floor(Math.random() * 1000000) + 15000000, blockHash: `0x${'c'.repeat(64)}`, gasUsed: BigInt((options.gasLimit || 100000n).toString()), effectiveGasPrice: BigInt('20000000000'), status: 1, from: '0x0000000000000000000000000000000000000000', to: address, logs: [], contractAddress: undefined }; } this.emit('transaction:sent', { type: 'transaction:sent', data: receipt, timestamp: new Date() }); return receipt; } async listenToEvents(filter, callback) { const listenerId = `${filter.address}_${filter.event}_${Date.now()}`; const listener = { id: listenerId, filter, callback, active: true }; this.eventListeners.set(listenerId, listener); if (USE_REAL_CONTRACTS && this.provider && provider_factory_1.ProviderFactory.isRealProvider(this.provider)) { // Real event listening const contract = this.contracts.get(filter.address); if (contract) { let contractInstance = this.contractInstances.get(filter.address); if (!contractInstance) { contractInstance = new ethers_1.ethers.Contract(filter.address, contract.abi, this.provider); this.contractInstances.set(filter.address, contractInstance); } // Use event emitter pattern const eventFragment = contractInstance.interface.getEvent(filter.event); if (!eventFragment) { throw new Error(`Event ${filter.event} not found in contract ABI`); } // Create topic filter // eslint-disable-next-line @typescript-eslint/no-explicit-any const filters = contractInstance.filters; const topicFilter = filters && filters[filter.event] ? filters[filter.event]() : filter.event; // Listen to events contractInstance.on(topicFilter, (...args) => { const eventLog = args[args.length - 1]; const event = { address: filter.address, blockNumber: eventLog.blockNumber, transactionHash: eventLog.transactionHash, transactionIndex: eventLog.transactionIndex, blockHash: eventLog.blockHash, logIndex: eventLog.index, removed: eventLog.removed || false, id: `${eventLog.transactionHash}_${eventLog.index}`, returnValues: Object.fromEntries(eventFragment.inputs.map((input, i) => [input.name, args[i]])), event: filter.event, signature: eventFragment.format('sighash'), raw: { data: eventLog.data || '0x', topics: eventLog.topics || [] } }; callback(event); this.emit('event:received', event); }); } } else { // Mock event emission after a delay setTimeout(() => { const mockEvent = { address: filter.address, blockNumber: Math.floor(Math.random() * 1000000) + 15000000, transactionHash: `0x${'d'.repeat(64)}`, transactionIndex: 0, blockHash: `0x${'e'.repeat(64)}`, logIndex: 0, removed: false, id: `${filter.address}_${filter.event}_${Date.now()}`, returnValues: { from: '0x0000000000000000000000000000000000000000', to: '0x742d35Cc6634C0532925a3b844Bc9e7595f6933f', value: '1000000000000000000' }, event: filter.event, signature: `${filter.event}(address,address,uint256)`, raw: { data: '0x', topics: [] } }; if (listener.active) { callback(mockEvent); this.emit('event:received', mockEvent); } }, 50); } return listener; } async removeEventListener(listener) { const storedListener = this.eventListeners.get(listener.id); if (storedListener) { storedListener.active = false; this.eventListeners.delete(listener.id); if (USE_REAL_CONTRACTS && this.provider) { // Remove real event listener const contractInstance = this.contractInstances.get(listener.filter.address); if (contractInstance) { contractInstance.removeAllListeners(listener.filter.event); } } } } async queryEvents(filter) { if (USE_REAL_CONTRACTS && this.provider && provider_factory_1.ProviderFactory.isRealProvider(this.provider)) { // Real event query const contract = this.contracts.get(filter.address); if (!contract) return []; let contractInstance = this.contractInstances.get(filter.address); if (!contractInstance) { contractInstance = new ethers_1.ethers.Contract(filter.address, contract.abi, this.provider); this.contractInstances.set(filter.address, contractInstance); } // Get event fragment const eventFragment = contractInstance.interface.getEvent(filter.event); if (!eventFragment) { throw new Error(`Event ${filter.event} not found in contract ABI`); } // Create filter with parameters // eslint-disable-next-line @typescript-eslint/no-explicit-any const filters = contractInstance.filters; const topicFilter = filter.filter ? filters && filters[filter.event] ? filters[filter.event](...Object.values(filter.filter)) : filter.event : filters && filters[filter.event] ? filters[filter.event]() : filter.event; const logs = await contractInstance.queryFilter(topicFilter, filter.fromBlock, filter.toBlock); return logs.map(log => { const parsedLog = contractInstance.interface.parseLog({ topics: log.topics, data: log.data }); return { address: log.address, blockNumber: log.blockNumber, transactionHash: log.transactionHash, transactionIndex: log.index, blockHash: log.blockHash, logIndex: log.index, removed: log.removed, id: `${log.transactionHash}_${log.index}`, returnValues: parsedLog ? Object.fromEntries(parsedLog.fragment.inputs.map((input, i) => [input.name, parsedLog.args[i]])) : {}, event: filter.event, signature: eventFragment.format('sighash'), raw: { data: log.data, topics: log.topics } }; }); } else { // Mock implementation - return empty array return []; } } getContract(address) { return this.contracts.get(address); } getAllContracts() { return Array.from(this.contracts.values()); } getContractMetadata(_address) { // Mock metadata return { name: 'Mock Contract', symbol: 'MOCK', decimals: 18, totalSupply: '1000000000000000000000000', owner: '0x0000000000000000000000000000000000000000', compiler: 'solc-0.8.19', optimizer: true, runs: 200 }; } async estimateDeploymentGas(deployment) { if (USE_REAL_CONTRACTS && this.provider && provider_factory_1.ProviderFactory.isRealProvider(this.provider)) { try { const contractFactory = new ethers_1.ethers.ContractFactory(deployment.abi, deployment.bytecode, await this.provider.getSigner()); const deploymentArgs = deployment.constructorArgs || []; const deployTx = await contractFactory.getDeployTransaction(...deploymentArgs); const gasEstimate = await this.provider.estimateGas(deployTx); return gasEstimate; } catch (error) { console.warn('Gas estimation failed, using default:', error); return 3000000n; // Default gas limit } } // Mock gas estimation return 1500000n; } encodeConstructor(abi, args) { const constructorAbi = abi.find(item => item.type === 'constructor'); if (!constructorAbi) return '0x'; const abiCoder = new ethers_1.ethers.AbiCoder(); const types = constructorAbi.inputs?.map(input => input.type) || []; return abiCoder.encode(types, args); } encodeFunctionCall(abi, functionName, args) { const functionAbi = abi.find(item => item.type === 'function' && item.name === functionName); if (!functionAbi) throw new Error(`Function ${functionName} not found in ABI`); const iface = new ethers_1.ethers.Interface(abi); return iface.encodeFunctionData(functionName, args); } validateABI(abi) { if (!Array.isArray(abi)) { throw new Error('Invalid ABI: must be an array'); } for (const item of abi) { if (!item.type) { throw new Error('Invalid ABI: item missing type'); } } } getFunctionSignatures(abi) { return abi .filter(item => item.type === 'function') .map(item => { const inputs = item.inputs?.map(i => i.type).join(',') || ''; return `${item.name}(${inputs})`; }); } getEventSignatures(abi) { return abi .filter(item => item.type === 'event') .map(item => { const inputs = item.inputs?.map(i => i.type).join(',') || ''; return `${item.name}(${inputs})`; }); } getContractInstance(address) { return this.contractInstances.get(address); } async batchCall(address, calls) { if (USE_REAL_CONTRACTS && this.provider && provider_factory_1.ProviderFactory.isRealProvider(this.provider)) { const contract = this.contracts.get(address); if (!contract) throw new Error('Contract not found'); let contractInstance = this.contractInstances.get(address); if (!contractInstance) { contractInstance = new ethers_1.ethers.Contract(address, contract.abi, this.provider); this.contractInstances.set(address, contractInstance); } // Use Promise.all with encoded calls return Promise.all(calls.map(async (call) => { const data = contractInstance.interface.encodeFunctionData(call.method, call.args); const result = await this.provider.call({ to: address, data }); return contractInstance.interface.decodeFunctionResult(call.method, result); })); } else { // Mock batch results return calls.map(call => `Mock result for ${call.method}`); } } async reinitialize() { this.contracts.clear(); this.eventListeners.clear(); this.contractInstances.clear(); await this.initialize(); } async checkHealth() { return { isHealthy: this.isEnabled, connectedChains: 1, activeContracts: this.contracts.size, lastBlockNumber: 0, rpcLatency: 0 }; } getMetrics() { return { totalTransactions: 0, successfulTransactions: 0, failedTransactions: 0, averageGasUsed: '100000', totalValueTransferred: '0', activeWallets: 0 }; } async shutdown() { // Remove all event listeners for (const contractInstance of this.contractInstances.values()) { if (contractInstance instanceof ethers_1.ethers.Contract) { contractInstance.removeAllListeners(); } } this.contracts.clear(); this.eventListeners.clear(); this.contractInstances.clear(); this.removeAllListeners(); } // Compatibility aliases async getHealth() { return this.checkHealth(); } } exports.SmartContractManager = SmartContractManager; //# sourceMappingURL=smart-contract-manager.js.map