diff --git a/README.md b/README.md index 23c1790..2f166bb 100644 --- a/README.md +++ b/README.md @@ -1,99 +1,220 @@ # natural-language-agreements +Natural Language Agreement Oracle - Create and manage blockchain escrows using natural language demands powered by AI. + +## Table of Contents + +- [Prerequisites](#prerequisites) +- [Installation](#installation) +- [Quick Start](#quick-start) +- [CLI Commands](#cli-commands) +- [LLM Providers](#llm-providers) +- [Deployment](#deployment-to-other-networks) +- [Examples](#examples) + ## Prerequisites -- [Bun](https://bun.sh) - Fast all-in-one JavaScript runtime -- [Foundry](https://book.getfoundry.sh/getting-started/installation) - Ethereum development toolkit (for Anvil) +- [Node.js](https://nodejs.org/) >= 18.0.0 (or [Bun](https://bun.sh)) +- [Foundry](https://book.getfoundry.sh/getting-started/installation) - Ethereum development toolkit (includes Anvil) +- API key for at least one LLM provider (OpenAI, Anthropic, or OpenRouter) -### Setup Instructions +## Installation -1. **Clone the repository:** +### Option 1: Install from npm (Recommended) + +Install the `nla` CLI globally: ```bash -# Navigate to your projects directory -cd ~/Desktop # or your preferred location - -# Clone this repository -git clone https://github.com/arkhai-io/natural-language-agreements.git -cd natural-language-agreements +npm install -g nla ``` -2. **Install dependencies:** +Verify installation: ```bash -bun install -``` - -3. **Install the `nla` CLI globally (optional but recommended):** - -```bash -# Link the CLI to make it available globally -bun link - -# Now you can use 'nla' from anywhere! nla help ``` -> **Note:** If you don't install globally, you can still use the CLI with `bun run cli/index.ts` instead of `nla`. +### Option 2: Install from source + +```bash +# Clone the repository +git clone https://github.com/arkhai-io/natural-language-agreements.git +cd natural-language-agreements + +# Install dependencies +bun install # or: npm install + +# Link the CLI globally +bun link # or: npm link + +# Verify installation +nla help +``` ## Quick Start -### Option 1: Automated Setup (Easiest - 1 command!) +### 1. Configure API Keys -Set your OpenAI API key and run everything: +Create a `.env` file or export environment variables: + +```bash +# Required: At least one LLM provider +export OPENAI_API_KEY=sk-your-openai-key +# or +export ANTHROPIC_API_KEY=sk-ant-your-anthropic-key +# or +export OPENROUTER_API_KEY=sk-or-your-openrouter-key + +# Optional: Private key for deploying/signing transactions +export PRIVATE_KEY=0x... +``` + +### 2. Start Development Environment + +Run the all-in-one setup command: ```bash -export OPENAI_API_KEY=sk-your-key-here nla dev ``` -This will: -- ✅ Check all prerequisites -- ✅ Start Anvil (local blockchain) -- ✅ Deploy all contracts -- ✅ Deploy and distribute mock ERC20 tokens -- ✅ Start the oracle -- ✅ Ready to test! +This automatically: +- ✅ Starts Anvil (local blockchain) +- ✅ Deploys all contracts +- ✅ Creates mock ERC20 tokens +- ✅ Starts the oracle listener +- ✅ Displays contract addresses + +### 3. Create Your First Escrow + +```bash +nla escrow:create \ + --demand "The sky is blue" \ + --amount 10 \ + --token 0xa513e6e4b8f2a923d98304ec87f64353c4d5c853 \ + --oracle 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 +``` + +### 4. Stop Services -To stop everything: ```bash nla stop ``` -> **Note:** If you haven't installed the CLI globally yet, run `bun link` first, or use `bun run cli/index.ts dev` instead. +## CLI Commands -### Option 2: Manual Setup (Step by Step) +### Core Commands -#### 1. Start Local Blockchain +| Command | Description | +|---------|-------------| +| `nla dev` | Start complete local development environment | +| `nla deploy [network] [rpc]` | Deploy contracts to a network | +| `nla start-oracle [options]` | Start the oracle service | +| `nla stop` | Stop all running services | +| `nla help` | Display help information | + +### Wallet Management + +| Command | Description | +|---------|-------------| +| `nla wallet:set --private-key ` | Save private key to config | +| `nla wallet:show` | Show current wallet address | +| `nla wallet:clear` | Remove private key from config | + +### Escrow Operations + +| Command | Description | +|---------|-------------| +| `nla escrow:create [options]` | Create a new escrow | +| `nla escrow:fulfill [options]` | Submit fulfillment for an escrow | +| `nla escrow:collect [options]` | Collect approved escrow funds | +| `nla escrow:status --escrow-uid ` | Check escrow status | + +### Environment Management + +| Command | Description | +|---------|-------------| +| `nla switch ` | Switch between environments | +| `nla network` | Show current environment | + +Available environments: `devnet`, `sepolia`, `base-sepolia`, `mainnet` + +### Global Options + +Most commands support these options: + +| Option | Description | +|--------|-------------| +| `--private-key ` | Private key for signing transactions | +| `--rpc-url ` | Custom RPC endpoint URL | +| `--deployment ` | Path to deployment JSON file | +| `--env ` | Path to .env file | +| `--help, -h` | Show command help | + +### Oracle-Specific Options + +When starting the oracle: ```bash -# Terminal 1: Start Anvil -anvil +nla start-oracle [options] ``` -#### 2. Deploy Contracts +| Option | Description | +|--------|-------------| +| `--rpc-url ` | RPC URL (overrides deployment file) | +| `--private-key ` | Oracle operator private key | +| `--deployment ` | Deployment file path | +| `--polling-interval ` | Polling interval (default: 5000ms) | +| `--openai-api-key ` | OpenAI API key | +| `--anthropic-api-key ` | Anthropic API key | +| `--openrouter-api-key ` | OpenRouter API key | +| `--perplexity-api-key ` | Perplexity API key | + +**Example with custom RPC:** ```bash -# Terminal 2: Deploy to localhost -export OPENAI_API_KEY=sk-your-key-here -nla deploy +nla start-oracle --rpc-url https://eth-mainnet.g.alchemy.com/v2/YOUR-KEY ``` -This creates `cli/deployments/devnet.json` with all contract addresses. - -#### 3. Start Oracle +### Escrow Creation Options ```bash -# Terminal 2 (or 3): Start oracle -nla start-oracle +nla escrow:create \ + --demand \ + --amount \ + --token
\ + --oracle
\ + [--arbitration-provider ] \ + [--arbitration-model ] \ + [--arbitration-prompt ] \ + [--private-key ] ``` +| Option | Required | Description | +|--------|----------|-------------| +| `--demand` | Yes | Natural language demand | +| `--amount` | Yes | Token amount to escrow | +| `--token` | Yes | ERC20 token contract address | +| `--oracle` | Yes | Oracle address | +| `--arbitration-provider` | No | AI provider (default: OpenAI) | +| `--arbitration-model` | No | Model name (default: gpt-4o-mini) | +| `--arbitration-prompt` | No | Custom prompt template | +| `--private-key` | No | Signer private key | -Watch the oracle terminal - you'll see it process arbitration requests in real-time! +### NPM Scripts -## CLI Tools +If installed from source, you can use npm/bun scripts: -The `nla` CLI provides a unified interface for all Natural Language Agreement operations with support for multiple LLM providers. +```bash +bun run setup # Same as: nla dev +bun run deploy # Same as: nla deploy +bun run oracle # Same as: nla start-oracle +bun run stop # Same as: nla stop +bun run escrow:create # Same as: nla escrow:create +bun run escrow:fulfill # Same as: nla escrow:fulfill +bun run escrow:collect # Same as: nla escrow:collect +``` + +## LLM Providers ### Supported LLM Providers @@ -114,41 +235,45 @@ The oracle supports multiple AI providers for arbitration: - API Key: Get from [OpenRouter](https://openrouter.ai/keys) - Environment Variable: `OPENROUTER_API_KEY` -### Installation +4. **Perplexity** (for enhanced search) + - Optional: Enhances LLM responses with real-time search + - API Key: Get from [Perplexity](https://www.perplexity.ai/settings/api) + - Environment Variable: `PERPLEXITY_API_KEY` -To use the `nla` command globally: +## Examples + +### Basic Escrow Workflow ```bash -# From the natural-language-agreements directory -bun link +# 1. Start development environment +nla dev -# Verify installation -nla help -``` - -**Alternative (without global install):** -```bash -# Use the CLI directly -bun run cli/index.ts help - -# Or use npm scripts -bun run setup # Same as: nla dev -bun run deploy # Same as: nla deploy -``` - -For a complete guide to all CLI commands and options, see [CLI Documentation](cli/README.md). - -### Quick CLI Examples - -```bash -# Create an escrow with OpenAI (default) +# 2. Create an escrow nla escrow:create \ --demand "The sky is blue" \ --amount 10 \ --token 0xa513e6e4b8f2a923d98304ec87f64353c4d5c853 \ - --oracle 0x70997970C51812dc3A010C7d01b50e0d17dc79C8 + --oracle 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 -# Create an escrow with custom arbitration settings +# 3. Fulfill the escrow +nla escrow:fulfill \ + --escrow-uid 0x... \ + --fulfillment "The sky appears blue today" \ + --oracle 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 + +# 4. Check status +nla escrow:status --escrow-uid 0x... + +# 5. Collect funds (if approved) +nla escrow:collect \ + --escrow-uid 0x... \ + --fulfillment-uid 0x... +``` + +### Using Different AI Providers + +```bash +# Create escrow with Anthropic Claude nla escrow:create \ --demand "Deliver package by Friday" \ --amount 100 \ @@ -157,19 +282,45 @@ nla escrow:create \ --arbitration-provider "Anthropic" \ --arbitration-model "claude-3-5-sonnet-20241022" -# Fulfill an escrow -nla escrow:fulfill \ - --escrow-uid 0x... \ - --fulfillment "The sky appears blue today" \ - --oracle 0x70997970C51812dc3A010C7d01b50e0d17dc79C8 +# Create escrow with OpenRouter +nla escrow:create \ + --demand "Write a 1000-word article" \ + --amount 50 \ + --token 0xa513e6e4b8f2a923d98304ec87f64353c4d5c853 \ + --oracle 0x70997970C51812dc3A010C7d01b50e0d17dc79C8 \ + --arbitration-provider "OpenRouter" \ + --arbitration-model "anthropic/claude-3-opus" +``` -# Check escrow status -nla escrow:status --escrow-uid 0x... +### Custom RPC and Deployment -# Collect approved escrow -nla escrow:collect \ - --escrow-uid 0x... \ - --fulfillment-uid 0x... +```bash +# Deploy to custom network +nla deploy \ + --network sepolia \ + --rpc-url https://sepolia.infura.io/v3/YOUR-KEY \ + --private-key 0x... + +# Start oracle with custom RPC +nla start-oracle \ + --rpc-url https://eth-mainnet.g.alchemy.com/v2/YOUR-KEY \ + --private-key 0x... + +# Use specific deployment file +nla start-oracle --deployment ./my-deployment.json +``` + +### Wallet Management + +```bash +# Save private key globally +nla wallet:set --private-key 0x... + +# Check current wallet +nla wallet:show + +# Clear saved key +nla wallet:clear ``` ## Deployment to Other Networks @@ -196,103 +347,150 @@ nla start-oracle sepolia export PRIVATE_KEY=0x... export OPENAI_API_KEY=sk-... -# Deploy +# Deploy contracts nla deploy mainnet https://mainnet.infura.io/v3/YOUR-KEY # Start oracle (consider running as a service) -nla start-oracle mainnet +nla start-oracle --rpc-url https://mainnet.infura.io/v3/YOUR-KEY ``` -## Available Commands +## Environment Configuration -The `nla` CLI provides unified access to all functionality: +The CLI uses environment files and a config directory for storing settings: + +### Environment Variables + +Create a `.env` file in your project root: ```bash -nla dev # Complete local setup (all-in-one) -nla deploy [network] [rpc] # Deploy contracts to network -nla start-oracle [network] # Start oracle for network -nla stop # Stop all services +# Private Keys +PRIVATE_KEY=0x... # For signing transactions -nla escrow:create [options] # Create a new escrow -nla escrow:fulfill [options] # Fulfill an existing escrow -nla escrow:collect [options] # Collect an approved escrow -nla escrow:status [options] # Check escrow status +# RPC URLs +RPC_URL=http://localhost:8545 # Default RPC endpoint -nla help # Show help +# LLM Provider API Keys (at least one required) +OPENAI_API_KEY=sk-... +ANTHROPIC_API_KEY=sk-ant-... +OPENROUTER_API_KEY=sk-or-... +PERPLEXITY_API_KEY=pplx-... # Optional for search ``` -### Escrow Creation Options +### Config Directory -When creating an escrow, you can customize the arbitration settings: +The CLI stores configuration in `~/.nla/`: + +- `~/.nla/config.json` - Saved wallet private key (via `nla wallet:set`) +- `~/.nla/environment` - Current active environment + +## Advanced Usage + +### Using Custom Deployment Files + +Create a custom deployment file: + +```json +{ + "network": "custom", + "chainId": 1, + "rpcUrl": "https://your-rpc.example.com", + "addresses": { + "eas": "0x...", + "trustedOracleArbiter": "0x...", + "erc20EscrowObligation": "0x..." + } +} +``` + +Use it with commands: ```bash -nla escrow:create \ - --demand "Your natural language demand" \ - --amount \ - --token \ - --oracle \ - --arbitration-provider "OpenAI|Anthropic|OpenRouter" \ # Optional, default: OpenAI - --arbitration-model "model-name" \ # Optional, default: gpt-4o-mini - --arbitration-prompt "Custom prompt template" # Optional +nla start-oracle --deployment ./my-deployment.json +nla escrow:create --deployment ./my-deployment.json ... ``` -**Default Arbitration Settings:** -- Provider: `OpenAI` -- Model: `gpt-4o-mini` -- Prompt: Standard evaluation template +### Running Oracle as a Service -**NPM Scripts (alternative):** -```bash -bun run setup # Same as: nla dev -bun run deploy # Same as: nla deploy -bun run oracle # Same as: nla start-oracle -bun run stop # Same as: nla stop +For production deployments, run the oracle as a systemd service: + +```ini +[Unit] +Description=NLA Oracle Service +After=network.target + +[Service] +Type=simple +User=nla +WorkingDirectory=/home/nla +ExecStart=/usr/bin/nla start-oracle --rpc-url https://mainnet.infura.io/v3/YOUR-KEY +Restart=always +RestartSec=10 + +[Install] +WantedBy=multi-user.target ``` -## Monitoring +### Multiple Environments -### View Oracle Logs +Switch between different networks: ```bash -# If using systemd -sudo journalctl -u nla-oracle -f +# Switch to sepolia +nla switch sepolia -# If using nohup -tail -f oracle.log +# Check current environment +nla network -# If using Anvil -tail -f anvil.log +# Switch to mainnet +nla switch mainnet + +# Back to local development +nla switch devnet ``` -### Check Oracle Status +## Troubleshooting +### Common Issues + +**"Private key is required" error:** ```bash -# Check if oracle is running -ps aux | grep "bun run oracle" +# Option 1: Set globally +nla wallet:set --private-key 0x... -# Check if Anvil is running -lsof -i :8545 +# Option 2: Export environment variable +export PRIVATE_KEY=0x... + +# Option 3: Pass with command +nla deploy --private-key 0x... ``` -## Usage - -### Running Tests - +**"RPC URL not found" error:** ```bash -bun test +# Option 1: Pass RPC URL +nla start-oracle --rpc-url https://... + +# Option 2: Set in environment +export RPC_URL=https://... + +# Option 3: Use deployment file with rpcUrl +nla start-oracle --deployment ./deployment.json ``` -### Development Mode - -To run: - +**"No LLM provider API key" error:** ```bash -bun run index.ts +# Set at least one provider key +export OPENAI_API_KEY=sk-... +# or +export ANTHROPIC_API_KEY=sk-ant-... ``` -## Development +## Documentation -This project was created using `bun init` in bun v1.2.20. [Bun](https://bun.com) is a fast all-in-one JavaScript runtime. +For more detailed documentation: + +- [CLI Documentation](cli/README.md) - Complete CLI reference +- [API Documentation](https://github.com/arkhai-io/natural-language-agreements) - GitHub repository +- [Alkahest SDK](https://github.com/arkhai-io/alkahest-ts) - Underlying SDK ## Project Structure @@ -308,104 +506,49 @@ natural-language-agreements/ │ ├── server/ # Server-side components │ │ ├── deploy.ts # Contract deployment script │ │ └── oracle.ts # Multi-provider oracle service -│ ├── scripts/ # Shell scripts for orchestration -│ │ ├── dev.sh # Development environment setup -│ │ ├── deploy.sh # Deployment wrapper -│ │ ├── start-oracle.sh # Oracle starter -│ │ └── stop.sh # Cleanup script +│ ├── commands/ # CLI command implementations +│ │ ├── dev.ts # Development environment setup +│ │ ├── stop.ts # Stop services +│ │ ├── switch.ts # Switch environments +│ │ └── wallet.ts # Wallet management │ └── deployments/ # Deployment addresses (generated) │ ├── devnet.json │ ├── sepolia.json +│ ├── base-sepolia.json │ └── mainnet.json ├── nla.ts # Natural Language Agreement client library -│ # - Multi-provider LLM support -│ # - Arbitration encoding/decoding -│ # - OpenAI, Anthropic, OpenRouter integration -├── tests/ -│ ├── nla.test.ts # Basic tests -│ └── nlaOracle.test.ts # Oracle arbitration tests +├── tests/ # Test files +│ ├── nla.test.ts +│ └── nlaOracle.test.ts ├── index.ts # Main exports -├── package.json -└── README.md +└── package.json # Package configuration ``` -## Troubleshooting - -### "Cannot find module 'alkahest-ts'" -- Run `bun install` to ensure all dependencies are installed -- Clear the cache: `rm -rf node_modules && bun install` -- Check that package.json includes alkahest-ts dependency - -### "Deployer has no ETH" -- Fund your deployer account before running deployment -- For testnets, use a faucet - -### "Oracle not detecting arbitration requests" -- Verify RPC URL is correct and accessible -- Check that EAS contract address matches deployment -- Ensure oracle has ETH for gas -- Check polling interval (try lowering it) - -### "OpenAI API errors" -- Verify API key is valid and active -- Check OpenAI usage limits and billing -- Ensure model name is correct (e.g., "gpt-4o-mini", "gpt-4o") - -### "Anthropic API errors" -- Verify ANTHROPIC_API_KEY is set correctly -- Check Anthropic usage limits and billing -- Ensure model name is correct (e.g., "claude-3-5-sonnet-20241022") - -### "Arbitration provider not found" -- The oracle was configured with a different provider than the escrow -- Make sure the oracle has the correct API keys for the provider specified in the escrow -- Supported providers: OpenAI, Anthropic, OpenRouter - -### "Module resolution errors" -- Run `bun install` to ensure alkahest-ts is properly installed -- Check that you're using the correct version of Bun: `bun --version` -- Clear Bun's cache: `rm -rf node_modules && bun install` - ## Security Notes ⚠️ **Important Security Considerations:** -- Never commit your real private keys to version control +- Never commit real private keys to version control - Use environment variables or secure secret management for production - The `.env` file is gitignored by default -- The example private key in `.env.example` is from Anvil and should NEVER be used in production -- Keep all API keys secure (OpenAI, Anthropic, OpenRouter): - * Don't expose them in logs or error messages - * Use environment variables or secure secret management - * Rotate keys regularly - * Monitor usage for unauthorized access -- Run the oracle in a secure environment with proper access controls +- Example keys in `.env.example` are from Anvil - NEVER use in production +- Keep API keys secure (OpenAI, Anthropic, OpenRouter) - For production deployments: * Use hardware wallets or secure key management services * Implement rate limiting on the oracle * Monitor arbitration decisions for anomalies - * Consider using a multi-signature setup for critical operations + * Consider multi-signature setups for critical operations -## Features +## License -✨ **Multi-Provider LLM Support** -- OpenAI (GPT-4, GPT-4o, GPT-3.5-turbo) -- Anthropic (Claude 3 family) -- OpenRouter (Access to any model) -- Configurable per-escrow arbitration settings +MIT -🔧 **Flexible Configuration** -- Custom arbitration prompts -- Provider and model selection -- Default settings with override capability +## Contributing -🚀 **Easy Deployment** -- One-command development setup (`nla dev`) -- Automated contract deployment -- Built-in test token distribution +Contributions are welcome! Please feel free to submit a Pull Request. -⚡ **Developer Friendly** -- TypeScript support -- Comprehensive CLI tools -- Unified interface for all operations -- Detailed error messages and logging +## Support + +For issues and questions: +- GitHub Issues: https://github.com/arkhai-io/natural-language-agreements/issues +- Documentation: [CLI README](cli/README.md) \ No newline at end of file diff --git a/cli/client/status-escrow.ts b/cli/client/status-escrow.ts new file mode 100644 index 0000000..cf89758 --- /dev/null +++ b/cli/client/status-escrow.ts @@ -0,0 +1,224 @@ +#!/usr/bin/env node +import { parseArgs } from "util"; +import { createPublicClient, http, parseAbiParameters, decodeAbiParameters } from "viem"; +import { contracts } from "alkahest-ts"; +import { getChainFromNetwork, loadDeploymentWithDefaults } from "../utils.js"; + +// Helper function to display usage +function displayHelp() { + console.log(` +Natural Language Agreement - Check Escrow Status + +Usage: + bun status-escrow.ts [options] + +Options: + --escrow-uid Escrow UID to check (required) + --deployment Load addresses from deployment file (optional) + --help, -h Display this help message + +Environment Variables: + RPC_URL RPC URL for blockchain connection + +Examples: + # Check escrow status + bun status-escrow.ts --escrow-uid 0x... + + # Use specific deployment file + bun status-escrow.ts --escrow-uid 0x... --deployment ./deployments/sepolia.json +`); +} + +// Parse command line arguments +function parseCliArgs() { + const { values } = parseArgs({ + args: process.argv.slice(2), + options: { + "escrow-uid": { type: "string" }, + "deployment": { type: "string" }, + "help": { type: "boolean", short: "h" }, + }, + strict: true, + }); + + return values; +} + +// Main function +async function main() { + try { + const args = parseCliArgs(); + + // Display help if requested + if (args.help) { + displayHelp(); + process.exit(0); + } + + const escrowUid = args["escrow-uid"]; + const deploymentFile = args["deployment"]; + + // Validate required parameters + if (!escrowUid) { + console.error("❌ Error: --escrow-uid is required"); + console.error("Run with --help for usage information."); + process.exit(1); + } + + console.log("🔍 Checking Escrow Status\n"); + + // Load deployment file (auto-detects current network if not specified) + const deployment = loadDeploymentWithDefaults(deploymentFile); + const addresses = deployment.addresses; + + console.log(`Configuration:`); + console.log(` 📦 Escrow UID: ${escrowUid}`); + console.log(` 🌐 Network: ${deployment.network}`); + console.log(` 📡 RPC URL: ${deployment.rpcUrl}\n`); + + if (!addresses.eas) { + console.error("❌ Error: EAS address not found in deployment file."); + console.error(" Make sure you have a valid deployment file."); + process.exit(1); + } + + // Create public client + const chain = getChainFromNetwork(deployment.network); + const publicClient = createPublicClient({ + chain, + transport: http(deployment.rpcUrl), + }); + + // Get escrow attestation + console.log("📋 Fetching escrow details...\n"); + + const escrow = await publicClient.readContract({ + address: addresses.eas as `0x${string}`, + abi: contracts.IEAS.abi.abi, + functionName: "getAttestation", + args: [escrowUid as `0x${string}`], + }) as any; + + console.log("📦 Escrow Information:"); + console.log(` UID: ${escrow.uid}`); + console.log(` Schema: ${escrow.schema}`); + console.log(` Attester: ${escrow.attester}`); + console.log(` Recipient: ${escrow.recipient}`); + console.log(` Revoked: ${escrow.revocationTime > 0n ? "Yes ❌" : "No ✅"}`); + + // Try to decode the data + try { + const llmAbi = parseAbiParameters("(string demand, string arbitrationModel, address arbitrator)"); + const decoded = decodeAbiParameters(llmAbi, escrow.data); + console.log(`\n📝 Escrow Details:`); + console.log(` Demand: "${decoded[0].demand}"`); + console.log(` Model: ${decoded[0].arbitrationModel}`); + console.log(` Arbitrator: ${decoded[0].arbitrator}`); + } catch (e) { + console.log(`\n📝 Raw Data: ${escrow.data}`); + } + + // Check for fulfillments + console.log(`\n🔎 Checking for fulfillments...`); + + // Get all Attested events + const filter = await publicClient.createContractEventFilter({ + address: addresses.eas as `0x${string}`, + abi: contracts.IEAS.abi.abi, + eventName: "Attested", + fromBlock: 0n, + }); + + const events = await publicClient.getFilterLogs({ filter }); + + // Debug: log total events found + console.log(` Total events found: ${events.length}`); + + // Debug: show all refUIDs + console.log(` Debug - Looking for escrow UID: ${escrowUid.toLowerCase()}`); + events.forEach((event: any, index: number) => { + const refUID = event.args?.refUID; + console.log(` Event ${index}: refUID = ${refUID ? refUID.toLowerCase() : 'null'}, uid = ${event.args?.uid}`); + }); + + // Find fulfillments that reference this escrow + const fulfillments = events.filter((event: any) => { + const refUID = event.args?.refUID; + // Compare as lowercase hex strings to avoid case sensitivity issues + return refUID && refUID.toLowerCase() === escrowUid.toLowerCase(); + }); + + console.log(` Fulfillments matching escrow: ${fulfillments.length}`); + + if (fulfillments.length === 0) { + console.log(` No fulfillments found yet\n`); + } else { + console.log(` Found ${fulfillments.length} fulfillment(s):\n`); + + for (const fulfillment of fulfillments) { + const fulfillmentUid = fulfillment.args?.uid; + if (!fulfillmentUid) continue; + + const fulfillmentAttestation = await publicClient.readContract({ + address: addresses.eas as `0x${string}`, + abi: contracts.IEAS.abi.abi, + functionName: "getAttestation", + args: [fulfillmentUid], + }) as any; + + console.log(` 📨 Fulfillment UID: ${fulfillmentUid}`); + console.log(` Attester: ${fulfillmentAttestation.attester}`); + console.log(` Revoked: ${fulfillmentAttestation.revocationTime > 0n ? "Yes ❌" : "No ✅"}`); + + // Try to decode fulfillment data + try { + const fulfillmentAbi = parseAbiParameters("(string item)"); + const fulfillmentData = decodeAbiParameters(fulfillmentAbi, fulfillmentAttestation.data); + console.log(` Fulfillment Text: "${fulfillmentData[0].item}"`); + } catch (e) { + // Skip if can't decode + } + + // Check for arbitration decision + const decisions = events.filter((e: any) => { + const refUID = e.args?.refUID; + return refUID && refUID.toLowerCase() === fulfillmentUid.toLowerCase(); + }); + if (decisions.length > 0) { + console.log(` ⚖️ Arbitration: Decision recorded`); + for (const decision of decisions) { + const decisionUid = decision.args?.uid; + if (!decisionUid) continue; + + const decisionAttestation = await publicClient.readContract({ + address: addresses.eas as `0x${string}`, + abi: contracts.IEAS.abi.abi, + functionName: "getAttestation", + args: [decisionUid], + }) as any; + + try { + const decisionAbi = parseAbiParameters("(bool item)"); + const decisionData = decodeAbiParameters(decisionAbi, decisionAttestation.data); + console.log(` Result: ${decisionData[0].item ? "✅ APPROVED" : "❌ REJECTED"}`); + } catch (e) { + console.log(` Result: Unknown`); + } + } + } else { + console.log(` ⚖️ Arbitration: Pending...`); + } + console.log(); + } + } + + console.log("✨ Status check complete!\n"); + + } catch (error) { + console.error("❌ Fatal error:", error); + process.exit(1); + } +} + +// Run the script +main(); diff --git a/cli/index.ts b/cli/index.ts index 5c559d3..c7b0755 100755 --- a/cli/index.ts +++ b/cli/index.ts @@ -233,8 +233,8 @@ async function main() { scriptPath = "./client/collect-escrow.js"; break; case "escrow:status": - await runStatusCommand(args); - return; + scriptPath = "./client/status-escrow.js"; + break; default: console.error(`❌ Unknown command: ${command}`); console.error("Run 'nla help' for usage information."); @@ -261,144 +261,5 @@ async function main() { } } -// Status command handler -async function runStatusCommand(args: any) { - const escrowUid = args["escrow-uid"]; - const rpcUrl = args["rpc-url"] || process.env.RPC_URL || "http://localhost:8545"; - const deploymentFile = args["deployment"]; - - if (!escrowUid) { - console.error("❌ Error: --escrow-uid is required for status command"); - process.exit(1); - } - - console.log("🔍 Checking Escrow Status\n"); - console.log(`Configuration:`); - console.log(` 📦 Escrow UID: ${escrowUid}`); - console.log(` 🌐 RPC URL: ${rpcUrl}\n`); - - // Import required modules - const { createPublicClient, http, parseAbiParameters } = await import("viem"); - const { foundry } = await import("viem/chains"); - const { existsSync, readFileSync } = await import("fs"); - - // Load deployment addresses - let addresses: any = {}; - if (deploymentFile && existsSync(deploymentFile)) { - const deployment = JSON.parse(readFileSync(deploymentFile, "utf-8")); - addresses = deployment.addresses; - } - - // Create public client - const publicClient = createPublicClient({ - chain: foundry, - transport: http(rpcUrl), - }); - - - if (!addresses.eas) { - console.error("❌ Error: EAS address not found. Use --deployment to specify deployment file."); - process.exit(1); - } - - // Get escrow attestation - console.log("📋 Fetching escrow details...\n"); - - const escrow = await publicClient.readContract({ - address: addresses.eas, - abi: contracts.IEAS.abi.abi, - functionName: "getAttestation", - args: [escrowUid], - }) as any; - - console.log("📦 Escrow Information:"); - console.log(` UID: ${escrow.uid}`); - console.log(` Schema: ${escrow.schema}`); - console.log(` Attester: ${escrow.attester}`); - console.log(` Recipient: ${escrow.recipient}`); - console.log(` Revoked: ${escrow.revocationTime > 0n ? "Yes ❌" : "No ✅"}`); - - // Try to decode the data - try { - const llmAbi = parseAbiParameters("(string demand, string arbitrationModel, address arbitrator)"); - const decoded = await import("viem").then(m => - m.decodeAbiParameters(llmAbi, escrow.data) - ); - console.log(`\n📝 Escrow Details:`); - console.log(` Demand: "${decoded[0].demand}"`); - console.log(` Model: ${decoded[0].arbitrationModel}`); - console.log(` Arbitrator: ${decoded[0].arbitrator}`); - } catch (e) { - console.log(`\n📝 Raw Data: ${escrow.data}`); - } - - // Check for fulfillments - console.log(`\n🔎 Checking for fulfillments...`); - - const filter = await publicClient.createContractEventFilter({ - address: addresses.eas, - abi: contracts.IEAS.abi.abi, - eventName: "Attested", - fromBlock: 0n, - }); - - const events = await publicClient.getFilterLogs({ filter }); - - // Find fulfillments that reference this escrow - const fulfillments = events.filter((event: any) => { - return (event as any).args?.refUID === escrowUid; - }); - - if (fulfillments.length === 0) { - console.log(` No fulfillments found yet`); - } else { - console.log(` Found ${fulfillments.length} fulfillment(s):\n`); - - for (const fulfillment of fulfillments) { - const fulfillmentUid = (fulfillment as any).args?.uid; - const fulfillmentAttestation = await publicClient.readContract({ - address: addresses.eas, - abi: contracts.IEAS.abi.abi, - functionName: "getAttestation", - args: [fulfillmentUid], - }) as any; - - console.log(` 📨 Fulfillment UID: ${fulfillmentUid}`); - console.log(` Attester: ${fulfillmentAttestation.attester}`); - console.log(` Revoked: ${fulfillmentAttestation.revocationTime > 0n ? "Yes ❌" : "No ✅"}`); - - // Check for arbitration decision - const decisions = events.filter((e: any) => (e as any).args?.refUID === fulfillmentUid); - if (decisions.length > 0) { - console.log(` ⚖️ Arbitration: Decision recorded`); - for (const decision of decisions) { - const decisionUid = (decision as any).args?.uid; - const decisionAttestation = await publicClient.readContract({ - address: addresses.eas, - abi: contracts.IEAS.abi.abi, - functionName: "getAttestation", - args: [decisionUid], - }) as any; - - try { - const decisionAbi = parseAbiParameters("(bool item)"); - const decisionData = await import("viem").then(m => - m.decodeAbiParameters(decisionAbi, decisionAttestation.data) - ); - console.log(` Result: ${decisionData[0].item ? "✅ APPROVED" : "❌ REJECTED"}`); - } catch (e) { - console.log(` Result: Unknown`); - } - } - } else { - console.log(` ⚖️ Arbitration: Pending...`); - } - console.log(); - } - } - - console.log("✨ Status check complete!\n"); -} - // Run the CLI main(); diff --git a/package.json b/package.json index 6082040..9aa68a3 100644 --- a/package.json +++ b/package.json @@ -53,7 +53,8 @@ "stop": "bun run cli/index.ts stop", "escrow:create": "bun run cli/index.ts escrow:create", "escrow:fulfill": "bun run cli/index.ts escrow:fulfill", - "escrow:collect": "bun run cli/index.ts escrow:collect" + "escrow:collect": "bun run cli/index.ts escrow:collect", + "escrow:status": "bun run cli/index.ts escrow:status" }, "peerDependencies": { "typescript": "^5.9.3"