From 6c8e33dd1c17d0d3be4da0f520298b4071b4460f Mon Sep 17 00:00:00 2001 From: Utkarsh Bhimte Date: Wed, 2 Apr 2025 00:17:29 +0530 Subject: [PATCH] feat: integrate ENS tools into the main toolset --- src/tools/ens/handlers.ts | 69 +++++++++++++++++++++++++++++++++++++++ src/tools/ens/index.ts | 35 ++++++++++++++++++++ src/tools/ens/schemas.ts | 27 +++++++++++++++ src/tools/index.ts | 3 ++ 4 files changed, 134 insertions(+) create mode 100644 src/tools/ens/handlers.ts create mode 100644 src/tools/ens/index.ts create mode 100644 src/tools/ens/schemas.ts diff --git a/src/tools/ens/handlers.ts b/src/tools/ens/handlers.ts new file mode 100644 index 0000000..8ef896b --- /dev/null +++ b/src/tools/ens/handlers.ts @@ -0,0 +1,69 @@ +import { ethers } from "ethers"; +import { ResolveEnsNameInput, LookupEnsAddressInput } from "./schemas.js"; +import { PublicActions, WalletClient } from 'viem'; + +/** + * Provider mapping for different networks + */ +const getProvider = (chainId = 1) => { + const providers: Record = { + 1: new ethers.JsonRpcProvider(process.env.ETH_MAINNET_RPC_URL || "https://ethereum.publicnode.com"), + // Add support for other chains as needed + 5: new ethers.JsonRpcProvider(process.env.ETH_GOERLI_RPC_URL), + 11155111: new ethers.JsonRpcProvider(process.env.ETH_SEPOLIA_RPC_URL), + }; + + if (!providers[chainId]) { + throw new Error(`Chain ID ${chainId} not supported for ENS resolution`); + } + + return providers[chainId]; +}; + +/** + * Resolves an ENS name to its corresponding Ethereum address + * + * @param param0 - Object containing the ENS name to resolve + * @returns The Ethereum address corresponding to the ENS name, or null if not resolved + */ +export async function resolveEnsName(_wallet: WalletClient & PublicActions, { name, chainId = 1 }: ResolveEnsNameInput): Promise { + try { + const provider = getProvider(chainId); + const address = await provider.resolveName(name); + if (!address) { + throw new Error(`Failed to resolve ENS name: ${name}`); + } + return address; + } catch (error) { + console.error(`Error resolving ENS name ${name}:`, error); + if (error instanceof Error) { + throw new Error(`Failed to resolve ENS name: ${error.message}`); + } + throw new Error(`Failed to resolve ENS name: ${String(error)}`); + } +} + +/** + * Performs a reverse lookup to find the ENS name for an Ethereum address + * + * @param param0 - Object containing the Ethereum address to lookup + * @returns The ENS name corresponding to the address, or null if not found + */ +export async function lookupEnsAddress(_wallet: WalletClient & PublicActions, { address, chainId = 1 }: LookupEnsAddressInput): Promise { + try { + const provider = getProvider(chainId); + const ensName = await provider.lookupAddress(address); + + if (!ensName) { + throw new Error(`Failed to lookup ENS address: ${address}`); + } + + return ensName; + } catch (error) { + console.error(`Error looking up address ${address}:`, error); + if (error instanceof Error) { + throw new Error(`Failed to lookup ENS address: ${error.message}`); + } + throw new Error(`Failed to lookup ENS address: ${String(error)}`); + } +} \ No newline at end of file diff --git a/src/tools/ens/index.ts b/src/tools/ens/index.ts new file mode 100644 index 0000000..3611168 --- /dev/null +++ b/src/tools/ens/index.ts @@ -0,0 +1,35 @@ +// import { resolveEnsName, lookupEnsAddress } from "./handlers"; +import { generateTool } from '../../utils.js'; +import { lookupEnsAddress, resolveEnsName } from './handlers.js'; +import { LookupEnsAddressSchema, ResolveEnsNameSchema } from "./schemas.js"; + +/** + * Tool for resolving ENS (Ethereum Name Service) names and addresses + */ +export const ensTools = { + /** + * Resolves an ENS name to its corresponding Ethereum address + * @example + * // Returns "0x123...abc" + * await resolveEnsName({ name: "vitalik.eth" }); + */ + resolveEnsName: generateTool({ + name: "resolve_ens_name", + description: "Resolves an ENS name to its corresponding Ethereum address", + inputSchema: ResolveEnsNameSchema, + toolHandler: resolveEnsName, + }), + + /** + * Performs a reverse lookup to find the ENS name for an Ethereum address + * @example + * // Returns "vitalik.eth" + * await lookupEnsAddress({ address: "0x123...abc" }); + */ + lookupEnsAddress: generateTool({ + name: "lookup_ens_address", + description: "Performs a reverse lookup to find the ENS name for an Ethereum address", + inputSchema: LookupEnsAddressSchema, + toolHandler: lookupEnsAddress, + }), +}; \ No newline at end of file diff --git a/src/tools/ens/schemas.ts b/src/tools/ens/schemas.ts new file mode 100644 index 0000000..0f755f0 --- /dev/null +++ b/src/tools/ens/schemas.ts @@ -0,0 +1,27 @@ +import { z } from "zod"; + +/** + * Schema for resolveEnsName input + */ +export const ResolveEnsNameSchema = z.object({ + name: z.string().describe("The ENS name to resolve (e.g., 'vitalik.eth')"), + chainId: z.number().optional().describe("Ethereum chain ID. Defaults to 1 (Ethereum mainnet)"), +}); + +/** + * Schema for lookupEnsAddress input + */ +export const LookupEnsAddressSchema = z.object({ + address: z.string().describe("The Ethereum address to lookup (e.g., '0x123...')"), + chainId: z.number().optional().describe("Ethereum chain ID. Defaults to 1 (Ethereum mainnet)"), +}); + +/** + * Type for resolveEnsName input + */ +export type ResolveEnsNameInput = z.infer; + +/** + * Type for lookupEnsAddress input + */ +export type LookupEnsAddressInput = z.infer; \ No newline at end of file diff --git a/src/tools/index.ts b/src/tools/index.ts index 51bc97e..b3d94f2 100644 --- a/src/tools/index.ts +++ b/src/tools/index.ts @@ -1,4 +1,5 @@ import { callContractTool } from './contracts/index.js'; +import { ensTools } from "./ens/index.js"; import { erc20BalanceTool, erc20TransferTool } from './erc20/index.js'; import { getMorphoVaultsTool } from './morpho/index.js'; import { listNftsTool, transferNftTool } from './nft/index.js'; @@ -16,6 +17,8 @@ export const baseMcpTools: ToolWithHandler[] = [ listNftsTool, transferNftTool, buyOpenRouterCreditsTool, + ensTools.resolveEnsName, + ensTools.lookupEnsAddress ]; export const toolToHandler: Record = baseMcpTools.reduce<