├── pvkey.txt ├── address.txt ├── addressFaucet.txt ├── addressERC20.txt ├── addressNFT.txt ├── contractERC20.txt ├── requirements.txt ├── proxies.txt ├── README-VN.md ├── README.md ├── scripts ├── buymeme.py ├── sellmeme.py ├── mintsusdt.py ├── sopixel.py ├── mintping.py ├── mintpong.py ├── deploytoken.py └── sendtx.py └── main.py /pvkey.txt: -------------------------------------------------------------------------------- 1 | PRIVATEKEY 1 2 | PRIVATEKEY 2 3 | PRIVATEKEY 3 4 | ... 5 | -------------------------------------------------------------------------------- /address.txt: -------------------------------------------------------------------------------- 1 | ADDRESS SENDTX 1 2 | ADDRESS SENDTX 2 3 | ADDRESS SENDTX 3 4 | ... 5 | -------------------------------------------------------------------------------- /addressFaucet.txt: -------------------------------------------------------------------------------- 1 | ADDRESS FAUCET 1 2 | ADDRESS FAUCET 2 3 | ADDRESS FAUCET 3 4 | ... -------------------------------------------------------------------------------- /addressERC20.txt: -------------------------------------------------------------------------------- 1 | ADDRESS SEND-ERC20 1 2 | ADDRESS SEND-ERC20 2 3 | ADDRESS SEND-ERC20 3 4 | ... -------------------------------------------------------------------------------- /addressNFT.txt: -------------------------------------------------------------------------------- 1 | ADDRESS CONTRACT NFT 1 2 | ADDRESS CONTRACT NFT 2 3 | ADDRESS CONTRACT NFT 3 4 | ... 5 | -------------------------------------------------------------------------------- /contractERC20.txt: -------------------------------------------------------------------------------- 1 | ADDRESS CONTRACT-ERC20 1 2 | ADDRESS CONTRACT-ERC20 2 3 | ADDRESS CONTRACT-ERC20 3 4 | ... -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | inquirer 2 | web3 3 | colorama 4 | py-solc-x 5 | eth-account 6 | aiohttp 7 | loguru 8 | aiohttp_socks 9 | -------------------------------------------------------------------------------- /proxies.txt: -------------------------------------------------------------------------------- 1 | PROXY 1 [socks5://user:pass@host:port ,...] 2 | PROXY 2 [socks5://user:pass@host:port ,...] 3 | PROXY 3 [socks5://user:pass@host:port ,...] 4 | ... 5 | -------------------------------------------------------------------------------- /README-VN.md: -------------------------------------------------------------------------------- 1 | # Script Tự Động Somnia Testnet 2 | 3 | Kho lưu trữ này chứa một bộ sưu tập các script Python được thiết kế để tự động hóa nhiều tác vụ trên Somnia Testnet, bao gồm nhận token từ faucet, mint token, hoán đổi token, triển khai hợp đồng, gửi giao dịch và giao dịch memecoin. Các script này được tích hợp với file `main.py` trung tâm để thực thi dễ dàng, hỗ trợ nhiều khóa riêng tư và giao diện CLI thân thiện với người dùng. 4 | 5 | ## Tính Năng Tổng Quan 6 | 7 | ### Tính Năng Chung 8 | 9 | - **Hỗ Trợ Nhiều Tài Khoản**: Đọc khóa riêng tư từ `pvkey.txt` để thực hiện hành động trên nhiều tài khoản. 10 | - **CLI Màu Sắc**: Sử dụng `colorama` để hiển thị đầu ra hấp dẫn với văn bản và viền màu. 11 | - **Thực Thi Bất Đồng Bộ**: Được xây dựng với `asyncio` để tương tác blockchain hiệu quả. 12 | - **Xử Lý Lỗi**: Bắt lỗi toàn diện cho các giao dịch blockchain và vấn đề RPC. 13 | - **Hỗ Trợ Song Ngữ**: Hỗ trợ đầu ra bằng cả tiếng Việt và tiếng Anh dựa trên lựa chọn của người dùng. 14 | 15 | ### Các Script Bao Gồm 16 | 17 | #### 1. `faucetstt.py` - Nhận Token $STT từ Faucet 18 | - **Mô Tả**: Nhận $STT từ faucet của Somnia Testnet thông qua API. 19 | - **Tính Năng**: 20 | - Đọc địa chỉ từ `addressFaucet.txt`. 21 | - Hỗ trợ sử dụng proxy từ `proxies.txt` (tùy chọn). 22 | - Hiển thị IP công khai qua proxy và phản hồi API. 23 | - Độ trễ ngẫu nhiên (5-15 giây) giữa các yêu cầu. 24 | - **Cách Dùng**: Chọn từ menu `main.py`; yêu cầu `addressFaucet.txt`. 25 | 26 | #### 2. `sendtx.py` - Gửi Giao Dịch 27 | - **Mô Tả**: Gửi giao dịch trên Somnia Testnet, đến địa chỉ ngẫu nhiên hoặc từ `address.txt`. 28 | - **Tính Năng**: 29 | - Số giao dịch và lượng STT do người dùng cấu hình (mặc định: 0.000001 STT). 30 | - Độ trễ ngẫu nhiên (1-3 giây) giữa các giao dịch. 31 | - Nhật ký giao dịch chi tiết với liên kết explorer. 32 | - **Cách Dùng**: Chọn từ menu `main.py`, nhập số giao dịch và lượng STT. 33 | 34 | #### 3. `deploytoken.py` - Triển Khai Hợp Đồng Token ERC-20 35 | - **Mô Tả**: Triển khai hợp đồng thông minh ERC-20 tùy chỉnh trên Somnia Testnet. 36 | - **Tính Năng**: 37 | - Người dùng nhập tên token, ký hiệu, số thập phân (mặc định: 18) và tổng cung. 38 | - Biên dịch và triển khai bằng `solcx` (Solidity 0.8.22). 39 | - Lưu địa chỉ hợp đồng vào `contractERC20.txt`. 40 | - Độ trễ ngẫu nhiên (10-30 giây) giữa các lần triển khai. 41 | - **Cách Dùng**: Chọn từ menu `main.py`, cung cấp chi tiết token. 42 | 43 | #### 4. `sendtoken.py` - Gửi Token ERC-20 44 | - **Mô Tả**: Chuyển token ERC-20 đến địa chỉ ngẫu nhiên hoặc từ `addressERC20.txt`. 45 | - **Tính Năng**: 46 | - Người dùng nhập địa chỉ hợp đồng và số lượng. 47 | - Độ trễ ngẫu nhiên (10-30 giây) giữa các lần chuyển. 48 | - Nhật ký giao dịch chi tiết với liên kết explorer. 49 | - **Cách Dùng**: Chọn từ menu `main.py`, nhập địa chỉ hợp đồng và số lượng. 50 | 51 | #### 5. `deploynft.py` - Triển Khai Hợp Đồng NFT 52 | - **Mô Tả**: Triển khai hợp đồng thông minh NFT trên Somnia Testnet. 53 | - **Tính Năng**: 54 | - Tự động hóa triển khai hợp đồng NFT cho nhiều ví. 55 | - Hiển thị địa chỉ hợp đồng đã triển khai. 56 | - **Cách Dùng**: Chọn từ menu `main.py`. 57 | 58 | #### 6. `mintpong.py` - Mint $PONG 59 | - **Mô Tả**: Mint 1000 token $PONG trên Somnia Testnet. 60 | - **Tính Năng**: 61 | - Độ trễ ngẫu nhiên (100-300 giây) giữa các lần mint. 62 | - Kiểm tra số dư STT trước khi mint (tối thiểu 0.001 STT). 63 | - Nhật ký giao dịch chi tiết với liên kết explorer. 64 | - **Cách Dùng**: Chọn từ menu `main.py`. 65 | 66 | #### 7. `mintping.py` - Mint $PING 67 | - **Mô Tả**: Mint token $PING trên Somnia Testnet. 68 | - **Tính Năng**: 69 | - Chức năng tương tự `mintpong.py` với thông tin cụ thể của $PING. 70 | - Độ trễ ngẫu nhiên (100-300 giây) giữa các lần mint. 71 | - **Cách Dùng**: Chọn từ menu `main.py`. 72 | 73 | #### 8. `swappong.py` - Hoán Đổi $PONG sang $PING 74 | - **Mô Tả**: Hoán đổi $PONG sang $PING trên Somnia Testnet bằng swap router. 75 | - **Tính Năng**: 76 | - Người dùng nhập số lượng hoán đổi và số lần hoán đổi. 77 | - Phê duyệt và hoán đổi token với độ trễ ngẫu nhiên (10-30 giây giữa các lần swap, 100-300 giây giữa các ví). 78 | - Nhật ký giao dịch chi tiết với liên kết explorer. 79 | - **Cách Dùng**: Chọn từ menu `main.py`, nhập số lượng và số lần hoán đổi. 80 | 81 | #### 9. `swapping.py` - Hoán Đổi $PING sang $PONG 82 | - **Mô Tả**: Hoán đổi $PING sang $PONG trên Somnia Testnet bằng swap router. 83 | - **Tính Năng**: 84 | - Tương tự `swappong.py` nhưng theo hướng ngược lại. 85 | - Số lượng và chu kỳ hoán đổi do người dùng cấu hình. 86 | - **Cách Dùng**: Chọn từ menu `main.py`, nhập số lượng và số lần hoán đổi. 87 | 88 | #### 10. `conftnft.py` - Mint NFT Cộng Đồng (CoNFT) 89 | - **Mô Tả**: Mint NFT Community Member of Somnia (CMS - CoNFT) với chi phí 0.1 STT. 90 | - **Tính Năng**: 91 | - Kiểm tra xem ví đã mint chưa (qua `balanceOf`). 92 | - Xác minh số dư STT (tối thiểu 0.1 STT). 93 | - Độ trễ ngẫu nhiên (10-30 giây) giữa các lần mint. 94 | - Nhật ký giao dịch chi tiết với liên kết explorer. 95 | - **Cách Dùng**: Chọn từ menu `main.py`. 96 | 97 | #### 11. `mintsusdt.py` - Mint 1000 sUSDT 98 | - **Mô Tả**: Mint 1000 token sUSDT trên Somnia Testnet. 99 | - **Tính Năng**: 100 | - Kiểm tra xem ví đã mint chưa để tránh lặp lại. 101 | - Độ trễ ngẫu nhiên (10-30 giây) giữa các lần mint. 102 | - Nhật ký giao dịch chi tiết với liên kết explorer. 103 | - **Cách Dùng**: Chọn từ menu `main.py`. 104 | 105 | #### 12. `buymeme.py` - Mua Memecoin 106 | - **Mô Tả**: Mua memecoin (SOMI, SMSM, SMI) bằng sUSDT trên Somnia Testnet. 107 | - **Tính Năng**: 108 | - Người dùng chọn token (SOMI, SMSM, hoặc SMI) và số lượng sUSDT. 109 | - Phê duyệt và hoán đổi với dung sai trượt giá 5%. 110 | - Hiển thị số dư, giá và vốn hóa thị trường. 111 | - Độ trễ ngẫu nhiên (10-30 giây) giữa các lần mua. 112 | - **Cách Dùng**: Chọn từ menu `main.py`, chọn token và số lượng. 113 | 114 | #### 13. `sellmeme.py` - Bán Memecoin 115 | - **Mô Tả**: Bán memecoin (SOMI, SMSM, SMI) lấy sUSDT trên Somnia Testnet. 116 | - **Tính Năng**: 117 | - Người dùng chọn token và số lượng để bán. 118 | - Phê duyệt và thực hiện hoán đổi với độ trễ ngẫu nhiên (10-30 giây). 119 | - Hiển thị số dư, giá và vốn hóa thị trường. 120 | - **Cách Dùng**: Chọn từ menu `main.py`, chọn token và số lượng. 121 | 122 | ## Yêu Cầu 123 | - **Python 3.8+** 124 | - **Phụ Thuộc**: Cài đặt qua `pip install -r requirements.txt` (bao gồm `web3.py`, `colorama`, `aiohttp`, `aiohttp_socks`, `solcx`, và `eth-account`). 125 | - **pvkey.txt**: Thêm khóa riêng tư (mỗi khóa một dòng) để tự động hóa ví. 126 | - **address.txt** / **addressERC20.txt** / **addressFaucet.txt** / **proxies.txt**: File tùy chọn để chỉ định địa chỉ hoặc proxy. 127 | 128 | ## Cài Đặt 129 | 130 | 1. **Clone this repository:** 131 | - Open cmd or Shell, then run the command: 132 | ```sh 133 | git clone https://github.com/thog9/Somnia-testnet.git 134 | ``` 135 | ```sh 136 | cd Somnia-testnet 137 | ``` 138 | 2. **Install Dependencies:** 139 | - Open cmd or Shell, then run the command: 140 | ```sh 141 | pip install -r requirements.txt 142 | ``` 143 | 3. **Prepare Input Files:** 144 | - Open the `pvkey.txt`: Add your private keys (one per line) in the root directory. 145 | ```sh 146 | nano pvkey.txt 147 | ``` 148 | - Open the `address.txt`(optional): Add recipient addresses (one per line) for `sendtx.py`, `faucetstt.py`, `deploytoken.py`, `sendtoken.py`, `proxies.txt`. 149 | ```sh 150 | nano address.txt 151 | ``` 152 | ```sh 153 | nano addressERC20.txt 154 | ``` 155 | ```sh 156 | nano addressFaucet.txt 157 | ``` 158 | ```sh 159 | nano proxies.txt 160 | ``` 161 | ```sh 162 | nano contractERC20.txt 163 | ``` 164 | 4. **Run:** 165 | - Open cmd or Shell, then run command: 166 | ```sh 167 | python main.py 168 | ``` 169 | - Chọn ngôn ngữ (Tiếng Việt/Tiếng Anh) và chọn script từ menu. 170 | 171 | ## Liên Hệ 172 | 173 | - **Telegram**: [thog099](https://t.me/thog099) 174 | - **Kênh Telegram**: [thogairdrops](https://t.me/thogairdrops) 175 | - **Replit**: Thog 176 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Somnia Testnet Automation Scripts 2 | 3 | This repository contains a collection of Python scripts designed to automate various tasks on the Somnia Testnet, including faucet claiming, token minting, swapping, contract deployment, transaction sending, and memecoin trading. These scripts are integrated with a central `main.py` file for streamlined execution, supporting multiple private keys and a user-friendly CLI interface. 4 | 5 | ## Features Overview 6 | 7 | ### General Features 8 | 9 | - **Multi-Account Support**: Reads private keys from `pvkey.txt` to perform actions across multiple accounts. 10 | - **Colorful CLI**: Uses `colorama` for visually appealing output with colored text and borders. 11 | - **Asynchronous Execution**: Built with `asyncio` for efficient blockchain interactions. 12 | - **Error Handling**: Comprehensive error catching for blockchain transactions and RPC issues. 13 | - **Bilingual Support**: Supports both Vietnamese and English output based on user selection. 14 | 15 | ### Included Scripts 16 | 17 | #### 1. `faucetstt.py` - Faucet Token $STT 18 | - **Description**: Claims $STT tokens from the Somnia Testnet faucet via API. 19 | - **Features**: 20 | - Reads addresses from `addressFaucet.txt`. 21 | - Supports proxy usage from `proxies.txt` (optional). 22 | - Displays public IP via proxy and API response. 23 | - Random delays (5-15 seconds) between requests. 24 | - **Usage**: Select from `main.py` menu; requires `addressFaucet.txt`. 25 | 26 | #### 2. `sendtx.py` - Send Transactions 27 | - **Description**: Sends transactions on Somnia Testnet, either to random addresses or from `address.txt`. 28 | - **Features**: 29 | - User-configurable transaction count and STT amount (default: 0.000001 STT). 30 | - Random delays (1-3 seconds) between transactions. 31 | - Detailed transaction logs with explorer links. 32 | - **Usage**: Select from `main.py` menu, input transaction count and amount. 33 | 34 | #### 3. `deploytoken.py` - Deploy ERC-20 Token Contract 35 | - **Description**: Deploys a custom ERC-20 token smart contract on Somnia Testnet. 36 | - **Features**: 37 | - User inputs for token name, symbol, decimals (default: 18), and total supply. 38 | - Compiles and deploys using `solcx` (Solidity 0.8.22). 39 | - Saves deployed contract addresses to `contractERC20.txt`. 40 | - Random delays (10-30 seconds) between deployments. 41 | - **Usage**: Select from `main.py` menu, provide token details. 42 | 43 | #### 4. `sendtoken.py` - Send ERC-20 Tokens 44 | - **Description**: Transfers ERC-20 tokens to random addresses or from `addressERC20.txt`. 45 | - **Features**: 46 | - User inputs for contract address and amount. 47 | - Random delays (10-30 seconds) between transfers. 48 | - Detailed transaction logs with explorer links. 49 | - **Usage**: Select from `main.py` menu, input contract address and amount. 50 | 51 | #### 5. `deploynft.py` - Deploy NFT Contract 52 | - **Description**: Deploys an NFT smart contract on Somnia Testnet. 53 | - **Features**: 54 | - Automates NFT contract deployment for multiple wallets. 55 | - Displays deployed contract address. 56 | - **Usage**: Select from `main.py` menu. 57 | 58 | #### 6. `mintpong.py` - Mint $PONG 59 | - **Description**: Mints 1000 $PONG tokens on Somnia Testnet. 60 | - **Features**: 61 | - Random delays (100-300 seconds) between mints. 62 | - Checks STT balance before minting (minimum 0.001 STT). 63 | - Detailed transaction logs with explorer links. 64 | - **Usage**: Select from `main.py` menu. 65 | 66 | #### 7. `mintping.py` - Mint $PING 67 | - **Description**: Mints $PING tokens on Somnia Testnet. 68 | - **Features**: 69 | - Similar functionality to `mintpong.py` with $PING token specifics. 70 | - Random delays (100-300 seconds) between mints. 71 | - **Usage**: Select from `main.py` menu. 72 | 73 | #### 8. `swappong.py` - Swap $PONG to $PING 74 | - **Description**: Swaps $PONG to $PING on Somnia Testnet using a swap router. 75 | - **Features**: 76 | - User inputs for swap amount and number of swaps. 77 | - Approves and swaps tokens with random delays (10-30 seconds between swaps, 100-300 seconds between wallets). 78 | - Detailed transaction logs with explorer links. 79 | - **Usage**: Select from `main.py` menu, input swap amount and times. 80 | 81 | #### 9. `swapping.py` - Swap $PING to $PONG 82 | - **Description**: Swaps $PING to $PONG on Somnia Testnet using a swap router. 83 | - **Features**: 84 | - Similar to `swappong.py` but in reverse direction. 85 | - User-configurable swap amount and cycles. 86 | - **Usage**: Select from `main.py` menu, input swap amount and times. 87 | 88 | #### 10. `conftnft.py` - Mint Community NFT (CoNFT) 89 | - **Description**: Mints the Community Member of Somnia (CMS - CoNFT) NFT for 0.1 STT. 90 | - **Features**: 91 | - Checks if wallet has already minted (via `balanceOf`). 92 | - Verifies STT balance (minimum 0.1 STT). 93 | - Random delays (10-30 seconds) between mints. 94 | - Detailed transaction logs with explorer links. 95 | - **Usage**: Select from `main.py` menu. 96 | 97 | #### 11. `mintsusdt.py` - Mint 1000 sUSDT 98 | - **Description**: Mints 1000 sUSDT tokens on Somnia Testnet. 99 | - **Features**: 100 | - Checks if wallet has already minted to avoid duplicates. 101 | - Random delays (10-30 seconds) between mints. 102 | - Detailed transaction logs with explorer links. 103 | - **Usage**: Select from `main.py` menu. 104 | 105 | #### 12. `buymeme.py` - Buy Memecoins 106 | - **Description**: Buys memecoins (SOMI, SMSM, SMI) with sUSDT on Somnia Testnet. 107 | - **Features**: 108 | - User selects token (SOMI, SMSM, or SMI) and sUSDT amount. 109 | - Approves and swaps with 5% slippage tolerance. 110 | - Displays balance, price, and market cap. 111 | - Random delays (10-30 seconds) between buys. 112 | - **Usage**: Select from `main.py` menu, choose token and amount. 113 | 114 | #### 13. `sellmeme.py` - Sell Memecoins 115 | - **Description**: Sells memecoins (SOMI, SMSM, SMI) for sUSDT on Somnia Testnet. 116 | - **Features**: 117 | - User selects token and amount to sell. 118 | - Approves and executes swaps with random delays (10-30 seconds). 119 | - Displays balance, price, and market cap. 120 | - **Usage**: Select from `main.py` menu, choose token and amount. 121 | 122 | ## Prerequisites 123 | - **Python 3.8+** 124 | - **Dependencies**: Install via `pip install -r requirements.txt` (ensure `web3.py`, `colorama`, `aiohttp`, `aiohttp_socks`, `solcx`, and `eth-account` are included). 125 | - **pvkey.txt**: Add private keys (one per line) for wallet automation. 126 | - **address.txt** / **addressERC20.txt** / **addressFaucet.txt** / **proxies.txt**: Optional files for specifying addresses or proxies. 127 | 128 | ## Installation 129 | 130 | 1. **Clone this repository:** 131 | - Open cmd or Shell, then run the command: 132 | ```sh 133 | git clone https://github.com/thog9/Somnia-testnet.git 134 | ``` 135 | ```sh 136 | cd Somnia-testnet 137 | ``` 138 | 2. **Install Dependencies:** 139 | - Open cmd or Shell, then run the command: 140 | ```sh 141 | pip install -r requirements.txt 142 | ``` 143 | 3. **Prepare Input Files:** 144 | - Open the `pvkey.txt`: Add your private keys (one per line) in the root directory. 145 | ```sh 146 | nano pvkey.txt 147 | ``` 148 | - Open the `address.txt`(optional): Add recipient addresses (one per line) for `sendtx.py`, `faucetstt.py`, `deploytoken.py`, `sendtoken.py`, `proxies.txt`. 149 | ```sh 150 | nano address.txt 151 | ``` 152 | ```sh 153 | nano addressERC20.txt 154 | ``` 155 | ```sh 156 | nano addressFaucet.txt 157 | ``` 158 | ```sh 159 | nano proxies.txt 160 | ``` 161 | ```sh 162 | nano contractERC20.txt 163 | ``` 164 | 4. **Run:** 165 | - Open cmd or Shell, then run command: 166 | ```sh 167 | python main.py 168 | ``` 169 | - Choose a language (Vietnamese/English). 170 | 171 | ## Contact 172 | 173 | - **Telegram**: [thog099](https://t.me/thog099) 174 | - **Channel**: [thogairdrops](https://t.me/thogairdrops) 175 | - **Replit**: Thog 176 | -------------------------------------------------------------------------------- /scripts/buymeme.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import asyncio 4 | import random 5 | from web3 import Web3 6 | from eth_account import Account 7 | from colorama import init, Fore, Style 8 | 9 | # Khởi tạo colorama 10 | init(autoreset=True) 11 | 12 | # Độ rộng viền 13 | BORDER_WIDTH = 80 14 | 15 | # Constants 16 | NETWORK_URL = "https://dream-rpc.somnia.network" 17 | CHAIN_ID = 50312 18 | EXPLORER_URL = "https://shannon-explorer.somnia.network/tx/0x" 19 | ROUTER_ADDRESS = "0x6aac14f090a35eea150705f72d90e4cdc4a49b2c" 20 | SPENDER_ADDRESS = "0x6aac14f090a35eea150705f72d90e4cdc4a49b2c" 21 | SUSDT_ADDRESS = "0x65296738D4E5edB1515e40287B6FDf8320E6eE04" 22 | TOKENS = { 23 | "SOMI": {"address": "0x7a7045415f3682C3349E4b68d2940204b81fFF33", "price": 0.99960}, 24 | "SMSM": {"address": "0x6756B4542d545270CacF1F15C3b7DefE589Ba1aa", "price": 0.99959}, 25 | "SMI": {"address": "0xC9005DD5C562bDdEF1Cf3C90Ad5B1Bf54fB8aa9d", "price": 0.99959}, 26 | "sUSDT": {"address": "0x65296738D4E5edB1515e40287B6FDf8320E6eE04", "price": 1.0} 27 | } 28 | 29 | # ABI cho token ERC-20 và Router 30 | TOKEN_ABI = [ 31 | {"constant": False, "inputs": [{"name": "spender", "type": "address"}, {"name": "amount", "type": "uint256"}], "name": "approve", "outputs": [{"name": "", "type": "bool"}], "type": "function"}, 32 | {"constant": True, "inputs": [{"name": "_owner", "type": "address"}], "name": "balanceOf", "outputs": [{"name": "balance", "type": "uint256"}], "type": "function"}, 33 | {"constant": True, "inputs": [], "name": "decimals", "outputs": [{"name": "", "type": "uint8"}], "type": "function"}, 34 | {"constant": True, "inputs": [], "name": "totalSupply", "outputs": [{"name": "", "type": "uint256"}], "type": "function"} 35 | ] 36 | 37 | SWAP_ROUTER_ABI = [ 38 | { 39 | "inputs": [ 40 | { 41 | "components": [ 42 | {"name": "tokenIn", "type": "address"}, 43 | {"name": "tokenOut", "type": "address"}, 44 | {"name": "fee", "type": "uint24"}, 45 | {"name": "recipient", "type": "address"}, 46 | {"name": "amountIn", "type": "uint256"}, 47 | {"name": "amountOutMinimum", "type": "uint256"}, 48 | {"name": "sqrtPriceLimitX96", "type": "uint160"} 49 | ], 50 | "name": "params", 51 | "type": "tuple" 52 | } 53 | ], 54 | "name": "exactInputSingle", 55 | "outputs": [{"name": "amountOut", "type": "uint256"}], 56 | "stateMutability": "nonpayable", 57 | "type": "function" 58 | } 59 | ] 60 | 61 | # Từ vựng song ngữ 62 | LANG = { 63 | 'vi': { 64 | 'title': 'MUA TOKEN MEME - SOMNIA TESTNET', 65 | 'info': 'Thông tin', 66 | 'found': 'Tìm thấy', 67 | 'wallets': 'ví', 68 | 'processing_wallet': 'XỬ LÝ VÍ', 69 | 'select_token': 'Chọn token để mua (1: SOMI │ 2: SMSM │ 3: SMI): ', 70 | 'enter_amount': 'Nhập số lượng sUSDT để mua token: ', 71 | 'approve_success': 'Đã approve {amount} sUSDT thành công!', 72 | 'buy_success': 'Đã mua {token} bằng {amount} sUSDT thành công!', 73 | 'approve_failure': 'Approve thất bại', 74 | 'buy_failure': 'Mua thất bại', 75 | 'balance': 'Số dư : {balance} {token}', 76 | 'price': 'Giá : {price} sUSDT/{token}', 77 | 'market_cap': 'Vốn hóa : {market_cap} sUSDT', 78 | 'insufficient_balance': 'Số dư sUSDT không đủ: {balance} < {amount}', 79 | 'pausing': 'Tạm nghỉ', 80 | 'completed': 'HOÀN THÀNH: {successful}/{total} GIAO DỊCH THÀNH CÔNG', 81 | 'error': 'Lỗi', 82 | 'connect_success': 'Thành công: Đã kết nối mạng Somnia Testnet', 83 | 'invalid_input': 'Lựa chọn không hợp lệ', 84 | 'pvkey_not_found': 'File pvkey.txt không tồn tại', 85 | 'pvkey_empty': 'Không tìm thấy private key hợp lệ' 86 | }, 87 | 'en': { 88 | 'title': 'BUY MEME TOKEN - SOMNIA TESTNET', 89 | 'info': 'Info', 90 | 'found': 'Found', 91 | 'wallets': 'wallets', 92 | 'processing_wallet': 'PROCESSING WALLET', 93 | 'select_token': 'Select token to buy (1: SOMI │ 2: SMSM │ 3: SMI): ', 94 | 'enter_amount': 'Enter sUSDT amount to buy token: ', 95 | 'approve_success': 'Successfully approved {amount} sUSDT!', 96 | 'buy_success': 'Successfully bought {token} with {amount} sUSDT!', 97 | 'approve_failure': 'Approve failed', 98 | 'buy_failure': 'Buy failed', 99 | 'balance': 'Balance : {balance} {token}', 100 | 'price': 'Price : {price} sUSDT/{token}', 101 | 'market_cap': 'Market Cap : {market_cap} sUSDT', 102 | 'insufficient_balance': 'Insufficient sUSDT balance: {balance} < {amount}', 103 | 'pausing': 'Pausing', 104 | 'completed': 'COMPLETED: {successful}/{total} TRANSACTIONS SUCCESSFUL', 105 | 'error': 'Error', 106 | 'connect_success': 'Success: Connected to Somnia Testnet', 107 | 'invalid_input': 'Invalid selection', 108 | 'pvkey_not_found': 'pvkey.txt file not found', 109 | 'pvkey_empty': 'No valid private keys found' 110 | } 111 | } 112 | 113 | # Hàm hiển thị viền 114 | def print_border(text: str, color=Fore.CYAN, width=BORDER_WIDTH): 115 | text = f" {text} " 116 | padded_text = text.center(width - 2, "─") 117 | print(f"{color}┌{'─' * (width - 2)}┐{Style.RESET_ALL}") 118 | print(f"{color}│{padded_text}│{Style.RESET_ALL}") 119 | print(f"{color}└{'─' * (width - 2)}┘{Style.RESET_ALL}") 120 | print() 121 | 122 | # Hàm hiển thị phân cách 123 | def print_separator(color=Fore.MAGENTA): 124 | print(f"{color}{'═' * BORDER_WIDTH}{Style.RESET_ALL}") 125 | print() 126 | 127 | # Hàm kiểm tra private key hợp lệ 128 | def is_valid_private_key(key: str) -> bool: 129 | key = key.strip() 130 | if not key.startswith('0x'): 131 | key = '0x' + key 132 | try: 133 | bytes.fromhex(key.replace('0x', '')) 134 | return len(key) == 66 135 | except ValueError: 136 | return False 137 | 138 | # Hàm đọc private keys từ pvkey.txt 139 | def load_private_keys(file_path: str = "pvkey.txt", language: str = 'en') -> list: 140 | try: 141 | if not os.path.exists(file_path): 142 | print(f"{Fore.RED} ✖ {LANG[language]['pvkey_not_found']}{Style.RESET_ALL}") 143 | with open(file_path, 'w') as f: 144 | f.write("# Thêm private keys vào đây, mỗi key trên một dòng\n# Ví dụ: 0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef\n") 145 | sys.exit(1) 146 | 147 | valid_keys = [] 148 | with open(file_path, 'r') as f: 149 | for i, line in enumerate(f, 1): 150 | key = line.strip() 151 | if key and not key.startswith('#'): 152 | if is_valid_private_key(key): 153 | if not key.startswith('0x'): 154 | key = '0x' + key 155 | valid_keys.append((i, key)) 156 | 157 | if not valid_keys: 158 | print(f"{Fore.RED} ✖ {LANG[language]['pvkey_empty']}{Style.RESET_ALL}") 159 | sys.exit(1) 160 | 161 | return valid_keys 162 | except Exception as e: 163 | print(f"{Fore.RED} ✖ {LANG[language]['error']}: {str(e)}{Style.RESET_ALL}") 164 | sys.exit(1) 165 | 166 | # Hàm kết nối Web3 167 | def connect_web3(language: str = 'en'): 168 | try: 169 | w3 = Web3(Web3.HTTPProvider(NETWORK_URL)) 170 | if not w3.is_connected(): 171 | print(f"{Fore.RED} ✖ {LANG[language]['connect_error']}{Style.RESET_ALL}") 172 | sys.exit(1) 173 | print(f"{Fore.GREEN} ✔ {LANG[language]['connect_success']} │ Chain ID: {w3.eth.chain_id}{Style.RESET_ALL}") 174 | return w3 175 | except Exception as e: 176 | print(f"{Fore.RED} ✖ {LANG[language]['error']}: {str(e)}{Style.RESET_ALL}") 177 | sys.exit(1) 178 | 179 | # Hàm lấy thông tin token 180 | def get_token_info(w3: Web3, token_symbol: str, wallet_address: str, language: str = 'en'): 181 | contract = w3.eth.contract(address=Web3.to_checksum_address(TOKENS[token_symbol]["address"]), abi=TOKEN_ABI) 182 | try: 183 | balance = contract.functions.balanceOf(wallet_address).call() / 10**contract.functions.decimals().call() 184 | total_supply = contract.functions.totalSupply().call() / 10**contract.functions.decimals().call() 185 | price = TOKENS[token_symbol]["price"] 186 | market_cap = price * total_supply 187 | print(f"{Fore.YELLOW} {LANG[language]['balance'].format(balance=f'{balance:,.2f}', token=token_symbol)}{Style.RESET_ALL}") 188 | print(f"{Fore.YELLOW} {LANG[language]['price'].format(price=f'{price:,.5f}', token=token_symbol)}{Style.RESET_ALL}") 189 | if token_symbol != "sUSDT": # Chỉ hiển thị Market Cap cho token không phải sUSDT 190 | total_supply = contract.functions.totalSupply().call() / 10**contract.functions.decimals().call() 191 | market_cap = price * total_supply 192 | print(f"{Fore.YELLOW} {LANG[language]['market_cap'].format(market_cap=f'{market_cap:,.2f}')}{Style.RESET_ALL}") 193 | print() 194 | return balance 195 | except Exception as e: 196 | print(f"{Fore.RED} ✖ {LANG[language]['error']}: {str(e)}{Style.RESET_ALL}") 197 | return 0 198 | 199 | # Hàm chọn token để mua 200 | def select_token(language: str = 'en'): 201 | print_border("CHỌN TOKEN ĐỂ MUA / SELECT TOKEN TO BUY", Fore.YELLOW) 202 | print(f"{Fore.CYAN} 1. Somini (SOMI){Style.RESET_ALL}") 203 | print(f"{Fore.CYAN} 2. Somsom (SMSM){Style.RESET_ALL}") 204 | print(f"{Fore.CYAN} 3. Somi (SMI){Style.RESET_ALL}") 205 | print() 206 | while True: 207 | choice = input(f"{Fore.YELLOW} > {LANG[language]['select_token']}{Style.RESET_ALL}") 208 | if choice == "1": 209 | return "SOMI" 210 | elif choice == "2": 211 | return "SMSM" 212 | elif choice == "3": 213 | return "SMI" 214 | else: 215 | print(f"{Fore.RED} ✖ {LANG[language]['invalid_input']}{Style.RESET_ALL}") 216 | 217 | # Hàm nhập số lượng sUSDT 218 | def get_amount(language: str = 'en'): 219 | while True: 220 | try: 221 | amount = float(input(f"{Fore.YELLOW} > {LANG[language]['enter_amount']}{Style.RESET_ALL}")) 222 | if amount <= 0: 223 | print(f"{Fore.RED} ✖ {LANG[language]['error']}: Amount must be greater than 0{Style.RESET_ALL}") 224 | else: 225 | return amount 226 | except ValueError: 227 | print(f"{Fore.RED} ✖ {LANG[language]['error']}: Invalid number{Style.RESET_ALL}") 228 | 229 | # Hàm approve token 230 | async def approve_token(w3: Web3, private_key: str, token_address: str, spender_address: str, amount: float, wallet_index: int, language: str = 'en'): 231 | account = Account.from_key(private_key) 232 | token_contract = w3.eth.contract(address=Web3.to_checksum_address(token_address), abi=TOKEN_ABI) 233 | decimals = token_contract.functions.decimals().call() 234 | amount_wei = int(amount * (10 ** decimals)) 235 | 236 | tx = token_contract.functions.approve( 237 | Web3.to_checksum_address(spender_address), 238 | amount_wei 239 | ).build_transaction({ 240 | 'from': account.address, 241 | 'nonce': w3.eth.get_transaction_count(account.address), 242 | 'gas': 200000, 243 | 'gasPrice': w3.eth.gas_price 244 | }) 245 | 246 | signed_tx = w3.eth.account.sign_transaction(tx, private_key) 247 | tx_hash = w3.eth.send_raw_transaction(signed_tx.raw_transaction) 248 | receipt = w3.eth.wait_for_transaction_receipt(tx_hash, timeout=120) 249 | 250 | if receipt.status == 1: 251 | print(f"{Fore.GREEN} ✔ {LANG[language]['approve_success'].format(amount=f'{amount:,.2f}')}{Style.RESET_ALL}") 252 | print() 253 | return tx_hash.hex() 254 | else: 255 | print(f"{Fore.RED} ✖ {LANG[language]['approve_failure']}{Style.RESET_ALL}") 256 | return None 257 | 258 | # Hàm mua token 259 | async def buy_token(w3: Web3, private_key: str, token_symbol: str, amount: float, wallet_index: int, language: str = 'en'): 260 | account = Account.from_key(private_key) 261 | token_in = SUSDT_ADDRESS 262 | token_out = TOKENS[token_symbol]["address"] 263 | swap_router = w3.eth.contract(address=Web3.to_checksum_address(ROUTER_ADDRESS), abi=SWAP_ROUTER_ABI) 264 | 265 | susdt_contract = w3.eth.contract(address=Web3.to_checksum_address(token_in), abi=TOKEN_ABI) 266 | decimals = susdt_contract.functions.decimals().call() 267 | amount_in_wei = int(amount * (10 ** decimals)) 268 | amount_out_minimum = int(amount * 0.95 * (10 ** decimals)) # 5% slippage 269 | 270 | tx_data = swap_router.functions.exactInputSingle( 271 | ( 272 | Web3.to_checksum_address(token_in), 273 | Web3.to_checksum_address(token_out), 274 | 500, # Fee 275 | account.address, 276 | amount_in_wei, 277 | amount_out_minimum, 278 | 0 279 | ) 280 | ).build_transaction({ 281 | 'from': account.address, 282 | 'nonce': w3.eth.get_transaction_count(account.address), 283 | 'gas': 300000, 284 | 'gasPrice': w3.eth.gas_price, 285 | 'chainId': CHAIN_ID 286 | }) 287 | 288 | signed_tx = w3.eth.account.sign_transaction(tx_data, private_key) 289 | tx_hash = w3.eth.send_raw_transaction(signed_tx.raw_transaction) 290 | tx_link = f"{EXPLORER_URL}{tx_hash.hex()}" 291 | receipt = w3.eth.wait_for_transaction_receipt(tx_hash, timeout=120) 292 | 293 | if receipt.status == 1: 294 | print(f"{Fore.GREEN} ✔ {LANG[language]['buy_success'].format(token=token_symbol, amount=f'{amount:,.2f}')} │ Tx: {tx_link}{Style.RESET_ALL}") 295 | print() 296 | return True 297 | else: 298 | print(f"{Fore.RED} ✖ {LANG[language]['buy_failure']} │ Tx: {tx_link}{Style.RESET_ALL}") 299 | return False 300 | 301 | # Hàm chính 302 | async def run_buymeme(language: str = 'en'): 303 | print() 304 | print_border(LANG[language]['title'], Fore.CYAN) 305 | private_keys = load_private_keys('pvkey.txt', language) 306 | print(f"{Fore.YELLOW} ℹ {LANG[language]['info']}: {LANG[language]['found']} {len(private_keys)} {LANG[language]['wallets']}{Style.RESET_ALL}") 307 | print() 308 | 309 | if not private_keys: 310 | return 311 | 312 | w3 = connect_web3(language) 313 | print() 314 | token_symbol = select_token(language) 315 | amount = get_amount(language) 316 | print_separator() 317 | 318 | successful_buys = 0 319 | total_wallets = len(private_keys) 320 | 321 | random.shuffle(private_keys) 322 | for i, (profile_num, private_key) in enumerate(private_keys, 1): 323 | print_border(f"{LANG[language]['processing_wallet']} {profile_num} ({i}/{len(private_keys)})", Fore.MAGENTA) 324 | 325 | account = Account.from_key(private_key) 326 | susdt_balance = get_token_info(w3, "sUSDT", account.address, language) 327 | if susdt_balance < amount: 328 | print(f"{Fore.RED} ✖ {LANG[language]['insufficient_balance'].format(balance=f'{susdt_balance:,.2f}', amount=f'{amount:,.2f}')}{Style.RESET_ALL}") 329 | print_separator() 330 | continue 331 | 332 | get_token_info(w3, token_symbol, account.address, language) 333 | approve_tx = await approve_token(w3, private_key, SUSDT_ADDRESS, SPENDER_ADDRESS, amount, i, language) 334 | if not approve_tx: 335 | print_separator() 336 | continue 337 | 338 | if await buy_token(w3, private_key, token_symbol, amount, i, language): 339 | successful_buys += 1 340 | 341 | if i < len(private_keys): 342 | delay = random.uniform(10, 30) 343 | print(f"{Fore.YELLOW} ℹ {LANG[language]['pausing']} {delay:.2f} {'giây' if language == 'vi' else 'seconds'}{Style.RESET_ALL}") 344 | print() 345 | await asyncio.sleep(delay) 346 | print_separator() 347 | 348 | print() 349 | print_border(f"{LANG[language]['completed'].format(successful=successful_buys, total=total_wallets)}", Fore.GREEN) 350 | 351 | if __name__ == "__main__": 352 | asyncio.run(run_buymeme('vi')) 353 | -------------------------------------------------------------------------------- /scripts/sellmeme.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import asyncio 4 | import random 5 | from web3 import Web3 6 | from eth_account import Account 7 | from colorama import init, Fore, Style 8 | 9 | # Khởi tạo colorama 10 | init(autoreset=True) 11 | 12 | # Độ rộng viền 13 | BORDER_WIDTH = 80 14 | 15 | # Constants 16 | NETWORK_URL = "https://dream-rpc.somnia.network" 17 | CHAIN_ID = 50312 18 | EXPLORER_URL = "https://shannon-explorer.somnia.network/tx/0x" 19 | ROUTER_ADDRESS = "0x6aac14f090a35eea150705f72d90e4cdc4a49b2c" 20 | SPENDER_ADDRESS = "0x6aac14f090a35eea150705f72d90e4cdc4a49b2c" 21 | SUSDT_ADDRESS = "0x65296738D4E5edB1515e40287B6FDf8320E6eE04" 22 | TOKENS = { 23 | "SOMI": {"address": "0x7a7045415f3682C3349E4b68d2940204b81fFF33", "price": 0.99960}, 24 | "SMSM": {"address": "0x6756B4542d545270CacF1F15C3b7DefE589Ba1aa", "price": 0.99959}, 25 | "SMI": {"address": "0xC9005DD5C562bDdEF1Cf3C90Ad5B1Bf54fB8aa9d", "price": 0.99959}, 26 | "sUSDT": {"address": "0x65296738D4E5edB1515e40287B6FDf8320E6eE04", "price": 1.0} 27 | } 28 | 29 | # ABI cho token ERC-20 và Router 30 | TOKEN_ABI = [ 31 | {"constant": False, "inputs": [{"name": "spender", "type": "address"}, {"name": "amount", "type": "uint256"}], "name": "approve", "outputs": [{"name": "", "type": "bool"}], "type": "function"}, 32 | {"constant": True, "inputs": [{"name": "_owner", "type": "address"}], "name": "balanceOf", "outputs": [{"name": "balance", "type": "uint256"}], "type": "function"}, 33 | {"constant": True, "inputs": [], "name": "decimals", "outputs": [{"name": "", "type": "uint8"}], "type": "function"}, 34 | {"constant": True, "inputs": [], "name": "totalSupply", "outputs": [{"name": "", "type": "uint256"}], "type": "function"} 35 | ] 36 | 37 | SWAP_ROUTER_ABI = [ 38 | { 39 | "inputs": [ 40 | { 41 | "components": [ 42 | {"name": "tokenIn", "type": "address"}, 43 | {"name": "tokenOut", "type": "address"}, 44 | {"name": "fee", "type": "uint24"}, 45 | {"name": "recipient", "type": "address"}, 46 | {"name": "amountIn", "type": "uint256"}, 47 | {"name": "amountOutMinimum", "type": "uint256"}, 48 | {"name": "sqrtPriceLimitX96", "type": "uint160"} 49 | ], 50 | "name": "params", 51 | "type": "tuple" 52 | } 53 | ], 54 | "name": "exactInputSingle", 55 | "outputs": [{"name": "amountOut", "type": "uint256"}], 56 | "stateMutability": "nonpayable", 57 | "type": "function" 58 | } 59 | ] 60 | 61 | # Từ vựng song ngữ 62 | LANG = { 63 | 'vi': { 64 | 'title': 'BÁN TOKEN MEME - SOMNIA TESTNET', 65 | 'info': 'Thông tin', 66 | 'found': 'Tìm thấy', 67 | 'wallets': 'ví', 68 | 'processing_wallet': 'XỬ LÝ VÍ', 69 | 'select_token': 'Chọn token để bán (1: SOMI │ 2: SMSM │ 3: SMI): ', 70 | 'enter_amount': 'Nhập số lượng {token} để bán: ', 71 | 'approve_success': 'Đã approve {amount} {token} thành công!', 72 | 'sell_success': 'Đã bán {amount} {token} lấy sUSDT thành công!', 73 | 'approve_failure': 'Approve thất bại', 74 | 'sell_failure': 'Bán thất bại', 75 | 'balance': 'Số dư : {balance} {token}', 76 | 'price': 'Giá : {price} sUSDT/{token}', 77 | 'market_cap': 'Vốn hóa : {market_cap} sUSDT', 78 | 'insufficient_balance': 'Số dư {token} không đủ: {balance} < {amount}', 79 | 'pausing': 'Tạm nghỉ', 80 | 'completed': 'HOÀN THÀNH: {successful}/{total} GIAO DỊCH THÀNH CÔNG', 81 | 'error': 'Lỗi', 82 | 'connect_success': 'Thành công: Đã kết nối mạng Somnia Testnet', 83 | 'invalid_input': 'Lựa chọn không hợp lệ', 84 | 'pvkey_not_found': 'File pvkey.txt không tồn tại', 85 | 'pvkey_empty': 'Không tìm thấy private key hợp lệ' 86 | }, 87 | 'en': { 88 | 'title': 'SELL MEME TOKEN - SOMNIA TESTNET', 89 | 'info': 'Info', 90 | 'found': 'Found', 91 | 'wallets': 'wallets', 92 | 'processing_wallet': 'PROCESSING WALLET', 93 | 'select_token': 'Select token to sell (1: SOMI │ 2: SMSM │ 3: SMI): ', 94 | 'enter_amount': 'Enter {token} amount to sell: ', 95 | 'approve_success': 'Successfully approved {amount} {token}!', 96 | 'sell_success': 'Successfully sold {amount} {token} for sUSDT!', 97 | 'approve_failure': 'Approve failed', 98 | 'sell_failure': 'Sell failed', 99 | 'balance': 'Balance : {balance} {token}', 100 | 'price': 'Price : {price} sUSDT/{token}', 101 | 'market_cap': 'Market Cap : {market_cap} sUSDT', 102 | 'insufficient_balance': 'Insufficient {token} balance: {balance} < {amount}', 103 | 'pausing': 'Pausing', 104 | 'completed': 'COMPLETED: {successful}/{total} TRANSACTIONS SUCCESSFUL', 105 | 'error': 'Error', 106 | 'connect_success': 'Success: Connected to Somnia Testnet', 107 | 'invalid_input': 'Invalid selection', 108 | 'pvkey_not_found': 'pvkey.txt file not found', 109 | 'pvkey_empty': 'No valid private keys found' 110 | } 111 | } 112 | 113 | # Hàm hiển thị viền 114 | def print_border(text: str, color=Fore.CYAN, width=BORDER_WIDTH): 115 | text = f" {text} " 116 | padded_text = text.center(width - 2, "─") 117 | print(f"{color}┌{'─' * (width - 2)}┐{Style.RESET_ALL}") 118 | print(f"{color}│{padded_text}│{Style.RESET_ALL}") 119 | print(f"{color}└{'─' * (width - 2)}┘{Style.RESET_ALL}") 120 | print() 121 | 122 | # Hàm hiển thị phân cách 123 | def print_separator(color=Fore.MAGENTA): 124 | print(f"{color}{'═' * BORDER_WIDTH}{Style.RESET_ALL}") 125 | print() 126 | 127 | # Hàm kiểm tra private key hợp lệ 128 | def is_valid_private_key(key: str) -> bool: 129 | key = key.strip() 130 | if not key.startswith('0x'): 131 | key = '0x' + key 132 | try: 133 | bytes.fromhex(key.replace('0x', '')) 134 | return len(key) == 66 135 | except ValueError: 136 | return False 137 | 138 | # Hàm đọc private keys từ pvkey.txt 139 | def load_private_keys(file_path: str = "pvkey.txt", language: str = 'en') -> list: 140 | try: 141 | if not os.path.exists(file_path): 142 | print(f"{Fore.RED} ✖ {LANG[language]['pvkey_not_found']}{Style.RESET_ALL}") 143 | with open(file_path, 'w') as f: 144 | f.write("# Thêm private keys vào đây, mỗi key trên một dòng\n# Ví dụ: 0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef\n") 145 | sys.exit(1) 146 | 147 | valid_keys = [] 148 | with open(file_path, 'r') as f: 149 | for i, line in enumerate(f, 1): 150 | key = line.strip() 151 | if key and not key.startswith('#'): 152 | if is_valid_private_key(key): 153 | if not key.startswith('0x'): 154 | key = '0x' + key 155 | valid_keys.append((i, key)) 156 | 157 | if not valid_keys: 158 | print(f"{Fore.RED} ✖ {LANG[language]['pvkey_empty']}{Style.RESET_ALL}") 159 | sys.exit(1) 160 | 161 | return valid_keys 162 | except Exception as e: 163 | print(f"{Fore.RED} ✖ {LANG[language]['error']}: {str(e)}{Style.RESET_ALL}") 164 | sys.exit(1) 165 | 166 | # Hàm kết nối Web3 167 | def connect_web3(language: str = 'en'): 168 | try: 169 | w3 = Web3(Web3.HTTPProvider(NETWORK_URL)) 170 | if not w3.is_connected(): 171 | print(f"{Fore.RED} ✖ {LANG[language]['connect_error']}{Style.RESET_ALL}") 172 | sys.exit(1) 173 | print(f"{Fore.GREEN} ✔ {LANG[language]['connect_success']} │ Chain ID: {w3.eth.chain_id}{Style.RESET_ALL}") 174 | return w3 175 | except Exception as e: 176 | print(f"{Fore.RED} ✖ {LANG[language]['error']}: {str(e)}{Style.RESET_ALL}") 177 | sys.exit(1) 178 | 179 | # Hàm lấy thông tin token 180 | def get_token_info(w3: Web3, token_symbol: str, wallet_address: str, language: str = 'en'): 181 | contract = w3.eth.contract(address=Web3.to_checksum_address(TOKENS[token_symbol]["address"]), abi=TOKEN_ABI) 182 | try: 183 | balance = contract.functions.balanceOf(wallet_address).call() / 10**contract.functions.decimals().call() 184 | total_supply = contract.functions.totalSupply().call() / 10**contract.functions.decimals().call() 185 | price = TOKENS[token_symbol]["price"] 186 | market_cap = price * total_supply 187 | print(f"{Fore.YELLOW} {LANG[language]['balance'].format(balance=f'{balance:,.2f}', token=token_symbol)}{Style.RESET_ALL}") 188 | print(f"{Fore.YELLOW} {LANG[language]['price'].format(price=f'{price:,.5f}', token=token_symbol)}{Style.RESET_ALL}") 189 | if token_symbol != "sUSDT": # Chỉ hiển thị Market Cap cho token không phải sUSDT 190 | total_supply = contract.functions.totalSupply().call() / 10**contract.functions.decimals().call() 191 | market_cap = price * total_supply 192 | print(f"{Fore.YELLOW} {LANG[language]['market_cap'].format(market_cap=f'{market_cap:,.2f}')}{Style.RESET_ALL}") 193 | print() 194 | return balance 195 | except Exception as e: 196 | print(f"{Fore.RED} ✖ {LANG[language]['error']}: {str(e)}{Style.RESET_ALL}") 197 | return 0 198 | 199 | # Hàm chọn token để bán 200 | def select_token(language: str = 'en'): 201 | print_border("CHỌN TOKEN ĐỂ BÁN / SELECT TOKEN TO SELL", Fore.YELLOW) 202 | print(f"{Fore.CYAN} 1. Somini (SOMI){Style.RESET_ALL}") 203 | print(f"{Fore.CYAN} 2. Somsom (SMSM){Style.RESET_ALL}") 204 | print(f"{Fore.CYAN} 3. Somi (SMI){Style.RESET_ALL}") 205 | print() 206 | while True: 207 | choice = input(f"{Fore.YELLOW} > {LANG[language]['select_token']}{Style.RESET_ALL}") 208 | if choice == "1": 209 | return "SOMI" 210 | elif choice == "2": 211 | return "SMSM" 212 | elif choice == "3": 213 | return "SMI" 214 | else: 215 | print(f"{Fore.RED} ✖ {LANG[language]['invalid_input']}{Style.RESET_ALL}") 216 | 217 | # Hàm nhập số lượng token 218 | def get_amount(token_symbol: str, language: str = 'en'): 219 | while True: 220 | try: 221 | amount = float(input(f"{Fore.YELLOW} > {LANG[language]['enter_amount'].format(token=token_symbol)}{Style.RESET_ALL}")) 222 | if amount <= 0: 223 | print(f"{Fore.RED} ✖ {LANG[language]['error']}: Amount must be greater than 0{Style.RESET_ALL}") 224 | else: 225 | return amount 226 | except ValueError: 227 | print(f"{Fore.RED} ✖ {LANG[language]['error']}: Invalid number{Style.RESET_ALL}") 228 | 229 | # Hàm approve token 230 | async def approve_token(w3: Web3, private_key: str, token_address: str, spender_address: str, amount: float, wallet_index: int, token_symbol: str, language: str = 'en'): 231 | account = Account.from_key(private_key) 232 | token_contract = w3.eth.contract(address=Web3.to_checksum_address(token_address), abi=TOKEN_ABI) 233 | decimals = token_contract.functions.decimals().call() 234 | amount_wei = int(amount * (10 ** decimals)) 235 | 236 | tx = token_contract.functions.approve( 237 | Web3.to_checksum_address(spender_address), 238 | amount_wei 239 | ).build_transaction({ 240 | 'from': account.address, 241 | 'nonce': w3.eth.get_transaction_count(account.address), 242 | 'gas': 200000, 243 | 'gasPrice': w3.eth.gas_price 244 | }) 245 | 246 | signed_tx = w3.eth.account.sign_transaction(tx, private_key) 247 | tx_hash = w3.eth.send_raw_transaction(signed_tx.raw_transaction) 248 | receipt = w3.eth.wait_for_transaction_receipt(tx_hash, timeout=120) 249 | 250 | if receipt.status == 1: 251 | print(f"{Fore.GREEN} ✔ {LANG[language]['approve_success'].format(amount=f'{amount:,.2f}', token=token_symbol)}{Style.RESET_ALL}") 252 | print() 253 | return tx_hash.hex() 254 | else: 255 | print(f"{Fore.RED} ✖ {LANG[language]['approve_failure']}{Style.RESET_ALL}") 256 | return None 257 | 258 | # Hàm bán token 259 | async def sell_token(w3: Web3, private_key: str, token_symbol: str, amount: float, wallet_index: int, language: str = 'en'): 260 | account = Account.from_key(private_key) 261 | token_in = TOKENS[token_symbol]["address"] 262 | token_out = SUSDT_ADDRESS 263 | swap_router = w3.eth.contract(address=Web3.to_checksum_address(ROUTER_ADDRESS), abi=SWAP_ROUTER_ABI) 264 | 265 | token_contract = w3.eth.contract(address=Web3.to_checksum_address(token_in), abi=TOKEN_ABI) 266 | decimals = token_contract.functions.decimals().call() 267 | amount_in_wei = int(amount * (10 ** decimals)) 268 | amount_out_minimum = int(amount * 0.95 * (10 ** decimals)) # 5% slippage 269 | 270 | tx_data = swap_router.functions.exactInputSingle( 271 | ( 272 | Web3.to_checksum_address(token_in), 273 | Web3.to_checksum_address(token_out), 274 | 500, # Fee 275 | account.address, 276 | amount_in_wei, 277 | amount_out_minimum, 278 | 0 279 | ) 280 | ).build_transaction({ 281 | 'from': account.address, 282 | 'nonce': w3.eth.get_transaction_count(account.address), 283 | 'gas': 300000, 284 | 'gasPrice': w3.eth.gas_price, 285 | 'chainId': CHAIN_ID 286 | }) 287 | 288 | signed_tx = w3.eth.account.sign_transaction(tx_data, private_key) 289 | tx_hash = w3.eth.send_raw_transaction(signed_tx.raw_transaction) 290 | tx_link = f"{EXPLORER_URL}{tx_hash.hex()}" 291 | receipt = w3.eth.wait_for_transaction_receipt(tx_hash, timeout=120) 292 | 293 | if receipt.status == 1: 294 | print(f"{Fore.GREEN} ✔ {LANG[language]['sell_success'].format(amount=f'{amount:,.2f}', token=token_symbol)} │ Tx: {tx_link}{Style.RESET_ALL}") 295 | print() 296 | return True 297 | else: 298 | print(f"{Fore.RED} ✖ {LANG[language]['sell_failure']} │ Tx: {tx_link}{Style.RESET_ALL}") 299 | return False 300 | 301 | # Hàm chính 302 | async def run_sellmeme(language: str = 'en'): 303 | print() 304 | print_border(LANG[language]['title'], Fore.CYAN) 305 | private_keys = load_private_keys('pvkey.txt', language) 306 | print(f"{Fore.YELLOW} ℹ {LANG[language]['info']}: {LANG[language]['found']} {len(private_keys)} {LANG[language]['wallets']}{Style.RESET_ALL}") 307 | print() 308 | 309 | if not private_keys: 310 | return 311 | 312 | w3 = connect_web3(language) 313 | print() 314 | token_symbol = select_token(language) 315 | amount = get_amount(token_symbol, language) 316 | print_separator() 317 | 318 | successful_sells = 0 319 | total_wallets = len(private_keys) 320 | 321 | random.shuffle(private_keys) 322 | for i, (profile_num, private_key) in enumerate(private_keys, 1): 323 | print_border(f"{LANG[language]['processing_wallet']} {profile_num} ({i}/{len(private_keys)})", Fore.MAGENTA) 324 | 325 | account = Account.from_key(private_key) 326 | token_balance = get_token_info(w3, token_symbol, account.address, language) 327 | if token_balance < amount: 328 | print(f"{Fore.RED} ✖ {LANG[language]['insufficient_balance'].format(balance=f'{token_balance:,.2f}', amount=f'{amount:,.2f}', token=token_symbol)}{Style.RESET_ALL}") 329 | print_separator() 330 | continue 331 | 332 | get_token_info(w3, "sUSDT", account.address, language) 333 | approve_tx = await approve_token(w3, private_key, TOKENS[token_symbol]["address"], SPENDER_ADDRESS, amount, i, token_symbol, language) 334 | if not approve_tx: 335 | print_separator() 336 | continue 337 | 338 | if await sell_token(w3, private_key, token_symbol, amount, i, language): 339 | successful_sells += 1 340 | 341 | if i < len(private_keys): 342 | delay = random.uniform(10, 30) 343 | print(f"{Fore.YELLOW} ℹ {LANG[language]['pausing']} {delay:.2f} {'giây' if language == 'vi' else 'seconds'}{Style.RESET_ALL}") 344 | print() 345 | await asyncio.sleep(delay) 346 | print_separator() 347 | 348 | print() 349 | print_border(f"{LANG[language]['completed'].format(successful=successful_sells, total=total_wallets)}", Fore.GREEN) 350 | 351 | if __name__ == "__main__": 352 | asyncio.run(run_sellmeme('vi')) 353 | -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import asyncio 4 | from colorama import init, Fore, Style 5 | import inquirer 6 | 7 | # Khởi tạo colorama 8 | init(autoreset=True) 9 | 10 | # Độ rộng viền cố định 11 | BORDER_WIDTH = 80 12 | 13 | # Hàm hiển thị viền đẹp mắt 14 | def print_border(text: str, color=Fore.CYAN, width=BORDER_WIDTH): 15 | text = text.strip() 16 | if len(text) > width - 4: 17 | text = text[:width - 7] + "..." # Cắt dài và thêm "..." 18 | padded_text = f" {text} ".center(width - 2) 19 | print(f"{color}┌{'─' * (width - 2)}┐{Style.RESET_ALL}") 20 | print(f"{color}│{padded_text}│{Style.RESET_ALL}") 21 | print(f"{color}└{'─' * (width - 2)}┘{Style.RESET_ALL}") 22 | 23 | # Hàm hiển thị banner 24 | def _banner(): 25 | banner = r""" 26 | 27 | 28 | ░██████╗░█████╗░███╗░░░███╗███╗░░██╗██╗░█████╗░  ████████╗███████╗░██████╗████████╗███╗░░██╗███████╗████████╗ 29 | ██╔════╝██╔══██╗████╗░████║████╗░██║██║██╔══██╗  ╚══██╔══╝██╔════╝██╔════╝╚══██╔══╝████╗░██║██╔════╝╚══██╔══╝ 30 | ╚█████╗░██║░░██║██╔████╔██║██╔██╗██║██║███████║  ░░░██║░░░█████╗░░╚█████╗░░░░██║░░░██╔██╗██║█████╗░░░░░██║░░░ 31 | ░╚═══██╗██║░░██║██║╚██╔╝██║██║╚████║██║██╔══██║  ░░░██║░░░██╔══╝░░░╚═══██╗░░░██║░░░██║╚████║██╔══╝░░░░░██║░░░ 32 | ██████╔╝╚█████╔╝██║░╚═╝░██║██║░╚███║██║██║░░██║  ░░░██║░░░███████╗██████╔╝░░░██║░░░██║░╚███║███████╗░░░██║░░░ 33 | ╚═════╝░░╚════╝░╚═╝░░░░░╚═╝╚═╝░░╚══╝╚═╝╚═╝░░╚═╝  ░░░╚═╝░░░╚══════╝╚═════╝░░░░╚═╝░░░╚═╝░░╚══╝╚══════╝░░░╚═╝░░░ 34 | 35 | 36 | """ 37 | print(f"{Fore.GREEN}{banner:^80}{Style.RESET_ALL}") 38 | print(f"{Fore.GREEN}{'═' * BORDER_WIDTH}{Style.RESET_ALL}") 39 | print_border("SOMNIA TESTNET", Fore.GREEN) 40 | print(f"{Fore.YELLOW}│ {'Liên hệ / Contact'}: {Fore.CYAN}https://t.me/thog099{Style.RESET_ALL}") 41 | print(f"{Fore.YELLOW}│ {'Discord'}: {Fore.CYAN}https://discord.gg/MnmYBKfHQf{Style.RESET_ALL}") 42 | print(f"{Fore.YELLOW}│ {'Channel Telegram'}: {Fore.CYAN}https://t.me/thogairdrops{Style.RESET_ALL}") 43 | print(f"{Fore.GREEN}{'═' * BORDER_WIDTH}{Style.RESET_ALL}") 44 | 45 | # Hàm xóa màn hình 46 | def _clear(): 47 | os.system('cls' if os.name == 'nt' else 'clear') 48 | 49 | # Các hàm giả lập cho các lệnh (cần triển khai thực tế) 50 | 51 | async def run_faucetstt(language: str): 52 | from scripts.faucetstt import run_faucetstt as faucetstt_run 53 | await faucetstt_run(language) 54 | 55 | async def run_sendtx(language: str): 56 | from scripts.sendtx import run_sendtx as sendtx_run 57 | await sendtx_run(language) 58 | 59 | async def run_deploytoken(language: str): 60 | from scripts.deploytoken import run_deploytoken as deploytoken_run 61 | await deploytoken_run(language) 62 | 63 | async def run_sendtoken(language: str): 64 | from scripts.sendtoken import run_sendtoken as sendtoken_run 65 | await sendtoken_run(language) 66 | 67 | async def run_deploynft(language: str): 68 | from scripts.deploynft import run_deploynft as deploynft_run 69 | await deploynft_run(language) 70 | 71 | async def run_mintpong(language: str): 72 | from scripts.mintpong import run_mintpong as mintpong_run 73 | await mintpong_run(language) 74 | 75 | async def run_mintping(language: str): 76 | from scripts.mintping import run_mintping as mintping_run 77 | await mintping_run(language) 78 | 79 | async def run_swappong(language: str): 80 | from scripts.swappong import run_swappong as swappong_run 81 | await swappong_run(language) 82 | 83 | async def run_swapping(language: str): 84 | from scripts.swapping import run_swapping as swapping_run 85 | await swapping_run(language) 86 | 87 | async def run_conftnft(language: str): 88 | from scripts.conftnft import run_conftnft as conftnft_run 89 | await conftnft_run(language) 90 | 91 | async def run_mintsusdt(language: str): 92 | from scripts.mintsusdt import run_mintsusdt as mintsusdt_run 93 | await mintsusdt_run(language) 94 | 95 | async def run_buymeme(language: str): 96 | from scripts.buymeme import run_buymeme as buymeme_run 97 | await buymeme_run(language) 98 | 99 | async def run_sellmeme(language: str): 100 | from scripts.sellmeme import run_sellmeme as sellmeme_run 101 | await sellmeme_run(language) 102 | 103 | async def run_fun(language: str): 104 | from scripts.fun import run_fun as fun_run 105 | await fun_run(language) 106 | 107 | async def run_lovesomini(language: str): 108 | from scripts.lovesomini import run_lovesomini as lovesomini_run 109 | await lovesomini_run(language) 110 | 111 | async def run_mintnerzo(language: str): 112 | from scripts.mintnerzo import run_mintnerzo as mintnerzo_run 113 | await mintnerzo_run(language) 114 | 115 | async def run_mintaura(language: str): 116 | from scripts.mintaura import run_mintaura as mintaura_run 117 | await mintaura_run(language) 118 | 119 | async def run_nftcollection(language: str): 120 | from scripts.nftcollection import run_nftcollection as nftcollection_run 121 | await nftcollection_run(language) 122 | 123 | async def run_mintair(language: str): 124 | from scripts.mintair import run_mintair as mintair_run 125 | await mintair_run(language) 126 | 127 | async def run_mintalze(language: str): 128 | from scripts.mintalze import run_mintalze as mintalze_run 129 | await mintalze_run(language) 130 | 131 | async def run_quickswap(language: str): 132 | from scripts.quickswap import run_quickswap as quickswap_run 133 | await quickswap_run(language) 134 | 135 | async def run_wrapquick(language: str): 136 | from scripts.wrapquick import run_wrapquick as wrapquick_run 137 | await wrapquick_run(language) 138 | 139 | async def run_wrapsoex(language: str): 140 | from scripts.wrapsoex import run_wrapsoex as wrapsoex_run 141 | await wrapsoex_run(language) 142 | 143 | async def run_mintomnihub(language: str): 144 | from scripts.mintomnihub import run_mintomnihub as mintomnihub_run 145 | await mintomnihub_run(language) 146 | 147 | async def run_swapsoex(language: str): 148 | from scripts.swapsoex import run_swapsoex as swapsoex_run 149 | await swapsoex_run(language) 150 | 151 | async def run_sopixel(language: str): 152 | from scripts.sopixel import run_sopixel as sopixel_run 153 | await sopixel_run(language) 154 | 155 | 156 | async def cmd_exit(language: str): 157 | print_border(f"Exiting...", Fore.GREEN) 158 | sys.exit(0) 159 | 160 | # Danh sách lệnh menu 161 | SCRIPT_MAP = { 162 | "faucetstt": run_faucetstt, 163 | "sendtx": run_sendtx, 164 | "deploytoken": run_deploytoken, 165 | "sendtoken": run_sendtoken, 166 | "deploynft": run_deploynft, 167 | "mintpong": run_mintpong, 168 | "mintping": run_mintping, 169 | "swappong": run_swappong, 170 | "swapping": run_swapping, 171 | "conftnft": run_conftnft, 172 | "mintsusdt": run_mintsusdt, 173 | "buymeme": run_buymeme, 174 | "sellmeme": run_sellmeme, 175 | "fun": run_fun, 176 | "lovesomini": run_lovesomini, 177 | "mintnerzo": run_mintnerzo, 178 | "mintaura": run_mintaura, 179 | "nftcollection": run_nftcollection, 180 | "mintair": run_mintair, 181 | "mintalze": run_mintalze, 182 | "quickswap": run_quickswap, 183 | "wrapquick": run_wrapquick, 184 | "wrapsoex": run_wrapsoex, 185 | "mintomnihub": run_mintomnihub, 186 | "swapsoex": run_swapsoex, 187 | "sopixel": run_sopixel, 188 | "exit": cmd_exit 189 | } 190 | 191 | def get_available_scripts(language): 192 | scripts = { 193 | 'vi': [ 194 | {"name": "1. Faucet token $STT | Somnia Testnet", "value": "faucetstt", "locked": True}, 195 | {"name": "2. Mint $PONG | Somnia Testnet", "value": "mintpong"}, 196 | {"name": "3. Mint $PING | Somnia Testnet", "value": "mintping"}, 197 | {"name": "4. QuickSwap [ STT | USDC | WETH | WSTT ] | Somnia Testnet", "value": "quickswap", "locked": True}, 198 | {"name": "5. Somnia Exchange [ STT | USDT.g | NIA | WSTT ] | Somnia Testnet", "value": "swapsoex", "locked": True}, 199 | {"name": "6. Wrap/Unwrap STT -> QuickSwap | Somnia Testnet", "value": "quickswap"}, 200 | {"name": "7. Wrap/Unwrap STT -> Somnia Exchange | Somnia Testnet", "value": "wrapsoex"}, 201 | {"name": "8. Swap $PONG -> $PING | Somnia Testnet", "value": "swappong", "locked": True}, 202 | {"name": "9. Swap $PING -> $PONG | Somnia Testnet", "value": "swapping", "separator": True, "locked": True}, 203 | {"name": "10. Mint NFT Community Member of Somnia (CMS - CoNFT) | Somnia Testnet", "value": "conftnft", "locked": True}, 204 | #{"name": "11. Mint 1000 sUSDT [END] | Somnia Testnet", "value": "mintsusdt"}, 205 | #{"name": "12. Memecoin Trading - Mua Memecoin ( SOMI / SMSM / SMI ) [END] | Somnia Testnet", "value": "buymeme"}, 206 | #{"name": "13. Memecoin Trading - Bán Memecoin ( SOMI / SMSM / SMI )[END] | Somnia Testnet", "value": "sellmeme"}, 207 | {"name": "11. Quills Fun - Mint Tin Nhắn NFT | Somnia Testnet", "value": "fun", "locked": True}, 208 | {"name": "12. Love Somini | Somnia Testnet", "value": "lovesomini", "locked": True}, 209 | {"name": "13. Mint NFTs NERZO | Somnia Testnet", "value": "mintnerzo", "locked": True}, 210 | {"name": "14. Mint Somni ✨ | Somnia Testnet", "value": "mintaura", "locked": True}, 211 | {"name": "15. Mint ALZE | Somnia Testnet", "value": "mintalze", "locked": True}, 212 | {"name": "16. Mint OmniHub [ OMNIHUB x SOMNIA | Somnia Cats ] | Somnia Testnet", "value": "mintomnihub", "locked": True}, 213 | {"name": "17. Deploy Smart Contract Mintair | Somnia Testnet", "value": "mintair", "locked": True}, 214 | {"name": "18. Deploy NFT - Quản lý bộ sưu tập NFT [ Tạo | Mint | Đốt ] | Somnia Testnet", "value": "nftcollection"}, 215 | {"name": "19. Send TX ngẫu nhiên hoặc File (address.txt) | Somnia Testnet", "value": "sendtx"}, 216 | {"name": "20. Deploy Token smart-contract | Somnia Testnet", "value": "deploytoken"}, 217 | {"name": "21. Send Token ERC20 ngẫu nhiên hoặc File (addressERC20.txt) | Somnia Testnet", "value": "sendtoken"}, 218 | {"name": "22. Tô màu Somnia Pixel | Somnia Testnet", "value": "sopixel"}, 219 | 220 | {"name": "23. Exit", "value": "exit"}, 221 | 222 | ], 223 | 'en': [ 224 | {"name": "1. Faucet token $STT", "value": "faucetstt", "locked": True}, 225 | {"name": "2. Mint $PONG | Somnia Testnet", "value": "mintpong"}, 226 | {"name": "3. Mint $PING | Somnia Testnet", "value": "mintping"}, 227 | {"name": "4. QuickSwap [ STT | USDC | WETH | WSTT ] | Somnia Testnet", "value": "quickswap", "locked": True}, 228 | {"name": "5. Somnia Exchange [ STT | USDT.g | NIA | WSTT ] | Somnia Testnet", "value": "swapsoex", "locked": True}, 229 | {"name": "6. Wrap/Unwrap STT -> QuickSwap | Somnia Testnet", "value": "wrapquick"}, 230 | {"name": "7. Wrap/Unwrap STT -> Somnia Exchange | Somnia Testnet", "value": "wrapsoex"}, 231 | {"name": "8. Swap $PONG -> $PING | Somnia Testnet", "value": "swappong", "locked": True}, 232 | {"name": "9. Swap $PING -> $PONG | Somnia Testnet", "value": "swapping", "separator": True, "locked": True}, 233 | {"name": "10. Mint NFT Community Member of Somnia (CMS - CoNFT) | Somnia Testnet", "value": "conftnft", "locked": True}, 234 | #{"name": "11. Mint 1000 sUSDT [END] | Somnia Testnet", "value": "mintsusdt"}, 235 | #{"name": "12. Memecoin Trading - Buy Memecoin ( SOMI / SMSM / SMI ) [END] | Somnia Testnet", "value": "buymeme"}, 236 | #{"name": "13. Memecoin Trading - Sell Memecoin ( SOMI / SMSM / SMI ) [END] | Somnia Testnet", "value": "sellmeme"}, 237 | {"name": "11. Quills Fun - Mint Message NFT | Somnia Testnet", "value": "fun", "locked": True}, 238 | {"name": "12. Love Somini | Somnia Testnet", "value": "lovesomini", "locked": True}, 239 | {"name": "13. Mint NFTs NERZO | Somnia Testnet", "value": "mintnerzo", "locked": True}, 240 | {"name": "14. Mint Somni ✨ | Somnia Testnet", "value": "mintaura", "locked": True}, 241 | {"name": "15. Mint ALZE | Somnia Testnet", "value": "mintalze", "locked": True}, 242 | {"name": "16. Mint OmniHub [ OMNIHUB x SOMNIA | Somnia Cats ] | Somnia Testnet", "value": "mintomnihub", "locked": True}, 243 | {"name": "17. Deploy Smart Contract Mintair | Somnia Testnet", "value": "mintair", "locked": True}, 244 | {"name": "18. Deploy NFT - Manage NFT Collection [ Create | Mint | Burn ] | Somnia Testnet", "value": "nftcollection"}, 245 | {"name": "19. Send Random TX or File (address.txt) | Somnia Testnet", "value": "sendtx"}, 246 | {"name": "20. Deploy Token smart-contract", "value": "deploytoken"}, 247 | {"name": "21. Send Token ERC20 Random or File (addressERC20.txt) | Somnia Testnet", "value": "sendtoken"}, 248 | {"name": "22. Somnia Pixel Color | Somnia Testnet", "value": "sopixel"}, 249 | 250 | {"name": "23. Exit", "value": "exit"}, 251 | ] 252 | } 253 | return scripts[language] 254 | 255 | def run_script(script_func, language): 256 | """Chạy script bất kể nó là async hay không.""" 257 | if asyncio.iscoroutinefunction(script_func): 258 | asyncio.run(script_func(language)) 259 | else: 260 | script_func(language) 261 | 262 | def select_language(): 263 | while True: 264 | _clear() 265 | _banner() 266 | print(f"{Fore.GREEN}{'═' * BORDER_WIDTH}{Style.RESET_ALL}") 267 | print_border("CHỌN NGÔN NGỮ / SELECT LANGUAGE", Fore.YELLOW) 268 | questions = [ 269 | inquirer.List('language', 270 | message=f"{Fore.CYAN}Vui lòng chọn / Please select:{Style.RESET_ALL}", 271 | choices=[("1. Tiếng Việt", 'vi'), ("2. English", 'en')], 272 | carousel=True) 273 | ] 274 | answer = inquirer.prompt(questions) 275 | if answer and answer['language'] in ['vi', 'en']: 276 | return answer['language'] 277 | print(f"{Fore.RED}❌ {'Lựa chọn không hợp lệ / Invalid choice':^76}{Style.RESET_ALL}") 278 | 279 | def main(): 280 | _clear() 281 | _banner() 282 | language = select_language() 283 | 284 | messages = { 285 | "vi": { 286 | "running": "Đang thực thi: {}", 287 | "completed": "Đã hoàn thành: {}", 288 | "error": "Lỗi: {}", 289 | "press_enter": "Nhấn Enter để tiếp tục...", 290 | "menu_title": "MENU CHÍNH", 291 | "select_script": "Chọn script để chạy", 292 | "locked": "🔒 Script này bị khóa! Vui lòng vào group hoặc donate để mở khóa." 293 | }, 294 | "en": { 295 | "running": "Running: {}", 296 | "completed": "Completed: {}", 297 | "error": "Error: {}", 298 | "press_enter": "Press Enter to continue...", 299 | "menu_title": "MAIN MENU", 300 | "select_script": "Select script to run", 301 | "locked": "🔒 This script is locked! Please join our group or donate to unlock." 302 | } 303 | } 304 | 305 | while True: 306 | _clear() 307 | _banner() 308 | print(f"{Fore.YELLOW}{'═' * BORDER_WIDTH}{Style.RESET_ALL}") 309 | print_border(messages[language]["menu_title"], Fore.YELLOW) 310 | print(f"{Fore.CYAN}│ {messages[language]['select_script'].center(BORDER_WIDTH - 4)} │{Style.RESET_ALL}") 311 | 312 | available_scripts = get_available_scripts(language) 313 | questions = [ 314 | inquirer.List('script', 315 | message=f"{Fore.CYAN}{messages[language]['select_script']}{Style.RESET_ALL}", 316 | choices=[script["name"] for script in available_scripts], 317 | carousel=True) 318 | ] 319 | answers = inquirer.prompt(questions) 320 | if not answers: 321 | continue 322 | 323 | selected_script_name = answers['script'] 324 | selected_script = next(script for script in available_scripts if script["name"] == selected_script_name) 325 | selected_script_value = selected_script["value"] 326 | 327 | if selected_script.get("locked"): 328 | _clear() 329 | _banner() 330 | print_border("SCRIPT BỊ KHÓA / LOCKED", Fore.RED) 331 | print(f"{Fore.YELLOW}{messages[language]['locked']}") 332 | print('') 333 | print(f"{Fore.CYAN}→ Telegram: https://t.me/thogairdrops") 334 | print(f"{Fore.CYAN}→ Donate: https://buymecafe.vercel.app{Style.RESET_ALL}") 335 | print('') 336 | input(f"{Fore.YELLOW}⏎ {messages[language]['press_enter']}{Style.RESET_ALL:^76}") 337 | continue 338 | 339 | script_func = SCRIPT_MAP.get(selected_script_value) 340 | if script_func is None: 341 | print(f"{Fore.RED}{'═' * BORDER_WIDTH}{Style.RESET_ALL}") 342 | print_border(f"{'Chưa triển khai / Not implemented'}: {selected_script_name}", Fore.RED) 343 | input(f"{Fore.YELLOW}⏎ {messages[language]['press_enter']}{Style.RESET_ALL:^76}") 344 | continue 345 | 346 | try: 347 | print(f"{Fore.CYAN}{'═' * BORDER_WIDTH}{Style.RESET_ALL}") 348 | print_border(messages[language]["running"].format(selected_script_name), Fore.CYAN) 349 | run_script(script_func, language) 350 | print(f"{Fore.GREEN}{'═' * BORDER_WIDTH}{Style.RESET_ALL}") 351 | print_border(messages[language]["completed"].format(selected_script_name), Fore.GREEN) 352 | input(f"{Fore.YELLOW}⏎ {messages[language]['press_enter']}{Style.RESET_ALL:^76}") 353 | except Exception as e: 354 | print(f"{Fore.RED}{'═' * BORDER_WIDTH}{Style.RESET_ALL}") 355 | print_border(messages[language]["error"].format(str(e)), Fore.RED) 356 | print('') 357 | input(f"{Fore.YELLOW}⏎ {messages[language]['press_enter']}{Style.RESET_ALL:^76}") 358 | 359 | if __name__ == "__main__": 360 | main() 361 | -------------------------------------------------------------------------------- /scripts/mintsusdt.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import asyncio 4 | import random 5 | from web3 import Web3 6 | from eth_account import Account 7 | from colorama import init, Fore, Style 8 | from typing import List, Dict 9 | import aiohttp 10 | from aiohttp_socks import ProxyConnector 11 | 12 | # Khởi tạo colorama 13 | init(autoreset=True) 14 | 15 | # Cấu hình 16 | NETWORK_URL = "https://dream-rpc.somnia.network" 17 | CHAIN_ID = 50312 18 | EXPLORER_URL = "https://shannon-explorer.somnia.network" 19 | IP_CHECK_URL = "https://api.ipify.org?format=json" 20 | SHUFFLE_WALLETS = True 21 | MINT_SUSDT_SLEEP_RANGE = [10, 30] # [min, max] in seconds 22 | CONFIG = { 23 | "MAX_CONCURRENCY": 5, 24 | "MINIMUM_BALANCE": 0.001 # STT 25 | } 26 | BORDER_WIDTH = 80 27 | HEADERS = { 28 | "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36", 29 | "Accept": "application/json, text/plain, */*", 30 | "Accept-Encoding": "gzip, deflate, br", 31 | "Accept-Language": "en-US,en;q=0.9", 32 | } 33 | 34 | # Constants 35 | CONTRACT_ADDRESS = "0x65296738D4E5edB1515e40287B6FDf8320E6eE04" # Hợp đồng sUSDT 36 | MINT_AMOUNT = 1000 # Mint 1000 sUSDT 37 | MINT_DATA = "0x1249c58b" # Bytecode để mint 38 | 39 | # ABI tối thiểu để kiểm tra số dư sUSDT 40 | SUSDT_TOKEN_ABI = [ 41 | { 42 | "constant": True, 43 | "inputs": [{"name": "_owner", "type": "address"}], 44 | "name": "balanceOf", 45 | "outputs": [{"name": "balance", "type": "uint256"}], 46 | "type": "function" 47 | }, 48 | { 49 | "inputs": [], 50 | "name": "decimals", 51 | "outputs": [{"internalType": "uint8", "name": "", "type": "uint8"}], 52 | "stateMutability": "view", 53 | "type": "function" 54 | } 55 | ] 56 | 57 | # Từ vựng song ngữ 58 | LANG = { 59 | 'vi': { 60 | 'title': '✨ MINT sUSDT - SOMNIA TESTNET ✨', 61 | 'info': 'ℹ Thông tin', 62 | 'found': 'Tìm thấy', 63 | 'wallets': 'ví', 64 | 'found_proxies': 'Tìm thấy {count} proxy trong proxies.txt', 65 | 'processing_wallet': '⚙ XỬ LÝ VÍ', 66 | 'checking_balance': 'Đang kiểm tra số dư...', 67 | 'insufficient_balance': 'Số dư không đủ (cần ít nhất {required:.6f} STT cho giao dịch)', 68 | 'success': '✅ Đã mint 1000 sUSDT thành công!', 69 | 'failure': '❌ Mint sUSDT thất bại', 70 | 'timeout': '⏰ Giao dịch chưa xác nhận sau {timeout} giây, kiểm tra trên explorer', 71 | 'address': 'Địa chỉ ví', 72 | 'amount': 'Số lượng: 1000 sUSDT', 73 | 'gas': 'Gas', 74 | 'block': 'Khối', 75 | 'balance': 'Số dư STT: {stt:.6f} STT | sUSDT: {susdt:.2f}', 76 | 'pausing': 'Tạm nghỉ', 77 | 'seconds': 'giây', 78 | 'completed': '🏁 HOÀN THÀNH: {successful}/{total} VÍ THÀNH CÔNG', 79 | 'error': 'Lỗi', 80 | 'connect_success': '✅ Thành công: Đã kết nối mạng Somnia Testnet', 81 | 'connect_error': '❌ Không thể kết nối RPC', 82 | 'web3_error': '❌ Kết nối Web3 thất bại', 83 | 'pvkey_not_found': '❌ File pvkey.txt không tồn tại', 84 | 'pvkey_empty': '❌ Không tìm thấy private key hợp lệ', 85 | 'pvkey_error': '❌ Đọc pvkey.txt thất bại', 86 | 'invalid_key': 'không hợp lệ, bỏ qua', 87 | 'warning_line': '⚠ Cảnh báo: Dòng', 88 | 'estimating_gas': 'Đang ước lượng gas...', 89 | 'using_proxy': '🔄 Sử dụng Proxy - [{proxy}] với IP công khai - [{public_ip}]', 90 | 'no_proxy': 'Không có proxy', 91 | 'unknown': 'Không xác định', 92 | 'no_proxies': 'Không tìm thấy proxy trong proxies.txt', 93 | 'invalid_proxy': '⚠ Proxy không hợp lệ hoặc không hoạt động: {proxy}', 94 | 'ip_check_failed': '⚠ Không thể kiểm tra IP công khai: {error}', 95 | 'sending_tx': 'Đang gửi giao dịch...', 96 | 'already_minted': '⚠ Ví này đã mint sUSDT! Không thực hiện lại yêu cầu này.' 97 | }, 98 | 'en': { 99 | 'title': '✨ MINT sUSDT - SOMNIA TESTNET ✨', 100 | 'info': 'ℹ Info', 101 | 'found': 'Found', 102 | 'wallets': 'wallets', 103 | 'found_proxies': 'Found {count} proxies in proxies.txt', 104 | 'processing_wallet': '⚙ PROCESSING WALLET', 105 | 'checking_balance': 'Checking balance...', 106 | 'insufficient_balance': 'Insufficient balance (need at least {required:.6f} STT for transaction)', 107 | 'success': '✅ Successfully minted 1000 sUSDT!', 108 | 'failure': '❌ Failed to mint sUSDT', 109 | 'timeout': '⏰ Transaction not confirmed after {timeout} seconds', 110 | 'address': 'Wallet address', 111 | 'amount': 'Amount: 1000 sUSDT', 112 | 'gas': 'Gas', 113 | 'block': 'Block', 114 | 'balance': 'Balance: {stt:.6f} STT | sUSDT: {susdt:.2f}', 115 | 'pausing': 'Pausing', 116 | 'seconds': 'seconds', 117 | 'completed': '🏁 COMPLETED: {successful}/{total} WALLETS SUCCESSFUL', 118 | 'error': 'Error', 119 | 'connect_success': '✅ Success: Connected to Somnia Testnet', 120 | 'connect_error': '❌ Failed to connect to RPC', 121 | 'web3_error': '❌ Web3 connection failed', 122 | 'pvkey_not_found': '❌ pvkey.txt file not found', 123 | 'pvkey_empty': '❌ No valid private keys found', 124 | 'pvkey_error': '❌ Failed to read pvkey.txt', 125 | 'invalid_key': 'is invalid, skipped', 126 | 'warning_line': '⚠ Warning: Line', 127 | 'estimating_gas': 'Estimating gas...', 128 | 'using_proxy': '🔄 Using Proxy - [{proxy}] with Public IP - [{public_ip}]', 129 | 'no_proxy': 'None', 130 | 'unknown': 'Unknown', 131 | 'no_proxies': 'No proxies found in proxies.txt', 132 | 'invalid_proxy': '⚠ Invalid or unresponsive proxy: {proxy}', 133 | 'ip_check_failed': '⚠ Failed to check public IP: {error}', 134 | 'sending_tx': 'Sending transaction...', 135 | 'already_minted': '⚠ This wallet has already minted sUSDT! Skipping this request.' 136 | } 137 | } 138 | 139 | # Hàm hiển thị viền đẹp mắt 140 | def print_border(text: str, color=Fore.CYAN, width=BORDER_WIDTH): 141 | text = text.strip() 142 | if len(text) > width - 4: 143 | text = text[:width - 7] + "..." 144 | padded_text = f" {text} ".center(width - 2) 145 | print(f"{color}┌{'─' * (width - 2)}┐{Style.RESET_ALL}", flush=True) 146 | print(f"{color}│{padded_text}│{Style.RESET_ALL}", flush=True) 147 | print(f"{color}└{'─' * (width - 2)}┘{Style.RESET_ALL}", flush=True) 148 | 149 | # Hàm hiển thị phân cách 150 | def print_separator(color=Fore.MAGENTA): 151 | print(f"{color}{'═' * BORDER_WIDTH}{Style.RESET_ALL}", flush=True) 152 | 153 | # Hàm in thông báo 154 | def print_message(message: str, color=Fore.YELLOW): 155 | print(f"{color}{message}{Style.RESET_ALL}", flush=True) 156 | 157 | # Hàm kiểm tra private key hợp lệ 158 | def is_valid_private_key(key: str) -> bool: 159 | key = key.strip() 160 | if not key.startswith('0x'): 161 | key = '0x' + key 162 | try: 163 | bytes.fromhex(key.replace('0x', '')) 164 | return len(key) == 66 165 | except ValueError: 166 | return False 167 | 168 | # Hàm đọc private keys từ file pvkey.txt 169 | def load_private_keys(file_path: str = "pvkey.txt", language: str = 'en') -> List[tuple]: 170 | try: 171 | if not os.path.exists(file_path): 172 | print_message(f" ✖ {LANG[language]['pvkey_not_found']}", Fore.RED) 173 | with open(file_path, 'w') as f: 174 | f.write("# Thêm private keys vào đây, mỗi key trên một dòng\n# Ví dụ: 0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef\n") 175 | sys.exit(1) 176 | 177 | valid_keys = [] 178 | with open(file_path, 'r') as f: 179 | for i, line in enumerate(f, 1): 180 | key = line.strip() 181 | if key and not key.startswith('#'): 182 | if is_valid_private_key(key): 183 | if not key.startswith('0x'): 184 | key = '0x' + key 185 | valid_keys.append((i, key)) 186 | else: 187 | print_message(f" ⚠ {LANG[language]['warning_line']} {i} {LANG[language]['invalid_key']}: {key}", Fore.YELLOW) 188 | 189 | if not valid_keys: 190 | print_message(f" ✖ {LANG[language]['pvkey_empty']}", Fore.RED) 191 | sys.exit(1) 192 | 193 | return valid_keys 194 | except Exception as e: 195 | print_message(f" ✖ {LANG[language]['pvkey_error']}: {str(e)}", Fore.RED) 196 | sys.exit(1) 197 | 198 | # Hàm đọc proxies từ proxies.txt 199 | def load_proxies(file_path: str = "proxies.txt", language: str = 'en') -> List[str]: 200 | try: 201 | if not os.path.exists(file_path): 202 | print_message(f" ⚠ {LANG[language]['no_proxies']}. Dùng không proxy.", Fore.YELLOW) 203 | with open(file_path, 'w') as f: 204 | f.write("# Thêm proxy vào đây, mỗi proxy trên một dòng\n# Ví dụ: socks5://user:pass@host:port hoặc http://host:port\n") 205 | return [] 206 | 207 | proxies = [] 208 | with open(file_path, 'r') as f: 209 | for line in f: 210 | proxy = line.strip() 211 | if proxy and not line.startswith('#'): 212 | proxies.append(proxy) 213 | 214 | if not proxies: 215 | print_message(f" ⚠ {LANG[language]['no_proxies']}. Dùng không proxy.", Fore.YELLOW) 216 | return [] 217 | 218 | print_message(f" ℹ {LANG[language]['found_proxies'].format(count=len(proxies))}") 219 | return proxies 220 | except Exception as e: 221 | print_message(f" ✖ {LANG[language]['error']}: {str(e)}", Fore.RED) 222 | return [] 223 | 224 | # Hàm lấy IP công khai qua proxy 225 | async def get_proxy_ip(proxy: str = None, language: str = 'en') -> str: 226 | try: 227 | if proxy: 228 | if proxy.startswith(('socks5://', 'socks4://', 'http://', 'https://')): 229 | connector = ProxyConnector.from_url(proxy) 230 | else: 231 | parts = proxy.split(':') 232 | if len(parts) == 4: # host:port:user:pass 233 | proxy_url = f"socks5://{parts[2]}:{parts[3]}@{parts[0]}:{parts[1]}" 234 | connector = ProxyConnector.from_url(proxy_url) 235 | elif len(parts) == 3 and '@' in proxy: # user:pass@host:port 236 | connector = ProxyConnector.from_url(f"socks5://{proxy}") 237 | else: 238 | return f"⚠ {LANG[language]['invalid_proxy'].format(proxy=proxy)}" 239 | async with aiohttp.ClientSession(connector=connector, timeout=aiohttp.ClientTimeout(total=10)) as session: 240 | async with session.get(IP_CHECK_URL, headers=HEADERS) as response: 241 | if response.status == 200: 242 | data = await response.json() 243 | return data.get('ip', LANG[language]['unknown']) 244 | return f"⚠ {LANG[language]['ip_check_failed'].format(error=f'HTTP {response.status}')}" 245 | else: 246 | async with aiohttp.ClientSession(timeout=aiohttp.ClientTimeout(total=10)) as session: 247 | async with session.get(IP_CHECK_URL, headers=HEADERS) as response: 248 | if response.status == 200: 249 | data = await response.json() 250 | return data.get('ip', LANG[language]['unknown']) 251 | return f"⚠ {LANG[language]['ip_check_failed'].format(error=f'HTTP {response.status}')}" 252 | except Exception as e: 253 | return f"⚠ {LANG[language]['ip_check_failed'].format(error=str(e))}" 254 | 255 | # Hàm kết nối Web3 256 | def connect_web3(language: str): 257 | try: 258 | web3 = Web3(Web3.HTTPProvider(NETWORK_URL)) 259 | if not web3.is_connected(): 260 | print_message(f" ✖ {LANG[language]['connect_error']}", Fore.RED) 261 | sys.exit(1) 262 | print_message(f" ✔ {LANG[language]['connect_success']} │ Chain ID: {web3.eth.chain_id}", Fore.GREEN) 263 | return web3 264 | except Exception as e: 265 | print_message(f" ✖ {LANG[language]['web3_error']}: {str(e)}", Fore.RED) 266 | sys.exit(1) 267 | 268 | # Hàm kiểm tra số dư sUSDT 269 | def get_susdt_balance(w3: Web3, address: str) -> float: 270 | try: 271 | contract = w3.eth.contract(address=Web3.to_checksum_address(CONTRACT_ADDRESS), abi=SUSDT_TOKEN_ABI) 272 | decimals = contract.functions.decimals().call() 273 | balance_wei = contract.functions.balanceOf(Web3.to_checksum_address(address)).call() 274 | return balance_wei / (10 ** decimals) 275 | except Exception: 276 | return 0.0 277 | 278 | # Hàm kiểm tra xem ví đã mint sUSDT chưa 279 | def has_minted_susdt(w3: Web3, address: str, language: str = 'en') -> bool: 280 | try: 281 | balance = get_susdt_balance(w3, address) 282 | return balance > 0 # Nếu balance > 0, ví đã mint sUSDT 283 | except Exception as e: 284 | print_message(f" ⚠ {'Không kiểm tra được số dư sUSDT' if language == 'vi' else 'Failed to check sUSDT balance'}: {str(e)}", Fore.YELLOW) 285 | return False 286 | 287 | # Hàm mint sUSDT 288 | async def mint_susdt(web3: Web3, private_key: str, wallet_index: int, proxy: str = None, language: str = 'en') -> Dict: 289 | result = { 290 | 'index': wallet_index, 291 | 'success': False, 292 | 'output': [], 293 | 'address': '', 294 | 'tx_hash': '', 295 | 'gas_used': 0, 296 | 'block_number': 0, 297 | 'new_stt_balance': 0.0, 298 | 'new_susdt_balance': 0.0 299 | } 300 | 301 | def add_output(message: str, color=Fore.YELLOW): 302 | result['output'].append((message, color)) 303 | 304 | try: 305 | account = Account.from_key(private_key) 306 | address = account.address 307 | result['address'] = address 308 | 309 | # Proxy info 310 | public_ip = await get_proxy_ip(proxy, language) 311 | proxy_display = proxy if proxy else LANG[language]['no_proxy'] 312 | add_output(f" 🔄 {LANG[language]['using_proxy'].format(proxy=proxy_display, public_ip=public_ip)}", Fore.CYAN) 313 | 314 | # Kiểm tra xem ví đã mint chưa 315 | if has_minted_susdt(web3, address, language): 316 | add_output(f" ⚠ {LANG[language]['already_minted']}", Fore.YELLOW) 317 | return result 318 | 319 | # Kiểm tra số dư 320 | add_output(f" > {LANG[language]['checking_balance']}", Fore.CYAN) 321 | stt_balance = web3.from_wei(web3.eth.get_balance(address), 'ether') 322 | susdt_balance = get_susdt_balance(web3, address) 323 | add_output(f" - {LANG[language]['balance'].format(stt=stt_balance, susdt=susdt_balance)}", Fore.YELLOW) 324 | if stt_balance < CONFIG['MINIMUM_BALANCE']: 325 | add_output(f" ✖ {LANG[language]['insufficient_balance'].format(required=CONFIG['MINIMUM_BALANCE'])}: {stt_balance:.6f} STT", Fore.RED) 326 | return result 327 | 328 | # Chuẩn bị giao dịch 329 | nonce = web3.eth.get_transaction_count(address) 330 | gas_price = int(web3.eth.gas_price * random.uniform(1.03, 1.1)) # Tăng nhẹ gasPrice 331 | tx = { 332 | 'nonce': nonce, 333 | 'to': Web3.to_checksum_address(CONTRACT_ADDRESS), 334 | 'value': 0, 335 | 'data': MINT_DATA, 336 | 'chainId': CHAIN_ID, 337 | 'gas': 200000, 338 | 'gasPrice': gas_price 339 | } 340 | 341 | # Ước tính gas 342 | add_output(f" > {LANG[language]['estimating_gas']}", Fore.CYAN) 343 | try: 344 | gas_estimate = web3.eth.estimate_gas(tx) 345 | add_output(f" - Gas ước lượng: {gas_estimate}", Fore.YELLOW) 346 | tx['gas'] = gas_estimate + 10000 # Thêm buffer 347 | except Exception as e: 348 | add_output(f" ✖ {LANG[language]['error']}: {LANG[language]['estimating_gas']} failed: {str(e)}", Fore.RED) 349 | return result 350 | 351 | # Ký và gửi giao dịch 352 | add_output(f" > {LANG[language]['sending_tx']}", Fore.CYAN) 353 | signed_tx = account.sign_transaction(tx) 354 | tx_hash = web3.eth.send_raw_transaction(signed_tx.raw_transaction) 355 | result['tx_hash'] = tx_hash.hex() 356 | 357 | add_output(f" ✔ {LANG[language]['success'].replace('Đã mint 1000 sUSDT thành công!', 'Đã gửi giao dịch')}: {EXPLORER_URL}/tx/0x{tx_hash.hex()}", Fore.GREEN) 358 | 359 | # Chờ xác nhận 360 | receipt = await asyncio.get_event_loop().run_in_executor(None, lambda: web3.eth.wait_for_transaction_receipt(tx_hash, timeout=180)) 361 | if receipt.status == 1: 362 | new_stt_balance = web3.from_wei(web3.eth.get_balance(address), 'ether') 363 | new_susdt_balance = get_susdt_balance(web3, address) 364 | result['success'] = True 365 | result['gas_used'] = receipt['gasUsed'] 366 | result['block_number'] = receipt['blockNumber'] 367 | result['new_stt_balance'] = new_stt_balance 368 | result['new_susdt_balance'] = new_susdt_balance 369 | add_output(f" ✔ {LANG[language]['success']}", Fore.GREEN) 370 | add_output(f" - {LANG[language]['address']}: {address}", Fore.YELLOW) 371 | add_output(f" - {LANG[language]['amount']}", Fore.YELLOW) 372 | add_output(f" - {LANG[language]['gas']}: {receipt['gasUsed']}", Fore.YELLOW) 373 | add_output(f" - {LANG[language]['block']}: {receipt['blockNumber']}", Fore.YELLOW) 374 | add_output(f" - {LANG[language]['balance'].format(stt=new_stt_balance, susdt=new_susdt_balance)}", Fore.YELLOW) 375 | else: 376 | add_output(f" ✖ {LANG[language]['failure']}", Fore.RED) 377 | return result 378 | 379 | except Exception as e: 380 | add_output(f" ✖ {LANG[language]['failure']}: {str(e)}", Fore.RED) 381 | return result 382 | 383 | # Hàm xử lý từng ví 384 | async def process_wallet(profile_num: int, private_key: str, proxy: str, w3: Web3, language: str) -> Dict: 385 | return await mint_susdt(w3, private_key, profile_num, proxy, language) 386 | 387 | # Hàm chính 388 | async def run_mintsusdt(language: str = 'vi'): 389 | print_border(LANG[language]['title'], Fore.CYAN) 390 | 391 | private_keys = load_private_keys('pvkey.txt', language) 392 | proxies = load_proxies('proxies.txt', language) 393 | print_message(f" ℹ {LANG[language]['info']}: {LANG[language]['found']} {len(private_keys)} {LANG[language]['wallets']}", Fore.YELLOW) 394 | print() 395 | 396 | w3 = connect_web3(language) 397 | 398 | successful_mints = 0 399 | total_wallets = len(private_keys) 400 | CONFIG['TOTAL_WALLETS'] = total_wallets 401 | CONFIG['MAX_CONCURRENCY'] = min(CONFIG['MAX_CONCURRENCY'], total_wallets) 402 | 403 | if SHUFFLE_WALLETS: 404 | random.shuffle(private_keys) 405 | 406 | semaphore = asyncio.Semaphore(CONFIG['MAX_CONCURRENCY']) 407 | current_index = 0 408 | 409 | async def limited_task(profile_num, private_key, proxy): 410 | nonlocal current_index, successful_mints 411 | async with semaphore: 412 | current_index += 1 413 | result = await process_wallet(profile_num, private_key, proxy, w3, language) 414 | # Print header, results, and separator after task completes 415 | print_border( 416 | f"{LANG[language]['processing_wallet']} {profile_num} ({current_index}/{total_wallets})", 417 | Fore.MAGENTA 418 | ) 419 | for message, color in result['output']: 420 | print_message(message, color) 421 | print_separator(Fore.GREEN if result['success'] else Fore.RED) 422 | if result['success']: 423 | successful_mints += 1 424 | # Add delay after processing each wallet (except the last one) 425 | if current_index < total_wallets: 426 | delay = random.uniform(MINT_SUSDT_SLEEP_RANGE[0], MINT_SUSDT_SLEEP_RANGE[1]) 427 | print_message(f" ℹ {LANG[language]['pausing']} {delay:.2f} {LANG[language]['seconds']}", Fore.YELLOW) 428 | await asyncio.sleep(delay) 429 | 430 | # Chạy song song 431 | tasks = [] 432 | for _, (profile_num, private_key) in enumerate(private_keys): 433 | proxy = proxies[len(tasks) % len(proxies)] if proxies else None 434 | tasks.append(limited_task(profile_num, private_key, proxy)) 435 | 436 | await asyncio.gather(*tasks, return_exceptions=True) 437 | 438 | print_border( 439 | f"{LANG[language]['completed'].format(successful=successful_mints, total=total_wallets)}", 440 | Fore.GREEN 441 | ) 442 | 443 | if __name__ == "__main__": 444 | asyncio.run(run_mintsusdt('vi')) 445 | -------------------------------------------------------------------------------- /scripts/sopixel.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import asyncio 4 | import random 5 | import time 6 | from web3 import Web3 7 | from eth_account import Account 8 | from colorama import init, Fore, Style 9 | import aiohttp 10 | from aiohttp_socks import ProxyConnector 11 | from typing import List 12 | 13 | # Initialize colorama 14 | init(autoreset=True) 15 | 16 | # Border width 17 | BORDER_WIDTH = 80 18 | 19 | # Constants 20 | NETWORK_URL = "https://dream-rpc.somnia.network" 21 | CHAIN_ID = 50312 22 | EXPLORER_URL = "https://shannon-explorer.somnia.network/tx/0x" 23 | CONTRACT_ADDRESS = Web3.to_checksum_address("0x496eF0E9944ff8c83fa74FeB580f2FB581ecFfFd") 24 | IP_CHECK_URL = "https://api.ipify.org?format=json" 25 | MAX_WAIT_TIME = 180 # Timeout 3 minutes 26 | HEADERS = { 27 | "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36", 28 | "Accept": "application/json, text/plain, */*", 29 | "Accept-Language": "en-GB,en-US;q=0.9,en;q=0.8", 30 | } 31 | CONFIG = { 32 | "PAUSE_BETWEEN_ATTEMPTS": [10, 30], 33 | "PAUSE_BETWEEN_PIXELS": [20, 40], 34 | "MAX_CONCURRENCY": 5, 35 | "MAX_RETRIES": 3, 36 | "MINIMUM_BALANCE": 0.001, # WSTT 37 | "PIXEL_VALUE": 0.01, # 0.01 WSTT per pixel 38 | "DEFAULT_GAS": 300000 39 | } 40 | 41 | # Contract ABI 42 | CONTRACT_ABI = [ 43 | { 44 | "inputs": [ 45 | {"internalType": "uint256", "name": "x", "type": "uint256"}, 46 | {"internalType": "uint256", "name": "y", "type": "uint256"}, 47 | {"internalType": "uint24", "name": "color", "type": "uint24"} 48 | ], 49 | "name": "colorPixel", 50 | "outputs": [], 51 | "stateMutability": "payable", 52 | "type": "function" 53 | } 54 | ] 55 | 56 | # Bilingual vocabulary 57 | LANG = { 58 | 'vi': { 59 | 'title': '✨ SOPIXEL - SOMNIA TESTNET ✨', 60 | 'info': 'Thông tin', 61 | 'found': 'Tìm thấy', 62 | 'wallets': 'ví', 63 | 'processing_wallets': '⚙ ĐANG XỬ LÝ {count} VÍ', 64 | 'checking_balance': 'Đang kiểm tra số dư...', 65 | 'insufficient_balance': 'Số dư không đủ: {balance:.6f} WSTT (cần ít nhất {required:.6f})', 66 | 'preparing_tx': 'Đang chuẩn bị giao dịch...', 67 | 'estimating_gas': 'Đang ước lượng gas...', 68 | 'sending_tx': 'Đang gửi giao dịch...', 69 | 'success_pixel': '✅ Tô màu pixel thành công!', 70 | 'failure': '❌ Tô màu pixel thất bại', 71 | 'address': 'Địa chỉ ví', 72 | 'pixel': 'Pixel', 73 | 'color': 'Màu sắc', 74 | 'gas': 'Gas', 75 | 'block': 'Khối', 76 | 'balance': 'Số dư WSTT', 77 | 'balance_info': 'Số dư', 78 | 'pausing': 'Tạm nghỉ', 79 | 'seconds': 'giây', 80 | 'completed': '🏁 HOÀN THÀNH: {successful}/{total} PIXEL THÀNH CÔNG', 81 | 'error': 'Lỗi', 82 | 'connect_success': '✅ Thành công: Đã kết nối với mạng Somnia Testnet', 83 | 'connect_error': '❌ Không thể kết nối với RPC', 84 | 'web3_error': '❌ Kết nối Web3 thất bại', 85 | 'pvkey_not_found': '❌ Không tìm thấy tệp pvkey.txt', 86 | 'pvkey_empty': '❌ Không tìm thấy khóa riêng hợp lệ', 87 | 'pvkey_error': '❌ Không thể đọc pvkey.txt', 88 | 'invalid_key': 'không hợp lệ, đã bỏ qua', 89 | 'warning_line': 'Cảnh báo: Dòng', 90 | 'gas_estimation_failed': 'Không thể ước tính gas: {error}', 91 | 'default_gas_used': 'Sử dụng gas mặc định: {gas}', 92 | 'tx_rejected': '⚠ Giao dịch bị từ chối bởi hợp đồng hoặc mạng', 93 | 'pixel_count_prompt': '⚛️ Nhập số lượng pixel cần tô màu cho mỗi ví: ', 94 | 'invalid_pixel_count': '❌ Số lượng pixel phải là số nguyên dương', 95 | 'found_proxies': 'Tìm thấy {count} proxy trong proxies.txt', 96 | 'no_proxies': 'Không tìm thấy proxy trong proxies.txt', 97 | 'using_proxy': '🔄 Sử dụng Proxy - [{proxy}] với IP công khai - [{public_ip}]', 98 | 'no_proxy': 'Không có proxy', 99 | 'unknown': 'Không xác định', 100 | 'invalid_proxy': '⚠ Proxy không hợp lệ hoặc không hoạt động: {proxy}', 101 | 'ip_check_failed': '⚠ Không thể kiểm tra IP công khai: {error}', 102 | }, 103 | 'en': { 104 | 'title': '✨ SOPIXEL - SOMNIA TESTNET ✨', 105 | 'info': 'Info', 106 | 'found': 'Found', 107 | 'wallets': 'wallets', 108 | 'processing_wallets': '⚙ PROCESSING {count} WALLETS', 109 | 'checking_balance': 'Checking balance...', 110 | 'insufficient_balance': 'Insufficient balance: {balance:.6f} WSTT (need at least {required:.6f})', 111 | 'preparing_tx': 'Preparing transaction...', 112 | 'estimating_gas': 'Estimating gas...', 113 | 'sending_tx': 'Sending transaction...', 114 | 'success_pixel': '✅ Successfully colored pixel!', 115 | 'failure': '❌ Failed to color pixel', 116 | 'address': 'Wallet address', 117 | 'pixel': 'Pixel', 118 | 'color': 'Color', 119 | 'gas': 'Gas', 120 | 'block': 'Block', 121 | 'balance': 'WSTT Balance', 122 | 'balance_info': 'Balance', 123 | 'pausing': 'Pausing', 124 | 'seconds': 'seconds', 125 | 'completed': '🏁 COMPLETED: {successful}/{total} PIXELS SUCCESSFUL', 126 | 'error': 'Error', 127 | 'connect_success': '✅ Success: Connected to Somnia Testnet', 128 | 'connect_error': '❌ Failed to connect to RPC', 129 | 'web3_error': '❌ Web3 connection failed', 130 | 'pvkey_not_found': '❌ pvkey.txt file not found', 131 | 'pvkey_empty': '❌ No valid private keys found', 132 | 'pvkey_error': '❌ Failed to read pvkey.txt', 133 | 'invalid_key': 'is invalid, skipped', 134 | 'warning_line': 'Warning: Line', 135 | 'gas_estimation_failed': 'Failed to estimate gas: {error}', 136 | 'default_gas_used': 'Using default gas: {gas}', 137 | 'tx_rejected': '⚠ Transaction rejected by contract or network', 138 | 'pixel_count_prompt': '⚛️ Enter the number of pixels to color per wallet: ', 139 | 'invalid_pixel_count': '❌ Pixel count must be a positive integer', 140 | 'found_proxies': 'Found {count} proxies in proxies.txt', 141 | 'no_proxies': 'No proxies found in proxies.txt', 142 | 'using_proxy': '🔄 Using Proxy - [{proxy}] with Public IP - [{public_ip}]', 143 | 'no_proxy': 'None', 144 | 'unknown': 'Unknown', 145 | 'invalid_proxy': '⚠ Invalid or unresponsive proxy: {proxy}', 146 | 'ip_check_failed': '⚠ Failed to check public IP: {error}', 147 | } 148 | } 149 | 150 | # Display functions 151 | def print_border(text: str, color=Fore.CYAN, width=BORDER_WIDTH): 152 | text = text.strip() 153 | if len(text) > width - 4: 154 | text = text[:width - 7] + "..." 155 | padded_text = f" {text} ".center(width - 2) 156 | print(f"{color}┌{'─' * (width - 2)}┐{Style.RESET_ALL}") 157 | print(f"{color}│{padded_text}│{Style.RESET_ALL}") 158 | print(f"{color}└{'─' * (width - 2)}┘{Style.RESET_ALL}") 159 | 160 | def print_separator(color=Fore.MAGENTA): 161 | print(f"{color}{'═' * BORDER_WIDTH}{Style.RESET_ALL}") 162 | 163 | def print_message(message: str, color=Fore.YELLOW): 164 | print(f"{color}{message}{Style.RESET_ALL}") 165 | 166 | def print_wallets_summary(count: int, language: str = 'en'): 167 | print_border( 168 | LANG[language]['processing_wallets'].format(count=count), 169 | Fore.MAGENTA 170 | ) 171 | print() 172 | 173 | def display_all_wallets_balances(w3: Web3, private_keys: List[tuple], language: str = 'en'): 174 | print_border(LANG[language]['balance_info'], Fore.CYAN) 175 | print(f"{Fore.CYAN} Wallet | {LANG[language]['balance']:<10}{Style.RESET_ALL}") 176 | print(f"{Fore.CYAN} {'-' * 6} | {'-' * 10}{Style.RESET_ALL}") 177 | 178 | for i, (profile_num, key) in enumerate(private_keys, 1): 179 | address = Account.from_key(key).address 180 | balance = check_balance(w3, address, language) 181 | print(f"{Fore.YELLOW} {i:<6} | {balance:>10.6f}{Style.RESET_ALL}") 182 | 183 | print() 184 | 185 | # Utility functions 186 | def is_valid_private_key(key: str) -> bool: 187 | key = key.strip() 188 | if not key.startswith('0x'): 189 | key = '0x' + key 190 | try: 191 | bytes.fromhex(key.replace('0x', '')) 192 | return len(key) == 66 193 | except ValueError: 194 | return False 195 | 196 | def load_private_keys(file_path: str = "pvkey.txt", language: str = 'en') -> list: 197 | try: 198 | if not os.path.exists(file_path): 199 | print(f"{Fore.RED} ✖ {LANG[language]['pvkey_not_found']}{Style.RESET_ALL}") 200 | with open(file_path, 'w') as f: 201 | f.write("# Add private keys here, one per line\n# Example: 0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef\n") 202 | sys.exit(1) 203 | 204 | valid_keys = [] 205 | with open(file_path, 'r') as f: 206 | for i, line in enumerate(f, 1): 207 | key = line.strip() 208 | if key and not key.startswith('#'): 209 | if is_valid_private_key(key): 210 | if not key.startswith('0x'): 211 | key = '0x' + key 212 | valid_keys.append((i, key)) 213 | else: 214 | print(f"{Fore.YELLOW} ⚠ {LANG[language]['warning_line']} {i} {LANG[language]['invalid_key']}: {key}{Style.RESET_ALL}") 215 | 216 | if not valid_keys: 217 | print(f"{Fore.RED} ✖ {LANG[language]['pvkey_empty']}{Style.RESET_ALL}") 218 | sys.exit(1) 219 | 220 | return valid_keys 221 | except Exception as e: 222 | print(f"{Fore.RED} ✖ {LANG[language]['pvkey_error']}: {str(e)}{Style.RESET_ALL}") 223 | sys.exit(1) 224 | 225 | def load_proxies(file_path: str = "proxies.txt", language: str = 'en') -> list: 226 | try: 227 | if not os.path.exists(file_path): 228 | print(f"{Fore.YELLOW} ⚠ {LANG[language]['no_proxies']}. Dùng không proxy.{Style.RESET_ALL}") 229 | with open(file_path, 'w') as f: 230 | f.write("# Thêm proxy vào đây, mỗi proxy trên một dòng\n# Ví dụ: socks5://user:pass@host:port hoặc http://host:port\n") 231 | return [] 232 | 233 | proxies = [] 234 | with open(file_path, 'r') as f: 235 | for line in f: 236 | proxy = line.strip() 237 | if proxy and not line.startswith('#'): 238 | proxies.append(proxy) 239 | 240 | if not proxies: 241 | print(f"{Fore.YELLOW} ⚠ {LANG[language]['no_proxies']}. Dùng không proxy.{Style.RESET_ALL}") 242 | return [] 243 | 244 | print(f"{Fore.YELLOW} ℹ {LANG[language]['found_proxies'].format(count=len(proxies))}{Style.RESET_ALL}") 245 | return proxies 246 | except Exception as e: 247 | print(f"{Fore.RED} ✖ {LANG[language]['error']}: {str(e)}{Style.RESET_ALL}") 248 | return [] 249 | 250 | async def get_proxy_ip(proxy: str = None, language: str = 'en') -> str: 251 | try: 252 | if proxy: 253 | if proxy.startswith(('socks5://', 'socks4://', 'http://', 'https://')): 254 | connector = ProxyConnector.from_url(proxy) 255 | else: 256 | parts = proxy.split(':') 257 | if len(parts) == 4: # host:port:user:pass 258 | proxy_url = f"socks5://{parts[2]}:{parts[3]}@{parts[0]}:{parts[1]}" 259 | connector = ProxyConnector.from_url(proxy_url) 260 | elif len(parts) == 3 and '@' in proxy: # user:pass@host:port 261 | connector = ProxyConnector.from_url(f"socks5://{proxy}") 262 | else: 263 | print(f"{Fore.YELLOW} ⚠ {LANG[language]['invalid_proxy'].format(proxy=proxy)}{Style.RESET_ALL}") 264 | return LANG[language]['unknown'] 265 | async with aiohttp.ClientSession(connector=connector, timeout=aiohttp.ClientTimeout(total=10)) as session: 266 | async with session.get(IP_CHECK_URL, headers=HEADERS) as response: 267 | if response.status == 200: 268 | data = await response.json() 269 | return data.get('ip', LANG[language]['unknown']) 270 | print(f"{Fore.YELLOW} ⚠ {LANG[language]['ip_check_failed'].format(error=f'HTTP {response.status}')}{Style.RESET_ALL}") 271 | return LANG[language]['unknown'] 272 | else: 273 | async with aiohttp.ClientSession(timeout=aiohttp.ClientTimeout(total=10)) as session: 274 | async with session.get(IP_CHECK_URL, headers=HEADERS) as response: 275 | if response.status == 200: 276 | data = await response.json() 277 | return data.get('ip', LANG[language]['unknown']) 278 | print(f"{Fore.YELLOW} ⚠ {LANG[language]['ip_check_failed'].format(error=f'HTTP {response.status}')}{Style.RESET_ALL}") 279 | return LANG[language]['unknown'] 280 | except Exception as e: 281 | print(f"{Fore.YELLOW} ⚠ {LANG[language]['ip_check_failed'].format(error=str(e))}{Style.RESET_ALL}") 282 | return LANG[language]['unknown'] 283 | 284 | def connect_web3(language: str = 'en'): 285 | try: 286 | w3 = Web3(Web3.HTTPProvider(NETWORK_URL)) 287 | if not w3.is_connected(): 288 | print(f"{Fore.RED} ✖ {LANG[language]['connect_error']}{Style.RESET_ALL}") 289 | sys.exit(1) 290 | print(f"{Fore.GREEN} ✔ {LANG[language]['connect_success']} │ Chain ID: {w3.eth.chain_id}{Style.RESET_ALL}") 291 | return w3 292 | except Exception as e: 293 | print(f"{Fore.RED} ✖ {LANG[language]['web3_error']}: {str(e)}{Style.RESET_ALL}") 294 | sys.exit(1) 295 | 296 | def check_balance(w3: Web3, address: str, language: str = 'en') -> float: 297 | try: 298 | balance = w3.eth.get_balance(address) 299 | return float(w3.from_wei(balance, 'ether')) 300 | except Exception as e: 301 | print(f"{Fore.YELLOW} ⚠ {LANG[language]['error']}: {str(e)}{Style.RESET_ALL}") 302 | return -1 303 | 304 | async def color_pixel(w3: Web3, private_key: str, wallet_index: int, pixel_count: int, proxy: str = None, language: str = 'en'): 305 | account = Account.from_key(private_key) 306 | sender_address = account.address 307 | contract = w3.eth.contract(address=CONTRACT_ADDRESS, abi=CONTRACT_ABI) 308 | successful_pixels = 0 309 | 310 | nonce = w3.eth.get_transaction_count(sender_address, 'pending') 311 | 312 | for i in range(pixel_count): 313 | print_border(f"Pixel {i+1}/{pixel_count} for Wallet {wallet_index}", Fore.YELLOW) 314 | print(f"{Fore.CYAN} > {LANG[language]['checking_balance']}{Style.RESET_ALL}") 315 | 316 | public_ip = await get_proxy_ip(proxy, language) 317 | proxy_display = proxy if proxy else LANG[language]['no_proxy'] 318 | print(f"{Fore.CYAN} 🔄 {LANG[language]['using_proxy'].format(proxy=proxy_display, public_ip=public_ip)}{Style.RESET_ALL}") 319 | 320 | balance = check_balance(w3, sender_address, language) 321 | required_balance = CONFIG['PIXEL_VALUE'] * (i + 1) 322 | if balance < required_balance or balance < CONFIG['MINIMUM_BALANCE']: 323 | print(f"{Fore.RED} ✖ {LANG[language]['insufficient_balance'].format(balance=balance, required=max(required_balance, CONFIG['MINIMUM_BALANCE']))}{Style.RESET_ALL}") 324 | break 325 | 326 | print(f"{Fore.CYAN} > {LANG[language]['preparing_tx']}{Style.RESET_ALL}") 327 | x = random.randint(0, 1023) 328 | y = random.randint(0, 1023) 329 | color = random.randint(0, 0xFFFFFF) 330 | 331 | try: 332 | latest_block = await asyncio.get_event_loop().run_in_executor(None, lambda: w3.eth.get_block('latest')) 333 | base_fee = latest_block.get('baseFeePerGas', w3.to_wei('1', 'gwei')) 334 | priority_fee = w3.to_wei('2', 'gwei') 335 | if base_fee > w3.to_wei('20', 'gwei'): 336 | priority_fee = w3.to_wei('3', 'gwei') 337 | if base_fee > w3.to_wei('50', 'gwei'): 338 | priority_fee = w3.to_wei('5', 'gwei') 339 | if base_fee > w3.to_wei('100', 'gwei'): 340 | priority_fee = w3.to_wei('8', 'gwei') 341 | max_fee = base_fee + priority_fee + (base_fee // 5) 342 | 343 | tx_params = contract.functions.colorPixel(x, y, color).build_transaction({ 344 | 'nonce': nonce, 345 | 'from': sender_address, 346 | 'value': w3.to_wei(CONFIG['PIXEL_VALUE'], 'ether'), 347 | 'chainId': CHAIN_ID, 348 | 'maxFeePerGas': max_fee, 349 | 'maxPriorityFeePerGas': priority_fee 350 | }) 351 | 352 | print(f"{Fore.CYAN} > {LANG[language]['estimating_gas']}{Style.RESET_ALL}") 353 | try: 354 | estimated_gas = w3.eth.estimate_gas(tx_params) 355 | tx_params['gas'] = int(estimated_gas * 1.2) 356 | print(f"{Fore.YELLOW} Gas estimated: {tx_params['gas']}{Style.RESET_ALL}") 357 | except Exception as e: 358 | tx_params['gas'] = CONFIG['DEFAULT_GAS'] 359 | print(f"{Fore.YELLOW} {LANG[language]['gas_estimation_failed'].format(error=str(e))}. {LANG[language]['default_gas_used'].format(gas=CONFIG['DEFAULT_GAS'])}{Style.RESET_ALL}") 360 | 361 | print(f"{Fore.CYAN} > {LANG[language]['sending_tx']}{Style.RESET_ALL}") 362 | for attempt in range(CONFIG['MAX_RETRIES']): 363 | try: 364 | signed_tx = w3.eth.account.sign_transaction(tx_params, private_key) 365 | tx_hash = w3.eth.send_raw_transaction(signed_tx.raw_transaction) 366 | tx_link = f"{EXPLORER_URL}{tx_hash.hex()}" 367 | 368 | receipt = await asyncio.get_event_loop().run_in_executor(None, lambda: w3.eth.wait_for_transaction_receipt(tx_hash, timeout=MAX_WAIT_TIME)) 369 | 370 | if receipt.status == 1: 371 | successful_pixels += 1 372 | balance_after = check_balance(w3, sender_address, language) 373 | print(f"{Fore.GREEN} ✔ {LANG[language]['success_pixel']} │ Tx: {tx_link}{Style.RESET_ALL}") 374 | print(f"{Fore.YELLOW} {LANG[language]['address']:<12}: {sender_address}{Style.RESET_ALL}") 375 | print(f"{Fore.YELLOW} {LANG[language]['pixel']:<12}: x={x}, y={y}{Style.RESET_ALL}") 376 | print(f"{Fore.YELLOW} {LANG[language]['color']:<12}: #{hex(color)[2:].zfill(6).upper()}{Style.RESET_ALL}") 377 | print(f"{Fore.YELLOW} {LANG[language]['gas']:<12}: {receipt['gasUsed']}{Style.RESET_ALL}") 378 | print(f"{Fore.YELLOW} {LANG[language]['block']:<12}: {receipt['blockNumber']}{Style.RESET_ALL}") 379 | print(f"{Fore.YELLOW} {LANG[language]['balance']:<12}: {balance_after:.6f}{Style.RESET_ALL}") 380 | nonce += 1 381 | break 382 | else: 383 | print(f"{Fore.RED} ✖ {LANG[language]['failure']} │ Tx: {tx_link}{Style.RESET_ALL}") 384 | print(f"{Fore.RED} {LANG[language]['tx_rejected']}{Style.RESET_ALL}") 385 | break 386 | 387 | except Exception as e: 388 | if attempt < CONFIG['MAX_RETRIES'] - 1: 389 | delay = random.uniform(5, 15) 390 | print(f"{Fore.RED} ✖ {LANG[language]['failure']}: {str(e)} │ Tx: {tx_link if 'tx_hash' in locals() else 'Chưa gửi'}{Style.RESET_ALL}") 391 | print(f"{Fore.YELLOW} {LANG[language]['pausing']} {delay:.2f} {LANG[language]['seconds']}{Style.RESET_ALL}") 392 | await asyncio.sleep(delay) 393 | continue 394 | print(f"{Fore.RED} ✖ {LANG[language]['failure']}: {str(e)} │ Tx: {tx_link if 'tx_hash' in locals() else 'Chưa gửi'}{Style.RESET_ALL}") 395 | break 396 | 397 | if i < pixel_count - 1: 398 | delay = random.uniform(CONFIG['PAUSE_BETWEEN_PIXELS'][0], CONFIG['PAUSE_BETWEEN_PIXELS'][1]) 399 | print(f"{Fore.YELLOW} {LANG[language]['pausing']} {delay:.2f} {LANG[language]['seconds']}{Style.RESET_ALL}") 400 | await asyncio.sleep(delay) 401 | 402 | except Exception as e: 403 | print(f"{Fore.RED} ✖ {LANG[language]['failure']}: {str(e)}{Style.RESET_ALL}") 404 | break 405 | 406 | return successful_pixels 407 | 408 | async def run_sopixel(language: str = 'vi'): 409 | print() 410 | print_border(LANG[language]['title'], Fore.CYAN) 411 | print() 412 | 413 | proxies = load_proxies('proxies.txt', language) 414 | 415 | private_keys = load_private_keys('pvkey.txt', language) 416 | print(f"{Fore.YELLOW} ℹ {LANG[language]['info']}: {LANG[language]['found']} {len(private_keys)} {LANG[language]['wallets']}{Style.RESET_ALL}") 417 | print() 418 | 419 | if not private_keys: 420 | return 421 | 422 | w3 = connect_web3(language) 423 | print() 424 | 425 | # Display balance table for all wallets 426 | display_all_wallets_balances(w3, private_keys, language) 427 | print_separator() 428 | 429 | # Get pixel count 430 | while True: 431 | print(f"{Fore.CYAN}{LANG[language]['pixel_count_prompt']}{Style.RESET_ALL}") 432 | try: 433 | pixel_count = int(input(f"{Fore.GREEN} > {Style.RESET_ALL}")) 434 | if pixel_count > 0: 435 | break 436 | print(f"{Fore.RED} ✖ {LANG[language]['invalid_pixel_count']}{Style.RESET_ALL}") 437 | except ValueError: 438 | print(f"{Fore.RED} ✖ {LANG[language]['invalid_pixel_count']}{Style.RESET_ALL}") 439 | 440 | print(f"{Fore.GREEN} ℹ {LANG[language]['info']}: Số pixel sẽ tô màu cho mỗi ví: {pixel_count}{Style.RESET_ALL}") 441 | print_separator() 442 | 443 | total_pixels = 0 444 | successful_pixels = 0 445 | 446 | print_wallets_summary(len(private_keys), language) 447 | random.shuffle(private_keys) 448 | 449 | # Process wallets with concurrency 450 | async def process_wallet(index, profile_num, private_key): 451 | nonlocal successful_pixels, total_pixels 452 | proxy = proxies[index % len(proxies)] if proxies else None 453 | 454 | async with semaphore: 455 | pixels = await color_pixel(w3, private_key, profile_num, pixel_count, proxy, language) 456 | successful_pixels += pixels 457 | total_pixels += pixel_count 458 | if index < len(private_keys) - 1: 459 | delay = random.uniform(CONFIG['PAUSE_BETWEEN_ATTEMPTS'][0], CONFIG['PAUSE_BETWEEN_ATTEMPTS'][1]) 460 | print_message(f" ℹ {LANG[language]['pausing']} {delay:.2f} {LANG[language]['seconds']}", Fore.YELLOW) 461 | await asyncio.sleep(delay) 462 | 463 | semaphore = asyncio.Semaphore(CONFIG['MAX_CONCURRENCY']) 464 | tasks = [process_wallet(i, profile_num, key) for i, (profile_num, key) in enumerate(private_keys)] 465 | await asyncio.gather(*tasks, return_exceptions=True) 466 | 467 | print() 468 | print_border(f"{LANG[language]['completed'].format(successful=successful_pixels, total=total_pixels)}", Fore.GREEN) 469 | print() 470 | 471 | if __name__ == "__main__": 472 | asyncio.run(run_sopixel('vi')) 473 | -------------------------------------------------------------------------------- /scripts/mintping.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import asyncio 4 | import random 5 | from web3 import Web3 6 | from eth_account import Account 7 | from colorama import init, Fore, Style 8 | import aiohttp 9 | from aiohttp_socks import ProxyConnector 10 | 11 | # Initialize colorama 12 | init(autoreset=True) 13 | 14 | # Border width 15 | BORDER_WIDTH = 80 16 | 17 | # Constants 18 | NETWORK_URL = "https://dream-rpc.somnia.network" 19 | CHAIN_ID = 50312 20 | EXPLORER_URL = "https://shannon-explorer.somnia.network/tx/0x" 21 | PING_CONTRACT = Web3.to_checksum_address("0x33E7fAB0a8a5da1A923180989bD617c9c2D1C493") 22 | IP_CHECK_URL = "https://api.ipify.org?format=json" 23 | MAX_WAIT_TIME = 180 # Timeout 3 minutes 24 | HEADERS = { 25 | "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36", 26 | "Accept": "application/json, text/plain, */*", 27 | "Accept-Encoding": "gzip, deflate, br", 28 | "Accept-Language": "en-US,en;q=0.9", 29 | } 30 | CONFIG = { 31 | "PAUSE_BETWEEN_ATTEMPTS": [10, 30], 32 | "MAX_CONCURRENCY": 5, 33 | "MAX_RETRIES": 3, 34 | "MINIMUM_BALANCE": 0.001, # STT 35 | "DEFAULT_GAS": 500000 36 | } 37 | 38 | # Token information 39 | TOKENS = [ 40 | {"symbol": "PING", "address": PING_CONTRACT, "amount": 1000, "decimals": 18} 41 | ] 42 | 43 | # PING contract ABI 44 | PING_ABI = [ 45 | { 46 | "inputs": [], 47 | "name": "mint", 48 | "outputs": [], 49 | "stateMutability": "payable", 50 | "type": "function" 51 | }, 52 | { 53 | "inputs": [{"name": "owner", "type": "address"}], 54 | "name": "balanceOf", 55 | "outputs": [{"name": "", "type": "uint256"}], 56 | "stateMutability": "view", 57 | "type": "function" 58 | }, 59 | { 60 | "inputs": [{"name": "account", "type": "address"}], 61 | "name": "isMinter", 62 | "outputs": [{"name": "", "type": "bool"}], 63 | "stateMutability": "view", 64 | "type": "function" 65 | } 66 | ] 67 | 68 | # Bilingual vocabulary 69 | LANG = { 70 | 'vi': { 71 | 'title': '✨ MINT $PING - SOMNIA TESTNET ✨', 72 | 'info': 'ℹ Thông tin', 73 | 'found': 'Tìm thấy', 74 | 'wallets': 'ví', 75 | 'found_proxies': 'Tìm thấy {count} proxy trong proxies.txt', 76 | 'processing_wallets': '⚙ ĐANG XỬ LÝ {count} VÍ', 77 | 'checking_balance': 'Đang kiểm tra số dư...', 78 | 'already_minted': '❌ Ví này đã mint $PING rồi! Vui lòng không thử lại.', 79 | 'insufficient_balance': 'Số dư không đủ (cần ít nhất {required:.6f} STT cho giao dịch)', 80 | 'preparing_tx': 'Đang chuẩn bị giao dịch...', 81 | 'sending_tx': 'Đang gửi giao dịch...', 82 | 'waiting_tx': 'Đang đợi xác nhận giao dịch...', 83 | 'success': '✅ Mint thành công {amount} {symbol}!', 84 | 'failure': '❌ Mint {symbol} thất bại', 85 | 'timeout': '⚠ Giao dịch chưa nhận được receipt sau {timeout} giây, kiểm tra trên explorer...', 86 | 'address': 'Địa chỉ ví', 87 | 'amount': 'Số dư {symbol}', 88 | 'gas': 'Gas', 89 | 'block': 'Khối', 90 | 'balance': 'Số dư STT', 91 | 'balance_info': 'Số dư', 92 | 'pausing': 'Tạm nghỉ', 93 | 'seconds': 'giây', 94 | 'completed': '🏁 HOÀN THÀNH: {successful}/{total} GIAO DỊCH THÀNH CÔNG', 95 | 'error': 'Lỗi', 96 | 'connect_success': '✅ Thành công: Đã kết nối mạng Somnia Testnet', 97 | 'connect_error': '❌ Không thể kết nối với RPC', 98 | 'web3_error': '❌ Kết nối Web3 thất bại', 99 | 'pvkey_not_found': '❌ File pvkey.txt không tồn tại', 100 | 'pvkey_empty': '❌ Không tìm thấy private key hợp lệ', 101 | 'pvkey_error': '❌ Đọc pvkey.txt thất bại', 102 | 'invalid_key': 'không hợp lệ, bỏ qua', 103 | 'warning_line': '⚠ Cảnh báo: Dòng', 104 | 'debugging_tx': 'Đang debug giao dịch...', 105 | 'gas_estimation_failed': '⚠ Không thể ước lượng gas', 106 | 'default_gas_used': 'Sử dụng gas mặc định: {gas}', 107 | 'tx_rejected': '⚠ Giao dịch bị từ chối bởi hợp đồng hoặc mạng', 108 | 'stop_wallet': 'Dừng xử lý ví {wallet}: Quá nhiều giao dịch thất bại liên tiếp', 109 | 'balance_check_failed': '⚠ Không kiểm tra được số dư {symbol}', 110 | 'using_proxy': '🔄 Sử dụng Proxy - [{proxy}] với IP công khai - [{public_ip}]', 111 | 'no_proxy': 'Không có proxy', 112 | 'unknown': 'Không xác định', 113 | 'no_proxies': 'Không tìm thấy proxy trong proxies.txt', 114 | 'invalid_proxy': '⚠ Proxy không hợp lệ hoặc không hoạt động: {proxy}', 115 | 'ip_check_failed': '⚠ Không thể kiểm tra IP công khai: {error}', 116 | 'invalid_contract_data': '⚠ Hợp đồng từ chối giao dịch. Kiểm tra dữ liệu giao dịch hoặc trạng thái hợp đồng tại https://shannon-explorer.somnia.network/address/0x9beaA0016c22B646Ac311Ab171270B0ECf23098F' 117 | }, 118 | 'en': { 119 | 'title': '✨ MINT $PING - SOMNIA TESTNET ✨', 120 | 'info': 'ℹ Info', 121 | 'found': 'Found', 122 | 'wallets': 'wallets', 123 | 'found_proxies': 'Found {count} proxies in proxies.txt', 124 | 'processing_wallets': '⚙ PROCESSING {count} WALLETS', 125 | 'checking_balance': 'Checking balance...', 126 | 'already_minted': '❌ Wallet already minted $PING! Please do not try again.', 127 | 'insufficient_balance': 'Insufficient balance (need at least {required:.6f} STT for transaction)', 128 | 'preparing_tx': 'Preparing transaction...', 129 | 'sending_tx': 'Sending transaction...', 130 | 'waiting_tx': 'Waiting for transaction confirmation...', 131 | 'success': '✅ Successfully minted {amount} {symbol}!', 132 | 'failure': '❌ Failed to mint {symbol}', 133 | 'timeout': '⚠ Transaction receipt not received after {timeout} seconds, check on explorer...', 134 | 'address': 'Wallet address', 135 | 'amount': '{symbol} Balance', 136 | 'gas': 'Gas', 137 | 'block': 'Block', 138 | 'balance': 'STT Balance', 139 | 'balance_info': 'Balance', 140 | 'pausing': 'Pausing', 141 | 'seconds': 'seconds', 142 | 'completed': '🏁 COMPLETED: {successful}/{total} TRANSACTIONS SUCCESSFUL', 143 | 'error': 'Error', 144 | 'connect_success': '✅ Success: Connected to Somnia Testnet', 145 | 'connect_error': '❌ Failed to connect to RPC', 146 | 'web3_error': '❌ Web3 connection failed', 147 | 'pvkey_not_found': '❌ pvkey.txt file not found', 148 | 'pvkey_empty': '❌ No valid private keys found', 149 | 'pvkey_error': '❌ Failed to read pvkey.txt', 150 | 'invalid_key': 'is invalid, skipped', 151 | 'warning_line': '⚠ Warning: Line', 152 | 'debugging_tx': 'Debugging transaction...', 153 | 'gas_estimation_failed': '⚠ Failed to estimate gas', 154 | 'default_gas_used': 'Using default gas: {gas}', 155 | 'tx_rejected': '⚠ Transaction rejected by contract or network', 156 | 'stop_wallet': 'Stopping wallet {wallet}: Too many consecutive failed transactions', 157 | 'balance_check_failed': '⚠ Failed to check {symbol} balance', 158 | 'using_proxy': '🔄 Using Proxy - [{proxy}] with Public IP - [{public_ip}]', 159 | 'no_proxy': 'None', 160 | 'unknown': 'Unknown', 161 | 'no_proxies': 'No proxies found in proxies.txt', 162 | 'invalid_proxy': '⚠ Invalid or unresponsive proxy: {proxy}', 163 | 'ip_check_failed': '⚠ Failed to check public IP: {error}', 164 | 'invalid_contract_data': '⚠ Contract rejected transaction. Check transaction data or contract status at https://shannon-explorer.somnia.network/address/0x9beaA0016c22B646Ac311Ab171270B0ECf23098F' 165 | } 166 | } 167 | 168 | # Display functions 169 | def print_border(text: str, color=Fore.CYAN, width=BORDER_WIDTH): 170 | text = text.strip() 171 | if len(text) > width - 4: 172 | text = text[:width - 7] + "..." 173 | padded_text = f" {text} ".center(width - 2) 174 | print(f"{color}┌{'─' * (width - 2)}┐{Style.RESET_ALL}") 175 | print(f"{color}│{padded_text}│{Style.RESET_ALL}") 176 | print(f"{color}└{'─' * (width - 2)}┘{Style.RESET_ALL}") 177 | 178 | def print_separator(color=Fore.MAGENTA): 179 | print(f"{color}{'═' * BORDER_WIDTH}{Style.RESET_ALL}") 180 | 181 | def print_wallets_summary(private_keys: list, language: str = 'en'): 182 | print_border( 183 | LANG[language]['processing_wallets'].format(count=len(private_keys)), 184 | Fore.MAGENTA 185 | ) 186 | print() 187 | 188 | def display_all_wallets_balances(w3: Web3, private_keys: list, language: str = 'en'): 189 | print_border(LANG[language]['balance_info'], Fore.CYAN) 190 | print(f"{Fore.CYAN} Wallet | {'STT':<10} | {'PING':<10}{Style.RESET_ALL}") 191 | print(f"{Fore.CYAN} {'-' * 6} | {'-' * 10} | {'-' * 10}{Style.RESET_ALL}") 192 | 193 | for i, (profile_num, key) in enumerate(private_keys, 1): 194 | address = Account.from_key(key).address 195 | eth_balance = check_balance(w3, address, "native", 18, language) 196 | ping_balance = check_balance(w3, address, TOKENS[0]["address"], TOKENS[0]["decimals"], language) 197 | print(f"{Fore.YELLOW} {i:<6} | {eth_balance:>10.6f} | {ping_balance:>10.6f}{Style.RESET_ALL}") 198 | 199 | print() 200 | 201 | # Utility functions 202 | def is_valid_private_key(key: str) -> bool: 203 | key = key.strip() 204 | if not key.startswith('0x'): 205 | key = '0x' + key 206 | try: 207 | bytes.fromhex(key.replace('0x', '')) 208 | return len(key) == 66 209 | except ValueError: 210 | return False 211 | 212 | def load_private_keys(file_path: str = "pvkey.txt", language: str = 'en') -> list: 213 | try: 214 | if not os.path.exists(file_path): 215 | print(f"{Fore.RED} ✖ {LANG[language]['pvkey_not_found']}{Style.RESET_ALL}") 216 | with open(file_path, 'w') as f: 217 | f.write("# Add private keys here, one per line\n# Example: 0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef\n") 218 | sys.exit(1) 219 | 220 | valid_keys = [] 221 | with open(file_path, 'r') as f: 222 | for i, line in enumerate(f, 1): 223 | key = line.strip() 224 | if key and not key.startswith('#'): 225 | if is_valid_private_key(key): 226 | if not key.startswith('0x'): 227 | key = '0x' + key 228 | valid_keys.append((i, key)) 229 | else: 230 | print(f"{Fore.YELLOW} ⚠ {LANG[language]['warning_line']} {i} {LANG[language]['invalid_key']}: {key[:10]}...{Style.RESET_ALL}") 231 | 232 | if not valid_keys: 233 | print(f"{Fore.RED} ✖ {LANG[language]['pvkey_empty']}{Style.RESET_ALL}") 234 | sys.exit(1) 235 | 236 | return valid_keys 237 | except Exception as e: 238 | print(f"{Fore.RED} ✖ {LANG[language]['pvkey_error']}: {str(e)}{Style.RESET_ALL}") 239 | sys.exit(1) 240 | 241 | def load_proxies(file_path: str = "proxies.txt", language: str = 'en') -> list: 242 | try: 243 | if not os.path.exists(file_path): 244 | print(f"{Fore.YELLOW} ⚠ {LANG[language]['no_proxies']}. Using no proxy.{Style.RESET_ALL}") 245 | with open(file_path, 'w') as f: 246 | f.write("# Add proxies here, one per line\n# Example: socks5://user:pass@host:port or http://host:port\n") 247 | return [] 248 | 249 | proxies = [] 250 | with open(file_path, 'r') as f: 251 | for line in f: 252 | proxy = line.strip() 253 | if proxy and not line.startswith('#'): 254 | proxies.append(proxy) 255 | 256 | if not proxies: 257 | print(f"{Fore.YELLOW} ⚠ {LANG[language]['no_proxies']}. Using no proxy.{Style.RESET_ALL}") 258 | return [] 259 | 260 | print(f"{Fore.YELLOW} ℹ {LANG[language]['found_proxies'].format(count=len(proxies))}{Style.RESET_ALL}") 261 | return proxies 262 | except Exception as e: 263 | print(f"{Fore.RED} ✖ {LANG[language]['error']}: {str(e)}{Style.RESET_ALL}") 264 | return [] 265 | 266 | async def get_proxy_ip(proxy: str = None, language: str = 'en') -> str: 267 | try: 268 | if proxy: 269 | if proxy.startswith(('socks5://', 'socks4://', 'http://', 'https://')): 270 | connector = ProxyConnector.from_url(proxy) 271 | else: 272 | parts = proxy.split(':') 273 | if len(parts) == 4: # host:port:user:pass 274 | proxy_url = f"socks5://{parts[2]}:{parts[3]}@{parts[0]}:{parts[1]}" 275 | connector = ProxyConnector.from_url(proxy_url) 276 | elif len(parts) == 3 and '@' in proxy: # user:pass@host:port 277 | connector = ProxyConnector.from_url(f"socks5://{proxy}") 278 | else: 279 | print(f"{Fore.YELLOW} ⚠ {LANG[language]['invalid_proxy'].format(proxy=proxy)}{Style.RESET_ALL}") 280 | return LANG[language]['unknown'] 281 | async with aiohttp.ClientSession(connector=connector, timeout=aiohttp.ClientTimeout(total=10)) as session: 282 | async with session.get(IP_CHECK_URL, headers=HEADERS) as response: 283 | if response.status == 200: 284 | data = await response.json() 285 | return data.get('ip', LANG[language]['unknown']) 286 | print(f"{Fore.YELLOW} ⚠ {LANG[language]['ip_check_failed'].format(error=f'HTTP {response.status}')}{Style.RESET_ALL}") 287 | return LANG[language]['unknown'] 288 | else: 289 | async with aiohttp.ClientSession(timeout=aiohttp.ClientTimeout(total=10)) as session: 290 | async with session.get(IP_CHECK_URL, headers=HEADERS) as response: 291 | if response.status == 200: 292 | data = await response.json() 293 | return data.get('ip', LANG[language]['unknown']) 294 | print(f"{Fore.YELLOW} ⚠ {LANG[language]['ip_check_failed'].format(error=f'HTTP {response.status}')}{Style.RESET_ALL}") 295 | return LANG[language]['unknown'] 296 | except Exception as e: 297 | print(f"{Fore.YELLOW} ⚠ {LANG[language]['ip_check_failed'].format(error=str(e))}{Style.RESET_ALL}") 298 | return LANG[language]['unknown'] 299 | 300 | def connect_web3(language: str = 'en'): 301 | try: 302 | w3 = Web3(Web3.HTTPProvider(NETWORK_URL)) 303 | if not w3.is_connected(): 304 | print(f"{Fore.RED} ✖ {LANG[language]['connect_error']}{Style.RESET_ALL}") 305 | sys.exit(1) 306 | print(f"{Fore.GREEN} ✔ {LANG[language]['connect_success']} │ Chain ID: {w3.eth.chain_id}{Style.RESET_ALL}") 307 | return w3 308 | except Exception as e: 309 | print(f"{Fore.RED} ✖ {LANG[language]['web3_error']}: {str(e)}{Style.RESET_ALL}") 310 | sys.exit(1) 311 | 312 | def check_balance(w3: Web3, address: str, token_address: str, decimals: int, language: str = 'en') -> float: 313 | if token_address == "native": 314 | try: 315 | balance = w3.eth.get_balance(address) 316 | return float(w3.from_wei(balance, 'ether')) 317 | except Exception as e: 318 | print(f"{Fore.YELLOW} ⚠ {LANG[language]['balance_check_failed'].format(symbol='STT')}: {str(e)}{Style.RESET_ALL}") 319 | return -1 320 | else: 321 | contract = w3.eth.contract(address=Web3.to_checksum_address(token_address), abi=PING_ABI) 322 | try: 323 | balance = contract.functions.balanceOf(address).call() 324 | return balance / (10 ** decimals) 325 | except Exception as e: 326 | print(f"{Fore.YELLOW} ⚠ {LANG[language]['balance_check_failed'].format(symbol='PING')}: {str(e)}{Style.RESET_ALL}") 327 | return -1 328 | 329 | def debug_transaction(w3: Web3, tx_params: dict, language: str = 'en') -> str: 330 | try: 331 | print(f"{Fore.CYAN} > {LANG[language]['debugging_tx']}{Style.RESET_ALL}") 332 | w3.eth.call(tx_params) 333 | return "Transaction expected to succeed" 334 | except Exception as e: 335 | return f"Transaction expected to fail: {str(e)}" 336 | 337 | async def mint_token(w3: Web3, private_key: str, wallet_index: int, token: dict, proxy: str = None, language: str = 'en'): 338 | account = Account.from_key(private_key) 339 | sender_address = account.address 340 | contract = w3.eth.contract(address=Web3.to_checksum_address(token['address']), abi=PING_ABI) 341 | 342 | # Check if wallet has already minted 343 | token_balance = check_balance(w3, sender_address, token['address'], token['decimals'], language) 344 | if token_balance > 0: 345 | print(f"{Fore.RED} ✖ {LANG[language]['already_minted']}{Style.RESET_ALL}") 346 | print(f"{Fore.YELLOW} - {LANG[language]['amount'].format(symbol=token['symbol'])}: {token_balance:.6f} {token['symbol']}{Style.RESET_ALL}") 347 | return False 348 | 349 | for attempt in range(CONFIG['MAX_RETRIES']): 350 | try: 351 | # Display proxy info 352 | public_ip = await get_proxy_ip(proxy, language) 353 | proxy_display = proxy if proxy else LANG[language]['no_proxy'] 354 | print(f"{Fore.CYAN} 🔄 {LANG[language]['using_proxy'].format(proxy=proxy_display, public_ip=public_ip)}{Style.RESET_ALL}") 355 | 356 | print(f"{Fore.CYAN} > {LANG[language]['checking_balance']}{Style.RESET_ALL}") 357 | eth_balance = float(w3.from_wei(w3.eth.get_balance(sender_address), 'ether')) 358 | print(f"{Fore.YELLOW} - {LANG[language]['balance']}: {eth_balance:.6f} STT{Style.RESET_ALL}") 359 | print(f"{Fore.YELLOW} - {LANG[language]['amount'].format(symbol=token['symbol'])}: {token_balance if token_balance >= 0 else 'N/A'} {token['symbol']}{Style.RESET_ALL}") 360 | 361 | if eth_balance < CONFIG['MINIMUM_BALANCE']: 362 | print(f"{Fore.RED} ✖ {LANG[language]['insufficient_balance'].format(required=CONFIG['MINIMUM_BALANCE'])}{Style.RESET_ALL}") 363 | return False 364 | 365 | print(f"{Fore.CYAN} > {LANG[language]['preparing_tx']}{Style.RESET_ALL}") 366 | nonce = w3.eth.get_transaction_count(sender_address, 'pending') 367 | tx_params = contract.functions.mint().build_transaction({ 368 | 'nonce': nonce, 369 | 'from': sender_address, 370 | 'chainId': CHAIN_ID, 371 | 'gasPrice': int(w3.eth.gas_price * random.uniform(1.03, 1.1)), 372 | 'value': 0 373 | }) 374 | 375 | try: 376 | estimated_gas = w3.eth.estimate_gas(tx_params) 377 | gas_limit = int(estimated_gas * 1.2) 378 | tx_params['gas'] = gas_limit 379 | print(f"{Fore.YELLOW} - Gas estimated: {estimated_gas} | Gas limit used: {gas_limit}{Style.RESET_ALL}") 380 | except Exception as e: 381 | tx_params['gas'] = CONFIG['DEFAULT_GAS'] 382 | print(f"{Fore.YELLOW} - {LANG[language]['gas_estimation_failed']}: {str(e)}. {LANG[language]['default_gas_used'].format(gas=CONFIG['DEFAULT_GAS'])}{Style.RESET_ALL}") 383 | 384 | total_required = tx_params['gas'] * tx_params['gasPrice'] 385 | total_required_eth = float(w3.from_wei(total_required, 'ether')) 386 | if eth_balance < total_required_eth: 387 | print(f"{Fore.RED} ✖ {LANG[language]['insufficient_balance'].format(required=total_required_eth)}{Style.RESET_ALL}") 388 | return False 389 | 390 | debug_result = debug_transaction(w3, tx_params, language) 391 | print(f"{Fore.YELLOW} - {debug_result}{Style.RESET_ALL}") 392 | if "fail" in debug_result.lower(): 393 | print(f"{Fore.RED} ✖ {LANG[language]['invalid_contract_data']}{Style.RESET_ALL}") 394 | return False 395 | 396 | print(f"{Fore.CYAN} > {LANG[language]['sending_tx']}{Style.RESET_ALL}") 397 | signed_tx = w3.eth.account.sign_transaction(tx_params, private_key) 398 | tx_hash = w3.eth.send_raw_transaction(signed_tx.raw_transaction) 399 | tx_link = f"{EXPLORER_URL}{tx_hash.hex()}" 400 | 401 | print(f"{Fore.CYAN} > {LANG[language]['waiting_tx']}{Style.RESET_ALL}") 402 | receipt = await wait_for_receipt(w3, tx_hash, MAX_WAIT_TIME, language) 403 | 404 | if receipt is None: 405 | print(f"{Fore.YELLOW} {LANG[language]['timeout'].format(timeout=MAX_WAIT_TIME)}{Style.RESET_ALL}") 406 | print(f"{Fore.YELLOW} - Tx: {tx_link}{Style.RESET_ALL}") 407 | return True 408 | elif receipt.status == 1: 409 | token_balance = check_balance(w3, sender_address, token['address'], token['decimals'], language) 410 | print(f"{Fore.GREEN} ✔ {LANG[language]['success'].format(amount=token['amount'], symbol=token['symbol'])} | Tx: {tx_link}{Style.RESET_ALL}") 411 | print(f"{Fore.YELLOW} - {LANG[language]['address']}: {sender_address}{Style.RESET_ALL}") 412 | print(f"{Fore.YELLOW} - {LANG[language]['gas']}: {receipt['gasUsed']}{Style.RESET_ALL}") 413 | print(f"{Fore.YELLOW} - {LANG[language]['block']}: {receipt['blockNumber']}{Style.RESET_ALL}") 414 | print(f"{Fore.YELLOW} - {LANG[language]['balance']}: {eth_balance:.6f} STT{Style.RESET_ALL}") 415 | print(f"{Fore.YELLOW} - {LANG[language]['amount'].format(symbol=token['symbol'])}: {token_balance if token_balance >= 0 else 'N/A'} {token['symbol']}{Style.RESET_ALL}") 416 | return True 417 | else: 418 | print(f"{Fore.RED} ✖ {LANG[language]['failure'].format(symbol=token['symbol'])} | Tx: {tx_link}{Style.RESET_ALL}") 419 | print(f"{Fore.RED} - {LANG[language]['tx_rejected']}{Style.RESET_ALL}") 420 | return False 421 | 422 | except Exception as e: 423 | if attempt < CONFIG['MAX_RETRIES'] - 1: 424 | delay = random.uniform(5, 15) 425 | print(f"{Fore.RED} ✖ {LANG[language]['failure'].format(symbol=token['symbol'])}: {str(e)} | Tx: {tx_link if 'tx_hash' in locals() else 'Not sent'}{Style.RESET_ALL}") 426 | print(f"{Fore.YELLOW} - {LANG[language]['pausing']} {delay:.2f} {LANG[language]['seconds']}{Style.RESET_ALL}") 427 | await asyncio.sleep(delay) 428 | continue 429 | print(f"{Fore.RED} ✖ {LANG[language]['invalid_contract_data']}: {str(e)} | Tx: {tx_link if 'tx_hash' in locals() else 'Not sent'}{Style.RESET_ALL}") 430 | return False 431 | 432 | async def wait_for_receipt(w3: Web3, tx_hash: str, max_wait_time: int, language: str = 'en'): 433 | start_time = asyncio.get_event_loop().time() 434 | while True: 435 | try: 436 | receipt = w3.eth.get_transaction_receipt(tx_hash) 437 | if receipt is not None: 438 | return receipt 439 | except Exception: 440 | pass 441 | elapsed_time = asyncio.get_event_loop().time() - start_time 442 | if elapsed_time > max_wait_time: 443 | return None 444 | await asyncio.sleep(5) 445 | 446 | async def run_mintping(language: str = 'en'): 447 | print() 448 | print_border(LANG[language]['title'], Fore.CYAN) 449 | print() 450 | 451 | private_keys = load_private_keys('pvkey.txt', language) 452 | proxies = load_proxies('proxies.txt', language) 453 | print(f"{Fore.YELLOW} ℹ {LANG[language]['info']}: {LANG[language]['found']} {len(private_keys)} {LANG[language]['wallets']}{Style.RESET_ALL}") 454 | print() 455 | 456 | if not private_keys: 457 | return 458 | 459 | w3 = connect_web3(language) 460 | print() 461 | 462 | # Display balance table 463 | display_all_wallets_balances(w3, private_keys, language) 464 | print_separator() 465 | 466 | successful_mints = 0 467 | total_mints = len(private_keys) * len(TOKENS) 468 | failed_attempts = 0 469 | CONFIG['TOTAL_WALLETS'] = len(private_keys) 470 | CONFIG['MAX_CONCURRENCY'] = min(CONFIG['MAX_CONCURRENCY'], len(private_keys)) 471 | 472 | print_wallets_summary(private_keys, language) 473 | 474 | random.shuffle(private_keys) 475 | semaphore = asyncio.Semaphore(CONFIG['MAX_CONCURRENCY']) 476 | async def limited_task(index, profile_num, private_key, proxy): 477 | nonlocal successful_mints, failed_attempts 478 | async with semaphore: 479 | account = Account.from_key(private_key) 480 | print(f"{Fore.YELLOW}Processing wallet: {account.address} ({index + 1}/{len(private_keys)}){Style.RESET_ALL}") 481 | print() 482 | for token in TOKENS: 483 | if await mint_token(w3, private_key, profile_num, token, proxy, language): 484 | successful_mints += 1 485 | failed_attempts = 0 486 | else: 487 | failed_attempts += 1 488 | if failed_attempts >= 3: 489 | print(f"{Fore.RED} ✖ {LANG[language]['stop_wallet'].format(wallet=profile_num)}{Style.RESET_ALL}") 490 | return 491 | await asyncio.sleep(random.uniform(5, 10)) 492 | if index < len(private_keys) - 1: 493 | delay = random.uniform(CONFIG['PAUSE_BETWEEN_ATTEMPTS'][0], CONFIG['PAUSE_BETWEEN_ATTEMPTS'][1]) 494 | print(f"{Fore.YELLOW} ℹ {LANG[language]['pausing']} {delay:.2f} {LANG[language]['seconds']}{Style.RESET_ALL}") 495 | await asyncio.sleep(delay) 496 | print_separator(Fore.GREEN if successful_mints > 0 else Fore.RED) 497 | 498 | tasks = [] 499 | for i, (profile_num, private_key) in enumerate(private_keys): 500 | proxy = proxies[i % len(proxies)] if proxies else None 501 | tasks.append(limited_task(i, profile_num, private_key, proxy)) 502 | 503 | await asyncio.gather(*tasks, return_exceptions=True) 504 | 505 | print() 506 | print_border( 507 | f"{LANG[language]['completed'].format(successful=successful_mints, total=total_mints)}", 508 | Fore.GREEN 509 | ) 510 | print() 511 | 512 | if __name__ == "__main__": 513 | asyncio.run(run_mintping('vi')) 514 | -------------------------------------------------------------------------------- /scripts/mintpong.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import asyncio 4 | import random 5 | from web3 import Web3 6 | from eth_account import Account 7 | from colorama import init, Fore, Style 8 | import aiohttp 9 | from aiohttp_socks import ProxyConnector 10 | 11 | # Initialize colorama 12 | init(autoreset=True) 13 | 14 | # Border width 15 | BORDER_WIDTH = 80 16 | 17 | # Constants 18 | NETWORK_URL = "https://dream-rpc.somnia.network" 19 | CHAIN_ID = 50312 20 | EXPLORER_URL = "https://shannon-explorer.somnia.network/tx/0x" 21 | PONG_CONTRACT = Web3.to_checksum_address("0x9beaA0016c22B646Ac311Ab171270B0ECf23098F") 22 | IP_CHECK_URL = "https://api.ipify.org?format=json" 23 | MAX_WAIT_TIME = 180 # Timeout 3 minutes 24 | HEADERS = { 25 | "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36", 26 | "Accept": "application/json, text/plain, */*", 27 | "Accept-Encoding": "gzip, deflate, br", 28 | "Accept-Language": "en-US,en;q=0.9", 29 | } 30 | CONFIG = { 31 | "PAUSE_BETWEEN_ATTEMPTS": [10, 30], 32 | "MAX_CONCURRENCY": 5, 33 | "MAX_RETRIES": 3, 34 | "MINIMUM_BALANCE": 0.001, # STT 35 | "DEFAULT_GAS": 500000 36 | } 37 | 38 | # Token information 39 | TOKENS = [ 40 | {"symbol": "PONG", "address": PONG_CONTRACT, "amount": 1000, "decimals": 18} 41 | ] 42 | 43 | # PONG contract ABI 44 | PONG_ABI = [ 45 | { 46 | "inputs": [], 47 | "name": "mint", 48 | "outputs": [], 49 | "stateMutability": "payable", 50 | "type": "function" 51 | }, 52 | { 53 | "inputs": [{"name": "owner", "type": "address"}], 54 | "name": "balanceOf", 55 | "outputs": [{"name": "", "type": "uint256"}], 56 | "stateMutability": "view", 57 | "type": "function" 58 | }, 59 | { 60 | "inputs": [{"name": "account", "type": "address"}], 61 | "name": "isMinter", 62 | "outputs": [{"name": "", "type": "bool"}], 63 | "stateMutability": "view", 64 | "type": "function" 65 | } 66 | ] 67 | 68 | # Bilingual vocabulary 69 | LANG = { 70 | 'vi': { 71 | 'title': '✨ MINT $PONG - SOMNIA TESTNET ✨', 72 | 'info': 'ℹ Thông tin', 73 | 'found': 'Tìm thấy', 74 | 'wallets': 'ví', 75 | 'found_proxies': 'Tìm thấy {count} proxy trong proxies.txt', 76 | 'processing_wallets': '⚙ ĐANG XỬ LÝ {count} VÍ', 77 | 'checking_balance': 'Đang kiểm tra số dư...', 78 | 'already_minted': '❌ Ví này đã mint $PONG rồi! Vui lòng không thử lại.', 79 | 'insufficient_balance': 'Số dư không đủ (cần ít nhất {required:.6f} STT cho giao dịch)', 80 | 'preparing_tx': 'Đang chuẩn bị giao dịch...', 81 | 'sending_tx': 'Đang gửi giao dịch...', 82 | 'waiting_tx': 'Đang đợi xác nhận giao dịch...', 83 | 'success': '✅ Mint thành công {amount} {symbol}!', 84 | 'failure': '❌ Mint {symbol} thất bại', 85 | 'timeout': '⚠ Giao dịch chưa nhận được receipt sau {timeout} giây, kiểm tra trên explorer...', 86 | 'address': 'Địa chỉ ví', 87 | 'amount': 'Số dư {symbol}', 88 | 'gas': 'Gas', 89 | 'block': 'Khối', 90 | 'balance': 'Số dư STT', 91 | 'balance_info': 'Số dư', 92 | 'pausing': 'Tạm nghỉ', 93 | 'seconds': 'giây', 94 | 'completed': '🏁 HOÀN THÀNH: {successful}/{total} GIAO DỊCH THÀNH CÔNG', 95 | 'error': 'Lỗi', 96 | 'connect_success': '✅ Thành công: Đã kết nối mạng Somnia Testnet', 97 | 'connect_error': '❌ Không thể kết nối với RPC', 98 | 'web3_error': '❌ Kết nối Web3 thất bại', 99 | 'pvkey_not_found': '❌ File pvkey.txt không tồn tại', 100 | 'pvkey_empty': '❌ Không tìm thấy private key hợp lệ', 101 | 'pvkey_error': '❌ Đọc pvkey.txt thất bại', 102 | 'invalid_key': 'không hợp lệ, bỏ qua', 103 | 'warning_line': '⚠ Cảnh báo: Dòng', 104 | 'debugging_tx': 'Đang debug giao dịch...', 105 | 'gas_estimation_failed': '⚠ Không thể ước lượng gas', 106 | 'default_gas_used': 'Sử dụng gas mặc định: {gas}', 107 | 'tx_rejected': '⚠ Giao dịch bị từ chối bởi hợp đồng hoặc mạng', 108 | 'stop_wallet': 'Dừng xử lý ví {wallet}: Quá nhiều giao dịch thất bại liên tiếp', 109 | 'balance_check_failed': '⚠ Không kiểm tra được số dư {symbol}', 110 | 'using_proxy': '🔄 Sử dụng Proxy - [{proxy}] với IP công khai - [{public_ip}]', 111 | 'no_proxy': 'Không có proxy', 112 | 'unknown': 'Không xác định', 113 | 'no_proxies': 'Không tìm thấy proxy trong proxies.txt', 114 | 'invalid_proxy': '⚠ Proxy không hợp lệ hoặc không hoạt động: {proxy}', 115 | 'ip_check_failed': '⚠ Không thể kiểm tra IP công khai: {error}', 116 | 'invalid_contract_data': '⚠ Hợp đồng từ chối giao dịch. Kiểm tra dữ liệu giao dịch hoặc trạng thái hợp đồng tại https://shannon-explorer.somnia.network/address/0x9beaA0016c22B646Ac311Ab171270B0ECf23098F' 117 | }, 118 | 'en': { 119 | 'title': '✨ MINT $PONG - SOMNIA TESTNET ✨', 120 | 'info': 'ℹ Info', 121 | 'found': 'Found', 122 | 'wallets': 'wallets', 123 | 'found_proxies': 'Found {count} proxies in proxies.txt', 124 | 'processing_wallets': '⚙ PROCESSING {count} WALLETS', 125 | 'checking_balance': 'Checking balance...', 126 | 'already_minted': '❌ Wallet already minted $PONG! Please do not try again.', 127 | 'insufficient_balance': 'Insufficient balance (need at least {required:.6f} STT for transaction)', 128 | 'preparing_tx': 'Preparing transaction...', 129 | 'sending_tx': 'Sending transaction...', 130 | 'waiting_tx': 'Waiting for transaction confirmation...', 131 | 'success': '✅ Successfully minted {amount} {symbol}!', 132 | 'failure': '❌ Failed to mint {symbol}', 133 | 'timeout': '⚠ Transaction receipt not received after {timeout} seconds, check on explorer...', 134 | 'address': 'Wallet address', 135 | 'amount': '{symbol} Balance', 136 | 'gas': 'Gas', 137 | 'block': 'Block', 138 | 'balance': 'STT Balance', 139 | 'balance_info': 'Balance', 140 | 'pausing': 'Pausing', 141 | 'seconds': 'seconds', 142 | 'completed': '🏁 COMPLETED: {successful}/{total} TRANSACTIONS SUCCESSFUL', 143 | 'error': 'Error', 144 | 'connect_success': '✅ Success: Connected to Somnia Testnet', 145 | 'connect_error': '❌ Failed to connect to RPC', 146 | 'web3_error': '❌ Web3 connection failed', 147 | 'pvkey_not_found': '❌ pvkey.txt file not found', 148 | 'pvkey_empty': '❌ No valid private keys found', 149 | 'pvkey_error': '❌ Failed to read pvkey.txt', 150 | 'invalid_key': 'is invalid, skipped', 151 | 'warning_line': '⚠ Warning: Line', 152 | 'debugging_tx': 'Debugging transaction...', 153 | 'gas_estimation_failed': '⚠ Failed to estimate gas', 154 | 'default_gas_used': 'Using default gas: {gas}', 155 | 'tx_rejected': '⚠ Transaction rejected by contract or network', 156 | 'stop_wallet': 'Stopping wallet {wallet}: Too many consecutive failed transactions', 157 | 'balance_check_failed': '⚠ Failed to check {symbol} balance', 158 | 'using_proxy': '🔄 Using Proxy - [{proxy}] with Public IP - [{public_ip}]', 159 | 'no_proxy': 'None', 160 | 'unknown': 'Unknown', 161 | 'no_proxies': 'No proxies found in proxies.txt', 162 | 'invalid_proxy': '⚠ Invalid or unresponsive proxy: {proxy}', 163 | 'ip_check_failed': '⚠ Failed to check public IP: {error}', 164 | 'invalid_contract_data': '⚠ Contract rejected transaction. Check transaction data or contract status at https://shannon-explorer.somnia.network/address/0x9beaA0016c22B646Ac311Ab171270B0ECf23098F' 165 | } 166 | } 167 | 168 | # Display functions 169 | def print_border(text: str, color=Fore.CYAN, width=BORDER_WIDTH): 170 | text = text.strip() 171 | if len(text) > width - 4: 172 | text = text[:width - 7] + "..." 173 | padded_text = f" {text} ".center(width - 2) 174 | print(f"{color}┌{'─' * (width - 2)}┐{Style.RESET_ALL}") 175 | print(f"{color}│{padded_text}│{Style.RESET_ALL}") 176 | print(f"{color}└{'─' * (width - 2)}┘{Style.RESET_ALL}") 177 | 178 | def print_separator(color=Fore.MAGENTA): 179 | print(f"{color}{'═' * BORDER_WIDTH}{Style.RESET_ALL}") 180 | 181 | def print_wallets_summary(private_keys: list, language: str = 'en'): 182 | print_border( 183 | LANG[language]['processing_wallets'].format(count=len(private_keys)), 184 | Fore.MAGENTA 185 | ) 186 | print() 187 | 188 | def display_all_wallets_balances(w3: Web3, private_keys: list, language: str = 'en'): 189 | print_border(LANG[language]['balance_info'], Fore.CYAN) 190 | print(f"{Fore.CYAN} Wallet | {'STT':<10} | {'PONG':<10}{Style.RESET_ALL}") 191 | print(f"{Fore.CYAN} {'-' * 6} | {'-' * 10} | {'-' * 10}{Style.RESET_ALL}") 192 | 193 | for i, (profile_num, key) in enumerate(private_keys, 1): 194 | address = Account.from_key(key).address 195 | eth_balance = check_balance(w3, address, "native", 18, language) 196 | pong_balance = check_balance(w3, address, TOKENS[0]["address"], TOKENS[0]["decimals"], language) 197 | print(f"{Fore.YELLOW} {i:<6} | {eth_balance:>10.6f} | {pong_balance:>10.6f}{Style.RESET_ALL}") 198 | 199 | print() 200 | 201 | # Utility functions 202 | def is_valid_private_key(key: str) -> bool: 203 | key = key.strip() 204 | if not key.startswith('0x'): 205 | key = '0x' + key 206 | try: 207 | bytes.fromhex(key.replace('0x', '')) 208 | return len(key) == 66 209 | except ValueError: 210 | return False 211 | 212 | def load_private_keys(file_path: str = "pvkey.txt", language: str = 'en') -> list: 213 | try: 214 | if not os.path.exists(file_path): 215 | print(f"{Fore.RED} ✖ {LANG[language]['pvkey_not_found']}{Style.RESET_ALL}") 216 | with open(file_path, 'w') as f: 217 | f.write("# Add private keys here, one per line\n# Example: 0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef\n") 218 | sys.exit(1) 219 | 220 | valid_keys = [] 221 | with open(file_path, 'r') as f: 222 | for i, line in enumerate(f, 1): 223 | key = line.strip() 224 | if key and not key.startswith('#'): 225 | if is_valid_private_key(key): 226 | if not key.startswith('0x'): 227 | key = '0x' + key 228 | valid_keys.append((i, key)) 229 | else: 230 | print(f"{Fore.YELLOW} ⚠ {LANG[language]['warning_line']} {i} {LANG[language]['invalid_key']}: {key[:10]}...{Style.RESET_ALL}") 231 | 232 | if not valid_keys: 233 | print(f"{Fore.RED} ✖ {LANG[language]['pvkey_empty']}{Style.RESET_ALL}") 234 | sys.exit(1) 235 | 236 | return valid_keys 237 | except Exception as e: 238 | print(f"{Fore.RED} ✖ {LANG[language]['pvkey_error']}: {str(e)}{Style.RESET_ALL}") 239 | sys.exit(1) 240 | 241 | def load_proxies(file_path: str = "proxies.txt", language: str = 'en') -> list: 242 | try: 243 | if not os.path.exists(file_path): 244 | print(f"{Fore.YELLOW} ⚠ {LANG[language]['no_proxies']}. Using no proxy.{Style.RESET_ALL}") 245 | with open(file_path, 'w') as f: 246 | f.write("# Add proxies here, one per line\n# Example: socks5://user:pass@host:port or http://host:port\n") 247 | return [] 248 | 249 | proxies = [] 250 | with open(file_path, 'r') as f: 251 | for line in f: 252 | proxy = line.strip() 253 | if proxy and not line.startswith('#'): 254 | proxies.append(proxy) 255 | 256 | if not proxies: 257 | print(f"{Fore.YELLOW} ⚠ {LANG[language]['no_proxies']}. Using no proxy.{Style.RESET_ALL}") 258 | return [] 259 | 260 | print(f"{Fore.YELLOW} ℹ {LANG[language]['found_proxies'].format(count=len(proxies))}{Style.RESET_ALL}") 261 | return proxies 262 | except Exception as e: 263 | print(f"{Fore.RED} ✖ {LANG[language]['error']}: {str(e)}{Style.RESET_ALL}") 264 | return [] 265 | 266 | async def get_proxy_ip(proxy: str = None, language: str = 'en') -> str: 267 | try: 268 | if proxy: 269 | if proxy.startswith(('socks5://', 'socks4://', 'http://', 'https://')): 270 | connector = ProxyConnector.from_url(proxy) 271 | else: 272 | parts = proxy.split(':') 273 | if len(parts) == 4: # host:port:user:pass 274 | proxy_url = f"socks5://{parts[2]}:{parts[3]}@{parts[0]}:{parts[1]}" 275 | connector = ProxyConnector.from_url(proxy_url) 276 | elif len(parts) == 3 and '@' in proxy: # user:pass@host:port 277 | connector = ProxyConnector.from_url(f"socks5://{proxy}") 278 | else: 279 | print(f"{Fore.YELLOW} ⚠ {LANG[language]['invalid_proxy'].format(proxy=proxy)}{Style.RESET_ALL}") 280 | return LANG[language]['unknown'] 281 | async with aiohttp.ClientSession(connector=connector, timeout=aiohttp.ClientTimeout(total=10)) as session: 282 | async with session.get(IP_CHECK_URL, headers=HEADERS) as response: 283 | if response.status == 200: 284 | data = await response.json() 285 | return data.get('ip', LANG[language]['unknown']) 286 | print(f"{Fore.YELLOW} ⚠ {LANG[language]['ip_check_failed'].format(error=f'HTTP {response.status}')}{Style.RESET_ALL}") 287 | return LANG[language]['unknown'] 288 | else: 289 | async with aiohttp.ClientSession(timeout=aiohttp.ClientTimeout(total=10)) as session: 290 | async with session.get(IP_CHECK_URL, headers=HEADERS) as response: 291 | if response.status == 200: 292 | data = await response.json() 293 | return data.get('ip', LANG[language]['unknown']) 294 | print(f"{Fore.YELLOW} ⚠ {LANG[language]['ip_check_failed'].format(error=f'HTTP {response.status}')}{Style.RESET_ALL}") 295 | return LANG[language]['unknown'] 296 | except Exception as e: 297 | print(f"{Fore.YELLOW} ⚠ {LANG[language]['ip_check_failed'].format(error=str(e))}{Style.RESET_ALL}") 298 | return LANG[language]['unknown'] 299 | 300 | def connect_web3(language: str = 'en'): 301 | try: 302 | w3 = Web3(Web3.HTTPProvider(NETWORK_URL)) 303 | if not w3.is_connected(): 304 | print(f"{Fore.RED} ✖ {LANG[language]['connect_error']}{Style.RESET_ALL}") 305 | sys.exit(1) 306 | print(f"{Fore.GREEN} ✔ {LANG[language]['connect_success']} │ Chain ID: {w3.eth.chain_id}{Style.RESET_ALL}") 307 | return w3 308 | except Exception as e: 309 | print(f"{Fore.RED} ✖ {LANG[language]['web3_error']}: {str(e)}{Style.RESET_ALL}") 310 | sys.exit(1) 311 | 312 | def check_balance(w3: Web3, address: str, token_address: str, decimals: int, language: str = 'en') -> float: 313 | if token_address == "native": 314 | try: 315 | balance = w3.eth.get_balance(address) 316 | return float(w3.from_wei(balance, 'ether')) 317 | except Exception as e: 318 | print(f"{Fore.YELLOW} ⚠ {LANG[language]['balance_check_failed'].format(symbol='STT')}: {str(e)}{Style.RESET_ALL}") 319 | return -1 320 | else: 321 | contract = w3.eth.contract(address=Web3.to_checksum_address(token_address), abi=PONG_ABI) 322 | try: 323 | balance = contract.functions.balanceOf(address).call() 324 | return balance / (10 ** decimals) 325 | except Exception as e: 326 | print(f"{Fore.YELLOW} ⚠ {LANG[language]['balance_check_failed'].format(symbol='PONG')}: {str(e)}{Style.RESET_ALL}") 327 | return -1 328 | 329 | def debug_transaction(w3: Web3, tx_params: dict, language: str = 'en') -> str: 330 | try: 331 | print(f"{Fore.CYAN} > {LANG[language]['debugging_tx']}{Style.RESET_ALL}") 332 | w3.eth.call(tx_params) 333 | return "Transaction expected to succeed" 334 | except Exception as e: 335 | return f"Transaction expected to fail: {str(e)}" 336 | 337 | async def mint_token(w3: Web3, private_key: str, wallet_index: int, token: dict, proxy: str = None, language: str = 'en'): 338 | account = Account.from_key(private_key) 339 | sender_address = account.address 340 | contract = w3.eth.contract(address=Web3.to_checksum_address(token['address']), abi=PONG_ABI) 341 | 342 | # Check if wallet has already minted 343 | token_balance = check_balance(w3, sender_address, token['address'], token['decimals'], language) 344 | if token_balance > 0: 345 | print(f"{Fore.RED} ✖ {LANG[language]['already_minted']}{Style.RESET_ALL}") 346 | print(f"{Fore.YELLOW} - {LANG[language]['amount'].format(symbol=token['symbol'])}: {token_balance:.6f} {token['symbol']}{Style.RESET_ALL}") 347 | return False 348 | 349 | for attempt in range(CONFIG['MAX_RETRIES']): 350 | try: 351 | # Display proxy info 352 | public_ip = await get_proxy_ip(proxy, language) 353 | proxy_display = proxy if proxy else LANG[language]['no_proxy'] 354 | print(f"{Fore.CYAN} 🔄 {LANG[language]['using_proxy'].format(proxy=proxy_display, public_ip=public_ip)}{Style.RESET_ALL}") 355 | 356 | print(f"{Fore.CYAN} > {LANG[language]['checking_balance']}{Style.RESET_ALL}") 357 | eth_balance = float(w3.from_wei(w3.eth.get_balance(sender_address), 'ether')) 358 | print(f"{Fore.YELLOW} - {LANG[language]['balance']}: {eth_balance:.6f} STT{Style.RESET_ALL}") 359 | print(f"{Fore.YELLOW} - {LANG[language]['amount'].format(symbol=token['symbol'])}: {token_balance if token_balance >= 0 else 'N/A'} {token['symbol']}{Style.RESET_ALL}") 360 | 361 | if eth_balance < CONFIG['MINIMUM_BALANCE']: 362 | print(f"{Fore.RED} ✖ {LANG[language]['insufficient_balance'].format(required=CONFIG['MINIMUM_BALANCE'])}{Style.RESET_ALL}") 363 | return False 364 | 365 | print(f"{Fore.CYAN} > {LANG[language]['preparing_tx']}{Style.RESET_ALL}") 366 | nonce = w3.eth.get_transaction_count(sender_address, 'pending') 367 | tx_params = contract.functions.mint().build_transaction({ 368 | 'nonce': nonce, 369 | 'from': sender_address, 370 | 'chainId': CHAIN_ID, 371 | 'gasPrice': int(w3.eth.gas_price * random.uniform(1.03, 1.1)), 372 | 'value': 0 373 | }) 374 | 375 | try: 376 | estimated_gas = w3.eth.estimate_gas(tx_params) 377 | gas_limit = int(estimated_gas * 1.2) 378 | tx_params['gas'] = gas_limit 379 | print(f"{Fore.YELLOW} - Gas estimated: {estimated_gas} | Gas limit used: {gas_limit}{Style.RESET_ALL}") 380 | except Exception as e: 381 | tx_params['gas'] = CONFIG['DEFAULT_GAS'] 382 | print(f"{Fore.YELLOW} - {LANG[language]['gas_estimation_failed']}: {str(e)}. {LANG[language]['default_gas_used'].format(gas=CONFIG['DEFAULT_GAS'])}{Style.RESET_ALL}") 383 | 384 | total_required = tx_params['gas'] * tx_params['gasPrice'] 385 | total_required_eth = float(w3.from_wei(total_required, 'ether')) 386 | if eth_balance < total_required_eth: 387 | print(f"{Fore.RED} ✖ {LANG[language]['insufficient_balance'].format(required=total_required_eth)}{Style.RESET_ALL}") 388 | return False 389 | 390 | debug_result = debug_transaction(w3, tx_params, language) 391 | print(f"{Fore.YELLOW} - {debug_result}{Style.RESET_ALL}") 392 | if "fail" in debug_result.lower(): 393 | print(f"{Fore.RED} ✖ {LANG[language]['invalid_contract_data']}{Style.RESET_ALL}") 394 | return False 395 | 396 | print(f"{Fore.CYAN} > {LANG[language]['sending_tx']}{Style.RESET_ALL}") 397 | signed_tx = w3.eth.account.sign_transaction(tx_params, private_key) 398 | tx_hash = w3.eth.send_raw_transaction(signed_tx.raw_transaction) 399 | tx_link = f"{EXPLORER_URL}{tx_hash.hex()}" 400 | 401 | print(f"{Fore.CYAN} > {LANG[language]['waiting_tx']}{Style.RESET_ALL}") 402 | receipt = await wait_for_receipt(w3, tx_hash, MAX_WAIT_TIME, language) 403 | 404 | if receipt is None: 405 | print(f"{Fore.YELLOW} {LANG[language]['timeout'].format(timeout=MAX_WAIT_TIME)}{Style.RESET_ALL}") 406 | print(f"{Fore.YELLOW} - Tx: {tx_link}{Style.RESET_ALL}") 407 | return True 408 | elif receipt.status == 1: 409 | token_balance = check_balance(w3, sender_address, token['address'], token['decimals'], language) 410 | print(f"{Fore.GREEN} ✔ {LANG[language]['success'].format(amount=token['amount'], symbol=token['symbol'])} | Tx: {tx_link}{Style.RESET_ALL}") 411 | print(f"{Fore.YELLOW} - {LANG[language]['address']}: {sender_address}{Style.RESET_ALL}") 412 | print(f"{Fore.YELLOW} - {LANG[language]['gas']}: {receipt['gasUsed']}{Style.RESET_ALL}") 413 | print(f"{Fore.YELLOW} - {LANG[language]['block']}: {receipt['blockNumber']}{Style.RESET_ALL}") 414 | print(f"{Fore.YELLOW} - {LANG[language]['balance']}: {eth_balance:.6f} STT{Style.RESET_ALL}") 415 | print(f"{Fore.YELLOW} - {LANG[language]['amount'].format(symbol=token['symbol'])}: {token_balance if token_balance >= 0 else 'N/A'} {token['symbol']}{Style.RESET_ALL}") 416 | return True 417 | else: 418 | print(f"{Fore.RED} ✖ {LANG[language]['failure'].format(symbol=token['symbol'])} | Tx: {tx_link}{Style.RESET_ALL}") 419 | print(f"{Fore.RED} - {LANG[language]['tx_rejected']}{Style.RESET_ALL}") 420 | return False 421 | 422 | except Exception as e: 423 | if attempt < CONFIG['MAX_RETRIES'] - 1: 424 | delay = random.uniform(5, 15) 425 | print(f"{Fore.RED} ✖ {LANG[language]['failure'].format(symbol=token['symbol'])}: {str(e)} | Tx: {tx_link if 'tx_hash' in locals() else 'Not sent'}{Style.RESET_ALL}") 426 | print(f"{Fore.YELLOW} - {LANG[language]['pausing']} {delay:.2f} {LANG[language]['seconds']}{Style.RESET_ALL}") 427 | await asyncio.sleep(delay) 428 | continue 429 | print(f"{Fore.RED} ✖ {LANG[language]['invalid_contract_data']}: {str(e)} | Tx: {tx_link if 'tx_hash' in locals() else 'Not sent'}{Style.RESET_ALL}") 430 | return False 431 | 432 | async def wait_for_receipt(w3: Web3, tx_hash: str, max_wait_time: int, language: str = 'en'): 433 | start_time = asyncio.get_event_loop().time() 434 | while True: 435 | try: 436 | receipt = w3.eth.get_transaction_receipt(tx_hash) 437 | if receipt is not None: 438 | return receipt 439 | except Exception: 440 | pass 441 | elapsed_time = asyncio.get_event_loop().time() - start_time 442 | if elapsed_time > max_wait_time: 443 | return None 444 | await asyncio.sleep(5) 445 | 446 | async def run_mintpong(language: str = 'en'): 447 | print() 448 | print_border(LANG[language]['title'], Fore.CYAN) 449 | print() 450 | 451 | private_keys = load_private_keys('pvkey.txt', language) 452 | proxies = load_proxies('proxies.txt', language) 453 | print(f"{Fore.YELLOW} ℹ {LANG[language]['info']}: {LANG[language]['found']} {len(private_keys)} {LANG[language]['wallets']}{Style.RESET_ALL}") 454 | print() 455 | 456 | if not private_keys: 457 | return 458 | 459 | w3 = connect_web3(language) 460 | print() 461 | 462 | # Display balance table 463 | display_all_wallets_balances(w3, private_keys, language) 464 | print_separator() 465 | 466 | successful_mints = 0 467 | total_mints = len(private_keys) * len(TOKENS) 468 | failed_attempts = 0 469 | CONFIG['TOTAL_WALLETS'] = len(private_keys) 470 | CONFIG['MAX_CONCURRENCY'] = min(CONFIG['MAX_CONCURRENCY'], len(private_keys)) 471 | 472 | print_wallets_summary(private_keys, language) 473 | 474 | random.shuffle(private_keys) 475 | semaphore = asyncio.Semaphore(CONFIG['MAX_CONCURRENCY']) 476 | async def limited_task(index, profile_num, private_key, proxy): 477 | nonlocal successful_mints, failed_attempts 478 | async with semaphore: 479 | account = Account.from_key(private_key) 480 | print(f"{Fore.YELLOW}Processing wallet: {account.address} ({index + 1}/{len(private_keys)}){Style.RESET_ALL}") 481 | print() 482 | for token in TOKENS: 483 | if await mint_token(w3, private_key, profile_num, token, proxy, language): 484 | successful_mints += 1 485 | failed_attempts = 0 486 | else: 487 | failed_attempts += 1 488 | if failed_attempts >= 3: 489 | print(f"{Fore.RED} ✖ {LANG[language]['stop_wallet'].format(wallet=profile_num)}{Style.RESET_ALL}") 490 | return 491 | await asyncio.sleep(random.uniform(5, 10)) 492 | if index < len(private_keys) - 1: 493 | delay = random.uniform(CONFIG['PAUSE_BETWEEN_ATTEMPTS'][0], CONFIG['PAUSE_BETWEEN_ATTEMPTS'][1]) 494 | print(f"{Fore.YELLOW} ℹ {LANG[language]['pausing']} {delay:.2f} {LANG[language]['seconds']}{Style.RESET_ALL}") 495 | await asyncio.sleep(delay) 496 | print_separator(Fore.GREEN if successful_mints > 0 else Fore.RED) 497 | 498 | tasks = [] 499 | for i, (profile_num, private_key) in enumerate(private_keys): 500 | proxy = proxies[i % len(proxies)] if proxies else None 501 | tasks.append(limited_task(i, profile_num, private_key, proxy)) 502 | 503 | await asyncio.gather(*tasks, return_exceptions=True) 504 | 505 | print() 506 | print_border( 507 | f"{LANG[language]['completed'].format(successful=successful_mints, total=total_mints)}", 508 | Fore.GREEN 509 | ) 510 | print() 511 | 512 | if __name__ == "__main__": 513 | asyncio.run(run_mintpong('vi')) 514 | -------------------------------------------------------------------------------- /scripts/deploytoken.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import asyncio 4 | import random 5 | from web3 import Web3 6 | from web3.exceptions import ContractLogicError 7 | from eth_account import Account 8 | from solcx import compile_source, install_solc, get_solc_version 9 | from colorama import init, Fore, Style 10 | import aiohttp 11 | from aiohttp_socks import ProxyConnector 12 | 13 | # Khởi tạo colorama 14 | init(autoreset=True) 15 | 16 | # Constants 17 | NETWORK_URL = "https://dream-rpc.somnia.network" 18 | CHAIN_ID = 50312 19 | EXPLORER_URL = "https://shannon-explorer.somnia.network/tx/0x" 20 | IP_CHECK_URL = "https://api.ipify.org?format=json" 21 | SOLC_VERSION = "0.8.19" # Dùng phiên bản không có PUSH0 22 | HEADERS = { 23 | "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36", 24 | "Accept": "application/json, text/plain, */*", 25 | "Accept-Encoding": "gzip, deflate, br", 26 | "Accept-Language": "en-US,en;q=0.9", 27 | } 28 | CONFIG = { 29 | "PAUSE_BETWEEN_ATTEMPTS": [10, 30], 30 | "MAX_CONCURRENCY": 5, 31 | "MAX_RETRIES": 3, 32 | "MINIMUM_BALANCE": 0.001 # STT 33 | } 34 | 35 | # Source code của CustomToken.sol 36 | CONTRACT_SOURCE = """ 37 | // SPDX-License-Identifier: MIT 38 | pragma solidity ^0.8.19; 39 | 40 | contract CustomToken { 41 | string private _name; 42 | string private _symbol; 43 | uint8 private _decimals; 44 | uint256 private _totalSupply; 45 | address public owner; 46 | 47 | mapping(address => uint256) private _balances; 48 | mapping(address => mapping(address => uint256)) private _allowances; 49 | 50 | event Transfer(address indexed from, address indexed to, uint256 value); 51 | event Approval(address indexed owner, address indexed spender, uint256 value); 52 | 53 | modifier onlyOwner() { 54 | require(msg.sender == owner, "Only owner can call this function"); 55 | _; 56 | } 57 | 58 | constructor( 59 | string memory name_, 60 | string memory symbol_, 61 | uint8 decimals_, 62 | uint256 totalSupply_ 63 | ) { 64 | owner = msg.sender; 65 | _name = name_; 66 | _symbol = symbol_; 67 | _decimals = decimals_; 68 | _totalSupply = totalSupply_; 69 | _balances[address(this)] = totalSupply_; 70 | emit Transfer(address(0), address(this), totalSupply_); 71 | } 72 | 73 | function name() public view returns (string memory) { 74 | return _name; 75 | } 76 | 77 | function symbol() public view returns (string memory) { 78 | return _symbol; 79 | } 80 | 81 | function decimals() public view returns (uint8) { 82 | return _decimals; 83 | } 84 | 85 | function totalSupply() public view returns (uint256) { 86 | return _totalSupply; 87 | } 88 | 89 | function balanceOf(address account) public view returns (uint256) { 90 | return _balances[account]; 91 | } 92 | 93 | function transfer(address to, uint256 amount) public returns (bool) { 94 | _transfer(msg.sender, to, amount); 95 | return true; 96 | } 97 | 98 | function allowance(address tokenOwner, address spender) public view returns (uint256) { 99 | return _allowances[tokenOwner][spender]; 100 | } 101 | 102 | function approve(address spender, uint256 amount) public returns (bool) { 103 | _approve(msg.sender, spender, amount); 104 | return true; 105 | } 106 | 107 | function transferFrom(address from, address to, uint256 amount) public returns (bool) { 108 | uint256 currentAllowance = _allowances[from][msg.sender]; 109 | require(currentAllowance >= amount, "ERC20: transfer amount exceeds allowance"); 110 | _transfer(from, to, amount); 111 | _approve(from, msg.sender, currentAllowance - amount); 112 | return true; 113 | } 114 | 115 | function _transfer(address from, address to, uint256 amount) internal { 116 | require(from != address(0), "ERC20: transfer from the zero address"); 117 | require(to != address(0), "ERC20: transfer to the zero address"); 118 | require(_balances[from] >= amount, "ERC20: transfer amount exceeds balance"); 119 | _balances[from] -= amount; 120 | _balances[to] += amount; 121 | emit Transfer(from, to, amount); 122 | } 123 | 124 | function _approve(address tokenOwner, address spender, uint256 amount) internal { 125 | require(tokenOwner != address(0), "ERC20: approve from the zero address"); 126 | require(spender != address(0), "ERC20: approve to the zero address"); 127 | _allowances[tokenOwner][spender] = amount; 128 | emit Approval(tokenOwner, spender, amount); 129 | } 130 | 131 | function sendToken(address recipient, uint256 amount) external onlyOwner { 132 | _transfer(address(this), recipient, amount); 133 | } 134 | } 135 | """ 136 | 137 | # Từ vựng song ngữ 138 | LANG = { 139 | 'vi': { 140 | 'title': '✨ TRIỂN KHAI TOKEN ERC20 - SOMNIA TESTNET ✨', 141 | 'info': 'ℹ Thông tin', 142 | 'found': 'Tìm thấy', 143 | 'wallets': 'ví', 144 | 'found_proxies': 'Tìm thấy {count} proxy trong proxies.txt', 145 | 'processing_wallet': '⚙ XỬ LÝ VÍ', 146 | 'checking_balance': 'Đang kiểm tra số dư...', 147 | 'insufficient_balance': 'Số dư không đủ (cần ít nhất {required:.6f} STT cho giao dịch)', 148 | 'enter_name': 'Nhập tên token (VD: Thog Token): ', 149 | 'enter_symbol': 'Nhập ký hiệu token (VD: THOG): ', 150 | 'enter_decimals': 'Nhập số thập phân (mặc định 18): ', 151 | 'enter_supply': 'Nhập tổng cung (ví dụ: 1000000): ', 152 | 'preparing_tx': 'Đang chuẩn bị giao dịch...', 153 | 'sending_tx': 'Đang gửi giao dịch...', 154 | 'success': '✅ Triển khai hợp đồng thành công!', 155 | 'failure': '❌ Triển khai hợp đồng thất bại', 156 | 'timeout': '⏰ Giao dịch chưa xác nhận sau {timeout} giây, kiểm tra trên explorer', 157 | 'address': 'Địa chỉ ví', 158 | 'contract_address': 'Địa chỉ hợp đồng', 159 | 'gas': 'Gas', 160 | 'block': 'Khối', 161 | 'balance': 'Số dư STT', 162 | 'pausing': 'Tạm nghỉ', 163 | 'seconds': 'giây', 164 | 'completed': '🏁 HOÀN THÀNH: {successful}/{total} GIAO DỊCH THÀNH CÔNG', 165 | 'error': 'Lỗi', 166 | 'invalid_number': 'Vui lòng nhập số hợp lệ', 167 | 'connect_success': '✅ Thành công: Đã kết nối mạng Somnia Testnet', 168 | 'connect_error': '❌ Không thể kết nối RPC', 169 | 'web3_error': '❌ Kết nối Web3 thất bại', 170 | 'pvkey_not_found': '❌ File pvkey.txt không tồn tại', 171 | 'pvkey_empty': '❌ Không tìm thấy private key hợp lệ', 172 | 'pvkey_error': '❌ Đọc pvkey.txt thất bại', 173 | 'invalid_key': 'không hợp lệ, bỏ qua', 174 | 'warning_line': '⚠ Cảnh báo: Dòng', 175 | 'installing_solc': 'Đang cài đặt solc phiên bản {version}...', 176 | 'solc_installed': 'Đã cài đặt solc phiên bản {version}', 177 | 'estimating_gas': 'Đang ước lượng gas...', 178 | 'gas_estimation_failed': 'Không thể ước lượng gas', 179 | 'default_gas_used': 'Sử dụng gas mặc định: {gas}', 180 | 'tx_rejected': 'Giao dịch bị từ chối bởi mạng', 181 | 'stop_wallet': 'Dừng xử lý ví {wallet}: Quá nhiều giao dịch thất bại liên tiếp', 182 | 'using_proxy': '🔄 Sử dụng Proxy - [{proxy}] với IP công khai - [{public_ip}]', 183 | 'no_proxy': 'Không có proxy', 184 | 'unknown': 'Không xác định', 185 | 'no_proxies': 'Không tìm thấy proxy trong proxies.txt', 186 | 'invalid_proxy': '⚠ Proxy không hợp lệ hoặc không hoạt động: {proxy}', 187 | 'proxy_error': '❌ Lỗi kết nối proxy: {error}', 188 | 'ip_check_failed': '⚠ Không thể kiểm tra IP công khai: {error}', 189 | }, 190 | 'en': { 191 | 'title': '✨ DEPLOY ERC20 TOKEN - SOMNIA TESTNET ✨', 192 | 'info': 'ℹ Info', 193 | 'found': 'Found', 194 | 'wallets': 'wallets', 195 | 'found_proxies': 'Found {count} proxies in proxies.txt', 196 | 'processing_wallet': '⚙ PROCESSING WALLET', 197 | 'checking_balance': 'Checking balance...', 198 | 'insufficient_balance': 'Insufficient balance (need at least {required:.6f} STT for transaction)', 199 | 'enter_name': 'Enter token name (e.g., Thog Token): ', 200 | 'enter_symbol': 'Enter token symbol (e.g., THOG): ', 201 | 'enter_decimals': 'Enter decimals (default 18): ', 202 | 'enter_supply': 'Enter total supply (e.g., 1000000): ', 203 | 'preparing_tx': 'Preparing transaction...', 204 | 'sending_tx': 'Sending transaction...', 205 | 'success': '✅ Successfully deployed contract!', 206 | 'failure': '❌ Failed to deploy contract', 207 | 'timeout': '⏰ Transaction not confirmed after {timeout} seconds, check on explorer', 208 | 'address': 'Wallet address', 209 | 'contract_address': 'Contract address', 210 | 'gas': 'Gas', 211 | 'block': 'Block', 212 | 'balance': 'STT Balance', 213 | 'pausing': 'Pausing', 214 | 'seconds': 'seconds', 215 | 'completed': '🏁 COMPLETED: {successful}/{total} TRANSACTIONS SUCCESSFUL', 216 | 'error': 'Error', 217 | 'invalid_number': 'Please enter a valid number', 218 | 'connect_success': '✅ Success: Connected to Somnia Testnet', 219 | 'connect_error': '❌ Failed to connect to RPC', 220 | 'web3_error': '❌ Web3 connection failed', 221 | 'pvkey_not_found': '❌ pvkey.txt file not found', 222 | 'pvkey_empty': '❌ No valid private keys found', 223 | 'pvkey_error': '❌ Failed to read pvkey.txt', 224 | 'invalid_key': 'is invalid, skipped', 225 | 'warning_line': '⚠ Warning: Line', 226 | 'installing_solc': 'Installing solc version {version}...', 227 | 'solc_installed': 'Installed solc version {version}', 228 | 'estimating_gas': 'Estimating gas...', 229 | 'gas_estimation_failed': 'Failed to estimate gas', 230 | 'default_gas_used': 'Using default gas: {gas}', 231 | 'tx_rejected': 'Transaction rejected by network', 232 | 'stop_wallet': 'Stopping wallet {wallet}: Too many consecutive failed transactions', 233 | 'using_proxy': '🔄 Using Proxy - [{proxy}] with Public IP - [{public_ip}]', 234 | 'no_proxy': 'None', 235 | 'unknown': 'Unknown', 236 | 'no_proxies': 'No proxies found in proxies.txt', 237 | 'invalid_proxy': '⚠ Invalid or unresponsive proxy: {proxy}', 238 | 'proxy_error': '❌ Proxy connection error: {error}', 239 | 'ip_check_failed': '⚠ Failed to check public IP: {error}', 240 | } 241 | } 242 | 243 | # Hàm hiển thị viền đẹp mắt 244 | def print_border(text: str, color=Fore.CYAN, width=80): 245 | text = text.strip() 246 | if len(text) > width - 4: 247 | text = text[:width - 7] + "..." 248 | padded_text = f" {text} ".center(width - 2) 249 | print(f"{color}┌{'─' * (width - 2)}┐{Style.RESET_ALL}") 250 | print(f"{color}│{padded_text}│{Style.RESET_ALL}") 251 | print(f"{color}└{'─' * (width - 2)}┘{Style.RESET_ALL}") 252 | 253 | # Hàm hiển thị phân cách 254 | def print_separator(color=Fore.MAGENTA): 255 | print(f"{color}{'═' * 80}{Style.RESET_ALL}") 256 | 257 | # Hàm kiểm tra private key hợp lệ 258 | def is_valid_private_key(key: str) -> bool: 259 | key = key.strip() 260 | if not key.startswith('0x'): 261 | key = '0x' + key 262 | try: 263 | bytes.fromhex(key.replace('0x', '')) 264 | return len(key) == 66 265 | except ValueError: 266 | return False 267 | 268 | # Hàm đọc private keys từ file pvkey.txt 269 | def load_private_keys(file_path: str = "pvkey.txt", language: str = 'en') -> list: 270 | try: 271 | if not os.path.exists(file_path): 272 | print(f"{Fore.RED} ✖ {LANG[language]['pvkey_not_found']}{Style.RESET_ALL}") 273 | with open(file_path, 'w') as f: 274 | f.write("# Thêm private keys vào đây, mỗi key trên một dòng\n# Ví dụ: 0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef\n") 275 | sys.exit(1) 276 | 277 | valid_keys = [] 278 | with open(file_path, 'r') as f: 279 | for i, line in enumerate(f, 1): 280 | key = line.strip() 281 | if key and not key.startswith('#'): 282 | if is_valid_private_key(key): 283 | if not key.startswith('0x'): 284 | key = '0x' + key 285 | valid_keys.append((i, key)) 286 | else: 287 | print(f"{Fore.YELLOW} ⚠ {LANG[language]['warning_line']} {i} {LANG[language]['invalid_key']}: {key}{Style.RESET_ALL}") 288 | 289 | if not valid_keys: 290 | print(f"{Fore.RED} ✖ {LANG[language]['pvkey_empty']}{Style.RESET_ALL}") 291 | sys.exit(1) 292 | 293 | return valid_keys 294 | except Exception as e: 295 | print(f"{Fore.RED} ✖ {LANG[language]['pvkey_error']}: {str(e)}{Style.RESET_ALL}") 296 | sys.exit(1) 297 | 298 | # Hàm đọc proxies từ proxies.txt 299 | def load_proxies(file_path: str = "proxies.txt", language: str = 'en') -> list: 300 | try: 301 | if not os.path.exists(file_path): 302 | print(f"{Fore.YELLOW} ⚠ {LANG[language]['no_proxies']}. Dùng không proxy.{Style.RESET_ALL}") 303 | with open(file_path, 'w') as f: 304 | f.write("# Thêm proxy vào đây, mỗi proxy trên một dòng\n# Ví dụ: socks5://user:pass@host:port hoặc http://host:port\n") 305 | return [] 306 | 307 | proxies = [] 308 | with open(file_path, 'r') as f: 309 | for line in f: 310 | proxy = line.strip() 311 | if proxy and not proxy.startswith('#'): 312 | proxies.append(proxy) 313 | 314 | if not proxies: 315 | print(f"{Fore.YELLOW} ⚠ {LANG[language]['no_proxies']}. Dùng không proxy.{Style.RESET_ALL}") 316 | return [] 317 | 318 | print(f"{Fore.YELLOW} ℹ {LANG[language]['found_proxies'].format(count=len(proxies))}{Style.RESET_ALL}") 319 | return proxies 320 | except Exception as e: 321 | print(f"{Fore.RED} ✖ {LANG[language]['error']}: {str(e)}{Style.RESET_ALL}") 322 | return [] 323 | 324 | # Hàm lấy IP công khai qua proxy 325 | async def get_proxy_ip(proxy: str = None, language: str = 'en') -> str: 326 | try: 327 | if proxy: 328 | if proxy.startswith(('socks5://', 'socks4://', 'http://', 'https://')): 329 | connector = ProxyConnector.from_url(proxy) 330 | else: 331 | parts = proxy.split(':') 332 | if len(parts) == 4: # host:port:user:pass 333 | proxy_url = f"socks5://{parts[2]}:{parts[3]}@{parts[0]}:{parts[1]}" 334 | connector = ProxyConnector.from_url(proxy_url) 335 | elif len(parts) == 3 and '@' in proxy: # user:pass@host:port 336 | connector = ProxyConnector.from_url(f"socks5://{proxy}") 337 | else: 338 | print(f"{Fore.YELLOW} ⚠ {LANG[language]['invalid_proxy'].format(proxy=proxy)}{Style.RESET_ALL}") 339 | return LANG[language]['unknown'] 340 | async with aiohttp.ClientSession(connector=connector, timeout=aiohttp.ClientTimeout(total=10)) as session: 341 | async with session.get(IP_CHECK_URL, headers=HEADERS) as response: 342 | if response.status == 200: 343 | data = await response.json() 344 | return data.get('ip', LANG[language]['unknown']) 345 | print(f"{Fore.YELLOW} ⚠ {LANG[language]['ip_check_failed'].format(error=f'HTTP {response.status}')}{Style.RESET_ALL}") 346 | return LANG[language]['unknown'] 347 | else: 348 | async with aiohttp.ClientSession(timeout=aiohttp.ClientTimeout(total=10)) as session: 349 | async with session.get(IP_CHECK_URL, headers=HEADERS) as response: 350 | if response.status == 200: 351 | data = await response.json() 352 | return data.get('ip', LANG[language]['unknown']) 353 | print(f"{Fore.YELLOW} ⚠ {LANG[language]['ip_check_failed'].format(error=f'HTTP {response.status}')}{Style.RESET_ALL}") 354 | return LANG[language]['unknown'] 355 | except Exception as e: 356 | print(f"{Fore.YELLOW} ⚠ {LANG[language]['ip_check_failed'].format(error=str(e))}{Style.RESET_ALL}") 357 | return LANG[language]['unknown'] 358 | 359 | # Hàm kết nối Web3 360 | def connect_web3(language: str = 'en'): 361 | try: 362 | w3 = Web3(Web3.HTTPProvider(NETWORK_URL)) 363 | if not w3.is_connected(): 364 | print(f"{Fore.RED} ✖ {LANG[language]['connect_error']}{Style.RESET_ALL}") 365 | sys.exit(1) 366 | print(f"{Fore.GREEN} ✔ {LANG[language]['connect_success']} │ Chain ID: {w3.eth.chain_id}{Style.RESET_ALL}") 367 | return w3 368 | except Exception as e: 369 | print(f"{Fore.RED} ✖ {LANG[language]['web3_error']}: {str(e)}{Style.RESET_ALL}") 370 | sys.exit(1) 371 | 372 | # Hàm kiểm tra và cài đặt solc 373 | def ensure_solc_installed(language: str = 'en'): 374 | try: 375 | current_version = get_solc_version() 376 | if str(current_version) != SOLC_VERSION: 377 | raise Exception("Phiên bản solc không khớp") 378 | except Exception: 379 | print(f"{Fore.YELLOW} ℹ {LANG[language]['installing_solc'].format(version=SOLC_VERSION)}{Style.RESET_ALL}") 380 | install_solc(SOLC_VERSION) 381 | print(f"{Fore.GREEN} ✔ {LANG[language]['solc_installed'].format(version=SOLC_VERSION)}{Style.RESET_ALL}") 382 | 383 | # Hàm biên dịch hợp đồng 384 | def compile_contract(language: str = 'en'): 385 | ensure_solc_installed(language) 386 | compiled_sol = compile_source(CONTRACT_SOURCE, output_values=['abi', 'bin'], solc_version=SOLC_VERSION) 387 | contract_id, contract_interface = compiled_sol.popitem() 388 | return contract_interface['abi'], contract_interface['bin'] 389 | 390 | # Hàm đợi receipt thủ công 391 | async def wait_for_receipt(w3: Web3, tx_hash: str, max_wait_time: int = 300, language: str = 'en'): 392 | start_time = asyncio.get_event_loop().time() 393 | while True: 394 | try: 395 | receipt = w3.eth.get_transaction_receipt(tx_hash) 396 | if receipt is not None: 397 | return receipt 398 | except Exception: 399 | pass 400 | 401 | elapsed_time = asyncio.get_event_loop().time() - start_time 402 | if elapsed_time > max_wait_time: 403 | return None 404 | 405 | await asyncio.sleep(5) # Kiểm tra mỗi 5 giây 406 | 407 | # Hàm triển khai hợp đồng 408 | async def deploy_contract(w3: Web3, private_key: str, wallet_index: int, name: str, symbol: str, decimals: int, total_supply: int, proxy: str = None, language: str = 'en'): 409 | account = Account.from_key(private_key) 410 | sender_address = account.address 411 | 412 | for attempt in range(CONFIG['MAX_RETRIES']): 413 | try: 414 | # Display proxy info 415 | public_ip = await get_proxy_ip(proxy, language) 416 | proxy_display = proxy if proxy else LANG[language]['no_proxy'] 417 | print(f"{Fore.CYAN} 🔄 {LANG[language]['using_proxy'].format(proxy=proxy_display, public_ip=public_ip)}{Style.RESET_ALL}") 418 | 419 | # Kiểm tra số dư ví 420 | print(f"{Fore.CYAN} > {LANG[language]['checking_balance']}{Style.RESET_ALL}") 421 | eth_balance = float(w3.from_wei(w3.eth.get_balance(sender_address), 'ether')) 422 | if eth_balance < CONFIG['MINIMUM_BALANCE']: 423 | print(f"{Fore.RED} ✖ {LANG[language]['insufficient_balance'].format(required=CONFIG['MINIMUM_BALANCE'])}: {eth_balance:.6f} STT{Style.RESET_ALL}") 424 | return None 425 | 426 | # Biên dịch hợp đồng 427 | abi, bytecode = compile_contract(language) 428 | contract = w3.eth.contract(abi=abi, bytecode=bytecode) 429 | 430 | # Chuẩn bị giao dịch 431 | print(f"{Fore.CYAN} > {LANG[language]['preparing_tx']}{Style.RESET_ALL}") 432 | nonce = w3.eth.get_transaction_count(sender_address) 433 | total_supply_wei = w3.to_wei(total_supply, 'ether') 434 | 435 | # Ước lượng gas 436 | print(f"{Fore.CYAN} > {LANG[language]['estimating_gas']}{Style.RESET_ALL}") 437 | try: 438 | estimated_gas = contract.constructor(name, symbol, decimals, total_supply_wei).estimate_gas({ 439 | 'from': sender_address 440 | }) 441 | gas_limit = int(estimated_gas * 1.2) 442 | print(f"{Fore.YELLOW} - Gas ước lượng: {estimated_gas} | Gas limit sử dụng: {gas_limit}{Style.RESET_ALL}") 443 | except ContractLogicError as e: 444 | print(f"{Fore.RED} ✖ {LANG[language]['gas_estimation_failed']}: {str(e)} (Contract có thể không hợp lệ){Style.RESET_ALL}") 445 | return None 446 | except Exception as e: 447 | gas_limit = 5000000 448 | print(f"{Fore.YELLOW} - {LANG[language]['gas_estimation_failed']}: {str(e)}. {LANG[language]['default_gas_used'].format(gas=gas_limit)}{Style.RESET_ALL}") 449 | 450 | gas_price = int(w3.eth.gas_price * random.uniform(1.03, 1.1)) 451 | required_balance = w3.from_wei(gas_limit * gas_price, 'ether') 452 | if eth_balance < required_balance: 453 | print(f"{Fore.RED} ✖ {LANG[language]['insufficient_balance'].format(required=required_balance)}: {eth_balance:.6f} STT{Style.RESET_ALL}") 454 | return None 455 | 456 | tx = contract.constructor(name, symbol, decimals, total_supply_wei).build_transaction({ 457 | 'from': sender_address, 458 | 'nonce': nonce, 459 | 'chainId': CHAIN_ID, 460 | 'gas': gas_limit, 461 | 'gasPrice': gas_price 462 | }) 463 | 464 | # Gửi giao dịch 465 | print(f"{Fore.CYAN} > {LANG[language]['sending_tx']}{Style.RESET_ALL}") 466 | signed_tx = w3.eth.account.sign_transaction(tx, private_key) 467 | tx_hash = w3.eth.send_raw_transaction(signed_tx.raw_transaction) 468 | tx_link = f"{EXPLORER_URL}{tx_hash.hex()}" 469 | 470 | # Đợi receipt 471 | receipt = await wait_for_receipt(w3, tx_hash, max_wait_time=300, language=language) 472 | 473 | if receipt is None: 474 | print(f"{Fore.YELLOW} {LANG[language]['timeout'].format(timeout=300)} - Tx: {tx_link}{Style.RESET_ALL}") 475 | return tx_hash.hex() 476 | elif receipt.status == 1: 477 | contract_address = receipt['contractAddress'] 478 | print(f"{Fore.GREEN} ✔ {LANG[language]['success']} | Tx: {tx_link}{Style.RESET_ALL}") 479 | print(f"{Fore.YELLOW} - {LANG[language]['address']}: {sender_address}{Style.RESET_ALL}") 480 | print(f"{Fore.YELLOW} - {LANG[language]['contract_address']}: {contract_address}{Style.RESET_ALL}") 481 | print(f"{Fore.YELLOW} - {LANG[language]['gas']}: {receipt['gasUsed']}{Style.RESET_ALL}") 482 | print(f"{Fore.YELLOW} - {LANG[language]['block']}: {receipt['blockNumber']}{Style.RESET_ALL}") 483 | print(f"{Fore.YELLOW} - {LANG[language]['balance']}: {w3.from_wei(w3.eth.get_balance(sender_address), 'ether'):.6f} STT{Style.RESET_ALL}") 484 | return contract_address 485 | else: 486 | print(f"{Fore.RED} ✖ {LANG[language]['failure']} | Tx: {tx_link}{Style.RESET_ALL}") 487 | print(f"{Fore.RED} - {LANG[language]['tx_rejected']}{Style.RESET_ALL}") 488 | return None 489 | except Exception as e: 490 | if attempt < CONFIG['MAX_RETRIES'] - 1: 491 | delay = random.uniform(5, 15) 492 | print(f"{Fore.RED} ✖ {LANG[language]['failure']}: {str(e)} | Tx: {tx_link if 'tx_hash' in locals() else 'Chưa gửi'}{Style.RESET_ALL}") 493 | print(f"{Fore.YELLOW} ⚠ {LANG[language]['pausing']} {delay:.2f} {LANG[language]['seconds']}{Style.RESET_ALL}") 494 | await asyncio.sleep(delay) 495 | continue 496 | print(f"{Fore.RED} ✖ {LANG[language]['failure']}: {str(e)} | Tx: {tx_link if 'tx_hash' in locals() else 'Chưa gửi'}{Style.RESET_ALL}") 497 | return None 498 | 499 | # Hàm xử lý từng ví 500 | async def process_wallet(index: int, profile_num: int, private_key: str, proxy: str, w3: Web3, name: str, symbol: str, decimals: int, total_supply: int, language: str): 501 | total_wallets = CONFIG.get('TOTAL_WALLETS', 1) 502 | print_border( 503 | f"{LANG[language]['processing_wallet']} {profile_num} ({index + 1}/{total_wallets})", 504 | Fore.MAGENTA 505 | ) 506 | print() 507 | 508 | contract_address = await deploy_contract(w3, private_key, profile_num, name, symbol, decimals, total_supply, proxy, language) 509 | result = contract_address is not None 510 | print_separator(Fore.GREEN if result else Fore.RED) 511 | return contract_address 512 | 513 | # Hàm chính 514 | async def run_deploytoken(language: str = 'vi'): 515 | print() 516 | print_border(LANG[language]['title'], Fore.CYAN) 517 | print() 518 | 519 | private_keys = load_private_keys('pvkey.txt', language) 520 | proxies = load_proxies('proxies.txt', language) 521 | print(f"{Fore.YELLOW} ℹ {LANG[language]['info']}: {LANG[language]['found']} {len(private_keys)} {LANG[language]['wallets']}{Style.RESET_ALL}") 522 | print() 523 | 524 | if not private_keys: 525 | return 526 | 527 | w3 = connect_web3(language) 528 | print() 529 | 530 | # Nhập thông tin token 531 | name = input(f"{Fore.YELLOW} > {LANG[language]['enter_name']} {Style.RESET_ALL}").strip() 532 | symbol = input(f"{Fore.YELLOW} > {LANG[language]['enter_symbol']} {Style.RESET_ALL}").strip() 533 | decimals_input = input(f"{Fore.YELLOW} > {LANG[language]['enter_decimals']} {Style.RESET_ALL}").strip() or "18" 534 | total_supply_input = input(f"{Fore.YELLOW} > {LANG[language]['enter_supply']} {Style.RESET_ALL}").strip() 535 | 536 | try: 537 | decimals = int(decimals_input) 538 | total_supply = int(total_supply_input) 539 | if decimals <= 0 or total_supply <= 0: 540 | raise ValueError 541 | except ValueError: 542 | print(f"{Fore.RED} ✖ {LANG[language]['error']}: {LANG[language]['invalid_number']}{Style.RESET_ALL}") 543 | return 544 | 545 | successful_deploys = 0 546 | total_wallets = len(private_keys) 547 | failed_attempts = 0 548 | CONFIG['TOTAL_WALLETS'] = total_wallets 549 | CONFIG['MAX_CONCURRENCY'] = min(CONFIG['MAX_CONCURRENCY'], total_wallets) 550 | 551 | random.shuffle(private_keys) 552 | semaphore = asyncio.Semaphore(CONFIG['MAX_CONCURRENCY']) 553 | async def limited_task(index, profile_num, private_key, proxy): 554 | nonlocal successful_deploys, failed_attempts 555 | async with semaphore: 556 | contract_address = await process_wallet(index, profile_num, private_key, proxy, w3, name, symbol, decimals, total_supply, language) 557 | if contract_address: 558 | successful_deploys += 1 559 | failed_attempts = 0 560 | with open('contractERC20.txt', 'a') as f: 561 | f.write(f"{contract_address}\n") 562 | else: 563 | failed_attempts += 1 564 | if failed_attempts >= 3: 565 | print(f"{Fore.RED} ✖ {LANG[language]['stop_wallet'].format(wallet=profile_num)}{Style.RESET_ALL}") 566 | return 567 | if index < total_wallets - 1: 568 | delay = random.uniform(CONFIG['PAUSE_BETWEEN_ATTEMPTS'][0], CONFIG['PAUSE_BETWEEN_ATTEMPTS'][1]) 569 | print(f"{Fore.YELLOW} ℹ {LANG[language]['pausing']} {delay:.2f} {LANG[language]['seconds']}{Style.RESET_ALL}") 570 | await asyncio.sleep(delay) 571 | 572 | tasks = [] 573 | for i, (profile_num, private_key) in enumerate(private_keys): 574 | proxy = proxies[i % len(proxies)] if proxies else None 575 | tasks.append(limited_task(i, profile_num, private_key, proxy)) 576 | 577 | await asyncio.gather(*tasks, return_exceptions=True) 578 | 579 | print() 580 | print_border( 581 | f"{LANG[language]['completed'].format(successful=successful_deploys, total=total_wallets)}", 582 | Fore.GREEN 583 | ) 584 | print() 585 | 586 | if __name__ == "__main__": 587 | asyncio.run(run_deploytoken('vi')) 588 | -------------------------------------------------------------------------------- /scripts/sendtx.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import asyncio 4 | import random 5 | from web3 import Web3 6 | from eth_account import Account 7 | from colorama import init, Fore, Style 8 | import aiohttp 9 | from aiohttp_socks import ProxyConnector 10 | 11 | # Khởi tạo colorama 12 | init(autoreset=True) 13 | 14 | # Độ rộng viền 15 | BORDER_WIDTH = 80 16 | 17 | # Constants 18 | NETWORK_URL = "https://dream-rpc.somnia.network" 19 | CHAIN_ID = 50312 20 | EXPLORER_URL = "https://shannon-explorer.somnia.network/tx/0x" 21 | IP_CHECK_URL = "https://api.ipify.org?format=json" 22 | HEADERS = { 23 | "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36", 24 | "Accept": "application/json, text/plain, */*", 25 | "Accept-Encoding": "gzip, deflate, br", 26 | "Accept-Language": "en-US,en;q=0.9", 27 | } 28 | CONFIG = { 29 | "PAUSE_BETWEEN_ATTEMPTS": [10, 30], 30 | "MAX_CONCURRENCY": 5, 31 | "MAX_RETRIES": 3, 32 | "MINIMUM_BALANCE": 0.001 # STT 33 | } 34 | 35 | # Từ vựng song ngữ 36 | LANG = { 37 | 'vi': { 38 | 'title': '✨ GỬI GIAO DỊCH - SOMNIA TESTNET ✨', 39 | 'info': 'ℹ Thông tin', 40 | 'found': 'Tìm thấy', 41 | 'wallets': 'ví', 42 | 'found_proxies': 'Tìm thấy {count} proxy trong proxies.txt', 43 | 'enter_tx_count': '✦ NHẬP SỐ LƯỢNG GIAO DỊCH', 44 | 'tx_count_prompt': 'Số giao dịch (mặc định 1): ', 45 | 'selected': 'Đã chọn', 46 | 'transactions': 'giao dịch', 47 | 'enter_amount': '✦ NHẬP SỐ LƯỢNG STT', 48 | 'amount_prompt': 'Số lượng STT (mặc định 0.000001, tối đa 999): ', 49 | 'amount_unit': 'STT', 50 | 'select_tx_type': '✦ CHỌN LOẠI GIAO DỊCH', 51 | 'random_option': '1. Gửi đến địa chỉ ngẫu nhiên', 52 | 'file_option': '2. Gửi đến địa chỉ từ file (address.txt)', 53 | 'choice_prompt': 'Nhập lựa chọn (1 hoặc 2): ', 54 | 'start_random': '✨ BẮT ĐẦU GỬI {tx_count} GIAO DỊCH NGẪU NHIÊN', 55 | 'start_file': '✨ BẮT ĐẦU GỬI GIAO DỊCH ĐẾN {addr_count} ĐỊA CHỈ TỪ FILE', 56 | 'processing_wallet': '⚙ XỬ LÝ VÍ', 57 | 'checking_balance': 'Đang kiểm tra số dư...', 58 | 'insufficient_balance': 'Số dư không đủ (cần ít nhất {required:.6f} STT cho giao dịch)', 59 | 'transaction': 'Giao dịch', 60 | 'to_address': 'Địa chỉ nhận', 61 | 'sending': 'Đang gửi giao dịch...', 62 | 'success': '✅ Giao dịch thành công!', 63 | 'failure': '❌ Giao dịch thất bại', 64 | 'timeout': '⏰ Giao dịch chưa xác nhận sau {timeout} giây, kiểm tra trên explorer', 65 | 'sender': 'Người gửi', 66 | 'receiver': 'Người nhận', 67 | 'amount': 'Số lượng', 68 | 'gas': 'Gas', 69 | 'block': 'Khối', 70 | 'balance': 'Số dư', 71 | 'pausing': 'Tạm nghỉ', 72 | 'seconds': 'giây', 73 | 'completed': '🏁 HOÀN THÀNH: {successful}/{total} GIAO DỊCH THÀNH CÔNG', 74 | 'error': 'Lỗi', 75 | 'invalid_number': 'Vui lòng nhập số hợp lệ', 76 | 'tx_count_error': 'Số giao dịch phải lớn hơn 0', 77 | 'amount_error': 'Số lượng phải lớn hơn 0 và không quá 999', 78 | 'invalid_choice': 'Lựa chọn không hợp lệ', 79 | 'connect_success': '✅ Thành công: Đã kết nối mạng Somnia Testnet', 80 | 'connect_error': '❌ Không thể kết nối RPC', 81 | 'web3_error': '❌ Kết nối Web3 thất bại', 82 | 'pvkey_not_found': '❌ File pvkey.txt không tồn tại', 83 | 'pvkey_empty': '❌ Không tìm thấy private key hợp lệ', 84 | 'pvkey_error': '❌ Đọc pvkey.txt thất bại', 85 | 'addr_not_found': '❌ File address.txt không tồn tại', 86 | 'addr_empty': '❌ Không tìm thấy địa chỉ hợp lệ trong address.txt', 87 | 'addr_error': '❌ Đọc address.txt thất bại', 88 | 'invalid_addr': 'không phải địa chỉ hợp lệ, bỏ qua', 89 | 'warning_line': '⚠ Cảnh báo: Dòng', 90 | 'using_proxy': '🔄 Sử dụng Proxy - [{proxy}] với IP công khai - [{public_ip}]', 91 | 'no_proxy': 'Không có proxy', 92 | 'unknown': 'Không xác định', 93 | 'no_proxies': 'Không tìm thấy proxy trong proxies.txt', 94 | 'invalid_proxy': '⚠ Proxy không hợp lệ hoặc không hoạt động: {proxy}', 95 | 'proxy_error': '❌ Lỗi kết nối proxy: {error}', 96 | 'ip_check_failed': '⚠ Không thể kiểm tra IP công khai: {error}', 97 | }, 98 | 'en': { 99 | 'title': '✨ SEND TRANSACTION - SOMNIA TESTNET ✨', 100 | 'info': 'ℹ Info', 101 | 'found': 'Found', 102 | 'wallets': 'wallets', 103 | 'found_proxies': 'Found {count} proxies in proxies.txt', 104 | 'enter_tx_count': '✦ ENTER NUMBER OF TRANSACTIONS', 105 | 'tx_count_prompt': 'Number of transactions (default 1): ', 106 | 'selected': 'Selected', 107 | 'transactions': 'transactions', 108 | 'enter_amount': '✦ ENTER AMOUNT OF STT', 109 | 'amount_prompt': 'Amount of STT (default 0.000001, max 999): ', 110 | 'amount_unit': 'STT', 111 | 'select_tx_type': '✦ SELECT TRANSACTION TYPE', 112 | 'random_option': '1. Send to random address', 113 | 'file_option': '2. Send to addresses from file (address.txt)', 114 | 'choice_prompt': 'Enter choice (1 or 2): ', 115 | 'start_random': '✨ STARTING {tx_count} RANDOM TRANSACTIONS', 116 | 'start_file': '✨ STARTING TRANSACTIONS TO {addr_count} ADDRESSES FROM FILE', 117 | 'processing_wallet': '⚙ PROCESSING WALLET', 118 | 'checking_balance': 'Checking balance...', 119 | 'insufficient_balance': 'Insufficient balance (need at least {required:.6f} STT for transaction)', 120 | 'transaction': 'Transaction', 121 | 'to_address': 'Receiver address', 122 | 'sending': 'Sending transaction...', 123 | 'success': '✅ Transaction successful!', 124 | 'failure': '❌ Transaction failed', 125 | 'timeout': '⏰ Transaction not confirmed after {timeout} seconds, check on explorer', 126 | 'sender': 'Sender', 127 | 'receiver': 'Receiver', 128 | 'amount': 'Amount', 129 | 'gas': 'Gas', 130 | 'block': 'Block', 131 | 'balance': 'Balance', 132 | 'pausing': 'Pausing', 133 | 'seconds': 'seconds', 134 | 'completed': '🏁 COMPLETED: {successful}/{total} TRANSACTIONS SUCCESSFUL', 135 | 'error': 'Error', 136 | 'invalid_number': 'Please enter a valid number', 137 | 'tx_count_error': 'Number of transactions must be greater than 0', 138 | 'amount_error': 'Amount must be greater than 0 and not exceed 999', 139 | 'invalid_choice': 'Invalid choice', 140 | 'connect_success': '✅ Success: Connected to Somnia Testnet', 141 | 'connect_error': '❌ Failed to connect to RPC', 142 | 'web3_error': '❌ Web3 connection failed', 143 | 'pvkey_not_found': '❌ pvkey.txt file not found', 144 | 'pvkey_empty': '❌ No valid private keys found', 145 | 'pvkey_error': '❌ Failed to read pvkey.txt', 146 | 'addr_not_found': '❌ address.txt file not found', 147 | 'addr_empty': '❌ No valid addresses found in address.txt', 148 | 'addr_error': '❌ Failed to read address.txt', 149 | 'invalid_addr': 'is not a valid address, skipped', 150 | 'warning_line': '⚠ Warning: Line', 151 | 'using_proxy': '🔄 Using Proxy - [{proxy}] with Public IP - [{public_ip}]', 152 | 'no_proxy': 'None', 153 | 'unknown': 'Unknown', 154 | 'no_proxies': 'No proxies found in proxies.txt', 155 | 'invalid_proxy': '⚠ Invalid or unresponsive proxy: {proxy}', 156 | 'proxy_error': '❌ Proxy connection error: {error}', 157 | 'ip_check_failed': '⚠ Failed to check public IP: {error}', 158 | } 159 | } 160 | 161 | # Hàm hiển thị viền đẹp mắt 162 | def print_border(text: str, color=Fore.CYAN, width=BORDER_WIDTH): 163 | text = text.strip() 164 | if len(text) > width - 4: 165 | text = text[:width - 7] + "..." 166 | padded_text = f" {text} ".center(width - 2) 167 | print(f"{color}┌{'─' * (width - 2)}┐{Style.RESET_ALL}") 168 | print(f"{color}│{padded_text}│{Style.RESET_ALL}") 169 | print(f"{color}└{'─' * (width - 2)}┘{Style.RESET_ALL}") 170 | 171 | # Hàm hiển thị phân cách 172 | def print_separator(color=Fore.MAGENTA): 173 | print(f"{color}{'═' * BORDER_WIDTH}{Style.RESET_ALL}") 174 | 175 | # Hàm kiểm tra private key hợp lệ 176 | def is_valid_private_key(key: str) -> bool: 177 | key = key.strip() 178 | if not key.startswith('0x'): 179 | key = '0x' + key 180 | try: 181 | bytes.fromhex(key.replace('0x', '')) 182 | return len(key) == 66 183 | except ValueError: 184 | return False 185 | 186 | # Hàm đọc private keys từ file pvkey.txt 187 | def load_private_keys(file_path: str = "pvkey.txt", language: str = 'en') -> list: 188 | try: 189 | if not os.path.exists(file_path): 190 | print(f"{Fore.RED} ✖ {LANG[language]['pvkey_not_found']}{Style.RESET_ALL}") 191 | with open(file_path, 'w') as f: 192 | f.write("# Thêm private keys vào đây, mỗi key trên một dòng\n# Ví dụ: 0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef\n") 193 | sys.exit(1) 194 | 195 | valid_keys = [] 196 | with open(file_path, 'r') as f: 197 | for i, line in enumerate(f, 1): 198 | key = line.strip() 199 | if key and not key.startswith('#'): 200 | if is_valid_private_key(key): 201 | if not key.startswith('0x'): 202 | key = '0x' + key 203 | valid_keys.append((i, key)) 204 | else: 205 | print(f"{Fore.YELLOW} ⚠ {LANG[language]['warning_line']} {i}: {LANG[language]['invalid_addr']} - {key}{Style.RESET_ALL}") 206 | 207 | if not valid_keys: 208 | print(f"{Fore.RED} ✖ {LANG[language]['pvkey_empty']}{Style.RESET_ALL}") 209 | sys.exit(1) 210 | 211 | return valid_keys 212 | except Exception as e: 213 | print(f"{Fore.RED} ✖ {LANG[language]['pvkey_error']}: {str(e)}{Style.RESET_ALL}") 214 | sys.exit(1) 215 | 216 | # Hàm đọc địa chỉ từ file address.txt 217 | def load_addresses(file_path: str = "address.txt", language: str = 'en') -> list: 218 | try: 219 | if not os.path.exists(file_path): 220 | print(f"{Fore.YELLOW} ⚠ {LANG[language]['addr_not_found']}. Tạo file mới.{Style.RESET_ALL}") 221 | with open(file_path, 'w') as f: 222 | f.write("# Thêm địa chỉ nhận vào đây, mỗi địa chỉ trên một dòng\n# Ví dụ: 0x1234567890abcdef1234567890abcdef1234567890\n") 223 | return None 224 | 225 | addresses = [] 226 | with open(file_path, 'r') as f: 227 | for i, line in enumerate(f, 1): 228 | addr = line.strip() 229 | if addr and not addr.startswith('#'): 230 | if Web3.is_address(addr): 231 | addresses.append(Web3.to_checksum_address(addr)) 232 | else: 233 | print(f"{Fore.YELLOW} ⚠ {LANG[language]['warning_line']} {i}: {LANG[language]['invalid_addr']} - {addr}{Style.RESET_ALL}") 234 | 235 | if not addresses: 236 | print(f"{Fore.RED} ✖ {LANG[language]['addr_empty']}{Style.RESET_ALL}") 237 | return None 238 | 239 | return addresses 240 | except Exception as e: 241 | print(f"{Fore.RED} ✖ {LANG[language]['addr_error']}: {str(e)}{Style.RESET_ALL}") 242 | return None 243 | 244 | # Hàm đọc proxies từ proxies.txt 245 | def load_proxies(file_path: str = "proxies.txt", language: str = 'en') -> list: 246 | try: 247 | if not os.path.exists(file_path): 248 | print(f"{Fore.YELLOW} ⚠ {LANG[language]['no_proxies']}. Dùng không proxy.{Style.RESET_ALL}") 249 | with open(file_path, 'w') as f: 250 | f.write("# Thêm proxy vào đây, mỗi proxy trên một dòng\n# Ví dụ: socks5://user:pass@host:port hoặc http://host:port\n") 251 | return [] 252 | 253 | proxies = [] 254 | with open(file_path, 'r') as f: 255 | for line in f: 256 | proxy = line.strip() 257 | if proxy and not proxy.startswith('#'): 258 | proxies.append(proxy) 259 | 260 | if not proxies: 261 | print(f"{Fore.YELLOW} ⚠ {LANG[language]['no_proxies']}. Dùng không proxy.{Style.RESET_ALL}") 262 | return [] 263 | 264 | print(f"{Fore.YELLOW} ℹ {LANG[language]['found_proxies'].format(count=len(proxies))}{Style.RESET_ALL}") 265 | return proxies 266 | except Exception as e: 267 | print(f"{Fore.RED} ✖ {LANG[language]['error']}: {str(e)}{Style.RESET_ALL}") 268 | return [] 269 | 270 | # Hàm lấy IP công khai qua proxy 271 | async def get_proxy_ip(proxy: str = None, language: str = 'en') -> str: 272 | try: 273 | if proxy: 274 | if proxy.startswith(('socks5://', 'socks4://', 'http://', 'https://')): 275 | connector = ProxyConnector.from_url(proxy) 276 | else: 277 | parts = proxy.split(':') 278 | if len(parts) == 4: # host:port:user:pass 279 | proxy_url = f"socks5://{parts[2]}:{parts[3]}@{parts[0]}:{parts[1]}" 280 | connector = ProxyConnector.from_url(proxy_url) 281 | elif len(parts) == 3 and '@' in proxy: # user:pass@host:port 282 | connector = ProxyConnector.from_url(f"socks5://{proxy}") 283 | else: 284 | print(f"{Fore.YELLOW} ⚠ {LANG[language]['invalid_proxy'].format(proxy=proxy)}{Style.RESET_ALL}") 285 | return LANG[language]['unknown'] 286 | async with aiohttp.ClientSession(connector=connector, timeout=aiohttp.ClientTimeout(total=10)) as session: 287 | async with session.get(IP_CHECK_URL, headers=HEADERS) as response: 288 | if response.status == 200: 289 | data = await response.json() 290 | return data.get('ip', LANG[language]['unknown']) 291 | print(f"{Fore.YELLOW} ⚠ {LANG[language]['ip_check_failed'].format(error=f'HTTP {response.status}')}{Style.RESET_ALL}") 292 | return LANG[language]['unknown'] 293 | else: 294 | async with aiohttp.ClientSession(timeout=aiohttp.ClientTimeout(total=10)) as session: 295 | async with session.get(IP_CHECK_URL, headers=HEADERS) as response: 296 | if response.status == 200: 297 | data = await response.json() 298 | return data.get('ip', LANG[language]['unknown']) 299 | print(f"{Fore.YELLOW} ⚠ {LANG[language]['ip_check_failed'].format(error=f'HTTP {response.status}')}{Style.RESET_ALL}") 300 | return LANG[language]['unknown'] 301 | except Exception as e: 302 | print(f"{Fore.YELLOW} ⚠ {LANG[language]['ip_check_failed'].format(error=str(e))}{Style.RESET_ALL}") 303 | return LANG[language]['unknown'] 304 | 305 | # Hàm kết nối Web3 306 | def connect_web3(language: str = 'en'): 307 | try: 308 | w3 = Web3(Web3.HTTPProvider(NETWORK_URL)) 309 | if not w3.is_connected(): 310 | print(f"{Fore.RED} ✖ {LANG[language]['connect_error']}{Style.RESET_ALL}") 311 | sys.exit(1) 312 | print(f"{Fore.GREEN} ✔ {LANG[language]['connect_success']} │ Chain ID: {w3.eth.chain_id}{Style.RESET_ALL}") 313 | return w3 314 | except Exception as e: 315 | print(f"{Fore.RED} ✖ {LANG[language]['web3_error']}: {str(e)}{Style.RESET_ALL}") 316 | sys.exit(1) 317 | 318 | # Hàm đợi receipt thủ công 319 | async def wait_for_receipt(w3: Web3, tx_hash: str, max_wait_time: int = 300, language: str = 'en'): 320 | start_time = asyncio.get_event_loop().time() 321 | while True: 322 | try: 323 | receipt = w3.eth.get_transaction_receipt(tx_hash) 324 | if receipt is not None: 325 | return receipt 326 | except Exception: 327 | pass 328 | 329 | elapsed_time = asyncio.get_event_loop().time() - start_time 330 | if elapsed_time > max_wait_time: 331 | return None 332 | 333 | await asyncio.sleep(5) # Kiểm tra mỗi 5 giây 334 | 335 | # Tạo địa chỉ ngẫu nhiên với checksum 336 | def get_random_address(w3: Web3): 337 | random_account = w3.eth.account.create() 338 | return random_account.address 339 | 340 | # Hàm gửi giao dịch 341 | async def send_transaction(w3: Web3, private_key: str, to_address: str, amount: float, wallet_index: int, tx_index: int, total_tx: int, language: str = 'en'): 342 | account = Account.from_key(private_key) 343 | sender_address = account.address 344 | 345 | for attempt in range(CONFIG['MAX_RETRIES']): 346 | try: 347 | nonce = w3.eth.get_transaction_count(sender_address) 348 | gas_price = int(w3.eth.gas_price * random.uniform(1.03, 1.1)) 349 | 350 | # Ước lượng gas 351 | try: 352 | estimated_gas = w3.eth.estimate_gas({ 353 | 'from': sender_address, 354 | 'to': to_address, 355 | 'value': w3.to_wei(amount, 'ether') 356 | }) 357 | gas_limit = int(estimated_gas * 1.2) 358 | print(f"{Fore.YELLOW} - Gas ước lượng: {estimated_gas} | Gas limit sử dụng: {gas_limit}{Style.RESET_ALL}") 359 | except Exception as e: 360 | gas_limit = 30000 361 | print(f"{Fore.YELLOW} - Không thể ước lượng gas: {str(e)}. Dùng gas mặc định: {gas_limit}{Style.RESET_ALL}") 362 | 363 | balance = w3.from_wei(w3.eth.get_balance(sender_address), 'ether') 364 | required_balance = w3.from_wei(gas_limit * gas_price + w3.to_wei(amount, 'ether'), 'ether') 365 | if balance < required_balance: 366 | print(f"{Fore.RED} ✖ {LANG[language]['insufficient_balance'].format(required=required_balance)}: {balance:.6f} STT{Style.RESET_ALL}") 367 | return False 368 | 369 | tx = { 370 | 'nonce': nonce, 371 | 'to': to_address, 372 | 'value': w3.to_wei(amount, 'ether'), 373 | 'gas': gas_limit, 374 | 'gasPrice': gas_price, 375 | 'chainId': CHAIN_ID, 376 | } 377 | 378 | print(f"{Fore.CYAN} > {LANG[language]['sending']}{Style.RESET_ALL}") 379 | signed_tx = w3.eth.account.sign_transaction(tx, private_key) 380 | tx_hash = w3.eth.send_raw_transaction(signed_tx.raw_transaction) 381 | tx_link = f"{EXPLORER_URL}{tx_hash.hex()}" 382 | 383 | receipt = await wait_for_receipt(w3, tx_hash, max_wait_time=300, language=language) 384 | 385 | if receipt is None: 386 | print(f"{Fore.YELLOW} {LANG[language]['timeout'].format(timeout=300)} - Tx: {tx_link}{Style.RESET_ALL}") 387 | return True 388 | elif receipt.status == 1: 389 | print(f"{Fore.GREEN} ✔ {LANG[language]['success']}{Style.RESET_ALL}") 390 | print(f"{Fore.YELLOW} - {LANG[language]['sender']}: {sender_address}{Style.RESET_ALL}") 391 | print(f"{Fore.YELLOW} - {LANG[language]['receiver']}: {to_address}{Style.RESET_ALL}") 392 | print(f"{Fore.YELLOW} - {LANG[language]['amount']}: {amount:.6f} {LANG[language]['amount_unit']}{Style.RESET_ALL}") 393 | print(f"{Fore.YELLOW} - {LANG[language]['gas']}: {receipt['gasUsed']}{Style.RESET_ALL}") 394 | print(f"{Fore.YELLOW} - {LANG[language]['block']}: {receipt['blockNumber']}{Style.RESET_ALL}") 395 | print(f"{Fore.YELLOW} - Tx: {tx_link}{Style.RESET_ALL}") 396 | print(f"{Fore.YELLOW} - {LANG[language]['balance']}: {w3.from_wei(w3.eth.get_balance(sender_address), 'ether'):.6f} {LANG[language]['amount_unit']}{Style.RESET_ALL}") 397 | return True 398 | else: 399 | print(f"{Fore.RED} ✖ {LANG[language]['failure']} | Tx: {tx_link}{Style.RESET_ALL}") 400 | return False 401 | except Exception as e: 402 | if attempt < CONFIG['MAX_RETRIES'] - 1: 403 | delay = random.uniform(5, 15) 404 | print(f"{Fore.RED} ✖ {LANG[language]['failure']}: {str(e)} | Tx: {tx_link if 'tx_hash' in locals() else 'Chưa gửi'}{Style.RESET_ALL}") 405 | print(f"{Fore.YELLOW} ⚠ {LANG[language]['pausing']} {delay:.2f} {LANG[language]['seconds']}{Style.RESET_ALL}") 406 | await asyncio.sleep(delay) 407 | continue 408 | print(f"{Fore.RED} ✖ {LANG[language]['failure']}: {str(e)} | Tx: {tx_link if 'tx_hash' in locals() else 'Chưa gửi'}{Style.RESET_ALL}") 409 | return False 410 | 411 | # Hàm nhập số lượng giao dịch 412 | def get_tx_count(language: str = 'en') -> int: 413 | print_border(LANG[language]['enter_tx_count'], Fore.YELLOW) 414 | while True: 415 | try: 416 | tx_count_input = input(f"{Fore.YELLOW} > {LANG[language]['tx_count_prompt']}{Style.RESET_ALL}") 417 | tx_count = int(tx_count_input) if tx_count_input.strip() else 1 418 | if tx_count <= 0: 419 | print(f"{Fore.RED} ✖ {LANG[language]['error']}: {LANG[language]['tx_count_error']}{Style.RESET_ALL}") 420 | else: 421 | print(f"{Fore.GREEN} ✔ {LANG[language]['selected']}: {tx_count} {LANG[language]['transactions']}{Style.RESET_ALL}") 422 | return tx_count 423 | except ValueError: 424 | print(f"{Fore.RED} ✖ {LANG[language]['error']}: {LANG[language]['invalid_number']}{Style.RESET_ALL}") 425 | 426 | # Hàm nhập số lượng STT 427 | def get_amount(language: str = 'en') -> float: 428 | print_border(LANG[language]['enter_amount'], Fore.YELLOW) 429 | while True: 430 | try: 431 | amount_input = input(f"{Fore.YELLOW} > {LANG[language]['amount_prompt']}{Style.RESET_ALL}") 432 | amount = float(amount_input) if amount_input.strip() else 0.000001 433 | if 0 < amount <= 999: 434 | print(f"{Fore.GREEN} ✔ {LANG[language]['selected']}: {amount} {LANG[language]['amount_unit']}{Style.RESET_ALL}") 435 | return amount 436 | print(f"{Fore.RED} ✖ {LANG[language]['error']}: {LANG[language]['amount_error']}{Style.RESET_ALL}") 437 | except ValueError: 438 | print(f"{Fore.RED} ✖ {LANG[language]['error']}: {LANG[language]['invalid_number']}{Style.RESET_ALL}") 439 | 440 | # Gửi giao dịch đến địa chỉ ngẫu nhiên 441 | async def send_to_random_addresses(w3: Web3, amount: float, tx_count: int, private_key: str, wallet_index: int, language: str = 'en'): 442 | successful_txs = 0 443 | 444 | for tx_iter in range(tx_count): 445 | print(f"{Fore.CYAN} > {LANG[language]['transaction']} {tx_iter + 1}/{tx_count}{Style.RESET_ALL}") 446 | to_address = get_random_address(w3) 447 | if await send_transaction(w3, private_key, to_address, amount, wallet_index, tx_iter + 1, tx_count, language): 448 | successful_txs += 1 449 | if tx_iter < tx_count - 1: 450 | delay = random.uniform(CONFIG['PAUSE_BETWEEN_ATTEMPTS'][0], CONFIG['PAUSE_BETWEEN_ATTEMPTS'][1]) 451 | print(f"{Fore.YELLOW} ℹ {LANG[language]['pausing']} {delay:.2f} {LANG[language]['seconds']}{Style.RESET_ALL}") 452 | await asyncio.sleep(delay) 453 | print_separator() 454 | 455 | return successful_txs 456 | 457 | # Gửi giao dịch đến địa chỉ từ file 458 | async def send_to_file_addresses(w3: Web3, amount: float, addresses: list, private_key: str, wallet_index: int, language: str = 'en'): 459 | successful_txs = 0 460 | 461 | for addr_iter, to_address in enumerate(addresses, 1): 462 | print(f"{Fore.CYAN} > {LANG[language]['to_address']} {addr_iter}/{len(addresses)}{Style.RESET_ALL}") 463 | if await send_transaction(w3, private_key, to_address, amount, wallet_index, addr_iter, len(addresses), language): 464 | successful_txs += 1 465 | if addr_iter < len(addresses): 466 | delay = random.uniform(CONFIG['PAUSE_BETWEEN_ATTEMPTS'][0], CONFIG['PAUSE_BETWEEN_ATTEMPTS'][1]) 467 | print(f"{Fore.YELLOW} ℹ {LANG[language]['pausing']} {delay:.2f} {LANG[language]['seconds']}{Style.RESET_ALL}") 468 | await asyncio.sleep(delay) 469 | print_separator() 470 | 471 | return successful_txs 472 | 473 | # Hàm xử lý từng ví 474 | async def process_wallet(index: int, profile_num: int, private_key: str, proxy: str, w3: Web3, choice: str, tx_count: int, amount: float, language: str): 475 | total_wallets = CONFIG.get('TOTAL_WALLETS', 1) 476 | print_border( 477 | f"{LANG[language]['processing_wallet']} {profile_num} ({index + 1}/{total_wallets})", 478 | Fore.MAGENTA 479 | ) 480 | print() 481 | 482 | # Display proxy info 483 | public_ip = await get_proxy_ip(proxy, language) 484 | proxy_display = proxy if proxy else LANG[language]['no_proxy'] 485 | print(f"{Fore.CYAN} 🔄 {LANG[language]['using_proxy'].format(proxy=proxy_display, public_ip=public_ip)}{Style.RESET_ALL}") 486 | 487 | print(f"{Fore.CYAN} > {LANG[language]['checking_balance']}{Style.RESET_ALL}") 488 | eth_balance = float(w3.from_wei(w3.eth.get_balance(Account.from_key(private_key).address), 'ether')) 489 | if eth_balance < CONFIG['MINIMUM_BALANCE']: 490 | print(f"{Fore.RED} ✖ {LANG[language]['insufficient_balance'].format(required=CONFIG['MINIMUM_BALANCE'])}: {eth_balance:.6f} STT{Style.RESET_ALL}") 491 | return 0 492 | 493 | if choice == '1': 494 | print_border(LANG[language]['start_random'].format(tx_count=tx_count), Fore.CYAN) 495 | result = await send_to_random_addresses(w3, amount, tx_count, private_key, index, language) 496 | else: 497 | addresses = load_addresses('address.txt', language) 498 | if not addresses: 499 | return 0 500 | print_border(LANG[language]['start_file'].format(addr_count=len(addresses)), Fore.CYAN) 501 | result = await send_to_file_addresses(w3, amount, addresses, private_key, index, language) 502 | 503 | print_separator(Fore.GREEN if result > 0 else Fore.RED) 504 | return result 505 | 506 | # Hàm chính 507 | async def run_sendtx(language: str = 'vi'): 508 | print() 509 | print_border(LANG[language]['title'], Fore.CYAN) 510 | print() 511 | 512 | private_keys = load_private_keys('pvkey.txt', language) 513 | proxies = load_proxies('proxies.txt', language) 514 | print(f"{Fore.YELLOW} ℹ {LANG[language]['info']}: {LANG[language]['found']} {len(private_keys)} {LANG[language]['wallets']}{Style.RESET_ALL}") 515 | print() 516 | 517 | if not private_keys: 518 | return 519 | 520 | w3 = connect_web3(language) 521 | print() 522 | 523 | tx_count = get_tx_count(language) 524 | amount = get_amount(language) 525 | print_separator() 526 | 527 | while True: 528 | print_border(LANG[language]['select_tx_type'], Fore.YELLOW) 529 | print(f"{Fore.GREEN} ├─ {LANG[language]['random_option']}{Style.RESET_ALL}") 530 | print(f"{Fore.GREEN} └─ {LANG[language]['file_option']}{Style.RESET_ALL}") 531 | choice = input(f"{Fore.YELLOW} > {LANG[language]['choice_prompt']}{Style.RESET_ALL}").strip() 532 | 533 | if choice in ['1', '2']: 534 | break 535 | print(f"{Fore.RED} ✖ {LANG[language]['invalid_choice']}{Style.RESET_ALL}") 536 | print() 537 | 538 | successful_txs = 0 539 | total_wallets = len(private_keys) 540 | failed_attempts = 0 541 | CONFIG['TOTAL_WALLETS'] = total_wallets 542 | CONFIG['MAX_CONCURRENCY'] = min(CONFIG['MAX_CONCURRENCY'], total_wallets) 543 | 544 | random.shuffle(private_keys) 545 | semaphore = asyncio.Semaphore(CONFIG['MAX_CONCURRENCY']) 546 | async def limited_task(index, profile_num, private_key, proxy): 547 | nonlocal successful_txs, failed_attempts 548 | async with semaphore: 549 | result = await process_wallet(index, profile_num, private_key, proxy, w3, choice, tx_count, amount, language) 550 | if result > 0: 551 | successful_txs += result 552 | failed_attempts = 0 553 | else: 554 | failed_attempts += 1 555 | if failed_attempts >= 3: 556 | print(f"{Fore.RED} ✖ Dừng xử lý ví {profile_num}: Quá nhiều giao dịch thất bại liên tiếp{Style.RESET_ALL}") 557 | return 558 | if index < total_wallets - 1: 559 | delay = random.uniform(CONFIG['PAUSE_BETWEEN_ATTEMPTS'][0], CONFIG['PAUSE_BETWEEN_ATTEMPTS'][1]) 560 | print(f"{Fore.YELLOW} ℹ {LANG[language]['pausing']} {delay:.2f} {LANG[language]['seconds']}{Style.RESET_ALL}") 561 | await asyncio.sleep(delay) 562 | 563 | tasks = [] 564 | for i, (profile_num, private_key) in enumerate(private_keys): 565 | proxy = proxies[i % len(proxies)] if proxies else None 566 | tasks.append(limited_task(i, profile_num, private_key, proxy)) 567 | 568 | await asyncio.gather(*tasks, return_exceptions=True) 569 | 570 | total_txs = (tx_count if choice == '1' else len(load_addresses('address.txt', language) or [0])) * len(private_keys) 571 | print() 572 | print_border( 573 | f"{LANG[language]['completed'].format(successful=successful_txs, total=total_txs)}", 574 | Fore.GREEN 575 | ) 576 | print() 577 | 578 | if __name__ == "__main__": 579 | asyncio.run(run_sendtx('vi')) 580 | --------------------------------------------------------------------------------