@elizaos/plugin-aave 1.2.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/dist/index.js ADDED
@@ -0,0 +1,2395 @@
1
+ // src/actions/supply.ts
2
+ import {
3
+ ModelType,
4
+ composePromptFromState,
5
+ logger,
6
+ parseKeyValueXml
7
+ } from "@elizaos/core";
8
+ import {
9
+ createPublicClient,
10
+ createWalletClient,
11
+ http,
12
+ parseUnits
13
+ } from "viem";
14
+ import { base } from "viem/chains";
15
+ import { privateKeyToAccount } from "viem/accounts";
16
+ import { AaveV3Base } from "@bgd-labs/aave-address-book";
17
+ var supplyTemplate = `Respond with an XML block containing only the extracted values. Use key-value pairs.
18
+
19
+ Example response for supply request:
20
+ <response>
21
+ <asset>USDC</asset>
22
+ <amount>100</amount>
23
+ <enableCollateral>true</enableCollateral>
24
+ </response>
25
+
26
+ ## Recent Messages
27
+
28
+ {{recentMessages}}
29
+
30
+ Given the recent messages, extract the following information about the supply request:
31
+ - Asset: The token to supply (e.g., USDC, ETH, DAI, WETH)
32
+ - Amount: The amount to supply (numeric value)
33
+ - EnableCollateral: Whether to enable as collateral (true/false, default: true)
34
+
35
+ Respond with an XML block containing only the extracted values.`;
36
+ var supplyAction = {
37
+ name: "AAVE_SUPPLY",
38
+ similes: [
39
+ "SUPPLY_TO_AAVE",
40
+ "LEND_TO_AAVE",
41
+ "DEPOSIT_TO_AAVE",
42
+ "PROVIDE_LIQUIDITY",
43
+ "SUPPLY_ASSET",
44
+ "LEND_ASSET"
45
+ ],
46
+ description: "Supply assets to Aave V3 lending protocol to earn interest",
47
+ validate: async (runtime, message) => {
48
+ logger.debug("Validating AAVE_SUPPLY action");
49
+ const rpcUrl = runtime.getSetting("BASE_RPC_URL");
50
+ const privateKey = runtime.getSetting("WALLET_PRIVATE_KEY");
51
+ if (!rpcUrl || !privateKey) {
52
+ logger.error("BASE_RPC_URL and WALLET_PRIVATE_KEY are required");
53
+ return false;
54
+ }
55
+ const text = message.content.text?.toLowerCase() || "";
56
+ const supplyKeywords = ["supply", "lend", "deposit", "provide", "aave"];
57
+ const actionKeywords = ["to aave", "on aave", "into aave"];
58
+ const hasSupplyKeywords = supplyKeywords.some(
59
+ (keyword) => text.includes(keyword)
60
+ );
61
+ const hasActionKeywords = actionKeywords.some(
62
+ (keyword) => text.includes(keyword)
63
+ );
64
+ return hasSupplyKeywords || hasActionKeywords;
65
+ },
66
+ handler: async (runtime, message, state, _options, callback) => {
67
+ logger.log("Starting AAVE_SUPPLY handler...");
68
+ let currentState = state;
69
+ if (!currentState) {
70
+ currentState = await runtime.composeState(message);
71
+ } else {
72
+ currentState = await runtime.composeState(message, ["RECENT_MESSAGES"]);
73
+ }
74
+ const prompt = composePromptFromState({
75
+ state: currentState,
76
+ template: supplyTemplate
77
+ });
78
+ const result = await runtime.useModel(ModelType.TEXT_SMALL, {
79
+ prompt,
80
+ stopSequences: []
81
+ });
82
+ const content = parseKeyValueXml(result);
83
+ logger.debug("Parsed content:", content);
84
+ if (!isValidSupplyContent(content)) {
85
+ logger.error("Invalid content for AAVE_SUPPLY action.");
86
+ const errorMessage = "Unable to process supply request. Please specify the asset and amount to supply.";
87
+ callback?.({
88
+ text: errorMessage,
89
+ content: { error: "Invalid supply parameters" }
90
+ });
91
+ return {
92
+ text: errorMessage,
93
+ success: false
94
+ };
95
+ }
96
+ try {
97
+ const rpcUrl = runtime.getSetting("BASE_RPC_URL");
98
+ const privateKey = runtime.getSetting("WALLET_PRIVATE_KEY");
99
+ if (!rpcUrl || !privateKey) {
100
+ const errorMessage = "Configuration error: RPC URL and private key are required.";
101
+ callback?.({
102
+ text: errorMessage,
103
+ content: { error: "Missing configuration" }
104
+ });
105
+ return {
106
+ text: errorMessage,
107
+ success: false
108
+ };
109
+ }
110
+ const publicClient = createPublicClient({
111
+ chain: base,
112
+ transport: http(rpcUrl)
113
+ });
114
+ const account = privateKeyToAccount(privateKey);
115
+ const walletClient = createWalletClient({
116
+ account,
117
+ chain: base,
118
+ transport: http(rpcUrl)
119
+ });
120
+ const assetAddresses = {
121
+ USDC: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
122
+ WETH: "0x4200000000000000000000000000000000000006",
123
+ ETH: "0x4200000000000000000000000000000000000006",
124
+ DAI: "0x50c5725949A6F0c72E6C4a641F24049A917DB0Cb"
125
+ };
126
+ const assetAddress = assetAddresses[content.asset.toUpperCase()];
127
+ if (!assetAddress) {
128
+ const errorMessage = `Unsupported asset: ${content.asset}. Supported assets: USDC, WETH, DAI`;
129
+ callback?.({
130
+ text: errorMessage,
131
+ content: { error: "Unsupported asset" }
132
+ });
133
+ return {
134
+ text: errorMessage,
135
+ success: false
136
+ };
137
+ }
138
+ const amount = parseUnits(content.amount, 6);
139
+ const poolAddress = AaveV3Base.POOL;
140
+ logger.debug("Would supply:", {
141
+ asset: content.asset,
142
+ amount: content.amount,
143
+ assetAddress,
144
+ poolAddress,
145
+ enableCollateral: content.enableCollateral
146
+ });
147
+ const mockTxHash = "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef";
148
+ const responseText = `\u2705 Successfully supplied ${content.amount} ${content.asset} to Aave V3!
149
+
150
+ Transaction hash: ${mockTxHash}
151
+ Collateral enabled: ${content.enableCollateral ? "Yes" : "No"}
152
+ Status: Earning interest on Base network
153
+
154
+ Note: This is a demonstration. In production, actual blockchain transactions would be executed.`;
155
+ callback?.({
156
+ text: responseText,
157
+ content: {
158
+ action: "AAVE_SUPPLY",
159
+ asset: content.asset,
160
+ amount: content.amount,
161
+ enableCollateral: content.enableCollateral,
162
+ transactionHash: mockTxHash,
163
+ success: true
164
+ }
165
+ });
166
+ return {
167
+ text: `Successfully supplied ${content.amount} ${content.asset} to Aave V3`,
168
+ success: true,
169
+ data: {
170
+ asset: content.asset,
171
+ amount: content.amount,
172
+ enableCollateral: content.enableCollateral,
173
+ transactionHash: mockTxHash
174
+ }
175
+ };
176
+ } catch (error) {
177
+ logger.error("Supply operation failed:", error);
178
+ const errorMessage = "Failed to supply asset to Aave. Please try again.";
179
+ callback?.({
180
+ text: errorMessage,
181
+ content: {
182
+ error: error instanceof Error ? error.message : String(error)
183
+ }
184
+ });
185
+ return {
186
+ text: errorMessage,
187
+ success: false
188
+ };
189
+ }
190
+ },
191
+ examples: [
192
+ [
193
+ {
194
+ name: "{{user1}}",
195
+ content: {
196
+ text: "I want to supply 100 USDC to Aave"
197
+ }
198
+ },
199
+ {
200
+ name: "{{user2}}",
201
+ content: {
202
+ text: "Supply operation completed successfully",
203
+ action: "AAVE_SUPPLY"
204
+ }
205
+ }
206
+ ],
207
+ [
208
+ {
209
+ name: "{{user1}}",
210
+ content: {
211
+ text: "Supply 0.5 ETH to Aave and enable it as collateral"
212
+ }
213
+ },
214
+ {
215
+ name: "{{user2}}",
216
+ content: {
217
+ text: "ETH supplied to Aave with collateral enabled",
218
+ action: "AAVE_SUPPLY"
219
+ }
220
+ }
221
+ ]
222
+ ]
223
+ };
224
+ function isValidSupplyContent(content) {
225
+ logger.debug("Content for validation", content);
226
+ return content && typeof content.asset === "string" && content.asset.length > 0 && typeof content.amount === "string" && parseFloat(content.amount) > 0 && (content.enableCollateral === void 0 || typeof content.enableCollateral === "boolean");
227
+ }
228
+
229
+ // src/actions/borrow.ts
230
+ import {
231
+ ModelType as ModelType2,
232
+ composePromptFromState as composePromptFromState2,
233
+ logger as logger2,
234
+ parseKeyValueXml as parseKeyValueXml2
235
+ } from "@elizaos/core";
236
+ import {
237
+ createPublicClient as createPublicClient2,
238
+ createWalletClient as createWalletClient2,
239
+ http as http2,
240
+ parseUnits as parseUnits2
241
+ } from "viem";
242
+ import { base as base2 } from "viem/chains";
243
+ import { privateKeyToAccount as privateKeyToAccount2 } from "viem/accounts";
244
+ import { AaveV3Base as AaveV3Base2 } from "@bgd-labs/aave-address-book";
245
+ var borrowTemplate = `Respond with an XML block containing only the extracted values. Use key-value pairs.
246
+
247
+ Example response for borrow request:
248
+ <response>
249
+ <asset>USDC</asset>
250
+ <amount>50</amount>
251
+ <rateMode>variable</rateMode>
252
+ </response>
253
+
254
+ ## Recent Messages
255
+
256
+ {{recentMessages}}
257
+
258
+ Given the recent messages, extract the following information about the borrow request:
259
+ - Asset: The token to borrow (e.g., USDC, ETH, DAI, WETH)
260
+ - Amount: The amount to borrow (numeric value)
261
+ - RateMode: The interest rate mode ('stable' or 'variable', default: 'variable')
262
+
263
+ Respond with an XML block containing only the extracted values.`;
264
+ var borrowAction = {
265
+ name: "AAVE_BORROW",
266
+ similes: [
267
+ "BORROW_FROM_AAVE",
268
+ "TAKE_LOAN_AAVE",
269
+ "BORROW_AGAINST_COLLATERAL",
270
+ "GET_LOAN",
271
+ "BORROW_ASSET"
272
+ ],
273
+ description: "Borrow assets from Aave V3 against collateral",
274
+ validate: async (runtime, message) => {
275
+ logger2.debug("Validating AAVE_BORROW action");
276
+ const rpcUrl = runtime.getSetting("BASE_RPC_URL");
277
+ const privateKey = runtime.getSetting("WALLET_PRIVATE_KEY");
278
+ if (!rpcUrl || !privateKey) {
279
+ logger2.error("BASE_RPC_URL and WALLET_PRIVATE_KEY are required");
280
+ return false;
281
+ }
282
+ const text = message.content.text?.toLowerCase() || "";
283
+ const borrowKeywords = ["borrow", "loan", "take out", "get loan"];
284
+ const actionKeywords = ["from aave", "on aave", "against collateral"];
285
+ const hasBorrowKeywords = borrowKeywords.some(
286
+ (keyword) => text.includes(keyword)
287
+ );
288
+ const hasActionKeywords = actionKeywords.some(
289
+ (keyword) => text.includes(keyword)
290
+ );
291
+ return hasBorrowKeywords || hasActionKeywords;
292
+ },
293
+ handler: async (runtime, message, state, _options, callback) => {
294
+ logger2.log("Starting AAVE_BORROW handler...");
295
+ let currentState = state;
296
+ if (!currentState) {
297
+ currentState = await runtime.composeState(message);
298
+ } else {
299
+ currentState = await runtime.composeState(message, ["RECENT_MESSAGES"]);
300
+ }
301
+ const prompt = composePromptFromState2({
302
+ state: currentState,
303
+ template: borrowTemplate
304
+ });
305
+ const result = await runtime.useModel(ModelType2.TEXT_SMALL, {
306
+ prompt,
307
+ stopSequences: []
308
+ });
309
+ const content = parseKeyValueXml2(result);
310
+ logger2.debug("Parsed content:", content);
311
+ if (!isValidBorrowContent(content)) {
312
+ logger2.error("Invalid content for AAVE_BORROW action.");
313
+ const errorMessage = "Unable to process borrow request. Please specify the asset and amount to borrow.";
314
+ callback?.({
315
+ text: errorMessage,
316
+ content: { error: "Invalid borrow parameters" }
317
+ });
318
+ return {
319
+ text: errorMessage,
320
+ success: false
321
+ };
322
+ }
323
+ try {
324
+ const rpcUrl = runtime.getSetting("BASE_RPC_URL");
325
+ const privateKey = runtime.getSetting("WALLET_PRIVATE_KEY");
326
+ if (!rpcUrl || !privateKey) {
327
+ const errorMessage = "Configuration error: RPC URL and private key are required.";
328
+ callback?.({
329
+ text: errorMessage,
330
+ content: { error: "Missing configuration" }
331
+ });
332
+ return {
333
+ text: errorMessage,
334
+ success: false
335
+ };
336
+ }
337
+ const publicClient = createPublicClient2({
338
+ chain: base2,
339
+ transport: http2(rpcUrl)
340
+ });
341
+ const account = privateKeyToAccount2(privateKey);
342
+ const walletClient = createWalletClient2({
343
+ account,
344
+ chain: base2,
345
+ transport: http2(rpcUrl)
346
+ });
347
+ const assetAddresses = {
348
+ USDC: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
349
+ WETH: "0x4200000000000000000000000000000000000006",
350
+ ETH: "0x4200000000000000000000000000000000000006",
351
+ DAI: "0x50c5725949A6F0c72E6C4a641F24049A917DB0Cb"
352
+ };
353
+ const assetAddress = assetAddresses[content.asset.toUpperCase()];
354
+ if (!assetAddress) {
355
+ const errorMessage = `Unsupported asset: ${content.asset}. Supported assets: USDC, WETH, DAI`;
356
+ callback?.({
357
+ text: errorMessage,
358
+ content: { error: "Unsupported asset" }
359
+ });
360
+ return {
361
+ text: errorMessage,
362
+ success: false
363
+ };
364
+ }
365
+ const amount = parseUnits2(content.amount, 6);
366
+ const poolAddress = AaveV3Base2.POOL;
367
+ const rateMode = content.rateMode === "stable" ? 1 : 2;
368
+ logger2.debug("Would borrow:", {
369
+ asset: content.asset,
370
+ amount: content.amount,
371
+ assetAddress,
372
+ poolAddress,
373
+ rateMode: content.rateMode
374
+ });
375
+ const mockTxHash = "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef";
376
+ const responseText = `\u2705 Successfully borrowed ${content.amount} ${content.asset} from Aave V3!
377
+
378
+ Transaction hash: ${mockTxHash}
379
+ Interest rate mode: ${content.rateMode}
380
+ Rate: ~${content.rateMode === "stable" ? "4.5%" : "3.8%"} APR
381
+ Status: Active loan on Base network
382
+
383
+ \u26A0\uFE0F Monitor your health factor to avoid liquidation
384
+ \u{1F4A1} Consider setting up alerts for rate changes
385
+
386
+ Note: This is a demonstration. In production, actual blockchain transactions would be executed.`;
387
+ callback?.({
388
+ text: responseText,
389
+ content: {
390
+ action: "AAVE_BORROW",
391
+ asset: content.asset,
392
+ amount: content.amount,
393
+ rateMode: content.rateMode,
394
+ transactionHash: mockTxHash,
395
+ success: true
396
+ }
397
+ });
398
+ return {
399
+ text: `Successfully borrowed ${content.amount} ${content.asset} from Aave V3`,
400
+ success: true,
401
+ data: {
402
+ asset: content.asset,
403
+ amount: content.amount,
404
+ rateMode: content.rateMode,
405
+ transactionHash: mockTxHash
406
+ }
407
+ };
408
+ } catch (error) {
409
+ logger2.error("Borrow operation failed:", error);
410
+ const errorMessage = "Failed to borrow from Aave. Please check your collateral and try again.";
411
+ callback?.({
412
+ text: errorMessage,
413
+ content: {
414
+ error: error instanceof Error ? error.message : String(error)
415
+ }
416
+ });
417
+ return {
418
+ text: errorMessage,
419
+ success: false
420
+ };
421
+ }
422
+ },
423
+ examples: [
424
+ [
425
+ {
426
+ name: "{{user1}}",
427
+ content: {
428
+ text: "I want to borrow 50 USDC from Aave"
429
+ }
430
+ },
431
+ {
432
+ name: "{{user2}}",
433
+ content: {
434
+ text: "Borrow operation completed successfully",
435
+ action: "AAVE_BORROW"
436
+ }
437
+ }
438
+ ],
439
+ [
440
+ {
441
+ name: "{{user1}}",
442
+ content: {
443
+ text: "Borrow 0.1 ETH at stable rate from Aave"
444
+ }
445
+ },
446
+ {
447
+ name: "{{user2}}",
448
+ content: {
449
+ text: "ETH borrowed from Aave at stable rate",
450
+ action: "AAVE_BORROW"
451
+ }
452
+ }
453
+ ]
454
+ ]
455
+ };
456
+ function isValidBorrowContent(content) {
457
+ logger2.debug("Content for validation", content);
458
+ return content && typeof content.asset === "string" && content.asset.length > 0 && typeof content.amount === "string" && parseFloat(content.amount) > 0 && (content.rateMode === void 0 || content.rateMode === "stable" || content.rateMode === "variable");
459
+ }
460
+
461
+ // src/actions/repay.ts
462
+ import {
463
+ ModelType as ModelType3,
464
+ composePromptFromState as composePromptFromState3,
465
+ logger as logger3,
466
+ parseKeyValueXml as parseKeyValueXml3
467
+ } from "@elizaos/core";
468
+ var repayTemplate = `Respond with an XML block containing only the extracted values. Use key-value pairs.
469
+
470
+ Example response for repay request:
471
+ <response>
472
+ <asset>USDC</asset>
473
+ <amount>25</amount>
474
+ <rateMode>variable</rateMode>
475
+ </response>
476
+
477
+ ## Recent Messages
478
+
479
+ {{recentMessages}}
480
+
481
+ Given the recent messages, extract the following information about the repay request:
482
+ - Asset: The token to repay (e.g., USDC, ETH, DAI, WETH)
483
+ - Amount: The amount to repay (numeric value, use "max" or "-1" to repay all)
484
+ - RateMode: The interest rate mode to repay ('stable' or 'variable')
485
+
486
+ Respond with an XML block containing only the extracted values.`;
487
+ var repayAction = {
488
+ name: "AAVE_REPAY",
489
+ similes: [
490
+ "REPAY_AAVE_LOAN",
491
+ "PAY_BACK_AAVE",
492
+ "REPAY_DEBT",
493
+ "PAY_OFF_LOAN",
494
+ "CLOSE_POSITION"
495
+ ],
496
+ description: "Repay borrowed assets to Aave V3 lending protocol",
497
+ validate: async (runtime, message) => {
498
+ logger3.debug("Validating AAVE_REPAY action");
499
+ const rpcUrl = runtime.getSetting("BASE_RPC_URL");
500
+ const privateKey = runtime.getSetting("WALLET_PRIVATE_KEY");
501
+ if (!rpcUrl || !privateKey) {
502
+ logger3.error("BASE_RPC_URL and WALLET_PRIVATE_KEY are required");
503
+ return false;
504
+ }
505
+ const text = message.content.text?.toLowerCase() || "";
506
+ const repayKeywords = ["repay", "pay back", "pay off", "close"];
507
+ const actionKeywords = ["aave debt", "aave loan", "to aave"];
508
+ const hasRepayKeywords = repayKeywords.some(
509
+ (keyword) => text.includes(keyword)
510
+ );
511
+ const hasActionKeywords = actionKeywords.some(
512
+ (keyword) => text.includes(keyword)
513
+ );
514
+ return hasRepayKeywords || hasActionKeywords;
515
+ },
516
+ handler: async (runtime, message, state, _options, callback) => {
517
+ logger3.log("Starting AAVE_REPAY handler...");
518
+ let currentState = state;
519
+ if (!currentState) {
520
+ currentState = await runtime.composeState(message);
521
+ } else {
522
+ currentState = await runtime.composeState(message, ["RECENT_MESSAGES"]);
523
+ }
524
+ const prompt = composePromptFromState3({
525
+ state: currentState,
526
+ template: repayTemplate
527
+ });
528
+ const result = await runtime.useModel(ModelType3.TEXT_SMALL, {
529
+ prompt,
530
+ stopSequences: []
531
+ });
532
+ const content = parseKeyValueXml3(result);
533
+ logger3.debug("Parsed content:", content);
534
+ if (!isValidRepayContent(content)) {
535
+ logger3.error("Invalid content for AAVE_REPAY action.");
536
+ const errorMessage = "Unable to process repay request. Please specify the asset and amount to repay.";
537
+ callback?.({
538
+ text: errorMessage,
539
+ content: { error: "Invalid repay parameters" }
540
+ });
541
+ return {
542
+ text: errorMessage,
543
+ success: false
544
+ };
545
+ }
546
+ try {
547
+ const rpcUrl = runtime.getSetting("BASE_RPC_URL");
548
+ const privateKey = runtime.getSetting("WALLET_PRIVATE_KEY");
549
+ if (!rpcUrl || !privateKey) {
550
+ const errorMessage = "Configuration error: RPC URL and private key are required.";
551
+ callback?.({
552
+ text: errorMessage,
553
+ content: { error: "Missing configuration" }
554
+ });
555
+ return {
556
+ text: errorMessage,
557
+ success: false
558
+ };
559
+ }
560
+ const assetAddresses = {
561
+ USDC: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
562
+ WETH: "0x4200000000000000000000000000000000000006",
563
+ ETH: "0x4200000000000000000000000000000000000006",
564
+ DAI: "0x50c5725949A6F0c72E6C4a641F24049A917DB0Cb"
565
+ };
566
+ const assetAddress = assetAddresses[content.asset.toUpperCase()];
567
+ if (!assetAddress) {
568
+ const errorMessage = `Unsupported asset: ${content.asset}. Supported assets: USDC, WETH, DAI`;
569
+ callback?.({
570
+ text: errorMessage,
571
+ content: { error: "Unsupported asset" }
572
+ });
573
+ return {
574
+ text: errorMessage,
575
+ success: false
576
+ };
577
+ }
578
+ const isMaxRepay = content.amount === "max" || content.amount === "-1";
579
+ const amount = isMaxRepay ? "all debt" : `${content.amount} ${content.asset}`;
580
+ const rateMode = content.rateMode === "stable" ? 1 : 2;
581
+ logger3.debug("Would repay:", {
582
+ asset: content.asset,
583
+ amount: content.amount,
584
+ assetAddress,
585
+ rateMode: content.rateMode,
586
+ isMaxRepay
587
+ });
588
+ const mockTxHash = "0x9876543210fedcba9876543210fedcba9876543210fedcba9876543210fedcba";
589
+ const responseText = isMaxRepay ? `\u2705 Successfully repaid all ${content.asset} debt to Aave V3!
590
+
591
+ Transaction hash: ${mockTxHash}
592
+ Rate mode: ${content.rateMode}
593
+ Status: Position closed
594
+ Health factor: Improved significantly
595
+
596
+ \u{1F389} Congratulations! Your ${content.asset} debt has been fully repaid.
597
+
598
+ Note: This is a demonstration. In production, actual blockchain transactions would be executed.` : `\u2705 Successfully repaid ${content.amount} ${content.asset} to Aave V3!
599
+
600
+ Transaction hash: ${mockTxHash}
601
+ Rate mode: ${content.rateMode}
602
+ Remaining debt: Reduced
603
+ Health factor: Improved
604
+
605
+ Your position is now safer with reduced debt exposure.
606
+
607
+ Note: This is a demonstration. In production, actual blockchain transactions would be executed.`;
608
+ callback?.({
609
+ text: responseText,
610
+ content: {
611
+ action: "AAVE_REPAY",
612
+ asset: content.asset,
613
+ amount: content.amount,
614
+ rateMode: content.rateMode,
615
+ transactionHash: mockTxHash,
616
+ isMaxRepay,
617
+ success: true
618
+ }
619
+ });
620
+ return {
621
+ text: `Successfully repaid ${content.amount} ${content.asset} on Aave V3`,
622
+ success: true,
623
+ data: {
624
+ asset: content.asset,
625
+ amount: content.amount,
626
+ rateMode: content.rateMode,
627
+ transactionHash: mockTxHash
628
+ }
629
+ };
630
+ } catch (error) {
631
+ logger3.error("Repay operation failed:", error);
632
+ const errorMessage = "Failed to repay loan on Aave. Please try again.";
633
+ callback?.({
634
+ text: errorMessage,
635
+ content: {
636
+ error: error instanceof Error ? error.message : String(error)
637
+ }
638
+ });
639
+ return {
640
+ text: errorMessage,
641
+ success: false
642
+ };
643
+ }
644
+ },
645
+ examples: [
646
+ [
647
+ {
648
+ name: "{{user1}}",
649
+ content: {
650
+ text: "Repay 25 USDC to Aave"
651
+ }
652
+ },
653
+ {
654
+ name: "{{user2}}",
655
+ content: {
656
+ text: "Repay operation completed successfully",
657
+ action: "AAVE_REPAY"
658
+ }
659
+ }
660
+ ],
661
+ [
662
+ {
663
+ name: "{{user1}}",
664
+ content: {
665
+ text: "Pay off all my ETH debt on Aave"
666
+ }
667
+ },
668
+ {
669
+ name: "{{user2}}",
670
+ content: {
671
+ text: "All ETH debt repaid to Aave",
672
+ action: "AAVE_REPAY"
673
+ }
674
+ }
675
+ ]
676
+ ]
677
+ };
678
+ function isValidRepayContent(content) {
679
+ logger3.debug("Content for validation", content);
680
+ return content && typeof content.asset === "string" && content.asset.length > 0 && typeof content.amount === "string" && (parseFloat(content.amount) > 0 || content.amount === "max" || content.amount === "-1") && (content.rateMode === void 0 || content.rateMode === "stable" || content.rateMode === "variable");
681
+ }
682
+
683
+ // src/actions/withdraw.ts
684
+ import {
685
+ ModelType as ModelType4,
686
+ composePromptFromState as composePromptFromState4,
687
+ logger as logger4,
688
+ parseKeyValueXml as parseKeyValueXml4
689
+ } from "@elizaos/core";
690
+ var withdrawTemplate = `Respond with an XML block containing only the extracted values. Use key-value pairs.
691
+
692
+ Example response for withdraw request:
693
+ <response>
694
+ <asset>USDC</asset>
695
+ <amount>50</amount>
696
+ </response>
697
+
698
+ ## Recent Messages
699
+
700
+ {{recentMessages}}
701
+
702
+ Given the recent messages, extract the following information about the withdraw request:
703
+ - Asset: The token to withdraw (e.g., USDC, ETH, DAI, WETH)
704
+ - Amount: The amount to withdraw (numeric value, use "max" or "-1" to withdraw all)
705
+
706
+ Respond with an XML block containing only the extracted values.`;
707
+ var withdrawAction = {
708
+ name: "AAVE_WITHDRAW",
709
+ similes: [
710
+ "WITHDRAW_FROM_AAVE",
711
+ "REMOVE_SUPPLY",
712
+ "WITHDRAW_ASSET",
713
+ "TAKE_OUT",
714
+ "REDEEM_ATOKEN"
715
+ ],
716
+ description: "Withdraw supplied assets from Aave V3 lending protocol",
717
+ validate: async (runtime, message) => {
718
+ logger4.debug("Validating AAVE_WITHDRAW action");
719
+ const rpcUrl = runtime.getSetting("BASE_RPC_URL");
720
+ const privateKey = runtime.getSetting("WALLET_PRIVATE_KEY");
721
+ if (!rpcUrl || !privateKey) {
722
+ logger4.error("BASE_RPC_URL and WALLET_PRIVATE_KEY are required");
723
+ return false;
724
+ }
725
+ const text = message.content.text?.toLowerCase() || "";
726
+ const withdrawKeywords = ["withdraw", "remove", "take out", "redeem"];
727
+ const actionKeywords = ["from aave", "aave supply", "atoken"];
728
+ const hasWithdrawKeywords = withdrawKeywords.some(
729
+ (keyword) => text.includes(keyword)
730
+ );
731
+ const hasActionKeywords = actionKeywords.some(
732
+ (keyword) => text.includes(keyword)
733
+ );
734
+ return hasWithdrawKeywords || hasActionKeywords;
735
+ },
736
+ handler: async (runtime, message, state, _options, callback) => {
737
+ logger4.log("Starting AAVE_WITHDRAW handler...");
738
+ let currentState = state;
739
+ if (!currentState) {
740
+ currentState = await runtime.composeState(message);
741
+ } else {
742
+ currentState = await runtime.composeState(message, ["RECENT_MESSAGES"]);
743
+ }
744
+ const prompt = composePromptFromState4({
745
+ state: currentState,
746
+ template: withdrawTemplate
747
+ });
748
+ const result = await runtime.useModel(ModelType4.TEXT_SMALL, {
749
+ prompt,
750
+ stopSequences: []
751
+ });
752
+ const content = parseKeyValueXml4(result);
753
+ logger4.debug("Parsed content:", content);
754
+ if (!isValidWithdrawContent(content)) {
755
+ logger4.error("Invalid content for AAVE_WITHDRAW action.");
756
+ const errorMessage = "Unable to process withdraw request. Please specify the asset and amount to withdraw.";
757
+ callback?.({
758
+ text: errorMessage,
759
+ content: { error: "Invalid withdraw parameters" }
760
+ });
761
+ return {
762
+ text: errorMessage,
763
+ success: false
764
+ };
765
+ }
766
+ try {
767
+ const rpcUrl = runtime.getSetting("BASE_RPC_URL");
768
+ const privateKey = runtime.getSetting("WALLET_PRIVATE_KEY");
769
+ if (!rpcUrl || !privateKey) {
770
+ const errorMessage = "Configuration error: RPC URL and private key are required.";
771
+ callback?.({
772
+ text: errorMessage,
773
+ content: { error: "Missing configuration" }
774
+ });
775
+ return {
776
+ text: errorMessage,
777
+ success: false
778
+ };
779
+ }
780
+ const assetAddresses = {
781
+ USDC: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
782
+ WETH: "0x4200000000000000000000000000000000000006",
783
+ ETH: "0x4200000000000000000000000000000000000006",
784
+ DAI: "0x50c5725949A6F0c72E6C4a641F24049A917DB0Cb"
785
+ };
786
+ const assetAddress = assetAddresses[content.asset.toUpperCase()];
787
+ if (!assetAddress) {
788
+ const errorMessage = `Unsupported asset: ${content.asset}. Supported assets: USDC, WETH, DAI`;
789
+ callback?.({
790
+ text: errorMessage,
791
+ content: { error: "Unsupported asset" }
792
+ });
793
+ return {
794
+ text: errorMessage,
795
+ success: false
796
+ };
797
+ }
798
+ const isMaxWithdraw = content.amount === "max" || content.amount === "-1";
799
+ const amount = isMaxWithdraw ? "all supplied funds" : `${content.amount} ${content.asset}`;
800
+ logger4.debug("Would withdraw:", {
801
+ asset: content.asset,
802
+ amount: content.amount,
803
+ assetAddress,
804
+ isMaxWithdraw
805
+ });
806
+ const mockTxHash = "0xabcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890";
807
+ const responseText = isMaxWithdraw ? `\u2705 Successfully withdrew all ${content.asset} from Aave V3!
808
+
809
+ Transaction hash: ${mockTxHash}
810
+ Status: Supply position closed
811
+ Remaining supply: 0 ${content.asset}
812
+ Health factor: Updated
813
+
814
+ \u{1F389} All your ${content.asset} has been withdrawn successfully.
815
+
816
+ Note: This is a demonstration. In production, actual blockchain transactions would be executed.` : `\u2705 Successfully withdrew ${content.amount} ${content.asset} from Aave V3!
817
+
818
+ Transaction hash: ${mockTxHash}
819
+ Withdrawn amount: ${content.amount} ${content.asset}
820
+ Remaining supply: Reduced
821
+ Health factor: Updated
822
+
823
+ Your ${content.asset} has been successfully withdrawn to your wallet.
824
+
825
+ Note: This is a demonstration. In production, actual blockchain transactions would be executed.`;
826
+ callback?.({
827
+ text: responseText,
828
+ content: {
829
+ action: "AAVE_WITHDRAW",
830
+ asset: content.asset,
831
+ amount: content.amount,
832
+ transactionHash: mockTxHash,
833
+ isMaxWithdraw,
834
+ success: true
835
+ }
836
+ });
837
+ return {
838
+ text: `Successfully withdrew ${content.amount} ${content.asset} from Aave V3`,
839
+ success: true,
840
+ data: {
841
+ asset: content.asset,
842
+ amount: content.amount,
843
+ transactionHash: mockTxHash
844
+ }
845
+ };
846
+ } catch (error) {
847
+ logger4.error("Withdraw operation failed:", error);
848
+ const errorMessage = "Failed to withdraw from Aave. Please try again.";
849
+ callback?.({
850
+ text: errorMessage,
851
+ content: {
852
+ error: error instanceof Error ? error.message : String(error)
853
+ }
854
+ });
855
+ return {
856
+ text: errorMessage,
857
+ success: false
858
+ };
859
+ }
860
+ },
861
+ examples: [
862
+ [
863
+ {
864
+ name: "{{user1}}",
865
+ content: {
866
+ text: "Withdraw 50 USDC from Aave"
867
+ }
868
+ },
869
+ {
870
+ name: "{{user2}}",
871
+ content: {
872
+ text: "Withdraw operation completed successfully",
873
+ action: "AAVE_WITHDRAW"
874
+ }
875
+ }
876
+ ],
877
+ [
878
+ {
879
+ name: "{{user1}}",
880
+ content: {
881
+ text: "Withdraw all my ETH supply from Aave"
882
+ }
883
+ },
884
+ {
885
+ name: "{{user2}}",
886
+ content: {
887
+ text: "All ETH withdrawn from Aave",
888
+ action: "AAVE_WITHDRAW"
889
+ }
890
+ }
891
+ ]
892
+ ]
893
+ };
894
+ function isValidWithdrawContent(content) {
895
+ logger4.debug("Content for validation", content);
896
+ return content && typeof content.asset === "string" && content.asset.length > 0 && typeof content.amount === "string" && (parseFloat(content.amount) > 0 || content.amount === "max" || content.amount === "-1");
897
+ }
898
+
899
+ // src/actions/rateSwitch.ts
900
+ import {
901
+ ModelType as ModelType5,
902
+ composePromptFromState as composePromptFromState5,
903
+ logger as logger5,
904
+ parseKeyValueXml as parseKeyValueXml5
905
+ } from "@elizaos/core";
906
+ var rateSwitchTemplate = `Respond with an XML block containing only the extracted values. Use key-value pairs.
907
+
908
+ Example response for rate switch request:
909
+ <response>
910
+ <asset>USDC</asset>
911
+ <targetRateMode>stable</targetRateMode>
912
+ </response>
913
+
914
+ ## Recent Messages
915
+
916
+ {{recentMessages}}
917
+
918
+ Given the recent messages, extract the following information about the rate switch request:
919
+ - Asset: The borrowed token to switch rates for (e.g., USDC, ETH, DAI, WETH)
920
+ - TargetRateMode: The desired rate mode ('stable' or 'variable')
921
+
922
+ Respond with an XML block containing only the extracted values.`;
923
+ var rateSwitchAction = {
924
+ name: "AAVE_RATE_SWITCH",
925
+ similes: [
926
+ "SWITCH_RATE_MODE",
927
+ "CHANGE_INTEREST_RATE",
928
+ "SWITCH_TO_STABLE",
929
+ "SWITCH_TO_VARIABLE",
930
+ "CHANGE_RATE"
931
+ ],
932
+ description: "Switch between stable and variable interest rates on borrowed assets",
933
+ validate: async (runtime, message) => {
934
+ logger5.debug("Validating AAVE_RATE_SWITCH action");
935
+ const rpcUrl = runtime.getSetting("BASE_RPC_URL");
936
+ const privateKey = runtime.getSetting("WALLET_PRIVATE_KEY");
937
+ if (!rpcUrl || !privateKey) {
938
+ logger5.error("BASE_RPC_URL and WALLET_PRIVATE_KEY are required");
939
+ return false;
940
+ }
941
+ const text = message.content.text?.toLowerCase() || "";
942
+ const switchKeywords = ["switch", "change", "convert"];
943
+ const rateKeywords = ["rate", "stable", "variable", "interest"];
944
+ const actionKeywords = ["aave", "rate mode"];
945
+ const hasSwitchKeywords = switchKeywords.some(
946
+ (keyword) => text.includes(keyword)
947
+ );
948
+ const hasRateKeywords = rateKeywords.some(
949
+ (keyword) => text.includes(keyword)
950
+ );
951
+ const hasActionKeywords = actionKeywords.some(
952
+ (keyword) => text.includes(keyword)
953
+ );
954
+ return hasSwitchKeywords && hasRateKeywords || hasActionKeywords;
955
+ },
956
+ handler: async (runtime, message, state, _options, callback) => {
957
+ logger5.log("Starting AAVE_RATE_SWITCH handler...");
958
+ let currentState = state;
959
+ if (!currentState) {
960
+ currentState = await runtime.composeState(message);
961
+ } else {
962
+ currentState = await runtime.composeState(message, ["RECENT_MESSAGES"]);
963
+ }
964
+ const prompt = composePromptFromState5({
965
+ state: currentState,
966
+ template: rateSwitchTemplate
967
+ });
968
+ const result = await runtime.useModel(ModelType5.TEXT_SMALL, {
969
+ prompt,
970
+ stopSequences: []
971
+ });
972
+ const content = parseKeyValueXml5(result);
973
+ logger5.debug("Parsed content:", content);
974
+ if (!isValidRateSwitchContent(content)) {
975
+ logger5.error("Invalid content for AAVE_RATE_SWITCH action.");
976
+ const errorMessage = "Unable to process rate switch request. Please specify the asset and rate mode to switch to.";
977
+ callback?.({
978
+ text: errorMessage,
979
+ content: { error: "Invalid rate switch parameters" }
980
+ });
981
+ return {
982
+ text: errorMessage,
983
+ success: false
984
+ };
985
+ }
986
+ try {
987
+ const rpcUrl = runtime.getSetting("BASE_RPC_URL");
988
+ const privateKey = runtime.getSetting("WALLET_PRIVATE_KEY");
989
+ if (!rpcUrl || !privateKey) {
990
+ const errorMessage = "Configuration error: RPC URL and private key are required.";
991
+ callback?.({
992
+ text: errorMessage,
993
+ content: { error: "Missing configuration" }
994
+ });
995
+ return {
996
+ text: errorMessage,
997
+ success: false
998
+ };
999
+ }
1000
+ const assetAddresses = {
1001
+ USDC: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
1002
+ WETH: "0x4200000000000000000000000000000000000006",
1003
+ ETH: "0x4200000000000000000000000000000000000006",
1004
+ DAI: "0x50c5725949A6F0c72E6C4a641F24049A917DB0Cb"
1005
+ };
1006
+ const assetAddress = assetAddresses[content.asset.toUpperCase()];
1007
+ if (!assetAddress) {
1008
+ const errorMessage = `Unsupported asset: ${content.asset}. Supported assets: USDC, WETH, DAI`;
1009
+ callback?.({
1010
+ text: errorMessage,
1011
+ content: { error: "Unsupported asset" }
1012
+ });
1013
+ return {
1014
+ text: errorMessage,
1015
+ success: false
1016
+ };
1017
+ }
1018
+ const targetRateMode = content.targetRateMode.toLowerCase();
1019
+ const currentRateMode = targetRateMode === "stable" ? "variable" : "stable";
1020
+ logger5.debug("Would switch rate:", {
1021
+ asset: content.asset,
1022
+ fromRate: currentRateMode,
1023
+ toRate: targetRateMode,
1024
+ assetAddress
1025
+ });
1026
+ const mockTxHash = "0xdef123456789abcdef123456789abcdef123456789abcdef123456789abcdef12";
1027
+ const responseText = `\u2705 Successfully switched ${content.asset} interest rate to ${targetRateMode} mode!
1028
+
1029
+ Transaction hash: ${mockTxHash}
1030
+ Asset: ${content.asset}
1031
+ Previous rate: ${currentRateMode}
1032
+ New rate: ${targetRateMode}
1033
+ New APR: ~${targetRateMode === "stable" ? "4.2%" : "3.9%"}
1034
+
1035
+ ${targetRateMode === "stable" ? "\u{1F512} Your rate is now fixed and protected from market volatility" : "\u{1F4C8} Your rate will now fluctuate with market conditions but typically offers better rates"}
1036
+
1037
+ Note: This is a demonstration. In production, actual blockchain transactions would be executed.`;
1038
+ callback?.({
1039
+ text: responseText,
1040
+ content: {
1041
+ action: "AAVE_RATE_SWITCH",
1042
+ asset: content.asset,
1043
+ fromRateMode: currentRateMode,
1044
+ toRateMode: targetRateMode,
1045
+ transactionHash: mockTxHash,
1046
+ success: true
1047
+ }
1048
+ });
1049
+ return {
1050
+ text: `Successfully switched to ${content.targetRateMode} rate for ${content.asset}`,
1051
+ success: true,
1052
+ data: {
1053
+ asset: content.asset,
1054
+ rateMode: content.targetRateMode,
1055
+ transactionHash: mockTxHash
1056
+ }
1057
+ };
1058
+ } catch (error) {
1059
+ logger5.error("Rate switch operation failed:", error);
1060
+ const errorMessage = "Failed to switch interest rate mode. Please try again.";
1061
+ callback?.({
1062
+ text: errorMessage,
1063
+ content: {
1064
+ error: error instanceof Error ? error.message : String(error)
1065
+ }
1066
+ });
1067
+ return {
1068
+ text: errorMessage,
1069
+ success: false
1070
+ };
1071
+ }
1072
+ },
1073
+ examples: [
1074
+ [
1075
+ {
1076
+ name: "{{user1}}",
1077
+ content: {
1078
+ text: "Switch my USDC loan to stable rate"
1079
+ }
1080
+ },
1081
+ {
1082
+ name: "{{user2}}",
1083
+ content: {
1084
+ text: "Interest rate switched to stable mode",
1085
+ action: "AAVE_RATE_SWITCH"
1086
+ }
1087
+ }
1088
+ ],
1089
+ [
1090
+ {
1091
+ name: "{{user1}}",
1092
+ content: {
1093
+ text: "Change my ETH borrow to variable rate"
1094
+ }
1095
+ },
1096
+ {
1097
+ name: "{{user2}}",
1098
+ content: {
1099
+ text: "ETH loan switched to variable rate",
1100
+ action: "AAVE_RATE_SWITCH"
1101
+ }
1102
+ }
1103
+ ]
1104
+ ]
1105
+ };
1106
+ function isValidRateSwitchContent(content) {
1107
+ logger5.debug("Content for validation", content);
1108
+ return content && typeof content.asset === "string" && content.asset.length > 0 && typeof content.targetRateMode === "string" && (content.targetRateMode === "stable" || content.targetRateMode === "variable");
1109
+ }
1110
+
1111
+ // src/actions/collateralManagement.ts
1112
+ import {
1113
+ ModelType as ModelType6,
1114
+ composePromptFromState as composePromptFromState6,
1115
+ logger as logger6,
1116
+ parseKeyValueXml as parseKeyValueXml6
1117
+ } from "@elizaos/core";
1118
+ var collateralManagementTemplate = `Respond with an XML block containing only the extracted values. Use key-value pairs.
1119
+
1120
+ Example response for collateral management request:
1121
+ <response>
1122
+ <asset>USDC</asset>
1123
+ <enable>true</enable>
1124
+ </response>
1125
+
1126
+ ## Recent Messages
1127
+
1128
+ {{recentMessages}}
1129
+
1130
+ Given the recent messages, extract the following information about the collateral management request:
1131
+ - Asset: The token to manage as collateral (e.g., USDC, ETH, DAI, WETH)
1132
+ - Enable: Whether to enable or disable as collateral (true/false)
1133
+
1134
+ Respond with an XML block containing only the extracted values.`;
1135
+ var collateralManagementAction = {
1136
+ name: "AAVE_COLLATERAL_MANAGEMENT",
1137
+ similes: [
1138
+ "ENABLE_COLLATERAL",
1139
+ "DISABLE_COLLATERAL",
1140
+ "TOGGLE_COLLATERAL",
1141
+ "MANAGE_COLLATERAL",
1142
+ "SET_COLLATERAL"
1143
+ ],
1144
+ description: "Enable or disable assets as collateral in Aave V3",
1145
+ validate: async (runtime, message) => {
1146
+ logger6.debug("Validating AAVE_COLLATERAL_MANAGEMENT action");
1147
+ const rpcUrl = runtime.getSetting("BASE_RPC_URL");
1148
+ const privateKey = runtime.getSetting("WALLET_PRIVATE_KEY");
1149
+ if (!rpcUrl || !privateKey) {
1150
+ logger6.error("BASE_RPC_URL and WALLET_PRIVATE_KEY are required");
1151
+ return false;
1152
+ }
1153
+ const text = message.content.text?.toLowerCase() || "";
1154
+ const collateralKeywords = [
1155
+ "collateral",
1156
+ "enable",
1157
+ "disable",
1158
+ "toggle",
1159
+ "manage"
1160
+ ];
1161
+ const actionKeywords = [
1162
+ "as collateral",
1163
+ "for borrowing",
1164
+ "aave collateral"
1165
+ ];
1166
+ const hasCollateralKeywords = collateralKeywords.some(
1167
+ (keyword) => text.includes(keyword)
1168
+ );
1169
+ const hasActionKeywords = actionKeywords.some(
1170
+ (keyword) => text.includes(keyword)
1171
+ );
1172
+ return hasCollateralKeywords || hasActionKeywords;
1173
+ },
1174
+ handler: async (runtime, message, state, _options, callback) => {
1175
+ logger6.log("Starting AAVE_COLLATERAL_MANAGEMENT handler...");
1176
+ let currentState = state;
1177
+ if (!currentState) {
1178
+ currentState = await runtime.composeState(message);
1179
+ } else {
1180
+ currentState = await runtime.composeState(message, ["RECENT_MESSAGES"]);
1181
+ }
1182
+ const prompt = composePromptFromState6({
1183
+ state: currentState,
1184
+ template: collateralManagementTemplate
1185
+ });
1186
+ const result = await runtime.useModel(ModelType6.TEXT_SMALL, {
1187
+ prompt,
1188
+ stopSequences: []
1189
+ });
1190
+ const content = parseKeyValueXml6(result);
1191
+ logger6.debug("Parsed content:", content);
1192
+ if (!isValidCollateralContent(content)) {
1193
+ logger6.error("Invalid content for AAVE_COLLATERAL_MANAGEMENT action.");
1194
+ const errorMessage = "Unable to process collateral management request. Please specify the asset and action (enable/disable).";
1195
+ callback?.({
1196
+ text: errorMessage,
1197
+ content: { error: "Invalid collateral parameters" }
1198
+ });
1199
+ return {
1200
+ text: errorMessage,
1201
+ success: false
1202
+ };
1203
+ }
1204
+ try {
1205
+ const rpcUrl = runtime.getSetting("BASE_RPC_URL");
1206
+ const privateKey = runtime.getSetting("WALLET_PRIVATE_KEY");
1207
+ if (!rpcUrl || !privateKey) {
1208
+ const errorMessage = "Configuration error: RPC URL and private key are required.";
1209
+ callback?.({
1210
+ text: errorMessage,
1211
+ content: { error: "Missing configuration" }
1212
+ });
1213
+ return {
1214
+ text: errorMessage,
1215
+ success: false
1216
+ };
1217
+ }
1218
+ const assetAddresses = {
1219
+ USDC: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
1220
+ WETH: "0x4200000000000000000000000000000000000006",
1221
+ ETH: "0x4200000000000000000000000000000000000006",
1222
+ DAI: "0x50c5725949A6F0c72E6C4a641F24049A917DB0Cb"
1223
+ };
1224
+ const assetAddress = assetAddresses[content.asset.toUpperCase()];
1225
+ if (!assetAddress) {
1226
+ const errorMessage = `Unsupported asset: ${content.asset}. Supported assets: USDC, WETH, DAI`;
1227
+ callback?.({
1228
+ text: errorMessage,
1229
+ content: { error: "Unsupported asset" }
1230
+ });
1231
+ return {
1232
+ text: errorMessage,
1233
+ success: false
1234
+ };
1235
+ }
1236
+ const isEnabling = content.enable;
1237
+ const action = isEnabling ? "enabled" : "disabled";
1238
+ const actionVerb = isEnabling ? "enable" : "disable";
1239
+ logger6.debug("Would manage collateral:", {
1240
+ asset: content.asset,
1241
+ enable: content.enable,
1242
+ assetAddress
1243
+ });
1244
+ const mockTxHash = "0xcdef456789abcdef456789abcdef456789abcdef456789abcdef456789abcdef45";
1245
+ const responseText = `\u2705 Successfully ${action} ${content.asset} as collateral in Aave V3!
1246
+
1247
+ Transaction hash: ${mockTxHash}
1248
+ Asset: ${content.asset}
1249
+ Status: ${action.charAt(0).toUpperCase() + action.slice(1)} as collateral
1250
+ ${isEnabling ? `
1251
+ \u{1F4B0} Your ${content.asset} can now be used as collateral for borrowing
1252
+ \u{1F4C8} This increases your borrowing power
1253
+ \u26A0\uFE0F Remember that collateral can be liquidated if health factor drops below 1.0` : `
1254
+ \u{1F513} Your ${content.asset} is no longer being used as collateral
1255
+ \u{1F4C9} This reduces your borrowing power but protects the asset from liquidation
1256
+ \u2705 Position is safer but with reduced leverage capability`}
1257
+
1258
+ Note: This is a demonstration. In production, actual blockchain transactions would be executed.`;
1259
+ callback?.({
1260
+ text: responseText,
1261
+ content: {
1262
+ action: "AAVE_COLLATERAL_MANAGEMENT",
1263
+ asset: content.asset,
1264
+ enable: content.enable,
1265
+ transactionHash: mockTxHash,
1266
+ success: true
1267
+ }
1268
+ });
1269
+ return {
1270
+ text: `Successfully ${content.enable ? "enabled" : "disabled"} ${content.asset} as collateral on Aave V3`,
1271
+ success: true,
1272
+ data: {
1273
+ asset: content.asset,
1274
+ action: content.enable ? "enable" : "disable",
1275
+ transactionHash: mockTxHash
1276
+ }
1277
+ };
1278
+ } catch (error) {
1279
+ logger6.error("Collateral management operation failed:", error);
1280
+ const errorMessage = "Failed to manage collateral on Aave. Please try again.";
1281
+ callback?.({
1282
+ text: errorMessage,
1283
+ content: {
1284
+ error: error instanceof Error ? error.message : String(error)
1285
+ }
1286
+ });
1287
+ return {
1288
+ text: errorMessage,
1289
+ success: false
1290
+ };
1291
+ }
1292
+ },
1293
+ examples: [
1294
+ [
1295
+ {
1296
+ name: "{{user1}}",
1297
+ content: {
1298
+ text: "Enable USDC as collateral on Aave"
1299
+ }
1300
+ },
1301
+ {
1302
+ name: "{{user2}}",
1303
+ content: {
1304
+ text: "USDC enabled as collateral",
1305
+ action: "AAVE_COLLATERAL_MANAGEMENT"
1306
+ }
1307
+ }
1308
+ ],
1309
+ [
1310
+ {
1311
+ name: "{{user1}}",
1312
+ content: {
1313
+ text: "Disable ETH collateral to protect it"
1314
+ }
1315
+ },
1316
+ {
1317
+ name: "{{user2}}",
1318
+ content: {
1319
+ text: "ETH collateral disabled successfully",
1320
+ action: "AAVE_COLLATERAL_MANAGEMENT"
1321
+ }
1322
+ }
1323
+ ]
1324
+ ]
1325
+ };
1326
+ function isValidCollateralContent(content) {
1327
+ logger6.debug("Content for validation", content);
1328
+ return content && typeof content.asset === "string" && content.asset.length > 0 && typeof content.enable === "boolean";
1329
+ }
1330
+
1331
+ // src/actions/eMode.ts
1332
+ import {
1333
+ ModelType as ModelType7,
1334
+ composePromptFromState as composePromptFromState7,
1335
+ logger as logger7,
1336
+ parseKeyValueXml as parseKeyValueXml7
1337
+ } from "@elizaos/core";
1338
+ var eModeTemplate = `Respond with an XML block containing only the extracted values. Use key-value pairs.
1339
+
1340
+ Example response for eMode request:
1341
+ <response>
1342
+ <categoryId>1</categoryId>
1343
+ <enable>true</enable>
1344
+ </response>
1345
+
1346
+ ## Recent Messages
1347
+
1348
+ {{recentMessages}}
1349
+
1350
+ Given the recent messages, extract the following information about the eMode request:
1351
+ - CategoryId: The eMode category ID (1 for stablecoins, 2 for ETH correlated assets)
1352
+ - Enable: Whether to enable or disable eMode (true/false)
1353
+
1354
+ Common eMode categories:
1355
+ - Category 1: Stablecoins (USDC, DAI, USDT)
1356
+ - Category 2: ETH derivatives (ETH, stETH, wstETH)
1357
+
1358
+ Respond with an XML block containing only the extracted values.`;
1359
+ var eModeAction = {
1360
+ name: "AAVE_EMODE",
1361
+ similes: [
1362
+ "ENABLE_EMODE",
1363
+ "DISABLE_EMODE",
1364
+ "TOGGLE_EMODE",
1365
+ "EFFICIENCY_MODE",
1366
+ "HIGH_EFFICIENCY"
1367
+ ],
1368
+ description: "Enable or disable Efficiency Mode (eMode) in Aave V3 for higher borrowing power with correlated assets",
1369
+ validate: async (runtime, message) => {
1370
+ logger7.debug("Validating AAVE_EMODE action");
1371
+ const rpcUrl = runtime.getSetting("BASE_RPC_URL");
1372
+ const privateKey = runtime.getSetting("WALLET_PRIVATE_KEY");
1373
+ if (!rpcUrl || !privateKey) {
1374
+ logger7.error("BASE_RPC_URL and WALLET_PRIVATE_KEY are required");
1375
+ return false;
1376
+ }
1377
+ const text = message.content.text?.toLowerCase() || "";
1378
+ const eModeKeywords = [
1379
+ "emode",
1380
+ "efficiency mode",
1381
+ "high efficiency",
1382
+ "enable emode",
1383
+ "disable emode"
1384
+ ];
1385
+ const actionKeywords = [
1386
+ "aave emode",
1387
+ "efficiency",
1388
+ "higher ltv",
1389
+ "better rates"
1390
+ ];
1391
+ const hasEModeKeywords = eModeKeywords.some(
1392
+ (keyword) => text.includes(keyword)
1393
+ );
1394
+ const hasActionKeywords = actionKeywords.some(
1395
+ (keyword) => text.includes(keyword)
1396
+ );
1397
+ return hasEModeKeywords || hasActionKeywords;
1398
+ },
1399
+ handler: async (runtime, message, state, _options, callback) => {
1400
+ logger7.log("Starting AAVE_EMODE handler...");
1401
+ let currentState = state;
1402
+ if (!currentState) {
1403
+ currentState = await runtime.composeState(message);
1404
+ } else {
1405
+ currentState = await runtime.composeState(message, ["RECENT_MESSAGES"]);
1406
+ }
1407
+ const prompt = composePromptFromState7({
1408
+ state: currentState,
1409
+ template: eModeTemplate
1410
+ });
1411
+ const result = await runtime.useModel(ModelType7.TEXT_SMALL, {
1412
+ prompt,
1413
+ stopSequences: []
1414
+ });
1415
+ const content = parseKeyValueXml7(result);
1416
+ logger7.debug("Parsed content:", content);
1417
+ if (!isValidEModeContent(content)) {
1418
+ logger7.error("Invalid content for AAVE_EMODE action.");
1419
+ const errorMessage = "Unable to process e-mode request. Please specify the category ID.";
1420
+ callback?.({
1421
+ text: errorMessage,
1422
+ content: { error: "Invalid e-mode parameters" }
1423
+ });
1424
+ return {
1425
+ text: errorMessage,
1426
+ success: false
1427
+ };
1428
+ }
1429
+ try {
1430
+ const rpcUrl = runtime.getSetting("BASE_RPC_URL");
1431
+ const privateKey = runtime.getSetting("WALLET_PRIVATE_KEY");
1432
+ if (!rpcUrl || !privateKey) {
1433
+ const errorMessage = "Configuration error: RPC URL and private key are required.";
1434
+ callback?.({
1435
+ text: errorMessage,
1436
+ content: { error: "Missing configuration" }
1437
+ });
1438
+ return {
1439
+ text: errorMessage,
1440
+ success: false
1441
+ };
1442
+ }
1443
+ const isEnabling = content?.enable ?? false;
1444
+ const categoryId = content?.categoryId || 0;
1445
+ const action = isEnabling ? "enabled" : "disabled";
1446
+ const categoryInfo = getCategoryInfo(categoryId);
1447
+ logger7.debug("Would set eMode:", {
1448
+ categoryId,
1449
+ enable: content.enable,
1450
+ categoryInfo
1451
+ });
1452
+ const mockTxHash = "0xedef789abcdef789abcdef789abcdef789abcdef789abcdef789abcdef789abcdef";
1453
+ const responseText = isEnabling ? `\u2705 Successfully enabled Efficiency Mode (eMode) in Aave V3!
1454
+
1455
+ Transaction hash: ${mockTxHash}
1456
+ Category: ${categoryId} - ${categoryInfo.name}
1457
+ Status: eMode enabled
1458
+
1459
+ Benefits of eMode:
1460
+ \u{1F3AF} Higher LTV: Up to ${categoryInfo.ltv}% (vs standard ~80%)
1461
+ \u{1F4C8} Higher Liquidation Threshold: ${categoryInfo.liquidationThreshold}%
1462
+ \u{1F4B0} Better borrowing power with correlated assets
1463
+ \u26A1 Optimized for ${categoryInfo.description}
1464
+
1465
+ \u26A0\uFE0F Remember: eMode works best when your collateral and borrowed assets are in the same category.
1466
+
1467
+ Note: This is a demonstration. In production, actual blockchain transactions would be executed.` : `\u2705 Successfully disabled Efficiency Mode (eMode) in Aave V3!
1468
+
1469
+ Transaction hash: ${mockTxHash}
1470
+ Status: eMode disabled
1471
+ Previous Category: ${categoryId} - ${categoryInfo.name}
1472
+
1473
+ Changes:
1474
+ \u{1F4C9} LTV reduced to standard rates (~80%)
1475
+ \u{1F4C9} Liquidation threshold reduced to standard rates
1476
+ \u{1F504} Back to standard borrowing parameters
1477
+ \u2705 More flexibility to use diverse assets
1478
+
1479
+ Note: This is a demonstration. In production, actual blockchain transactions would be executed.`;
1480
+ callback?.({
1481
+ text: responseText,
1482
+ content: {
1483
+ action: "AAVE_EMODE",
1484
+ categoryId: content?.categoryId || 0,
1485
+ enable: content?.enable ?? false,
1486
+ categoryInfo,
1487
+ transactionHash: mockTxHash,
1488
+ success: true
1489
+ }
1490
+ });
1491
+ return {
1492
+ text: `Successfully ${categoryId === 0 ? "disabled" : "enabled"} E-Mode on Aave V3`,
1493
+ success: true,
1494
+ data: {
1495
+ categoryId,
1496
+ transactionHash: mockTxHash
1497
+ }
1498
+ };
1499
+ } catch (error) {
1500
+ logger7.error("E-Mode operation failed:", error);
1501
+ const errorMessage = "Failed to set e-mode. Please try again.";
1502
+ callback?.({
1503
+ text: errorMessage,
1504
+ content: {
1505
+ error: error instanceof Error ? error.message : String(error)
1506
+ }
1507
+ });
1508
+ return {
1509
+ text: errorMessage,
1510
+ success: false
1511
+ };
1512
+ }
1513
+ },
1514
+ examples: [
1515
+ [
1516
+ {
1517
+ name: "{{user1}}",
1518
+ content: {
1519
+ text: "Enable eMode for stablecoins on Aave"
1520
+ }
1521
+ },
1522
+ {
1523
+ name: "{{user2}}",
1524
+ content: {
1525
+ text: "Efficiency Mode enabled for stablecoin category",
1526
+ action: "AAVE_EMODE"
1527
+ }
1528
+ }
1529
+ ],
1530
+ [
1531
+ {
1532
+ name: "{{user1}}",
1533
+ content: {
1534
+ text: "Disable efficiency mode on Aave"
1535
+ }
1536
+ },
1537
+ {
1538
+ name: "{{user2}}",
1539
+ content: {
1540
+ text: "Efficiency Mode disabled",
1541
+ action: "AAVE_EMODE"
1542
+ }
1543
+ }
1544
+ ]
1545
+ ]
1546
+ };
1547
+ function isValidEModeContent(content) {
1548
+ logger7.debug("Content for validation", content);
1549
+ return content && typeof content.categoryId === "number" && content.categoryId >= 0 && content.categoryId <= 2 && typeof content.enable === "boolean";
1550
+ }
1551
+ function getCategoryInfo(categoryId) {
1552
+ switch (categoryId) {
1553
+ case 1:
1554
+ return {
1555
+ name: "Stablecoins",
1556
+ description: "USD-pegged stablecoins (USDC, DAI, USDT)",
1557
+ ltv: 93,
1558
+ liquidationThreshold: 95
1559
+ };
1560
+ case 2:
1561
+ return {
1562
+ name: "ETH Correlated",
1563
+ description: "ETH and ETH derivative assets (ETH, stETH, wstETH)",
1564
+ ltv: 90,
1565
+ liquidationThreshold: 93
1566
+ };
1567
+ default:
1568
+ return {
1569
+ name: "Disabled",
1570
+ description: "Standard mode with diverse assets",
1571
+ ltv: 80,
1572
+ liquidationThreshold: 85
1573
+ };
1574
+ }
1575
+ }
1576
+
1577
+ // src/actions/flashLoan.ts
1578
+ import {
1579
+ ModelType as ModelType8,
1580
+ composePromptFromState as composePromptFromState8,
1581
+ logger as logger8,
1582
+ parseKeyValueXml as parseKeyValueXml8
1583
+ } from "@elizaos/core";
1584
+ var flashLoanTemplate = `Respond with an XML block containing only the extracted values. Use key-value pairs.
1585
+
1586
+ Example response for flash loan request:
1587
+ <response>
1588
+ <assets>USDC,ETH</assets>
1589
+ <amounts>1000,0.5</amounts>
1590
+ <receiverAddress>0x1234567890abcdef1234567890abcdef12345678</receiverAddress>
1591
+ <params></params>
1592
+ </response>
1593
+
1594
+ ## Recent Messages
1595
+
1596
+ {{recentMessages}}
1597
+
1598
+ Given the recent messages, extract the following information about the flash loan request:
1599
+ - Assets: Comma-separated list of tokens to flash loan (e.g., USDC, ETH, DAI)
1600
+ - Amounts: Comma-separated list of amounts corresponding to each asset
1601
+ - ReceiverAddress: Optional address of the flash loan receiver contract (defaults to user address)
1602
+ - Params: Optional additional parameters for the flash loan (usually empty)
1603
+
1604
+ Respond with an XML block containing only the extracted values.`;
1605
+ var flashLoanAction = {
1606
+ name: "AAVE_FLASH_LOAN",
1607
+ similes: [
1608
+ "FLASH_LOAN",
1609
+ "GET_FLASH_LOAN",
1610
+ "EXECUTE_FLASH_LOAN",
1611
+ "ARBITRAGE_FLASH_LOAN",
1612
+ "INSTANT_LOAN"
1613
+ ],
1614
+ description: "Execute a flash loan from Aave V3 for arbitrage or other advanced strategies",
1615
+ validate: async (runtime, message) => {
1616
+ logger8.debug("Validating AAVE_FLASH_LOAN action");
1617
+ const rpcUrl = runtime.getSetting("BASE_RPC_URL");
1618
+ const privateKey = runtime.getSetting("WALLET_PRIVATE_KEY");
1619
+ if (!rpcUrl || !privateKey) {
1620
+ logger8.error("BASE_RPC_URL and WALLET_PRIVATE_KEY are required");
1621
+ return false;
1622
+ }
1623
+ const text = message.content.text?.toLowerCase() || "";
1624
+ const flashLoanKeywords = [
1625
+ "flash loan",
1626
+ "flashloan",
1627
+ "instant loan",
1628
+ "arbitrage",
1629
+ "flash borrow"
1630
+ ];
1631
+ const actionKeywords = ["aave flash", "from aave", "flash loan aave"];
1632
+ const hasFlashLoanKeywords = flashLoanKeywords.some(
1633
+ (keyword) => text.includes(keyword)
1634
+ );
1635
+ const hasActionKeywords = actionKeywords.some(
1636
+ (keyword) => text.includes(keyword)
1637
+ );
1638
+ return hasFlashLoanKeywords || hasActionKeywords;
1639
+ },
1640
+ handler: async (runtime, message, state, _options, callback) => {
1641
+ logger8.log("Starting AAVE_FLASH_LOAN handler...");
1642
+ let currentState = state;
1643
+ if (!currentState) {
1644
+ currentState = await runtime.composeState(message);
1645
+ } else {
1646
+ currentState = await runtime.composeState(message, ["RECENT_MESSAGES"]);
1647
+ }
1648
+ const prompt = composePromptFromState8({
1649
+ state: currentState,
1650
+ template: flashLoanTemplate
1651
+ });
1652
+ const result = await runtime.useModel(ModelType8.TEXT_SMALL, {
1653
+ prompt,
1654
+ stopSequences: []
1655
+ });
1656
+ const content = parseKeyValueXml8(result);
1657
+ logger8.debug("Parsed content:", content);
1658
+ if (!isValidFlashLoanContent(content)) {
1659
+ logger8.error("Invalid content for AAVE_FLASH_LOAN action.");
1660
+ const errorMessage = "Unable to process flash loan request. Please specify the asset, amount, and operation details.";
1661
+ callback?.({
1662
+ text: errorMessage,
1663
+ content: { error: "Invalid flash loan parameters" }
1664
+ });
1665
+ return {
1666
+ text: errorMessage,
1667
+ success: false
1668
+ };
1669
+ }
1670
+ try {
1671
+ const rpcUrl = runtime.getSetting("BASE_RPC_URL");
1672
+ const privateKey = runtime.getSetting("WALLET_PRIVATE_KEY");
1673
+ if (!rpcUrl || !privateKey) {
1674
+ const errorMessage = "Configuration error: RPC URL and private key are required.";
1675
+ callback?.({
1676
+ text: errorMessage,
1677
+ content: { error: "Missing configuration" }
1678
+ });
1679
+ return {
1680
+ text: errorMessage,
1681
+ success: false
1682
+ };
1683
+ }
1684
+ const assetsStr = typeof content.assets === "string" ? content.assets : content.assets.join(",");
1685
+ const amountsStr = typeof content.amounts === "string" ? content.amounts : content.amounts.join(",");
1686
+ const assets = assetsStr.split(",").map((asset) => asset.trim());
1687
+ const amounts = amountsStr.split(",").map((amount) => amount.trim());
1688
+ if (assets.length !== amounts.length) {
1689
+ const errorMessage = "Error: The number of assets must match the number of amounts.";
1690
+ callback?.({
1691
+ text: errorMessage,
1692
+ content: { error: "Asset/amount mismatch" }
1693
+ });
1694
+ return {
1695
+ text: errorMessage,
1696
+ success: false
1697
+ };
1698
+ }
1699
+ const assetAddresses = {
1700
+ USDC: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
1701
+ WETH: "0x4200000000000000000000000000000000000006",
1702
+ ETH: "0x4200000000000000000000000000000000000006",
1703
+ DAI: "0x50c5725949A6F0c72E6C4a641F24049A917DB0Cb"
1704
+ };
1705
+ const unsupportedAssets = assets.filter(
1706
+ (asset) => !assetAddresses[asset.toUpperCase()]
1707
+ );
1708
+ if (unsupportedAssets.length > 0) {
1709
+ const errorMessage = `Unsupported assets: ${unsupportedAssets.join(", ")}. Supported assets: USDC, WETH, DAI`;
1710
+ callback?.({
1711
+ text: errorMessage,
1712
+ content: { error: "Unsupported assets" }
1713
+ });
1714
+ return {
1715
+ text: errorMessage,
1716
+ success: false
1717
+ };
1718
+ }
1719
+ const resolvedAssetAddresses = assets.map(
1720
+ (asset) => assetAddresses[asset.toUpperCase()]
1721
+ );
1722
+ logger8.debug("Would execute flash loan:", {
1723
+ assets,
1724
+ amounts,
1725
+ assetAddresses: resolvedAssetAddresses,
1726
+ receiverAddress: content.receiverAddress || "user_address",
1727
+ params: content.params || ""
1728
+ });
1729
+ const totalFeesEstimate = amounts.reduce(
1730
+ (total, amount, index) => {
1731
+ const amountNum = parseFloat(amount);
1732
+ const feeAmount = (amountNum * 9e-4).toFixed(6);
1733
+ return total + `
1734
+ - ${assets[index]}: ${feeAmount} (0.09% fee)`;
1735
+ },
1736
+ ""
1737
+ );
1738
+ const mockTxHash = "0xfed456789abcdef456789abcdef456789abcdef456789abcdef456789abcdef456";
1739
+ const responseText = `\u2705 Flash loan executed successfully on Aave V3!
1740
+
1741
+ Transaction hash: ${mockTxHash}
1742
+ Flash loan details:
1743
+ ${assets.map((asset, i) => `- ${amounts[i]} ${asset}`).join("\n")}
1744
+
1745
+ Estimated fees:${totalFeesEstimate}
1746
+
1747
+ \u26A0\uFE0F IMPORTANT NOTES:
1748
+ - Flash loans must be repaid within the same transaction
1749
+ - You need a receiver contract to handle the flash loan logic
1750
+ - Ensure your arbitrage/strategy covers the fees
1751
+ - This is an advanced feature requiring smart contract development
1752
+
1753
+ \u{1F50D} Use cases:
1754
+ - Arbitrage opportunities
1755
+ - Debt refinancing
1756
+ - Collateral swapping
1757
+ - Liquidation protection
1758
+
1759
+ Note: This is a demonstration. In production, you would need a proper flash loan receiver contract and actual blockchain execution.`;
1760
+ callback?.({
1761
+ text: responseText,
1762
+ content: {
1763
+ action: "AAVE_FLASH_LOAN",
1764
+ assets,
1765
+ amounts,
1766
+ receiverAddress: content.receiverAddress || "user_address",
1767
+ params: content.params || "",
1768
+ transactionHash: mockTxHash,
1769
+ estimatedFees: totalFeesEstimate,
1770
+ success: true
1771
+ }
1772
+ });
1773
+ return {
1774
+ text: `Successfully executed flash loan for ${assets.join(", ")}`,
1775
+ success: true,
1776
+ data: {
1777
+ assets: content.assets,
1778
+ amounts: content.amounts,
1779
+ transactionHash: mockTxHash
1780
+ }
1781
+ };
1782
+ } catch (error) {
1783
+ logger8.error("Flash loan operation failed:", error);
1784
+ const errorMessage = "Failed to execute flash loan. Please try again.";
1785
+ callback?.({
1786
+ text: errorMessage,
1787
+ content: {
1788
+ error: error instanceof Error ? error.message : String(error)
1789
+ }
1790
+ });
1791
+ return {
1792
+ text: errorMessage,
1793
+ success: false
1794
+ };
1795
+ }
1796
+ },
1797
+ examples: [
1798
+ [
1799
+ {
1800
+ name: "{{user1}}",
1801
+ content: {
1802
+ text: "Execute a flash loan of 1000 USDC for arbitrage"
1803
+ }
1804
+ },
1805
+ {
1806
+ name: "{{user2}}",
1807
+ content: {
1808
+ text: "Flash loan executed successfully",
1809
+ action: "AAVE_FLASH_LOAN"
1810
+ }
1811
+ }
1812
+ ],
1813
+ [
1814
+ {
1815
+ name: "{{user1}}",
1816
+ content: {
1817
+ text: "Get flash loan of 0.5 ETH and 500 USDC from Aave"
1818
+ }
1819
+ },
1820
+ {
1821
+ name: "{{user2}}",
1822
+ content: {
1823
+ text: "Multi-asset flash loan completed",
1824
+ action: "AAVE_FLASH_LOAN"
1825
+ }
1826
+ }
1827
+ ]
1828
+ ]
1829
+ };
1830
+ function isValidFlashLoanContent(content) {
1831
+ logger8.debug("Content for validation", content);
1832
+ if (!content || !content.assets || typeof content.assets !== "string" && !Array.isArray(content.assets) || !content.amounts || typeof content.amounts !== "string" && !Array.isArray(content.amounts)) {
1833
+ return false;
1834
+ }
1835
+ const assetsStr = typeof content.assets === "string" ? content.assets : content.assets.join(",");
1836
+ const amountsStr = typeof content.amounts === "string" ? content.amounts : content.amounts.join(",");
1837
+ const assets = assetsStr.split(",").map((asset) => asset.trim());
1838
+ const amounts = amountsStr.split(",").map((amount) => amount.trim());
1839
+ if (assets.length === 0 || amounts.length === 0) {
1840
+ return false;
1841
+ }
1842
+ if (assets.length !== amounts.length) {
1843
+ return false;
1844
+ }
1845
+ const validAmounts = amounts.every(
1846
+ (amount) => !isNaN(parseFloat(amount)) && parseFloat(amount) > 0
1847
+ );
1848
+ const validAssets = assets.every((asset) => asset.length > 0);
1849
+ return validAmounts && validAssets;
1850
+ }
1851
+
1852
+ // src/providers/positionContext.ts
1853
+ import {
1854
+ logger as logger9
1855
+ } from "@elizaos/core";
1856
+ import BigNumber from "bignumber.js";
1857
+ import { createPublicClient as createPublicClient3, http as http3 } from "viem";
1858
+ import { base as base3 } from "viem/chains";
1859
+ import { AaveV3Base as AaveV3Base3 } from "@bgd-labs/aave-address-book";
1860
+ var positionContextProvider = {
1861
+ name: "positionContextProvider",
1862
+ description: "Provides current Aave V3 position context including supplies, borrows, and health metrics",
1863
+ dynamic: true,
1864
+ get: async (runtime, message, state) => {
1865
+ logger9.debug("positionContextProvider::get");
1866
+ const rpcUrl = runtime.getSetting("BASE_RPC_URL");
1867
+ const walletAddress = runtime.getSetting("WALLET_ADDRESS");
1868
+ if (!rpcUrl) {
1869
+ logger9.error("BASE_RPC_URL not configured");
1870
+ return {
1871
+ text: "Position data unavailable - RPC URL not configured",
1872
+ data: { error: "RPC URL required" }
1873
+ };
1874
+ }
1875
+ if (!walletAddress) {
1876
+ logger9.debug(
1877
+ "positionContextProvider: No wallet address configured, returning neutral context"
1878
+ );
1879
+ return {
1880
+ text: "Position data available when wallet address is provided",
1881
+ data: { provider: "positionContextProvider", status: "neutral" }
1882
+ };
1883
+ }
1884
+ try {
1885
+ const publicClient = createPublicClient3({
1886
+ chain: base3,
1887
+ transport: http3(rpcUrl)
1888
+ });
1889
+ const poolAddress = AaveV3Base3.POOL;
1890
+ const userData = await publicClient.readContract({
1891
+ address: poolAddress,
1892
+ abi: [
1893
+ {
1894
+ inputs: [
1895
+ { internalType: "address", name: "user", type: "address" }
1896
+ ],
1897
+ name: "getUserAccountData",
1898
+ outputs: [
1899
+ {
1900
+ internalType: "uint256",
1901
+ name: "totalCollateralETH",
1902
+ type: "uint256"
1903
+ },
1904
+ {
1905
+ internalType: "uint256",
1906
+ name: "totalDebtETH",
1907
+ type: "uint256"
1908
+ },
1909
+ {
1910
+ internalType: "uint256",
1911
+ name: "availableBorrowsETH",
1912
+ type: "uint256"
1913
+ },
1914
+ {
1915
+ internalType: "uint256",
1916
+ name: "currentLiquidationThreshold",
1917
+ type: "uint256"
1918
+ },
1919
+ { internalType: "uint256", name: "ltv", type: "uint256" },
1920
+ {
1921
+ internalType: "uint256",
1922
+ name: "healthFactor",
1923
+ type: "uint256"
1924
+ }
1925
+ ],
1926
+ stateMutability: "view",
1927
+ type: "function"
1928
+ }
1929
+ ],
1930
+ functionName: "getUserAccountData",
1931
+ args: [walletAddress]
1932
+ });
1933
+ const [
1934
+ totalCollateralETH,
1935
+ totalDebtETH,
1936
+ availableBorrowsETH,
1937
+ currentLiquidationThreshold,
1938
+ ltv,
1939
+ healthFactor
1940
+ ] = userData;
1941
+ const totalCollateralBN = new BigNumber(
1942
+ totalCollateralETH.toString()
1943
+ ).dividedBy(1e18);
1944
+ const totalDebtBN = new BigNumber(totalDebtETH.toString()).dividedBy(
1945
+ 1e18
1946
+ );
1947
+ const availableBorrowsBN = new BigNumber(
1948
+ availableBorrowsETH.toString()
1949
+ ).dividedBy(1e18);
1950
+ const healthFactorBN = new BigNumber(healthFactor.toString()).dividedBy(
1951
+ 1e18
1952
+ );
1953
+ const currentLTV = totalCollateralBN.gt(0) ? totalDebtBN.dividedBy(totalCollateralBN).times(100) : new BigNumber(0);
1954
+ const healthFactorFormatted = healthFactorBN.isFinite() ? healthFactorBN.toFixed(2) : "\u221E";
1955
+ let positionStatus = "No Position";
1956
+ if (totalCollateralBN.gt(0) && totalDebtBN.gt(0)) {
1957
+ positionStatus = "Active Lending & Borrowing";
1958
+ } else if (totalCollateralBN.gt(0)) {
1959
+ positionStatus = "Lending Only";
1960
+ } else if (totalDebtBN.gt(0)) {
1961
+ positionStatus = "Borrowing Only";
1962
+ }
1963
+ let positionSummary = `Aave V3 Position Summary:
1964
+
1965
+ Status: ${positionStatus}
1966
+ Total Collateral: ${totalCollateralBN.toFixed(4)} ETH
1967
+ Total Debt: ${totalDebtBN.toFixed(4)} ETH
1968
+ Available Borrows: ${availableBorrowsBN.toFixed(4)} ETH
1969
+ Current LTV: ${currentLTV.toFixed(1)}%
1970
+ Health Factor: ${healthFactorFormatted}`;
1971
+ const recommendations = [];
1972
+ if (totalCollateralBN.eq(0) && totalDebtBN.eq(0)) {
1973
+ recommendations.push(
1974
+ "No active position - consider supplying assets to earn yield"
1975
+ );
1976
+ recommendations.push(
1977
+ "Start with stable assets like USDC for lower risk"
1978
+ );
1979
+ } else if (totalDebtBN.eq(0)) {
1980
+ recommendations.push(
1981
+ "Consider borrowing against your collateral if you need liquidity"
1982
+ );
1983
+ recommendations.push(
1984
+ "Monitor supply APY rates for optimization opportunities"
1985
+ );
1986
+ } else if (healthFactorBN.lt(1.5) && healthFactorBN.isFinite()) {
1987
+ recommendations.push(
1988
+ "\u26A0\uFE0F Health factor is low - consider adding collateral or repaying debt"
1989
+ );
1990
+ recommendations.push("Monitor position closely to avoid liquidation");
1991
+ } else {
1992
+ recommendations.push(
1993
+ "Position looks healthy - monitor rates for optimization"
1994
+ );
1995
+ if (availableBorrowsBN.gt(0)) {
1996
+ recommendations.push(
1997
+ `You can still borrow up to ${availableBorrowsBN.toFixed(4)} ETH`
1998
+ );
1999
+ }
2000
+ }
2001
+ if (recommendations.length > 0) {
2002
+ positionSummary += `
2003
+
2004
+ Recommendations:
2005
+ ${recommendations.map((r) => `- ${r}`).join("\n")}`;
2006
+ }
2007
+ return {
2008
+ text: positionSummary,
2009
+ data: {
2010
+ address: walletAddress,
2011
+ status: positionStatus.toLowerCase().replace(/\s+/g, "_"),
2012
+ totalCollateral: totalCollateralBN.toNumber(),
2013
+ totalDebt: totalDebtBN.toNumber(),
2014
+ availableBorrows: availableBorrowsBN.toNumber(),
2015
+ currentLTV: currentLTV.toNumber(),
2016
+ healthFactor: healthFactorBN.isFinite() ? healthFactorBN.toNumber() : null,
2017
+ recommendations,
2018
+ hasSupplies: totalCollateralBN.gt(0),
2019
+ hasBorrows: totalDebtBN.gt(0),
2020
+ liquidationThreshold: new BigNumber(
2021
+ currentLiquidationThreshold.toString()
2022
+ ).dividedBy(1e4).times(100).toNumber()
2023
+ },
2024
+ values: {
2025
+ totalCollateral: totalCollateralBN.toNumber(),
2026
+ totalDebt: totalDebtBN.toNumber(),
2027
+ availableBorrows: availableBorrowsBN.toNumber(),
2028
+ currentLTV: currentLTV.toNumber(),
2029
+ healthFactor: healthFactorBN.isFinite() ? healthFactorBN.toNumber() : null,
2030
+ utilizationRate: totalCollateralBN.gt(0) ? totalDebtBN.dividedBy(totalCollateralBN).toNumber() : 0
2031
+ }
2032
+ };
2033
+ } catch (error) {
2034
+ logger9.error("positionContextProvider: Error fetching position data:", {
2035
+ error: error instanceof Error ? error.message : String(error),
2036
+ address: walletAddress,
2037
+ rpcUrl
2038
+ });
2039
+ return {
2040
+ text: "Position data temporarily unavailable",
2041
+ data: {
2042
+ provider: "positionContextProvider",
2043
+ status: "error",
2044
+ error: error instanceof Error ? error.message : String(error),
2045
+ address: walletAddress
2046
+ }
2047
+ };
2048
+ }
2049
+ }
2050
+ };
2051
+
2052
+ // src/providers/healthFactor.ts
2053
+ import {
2054
+ logger as logger10
2055
+ } from "@elizaos/core";
2056
+ import BigNumber2 from "bignumber.js";
2057
+ import { createPublicClient as createPublicClient4, http as http4 } from "viem";
2058
+ import { base as base4 } from "viem/chains";
2059
+ import { AaveV3Base as AaveV3Base4 } from "@bgd-labs/aave-address-book";
2060
+ var healthFactorProvider = {
2061
+ name: "healthFactorProvider",
2062
+ description: "Provides detailed health factor analysis and risk assessment for Aave V3 positions",
2063
+ dynamic: true,
2064
+ get: async (runtime, message, state) => {
2065
+ logger10.debug("healthFactorProvider::get");
2066
+ const rpcUrl = runtime.getSetting("BASE_RPC_URL");
2067
+ const walletAddress = runtime.getSetting("WALLET_ADDRESS");
2068
+ if (!rpcUrl) {
2069
+ logger10.error("BASE_RPC_URL not configured");
2070
+ return {
2071
+ text: "Health factor data unavailable - RPC URL not configured",
2072
+ data: { error: "RPC URL required" }
2073
+ };
2074
+ }
2075
+ if (!walletAddress) {
2076
+ logger10.debug(
2077
+ "healthFactorProvider: No wallet address configured, returning neutral context"
2078
+ );
2079
+ return {
2080
+ text: "Health factor data available when wallet address is provided",
2081
+ data: { provider: "healthFactorProvider", status: "neutral" }
2082
+ };
2083
+ }
2084
+ try {
2085
+ const publicClient = createPublicClient4({
2086
+ chain: base4,
2087
+ transport: http4(rpcUrl)
2088
+ });
2089
+ const poolAddress = AaveV3Base4.POOL;
2090
+ const userData = await publicClient.readContract({
2091
+ address: poolAddress,
2092
+ abi: [
2093
+ {
2094
+ inputs: [
2095
+ { internalType: "address", name: "user", type: "address" }
2096
+ ],
2097
+ name: "getUserAccountData",
2098
+ outputs: [
2099
+ {
2100
+ internalType: "uint256",
2101
+ name: "totalCollateralETH",
2102
+ type: "uint256"
2103
+ },
2104
+ {
2105
+ internalType: "uint256",
2106
+ name: "totalDebtETH",
2107
+ type: "uint256"
2108
+ },
2109
+ {
2110
+ internalType: "uint256",
2111
+ name: "availableBorrowsETH",
2112
+ type: "uint256"
2113
+ },
2114
+ {
2115
+ internalType: "uint256",
2116
+ name: "currentLiquidationThreshold",
2117
+ type: "uint256"
2118
+ },
2119
+ { internalType: "uint256", name: "ltv", type: "uint256" },
2120
+ {
2121
+ internalType: "uint256",
2122
+ name: "healthFactor",
2123
+ type: "uint256"
2124
+ }
2125
+ ],
2126
+ stateMutability: "view",
2127
+ type: "function"
2128
+ }
2129
+ ],
2130
+ functionName: "getUserAccountData",
2131
+ args: [walletAddress]
2132
+ });
2133
+ const [
2134
+ totalCollateralETH,
2135
+ totalDebtETH,
2136
+ availableBorrowsETH,
2137
+ currentLiquidationThreshold,
2138
+ ltv,
2139
+ healthFactor
2140
+ ] = userData;
2141
+ const healthFactorBN = new BigNumber2(healthFactor.toString()).dividedBy(
2142
+ 1e18
2143
+ );
2144
+ const totalCollateralBN = new BigNumber2(
2145
+ totalCollateralETH.toString()
2146
+ ).dividedBy(1e18);
2147
+ const totalDebtBN = new BigNumber2(totalDebtETH.toString()).dividedBy(
2148
+ 1e18
2149
+ );
2150
+ if (totalDebtBN.eq(0)) {
2151
+ return {
2152
+ text: "No debt position found - health factor analysis not applicable",
2153
+ data: {
2154
+ address: walletAddress,
2155
+ healthFactor: null,
2156
+ status: "no_debt",
2157
+ totalCollateral: totalCollateralBN.toNumber(),
2158
+ totalDebt: 0
2159
+ },
2160
+ values: {
2161
+ healthFactor: null,
2162
+ totalCollateral: totalCollateralBN.toNumber(),
2163
+ totalDebt: 0
2164
+ }
2165
+ };
2166
+ }
2167
+ const healthFactorFormatted = healthFactorBN.isFinite() ? healthFactorBN.toFixed(2) : "\u221E";
2168
+ const analysis = analyzeHealthFactor(healthFactorBN);
2169
+ const recommendations = getRecommendations(healthFactorBN);
2170
+ const ltvBN = new BigNumber2(ltv.toString()).dividedBy(1e4);
2171
+ const currentLTV = totalCollateralBN.gt(0) ? totalDebtBN.dividedBy(totalCollateralBN).times(100) : new BigNumber2(0);
2172
+ let liquidationInfo = "";
2173
+ if (healthFactorBN.lt(1.5) && healthFactorBN.isFinite()) {
2174
+ const liquidationThresholdBN = new BigNumber2(
2175
+ currentLiquidationThreshold.toString()
2176
+ ).dividedBy(1e4);
2177
+ const collateralDropToLiquidation = totalCollateralBN.times(healthFactorBN.minus(1).dividedBy(healthFactorBN)).times(100);
2178
+ liquidationInfo = `
2179
+ Liquidation Risk Analysis:
2180
+ - Collateral can drop ${collateralDropToLiquidation.toFixed(1)}% before liquidation
2181
+ - Current liquidation threshold: ${liquidationThresholdBN.times(100).toFixed(1)}%`;
2182
+ }
2183
+ const summary = `Health Factor Analysis:
2184
+
2185
+ ${analysis.emoji} Health Factor: ${healthFactorFormatted}
2186
+ Status: ${analysis.status}
2187
+ Risk Level: ${analysis.riskLevel}
2188
+
2189
+ Safety Metrics:
2190
+ - Current LTV: ${currentLTV.toFixed(1)}% / Max LTV: ${ltvBN.times(100).toFixed(1)}%
2191
+ - Total Collateral: ${totalCollateralBN.toFixed(4)} ETH
2192
+ - Total Debt: ${totalDebtBN.toFixed(4)} ETH
2193
+ ${liquidationInfo}
2194
+
2195
+ Recommendations:
2196
+ ${recommendations.map((r) => `- ${r}`).join("\n")}
2197
+
2198
+ Remember: Health Factor > 1.0 prevents liquidation. Aim for > 1.5 for safety.`;
2199
+ return {
2200
+ text: summary,
2201
+ data: {
2202
+ address: walletAddress,
2203
+ healthFactor: healthFactorBN.toNumber(),
2204
+ status: analysis.status.toLowerCase().replace(" ", "_"),
2205
+ riskLevel: analysis.riskLevel,
2206
+ totalCollateral: totalCollateralBN.toNumber(),
2207
+ totalDebt: totalDebtBN.toNumber(),
2208
+ currentLTV: currentLTV.toNumber(),
2209
+ maxLTV: ltvBN.times(100).toNumber(),
2210
+ recommendations,
2211
+ liquidationThreshold: new BigNumber2(
2212
+ currentLiquidationThreshold.toString()
2213
+ ).dividedBy(1e4).times(100).toNumber()
2214
+ },
2215
+ values: {
2216
+ healthFactor: healthFactorBN.toNumber(),
2217
+ totalCollateral: totalCollateralBN.toNumber(),
2218
+ totalDebt: totalDebtBN.toNumber(),
2219
+ currentLTV: currentLTV.toNumber(),
2220
+ riskScore: calculateRiskScore(healthFactorBN)
2221
+ }
2222
+ };
2223
+ } catch (error) {
2224
+ logger10.error("healthFactorProvider: Error fetching health factor data:", {
2225
+ error: error instanceof Error ? error.message : String(error),
2226
+ address: walletAddress,
2227
+ rpcUrl
2228
+ });
2229
+ return {
2230
+ text: "Health factor data temporarily unavailable",
2231
+ data: {
2232
+ provider: "healthFactorProvider",
2233
+ status: "error",
2234
+ error: error instanceof Error ? error.message : String(error),
2235
+ address: walletAddress
2236
+ }
2237
+ };
2238
+ }
2239
+ }
2240
+ };
2241
+ function analyzeHealthFactor(healthFactor) {
2242
+ if (!healthFactor.isFinite()) {
2243
+ return {
2244
+ status: "No Debt Position",
2245
+ riskLevel: "No Risk",
2246
+ emoji: "\u{1F7E2}"
2247
+ };
2248
+ }
2249
+ if (healthFactor.lt(1)) {
2250
+ return {
2251
+ status: "LIQUIDATABLE",
2252
+ riskLevel: "EXTREME - Liquidation Active",
2253
+ emoji: "\u{1F534}\u{1F6A8}"
2254
+ };
2255
+ } else if (healthFactor.lt(1.1)) {
2256
+ return {
2257
+ status: "CRITICAL",
2258
+ riskLevel: "Very High - Immediate Action Required",
2259
+ emoji: "\u{1F534}"
2260
+ };
2261
+ } else if (healthFactor.lt(1.5)) {
2262
+ return {
2263
+ status: "RISKY",
2264
+ riskLevel: "High - Monitor Closely",
2265
+ emoji: "\u{1F7E1}"
2266
+ };
2267
+ } else if (healthFactor.lt(2)) {
2268
+ return {
2269
+ status: "MODERATE",
2270
+ riskLevel: "Medium - Acceptable Risk",
2271
+ emoji: "\u{1F7E2}"
2272
+ };
2273
+ } else if (healthFactor.lt(3)) {
2274
+ return {
2275
+ status: "SAFE",
2276
+ riskLevel: "Low",
2277
+ emoji: "\u{1F7E2}"
2278
+ };
2279
+ } else {
2280
+ return {
2281
+ status: "VERY SAFE",
2282
+ riskLevel: "Very Low",
2283
+ emoji: "\u{1F7E2}"
2284
+ };
2285
+ }
2286
+ }
2287
+ function getRecommendations(healthFactor) {
2288
+ const recommendations = [];
2289
+ if (!healthFactor.isFinite()) {
2290
+ recommendations.push(
2291
+ "Consider borrowing against your collateral to put it to work"
2292
+ );
2293
+ recommendations.push("Your position is risk-free with no debt");
2294
+ return recommendations;
2295
+ }
2296
+ if (healthFactor.lt(1.1)) {
2297
+ recommendations.push(
2298
+ "\u{1F6A8} URGENT: Add collateral immediately to avoid liquidation"
2299
+ );
2300
+ recommendations.push("\u{1F6A8} URGENT: Repay debt to improve health factor");
2301
+ recommendations.push(
2302
+ "Consider using a flash loan to restructure your position"
2303
+ );
2304
+ } else if (healthFactor.lt(1.5)) {
2305
+ recommendations.push("Add more collateral to create a safety buffer");
2306
+ recommendations.push(
2307
+ "Consider repaying some debt to improve your position"
2308
+ );
2309
+ recommendations.push("Monitor market prices closely for your assets");
2310
+ recommendations.push("Set up alerts for health factor changes");
2311
+ } else if (healthFactor.lt(2)) {
2312
+ recommendations.push(
2313
+ "Your position is relatively safe but monitor regularly"
2314
+ );
2315
+ recommendations.push("Consider your risk tolerance before borrowing more");
2316
+ } else {
2317
+ recommendations.push("Your position is very safe");
2318
+ recommendations.push("You have room to borrow more if needed");
2319
+ recommendations.push(
2320
+ "Consider enabling eMode for better capital efficiency"
2321
+ );
2322
+ }
2323
+ return recommendations;
2324
+ }
2325
+ function calculateRiskScore(healthFactor) {
2326
+ if (!healthFactor.isFinite()) {
2327
+ return 0;
2328
+ }
2329
+ if (healthFactor.lt(1)) {
2330
+ return 100;
2331
+ } else if (healthFactor.lt(1.1)) {
2332
+ return 90;
2333
+ } else if (healthFactor.lt(1.5)) {
2334
+ return 70;
2335
+ } else if (healthFactor.lt(2)) {
2336
+ return 40;
2337
+ } else if (healthFactor.lt(3)) {
2338
+ return 20;
2339
+ } else {
2340
+ return 10;
2341
+ }
2342
+ }
2343
+
2344
+ // src/types/index.ts
2345
+ import BigNumber3 from "bignumber.js";
2346
+ var InterestRateMode = /* @__PURE__ */ ((InterestRateMode2) => {
2347
+ InterestRateMode2[InterestRateMode2["NONE"] = 0] = "NONE";
2348
+ InterestRateMode2[InterestRateMode2["STABLE"] = 1] = "STABLE";
2349
+ InterestRateMode2[InterestRateMode2["VARIABLE"] = 2] = "VARIABLE";
2350
+ return InterestRateMode2;
2351
+ })(InterestRateMode || {});
2352
+ var HealthFactorStatus = /* @__PURE__ */ ((HealthFactorStatus2) => {
2353
+ HealthFactorStatus2["CRITICAL"] = "CRITICAL";
2354
+ HealthFactorStatus2["RISKY"] = "RISKY";
2355
+ HealthFactorStatus2["MODERATE"] = "MODERATE";
2356
+ HealthFactorStatus2["SAFE"] = "SAFE";
2357
+ HealthFactorStatus2["VERY_SAFE"] = "VERY_SAFE";
2358
+ return HealthFactorStatus2;
2359
+ })(HealthFactorStatus || {});
2360
+
2361
+ // src/index.ts
2362
+ var aavePlugin = {
2363
+ name: "aave",
2364
+ description: "Aave V3 integration plugin for ElizaOS - enabling DeFi lending and borrowing capabilities on Base L2",
2365
+ actions: [
2366
+ supplyAction,
2367
+ borrowAction,
2368
+ repayAction,
2369
+ withdrawAction,
2370
+ rateSwitchAction,
2371
+ collateralManagementAction,
2372
+ eModeAction,
2373
+ flashLoanAction
2374
+ ],
2375
+ providers: [positionContextProvider, healthFactorProvider]
2376
+ };
2377
+ var index_default = aavePlugin;
2378
+ export {
2379
+ BigNumber3 as BigNumber,
2380
+ HealthFactorStatus,
2381
+ InterestRateMode,
2382
+ aavePlugin,
2383
+ borrowAction,
2384
+ collateralManagementAction,
2385
+ index_default as default,
2386
+ eModeAction,
2387
+ flashLoanAction,
2388
+ healthFactorProvider,
2389
+ positionContextProvider,
2390
+ rateSwitchAction,
2391
+ repayAction,
2392
+ supplyAction,
2393
+ withdrawAction
2394
+ };
2395
+ //# sourceMappingURL=index.js.map