@coinbase/agentkit 0.0.0-nightly-20250717210412 → 0.0.0-nightly-20250724210458

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 (38) hide show
  1. package/README.md +9 -1
  2. package/dist/action-providers/cdp/index.d.ts +0 -3
  3. package/dist/action-providers/cdp/index.js +0 -3
  4. package/dist/action-providers/farcaster/farcasterActionProvider.js +2 -0
  5. package/dist/action-providers/farcaster/farcasterActionProvider.test.js +55 -0
  6. package/dist/action-providers/farcaster/schemas.d.ts +13 -0
  7. package/dist/action-providers/farcaster/schemas.js +6 -0
  8. package/dist/action-providers/twitter/schemas.d.ts +16 -0
  9. package/dist/action-providers/twitter/schemas.js +23 -1
  10. package/dist/action-providers/twitter/twitterActionProvider.d.ts +8 -1
  11. package/dist/action-providers/twitter/twitterActionProvider.js +56 -5
  12. package/dist/action-providers/twitter/twitterActionProvider.test.js +52 -2
  13. package/dist/action-providers/weth/constants.d.ts +9 -0
  14. package/dist/action-providers/weth/constants.js +12 -0
  15. package/dist/action-providers/weth/schemas.d.ts +7 -0
  16. package/dist/action-providers/weth/schemas.js +7 -1
  17. package/dist/action-providers/weth/wethActionProvider.d.ts +9 -1
  18. package/dist/action-providers/weth/wethActionProvider.js +50 -1
  19. package/dist/action-providers/weth/wethActionProvider.test.js +60 -0
  20. package/dist/action-providers/x402/schemas.d.ts +9 -45
  21. package/dist/action-providers/x402/schemas.js +53 -17
  22. package/dist/action-providers/x402/x402ActionProvider.js +24 -19
  23. package/dist/action-providers/x402/x402ActionProvider.test.js +4 -35
  24. package/dist/wallet-providers/cdpEvmWalletProvider.d.ts +4 -4
  25. package/dist/wallet-providers/cdpEvmWalletProvider.js +4 -4
  26. package/dist/wallet-providers/cdpShared.d.ts +2 -2
  27. package/dist/wallet-providers/cdpSmartWalletProvider.d.ts +2 -2
  28. package/dist/wallet-providers/cdpSmartWalletProvider.js +11 -4
  29. package/dist/wallet-providers/cdpSmartWalletProvider.test.js +31 -11
  30. package/dist/wallet-providers/legacyCdpWalletProvider.d.ts +1 -1
  31. package/dist/wallet-providers/legacyCdpWalletProvider.js +1 -1
  32. package/package.json +2 -2
  33. package/dist/action-providers/cdp/cdpEvmWalletActionProvider.d.ts +0 -17
  34. package/dist/action-providers/cdp/cdpEvmWalletActionProvider.js +0 -20
  35. package/dist/action-providers/cdp/cdpSmartWalletActionProvider.d.ts +0 -17
  36. package/dist/action-providers/cdp/cdpSmartWalletActionProvider.js +0 -20
  37. package/dist/action-providers/cdp/cdpSolanaWalletActionProvider.d.ts +0 -17
  38. package/dist/action-providers/cdp/cdpSolanaWalletActionProvider.js +0 -20
package/README.md CHANGED
@@ -261,7 +261,7 @@ const agent = createReactAgent({
261
261
  </tr>
262
262
  <tr>
263
263
  <td width="200"><code>post_cast</code></td>
264
- <td width="768">Creates a new cast (message) on Farcaster with up to 280 characters.</td>
264
+ <td width="768">Creates a new cast (message) on Farcaster with up to 280 characters and support for up to 2 embedded URLs.</td>
265
265
  </tr>
266
266
  </table>
267
267
  </details>
@@ -362,6 +362,10 @@ const agent = createReactAgent({
362
362
  <td width="200"><code>post_tweet_reply</code></td>
363
363
  <td width="768">Creates a reply to an existing tweet using the tweet's unique identifier.</td>
364
364
  </tr>
365
+ <tr>
366
+ <td width="200"><code>upload_media</code></td>
367
+ <td width="768">Uploads media (images, videos) to Twitter that can be attached to tweets.</td>
368
+ </tr>
365
369
  </table>
366
370
  </details>
367
371
  <details>
@@ -384,6 +388,10 @@ const agent = createReactAgent({
384
388
  <td width="200"><code>wrap_eth</code></td>
385
389
  <td width="768">Converts native ETH to Wrapped ETH (WETH) on Base Sepolia or Base Mainnet.</td>
386
390
  </tr>
391
+ <tr>
392
+ <td width="200"><code>unwrap_eth</code></td>
393
+ <td width="768">Converts Wrapped ETH (WETH) to Native ETH on Base Sepolia or Base Mainnet.</td>
394
+ </tr>
387
395
  </table>
388
396
  </details>
389
397
  <details>
@@ -1,5 +1,2 @@
1
1
  export * from "./schemas";
2
2
  export * from "./cdpApiActionProvider";
3
- export * from "./cdpEvmWalletActionProvider";
4
- export * from "./cdpSolanaWalletActionProvider";
5
- export * from "./cdpSmartWalletActionProvider";
@@ -16,6 +16,3 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
17
  __exportStar(require("./schemas"), exports);
18
18
  __exportStar(require("./cdpApiActionProvider"), exports);
19
- __exportStar(require("./cdpEvmWalletActionProvider"), exports);
20
- __exportStar(require("./cdpSolanaWalletActionProvider"), exports);
21
- __exportStar(require("./cdpSmartWalletActionProvider"), exports);
@@ -90,6 +90,7 @@ class FarcasterActionProvider extends actionProvider_1.ActionProvider {
90
90
  body: JSON.stringify({
91
91
  signer_uuid: this.signerUuid,
92
92
  text: args.castText,
93
+ embeds: args.embeds,
93
94
  }),
94
95
  });
95
96
  const data = await response.json();
@@ -125,6 +126,7 @@ __decorate([
125
126
  name: "post_cast",
126
127
  description: `
127
128
  This tool will post a cast to Farcaster. The tool takes the text of the cast as input. Casts can be maximum 280 characters.
129
+ Optionally, up to 2 embeds (links to websites or mini apps) can be attached to the cast by providing an array of URLs in the embeds parameter.
128
130
 
129
131
  A successful response will return a message with the API response as a JSON payload:
130
132
  {}
@@ -23,6 +23,35 @@ describe("Farcaster Action Provider Input Schemas", () => {
23
23
  expect(result.success).toBe(true);
24
24
  expect(result.data).toEqual(validInput);
25
25
  });
26
+ it("should successfully parse valid cast text with embeds", () => {
27
+ const validInput = {
28
+ castText: "Hello, Farcaster!",
29
+ embeds: [{ url: "https://example.com" }],
30
+ };
31
+ const result = schemas_1.FarcasterPostCastSchema.safeParse(validInput);
32
+ expect(result.success).toBe(true);
33
+ expect(result.data).toEqual(validInput);
34
+ });
35
+ it("should fail when embed URL is invalid", () => {
36
+ const invalidInput = {
37
+ castText: "Hello, Farcaster!",
38
+ embeds: [{ url: "invalid-url" }],
39
+ };
40
+ const result = schemas_1.FarcasterPostCastSchema.safeParse(invalidInput);
41
+ expect(result.success).toBe(false);
42
+ });
43
+ it("should fail when there are more than 2 embeds", () => {
44
+ const invalidInput = {
45
+ castText: "Hello, Farcaster!",
46
+ embeds: [
47
+ { url: "https://example1.com" },
48
+ { url: "https://example2.com" },
49
+ { url: "https://example3.com" },
50
+ ],
51
+ };
52
+ const result = schemas_1.FarcasterPostCastSchema.safeParse(invalidInput);
53
+ expect(result.success).toBe(false);
54
+ });
26
55
  it("should fail parsing cast text over 280 characters", () => {
27
56
  const invalidInput = {
28
57
  castText: "a".repeat(281),
@@ -100,6 +129,32 @@ describe("Farcaster Action Provider", () => {
100
129
  body: JSON.stringify({
101
130
  signer_uuid: mockConfig.signerUuid,
102
131
  text: args.castText,
132
+ embeds: undefined,
133
+ }),
134
+ });
135
+ expect(mockFetch).toHaveBeenCalledTimes(1);
136
+ expect(result).toContain("Successfully posted cast to Farcaster");
137
+ expect(result).toContain(JSON.stringify(mockCastResponse));
138
+ });
139
+ it("should successfully post a cast with embeds", async () => {
140
+ mockFetch.mockResolvedValueOnce({
141
+ json: () => Promise.resolve(mockCastResponse),
142
+ });
143
+ const args = {
144
+ castText: "Hello, Farcaster!",
145
+ embeds: [{ url: "https://example.com" }],
146
+ };
147
+ const result = await actionProvider.postCast(args);
148
+ expect(mockFetch).toHaveBeenCalledWith("https://api.neynar.com/v2/farcaster/cast", {
149
+ method: "POST",
150
+ headers: {
151
+ api_key: mockConfig.neynarApiKey,
152
+ "Content-Type": "application/json",
153
+ },
154
+ body: JSON.stringify({
155
+ signer_uuid: mockConfig.signerUuid,
156
+ text: args.castText,
157
+ embeds: args.embeds,
103
158
  }),
104
159
  });
105
160
  expect(mockFetch).toHaveBeenCalledTimes(1);
@@ -8,8 +8,21 @@ export declare const FarcasterAccountDetailsSchema: z.ZodObject<{}, "strip", z.Z
8
8
  */
9
9
  export declare const FarcasterPostCastSchema: z.ZodObject<{
10
10
  castText: z.ZodString;
11
+ embeds: z.ZodOptional<z.ZodArray<z.ZodObject<{
12
+ url: z.ZodString;
13
+ }, "strip", z.ZodTypeAny, {
14
+ url: string;
15
+ }, {
16
+ url: string;
17
+ }>, "many">>;
11
18
  }, "strip", z.ZodTypeAny, {
12
19
  castText: string;
20
+ embeds?: {
21
+ url: string;
22
+ }[] | undefined;
13
23
  }, {
14
24
  castText: string;
25
+ embeds?: {
26
+ url: string;
27
+ }[] | undefined;
15
28
  }>;
@@ -15,6 +15,12 @@ exports.FarcasterAccountDetailsSchema = zod_1.z
15
15
  exports.FarcasterPostCastSchema = zod_1.z
16
16
  .object({
17
17
  castText: zod_1.z.string().max(280, "Cast text must be a maximum of 280 characters."),
18
+ embeds: zod_1.z
19
+ .array(zod_1.z.object({
20
+ url: zod_1.z.string().url("Embed URL must be a valid URL"),
21
+ }))
22
+ .max(2, "Maximum of 2 embeds allowed")
23
+ .optional(),
18
24
  })
19
25
  .strip()
20
26
  .describe("Input schema for posting a text-based cast");
@@ -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
  *
@@ -58,6 +58,30 @@ class WethActionProvider extends actionProvider_1.ActionProvider {
58
58
  return `Error wrapping ETH: ${error}`;
59
59
  }
60
60
  }
61
+ /**
62
+ * Unwraps WETH to ETH.
63
+ *
64
+ * @param walletProvider - The wallet provider to use for the action.
65
+ * @param args - The input arguments for the action.
66
+ * @returns A message containing the transaction hash.
67
+ */
68
+ async unwrapEth(walletProvider, args) {
69
+ try {
70
+ const hash = await walletProvider.sendTransaction({
71
+ to: constants_1.WETH_ADDRESS,
72
+ data: (0, viem_1.encodeFunctionData)({
73
+ abi: constants_1.WETH_ABI,
74
+ functionName: "withdraw",
75
+ args: [BigInt(args.amountToUnwrap)],
76
+ }),
77
+ });
78
+ await walletProvider.waitForTransactionReceipt(hash);
79
+ return `Unwrapped WETH with transaction hash: ${hash}`;
80
+ }
81
+ catch (error) {
82
+ return `Error unwrapping WETH: ${error}`;
83
+ }
84
+ }
61
85
  }
62
86
  exports.WethActionProvider = WethActionProvider;
63
87
  __decorate([
@@ -74,7 +98,7 @@ Important notes:
74
98
  - The amount is a string and cannot have any decimal points, since the unit of measurement is wei.
75
99
  - Make sure to use the exact amount provided, and if there's any doubt, check by getting more information before continuing with the action.
76
100
  - 1 wei = 0.000000000000000001 WETH
77
- - Minimum purchase amount is 100000000000000 wei (0.0000001 WETH)
101
+ - Minimum purchase amount is 100000000000 wei (0.0000001 WETH)
78
102
  - Only supported on the following networks:
79
103
  - Base Sepolia (ie, 'base-sepolia')
80
104
  - Base Mainnet (ie, 'base', 'base-mainnet')
@@ -85,5 +109,30 @@ Important notes:
85
109
  __metadata("design:paramtypes", [wallet_providers_1.EvmWalletProvider, void 0]),
86
110
  __metadata("design:returntype", Promise)
87
111
  ], WethActionProvider.prototype, "wrapEth", null);
112
+ __decorate([
113
+ (0, actionDecorator_1.CreateAction)({
114
+ name: "unwrap_eth",
115
+ description: `
116
+ This tool can only be used to unwrap WETH to ETH.
117
+ Do not use this tool for any other purpose, or trading other assets.
118
+
119
+ Inputs:
120
+ - Amount of WETH to unwrap.
121
+
122
+ Important notes:
123
+ - The amount is a string and cannot have any decimal points, since the unit of measurement is wei.
124
+ - Make sure to use the exact amount provided, and if there's any doubt, check by getting more information before continuing with the action.
125
+ - 1 wei = 0.000000000000000001 WETH
126
+ - Minimum unwrap amount is 100000000000 wei (0.0000001 WETH)
127
+ - Only supported on the following networks:
128
+ - Base Sepolia (ie, 'base-sepolia')
129
+ - Base Mainnet (ie, 'base', 'base-mainnet')
130
+ `,
131
+ schema: schemas_1.UnwrapEthSchema,
132
+ }),
133
+ __metadata("design:type", Function),
134
+ __metadata("design:paramtypes", [wallet_providers_1.EvmWalletProvider, void 0]),
135
+ __metadata("design:returntype", Promise)
136
+ ], WethActionProvider.prototype, "unwrapEth", null);
88
137
  const wethActionProvider = () => new WethActionProvider();
89
138
  exports.wethActionProvider = wethActionProvider;