Skip to content

Add Telegram Token Launch & Faucet Example (Golang + BNB Testnet)/tg token lauch example #19

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 17 commits into from
Apr 11, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions go/tg-token-launch-example/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Wallet private key using Mnemonic
MNEMONIC=your_faucet_wallet_private_key

# RPC endpoint for BNB Chain testnet
RPC_URL=https://data-seed-prebsc-1-s1.binance.org:8545/

# Telegram Bot Token from BotFather
TG_BOT_TOKEN=your_telegram_bot_token

# Factory contract deployed to testnet
TOKEN_FACTORY_ADDRESS=0xYourFactoryContractAddress

# Chain ID for BNB Testnet
CHAIN_ID=97

# HTTP port for internal APIs (if any)
PORT=8080
20 changes: 20 additions & 0 deletions go/tg-token-launch-example/Factory.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";

contract TokenFactory {
event TokenDeployed(address indexed owner, address indexed token, string name, string symbol);

function deployToken(string memory name, string memory symbol, address initialRecipient) external returns (address) {
ERC20Token newToken = new ERC20Token(name, symbol, initialRecipient);
emit TokenDeployed(initialRecipient, address(newToken), name, symbol);
return address(newToken);
}
}

contract ERC20Token is ERC20 {
constructor(string memory name, string memory symbol, address to) ERC20(name, symbol) {
_mint(to, 1_000_000 * 10 ** decimals());
}
}
183 changes: 183 additions & 0 deletions go/tg-token-launch-example/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
# 🧪 BNB Chain Telegram Token Launch & Faucet Demo

## Introduction

This project is a complete hands-on demo for creating and interacting with your own BEP20 token on **BNB Chain Testnet**, using a **Telegram bot interface** and a backend written in **Go**.

It showcases how to:
- Deploy BEP20 tokens via a **Solidity Factory contract**
- Interact with contracts using **Golang and go-ethereum**
- Let users manage tokens via a **Telegram Bot**

---

## 🔧 What You Will Be Building

- A **Solidity factory contract** that deploys ERC20-compatible tokens
- A **Telegram bot** that lets each user:
- Deploy their own token
- Use a faucet to send that token
- Check token balances
- A **Golang backend** that listens to the Telegram bot and interacts with contracts in real time

---

## 🚀 Getting Started

### ✅ 1. Create Your Wallet (Trust Wallet or MetaMask)

Create a new wallet and switch to **BNB Chain Testnet**.
Then grab some test BNB from the official [BNB Faucet](https://testnet.bnbchain.org/faucet-smart).

---

### ✅ 2. Create a Telegram Bot

Use [@BotFather](https://t.me/BotFather) to create a new Telegram bot.

- Run `/newbot` and follow the instructions
- Save the API token it gives you — you'll need it for `.env`

---

### ✅ 3. Deploy the Factory Contract

You can:
- Use our deployed example on testnet:
`0xeCb781015873dc48a4c0BCdf3ba74dF9269061C3`

**OR**

- Deploy `Factory.sol` using **Hardhat**, **Foundry**, or **Remix**

---

### ✅ 4. Clone the Repository

```bash
git clone https://github.com/your-org/example-hub.git
cd example-hub/go/tg-token-launch-example
```

---

### ✅ 5. Configure Environment Variables

Create a `.env` file:

```env
MNEMONIC=your_mnemonic_phrase_without_quotes
RPC_URL=https://data-seed-prebsc-1-s1.binance.org:8545/
TOKEN_FACTORY_ADDRESS=0xeCb781015873dc48a4c0BCdf3ba74dF9269061C3
TG_BOT_TOKEN=your_telegram_bot_api_token
```

---

### ✅ 6. Install Go Dependencies

Make sure you're using **Go 1.20+**:

```bash
go mod tidy
```

---

### ✅ 7. Run the Bot

```bash
go run .
```

If everything is set up, you'll see logs showing that the Telegram bot is running.

---

## 🤖 Telegram Bot Commands

After starting your bot, go chat with it on Telegram.

### `/deploy <Name> <Symbol>`

Deploys a new BEP20 token for your user.

```bash
/deploy TestCoin TST
```

---

### `/faucet <address>`

Sends 10 tokens of the user’s token to the given wallet address.

```bash
/faucet 0xYourFriendWallet
```

---

### `/balance <address>`

Checks the token balance for the given wallet address.

```bash
/balance 0xYourWallet
```

---

## ✅ Example User Flow

1. User sends `/deploy Meme MEME`
2. Bot responds: `✅ Token deployed: 0x...`
3. User sends `/faucet 0xabc...`
4. Bot sends 10 MEME tokens
5. User sends `/balance 0xabc...`
6. Bot replies with token balance

---

## 🗂 Project Structure

```
tg-token-launch-example/
├── Factory.sol # Solidity factory contract
├── main.go # Entry point
├── bot.go # Telegram bot logic
├── faucet.go # Send tokens
├── balance.go # Check balances
├── factory.go # Deploy tokens via contract
├── utils/
│ └── env.go # Loads .env + key derivation
├── go.mod / go.sum # Go module dependencies
├── .env # Your local config (not committed)
└── README.md
```

---

## 🧠 Learning Points

- Deriving Ethereum wallets from mnemonic phrases (BIP-44)
- Sending transactions and reading logs using go-ethereum
- Interacting with deployed smart contracts
- Parsing custom events in Go
- Building bots with Telegram Bot API

---

## 🪪 License

MIT — use this as a base to build your own launchpad, faucet, or dev tool.

---

## 🙌 Contributing

PRs and ideas are welcome! This is built for developers looking to quickly bootstrap real-world BNBChain projects.

---

### 🌐 Built with 💛 by the BNB Chain Dev Community
53 changes: 53 additions & 0 deletions go/tg-token-launch-example/balance.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package main

import (
"context"
"fmt"
"math/big"
"os"
"strings"

"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethclient"
)

// GetTokenBalance returns the BEP20 token balance of a given address
func GetTokenBalance(tokenAddress string, walletAddress string) (string, error) {
client, err := ethclient.Dial(os.Getenv("RPC_URL"))
if err != nil {
return "", err
}
defer client.Close()

contract := common.HexToAddress(tokenAddress)
account := common.HexToAddress(walletAddress)

tokenABI := `[{"constant":true,"inputs":[{"name":"_owner","type":"address"}],"name":"balanceOf","outputs":[{"name":"","type":"uint256"}],"type":"function"}]`
parsedABI, err := abi.JSON(strings.NewReader(tokenABI))
if err != nil {
return "", err
}

data, err := parsedABI.Pack("balanceOf", account)
if err != nil {
return "", err
}

callMsg := ethereum.CallMsg{To: &contract, Data: data}
output, err := client.CallContract(context.Background(), callMsg, nil)
if err != nil {
return "", err
}

var balance *big.Int
err = parsedABI.UnpackIntoInterface(&balance, "balanceOf", output)
if err != nil {
return "", err
}

// Return in human-readable format (e.g. 1.23 tokens)
human := new(big.Float).Quo(new(big.Float).SetInt(balance), big.NewFloat(1e18))
return fmt.Sprintf("%s tokens", human.Text('f', 6)), nil
}
87 changes: 87 additions & 0 deletions go/tg-token-launch-example/bot.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package main

import (
"log"
"os"
"strings"

tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api"
)

var userTokens = make(map[int64]string) // telegram user ID -> token address

func StartTelegramBot() {
bot, err := tgbotapi.NewBotAPI(os.Getenv("TG_BOT_TOKEN"))
if err != nil {
log.Panic(err)
}
bot.Debug = false

u := tgbotapi.NewUpdate(0)
u.Timeout = 60
updates, _ := bot.GetUpdatesChan(u)

for update := range updates {
if update.Message == nil {
continue
}

userID := update.Message.From.ID
args := strings.Split(update.Message.Text, " ")

switch {
case strings.HasPrefix(update.Message.Text, "/deploy"):
if len(args) != 3 {
bot.Send(tgbotapi.NewMessage(update.Message.Chat.ID, "Usage: /deploy <TokenName> <Symbol>"))
continue
}
name := args[1]
symbol := args[2]

tokenAddress, err := DeployToken(name, symbol)
if err != nil {
bot.Send(tgbotapi.NewMessage(update.Message.Chat.ID, "Error deploying token: "+err.Error()))
} else {
userTokens[int64(userID)] = tokenAddress
bot.Send(tgbotapi.NewMessage(update.Message.Chat.ID, "✅ Token deployed: "+tokenAddress))
}

case strings.HasPrefix(update.Message.Text, "/faucet"):
if len(args) != 2 {
bot.Send(tgbotapi.NewMessage(update.Message.Chat.ID, "Usage: /faucet <address>"))
continue
}
tokenAddr := userTokens[int64(userID)]
if tokenAddr == "" {
bot.Send(tgbotapi.NewMessage(update.Message.Chat.ID, "❌ You need to /deploy a token first"))
continue
}
txHash, err := SendToken(tokenAddr, args[1])
if err != nil {
bot.Send(tgbotapi.NewMessage(update.Message.Chat.ID, "Error: "+err.Error()))
} else {
bot.Send(tgbotapi.NewMessage(update.Message.Chat.ID, "Sent! TX: "+txHash))
}

case strings.HasPrefix(update.Message.Text, "/balance"):
if len(args) != 2 {
bot.Send(tgbotapi.NewMessage(update.Message.Chat.ID, "Usage: /balance <address>"))
continue
}
tokenAddr := userTokens[int64(userID)]
if tokenAddr == "" {
bot.Send(tgbotapi.NewMessage(update.Message.Chat.ID, "❌ You need to /deploy a token first"))
continue
}
balance, err := GetTokenBalance(tokenAddr, args[1])
if err != nil {
bot.Send(tgbotapi.NewMessage(update.Message.Chat.ID, "Error: "+err.Error()))
} else {
bot.Send(tgbotapi.NewMessage(update.Message.Chat.ID, "Balance: "+balance))
}

default:
bot.Send(tgbotapi.NewMessage(update.Message.Chat.ID, "Available commands:\n/deploy <Name> <Symbol>\n/faucet <address>\n/balance <address>"))
}
}
}
Loading