Skip to main content
Glama
clumsynonono

Aave Liquidation MCP Server

by clumsynonono
test-all-tools.js9.55 kB
/** * Comprehensive test suite for all MCP tools * Tests each tool with real data to verify functionality */ import { AaveClient } from './src/aave-client.js'; // Using public RPC endpoint const PUBLIC_RPC = 'https://eth.llamarpc.com'; // Known addresses for testing (these are public Aave protocol contracts and known users) const TEST_ADDRESSES = { // Aave Pool itself (will have no position, good for testing edge cases) POOL: '0x87870Bca3F3fD6335C3F4ce8392D69350B4fA4E2', // Random addresses (may or may not have positions) SAMPLE1: '0x742d35Cc6634C0532925a3b844Bc454e4438f44e', SAMPLE2: '0xBA12222222228d8Ba445958a75a0704d566BF2C8', // Balancer Vault (often has positions) }; const KNOWN_ASSETS = { WETH: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', USDC: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', DAI: '0x6B175474E89094C44Da98b954EedeAC495271d0F', }; let passedTests = 0; let failedTests = 0; let totalTests = 0; function testHeader(name) { console.log('\n' + '='.repeat(70)); console.log(`🧪 TEST: ${name}`); console.log('='.repeat(70)); } function testPass(message) { console.log(`✅ PASS: ${message}`); passedTests++; totalTests++; } function testFail(message, error) { console.log(`❌ FAIL: ${message}`); if (error) { console.log(` Error: ${error.message || error}`); } failedTests++; totalTests++; } async function runTests() { console.log('🚀 Starting Comprehensive MCP Tool Tests\n'); console.log('📡 Using public RPC endpoint (may be slower)'); console.log('⏱️ This will take 30-60 seconds...\n'); const client = new AaveClient(PUBLIC_RPC); // Test 1: Get Protocol Status testHeader('get_protocol_status'); try { const blockNumber = await client.getBlockNumber(); if (blockNumber > 0) { testPass(`Retrieved current block number: ${blockNumber.toLocaleString()}`); } else { testFail('Block number should be > 0'); } } catch (error) { testFail('Failed to get block number', error); } // Test 2: Get Aave Reserves testHeader('get_aave_reserves'); try { const reserves = await client.getAllReserves(); if (reserves.length > 0) { testPass(`Retrieved ${reserves.length} reserves`); const wethReserve = reserves.find(r => r.tokenAddress.toLowerCase() === KNOWN_ASSETS.WETH.toLowerCase()); if (wethReserve) { testPass(`Found WETH reserve: ${wethReserve.symbol}`); } else { testFail('WETH reserve not found'); } if (reserves.every(r => r.symbol && r.tokenAddress)) { testPass('All reserves have symbol and address'); } else { testFail('Some reserves missing data'); } } else { testFail('No reserves retrieved'); } } catch (error) { testFail('Failed to get reserves', error); } // Test 3: Get Asset Price testHeader('get_asset_price'); try { const wethPrice = await client.getAssetPrice(KNOWN_ASSETS.WETH); const priceNum = parseFloat(wethPrice); if (priceNum > 0) { testPass(`WETH price: $${parseFloat(wethPrice).toFixed(2)}`); } else { testFail('WETH price should be > 0'); } // Test USDC price (should be close to $1) const usdcPrice = await client.getAssetPrice(KNOWN_ASSETS.USDC); const usdcPriceNum = parseFloat(usdcPrice); if (usdcPriceNum > 0.95 && usdcPriceNum < 1.05) { testPass(`USDC price is reasonable: $${usdcPriceNum.toFixed(4)}`); } else { testFail(`USDC price seems off: $${usdcPriceNum.toFixed(4)}`); } } catch (error) { testFail('Failed to get asset prices', error); } // Test 4: Validate Address testHeader('validate_address'); try { const validAddr = client.isValidAddress(TEST_ADDRESSES.POOL); if (validAddr) { testPass('Valid address recognized as valid'); } else { testFail('Valid address not recognized'); } const invalidAddr1 = client.isValidAddress('0x123'); if (!invalidAddr1) { testPass('Invalid address (too short) rejected'); } else { testFail('Invalid address not rejected'); } const invalidAddr2 = client.isValidAddress('not an address'); if (!invalidAddr2) { testPass('Invalid address (not hex) rejected'); } else { testFail('Invalid address not rejected'); } } catch (error) { testFail('Address validation failed', error); } // Test 5: Get User Account Data testHeader('get_user_health'); try { // Test with pool address (should have no position) const poolData = await client.getUserAccountData(TEST_ADDRESSES.POOL); if (poolData.address) { testPass('User account data retrieved'); } else { testFail('No address in user data'); } if (poolData.healthFactorFormatted) { testPass(`Health factor formatted: ${poolData.healthFactorFormatted}`); } else { testFail('Health factor not formatted'); } // The pool itself shouldn't have a borrowing position if (poolData.totalDebtBase === 0n) { testPass('Pool address has no debt (as expected)'); } } catch (error) { testFail('Failed to get user account data', error); } // Test 6: Get User Positions testHeader('get_user_positions'); try { const positions = await client.getUserReserves(TEST_ADDRESSES.SAMPLE1); testPass(`Retrieved positions (${positions.collateral.length} collateral, ${positions.debt.length} debt)`); if (positions.collateral.every(p => p.symbol && p.asset)) { testPass('All collateral positions have required fields'); } else { testFail('Some collateral positions missing data'); } } catch (error) { testFail('Failed to get user positions', error); } // Test 7: Analyze Liquidation testHeader('analyze_liquidation'); try { const analysis = await client.analyzeLiquidationOpportunity(TEST_ADDRESSES.SAMPLE2); if (analysis === null) { testPass('Healthy position correctly returns null'); } else { testPass(`Found position at risk: Health Factor ${analysis.healthFactor}`); if (analysis.riskLevel && ['HIGH', 'MEDIUM', 'LOW'].includes(analysis.riskLevel)) { testPass(`Risk level properly classified: ${analysis.riskLevel}`); } else { testFail('Invalid risk level'); } } } catch (error) { testFail('Failed to analyze liquidation', error); } // Test 8: Batch Check Addresses testHeader('batch_check_addresses'); try { const addresses = [ TEST_ADDRESSES.POOL, TEST_ADDRESSES.SAMPLE1, TEST_ADDRESSES.SAMPLE2, ]; const results = await client.batchAnalyzeLiquidation(addresses); if (results.length === addresses.length) { testPass(`Batch check completed for all ${addresses.length} addresses`); } else { testFail(`Expected ${addresses.length} results, got ${results.length}`); } if (results.every(r => r.address)) { testPass('All batch results have addresses'); } else { testFail('Some batch results missing addresses'); } } catch (error) { testFail('Failed batch check', error); } // Test 9: Error Handling - Invalid Address testHeader('Error Handling'); try { await client.getUserAccountData('0x123invalid'); testFail('Should have thrown error for invalid address'); } catch (error) { testPass('Correctly throws error for invalid address'); } // Test 10: Error Handling - Invalid Asset try { await client.getAssetPrice('0x0000000000000000000000000000000000000000'); // May or may not error depending on Aave's handling testPass('Handled zero address query (no crash)'); } catch (error) { testPass('Correctly handles invalid asset address'); } // Final Report console.log('\n\n' + '='.repeat(70)); console.log('📊 TEST SUMMARY'); console.log('='.repeat(70)); console.log(`✅ Passed: ${passedTests}/${totalTests}`); console.log(`❌ Failed: ${failedTests}/${totalTests}`); console.log(`📈 Success Rate: ${((passedTests / totalTests) * 100).toFixed(1)}%`); console.log('='.repeat(70)); if (failedTests === 0) { console.log('\n🎉 ALL TESTS PASSED! MCP is ready to use!\n'); } else { console.log('\n⚠️ Some tests failed. Review the errors above.\n'); } // Performance note console.log('📝 Note: Test performance depends on RPC provider.'); console.log(' For production use, get a dedicated RPC key from Alchemy or Infura.\n'); } // Run tests runTests().catch(error => { console.error('❌ Fatal error during testing:', error); process.exit(1); }); //# sourceMappingURL=test-all-tools.js.map

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/clumsynonono/aave-liquidation-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server