Skip to main content
Glama

MCP Ethers Wallet

erc721.ts10.4 kB
/** * @file ERC721 Tool Handlers * @version 1.0.0 * * Tool handlers for ERC721 NFT standard operations */ import { z } from 'zod'; import { TokenOperationOptions } from '../../services/erc/types.js'; import { validateWithFriendlyErrors, createErrorResponse, CommonSchemas } from '../../utils/validation.js'; // This will be injected during initialization let ethersService: any; export function initializeErc721Handlers(service: any) { ethersService = service; } // Common schemas with user-friendly messages const contractAddressSchema = CommonSchemas.ethereumAddress.describe('ERC721 NFT contract address'); const providerSchema = CommonSchemas.provider; const chainIdSchema = CommonSchemas.chainId; const tokenIdSchema = z.union([z.string(), z.number()]).describe('NFT token ID (numeric identifier)'); // Options schema for transaction operations const optionsSchema = z.object({ gasLimit: z.union([z.string(), z.number()]).optional(), gasPrice: z.union([z.string(), z.number()]).optional(), maxFeePerGas: z.union([z.string(), z.number()]).optional(), maxPriorityFeePerGas: z.union([z.string(), z.number()]).optional(), nonce: z.number().optional(), value: z.string().optional(), }).optional(); export const erc721Handlers = { getERC721CollectionInfo: async (args: unknown) => { const schema = z.object({ contractAddress: contractAddressSchema, provider: providerSchema, chainId: chainIdSchema }); try { const { contractAddress, provider, chainId } = validateWithFriendlyErrors( schema, args, 'Get ERC721 Collection Information' ); const collectionInfo = await ethersService.getERC721CollectionInfo(contractAddress, provider, chainId); return { content: [{ type: "text", text: `NFT Collection Information: Name: ${collectionInfo.name} Symbol: ${collectionInfo.symbol}${collectionInfo.totalSupply ? ` Total Supply: ${collectionInfo.totalSupply}` : ''}` }] }; } catch (error) { return createErrorResponse(error, 'getting NFT collection information'); } }, getERC721Owner: async (args: unknown) => { const schema = z.object({ contractAddress: contractAddressSchema, tokenId: tokenIdSchema, provider: providerSchema, chainId: chainIdSchema }); try { const { contractAddress, tokenId, provider, chainId } = validateWithFriendlyErrors( schema, args, 'Get ERC721 Owner' ); const owner = await ethersService.getERC721Owner(contractAddress, tokenId, provider, chainId); // Get collection info to add context const collectionInfo = await ethersService.getERC721CollectionInfo(contractAddress, provider, chainId); return { content: [{ type: "text", text: `The owner of ${collectionInfo.name} #${tokenId} is ${owner}` }] }; } catch (error) { return createErrorResponse(error, 'getting NFT owner'); } }, getERC721Metadata: async (args: unknown) => { const schema = z.object({ contractAddress: contractAddressSchema, tokenId: tokenIdSchema, provider: providerSchema, chainId: chainIdSchema }); try { const { contractAddress, tokenId, provider, chainId } = validateWithFriendlyErrors( schema, args, 'Get ERC721 Metadata' ); const metadata = await ethersService.getERC721Metadata(contractAddress, tokenId, provider, chainId); // Get collection info to add context const collectionInfo = await ethersService.getERC721CollectionInfo(contractAddress, provider, chainId); // Build a formatted response with all metadata properties let metadataText = `NFT Metadata for ${collectionInfo.name} #${tokenId}:`; if (metadata.name) metadataText += `\nName: ${metadata.name}`; if (metadata.description) metadataText += `\nDescription: ${metadata.description}`; if (metadata.image) metadataText += `\nImage URL: ${metadata.image}`; if (metadata.external_url) metadataText += `\nExternal URL: ${metadata.external_url}`; // Add attributes if they exist if (metadata.attributes && metadata.attributes.length > 0) { metadataText += '\n\nAttributes:'; for (const attr of metadata.attributes) { if (attr.trait_type && attr.value !== undefined) { metadataText += `\n- ${attr.trait_type}: ${attr.value}`; } } } return { content: [{ type: "text", text: metadataText }] }; } catch (error) { return createErrorResponse(error, 'getting NFT metadata'); } }, getERC721TokensOfOwner: async (args: unknown) => { const schema = z.object({ contractAddress: contractAddressSchema, ownerAddress: z.string(), includeMetadata: z.boolean().optional().default(false), provider: providerSchema, chainId: chainIdSchema }); try { const { contractAddress, ownerAddress, includeMetadata, provider, chainId } = schema.parse(args); const tokens = await ethersService.getERC721TokensOfOwner( contractAddress, ownerAddress, includeMetadata, provider, chainId ); // Get collection info to add context const collectionInfo = await ethersService.getERC721CollectionInfo(contractAddress, provider, chainId); if (tokens.length === 0) { return { content: [{ type: "text", text: `${ownerAddress} doesn't own any tokens in the ${collectionInfo.name} collection.` }] }; } let responseText = `${ownerAddress} owns ${tokens.length} token(s) in the ${collectionInfo.name} collection:`; for (const token of tokens) { responseText += `\n\nToken ID: ${token.tokenId}`; if (token.metadata) { if (token.metadata.name) responseText += `\nName: ${token.metadata.name}`; if (token.metadata.image) responseText += `\nImage URL: ${token.metadata.image}`; // Add a few attributes if they exist (limited to avoid very long responses) if (token.metadata.attributes && token.metadata.attributes.length > 0) { responseText += '\nAttributes:'; const limit = Math.min(5, token.metadata.attributes.length); for (let i = 0; i < limit; i++) { const attr = token.metadata.attributes[i]; if (attr.trait_type && attr.value !== undefined) { responseText += `\n- ${attr.trait_type}: ${attr.value}`; } } if (token.metadata.attributes.length > limit) { responseText += `\n- ... ${token.metadata.attributes.length - limit} more attributes`; } } } } return { content: [{ type: "text", text: responseText }] }; } catch (error) { return { isError: true, content: [{ type: "text", text: `Error getting NFTs owned by address: ${error instanceof Error ? error.message : String(error)}` }] }; } }, transferERC721: async (args: unknown) => { const schema = z.object({ contractAddress: contractAddressSchema, toAddress: z.string(), tokenId: tokenIdSchema, provider: providerSchema, chainId: chainIdSchema, gasLimit: z.string().optional(), gasPrice: z.string().optional(), }); try { const { contractAddress, toAddress, tokenId, provider, chainId, gasLimit, gasPrice } = schema.parse(args); // Create options object for transaction parameters const options: TokenOperationOptions = {}; if (gasLimit) options.gasLimit = gasLimit; if (gasPrice) options.gasPrice = gasPrice; const tx = await ethersService.transferERC721( contractAddress, toAddress, tokenId, provider, chainId, options ); // Get collection info to add context const collectionInfo = await ethersService.getERC721CollectionInfo(contractAddress, provider, chainId); return { content: [{ type: "text", text: `Successfully transferred ${collectionInfo.name} #${tokenId} to ${toAddress}.\nTransaction Hash: ${tx.hash}` }] }; } catch (error) { return { isError: true, content: [{ type: "text", text: `Error transferring NFT: ${error instanceof Error ? error.message : String(error)}` }] }; } }, safeTransferERC721: async (args: unknown) => { const schema = z.object({ contractAddress: contractAddressSchema, toAddress: z.string(), tokenId: tokenIdSchema, data: z.string().optional().default('0x'), provider: providerSchema, chainId: chainIdSchema, gasLimit: z.string().optional(), gasPrice: z.string().optional(), }); try { const { contractAddress, toAddress, tokenId, data, provider, chainId, gasLimit, gasPrice } = schema.parse(args); // Create options object for transaction parameters const options: TokenOperationOptions = {}; if (gasLimit) options.gasLimit = gasLimit; if (gasPrice) options.gasPrice = gasPrice; const tx = await ethersService.safeTransferERC721( contractAddress, toAddress, tokenId, data, provider, chainId, options ); // Get collection info to add context const collectionInfo = await ethersService.getERC721CollectionInfo(contractAddress, provider, chainId); return { content: [{ type: "text", text: `Successfully safe-transferred ${collectionInfo.name} #${tokenId} to ${toAddress}.\nTransaction Hash: ${tx.hash}` }] }; } catch (error) { return { isError: true, content: [{ type: "text", text: `Error safe-transferring NFT: ${error instanceof Error ? error.message : String(error)}` }] }; } } };

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/crazyrabbitLTC/mcp-ethers-server'

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