@agether/agether 2.3.1 → 2.4.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/src/index.ts +59 -26
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.4.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/src/index.ts
CHANGED
|
@@ -4,15 +4,15 @@
|
|
|
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
18
|
import axios from "axios";
|
|
@@ -21,13 +21,27 @@ import * as path from "path";
|
|
|
21
21
|
import {
|
|
22
22
|
MorphoClient,
|
|
23
23
|
X402Client,
|
|
24
|
+
getContractAddresses,
|
|
25
|
+
ChainId,
|
|
24
26
|
} from "@agether/sdk";
|
|
25
27
|
|
|
26
28
|
// ─── Constants ────────────────────────────────────────────
|
|
27
29
|
|
|
28
|
-
const BACKEND_URL = "
|
|
29
|
-
|
|
30
|
-
|
|
30
|
+
const BACKEND_URL = "https://api.agether.ai";
|
|
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,13 @@ interface PluginConfig {
|
|
|
73
87
|
privateKey: string;
|
|
74
88
|
agentId?: string;
|
|
75
89
|
rpcUrl: string;
|
|
76
|
-
|
|
90
|
+
chainId: ChainId;
|
|
77
91
|
}
|
|
78
92
|
|
|
79
93
|
function txLink(hash: string): string {
|
|
80
|
-
|
|
94
|
+
if (!hash) return "";
|
|
95
|
+
const explorer = CHAIN_DEFAULTS[activeChainId]?.explorer ?? CHAIN_DEFAULTS[ChainId.Ethereum].explorer;
|
|
96
|
+
return `${explorer}/${hash}`;
|
|
81
97
|
}
|
|
82
98
|
|
|
83
99
|
function ok(text: string) {
|
|
@@ -102,18 +118,25 @@ function fail(err: unknown) {
|
|
|
102
118
|
/**
|
|
103
119
|
* Resolve RPC URL from environment secrets.
|
|
104
120
|
* Priority: ALCHEMY_API_KEY → ANKR_API_KEY → QUICKNODE_URL → publicnode fallback.
|
|
121
|
+
* Chain-aware: uses the correct endpoint subdomain for the configured chain.
|
|
105
122
|
*/
|
|
106
|
-
function resolveRpcUrl(): string {
|
|
123
|
+
function resolveRpcUrl(chainId: ChainId): string {
|
|
107
124
|
const alchemy = process.env.ALCHEMY_API_KEY;
|
|
108
|
-
if (alchemy)
|
|
125
|
+
if (alchemy) {
|
|
126
|
+
const alchemyChain = chainId === ChainId.Base ? "base-mainnet" : "eth-mainnet";
|
|
127
|
+
return `https://${alchemyChain}.g.alchemy.com/v2/${alchemy}`;
|
|
128
|
+
}
|
|
109
129
|
|
|
110
130
|
const ankr = process.env.ANKR_API_KEY;
|
|
111
|
-
if (ankr)
|
|
131
|
+
if (ankr) {
|
|
132
|
+
const ankrChain = chainId === ChainId.Base ? "base" : "eth";
|
|
133
|
+
return `https://rpc.ankr.com/${ankrChain}/${ankr}`;
|
|
134
|
+
}
|
|
112
135
|
|
|
113
136
|
const quicknode = process.env.QUICKNODE_URL;
|
|
114
137
|
if (quicknode) return quicknode;
|
|
115
138
|
|
|
116
|
-
return
|
|
139
|
+
return CHAIN_DEFAULTS[chainId]?.rpc ?? CHAIN_DEFAULTS[ChainId.Ethereum].rpc;
|
|
117
140
|
}
|
|
118
141
|
|
|
119
142
|
/**
|
|
@@ -134,16 +157,19 @@ function resolvePrivateKey(): string {
|
|
|
134
157
|
|
|
135
158
|
function getConfig(api: any): PluginConfig {
|
|
136
159
|
const cfg = api.config?.plugins?.entries?.["agether"]?.config ?? {};
|
|
160
|
+
const chainId = (cfg.chain as ChainId) || ChainId.Ethereum;
|
|
161
|
+
activeChainId = chainId; // Update module-level for txLink
|
|
137
162
|
return {
|
|
138
163
|
privateKey: resolvePrivateKey(),
|
|
139
164
|
agentId: cfg.agentId,
|
|
140
|
-
rpcUrl: resolveRpcUrl(),
|
|
141
|
-
|
|
165
|
+
rpcUrl: resolveRpcUrl(chainId),
|
|
166
|
+
chainId,
|
|
142
167
|
};
|
|
143
168
|
}
|
|
144
169
|
|
|
145
170
|
// Module-level cache
|
|
146
171
|
let cachedAgentId: string | undefined;
|
|
172
|
+
let activeChainId: ChainId = ChainId.Ethereum;
|
|
147
173
|
|
|
148
174
|
function createClient(cfg: PluginConfig): MorphoClient {
|
|
149
175
|
const agentId = cachedAgentId || cfg.agentId;
|
|
@@ -151,6 +177,7 @@ function createClient(cfg: PluginConfig): MorphoClient {
|
|
|
151
177
|
privateKey: cfg.privateKey,
|
|
152
178
|
rpcUrl: cfg.rpcUrl,
|
|
153
179
|
agentId,
|
|
180
|
+
chainId: cfg.chainId,
|
|
154
181
|
});
|
|
155
182
|
}
|
|
156
183
|
|
|
@@ -186,7 +213,7 @@ export default function register(api: any) {
|
|
|
186
213
|
api.registerTool({
|
|
187
214
|
name: "agether_balance",
|
|
188
215
|
description:
|
|
189
|
-
"Check ETH and USDC balances for the agent's EOA wallet and AgentAccount
|
|
216
|
+
"Check ETH and USDC balances for the agent's EOA wallet and AgentAccount.",
|
|
190
217
|
parameters: { type: "object", properties: {}, required: [] },
|
|
191
218
|
async execute() {
|
|
192
219
|
try {
|
|
@@ -204,7 +231,7 @@ export default function register(api: any) {
|
|
|
204
231
|
api.registerTool({
|
|
205
232
|
name: "agether_register",
|
|
206
233
|
description:
|
|
207
|
-
"Register a new ERC-8004 agent identity
|
|
234
|
+
"Register a new ERC-8004 agent identity and create a Safe smart account (via Safe7579). Returns the new agentId.",
|
|
208
235
|
parameters: {
|
|
209
236
|
type: "object",
|
|
210
237
|
properties: {
|
|
@@ -751,7 +778,7 @@ export default function register(api: any) {
|
|
|
751
778
|
api.registerTool({
|
|
752
779
|
name: "morpho_markets",
|
|
753
780
|
description:
|
|
754
|
-
"List available Morpho Blue USDC markets
|
|
781
|
+
"List available Morpho Blue USDC markets — liquidity, supply/borrow APY, utilization, LLTV. " +
|
|
755
782
|
"Optionally filter by collateral token.",
|
|
756
783
|
parameters: {
|
|
757
784
|
type: "object",
|
|
@@ -808,22 +835,22 @@ export default function register(api: any) {
|
|
|
808
835
|
if (params.refresh) {
|
|
809
836
|
// x402-gated fresh score — account address required for payment
|
|
810
837
|
const accountAddress = await client.getAccountAddress();
|
|
838
|
+
const contracts = getContractAddresses(cfg.chainId);
|
|
811
839
|
const x402 = new X402Client({
|
|
812
840
|
privateKey: cfg.privateKey,
|
|
813
841
|
rpcUrl: cfg.rpcUrl,
|
|
814
|
-
backendUrl: cfg.backendUrl,
|
|
815
842
|
agentId,
|
|
816
843
|
accountAddress,
|
|
817
844
|
// Safe7579 needs validator prefix for ERC-1271 isValidSignature routing
|
|
818
|
-
validatorModule:
|
|
845
|
+
validatorModule: contracts.erc8004ValidationModule,
|
|
819
846
|
});
|
|
820
|
-
const result = await x402.get(`${
|
|
847
|
+
const result = await x402.get(`${BACKEND_URL}/score/${agentId}?chain=${cfg.chainId}`);
|
|
821
848
|
if (!result.success) return fail(result.error || "Score request failed");
|
|
822
849
|
return ok(JSON.stringify(result.data, null, 2));
|
|
823
850
|
}
|
|
824
851
|
|
|
825
852
|
// Free: read current onchain score
|
|
826
|
-
const { data } = await axios.get(`${
|
|
853
|
+
const { data } = await axios.get(`${BACKEND_URL}/score/${agentId}/current?chain=${cfg.chainId}`);
|
|
827
854
|
return ok(JSON.stringify(data, null, 2));
|
|
828
855
|
} catch (e) { return fail(e); }
|
|
829
856
|
},
|
|
@@ -956,10 +983,10 @@ export default function register(api: any) {
|
|
|
956
983
|
|
|
957
984
|
const autoDrawEnabled = agetherCfg.autoDraw === true;
|
|
958
985
|
const autoYieldEnabled = agetherCfg.autoYield === true;
|
|
986
|
+
const contracts = getContractAddresses(cfg.chainId);
|
|
959
987
|
const x402 = new X402Client({
|
|
960
988
|
privateKey: cfg.privateKey,
|
|
961
989
|
rpcUrl: cfg.rpcUrl,
|
|
962
|
-
backendUrl: cfg.backendUrl,
|
|
963
990
|
agentId,
|
|
964
991
|
accountAddress,
|
|
965
992
|
autoDraw: autoDrawEnabled,
|
|
@@ -970,7 +997,7 @@ export default function register(api: any) {
|
|
|
970
997
|
// Persist every spending update to cache file
|
|
971
998
|
onSpendingUpdate: (state: any) => saveSpendCache(state),
|
|
972
999
|
// Safe7579 needs validator prefix for ERC-1271 isValidSignature routing
|
|
973
|
-
validatorModule:
|
|
1000
|
+
validatorModule: contracts.erc8004ValidationModule,
|
|
974
1001
|
});
|
|
975
1002
|
|
|
976
1003
|
let result;
|
|
@@ -1130,6 +1157,7 @@ export default function register(api: any) {
|
|
|
1130
1157
|
totalBorrowingHeadroom: `$${(Number(maxBorrow.total) / 1e6).toFixed(2)}`,
|
|
1131
1158
|
positions: positionHealth,
|
|
1132
1159
|
alerts: alerts.length > 0 ? alerts : ["✅ All positions healthy"],
|
|
1160
|
+
chain: CHAIN_DEFAULTS[cfg.chainId]?.chainName ?? `Chain ${cfg.chainId}`,
|
|
1133
1161
|
rpcSource: process.env.ALCHEMY_API_KEY ? "Alchemy" :
|
|
1134
1162
|
process.env.ANKR_API_KEY ? "Ankr" :
|
|
1135
1163
|
process.env.QUICKNODE_URL ? "QuickNode" : "PublicNode (free)",
|
|
@@ -1162,10 +1190,14 @@ export default function register(api: any) {
|
|
|
1162
1190
|
}
|
|
1163
1191
|
|
|
1164
1192
|
// 2. RPC URL
|
|
1165
|
-
const
|
|
1193
|
+
const pluginCfg = api.config?.plugins?.entries?.["agether"]?.config ?? {};
|
|
1194
|
+
const prefChainId = (pluginCfg.chain as ChainId) || ChainId.Ethereum;
|
|
1195
|
+
const chainName = CHAIN_DEFAULTS[prefChainId]?.chainName ?? "Unknown";
|
|
1196
|
+
const rpcUrl = resolveRpcUrl(prefChainId);
|
|
1166
1197
|
const rpcSource = process.env.ALCHEMY_API_KEY ? "Alchemy" :
|
|
1167
1198
|
process.env.ANKR_API_KEY ? "Ankr" :
|
|
1168
1199
|
process.env.QUICKNODE_URL ? "QuickNode" : "PublicNode (free fallback)";
|
|
1200
|
+
checks.push({ check: "Chain", status: `✅ ${chainName} (${prefChainId})` });
|
|
1169
1201
|
checks.push({ check: "RPC endpoint", status: `✅ ${rpcSource}`, detail: rpcUrl.replace(/\/[a-zA-Z0-9_-]{20,}$/, '/***') });
|
|
1170
1202
|
|
|
1171
1203
|
// 3. Agent registration
|
|
@@ -1361,8 +1393,9 @@ export default function register(api: any) {
|
|
|
1361
1393
|
const balances = await client.getBalances();
|
|
1362
1394
|
const agentId = balances.agentId ?? "?";
|
|
1363
1395
|
const safeUsdc = balances.agentAccount?.usdc ?? "0";
|
|
1396
|
+
const chainName = CHAIN_DEFAULTS[cfg.chainId]?.chainName ?? `Chain ${cfg.chainId}`;
|
|
1364
1397
|
api.logger?.info?.(
|
|
1365
|
-
`[agether] Session start — Agent #${agentId}, EOA: ${balances.eth} ETH / $${balances.usdc} USDC, Safe: $${safeUsdc} USDC`,
|
|
1398
|
+
`[agether] Session start — ${chainName}, Agent #${agentId}, EOA: ${balances.eth} ETH / $${balances.usdc} USDC, Safe: $${safeUsdc} USDC`,
|
|
1366
1399
|
);
|
|
1367
1400
|
} catch {
|
|
1368
1401
|
// Silently fail — hook should not block conversations
|