/** * ROB Domains — LangChain Integration Example * * Demonstrates using the ROB Domains Agent API with LangChain.js tools * and an OpenAI chat model. Covers Search, Mint, Set Primary, and Update Profile. * * Requires: npm install langchain @langchain/openai @langchain/core zod */ import { ChatOpenAI } from '@langchain/openai'; import { DynamicStructuredTool } from '@langchain/core/tools'; import { AgentExecutor, createOpenAIToolsAgent } from 'langchain/agents'; import { ChatPromptTemplate, MessagesPlaceholder, } from '@langchain/core/prompts'; import { z } from 'zod'; const BASE_URL = 'https://robdn.com'; async function fetchJson(url: string): Promise { const res = await fetch(url); return res.json(); } async function postJson(path: string, body: unknown): Promise { const res = await fetch(`${BASE_URL}${path}`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body), }); return res.json(); } // ─── Tools ─────────────────────────────────────────────────────────────────── const searchTool = new DynamicStructuredTool({ name: 'search_rob_domain', description: 'Search for a .rob domain. Returns availability, owner, tokenId, and profile.', schema: z.object({ domain: z.string().describe('Domain label without ".rob" suffix'), }), func: async ({ domain }) => { const result = await fetchJson(`${BASE_URL}/agent/search/${domain}`); return JSON.stringify(result); }, }); const profileTool = new DynamicStructuredTool({ name: 'get_rob_profile', description: 'Get the full on-chain profile for a .rob domain.', schema: z.object({ domain: z.string(), }), func: async ({ domain }) => { const result = await fetchJson(`${BASE_URL}/agent/profile/${domain}`); return JSON.stringify(result); }, }); const reverseTool = new DynamicStructuredTool({ name: 'reverse_resolve_rob', description: 'Reverse-resolve an Ethereum wallet address to its primary .rob domain.', schema: z.object({ wallet: z.string().describe('0x-prefixed Ethereum wallet address'), }), func: async ({ wallet }) => { const result = await fetchJson(`${BASE_URL}/agent/reverse/${wallet}`); return JSON.stringify(result); }, }); const mintTool = new DynamicStructuredTool({ name: 'build_mint_transaction', description: 'Generate an unsigned mint transaction payload for a .rob domain. User must sign and broadcast.', schema: z.object({ domain: z.string().describe('Domain label to mint'), }), func: async ({ domain }) => { const result = await postJson('/agent/tx/mint', { domain }); return JSON.stringify(result); }, }); const setPrimaryTool = new DynamicStructuredTool({ name: 'build_set_primary_transaction', description: 'Generate an unsigned set-primary-domain transaction payload.', schema: z.object({ tokenId: z.string(), wallet: z.string(), }), func: async ({ tokenId, wallet }) => { const result = await postJson('/agent/tx/set-primary', { tokenId, wallet }); return JSON.stringify(result); }, }); const updateProfileTool = new DynamicStructuredTool({ name: 'build_update_profile_transaction', description: 'Generate unsigned update-profile transaction payload(s) for a .rob domain.', schema: z.object({ tokenId: z.string(), bio: z.string().optional(), profilePicture: z.string().optional(), coverImage: z.string().optional(), background: z.string().optional(), socialLinks: z .array(z.object({ platform: z.string(), url: z.string() })) .optional(), customLinks: z .array(z.object({ title: z.string(), url: z.string() })) .optional(), }), func: async (input) => { const result = await postJson('/agent/tx/update-profile', input); return JSON.stringify(result); }, }); const workflowTool = new DynamicStructuredTool({ name: 'register_domain_workflow', description: 'Get a complete multi-step execution plan for registering a .rob domain.', schema: z.object({ domain: z.string(), wallet: z.string(), setPrimary: z.boolean().optional(), profile: z .object({ bio: z.string().optional(), profilePicture: z.string().optional(), socialLinks: z .array(z.object({ platform: z.string(), url: z.string() })) .optional(), }) .optional(), }), func: async (input) => { const result = await postJson('/agent/workflow/register-domain', input); return JSON.stringify(result); }, }); // ─── Agent Setup ───────────────────────────────────────────────────────────── async function createRobDomainsAgent(): Promise { const llm = new ChatOpenAI({ model: 'gpt-4o', temperature: 0, openAIApiKey: process.env.OPENAI_API_KEY, }); const tools = [ searchTool, profileTool, reverseTool, mintTool, setPrimaryTool, updateProfileTool, workflowTool, ]; const prompt = ChatPromptTemplate.fromMessages([ [ 'system', 'You are a helpful assistant for ROB Domains (.rob), a Web3 domain name system on Robinhood Chain. Help users search domains, read profiles, and generate unsigned transaction payloads for minting, setting primary domains, and updating profiles. Always remind users that they must sign and broadcast any transaction payload — the API never executes on their behalf.', ], ['human', '{input}'], new MessagesPlaceholder('agent_scratchpad'), ]); const agent = await createOpenAIToolsAgent({ llm, tools, prompt }); return new AgentExecutor({ agent, tools, verbose: false }); } // ─── Demo ───────────────────────────────────────────────────────────────────── async function main() { const executor = await createRobDomainsAgent(); console.log('─── Search ───'); const search = await executor.invoke({ input: 'Is "alice.rob" available?' }); console.log(search.output); console.log('\n─── Mint ───'); const mint = await executor.invoke({ input: 'Build a mint transaction for "alice.rob".', }); console.log(mint.output); console.log('\n─── Set Primary ───'); const setPrimary = await executor.invoke({ input: 'Build a set-primary transaction for token #42 owned by wallet 0xAbCd1234567890AbCd1234567890AbCd12345678.', }); console.log(setPrimary.output); console.log('\n─── Update Profile ───'); const updateProfile = await executor.invoke({ input: 'Build an update-profile transaction for token #42 with bio "Web3 builder".', }); console.log(updateProfile.output); } main().catch(console.error);