import {z} from 'zod' export type GetApiV2TracesTraceIdArgs = {trace_id: string}; export type GetApiV2TracesTraceIdOptions = {baseUrl?: string | undefined, fetch?: typeof fetch | undefined}; export const accountAddress = z.object({address: z.string(), icon: z.string().optional(), is_scam: z.boolean(), is_wallet: z.boolean(), name: z.string().optional()}); export const actionPhase = z.object({fwd_fees: z.number().int(), result_code: z.number().int(), result_code_description: z.string().optional(), skipped_actions: z.number().int(), success: z.boolean(), total_actions: z.number().int(), total_fees: z.number().int()}); export const bouncePhaseType = z.enum(["TrPhaseBounceNegfunds", "TrPhaseBounceNofunds", "TrPhaseBounceOk"]); export const computeSkipReason = z.enum(["cskip_no_state", "cskip_bad_state", "cskip_no_gas", "cskip_suspended"]); export const computePhase = z.object({exit_code: z.number().int().optional(), exit_code_description: z.string().optional(), gas_fees: z.number().int().optional(), gas_used: z.number().int().optional(), skip_reason: computeSkipReason.optional(), skipped: z.boolean(), success: z.boolean().optional(), vm_steps: z.number().int().optional()}); export const creditPhase = z.object({credit: z.number().int(), fees_collected: z.number().int()}); export const accountStatus = z.enum(["nonexist", "uninit", "active", "frozen"]); export const stateInit = z.object({boc: z.string(), interfaces: z.array(z.string())}); export const ecPreview = z.object({decimals: z.number().int(), id: z.number().int(), image: z.string(), symbol: z.string()}); export const extraCurrency = z.object({amount: z.string(), preview: ecPreview}); export const message = z.object({bounce: z.boolean(), bounced: z.boolean(), created_at: z.number().int(), created_lt: z.number().int(), decoded_body: z.unknown(), decoded_op_name: z.string().optional(), destination: accountAddress.optional(), fwd_fee: z.number().int(), hash: z.string(), ihr_disabled: z.boolean(), ihr_fee: z.number().int(), import_fee: z.number().int(), init: stateInit.optional(), msg_type: z.enum(["int_msg", "ext_in_msg", "ext_out_msg"]), op_code: z.string().optional(), raw_body: z.string().optional(), source: accountAddress.optional(), value: z.number().int(), value_extra: z.array(extraCurrency).optional()}); export const accStatusChange = z.enum(["acst_unchanged", "acst_frozen", "acst_deleted"]); export const storagePhase = z.object({fees_collected: z.number().int(), fees_due: z.number().int().optional(), status_change: accStatusChange}); export const transactionType = z.enum(["TransOrd", "TransTickTock", "TransSplitPrepare", "TransSplitInstall", "TransMergePrepare", "TransMergeInstall", "TransStorage"]); export const transaction = z.object({aborted: z.boolean(), account: accountAddress, action_phase: actionPhase.optional(), block: z.string(), bounce_phase: bouncePhaseType.optional(), compute_phase: computePhase.optional(), credit_phase: creditPhase.optional(), destroyed: z.boolean(), end_balance: z.number().int(), end_status: accountStatus, hash: z.string(), in_msg: message.optional(), lt: z.number().int(), orig_status: accountStatus, out_msgs: z.array(message), prev_trans_hash: z.string().optional(), prev_trans_lt: z.number().int().optional(), raw: z.string(), state_update_new: z.string(), state_update_old: z.string(), storage_phase: storagePhase.optional(), success: z.boolean(), total_fees: z.number().int(), transaction_type: transactionType, utime: z.number().int()}); export const trace: z.ZodType = z.object({children: z.array(z.lazy(() => trace)).optional(), emulated: z.boolean().optional(), interfaces: z.array(z.string()), transaction: transaction}); export const validateResponse = async ( schema: { readonly '~standard': { readonly validate: (value: unknown) => ({ readonly value: Output; readonly issues?: undefined } | { readonly issues: ReadonlyArray<{ readonly message: string; readonly path?: ReadonlyArray | undefined }> }) | Promise<{ readonly value: Output; readonly issues?: undefined } | { readonly issues: ReadonlyArray<{ readonly message: string; readonly path?: ReadonlyArray | undefined }> }> } }, value: unknown ): Promise => { const result = await schema['~standard'].validate(value) if (result.issues !== undefined) { const detail = result.issues .map(({ message, path }) => path?.length ? `${path.map((segment) => String(typeof segment === 'object' ? segment.key : segment)).join('.')}: ${message}` : message ) .join('; ') throw new Error(`Response does not match the schema: ${detail}`, { cause: { issues: result.issues } }) } return result.value }; /** * Get the trace by trace ID or hash of any transaction in trace * * GET /v2/traces/{trace_id} */ export const getApiV2TracesTraceId = async (args: GetApiV2TracesTraceIdArgs, options?: GetApiV2TracesTraceIdOptions) => { const {baseUrl = "https://tonapi.io", fetch: fetchFn = fetch} = options ?? {} const query = new URLSearchParams() const headers = new Headers() headers.set('accept', "application/json") const url = `${baseUrl.replace(/\/+$/, '')}/v2/traces/${encodeURIComponent(String(args.trace_id))}${query.size > 0 ? `?${query}` : ''}` const res = await fetchFn(url, {method: "GET", headers}) if (!res.ok) throw new Error(`Request failed with status ${res.status}`, {cause: {status: res.status, body: await res.text()}}) return validateResponse(trace, await res.json()) };