import { z } from 'zod';
import { DataiClient } from '../utils/api-client.js';
import { WalletAddressSchema, ChainSchema } from '../utils/validation.js';
import { DataAIError, ValidationError, APIError, NetworkError, TimeoutError } from '../utils/error-handler.js';
/**
* Tool 7: Get User Overall Balance by Chain
*
* COMPREHENSIVE DESCRIPTION:
* This tool fetches the overall balance for a specific wallet address on a single blockchain network.
* It provides aggregated balance information across multiple currencies (USD, EUR, AUD, etc.) for
* all assets held by the wallet on the specified chain.
*
* USE CASES:
* - Portfolio valuation in multiple currencies
* - Cross-chain balance comparison
* - Tax reporting and accounting
* - Wealth management and tracking
* - Multi-currency financial analysis
*
* SUPPORTED CHAINS:
* - eth (Ethereum Mainnet)
* - arb (Arbitrum)
* - matic (Polygon)
* - avax (Avalanche)
* - bsc (Binance Smart Chain)
* - base (Base)
* - op (Optimism)
*
* RESPONSE FORMAT:
* Returns an object with currency codes as keys and balance values as numbers:
* {
* "USD": 15420.50,
* "EUR": 14230.25,
* "AUD": 23150.75,
* "GBP": 12180.30,
* "JPY": 2250000.00,
* "CAD": 20850.40,
* "CHF": 14050.20
* }
*
* SECURITY CONSIDERATIONS:
* - Wallet addresses are validated using Ethereum address format
* - Chain parameters are validated against supported networks
* - Rate limiting applied to prevent API abuse
* - No sensitive data is logged or stored
* - All requests are authenticated via API key
*
* PERFORMANCE:
* - Typical response time: 1-3 seconds
* - Cached data may be up to 5 minutes old
* - Timeout configured for 180 seconds
* - Automatic retry on transient failures
*
* ERROR HANDLING:
* - Invalid wallet address: Returns validation error
* - Unsupported chain: Returns validation error
* - Network timeout: Returns timeout error with retry suggestion
* - API rate limit: Returns rate limit error with backoff time
* - Invalid API key: Returns authentication error
* - Wallet not found: Returns empty balance object
*
* EXAMPLES:
*
* Example 1 - Ethereum mainnet balance:
* Input: { wallet: "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045", chain: "eth" }
* Output: { "USD": 15420.50, "EUR": 14230.25, "AUD": 23150.75, ... }
*
* Example 2 - Arbitrum balance:
* Input: { wallet: "0x09CF915e195aF33FA7B932C253352Ae9FBdB0106", chain: "arb" }
* Output: { "USD": 8750.25, "EUR": 8100.15, "AUD": 13200.40, ... }
*
* Example 3 - Empty wallet:
* Input: { wallet: "0x0000000000000000000000000000000000000000", chain: "eth" }
* Output: { "USD": 0, "EUR": 0, "AUD": 0, ... }
*/
export function createGetUserOverallBalanceByChainTool(client: DataiClient) {
return {
name: "get_overall_balance_by_chain",
description: "Get comprehensive overall balance for a wallet on a specific blockchain network in multiple currencies (USD, EUR, AUD, GBP, JPY, CAD, CHF). Provides aggregated portfolio valuation across all assets held on the specified chain.",
// Enhanced parameter documentation
parameters: z.object({
wallet: WalletAddressSchema.describe("Ethereum wallet address (42-character hex string starting with 0x) to get overall balance for. Example: 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"),
chain: ChainSchema.describe("Blockchain network identifier to query. Supported values: 'eth' (Ethereum), 'arb' (Arbitrum), 'matic' (Polygon), 'avax' (Avalanche), 'bsc' (Binance Smart Chain), 'base' (Base), 'op' (Optimism)")
}),
// Tool annotations for better MCP client understanding
annotations: {
readOnlyHint: true, // This tool only reads data, doesn't modify anything
destructiveHint: false, // Safe operation, no destructive actions
openWorldHint: false, // Closed set of supported chains
idempotentHint: true, // Same inputs always produce same results (within cache window)
streamingHint: false, // Returns complete response, no streaming
title: "Overall Balance by Chain",
category: "Portfolio Analysis",
tags: ["balance", "portfolio", "multi-currency", "blockchain", "defi"]
},
execute: async (args: { wallet: string; chain: string }, context: { log: any }) => {
const { log } = context;
const startTime = Date.now();
const executionId = crypto.randomUUID().slice(0, 8);
log.info("Starting get_overall_balance_by_chain", {
executionId,
wallet: args.wallet,
chain: args.chain
});
try {
// Validate parameters with detailed error messages
const validatedArgs = {
wallet: WalletAddressSchema.parse(args.wallet),
chain: ChainSchema.parse(args.chain)
};
// Make API request with comprehensive logging
const response = await client.getUserOverallBalanceByChain(validatedArgs.wallet, validatedArgs.chain);
const duration = Date.now() - startTime;
const currencyCount = Object.keys(response.data || {}).length;
log.info("Overall balance by chain fetched successfully", {
executionId,
wallet: validatedArgs.wallet,
chain: validatedArgs.chain,
currencyCount,
duration: `${duration}ms`
});
return {
content: [{
type: "text" as const,
text: JSON.stringify(response.data, null, 2)
}]
};
} catch (error: any) {
const duration = Date.now() - startTime;
log.error("Failed to fetch overall balance by chain", {
executionId,
wallet: args.wallet,
chain: args.chain,
duration: `${duration}ms`,
error: error.message,
errorType: error.constructor.name,
errorCode: error.code || 'UNKNOWN',
success: false
});
// Transform error for user-friendly response with specific guidance
if (error instanceof ValidationError) {
const message = error.message.includes('wallet')
? `Invalid wallet address: ${args.wallet}. Please provide a valid Ethereum address (42 characters, starting with 0x).`
: `Invalid chain: ${args.chain}. Supported chains: eth, arb, matic, avax, bsc, base, op.`;
throw new DataAIError(message, 'VALIDATION_ERROR');
} else if (error instanceof TimeoutError) {
throw new DataAIError(
`Request timed out after ${duration}ms. The API may be experiencing high load. Please try again in a few moments.`,
'TIMEOUT_ERROR'
);
} else if (error instanceof NetworkError) {
throw new DataAIError(
'Network error occurred while fetching balance data. Please check your connection and try again.',
'NETWORK_ERROR'
);
} else if (error instanceof APIError) {
if (error.message.includes('404')) {
throw new DataAIError(
`No balance data found for wallet ${args.wallet} on ${args.chain} network. The wallet may be empty or the chain may not be supported.`,
'NOT_FOUND'
);
} else if (error.message.includes('429')) {
throw new DataAIError(
'Rate limit exceeded. Please wait a moment before making another request.',
'RATE_LIMIT_EXCEEDED'
);
} else if (error.message.includes('401') || error.message.includes('403')) {
throw new DataAIError(
'Authentication failed. Please check your API credentials.',
'AUTHENTICATION_ERROR'
);
} else {
throw new DataAIError(`API error: ${error.message}`, 'API_ERROR');
}
} else {
throw new DataAIError(
`Failed to fetch overall balance: ${error.message}. Please try again or contact support if the issue persists.`,
'UNKNOWN_ERROR'
);
}
}
}
};
}