import {z} from 'zod' export type OrderStatus = 'CREATED' | 'AWAITING_PAYMENT' | 'AWAITING_INFORMATION' | 'PENDING_USER_ACTION' | 'PROCESSING' | 'CANCELED' | 'COMPLETED' | 'FAILED'; export type GetApiTradingOrdersArgs = {pageSize: number, pageCursor?: string | undefined, order?: ('ASC' | 'DESC') | undefined, accountId?: Array | undefined, providerId?: Array | undefined, statuses?: Array | undefined, startTime?: number | undefined, endTime?: number | undefined, assetConversionType?: ('DIGITAL_ONLY' | 'FIAT') | undefined}; export type GetApiTradingOrdersOptions = {baseUrl?: string | undefined, fetch?: typeof fetch | undefined}; export const accountAccessResponse = z.object({type: z.literal("PROVIDER_ACCOUNT"), providerId: z.string().optional(), accountId: z.string()}); export const directAccessResponse = z.object({type: z.literal("PROVIDER"), providerId: z.string(), subProviders: z.array(z.string()).optional()}); export const accessTypeResponse = z.discriminatedUnion("type", [accountAccessResponse, directAccessResponse]); export const side = z.enum(["BUY", "SELL"]); export const orderStatus = z.enum(["CREATED", "AWAITING_PAYMENT", "AWAITING_INFORMATION", "PENDING_USER_ACTION", "PROCESSING", "CANCELED", "COMPLETED", "FAILED"]); export const peerType = z.enum(["VAULT_ACCOUNT", "EXCHANGE_ACCOUNT", "UNMANAGED_WALLET", "FIAT_ACCOUNT", "CONNECTED_ACCOUNT"]); export const internalReference = z.object({type: peerType, accountId: z.string()}); export const oneTimeAddressPeerType = z.literal("ONE_TIME_ADDRESS"); export const oneTimeAddressReference = z.object({type: oneTimeAddressPeerType, address: z.string(), tag: z.string().optional()}); export const accountReference = z.discriminatedUnion("type", [internalReference, oneTimeAddressReference]); export const platformPeerType = z.enum(["VAULT_ACCOUNT", "CONNECTED_ACCOUNT", "FIAT_ACCOUNT"]); export const platformAccount = z.object({type: platformPeerType, accountId: z.string()}); export const externalAccountType = z.literal("EXTERNAL"); export const externalAccountMobileMoneyType = z.literal("MOBILE_MONEY_RAIL"); export const mobilePhoneNumber = z.string(); export const externalAccountMobileMoneyProvider = z.enum(["M_PESA", "AIRTEL", "MTN", "TIGO", "ORANGE"]); export const externalAccountMobileMoney = z.object({type: externalAccountMobileMoneyType, mobilePhoneNumber: mobilePhoneNumber, provider: externalAccountMobileMoneyProvider, email: z.string(), successRedirectUrl: z.string().optional()}); export const externalAccountLocalBankAfrica = z.object({successRedirectUrl: z.string().optional()}); export const externalAccountSenderInformation = z.union([externalAccountMobileMoney, externalAccountLocalBankAfrica]); export const externalAccount = z.object({type: externalAccountType, senderInformation: externalAccountSenderInformation.optional()}); export const settlementSourceAccount = z.discriminatedUnion("type", [platformAccount, externalAccount]); export const orderSummary = z.object({id: z.string(), via: accessTypeResponse, side: side, baseAmount: z.string(), quoteAmount: z.string().optional(), baseAssetId: z.string(), quoteAssetId: z.string(), status: orderStatus, destination: accountReference, source: settlementSourceAccount.optional(), createdAt: z.string()}); export const getOrdersResponse = z.object({data: z.array(orderSummary), total: z.number().int(), next: z.string().optional()}); 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 orders * * GET /trading/orders */ export const getApiTradingOrders = async (args: GetApiTradingOrdersArgs, options?: GetApiTradingOrdersOptions) => { const {baseUrl = "https://api.fireblocks.io/v1", fetch: fetchFn = fetch} = options ?? {} const query = new URLSearchParams() const headers = new Headers() headers.set('accept', "application/json") if (args.pageSize != null) query.append("pageSize", String(args.pageSize)) if (args.pageCursor != null) query.append("pageCursor", String(args.pageCursor)) if (args.order != null) query.append("order", String(args.order)) for (const item of args.accountId ?? []) query.append("accountId", String(item)) for (const item of args.providerId ?? []) query.append("providerId", String(item)) for (const item of args.statuses ?? []) query.append("statuses", String(item)) if (args.startTime != null) query.append("startTime", String(args.startTime)) if (args.endTime != null) query.append("endTime", String(args.endTime)) if (args.assetConversionType != null) query.append("assetConversionType", String(args.assetConversionType)) const url = `${baseUrl.replace(/\/+$/, '')}/trading/orders${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(getOrdersResponse, await res.json()) };