diff --git a/src/core/services/mirrornode/__tests__/unit/mocks.ts b/src/core/services/mirrornode/__tests__/unit/mocks.ts index fc2564ff2..f45e3775a 100644 --- a/src/core/services/mirrornode/__tests__/unit/mocks.ts +++ b/src/core/services/mirrornode/__tests__/unit/mocks.ts @@ -176,7 +176,6 @@ export const createMockTransactionDetailsResponse = ( transaction_hash: 'hash123', name: 'CRYPTOTRANSFER', node: '0.0.3', - transaction_fee: 100000, scheduled: false, transfers: [ { account: '0.0.1234', amount: -1000000 }, diff --git a/src/core/services/mirrornode/hedera-mirrornode-service.ts b/src/core/services/mirrornode/hedera-mirrornode-service.ts index b8e98bfa4..f412e4652 100644 --- a/src/core/services/mirrornode/hedera-mirrornode-service.ts +++ b/src/core/services/mirrornode/hedera-mirrornode-service.ts @@ -44,6 +44,7 @@ import { TopicInfoSchema, TopicMessagesAPIResponseSchema, TopicMessageSchema, + TransactionDetailsResponseSchema, } from './schemas'; import { MirrorNodeKeyType, NetworkToBaseUrl } from './types'; @@ -450,7 +451,11 @@ export class HederaMirrornodeServiceDefaultImpl implements HederaMirrornodeServi ); } - return (await response.json()) as TransactionDetailsResponse; + return parseWithSchema( + TransactionDetailsResponseSchema, + await response.json(), + `Mirror Node GET /transactions/${transactionId}`, + ); } catch (error) { if (error instanceof CliError) throw error; throw new NetworkError( diff --git a/src/core/services/mirrornode/schemas.ts b/src/core/services/mirrornode/schemas.ts index c07c8f834..926f4f036 100644 --- a/src/core/services/mirrornode/schemas.ts +++ b/src/core/services/mirrornode/schemas.ts @@ -19,6 +19,12 @@ import { type TopicMessage, type TopicMessageChunkInfo, type TopicMessagesAPIResponse, + type TransactionAssessedCustomFeeItem, + type TransactionDetailItem, + type TransactionDetailsResponse, + type TransactionNftTransferItem, + type TransactionTokenTransferItem, + type TransactionTransferItem, } from './types'; const mirrorKeyObject = z.object({ @@ -202,3 +208,60 @@ export const TopicInfoSchema: z.ZodType = z.object({ created_timestamp: z.string(), deleted: z.boolean(), }); + +const transactionTransferItemSchema: z.ZodType = + z.object({ + account: z.string(), + amount: z.number(), + is_approval: z.boolean().optional(), + }); + +const transactionTokenTransferItemSchema: z.ZodType = + z.object({ + token_id: z.string(), + account: z.string(), + amount: z.number(), + is_approval: z.boolean().optional(), + }); + +const transactionNftTransferItemSchema: z.ZodType = + z.object({ + is_approval: z.boolean(), + receiver_account_id: z.string(), + sender_account_id: z.string(), + serial_number: z.number(), + token_id: z.string(), + }); + +const transactionAssessedCustomFeeItemSchema: z.ZodType = + z.object({ + amount: z.number(), + collector_account_id: z.string(), + token_id: z.string().nullable().optional(), + effective_payer_account_ids: z.array(z.string()).optional(), + }); + +export const TransactionDetailItemSchema: z.ZodType = + z.object({ + transaction_id: z.string(), + consensus_timestamp: z.string(), + valid_start_timestamp: z.string(), + charged_tx_fee: z.number(), + memo_base64: z.union([z.string(), z.null()]).optional(), + result: z.string(), + transaction_hash: z.string(), + name: z.string(), + node: z.string(), + scheduled: z.boolean(), + transfers: z.array(transactionTransferItemSchema), + token_transfers: z.array(transactionTokenTransferItemSchema).optional(), + nft_transfers: z.array(transactionNftTransferItemSchema).optional(), + assessed_custom_fees: z + .array(transactionAssessedCustomFeeItemSchema) + .optional(), + }); + +export const TransactionDetailsResponseSchema: z.ZodType = + z.object({ + transactions: z.array(TransactionDetailItemSchema), + }); diff --git a/src/core/services/mirrornode/types.ts b/src/core/services/mirrornode/types.ts index e72a18f4c..18484b063 100644 --- a/src/core/services/mirrornode/types.ts +++ b/src/core/services/mirrornode/types.ts @@ -157,40 +157,53 @@ export interface TopicMessagesResponse { } // Transaction Details +export interface TransactionTransferItem { + account: string; + amount: number; + is_approval?: boolean; +} + +export interface TransactionTokenTransferItem { + token_id: string; + account: string; + amount: number; + is_approval?: boolean; +} + +export interface TransactionNftTransferItem { + is_approval: boolean; + receiver_account_id: string; + sender_account_id: string; + serial_number: number; + token_id: string; +} + +export interface TransactionAssessedCustomFeeItem { + amount: number; + collector_account_id: string; + token_id?: string | null; + effective_payer_account_ids?: string[]; +} + +export interface TransactionDetailItem { + transaction_id: string; + consensus_timestamp: string; + valid_start_timestamp: string; + charged_tx_fee: number; + memo_base64?: string | null; + result: string; + transaction_hash: string; + name: string; + node: string; + scheduled: boolean; + transfers: TransactionTransferItem[]; + token_transfers?: TransactionTokenTransferItem[]; + nft_transfers?: TransactionNftTransferItem[]; + assessed_custom_fees?: TransactionAssessedCustomFeeItem[]; +} + export interface TransactionDetailsResponse { - transactions: Array<{ - transaction_id: string; - consensus_timestamp: string; - valid_start_timestamp: string; - charged_tx_fee: number; - memo_base64?: string; - result: string; - transaction_hash: string; - name: string; - node: string; - transaction_fee: number; - scheduled: boolean; - transfers: Array<{ - account: string; - amount: number; - }>; - token_transfers?: Array<{ - account: string; - amount: number; - token_id: string; - }>; - nft_transfers?: Array<{ - account: string; - amount: number; - token_id: string; - serial_number: number; - }>; - assessed_custom_fees?: Array<{ - amount: number; - collector_account_id: string; - token_id?: string; - }>; - }>; + transactions: TransactionDetailItem[]; } // Contract Info