Skip to content
Open
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
11 changes: 11 additions & 0 deletions app/api/telegram-members/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export async function GET() {
try {
const res = await fetch('https://t.me/gudteksolana', { next: { revalidate: 3600 } })
const text = await res.text()
const match = text.match(/tgme_page_extra[^>]*>([^<]+) members/)
const count = match ? parseInt(match[1].replace(/[^0-9]/g, '')) : 0
return Response.json({ count })
} catch (err) {
return Response.json({ count: 0 })
}
}
Comment on lines +1 to +11
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Consider using a more reliable approach than HTML scraping.

Web scraping is fragile and may break when Telegram updates their HTML structure. Additionally, this approach might violate Telegram's terms of service.

Consider these alternatives:

  1. Use the official Telegram Bot API to get channel member count
  2. Add response status validation before parsing
  3. Log errors for debugging purposes

🌐 Web query:

How to get Telegram channel member count using Telegram Bot API?

💡 Result:

To get the member count of a Telegram channel using the Telegram Bot API, you should use the getChatMemberCount method. This provides the total number of members in a channel or group, but does not return a list of user IDs or personal data—only the total count.

Steps to Get Telegram Channel Member Count

  1. Create and add your bot as an admin to the channel (if it’s a private channel, the bot must have permission to see member info).

  2. Obtain your bot token from BotFather.

  3. Make an HTTP GET request to the following endpoint, replacing <TOKEN> with your bot’s token and <CHANNEL_ID> with the username (for public channels) or the full unique channel ID (for private channels):

    https://api.telegram.org/bot<TOKEN>/getChatMemberCount?chat_id=<CHANNEL_ID>
    
    • For example, for a channel with username @example_channel:
      https://api.telegram.org/bot123456789:ABCdefGhIJKlmnoPQRstuVWXyz/getChatMemberCount?chat_id=@example_channel
      
  4. Parse the response: The API will return a JSON object with a result field containing the member count.

Example Response

{
  "ok": true,
  "result": 1523
}

Here, 1523 is the current member count of the channel.

Sample code using JavaScript (fetch):

fetch('https://api.telegram.org/bot<YOUR_BOT_TOKEN>/getChatMemberCount?chat_id=@your_channel_username')
  .then(response => response.json())
  .then(data => {
    console.log('Member count:', data.result);
  });

[1][4][8][10]

Notes and Limitations

  • This method returns only the member count, not the list of members or any identifying info, due to Telegram’s privacy policy[4][8].
  • The bot must be an admin in the channel to use this method in private channels. For public channels, being a member is often sufficient[1].
  • Historical or detailed user information is not accessible via the Bot API; you can only track new join/leave events as they happen, and must store that data yourself if needed[4].
  • If you require deeper analytics or historical data, consider third-party analytics bots like @TGStat_Bot or platforms like TGStat or Combot, but these also face the same privacy limitations for user lists and only provide aggregate statistics[5][7].

In summary:
To get the Telegram channel member count using the Bot API, use the getChatMemberCount method with your bot’s token and the channel’s identifier[1][4][8][10].

Citations:


Use the Telegram Bot API’s getChatMemberCount instead of HTML scraping

Switching to the official Bot API will be far more reliable—and compliant—than parsing Telegram’s page markup.

Key updates for app/api/telegram-members/route.ts (lines 1–11):

  • Pull your bot token and channel identifier from environment variables
  • Call getChatMemberCount via https://api.telegram.org/bot/getChatMemberCount?chat_id=<CHANNEL_ID>
  • Validate both the HTTP status and the API’s ok field before reading result
  • Log errors instead of silently swallowing them
  • Return proper HTTP status codes on failure

Suggested replacement snippet:

export async function GET() {
  const BOT_TOKEN = process.env.TELEGRAM_BOT_TOKEN;
  const CHAT_ID  = process.env.TELEGRAM_CHANNEL_ID;
  if (!BOT_TOKEN || !CHAT_ID) {
    console.error('Missing TELEGRAM_BOT_TOKEN or TELEGRAM_CHANNEL_ID');
    return Response.json({ count: 0 }, { status: 500 });
  }

  try {
    const res = await fetch(
      `https://api.telegram.org/bot${BOT_TOKEN}/getChatMemberCount?chat_id=${CHAT_ID}`
    );
    if (!res.ok) throw new Error(`Telegram API responded ${res.status}`);
    const payload = await res.json();
    if (!payload.ok) throw new Error(`Telegram API error: ${JSON.stringify(payload)}`);
    return Response.json({ count: payload.result });
  } catch (err) {
    console.error('Failed to fetch Telegram member count:', err);
    return Response.json({ count: 0 }, { status: 500 });
  }
}

– Ensure your bot is added as an admin (private channels) or a member (public channels).
– Store TELEGRAM_BOT_TOKEN and TELEGRAM_CHANNEL_ID securely (e.g. in your environment or secrets manager).
– Remove the old HTML‐scraping logic entirely.

🤖 Prompt for AI Agents
In app/api/telegram-members/route.ts lines 1 to 11, replace the current HTML
scraping logic with a call to the Telegram Bot API's getChatMemberCount
endpoint. Retrieve the bot token and channel ID from environment variables,
validate their presence, and use them to construct the API request URL. Check
both the HTTP response status and the API's 'ok' field before extracting the
member count from the 'result' field. Log any errors encountered and return
appropriate HTTP status codes on failure. Remove all existing HTML parsing code
and ensure the bot has the necessary permissions in the channel.

142 changes: 142 additions & 0 deletions app/tokenomics/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
'use client'

import { useEffect, useState } from 'react'
import Navbar from '@/components/Navbar'
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table'
import { Progress } from '@/components/ui/progress'
import { PieChart, Pie, Cell, Tooltip, ResponsiveContainer } from 'recharts'
import { formatTokenBalance } from '@/lib/wallet'
import Link from 'next/link'

const INITIAL_SUPPLY = 1_000_000_000 // 1B tokens

const TEAM_WALLETS = [
{ name: 'Treasury', address: '3xmpZy4SFCp6wzqgk4Mh7jg1yF3ggvG5Xqkt3F9CEbLj', balance: 250_000_000 },
{ name: 'Operations', address: 'CXcG8pN7fT9VqgaJcfH8YzjzD4xfH14RJCdxu2YgSUn9', balance: 50_000_000 },
{ name: 'Marketing', address: '4HwME8YgUCnYJdeL9N2qHcNXTc3swaP42gnikLzeGWih', balance: 30_000_000 },
{ name: 'Liquidity', address: 'HkENe7A1Ji4VwWUvDdDhcW7UZ5EKeosFVJeFt3PcTJSd', balance: 120_000_000 }
]
Comment on lines +13 to +18
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Validate team wallet data structure.

The team wallets contain addresses that should be verified as valid Solana addresses.


🏁 Script executed:

#!/bin/bash
# Check if these addresses follow Solana address format (base58, 32-44 characters)
echo "Validating Solana addresses:"
echo "3xmpZy4SFCp6wzqgk4Mh7jg1yF3ggvG5Xqkt3F9CEbLj" | wc -c
echo "CXcG8pN7fT9VqgaJcfH8YzjzD4xfH14RJCdxu2YgSUn9" | wc -c  
echo "4HwME8YgUCnYJdeL9N2qHcNXTc3swaP42gnikLzeGWih" | wc -c
echo "HkENe7A1Ji4VwWUvDdDhcW7UZ5EKeosFVJeFt3PcTJSd" | wc -c

Length of output: 318


Validate team wallet addresses using Solana PublicKey

The length check shows each string is 45 bytes (including newline), which implies a 44-character base58 string—the upper limit for Solana keys—but length alone doesn’t guarantee validity. Please enforce proper address validation and typing:

• Import and use PublicKey from @solana/web3.js so invalid strings throw at build/runtime.
• Change the address: string entries to address: PublicKey.
• Optionally add a Jest (or preferred framework) test that iterates TEAM_WALLETS and asserts new PublicKey(addr) doesn’t throw.

Example diff:

--- app/tokenomics/page.tsx
+++ app/tokenomics/page.tsx
@@ lines 13-18
- const TEAM_WALLETS = [
-   { name: 'Treasury',  address: '3xmpZy4SFCp6wzqgk4Mh7jg1yF3ggvG5Xqkt3F9CEbLj', balance: 250_000_000 },
+ import { PublicKey } from '@solana/web3.js'
+
+ const TEAM_WALLETS: { name: string; address: PublicKey; balance: number }[] = [
+   { name: 'Treasury',  address: new PublicKey('3xmpZy4SFCp6wzqgk4Mh7jg1yF3ggvG5Xqkt3F9CEbLj'), balance: 250_000_000 },

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In app/tokenomics/page.tsx around lines 13 to 18, the team wallet addresses are
currently plain strings validated only by length, which is insufficient. Import
PublicKey from @solana/web3.js and replace the address string values with
PublicKey instances by wrapping each address string in new PublicKey(). Update
the type of address to PublicKey accordingly. Additionally, create a test that
iterates over TEAM_WALLETS and asserts that constructing a new PublicKey with
each address does not throw an error to ensure validity.


const GOALS = [
{ name: 'Twitter Followers', target: 10000, value: 4200, burn: 0.5 },
{ name: 'Token Holders', target: 5000, value: 1300 },
{ name: 'Telegram Members', target: 2000, value: 800 }
]

export default function TokenomicsPage() {
const [now, setNow] = useState(Date.now())
// pretend the market cap recently crossed $400k
const marketCap = 450_000
const nextThreshold = 500_000
const holdStart = Date.now() - 12 * 60 * 60 * 1000 // halfway through 24h hold

useEffect(() => {
const id = setInterval(() => setNow(Date.now()), 1000)
return () => clearInterval(id)
}, [])

const countdown = Math.max(0, 24 * 60 * 60 * 1000 - (now - holdStart))
const countdownPct = ((24 * 60 * 60 * 1000 - countdown) / (24 * 60 * 60 * 1000)) * 100

const burnedTokens = 150_000_000
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Consider making burned tokens dynamic.

The burned tokens value is hardcoded, but the PR mentions displaying "live data from the Solana blockchain."

Is this hardcoded value intentional for the initial implementation, or should it fetch live burn data from the blockchain?


🏁 Script executed:

#!/bin/bash
# Check if there are other components or API routes that might handle live token data
fd -e tsx -e ts | xargs rg -l "burn.*token|token.*burn" | head -10

Length of output: 193


🏁 Script executed:

#!/bin/bash
# Inspect the API route handling token burns to see if it provides live burn data
cat app/api/game/burn-tokens/route.ts

Length of output: 7728


Implement dynamic burned tokens on the tokenomics page
The magic number 150_000_000 is currently a placeholder. To align with “live data from the Solana blockchain,” you’ll need to replace that hard-coded value with a call to an endpoint that returns the total burned GUDTEK supply—your existing POST /api/game/burn-tokens only verifies individual transactions, it doesn’t aggregate the global burn.

Next steps:

  • Add or extend an API route (e.g. GET /api/game/total-burned) that:
    • Connects to Solana (via @solana/web3.js) to fetch the token mint’s supply and compute burned tokens since initial supply,
    • Or indexes burn instructions program-wide (e.g., via a subgraph or on-chain scan).
  • In app/tokenomics/page.tsx (around line 41), fetch this new endpoint on the server or client, handle loading/error states, and render the returned value instead of 150_000_000.

Location to update:
• app/tokenomics/page.tsx – replace

const burnedTokens = 150_000_000

with a dynamic fetch to your total-burned API.

🤖 Prompt for AI Agents
In app/tokenomics/page.tsx at line 41, replace the hard-coded burnedTokens value
of 150_000_000 with a dynamic fetch from a new or extended API endpoint (e.g.,
GET /api/game/total-burned) that returns the total burned GUDTEK tokens.
Implement this API route to connect to Solana using @solana/web3.js or an
indexing method to calculate the burned tokens from the token mint supply. Then,
in the page component, fetch this data on the server or client side, manage
loading and error states, and render the fetched burned token value instead of
the static number.

const burnedPercent = (burnedTokens / INITIAL_SUPPLY) * 100
const teamTotal = TEAM_WALLETS.reduce((acc, w) => acc + w.balance, 0)
const teamPercent = (teamTotal / INITIAL_SUPPLY) * 100

const pieData = [
{ name: 'Burned', value: burnedPercent },
{ name: 'Team', value: teamPercent }
]
const colors = ['#FF3B3B', '#FFA500']

return (
<div className="min-h-screen bg-gradient-to-br from-orange-400 via-yellow-400 to-orange-500 overflow-hidden text-gray-900">
<div className="fixed inset-0 opacity-10" aria-hidden="true">
<div className="absolute inset-0 bg-[linear-gradient(90deg,#000_1px,transparent_1px),linear-gradient(180deg,#000_1px,transparent_1px)] bg-[size:50px_50px] animate-pulse" />
</div>
<Navbar />

<div className="relative z-10 max-w-5xl mx-auto pt-24 px-4 flex flex-col gap-8">
<h1 className="text-5xl md:text-6xl font-black tracking-tight">Tokenomics</h1>
<p className="text-lg md:text-xl text-gray-800">Live transparency into the GUDTEK ecosystem.</p>

<div className="bg-white/30 rounded-xl border-2 border-gray-900 p-4 shadow-xl">
<ResponsiveContainer width="100%" height={260}>
<PieChart>
<Pie data={pieData} dataKey="value" nameKey="name" innerRadius={60} outerRadius={100} label>
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Improve pie chart accessibility.

The pie chart lacks proper accessibility attributes for screen readers.

-              <Pie data={pieData} dataKey="value" nameKey="name" innerRadius={60} outerRadius={100} label>
+              <Pie 
+                data={pieData} 
+                dataKey="value" 
+                nameKey="name" 
+                innerRadius={60} 
+                outerRadius={100} 
+                label
+                aria-label="Token distribution chart"
+              >
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<Pie data={pieData} dataKey="value" nameKey="name" innerRadius={60} outerRadius={100} label>
<Pie
data={pieData}
dataKey="value"
nameKey="name"
innerRadius={60}
outerRadius={100}
label
aria-label="Token distribution chart"
>
🤖 Prompt for AI Agents
In app/tokenomics/page.tsx at line 66, the Pie chart component lacks
accessibility attributes for screen readers. Add appropriate ARIA attributes
such as aria-label or aria-labelledby to describe the chart, and ensure the
label prop is properly configured to provide accessible labels for each slice.
This will improve screen reader support and overall accessibility.

{pieData.map((_, i) => (
<Cell key={`cell-${i}`} fill={colors[i % colors.length]} />
))}
</Pie>
<Tooltip formatter={(v: number) => v.toFixed(2) + '%'} />
</PieChart>
</ResponsiveContainer>
</div>

<div>
<h2 className="text-2xl font-bold mb-2">Team Wallets</h2>
<Table>
<TableHeader>
<TableRow>
<TableHead>Name</TableHead>
<TableHead>Balance</TableHead>
<TableHead>% Supply</TableHead>
<TableHead>Solscan</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{TEAM_WALLETS.map(w => (
<TableRow key={w.address}>
<TableCell className="font-bold">{w.name}</TableCell>
<TableCell>{formatTokenBalance(w.balance)}</TableCell>
<TableCell>{((w.balance / INITIAL_SUPPLY) * 100).toFixed(3)}%</TableCell>
<TableCell>
<Link href={`https://solscan.io/account/${w.address}`} target="_blank" className="text-blue-600 underline">View</Link>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</div>

<div>
<h2 className="text-2xl font-bold mb-2">Goals</h2>
<Table>
<TableHeader>
<TableRow>
<TableHead>Milestone</TableHead>
<TableHead className="w-32">Progress</TableHead>
<TableHead className="text-right">Target</TableHead>
<TableHead className="text-right">Current</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{GOALS.map(g => (
<TableRow key={g.name}>
<TableCell className="font-bold">{g.name}{g.burn ? ` – Burn ${g.burn}%` : ''}</TableCell>
<TableCell>
<Progress value={Math.min(100, (g.value / g.target) * 100)} />
</TableCell>
<TableCell className="text-right">{g.target.toLocaleString()}</TableCell>
<TableCell className="text-right">{g.value.toLocaleString()}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</div>

<div className="bg-white/30 rounded-xl border-2 border-gray-900 p-4 shadow-xl">
<h2 className="text-xl font-bold mb-2">Next Burn</h2>
<p className="mb-2">1% burn every $100k in market cap (up to $1M) held for 24h.</p>
<p className="mb-2">Current Market Cap: ${marketCap.toLocaleString()}</p>
<p className="mb-2">Threshold: ${nextThreshold.toLocaleString()}</p>
<div className="space-y-1">
<Progress value={countdownPct * 100} />
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix incorrect progress calculation.

The progress value is being multiplied by 100, but the Progress component typically expects values between 0-100, not 0-10000.

-            <Progress value={countdownPct * 100} />
+            <Progress value={countdownPct} />
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<Progress value={countdownPct * 100} />
<Progress value={countdownPct} />
🤖 Prompt for AI Agents
In app/tokenomics/page.tsx at line 134, the Progress component's value is
incorrectly multiplied by 100, resulting in values up to 10000 instead of the
expected 0-100 range. Remove the multiplication by 100 and pass countdownPct
directly to the Progress value prop to fix the progress calculation.

<p className="text-sm">{(countdown / 3600000).toFixed(1)} hrs remaining</p>
</div>
</div>
</div>
</div>
)
}

3 changes: 2 additions & 1 deletion components/Navbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ export default function Navbar() {
{ name: "GudMusic", href: "/music" },
{ name: "GUD AI", href: "/pfp" },
{ name: "Hackathon", href: "/hackathon" },
{ name: "Utility", href: "/utility" }
{ name: "Utility", href: "/utility" },
{ name: "Tokenomics", href: "/tokenomics" },
]

// Close mobile menu when route changes
Expand Down