create-message-kit 1.2.13 → 1.2.15

Sign up to get free protection for your applications and to get access to all the features.
Files changed (90) hide show
  1. package/index.js +15 -15
  2. package/package.json +1 -1
  3. package/templates/coinbase-agent/.cursorrules +290 -0
  4. package/templates/coinbase-agent/.env.example +4 -0
  5. package/templates/coinbase-agent/.yarnrc.yml +9 -0
  6. package/templates/coinbase-agent/package.json +22 -0
  7. package/templates/coinbase-agent/src/index.ts +31 -0
  8. package/templates/coinbase-agent/src/plugins/learnweb3.ts +96 -0
  9. package/templates/coinbase-agent/src/plugins/redis.ts +15 -0
  10. package/templates/coinbase-agent/src/prompt.ts +64 -0
  11. package/templates/coinbase-agent/src/skills/drip.ts +83 -0
  12. package/templates/coinbase-agent/src/skills/mint.ts +99 -0
  13. package/templates/coinbase-agent/src/skills/pay.ts +35 -0
  14. package/templates/coinbase-agent/src/skills/swap.ts +52 -0
  15. package/templates/ens/.env.example +1 -0
  16. package/templates/ens/.yarnrc.yml +5 -0
  17. package/templates/ens/src/index.ts +1 -1
  18. package/templates/ens/src/prompt.ts +1 -14
  19. package/templates/faucet/.cursorrules +290 -0
  20. package/templates/faucet/.env.example +5 -0
  21. package/templates/faucet/.yarnrc.yml +9 -0
  22. package/templates/faucet/package.json +22 -0
  23. package/templates/faucet/src/index.ts +39 -0
  24. package/templates/faucet/src/plugins/learnweb3.ts +96 -0
  25. package/templates/faucet/src/plugins/redis.ts +15 -0
  26. package/templates/faucet/src/prompt.ts +11 -0
  27. package/templates/faucet/src/skills/faucet.ts +140 -0
  28. package/templates/gated-group/.cursorrules +290 -0
  29. package/templates/gated-group/.env.example +3 -0
  30. package/templates/gated-group/.yarnrc.yml +9 -0
  31. package/templates/gated-group/package.json +23 -0
  32. package/templates/gated-group/src/index.ts +17 -0
  33. package/templates/gated-group/src/plugins/alchemy.ts +27 -0
  34. package/templates/gated-group/src/plugins/xmtp.ts +137 -0
  35. package/templates/gated-group/src/prompt.ts +11 -0
  36. package/templates/gated-group/src/skills/gated.ts +88 -0
  37. package/templates/gm/.cursorrules +290 -0
  38. package/templates/gm/.env.example +2 -0
  39. package/templates/gm/.yarnrc.yml +9 -0
  40. package/templates/gm/package.json +20 -0
  41. package/templates/gm/src/index.ts +8 -0
  42. package/templates/playground/.cursorrules +290 -0
  43. package/templates/playground/.env.example +6 -0
  44. package/templates/playground/.yarnrc.yml +9 -0
  45. package/templates/playground/package.json +26 -0
  46. package/templates/playground/src/index.ts +40 -0
  47. package/templates/playground/src/plugins/alchemy.ts +27 -0
  48. package/templates/playground/src/plugins/minilog.ts +26 -0
  49. package/templates/playground/src/plugins/usdc.ts +99 -0
  50. package/templates/playground/src/plugins/xmtp.ts +137 -0
  51. package/templates/playground/src/prompt.ts +11 -0
  52. package/templates/playground/src/skills/broadcast.ts +39 -0
  53. package/templates/playground/src/skills/cash.ts +122 -0
  54. package/templates/playground/src/skills/cryptoPrice.ts +63 -0
  55. package/templates/playground/src/skills/dalle.ts +61 -0
  56. package/templates/playground/src/skills/gated.ts +88 -0
  57. package/templates/playground/src/skills/search.ts +159 -0
  58. package/templates/playground/src/skills/todo.ts +79 -0
  59. package/templates/playground/src/skills/token.ts +57 -0
  60. package/templates/playground/src/skills/web.ts +83 -0
  61. package/templates/playground/src/skills/wordle.ts +96 -0
  62. package/templates/simple/.env.example +1 -0
  63. package/templates/simple/.yarnrc.yml +5 -0
  64. package/templates/simple/src/index.ts +1 -1
  65. package/templates/simple/src/prompt.ts +2 -0
  66. package/templates/thegeneralstore/.cursorrules +290 -0
  67. package/templates/thegeneralstore/.env.example +9 -0
  68. package/templates/thegeneralstore/.yarnrc.yml +9 -0
  69. package/templates/thegeneralstore/package.json +24 -0
  70. package/templates/thegeneralstore/src/data/db.json +812 -0
  71. package/templates/thegeneralstore/src/index.ts +37 -0
  72. package/templates/thegeneralstore/src/plugins/learnweb3.ts +96 -0
  73. package/templates/thegeneralstore/src/plugins/lowdb.ts +11 -0
  74. package/templates/thegeneralstore/src/plugins/notion.ts +89 -0
  75. package/templates/thegeneralstore/src/plugins/redis.ts +15 -0
  76. package/templates/thegeneralstore/src/prompt.md +51 -0
  77. package/templates/thegeneralstore/src/prompt.ts +3 -0
  78. package/templates/thegeneralstore/src/skills/faucet.ts +114 -0
  79. package/templates/thegeneralstore/src/skills/notion.ts +17 -0
  80. package/templates/thegeneralstore/src/skills/poap.ts +48 -0
  81. package/templates/thegeneralstore/src/skills.ts +37 -0
  82. package/templates/toss/.cursorrules +290 -0
  83. package/templates/toss/.env.example +7 -0
  84. package/templates/toss/.yarnrc.yml +9 -0
  85. package/templates/toss/package.json +21 -0
  86. package/templates/toss/src/index.ts +33 -0
  87. package/templates/toss/src/plugins/helpers.ts +185 -0
  88. package/templates/toss/src/plugins/redis.ts +78 -0
  89. package/templates/toss/src/prompt.ts +54 -0
  90. package/templates/toss/src/skills/toss.ts +489 -0
@@ -0,0 +1,6 @@
1
+ OPENAI_API_KEY= # the API key for OpenAI
2
+ TEST_ENCRYPTION_KEY= # a different private key for encryption
3
+ KEY= # the private key of the wallet
4
+ RESEND_API_KEY= # the API key for Resend
5
+ FRAMEDL_API_KEY= # the API key for Framedl
6
+ ALCHEMY_SDK= # the API key for Alchemy
@@ -0,0 +1,9 @@
1
+ compressionLevel: mixed
2
+
3
+ enableGlobalCache: false
4
+
5
+ enableTelemetry: false
6
+
7
+ nodeLinker: node-modules
8
+
9
+ yarnPath: .yarn/releases/yarn-4.5.1.cjs
@@ -0,0 +1,26 @@
1
+ {
2
+ "name": "playground",
3
+ "private": true,
4
+ "type": "module",
5
+ "scripts": {
6
+ "build": "tsc",
7
+ "dev": "tsc -w & sleep 1 && NODE_NO_WARNINGS=1 node --watch dist/index.js",
8
+ "start": "node dist/index.js"
9
+ },
10
+ "dependencies": {
11
+ "@xmtp/message-kit": "workspace:*",
12
+ "alchemy-sdk": "^3.0.0",
13
+ "ethers": "^6.0.0",
14
+ "express": "^4.19.2",
15
+ "node-fetch": "^3.3.2",
16
+ "resend": "^4.0.1"
17
+ },
18
+ "devDependencies": {
19
+ "@types/express": "^4.17.20",
20
+ "@types/node": "^20.14.2",
21
+ "typescript": "^5.4.5"
22
+ },
23
+ "engines": {
24
+ "node": ">=20"
25
+ }
26
+ }
@@ -0,0 +1,40 @@
1
+ import {
2
+ run,
3
+ agentReply,
4
+ replaceVariables,
5
+ XMTPContext,
6
+ Agent,
7
+ xmtpClient,
8
+ } from "@xmtp/message-kit";
9
+ import fs from "fs";
10
+ import { systemPrompt } from "./prompt.js";
11
+ import { web } from "./skills/web.js";
12
+ import { cryptoPrice } from "./skills/cryptoPrice.js";
13
+ import { search } from "./skills/search.js";
14
+ export const agent: Agent = {
15
+ name: "Playground Agent",
16
+ tag: "@bot",
17
+ description: "A playground agent with a lot of skills.",
18
+ skills: [...web, ...cryptoPrice, ...search],
19
+ };
20
+
21
+ // [!region gated]
22
+ const { client } = await xmtpClient({
23
+ hideInitLogMessage: true,
24
+ });
25
+
26
+ run(
27
+ async (context: XMTPContext) => {
28
+ const {
29
+ message: { sender },
30
+ agent,
31
+ } = context;
32
+
33
+ let prompt = await replaceVariables(systemPrompt, sender.address, agent);
34
+
35
+ //This is only used for to update the docs.
36
+ fs.writeFileSync("example_prompt.md", prompt);
37
+ await agentReply(context, prompt);
38
+ },
39
+ { agent, experimental: true },
40
+ );
@@ -0,0 +1,27 @@
1
+ import { Alchemy, Network } from "alchemy-sdk";
2
+
3
+ const settings = {
4
+ apiKey: process.env.ALCHEMY_API_KEY, // Replace with your Alchemy API key
5
+ network: Network.BASE_MAINNET, // Use the appropriate network
6
+ };
7
+
8
+ export async function checkNft(
9
+ walletAddress: string,
10
+ collectionSlug: string,
11
+ ): Promise<boolean> {
12
+ const alchemy = new Alchemy(settings);
13
+ try {
14
+ const nfts = await alchemy.nft.getNftsForOwner(walletAddress);
15
+
16
+ const ownsNft = nfts.ownedNfts.some(
17
+ (nft: any) =>
18
+ nft.contract.name.toLowerCase() === collectionSlug.toLowerCase(),
19
+ );
20
+ console.log("is the nft owned: ", ownsNft);
21
+ return ownsNft as boolean;
22
+ } catch (error) {
23
+ console.error("Error fetching NFTs from Alchemy:", error);
24
+ }
25
+
26
+ return false;
27
+ }
@@ -0,0 +1,26 @@
1
+ import fetch from "node-fetch";
2
+
3
+ export async function sendLog(title: string, description: string) {
4
+ const response = await fetch("https://api.minilog.dev/v1/logs/testlog", {
5
+ method: "POST",
6
+ headers: {
7
+ "Content-Type": "application/json",
8
+ Authorization: "Bearer pthsm38sccpux5acriqish7isz5inet7q73ef7br",
9
+ },
10
+ body: JSON.stringify({
11
+ application: "myapp-1",
12
+ severity: "DEBUG",
13
+ data: title,
14
+ metadata: {
15
+ title,
16
+ description,
17
+ },
18
+ }),
19
+ });
20
+ console.log(response);
21
+ if (!response.ok) {
22
+ console.error("Failed to send log:", response.statusText);
23
+ } else {
24
+ console.log("Log sent successfully");
25
+ }
26
+ }
@@ -0,0 +1,99 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+ import { generatePrivateKey } from "viem/accounts";
4
+ import { ethers } from "ethers";
5
+
6
+ // Define the Base network RPC URL
7
+ const baseRpcUrl = "https://mainnet.base.org";
8
+
9
+ // Create a provider
10
+ const provider = new ethers.JsonRpcProvider(baseRpcUrl);
11
+
12
+ // USDC contract address on Base (replace with actual address)
13
+ const usdcAddress = "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913"; // Replace with the correct USDC contract address
14
+
15
+ // ERC-20 ABI (balanceOf and transfer functions)
16
+ const erc20Abi = [
17
+ "function balanceOf(address owner) view returns (uint256)",
18
+ "function transfer(address to, uint256 amount) returns (bool)",
19
+ ];
20
+
21
+ export class USDCWallet {
22
+ walletDir: string;
23
+ senderAddress: string;
24
+ privateKey: string;
25
+ agentAddress: string;
26
+ usdcContract: ethers.Contract;
27
+ wallet: ethers.Wallet;
28
+
29
+ constructor(senderAddress: string) {
30
+ this.senderAddress = senderAddress;
31
+ this.walletDir = path.join(process.cwd(), `./.data/usdcwallets`);
32
+ if (!fs.existsSync(this.walletDir)) {
33
+ fs.mkdirSync(this.walletDir, { recursive: true });
34
+ console.warn("USDC wallet created and saved successfully.");
35
+ }
36
+
37
+ const walletFilePath = path.join(this.walletDir, `${senderAddress}.usdc`);
38
+
39
+ if (fs.existsSync(walletFilePath)) {
40
+ const walletData = fs.readFileSync(walletFilePath, "utf8");
41
+ this.privateKey = walletData.match(/KEY=(.+)/)?.[1]?.trim() ?? "";
42
+ } else {
43
+ this.privateKey = generatePrivateKey();
44
+ let usdcWallet = new ethers.Wallet(this.privateKey, provider);
45
+ const walletData = `KEY=${this.privateKey}\nADDRESS=${usdcWallet.address}`;
46
+ fs.writeFileSync(walletFilePath, walletData);
47
+ }
48
+
49
+ // Initialize wallet and USDC contract
50
+ this.wallet = new ethers.Wallet(this.privateKey, provider);
51
+ this.agentAddress = this.wallet.address;
52
+ this.usdcContract = new ethers.Contract(usdcAddress, erc20Abi, this.wallet);
53
+ }
54
+
55
+ async checkBalances(): Promise<{ usdc: number; eth: number }> {
56
+ try {
57
+ // Check USDC balance
58
+ console.log(this.agentAddress);
59
+ const usdcBalance = await this.usdcContract.balanceOf(this.agentAddress);
60
+ const formattedUsdcBalance = ethers.formatUnits(usdcBalance, 6); // USDC has 6 decimals
61
+ console.warn(`USDC Balance: ${formattedUsdcBalance}`);
62
+
63
+ // Check ETH balance
64
+ const ethBalance = await provider.getBalance(this.agentAddress);
65
+ const formattedEthBalance = ethers.formatUnits(ethBalance, 18);
66
+ console.warn(`ETH Balance: ${formattedEthBalance}`);
67
+
68
+ return {
69
+ usdc: parseFloat(formattedUsdcBalance),
70
+ eth: parseFloat(formattedEthBalance),
71
+ };
72
+ } catch (error) {
73
+ console.error("Error fetching balances:", error);
74
+ return { usdc: 0, eth: 0 };
75
+ }
76
+ }
77
+ async transferUsdc(to: string, amount: number) {
78
+ if (!ethers.isAddress(to)) {
79
+ throw new Error("Invalid recipient address");
80
+ } else if (typeof amount !== "number" || amount <= 0) {
81
+ throw new Error("Invalid transfer amount");
82
+ }
83
+ try {
84
+ const amountInWei = ethers.parseUnits(amount.toString(), 6); // USDC has 6 decimals
85
+ const adminAgent = new USDCWallet(to);
86
+ const tx = await this.usdcContract.transfer(
87
+ adminAgent.agentAddress,
88
+ amountInWei,
89
+ );
90
+ const receipt = await tx.wait();
91
+ if (receipt.status !== 1) {
92
+ throw new Error("Transaction failed or was reverted");
93
+ }
94
+ } catch (error) {
95
+ console.warn(`Transferred ${amount} USDC to ${to}.`);
96
+ throw error;
97
+ }
98
+ }
99
+ }
@@ -0,0 +1,137 @@
1
+ import { isOnXMTP, V3Client, V2Client } from "@xmtp/message-kit";
2
+
3
+ export async function createGroup(
4
+ client: V3Client,
5
+ senderAddress: string,
6
+ clientAddress: string,
7
+ ) {
8
+ try {
9
+ let senderInboxId = "";
10
+ await client.conversations.sync();
11
+ const group = await client?.conversations.newGroup([
12
+ senderAddress,
13
+ clientAddress,
14
+ ]);
15
+ console.log("Group created", group?.id);
16
+ const members = await group.members();
17
+ const senderMember = members.find((member) =>
18
+ member.accountAddresses.includes(senderAddress.toLowerCase()),
19
+ );
20
+ if (senderMember) {
21
+ senderInboxId = senderMember.inboxId;
22
+ console.log("Sender's inboxId:", senderInboxId);
23
+ } else {
24
+ console.log("Sender not found in members list");
25
+ }
26
+ await group.addSuperAdmin(senderInboxId);
27
+ console.log(
28
+ "Sender is superAdmin",
29
+ await group.isSuperAdmin(senderInboxId),
30
+ );
31
+ await group.send(`Welcome to the new group!`);
32
+ await group.send(`You are now the admin of this group as well as the bot`);
33
+ return group;
34
+ } catch (error) {
35
+ console.log("Error creating group", error);
36
+ return null;
37
+ }
38
+ }
39
+
40
+ export async function removeFromGroup(
41
+ groupId: string,
42
+ client: V3Client,
43
+ v2client: V2Client,
44
+ senderAddress: string,
45
+ ): Promise<{ code: number; message: string }> {
46
+ try {
47
+ let lowerAddress = senderAddress.toLowerCase();
48
+ const { v2, v3 } = await isOnXMTP(client, v2client, lowerAddress);
49
+ console.warn("Checking if on XMTP: v2", v2, "v3", v3);
50
+ if (!v3)
51
+ return {
52
+ code: 400,
53
+ message: "You don't seem to have a v3 identity ",
54
+ };
55
+ const conversation =
56
+ await client.conversations.getConversationById(groupId);
57
+ console.warn("removing from group", conversation?.id);
58
+ await conversation?.sync();
59
+ await conversation?.removeMembers([lowerAddress]);
60
+ console.warn("Removed member from group");
61
+ await conversation?.sync();
62
+ const members = await conversation?.members();
63
+ console.warn("Number of members", members?.length);
64
+
65
+ let wasRemoved = true;
66
+ if (members) {
67
+ for (const member of members) {
68
+ let lowerMemberAddress = member.accountAddresses[0].toLowerCase();
69
+ if (lowerMemberAddress === lowerAddress) {
70
+ wasRemoved = false;
71
+ break;
72
+ }
73
+ }
74
+ }
75
+ return {
76
+ code: wasRemoved ? 200 : 400,
77
+ message: wasRemoved
78
+ ? "You have been removed from the group"
79
+ : "Failed to remove from group",
80
+ };
81
+ } catch (error) {
82
+ console.log("Error removing from group", error);
83
+ return {
84
+ code: 400,
85
+ message: "Failed to remove from group",
86
+ };
87
+ }
88
+ }
89
+ export async function addToGroup(
90
+ groupId: string,
91
+ client: V3Client,
92
+ address: string,
93
+ asAdmin: boolean = false,
94
+ ): Promise<{ code: number; message: string }> {
95
+ try {
96
+ let lowerAddress = address.toLowerCase();
97
+ const { v2, v3 } = await isOnXMTP(client, null, lowerAddress);
98
+ if (!v3)
99
+ return {
100
+ code: 400,
101
+ message: "You don't seem to have a v3 identity ",
102
+ };
103
+ const group = await client.conversations.getConversationById(groupId);
104
+ console.warn("Adding to group", group?.id);
105
+ await group?.sync();
106
+ await group?.addMembers([lowerAddress]);
107
+ console.warn("Added member to group");
108
+ await group?.sync();
109
+ if (asAdmin) {
110
+ await group?.addSuperAdmin(lowerAddress);
111
+ }
112
+ const members = await group?.members();
113
+ console.warn("Number of members", members?.length);
114
+
115
+ if (members) {
116
+ for (const member of members) {
117
+ let lowerMemberAddress = member.accountAddresses[0].toLowerCase();
118
+ if (lowerMemberAddress === lowerAddress) {
119
+ console.warn("Member exists", lowerMemberAddress);
120
+ return {
121
+ code: 200,
122
+ message: "You have been added to the group",
123
+ };
124
+ }
125
+ }
126
+ }
127
+ return {
128
+ code: 400,
129
+ message: "Failed to add to group",
130
+ };
131
+ } catch (error) {
132
+ return {
133
+ code: 400,
134
+ message: "Failed to add to group",
135
+ };
136
+ }
137
+ }
@@ -0,0 +1,11 @@
1
+ export const systemPrompt = `
2
+ Your are a helpful and playful community agent called {agent_name} that lives inside a messaging app called Converse.
3
+
4
+ {rules}
5
+
6
+ {user_context}
7
+
8
+ {skills}
9
+
10
+ {issues}
11
+ `;
@@ -0,0 +1,39 @@
1
+ import { XMTPContext, Skill } from "@xmtp/message-kit";
2
+
3
+ export const broadcast: Skill[] = [
4
+ {
5
+ skill: "send",
6
+ adminOnly: true,
7
+ handler: handler,
8
+ examples: ["/send Hello everyone, the event is starting now!"],
9
+ description: "Send updates to all subscribers.",
10
+ params: {
11
+ message: {
12
+ type: "prompt",
13
+ },
14
+ },
15
+ },
16
+ ];
17
+
18
+ async function handler(context: XMTPContext) {
19
+ const {
20
+ message: {
21
+ content: {
22
+ params: { message },
23
+ },
24
+ },
25
+ } = context;
26
+
27
+ const fakeSubscribers = ["0x93E2fc3e99dFb1238eB9e0eF2580EFC5809C7204"];
28
+ await context.send("This is how your message will look like:");
29
+ await context.send(message);
30
+ const emailResponse = await context.awaitResponse(
31
+ "Are you sure you want to send this broadcast?\nType 'yes' to confirm.",
32
+ ["yes", "no"],
33
+ );
34
+ if (emailResponse === "yes") {
35
+ await context.send("Sending broadcast...");
36
+ await context.sendTo(message, fakeSubscribers);
37
+ await context.send("Broadcast sent!");
38
+ }
39
+ }
@@ -0,0 +1,122 @@
1
+ import { XMTPContext } from "@xmtp/message-kit";
2
+ import type { Skill } from "@xmtp/message-kit";
3
+ import { USDCWallet } from "../plugins/usdc.js";
4
+
5
+ export const cash: Skill[] = [
6
+ {
7
+ skill: "balance",
8
+ handler: balanceHandler,
9
+ examples: ["/balance"],
10
+ description: "Check your balance.",
11
+ },
12
+ {
13
+ skill: "fund",
14
+ handler: fundHandler,
15
+ examples: ["/fund 1", "/fund 10"],
16
+ params: {
17
+ amount: {
18
+ type: "number",
19
+ default: "",
20
+ },
21
+ },
22
+ description: "Fund your wallet. Returns a url to fund your wallet.",
23
+ },
24
+ {
25
+ skill: "transfer",
26
+ handler: transferHandler,
27
+ examples: ["/transfer 0x40f08f0f853d1c42c61815652b7ccd5a50f0be09 1"],
28
+ params: {
29
+ address: {
30
+ type: "address",
31
+ default: "",
32
+ },
33
+ amount: {
34
+ type: "number",
35
+ default: "",
36
+ },
37
+ },
38
+ description: "Transfer USDC to another address.",
39
+ },
40
+ ];
41
+
42
+ async function balanceHandler(context: XMTPContext) {
43
+ const {
44
+ message: { sender },
45
+ } = context;
46
+ const usdcWallet = new USDCWallet(sender.address);
47
+ const { usdc } = await usdcWallet.checkBalances();
48
+ await context.send(
49
+ `Your balance is ${usdc} USDC. let me know if you want check again or to fund your wallet.`,
50
+ );
51
+ }
52
+
53
+ async function fundHandler(context: XMTPContext) {
54
+ try {
55
+ const {
56
+ message: {
57
+ sender,
58
+ content: {
59
+ params: { amount },
60
+ },
61
+ },
62
+ } = context;
63
+ const usdcWallet = new USDCWallet(sender.address);
64
+ const { usdc } = await usdcWallet.checkBalances();
65
+ const MAX_USDC = 10;
66
+
67
+ if (usdc >= MAX_USDC) {
68
+ await context.send(`Your balance is maxed out at ${MAX_USDC} USDC.`);
69
+ return;
70
+ }
71
+
72
+ const remainingLimit = MAX_USDC - usdc;
73
+ let fundAmount: number;
74
+
75
+ if (!amount) {
76
+ const options = Array.from(
77
+ { length: Math.floor(remainingLimit) },
78
+ (_, i) => (i + 1).toString(),
79
+ );
80
+ const response = await context.awaitResponse(
81
+ `Please specify the amount of USDC to prefund (1 to ${remainingLimit}):`,
82
+ options,
83
+ );
84
+ fundAmount = parseInt(response);
85
+ } else {
86
+ fundAmount = parseInt(amount);
87
+ }
88
+
89
+ if (isNaN(fundAmount) || fundAmount <= 0 || fundAmount > remainingLimit) {
90
+ await context.send(
91
+ `Invalid amount. Please specify a value between 1 and ${remainingLimit} USDC.`,
92
+ );
93
+ return;
94
+ }
95
+
96
+ await context.requestPayment(fundAmount, "USDC", usdcWallet.agentAddress);
97
+ await context.send(
98
+ "After funding, let me know so I can check your balance.",
99
+ );
100
+ } catch (error) {
101
+ await context.send(
102
+ "An error occurred while processing your request. Please try again.",
103
+ );
104
+ }
105
+ }
106
+
107
+ async function transferHandler(context: XMTPContext) {
108
+ const {
109
+ message: {
110
+ sender,
111
+ content: {
112
+ params: { address, amount },
113
+ },
114
+ },
115
+ } = context;
116
+ const usdcWallet = new USDCWallet(sender.address);
117
+ if (amount > 10) {
118
+ await context.send("You can only transfer up to 10 USDC at a time.");
119
+ return;
120
+ }
121
+ await usdcWallet.transferUsdc(address, amount);
122
+ }
@@ -0,0 +1,63 @@
1
+ import { XMTPContext } from "@xmtp/message-kit";
2
+ import type { Skill } from "@xmtp/message-kit";
3
+
4
+ // Example skill structure
5
+ export const cryptoPrice: Skill[] = [
6
+ {
7
+ skill: "price",
8
+ handler: handler,
9
+ examples: ["/price BTC USD", "/price ETH EUR"],
10
+ description: "Get the current exchange rate for a cryptocurrency.",
11
+ params: {
12
+ crypto: {
13
+ type: "string",
14
+ },
15
+ currency: {
16
+ type: "string",
17
+ default: "USD",
18
+ },
19
+ },
20
+ },
21
+ ];
22
+
23
+ async function handler(context: XMTPContext) {
24
+ const {
25
+ message: {
26
+ content: {
27
+ params: { crypto, currency },
28
+ },
29
+ },
30
+ } = context;
31
+
32
+ try {
33
+ const response = await fetch(
34
+ `https://min-api.cryptocompare.com/data/price?fsym=${crypto}&tsyms=${currency}`
35
+ );
36
+
37
+ if (!response.ok) {
38
+ return {
39
+ code: response.status,
40
+ message: `Unable to fetch exchange rate: ${response.statusText}`,
41
+ };
42
+ }
43
+
44
+ const data = await response.json() as Record<string, number>;
45
+
46
+ if (!data[currency]) {
47
+ return {
48
+ code: 400,
49
+ message: `Could not find exchange rate for ${crypto}/${currency}`,
50
+ };
51
+ }
52
+
53
+ return {
54
+ code: 200,
55
+ message: `1 ${crypto} = ${data[currency]} ${currency}`,
56
+ };
57
+ } catch (error: any) {
58
+ return {
59
+ code: 500,
60
+ message: `Error fetching exchange rate: ${error.message}`,
61
+ };
62
+ }
63
+ }
@@ -0,0 +1,61 @@
1
+ import { XMTPContext } from "@xmtp/message-kit";
2
+
3
+ import type { Skill } from "@xmtp/message-kit";
4
+ import OpenAI from "openai";
5
+
6
+ const openai = new OpenAI({
7
+ apiKey: process.env.OPENAI_API_KEY,
8
+ });
9
+
10
+ export const dalle: Skill[] = [
11
+ {
12
+ skill: "image",
13
+ handler: handler,
14
+ description: "Generate an image based on a prompt.",
15
+ examples: ["/image dog with a ball"],
16
+ params: {
17
+ prompt: {
18
+ type: "prompt",
19
+ },
20
+ },
21
+ },
22
+ ];
23
+
24
+ export async function handler(context: XMTPContext) {
25
+ const {
26
+ message: {
27
+ sender,
28
+ content: {
29
+ params: { prompt },
30
+ },
31
+ },
32
+ } = context;
33
+
34
+ if (!prompt) {
35
+ return {
36
+ code: 400,
37
+ message: "Missing required parameters. Please provide a prompt.",
38
+ };
39
+ }
40
+
41
+ try {
42
+ const response = await openai.images.generate({
43
+ prompt: prompt,
44
+ n: 1,
45
+ size: "1024x1024",
46
+ });
47
+
48
+ const imageUrl = response.data[0].url;
49
+ console.log(imageUrl);
50
+ const message = `Here is the image generated for the prompt "${prompt}": ${imageUrl}`;
51
+ context.send(message);
52
+ } catch (error) {
53
+ // @ts-ignore
54
+ const message = `Failed to generate image. Error: ${error?.message}
55
+ }`;
56
+ return {
57
+ code: 500,
58
+ message,
59
+ };
60
+ }
61
+ }