@agether/agether 2.3.1 → 2.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/openclaw.plugin.json +7 -0
- package/package.json +3 -3
- package/skills/agether/SKILL.md +32 -9
- package/src/index.ts +163 -37
package/openclaw.plugin.json
CHANGED
|
@@ -8,6 +8,12 @@
|
|
|
8
8
|
"type": "object",
|
|
9
9
|
"additionalProperties": false,
|
|
10
10
|
"properties": {
|
|
11
|
+
"chain": {
|
|
12
|
+
"type": "number",
|
|
13
|
+
"description": "Chain ID: 1 = Ethereum (default), 8453 = Base",
|
|
14
|
+
"default": 1,
|
|
15
|
+
"enum": [1, 8453]
|
|
16
|
+
},
|
|
11
17
|
"agentId": {
|
|
12
18
|
"type": "string",
|
|
13
19
|
"description": "ERC-8004 agent ID (set after registration, auto-saved by tools)"
|
|
@@ -36,6 +42,7 @@
|
|
|
36
42
|
"required": []
|
|
37
43
|
},
|
|
38
44
|
"uiHints": {
|
|
45
|
+
"chain": { "label": "Chain", "placeholder": "1 (Ethereum)" },
|
|
39
46
|
"agentId": { "label": "Agent ID", "placeholder": "17676" },
|
|
40
47
|
"autoDraw": { "label": "Auto-Draw Credit", "placeholder": "false" },
|
|
41
48
|
"autoYield": { "label": "Auto-Yield (use yield before borrowing)", "placeholder": "false" },
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agether/agether",
|
|
3
|
-
"version": "2.
|
|
4
|
-
"description": "OpenClaw plugin for Agether — onchain credit for AI agents",
|
|
3
|
+
"version": "2.5.0",
|
|
4
|
+
"description": "OpenClaw plugin for Agether — onchain credit for AI agents on Ethereum & Base",
|
|
5
5
|
"main": "src/index.ts",
|
|
6
6
|
"openclaw": {
|
|
7
7
|
"extensions": [
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
]
|
|
10
10
|
},
|
|
11
11
|
"dependencies": {
|
|
12
|
-
"@agether/sdk": "^2.
|
|
12
|
+
"@agether/sdk": "^2.7.0",
|
|
13
13
|
"axios": "^1.6.0",
|
|
14
14
|
"ethers": "^6.9.0"
|
|
15
15
|
},
|
package/skills/agether/SKILL.md
CHANGED
|
@@ -5,7 +5,7 @@ description: On-chain credit protocol for AI agents on Base. Morpho credit & len
|
|
|
5
5
|
|
|
6
6
|
# Agether — On-Chain Credit for AI Agents
|
|
7
7
|
|
|
8
|
-
You are an AI agent with on-chain financial capabilities on **Base
|
|
8
|
+
You are an AI agent with on-chain financial capabilities on **Ethereum** and **Base** mainnets. You can borrow USDC, earn yield by lending, and pay for APIs autonomously.
|
|
9
9
|
|
|
10
10
|
---
|
|
11
11
|
|
|
@@ -62,7 +62,24 @@ Both `agether_set_agent` and `agether_register` save the agentId to config perma
|
|
|
62
62
|
|
|
63
63
|
---
|
|
64
64
|
|
|
65
|
-
##
|
|
65
|
+
## � CHAIN RESOLUTION (CRITICAL)
|
|
66
|
+
|
|
67
|
+
If `agether_health` returns `chain: "?"`, the chain is NOT configured. You MUST resolve this before proceeding with any on-chain action:
|
|
68
|
+
|
|
69
|
+
```
|
|
70
|
+
1. Ask user: "Which blockchain would you like to use? **Ethereum** or **Base**?"
|
|
71
|
+
2. User chooses a chain
|
|
72
|
+
3. Call `agether_set_chain(chain: "<ethereum or base>")`
|
|
73
|
+
4. After set → call `agether_health` to confirm everything is working on that chain
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
`agether_set_chain` saves the chain to config permanently (survives restarts). Accepts: `ethereum`, `eth`, `1`, `base`, `8453`.
|
|
77
|
+
|
|
78
|
+
**IMPORTANT:** Resolve chain BEFORE resolving agentId — the agent registration happens on a specific chain.
|
|
79
|
+
|
|
80
|
+
---
|
|
81
|
+
|
|
82
|
+
## �🛠 YOUR TOOLS
|
|
66
83
|
|
|
67
84
|
### Health & Diagnostics
|
|
68
85
|
| Tool | What it does |
|
|
@@ -76,6 +93,7 @@ Both `agether_set_agent` and `agether_register` save the agentId to config perma
|
|
|
76
93
|
| `agether_balance` | Show ETH + USDC + all collateral token balances for both EOA wallet and AgentAccount (Safe). Tokens auto-discovered from markets. |
|
|
77
94
|
| `agether_register` | Register on-chain: mints ERC-8004 identity NFT + creates Safe account (via Safe7579). Auto-saves agentId to config. |
|
|
78
95
|
| `agether_set_agent` | Set a known agentId (e.g. from memory) and save to config. Use when agentId is "?" but you remember it. |
|
|
96
|
+
| `agether_set_chain` | Set the active blockchain (Ethereum or Base) and save to config. Use when chain is "?" or user wants to switch chains. Persists across restarts. |
|
|
79
97
|
| `agether_score` | Get credit score. `refresh: false` = free read. `refresh: true` = pays x402, computes fresh score on-chain. **Always call this when user asks about score.** |
|
|
80
98
|
| `agether_kya_status` | Check if KYA code verification is required for this deployment. |
|
|
81
99
|
| `agether_set_kya` | Enable or disable KYA verification gate (owner only). |
|
|
@@ -129,6 +147,7 @@ These settings are in `openclaw.json` under `plugins.entries.agether.config`:
|
|
|
129
147
|
| Setting | Type | Default | Description |
|
|
130
148
|
|---------|------|---------|-------------|
|
|
131
149
|
| `agentId` | string | — | ERC-8004 agent ID (auto-saved by register/set_agent tools) |
|
|
150
|
+
| `chain` | number | — | Chain ID: `1` (Ethereum) or `8453` (Base). Auto-saved by `agether_set_chain`. If not set, bot asks user on first interaction. |
|
|
132
151
|
| `autoDraw` | boolean | `false` | Auto-borrow from Morpho when USDC is low for x402 payments |
|
|
133
152
|
| `autoYield` | boolean | `false` | Auto-withdraw earned yield before borrowing. Waterfall: balance → yield → borrow |
|
|
134
153
|
| `dailySpendLimitUsdc` | number | `0` | Daily spending cap for x402 auto-draw (0 = unlimited). Persisted to cache file. |
|
|
@@ -156,11 +175,13 @@ When `autoYield` and/or `autoDraw` are enabled, `x402_pay` automatically sources
|
|
|
156
175
|
### Session start (first message or new conversation)
|
|
157
176
|
```
|
|
158
177
|
1. agether_health ← ONE call for full context
|
|
159
|
-
2. Check the "
|
|
178
|
+
2. Check the "chain" field:
|
|
179
|
+
→ If chain is "?" → go to CHAIN RESOLUTION (must resolve before anything else)
|
|
180
|
+
3. Check the "alerts" array:
|
|
160
181
|
→ If any 🔴 alerts → WARN user immediately (liquidation risk!)
|
|
161
182
|
→ If any 🟡 alerts → mention the risk casually
|
|
162
183
|
→ If "agentId: ?" → go to AGENT ID RESOLUTION
|
|
163
|
-
|
|
184
|
+
4. Now you have full context to handle any request
|
|
164
185
|
```
|
|
165
186
|
|
|
166
187
|
### User asks to call a paid API (x402)
|
|
@@ -278,7 +299,7 @@ For supply positions (lent USDC):
|
|
|
278
299
|
|
|
279
300
|
### User asks "what can I do?" or "what is agether?"
|
|
280
301
|
```
|
|
281
|
-
Explain: You're an AI agent with on-chain credit on Base.
|
|
302
|
+
Explain: You're an AI agent with on-chain credit on Ethereum and Base.
|
|
282
303
|
You can:
|
|
283
304
|
- Deposit collateral and borrow USDC instantly (125% collateral ratio)
|
|
284
305
|
- Earn yield by lending USDC to Morpho markets
|
|
@@ -287,7 +308,7 @@ You can:
|
|
|
287
308
|
- Sponsor other agents with collateral
|
|
288
309
|
- Monitor position health and avoid liquidation
|
|
289
310
|
- Move funds freely between AgentAccount and EOA wallet
|
|
290
|
-
-
|
|
311
|
+
- Operate on Ethereum or Base mainnet, all autonomous
|
|
291
312
|
```
|
|
292
313
|
|
|
293
314
|
---
|
|
@@ -338,7 +359,7 @@ If something **fails**:
|
|
|
338
359
|
|
|
339
360
|
| Parameter | Value |
|
|
340
361
|
|-----------|-------|
|
|
341
|
-
|
|
|
362
|
+
| Chains | Ethereum mainnet (1) or Base mainnet (8453) — set via `agether_set_chain` |
|
|
342
363
|
| Currency | USDC (6 decimals) |
|
|
343
364
|
| Max LTV | 80% (= 125% collateral ratio) |
|
|
344
365
|
| Collateral tokens | Dynamically discovered from Morpho markets (call `morpho_markets`) |
|
|
@@ -373,7 +394,7 @@ The plugin reads secrets from environment variables via OpenClaw's secrets syste
|
|
|
373
394
|
| Ankr RPC key | `ANKR_API_KEY` | No | Alternative premium RPC |
|
|
374
395
|
| QuickNode RPC URL | `QUICKNODE_URL` | No | Full URL including API key |
|
|
375
396
|
|
|
376
|
-
If no RPC key is set, the plugin falls back to `https://base-rpc.publicnode.com`
|
|
397
|
+
If no RPC key is set, the plugin falls back to free public RPCs (`https://ethereum-rpc.publicnode.com` or `https://base-rpc.publicnode.com` depending on chain — rate-limited).
|
|
377
398
|
|
|
378
399
|
---
|
|
379
400
|
|
|
@@ -407,7 +428,9 @@ Wallet transfers:
|
|
|
407
428
|
|
|
408
429
|
---
|
|
409
430
|
|
|
410
|
-
## 📍 CONTRACT ADDRESSES
|
|
431
|
+
## 📍 CONTRACT ADDRESSES
|
|
432
|
+
|
|
433
|
+
Contracts are deployed on both **Ethereum** and **Base** mainnets. The SDK auto-resolves the correct addresses based on the configured chain. Key shared addresses:
|
|
411
434
|
|
|
412
435
|
| Contract | Address |
|
|
413
436
|
|----------|---------|
|
package/src/index.ts
CHANGED
|
@@ -4,30 +4,44 @@
|
|
|
4
4
|
* Each tool = MorphoClient / X402Client call → format result with txLink.
|
|
5
5
|
* Market discovery via Morpho GraphQL API (no backend dependency for markets).
|
|
6
6
|
*
|
|
7
|
+
* Supported chains: Ethereum (1, default), Base (8453).
|
|
8
|
+
*
|
|
7
9
|
* v2: Secrets-first config
|
|
8
10
|
* ─────────────────────────
|
|
9
11
|
* - privateKey → AGETHER_PRIVATE_KEY env var (set via `openclaw secrets configure` → crypto)
|
|
10
12
|
* - rpcUrl → auto-resolved from ALCHEMY_API_KEY / ANKR_API_KEY / QUICKNODE_URL env vars,
|
|
11
|
-
* falls back to
|
|
12
|
-
* - backendUrl → hardcoded (no config needed)
|
|
13
|
+
* falls back to publicnode.com (chain-aware)
|
|
13
14
|
* - agentId → optional plugin config field (auto-saved by tools)
|
|
14
|
-
*
|
|
15
|
-
* The plugin works with an empty `plugins.entries.agether.config: {}`.
|
|
15
|
+
* - chain → plugin config field (default: 1 = Ethereum)
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
|
-
import axios from "axios";
|
|
19
18
|
import * as fs from "fs";
|
|
20
19
|
import * as path from "path";
|
|
21
20
|
import {
|
|
22
21
|
MorphoClient,
|
|
23
22
|
X402Client,
|
|
23
|
+
ScoringClient,
|
|
24
|
+
getContractAddresses,
|
|
25
|
+
ChainId,
|
|
24
26
|
} from "@agether/sdk";
|
|
25
27
|
|
|
26
28
|
// ─── Constants ────────────────────────────────────────────
|
|
27
29
|
|
|
28
30
|
const BACKEND_URL = "http://95.179.189.214:3001";
|
|
29
|
-
|
|
30
|
-
|
|
31
|
+
|
|
32
|
+
// Per-chain defaults
|
|
33
|
+
const CHAIN_DEFAULTS: Record<number, { rpc: string; explorer: string; chainName: string }> = {
|
|
34
|
+
[ChainId.Ethereum]: {
|
|
35
|
+
rpc: "https://ethereum-rpc.publicnode.com",
|
|
36
|
+
explorer: "https://etherscan.io/tx",
|
|
37
|
+
chainName: "Ethereum",
|
|
38
|
+
},
|
|
39
|
+
[ChainId.Base]: {
|
|
40
|
+
rpc: "https://base-rpc.publicnode.com",
|
|
41
|
+
explorer: "https://basescan.org/tx",
|
|
42
|
+
chainName: "Base",
|
|
43
|
+
},
|
|
44
|
+
};
|
|
31
45
|
|
|
32
46
|
// ─── Spending Cache (persists dailySpendLimit tracker across restarts) ────
|
|
33
47
|
|
|
@@ -73,11 +87,14 @@ interface PluginConfig {
|
|
|
73
87
|
privateKey: string;
|
|
74
88
|
agentId?: string;
|
|
75
89
|
rpcUrl: string;
|
|
76
|
-
|
|
90
|
+
chainId: ChainId;
|
|
91
|
+
chainConfigured: boolean; // true if user explicitly set chain in config
|
|
77
92
|
}
|
|
78
93
|
|
|
79
94
|
function txLink(hash: string): string {
|
|
80
|
-
|
|
95
|
+
if (!hash) return "";
|
|
96
|
+
const explorer = CHAIN_DEFAULTS[activeChainId]?.explorer ?? CHAIN_DEFAULTS[ChainId.Ethereum].explorer;
|
|
97
|
+
return `${explorer}/${hash}`;
|
|
81
98
|
}
|
|
82
99
|
|
|
83
100
|
function ok(text: string) {
|
|
@@ -102,18 +119,25 @@ function fail(err: unknown) {
|
|
|
102
119
|
/**
|
|
103
120
|
* Resolve RPC URL from environment secrets.
|
|
104
121
|
* Priority: ALCHEMY_API_KEY → ANKR_API_KEY → QUICKNODE_URL → publicnode fallback.
|
|
122
|
+
* Chain-aware: uses the correct endpoint subdomain for the configured chain.
|
|
105
123
|
*/
|
|
106
|
-
function resolveRpcUrl(): string {
|
|
124
|
+
function resolveRpcUrl(chainId: ChainId): string {
|
|
107
125
|
const alchemy = process.env.ALCHEMY_API_KEY;
|
|
108
|
-
if (alchemy)
|
|
126
|
+
if (alchemy) {
|
|
127
|
+
const alchemyChain = chainId === ChainId.Base ? "base-mainnet" : "eth-mainnet";
|
|
128
|
+
return `https://${alchemyChain}.g.alchemy.com/v2/${alchemy}`;
|
|
129
|
+
}
|
|
109
130
|
|
|
110
131
|
const ankr = process.env.ANKR_API_KEY;
|
|
111
|
-
if (ankr)
|
|
132
|
+
if (ankr) {
|
|
133
|
+
const ankrChain = chainId === ChainId.Base ? "base" : "eth";
|
|
134
|
+
return `https://rpc.ankr.com/${ankrChain}/${ankr}`;
|
|
135
|
+
}
|
|
112
136
|
|
|
113
137
|
const quicknode = process.env.QUICKNODE_URL;
|
|
114
138
|
if (quicknode) return quicknode;
|
|
115
139
|
|
|
116
|
-
return
|
|
140
|
+
return CHAIN_DEFAULTS[chainId]?.rpc ?? CHAIN_DEFAULTS[ChainId.Ethereum].rpc;
|
|
117
141
|
}
|
|
118
142
|
|
|
119
143
|
/**
|
|
@@ -134,16 +158,22 @@ function resolvePrivateKey(): string {
|
|
|
134
158
|
|
|
135
159
|
function getConfig(api: any): PluginConfig {
|
|
136
160
|
const cfg = api.config?.plugins?.entries?.["agether"]?.config ?? {};
|
|
161
|
+
const rawChain = cfg.chain;
|
|
162
|
+
const chainConfigured = rawChain !== undefined && rawChain !== null && rawChain !== "";
|
|
163
|
+
const chainId = (rawChain as ChainId) || ChainId.Ethereum;
|
|
164
|
+
activeChainId = chainId; // Update module-level for txLink
|
|
137
165
|
return {
|
|
138
166
|
privateKey: resolvePrivateKey(),
|
|
139
167
|
agentId: cfg.agentId,
|
|
140
|
-
rpcUrl: resolveRpcUrl(),
|
|
141
|
-
|
|
168
|
+
rpcUrl: resolveRpcUrl(chainId),
|
|
169
|
+
chainId,
|
|
170
|
+
chainConfigured,
|
|
142
171
|
};
|
|
143
172
|
}
|
|
144
173
|
|
|
145
174
|
// Module-level cache
|
|
146
175
|
let cachedAgentId: string | undefined;
|
|
176
|
+
let activeChainId: ChainId = ChainId.Ethereum;
|
|
147
177
|
|
|
148
178
|
function createClient(cfg: PluginConfig): MorphoClient {
|
|
149
179
|
const agentId = cachedAgentId || cfg.agentId;
|
|
@@ -151,6 +181,7 @@ function createClient(cfg: PluginConfig): MorphoClient {
|
|
|
151
181
|
privateKey: cfg.privateKey,
|
|
152
182
|
rpcUrl: cfg.rpcUrl,
|
|
153
183
|
agentId,
|
|
184
|
+
chainId: cfg.chainId,
|
|
154
185
|
});
|
|
155
186
|
}
|
|
156
187
|
|
|
@@ -177,6 +208,29 @@ function persistAgentId(agentId: string): string {
|
|
|
177
208
|
}
|
|
178
209
|
}
|
|
179
210
|
|
|
211
|
+
/** Persist chain to openclaw.json so it survives restarts. */
|
|
212
|
+
function persistChainId(chainId: ChainId): string {
|
|
213
|
+
activeChainId = chainId;
|
|
214
|
+
try {
|
|
215
|
+
const home = process.env.HOME || process.env.USERPROFILE || "/root";
|
|
216
|
+
const cfgPath = path.join(home, ".openclaw", "openclaw.json");
|
|
217
|
+
const raw = fs.readFileSync(cfgPath, "utf-8");
|
|
218
|
+
const json = JSON.parse(raw);
|
|
219
|
+
|
|
220
|
+
// Ensure config path exists
|
|
221
|
+
if (!json.plugins) json.plugins = {};
|
|
222
|
+
if (!json.plugins.entries) json.plugins.entries = {};
|
|
223
|
+
if (!json.plugins.entries.agether) json.plugins.entries.agether = {};
|
|
224
|
+
if (!json.plugins.entries.agether.config) json.plugins.entries.agether.config = {};
|
|
225
|
+
|
|
226
|
+
json.plugins.entries.agether.config.chain = chainId;
|
|
227
|
+
fs.writeFileSync(cfgPath, JSON.stringify(json, null, 2));
|
|
228
|
+
return "saved";
|
|
229
|
+
} catch (e) {
|
|
230
|
+
return `write failed: ${e instanceof Error ? e.message : String(e)}`;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
180
234
|
// ─── Plugin Entry ─────────────────────────────────────────
|
|
181
235
|
|
|
182
236
|
export default function register(api: any) {
|
|
@@ -186,7 +240,7 @@ export default function register(api: any) {
|
|
|
186
240
|
api.registerTool({
|
|
187
241
|
name: "agether_balance",
|
|
188
242
|
description:
|
|
189
|
-
"Check ETH and USDC balances for the agent's EOA wallet and AgentAccount
|
|
243
|
+
"Check ETH and USDC balances for the agent's EOA wallet and AgentAccount.",
|
|
190
244
|
parameters: { type: "object", properties: {}, required: [] },
|
|
191
245
|
async execute() {
|
|
192
246
|
try {
|
|
@@ -204,7 +258,7 @@ export default function register(api: any) {
|
|
|
204
258
|
api.registerTool({
|
|
205
259
|
name: "agether_register",
|
|
206
260
|
description:
|
|
207
|
-
"Register a new ERC-8004 agent identity
|
|
261
|
+
"Register a new ERC-8004 agent identity and create a Safe smart account (via Safe7579). Returns the new agentId.",
|
|
208
262
|
parameters: {
|
|
209
263
|
type: "object",
|
|
210
264
|
properties: {
|
|
@@ -271,6 +325,53 @@ export default function register(api: any) {
|
|
|
271
325
|
},
|
|
272
326
|
});
|
|
273
327
|
|
|
328
|
+
// ═══════════════════════════════════════════════════════
|
|
329
|
+
// TOOL: agether_set_chain
|
|
330
|
+
// ═══════════════════════════════════════════════════════
|
|
331
|
+
api.registerTool({
|
|
332
|
+
name: "agether_set_chain",
|
|
333
|
+
description:
|
|
334
|
+
"Set the active blockchain network and save it to config. " +
|
|
335
|
+
"Use this when the user chooses a chain (Ethereum or Base). " +
|
|
336
|
+
"Accepts chain name ('ethereum', 'base') or chain ID (1, 8453). " +
|
|
337
|
+
"This persists across restarts.",
|
|
338
|
+
parameters: {
|
|
339
|
+
type: "object",
|
|
340
|
+
properties: {
|
|
341
|
+
chain: {
|
|
342
|
+
type: "string",
|
|
343
|
+
description: "Chain name ('ethereum', 'base') or chain ID ('1', '8453')",
|
|
344
|
+
},
|
|
345
|
+
},
|
|
346
|
+
required: ["chain"],
|
|
347
|
+
},
|
|
348
|
+
async execute(_id: string, params: { chain: string }) {
|
|
349
|
+
try {
|
|
350
|
+
// Resolve chain name / ID to ChainId
|
|
351
|
+
const input = params.chain.toLowerCase().trim();
|
|
352
|
+
let chainId: ChainId;
|
|
353
|
+
if (input === "ethereum" || input === "eth" || input === "1") {
|
|
354
|
+
chainId = ChainId.Ethereum;
|
|
355
|
+
} else if (input === "base" || input === "8453") {
|
|
356
|
+
chainId = ChainId.Base;
|
|
357
|
+
} else {
|
|
358
|
+
return fail(`Unknown chain "${params.chain}". Supported: ethereum (1), base (8453)`);
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
const persistStatus = persistChainId(chainId);
|
|
362
|
+
const chainInfo = CHAIN_DEFAULTS[chainId];
|
|
363
|
+
return ok(JSON.stringify({
|
|
364
|
+
status: "chain_set",
|
|
365
|
+
chainId,
|
|
366
|
+
chainName: chainInfo.chainName,
|
|
367
|
+
explorer: chainInfo.explorer,
|
|
368
|
+
rpc: resolveRpcUrl(chainId).replace(/\/[a-zA-Z0-9_-]{20,}$/, '/***'),
|
|
369
|
+
configSaved: persistStatus,
|
|
370
|
+
}));
|
|
371
|
+
} catch (e) { return fail(e); }
|
|
372
|
+
},
|
|
373
|
+
});
|
|
374
|
+
|
|
274
375
|
// ═══════════════════════════════════════════════════════
|
|
275
376
|
// TOOL: agether_kya_status
|
|
276
377
|
// ═══════════════════════════════════════════════════════
|
|
@@ -751,7 +852,7 @@ export default function register(api: any) {
|
|
|
751
852
|
api.registerTool({
|
|
752
853
|
name: "morpho_markets",
|
|
753
854
|
description:
|
|
754
|
-
"List available Morpho Blue USDC markets
|
|
855
|
+
"List available Morpho Blue USDC markets — liquidity, supply/borrow APY, utilization, LLTV. " +
|
|
755
856
|
"Optionally filter by collateral token.",
|
|
756
857
|
parameters: {
|
|
757
858
|
type: "object",
|
|
@@ -806,24 +907,30 @@ export default function register(api: any) {
|
|
|
806
907
|
const agentId = client.getAgentId();
|
|
807
908
|
|
|
808
909
|
if (params.refresh) {
|
|
809
|
-
// x402-gated fresh score
|
|
910
|
+
// x402-gated fresh score via ScoringClient
|
|
810
911
|
const accountAddress = await client.getAccountAddress();
|
|
811
|
-
const
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
912
|
+
const contracts = getContractAddresses(cfg.chainId);
|
|
913
|
+
const scoring = new ScoringClient({
|
|
914
|
+
endpoint: BACKEND_URL,
|
|
915
|
+
chainId: cfg.chainId,
|
|
916
|
+
x402: {
|
|
917
|
+
privateKey: cfg.privateKey,
|
|
918
|
+
rpcUrl: cfg.rpcUrl,
|
|
919
|
+
agentId,
|
|
920
|
+
accountAddress,
|
|
921
|
+
validatorModule: contracts.erc8004ValidationModule,
|
|
922
|
+
},
|
|
819
923
|
});
|
|
820
|
-
const result = await
|
|
821
|
-
|
|
822
|
-
return ok(JSON.stringify(result.data, null, 2));
|
|
924
|
+
const result = await scoring.requestScore(agentId);
|
|
925
|
+
return ok(JSON.stringify(result, null, 2));
|
|
823
926
|
}
|
|
824
927
|
|
|
825
|
-
// Free: read current onchain score
|
|
826
|
-
const
|
|
928
|
+
// Free: read current onchain score via ScoringClient
|
|
929
|
+
const scoring = new ScoringClient({
|
|
930
|
+
endpoint: BACKEND_URL,
|
|
931
|
+
chainId: cfg.chainId,
|
|
932
|
+
});
|
|
933
|
+
const data = await scoring.getCurrentScore(agentId);
|
|
827
934
|
return ok(JSON.stringify(data, null, 2));
|
|
828
935
|
} catch (e) { return fail(e); }
|
|
829
936
|
},
|
|
@@ -956,10 +1063,10 @@ export default function register(api: any) {
|
|
|
956
1063
|
|
|
957
1064
|
const autoDrawEnabled = agetherCfg.autoDraw === true;
|
|
958
1065
|
const autoYieldEnabled = agetherCfg.autoYield === true;
|
|
1066
|
+
const contracts = getContractAddresses(cfg.chainId);
|
|
959
1067
|
const x402 = new X402Client({
|
|
960
1068
|
privateKey: cfg.privateKey,
|
|
961
1069
|
rpcUrl: cfg.rpcUrl,
|
|
962
|
-
backendUrl: cfg.backendUrl,
|
|
963
1070
|
agentId,
|
|
964
1071
|
accountAddress,
|
|
965
1072
|
autoDraw: autoDrawEnabled,
|
|
@@ -970,7 +1077,7 @@ export default function register(api: any) {
|
|
|
970
1077
|
// Persist every spending update to cache file
|
|
971
1078
|
onSpendingUpdate: (state: any) => saveSpendCache(state),
|
|
972
1079
|
// Safe7579 needs validator prefix for ERC-1271 isValidSignature routing
|
|
973
|
-
validatorModule:
|
|
1080
|
+
validatorModule: contracts.erc8004ValidationModule,
|
|
974
1081
|
});
|
|
975
1082
|
|
|
976
1083
|
let result;
|
|
@@ -1117,6 +1224,11 @@ export default function register(api: any) {
|
|
|
1117
1224
|
alerts.push("⛽ Low ETH for gas — may fail on-chain transactions. Send ETH to EOA.");
|
|
1118
1225
|
}
|
|
1119
1226
|
|
|
1227
|
+
// Check if chain is explicitly configured
|
|
1228
|
+
if (!cfg.chainConfigured) {
|
|
1229
|
+
alerts.push("⚠️ Chain not configured — currently defaulting to Ethereum. Ask user which chain to use and call agether_set_chain.");
|
|
1230
|
+
}
|
|
1231
|
+
|
|
1120
1232
|
const result = {
|
|
1121
1233
|
agentId: balances.agentId,
|
|
1122
1234
|
walletAddress: balances.address,
|
|
@@ -1130,6 +1242,9 @@ export default function register(api: any) {
|
|
|
1130
1242
|
totalBorrowingHeadroom: `$${(Number(maxBorrow.total) / 1e6).toFixed(2)}`,
|
|
1131
1243
|
positions: positionHealth,
|
|
1132
1244
|
alerts: alerts.length > 0 ? alerts : ["✅ All positions healthy"],
|
|
1245
|
+
chain: cfg.chainConfigured
|
|
1246
|
+
? (CHAIN_DEFAULTS[cfg.chainId]?.chainName ?? `Chain ${cfg.chainId}`)
|
|
1247
|
+
: "?",
|
|
1133
1248
|
rpcSource: process.env.ALCHEMY_API_KEY ? "Alchemy" :
|
|
1134
1249
|
process.env.ANKR_API_KEY ? "Ankr" :
|
|
1135
1250
|
process.env.QUICKNODE_URL ? "QuickNode" : "PublicNode (free)",
|
|
@@ -1161,11 +1276,21 @@ export default function register(api: any) {
|
|
|
1161
1276
|
checks.push({ check: "Private key (AGETHER_PRIVATE_KEY)", status: "❌ missing", detail: "Set via: openclaw secrets configure → source: env → id: AGETHER_PRIVATE_KEY" });
|
|
1162
1277
|
}
|
|
1163
1278
|
|
|
1164
|
-
// 2. RPC URL
|
|
1165
|
-
const
|
|
1279
|
+
// 2. Chain & RPC URL
|
|
1280
|
+
const pluginCfg = api.config?.plugins?.entries?.["agether"]?.config ?? {};
|
|
1281
|
+
const rawChain = pluginCfg.chain;
|
|
1282
|
+
const chainConfigured = rawChain !== undefined && rawChain !== null && rawChain !== "";
|
|
1283
|
+
const prefChainId = (rawChain as ChainId) || ChainId.Ethereum;
|
|
1284
|
+
const chainName = CHAIN_DEFAULTS[prefChainId]?.chainName ?? "Unknown";
|
|
1285
|
+
const rpcUrl = resolveRpcUrl(prefChainId);
|
|
1166
1286
|
const rpcSource = process.env.ALCHEMY_API_KEY ? "Alchemy" :
|
|
1167
1287
|
process.env.ANKR_API_KEY ? "Ankr" :
|
|
1168
1288
|
process.env.QUICKNODE_URL ? "QuickNode" : "PublicNode (free fallback)";
|
|
1289
|
+
if (chainConfigured) {
|
|
1290
|
+
checks.push({ check: "Chain", status: `✅ ${chainName} (${prefChainId})` });
|
|
1291
|
+
} else {
|
|
1292
|
+
checks.push({ check: "Chain", status: "⚠️ not configured (defaulting to Ethereum)", detail: "Ask user which chain to use and call agether_set_chain" });
|
|
1293
|
+
}
|
|
1169
1294
|
checks.push({ check: "RPC endpoint", status: `✅ ${rpcSource}`, detail: rpcUrl.replace(/\/[a-zA-Z0-9_-]{20,}$/, '/***') });
|
|
1170
1295
|
|
|
1171
1296
|
// 3. Agent registration
|
|
@@ -1361,8 +1486,9 @@ export default function register(api: any) {
|
|
|
1361
1486
|
const balances = await client.getBalances();
|
|
1362
1487
|
const agentId = balances.agentId ?? "?";
|
|
1363
1488
|
const safeUsdc = balances.agentAccount?.usdc ?? "0";
|
|
1489
|
+
const chainName = CHAIN_DEFAULTS[cfg.chainId]?.chainName ?? `Chain ${cfg.chainId}`;
|
|
1364
1490
|
api.logger?.info?.(
|
|
1365
|
-
`[agether] Session start — Agent #${agentId}, EOA: ${balances.eth} ETH / $${balances.usdc} USDC, Safe: $${safeUsdc} USDC`,
|
|
1491
|
+
`[agether] Session start — ${chainName}, Agent #${agentId}, EOA: ${balances.eth} ETH / $${balances.usdc} USDC, Safe: $${safeUsdc} USDC`,
|
|
1366
1492
|
);
|
|
1367
1493
|
} catch {
|
|
1368
1494
|
// Silently fail — hook should not block conversations
|