// Copyright 2026 Tether Operations Limited // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. /** * Validation utilities for WDK provider props and inputs * * These functions throw errors for invalid inputs. * Uses Zod schemas for validation with better error messages. * For type guards (boolean returns), see typeGuards.ts */ import { z } from 'zod' import { wdkConfigsSchema, accountIndexSchema, networkNameSchema, balanceStringSchema, assetIdSchema, } from './schemas' import type { WdkConfigs } from '../types' /** * Extract error message from Zod error */ function getZodErrorMessage(error: unknown): string { if (error instanceof z.ZodError) { // Get the first error message for simplicity const firstIssue = error.issues[0] if (firstIssue) { return firstIssue.message } return 'Validation failed' } if (error instanceof Error) { return error.message } return String(error) } /** * Validate network configuration */ export function validateWdkConfigs(networkConfigs: WdkConfigs): void { try { wdkConfigsSchema.parse(networkConfigs) } catch (error) { throw new Error(`Invalid networkConfigs: ${getZodErrorMessage(error)}`) } } /** * Validate balance refresh interval */ export function validateBalanceRefreshInterval(interval: number | undefined): void { if (interval !== undefined) { if (typeof interval !== 'number') { throw new Error('balanceRefreshInterval must be a number') } if (interval < 0) { throw new Error('balanceRefreshInterval must be a non-negative number') } if (!Number.isFinite(interval)) { throw new Error('balanceRefreshInterval must be a finite number') } } } /** * Validate that an object has required methods * * @param obj - Object to validate * @param requiredMethods - Array of required method names * @param objectName - Name of the object for error messages */ export function validateRequiredMethods( obj: unknown, requiredMethods: string[], objectName: string ): void { if (!obj || typeof obj !== 'object') { throw new Error(`${objectName} must be an object`) } for (const methodName of requiredMethods) { if (typeof (obj as Record)[methodName] !== 'function') { throw new Error(`${objectName} must have a ${methodName} method`) } } } /** * Validate account index */ export function validateAccountIndex(accountIndex: number): void { try { accountIndexSchema.parse(accountIndex) } catch (error) { const message = getZodErrorMessage(error) throw new Error(`Invalid accountIndex: ${message}`) } } /** * Validate network name */ export function validateNetworkName(network: string): void { try { networkNameSchema.parse(network) } catch (error) { const message = getZodErrorMessage(error) // Provide a fallback message if Zod error doesn't have a clear message if (message === 'Validation failed' || !message) { throw new Error('network must be a non-empty string containing only alphanumeric characters, hyphens, and underscores') } throw new Error(`Invalid network name: ${message}`) } } /** * Validate a module name */ export function validateModuleName(moduleName: string): void { if (typeof moduleName !== 'string' || moduleName.trim().length === 0) { throw new Error('moduleName must be a non-empty string') } } export function validateAssetId(assetId: string): void { try { assetIdSchema.parse(assetId) } catch (error) { const message = getZodErrorMessage(error) throw new Error(`Invalid assetId: ${message}`) } } /** * Validate balance string */ export function validateBalance(balance: string): void { try { balanceStringSchema.parse(balance) } catch (error) { const message = getZodErrorMessage(error) throw new Error(`Invalid balance: ${message}`) } } /** * Validate wallet parameters (network, accountIndex, optional tokenAddress) * Convenience function to validate common wallet operation parameters * * @param network - Network name * @param accountIndex - Account index * @param assetId - Asset ID (optional) */ export function validateWalletParams( network: string, accountIndex: number, assetId?: string ): void { validateNetworkName(network) validateAccountIndex(accountIndex) if (assetId) { validateAssetId(assetId) } }