UNPKG

blockchain-api

Version:

API utilities for interacting with the Exatechl2 blockchain

256 lines (211 loc) 7.35 kB
/** * Contract Operations */ import { callRpc } from '../../core/rpc'; import { callExplorerApi } from '../../core/explorerApi'; import { getTransactionReceipt } from '../transactions/transactions'; import { getBlockByNumber, getLatestBlockNumber } from '../blocks/blocks'; import { ContractInfo } from '../../types'; import { parseHexToDecimal, } from '../../utils/formatters'; import { registerSystemContract, } from '../../utils/registry'; import { isSystemContract, checkIsContract, getContractBytecode, isTransactionObject } from './contract-utils'; import { logger } from '../../utils/logger'; const initializeSystemContracts = () => { const systemContracts = [ { address: '0x00000000000000000000000000000000000a4b05', name: 'Core System Contract' } ]; systemContracts.forEach(contract => { registerSystemContract(contract.address, contract.name); }); }; initializeSystemContracts(); /** * @param {string} address * @returns {Promise<ContractInfo>} */ export async function getContractInfo(address: string): Promise<ContractInfo> { try { const isContractAddress = await checkIsContract(address); if (!isContractAddress) { throw new Error(`Address ${address} is not a contract`); } const bytecode = await getContractBytecode(address); const balance = await callRpc<string>('eth_getBalance', [address, 'latest']); if (isSystemContract(address.toLowerCase())) { return { address, bytecode, creator: "System Contract", creationTx: null, creationTimestamp: null, balance, isVerified: false }; } let creator = null; let creationTx = null; let creationTimestamp = null; let isVerified = false; try { const contractFromExplorer = await callExplorerApi<any>(`contracts/${address}`); if (contractFromExplorer && contractFromExplorer.data) { const contractData = contractFromExplorer.data; creator = contractData.creator_address || null; creationTx = contractData.creation_transaction || null; creationTimestamp = contractData.creation_timestamp || null; isVerified = contractData.is_verified || false; logger.debug(`Got contract info from Explorer DB for ${address}`); } } catch (e: any) { logger.warn(`Could not fetch contract info from Explorer DB: ${e.message}`); } return { address, bytecode, creator, creationTx, creationTimestamp, balance, isVerified }; } catch (error) { logger.error('Error getting contract info:', error); throw error; } } /** * * @param {string} address * @returns {Promise<boolean>} */ export async function isContractVerified(address: string): Promise<boolean> { try { const isContractAddress = await checkIsContract(address); if (!isContractAddress) { return false; } try { const contractFromExplorer = await callExplorerApi<any>(`contracts/${address}`); if (contractFromExplorer && contractFromExplorer.data) { return contractFromExplorer.data.is_verified || false; } } catch (e) { logger.warn(`Could not fetch verification status from Explorer DB: ${e}`); } return false; } catch (error) { logger.error('Error checking contract verification:', error); return false; } } /** * * @param {string} address * @returns {Promise<object | null>} */ export async function getContractAbi(address: string): Promise<object | null> { try { const isVerified = await isContractVerified(address); if (!isVerified) { return null; } try { const abiFromExplorer = await callExplorerApi<any>(`contracts/${address}/abi`); if (abiFromExplorer && abiFromExplorer.data && abiFromExplorer.data.abi) { return JSON.parse(abiFromExplorer.data.abi); } } catch (e) { logger.warn(`Could not fetch ABI from Explorer DB: ${e}`); } return null; } catch (error) { logger.error('Error getting contract ABI:', error); return null; } } /** * * @param {number} maxResults * @param {number} maxBlocks * @returns {Promise<string[]>} */ export async function findValidContracts(maxResults: number = 5, maxBlocks: number = 10): Promise<string[]> { try { const latestBlockHex = await getLatestBlockNumber(); const latestBlock = parseHexToDecimal(latestBlockHex); const contractAddresses: string[] = []; const processedAddresses = new Set<string>(); for (let i = 0; i < maxBlocks; i++) { if (contractAddresses.length >= maxResults) { break; } const blockNumber = latestBlock - i; if (blockNumber < 0) break; try { const block = await getBlockByNumber(blockNumber, true); if (block && block.transactions && Array.isArray(block.transactions)) { const contractCreations = block.transactions .filter(tx => isTransactionObject(tx) && !tx.to); for (const tx of contractCreations) { if (isTransactionObject(tx)) { try { const receipt = await getTransactionReceipt(tx.hash); if (receipt && receipt.contractAddress && !processedAddresses.has(receipt.contractAddress)) { const isContract = await checkIsContract(receipt.contractAddress); if (isContract) { contractAddresses.push(receipt.contractAddress); processedAddresses.add(receipt.contractAddress); if (contractAddresses.length >= maxResults) { break; } } } } catch (e) { logger.warn(`Error processing transaction ${tx.hash}:`, e); } } } if (contractAddresses.length < maxResults) { const contractInteractions = block.transactions .filter(tx => isTransactionObject(tx) && tx.to); for (const tx of contractInteractions) { if (isTransactionObject(tx) && tx.to) { if (processedAddresses.has(tx.to)) { continue; } try { const isContract = await checkIsContract(tx.to); if (isContract) { contractAddresses.push(tx.to); processedAddresses.add(tx.to); if (contractAddresses.length >= maxResults) { break; } } } catch (e) { logger.warn(`Error checking if address is contract ${tx.to}:`, e); } } } } } } catch (e) { logger.warn(`Error processing block ${blockNumber}:`, e); continue; } } return contractAddresses; } catch (error) { logger.error('Error finding valid contracts:', error); return []; } }