Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 69 additions & 0 deletions src/tools/ens/handlers.ts
Original file line number Diff line number Diff line change
@@ -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<number, ethers.Provider> = {
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<string> {
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<string> {
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)}`);
}
}
35 changes: 35 additions & 0 deletions src/tools/ens/index.ts
Original file line number Diff line number Diff line change
@@ -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,
}),
};
27 changes: 27 additions & 0 deletions src/tools/ens/schemas.ts
Original file line number Diff line number Diff line change
@@ -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<typeof ResolveEnsNameSchema>;

/**
* Type for lookupEnsAddress input
*/
export type LookupEnsAddressInput = z.infer<typeof LookupEnsAddressSchema>;
3 changes: 3 additions & 0 deletions src/tools/index.ts
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -16,6 +17,8 @@ export const baseMcpTools: ToolWithHandler[] = [
listNftsTool,
transferNftTool,
buyOpenRouterCreditsTool,
ensTools.resolveEnsName,
ensTools.lookupEnsAddress
];

export const toolToHandler: Record<string, ToolHandler> = baseMcpTools.reduce<
Expand Down