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.
- package/index.js +15 -15
- package/package.json +1 -1
- package/templates/coinbase-agent/.cursorrules +290 -0
- package/templates/coinbase-agent/.env.example +4 -0
- package/templates/coinbase-agent/.yarnrc.yml +9 -0
- package/templates/coinbase-agent/package.json +22 -0
- package/templates/coinbase-agent/src/index.ts +31 -0
- package/templates/coinbase-agent/src/plugins/learnweb3.ts +96 -0
- package/templates/coinbase-agent/src/plugins/redis.ts +15 -0
- package/templates/coinbase-agent/src/prompt.ts +64 -0
- package/templates/coinbase-agent/src/skills/drip.ts +83 -0
- package/templates/coinbase-agent/src/skills/mint.ts +99 -0
- package/templates/coinbase-agent/src/skills/pay.ts +35 -0
- package/templates/coinbase-agent/src/skills/swap.ts +52 -0
- package/templates/ens/.env.example +1 -0
- package/templates/ens/.yarnrc.yml +5 -0
- package/templates/ens/src/index.ts +1 -1
- package/templates/ens/src/prompt.ts +1 -14
- package/templates/faucet/.cursorrules +290 -0
- package/templates/faucet/.env.example +5 -0
- package/templates/faucet/.yarnrc.yml +9 -0
- package/templates/faucet/package.json +22 -0
- package/templates/faucet/src/index.ts +39 -0
- package/templates/faucet/src/plugins/learnweb3.ts +96 -0
- package/templates/faucet/src/plugins/redis.ts +15 -0
- package/templates/faucet/src/prompt.ts +11 -0
- package/templates/faucet/src/skills/faucet.ts +140 -0
- package/templates/gated-group/.cursorrules +290 -0
- package/templates/gated-group/.env.example +3 -0
- package/templates/gated-group/.yarnrc.yml +9 -0
- package/templates/gated-group/package.json +23 -0
- package/templates/gated-group/src/index.ts +17 -0
- package/templates/gated-group/src/plugins/alchemy.ts +27 -0
- package/templates/gated-group/src/plugins/xmtp.ts +137 -0
- package/templates/gated-group/src/prompt.ts +11 -0
- package/templates/gated-group/src/skills/gated.ts +88 -0
- package/templates/gm/.cursorrules +290 -0
- package/templates/gm/.env.example +2 -0
- package/templates/gm/.yarnrc.yml +9 -0
- package/templates/gm/package.json +20 -0
- package/templates/gm/src/index.ts +8 -0
- package/templates/playground/.cursorrules +290 -0
- package/templates/playground/.env.example +6 -0
- package/templates/playground/.yarnrc.yml +9 -0
- package/templates/playground/package.json +26 -0
- package/templates/playground/src/index.ts +40 -0
- package/templates/playground/src/plugins/alchemy.ts +27 -0
- package/templates/playground/src/plugins/minilog.ts +26 -0
- package/templates/playground/src/plugins/usdc.ts +99 -0
- package/templates/playground/src/plugins/xmtp.ts +137 -0
- package/templates/playground/src/prompt.ts +11 -0
- package/templates/playground/src/skills/broadcast.ts +39 -0
- package/templates/playground/src/skills/cash.ts +122 -0
- package/templates/playground/src/skills/cryptoPrice.ts +63 -0
- package/templates/playground/src/skills/dalle.ts +61 -0
- package/templates/playground/src/skills/gated.ts +88 -0
- package/templates/playground/src/skills/search.ts +159 -0
- package/templates/playground/src/skills/todo.ts +79 -0
- package/templates/playground/src/skills/token.ts +57 -0
- package/templates/playground/src/skills/web.ts +83 -0
- package/templates/playground/src/skills/wordle.ts +96 -0
- package/templates/simple/.env.example +1 -0
- package/templates/simple/.yarnrc.yml +5 -0
- package/templates/simple/src/index.ts +1 -1
- package/templates/simple/src/prompt.ts +2 -0
- package/templates/thegeneralstore/.cursorrules +290 -0
- package/templates/thegeneralstore/.env.example +9 -0
- package/templates/thegeneralstore/.yarnrc.yml +9 -0
- package/templates/thegeneralstore/package.json +24 -0
- package/templates/thegeneralstore/src/data/db.json +812 -0
- package/templates/thegeneralstore/src/index.ts +37 -0
- package/templates/thegeneralstore/src/plugins/learnweb3.ts +96 -0
- package/templates/thegeneralstore/src/plugins/lowdb.ts +11 -0
- package/templates/thegeneralstore/src/plugins/notion.ts +89 -0
- package/templates/thegeneralstore/src/plugins/redis.ts +15 -0
- package/templates/thegeneralstore/src/prompt.md +51 -0
- package/templates/thegeneralstore/src/prompt.ts +3 -0
- package/templates/thegeneralstore/src/skills/faucet.ts +114 -0
- package/templates/thegeneralstore/src/skills/notion.ts +17 -0
- package/templates/thegeneralstore/src/skills/poap.ts +48 -0
- package/templates/thegeneralstore/src/skills.ts +37 -0
- package/templates/toss/.cursorrules +290 -0
- package/templates/toss/.env.example +7 -0
- package/templates/toss/.yarnrc.yml +9 -0
- package/templates/toss/package.json +21 -0
- package/templates/toss/src/index.ts +33 -0
- package/templates/toss/src/plugins/helpers.ts +185 -0
- package/templates/toss/src/plugins/redis.ts +78 -0
- package/templates/toss/src/prompt.ts +54 -0
- 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,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,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
|
+
}
|