├── README.md ├── package.json ├── .env └── index.js /README.md: -------------------------------------------------------------------------------- 1 | # R2FinalTestnet-NTE 2 | Full Tutorial Join https://t.me/NTExhaust 3 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "NT Exhaust - R2 Testnet Auto Bot", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "type": "module", 6 | "scripts": { 7 | "start": "node index.js" 8 | }, 9 | "author": "VinSenzo", 10 | "license": "ISC", 11 | "dependencies": { 12 | "axios": "^1.10.0", 13 | "blessed": "^0.1.81", 14 | "dotenv": "^16.4.7", 15 | "ethers": "^6.13.7", 16 | "figlet": "^1.8.0", 17 | "uuid": "^11.1.0" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /.env: -------------------------------------------------------------------------------- 1 | PRIVATE_KEY=YourPrivateKey 2 | DISCORD_TOKEN=YourDiscordToken 3 | RPC_URL=https://ethereum-sepolia-rpc.publicnode.com/ 4 | USDC_ADDRESS=0x8BEbFCBe5468F146533C182dF3DFbF5ff9BE00E2 5 | R2USD_ADDRESS=0x9e8FF356D35a2Da385C546d6Bf1D77ff85133365 6 | sR2USD_ADDRESS=0x006CbF409CA275bA022111dB32BDAE054a97d488 7 | R2_ADDRESS=0xb816bB88f836EA75Ca4071B46FF285f690C43bb7 8 | R2_USDC_ADDRESS=0x8BEbFCBe5468F146533C182dF3DFbF5ff9BE00E2 9 | R2_R2USD_ADDRESS=0x9e8FF356D35a2Da385C546d6Bf1D77ff85133365 10 | PHAROS_RPC_URL=https://testnet.dplabs-internal.com/ 11 | PHAROS_USDC_ADDRESS=0x8bebfcbe5468f146533c182df3dfbf5ff9be00e2 12 | PHAROS_R2USD_ADDRESS=0x4f5b54d4af2568cefafa73bb062e5d734b55aa05 13 | PHAROS_sR2USD_ADDRESS=0xf8694d25947a0097cb2cea2fc07b071bdf72e1f8 14 | MONAD_RPC_URL=https://testnet-rpc.monad.xyz 15 | MONAD_USDC_ADDRESS=0x8BEbFCBe5468F146533C182dF3DFbF5ff9BE00E2 16 | MONAD_R2USD_ADDRESS=0x4f5b54d4AF2568cefafA73bB062e5d734b55AA05 17 | MONAD_sR2USD_ADDRESS=0xF8694d25947A0097CB2cea2Fc07b071Bdf72e1f8 18 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | import "dotenv/config"; 2 | import blessed from "blessed"; 3 | import figlet from "figlet"; 4 | import { ethers } from "ethers"; 5 | import axios from "axios"; 6 | import FormData from "form-data"; 7 | import { v4 as uuid } from "uuid"; 8 | 9 | const DISCORD_TOKEN = process.env.DISCORD_TOKEN; 10 | const APP_ID = "1356609826230243469"; 11 | const GUILD_ID = "1308368864505106442"; 12 | const COMMAND_ID = "1356665931056808211"; 13 | const COMMAND_VERSION = "1356665931056808212"; 14 | const initialProvider = new ethers.JsonRpcProvider(process.env.RPC_URL); 15 | const initialWallet = new ethers.Wallet(process.env.PRIVATE_KEY, initialProvider); 16 | const wallet_address = initialWallet.address; 17 | 18 | const NETWORK_CHANNEL_IDS = { 19 | "Sepolia": "1339883019556749395", 20 | "Pharos": "1395349206947987456", 21 | "Monad": "1367156681154236467" 22 | }; 23 | 24 | const SEPOLIA_CONFIG = { 25 | RPC_URL: process.env.RPC_URL, 26 | USDC_ADDRESS: process.env.USDC_ADDRESS, 27 | R2USD_ADDRESS: process.env.R2USD_ADDRESS, 28 | sR2USD_ADDRESS: process.env.sR2USD_ADDRESS, 29 | ROUTER_USDC_TO_R2USD: "0x9e8FF356D35a2Da385C546d6Bf1D77ff85133365", 30 | ROUTER_R2USD_TO_USDC: "0x47d1B0623bB3E557bF8544C159c9ae51D091F8a2", 31 | STAKING_CONTRACT: "0x006CbF409CA275bA022111dB32BDAE054a97d488", 32 | LP_R2USD_sR2USD: "0xe85A06C238439F981c90b2C91393b2F3c46e27FC", 33 | LP_USDC_R2USD: "0x47d1B0623bB3E557bF8544C159c9ae51D091F8a2", 34 | NETWORK_NAME: "Sepolia Testnet" 35 | }; 36 | 37 | const SEPOLIA_R2_CONFIG = { 38 | RPC_URL: process.env.RPC_URL, 39 | R2_ADDRESS: process.env.R2_ADDRESS, 40 | USDC_ADDRESS: process.env.USDC_ADDRESS, 41 | R2USD_ADDRESS: process.env.R2_R2USD_ADDRESS, 42 | ROUTER_USDC_TO_R2USD: "0xeE567Fe1712Faf6149d80dA1E6934E354124CfE3", 43 | ROUTER_R2USD_TO_USDC: "0xeE567Fe1712Faf6149d80dA1E6934E354124CfE3", 44 | ROUTER_ADDRESS: "0xeE567Fe1712Faf6149d80dA1E6934E354124CfE3", 45 | LP_R2_R2USD: "0x9Ae18109692b43e95Ae6BE5350A5Acc5211FE9a1", 46 | LP_USDC_R2: "0xCdfDD7dD24bABDD05A2ff4dfcf06384c5Ad661a9", 47 | NETWORK_NAME: "Sepolia R2 Testnet" 48 | }; 49 | 50 | 51 | const PHRS_CONFIG = { 52 | RPC_URL: process.env.PHAROS_RPC_URL, 53 | USDC_ADDRESS: process.env.PHAROS_USDC_ADDRESS, 54 | R2USD_ADDRESS: process.env.PHAROS_R2USD_ADDRESS, 55 | sR2USD_ADDRESS: process.env.PHAROS_sR2USD_ADDRESS, 56 | ROUTER_USDC_TO_R2USD: "0x4f5b54d4AF2568cefafA73bB062e5d734b55AA05", 57 | ROUTER_R2USD_TO_USDC: "0x4f5b54d4AF2568cefafA73bB062e5d734b55AA05", 58 | STAKING_CONTRACT: "0xF8694d25947A0097CB2cea2Fc07b071Bdf72e1f8", 59 | NETWORK_NAME: "Pharos Network" 60 | }; 61 | 62 | const MONAD_CONFIG = { 63 | RPC_URL: process.env.MONAD_RPC_URL, 64 | USDC_ADDRESS: process.env.MONAD_USDC_ADDRESS, 65 | R2USD_ADDRESS: process.env.MONAD_R2USD_ADDRESS, 66 | sR2USD_ADDRESS: process.env.MONAD_sR2USD_ADDRESS, 67 | ROUTER_USDC_TO_R2USD: "0x4f5b54d4AF2568cefafA73bB062e5d734b55AA05", 68 | ROUTER_R2USD_TO_USDC: "0x4f5b54d4AF2568cefafA73bB062e5d734b55AA05", 69 | STAKING_CONTRACT: "0xF8694d25947A0097CB2cea2Fc07b071Bdf72e1f8", 70 | LP_R2USD_sR2USD_BAL: "0xF472a0de13BdC08907937Acf5E35dA9fCCAa18C4", 71 | LP_USDC_R2USD_BAL: "0x425cDE58dd27BD6C5Ca2dD345230A0b332Bb8e71", 72 | LP_R2USD_sR2USD: "0xfB8e1C3b833f9E67a71C859a132cf783b645e436", 73 | LP_USDC_R2USD: "0xfB8e1C3b833f9E67a71C859a132cf783b645e436", 74 | NETWORK_NAME: "Monad Network" 75 | }; 76 | 77 | 78 | const DEBUG_MODE = false; 79 | 80 | const ERC20ABI = [ 81 | "function decimals() view returns (uint8)", 82 | "function balanceOf(address owner) view returns (uint256)", 83 | "function approve(address spender, uint256 amount) returns (bool)", 84 | "function allowance(address owner, address spender) view returns (uint256)", 85 | "function transfer(address recipient, uint256 amount) returns (bool)", 86 | "function transferFrom(address sender, address recipient, uint256 amount) returns (bool)" 87 | ]; 88 | 89 | const LP_CONTRACT_ABI = [ 90 | { 91 | "stateMutability": "nonpayable", 92 | "type": "function", 93 | "name": "add_liquidity", 94 | "inputs": [ 95 | {"name": "_amounts", "type": "uint256[]"}, 96 | {"name": "_min_mint_amount", "type": "uint256"}, 97 | {"name": "_receiver", "type": "address"} 98 | ], 99 | "outputs": [{"name": "", "type": "uint256"}] 100 | }, 101 | { 102 | "stateMutability": "view", 103 | "type": "function", 104 | "name": "calc_token_amount", 105 | "inputs": [ 106 | {"name": "_amounts", "type": "uint256[]"}, 107 | {"name": "_is_deposit", "type": "bool"} 108 | ], 109 | "outputs": [{"name": "", "type": "uint256"}] 110 | }, 111 | { 112 | "stateMutability": "view", 113 | "type": "function", 114 | "name": "balanceOf", 115 | "inputs": [{"name": "arg0", "type": "address"}], 116 | "outputs": [{"name": "", "type": "uint256"}] 117 | }, 118 | { 119 | "stateMutability": "view", 120 | "type": "function", 121 | "name": "decimals", 122 | "inputs": [], 123 | "outputs": [{"name": "", "type": "uint8"}] 124 | } 125 | ]; 126 | 127 | const LP_USDC_R2USD_ABI = [ 128 | { 129 | "stateMutability": "view", 130 | "type": "function", 131 | "name": "get_balances", 132 | "inputs": [], 133 | "outputs": [{"name": "", "type": "uint256[]"}] 134 | }, 135 | { 136 | "stateMutability": "view", 137 | "type": "function", 138 | "name": "calc_token_amount", 139 | "inputs": [ 140 | {"name": "_amounts", "type": "uint256[]"}, 141 | {"name": "_is_deposit", "type": "bool"} 142 | ], 143 | "outputs": [{"name": "", "type": "uint256"}] 144 | }, 145 | { 146 | "stateMutability": "nonpayable", 147 | "type": "function", 148 | "name": "add_liquidity", 149 | "inputs": [ 150 | {"name": "_amounts", "type": "uint256[]"}, 151 | {"name": "_min_mint_amount", "type": "uint256"}, 152 | {"name": "_receiver", "type": "address"} 153 | ], 154 | "outputs": [{"name": "", "type": "uint256"}] 155 | } 156 | ]; 157 | 158 | const UNISWAP_V2_LP_ABI = [ 159 | { 160 | "constant": false, 161 | "inputs": [ 162 | {"name": "tokenA", "type": "address"}, 163 | {"name": "tokenB", "type": "address"}, 164 | {"name": "amountADesired", "type": "uint256"}, 165 | {"name": "amountBDesired", "type": "uint256"}, 166 | {"name": "amountAMin", "type": "uint256"}, 167 | {"name": "amountBMin", "type": "uint256"}, 168 | {"name": "to", "type": "address"}, 169 | {"name": "deadline", "type": "uint256"} 170 | ], 171 | "name": "addLiquidity", 172 | "outputs": [ 173 | {"name": "amountA", "type": "uint256"}, 174 | {"name": "amountB", "type": "uint256"}, 175 | {"name": "liquidity", "type": "uint256"} 176 | ], 177 | "payable": false, 178 | "stateMutability": "nonpayable", 179 | "type": "function" 180 | } 181 | ]; 182 | 183 | const randomAmountRanges = { 184 | "SWAP_R2USD_USDC": { 185 | USDC: { min: 50, max: 200 }, 186 | R2USD: { min: 50, max: 200 } 187 | }, 188 | "SWAP_R2_USDC": { 189 | R2: { min: 50, max: 200 }, 190 | USDC: { min: 50, max: 200 } 191 | }, 192 | "SWAP_R2_R2USD": { 193 | R2: { min: 50, max: 200 }, 194 | R2USD: { min: 50, max: 200 } 195 | } 196 | }; 197 | 198 | let currentNetwork = "Sepolia"; 199 | let walletInfoByNetwork = { 200 | "Sepolia": { 201 | address: "", 202 | balanceNative: "0.00", 203 | balanceUsdc: "0.00", 204 | balanceR2usd: "0.00", 205 | balanceSr2usd: "0.00", 206 | balanceLpR2usdSr2usd: "0.00", 207 | balanceLpUsdcR2usd: "0.00", 208 | network: SEPOLIA_CONFIG.NETWORK_NAME, 209 | status: "Initializing" 210 | }, 211 | "Sepolia R2": { 212 | address: "", 213 | balanceNative: "0.00", 214 | balanceUsdc: "0.00", 215 | balanceR2usd: "0.00", 216 | balanceR2: "0.00", 217 | balanceLpR2R2usd: "0.00", 218 | balanceLpUsdcR2: "0.00", 219 | network: SEPOLIA_R2_CONFIG.NETWORK_NAME, 220 | status: "Initializing" 221 | }, 222 | "Pharos": { 223 | address: "", 224 | balanceNative: "0.00", 225 | balanceUsdc: "0.00", 226 | balanceR2usd: "0.00", 227 | balanceSr2usd: "0.00", 228 | balanceLpR2usdSr2usd: "N/A", 229 | balanceLpUsdcR2usd: "N/A", 230 | network: PHRS_CONFIG.NETWORK_NAME, 231 | status: "Initializing" 232 | }, 233 | "Monad": { 234 | address: "", 235 | balanceNative: "0.00", 236 | balanceUsdc: "0.00", 237 | balanceR2usd: "0.00", 238 | balanceSr2usd: "0.00", 239 | balanceLpR2usdSr2usd: "N/A", 240 | balanceLpUsdcR2usd: "N/A", 241 | network: MONAD_CONFIG.NETWORK_NAME, 242 | status: "Initializing" 243 | } 244 | }; 245 | 246 | let transactionLogs = []; 247 | let MY_USER_ID = null; 248 | let claimRunning = false; 249 | let claimCancelled = false; 250 | let dailyClaimInterval = null; 251 | let swapRunningSepolia = false; 252 | let swapCancelledSepolia = false; 253 | let swapRunningSepoliaR2 = false; 254 | let swapCancelledSepoliaR2 = false; 255 | let swapRunningPharos = false; 256 | let swapCancelledPharos = false; 257 | let swapRunningMonad = false; 258 | let swapCancelledMonad = false; 259 | let globalWallet = null; 260 | let provider = null; 261 | let transactionQueue = Promise.resolve(); 262 | let transactionQueueList = []; 263 | let transactionIdCounter = 0; 264 | let nextNonceSepolia = null; 265 | let nextNonceSepoliaR2 = null; 266 | let nextNoncePharos = null; 267 | let nextNonceMonad = null; 268 | let swapDirection = { 269 | "Sepolia": true, 270 | "Sepolia R2": true, 271 | "Pharos": true, 272 | "Monad": true 273 | }; 274 | 275 | function getShortAddress(address) { 276 | return address ? address.slice(0, 6) + "..." + address.slice(-4) : "N/A"; 277 | } 278 | 279 | function getShortHash(hash) { 280 | return hash ? hash.slice(0, 6) + "..." + hash.slice(-4) : "N/A"; 281 | } 282 | 283 | function addLog(message, type, network = currentNetwork) { 284 | if (type === "debug" && !DEBUG_MODE) return; 285 | const timestamp = new Date().toLocaleTimeString(); 286 | let coloredMessage = message; 287 | if (type === "swap") coloredMessage = `{bright-cyan-fg}${message}{/bright-cyan-fg}`; 288 | else if (type === "system") coloredMessage = `{bright-white-fg}${message}{/bright-white-fg}`; 289 | else if (type === "error") coloredMessage = `{bright-red-fg}${message}{/bright-red-fg}`; 290 | else if (type === "success") coloredMessage = `{bright-green-fg}${message}{/bright-green-fg}`; 291 | else if (type === "warning") coloredMessage = `{bright-yellow-fg}${message}{/bright-yellow-fg}`; 292 | else if (type === "debug") coloredMessage = `{bright-magenta-fg}${message}{/bright-magenta-fg}`; 293 | 294 | transactionLogs.push(`{bright-cyan-fg}[{/bright-cyan-fg} {bold}{magenta-fg}${timestamp}{/magenta-fg}{/bold} {bright-cyan-fg}]{/bright-cyan-fg} {bold}{bright-cyan-fg}[{/bright-cyan-fg}{magenta-fg}${network}{/magenta-fg}{bright-cyan-fg}]{/bright-cyan-fg}{/bold}{bold} ${coloredMessage}{/bold}`); 295 | updateLogs(); 296 | } 297 | 298 | function getRandomDelay() { 299 | return Math.random() * (60000 - 30000) + 30000; 300 | } 301 | 302 | function getRandomNumber(min, max) { 303 | return Math.random() * (max - min) + min; 304 | } 305 | 306 | function updateLogs() { 307 | logsBox.setContent(transactionLogs.join("\n")); 308 | logsBox.setScrollPerc(100); 309 | safeRender(); 310 | } 311 | 312 | function clearTransactionLogs() { 313 | transactionLogs = []; 314 | logsBox.setContent(""); 315 | logsBox.setScroll(0); 316 | updateLogs(); 317 | safeRender(); 318 | addLog("Transaction logs telah dihapus.", "system", currentNetwork); 319 | } 320 | 321 | async function fetchMyUserId() { 322 | if (MY_USER_ID) return MY_USER_ID; 323 | const res = await axios.get("https://discord.com/api/v9/users/@me", { 324 | headers: { Authorization: DISCORD_TOKEN } 325 | }); 326 | MY_USER_ID = res.data.id; 327 | return MY_USER_ID; 328 | } 329 | 330 | async function delayWithCancel(ms) { 331 | const start = Date.now(); 332 | while (Date.now() - start < ms) { 333 | if (claimCancelled) { 334 | return false; 335 | } 336 | await new Promise(resolve => setTimeout(resolve, 100)); 337 | } 338 | return true; 339 | } 340 | 341 | async function waitWithCancel(delay, type, network) { 342 | return Promise.race([ 343 | new Promise(resolve => setTimeout(resolve, delay)), 344 | new Promise(resolve => { 345 | const interval = setInterval(() => { 346 | if (type === "swap" && ( 347 | (network === "Sepolia" && swapCancelledSepolia) || 348 | (network === "Sepolia R2" && swapCancelledSepoliaR2) || 349 | (network === "Pharos" && swapCancelledPharos) || 350 | (network === "Monad" && swapCancelledMonad) 351 | )) { 352 | clearInterval(interval); 353 | resolve(); 354 | } 355 | }, 100); 356 | }) 357 | ]); 358 | } 359 | 360 | async function addTransactionToQueue(transactionFunction, description = "Transaksi", network) { 361 | const transactionId = ++transactionIdCounter; 362 | transactionQueueList.push({ 363 | id: transactionId, 364 | description, 365 | timestamp: new Date().toLocaleTimeString(), 366 | status: "queued" 367 | }); 368 | addLog(`Transaksi [${transactionId}] ditambahkan ke antrean: ${description}`, "system", network || currentNetwork); 369 | updateQueueDisplay(); 370 | 371 | transactionQueue = transactionQueue.then(async () => { 372 | updateTransactionStatus(transactionId, "processing"); 373 | let normalizedNetwork; 374 | let localProvider; 375 | try { 376 | let config; 377 | normalizedNetwork = network; 378 | if (!network) { 379 | normalizedNetwork = currentNetwork; 380 | } 381 | if (normalizedNetwork === "Sepolia" || normalizedNetwork === "Sepolia Testnet") { 382 | config = SEPOLIA_CONFIG; 383 | normalizedNetwork = "Sepolia"; 384 | } else if (normalizedNetwork === "Sepolia R2" || normalizedNetwork === "Sepolia R2 Testnet") { 385 | config = SEPOLIA_R2_CONFIG; 386 | normalizedNetwork = "Sepolia R2"; 387 | } else if (normalizedNetwork === "Pharos" || normalizedNetwork === "Pharos Network") { 388 | config = PHRS_CONFIG; 389 | normalizedNetwork = "Pharos"; 390 | } else if (normalizedNetwork === "Monad" || normalizedNetwork === "Monad Network") { 391 | config = MONAD_CONFIG; 392 | normalizedNetwork = "Monad"; 393 | } else { 394 | throw new Error(`Jaringan tidak dikenal: ${normalizedNetwork || 'tidak diberikan'}`); 395 | } 396 | 397 | addLog(`NormalizedNetwork disetel ke: ${normalizedNetwork}`, "debug", normalizedNetwork); 398 | 399 | localProvider = new ethers.JsonRpcProvider(config.RPC_URL); 400 | const localWallet = new ethers.Wallet(process.env.PRIVATE_KEY, localProvider); 401 | 402 | let nextNonce; 403 | if (normalizedNetwork === "Sepolia") { 404 | if (nextNonceSepolia === null) { 405 | nextNonceSepolia = await localProvider.getTransactionCount(localWallet.address, "pending"); 406 | } 407 | nextNonce = nextNonceSepolia; 408 | } else if (normalizedNetwork === "Sepolia R2") { 409 | if (nextNonceSepoliaR2 === null) { 410 | nextNonceSepoliaR2 = await localProvider.getTransactionCount(localWallet.address, "pending"); 411 | } 412 | nextNonce = nextNonceSepoliaR2; 413 | } else if (normalizedNetwork === "Pharos") { 414 | if (nextNoncePharos === null) { 415 | nextNoncePharos = await localProvider.getTransactionCount(localWallet.address, "pending"); 416 | } 417 | nextNonce = nextNoncePharos; 418 | } else if (normalizedNetwork === "Monad") { 419 | if (nextNonceMonad === null) { 420 | nextNonceMonad = await localProvider.getTransactionCount(localWallet.address, "pending"); 421 | } 422 | nextNonce = nextNonceMonad; 423 | } 424 | 425 | const tx = await transactionFunction(nextNonce, localWallet, localProvider, config); 426 | const txHash = tx.hash; 427 | addLog(`Transaksi Dikirim. Hash: ${getShortHash(txHash)}`, "warning", normalizedNetwork); 428 | const receipt = await tx.wait(); 429 | if (normalizedNetwork === "Sepolia") nextNonceSepolia++; 430 | else if (normalizedNetwork === "Sepolia R2") nextNonceSepoliaR2++; 431 | else if (normalizedNetwork === "Pharos") nextNoncePharos++; 432 | else if (normalizedNetwork === "Monad") nextNonceMonad++; 433 | 434 | if (receipt.status === 1) { 435 | updateTransactionStatus(transactionId, "completed"); 436 | addLog(`Transaksi Selesai. Hash: ${getShortHash(receipt.transactionHash || txHash)}`, "success", normalizedNetwork); 437 | } else { 438 | updateTransactionStatus(transactionId, "failed"); 439 | addLog(`Transaksi [${transactionId}] gagal: Transaksi ditolak oleh kontrak.`, "error", normalizedNetwork); 440 | } 441 | return { receipt, txHash, tx }; 442 | } catch (error) { 443 | updateTransactionStatus(transactionId, "error"); 444 | let errorMessage = error.message; 445 | if (error.code === "CALL_EXCEPTION") { 446 | errorMessage = `Transaksi ditolak oleh kontrak: ${error.reason || "Alasan tidak diketahui"}`; 447 | } 448 | addLog(`Transaksi [${transactionId}] gagal: ${errorMessage}`, "error", network || currentNetwork); 449 | if (error.message.includes("nonce has already been used")) { 450 | if (normalizedNetwork === "Sepolia") nextNonceSepolia++; 451 | else if (normalizedNetwork === "Sepolia R2") nextNonceSepoliaR2++; 452 | else if (normalizedNetwork === "Pharos") nextNoncePharos++; 453 | else if (normalizedNetwork === "Monad") nextNonceMonad++; 454 | } else if (error.message.includes("nonce too high")) { 455 | const currentNonce = await localProvider.getTransactionCount(localWallet.address, "pending"); 456 | if (normalizedNetwork === "Sepolia") nextNonceSepolia = currentNonce; 457 | else if (normalizedNetwork === "Sepolia R2") nextNonceSepoliaR2 = currentNonce; 458 | else if (normalizedNetwork === "Pharos") nextNoncePharos = currentNonce; 459 | else if (normalizedNetwork === "Monad") nextNonceMonad = currentNonce; 460 | } 461 | return null; 462 | } finally { 463 | removeTransactionFromQueue(transactionId); 464 | updateQueueDisplay(); 465 | } 466 | }); 467 | return transactionQueue; 468 | } 469 | 470 | async function claimAllFaucetsWithDelay(network, { isDailyClaim = false } = {}) { 471 | claimRunning = true; 472 | claimCancelled = false; 473 | claimFaucetSubMenu.setItems(getClaimFaucetSubMenuItems()); 474 | safeRender(); 475 | 476 | const networks = Object.keys(NETWORK_CHANNEL_IDS); 477 | for (const network of networks) { 478 | if (claimCancelled) { 479 | addLog("Proses claim faucet dihentikan.", "warning", network); 480 | break; 481 | } 482 | await claimFaucet(network); 483 | if (!claimCancelled && network !== networks[networks.length - 1]) { 484 | const delay = Math.floor(Math.random() * (10000 - 5000 + 1)) + 5000; 485 | addLog(`Menunggu ${delay / 1000} Detik sebelum klaim berikutnya...`, "swap", network); 486 | const delayCompleted = await delayWithCancel(delay); 487 | if (!delayCompleted) { 488 | addLog("Proses claim faucet dihentikan selama jeda.", "warning", network); 489 | break; 490 | } 491 | } 492 | } 493 | 494 | claimRunning = false; 495 | if (isDailyClaim) { 496 | addLog("Auto Daily Claim Faucet selesai, menunggu 24 jam untuk Looping.", "swap", network); 497 | } else { 498 | addLog("Auto Claim Faucet selesai.", "success", network); 499 | } claimFaucetSubMenu.setItems(getClaimFaucetSubMenuItems()); 500 | safeRender(); 501 | } 502 | 503 | function startAutoDailyClaim() { 504 | if (dailyClaimInterval) { 505 | addLog("Auto Daily Claim Faucet All Network sudah berjalan.", "warning"); 506 | return; 507 | } 508 | dailyClaimInterval = setInterval(() => { 509 | if (!claimRunning) { 510 | claimAllFaucetsWithDelay(); 511 | } 512 | },86400000); 513 | claimAllFaucetsWithDelay(); 514 | addLog("Auto Daily Claim Faucet All Network dimulai.", "system"); 515 | claimFaucetSubMenu.setItems(getClaimFaucetSubMenuItems()); 516 | safeRender(); 517 | } 518 | 519 | function stopAutoDailyClaim(network) { 520 | if (dailyClaimInterval) { 521 | clearInterval(dailyClaimInterval); 522 | dailyClaimInterval = null; 523 | addLog("Auto Daily Claim Faucet All Network dihentikan.", "system", network); 524 | } 525 | if (claimRunning) { 526 | claimCancelled = true; 527 | addLog("Proses claim faucet dihentikan.", "system", network); 528 | } 529 | claimFaucetSubMenu.setItems(getClaimFaucetSubMenuItems()); 530 | safeRender(); 531 | } 532 | 533 | async function claimFaucet(network) { 534 | try { 535 | if (!DISCORD_TOKEN) { 536 | throw new Error("DISCORD_TOKEN tidak ditemukan di .env. Silakan tambahkan token Discord Anda."); 537 | } 538 | if (!wallet_address) { 539 | throw new Error("Wallet address tidak tersedia. Cek Private Key Anda"); 540 | } 541 | const channelId = NETWORK_CHANNEL_IDS[network]; 542 | if (!channelId) { 543 | throw new Error(`Jaringan ${network} tidak didukung untuk claim faucet.`); 544 | } 545 | 546 | const userId = await fetchMyUserId(); 547 | const address = wallet_address; 548 | 549 | const payload = { 550 | type: 2, 551 | application_id: APP_ID, 552 | guild_id: GUILD_ID, 553 | channel_id: channelId, 554 | session_id: uuid(), 555 | data: { 556 | version: COMMAND_VERSION, 557 | id: COMMAND_ID, 558 | name: "faucet", 559 | type: 1, 560 | options: [{ type: 3, name: "address", value: address }] 561 | }, 562 | nonce: Date.now().toString() 563 | }; 564 | const form = new FormData(); 565 | form.append("payload_json", JSON.stringify(payload)); 566 | 567 | await axios.post("https://discord.com/api/v9/interactions", form, { 568 | headers: { Authorization: DISCORD_TOKEN, ...form.getHeaders() } 569 | }); 570 | addLog(`Command Claiming Faucet Sent...`, "swap", network); 571 | await new Promise(resolve => setTimeout(resolve, 2000)); 572 | const res = await axios.get( 573 | `https://discord.com/api/v9/channels/${channelId}/messages?limit=10`, 574 | { headers: { Authorization: DISCORD_TOKEN } } 575 | ); 576 | const messages = res.data; 577 | const myResponse = messages.find(m => 578 | m.author.id === APP_ID && m.interaction?.user?.id === userId 579 | ); 580 | 581 | if (!myResponse) { 582 | addLog(`No Response Claiming ${network}.`, "warning", network); 583 | return; 584 | } 585 | 586 | const txt = myResponse.content || ""; 587 | if (txt.includes("successfully")) { 588 | addLog(`Claiming Faucet ${network} Successfully`, "success", network); 589 | } else if (txt.toLowerCase().includes("claim failed")) { 590 | addLog(`${txt.split("\n")[0]}`, "warning", network); 591 | } else { 592 | addLog(`Unknown Status Clain at ${network}: ${txt}`, "system", network); 593 | } 594 | } catch (error) { 595 | addLog(`Error: ${error.message}`, "error", network); 596 | } 597 | } 598 | 599 | function updateTransactionStatus(id, status) { 600 | transactionQueueList.forEach(tx => { 601 | if (tx.id === id) tx.status = status; 602 | }); 603 | updateQueueDisplay(); 604 | } 605 | 606 | function removeTransactionFromQueue(id) { 607 | transactionQueueList = transactionQueueList.filter(tx => tx.id !== id); 608 | updateQueueDisplay(); 609 | } 610 | 611 | function getTransactionQueueContent() { 612 | if (transactionQueueList.length === 0) return "Tidak ada transaksi dalam antrean."; 613 | return transactionQueueList 614 | .map(tx => `ID: ${tx.id} | ${tx.description} | ${tx.status} | ${tx.timestamp}`) 615 | .join("\n"); 616 | } 617 | 618 | let queueMenuBox = null; 619 | let queueUpdateInterval = null; 620 | 621 | function showTransactionQueueMenu() { 622 | const container = blessed.box({ 623 | label: " Antrian Transaksi ", 624 | top: "10%", 625 | left: "center", 626 | width: "80%", 627 | height: "80%", 628 | border: { type: "line" }, 629 | style: { border: { fg: "blue" } }, 630 | keys: true, 631 | mouse: true, 632 | interactive: true 633 | }); 634 | const contentBox = blessed.box({ 635 | top: 0, 636 | left: 0, 637 | width: "100%", 638 | height: "90%", 639 | content: getTransactionQueueContent(), 640 | scrollable: true, 641 | keys: true, 642 | mouse: true, 643 | alwaysScroll: true, 644 | scrollbar: { ch: " ", inverse: true, style: { bg: "blue" } } 645 | }); 646 | const exitButton = blessed.button({ 647 | content: " [Keluar] ", 648 | bottom: 0, 649 | left: "center", 650 | shrink: true, 651 | padding: { left: 1, right: 1 }, 652 | style: { fg: "white", bg: "red", hover: { bg: "blue" } }, 653 | mouse: true, 654 | keys: true, 655 | interactive: true 656 | }); 657 | exitButton.on("press", () => { 658 | addLog("Keluar Dari Menu Antrian Transaksi.", "system", currentNetwork); 659 | clearInterval(queueUpdateInterval); 660 | container.destroy(); 661 | queueMenuBox = null; 662 | mainMenu.show(); 663 | mainMenu.focus(); 664 | screen.render(); 665 | }); 666 | container.key(["a", "s", "d"], () => { 667 | addLog("Keluar Dari Menu Antrian Transaksi.", "system", currentNetwork); 668 | clearInterval(queueUpdateInterval); 669 | container.destroy(); 670 | queueMenuBox = null; 671 | mainMenu.show(); 672 | mainMenu.focus(); 673 | screen.render(); 674 | }); 675 | container.append(contentBox); 676 | container.append(exitButton); 677 | queueUpdateInterval = setInterval(() => { 678 | contentBox.setContent(getTransactionQueueContent()); 679 | screen.render(); 680 | }, 1000); 681 | mainMenu.hide(); 682 | screen.append(container); 683 | container.focus(); 684 | screen.render(); 685 | } 686 | 687 | function updateQueueDisplay() { 688 | if (queueMenuBox) { 689 | queueMenuBox.setContent(getTransactionQueueContent()); 690 | screen.render(); 691 | } 692 | } 693 | 694 | const screen = blessed.screen({ 695 | smartCSR: true, 696 | title: "R2 Auto Bot", 697 | fullUnicode: true, 698 | mouse: true 699 | }); 700 | 701 | let renderTimeout; 702 | 703 | function safeRender() { 704 | if (renderTimeout) clearTimeout(renderTimeout); 705 | renderTimeout = setTimeout(() => { screen.render(); }, 50); 706 | } 707 | 708 | const headerBox = blessed.box({ 709 | top: 0, 710 | left: "center", 711 | width: "100%", 712 | tags: true, 713 | style: { fg: "white", bg: "default" } 714 | }); 715 | 716 | figlet.text("NT EXHAUST".toUpperCase(), { font: "ANSI Shadow" }, (err, data) => { 717 | if (err) headerBox.setContent("{center}{bold}NT EXHAUST{/bold}{/center}"); 718 | else headerBox.setContent(`{center}{bold}{bright-cyan-fg}${data}{/bright-cyan-fg}{/bold}{/center}`); 719 | safeRender(); 720 | }); 721 | 722 | const descriptionBox = blessed.box({ 723 | left: "center", 724 | width: "100%", 725 | content: "{center}{bold}{grey-fg}________________________________________________________________________{/grey-fg}{/bold}{/center}", 726 | tags: true, 727 | style: { fg: "white", bg: "default" } 728 | }); 729 | 730 | const logsBox = blessed.box({ 731 | label: " Transaction Logs ", 732 | left: 0, 733 | border: { type: "line" }, 734 | scrollable: true, 735 | alwaysScroll: true, 736 | mouse: true, 737 | keys: true, 738 | vi: true, 739 | tags: true, 740 | style: { border: { fg: "yellow" }, fg: "white" }, 741 | scrollbar: { ch: " ", inverse: true, style: { bg: "blue" } }, 742 | content: "" 743 | }); 744 | 745 | const welcomeBox = blessed.box({ 746 | label: " Dashboard ", 747 | border: { type: "line" }, 748 | tags: true, 749 | style: { border: { fg: "cyan" }, fg: "white", bg: "default" }, 750 | content: "{center}{bold}Initializing...{/bold}{/center}" 751 | }); 752 | 753 | const walletBox = blessed.box({ 754 | label: " Informasi Wallet ", 755 | border: { type: "line" }, 756 | tags: true, 757 | style: { border: { fg: "magenta" }, fg: "white", bg: "default" }, 758 | content: "Loading data wallet..." 759 | }); 760 | 761 | walletBox.hide(); 762 | 763 | const mainMenu = blessed.list({ 764 | label: " Menu ", 765 | left: "60%", 766 | keys: true, 767 | vi: true, 768 | mouse: true, 769 | border: { type: "line" }, 770 | style: { fg: "white", bg: "default", border: { fg: "red" }, selected: { bg: "green", fg: "black" } }, 771 | items: getMainMenItems() 772 | }); 773 | 774 | function getMainMenItems() { 775 | let items = []; 776 | if (swapRunningSepolia || swapRunningSepoliaR2 || swapRunningPharos || swapRunningMonad) items.push("Stop All Transaction"); 777 | items = items.concat([ 778 | "Sepolia Network", 779 | "Sepolia R2 Network", 780 | "Pharos Network", 781 | "Monad Network", 782 | "Claim Faucet", 783 | "Antrian Transaksi", 784 | "Clear Transaction Logs", 785 | "Refresh", 786 | "Exit" 787 | ]); 788 | return items; 789 | } 790 | 791 | function getSepoliaSubMenuItems() { 792 | let items = []; 793 | if (swapRunningSepolia) items.push("Stop Transaction"); 794 | items = items.concat([ 795 | "Auto Swap R2USD & USDC", 796 | "Auto Stake R2USD & sR2USD", 797 | "Auto Add LP R2USD & sR2USD", 798 | "Auto Add LP USDC & R2USD", 799 | "Manual Swap", 800 | "Change Random Amount", 801 | "Clear Transaction Logs", 802 | "Back To Main Menu", 803 | "Refresh" 804 | ]); 805 | return items; 806 | } 807 | 808 | function getSepoliaR2SubMenuItems() { 809 | let items = []; 810 | if (swapRunningSepoliaR2) items.push("Stop Transaction"); 811 | items = items.concat([ 812 | "Auto Swap R2 & USDC", 813 | "Auto Swap R2 & R2USD", 814 | "Auto Add LP R2 & USDC", 815 | "Auto Add LP R2 & R2USD", 816 | "Manual Swap", 817 | "Change Random Amount", 818 | "Clear Transaction Logs", 819 | "Back To Main Menu", 820 | "Refresh" 821 | ]); 822 | return items; 823 | } 824 | 825 | function getPharosSubMenuItems() { 826 | let items = []; 827 | if (swapRunningPharos) items.push("Stop Transaction"); 828 | items = items.concat([ 829 | "Auto Swap R2USD & USDC", 830 | "Auto Stake R2USD & sR2USD", 831 | "Manual Swap", 832 | "Change Random Amount", 833 | "Clear Transaction Logs", 834 | "Back To Main Menu", 835 | "Refresh" 836 | ]); 837 | return items; 838 | } 839 | 840 | function getMonadSubMenuItems() { 841 | let items = []; 842 | if (swapRunningMonad) items.push("Stop Transaction"); 843 | items = items.concat([ 844 | "Auto Swap R2USD & USDC", 845 | "Auto Stake R2USD & sR2USD", 846 | "Auto Add LP R2USD & sR2USD", 847 | "Auto Add LP USDC & R2USD", 848 | "Manual Swap", 849 | "Change Random Amount", 850 | "Clear Transaction Logs", 851 | "Back To Main Menu", 852 | "Refresh" 853 | ]); 854 | return items; 855 | } 856 | 857 | 858 | function getClaimFaucetSubMenuItems() { 859 | const items = [ 860 | "Auto Claim Faucet All Network", 861 | "Auto Daily Claim Faucet All Network", 862 | "Claim Faucet Sepolia", 863 | "Claim Faucet Sepolia R2", 864 | "Claim Faucet Pharos", 865 | "Claim Faucet Monad", 866 | "Clear Transaction Logs", 867 | "Refresh", 868 | "Back to Main Menu", 869 | ]; 870 | 871 | if (dailyClaimInterval) { 872 | items.splice(1, 0, "Stop Auto Daily Claim"); 873 | } else if (claimRunning) { 874 | items.splice(1, 0, "Stop Proses"); 875 | } 876 | 877 | return items; 878 | } 879 | 880 | 881 | const sepoliaSubMenu = blessed.list({ 882 | label: " Sepolia Network Sub Menu ", 883 | left: "60%", 884 | keys: true, 885 | vi: true, 886 | mouse: true, 887 | tags: true, 888 | border: { type: "line" }, 889 | style: { fg: "white", bg: "default", border: { fg: "red" }, selected: { bg: "cyan", fg: "black" } }, 890 | items: getSepoliaSubMenuItems() 891 | }); 892 | sepoliaSubMenu.hide(); 893 | 894 | const r2SepoliaSubMenu = blessed.list({ 895 | label: " Sepolia R2 Network Sub Menu ", 896 | left: "60%", 897 | keys: true, 898 | vi: true, 899 | mouse: true, 900 | tags: true, 901 | border: { type: "line" }, 902 | style: { fg: "white", bg: "default", border: { fg: "red" }, selected: { bg: "cyan", fg: "black" } }, 903 | items: getSepoliaR2SubMenuItems() 904 | }); 905 | r2SepoliaSubMenu.hide(); 906 | 907 | const pharosSubMenu = blessed.list({ 908 | label: " Pharos Network Sub Menu ", 909 | left: "60%", 910 | keys: true, 911 | vi: true, 912 | mouse: true, 913 | tags: true, 914 | border: { type: "line" }, 915 | style: { fg: "white", bg: "default", border: { fg: "red" }, selected: { bg: "cyan", fg: "black" } }, 916 | items: getPharosSubMenuItems() 917 | }); 918 | pharosSubMenu.hide(); 919 | 920 | const monadSubMenu = blessed.list({ 921 | label: " Monad Network Sub Menu ", 922 | left: "60%", 923 | keys: true, 924 | vi: true, 925 | mouse: true, 926 | tags: true, 927 | border: { type: "line" }, 928 | style: { fg: "white", bg: "default", border: { fg: "red" }, selected: { bg: "cyan", fg: "black" } }, 929 | items: getMonadSubMenuItems() 930 | }); 931 | monadSubMenu.hide(); 932 | 933 | const claimFaucetSubMenu = blessed.list({ 934 | label: " Claim Faucet ", 935 | left: "60%", 936 | keys: true, 937 | vi: true, 938 | mouse: true, 939 | tags: true, 940 | border: { type: "line" }, 941 | style: { fg: "white", bg: "default", border: { fg: "red" }, selected: { bg: "cyan", fg: "black" } }, 942 | items: getClaimFaucetSubMenuItems() 943 | }); 944 | claimFaucetSubMenu.hide(); 945 | 946 | const sepoliaManualSwapSubMenu = blessed.list({ 947 | label: " Manual Swap ", 948 | left: "60%", 949 | keys: true, 950 | vi: true, 951 | mouse: true, 952 | tags: true, 953 | border: { type: "line" }, 954 | style: { fg: "white", bg: "default", border: { fg: "red" }, selected: { bg: "cyan", fg: "black" } }, 955 | items: ["USDC -> R2USD", "R2USD -> USDC", "Back To Sepolia Network Menu"] 956 | }); 957 | sepoliaManualSwapSubMenu.hide(); 958 | 959 | const r2SepoliaManualSwapSubMenu = blessed.list({ 960 | label: " Manual Swap ", 961 | left: "60%", 962 | keys: true, 963 | vi: true, 964 | mouse: true, 965 | tags: true, 966 | border: { type: "line" }, 967 | style: { fg: "white", bg: "default", border: { fg: "red" }, selected: { bg: "cyan", fg: "black" } }, 968 | items: [ 969 | "R2 -> USDC", 970 | "USDC -> R2", 971 | "R2 -> R2USD", 972 | "R2USD -> R2", 973 | "Back To Sepolia R2 Network Menu" 974 | ] 975 | }); 976 | r2SepoliaManualSwapSubMenu.hide(); 977 | 978 | const pharosManualSwapSubMenu = blessed.list({ 979 | label: " Manual Swap ", 980 | left: "60%", 981 | keys: true, 982 | vi: true, 983 | mouse: true, 984 | tags: true, 985 | border: { type: "line" }, 986 | style: { fg: "white", bg: "default", border: { fg: "red" }, selected: { bg: "cyan", fg: "black" } }, 987 | items: ["USDC -> R2USD", "R2USD -> USDC", "Back To Pharos Network Menu"] 988 | }); 989 | pharosManualSwapSubMenu.hide(); 990 | 991 | const monadManualSwapSubMenu = blessed.list({ 992 | label: " Manual Swap ", 993 | left: "60%", 994 | keys: true, 995 | vi: true, 996 | mouse: true, 997 | tags: true, 998 | border: { type: "line" }, 999 | style: { fg: "white", bg: "default", border: { fg: "red" }, selected: { bg: "cyan", fg: "black" } }, 1000 | items: ["USDC -> R2USD", "R2USD -> USDC", "Back To Monad Network Menu"] 1001 | }); 1002 | monadManualSwapSubMenu.hide(); 1003 | 1004 | const sepoliaChangeRandomAmountSubMenu = blessed.list({ 1005 | label: " Change Random Amount ", 1006 | left: "60%", 1007 | keys: true, 1008 | vi: true, 1009 | mouse: true, 1010 | tags: true, 1011 | border: { type: "line" }, 1012 | style: { fg: "white", bg: "default", border: { fg: "red" }, selected: { bg: "cyan", fg: "black" } }, 1013 | items: ["SWAP_R2USD_USDC", "Back To Sepolia Network Menu"] 1014 | }); 1015 | sepoliaChangeRandomAmountSubMenu.hide(); 1016 | 1017 | const r2SepoliaChangeRandomAmountSubMenu = blessed.list({ 1018 | label: " Change Random Amount ", 1019 | left: "60%", 1020 | keys: true, 1021 | vi: true, 1022 | mouse: true, 1023 | tags: true, 1024 | border: { type: "line" }, 1025 | style: { fg: "white", bg: "default", border: { fg: "red" }, selected: { bg: "cyan", fg: "black" } }, 1026 | items: ["SWAP_R2USD_USDC", "Back To Sepolia R2 Network Menu"] 1027 | }); 1028 | r2SepoliaChangeRandomAmountSubMenu.hide(); 1029 | 1030 | const pharosChangeRandomAmountSubMenu = blessed.list({ 1031 | label: " Change Random Amount ", 1032 | left: "60%", 1033 | keys: true, 1034 | vi: true, 1035 | mouse: true, 1036 | tags: true, 1037 | border: { type: "line" }, 1038 | style: { fg: "white", bg: "default", border: { fg: "red" }, selected: { bg: "cyan", fg: "black" } }, 1039 | items: ["SWAP_R2USD_USDC", "Back To Pharos Network Menu"] 1040 | }); 1041 | pharosChangeRandomAmountSubMenu.hide(); 1042 | 1043 | const monadChangeRandomAmountSubMenu = blessed.list({ 1044 | label: " Change Random Amount ", 1045 | left: "60%", 1046 | keys: true, 1047 | vi: true, 1048 | mouse: true, 1049 | tags: true, 1050 | border: { type: "line" }, 1051 | style: { fg: "white", bg: "default", border: { fg: "red" }, selected: { bg: "cyan", fg: "black" } }, 1052 | items: ["SWAP_R2USD_USDC", "Back To Monad Network Menu"] 1053 | }); 1054 | monadChangeRandomAmountSubMenu.hide(); 1055 | 1056 | const promptBox = blessed.prompt({ 1057 | parent: screen, 1058 | border: "line", 1059 | height: 5, 1060 | width: "60%", 1061 | top: "center", 1062 | left: "center", 1063 | label: "{bright-blue-fg}Prompt{/bright-blue-fg}", 1064 | tags: true, 1065 | keys: true, 1066 | vi: true, 1067 | mouse: true, 1068 | style: { fg: "bright-red", bg: "default", border: { fg: "red" } } 1069 | }); 1070 | 1071 | screen.append(headerBox); 1072 | screen.append(descriptionBox); 1073 | screen.append(logsBox); 1074 | screen.append(welcomeBox); 1075 | screen.append(walletBox); 1076 | screen.append(mainMenu); 1077 | screen.append(sepoliaSubMenu); 1078 | screen.append(r2SepoliaSubMenu); 1079 | screen.append(pharosSubMenu); 1080 | screen.append(monadSubMenu); 1081 | screen.append(claimFaucetSubMenu); 1082 | screen.append(sepoliaManualSwapSubMenu); 1083 | screen.append(r2SepoliaManualSwapSubMenu); 1084 | screen.append(pharosManualSwapSubMenu); 1085 | screen.append(monadManualSwapSubMenu); 1086 | screen.append(sepoliaChangeRandomAmountSubMenu); 1087 | screen.append(r2SepoliaChangeRandomAmountSubMenu); 1088 | screen.append(pharosChangeRandomAmountSubMenu); 1089 | screen.append(monadChangeRandomAmountSubMenu); 1090 | 1091 | function updateWelcomeBox() { 1092 | const currentTime = new Date().toLocaleString("en-US", { timeZone: "Asia/Jakarta" }); 1093 | const botVersion = "V2.1.0"; 1094 | 1095 | const content = 1096 | `{center}{bold}{bright-red-fg}[:: R2 :: AUTO :: BOT ::]{/bright-red-fg}{/bold}{/center}\n\n` + 1097 | `{center}{bold}{bright-yellow-fg}Version : ${botVersion}{/bright-yellow-fg}{/bold}{/center}\n` + 1098 | `{center}{bold}{bright-cyan-fg}➥ Join Telegram : t.me/NTExhaust{/bright-cyan-fg}{/bold}{/center}\n` + 1099 | `{center}{bold}{bright-cyan-fg}➥ Subscribe : Youtube.com/@NTExhaust{/bright-cyan-fg}{/bold}{/center}\n` + 1100 | `{center}{bold}{grey-fg}Donate : saweria.co/vinsenzo{/grey-fg}{/bold}{/center}\n`; 1101 | 1102 | welcomeBox.setContent(content); 1103 | safeRender(); 1104 | } 1105 | 1106 | function adjustLayout() { 1107 | const screenHeight = screen.height; 1108 | const screenWidth = screen.width; 1109 | const headerHeight = Math.max(8, Math.floor(screenHeight * 0.15)); 1110 | headerBox.top = 0; 1111 | headerBox.height = headerHeight; 1112 | headerBox.width = "100%"; 1113 | descriptionBox.top = "23%"; 1114 | descriptionBox.height = Math.floor(screenHeight * 0.05); 1115 | logsBox.top = headerHeight + descriptionBox.height; 1116 | logsBox.left = 0; 1117 | logsBox.width = Math.floor(screenWidth * 0.6); 1118 | logsBox.height = screenHeight - (headerHeight + descriptionBox.height); 1119 | welcomeBox.top = headerHeight + descriptionBox.height; 1120 | welcomeBox.left = Math.floor(screenWidth * 0.6); 1121 | welcomeBox.width = Math.floor(screenWidth * 0.4); 1122 | welcomeBox.height = Math.floor(screenHeight * 0.35); 1123 | walletBox.top = headerHeight + descriptionBox.height; 1124 | walletBox.left = Math.floor(screenWidth * 0.6); 1125 | walletBox.width = Math.floor(screenWidth * 0.4); 1126 | walletBox.height = Math.floor(screenHeight * 0.35); 1127 | mainMenu.top = headerHeight + descriptionBox.height + welcomeBox.height; 1128 | mainMenu.left = Math.floor(screenWidth * 0.6); 1129 | mainMenu.width = Math.floor(screenWidth * 0.4); 1130 | mainMenu.height = screenHeight - (headerHeight + descriptionBox.height + welcomeBox.height); 1131 | sepoliaSubMenu.top = mainMenu.top; 1132 | sepoliaSubMenu.left = mainMenu.left; 1133 | sepoliaSubMenu.width = mainMenu.width; 1134 | sepoliaSubMenu.height = mainMenu.height; 1135 | r2SepoliaSubMenu.top = mainMenu.top; 1136 | r2SepoliaSubMenu.left = mainMenu.left; 1137 | r2SepoliaSubMenu.width = mainMenu.width; 1138 | r2SepoliaSubMenu.height = mainMenu.height; 1139 | pharosSubMenu.top = mainMenu.top; 1140 | pharosSubMenu.left = mainMenu.left; 1141 | pharosSubMenu.width = mainMenu.width; 1142 | pharosSubMenu.height = mainMenu.height; 1143 | monadSubMenu.top = mainMenu.top; 1144 | monadSubMenu.left = mainMenu.left; 1145 | monadSubMenu.width = mainMenu.width; 1146 | monadSubMenu.height = mainMenu.height; 1147 | claimFaucetSubMenu.top = mainMenu.top; 1148 | claimFaucetSubMenu.left = mainMenu.left; 1149 | claimFaucetSubMenu.width = mainMenu.width; 1150 | claimFaucetSubMenu.height = mainMenu.height; 1151 | sepoliaManualSwapSubMenu.top = mainMenu.top; 1152 | sepoliaManualSwapSubMenu.left = mainMenu.left; 1153 | sepoliaManualSwapSubMenu.width = mainMenu.width; 1154 | sepoliaManualSwapSubMenu.height = mainMenu.height; 1155 | r2SepoliaManualSwapSubMenu.top = mainMenu.top; 1156 | r2SepoliaManualSwapSubMenu.left = mainMenu.left; 1157 | r2SepoliaManualSwapSubMenu.width = mainMenu.width; 1158 | r2SepoliaManualSwapSubMenu.height = mainMenu.height; 1159 | pharosManualSwapSubMenu.top = mainMenu.top; 1160 | pharosManualSwapSubMenu.left = mainMenu.left; 1161 | pharosManualSwapSubMenu.width = mainMenu.width; 1162 | pharosManualSwapSubMenu.height = mainMenu.height; 1163 | monadManualSwapSubMenu.top = mainMenu.top; 1164 | monadManualSwapSubMenu.left = mainMenu.left; 1165 | monadManualSwapSubMenu.width = mainMenu.width; 1166 | monadManualSwapSubMenu.height = mainMenu.height; 1167 | sepoliaChangeRandomAmountSubMenu.top = mainMenu.top; 1168 | sepoliaChangeRandomAmountSubMenu.left = mainMenu.left; 1169 | sepoliaChangeRandomAmountSubMenu.width = mainMenu.width; 1170 | sepoliaChangeRandomAmountSubMenu.height = mainMenu.height; 1171 | r2SepoliaChangeRandomAmountSubMenu.top = mainMenu.top; 1172 | r2SepoliaChangeRandomAmountSubMenu.left = mainMenu.left; 1173 | r2SepoliaChangeRandomAmountSubMenu.width = mainMenu.width; 1174 | r2SepoliaChangeRandomAmountSubMenu.height = mainMenu.height; 1175 | pharosChangeRandomAmountSubMenu.top = mainMenu.top; 1176 | pharosChangeRandomAmountSubMenu.left = mainMenu.left; 1177 | pharosChangeRandomAmountSubMenu.width = mainMenu.width; 1178 | pharosChangeRandomAmountSubMenu.height = mainMenu.height; 1179 | monadChangeRandomAmountSubMenu.top = mainMenu.top; 1180 | monadChangeRandomAmountSubMenu.left = mainMenu.left; 1181 | monadChangeRandomAmountSubMenu.width = mainMenu.width; 1182 | monadChangeRandomAmountSubMenu.height = mainMenu.height; 1183 | safeRender(); 1184 | } 1185 | 1186 | screen.on("resize", adjustLayout); 1187 | adjustLayout(); 1188 | 1189 | async function getTokenBalance(tokenAddress, provider, wallet) { 1190 | try { 1191 | const contract = new ethers.Contract(tokenAddress, ERC20ABI, provider); 1192 | const balance = await contract.balanceOf(wallet.address); 1193 | const decimals = await contract.decimals(); 1194 | return ethers.formatUnits(balance, decimals); 1195 | } catch (error) { 1196 | addLog(`Gagal mengambil saldo token ${tokenAddress}: ${error.message}`, "error", currentNetwork); 1197 | return "0"; 1198 | } 1199 | } 1200 | 1201 | async function updateWalletData(network) { 1202 | addLog(`Debug: updateWalletData menerima network = ${network}`, "debug", network || currentNetwork); 1203 | try { 1204 | let config; 1205 | let normalizedNetwork = network; 1206 | if (network === "Sepolia" || network === "Sepolia Network" || network === "Sepolia Testnet") { 1207 | config = SEPOLIA_CONFIG; 1208 | normalizedNetwork = "Sepolia"; 1209 | } else if (network === "Sepolia R2" || network === "Sepolia DC R2 Network" || network === "Sepolia R2 Testnet") { 1210 | config = SEPOLIA_R2_CONFIG; 1211 | normalizedNetwork = "Sepolia R2"; 1212 | } else if (network === "Pharos" || network === "Pharos Network") { 1213 | config = PHRS_CONFIG; 1214 | normalizedNetwork = "Pharos"; 1215 | } else if (network === "Monad" || network === "Monad Network") { 1216 | config = MONAD_CONFIG; 1217 | normalizedNetwork = "Monad"; 1218 | } else { 1219 | throw new Error(`Jaringan tidak dikenal: ${network || 'tidak diberikan'}`); 1220 | } 1221 | 1222 | const localProvider = new ethers.JsonRpcProvider(config.RPC_URL, undefined, { timeout: 60000 }); 1223 | const localWallet = new ethers.Wallet(process.env.PRIVATE_KEY, localProvider); 1224 | 1225 | walletInfoByNetwork[normalizedNetwork].address = localWallet.address; 1226 | 1227 | if (normalizedNetwork === "Sepolia R2") { 1228 | const [nativeBalance, usdcBalance, r2usdBalance, r2Balance] = await Promise.all([ 1229 | localProvider.getBalance(localWallet.address), 1230 | getTokenBalance(config.USDC_ADDRESS, localProvider, localWallet), 1231 | getTokenBalance(config.R2USD_ADDRESS, localProvider, localWallet), 1232 | getTokenBalance(config.R2_ADDRESS, localProvider, localWallet) 1233 | ]); 1234 | walletInfoByNetwork[normalizedNetwork].balanceNative = ethers.formatEther(nativeBalance); 1235 | walletInfoByNetwork[normalizedNetwork].balanceUsdc = usdcBalance; 1236 | walletInfoByNetwork[normalizedNetwork].balanceR2usd = r2usdBalance; 1237 | walletInfoByNetwork[normalizedNetwork].balanceR2 = r2Balance; 1238 | 1239 | const [lpR2R2usdBalance, lpUsdcR2Balance] = await Promise.all([ 1240 | getTokenBalance(config.LP_R2_R2USD, localProvider, localWallet), 1241 | getTokenBalance(config.LP_USDC_R2, localProvider, localWallet) 1242 | ]); 1243 | walletInfoByNetwork[normalizedNetwork].balanceLpR2R2usd = lpR2R2usdBalance; 1244 | walletInfoByNetwork[normalizedNetwork].balanceLpUsdcR2 = lpUsdcR2Balance; 1245 | } else { 1246 | const [nativeBalance, usdcBalance, r2usdBalance, sr2usdBalance] = await Promise.all([ 1247 | localProvider.getBalance(localWallet.address), 1248 | getTokenBalance(config.USDC_ADDRESS, localProvider, localWallet), 1249 | getTokenBalance(config.R2USD_ADDRESS, localProvider, localWallet), 1250 | getTokenBalance(config.sR2USD_ADDRESS, localProvider, localWallet) 1251 | ]); 1252 | walletInfoByNetwork[normalizedNetwork].balanceNative = ethers.formatEther(nativeBalance); 1253 | walletInfoByNetwork[normalizedNetwork].balanceUsdc = usdcBalance; 1254 | walletInfoByNetwork[normalizedNetwork].balanceR2usd = r2usdBalance; 1255 | walletInfoByNetwork[normalizedNetwork].balanceSr2usd = sr2usdBalance; 1256 | 1257 | if (normalizedNetwork === "Monad") { 1258 | const [lpR2usdSr2usdBalance, lpUsdcR2usdBalance] = await Promise.all([ 1259 | getTokenBalance(config.LP_R2USD_sR2USD_BAL, localProvider, localWallet), 1260 | getTokenBalance(config.LP_USDC_R2USD_BAL, localProvider, localWallet) 1261 | ]); 1262 | walletInfoByNetwork[normalizedNetwork].balanceLpR2usdSr2usd = lpR2usdSr2usdBalance; 1263 | walletInfoByNetwork[normalizedNetwork].balanceLpUsdcR2usd = lpUsdcR2usdBalance; 1264 | } else if (normalizedNetwork === "Sepolia") { 1265 | const [lpR2usdSr2usdBalance, lpUsdcR2usdBalance] = await Promise.all([ 1266 | getTokenBalance(config.LP_R2USD_sR2USD, localProvider, localWallet), 1267 | getTokenBalance(config.LP_USDC_R2USD, localProvider, localWallet) 1268 | ]); 1269 | walletInfoByNetwork[normalizedNetwork].balanceLpR2usdSr2usd = lpR2usdSr2usdBalance; 1270 | walletInfoByNetwork[normalizedNetwork].balanceLpUsdcR2usd = lpUsdcR2usdBalance; 1271 | } 1272 | } 1273 | 1274 | const currentNonce = await localProvider.getTransactionCount(localWallet.address, "pending"); 1275 | if (normalizedNetwork === "Sepolia") { 1276 | nextNonceSepolia = currentNonce; 1277 | addLog(`Nonce awal untuk Sepolia: ${nextNonceSepolia}`, "debug", normalizedNetwork); 1278 | } else if (normalizedNetwork === "Sepolia R2") { 1279 | nextNonceSepoliaR2 = currentNonce; 1280 | addLog(`Nonce awal untuk Sepolia R2: ${nextNonceSepoliaR2}`, "debug", normalizedNetwork); 1281 | } else if (normalizedNetwork === "Pharos") { 1282 | nextNoncePharos = currentNonce; 1283 | addLog(`Nonce awal untuk Pharos: ${nextNoncePharos}`, "debug", normalizedNetwork); 1284 | } else if (normalizedNetwork === "Monad") { 1285 | nextNonceMonad = currentNonce; 1286 | addLog(`Nonce awal untuk Monad: ${nextNonceMonad}`, "debug", normalizedNetwork); 1287 | } 1288 | 1289 | walletInfoByNetwork[normalizedNetwork].network = config.NETWORK_NAME; 1290 | walletInfoByNetwork[normalizedNetwork].status = "Ready"; 1291 | 1292 | if (normalizedNetwork === currentNetwork) { 1293 | updateWallet(); 1294 | } 1295 | 1296 | addLog("Wallet Information Updated !!", "system", normalizedNetwork); 1297 | } catch (error) { 1298 | if (error.code === "TIMEOUT") { 1299 | addLog(`Timeout saat mencoba menghubungi RPC ${config.RPC_URL}. Coba lagi nanti.`, "error", network || currentNetwork); 1300 | } else { 1301 | addLog(`Gagal mengambil data wallet: ${error.message}`, "error", network || currentNetwork); 1302 | } 1303 | } 1304 | } 1305 | 1306 | function updateWallet() { 1307 | const walletInfo = walletInfoByNetwork[currentNetwork]; 1308 | const shortAddress = walletInfo.address ? getShortAddress(walletInfo.address) : "N/A"; 1309 | let nativeToken; 1310 | if (walletInfo.network === "Monad Network") { 1311 | nativeToken = "MON"; 1312 | } else if (walletInfo.network === "Pharos Network") { 1313 | nativeToken = "PHRS"; 1314 | } else { 1315 | nativeToken = "ETH"; 1316 | } 1317 | const nativeBalance = walletInfo.balanceNative ? Number(walletInfo.balanceNative).toFixed(4) : "0.0000"; 1318 | const usdc = walletInfo.balanceUsdc ? Number(walletInfo.balanceUsdc).toFixed(2) : "0.00"; 1319 | const r2usd = walletInfo.balanceR2usd ? Number(walletInfo.balanceR2usd).toFixed(4) : "0.0000"; 1320 | 1321 | let tokenBalances = ''; 1322 | if (walletInfo.network === "Sepolia R2 Testnet") { 1323 | const r2 = walletInfo.balanceR2 ? Number(walletInfo.balanceR2).toFixed(4) : "0.0000"; 1324 | const lpR2R2usd = walletInfo.balanceLpR2R2usd ? Number(walletInfo.balanceLpR2R2usd).toFixed(6) : "0.00000"; 1325 | const lpUsdcR2 = walletInfo.balanceLpUsdcR2 ? Number(walletInfo.balanceLpUsdcR2).toFixed(6) : "0.00000"; 1326 | tokenBalances = `│ ├── R2 : {bright-green-fg}${r2}{/bright-green-fg}\n` + 1327 | `│ ├── LP R2-R2USD : {bright-green-fg}${lpR2R2usd}{/bright-green-fg}\n` + 1328 | `│ └── LP USDC-R2 : {bright-green-fg}${lpUsdcR2}{/bright-green-fg}\n`; 1329 | } else { 1330 | const sr2usd = walletInfo.balanceSr2usd ? Number(walletInfo.balanceSr2usd).toFixed(6) : "0.000000"; 1331 | const lpR2usdSr2usd = walletInfo.network === "Pharos Network" ? "N/A" : (walletInfo.balanceLpR2usdSr2usd ? Number(walletInfo.balanceLpR2usdSr2usd).toFixed(12) : "0.000000000000"); 1332 | const lpUsdcR2usd = walletInfo.network === "Pharos Network" ? "N/A" : (walletInfo.balanceLpUsdcR2usd ? Number(walletInfo.balanceLpUsdcR2usd).toFixed(12) : "0.000000000000"); 1333 | tokenBalances = `│ ├── sR2USD : {bright-green-fg}${sr2usd}{/bright-green-fg}\n` + 1334 | `│ ├── LP R2USD-sR2USD : {bright-green-fg}${lpR2usdSr2usd}{/bright-green-fg}\n` + 1335 | `│ └── LP USDC-R2USD : {bright-green-fg}${lpUsdcR2usd}{/bright-green-fg}\n`; 1336 | } 1337 | 1338 | const content = `┌── Address : {bright-yellow-fg}${shortAddress}{/bright-yellow-fg}\n` + 1339 | `│ ├── ${nativeToken} : {bright-green-fg}${nativeBalance}{/bright-green-fg}\n` + 1340 | `│ ├── USDC : {bright-green-fg}${usdc}{/bright-green-fg}\n` + 1341 | `│ ├── R2USD : {bright-green-fg}${r2usd}{/bright-green-fg}\n` + 1342 | tokenBalances + 1343 | `└── Network : {bright-cyan-fg}${walletInfo.network}{/bright-cyan-fg}`; 1344 | walletBox.setContent(content); 1345 | safeRender(); 1346 | } 1347 | 1348 | async function ensureApproval(tokenAddress, spender, amount, wallet, network) { 1349 | const tokenContract = new ethers.Contract(tokenAddress, ERC20ABI, wallet); 1350 | let allowance = await tokenContract.allowance(wallet.address, spender); 1351 | addLog(`Allowance saat ini untuk ${spender}: ${ethers.formatUnits(allowance, 6)}`, "debug", network); 1352 | allowance = BigInt(allowance.toString()); 1353 | const amountBigInt = BigInt(amount.toString()); 1354 | 1355 | if (allowance < amountBigInt) { 1356 | const approveAmount = ethers.parseUnits("1000000", 6); 1357 | addLog(`Approving ${ethers.formatUnits(approveAmount, 6)} tokens untuk ${spender}`, "system", network); 1358 | const approveTx = await tokenContract.approve(spender, approveAmount); 1359 | await approveTx.wait(); 1360 | addLog(`Approval berhasil untuk ${spender}`, "success", network); 1361 | } else { 1362 | addLog(`Allowance cukup untuk ${spender}`, "debug", network); 1363 | } 1364 | } 1365 | 1366 | async function checkContractPaused(contractAddress, provider, network) { 1367 | const contract = new ethers.Contract(contractAddress, [ 1368 | "function paused() view returns (bool)" 1369 | ], provider); 1370 | try { 1371 | const paused = await contract.paused(); 1372 | addLog(`Status paused kontrak ${contractAddress}: ${paused}`, "debug", network); 1373 | return paused; 1374 | } catch (error) { 1375 | addLog(`Kontrak ${contractAddress} tidak memiliki fungsi paused() atau gagal: ${error.message}`, "warning", network); 1376 | return false; 1377 | } 1378 | } 1379 | 1380 | async function swapTokens(amount, tokenIn, tokenOut, nonce, wallet, provider, config) { 1381 | const network = config.NETWORK_NAME; 1382 | const routerAddress = "0xeE567Fe1712Faf6149d80dA1E6934E354124CfE3"; 1383 | 1384 | const tokenInContract = new ethers.Contract(tokenIn, ERC20ABI, provider); 1385 | const tokenOutContract = new ethers.Contract(tokenOut, ERC20ABI, provider); 1386 | 1387 | const balanceIn = await tokenInContract.balanceOf(wallet.address); 1388 | if (BigInt(balanceIn.toString()) < BigInt(amount.toString())) { 1389 | throw new Error(`Saldo ${tokenIn} tidak cukup`); 1390 | } 1391 | 1392 | await ensureApproval(tokenIn, routerAddress, amount, wallet, network); 1393 | 1394 | const deadline = Math.floor(Date.now() / 1000) + 60 * 20; 1395 | const amountOutMin = 0; 1396 | 1397 | const routerContract = new ethers.Contract(routerAddress, [ 1398 | "function swapExactTokensForTokens(uint256 amountIn, uint256 amountOutMin, address[] path, address to, uint256 deadline) returns (uint256[])" 1399 | ], wallet); 1400 | 1401 | const path = [tokenIn, tokenOut]; 1402 | 1403 | const tx = await routerContract.swapExactTokensForTokens( 1404 | amount, 1405 | amountOutMin, 1406 | path, 1407 | wallet.address, 1408 | deadline, 1409 | { nonce: nonce } 1410 | ); 1411 | 1412 | return tx; 1413 | } 1414 | 1415 | async function autoSwapR2Usdc(network) { 1416 | const ranges = randomAmountRanges["SWAP_R2_USDC"]; 1417 | let amount; 1418 | let txPromise; 1419 | let currentDirection = swapDirection[network]; 1420 | 1421 | if (currentDirection) { 1422 | amount = getRandomNumber(ranges["R2"].min, ranges["R2"].max).toFixed(18); 1423 | addLog(`Mencoba swap: ${amount} R2 ke USDC`, "swap", network); 1424 | try { 1425 | txPromise = addTransactionToQueue( 1426 | (nonce, wallet, provider, config) => swapTokens(ethers.parseUnits(amount, 18), config.R2_ADDRESS, config.USDC_ADDRESS, nonce, wallet, provider, config), 1427 | `Swap ${amount} R2 to USDC`, 1428 | network 1429 | ); 1430 | } catch (error) { 1431 | if (error.message.includes("Saldo R2 tidak cukup")) { 1432 | addLog("Saldo R2 tidak cukup, mencoba swap USDC ke R2", "warning", network); 1433 | amount = getRandomNumber(ranges["USDC"].min, ranges["USDC"].max).toFixed(6); 1434 | txPromise = addTransactionToQueue( 1435 | (nonce, wallet, provider, config) => swapTokens(ethers.parseUnits(amount, 6), config.USDC_ADDRESS, config.R2_ADDRESS, nonce, wallet, provider, config), 1436 | `Swap ${amount} USDC to R2`, 1437 | network 1438 | ); 1439 | } else { 1440 | throw error; 1441 | } 1442 | } 1443 | } else { 1444 | amount = getRandomNumber(ranges["USDC"].min, ranges["USDC"].max).toFixed(6); 1445 | addLog(`Mencoba swap: ${amount} USDC ke R2`, "swap", network); 1446 | try { 1447 | txPromise = addTransactionToQueue( 1448 | (nonce, wallet, provider, config) => swapTokens(ethers.parseUnits(amount, 6), config.USDC_ADDRESS, config.R2_ADDRESS, nonce, wallet, provider, config), 1449 | `Swap ${amount} USDC to R2`, 1450 | network 1451 | ); 1452 | } catch (error) { 1453 | if (error.message.includes("Saldo USDC tidak cukup")) { 1454 | addLog("Saldo USDC tidak cukup, mencoba swap R2 ke USDC", "warning", network); 1455 | amount = getRandomNumber(ranges["R2"].min, ranges["R2"].max).toFixed(18); 1456 | txPromise = addTransactionToQueue( 1457 | (nonce, wallet, provider, config) => swapTokens(ethers.parseUnits(amount, 18), config.R2_ADDRESS, config.USDC_ADDRESS, nonce, wallet, provider, config), 1458 | `Swap ${amount} R2 to USDC`, 1459 | network 1460 | ); 1461 | } else { 1462 | throw error; 1463 | } 1464 | } 1465 | } 1466 | 1467 | try { 1468 | const result = await txPromise; 1469 | if (result && result.receipt && result.receipt.status === 1) { 1470 | swapDirection[network] = !currentDirection; 1471 | addLog(`Arah swap diubah menjadi: ${swapDirection[network] ? "R2 -> USDC" : "USDC -> R2"}`, "debug", network); 1472 | } 1473 | return result; 1474 | } catch (error) { 1475 | addLog(`Swap gagal: ${error.message}`, "error", network); 1476 | return null; 1477 | } 1478 | } 1479 | 1480 | async function autoSwapR2R2usd(network) { 1481 | const ranges = randomAmountRanges["SWAP_R2_R2USD"]; 1482 | let amount; 1483 | let txPromise; 1484 | let currentDirection = swapDirection[network]; 1485 | 1486 | if (currentDirection) { 1487 | amount = getRandomNumber(ranges["R2"].min, ranges["R2"].max).toFixed(4); 1488 | addLog(`Mencoba swap: ${amount} R2 ke R2USD`, "swap", network); 1489 | try { 1490 | txPromise = addTransactionToQueue( 1491 | (nonce, wallet, provider, config) => swapTokens(ethers.parseUnits(amount, 18), config.R2_ADDRESS, config.R2USD_ADDRESS, nonce, wallet, provider, config), 1492 | `Swap ${amount} R2 to R2USD`, 1493 | network 1494 | ); 1495 | } catch (error) { 1496 | if (error.message.includes("Saldo R2 tidak cukup")) { 1497 | addLog("Saldo R2 tidak cukup, mencoba swap R2USD ke R2", "warning", network); 1498 | amount = getRandomNumber(ranges["R2USD"].min, ranges["R2USD"].max).toFixed(4); 1499 | txPromise = addTransactionToQueue( 1500 | (nonce, wallet, provider, config) => swapTokens(ethers.parseUnits(amount, 6), config.R2USD_ADDRESS, config.R2_ADDRESS, nonce, wallet, provider, config), 1501 | `Swap ${amount} R2USD to R2`, 1502 | network 1503 | ); 1504 | } else { 1505 | throw error; 1506 | } 1507 | } 1508 | } else { 1509 | amount = getRandomNumber(ranges["R2USD"].min, ranges["R2USD"].max).toFixed(6); 1510 | addLog(`Mencoba swap: ${amount} R2USD ke R2`, "swap", network); 1511 | try { 1512 | txPromise = addTransactionToQueue( 1513 | (nonce, wallet, provider, config) => swapTokens(ethers.parseUnits(amount, 6), config.R2USD_ADDRESS, config.R2_ADDRESS, nonce, wallet, provider, config), 1514 | `Swap ${amount} R2USD to R2`, 1515 | network 1516 | ); 1517 | } catch (error) { 1518 | if (error.message.includes("Saldo R2USD tidak cukup")) { 1519 | addLog("Saldo R2USD tidak cukup, mencoba swap R2 ke R2USD", "warning", network); 1520 | amount = getRandomNumber(ranges["R2"].min, ranges["R2"].max).toFixed(4); 1521 | txPromise = addTransactionToQueue( 1522 | (nonce, wallet, provider, config) => swapTokens(ethers.parseUnits(amount, 18), config.R2_ADDRESS, config.R2USD_ADDRESS, nonce, wallet, provider, config), 1523 | `Swap ${amount} R2 to R2USD`, 1524 | network 1525 | ); 1526 | } else { 1527 | throw error; 1528 | } 1529 | } 1530 | } 1531 | 1532 | try { 1533 | const result = await txPromise; 1534 | if (result && result.receipt && result.receipt.status === 1) { 1535 | swapDirection[network] = !currentDirection; 1536 | addLog(`Arah swap diubah menjadi: ${swapDirection[network] ? "R2 -> R2USD" : "R2USD -> R2"}`, "debug", network); 1537 | } 1538 | return result; 1539 | } catch (error) { 1540 | addLog(`Swap gagal: ${error.message}`, "error", network); 1541 | return null; 1542 | } 1543 | } 1544 | 1545 | async function autoAddLpR2Usdc(amountR2, nonce, wallet, provider, config) { 1546 | const network = config.NETWORK_NAME; 1547 | const routerAddress = config.ROUTER_ADDRESS; 1548 | if (!routerAddress) { 1549 | throw new Error("Router address tidak valid atau tidak didefinisikan di config"); 1550 | } 1551 | 1552 | const r2Contract = new ethers.Contract(config.R2_ADDRESS, ERC20ABI, provider); 1553 | const usdcContract = new ethers.Contract(config.USDC_ADDRESS, ERC20ABI, provider); 1554 | 1555 | const balanceR2 = await r2Contract.balanceOf(wallet.address); 1556 | const balanceUsdc = await usdcContract.balanceOf(wallet.address); 1557 | 1558 | const amountR2Wei = ethers.parseUnits(amountR2.toString(), 18); 1559 | if (BigInt(balanceR2.toString()) < BigInt(amountR2Wei.toString())) { 1560 | throw new Error(`Saldo R2 tidak cukup: ${ethers.formatUnits(balanceR2, 18)} R2`); 1561 | } 1562 | 1563 | const amountUsdc = (parseFloat(amountR2) * 150.041216 / 100).toFixed(6); 1564 | const amountUsdcWei = ethers.parseUnits(amountUsdc, 6); 1565 | if (BigInt(balanceUsdc.toString()) < BigInt(amountUsdcWei.toString())) { 1566 | throw new Error(`Saldo USDC tidak cukup: ${ethers.formatUnits(balanceUsdc, 6)} USDC`); 1567 | } 1568 | 1569 | await ensureApproval(config.R2_ADDRESS, routerAddress, amountR2Wei, wallet, network); 1570 | await ensureApproval(config.USDC_ADDRESS, routerAddress, amountUsdcWei, wallet, network); 1571 | 1572 | const routerContract = new ethers.Contract(routerAddress, [ 1573 | "function addLiquidity(address tokenA, address tokenB, uint256 amountADesired, uint256 amountBDesired, uint256 amountAMin, uint256 amountBMin, address to, uint256 deadline) returns (uint256 amountA, uint256 amountB, uint256 liquidity)" 1574 | ], wallet); 1575 | 1576 | const deadline = Math.floor(Date.now() / 1000) + 60 * 20; 1577 | const amountAMin = ethers.parseUnits((parseFloat(amountR2) * 0.99).toFixed(4), 6); 1578 | const amountBMin = ethers.parseUnits((parseFloat(amountUsdc) * 0.99).toFixed(4), 6); 1579 | 1580 | const tx = await routerContract.addLiquidity( 1581 | config.R2_ADDRESS, 1582 | config.USDC_ADDRESS, 1583 | amountR2Wei, 1584 | amountUsdcWei, 1585 | amountAMin, 1586 | amountBMin, 1587 | wallet.address, 1588 | deadline, 1589 | { nonce: nonce, gasLimit: 500000 } 1590 | ); 1591 | 1592 | return tx; 1593 | } 1594 | 1595 | async function autoAddLpR2R2usd(amountR2, nonce, wallet, provider, config) { 1596 | const network = config.NETWORK_NAME; 1597 | const routerAddress = config.ROUTER_ADDRESS; 1598 | if (!routerAddress) { 1599 | throw new Error("Router address tidak valid atau tidak didefinisikan di config"); 1600 | } 1601 | 1602 | const r2Contract = new ethers.Contract(config.R2_ADDRESS, ERC20ABI, provider); 1603 | const r2usdContract = new ethers.Contract(config.R2USD_ADDRESS, ERC20ABI, provider); 1604 | 1605 | const balanceR2 = await r2Contract.balanceOf(wallet.address); 1606 | const balanceR2usd = await r2usdContract.balanceOf(wallet.address); 1607 | 1608 | const amountR2Wei = ethers.parseUnits(amountR2.toString(), 18); 1609 | if (BigInt(balanceR2.toString()) < BigInt(amountR2Wei.toString())) { 1610 | throw new Error(`Saldo R2 tidak cukup: ${ethers.formatUnits(balanceR2, 18)} R2`); 1611 | } 1612 | 1613 | const amountR2usd = (parseFloat(amountR2) * 148.427749 / 100).toFixed(6); 1614 | const amountR2usdWei = ethers.parseUnits(amountR2usd, 6); 1615 | if (BigInt(balanceR2usd.toString()) < BigInt(amountR2usdWei.toString())) { 1616 | throw new Error(`Saldo R2USD tidak cukup: ${ethers.formatUnits(balanceR2usd, 6)} R2USD`); 1617 | } 1618 | 1619 | await ensureApproval(config.R2_ADDRESS, routerAddress, amountR2Wei, wallet, network); 1620 | await ensureApproval(config.R2USD_ADDRESS, routerAddress, amountR2usdWei, wallet, network); 1621 | 1622 | const routerContract = new ethers.Contract(routerAddress, [ 1623 | "function addLiquidity(address tokenA, address tokenB, uint256 amountADesired, uint256 amountBDesired, uint256 amountAMin, uint256 amountBMin, address to, uint256 deadline) returns (uint256 amountA, uint256 amountB, uint256 liquidity)" 1624 | ], wallet); 1625 | 1626 | const deadline = Math.floor(Date.now() / 1000) + 60 * 20; 1627 | const amountAMin = ethers.parseUnits((parseFloat(amountR2) * 0.99).toFixed(4), 6); 1628 | const amountBMin = ethers.parseUnits((parseFloat(amountR2usd) * 0.99).toFixed(4), 6); 1629 | 1630 | const tx = await routerContract.addLiquidity( 1631 | config.R2_ADDRESS, 1632 | config.R2USD_ADDRESS, 1633 | amountR2Wei, 1634 | amountR2usdWei, 1635 | amountAMin, 1636 | amountBMin, 1637 | wallet.address, 1638 | deadline, 1639 | { nonce: nonce, gasLimit: 500000 } 1640 | ); 1641 | 1642 | return tx; 1643 | } 1644 | 1645 | async function swapUsdcToR2usd(amountUsdc, nonce, wallet, provider, config) { 1646 | const network = config.NETWORK_NAME; 1647 | const amount = ethers.parseUnits(amountUsdc.toString(), 6); 1648 | const routerContractAddress = config.ROUTER_USDC_TO_R2USD; 1649 | 1650 | const isPaused = await checkContractPaused(routerContractAddress, provider, network); 1651 | if (isPaused) { 1652 | throw new Error("Kontrak dalam status paused, swap tidak dapat dilakukan"); 1653 | } 1654 | 1655 | const usdcContract = new ethers.Contract(config.USDC_ADDRESS, ERC20ABI, provider); 1656 | let balance = await usdcContract.balanceOf(wallet.address); 1657 | addLog(`Saldo USDC: ${ethers.formatUnits(balance, 6)}, Tipe: ${typeof balance}`, "debug", network); 1658 | 1659 | balance = BigInt(balance.toString()); 1660 | const amountBigInt = BigInt(amount.toString()); 1661 | 1662 | if (balance < amountBigInt) { 1663 | throw new Error(`Saldo USDC tidak cukup: ${ethers.formatUnits(balance, 6)} USDC`); 1664 | } 1665 | 1666 | await ensureApproval(config.USDC_ADDRESS, routerContractAddress, amount, wallet, network); 1667 | 1668 | const methodId = "0x095e7a95"; 1669 | const data = ethers.concat([ 1670 | methodId, 1671 | ethers.AbiCoder.defaultAbiCoder().encode( 1672 | ["address", "uint256", "uint256", "uint256", "uint256", "uint256", "uint256"], 1673 | [wallet.address, amount, 0, 0, 0, 0, 0] 1674 | ), 1675 | ]); 1676 | 1677 | addLog(`Data transaksi: ${data}`, "debug", network); 1678 | 1679 | try { 1680 | await provider.call({ 1681 | to: routerContractAddress, 1682 | data, 1683 | from: wallet.address, 1684 | }); 1685 | addLog("Simulasi transaksi berhasil, melanjutkan ke pengiriman", "debug", network); 1686 | } catch (error) { 1687 | throw new Error(`Simulasi transaksi gagal: ${error.reason || error.message}`); 1688 | } 1689 | 1690 | const tx = await wallet.sendTransaction({ 1691 | to: routerContractAddress, 1692 | data: data, 1693 | gasLimit: 500000, 1694 | nonce: nonce, 1695 | }); 1696 | 1697 | return tx; 1698 | } 1699 | 1700 | async function swapR2usdToUsdC(amountR2usd, nonce, wallet, provider, config) { 1701 | const network = config.NETWORK_NAME; 1702 | const amount = ethers.parseUnits(amountR2usd.toString(), 6); 1703 | const routerContractAddress = config.ROUTER_R2USD_TO_USDC; 1704 | 1705 | const r2usdContract = new ethers.Contract(config.R2USD_ADDRESS, ERC20ABI, provider); 1706 | let balance = await r2usdContract.balanceOf(wallet.address); 1707 | addLog(`Saldo R2USD: ${ethers.formatUnits(balance, 6)}, Tipe: ${typeof balance}`, "debug", network); 1708 | 1709 | balance = BigInt(balance.toString()); 1710 | const amountBigInt = BigInt(amount.toString()); 1711 | 1712 | if (balance < amountBigInt) { 1713 | throw new Error(`Saldo R2USD tidak cukup: ${ethers.formatUnits(balance, 6)} R2USD`); 1714 | } 1715 | 1716 | const methodId = "0x9dc29fac"; 1717 | const data = ethers.concat([ 1718 | methodId, 1719 | ethers.AbiCoder.defaultAbiCoder().encode( 1720 | ["address", "uint256"], 1721 | [wallet.address, amount] 1722 | ), 1723 | ]); 1724 | 1725 | addLog(`Data transaksi: ${data}`, "debug", network); 1726 | 1727 | try { 1728 | await provider.call({ 1729 | to: routerContractAddress, 1730 | data, 1731 | from: wallet.address, 1732 | }); 1733 | addLog("Simulasi transaksi burn berhasil, melanjutkan ke pengiriman", "debug", network); 1734 | } catch (error) { 1735 | throw new Error(`Simulasi transaksi burn gagal: ${error.reason || error.message}`); 1736 | } 1737 | 1738 | const tx = await wallet.sendTransaction({ 1739 | to: routerContractAddress, 1740 | data: data, 1741 | gasLimit: 500000, 1742 | nonce: nonce, 1743 | }); 1744 | 1745 | return tx; 1746 | } 1747 | 1748 | async function swapR2usdToUsdc(amountR2usd, nonce, wallet, provider, config) { 1749 | const network = config.NETWORK_NAME; 1750 | const amount = ethers.parseUnits(amountR2usd.toString(), 6); 1751 | const routerContractAddress = config.ROUTER_R2USD_TO_USDC; 1752 | 1753 | const r2usdContract = new ethers.Contract(config.R2USD_ADDRESS, ERC20ABI, provider); 1754 | let balance = await r2usdContract.balanceOf(wallet.address); 1755 | addLog(`Saldo R2USD: ${ethers.formatUnits(balance, 6)}, Tipe: ${typeof balance}`, "debug", network); 1756 | 1757 | balance = BigInt(balance.toString()); 1758 | const amountBigInt = BigInt(amount.toString()); 1759 | 1760 | if (balance < amountBigInt) { 1761 | throw new Error(`Saldo R2USD tidak cukup: ${ethers.formatUnits(balance, 6)} R2USD`); 1762 | } 1763 | 1764 | await ensureApproval(config.R2USD_ADDRESS, routerContractAddress, amount, wallet, network); 1765 | 1766 | const slippage = 0.97; 1767 | const minDyAmount = (parseFloat(amountR2usd) * slippage).toFixed(6); 1768 | const minDy = ethers.parseUnits(minDyAmount, 6); 1769 | 1770 | addLog(`minDy setelah slippage: ${minDyAmount} USDC`, "debug", network); 1771 | 1772 | const methodId = "0x3df02124"; 1773 | const data = ethers.concat([ 1774 | methodId, 1775 | ethers.AbiCoder.defaultAbiCoder().encode( 1776 | ["int128", "int128", "uint256", "uint256"], 1777 | [0, 1, amount, minDy] 1778 | ), 1779 | ]); 1780 | 1781 | addLog(`Data transaksi: ${data}`, "debug", network); 1782 | 1783 | try { 1784 | await provider.call({ 1785 | to: routerContractAddress, 1786 | data, 1787 | from: wallet.address, 1788 | }); 1789 | addLog("Simulasi transaksi berhasil, melanjutkan ke pengiriman", "debug", network); 1790 | } catch (error) { 1791 | throw new Error(`Simulasi transaksi gagal: ${error.reason || error.message}`); 1792 | } 1793 | 1794 | const tx = await wallet.sendTransaction({ 1795 | to: routerContractAddress, 1796 | data: data, 1797 | gasLimit: 500000, 1798 | nonce: nonce, 1799 | }); 1800 | 1801 | return tx; 1802 | } 1803 | 1804 | async function autoSwapR2usdUsdc(network) { 1805 | const ranges = randomAmountRanges["SWAP_R2USD_USDC"]; 1806 | let amount; 1807 | let txPromise; 1808 | let currentDirection = swapDirection[network]; 1809 | 1810 | if (currentDirection) { 1811 | amount = getRandomNumber(ranges["USDC"].min, ranges["USDC"].max).toFixed(6); 1812 | addLog(`Mencoba swap: ${amount} USDC ke R2USD`, "swap", network); 1813 | try { 1814 | txPromise = addTransactionToQueue( 1815 | (nonce, wallet, provider, config) => swapUsdcToR2usd(amount, nonce, wallet, provider, config), 1816 | `Swap ${amount} USDC to R2USD`, 1817 | network 1818 | ); 1819 | } catch (error) { 1820 | if (error.message.includes("Saldo USDC tidak cukup")) { 1821 | addLog("Saldo USDC tidak cukup, mencoba swap R2USD ke USDC", "warning", network); 1822 | amount = getRandomNumber(ranges["R2USD"].min, ranges["R2USD"].max).toFixed(6); 1823 | if (network === "Pharos" || network === "Monad") { 1824 | txPromise = addTransactionToQueue( 1825 | (nonce, wallet, provider, config) => swapR2usdToUsdC(amount, nonce, wallet, provider, config), 1826 | `Swap ${amount} R2USD to USDC`, 1827 | network 1828 | ); 1829 | } else { 1830 | txPromise = addTransactionToQueue( 1831 | (nonce, wallet, provider, config) => swapR2usdToUsdc(amount, nonce, wallet, provider, config), 1832 | `Swap ${amount} R2USD to USDC`, 1833 | network 1834 | ); 1835 | } 1836 | } else { 1837 | throw error; 1838 | } 1839 | } 1840 | } else { 1841 | amount = getRandomNumber(ranges["R2USD"].min, ranges["R2USD"].max).toFixed(6); 1842 | addLog(`Mencoba swap: ${amount} R2USD ke USDC`, "swap", network); 1843 | try { 1844 | if (network === "Pharos" || network === "Monad") { 1845 | txPromise = addTransactionToQueue( 1846 | (nonce, wallet, provider, config) => swapR2usdToUsdC(amount, nonce, wallet, provider, config), 1847 | `Swap ${amount} R2USD to USDC`, 1848 | network 1849 | ); 1850 | } else { 1851 | txPromise = addTransactionToQueue( 1852 | (nonce, wallet, provider, config) => swapR2usdToUsdc(amount, nonce, wallet, provider, config), 1853 | `Swap ${amount} R2USD to USDC`, 1854 | network 1855 | ); 1856 | } 1857 | } catch (error) { 1858 | if (error.message.includes("Saldo R2USD tidak cukup")) { 1859 | addLog("Saldo R2USD tidak cukup, mencoba swap USDC ke R2USD", "warning", network); 1860 | amount = getRandomNumber(ranges["USDC"].min, ranges["USDC"].max).toFixed(6); 1861 | txPromise = addTransactionToQueue( 1862 | (nonce, wallet, provider, config) => swapUsdcToR2usd(amount, nonce, wallet, provider, config), 1863 | `Swap ${amount} USDC to R2USD`, 1864 | network 1865 | ); 1866 | } else { 1867 | throw error; 1868 | } 1869 | } 1870 | } 1871 | 1872 | try { 1873 | const result = await txPromise; 1874 | if (result && result.receipt && result.receipt.status === 1) { 1875 | swapDirection[network] = !currentDirection; 1876 | addLog(`Arah swap diubah menjadi: ${swapDirection[network] ? "USDC -> R2USD" : "R2USD -> USDC"}`, "debug", network); 1877 | } 1878 | return result; 1879 | } catch (error) { 1880 | addLog(`Swap gagal: ${error.message}`, "error", network); 1881 | return null; 1882 | } 1883 | } 1884 | 1885 | async function autoStakeR2usdSr2usd(amountR2usd, nonce, wallet, provider, config) { 1886 | const network = config.NETWORK_NAME; 1887 | const amount = parseFloat(amountR2usd); 1888 | if (isNaN(amount) || amount <= 0) { 1889 | throw new Error("Jumlah R2USD harus lebih besar dari 0"); 1890 | } 1891 | 1892 | const amountWei = ethers.parseUnits(amount.toString(), 6); 1893 | const stakingContractAddress = config.STAKING_CONTRACT; 1894 | 1895 | const r2usdContract = new ethers.Contract(config.R2USD_ADDRESS, ERC20ABI, provider); 1896 | let balance = await r2usdContract.balanceOf(wallet.address); 1897 | addLog(`Saldo R2USD: ${ethers.formatUnits(balance, 6)}, Tipe: ${typeof balance}`, "debug", network); 1898 | 1899 | balance = BigInt(balance.toString()); 1900 | const amountBigInt = BigInt(amountWei.toString()); 1901 | 1902 | if (balance < amountBigInt) { 1903 | throw new Error(`Saldo R2USD tidak cukup: ${ethers.formatUnits(balance, 6)} R2USD`); 1904 | } 1905 | 1906 | await ensureApproval(config.R2USD_ADDRESS, stakingContractAddress, amountWei, wallet, network); 1907 | 1908 | const methodId = "0x1a5f0f00"; 1909 | const data = ethers.concat([ 1910 | methodId, 1911 | ethers.AbiCoder.defaultAbiCoder().encode( 1912 | ["uint256", "uint256", "uint256", "uint8", "uint256", "uint256"], 1913 | [amountWei, 0, 0, 0, 0, 0] 1914 | ), 1915 | ]); 1916 | 1917 | addLog(`Data transaksi: ${data}`, "debug", network); 1918 | 1919 | try { 1920 | await provider.call({ 1921 | to: stakingContractAddress, 1922 | data, 1923 | from: wallet.address, 1924 | }); 1925 | addLog("Simulasi transaksi staking berhasil, melanjutkan ke pengiriman", "debug", network); 1926 | } catch (error) { 1927 | throw new Error(`Simulasi transaksi staking gagal: ${error.reason || error.message}`); 1928 | } 1929 | 1930 | const tx = await wallet.sendTransaction({ 1931 | to: stakingContractAddress, 1932 | data: data, 1933 | gasLimit: 500000, 1934 | nonce: nonce, 1935 | }); 1936 | 1937 | return tx; 1938 | } 1939 | 1940 | async function autoAddLpR2usdSr2usd(amountR2usd, nonce, wallet, provider, config) { 1941 | if (!config) { 1942 | throw new Error("Config tidak didefinisikan"); 1943 | } 1944 | const network = config.NETWORK_NAME; 1945 | addLog(`Menambahkan LP untuk ${amountR2usd} R2USD`, "debug", network); 1946 | const amount = parseFloat(amountR2usd); 1947 | if (isNaN(amount) || amount <= 0) { 1948 | throw new Error("Jumlah R2USD harus lebih besar dari 0"); 1949 | } 1950 | 1951 | const amountWei = ethers.parseUnits(amount.toString(), 6); 1952 | const amountSr2usdWei = amountWei; 1953 | const lpContractAddress = config.LP_R2USD_sR2USD; 1954 | 1955 | const r2usdContract = new ethers.Contract(config.R2USD_ADDRESS, ERC20ABI, provider); 1956 | const sr2usdContract = new ethers.Contract(config.sR2USD_ADDRESS, ERC20ABI, provider); 1957 | 1958 | let balanceR2usd = await r2usdContract.balanceOf(wallet.address); 1959 | let balanceSr2usd = await sr2usdContract.balanceOf(wallet.address); 1960 | 1961 | addLog(`Saldo R2USD: ${ethers.formatUnits(balanceR2usd, 6)}`, "debug", network); 1962 | addLog(`Saldo sR2USD: ${ethers.formatUnits(balanceSr2usd, 6)}`, "debug", network); 1963 | 1964 | balanceR2usd = BigInt(balanceR2usd.toString()); 1965 | balanceSr2usd = BigInt(balanceSr2usd.toString()); 1966 | const amountBigInt = BigInt(amountWei.toString()); 1967 | 1968 | if (balanceR2usd < amountBigInt) { 1969 | throw new Error(`Saldo R2USD tidak cukup: ${ethers.formatUnits(balanceR2usd, 6)} R2USD`); 1970 | } 1971 | if (balanceSr2usd < amountBigInt) { 1972 | throw new Error(`Saldo sR2USD tidak cukup: ${ethers.formatUnits(balanceSr2usd, 6)} sR2USD`); 1973 | } 1974 | 1975 | await ensureApproval(config.R2USD_ADDRESS, lpContractAddress, amountWei, wallet, network); 1976 | await ensureApproval(config.sR2USD_ADDRESS, lpContractAddress, amountSr2usdWei, wallet, network); 1977 | 1978 | let txData; 1979 | const lpContractWithSigner = new ethers.Contract(lpContractAddress, network === "Monad Network" ? UNISWAP_V2_LP_ABI : LP_CONTRACT_ABI, wallet); 1980 | 1981 | if (network === "Monad Network") { 1982 | const deadline = Math.floor(Date.now() / 1000) + 60 * 20; 1983 | const slippageTolerance = 0.99; 1984 | const amountR2usdMin = BigInt(Math.floor(amount * slippageTolerance * 10**6)); 1985 | const amountSr2usdMin = BigInt(Math.floor(amount * slippageTolerance * 10**6)); 1986 | 1987 | txData = await lpContractWithSigner.addLiquidity.populateTransaction( 1988 | config.R2USD_ADDRESS, 1989 | config.sR2USD_ADDRESS, 1990 | amountWei, 1991 | amountSr2usdWei, 1992 | amountR2usdMin, 1993 | amountSr2usdMin, 1994 | wallet.address, 1995 | deadline 1996 | ); 1997 | addLog(`Data transaksi: ${txData.data}`, "debug", network); 1998 | } else { 1999 | const lpContract = new ethers.Contract(lpContractAddress, LP_CONTRACT_ABI, provider); 2000 | let estimatedLpTokens; 2001 | try { 2002 | estimatedLpTokens = await lpContract.calc_token_amount([amountSr2usdWei, amountWei], true); 2003 | addLog(`Estimasi LP token: ${ethers.formatUnits(estimatedLpTokens, 18)}`, "debug", network); 2004 | } catch (error) { 2005 | addLog(`Gagal mengestimasi LP token: ${error.message}, menggunakan _min_mint_amount=0`, "warning", network); 2006 | estimatedLpTokens = 0; 2007 | } 2008 | 2009 | const slippageTolerance = 0.99; 2010 | const minMintAmount = estimatedLpTokens === 0 ? 0 : BigInt(Math.floor(Number(estimatedLpTokens) * slippageTolerance)); 2011 | 2012 | txData = await lpContractWithSigner.add_liquidity.populateTransaction( 2013 | [amountSr2usdWei, amountWei], 2014 | minMintAmount, 2015 | wallet.address 2016 | ); 2017 | addLog(`Data transaksi: ${txData.data}`, "debug", network); 2018 | } 2019 | 2020 | try { 2021 | await provider.call({ 2022 | to: lpContractAddress, 2023 | data: txData.data, 2024 | from: wallet.address, 2025 | }); 2026 | addLog("Simulasi transaksi add liquidity berhasil", "debug", network); 2027 | } catch (error) { 2028 | throw new Error(`Simulasi transaksi add liquidity gagal: ${error.reason || error.message}`); 2029 | } 2030 | 2031 | const tx = await wallet.sendTransaction({ 2032 | to: lpContractAddress, 2033 | data: txData.data, 2034 | gasLimit: 500000, 2035 | nonce: nonce, 2036 | }); 2037 | 2038 | return tx; 2039 | } 2040 | 2041 | 2042 | 2043 | async function autoAddLpUsdcR2usd(amountUsdc, nonce, wallet, provider, config) { 2044 | if (!config) { 2045 | throw new Error("Config tidak didefinisikan"); 2046 | } 2047 | const network = config.NETWORK_NAME; 2048 | addLog(`Menambahkan LP untuk ${amountUsdc} USDC`, "debug", network); 2049 | const amount = parseFloat(amountUsdc); 2050 | if (isNaN(amount) || amount <= 0) { 2051 | throw new Error("Jumlah USDC harus lebih besar dari 0"); 2052 | } 2053 | 2054 | const amountWei = ethers.parseUnits(amount.toString(), 6); 2055 | const amountR2usdWei = amountWei; 2056 | const lpContractAddress = config.LP_USDC_R2USD; 2057 | 2058 | const usdcContract = new ethers.Contract(config.USDC_ADDRESS, ERC20ABI, provider); 2059 | const r2usdContract = new ethers.Contract(config.R2USD_ADDRESS, ERC20ABI, provider); 2060 | 2061 | let balanceUsdc = await usdcContract.balanceOf(wallet.address); 2062 | let balanceR2usd = await r2usdContract.balanceOf(wallet.address); 2063 | 2064 | addLog(`Saldo USDC: ${ethers.formatUnits(balanceUsdc, 6)}`, "debug", network); 2065 | addLog(`Saldo R2USD: ${ethers.formatUnits(balanceR2usd, 6)}`, "debug", network); 2066 | 2067 | balanceUsdc = BigInt(balanceUsdc.toString()); 2068 | balanceR2usd = BigInt(balanceR2usd.toString()); 2069 | const amountBigInt = BigInt(amountWei.toString()); 2070 | 2071 | if (balanceUsdc < amountBigInt) { 2072 | throw new Error(`Saldo USDC tidak cukup: ${ethers.formatUnits(balanceUsdc, 6)} USDC`); 2073 | } 2074 | if (balanceR2usd < amountBigInt) { 2075 | throw new Error(`Saldo R2USD tidak cukup: ${ethers.formatUnits(balanceR2usd, 6)} R2USD`); 2076 | } 2077 | 2078 | await ensureApproval(config.USDC_ADDRESS, lpContractAddress, amountWei, wallet, network); 2079 | await ensureApproval(config.R2USD_ADDRESS, lpContractAddress, amountR2usdWei, wallet, network); 2080 | 2081 | let txData; 2082 | const lpContractWithSigner = new ethers.Contract(lpContractAddress, network === "Monad Network" ? UNISWAP_V2_LP_ABI : LP_USDC_R2USD_ABI, wallet); 2083 | 2084 | if (network === "Monad Network") { 2085 | const deadline = Math.floor(Date.now() / 1000) + 60 * 20; 2086 | const slippageTolerance = 0.99; 2087 | const amountUsdcMin = BigInt(Math.floor(amount * slippageTolerance * 10**6)); 2088 | const amountR2usdMin = BigInt(Math.floor(amount * slippageTolerance * 10**6)); 2089 | 2090 | txData = await lpContractWithSigner.addLiquidity.populateTransaction( 2091 | config.USDC_ADDRESS, 2092 | config.R2USD_ADDRESS, 2093 | amountWei, 2094 | amountR2usdWei, 2095 | amountUsdcMin, 2096 | amountR2usdMin, 2097 | wallet.address, 2098 | deadline 2099 | ); 2100 | addLog(`Data transaksi: ${txData.data}`, "debug", network); 2101 | } else { 2102 | const lpContract = new ethers.Contract(lpContractAddress, LP_USDC_R2USD_ABI, provider); 2103 | let estimatedLpTokens; 2104 | try { 2105 | estimatedLpTokens = await lpContract.calc_token_amount([amountR2usdWei, amountWei], true); 2106 | addLog(`Estimasi LP token: ${ethers.formatUnits(estimatedLpTokens, 18)}`, "debug", network); 2107 | } catch (error) { 2108 | addLog(`Gagal mengestimasi LP token: ${error.message}, menggunakan _min_mint_amount=0`, "warning", network); 2109 | estimatedLpTokens = 0; 2110 | } 2111 | 2112 | const slippageTolerance = 0.99; 2113 | const minMintAmount = estimatedLpTokens === 0 ? 0 : BigInt(Math.floor(Number(estimatedLpTokens) * slippageTolerance)); 2114 | 2115 | txData = await lpContractWithSigner.add_liquidity.populateTransaction( 2116 | [amountR2usdWei, amountWei], 2117 | minMintAmount, 2118 | wallet.address 2119 | ); 2120 | addLog(`Data transaksi: ${txData.data}`, "debug", network); 2121 | } 2122 | 2123 | try { 2124 | await provider.call({ 2125 | to: lpContractAddress, 2126 | data: txData.data, 2127 | from: wallet.address, 2128 | }); 2129 | addLog("Simulasi transaksi add liquidity berhasil", "debug", network); 2130 | } catch (error) { 2131 | throw new Error(`Simulasi transaksi add liquidity gagal: ${error.reason || error.message}`); 2132 | } 2133 | 2134 | const tx = await wallet.sendTransaction({ 2135 | to: lpContractAddress, 2136 | data: txData.data, 2137 | gasLimit: 500000, 2138 | nonce: nonce, 2139 | }); 2140 | 2141 | return tx; 2142 | } 2143 | 2144 | async function runAutoAction(actionFunction, actionName, network) { 2145 | addLog(`Debug: runAutoAction menerima network = ${network}`, "debug", network || currentNetwork); 2146 | let normalizedNetwork = network; 2147 | if (!network) { 2148 | addLog("Peringatan: Parameter network tidak diberikan, menggunakan currentNetwork", "warning", currentNetwork); 2149 | normalizedNetwork = currentNetwork; 2150 | } 2151 | if (normalizedNetwork === "Sepolia Testnet") normalizedNetwork = "Sepolia"; 2152 | else if (normalizedNetwork === "Sepolia R2 Testnet") normalizedNetwork = "Sepolia R2"; 2153 | else if (normalizedNetwork === "Pharos Network") normalizedNetwork = "Pharos"; 2154 | else if (normalizedNetwork === "Monad Network") normalizedNetwork = "Monad"; 2155 | 2156 | const swapRunning = normalizedNetwork === "Sepolia" ? swapRunningSepolia : 2157 | normalizedNetwork === "Sepolia R2" ? swapRunningSepoliaR2 : 2158 | normalizedNetwork === "Pharos" ? swapRunningPharos : 2159 | normalizedNetwork === "Monad" ? swapRunningMonad : false; 2160 | 2161 | if (swapRunning) { 2162 | addLog("Transaksi sedang berjalan di jaringan ini. Hentikan transaksi terlebih dahulu.", "warning", normalizedNetwork); 2163 | return; 2164 | } 2165 | 2166 | promptBox.setFront(); 2167 | if (actionName.includes("Stake") || actionName.includes("Add LP")) { 2168 | const tokenPrompt = actionName.includes("R2 & USDC") ? "R2" : 2169 | actionName.includes("USDC & R2USD") ? "USDC" : "R2USD"; 2170 | promptBox.input(`Masukkan jumlah ${tokenPrompt} untuk ${actionName}`, "", async (err, value) => { 2171 | promptBox.hide(); 2172 | safeRender(); 2173 | if (err || !value) { 2174 | addLog(`${actionName}: Input tidak valid atau dibatalkan.`, "swap", normalizedNetwork); 2175 | return; 2176 | } 2177 | const amount = parseFloat(value); 2178 | if (isNaN(amount) || amount <= 0) { 2179 | addLog(`${actionName}: Jumlah harus berupa angka lebih besar dari 0.`, "swap", normalizedNetwork); 2180 | return; 2181 | } 2182 | addLog(`${actionName}: Mulai ${actionName.includes("Stake") ? "staking" : "menambahkan LP"} ${amount} ${tokenPrompt}.`, "swap", normalizedNetwork); 2183 | 2184 | if (normalizedNetwork === "Sepolia") { 2185 | swapRunningSepolia = true; 2186 | addLog("swapRunningSepolia diatur ke true", "debug", normalizedNetwork); 2187 | } else if (normalizedNetwork === "Sepolia R2") { 2188 | swapRunningSepoliaR2 = true; 2189 | addLog("swapRunningSepoliaR2 diatur ke true", "debug", normalizedNetwork); 2190 | } else if (normalizedNetwork === "Pharos") { 2191 | swapRunningPharos = true; 2192 | addLog("swapRunningPharos diatur ke true", "debug", normalizedNetwork); 2193 | } else if (normalizedNetwork === "Monad") { 2194 | swapRunningMonad = true; 2195 | addLog("swapRunningMonad diatur ke true", "debug", normalizedNetwork); 2196 | } 2197 | 2198 | mainMenu.setItems(getMainMenItems()); 2199 | const activeSubMenu = normalizedNetwork === "Sepolia" ? sepoliaSubMenu : 2200 | normalizedNetwork === "Sepolia R2" ? r2SepoliaSubMenu : 2201 | normalizedNetwork === "Pharos" ? pharosSubMenu : 2202 | monadSubMenu; 2203 | activeSubMenu.setItems(normalizedNetwork === "Sepolia" ? getSepoliaSubMenuItems() : 2204 | normalizedNetwork === "Sepolia R2" ? getSepoliaR2SubMenuItems() : 2205 | normalizedNetwork === "Pharos" ? getPharosSubMenuItems() : 2206 | getMonadSubMenuItems()); 2207 | activeSubMenu.show(); 2208 | activeSubMenu.focus(); 2209 | safeRender(); 2210 | 2211 | try { 2212 | const selectedFunction = actionName.includes("Stake") ? 2213 | (nonce, wallet, provider, config) => autoStakeR2usdSr2usd(amount, nonce, wallet, provider, config) : 2214 | actionName.includes("USDC & R2USD") ? 2215 | (nonce, wallet, provider, config) => autoAddLpUsdcR2usd(amount, nonce, wallet, provider, config) : 2216 | actionName.includes("R2 & USDC") ? 2217 | (nonce, wallet, provider, config) => autoAddLpR2Usdc(amount, nonce, wallet, provider, config) : 2218 | actionName.includes("R2 & R2USD") ? 2219 | (nonce, wallet, provider, config) => autoAddLpR2R2usd(amount, nonce, wallet, provider, config) : 2220 | (nonce, wallet, provider, config) => autoAddLpR2usdSr2usd(amount, nonce, wallet, provider, config); 2221 | 2222 | await addTransactionToQueue( 2223 | selectedFunction, 2224 | `${actionName} ${amount} ${tokenPrompt}`, 2225 | normalizedNetwork 2226 | ); 2227 | await updateWalletData(normalizedNetwork); 2228 | addLog(`${actionName}: ${actionName.includes("Stake") ? "Staking" : "Penambahan LP"} ${amount} ${tokenPrompt} selesai.`, "success", normalizedNetwork); 2229 | } catch (error) { 2230 | addLog(`${actionName}: Gagal - ${error.message}`, "error", normalizedNetwork); 2231 | } finally { 2232 | if (normalizedNetwork === "Sepolia") { 2233 | swapRunningSepolia = false; 2234 | addLog("swapRunningSepolia diatur ke false", "debug", normalizedNetwork); 2235 | } else if (normalizedNetwork === "Sepolia R2") { 2236 | swapRunningSepoliaR2 = false; 2237 | addLog("swapRunningSepoliaR2 diatur ke false", "debug", normalizedNetwork); 2238 | } else if (normalizedNetwork === "Pharos") { 2239 | swapRunningPharos = false; 2240 | addLog("swapRunningPharos diatur ke false", "debug", normalizedNetwork); 2241 | } else if (normalizedNetwork === "Monad") { 2242 | swapRunningMonad = false; 2243 | addLog("swapRunningMonad diatur ke false", "debug", normalizedNetwork); 2244 | } 2245 | 2246 | mainMenu.setItems(getMainMenItems()); 2247 | activeSubMenu.setItems(normalizedNetwork === "Sepolia" ? getSepoliaSubMenuItems() : 2248 | normalizedNetwork === "Sepolia R2" ? getSepoliaR2SubMenuItems() : 2249 | normalizedNetwork === "Pharos" ? getPharosSubMenuItems() : 2250 | getMonadSubMenuItems()); 2251 | safeRender(); 2252 | } 2253 | addLog(`${actionName}: Selesai.`, "swap", normalizedNetwork); 2254 | }); 2255 | } else { 2256 | promptBox.readInput(`Masukkan jumlah Swap untuk ${actionName}`, "", async (err, value) => { 2257 | promptBox.hide(); 2258 | safeRender(); 2259 | if (err || !value) { 2260 | addLog(`${actionName}: Input tidak valid atau dibatalkan.`, "swap", normalizedNetwork); 2261 | return; 2262 | } 2263 | const loopCount = parseInt(value); 2264 | if (isNaN(loopCount)) { 2265 | addLog(`${actionName}: Input harus berupa angka.`, "swap", normalizedNetwork); 2266 | return; 2267 | } 2268 | addLog(`${actionName}: Mulai ${loopCount} Swap.`, "swap", normalizedNetwork); 2269 | 2270 | if (normalizedNetwork === "Sepolia") { 2271 | swapRunningSepolia = true; 2272 | swapCancelledSepolia = false; 2273 | addLog("swapRunningSepolia diatur ke true", "debug", normalizedNetwork); 2274 | } else if (normalizedNetwork === "Sepolia R2") { 2275 | swapRunningSepoliaR2 = true; 2276 | swapCancelledSepoliaR2 = false; 2277 | addLog("swapRunningSepoliaR2 diatur ke true", "debug", normalizedNetwork); 2278 | } else if (normalizedNetwork === "Pharos") { 2279 | swapRunningPharos = true; 2280 | swapCancelledPharos = false; 2281 | addLog("swapRunningPharos diatur ke true", "debug", normalizedNetwork); 2282 | } else if (normalizedNetwork === "Monad") { 2283 | swapRunningMonad = true; 2284 | swapCancelledMonad = false; 2285 | addLog("swapRunningMonad diatur ke true", "debug", normalizedNetwork); 2286 | } 2287 | 2288 | mainMenu.setItems(getMainMenItems()); 2289 | const activeSubMenu = normalizedNetwork === "Sepolia" ? sepoliaSubMenu : 2290 | normalizedNetwork === "Sepolia R2" ? r2SepoliaSubMenu : 2291 | normalizedNetwork === "Pharos" ? pharosSubMenu : 2292 | monadSubMenu; 2293 | activeSubMenu.setItems(normalizedNetwork === "Sepolia" ? getSepoliaSubMenuItems() : 2294 | normalizedNetwork === "Sepolia R2" ? getSepoliaR2SubMenuItems() : 2295 | normalizedNetwork === "Pharos" ? getPharosSubMenuItems() : 2296 | getMonadSubMenuItems()); 2297 | activeSubMenu.show(); 2298 | activeSubMenu.focus(); 2299 | safeRender(); 2300 | 2301 | try { 2302 | for (let i = 1; i <= loopCount; i++) { 2303 | if ((normalizedNetwork === "Sepolia" && swapCancelledSepolia) || 2304 | (normalizedNetwork === "Sepolia R2" && swapCancelledSepoliaR2) || 2305 | (normalizedNetwork === "Pharos" && swapCancelledPharos) || 2306 | (normalizedNetwork === "Monad" && swapCancelledMonad)) { 2307 | addLog(`${actionName}: Dihentikan pada Swap ${i}.`, "swap", normalizedNetwork); 2308 | break; 2309 | } 2310 | addLog(`Memulai Swap ke-${i}`, "swap", normalizedNetwork); 2311 | const success = await actionFunction(normalizedNetwork); 2312 | if (success) { 2313 | await updateWalletData(normalizedNetwork); 2314 | } 2315 | if (i < loopCount) { 2316 | const delayTime = getRandomDelay(); 2317 | const minutes = Math.floor(delayTime / 60000); 2318 | const seconds = Math.floor((delayTime % 60000) / 1000); 2319 | addLog(`Swap ke-${i} selesai. Menunggu ${minutes} menit ${seconds} detik.`, "swap", normalizedNetwork); 2320 | await waitWithCancel(delayTime, "swap", normalizedNetwork); 2321 | if ((normalizedNetwork === "Sepolia" && swapCancelledSepolia) || 2322 | (normalizedNetwork === "Sepolia R2" && swapCancelledSepoliaR2) || 2323 | (normalizedNetwork === "Pharos" && swapCancelledPharos) || 2324 | (normalizedNetwork === "Monad" && swapCancelledMonad)) { 2325 | addLog(`${actionName}: Dihentikan saat periode tunggu.`, "swap", normalizedNetwork); 2326 | break; 2327 | } 2328 | } 2329 | } 2330 | } finally { 2331 | if (normalizedNetwork === "Sepolia") { 2332 | swapRunningSepolia = false; 2333 | addLog("swapRunningSepolia diatur ke false", "debug", normalizedNetwork); 2334 | } else if (normalizedNetwork === "Sepolia R2") { 2335 | swapRunningSepoliaR2 = false; 2336 | addLog("swapRunningSepoliaR2 diatur ke false", "debug", normalizedNetwork); 2337 | } else if (normalizedNetwork === "Pharos") { 2338 | swapRunningPharos = false; 2339 | addLog("swapRunningPharos diatur ke false", "debug", normalizedNetwork); 2340 | } else if (normalizedNetwork === "Monad") { 2341 | swapRunningMonad = false; 2342 | addLog("swapRunningMonad diatur ke false", "debug", normalizedNetwork); 2343 | } 2344 | 2345 | mainMenu.setItems(getMainMenItems()); 2346 | activeSubMenu.setItems(normalizedNetwork === "Sepolia" ? getSepoliaSubMenuItems() : 2347 | normalizedNetwork === "Sepolia R2" ? getSepoliaR2SubMenuItems() : 2348 | normalizedNetwork === "Pharos" ? getPharosSubMenuItems() : 2349 | getMonadSubMenuItems()); 2350 | safeRender(); 2351 | } 2352 | addLog(`${actionName}: Selesai.`, "swap", normalizedNetwork); 2353 | }); 2354 | } 2355 | } 2356 | 2357 | function changeRandomAmount(action) { 2358 | const tokens = Object.keys(randomAmountRanges[action]); 2359 | let index = 0; 2360 | function promptForToken() { 2361 | if (index >= tokens.length) { 2362 | addLog(`Change Random Amount: Random amounts untuk ${action} diperbarui.`, "success", currentNetwork); 2363 | const activeSubMenu = currentNetwork === "Sepolia" ? sepoliaChangeRandomAmountSubMenu : 2364 | currentNetwork === "Sepolia R2" ? r2SepoliaChangeRandomAmountSubMenu : 2365 | currentNetwork === "Pharos" ? pharosChangeRandomAmountSubMenu : 2366 | monadChangeRandomAmountSubMenu; 2367 | activeSubMenu.show(); 2368 | activeSubMenu.focus(); 2369 | safeRender(); 2370 | return; 2371 | } 2372 | const token = tokens[index]; 2373 | promptBox.setFront(); 2374 | promptBox.input(`Masukkan rentang random amount untuk ${token} pada ${action} (format: min,max, contoh: 50,200)`, "", (err, value) => { 2375 | promptBox.hide(); 2376 | safeRender(); 2377 | if (err || !value) { 2378 | addLog(`Change Random Amount: Input untuk ${token} pada ${action} dibatalkan.`, "system", currentNetwork); 2379 | const activeSubMenu = currentNetwork === "Sepolia" ? sepoliaChangeRandomAmountSubMenu : 2380 | currentNetwork === "Sepolia R2" ? r2SepoliaChangeRandomAmountSubMenu : 2381 | currentNetwork === "Pharos" ? pharosChangeRandomAmountSubMenu : 2382 | monadChangeRandomAmountSubMenu; 2383 | activeSubMenu.show(); 2384 | activeSubMenu.focus(); 2385 | safeRender(); 2386 | return; 2387 | } 2388 | const [min, max] = value.split(",").map(v => parseFloat(v.trim())); 2389 | if (isNaN(min) || isNaN(max) || min <= 0 || max <= min) { 2390 | addLog(`Change Random Amount: Input tidak valid untuk ${token} pada ${action}. Gunakan format min,max (contoh: 50,200) dengan min > 0 dan max > min.`, "error", currentNetwork); 2391 | const activeSubMenu = currentNetwork === "Sepolia" ? sepoliaChangeRandomAmountSubMenu : 2392 | currentNetwork === "Sepolia R2" ? r2SepoliaChangeRandomAmountSubMenu : 2393 | currentNetwork === "Pharos" ? pharosChangeRandomAmountSubMenu : 2394 | monadChangeRandomAmountSubMenu; 2395 | activeSubMenu.show(); 2396 | activeSubMenu.focus(); 2397 | safeRender(); 2398 | return; 2399 | } 2400 | randomAmountRanges[action][token].min = min; 2401 | randomAmountRanges[action][token].max = max; 2402 | index++; 2403 | promptForToken(); 2404 | }); 2405 | } 2406 | promptForToken(); 2407 | } 2408 | 2409 | mainMenu.on("select", (item) => { 2410 | const selected = item.getText(); 2411 | if (selected === "Sepolia Network") { 2412 | currentNetwork = "Sepolia"; 2413 | welcomeBox.hide(); 2414 | walletBox.show(); 2415 | updateWalletData("Sepolia"); 2416 | sepoliaSubMenu.setItems(getSepoliaSubMenuItems()); 2417 | sepoliaSubMenu.show(); 2418 | r2SepoliaSubMenu.hide(); 2419 | pharosSubMenu.hide(); 2420 | monadSubMenu.hide(); 2421 | sepoliaSubMenu.focus(); 2422 | safeRender(); 2423 | } else if (selected === "Sepolia R2 Network") { 2424 | currentNetwork = "Sepolia R2"; 2425 | welcomeBox.hide(); 2426 | walletBox.show(); 2427 | updateWalletData("Sepolia R2"); 2428 | r2SepoliaSubMenu.setItems(getSepoliaR2SubMenuItems()); 2429 | r2SepoliaSubMenu.show(); 2430 | sepoliaSubMenu.hide(); 2431 | pharosSubMenu.hide(); 2432 | monadSubMenu.hide(); 2433 | r2SepoliaSubMenu.focus(); 2434 | safeRender(); 2435 | } else if (selected === "Pharos Network") { 2436 | currentNetwork = "Pharos"; 2437 | welcomeBox.hide(); 2438 | walletBox.show(); 2439 | updateWalletData("Pharos"); 2440 | pharosSubMenu.setItems(getPharosSubMenuItems()); 2441 | pharosSubMenu.show(); 2442 | sepoliaSubMenu.hide(); 2443 | r2SepoliaSubMenu.hide(); 2444 | monadSubMenu.hide(); 2445 | pharosSubMenu.focus(); 2446 | safeRender(); 2447 | } else if (selected === "Monad Network") { 2448 | currentNetwork = "Monad"; 2449 | welcomeBox.hide(); 2450 | walletBox.show(); 2451 | updateWalletData("Monad"); 2452 | monadSubMenu.setItems(getMonadSubMenuItems()); 2453 | monadSubMenu.show(); 2454 | sepoliaSubMenu.hide(); 2455 | r2SepoliaSubMenu.hide(); 2456 | pharosSubMenu.hide(); 2457 | monadSubMenu.focus(); 2458 | safeRender(); 2459 | } else if (selected === "Claim Faucet") { 2460 | mainMenu.hide(); 2461 | claimFaucetSubMenu.setItems(getClaimFaucetSubMenuItems()); 2462 | claimFaucetSubMenu.show(); 2463 | claimFaucetSubMenu.focus(); 2464 | safeRender(); 2465 | } else if (selected === "Antrian Transaksi") { 2466 | showTransactionQueueMenu(); 2467 | } else if (selected === "Stop All Transaction") { 2468 | if (swapRunningSepolia || swapRunningSepoliaR2 || swapRunningPharos || swapRunningMonad) { 2469 | swapCancelledSepolia = true; 2470 | swapCancelledSepoliaR2 = true; 2471 | swapCancelledPharos = true; 2472 | swapCancelledMonad = true; 2473 | addLog("Stop All Transaction: Semua transaksi akan dihentikan.", "system", currentNetwork); 2474 | } 2475 | } else if (selected === "Clear Transaction Logs") { 2476 | clearTransactionLogs(); 2477 | } else if (selected === "Refresh") { 2478 | updateWelcomeBox(); 2479 | updateWalletData(currentNetwork); 2480 | safeRender(); 2481 | addLog("Refreshed", "system", currentNetwork); 2482 | } else if (selected === "Exit") { 2483 | process.exit(0); 2484 | } 2485 | }); 2486 | 2487 | sepoliaSubMenu.on("select", (item) => { 2488 | const selected = item.getText(); 2489 | if (selected === "Auto Swap R2USD & USDC") { 2490 | if (swapRunningSepolia) { 2491 | addLog("Transaksi sedang berjalan di Sepolia. Hentikan transaksi terlebih dahulu.", "warning", currentNetwork); 2492 | } else { 2493 | runAutoAction(autoSwapR2usdUsdc, "Auto Swap R2USD & USDC", "Sepolia"); 2494 | } 2495 | } else if (selected === "Auto Stake R2USD & sR2USD") { 2496 | if (swapRunningSepolia) { 2497 | addLog("Transaksi sedang berjalan di Sepolia. Hentikan transaksi terlebih dahulu.", "warning", currentNetwork); 2498 | } else { 2499 | runAutoAction(autoStakeR2usdSr2usd, "Auto Stake R2USD & sR2USD", "Sepolia"); 2500 | } 2501 | } else if (selected === "Auto Add LP R2USD & sR2USD") { 2502 | if (swapRunningSepolia) { 2503 | addLog("Transaksi sedang berjalan di Sepolia. Hentikan transaksi terlebih dahulu.", "warning", currentNetwork); 2504 | } else { 2505 | runAutoAction(autoAddLpR2usdSr2usd, "Auto Add LP R2USD & sR2USD", "Sepolia"); 2506 | } 2507 | } else if (selected === "Auto Add LP USDC & R2USD") { 2508 | if (swapRunningSepolia) { 2509 | addLog("Transaksi sedang berjalan di Sepolia. Hentikan transaksi terlebih dahulu.", "warning", currentNetwork); 2510 | } else { 2511 | runAutoAction(autoAddLpUsdcR2usd, "Auto Add LP USDC & R2USD", "Sepolia"); 2512 | } 2513 | } else if (selected === "Manual Swap") { 2514 | sepoliaSubMenu.hide(); 2515 | sepoliaManualSwapSubMenu.show(); 2516 | sepoliaManualSwapSubMenu.focus(); 2517 | safeRender(); 2518 | } else if (selected === "Change Random Amount") { 2519 | sepoliaSubMenu.hide(); 2520 | sepoliaChangeRandomAmountSubMenu.show(); 2521 | sepoliaChangeRandomAmountSubMenu.focus(); 2522 | safeRender(); 2523 | } else if (selected === "Stop Transaction") { 2524 | if (swapRunningSepolia) { 2525 | swapCancelledSepolia = true; 2526 | addLog("Perintah Stop Transaction diterima untuk Sepolia.", "swap", currentNetwork); 2527 | } 2528 | } else if (selected === "Clear Transaction Logs") { 2529 | clearTransactionLogs(); 2530 | } else if (selected === "Back To Main Menu") { 2531 | sepoliaSubMenu.hide(); 2532 | walletBox.hide(); 2533 | welcomeBox.show(); 2534 | mainMenu.show(); 2535 | mainMenu.focus(); 2536 | updateWelcomeBox(); 2537 | safeRender(); 2538 | } else if (selected === "Refresh") { 2539 | updateWalletData("Sepolia"); 2540 | safeRender(); 2541 | addLog("Refreshed", "system", currentNetwork); 2542 | } 2543 | }); 2544 | 2545 | r2SepoliaSubMenu.on("select", (item) => { 2546 | const selected = item.getText(); 2547 | if (selected === "Auto Swap R2 & USDC") { 2548 | if (swapRunningSepoliaR2) { 2549 | addLog("Transaksi sedang berjalan di Sepolia R2. Hentikan transaksi terlebih dahulu.", "warning", currentNetwork); 2550 | } else { 2551 | runAutoAction(autoSwapR2Usdc, "Auto Swap R2 & USDC", "Sepolia R2", "swap"); 2552 | } 2553 | } else if (selected === "Auto Swap R2 & R2USD") { 2554 | if (swapRunningSepoliaR2) { 2555 | addLog("Transaksi sedang berjalan di Sepolia R2. Hentikan transaksi terlebih dahulu.", "warning", currentNetwork); 2556 | } else { 2557 | runAutoAction(autoSwapR2R2usd, "Auto Swap R2 & R2USD", "Sepolia R2", "swap"); 2558 | } 2559 | } else if (selected === "Auto Add LP R2 & USDC") { 2560 | if (swapRunningSepoliaR2) { 2561 | addLog("Transaksi sedang berjalan di Sepolia R2. Hentikan transaksi terlebih dahulu.", "warning", currentNetwork); 2562 | } else { 2563 | runAutoAction(autoAddLpR2Usdc, "Auto Add LP R2 & USDC", "Sepolia R2", "single"); 2564 | } 2565 | } else if (selected === "Auto Add LP R2 & R2USD") { 2566 | if (swapRunningSepoliaR2) { 2567 | addLog("Transaksi sedang berjalan di Sepolia R2. Hentikan transaksi terlebih dahulu.", "warning", currentNetwork); 2568 | } else { 2569 | runAutoAction(autoAddLpR2R2usd, "Auto Add LP R2 & R2USD", "Sepolia R2", "single"); 2570 | } 2571 | } else if (selected === "Manual Swap") { 2572 | r2SepoliaSubMenu.hide(); 2573 | r2SepoliaManualSwapSubMenu.show(); 2574 | r2SepoliaManualSwapSubMenu.focus(); 2575 | safeRender(); 2576 | } else if (selected === "Change Random Amount") { 2577 | r2SepoliaSubMenu.hide(); 2578 | r2SepoliaChangeRandomAmountSubMenu.show(); 2579 | r2SepoliaChangeRandomAmountSubMenu.focus(); 2580 | safeRender(); 2581 | } else if (selected === "Stop Transaction") { 2582 | if (swapRunningSepoliaR2) { 2583 | swapCancelledSepoliaR2 = true; 2584 | addLog("Perintah Stop Transaction diterima untuk Sepolia R2.", "swap", currentNetwork); 2585 | } 2586 | } else if (selected === "Clear Transaction Logs") { 2587 | clearTransactionLogs(); 2588 | } else if (selected === "Back To Main Menu") { 2589 | r2SepoliaSubMenu.hide(); 2590 | walletBox.hide(); 2591 | welcomeBox.show(); 2592 | mainMenu.show(); 2593 | mainMenu.focus(); 2594 | updateWelcomeBox(); 2595 | safeRender(); 2596 | } else if (selected === "Refresh") { 2597 | updateWalletData("Sepolia R2"); 2598 | safeRender(); 2599 | addLog("Refreshed", "system", currentNetwork); 2600 | } 2601 | }); 2602 | 2603 | pharosSubMenu.on("select", (item) => { 2604 | const selected = item.getText(); 2605 | if (selected === "Auto Swap R2USD & USDC") { 2606 | if (swapRunningPharos) { 2607 | addLog("Transaksi sedang berjalan di Pharos. Hentikan transaksi terlebih dahulu.", "warning", currentNetwork); 2608 | } else { 2609 | runAutoAction(autoSwapR2usdUsdc, "Auto Swap R2USD & USDC", "Pharos"); 2610 | } 2611 | } else if (selected === "Auto Stake R2USD & sR2USD") { 2612 | if (swapRunningPharos) { 2613 | addLog("Transaksi sedang berjalan di Pharos. Hentikan transaksi terlebih dahulu.", "warning", currentNetwork); 2614 | } else { 2615 | runAutoAction(autoStakeR2usdSr2usd, "Auto Stake R2USD & sR2USD", "Pharos"); 2616 | } 2617 | } else if (selected === "Manual Swap") { 2618 | pharosSubMenu.hide(); 2619 | pharosManualSwapSubMenu.show(); 2620 | pharosManualSwapSubMenu.focus(); 2621 | safeRender(); 2622 | } else if (selected === "Change Random Amount") { 2623 | pharosSubMenu.hide(); 2624 | pharosChangeRandomAmountSubMenu.show(); 2625 | pharosChangeRandomAmountSubMenu.focus(); 2626 | safeRender(); 2627 | } else if (selected === "Stop Transaction") { 2628 | if (swapRunningPharos) { 2629 | swapCancelledPharos = true; 2630 | addLog("Perintah Stop Transaction diterima untuk Pharos.", "swap", currentNetwork); 2631 | } 2632 | } else if (selected === "Clear Transaction Logs") { 2633 | clearTransactionLogs(); 2634 | } else if (selected === "Back To Main Menu") { 2635 | pharosSubMenu.hide(); 2636 | sepoliaSubMenu.hide(); 2637 | r2SepoliaSubMenu.hide(); 2638 | monadSubMenu.hide(); 2639 | walletBox.hide(); 2640 | welcomeBox.show(); 2641 | mainMenu.show(); 2642 | mainMenu.focus(); 2643 | updateWelcomeBox(); 2644 | safeRender(); 2645 | } else if (selected === "Refresh") { 2646 | updateWalletData("Pharos"); 2647 | safeRender(); 2648 | addLog("Refreshed", "system", currentNetwork); 2649 | } 2650 | }); 2651 | 2652 | monadSubMenu.on("select", (item) => { 2653 | const selected = item.getText(); 2654 | if (selected === "Auto Swap R2USD & USDC") { 2655 | if (swapRunningMonad) { 2656 | addLog("Transaksi sedang berjalan di Monad. Hentikan transaksi terlebih dahulu.", "warning", currentNetwork); 2657 | } else { 2658 | runAutoAction(autoSwapR2usdUsdc, "Auto Swap R2USD & USDC", "Monad"); 2659 | } 2660 | } else if (selected === "Auto Stake R2USD & sR2USD") { 2661 | if (swapRunningMonad) { 2662 | addLog("Transaksi sedang berjalan di Monad. Hentikan transaksi terlebih dahulu.", "warning", currentNetwork); 2663 | } else { 2664 | runAutoAction(autoStakeR2usdSr2usd, "Auto Stake R2USD & sR2USD", "Monad"); 2665 | } 2666 | } else if (selected === "Auto Add LP R2USD & sR2USD") { 2667 | if (swapRunningMonad) { 2668 | addLog("Transaksi sedang berjalan di Monad. Hentikan transaksi terlebih dahulu.", "warning", currentNetwork); 2669 | } else { 2670 | runAutoAction(autoAddLpR2usdSr2usd, "Auto Add LP R2USD & sR2USD", "Monad", "single"); 2671 | } 2672 | } else if (selected === "Auto Add LP USDC & R2USD") { 2673 | if (swapRunningMonad) { 2674 | addLog("Transaksi sedang berjalan di Monad. Hentikan transaksi terlebih dahulu.", "warning", currentNetwork); 2675 | } else { 2676 | runAutoAction(autoAddLpUsdcR2usd, "Auto Add LP USDC & R2USD", "Monad", "single"); 2677 | } 2678 | } else if (selected === "Manual Swap") { 2679 | monadSubMenu.hide(); 2680 | monadManualSwapSubMenu.show(); 2681 | monadManualSwapSubMenu.focus(); 2682 | safeRender(); 2683 | } else if (selected === "Change Random Amount") { 2684 | monadSubMenu.hide(); 2685 | monadChangeRandomAmountSubMenu.show(); 2686 | monadChangeRandomAmountSubMenu.focus(); 2687 | safeRender(); 2688 | } else if (selected === "Stop Transaction") { 2689 | if (swapRunningMonad) { 2690 | swapCancelledMonad = true; 2691 | addLog("Perintah Stop Transaction diterima untuk Monad.", "swap", currentNetwork); 2692 | } 2693 | } else if (selected === "Clear Transaction Logs") { 2694 | clearTransactionLogs(); 2695 | } else if (selected === "Back To Main Menu") { 2696 | monadSubMenu.hide(); 2697 | sepoliaSubMenu.hide(); 2698 | r2SepoliaSubMenu.hide(); 2699 | pharosSubMenu.hide(); 2700 | monadManualSwapSubMenu.hide(); 2701 | monadChangeRandomAmountSubMenu.hide(); 2702 | walletBox.hide(); 2703 | welcomeBox.show(); 2704 | mainMenu.show(); 2705 | mainMenu.focus(); 2706 | updateWelcomeBox(); 2707 | safeRender(); 2708 | } else if (selected === "Refresh") { 2709 | updateWalletData("Monad"); 2710 | safeRender(); 2711 | addLog("Refreshed", "system", currentNetwork); 2712 | } 2713 | }); 2714 | 2715 | 2716 | claimFaucetSubMenu.on("select", (item) => { 2717 | const selected = item.getText(); 2718 | if (selected === "Auto Claim Faucet All Network") { 2719 | if (claimRunning) { 2720 | addLog("Proses claim faucet sedang berjalan. Hentikan proses terlebih dahulu.", "warning"); 2721 | } else { 2722 | addLog("Memulai Auto Claim Faucet untuk semua jaringan.", "system"); 2723 | claimAllFaucetsWithDelay(); 2724 | } 2725 | } else if (selected === "Auto Daily Claim Faucet All Network") { 2726 | if (claimRunning) { 2727 | addLog("Proses claim faucet sedang berjalan. Hentikan proses terlebih dahulu.", "warning"); 2728 | } else { 2729 | startAutoDailyClaim(); 2730 | } 2731 | } else if (selected === "Stop Auto Daily Claim") { 2732 | stopAutoDailyClaim(); 2733 | } else if (selected === "Stop Proses") { 2734 | if (claimRunning) { 2735 | claimCancelled = true; 2736 | addLog("Perintah Stop Proses diterima untuk claim faucet.", "system"); 2737 | claimFaucetSubMenu.setItems(getClaimFaucetSubMenuItems()); 2738 | safeRender(); 2739 | } 2740 | } else if (selected.startsWith("Claim Faucet ")) { 2741 | const network = selected.replace("Claim Faucet ", ""); 2742 | addLog(`Memulai Claim Faucet untuk ${network}.`, "system"); 2743 | claimFaucet(network); 2744 | } else if (selected === "Clear Transaction Logs") { 2745 | clearTransactionLogs(); 2746 | } else if (selected === "Back to Main Menu") { 2747 | claimFaucetSubMenu.hide(); 2748 | mainMenu.setItems(getMainMenItems()); 2749 | mainMenu.show(); 2750 | mainMenu.focus(); 2751 | safeRender(); 2752 | } else if (selected === "Refresh") { 2753 | addLog("Refreshed", "system"); 2754 | claimFaucetSubMenu.setItems(getClaimFaucetSubMenuItems()); 2755 | safeRender(); 2756 | } 2757 | }); 2758 | 2759 | sepoliaManualSwapSubMenu.on("select", (item) => { 2760 | const selected = item.getText(); 2761 | if (selected === "USDC -> R2USD") { 2762 | promptBox.setFront(); 2763 | promptBox.input("Masukkan jumlah USDC yang ingin di-swap ke R2USD", "", async (err, value) => { 2764 | promptBox.hide(); 2765 | safeRender(); 2766 | if (err || !value) { 2767 | addLog("Manual Swap: Input tidak valid atau dibatalkan.", "swap", currentNetwork); 2768 | return; 2769 | } 2770 | const amount = parseFloat(value); 2771 | if (isNaN(amount) || amount <= 0) { 2772 | addLog("Manual Swap: Jumlah harus berupa angka lebih besar dari 0.", "swap", currentNetwork); 2773 | return; 2774 | } 2775 | addLog(`Manual Swap: Memulai swap ${amount} USDC ke R2USD.`, "swap", currentNetwork); 2776 | try { 2777 | await addTransactionToQueue( 2778 | (nonce, wallet, provider, config) => swapUsdcToR2usd(amount, nonce, wallet, provider, config), 2779 | `Manual Swap ${amount} USDC to R2USD`, 2780 | "Sepolia" 2781 | ); 2782 | await updateWalletData("Sepolia"); 2783 | addLog(`Manual Swap: Swap ${amount} USDC ke R2USD selesai.`, "success", currentNetwork); 2784 | } catch (error) { 2785 | addLog(`Manual Swap: Gagal - ${error.message}`, "error", currentNetwork); 2786 | } 2787 | }); 2788 | } else if (selected === "R2USD -> USDC") { 2789 | promptBox.setFront(); 2790 | promptBox.input("Masukkan jumlah R2USD yang ingin di-swap ke USDC", "", async (err, value) => { 2791 | promptBox.hide(); 2792 | safeRender(); 2793 | if (err || !value) { 2794 | addLog("Manual Swap: Input tidak valid atau dibatalkan.", "swap", currentNetwork); 2795 | return; 2796 | } 2797 | const amount = parseFloat(value); 2798 | if (isNaN(amount) || amount <= 0) { 2799 | addLog("Manual Swap: Jumlah harus berupa angka lebih besar dari 0.", "swap", currentNetwork); 2800 | return; 2801 | } 2802 | addLog(`Manual Swap: Memulai swap ${amount} R2USD ke USDC.`, "swap", currentNetwork); 2803 | try { 2804 | await addTransactionToQueue( 2805 | (nonce, wallet, provider, config) => swapR2usdToUsdc(amount, nonce, wallet, provider, config), 2806 | `Manual Swap ${amount} R2USD to USDC`, 2807 | "Sepolia" 2808 | ); 2809 | await updateWalletData("Sepolia"); 2810 | addLog(`Manual Swap: Swap ${amount} R2USD ke USDC selesai.`, "success", currentNetwork); 2811 | } catch (error) { 2812 | addLog(`Manual Swap: Gagal - ${error.message}`, "error", currentNetwork); 2813 | } 2814 | }); 2815 | } else if (selected === "Back To Sepolia Network Menu") { 2816 | sepoliaManualSwapSubMenu.hide(); 2817 | sepoliaSubMenu.setItems(getSepoliaSubMenuItems()); 2818 | sepoliaSubMenu.show(); 2819 | sepoliaSubMenu.focus(); 2820 | safeRender(); 2821 | } 2822 | }); 2823 | 2824 | r2SepoliaManualSwapSubMenu.on("select", (item) => { 2825 | const selected = item.getText(); 2826 | if (selected === "R2 -> USDC") { 2827 | promptBox.setFront(); 2828 | promptBox.input("Masukkan jumlah R2 yang ingin di-swap ke USDC", "", async (err, value) => { 2829 | promptBox.hide(); 2830 | safeRender(); 2831 | if (err || !value) { 2832 | addLog("Manual Swap: Input tidak valid atau dibatalkan.", "swap", currentNetwork); 2833 | return; 2834 | } 2835 | const amount = ethers.parseUnits(value, 18); 2836 | addLog(`Manual Swap: Memulai swap ${value} R2 ke USDC.`, "swap", currentNetwork); 2837 | try { 2838 | await addTransactionToQueue( 2839 | (nonce, wallet, provider, config) => swapTokens(amount, config.R2_ADDRESS, config.USDC_ADDRESS, nonce, wallet, provider, config), 2840 | `Manual Swap ${value} R2 to USDC`, 2841 | "Sepolia R2" 2842 | ); 2843 | await updateWalletData("Sepolia R2"); 2844 | addLog(`Manual Swap: Swap ${value} R2 ke USDC selesai.`, "success", currentNetwork); 2845 | } catch (error) { 2846 | addLog(`Manual Swap: Gagal - ${error.message}`, "error", currentNetwork); 2847 | } 2848 | }); 2849 | } else if (selected === "USDC -> R2") { 2850 | promptBox.setFront(); 2851 | promptBox.input("Masukkan jumlah USDC yang ingin di-swap ke R2", "", async (err, value) => { 2852 | promptBox.hide(); 2853 | safeRender(); 2854 | if (err || !value) { 2855 | addLog("Manual Swap: Input tidak valid atau dibatalkan.", "swap", currentNetwork); 2856 | return; 2857 | } 2858 | const amount = ethers.parseUnits(value, 6); 2859 | addLog(`Manual Swap: Memulai swap ${value} USDC ke R2.`, "swap", currentNetwork); 2860 | try { 2861 | await addTransactionToQueue( 2862 | (nonce, wallet, provider, config) => swapTokens(amount, config.USDC_ADDRESS, config.R2_ADDRESS, nonce, wallet, provider, config), 2863 | `Manual Swap ${value} USDC to R2`, 2864 | "Sepolia R2" 2865 | ); 2866 | await updateWalletData("Sepolia R2"); 2867 | addLog(`Manual Swap: Swap ${value} USDC ke R2 selesai.`, "success", currentNetwork); 2868 | } catch (error) { 2869 | addLog(`Manual Swap: Gagal - ${error.message}`, "error", currentNetwork); 2870 | } 2871 | }); 2872 | } else if (selected === "R2 -> R2USD") { 2873 | promptBox.setFront(); 2874 | promptBox.input("Masukkan jumlah R2 yang ingin di-swap ke R2USD", "", async (err, value) => { 2875 | promptBox.hide(); 2876 | safeRender(); 2877 | if (err || !value) { 2878 | addLog("Manual Swap: Input tidak valid atau dibatalkan.", "swap", currentNetwork); 2879 | return; 2880 | } 2881 | const amount = ethers.parseUnits(value, 18); 2882 | addLog(`Manual Swap: Memulai swap ${value} R2 ke R2USD.`, "swap", currentNetwork); 2883 | try { 2884 | await addTransactionToQueue( 2885 | (nonce, wallet, provider, config) => swapTokens(amount, config.R2_ADDRESS, config.R2USD_ADDRESS, nonce, wallet, provider, config), 2886 | `Manual Swap ${value} R2 to R2USD`, 2887 | "Sepolia R2" 2888 | ); 2889 | await updateWalletData("Sepolia R2"); 2890 | addLog(`Manual Swap: Swap ${value} R2 ke R2USD selesai.`, "success", currentNetwork); 2891 | } catch (error) { 2892 | addLog(`Manual Swap: Gagal - ${error.message}`, "error", currentNetwork); 2893 | } 2894 | }); 2895 | } else if (selected === "R2USD -> R2") { 2896 | promptBox.setFront(); 2897 | promptBox.input("Masukkan jumlah R2USD yang ingin di-swap ke R2", "", async (err, value) => { 2898 | promptBox.hide(); 2899 | safeRender(); 2900 | if (err || !value) { 2901 | addLog("Manual Swap: Input tidak valid atau dibatalkan.", "swap", currentNetwork); 2902 | return; 2903 | } 2904 | const amount = ethers.parseUnits(value, 6); 2905 | addLog(`Manual Swap: Memulai swap ${value} R2USD ke R2.`, "swap", currentNetwork); 2906 | try { 2907 | await addTransactionToQueue( 2908 | (nonce, wallet, provider, config) => swapTokens(amount, config.R2USD_ADDRESS, config.R2_ADDRESS, nonce, wallet, provider, config), 2909 | `Manual Swap ${value} R2USD to R2`, 2910 | "Sepolia R2" 2911 | ); 2912 | await updateWalletData("Sepolia R2"); 2913 | addLog(`Manual Swap: Swap ${value} R2USD ke R2 selesai.`, "success", currentNetwork); 2914 | } catch (error) { 2915 | addLog(`Manual Swap: Gagal - ${error.message}`, "error", currentNetwork); 2916 | } 2917 | }); 2918 | } else if (selected === "Back To Sepolia R2 Network Menu") { 2919 | r2SepoliaManualSwapSubMenu.hide(); 2920 | r2SepoliaSubMenu.setItems(getSepoliaR2SubMenuItems()); 2921 | r2SepoliaSubMenu.show(); 2922 | r2SepoliaSubMenu.focus(); 2923 | safeRender(); 2924 | } 2925 | }); 2926 | 2927 | pharosManualSwapSubMenu.on("select", (item) => { 2928 | const selected = item.getText(); 2929 | if (selected === "USDC -> R2USD") { 2930 | promptBox.setFront(); 2931 | promptBox.input("Masukkan jumlah USDC yang ingin di-swap ke R2USD", "", async (err, value) => { 2932 | promptBox.hide(); 2933 | safeRender(); 2934 | if (err || !value) { 2935 | addLog("Manual Swap: Input tidak valid atau dibatalkan.", "swap", currentNetwork); 2936 | return; 2937 | } 2938 | const amount = parseFloat(value); 2939 | if (isNaN(amount) || amount <= 0) { 2940 | addLog("Manual Swap: Jumlah harus berupa angka lebih besar dari 0.", "swap", currentNetwork); 2941 | return; 2942 | } 2943 | addLog(`Manual Swap: Memulai swap ${amount} USDC ke R2USD.`, "swap", currentNetwork); 2944 | try { 2945 | await addTransactionToQueue( 2946 | (nonce, wallet, provider, config) => swapUsdcToR2usd(amount, nonce, wallet, provider, config), 2947 | `Manual Swap ${amount} USDC to R2USD`, 2948 | "Pharos" 2949 | ); 2950 | await updateWalletData("Pharos"); 2951 | addLog(`Manual Swap: Swap ${amount} USDC ke R2USD selesai.`, "success", currentNetwork); 2952 | } catch (error) { 2953 | addLog(`Manual Swap: Gagal - ${error.message}`, "error", currentNetwork); 2954 | } 2955 | }); 2956 | } else if (selected === "R2USD -> USDC") { 2957 | promptBox.setFront(); 2958 | promptBox.input("Masukkan jumlah R2USD yang ingin di-swap ke USDC", "", async (err, value) => { 2959 | promptBox.hide(); 2960 | safeRender(); 2961 | if (err || !value) { 2962 | addLog("Manual Swap: Input tidak valid atau dibatalkan.", "swap", currentNetwork); 2963 | return; 2964 | } 2965 | const amount = parseFloat(value); 2966 | if (isNaN(amount) || amount <= 0) { 2967 | addLog("Manual Swap: Jumlah harus berupa angka lebih besar dari 0.", "swap", currentNetwork); 2968 | return; 2969 | } 2970 | addLog(`Manual Swap: Memulai swap ${amount} R2USD ke USDC.`, "swap", currentNetwork); 2971 | try { 2972 | await addTransactionToQueue( 2973 | (nonce, wallet, provider, config) => swapR2usdToUsdC(amount, nonce, wallet, provider, config), 2974 | `Manual Swap ${amount} R2USD to USDC`, 2975 | "Pharos" 2976 | ); 2977 | await updateWalletData("Pharos"); 2978 | addLog(`Manual Swap: Swap ${amount} R2USD ke USDC selesai.`, "success", currentNetwork); 2979 | } catch (error) { 2980 | addLog(`Manual Swap: Gagal - ${error.message}`, "error", currentNetwork); 2981 | } 2982 | }); 2983 | } else if (selected === "Back To Pharos Network Menu") { 2984 | pharosManualSwapSubMenu.hide(); 2985 | pharosSubMenu.setItems(getPharosSubMenuItems()); 2986 | pharosSubMenu.show(); 2987 | pharosSubMenu.focus(); 2988 | safeRender(); 2989 | } 2990 | }); 2991 | 2992 | monadManualSwapSubMenu.on("select", (item) => { 2993 | const selected = item.getText(); 2994 | if (selected === "USDC -> R2USD") { 2995 | promptBox.setFront(); 2996 | promptBox.input("Masukkan jumlah USDC yang ingin di-swap ke R2USD", "", async (err, value) => { 2997 | promptBox.hide(); 2998 | safeRender(); 2999 | if (err || !value) { 3000 | addLog("Manual Swap: Input tidak valid atau dibatalkan.", "swap", currentNetwork); 3001 | return; 3002 | } 3003 | const amount = parseFloat(value); 3004 | if (isNaN(amount) || amount <= 0) { 3005 | addLog("Manual Swap: Jumlah harus berupa angka lebih besar dari 0.", "swap", currentNetwork); 3006 | return; 3007 | } 3008 | addLog(`Manual Swap: Memulai swap ${amount} USDC ke R2USD.`, "swap", currentNetwork); 3009 | try { 3010 | await addTransactionToQueue( 3011 | (nonce, wallet, provider, config) => swapUsdcToR2usd(amount, nonce, wallet, provider, config), 3012 | `Manual Swap ${amount} USDC to R2USD`, 3013 | "Monad" 3014 | ); 3015 | await updateWalletData("Monad"); 3016 | addLog(`Manual Swap: Swap ${amount} USDC ke R2USD selesai.`, "success", currentNetwork); 3017 | } catch (error) { 3018 | addLog(`Manual Swap: Gagal - ${error.message}`, "error", currentNetwork); 3019 | } 3020 | }); 3021 | } else if (selected === "R2USD -> USDC") { 3022 | promptBox.setFront(); 3023 | promptBox.input("Masukkan jumlah R2USD yang ingin di-swap ke USDC", "", async (err, value) => { 3024 | promptBox.hide(); 3025 | safeRender(); 3026 | if (err || !value) { 3027 | addLog("Manual Swap: Input tidak valid atau dibatalkan.", "swap", currentNetwork); 3028 | return; 3029 | } 3030 | const amount = parseFloat(value); 3031 | if (isNaN(amount) || amount <= 0) { 3032 | addLog("Manual Swap: Jumlah harus berupa angka lebih besar dari 0.", "swap", currentNetwork); 3033 | return; 3034 | } 3035 | addLog(`Manual Swap: Memulai swap ${amount} R2USD ke USDC.`, "swap", currentNetwork); 3036 | try { 3037 | await addTransactionToQueue( 3038 | (nonce, wallet, provider, config) => swapR2usdToUsdC(amount, nonce, wallet, provider, config), 3039 | `Manual Swap ${amount} R2USD to USDC`, 3040 | "Monad" 3041 | ); 3042 | await updateWalletData("Monad"); 3043 | addLog(`Manual Swap: Swap ${amount} R2USD ke USDC selesai.`, "success", currentNetwork); 3044 | } catch (error) { 3045 | addLog(`Manual Swap: Gagal - ${error.message}`, "error", currentNetwork); 3046 | } 3047 | }); 3048 | } else if (selected === "Back To Monad Network Menu") { 3049 | monadManualSwapSubMenu.hide(); 3050 | monadSubMenu.setItems(getMonadSubMenuItems()); 3051 | monadSubMenu.show(); 3052 | monadSubMenu.focus(); 3053 | safeRender(); 3054 | } 3055 | }); 3056 | 3057 | 3058 | sepoliaChangeRandomAmountSubMenu.on("select", (item) => { 3059 | const selected = item.getText(); 3060 | if (selected === "SWAP_R2USD_USDC") { 3061 | changeRandomAmount("SWAP_R2USD_USDC"); 3062 | } else if (selected === "Back To Sepolia Network Menu") { 3063 | sepoliaChangeRandomAmountSubMenu.hide(); 3064 | sepoliaSubMenu.setItems(getSepoliaSubMenuItems()); 3065 | sepoliaSubMenu.show(); 3066 | sepoliaSubMenu.focus(); 3067 | safeRender(); 3068 | } 3069 | }); 3070 | 3071 | r2SepoliaChangeRandomAmountSubMenu.on("select", (item) => { 3072 | const selected = item.getText(); 3073 | if (selected === "SWAP_R2USD_USDC") { 3074 | changeRandomAmount("SWAP_R2USD_USDC"); 3075 | } else if (selected === "Back To Sepolia R2 Network Menu") { 3076 | r2SepoliaChangeRandomAmountSubMenu.hide(); 3077 | r2SepoliaSubMenu.setItems(getSepoliaR2SubMenuItems()); 3078 | r2SepoliaSubMenu.show(); 3079 | r2SepoliaSubMenu.focus(); 3080 | safeRender(); 3081 | } 3082 | }); 3083 | 3084 | 3085 | pharosChangeRandomAmountSubMenu.on("select", (item) => { 3086 | const selected = item.getText(); 3087 | if (selected === "SWAP_R2USD_USDC") { 3088 | changeRandomAmount("SWAP_R2USD_USDC"); 3089 | } else if (selected === "Back To Pharos Network Menu") { 3090 | pharosChangeRandomAmountSubMenu.hide(); 3091 | pharosSubMenu.setItems(getPharosSubMenuItems()); 3092 | pharosSubMenu.show(); 3093 | pharosSubMenu.focus(); 3094 | safeRender(); 3095 | } 3096 | }); 3097 | 3098 | monadChangeRandomAmountSubMenu.on("select", (item) => { 3099 | const selected = item.getText(); 3100 | if (selected === "SWAP_R2USD_USDC") { 3101 | changeRandomAmount("SWAP_R2USD_USDC"); 3102 | } else if (selected === "Back To Monad Network Menu") { 3103 | monadChangeRandomAmountSubMenu.hide(); 3104 | monadSubMenu.setItems(getMonadSubMenuItems()); 3105 | monadSubMenu.show(); 3106 | monadSubMenu.focus(); 3107 | safeRender(); 3108 | } 3109 | }); 3110 | 3111 | screen.key(["escape", "q", "C-c"], () => { 3112 | process.exit(0); 3113 | }); 3114 | screen.key(["C-up"], () => { logsBox.scroll(-1); safeRender(); }); 3115 | screen.key(["C-down"], () => { logsBox.scroll(1); safeRender(); }); 3116 | 3117 | safeRender(); 3118 | mainMenu.focus(); 3119 | addLog("Dont Forget To Subscribe YT And Telegram @NTExhaust!!", "system"); 3120 | updateWelcomeBox(); 3121 | --------------------------------------------------------------------------------