import { z } from 'zod';
import { SUPPORTED_CHAINS, COMMON_PROTOCOLS } from '../config/constants.js';
// Base validation patterns
const ETHEREUM_ADDRESS_REGEX = /^0x[a-fA-F0-9]{40}$/;
const SOLANA_ADDRESS_REGEX = /^[1-9A-HJ-NP-Za-km-z]{32,44}$/; // Base58 format, 32-44 characters
// Individual wallet type schemas
export const EthereumWalletSchema = z
.string()
.regex(ETHEREUM_ADDRESS_REGEX, 'Invalid Ethereum wallet address format')
.describe('Ethereum wallet address (42 characters starting with 0x)');
export const SolanaWalletSchema = z
.string()
.regex(SOLANA_ADDRESS_REGEX, 'Invalid Solana wallet address format')
.describe('Solana wallet address (Base58 format, 32-44 characters)');
// Unified wallet schema supporting both Ethereum and Solana
export const WalletAddressSchema = z
.union([EthereumWalletSchema, SolanaWalletSchema])
.describe('Wallet address (Ethereum or Solana format)');
// Wallet type detection helpers
export function detectWalletType(address: string): 'ethereum' | 'solana' | 'unknown' {
if (ETHEREUM_ADDRESS_REGEX.test(address)) {
return 'ethereum';
}
if (SOLANA_ADDRESS_REGEX.test(address)) {
return 'solana';
}
return 'unknown';
}
export function isEthereumWallet(address: string): boolean {
return ETHEREUM_ADDRESS_REGEX.test(address);
}
export function isSolanaWallet(address: string): boolean {
return SOLANA_ADDRESS_REGEX.test(address);
}
export const ChainSchema = z
.enum(SUPPORTED_CHAINS)
.describe('Supported blockchain network identifier');
export const ChainsArraySchema = z
.array(ChainSchema)
.min(1, 'At least one chain must be specified')
.max(7, 'Maximum 7 chains can be specified')
.describe('Array of supported blockchain network identifiers');
export const ProtocolSchema = z
.string()
.min(2, 'Protocol name must be at least 2 characters')
.max(50, 'Protocol name must be less than 50 characters')
.toLowerCase()
.describe('DeFi protocol identifier');
// Tool-specific parameter schemas
// Tool 1: Get All User DeFi Positions
export const GetAllUserDeFiPositionsSchema = z.object({
wallet: WalletAddressSchema
});
// Tool 2: Get User DeFi Positions by Chain
export const GetUserDeFiPositionsByChainSchema = z.object({
wallet: WalletAddressSchema,
chain: ChainSchema
});
// Tool 3: Get User DeFi Positions by Multiple Chains
export const GetUserDeFiPositionsByMultipleChainsSchema = z.object({
wallet: WalletAddressSchema,
chains: z.union([
ChainsArraySchema,
z.string().transform((str) => str.split(',').map(s => s.trim()) as typeof SUPPORTED_CHAINS[number][])
])
});
// Tool 4: Get User DeFi Positions by Protocol
export const GetUserDeFiPositionsByProtocolSchema = z.object({
wallet: WalletAddressSchema,
protocol: ProtocolSchema
});
// Tool 5: Get User DeFi Protocol Balances by Chain
export const GetUserDeFiProtocolBalancesByChainSchema = z.object({
wallet: WalletAddressSchema,
chain: ChainSchema
});
// Tool 6: Get User Overall Balance All Chains
export const GetUserOverallBalanceAllChainsSchema = z.object({
wallet: WalletAddressSchema
});
// Tool 7: Get User Overall Balance by Chain
export const GetUserOverallBalanceByChainSchema = z.object({
wallet: WalletAddressSchema,
chain: ChainSchema
});
// Tool 8: Get Wallet Balances by Chain
export const GetWalletBalancesByChainSchema = z.object({
wallet: WalletAddressSchema,
chain: ChainSchema
});
// Type inference from schemas
export type WalletAddress = z.infer<typeof WalletAddressSchema>;
export type Chain = z.infer<typeof ChainSchema>;
export type ChainsArray = z.infer<typeof ChainsArraySchema>;
export type Protocol = z.infer<typeof ProtocolSchema>;
export type GetAllUserDeFiPositionsParams = z.infer<typeof GetAllUserDeFiPositionsSchema>;
export type GetUserDeFiPositionsByChainParams = z.infer<typeof GetUserDeFiPositionsByChainSchema>;
export type GetUserDeFiPositionsByMultipleChainsParams = z.infer<typeof GetUserDeFiPositionsByMultipleChainsSchema>;
export type GetUserDeFiPositionsByProtocolParams = z.infer<typeof GetUserDeFiPositionsByProtocolSchema>;
export type GetUserDeFiProtocolBalancesByChainParams = z.infer<typeof GetUserDeFiProtocolBalancesByChainSchema>;
export type GetUserOverallBalanceAllChainsParams = z.infer<typeof GetUserOverallBalanceAllChainsSchema>;
export type GetUserOverallBalanceByChainParams = z.infer<typeof GetUserOverallBalanceByChainSchema>;
export type GetWalletBalancesByChainParams = z.infer<typeof GetWalletBalancesByChainSchema>;
// Validation helper functions
export function validateWalletAddress(address: string): {
isValid: boolean;
error?: string;
walletType?: 'ethereum' | 'solana'
} {
try {
WalletAddressSchema.parse(address);
const walletType = detectWalletType(address);
return {
isValid: true,
walletType: walletType === 'unknown' ? undefined : walletType
};
} catch (error) {
if (error instanceof z.ZodError) {
const detectedType = detectWalletType(address);
let errorMessage = error.errors[0]?.message || 'Invalid wallet address';
if (detectedType === 'unknown') {
errorMessage = 'Wallet address must be either Ethereum (0x...) or Solana (Base58) format';
}
return {
isValid: false,
error: errorMessage
};
}
return { isValid: false, error: 'Validation error' };
}
}
export function validateChain(chain: string): { isValid: boolean; error?: string } {
try {
ChainSchema.parse(chain);
return { isValid: true };
} catch (error) {
if (error instanceof z.ZodError) {
return {
isValid: false,
error: error.errors[0]?.message || 'Invalid chain identifier'
};
}
return { isValid: false, error: 'Validation error' };
}
}
export function validateProtocol(protocol: string): { isValid: boolean; error?: string } {
try {
ProtocolSchema.parse(protocol);
return { isValid: true };
} catch (error) {
if (error instanceof z.ZodError) {
return {
isValid: false,
error: error.errors[0]?.message || 'Invalid protocol identifier'
};
}
return { isValid: false, error: 'Validation error' };
}
}
// Generic validation function for tool parameters
export function validateToolParams<T>(
schema: z.ZodSchema<T>,
params: unknown
): { success: true; data: T } | { success: false; error: string } {
try {
const data = schema.parse(params);
return { success: true, data };
} catch (error) {
if (error instanceof z.ZodError) {
const errorMessage = error.errors
.map(err => `${err.path.join('.')}: ${err.message}`)
.join(', ');
return { success: false, error: errorMessage };
}
return { success: false, error: 'Validation failed' };
}
}