@coinbase/agentkit 0.9.0 → 0.10.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 (74) hide show
  1. package/README.md +137 -55
  2. package/dist/action-providers/cdp/cdpApiActionProvider.js +2 -0
  3. package/dist/action-providers/cdp/cdpEvmWalletActionProvider.d.ts +43 -0
  4. package/dist/action-providers/cdp/cdpEvmWalletActionProvider.js +151 -0
  5. package/dist/action-providers/cdp/cdpEvmWalletActionProvider.test.d.ts +1 -0
  6. package/dist/action-providers/cdp/cdpEvmWalletActionProvider.test.js +242 -0
  7. package/dist/action-providers/cdp/cdpSmartWalletActionProvider.d.ts +42 -0
  8. package/dist/action-providers/cdp/cdpSmartWalletActionProvider.js +132 -0
  9. package/dist/action-providers/cdp/cdpSmartWalletActionProvider.test.d.ts +1 -0
  10. package/dist/action-providers/cdp/cdpSmartWalletActionProvider.test.js +199 -0
  11. package/dist/action-providers/cdp/index.d.ts +3 -0
  12. package/dist/action-providers/cdp/index.js +3 -0
  13. package/dist/action-providers/cdp/schemas.d.ts +29 -0
  14. package/dist/action-providers/cdp/schemas.js +32 -1
  15. package/dist/action-providers/cdp/spendPermissionUtils.d.ts +24 -0
  16. package/dist/action-providers/cdp/spendPermissionUtils.js +66 -0
  17. package/dist/action-providers/farcaster/farcasterActionProvider.js +2 -0
  18. package/dist/action-providers/farcaster/farcasterActionProvider.test.js +55 -0
  19. package/dist/action-providers/farcaster/schemas.d.ts +13 -0
  20. package/dist/action-providers/farcaster/schemas.js +6 -0
  21. package/dist/action-providers/index.d.ts +3 -0
  22. package/dist/action-providers/index.js +3 -0
  23. package/dist/action-providers/truemarkets/constants.d.ts +179 -0
  24. package/dist/action-providers/truemarkets/constants.js +46 -0
  25. package/dist/action-providers/truemarkets/index.d.ts +1 -0
  26. package/dist/action-providers/truemarkets/index.js +17 -0
  27. package/dist/action-providers/truemarkets/schemas.d.ts +21 -0
  28. package/dist/action-providers/truemarkets/schemas.js +29 -0
  29. package/dist/action-providers/truemarkets/truemarketsActionProvider.d.ts +51 -0
  30. package/dist/action-providers/truemarkets/truemarketsActionProvider.js +469 -0
  31. package/dist/action-providers/truemarkets/truemarketsActionProvider.test.d.ts +1 -0
  32. package/dist/action-providers/truemarkets/truemarketsActionProvider.test.js +217 -0
  33. package/dist/action-providers/truemarkets/utils.d.ts +10 -0
  34. package/dist/action-providers/truemarkets/utils.js +9 -0
  35. package/dist/action-providers/twitter/schemas.d.ts +16 -0
  36. package/dist/action-providers/twitter/schemas.js +23 -1
  37. package/dist/action-providers/twitter/twitterActionProvider.d.ts +8 -1
  38. package/dist/action-providers/twitter/twitterActionProvider.js +56 -5
  39. package/dist/action-providers/twitter/twitterActionProvider.test.js +52 -2
  40. package/dist/action-providers/weth/constants.d.ts +9 -0
  41. package/dist/action-providers/weth/constants.js +12 -0
  42. package/dist/action-providers/weth/schemas.d.ts +7 -0
  43. package/dist/action-providers/weth/schemas.js +7 -1
  44. package/dist/action-providers/weth/wethActionProvider.d.ts +9 -1
  45. package/dist/action-providers/weth/wethActionProvider.js +50 -1
  46. package/dist/action-providers/weth/wethActionProvider.test.js +60 -0
  47. package/dist/action-providers/zerion/constants.d.ts +1 -0
  48. package/dist/action-providers/zerion/constants.js +4 -0
  49. package/dist/action-providers/zerion/index.d.ts +2 -0
  50. package/dist/action-providers/zerion/index.js +18 -0
  51. package/dist/action-providers/zerion/schemas.d.ts +11 -0
  52. package/dist/action-providers/zerion/schemas.js +15 -0
  53. package/dist/action-providers/zerion/types.d.ts +125 -0
  54. package/dist/action-providers/zerion/types.js +16 -0
  55. package/dist/action-providers/zerion/utils.d.ts +3 -0
  56. package/dist/action-providers/zerion/utils.js +45 -0
  57. package/dist/action-providers/zerion/zerionActionProvider.d.ts +57 -0
  58. package/dist/action-providers/zerion/zerionActionProvider.js +159 -0
  59. package/dist/action-providers/zerion/zerionActionProvider.test.d.ts +1 -0
  60. package/dist/action-providers/zerion/zerionActionProvider.test.js +213 -0
  61. package/dist/action-providers/zora/index.d.ts +3 -0
  62. package/dist/action-providers/zora/index.js +19 -0
  63. package/dist/action-providers/zora/schemas.d.ts +29 -0
  64. package/dist/action-providers/zora/schemas.js +31 -0
  65. package/dist/action-providers/zora/utils.d.ts +28 -0
  66. package/dist/action-providers/zora/utils.js +200 -0
  67. package/dist/action-providers/zora/zoraActionProvider.d.ts +36 -0
  68. package/dist/action-providers/zora/zoraActionProvider.js +151 -0
  69. package/dist/action-providers/zora/zoraActionProvider.test.d.ts +1 -0
  70. package/dist/action-providers/zora/zoraActionProvider.test.js +205 -0
  71. package/dist/wallet-providers/cdpShared.d.ts +4 -0
  72. package/dist/wallet-providers/cdpSmartWalletProvider.d.ts +8 -1
  73. package/dist/wallet-providers/cdpSmartWalletProvider.js +23 -11
  74. package/package.json +3 -2
@@ -0,0 +1,217 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const truemarketsActionProvider_1 = require("./truemarketsActionProvider");
4
+ const constants_1 = require("./constants");
5
+ const viem_1 = require("viem");
6
+ // Mock viem's createPublicClient
7
+ jest.mock("viem", () => {
8
+ const originalModule = jest.requireActual("viem");
9
+ return {
10
+ ...originalModule,
11
+ createPublicClient: jest.fn().mockImplementation(() => ({
12
+ // Mock public client methods as needed
13
+ multicall: jest.fn().mockImplementation(({ contracts }) => {
14
+ // Create mock responses with success status
15
+ return contracts.map(() => ({
16
+ status: "success",
17
+ result: "mock result",
18
+ }));
19
+ }),
20
+ readContract: jest.fn(),
21
+ })),
22
+ http: jest.fn().mockImplementation(url => ({ url })),
23
+ };
24
+ });
25
+ describe("TrueMarketsActionProvider", () => {
26
+ let provider;
27
+ let mockWallet;
28
+ // Mock addresses and data for tests
29
+ const MOCK_MARKET_ADDRESS = "0x1234567890123456789012345678901234567890";
30
+ const MOCK_YES_POOL_ADDRESS = "0x2345678901234567890123456789012345678901";
31
+ const MOCK_NO_POOL_ADDRESS = "0x3456789012345678901234567890123456789012";
32
+ const MOCK_YES_TOKEN_ADDRESS = "0x4567890123456789012345678901234567890123";
33
+ const MOCK_NO_TOKEN_ADDRESS = "0x5678901234567890123456789012345678901234";
34
+ const MOCK_MARKET_QUESTION = "Will this test pass?";
35
+ const MOCK_ADDITIONAL_INFO = "Test additional info";
36
+ const MOCK_MARKET_SOURCE = "Test source";
37
+ const MOCK_STATUS_NUM = 0n; // Created status
38
+ const MOCK_END_OF_TRADING = 1717171717n; // Unix timestamp
39
+ describe("constructor", () => {
40
+ it("should use the provided RPC_URL for the public client", () => {
41
+ const customRpcUrl = "https://custom-rpc.example.com";
42
+ (0, truemarketsActionProvider_1.truemarketsActionProvider)({ RPC_URL: customRpcUrl });
43
+ // Verify createPublicClient was called with the correct URL
44
+ expect(viem_1.createPublicClient).toHaveBeenCalledWith(expect.objectContaining({
45
+ chain: expect.anything(),
46
+ transport: expect.objectContaining({ url: customRpcUrl }),
47
+ }));
48
+ });
49
+ });
50
+ beforeEach(() => {
51
+ jest.clearAllMocks();
52
+ provider = (0, truemarketsActionProvider_1.truemarketsActionProvider)();
53
+ mockWallet = {
54
+ readContract: jest.fn(),
55
+ getName: jest.fn().mockReturnValue("evm_wallet_provider"),
56
+ getNetwork: jest.fn().mockReturnValue({
57
+ networkId: "base-mainnet",
58
+ }),
59
+ };
60
+ });
61
+ afterEach(() => {
62
+ jest.clearAllMocks();
63
+ });
64
+ describe("getPredictionMarkets", () => {
65
+ it("should successfully fetch active markets", async () => {
66
+ // Mock readContract calls
67
+ mockWallet.readContract
68
+ // First call: numberOfActiveMarkets
69
+ .mockResolvedValueOnce(2n);
70
+ // Create a spy on the actual implementation
71
+ const getPredictionMarketsSpy = jest.spyOn(provider, "getPredictionMarkets");
72
+ // Replace the original implementation with our mocked version to return JSON string
73
+ getPredictionMarketsSpy.mockImplementation(async () => {
74
+ return JSON.stringify({
75
+ success: true,
76
+ totalMarkets: 2,
77
+ markets: [
78
+ {
79
+ id: 1,
80
+ address: MOCK_MARKET_ADDRESS,
81
+ marketQuestion: MOCK_MARKET_QUESTION,
82
+ },
83
+ {
84
+ id: 0,
85
+ address: "0x6789012345678901234567890123456789012345",
86
+ marketQuestion: "Will this other test pass?",
87
+ },
88
+ ],
89
+ });
90
+ });
91
+ const args = {
92
+ limit: 10,
93
+ offset: 0,
94
+ sortOrder: "desc",
95
+ };
96
+ const responseString = await provider.getPredictionMarkets(mockWallet, args);
97
+ const response = JSON.parse(responseString);
98
+ // Verify response contains expected data
99
+ expect(response.success).toBe(true);
100
+ expect(response.totalMarkets).toBe(2);
101
+ expect(response.markets.length).toBe(2);
102
+ expect(response.markets[0].marketQuestion).toBe(MOCK_MARKET_QUESTION);
103
+ expect(response.markets[0].address).toBe(MOCK_MARKET_ADDRESS);
104
+ // Restore the original implementation
105
+ getPredictionMarketsSpy.mockRestore();
106
+ });
107
+ it("should handle no active markets", async () => {
108
+ mockWallet.readContract.mockResolvedValueOnce(0n);
109
+ const args = {
110
+ limit: 10,
111
+ offset: 0,
112
+ sortOrder: "desc",
113
+ };
114
+ const responseString = await provider.getPredictionMarkets(mockWallet, args);
115
+ const response = JSON.parse(responseString);
116
+ expect(response.success).toBe(true);
117
+ expect(response.totalMarkets).toBe(0);
118
+ expect(response.markets.length).toBe(0);
119
+ });
120
+ it("should handle errors", async () => {
121
+ const error = new Error("Failed to fetch active markets");
122
+ mockWallet.readContract.mockRejectedValueOnce(error);
123
+ const args = {
124
+ limit: 10,
125
+ offset: 0,
126
+ sortOrder: "desc",
127
+ };
128
+ const responseString = await provider.getPredictionMarkets(mockWallet, args);
129
+ const response = JSON.parse(responseString);
130
+ expect(response.success).toBe(false);
131
+ expect(response.error).toBe("Error retrieving active markets: Error: Failed to fetch active markets");
132
+ });
133
+ });
134
+ describe("getPredictionMarketDetails", () => {
135
+ let mockPublicClient;
136
+ beforeEach(() => {
137
+ // Access the mocked public client
138
+ mockPublicClient = viem_1.createPublicClient.mock.results[0].value;
139
+ // Setup multicall mock responses
140
+ mockPublicClient.multicall
141
+ // Basic info calls
142
+ .mockResolvedValueOnce([
143
+ { status: "success", result: MOCK_MARKET_QUESTION },
144
+ { status: "success", result: MOCK_ADDITIONAL_INFO },
145
+ { status: "success", result: MOCK_MARKET_SOURCE },
146
+ { status: "success", result: MOCK_STATUS_NUM },
147
+ { status: "success", result: MOCK_END_OF_TRADING },
148
+ { status: "success", result: [MOCK_YES_POOL_ADDRESS, MOCK_NO_POOL_ADDRESS] },
149
+ { status: "success", result: 0n },
150
+ ])
151
+ // Pool info calls
152
+ .mockResolvedValueOnce([
153
+ { status: "success", result: constants_1.USDC_ADDRESS },
154
+ { status: "success", result: MOCK_YES_TOKEN_ADDRESS },
155
+ { status: "success", result: MOCK_NO_TOKEN_ADDRESS },
156
+ { status: "success", result: constants_1.USDC_ADDRESS },
157
+ { status: "success", result: [79228162514264337593543950336n, 0, 0, 0, 0, 0, true] },
158
+ { status: "success", result: [79228162514264337593543950336n, 0, 0, 0, 0, 0, true] },
159
+ ])
160
+ // Balance calls
161
+ .mockResolvedValueOnce([
162
+ { status: "success", result: 1000000n },
163
+ { status: "success", result: 500000000000000000000n },
164
+ { status: "success", result: 2000000n },
165
+ { status: "success", result: 1000000000000000000000n },
166
+ ]);
167
+ });
168
+ it("should successfully fetch market details", async () => {
169
+ const args = MOCK_MARKET_ADDRESS;
170
+ const responseString = await provider.getPredictionMarketDetails(mockWallet, args);
171
+ const response = JSON.parse(responseString);
172
+ // Verify the expected JSON structure
173
+ expect(response.success).toBe(true);
174
+ expect(response.marketAddress).toBe(MOCK_MARKET_ADDRESS);
175
+ expect(response.question).toBe(MOCK_MARKET_QUESTION);
176
+ expect(response.additionalInfo).toBe(MOCK_ADDITIONAL_INFO);
177
+ expect(response.source).toBe(MOCK_MARKET_SOURCE);
178
+ expect(response.status).toBe(0);
179
+ // Verify tokens
180
+ expect(response.tokens.yes.lpAddress).toBe(MOCK_YES_POOL_ADDRESS);
181
+ expect(response.tokens.yes.tokenAddress).toBe(MOCK_YES_TOKEN_ADDRESS);
182
+ expect(response.tokens.no.lpAddress).toBe(MOCK_NO_POOL_ADDRESS);
183
+ expect(response.tokens.no.tokenAddress).toBe(MOCK_NO_TOKEN_ADDRESS);
184
+ // Verify prices and tvl exist
185
+ expect(typeof response.prices.yes).toBe("number");
186
+ expect(typeof response.prices.no).toBe("number");
187
+ expect(typeof response.tvl).toBe("number");
188
+ });
189
+ it("should handle errors", async () => {
190
+ const error = new Error("Failed to fetch market details");
191
+ mockWallet.readContract.mockReset();
192
+ mockPublicClient.multicall.mockReset();
193
+ mockPublicClient.multicall.mockRejectedValueOnce(error);
194
+ const args = MOCK_MARKET_ADDRESS;
195
+ const responseString = await provider.getPredictionMarketDetails(mockWallet, args);
196
+ const response = JSON.parse(responseString);
197
+ expect(response.success).toBe(false);
198
+ expect(response.error).toBe("Error retrieving market details: Error: Failed to fetch market details");
199
+ });
200
+ });
201
+ describe("supportsNetwork", () => {
202
+ it("should return true for base-mainnet", () => {
203
+ const network = {
204
+ networkId: "base-mainnet",
205
+ protocolFamily: "evm",
206
+ };
207
+ expect(provider.supportsNetwork(network)).toBe(true);
208
+ });
209
+ it("should return false for other networks", () => {
210
+ const network = {
211
+ networkId: "ethereum-mainnet",
212
+ protocolFamily: "evm",
213
+ };
214
+ expect(provider.supportsNetwork(network)).toBe(false);
215
+ });
216
+ });
217
+ });
@@ -0,0 +1,10 @@
1
+ export type Slot0Result = readonly [bigint, number, number, number, number, number, boolean];
2
+ export declare const GetMarketStatus: Record<number, string>;
3
+ /**
4
+ * Interface representing a TruthMarket
5
+ */
6
+ export interface TruthMarket {
7
+ id: number;
8
+ address: string;
9
+ marketQuestion: string;
10
+ }
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.GetMarketStatus = void 0;
4
+ const constants_1 = require("./constants");
5
+ // Create mapping for status lookup
6
+ exports.GetMarketStatus = Object.entries(constants_1.MarketStatus).reduce((acc, [key, value]) => {
7
+ acc[value] = key;
8
+ return acc;
9
+ }, {});
@@ -18,10 +18,13 @@ export declare const TwitterAccountMentionsSchema: z.ZodObject<{
18
18
  */
19
19
  export declare const TwitterPostTweetSchema: z.ZodObject<{
20
20
  tweet: z.ZodString;
21
+ mediaIds: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
21
22
  }, "strip", z.ZodTypeAny, {
22
23
  tweet: string;
24
+ mediaIds?: string[] | undefined;
23
25
  }, {
24
26
  tweet: string;
27
+ mediaIds?: string[] | undefined;
25
28
  }>;
26
29
  /**
27
30
  * Input schema for posting a tweet reply.
@@ -29,10 +32,23 @@ export declare const TwitterPostTweetSchema: z.ZodObject<{
29
32
  export declare const TwitterPostTweetReplySchema: z.ZodObject<{
30
33
  tweetId: z.ZodString;
31
34
  tweetReply: z.ZodString;
35
+ mediaIds: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
32
36
  }, "strip", z.ZodTypeAny, {
33
37
  tweetId: string;
34
38
  tweetReply: string;
39
+ mediaIds?: string[] | undefined;
35
40
  }, {
36
41
  tweetId: string;
37
42
  tweetReply: string;
43
+ mediaIds?: string[] | undefined;
44
+ }>;
45
+ /**
46
+ * Input schema for uploading media.
47
+ */
48
+ export declare const TwitterUploadMediaSchema: z.ZodObject<{
49
+ filePath: z.ZodString;
50
+ }, "strip", z.ZodTypeAny, {
51
+ filePath: string;
52
+ }, {
53
+ filePath: string;
38
54
  }>;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.TwitterPostTweetReplySchema = exports.TwitterPostTweetSchema = exports.TwitterAccountMentionsSchema = exports.TwitterAccountDetailsSchema = void 0;
3
+ exports.TwitterUploadMediaSchema = exports.TwitterPostTweetReplySchema = exports.TwitterPostTweetSchema = exports.TwitterAccountMentionsSchema = exports.TwitterAccountDetailsSchema = void 0;
4
4
  const zod_1 = require("zod");
5
5
  /**
6
6
  * Input schema for retrieving account details.
@@ -27,6 +27,11 @@ exports.TwitterAccountMentionsSchema = zod_1.z
27
27
  exports.TwitterPostTweetSchema = zod_1.z
28
28
  .object({
29
29
  tweet: zod_1.z.string().max(280, "Tweet must be a maximum of 280 characters."),
30
+ mediaIds: zod_1.z
31
+ .array(zod_1.z.string())
32
+ .max(4, "Maximum of 4 media IDs allowed")
33
+ .optional()
34
+ .describe("Optional array of 1-4 media IDs to attach to the tweet"),
30
35
  })
31
36
  .strip()
32
37
  .describe("Input schema for posting a tweet");
@@ -39,6 +44,23 @@ exports.TwitterPostTweetReplySchema = zod_1.z
39
44
  tweetReply: zod_1.z
40
45
  .string()
41
46
  .max(280, "The reply to the tweet which must be a maximum of 280 characters."),
47
+ mediaIds: zod_1.z
48
+ .array(zod_1.z.string())
49
+ .max(4, "Maximum of 4 media IDs allowed")
50
+ .optional()
51
+ .describe("Optional array of 1-4 media IDs to attach to the tweet"),
42
52
  })
43
53
  .strip()
44
54
  .describe("Input schema for posting a tweet reply");
55
+ /**
56
+ * Input schema for uploading media.
57
+ */
58
+ exports.TwitterUploadMediaSchema = zod_1.z
59
+ .object({
60
+ filePath: zod_1.z
61
+ .string()
62
+ .min(1, "File path is required.")
63
+ .describe("The path to the media file to upload"),
64
+ })
65
+ .strip()
66
+ .describe("Input schema for uploading media to Twitter");
@@ -1,7 +1,7 @@
1
1
  import { z } from "zod";
2
2
  import { ActionProvider } from "../actionProvider";
3
3
  import { Network } from "../../network";
4
- import { TwitterAccountDetailsSchema, TwitterAccountMentionsSchema, TwitterPostTweetSchema, TwitterPostTweetReplySchema } from "./schemas";
4
+ import { TwitterAccountDetailsSchema, TwitterAccountMentionsSchema, TwitterPostTweetSchema, TwitterPostTweetReplySchema, TwitterUploadMediaSchema } from "./schemas";
5
5
  /**
6
6
  * Configuration options for the TwitterActionProvider.
7
7
  */
@@ -65,6 +65,13 @@ export declare class TwitterActionProvider extends ActionProvider {
65
65
  * @returns A JSON string containing the posted reply details or error message
66
66
  */
67
67
  postTweetReply(args: z.infer<typeof TwitterPostTweetReplySchema>): Promise<string>;
68
+ /**
69
+ * Upload media to Twitter.
70
+ *
71
+ * @param args - The arguments containing the file path
72
+ * @returns A JSON string containing the media ID or error message
73
+ */
74
+ uploadMedia(args: z.infer<typeof TwitterUploadMediaSchema>): Promise<string>;
68
75
  /**
69
76
  * Checks if the Twitter action provider supports the given network.
70
77
  * Twitter actions don't depend on blockchain networks, so always return true.
@@ -90,11 +90,19 @@ class TwitterActionProvider extends actionProvider_1.ActionProvider {
90
90
  */
91
91
  async postTweet(args) {
92
92
  try {
93
- const response = await this.getClient().v2.tweet(args.tweet);
93
+ let mediaOptions = {};
94
+ if (args.mediaIds && args.mediaIds.length > 0) {
95
+ // Convert array to tuple format expected by the Twitter API
96
+ const mediaIdsTuple = args.mediaIds;
97
+ mediaOptions = {
98
+ media: { media_ids: mediaIdsTuple },
99
+ };
100
+ }
101
+ const response = await this.getClient().v2.tweet(args.tweet, mediaOptions);
94
102
  return `Successfully posted to Twitter:\n${JSON.stringify(response)}`;
95
103
  }
96
104
  catch (error) {
97
- return `Error posting to Twitter:\n${error}`;
105
+ return `Error posting to Twitter:\n${error} with media ids: ${args.mediaIds}`;
98
106
  }
99
107
  }
100
108
  /**
@@ -105,15 +113,36 @@ class TwitterActionProvider extends actionProvider_1.ActionProvider {
105
113
  */
106
114
  async postTweetReply(args) {
107
115
  try {
108
- const response = await this.getClient().v2.tweet(args.tweetReply, {
116
+ const options = {
109
117
  reply: { in_reply_to_tweet_id: args.tweetId },
110
- });
118
+ };
119
+ if (args.mediaIds && args.mediaIds.length > 0) {
120
+ // Convert array to tuple format expected by the Twitter API
121
+ const mediaIdsTuple = args.mediaIds;
122
+ options.media = { media_ids: mediaIdsTuple };
123
+ }
124
+ const response = await this.getClient().v2.tweet(args.tweetReply, options);
111
125
  return `Successfully posted reply to Twitter:\n${JSON.stringify(response)}`;
112
126
  }
113
127
  catch (error) {
114
128
  return `Error posting reply to Twitter: ${error}`;
115
129
  }
116
130
  }
131
+ /**
132
+ * Upload media to Twitter.
133
+ *
134
+ * @param args - The arguments containing the file path
135
+ * @returns A JSON string containing the media ID or error message
136
+ */
137
+ async uploadMedia(args) {
138
+ try {
139
+ const mediaId = await this.getClient().v1.uploadMedia(args.filePath);
140
+ return `Successfully uploaded media to Twitter: ${mediaId}`;
141
+ }
142
+ catch (error) {
143
+ return `Error uploading media to Twitter: ${error}`;
144
+ }
145
+ }
117
146
  /**
118
147
  * Checks if the Twitter action provider supports the given network.
119
148
  * Twitter actions don't depend on blockchain networks, so always return true.
@@ -183,6 +212,8 @@ __decorate([
183
212
  name: "post_tweet",
184
213
  description: `
185
214
  This tool will post a tweet on Twitter. The tool takes the text of the tweet as input. Tweets can be maximum 280 characters.
215
+ Optionally, up to 4 media items (images, videos) can be attached to the tweet by providing their media IDs in the mediaIds array.
216
+ Media IDs are obtained by first uploading the media using the upload_media action.
186
217
 
187
218
  A successful response will return a message with the API response as a JSON payload:
188
219
  {"data": {"text": "hello, world!", "id": "0123456789012345678", "edit_history_tweet_ids": ["0123456789012345678"]}}
@@ -199,7 +230,10 @@ __decorate([
199
230
  (0, actionDecorator_1.CreateAction)({
200
231
  name: "post_tweet_reply",
201
232
  description: `
202
- This tool will post a tweet on Twitter. The tool takes the text of the tweet as input. Tweets can be maximum 280 characters.
233
+ This tool will post a reply to a tweet on Twitter. The tool takes the text of the reply and the ID of the tweet to reply to as input.
234
+ Replies can be maximum 280 characters.
235
+ Optionally, up to 4 media items (images, videos) can be attached to the reply by providing their media IDs in the mediaIds array.
236
+ Media IDs can be obtained by first uploading the media using the upload_media action.
203
237
 
204
238
  A successful response will return a message with the API response as a JSON payload:
205
239
  {"data": {"text": "hello, world!", "id": "0123456789012345678", "edit_history_tweet_ids": ["0123456789012345678"]}}
@@ -212,6 +246,23 @@ A failure response will return a message with the Twitter API request error:
212
246
  __metadata("design:paramtypes", [void 0]),
213
247
  __metadata("design:returntype", Promise)
214
248
  ], TwitterActionProvider.prototype, "postTweetReply", null);
249
+ __decorate([
250
+ (0, actionDecorator_1.CreateAction)({
251
+ name: "upload_media",
252
+ description: `
253
+ This tool will upload media (images, videos, etc.) to Twitter.
254
+
255
+ A successful response will return a message with the media ID:
256
+ Successfully uploaded media to Twitter: 1234567890
257
+
258
+ A failure response will return a message with the Twitter API request error:
259
+ Error uploading media to Twitter: Invalid file format`,
260
+ schema: schemas_1.TwitterUploadMediaSchema,
261
+ }),
262
+ __metadata("design:type", Function),
263
+ __metadata("design:paramtypes", [void 0]),
264
+ __metadata("design:returntype", Promise)
265
+ ], TwitterActionProvider.prototype, "uploadMedia", null);
215
266
  /**
216
267
  * Factory function to create a new TwitterActionProvider instance.
217
268
  *
@@ -14,16 +14,24 @@ const MOCK_USERNAME = "CDPAgentkit";
14
14
  const MOCK_TWEET = "Hello, world!";
15
15
  const MOCK_TWEET_ID = "0123456789012345678";
16
16
  const MOCK_TWEET_REPLY = "Hello again!";
17
+ const MOCK_MEDIA_ID = "987654321";
18
+ const MOCK_FILE_PATH = "/path/to/image.jpg";
17
19
  describe("TwitterActionProvider", () => {
18
20
  let mockClient;
19
21
  let provider;
22
+ let mockUploadMedia;
20
23
  beforeEach(() => {
21
24
  mockClient = {
22
25
  me: jest.fn(),
23
26
  userMentionTimeline: jest.fn(),
24
27
  tweet: jest.fn(),
25
28
  };
29
+ mockUploadMedia = jest.fn();
26
30
  jest.spyOn(twitter_api_v2_1.TwitterApi.prototype, "v2", "get").mockReturnValue(mockClient);
31
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
32
+ jest.spyOn(twitter_api_v2_1.TwitterApi.prototype, "v1", "get").mockReturnValue({
33
+ uploadMedia: mockUploadMedia,
34
+ });
27
35
  provider = new twitterActionProvider_1.TwitterActionProvider(MOCK_CONFIG);
28
36
  });
29
37
  describe("Constructor", () => {
@@ -145,7 +153,16 @@ describe("TwitterActionProvider", () => {
145
153
  });
146
154
  it("should successfully post a tweet", async () => {
147
155
  const response = await provider.postTweet({ tweet: MOCK_TWEET });
148
- expect(mockClient.tweet).toHaveBeenCalledWith(MOCK_TWEET);
156
+ expect(mockClient.tweet).toHaveBeenCalledWith(MOCK_TWEET, {});
157
+ expect(response).toContain("Successfully posted to Twitter");
158
+ expect(response).toContain(JSON.stringify(mockResponse));
159
+ });
160
+ it("should successfully post a tweet with media", async () => {
161
+ const mediaIds = [MOCK_MEDIA_ID];
162
+ const response = await provider.postTweet({ tweet: MOCK_TWEET, mediaIds });
163
+ expect(mockClient.tweet).toHaveBeenCalledWith(MOCK_TWEET, {
164
+ media: { media_ids: mediaIds },
165
+ });
149
166
  expect(response).toContain("Successfully posted to Twitter");
150
167
  expect(response).toContain(JSON.stringify(mockResponse));
151
168
  });
@@ -153,7 +170,7 @@ describe("TwitterActionProvider", () => {
153
170
  const error = new Error("An error has occurred");
154
171
  mockClient.tweet.mockRejectedValue(error);
155
172
  const response = await provider.postTweet({ tweet: MOCK_TWEET });
156
- expect(mockClient.tweet).toHaveBeenCalledWith(MOCK_TWEET);
173
+ expect(mockClient.tweet).toHaveBeenCalledWith(MOCK_TWEET, {});
157
174
  expect(response).toContain("Error posting to Twitter");
158
175
  expect(response).toContain(error.message);
159
176
  });
@@ -180,6 +197,20 @@ describe("TwitterActionProvider", () => {
180
197
  expect(response).toContain("Successfully posted reply to Twitter");
181
198
  expect(response).toContain(JSON.stringify(mockResponse));
182
199
  });
200
+ it("should successfully post a tweet reply with media", async () => {
201
+ const mediaIds = [MOCK_MEDIA_ID];
202
+ const response = await provider.postTweetReply({
203
+ tweetId: MOCK_TWEET_ID,
204
+ tweetReply: MOCK_TWEET_REPLY,
205
+ mediaIds,
206
+ });
207
+ expect(mockClient.tweet).toHaveBeenCalledWith(MOCK_TWEET_REPLY, {
208
+ reply: { in_reply_to_tweet_id: MOCK_TWEET_ID },
209
+ media: { media_ids: mediaIds },
210
+ });
211
+ expect(response).toContain("Successfully posted reply to Twitter");
212
+ expect(response).toContain(JSON.stringify(mockResponse));
213
+ });
183
214
  it("should handle errors when posting a tweet reply", async () => {
184
215
  const error = new Error("An error has occurred");
185
216
  mockClient.tweet.mockRejectedValue(error);
@@ -194,6 +225,25 @@ describe("TwitterActionProvider", () => {
194
225
  expect(response).toContain(error.message);
195
226
  });
196
227
  });
228
+ describe("Upload Media Action", () => {
229
+ beforeEach(() => {
230
+ mockUploadMedia.mockResolvedValue(MOCK_MEDIA_ID);
231
+ });
232
+ it("should successfully upload media", async () => {
233
+ const response = await provider.uploadMedia({ filePath: MOCK_FILE_PATH });
234
+ expect(mockUploadMedia).toHaveBeenCalledWith(MOCK_FILE_PATH);
235
+ expect(response).toContain("Successfully uploaded media to Twitter");
236
+ expect(response).toContain(MOCK_MEDIA_ID);
237
+ });
238
+ it("should handle errors when uploading media", async () => {
239
+ const error = new Error("Invalid file format");
240
+ mockUploadMedia.mockRejectedValue(error);
241
+ const response = await provider.uploadMedia({ filePath: MOCK_FILE_PATH });
242
+ expect(mockUploadMedia).toHaveBeenCalledWith(MOCK_FILE_PATH);
243
+ expect(response).toContain("Error uploading media to Twitter");
244
+ expect(response).toContain(error.message);
245
+ });
246
+ });
197
247
  describe("Network Support", () => {
198
248
  it("should always return true for network support", () => {
199
249
  expect(provider.supportsNetwork({ protocolFamily: "evm", networkId: "1" })).toBe(true);
@@ -5,6 +5,15 @@ export declare const WETH_ABI: readonly [{
5
5
  readonly outputs: readonly [];
6
6
  readonly stateMutability: "payable";
7
7
  readonly type: "function";
8
+ }, {
9
+ readonly inputs: readonly [{
10
+ readonly name: "wad";
11
+ readonly type: "uint256";
12
+ }];
13
+ readonly name: "withdraw";
14
+ readonly outputs: readonly [];
15
+ readonly stateMutability: "nonpayable";
16
+ readonly type: "function";
8
17
  }, {
9
18
  readonly inputs: readonly [{
10
19
  readonly name: "account";
@@ -10,6 +10,18 @@ exports.WETH_ABI = [
10
10
  stateMutability: "payable",
11
11
  type: "function",
12
12
  },
13
+ {
14
+ inputs: [
15
+ {
16
+ name: "wad",
17
+ type: "uint256",
18
+ },
19
+ ],
20
+ name: "withdraw",
21
+ outputs: [],
22
+ stateMutability: "nonpayable",
23
+ type: "function",
24
+ },
13
25
  {
14
26
  inputs: [
15
27
  {
@@ -6,3 +6,10 @@ export declare const WrapEthSchema: z.ZodObject<{
6
6
  }, {
7
7
  amountToWrap: string;
8
8
  }>;
9
+ export declare const UnwrapEthSchema: z.ZodObject<{
10
+ amountToUnwrap: z.ZodString;
11
+ }, "strip", z.ZodTypeAny, {
12
+ amountToUnwrap: string;
13
+ }, {
14
+ amountToUnwrap: string;
15
+ }>;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.WrapEthSchema = void 0;
3
+ exports.UnwrapEthSchema = exports.WrapEthSchema = void 0;
4
4
  const zod_1 = require("zod");
5
5
  exports.WrapEthSchema = zod_1.z
6
6
  .object({
@@ -8,3 +8,9 @@ exports.WrapEthSchema = zod_1.z
8
8
  })
9
9
  .strip()
10
10
  .describe("Instructions for wrapping ETH to WETH");
11
+ exports.UnwrapEthSchema = zod_1.z
12
+ .object({
13
+ amountToUnwrap: zod_1.z.string().describe("Amount of WETH to unwrap in wei"),
14
+ })
15
+ .strip()
16
+ .describe("Instructions for unwrapping WETH to ETH");
@@ -1,7 +1,7 @@
1
1
  import { z } from "zod";
2
2
  import { ActionProvider } from "../actionProvider";
3
3
  import { Network } from "../../network";
4
- import { WrapEthSchema } from "./schemas";
4
+ import { WrapEthSchema, UnwrapEthSchema } from "./schemas";
5
5
  import { EvmWalletProvider } from "../../wallet-providers";
6
6
  /**
7
7
  * WethActionProvider is an action provider for WETH.
@@ -19,6 +19,14 @@ export declare class WethActionProvider extends ActionProvider<EvmWalletProvider
19
19
  * @returns A message containing the transaction hash.
20
20
  */
21
21
  wrapEth(walletProvider: EvmWalletProvider, args: z.infer<typeof WrapEthSchema>): Promise<string>;
22
+ /**
23
+ * Unwraps WETH to ETH.
24
+ *
25
+ * @param walletProvider - The wallet provider to use for the action.
26
+ * @param args - The input arguments for the action.
27
+ * @returns A message containing the transaction hash.
28
+ */
29
+ unwrapEth(walletProvider: EvmWalletProvider, args: z.infer<typeof UnwrapEthSchema>): Promise<string>;
22
30
  /**
23
31
  * Checks if the Weth action provider supports the given network.
24
32
  *