Written by
Owen Venter
Published on
October 10, 2023
Copy link

How to set up a Solana Telegram bot

There are roughly 400 transactions happening per second on the Solana blockchain. These include token transfers, NFT sales, token swaps, bids, DAO votes, bets and lots more. But with so much data, how do you keep track of changes that you care about? That's where webhooks come in. Webhooks allow you to track these changes and stay up to-date on the things you care about. This guide will show you how to set up a Telegram bot to get on-chain updates sent directly to a user or channel using a Cloudflare worker.

Telegram bot sending updates.

Steps to set up a Telegram Bot

  1. Set up the Telegram channel and the Telegram bot.
  2. Create a Cloudflare worker to act as a connection between the webhook that is sending updates and the Telegram bot.
  3. Create a Helius webhook.
  4. Edit the addresses the webhook tracks via the Helius Webhook API. This step is only needed for tracking entire NFT collections.

Set Up a Telegram Bot

Telegram bots are a great way to automate certain tasks, especially updates. Here are the steps to set one up.

  1. Start a Conversation with BotFather: Search for 'BotFather' in the Telegram search bar and click on it to begin a conversation.
  2. Create a New Bot: Type and send '/newbot'. BotFather will now guide you through naming your bot and giving it a username.
  3. Access Token: Once done, BotFather will provide an access token.  Note it down securely as we will be using this later on in the tutorial.

For channel updates:

  1. Create a Telegram Channel: Go to the menu (☰) and select 'New Channel'.
  2. Invite the Bot: Once you've set up the channel, click on the channel's name, select 'Administrators' and add your bot as one.
  3. Find the Chat ID:
  4. To determine your channel's chat ID, follow these steps:
  5. Send a Message: First, send a message to the channel you've created.
  6. Use the Telegram API: Navigate to https://api.telegram.org/bot<YOUR_BOT_TOKEN>/getUpdates in your browser, replacing <YOUR_BOT_TOKEN> with the token you received from BotFather.
  7. Locate the Chat ID: In the returned data, you'll find a field named chat and inside it, an id which is the chat ID of your channel.
  8. Remember to note down this chat ID securely, as you'll need it for further configurations.

Create a Cloudflare worker

Cloudflare workers allow you to deploy code that is hosted online and will run permanently so as to constantly be on alert for updates from your web hook.

  1. Log into Cloudflare: Start by logging into your Cloudflare account. If you don’t have one yet, you can sign up for free.
  2. Access the Workers section: From the Cloudflare dashboard, navigate to the "Workers" tab.
  3. Create a new Worker: Click on the “Create a Worker” button. This will take you to the Worker Editor.
  1. Edit the Worker Code:
  2. In the editor, you'll see a script template. Replace the default code with the provided Cloudflare code for your Telegram bot.
  3. Locate the variables for the BOT_TOKEN and CHAT_ID within the code. These are placeholders and will be used to send messages via your bot to the correct chat.
  4. Configure Environment Variables:
  5. In the Cloudflare Workers dashboard, navigate to the "Settings" tab of your worker.
  6. Here, you can add your bot’s token, the chat token, and your Helius API key as environment variables. This helps keep sensitive data out of your main code and secure.
  7. Ensure that you name these environment variables correctly to match what's expected in your code (e.g., BOT_TOKEN, CHAT_ID, etc.).
  8. Deploy the Worker: After making sure your code and environment variables are correctly set, click on the “Save and Deploy” button. Your worker will be deployed and assigned a unique public URL.
  9. Note Down the Public URL: After deployment, make sure to note down the public URL assigned to your worker. This URL is what you'll be using as the webhook endpoint for your Telegram bot.

Cloudflare Worker code:


const TELEGRAM_BOT_TOKEN = BOT_TOKEN;
const TELEGRAM_CHAT_ID = CHAT_ID;
const HELIUS_API_KEY=API_KEY;
const HELIUS_RPC_URL = `https://mainnet.helius-rpc.com/?api-key=${HELIUS_API_KEY}`;

addEventListener('fetch', event => {
  event.respondWith(handleRequest(event.request))
})

async function handleRequest(request) {
  if (request.method === 'POST') {
    const requestBody = await request.json();
    console.log('Received POST request with body:', requestBody);


    //THIS IS FOR NFT UPDATES (comment this section out if you are doing something else)
    // Extract transaction description, timestamp, signature, and mint address
      // const NFTdescription = requestBody[0].description;
      // const NFTtimestamp = new Date(requestBody[0].timestamp * 1000).toLocaleString(); // Convert Unix timestamp to readable date-time
      // const NFTsignature = `https://solscan.io/tx/${requestBody[0].signature}`
      // const NFTmintAddress = requestBody[0].events.nft.nfts[0].mint;
      // const NFTimageUrl = await getAssetImageUrl(NFTmintAddress);// Get NFT image URL
      // // Construct the message
      // const messageToSendNFT = 
      // `----NEW UPDATE---\n`+
      // `Description:\n${NFTdescription}\n` +
      // `Mint Address:\n${NFTmintAddress}\n` +
      // `Signature:\n${NFTsignature}\n` +
      // `Timestamp:\n${NFTtimestamp}`;
      // await sendToTelegramNFT(messageToSendNFT, NFTimageUrl); // Send to Telegram


    //THIS IS FOR TRANSFER UPDATES (comment this section out if you are doing something else)
    // Extract transaction description, timestamp, signature
      const Transferdescription = requestBody[0].description;
      const Transfertimestamp = new Date(requestBody[0].timestamp * 1000).toLocaleString(); // Convert Unix timestamp to readable date-time
      const Transfersignature = `https://xray.helius.xyz/tx/${requestBody[0].signature}`
      // Construct the message
      const messageToSendTransfer = 
      `----NEW UPDATE---\n`+
      `Description:\n${Transferdescription}\n` +
      `Signature:\n${Transfersignature}\n` +
      `Timestamp:\n${Transfertimestamp}`;
      await sendToTelegramTransfer(messageToSendTransfer); // Send to Telegram



    return new Response('Logged POST request body.', {status: 200});
  } else {
    return new Response('Method not allowed.', {status: 405});
  }
}

// This function is used to send NFT Updates to the bot
async function sendToTelegramNFT(message, imageUrl) {
  const telegramUrl = `https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/sendPhoto`;
  const response = await fetch(telegramUrl, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      chat_id: TELEGRAM_CHAT_ID,
      photo: imageUrl,
      caption: message,
      parse_mode: "HTML"
    }),
  });
  const responseData = await response.json();

  if (!response.ok) {
    console.error('Failed to send photo to Telegram:', responseData);
  }
}



//This function is used to send Transfer Updates to the Bot
async function sendToTelegramTransfer(message) {
  const telegramUrl = `https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/sendMessage`;
  const response = await fetch(telegramUrl, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      chat_id: TELEGRAM_CHAT_ID,
      text: message, 
      parse_mode: "HTML"
    }),
  });
  const responseData = await response.json();

  if (!response.ok) {
    console.error('Failed to send message to Telegram:', responseData);
  }
}


//This function gets images associated to NFTs that are features in updates.
async function getAssetImageUrl(mintAddress) {
  const response = await fetch(HELIUS_RPC_URL, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      jsonrpc: '2.0',
      id: 'my-id',
      method: 'getAsset',
      params: {
        id: mintAddress,
      },
    }),
  });
  const { result } = await response.json();
  return result.content.links.image;
}

Set up a Helius Webhook

Helius webhooks bridge the gap between changes on the Solana blockchain and your Telegram channel. In this example I will be setting up a webhook to track all transactions that a certain wallet makes.

  1. Sign Up: Visit dev.helius.xyz and create a free account.
  2. Create a Webhook: Within your dashboard, opt to create a new webhook. You'll have a choice to decide its type - Raw, Enhanced, Discord, or Account. For specific categorised updates, 'Enhanced' is recommended. When choosing this, specify the updates you're interested in, e.g., NFT_SALES or NFT_LISTING. A comprehensive list is available here.
  3. Supply Worker Link and Solana Address: Ensure you provide the link to your deployed Cloudflare worker. Also, mention the specific Solana account address (could be a user's wallet or a list of mints from a collection) you wish to monitor.
Helius webhook Setup settings

How to track NFT collections (Sales Bot Setup)

If you want to track a certain collection to get updates when something happens to an NFT in that collection you will need to add all the NFT mint addresses as accounts on you webhook. To do this you will need to make use of the Helius webhook endpoint to edit the webhook that you initially made.Once this is set up you will be able to track sales, listings, bids, burns and any other transaction that an NFT is involved in.

The first thing you will need is a hash/mint list for the collection you want to track. You can use the following code to get a mint list using DAS. All you will need to do is add in your Helius API key and replace the creatorAddress with the address of the first verified creator of an NFT. You can find the first verified creator by going on your favourite explorer, looking for the metadata and finding the address under the creators section:

Finding the Creator Address

Snapshot Code:


const fs = require('fs');
const url = `https://mainnet.helius-rpc.com/?api-key=`;

const getAssetsByCreator = async () => {
  let page = 1;
  let allMintAddresses = [];

  while (true) {
    const response = await fetch(url, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        jsonrpc: '2.0',
        id: `my-id-page-${page}`,
        method: 'getAssetsByCreator',
        params: {
          creatorAddress: 'GFQBHxuNcAwYwHtEpggMwbH5kwV6Ne4JowHbLVfsMC6C',
          onlyVerified: true,
          page: page,
          limit: 1000
        },
      }),
    });

    const { result } = await response.json();

    // If there are no more items, break the loop
    if (!response.ok) {
      throw new Error(`HTTP error! Status: ${response.status}`);
    }else{
      if (!result.items || result.items.length === 0) {
        break;
      }
    }

    // Extract mint addresses and add them to the list
    const mintAddresses = result.items.map(item => item.id); 
    allMintAddresses.push(...mintAddresses);

    // Check if there are less than 1000 results to stop further requests
    if (result.items.length < 1000) {
      break;
    }

    page++;
  }

  // Join the addresses by comma
  const addressesString = allMintAddresses.join(',');
  console.log("All Mint Addresses: ", addressesString);
  fs.writeFileSync('addresses.json', JSON.stringify(allMintAddresses, null, 2));
};

getAssetsByCreator();

Once you have this you can run the script by opening your terminal, navigating to the directory with the script in it and running node snapshot.js. This will create a JSON file called addresses.json in that directory. Alternatively you can use a tool like the Magic Eden snapshot tool to get a hash list.

Once you have a hash list you can update the list of accounts that your webhook tracks. To do this you will need to use the Helius webhooks end point. I’ve attached some code below that makes this easy. Simply create a new JavaScript file called webhookUpdate.js (in the same directory as your hashlist) and add the code below. You can then edit the code to include your Helius API key and your webhook ID (this can be found on the dev portal). You will then need to edit the body of the request to match the data that you used when creating the webhook. So if you choose to track NFT listings and sales make sure to include that. Do not edit the accountAddresses field as this is set to use the hashlist that we created earlier.

Once these changes have been made you can run this script from your terminal with node webhookUpdate.js. After running this code you should receive confirmation in the terminal.

webhookUpdate.js:


const fs = require('fs');

const editWebhook = async () => {
    try {
      // Read the JSON file
      const addresses = JSON.parse(fs.readFileSync('addresses.json', 'utf8'));
  
      const response = await fetch(
        "https://api.helius.xyz/v0/webhooks/?api-key=",
        {
          method: 'PUT',
          headers: {
            'Content-Type': 'application/json',
          },
          body: JSON.stringify({
            webhookURL: "",
            transactionTypes: ["NFT_LISTING","NFT_SALE"],
            accountAddresses: addresses,  // Use the addresses from the file
            webhookType: "enhanced"
          }),
        }
      );
      const data = await response.json();
      console.log({ data });
    } catch (e) {
      console.error("error", e);
    }
  };
  
  editWebhook();

Once the webhook has been updated you can go back the the Cloudflare worker and edit the code to return details specific to NFTs. The code that is needed is already included, all you would need to do is comment out the section for sending TRANSFER updates and uncomment the section for sending NFT updates.

When an event that you are tracking happens on chain the webhook will send an update to the Cloudflare worker. This update will be in json format and will typically contain a lot of information such as all the accounts involved, changes in each account and info relating to the type of event. In the code provided we extract the parts that we believe to be important.

If you would like to customise the message that is sent or change the data that is extracted from the webhook update you can make changes to the messageToSend variable. If you would like to track something outside of transfers and NFT events you can still do so. You can modify the Cloudflare code to send the entire requestBody[0]. This will forward the full webhook JSON response to your Telegram bot, allowing you to view the response structure. You can then view and decide which sections of this you would like to include in your message.

With this setup, your Telegram channel becomes a hub for real-time updates from the Solana blockchain, all thanks to Helius webhooks. We hope you have found this guide useful, if you have any questions or issues please feel free to message the team on Discord or Twitter.