@agether/openclaw-plugin 1.4.0 → 1.5.1
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/package.json +2 -2
- package/src/index.ts +150 -574
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agether/openclaw-plugin",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.1",
|
|
4
4
|
"description": "OpenClaw plugin for Agether — on-chain credit for AI agents",
|
|
5
5
|
"main": "src/index.ts",
|
|
6
6
|
"openclaw": {
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
]
|
|
10
10
|
},
|
|
11
11
|
"dependencies": {
|
|
12
|
-
"@agether/sdk": "^1.
|
|
12
|
+
"@agether/sdk": "^1.4.0",
|
|
13
13
|
"axios": "^1.6.0",
|
|
14
14
|
"ethers": "^6.9.0"
|
|
15
15
|
},
|
package/src/index.ts
CHANGED
|
@@ -1,19 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Agether OpenClaw Plugin
|
|
2
|
+
* Agether OpenClaw Plugin — thin wrapper over @agether/sdk
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* Each tool = SDK call → format result with txLink.
|
|
5
|
+
* Backend-only calls (score, compare, markets) use axios directly.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import { ethers } from "ethers";
|
|
9
8
|
import axios from "axios";
|
|
10
9
|
import {
|
|
11
|
-
|
|
12
|
-
AGENT_ACCOUNT_ABI,
|
|
13
|
-
MORPHO_CREDIT_ABI,
|
|
14
|
-
ERC20_ABI,
|
|
10
|
+
MorphoCreditClient,
|
|
15
11
|
X402Client,
|
|
16
|
-
getDefaultConfig,
|
|
17
12
|
} from "@agether/sdk";
|
|
18
13
|
|
|
19
14
|
// ─── Helpers ──────────────────────────────────────────────
|
|
@@ -21,23 +16,32 @@ import {
|
|
|
21
16
|
interface PluginConfig {
|
|
22
17
|
privateKey: string;
|
|
23
18
|
agentId?: string;
|
|
24
|
-
rpcUrl
|
|
25
|
-
backendUrl
|
|
19
|
+
rpcUrl: string;
|
|
20
|
+
backendUrl: string;
|
|
26
21
|
autoDraw?: boolean;
|
|
27
22
|
}
|
|
28
23
|
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
24
|
+
const BASESCAN = "https://basescan.org/tx";
|
|
25
|
+
function txLink(hash: string): string {
|
|
26
|
+
return hash ? `${BASESCAN}/${hash}` : "";
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function ok(text: string) {
|
|
30
|
+
return { content: [{ type: "text" as const, text }] };
|
|
31
|
+
}
|
|
35
32
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
}
|
|
33
|
+
function fail(err: unknown) {
|
|
34
|
+
let msg = err instanceof Error ? err.message : String(err);
|
|
35
|
+
if (msg.includes("0xda04aecc")) {
|
|
36
|
+
msg = "ExceedsMaxLtv — collateral too low for this borrow amount (max 80% LTV / 125% collateral ratio)";
|
|
37
|
+
} else if (msg.includes("0xfeca99cb")) {
|
|
38
|
+
msg = "ExecutionFailed — the inner contract call reverted. Likely ExceedsMaxLtv or insufficient collateral.";
|
|
39
|
+
} else if (msg.includes("0xa920ef9f")) {
|
|
40
|
+
msg = "PositionNotActive — no collateral deposited for this token";
|
|
41
|
+
}
|
|
42
|
+
if (msg.length > 300) msg = msg.slice(0, 250) + "…";
|
|
43
|
+
return { content: [{ type: "text" as const, text: `❌ ${msg}` }], isError: true };
|
|
44
|
+
}
|
|
41
45
|
|
|
42
46
|
function getConfig(api: any): PluginConfig {
|
|
43
47
|
const cfg = api.config?.plugins?.entries?.agether?.config;
|
|
@@ -53,47 +57,27 @@ function getConfig(api: any): PluginConfig {
|
|
|
53
57
|
};
|
|
54
58
|
}
|
|
55
59
|
|
|
56
|
-
function getSigner(cfg: PluginConfig) {
|
|
57
|
-
const provider = new ethers.JsonRpcProvider(cfg.rpcUrl);
|
|
58
|
-
return new ethers.Wallet(cfg.privateKey, provider);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
60
|
async function getBackendStatus(cfg: PluginConfig) {
|
|
62
61
|
const { data } = await axios.get(`${cfg.backendUrl}/status`);
|
|
63
62
|
return data;
|
|
64
63
|
}
|
|
65
64
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
const addr = await factory.getAccount(agentId);
|
|
69
|
-
if (addr === ethers.ZeroAddress) throw new Error("No AgentAccount. Register first.");
|
|
70
|
-
return addr;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
// ─── Auto-resolve agentId from chain ──────────────────────
|
|
74
|
-
let _cachedAgentId: string | null = null;
|
|
75
|
-
|
|
76
|
-
async function resolveAgentId(cfg: PluginConfig, signer: ethers.Wallet, status: any): Promise<string> {
|
|
77
|
-
// 1. Config takes priority
|
|
78
|
-
if (cfg.agentId && cfg.agentId !== "0") return cfg.agentId;
|
|
79
|
-
// 2. Session cache
|
|
80
|
-
if (_cachedAgentId) return _cachedAgentId;
|
|
81
|
-
// 3. Look up on-chain: does this wallet own any ERC-8004 token?
|
|
82
|
-
const agentRegistry = new ethers.Contract(status.contracts.agentRegistry, ERC8004_ABI, signer.provider!);
|
|
83
|
-
const balance: bigint = await agentRegistry.balanceOf(signer.address);
|
|
84
|
-
if (balance === 0n) throw new Error("No agent registered. Use agether_register first.");
|
|
85
|
-
const tokenId: bigint = await agentRegistry.tokenOfOwnerByIndex(signer.address, 0);
|
|
86
|
-
_cachedAgentId = tokenId.toString();
|
|
87
|
-
return _cachedAgentId;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
function ok(text: string) {
|
|
91
|
-
return { content: [{ type: "text" as const, text }] };
|
|
92
|
-
}
|
|
65
|
+
// Module-level cache: survives across tool calls within the same process
|
|
66
|
+
let cachedAgentId: string | undefined;
|
|
93
67
|
|
|
94
|
-
function
|
|
95
|
-
const
|
|
96
|
-
return
|
|
68
|
+
function createClient(cfg: PluginConfig, status: any): MorphoCreditClient {
|
|
69
|
+
const agentId = cachedAgentId || cfg.agentId;
|
|
70
|
+
return new MorphoCreditClient({
|
|
71
|
+
privateKey: cfg.privateKey,
|
|
72
|
+
rpcUrl: cfg.rpcUrl,
|
|
73
|
+
agentId,
|
|
74
|
+
contracts: {
|
|
75
|
+
morphoCredit: status.contracts.morphoCredit,
|
|
76
|
+
accountFactory: status.contracts.accountFactory,
|
|
77
|
+
usdc: status.contracts.usdc,
|
|
78
|
+
agentRegistry: status.contracts.agentRegistry,
|
|
79
|
+
},
|
|
80
|
+
});
|
|
97
81
|
}
|
|
98
82
|
|
|
99
83
|
// ─── Plugin Entry ─────────────────────────────────────────
|
|
@@ -110,38 +94,9 @@ export default function register(api: any) {
|
|
|
110
94
|
async execute() {
|
|
111
95
|
try {
|
|
112
96
|
const cfg = getConfig(api);
|
|
113
|
-
const signer = getSigner(cfg);
|
|
114
97
|
const status = await getBackendStatus(cfg);
|
|
115
|
-
const
|
|
116
|
-
|
|
117
|
-
const provider = signer.provider!;
|
|
118
|
-
const ethBal = await provider.getBalance(address);
|
|
119
|
-
const usdc = new ethers.Contract(status.contracts.usdc, ERC20_ABI, provider);
|
|
120
|
-
const usdcBal = await usdc.balanceOf(address);
|
|
121
|
-
|
|
122
|
-
const result: any = {
|
|
123
|
-
address,
|
|
124
|
-
agentId: cfg.agentId || _cachedAgentId || "not registered",
|
|
125
|
-
eth: ethers.formatEther(ethBal),
|
|
126
|
-
usdc: ethers.formatUnits(usdcBal, 6),
|
|
127
|
-
};
|
|
128
|
-
|
|
129
|
-
// AgentAccount balances — try to auto-resolve agentId
|
|
130
|
-
try {
|
|
131
|
-
const agentId = await resolveAgentId(cfg, signer, status);
|
|
132
|
-
result.agentId = agentId;
|
|
133
|
-
if (status.contracts.accountFactory) {
|
|
134
|
-
const accountAddr = await getAccountAddr(signer, status.contracts.accountFactory, agentId);
|
|
135
|
-
const accEth = await provider.getBalance(accountAddr);
|
|
136
|
-
const accUsdc = await usdc.balanceOf(accountAddr);
|
|
137
|
-
result.agentAccount = {
|
|
138
|
-
address: accountAddr,
|
|
139
|
-
eth: ethers.formatEther(accEth),
|
|
140
|
-
usdc: ethers.formatUnits(accUsdc, 6),
|
|
141
|
-
};
|
|
142
|
-
}
|
|
143
|
-
} catch { /* no agent registered yet */ }
|
|
144
|
-
|
|
98
|
+
const client = createClient(cfg, status);
|
|
99
|
+
const result = await client.getBalances();
|
|
145
100
|
return ok(JSON.stringify(result, null, 2));
|
|
146
101
|
} catch (e) { return fail(e); }
|
|
147
102
|
},
|
|
@@ -153,7 +108,7 @@ export default function register(api: any) {
|
|
|
153
108
|
api.registerTool({
|
|
154
109
|
name: "agether_register",
|
|
155
110
|
description:
|
|
156
|
-
"Register a new ERC-8004 agent identity on Base and create an AgentAccount. Returns the new agentId.
|
|
111
|
+
"Register a new ERC-8004 agent identity on Base and create an AgentAccount. Returns the new agentId.",
|
|
157
112
|
parameters: {
|
|
158
113
|
type: "object",
|
|
159
114
|
properties: {
|
|
@@ -164,82 +119,17 @@ export default function register(api: any) {
|
|
|
164
119
|
async execute(_id: string, params: { name: string }) {
|
|
165
120
|
try {
|
|
166
121
|
const cfg = getConfig(api);
|
|
167
|
-
const signer = getSigner(cfg);
|
|
168
122
|
const status = await getBackendStatus(cfg);
|
|
169
|
-
|
|
170
|
-
const
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
// If agentId already set, verify and use
|
|
175
|
-
if (cfg.agentId && cfg.agentId !== "0") {
|
|
176
|
-
agentId = BigInt(cfg.agentId);
|
|
177
|
-
const owner = await agentRegistry.ownerOf(agentId);
|
|
178
|
-
if (owner.toLowerCase() !== signer.address.toLowerCase()) {
|
|
179
|
-
return fail("agentId in config does not belong to this wallet");
|
|
180
|
-
}
|
|
181
|
-
// Ensure AgentAccount exists
|
|
182
|
-
if (status.contracts.accountFactory) {
|
|
183
|
-
const factory = new ethers.Contract(status.contracts.accountFactory, ACCOUNT_FACTORY_ABI, signer);
|
|
184
|
-
const exists = await factory.accountExists(agentId);
|
|
185
|
-
if (!exists) {
|
|
186
|
-
const tx = await factory.createAccount(agentId);
|
|
187
|
-
await tx.wait();
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
return ok(JSON.stringify({
|
|
191
|
-
status: "already_registered",
|
|
192
|
-
agentId: agentId.toString(),
|
|
193
|
-
address: signer.address,
|
|
194
|
-
}));
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
// Mint new ERC-8004 identity
|
|
198
|
-
const registrationFile = JSON.stringify({
|
|
199
|
-
type: "https://eips.ethereum.org/EIPS/eip-8004#registration-v1",
|
|
200
|
-
name: params.name,
|
|
201
|
-
description: "AI agent registered via Agether OpenClaw plugin",
|
|
202
|
-
active: true,
|
|
203
|
-
registrations: [{
|
|
204
|
-
agentId: 0,
|
|
205
|
-
agentRegistry: `eip155:${status.chainId}:${status.contracts.agentRegistry}`,
|
|
206
|
-
}],
|
|
207
|
-
});
|
|
208
|
-
const agentURI = `data:application/json;base64,${Buffer.from(registrationFile).toString("base64")}`;
|
|
209
|
-
|
|
210
|
-
const tx = await agentRegistry["register(string)"](agentURI);
|
|
211
|
-
const receipt = await tx.wait();
|
|
212
|
-
|
|
213
|
-
// Parse agentId from Transfer event
|
|
214
|
-
const transferTopic = ethers.id("Transfer(address,address,uint256)");
|
|
215
|
-
const transferLog = receipt.logs.find((l: any) => l.topics[0] === transferTopic);
|
|
216
|
-
if (transferLog?.topics?.length >= 4) {
|
|
217
|
-
agentId = BigInt(transferLog.topics[3]);
|
|
218
|
-
} else {
|
|
219
|
-
return fail("Could not parse agentId from receipt");
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
// Create AgentAccount
|
|
223
|
-
let accountAddr = "";
|
|
224
|
-
if (status.contracts.accountFactory) {
|
|
225
|
-
const factory = new ethers.Contract(status.contracts.accountFactory, ACCOUNT_FACTORY_ABI, signer);
|
|
226
|
-
const exists = await factory.accountExists(agentId);
|
|
227
|
-
if (!exists) {
|
|
228
|
-
const accTx = await factory.createAccount(agentId);
|
|
229
|
-
await accTx.wait();
|
|
230
|
-
}
|
|
231
|
-
accountAddr = await factory.getAccount(agentId);
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
// Cache agentId for the session so all subsequent tools work
|
|
235
|
-
_cachedAgentId = agentId.toString();
|
|
236
|
-
|
|
123
|
+
const client = createClient(cfg, status);
|
|
124
|
+
const result = await client.register(params.name);
|
|
125
|
+
// Cache agentId for subsequent tool calls
|
|
126
|
+
cachedAgentId = result.agentId;
|
|
237
127
|
return ok(JSON.stringify({
|
|
238
|
-
status: "registered",
|
|
239
|
-
agentId: agentId
|
|
240
|
-
address:
|
|
241
|
-
agentAccount:
|
|
242
|
-
tx: tx.
|
|
128
|
+
status: result.alreadyRegistered ? "already_registered" : "registered",
|
|
129
|
+
agentId: result.agentId,
|
|
130
|
+
address: result.address,
|
|
131
|
+
agentAccount: result.agentAccount,
|
|
132
|
+
tx: result.tx ? txLink(result.tx) : undefined,
|
|
243
133
|
}));
|
|
244
134
|
} catch (e) { return fail(e); }
|
|
245
135
|
},
|
|
@@ -256,33 +146,10 @@ export default function register(api: any) {
|
|
|
256
146
|
async execute() {
|
|
257
147
|
try {
|
|
258
148
|
const cfg = getConfig(api);
|
|
259
|
-
const signer = getSigner(cfg);
|
|
260
149
|
const status = await getBackendStatus(cfg);
|
|
261
|
-
const
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
const morpho = new ethers.Contract(status.contracts.morphoCredit, MORPHO_CREDIT_ABI, signer.provider!);
|
|
265
|
-
|
|
266
|
-
const positions: any[] = [];
|
|
267
|
-
for (const [symbol, info] of Object.entries(COLLATERAL_TOKENS)) {
|
|
268
|
-
const pos = await morpho.getPosition(accountAddr, info.address);
|
|
269
|
-
if (pos.isActive || pos.collateralAmount > 0n || pos.borrowedAmount > 0n) {
|
|
270
|
-
positions.push({
|
|
271
|
-
token: symbol,
|
|
272
|
-
collateral: ethers.formatUnits(pos.collateralAmount, info.decimals),
|
|
273
|
-
debt: `$${ethers.formatUnits(pos.borrowedAmount, 6)}`,
|
|
274
|
-
active: pos.isActive,
|
|
275
|
-
});
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
const totalDebt = await morpho.getTotalDebt(accountAddr);
|
|
280
|
-
|
|
281
|
-
return ok(JSON.stringify({
|
|
282
|
-
agentAccount: accountAddr,
|
|
283
|
-
totalDebt: `$${ethers.formatUnits(totalDebt, 6)}`,
|
|
284
|
-
positions: positions.length > 0 ? positions : "No active positions",
|
|
285
|
-
}, null, 2));
|
|
150
|
+
const client = createClient(cfg, status);
|
|
151
|
+
const result = await client.getStatus();
|
|
152
|
+
return ok(JSON.stringify(result, null, 2));
|
|
286
153
|
} catch (e) { return fail(e); }
|
|
287
154
|
},
|
|
288
155
|
});
|
|
@@ -305,55 +172,22 @@ export default function register(api: any) {
|
|
|
305
172
|
async execute(_id: string, params: { amount: string; token: string }) {
|
|
306
173
|
try {
|
|
307
174
|
const cfg = getConfig(api);
|
|
308
|
-
const signer = getSigner(cfg);
|
|
309
175
|
const status = await getBackendStatus(cfg);
|
|
310
|
-
const
|
|
311
|
-
|
|
312
|
-
const tokenInfo = COLLATERAL_TOKENS[params.token];
|
|
313
|
-
if (!tokenInfo) return fail(`Unsupported token: ${params.token}. Use WETH, wstETH, or cbETH`);
|
|
314
|
-
|
|
315
|
-
const morphoCreditAddr = status.contracts.morphoCredit;
|
|
316
|
-
const accountAddr = await getAccountAddr(signer, status.contracts.accountFactory, agentId);
|
|
317
|
-
const amount = ethers.parseUnits(params.amount, tokenInfo.decimals);
|
|
318
|
-
|
|
319
|
-
// Check balance
|
|
320
|
-
const token = new ethers.Contract(tokenInfo.address, ERC20_ABI, signer);
|
|
321
|
-
const balance = await token.balanceOf(signer.address);
|
|
322
|
-
if (balance < amount) {
|
|
323
|
-
return fail(`Insufficient ${params.token}: have ${ethers.formatUnits(balance, tokenInfo.decimals)}, need ${params.amount}`);
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
// Approve
|
|
327
|
-
const approveTx = await token.approve(morphoCreditAddr, amount);
|
|
328
|
-
await approveTx.wait();
|
|
329
|
-
|
|
330
|
-
// Deposit for AgentAccount
|
|
331
|
-
const morpho = new ethers.Contract(morphoCreditAddr, MORPHO_CREDIT_ABI, signer);
|
|
332
|
-
const depositTx = await morpho.depositCollateralFor(accountAddr, tokenInfo.address, amount);
|
|
333
|
-
await depositTx.wait();
|
|
334
|
-
|
|
335
|
-
// Approve credit provider on AgentAccount
|
|
336
|
-
const account = new ethers.Contract(accountAddr, AGENT_ACCOUNT_ABI, signer);
|
|
337
|
-
try {
|
|
338
|
-
const cpTx = await account.approveCreditProvider(morphoCreditAddr);
|
|
339
|
-
await cpTx.wait();
|
|
340
|
-
} catch { /* already approved */ }
|
|
341
|
-
|
|
342
|
-
const pos = await morpho.getPosition(accountAddr, tokenInfo.address);
|
|
343
|
-
|
|
176
|
+
const client = createClient(cfg, status);
|
|
177
|
+
const result = await client.deposit(params.token, params.amount);
|
|
344
178
|
return ok(JSON.stringify({
|
|
345
179
|
status: "deposited",
|
|
346
180
|
amount: `${params.amount} ${params.token}`,
|
|
347
|
-
totalCollateral:
|
|
348
|
-
agentAccount:
|
|
349
|
-
tx:
|
|
181
|
+
totalCollateral: result.totalCollateral.toString(),
|
|
182
|
+
agentAccount: result.agentAccount,
|
|
183
|
+
tx: txLink(result.tx),
|
|
350
184
|
}));
|
|
351
185
|
} catch (e) { return fail(e); }
|
|
352
186
|
},
|
|
353
187
|
});
|
|
354
188
|
|
|
355
189
|
// ═══════════════════════════════════════════════════════
|
|
356
|
-
// TOOL: morpho_deposit_and_borrow
|
|
190
|
+
// TOOL: morpho_deposit_and_borrow
|
|
357
191
|
// ═══════════════════════════════════════════════════════
|
|
358
192
|
api.registerTool({
|
|
359
193
|
name: "morpho_deposit_and_borrow",
|
|
@@ -371,66 +205,24 @@ export default function register(api: any) {
|
|
|
371
205
|
async execute(_id: string, params: { collateralAmount: string; token: string; borrowAmount: string }) {
|
|
372
206
|
try {
|
|
373
207
|
const cfg = getConfig(api);
|
|
374
|
-
const signer = getSigner(cfg);
|
|
375
208
|
const status = await getBackendStatus(cfg);
|
|
376
|
-
const
|
|
377
|
-
|
|
378
|
-
const tokenInfo = COLLATERAL_TOKENS[params.token];
|
|
379
|
-
if (!tokenInfo) return fail(`Unsupported token: ${params.token}`);
|
|
380
|
-
|
|
381
|
-
const morphoCreditAddr = status.contracts.morphoCredit;
|
|
382
|
-
const accountAddr = await getAccountAddr(signer, status.contracts.accountFactory, agentId);
|
|
383
|
-
const collateralWei = ethers.parseUnits(params.collateralAmount, tokenInfo.decimals);
|
|
384
|
-
const borrowWei = ethers.parseUnits(params.borrowAmount, 6);
|
|
385
|
-
|
|
386
|
-
// Check collateral balance
|
|
387
|
-
const token = new ethers.Contract(tokenInfo.address, ERC20_ABI, signer);
|
|
388
|
-
const balance = await token.balanceOf(signer.address);
|
|
389
|
-
if (balance < collateralWei) {
|
|
390
|
-
return fail(`Insufficient ${params.token}: have ${ethers.formatUnits(balance, tokenInfo.decimals)}, need ${params.collateralAmount}`);
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
// Step 1: Approve collateral
|
|
394
|
-
const approveTx = await token.approve(morphoCreditAddr, collateralWei);
|
|
395
|
-
await approveTx.wait();
|
|
396
|
-
|
|
397
|
-
// Step 2: Deposit collateral for AgentAccount
|
|
398
|
-
const morpho = new ethers.Contract(morphoCreditAddr, MORPHO_CREDIT_ABI, signer);
|
|
399
|
-
const depositTx = await morpho.depositCollateralFor(accountAddr, tokenInfo.address, collateralWei);
|
|
400
|
-
await depositTx.wait();
|
|
401
|
-
|
|
402
|
-
// Step 3: Approve credit provider on AgentAccount
|
|
403
|
-
const account = new ethers.Contract(accountAddr, AGENT_ACCOUNT_ABI, signer);
|
|
404
|
-
try {
|
|
405
|
-
const cpTx = await account.approveCreditProvider(morphoCreditAddr);
|
|
406
|
-
await cpTx.wait();
|
|
407
|
-
} catch { /* already approved */ }
|
|
408
|
-
|
|
409
|
-
// Step 4: Borrow via AgentAccount.execute → drawWithCollateral
|
|
410
|
-
const morphoIface = new ethers.Interface(MORPHO_CREDIT_ABI);
|
|
411
|
-
const borrowCalldata = morphoIface.encodeFunctionData("drawWithCollateral", [tokenInfo.address, borrowWei]);
|
|
412
|
-
const borrowTx = await account.execute(morphoCreditAddr, 0, borrowCalldata);
|
|
413
|
-
await borrowTx.wait();
|
|
414
|
-
|
|
415
|
-
const pos = await morpho.getPosition(accountAddr, tokenInfo.address);
|
|
416
|
-
const totalDebt = await morpho.getTotalDebt(accountAddr);
|
|
417
|
-
|
|
209
|
+
const client = createClient(cfg, status);
|
|
210
|
+
const result = await client.depositAndBorrow(params.token, params.collateralAmount, params.borrowAmount);
|
|
418
211
|
return ok(JSON.stringify({
|
|
419
212
|
status: "deposited_and_borrowed",
|
|
420
213
|
collateral: `${params.collateralAmount} ${params.token}`,
|
|
421
214
|
borrowed: `$${params.borrowAmount}`,
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
borrowTx: borrowTx.hash,
|
|
215
|
+
totalDebt: result.totalDebt.toString(),
|
|
216
|
+
agentAccount: result.agentAccount,
|
|
217
|
+
depositTx: txLink(result.depositTx),
|
|
218
|
+
borrowTx: txLink(result.borrowTx),
|
|
427
219
|
}));
|
|
428
220
|
} catch (e) { return fail(e); }
|
|
429
221
|
},
|
|
430
222
|
});
|
|
431
223
|
|
|
432
224
|
// ═══════════════════════════════════════════════════════
|
|
433
|
-
// TOOL: morpho_sponsor
|
|
225
|
+
// TOOL: morpho_sponsor
|
|
434
226
|
// ═══════════════════════════════════════════════════════
|
|
435
227
|
api.registerTool({
|
|
436
228
|
name: "morpho_sponsor",
|
|
@@ -443,88 +235,31 @@ export default function register(api: any) {
|
|
|
443
235
|
agentAddress: { type: "string", description: "Target AgentAccount address (alternative to agentId)" },
|
|
444
236
|
amount: { type: "string", description: "Collateral amount (e.g. '0.05')" },
|
|
445
237
|
token: { type: "string", enum: ["WETH", "wstETH", "cbETH"], description: "Collateral token" },
|
|
446
|
-
borrow: { type: "string", description: "Optional: USDC amount to borrow for the agent
|
|
238
|
+
borrow: { type: "string", description: "Optional: USDC amount to borrow for the agent" },
|
|
447
239
|
},
|
|
448
240
|
required: ["amount", "token"],
|
|
449
241
|
},
|
|
450
242
|
async execute(_id: string, params: { agentId?: string; agentAddress?: string; amount: string; token: string; borrow?: string }) {
|
|
451
243
|
try {
|
|
244
|
+
if (!params.agentId && !params.agentAddress) return fail("Provide either agentId or agentAddress");
|
|
452
245
|
const cfg = getConfig(api);
|
|
453
|
-
const signer = getSigner(cfg);
|
|
454
246
|
const status = await getBackendStatus(cfg);
|
|
247
|
+
const client = createClient(cfg, status);
|
|
455
248
|
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
const tokenInfo = COLLATERAL_TOKENS[params.token];
|
|
461
|
-
if (!tokenInfo) return fail(`Unsupported token: ${params.token}`);
|
|
462
|
-
|
|
463
|
-
const morphoCreditAddr = status.contracts.morphoCredit;
|
|
464
|
-
let accountAddr: string;
|
|
465
|
-
|
|
466
|
-
// Resolve target AgentAccount
|
|
467
|
-
if (params.agentAddress) {
|
|
468
|
-
accountAddr = params.agentAddress;
|
|
469
|
-
} else {
|
|
470
|
-
const factory = new ethers.Contract(status.contracts.accountFactory, ACCOUNT_FACTORY_ABI, signer);
|
|
471
|
-
accountAddr = await factory.getAccount(params.agentId!);
|
|
472
|
-
if (accountAddr === ethers.ZeroAddress) {
|
|
473
|
-
return fail(`No AgentAccount for agent #${params.agentId}`);
|
|
474
|
-
}
|
|
475
|
-
}
|
|
476
|
-
|
|
477
|
-
const collateralWei = ethers.parseUnits(params.amount, tokenInfo.decimals);
|
|
478
|
-
|
|
479
|
-
// Check sponsor's balance
|
|
480
|
-
const token = new ethers.Contract(tokenInfo.address, ERC20_ABI, signer);
|
|
481
|
-
const balance = await token.balanceOf(signer.address);
|
|
482
|
-
if (balance < collateralWei) {
|
|
483
|
-
return fail(`Insufficient ${params.token}: have ${ethers.formatUnits(balance, tokenInfo.decimals)}, need ${params.amount}`);
|
|
484
|
-
}
|
|
249
|
+
const target = params.agentId
|
|
250
|
+
? { agentId: params.agentId }
|
|
251
|
+
: { address: params.agentAddress! };
|
|
485
252
|
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
await approveTx.wait();
|
|
489
|
-
|
|
490
|
-
const morpho = new ethers.Contract(morphoCreditAddr, MORPHO_CREDIT_ABI, signer);
|
|
491
|
-
const depositTx = await morpho.depositCollateralFor(accountAddr, tokenInfo.address, collateralWei);
|
|
492
|
-
await depositTx.wait();
|
|
493
|
-
|
|
494
|
-
const result: any = {
|
|
253
|
+
const result = await client.sponsor(target, params.token, params.amount, params.borrow);
|
|
254
|
+
return ok(JSON.stringify({
|
|
495
255
|
status: "sponsored",
|
|
496
|
-
target:
|
|
497
|
-
targetAgentId:
|
|
256
|
+
target: result.targetAccount,
|
|
257
|
+
targetAgentId: result.targetAgentId || "by address",
|
|
498
258
|
collateral: `${params.amount} ${params.token}`,
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
if (params.borrow) {
|
|
504
|
-
try {
|
|
505
|
-
const account = new ethers.Contract(accountAddr, AGENT_ACCOUNT_ABI, signer);
|
|
506
|
-
try {
|
|
507
|
-
const cpTx = await account.approveCreditProvider(morphoCreditAddr);
|
|
508
|
-
await cpTx.wait();
|
|
509
|
-
} catch { /* already approved or not owner */ }
|
|
510
|
-
|
|
511
|
-
const borrowWei = ethers.parseUnits(params.borrow, 6);
|
|
512
|
-
const morphoIface = new ethers.Interface(MORPHO_CREDIT_ABI);
|
|
513
|
-
const calldata = morphoIface.encodeFunctionData("drawWithCollateral", [tokenInfo.address, borrowWei]);
|
|
514
|
-
const borrowTx = await account.execute(morphoCreditAddr, 0, calldata);
|
|
515
|
-
await borrowTx.wait();
|
|
516
|
-
result.borrowed = `$${params.borrow}`;
|
|
517
|
-
result.borrowTx = borrowTx.hash;
|
|
518
|
-
} catch (e: any) {
|
|
519
|
-
result.borrowError = `Borrow failed (caller may not own this AgentAccount): ${e.message}`;
|
|
520
|
-
}
|
|
521
|
-
}
|
|
522
|
-
|
|
523
|
-
const pos = await morpho.getPosition(accountAddr, tokenInfo.address);
|
|
524
|
-
result.totalCollateral = `${ethers.formatUnits(pos.collateralAmount, tokenInfo.decimals)} ${params.token}`;
|
|
525
|
-
result.totalDebt = `$${ethers.formatUnits(pos.borrowedAmount, 6)}`;
|
|
526
|
-
|
|
527
|
-
return ok(JSON.stringify(result));
|
|
259
|
+
borrowed: result.borrowed ? `$${params.borrow}` : undefined,
|
|
260
|
+
depositTx: txLink(result.depositTx),
|
|
261
|
+
borrowTx: result.borrowTx ? txLink(result.borrowTx) : undefined,
|
|
262
|
+
}));
|
|
528
263
|
} catch (e) { return fail(e); }
|
|
529
264
|
},
|
|
530
265
|
});
|
|
@@ -546,45 +281,16 @@ export default function register(api: any) {
|
|
|
546
281
|
async execute(_id: string, params: { amount: string }) {
|
|
547
282
|
try {
|
|
548
283
|
const cfg = getConfig(api);
|
|
549
|
-
const signer = getSigner(cfg);
|
|
550
284
|
const status = await getBackendStatus(cfg);
|
|
551
|
-
const
|
|
552
|
-
|
|
553
|
-
const accountAddr = await getAccountAddr(signer, status.contracts.accountFactory, agentId);
|
|
554
|
-
const morpho = new ethers.Contract(status.contracts.morphoCredit, MORPHO_CREDIT_ABI, signer.provider!);
|
|
555
|
-
|
|
556
|
-
// Find active collateral
|
|
557
|
-
let activeToken: string | null = null;
|
|
558
|
-
for (const [symbol, info] of Object.entries(COLLATERAL_TOKENS)) {
|
|
559
|
-
const pos = await morpho.getPosition(accountAddr, info.address);
|
|
560
|
-
if (pos.collateralAmount > 0n) { activeToken = symbol; break; }
|
|
561
|
-
}
|
|
562
|
-
if (!activeToken) return fail("No collateral deposited. Use morpho_deposit first.");
|
|
563
|
-
|
|
564
|
-
const collateralAddr = COLLATERAL_TOKENS[activeToken].address;
|
|
565
|
-
const amountWei = ethers.parseUnits(params.amount, 6);
|
|
566
|
-
|
|
567
|
-
// Borrow via AgentAccount.execute → drawWithCollateral
|
|
568
|
-
const account = new ethers.Contract(accountAddr, AGENT_ACCOUNT_ABI, signer);
|
|
569
|
-
|
|
570
|
-
// Ensure credit provider approved (one-time, idempotent)
|
|
571
|
-
try { const cpTx = await account.approveCreditProvider(status.contracts.morphoCredit); await cpTx.wait(); } catch { /* already approved */ }
|
|
572
|
-
|
|
573
|
-
const morphoIface = new ethers.Interface(MORPHO_CREDIT_ABI);
|
|
574
|
-
const calldata = morphoIface.encodeFunctionData("drawWithCollateral", [collateralAddr, amountWei]);
|
|
575
|
-
|
|
576
|
-
const tx = await account.execute(status.contracts.morphoCredit, 0, calldata);
|
|
577
|
-
await tx.wait();
|
|
578
|
-
|
|
579
|
-
const totalDebt = await morpho.getTotalDebt(accountAddr);
|
|
580
|
-
|
|
285
|
+
const client = createClient(cfg, status);
|
|
286
|
+
const result = await client.borrow(params.amount);
|
|
581
287
|
return ok(JSON.stringify({
|
|
582
288
|
status: "borrowed",
|
|
583
289
|
amount: `$${params.amount}`,
|
|
584
|
-
collateral:
|
|
585
|
-
totalDebt:
|
|
586
|
-
agentAccount:
|
|
587
|
-
tx: tx
|
|
290
|
+
collateral: result.collateralToken,
|
|
291
|
+
totalDebt: result.totalDebt.toString(),
|
|
292
|
+
agentAccount: result.agentAccount,
|
|
293
|
+
tx: txLink(result.tx),
|
|
588
294
|
}));
|
|
589
295
|
} catch (e) { return fail(e); }
|
|
590
296
|
},
|
|
@@ -607,53 +313,14 @@ export default function register(api: any) {
|
|
|
607
313
|
async execute(_id: string, params: { amount: string }) {
|
|
608
314
|
try {
|
|
609
315
|
const cfg = getConfig(api);
|
|
610
|
-
const signer = getSigner(cfg);
|
|
611
316
|
const status = await getBackendStatus(cfg);
|
|
612
|
-
const
|
|
613
|
-
|
|
614
|
-
const accountAddr = await getAccountAddr(signer, status.contracts.accountFactory, agentId);
|
|
615
|
-
const morpho = new ethers.Contract(status.contracts.morphoCredit, MORPHO_CREDIT_ABI, signer.provider!);
|
|
616
|
-
|
|
617
|
-
// Find active collateral with debt
|
|
618
|
-
let activeToken: string | null = null;
|
|
619
|
-
for (const [symbol, info] of Object.entries(COLLATERAL_TOKENS)) {
|
|
620
|
-
const pos = await morpho.getPosition(accountAddr, info.address);
|
|
621
|
-
if (pos.borrowedAmount > 0n) { activeToken = symbol; break; }
|
|
622
|
-
}
|
|
623
|
-
if (!activeToken) return fail("No Morpho debt to repay");
|
|
624
|
-
|
|
625
|
-
const collateralAddr = COLLATERAL_TOKENS[activeToken].address;
|
|
626
|
-
const amountWei = ethers.parseUnits(params.amount, 6);
|
|
627
|
-
|
|
628
|
-
// Check USDC in AgentAccount
|
|
629
|
-
const usdc = new ethers.Contract(status.contracts.usdc, ERC20_ABI, signer.provider!);
|
|
630
|
-
const accBalance = await usdc.balanceOf(accountAddr);
|
|
631
|
-
if (accBalance < amountWei) {
|
|
632
|
-
return fail(`Insufficient USDC in AgentAccount: have $${ethers.formatUnits(accBalance, 6)}, need $${params.amount}`);
|
|
633
|
-
}
|
|
634
|
-
|
|
635
|
-
const account = new ethers.Contract(accountAddr, AGENT_ACCOUNT_ABI, signer);
|
|
636
|
-
const morphoIface = new ethers.Interface(MORPHO_CREDIT_ABI);
|
|
637
|
-
const erc20Iface = new ethers.Interface(ERC20_ABI);
|
|
638
|
-
|
|
639
|
-
// Single tx via executeBatch: approve USDC + repayWithCollateral
|
|
640
|
-
const tx = await account.executeBatch(
|
|
641
|
-
[status.contracts.usdc, status.contracts.morphoCredit],
|
|
642
|
-
[0, 0],
|
|
643
|
-
[
|
|
644
|
-
erc20Iface.encodeFunctionData("approve", [status.contracts.morphoCredit, amountWei]),
|
|
645
|
-
morphoIface.encodeFunctionData("repayWithCollateral", [collateralAddr, amountWei]),
|
|
646
|
-
],
|
|
647
|
-
);
|
|
648
|
-
await tx.wait();
|
|
649
|
-
|
|
650
|
-
const totalDebt = await morpho.getTotalDebt(accountAddr);
|
|
651
|
-
|
|
317
|
+
const client = createClient(cfg, status);
|
|
318
|
+
const result = await client.repay(params.amount);
|
|
652
319
|
return ok(JSON.stringify({
|
|
653
320
|
status: "repaid",
|
|
654
321
|
amount: `$${params.amount}`,
|
|
655
|
-
remainingDebt:
|
|
656
|
-
tx: tx
|
|
322
|
+
remainingDebt: result.remainingDebt.toString(),
|
|
323
|
+
tx: txLink(result.tx),
|
|
657
324
|
}));
|
|
658
325
|
} catch (e) { return fail(e); }
|
|
659
326
|
},
|
|
@@ -677,56 +344,23 @@ export default function register(api: any) {
|
|
|
677
344
|
async execute(_id: string, params: { amount: string; token: string }) {
|
|
678
345
|
try {
|
|
679
346
|
const cfg = getConfig(api);
|
|
680
|
-
const signer = getSigner(cfg);
|
|
681
347
|
const status = await getBackendStatus(cfg);
|
|
682
|
-
const
|
|
683
|
-
|
|
684
|
-
const tokenInfo = COLLATERAL_TOKENS[params.token];
|
|
685
|
-
if (!tokenInfo) return fail(`Unsupported token: ${params.token}`);
|
|
686
|
-
|
|
687
|
-
const accountAddr = await getAccountAddr(signer, status.contracts.accountFactory, agentId);
|
|
688
|
-
const morpho = new ethers.Contract(status.contracts.morphoCredit, MORPHO_CREDIT_ABI, signer.provider!);
|
|
689
|
-
const pos = await morpho.getPosition(accountAddr, tokenInfo.address);
|
|
690
|
-
|
|
691
|
-
if (pos.collateralAmount === 0n) return fail(`No ${params.token} collateral deposited`);
|
|
692
|
-
|
|
693
|
-
const withdrawAmount = params.amount.toLowerCase() === "all" ? pos.collateralAmount : ethers.parseUnits(params.amount, tokenInfo.decimals);
|
|
694
|
-
|
|
695
|
-
if (withdrawAmount > pos.collateralAmount) {
|
|
696
|
-
return fail(`Cannot withdraw more than deposited: max ${ethers.formatUnits(pos.collateralAmount, tokenInfo.decimals)} ${params.token}`);
|
|
697
|
-
}
|
|
698
|
-
|
|
699
|
-
const account = new ethers.Contract(accountAddr, AGENT_ACCOUNT_ABI, signer);
|
|
700
|
-
const morphoIface = new ethers.Interface(MORPHO_CREDIT_ABI);
|
|
701
|
-
const erc20Iface = new ethers.Interface(ERC20_ABI);
|
|
702
|
-
|
|
703
|
-
// Single tx via executeBatch: withdrawCollateral + transfer to EOA
|
|
704
|
-
const tx = await account.executeBatch(
|
|
705
|
-
[status.contracts.morphoCredit, tokenInfo.address],
|
|
706
|
-
[0, 0],
|
|
707
|
-
[
|
|
708
|
-
morphoIface.encodeFunctionData("withdrawCollateral", [tokenInfo.address, withdrawAmount]),
|
|
709
|
-
erc20Iface.encodeFunctionData("transfer", [signer.address, withdrawAmount]),
|
|
710
|
-
],
|
|
711
|
-
);
|
|
712
|
-
await tx.wait();
|
|
713
|
-
|
|
714
|
-
const newPos = await morpho.getPosition(accountAddr, tokenInfo.address);
|
|
715
|
-
|
|
348
|
+
const client = createClient(cfg, status);
|
|
349
|
+
const result = await client.withdraw(params.token, params.amount);
|
|
716
350
|
return ok(JSON.stringify({
|
|
717
351
|
status: "withdrawn",
|
|
718
|
-
amount:
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
destination:
|
|
722
|
-
tx: tx
|
|
352
|
+
amount: result.amount.toString(),
|
|
353
|
+
token: result.token,
|
|
354
|
+
remainingCollateral: result.remainingCollateral.toString(),
|
|
355
|
+
destination: result.destination,
|
|
356
|
+
tx: txLink(result.tx),
|
|
723
357
|
}));
|
|
724
358
|
} catch (e) { return fail(e); }
|
|
725
359
|
},
|
|
726
360
|
});
|
|
727
361
|
|
|
728
362
|
// ═══════════════════════════════════════════════════════
|
|
729
|
-
// TOOL: morpho_compare
|
|
363
|
+
// TOOL: morpho_compare (backend API)
|
|
730
364
|
// ═══════════════════════════════════════════════════════
|
|
731
365
|
api.registerTool({
|
|
732
366
|
name: "morpho_compare",
|
|
@@ -743,27 +377,20 @@ export default function register(api: any) {
|
|
|
743
377
|
try {
|
|
744
378
|
const cfg = getConfig(api);
|
|
745
379
|
const { data } = await axios.get(`${cfg.backendUrl}/morpho/estimate/${params.amount}`);
|
|
746
|
-
const
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
const balances: Record<string, string> = {};
|
|
750
|
-
for (const [symbol, info] of Object.entries(COLLATERAL_TOKENS)) {
|
|
751
|
-
const token = new ethers.Contract(info.address, ERC20_ABI, signer.provider!);
|
|
752
|
-
const bal = await token.balanceOf(signer.address);
|
|
753
|
-
balances[symbol] = ethers.formatUnits(bal, info.decimals);
|
|
754
|
-
}
|
|
755
|
-
|
|
380
|
+
const status = await getBackendStatus(cfg);
|
|
381
|
+
const client = createClient(cfg, status);
|
|
382
|
+
const balances = await client.getBalances();
|
|
756
383
|
return ok(JSON.stringify({
|
|
757
384
|
borrowAmount: `$${params.amount}`,
|
|
758
385
|
estimates: data,
|
|
759
|
-
walletBalances: balances,
|
|
386
|
+
walletBalances: { eth: balances.eth, usdc: balances.usdc },
|
|
760
387
|
}, null, 2));
|
|
761
388
|
} catch (e) { return fail(e); }
|
|
762
389
|
},
|
|
763
390
|
});
|
|
764
391
|
|
|
765
392
|
// ═══════════════════════════════════════════════════════
|
|
766
|
-
// TOOL: morpho_markets
|
|
393
|
+
// TOOL: morpho_markets (backend API)
|
|
767
394
|
// ═══════════════════════════════════════════════════════
|
|
768
395
|
api.registerTool({
|
|
769
396
|
name: "morpho_markets",
|
|
@@ -779,7 +406,7 @@ export default function register(api: any) {
|
|
|
779
406
|
});
|
|
780
407
|
|
|
781
408
|
// ═══════════════════════════════════════════════════════
|
|
782
|
-
// TOOL: agether_score
|
|
409
|
+
// TOOL: agether_score (backend API)
|
|
783
410
|
// ═══════════════════════════════════════════════════════
|
|
784
411
|
api.registerTool({
|
|
785
412
|
name: "agether_score",
|
|
@@ -788,9 +415,9 @@ export default function register(api: any) {
|
|
|
788
415
|
async execute() {
|
|
789
416
|
try {
|
|
790
417
|
const cfg = getConfig(api);
|
|
791
|
-
const signer = getSigner(cfg);
|
|
792
418
|
const status = await getBackendStatus(cfg);
|
|
793
|
-
const
|
|
419
|
+
const client = createClient(cfg, status);
|
|
420
|
+
const agentId = await client.getAgentId();
|
|
794
421
|
const { data } = await axios.get(`${cfg.backendUrl}/credit/score/${agentId}`);
|
|
795
422
|
return ok(JSON.stringify(data, null, 2));
|
|
796
423
|
} catch (e) { return fail(e); }
|
|
@@ -813,97 +440,66 @@ export default function register(api: any) {
|
|
|
813
440
|
async execute(_id: string, params: { amount: string }) {
|
|
814
441
|
try {
|
|
815
442
|
const cfg = getConfig(api);
|
|
816
|
-
const signer = getSigner(cfg);
|
|
817
443
|
const status = await getBackendStatus(cfg);
|
|
818
|
-
const
|
|
819
|
-
|
|
820
|
-
const accountAddr = await getAccountAddr(signer, status.contracts.accountFactory, agentId);
|
|
821
|
-
const amountWei = ethers.parseUnits(params.amount, 6);
|
|
822
|
-
|
|
823
|
-
const usdc = new ethers.Contract(status.contracts.usdc, ERC20_ABI, signer);
|
|
824
|
-
const balance = await usdc.balanceOf(signer.address);
|
|
825
|
-
if (balance < amountWei) {
|
|
826
|
-
return fail(`Insufficient USDC: have $${ethers.formatUnits(balance, 6)}, need $${params.amount}`);
|
|
827
|
-
}
|
|
828
|
-
|
|
829
|
-
const tx = await usdc.transfer(accountAddr, amountWei);
|
|
830
|
-
await tx.wait();
|
|
831
|
-
|
|
444
|
+
const client = createClient(cfg, status);
|
|
445
|
+
const result = await client.fundAccount(params.amount);
|
|
832
446
|
return ok(JSON.stringify({
|
|
833
447
|
status: "funded",
|
|
834
448
|
amount: `$${params.amount}`,
|
|
835
|
-
agentAccount:
|
|
836
|
-
tx: tx
|
|
449
|
+
agentAccount: result.agentAccount,
|
|
450
|
+
tx: txLink(result.tx),
|
|
837
451
|
}));
|
|
838
452
|
} catch (e) { return fail(e); }
|
|
839
453
|
},
|
|
840
454
|
});
|
|
841
455
|
|
|
842
456
|
// ═══════════════════════════════════════════════════════
|
|
843
|
-
// TOOL: x402_pay
|
|
457
|
+
// TOOL: x402_pay
|
|
844
458
|
// ═══════════════════════════════════════════════════════
|
|
845
459
|
api.registerTool({
|
|
846
460
|
name: "x402_pay",
|
|
847
461
|
description:
|
|
848
462
|
"Make a paid API call using x402 protocol. Pays with USDC from AgentAccount via EIP-3009 signature. " +
|
|
849
|
-
"If autoDraw is enabled
|
|
850
|
-
"credit line when AgentAccount balance is insufficient.",
|
|
463
|
+
"If autoDraw is enabled, automatically borrows USDC from Morpho credit line when balance is insufficient.",
|
|
851
464
|
parameters: {
|
|
852
465
|
type: "object",
|
|
853
466
|
properties: {
|
|
854
467
|
url: { type: "string", description: "API endpoint URL" },
|
|
855
468
|
method: { type: "string", enum: ["GET", "POST"], description: "HTTP method (default: GET)" },
|
|
856
469
|
body: { type: "string", description: "JSON body for POST requests" },
|
|
857
|
-
autoDraw: { type: "boolean", description: "Auto-borrow from Morpho if USDC insufficient
|
|
470
|
+
autoDraw: { type: "boolean", description: "Auto-borrow from Morpho if USDC insufficient" },
|
|
858
471
|
},
|
|
859
472
|
required: ["url"],
|
|
860
473
|
},
|
|
861
474
|
async execute(_id: string, params: { url: string; method?: string; body?: string; autoDraw?: boolean }) {
|
|
862
475
|
try {
|
|
863
476
|
const cfg = getConfig(api);
|
|
864
|
-
const signer = getSigner(cfg);
|
|
865
477
|
const status = await getBackendStatus(cfg);
|
|
478
|
+
const client = createClient(cfg, status);
|
|
866
479
|
|
|
867
|
-
// Resolve AgentAccount address for x402 payments
|
|
868
480
|
let accountAddress: string | undefined;
|
|
869
481
|
let agentId: string | undefined;
|
|
870
482
|
try {
|
|
871
|
-
agentId = await
|
|
872
|
-
accountAddress = await
|
|
483
|
+
agentId = await client.getAgentId();
|
|
484
|
+
accountAddress = await client.getAccountAddress();
|
|
873
485
|
} catch { /* no account, pay from EOA */ }
|
|
874
486
|
|
|
875
|
-
// Auto-draw:
|
|
487
|
+
// Auto-draw: borrow $10 if balance < $1
|
|
876
488
|
const shouldAutoDraw = params.autoDraw ?? cfg.autoDraw;
|
|
877
|
-
if (shouldAutoDraw && accountAddress
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
let collateralAddr: string | null = null;
|
|
885
|
-
for (const [, info] of Object.entries(COLLATERAL_TOKENS)) {
|
|
886
|
-
const pos = await morpho.getPosition(accountAddress, info.address);
|
|
887
|
-
if (pos.collateralAmount > 0n) { collateralAddr = info.address; break; }
|
|
888
|
-
}
|
|
889
|
-
if (collateralAddr) {
|
|
890
|
-
const account = new ethers.Contract(accountAddress, AGENT_ACCOUNT_ABI, signer);
|
|
891
|
-
const morphoIface = new ethers.Interface(MORPHO_CREDIT_ABI);
|
|
892
|
-
const drawAmount = ethers.parseUnits("10", 6); // $10 auto-draw
|
|
893
|
-
const calldata = morphoIface.encodeFunctionData("drawWithCollateral", [collateralAddr, drawAmount]);
|
|
894
|
-
const tx = await account.execute(status.contracts.morphoCredit, 0, calldata);
|
|
895
|
-
await tx.wait();
|
|
896
|
-
}
|
|
897
|
-
} catch { /* auto-draw failed — proceed anyway, x402 might still work from EOA */ }
|
|
898
|
-
}
|
|
489
|
+
if (shouldAutoDraw && accountAddress) {
|
|
490
|
+
try {
|
|
491
|
+
const accUsdc = await client.getAccountUSDC();
|
|
492
|
+
if (accUsdc < 1_000_000n) {
|
|
493
|
+
await client.borrow("10");
|
|
494
|
+
}
|
|
495
|
+
} catch { /* auto-draw failed, proceed anyway */ }
|
|
899
496
|
}
|
|
900
497
|
|
|
901
|
-
// Use SDK's X402Client directly (no execSync)
|
|
902
498
|
const x402 = new X402Client({
|
|
903
499
|
privateKey: cfg.privateKey,
|
|
904
|
-
rpcUrl: cfg.rpcUrl
|
|
905
|
-
backendUrl: cfg.backendUrl
|
|
906
|
-
agentId
|
|
500
|
+
rpcUrl: cfg.rpcUrl,
|
|
501
|
+
backendUrl: cfg.backendUrl,
|
|
502
|
+
agentId,
|
|
907
503
|
accountAddress,
|
|
908
504
|
});
|
|
909
505
|
|
|
@@ -914,13 +510,14 @@ export default function register(api: any) {
|
|
|
914
510
|
result = await x402.get(params.url);
|
|
915
511
|
}
|
|
916
512
|
|
|
917
|
-
if (!result.success)
|
|
918
|
-
return fail(result.error || "x402 payment failed");
|
|
919
|
-
}
|
|
513
|
+
if (!result.success) return fail(result.error || "x402 payment failed");
|
|
920
514
|
|
|
921
515
|
const response: any = { status: "paid", data: result.data };
|
|
922
516
|
if (result.paymentInfo) {
|
|
923
|
-
response.payment =
|
|
517
|
+
response.payment = {
|
|
518
|
+
...result.paymentInfo,
|
|
519
|
+
tx: result.paymentInfo.txHash ? txLink(result.paymentInfo.txHash) : undefined,
|
|
520
|
+
};
|
|
924
521
|
}
|
|
925
522
|
return ok(JSON.stringify(response, null, 2));
|
|
926
523
|
} catch (e) { return fail(e); }
|
|
@@ -928,7 +525,7 @@ export default function register(api: any) {
|
|
|
928
525
|
});
|
|
929
526
|
|
|
930
527
|
// ═══════════════════════════════════════════════════════
|
|
931
|
-
//
|
|
528
|
+
// SLASH COMMANDS (no AI needed)
|
|
932
529
|
// ═══════════════════════════════════════════════════════
|
|
933
530
|
|
|
934
531
|
api.registerCommand({
|
|
@@ -937,29 +534,17 @@ export default function register(api: any) {
|
|
|
937
534
|
handler: async () => {
|
|
938
535
|
try {
|
|
939
536
|
const cfg = getConfig(api);
|
|
940
|
-
const signer = getSigner(cfg);
|
|
941
|
-
const provider = signer.provider!;
|
|
942
|
-
const ethBal = await provider.getBalance(signer.address);
|
|
943
537
|
const status = await getBackendStatus(cfg);
|
|
944
|
-
const
|
|
945
|
-
const
|
|
946
|
-
|
|
947
|
-
let
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
text += `USDC: $${ethers.formatUnits(usdcBal, 6)}`;
|
|
954
|
-
|
|
955
|
-
if (agentId && status.contracts.accountFactory) {
|
|
956
|
-
try {
|
|
957
|
-
const accAddr = await getAccountAddr(signer, status.contracts.accountFactory, agentId);
|
|
958
|
-
const accUsdc = await usdc.balanceOf(accAddr);
|
|
959
|
-
text += `\n\n🏦 AgentAccount: ${accAddr}\nUSDC: $${ethers.formatUnits(accUsdc, 6)}`;
|
|
960
|
-
} catch { /* no account */ }
|
|
538
|
+
const client = createClient(cfg, status);
|
|
539
|
+
const b = await client.getBalances();
|
|
540
|
+
|
|
541
|
+
let text = `💰 Agent #${b.agentId}\n`;
|
|
542
|
+
text += `Address: ${b.address}\n`;
|
|
543
|
+
text += `ETH: ${parseFloat(b.eth).toFixed(6)}\n`;
|
|
544
|
+
text += `USDC: $${b.usdc}`;
|
|
545
|
+
if (b.agentAccount) {
|
|
546
|
+
text += `\n\n🏦 AgentAccount: ${b.agentAccount.address}\nUSDC: $${b.agentAccount.usdc}`;
|
|
961
547
|
}
|
|
962
|
-
|
|
963
548
|
return { text };
|
|
964
549
|
} catch (e: any) {
|
|
965
550
|
return { text: `❌ ${e.message}` };
|
|
@@ -973,23 +558,14 @@ export default function register(api: any) {
|
|
|
973
558
|
handler: async () => {
|
|
974
559
|
try {
|
|
975
560
|
const cfg = getConfig(api);
|
|
976
|
-
const signer = getSigner(cfg);
|
|
977
561
|
const status = await getBackendStatus(cfg);
|
|
978
|
-
const
|
|
979
|
-
|
|
980
|
-
const accountAddr = await getAccountAddr(signer, status.contracts.accountFactory, agentId);
|
|
981
|
-
const morpho = new ethers.Contract(status.contracts.morphoCredit, MORPHO_CREDIT_ABI, signer.provider!);
|
|
982
|
-
const totalDebt = await morpho.getTotalDebt(accountAddr);
|
|
562
|
+
const client = createClient(cfg, status);
|
|
563
|
+
const s = await client.getStatus();
|
|
983
564
|
|
|
984
|
-
let text = `📊 Morpho
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
const pos = await morpho.getPosition(accountAddr, info.address);
|
|
988
|
-
if (pos.collateralAmount > 0n || pos.borrowedAmount > 0n) {
|
|
989
|
-
text += `\n${symbol}: ${ethers.formatUnits(pos.collateralAmount, info.decimals)} collateral, $${ethers.formatUnits(pos.borrowedAmount, 6)} debt`;
|
|
990
|
-
}
|
|
565
|
+
let text = `📊 Morpho\nAccount: ${s.agentAccount}\nTotal debt: ${s.totalDebt}\n`;
|
|
566
|
+
for (const p of s.positions) {
|
|
567
|
+
text += `\n${p.token}: ${p.collateral} collateral, ${p.debt} debt`;
|
|
991
568
|
}
|
|
992
|
-
|
|
993
569
|
return { text };
|
|
994
570
|
} catch (e: any) {
|
|
995
571
|
return { text: `❌ ${e.message}` };
|