import { z } from 'zod';
import { DataiClient } from '../utils/api-client.js';
import { WalletAddressSchema } from '../utils/validation.js';
import { DataAIError, ValidationError, APIError, NetworkError, TimeoutError } from '../utils/error-handler.js';
/**
* Tool 4: Get User DeFi Positions by Protocol
*
* COMPREHENSIVE DESCRIPTION:
* This tool fetches DeFi positions for a specific wallet address filtered by a specific protocol.
* It provides detailed analysis of a user's exposure to a particular DeFi protocol across all chains
* where that protocol operates. Perfect for protocol-specific risk assessment and yield optimization.
*
* USE CASES:
* - Protocol-specific exposure analysis
* - Risk assessment for single protocol
* - Yield optimization within protocol
* - Protocol migration planning
* - Governance token analysis
* - Protocol performance tracking
*
* SUPPORTED PROTOCOLS:
* - uniswap (Uniswap V2/V3/V4 across chains)
* - aave (Aave V2/V3 lending/borrowing)
* - compound (Compound lending/borrowing)
* - makerdao (MakerDAO CDP, DSR)
* - curve (Curve pools and gauges)
* - balancer (Balancer pools and farming)
* - sushiswap (SushiSwap pools and farming)
* - quickswap (QuickSwap on Polygon)
* - traderjoe (TraderJoe on Avalanche)
* - pancakeswap (PancakeSwap on BSC)
* - 1inch (1inch liquidity pools)
* - superfluid (Superfluid streaming)
* - sablier (Sablier vesting)
* - element (Element fixed yield)
* - hop (Hop Protocol bridge liquidity)
* - shibaswap (ShibaSwap pools)
*
* RESPONSE FORMAT:
* Returns an array of positions for the specified protocol across all chains:
* [
* {
* "chain": "eth",
* "protocol": "uniswap-v3",
* "type": "liquidity",
* "pool": "USDC/ETH",
* "value_usd": 5420.50,
* "apy": 12.5,
* "tokens": [...],
* "rewards": [...]
* }
* ]
*
* PERFORMANCE:
* - Typical response time: 1-4 seconds (protocol-specific)
* - Faster than cross-protocol queries
* - Optimized for single protocol analysis
*/
export function createGetUserDeFiPositionsByProtocolTool(client: DataiClient) {
return {
name: "get_defi_by_protocol",
description: "Get DeFi positions for a wallet filtered by a specific protocol across all chains. Provides detailed analysis of exposure to a particular DeFi protocol for risk assessment and yield optimization.",
parameters: z.object({
wallet: WalletAddressSchema.describe("Ethereum wallet address (42-character hex string starting with 0x) to get DeFi positions for"),
protocol: z.string().min(1).describe("DeFi protocol to filter by. Examples: 'uniswap', 'aave', 'compound', 'makerdao', 'curve', 'balancer', 'sushiswap', 'quickswap', 'traderjoe', 'pancakeswap'")
}),
annotations: {
readOnlyHint: true,
destructiveHint: false,
openWorldHint: true,
idempotentHint: true,
streamingHint: false,
title: "DeFi Positions by Protocol",
category: "Protocol Analysis",
tags: ["defi", "protocol-specific", "exposure", "risk-assessment", "yield"]
},
execute: async (args: { wallet: string; protocol: string }, context: { log: any }) => {
const { log } = context;
const startTime = Date.now();
const executionId = crypto.randomUUID().slice(0, 8);
log.info("Starting get_defi_by_protocol", {
executionId,
wallet: args.wallet,
protocol: args.protocol
});
try {
// Validate parameters
const validatedArgs = {
wallet: WalletAddressSchema.parse(args.wallet),
protocol: z.string().min(1).parse(args.protocol)
};
// Make API request
const response = await client.getUserDeFiPositionsByProtocol(validatedArgs.wallet, validatedArgs.protocol);
const duration = Date.now() - startTime;
const positionCount = Array.isArray(response.data) ? response.data.length : 0;
log.info("DeFi positions by protocol fetched successfully", {
executionId,
wallet: validatedArgs.wallet,
protocol: validatedArgs.protocol,
positionCount,
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 DeFi positions by protocol", {
executionId,
wallet: args.wallet,
protocol: args.protocol,
duration: `${duration}ms`,
error: error.message,
errorType: error.constructor.name,
errorCode: error.code || 'UNKNOWN',
success: false
});
// Transform error for user-friendly response
if (error instanceof ValidationError) {
if (error.message.includes('wallet')) {
throw new DataAIError(
`Invalid wallet address: ${args.wallet}. Please provide a valid Ethereum address (42 characters, starting with 0x).`,
'VALIDATION_ERROR'
);
} else if (error.message.includes('protocol')) {
throw new DataAIError(
`Invalid protocol: ${args.protocol}. Please provide a valid protocol name (e.g., 'uniswap', 'aave', 'compound').`,
'VALIDATION_ERROR'
);
} else {
throw new DataAIError(`Invalid parameters: ${error.message}`, 'VALIDATION_ERROR');
}
} else if (error instanceof TimeoutError) {
throw new DataAIError(
`Request timed out after ${duration}ms for protocol ${args.protocol}. Please try again in a few moments.`,
'TIMEOUT_ERROR'
);
} else if (error instanceof NetworkError) {
throw new DataAIError(
`Network error occurred while fetching ${args.protocol} positions. Please check your connection and try again.`,
'NETWORK_ERROR'
);
} else if (error instanceof APIError) {
if (error.message.includes('404')) {
throw new DataAIError(
`No ${args.protocol} positions found for wallet ${args.wallet}. The wallet may not have any positions in this protocol.`,
'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 {
throw new DataAIError(`API error: ${error.message}`, 'API_ERROR');
}
} else {
throw new DataAIError(
`Failed to fetch ${args.protocol} positions: ${error.message}. Please try again or contact support if the issue persists.`,
'UNKNOWN_ERROR'
);
}
}
}
};
}