diff --git a/.env.example b/.env.example index 2dd1a25..aae978b 100644 --- a/.env.example +++ b/.env.example @@ -34,7 +34,19 @@ ORACLE_PRIVATE_KEY=0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b7 # ================================ # AI CONFIGURATION # ================================ +# At least ONE of the following API keys is required for the oracle to function -# OpenAI API Key (required for oracle) +# OpenAI API Key (optional) # Get your API key from https://platform.openai.com/api-keys +# Supports models: gpt-4o, gpt-4o-mini, gpt-4-turbo, gpt-3.5-turbo OPENAI_API_KEY=sk-proj... + +# Anthropic API Key (optional) +# Get your API key from https://console.anthropic.com/ +# Supports models: claude-3-5-sonnet-20241022, claude-3-opus-20240229, claude-3-sonnet-20240229 +# ANTHROPIC_API_KEY=sk-ant-... + +# OpenRouter API Key (optional) +# Get your API key from https://openrouter.ai/keys +# Supports any model available on OpenRouter (e.g., openai/gpt-4, anthropic/claude-3-opus) +# OPENROUTER_API_KEY=sk-or-... diff --git a/.gitignore b/.gitignore index 8d6c402..ac666dd 100644 --- a/.gitignore +++ b/.gitignore @@ -39,3 +39,6 @@ anvil.log .DS_Store .env + +# Local deployment files +cli/deployments/localhost.json \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..58f6305 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "cSpell.words": [ + "Tierable" + ] +} \ No newline at end of file diff --git a/README.md b/README.md index 536f55f..dadc439 100644 --- a/README.md +++ b/README.md @@ -2,46 +2,29 @@ ## Prerequisites -This project depends on the `alkahest` repository, which must be cloned in the same parent directory. +- [Bun](https://bun.sh) - Fast all-in-one JavaScript runtime +- [Foundry](https://book.getfoundry.sh/getting-started/installation) - Ethereum development toolkit (for Anvil) ### Setup Instructions -1. **Clone both repositories in the same parent directory:** +1. **Clone the repository:** ```bash # Navigate to your projects directory cd ~/Desktop # or your preferred location -# Clone the alkahest repository -git clone https://github.com/arkhai-io/alkahest.git - # Clone this repository git clone https://github.com/arkhai-io/natural-language-agreements.git - -# Your directory structure should look like: -# parent-directory/ -# ├── alkahest/ -# │ └── sdks/ -# │ └── ts/ -# └── natural-language-agreements/ -``` - -2. **Install alkahest dependencies:** - -```bash -cd alkahest -bun install -cd .. -``` - -3. **Install this project's dependencies:** - -```bash cd natural-language-agreements +``` + +2. **Install dependencies:** + +```bash bun install ``` -4. **Install the `nla` CLI globally (optional but recommended):** +3. **Install the `nla` CLI globally (optional but recommended):** ```bash # Link the CLI to make it available globally @@ -110,7 +93,26 @@ Watch the oracle terminal - you'll see it process arbitration requests in real-t ## CLI Tools -The `nla` CLI provides a unified interface for all Natural Language Agreement operations. +The `nla` CLI provides a unified interface for all Natural Language Agreement operations with support for multiple LLM providers. + +### Supported LLM Providers + +The oracle supports multiple AI providers for arbitration: + +1. **OpenAI** (default) + - Models: `gpt-4o`, `gpt-4o-mini`, `gpt-4-turbo`, `gpt-3.5-turbo` + - API Key: Get from [OpenAI Platform](https://platform.openai.com/api-keys) + - Environment Variable: `OPENAI_API_KEY` + +2. **Anthropic (Claude)** + - Models: `claude-3-5-sonnet-20241022`, `claude-3-opus-20240229`, `claude-3-sonnet-20240229` + - API Key: Get from [Anthropic Console](https://console.anthropic.com/) + - Environment Variable: `ANTHROPIC_API_KEY` + +3. **OpenRouter** + - Models: Any model available on OpenRouter (e.g., `openai/gpt-4`, `anthropic/claude-3-opus`) + - API Key: Get from [OpenRouter](https://openrouter.ai/keys) + - Environment Variable: `OPENROUTER_API_KEY` ### Installation @@ -139,13 +141,22 @@ For a complete guide to all CLI commands and options, see [CLI Documentation](cl ### Quick CLI Examples ```bash -# Create an escrow +# Create an escrow with OpenAI (default) nla escrow:create \ --demand "The sky is blue" \ --amount 10 \ --token 0xa513e6e4b8f2a923d98304ec87f64353c4d5c853 \ --oracle 0x70997970C51812dc3A010C7d01b50e0d17dc79C8 +# Create an escrow with custom arbitration settings +nla escrow:create \ + --demand "Deliver package by Friday" \ + --amount 100 \ + --token 0xa513e6e4b8f2a923d98304ec87f64353c4d5c853 \ + --oracle 0x70997970C51812dc3A010C7d01b50e0d17dc79C8 \ + --arbitration-provider "Anthropic" \ + --arbitration-model "claude-3-5-sonnet-20241022" + # Fulfill an escrow nla escrow:fulfill \ --escrow-uid 0x... \ @@ -212,6 +223,26 @@ nla escrow:status [options] # Check escrow status nla help # Show help ``` +### Escrow Creation Options + +When creating an escrow, you can customize the arbitration settings: + +```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 +``` + +**Default Arbitration Settings:** +- Provider: `OpenAI` +- Model: `gpt-4o-mini` +- Prompt: Standard evaluation template + **NPM Scripts (alternative):** ```bash bun run setup # Same as: nla dev @@ -273,12 +304,12 @@ natural-language-agreements/ │ ├── index.ts # Main CLI entry point (nla command) │ ├── README.md # CLI documentation │ ├── client/ # User-facing escrow tools -│ │ ├── create-escrow.ts # Create escrow CLI +│ │ ├── create-escrow.ts # Create escrow CLI with arbitration config │ │ ├── fulfill-escrow.ts # Fulfill escrow CLI │ │ └── collect-escrow.ts # Collect escrow CLI │ ├── server/ # Server-side components │ │ ├── deploy.ts # Contract deployment script -│ │ └── oracle.ts # Oracle service +│ │ └── oracle.ts # Multi-provider oracle service │ ├── scripts/ # Shell scripts for orchestration │ │ ├── dev.sh # Development environment setup │ │ ├── deploy.sh # Deployment wrapper @@ -288,12 +319,14 @@ natural-language-agreements/ │ ├── localhost.json │ ├── sepolia.json │ └── mainnet.json -├── clients/ -│ └── nla.ts # Natural Language Agreement client library +├── 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 -├── index.ts # Development entry point +├── index.ts # Main exports ├── package.json └── README.md ``` @@ -301,8 +334,9 @@ natural-language-agreements/ ## Troubleshooting ### "Cannot find module 'alkahest-ts'" -- Ensure alkahest is cloned in the parent directory -- Run `bun install` in both alkahest and this project +- 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 @@ -317,7 +351,22 @@ natural-language-agreements/ ### "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") +- 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 @@ -327,5 +376,38 @@ natural-language-agreements/ - 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 -- Ensure your OpenAI API key is kept secure and not exposed in logs or error messages +- 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 +- 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 + +## Features + +✨ **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 + +🔧 **Flexible Configuration** +- Custom arbitration prompts +- Provider and model selection +- Default settings with override capability + +🚀 **Easy Deployment** +- One-command development setup (`nla dev`) +- Automated contract deployment +- Built-in test token distribution + +⚡ **Developer Friendly** +- TypeScript support +- Comprehensive CLI tools +- Unified interface for all operations +- Detailed error messages and logging diff --git a/bun.lock b/bun.lock index 54ad8ae..5152949 100644 --- a/bun.lock +++ b/bun.lock @@ -5,11 +5,12 @@ "": { "name": "natural-language-agreement-extension", "dependencies": { - "@ai-sdk/openai": "^2.0.50", + "@ai-sdk/anthropic": "^3.0.2", + "@ai-sdk/openai": "^3.0.2", "@viem/anvil": "^0.0.10", - "ai": "^5.0.68", - "alkahest-ts": "git+https://github.com/VAR-META-Tech/alkahest.git#ts-package", + "ai": "^6.0.5", "arktype": "^2.1.23", + "alkahest-ts": "github:arkhai-io/alkahest", "viem": "^2.42.1", "zod": "^3.25.76", }, @@ -24,13 +25,15 @@ "packages": { "@adraffy/ens-normalize": ["@adraffy/ens-normalize@1.11.1", "", {}, "sha512-nhCBV3quEgesuf7c7KYfperqSS14T8bYuvJ8PcLJp6znkZpFc0AuW4qBtr8eKVyPPe/8RSr7sglCWPU5eaxwKQ=="], - "@ai-sdk/gateway": ["@ai-sdk/gateway@2.0.21", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.19", "@vercel/oidc": "3.0.5" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-BwV7DU/lAm3Xn6iyyvZdWgVxgLu3SNXzl5y57gMvkW4nGhAOV5269IrJzQwGt03bb107sa6H6uJwWxc77zXoGA=="], + "@ai-sdk/anthropic": ["@ai-sdk/anthropic@3.0.2", "", { "dependencies": { "@ai-sdk/provider": "3.0.1", "@ai-sdk/provider-utils": "4.0.2" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-D6iSsrOYryBSPsFtOiEDv54jnjVCU/flIuXdjuRY7LdikB0KGjpazN8Dt4ONXzL+ux69ds2nzFNKke/w/fgLAA=="], - "@ai-sdk/openai": ["@ai-sdk/openai@2.0.86", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.19" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-obsLIOyA93lbQiSt1rvBItoVQp1U2RDPs0bNG0JYhm6Gku8Dg/0Cm8e4NUWT5p5PN10/doKSb3SMSKCixwIAKA=="], + "@ai-sdk/gateway": ["@ai-sdk/gateway@3.0.4", "", { "dependencies": { "@ai-sdk/provider": "3.0.1", "@ai-sdk/provider-utils": "4.0.2", "@vercel/oidc": "3.0.5" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-OlccjNYZ5+4FaNyvs0kb3N5H6U/QCKlKPTGsgUo8IZkqfMQu8ALI1XD6l/BCuTKto+OO9xUPObT/W7JhbqJ5nA=="], - "@ai-sdk/provider": ["@ai-sdk/provider@2.0.0", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-6o7Y2SeO9vFKB8lArHXehNuusnpddKPk7xqL7T2/b+OvXMRIXUO1rR4wcv1hAFUAT9avGZshty3Wlua/XA7TvA=="], + "@ai-sdk/openai": ["@ai-sdk/openai@3.0.2", "", { "dependencies": { "@ai-sdk/provider": "3.0.1", "@ai-sdk/provider-utils": "4.0.2" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-GONwavgSWtcWO+t9+GpGK8l7nIYh+zNtCL/NYDSeHxHiw6ksQS9XMRWrZyE5NpJ0EXNxSAWCHIDmb1WvTqhq9Q=="], - "@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.19", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-W41Wc9/jbUVXVwCN/7bWa4IKe8MtxO3EyA0Hfhx6grnmiYlCvpI8neSYWFE0zScXJkgA/YK3BRybzgyiXuu6JA=="], + "@ai-sdk/provider": ["@ai-sdk/provider@3.0.1", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-2lR4w7mr9XrydzxBSjir4N6YMGdXD+Np1Sh0RXABh7tWdNFFwIeRI1Q+SaYZMbfL8Pg8RRLcrxQm51yxTLhokg=="], + + "@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@4.0.2", "", { "dependencies": { "@ai-sdk/provider": "3.0.1", "@standard-schema/spec": "^1.1.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-KaykkuRBdF/ffpI5bwpL4aSCmO/99p8/ci+VeHwJO8tmvXtiVAb99QeyvvvXmL61e9Zrvv4GBGoajW19xdjkVQ=="], "@ark/schema": ["@ark/schema@0.56.0", "", { "dependencies": { "@ark/util": "0.56.0" } }, "sha512-ECg3hox/6Z/nLajxXqNhgPtNdHWC9zNsDyskwO28WinoFEnWow4IsERNz9AnXRhTZJnYIlAJ4uGn3nlLk65vZA=="], @@ -50,7 +53,7 @@ "@scure/bip39": ["@scure/bip39@1.6.0", "", { "dependencies": { "@noble/hashes": "~1.8.0", "@scure/base": "~1.2.5" } }, "sha512-+lF0BbLiJNwVlev4eKelw1WWLaiKXw7sSl8T6FvBlWkdX+94aGJ4o8XjUdlyhTCjd8c+B3KT3JfS8P0bLRNU6A=="], - "@standard-schema/spec": ["@standard-schema/spec@1.0.0", "", {}, "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA=="], + "@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="], "@types/bun": ["@types/bun@1.3.4", "", { "dependencies": { "bun-types": "1.3.4" } }, "sha512-EEPTKXHP+zKGPkhRLv+HI0UEX8/o+65hqARxLy8Ov5rIxMBPNTjeZww00CIihrIQGEQBYg+0roO5qOnS/7boGA=="], @@ -62,9 +65,9 @@ "abitype": ["abitype@1.1.0", "", { "peerDependencies": { "typescript": ">=5.0.4", "zod": "^3.22.0 || ^4.0.0" }, "optionalPeers": ["typescript", "zod"] }, "sha512-6Vh4HcRxNMLA0puzPjM5GBgT4aAcFGKZzSgAXvuZ27shJP6NEpielTuqbBmZILR5/xd0PizkBGy5hReKz9jl5A=="], - "ai": ["ai@5.0.113", "", { "dependencies": { "@ai-sdk/gateway": "2.0.21", "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.19", "@opentelemetry/api": "1.9.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-26vivpSO/mzZj0k1Si2IpsFspp26ttQICHRySQiMrtWcRd5mnJMX2a8sG28vmZ38C+JUn1cWmfZrsLMxkSMw9g=="], + "ai": ["ai@6.0.5", "", { "dependencies": { "@ai-sdk/gateway": "3.0.4", "@ai-sdk/provider": "3.0.1", "@ai-sdk/provider-utils": "4.0.2", "@opentelemetry/api": "1.9.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-CKL3dDHedWskC6EY67LrULonZBU9vL+Bwa+xQEcprBhJfxpogntG3utjiAkYuy5ZQatyWk+SmWG8HLvcnhvbRg=="], - "alkahest-ts": ["alkahest-ts@github:VAR-META-Tech/alkahest#d40de37", {}, "VAR-META-Tech-alkahest-d40de37"], + "alkahest-ts": ["alkahest-ts@github:arkhai-io/alkahest#5df4180", {}, "arkhai-io-alkahest-5df4180"], "arkregex": ["arkregex@0.0.5", "", { "dependencies": { "@ark/util": "0.56.0" } }, "sha512-ncYjBdLlh5/QnVsAA8De16Tc9EqmYM7y/WU9j+236KcyYNUXogpz3sC4ATIZYzzLxwI+0sEOaQLEmLmRleaEXw=="], diff --git a/cli/client/collect-escrow.ts b/cli/client/collect-escrow.ts index f144dd0..3746fa3 100644 --- a/cli/client/collect-escrow.ts +++ b/cli/client/collect-escrow.ts @@ -145,7 +145,7 @@ async function main() { console.log("💰 Collecting escrow...\n"); // Collect the escrow - const collectionHash = await client.erc20.collectEscrow( + const collectionHash = await client.erc20.escrow.nonTierable.collect( escrowUid as `0x${string}`, fulfillmentUid as `0x${string}`, ); diff --git a/cli/client/create-escrow.ts b/cli/client/create-escrow.ts index 242437f..0ac0731 100644 --- a/cli/client/create-escrow.ts +++ b/cli/client/create-escrow.ts @@ -13,7 +13,7 @@ import { foundry } from "viem/chains"; import { existsSync, readFileSync } from "fs"; import { resolve } from "path"; import { makeClient } from "alkahest-ts"; -import { makeLLMClient } from "../../clients/nla.ts"; +import { makeLLMClient } from "../.."; import {fixtures} from "alkahest-ts"; // Helper function to display usage @@ -34,6 +34,9 @@ Options: --private-key Your private key (required) --deployment Path to deployment file (default: ./cli/deployments/localhost.json) --rpc-url RPC URL (default: from deployment file) + --arbitration-provider Arbitration provider (default: OpenAI) + --arbitration-model Arbitration model (default: gpt-4o-mini) + --arbitration-prompt Custom arbitration prompt (optional) --help, -h Display this help message Environment Variables (alternative to CLI options): @@ -67,6 +70,9 @@ function parseCliArgs() { "private-key": { type: "string" }, "deployment": { type: "string" }, "rpc-url": { type: "string" }, + "arbitration-provider": { type: "string" }, + "arbitration-model": { type: "string" }, + "arbitration-prompt": { type: "string" }, "help": { type: "boolean", short: "h" }, }, strict: true, @@ -92,6 +98,16 @@ async function main() { const oracleAddress = args.oracle; const privateKey = args["private-key"] || process.env.PRIVATE_KEY; const deploymentPath = args.deployment || "./cli/deployments/localhost.json"; + + // Arbitration configuration with defaults + const arbitrationProvider = args["arbitration-provider"] || "OpenAI"; + const arbitrationModel = args["arbitration-model"] || "gpt-4o-mini"; + const arbitrationPrompt = args["arbitration-prompt"] || + `Evaluate the fulfillment against the demand and decide whether the demand was validly fulfilled + +Demand: {{demand}} + +Fulfillment: {{obligation}}`; // Validate required parameters if (!demand) { @@ -167,17 +183,11 @@ async function main() { deployment.addresses ); - // Extend with LLM client + // Extend with LLM client (only for encoding the demand, no API calls needed) const llmClient = client.extend((c) => ({ llm: makeLLMClient([]), })); - // Add OpenAI provider (needed for encoding demands) - llmClient.llm.addProvider({ - providerName: "OpenAI", - apiKey: process.env.OPENAI_API_KEY || "", - }); - // Check token balance const tokenBalance = await walletClient.readContract({ address: tokenAddress as `0x${string}`, @@ -197,22 +207,18 @@ async function main() { // Encode the demand with oracle arbiter const arbiter = deployment.addresses.trustedOracleArbiter; - const encodedDemand = client.arbiters.general.trustedOracle.encode({ + const encodedDemand = client.arbiters.general.trustedOracle.encodeDemand({ oracle: oracleAddress as `0x${string}`, data: llmClient.llm.encodeDemand({ - arbitrationProvider: "OpenAI", - arbitrationModel: "gpt-4.1", - arbitrationPrompt: `Evaluate the fulfillment against the demand and decide whether the demand was validly fulfilled - -Demand: {{demand}} - -Fulfillment: {{obligation}}`, + arbitrationProvider, + arbitrationModel, + arbitrationPrompt, demand: demand }) }); // Create the escrow - const { attested: escrow } = await client.erc20.permitAndBuyWithErc20( + const { attested: escrow } = await client.erc20.escrow.nonTierable.permitAndCreate( { address: tokenAddress as `0x${string}`, value: BigInt(amount), @@ -228,10 +234,16 @@ Fulfillment: {{obligation}}`, console.log(` Recipient: ${escrow.recipient}`); console.log("🎯 Next Steps:"); - console.log("1. Wait for someone to fulfill the obligation"); - console.log("2. The oracle will arbitrate the fulfillment"); - console.log("3. If approved, you can collect the escrow"); - console.log(`\n Escrow UID: ${escrow.uid}`); + console.log("1. Someone fulfills the obligation:"); + console.log(` nla escrow:fulfill \\`); + console.log(` --escrow-uid ${escrow.uid} \\`); + console.log(` --fulfillment "Yes, the sky is blue" \\`); + console.log(` --oracle ${oracleAddress}`); + console.log("\n2. The oracle will arbitrate the fulfillment automatically"); + console.log("\n3. If approved, collect the escrow:"); + console.log(` nla escrow:collect \\`); + console.log(` --escrow-uid ${escrow.uid} \\`); + console.log(` --fulfillment-uid `); } catch (error) { console.error("❌ Failed to create escrow:", error); diff --git a/cli/client/fulfill-escrow.ts b/cli/client/fulfill-escrow.ts index 7a24010..9a72570 100644 --- a/cli/client/fulfill-escrow.ts +++ b/cli/client/fulfill-escrow.ts @@ -13,6 +13,7 @@ import { foundry } from "viem/chains"; import { existsSync, readFileSync } from "fs"; import { resolve } from "path"; import { makeClient } from "alkahest-ts"; +import { makeLLMClient } from "../.."; // Helper function to display usage function displayHelp() { @@ -161,26 +162,28 @@ async function main() { fulfillment, escrowUid as `0x${string}`, ); - console.log("✅ Fulfillment created!\n"); console.log("📋 Fulfillment Details:"); console.log(` UID: ${fulfillmentAttestation.uid}`); console.log(` Attester: ${fulfillmentAttestation.attester}\n`); console.log("📤 Requesting arbitration from oracle...\n"); - + const escrow = await client.getAttestation(escrowUid as `0x${string}`); + const decodedEscrow = client.erc20.escrow.nonTierable.decodeObligation(escrow.data); // Request arbitration - await client.oracle.requestArbitration( + await client.arbiters.general.trustedOracle.requestArbitration( fulfillmentAttestation.uid, oracleAddress as `0x${string}`, + decodedEscrow.demand ); console.log("✨ Arbitration requested successfully!\n"); console.log("🎯 Next Steps:"); console.log("1. Wait for the oracle to arbitrate (usually a few seconds)"); - console.log("2. Check the result with the oracle"); - console.log("3. If approved, collect the escrow"); - console.log(`\n Fulfillment UID: ${fulfillmentAttestation.uid}`); + console.log("\n2. If approved, collect the escrow:"); + console.log(` nla escrow:collect \\`); + console.log(` --escrow-uid ${escrowUid} \\`); + console.log(` --fulfillment-uid ${fulfillmentAttestation.uid}`); } catch (error) { console.error("❌ Failed to fulfill escrow:", error); diff --git a/cli/deployments/localhost.json b/cli/deployments/localhost.json deleted file mode 100644 index 5c43d7d..0000000 --- a/cli/deployments/localhost.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "network": "localhost", - "chainId": 31337, - "rpcUrl": "http://localhost:8545", - "deployedAt": "2025-12-15T17:03:01.822Z", - "deployer": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", - "addresses": { - "easSchemaRegistry": "0x5fbdb2315678afecb367f032d93f642f64180aa3", - "eas": "0xe7f1725e7734ce288f8367e1bb143e90bb3f0512", - "trustedOracleArbiter": "0x9fe46736679d2d9a65f0992f2272de9f3c7fa6e0", - "stringObligation": "0xcf7ed3acca5a467e9e704c703e8d87f634fb0fc9", - "erc20EscrowObligation": "0xdc64a140aa3e981100a9beca4e685f962f0cf6c9", - "erc20PaymentObligation": "0x5fc8d32690cc91d4c39d9d3abcbd16989f875707", - "erc20BarterUtils": "0x0165878a594ca255338adfa4d48449f69242eb8f", - "mockERC20A": "0xa513e6e4b8f2a923d98304ec87f64353c4d5c853", - "mockERC20B": "0x2279b7a0a67db372996a5fab50d91eaa73d2ebe6", - "mockERC20C": "0x8a791620dd6260079bf849dc5567adc3f2fdc318" - } -} \ No newline at end of file diff --git a/cli/index.ts b/cli/index.ts index ad6d548..e50beff 100755 --- a/cli/index.ts +++ b/cli/index.ts @@ -36,6 +36,9 @@ Options (vary by command): --private-key Private key (all commands) --rpc-url RPC URL (default: http://localhost:8545) --deployment Load addresses from deployment file + --arbitration-provider Arbitration provider (create, default: OpenAI) + --arbitration-model Arbitration model (create, default: gpt-4o-mini) + --arbitration-prompt Custom arbitration prompt (create, optional) Environment Variables: PRIVATE_KEY Private key for transactions @@ -107,6 +110,9 @@ function parseCliArgs() { "private-key": { type: "string" }, "rpc-url": { type: "string" }, "deployment": { type: "string" }, + "arbitration-provider": { type: "string" }, + "arbitration-model": { type: "string" }, + "arbitration-prompt": { type: "string" }, }, strict: true, }); @@ -261,7 +267,7 @@ async function runStatusCommand(args: any) { const escrow = await publicClient.readContract({ address: addresses.eas, - abi: contracts.IEAS.abi, + abi: contracts.IEAS.abi.abi, functionName: "getAttestation", args: [escrowUid], }) as any; @@ -292,7 +298,7 @@ async function runStatusCommand(args: any) { const filter = await publicClient.createContractEventFilter({ address: addresses.eas, - abi: contracts.IEAS.abi, + abi: contracts.IEAS.abi.abi, eventName: "Attested", fromBlock: 0n, }); @@ -313,7 +319,7 @@ async function runStatusCommand(args: any) { const fulfillmentUid = (fulfillment as any).args?.uid; const fulfillmentAttestation = await publicClient.readContract({ address: addresses.eas, - abi: contracts.IEAS.abi, + abi: contracts.IEAS.abi.abi, functionName: "getAttestation", args: [fulfillmentUid], }) as any; @@ -330,7 +336,7 @@ async function runStatusCommand(args: any) { const decisionUid = (decision as any).args?.uid; const decisionAttestation = await publicClient.readContract({ address: addresses.eas, - abi: contracts.IEAS.abi, + abi: contracts.IEAS.abi.abi, functionName: "getAttestation", args: [decisionUid], }) as any; diff --git a/cli/server/deploy.ts b/cli/server/deploy.ts index fa55ea7..092adb5 100644 --- a/cli/server/deploy.ts +++ b/cli/server/deploy.ts @@ -159,7 +159,7 @@ async function main() { const StringObligation = contracts.StringObligation; const ERC20EscrowObligation = contracts.ERC20EscrowObligation; const ERC20PaymentObligation = contracts.ERC20PaymentObligation; - const ERC20BarterCrossToken = contracts.ERC20BarterCrossToken; + const ERC20BarterUtils = contracts.ERC20BarterUtils; console.log("✅ Contract artifacts loaded\n"); @@ -213,8 +213,8 @@ async function main() { addresses.trustedOracleArbiter = await deployContract( "Trusted Oracle Arbiter", - TrustedOracleArbiter.abi, - TrustedOracleArbiter.bytecode.object, + TrustedOracleArbiter.abi.abi, + TrustedOracleArbiter.abi.bytecode.object, [addresses.eas] ); @@ -223,32 +223,31 @@ async function main() { addresses.stringObligation = await deployContract( "String Obligation", - StringObligation.abi, - StringObligation.bytecode.object, + StringObligation.abi.abi, + StringObligation.abi.bytecode.object, [addresses.eas, addresses.easSchemaRegistry] ); addresses.erc20EscrowObligation = await deployContract( "ERC20 Escrow Obligation", - ERC20EscrowObligation.abi, - ERC20EscrowObligation.bytecode.object, + ERC20EscrowObligation.abi.abi, + ERC20EscrowObligation.abi.bytecode.object, [addresses.eas, addresses.easSchemaRegistry] ); addresses.erc20PaymentObligation = await deployContract( "ERC20 Payment Obligation", - ERC20PaymentObligation.abi, - ERC20PaymentObligation.bytecode.object, + ERC20PaymentObligation.abi.abi, + ERC20PaymentObligation.abi.bytecode.object, [addresses.eas, addresses.easSchemaRegistry] ); - // Deploy barter utils (required for permitAndBuyWithErc20) console.log("🔄 Deploying barter utils...\n"); addresses.erc20BarterUtils = await deployContract( "ERC20 Barter Utils", - ERC20BarterCrossToken.abi, - ERC20BarterCrossToken.bytecode.object, + ERC20BarterUtils.abi.abi, + ERC20BarterUtils.abi.bytecode.object, [ addresses.eas, addresses.erc20EscrowObligation, @@ -259,6 +258,8 @@ async function main() { "0x0000000000000000000000000000000000000000", // erc1155Payment (not used) "0x0000000000000000000000000000000000000000", // tokenBundleEscrow (not used) "0x0000000000000000000000000000000000000000", // tokenBundlePayment (not used) + "0x0000000000000000000000000000000000000000", // nativeEscrow (not used) + "0x0000000000000000000000000000000000000000", // nativePayment (not used) ] ); @@ -363,9 +364,17 @@ async function main() { console.log("\n🎯 Next steps:"); console.log("1. Start the oracle:"); - console.log(" ./scripts/start-oracle.sh " + network); - console.log("\n2. Create an escrow:"); - console.log(` bun run escrow:create --demand "Your demand" --amount 10 --token ${addresses.mockERC20A} --oracle 0x70997970C51812dc3A010C7d01b50e0d17dc79C8 --private-key 0xac09...`); + console.log(` nla start-oracle`); + console.log("\n2. Export your private key (use a test account private key):"); + console.log(` export PRIVATE_KEY=0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80`); + console.log("\n3. Create an escrow:"); + console.log(` nla escrow:create \\`); + console.log(` --demand "The sky is blue" \\`); + console.log(` --amount 10 \\`); + console.log(` --token ${addresses.mockERC20A} \\`); + console.log(` --oracle 0x70997970C51812dc3A010C7d01b50e0d17dc79C8 \\`); + console.log(` --arbitration-provider "OpenAI" \\`); + console.log(` --arbitration-model "gpt-4o-mini"`); } catch (error) { console.error("❌ Deployment failed:", error); diff --git a/cli/server/oracle.ts b/cli/server/oracle.ts index 335ce14..3c1c251 100644 --- a/cli/server/oracle.ts +++ b/cli/server/oracle.ts @@ -3,7 +3,7 @@ import { parseArgs } from "util"; import { parseAbiParameters, createWalletClient, http, publicActions } from "viem"; import { privateKeyToAccount } from "viem/accounts"; import { foundry } from "viem/chains"; -import { makeLLMClient } from "../../clients/nla"; +import { makeLLMClient } from "../.."; import { existsSync, readFileSync } from "fs"; import { resolve } from "path"; import { makeClient } from "alkahest-ts"; @@ -20,7 +20,9 @@ Usage: 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) + --openai-api-key OpenAI API key (optional) + --anthropic-api-key Anthropic API key (optional) + --openrouter-api-key OpenRouter API key (optional) --eas-contract
EAS contract address (optional) --deployment Load addresses from deployment file (optional) --polling-interval Polling interval in milliseconds (default: 5000) @@ -30,6 +32,8 @@ 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 + ANTHROPIC_API_KEY Anthropic API key + OPENROUTER_API_KEY OpenRouter API key EAS_CONTRACT_ADDRESS EAS contract address Examples: @@ -55,6 +59,8 @@ function parseCliArgs() { "rpc-url": { type: "string" }, "private-key": { type: "string" }, "openai-api-key": { type: "string" }, + "anthropic-api-key": { type: "string" }, + "openrouter-api-key": { type: "string" }, "eas-contract": { type: "string" }, "deployment": { type: "string" }, "polling-interval": { type: "string" }, @@ -109,6 +115,8 @@ async function main() { const privateKey = args["private-key"] || process.env.ORACLE_PRIVATE_KEY; const openaiApiKey = args["openai-api-key"] || process.env.OPENAI_API_KEY; + const anthropicApiKey = args["anthropic-api-key"] || process.env.ANTHROPIC_API_KEY; + const openrouterApiKey = args["openrouter-api-key"] || process.env.OPENROUTER_API_KEY; const pollingInterval = parseInt(args["polling-interval"] || "5000"); // Validate required parameters @@ -124,8 +132,10 @@ async function main() { process.exit(1); } - if (!openaiApiKey) { - console.error("❌ Error: OpenAI API key is required. Use --openai-api-key or set OPENAI_API_KEY environment variable."); + // Check if at least one API key is provided + if (!openaiApiKey && !anthropicApiKey && !openrouterApiKey) { + console.error("❌ Error: At least one LLM provider API key is required."); + console.error(" Set one of: OPENAI_API_KEY, ANTHROPIC_API_KEY, or OPENROUTER_API_KEY"); console.error("Run with --help for usage information."); process.exit(1); } @@ -134,7 +144,14 @@ async function main() { console.log("Configuration:"); console.log(` 📡 RPC URL: ${rpcUrl}`); console.log(` 🔑 Oracle Key: ${privateKey.slice(0, 6)}...${privateKey.slice(-4)}`); - console.log(` 🤖 AI Provider: OpenAI`); + + // Show available providers + const availableProviders = []; + if (openaiApiKey) availableProviders.push("OpenAI"); + if (anthropicApiKey) availableProviders.push("Anthropic"); + if (openrouterApiKey) availableProviders.push("OpenRouter"); + console.log(` 🤖 AI Providers: ${availableProviders.join(", ")}`); + if (easContract) { console.log(` 📝 EAS Contract: ${easContract}`); } @@ -158,19 +175,39 @@ async function main() { llm: makeLLMClient([]), })); - llmClient.llm.addProvider({ - providerName: "OpenAI", - apiKey: openaiApiKey, - }); + // Add all available providers + if (openaiApiKey) { + llmClient.llm.addProvider({ + providerName: "OpenAI", + apiKey: openaiApiKey, + }); + console.log("✅ OpenAI provider configured"); + } - console.log("🎯 LLM Arbitrator configured and ready\n"); + if (anthropicApiKey) { + llmClient.llm.addProvider({ + providerName: "Anthropic", + apiKey: anthropicApiKey, + }); + console.log("✅ Anthropic provider configured"); + } + + if (openrouterApiKey) { + llmClient.llm.addProvider({ + providerName: "OpenRouter", + apiKey: openrouterApiKey, + }); + console.log("✅ OpenRouter provider configured"); + } + + console.log("\n🎯 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( + const { unwatch } = await client.arbiters.general.trustedOracle.listenAndArbitrate( async (attestation: any) => { console.log(`\n📨 New arbitration request received!`); console.log(` Attestation UID: ${attestation.uid}`); diff --git a/clients/nla.ts b/clients/nla.ts deleted file mode 100644 index 8711f48..0000000 --- a/clients/nla.ts +++ /dev/null @@ -1,98 +0,0 @@ -import { - decodeAbiParameters, - encodeAbiParameters, - parseAbiParameters, -} from "viem"; -import { generateText } from "ai"; -import { createOpenAI } from "@ai-sdk/openai"; - - -export type LLMProvider = { - providerName: string; - apiKey?: string; -}; - -export type LLMDemand = { - arbitrationProvider: string; - arbitrationModel: string; - arbitrationPrompt: string; - demand: string; -}; - -export const makeLLMClient = ( - providers: LLMProvider[], -) => { - - const LLMAbi = parseAbiParameters( - "(string arbitrationProvider, string arbitrationModel, string arbitrationPrompt, string demand)", - ); - const encodeDemand = (demand: LLMDemand) => { - return encodeAbiParameters( - LLMAbi, - [demand], - ); - }; - - const arbitrate = async (demand: LLMDemand, obligation: string): Promise => { - try { - const matchingProvider = providers.find(provider => - provider.providerName.toLowerCase().includes(demand.arbitrationProvider.toLowerCase()) || - demand.arbitrationProvider.toLowerCase().includes(provider.providerName.toLowerCase()) - ); - - const selectedProvider = matchingProvider || providers[0]; - - if (!selectedProvider) { - throw new Error('No LLM provider available'); - } - - console.log(`Using provider: ${selectedProvider.providerName} for arbitration demand: ${JSON.stringify(demand)}`); - if (selectedProvider.providerName.toLowerCase() === 'openai') { - - const openai = createOpenAI({ - apiKey: selectedProvider.apiKey, - }) - - // Replace placeholders with actual values - const promptTemplate = `${demand.arbitrationPrompt}` - .replace(/\{\{demand\}\}/g, demand.demand) - .replace(/\{\{obligation\}\}/g, obligation); - - - const { text } = await generateText({ - model: openai(demand.arbitrationModel), - system: "You are an arbitrator that always tells the truth. You must respond with only 'true' or 'false' - no other words or explanations.", - prompt: `${promptTemplate} -Based on the above information, determine if the fulfillment satisfies the demand. -Answer ONLY with 'true' or 'false' - no explanations or additional text.` - }); - - console.log(`LLM Response: ${text}`); - - const cleanedResponse = text.trim().toLowerCase(); - return cleanedResponse === 'true'; - } - return false; - } catch (error) { - console.error('Error in LLM arbitration:', error); - throw new Error(`LLM arbitration failed: ${error}`); - } - }; - - const addProvider = (provider: LLMProvider): void => { - providers.push(provider); - }; - - const getProvider = (providerName: string): LLMProvider | undefined => { - return providers.find(provider => provider.providerName.toLowerCase() === providerName.toLowerCase()); - }; - - return { - LLMAbi, - arbitrate, - encodeDemand, - addProvider, - getProvider, - providers, - }; -}; diff --git a/index.ts b/index.ts index afea74a..65a94ed 100644 --- a/index.ts +++ b/index.ts @@ -1,12 +1,2 @@ -import { generateText } from "ai" -import { createOpenAI } from "@ai-sdk/openai" - -const openai = createOpenAI({ - apiKey: undefined, -}) - -const { text } = await generateText({ - model: openai("gpt-4.1"), - prompt: "What is love?", -}) -console.log(text); \ No newline at end of file +export { makeLLMClient } from "./nla"; +export type { LLMProvider, LLMDemand } from "./nla"; diff --git a/nla.ts b/nla.ts new file mode 100644 index 0000000..f0c4425 --- /dev/null +++ b/nla.ts @@ -0,0 +1,183 @@ +/** + * Natural Language Agreement (NLA) Client + * + * Supports multiple LLM providers for arbitration: + * + * 1. OpenAI: + * - providerName: "OpenAI" + * - models: "gpt-4", "gpt-4-turbo", "gpt-3.5-turbo", etc. + * - Get API key from: https://platform.openai.com/api-keys + * + * 2. Anthropic (Claude): + * - providerName: "Anthropic" or "Claude" + * - models: "claude-3-5-sonnet-20241022", "claude-3-opus-20240229", etc. + * - Get API key from: https://console.anthropic.com/ + * + * 3. OpenRouter: + * - providerName: "OpenRouter" + * - models: Any model available on OpenRouter (e.g., "openai/gpt-4", "anthropic/claude-3-opus") + * - Get API key from: https://openrouter.ai/keys + * - baseURL: "https://openrouter.ai/api/v1" (default) + * + * Example usage: + * ```typescript + * const llmClient = makeLLMClient([]); + * + * // Add OpenAI provider + * llmClient.addProvider({ + * providerName: "OpenAI", + * apiKey: "sk-..." + * }); + * + * // Add Anthropic provider + * llmClient.addProvider({ + * providerName: "Anthropic", + * apiKey: "sk-ant-..." + * }); + * + * // Add OpenRouter provider + * llmClient.addProvider({ + * providerName: "OpenRouter", + * apiKey: "sk-or-...", + * baseURL: "https://openrouter.ai/api/v1" + * }); + * ``` + */ + +import { + decodeAbiParameters, + encodeAbiParameters, + parseAbiParameters, +} from "viem"; +import { generateText } from "ai"; +import { createOpenAI } from "@ai-sdk/openai"; +import { createAnthropic } from "@ai-sdk/anthropic"; + + +export type LLMProvider = { + providerName: string; + apiKey?: string; + baseURL?: string; // For OpenRouter or custom endpoints +}; + +export type LLMDemand = { + arbitrationProvider: string; + arbitrationModel: string; + arbitrationPrompt: string; + demand: string; +}; + +export const makeLLMClient = ( + providers: LLMProvider[], +) => { + + const LLMAbi = parseAbiParameters( + "(string arbitrationProvider, string arbitrationModel, string arbitrationPrompt, string demand)", + ); + const encodeDemand = (demand: LLMDemand) => { + return encodeAbiParameters( + LLMAbi, + [demand], + ); + }; + + const arbitrate = async (demand: LLMDemand, obligation: string): Promise => { + try { + const matchingProvider = providers.find(provider => + provider.providerName.toLowerCase().includes(demand.arbitrationProvider.toLowerCase()) || + demand.arbitrationProvider.toLowerCase().includes(provider.providerName.toLowerCase()) + ); + + const selectedProvider = matchingProvider || providers[0]; + + if (!selectedProvider) { + throw new Error('No LLM provider available'); + } + + console.log(`Using provider: ${selectedProvider.providerName} for arbitration demand: ${JSON.stringify(demand)}`); + + // Replace placeholders with actual values + const promptTemplate = `${demand.arbitrationPrompt}` + .replace(/\{\{demand\}\}/g, demand.demand) + .replace(/\{\{obligation\}\}/g, obligation); + + const systemPrompt = "You are an arbitrator that always tells the truth. You must respond with only 'true' or 'false' - no other words or explanations."; + const userPrompt = `${promptTemplate} +Based on the above information, determine if the fulfillment satisfies the demand. +Answer ONLY with 'true' or 'false' - no explanations or additional text.`; + + let text: string; + const providerName = selectedProvider.providerName.toLowerCase(); + + if (providerName === 'openai' || providerName.includes('openai')) { + const openai = createOpenAI({ + apiKey: selectedProvider.apiKey, + baseURL: selectedProvider.baseURL, + }); + + const result = await generateText({ + model: openai(demand.arbitrationModel), + system: systemPrompt, + prompt: userPrompt, + }); + text = result.text; + + } else if (providerName === 'anthropic' || providerName.includes('anthropic') || providerName.includes('claude')) { + const anthropic = createAnthropic({ + apiKey: selectedProvider.apiKey, + baseURL: selectedProvider.baseURL, + }); + + const result = await generateText({ + model: anthropic(demand.arbitrationModel), + system: systemPrompt, + prompt: userPrompt, + }); + text = result.text; + + } else if (providerName === 'openrouter' || providerName.includes('openrouter')) { + // OpenRouter uses OpenAI-compatible API + const openrouter = createOpenAI({ + apiKey: selectedProvider.apiKey, + baseURL: selectedProvider.baseURL, + }); + + const result = await generateText({ + model: openrouter(demand.arbitrationModel), + system: systemPrompt, + prompt: userPrompt, + }); + text = result.text; + + } else { + throw new Error(`Unsupported provider: ${selectedProvider.providerName}`); + } + + console.log(`LLM Response: ${text}`); + + const cleanedResponse = text.trim().toLowerCase(); + return cleanedResponse === 'true'; + + } catch (error) { + console.error('Error in LLM arbitration:', error); + throw new Error(`LLM arbitration failed: ${error}`); + } + }; + + const addProvider = (provider: LLMProvider): void => { + providers.push(provider); + }; + + const getProvider = (providerName: string): LLMProvider | undefined => { + return providers.find(provider => provider.providerName.toLowerCase() === providerName.toLowerCase()); + }; + + return { + LLMAbi, + arbitrate, + encodeDemand, + addProvider, + getProvider, + providers, + }; +}; diff --git a/package.json b/package.json index 0665723..fd33227 100644 --- a/package.json +++ b/package.json @@ -25,10 +25,11 @@ "typescript": "^5.9.3" }, "dependencies": { - "@ai-sdk/openai": "^2.0.50", + "@ai-sdk/anthropic": "^3.0.2", + "@ai-sdk/openai": "^3.0.2", "@viem/anvil": "^0.0.10", - "ai": "^5.0.68", - "alkahest-ts": "git+https://github.com/VAR-META-Tech/alkahest.git#ts-package", + "ai": "^6.0.5", + "alkahest-ts": "github:arkhai-io/alkahest", "arktype": "^2.1.23", "viem": "^2.42.1", "zod": "^3.25.76" diff --git a/tests/nla.test.ts b/tests/nla.test.ts index dfa4264..1c342fb 100644 --- a/tests/nla.test.ts +++ b/tests/nla.test.ts @@ -1,6 +1,6 @@ import { afterAll, beforeAll, beforeEach, expect, test } from "bun:test"; -import { makeLLMClient } from "../clients/nla"; -import type { LLMProvider, LLMDemand } from "../clients/nla"; +import { makeLLMClient } from ".."; +import type { LLMProvider, LLMDemand } from ".."; import { setupTestEnvironment, type TestContext, diff --git a/tests/nlaOracle.test.ts b/tests/nlaOracle.test.ts index e76c6a3..7aaf051 100644 --- a/tests/nlaOracle.test.ts +++ b/tests/nlaOracle.test.ts @@ -8,7 +8,7 @@ import { setupTestEnvironment, type TestContext, } from "alkahest-ts"; -import { makeLLMClient } from "../clients/nla"; +import { makeLLMClient } from ".."; let testContext: TestContext; let charlieClient: ReturnType }>>; @@ -40,7 +40,7 @@ afterAll(async () => { test("listenAndArbitrate Natural Language", async () => { const arbiter = testContext.addresses.trustedOracleArbiter; - const demand = testContext.alice.client.arbiters.general.trustedOracle.encode({ + const demand = testContext.alice.client.arbiters.general.trustedOracle.encodeDemand({ oracle: testContext.bob.address, data: charlieClient.llm.encodeDemand({ arbitrationProvider: "OpenAI", @@ -55,7 +55,7 @@ Fulfillment: {{obligation}}`, }); const { attested: escrow } = - await testContext.alice.client.erc20.permitAndBuyWithErc20( + await testContext.alice.client.erc20.escrow.nonTierable.permitAndCreate( { address: testContext.mockAddresses.erc20A, value: 10n, @@ -66,7 +66,7 @@ Fulfillment: {{obligation}}`, const obligationAbi = parseAbiParameters("(string item)"); const { decisions, unwatch } = - await testContext.bob.client.oracle.listenAndArbitrate( + await testContext.bob.client.arbiters.general.trustedOracle.listenAndArbitrate( async (attestation) => { console.log("arbitrating"); const obligation = testContext.bob.client.extractObligationData( @@ -102,15 +102,16 @@ Fulfillment: {{obligation}}`, escrow.uid, ); - await testContext.bob.client.oracle.requestArbitration( + await testContext.bob.client.arbiters.general.trustedOracle.requestArbitration( fulfillment.uid, testContext.bob.address, + demand ); //Should call WaitForArbitration() - await Bun.sleep(2000); + await Bun.sleep(5000); - const collectionHash = await testContext.bob.client.erc20.collectEscrow( + const collectionHash = await testContext.bob.client.erc20.escrow.nonTierable.collect( escrow.uid, fulfillment.uid, );