<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Steven</title>
    <description>The latest articles on DEV Community by Steven (@imcrazysteven).</description>
    <link>https://dev.to/imcrazysteven</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2386681%2Fa70b0695-11e6-4574-9c2e-2f5ff62b3576.png</url>
      <title>DEV Community: Steven</title>
      <link>https://dev.to/imcrazysteven</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/imcrazysteven"/>
    <language>en</language>
    <item>
      <title>Discover Trending Crypto Tokens with GMGN.AI API: A Complete Developer Guide</title>
      <dc:creator>Steven</dc:creator>
      <pubDate>Sat, 24 May 2025 16:21:59 +0000</pubDate>
      <link>https://dev.to/imcrazysteven/discover-trending-crypto-tokens-with-gmgnai-api-a-complete-developer-guide-33fc</link>
      <guid>https://dev.to/imcrazysteven/discover-trending-crypto-tokens-with-gmgnai-api-a-complete-developer-guide-33fc</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdbq1cv2entqc2u5yxvqx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdbq1cv2entqc2u5yxvqx.png" alt=" " width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The cryptocurrency market moves fast, and staying ahead of trends can make all the difference. Whether you're building a trading bot, portfolio tracker, or market analysis tool, having access to real-time token data is crucial. Today, I'll walk you through the &lt;strong&gt;GMGN.AI API&lt;/strong&gt; - a powerful tool for tracking trending tokens across multiple blockchains.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is GMGN.AI API?
&lt;/h2&gt;

&lt;p&gt;GMGN.AI provides a comprehensive API that allows developers to access trending token data across major blockchains including Ethereum, Solana, Base, BSC, and Tron. What sets it apart is its focus on &lt;strong&gt;smart money tracking&lt;/strong&gt; and &lt;strong&gt;safety filters&lt;/strong&gt; to help identify legitimate opportunities while avoiding honeypots and scams.&lt;/p&gt;

&lt;h2&gt;
  
  
  Important Implementation Considerations
&lt;/h2&gt;

&lt;p&gt;⚠️ &lt;strong&gt;Network Protection Notice&lt;/strong&gt;: The GMGN.AI API is protected by advanced security measures including Cloudflare protection. For production applications, you'll need to implement appropriate request handling solutions that can work with protected endpoints. Consider using proxy services, rotating user agents, or specialized HTTP clients that can handle modern web security measures.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Features
&lt;/h2&gt;

&lt;p&gt;🔍 &lt;strong&gt;Multi-chain Support&lt;/strong&gt;: Ethereum, Solana, Base, BSC, and Tron&lt;/p&gt;

&lt;p&gt;📊 &lt;strong&gt;Multiple Sorting Criteria&lt;/strong&gt;: Volume, market cap, holder count, smart money activity, and more&lt;/p&gt;

&lt;p&gt;🛡️ &lt;strong&gt;Safety Filters&lt;/strong&gt;: Built-in honeypot detection, verification status, and ownership renouncement&lt;/p&gt;

&lt;p&gt;⏱️ &lt;strong&gt;Flexible Time Periods&lt;/strong&gt;: From 1-minute to 24-hour trending data&lt;/p&gt;

&lt;p&gt;💡 &lt;strong&gt;Smart Money Insights&lt;/strong&gt;: Track what experienced traders are buying and selling&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting Started
&lt;/h2&gt;

&lt;p&gt;The GMGN.AI API uses a RESTful endpoint structure that's easy to understand and implement:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://gmgn.ai/defi/quotation/v1/rank/{chain}/swaps/{time_period}?orderby={criteria}&amp;amp;direction={direction}&amp;amp;filters[]={safety_filters}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Basic Parameters
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Supported Chains {chain}:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;eth&lt;/code&gt; - Ethereum&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;sol&lt;/code&gt; - Solana
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;base&lt;/code&gt; - Base&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;bsc&lt;/code&gt; - Binance Smart Chain&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;tron&lt;/code&gt; - Tron&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Time Periods {time_period}:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;1m&lt;/code&gt;, &lt;code&gt;5m&lt;/code&gt;, &lt;code&gt;1h&lt;/code&gt;, &lt;code&gt;6h&lt;/code&gt;, &lt;code&gt;24h&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Sorting Criteria {criteria}:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;volume&lt;/code&gt; - 24h trading volume&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;marketcap&lt;/code&gt; - Market capitalization&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;swaps&lt;/code&gt; - Number of transactions&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;holder_count&lt;/code&gt; - Number of holders&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;smartmoney&lt;/code&gt; - Smart money activity&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;liquidity&lt;/code&gt; - Available liquidity&lt;/li&gt;
&lt;li&gt;And many more...&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Direction {direction}:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;asc&lt;/code&gt;: Ascending order&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;desc&lt;/code&gt;: Descending order&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Practical Examples
&lt;/h2&gt;

&lt;p&gt;Let's dive into some real-world use cases with code examples.&lt;/p&gt;

&lt;h3&gt;
  
  
  Level 1: Basic Token Discovery
&lt;/h3&gt;

&lt;h4&gt;
  
  
  High-Volume Ethereum Token Scanner
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;async function getHighVolumeTokens() {
    const url = 'https://gmgn.ai/defi/quotation/v1/rank/eth/swaps/6h?orderby=volume&amp;amp;direction=desc&amp;amp;filters[]=not_honeypot&amp;amp;filters[]=verified&amp;amp;filters[]=renounced';

    // Note: For production use, implement appropriate request handling
    // that can work with protected endpoints
    const requestOptions = {
        method: 'GET',
        headers: {
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
            'Accept': 'application/json',
            'Referer': 'https://gmgn.ai/',
        }
    };

    try {
        const response = await fetch(url, requestOptions);
        const data = await response.json();

        if (data.code === 0) {
            const topTokens = data.data.rank.slice(0, 10);

            topTokens.forEach((token, index) =&amp;gt; {
                console.log(`${index + 1}. ${token.symbol}`);
                console.log(`   Volume: ${token.volume.toLocaleString()}`);
                console.log(`   Price: ${token.price}`);
                console.log(`   Market Cap: ${token.market_cap.toLocaleString()}`);
                console.log(`   Holders: ${token.holder_count}`);
                console.log('---');
            });
        }
    } catch (error) {
        console.error('Error:', error);
        // Consider implementing retry logic with exponential backoff
    }
}

getHighVolumeTokens();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Level 2: Advanced Smart Money Analytics
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Intelligent Smart Money Tracker
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import requests
import pandas as pd
import time
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry

def create_session_with_retries():
    """Create a requests session with retry strategy and proper headers"""
    session = requests.Session()

    # Configure retry strategy
    retry_strategy = Retry(
        total=3,
        backoff_factor=1,
        status_forcelist=[429, 500, 502, 503, 504],
    )

    adapter = HTTPAdapter(max_retries=retry_strategy)
    session.mount("http://", adapter)
    session.mount("https://", adapter)

    # Set headers that work better with protected endpoints
    session.headers.update({
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36',
        'Accept': 'application/json, text/plain, */*',
        'Accept-Language': 'en-US,en;q=0.9',
        'Referer': 'https://gmgn.ai/',
        'Origin': 'https://gmgn.ai'
    })

    return session

def track_smart_money(chain='sol', time_period='24h'):
    url = f"https://gmgn.ai/defi/quotation/v1/rank/{chain}/swaps/{time_period}"
    params = {
        'orderby': 'smartmoney',
        'direction': 'desc',
        'filters[]': ['not_honeypot', 'verified', 'renounced']
    }

    session = create_session_with_retries()

    try:
        response = session.get(url, params=params, timeout=30)
        data = response.json()

        if data['code'] == 0:
            tokens = data['data']['rank']

            # Create DataFrame for analysis
            df = pd.DataFrame([{
                'symbol': token['symbol'],
                'price': token['price'],
                'smart_buys': token['smart_buy_24h'],
                'smart_sells': token['smart_sell_24h'],
                'smart_ratio': token['smart_buy_24h'] / max(token['smart_sell_24h'], 1),
                'volume': token['volume'],
                'holders': token['holder_count']
            } for token in tokens[:20]])

            # Filter for tokens with strong smart money interest
            hot_tokens = df[df['smart_ratio'] &amp;gt; 3]  # More smart buys than sells

            print("🔥 Tokens with Strong Smart Money Interest:")
            print(hot_tokens.to_string(index=False))

            return hot_tokens

    except requests.exceptions.RequestException as e:
        print(f"Request failed: {e}")
        # In production, consider implementing alternative data sources

    return None

# Usage
smart_tokens = track_smart_money('sol', '6h')
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Level 3: Enterprise-Grade API Client
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Production-Ready GMGN Client
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class GMGNApiClient {
    constructor() {
        this.baseUrl = 'https://gmgn.ai/defi/quotation/v1/rank';
        this.defaultHeaders = {
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
            'Accept': 'application/json',
            'Accept-Language': 'en-US,en;q=0.9',
            'Referer': 'https://gmgn.ai/',
            'Cache-Control': 'no-cache'
        };
        this.requestQueue = [];
        this.isProcessing = false;
    }

    async makeRequest(url, options = {}) {
        return new Promise((resolve, reject) =&amp;gt; {
            this.requestQueue.push({ url, options, resolve, reject });
            this.processQueue();
        });
    }

    async processQueue() {
        if (this.isProcessing || this.requestQueue.length === 0) return;

        this.isProcessing = true;

        while (this.requestQueue.length &amp;gt; 0) {
            const { url, options, resolve, reject } = this.requestQueue.shift();

            try {
                // Add delay between requests to avoid rate limiting
                await this.delay(1000);

                const response = await fetch(url, {
                    ...options,
                    headers: { ...this.defaultHeaders, ...options.headers }
                });

                if (!response.ok) {
                    throw new Error(`HTTP ${response.status}: ${response.statusText}`);
                }

                const data = await response.json();
                resolve(data);

            } catch (error) {
                console.warn(`Request failed, retrying: ${error.message}`);

                // Implement exponential backoff for retries
                await this.delay(2000);

                try {
                    const retryResponse = await fetch(url, {
                        ...options,
                        headers: { ...this.defaultHeaders, ...options.headers }
                    });

                    if (retryResponse.ok) {
                        const data = await retryResponse.json();
                        resolve(data);
                    } else {
                        reject(new Error(`Retry failed: ${retryResponse.status}`));
                    }
                } catch (retryError) {
                    reject(retryError);
                }
            }
        }

        this.isProcessing = false;
    }

    delay(ms) {
        return new Promise(resolve =&amp;gt; setTimeout(resolve, ms));
    }

    async getTrendingTokens(chain, timePeriod, orderBy = 'volume', filters = ['not_honeypot']) {
        const params = new URLSearchParams({
            orderby: orderBy,
            direction: 'desc'
        });

        filters.forEach(filter =&amp;gt; params.append('filters[]', filter));

        const url = `${this.baseUrl}/${chain}/swaps/${timePeriod}?${params}`;

        return this.makeRequest(url);
    }
}

// Usage example
const apiClient = new GMGNApiClient();

async function findNewGems(chain = 'base') {
    try {
        const data = await apiClient.getTrendingTokens(
            chain, 
            '1h', 
            'open_timestamp', 
            ['not_honeypot', 'verified', 'renounced']
        );

        if (data.code === 0) {
            const currentTime = Math.floor(Date.now() / 1000);

            const newTokens = data.data.rank.filter(token =&amp;gt; {
                const tokenAge = currentTime - token.open_timestamp;
                return tokenAge &amp;lt;= 3600 &amp;amp;&amp;amp; // Less than 1 hour old
                       token.holder_count &amp;gt; 50 &amp;amp;&amp;amp; 
                       token.volume &amp;gt; 10000;
            });

            console.log('💎 New Gems Found:', newTokens.length);
            return newTokens;
        }
    } catch (error) {
        console.error('Failed to fetch new gems:', error);
        return [];
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Best Practices for Production Use
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Implement Robust Error Handling
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class APIErrorHandler {
    static async handleRequest(requestFn, maxRetries = 3) {
        for (let attempt = 1; attempt &amp;lt;= maxRetries; attempt++) {
            try {
                return await requestFn();
            } catch (error) {
                console.log(`Attempt ${attempt} failed:`, error.message);

                if (attempt === maxRetries) {
                    throw new Error(`All ${maxRetries} attempts failed: ${error.message}`);
                }

                // Exponential backoff with jitter
                const delay = Math.min(1000 * Math.pow(2, attempt) + Math.random() * 1000, 30000);
                await new Promise(resolve =&amp;gt; setTimeout(resolve, delay));
            }
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Request Rate Limiting
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class RateLimiter {
    constructor(requestsPerMinute = 30) {
        this.requests = [];
        this.limit = requestsPerMinute;
    }

    async waitIfNeeded() {
        const now = Date.now();
        this.requests = this.requests.filter(time =&amp;gt; now - time &amp;lt; 60000);

        if (this.requests.length &amp;gt;= this.limit) {
            const oldestRequest = Math.min(...this.requests);
            const waitTime = 60000 - (now - oldestRequest) + 1000; // Add 1s buffer
            console.log(`Rate limit reached, waiting ${waitTime}ms`);
            await new Promise(resolve =&amp;gt; setTimeout(resolve, waitTime));
        }

        this.requests.push(now);
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Data Caching Strategy
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class CacheManager {
    constructor(ttl = 60000) { // 1 minute default TTL
        this.cache = new Map();
        this.ttl = ttl;
    }

    set(key, value) {
        this.cache.set(key, {
            value,
            timestamp: Date.now()
        });
    }

    get(key) {
        const item = this.cache.get(key);

        if (!item) return null;

        if (Date.now() - item.timestamp &amp;gt; this.ttl) {
            this.cache.delete(key);
            return null;
        }

        return item.value;
    }

    clear() {
        this.cache.clear();
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;The GMGN.AI API is a powerful tool for cryptocurrency developers and traders. Its combination of multi-chain support, safety filters, and smart money insights makes it invaluable for building sophisticated trading tools and market analysis applications.&lt;/p&gt;

&lt;p&gt;Key takeaways:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ Always use safety filters to avoid honeypots&lt;/li&gt;
&lt;li&gt;✅ Pay attention to smart money metrics for better insights&lt;/li&gt;
&lt;li&gt;✅ Implement proper error handling and rate limiting&lt;/li&gt;
&lt;li&gt;✅ Combine multiple sorting criteria for comprehensive analysis&lt;/li&gt;
&lt;li&gt;✅ Use liquidity lock information to assess token safety&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Whether you're building a trading bot, portfolio tracker, or market analysis tool, the GMGN.AI API provides the real-time data you need to stay ahead in the fast-moving crypto market.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Have you used the GMGN.AI API in your projects? Share your experiences and use cases in the comments below!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Useful Links:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://gmgn.ai" rel="noopener noreferrer"&gt;GMGN.AI Platform&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/imcrazysteven/GMGN-API" rel="noopener noreferrer"&gt;API Documentation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>gmgn</category>
      <category>defi</category>
      <category>crypto</category>
      <category>token</category>
    </item>
    <item>
      <title>Building a Powerful EVM Token Analysis Telegram Bot</title>
      <dc:creator>Steven</dc:creator>
      <pubDate>Tue, 22 Apr 2025 07:46:10 +0000</pubDate>
      <link>https://dev.to/imcrazysteven/building-a-powerful-evm-token-analysis-telegram-bot-39og</link>
      <guid>https://dev.to/imcrazysteven/building-a-powerful-evm-token-analysis-telegram-bot-39og</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In today's fast-paced crypto world, having real-time insights into token metrics can make the difference between a successful trade and a missed opportunity. While many traders rely on scattered data sources and manual research, what if you could access comprehensive token analytics directly through Telegram?&lt;/p&gt;

&lt;p&gt;That's exactly what the &lt;strong&gt;EVM Token Analysis Bot&lt;/strong&gt; provides — a powerful Telegram-based solution that delivers detailed token insights across multiple EVM-compatible blockchains including Ethereum, Binance Smart Chain (BSC), and Base.&lt;/p&gt;

&lt;p&gt;In this article, I'll walk you through the architecture, features, and implementation details of this advanced blockchain analytics bot that makes on-chain data analysis accessible to everyone.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fubp8g39z76su4be1ay2m.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fubp8g39z76su4be1ay2m.PNG" alt=" " width="446" height="602"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Token Analysis Matters in DeFi
&lt;/h2&gt;

&lt;p&gt;The decentralized finance landscape is incredibly dynamic, with thousands of tokens launching across multiple chains daily. For traders and investors, understanding token metrics is crucial for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Identifying early opportunities&lt;/strong&gt; – Finding promising tokens before they gain mainstream attention&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Avoiding scams&lt;/strong&gt; – Detecting red flags in token contracts and deployer behavior&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Understanding market dynamics&lt;/strong&gt; – Tracking whale movements and profit patterns&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Making data-driven decisions&lt;/strong&gt; – Basing trades on actual on-chain metrics rather than speculation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;However, gathering this data traditionally requires:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Navigating multiple block explorers&lt;/li&gt;
&lt;li&gt;Using various analytics platforms&lt;/li&gt;
&lt;li&gt;Understanding complex blockchain data&lt;/li&gt;
&lt;li&gt;Spending hours on manual research&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;strong&gt;EVM Token Analysis Bot&lt;/strong&gt; solves these challenges by bringing comprehensive token analytics directly to Telegram, making powerful insights available with just a few clicks.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Features of the EVM Token Analysis Bot
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Free Features
&lt;/h3&gt;

&lt;p&gt;The bot offers several powerful features even to free users:&lt;/p&gt;

&lt;h4&gt;
  
  
  First Buyers &amp;amp; Profits Analysis
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;View the first 1–50 wallets that bought a token&lt;/li&gt;
&lt;li&gt;Detailed statistics including buy &amp;amp; sell amounts&lt;/li&gt;
&lt;li&gt;Total trades, PNL, and win rate metrics&lt;/li&gt;
&lt;li&gt;Limited to 3 token scans per day for free users&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Most Profitable Wallets
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Discover which wallets have made the most profit from a specific token&lt;/li&gt;
&lt;li&gt;View buy &amp;amp; sell totals and net profit&lt;/li&gt;
&lt;li&gt;Identify successful trading patterns&lt;/li&gt;
&lt;li&gt;Limited to 3 token scans per day for free users&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Market Cap &amp;amp; ATH Analysis
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;View the all-time high market cap of any token&lt;/li&gt;
&lt;li&gt;See ATH date and percentage from ATH&lt;/li&gt;
&lt;li&gt;Track market cap evolution&lt;/li&gt;
&lt;li&gt;Limited to 3 token scans per day for free users&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Premium Features
&lt;/h3&gt;

&lt;p&gt;For power users, the premium tier unlocks advanced capabilities:&lt;/p&gt;

&lt;h4&gt;
  
  
  Deployer Wallet Scan
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Reveal the deployer wallet address&lt;/li&gt;
&lt;li&gt;See all tokens deployed by the same wallet&lt;/li&gt;
&lt;li&gt;View ATH market cap and x-multipliers for each token&lt;/li&gt;
&lt;li&gt;Identify potential connections between projects&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Top Holders &amp;amp; Whale Watch
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;See the top 10 holders of any token&lt;/li&gt;
&lt;li&gt;Monitor whale wallets&lt;/li&gt;
&lt;li&gt;Get notifications when Dev, whales, or top holders sell&lt;/li&gt;
&lt;li&gt;Track significant wallet movements&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  High Net Worth Holders
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Scan for wallets holding over $10,000 worth of a token&lt;/li&gt;
&lt;li&gt;View total worth in USD and token amount&lt;/li&gt;
&lt;li&gt;See average holding time&lt;/li&gt;
&lt;li&gt;Identify potential market movers&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Unlimited Access
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Premium subscribers get unlimited access to all features&lt;/li&gt;
&lt;li&gt;No daily scan limits&lt;/li&gt;
&lt;li&gt;Priority data processing&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Technical Architecture
&lt;/h2&gt;

&lt;p&gt;The EVM Token Analysis Bot is built with a modular architecture that ensures reliability, scalability, and efficient data processing:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Multi-Chain Support
&lt;/h3&gt;

&lt;p&gt;The bot supports multiple EVM-compatible blockchains:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ethereum Mainnet&lt;/li&gt;
&lt;li&gt;Binance Smart Chain&lt;/li&gt;
&lt;li&gt;Base Network&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each chain connection is initialized with appropriate middleware and configuration.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def _initialize_web3(self, network: str) -&amp;gt; Web3:
    """Initialize Web3 connection for the specified network."""
    provider_url = NETWORKS[network]["provider_url"]
    logger.info(f"Initializing Web3 for {network} with provider URL: {provider_url}")

    web3 = Web3(Web3.HTTPProvider(provider_url))

    # Add PoA middleware for networks like BSC
    web3.middleware_onion.inject(ExtraDataToPOAMiddleware, layer=0)

    # For BNB Chain (BSC), we need additional configuration
    if network == "BNB":
        # BSC uses a different gas price strategy
        from web3.gas_strategies.rpc import rpc_gas_price_strategy
        web3.eth.set_gas_price_strategy(rpc_gas_price_strategy)

    if not web3.is_connected():
        logger.error(f"Failed to connect to {network} network")
        raise ConnectionError(f"Cannot connect to {network} network")

    logger.info(f"Connected to {network} network: Chain ID {web3.eth.chain_id}")
    return web3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Data Models
&lt;/h3&gt;

&lt;p&gt;The bot uses structured data models to ensure consistency and reliability:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class TokenData:
    """Model for cached token data"""
    def __init__(
        self,
        address: str,
        name: Optional[str] = None,
        symbol: Optional[str] = None,
        deployer: Optional[str] = None,
        deployment_date: Optional[datetime] = None,
        current_price: Optional[float] = None,
        current_market_cap: Optional[float] = None,
        ath_market_cap: Optional[float] = None,
        ath_date: Optional[datetime] = None,
        last_updated: Optional[datetime] = None
    ):
        self.address = address
        self.name = name
        self.symbol = symbol
        self.deployer = deployer
        self.deployment_date = deployment_date
        self.current_price = current_price
        self.current_market_cap = current_market_cap
        self.ath_market_cap = ath_market_cap
        self.ath_date = ath_date
        self.last_updated = last_updated or datetime.now()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Database Integration
&lt;/h3&gt;

&lt;p&gt;The bot uses MongoDB for efficient data storage and retrieval:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def init_database() -&amp;gt; bool:
    """Initialize the database connection and set up indexes"""
    global _db

    try:
        # Connect to MongoDB
        client = MongoClient(MONGODB_URI) 
        _db = client[DB_NAME]

        # Set up indexes for collections
        # Users collection
        _db.users.create_index([("user_id", ASCENDING)], unique=True)

        # User scans collection
        _db.user_scans.create_index([
            ("user_id", ASCENDING),
            ("scan_type", ASCENDING),
            ("date", ASCENDING)
        ], unique=True)

        # Token data collection
        _db.token_data.create_index([("address", ASCENDING)], unique=True)
        _db.token_data.create_index([("deployer", ASCENDING)])

        # Additional indexes for watchlists
        # ...

        return True
    except Exception as e:
        logging.error(f"Failed to initialize database: {e}")
        return False
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. Subscription Management
&lt;/h3&gt;

&lt;p&gt;The bot implements a subscription system to manage free and premium users:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def check_subscription_payment(self, user_id: int) -&amp;gt; Dict[str, Any]:
    if user_id not in self.subscriptions:
        logger.warning(f"No subscription found for user {user_id}")
        return {
            "success": False,
            "error": {
                "code": "SUBSCRIPTION_NOT_FOUND",
                "message": f"No subscription found for user {user_id}"
            }
        }

    subscription = self.subscriptions[user_id]
    network = subscription["network"]
    address = subscription["wallet_address"]
    required_amount = subscription["amount_required"]

    balance_result = self.check_wallet_balance(address, network)
    if not balance_result["success"]:
        return balance_result

    current_balance = balance_result["data"]["native_balance"]
    formatted_balance = balance_result["data"]["formatted_native_balance"]

    # Process payment logic
    # ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  5. Telegram Bot Interface
&lt;/h3&gt;

&lt;p&gt;The bot provides an intuitive Telegram interface with rich menus and callbacks:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;async def handle_start_menu(update: Update, context: ContextTypes.DEFAULT_TYPE) -&amp;gt; None:
    """Handle the main menu display with only token analysis functionality"""

    if "default_network" not in context.user_data:
        context.user_data["default_network"] = "eth"

    selected_network = context.user_data.get("default_network")
    network_display = {
        "eth": "🌐 Ethereum",
        "base": "🛡️ Base",
        "bsc": "🔶 BSC"
    }

    welcome_message = (
        f"🆘 &amp;lt;b&amp;gt;Welcome to EVM Token Analysis Bot, {update.effective_user.first_name}! 🎉&amp;lt;/b&amp;gt;\n\n"
        f"📊 &amp;lt;b&amp;gt;Token Analysis Features:&amp;lt;/b&amp;gt;\n"
        # Feature list...
    )

    token_analysis_keyboard = [
        [InlineKeyboardButton("🛒 First Buyers &amp;amp; Profits of a token", callback_data="token_first_buyers")],
        [InlineKeyboardButton("💰 Most Profitable Wallets of a token", callback_data="token_most_profitable_wallets")],
        # Additional buttons...
    ]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  6. Error Handling
&lt;/h3&gt;

&lt;p&gt;The bot implements robust error handling to ensure reliability:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;async def handle_telegram_error(update: Optional[Update], context: ContextTypes.DEFAULT_TYPE) -&amp;gt; None:
    """Handle generic TelegramError errors."""
    if update and update.effective_message:
        error_message = "An error occurred while processing your request. Please try again later."

        try:
            await update.effective_message.reply_text(error_message)
        except Exception as e:
            logger.error(f"Failed to send error message: {e}")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Implementation Highlights
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Rate Limiting for Free Users
&lt;/h3&gt;

&lt;p&gt;The bot implements a fair usage policy for free users:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;async def handle_period_selection(
    update: Update, 
    context: ContextTypes.DEFAULT_TYPE,
    feature_info:str, 
    scan_type: str,
    callback_prefix: str
) -&amp;gt; None:
    """Generic handler for period selection"""
    query = update.callback_query
    user = await check_callback_user(update)

    # Check if user has reached daily limit
    has_reached_limit, current_count = await check_rate_limit_service(
        user.user_id, scan_type, FREE_WALLET_SCANS_DAILY
    )

    if has_reached_limit and not user.is_premium:
        keyboard = [
            [InlineKeyboardButton("💎 Upgrade to Premium", callback_data="premium_info")],
            [InlineKeyboardButton("🔙 Back", callback_data="wallet_analysis")]
        ]
        reply_markup = InlineKeyboardMarkup(keyboard)

        await query.message.reply_text(
            f"⚠️ &amp;lt;b&amp;gt;Daily Limit Reached&amp;lt;/b&amp;gt;\n\n"
            # Limit message...
            f"To unlock unlimited access to powerful wallet analysis features, upgrade to &amp;lt;b&amp;gt;Premium&amp;lt;/b&amp;gt; and explore the full potential of on-chain intelligence. 💎\n",
            reply_markup=reply_markup,
            parse_mode=ParseMode.HTML
        )
        return
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Premium Feature Access Control
&lt;/h3&gt;

&lt;p&gt;The bot checks user subscription status before allowing access to premium features:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;async def handle_high_net_worth_holders(update: Update, context: ContextTypes.DEFAULT_TYPE) -&amp;gt; None:
    """Handle high net worth token holders button callback"""
    query = update.callback_query
    user = await check_callback_user(update)

    # Check if user is premium
    if not user.is_premium:
        keyboard = [
            [InlineKeyboardButton("💎 Upgrade to Premium", callback_data="premium_info")],
            [InlineKeyboardButton("🔙 Back", callback_data="main_menu")]
        ]
        reply_markup = InlineKeyboardMarkup(keyboard)

        await query.message.reply_text(
            "⭐ &amp;lt;b&amp;gt;Premium Feature&amp;lt;/b&amp;gt;\n\n"
            "&amp;lt;b&amp;gt;💎 High Net Worth Holders&amp;lt;/b&amp;gt; is an exclusive premium feature that identifies:\n"
            # Feature description...
            "💎 &amp;lt;b&amp;gt;Upgrade to Premium&amp;lt;/b&amp;gt; for unlimited access to this and other advanced features.",
            reply_markup=reply_markup,
            parse_mode=ParseMode.HTML
        )
        return

    # If premium, proceed with feature
    await handle_token_analysis_token_input(update, context, "high_net_worth_holders")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Admin Management Features
&lt;/h3&gt;

&lt;p&gt;The bot includes administrative capabilities for managing users and subscriptions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;async def handle_admin_add_premium(update: Update, context: ContextTypes.DEFAULT_TYPE) -&amp;gt; None:
    """Admin command to manually add premium to a user"""
    # Check if user is admin
    user_id = update.effective_user.id
    user = get_user(user_id)

    if not user or not hasattr(user, 'is_admin') or not user.is_admin:
        await update.message.reply_text("⛔ This command is only available to administrators.")
        return

    # Command logic for adding premium access
    # ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Setting Up Your Own EVM Token Analysis Bot
&lt;/h2&gt;

&lt;p&gt;Want to run your own instance of the EVM Token Analysis Bot? Here's how to get started:&lt;/p&gt;

&lt;h3&gt;
  
  
  Prerequisites
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Python 3.8+&lt;/li&gt;
&lt;li&gt;Telegram Bot Token (from &lt;code&gt;@BotFather&lt;/code&gt; on Telegram)&lt;/li&gt;
&lt;li&gt;Dune Analytics API credentials&lt;/li&gt;
&lt;li&gt;MongoDB database&lt;/li&gt;
&lt;li&gt;API keys for blockchain providers&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Installation Steps
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;1. Clone the repository:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git clone https://github.com/imcrazysteven/EVM-Token-Analysis-Telegram-Bot.git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;2.  Navigate to the project directory:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cd EVM-Token-Analysis-Telegram-Bot
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;3. Install dependencies:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pip install -r requirements.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;4. Create a &lt;code&gt;.env&lt;/code&gt; file with your credentials:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# DB Configuration&lt;/span&gt;
&lt;span class="nv"&gt;MONGODB_URI&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"mongodb://localhost:27017/"&lt;/span&gt;
&lt;span class="nv"&gt;DB_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"evm_token_analysis"&lt;/span&gt;

&lt;span class="c"&gt;# API Keys&lt;/span&gt;
&lt;span class="nv"&gt;DUNE_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;your_dune_api_key_here
&lt;span class="nv"&gt;ZENROWS_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;your_zenrows_api_key_here
&lt;span class="nv"&gt;MORALIS_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;your_moralis_api_key_here

&lt;span class="c"&gt;# ===== BLOCKCHAIN CONFIGURATION =====&lt;/span&gt;
&lt;span class="c"&gt;# Ethereum Mainnet&lt;/span&gt;
&lt;span class="nv"&gt;ETH_PROVIDER_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;https://mainnet.infura.io/v3/your_infura_key_here
&lt;span class="nv"&gt;ETH_CHAIN_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1
&lt;span class="nv"&gt;ETH_ADMIN_WALLET&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;your_admin_wallet_address

&lt;span class="c"&gt;# Binance Smart Chain Mainnet&lt;/span&gt;
&lt;span class="nv"&gt;BNB_PROVIDER_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;https://bsc-dataseed.binance.org/
&lt;span class="nv"&gt;BNB_CHAIN_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;56
&lt;span class="nv"&gt;BNB_ADMIN_WALLET&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;your_admin_wallet_address

&lt;span class="c"&gt;# Subscription Check Interval (in seconds)&lt;/span&gt;
&lt;span class="nv"&gt;CHECK_INTERVAL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;60

&lt;span class="c"&gt;# TELEGRAM BOT SETTING&lt;/span&gt;
&lt;span class="nv"&gt;TELEGRAM_TOKEN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;your_telegram_bot_token_here

&lt;span class="c"&gt;# Free tier limits&lt;/span&gt;
&lt;span class="nv"&gt;FREE_TOKEN_SCANS_DAILY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;3
&lt;span class="nv"&gt;FREE_WALLET_SCANS_DAILY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;3
&lt;span class="nv"&gt;FREE_PROFITABLE_WALLETS_LIMIT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;5

&lt;span class="c"&gt;# Premium tier limits&lt;/span&gt;
&lt;span class="nv"&gt;PREMIUM_TOKEN_SCANS_DAILY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;100
&lt;span class="nv"&gt;PREMIUM_WALLET_SCANS_DAILY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;100
&lt;span class="nv"&gt;PREMIUM_PROFITABLE_WALLETS_LIMIT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;50

&lt;span class="c"&gt;# Additional API keys&lt;/span&gt;
&lt;span class="nv"&gt;WEB3_PROVIDER_URI&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;https://mainnet.infura.io/v3/your_infura_key_here
&lt;span class="nv"&gt;ETHERSCAN_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;your_etherscan_api_key_here
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;5. Start the bot:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;python src/main.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Advanced Features to Consider
&lt;/h2&gt;

&lt;p&gt;Once you've implemented the core functionality, consider these enhancements to further improve the bot's utility:&lt;/p&gt;

&lt;h4&gt;
  
  
  Cross-Chain Token Correlation
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Identify tokens deployed by the same entity across multiple chains&lt;/li&gt;
&lt;li&gt;Track related token performance&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Sentiment Analysis Integration
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Combine on-chain data with social media sentiment&lt;/li&gt;
&lt;li&gt;Provide a more complete picture of token prospects&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Predictive Analytics
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Use historical data to identify patterns&lt;/li&gt;
&lt;li&gt;Provide probability-based insights on token performance&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Custom Alert Thresholds
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Allow users to set custom thresholds for notifications&lt;/li&gt;
&lt;li&gt;Personalize the analysis experience&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Integration with Trading Platforms
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Connect with DEX aggregators for direct trading&lt;/li&gt;
&lt;li&gt;Enable one-click trading based on analysis&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;The &lt;strong&gt;EVM Token Analysis Telegram Bot&lt;/strong&gt; serves as your personal on-chain intelligence assistant—transforming complex blockchain data into actionable insights and giving you a competitive edge in the fast-moving crypto markets.&lt;/p&gt;

&lt;p&gt;This powerful tool converts raw blockchain data into comprehensible analytics, delivering real-time token insights across multiple EVM-compatible networks including Ethereum, BSC, and Base. By combining sophisticated token analysis with convenient Telegram delivery, this bot provides crypto traders, investors, and researchers with the critical information needed to navigate the complex DeFi landscape.&lt;/p&gt;

&lt;p&gt;The modular architecture and MongoDB integration ensure both performance and historical data preservation, while the intuitive Telegram interface makes advanced blockchain analytics accessible even to users without technical blockchain knowledge. The ability to analyze tokens across multiple chains in a single interface eliminates the need to juggle various block explorers and analytics platforms.&lt;/p&gt;

&lt;p&gt;The bot's tiered access model ensures that essential analytics are available to everyone while providing premium capabilities for serious traders and researchers. Features like first buyer analysis, profit tracking, and deployer wallet scanning provide unprecedented visibility into token metrics that would otherwise require hours of manual research.&lt;/p&gt;

&lt;p&gt;As blockchain ecosystems continue to evolve and fragment across multiple chains, tools that provide cross-chain visibility become increasingly valuable. Whether you're researching potential investments, monitoring existing holdings, or tracking market-moving whale wallets, this bot serves as your personal blockchain analyst, working around the clock to deliver the insights you need.&lt;/p&gt;

&lt;p&gt;By implementing the EVM Token Analysis Bot, you'll gain a significant advantage in the information-rich but often opaque world of decentralized finance. The detailed token metrics and wallet analysis transform raw blockchain data into the kind of actionable intelligence that can make the difference between capturing opportunities and missing them entirely.&lt;/p&gt;

&lt;p&gt;In a market where information asymmetry often determines success, having a dedicated token analysis tool delivering insights directly to your Telegram isn't just convenient—it's a strategic necessity for serious participants in the blockchain economy.&lt;/p&gt;

&lt;h2&gt;
  
  
  📁 GitHub Repository
&lt;/h2&gt;

&lt;p&gt;🔗 &lt;a href="https://github.com/imcrazysteven/EVM-Token-Analysis-Telegram-Bot" rel="noopener noreferrer"&gt;View Source Code on GitHub&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can see the sample video there.&lt;br&gt;
If you want to see the whole code or develop EVM Token Analysis telegram bot on your own, please contact me with the contact information below.&lt;br&gt;
Please don’t forget to leave a star if you like the bot.&lt;/p&gt;

&lt;h2&gt;
  
  
  Contact Information
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;GitHub: &lt;a href="https://github.com/imcrazysteven" rel="noopener noreferrer"&gt;Steven&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Gmail: &lt;a href="//imcrazysteven143@gmail.com"&gt;Steven&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Telegram: &lt;a href="https://t.me/imcrazysteven" rel="noopener noreferrer"&gt;Steven&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Twitter: &lt;a href="https://x.com/imcrazysteven" rel="noopener noreferrer"&gt;Steven&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Instagram: &lt;a href="https://instagram.com/imcrazysteven" rel="noopener noreferrer"&gt;Steven&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>evm</category>
      <category>telegrambot</category>
      <category>tokenanalysis</category>
    </item>
    <item>
      <title>How to develop professional EVM Wallet Tracking Telegram Bot</title>
      <dc:creator>Steven</dc:creator>
      <pubDate>Mon, 21 Apr 2025 04:27:09 +0000</pubDate>
      <link>https://dev.to/imcrazysteven/how-to-develop-professional-evm-wallet-tracking-telegram-bot-587m</link>
      <guid>https://dev.to/imcrazysteven/how-to-develop-professional-evm-wallet-tracking-telegram-bot-587m</guid>
      <description>&lt;p&gt;This article gives you a comprehensive guide to building a professional EVM (Etherum, Polygon, Arbitrum, Base, BSC, etc) wallet monitoring system that monitors transactions, and delivers real-time notifications through Telegram.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2vxq306lw11jb3g0cf8j.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2vxq306lw11jb3g0cf8j.PNG" alt=" " width="437" height="288"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  🧠 Introduction to EVM Wallet Tracking Bot
&lt;/h2&gt;

&lt;p&gt;This guide will walk you through creating a sophisticated wallet tracking bot that monitors transactions across multiple EVM chains (Ethereum, Polygon, Arbitrum, Base, BSC, etc.) and sends detailed notifications via Telegram. By the end, you'll have a powerful tool that provides instant alerts about token purchases, sales, swaps, transfers, and other on-chain activities.&lt;/p&gt;

&lt;p&gt;Unlike basic monitoring tools that only show balance changes, this professional-grade solution breaks down transactions with full context, delivering human-readable alerts directly to your Telegram. With support for multi-wallet and multi-chain tracking, historical data storage via MongoDB, and an intuitive command interface, this bot serves as your personal blockchain analyst, giving you the tactical edge needed to make informed decisions in real-time.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why Wallet Monitoring Matters in DeFi
&lt;/h3&gt;

&lt;p&gt;In the high-stakes, real-time world of DeFi, staying ahead of market movements and tracking key wallets is no longer a luxury—it's a necessity. With thousands of wallets making transactions every second across multiple chains like Ethereum, BSC, and Polygon, traders and developers need instant visibility into wallet activities.&lt;/p&gt;

&lt;p&gt;Imagine this: You're a crypto trader watching a major whale wallet. Suddenly, the wallet dumps a massive amount of tokens. If you see this minutes later, it's already too late. But what if you got notified the second it happened? That’s where the &lt;strong&gt;EVM Wallet Tracking Telegram Bot&lt;/strong&gt; comes into play.&lt;/p&gt;

&lt;p&gt;This tool lets you track wallet activities across EVM-compatible blockchains in real time. Whether you’re a DeFi enthusiast, investor, security analyst, or just a curious blockchain explorer, this bot gives you a tactical edge with up-to-the-second notifications.&lt;/p&gt;

&lt;h3&gt;
  
  
  What Makes This Bot Different?
&lt;/h3&gt;

&lt;p&gt;Most bots just show wallet balance changes, but this one breaks it down with full context:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Detects &lt;strong&gt;sells, buys, swaps&lt;/strong&gt;, and &lt;strong&gt;contract interactions&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Sends formatted, human-readable alerts to &lt;strong&gt;Telegram&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Supports &lt;strong&gt;multi-wallet&lt;/strong&gt; and &lt;strong&gt;multi-chain&lt;/strong&gt; tracking&lt;/li&gt;
&lt;li&gt;Offers &lt;strong&gt;historical data&lt;/strong&gt; storage via MongoDB&lt;/li&gt;
&lt;li&gt;Easy command-line interface through Telegram bot commands&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It's not just a tracker—it's your personal blockchain analyst, sliding into your Telegram DMs.&lt;/p&gt;

&lt;h2&gt;
  
  
  🤖 Setting Up the Telegram Bot
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Creating Your Telegram Bot with BotFather
&lt;/h3&gt;

&lt;p&gt;First things first: you need your own Telegram bot. Setting it up is easy:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open Telegram and search for &lt;code&gt;@BotFather&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Send &lt;code&gt;/newbot&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Give it a name like &lt;code&gt;EVM Wallet Tracker&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Choose a unique username ending in &lt;code&gt;bot&lt;/code&gt;, such as &lt;code&gt;evmwallettrackbot&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;BotFather will return a &lt;strong&gt;Bot Token&lt;/strong&gt; — save this securely!&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Understanding Chat IDs &amp;amp; Bot Tokens
&lt;/h3&gt;

&lt;p&gt;To send messages to yourself or a group, your bot needs to know &lt;em&gt;where&lt;/em&gt; to send them. That’s where &lt;strong&gt;Chat IDs&lt;/strong&gt; come in.&lt;/p&gt;

&lt;p&gt;There are a few ways to get this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Send a message to your bot and use a script to log the response&lt;/li&gt;
&lt;li&gt;Use Telegram’s API directly&lt;/li&gt;
&lt;li&gt;Use bot management tools&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once you have your &lt;strong&gt;Bot Token&lt;/strong&gt; and &lt;strong&gt;Chat ID&lt;/strong&gt;, your bot can send messages directly to your Telegram—nice!&lt;/p&gt;

&lt;h3&gt;
  
  
  Integrating Telegram Bot with Your Code
&lt;/h3&gt;

&lt;p&gt;The bot uses the Telegram Bot API, which makes sending messages as simple as an HTTP request. But this bot comes fully packaged—commands are already built-in for you:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="sb"&gt;`&lt;/span&gt;/start&lt;span class="sb"&gt;`&lt;/span&gt; - See welcome message  
&lt;span class="sb"&gt;`&lt;/span&gt;/track &lt;span class="o"&gt;[&lt;/span&gt;chain] &lt;span class="o"&gt;[&lt;/span&gt;address]&lt;span class="sb"&gt;`&lt;/span&gt; - Start tracking  
&lt;span class="sb"&gt;`&lt;/span&gt;/list&lt;span class="sb"&gt;`&lt;/span&gt; - List tracked wallets  
&lt;span class="sb"&gt;`&lt;/span&gt;/remove&lt;span class="sb"&gt;`&lt;/span&gt; - Remove a wallet  
&lt;span class="sb"&gt;`&lt;/span&gt;/stop &lt;span class="o"&gt;[&lt;/span&gt;chain] &lt;span class="o"&gt;[&lt;/span&gt;address]&lt;span class="sb"&gt;`&lt;/span&gt; - Stop tracking temporarily, 
&lt;span class="sb"&gt;`&lt;/span&gt;/resume &lt;span class="o"&gt;[&lt;/span&gt;chain] &lt;span class="o"&gt;[&lt;/span&gt;address]&lt;span class="sb"&gt;`&lt;/span&gt; - Resume tracking again, 
&lt;span class="sb"&gt;`&lt;/span&gt;/stopall&lt;span class="sb"&gt;`&lt;/span&gt; - Stop all tracking temporarily, 
&lt;span class="sb"&gt;`&lt;/span&gt;/resumeall&lt;span class="sb"&gt;`&lt;/span&gt; - Control tracking  
&lt;span class="sb"&gt;`&lt;/span&gt;/help&lt;span class="sb"&gt;`&lt;/span&gt; - Get &lt;span class="nb"&gt;command help&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  ⚙️ Installing and Configuring the Bot
&lt;/h3&gt;

&lt;p&gt;&lt;b&gt;Cloning the Repository and Installing Dependencies&lt;/b&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Clone the repo
git clone https://github.com/imcrazysteven/EVM-Wallet-Tracking-Telegram-Bot.git

# Navigate to the project directory
cd EVM-Wallet-Tracking-Telegram-Bot

# Install dependencies
pip install -r requirements.txt

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;b&gt;Setting Up the Environment Variables&lt;/b&gt;&lt;/p&gt;

&lt;p&gt;Create a &lt;code&gt;.env&lt;/code&gt; file in the root directory and paste the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# API Keys&lt;/span&gt;
&lt;span class="nv"&gt;MORALIS_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;Your Moralis API Key : API key from Moralis &lt;span class="k"&gt;for &lt;/span&gt;accessing EVM blockchain data
&lt;span class="nv"&gt;TELEGRAM_BOT_TOKEN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;Your Telegram Bot Token : Your Telegram bot token from BotFather

&lt;span class="c"&gt;# Optional Configuration&lt;/span&gt;
&lt;span class="nv"&gt;POLL_INTERVAL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;20 : Optional, controls how often bot checks &lt;span class="k"&gt;for &lt;/span&gt;updates

&lt;span class="c"&gt;# DATABASE CONFIGURATION&lt;/span&gt;
MONGODB_URI &lt;span class="o"&gt;=&lt;/span&gt; mongodb://localhost:27017/db_name : MongoDB connection string &lt;span class="k"&gt;for &lt;/span&gt;storing transaction &lt;span class="nb"&gt;history&lt;/span&gt;

&lt;span class="c"&gt;# BLOCKCHAIN CONFIGURATION: Web3 RPC URLs for each EVM-compatible blockchain (Ethereum, Polygon, BSC, etc.)&lt;/span&gt;
&lt;span class="nv"&gt;ETH_PROVIDER_URI&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;https://mainnet.infura.io/v3/your-api-key
&lt;span class="nv"&gt;BSC_PROVIDER_URI&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;https://bsc-dataseed.binance.org/
&lt;span class="nv"&gt;BASE_PROVIDER_URI&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;https://mainnet.base.org
&lt;span class="nv"&gt;POLYGON_PROVIDER_URI&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;https://polygon-rpc.com
&lt;span class="nv"&gt;ARBITRUM_PROVIDER_URI&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;https://arb1.arbitrum.io/rpc
&lt;span class="nv"&gt;OPTIMISM_PROVIDER_URI&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;https://mainnet.optimism.io
&lt;span class="nv"&gt;AVALANCHE_PROVIDER_URI&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;https://api.avax.network/ext/bc/C/rpc
&lt;span class="nv"&gt;FANTOM_PROVIDER_URI&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;https://rpc.ankr.com/fantom
&lt;span class="nv"&gt;CRONOS_PROVIDER_URI&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;https://evm.cronos.org
&lt;span class="nv"&gt;GNOSIS_PROVIDER_URI&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;https://rpc.gnosischain.com
&lt;span class="nv"&gt;CHILIZ_PROVIDER_URI&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;https://rpc.ankr.com/chiliz
&lt;span class="nv"&gt;MOONBEAM_PROVIDER_URI&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;https://rpc.api.moonbeam.network
&lt;span class="nv"&gt;RONIN_PROVIDER_URI&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;https://api.roninchain.com/rpc
&lt;span class="nv"&gt;LISK_PROVIDER_URI&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;https://rpc.mainnet.lisk.com
&lt;span class="nv"&gt;PULSECHAIN_PROVIDER_URI&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;https://rpc.pulsechain.com
&lt;span class="nv"&gt;LINEA_PROVIDER_URI&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;https://rpc.linea.build
&lt;span class="nv"&gt;PALM_PROVIDER_URI&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;https://rpc.palm.io
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  ⛓️ Blockchain Data Integration
&lt;/h3&gt;

&lt;p&gt;&lt;b&gt;Using Moralis API for Real-Time Blockchain Data&lt;/b&gt;&lt;/p&gt;

&lt;p&gt;Once integrated:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Your bot connects to live WebSocket streams&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;It listens for activity on any wallet you specify&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;It pulls all token details, symbols, amounts, and values in real time&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;b&gt;Distinguishing Wallet vs Contract Interactions&lt;/b&gt;&lt;/p&gt;

&lt;p&gt;The bot uses Web3 providers to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Determine if an address is a contract&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Filter out non-human interactions&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Only alert you when something useful happens&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  🕵️ How the Bot Monitors Transactions
&lt;/h3&gt;

&lt;p&gt;&lt;b&gt;Real-Time Wallet Tracking via Moralis API&lt;/b&gt;&lt;/p&gt;

&lt;p&gt;The bot periodically calls Moralis API to fetch events for the wallets you're tracking. When a transaction hits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Instantly receives the data&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Parses the transaction&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Sends a formatted message to &lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;b&gt;Extracting Token Swap, Transfer, and Liquidity Events&lt;/b&gt;&lt;/p&gt;

&lt;p&gt;Details extracted:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Token name &amp;amp; symbol&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Token amount&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;USD value&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Transaction type&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Explorer links&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  🧩 Behind the Scenes: How It Works
&lt;/h3&gt;

&lt;p&gt;&lt;b&gt;Transaction Analysis Workflow&lt;/b&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Receive transaction&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Validate sender&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Parse and classify&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Enrich with token info&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Store in DB&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Send Telegram alert&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;b&gt;Database Storage with MongoDB&lt;/b&gt;&lt;/p&gt;

&lt;p&gt;Transaction fields stored:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Wallet address&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Token name &amp;amp; symbol&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Value&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Timestamp&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Chain&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Txn type&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Last checked Block&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;b&gt;Optimizing Telegram Notifications for Readability&lt;/b&gt;&lt;/p&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;🔔 New Wallet Activity
👛 Wallet: 0xfc9928...e535
🔗 Chain: Ethereum
📊 Type: 🔴 SELL
🪙 Token: REALM (REALM)
💲 Price: $0.00002340
📈 Amount: 146.16K
💰 Value: $3.42
🕒 Time: 2025-04-19 18:46:23 UTC
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  🚀 Advanced Features &amp;amp; Enhancements
&lt;/h3&gt;

&lt;p&gt;&lt;b&gt;Multi-threaded Blockchain Connections&lt;/b&gt;&lt;/p&gt;

&lt;p&gt;Each chain runs on a separate thread for better performance.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Token Info &amp;amp; Symbol Recognition&lt;/b&gt;&lt;/p&gt;

&lt;p&gt;Token metadata is enriched using Moralis APIs.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Efficient Resource Management&lt;/b&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Caching&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;DB indexing&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Error handling&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  🔐 FAQs
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;b&gt;Does this bot require private keys?&lt;/b&gt;&lt;br&gt;
No. Only public addresses.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;b&gt;Can I track more than one wallet?&lt;/b&gt;&lt;br&gt;
Yes, unlimited.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;b&gt;Which blockchains are supported?&lt;/b&gt;&lt;br&gt;
All major EVM chains, like Ethereum, Polygon, Base, Binance Smart Chain, Arbitrum, Optimism, Avalanche, Fantom, etc.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;b&gt;Will it notify me of all types of transactions?&lt;/b&gt;&lt;br&gt;
It focuses on key ones (buy, sell, transfer).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;b&gt;Can I view past transaction history?&lt;/b&gt;&lt;br&gt;
Yes, stored in MongoDB.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Key Features Implemented
&lt;/h2&gt;

&lt;p&gt;The wallet tracking system includes several powerful features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;1. &lt;b&gt;Multi-chain Transaction Monitoring&lt;/b&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The system captures transactions as they occur across multiple EVM-compatible blockchains (Ethereum, Polygon, Arbitrum, Base, BSC, etc.), with minimal delay between on-chain confirmation and notification delivery. This cross-chain awareness is crucial for comprehensive portfolio management and market intelligence.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;2. &lt;b&gt;Transaction Type Classification&lt;/b&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The bot intelligently classifies transactions into meaningful categories like buys, sells, swaps, and transfers, providing context that raw blockchain data lacks. This classification makes notifications immediately actionable without requiring manual analysis.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;3. &lt;b&gt;Token Information Enrichment&lt;/b&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All transactions are enriched with token metadata including names, symbols, and current prices, transforming cryptic contract addresses into human-readable information that's easy to understand at a glance.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;4. &lt;b&gt;Formatted Telegram Notifications&lt;/b&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Notifications are delivered through Telegram with rich, emoji-enhanced formatting that makes complex transaction data instantly comprehensible. Each notification includes direct links to block explorers for deeper investigation.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;5. &lt;b&gt;MongoDB Historical Storage&lt;/b&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All monitored transactions are stored in MongoDB, creating a searchable history that can be used for pattern recognition, performance analysis, and tax reporting. This historical record becomes increasingly valuable over time.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;6. &lt;b&gt;Command-based Interface&lt;/b&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The bot offers an intuitive command system through Telegram, allowing users to add/remove wallets, list tracked addresses, and control notification settings without leaving their messaging app.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;7. &lt;b&gt;Multi-threaded Architecture&lt;/b&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each blockchain connection runs on a separate thread, ensuring that monitoring one slow chain doesn't impact performance on others and maximizing the system's ability to capture transactions in real-time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Advanced Features to Consider
&lt;/h2&gt;

&lt;p&gt;Once you've implemented the core functionality, consider these enhancements:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;1. &lt;b&gt;Smart Contract Interaction Analysis&lt;/b&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Extend the bot to decode complex smart contract interactions, providing insights into DeFi operations like liquidity provision, staking, and governance participation.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;2. &lt;b&gt;Whale Alert Thresholds&lt;/b&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Implement customizable thresholds for "whale" transactions, allowing users to receive special notifications when movements exceed certain token amounts or dollar values.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;3. &lt;b&gt;Gas Optimization Alerts&lt;/b&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Add notifications about optimal gas prices across different chains, helping users time their transactions to minimize fees during network congestion.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;4. &lt;b&gt;Portfolio Performance Tracking&lt;/b&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Develop functionality to track overall portfolio performance across multiple wallets and chains, providing periodic summaries of value changes and transaction activity.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;5. &lt;b&gt;Custom Notification Filters&lt;/b&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Allow users to set granular criteria for which transactions trigger notifications, such as minimum value thresholds, specific tokens, or transaction types.&lt;/p&gt;

&lt;h2&gt;
  
  
  🏁 Conclusion
&lt;/h2&gt;

&lt;p&gt;The EVM Wallet Tracking Telegram Bot is your personal DeFi assistant—analyzing, alerting, and giving you the upper hand in real-time blockchain tracking.&lt;/p&gt;

&lt;p&gt;The EVM Wallet Tracking Telegram Bot transforms complex blockchain data into actionable intelligence, delivering real-time insights across multiple EVM-compatible networks. By combining sophisticated transaction analysis with convenient Telegram notifications, this tool provides crypto traders, investors, and developers with the situational awareness needed to navigate the fast-moving DeFi landscape.&lt;/p&gt;

&lt;p&gt;The multi-threaded architecture and MongoDB integration ensure both performance and historical data preservation, while the command-based Telegram interface makes the system accessible even to users without technical blockchain knowledge. The ability to track multiple wallets across multiple chains in a single interface eliminates the need to juggle various block explorers and monitoring tools.&lt;/p&gt;

&lt;p&gt;As blockchain ecosystems continue to evolve, tools that provide cross-chain visibility become increasingly valuable. Whether you're tracking your own investments, monitoring competitor strategies, or keeping tabs on market-moving whale wallets, this bot serves as your personal blockchain analyst, working around the clock to keep you informed of significant on-chain movements.&lt;/p&gt;

&lt;p&gt;By implementing this EVM Wallet Tracking Bot, you'll gain a tactical advantage in the information-rich but often opaque world of decentralized finance. The real-time notifications and detailed transaction breakdowns transform raw blockchain data into the kind of actionable intelligence that can make the difference between capturing opportunities and missing them entirely.&lt;/p&gt;

&lt;h2&gt;
  
  
  📁 GitHub Repository
&lt;/h2&gt;

&lt;p&gt;🔗 &lt;a href="https://github.com/imcrazysteven/EVM-Wallet-Tracking-Telegram-Bot" rel="noopener noreferrer"&gt;View Source Code on GitHub&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can see the sample video there.&lt;br&gt;
If you want to see the whole code or develop EVM wallet tracking telegram bot on your own, please contact me with the contact information below.&lt;br&gt;
Please don’t forget to leave a star if you like the bot.&lt;/p&gt;

&lt;h2&gt;
  
  
  Contact Information
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;GitHub: &lt;a href="https://github.com/imcrazysteven" rel="noopener noreferrer"&gt;Steven&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Gmail: &lt;a href="//imcrazysteven143@gmail.com"&gt;Steven&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Telegram: &lt;a href="https://t.me/imcrazysteven" rel="noopener noreferrer"&gt;Steven&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Twitter: &lt;a href="https://x.com/imcrazysteven" rel="noopener noreferrer"&gt;Steven&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Instagram: &lt;a href="https://instagram.com/imcrazysteven" rel="noopener noreferrer"&gt;Steven&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>evm</category>
      <category>tracking</category>
      <category>telegrambot</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Building a Professional Solana Wallet Tracking Telegram Bot</title>
      <dc:creator>Steven</dc:creator>
      <pubDate>Thu, 17 Apr 2025 08:10:28 +0000</pubDate>
      <link>https://dev.to/imcrazysteven/building-a-professional-solana-wallet-tracking-telegram-bot-16od</link>
      <guid>https://dev.to/imcrazysteven/building-a-professional-solana-wallet-tracking-telegram-bot-16od</guid>
      <description>&lt;p&gt;This article gives you a comprehensive guide to building a professional Solana wallet monitoring system that monitors transactions, calculates PnL and delivers real-time notifications through Telegram.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fml1yvkc50o7cdp0yag2j.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fml1yvkc50o7cdp0yag2j.PNG" alt=" " width="433" height="349"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In the dynamic landscape of Crypto trading, especially on Solana Blockchain, staying up to date with wallet activity is very important for traders, investors, and developers looking to make informed decisions. Whether you're tracking your own investments, monitoring whale movements, or keeping an eye on competitor strategies, having real-time insights into on-chain transactions can provide a significant advantage.&lt;/p&gt;

&lt;p&gt;This guide will walk you through creating a sophisticated wallet tracking bot that monitors transactions and sends detailed notifications via Telegram. By the end, you'll have a powerful tool that provides instant alerts about token purchases, sales, transfers, and other on-chain activities.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Build a Solana Wallet Tracker?
&lt;/h2&gt;

&lt;p&gt;Solana's high-speed, low-cost blockchain has become a hub for DeFi, NFTs, and gaming applications. With thousands of transactions occurring every second, manually monitoring wallet activities is impossible. A dedicated tracking system offers several benefits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;b&gt;Real-time awareness&lt;/b&gt; of trading activities and token movements&lt;/li&gt;
&lt;li&gt;
&lt;b&gt;Investment insights&lt;/b&gt; by tracking successful traders' strategies&lt;/li&gt;
&lt;li&gt;
&lt;b&gt;Security monitoring&lt;/b&gt; to detect unauthorized transactions&lt;/li&gt;
&lt;li&gt;
&lt;b&gt;Market intelligence&lt;/b&gt; by observing whale movements and trends&lt;/li&gt;
&lt;li&gt;
&lt;b&gt;Portfolio management&lt;/b&gt; with automated transaction recording&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step-by-Step Guide for developing Solana Wallet Tracking Telegram Bot
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Setting Up Your Telegram Bot
&lt;/h3&gt;

&lt;p&gt;Telegram bots provide an excellent interface for receiving notifications and interacting with your tracking system. The process starts with creating and &lt;br&gt;
configuring your bot.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;b&gt;Creating your Telegram Bot&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The first step is to create a new bot through Telegram's BotFather:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open Telegram and search for the &lt;a class="mentioned-user" href="https://dev.to/botfather"&gt;@botfather&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Send the &lt;code&gt;/newbot&lt;/code&gt; command&lt;/li&gt;
&lt;li&gt;Follow the prompts to name your bot (e.g., "SolanaWalletTracker")&lt;/li&gt;
&lt;li&gt;Choose a username that ends with "bot" (e.g., "SolanaWalletTrackerBot")&lt;/li&gt;
&lt;li&gt;BotFather will provide a token - this is your API access key&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This token is the authentication credential your application will use to send and receive messages through your bot. Keep it secure and never share it publicly.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;b&gt;Understanding Chat IDs&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Telegram uses chat IDs to identify conversations. To send notifications to yourself or specific channels, you'll need to obtain these IDs. When a user interacts with your bot, Telegram assigns a unique chat ID to that conversation.&lt;/p&gt;

&lt;p&gt;You can retrieve chat IDs by:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Using Telegram's API directly&lt;/li&gt;
&lt;li&gt;Setting up a simple script to log message events&lt;/li&gt;
&lt;li&gt;Using third-party tools designed for Telegram bot development&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For personal use, you'll want to save your own chat ID in your environment configuration.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Environment Configuration&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Proper configuration management is essential for security and flexibility. By using environment variables, you can keep sensitive information out of your codebase and easily adjust settings between development and production environments.&lt;/p&gt;

&lt;p&gt;Your configuration should include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Telegram bot credentials&lt;/li&gt;
&lt;li&gt;Solana RPC endpoints&lt;/li&gt;
&lt;li&gt;Database connection strings&lt;/li&gt;
&lt;li&gt;Default wallet addresses (optional)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This separation of configuration from code follows best practices for application development and security.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Implementing the Telegram Bot
&lt;/h3&gt;

&lt;p&gt;The Telegram bot serves as both the notification channel and user interface for your tracking system. It needs to handle commands, process wallet addresses, and deliver formatted transaction alerts.&lt;/p&gt;

&lt;p&gt;A well-designed bot should:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Respond to standard commands like &lt;code&gt;/start&lt;/code&gt; and &lt;code&gt;/help&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Process wallet addresses when users send them&lt;/li&gt;
&lt;li&gt;Validate input to ensure addresses are properly formatted&lt;/li&gt;
&lt;li&gt;Maintain user preferences for which wallets to track&lt;/li&gt;
&lt;li&gt;Format transaction data into readable notifications&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The bot becomes the primary way users interact with your system, so investing time in creating a smooth, intuitive experience pays dividends in usability.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Integrating Helius RPC
&lt;/h3&gt;

&lt;p&gt;Helius provides specialized Solana RPC services with enhanced capabilities for developers. Unlike standard RPC providers, Helius offers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Higher rate limits for API calls&lt;/li&gt;
&lt;li&gt;WebSocket support for real-time data &lt;/li&gt;
&lt;li&gt;Enhanced transaction data with parsed information&lt;/li&gt;
&lt;li&gt;Specialized endpoints for token data and NFTs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Setting up a connection to Helius involves:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Creating an account on the Helius platform&lt;/li&gt;
&lt;li&gt;Generating API keys for authentication&lt;/li&gt;
&lt;li&gt;Configuring both HTTP and WebSocket endpoints&lt;/li&gt;
&lt;li&gt;Establishing connection handling with proper error management&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The WebSocket connection is particularly important for real-time monitoring, as it allows the blockchain to push notifications to your application rather than requiring constant polling.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. MongoDB Database Configuration and Connection
&lt;/h3&gt;

&lt;p&gt;MongoDB provides a flexible, document-oriented database that's well-suited for storing blockchain transaction data. Its schema-less design accommodates the varying structure of transaction information, while still allowing for efficient querying and analysis.&lt;/p&gt;

&lt;p&gt;When setting up your database:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create appropriate collections for transactions, wallets, and user preferences&lt;/li&gt;
&lt;li&gt;Implement indexes for frequently queried fields like wallet addresses and timestamps&lt;/li&gt;
&lt;li&gt;Configure connection pooling for efficient database access&lt;/li&gt;
&lt;li&gt;Implement proper error handling and reconnection logic&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A well-designed database structure enables powerful features like historical analysis, PNL calculation, and pattern recognition across transactions.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. WebSocket Handling for Real-time Monitoring
&lt;/h3&gt;

&lt;p&gt;WebSockets provide a persistent connection between your application and the Solana blockchain, enabling real-time notification of new transactions. Properly managing these connections is crucial for reliable operation.&lt;/p&gt;

&lt;p&gt;Key considerations include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Implementing reconnection logic for network interruptions&lt;/li&gt;
&lt;li&gt;Sending periodic ping messages to keep connections alive&lt;/li&gt;
&lt;li&gt;Properly subscribing to relevant blockchain events&lt;/li&gt;
&lt;li&gt;Processing incoming messages efficiently&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;WebSockets are the foundation of real-time monitoring, allowing your system to react instantly to on-chain activities rather than discovering them through periodic polling.&lt;/p&gt;

&lt;h3&gt;
  
  
  6. Transaction Analysis and Tracking
&lt;/h3&gt;

&lt;p&gt;The heart of your wallet tracking system is the ability to analyze transactions and extract meaningful information. This involves:&lt;/p&gt;

&lt;p&gt;&lt;u&gt;1. &lt;b&gt;Receiving transaction signatures&lt;/b&gt; from the WebSocket connection&lt;br&gt;
&lt;/u&gt;&lt;br&gt;
&lt;u&gt;2. &lt;b&gt;Fetching complete transaction data&lt;/b&gt; using the RPC connection&lt;br&gt;
&lt;/u&gt;&lt;br&gt;
&lt;u&gt;3. &lt;b&gt;Parsing the transaction&lt;/b&gt; to identify:&lt;br&gt;
&lt;/u&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Token transfers and amounts&lt;/li&gt;
&lt;li&gt;SOL movements&lt;/li&gt;
&lt;li&gt;Transaction type (swap, transfer, etc.)&lt;/li&gt;
&lt;li&gt;Involved parties and contracts&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;u&gt;4. &lt;b&gt;Calculating financial implications&lt;/b&gt; like:&lt;br&gt;
&lt;/u&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Purchase or sale amounts&lt;/li&gt;
&lt;li&gt;Price impact&lt;/li&gt;
&lt;li&gt;Profit and loss&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;u&gt;5. &lt;b&gt;Formatting the data&lt;/b&gt; into human-readable notifications&lt;br&gt;
&lt;/u&gt;&lt;br&gt;
This analysis requires understanding Solana's transaction structure, account system, and token standards. The complexity comes from needing to interpret the raw blockchain data into meaningful financial information.&lt;/p&gt;

&lt;p&gt;For example, identifying a token swap requires:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Recognizing the DEX program being called&lt;/li&gt;
&lt;li&gt;Tracking token balance changes before and after the transaction&lt;/li&gt;
&lt;li&gt;Calculating the effective exchange rate&lt;/li&gt;
&lt;li&gt;Determining if it was a buy or sell operation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The analysis logic forms the intelligence layer of your tracking system, transforming raw blockchain events into actionable insights.&lt;/p&gt;

&lt;h3&gt;
  
  
  7. Utility Functions and Helpers
&lt;/h3&gt;

&lt;p&gt;A collection of utility functions makes your code more maintainable and readable. These helpers handle common tasks like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Validating Solana addresses&lt;/li&gt;
&lt;li&gt;Formatting wallet addresses for display (e.g., "Ab3X...j9Zk")&lt;/li&gt;
&lt;li&gt;Converting between token units (e.g., lamports to SOL)&lt;/li&gt;
&lt;li&gt;Generating explorer links for transactions and accounts&lt;/li&gt;
&lt;li&gt;Managing timeouts and retries for network operations&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These utilities encapsulate common operations and ensure consistency throughout your application.&lt;/p&gt;

&lt;h3&gt;
  
  
  8. Main Application Entry Point
&lt;/h3&gt;

&lt;p&gt;The entry point coordinates the initialization and operation of all system components. It handles:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Loading configuration from environment variables&lt;/li&gt;
&lt;li&gt;Establishing database connections&lt;/li&gt;
&lt;li&gt;Initializing the Telegram bot&lt;/li&gt;
&lt;li&gt;Setting up error handling and logging&lt;/li&gt;
&lt;li&gt;Managing the application lifecycle&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Proper initialization ensures all components are ready before the system begins processing transactions.&lt;/p&gt;

&lt;h3&gt;
  
  
  9. Running and Maintaining the Application
&lt;/h3&gt;

&lt;p&gt;Once built, your tracking system needs proper deployment and maintenance:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;b&gt;Deployment options&lt;/b&gt; include cloud services, dedicated servers, or container platforms&lt;/li&gt;
&lt;li&gt;
&lt;b&gt;Monitoring&lt;/b&gt; ensures the system remains operational and responsive&lt;/li&gt;
&lt;li&gt;
&lt;b&gt;Logging&lt;/b&gt; captures important events and errors for troubleshooting&lt;/li&gt;
&lt;li&gt;
&lt;b&gt;Backup procedures&lt;/b&gt; protect user data and transaction history&lt;/li&gt;
&lt;li&gt;
&lt;b&gt;Update processes&lt;/b&gt; allow for adding features and fixing issues&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A production-ready system requires attention to these operational concerns to ensure reliability.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Features Implemented
&lt;/h2&gt;

&lt;p&gt;The wallet tracking system includes several powerful features:&lt;/p&gt;

&lt;p&gt;&lt;u&gt;1. &lt;b&gt; Real-time Transaction Monitoring&lt;/b&gt;&lt;br&gt;
&lt;/u&gt;&lt;br&gt;
The system captures transactions as they occur on the Solana blockchain, with minimal delay between on-chain confirmation and notification delivery. This real-time awareness is crucial for time-sensitive trading decisions and security monitoring.&lt;/p&gt;

&lt;p&gt;&lt;u&gt;2. &lt;b&gt;Multi-wallet Support&lt;/b&gt;&lt;br&gt;
&lt;/u&gt;&lt;br&gt;
Users can track multiple wallets simultaneously, allowing them to monitor their entire portfolio or keep tabs on several accounts of interest. The Telegram interface makes adding and removing wallets simple and intuitive.&lt;/p&gt;

&lt;p&gt;&lt;u&gt;3. &lt;b&gt;Detailed Transaction Analysis&lt;/b&gt;&lt;br&gt;
&lt;/u&gt;&lt;br&gt;
Beyond simply notifying about transactions, the system analyzes each one to extract meaningful information: Was it a buy or sell? Which token was involved? How much was transferred? This analysis transforms raw blockchain data into actionable intelligence.&lt;/p&gt;

&lt;p&gt;&lt;u&gt;4. &lt;b&gt;Instant Telegram Notifications&lt;/b&gt;&lt;br&gt;
&lt;/u&gt;&lt;br&gt;
Notifications are delivered immediately through Telegram, with rich formatting that makes the information easy to understand at a glance. Links to block explorers allow for deeper investigation when needed.&lt;/p&gt;

&lt;p&gt;&lt;u&gt;5. &lt;b&gt;Transaction History&lt;/b&gt;&lt;br&gt;
&lt;/u&gt;&lt;br&gt;
All monitored transactions are stored in MongoDB, creating a searchable history that can be used for pattern recognition, performance analysis, and tax reporting. This historical record becomes increasingly valuable over time.&lt;/p&gt;

&lt;p&gt;&lt;u&gt;6. &lt;b&gt;Token Information&lt;/b&gt;&lt;br&gt;
&lt;/u&gt;&lt;br&gt;
The system enriches notifications with token details like symbols and names, making it easier to understand transaction context without needing to look up token addresses.&lt;/p&gt;

&lt;p&gt;&lt;u&gt;7. &lt;b&gt;PNL Tracking&lt;/b&gt;&lt;br&gt;
&lt;/u&gt;&lt;br&gt;
For trading operations, the system calculates and displays profit and loss information, helping users understand the financial impact of each transaction.&lt;/p&gt;

&lt;h2&gt;
  
  
  Advanced Features to Consider
&lt;/h2&gt;

&lt;p&gt;Once you've implemented the core functionality, consider these enhancements:&lt;/p&gt;

&lt;p&gt;&lt;u&gt;1. &lt;b&gt;Token Price Tracking&lt;/b&gt;&lt;br&gt;
&lt;/u&gt;&lt;br&gt;
Integrate with price APIs to show current token values and calculate USD equivalents for transactions. This provides more meaningful financial context for notifications.&lt;/p&gt;

&lt;p&gt;&lt;u&gt;2. &lt;b&gt;Whale Alert&lt;/b&gt;&lt;br&gt;
&lt;/u&gt;&lt;br&gt;
Create specialized notifications for large transactions that might impact market conditions. Users could set thresholds for what constitutes a "whale" movement.&lt;/p&gt;

&lt;p&gt;&lt;u&gt;3. &lt;b&gt;Portfolio Dashboard&lt;/b&gt;&lt;br&gt;
&lt;/u&gt;&lt;br&gt;
Develop a web interface that visualizes wallet performance, token holdings, and transaction history. This could complement the Telegram notifications with more detailed analysis.&lt;/p&gt;

&lt;p&gt;&lt;u&gt;4. &lt;b&gt;Custom Notification Filters&lt;/b&gt;&lt;br&gt;
&lt;/u&gt;&lt;br&gt;
Allow users to set criteria for which transactions trigger notifications, such as minimum value thresholds or specific token types.&lt;/p&gt;

&lt;p&gt;&lt;u&gt;5. &lt;b&gt;Multi-chain Support&lt;/b&gt;&lt;br&gt;
&lt;/u&gt;&lt;br&gt;
Extend the system to monitor other blockchains like Ethereum or Binance Smart Chain, creating a comprehensive cross-chain monitoring solution.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Building a Solana wallet tracking system with Telegram notifications combines blockchain technology, database management, and messaging APIs into a powerful tool for crypto enthusiasts, traders, and developers. The system provides real-time insights into on-chain activities, helping users make informed decisions and stay aware of important transactions.&lt;/p&gt;

&lt;p&gt;The modular architecture described in this guide allows for easy maintenance and future enhancements. By separating concerns between blockchain interaction, data storage, and user notifications, you can evolve each component independently as needs change.&lt;/p&gt;

&lt;p&gt;Remember that blockchain technology and APIs evolve rapidly. Stay informed about changes to Solana's transaction structure, RPC interfaces, and token standards to ensure your tracking system remains accurate and reliable.&lt;/p&gt;

&lt;p&gt;With the foundation provided in this guide, you can build a sophisticated wallet tracking system that delivers valuable insights through convenient Telegram notifications. Whether for personal use, team collaboration, or as a service for others, this system transforms the complex world of blockchain transactions into accessible, actionable information.&lt;/p&gt;

&lt;h2&gt;
  
  
  Github repository
&lt;/h2&gt;

&lt;p&gt;This project is available on my &lt;a href="https://github.com/imcrazysteven/Solana-Wallet-Tracking-Telegram-Bot" rel="noopener noreferrer"&gt;github repository&lt;/a&gt;. &lt;br&gt;
If you want to see the whole code or develop Solana wallet tracking telegram bot, you can contact me with the contact information below.&lt;/p&gt;

&lt;h2&gt;
  
  
  Contact Information
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;GitHub: &lt;a href="https://github.com/imcrazysteven" rel="noopener noreferrer"&gt;Steven&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Gmail: &lt;a href="//imcrazysteven143@gmail.com"&gt;Steven&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Telegram: &lt;a href="https://t.me/imcrazysteven" rel="noopener noreferrer"&gt;Steven&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Twitter: &lt;a href="https://x.com/imcrazysteven" rel="noopener noreferrer"&gt;Steven&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Instagram: &lt;a href="https://instagram.com/imcrazysteven" rel="noopener noreferrer"&gt;Steven&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
    </item>
    <item>
      <title>How to integrate ERC20 token presale smart contract with Frontend</title>
      <dc:creator>Steven</dc:creator>
      <pubDate>Sat, 16 Nov 2024 12:23:13 +0000</pubDate>
      <link>https://dev.to/imcrazysteven/how-to-integrate-erc20-token-presale-smart-contract-with-frontend-3mjj</link>
      <guid>https://dev.to/imcrazysteven/how-to-integrate-erc20-token-presale-smart-contract-with-frontend-3mjj</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Integrating an ERC20 token presale smart contract with a frontend using Rainbow Kit is a great way to provide a seamless user experience for token buyers. Rainbow Kit simplifies the wallet connection process and user interface, making it easier for developers to build decentralized applications (dApps). This article will guide you through the steps necessary to set up the integration.&lt;br&gt;
We are going to use Next.js for the frontend development, Ethers.js for smart contract integration and RainbowKit with Wagmi for wallet connections.&lt;/p&gt;

&lt;p&gt;The image below is what I am going to create for SPX token presale.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Famgrrkz92v7oezzp307b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Famgrrkz92v7oezzp307b.png" alt=" " width="447" height="788"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can also refer &lt;a href="https://dev.to/imcrazysteven/comprehensive-guide-to-write-erc20-token-presale-smart-contract-on-ethereum-blockchain-using-solidity-1h6e"&gt;this article&lt;/a&gt; for SPX presale smart contract.&lt;/p&gt;
&lt;h2&gt;
  
  
  How to implement step by step?
&lt;/h2&gt;
&lt;h3&gt;
  
  
  1. Create a Next.js project
&lt;/h3&gt;

&lt;p&gt;We are going to use Next.js with Typescript and Tailwind css for the frontend development.&lt;/p&gt;

&lt;p&gt;And install the required dependencies.&lt;/p&gt;
&lt;h3&gt;
  
  
  2. Configure Web3 Providers
&lt;/h3&gt;

&lt;p&gt;Create a new file &lt;code&gt;src/wagmi.ts&lt;/code&gt; and configure the chains and projectId.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { getDefaultConfig } from "@rainbow-me/rainbowkit";
import { mainnet, sepolia } from "wagmi/chains";

export const config = getDefaultConfig({
  appName: "IRONWILL",
  projectId: "68449b069bf507d7dc50e5c1bcb82c50", // Replace with your actual project ID
  chains: [mainnet, sepolia],
  ssr: true, // Enable server-side rendering support
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Create Provider Utilites and blockchain utilities
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Create a new file &lt;code&gt;src/utils/web3Providers.ts&lt;/code&gt; and write the following code.
This code creates a provider instance, handle provider switching, and manage the RPC connections
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { useMemo } from 'react';
import {
  FallbackProvider,
  JsonRpcProvider,
  BrowserProvider,
  JsonRpcSigner,
} from 'ethers';
import type { Account, Chain, Client, Transport } from 'viem';
import { type Config, useClient, useConnectorClient } from 'wagmi';

export function clientToProvider(client: Client&amp;lt;Transport, Chain&amp;gt;) {
  const { chain, transport } = client;
  const network = {
    chainId: chain.id,
    name: chain.name,
    ensAddress: chain.contracts?.ensRegistry?.address,
  };
  if (transport.type === 'fallback') {
    const providers = (transport.transports as ReturnType&amp;lt;Transport&amp;gt;[]).map(
      ({ value }) =&amp;gt; new JsonRpcProvider(value?.url, network)
    );
    if (providers.length === 1) return providers[0];
    return new FallbackProvider(providers);
  }
  return new JsonRpcProvider(transport.url, network);
}

export function useEthersProvider({ chainId }: { chainId?: number } = {}) {
  const client = useClient&amp;lt;Config&amp;gt;({ chainId })!;
  return useMemo(() =&amp;gt; clientToProvider(client), [client]);
}

export function clientToSigner(client: Client&amp;lt;Transport, Chain, Account&amp;gt;) {
  const { account, chain, transport } = client;
  const network = {
    chainId: chain.id,
    name: chain.name,
    ensAddress: chain.contracts?.ensRegistry?.address,
  };
  const provider = new BrowserProvider(transport, network);
  const signer = new JsonRpcSigner(provider, account.address);
  return signer;
}

export async function useEthersSigner({ chainId }: { chainId?: number } = {}) {
  const { data: client } = useConnectorClient&amp;lt;Config&amp;gt;({ chainId });
  return useMemo(() =&amp;gt; (client ? clientToSigner(client) : undefined), [client]);
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Then create a new file &lt;code&gt;src/utils/ethUtils.ts&lt;/code&gt; and write the following code.
This code has the several blockchain utility functions.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { ethers } from "ethers";
import { toast } from "react-toastify";

const ERC20_ABI = [
  "function balanceOf(address owner) view returns (uint256)",
  "function decimals() view returns (uint8)",
  "function symbol() view returns (string)",
];

export const TOKENS = {
  // sepolia
  USDT: "0xaA8E23Fb1079EA71e0a56F48a2aA51851D8433D0",
  USDC: "0x94a9D9AC8a22534E3FaCa9F4e7F2E2cf85d5E4C8",
  DAI: "0xFF34B3d4Aee8ddCd6F9AFFFB6Fe49bD371b8a357",
  SPX: "0x60081fa3c771BA945Aa3E2112b1f557D80e88575",
};

export async function getBalances(
  address: string
): Promise&amp;lt;{ [key: string]: string }&amp;gt; {
  const provider = new ethers.JsonRpcProvider(
    process.env.NEXT_PUBLIC_INFURA_URL
  );

  const balances: { [key: string]: string } = {};

  try {
    // Get ETH balance
    const ethBalance = await provider.getBalance(address);
    balances.ETH = ethers.formatEther(ethBalance);

    // Get token balances
    for (const [symbol, tokenAddress] of Object.entries(TOKENS)) {
      const contract = new ethers.Contract(tokenAddress, ERC20_ABI, provider);
      const balance = await contract.balanceOf(address);
      const decimals = await contract.decimals();
      balances[symbol] = ethers.formatUnits(balance, decimals);
    }

    return balances;
  } catch (error) {
    console.error("Error fetching balances:", error);
    toast.error("Error fetching balances");
    return {};
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. Create a context for web3 and custom hooks
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Create a new file &lt;code&gt;src/contexts/web3Context.tsx&lt;/code&gt; and write the following code.
This code creates a context for web3 and provides the web3 context to the entire application.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"use client";

import {createContext, useCallback, useEffect, useMemo, useState} from "react";
import Web3 from "web3";
import { useAccount, useChainId } from "wagmi";
import { Contract, ContractRunner, ethers } from "ethers";

import SPXTokenContractABI from "@/abis/SPXTokenContractABI.json";
import PresaleContractABI from "@/abis/PresaleContractABI.json";
import { useEthersProvider, useEthersSigner } from "@/utils/web3Providers";
import {defaultRPC, SPXTokenContractAddress, PresaleContractAddress } from "@/data/constants";
import { Web3ContextType } from "@/types";

declare let window: any;

let web3: any;

const Web3Context = createContext&amp;lt;Web3ContextType | null&amp;gt;(null);

export const Web3Provider = ({ children }: { children: React.ReactNode }) =&amp;gt; {
  const { address, isConnected } = useAccount();
  const chainId = useChainId();
  const signer = useEthersSigner();
  const ethersProvider = useEthersProvider();
  let defaultProvider: any;
  if (chainId === 1) {
    defaultProvider = new ethers.JsonRpcProvider(defaultRPC.mainnet);
  } else if (chainId === 11155111) {
    defaultProvider = new ethers.JsonRpcProvider(defaultRPC.sepolia);
  }

  const [provider, setProvider] = useState&amp;lt;ContractRunner&amp;gt;(defaultProvider);
  const [SPXTokenContract, setSPXTokenContract] = useState&amp;lt;Contract&amp;gt;(
    {} as Contract
  );
  const [presaleContract, setPresaleContract] = useState&amp;lt;Contract&amp;gt;(
    {} as Contract
  );
  const [spxTokenAddress, setSPXTokenAddress] = useState&amp;lt;string&amp;gt;("");

  const init = useCallback(async () =&amp;gt; {
    try {
      if (typeof window !== "undefined") {
        web3 = new Web3(window.ethereum);
      }
      if (!isConnected || !ethersProvider) {
        // console.log("Not connected wallet");
      } else {
        setProvider(ethersProvider);
        // console.log("Connected wallet");
      }

      if (chainId === 1) {
        const _spxTokenContractWeb3: any = new web3.eth.Contract(
          SPXTokenContractABI,
          SPXTokenContractAddress.mainnet
        );
        const _presaleContractWeb3: any = new web3.eth.Contract(
          PresaleContractABI,
          PresaleContractAddress.mainnet
        );
        setSPXTokenContract(_spxTokenContractWeb3);
        setPresaleContract(_presaleContractWeb3);
        setSPXTokenAddress(SPXTokenContractAddress.mainnet);
      } else if (chainId === 11155111) {
        const _spxTokenContractWeb3: any = new web3.eth.Contract(
          SPXTokenContractABI,
          SPXTokenContractAddress.sepolia
        );
        const _presaleContractWeb3: any = new web3.eth.Contract(
          PresaleContractABI,
          PresaleContractAddress.sepolia
        );
        setSPXTokenContract(_spxTokenContractWeb3);
        setPresaleContract(_presaleContractWeb3);
        setSPXTokenAddress(SPXTokenContractAddress.sepolia);
      }
    } catch (err) {
      console.log(err);
    }
  }, [isConnected, ethersProvider, chainId]);

  useEffect(() =&amp;gt; {
    init();
  }, [init]);

  const value = useMemo(
    () =&amp;gt; ({
      account: address,
      chainId,
      isConnected,
      library: provider ?? signer,
      SPXTokenContract,
      presaleContract,
      spxTokenAddress,
      web3,
    }),
    [
      address,
      chainId,
      isConnected,
      provider,
      signer,
      SPXTokenContract,
      presaleContract,
      spxTokenAddress,
    ]
  );

  return &amp;lt;Web3Context.Provider value={value}&amp;gt;{children}&amp;lt;/Web3Context.Provider&amp;gt;;
};

export default Web3Context;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Then create a custom hook (&lt;code&gt;src/hooks/useWeb3.ts&lt;/code&gt;) to use the web3 context.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { useContext } from "react";
import Web3Context from "@/contexts/web3Context";

const useWeb3 = () =&amp;gt; {
  const context = useContext(Web3Context);
  if (!context) throw new Error("context must be use inside provider");
  return context;
};

export default useWeb3;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  5. Combine providers
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"use client";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { WagmiProvider } from "wagmi";
import { RainbowKitProvider, darkTheme } from "@rainbow-me/rainbowkit";
import { config } from "@/wagmi";
import "@rainbow-me/rainbowkit/styles.css";
import { Web3Provider } from "@/contexts/web3Context";
const client = new QueryClient();

export default function RootProvider({
  children,
}: Readonly&amp;lt;{
  children: React.ReactNode;
}&amp;gt;) {
  return (
    &amp;lt;WagmiProvider config={config}&amp;gt;
      &amp;lt;QueryClientProvider client={client}&amp;gt;
        &amp;lt;RainbowKitProvider
          coolMode={true}
          theme={darkTheme({
            accentColor: "#824B3D",
            accentColorForeground: "#dbdbcf",
            borderRadius: "small",
          })}
        &amp;gt;
          &amp;lt;Web3Provider&amp;gt;{children}&amp;lt;/Web3Provider&amp;gt;
        &amp;lt;/RainbowKitProvider&amp;gt;
      &amp;lt;/QueryClientProvider&amp;gt;
    &amp;lt;/WagmiProvider&amp;gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  6. Web3 Integration
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Connect wallet&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is the code for the connect wallet button.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { ConnectButton } from "@rainbow-me/rainbowkit";

const PreSale: React.FC = () =&amp;gt; {

  ....

 const handlePayAmountChange = async (
    e: React.ChangeEvent&amp;lt;HTMLInputElement&amp;gt; | { target: { value: string } }
  ) =&amp;gt; {
    if (preSaleStage !== PreSaleStage.Running) return;

    const value = e.target.value;
    const regex = /^\d*\.?\d{0,6}$/;
    if (!regex.test(value)) {
      return;
    }

    if (value === "" || value === "0") {
      setPayAmount(0);
      setTokenAmount(0);
    } else {
      try {
        const newPayAmount = ethers.parseUnits(value, 6);
        setPayAmount(parseFloat(value));
        if (paymentType === "ETH") {
          const ethAmount = ethers.parseEther(value);
          const temp = await presaleContract.methods
            .estimatedTokenAmountAvailableWithETH(ethAmount.toString())
            .call();
          const expectedTokenAmount = ethers.formatUnits(temp, 18);
          setTokenAmount(parseFloat(expectedTokenAmount));
        } else {
          const temp = await presaleContract.methods
            .estimatedTokenAmountAvailableWithCoin(
              newPayAmount.toString(),
              TOKENS[paymentType as keyof typeof TOKENS]
            )
            .call();
          const expectedTokenAmount =
            paymentType === "DAI"
              ? ethers.formatUnits(temp, 6)
              : ethers.formatUnits(temp, 18);
          setTokenAmount(parseFloat(expectedTokenAmount));
        }
      } catch (error) {
        console.error("Error fetching token amount:", error);
        toast.error("Error fetching token amount");
        setTokenAmount(0);
      }
    }
  };

    const handleBuy = async () =&amp;gt; {
    setPayAmount(0);
    setTokenAmount(0);
    try {
      switch (paymentType) {
        case "ETH":
          const ethAmount = ethers.parseEther(payAmount.toString());
          const txETH = await presaleContract.methods
            .buyWithETH()
            .send({ from: account, value: ethAmount.toString() });
          break;
        case "USDT":
          const txUSDT = await presaleContract.methods
            .buyWithUSDT(toBigInt(tokenAmount.toString()))
            .send({ from: account });
          break;
        case "USDC":
          const txUSDC = await presaleContract.methods
            .buyWithUSDC(toBigInt(tokenAmount.toString()))
            .send({ from: account });
          break;
        case "DAI":
          const txDAI = await presaleContract.methods
            .buyWithDAI(toBigInt(tokenAmount.toString()))
            .send({ from: account });
          break;
      }

      const balance = await presaleContract.methods
        .getTokenAmountForInvestor(account)
        .call();
      const formattedBalance = ethers.formatUnits(balance, 18);
      const tempFundsRaised = await presaleContract.methods
        .getFundsRaised()
        .call();
      const tempTokensAvailable = await presaleContract.methods
        .tokensAvailable()
        .call();
      const formattedTokensAvailable = parseFloat(
        ethers.formatUnits(tempTokensAvailable, 18)
      );

      fetchBalances();
      setFundsRaised(parseFloat(tempFundsRaised) / 1e6);
      setTokensAvailable(Math.floor(formattedTokensAvailable));
      setClaimableSPXBalance(formattedBalance);
    } catch (error) {
      toast.error("Transaction failed. Please try again.");
    }
  };

  ...

  return {
    &amp;lt;button
      className="max-w-[70%] w-full bg-[#824B3D] p-3 rounded font-bold mb-4 hover:bg-orange-800 truncate"
      onClick={account ? openAccountModal : openConnectModal}
    &amp;gt;
      {account ? account.displayName : "CONNECT WALLET"}
    &amp;lt;/button&amp;gt;

    {account &amp;amp;&amp;amp; (
    &amp;lt;button
      className="w-full bg-[#824B3D] p-3 rounded font-bold mb-4 hover:bg-orange-800 disabled:bg-[#333] disabled:cursor-not-allowed truncate"
      disabled={
        isLoading ||
        preSaleStage === PreSaleStage.Ready ||
        preSaleStage === PreSaleStage.Ended ||
        (preSaleStage === PreSaleStage.Running &amp;amp;&amp;amp;
          (payAmount &amp;lt; min ||
            payAmount &amp;gt;
              parseFloat(balances[paymentType]))) ||
        (preSaleStage === PreSaleStage.Claimable &amp;amp;&amp;amp;
          parseFloat(claimableSPXBalance) &amp;lt; 1)
      }
      onClick={
        preSaleStage !== PreSaleStage.Claimable
          ? handleBuy
          : handleClaim
      }
    &amp;gt;
      {preSaleStage &amp;lt; PreSaleStage.Ended
        ? "BUY"
        : "CLAIM"}
    &amp;lt;/button&amp;gt;
  )}

  ....
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Building a Web3 presale dApp with Next.js, Ethers.js, Wagmi, and RainbowKit creates a powerful and user-friendly platform for token sales. This implementation demonstrates how modern Web3 tools can create sophisticated yet accessible DeFi applications that bridge the gap between blockchain technology and mainstream users.&lt;/p&gt;

</description>
      <category>nextjs</category>
      <category>web3</category>
      <category>smartcontract</category>
      <category>typescript</category>
    </item>
    <item>
      <title>How to test smart contract on Sepolia testnet?</title>
      <dc:creator>Steven</dc:creator>
      <pubDate>Wed, 13 Nov 2024 05:00:38 +0000</pubDate>
      <link>https://dev.to/imcrazysteven/how-to-test-smart-contract-on-sepolia-testnet-i1d</link>
      <guid>https://dev.to/imcrazysteven/how-to-test-smart-contract-on-sepolia-testnet-i1d</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Testing smart contracts&lt;/strong&gt; on a testnet before mainnet deployment is crucial for ensuring functionality and security. The Sepolia testnet provides an ideal environment to test the smart contract with real network conditions without risking real assets.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Real-World Simulation:&lt;/em&gt;&lt;/strong&gt; Testing on a testnet mimics the actual blockchain environment, allowing developers to see how their contracts perform with real transactions and network conditions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Interaction with Other Contracts:&lt;/em&gt;&lt;/strong&gt; Testnets let developers check how their smart contracts work when interacting with other contracts or dApps, helping to catch potential issues that weren't apparent during unit testing.&lt;/p&gt;

&lt;p&gt;We have already done the &lt;a href="https://dev.to/imcrazysteven/thorough-testing-for-erc20-token-presale-smart-contract-using-hardhat-and-chai-3i4g"&gt;thorough unit testing using Hardhat and Chai&lt;/a&gt;, but we are going to check if this smart contract is working well on Sepolia testnet.&lt;/p&gt;

&lt;p&gt;This article will walk you through deploying the &lt;a href="https://dev.to/imcrazysteven/comprehensive-guide-to-write-erc20-token-presale-smart-contract-on-ethereum-blockchain-using-solidity-1h6e"&gt;SPX ERC20 token presale smart contract&lt;/a&gt; on the Sepolia testnet, an Ethereum test network and thorough testing it on sepolia etherscan.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deployment on Sepolia testnet
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Configure Hardhat settings
&lt;/h3&gt;

&lt;p&gt;First, we need to configure the Hardhat settings to deploy our smart contract on the Sepolia testnet.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create a new file named &lt;code&gt;hardhat.config.ts&lt;/code&gt; in the root directory of your project.&lt;/li&gt;
&lt;li&gt;Open the file and add the following code:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { HardhatUserConfig } from "hardhat/config";
import "@nomicfoundation/hardhat-toolbox";
import "dotenv/config";

const infuraKey: string = process.env.INFURA_API_KEY as string;
const privateKey: string = process.env.PRIVATE_KEY ? process.env.PRIVATE_KEY as string: "";
const etherscanKey: string = process.env.ETHERSCAN_KEY ? process.env.ETHERSCAN_KEY as string: "";

const config: HardhatUserConfig = {
  solidity: {
    version: "0.8.27",
    settings: {
      optimizer: {
        enabled: true,
        runs: 100,
      },
      viaIR: true,
    },
  },
  networks: {
    sepolia: {
      url: `https://sepolia.infura.io/v3/${infuraKey}`,
      accounts: [`0x${privateKey}`],
    },
    mainnet: {
      url: `https://mainnet.infura.io/v3/${infuraKey}`,
      accounts: [`0x${privateKey}`],
    },
    hardhat: {
      chainId: 31337,
    },
  },
  etherscan: {
    apiKey: {
      eth_mainnet: etherscanKey,
      eth_sepolia: etherscanKey
    },
  },
  gasReporter: {
    enabled: true,
  },
  sourcify: {
    enabled: true,
  },
};

export default config;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Second, we need to create a &lt;code&gt;.env&lt;/code&gt; file in the root directory of your project.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;INFURA_API_KEY=your_infura_api_key
PRIVATE_KEY=your_wallet_private_key
ETHERSCAN_KEY=your_etherscan_api_key
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Writing the deployment script
&lt;/h3&gt;

&lt;p&gt;First we need to deploy SPX ERC20 token smart contract, so let's create a new file named &lt;code&gt;deploy_SPX.ts&lt;/code&gt; in the &lt;code&gt;scripts&lt;/code&gt; directory of your project.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { ethers } from 'hardhat'

async function main() {
  const [deployer] = await ethers.getSigners();
  const instanceSPX = await ethers.deployContract("SPX");
  await instanceSPX.waitForDeployment()
  const SPX_Address = await instanceSPX.getAddress();
  console.log(`SPX is deployed. ${SPX_Address}`);
}

main()
  .then(() =&amp;gt; process.exit(0))
  .catch(error =&amp;gt; {
    console.error(error)
    process.exitCode = 1
  })
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, deploy the SPX ERC20 token smart contract on the Sepolia testnet.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx hardhat run scripts/deploy_SPX.ts --network sepolia
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the output, you will see the deployed SPX contract address.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SPX is deployed. 0xF4072Ee965121c2857EeBa0D3e3C6B9795403072
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Just copy it and paste it into the &lt;code&gt;spxAddress&lt;/code&gt; variable in the &lt;code&gt;deploy_Presale.ts&lt;/code&gt; script below. &lt;/p&gt;

&lt;p&gt;Similarly, create a new file named &lt;code&gt;deploy_Presale.ts&lt;/code&gt; in the &lt;code&gt;scripts&lt;/code&gt; directory of your project.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { ethers } from 'hardhat'

async function main() {
  const softcap = ethers.parseUnits("300000", 6);
  const hardcap = ethers.parseUnits("1020000", 6);
  const presaleStartTimeInMilliSeconds = new Date("2024-11-15T00:00:00Z"); //2024-11-15T00:00:00Z 
  const presaleStartTime = Math.floor(presaleStartTimeInMilliSeconds.getTime() / 1000);

  const presaleDuration = 24 * 3600 * 30;  //30 days
  const presaleTokenPercent = 10;
  const spxAddress = "0xF4072Ee965121c2857EeBa0D3e3C6B9795403072";   //Deployed SPX token contract on Sepolia

  const [deployer] = await ethers.getSigners();

  const instancePresale = await ethers.deployContract("Presale", [softcap, hardcap, presaleStartTime, presaleDuration, ficcoAddress, presaleTokenPercent]);
  await instancePresale.waitForDeployment();
  const Presale_Address = await instancePresale.getAddress();
  console.log(`Presale is deployed to ${Presale_Address} and presale start time is ${presaleStartTime}`);       
}

main()
  .then(() =&amp;gt; process.exit(0))
  .catch(error =&amp;gt; {
    console.error(error)
    process.exitCode = 1
  })
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, deploy the presale smart contract on the Sepolia testnet.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx hardhat run scripts/deploy_Presale.ts --network sepolia
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the output, you will see the deployed Presale contract address.&lt;br&gt;
The output should be similar to the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Presale is deployed to 0x2Ae586f1EbE743eFDCD371E939757EEb42dC6CA7 and presale start time is 1731542400
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Testing the Presale smart contract on Sepolia testnet
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Verification of the SPX token contract and Presale smart contract on Sepolia testnet
&lt;/h3&gt;

&lt;p&gt;In order to test the SPX token contract and Presale smart contract on Sepolia testnet, we need to verify them on &lt;a href="https://sepolia.etherscan.io/" rel="noopener noreferrer"&gt;Sepolia Etherscan&lt;/a&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;SPX token contract verification
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx hardhat verify 0xF4072Ee965121c2857EeBa0D3e3C6B9795403072 --network sepolia
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The command structure is &lt;code&gt;npx hardhat verify &amp;lt;contract_address&amp;gt; &amp;lt;contract constructor arguments&amp;gt; --network &amp;lt;network_name&amp;gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The output should be similar to the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;The contract 0xF4072Ee965121c2857EeBa0D3e3C6B9795403072 has been successfully verified on the block explorer. 
https://sepolia.etherscan.io/address/0xF4072Ee965121c2857EeBa0D3e3C6B9795403072#code

The contract 0xF4072Ee965121c2857EeBa0D3e3C6B9795403072 has already been verified on Sourcify.
https://repo.sourcify.dev/contracts/full_match/11155111/0xF4072Ee965121c2857EeBa0D3e3C6B9795403072/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Presale smart contract verification
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx hardhat verify 0x2Ae586f1EbE743eFDCD371E939757EEb42dC6CA7 300000000000 1020000000000 1731542400 2592000 0xF4072Ee965121c2857EeBa0D3e3C6B9795403072 10 --network sepolia
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, we need to pass the constructor arguments of the Presale smart contract one by one with &lt;code&gt;space&lt;/code&gt; .&lt;/p&gt;

&lt;p&gt;The output should be similar to the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;The contract 0x2Ae586f1EbE743eFDCD371E939757EEb42dC6CA7 has been successfully verified on the block explorer.
https://sepolia.etherscan.io/address/0x2Ae586f1EbE743eFDCD371E939757EEb42dC6CA7#code

Successfully verified contract Presale on Sourcify.
https://repo.sourcify.dev/contracts/full_match/11155111/0x2Ae586f1EbE743eFDCD371E939757EEb42dC6CA7/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Testing the Presale smart contract on Sepolia testnet
&lt;/h3&gt;

&lt;p&gt;You can test the Presale smart contract on Sepolia testnet with Sepolia Etherscan.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Go to the &lt;a href="https://sepolia.etherscan.io/" rel="noopener noreferrer"&gt;Sepolia Etherscan&lt;/a&gt; website and search for the SPX ERC20 token contract address and Presale smart contract address.&lt;/li&gt;
&lt;li&gt;Click on the "Contract" tab.&lt;/li&gt;
&lt;li&gt;Click on the "Read Contract" tab.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can see all the read-only functions of the SPX ERC20 token contract and Presale smart contract.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7puknw9lg2wix6ck9sod.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7puknw9lg2wix6ck9sod.png" alt="Read functions of Presale smart contract" width="800" height="540"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Click on the "Write Contract" tab.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can see all the write functions of the SPX ERC20 token contract and Presale smart contract.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu403l7p7fiyu5d4gjxjb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu403l7p7fiyu5d4gjxjb.png" alt="Write functions of Presale smart contract" width="800" height="447"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Connect your MetaMask wallet to Sepolia testnet.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Make sure you have enough Sepolia ETH and USDT, USDC and DAI faucet in your MetaMask wallet.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We can test all the functionalities of the SPX ERC20 token contract and Presale smart contract here.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Before Presale:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Transfer tokens to presale contract.&lt;/li&gt;
&lt;li&gt;Verify token balance&lt;/li&gt;
&lt;li&gt;Check the &lt;code&gt;buy&lt;/code&gt; functions not working before presale starts&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;During Presale:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Test buying tokens with different payment methods&lt;/li&gt;
&lt;li&gt;Verify investment amounts&lt;/li&gt;
&lt;li&gt;Check token allocation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;After Presale:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Set claim time&lt;/li&gt;
&lt;li&gt;Test claim function&lt;/li&gt;
&lt;li&gt;Verify token distribution&lt;/li&gt;
&lt;li&gt;Test withdrawal or refund based on softcap achievement&lt;/li&gt;
&lt;li&gt;Check the &lt;code&gt;buy&lt;/code&gt; functions not working after presale ends&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Make sure all the &lt;code&gt;require&lt;/code&gt; statements are working properly and &lt;code&gt;function&lt;/code&gt;s are working correctly with the correct input and output and within correct timespan.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Testing on Sepolia Testnet provides a realistic environment to validate:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Contract functionality&lt;/li&gt;
&lt;li&gt;Security measures&lt;/li&gt;
&lt;li&gt;Gas optimization&lt;/li&gt;
&lt;li&gt;User interaction flows&lt;/li&gt;
&lt;li&gt;Integration with other contracts&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Always verify all functions thoroughly before proceeding to mainnet deployment. The systematic testing approach on Sepolia ensures a smooth and secure launch on the mainnet.&lt;/p&gt;

</description>
      <category>smartcontract</category>
      <category>sepolia</category>
      <category>testing</category>
      <category>etherscan</category>
    </item>
    <item>
      <title>Thorough Testing for ERC20 Token Presale Smart Contract using Hardhat and Chai</title>
      <dc:creator>Steven</dc:creator>
      <pubDate>Sat, 09 Nov 2024 18:43:39 +0000</pubDate>
      <link>https://dev.to/imcrazysteven/thorough-testing-for-erc20-token-presale-smart-contract-using-hardhat-and-chai-3i4g</link>
      <guid>https://dev.to/imcrazysteven/thorough-testing-for-erc20-token-presale-smart-contract-using-hardhat-and-chai-3i4g</guid>
      <description>&lt;h3&gt;
  
  
  Introduction
&lt;/h3&gt;

&lt;p&gt;This article shows how to thoroughly test a &lt;a href="https://dev.to/imcrazysteven/comprehensive-guide-to-write-erc20-token-presale-smart-contract-on-ethereum-blockchain-using-solidity-1h6e"&gt;SPX ERC20 token presale smart contract&lt;/a&gt; using Hardhat and Chai on Ethereum Sepolia testnet. We'll cover testing all key functionalities including token purchases with multiple currencies, claiming, refunds and withdraw functions.&lt;/p&gt;

&lt;h3&gt;
  
  
  Prerequisites
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Hardhat development environment&lt;/li&gt;
&lt;li&gt;Chai assertion library&lt;/li&gt;
&lt;li&gt;Ethereum-waffle for blockchain testing&lt;/li&gt;
&lt;li&gt;Basic understanding of TypeScript testing&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Testing Presale smart contract step by step
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Test Structure
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;describe('Presale Contract', async function () {
    // Before testing, deploy SPX ERC20 token smart contract and Presale smart contract 
    before(async function () {
        // Deploy SPX, Presale, USDT, USDC, DAI token contract to Sepolia testnet and gets contract instance.
        // Approve presale contract to spend USDT, USDC, and DAI from investors
    })

    describe("Presale setup", function () {
        //Checks if sufficient tokens are available in presale contract
    })

    // Test token purchase functions
    describe("Buying SPX with USDT", function () {
        // It should not allow investors spending USDT more thatn allowance
        // It should allow investors buying SPX tokens with USDT
        // It should not allow investors buying SPX tokens before presale starts
        // It should now allow investors buying SPX tokens after presale ends 
    })
    describe("Buying SPX with USDC", function () {
        // It should not allow investors spending USDC more thatn allowance
        // It should allow investors buying SPX tokens with USDC
        // It should not allow investors buying SPX tokens before presale starts
        // It should now allow investors buying SPX tokens after presale ends 
    })
    describe("Buying SPX with DAI", function () {
        // It should not allow investors spending DAI more thatn allowance
        // It should allow investors buying SPX tokens with DAI
        // It should not allow investors buying SPX tokens before presale starts
        // It should now allow investors buying SPX tokens after presale ends 
    })
    describe("Buying SPX with ETH", function () {
        // It should allow investors buying SPX tokens with ETH
        // It should not allow investors buying SPX tokens before presale starts
        // It should now allow investors buying SPX tokens after presale ends 
    })

    // Test admin functions
    describe("Claim functionality", function () {
        before(async function () {
            // Set the claim time before each test
        })
        // It should revert if trying to claim tokens before claim time is set
        // It should correctly distribute bonus tokens among multiple early investors
        // It should allow investors to claim their tokens
        // It should revert if a non-owner tries to set the claim time
    })

    describe("Withdraw functionality", function () {
        before(async function () {
            // Set the predefined multisig wallet before each test       
        })
        // It should allow the owner to withdraw balance of contract to wallet after presale ends
        // It should revert if non-owner tries to withdraw
        // It should revert if a non-owner tries to set the wallet
        // It should revert if trying to withdraw before the presale ends
    })

    describe("Refund Functionality", function () {
        // It should allow the owner to refund to investors if softcap is not reached
        // It should revert if non-owner tries to refund
        // It should revert if trying to refund before the presale ends
    })
})
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Important Key Test Cases
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Presale setup&lt;/em&gt;&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;describe("Presale setup", function () {
    it("should set up presale correctly", async function () {
        expect(await presale.getFundsRaised()).to.equal(0);
        expect(await presale.tokensAvailable()).to.equal(presaleSupply);
    });
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;&lt;em&gt;Token purchase function test&lt;/em&gt;&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;describe("Buying SPX with USDT", function () {
    it("should not allow investors spending usdt more than allowance", async function () {
        const tokenAmount = ethers.parseUnits("20000000", 18); 
        await expect(presale.connect(investor1).buyWithUSDT(tokenAmount))
            .to.be.revertedWith("Insufficient allowance set for the contract.");
    });

    it("should allow investors buying SPX tokens with USDT.", async function () {
        const tokenAmount = ethers.parseUnits("1500000", 18); 
        const usdtAmount = await presale.estimatedCoinAmountForTokenAmount(tokenAmount, usdtMockInterface);

        const investmentsforUserBeforeTx1 = await presale.getInvestments(investor1.address, SEPOLIA_USDT);
        const investmentsforUserBeforeTx2 = await presale.getInvestments(investor2.address, SEPOLIA_USDT);
        const fundsRaisedBeforeTx = await presale.getFundsRaised();
        const investorTokenBalanceBeforeTx1 = await presale.getTokenAmountForInvestor(investor1.address);
        const investorTokenBalanceBeforeTx2 = await presale.getTokenAmountForInvestor(investor2.address);
        const tokensAvailableBeforeTx = await presale.tokensAvailable();

        const tx1 = await presale.connect(investor1).buyWithUSDT(tokenAmount);
        await tx1.wait();
        const tx2 = await presale.connect(investor2).buyWithUSDT(tokenAmount);
        await tx2.wait();

        const investmentsforUserAfterTx1 = await presale.getInvestments(investor1.address, SEPOLIA_USDT);
        const investmentsforUserAfterTx2 = await presale.getInvestments(investor2.address, SEPOLIA_USDT);
        const fundsRaisedAfterTx = await presale.getFundsRaised();
        const investorTokenBalanceAfterTx1 = await presale.getTokenAmountForInvestor(investor1.address);
        const investorTokenBalanceAfterTx2 = await presale.getTokenAmountForInvestor(investor2.address);
        const tokensAvailableAfterTx = await presale.tokensAvailable();

        expect(investorTokenBalanceAfterTx1).to.equal(investorTokenBalanceBeforeTx1 + tokenAmount);
        expect(investorTokenBalanceAfterTx2).to.equal(investorTokenBalanceBeforeTx2 + tokenAmount);
        expect(tokensAvailableAfterTx).to.equal(tokensAvailableBeforeTx - BigInt(2) * tokenAmount);
        expect(investmentsforUserAfterTx1).to.equal(investmentsforUserBeforeTx1 + usdtAmount);
        expect(investmentsforUserAfterTx2).to.equal(investmentsforUserBeforeTx2 + usdtAmount);
        expect(fundsRaisedAfterTx).to.equal(fundsRaisedBeforeTx + usdtAmount * BigInt(2) / BigInt(1000000));
    });

    //Before presale starts
    it("should not allow investors buying SPX tokens before presale starts", async function () {
        const tokenAmount = ethers.parseUnits("1500000", 18);
        await expect(presale.connect(investor1).buyWithUSDT(tokenAmount))
            .to.be.revertedWith("Invalid time for buying the token.");
    });

    //After presale ends
    it("should not allow investors buying SPX tokens after presale ends", async function () {
        const tokenAmount = ethers.parseUnits("1500000", 18);
        await expect(presale.connect(investor1).buyWithUSDT(tokenAmount))
            .to.be.revertedWith("Invalid time for buying the token.");
    });
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This test suite "Buying SPX with USDT" contains four test cases that verify different scenarios:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;u&gt;Testing insufficient allowance:&lt;/u&gt;&lt;/em&gt; &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Tests buying 20,000,000 SPX tokens (worth 1600 USDT) Investors have 1000 USDT allowance&lt;/li&gt;
&lt;li&gt;Expects transaction to fail with "Insufficient allowance" error&lt;/li&gt;
&lt;li&gt;Verifies allowance checks are working properly&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;&lt;u&gt;Testing successful token purchase:&lt;/u&gt;&lt;/em&gt; &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Tests buying 1,500,000 SPX tokens (worth 120 USDT)&lt;/li&gt;
&lt;li&gt;Records state before transactions:

&lt;ul&gt;
&lt;li&gt;USDT investments for both investors&lt;/li&gt;
&lt;li&gt;Total funds raised&lt;/li&gt;
&lt;li&gt;Token balances&lt;/li&gt;
&lt;li&gt;Available tokens&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Executes purchases for two investors&lt;/li&gt;

&lt;li&gt;Verifies after transaction:

&lt;ul&gt;
&lt;li&gt;Token balances increased correctly&lt;/li&gt;
&lt;li&gt;Available tokens decreased properly&lt;/li&gt;
&lt;li&gt;Investment amounts recorded accurately&lt;/li&gt;
&lt;li&gt;Funds raised updated correctly&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;&lt;u&gt;Testing presale timing restrictions (before start):&lt;/u&gt;&lt;/em&gt; &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Attempts purchase before presale start time&lt;/li&gt;
&lt;li&gt;Expects revert with "Invalid time" message&lt;/li&gt;
&lt;li&gt;Verifies timing restrictions work&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;&lt;u&gt;Testing presale timing restrictions (after end):&lt;/u&gt;&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Attempts purchase after presale end time&lt;/li&gt;
&lt;li&gt;Expects revert with "Invalid time" message&lt;/li&gt;
&lt;li&gt;Verifies presale end time enforcement&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This test suite ensures the USDT purchase functionality works correctly under all expected conditions.&lt;br&gt;
Similarly, we can write test suite for USDC, DAI, ETH purchase functionality.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Claim function test&lt;/em&gt;&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;describe("Claim functionality", function () {
    before(async function () {
        // Set the claim time before each test
        const setClaimTimeTx = await presale.connect(owner).setClaimTime(claimTime);
        await setClaimTimeTx.wait();
    });

    //Before presale ends
    it("should revert if trying to claim tokens before claim time is set", async function () {
        await presale.connect(investor1).buyWithUSDT(ethers.parseUnits("1500000", 18));
        await expect(presale.connect(investor1).claim(investor1.address)).to.be.revertedWith("It's not claiming time yet.");
    });

    it("should correctly distribute bonus tokens among multiple early investors", async function () {
        expect(await presale.isEarlyInvestors(investor1.address)).to.be.true;
        expect(await presale.isEarlyInvestors(investor2.address)).to.be.true;
    });

    it("should allow investors to claim their tokens", async function () {
        const initialBalance = await spx.balanceOf(investor2.address);
        const tokenAmount = await presale.getTokenAmountForInvestor(investor2.address);
        const bonusTokenAmount = await presale.getBonusTokenAmount();

        const claimTx = await presale.connect(investor2).claim(investor2.address);
        await claimTx.wait();
        const finalBalance = await spx.balanceOf(investor2.address);

        expect(finalBalance - initialBalance).to.equal(tokenAmount + bonusTokenAmount);
        expect(await presale.getTokenAmountForInvestor(investor2.address)).to.equal(0);
        //Second claim
        await expect(presale.connect(investor2).claim(investor2.address))
            .to.be.revertedWith("No tokens claim.");
    });

    it("should revert if a non-owner tries to set the claim time", async function () {
        await expect(presale.connect(investor1).setClaimTime(claimTime)).to.be.revertedWithCustomError(presale, "NotOwner");
    });
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This Claim test suite verifies the token claiming process with five key test cases:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;u&gt;Initial setup:&lt;/u&gt;&lt;/em&gt; &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Sets up claim time before running tests&lt;/li&gt;
&lt;li&gt;Uses owner account to set claim time&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;&lt;u&gt;Early Claim Prevention:&lt;/u&gt;&lt;/em&gt; &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Tests claiming before proper time&lt;/li&gt;
&lt;li&gt;Buys tokens with USDT first&lt;/li&gt;
&lt;li&gt;Verifies claim attempt fails with timing error&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;&lt;u&gt;Early Investor Bonus Verification:&lt;/u&gt;&lt;/em&gt; &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Checks early investor status for two investors&lt;/li&gt;
&lt;li&gt;Verifies both are marked as early investors&lt;/li&gt;
&lt;li&gt;Ensures bonus distribution eligibility&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;&lt;u&gt;Token Claiming Process:&lt;/u&gt;&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Records initial token balance&lt;/li&gt;
&lt;li&gt;Gets expected token amount and bonus&lt;/li&gt;
&lt;li&gt;Executes claim transaction&lt;/li&gt;
&lt;li&gt;Verifies:

&lt;ul&gt;
&lt;li&gt;Final balance matches expected amount&lt;/li&gt;
&lt;li&gt;Token balance reset to zero&lt;/li&gt;
&lt;li&gt;Second claim attempt fails&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;&lt;u&gt;Token Claiming Process:&lt;/u&gt;&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Tests unauthorized claim time setting&lt;/li&gt;
&lt;li&gt;Verifies only owner can set claim time&lt;/li&gt;
&lt;li&gt;Checks custom error handling&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This test suite ensures the claiming mechanism works correctly and securely under various conditions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Withdraw function test&lt;/em&gt;&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;describe("Withdraw functionality", function () {
    before(async function () {
        const setWalletTx = await presale.connect(owner).setWallet(wallet.address);
        await setWalletTx.wait();
    })

    it("should allow the owner to withdraw balance of contract to wallet after presale ends", async function () {
        const initialUSDTBalance = await usdtMockInterface.balanceOf(wallet.address);
        const initialUSDCBalance = await usdcMockInterface.balanceOf(wallet.address);
        const initialDAIBalance = await daiMockInterface.balanceOf(wallet.address);

        const usdtAmount = await usdtMockInterface.balanceOf(presaleAddress);
        const usdcAmount = await usdcMockInterface.balanceOf(presaleAddress);
        const daiAmount = await daiMockInterface.balanceOf(presaleAddress);

        const withdrawTx = await presale.connect(owner).withdraw();
        await withdrawTx.wait();

        const finalUSDTBalance = await usdtMockInterface.balanceOf(wallet.address);
        const finalUSDCBalance = await usdcMockInterface.balanceOf(wallet.address);
        const finalDAIBalance = await daiMockInterface.balanceOf(wallet.address);

        expect(finalUSDTBalance).to.equal(initialUSDTBalance + usdtAmount);
        expect(finalUSDCBalance).to.equal(initialUSDCBalance + usdcAmount);
        expect(finalDAIBalance).to.equal(initialDAIBalance + daiAmount);
    });

    it("should revert if non-owner tries to withdraw", async function () {
        await expect(presale.connect(investor1).withdraw()).to.be.revertedWithCustomError(presale, "NotOwner");
    });

    it("should revert if a non-owner tries to set the wallet", async function () {
        await expect(presale.connect(investor1).setWallet(wallet)).to.be.revertedWithCustomError(presale, "NotOwner");
    });

    //Before presale ends
    it("should revert if trying to withdraw before the presale ends", async function () {
        await expect(presale.connect(owner).withdraw())
            .to.be.revertedWith("Cannot withdraw because presale is still in progress.");
    })
})
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This Withdraw test suite verifies the token claiming process with five key test cases:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;u&gt;Initial setup:&lt;/u&gt;&lt;/em&gt; &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Sets withdraw wallet address before tests&lt;/li&gt;
&lt;li&gt;Uses owner account to configure wallet&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;&lt;u&gt;Owner Withdraw Testing:&lt;/u&gt;&lt;/em&gt; &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Records initial balances of USDT, USDC, DAI in wallet&lt;/li&gt;
&lt;li&gt;Gets contract balances for all tokens&lt;/li&gt;
&lt;li&gt;Executes withdraw&lt;/li&gt;
&lt;li&gt;Verifies final balances match expected amounts:

&lt;ul&gt;
&lt;li&gt;USDT balance increased correctly&lt;/li&gt;
&lt;li&gt;USDC balance increased correctly&lt;/li&gt;
&lt;li&gt;DAI balance increased correctly&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;&lt;u&gt;Unauthorized Withdraw Prevention:&lt;/u&gt;&lt;/em&gt; &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Tests withdraw with non-owner account&lt;/li&gt;
&lt;li&gt;Verifies custom error "NotOwner" is thrown&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;&lt;u&gt;Wallet Setting Access Control:&lt;/u&gt;&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Tests unauthorized wallet address setting&lt;/li&gt;
&lt;li&gt;Verifies only owner can set wallet address&lt;/li&gt;
&lt;li&gt;Checks custom error handling&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;&lt;u&gt;Early Withdraw Prevention:&lt;/u&gt;&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Tests withdraw before presale end time&lt;/li&gt;
&lt;li&gt;Verifies timing restriction message&lt;/li&gt;
&lt;li&gt;Ensures funds are locked during presale&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This test suite ensures the withdrawl mechanism works correctly and securely under various conditions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Refund function test&lt;/em&gt;&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;describe("Refund Functionality", function () {
    it("should allow the owner to refund to investors if softcap is not reached", async function () {
        const investor1USDTInitialBalance = await usdtMockInterface.balanceOf(investor1.address);
        const investor2USDTInitialBalance = await usdtMockInterface.balanceOf(investor2.address);
        const investor1USDCInitialBalance = await usdcMockInterface.balanceOf(investor1.address);
        const investor2USDCInitialBalance = await usdcMockInterface.balanceOf(investor2.address);
        const investor1DAIInitialBalance = await daiMockInterface.balanceOf(investor1.address);
        const investor2DAIInitialBalance = await daiMockInterface.balanceOf(investor2.address);

        const investor1USDTAmount = await presale.getInvestments(investor1.address, usdtMockInterface);
        const investor2USDTAmount = await presale.getInvestments(investor2.address, usdtMockInterface);
        const investor1USDCAmount = await presale.getInvestments(investor1.address, usdcMockInterface);
        const investor2USDCAmount = await presale.getInvestments(investor2.address, usdcMockInterface);
        const investor1DAIAmount = await presale.getInvestments(investor1.address, daiMockInterface);
        const investor2DAIAmount = await presale.getInvestments(investor2.address, daiMockInterface);

        await usdtMockInterface.connect(investor1).approve(presaleAddress, investor1USDTAmount);
        await usdtMockInterface.connect(investor2).approve(presaleAddress, investor2USDTAmount);
        await usdcMockInterface.connect(investor1).approve(presaleAddress, investor1USDCAmount);
        await usdcMockInterface.connect(investor2).approve(presaleAddress, investor2USDCAmount);
        await daiMockInterface.connect(investor1).approve(presaleAddress, investor1DAIAmount);
        await daiMockInterface.connect(investor2).approve(presaleAddress, investor2DAIAmount);

        const tx = await presale.connect(owner).refund();
        await tx.wait();

        const investor1USDTFinalBalance = await usdtMockInterface.balanceOf(investor1.address);
        const investor2USDTFinalBalance = await usdtMockInterface.balanceOf(investor2.address);
        const investor1USDCFinalBalance = await usdcMockInterface.balanceOf(investor1.address);
        const investor2USDCFinalBalance = await usdcMockInterface.balanceOf(investor2.address);
        const investor1DAIFinalBalance = await daiMockInterface.balanceOf(investor1.address);
        const investor2DAIFinalBalance = await daiMockInterface.balanceOf(investor2.address);

        expect(investor1USDTFinalBalance).to.equal(investor1USDTInitialBalance + investor1USDTAmount);
        expect(investor2USDTFinalBalance).to.equal(investor2USDTInitialBalance + investor2USDTAmount);
        expect(investor1USDCFinalBalance).to.equal(investor1USDCInitialBalance + investor1USDCAmount);
        expect(investor2USDCFinalBalance).to.equal(investor2USDCInitialBalance + investor2USDCAmount);
        expect(investor1DAIFinalBalance).to.equal(investor1DAIInitialBalance + investor1DAIAmount);
        expect(investor2DAIFinalBalance).to.equal(investor2DAIInitialBalance + investor2DAIAmount);
    });

    it("should revert if non-owner tries to refund", async function () {
        await expect(presale.connect(investor1).refund())
            .to.be.revertedWithCustomError(presale, "NotOwner");
    });

    //After presale ends
    it("should revert if trying to refund before the presale ends", async function () {
        await expect(presale.connect(owner).refund())
            .to.be.revertedWith("Cannot refund because presale is still in progress.");
    })
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This Refund test suite verifies the token refunding process with three key test cases:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;u&gt;Owner Refund Testing:&lt;/u&gt;&lt;/em&gt; &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Records initial balances for all investors in USDT, USDC, DAI&lt;/li&gt;
&lt;li&gt;Gets investment amounts for each investor and token&lt;/li&gt;
&lt;li&gt;Sets up approvals for all tokens and investors&lt;/li&gt;
&lt;li&gt;Executes refund transaction&lt;/li&gt;
&lt;li&gt;Verifies final balances:

&lt;ul&gt;
&lt;li&gt;USDT balances returned correctly&lt;/li&gt;
&lt;li&gt;USDC balances returned correctly&lt;/li&gt;
&lt;li&gt;DAI balances returned correctly&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Ensures each investor receives their exact investment back&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;&lt;u&gt;Unauthorized Refund Prevention:&lt;/u&gt;&lt;/em&gt; &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Tests refund with non-owner account&lt;/li&gt;
&lt;li&gt;Verifies custom error "NotOwner" is thrown&lt;/li&gt;
&lt;li&gt;Ensures only owner can initiate refunds&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;&lt;u&gt;Early Refund Prevention:&lt;/u&gt;&lt;/em&gt; &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Tests refund before presale end time&lt;/li&gt;
&lt;li&gt;Verifies timing restriction message&lt;/li&gt;
&lt;li&gt;Ensures refunds can't happen during active presale&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This ensures the refund mechanism works correctly and securely for all investors and tokens under various conditions.&lt;/p&gt;

&lt;h3&gt;
  
  
  Best Practices
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Use before hooks for test setup&lt;/li&gt;
&lt;li&gt;Test both success and failure cases&lt;/li&gt;
&lt;li&gt;Verify events and state changes&lt;/li&gt;
&lt;li&gt;Test access control&lt;/li&gt;
&lt;li&gt;Use helper functions for common operations&lt;/li&gt;
&lt;li&gt;Maintain test isolation&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;Thorough testing is crucial for presale contracts handling significant value. This testing approach ensures the contract behaves correctly under all conditions while maintaining security and fairness for all participants.&lt;/p&gt;

</description>
      <category>erc20</category>
      <category>presale</category>
      <category>test</category>
      <category>smartcontract</category>
    </item>
    <item>
      <title>Comprehensive guide to write ERC20 token presale smart contract on Ethereum blockchain using Solidity</title>
      <dc:creator>Steven</dc:creator>
      <pubDate>Sat, 09 Nov 2024 07:52:29 +0000</pubDate>
      <link>https://dev.to/imcrazysteven/comprehensive-guide-to-write-erc20-token-presale-smart-contract-on-ethereum-blockchain-using-solidity-1h6e</link>
      <guid>https://dev.to/imcrazysteven/comprehensive-guide-to-write-erc20-token-presale-smart-contract-on-ethereum-blockchain-using-solidity-1h6e</guid>
      <description>&lt;h3&gt;Introduction&lt;/h3&gt;

&lt;p&gt;This article will give you a comprehensive guide to build a presale contract that accepts ETH and major stablecoins step by step.&lt;/p&gt;

&lt;h5&gt; Key Features &lt;/h5&gt;

&lt;ul&gt;
&lt;li&gt;Multiple payment options(ETH, USDT, USDC, DAI)&lt;/li&gt;
&lt;li&gt;Early Investor bonus system&lt;/li&gt;
&lt;li&gt;Staged token buying campaign&lt;/li&gt;
&lt;/ul&gt;

&lt;h5&gt; Prerequisites &lt;/h5&gt;

&lt;ul&gt;
&lt;li&gt;Hardhat development environment&lt;/li&gt;
&lt;li&gt;Openzeppelin contracts&lt;/li&gt;
&lt;li&gt;Ethereum development experience&lt;/li&gt;
&lt;li&gt;Basic understanding of ERC20 tokens&lt;/li&gt;
&lt;/ul&gt;

&lt;h5&gt; Token features &lt;/h5&gt;

&lt;ul&gt;
&lt;li&gt;Type: ERC20&lt;/li&gt;
&lt;li&gt;Name: Silver Phoenix&lt;/li&gt;
&lt;li&gt;Symbol: SPX&lt;/li&gt;
&lt;li&gt;Decimal: 18&lt;/li&gt;
&lt;li&gt;Total Supply: 100 billion&lt;/li&gt;
&lt;/ul&gt;

&lt;h5&gt; Presale Features &lt;/h5&gt;

&lt;ul&gt;
&lt;li&gt;Presale Supply: 10 billion (10%)&lt;/li&gt;
&lt;li&gt;Presale Period: 30 days&lt;/li&gt;
&lt;li&gt;Presale Stage: 4&lt;/li&gt;
&lt;li&gt;Softcap: 500000 USDT&lt;/li&gt;
&lt;li&gt;Hardcap: 1020000 USDT
&lt;/li&gt;
&lt;li&gt;Price and token amounts for each stage:&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
  &lt;tr&gt;
   &lt;th&gt;Stage&lt;/th&gt;
   &lt;th&gt;Price&lt;/th&gt;
   &lt;th&gt;Token Amount&lt;/th&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;1
   &lt;/td&gt;
&lt;td&gt;0.00008 USDT
   &lt;/td&gt;
&lt;td&gt;3 billion
  &lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;2
   &lt;/td&gt;
&lt;td&gt;0.00010 USDT
   &lt;/td&gt;
&lt;td&gt;4 billion
  &lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;3
   &lt;/td&gt;
&lt;td&gt;0.00012 USDT
   &lt;/td&gt;
&lt;td&gt;2 billion
  &lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;4
   &lt;/td&gt;
&lt;td&gt;0.00014 USDT
   &lt;/td&gt;
&lt;td&gt;1 billion
  &lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;Options for buying tokens: ETH, USDT, USDC, DAI&lt;/li&gt;
&lt;li&gt;Claim time: After second public sale ends&lt;/li&gt;
&lt;li&gt;Minimum amount for buying tokens: 100 USDT&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Investors who bought tokens before softcap reached are listed on early investors and can get bonus tokens after presale ends if unsold tokens exist.&lt;/p&gt;

&lt;h3&gt; How to implement step by step &lt;/h3&gt;

&lt;h5&gt; Key functions: &lt;/h5&gt;

&lt;ul&gt;
&lt;li&gt;buyWithETH&lt;/li&gt;
&lt;li&gt;buyWithStableCoin&lt;/li&gt;
&lt;li&gt;Helper functions for calculating token amounts available with ETH or Stable coin and vice versa&lt;/li&gt;
&lt;li&gt;Claim &lt;/li&gt;
&lt;li&gt;Withdraw&lt;/li&gt;
&lt;li&gt;Refund&lt;/li&gt;
&lt;li&gt;Several helper functions like set and get functions&lt;/li&gt;
&lt;/ul&gt;

&lt;h5&gt;
  
  
  &lt;em&gt;Buy SPX token with ETH&lt;/em&gt;
&lt;/h5&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function buyWithETH() external payable whenNotPaused nonReentrant {
    require(
        block.timestamp &amp;gt;= startTime &amp;amp;&amp;amp; block.timestamp &amp;lt;= endTime,
        "Invalid time for buying the token"
    );

    uint256 _estimatedTokenAmount = estimatedTokenAmountAvailableWithETH(
        msg.value
    );
    uint256 _tokensAvailable = tokensAvailable();

    require(
        _estimatedTokenAmount &amp;lt;= _tokensAvailable &amp;amp;&amp;amp;
            _estimatedTokenAmount &amp;gt; 0,
        "Invalid token amount to buy"
    );

    uint256 minUSDTOutput = (_estimatedTokenAmount * 90) / 100;
    // Swap ETH for USDT
    address[] memory path = new address[](2);
    path[0] = router.WETH();
    path[1] = USDT;

    uint256[] memory amounts = router.swapExactETHForTokens{
        value: msg.value
    }(minUSDTOutput, path, address(this), block.timestamp + 15 minutes);

    // Ensure the swap was successful
    require(amounts.length &amp;gt; 1, "Swap failed, no USDT received");
    uint256 _usdtAmount = amounts[1];

    // Calculate final token amount
    uint256 _tokenAmount = estimatedTokenAmountAvailableWithCoin(
        _usdtAmount,
        USDTInterface
    );

    //Update investor records
    _updateInvestorRecords(
        msg.sender,
        _tokenAmount,
        USDTInterface,
        _usdtAmount
    );

    //Update presale stats
    _updatePresaleStats(_tokenAmount, _usdtAmount, 6);

    emit TokensBought(
        msg.sender,
        _tokenAmount,
        _usdtAmount,
        block.timestamp
    );
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;First&lt;/strong&gt;, this function checks if presale is ongoing or not.&lt;br&gt;
&lt;strong&gt;Next&lt;/strong&gt;, it estimates how much tokens investor can buy with specific ETH amount and checks if this amount is availbale for purchase.&lt;br&gt;
&lt;strong&gt;Next&lt;/strong&gt;, it swaps ETH to USDT using Uniswap V2 Router for buying SPX tokens and returns USDT amount equivalent.&lt;br&gt;
&lt;strong&gt;Next&lt;/strong&gt;, it calculates how much tokens investor can buy with swapped USDT equivalent.&lt;br&gt;
&lt;strong&gt;Next&lt;/strong&gt;, it updates investor and investment status.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function _updateInvestorRecords(
    address investor_,
    uint256 tokenAmount_,
    IERC20 coin_,
    uint256 coinAmount_
) private {
    if (investorTokenBalance[investor_] == 0) {
        investors.push(investor_);
        if (fundsRaised &amp;lt; softcap &amp;amp;&amp;amp; !earlyInvestorsMapping[investor_]) {
            earlyInvestorsMapping[investor_] = true;
            earlyInvestors.push(investor_);
        }
    }

    investorTokenBalance[investor_] += tokenAmount_;
    investments[investor_][address(coin_)] += coinAmount_;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Next&lt;/strong&gt;, it updates presale status.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function _updatePresaleStats(
    uint256 tokenAmount_,
    uint256 coinAmount_,
    uint8 coinDecimals_
) private {
    totalTokensSold += tokenAmount_;
    fundsRaised += coinAmount_ / (10 ** (coinDecimals_ - 6));
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Last&lt;/strong&gt;, it emits &lt;code&gt;TokensBought&lt;/code&gt; event.&lt;/p&gt;

&lt;h5&gt;
  
  
  &lt;em&gt;Buy SPX token with Stable Coins&lt;/em&gt;
&lt;/h5&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function _buyWithCoin(
    IERC20 coin_,
    uint256 tokenAmount_
) internal checkSaleState(tokenAmount_) whenNotPaused nonReentrant {
    uint256 _coinAmount = estimatedCoinAmountForTokenAmount(
        tokenAmount_,
        coin_
    );
    uint8 _coinDecimals = getCoinDecimals(coin_);

    //Check allowances and balances
    require(
        _coinAmount &amp;lt;= coin_.allowance(msg.sender, address(this)),
        "Insufficient allowance"
    );
    require(
        _coinAmount &amp;lt;= coin_.balanceOf(msg.sender),
        "Insufficient balance."
    );

    //Send the coin to the contract
    SafeERC20.safeTransferFrom(
        coin_,
        msg.sender,
        address(this),
        _coinAmount
    );

    //Update the investor status
    _updateInvestorRecords(msg.sender, tokenAmount_, coin_, _coinAmount);

    // Update presale stats
    _updatePresaleStats(tokenAmount_, _coinAmount, _coinDecimals);

    emit TokensBought(
        msg.sender,
        tokenAmount_,
        _coinAmount,
        block.timestamp
    );
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;First,&lt;/strong&gt; this function checks if presale is ongoing, tokenAmount that investor wants to buy is available, and so on.(modifiers)&lt;br&gt;
 &lt;strong&gt;Next,&lt;/strong&gt; it calculates how much coins are needed to buy those amount of tokens and checks if investor has sufficient balance and allowance.&lt;br&gt;
&lt;strong&gt;Next,&lt;/strong&gt; it transfers Stable coins to presale contract.&lt;br&gt;
&lt;strong&gt;Then,&lt;/strong&gt; it updates investor &amp;amp; investment status and presale status, &lt;strong&gt;finally,&lt;/strong&gt; emits &lt;code&gt;TokensBought&lt;/code&gt; event.&lt;/p&gt;

&lt;p&gt;Each functions for buying token with specific stable coins can be written as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function buyWithUSDT(uint256 tokenAmount_) external whenNotPaused {
    _buyWithCoin(USDTInterface, tokenAmount_);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h5&gt;
  
  
  &lt;em&gt;Helper function to calculate SPX token amount with ETH and vice versa&lt;/em&gt;
&lt;/h5&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function estimatedTokenAmountAvailableWithETH(
    uint256 ethAmount_
) public view returns (uint256) {
    // Swap ETH for USDT
    address[] memory path = new address[](2);
    path[0] = router.WETH();
    path[1] = USDT;
    uint256[] memory amounts = router.getAmountsOut(ethAmount_, path);
    require(amounts.length &amp;gt; 1, "Invalid path");
    uint256 _usdtAmount = amounts[1];

    // Calculate token amount
    return
        estimatedTokenAmountAvailableWithCoin(_usdtAmount, USDTInterface);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This function calculates how much tokens user can buy with specific eth amount using Uniswap V2 Router and &lt;code&gt;estimatedTokenAmountAvailableWithCoin&lt;/code&gt; function. &lt;/p&gt;

&lt;h5&gt;
  
  
  &lt;em&gt;Helper function to calculate SPX token amount with Stable Coin and vice versa&lt;/em&gt;
&lt;/h5&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    function estimatedTokenAmountAvailableWithCoin(
        uint256 coinAmount_,
        IERC20 coin_
    ) public view returns (uint256) {
        uint256 tokenAmount = 0;
        uint256 remainingCoinAmount = coinAmount_;
        uint8 _coinDecimals = getCoinDecimals(coin_);

        for (uint8 i = 0; i &amp;lt; thresholds.length; i++) {
            // Get the current token price at the index
            uint256 _priceAtCurrentTier = getCurrentTokenPriceForIndex(i);
            uint256 _currentThreshold = thresholds[i];

            // Determine the number of tokens available at this tier
            uint256 numTokensAvailableAtTier = _currentThreshold &amp;gt;
                totalTokensSold
                ? _currentThreshold - totalTokensSold
                : 0;

            // Calculate the maximum number of tokens that can be bought with the remaining coin amount
            uint256 maxTokensAffordable = (remainingCoinAmount *
                (10 ** (18 - _coinDecimals + 6))) / _priceAtCurrentTier;

            // Determine how many tokens can actually be bought at this tier
            uint256 tokensToBuyAtTier = numTokensAvailableAtTier &amp;lt;
                maxTokensAffordable
                ? numTokensAvailableAtTier
                : maxTokensAffordable;

            // Update amounts
            tokenAmount += tokensToBuyAtTier;
            remainingCoinAmount -=
                (tokensToBuyAtTier * _priceAtCurrentTier) /
                (10 ** (18 - _coinDecimals + 6));

            // If there is no remaining coin amount, break out of the loop
            if (remainingCoinAmount == 0) {
                break;
            }
        }

        return tokenAmount;
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This function ensures:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Accurate token calculations across different price tiers&lt;/li&gt;
&lt;li&gt;Proper decimal handling for different stablecoins&lt;/li&gt;
&lt;li&gt;Maximum token availability limits per tier&lt;/li&gt;
&lt;li&gt;Efficient use of remaining purchase amount&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The implementation supports the presale's tiered pricing structure while maintaining precision in token calculations.&lt;/p&gt;

&lt;h5&gt;
  
  
  &lt;em&gt;Claim function&lt;/em&gt;
&lt;/h5&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function claim(address investor_) external nonReentrant {
    require(
        block.timestamp &amp;gt; claimTime &amp;amp;&amp;amp; claimTime &amp;gt; 0,
        "It's not claiming time yet."
    );

    require(
        fundsRaised &amp;gt;= softcap,
        "Can not claim as softcap not reached. Instead you can be refunded."
    );

    uint256 _tokenAmountforUser = getTokenAmountForInvestor(investor_);
    uint256 _bonusTokenAmount = getBonusTokenAmount();

    if (isEarlyInvestors(investor_))
        _tokenAmountforUser += _bonusTokenAmount;
    require(_tokenAmountforUser &amp;gt; 0, "No tokens claim.");
    investorTokenBalance[investor_] = 0;
    earlyInvestorsMapping[investor_] = false;

    SafeERC20.safeTransfer(token, investor_, _tokenAmountforUser);
    emit TokensClaimed(investor_, _tokenAmountforUser);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This function &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;checks claim time and softcap requirements&lt;/li&gt;
&lt;li&gt;calculates total tokens including bonuses&lt;/li&gt;
&lt;li&gt;resets investor balances and early investor status&lt;/li&gt;
&lt;li&gt;uses &lt;code&gt;SafeERC20&lt;/code&gt; for token transfers&lt;/li&gt;
&lt;li&gt;emits &lt;code&gt;TokensClaimed&lt;/code&gt; event&lt;/li&gt;
&lt;/ul&gt;

&lt;h5&gt;
  
  
  &lt;em&gt;Withdraw function&lt;/em&gt;
&lt;/h5&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function withdraw() external onlyOwner nonReentrant {
    require(
        block.timestamp &amp;gt; endTime,
        "Cannot withdraw because presale is still in progress."
    );

    require(wallet != address(0), "Wallet not set");

    require(
        fundsRaised &amp;gt; softcap,
        "Can not withdraw as softcap not reached."
    );

    uint256 _usdtBalance = USDTInterface.balanceOf(address(this));
    uint256 _usdcBalance = USDCInterface.balanceOf(address(this));
    uint256 _daiBalance = DAIInterface.balanceOf(address(this));

    require(
        _usdtBalance &amp;gt; 0 &amp;amp;&amp;amp; _usdcBalance &amp;gt; 0 &amp;amp;&amp;amp; _daiBalance &amp;gt; 0,
        "No funds to withdraw"
    );

    if (_usdtBalance &amp;gt; 0)
        SafeERC20.safeTransfer(USDTInterface, wallet, _usdtBalance);
    if (_usdcBalance &amp;gt; 0)
        SafeERC20.safeTransfer(USDCInterface, wallet, _usdcBalance);
    if (_daiBalance &amp;gt; 0)
        SafeERC20.safeTransfer(DAIInterface, wallet, _daiBalance);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This function &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;validates if predefined multisig wallet address is set&lt;/li&gt;
&lt;li&gt;ensures presale is already ended&lt;/li&gt;
&lt;li&gt;verifies sufficient funds exist&lt;/li&gt;
&lt;li&gt;uses &lt;code&gt;SafeERC20&lt;/code&gt; for transfers&lt;/li&gt;
&lt;/ul&gt;

&lt;h5&gt;
  
  
  &lt;em&gt;Refund function&lt;/em&gt;
&lt;/h5&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function refund() external onlyOwner nonReentrant {
    require(
        block.timestamp &amp;gt; endTime,
        "Cannot refund because presale is still in progress."
    );
    require(fundsRaised &amp;lt; softcap, "Softcap reached, refund not available");

    // refund all funds to investors
    for (uint256 i = 0; i &amp;lt; investors.length; i++) {
        address investor = investors[i];

        //Refund USDT
        uint256 _usdtAmount = investments[investor][address(USDTInterface)];
        if (_usdtAmount &amp;gt; 0) {
            investments[investor][address(USDTInterface)] = 0;
            SafeERC20.safeTransfer(USDTInterface, investor, _usdtAmount);
            emit FundsRefunded(investor, _usdtAmount, block.timestamp);
        }

        //Refund USDC
        uint256 _usdcAmount = investments[investor][address(USDCInterface)];
        if (_usdcAmount &amp;gt; 0) {
            investments[investor][address(USDCInterface)] = 0;
            SafeERC20.safeTransfer(USDCInterface, investor, _usdcAmount);
            emit FundsRefunded(investor, _usdcAmount, block.timestamp);
        }

        //Refund DAI
        uint256 _daiAmount = investments[investor][address(DAIInterface)];
        if (_daiAmount &amp;gt; 0) {
            investments[investor][address(DAIInterface)] = 0;
            SafeERC20.safeTransfer(DAIInterface, investor, _daiAmount);
            emit FundsRefunded(investor, _daiAmount, block.timestamp);
        }
    }

    fundsRaised = 0;
    delete investors;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This function &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;loops through all investors&lt;/li&gt;
&lt;li&gt;checks and refunds each stable coin separately&lt;/li&gt;
&lt;li&gt;Resets investment records to zero&lt;/li&gt;
&lt;li&gt;Emits &lt;code&gt;FundsRefunded&lt;/code&gt; events&lt;/li&gt;
&lt;li&gt;Clears global state(&lt;code&gt;fundsRaised&lt;/code&gt; and &lt;code&gt;investors&lt;/code&gt; array)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;This SPX token presale smart contract demonstrates a robust and versatile implementation that effectively handles multiple payment methods including ETH, USDT, USDC, and DAI.&lt;br&gt;
This implementation serves as an excellent template for future presale contracts, offering a balance of security, functionality, and user accessibility. &lt;br&gt;
It's architecture ensures fair distribution while protecting both investor and project owner interests through its well-structured validation and distribution mechanisms. &lt;/p&gt;

</description>
      <category>ethereum</category>
      <category>smartcontract</category>
      <category>presale</category>
      <category>blockchain</category>
    </item>
  </channel>
</rss>
