From 7869b72aef63319368322bf6164518bab0d2200b Mon Sep 17 00:00:00 2001 From: Prathamesh Musale Date: Thu, 12 Sep 2024 19:51:02 +0530 Subject: [PATCH] Parse block tag in eth_call handler --- packages/cli/src/server.ts | 5 ++- packages/util/src/eth-rpc-handlers.ts | 53 +++++++++++++++++++++------ 2 files changed, 45 insertions(+), 13 deletions(-) diff --git a/packages/cli/src/server.ts b/packages/cli/src/server.ts index d7174091..995c052a 100644 --- a/packages/cli/src/server.ts +++ b/packages/cli/src/server.ts @@ -277,7 +277,7 @@ export class ServerCmd { ) => Promise, typeDefs: TypeSource, paymentsManager?: PaymentsManager, - createEthRPCHandlers?: (indexer: IndexerInterface) => Promise + createEthRPCHandlers?: (indexer: IndexerInterface, ethProvider: JsonRpcProvider) => Promise ): Promise<{ app: Application, server: ApolloServer @@ -286,6 +286,7 @@ export class ServerCmd { const jobQueue = this._baseCmd.jobQueue; const indexer = this._baseCmd.indexer; const eventWatcher = this._baseCmd.eventWatcher; + const ethProvider = this._baseCmd.ethProvider; assert(config); assert(jobQueue); @@ -318,7 +319,7 @@ export class ServerCmd { const gqlLogger = createGQLLogger(config.server.gql.logDir); const resolvers = await createResolvers(indexer, eventWatcher, gqlLogger); - const ethRPCHandlers = createEthRPCHandlers ? await createEthRPCHandlers(indexer) : {}; + const ethRPCHandlers = createEthRPCHandlers ? await createEthRPCHandlers(indexer, ethProvider) : {}; // Create an Express app const app: Application = express(); diff --git a/packages/util/src/eth-rpc-handlers.ts b/packages/util/src/eth-rpc-handlers.ts index 6cd8c4ce..8bd0589f 100644 --- a/packages/util/src/eth-rpc-handlers.ts +++ b/packages/util/src/eth-rpc-handlers.ts @@ -1,4 +1,8 @@ /* eslint-disable @typescript-eslint/no-unused-vars */ +import { utils } from 'ethers'; + +import { JsonRpcProvider } from '@ethersproject/providers'; + import { IndexerInterface } from './types'; const CODE_INVALID_PARAMS = -32602; @@ -11,6 +15,7 @@ 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'; +const ERROR_INVALID_BLOCK_TAG = 'Invalid block tag'; class ErrorWithCode extends Error { code: number; @@ -21,7 +26,8 @@ class ErrorWithCode extends Error { } export const createEthRPCHandlers = async ( - indexer: IndexerInterface + indexer: IndexerInterface, + ethProvider: JsonRpcProvider ): Promise => { return { eth_blockNumber: async (args: any, callback: any) => { @@ -32,22 +38,18 @@ export const createEthRPCHandlers = async ( }, eth_call: async (args: any, callback: any) => { - // TODO: Parse blockTag - try { + if (!indexer.contractMap) { + throw new ErrorWithCode(CODE_INTERNAL_ERROR, ERROR_CONTRACT_MAP_NOT_SET); + } + if (args.length === 0) { throw new ErrorWithCode(CODE_INVALID_PARAMS, ERROR_CONTRACT_INSUFFICIENT_PARAMS); } const { to, data, blockTag } = args[0]; - if (!indexer.contractMap) { - throw new ErrorWithCode(CODE_INTERNAL_ERROR, ERROR_CONTRACT_MAP_NOT_SET); - } - - // For values other than blockHash, resolve value from block_progress table - const latestBlock = await indexer.getLatestCanonicalBlock(); - const blockHash = latestBlock?.blockHash; + const blockHash = await parseBlockTag(indexer, ethProvider, blockTag); const watchedContract = indexer.getWatchedContracts().find(contract => contract.address === to); if (!watchedContract) { @@ -78,7 +80,7 @@ export const createEthRPCHandlers = async ( } const result = await indexerMethod(blockHash, to, ...decodedData); - const encodedResult = contractInterface.encodeFunctionResult(functionFragment, [result.value]); + const encodedResult = contractInterface.encodeFunctionResult(functionFragment, Array.isArray(result.value) ? result.value : [result.value]); callback(null, encodedResult); } catch (error: any) { @@ -98,3 +100,32 @@ export const createEthRPCHandlers = async ( } }; }; + +const parseBlockTag = async (indexer: IndexerInterface, ethProvider: JsonRpcProvider, blockTag: any): Promise => { + if (utils.isHexString(blockTag)) { + // Return value if hex string is of block hash length + if (utils.hexDataLength(blockTag) === 32) { + return blockTag; + } + + // Treat hex value as a block number + const block = await ethProvider.getBlock(blockTag); + return block.hash; + } + + // TODO: Handle pending, safe and finalized + if (['earliest', 'latest', 'pending', 'safe', 'finalized', null, undefined].includes(blockTag)) { + const syncStatus = await indexer.getSyncStatus(); + if (!syncStatus) { + throw new ErrorWithCode(CODE_INTERNAL_ERROR, 'SyncStatus not found'); + } + + if (blockTag === 'earliest') { + return syncStatus.initialIndexedBlockHash; + } + + return syncStatus.latestIndexedBlockHash; + } + + throw new ErrorWithCode(CODE_INVALID_PARAMS, ERROR_INVALID_BLOCK_TAG); +};