├── private.txt ├── package.json ├── README.md ├── .gitignore ├── signature.js └── index.js /private.txt: -------------------------------------------------------------------------------- 1 | base58privatekey 2 | base58privatekey 3 | base58privatekey 4 | base58privatekey 5 | base58privatekey -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "@solana/web3.js": "^1.93.1", 4 | "bs58": "^5.0.0", 5 | "fs": "^0.0.1-security", 6 | "prompts": "^2.4.2", 7 | "tweetnacl": "^1.0.3", 8 | "twisters": "^1.1.0" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SONIC DAILY TRANSACTION HELPER 2 | 3 |
4 | 5 | ### ⭐💻 Feel free to star this repository and help development by pull request ~ 6 | 7 |
8 | 9 | ## Features 10 | 11 | - **Claim 1 SOL Faucet** : Need 2Captcha key. 12 | - **Generate Random Addresses** : 100 Addresses by default. 13 | - **Send SOL** : 0.001 SOL by default. 14 | - **Transaction Delay**: 5 seconds by default. 15 | - **Daily Check In**: Earn 1-2 Mystery Boxes. 16 | - **Claim Transaction Milestones**: Earn 2-6 Mystery Boxes. 17 | - **Open Mystery Box**: Earn 1-5 rings / points. 18 | - **Get User Info**: Get points and boxes count. 19 | - **Integrate with Telegram Bot as Notification**: Make alert of claimed account. 20 | 21 | ## Installation 22 | 23 | - Clone this repo 24 | 25 | ``` 26 | git clone https://github.com/nhaidaar/sonic-daily-tx 27 | cd sonic-daily-tx 28 | ``` 29 | 30 | - Install requirements 31 | 32 | ``` 33 | npm install 34 | ``` 35 | 36 | - Put your private key in `private.txt` 37 | 38 | - Put your 2captcha key in `index.js` line 8 39 | 40 | ``` 41 | const captchaKey = 'INSERT_YOUR_2CAPTCHA_KEY_HERE'; 42 | ``` 43 | 44 | - If you want to use telegram bot as notification, put your bot token and chat_id in `index.js` line 296, 297 45 | 46 | ``` 47 | const token = 'INSERT_YOUR_TELEGRAM_BOT_TOKEN_HERE'; 48 | const chatid = 'INSERT_YOUR_TELEGRAM_BOT_CHATID_HERE'; 49 | ``` 50 | 51 | - Run script using `node index.js` 52 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | .pnpm-debug.log* 9 | 10 | # Diagnostic reports (https://nodejs.org/api/report.html) 11 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 12 | 13 | # Runtime data 14 | pids 15 | *.pid 16 | *.seed 17 | *.pid.lock 18 | 19 | # Directory for instrumented libs generated by jscoverage/JSCover 20 | lib-cov 21 | 22 | # Coverage directory used by tools like istanbul 23 | coverage 24 | *.lcov 25 | 26 | # nyc test coverage 27 | .nyc_output 28 | 29 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 30 | .grunt 31 | 32 | # Bower dependency directory (https://bower.io/) 33 | bower_components 34 | 35 | # node-waf configuration 36 | .lock-wscript 37 | 38 | # Compiled binary addons (https://nodejs.org/api/addons.html) 39 | build/Release 40 | 41 | # Dependency directories 42 | node_modules/ 43 | jspm_packages/ 44 | 45 | # Snowpack dependency directory (https://snowpack.dev/) 46 | web_modules/ 47 | 48 | # TypeScript cache 49 | *.tsbuildinfo 50 | 51 | # Optional npm cache directory 52 | .npm 53 | 54 | # Optional eslint cache 55 | .eslintcache 56 | 57 | # Optional stylelint cache 58 | .stylelintcache 59 | 60 | # Microbundle cache 61 | .rpt2_cache/ 62 | .rts2_cache_cjs/ 63 | .rts2_cache_es/ 64 | .rts2_cache_umd/ 65 | 66 | # Optional REPL history 67 | .node_repl_history 68 | 69 | # Output of 'npm pack' 70 | *.tgz 71 | 72 | # Yarn Integrity file 73 | .yarn-integrity 74 | 75 | # dotenv environment variable files 76 | # .env 77 | # .env.development.local 78 | # .env.test.local 79 | # .env.production.local 80 | # .env.local 81 | 82 | # parcel-bundler cache (https://parceljs.org/) 83 | .cache 84 | .parcel-cache 85 | 86 | # Next.js build output 87 | .next 88 | out 89 | 90 | # Nuxt.js build / generate output 91 | .nuxt 92 | dist 93 | 94 | # Gatsby files 95 | .cache/ 96 | # Comment in the public line in if your project uses Gatsby and not Next.js 97 | # https://nextjs.org/blog/next-9-1#public-directory-support 98 | # public 99 | 100 | # vuepress build output 101 | .vuepress/dist 102 | 103 | # vuepress v2.x temp and cache directory 104 | .temp 105 | .cache 106 | 107 | # Docusaurus cache and generated files 108 | .docusaurus 109 | 110 | # Serverless directories 111 | .serverless/ 112 | 113 | # FuseBox cache 114 | .fusebox/ 115 | 116 | # DynamoDB Local files 117 | .dynamodb/ 118 | 119 | # TernJS port file 120 | .tern-port 121 | 122 | # Stores VSCode versions used for testing VSCode extensions 123 | .vscode-test 124 | 125 | # yarn v2 126 | .yarn/cache 127 | .yarn/unplugged 128 | .yarn/build-state.yml 129 | .yarn/install-state.gz 130 | .pnp.* -------------------------------------------------------------------------------- /signature.js: -------------------------------------------------------------------------------- 1 | const sol = require("@solana/web3.js"); 2 | const bs58 = require("bs58"); 3 | const { readFileSync } = require("fs"); 4 | const nacl = require("tweetnacl"); 5 | 6 | const rpc = 'https://devnet.sonic.game/'; 7 | const connection = new sol.Connection(rpc, 'confirmed'); 8 | 9 | function getKeypairFromPrivateKey(privateKey) { 10 | const decoded = bs58.decode(privateKey); 11 | return sol.Keypair.fromSecretKey(decoded); 12 | } 13 | 14 | async function Tx(trans, keyPair) { 15 | const tx = await sol.sendAndConfirmTransaction(connection, trans, [ 16 | keyPair, 17 | ]); 18 | console.log(`Tx Url: https://explorer.sonic.game/tx/${tx}`); 19 | return tx; 20 | } 21 | 22 | const getSolanaBalance = (fromKeypair) => { 23 | return new Promise(async (resolve) => { 24 | try { 25 | const balance = await connection.getBalance(fromKeypair.publicKey); 26 | resolve(balance / sol.LAMPORTS_PER_SOL); 27 | } catch (error) { 28 | resolve('Error getting balance!'); 29 | } 30 | }); 31 | } 32 | const getDailyLogin = (keyPair, auth) => new Promise(async (resolve, reject) => { 33 | const data = await fetch(`https://odyssey-api.sonic.game/user/check-in/transaction`, { 34 | headers: { 35 | 'accept': '*/*', 36 | 'accept-language': 'en-US,en;q=0.6', 37 | 'if-none-match': 'W/"192-D/PuxxsvlPPenys+YyKzNiw6SKg"', 38 | 'origin': 'https://odyssey.sonic.game', 39 | 'priority': 'u=1, i', 40 | 'referer': 'https://odyssey.sonic.game/', 41 | 'sec-ch-ua': '"Not/A)Brand";v="8", "Chromium";v="126", "Brave";v="126"', 42 | 'sec-ch-ua-mobile': '?0', 43 | 'sec-ch-ua-platform': '"Windows"', 44 | 'sec-fetch-dest': 'empty', 45 | 'sec-fetch-mode': 'cors', 46 | 'sec-fetch-site': 'same-site', 47 | 'sec-gpc': '1', 48 | 'Authorization': `${auth}`, 49 | 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36' 50 | } 51 | }).then(response => response.json()); 52 | if (data.data) { 53 | const transactionBuffer = Buffer.from(data.data.hash, "base64"); 54 | const transaction = sol.Transaction.from(transactionBuffer); 55 | const signature = await Tx(transaction, keyPair); 56 | const checkin = await fetch('https://odyssey-api.sonic.game/user/check-in', { 57 | method: 'POST', 58 | headers: { 59 | 'accept': '*/*', 60 | 'accept-language': 'en-US,en;q=0.6', 61 | 'content-type': 'application/json', 62 | 'origin': 'https://odyssey.sonic.game', 63 | 'priority': 'u=1, i', 64 | 'referer': 'https://odyssey.sonic.game/', 65 | 'sec-ch-ua': '"Not/A)Brand";v="8", "Chromium";v="126", "Brave";v="126"', 66 | 'sec-ch-ua-mobile': '?0', 67 | 'sec-ch-ua-platform': '"Windows"', 68 | 'sec-fetch-dest': 'empty', 69 | 'sec-fetch-mode': 'cors', 70 | 'sec-fetch-site': 'same-site', 71 | 'sec-gpc': '1', 72 | 'Authorization': `${auth}`, 73 | 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36' 74 | }, 75 | body: JSON.stringify({ 76 | 'hash': `${signature}` 77 | }) 78 | }).then(response => response.json()); 79 | resolve(checkin) 80 | } else { 81 | resolve(data) 82 | } 83 | }) 84 | 85 | // const openBox = (keyPair, auth) => new Promise(async (resolve, reject) => { 86 | // console.log(anu ga ada :v) 87 | // }) 88 | 89 | 90 | const getTokenLogin = (keyPair) => new Promise(async (resolve, reject) => { 91 | const message = await fetch(`https://odyssey-api.sonic.game/auth/sonic/challenge?wallet=${keyPair.publicKey}`, { 92 | headers: { 93 | 'accept': '*/*', 94 | 'accept-language': 'en-US,en;q=0.6', 95 | 'if-none-match': 'W/"192-D/PuxxsvlPPenys+YyKzNiw6SKg"', 96 | 'origin': 'https://odyssey.sonic.game', 97 | 'priority': 'u=1, i', 98 | 'referer': 'https://odyssey.sonic.game/', 99 | 'sec-ch-ua': '"Not/A)Brand";v="8", "Chromium";v="126", "Brave";v="126"', 100 | 'sec-ch-ua-mobile': '?0', 101 | 'sec-ch-ua-platform': '"Windows"', 102 | 'sec-fetch-dest': 'empty', 103 | 'sec-fetch-mode': 'cors', 104 | 'sec-fetch-site': 'same-site', 105 | 'sec-gpc': '1', 106 | 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36' 107 | } 108 | }).then(response => response.json()); 109 | 110 | const sign = nacl.sign.detached(Buffer.from(message.data), keyPair.secretKey); 111 | const signature = Buffer.from(sign).toString('base64'); 112 | const publicKey = keyPair.publicKey.toBase58(); 113 | const addressEncoded = Buffer.from(keyPair.publicKey.toBytes()).toString("base64") 114 | const authorize = await fetch('https://odyssey-api.sonic.game/auth/sonic/authorize', { 115 | method: 'POST', 116 | headers: { 117 | 'accept': '*/*', 118 | 'accept-language': 'en-US,en;q=0.6', 119 | 'content-type': 'application/json', 120 | 'origin': 'https://odyssey.sonic.game', 121 | 'priority': 'u=1, i', 122 | 'referer': 'https://odyssey.sonic.game/', 123 | 'sec-ch-ua': '"Not/A)Brand";v="8", "Chromium";v="126", "Brave";v="126"', 124 | 'sec-ch-ua-mobile': '?0', 125 | 'sec-ch-ua-platform': '"Windows"', 126 | 'sec-fetch-dest': 'empty', 127 | 'sec-fetch-mode': 'cors', 128 | 'sec-fetch-site': 'same-site', 129 | 'sec-gpc': '1', 130 | 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36' 131 | }, 132 | body: JSON.stringify({ 133 | 'address': `${publicKey}`, 134 | 'address_encoded': `${addressEncoded}`, 135 | 'signature': `${signature}` 136 | }) 137 | }).then(response => response.json()); 138 | const token = authorize.data.token; 139 | resolve(token); 140 | }); 141 | 142 | (async () => { 143 | const keypairs = []; 144 | // const privateKey = "paste PK disini" 145 | const listAccounts = readFileSync("./private.txt", "utf-8") 146 | .split("\n") 147 | .map((a) => a.trim()); 148 | 149 | for (const privateKey of listAccounts) { 150 | keypairs.push(getKeypairFromPrivateKey(privateKey)); 151 | } 152 | 153 | if (keypairs.length === 0) { 154 | throw new Error('Please fill at least 1 private key in private.txt'); 155 | } 156 | for (const [index, keypair] of keypairs.entries()) { 157 | const publicKey = keypair.publicKey.toBase58() 158 | const initialBalance = (await getSolanaBalance(keypair)) 159 | console.log(publicKey) 160 | console.log(initialBalance) 161 | const getToken = await getTokenLogin(keypair) // ini buat ngambil token login 162 | const getdaily = await getDailyLogin(keypair, getToken) // ini buat claim daily check-in 163 | console.log(getdaily) 164 | // const getOpenBox = await openBox(keypair, getToken) 165 | // console.log(getOpenBox) 166 | } 167 | })() 168 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | const { readFileSync } = require("fs"); 2 | const { Twisters } = require("twisters"); 3 | const sol = require("@solana/web3.js"); 4 | const bs58 = require("bs58"); 5 | const prompts = require('prompts'); 6 | const nacl = require("tweetnacl"); 7 | 8 | const captchaKey = 'INSERT_YOUR_2CAPTCHA_KEY_HERE'; 9 | const rpc = 'https://devnet.sonic.game/'; 10 | const connection = new sol.Connection(rpc, 'confirmed'); 11 | const keypairs = []; 12 | const twisters = new Twisters(); 13 | 14 | let defaultHeaders = { 15 | 'accept': '*/*', 16 | 'accept-language': 'en-US,en;q=0.7', 17 | 'content-type': 'application/json', 18 | 'priority': 'u=1, i', 19 | 'sec-ch-ua': '"Not/A)Brand";v="8", "Chromium";v="126", "Brave";v="126"', 20 | 'sec-ch-ua-mobile': '?0', 21 | 'sec-ch-ua-platform': '"Windows"', 22 | 'sec-fetch-dest': 'empty', 23 | 'sec-fetch-mode': 'cors', 24 | 'sec-fetch-site': 'same-site', 25 | 'sec-gpc': '1', 26 | 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36' 27 | }; 28 | 29 | function generateRandomAddresses(count) { 30 | const addresses = []; 31 | for (let i = 0; i < count; i++) { 32 | const keypair = sol.Keypair.generate(); 33 | addresses.push(keypair.publicKey.toString()); 34 | } 35 | return addresses; 36 | } 37 | 38 | function getKeypairFromPrivateKey(privateKey) { 39 | const decoded = bs58.decode(privateKey); 40 | return sol.Keypair.fromSecretKey(decoded); 41 | } 42 | 43 | const sendTransaction = (transaction, keyPair) => new Promise(async (resolve) => { 44 | try { 45 | transaction.partialSign(keyPair); 46 | const rawTransaction = transaction.serialize(); 47 | const signature = await connection.sendRawTransaction(rawTransaction); 48 | await connection.confirmTransaction(signature); 49 | // const hash = await sol.sendAndConfirmTransaction(connection, transaction, [keyPair]); 50 | resolve(signature); 51 | } catch (error) { 52 | resolve(error); 53 | } 54 | }); 55 | 56 | const delay = (seconds) => { 57 | return new Promise((resolve) => { 58 | return setTimeout(resolve, seconds * 1000); 59 | }); 60 | } 61 | 62 | const twocaptcha_turnstile = (sitekey, pageurl) => new Promise(async (resolve) => { 63 | try { 64 | const getToken = await fetch(`https://2captcha.com/in.php?key=${captchaKey}&method=turnstile&sitekey=${sitekey}&pageurl=${pageurl}&json=1`, { 65 | method: 'GET', 66 | }) 67 | .then(res => res.text()) 68 | .then(res => { 69 | if (res == 'ERROR_WRONG_USER_KEY' || res == 'ERROR_ZERO_BALANCE') { 70 | return resolve(res); 71 | } else { 72 | return res.split('|'); 73 | } 74 | }); 75 | 76 | if (getToken[0] != 'OK') { 77 | resolve('FAILED_GETTING_TOKEN'); 78 | } 79 | 80 | const task = getToken[1]; 81 | 82 | for (let i = 0; i < 60; i++) { 83 | const token = await fetch( 84 | `https://2captcha.com/res.php?key=${captchaKey}&action=get&id=${task}&json=1` 85 | ).then(res => res.json()); 86 | 87 | if (token.status == 1) { 88 | resolve(token); 89 | break; 90 | } 91 | await delay(2); 92 | } 93 | } catch (error) { 94 | resolve('FAILED_GETTING_TOKEN'); 95 | } 96 | }); 97 | 98 | const claimFaucet = (address) => new Promise(async (resolve) => { 99 | let success = false; 100 | 101 | while (!success) { 102 | const bearer = await twocaptcha_turnstile('0x4AAAAAAAc6HG1RMG_8EHSC', 'https://faucet.sonic.game/#/'); 103 | if (bearer == 'ERROR_WRONG_USER_KEY' || bearer == 'ERROR_ZERO_BALANCE' || bearer == 'FAILED_GETTING_TOKEN' ) { 104 | success = true; 105 | resolve(`Failed claim, ${bearer}`); 106 | } 107 | 108 | try { 109 | const res = await fetch(`https://faucet-api.sonic.game/airdrop/${address}/1/${bearer.request}`, { 110 | headers: { 111 | "Accept": "application/json, text/plain, */*", 112 | "Content-Type": "application/json", 113 | "Accept-Language": "en-US,en;q=0.9,id;q=0.8", 114 | "Dnt": "1", 115 | "Origin": "https://faucet.sonic.game", 116 | "Priority": "u=1, i", 117 | "Referer": "https://faucet.sonic.game/", 118 | "User-Agent": bearer.useragent, 119 | "sec-ch-ua-mobile": "?0", 120 | "sec-ch-ua-platform": "Windows", 121 | } 122 | }).then(res => res.json()); 123 | 124 | if (res.status == 'ok') { 125 | success = true; 126 | resolve(`Successfully claim faucet 1 SOL!`); 127 | } 128 | // } else { 129 | // resolve(`Failed to claim, ${res.error}`); 130 | // } 131 | } catch (error) {} 132 | // resolve(`Failed claim, ${error}`); 133 | // } 134 | } 135 | }); 136 | 137 | const getLoginToken = (keyPair) => new Promise(async (resolve) => { 138 | let success = false; 139 | while (!success) { 140 | try { 141 | const message = await fetch(`https://odyssey-api.sonic.game/auth/sonic/challenge?wallet=${keyPair.publicKey}`, { 142 | headers: defaultHeaders 143 | }).then(res => res.json()); 144 | 145 | const sign = nacl.sign.detached(Buffer.from(message.data), keyPair.secretKey); 146 | const signature = Buffer.from(sign).toString('base64'); 147 | const publicKey = keyPair.publicKey.toBase58(); 148 | const addressEncoded = Buffer.from(keyPair.publicKey.toBytes()).toString("base64") 149 | const authorize = await fetch('https://odyssey-api.sonic.game/auth/sonic/authorize', { 150 | method: 'POST', 151 | headers: defaultHeaders, 152 | body: JSON.stringify({ 153 | 'address': `${publicKey}`, 154 | 'address_encoded': `${addressEncoded}`, 155 | 'signature': `${signature}` 156 | }) 157 | }).then(res => res.json()); 158 | 159 | const token = authorize.data.token; 160 | success = true; 161 | resolve(token); 162 | } catch (e) {} 163 | } 164 | }); 165 | 166 | const dailyCheckin = (keyPair, auth) => new Promise(async (resolve) => { 167 | let success = false; 168 | while (!success) { 169 | try { 170 | const data = await fetch(`https://odyssey-api.sonic.game/user/check-in/transaction`, { 171 | headers: { 172 | ...defaultHeaders, 173 | 'authorization': `${auth}` 174 | } 175 | }).then(res => res.json()); 176 | 177 | if (data.message == 'current account already checked in') { 178 | success = true; 179 | resolve('Already check in today!'); 180 | } 181 | 182 | if (data.data) { 183 | const transactionBuffer = Buffer.from(data.data.hash, "base64"); 184 | const transaction = sol.Transaction.from(transactionBuffer); 185 | const signature = await sendTransaction(transaction, keyPair); 186 | const checkin = await fetch('https://odyssey-api.sonic.game/user/check-in', { 187 | method: 'POST', 188 | headers: { 189 | ...defaultHeaders, 190 | 'authorization': `${auth}` 191 | }, 192 | body: JSON.stringify({ 193 | 'hash': `${signature}` 194 | }) 195 | }).then(res => res.json()); 196 | 197 | success = true; 198 | resolve(`Successfully to check in, day ${checkin.data.accumulative_days}!`); 199 | } 200 | } catch (e) {} 201 | } 202 | }); 203 | 204 | const dailyMilestone = (auth, stage) => new Promise(async (resolve) => { 205 | let success = false; 206 | while (!success) { 207 | try { 208 | await fetch('https://odyssey-api.sonic.game/user/transactions/state/daily', { 209 | method: 'GET', 210 | headers: { 211 | ...defaultHeaders, 212 | 'authorization': `${auth}` 213 | }, 214 | }); 215 | 216 | const data = await fetch('https://odyssey-api.sonic.game/user/transactions/rewards/claim', { 217 | method: 'POST', 218 | headers: { 219 | ...defaultHeaders, 220 | 'authorization': `${auth}` 221 | }, 222 | body: JSON.stringify({ 223 | 'stage': stage 224 | }) 225 | }).then(res => res.json()); 226 | 227 | if (data.message == 'interact rewards already claimed') { 228 | success = true; 229 | resolve(`Already claim milestone ${stage}!`); 230 | } 231 | 232 | if (data.data) { 233 | success = true; 234 | resolve(`Successfully to claim milestone ${stage}.`) 235 | } 236 | } catch (e) {} 237 | } 238 | }); 239 | 240 | const openBox = (keyPair, auth) => new Promise(async (resolve) => { 241 | let success = false; 242 | while (!success) { 243 | try { 244 | const data = await fetch(`https://odyssey-api.sonic.game/user/rewards/mystery-box/build-tx`, { 245 | headers: { 246 | ...defaultHeaders, 247 | 'authorization': auth 248 | } 249 | }).then(res => res.json()); 250 | 251 | if (data.data) { 252 | const transactionBuffer = Buffer.from(data.data.hash, "base64"); 253 | const transaction = sol.Transaction.from(transactionBuffer); 254 | transaction.partialSign(keyPair); 255 | const signature = await sendTransaction(transaction, keyPair); 256 | const open = await fetch('https://odyssey-api.sonic.game/user/rewards/mystery-box/open', { 257 | method: 'POST', 258 | headers: { 259 | ...defaultHeaders, 260 | 'authorization': auth 261 | }, 262 | body: JSON.stringify({ 263 | 'hash': signature 264 | }) 265 | }).then(res => res.json()); 266 | 267 | if (open.data) { 268 | success = true; 269 | resolve(open.data.amount); 270 | } 271 | } 272 | } catch (e) {} 273 | } 274 | }); 275 | 276 | const getUserInfo = (auth) => new Promise(async (resolve) => { 277 | let success = false; 278 | while (!success) { 279 | try { 280 | const data = await fetch('https://odyssey-api.sonic.game/user/rewards/info', { 281 | headers: { 282 | ...defaultHeaders, 283 | 'authorization': `${auth}`, 284 | } 285 | }).then(res => res.json()); 286 | 287 | if (data.data) { 288 | success = true; 289 | resolve(data.data); 290 | } 291 | } catch (e) {} 292 | } 293 | }); 294 | 295 | const tgMessage = async (message) => { 296 | const token = 'INSERT_YOUR_TELEGRAM_BOT_TOKEN_HERE'; 297 | const chatid = 'INSERT_YOUR_TELEGRAM_BOT_CHATID_HERE'; 298 | const boturl = `https://api.telegram.org/bot${token}/sendMessage`; 299 | 300 | await fetch(boturl, { 301 | method: 'POST', 302 | headers: { 303 | 'Content-Type': 'application/json', 304 | }, 305 | body: JSON.stringify({ 306 | chat_id: chatid, 307 | link_preview_options: {is_disabled: true}, 308 | text: message, 309 | }), 310 | }); 311 | }; 312 | 313 | function extractAddressParts(address) { 314 | const firstThree = address.slice(0, 4); 315 | const lastFour = address.slice(-4); 316 | return `${firstThree}...${lastFour}`; 317 | } 318 | 319 | (async () => { 320 | // GET PRIVATE KEY 321 | const listAccounts = readFileSync("./private.txt", "utf-8") 322 | .split("\n") 323 | .map((a) => a.trim()); 324 | for (const privateKey of listAccounts) { 325 | keypairs.push(getKeypairFromPrivateKey(privateKey)); 326 | } 327 | if (keypairs.length === 0) { 328 | throw new Error('Please fill at least 1 private key in private.txt'); 329 | } 330 | 331 | // ASK TO CLAIM FAUCET 332 | const q = await prompts([ 333 | { 334 | type: 'confirm', 335 | name: 'claim', 336 | message: 'Claim Faucet? (need 2captcha key)', 337 | }, 338 | { 339 | type: 'confirm', 340 | name: 'openBox', 341 | message: 'Auto Open Mystery Box?', 342 | }, 343 | { 344 | type: 'confirm', 345 | name: 'useBot', 346 | message: 'Use Telegram Bot as Notification?', 347 | }, 348 | { 349 | type: 'number', 350 | name: 'index', 351 | message: `You have ${keypairs.length} account, which one do you want to start with? (default is 1)`, 352 | } 353 | ]); 354 | 355 | 356 | // CUSTOM YOURS 357 | const addressCount = 100; 358 | const amountToSend = 0.001; // in SOL 359 | const delayBetweenRequests = 5; // in seconds 360 | 361 | // DOING TASK FOR EACH PRIVATE KEY 362 | for(let index = (q.index - 1); index < keypairs.length; index++) { 363 | const publicKey = keypairs[index].publicKey.toBase58(); 364 | const randomAddresses = generateRandomAddresses(addressCount); 365 | 366 | twisters.put(`${publicKey}`, { 367 | text: ` === ACCOUNT ${(index + 1)} === 368 | Address : ${publicKey} 369 | Points : - 370 | Mystery Box : - 371 | Status : Getting user token...` 372 | }); 373 | 374 | let token = await getLoginToken(keypairs[index]); 375 | const initialInfo = await getUserInfo(token); 376 | let info = initialInfo; 377 | 378 | twisters.put(`${publicKey}`, { 379 | text: ` === ACCOUNT ${(index + 1)} === 380 | Address : ${publicKey} 381 | Points : ${info.ring} 382 | Mystery Box : ${info.ring_monitor} 383 | Status : -` 384 | }); 385 | 386 | // CLAIM FAUCET 387 | if (q.claim) { 388 | twisters.put(`${publicKey}`, { 389 | text: ` === ACCOUNT ${(index + 1)} === 390 | Address : ${publicKey} 391 | Points : ${info.ring} 392 | Mystery Box : ${info.ring_monitor} 393 | Status : Trying to claim faucet...` 394 | }); 395 | const faucetStatus = await claimFaucet(keypairs[index].publicKey.toBase58()); 396 | twisters.put(`${publicKey}`, { 397 | text: ` === ACCOUNT ${(index + 1)} === 398 | Address : ${publicKey} 399 | Points : ${info.ring} 400 | Mystery Box : ${info.ring_monitor} 401 | Status : ${faucetStatus}` 402 | }); 403 | await delay(delayBetweenRequests); 404 | } 405 | 406 | // SENDING SOL 407 | for (const [i, address] of randomAddresses.entries()) { 408 | try { 409 | const toPublicKey = new sol.PublicKey(address); 410 | const transaction = new sol.Transaction().add( 411 | sol.SystemProgram.transfer({ 412 | fromPubkey: keypairs[index].publicKey, 413 | toPubkey: toPublicKey, 414 | lamports: amountToSend * sol.LAMPORTS_PER_SOL, 415 | }) 416 | ); 417 | await sendTransaction(transaction, keypairs[index]); 418 | 419 | twisters.put(`${publicKey}`, { 420 | text: ` === ACCOUNT ${(index + 1)} === 421 | Address : ${publicKey} 422 | Points : ${info.ring} 423 | Mystery Box : ${info.ring_monitor} 424 | Status : [${(i + 1)}/${randomAddresses.length}] Successfully to sent ${amountToSend} SOL to ${address}` 425 | }); 426 | 427 | await delay(delayBetweenRequests); 428 | } catch (error) { 429 | twisters.put(`${publicKey}`, { 430 | text: ` === ACCOUNT ${(index + 1)} === 431 | Address : ${publicKey} 432 | Points : ${info.ring} 433 | Mystery Box : ${info.ring_monitor} 434 | Status : [${(i + 1)}/${randomAddresses.length}] Failed to sent ${amountToSend} SOL to ${address}` 435 | }); 436 | 437 | await delay(delayBetweenRequests); 438 | } 439 | } 440 | 441 | token = await getLoginToken(keypairs[index]); 442 | 443 | // CHECK IN TASK 444 | twisters.put(`${publicKey}`, { 445 | text: ` === ACCOUNT ${(index + 1)} === 446 | Address : ${publicKey} 447 | Points : ${info.ring} 448 | Mystery Box : ${info.ring_monitor} 449 | Status : Try to daily check in...` 450 | }); 451 | const checkin = await dailyCheckin(keypairs[index], token); 452 | info = await getUserInfo(token); 453 | twisters.put(`${publicKey}`, { 454 | text: ` === ACCOUNT ${(index + 1)} === 455 | Address : ${publicKey} 456 | Points : ${info.ring} 457 | Mystery Box : ${info.ring_monitor} 458 | Status : ${checkin}` 459 | }); 460 | await delay(delayBetweenRequests); 461 | 462 | // CLAIM MILESTONES 463 | twisters.put(`${publicKey}`, { 464 | text: ` === ACCOUNT ${(index + 1)} === 465 | Address : ${publicKey} 466 | Points : ${info.ring} 467 | Mystery Box : ${info.ring_monitor} 468 | Status : Try to claim milestones...` 469 | }); 470 | for (let i = 1; i <= 3; i++) { 471 | const milestones = await dailyMilestone(token, i); 472 | twisters.put(`${publicKey}`, { 473 | text: ` === ACCOUNT ${(index + 1)} === 474 | Address : ${publicKey} 475 | Points : ${info.ring} 476 | Mystery Box : ${info.ring_monitor} 477 | Status : ${milestones}` 478 | }); 479 | await delay(delayBetweenRequests); 480 | } 481 | 482 | info = await getUserInfo(token); 483 | let msg = `Earned ${(info.ring_monitor - initialInfo.ring_monitor)} Mystery Box\nYou have ${info.ring} Points and ${info.ring_monitor} Mystery Box now.`; 484 | 485 | if (q.openBox) { 486 | const totalBox = info.ring_monitor; 487 | twisters.put(`${publicKey}`, { 488 | text: `=== ACCOUNT ${(index + 1)} === 489 | Address : ${publicKey} 490 | Points : ${info.ring} 491 | Mystery Box : ${info.ring_monitor} 492 | Status : Preparing for open ${totalBox} mystery boxes...` 493 | }); 494 | 495 | for (let i = 0; i < totalBox; i++) { 496 | const openedBox = await openBox(keypairs[index], token); 497 | info = await getUserInfo(token); 498 | twisters.put(`${publicKey}`, { 499 | text: ` === ACCOUNT ${(index + 1)} === 500 | Address : ${publicKey} 501 | Points : ${info.ring} 502 | Mystery Box : ${info.ring_monitor} 503 | Status : [${(i + 1)}/${totalBox}] You got ${openedBox} points!` 504 | }); 505 | await delay(delayBetweenRequests); 506 | } 507 | 508 | info = await getUserInfo(token); 509 | msg = `Earned ${(info.ring - initialInfo.ring)} Points\nYou have ${info.ring} Points and ${info.ring_monitor} Mystery Box now.`; 510 | } 511 | 512 | if (q.useBot) { 513 | await tgMessage(`${extractAddressParts(publicKey)} | ${msg}`); 514 | } 515 | 516 | // YOUR POINTS AND MYSTERY BOX COUNT 517 | twisters.put(`${publicKey}`, { 518 | active: false, 519 | text: ` === ACCOUNT ${(index + 1)} === 520 | Address : ${publicKey} 521 | Points : ${info.ring} 522 | Mystery Box : ${info.ring_monitor} 523 | Status : ${msg}` 524 | }); 525 | } 526 | })(); 527 | --------------------------------------------------------------------------------