diff --git a/.gitignore b/.gitignore index cb5c0c1..f9f74af 100644 --- a/.gitignore +++ b/.gitignore @@ -41,4 +41,4 @@ anvil.log .env # Local deployment files -cli/deployments/devnet.json \ No newline at end of file +cli/deployments/anvil.json \ No newline at end of file diff --git a/README.md b/README.md index 2f166bb..94dffd8 100644 --- a/README.md +++ b/README.md @@ -136,7 +136,7 @@ nla stop | `nla switch ` | Switch between environments | | `nla network` | Show current environment | -Available environments: `devnet`, `sepolia`, `base-sepolia`, `mainnet` +Available environments: `anvil`, `sepolia`, `base-sepolia`, `mainnet` ### Global Options @@ -445,7 +445,7 @@ nla network nla switch mainnet # Back to local development -nla switch devnet +nla switch anvil ``` ## Troubleshooting @@ -512,7 +512,7 @@ natural-language-agreements/ │ │ ├── switch.ts # Switch environments │ │ └── wallet.ts # Wallet management │ └── deployments/ # Deployment addresses (generated) -│ ├── devnet.json +│ ├── anvil.json │ ├── sepolia.json │ ├── base-sepolia.json │ └── mainnet.json diff --git a/bun.lock b/bun.lock index 684ca24..5beb0f2 100644 --- a/bun.lock +++ b/bun.lock @@ -3,7 +3,7 @@ "configVersion": 1, "workspaces": { "": { - "name": "natural-language-agreement-extension", + "name": "nla", "dependencies": { "@ai-sdk/anthropic": "^3.0.2", "@ai-sdk/openai": "^3.0.2", @@ -11,7 +11,7 @@ "@perplexity-ai/ai-sdk": "^0.1.2", "@viem/anvil": "^0.0.10", "ai": "^6.0.5", - "alkahest-ts": "^0.6.1", + "alkahest-ts": "0.7.2", "arktype": "^2.1.23", "viem": "^2.42.1", "zod": "^3.25.76", @@ -29,15 +29,15 @@ "packages": { "@adraffy/ens-normalize": ["@adraffy/ens-normalize@1.11.1", "", {}, "sha512-nhCBV3quEgesuf7c7KYfperqSS14T8bYuvJ8PcLJp6znkZpFc0AuW4qBtr8eKVyPPe/8RSr7sglCWPU5eaxwKQ=="], - "@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/anthropic": ["@ai-sdk/anthropic@3.0.42", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.14" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-snoLXB9DmvAmmngbPN/Io8IGzZ9zWpC208EgIIztYf1e1JhwuMkgKCYkL30vGhSen4PrBafu2+sO4G/17wu45A=="], - "@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/gateway": ["@ai-sdk/gateway@3.0.42", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.14", "@vercel/oidc": "3.1.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-Il9lZWPUQMX59H5yJvA08gxfL2Py8oHwvAYRnK0Mt91S+JgPcyk/yEmXNDZG9ghJrwSawtK5Yocy8OnzsTOGsw=="], - "@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/openai": ["@ai-sdk/openai@3.0.27", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.14" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-pLMxWOypwroXiK9dxNpn60/HGhWWWDEOJ3lo9vZLoxvpJNtKnLKojwVIvlW3yEjlD7ll1+jUO2uzsABNTaP5Yg=="], - "@ai-sdk/provider": ["@ai-sdk/provider@3.0.1", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-2lR4w7mr9XrydzxBSjir4N6YMGdXD+Np1Sh0RXABh7tWdNFFwIeRI1Q+SaYZMbfL8Pg8RRLcrxQm51yxTLhokg=="], + "@ai-sdk/provider": ["@ai-sdk/provider@3.0.8", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-oGMAgGoQdBXbZqNG0Ze56CHjDZ1IDYOwGYxYjO5KLSlz5HiNQ9udIXsPZ61VWaHGZ5XW/jyjmr6t2xz2jGVwbQ=="], - "@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=="], + "@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@4.0.14", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@standard-schema/spec": "^1.1.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-7bzKd9lgiDeXM7O4U4nQ8iTxguAOkg8LZGD9AfDVZYjO5cKYRwBPwVjboFcVrxncRHu0tYxZtXZtiLKpG4pEng=="], "@ark/schema": ["@ark/schema@0.56.0", "", { "dependencies": { "@ark/util": "0.56.0" } }, "sha512-ECg3hox/6Z/nLajxXqNhgPtNdHWC9zNsDyskwO28WinoFEnWow4IsERNz9AnXRhTZJnYIlAJ4uGn3nlLk65vZA=="], @@ -65,25 +65,25 @@ "@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=="], + "@types/bun": ["@types/bun@1.3.9", "", { "dependencies": { "bun-types": "1.3.9" } }, "sha512-KQ571yULOdWJiMH+RIWIOZ7B2RXQGpL1YQrBtLIV3FqDcCu6FsbFUBwhdKUlCKUpS3PJDsHlJ1QKlpxoVR+xtw=="], - "@types/node": ["@types/node@20.19.30", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-WJtwWJu7UdlvzEAUm484QNg5eAoq5QR08KDNx7g45Usrs2NtOPiX8ugDqmKdXkyL03rBqU5dYNYVQetEpBHq2g=="], + "@types/node": ["@types/node@20.19.33", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-Rs1bVAIdBs5gbTIKza/tgpMuG1k3U/UMJLWecIMxNdJFDMzcM5LOiLVRYh3PilWEYDIeUDv7bpiHPLPsbydGcw=="], - "@vercel/oidc": ["@vercel/oidc@3.0.5", "", {}, "sha512-fnYhv671l+eTTp48gB4zEsTW/YtRgRPnkI2nT7x6qw5rkI1Lq2hTmQIpHPgyThI0znLK+vX2n9XxKdXZ7BUbbw=="], + "@vercel/oidc": ["@vercel/oidc@3.1.0", "", {}, "sha512-Fw28YZpRnA3cAHHDlkt7xQHiJ0fcL+NRcIqsocZQUSmbzeIKRpwttJjik5ZGanXP+vlA4SbTg+AbA3bP363l+w=="], "@viem/anvil": ["@viem/anvil@0.0.10", "", { "dependencies": { "execa": "^7.1.1", "get-port": "^6.1.2", "http-proxy": "^1.18.1", "ws": "^8.13.0" } }, "sha512-9PzYXBRikfSUhhm8Bd0avv07agwcbMJ5FaSu2D2vbE0cxkvXGtolL3fW5nz2yefMqOqVQL4XzfM5nwY81x3ytw=="], - "abitype": ["abitype@1.1.0", "", { "peerDependencies": { "typescript": ">=5.0.4", "zod": "^3.22.0 || ^4.0.0" }, "optionalPeers": ["typescript", "zod"] }, "sha512-6Vh4HcRxNMLA0puzPjM5GBgT4aAcFGKZzSgAXvuZ27shJP6NEpielTuqbBmZILR5/xd0PizkBGy5hReKz9jl5A=="], + "abitype": ["abitype@1.2.3", "", { "peerDependencies": { "typescript": ">=5.0.4", "zod": "^3.22.0 || ^4.0.0" }, "optionalPeers": ["typescript", "zod"] }, "sha512-Ofer5QUnuUdTFsBRwARMoWKOH1ND5ehwYhJ3OJ/BQO+StkwQjHw0XyVh4vDttzHB7QOFhPHa/o413PJ82gU/Tg=="], - "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=="], + "ai": ["ai@6.0.82", "", { "dependencies": { "@ai-sdk/gateway": "3.0.42", "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.14", "@opentelemetry/api": "1.9.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-WLml1ab2IXtREgkxrq2Pl6lFO6NKgC17MqTzmK5mO1UO6tMAJiVjkednw9p0j4+/LaUIZQoRiIT8wA37LswZ9Q=="], - "alkahest-ts": ["alkahest-ts@0.6.1", "", { "dependencies": { "@viem/anvil": "^0.0.10", "arktype": "^2.1.23", "zod": "^3.25.76" }, "peerDependencies": { "typescript": "^5.9.3", "viem": "^2.38.3" } }, "sha512-0u1xUM9OLca6emKDVzn6ISx4VFg7TGZtA/7hnT3SOHOWVPLk5ruX12tbX5MO7hG1jxVHAO3wgIE2t7vl864/nQ=="], + "alkahest-ts": ["alkahest-ts@0.7.2", "", { "dependencies": { "@viem/anvil": "^0.0.10", "arktype": "^2.1.23", "zod": "^3.25.76" }, "peerDependencies": { "typescript": "^5.9.3", "viem": "^2.38.3" } }, "sha512-TWk1JftpPAQDSfTymzKxkeiew2/VxIMvbsgLkPm3ECsloxZe4FNUIAXOKQH3PEqIHeKboBdtBN5TrsL0WhbooQ=="], "arkregex": ["arkregex@0.0.5", "", { "dependencies": { "@ark/util": "0.56.0" } }, "sha512-ncYjBdLlh5/QnVsAA8De16Tc9EqmYM7y/WU9j+236KcyYNUXogpz3sC4ATIZYzzLxwI+0sEOaQLEmLmRleaEXw=="], "arktype": ["arktype@2.1.29", "", { "dependencies": { "@ark/schema": "0.56.0", "@ark/util": "0.56.0", "arkregex": "0.0.5" } }, "sha512-jyfKk4xIOzvYNayqnD8ZJQqOwcrTOUbIU4293yrzAjA3O1dWh61j71ArMQ6tS/u4pD7vabSPe7nG3RCyoXW6RQ=="], - "bun-types": ["bun-types@1.3.4", "", { "dependencies": { "@types/node": "*" } }, "sha512-5ua817+BZPZOlNaRgGBpZJOSAQ9RQ17pkwPD0yR7CfJg+r8DgIILByFifDTa+IPDDxzf5VNhtNlcKqFzDgJvlQ=="], + "bun-types": ["bun-types@1.3.9", "", { "dependencies": { "@types/node": "*" } }, "sha512-+UBWWOakIP4Tswh0Bt0QD0alpTY8cb5hvgiYeWCMet9YukHbzuruIEeXC2D7nMJPB12kbh8C7XJykSexEqGKJg=="], "cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="], @@ -119,7 +119,7 @@ "onetime": ["onetime@6.0.0", "", { "dependencies": { "mimic-fn": "^4.0.0" } }, "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ=="], - "ox": ["ox@0.9.6", "", { "dependencies": { "@adraffy/ens-normalize": "^1.11.0", "@noble/ciphers": "^1.3.0", "@noble/curves": "1.9.1", "@noble/hashes": "^1.8.0", "@scure/bip32": "^1.7.0", "@scure/bip39": "^1.6.0", "abitype": "^1.0.9", "eventemitter3": "5.0.1" }, "peerDependencies": { "typescript": ">=5.4.0" }, "optionalPeers": ["typescript"] }, "sha512-8SuCbHPvv2eZLYXrNmC0EC12rdzXQLdhnOMlHDW2wiCPLxBrOOJwX5L5E61by+UjTPOryqQiRSnjIKCI+GykKg=="], + "ox": ["ox@0.12.1", "", { "dependencies": { "@adraffy/ens-normalize": "^1.11.0", "@noble/ciphers": "^1.3.0", "@noble/curves": "1.9.1", "@noble/hashes": "^1.8.0", "@scure/bip32": "^1.7.0", "@scure/bip39": "^1.6.0", "abitype": "^1.2.3", "eventemitter3": "5.0.1" }, "peerDependencies": { "typescript": ">=5.4.0" }, "optionalPeers": ["typescript"] }, "sha512-uU0llpthaaw4UJoXlseCyBHmQ3bLrQmz9rRLIAUHqv46uHuae9SE+ukYBRIPVCnlEnHKuWjDUcDFHWx9gbGNoA=="], "path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="], @@ -137,20 +137,18 @@ "undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], - "viem": ["viem@2.42.1", "", { "dependencies": { "@noble/curves": "1.9.1", "@noble/hashes": "1.8.0", "@scure/bip32": "1.7.0", "@scure/bip39": "1.6.0", "abitype": "1.1.0", "isows": "1.0.7", "ox": "0.9.6", "ws": "8.18.3" }, "peerDependencies": { "typescript": ">=5.0.4" }, "optionalPeers": ["typescript"] }, "sha512-NzT/f54jT+b0Um6pYzN/uAGMLg+3twhricAzXS+XH8pVIREzPEh7P25rlhPQnLYiPWzQd9mrFcvnm73Sc8bx+A=="], + "viem": ["viem@2.45.3", "", { "dependencies": { "@noble/curves": "1.9.1", "@noble/hashes": "1.8.0", "@scure/bip32": "1.7.0", "@scure/bip39": "1.6.0", "abitype": "1.2.3", "isows": "1.0.7", "ox": "0.12.1", "ws": "8.18.3" }, "peerDependencies": { "typescript": ">=5.0.4" }, "optionalPeers": ["typescript"] }, "sha512-axOD7rIbGiDHHA1MHKmpqqTz3CMCw7YpE/FVypddQMXL5i364VkNZh9JeEJH17NO484LaZUOMueo35IXyL76Mw=="], "which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="], - "ws": ["ws@8.18.3", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg=="], + "ws": ["ws@8.19.0", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg=="], "zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="], - "bun-types/@types/node": ["@types/node@25.0.2", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-gWEkeiyYE4vqjON/+Obqcoeffmk0NF15WSBwSs7zwVA2bAbTaE0SJ7P0WNGoJn8uE7fiaV5a7dKYIJriEqOrmA=="], - "npm-run-path/path-key": ["path-key@4.0.0", "", {}, "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ=="], "ox/eventemitter3": ["eventemitter3@5.0.1", "", {}, "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA=="], - "bun-types/@types/node/undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="], + "viem/ws": ["ws@8.18.3", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg=="], } } diff --git a/cli/README.md b/cli/README.md index 5f2fcaf..fe52ab3 100644 --- a/cli/README.md +++ b/cli/README.md @@ -99,7 +99,7 @@ Monitor the escrow and arbitration progress: ```bash nla escrow:status \ --escrow-uid 0x... \ - --deployment ./cli/deployments/devnet.json + --deployment ./cli/deployments/anvil.json ``` This will show: @@ -185,7 +185,7 @@ Options: --token
ERC20 token address (required) --oracle
Oracle address (required) --private-key Your private key (or set PRIVATE_KEY env var) - --deployment Deployment file (default: ./cli/deployments/devnet.json) + --deployment Deployment file (default: ./cli/deployments/anvil.json) --rpc-url RPC URL (default: from deployment) --help, -h Show help ``` @@ -200,7 +200,7 @@ Options: --fulfillment Your fulfillment text (required) --oracle
Oracle address (required) --private-key Your private key (or set PRIVATE_KEY env var) - --deployment Deployment file (default: ./cli/deployments/devnet.json) + --deployment Deployment file (default: ./cli/deployments/anvil.json) --rpc-url RPC URL (default: from deployment) --help, -h Show help ``` @@ -214,7 +214,7 @@ Options: --escrow-uid Escrow UID (required) --fulfillment-uid Approved fulfillment UID (required) --private-key Your private key (or set PRIVATE_KEY env var) - --deployment Deployment file (default: ./cli/deployments/devnet.json) + --deployment Deployment file (default: ./cli/deployments/anvil.json) --rpc-url RPC URL (default: from deployment) --help, -h Show help ``` @@ -226,7 +226,7 @@ nla escrow:status [options] Options: --escrow-uid Escrow UID to check (required) - --deployment Deployment file (default: ./cli/deployments/devnet.json) + --deployment Deployment file (default: ./cli/deployments/anvil.json) --rpc-url RPC URL (default: from deployment) --help, -h Show help ``` @@ -293,7 +293,7 @@ nla escrow:fulfill \ # Terminal 2: Check the status nla escrow:status \ --escrow-uid 0xd9e1402e96c2f7a64e60bf53a45445f7254e9b72389f6ede25181bff542d7b65 \ - --deployment ./cli/deployments/devnet.json + --deployment ./cli/deployments/anvil.json # Terminal 2: Collect the escrow (as Bob) nla escrow:collect \ diff --git a/cli/client/collect-escrow.ts b/cli/client/collect-escrow.ts index 215c83c..bef0283 100644 --- a/cli/client/collect-escrow.ts +++ b/cli/client/collect-escrow.ts @@ -32,7 +32,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: ./cli/deployments/devnet.json) + --deployment Path to deployment file (default: ./cli/deployments/anvil.json) --rpc-url RPC URL (default: from deployment file) --help, -h Display this help message diff --git a/cli/client/fulfill-escrow.ts b/cli/client/fulfill-escrow.ts index 93a1968..dc62cb3 100644 --- a/cli/client/fulfill-escrow.ts +++ b/cli/client/fulfill-escrow.ts @@ -7,7 +7,7 @@ */ import { parseArgs } from "util"; -import { createWalletClient, http, publicActions, formatEther } from "viem"; +import { createWalletClient, http, publicActions, formatEther, toHex, keccak256 } from "viem"; import { privateKeyToAccount } from "viem/accounts"; import { existsSync, readFileSync } from "fs"; import { resolve, dirname, join } from "path"; @@ -35,7 +35,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: ./cli/deployments/devnet.json) + --deployment Path to deployment file (default: ./cli/deployments/anvil.json) --rpc-url RPC URL (default: from deployment file) --help, -h Display this help message @@ -170,11 +170,40 @@ async function main() { console.log("📋 Creating fulfillment obligation...\n"); - // Create the fulfillment - const { attested: fulfillmentAttestation } = await client.stringObligation.doObligation( - fulfillment, + // Create the fulfillment using CommitRevealObligation (commit-reveal flow) + const schema = keccak256(toHex("{item:string}")); + const salt = keccak256(toHex(crypto.randomUUID())); + const payload = toHex(fulfillment); + const obligationData = { payload, salt, schema }; + + // Step 1: Compute and submit commitment + console.log("🔒 Computing commitment..."); + const commitment = await client.commitReveal.computeCommitment( + escrowUid as `0x${string}`, + account.address, + obligationData, + ); + console.log(` Commitment: ${commitment}`); + + console.log("📝 Submitting commitment (with bond)..."); + const { hash: commitHash } = await client.commitReveal.commit(commitment); + console.log(` Commit tx: ${commitHash}`); + + // Step 2: Wait for next block + console.log("⏳ Waiting for next block..."); + await walletClient.waitForTransactionReceipt({ hash: commitHash }); + + // Step 3: Reveal - create the obligation + console.log("🔓 Revealing obligation..."); + const { attested: fulfillmentAttestation } = await client.commitReveal.doObligation( + obligationData, escrowUid as `0x${string}`, ); + + // Step 4: Reclaim bond + console.log("💰 Reclaiming bond..."); + await client.commitReveal.reclaimBond(fulfillmentAttestation.uid); + console.log("✅ Fulfillment created!\n"); console.log("📋 Fulfillment Details:"); console.log(` UID: ${fulfillmentAttestation.uid}`); diff --git a/cli/commands/dev.ts b/cli/commands/dev.ts index 87a6744..2a142c2 100644 --- a/cli/commands/dev.ts +++ b/cli/commands/dev.ts @@ -72,14 +72,14 @@ export async function runDevCommand(cliDir: string, envPath?: string, cliPrivate console.log(`${colors.blue} Natural Language Agreement Oracle - Quick Setup${colors.reset}`); console.log(`${colors.blue}════════════════════════════════════════════════════════${colors.reset}\n`); - // Auto-switch to devnet environment + // Auto-switch to anvil environment const currentEnv = getCurrentEnvironment(); - if (currentEnv !== 'devnet') { - console.log(`${colors.yellow}🔄 Switching environment from ${currentEnv} to devnet...${colors.reset}`); - setCurrentEnvironment('devnet'); - console.log(`${colors.green}✅ Switched to devnet${colors.reset}\n`); + if (currentEnv !== 'anvil') { + console.log(`${colors.yellow}🔄 Switching environment from ${currentEnv} to anvil...${colors.reset}`); + setCurrentEnvironment('anvil'); + console.log(`${colors.green}✅ Switched to anvil${colors.reset}\n`); } else { - console.log(`${colors.green}✅ Already on devnet environment${colors.reset}\n`); + console.log(`${colors.green}✅ Already on anvil environment${colors.reset}\n`); } // Load .env file first @@ -188,8 +188,8 @@ export async function runDevCommand(cliDir: string, envPath?: string, cliPrivate const oracleScript = join(cliDir, 'server', 'oracle.js'); // Look for deployment file in source directory (for local dev) or dist directory (for installed package) - const sourcePath = join(process.cwd(), 'cli', 'deployments', 'devnet.json'); - const distPath = join(cliDir, 'deployments', 'devnet.json'); + const sourcePath = join(process.cwd(), 'cli', 'deployments', 'anvil.json'); + const distPath = join(cliDir, 'deployments', 'anvil.json'); const deploymentFile = existsSync(sourcePath) ? sourcePath : distPath; const oracleArgs = ['run', oracleScript, '--deployment', deploymentFile]; diff --git a/cli/commands/switch.ts b/cli/commands/switch.ts index c5755d5..d0d2119 100644 --- a/cli/commands/switch.ts +++ b/cli/commands/switch.ts @@ -33,7 +33,7 @@ export function runSwitchCommand(env?: string) { const current = getCurrentEnvironment(); console.log(`${colors.blue}Current environment:${colors.reset} ${colors.green}${current}${colors.reset}\n`); console.log('Available environments:'); - console.log(' • devnet (local Anvil blockchain)'); + console.log(' • anvil (local Anvil blockchain)'); console.log(' • sepolia (Ethereum Sepolia testnet)'); console.log(' • base-sepolia (Base Sepolia testnet)'); console.log(' • mainnet (Ethereum mainnet)\n'); @@ -43,10 +43,10 @@ export function runSwitchCommand(env?: string) { } // Validate environment - const validEnvs = ['devnet', 'sepolia', 'base-sepolia', 'mainnet']; + const validEnvs = ['anvil', 'sepolia', 'base-sepolia', 'mainnet']; if (!validEnvs.includes(env)) { console.error(`${colors.red}❌ Invalid environment: ${env}${colors.reset}`); - console.log('Valid environments: devnet, sepolia, base-sepolia, mainnet\n'); + console.log('Valid environments: anvil, sepolia, base-sepolia, mainnet\n'); process.exit(1); } @@ -62,7 +62,7 @@ export function runSwitchCommand(env?: string) { console.log(`${colors.green}✅ Switched to ${env}${colors.reset}\n`); // Show info about the environment - if (env === 'devnet') { + if (env === 'anvil') { console.log('📝 Using local Anvil blockchain (http://localhost:8545)'); console.log(' Run "nla dev" to start the development environment\n'); } else if (env === 'sepolia') { diff --git a/cli/deployments/base-sepolia.json b/cli/deployments/base-sepolia.json index b7346d1..029db17 100644 --- a/cli/deployments/base-sepolia.json +++ b/cli/deployments/base-sepolia.json @@ -20,7 +20,7 @@ "attestationEscrowObligation": "", "attestationEscrowObligation2": "", "attestationBarterUtils": "", - "stringObligation": "", + "commitRevealObligation": "", "trivialArbiter": "", "trustedOracleArbiter": "", "anyArbiter": "", diff --git a/cli/deployments/sepolia.json b/cli/deployments/sepolia.json index 1a0a6a8..df4c006 100644 --- a/cli/deployments/sepolia.json +++ b/cli/deployments/sepolia.json @@ -20,7 +20,7 @@ "attestationEscrowObligation": "", "attestationEscrowObligation2": "", "attestationBarterUtils": "", - "stringObligation": "", + "commitRevealObligation": "", "trivialArbiter": "", "trustedOracleArbiter": "", "anyArbiter": "", diff --git a/cli/index.ts b/cli/index.ts index c7b0755..bee2ae7 100755 --- a/cli/index.ts +++ b/cli/index.ts @@ -30,7 +30,7 @@ Commands: deploy Deploy contracts to blockchain start-oracle Start the oracle service stop Stop all services (Anvil + Oracle) - switch [env] Switch between environments (devnet, sepolia, base-sepolia, mainnet) + switch [env] Switch between environments (anvil, sepolia, base-sepolia, mainnet) network Show current network/environment wallet:set Set wallet private key wallet:show Show current wallet address diff --git a/cli/server/deploy.ts b/cli/server/deploy.ts index 5cc57bb..d311198 100644 --- a/cli/server/deploy.ts +++ b/cli/server/deploy.ts @@ -164,7 +164,7 @@ async function main() { const SchemaRegistry = fixtures.SchemaRegistry; const MockERC20Permit = fixtures.MockERC20Permit; const TrustedOracleArbiter = contracts.TrustedOracleArbiter; - const StringObligation = contracts.StringObligation; + const CommitRevealObligation = contracts.CommitRevealObligation; const ERC20EscrowObligation = contracts.ERC20EscrowObligation; const ERC20PaymentObligation = contracts.ERC20PaymentObligation; const ERC20BarterUtils = contracts.ERC20BarterUtils; @@ -229,10 +229,10 @@ async function main() { // Deploy obligations console.log("📋 Deploying obligations...\n"); - addresses.stringObligation = await deployContract( - "String Obligation", - StringObligation.abi.abi, - StringObligation.abi.bytecode.object, + addresses.commitRevealObligation = await deployContract( + "Commit Reveal Obligation", + CommitRevealObligation.abi.abi, + CommitRevealObligation.abi.bytecode.object, [addresses.eas, addresses.easSchemaRegistry] ); @@ -338,8 +338,8 @@ async function main() { const scriptDir = import.meta.dir; const projectRoot = resolve(scriptDir, "../.."); - // Map localhost to devnet for file naming - const deploymentFileName = network === "localhost" ? "devnet" : network; + // Map localhost to anvil for file naming + const deploymentFileName = network === "localhost" ? "anvil" : network; const outputPath = args.output || resolve(projectRoot, `cli/deployments/${deploymentFileName}.json`); const outputDir = resolve(outputPath, ".."); diff --git a/cli/server/oracle.ts b/cli/server/oracle.ts index f7947d6..5b603f9 100644 --- a/cli/server/oracle.ts +++ b/cli/server/oracle.ts @@ -1,6 +1,6 @@ #!/usr/bin/env node import { parseArgs } from "util"; -import { parseAbiParameters, createWalletClient, http, publicActions } from "viem"; +import { createWalletClient, http, publicActions, fromHex } from "viem"; import { privateKeyToAccount } from "viem/accounts"; import { makeLLMClient } from "../.."; import { existsSync, readFileSync } from "fs"; @@ -250,22 +250,16 @@ async function main() { 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.arbiters.general.trustedOracle.arbitrateMany( async ({ attestation, demand }) => { console.log(`\n📨 New arbitration request received!`); console.log(` Attestation UID: ${attestation.uid}`); - + try { - // Extract obligation data - const obligation = client.extractObligationData( - obligationAbi, - attestation, - ); - const obligationItem = obligation[0].item; + // Extract obligation data from CommitRevealObligation + const commitRevealData = client.commitReveal.decode(attestation.data); + const obligationItem = fromHex(commitRevealData.payload, 'string'); console.log(` Obligation: "${obligationItem}"`); const trustedOracleDemandData = client.arbiters.general.trustedOracle.decodeDemand(demand); @@ -324,7 +318,7 @@ async function main() { console.log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"); console.log("📝 Next Steps - Create Your First Escrow:\n"); - if (currentEnv === 'devnet') { + if (currentEnv === 'anvil') { console.log("1. Export your private key (use a test account):"); console.log(" export PRIVATE_KEY=0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80\n"); } else { @@ -337,7 +331,7 @@ async function main() { console.log(" --demand \"The sky is blue\" \\"); console.log(" --amount 10 \\"); - if (currentEnv === 'devnet' && deployment.addresses.mockERC20A) { + if (currentEnv === 'anvil' && deployment.addresses.mockERC20A) { console.log(` --token ${deployment.addresses.mockERC20A} \\`); } else { console.log(" --token \\"); diff --git a/cli/utils.ts b/cli/utils.ts index 4e217c4..99573b4 100644 --- a/cli/utils.ts +++ b/cli/utils.ts @@ -62,7 +62,7 @@ export function getChainFromNetwork(network: string): Chain { switch (normalized) { case "localhost": - case "devnet": + case "anvil": return foundry; case "sepolia": case "ethereum-sepolia": @@ -95,15 +95,15 @@ export function getCurrentEnvironment(): string { const configPath = join(getNLAConfigDir(), 'config.json'); if (!existsSync(configPath)) { - // Default to devnet - return 'devnet'; + // Default to anvil + return 'anvil'; } try { const config = JSON.parse(readFileSync(configPath, 'utf-8')); - return config.environment || 'devnet'; + return config.environment || 'anvil'; } catch (e) { - return 'devnet'; + return 'anvil'; } } @@ -177,7 +177,7 @@ export function clearPrivateKey(): void { /** * Get deployment path for environment * Automatically looks in the CLI directory where utils.ts is located - * @param env - The environment name (devnet, sepolia, etc.) + * @param env - The environment name (anvil, sepolia, etc.) */ export function getDeploymentPath(env?: string): string { const environment = env || getCurrentEnvironment(); diff --git a/package.json b/package.json index 9aa68a3..2a84188 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "nla", - "version": "1.0.8", + "version": "1.1.0", "description": "Natural Language Agreement Oracle - CLI for creating and managing blockchain agreements using natural language", "type": "module", "private": false, @@ -66,7 +66,7 @@ "@perplexity-ai/ai-sdk": "^0.1.2", "@viem/anvil": "^0.0.10", "ai": "^6.0.5", - "alkahest-ts": "^0.6.1", + "alkahest-ts": "0.7.2", "arktype": "^2.1.23", "viem": "^2.42.1", "zod": "^3.25.76" diff --git a/tests/nlaOracle.test.ts b/tests/nlaOracle.test.ts index dff9241..be71ec7 100644 --- a/tests/nlaOracle.test.ts +++ b/tests/nlaOracle.test.ts @@ -1,15 +1,11 @@ import { afterAll, beforeAll, beforeEach, expect, test } from "bun:test"; -import { decodeAbiParameters, encodeAbiParameters, parseAbiParameters } from "viem"; - -import { generateText } from "ai" -import { openai } from "@ai-sdk/openai" +import { toHex, keccak256, fromHex } from "viem"; import { setupTestEnvironment, type TestContext, } from "alkahest-ts"; import { makeLLMClient } from ".."; -import { de } from "zod/v4/locales"; import { ProviderName } from "../nla"; let testContext: TestContext; @@ -74,43 +70,54 @@ Fulfillment: {{obligation}}`, 0n, ); - const obligationAbi = parseAbiParameters("(string item)"); const { decisions, unwatch } = await testContext.bob.client.arbiters.general.trustedOracle.arbitrateMany( async ({ attestation, demand }) => { console.log("Arbitrating ", attestation, demand); - const obligation = charlieClient.extractObligationData( - obligationAbi, - attestation, - ); - console.log("Obligation:", obligation); + const commitRevealData = testContext.bob.client.commitReveal.decode(attestation.data); + const obligationItem = fromHex(commitRevealData.payload, 'string'); + console.log("Obligation:", obligationItem); const trustedOracleDemandData = testContext.bob.client.arbiters.general.trustedOracle.decodeDemand(demand); const nlaDemandData = charlieClient.llm.decodeDemand(trustedOracleDemandData.data); - - const result = await charlieClient.llm.arbitrate(nlaDemandData, obligation[0].item); + + const result = await charlieClient.llm.arbitrate(nlaDemandData, obligationItem); console.log("response", result); return result; }, { onAfterArbitrate: async (decision) => { - const obligation = testContext.bob.client.extractObligationData( - obligationAbi, - decision.attestation, - ); + const commitRevealData = testContext.bob.client.commitReveal.decode(decision.attestation.data); + const obligationItem = fromHex(commitRevealData.payload, 'string'); expect(decision.attestation.uid).toEqual(fulfillment.uid); - expect(obligation[0].item).toEqual("The sky appears blue today"); + expect(obligationItem).toEqual("The sky appears blue today"); expect(decision.decision).toBe(true); }, pollingInterval: 50, }, ); + const schema = keccak256(toHex("{item:string}")); + const salt = keccak256(toHex(crypto.randomUUID())); + const payload = toHex("The sky appears blue today"); + const obligationData = { payload, salt, schema }; + + // Commit-reveal flow: commit, wait a block, reveal, reclaim bond + const commitment = await testContext.bob.client.commitReveal.computeCommitment( + escrow.uid, + testContext.bob.address, + obligationData, + ); + await testContext.bob.client.commitReveal.commit(commitment); + await testContext.testClient.mine({ blocks: 1 }); + const { attested: fulfillment } = - await testContext.bob.client.stringObligation.doObligation( - "The sky appears blue today", + await testContext.bob.client.commitReveal.doObligation( + obligationData, escrow.uid, ); + await testContext.bob.client.commitReveal.reclaimBond(fulfillment.uid); + await testContext.bob.client.arbiters.general.trustedOracle.requestArbitration( fulfillment.uid, testContext.bob.address, @@ -129,3 +136,128 @@ Fulfillment: {{obligation}}`, unwatch(); }, { timeout: 20000 }); + +test("full flow: alice escrow -> bob fulfill -> charlie arbitrate -> bob collect", async () => { + // 1. Alice deposits an escrow with a demand, designating Charlie as oracle + const arbiter = testContext.addresses.trustedOracleArbiter; + const demand = testContext.alice.client.arbiters.general.trustedOracle.encodeDemand({ + oracle: testContext.charlie.address, + data: charlieClient.llm.encodeDemand({ + arbitrationProvider: ProviderName.OpenRouter, + arbitrationModel: "openai/gpt-4o", + arbitrationPrompt: `Evaluate the fulfillment against the demand and decide whether the demand was validly fulfilled + +Demand: {{demand}} + +Fulfillment: {{obligation}}`, + demand: "Write a haiku about the ocean" + }) + }); + + const escrowAmount = 100n; + const { attested: escrow } = + await testContext.alice.client.erc20.escrow.nonTierable.permitAndCreate( + { + address: testContext.mockAddresses.erc20A, + value: escrowAmount, + }, + { arbiter, demand }, + 0n, + ); + + expect(escrow.uid).toBeTruthy(); + console.log(`✅ Alice created escrow: ${escrow.uid}`); + + // 3. Charlie starts listening as oracle (set up before Bob fulfills) + let arbitrationResolved: () => void; + const arbitrationDone = new Promise((resolve) => { + arbitrationResolved = resolve; + }); + + const { unwatch } = + await charlieClient.arbiters.general.trustedOracle.arbitrateMany( + async ({ attestation, demand: demandData }) => { + console.log(`📨 Charlie received arbitration request for: ${attestation.uid}`); + const commitRevealData = charlieClient.commitReveal.decode(attestation.data); + const obligationItem = fromHex(commitRevealData.payload, 'string'); + console.log(` Obligation text: "${obligationItem}"`); + + const trustedOracleDemandData = charlieClient.arbiters.general.trustedOracle.decodeDemand(demandData); + const nlaDemandData = charlieClient.llm.decodeDemand(trustedOracleDemandData.data); + console.log(` Demand: "${nlaDemandData.demand}"`); + + const result = await charlieClient.llm.arbitrate(nlaDemandData, obligationItem); + console.log(` ✨ Arbitration result: ${result}`); + return result; + }, + { + onAfterArbitrate: async (decision) => { + console.log(`📝 Charlie recorded decision: ${decision.decision}`); + arbitrationResolved(); + }, + pollingInterval: 50, + }, + ); + + // 2. Bob fulfills the escrow using commit-reveal + const schema = keccak256(toHex("{item:string}")); + const salt = keccak256(toHex(crypto.randomUUID())); + const fulfillmentText = "Waves crash on shore\nSalt and foam kiss weathered rocks\nThe tide breathes in, out"; + const payload = toHex(fulfillmentText); + const obligationData = { payload, salt, schema }; + + // Commit phase + const commitment = await testContext.bob.client.commitReveal.computeCommitment( + escrow.uid, + testContext.bob.address, + obligationData, + ); + await testContext.bob.client.commitReveal.commit(commitment); + await testContext.testClient.mine({ blocks: 1 }); + + // Reveal phase + const { attested: fulfillment } = + await testContext.bob.client.commitReveal.doObligation( + obligationData, + escrow.uid, + ); + expect(fulfillment.uid).toBeTruthy(); + console.log(`✅ Bob created fulfillment: ${fulfillment.uid}`); + + // Reclaim bond + await testContext.bob.client.commitReveal.reclaimBond(fulfillment.uid); + console.log(`✅ Bob reclaimed bond`); + + // Bob requests arbitration from Charlie + await testContext.bob.client.arbiters.general.trustedOracle.requestArbitration( + fulfillment.uid, + testContext.charlie.address, + demand, + ); + console.log(`📤 Bob requested arbitration from Charlie`); + + // Wait for Charlie to arbitrate + await arbitrationDone; + console.log(`✅ Arbitration complete`); + + // 4. Bob collects the escrow reward + const bobBalanceBefore = await testContext.testClient.getErc20Balance( + { address: testContext.mockAddresses.erc20A }, + testContext.bob.address, + ); + + const collectionHash = await testContext.bob.client.erc20.escrow.nonTierable.collect( + escrow.uid, + fulfillment.uid, + ); + expect(collectionHash).toBeTruthy(); + + const bobBalanceAfter = await testContext.testClient.getErc20Balance( + { address: testContext.mockAddresses.erc20A }, + testContext.bob.address, + ); + expect(bobBalanceAfter - bobBalanceBefore).toEqual(escrowAmount); + console.log(`✅ Bob collected ${escrowAmount} tokens from escrow`); + + unwatch(); +}, { timeout: 30000 });