@agether/openclaw-plugin 1.1.1 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/openclaw.plugin.json +1 -1
- package/package.json +2 -2
- package/src/index.ts +89 -54
package/openclaw.plugin.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"id": "agether",
|
|
3
3
|
"name": "Agether Credit",
|
|
4
4
|
"description": "On-chain credit protocol for AI agents — Morpho-backed overcollateralized credit, ERC-8004 identity, x402 payments",
|
|
5
|
-
"version": "1.
|
|
5
|
+
"version": "1.2.0",
|
|
6
6
|
"skills": ["skills/agether"],
|
|
7
7
|
"configSchema": {
|
|
8
8
|
"type": "object",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agether/openclaw-plugin",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.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.3.0",
|
|
13
13
|
"axios": "^1.6.0",
|
|
14
14
|
"ethers": "^6.9.0"
|
|
15
15
|
},
|
package/src/index.ts
CHANGED
|
@@ -30,6 +30,7 @@ const ERC8004_ABI = [
|
|
|
30
30
|
"function register(string agentURI) returns (uint256)",
|
|
31
31
|
"function ownerOf(uint256 tokenId) view returns (address)",
|
|
32
32
|
"function balanceOf(address owner) view returns (uint256)",
|
|
33
|
+
"function tokenOfOwnerByIndex(address owner, uint256 index) view returns (uint256)",
|
|
33
34
|
];
|
|
34
35
|
|
|
35
36
|
const COLLATERAL_TOKENS: Record<string, { address: string; decimals: number }> = {
|
|
@@ -69,6 +70,23 @@ async function getAccountAddr(signer: ethers.Wallet, factoryAddr: string, agentI
|
|
|
69
70
|
return addr;
|
|
70
71
|
}
|
|
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
|
+
|
|
72
90
|
function ok(text: string) {
|
|
73
91
|
return { content: [{ type: "text" as const, text }] };
|
|
74
92
|
}
|
|
@@ -103,15 +121,17 @@ export default function register(api: any) {
|
|
|
103
121
|
|
|
104
122
|
const result: any = {
|
|
105
123
|
address,
|
|
106
|
-
agentId: cfg.agentId || "not
|
|
124
|
+
agentId: cfg.agentId || _cachedAgentId || "not registered",
|
|
107
125
|
eth: ethers.formatEther(ethBal),
|
|
108
126
|
usdc: ethers.formatUnits(usdcBal, 6),
|
|
109
127
|
};
|
|
110
128
|
|
|
111
|
-
// AgentAccount balances
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
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);
|
|
115
135
|
const accEth = await provider.getBalance(accountAddr);
|
|
116
136
|
const accUsdc = await usdc.balanceOf(accountAddr);
|
|
117
137
|
result.agentAccount = {
|
|
@@ -119,8 +139,8 @@ export default function register(api: any) {
|
|
|
119
139
|
eth: ethers.formatEther(accEth),
|
|
120
140
|
usdc: ethers.formatUnits(accUsdc, 6),
|
|
121
141
|
};
|
|
122
|
-
}
|
|
123
|
-
}
|
|
142
|
+
}
|
|
143
|
+
} catch { /* no agent registered yet */ }
|
|
124
144
|
|
|
125
145
|
return ok(JSON.stringify(result, null, 2));
|
|
126
146
|
} catch (e) { return fail(e); }
|
|
@@ -211,13 +231,15 @@ export default function register(api: any) {
|
|
|
211
231
|
accountAddr = await factory.getAccount(agentId);
|
|
212
232
|
}
|
|
213
233
|
|
|
234
|
+
// Cache agentId for the session so all subsequent tools work
|
|
235
|
+
_cachedAgentId = agentId.toString();
|
|
236
|
+
|
|
214
237
|
return ok(JSON.stringify({
|
|
215
238
|
status: "registered",
|
|
216
239
|
agentId: agentId.toString(),
|
|
217
240
|
address: signer.address,
|
|
218
241
|
agentAccount: accountAddr,
|
|
219
242
|
tx: tx.hash,
|
|
220
|
-
important: "Save this agentId in your plugin config: plugins.entries.agether.config.agentId",
|
|
221
243
|
}));
|
|
222
244
|
} catch (e) { return fail(e); }
|
|
223
245
|
},
|
|
@@ -236,9 +258,9 @@ export default function register(api: any) {
|
|
|
236
258
|
const cfg = getConfig(api);
|
|
237
259
|
const signer = getSigner(cfg);
|
|
238
260
|
const status = await getBackendStatus(cfg);
|
|
239
|
-
|
|
261
|
+
const agentId = await resolveAgentId(cfg, signer, status);
|
|
240
262
|
|
|
241
|
-
const accountAddr = await getAccountAddr(signer, status.contracts.accountFactory,
|
|
263
|
+
const accountAddr = await getAccountAddr(signer, status.contracts.accountFactory, agentId);
|
|
242
264
|
const morpho = new ethers.Contract(status.contracts.morphoCredit, MORPHO_CREDIT_ABI, signer.provider!);
|
|
243
265
|
|
|
244
266
|
const positions: any[] = [];
|
|
@@ -285,13 +307,13 @@ export default function register(api: any) {
|
|
|
285
307
|
const cfg = getConfig(api);
|
|
286
308
|
const signer = getSigner(cfg);
|
|
287
309
|
const status = await getBackendStatus(cfg);
|
|
288
|
-
|
|
310
|
+
const agentId = await resolveAgentId(cfg, signer, status);
|
|
289
311
|
|
|
290
312
|
const tokenInfo = COLLATERAL_TOKENS[params.token];
|
|
291
313
|
if (!tokenInfo) return fail(`Unsupported token: ${params.token}. Use WETH, wstETH, or cbETH`);
|
|
292
314
|
|
|
293
315
|
const morphoCreditAddr = status.contracts.morphoCredit;
|
|
294
|
-
const accountAddr = await getAccountAddr(signer, status.contracts.accountFactory,
|
|
316
|
+
const accountAddr = await getAccountAddr(signer, status.contracts.accountFactory, agentId);
|
|
295
317
|
const amount = ethers.parseUnits(params.amount, tokenInfo.decimals);
|
|
296
318
|
|
|
297
319
|
// Check balance
|
|
@@ -351,13 +373,13 @@ export default function register(api: any) {
|
|
|
351
373
|
const cfg = getConfig(api);
|
|
352
374
|
const signer = getSigner(cfg);
|
|
353
375
|
const status = await getBackendStatus(cfg);
|
|
354
|
-
|
|
376
|
+
const agentId = await resolveAgentId(cfg, signer, status);
|
|
355
377
|
|
|
356
378
|
const tokenInfo = COLLATERAL_TOKENS[params.token];
|
|
357
379
|
if (!tokenInfo) return fail(`Unsupported token: ${params.token}`);
|
|
358
380
|
|
|
359
381
|
const morphoCreditAddr = status.contracts.morphoCredit;
|
|
360
|
-
const accountAddr = await getAccountAddr(signer, status.contracts.accountFactory,
|
|
382
|
+
const accountAddr = await getAccountAddr(signer, status.contracts.accountFactory, agentId);
|
|
361
383
|
const collateralWei = ethers.parseUnits(params.collateralAmount, tokenInfo.decimals);
|
|
362
384
|
const borrowWei = ethers.parseUnits(params.borrowAmount, 6);
|
|
363
385
|
|
|
@@ -526,9 +548,9 @@ export default function register(api: any) {
|
|
|
526
548
|
const cfg = getConfig(api);
|
|
527
549
|
const signer = getSigner(cfg);
|
|
528
550
|
const status = await getBackendStatus(cfg);
|
|
529
|
-
|
|
551
|
+
const agentId = await resolveAgentId(cfg, signer, status);
|
|
530
552
|
|
|
531
|
-
const accountAddr = await getAccountAddr(signer, status.contracts.accountFactory,
|
|
553
|
+
const accountAddr = await getAccountAddr(signer, status.contracts.accountFactory, agentId);
|
|
532
554
|
const morpho = new ethers.Contract(status.contracts.morphoCredit, MORPHO_CREDIT_ABI, signer.provider!);
|
|
533
555
|
|
|
534
556
|
// Find active collateral
|
|
@@ -544,6 +566,10 @@ export default function register(api: any) {
|
|
|
544
566
|
|
|
545
567
|
// Borrow via AgentAccount.execute → drawWithCollateral
|
|
546
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
|
+
|
|
547
573
|
const morphoIface = new ethers.Interface(MORPHO_CREDIT_ABI);
|
|
548
574
|
const calldata = morphoIface.encodeFunctionData("drawWithCollateral", [collateralAddr, amountWei]);
|
|
549
575
|
|
|
@@ -583,9 +609,9 @@ export default function register(api: any) {
|
|
|
583
609
|
const cfg = getConfig(api);
|
|
584
610
|
const signer = getSigner(cfg);
|
|
585
611
|
const status = await getBackendStatus(cfg);
|
|
586
|
-
|
|
612
|
+
const agentId = await resolveAgentId(cfg, signer, status);
|
|
587
613
|
|
|
588
|
-
const accountAddr = await getAccountAddr(signer, status.contracts.accountFactory,
|
|
614
|
+
const accountAddr = await getAccountAddr(signer, status.contracts.accountFactory, agentId);
|
|
589
615
|
const morpho = new ethers.Contract(status.contracts.morphoCredit, MORPHO_CREDIT_ABI, signer.provider!);
|
|
590
616
|
|
|
591
617
|
// Find active collateral with debt
|
|
@@ -610,14 +636,15 @@ export default function register(api: any) {
|
|
|
610
636
|
const morphoIface = new ethers.Interface(MORPHO_CREDIT_ABI);
|
|
611
637
|
const erc20Iface = new ethers.Interface(ERC20_ABI);
|
|
612
638
|
|
|
613
|
-
//
|
|
614
|
-
const
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
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
|
+
);
|
|
621
648
|
await tx.wait();
|
|
622
649
|
|
|
623
650
|
const totalDebt = await morpho.getTotalDebt(accountAddr);
|
|
@@ -652,12 +679,12 @@ export default function register(api: any) {
|
|
|
652
679
|
const cfg = getConfig(api);
|
|
653
680
|
const signer = getSigner(cfg);
|
|
654
681
|
const status = await getBackendStatus(cfg);
|
|
655
|
-
|
|
682
|
+
const agentId = await resolveAgentId(cfg, signer, status);
|
|
656
683
|
|
|
657
684
|
const tokenInfo = COLLATERAL_TOKENS[params.token];
|
|
658
685
|
if (!tokenInfo) return fail(`Unsupported token: ${params.token}`);
|
|
659
686
|
|
|
660
|
-
const accountAddr = await getAccountAddr(signer, status.contracts.accountFactory,
|
|
687
|
+
const accountAddr = await getAccountAddr(signer, status.contracts.accountFactory, agentId);
|
|
661
688
|
const morpho = new ethers.Contract(status.contracts.morphoCredit, MORPHO_CREDIT_ABI, signer.provider!);
|
|
662
689
|
const pos = await morpho.getPosition(accountAddr, tokenInfo.address);
|
|
663
690
|
|
|
@@ -671,15 +698,18 @@ export default function register(api: any) {
|
|
|
671
698
|
|
|
672
699
|
const account = new ethers.Contract(accountAddr, AGENT_ACCOUNT_ABI, signer);
|
|
673
700
|
const morphoIface = new ethers.Interface(MORPHO_CREDIT_ABI);
|
|
701
|
+
const erc20Iface = new ethers.Interface(ERC20_ABI);
|
|
674
702
|
|
|
675
|
-
//
|
|
676
|
-
const
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
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();
|
|
683
713
|
|
|
684
714
|
const newPos = await morpho.getPosition(accountAddr, tokenInfo.address);
|
|
685
715
|
|
|
@@ -689,7 +719,7 @@ export default function register(api: any) {
|
|
|
689
719
|
remainingCollateral: `${ethers.formatUnits(newPos.collateralAmount, tokenInfo.decimals)} ${params.token}`,
|
|
690
720
|
remainingDebt: `$${ethers.formatUnits(newPos.borrowedAmount, 6)}`,
|
|
691
721
|
destination: signer.address,
|
|
692
|
-
tx:
|
|
722
|
+
tx: tx.hash,
|
|
693
723
|
}));
|
|
694
724
|
} catch (e) { return fail(e); }
|
|
695
725
|
},
|
|
@@ -758,8 +788,10 @@ export default function register(api: any) {
|
|
|
758
788
|
async execute() {
|
|
759
789
|
try {
|
|
760
790
|
const cfg = getConfig(api);
|
|
761
|
-
|
|
762
|
-
const
|
|
791
|
+
const signer = getSigner(cfg);
|
|
792
|
+
const status = await getBackendStatus(cfg);
|
|
793
|
+
const agentId = await resolveAgentId(cfg, signer, status);
|
|
794
|
+
const { data } = await axios.get(`${cfg.backendUrl}/credit/score/${agentId}`);
|
|
763
795
|
return ok(JSON.stringify(data, null, 2));
|
|
764
796
|
} catch (e) { return fail(e); }
|
|
765
797
|
},
|
|
@@ -783,9 +815,9 @@ export default function register(api: any) {
|
|
|
783
815
|
const cfg = getConfig(api);
|
|
784
816
|
const signer = getSigner(cfg);
|
|
785
817
|
const status = await getBackendStatus(cfg);
|
|
786
|
-
|
|
818
|
+
const agentId = await resolveAgentId(cfg, signer, status);
|
|
787
819
|
|
|
788
|
-
const accountAddr = await getAccountAddr(signer, status.contracts.accountFactory,
|
|
820
|
+
const accountAddr = await getAccountAddr(signer, status.contracts.accountFactory, agentId);
|
|
789
821
|
const amountWei = ethers.parseUnits(params.amount, 6);
|
|
790
822
|
|
|
791
823
|
const usdc = new ethers.Contract(status.contracts.usdc, ERC20_ABI, signer);
|
|
@@ -834,15 +866,15 @@ export default function register(api: any) {
|
|
|
834
866
|
|
|
835
867
|
// Resolve AgentAccount address for x402 payments
|
|
836
868
|
let accountAddress: string | undefined;
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
}
|
|
869
|
+
let agentId: string | undefined;
|
|
870
|
+
try {
|
|
871
|
+
agentId = await resolveAgentId(cfg, signer, status);
|
|
872
|
+
accountAddress = await getAccountAddr(signer, status.contracts.accountFactory, agentId);
|
|
873
|
+
} catch { /* no account, pay from EOA */ }
|
|
842
874
|
|
|
843
875
|
// Auto-draw: if enabled, check USDC balance and borrow if needed
|
|
844
876
|
const shouldAutoDraw = params.autoDraw ?? cfg.autoDraw;
|
|
845
|
-
if (shouldAutoDraw && accountAddress &&
|
|
877
|
+
if (shouldAutoDraw && accountAddress && agentId) {
|
|
846
878
|
const usdc = new ethers.Contract(status.contracts.usdc, ERC20_ABI, signer.provider!);
|
|
847
879
|
const accBalance = await usdc.balanceOf(accountAddress);
|
|
848
880
|
// If balance < $1, try to auto-borrow $10 (enough for several x402 calls)
|
|
@@ -871,7 +903,7 @@ export default function register(api: any) {
|
|
|
871
903
|
privateKey: cfg.privateKey,
|
|
872
904
|
rpcUrl: cfg.rpcUrl!,
|
|
873
905
|
backendUrl: cfg.backendUrl!,
|
|
874
|
-
agentId:
|
|
906
|
+
agentId: agentId,
|
|
875
907
|
accountAddress,
|
|
876
908
|
});
|
|
877
909
|
|
|
@@ -912,14 +944,17 @@ export default function register(api: any) {
|
|
|
912
944
|
const usdc = new ethers.Contract(status.contracts.usdc, ERC20_ABI, provider);
|
|
913
945
|
const usdcBal = await usdc.balanceOf(signer.address);
|
|
914
946
|
|
|
915
|
-
let
|
|
947
|
+
let agentId: string | undefined;
|
|
948
|
+
try { agentId = await resolveAgentId(cfg, signer, status); } catch {}
|
|
949
|
+
|
|
950
|
+
let text = `💰 Agent #${agentId || "?"}\n`;
|
|
916
951
|
text += `Address: ${signer.address}\n`;
|
|
917
952
|
text += `ETH: ${parseFloat(ethers.formatEther(ethBal)).toFixed(6)}\n`;
|
|
918
953
|
text += `USDC: $${ethers.formatUnits(usdcBal, 6)}`;
|
|
919
954
|
|
|
920
|
-
if (
|
|
955
|
+
if (agentId && status.contracts.accountFactory) {
|
|
921
956
|
try {
|
|
922
|
-
const accAddr = await getAccountAddr(signer, status.contracts.accountFactory,
|
|
957
|
+
const accAddr = await getAccountAddr(signer, status.contracts.accountFactory, agentId);
|
|
923
958
|
const accUsdc = await usdc.balanceOf(accAddr);
|
|
924
959
|
text += `\n\n🏦 AgentAccount: ${accAddr}\nUSDC: $${ethers.formatUnits(accUsdc, 6)}`;
|
|
925
960
|
} catch { /* no account */ }
|
|
@@ -940,13 +975,13 @@ export default function register(api: any) {
|
|
|
940
975
|
const cfg = getConfig(api);
|
|
941
976
|
const signer = getSigner(cfg);
|
|
942
977
|
const status = await getBackendStatus(cfg);
|
|
943
|
-
|
|
978
|
+
const agentId = await resolveAgentId(cfg, signer, status);
|
|
944
979
|
|
|
945
|
-
const accountAddr = await getAccountAddr(signer, status.contracts.accountFactory,
|
|
980
|
+
const accountAddr = await getAccountAddr(signer, status.contracts.accountFactory, agentId);
|
|
946
981
|
const morpho = new ethers.Contract(status.contracts.morphoCredit, MORPHO_CREDIT_ABI, signer.provider!);
|
|
947
982
|
const totalDebt = await morpho.getTotalDebt(accountAddr);
|
|
948
983
|
|
|
949
|
-
let text = `📊 Morpho — Agent #${
|
|
984
|
+
let text = `📊 Morpho — Agent #${agentId}\nAccount: ${accountAddr}\nTotal debt: $${ethers.formatUnits(totalDebt, 6)}\n`;
|
|
950
985
|
|
|
951
986
|
for (const [symbol, info] of Object.entries(COLLATERAL_TOKENS)) {
|
|
952
987
|
const pos = await morpho.getPosition(accountAddr, info.address);
|