├── 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 |
--------------------------------------------------------------------------------