UNPKG

devion-mcp-server

Version:

MCP server for Devion blockchain infrastructure - AI-native blockchain data access for developers and AI agents

221 lines 9.26 kB
import { DevionSDK } from 'devion-sdk'; import { z } from 'zod'; import { SUPPORTED_NETWORKS } from '../types.js'; export const callContractFunction = { name: 'call_contract_function', description: 'Execute a read-only smart contract function call. Perfect for checking balances, getting contract state, or calling view functions.', inputSchema: { type: 'object', properties: { contract_address: { type: 'string', description: 'Smart contract address (0x...)', pattern: '^0x[a-fA-F0-9]{40}$' }, function_data: { type: 'string', description: 'Encoded function call data (0x...). Use tools like web3.js or ethers.js to encode function calls.', pattern: '^0x[a-fA-F0-9]*$' }, network: { type: 'string', enum: SUPPORTED_NETWORKS, description: 'Blockchain network to query (defaults to ethereum-mainnet)' }, from_address: { type: 'string', description: 'Optional sender address for the call (0x...)', pattern: '^0x[a-fA-F0-9]{40}$' }, block_number: { type: 'string', description: 'Block number to query at (hex format or "latest")', default: 'latest' } }, required: ['contract_address', 'function_data'] }, async handler(args) { const schema = z.object({ contract_address: z.string().regex(/^0x[a-fA-F0-9]{40}$/, 'Invalid contract address'), function_data: z.string().regex(/^0x[a-fA-F0-9]*$/, 'Invalid function call data'), network: z.enum(SUPPORTED_NETWORKS).default('ethereum-mainnet'), from_address: z.string().regex(/^0x[a-fA-F0-9]{40}$/, 'Invalid from address').optional(), block_number: z.string().default('latest') }); const { contract_address, function_data, network, from_address, block_number } = schema.parse(args); const sdk = new DevionSDK({ apiKey: process.env.DEVION_API_KEY, baseURL: 'https://api.devion.dev', network: network }); try { const callObject = { to: contract_address, data: function_data }; if (from_address) { callObject.from = from_address; } const result = await sdk.rpc('eth_call', [callObject, block_number]); const currentBlock = await sdk.getBlockNumber(); return { success: true, data: { contract_address, function_data, result, result_decoded: result === '0x' ? null : result, network, block_number: block_number === 'latest' ? (typeof currentBlock === 'string' ? parseInt(currentBlock, 16) : currentBlock) : (typeof block_number === 'string' ? parseInt(block_number, 16) : parseInt(block_number)), call_details: { from: from_address || '0x0000000000000000000000000000000000000000', to: contract_address, data: function_data }, timestamp: new Date().toISOString(), notes: [ 'Result is in hex format - decode using appropriate ABI', 'Use web3.js, ethers.js, or similar libraries to decode the result', 'Empty result (0x) may indicate function reverted or doesn\'t exist' ] } }; } catch (error) { return { success: false, error: error instanceof Error ? error.message : 'Contract call failed', details: { contract_address, function_data, network, suggestions: [ 'Verify the contract address is correct', 'Check that the function signature is properly encoded', 'Ensure the contract exists on the specified network', 'Try using a different block number if calling historical state' ], timestamp: new Date().toISOString() } }; } } }; export const getContractInfo = { name: 'get_contract_info', description: 'Get basic information about a smart contract including bytecode size and creation details', inputSchema: { type: 'object', properties: { contract_address: { type: 'string', description: 'Smart contract address (0x...)', pattern: '^0x[a-fA-F0-9]{40}$' }, network: { type: 'string', enum: SUPPORTED_NETWORKS, description: 'Blockchain network to query (defaults to ethereum-mainnet)' }, include_bytecode: { type: 'boolean', description: 'Include the full contract bytecode (can be large)', default: false } }, required: ['contract_address'] }, async handler(args) { const schema = z.object({ contract_address: z.string().regex(/^0x[a-fA-F0-9]{40}$/, 'Invalid contract address'), network: z.enum(SUPPORTED_NETWORKS).default('ethereum-mainnet'), include_bytecode: z.boolean().default(false) }); const { contract_address, network, include_bytecode } = schema.parse(args); const sdk = new DevionSDK({ apiKey: process.env.DEVION_API_KEY, baseURL: 'https://api.devion.dev', network: network }); try { const [code, balance, blockNumber] = await Promise.all([ sdk.rpc('eth_getCode', [contract_address, 'latest']), sdk.getBalance(contract_address, { formatted: true }), sdk.getBlockNumber() ]); const isContract = code && code !== '0x'; const bytecodeSize = code ? (code.length - 2) / 2 : 0; const result = { success: true, data: { address: contract_address, network, is_contract: isContract, balance: parseFloat(balance), balance_formatted: `${balance} ${getNetworkCurrency(network)}`, bytecode_size_bytes: bytecodeSize, bytecode_size_kb: Math.round(bytecodeSize / 1024 * 100) / 100, block_number: typeof blockNumber === 'string' ? parseInt(blockNumber, 16) : blockNumber, timestamp: new Date().toISOString() } }; if (include_bytecode && isContract) { result.data.bytecode = code; result.data.bytecode_hash = `0x${Buffer.from(code.slice(2), 'hex').toString('hex').slice(0, 64)}...`; } if (isContract) { result.data.analysis = { type: 'Smart Contract', size_category: bytecodeSize < 1000 ? 'small' : bytecodeSize < 10000 ? 'medium' : 'large', has_balance: parseFloat(balance) > 0, notes: [ 'This is a deployed smart contract', bytecodeSize > 24576 ? 'Large contract - may have complex functionality' : 'Standard sized contract', parseFloat(balance) > 0 ? 'Contract holds funds' : 'Contract has no balance' ] }; } else { result.data.analysis = { type: 'Externally Owned Account (EOA)', notes: [ 'This is a regular wallet address, not a smart contract', 'No contract code deployed at this address' ] }; } return result; } catch (error) { return { success: false, error: error instanceof Error ? error.message : 'Failed to get contract info', details: { contract_address, network, timestamp: new Date().toISOString() } }; } } }; function getNetworkCurrency(network) { switch (network) { case 'ethereum-mainnet': return 'ETH'; case 'polygon-mainnet': return 'MATIC'; case 'base-mainnet': case 'base-sepolia': return 'ETH'; case 'arbitrum-mainnet': case 'arbitrum-sepolia': return 'ETH'; default: return 'ETH'; } } //# sourceMappingURL=contracts.js.map