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
JavaScript
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