Skip to content

Instantly share code, notes, and snippets.

@Turupawn
Created March 7, 2025 22:47
Show Gist options
  • Save Turupawn/ec60dd2f5866b0c9b6f21b5e4a177eed to your computer and use it in GitHub Desktop.
Save Turupawn/ec60dd2f5866b0c9b6f21b5e4a177eed to your computer and use it in GitHub Desktop.
// We'll use the official node-telegram-bot-api library to interact with the Telegram API and ethers to verify the signature
const TelegramBot = require("node-telegram-bot-api");
const { ethers } = require("ethers");
require("dotenv").config();
const express = require("express");
const cors = require("cors");
const bot = new TelegramBot(process.env.BOT_TOKEN, { polling: true });
const CHAIN_ID = process.env.CHAIN_ID;
const WEB_DAPP_URL = process.env.WEB_DAPP_URL;
// Token balance checker
const requiredTokenBalance = ethers.parseUnits("1000", 18); // Required balance of 1000 SRC tokens
const tokenAddress = "0xd29687c813D741E2F938F4aC377128810E217b1b";
const rpcUrl = "https://rpc.ankr.com/scroll";
const provider = new ethers.JsonRpcProvider(rpcUrl);
const abi = ["function balanceOf(address owner) view returns (uint256)"];
const contract = new ethers.Contract(tokenAddress, abi, provider);
const blockNumber = 13895425; // 8949016 is one of the first blocks of SCR
const app = express();
app.use(cors());
app.use(express.json());
// Starts the telegram bot and the API server that recieves the signature and verifies it
(async () => {
try {
bot.botInfo = await bot.getMe();
app.listen(8080, () => {
console.log("\nServer is running on port 8080");
console.log("Bot is running...");
});
} catch (error) {
console.error(error);
process.exit(1);
}
})();
// The /verify endpoint is used to verify the signature and send a welcome message to the user
app.post("/verify", async (req, res) => {
const { userId, message, signature } = req.body;
try {
let signerAddress = await getAuthenticationSigner(userId, message, signature);
signerAddress = "0xb6F5414bAb8d5ad8F33E37591C02f7284E974FcB"
const balance = await contract.balanceOf(signerAddress, { blockTag: blockNumber }); // 8949016 is one of the first blocks of SCR
await bot.sendMessage(
Number(userId),
`Welcome! You're authenticated as ${signerAddress}.\n\nYou have ${balance} SCR in your wallet.`
);
res.json({ success: true, signerAddress });
} catch (error) {
console.error("Verification error:", error);
res.status(400).json({ success: false, error: error.message });
}
});
// getAuthenticationSigner returns the signer address by verifying the signature
function getAuthenticationSigner(userId, message, signature) {
// accessRequest is the actual data schema of the message that we want to verify
const accessRequest = {
userId: userId,
message: message,
};
// domain is the general information about your dapp, this is the same for all the messages
const domain = {
name: "Telegram Group Access",
version: "1",
chainId: CHAIN_ID,
};
// types is the data schema of the message that we want to verify
const types = {
AccessRequest: [
{ name: "userId", type: "uint256" },
{ name: "message", type: "string" },
]
};
// verifyTypedData verifies the signature in the erc712 style and return the signer address by ecrecovering
// We don't need to do worry about those details, ethers does it for us
return ethers.verifyTypedData(domain, types, accessRequest, signature);
}
// This is the main function that runs when the bot receives a message
bot.on("message", async (msg) => {
const text = msg.text || "";
// It checks if the message is "authenticate" and if so, it sends a message to the user to visit the website
if (text.toLowerCase() === "authenticate") {
// userId is the user's id in telegram
const userId = msg.from.id;
// We send the user to the web dapp to authenticate
bot.sendMessage(userId, `Please visit: ${WEB_DAPP_URL}?userId=${userId}`);
return;
}
});
console.log("\nBot is running...");
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment