@agether/sdk 1.5.1 → 1.5.3
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/dist/MorphoClient-AV27HBOF.mjs +6 -0
- package/dist/X402Client-PY4FOTQC.mjs +6 -0
- package/dist/chunk-OMCWZ3VN.mjs +840 -0
- package/dist/chunk-PTXYOTCG.mjs +257 -0
- package/dist/cli.d.mts +1 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +2 -2
- package/dist/cli.mjs +680 -0
- package/dist/index.js +2 -2
- package/dist/index.mjs +31 -1089
- package/package.json +1 -1
package/dist/cli.mjs
ADDED
|
@@ -0,0 +1,680 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/cli.ts
|
|
4
|
+
import { ethers } from "ethers";
|
|
5
|
+
import * as fs from "fs";
|
|
6
|
+
import * as path from "path";
|
|
7
|
+
import * as os from "os";
|
|
8
|
+
var CONFIG_PATH = path.join(os.homedir(), ".agether", "config.json");
|
|
9
|
+
var DEFAULT_RPC = "https://base-rpc.publicnode.com";
|
|
10
|
+
var DEFAULT_BACKEND = "http://95.179.189.214:3001";
|
|
11
|
+
function loadConfig() {
|
|
12
|
+
try {
|
|
13
|
+
if (fs.existsSync(CONFIG_PATH)) {
|
|
14
|
+
return JSON.parse(fs.readFileSync(CONFIG_PATH, "utf-8"));
|
|
15
|
+
}
|
|
16
|
+
} catch {
|
|
17
|
+
}
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
function saveConfig(config) {
|
|
21
|
+
const dir = path.dirname(CONFIG_PATH);
|
|
22
|
+
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
23
|
+
fs.writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2));
|
|
24
|
+
}
|
|
25
|
+
function requireConfig() {
|
|
26
|
+
const config = loadConfig();
|
|
27
|
+
if (!config) {
|
|
28
|
+
console.error("\u274C Not initialized. Run: agether init <private-key>");
|
|
29
|
+
process.exit(1);
|
|
30
|
+
}
|
|
31
|
+
return config;
|
|
32
|
+
}
|
|
33
|
+
async function waitForTx(tx, retries = 5) {
|
|
34
|
+
for (let attempt = 1; attempt <= retries; attempt++) {
|
|
35
|
+
try {
|
|
36
|
+
const receipt = await tx.wait();
|
|
37
|
+
if (receipt) return receipt;
|
|
38
|
+
} catch (e) {
|
|
39
|
+
if (attempt === retries) throw e;
|
|
40
|
+
console.log(` \u23F3 Receipt fetch failed (attempt ${attempt}/${retries}), retrying...`);
|
|
41
|
+
await new Promise((r) => setTimeout(r, 2e3 * attempt));
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
throw new Error("Failed to get transaction receipt after retries");
|
|
45
|
+
}
|
|
46
|
+
async function getMorphoClient(config) {
|
|
47
|
+
const { MorphoClient } = await import("./MorphoClient-AV27HBOF.mjs");
|
|
48
|
+
return new MorphoClient({
|
|
49
|
+
privateKey: config.privateKey,
|
|
50
|
+
rpcUrl: config.rpcUrl,
|
|
51
|
+
agentId: config.agentId !== "0" ? config.agentId : void 0
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
async function getX402Client(config) {
|
|
55
|
+
const { X402Client } = await import("./X402Client-PY4FOTQC.mjs");
|
|
56
|
+
let accountAddress;
|
|
57
|
+
try {
|
|
58
|
+
const mc = await getMorphoClient(config);
|
|
59
|
+
accountAddress = await mc.getAccountAddress();
|
|
60
|
+
} catch {
|
|
61
|
+
}
|
|
62
|
+
return new X402Client({
|
|
63
|
+
privateKey: config.privateKey,
|
|
64
|
+
rpcUrl: config.rpcUrl,
|
|
65
|
+
backendUrl: config.backendUrl,
|
|
66
|
+
agentId: config.agentId,
|
|
67
|
+
accountAddress
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
var ERC8004_ABI = [
|
|
71
|
+
"function register(string agentURI) external returns (uint256 agentId)",
|
|
72
|
+
"function register() external returns (uint256 agentId)",
|
|
73
|
+
"function ownerOf(uint256 tokenId) view returns (address)",
|
|
74
|
+
"function balanceOf(address owner) view returns (uint256)",
|
|
75
|
+
"event Transfer(address indexed from, address indexed to, uint256 indexed tokenId)"
|
|
76
|
+
];
|
|
77
|
+
var ACCOUNT_FACTORY_ABI = [
|
|
78
|
+
"function getAccount(uint256 agentId) view returns (address)",
|
|
79
|
+
"function accountExists(uint256 agentId) view returns (bool)",
|
|
80
|
+
"function createAccount(uint256 agentId) returns (address account)",
|
|
81
|
+
"event AccountCreated(uint256 indexed agentId, address indexed account, address indexed owner)"
|
|
82
|
+
];
|
|
83
|
+
var VALIDATION_REGISTRY_ABI = [
|
|
84
|
+
"function isAgentCodeApproved(uint256 agentId) view returns (bool)"
|
|
85
|
+
];
|
|
86
|
+
var MOCK_ERC20_ABI = [
|
|
87
|
+
"function mint(address to, uint256 amount) external"
|
|
88
|
+
];
|
|
89
|
+
async function apiGet(backendUrl, endpoint) {
|
|
90
|
+
const res = await fetch(`${backendUrl}${endpoint}`);
|
|
91
|
+
return res.json();
|
|
92
|
+
}
|
|
93
|
+
function decodeError(error) {
|
|
94
|
+
const msg = error?.message || String(error);
|
|
95
|
+
if (msg.includes("CodeNotApproved")) return "Agent code not approved. Complete KYA first.";
|
|
96
|
+
if (msg.includes("0xda04aecc") || msg.includes("ExceedsMaxLtv"))
|
|
97
|
+
return "ExceedsMaxLtv \u2014 collateral too low for this borrow amount.";
|
|
98
|
+
if (msg.includes("0xfeca99cb") || msg.includes("ExecutionFailed"))
|
|
99
|
+
return "ExecutionFailed \u2014 the inner contract call reverted.";
|
|
100
|
+
if (msg.includes("0xa920ef9f"))
|
|
101
|
+
return "PositionNotActive \u2014 no collateral deposited for this token.";
|
|
102
|
+
if (msg.length > 200) return msg.slice(0, 200) + "...";
|
|
103
|
+
return msg;
|
|
104
|
+
}
|
|
105
|
+
async function cmdInit(privateKey, agentId) {
|
|
106
|
+
const rpcUrl = process.env.AGETHER_RPC_URL || DEFAULT_RPC;
|
|
107
|
+
const backendUrl = process.env.AGETHER_BACKEND_URL || DEFAULT_BACKEND;
|
|
108
|
+
let wallet;
|
|
109
|
+
try {
|
|
110
|
+
wallet = new ethers.Wallet(privateKey);
|
|
111
|
+
} catch {
|
|
112
|
+
console.error("\u274C Invalid private key");
|
|
113
|
+
process.exit(1);
|
|
114
|
+
}
|
|
115
|
+
const config = { privateKey, agentId: agentId || "0", rpcUrl, backendUrl };
|
|
116
|
+
saveConfig(config);
|
|
117
|
+
console.log("\u2705 Initialized Agether CLI");
|
|
118
|
+
console.log(` Address: ${wallet.address}`);
|
|
119
|
+
console.log(` RPC: ${rpcUrl}`);
|
|
120
|
+
console.log(` Backend: ${backendUrl}`);
|
|
121
|
+
console.log(` Config: ${CONFIG_PATH}`);
|
|
122
|
+
}
|
|
123
|
+
async function cmdRegister(name) {
|
|
124
|
+
const config = requireConfig();
|
|
125
|
+
const provider = new ethers.JsonRpcProvider(config.rpcUrl);
|
|
126
|
+
const signer = new ethers.Wallet(config.privateKey, provider);
|
|
127
|
+
const agentName = name || `Agent-${signer.address.slice(0, 8)}`;
|
|
128
|
+
console.log(`\u{1F916} Registering agent: ${agentName}
|
|
129
|
+
`);
|
|
130
|
+
console.log(` Wallet: ${signer.address}`);
|
|
131
|
+
console.log("\n [1/4] Fetching contract addresses...");
|
|
132
|
+
let contracts;
|
|
133
|
+
try {
|
|
134
|
+
const statusResp = await apiGet(config.backendUrl, "/status");
|
|
135
|
+
contracts = statusResp.contracts || {};
|
|
136
|
+
console.log(" \u2713 Backend OK");
|
|
137
|
+
} catch (e) {
|
|
138
|
+
console.error(` \u274C Failed to reach backend: ${e.message}`);
|
|
139
|
+
process.exit(1);
|
|
140
|
+
}
|
|
141
|
+
const registryAddr = contracts.agentRegistry || contracts.identityRegistry;
|
|
142
|
+
const factoryAddr = contracts.accountFactory;
|
|
143
|
+
const validationAddr = contracts.validationRegistry;
|
|
144
|
+
if (!registryAddr || !factoryAddr) {
|
|
145
|
+
console.error(" \u274C Backend missing agentRegistry or accountFactory");
|
|
146
|
+
process.exit(1);
|
|
147
|
+
}
|
|
148
|
+
console.log(" [2/4] Registering on ERC-8004 IdentityRegistry...");
|
|
149
|
+
const agentRegistry = new ethers.Contract(registryAddr, ERC8004_ABI, signer);
|
|
150
|
+
let agentId;
|
|
151
|
+
if (config.agentId && config.agentId !== "0") {
|
|
152
|
+
agentId = BigInt(config.agentId);
|
|
153
|
+
try {
|
|
154
|
+
const owner = await agentRegistry.ownerOf(agentId);
|
|
155
|
+
if (owner.toLowerCase() === signer.address.toLowerCase()) {
|
|
156
|
+
console.log(` \u2713 Already registered as Agent #${agentId}`);
|
|
157
|
+
} else {
|
|
158
|
+
console.error(" \u274C agentId does not belong to this wallet");
|
|
159
|
+
process.exit(1);
|
|
160
|
+
}
|
|
161
|
+
} catch {
|
|
162
|
+
console.error(` \u274C agentId ${agentId} does not exist on-chain`);
|
|
163
|
+
process.exit(1);
|
|
164
|
+
}
|
|
165
|
+
} else {
|
|
166
|
+
const existingBalance = await agentRegistry.balanceOf(signer.address);
|
|
167
|
+
if (existingBalance > 0n) {
|
|
168
|
+
console.log(` \u26A0 Wallet already owns ${existingBalance} token(s), minting another`);
|
|
169
|
+
}
|
|
170
|
+
try {
|
|
171
|
+
const registrationFile = JSON.stringify({
|
|
172
|
+
type: "https://eips.ethereum.org/EIPS/eip-8004#registration-v1",
|
|
173
|
+
name: agentName,
|
|
174
|
+
description: "AI agent registered via Agether CLI",
|
|
175
|
+
active: true
|
|
176
|
+
});
|
|
177
|
+
const agentURI = `data:application/json;base64,${Buffer.from(registrationFile).toString("base64")}`;
|
|
178
|
+
const tx = await agentRegistry["register(string)"](agentURI);
|
|
179
|
+
const receipt = await waitForTx(tx);
|
|
180
|
+
const transferTopic = ethers.id("Transfer(address,address,uint256)");
|
|
181
|
+
const transferLog = receipt.logs.find((log) => log.topics[0] === transferTopic);
|
|
182
|
+
if (transferLog && transferLog.topics.length >= 4) {
|
|
183
|
+
agentId = BigInt(transferLog.topics[3]);
|
|
184
|
+
} else {
|
|
185
|
+
console.error(" \u274C Could not parse agentId from receipt");
|
|
186
|
+
process.exit(1);
|
|
187
|
+
}
|
|
188
|
+
console.log(` \u2713 Agent #${agentId} registered`);
|
|
189
|
+
console.log(` TX: ${tx.hash}`);
|
|
190
|
+
config.agentId = agentId.toString();
|
|
191
|
+
saveConfig(config);
|
|
192
|
+
} catch (e) {
|
|
193
|
+
if (config.agentId && config.agentId !== "0") {
|
|
194
|
+
agentId = BigInt(config.agentId);
|
|
195
|
+
console.log(` Using existing Agent #${agentId}`);
|
|
196
|
+
} else {
|
|
197
|
+
const balance = await agentRegistry.balanceOf(signer.address);
|
|
198
|
+
if (balance > 0n) {
|
|
199
|
+
console.error(` Wallet owns ${balance} token(s) but agentId unknown.`);
|
|
200
|
+
console.error(" Set manually: agether init <pk> --agent-id <id>");
|
|
201
|
+
} else {
|
|
202
|
+
console.error(` \u274C Registration failed: ${e.message}`);
|
|
203
|
+
}
|
|
204
|
+
process.exit(1);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
const network = await provider.getNetwork();
|
|
209
|
+
const chainId = Number(network.chainId);
|
|
210
|
+
if (chainId === 31337 || chainId === 1) {
|
|
211
|
+
console.log(" [3/4] Minting test USDC (Hardhat fork)...");
|
|
212
|
+
const deployerPk = "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80";
|
|
213
|
+
const deployer = new ethers.Wallet(deployerPk, provider);
|
|
214
|
+
const usdcAddr = contracts.usdc;
|
|
215
|
+
if (usdcAddr) {
|
|
216
|
+
try {
|
|
217
|
+
const usdc = new ethers.Contract(usdcAddr, MOCK_ERC20_ABI, deployer);
|
|
218
|
+
const tx = await usdc.mint(signer.address, BigInt(5e10));
|
|
219
|
+
await waitForTx(tx);
|
|
220
|
+
console.log(" \u2713 Minted $50,000 USDC");
|
|
221
|
+
} catch {
|
|
222
|
+
console.log(" \u26A0 Mint failed (probably real network)");
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
} else {
|
|
226
|
+
console.log(" [3/4] Skipping USDC mint (real network)");
|
|
227
|
+
}
|
|
228
|
+
console.log(" [4/4] Creating AgentAccount...");
|
|
229
|
+
if (factoryAddr) {
|
|
230
|
+
const factory = new ethers.Contract(factoryAddr, ACCOUNT_FACTORY_ABI, signer);
|
|
231
|
+
try {
|
|
232
|
+
const exists = await factory.accountExists(agentId);
|
|
233
|
+
if (exists) {
|
|
234
|
+
const addr = await factory.getAccount(agentId);
|
|
235
|
+
console.log(` Already exists: ${addr}`);
|
|
236
|
+
} else {
|
|
237
|
+
const tx = await factory.createAccount(agentId);
|
|
238
|
+
await waitForTx(tx);
|
|
239
|
+
const addr = await factory.getAccount(agentId);
|
|
240
|
+
console.log(` \u2713 Created: ${addr}`);
|
|
241
|
+
console.log(` TX: ${tx.hash}`);
|
|
242
|
+
}
|
|
243
|
+
} catch (e) {
|
|
244
|
+
console.error(` \u274C Failed: ${e.message}`);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
if (validationAddr) {
|
|
248
|
+
const vr = new ethers.Contract(validationAddr, VALIDATION_REGISTRY_ABI, provider);
|
|
249
|
+
try {
|
|
250
|
+
const approved = await vr.isAgentCodeApproved(agentId);
|
|
251
|
+
console.log(`
|
|
252
|
+
KYA Status: ${approved ? "\u2705 Approved" : "\u23F3 Pending"}`);
|
|
253
|
+
} catch {
|
|
254
|
+
console.log("\n KYA Status: \u26A0 Could not check");
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
console.log(`
|
|
258
|
+
\u2705 Agent #${agentId} ready!`);
|
|
259
|
+
console.log(` Config: ${CONFIG_PATH}`);
|
|
260
|
+
console.log("\n Next steps:");
|
|
261
|
+
console.log(" agether balance");
|
|
262
|
+
console.log(" agether deposit --amount 0.05 --token WETH");
|
|
263
|
+
console.log(" agether borrow --amount 100");
|
|
264
|
+
}
|
|
265
|
+
async function cmdBalance() {
|
|
266
|
+
const config = requireConfig();
|
|
267
|
+
const mc = await getMorphoClient(config);
|
|
268
|
+
const balances = await mc.getBalances();
|
|
269
|
+
console.log(`
|
|
270
|
+
\u{1F4B0} Agent #${balances.agentId} Balances
|
|
271
|
+
`);
|
|
272
|
+
console.log(` EOA: ${balances.address}`);
|
|
273
|
+
console.log(` ETH: ${parseFloat(balances.eth).toFixed(6)}`);
|
|
274
|
+
console.log(` USDC: $${parseFloat(balances.usdc).toFixed(2)}`);
|
|
275
|
+
if (balances.agentAccount) {
|
|
276
|
+
console.log(`
|
|
277
|
+
AgentAccount: ${balances.agentAccount.address}`);
|
|
278
|
+
console.log(` ETH: ${parseFloat(balances.agentAccount.eth).toFixed(6)}`);
|
|
279
|
+
console.log(` USDC: $${parseFloat(balances.agentAccount.usdc).toFixed(2)}`);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
async function cmdStatus() {
|
|
283
|
+
const config = requireConfig();
|
|
284
|
+
const mc = await getMorphoClient(config);
|
|
285
|
+
console.log("\n\u{1F4CA} Morpho Positions\n");
|
|
286
|
+
const status = await mc.getStatus();
|
|
287
|
+
console.log(` Agent #${status.agentId}`);
|
|
288
|
+
console.log(` Account: ${status.agentAccount}`);
|
|
289
|
+
console.log(` Total Debt: $${parseFloat(status.totalDebt).toFixed(2)}`);
|
|
290
|
+
if (status.positions.length === 0) {
|
|
291
|
+
console.log("\n No active positions. Deposit collateral first:");
|
|
292
|
+
console.log(" agether deposit --amount 0.05 --token WETH");
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
console.log("\n \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510");
|
|
296
|
+
console.log(" \u2502 Token \u2502 Collateral \u2502 Debt (USDC) \u2502");
|
|
297
|
+
console.log(" \u251C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524");
|
|
298
|
+
for (const p of status.positions) {
|
|
299
|
+
const tok = p.collateralToken.padEnd(9);
|
|
300
|
+
const col = parseFloat(p.collateral).toFixed(6).padStart(16);
|
|
301
|
+
const debt = ("$" + parseFloat(p.debt).toFixed(2)).padStart(16);
|
|
302
|
+
console.log(` \u2502 ${tok} \u2502 ${col} \u2502 ${debt} \u2502`);
|
|
303
|
+
}
|
|
304
|
+
console.log(" \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518");
|
|
305
|
+
try {
|
|
306
|
+
const score = await mc.getCreditScore();
|
|
307
|
+
const { fresh, age } = await mc.isScoreFresh();
|
|
308
|
+
const ageHrs = Number(age) / 3600;
|
|
309
|
+
console.log(`
|
|
310
|
+
Credit Score: ${score} ${fresh ? "\u2705" : `\u26A0\uFE0F stale (${ageHrs.toFixed(1)}h old)`}`);
|
|
311
|
+
} catch {
|
|
312
|
+
console.log("\n Credit Score: not yet computed");
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
async function cmdScore() {
|
|
316
|
+
const config = requireConfig();
|
|
317
|
+
console.log(`
|
|
318
|
+
\u{1F4C8} Agent #${config.agentId} Score
|
|
319
|
+
`);
|
|
320
|
+
try {
|
|
321
|
+
const data = await apiGet(config.backendUrl, `/score/${config.agentId}/current`);
|
|
322
|
+
if (data.score !== void 0) {
|
|
323
|
+
console.log(` On-chain Score: ${data.score}`);
|
|
324
|
+
console.log(` Timestamp: ${data.timestamp ? new Date(Number(data.timestamp) * 1e3).toISOString() : "N/A"}`);
|
|
325
|
+
console.log(` Signer: ${data.signer || "N/A"}`);
|
|
326
|
+
console.log(` Fresh: ${data.fresh ? "\u2705" : "\u26A0\uFE0F stale"}`);
|
|
327
|
+
} else {
|
|
328
|
+
console.log(" No score on-chain yet.");
|
|
329
|
+
}
|
|
330
|
+
} catch {
|
|
331
|
+
console.log(" Could not fetch current score.");
|
|
332
|
+
}
|
|
333
|
+
console.log("\n To compute a fresh score (x402-gated, costs USDC):");
|
|
334
|
+
console.log(` agether x402 ${config.backendUrl}/score/${config.agentId}`);
|
|
335
|
+
}
|
|
336
|
+
async function cmdMarkets() {
|
|
337
|
+
const config = requireConfig();
|
|
338
|
+
const mc = await getMorphoClient(config);
|
|
339
|
+
console.log("\n\u{1F4C8} Morpho Blue Markets (Base USDC)\n");
|
|
340
|
+
const markets = await mc.getMarkets();
|
|
341
|
+
if (markets.length === 0) {
|
|
342
|
+
console.log(" No markets found. API may be unavailable.");
|
|
343
|
+
return;
|
|
344
|
+
}
|
|
345
|
+
console.log(" \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510");
|
|
346
|
+
console.log(" \u2502 Collateral \u2502 LLTV \u2502 Util % \u2502 Supply (USDC) \u2502");
|
|
347
|
+
console.log(" \u251C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524");
|
|
348
|
+
for (const m of markets.slice(0, 15)) {
|
|
349
|
+
const col = (m.collateralAsset?.symbol || "N/A").padEnd(13);
|
|
350
|
+
const lltv = (Number(m.lltv) / 1e18 * 100).toFixed(0).padStart(6) + "%";
|
|
351
|
+
const util = (m.utilization * 100).toFixed(1).padStart(8) + "%";
|
|
352
|
+
const supply = ("$" + (Number(m.totalSupplyAssets) / 1e6).toLocaleString()).padStart(16);
|
|
353
|
+
console.log(` \u2502 ${col} \u2502 ${lltv} \u2502 ${util} \u2502 ${supply} \u2502`);
|
|
354
|
+
}
|
|
355
|
+
console.log(" \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518");
|
|
356
|
+
}
|
|
357
|
+
async function cmdDeposit(amount, token) {
|
|
358
|
+
const config = requireConfig();
|
|
359
|
+
const mc = await getMorphoClient(config);
|
|
360
|
+
console.log(`
|
|
361
|
+
\u{1F4B0} Depositing ${amount} ${token} as collateral...
|
|
362
|
+
`);
|
|
363
|
+
try {
|
|
364
|
+
const result = await mc.supplyCollateral(token, amount);
|
|
365
|
+
console.log(`\u2705 Deposited ${amount} ${token}`);
|
|
366
|
+
console.log(` AgentAccount: ${result.agentAccount}`);
|
|
367
|
+
console.log(` TX: ${result.tx}`);
|
|
368
|
+
} catch (e) {
|
|
369
|
+
console.error(`\u274C ${decodeError(e)}`);
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
async function cmdBorrow(amount, token) {
|
|
373
|
+
const config = requireConfig();
|
|
374
|
+
const mc = await getMorphoClient(config);
|
|
375
|
+
console.log(`
|
|
376
|
+
\u{1F4B8} Borrowing $${amount} USDC...
|
|
377
|
+
`);
|
|
378
|
+
try {
|
|
379
|
+
const result = await mc.borrow(amount, token);
|
|
380
|
+
console.log(`\u2705 Borrowed $${amount} USDC`);
|
|
381
|
+
console.log(` Collateral: ${result.collateralToken}`);
|
|
382
|
+
console.log(` AgentAccount: ${result.agentAccount}`);
|
|
383
|
+
console.log(` TX: ${result.tx}`);
|
|
384
|
+
} catch (e) {
|
|
385
|
+
console.error(`\u274C ${decodeError(e)}`);
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
async function cmdDepositAndBorrow(collateralAmount, token, borrowAmount) {
|
|
389
|
+
const config = requireConfig();
|
|
390
|
+
const mc = await getMorphoClient(config);
|
|
391
|
+
console.log(`
|
|
392
|
+
\uFFFD\uFFFD Depositing ${collateralAmount} ${token} + Borrowing $${borrowAmount}...
|
|
393
|
+
`);
|
|
394
|
+
try {
|
|
395
|
+
const result = await mc.depositAndBorrow(token, collateralAmount, borrowAmount);
|
|
396
|
+
console.log(`\u2705 Deposited ${collateralAmount} ${token} & Borrowed $${borrowAmount} USDC`);
|
|
397
|
+
console.log(` AgentAccount: ${result.agentAccount}`);
|
|
398
|
+
console.log(` TX: ${result.tx}`);
|
|
399
|
+
} catch (e) {
|
|
400
|
+
console.error(`\u274C ${decodeError(e)}`);
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
async function cmdRepay(amount, token) {
|
|
404
|
+
const config = requireConfig();
|
|
405
|
+
const mc = await getMorphoClient(config);
|
|
406
|
+
console.log(`
|
|
407
|
+
\u{1F4B3} Repaying $${amount} USDC...
|
|
408
|
+
`);
|
|
409
|
+
try {
|
|
410
|
+
const result = await mc.repay(amount, token);
|
|
411
|
+
console.log(`\u2705 Repaid $${amount}`);
|
|
412
|
+
console.log(` Remaining debt: $${parseFloat(result.remainingDebt).toFixed(2)}`);
|
|
413
|
+
console.log(` TX: ${result.tx}`);
|
|
414
|
+
} catch (e) {
|
|
415
|
+
console.error(`\u274C ${decodeError(e)}`);
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
async function cmdWithdraw(amount, token) {
|
|
419
|
+
const config = requireConfig();
|
|
420
|
+
const mc = await getMorphoClient(config);
|
|
421
|
+
console.log(`
|
|
422
|
+
\u{1F4E4} Withdrawing ${amount} ${token}...
|
|
423
|
+
`);
|
|
424
|
+
try {
|
|
425
|
+
const result = await mc.withdrawCollateral(token, amount);
|
|
426
|
+
console.log(`\u2705 Withdrew ${result.amount} ${token}`);
|
|
427
|
+
console.log(` Remaining collateral: ${result.remainingCollateral}`);
|
|
428
|
+
console.log(` Destination: ${result.destination}`);
|
|
429
|
+
console.log(` TX: ${result.tx}`);
|
|
430
|
+
} catch (e) {
|
|
431
|
+
console.error(`\u274C ${decodeError(e)}`);
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
async function cmdSponsor(amount, token, agentId, address) {
|
|
435
|
+
const config = requireConfig();
|
|
436
|
+
const mc = await getMorphoClient(config);
|
|
437
|
+
const target = agentId ? { agentId } : { address };
|
|
438
|
+
console.log(`
|
|
439
|
+
\u{1F381} Sponsoring ${amount} ${token} to ${agentId || address}...
|
|
440
|
+
`);
|
|
441
|
+
try {
|
|
442
|
+
const result = await mc.sponsor(target, token, amount);
|
|
443
|
+
console.log(`\u2705 Sponsored ${amount} ${token}`);
|
|
444
|
+
console.log(` Target: ${result.targetAccount}`);
|
|
445
|
+
console.log(` TX: ${result.tx}`);
|
|
446
|
+
} catch (e) {
|
|
447
|
+
console.error(`\u274C ${decodeError(e)}`);
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
async function cmdFund(amount) {
|
|
451
|
+
const config = requireConfig();
|
|
452
|
+
const mc = await getMorphoClient(config);
|
|
453
|
+
console.log(`
|
|
454
|
+
\u{1F4B5} Funding AgentAccount with $${amount} USDC...
|
|
455
|
+
`);
|
|
456
|
+
try {
|
|
457
|
+
const result = await mc.fundAccount(amount);
|
|
458
|
+
console.log(`\u2705 Funded $${amount} USDC`);
|
|
459
|
+
console.log(` AgentAccount: ${result.agentAccount}`);
|
|
460
|
+
console.log(` TX: ${result.tx}`);
|
|
461
|
+
} catch (e) {
|
|
462
|
+
console.error(`\u274C ${decodeError(e)}`);
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
async function cmdX402Call(url, method = "GET", body) {
|
|
466
|
+
const config = requireConfig();
|
|
467
|
+
console.log("\n\u{1F510} x402 Paid API Call\n");
|
|
468
|
+
const x402 = await getX402Client(config);
|
|
469
|
+
console.log(` Wallet: ${new ethers.Wallet(config.privateKey).address}`);
|
|
470
|
+
console.log(`
|
|
471
|
+
\u{1F4E1} ${method} ${url}`);
|
|
472
|
+
if (body) console.log(`\u{1F4E6} Body: ${body}`);
|
|
473
|
+
try {
|
|
474
|
+
let result;
|
|
475
|
+
if (method === "POST" && body) {
|
|
476
|
+
result = await x402.post(url, JSON.parse(body));
|
|
477
|
+
} else {
|
|
478
|
+
result = await x402.get(url);
|
|
479
|
+
}
|
|
480
|
+
if (result.success) {
|
|
481
|
+
console.log("\n\u2705 Success!");
|
|
482
|
+
if (result.paymentInfo) {
|
|
483
|
+
console.log(`\u{1F4B0} Paid: ${result.paymentInfo.amount} ${result.paymentInfo.asset} on ${result.paymentInfo.network}`);
|
|
484
|
+
if (result.paymentInfo.txHash) console.log(`\u{1F4DC} TX: ${result.paymentInfo.txHash}`);
|
|
485
|
+
}
|
|
486
|
+
console.log("\n\u{1F4C4} Response:");
|
|
487
|
+
console.log(JSON.stringify(result.data, null, 2));
|
|
488
|
+
} else {
|
|
489
|
+
console.error(`
|
|
490
|
+
\u274C Failed: ${result.error}`);
|
|
491
|
+
}
|
|
492
|
+
} catch (e) {
|
|
493
|
+
console.error(`\u274C Error: ${e.message}`);
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
function cmdHelp() {
|
|
497
|
+
console.log(`
|
|
498
|
+
\u{1F3E6} Agether CLI \u2014 Direct Morpho Blue Credit for AI Agents
|
|
499
|
+
|
|
500
|
+
USAGE:
|
|
501
|
+
agether <command> [options]
|
|
502
|
+
|
|
503
|
+
SETUP:
|
|
504
|
+
init <private-key> [--agent-id <id>] Initialize with private key
|
|
505
|
+
register [--name <n>] Register ERC-8004 + create AgentAccount
|
|
506
|
+
|
|
507
|
+
INFO:
|
|
508
|
+
balance Check ETH + USDC balances
|
|
509
|
+
status Show Morpho positions & credit score
|
|
510
|
+
score Get credit score
|
|
511
|
+
markets List Morpho Blue USDC markets
|
|
512
|
+
|
|
513
|
+
MORPHO LENDING:
|
|
514
|
+
deposit --amount <n> --token <t> Deposit collateral (WETH, wstETH, cbETH)
|
|
515
|
+
borrow --amount <usd> [--token <t>] Borrow USDC against collateral
|
|
516
|
+
deposit-and-borrow --amount <n> --token <t> --borrow <usd>
|
|
517
|
+
Deposit + borrow in one batched tx
|
|
518
|
+
repay --amount <usd> [--token <t>] Repay borrowed USDC
|
|
519
|
+
withdraw --amount <n> --token <t> Withdraw collateral (use 'all' for max)
|
|
520
|
+
sponsor --amount <n> --token <t> --agent-id <id> Send collateral to another agent
|
|
521
|
+
fund --amount <usd> Transfer USDC from EOA to AgentAccount
|
|
522
|
+
|
|
523
|
+
x402 PAYMENTS:
|
|
524
|
+
x402 <url> [--method GET|POST] [--body <json>] Make a paid API call
|
|
525
|
+
|
|
526
|
+
ENVIRONMENT:
|
|
527
|
+
AGETHER_RPC_URL RPC endpoint (default: ${DEFAULT_RPC})
|
|
528
|
+
AGETHER_BACKEND_URL Backend URL (default: ${DEFAULT_BACKEND})
|
|
529
|
+
|
|
530
|
+
EXAMPLE FLOW:
|
|
531
|
+
agether init 0xYOUR_PRIVATE_KEY
|
|
532
|
+
agether register --name "MyAgent"
|
|
533
|
+
agether deposit --amount 0.05 --token WETH # ~$125 collateral
|
|
534
|
+
agether borrow --amount 50 # Borrow $50 USDC
|
|
535
|
+
agether status # Check positions
|
|
536
|
+
agether repay --amount 50 # Repay when done
|
|
537
|
+
agether withdraw --amount all --token WETH # Withdraw collateral
|
|
538
|
+
`);
|
|
539
|
+
}
|
|
540
|
+
function parseArgs(args) {
|
|
541
|
+
const command = args[0] || "help";
|
|
542
|
+
const positional = [];
|
|
543
|
+
const options = {};
|
|
544
|
+
for (let i = 1; i < args.length; i++) {
|
|
545
|
+
const arg = args[i];
|
|
546
|
+
if (arg.startsWith("--")) {
|
|
547
|
+
const key = arg.slice(2);
|
|
548
|
+
const next = args[i + 1];
|
|
549
|
+
if (next && !next.startsWith("--")) {
|
|
550
|
+
options[key] = next;
|
|
551
|
+
i++;
|
|
552
|
+
} else {
|
|
553
|
+
options[key] = true;
|
|
554
|
+
}
|
|
555
|
+
} else {
|
|
556
|
+
positional.push(arg);
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
return { command, positional, options };
|
|
560
|
+
}
|
|
561
|
+
async function main() {
|
|
562
|
+
const { command, positional, options } = parseArgs(process.argv.slice(2));
|
|
563
|
+
try {
|
|
564
|
+
switch (command) {
|
|
565
|
+
case "init":
|
|
566
|
+
if (!positional[0]) {
|
|
567
|
+
console.error("\u274C Private key required: agether init <private-key>");
|
|
568
|
+
process.exit(1);
|
|
569
|
+
}
|
|
570
|
+
await cmdInit(positional[0], options["agent-id"]);
|
|
571
|
+
break;
|
|
572
|
+
case "register":
|
|
573
|
+
await cmdRegister(options.name);
|
|
574
|
+
break;
|
|
575
|
+
case "balance":
|
|
576
|
+
await cmdBalance();
|
|
577
|
+
break;
|
|
578
|
+
case "status":
|
|
579
|
+
await cmdStatus();
|
|
580
|
+
break;
|
|
581
|
+
case "score":
|
|
582
|
+
await cmdScore();
|
|
583
|
+
break;
|
|
584
|
+
case "markets":
|
|
585
|
+
await cmdMarkets();
|
|
586
|
+
break;
|
|
587
|
+
case "deposit":
|
|
588
|
+
if (!options.amount || !options.token) {
|
|
589
|
+
console.error("\u274C --amount and --token required");
|
|
590
|
+
console.error(" agether deposit --amount 0.05 --token WETH");
|
|
591
|
+
process.exit(1);
|
|
592
|
+
}
|
|
593
|
+
await cmdDeposit(options.amount, options.token);
|
|
594
|
+
break;
|
|
595
|
+
case "borrow":
|
|
596
|
+
if (!options.amount) {
|
|
597
|
+
console.error("\u274C --amount required (in USD)");
|
|
598
|
+
console.error(" agether borrow --amount 100");
|
|
599
|
+
process.exit(1);
|
|
600
|
+
}
|
|
601
|
+
await cmdBorrow(options.amount, options.token);
|
|
602
|
+
break;
|
|
603
|
+
case "deposit-and-borrow":
|
|
604
|
+
if (!options.amount || !options.token || !options.borrow) {
|
|
605
|
+
console.error("\u274C --amount, --token, and --borrow required");
|
|
606
|
+
console.error(" agether deposit-and-borrow --amount 0.05 --token WETH --borrow 100");
|
|
607
|
+
process.exit(1);
|
|
608
|
+
}
|
|
609
|
+
await cmdDepositAndBorrow(
|
|
610
|
+
options.amount,
|
|
611
|
+
options.token,
|
|
612
|
+
options.borrow
|
|
613
|
+
);
|
|
614
|
+
break;
|
|
615
|
+
case "repay":
|
|
616
|
+
if (!options.amount) {
|
|
617
|
+
console.error("\u274C --amount required");
|
|
618
|
+
process.exit(1);
|
|
619
|
+
}
|
|
620
|
+
await cmdRepay(options.amount, options.token);
|
|
621
|
+
break;
|
|
622
|
+
case "withdraw":
|
|
623
|
+
if (!options.amount || !options.token) {
|
|
624
|
+
console.error("\u274C --amount and --token required");
|
|
625
|
+
console.error(" agether withdraw --amount 0.05 --token WETH");
|
|
626
|
+
console.error(" agether withdraw --amount all --token WETH");
|
|
627
|
+
process.exit(1);
|
|
628
|
+
}
|
|
629
|
+
await cmdWithdraw(options.amount, options.token);
|
|
630
|
+
break;
|
|
631
|
+
case "sponsor":
|
|
632
|
+
if (!options.amount || !options.token) {
|
|
633
|
+
console.error("\u274C --amount and --token required, plus --agent-id or --address");
|
|
634
|
+
process.exit(1);
|
|
635
|
+
}
|
|
636
|
+
if (!options["agent-id"] && !options.address) {
|
|
637
|
+
console.error("\u274C --agent-id or --address required");
|
|
638
|
+
process.exit(1);
|
|
639
|
+
}
|
|
640
|
+
await cmdSponsor(
|
|
641
|
+
options.amount,
|
|
642
|
+
options.token,
|
|
643
|
+
options["agent-id"],
|
|
644
|
+
options.address
|
|
645
|
+
);
|
|
646
|
+
break;
|
|
647
|
+
case "fund":
|
|
648
|
+
if (!options.amount) {
|
|
649
|
+
console.error("\u274C --amount required (USDC)");
|
|
650
|
+
process.exit(1);
|
|
651
|
+
}
|
|
652
|
+
await cmdFund(options.amount);
|
|
653
|
+
break;
|
|
654
|
+
case "x402":
|
|
655
|
+
if (!positional[0]) {
|
|
656
|
+
console.error("\u274C URL required: agether x402 <url>");
|
|
657
|
+
process.exit(1);
|
|
658
|
+
}
|
|
659
|
+
await cmdX402Call(
|
|
660
|
+
positional[0],
|
|
661
|
+
options.method || "GET",
|
|
662
|
+
options.body
|
|
663
|
+
);
|
|
664
|
+
break;
|
|
665
|
+
case "help":
|
|
666
|
+
case "--help":
|
|
667
|
+
case "-h":
|
|
668
|
+
cmdHelp();
|
|
669
|
+
break;
|
|
670
|
+
default:
|
|
671
|
+
console.error(`\u274C Unknown command: ${command}`);
|
|
672
|
+
cmdHelp();
|
|
673
|
+
process.exit(1);
|
|
674
|
+
}
|
|
675
|
+
} catch (e) {
|
|
676
|
+
console.error(`\u274C Error: ${e.message}`);
|
|
677
|
+
process.exit(1);
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
main();
|
package/dist/index.js
CHANGED
|
@@ -467,8 +467,8 @@ var MorphoClient = class {
|
|
|
467
467
|
const addrs = { ...defaultCfg.contracts, ...config.contracts };
|
|
468
468
|
this.accountFactory = new import_ethers2.Contract(addrs.accountFactory, ACCOUNT_FACTORY_ABI, this.wallet);
|
|
469
469
|
this.morphoBlue = new import_ethers2.Contract(addrs.morphoBlue, MORPHO_BLUE_ABI, this.provider);
|
|
470
|
-
this.agentReputation = new import_ethers2.Contract(addrs.agentReputation, AGENT_REPUTATION_ABI, this.
|
|
471
|
-
this.identityRegistry = new import_ethers2.Contract(addrs.identityRegistry, IDENTITY_REGISTRY_ABI, this.
|
|
470
|
+
this.agentReputation = new import_ethers2.Contract(addrs.agentReputation, AGENT_REPUTATION_ABI, this.wallet);
|
|
471
|
+
this.identityRegistry = new import_ethers2.Contract(addrs.identityRegistry, IDENTITY_REGISTRY_ABI, this.wallet);
|
|
472
472
|
}
|
|
473
473
|
// ════════════════════════════════════════════════════════
|
|
474
474
|
// Account Management
|