contractTests.ts•6.57 kB
/**
* @file Contract Tests
* @version 1.0.0
* @status IN_DEVELOPMENT
* @lastModified 2024-07-05
*
* Tests for contract-related MCP tools
*
* IMPORTANT:
* - Tests contract interaction through MCP protocol
* - Validates contract responses
*
* Functionality:
* - Tests getContractCode
* - Tests contract call methods
*/
import { McpStandardClient } from '../../client/mcpStandardClient.js';
import { assert, assertDefined, assertToolSuccess } from '../../client/utils/assertions.js';
import { logger } from '../../../utils/logger.js';
// Well-known contract addresses for testing read-only calls
const TEST_CONTRACTS = {
// USDC contract on Ethereum mainnet
usdc: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',
// WETH contract on Ethereum mainnet
weth: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2',
// OpenSea Seaport contract
seaport: '0x00000000006c3852cbEf3e08E8dF289169EdE581'
};
// Simple ABI snippets for testing
const USDC_ABI = '[{"constant":true,"inputs":[],"name":"name","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"symbol","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"decimals","outputs":[{"name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"}]';
/**
* Get the list of contract-related tests
*
* @param client The MCP client to use for testing
* @returns Array of test cases
*/
export function getContractTests(client: McpStandardClient): Array<{ name: string; test: () => Promise<void> }> {
return [
{
name: 'Get contract code',
test: async () => {
const result = await client.callTool('getContractCode', {
address: TEST_CONTRACTS.usdc
});
assertToolSuccess(result, 'Failed to get contract code');
// Find the text content in the response
const codeText = result.content.find((item: any) => item.type === 'text')?.text;
assertDefined(codeText, 'Contract code response has no text content');
// Verify the response contains contract bytecode (starts with 0x)
assert(
codeText.includes('0x'),
`Contract code response does not contain bytecode`
);
// Contract bytecode should be fairly long
assert(
codeText.length > 100,
`Contract code response is too short to be valid bytecode`
);
logger.debug('Contract code response length', { length: codeText.length });
}
},
{
name: 'Read USDC contract name',
test: async () => {
const result = await client.callTool('contractCall', {
contractAddress: TEST_CONTRACTS.usdc,
abi: USDC_ABI,
method: 'name',
args: []
});
assertToolSuccess(result, 'Failed to call contract name method');
// Find the text content in the response
const nameText = result.content.find((item: any) => item.type === 'text')?.text;
assertDefined(nameText, 'Contract name response has no text content');
// USDC's name should be "USD Coin" or similar
assert(
nameText.toLowerCase().includes('usd') && nameText.toLowerCase().includes('coin'),
`Contract name response does not contain expected USDC name: ${nameText}`
);
logger.debug('USDC contract name', { nameText });
}
},
{
name: 'Read USDC contract symbol',
test: async () => {
const result = await client.callTool('contractCall', {
contractAddress: TEST_CONTRACTS.usdc,
abi: USDC_ABI,
method: 'symbol',
args: []
});
assertToolSuccess(result, 'Failed to call contract symbol method');
// Find the text content in the response
const symbolText = result.content.find((item: any) => item.type === 'text')?.text;
assertDefined(symbolText, 'Contract symbol response has no text content');
// USDC's symbol should be "USDC"
assert(
symbolText.includes('USDC'),
`Contract symbol response does not contain expected USDC symbol: ${symbolText}`
);
logger.debug('USDC contract symbol', { symbolText });
}
},
{
name: 'Read USDC contract decimals',
test: async () => {
const result = await client.callTool('contractCall', {
contractAddress: TEST_CONTRACTS.usdc,
abi: USDC_ABI,
method: 'decimals',
args: []
});
assertToolSuccess(result, 'Failed to call contract decimals method');
// Find the text content in the response
const decimalsText = result.content.find((item: any) => item.type === 'text')?.text;
assertDefined(decimalsText, 'Contract decimals response has no text content');
// USDC's decimals should be 6
assert(
decimalsText.includes('6'),
`Contract decimals response does not contain expected USDC decimals (6): ${decimalsText}`
);
logger.debug('USDC contract decimals', { decimalsText });
}
},
{
name: 'Read balance of address on USDC contract',
test: async () => {
// Check Vitalik's USDC balance
const vitalikAddress = '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045';
const result = await client.callTool('contractCall', {
contractAddress: TEST_CONTRACTS.usdc,
abi: USDC_ABI,
method: 'balanceOf',
args: [vitalikAddress]
});
assertToolSuccess(result, 'Failed to call contract balanceOf method');
// Find the text content in the response
const balanceText = result.content.find((item: any) => item.type === 'text')?.text;
assertDefined(balanceText, 'Contract balanceOf response has no text content');
// The balance should be a number
assert(
/\d/.test(balanceText),
`Contract balanceOf response does not contain a numeric value: ${balanceText}`
);
logger.debug(`USDC balance for ${vitalikAddress}`, { balanceText });
}
}
];
}