@coinbase/agentkit 0.1.1 → 0.1.2

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.
Files changed (33) hide show
  1. package/README.md +19 -0
  2. package/dist/action-providers/alchemy/alchemyTokenPricesActionProvider.d.ts +55 -0
  3. package/dist/action-providers/alchemy/alchemyTokenPricesActionProvider.js +173 -0
  4. package/dist/action-providers/alchemy/alchemyTokenPricesActionProvider.test.d.ts +1 -0
  5. package/dist/action-providers/alchemy/alchemyTokenPricesActionProvider.test.js +131 -0
  6. package/dist/action-providers/alchemy/index.d.ts +2 -0
  7. package/dist/action-providers/alchemy/index.js +18 -0
  8. package/dist/action-providers/alchemy/schemas.d.ts +41 -0
  9. package/dist/action-providers/alchemy/schemas.js +34 -0
  10. package/dist/action-providers/basename/basenameActionProvider.d.ts +1 -1
  11. package/dist/action-providers/cdp/cdpApiActionProvider.js +7 -1
  12. package/dist/action-providers/erc20/erc20ActionProvider.d.ts +1 -1
  13. package/dist/action-providers/erc721/erc721ActionProvider.d.ts +1 -1
  14. package/dist/action-providers/index.d.ts +2 -0
  15. package/dist/action-providers/index.js +2 -0
  16. package/dist/action-providers/moonwell/constants.d.ts +78 -0
  17. package/dist/action-providers/moonwell/constants.js +111 -0
  18. package/dist/action-providers/moonwell/index.d.ts +1 -0
  19. package/dist/action-providers/moonwell/index.js +5 -0
  20. package/dist/action-providers/moonwell/moonwellActionProvider.d.ts +39 -0
  21. package/dist/action-providers/moonwell/moonwellActionProvider.js +249 -0
  22. package/dist/action-providers/moonwell/moonwellActionProvider.test.d.ts +1 -0
  23. package/dist/action-providers/moonwell/moonwellActionProvider.test.js +455 -0
  24. package/dist/action-providers/moonwell/schemas.d.ts +30 -0
  25. package/dist/action-providers/moonwell/schemas.js +39 -0
  26. package/dist/action-providers/morpho/morphoActionProvider.d.ts +1 -1
  27. package/dist/action-providers/pyth/pythActionProvider.test.d.ts +1 -0
  28. package/dist/action-providers/pyth/pythActionProvider.test.js +113 -0
  29. package/dist/action-providers/weth/wethActionProvider.d.ts +1 -1
  30. package/dist/action-providers/wow/wowActionProvider.d.ts +1 -1
  31. package/dist/wallet-providers/cdpWalletProvider.js +17 -1
  32. package/dist/wallet-providers/viemWalletProvider.js +1 -1
  33. package/package.json +1 -1
@@ -0,0 +1,455 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const viem_1 = require("viem");
4
+ const utils_1 = require("../../utils");
5
+ const moonwellActionProvider_1 = require("./moonwellActionProvider");
6
+ const constants_1 = require("./constants");
7
+ const schemas_1 = require("./schemas");
8
+ const MOCK_MTOKEN_ADDRESS = "0x73902f619CEB9B31FD8EFecf435CbDf89E369Ba6";
9
+ const MOCK_ATOMIC_ASSETS = "1000000000000000000";
10
+ const MOCK_WHOLE_ASSETS = "1.0";
11
+ const MOCK_TOKEN_ADDRESS = "0x4200000000000000000000000000000000000006";
12
+ const MOCK_TX_HASH = "0xabcdef1234567890";
13
+ const MOCK_RECEIPT = { status: "success", blockNumber: 1234567 };
14
+ const WETH_MTOKEN = "0x628ff693426583D9a7FB391E54366292F509D457";
15
+ jest.mock("../../utils");
16
+ const mockApprove = utils_1.approve;
17
+ describe("Moonwell Action Provider", () => {
18
+ const actionProvider = new moonwellActionProvider_1.MoonwellActionProvider();
19
+ let mockWallet;
20
+ let consoleErrorSpy;
21
+ beforeEach(() => {
22
+ // Mock console.error to suppress debug logs
23
+ consoleErrorSpy = jest.spyOn(console, "error").mockImplementation(() => { });
24
+ mockWallet = {
25
+ getAddress: jest.fn().mockReturnValue(MOCK_TOKEN_ADDRESS),
26
+ getNetwork: jest
27
+ .fn()
28
+ .mockReturnValue({ protocolFamily: "evm", networkId: "base-mainnet" }),
29
+ sendTransaction: jest.fn().mockResolvedValue(MOCK_TX_HASH),
30
+ waitForTransactionReceipt: jest.fn().mockResolvedValue(MOCK_RECEIPT),
31
+ };
32
+ mockApprove.mockResolvedValue("Approval successful");
33
+ });
34
+ afterEach(() => {
35
+ consoleErrorSpy.mockRestore();
36
+ });
37
+ describe("Mint Schema", () => {
38
+ it("should successfully parse valid input", () => {
39
+ const validInput = {
40
+ mTokenAddress: MOCK_MTOKEN_ADDRESS,
41
+ assets: MOCK_WHOLE_ASSETS,
42
+ tokenAddress: MOCK_TOKEN_ADDRESS,
43
+ };
44
+ const result = schemas_1.MintSchema.safeParse(validInput);
45
+ expect(result.success).toBe(true);
46
+ if (result.success) {
47
+ expect(result.data).toEqual(validInput);
48
+ }
49
+ });
50
+ it("should fail parsing empty input", () => {
51
+ const emptyInput = {};
52
+ const result = schemas_1.MintSchema.safeParse(emptyInput);
53
+ expect(result.success).toBe(false);
54
+ });
55
+ it("should fail with invalid mToken address", () => {
56
+ const invalidInput = {
57
+ mTokenAddress: "not_an_address",
58
+ assets: MOCK_WHOLE_ASSETS,
59
+ tokenAddress: MOCK_TOKEN_ADDRESS,
60
+ };
61
+ const result = schemas_1.MintSchema.safeParse(invalidInput);
62
+ expect(result.success).toBe(false);
63
+ });
64
+ it("should handle valid asset string formats", () => {
65
+ const validInput = {
66
+ mTokenAddress: MOCK_MTOKEN_ADDRESS,
67
+ assets: MOCK_WHOLE_ASSETS,
68
+ tokenAddress: MOCK_TOKEN_ADDRESS,
69
+ };
70
+ const validInputs = [
71
+ { ...validInput, assets: "1.5" },
72
+ { ...validInput, assets: "0.00001" },
73
+ { ...validInput, assets: "1000" },
74
+ ];
75
+ validInputs.forEach(input => {
76
+ const result = schemas_1.MintSchema.safeParse(input);
77
+ expect(result.success).toBe(true);
78
+ });
79
+ });
80
+ it("should reject invalid asset strings", () => {
81
+ const validInput = {
82
+ mTokenAddress: MOCK_MTOKEN_ADDRESS,
83
+ assets: MOCK_WHOLE_ASSETS,
84
+ tokenAddress: MOCK_TOKEN_ADDRESS,
85
+ };
86
+ const invalidInputs = [
87
+ { ...validInput, assets: "" },
88
+ { ...validInput, assets: "1,000" },
89
+ { ...validInput, assets: "1.2.3" },
90
+ { ...validInput, assets: "abc" },
91
+ ];
92
+ invalidInputs.forEach(input => {
93
+ const result = schemas_1.MintSchema.safeParse(input);
94
+ expect(result.success).toBe(false);
95
+ });
96
+ });
97
+ });
98
+ describe("Redeem Schema", () => {
99
+ it("should successfully parse valid input", () => {
100
+ const validInput = {
101
+ mTokenAddress: MOCK_MTOKEN_ADDRESS,
102
+ assets: MOCK_ATOMIC_ASSETS,
103
+ };
104
+ const result = schemas_1.RedeemSchema.safeParse(validInput);
105
+ expect(result.success).toBe(true);
106
+ if (result.success) {
107
+ expect(result.data).toEqual(validInput);
108
+ }
109
+ });
110
+ it("should fail parsing empty input", () => {
111
+ const emptyInput = {};
112
+ const result = schemas_1.RedeemSchema.safeParse(emptyInput);
113
+ expect(result.success).toBe(false);
114
+ });
115
+ it("should fail with invalid mToken address", () => {
116
+ const invalidInput = {
117
+ mTokenAddress: "not_an_address",
118
+ assets: MOCK_ATOMIC_ASSETS,
119
+ };
120
+ const result = schemas_1.RedeemSchema.safeParse(invalidInput);
121
+ expect(result.success).toBe(false);
122
+ });
123
+ it("should handle valid asset string formats", () => {
124
+ const validInput = {
125
+ mTokenAddress: MOCK_MTOKEN_ADDRESS,
126
+ assets: MOCK_ATOMIC_ASSETS,
127
+ };
128
+ const validInputs = [
129
+ { ...validInput, assets: "1000000000000000000" },
130
+ { ...validInput, assets: "1" },
131
+ { ...validInput, assets: "999999999999999999999" },
132
+ ];
133
+ validInputs.forEach(input => {
134
+ const result = schemas_1.RedeemSchema.safeParse(input);
135
+ expect(result.success).toBe(true);
136
+ });
137
+ });
138
+ it("should reject invalid asset strings", () => {
139
+ const validInput = {
140
+ mTokenAddress: MOCK_MTOKEN_ADDRESS,
141
+ assets: MOCK_ATOMIC_ASSETS,
142
+ };
143
+ const invalidInputs = [
144
+ { ...validInput, assets: "" },
145
+ { ...validInput, assets: "1,000" },
146
+ { ...validInput, assets: "1.2.3" },
147
+ { ...validInput, assets: "abc" },
148
+ { ...validInput, assets: "-1.5" },
149
+ ];
150
+ invalidInputs.forEach(input => {
151
+ const result = schemas_1.RedeemSchema.safeParse(input);
152
+ expect(result.success).toBe(false);
153
+ });
154
+ });
155
+ });
156
+ describe("mint", () => {
157
+ it("should successfully deposit to Moonwell MToken", async () => {
158
+ const args = {
159
+ mTokenAddress: MOCK_MTOKEN_ADDRESS,
160
+ assets: MOCK_WHOLE_ASSETS,
161
+ tokenAddress: MOCK_TOKEN_ADDRESS,
162
+ };
163
+ mockWallet.getNetwork.mockReturnValue({
164
+ protocolFamily: "evm",
165
+ networkId: "base-mainnet",
166
+ });
167
+ const atomicAssets = (0, viem_1.parseEther)(MOCK_WHOLE_ASSETS);
168
+ const response = await actionProvider.mint(mockWallet, args);
169
+ expect(mockApprove).toHaveBeenCalledWith(mockWallet, MOCK_TOKEN_ADDRESS, MOCK_MTOKEN_ADDRESS, atomicAssets);
170
+ expect(mockWallet.sendTransaction).toHaveBeenCalledWith({
171
+ to: MOCK_MTOKEN_ADDRESS,
172
+ data: (0, viem_1.encodeFunctionData)({
173
+ abi: constants_1.MTOKEN_ABI,
174
+ functionName: "mint",
175
+ args: [atomicAssets],
176
+ }),
177
+ value: 0n,
178
+ });
179
+ expect(mockWallet.waitForTransactionReceipt).toHaveBeenCalledWith(MOCK_TX_HASH);
180
+ expect(response).toContain(`Deposited ${MOCK_WHOLE_ASSETS}`);
181
+ expect(response).toContain(MOCK_TX_HASH);
182
+ expect(response).toContain(JSON.stringify(MOCK_RECEIPT));
183
+ });
184
+ it("should successfully deposit to Moonwell MToken on sepolia", async () => {
185
+ const args = {
186
+ mTokenAddress: "0x2F39a349A79492a70E152760ce7123A1933eCf28", // Sepolia WETH mToken
187
+ assets: MOCK_WHOLE_ASSETS,
188
+ tokenAddress: MOCK_TOKEN_ADDRESS,
189
+ };
190
+ mockWallet.getNetwork.mockReturnValue({
191
+ protocolFamily: "evm",
192
+ networkId: "base-sepolia",
193
+ });
194
+ const atomicAssets = (0, viem_1.parseEther)(MOCK_WHOLE_ASSETS);
195
+ const response = await actionProvider.mint(mockWallet, args);
196
+ expect(mockApprove).toHaveBeenCalledWith(mockWallet, MOCK_TOKEN_ADDRESS, args.mTokenAddress, atomicAssets);
197
+ expect(mockWallet.sendTransaction).toHaveBeenCalledWith({
198
+ to: args.mTokenAddress,
199
+ data: (0, viem_1.encodeFunctionData)({
200
+ abi: constants_1.MTOKEN_ABI,
201
+ functionName: "mint",
202
+ args: [atomicAssets],
203
+ }),
204
+ value: 0n,
205
+ });
206
+ expect(mockWallet.waitForTransactionReceipt).toHaveBeenCalledWith(MOCK_TX_HASH);
207
+ expect(response).toContain(`Deposited ${MOCK_WHOLE_ASSETS}`);
208
+ expect(response).toContain(MOCK_TX_HASH);
209
+ expect(response).toContain(JSON.stringify(MOCK_RECEIPT));
210
+ });
211
+ it("should reject deposit with zero assets amount", async () => {
212
+ const args = {
213
+ mTokenAddress: MOCK_MTOKEN_ADDRESS,
214
+ assets: "0.0",
215
+ tokenAddress: MOCK_TOKEN_ADDRESS,
216
+ };
217
+ const response = await actionProvider.mint(mockWallet, args);
218
+ expect(response).toBe("Error: Assets amount must be greater than 0");
219
+ expect(mockWallet.sendTransaction).not.toHaveBeenCalled();
220
+ });
221
+ it("should reject deposit with invalid MToken address not in MOONWELL_BASE_ADDRESSES", async () => {
222
+ const invalidMTokenAddress = "0x1234567890123456789012345678901234567890"; // Valid address format but not in MOONWELL_BASE_ADDRESSES
223
+ const args = {
224
+ mTokenAddress: invalidMTokenAddress,
225
+ assets: MOCK_WHOLE_ASSETS,
226
+ tokenAddress: MOCK_TOKEN_ADDRESS,
227
+ };
228
+ mockWallet.getNetwork.mockReturnValue({
229
+ protocolFamily: "evm",
230
+ networkId: "base-mainnet",
231
+ });
232
+ const response = await actionProvider.mint(mockWallet, args);
233
+ expect(response).toBe("Error: Invalid MToken address");
234
+ expect(mockWallet.sendTransaction).not.toHaveBeenCalled();
235
+ });
236
+ it("should reject deposit with invalid MToken address not in MOONWELL_BASE_SEPOLIA_ADDRESSES", async () => {
237
+ const invalidMTokenAddress = "0x1234567890123456789012345678901234567890"; // Valid address format but not in MOONWELL_BASE_SEPOLIA_ADDRESSES
238
+ const args = {
239
+ mTokenAddress: invalidMTokenAddress,
240
+ assets: MOCK_WHOLE_ASSETS,
241
+ tokenAddress: MOCK_TOKEN_ADDRESS,
242
+ };
243
+ mockWallet.getNetwork.mockReturnValue({
244
+ protocolFamily: "evm",
245
+ networkId: "base-sepolia",
246
+ });
247
+ const response = await actionProvider.mint(mockWallet, args);
248
+ expect(response).toBe("Error: Invalid MToken address");
249
+ expect(mockWallet.sendTransaction).not.toHaveBeenCalled();
250
+ });
251
+ it("should handle approval failure", async () => {
252
+ const args = {
253
+ mTokenAddress: MOCK_MTOKEN_ADDRESS,
254
+ assets: MOCK_WHOLE_ASSETS,
255
+ tokenAddress: MOCK_TOKEN_ADDRESS,
256
+ };
257
+ mockApprove.mockResolvedValue("Error: Approval failed");
258
+ const response = await actionProvider.mint(mockWallet, args);
259
+ expect(mockApprove).toHaveBeenCalled();
260
+ expect(response).toContain("Error approving Moonwell MToken as spender: Error: Approval failed");
261
+ expect(mockWallet.sendTransaction).not.toHaveBeenCalled();
262
+ });
263
+ it("should handle deposit errors", async () => {
264
+ const args = {
265
+ mTokenAddress: MOCK_MTOKEN_ADDRESS,
266
+ assets: MOCK_WHOLE_ASSETS,
267
+ tokenAddress: MOCK_TOKEN_ADDRESS,
268
+ };
269
+ const error = new Error("Failed to deposit");
270
+ mockWallet.sendTransaction.mockRejectedValue(error);
271
+ const response = await actionProvider.mint(mockWallet, args);
272
+ expect(mockApprove).toHaveBeenCalled();
273
+ expect(mockWallet.sendTransaction).toHaveBeenCalled();
274
+ expect(response).toBe("Error minting Moonwell MToken: Failed to deposit");
275
+ expect(consoleErrorSpy).toHaveBeenCalledWith("DEBUG - Mint error:", error);
276
+ });
277
+ describe("ETH deposits via router", () => {
278
+ beforeEach(() => {
279
+ // Clear all mocks before each test
280
+ jest.clearAllMocks();
281
+ mockWallet = {
282
+ getAddress: jest.fn().mockReturnValue(MOCK_TOKEN_ADDRESS),
283
+ getNetwork: jest
284
+ .fn()
285
+ .mockReturnValue({ protocolFamily: "evm", networkId: "base-mainnet" }),
286
+ sendTransaction: jest.fn().mockResolvedValue(MOCK_TX_HASH),
287
+ waitForTransactionReceipt: jest.fn().mockResolvedValue(MOCK_RECEIPT),
288
+ };
289
+ });
290
+ it("should use router for ETH deposits on mainnet", async () => {
291
+ const args = {
292
+ mTokenAddress: WETH_MTOKEN,
293
+ assets: MOCK_WHOLE_ASSETS,
294
+ tokenAddress: MOCK_TOKEN_ADDRESS,
295
+ };
296
+ mockWallet.getNetwork.mockReturnValue({
297
+ protocolFamily: "evm",
298
+ networkId: "base-mainnet",
299
+ });
300
+ const atomicAssets = (0, viem_1.parseEther)(MOCK_WHOLE_ASSETS);
301
+ const response = await actionProvider.mint(mockWallet, args);
302
+ // Should not call approve for ETH deposits
303
+ expect(mockApprove).not.toHaveBeenCalled();
304
+ expect(mockWallet.sendTransaction).toHaveBeenCalledWith({
305
+ to: constants_1.WETH_ROUTER_ADDRESS,
306
+ data: (0, viem_1.encodeFunctionData)({
307
+ abi: constants_1.ETH_ROUTER_ABI,
308
+ functionName: "mint",
309
+ args: [MOCK_TOKEN_ADDRESS],
310
+ }),
311
+ value: atomicAssets,
312
+ });
313
+ expect(mockWallet.waitForTransactionReceipt).toHaveBeenCalledWith(MOCK_TX_HASH);
314
+ expect(response).toContain(`Deposited ${MOCK_WHOLE_ASSETS} ETH to Moonwell WETH via router`);
315
+ expect(response).toContain(MOCK_TX_HASH);
316
+ expect(response).toContain(JSON.stringify(MOCK_RECEIPT));
317
+ });
318
+ it("should not use router for ETH deposits on sepolia", async () => {
319
+ // Clear mocks before test
320
+ jest.clearAllMocks();
321
+ mockApprove.mockResolvedValue("Approval successful");
322
+ const args = {
323
+ mTokenAddress: "0x2F39a349A79492a70E152760ce7123A1933eCf28", // Sepolia WETH mToken
324
+ assets: MOCK_WHOLE_ASSETS,
325
+ tokenAddress: MOCK_TOKEN_ADDRESS,
326
+ };
327
+ mockWallet.getNetwork.mockReturnValue({
328
+ protocolFamily: "evm",
329
+ networkId: "base-sepolia",
330
+ });
331
+ const atomicAssets = (0, viem_1.parseEther)(MOCK_WHOLE_ASSETS);
332
+ const response = await actionProvider.mint(mockWallet, args);
333
+ // Should call approve for token deposits on non-mainnet
334
+ expect(mockApprove).toHaveBeenCalledWith(mockWallet, MOCK_TOKEN_ADDRESS, args.mTokenAddress, atomicAssets);
335
+ expect(mockWallet.sendTransaction).toHaveBeenCalledWith({
336
+ to: args.mTokenAddress,
337
+ data: (0, viem_1.encodeFunctionData)({
338
+ abi: constants_1.MTOKEN_ABI,
339
+ functionName: "mint",
340
+ args: [atomicAssets],
341
+ }),
342
+ value: 0n,
343
+ });
344
+ expect(mockWallet.waitForTransactionReceipt).toHaveBeenCalledWith(MOCK_TX_HASH);
345
+ expect(response).toContain(`Deposited ${MOCK_WHOLE_ASSETS}`);
346
+ expect(response).toContain(MOCK_TX_HASH);
347
+ expect(response).toContain(JSON.stringify(MOCK_RECEIPT));
348
+ });
349
+ it("should handle errors in ETH deposit via router", async () => {
350
+ const args = {
351
+ mTokenAddress: WETH_MTOKEN,
352
+ assets: MOCK_WHOLE_ASSETS,
353
+ tokenAddress: MOCK_TOKEN_ADDRESS,
354
+ };
355
+ mockWallet.getNetwork.mockReturnValue({
356
+ protocolFamily: "evm",
357
+ networkId: "base-mainnet",
358
+ });
359
+ const error = new Error("Failed to deposit ETH");
360
+ mockWallet.sendTransaction.mockRejectedValue(error);
361
+ const response = await actionProvider.mint(mockWallet, args);
362
+ // Should not call approve for ETH deposits on mainnet
363
+ expect(mockApprove).not.toHaveBeenCalled();
364
+ expect(response).toBe("Error minting Moonwell MToken: Failed to deposit ETH");
365
+ expect(consoleErrorSpy).toHaveBeenCalledWith("DEBUG - Mint error:", error);
366
+ });
367
+ });
368
+ });
369
+ describe("redeem", () => {
370
+ it("should successfully redeem from Moonwell MToken", async () => {
371
+ const args = {
372
+ mTokenAddress: MOCK_MTOKEN_ADDRESS,
373
+ assets: "1.0",
374
+ };
375
+ const decimals = constants_1.MTOKENS_UNDERLYING_DECIMALS[constants_1.MOONWELL_BASE_ADDRESSES[args.mTokenAddress]];
376
+ const atomicAssets = (0, viem_1.parseUnits)(args.assets, decimals);
377
+ const response = await actionProvider.redeem(mockWallet, args);
378
+ const expectedData = (0, viem_1.encodeFunctionData)({
379
+ abi: constants_1.MTOKEN_ABI,
380
+ functionName: "redeemUnderlying",
381
+ args: [atomicAssets],
382
+ });
383
+ expect(mockWallet.sendTransaction).toHaveBeenCalledWith({
384
+ to: MOCK_MTOKEN_ADDRESS,
385
+ data: expectedData,
386
+ value: 0n,
387
+ });
388
+ expect(mockWallet.waitForTransactionReceipt).toHaveBeenCalledWith(MOCK_TX_HASH);
389
+ expect(response).toContain(`Redeemed ${args.assets}`);
390
+ expect(response).toContain(MOCK_TX_HASH);
391
+ expect(response).toContain(JSON.stringify(MOCK_RECEIPT));
392
+ });
393
+ it("should reject redeem with zero assets amount", async () => {
394
+ const args = {
395
+ mTokenAddress: MOCK_MTOKEN_ADDRESS,
396
+ assets: "0",
397
+ };
398
+ const response = await actionProvider.redeem(mockWallet, args);
399
+ expect(response).toBe("Error: Assets amount must be greater than 0");
400
+ expect(mockWallet.sendTransaction).not.toHaveBeenCalled();
401
+ });
402
+ it("should reject redeem with invalid MToken address not in MOONWELL_BASE_ADDRESSES", async () => {
403
+ const invalidMTokenAddress = "0x1234567890123456789012345678901234567890"; // Valid address format but not in MOONWELL_BASE_ADDRESSES
404
+ const args = {
405
+ mTokenAddress: invalidMTokenAddress,
406
+ assets: MOCK_ATOMIC_ASSETS,
407
+ };
408
+ const response = await actionProvider.redeem(mockWallet, args);
409
+ expect(response).toBe("Error: Invalid MToken address");
410
+ expect(mockWallet.sendTransaction).not.toHaveBeenCalled();
411
+ });
412
+ it("should handle errors when redeeming", async () => {
413
+ const args = {
414
+ mTokenAddress: MOCK_MTOKEN_ADDRESS,
415
+ assets: MOCK_ATOMIC_ASSETS,
416
+ };
417
+ const error = new Error("Failed to redeem");
418
+ mockWallet.sendTransaction.mockRejectedValue(error);
419
+ const response = await actionProvider.redeem(mockWallet, args);
420
+ expect(mockWallet.sendTransaction).toHaveBeenCalled();
421
+ expect(response).toBe("Error redeeming from Moonwell MToken: Failed to redeem");
422
+ expect(consoleErrorSpy).toHaveBeenCalledWith("DEBUG - Redeem error:", error);
423
+ });
424
+ });
425
+ describe("supportsNetwork", () => {
426
+ it("should return true for Base Mainnet", () => {
427
+ const result = actionProvider.supportsNetwork({
428
+ protocolFamily: "evm",
429
+ networkId: "base-mainnet",
430
+ });
431
+ expect(result).toBe(true);
432
+ });
433
+ it("should return true for Base Sepolia", () => {
434
+ const result = actionProvider.supportsNetwork({
435
+ protocolFamily: "evm",
436
+ networkId: "base-sepolia",
437
+ });
438
+ expect(result).toBe(true);
439
+ });
440
+ it("should return false for other EVM networks", () => {
441
+ const result = actionProvider.supportsNetwork({
442
+ protocolFamily: "evm",
443
+ networkId: "ethereum",
444
+ });
445
+ expect(result).toBe(false);
446
+ });
447
+ it("should return false for non-EVM networks", () => {
448
+ const result = actionProvider.supportsNetwork({
449
+ protocolFamily: "bitcoin",
450
+ networkId: "base-mainnet",
451
+ });
452
+ expect(result).toBe(false);
453
+ });
454
+ });
455
+ });
@@ -0,0 +1,30 @@
1
+ import { z } from "zod";
2
+ /**
3
+ * Input schema for Moonwell MToken mint action.
4
+ */
5
+ export declare const MintSchema: z.ZodObject<{
6
+ assets: z.ZodString;
7
+ tokenAddress: z.ZodString;
8
+ mTokenAddress: z.ZodString;
9
+ }, "strip", z.ZodTypeAny, {
10
+ assets: string;
11
+ tokenAddress: string;
12
+ mTokenAddress: string;
13
+ }, {
14
+ assets: string;
15
+ tokenAddress: string;
16
+ mTokenAddress: string;
17
+ }>;
18
+ /**
19
+ * Input schema for Moonwell MToken redeem action.
20
+ */
21
+ export declare const RedeemSchema: z.ZodObject<{
22
+ mTokenAddress: z.ZodString;
23
+ assets: z.ZodString;
24
+ }, "strip", z.ZodTypeAny, {
25
+ assets: string;
26
+ mTokenAddress: string;
27
+ }, {
28
+ assets: string;
29
+ mTokenAddress: string;
30
+ }>;
@@ -0,0 +1,39 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.RedeemSchema = exports.MintSchema = void 0;
4
+ const zod_1 = require("zod");
5
+ /**
6
+ * Input schema for Moonwell MToken mint action.
7
+ */
8
+ exports.MintSchema = zod_1.z
9
+ .object({
10
+ assets: zod_1.z
11
+ .string()
12
+ .regex(/^\d+(\.\d+)?$/, "Must be a valid integer or decimal value")
13
+ .describe("The quantity of assets to use to mint, in whole units"),
14
+ tokenAddress: zod_1.z
15
+ .string()
16
+ .regex(/^0x[a-fA-F0-9]{40}$/, "Invalid Ethereum address format")
17
+ .describe("The address of the assets token to approve for minting"),
18
+ mTokenAddress: zod_1.z
19
+ .string()
20
+ .regex(/^0x[a-fA-F0-9]{40}$/, "Invalid Ethereum address format")
21
+ .describe("The address of the Moonwell MToken to mint from"),
22
+ })
23
+ .describe("Input schema for Moonwell MToken mint action");
24
+ /**
25
+ * Input schema for Moonwell MToken redeem action.
26
+ */
27
+ exports.RedeemSchema = zod_1.z
28
+ .object({
29
+ mTokenAddress: zod_1.z
30
+ .string()
31
+ .regex(/^0x[a-fA-F0-9]{40}$/, "Invalid Ethereum address format")
32
+ .describe("The address of the Moonwell MToken to redeem from"),
33
+ assets: zod_1.z
34
+ .string()
35
+ .regex(/^\d+(\.\d+)?$/, "Must be a valid integer or decimal value")
36
+ .describe("The quantity of assets to redeem, in whole units"),
37
+ })
38
+ .strip()
39
+ .describe("Input schema for Moonwell MToken redeem action");
@@ -7,7 +7,7 @@ export declare const SUPPORTED_NETWORKS: string[];
7
7
  /**
8
8
  * MorphoActionProvider is an action provider for Morpho Vault interactions.
9
9
  */
10
- export declare class MorphoActionProvider extends ActionProvider {
10
+ export declare class MorphoActionProvider extends ActionProvider<EvmWalletProvider> {
11
11
  /**
12
12
  * Constructor for the MorphoActionProvider class.
13
13
  */
@@ -0,0 +1,113 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const pythActionProvider_1 = require("./pythActionProvider");
4
+ describe("PythActionProvider", () => {
5
+ const fetchMock = jest.fn();
6
+ global.fetch = fetchMock;
7
+ const provider = (0, pythActionProvider_1.pythActionProvider)();
8
+ beforeEach(() => {
9
+ jest.resetAllMocks().restoreAllMocks();
10
+ });
11
+ describe("fetchPriceFeed", () => {
12
+ it("should return the first price feed ID that matches the input token symbol", async () => {
13
+ fetchMock.mockResolvedValueOnce({
14
+ ok: true,
15
+ json: async () => [{ id: "some-price-feed-id", attributes: { base: "BTC" } }],
16
+ });
17
+ const priceFeedId = await provider.fetchPriceFeed({ tokenSymbol: "BTC" });
18
+ expect(priceFeedId).toEqual("some-price-feed-id");
19
+ });
20
+ it("should throw an error if no price feed is found", async () => {
21
+ fetchMock.mockResolvedValueOnce({
22
+ ok: true,
23
+ json: async () => [{ id: "some-price-feed-id", attributes: { base: "BTC" } }],
24
+ });
25
+ await expect(provider.fetchPriceFeed({ tokenSymbol: "ETH" })).rejects.toThrow("No price feed found for ETH");
26
+ });
27
+ it("should throw an error if the response is not ok", async () => {
28
+ fetchMock.mockResolvedValueOnce({
29
+ ok: false,
30
+ status: 404,
31
+ });
32
+ await expect(provider.fetchPriceFeed({ tokenSymbol: "BTC" })).rejects.toThrow("HTTP error! status: 404");
33
+ });
34
+ it("should throw an error if response is ok but no data is returned", async () => {
35
+ fetchMock.mockResolvedValueOnce({
36
+ ok: true,
37
+ json: async () => [],
38
+ });
39
+ const provider = (0, pythActionProvider_1.pythActionProvider)();
40
+ await expect(provider.fetchPriceFeed({ tokenSymbol: "BTC" })).rejects.toThrow("No price feed found for BTC");
41
+ });
42
+ });
43
+ describe("fetchPrice", () => {
44
+ it("should return the price for a given price feed ID", async () => {
45
+ fetchMock.mockResolvedValueOnce({
46
+ ok: true,
47
+ json: async () => ({
48
+ parsed: [
49
+ {
50
+ price: {
51
+ price: 100,
52
+ expo: 2,
53
+ },
54
+ },
55
+ ],
56
+ }),
57
+ });
58
+ const price = await provider.fetchPrice({ priceFeedID: "some-price-feed-id" });
59
+ expect(price).toEqual("1");
60
+ });
61
+ it("should return the price for a given price feed ID with a negative exponent", async () => {
62
+ fetchMock.mockResolvedValueOnce({
63
+ ok: true,
64
+ json: async () => ({
65
+ parsed: [
66
+ {
67
+ price: {
68
+ price: 100,
69
+ expo: -2,
70
+ },
71
+ },
72
+ ],
73
+ }),
74
+ });
75
+ const provider = (0, pythActionProvider_1.pythActionProvider)();
76
+ const price = await provider.fetchPrice({ priceFeedID: "some-price-feed-id" });
77
+ expect(price).toEqual("1.00");
78
+ });
79
+ it("should handle scaled price starting with a decimal", async () => {
80
+ fetchMock.mockResolvedValueOnce({
81
+ ok: true,
82
+ json: async () => ({
83
+ parsed: [
84
+ {
85
+ price: {
86
+ price: 25,
87
+ expo: -2,
88
+ },
89
+ },
90
+ ],
91
+ }),
92
+ });
93
+ const price = await provider.fetchPrice({ priceFeedID: "some-price-feed-id" });
94
+ expect(price).toEqual("0.25");
95
+ });
96
+ it("should throw an error if there is no price data", async () => {
97
+ fetchMock.mockResolvedValueOnce({
98
+ ok: true,
99
+ json: async () => ({
100
+ parsed: [],
101
+ }),
102
+ });
103
+ await expect(provider.fetchPrice({ priceFeedID: "some-price-feed-id" })).rejects.toThrow("No price data found for some-price-feed-id");
104
+ });
105
+ it("should throw an error if response is not ok", async () => {
106
+ fetchMock.mockResolvedValueOnce({
107
+ ok: false,
108
+ status: 404,
109
+ });
110
+ await expect(provider.fetchPrice({ priceFeedID: "some-price-feed-id" })).rejects.toThrow("HTTP error! status: 404");
111
+ });
112
+ });
113
+ });
@@ -6,7 +6,7 @@ import { EvmWalletProvider } from "../../wallet-providers";
6
6
  /**
7
7
  * WethActionProvider is an action provider for WETH.
8
8
  */
9
- export declare class WethActionProvider extends ActionProvider {
9
+ export declare class WethActionProvider extends ActionProvider<EvmWalletProvider> {
10
10
  /**
11
11
  * Constructor for the WethActionProvider.
12
12
  */
@@ -6,7 +6,7 @@ import { WowBuyTokenInput, WowCreateTokenInput, WowSellTokenInput } from "./sche
6
6
  /**
7
7
  * WowActionProvider is an action provider for Wow protocol interactions.
8
8
  */
9
- export declare class WowActionProvider extends ActionProvider {
9
+ export declare class WowActionProvider extends ActionProvider<EvmWalletProvider> {
10
10
  /**
11
11
  * Constructor for the WowActionProvider class.
12
12
  */