@coinbase/agentkit 0.3.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. package/README.md +90 -7
  2. package/dist/action-providers/across/acrossActionProvider.d.ts +50 -0
  3. package/dist/action-providers/across/acrossActionProvider.js +333 -0
  4. package/dist/action-providers/across/acrossActionProvider.test.d.ts +1 -0
  5. package/dist/action-providers/across/acrossActionProvider.test.js +391 -0
  6. package/dist/action-providers/across/constants.d.ts +1 -0
  7. package/dist/action-providers/across/constants.js +2 -0
  8. package/dist/action-providers/across/index.d.ts +1 -0
  9. package/dist/action-providers/across/index.js +17 -0
  10. package/dist/action-providers/across/schemas.d.ts +36 -0
  11. package/dist/action-providers/across/schemas.js +46 -0
  12. package/dist/action-providers/across/utils.d.ts +7 -0
  13. package/dist/action-providers/across/utils.js +25 -0
  14. package/dist/action-providers/defillama/constants.d.ts +8 -0
  15. package/dist/action-providers/defillama/constants.js +11 -0
  16. package/dist/action-providers/defillama/defillamaActionProvider.d.ts +54 -0
  17. package/dist/action-providers/defillama/defillamaActionProvider.js +180 -0
  18. package/dist/action-providers/defillama/defillamaActionProvider.test.d.ts +1 -0
  19. package/dist/action-providers/defillama/defillamaActionProvider.test.js +114 -0
  20. package/dist/action-providers/defillama/index.d.ts +1 -0
  21. package/dist/action-providers/defillama/index.js +17 -0
  22. package/dist/action-providers/defillama/schemas.d.ts +34 -0
  23. package/dist/action-providers/defillama/schemas.js +34 -0
  24. package/dist/action-providers/defillama/types.d.ts +73 -0
  25. package/dist/action-providers/defillama/types.js +2 -0
  26. package/dist/action-providers/defillama/utils.d.ts +10 -0
  27. package/dist/action-providers/defillama/utils.js +87 -0
  28. package/dist/action-providers/defillama/utils.test.d.ts +1 -0
  29. package/dist/action-providers/defillama/utils.test.js +124 -0
  30. package/dist/action-providers/erc20/constants.d.ts +2 -0
  31. package/dist/action-providers/erc20/constants.js +12 -1
  32. package/dist/action-providers/erc20/erc20ActionProvider.js +18 -0
  33. package/dist/action-providers/erc20/erc20ActionProvider.test.js +4 -0
  34. package/dist/action-providers/index.d.ts +3 -0
  35. package/dist/action-providers/index.js +3 -0
  36. package/dist/action-providers/messari/constants.d.ts +17 -0
  37. package/dist/action-providers/messari/constants.js +20 -0
  38. package/dist/action-providers/messari/index.d.ts +5 -0
  39. package/dist/action-providers/messari/index.js +21 -0
  40. package/dist/action-providers/messari/messariActionProvider.d.ts +42 -0
  41. package/dist/action-providers/messari/messariActionProvider.js +128 -0
  42. package/dist/action-providers/messari/messariActionProvider.test.d.ts +1 -0
  43. package/dist/action-providers/messari/messariActionProvider.test.js +152 -0
  44. package/dist/action-providers/messari/schemas.d.ts +11 -0
  45. package/dist/action-providers/messari/schemas.js +16 -0
  46. package/dist/action-providers/messari/types.d.ts +40 -0
  47. package/dist/action-providers/messari/types.js +2 -0
  48. package/dist/action-providers/messari/utils.d.ts +22 -0
  49. package/dist/action-providers/messari/utils.js +65 -0
  50. package/dist/action-providers/morpho/morphoActionProvider.js +11 -4
  51. package/dist/action-providers/morpho/morphoActionProvider.test.js +2 -0
  52. package/dist/wallet-providers/cdpWalletProvider.d.ts +11 -2
  53. package/dist/wallet-providers/cdpWalletProvider.js +24 -0
  54. package/dist/wallet-providers/cdpWalletProvider.test.d.ts +1 -0
  55. package/dist/wallet-providers/cdpWalletProvider.test.js +701 -0
  56. package/dist/wallet-providers/evmWalletProvider.test.d.ts +1 -0
  57. package/dist/wallet-providers/evmWalletProvider.test.js +56 -0
  58. package/dist/wallet-providers/index.d.ts +1 -0
  59. package/dist/wallet-providers/index.js +1 -0
  60. package/dist/wallet-providers/privyEvmDelegatedEmbeddedWalletProvider.d.ts +167 -0
  61. package/dist/wallet-providers/privyEvmDelegatedEmbeddedWalletProvider.js +438 -0
  62. package/dist/wallet-providers/privyEvmDelegatedEmbeddedWalletProvider.test.d.ts +1 -0
  63. package/dist/wallet-providers/privyEvmDelegatedEmbeddedWalletProvider.test.js +280 -0
  64. package/dist/wallet-providers/privyEvmWalletProvider.test.d.ts +1 -0
  65. package/dist/wallet-providers/privyEvmWalletProvider.test.js +331 -0
  66. package/dist/wallet-providers/privyShared.d.ts +9 -0
  67. package/dist/wallet-providers/privyShared.js +16 -5
  68. package/dist/wallet-providers/privySvmWalletProvider.test.d.ts +1 -0
  69. package/dist/wallet-providers/privySvmWalletProvider.test.js +310 -0
  70. package/dist/wallet-providers/privyWalletProvider.d.ts +21 -8
  71. package/dist/wallet-providers/privyWalletProvider.js +39 -7
  72. package/dist/wallet-providers/privyWalletProvider.test.d.ts +1 -0
  73. package/dist/wallet-providers/privyWalletProvider.test.js +124 -0
  74. package/dist/wallet-providers/smartWalletProvider.test.d.ts +1 -0
  75. package/dist/wallet-providers/smartWalletProvider.test.js +388 -0
  76. package/dist/wallet-providers/solanaKeypairWalletProvider.test.js +210 -16
  77. package/dist/wallet-providers/svmWalletProvider.test.d.ts +1 -0
  78. package/dist/wallet-providers/svmWalletProvider.test.js +55 -0
  79. package/dist/wallet-providers/viemWalletProvider.test.d.ts +1 -0
  80. package/dist/wallet-providers/viemWalletProvider.test.js +338 -0
  81. package/dist/wallet-providers/walletProvider.test.d.ts +1 -0
  82. package/dist/wallet-providers/walletProvider.test.js +103 -0
  83. package/package.json +24 -20
@@ -0,0 +1,124 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const utils_1 = require("./utils");
4
+ describe("DefiLlama Utilities", () => {
5
+ describe("pruneGetProtocolResponse", () => {
6
+ it("should handle null input", () => {
7
+ expect((0, utils_1.pruneGetProtocolResponse)(null)).toBeNull();
8
+ });
9
+ it("should not modify objects without time-series data", () => {
10
+ const input = {
11
+ id: "test-protocol",
12
+ name: "Protocol",
13
+ symbol: "ABC",
14
+ };
15
+ const result = (0, utils_1.pruneGetProtocolResponse)(input);
16
+ expect(result).not.toBeNull();
17
+ expect(result).toEqual(input);
18
+ });
19
+ it("should prune time-series arrays", () => {
20
+ const timeSeriesData = Array.from({ length: 20 }, (_, i) => ({
21
+ date: Date.now() - i * 86400000,
22
+ totalLiquidityUSD: 1000 + i,
23
+ }));
24
+ const input = {
25
+ id: "test-protocol",
26
+ name: "Protocol",
27
+ tvl: [...timeSeriesData],
28
+ };
29
+ const result = (0, utils_1.pruneGetProtocolResponse)(input, 5);
30
+ expect(result).not.toBeNull();
31
+ if (result) {
32
+ expect(result.name).toBe("Protocol");
33
+ expect(result.tvl).toHaveLength(5);
34
+ const resultDates = result.tvl.map(entry => entry.date);
35
+ const sortedDates = [...resultDates].sort((a, b) => b - a);
36
+ expect(resultDates).toEqual(sortedDates);
37
+ }
38
+ });
39
+ it("should prune nested time-series arrays in chainTvls", () => {
40
+ const timeSeriesData = Array.from({ length: 20 }, (_, i) => ({
41
+ date: Date.now() - i * 86400000,
42
+ totalLiquidityUSD: 1000 + i,
43
+ }));
44
+ const tokenTimeSeriesData = Array.from({ length: 20 }, (_, i) => ({
45
+ date: Date.now() - i * 86400000,
46
+ tokens: { ETH: 1000 + i },
47
+ }));
48
+ const input = {
49
+ id: "test-protocol",
50
+ name: "Protocol",
51
+ chainTvls: {
52
+ Ethereum: {
53
+ tvl: [...timeSeriesData],
54
+ tokens: [...tokenTimeSeriesData],
55
+ },
56
+ Polygon: {
57
+ tvl: [...timeSeriesData],
58
+ tokensInUsd: [...tokenTimeSeriesData],
59
+ },
60
+ },
61
+ };
62
+ const result = (0, utils_1.pruneGetProtocolResponse)(input, 3);
63
+ expect(result).not.toBeNull();
64
+ if (result) {
65
+ expect(result.name).toBe("Protocol");
66
+ expect(result.chainTvls).toBeDefined();
67
+ if (result.chainTvls) {
68
+ expect(result.chainTvls.Ethereum.tvl).toHaveLength(3);
69
+ expect(result.chainTvls.Ethereum.tokens).toHaveLength(3);
70
+ expect(result.chainTvls.Polygon.tvl).toHaveLength(3);
71
+ expect(result.chainTvls.Polygon.tokensInUsd).toHaveLength(3);
72
+ const ethereumTvlDates = result.chainTvls.Ethereum.tvl.map(entry => entry.date);
73
+ const sortedDates = [...ethereumTvlDates].sort((a, b) => b - a);
74
+ expect(ethereumTvlDates).toEqual(sortedDates);
75
+ }
76
+ }
77
+ });
78
+ it("should respect the maxEntries parameter", () => {
79
+ const timeSeriesData = Array.from({ length: 50 }, (_, i) => ({
80
+ date: Date.now() - i * 86400000,
81
+ totalLiquidityUSD: 1000 + i,
82
+ }));
83
+ const tokenTimeSeriesData = Array.from({ length: 50 }, (_, i) => ({
84
+ date: Date.now() - i * 86400000,
85
+ tokens: { ETH: 1000 + i },
86
+ }));
87
+ const input = {
88
+ id: "test-protocol",
89
+ name: "Protocol",
90
+ tvl: [...timeSeriesData],
91
+ tokens: [...tokenTimeSeriesData],
92
+ };
93
+ const result1 = (0, utils_1.pruneGetProtocolResponse)(input, 1);
94
+ expect(result1).not.toBeNull();
95
+ if (result1) {
96
+ expect(result1.tvl).toHaveLength(1);
97
+ expect(result1.tokens).toHaveLength(1);
98
+ }
99
+ const result10 = (0, utils_1.pruneGetProtocolResponse)(input, 10);
100
+ expect(result10).not.toBeNull();
101
+ if (result10) {
102
+ expect(result10.tvl).toHaveLength(10);
103
+ expect(result10.tokens).toHaveLength(10);
104
+ }
105
+ });
106
+ it("should handle arrays without date property", () => {
107
+ const nonDateArray = Array.from({ length: 20 }, (_, i) => ({
108
+ value: 1000 + i,
109
+ name: `Item ${i}`,
110
+ }));
111
+ // Using a type assertion for this test case as it's testing a specific behavior
112
+ const input = {
113
+ id: "test-protocol",
114
+ name: "Protocol",
115
+ tvl: [...nonDateArray],
116
+ };
117
+ const result = (0, utils_1.pruneGetProtocolResponse)(input, 5);
118
+ expect(result).not.toBeNull();
119
+ if (result) {
120
+ expect(result.tvl).toHaveLength(5);
121
+ }
122
+ });
123
+ });
124
+ });
@@ -133,3 +133,5 @@ export declare const abi: readonly [{
133
133
  readonly type: "bool";
134
134
  }];
135
135
  }];
136
+ export declare const BaseTokenToAssetId: Map<string, string>;
137
+ export declare const BaseSepoliaTokenToAssetId: Map<string, string>;
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.abi = void 0;
3
+ exports.BaseSepoliaTokenToAssetId = exports.BaseTokenToAssetId = exports.abi = void 0;
4
+ const coinbase_sdk_1 = require("@coinbase/coinbase-sdk");
4
5
  exports.abi = [
5
6
  {
6
7
  type: "event",
@@ -189,3 +190,13 @@ exports.abi = [
189
190
  ],
190
191
  },
191
192
  ];
193
+ exports.BaseTokenToAssetId = new Map([
194
+ ["0xcbB7C0000aB88B473b1f5aFd9ef808440eed33Bf", coinbase_sdk_1.Coinbase.assets.Cbbtc],
195
+ ["0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", coinbase_sdk_1.Coinbase.assets.Usdc],
196
+ ["0x60a3E35Cc302bFA44Cb288Bc5a4F316Fdb1adb42", coinbase_sdk_1.Coinbase.assets.Eurc],
197
+ ]);
198
+ exports.BaseSepoliaTokenToAssetId = new Map([
199
+ ["0xcbB7C0006F23900c38EB856149F799620fcb8A4a", coinbase_sdk_1.Coinbase.assets.Cbbtc],
200
+ ["0x036CbD53842c5426634e7929541eC2318f3dCF7e", coinbase_sdk_1.Coinbase.assets.Usdc],
201
+ ["0x808456652fdb597867f38412077A9182bf77359F", coinbase_sdk_1.Coinbase.assets.Eurc],
202
+ ]);
@@ -70,6 +70,24 @@ class ERC20ActionProvider extends actionProvider_1.ActionProvider {
70
70
  */
71
71
  async transfer(walletProvider, args) {
72
72
  try {
73
+ // Check if we can do gasless transfer
74
+ const isCdpWallet = walletProvider.getName() === "cdp_wallet_provider";
75
+ const network = walletProvider.getNetwork();
76
+ const tokenAddress = (0, viem_1.getAddress)(args.contractAddress);
77
+ const canDoGasless = isCdpWallet &&
78
+ ((network.networkId === "base-mainnet" && constants_1.BaseTokenToAssetId.has(tokenAddress)) ||
79
+ (network.networkId === "base-sepolia" && constants_1.BaseSepoliaTokenToAssetId.has(tokenAddress)));
80
+ if (canDoGasless) {
81
+ // Cast to CdpWalletProvider to access erc20Transfer
82
+ const cdpWallet = walletProvider;
83
+ const assetId = network.networkId === "base-mainnet"
84
+ ? constants_1.BaseTokenToAssetId.get(tokenAddress)
85
+ : constants_1.BaseSepoliaTokenToAssetId.get(tokenAddress);
86
+ const hash = await cdpWallet.gaslessERC20Transfer(assetId, args.destination, args.amount);
87
+ await walletProvider.waitForTransactionReceipt(hash);
88
+ return `Transferred ${args.amount} of ${args.contractAddress} to ${args.destination} using gasless transfer.\nTransaction hash: ${hash}`;
89
+ }
90
+ // Fallback to regular transfer
73
91
  const hash = await walletProvider.sendTransaction({
74
92
  to: args.contractAddress,
75
93
  data: (0, viem_1.encodeFunctionData)({
@@ -74,6 +74,10 @@ describe("Transfer Action", () => {
74
74
  mockWallet = {
75
75
  sendTransaction: jest.fn(),
76
76
  waitForTransactionReceipt: jest.fn(),
77
+ getName: jest.fn().mockReturnValue("evm_wallet_provider"),
78
+ getNetwork: jest.fn().mockReturnValue({
79
+ networkId: "base-mainnet",
80
+ }),
77
81
  };
78
82
  mockWallet.sendTransaction.mockResolvedValue(TRANSACTION_HASH);
79
83
  mockWallet.waitForTransactionReceipt.mockResolvedValue({});
@@ -1,14 +1,17 @@
1
1
  export * from "./actionDecorator";
2
2
  export * from "./actionProvider";
3
3
  export * from "./customActionProvider";
4
+ export * from "./across";
4
5
  export * from "./alchemy";
5
6
  export * from "./basename";
6
7
  export * from "./cdp";
7
8
  export * from "./compound";
9
+ export * from "./defillama";
8
10
  export * from "./erc20";
9
11
  export * from "./erc721";
10
12
  export * from "./farcaster";
11
13
  export * from "./jupiter";
14
+ export * from "./messari";
12
15
  export * from "./pyth";
13
16
  export * from "./moonwell";
14
17
  export * from "./morpho";
@@ -17,14 +17,17 @@ Object.defineProperty(exports, "__esModule", { value: true });
17
17
  __exportStar(require("./actionDecorator"), exports);
18
18
  __exportStar(require("./actionProvider"), exports);
19
19
  __exportStar(require("./customActionProvider"), exports);
20
+ __exportStar(require("./across"), exports);
20
21
  __exportStar(require("./alchemy"), exports);
21
22
  __exportStar(require("./basename"), exports);
22
23
  __exportStar(require("./cdp"), exports);
23
24
  __exportStar(require("./compound"), exports);
25
+ __exportStar(require("./defillama"), exports);
24
26
  __exportStar(require("./erc20"), exports);
25
27
  __exportStar(require("./erc721"), exports);
26
28
  __exportStar(require("./farcaster"), exports);
27
29
  __exportStar(require("./jupiter"), exports);
30
+ __exportStar(require("./messari"), exports);
28
31
  __exportStar(require("./pyth"), exports);
29
32
  __exportStar(require("./moonwell"), exports);
30
33
  __exportStar(require("./morpho"), exports);
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Base URL for the Messari API
3
+ */
4
+ export declare const MESSARI_BASE_URL = "https://api.messari.io/ai/v1";
5
+ /**
6
+ * Default error message when API key is missing
7
+ */
8
+ export declare const API_KEY_MISSING_ERROR = "MESSARI_API_KEY is not configured.";
9
+ /**
10
+ * Rate limits by subscription tier
11
+ */
12
+ export declare const RATE_LIMITS: {
13
+ FREE: string;
14
+ LITE: string;
15
+ PRO: string;
16
+ ENTERPRISE: string;
17
+ };
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.RATE_LIMITS = exports.API_KEY_MISSING_ERROR = exports.MESSARI_BASE_URL = void 0;
4
+ /**
5
+ * Base URL for the Messari API
6
+ */
7
+ exports.MESSARI_BASE_URL = "https://api.messari.io/ai/v1";
8
+ /**
9
+ * Default error message when API key is missing
10
+ */
11
+ exports.API_KEY_MISSING_ERROR = "MESSARI_API_KEY is not configured.";
12
+ /**
13
+ * Rate limits by subscription tier
14
+ */
15
+ exports.RATE_LIMITS = {
16
+ FREE: "2 requests per day",
17
+ LITE: "10 requests per day",
18
+ PRO: "20 requests per day",
19
+ ENTERPRISE: "50 requests per day",
20
+ };
@@ -0,0 +1,5 @@
1
+ export * from "./messariActionProvider";
2
+ export * from "./schemas";
3
+ export * from "./types";
4
+ export * from "./constants";
5
+ export * from "./utils";
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./messariActionProvider"), exports);
18
+ __exportStar(require("./schemas"), exports);
19
+ __exportStar(require("./types"), exports);
20
+ __exportStar(require("./constants"), exports);
21
+ __exportStar(require("./utils"), exports);
@@ -0,0 +1,42 @@
1
+ import { z } from "zod";
2
+ import { ActionProvider } from "../actionProvider";
3
+ import { Network } from "../../network";
4
+ import { MessariResearchQuestionSchema } from "./schemas";
5
+ import { MessariActionProviderConfig } from "./types";
6
+ /**
7
+ * MessariActionProvider is an action provider for Messari AI toolkit interactions.
8
+ * It enables AI agents to ask research questions about crypto markets, protocols, and tokens.
9
+ *
10
+ * @augments ActionProvider
11
+ */
12
+ export declare class MessariActionProvider extends ActionProvider {
13
+ private readonly apiKey;
14
+ /**
15
+ * Constructor for the MessariActionProvider class.
16
+ *
17
+ * @param config - The configuration options for the MessariActionProvider
18
+ */
19
+ constructor(config?: MessariActionProviderConfig);
20
+ /**
21
+ * Makes a request to the Messari AI API with a research question
22
+ *
23
+ * @param args - The arguments containing the research question
24
+ * @returns A string containing the research results or an error message
25
+ */
26
+ researchQuestion(args: z.infer<typeof MessariResearchQuestionSchema>): Promise<string>;
27
+ /**
28
+ * Checks if the action provider supports the given network.
29
+ * Messari research is network-agnostic, so it supports all networks.
30
+ *
31
+ * @param _ - The network to check
32
+ * @returns Always returns true as Messari research is network-agnostic
33
+ */
34
+ supportsNetwork(_: Network): boolean;
35
+ }
36
+ /**
37
+ * Factory function to create a new MessariActionProvider instance.
38
+ *
39
+ * @param config - The configuration options for the MessariActionProvider
40
+ * @returns A new instance of MessariActionProvider
41
+ */
42
+ export declare const messariActionProvider: (config?: MessariActionProviderConfig) => MessariActionProvider;
@@ -0,0 +1,128 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.messariActionProvider = exports.MessariActionProvider = void 0;
13
+ const zod_1 = require("zod");
14
+ const actionProvider_1 = require("../actionProvider");
15
+ const actionDecorator_1 = require("../actionDecorator");
16
+ const schemas_1 = require("./schemas");
17
+ const constants_1 = require("./constants");
18
+ const utils_1 = require("./utils");
19
+ /**
20
+ * MessariActionProvider is an action provider for Messari AI toolkit interactions.
21
+ * It enables AI agents to ask research questions about crypto markets, protocols, and tokens.
22
+ *
23
+ * @augments ActionProvider
24
+ */
25
+ class MessariActionProvider extends actionProvider_1.ActionProvider {
26
+ /**
27
+ * Constructor for the MessariActionProvider class.
28
+ *
29
+ * @param config - The configuration options for the MessariActionProvider
30
+ */
31
+ constructor(config = {}) {
32
+ super("messari", []);
33
+ config.apiKey || (config.apiKey = process.env.MESSARI_API_KEY);
34
+ if (!config.apiKey) {
35
+ throw new Error(constants_1.API_KEY_MISSING_ERROR);
36
+ }
37
+ this.apiKey = config.apiKey;
38
+ }
39
+ /**
40
+ * Makes a request to the Messari AI API with a research question
41
+ *
42
+ * @param args - The arguments containing the research question
43
+ * @returns A string containing the research results or an error message
44
+ */
45
+ async researchQuestion(args) {
46
+ try {
47
+ // Make API request
48
+ const response = await fetch(`${constants_1.MESSARI_BASE_URL}/chat/completions`, {
49
+ method: "POST",
50
+ headers: {
51
+ "Content-Type": "application/json",
52
+ "x-messari-api-key": this.apiKey,
53
+ },
54
+ body: JSON.stringify({
55
+ messages: [
56
+ {
57
+ role: "user",
58
+ content: args.question,
59
+ },
60
+ ],
61
+ }),
62
+ });
63
+ if (!response.ok) {
64
+ throw await (0, utils_1.createMessariError)(response);
65
+ }
66
+ // Parse and validate response
67
+ let data;
68
+ try {
69
+ data = (await response.json());
70
+ }
71
+ catch (jsonError) {
72
+ throw new Error(`Failed to parse API response: ${jsonError instanceof Error ? jsonError.message : String(jsonError)}`);
73
+ }
74
+ if (!data.data?.messages?.[0]?.content) {
75
+ throw new Error("Received invalid response format from Messari API");
76
+ }
77
+ const result = data.data.messages[0].content;
78
+ return `Messari Research Results:\n\n${result}`;
79
+ }
80
+ catch (error) {
81
+ if (error instanceof Error && "responseText" in error) {
82
+ return (0, utils_1.formatMessariApiError)(error);
83
+ }
84
+ return (0, utils_1.formatGenericError)(error);
85
+ }
86
+ }
87
+ /**
88
+ * Checks if the action provider supports the given network.
89
+ * Messari research is network-agnostic, so it supports all networks.
90
+ *
91
+ * @param _ - The network to check
92
+ * @returns Always returns true as Messari research is network-agnostic
93
+ */
94
+ supportsNetwork(_) {
95
+ return true; // Messari research is network-agnostic
96
+ }
97
+ }
98
+ exports.MessariActionProvider = MessariActionProvider;
99
+ __decorate([
100
+ (0, actionDecorator_1.CreateAction)({
101
+ name: "research_question",
102
+ description: `
103
+ This tool queries Messari AI for comprehensive crypto research across these datasets:
104
+ 1. News/Content - Latest crypto news, blogs, podcasts
105
+ 2. Exchanges - CEX/DEX volumes, market share, assets listed
106
+ 3. Onchain Data - Active addresses, transaction fees, total transactions.
107
+ 4. Token Unlocks - Upcoming supply unlocks, vesting schedules, and token emission details
108
+ 5. Market Data - Asset prices, trading volume, market cap, TVL, and historical performance
109
+ 6. Fundraising - Investment data, funding rounds, venture capital activity.
110
+ 7. Protocol Research - Technical analysis of how protocols work, tokenomics, and yield mechanisms
111
+ 8. Social Data - Twitter followers and Reddit subscribers metrics, growth trends
112
+
113
+ Examples: "Which DEXs have the highest trading volume this month?", "When is Arbitrum's next major token unlock?", "How does Morpho generate yield for users?", "Which cryptocurrency has gained the most Twitter followers in 2023?", "What did Vitalik Buterin say about rollups in his recent blog posts?"
114
+ `,
115
+ schema: schemas_1.MessariResearchQuestionSchema,
116
+ }),
117
+ __metadata("design:type", Function),
118
+ __metadata("design:paramtypes", [void 0]),
119
+ __metadata("design:returntype", Promise)
120
+ ], MessariActionProvider.prototype, "researchQuestion", null);
121
+ /**
122
+ * Factory function to create a new MessariActionProvider instance.
123
+ *
124
+ * @param config - The configuration options for the MessariActionProvider
125
+ * @returns A new instance of MessariActionProvider
126
+ */
127
+ const messariActionProvider = (config = {}) => new MessariActionProvider(config);
128
+ exports.messariActionProvider = messariActionProvider;
@@ -0,0 +1,152 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const messariActionProvider_1 = require("./messariActionProvider");
4
+ const constants_1 = require("./constants");
5
+ const MOCK_API_KEY = "messari-test-key";
6
+ const MOCK_RESEARCH_RESPONSE = {
7
+ data: {
8
+ messages: [
9
+ {
10
+ role: "assistant",
11
+ content: "Ethereum (ETH) has shown strong performance over the past month with a 15% price increase. The current price is approximately $3,500, up from $3,000 at the beginning of the month. Trading volume has also increased by 20% in the same period.",
12
+ },
13
+ ],
14
+ },
15
+ };
16
+ const MOCK_ERROR_RESPONSE = {
17
+ error: "Internal server error, please try again. If the problem persists, please contact support",
18
+ data: null,
19
+ };
20
+ describe("MessariActionProvider", () => {
21
+ let provider;
22
+ beforeEach(() => {
23
+ process.env.MESSARI_API_KEY = MOCK_API_KEY;
24
+ provider = (0, messariActionProvider_1.messariActionProvider)({ apiKey: MOCK_API_KEY });
25
+ jest.restoreAllMocks();
26
+ });
27
+ afterEach(() => {
28
+ jest.clearAllMocks();
29
+ delete process.env.MESSARI_API_KEY;
30
+ });
31
+ describe("constructor", () => {
32
+ it("should initialize with API key from constructor", () => {
33
+ const customProvider = (0, messariActionProvider_1.messariActionProvider)({ apiKey: "custom-key" });
34
+ expect(customProvider["apiKey"]).toBe("custom-key");
35
+ });
36
+ it("should initialize with API key from environment variable", () => {
37
+ process.env.MESSARI_API_KEY = "env-key";
38
+ const envProvider = (0, messariActionProvider_1.messariActionProvider)();
39
+ expect(envProvider["apiKey"]).toBe("env-key");
40
+ });
41
+ it("should throw error if API key is not provided", () => {
42
+ delete process.env.MESSARI_API_KEY;
43
+ expect(() => (0, messariActionProvider_1.messariActionProvider)()).toThrow(constants_1.API_KEY_MISSING_ERROR);
44
+ });
45
+ });
46
+ describe("researchQuestion", () => {
47
+ it("should successfully fetch research results", async () => {
48
+ const fetchMock = jest.spyOn(global, "fetch").mockResolvedValue({
49
+ ok: true,
50
+ json: async () => MOCK_RESEARCH_RESPONSE,
51
+ });
52
+ const question = "What is the current price of Ethereum?";
53
+ const response = await provider.researchQuestion({ question });
54
+ expect(fetchMock).toHaveBeenCalled();
55
+ const [url, options] = fetchMock.mock.calls[0];
56
+ expect(url).toBe("https://api.messari.io/ai/v1/chat/completions");
57
+ expect(options).toEqual(expect.objectContaining({
58
+ method: "POST",
59
+ headers: expect.objectContaining({
60
+ "Content-Type": "application/json",
61
+ "x-messari-api-key": MOCK_API_KEY,
62
+ }),
63
+ body: JSON.stringify({
64
+ messages: [
65
+ {
66
+ role: "user",
67
+ content: question,
68
+ },
69
+ ],
70
+ }),
71
+ }));
72
+ expect(response).toContain("Messari Research Results:");
73
+ expect(response).toContain(MOCK_RESEARCH_RESPONSE.data.messages[0].content);
74
+ });
75
+ it("should handle non-ok response with structured error format", async () => {
76
+ const errorResponseText = JSON.stringify(MOCK_ERROR_RESPONSE);
77
+ jest.spyOn(global, "fetch").mockResolvedValue({
78
+ ok: false,
79
+ status: 500,
80
+ statusText: "Internal Server Error",
81
+ text: async () => errorResponseText,
82
+ });
83
+ const response = await provider.researchQuestion({
84
+ question: "What is the current price of Bitcoin?",
85
+ });
86
+ expect(response).toContain("Messari API Error: Internal server error");
87
+ expect(response).not.toContain("500");
88
+ });
89
+ it("should handle non-ok response with non-JSON error format", async () => {
90
+ const plainTextError = "Rate limit exceeded";
91
+ jest.spyOn(global, "fetch").mockResolvedValue({
92
+ ok: false,
93
+ status: 429,
94
+ statusText: "Too Many Requests",
95
+ text: async () => plainTextError,
96
+ });
97
+ const response = await provider.researchQuestion({
98
+ question: "What is the current price of Bitcoin?",
99
+ });
100
+ expect(response).toContain("Messari API Error:");
101
+ expect(response).toContain("429");
102
+ expect(response).toContain("Too Many Requests");
103
+ expect(response).toContain(plainTextError);
104
+ });
105
+ it("should handle JSON parsing error in successful response", async () => {
106
+ jest.spyOn(global, "fetch").mockResolvedValue({
107
+ ok: true,
108
+ json: async () => {
109
+ throw new Error("Invalid JSON");
110
+ },
111
+ });
112
+ const response = await provider.researchQuestion({
113
+ question: "What is the market cap of Solana?",
114
+ });
115
+ expect(response).toContain("Unexpected error: Failed to parse API response");
116
+ expect(response).toContain("Invalid JSON");
117
+ });
118
+ it("should handle invalid response format", async () => {
119
+ jest.spyOn(global, "fetch").mockResolvedValue({
120
+ ok: true,
121
+ json: async () => ({ data: { messages: [] } }),
122
+ });
123
+ const response = await provider.researchQuestion({
124
+ question: "What is the market cap of Solana?",
125
+ });
126
+ expect(response).toContain("Unexpected error: Received invalid response format from Messari API");
127
+ });
128
+ it("should handle fetch error", async () => {
129
+ const error = new Error("Network error");
130
+ jest.spyOn(global, "fetch").mockRejectedValue(error);
131
+ const response = await provider.researchQuestion({
132
+ question: "What is the market cap of Solana?",
133
+ });
134
+ expect(response).toContain("Unexpected error: Network error");
135
+ });
136
+ it("should handle string error with JSON content", async () => {
137
+ const stringifiedError = JSON.stringify(MOCK_ERROR_RESPONSE);
138
+ jest.spyOn(global, "fetch").mockRejectedValue(stringifiedError);
139
+ const response = await provider.researchQuestion({
140
+ question: "What is the market cap of Solana?",
141
+ });
142
+ expect(response).toContain("Messari API Error: Internal server error");
143
+ });
144
+ });
145
+ describe("supportsNetwork", () => {
146
+ it("should always return true as research is network-agnostic", () => {
147
+ expect(provider.supportsNetwork({ protocolFamily: "evm" })).toBe(true);
148
+ expect(provider.supportsNetwork({ protocolFamily: "solana" })).toBe(true);
149
+ expect(provider.supportsNetwork({ protocolFamily: "unknown" })).toBe(true);
150
+ });
151
+ });
152
+ });
@@ -0,0 +1,11 @@
1
+ import { z } from "zod";
2
+ /**
3
+ * Input schema for submitting a research question to Messari AI.
4
+ */
5
+ export declare const MessariResearchQuestionSchema: z.ZodObject<{
6
+ question: z.ZodString;
7
+ }, "strip", z.ZodTypeAny, {
8
+ question: string;
9
+ }, {
10
+ question: string;
11
+ }>;
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MessariResearchQuestionSchema = void 0;
4
+ const zod_1 = require("zod");
5
+ /**
6
+ * Input schema for submitting a research question to Messari AI.
7
+ */
8
+ exports.MessariResearchQuestionSchema = zod_1.z
9
+ .object({
10
+ question: zod_1.z
11
+ .string()
12
+ .min(1, "Research question is required.")
13
+ .describe("The research question about crypto markets, protocols, or tokens"),
14
+ })
15
+ .strip()
16
+ .describe("Input schema for submitting a research question to Messari AI");