@agether/openclaw-plugin 1.4.0 → 1.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/package.json +2 -2
- package/src/index.ts +145 -575
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agether/openclaw-plugin",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.0",
|
|
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,23 @@ 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
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
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
|
-
}
|
|
93
|
-
|
|
94
|
-
function fail(err: unknown) {
|
|
95
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
96
|
-
return { content: [{ type: "text" as const, text: `❌ ${msg}` }], isError: true };
|
|
65
|
+
function createClient(cfg: PluginConfig, status: any): MorphoCreditClient {
|
|
66
|
+
return new MorphoCreditClient({
|
|
67
|
+
privateKey: cfg.privateKey,
|
|
68
|
+
rpcUrl: cfg.rpcUrl,
|
|
69
|
+
agentId: cfg.agentId,
|
|
70
|
+
contracts: {
|
|
71
|
+
morphoCredit: status.contracts.morphoCredit,
|
|
72
|
+
accountFactory: status.contracts.accountFactory,
|
|
73
|
+
usdc: status.contracts.usdc,
|
|
74
|
+
agentRegistry: status.contracts.agentRegistry,
|
|
75
|
+
},
|
|
76
|
+
});
|
|
97
77
|
}
|
|
98
78
|
|
|
99
79
|
// ─── Plugin Entry ─────────────────────────────────────────
|
|
@@ -110,38 +90,9 @@ export default function register(api: any) {
|
|
|
110
90
|
async execute() {
|
|
111
91
|
try {
|
|
112
92
|
const cfg = getConfig(api);
|
|
113
|
-
const signer = getSigner(cfg);
|
|
114
93
|
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
|
-
|
|
94
|
+
const client = createClient(cfg, status);
|
|
95
|
+
const result = await client.getBalances();
|
|
145
96
|
return ok(JSON.stringify(result, null, 2));
|
|
146
97
|
} catch (e) { return fail(e); }
|
|
147
98
|
},
|
|
@@ -153,7 +104,7 @@ export default function register(api: any) {
|
|
|
153
104
|
api.registerTool({
|
|
154
105
|
name: "agether_register",
|
|
155
106
|
description:
|
|
156
|
-
"Register a new ERC-8004 agent identity on Base and create an AgentAccount. Returns the new agentId.
|
|
107
|
+
"Register a new ERC-8004 agent identity on Base and create an AgentAccount. Returns the new agentId.",
|
|
157
108
|
parameters: {
|
|
158
109
|
type: "object",
|
|
159
110
|
properties: {
|
|
@@ -164,82 +115,15 @@ export default function register(api: any) {
|
|
|
164
115
|
async execute(_id: string, params: { name: string }) {
|
|
165
116
|
try {
|
|
166
117
|
const cfg = getConfig(api);
|
|
167
|
-
const signer = getSigner(cfg);
|
|
168
118
|
const status = await getBackendStatus(cfg);
|
|
169
|
-
|
|
170
|
-
const
|
|
171
|
-
|
|
172
|
-
let agentId: bigint;
|
|
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
|
-
|
|
119
|
+
const client = createClient(cfg, status);
|
|
120
|
+
const result = await client.register(params.name);
|
|
237
121
|
return ok(JSON.stringify({
|
|
238
|
-
status: "registered",
|
|
239
|
-
agentId: agentId
|
|
240
|
-
address:
|
|
241
|
-
agentAccount:
|
|
242
|
-
tx: tx.
|
|
122
|
+
status: result.alreadyRegistered ? "already_registered" : "registered",
|
|
123
|
+
agentId: result.agentId,
|
|
124
|
+
address: result.address,
|
|
125
|
+
agentAccount: result.agentAccount,
|
|
126
|
+
tx: result.tx ? txLink(result.tx) : undefined,
|
|
243
127
|
}));
|
|
244
128
|
} catch (e) { return fail(e); }
|
|
245
129
|
},
|
|
@@ -256,33 +140,10 @@ export default function register(api: any) {
|
|
|
256
140
|
async execute() {
|
|
257
141
|
try {
|
|
258
142
|
const cfg = getConfig(api);
|
|
259
|
-
const signer = getSigner(cfg);
|
|
260
143
|
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));
|
|
144
|
+
const client = createClient(cfg, status);
|
|
145
|
+
const result = await client.getStatus();
|
|
146
|
+
return ok(JSON.stringify(result, null, 2));
|
|
286
147
|
} catch (e) { return fail(e); }
|
|
287
148
|
},
|
|
288
149
|
});
|
|
@@ -305,55 +166,22 @@ export default function register(api: any) {
|
|
|
305
166
|
async execute(_id: string, params: { amount: string; token: string }) {
|
|
306
167
|
try {
|
|
307
168
|
const cfg = getConfig(api);
|
|
308
|
-
const signer = getSigner(cfg);
|
|
309
169
|
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
|
-
|
|
170
|
+
const client = createClient(cfg, status);
|
|
171
|
+
const result = await client.deposit(params.token, params.amount);
|
|
344
172
|
return ok(JSON.stringify({
|
|
345
173
|
status: "deposited",
|
|
346
174
|
amount: `${params.amount} ${params.token}`,
|
|
347
|
-
totalCollateral:
|
|
348
|
-
agentAccount:
|
|
349
|
-
tx:
|
|
175
|
+
totalCollateral: result.totalCollateral.toString(),
|
|
176
|
+
agentAccount: result.agentAccount,
|
|
177
|
+
tx: txLink(result.tx),
|
|
350
178
|
}));
|
|
351
179
|
} catch (e) { return fail(e); }
|
|
352
180
|
},
|
|
353
181
|
});
|
|
354
182
|
|
|
355
183
|
// ═══════════════════════════════════════════════════════
|
|
356
|
-
// TOOL: morpho_deposit_and_borrow
|
|
184
|
+
// TOOL: morpho_deposit_and_borrow
|
|
357
185
|
// ═══════════════════════════════════════════════════════
|
|
358
186
|
api.registerTool({
|
|
359
187
|
name: "morpho_deposit_and_borrow",
|
|
@@ -371,66 +199,24 @@ export default function register(api: any) {
|
|
|
371
199
|
async execute(_id: string, params: { collateralAmount: string; token: string; borrowAmount: string }) {
|
|
372
200
|
try {
|
|
373
201
|
const cfg = getConfig(api);
|
|
374
|
-
const signer = getSigner(cfg);
|
|
375
202
|
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
|
-
|
|
203
|
+
const client = createClient(cfg, status);
|
|
204
|
+
const result = await client.depositAndBorrow(params.token, params.collateralAmount, params.borrowAmount);
|
|
418
205
|
return ok(JSON.stringify({
|
|
419
206
|
status: "deposited_and_borrowed",
|
|
420
207
|
collateral: `${params.collateralAmount} ${params.token}`,
|
|
421
208
|
borrowed: `$${params.borrowAmount}`,
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
borrowTx: borrowTx.hash,
|
|
209
|
+
totalDebt: result.totalDebt.toString(),
|
|
210
|
+
agentAccount: result.agentAccount,
|
|
211
|
+
depositTx: txLink(result.depositTx),
|
|
212
|
+
borrowTx: txLink(result.borrowTx),
|
|
427
213
|
}));
|
|
428
214
|
} catch (e) { return fail(e); }
|
|
429
215
|
},
|
|
430
216
|
});
|
|
431
217
|
|
|
432
218
|
// ═══════════════════════════════════════════════════════
|
|
433
|
-
// TOOL: morpho_sponsor
|
|
219
|
+
// TOOL: morpho_sponsor
|
|
434
220
|
// ═══════════════════════════════════════════════════════
|
|
435
221
|
api.registerTool({
|
|
436
222
|
name: "morpho_sponsor",
|
|
@@ -443,88 +229,31 @@ export default function register(api: any) {
|
|
|
443
229
|
agentAddress: { type: "string", description: "Target AgentAccount address (alternative to agentId)" },
|
|
444
230
|
amount: { type: "string", description: "Collateral amount (e.g. '0.05')" },
|
|
445
231
|
token: { type: "string", enum: ["WETH", "wstETH", "cbETH"], description: "Collateral token" },
|
|
446
|
-
borrow: { type: "string", description: "Optional: USDC amount to borrow for the agent
|
|
232
|
+
borrow: { type: "string", description: "Optional: USDC amount to borrow for the agent" },
|
|
447
233
|
},
|
|
448
234
|
required: ["amount", "token"],
|
|
449
235
|
},
|
|
450
236
|
async execute(_id: string, params: { agentId?: string; agentAddress?: string; amount: string; token: string; borrow?: string }) {
|
|
451
237
|
try {
|
|
238
|
+
if (!params.agentId && !params.agentAddress) return fail("Provide either agentId or agentAddress");
|
|
452
239
|
const cfg = getConfig(api);
|
|
453
|
-
const signer = getSigner(cfg);
|
|
454
240
|
const status = await getBackendStatus(cfg);
|
|
241
|
+
const client = createClient(cfg, status);
|
|
455
242
|
|
|
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
|
-
}
|
|
485
|
-
|
|
486
|
-
// Approve + deposit for target
|
|
487
|
-
const approveTx = await token.approve(morphoCreditAddr, collateralWei);
|
|
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();
|
|
243
|
+
const target = params.agentId
|
|
244
|
+
? { agentId: params.agentId }
|
|
245
|
+
: { address: params.agentAddress! };
|
|
493
246
|
|
|
494
|
-
const result
|
|
247
|
+
const result = await client.sponsor(target, params.token, params.amount, params.borrow);
|
|
248
|
+
return ok(JSON.stringify({
|
|
495
249
|
status: "sponsored",
|
|
496
|
-
target:
|
|
497
|
-
targetAgentId:
|
|
250
|
+
target: result.targetAccount,
|
|
251
|
+
targetAgentId: result.targetAgentId || "by address",
|
|
498
252
|
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));
|
|
253
|
+
borrowed: result.borrowed ? `$${params.borrow}` : undefined,
|
|
254
|
+
depositTx: txLink(result.depositTx),
|
|
255
|
+
borrowTx: result.borrowTx ? txLink(result.borrowTx) : undefined,
|
|
256
|
+
}));
|
|
528
257
|
} catch (e) { return fail(e); }
|
|
529
258
|
},
|
|
530
259
|
});
|
|
@@ -546,45 +275,16 @@ export default function register(api: any) {
|
|
|
546
275
|
async execute(_id: string, params: { amount: string }) {
|
|
547
276
|
try {
|
|
548
277
|
const cfg = getConfig(api);
|
|
549
|
-
const signer = getSigner(cfg);
|
|
550
278
|
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
|
-
|
|
279
|
+
const client = createClient(cfg, status);
|
|
280
|
+
const result = await client.borrow(params.amount);
|
|
581
281
|
return ok(JSON.stringify({
|
|
582
282
|
status: "borrowed",
|
|
583
283
|
amount: `$${params.amount}`,
|
|
584
|
-
collateral:
|
|
585
|
-
totalDebt:
|
|
586
|
-
agentAccount:
|
|
587
|
-
tx: tx
|
|
284
|
+
collateral: result.collateralToken,
|
|
285
|
+
totalDebt: result.totalDebt.toString(),
|
|
286
|
+
agentAccount: result.agentAccount,
|
|
287
|
+
tx: txLink(result.tx),
|
|
588
288
|
}));
|
|
589
289
|
} catch (e) { return fail(e); }
|
|
590
290
|
},
|
|
@@ -607,53 +307,14 @@ export default function register(api: any) {
|
|
|
607
307
|
async execute(_id: string, params: { amount: string }) {
|
|
608
308
|
try {
|
|
609
309
|
const cfg = getConfig(api);
|
|
610
|
-
const signer = getSigner(cfg);
|
|
611
310
|
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
|
-
|
|
311
|
+
const client = createClient(cfg, status);
|
|
312
|
+
const result = await client.repay(params.amount);
|
|
652
313
|
return ok(JSON.stringify({
|
|
653
314
|
status: "repaid",
|
|
654
315
|
amount: `$${params.amount}`,
|
|
655
|
-
remainingDebt:
|
|
656
|
-
tx: tx
|
|
316
|
+
remainingDebt: result.remainingDebt.toString(),
|
|
317
|
+
tx: txLink(result.tx),
|
|
657
318
|
}));
|
|
658
319
|
} catch (e) { return fail(e); }
|
|
659
320
|
},
|
|
@@ -677,56 +338,23 @@ export default function register(api: any) {
|
|
|
677
338
|
async execute(_id: string, params: { amount: string; token: string }) {
|
|
678
339
|
try {
|
|
679
340
|
const cfg = getConfig(api);
|
|
680
|
-
const signer = getSigner(cfg);
|
|
681
341
|
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
|
-
|
|
342
|
+
const client = createClient(cfg, status);
|
|
343
|
+
const result = await client.withdraw(params.token, params.amount);
|
|
716
344
|
return ok(JSON.stringify({
|
|
717
345
|
status: "withdrawn",
|
|
718
|
-
amount:
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
destination:
|
|
722
|
-
tx: tx
|
|
346
|
+
amount: result.amount.toString(),
|
|
347
|
+
token: result.token,
|
|
348
|
+
remainingCollateral: result.remainingCollateral.toString(),
|
|
349
|
+
destination: result.destination,
|
|
350
|
+
tx: txLink(result.tx),
|
|
723
351
|
}));
|
|
724
352
|
} catch (e) { return fail(e); }
|
|
725
353
|
},
|
|
726
354
|
});
|
|
727
355
|
|
|
728
356
|
// ═══════════════════════════════════════════════════════
|
|
729
|
-
// TOOL: morpho_compare
|
|
357
|
+
// TOOL: morpho_compare (backend API)
|
|
730
358
|
// ═══════════════════════════════════════════════════════
|
|
731
359
|
api.registerTool({
|
|
732
360
|
name: "morpho_compare",
|
|
@@ -743,27 +371,20 @@ export default function register(api: any) {
|
|
|
743
371
|
try {
|
|
744
372
|
const cfg = getConfig(api);
|
|
745
373
|
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
|
-
|
|
374
|
+
const status = await getBackendStatus(cfg);
|
|
375
|
+
const client = createClient(cfg, status);
|
|
376
|
+
const balances = await client.getBalances();
|
|
756
377
|
return ok(JSON.stringify({
|
|
757
378
|
borrowAmount: `$${params.amount}`,
|
|
758
379
|
estimates: data,
|
|
759
|
-
walletBalances: balances,
|
|
380
|
+
walletBalances: { eth: balances.eth, usdc: balances.usdc },
|
|
760
381
|
}, null, 2));
|
|
761
382
|
} catch (e) { return fail(e); }
|
|
762
383
|
},
|
|
763
384
|
});
|
|
764
385
|
|
|
765
386
|
// ═══════════════════════════════════════════════════════
|
|
766
|
-
// TOOL: morpho_markets
|
|
387
|
+
// TOOL: morpho_markets (backend API)
|
|
767
388
|
// ═══════════════════════════════════════════════════════
|
|
768
389
|
api.registerTool({
|
|
769
390
|
name: "morpho_markets",
|
|
@@ -779,7 +400,7 @@ export default function register(api: any) {
|
|
|
779
400
|
});
|
|
780
401
|
|
|
781
402
|
// ═══════════════════════════════════════════════════════
|
|
782
|
-
// TOOL: agether_score
|
|
403
|
+
// TOOL: agether_score (backend API)
|
|
783
404
|
// ═══════════════════════════════════════════════════════
|
|
784
405
|
api.registerTool({
|
|
785
406
|
name: "agether_score",
|
|
@@ -788,9 +409,9 @@ export default function register(api: any) {
|
|
|
788
409
|
async execute() {
|
|
789
410
|
try {
|
|
790
411
|
const cfg = getConfig(api);
|
|
791
|
-
const signer = getSigner(cfg);
|
|
792
412
|
const status = await getBackendStatus(cfg);
|
|
793
|
-
const
|
|
413
|
+
const client = createClient(cfg, status);
|
|
414
|
+
const agentId = await client.getAgentId();
|
|
794
415
|
const { data } = await axios.get(`${cfg.backendUrl}/credit/score/${agentId}`);
|
|
795
416
|
return ok(JSON.stringify(data, null, 2));
|
|
796
417
|
} catch (e) { return fail(e); }
|
|
@@ -813,97 +434,66 @@ export default function register(api: any) {
|
|
|
813
434
|
async execute(_id: string, params: { amount: string }) {
|
|
814
435
|
try {
|
|
815
436
|
const cfg = getConfig(api);
|
|
816
|
-
const signer = getSigner(cfg);
|
|
817
437
|
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
|
-
|
|
438
|
+
const client = createClient(cfg, status);
|
|
439
|
+
const result = await client.fundAccount(params.amount);
|
|
832
440
|
return ok(JSON.stringify({
|
|
833
441
|
status: "funded",
|
|
834
442
|
amount: `$${params.amount}`,
|
|
835
|
-
agentAccount:
|
|
836
|
-
tx: tx
|
|
443
|
+
agentAccount: result.agentAccount,
|
|
444
|
+
tx: txLink(result.tx),
|
|
837
445
|
}));
|
|
838
446
|
} catch (e) { return fail(e); }
|
|
839
447
|
},
|
|
840
448
|
});
|
|
841
449
|
|
|
842
450
|
// ═══════════════════════════════════════════════════════
|
|
843
|
-
// TOOL: x402_pay
|
|
451
|
+
// TOOL: x402_pay
|
|
844
452
|
// ═══════════════════════════════════════════════════════
|
|
845
453
|
api.registerTool({
|
|
846
454
|
name: "x402_pay",
|
|
847
455
|
description:
|
|
848
456
|
"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.",
|
|
457
|
+
"If autoDraw is enabled, automatically borrows USDC from Morpho credit line when balance is insufficient.",
|
|
851
458
|
parameters: {
|
|
852
459
|
type: "object",
|
|
853
460
|
properties: {
|
|
854
461
|
url: { type: "string", description: "API endpoint URL" },
|
|
855
462
|
method: { type: "string", enum: ["GET", "POST"], description: "HTTP method (default: GET)" },
|
|
856
463
|
body: { type: "string", description: "JSON body for POST requests" },
|
|
857
|
-
autoDraw: { type: "boolean", description: "Auto-borrow from Morpho if USDC insufficient
|
|
464
|
+
autoDraw: { type: "boolean", description: "Auto-borrow from Morpho if USDC insufficient" },
|
|
858
465
|
},
|
|
859
466
|
required: ["url"],
|
|
860
467
|
},
|
|
861
468
|
async execute(_id: string, params: { url: string; method?: string; body?: string; autoDraw?: boolean }) {
|
|
862
469
|
try {
|
|
863
470
|
const cfg = getConfig(api);
|
|
864
|
-
const signer = getSigner(cfg);
|
|
865
471
|
const status = await getBackendStatus(cfg);
|
|
472
|
+
const client = createClient(cfg, status);
|
|
866
473
|
|
|
867
|
-
// Resolve AgentAccount address for x402 payments
|
|
868
474
|
let accountAddress: string | undefined;
|
|
869
475
|
let agentId: string | undefined;
|
|
870
476
|
try {
|
|
871
|
-
agentId = await
|
|
872
|
-
accountAddress = await
|
|
477
|
+
agentId = await client.getAgentId();
|
|
478
|
+
accountAddress = await client.getAccountAddress();
|
|
873
479
|
} catch { /* no account, pay from EOA */ }
|
|
874
480
|
|
|
875
|
-
// Auto-draw:
|
|
481
|
+
// Auto-draw: borrow $10 if balance < $1
|
|
876
482
|
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
|
-
}
|
|
483
|
+
if (shouldAutoDraw && accountAddress) {
|
|
484
|
+
try {
|
|
485
|
+
const accUsdc = await client.getAccountUSDC();
|
|
486
|
+
if (accUsdc < 1_000_000n) {
|
|
487
|
+
await client.borrow("10");
|
|
488
|
+
}
|
|
489
|
+
} catch { /* auto-draw failed, proceed anyway */ }
|
|
899
490
|
}
|
|
900
491
|
|
|
901
|
-
// Use SDK's X402Client directly (no execSync)
|
|
902
492
|
const x402 = new X402Client({
|
|
903
493
|
privateKey: cfg.privateKey,
|
|
904
|
-
rpcUrl: cfg.rpcUrl
|
|
905
|
-
backendUrl: cfg.backendUrl
|
|
906
|
-
agentId
|
|
494
|
+
rpcUrl: cfg.rpcUrl,
|
|
495
|
+
backendUrl: cfg.backendUrl,
|
|
496
|
+
agentId,
|
|
907
497
|
accountAddress,
|
|
908
498
|
});
|
|
909
499
|
|
|
@@ -914,13 +504,14 @@ export default function register(api: any) {
|
|
|
914
504
|
result = await x402.get(params.url);
|
|
915
505
|
}
|
|
916
506
|
|
|
917
|
-
if (!result.success)
|
|
918
|
-
return fail(result.error || "x402 payment failed");
|
|
919
|
-
}
|
|
507
|
+
if (!result.success) return fail(result.error || "x402 payment failed");
|
|
920
508
|
|
|
921
509
|
const response: any = { status: "paid", data: result.data };
|
|
922
510
|
if (result.paymentInfo) {
|
|
923
|
-
response.payment =
|
|
511
|
+
response.payment = {
|
|
512
|
+
...result.paymentInfo,
|
|
513
|
+
tx: result.paymentInfo.txHash ? txLink(result.paymentInfo.txHash) : undefined,
|
|
514
|
+
};
|
|
924
515
|
}
|
|
925
516
|
return ok(JSON.stringify(response, null, 2));
|
|
926
517
|
} catch (e) { return fail(e); }
|
|
@@ -928,7 +519,7 @@ export default function register(api: any) {
|
|
|
928
519
|
});
|
|
929
520
|
|
|
930
521
|
// ═══════════════════════════════════════════════════════
|
|
931
|
-
//
|
|
522
|
+
// SLASH COMMANDS (no AI needed)
|
|
932
523
|
// ═══════════════════════════════════════════════════════
|
|
933
524
|
|
|
934
525
|
api.registerCommand({
|
|
@@ -937,29 +528,17 @@ export default function register(api: any) {
|
|
|
937
528
|
handler: async () => {
|
|
938
529
|
try {
|
|
939
530
|
const cfg = getConfig(api);
|
|
940
|
-
const signer = getSigner(cfg);
|
|
941
|
-
const provider = signer.provider!;
|
|
942
|
-
const ethBal = await provider.getBalance(signer.address);
|
|
943
531
|
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 */ }
|
|
532
|
+
const client = createClient(cfg, status);
|
|
533
|
+
const b = await client.getBalances();
|
|
534
|
+
|
|
535
|
+
let text = `💰 Agent #${b.agentId}\n`;
|
|
536
|
+
text += `Address: ${b.address}\n`;
|
|
537
|
+
text += `ETH: ${parseFloat(b.eth).toFixed(6)}\n`;
|
|
538
|
+
text += `USDC: $${b.usdc}`;
|
|
539
|
+
if (b.agentAccount) {
|
|
540
|
+
text += `\n\n🏦 AgentAccount: ${b.agentAccount.address}\nUSDC: $${b.agentAccount.usdc}`;
|
|
961
541
|
}
|
|
962
|
-
|
|
963
542
|
return { text };
|
|
964
543
|
} catch (e: any) {
|
|
965
544
|
return { text: `❌ ${e.message}` };
|
|
@@ -973,23 +552,14 @@ export default function register(api: any) {
|
|
|
973
552
|
handler: async () => {
|
|
974
553
|
try {
|
|
975
554
|
const cfg = getConfig(api);
|
|
976
|
-
const signer = getSigner(cfg);
|
|
977
555
|
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);
|
|
556
|
+
const client = createClient(cfg, status);
|
|
557
|
+
const s = await client.getStatus();
|
|
983
558
|
|
|
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
|
-
}
|
|
559
|
+
let text = `📊 Morpho\nAccount: ${s.agentAccount}\nTotal debt: ${s.totalDebt}\n`;
|
|
560
|
+
for (const p of s.positions) {
|
|
561
|
+
text += `\n${p.token}: ${p.collateral} collateral, ${p.debt} debt`;
|
|
991
562
|
}
|
|
992
|
-
|
|
993
563
|
return { text };
|
|
994
564
|
} catch (e: any) {
|
|
995
565
|
return { text: `❌ ${e.message}` };
|