├── .gitignore ├── README.md ├── account_tmp.js ├── index.js ├── package.json ├── proxy_list_tmp.js └── src ├── api └── api.js ├── config ├── address_list.js └── config.js ├── core └── solana.js └── utils ├── helper.js ├── logger.js └── twist.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | account.js 3 | log/* 4 | package-lock.json 5 | proxy_list.js -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SONIC TX BOT 2 | 3 | Sonic TX bot for adding more tx on chain 4 | 5 | ## BOT FEATURE 6 | 7 | - Support PK and SEED 8 | - Proxy Support 9 | - Auto Check In 10 | - Auto TX until 100 Times 11 | - Auto Claim TX Milestone 12 | - Auto Opening Mystery Box 13 | - Support on testnet-v1 14 | 15 | ## PREREQUISITE 16 | 17 | - Git 18 | - Node JS > v18 19 | 20 | ## SETUP 21 | 22 | - run `git clone https://github.com/Widiskel/sonic-tx-bot.git` 23 | - run `cd sonic-tx-bot` 24 | - run `npm install` 25 | - run `cp account_tmp.js account.js && cp proxy_list_tmp.js proxy_list.js` 26 | - fill up account.js `nano account.js` fill with your account private key 27 | - fill up proxy_list.js `nano proxy_list.js` fill with your proxy list 28 | - npm run start 29 | 30 | ## CONFIGURATION 31 | 32 | im adding config file for you to configure, open `src config/config.js` and adjust config. Here some configurable variables. 33 | 34 | ```js 35 | export class Config { 36 | static sendAmount = 0.0001; //amount to send in sol 37 | static destAddress = addressList; //address destination list 38 | static maxRetry = 3; // max error retry for claiming 39 | } 40 | ``` 41 | 42 | to configure destination address list, open `src config/address_list.js` adjust the list with yours. the bot will pick random destination address from that list to send token or it will send to its own wallet address. 43 | 44 | ## HOW TO UPDATE 45 | 46 | to update just run `git pull` or if it failed because of unstaged commit, just run `git stash` and then `git pull`. after that do `npm install` or `npm update`. 47 | 48 | ## CONTRIBUTE 49 | 50 | Feel free to fork and contribute adding more feature thanks. 51 | 52 | ## NOTE 53 | 54 | Bot running using twister, so if you run multiple account maybe some account not showed on your terminal because it depens on your windows screen, but it still running. you can check on `app.log`. 55 | 56 | ## SUPPORT 57 | 58 | want to support me for creating another bot ? 59 | buy me a coffee on 60 | 61 | EVM : `0x0fd08d2d42ff086bf8c6d057d02d802bf217559a` 62 | 63 | SOLANA : `3tE3Hs7P2wuRyVxyMD7JSf8JTAmEekdNsQWqAnayE1CN` 64 | -------------------------------------------------------------------------------- /account_tmp.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * export const account = [ 4 | * "BACKPACK WALLET ACCOUNT PRIVATE KEY", 5 | * "BACKPACK WALLET ACCOUNT PRIVATE KEY", 6 | * "BACKPACK WALLET ACCOUNT PRIVATE KEY", 7 | * ]; 8 | * 9 | */ 10 | 11 | export const account = []; 12 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | import { account } from "./account.js"; 2 | import { proxyList } from "./proxy_list.js"; 3 | import { Solana } from "./src/core/solana.js"; 4 | import { Helper } from "./src/utils/helper.js"; 5 | import logger from "./src/utils/logger.js"; 6 | 7 | async function operation(acc, proxy) { 8 | const solana = new Solana(acc, proxy); 9 | try { 10 | await solana.connectWallet(); 11 | await solana.checkBalance(); 12 | if (solana.balance < 0.01) { 13 | throw Error("You need at least 0.01 SOL To Use This BOT"); 14 | } 15 | await solana.connect(); 16 | await Helper.delay(500, acc, `Getting Wallet Balance Information`, solana); 17 | await solana.getRewardInfo(); 18 | await solana.getDailyTx(); 19 | await solana.checkIn(); 20 | await Helper.delay(500, acc, `Starting Mass Tx`, solana); 21 | 22 | if (100 - solana.dailyTx.total_transactions > 0) { 23 | while (solana.dailyTx.total_transactions <= 100) { 24 | await solana.sendSolToAddress(acc); 25 | const randWait = Helper.random(1000, 3000); 26 | await Helper.delay(randWait, acc, "Delaying before do next tx", solana); 27 | } 28 | } 29 | 30 | await solana.getDailyTx(); 31 | 32 | const claimableStage = []; 33 | if (solana.dailyTx.total_transactions >= 10) { 34 | claimableStage.push(1); 35 | } 36 | if (solana.dailyTx.total_transactions >= 50) { 37 | claimableStage.push(2); 38 | } 39 | if (solana.dailyTx.total_transactions >= 100) { 40 | claimableStage.push(3); 41 | } 42 | 43 | for (const stage of claimableStage) { 44 | await solana.claimTxMilestone(stage); 45 | } 46 | 47 | await solana.getRewardInfo(); 48 | await Helper.delay( 49 | 500, 50 | acc, 51 | `Opening ${solana.reward.ring_monitor} Mystery box`, 52 | solana 53 | ); 54 | 55 | while (solana.reward.ring_monitor != 0) { 56 | await solana.claimMysteryBox().catch(async (err) => { 57 | if (err.message.includes("custom program error")) { 58 | await Helper.delay( 59 | 3000, 60 | acc, 61 | `Error while claiming mystery box, possible Sonic program error, skipping open box`, 62 | solana 63 | ); 64 | } 65 | }); 66 | await solana.getRewardInfo(); 67 | } 68 | 69 | await Helper.delay( 70 | 60000 * 60 * 24, 71 | acc, 72 | `Account Processing Complete, Delaying for 24 H`, 73 | solana 74 | ); 75 | } catch (error) { 76 | let msg = error.message; 77 | if (msg.includes("")) { 78 | msg = msg.split("")[0]; 79 | } 80 | await Helper.delay( 81 | 500, 82 | acc, 83 | `Error ${msg}, Retrying using Account ${ 84 | account.indexOf(acc) + 1 85 | } after 10 Second...`, 86 | solana 87 | ); 88 | 89 | logger.info(`Retrying using Account ${account.indexOf(acc) + 1}...`); 90 | logger.error(error); 91 | await Helper.delay(10000); 92 | await operation(acc, proxy); 93 | } 94 | } 95 | 96 | process.on("unhandledRejection", (reason) => { 97 | throw Error("Unhandled Exception : " + reason); 98 | }); 99 | 100 | async function startBot() { 101 | return new Promise(async (resolve, reject) => { 102 | try { 103 | logger.info(`BOT STARTED`); 104 | if (account.length == 0) 105 | throw Error("Please input your account first on account.js file"); 106 | 107 | if (proxyList.length != account.length && proxyList.length != 0) 108 | throw Error( 109 | `You Have ${account.length} Accounts But Provide ${proxyList.length}` 110 | ); 111 | 112 | const promiseList = []; 113 | 114 | for (const acc of account) { 115 | const accIdx = account.indexOf(acc); 116 | const proxy = proxyList[accIdx]; 117 | promiseList.push(operation(acc, proxy)); 118 | } 119 | 120 | await Promise.all(promiseList); 121 | resolve(); 122 | } catch (error) { 123 | logger.info(`BOT STOPPED`); 124 | logger.error(JSON.stringify(error)); 125 | reject(error); 126 | } 127 | }); 128 | } 129 | 130 | (async () => { 131 | try { 132 | logger.clear(); 133 | logger.info(""); 134 | logger.info("Application Started"); 135 | console.log("EVM TX DEPLOYER BOT"); 136 | console.log(); 137 | console.log("By : Widiskel"); 138 | console.log("Follow On : https://github.com/Widiskel"); 139 | console.log("Join Channel : https://t.me/skeldrophunt"); 140 | console.log("Dont forget to run git pull to keep up to date"); 141 | console.log(); 142 | console.log(); 143 | Helper.showSkelLogo(); 144 | await startBot(); 145 | } catch (error) { 146 | console.log("Error During executing bot", error); 147 | await startBot(); 148 | } 149 | })(); 150 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sonic", 3 | "version": "1.0.0", 4 | "type": "module", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "NODE_NO_WARNINGS=1 node index.js", 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "keywords": [], 11 | "author": "", 12 | "license": "ISC", 13 | "description": "", 14 | "dependencies": { 15 | "@solana/web3.js": "^1.93.0", 16 | "bs58": "^6.0.0", 17 | "https-proxy-agent": "^7.0.5", 18 | "node-fetch": "^3.3.2", 19 | "tweetnacl": "^1.0.3", 20 | "twisters": "^1.1.0", 21 | "winston": "^3.13.0" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /src/api/api.js: -------------------------------------------------------------------------------- 1 | import { Helper } from "../utils/helper.js"; 2 | import logger from "../utils/logger.js"; 3 | import { HttpsProxyAgent } from "https-proxy-agent"; 4 | import fetch from "node-fetch"; 5 | 6 | export class API { 7 | constructor(url, proxy) { 8 | this.url = url; 9 | this.ua = Helper.randomUserAgent(); 10 | this.proxy = this.proxy; 11 | if (this.proxy) { 12 | this.agent = new HttpsProxyAgent(this.proxy); 13 | } else { 14 | this.agent = undefined; 15 | } 16 | } 17 | 18 | generateHeaders(token) { 19 | const headers = { 20 | Accept: "*/*", 21 | "Accept-Language": "en-GB,en-US;q=0.9,en;q=0.8", 22 | "Content-Type": "application/json", 23 | "User-Agent": this.ua, 24 | }; 25 | 26 | if (this.token) { 27 | headers.Authorization = token; 28 | } 29 | // console.log(headers); 30 | return headers; 31 | } 32 | 33 | async fetch( 34 | endpoint, 35 | method, 36 | token, 37 | body = {}, 38 | cred = "include", 39 | additionalHeader = {}, 40 | customUrl = undefined 41 | ) { 42 | try { 43 | const url = `${customUrl == undefined ? this.url : customUrl}${endpoint}`; 44 | let headers = this.generateHeaders(token); 45 | headers = Object.assign(headers, additionalHeader); 46 | const options = { 47 | cache: "default", 48 | credentials: cred, 49 | headers, 50 | method, 51 | mode: "cors", 52 | redirect: "follow", 53 | referrer: this.url, 54 | agent: this.agent, 55 | referrerPolicy: "strict-origin-when-cross-origin", 56 | }; 57 | 58 | if (method !== "GET") { 59 | options.body = `${JSON.stringify(body)}`; 60 | } 61 | 62 | logger.info(`${method} : ${url}`); 63 | logger.info(`Request Header : ${JSON.stringify(headers)}`); 64 | logger.info(`Request Body : ${JSON.stringify(body)}`); 65 | 66 | const res = await fetch(url, options); 67 | 68 | logger.info(`Response : ${res.status} ${res.statusText}`); 69 | logger.info(``); 70 | 71 | if (res.ok || res.status == 400) { 72 | const data = await res.json(); 73 | logger.info(`Response Data : ${JSON.stringify(data)}`); 74 | return data; 75 | } else { 76 | throw new Error(res.statusText); 77 | } 78 | } catch (err) { 79 | logger.error(`Error : ${err.message}`); 80 | throw err; 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/config/address_list.js: -------------------------------------------------------------------------------- 1 | export const addressList = [ 2 | undefined, //leave it undefined, it will be used as param to send to your own address 3 | // "ANOTHER ADDRESS", 4 | ]; 5 | -------------------------------------------------------------------------------- /src/config/config.js: -------------------------------------------------------------------------------- 1 | import { addressList } from "./address_list.js"; 2 | 3 | export class Config { 4 | static sendAmount = 0.0001; //amount to send in sol 5 | static destAddress = addressList; //address destination list 6 | static maxRetry = 3; // max error retry for claiming 7 | } 8 | -------------------------------------------------------------------------------- /src/core/solana.js: -------------------------------------------------------------------------------- 1 | import { 2 | Connection, 3 | Keypair, 4 | PublicKey, 5 | LAMPORTS_PER_SOL, 6 | Transaction, 7 | SystemProgram, 8 | sendAndConfirmTransaction, 9 | } from "@solana/web3.js"; 10 | import { Helper } from "../utils/helper.js"; 11 | import { Config } from "../config/config.js"; 12 | import nacl from "tweetnacl"; 13 | import { API } from "../api/api.js"; 14 | import logger from "../utils/logger.js"; 15 | 16 | export class Solana extends API { 17 | constructor(pk, proxy) { 18 | const apiUrl = "https://odyssey-api-beta.sonic.game"; 19 | super(apiUrl, proxy); 20 | this.pk = pk; 21 | this.draw = 0; 22 | this.lottery = 0; 23 | this.currentError = 0; 24 | this.connection = new Connection( 25 | "https://api.testnet.sonic.game", 26 | "confirmed" 27 | ); 28 | } 29 | 30 | async connectWallet() { 31 | try { 32 | const privateKeyBuffer = Helper.base58decoder(this.pk); 33 | /** @type {Keypair} */ 34 | this.wallet = Keypair.fromSecretKey(privateKeyBuffer); 35 | /** @type {PublicKey} */ 36 | this.address = new PublicKey(this.wallet.publicKey.toBase58()); 37 | } catch (error) { 38 | throw error; 39 | } 40 | } 41 | 42 | async connect() { 43 | logger.info(`Connecting to Sonic Odyssey`); 44 | await this.fetch( 45 | `/testnet-v1/auth/sonic/challenge?wallet=${this.address}`, 46 | "GET", 47 | undefined, 48 | null, 49 | "omit" 50 | ) 51 | .then(async (challangeRes) => { 52 | const message = challangeRes.data; 53 | const messageBytes = new TextEncoder().encode(message); 54 | const signature = nacl.sign.detached( 55 | messageBytes, 56 | this.wallet.secretKey 57 | ); 58 | const signatureBase64 = Buffer.from(signature).toString("base64"); 59 | const addressEncoded = Buffer.from( 60 | this.wallet.publicKey.toBytes() 61 | ).toString("base64"); 62 | const requestBody = { 63 | address: this.address.toBase58(), 64 | address_encoded: addressEncoded, 65 | signature: signatureBase64, 66 | }; 67 | 68 | await this.fetch( 69 | `/testnet-v1/auth/sonic/authorize`, 70 | "POST", 71 | undefined, 72 | requestBody, 73 | "omit" 74 | ) 75 | .then(async (authorizeRes) => { 76 | if (authorizeRes.code == 0) { 77 | this.token = authorizeRes.data.token; 78 | logger.info(`Connected to Sonic Odyssey`); 79 | await Helper.delay( 80 | 1000, 81 | this.pk, 82 | `Connected to Sonic Odyssey`, 83 | this 84 | ); 85 | } else { 86 | throw new Error(authorizeRes.message); 87 | } 88 | }) 89 | .catch((err) => { 90 | throw err; 91 | }); 92 | }) 93 | .catch((err) => { 94 | throw err; 95 | }); 96 | } 97 | 98 | async checkBalance() { 99 | try { 100 | this.balance = 101 | (await this.connection.getBalance(this.address)) / LAMPORTS_PER_SOL; 102 | } catch (error) { 103 | throw error; 104 | } 105 | } 106 | 107 | /** @param {Transaction} trans */ 108 | async doTx(trans) { 109 | try { 110 | logger.info(`Execute Transaction ${JSON.stringify(trans)}`); 111 | const tx = await sendAndConfirmTransaction(this.connection, trans, [ 112 | this.wallet, 113 | ]); 114 | logger.info(`Tx Url: https://explorer.sonic.game/tx/${tx}`); 115 | await Helper.delay( 116 | 1000, 117 | this.pk, 118 | `Tx Url: https://explorer.sonic.game/tx/${tx}`, 119 | this 120 | ); 121 | return tx; 122 | } catch (error) { 123 | logger.error(`Transaction failed: ${error.message}`, error); 124 | throw error; 125 | } 126 | } 127 | 128 | /** @param {Transaction} trans */ 129 | async doRawTx(trans) { 130 | try { 131 | logger.info(`Execute Raw Transaction ${JSON.stringify(trans)}`); 132 | const rawTransaction = trans.serialize(); 133 | const tx = await this.connection.sendRawTransaction(rawTransaction); 134 | await this.confirmTx(tx); 135 | logger.info(`Tx Url: https://explorer.sonic.game/tx/${tx}`); 136 | await Helper.delay( 137 | 1000, 138 | this.pk, 139 | `Tx Url: https://explorer.sonic.game/tx/${tx}`, 140 | this 141 | ); 142 | return tx; 143 | } catch (error) { 144 | logger.error(`Transaction failed: ${error.message}`, error); 145 | throw error; 146 | } 147 | } 148 | 149 | /** @param {Transaction} trans */ 150 | async confirmTx(signature) { 151 | try { 152 | logger.info(`Confirming Transaction...`); 153 | await Helper.delay( 154 | 2000, 155 | this.pk, 156 | `Confirming Transaction, Estimated take 30 Seconds..`, 157 | this 158 | ); 159 | await this.connection.confirmTransaction(signature, "finalized"); 160 | 161 | logger.info(`Transaction Confirmed`); 162 | await Helper.delay(2000, this.pk, `Transaction Confirmed`, this); 163 | } catch (error) { 164 | logger.error(`Transaction failed: ${error.message}`, error); 165 | if (this.currentError < Config.maxRetry) { 166 | this.currentError += 1; 167 | await Helper.delay( 168 | 2000, 169 | this.pk, 170 | `Transaction Not Confirmed after 30 Second, Retrying...`, 171 | this 172 | ); 173 | await this.confirmTx(signature); 174 | } else { 175 | this.currentError = 0; 176 | await Helper.delay( 177 | 2000, 178 | this.pk, 179 | `Transaction not confirmed and max retry reached`, 180 | this 181 | ); 182 | throw Error("Transaction not confirmed and max retry reached"); 183 | } 184 | } 185 | } 186 | 187 | async sendSolToAddress() { 188 | try { 189 | const destAddress = 190 | Config.destAddress[Helper.random(0, Config.destAddress.length - 1)] ?? 191 | this.address; 192 | const amount = Config.sendAmount; 193 | logger.info(`Sending ${amount} to ${destAddress}`); 194 | await Helper.delay( 195 | 1000, 196 | this.pk, 197 | `Sending ${amount} to ${destAddress}`, 198 | this 199 | ); 200 | const transferInstruction = SystemProgram.transfer({ 201 | fromPubkey: this.address, 202 | toPubkey: destAddress, 203 | lamports: amount * LAMPORTS_PER_SOL, 204 | }); 205 | const transaction = new Transaction().add(transferInstruction); 206 | await this.doTx(transaction) 207 | .then(async () => { 208 | await this.checkBalance(); 209 | }) 210 | .catch((err) => { 211 | throw err; 212 | }); 213 | this.dailyTx.total_transactions += 1; 214 | } catch (error) { 215 | throw error; 216 | } 217 | } 218 | 219 | async checkIn() { 220 | logger.info(`Try to Check-in`); 221 | await Helper.delay(1000, this.pk, `Try to Check-in`, this); 222 | await this.fetch( 223 | `/testnet-v1/user/check-in/transaction`, 224 | "GET", 225 | this.token, 226 | null 227 | ) 228 | .then(async (data) => { 229 | if (data.code == 0) { 230 | const transactionBuffer = Buffer.from(data.data.hash, "base64"); 231 | const transaction = Transaction.from(transactionBuffer); 232 | 233 | const tx = await this.doTx(transaction); 234 | 235 | logger.info( 236 | `Check-in Transaction Executed Successfully, continue with post check in process` 237 | ); 238 | await Helper.delay( 239 | 1000, 240 | this.pk, 241 | `Check-in Transaction Executed Successfully, continue with post check in process`, 242 | this 243 | ); 244 | this.dailyTx.total_transactions += 1; 245 | await this.postCheckIn(tx); 246 | } else { 247 | await Helper.delay(1000, this.pk, data.message, this); 248 | } 249 | }) 250 | .catch((err) => { 251 | throw err; 252 | }); 253 | } 254 | 255 | async postCheckIn(tx) { 256 | await Helper.delay(1000, this.pk, `Execute post check in process`, this); 257 | await this.fetch(`/testnet-v1/user/check-in`, "POST", this.token, { 258 | hash: tx, 259 | }) 260 | .then(async (data) => { 261 | if (data.code != 0) { 262 | throw new Error(data.message); 263 | } else { 264 | await Helper.delay(1000, this.pk, "Checked in Successfully", this); 265 | } 266 | }) 267 | .catch((err) => { 268 | throw err; 269 | }); 270 | } 271 | 272 | async getRewardInfo() { 273 | try { 274 | await Helper.delay(1000, this.pk, `Getting Reward Information`, this); 275 | await this.fetch("/testnet-v1/user/rewards/info", "GET", this.token) 276 | .then(async (data) => { 277 | if (data.code == 0) { 278 | this.reward = data.data; 279 | await Helper.delay( 280 | 1000, 281 | this.pk, 282 | `Successfully Get User Reward Information`, 283 | this 284 | ); 285 | } else { 286 | throw new Error("Unable to get user reward info"); 287 | } 288 | }) 289 | .catch((err) => { 290 | throw err; 291 | }); 292 | } catch (error) { 293 | throw error; 294 | } 295 | } 296 | 297 | async getDailyTx() { 298 | try { 299 | await Helper.delay(1000, this.pk, `Getting Daily Tx Info`, this); 300 | await this.fetch( 301 | `/testnet-v1/user/transactions/state/daily`, 302 | "GET", 303 | this.token, 304 | null 305 | ) 306 | .then(async (data) => { 307 | if (data.code != 0) { 308 | throw new Error(data.message); 309 | } else { 310 | this.dailyTx = data.data; 311 | await Helper.delay( 312 | 1000, 313 | this.pk, 314 | `Successfully Get Daily Tx Information`, 315 | this 316 | ); 317 | } 318 | }) 319 | .catch((err) => { 320 | throw err; 321 | }); 322 | } catch (error) { 323 | throw error; 324 | } 325 | } 326 | 327 | async claimTxMilestone(stage) { 328 | logger.info(`Claiming Tx Milestone Stage ${stage}`); 329 | await Helper.delay( 330 | 1000, 331 | this.pk, 332 | `Claiming Tx Milestone Stage ${stage}`, 333 | this 334 | ); 335 | await this.fetch( 336 | `/testnet-v1/user/transactions/rewards/claim`, 337 | "POST", 338 | this.token, 339 | { 340 | stage: stage, 341 | } 342 | ) 343 | .then(async (data) => { 344 | if (data.code == 0) { 345 | await Helper.delay(1000, this.pk, `Claimed Successfully`, this); 346 | } else { 347 | await Helper.delay(1000, this.pk, data.message, this); 348 | } 349 | }) 350 | .catch((err) => { 351 | throw err; 352 | }); 353 | } 354 | 355 | async claimMysteryBox() { 356 | await Helper.delay( 357 | 1000, 358 | this.pk, 359 | `Build Tx for Claiming Mystery BOX`, 360 | this 361 | ); 362 | logger.info(`Build Tx for Claiming Mystery BOX`); 363 | await this.fetch( 364 | "/testnet-v1/user/rewards/mystery-box/build-tx", 365 | "GET", 366 | this.token, 367 | undefined 368 | ) 369 | .then(async (data) => { 370 | if (data.code == 0) { 371 | const transactionBuffer = Buffer.from(data.data.hash, "base64"); 372 | const transaction = Transaction.from(transactionBuffer); 373 | transaction.partialSign(this.wallet); 374 | const tx = await this.doRawTx(transaction); 375 | await this.openMysteryBox(tx); 376 | } else { 377 | await Helper.delay(1000, this.pk, data.message, this); 378 | logger.error(data.message); 379 | } 380 | }) 381 | .catch((err) => { 382 | throw err; 383 | }); 384 | } 385 | 386 | async openMysteryBox(hash) { 387 | await Helper.delay(1000, this.pk, `Opening Mystery Box`, this); 388 | logger.info(`Opening Mystery Box`); 389 | await this.fetch("/user/rewards/mystery-box/open", "POST", this.token, { 390 | hash: hash, 391 | }) 392 | .then(async (data) => { 393 | if (data.code == 0) { 394 | await Helper.delay( 395 | 3000, 396 | this.pk, 397 | `Successfully open mystery box got ${data.data.amount} RING`, 398 | this 399 | ); 400 | } else { 401 | await Helper.delay(1000, this.pk, data.message, this); 402 | logger.error(data.message); 403 | } 404 | }) 405 | .catch((err) => { 406 | throw err; 407 | }); 408 | } 409 | } 410 | -------------------------------------------------------------------------------- /src/utils/helper.js: -------------------------------------------------------------------------------- 1 | import bs58 from "bs58"; 2 | import twist from "./twist.js"; 3 | 4 | export class Helper { 5 | static base58decoder(base58PrivateKey) { 6 | try { 7 | const privateKeyBuffer = bs58.decode(base58PrivateKey); 8 | return privateKeyBuffer; 9 | } catch (error) { 10 | throw error; 11 | } 12 | } 13 | 14 | static delay = (ms, acc, msg, obj) => { 15 | return new Promise(async (resolve) => { 16 | let remainingMilliseconds = ms; 17 | 18 | if (acc != undefined) { 19 | await twist.log(msg, acc, obj, `Delaying for ${this.msToTime(ms)}`); 20 | } else { 21 | twist.info(`Delaying for ${this.msToTime(ms)}`); 22 | } 23 | 24 | const interval = setInterval(async () => { 25 | remainingMilliseconds -= 1000; 26 | if (acc != undefined) { 27 | await twist.log( 28 | msg, 29 | acc, 30 | obj, 31 | `Delaying for ${this.msToTime(remainingMilliseconds)}` 32 | ); 33 | } else { 34 | twist.info(`Delaying for ${this.msToTime(remainingMilliseconds)}`); 35 | } 36 | 37 | if (remainingMilliseconds <= 0) { 38 | clearInterval(interval); 39 | resolve(); 40 | } 41 | }, 1000); 42 | 43 | setTimeout(async () => { 44 | clearInterval(interval); 45 | await twist.clearInfo(); 46 | if (acc) { 47 | await twist.log(msg, acc, obj); 48 | } 49 | resolve(); 50 | }, ms); 51 | }); 52 | }; 53 | 54 | static random(min, max) { 55 | const rand = Math.floor(Math.random() * (max - min + 1)) + min; 56 | return rand; 57 | } 58 | 59 | static randomUserAgent() { 60 | const list_useragent = [ 61 | "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", 62 | "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", 63 | "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", 64 | "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", 65 | "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", 66 | "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", 67 | ]; 68 | return list_useragent[Math.floor(Math.random() * list_useragent.length)]; 69 | } 70 | 71 | static msToTime(milliseconds) { 72 | const hours = Math.floor(milliseconds / (1000 * 60 * 60)); 73 | const remainingMillisecondsAfterHours = milliseconds % (1000 * 60 * 60); 74 | const minutes = Math.floor(remainingMillisecondsAfterHours / (1000 * 60)); 75 | const remainingMillisecondsAfterMinutes = 76 | remainingMillisecondsAfterHours % (1000 * 60); 77 | const seconds = Math.round(remainingMillisecondsAfterMinutes / 1000); 78 | 79 | return `${hours} Hours ${minutes} Minutes ${seconds} Seconds`; 80 | } 81 | 82 | static showSkelLogo() { 83 | console.log(` 84 | 85 | ... 86 | .;:. 87 | .;ol,. 88 | .;ooc:' 89 | .. .;ooccc:'. .. 90 | .',....'cdxlccccc;.....,'. 91 | .;;..'';clolccccccc:,''..;;. 92 | ':c'..':cccccccccccccc;...'c:. 93 | ':cc,.'ccccccccccccccccc:..;cc:' 94 | ...:cc;.':cccccccccccccccccc:..:cc:... 95 | .;';cc;.':;;:cccccccccccccc:;;;'.;cc,,;. 96 | .cc':c:.',.....;cccccccccc;.....,..:c:'c: 97 | ,x:'cc;.,' .':cccccc:'. ',.;cc':x' 98 | lO,'cc;.;, .;cccc:. ,;.;cc';0l 99 | .o0;.;c;.,:'......',''''''......':,.;c;.:0l. 100 | .lxl,.;,..;c::::;:,. .,:;::::c;..,;.,oxl. 101 | .lkxOl.. ..'..;::'..''..'::;..'.. ..c0xkl. 102 | .cKMx. .;c:;:cc:;:c:. .xMKc. 103 | ;KX: ;o::l:;cc;o:. ;KK; 104 | :KK:. ,d,cd,'ol'o: .:0K: 105 | ;0NOl:;:loo;. ... .. .;ldlc::lkN0: 106 | .lONNNKOx0Xd,;;'.,:,lKKkk0XNN0o. 107 | .','.. .lX0doooodOXd. .','. 108 | .,okkddxkd;. 109 | 'oxxd;. 110 | ........................................ 111 | .OWo xNd lox xxl Ald xoc dakkkkkxsx. 112 | .OWo o0W cXW dM0 MMN lNK laddKMNkso. 113 | .kMKoxsNN oWX dW0 MMMWO lWK axM0 . 114 | .OMWXNaMX dM0 kM0 MMKxNXKW0 axMk . 115 | .OMk dWK oWX XWdx Mxx XMMO akMx . 116 | 'OWo dM0 'kNNXNNd DMD OWk aoWd . 117 | ........................................ 118 | 119 | `); 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /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/utils/twist.js: -------------------------------------------------------------------------------- 1 | import { Twisters } from "twisters"; 2 | import { account } from "../../account.js"; 3 | import { Solana } from "../core/solana.js"; 4 | import logger from "./logger.js"; 5 | 6 | class Twist { 7 | constructor() { 8 | /** @type {Twisters}*/ 9 | this.twisters = new Twisters(); 10 | } 11 | 12 | /** 13 | * @param {string} acc 14 | * @param {Solana} solana 15 | * @param {string} msg 16 | */ 17 | log(msg = "", acc = "", solana = new Solana(acc), delay) { 18 | const accIdx = account.indexOf(acc); 19 | if (delay == undefined) { 20 | logger.info(`Account ${accIdx + 1} - ${msg}`); 21 | delay = "-"; 22 | } 23 | 24 | const address = solana.address ?? "-"; 25 | const balance = solana.balance ?? "-"; 26 | const reward = solana.reward ?? {}; 27 | const ring = reward.ring ?? "?"; 28 | const ring_monitor = reward.ring_monitor ?? "-"; 29 | const dailyTx = solana.dailyTx ?? {}; 30 | const total_transactions = dailyTx.total_transactions ?? "-"; 31 | 32 | this.twisters.put(acc, { 33 | text: ` 34 | ================= Account ${account.indexOf(acc) + 1} ============= 35 | Wallet Address : ${address} 36 | Balance : ${balance} SOL | ${ring} RING 37 | Mystery Box : ${ring_monitor} 38 | Daily TX : ${total_transactions} 39 | 40 | Status : ${msg} 41 | Delay : ${delay} 42 | ============================================== 43 | `, 44 | }); 45 | } 46 | 47 | /** 48 | * @param {string} msg 49 | */ 50 | info(msg = "") { 51 | this.twisters.put(2, { 52 | text: ` 53 | ============================================== 54 | Info : ${msg} 55 | ==============================================`, 56 | }); 57 | return; 58 | } 59 | 60 | clearInfo() { 61 | this.twisters.remove(2); 62 | } 63 | 64 | clear(acc) { 65 | this.twisters.remove(acc); 66 | } 67 | } 68 | export default new Twist(); 69 | --------------------------------------------------------------------------------