#!/usr/bin/env bun import { parseArgs } from "util"; import { parseAbiParameters, createWalletClient, http, publicActions } from "viem"; import { privateKeyToAccount } from "viem/accounts"; import { foundry } from "viem/chains"; import { makeLLMClient } from "../.."; import { existsSync, readFileSync } from "fs"; import { resolve } from "path"; import { makeClient } from "alkahest-ts"; import { fixtures } from "alkahest-ts"; // Helper function to display usage function displayHelp() { console.log(` Natural Language Agreement Oracle CLI Usage: bun oracle.ts [options] Options: --rpc-url RPC URL for the blockchain network (required) --private-key Private key of the oracle operator (required) --openai-api-key OpenAI API key (required) --eas-contract
EAS contract address (optional) --deployment Load addresses from deployment file (optional) --polling-interval Polling interval in milliseconds (default: 5000) --help, -h Display this help message Environment Variables (alternative to CLI options): RPC_URL RPC URL for the blockchain network ORACLE_PRIVATE_KEY Private key of the oracle operator OPENAI_API_KEY OpenAI API key EAS_CONTRACT_ADDRESS EAS contract address Examples: # Using command line options bun oracle.ts --rpc-url http://localhost:8545 --private-key 0x... --openai-api-key sk-... # Using deployment file bun oracle.ts --deployment ./deployments/localhost.json --private-key 0x... --openai-api-key sk-... # Using environment variables export OPENAI_API_KEY=sk-... export RPC_URL=http://localhost:8545 export ORACLE_PRIVATE_KEY=0x... bun oracle.ts `); } // Parse command line arguments function parseCliArgs() { const { values } = parseArgs({ args: Bun.argv.slice(2), options: { "rpc-url": { type: "string" }, "private-key": { type: "string" }, "openai-api-key": { type: "string" }, "eas-contract": { type: "string" }, "deployment": { type: "string" }, "polling-interval": { type: "string" }, "help": { type: "boolean", short: "h" }, }, strict: true, }); return values; } // Load deployment file function loadDeployment(filePath: string) { if (!existsSync(filePath)) { throw new Error(`Deployment file not found: ${filePath}`); } const content = readFileSync(filePath, "utf-8"); return JSON.parse(content); } // Main function async function main() { try { const args = parseCliArgs(); // Display help if requested if (args.help) { displayHelp(); process.exit(0); } let rpcUrl = args["rpc-url"] || process.env.RPC_URL; let easContract = args["eas-contract"] || process.env.EAS_CONTRACT_ADDRESS; let deploymentAddresses = null; // Load deployment file if provided if (args.deployment) { console.log(`šŸ“„ Loading deployment from: ${args.deployment}\n`); const deployment = loadDeployment(args.deployment); deploymentAddresses = deployment.addresses; if (!rpcUrl) { rpcUrl = deployment.rpcUrl; } if (!easContract) { easContract = deployment.addresses.eas; } console.log(`āœ… Loaded deployment (${deployment.network})\n`); } const privateKey = args["private-key"] || process.env.ORACLE_PRIVATE_KEY; const openaiApiKey = args["openai-api-key"] || process.env.OPENAI_API_KEY; const pollingInterval = parseInt(args["polling-interval"] || "5000"); // Validate required parameters if (!rpcUrl) { console.error("āŒ Error: RPC URL is required. Use --rpc-url or set RPC_URL environment variable."); console.error("Run with --help for usage information."); process.exit(1); } if (!privateKey) { console.error("āŒ Error: Private key is required. Use --private-key or set ORACLE_PRIVATE_KEY environment variable."); console.error("Run with --help for usage information."); process.exit(1); } if (!openaiApiKey) { console.error("āŒ Error: OpenAI API key is required. Use --openai-api-key or set OPENAI_API_KEY environment variable."); console.error("Run with --help for usage information."); process.exit(1); } console.log("šŸš€ Starting Natural Language Agreement Oracle...\n"); console.log("Configuration:"); console.log(` šŸ“” RPC URL: ${rpcUrl}`); console.log(` šŸ”‘ Oracle Key: ${privateKey.slice(0, 6)}...${privateKey.slice(-4)}`); console.log(` šŸ¤– AI Provider: OpenAI`); if (easContract) { console.log(` šŸ“ EAS Contract: ${easContract}`); } console.log(` ā±ļø Polling Interval: ${pollingInterval}ms\n`); // Create wallet client const account = privateKeyToAccount(privateKey as `0x${string}`); const walletClient = createWalletClient({ account, chain: foundry, transport: http(rpcUrl), }).extend(publicActions) as any; // Create alkahest client const client = makeClient(walletClient, deploymentAddresses || { eas: easContract }); console.log(`āœ… Oracle initialized with address: ${account.address}\n`); // Create LLM client const llmClient = client.extend(() => ({ llm: makeLLMClient([]), })); llmClient.llm.addProvider({ providerName: "OpenAI", apiKey: openaiApiKey, }); console.log("šŸŽÆ LLM Arbitrator configured and ready\n"); console.log("šŸ‘‚ Listening for arbitration requests...\n"); // Define the obligation ABI const obligationAbi = parseAbiParameters("(string item)"); // Start listening and arbitrating const { unwatch } = await client.oracle.listenAndArbitrate( async (attestation: any) => { console.log(`\nšŸ“Ø New arbitration request received!`); console.log(` Attestation UID: ${attestation.uid}`); try { // Extract obligation data const obligation = client.extractObligationData( obligationAbi, attestation, ); console.log(` Obligation: "${obligation[0].item}"`); // Get demand data const [, demand] = await client.getEscrowAndDemand( llmClient.llm.LLMAbi, attestation, ); console.log(` Demand: "${demand[0].demand}"`); console.log(` Model: ${demand[0].arbitrationModel}`); // Perform arbitration using LLM console.log(` šŸ¤” Arbitrating with AI...`); const result = await llmClient.llm.arbitrate( demand[0], obligation[0].item ); console.log(` ✨ Arbitration result: ${result ? "āœ… APPROVED" : "āŒ REJECTED"}`); return result; } catch (error) { console.error(` āŒ Error during arbitration:`, error); throw error; } }, { onAfterArbitrate: async (decision: any) => { console.log(` šŸ“ Arbitration decision recorded on-chain`); console.log(` Decision UID: ${decision.attestation.uid}`); console.log(` Result: ${decision.decision ? "āœ… Fulfilled" : "āŒ Not Fulfilled"}\n`); }, pollingInterval, }, ); console.log("✨ Oracle is now running. Press Ctrl+C to stop.\n"); // Handle graceful shutdown const shutdown = async () => { console.log("\n\nšŸ›‘ Shutting down oracle..."); unwatch(); console.log("šŸ‘‹ Oracle stopped gracefully"); process.exit(0); }; process.on("SIGINT", shutdown); process.on("SIGTERM", shutdown); // Keep the process alive await new Promise(() => { }); } catch (error) { console.error("āŒ Fatal error:", error); process.exit(1); } } // Run the CLI main();