├── assets └── img1.png ├── src ├── core │ ├── network │ │ └── rpc.js │ ├── db │ │ └── sqlite.js │ ├── contract │ │ ├── trwa_faucet.js │ │ ├── pool_proxy.js │ │ ├── trwa.js │ │ ├── usdc.js │ │ └── staking_pool.js │ ├── api │ │ └── api.js │ └── core.js └── utils │ ├── logger.js │ ├── twist.js │ ├── bless.js │ └── helper.js ├── accounts └── accounts_tmp.js ├── .gitignore ├── config ├── proxy_list_tmp.js └── config_tmp.js ├── LICENSE ├── package.json ├── setup.js ├── index.js └── README.md /assets/img1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Widiskel/trwa-launchpad-bot/HEAD/assets/img1.png -------------------------------------------------------------------------------- /src/core/network/rpc.js: -------------------------------------------------------------------------------- 1 | export class RPC { 2 | static CHAINID = 84532; 3 | static RPCURL = "https://sepolia.base.org"; 4 | static EXPLORER = "https://sepolia.basescan.org/"; 5 | static SYMBOL = "ETH"; 6 | } 7 | -------------------------------------------------------------------------------- /accounts/accounts_tmp.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Accounts list file 3 | * write your accounts like this 4 | * export const accountLists = [ 5 | * "YOUR SEED OR PRIVATE KEY", 6 | * "YOUR SEED OR PRIVATE KEY" 7 | * ]; 8 | * 9 | */ 10 | 11 | export const accountLists = []; 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | log/* 3 | test.js 4 | sessions/* 5 | accounts/accounts.js 6 | src/reference/* 7 | package-lock.json 8 | .DS_Store 9 | config/config.js 10 | config/proxy_list.js 11 | database.db 12 | app/accounts 13 | app/config 14 | # /index.js 15 | # build-config.json 16 | # /src -------------------------------------------------------------------------------- /config/proxy_list_tmp.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Example : 4 | * export const proxyList = [ 5 | "http://proxyUser:proxyPass@proxyHost:proxyPort", 6 | "http://proxyUser:proxyPass@proxyHost:proxyPort", 7 | "http://proxyUser:proxyPass@proxyHost:proxyPort", 8 | ]; 9 | */ 10 | 11 | export const proxyList = []; 12 | -------------------------------------------------------------------------------- /config/config_tmp.js: -------------------------------------------------------------------------------- 1 | export class Config { 2 | static TRWASTAKINGAMOUNT = 2500; //AMOUNT TRWA TO STAKE ON A POOL (FAUCET GIVE 15K TRWA CURRENTLY THERE 5 POOL) EX: 1000 3 | static GWEIPRICE = 1.5; //GWEI PRICE 4 | static WAITFORBLOCKCONFIRMATION = true; //IF TRUE AFTER TX EXECUTED BOT WILL WAIT TX TO BE MINED FIRST, IF FALSE AFTER TX EXECUTED BOT WILL CONTINUE TO NEXT TX 5 | static DISPLAY = "BLESS"; //TWIST or BLESS 6 | } 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Widianto Eka saputro 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "trwa-launchpad-bot.git", 3 | "version": "1.0.0", 4 | "type": "module", 5 | "description": "TRWA Bot is a powerful tool designed for you to automate TRWA Testnet airdrop.", 6 | "main": "index.js", 7 | "scripts": { 8 | "start": "node index.js", 9 | "setup": "node setup.js", 10 | "dev": "node index.js", 11 | "build": "rm -rf app && javascript-obfuscator --config build-config.json", 12 | "test": "node test.js" 13 | }, 14 | "keywords": [ 15 | "trwa-launchpad", 16 | "trwa-launchpad-bot.git", 17 | "evm-bot", 18 | "base-sepolia", 19 | "widiskel" 20 | ], 21 | "author": "Widiskel", 22 | "license": "MIT", 23 | "repository": { 24 | "type": "git", 25 | "url": "git+https://github.com/Widiskel/trwa-launchpad-bot.git.git" 26 | }, 27 | "dependencies": { 28 | "bip39": "^3.1.0", 29 | "blessed": "^0.1.81", 30 | "ethers": "^6.13.2", 31 | "https-proxy-agent": "^7.0.5", 32 | "input": "^1.0.1", 33 | "moment-timezone": "^0.5.45", 34 | "node-fetch": "^3.3.2", 35 | "sqlite": "^5.1.1", 36 | "sqlite3": "^5.1.7", 37 | "twisters": "^1.1.0", 38 | "winston": "^3.13.0" 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /setup.js: -------------------------------------------------------------------------------- 1 | import fs from "fs"; 2 | import path from "path"; 3 | 4 | async function fileExists(filePath) { 5 | try { 6 | await fs.promises.access(filePath); 7 | return true; 8 | } catch { 9 | return false; 10 | } 11 | } 12 | 13 | async function copyFile(src, dest) { 14 | try { 15 | if (await fileExists(dest)) { 16 | console.log(`File already exists at ${dest}, skipping copy.`); 17 | } else { 18 | await fs.promises.copyFile(src, dest); 19 | console.log(`Copied ${src} to ${dest}`); 20 | } 21 | } catch (err) { 22 | console.error(`Error copying file from ${src} to ${dest}:`, err); 23 | } 24 | } 25 | 26 | const copyOperations = [ 27 | { 28 | src: path.join("config", "config_tmp.js"), 29 | dest: path.join("config", "config.js"), 30 | }, 31 | { 32 | src: path.join("config", "proxy_list_tmp.js"), 33 | dest: path.join("config", "proxy_list.js"), 34 | }, 35 | { 36 | src: path.join("accounts", "accounts_tmp.js"), 37 | dest: path.join("accounts", "accounts.js"), 38 | }, 39 | ]; 40 | 41 | (async () => { 42 | console.log(`Copying Template File`); 43 | for (let { src, dest } of copyOperations) { 44 | await copyFile(src, dest); 45 | } 46 | console.log(`\nSetup Complete`); 47 | console.log( 48 | `Open and configure\n- accounts/accounts.js\n- config/config.js\n- config/proxy_list.js` 49 | ); 50 | })(); 51 | -------------------------------------------------------------------------------- /src/utils/logger.js: -------------------------------------------------------------------------------- 1 | import { createLogger, format, transports } from "winston"; 2 | import fs from "fs"; 3 | const { combine, timestamp, printf, colorize } = format; 4 | 5 | const customFormat = printf(({ level, message, timestamp }) => { 6 | return `${timestamp} [${level}]: ${message}`; 7 | }); 8 | 9 | class Logger { 10 | constructor() { 11 | this.logger = createLogger({ 12 | level: "debug", 13 | format: combine( 14 | timestamp({ 15 | format: "YYYY-MM-DD HH:mm:ss", 16 | }), 17 | colorize(), 18 | customFormat 19 | ), 20 | transports: [new transports.File({ filename: "log/app.log" })], 21 | exceptionHandlers: [new transports.File({ filename: "log/app.log" })], 22 | rejectionHandlers: [new transports.File({ filename: "log/app.log" })], 23 | }); 24 | } 25 | 26 | info(message) { 27 | this.logger.info(message); 28 | } 29 | 30 | warn(message) { 31 | this.logger.warn(message); 32 | } 33 | 34 | error(message) { 35 | this.logger.error(message); 36 | } 37 | 38 | debug(message) { 39 | this.logger.debug(message); 40 | } 41 | 42 | setLevel(level) { 43 | this.logger.level = level; 44 | } 45 | 46 | clear() { 47 | fs.truncate("log/app.log", 0, (err) => { 48 | if (err) { 49 | this.logger.error("Failed to clear the log file: " + err.message); 50 | } else { 51 | this.logger.info("Log file cleared"); 52 | } 53 | }); 54 | } 55 | } 56 | 57 | export default new Logger(); 58 | -------------------------------------------------------------------------------- /src/core/db/sqlite.js: -------------------------------------------------------------------------------- 1 | import sqlite3 from "sqlite3"; 2 | import { open } from "sqlite"; 3 | 4 | class SQLITE { 5 | async connectToDatabase() { 6 | return open({ 7 | filename: "./database.db", 8 | driver: sqlite3.Database, 9 | }); 10 | } 11 | 12 | async createTable() { 13 | const db = await this.connectToDatabase(); 14 | await db.exec(` 15 | CREATE TABLE IF NOT EXISTS tx_log ( 16 | id INTEGER PRIMARY KEY AUTOINCREMENT, 17 | address TEXT NOT NULL, 18 | tx_date DATE NOT NULL, 19 | type TEXT CHECK(type IN ('claim TRWA', 'mint USDC', 'stake', 'approve')) 20 | ) 21 | `); 22 | 23 | await db.close(); 24 | } 25 | 26 | async insertData(address, txDate, type) { 27 | const db = await this.connectToDatabase(); 28 | await db.run( 29 | "INSERT INTO tx_log (address, tx_date, type) VALUES (?, ?, ?)", 30 | [address, txDate, type] 31 | ); 32 | await db.close(); 33 | } 34 | 35 | async getTodayTxLog(address, type) { 36 | const db = await this.connectToDatabase(); 37 | const todayISO = new Date().toISOString().split("T")[0]; 38 | const todayTxLog = await db.all( 39 | ` 40 | SELECT * FROM tx_log 41 | WHERE DATE(tx_date) = ? AND address = ? AND type = ? 42 | ORDER BY tx_date DESC 43 | `, 44 | [todayISO, address, type] 45 | ); 46 | 47 | await db.close(); 48 | 49 | return todayTxLog; 50 | } 51 | async getTxLog(address, type) { 52 | const db = await this.connectToDatabase(); 53 | const txLog = await db.all( 54 | ` 55 | SELECT * FROM tx_log 56 | WHERE address = ? AND type = ? 57 | `, 58 | [address, type] 59 | ); 60 | 61 | await db.close(); 62 | 63 | return txLog; 64 | } 65 | } 66 | 67 | const sqlite = new SQLITE(); 68 | await sqlite.createTable(); 69 | 70 | export default sqlite; 71 | -------------------------------------------------------------------------------- /src/core/contract/trwa_faucet.js: -------------------------------------------------------------------------------- 1 | export class TRWAFAUCET { 2 | static CA = "0x219BA210Ef31613390df886763099D0eD35aa6B8"; 3 | static ABI = [ 4 | { 5 | inputs: [ 6 | { internalType: "contract IERC20", name: "_token", type: "address" }, 7 | ], 8 | stateMutability: "nonpayable", 9 | type: "constructor", 10 | }, 11 | { 12 | inputs: [], 13 | name: "claimAmount", 14 | outputs: [{ internalType: "uint256", name: "", type: "uint256" }], 15 | stateMutability: "view", 16 | type: "function", 17 | }, 18 | { 19 | inputs: [], 20 | name: "claimTokens", 21 | outputs: [], 22 | stateMutability: "nonpayable", 23 | type: "function", 24 | }, 25 | { 26 | inputs: [], 27 | name: "cooldownTime", 28 | outputs: [{ internalType: "uint256", name: "", type: "uint256" }], 29 | stateMutability: "view", 30 | type: "function", 31 | }, 32 | { 33 | inputs: [{ internalType: "address", name: "", type: "address" }], 34 | name: "lastClaimedTime", 35 | outputs: [{ internalType: "uint256", name: "", type: "uint256" }], 36 | stateMutability: "view", 37 | type: "function", 38 | }, 39 | { 40 | inputs: [], 41 | name: "maxClaimAmount", 42 | outputs: [{ internalType: "uint256", name: "", type: "uint256" }], 43 | stateMutability: "view", 44 | type: "function", 45 | }, 46 | { 47 | inputs: [{ internalType: "uint256", name: "amount", type: "uint256" }], 48 | name: "refillFaucet", 49 | outputs: [], 50 | stateMutability: "nonpayable", 51 | type: "function", 52 | }, 53 | { 54 | inputs: [], 55 | name: "token", 56 | outputs: [{ internalType: "contract IERC20", name: "", type: "address" }], 57 | stateMutability: "view", 58 | type: "function", 59 | }, 60 | { 61 | inputs: [{ internalType: "address", name: "", type: "address" }], 62 | name: "totalClaimed", 63 | outputs: [{ internalType: "uint256", name: "", type: "uint256" }], 64 | stateMutability: "view", 65 | type: "function", 66 | }, 67 | ]; 68 | } 69 | -------------------------------------------------------------------------------- /src/utils/twist.js: -------------------------------------------------------------------------------- 1 | import { Twisters } from "twisters"; 2 | import logger from "./logger.js"; 3 | import Core from "../core/core.js"; 4 | import { Config } from "../../config/config.js"; 5 | import { accountLists } from "../../accounts/accounts.js"; 6 | import sqlite from "../core/db/sqlite.js"; 7 | import { Helper } from "./helper.js"; 8 | 9 | export class Twist { 10 | constructor() { 11 | /** @type {Twisters}*/ 12 | this.twisters = new Twisters(); 13 | } 14 | 15 | /** 16 | * @param {string} acc 17 | * @param {Core} core 18 | * @param {string} msg 19 | * @param {string} delay 20 | */ 21 | async log(msg = "", acc = "", core = new Core(), delay) { 22 | const account = accountLists.find((item) => item == acc); 23 | const accIdx = accountLists.indexOf(account); 24 | if (delay == undefined) { 25 | logger.info(`Account ${accIdx + 1} - ${msg}`); 26 | delay = "-"; 27 | } 28 | 29 | const address = core.address ?? "-"; 30 | 31 | const balance = core.balance ?? {}; 32 | const eth = balance.ETH ?? "-"; 33 | const trwa = balance.TRWA ?? "-"; 34 | const usdc = balance.USDC ?? "-"; 35 | 36 | const todayTrwa = (await sqlite.getTodayTxLog(address, "claim TRWA")) 37 | .length; 38 | const todayUsdc = (await sqlite.getTodayTxLog(address, "mint USDC")).length; 39 | const todayStake = (await sqlite.getTodayTxLog(address, "stake")).length; 40 | 41 | let spinnerData = { 42 | msg, 43 | delay, 44 | address, 45 | eth, 46 | trwa, 47 | usdc, 48 | todayTrwa, 49 | todayUsdc, 50 | todayStake, 51 | }; 52 | 53 | let content; 54 | 55 | content = ` 56 | ================== Account ${accIdx + 1} ================= 57 | ${Helper.spinnerContent(spinnerData)} 58 | ============================================== 59 | `; 60 | 61 | this.twisters.put(account, { 62 | text: content, 63 | }); 64 | } 65 | /** 66 | * @param {string} msg 67 | */ 68 | info(msg = "") { 69 | this.twisters.put(2, { 70 | text: ` 71 | ============================================== 72 | Info : ${msg} 73 | ==============================================`, 74 | }); 75 | return; 76 | } 77 | 78 | clearInfo() { 79 | this.twisters.remove(2); 80 | } 81 | 82 | clear(acc) { 83 | this.twisters.remove(acc); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/core/contract/pool_proxy.js: -------------------------------------------------------------------------------- 1 | export class POOLPROXY { 2 | static CA = "0xB4D0040133EB541e80DE9564C9392cb43dBFce13"; 3 | static ABI = [ 4 | { 5 | inputs: [ 6 | { 7 | internalType: "address", 8 | name: "_logic", 9 | type: "address", 10 | }, 11 | { 12 | internalType: "address", 13 | name: "admin_", 14 | type: "address", 15 | }, 16 | { 17 | internalType: "bytes", 18 | name: "_data", 19 | type: "bytes", 20 | }, 21 | ], 22 | stateMutability: "payable", 23 | type: "constructor", 24 | }, 25 | { 26 | anonymous: false, 27 | inputs: [ 28 | { 29 | indexed: false, 30 | internalType: "address", 31 | name: "previousAdmin", 32 | type: "address", 33 | }, 34 | { 35 | indexed: false, 36 | internalType: "address", 37 | name: "newAdmin", 38 | type: "address", 39 | }, 40 | ], 41 | name: "AdminChanged", 42 | type: "event", 43 | }, 44 | { 45 | anonymous: false, 46 | inputs: [ 47 | { 48 | indexed: true, 49 | internalType: "address", 50 | name: "beacon", 51 | type: "address", 52 | }, 53 | ], 54 | name: "BeaconUpgraded", 55 | type: "event", 56 | }, 57 | { 58 | anonymous: false, 59 | inputs: [ 60 | { 61 | indexed: true, 62 | internalType: "address", 63 | name: "implementation", 64 | type: "address", 65 | }, 66 | ], 67 | name: "Upgraded", 68 | type: "event", 69 | }, 70 | { 71 | stateMutability: "payable", 72 | type: "fallback", 73 | }, 74 | { 75 | inputs: [], 76 | name: "admin", 77 | outputs: [ 78 | { 79 | internalType: "address", 80 | name: "admin_", 81 | type: "address", 82 | }, 83 | ], 84 | stateMutability: "nonpayable", 85 | type: "function", 86 | }, 87 | { 88 | inputs: [ 89 | { 90 | internalType: "address", 91 | name: "newAdmin", 92 | type: "address", 93 | }, 94 | ], 95 | name: "changeAdmin", 96 | outputs: [], 97 | stateMutability: "nonpayable", 98 | type: "function", 99 | }, 100 | { 101 | inputs: [], 102 | name: "implementation", 103 | outputs: [ 104 | { 105 | internalType: "address", 106 | name: "implementation_", 107 | type: "address", 108 | }, 109 | ], 110 | stateMutability: "nonpayable", 111 | type: "function", 112 | }, 113 | { 114 | inputs: [ 115 | { 116 | internalType: "address", 117 | name: "newImplementation", 118 | type: "address", 119 | }, 120 | ], 121 | name: "upgradeTo", 122 | outputs: [], 123 | stateMutability: "nonpayable", 124 | type: "function", 125 | }, 126 | { 127 | inputs: [ 128 | { 129 | internalType: "address", 130 | name: "newImplementation", 131 | type: "address", 132 | }, 133 | { 134 | internalType: "bytes", 135 | name: "data", 136 | type: "bytes", 137 | }, 138 | ], 139 | name: "upgradeToAndCall", 140 | outputs: [], 141 | stateMutability: "payable", 142 | type: "function", 143 | }, 144 | { 145 | stateMutability: "payable", 146 | type: "receive", 147 | }, 148 | ]; 149 | } 150 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | import { accountLists } from "./accounts/accounts.js"; 2 | import { Config } from "./config/config.js"; 3 | import { proxyList } from "./config/proxy_list.js"; 4 | import Core from "./src/core/core.js"; 5 | import sqlite from "./src/core/db/sqlite.js"; 6 | import { Helper } from "./src/utils/helper.js"; 7 | import logger from "./src/utils/logger.js"; 8 | 9 | async function operation(acc, proxy) { 10 | const core = new Core(acc, proxy); 11 | try { 12 | await core.connectWallet(); 13 | await core.getBalance(); 14 | await core.conectRwaDapps(); 15 | 16 | await core.claimTrwa(); 17 | await core.mintUsdc(); 18 | 19 | await core.getStakingPoolList(); 20 | const targetPool = core.targetPool; 21 | for (const item of core.stakingPool.filter( 22 | (item) => 23 | targetPool.includes(item.pool_address) && item.staking_type == "linear" 24 | )) { 25 | if (core.balance.TRWA < Config.TRWASTAKINGAMOUNT) { 26 | await Helper.delay( 27 | 3000, 28 | acc, 29 | `Current TRWA amount is ${core.balance.TRWA} TRWA less than configuration staking amount ${Config.TRWASTAKINGAMOUNT} TRWA`, 30 | core 31 | ); 32 | break; 33 | } 34 | await core.stake(item); 35 | } 36 | 37 | const delay = 60000 * 60; 38 | const account = accountLists.find((item) => item == acc); 39 | const accIdx = accountLists.indexOf(account); 40 | await Helper.delay( 41 | delay, 42 | acc, 43 | `Account ${accIdx + 1} Processing Done, Delaying for ${Helper.msToTime( 44 | delay 45 | )}`, 46 | core 47 | ); 48 | await operation(acc, proxy); 49 | } catch (error) { 50 | let account = acc; 51 | if (error.message) { 52 | await Helper.delay( 53 | 10000, 54 | acc, 55 | `Error : ${error.message}, Retry again after 10 Second`, 56 | core 57 | ); 58 | } else { 59 | await Helper.delay( 60 | 10000, 61 | acc, 62 | `Error :${JSON.stringify(error)}, Retry again after 10 Second`, 63 | core 64 | ); 65 | } 66 | 67 | await operation(account, proxy); 68 | } 69 | } 70 | 71 | async function startBot() { 72 | return new Promise(async (resolve, reject) => { 73 | try { 74 | logger.info(`BOT STARTED`); 75 | if (accountLists.length == 0) 76 | throw Error("Please input your account first on accounts.js file"); 77 | 78 | if (proxyList.length != accountLists.length && proxyList.length != 0) 79 | throw Error( 80 | `You Have ${accountLists.length} Accounts But Provide ${proxyList.length}` 81 | ); 82 | 83 | const promiseList = []; 84 | 85 | for (const acc of accountLists) { 86 | const accIdx = accountLists.indexOf(acc); 87 | const proxy = proxyList[accIdx]; 88 | 89 | promiseList.push(operation(acc, proxy)); 90 | } 91 | 92 | await sqlite.createTable(); 93 | await Promise.all(promiseList); 94 | resolve(); 95 | } catch (error) { 96 | logger.info(`BOT STOPPED`); 97 | logger.error(JSON.stringify(error)); 98 | reject(error); 99 | } 100 | }); 101 | } 102 | 103 | (async () => { 104 | try { 105 | logger.clear(); 106 | logger.info(""); 107 | logger.info("Application Started"); 108 | console.log("BASE RWA BOT"); 109 | console.log(); 110 | console.log("By : Widiskel"); 111 | console.log("Follow On : https://github.com/Widiskel"); 112 | console.log("Join Channel : https://t.me/skeldrophunt"); 113 | console.log("Dont forget to run git pull to keep up to date"); 114 | console.log(); 115 | console.log(); 116 | Helper.showSkelLogo(); 117 | await startBot(); 118 | } catch (error) { 119 | console.log("Error During executing bot", error); 120 | await startBot(); 121 | } 122 | })(); 123 | -------------------------------------------------------------------------------- /src/core/contract/trwa.js: -------------------------------------------------------------------------------- 1 | export class TRWA { 2 | static CA = "0x66b43eF7f5316fA62CbEB2D9C2a66c57d8d74792"; 3 | static ABI = [ 4 | { 5 | type: "function", 6 | selector: "0x42966c68", 7 | sig: "burn(uint256)", 8 | name: "burn", 9 | constant: false, 10 | payable: false, 11 | inputs: [ 12 | { 13 | type: "uint256", 14 | name: "", 15 | }, 16 | ], 17 | }, 18 | { 19 | inputs: [ 20 | { 21 | internalType: "address", 22 | name: "account", 23 | type: "address", 24 | }, 25 | ], 26 | name: "balanceOf", 27 | outputs: [ 28 | { 29 | internalType: "uint256", 30 | name: "", 31 | type: "uint256", 32 | }, 33 | ], 34 | stateMutability: "view", 35 | type: "function", 36 | }, 37 | { 38 | type: "function", 39 | selector: "0x79cc6790", 40 | sig: "burnFrom(address,uint256)", 41 | name: "burnFrom", 42 | constant: false, 43 | payable: false, 44 | inputs: [ 45 | { 46 | type: "address", 47 | name: "", 48 | }, 49 | { 50 | type: "uint256", 51 | name: "", 52 | }, 53 | ], 54 | }, 55 | { 56 | type: "function", 57 | selector: "0x95d89b41", 58 | sig: "symbol()", 59 | name: "symbol", 60 | constant: false, 61 | payable: false, 62 | inputs: [], 63 | stateMutability: "view", 64 | }, 65 | { 66 | type: "function", 67 | selector: "0xa9059cbb", 68 | sig: "transfer(address,uint256)", 69 | name: "transfer", 70 | constant: false, 71 | payable: false, 72 | inputs: [ 73 | { 74 | type: "address", 75 | name: "", 76 | }, 77 | { 78 | type: "uint256", 79 | name: "", 80 | }, 81 | ], 82 | }, 83 | { 84 | type: "function", 85 | selector: "0xdd62ed3e", 86 | sig: "allowance(address,address)", 87 | name: "allowance", 88 | constant: false, 89 | payable: false, 90 | inputs: [ 91 | { 92 | type: "address", 93 | name: "", 94 | }, 95 | { 96 | type: "address", 97 | name: "", 98 | }, 99 | ], 100 | stateMutability: "view", 101 | }, 102 | { 103 | type: "function", 104 | selector: "0x06fdde03", 105 | sig: "name()", 106 | name: "name", 107 | constant: false, 108 | payable: false, 109 | inputs: [], 110 | }, 111 | { 112 | type: "function", 113 | selector: "0x095ea7b3", 114 | sig: "approve(address,uint256)", 115 | name: "approve", 116 | constant: false, 117 | payable: false, 118 | inputs: [ 119 | { 120 | type: "address", 121 | name: "", 122 | }, 123 | { 124 | type: "uint256", 125 | name: "", 126 | }, 127 | ], 128 | }, 129 | { 130 | type: "function", 131 | selector: "0x18160ddd", 132 | sig: "totalSupply()", 133 | name: "totalSupply", 134 | constant: false, 135 | payable: false, 136 | inputs: [], 137 | stateMutability: "view", 138 | }, 139 | { 140 | type: "function", 141 | selector: "0x23b872dd", 142 | sig: "transferFrom(address,address,uint256)", 143 | name: "transferFrom", 144 | constant: false, 145 | payable: false, 146 | inputs: [ 147 | { 148 | type: "address", 149 | name: "", 150 | }, 151 | { 152 | type: "address", 153 | name: "", 154 | }, 155 | { 156 | type: "uint256", 157 | name: "", 158 | }, 159 | ], 160 | }, 161 | { 162 | type: "function", 163 | selector: "0x313ce567", 164 | sig: "decimals()", 165 | name: "decimals", 166 | constant: false, 167 | payable: false, 168 | inputs: [], 169 | stateMutability: "view", 170 | }, 171 | ]; 172 | } 173 | -------------------------------------------------------------------------------- /src/core/api/api.js: -------------------------------------------------------------------------------- 1 | import { HttpsProxyAgent } from "https-proxy-agent"; 2 | import fetch from "node-fetch"; 3 | import { Helper } from "../../utils/helper.js"; 4 | import logger from "../../utils/logger.js"; 5 | 6 | export class API { 7 | constructor(url, proxy) { 8 | this.url = url; 9 | this.proxy = proxy; 10 | this.ua = Helper.randomUserAgent(); 11 | } 12 | 13 | generateHeaders(token = this.query) { 14 | const headers = { 15 | Accept: "application/json, text/plain, */*", 16 | "Accept-Language": "en-US,en;q=0.9,id;q=0.8", 17 | "Content-Type": "application/json", 18 | "Sec-Fetch-Dest": "empty", 19 | "Sec-Fetch-Site": "same-site", 20 | "Sec-Fetch-Mode": "cors", 21 | "User-Agent": this.ua, 22 | }; 23 | 24 | if (token) { 25 | headers.Authorization = `Bearer ${ 26 | token.includes("Bearer ") ? token.replace("Bearer ", "") : token 27 | }`; 28 | } 29 | 30 | return headers; 31 | } 32 | 33 | replaceSensitiveData(str) { 34 | if (this.something) { 35 | if (typeof this.something === "string") { 36 | const regex = new RegExp(this.something, "g"); 37 | return str.replace(regex, "?????"); 38 | } else if (Array.isArray(this.something)) { 39 | this.something.forEach((sensitiveItem) => { 40 | const regex = new RegExp(sensitiveItem, "g"); 41 | str = str.replace(regex, "?????"); 42 | }); 43 | } 44 | } 45 | return str; 46 | } 47 | 48 | async fetch( 49 | endpoint, 50 | method, 51 | token, 52 | body = {}, 53 | additionalHeader = {}, 54 | isCustomUrl = false 55 | ) { 56 | const url = isCustomUrl ? endpoint : `${this.url}${endpoint}`; 57 | try { 58 | const headers = { 59 | ...this.generateHeaders(token), 60 | ...additionalHeader, 61 | }; 62 | const options = { 63 | headers, 64 | method, 65 | referer: this.origin + "/", 66 | }; 67 | 68 | logger.info( 69 | `${method} : ${this.replaceSensitiveData(url)} ${ 70 | this.proxy ? this.proxy : "" 71 | }` 72 | ); 73 | 74 | const dumHeader = headers; 75 | for (let key in dumHeader) { 76 | dumHeader[key] = this.replaceSensitiveData(dumHeader[key]); 77 | } 78 | logger.info(`Request Header : ${JSON.stringify(dumHeader)}`); 79 | 80 | if (method !== "GET") { 81 | options.body = `${JSON.stringify(body)}`; 82 | const dumBody = this.replaceSensitiveData(options.body); 83 | logger.info(`Request Body : ${dumBody}`); 84 | } 85 | 86 | if (this.proxy) { 87 | options.agent = new HttpsProxyAgent(this.proxy, { 88 | rejectUnauthorized: false, 89 | }); 90 | } 91 | const res = await fetch(url, options); 92 | logger.info(`Response : ${res.status} ${res.statusText}`); 93 | 94 | if (res.ok || res.status == 400 || res.status == 403) { 95 | const contentType = res.headers.get("content-type"); 96 | let data; 97 | if (contentType && contentType.includes("application/json")) { 98 | data = await res.json(); 99 | data.status = res.status; 100 | } else { 101 | data = { 102 | status: res.status, 103 | message: await res.text(), 104 | }; 105 | } 106 | 107 | if (res.ok) data.status = 200; 108 | let responseData = JSON.stringify(data); 109 | responseData = this.replaceSensitiveData(responseData); 110 | if (responseData.length > 200) { 111 | responseData = responseData.substring(0, 200) + "..."; 112 | } 113 | 114 | logger.info(`Response Data : ${responseData}`); 115 | return data; 116 | } else { 117 | throw res; 118 | } 119 | } catch (err) { 120 | if (err.status) { 121 | if (err.status == 404 || err.status == 503) { 122 | console.error(`Detect API Change Stopping bot`); 123 | throw Error(`Detect API Change Stopping bot`); 124 | } 125 | throw Error(`${err.response.status} - ${err.response.statusText}`); 126 | } 127 | if (err.response) { 128 | throw Error(`${err.response.status} - ${err.response.statusText}`); 129 | } 130 | throw Error(`${err.message}`); 131 | } 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /src/utils/bless.js: -------------------------------------------------------------------------------- 1 | import blessed from "blessed"; 2 | import logger from "./logger.js"; 3 | import Core from "../core/core.js"; 4 | import { Helper } from "./helper.js"; 5 | import { accountLists } from "../../accounts/accounts.js"; 6 | import sqlite from "../core/db/sqlite.js"; 7 | 8 | export class Bless { 9 | constructor() { 10 | this.screen = blessed.screen({ 11 | smartCSR: true, 12 | }); 13 | 14 | this.screen.title = "SKEL DROP HUNT"; 15 | this.titleBox = blessed.box({ 16 | top: 0, 17 | left: "center", 18 | width: "shrink", 19 | height: 2, 20 | tags: true, 21 | content: `{center}BASE RWA TESTNET BOT{/center} 22 | By: Widiskel`, 23 | style: { 24 | fg: "white", 25 | bold: true, 26 | }, 27 | }); 28 | this.screen.append(this.titleBox); 29 | this.subTitle = blessed.box({ 30 | top: 1, 31 | left: "center", 32 | width: "shrink", 33 | height: 2, 34 | tags: true, 35 | content: `By: Widiskel - Skel Drop hunt (https://t.me/skeldrophunt)`, 36 | style: { 37 | fg: "white", 38 | bold: true, 39 | }, 40 | }); 41 | this.screen.append(this.subTitle); 42 | this.tabList = blessed.box({ 43 | top: 5, 44 | left: "center", 45 | width: "100%", 46 | height: 3, 47 | tags: true, 48 | style: { 49 | fg: "white", 50 | }, 51 | }); 52 | this.screen.append(this.tabList); 53 | this.hintBox = blessed.box({ 54 | bottom: 0, 55 | left: "center", 56 | width: "100%", 57 | height: 3, 58 | tags: true, 59 | content: 60 | "{center}Use '->'(arrow right) and '<-'(arrow left) to switch between tabs{/center}", 61 | style: { 62 | fg: "white", 63 | }, 64 | }); 65 | this.screen.append(this.hintBox); 66 | this.infoBox = blessed.box({ 67 | bottom: 3, 68 | left: "center", 69 | width: "100%", 70 | height: 3, 71 | tags: true, 72 | content: "", 73 | style: { 74 | fg: "white", 75 | // bg: "black", 76 | }, 77 | }); 78 | this.screen.append(this.infoBox); 79 | this.tabs = []; 80 | this.currentTabIndex = 0; 81 | 82 | accountLists.forEach((account, idx) => { 83 | const tab = this.createAccountTab(`Account ${idx + 1}`); 84 | this.tabs.push(tab); 85 | this.screen.append(tab); 86 | tab.hide(); 87 | }); 88 | 89 | if (this.tabs.length > 0) { 90 | this.tabs[0].show(); 91 | } 92 | 93 | this.renderTabList(); 94 | 95 | this.screen.key(["q", "C-c"], () => { 96 | return process.exit(0); 97 | }); 98 | 99 | this.screen.key(["left", "right"], (ch, key) => { 100 | if (key.name === "right") { 101 | this.switchTab((this.currentTabIndex + 1) % this.tabs.length); 102 | } else if (key.name === "left") { 103 | this.switchTab( 104 | (this.currentTabIndex - 1 + this.tabs.length) % this.tabs.length 105 | ); 106 | } 107 | }); 108 | 109 | this.screen.render(); 110 | } 111 | 112 | createAccountTab(title) { 113 | return blessed.box({ 114 | label: title, 115 | top: 6, 116 | left: 0, 117 | width: "100%", 118 | height: "shrink", 119 | border: { 120 | type: "line", 121 | }, 122 | style: { 123 | fg: "white", 124 | border: { 125 | fg: "#f0f0f0", 126 | }, 127 | }, 128 | tags: true, 129 | }); 130 | } 131 | 132 | renderTabList() { 133 | let tabContent = ""; 134 | accountLists.forEach((account, idx) => { 135 | if (idx === this.currentTabIndex) { 136 | tabContent += `{blue-fg}{bold} Account ${idx + 1} {/bold}{/blue-fg} `; 137 | } else { 138 | tabContent += ` Account ${idx + 1} `; 139 | } 140 | }); 141 | this.tabList.setContent(`{center}${tabContent}{/center}`); 142 | this.screen.render(); 143 | } 144 | 145 | switchTab(index) { 146 | this.tabs[this.currentTabIndex].hide(); 147 | this.currentTabIndex = index; 148 | this.tabs[this.currentTabIndex].show(); 149 | this.renderTabList(); 150 | this.screen.render(); 151 | } 152 | 153 | async log(msg = "", acc = "", core = new Core(), delay) { 154 | const account = accountLists.find((item) => item == acc); 155 | const accIdx = accountLists.indexOf(account); 156 | 157 | if (delay === undefined) { 158 | logger.info(`Account ${accIdx + 1} - ${msg}`); 159 | delay = "-"; 160 | } 161 | let logContent; 162 | 163 | const address = core.address ?? "-"; 164 | const balance = core.balance ?? {}; 165 | const eth = balance.ETH ?? "-"; 166 | const trwa = balance.TRWA ?? "-"; 167 | const usdc = balance.USDC ?? "-"; 168 | 169 | const todayTrwa = (await sqlite.getTodayTxLog(address, "claim TRWA")) 170 | .length; 171 | const todayUsdc = (await sqlite.getTodayTxLog(address, "mint USDC")).length; 172 | const todayStake = (await sqlite.getTodayTxLog(address, "stake")).length; 173 | 174 | let spinnerData = { 175 | msg, 176 | delay, 177 | address, 178 | eth, 179 | trwa, 180 | usdc, 181 | todayTrwa, 182 | todayUsdc, 183 | todayStake, 184 | }; 185 | 186 | logContent = `${Helper.spinnerContent(spinnerData)}`; 187 | 188 | this.tabs[accIdx].setContent(logContent); 189 | this.screen.render(); 190 | } 191 | 192 | info(msg = "") { 193 | const formattedInfo = ` 194 | {center}Info: ${msg}{/center} 195 | `; 196 | this.infoBox.setContent(formattedInfo); 197 | this.screen.render(); 198 | } 199 | 200 | clearInfo() { 201 | this.infoBox.setContent(""); 202 | this.screen.render(); 203 | } 204 | } 205 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # TRWA Launchpad BOT 2 | ![TRWA](assets/img1.png) 3 | 4 | TRWA Bot is a powerful tool designed for you to automate TRWA Testnet airdrop. 5 | 6 | ## Table Of Contents 7 | - [TRWA Launchpad BOT](#trwa-launchpad-bot) 8 | - [Table Of Contents](#table-of-contents) 9 | - [Prerequisite](#prerequisite) 10 | - [Join My Telegram Channel](#join-my-telegram-channel) 11 | - [BOT FEATURE](#bot-feature) 12 | - [TRWA TESTNET INCENTIVE](#trwa-testnet-incentive) 13 | - [Setup \& Configure BOT](#setup--configure-bot) 14 | - [Linux](#linux) 15 | - [Windows](#windows) 16 | - [Update Bot](#update-bot) 17 | - [IMPORTANT NOTE (READ IT THIS IS NOT DECORATION)](#important-note-read-it-this-is-not-decoration) 18 | - [CONTRIBUTE](#contribute) 19 | - [SUPPORT](#support) 20 | 21 | ## Prerequisite 22 | - Git 23 | - Node JS (v22) 24 | - Base Sepolia ETH balance 25 | 26 | ## Join My Telegram Channel 27 | ``` 28 | 29 | ... 30 | .;:. 31 | .;ol,. 32 | .;ooc:' 33 | .. .;ooccc:'. .. 34 | .',....'cdxlccccc;.....,'. 35 | .;;..'';clolccccccc:,''..;;. 36 | ':c'..':cccccccccccccc;...'c:. 37 | ':cc,.'ccccccccccccccccc:..;cc:' 38 | ...:cc;.':cccccccccccccccccc:..:cc:... 39 | .;';cc;.':;;:cccccccccccccc:;;;'.;cc,,;. 40 | .cc':c:.',.....;cccccccccc;.....,..:c:'c: 41 | ,x:'cc;.,' .':cccccc:'. ',.;cc':x' 42 | lO,'cc;.;, .;cccc:. ,;.;cc';0l 43 | .o0;.;c;.,:'......',''''''......':,.;c;.:0l. 44 | .lxl,.;,..;c::::;:,. .,:;::::c;..,;.,oxl. 45 | .lkxOl.. ..'..;::'..''..'::;..'.. ..c0xkl. 46 | .cKMx. .;c:;:cc:;:c:. .xMKc. 47 | ;KX: ;o::l:;cc;o:. ;KK; 48 | :KK:. ,d,cd,'ol'o: .:0K: 49 | ;0NOl:;:loo;. ... .. .;ldlc::lkN0: 50 | .lONNNKOx0Xd,;;'.,:,lKKkk0XNN0o. 51 | .','.. .lX0doooodOXd. .','. 52 | .,okkddxkd;. 53 | 'oxxd;. 54 | ........................................ 55 | .OWo xNd lox xxl Ald xoc dakkkkkxsx. 56 | .OWo o0W cXW dM0 MMN lNK laddKMNkso. 57 | .kMKoxsNN oWX dW0 MMMWO lWK axM0 . 58 | .OMWXNaMX dM0 kM0 MMKxNXKW0 axMk . 59 | .OMk dWK oWX XWdx Mxx XMMO akMx . 60 | 'OWo dM0 'kNNXNNd DMD OWk aoWd . 61 | ........................................ 62 | 63 | ``` 64 | 65 | 66 | 67 | Anyway i create new telegram channel just for sharing bot or airdrop, join here 68 | [**https://t.me/skeldrophunt**](https://t.me/skeldrophunt). 69 | 70 | 71 | ## BOT FEATURE 72 | - Multi Account 73 | - Support PK & SEED 74 | - Auto Claim TRWA Faucet 75 | - Auto Claim USDC Faucet 76 | - Auto Stake 77 | 78 | 79 | ## TRWA TESTNET INCENTIVE 80 | #New Incentivized Testnet : RWA Launchpad 81 | Network : Base Sepolia 82 | 83 | Mint TRWA 84 | ➡️ Go to here : https://sepolia.basescan.org/address/0x219BA210Ef31613390df886763099D0eD35aa6B8#writeContract 85 | - Connect New Metamask 86 | - Click Claim Token 87 | 88 | 89 | Mint USDC 90 | ➡️ Go to here : https://base-sepolia.blockscout.com/address/0x6Ac3aB54Dc5019A2e57eCcb214337FF5bbD52897?tab=write_contract 91 | - Connect New Metamask 92 | - Click Mint 93 | - Enter Your Address 94 | - Input 1000000000 to mint USDC 95 | - Done 96 | 97 | 98 | ➡️ Go to : https://launch.rwa.inc/ 99 | ➖ Connect 100 | ➖ Click Account Setup 101 | ➖ Click Stake RWA 102 | ➖ Input 50% - 70% RWA 103 | ➖ Back to Account Setup 104 | ➖ Complete KYC Just 5 Minutes 105 | ➖ Done 106 | 107 | 📌 You can claim daily RWA token & stake for get Higher Tier 108 | 📌 The RWA world's first ecosystem utilizing the latest web3 technology to launch fractional assets on the blockchain : https://x.com/RWA_Inc_/status/1846189771795710099 109 | 📌 How to get ETH Base Sepolia ? 110 | - Bridge ETH Testnet here : 111 | - https://rinkeby.orbiter.finance/?source=Sepolia&dest=Base%20Sepolia&token=ETH 112 | - https://superbridge.app/base-sepolia 113 | - Done 114 | 115 | 116 | 117 | ## Setup & Configure BOT 118 | 119 | ### Linux 120 | 1. Clone project repo 121 | ``` 122 | git clone https://github.com/Widiskel/trwa-launchpad-bot.git && cd trwa-launchpad-bot 123 | ``` 124 | 2. Run 125 | ``` 126 | npm install && npm run setup 127 | ``` 128 | 3. Configure your accounts 129 | ``` 130 | nano accounts/accounts.js 131 | ``` 132 | 4. Configure the bot config 133 | ``` 134 | nano config/config.js 135 | ``` 136 | 5. Configure the proxy 137 | ``` 138 | nano config/proxy_list.js 139 | ``` 140 | 6. Run Bot 141 | ``` 142 | npm run start 143 | ``` 144 | 145 | ### Windows 146 | 1. Open your `Command Prompt` or `Power Shell`. 147 | 2. Clone project repo 148 | ``` 149 | git clone https://github.com/Widiskel/trwa-launchpad-bot.git && cd trwa-launchpad-bot 150 | ``` 151 | 3. Run 152 | ``` 153 | npm install && npm run setup 154 | ``` 155 | 5. Navigate to `trwa-launchpad-bot` directory. 156 | 6. Navigate to `accounts` folder and rename `accounts_tmp.js` to `accounts.js`. 157 | 7. Now open `acccounts.js` and setup your accounts. 158 | 8. Navigate to `config` and adjust the `config.js` as needed. 159 | 9. Also Configure proxy if you want to use proxy, by open `proxy_list.js`. (if you have 5 accounts, proxy is required) 160 | 10. Back to `trwa-launchpad-bot` directory. 161 | 11. To start the app open your `Command Prompt` or `Power Shell` 162 | 12. Run Bot 163 | ``` 164 | npm run start 165 | ``` 166 | 167 | ## Update Bot 168 | 169 | To update bot follow this step : 170 | 1. run 171 | ``` 172 | git pull 173 | ``` 174 | or 175 | ``` 176 | git pull --rebase 177 | ``` 178 | if error run 179 | ``` 180 | git stash && git pull 181 | ``` 182 | 2. run 183 | ``` 184 | npm update 185 | ``` 186 | 2. start the bot 187 | 188 | 189 | ## IMPORTANT NOTE (READ IT THIS IS NOT DECORATION) 190 | DWYOR & Always use a new wallet when running the bot, I am not responsible for any loss of assets. 191 | 192 | This bot is unencrypted , use it to learn about how to interact with evm smart contract, so when there is any new testnet or airdrop in the future , you can just develop based on this bot template, but please give me a credit. star and fork it. 193 | 194 | ## CONTRIBUTE 195 | 196 | Feel free to fork and contribute adding more feature thanks. 197 | 198 | ## SUPPORT 199 | 200 | want to support me for creating another bot ? 201 | **star** my repo or buy me a coffee on 202 | 203 | EVM : `0x1f0ea6e0b3590e1ab6c12ea0a24d3d0d9bf7707d` 204 | 205 | SOLANA : `3tE3Hs7P2wuRyVxyMD7JSf8JTAmEekdNsQWqAnayE1CN` 206 | -------------------------------------------------------------------------------- /src/core/contract/usdc.js: -------------------------------------------------------------------------------- 1 | export class USDC { 2 | static CA = "0x6Ac3aB54Dc5019A2e57eCcb214337FF5bbD52897"; 3 | static ABI = [ 4 | { 5 | inputs: [ 6 | { 7 | internalType: "string", 8 | name: "name", 9 | type: "string", 10 | }, 11 | { 12 | internalType: "string", 13 | name: "symbol", 14 | type: "string", 15 | }, 16 | { 17 | internalType: "uint256", 18 | name: "supply", 19 | type: "uint256", 20 | }, 21 | ], 22 | stateMutability: "nonpayable", 23 | type: "constructor", 24 | }, 25 | { 26 | inputs: [ 27 | { 28 | internalType: "address", 29 | name: "spender", 30 | type: "address", 31 | }, 32 | { 33 | internalType: "uint256", 34 | name: "allowance", 35 | type: "uint256", 36 | }, 37 | { 38 | internalType: "uint256", 39 | name: "needed", 40 | type: "uint256", 41 | }, 42 | ], 43 | name: "ERC20InsufficientAllowance", 44 | type: "error", 45 | }, 46 | { 47 | inputs: [ 48 | { 49 | internalType: "address", 50 | name: "sender", 51 | type: "address", 52 | }, 53 | { 54 | internalType: "uint256", 55 | name: "balance", 56 | type: "uint256", 57 | }, 58 | { 59 | internalType: "uint256", 60 | name: "needed", 61 | type: "uint256", 62 | }, 63 | ], 64 | name: "ERC20InsufficientBalance", 65 | type: "error", 66 | }, 67 | { 68 | inputs: [ 69 | { 70 | internalType: "address", 71 | name: "approver", 72 | type: "address", 73 | }, 74 | ], 75 | name: "ERC20InvalidApprover", 76 | type: "error", 77 | }, 78 | { 79 | inputs: [ 80 | { 81 | internalType: "address", 82 | name: "receiver", 83 | type: "address", 84 | }, 85 | ], 86 | name: "ERC20InvalidReceiver", 87 | type: "error", 88 | }, 89 | { 90 | inputs: [ 91 | { 92 | internalType: "address", 93 | name: "sender", 94 | type: "address", 95 | }, 96 | ], 97 | name: "ERC20InvalidSender", 98 | type: "error", 99 | }, 100 | { 101 | inputs: [ 102 | { 103 | internalType: "address", 104 | name: "spender", 105 | type: "address", 106 | }, 107 | ], 108 | name: "ERC20InvalidSpender", 109 | type: "error", 110 | }, 111 | { 112 | anonymous: false, 113 | inputs: [ 114 | { 115 | indexed: true, 116 | internalType: "address", 117 | name: "owner", 118 | type: "address", 119 | }, 120 | { 121 | indexed: true, 122 | internalType: "address", 123 | name: "spender", 124 | type: "address", 125 | }, 126 | { 127 | indexed: false, 128 | internalType: "uint256", 129 | name: "value", 130 | type: "uint256", 131 | }, 132 | ], 133 | name: "Approval", 134 | type: "event", 135 | }, 136 | { 137 | anonymous: false, 138 | inputs: [ 139 | { 140 | indexed: true, 141 | internalType: "address", 142 | name: "from", 143 | type: "address", 144 | }, 145 | { 146 | indexed: true, 147 | internalType: "address", 148 | name: "to", 149 | type: "address", 150 | }, 151 | { 152 | indexed: false, 153 | internalType: "uint256", 154 | name: "value", 155 | type: "uint256", 156 | }, 157 | ], 158 | name: "Transfer", 159 | type: "event", 160 | }, 161 | { 162 | inputs: [ 163 | { 164 | internalType: "address", 165 | name: "owner", 166 | type: "address", 167 | }, 168 | { 169 | internalType: "address", 170 | name: "spender", 171 | type: "address", 172 | }, 173 | ], 174 | name: "allowance", 175 | outputs: [ 176 | { 177 | internalType: "uint256", 178 | name: "", 179 | type: "uint256", 180 | }, 181 | ], 182 | stateMutability: "view", 183 | type: "function", 184 | }, 185 | { 186 | inputs: [ 187 | { 188 | internalType: "address", 189 | name: "spender", 190 | type: "address", 191 | }, 192 | { 193 | internalType: "uint256", 194 | name: "value", 195 | type: "uint256", 196 | }, 197 | ], 198 | name: "approve", 199 | outputs: [ 200 | { 201 | internalType: "bool", 202 | name: "", 203 | type: "bool", 204 | }, 205 | ], 206 | stateMutability: "nonpayable", 207 | type: "function", 208 | }, 209 | { 210 | inputs: [ 211 | { 212 | internalType: "address", 213 | name: "account", 214 | type: "address", 215 | }, 216 | ], 217 | name: "balanceOf", 218 | outputs: [ 219 | { 220 | internalType: "uint256", 221 | name: "", 222 | type: "uint256", 223 | }, 224 | ], 225 | stateMutability: "view", 226 | type: "function", 227 | }, 228 | { 229 | inputs: [], 230 | name: "decimals", 231 | outputs: [ 232 | { 233 | internalType: "uint8", 234 | name: "", 235 | type: "uint8", 236 | }, 237 | ], 238 | stateMutability: "pure", 239 | type: "function", 240 | }, 241 | { 242 | inputs: [ 243 | { 244 | internalType: "address", 245 | name: "to", 246 | type: "address", 247 | }, 248 | { 249 | internalType: "uint256", 250 | name: "amount", 251 | type: "uint256", 252 | }, 253 | ], 254 | name: "mint", 255 | outputs: [], 256 | stateMutability: "nonpayable", 257 | type: "function", 258 | }, 259 | { 260 | inputs: [], 261 | name: "name", 262 | outputs: [ 263 | { 264 | internalType: "string", 265 | name: "", 266 | type: "string", 267 | }, 268 | ], 269 | stateMutability: "view", 270 | type: "function", 271 | }, 272 | { 273 | inputs: [], 274 | name: "symbol", 275 | outputs: [ 276 | { 277 | internalType: "string", 278 | name: "", 279 | type: "string", 280 | }, 281 | ], 282 | stateMutability: "view", 283 | type: "function", 284 | }, 285 | { 286 | inputs: [], 287 | name: "totalSupply", 288 | outputs: [ 289 | { 290 | internalType: "uint256", 291 | name: "", 292 | type: "uint256", 293 | }, 294 | ], 295 | stateMutability: "view", 296 | type: "function", 297 | }, 298 | { 299 | inputs: [ 300 | { 301 | internalType: "address", 302 | name: "to", 303 | type: "address", 304 | }, 305 | { 306 | internalType: "uint256", 307 | name: "value", 308 | type: "uint256", 309 | }, 310 | ], 311 | name: "transfer", 312 | outputs: [ 313 | { 314 | internalType: "bool", 315 | name: "", 316 | type: "bool", 317 | }, 318 | ], 319 | stateMutability: "nonpayable", 320 | type: "function", 321 | }, 322 | { 323 | inputs: [ 324 | { 325 | internalType: "address", 326 | name: "from", 327 | type: "address", 328 | }, 329 | { 330 | internalType: "address", 331 | name: "to", 332 | type: "address", 333 | }, 334 | { 335 | internalType: "uint256", 336 | name: "value", 337 | type: "uint256", 338 | }, 339 | ], 340 | name: "transferFrom", 341 | outputs: [ 342 | { 343 | internalType: "bool", 344 | name: "", 345 | type: "bool", 346 | }, 347 | ], 348 | stateMutability: "nonpayable", 349 | type: "function", 350 | }, 351 | ]; 352 | } 353 | -------------------------------------------------------------------------------- /src/utils/helper.js: -------------------------------------------------------------------------------- 1 | import bip39 from "bip39"; 2 | import moment from "moment-timezone"; 3 | import { ethers } from "ethers"; 4 | import { Config } from "../../config/config.js"; 5 | import { Twist } from "./twist.js"; 6 | import { Bless } from "./bless.js"; 7 | import { RPC } from "../core/network/rpc.js"; 8 | 9 | export class Helper { 10 | static display = Config.DISPLAY; 11 | static twist = this.display == "TWIST" ? new Twist() : new Bless(); 12 | static spinnerContent = (data) => ` 13 | Address : ${data.address} 14 | Balance : ${data.eth} ${RPC.SYMBOL} 15 | ${data.trwa} TRWA 16 | ${data.usdc} USDC 17 | Today (TRWA|USDC|STAKE) : ${data.todayTrwa}x | ${data.todayUsdc}x | ${data.todayStake}x 18 | 19 | Status : ${data.msg} 20 | Delay : ${data.delay} 21 | `; 22 | static delay = (ms, acc, msg, obj) => { 23 | return new Promise(async (resolve) => { 24 | let remainingMilliseconds = ms; 25 | 26 | if (acc != undefined) { 27 | await this.twist.log( 28 | msg, 29 | acc, 30 | obj, 31 | `Delaying for ${this.msToTime(ms)}` 32 | ); 33 | } else { 34 | await this.twist.info(`Delaying for ${this.msToTime(ms)}`); 35 | } 36 | 37 | const interval = setInterval(async () => { 38 | remainingMilliseconds -= 1000; 39 | if (acc != undefined) { 40 | await this.twist.log( 41 | msg, 42 | acc, 43 | obj, 44 | `Delaying for ${this.msToTime(remainingMilliseconds)}` 45 | ); 46 | } else { 47 | await this.twist.info( 48 | `Delaying for ${this.msToTime(remainingMilliseconds)}` 49 | ); 50 | } 51 | 52 | if (remainingMilliseconds <= 0) { 53 | clearInterval(interval); 54 | resolve(); 55 | } 56 | }, 1000); 57 | 58 | setTimeout(async () => { 59 | clearInterval(interval); 60 | 61 | await this.twist.clearInfo(); 62 | 63 | if (acc) { 64 | await this.twist.log(msg, acc, obj); 65 | } 66 | resolve(); 67 | }, ms); 68 | }); 69 | }; 70 | 71 | static randomUserAgent() { 72 | const list_useragent = [ 73 | "Mozilla/5.0 (iPhone; CPU iPhone OS 17_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/125.0.6422.80 Mobile/15E148 Safari/604.1", 74 | "Mozilla/5.0 (iPhone; CPU iPhone OS 17_5_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 EdgiOS/125.2535.60 Mobile/15E148 Safari/605.1.15", 75 | "Mozilla/5.0 (Linux; Android 10; SM-G973F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.6422.113 Mobile Safari/537.36 EdgA/124.0.2478.104", 76 | "Mozilla/5.0 (Linux; Android 10; Pixel 3 XL) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.6422.113 Mobile Safari/537.36 EdgA/124.0.2478.104", 77 | "Mozilla/5.0 (Linux; Android 10; VOG-L29) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.6422.113 Mobile Safari/537.36 OPR/76.2.4027.73374", 78 | "Mozilla/5.0 (Linux; Android 10; SM-N975F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.6422.113 Mobile Safari/537.36 OPR/76.2.4027.73374", 79 | ]; 80 | return list_useragent[Math.floor(Math.random() * list_useragent.length)]; 81 | } 82 | 83 | static readTime(milliseconds) { 84 | const date = moment.unix(milliseconds); 85 | return date.format("YYYY-MM-DD HH:mm:ss"); 86 | } 87 | 88 | static getCurrentTimestamp() { 89 | const timestamp = moment().tz("Asia/Singapore").unix(); 90 | return timestamp.toString(); 91 | } 92 | 93 | static random(min, max) { 94 | const rand = Math.floor(Math.random() * (max - min + 1)) + min; 95 | return rand; 96 | } 97 | 98 | static randomFloat(min, max, fixed = 4) { 99 | const rand = Math.random() * (max - min) + min; 100 | return parseFloat(rand.toFixed(fixed)); 101 | } 102 | 103 | static msToTime(milliseconds) { 104 | const hours = Math.floor(milliseconds / (1000 * 60 * 60)); 105 | const remainingMillisecondsAfterHours = milliseconds % (1000 * 60 * 60); 106 | const minutes = Math.floor(remainingMillisecondsAfterHours / (1000 * 60)); 107 | const remainingMillisecondsAfterMinutes = 108 | remainingMillisecondsAfterHours % (1000 * 60); 109 | const seconds = Math.round(remainingMillisecondsAfterMinutes / 1000); 110 | 111 | return `${hours} Hours ${minutes} Minutes ${seconds} Seconds`; 112 | } 113 | 114 | static generateRandomString(length) { 115 | const characters = 116 | "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; 117 | let result = ""; 118 | const charactersLength = characters.length; 119 | for (let i = 0; i < length; i++) { 120 | result += characters.charAt(Math.floor(Math.random() * charactersLength)); 121 | } 122 | return result; 123 | } 124 | 125 | static serializeBigInt = (obj) => { 126 | return JSON.parse( 127 | JSON.stringify(obj, (key, value) => 128 | typeof value === "bigint" ? value.toString() : value 129 | ) 130 | ); 131 | }; 132 | 133 | static isMnemonic(input) { 134 | return bip39.validateMnemonic(input); 135 | } 136 | 137 | static isPrivateKey(input) { 138 | const data = input.replace(/^0x/, ""); 139 | const regex = /^[a-fA-F0-9]{64}$/; 140 | return regex.test(data); 141 | } 142 | 143 | static determineType(input) { 144 | if (this.isMnemonic(input)) { 145 | return "Secret Phrase"; 146 | } else if (this.isPrivateKey(input)) { 147 | return "Private Key"; 148 | } else { 149 | return "Unknown"; 150 | } 151 | } 152 | 153 | static generateNonce() { 154 | return ethers.hexlify(ethers.randomBytes(16)); 155 | } 156 | 157 | static isToday(date) { 158 | const lastCheckInDate = new Date(date); 159 | const today = new Date(); 160 | today.setHours(0, 0, 0, 0); 161 | const lastCheckInDateOnly = new Date(lastCheckInDate); 162 | lastCheckInDateOnly.setHours(0, 0, 0, 0); 163 | if (lastCheckInDateOnly.getTime() === today.getTime()) { 164 | return true; 165 | } else { 166 | return false; 167 | } 168 | } 169 | 170 | static findFunctionBySelector(targetSelector, ABIs) { 171 | for (const abi of ABIs) { 172 | for (const item of abi) { 173 | if (item.type === "function") { 174 | const functionSignature = `${item.name}(${item.inputs 175 | .map((input) => input.type) 176 | .join(",")})`; 177 | const selector = 178 | "0x" + 179 | ethers.keccak256(ethers.toUtf8Bytes(functionSignature)).slice(0, 8); 180 | 181 | // Check if the computed selector matches the target selector 182 | if (selector.includes(targetSelector)) { 183 | console.log(`Function found: ${functionSignature}`); 184 | return functionSignature; // or return item if you need the whole item 185 | } 186 | } 187 | } 188 | } 189 | console.log("Function not found"); 190 | return null; 191 | } 192 | 193 | static showSkelLogo() { 194 | console.log(` 195 | 196 | ... 197 | .;:. 198 | .;ol,. 199 | .;ooc:' 200 | .. .;ooccc:'. .. 201 | .',....'cdxlccccc;.....,'. 202 | .;;..'';clolccccccc:,''..;;. 203 | ':c'..':cccccccccccccc;...'c:. 204 | ':cc,.'ccccccccccccccccc:..;cc:' 205 | ...:cc;.':cccccccccccccccccc:..:cc:... 206 | .;';cc;.':;;:cccccccccccccc:;;;'.;cc,,;. 207 | .cc':c:.',.....;cccccccccc;.....,..:c:'c: 208 | ,x:'cc;.,' .':cccccc:'. ',.;cc':x' 209 | lO,'cc;.;, .;cccc:. ,;.;cc';0l 210 | .o0;.;c;.,:'......',''''''......':,.;c;.:0l. 211 | .lxl,.;,..;c::::;:,. .,:;::::c;..,;.,oxl. 212 | .lkxOl.. ..'..;::'..''..'::;..'.. ..c0xkl. 213 | .cKMx. .;c:;:cc:;:c:. .xMKc. 214 | ;KX: ;o::l:;cc;o:. ;KK; 215 | :KK:. ,d,cd,'ol'o: .:0K: 216 | ;0NOl:;:loo;. ... .. .;ldlc::lkN0: 217 | .lONNNKOx0Xd,;;'.,:,lKKkk0XNN0o. 218 | .','.. .lX0doooodOXd. .','. 219 | .,okkddxkd;. 220 | 'oxxd;. 221 | ........................................ 222 | .OWo xNd lox xxl Ald xoc dakkkkkxsx. 223 | .OWo o0W cXW dM0 MMN lNK laddKMNkso. 224 | .kMKoxsNN oWX dW0 MMMWO lWK axM0 . 225 | .OMWXNaMX dM0 kM0 MMKxNXKW0 axMk . 226 | .OMk dWK oWX XWdx Mxx XMMO akMx . 227 | 'OWo dM0 'kNNXNNd DMD OWk aoWd . 228 | ........................................ 229 | 230 | `); 231 | } 232 | } 233 | -------------------------------------------------------------------------------- /src/core/contract/staking_pool.js: -------------------------------------------------------------------------------- 1 | export class STAKINGPOOL { 2 | static CA = "0x30dB3bAcA6cfAD6C067203bCEFad3c867a72314D"; 3 | static ABI = [ 4 | { 5 | type: "function", 6 | selector: "0xa93460c8", 7 | sig: "LINEAR_MAXIMUM_DELAY_DURATION()", 8 | name: "LINEAR_MAXIMUM_DELAY_DURATION", 9 | constant: false, 10 | payable: false, 11 | inputs: [], 12 | }, 13 | { 14 | type: "function", 15 | selector: "0xbd6b47a5", 16 | sig: "linearFlexLockDuration()", 17 | name: "linearFlexLockDuration", 18 | constant: false, 19 | payable: false, 20 | inputs: [], 21 | }, 22 | { 23 | type: "function", 24 | selector: "0xc4690d3f", 25 | sig: "linearDeposit(uint256,uint128)", 26 | name: "linearDeposit", 27 | constant: false, 28 | payable: false, 29 | inputs: [ 30 | { 31 | type: "uint256", 32 | name: "", 33 | }, 34 | { 35 | type: "uint128", 36 | name: "", 37 | }, 38 | ], 39 | }, 40 | { 41 | type: "function", 42 | selector: "0xd5e35184", 43 | sig: "linearPoolInfo(uint256)", 44 | name: "linearPoolInfo", 45 | constant: false, 46 | payable: false, 47 | inputs: [ 48 | { 49 | type: "uint256", 50 | name: "", 51 | }, 52 | ], 53 | }, 54 | { 55 | type: "function", 56 | selector: "0xe67ec7e6", 57 | sig: "linearPendingWithdrawals(uint256,address)", 58 | name: "linearPendingWithdrawals", 59 | constant: false, 60 | payable: false, 61 | inputs: [ 62 | { 63 | type: "uint256", 64 | name: "", 65 | }, 66 | { 67 | type: "address", 68 | name: "", 69 | }, 70 | ], 71 | }, 72 | { 73 | type: "function", 74 | selector: "0xee212464", 75 | sig: "linearPoolLength()", 76 | name: "linearPoolLength", 77 | constant: false, 78 | payable: false, 79 | inputs: [], 80 | }, 81 | { 82 | type: "function", 83 | selector: "0xf2fde38b", 84 | sig: "transferOwnership(address)", 85 | name: "transferOwnership", 86 | constant: false, 87 | payable: false, 88 | inputs: [ 89 | { 90 | type: "address", 91 | name: "", 92 | }, 93 | ], 94 | }, 95 | { 96 | type: "function", 97 | selector: "0xfe81ef58", 98 | sig: "linearCompoundReward(uint256)", 99 | name: "linearCompoundReward", 100 | constant: false, 101 | payable: false, 102 | inputs: [ 103 | { 104 | type: "uint256", 105 | name: "", 106 | }, 107 | ], 108 | }, 109 | { 110 | type: "function", 111 | selector: "0x6dbe6cf1", 112 | sig: "linearSetAllowEmergencyWithdraw(bool)", 113 | name: "linearSetAllowEmergencyWithdraw", 114 | constant: false, 115 | payable: false, 116 | inputs: [ 117 | { 118 | type: "bool", 119 | name: "", 120 | }, 121 | ], 122 | }, 123 | { 124 | type: "function", 125 | selector: "0x715018a6", 126 | sig: "renounceOwnership()", 127 | name: "renounceOwnership", 128 | constant: false, 129 | payable: false, 130 | inputs: [], 131 | }, 132 | { 133 | type: "function", 134 | selector: "0x736a9cc2", 135 | sig: "linearPendingReward(uint256,address)", 136 | name: "linearPendingReward", 137 | constant: false, 138 | payable: false, 139 | inputs: [ 140 | { 141 | type: "uint256", 142 | name: "", 143 | }, 144 | { 145 | type: "address", 146 | name: "", 147 | }, 148 | ], 149 | }, 150 | 151 | { 152 | type: "function", 153 | selector: "0x8da5cb5b", 154 | sig: "owner()", 155 | name: "owner", 156 | constant: false, 157 | payable: false, 158 | inputs: [], 159 | }, 160 | { 161 | type: "function", 162 | selector: "0x97aba7f9", 163 | sig: "recoverSigner(bytes32,bytes)", 164 | name: "recoverSigner", 165 | constant: false, 166 | payable: false, 167 | inputs: [ 168 | { 169 | type: "bytes32", 170 | name: "", 171 | }, 172 | { 173 | type: "bytes", 174 | name: "", 175 | }, 176 | ], 177 | }, 178 | { 179 | type: "function", 180 | selector: "0x9880a9b1", 181 | sig: "linearClaimReward(uint256)", 182 | name: "linearClaimReward", 183 | constant: false, 184 | payable: false, 185 | inputs: [ 186 | { 187 | type: "uint256", 188 | name: "", 189 | }, 190 | ], 191 | }, 192 | { 193 | type: "function", 194 | selector: "0x32852f18", 195 | sig: "linearWithdraw(uint256,uint128)", 196 | name: "linearWithdraw", 197 | constant: false, 198 | payable: false, 199 | inputs: [ 200 | { 201 | type: "uint256", 202 | name: "", 203 | }, 204 | { 205 | type: "uint128", 206 | name: "", 207 | }, 208 | ], 209 | }, 210 | { 211 | type: "function", 212 | selector: "0x34b5143a", 213 | sig: "linearSetRewardDistributor(address)", 214 | name: "linearSetRewardDistributor", 215 | constant: false, 216 | payable: false, 217 | inputs: [ 218 | { 219 | type: "address", 220 | name: "", 221 | }, 222 | ], 223 | }, 224 | { 225 | type: "function", 226 | selector: "0x625aa6c9", 227 | sig: "linearBalanceOf(uint256,address)", 228 | name: "linearBalanceOf", 229 | constant: false, 230 | payable: false, 231 | inputs: [ 232 | { 233 | type: "uint256", 234 | name: "", 235 | }, 236 | { 237 | type: "address", 238 | name: "", 239 | }, 240 | ], 241 | }, 242 | { 243 | type: "function", 244 | selector: "0x66abdeae", 245 | sig: "__LinearPool_init(address)", 246 | name: "__LinearPool_init", 247 | constant: false, 248 | payable: false, 249 | inputs: [ 250 | { 251 | type: "address", 252 | name: "", 253 | }, 254 | ], 255 | }, 256 | { 257 | type: "function", 258 | selector: "0x66f74748", 259 | sig: "linearEmergencyWithdraw(uint256)", 260 | name: "linearEmergencyWithdraw", 261 | constant: false, 262 | payable: false, 263 | inputs: [ 264 | { 265 | type: "uint256", 266 | name: "", 267 | }, 268 | ], 269 | }, 270 | { 271 | type: "function", 272 | selector: "0x68931760", 273 | sig: "linearAddPool(uint128,uint128,uint128,uint64,uint128,uint128,uint128,uint128)", 274 | name: "linearAddPool", 275 | constant: false, 276 | payable: false, 277 | inputs: [ 278 | { 279 | type: "uint128", 280 | name: "", 281 | }, 282 | { 283 | type: "uint128", 284 | name: "", 285 | }, 286 | { 287 | type: "uint128", 288 | name: "", 289 | }, 290 | { 291 | type: "uint64", 292 | name: "", 293 | }, 294 | { 295 | type: "uint128", 296 | name: "", 297 | }, 298 | { 299 | type: "uint128", 300 | name: "", 301 | }, 302 | { 303 | type: "uint128", 304 | name: "", 305 | }, 306 | { 307 | type: "uint128", 308 | name: "", 309 | }, 310 | ], 311 | }, 312 | { 313 | type: "function", 314 | selector: "0x6a316d84", 315 | sig: "linearSetFlexLockDuration(uint128)", 316 | name: "linearSetFlexLockDuration", 317 | constant: false, 318 | payable: false, 319 | inputs: [ 320 | { 321 | type: "uint128", 322 | name: "", 323 | }, 324 | ], 325 | }, 326 | { 327 | type: "function", 328 | selector: "0x0d752918", 329 | sig: "linearRewardDistributor()", 330 | name: "linearRewardDistributor", 331 | constant: false, 332 | payable: false, 333 | inputs: [], 334 | }, 335 | { 336 | type: "function", 337 | selector: "0x1286fbcb", 338 | sig: "linearStakingData(uint256,address)", 339 | name: "linearStakingData", 340 | constant: false, 341 | payable: false, 342 | inputs: [ 343 | { 344 | type: "uint256", 345 | name: "", 346 | }, 347 | { 348 | type: "address", 349 | name: "", 350 | }, 351 | ], 352 | }, 353 | { 354 | type: "function", 355 | selector: "0x1b3df40d", 356 | sig: "linearSetPool(uint128,uint128,uint128,uint128,uint64,uint128)", 357 | name: "linearSetPool", 358 | constant: false, 359 | payable: false, 360 | inputs: [ 361 | { 362 | type: "uint128", 363 | name: "", 364 | }, 365 | { 366 | type: "uint128", 367 | name: "", 368 | }, 369 | { 370 | type: "uint128", 371 | name: "", 372 | }, 373 | { 374 | type: "uint128", 375 | name: "", 376 | }, 377 | { 378 | type: "uint64", 379 | name: "", 380 | }, 381 | { 382 | type: "uint128", 383 | name: "", 384 | }, 385 | ], 386 | }, 387 | { 388 | type: "function", 389 | selector: "0x20b66d94", 390 | sig: "linearAcceptedToken()", 391 | name: "linearAcceptedToken", 392 | constant: false, 393 | payable: false, 394 | inputs: [], 395 | }, 396 | { 397 | type: "function", 398 | selector: "0x213de833", 399 | sig: "linearTotalStaked(uint256)", 400 | name: "linearTotalStaked", 401 | constant: false, 402 | payable: false, 403 | inputs: [ 404 | { 405 | type: "uint256", 406 | name: "", 407 | }, 408 | ], 409 | }, 410 | { 411 | type: "function", 412 | selector: "0x276569e9", 413 | sig: "linearClaimPendingWithdraw(uint256)", 414 | name: "linearClaimPendingWithdraw", 415 | constant: false, 416 | payable: false, 417 | inputs: [ 418 | { 419 | type: "uint256", 420 | name: "", 421 | }, 422 | ], 423 | }, 424 | { 425 | type: "function", 426 | selector: "0x2fa8aaf2", 427 | sig: "linearAllowEmergencyWithdraw()", 428 | name: "linearAllowEmergencyWithdraw", 429 | constant: false, 430 | payable: false, 431 | inputs: [], 432 | }, 433 | ]; 434 | } 435 | -------------------------------------------------------------------------------- /src/core/core.js: -------------------------------------------------------------------------------- 1 | import { ethers } from "ethers"; 2 | import { Helper } from "../utils/helper.js"; 3 | import { API } from "./api/api.js"; 4 | import { RPC } from "./network/rpc.js"; 5 | import logger from "../utils/logger.js"; 6 | import { Config } from "../../config/config.js"; 7 | import { TRWAFAUCET } from "./contract/trwa_faucet.js"; 8 | import { TRWA } from "./contract/trwa.js"; 9 | import sqlite from "./db/sqlite.js"; 10 | import { USDC } from "./contract/usdc.js"; 11 | import { POOLPROXY } from "./contract/pool_proxy.js"; 12 | import { STAKINGPOOL } from "./contract/staking_pool.js"; 13 | 14 | export default class Core extends API { 15 | constructor(acc, proxy) { 16 | super("https://api.launch.rwa.inc", proxy); 17 | this.acc = acc; 18 | this.provider = new ethers.JsonRpcProvider(RPC.RPCURL, RPC.CHAINID); 19 | 20 | this.trwaFaucet = new ethers.Contract( 21 | TRWAFAUCET.CA, 22 | TRWAFAUCET.ABI, 23 | this.wallet 24 | ); 25 | this.usdc = new ethers.Contract(USDC.CA, USDC.ABI, this.provider); 26 | this.trwa = new ethers.Contract(TRWA.CA, TRWA.ABI, this.provider); 27 | 28 | this.targetPool = ["0xB4D0040133EB541e80DE9564C9392cb43dBFce13"]; 29 | this.proxyPool1 = new ethers.Contract( 30 | POOLPROXY.CA, 31 | POOLPROXY.ABI, 32 | this.provider 33 | ); 34 | this.pool1 = new ethers.Contract( 35 | STAKINGPOOL.CA, 36 | STAKINGPOOL.ABI, 37 | this.provider 38 | ); 39 | } 40 | 41 | async connectWallet() { 42 | try { 43 | if (!this.acc) { 44 | throw new Error("Please Set Up your wallet Private Key"); 45 | } 46 | const data = this.acc; 47 | await Helper.delay(500, this.acc, `Connecting to Account Wallet`, this); 48 | const type = Helper.determineType(data); 49 | logger.info(`Account Type : ${type}`); 50 | if (type == "Secret Phrase") { 51 | /** 52 | * @type {Wallet} 53 | */ 54 | this.wallet = ethers.Wallet.fromPhrase(data, this.provider); 55 | } else if (type == "Private Key") { 56 | /** 57 | * @type {Wallet} 58 | */ 59 | this.wallet = new ethers.Wallet(data.trim(), this.provider); 60 | } else { 61 | throw Error("Invalid account Secret Phrase or Private Key"); 62 | } 63 | this.address = this.wallet.address; 64 | await Helper.delay(500, this.acc, `Wallet connected..`, this); 65 | } catch (error) { 66 | await this.handleError(error); 67 | } 68 | } 69 | 70 | async getBalance(update = false) { 71 | try { 72 | if (!update) { 73 | await Helper.delay(500, this.acc, `Getting Wallet Balance`, this); 74 | } 75 | 76 | const ethBalance = ethers.formatEther( 77 | await this.provider.getBalance(this.wallet.address) 78 | ); 79 | const trwaBalance = ethers.formatEther( 80 | await this.trwa.balanceOf(this.address) 81 | ); 82 | const usdcBalance = ethers.formatUnits( 83 | await this.usdc.balanceOf(this.address), 84 | 6 85 | ); 86 | this.balance = { 87 | ETH: ethBalance, 88 | TRWA: trwaBalance, 89 | USDC: usdcBalance, 90 | }; 91 | if (update) await Helper.delay(500, this.acc, `Balance updated`, this); 92 | } catch (error) { 93 | await this.handleError(error); 94 | } 95 | } 96 | 97 | async claimTrwa() { 98 | try { 99 | await Helper.delay(2000, this.acc, `Try Claim TRWA Token`, this); 100 | 101 | const data = await this.trwaFaucet.claimTokens.populateTransaction(); 102 | const tx = await this.buildTxBody(data); 103 | await this.executeTx(tx); 104 | await sqlite.insertData( 105 | this.address, 106 | new Date().toISOString(), 107 | "claim TRWA" 108 | ); 109 | await Helper.delay( 110 | 2000, 111 | this.acc, 112 | `Successfully Claim TRWA Faucet`, 113 | this 114 | ); 115 | } catch (error) { 116 | await Helper.delay(3000, this.acc, error.message, this); 117 | } 118 | } 119 | async mintUsdc() { 120 | try { 121 | await Helper.delay(2000, this.acc, `Try Mint USDC Token`, this); 122 | 123 | const data = await this.usdc.mint.populateTransaction( 124 | this.address, 125 | 1000000000 126 | ); 127 | const tx = await this.buildTxBody(data); 128 | await this.executeTx(tx); 129 | await sqlite.insertData( 130 | this.address, 131 | new Date().toISOString(), 132 | "mint USDC" 133 | ); 134 | await Helper.delay(2000, this.acc, `Successfully Mint USDC`, this); 135 | } catch (error) { 136 | throw error; 137 | } 138 | } 139 | async stake(pool) { 140 | try { 141 | const approval = (await sqlite.getTxLog(this.address, "approve")).length; 142 | if (approval == 0) { 143 | const spender = pool.pool_address; 144 | await this.approveTokenSpend(TRWA.CA, TRWA.ABI, spender); 145 | } 146 | 147 | await Helper.delay( 148 | 2000, 149 | this.acc, 150 | `Try To Stake TRWA Token to Pools ${pool.pool_id} ${pool.title}`, 151 | this 152 | ); 153 | const poolAddress = pool.pool_address; 154 | if (poolAddress == (await this.proxyPool1.getAddress())) { 155 | let data = await this.pool1.linearDeposit.populateTransaction( 156 | pool.pool_id, 157 | ethers.parseUnits(Config.TRWASTAKINGAMOUNT.toString(), 18) 158 | ); 159 | data.to = await this.proxyPool1.getAddress(); 160 | const tx = await this.buildTxBody(data); 161 | await this.executeTx(tx); 162 | 163 | await Helper.delay( 164 | 3000, 165 | this.acc, 166 | `Successfully Stake ${Config.TRWASTAKINGAMOUNT} TRWA to Pool ${pool.pool_id} ${pool.title}`, 167 | this 168 | ); 169 | } else { 170 | await Helper.delay( 171 | 3000, 172 | this.acc, 173 | `This bot haven't provide Staking for this Pool`, 174 | this 175 | ); 176 | } 177 | await sqlite.insertData(this.address, new Date().toISOString(), "stake"); 178 | } catch (error) { 179 | await Helper.delay(3000, this.acc, error.message, this); 180 | } 181 | } 182 | 183 | async conectRwaDapps() { 184 | await Helper.delay(1000, this.acc, `Connecting to RWA Dapps`, this); 185 | const timestamp = Date.now(); 186 | const msg = `Launchpad User Signature`; 187 | logger.info(`Message to sign: ${msg}`); 188 | const signedMessage = await this.wallet.signMessage(msg); 189 | logger.info(`Signed Message: ${signedMessage}`); 190 | this.signatureMessage = signedMessage; 191 | await Helper.delay(1000, this.acc, `Connected To RWA Dapps`, this); 192 | } 193 | 194 | async getStakingPoolList() { 195 | try { 196 | await Helper.delay(1000, this.acc, `Getting Staking Pools`, this); 197 | const res = await this.fetch(`/staking-pool`, "GET"); 198 | if (res.status == 200) { 199 | this.stakingPool = res.data; 200 | await Helper.delay( 201 | 1000, 202 | this.acc, 203 | `Successfully Get Staking Pool`, 204 | this 205 | ); 206 | } else { 207 | throw res; 208 | } 209 | } catch (error) { 210 | await this.handleError(error); 211 | } 212 | } 213 | 214 | async buildTxBody(data) { 215 | const amountInWei = ethers.parseEther("0"); 216 | const nonce = await this.getOptimalNonce(); 217 | const gasLimit = await this.estimateGasWithRetry( 218 | data.to, 219 | amountInWei, 220 | data.data, 221 | true 222 | ); 223 | const tx = { 224 | to: data.to, 225 | gasLimit, 226 | gasPrice: ethers.parseUnits(Config.GWEIPRICE.toString(), "gwei"), 227 | nonce: nonce, 228 | data: data.data, 229 | }; 230 | return tx; 231 | } 232 | 233 | async approveTokenSpend(ca, abi, spender) { 234 | await Helper.delay( 235 | 2000, 236 | this.acc, 237 | `Approving Token to be spend on Staking Pool`, 238 | this 239 | ); 240 | const contractToApprove = new ethers.Contract(ca, abi, this.wallet); 241 | const tx = await contractToApprove.approve(spender, ethers.MaxUint256); 242 | await Helper.delay(2000, this.acc, `Token Approved`, this); 243 | const txRev = await tx.wait(); 244 | logger.info(`Tx Confirmed and Finalizing: ${JSON.stringify(txRev)}`); 245 | this.hash = txRev.hash; 246 | await Helper.delay( 247 | 5000, 248 | this.acc, 249 | `Approval Tx Executed \n${RPC.EXPLORER}tx/${txRev.hash}`, 250 | this 251 | ); 252 | } 253 | 254 | async executeTx(tx) { 255 | try { 256 | logger.info(`TX DATA ${JSON.stringify(Helper.serializeBigInt(tx))}`); 257 | await Helper.delay(500, this.acc, `Executing TX...`, this); 258 | const txRes = await this.wallet.sendTransaction(tx); 259 | if (Config.WAITFORBLOCKCONFIRMATION) { 260 | await Helper.delay( 261 | 500, 262 | this.acc, 263 | `Tx Executed Waiting For Block Confirmation...`, 264 | this 265 | ); 266 | const txRev = await txRes.wait(); 267 | logger.info(`Tx Confirmed and Finalizing: ${JSON.stringify(txRev)}`); 268 | this.hash = txRev.hash; 269 | await Helper.delay( 270 | 5000, 271 | this.acc, 272 | `Tx Executed \n${RPC.EXPLORER}tx/${txRev.hash}`, 273 | this 274 | ); 275 | } else { 276 | await Helper.delay( 277 | 5000, 278 | this.acc, 279 | `Tx Executed \n${RPC.EXPLORER}tx/${txRes.hash}`, 280 | this 281 | ); 282 | } 283 | await this.getBalance(true); 284 | } catch (error) { 285 | await this.handleError(error); 286 | } 287 | } 288 | 289 | async getOptimalNonce() { 290 | try { 291 | const latestNonce = await this.provider.getTransactionCount( 292 | this.wallet.address, 293 | "latest" 294 | ); 295 | const pendingNonce = await this.provider.getTransactionCount( 296 | this.wallet.address, 297 | "pending" 298 | ); 299 | const optimalNonce = 300 | pendingNonce > latestNonce ? pendingNonce : latestNonce; 301 | return optimalNonce; 302 | } catch (error) { 303 | await this.handleError(error); 304 | } 305 | } 306 | 307 | async estimateGasWithRetry( 308 | address, 309 | amount, 310 | rawdata, 311 | directThrow = false, 312 | retries = 3, 313 | delay = 3000 314 | ) { 315 | let error; 316 | 317 | for (let attempt = 0; attempt < retries; attempt++) { 318 | try { 319 | logger.info(`Estimating Gas for ${rawdata} TX`); 320 | const gasLimit = await this.provider.estimateGas({ 321 | from: this.wallet.address, 322 | to: address, 323 | value: amount, 324 | data: rawdata, 325 | }); 326 | return gasLimit; 327 | } catch (err) { 328 | if (directThrow) throw Error(err.shortMessage); 329 | await Helper.delay( 330 | delay, 331 | this.acc, 332 | `${err.shortMessage}... Attempt ${attempt + 1} of ${retries}`, 333 | this 334 | ); 335 | if (attempt === retries - 1) { 336 | throw Error(`Failed to estimate gas after ${retries} attempts.`); 337 | } 338 | } 339 | } 340 | } 341 | 342 | async handleError(error) { 343 | if (error.code) { 344 | if (error.code == 401) { 345 | throw Error(`Error ${error.msg}`); 346 | } else { 347 | await Helper.delay(3000, this.acc, `Error : ${error.msg}`, this); 348 | } 349 | } else { 350 | throw error; 351 | } 352 | } 353 | } 354 | --------------------------------------------------------------------------------