Update the sample command

This commit is contained in:
ngoc 2026-01-02 12:59:13 +07:00
parent ac45c5c949
commit a779177958
No known key found for this signature in database
GPG Key ID: 51FE6110113A5C32
7 changed files with 143 additions and 47 deletions

View File

@ -5,9 +5,10 @@
"": {
"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",
"ai": "^6.0.5",
"alkahest-ts": "git+https://github.com/VAR-META-Tech/alkahest.git#ts-package",
"arktype": "^2.1.23",
"viem": "^2.42.1",
@ -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,7 +65,7 @@
"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"],

View File

@ -167,17 +167,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}`,
@ -228,10 +222,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 <fulfillment-uid>`);
} catch (error) {
console.error("❌ Failed to create escrow:", error);

View File

@ -178,9 +178,10 @@ async function main() {
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);

View File

@ -2,7 +2,7 @@
"network": "localhost",
"chainId": 31337,
"rpcUrl": "http://localhost:8545",
"deployedAt": "2026-01-02T04:20:18.975Z",
"deployedAt": "2026-01-02T05:54:03.735Z",
"deployer": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266",
"addresses": {
"easSchemaRegistry": "0x5fbdb2315678afecb367f032d93f642f64180aa3",

View File

@ -363,9 +363,15 @@ 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`);
} catch (error) {
console.error("❌ Deployment failed:", error);

123
nla.ts
View File

@ -1,3 +1,49 @@
/**
* 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,
@ -5,11 +51,13 @@ import {
} 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 = {
@ -47,32 +95,69 @@ export const makeLLMClient = (
}
console.log(`Using provider: ${selectedProvider.providerName} for arbitration demand: ${JSON.stringify(demand)}`);
if (selectedProvider.providerName.toLowerCase() === 'openai') {
// 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,
})
// 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.`
baseURL: selectedProvider.baseURL,
});
console.log(`LLM Response: ${text}`);
const result = await generateText({
model: openai(demand.arbitrationModel),
system: systemPrompt,
prompt: userPrompt,
});
text = result.text;
const cleanedResponse = text.trim().toLowerCase();
return cleanedResponse === 'true';
} 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}`);
}
return false;
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}`);

View File

@ -25,9 +25,10 @@
"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",
"ai": "^6.0.5",
"alkahest-ts": "git+https://github.com/VAR-META-Tech/alkahest.git#ts-package",
"arktype": "^2.1.23",
"viem": "^2.42.1",