From 0d5abc519c5325d27984fcceb4d418bf8dc162c6 Mon Sep 17 00:00:00 2001 From: ngoc Date: Fri, 12 Dec 2025 16:20:43 +0700 Subject: [PATCH] Refactor nla cli app --- QUICKSTART.md | 4 +- cli/README.md | 157 +++++++-- cli/{ => client}/collect-escrow.ts | 6 +- cli/{ => client}/create-escrow.ts | 10 +- cli/{ => client}/fulfill-escrow.ts | 6 +- .../deployments}/localhost.json | 2 +- cli/index.ts | 315 ++++++++++++++++++ {scripts => cli/scripts}/deploy.sh | 8 +- {scripts => cli/scripts}/dev.sh | 4 +- {scripts => cli/scripts}/start-oracle.sh | 6 +- {scripts => cli/scripts}/stop.sh | 0 {setups => cli/server}/deploy.ts | 9 +- {setups => cli/server}/oracle.ts | 4 +- package.json | 17 +- 14 files changed, 476 insertions(+), 72 deletions(-) rename cli/{ => client}/collect-escrow.ts (96%) rename cli/{ => client}/create-escrow.ts (95%) rename cli/{ => client}/fulfill-escrow.ts (96%) rename {deployments => cli/deployments}/localhost.json (94%) create mode 100755 cli/index.ts rename {scripts => cli/scripts}/deploy.sh (84%) rename {scripts => cli/scripts}/dev.sh (97%) rename {scripts => cli/scripts}/start-oracle.sh (92%) rename {scripts => cli/scripts}/stop.sh (100%) rename {setups => cli/server}/deploy.ts (97%) rename {setups => cli/server}/oracle.ts (98%) diff --git a/QUICKSTART.md b/QUICKSTART.md index 7bc28f8..f2748e8 100644 --- a/QUICKSTART.md +++ b/QUICKSTART.md @@ -61,12 +61,12 @@ bun test tests/nlaOracle.test.ts ```bash # Get Sepolia ETH from faucet first export DEPLOYER_PRIVATE_KEY=0x... -bun run setups/deploy.ts --network sepolia --rpc-url https://sepolia.infura.io/v3/YOUR-KEY +bun run cli/server/deploy.ts --network sepolia --rpc-url https://sepolia.infura.io/v3/YOUR-KEY # Start oracle export ORACLE_PRIVATE_KEY=0x... export OPENAI_API_KEY=sk-... -bun run setups/oracle.ts sepolia +bun run cli/server/oracle.ts sepolia ``` ## Need Help? diff --git a/cli/README.md b/cli/README.md index 02c7aaa..1f3e6a1 100644 --- a/cli/README.md +++ b/cli/README.md @@ -2,19 +2,72 @@ User-friendly command-line tools to interact with Natural Language Agreement escrows. +## Installation + +First, install the CLI globally: + +```bash +bun link +``` + +Now you can use `nla` from anywhere in your terminal! + +## Unified CLI + +The `nla` command provides a unified interface for all escrow operations: + +```bash +nla [options] +``` + +**Available Commands:** +- `dev` - Start local development environment (Anvil + Deploy + Oracle) +- `escrow:create` - Create a new escrow with natural language demand +- `escrow:fulfill` - Fulfill an existing escrow +- `escrow:collect` - Collect an approved escrow +- `escrow:status` - Check the status of an escrow +- `help` - Display help message + ## Quick Start +### Start Development Environment + +The easiest way to get started is with the `dev` command: + +```bash +nla dev +``` + +This will: +1. Start Anvil (local Ethereum node) +2. Deploy all contracts +3. Deploy mock ERC20 tokens and distribute them +4. Start the oracle listening for arbitration requests + +**Note:** Keep this terminal open - it runs the oracle. Open a new terminal for creating escrows. + +### Prerequisites + +Before running `nla dev`, set your OpenAI API key: + +```bash +export OPENAI_API_KEY=sk-your-key-here +``` + +For creating escrows, also set: + +```bash +export PRIVATE_KEY=0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 +``` + ### 1. Create an Escrow -Create an escrow with a natural language demand: - ```bash -bun run escrow:create \ +nla escrow:create \ --demand "The sky is blue" \ --amount 10 \ - --token \ - --oracle 0x70997970C51812dc3A010C7d01b50e0d17dc79C8 \ - --private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 + --token 0xa513e6e4b8f2a923d98304ec87f64353c4d5c853 \ + --oracle 0x70997970C51812dc3A010C7d01b50e0d17dc79C8 ``` > **Note:** Get the token address from the deployment output. The deployment creates 3 test tokens (TSTA, TSTB, TSTC) and distributes 10,000 of each to all test accounts. @@ -23,31 +76,42 @@ bun run escrow:create \ ### 2. Fulfill the Escrow -Someone else (or you with a different key) can fulfill it: +Switch to a different account: ```bash -bun run escrow:fulfill \ +export PRIVATE_KEY=0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d + +nla escrow:fulfill \ --escrow-uid 0x... \ --fulfillment "The sky appears blue today" \ - --oracle 0x70997970C51812dc3A010C7d01b50e0d17dc79C8 \ - --private-key 0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d + --oracle 0x70997970C51812dc3A010C7d01b50e0d17dc79C8 ``` **Save the Fulfillment UID** from the output! -### 3. Wait for Oracle +### 3. Check Status -The oracle will automatically arbitrate the fulfillment (a few seconds). +Monitor the escrow and arbitration progress: + +```bash +nla escrow:status \ + --escrow-uid 0x... \ + --deployment ./cli/deployments/localhost.json +``` + +This will show: +- Escrow details (demand, oracle, recipient) +- All fulfillments +- Arbitration decisions (approved/rejected) ### 4. Collect the Escrow If approved, collect the escrowed tokens: ```bash -bun run escrow:collect \ +nla escrow:collect \ --escrow-uid 0x... \ - --fulfillment-uid 0x... \ - --private-key 0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d + --fulfillment-uid 0x... ``` ## Commands @@ -55,15 +119,15 @@ bun run escrow:collect \ ### Create Escrow ```bash -bun run escrow:create [options] +nla escrow:create [options] Options: --demand Natural language demand (required) --amount Amount of tokens to escrow (required) --token
ERC20 token address (required) --oracle
Oracle address (required) - --private-key Your private key (required) - --deployment Deployment file (default: ./deployments/localhost.json) + --private-key Your private key (or set PRIVATE_KEY env var) + --deployment Deployment file (default: ./cli/deployments/localhost.json) --rpc-url RPC URL (default: from deployment) --help, -h Show help ``` @@ -71,14 +135,14 @@ Options: ### Fulfill Escrow ```bash -bun run escrow:fulfill [options] +nla escrow:fulfill [options] Options: --escrow-uid Escrow UID to fulfill (required) --fulfillment Your fulfillment text (required) --oracle
Oracle address (required) - --private-key Your private key (required) - --deployment Deployment file (default: ./deployments/localhost.json) + --private-key Your private key (or set PRIVATE_KEY env var) + --deployment Deployment file (default: ./cli/deployments/localhost.json) --rpc-url RPC URL (default: from deployment) --help, -h Show help ``` @@ -86,13 +150,25 @@ Options: ### Collect Escrow ```bash -bun run escrow:collect [options] +nla escrow:collect [options] Options: --escrow-uid Escrow UID (required) --fulfillment-uid Approved fulfillment UID (required) - --private-key Your private key (required) - --deployment Deployment file (default: ./deployments/localhost.json) + --private-key Your private key (or set PRIVATE_KEY env var) + --deployment Deployment file (default: ./cli/deployments/localhost.json) + --rpc-url RPC URL (default: from deployment) + --help, -h Show help +``` + +### Check Status + +```bash +nla escrow:status [options] + +Options: + --escrow-uid Escrow UID to check (required) + --deployment Deployment file (default: ./cli/deployments/localhost.json) --rpc-url RPC URL (default: from deployment) --help, -h Show help ``` @@ -127,37 +203,44 @@ export OPENAI_API_KEY=sk-... # Required for oracle ## Example Workflow ```bash -# Terminal 1: Start the system -bun run setup +# Terminal 1: Start the development environment +nla dev # Note the token addresses from the deployment output! -# Example: Token A (TSTA): 0x5FbDB... +# Example: Token A (TSTA): 0xa513e6e4b8f2a923d98304ec87f64353c4d5c853 -# Terminal 2: Create an escrow (as Alice) +# Terminal 2: Set your private key and create an escrow (as Alice) export PRIVATE_KEY=0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 -bun run escrow:create \ + +nla escrow:create \ --demand "Deliver package by Friday" \ --amount 100 \ - --token 0x5FbDB... \ # Use Token A address from deployment + --token 0xa513e6e4b8f2a923d98304ec87f64353c4d5c853 \ --oracle 0x70997970C51812dc3A010C7d01b50e0d17dc79C8 -# Save the escrow UID, e.g.: 0x1234... +# Save the escrow UID, e.g.: 0xd9e1402e96c2f7a64e60bf53a45445f7254e9b72389f6ede25181bff542d7b65 # Terminal 2: Fulfill the escrow (as Bob) export PRIVATE_KEY=0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d -bun run escrow:fulfill \ - --escrow-uid 0x1234... \ + +nla escrow:fulfill \ + --escrow-uid 0xd9e1402e96c2f7a64e60bf53a45445f7254e9b72389f6ede25181bff542d7b65 \ --fulfillment "Package delivered on Thursday" \ --oracle 0x70997970C51812dc3A010C7d01b50e0d17dc79C8 -# Save the fulfillment UID, e.g.: 0x5678... +# Save the fulfillment UID, e.g.: 0xd124b274d5fb87e3d63b38fd2f6158730b73b53166898aa692b15f5a44178809 # Watch Terminal 1 - oracle will arbitrate automatically! +# Terminal 2: Check the status +nla escrow:status \ + --escrow-uid 0xd9e1402e96c2f7a64e60bf53a45445f7254e9b72389f6ede25181bff542d7b65 \ + --deployment ./cli/deployments/localhost.json + # Terminal 2: Collect the escrow (as Bob) -bun run escrow:collect \ - --escrow-uid 0x1234... \ - --fulfillment-uid 0x5678... +nla escrow:collect \ + --escrow-uid 0xd9e1402e96c2f7a64e60bf53a45445f7254e9b72389f6ede25181bff542d7b65 \ + --fulfillment-uid 0xd124b274d5fb87e3d63b38fd2f6158730b73b53166898aa692b15f5a44178809 ``` ## Troubleshooting diff --git a/cli/collect-escrow.ts b/cli/client/collect-escrow.ts similarity index 96% rename from cli/collect-escrow.ts rename to cli/client/collect-escrow.ts index c03eb66..6e682ee 100644 --- a/cli/collect-escrow.ts +++ b/cli/client/collect-escrow.ts @@ -26,7 +26,7 @@ Options: --escrow-uid Escrow UID to collect (required) --fulfillment-uid Fulfillment UID that was approved (required) --private-key Your private key (required) - --deployment Path to deployment file (default: ./deployments/localhost.json) + --deployment Path to deployment file (default: ./cli/deployments/localhost.json) --rpc-url RPC URL (default: from deployment file) --help, -h Display this help message @@ -79,7 +79,7 @@ async function main() { const escrowUid = args["escrow-uid"]; const fulfillmentUid = args["fulfillment-uid"]; const privateKey = args["private-key"] || process.env.PRIVATE_KEY; - const deploymentPath = args.deployment || "./deployments/localhost.json"; + const deploymentPath = args.deployment || "./cli/deployments/localhost.json"; // Validate required parameters if (!escrowUid) { @@ -117,7 +117,7 @@ async function main() { console.log(` 🌐 RPC URL: ${rpcUrl}\n`); // Import alkahest client - const { makeClient } = await import("../../alkahest/sdks/ts/src/index.ts"); + const { makeClient } = await import("../../../alkahest/sdks/ts/src/index.ts"); // Create account and wallet const account = privateKeyToAccount(privateKey as `0x${string}`); diff --git a/cli/create-escrow.ts b/cli/client/create-escrow.ts similarity index 95% rename from cli/create-escrow.ts rename to cli/client/create-escrow.ts index 3a2dbd8..d760b8f 100644 --- a/cli/create-escrow.ts +++ b/cli/client/create-escrow.ts @@ -29,7 +29,7 @@ Options: --token
ERC20 token address (required) --oracle
Oracle address that will arbitrate (required) --private-key Your private key (required) - --deployment Path to deployment file (default: ./deployments/localhost.json) + --deployment Path to deployment file (default: ./cli/deployments/localhost.json) --rpc-url RPC URL (default: from deployment file) --help, -h Display this help message @@ -88,7 +88,7 @@ async function main() { const tokenAddress = args.token; const oracleAddress = args.oracle; const privateKey = args["private-key"] || process.env.PRIVATE_KEY; - const deploymentPath = args.deployment || "./deployments/localhost.json"; + const deploymentPath = args.deployment || "./cli/deployments/localhost.json"; // Validate required parameters if (!demand) { @@ -140,8 +140,8 @@ async function main() { console.log(` 🌐 RPC URL: ${rpcUrl}\n`); // Import alkahest client - const { makeClient } = await import("../../alkahest/sdks/ts/src/index.ts"); - const { makeLLMClient } = await import("../clients/nla.ts"); + const { makeClient } = await import("../../../alkahest/sdks/ts/src/index.ts"); + const { makeLLMClient } = await import("../../clients/nla.ts"); // Create account and wallet const account = privateKeyToAccount(privateKey as `0x${string}`); @@ -180,7 +180,7 @@ async function main() { }); // Check token balance - const MockERC20Permit = await import("../../alkahest/sdks/ts/tests/fixtures/MockERC20Permit.json"); + const MockERC20Permit = await import("../../../alkahest/sdks/ts/tests/fixtures/MockERC20Permit.json"); const tokenBalance = await walletClient.readContract({ address: tokenAddress as `0x${string}`, abi: MockERC20Permit.abi, diff --git a/cli/fulfill-escrow.ts b/cli/client/fulfill-escrow.ts similarity index 96% rename from cli/fulfill-escrow.ts rename to cli/client/fulfill-escrow.ts index 6e15ccc..32c040a 100644 --- a/cli/fulfill-escrow.ts +++ b/cli/client/fulfill-escrow.ts @@ -28,7 +28,7 @@ Options: --fulfillment Your fulfillment text (required) --oracle
Oracle address that will arbitrate (required) --private-key Your private key (required) - --deployment Path to deployment file (default: ./deployments/localhost.json) + --deployment Path to deployment file (default: ./cli/deployments/localhost.json) --rpc-url RPC URL (default: from deployment file) --help, -h Display this help message @@ -84,7 +84,7 @@ async function main() { const fulfillment = args.fulfillment; const oracleAddress = args.oracle; const privateKey = args["private-key"] || process.env.PRIVATE_KEY; - const deploymentPath = args.deployment || "./deployments/localhost.json"; + const deploymentPath = args.deployment || "./cli/deployments/localhost.json"; // Validate required parameters if (!escrowUid) { @@ -129,7 +129,7 @@ async function main() { console.log(` 🌐 RPC URL: ${rpcUrl}\n`); // Import alkahest client - const { makeClient } = await import("../../alkahest/sdks/ts/src/index.ts"); + const { makeClient } = await import("../../../alkahest/sdks/ts/src/index.ts"); // Create account and wallet const account = privateKeyToAccount(privateKey as `0x${string}`); diff --git a/deployments/localhost.json b/cli/deployments/localhost.json similarity index 94% rename from deployments/localhost.json rename to cli/deployments/localhost.json index 4c800ff..73d9362 100644 --- a/deployments/localhost.json +++ b/cli/deployments/localhost.json @@ -2,7 +2,7 @@ "network": "localhost", "chainId": 31337, "rpcUrl": "http://localhost:8545", - "deployedAt": "2025-12-12T05:50:31.992Z", + "deployedAt": "2025-12-12T09:19:50.255Z", "deployer": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", "addresses": { "easSchemaRegistry": "0x5fbdb2315678afecb367f032d93f642f64180aa3", diff --git a/cli/index.ts b/cli/index.ts new file mode 100755 index 0000000..f982508 --- /dev/null +++ b/cli/index.ts @@ -0,0 +1,315 @@ +#!/usr/bin/env bun +import { parseArgs } from "util"; + +// Helper function to display usage +function displayHelp() { + console.log(` +Natural Language Agreement CLI + +Usage: + nla [options] + +Commands: + dev Start local development environment (Anvil + Deploy + Oracle) + escrow:create Create a new escrow with natural language demand + escrow:fulfill Fulfill an existing escrow + escrow:collect Collect an approved escrow + escrow:status Check the status of an escrow + help Display this help message + +Options (vary by command): + --demand Natural language demand (create) + --amount Amount of tokens to escrow (create) + --token
ERC20 token contract address (create) + --oracle
Oracle address (create, fulfill) + --escrow-uid Escrow UID (fulfill, collect, status) + --fulfillment Fulfillment text (fulfill) + --fulfillment-uid Fulfillment UID (collect) + --private-key Private key (all commands) + --rpc-url RPC URL (default: http://localhost:8545) + --deployment Load addresses from deployment file + +Environment Variables: + PRIVATE_KEY Private key for transactions + RPC_URL RPC URL for blockchain network + OPENAI_API_KEY OpenAI API key (for create command) + +Examples: + # Start development environment + nla dev + + # Create an escrow + nla escrow:create \\ + --demand "The sky is blue" \\ + --amount 10 \\ + --token 0xa513E6E4b8f2a923D98304ec87F64353C4D5C853 \\ + --oracle 0x70997970C51812dc3A010C7d01b50e0d17dc79C8 + + # Fulfill an escrow + nla escrow:fulfill \\ + --escrow-uid 0x... \\ + --fulfillment "The sky appears blue today" \\ + --oracle 0x70997970C51812dc3A010C7d01b50e0d17dc79C8 + + # Collect an escrow + nla escrow:collect \\ + --escrow-uid 0x... \\ + --fulfillment-uid 0x... + + # Check escrow status + nla escrow:status --escrow-uid 0x... +`); +} + +// Parse command line arguments +function parseCliArgs() { + const args = Bun.argv.slice(2); + + if (args.length === 0) { + displayHelp(); + process.exit(0); + } + + const command = args[0]; + + if (command === "help" || command === "--help" || command === "-h") { + displayHelp(); + process.exit(0); + } + + const { values } = parseArgs({ + args: args.slice(1), + options: { + "demand": { type: "string" }, + "amount": { type: "string" }, + "token": { type: "string" }, + "oracle": { type: "string" }, + "escrow-uid": { type: "string" }, + "fulfillment": { type: "string" }, + "fulfillment-uid": { type: "string" }, + "private-key": { type: "string" }, + "rpc-url": { type: "string" }, + "deployment": { type: "string" }, + }, + strict: true, + }); + + return { command, ...values }; +} + +// Main function +async function main() { + try { + const args = parseCliArgs(); + const command = args.command; + + // Handle dev command separately (runs shell script) + if (command === "dev") { + await runDevCommand(); + return; + } + + // Get the script path based on command + let scriptPath: string; + + switch (command) { + case "escrow:create": + scriptPath = "./client/create-escrow.ts"; + break; + case "escrow:fulfill": + scriptPath = "./client/fulfill-escrow.ts"; + break; + case "escrow:collect": + scriptPath = "./client/collect-escrow.ts"; + break; + case "escrow:status": + await runStatusCommand(args); + return; + default: + console.error(`āŒ Unknown command: ${command}`); + console.error("Run 'nla help' for usage information."); + process.exit(1); + } + + // Run the command as a subprocess with the args (excluding the command name) + const { spawnSync } = await import("child_process"); + const scriptDir = import.meta.dir; + const fullScriptPath = `${scriptDir}/${scriptPath}`; + + // Build args array without the command name + const commandArgs = Bun.argv.slice(3); // Skip bun, script, and command + + const result = spawnSync("bun", ["run", fullScriptPath, ...commandArgs], { + stdio: "inherit", + cwd: process.cwd(), + }); + + process.exit(result.status || 0); + + } catch (error) { + console.error("āŒ Error:", error); + process.exit(1); + } +} + +// Dev command handler +async function runDevCommand() { + console.log("šŸš€ Starting development environment...\n"); + + const { spawnSync } = await import("child_process"); + const scriptDir = import.meta.dir; + const devScriptPath = `${scriptDir}/scripts/dev.sh`; + + // Run the dev.sh script + const result = spawnSync(devScriptPath, [], { + stdio: "inherit", + cwd: process.cwd(), + shell: true, + }); + + process.exit(result.status || 0); +} + +// 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), + }); + + // Load EAS ABI + const { default: EAS } = await import("../../alkahest/contracts/out/EAS.sol/EAS.json"); + + 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: EAS.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: EAS.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: EAS.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: EAS.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/scripts/deploy.sh b/cli/scripts/deploy.sh similarity index 84% rename from scripts/deploy.sh rename to cli/scripts/deploy.sh index ddb5081..7441206 100755 --- a/scripts/deploy.sh +++ b/cli/scripts/deploy.sh @@ -42,22 +42,22 @@ echo -e "${GREEN}āœ… Contract artifacts found${NC}\n" # Run deployment echo -e "${BLUE}šŸ“ Deploying to ${NETWORK}...${NC}" -bun run setups/deploy.ts --network "$NETWORK" --rpc-url "$RPC_URL" +bun run cli/server/deploy.ts --network "$NETWORK" --rpc-url "$RPC_URL" # Check if deployment was successful if [ $? -eq 0 ]; then echo -e "\n${GREEN}✨ Deployment complete!${NC}" - echo -e "${BLUE}Deployment file saved to: deployments/${NETWORK}.json${NC}\n" + echo -e "${BLUE}Deployment file saved to: cli/deployments/${NETWORK}.json${NC}\n" echo -e "${YELLOW}Next steps:${NC}" echo "1. Start the oracle with:" - echo " ./scripts/start-oracle.sh $NETWORK" + echo " ./cli/scripts/start-oracle.sh $NETWORK" echo "" echo "2. Or using npm script:" echo " bun run oracle" echo "" echo "3. Or manually:" - echo " bun run setups/oracle.ts --deployment ./deployments/${NETWORK}.json" + echo " bun run cli/server/oracle.ts --deployment ./cli/deployments/${NETWORK}.json" else echo -e "\n${RED}āŒ Deployment failed${NC}" exit 1 diff --git a/scripts/dev.sh b/cli/scripts/dev.sh similarity index 97% rename from scripts/dev.sh rename to cli/scripts/dev.sh index ecca82c..6bbbf2c 100755 --- a/scripts/dev.sh +++ b/cli/scripts/dev.sh @@ -103,12 +103,12 @@ fi # Deploy contracts echo -e "\n${BLUE}šŸ“ Deploying contracts...${NC}\n" export DEPLOYER_PRIVATE_KEY="0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80" -./scripts/deploy.sh localhost http://localhost:8545 +./cli/scripts/deploy.sh localhost http://localhost:8545 # Start oracle echo -e "\n${BLUE}šŸš€ Starting oracle...${NC}\n" export ORACLE_PRIVATE_KEY="0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d" -./scripts/start-oracle.sh localhost +./cli/scripts/start-oracle.sh localhost # Cleanup function cleanup() { diff --git a/scripts/start-oracle.sh b/cli/scripts/start-oracle.sh similarity index 92% rename from scripts/start-oracle.sh rename to cli/scripts/start-oracle.sh index 71a804f..a39c1b2 100755 --- a/scripts/start-oracle.sh +++ b/cli/scripts/start-oracle.sh @@ -19,7 +19,7 @@ fi # Default network NETWORK=${1:-localhost} -DEPLOYMENT_FILE="./deployments/${NETWORK}.json" +DEPLOYMENT_FILE="./cli/deployments/${NETWORK}.json" echo -e "${BLUE}šŸš€ Starting Natural Language Agreement Oracle${NC}\n" @@ -27,7 +27,7 @@ echo -e "${BLUE}šŸš€ Starting Natural Language Agreement Oracle${NC}\n" if [ ! -f "$DEPLOYMENT_FILE" ]; then echo -e "${RED}āŒ Error: Deployment file not found: $DEPLOYMENT_FILE${NC}" echo "Please deploy contracts first:" - echo " ./scripts/deploy.sh $NETWORK" + echo " ./cli/scripts/deploy.sh $NETWORK" exit 1 fi @@ -54,7 +54,7 @@ echo "" # Start oracle echo -e "${BLUE}šŸ‘‚ Starting oracle (Press Ctrl+C to stop)...${NC}\n" -bun run setups/oracle.ts \ +bun run cli/server/oracle.ts \ --deployment "$DEPLOYMENT_FILE" \ --openai-api-key "$OPENAI_API_KEY" \ --private-key "$ORACLE_PRIVATE_KEY" diff --git a/scripts/stop.sh b/cli/scripts/stop.sh similarity index 100% rename from scripts/stop.sh rename to cli/scripts/stop.sh diff --git a/setups/deploy.ts b/cli/server/deploy.ts similarity index 97% rename from setups/deploy.ts rename to cli/server/deploy.ts index 85ad880..8c83118 100644 --- a/setups/deploy.ts +++ b/cli/server/deploy.ts @@ -25,7 +25,7 @@ Options: --network Network to deploy to: mainnet, sepolia, localhost (required) --rpc-url Custom RPC URL (overrides network default) --private-key Deployer's private key (required) - --output Output file for deployment addresses (default: ./deployments/.json) + --output Output file for deployment addresses (default: ./cli/deployments/.json) --help, -h Display this help message Environment Variables (alternative to CLI options): @@ -152,7 +152,7 @@ async function main() { console.log("šŸ“¦ Loading contract artifacts...\n"); // This requires alkahest to be properly set up - const alkahestPath = "../../alkahest/sdks/ts"; + const alkahestPath = "../../../alkahest/sdks/ts"; const contractsPath = `${alkahestPath}/src/contracts`; // Import necessary artifacts @@ -329,7 +329,10 @@ async function main() { } // Save deployment addresses - const outputPath = args.output || resolve(`./deployments/${network}.json`); + // Get the script directory and go up to project root, then into cli/deployments + const scriptDir = import.meta.dir; + const projectRoot = resolve(scriptDir, "../.."); + const outputPath = args.output || resolve(projectRoot, `cli/deployments/${network}.json`); const outputDir = resolve(outputPath, ".."); if (!existsSync(outputDir)) { diff --git a/setups/oracle.ts b/cli/server/oracle.ts similarity index 98% rename from setups/oracle.ts rename to cli/server/oracle.ts index 5f65001..ad2c90c 100644 --- a/setups/oracle.ts +++ b/cli/server/oracle.ts @@ -1,7 +1,7 @@ #!/usr/bin/env bun import { parseArgs } from "util"; import { parseAbiParameters } from "viem"; -import { makeLLMClient } from "../clients/nla"; +import { makeLLMClient } from "../../clients/nla"; import { existsSync, readFileSync } from "fs"; import { resolve } from "path"; @@ -127,7 +127,7 @@ async function main() { } // Import alkahest client - const { makeClient } = await import("../../alkahest/sdks/ts/src/index.ts"); + const { makeClient } = await import("../../../alkahest/sdks/ts/src/index.ts"); const { createWalletClient, http, publicActions } = await import("viem"); const { privateKeyToAccount } = await import("viem/accounts"); const { foundry } = await import("viem/chains"); diff --git a/package.json b/package.json index fda4fff..f760a1d 100644 --- a/package.json +++ b/package.json @@ -5,18 +5,21 @@ "private": true, "devDependencies": { "@types/bun": "latest" + }, + "bin": { + "nla": "cli/index.ts" }, "scripts": { "dev": "bun run index.ts", "start": "bun run index.ts", "test": "bun test ./tests --exclude alkahest-ts/** ", - "setup": "./scripts/dev.sh", - "deploy": "./scripts/deploy.sh", - "oracle": "./scripts/start-oracle.sh", - "stop": "./scripts/stop.sh", - "escrow:create": "bun run cli/create-escrow.ts", - "escrow:fulfill": "bun run cli/fulfill-escrow.ts", - "escrow:collect": "bun run cli/collect-escrow.ts" + "setup": "./cli/scripts/dev.sh", + "deploy": "./cli/scripts/deploy.sh", + "oracle": "./cli/scripts/start-oracle.sh", + "stop": "./cli/scripts/stop.sh", + "escrow:create": "bun run cli/client/create-escrow.ts", + "escrow:fulfill": "bun run cli/client/fulfill-escrow.ts", + "escrow:collect": "bun run cli/client/collect-escrow.ts" }, "peerDependencies": { "typescript": "^5.9.3"