diff --git a/src/agent/tools.ts b/src/agent/tools.ts index 3b725b4c..50529829 100644 --- a/src/agent/tools.ts +++ b/src/agent/tools.ts @@ -1389,6 +1389,7 @@ Model: ${ctx.inference.getDefaultModel()} args.agent_uri as string, ((args.network as string) || "mainnet") as any, ctx.db, + ctx.config.rpcUrl, ); return `Registered on-chain! Agent ID: ${entry.agentId}, TX: ${entry.txHash}`; } catch (err: any) { @@ -1435,9 +1436,10 @@ Model: ${ctx.inference.getDefaultModel()} const limit = (args.limit as number) || 10; // Phase 3.2: Pass db.raw for agent card caching + const rpcUrl = ctx.config.rpcUrl; const agents = keyword - ? await searchAgents(keyword, limit, network, undefined, ctx.db.raw) - : await discoverAgents(limit, network, undefined, ctx.db.raw); + ? await searchAgents(keyword, limit, network, undefined, ctx.db.raw, rpcUrl) + : await discoverAgents(limit, network, undefined, ctx.db.raw, rpcUrl); if (agents.length === 0) return "No agents found."; return agents @@ -1494,6 +1496,7 @@ Model: ${ctx.inference.getDefaultModel()} comment, network, ctx.db, + ctx.config.rpcUrl, ); return `Feedback submitted. TX: ${hash}`; }, diff --git a/src/conway/x402.ts b/src/conway/x402.ts index 39626d87..fc9f1fa8 100644 --- a/src/conway/x402.ts +++ b/src/conway/x402.ts @@ -213,9 +213,10 @@ export async function getUsdcBalanceDetailed( } try { + const rpcUrl = process.env.AUTOMATON_RPC_URL || undefined; const client = createPublicClient({ chain, - transport: http(undefined, { timeout: 10_000 }), + transport: http(rpcUrl, { timeout: 10_000 }), }); const balance = await client.readContract({ diff --git a/src/registry/discovery.ts b/src/registry/discovery.ts index f6d1b03d..9432c2ac 100644 --- a/src/registry/discovery.ts +++ b/src/registry/discovery.ts @@ -218,9 +218,10 @@ export async function discoverAgents( network: Network = "mainnet", config?: Partial, db?: import("better-sqlite3").Database, + rpcUrl?: string, ): Promise { const cfg = { ...DEFAULT_DISCOVERY_CONFIG, ...config }; - const total = await getTotalAgents(network); + const total = await getTotalAgents(network, rpcUrl); const agents: DiscoveredAgent[] = []; const overallStart = Date.now(); @@ -235,7 +236,7 @@ export async function discoverAgents( } try { - const agent = await queryAgent(i.toString(), network); + const agent = await queryAgent(i.toString(), network, rpcUrl); if (agent) { await enrichAgentWithCard(agent, cfg, db); agents.push(agent); @@ -247,7 +248,7 @@ export async function discoverAgents( } else { // totalSupply returned 0 (likely reverted) — fall back to Transfer event scanning logger.info("totalSupply returned 0, falling back to Transfer event scanning"); - const eventAgents = await getRegisteredAgentsByEvents(network, Math.min(limit, cfg.maxScanCount)); + const eventAgents = await getRegisteredAgentsByEvents(network, Math.min(limit, cfg.maxScanCount), rpcUrl); for (const { tokenId, owner } of eventAgents) { if (Date.now() - overallStart > DISCOVERY_TIMEOUT_MS) { @@ -257,7 +258,7 @@ export async function discoverAgents( try { // Try queryAgent first (gets tokenURI), fall back to event data only - const agent = await queryAgent(tokenId, network); + const agent = await queryAgent(tokenId, network, rpcUrl); if (agent) { // Use owner from event if queryAgent couldn't get it if (!agent.owner && owner) { @@ -347,8 +348,9 @@ export async function searchAgents( network: Network = "mainnet", config?: Partial, db?: import("better-sqlite3").Database, + rpcUrl?: string, ): Promise { - const all = await discoverAgents(50, network, config, db); + const all = await discoverAgents(50, network, config, db, rpcUrl); const lower = keyword.toLowerCase(); return all diff --git a/src/registry/erc8004.ts b/src/registry/erc8004.ts index 0800f644..beaa0ed1 100644 --- a/src/registry/erc8004.ts +++ b/src/registry/erc8004.ts @@ -75,6 +75,14 @@ const TRANSFER_EVENT_TOPIC = keccak256( type Network = "mainnet" | "testnet"; +/** + * Resolve the RPC transport URL. + * Priority: explicit parameter > AUTOMATON_RPC_URL env var > viem default (public RPC). + */ +function resolveRpcUrl(rpcUrl?: string): string | undefined { + return rpcUrl || process.env.AUTOMATON_RPC_URL || undefined; +} + // ─── Preflight Check ──────────────────────────────────────────── /** @@ -90,13 +98,14 @@ async function preflight( functionName: string; args: any[]; }, + rpcUrl?: string, ): Promise { const contracts = CONTRACTS[network]; const chain = contracts.chain; const publicClient = createPublicClient({ chain, - transport: http(), + transport: http(resolveRpcUrl(rpcUrl)), }); // Encode calldata for accurate gas estimation @@ -206,9 +215,11 @@ export async function registerAgent( agentURI: string, network: Network = "mainnet", db: AutomatonDatabase, + rpcUrl?: string, ): Promise { const contracts = CONTRACTS[network]; const chain = contracts.chain; + const rpc = resolveRpcUrl(rpcUrl); // Phase 3.2: Preflight gas check await preflight(account, network, { @@ -216,17 +227,17 @@ export async function registerAgent( abi: IDENTITY_ABI, functionName: "register", args: [agentURI], - }); + }, rpcUrl); const publicClient = createPublicClient({ chain, - transport: http(), + transport: http(rpc), }); const walletClient = createWalletClient({ account, chain, - transport: http(), + transport: http(rpc), }); // Call register(agentURI) @@ -292,6 +303,7 @@ export async function updateAgentURI( newAgentURI: string, network: Network = "mainnet", db: AutomatonDatabase, + rpcUrl?: string, ): Promise { const contracts = CONTRACTS[network]; const chain = contracts.chain; @@ -302,12 +314,12 @@ export async function updateAgentURI( abi: IDENTITY_ABI, functionName: "setAgentURI", args: [BigInt(agentId), newAgentURI], - }); + }, rpcUrl); const walletClient = createWalletClient({ account, chain, - transport: http(), + transport: http(resolveRpcUrl(rpcUrl)), }); const hash = await walletClient.writeContract({ @@ -352,6 +364,7 @@ export async function leaveFeedback( comment: string, network: Network = "mainnet", db: AutomatonDatabase, + rpcUrl?: string, ): Promise { // Phase 3.2: Validate score range 1-5 if (!Number.isInteger(score) || score < 1 || score > 5) { @@ -374,12 +387,12 @@ export async function leaveFeedback( abi: REPUTATION_ABI, functionName: "leaveFeedback", args: [BigInt(agentId), score, comment], - }); + }, rpcUrl); const walletClient = createWalletClient({ account, chain, - transport: http(), + transport: http(resolveRpcUrl(rpcUrl)), }); const hash = await walletClient.writeContract({ @@ -409,13 +422,14 @@ export async function leaveFeedback( export async function queryAgent( agentId: string, network: Network = "mainnet", + rpcUrl?: string, ): Promise { const contracts = CONTRACTS[network]; const chain = contracts.chain; const publicClient = createPublicClient({ chain, - transport: http(), + transport: http(resolveRpcUrl(rpcUrl)), }); try { @@ -456,13 +470,14 @@ export async function queryAgent( */ export async function getTotalAgents( network: Network = "mainnet", + rpcUrl?: string, ): Promise { const contracts = CONTRACTS[network]; const chain = contracts.chain; const publicClient = createPublicClient({ chain, - transport: http(), + transport: http(resolveRpcUrl(rpcUrl)), }); try { @@ -538,13 +553,14 @@ async function estimateTotalByBinarySearch( export async function getRegisteredAgentsByEvents( network: Network = "mainnet", limit: number = 20, + rpcUrl?: string, ): Promise<{ tokenId: string; owner: string }[]> { const contracts = CONTRACTS[network]; const chain = contracts.chain; const publicClient = createPublicClient({ chain, - transport: http(), + transport: http(resolveRpcUrl(rpcUrl)), }); try { @@ -647,13 +663,14 @@ export async function getRegisteredAgentsByEvents( export async function hasRegisteredAgent( address: Address, network: Network = "mainnet", + rpcUrl?: string, ): Promise { const contracts = CONTRACTS[network]; const chain = contracts.chain; const publicClient = createPublicClient({ chain, - transport: http(), + transport: http(resolveRpcUrl(rpcUrl)), }); try { diff --git a/src/setup/configure.ts b/src/setup/configure.ts index 0192e79f..0cb9b71a 100644 --- a/src/setup/configure.ts +++ b/src/setup/configure.ts @@ -303,6 +303,7 @@ async function configureGeneral(config: AutomatonConfig): Promise { ); config.maxChildren = await askNumber("Max child automatons", config.maxChildren); config.socialRelayUrl = (await askString("Social relay URL", config.socialRelayUrl)) || undefined; + config.rpcUrl = (await askString("RPC endpoint (Base chain, e.g. https://mainnet.base.org)", config.rpcUrl)) || undefined; console.log(""); } diff --git a/src/types.ts b/src/types.ts index 998c8ea1..261a5997 100644 --- a/src/types.ts +++ b/src/types.ts @@ -62,6 +62,8 @@ export interface AutomatonConfig { // Phase 2 config additions soulConfig?: SoulConfig; modelStrategy?: ModelStrategyConfig; + /** Custom RPC endpoint for Base chain interactions (overrides default public RPC) */ + rpcUrl?: string; } export const DEFAULT_CONFIG: Partial = {