From 71727084cf2b467030c5481690d815903a32edbb Mon Sep 17 00:00:00 2001 From: npty Date: Thu, 13 Feb 2025 15:24:16 +0700 Subject: [PATCH] feat: improve GMP transaction status retrieval --- ...getTransactionStatusOnDestinationChains.ts | 135 ++++++++---------- 1 file changed, 61 insertions(+), 74 deletions(-) diff --git a/apps/maestro/src/server/routers/gmp/getTransactionStatusOnDestinationChains.ts b/apps/maestro/src/server/routers/gmp/getTransactionStatusOnDestinationChains.ts index 88655d5f..88e699f7 100644 --- a/apps/maestro/src/server/routers/gmp/getTransactionStatusOnDestinationChains.ts +++ b/apps/maestro/src/server/routers/gmp/getTransactionStatusOnDestinationChains.ts @@ -5,6 +5,13 @@ import { z } from "zod"; import { publicProcedure } from "~/server/trpc"; +export type ChainStatus = { + status: GMPTxStatus; + txHash: string; + txId: string; + logIndex: number; +}; + export const SEARCHGMP_SOURCE = { includes: [ "call.returnValues.destinationChain", @@ -20,19 +27,13 @@ export const SEARCHGMP_SOURCE = { "gas", "gas_price_rate", "gas_paid", - "approved.receipt", - "confirm.receipt", - "executed.receipt", - "approved.topics", - "approved.returnValues", - "confirm.returnValues", - "executed.returnValues", - "approved.transaction", - "confirm.transaction", - "executed.transaction", - "approved.created_at", - "confirm.created_at", - "executed.created_at", + ...["approved", "confirm", "executed"].flatMap((prefix) => [ + `${prefix}.receipt`, + `${prefix}.topics`, + `${prefix}.returnValues`, + `${prefix}.transaction`, + `${prefix}.created_at`, + ]), ], }; @@ -40,6 +41,38 @@ const INPUT_SCHEMA = z.object({ txHash: z.string(), }); +async function processGMPData( + gmpData: any, + ctx: any +): Promise<[string, ChainStatus]> { + const { call, callback, status: firstHopStatus } = gmpData; + const destinationChain = ( + callback?.returnValues.destinationChain ?? + call.returnValues.destinationChain + ).toLowerCase(); + + let status = firstHopStatus; + + // Handle second hop for non-EVM chains + if (call.chain_type !== "evm" && callback) { + const secondHopData = await ctx.services.gmp.searchGMP({ + txHash: callback.returnValues.messageId, + _source: SEARCHGMP_SOURCE, + }); + status = secondHopData[0].status; + } + + return [ + destinationChain, + { + status, + txHash: call.transactionHash, + logIndex: call.logIndex ?? call._logIndex ?? 0, + txId: gmpData.message_id, + }, + ]; +} + /** * Get the status of an GMP transaction on destination chains */ @@ -52,71 +85,25 @@ export const getTransactionStatusOnDestinationChains = publicProcedure _source: SEARCHGMP_SOURCE, }); - if (data.length) { - const pendingResult = data.reduce( - async (acc, gmpData) => { - const { call, callback, status: firstHopStatus } = gmpData; - - const chainType = gmpData.call.chain_type; - let secondHopStatus: GMPTxStatus = "confirming"; - - if (gmpData.callback) { - const secondHopMessageId = - gmpData.callback.returnValues.messageId; - - const secondHopData = await ctx.services.gmp.searchGMP({ - txHash: secondHopMessageId, - _source: SEARCHGMP_SOURCE, - }); - - secondHopStatus = secondHopData[0].status; - } - - // For 2-hops transaction, the destination chain in callback is the final destination chain, which only exist after the transaction is already executed at the Axelar chain. - const destinationChain = - callback?.returnValues.destinationChain?.toLowerCase() || - call.returnValues.destinationChain.toLowerCase(); - - const awaitedAcc = await acc; - - return { - ...awaitedAcc, - [destinationChain]: { - status: chainType === "evm" ? firstHopStatus : secondHopStatus, - txHash: call.transactionHash, - logIndex: call.logIndex ?? call._logIndex ?? 0, - txId: gmpData.message_id, - }, - }; - }, - Promise.resolve( - {} as { - [chainId: string]: { - status: GMPTxStatus; - txHash: string; - txId: string; - logIndex: number; - }; - } - ) - ); + if (!data.length) { + throw new TRPCError({ + code: "NOT_FOUND", + message: "Transaction not found", + }); + } - const result = await pendingResult; + // Process all GMP data entries in parallel + const processedEntries = await Promise.all( + data.map((gmpData) => processGMPData(gmpData, ctx)) + ); - return result; - } + // Convert the processed entries into a ChainStatusMap + const result = Object.fromEntries(processedEntries); - // If we don't find the transaction, we throw a 404 error - throw new TRPCError({ - code: "NOT_FOUND", - message: "Transaction not found", - }); + return result; } catch (error) { - // If we get a TRPC error, we throw it - if (error instanceof TRPCError) { - throw error; - } - // otherwise, we throw an internal server error + if (error instanceof TRPCError) throw error; + throw new TRPCError({ code: "INTERNAL_SERVER_ERROR", message: "Failed to get transaction status",