Merge pull request #8 from arkhai-io/refactor

Chore: Update readme
This commit is contained in:
thanhngoc541 2026-02-03 16:44:51 +07:00 committed by GitHub
commit a18648e8d2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 594 additions and 365 deletions

589
README.md
View File

@ -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 <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 <uid>` | Check escrow status |
### Environment Management
| Command | Description |
|---------|-------------|
| `nla switch <env>` | 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 <key>` | Private key for signing transactions |
| `--rpc-url <url>` | Custom RPC endpoint URL |
| `--deployment <file>` | Path to deployment JSON file |
| `--env <file>` | 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 <url>` | RPC URL (overrides deployment file) |
| `--private-key <key>` | Oracle operator private key |
| `--deployment <file>` | Deployment file path |
| `--polling-interval <ms>` | Polling interval (default: 5000ms) |
| `--openai-api-key <key>` | OpenAI API key |
| `--anthropic-api-key <key>` | Anthropic API key |
| `--openrouter-api-key <key>` | OpenRouter API key |
| `--perplexity-api-key <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 <text> \
--amount <number> \
--token <address> \
--oracle <address> \
[--arbitration-provider <provider>] \
[--arbitration-model <model>] \
[--arbitration-prompt <prompt>] \
[--private-key <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-amount> \
--token <erc20-token-address> \
--oracle <oracle-address> \
--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)

224
cli/client/status-escrow.ts Normal file
View File

@ -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 <uid> Escrow UID to check (required)
--deployment <file> 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();

View File

@ -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();

View File

@ -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"