diff --git a/packages/util/src/eth-rpc-handlers.ts b/packages/util/src/eth-rpc-handlers.ts index 9cb288ca..6cd8c4ce 100644 --- a/packages/util/src/eth-rpc-handlers.ts +++ b/packages/util/src/eth-rpc-handlers.ts @@ -1,6 +1,25 @@ /* eslint-disable @typescript-eslint/no-unused-vars */ import { IndexerInterface } from './types'; +const CODE_INVALID_PARAMS = -32602; +const CODE_INTERNAL_ERROR = -32603; +const CODE_SERVER_ERROR = -32000; + +const ERROR_CONTRACT_MAP_NOT_SET = 'Contract map not set'; +const ERROR_CONTRACT_ABI_NOT_FOUND = 'Contract ABI not found'; +const ERROR_CONTRACT_INSUFFICIENT_PARAMS = 'Insufficient params'; +const ERROR_CONTRACT_NOT_RECOGNIZED = 'Contract not recognized'; +const ERROR_CONTRACT_METHOD_NOT_FOUND = 'Contract method not found'; +const ERROR_METHOD_NOT_IMPLEMENTED = 'Method not implemented'; + +class ErrorWithCode extends Error { + code: number; + constructor (code: number, message: string) { + super(message); + this.code = code; + } +} + export const createEthRPCHandlers = async ( indexer: IndexerInterface ): Promise => { @@ -13,48 +32,64 @@ export const createEthRPCHandlers = async ( }, eth_call: async (args: any, callback: any) => { - // TODO: Handle empty args - // TODO: Set errors in response // TODO: Parse blockTag - const { to, data, blockTag } = args[0]; + try { + if (args.length === 0) { + throw new ErrorWithCode(CODE_INVALID_PARAMS, ERROR_CONTRACT_INSUFFICIENT_PARAMS); + } - // For values other than blockHash, resolve value from block_progress table - const latestBlock = await indexer.getLatestCanonicalBlock(); - const blockHash = latestBlock?.blockHash; + const { to, data, blockTag } = args[0]; - const watchedContract = indexer.getWatchedContracts().find(contract => contract.address === to); - if (!watchedContract) { - throw new Error('Contract not recognized'); - } + if (!indexer.contractMap) { + throw new ErrorWithCode(CODE_INTERNAL_ERROR, ERROR_CONTRACT_MAP_NOT_SET); + } - if (!indexer.contractMap) { - throw new Error('Contract map not found'); - } + // For values other than blockHash, resolve value from block_progress table + const latestBlock = await indexer.getLatestCanonicalBlock(); + const blockHash = latestBlock?.blockHash; - const contractInterface = indexer.contractMap.get(watchedContract.kind); - if (!contractInterface) { - throw new Error('Contract ABI not found'); - } + const watchedContract = indexer.getWatchedContracts().find(contract => contract.address === to); + if (!watchedContract) { + throw new ErrorWithCode(CODE_INVALID_PARAMS, ERROR_CONTRACT_NOT_RECOGNIZED); + } - // Slice out method signature - const functionSelector = data.slice(0, 10); + const contractInterface = indexer.contractMap.get(watchedContract.kind); + if (!contractInterface) { + throw new ErrorWithCode(CODE_INTERNAL_ERROR, ERROR_CONTRACT_ABI_NOT_FOUND); + } - // Find the matching function from the ABI - const functionFragment = contractInterface.getFunction(functionSelector); - if (!functionFragment) { - throw new Error('Method not found'); - } + // Slice out method signature from data + const functionSelector = data.slice(0, 10); - // Decode the data based on the matched function - const decodedData = contractInterface.decodeFunctionData(functionFragment, data); + // Find the matching function from the ABI + const functionFragment = contractInterface.getFunction(functionSelector); + if (!functionFragment) { + throw new ErrorWithCode(CODE_INVALID_PARAMS, ERROR_CONTRACT_METHOD_NOT_FOUND); + } + + // Decode the data based on the matched function + const decodedData = contractInterface.decodeFunctionData(functionFragment, data); + + const functionName = functionFragment.name; + const indexerMethod = (indexer as any)[functionName].bind(indexer); + if (!indexerMethod) { + throw new ErrorWithCode(CODE_SERVER_ERROR, ERROR_METHOD_NOT_IMPLEMENTED); + } - const functionName = functionFragment.name; - const indexerMethod = (indexer as any)[functionName].bind(indexer); - if (indexerMethod && typeof indexerMethod === 'function') { const result = await indexerMethod(blockHash, to, ...decodedData); const encodedResult = contractInterface.encodeFunctionResult(functionFragment, [result.value]); + callback(null, encodedResult); + } catch (error: any) { + let callBackError; + if (error instanceof ErrorWithCode) { + callBackError = { code: error.code, message: error.message }; + } else { + callBackError = { code: CODE_SERVER_ERROR, message: error.message }; + } + + callback(callBackError); } },