This guide will walk through building a Twitter bot on top of the Clanker protocol similar to Bankr, Dealr, Beamr, and others. This bot will use an LLM to interpret user requests, Privy managed wallets to manage EVM accounts, and the Clanker protocol to deploy tokens on Base.
Use a library like twitter-api-v2 to interact with Twitter.
Install the library:
npm install twitter-api-v2
Create a basic Twitter client setup:
import { TwitterApi } from 'twitter-api-v2';// Initialize the Twitter client with your credentialsconst client = new TwitterApi({ appKey: process.env.TWITTER_API_KEY!, appSecret: process.env.TWITTER_API_SECRET!, accessToken: process.env.TWITTER_ACCESS_TOKEN!, accessSecret: process.env.TWITTER_ACCESS_SECRET!,});// Get the read/write clientconst rwClient = client.readWrite;
Example Node.js code to poll for mentions and robustly parse commands:
async function pollMentions() { let sinceId: string | undefined = undefined; while (true) { const mentions = await rwClient.v2.userMentionTimeline('YOUR_BOT_USER_ID', { since_id: sinceId, expansions: ['author_id', 'entities.mentions.username'], 'user.fields': ['username', 'name'], max_results: 5, }); for (const tweet of mentions.data?.data || []) { const parsed = processTweet(tweet, mentions); if (parsed) { // Call your LLM or transaction logic here // e.g. handleCommand(parsed) } sinceId = tweet.id; } await new Promise(res => setTimeout(res, 10000)); // poll every 10s }}function processTweet(tweet, mentionsResponse) { // Extract basic tweet information const text = tweet.text; const authorId = tweet.author_id; const tweetId = tweet.id; const mentions = tweet.entities?.mentions || []; // Extract all mentioned users const mentionedUsers = mentions.map(mention => { const username = mention.username; const user = mentionsResponse.includes?.users.find(u => u.username === username); return { username, userId: user?.id, isBot: username === 'bankr_bot' // Identify if this is our bot }; }); // Pass the structured data to the LLM for intent detection return { text, authorId, tweetId, mentions: mentionedUsers, // Additional context can be added here };}
This code polls for new mentions, parses the tweet for sender, recipient, amount, and currency, and passes the result to your LLM or transaction logic.
For production, consider using the filtered stream for real-time events.
In our example application, we will build two basic interactions with Privy wallets:
Create a wallet
Get a user’s wallet
Using these core building blocks, we can allow our bot to seamlessly and securely create and manage wallets for users.
1
Create a wallet for a user
This function creates a new wallet for a user and saves the wallet ID to the database.
/** * Creates a new wallet for a user * @param userId - The Twitter user ID * @returns The wallet object with id, address, and other properties */async function createUserWallet(userId) { // Create a new wallet for the user const wallet = await privy.walletApi.createWallet({chainType: 'ethereum'}); // EXAMPLE: Save the wallet ID to the database to save the mapping between the user and their wallet await db.wallets.set(userId, wallet.id); return wallet;}
2
Get a user wallet
This function checks if a user has a wallet and returns it if it exists.
/** * Gets a user's wallet if it exists * @param userId - The Twitter user ID * @returns The wallet object or null if no wallet exists */async function getUserWallet(userId) { // EXAMPLE: Check if user already has a wallet const walletId = await db.wallets.get(userId); // If wallet exists, retrieve and return the wallet if (walletId) { const wallet = await privy.walletApi.getWallet(walletId); return wallet; } return null;}
3
Get or create a wallet
A higher level function that uses the getUserWallet and createUserWallet functions to get or create a wallet for a user.
/** * Gets a user's wallet or creates one if they don't have one * @param userId - The Twitter user ID * @returns The wallet object with id, address, and other properties */async function getOrCreateUserWallet(userId) { // Try to get existing wallet const existingWallet = await getUserWallet(userId); // Return existing wallet if found if (existingWallet) { return existingWallet; } // Create a new wallet if none exists return createUserWallet(userId);}// Example usage:const wallet = await getOrCreateUserWallet(parsed.authorId);
Use an LLM to interpret user messages and decide what action to take. Treat the LLM as a black box that receives a prompt and returns a structured intent.
1
Sample LLM prompt and response
Prompt:
User: Launch a token called $CAT with 1B supplySystem: Extract the intent and parameters for a token launch on Base.Output format: { action: string, params: object }
Let users deploy tokens on Base by simply messaging the bot. The LLM interprets the intent, and the bot handles wallet lookup/creation and token deployment.
1
Sample LLM prompt and response
Prompt:
User: create a new $Example tokenSystem: Extract the intent and parameters for a token launch on Base.Output format: { action: string, params: object }
// Example incoming tweetconst tweet = { text: "@YOUR_BOT_HANDLE create a new $Example token", author_id: "1234567890", id: "9876543210", // ...other fields};// Remove bot mention to get user commandconst userMessage = tweet.text.replace(/@YOUR_BOT_HANDLE\s*/i, "").trim();
3
Use LLM to extract intent and parameters
// Send the user message to your LLMconst llmResponse = await llm.query({ prompt: userMessage });// Example LLM response:// {// action: "launch_token",// params: { name: "Example", symbol: "$Example" }// }if (llmResponse.action !== 'launch_token') { throw new Error('Not a token launch command');}const { name, symbol } = llmResponse.params;
4
Look up or create user's wallet
// Get or create a wallet for the userconst wallet = await getOrCreateUserWallet(tweet.author_id);// wallet.address will be used as the requestorAddress
// Send a DM or reply to the user with the token addressawait twitterClient.sendDM({ userId: tweet.author_id, text: `Token deployed! Address: ${tokenInfo.address}`});
Let users send tokens (e.g., ETH on Base) to other Twitter users by simply messaging the bot. The LLM interprets the intent, and the bot handles wallet lookup/creation and transaction sending.
1
Sample LLM prompt and response
Prompt:
User: Hey bot, send 0.01 ETH from my wallet to @privy_io on twitterSystem: Extract the intent and parameters for a token transfer on Base.Output format: { action: string, params: object }
// 1. Get sender's Twitter ID from the parsed tweetconst senderTwitterId = parsed.authorId;// 2. Get recipient's Twitter handle from LLM response (e.g., '@privy_io')const recipientHandle = llmResponse.params.recipient.replace('@', '');// 3. Look up recipient's Twitter ID from parsed mentionsconst recipientMention = parsed.mentions.find(m => m.username === recipientHandle);if (!recipientMention || !recipientMention.userId) { throw new Error('Recipient not found in tweet mentions');}const recipientTwitterId = recipientMention.userId;// 4. Get or create recipient's walletconst recipientWallet = await getOrCreateUserWallet(recipientTwitterId);// 5. Get or create sender's walletconst senderWallet = await getOrCreateUserWallet(senderTwitterId);
3
Send transaction using Privy server wallet (NodeJS)
import { parseEther } from 'viem';// 1. Parse the amount to wei (ETH uses 18 decimals)const amountEth = llmResponse.params.amount; // e.g., '1'const amountWei = parseEther(amountEth); // '1000000000000000000'// 2. Prepare transaction detailsconst transaction = { to: recipientWallet.address, // Recipient's EVM address value: amountWei, // Amount in wei chainId: 8453, // Base chain ID // (Optional: add gas, data, etc.)};// 3. Send the transaction using Privy server walletconst sendResult = await privy.walletApi.ethereum.sendTransaction({ walletId: senderWallet.id, // Sender's Privy wallet ID caip2: 'eip155:8453', // CAIP2 for Base transaction,});