import {z} from 'zod' export type PreScreening = {enabled: boolean}; export type ConversionOperationType = 'CONVERSION'; export type ConversionOperationConfigParams = {amount?: string | undefined, accountId?: string | undefined, srcAssetId?: string | undefined, destAssetId: string, slippageBasisPoints?: number | undefined}; export type CreateConversionConfigOperationRequest = {type: ConversionOperationType, params: ConversionOperationConfigParams}; export type TransferOperationType = 'TRANSFER'; export type AccountType = 'EXCHANGE_ACCOUNT' | 'UNMANAGED_WALLET' | 'VAULT_ACCOUNT' | 'NETWORK_CONNECTION' | 'FIAT_ACCOUNT'; export type Account = {accountId: string, accountType: AccountType}; export type OneTimeAddressAccount = {oneTimeAddress: string, tag?: string | undefined}; export type Destination = Account | OneTimeAddressAccount; export type TransferOperationConfigParams = {amount?: string | undefined, assetId?: string | undefined, source?: Account | undefined, destination: Destination}; export type CreateTransferConfigOperationRequest = {type: TransferOperationType, params: TransferOperationConfigParams}; export type DisbursementOperationType = 'DISBURSEMENT'; export type DisbursementAmountInstruction = {payeeAccount: Destination, assetId: string, amount: string}; export type DisbursementPercentageInstruction = {payeeAccount: Destination, assetId: string, percentage: string}; export type DisbursementInstruction = DisbursementAmountInstruction | DisbursementPercentageInstruction; export type DisbursementOperationConfigParams = {paymentAccount?: Account | undefined, instructionSet: Array}; export type CreateDisbursementConfigOperationRequest = {type: DisbursementOperationType, params: DisbursementOperationConfigParams}; export type CreateConfigOperationRequest = CreateConversionConfigOperationRequest | CreateTransferConfigOperationRequest | CreateDisbursementConfigOperationRequest; export type CorrelationData = {[key: string]: string} | string; export type CreateWorkflowConfigurationRequest = {configName: string, preScreening?: PreScreening | undefined, configOperations: Array, externalCorrelationData?: CorrelationData | undefined}; export type CreateApiPaymentsWorkflowConfigArgs = {'Idempotency-Key'?: string | undefined, body: CreateWorkflowConfigurationRequest}; export type CreateApiPaymentsWorkflowConfigOptions = {baseUrl?: string | undefined, fetch?: typeof fetch | undefined}; export const preScreening = z.object({enabled: z.boolean()}); export const workflowConfigStatus = z.enum(["PENDING", "VALIDATION_IN_PROGRESS", "VALIDATION_FAILED", "READY_FOR_EXECUTION"]); export const conversionOperationType = z.literal("CONVERSION"); export const conversionOperationConfigParams = z.object({amount: z.string().optional(), accountId: z.string().optional(), srcAssetId: z.string().optional(), destAssetId: z.string(), slippageBasisPoints: z.number().int().gte(0).lte(10000).optional()}); export const configOperationStatus = z.enum(["PENDING", "VALIDATION_IN_PROGRESS", "READY_FOR_EXECUTION", "VALIDATION_FAILED"]); export const conversionValidationFailure = z.object({reason: z.enum(["ACCOUNT_NOT_FOUND", "ACCOUNT_TYPE_NOT_SUPPORTED", "INSUFFICIENT_BALANCE", "ASSET_NOT_FOUND", "ASSETS_CONTINUITY_MISMATCH", "EXCHANGE_BASKETS_MISMATCH", "ACCOUNTS_CONTINUITY_MISMATCH", "ONE_TIME_ADDRESS_CONTINUITY_NOT_ALLOWED", "EQUAL_ACCOUNTS_NOT_ALLOWED", "EQUAL_ASSETS_NOT_ALLOWED", "INVALID_AMOUNT", "UNMANAGED_WALLET_AS_SOURCE_NOT_ALLOWED", "MANAGED_OPERATION_PARAMS_INVALID_SCHEMA", "ACCOUNT_IS_NOT_EXCHANGE", "UNSUPPORTED_TRADING_METHOD", "ASSETS_CAN_NOT_CONVERTED"]), data: z.record(z.string(), z.unknown()).optional()}); export const conversionConfigOperation = z.object({operationId: z.string(), type: conversionOperationType, params: conversionOperationConfigParams, status: configOperationStatus, validationFailure: conversionValidationFailure.optional()}); export const transferOperationType = z.literal("TRANSFER"); export const accountType = z.enum(["EXCHANGE_ACCOUNT", "UNMANAGED_WALLET", "VAULT_ACCOUNT", "NETWORK_CONNECTION", "FIAT_ACCOUNT"]); export const account = z.object({accountId: z.string(), accountType: accountType}); export const oneTimeAddressAccount = z.object({oneTimeAddress: z.string(), tag: z.string().optional()}); export const destination = z.union([account, oneTimeAddressAccount]); export const transferOperationConfigParams = z.object({amount: z.string().optional(), assetId: z.string().optional(), source: account.optional(), destination: destination}); export const transferValidationFailure = z.object({reason: z.enum(["ACCOUNT_NOT_FOUND", "ACCOUNT_TYPE_NOT_SUPPORTED", "INSUFFICIENT_BALANCE", "ASSET_NOT_FOUND", "ASSETS_CONTINUITY_MISMATCH", "EXCHANGE_BASKETS_MISMATCH", "ACCOUNTS_CONTINUITY_MISMATCH", "ONE_TIME_ADDRESS_CONTINUITY_NOT_ALLOWED", "EQUAL_ACCOUNTS_NOT_ALLOWED", "EQUAL_ASSETS_NOT_ALLOWED", "INVALID_AMOUNT", "UNMANAGED_WALLET_AS_SOURCE_NOT_ALLOWED", "MANAGED_OPERATION_PARAMS_INVALID_SCHEMA"]), data: z.record(z.string(), z.unknown()).optional()}); export const transferConfigOperation = z.object({operationId: z.string(), type: transferOperationType, params: transferOperationConfigParams, status: configOperationStatus, validationFailure: transferValidationFailure.optional()}); export const disbursementOperationType = z.literal("DISBURSEMENT"); export const disbursementAmountInstruction = z.object({payeeAccount: destination, assetId: z.string(), amount: z.string()}); export const disbursementPercentageInstruction = z.object({payeeAccount: destination, assetId: z.string(), percentage: z.string()}); export const disbursementInstruction = z.union([disbursementAmountInstruction, disbursementPercentageInstruction]); export const disbursementOperationConfigParams = z.object({paymentAccount: account.optional(), instructionSet: z.array(disbursementInstruction)}); export const disbursementValidationFailure = z.object({reason: z.enum(["ACCOUNT_NOT_FOUND", "ACCOUNT_TYPE_NOT_SUPPORTED", "INSUFFICIENT_BALANCE", "ASSET_NOT_FOUND", "ASSETS_CONTINUITY_MISMATCH", "EXCHANGE_BASKETS_MISMATCH", "ACCOUNTS_CONTINUITY_MISMATCH", "ONE_TIME_ADDRESS_CONTINUITY_NOT_ALLOWED", "EQUAL_ACCOUNTS_NOT_ALLOWED", "EQUAL_ASSETS_NOT_ALLOWED", "INVALID_AMOUNT", "UNMANAGED_WALLET_AS_SOURCE_NOT_ALLOWED", "MANAGED_OPERATION_PARAMS_INVALID_SCHEMA", "INSTRUCTIONS_EXCEED_HUNDRED_PERCENT", "INSTRUCTIONS_ARRAY_EMPTY"]), data: z.record(z.string(), z.unknown()).optional()}); export const disbursementConfigOperation = z.object({operationId: z.string(), type: disbursementOperationType, params: disbursementOperationConfigParams, status: configOperationStatus, validationFailure: disbursementValidationFailure.optional()}); export const configOperation = z.union([conversionConfigOperation, transferConfigOperation, disbursementConfigOperation]); export const correlationData = z.union([z.record(z.string(), z.string()), z.string()]); export const workflowConfiguration = z.object({configId: z.string(), configName: z.string(), preScreening: preScreening.optional(), status: workflowConfigStatus, createdAt: z.number(), configOperations: z.array(configOperation), externalCorrelationData: correlationData.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 }; /** * Create payment flow configuration * * POST /payments/workflow_config */ export const createApiPaymentsWorkflowConfig = async (args: CreateApiPaymentsWorkflowConfigArgs, options?: CreateApiPaymentsWorkflowConfigOptions) => { const {baseUrl = "https://api.fireblocks.io/v1", fetch: fetchFn = fetch} = options ?? {} const query = new URLSearchParams() const headers = new Headers() headers.set('content-type', "application/json") headers.set('accept', "application/json") if (args['Idempotency-Key'] != null) headers.set("Idempotency-Key", String(args['Idempotency-Key'])) const url = `${baseUrl.replace(/\/+$/, '')}/payments/workflow_config${query.size > 0 ? `?${query}` : ''}` const res = await fetchFn(url, {method: "POST", headers, body: JSON.stringify(args.body)}) if (!res.ok) throw new Error(`Request failed with status ${res.status}`, {cause: {status: res.status, body: await res.text()}}) return validateResponse(workflowConfiguration, await res.json()) };