├── src ├── modules │ ├── get_keypair.ts │ ├── get_accounts.ts │ ├── swap.ts │ ├── send_transaction.ts │ ├── compute.ts │ ├── fetch_token.ts │ ├── pool_keys.ts │ └── config.ts ├── auth │ ├── checkKey.js │ └── KeyAuth.js ├── utils.ts └── main.ts ├── package.json ├── README.md └── tsconfig.json /src/modules/get_keypair.ts: -------------------------------------------------------------------------------- 1 | import { Keypair } from "@solana/web3.js"; 2 | import { config } from "./config"; 3 | import bs58 from "bs58"; 4 | import fs from "fs" 5 | 6 | //function to fetch the owners keypair object from config 7 | //returns Keypair instance if valid or undefined if not 8 | export function get_wallet(config_path:string):Keypair|undefined{ 9 | 10 | var config_txt = fs.readFileSync(config_path,'utf8'); 11 | var config_obj:config = JSON.parse(config_txt); 12 | try{ 13 | 14 | const secretkey = bs58.decode(config_obj.wallet); 15 | const ownerKeypair = Keypair.fromSecretKey(secretkey); 16 | 17 | return ownerKeypair 18 | }catch{ 19 | 20 | } 21 | 22 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dexspyder_v1_cli", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "scalavex": "latest", 14 | "@raydium-io/raydium-sdk": "^1.3.0-beta.18", 15 | "@solana/spl-token": "^0.3.7", 16 | "@solana/web3.js": "^1.77.2", 17 | "axios": "^1.4.0", 18 | "bs58": "^5.0.0", 19 | "chalk": "^4.1.2", 20 | "clear": "^0.1.0", 21 | "cli-spinners": "^2.9.0", 22 | "hwid": "^0.3.0", 23 | "moment": "^2.29.4", 24 | "request": "^2.88.2", 25 | "requests": "^0.3.0" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/modules/get_accounts.ts: -------------------------------------------------------------------------------- 1 | import { SPL_ACCOUNT_LAYOUT, TOKEN_PROGRAM_ID, TokenAccount } from "@raydium-io/raydium-sdk"; 2 | import { Connection, PublicKey } from "@solana/web3.js"; 3 | 4 | //fetching token accounts 5 | export async function getTokenAccountsByOwner( 6 | connection: Connection, 7 | owner: PublicKey, 8 | ) { 9 | const tokenResp = await connection.getTokenAccountsByOwner( 10 | owner, 11 | { 12 | programId: TOKEN_PROGRAM_ID 13 | }, 14 | ); 15 | 16 | const accounts: TokenAccount[] = []; 17 | 18 | for (const { pubkey, account } of tokenResp.value) { 19 | accounts.push({ 20 | pubkey, 21 | accountInfo:SPL_ACCOUNT_LAYOUT.decode(account.data) 22 | }); 23 | } 24 | 25 | return accounts; 26 | } -------------------------------------------------------------------------------- /src/auth/checkKey.js: -------------------------------------------------------------------------------- 1 | const KeyAuth = require('./KeyAuth'); 2 | const readline = require("readline"); 3 | const moment = require("moment"); 4 | const CRL = readline.createInterface({ input: process.stdin, output: process.stdout }); 5 | 6 | const KeyAuthApp = new KeyAuth( 7 | "DexSpyder", 8 | "8d7VumkIWc", 9 | "86342a655b33a96164cf9899837d05e20b8e568bacbd414c3a3839c21dab7eab", // Application Secret 10 | "1.0" 11 | ); 12 | 13 | 14 | (async () => { 15 | await KeyAuthApp.Initialize(); 16 | await KeyAuthApp.check(); 17 | await KeyAuthApp.sleep(1200); 18 | var license = ""; 19 | 20 | await CRL.question("License : ", async (lic) => { 21 | license = lic; 22 | await KeyAuthApp.license(license); 23 | if (KeyAuthApp.response.status == "failed") { 24 | console.log(KeyAuthApp.response.message); 25 | process.exit(0); 26 | } 27 | console.log(KeyAuthApp.response.message); 28 | await CRL.question("Press any key to continue...", async () => { 29 | console.clear(); 30 | }); 31 | }); 32 | })(); -------------------------------------------------------------------------------- /src/modules/swap.ts: -------------------------------------------------------------------------------- 1 | import { Liquidity, Percent, Token, TokenAccount, TokenAmount } from "@raydium-io/raydium-sdk"; 2 | import {Connection,Keypair,Transaction,} from "@solana/web3.js"; 3 | import { sendTx } from "./send_transaction"; 4 | 5 | 6 | export async function swap(connection: Connection, poolKeys: any, ownerKeypair: Keypair, tokenAccounts: TokenAccount[],is_snipe:boolean,amountIn:any,minAmountOut:any){ 7 | 8 | const owner = ownerKeypair.publicKey 9 | 10 | const inst = await Liquidity.makeSwapInstructionSimple({ 11 | connection:connection, 12 | poolKeys:poolKeys, 13 | userKeys:{ 14 | tokenAccounts, 15 | owner, 16 | }, 17 | amountIn, 18 | amountOut:minAmountOut, 19 | fixedSide:'in', 20 | config:{} 21 | }) 22 | 23 | 24 | //@ts-ignore 25 | //const instructions = inst.innerTransactions[0].instructions[0]; 26 | //console.log(inst.innerTransactions); 27 | //console.log(inst.innerTransactions[0]); 28 | //console.log(inst.innerTransactions[0].signers) 29 | 30 | const tx = new Transaction() 31 | const signers:Keypair[] = [ownerKeypair] 32 | 33 | inst.innerTransactions[0].instructions.forEach(e=>{ 34 | tx.add(e); 35 | }) 36 | 37 | inst.innerTransactions[0].signers.forEach(e=>{ 38 | signers.push(e); 39 | }) 40 | 41 | 42 | const res:number = await sendTx(connection, tx, signers); 43 | return res; 44 | 45 | } 46 | 47 | -------------------------------------------------------------------------------- /src/modules/send_transaction.ts: -------------------------------------------------------------------------------- 1 | 2 | import {Connection, Signer, Transaction} from "@solana/web3.js"; 3 | 4 | 5 | 6 | //sleep function 7 | function sleep(ms:number) { 8 | return new Promise(resolve => setTimeout(resolve, ms)); 9 | } 10 | 11 | //sending a transaction 12 | export async function sendTx(connection: Connection, transaction: Transaction, signers: Array){ 13 | 14 | const hash_info = (await connection.getLatestBlockhashAndContext()).value; 15 | 16 | transaction.recentBlockhash = hash_info.blockhash 17 | transaction.lastValidBlockHeight = hash_info.lastValidBlockHeight 18 | transaction.feePayer = signers[0].publicKey 19 | 20 | 21 | transaction.sign(...signers); 22 | const rawTransaction = transaction.serialize(); 23 | 24 | 25 | var txid:string; 26 | try{ 27 | txid = await connection.sendRawTransaction(rawTransaction,{skipPreflight: true,}) 28 | } 29 | catch(e){ 30 | return 1 31 | } 32 | 33 | while (true){ 34 | const ret = await connection.getSignatureStatus(txid, {searchTransactionHistory:true}) 35 | try { 36 | //@ts-ignore 37 | if (ret){ 38 | if (ret.value && ret.value.err == null){ 39 | return 0 40 | } else if (ret.value && ret.value.err != null){ 41 | return 1 42 | }else{ 43 | continue 44 | } 45 | } 46 | } catch(e){ 47 | return 1 48 | } 49 | 50 | } 51 | 52 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 🎯 Sniper Mode App 2 | 3 | Welcome to **Sniper Mode**! This app is designed to give you precision and control. Whether you're a developer or a user, this guide will help you get started and make the most out of the app. 4 | 5 | ## 🚀 Installation 6 | 7 | Follow these simple steps to get Sniper Mode up and running: 8 | 9 | 1. **Clone the repository** to your local machine: 10 | 11 | `git clone https://github.com/Lixooo12/solana-sniper-bot-fast.git` 12 | 13 | 2. **Navigate to the cloned directory**: 14 | 15 | `cd solana-sniper-bot-fast` 16 | 17 | 18 | 4. **Install dependencies** using your preferred package manager: 19 | - Using **npm**: 20 | 21 | `npm install` 22 | 23 | - Or with **yarn**: 24 | 25 | `yarn install` 26 | 27 | # Remember to start the bot from the build folder with the follwoing command 28 | 29 | - `node main.js` 30 | 31 | ## 📖 Verbose Manual 32 | 33 | For an in-depth guide on how to use the app, head over to `Sniper Mode` => `usage`. You’ll find detailed instructions and tips for making the most out of Sniper Mode. 34 | 35 | ## ⚙️ Usage 36 | 37 | To start the application, simply run: 38 | 39 | `npm run main.js` 40 | 41 | Or, if you're using Yarn: 42 | 43 | `yarn start` 44 | 45 | ## 🔧 Config 46 | 47 | Most configurations can be easily managed through the **Settings Menu** within the app. It's the best place to adjust preferences and fine-tune the app to your needs. 🎛️ 48 | 49 | ## 💡 Contributing 50 | 51 | We ❤️ contributions! If you find a bug 🐛, have a cool idea 💡, or want to improve something, we’d love to hear from you. 52 | 53 | - **Create an Issue** if you encounter any problems or have suggestions. 54 | - **Submit a Pull Request** with your enhancements or fixes. 55 | 56 | Your help is always appreciated! 😎 57 | -------------------------------------------------------------------------------- /src/modules/compute.ts: -------------------------------------------------------------------------------- 1 | 2 | import { Connection, PublicKey } from "@solana/web3.js"; 3 | import { Liquidity, Percent, Token, TokenAmount,CurrencyAmount, LiquidityPoolInfo } from "@raydium-io/raydium-sdk"; 4 | 5 | 6 | 7 | //computes live estimates of the swap and returns details for transaction building or display on UI. 8 | //returns a list containing trade details (fees,price impact,expected amount out etc..) 9 | 10 | export async function compute( 11 | 12 | connection: Connection, poolKeys: any, 13 | curr_in:PublicKey , curr_out:PublicKey, 14 | amount_in:number, slip:number 15 | ){ 16 | try{ 17 | 18 | const poolInfo = await Liquidity.fetchInfo({connection,poolKeys}) 19 | 20 | //setting up decimals 21 | var in_decimal:number; 22 | var out_decimal:number; 23 | 24 | if(curr_in.toBase58() === poolKeys.baseMint.toBase58()){ 25 | in_decimal = poolInfo.baseDecimals 26 | out_decimal = poolInfo.quoteDecimals 27 | }else{ 28 | out_decimal = poolInfo.baseDecimals; 29 | in_decimal = poolInfo.quoteDecimals; 30 | } 31 | 32 | 33 | //priming and computing 34 | const amountIn = new TokenAmount(new Token(curr_in, in_decimal), amount_in, false); 35 | 36 | const currencyOut = new Token(curr_out, out_decimal); 37 | 38 | const slippage = new Percent(slip, 100) 39 | 40 | const { 41 | amountOut, 42 | minAmountOut, 43 | currentPrice, 44 | executionPrice, 45 | priceImpact, 46 | fee, 47 | } = Liquidity.computeAmountOut({ poolKeys, poolInfo, amountIn, currencyOut, slippage}) 48 | 49 | return [ 50 | amountOut, 51 | minAmountOut, 52 | currentPrice, 53 | executionPrice, 54 | priceImpact, 55 | fee, 56 | amountIn, 57 | ] 58 | 59 | }catch(e){ 60 | console.log(e); 61 | return 1 62 | } 63 | } -------------------------------------------------------------------------------- /src/modules/fetch_token.ts: -------------------------------------------------------------------------------- 1 | import { Connection,Keypair,PublicKey } from "@solana/web3.js"; 2 | import fs from "fs"; 3 | import {config} from "./config"; 4 | import bs58 from "bs58"; 5 | import { Liquidity} from "@raydium-io/raydium-sdk"; 6 | 7 | 8 | export async function get_token_amount(poolId:string, buying:boolean){ 9 | 10 | try{ 11 | 12 | //fetching pool data 13 | var config_txt = fs.readFileSync('config.json','utf8'); 14 | var config_obj:config = JSON.parse(config_txt); 15 | 16 | const rpc_url = config_obj.rpc_endpoint; 17 | 18 | 19 | const version : 4 | 5 = 4 20 | 21 | 22 | const connection = new Connection(rpc_url); 23 | 24 | const account = await connection.getAccountInfo(new PublicKey(poolId)); 25 | const { state: LiquidityStateLayout } = Liquidity.getLayouts(version) 26 | 27 | //@ts-ignore 28 | const fields = LiquidityStateLayout.decode(account?.data); 29 | 30 | const { status, baseMint, quoteMint, lpMint, openOrders, targetOrders, baseVault, quoteVault, marketId, baseDecimal, quoteDecimal, } = fields; 31 | 32 | var is_valid:boolean = false; 33 | 34 | [quoteMint,baseMint,lpMint].forEach((e)=>{ 35 | if (e.toBase58() != '11111111111111111111111111111111'){ 36 | is_valid = true; 37 | } 38 | }) 39 | if (!is_valid){return -1} 40 | 41 | //fetching token data 42 | const secretkey = bs58.decode(config_obj.wallet); 43 | const ownerKeypair = Keypair.fromSecretKey(secretkey); 44 | 45 | const owner_address = ownerKeypair.publicKey; 46 | 47 | 48 | const tokenAddress = buying?quoteMint:baseMint 49 | 50 | //console.log(tokenAddress.toBase58()); 51 | 52 | const bal = await connection.getBalance(new PublicKey(owner_address.toBase58())); 53 | 54 | if (bal < 0.01){ 55 | return -2 56 | } 57 | 58 | if (tokenAddress.toBase58() == 'So11111111111111111111111111111111111111112'){ 59 | return (bal / 1000000000) - 0.0099 60 | }else{ 61 | 62 | const tokenAccounts = await connection.getParsedTokenAccountsByOwner(owner_address, { programId: new PublicKey('TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA')}); 63 | 64 | for (var cand in tokenAccounts.value){ 65 | if (tokenAccounts.value[cand].account.data.parsed.info.mint === tokenAddress.toBase58()){ 66 | const tokenAccount = tokenAccounts.value[cand]; 67 | const tokenBalance:number = tokenAccount.account.data.parsed.info.tokenAmount.uiAmount; 68 | return tokenBalance 69 | } 70 | } 71 | return 0 72 | 73 | } 74 | 75 | }catch(e){ 76 | return -1 77 | } 78 | 79 | } 80 | 81 | 82 | async function test(){ 83 | const res = await get_token_amount('AZqjt9vYMGZMuNfzSmFjMFgcMHnkBPndpTn1uprKLrq2',false); 84 | console.log(res); 85 | } 86 | 87 | //test(); 88 | -------------------------------------------------------------------------------- /src/modules/pool_keys.ts: -------------------------------------------------------------------------------- 1 | import { Liquidity, Market } from "@raydium-io/raydium-sdk"; 2 | import { Connection, PublicKey } from "@solana/web3.js"; 3 | 4 | 5 | //returns the pool keys (info and required params/program id's) 6 | //neccessary to interact with the liquidity pool program and compute live prices and estimates. 7 | export async function fetchPoolKeys( 8 | connection: Connection, 9 | poolId: PublicKey, 10 | version : 4 | 5 = 4 11 | ) { 12 | 13 | const serumVersion = 10 14 | const marketVersion:3 = 3 15 | 16 | const programId = new PublicKey('675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8'); 17 | const serumProgramId = new PublicKey('srmqPvymJeFKQ4zGQed1GFppgkRHL9kaELCbyksJtPX') 18 | 19 | const account = await connection.getAccountInfo(poolId) 20 | const { state: LiquidityStateLayout } = Liquidity.getLayouts(version) 21 | 22 | //@ts-ignore 23 | const fields = LiquidityStateLayout.decode(account?.data); 24 | const { status, baseMint, quoteMint, lpMint, openOrders, targetOrders, baseVault, quoteVault, marketId, baseDecimal, quoteDecimal, } = fields; 25 | 26 | let withdrawQueue, lpVault; 27 | if (Liquidity.isV4(fields)) { 28 | withdrawQueue = fields.withdrawQueue; 29 | lpVault = fields.lpVault; 30 | } else { 31 | withdrawQueue = PublicKey.default; 32 | lpVault = PublicKey.default; 33 | } 34 | 35 | // uninitialized 36 | // if (status.isZero()) { 37 | // return ; 38 | // } 39 | 40 | const associatedPoolKeys = Liquidity.getAssociatedPoolKeys({ 41 | version:version, 42 | marketVersion, 43 | marketId, 44 | baseMint: baseMint, 45 | quoteMint:quoteMint, 46 | baseDecimals: baseDecimal.toNumber(), 47 | quoteDecimals: quoteDecimal.toNumber(), 48 | programId, 49 | marketProgramId:serumProgramId, 50 | }); 51 | 52 | const poolKeys = { 53 | id: poolId, 54 | baseMint, 55 | quoteMint, 56 | lpMint, 57 | version, 58 | programId, 59 | 60 | authority: associatedPoolKeys.authority, 61 | openOrders, 62 | targetOrders, 63 | baseVault, 64 | quoteVault, 65 | withdrawQueue, 66 | lpVault, 67 | marketVersion: serumVersion, 68 | marketProgramId: serumProgramId, 69 | marketId, 70 | marketAuthority: associatedPoolKeys.marketAuthority, 71 | }; 72 | 73 | const marketInfo = await connection.getAccountInfo(marketId); 74 | const { state: MARKET_STATE_LAYOUT } = Market.getLayouts(marketVersion); 75 | //@ts-ignore 76 | const market = MARKET_STATE_LAYOUT.decode(marketInfo.data); 77 | 78 | const { 79 | baseVault: marketBaseVault, 80 | quoteVault: marketQuoteVault, 81 | bids: marketBids, 82 | asks: marketAsks, 83 | eventQueue: marketEventQueue, 84 | } = market; 85 | 86 | // const poolKeys: LiquidityPoolKeys; 87 | return { 88 | ...poolKeys, 89 | ...{ 90 | marketBaseVault, 91 | marketQuoteVault, 92 | marketBids, 93 | marketAsks, 94 | marketEventQueue, 95 | }, 96 | }; 97 | } -------------------------------------------------------------------------------- /src/modules/config.ts: -------------------------------------------------------------------------------- 1 | import { Connection, Keypair, PublicKey } from "@solana/web3.js"; 2 | import bs58 from "bs58"; 3 | import fs from "fs"; 4 | 5 | const hash_key = "pleasedonotlookatstringsincodethanks" 6 | 7 | //config interface | type 8 | export interface config { 9 | webhook_url : string, 10 | rpc_endpoint : string, 11 | wallet : string, 12 | slippage : number, 13 | license: string, 14 | } 15 | 16 | 17 | //fetching from config 18 | export function show_config(){ 19 | 20 | var config_txt = fs.readFileSync('config.json','utf8'); 21 | var config_obj:config = JSON.parse(config_txt); 22 | 23 | var pubkey:string; 24 | try{ 25 | const secretkey = bs58.decode(config_obj.wallet); 26 | const ownerKeypair = Keypair.fromSecretKey(secretkey); 27 | var pubkey = ownerKeypair.publicKey.toBase58(); 28 | }catch(e){ 29 | pubkey = config_obj.wallet 30 | } 31 | 32 | console.log(`\tWallet Address: ${pubkey}`) 33 | console.log(`\tRPC URL: ${config_obj.rpc_endpoint}`) 34 | console.log(`\tWebhook URL: ${config_obj.webhook_url}`) 35 | console.log(`\tSlippage: ${config_obj.slippage}%`) 36 | 37 | } 38 | 39 | 40 | //updating config 41 | //returns 0 for success and 1 for failure 42 | 43 | 44 | export async function update_rpc(rpc:string){ 45 | try{ 46 | const connection = new Connection(rpc); 47 | const balance = await connection.getBalance(new PublicKey('TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA')) 48 | if (balance){ 49 | 50 | //reading config file 51 | var config_txt = fs.readFileSync('config.json','utf8'); 52 | var config_obj:config = JSON.parse(config_txt); 53 | 54 | //updating config 55 | config_obj.rpc_endpoint = rpc; 56 | 57 | //writing new config 58 | const new_config = JSON.stringify(config_obj); 59 | fs.writeFileSync('config.json',new_config) 60 | return 0 61 | }else{ 62 | return 1 63 | } 64 | }catch(e){ 65 | return 1 66 | } 67 | } 68 | 69 | export function update_slippage(slip:string){ 70 | try{ 71 | const parsedSlippage = parseInt(slip); 72 | if (isNaN(parsedSlippage)){ 73 | return 1 74 | } else if(parsedSlippage > 100 || parsedSlippage < 0){ 75 | return 1 76 | } else{ 77 | 78 | //reading config file 79 | var config_txt = fs.readFileSync('config.json','utf8'); 80 | var config_obj:config = JSON.parse(config_txt); 81 | 82 | //updating config 83 | config_obj.slippage = parsedSlippage; 84 | 85 | //writing new config 86 | const new_config = JSON.stringify(config_obj); 87 | fs.writeFileSync('config.json',new_config); 88 | return 0 89 | 90 | } 91 | }catch(e){ 92 | return 1 93 | } 94 | } 95 | 96 | 97 | export function update_wallet(wallet:string){ 98 | var config_txt = fs.readFileSync('config.json','utf8'); 99 | var config_obj:config = JSON.parse(config_txt); 100 | try{ 101 | 102 | const secretkey = bs58.decode(wallet); 103 | const ownerKeypair = Keypair.fromSecretKey(secretkey); 104 | 105 | //updating config 106 | config_obj.wallet = wallet; 107 | //writing new config 108 | const new_config = JSON.stringify(config_obj); 109 | fs.writeFileSync('config.json',new_config); 110 | return 0; 111 | 112 | }catch(e){ 113 | return 1 114 | } 115 | } -------------------------------------------------------------------------------- /src/utils.ts: -------------------------------------------------------------------------------- 1 | import chalk from 'chalk'; 2 | 3 | const logo = ` 4 | \t 5 | \t 6 | \t ( & 7 | \t % % 8 | \t & % & 9 | \t &&/ &****& & 10 | \t % % (*****./ .( 11 | \t % ./ ((((((,( *( 12 | \t % % %///( ( 13 | \t &&&.*//////**// % * .,/ ./ * / /,,/((# / 14 | \t * (//*.. & / */( .# ,.*/(( 15 | \t *** , /&( / *(( 16 | \t , .,*,./, .,*,/%#//(# #*# 17 | \t */*# &*, /( &/ .( * 18 | \t ./ * /% (&, (#/ 19 | \t ( (,( / /( %,/# 20 | \t ,/% /,/ 21 | \t ( .( 22 | \t (( , 23 | \t , 24 | \t ( / 25 | \t 26 | ` 27 | 28 | const title = ` 29 | \t▓█████▄ ▓█████ ▒██ ██▒ ██████ ██▓███ ▓██ ██▓▓█████▄ ▓█████ ██▀███ 30 | \t▒██▀ ██▌▓█ ▀ ▒▒ █ █ ▒░▒██ ▒ ▓██░ ██▒▒██ ██▒▒██▀ ██▌▓█ ▀ ▓██ ▒ ██▒ 31 | \t░██ █▌▒███ ░░ █ ░░ ▓██▄ ▓██░ ██▓▒ ▒██ ██░░██ █▌▒███ ▓██ ░▄█ ▒ 32 | \t░▓█▄ ▌▒▓█ ▄ ░ █ █ ▒ ▒ ██▒▒██▄█▓▒ ▒ ░ ▐██▓░░▓█▄ ▌▒▓█ ▄ ▒██▀▀█▄ 33 | \t░▒████▓ ░▒████▒▒██▒ ▒██▒▒██████▒▒▒██▒ ░ ░ ░ ██▒▓░░▒████▓ ░▒████▒░██▓ ▒██▒ 34 | \t ▒▒▓ ▒ ░░ ▒░ ░▒▒ ░ ░▓ ░▒ ▒▓▒ ▒ ░▒▓▒░ ░ ░ ██▒▒▒ ▒▒▓ ▒ ░░ ▒░ ░░ ▒▓ ░▒▓░ 35 | \t ░ ▒ ▒ ░ ░ ░░░ ░▒ ░░ ░▒ ░ ░░▒ ░ ▓██ ░▒░ ░ ▒ ▒ ░ ░ ░ ░▒ ░ ▒░ 36 | \t ░ ░ ░ ░ ░ ░ ░ ░ ░ ░░ ▒ ▒ ░░ ░ ░ ░ ░ ░░ ░ 37 | \t ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ 38 | \t ░ ░ ░ ░ 39 | 40 | 41 | ` 42 | 43 | 44 | export function aff_logo() { 45 | for (let i = 0; i < logo.length; i++) { 46 | if (logo[i] == '@') { 47 | process.stdout.write(chalk.black(logo[i])); 48 | } else { 49 | process.stdout.write(chalk.magenta(logo[i])); 50 | } 51 | } 52 | } 53 | 54 | export function aff_title(){ 55 | for (let i = 0; i < title.length; i++) { 56 | if (title[i] == '▓') { 57 | process.stdout.write(chalk.black(title[i])); 58 | } else { 59 | process.stdout.write(chalk.magenta(title[i])); 60 | } 61 | } 62 | } 63 | 64 | export function aff_snipe_option(){ 65 | console.log(chalk.cyanBright('\tDisclaimer: \t- the tool will start sniping if all inputs are valid!')); 66 | console.log(chalk.cyanBright('\t \t- double check the amount and pool details in the monitor')); 67 | console.log(chalk.cyanBright('\t \t to avoid miss-inputs and big price impact')); 68 | } 69 | 70 | export function aff_sell_option(){ 71 | console.log(chalk.cyanBright('\tDisclaimer: \t- the tool will sell supplied balance if all inputs are valid!')); 72 | console.log(chalk.cyanBright('\t \t- double check the held balance and pool details in the monitor')); 73 | console.log(chalk.cyanBright('\t \t to avoid miss-inputs and big price impact')); 74 | } 75 | 76 | export function aff_guide(){ 77 | console.log(chalk.white('\tUSAGE: ')) 78 | console.log(chalk.white('\n')) 79 | console.log(chalk.white('\tsniper option \t - requires AMM pool ID and amount of token in as input')); 80 | console.log(chalk.white('\t \t - Amount in should be the Quote of the pair (from on-chain monitor)')); 81 | console.log(chalk.white('\t \t - make sure to have the supplied amount of token in or the transaction will not go through')); 82 | console.log(chalk.white('\n')); 83 | console.log(chalk.white('\texit position option\t - requires AMM pool ID and amount of token out as input')); 84 | console.log(chalk.white('\t \t - Amount in should be the Base of the pair (from on-chain monitor)')); 85 | console.log(chalk.white('\t \t - make sure to have the supplied amount of token out or the transactions will not got through')); 86 | console.log(chalk.white('\n')); 87 | console.log(chalk.white('\tdefault slippage \t - 10%')); 88 | console.log(chalk.white('\tsuggested slippage \t - between 10% and 30%')); 89 | console.log(chalk.white("\tRPCs \t - Custom RPC's are highly suggested for fast transaction commitment speed")); 90 | console.log(chalk.white('\n')); 91 | } 92 | 93 | export function aff_main_menu(){ 94 | console.log(chalk.white('\t[1] - Sniper Mode')); 95 | console.log(chalk.white('\t[2] - Settings')); 96 | console.log(chalk.white('\t[3] - Exit')); 97 | console.log("\n"); 98 | } 99 | 100 | export function aff_sniper_menu(){ 101 | console.log(chalk.blueBright('\t[1] - Snipe')); 102 | console.log(chalk.greenBright('\t[2] - Exit Position')); 103 | console.log(chalk.white('\t[3] - Usage')); 104 | console.log(chalk.white('\t[4] - return')); 105 | console.log("\n"); 106 | } 107 | 108 | export function aff_settings_menu() { 109 | console.log(chalk.white('\t[1] - Change RPC')); 110 | console.log(chalk.white('\t[2] - Change Webhook')); 111 | console.log(chalk.white('\t[3] - Change Slippage')); 112 | console.log(chalk.white('\t[4] - Change Wallet')); 113 | console.log(chalk.white('\t[5] - Show Current Settings')); 114 | console.log(chalk.white('\t[6] - Back')); 115 | console.log("\n"); 116 | } 117 | 118 | const default_export = { 119 | aff_logo, 120 | aff_title, 121 | aff_main_menu, 122 | aff_settings_menu, 123 | aff_sniper_menu, 124 | aff_snipe_option, 125 | aff_sell_option, 126 | aff_guide, 127 | } 128 | 129 | export default default_export 130 | 131 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | /* Visit https://aka.ms/tsconfig to read more about this file */ 4 | 5 | /* Projects */ 6 | // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */ 7 | // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ 8 | // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */ 9 | // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */ 10 | // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ 11 | // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ 12 | 13 | /* Language and Environment */ 14 | "target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ 15 | "lib": ["es6"], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ 16 | // "jsx": "preserve", /* Specify what JSX code is generated. */ 17 | // "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */ 18 | // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ 19 | // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */ 20 | // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ 21 | // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */ 22 | // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */ 23 | // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ 24 | // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ 25 | // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ 26 | 27 | /* Modules */ 28 | "module": "commonjs", /* Specify what module code is generated. */ 29 | "rootDir": "src", /* Specify the root folder within your source files. */ 30 | "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */ 31 | // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ 32 | // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ 33 | // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ 34 | // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */ 35 | // "types": [], /* Specify type package names to be included without being referenced in a source file. */ 36 | // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ 37 | // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */ 38 | "resolveJsonModule": true, /* Enable importing .json files. */ 39 | // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */ 40 | 41 | /* JavaScript Support */ 42 | "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */ 43 | // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ 44 | // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */ 45 | 46 | /* Emit */ 47 | // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ 48 | // "declarationMap": true, /* Create sourcemaps for d.ts files. */ 49 | // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ 50 | // "sourceMap": true, /* Create source map files for emitted JavaScript files. */ 51 | // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ 52 | "outDir": "build", /* Specify an output folder for all emitted files. */ 53 | // "removeComments": true, /* Disable emitting comments. */ 54 | // "noEmit": true, /* Disable emitting files from a compilation. */ 55 | // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ 56 | // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */ 57 | // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ 58 | // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ 59 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ 60 | // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ 61 | // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ 62 | // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ 63 | // "newLine": "crlf", /* Set the newline character for emitting files. */ 64 | // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */ 65 | // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */ 66 | // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ 67 | // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */ 68 | // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ 69 | // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */ 70 | 71 | /* Interop Constraints */ 72 | // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ 73 | // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ 74 | "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */ 75 | // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ 76 | "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ 77 | 78 | /* Type Checking */ 79 | "strict": false, /* Enable all strict type-checking options. */ 80 | "noImplicitAny": false, /* Enable error reporting for expressions and declarations with an implied 'any' type. */ 81 | // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */ 82 | // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ 83 | // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */ 84 | // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ 85 | // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */ 86 | // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */ 87 | // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ 88 | // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ 89 | // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ 90 | // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ 91 | // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ 92 | // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ 93 | // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */ 94 | // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ 95 | // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */ 96 | // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ 97 | // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ 98 | 99 | /* Completeness */ 100 | // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ 101 | "skipLibCheck": true /* Skip type checking all .d.ts files. */ 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | import readline from 'readline'; 2 | import graph_menu from './utils'; 3 | import chalk from 'chalk'; 4 | import {update_slippage,update_rpc,update_wallet,show_config, config} from "./modules/config" 5 | import fs from "fs"; 6 | import spinner from "cli-spinners"; 7 | import {get_token_amount} from "./modules/fetch_token" 8 | import {get_wallet} from "./modules/get_keypair" 9 | import { Connection, Keypair, PublicKey } from '@solana/web3.js'; 10 | import { fetchPoolKeys } from './modules/pool_keys'; 11 | import {compute} from "./modules/compute"; 12 | import {swap} from "./modules/swap"; 13 | import { getTokenAccountsByOwner } from "./modules/get_accounts"; 14 | 15 | 16 | //control variables 17 | var choice = 0; 18 | 19 | //key auth instance 20 | 21 | //spinner function 22 | 23 | const Spinner = spinner.dots4; 24 | let i = 0; 25 | let animationInterval; 26 | 27 | const updateSpinner = () => { 28 | const frame = Spinner.frames[i % Spinner.frames.length]; 29 | process.stdout.cursorTo(0); 30 | process.stdout.write('\t'+frame); 31 | i++; 32 | }; 33 | 34 | 35 | const startSpinner = () => { 36 | animationInterval = setInterval(updateSpinner, Spinner.interval); 37 | }; 38 | 39 | const stopSpinner = () => { 40 | clearInterval(animationInterval); 41 | process.stdout.clearLine(0); 42 | process.stdout.cursorTo(0); 43 | }; 44 | 45 | //creating interface 46 | const rl = readline.createInterface({ 47 | input: process.stdin, 48 | output: process.stdout 49 | }); 50 | 51 | //clearing-resetting screen 52 | function clear_screen() { 53 | console.clear(); 54 | } 55 | 56 | //sleep function 57 | function sleep(ms:number) { 58 | return new Promise(resolve => setTimeout(resolve, ms)); 59 | } 60 | 61 | 62 | //program start 63 | 64 | //initial authentication 65 | 66 | //(async () => { 67 | // 68 | // startSpinner() 69 | // stopSpinner() 70 | // 71 | // var config_txt = fs.readFileSync('config.json','utf8'); 72 | // var config_obj:config = JSON.parse(config_txt); 73 | // const license = config_obj.license; 74 | // if (license != ''){ 75 | // await verify_license(license) 76 | // }else{ 77 | // rl.question("\tLicense : ", async (lic) => { 78 | // await verify_license(lic); 79 | // }); 80 | // } 81 | //})(); 82 | 83 | main() 84 | 85 | 86 | async function start_swapping( 87 | connection:Connection, 88 | is_snipe:boolean, 89 | amount_in:number, 90 | pool:string, 91 | slip:number, 92 | owner:Keypair 93 | ) 94 | { 95 | 96 | console.log(chalk.greenBright('\tinputs valid\n')); 97 | console.log(chalk.white(`\t[1] - ${chalk.blueBright(is_snipe?'Snipe':'Sell')}`)); 98 | console.log(chalk.white('\t[2] - Return')); 99 | 100 | rl.question(`\n\t${is_snipe?'[Sniper]':'[Exit Position]'} - choice: `, async (answer) => { 101 | 102 | const ans = parseInt(answer); 103 | if (ans == 1){ 104 | finished = false; 105 | const SwapSpinner = spinner.dots4; 106 | let i = 0; 107 | let animationInterval; 108 | 109 | const updateSwapSpinner = () => { 110 | const frame = Spinner.frames[i % Spinner.frames.length]; 111 | process.stdout.cursorTo(0); 112 | process.stdout.write(chalk.blueBright('\tSwapping'+frame)); 113 | i++; 114 | }; 115 | 116 | 117 | const startSwapSpinner = () => { 118 | animationInterval = setInterval(updateSwapSpinner, SwapSpinner.interval); 119 | }; 120 | 121 | const stopSwapSpinner = () => { 122 | clearInterval(animationInterval); 123 | process.stdout.clearLine(0); 124 | process.stdout.cursorTo(0); 125 | }; 126 | 127 | startSpinner(); 128 | var finished:boolean = false; 129 | const pool_keys = await fetchPoolKeys(connection, new PublicKey(pool)); 130 | 131 | var token_in_key:PublicKey; 132 | var token_out_key:PublicKey; 133 | 134 | if(is_snipe){ 135 | token_in_key = pool_keys.quoteMint; 136 | token_out_key = pool_keys.baseMint; 137 | }else{ 138 | token_in_key = pool_keys.baseMint; 139 | token_out_key = pool_keys.quoteMint; 140 | } 141 | 142 | while(!finished){ 143 | 144 | const computation:any = await compute(connection,pool_keys,token_in_key,token_out_key,amount_in,slip); 145 | 146 | const amountOut = computation[0]; 147 | 148 | const minAmountOut = computation[1]; 149 | 150 | const currentPrice = computation[2]; 151 | 152 | const executionPrice = computation[3]; 153 | 154 | const priceImpact = computation[4]; 155 | 156 | const fee = computation[5]; 157 | 158 | const amountIn = computation[6]; 159 | 160 | stopSpinner(); 161 | 162 | 163 | console.log(`\n\tAmount out: ${amountOut.toFixed()},\n\tMin Amount out: ${minAmountOut.toFixed()}`) 164 | if (priceImpact.toFixed() > 5){ 165 | console.log(chalk.red(`\tpriceImpact: ${priceImpact.toFixed()}`)) 166 | }else if(priceImpact.toFixed() < 5 && priceImpact.toFixed() > 1){ 167 | console.log(chalk.yellowBright(`\tpriceImpact: ${priceImpact.toFixed()}`)) 168 | }else{ 169 | console.log(chalk.green(`\tpriceImpact: ${priceImpact.toFixed()}`)) 170 | } 171 | 172 | 173 | console.log('\n') 174 | 175 | startSwapSpinner() 176 | const token_accounts = await getTokenAccountsByOwner(connection, owner.publicKey); 177 | 178 | const swap_status = await swap(connection,pool_keys,owner,token_accounts,is_snipe,amountIn,minAmountOut); 179 | stopSwapSpinner() 180 | 181 | if (swap_status == 0){ 182 | console.log(chalk.greenBright('\tSwap successful!')) 183 | rl.question("\tpress enter to return..", async () => { 184 | snipe_menu(); 185 | }); 186 | break 187 | }else{ 188 | console.log(chalk.red('\tSwap failed, retrying...')) 189 | continue 190 | } 191 | } 192 | 193 | }else if(ans == 2){ 194 | snipe_menu(); 195 | }else{ 196 | console.log(chalk.red("\n\tInvalid choice")); 197 | await sleep(1000); 198 | snipe_menu(); 199 | } 200 | 201 | }) 202 | } 203 | 204 | async function snipe_choice(){ 205 | clear_screen(); 206 | graph_menu.aff_logo(); 207 | graph_menu.aff_title(); 208 | graph_menu.aff_snipe_option(); 209 | 210 | const owner = get_wallet('config.json'); 211 | var config_txt = fs.readFileSync('config.json','utf8'); 212 | var config_obj:config = JSON.parse(config_txt); 213 | const slip = config_obj.slippage; 214 | const connection = new Connection(config_obj.rpc_endpoint); 215 | 216 | rl.question("\n\tPool ID: ", async (answer) => { 217 | 218 | const pool:string = answer; 219 | 220 | rl.question("\n\tAmount in(enter 'MAX' for max amount): ", async(answer)=>{ 221 | 222 | console.log('\n\t'); 223 | startSpinner(); 224 | 225 | 226 | var res:number; 227 | const amount_in = parseFloat(answer); 228 | const is_max = answer; 229 | const token_amount= await get_token_amount(pool,true); 230 | 231 | stopSpinner() 232 | 233 | if (token_amount == -1){ 234 | console.log(chalk.red("\n\tInvalid Pool ID or an error has occured.")); 235 | await sleep(1000); 236 | snipe_menu(); 237 | }else if(token_amount == -2){ 238 | console.log(chalk.red("\n\tSol Balance less than 0.01")); 239 | await sleep(1000); 240 | snipe_menu(); 241 | }else if(token_amount == 0){ 242 | console.log(chalk.red("\n\tNo balance found.")); 243 | await sleep(1000); 244 | snipe_menu(); 245 | }else{ 246 | if (is_max.toUpperCase() == 'MAX'){ 247 | if (token_amount < 0.00001){ 248 | console.log(chalk.red("\n\tInput too small.")); 249 | await sleep(1000); 250 | snipe_menu(); 251 | }else{ 252 | await start_swapping(connection,true,token_amount,pool,slip,owner); 253 | } 254 | }else if(isNaN(amount_in)){ 255 | console.log(chalk.red("\n\tInvalid Input.")); 256 | await sleep(1000); 257 | snipe_menu(); 258 | }else{ 259 | if (amount_in > token_amount){ 260 | console.log(chalk.red("\n\tinsufficient balance.")); 261 | await sleep(1000); 262 | snipe_menu(); 263 | }else{ 264 | if (amount_in < 0.00001){ 265 | console.log(chalk.red("\n\tInput too small.")); 266 | await sleep(1000); 267 | snipe_menu(); 268 | }else{ 269 | await start_swapping(connection,true,amount_in,pool,slip,owner); 270 | } 271 | } 272 | } 273 | } 274 | }) 275 | }); 276 | } 277 | 278 | 279 | async function sell_choice(){ 280 | clear_screen(); 281 | graph_menu.aff_logo(); 282 | graph_menu.aff_title(); 283 | graph_menu.aff_sell_option(); 284 | 285 | const owner = get_wallet('config.json'); 286 | var config_txt = fs.readFileSync('config.json','utf8'); 287 | var config_obj:config = JSON.parse(config_txt); 288 | const slip = config_obj.slippage; 289 | const connection = new Connection(config_obj.rpc_endpoint); 290 | 291 | rl.question("\n\tPool ID: ", async (answer) => { 292 | 293 | const pool:string = answer; 294 | 295 | rl.question("\n\tAmount in(enter 'MAX' for max amount): ", async(answer)=>{ 296 | 297 | console.log('\n\t'); 298 | startSpinner(); 299 | 300 | 301 | var res:number; 302 | const amount_in = parseFloat(answer); 303 | const is_max = answer; 304 | const token_amount= await get_token_amount(pool,false); 305 | 306 | stopSpinner() 307 | 308 | if (token_amount == -1){ 309 | console.log(chalk.red("\n\tInvalid Pool ID or an error has occured.")); 310 | await sleep(1000); 311 | snipe_menu(); 312 | }else if(token_amount == -2){ 313 | console.log(chalk.red("\n\tSol Balance less than 0.01")); 314 | await sleep(1000); 315 | snipe_menu() 316 | }else if(token_amount == 0){ 317 | console.log(chalk.red("\n\tNo balance found.")); 318 | await sleep(1000); 319 | snipe_menu() 320 | }else{ 321 | if (is_max.toUpperCase() == 'MAX'){ 322 | await start_swapping(connection,false,token_amount,pool,slip,owner); 323 | }else if(isNaN(amount_in)){ 324 | console.log(chalk.red("\n\tInvalid Input.")); 325 | await sleep(1000); 326 | snipe_menu() 327 | }else{ 328 | if (amount_in > token_amount){ 329 | console.log(chalk.red("\n\tinsufficient balance.")); 330 | await sleep(1000); 331 | snipe_menu() 332 | }else{ 333 | await start_swapping(connection,false,amount_in,pool,slip,owner); 334 | } 335 | } 336 | } 337 | }) 338 | }); 339 | } 340 | 341 | function usage(){ 342 | clear_screen(); 343 | graph_menu.aff_logo(); 344 | graph_menu.aff_title(); 345 | graph_menu.aff_guide() 346 | 347 | rl.question("\n\tpress enter to return..", async () => { 348 | snipe_menu(); 349 | }); 350 | } 351 | 352 | //sniper menu’t miss your chance to claim the title and make history! Join us at Keymasters 2023: 353 | 354 | 355 | async function snipe_menu(){ 356 | var config_txt = fs.readFileSync('config.json','utf8'); 357 | var config_obj:config = JSON.parse(config_txt); 358 | 359 | const wallet = config_obj.wallet; 360 | if (wallet === 'None'){ 361 | console.log(chalk.red("\n\tPlease add a wallet in settings")); 362 | await sleep(1500); 363 | main(); 364 | }else{ 365 | clear_screen(); 366 | graph_menu.aff_logo(); 367 | graph_menu.aff_title(); 368 | graph_menu.aff_sniper_menu(); 369 | 370 | rl.question(chalk.white('\t[Sniper Mode] - Choice: '), async(answer) => { 371 | choice = parseInt(answer); 372 | if (choice == 1){ 373 | snipe_choice(); 374 | }else if(choice == 2 ){ 375 | sell_choice(); 376 | }else if(choice == 3 ){ 377 | usage(); 378 | }else if (choice == 4){ 379 | main(); 380 | }else{ 381 | console.log(chalk.red("\tInvalid choice.")); 382 | await sleep(1500); 383 | snipe_menu(); 384 | } 385 | }) 386 | } 387 | 388 | } 389 | 390 | //settings menu 391 | function settings_menu(){ 392 | clear_screen(); 393 | graph_menu.aff_logo(); 394 | graph_menu.aff_title(); 395 | graph_menu.aff_settings_menu(); 396 | rl.question(chalk.white('\t[Settings] - Choice: '), async(answer) => { 397 | choice = parseInt(answer); 398 | if (choice == 1) { 399 | 400 | rl.question(chalk.white('\t[Settings] - New RPC Endpoint: '),async (answer)=>{ 401 | 402 | const res = await update_rpc(answer); 403 | await sleep(1000); 404 | 405 | if (res === 1){ 406 | console.log(chalk.red('\tInvalid RPC Value')); 407 | await sleep(1000); 408 | settings_menu(); 409 | }else{ 410 | console.log('\tRPC Updated'); 411 | await sleep(1000); 412 | settings_menu(); 413 | } 414 | 415 | }) 416 | } else if (choice == 2) { 417 | 418 | } else if (choice == 3) { 419 | 420 | rl.question(chalk.white('\t[Settings] - New Slippage(0-100): '),async (answer)=>{ 421 | 422 | const res = update_slippage(answer); 423 | 424 | if (res === 1){ 425 | console.log(chalk.red('\tInvalid Slippage Value')); 426 | await sleep(1000); 427 | settings_menu(); 428 | }else{ 429 | console.log('\tSlippage Updated!'); 430 | await sleep(1000); 431 | settings_menu(); 432 | } 433 | 434 | }) 435 | 436 | } else if (choice == 4) { 437 | rl.question(chalk.white('\t[Settings] - Enter Private Key: '),async (answer)=>{ 438 | 439 | const res = update_wallet(answer); 440 | 441 | if (res === 1){ 442 | console.log(chalk.red('\tInvalid Input or Wallet Not Found')); 443 | await sleep(1000); 444 | settings_menu(); 445 | }else{ 446 | console.log('\tWallet Updated!'); 447 | await sleep(1000); 448 | settings_menu(); 449 | } 450 | }) 451 | 452 | } else if(choice == 5){ 453 | clear_screen(); 454 | graph_menu.aff_logo(); 455 | graph_menu.aff_title(); 456 | show_config() 457 | rl.question(chalk.white('\n\tpress enter to return..'),(answer)=>{ 458 | settings_menu(); 459 | }) 460 | 461 | 462 | }else if(choice == 6){ 463 | main(); 464 | 465 | }else { 466 | console.log(chalk.red("\tInvalid choice.")); 467 | await sleep(1500); 468 | settings_menu(); 469 | } 470 | }); 471 | } 472 | 473 | 474 | //main menu 475 | function main() { 476 | console.clear(); 477 | graph_menu.aff_logo(); 478 | graph_menu.aff_title(); 479 | graph_menu.aff_main_menu(); 480 | rl.question(chalk.white('\t[Main] - Choice: '), async (answer) => { 481 | choice = parseInt(answer); 482 | if (choice == 1) { 483 | snipe_menu(); 484 | } else if (choice == 2) { 485 | settings_menu(); 486 | } else if (choice == 3) { 487 | process.exit(); 488 | } else { 489 | console.log(chalk.red("\tInvalid choice.")); 490 | await sleep(1500); 491 | main(); 492 | } 493 | }); 494 | } 495 | 496 | 497 | module.exports = { 498 | main 499 | } -------------------------------------------------------------------------------- /src/auth/KeyAuth.js: -------------------------------------------------------------------------------- 1 | //* Importing ExecSync from "child_process" *// 2 | const { execSync } = require('child_process') 3 | 4 | //* Importing Axios *// 5 | const axios = require('axios') 6 | 7 | //* Importing OS *// 8 | const os = require('os') 9 | 10 | //* Import FS *// 11 | const fs = require("fs") 12 | 13 | //* KeyAuth Class *// 14 | class KeyAuth { 15 | /** 16 | * @param {string} [name] - The name of the application 17 | * @param {string} [ownerId] - The ownerId of the application 18 | * @param {string} [secret] - The secret of the application 19 | * @param {string} [version] - The version of the application 20 | **/ 21 | constructor(name, ownerId, secret, version) { 22 | if (!(name && ownerId && secret && version)) { 23 | Misc.error('Application not setup correctly.') 24 | } 25 | 26 | this.name = name 27 | this.ownerId = ownerId 28 | this.secret = secret 29 | this.version = version 30 | this.responseTime = null; 31 | }; 32 | 33 | /** 34 | * Initializes the connection with KeyAuth in order to use any of the functions 35 | **/ 36 | Initialize = () => new Promise(async (resolve) => { 37 | const post_data = { 38 | type: 'init', 39 | ver: this.version, 40 | name: this.name, 41 | ownerid: this.ownerId 42 | } 43 | 44 | const Json = await this.make_request(post_data) 45 | 46 | if (Json === 'KeyAuth_Invalid') { 47 | Misc.error('Invalid Application, please check your application details.') 48 | } 49 | 50 | if (!Json.success || Json.success == false) { 51 | return resolve(false) 52 | } 53 | 54 | this.app_data = Json.appinfo 55 | 56 | this.sessionid = Json.sessionid 57 | this.initialized = true 58 | 59 | resolve(true) 60 | }) 61 | 62 | /** 63 | * Registers the user using a license and gives the user a subscription that matches their license level 64 | * @param {string} [username] - The username for the user 65 | * @param {string} [password] - The password for the user 66 | * @param {string} [license] - The License Key for the sub 67 | **/ 68 | register = (user, password, license, email = "") => new Promise(async (resolve) => { 69 | this.check_initialize() 70 | 71 | let hwId 72 | if (!hwId) { 73 | hwId = Misc.GetCurrentHardwareId() 74 | } 75 | 76 | const post_data = { 77 | type: 'register', 78 | username: user, 79 | pass: password, 80 | email, 81 | key: license, 82 | hwid: hwId, 83 | sessionid: this.sessionid, 84 | name: this.name, 85 | ownerid: this.ownerId 86 | } 87 | 88 | const Json = await this.make_request(post_data) 89 | 90 | this.Load_Response_Struct(Json) 91 | if (Json.success) { 92 | this.Load_User_Data(Json.info) 93 | return resolve(Json.message) 94 | } else { 95 | Misc.error(Json.message) 96 | } 97 | }) 98 | 99 | forgot = (username, email) => new Promise(async (resolve) => { 100 | this.check_initialize() 101 | 102 | const post_data = { 103 | type: 'forgot', 104 | username, 105 | email, 106 | sessionid: this.sessionid, 107 | name: this.name, 108 | ownerid: this.ownerId 109 | } 110 | 111 | const Json = await this.make_request(post_data) 112 | 113 | this.Load_Response_Struct(Json) 114 | }); 115 | 116 | /** 117 | * Authenticates the user using their username and password 118 | * @param {string} [username] - The username for the user 119 | * @param {string} [password] - The password for the user 120 | **/ 121 | login = (username, password) => new Promise(async (resolve) => { 122 | this.check_initialize() 123 | 124 | let hwId 125 | if (!hwId) { 126 | hwId = Misc.GetCurrentHardwareId() 127 | } 128 | 129 | const post_data = { 130 | type: 'login', 131 | username, 132 | pass: password, 133 | hwid: hwId, 134 | sessionid: this.sessionid, 135 | name: this.name, 136 | ownerid: this.ownerId 137 | } 138 | 139 | const Json = await this.make_request(post_data) 140 | 141 | this.Load_Response_Struct(Json) 142 | if (Json.success && Json.success == true) { 143 | this.Load_User_Data(Json.info) 144 | return resolve(Json) 145 | } else { 146 | Misc.error(Json.message) 147 | } 148 | }) 149 | 150 | /** 151 | * Authenticate without using usernames and passwords 152 | * @param {string} [key] - Licence used to login with 153 | **/ 154 | license = (key) => new Promise(async (resolve) => { 155 | this.check_initialize() 156 | 157 | let hwId 158 | if (!hwId) { 159 | hwId = Misc.GetCurrentHardwareId() 160 | } 161 | 162 | const post_data = { 163 | type: 'license', 164 | key, 165 | hwid: hwId, 166 | sessionid: this.sessionid, 167 | name: this.name, 168 | ownerid: this.ownerId 169 | } 170 | 171 | const Json = await this.make_request(post_data) 172 | 173 | this.Load_Response_Struct(Json) 174 | if (Json.success && Json.success == true) { 175 | this.Load_User_Data(Json.info) 176 | return resolve(Json) 177 | } else { 178 | Misc.error(Json.message) 179 | } 180 | }) 181 | 182 | /** 183 | * Gives the user a subscription that has the same level as the key 184 | * @param {string} [username] - Username of the user thats going to get upgraded 185 | * @param {string} [license] - License with the same level as the subscription you want to give the user 186 | **/ 187 | upgrade = (username, license) => new Promise(async (resolve) => { 188 | this.check_initialize() 189 | 190 | const post_data = { 191 | type: 'upgrade', 192 | username, 193 | key: license, 194 | sessionid: this.sessionid, 195 | name: this.name, 196 | ownerid: this.ownerId 197 | } 198 | 199 | const Json = await this.make_request(post_data) 200 | 201 | this.Load_Response_Struct(Json) 202 | if (!Json.success || Json.success == false) { 203 | return resolve(Json.message) 204 | } else { 205 | // Don't let them yet for dashboard. 206 | Misc.error(Json.message) 207 | } 208 | }) 209 | 210 | /** 211 | * Gets an existing global variable 212 | * @param {string} [VarId] - Name of the variable / Variable ID 213 | * returns {string} - The value of the variable / The content of the variable 214 | **/ 215 | var = (VarId) => new Promise(async (resolve) => { 216 | this.check_initialize() 217 | 218 | const post_data = { 219 | type: 'var', 220 | varid: VarId, 221 | sessionid: this.sessionid, 222 | name: this.name, 223 | ownerid: this.ownerId 224 | } 225 | 226 | const Json = await this.make_request(post_data) 227 | 228 | this.Load_Response_Struct(Json) 229 | if (Json.success && Json.success == true) { 230 | return resolve(Json) 231 | } 232 | 233 | resolve(Json.message) 234 | }) 235 | 236 | /** 237 | * Gets the an existing user variable 238 | * @Param {string} [VarId] - User Variable Name 239 | * returns {string} - The value of the variable / The content of the user variable 240 | **/ 241 | GetVar = (VarId) => new Promise(async (resolve) => { 242 | this.check_initialize() 243 | 244 | const post_data = { 245 | type: 'getvar', 246 | var: VarId, 247 | sessionid: this.sessionid, 248 | name: this.name, 249 | ownerid: this.ownerId 250 | } 251 | 252 | const Json = await this.make_request(post_data) 253 | 254 | this.Load_Response_Struct(Json) 255 | if (Json.success && Json.success == true) { 256 | return resolve(Json) 257 | } 258 | 259 | resolve(Json.message) 260 | }) 261 | 262 | /** 263 | * Change the data of an existing user variable, *User must be logged in* 264 | * @Param {string} [VarId] - User variable name 265 | * @Param {string} [VarData] - The content of the variable 266 | **/ 267 | SetVar = (VarId, VarData) => new Promise(async (resolve) => { 268 | this.check_initialize() 269 | 270 | const post_data = { 271 | type: 'setvar', 272 | var: VarId, 273 | data: VarData, 274 | sessionid: this.sessionid, 275 | name: this.name, 276 | ownerid: this.ownerId 277 | } 278 | 279 | const Json = await this.make_request(post_data) 280 | 281 | this.Load_Response_Struct(Json) 282 | if (Json.success && Json.success == true) { 283 | return resolve(Json) 284 | } 285 | 286 | resolve(Json.message) 287 | }) 288 | 289 | /** 290 | * Bans the current logged in user 291 | **/ 292 | ban = () => new Promise(async (resolve) => { 293 | this.check_initialize() 294 | 295 | const post_data = { 296 | type: 'ban', 297 | sessionid: this.sessionid, 298 | name: this.name, 299 | ownerid: this.ownerId 300 | } 301 | 302 | const Json = await this.make_request(post_data) 303 | 304 | this.Load_Response_Struct(Json) 305 | if (Json.success && Json.success == true) { 306 | return resolve(true) 307 | } 308 | 309 | resolve(Json.message) 310 | }) 311 | 312 | /** 313 | * KeyAuth acts as proxy and downlods the file in a secure way 314 | * @Param {string} [fileId] - File ID 315 | * @Param {string} [path] - Path to save the file 316 | * @Param {boolean} [execute] - Execute the file after download - Windows Only Requires path for file! 317 | * returns {byte} - Returns The bytes of the download file 318 | **/ 319 | file = (fileId, path = null, execute = false) => new Promise(async (resolve) => { 320 | this.check_initialize() 321 | 322 | const post_data = { 323 | type: 'file', 324 | fileid: fileId.toString(), 325 | sessionid: this.sessionid, 326 | name: this.name, 327 | ownerid: this.ownerId 328 | } 329 | 330 | const Json = await this.make_request(post_data) 331 | 332 | this.Load_Response_Struct(Json) 333 | if (Json.success && Json.success == true) { 334 | 335 | if (path != null) { 336 | var bytes = await this.strToByteArray(Json.contents); 337 | fs.writeFile(path, bytes, async (err) => { 338 | if (err) throw err; 339 | 340 | if (execute) { 341 | var exec = require('child_process').exec; 342 | await exec(path, function (error, stdout, stderr) { 343 | if (error) { 344 | console.error(error); 345 | return; 346 | } 347 | }); 348 | 349 | return resolve(true); 350 | } else { 351 | return resolve(true); 352 | } 353 | }); 354 | } else { 355 | return resolve(this.strToByteArray(Json.contents)) 356 | } 357 | } 358 | 359 | resolve(Json.message) 360 | }) 361 | 362 | 363 | /** 364 | * Sends a request to a webhook that you've added in the dashboard in a safe way without it being showed for example a http debugger 365 | * @Param {string} [webId] - Webhook ID 366 | * @Param {string} [Params] - Webhook Parameters 367 | * @Param {string} [message] - Body of the request, empty by default 368 | * @Param {string} [username] - Content type, empty by default 369 | * Returns {string} - Returns the response of the webhook 370 | **/ 371 | webhook = (webId, Params, body = '', contType = '') => new Promise(async (resolve) => { // havent tested 372 | this.check_initialize() 373 | 374 | const post_data = { 375 | type: 'webhook', 376 | webid: webId, 377 | params: Params, 378 | body, 379 | conttype: contType, 380 | sessionid: this.sessionid, 381 | name: this.name, 382 | ownerid: this.ownerId 383 | } 384 | 385 | const Json = await this.make_request(post_data) 386 | 387 | this.Load_Response_Struct(Json) 388 | if (Json.success && Json.success == true) { 389 | return resolve(Json.response) 390 | } 391 | 392 | resolve(Json.message) 393 | }) 394 | 395 | /** 396 | * Check if the current session is validated or not 397 | * Returns {string} - Returns if the session is valid or not 398 | **/ 399 | check = () => new Promise(async (resolve) => { 400 | this.check_initialize() 401 | 402 | const post_data = { 403 | type: 'check', 404 | sessionid: this.sessionid, 405 | name: this.name, 406 | ownerid: this.ownerId 407 | } 408 | 409 | const Json = await this.make_request(post_data) 410 | 411 | this.Load_Response_Struct(Json) 412 | if (Json.success && Json.success == true) { 413 | return resolve(Json) 414 | }; 415 | 416 | resolve(Json.message) 417 | }) 418 | 419 | /** 420 | * Checks if the current IP Address/HardwareId is blacklisted 421 | * returns {boolean} - Returns true if the IP Address/HardwareId is blacklisted, otherwise false 422 | **/ 423 | checkBlack = () => new Promise(async (resolve) => { 424 | this.check_initialize() 425 | 426 | const hwId = Misc.GetCurrentHardwareId() 427 | 428 | const post_data = { 429 | type: 'checkblacklist', 430 | hwid: hwId, 431 | sessionid: this.sessionid, 432 | name: this.name, 433 | ownerid: this.ownerId 434 | } 435 | 436 | const Json = await this.make_request(post_data) 437 | 438 | this.Load_Response_Struct(Json) 439 | if (Json.success && Json.success == true) { 440 | return resolve(true) 441 | } 442 | 443 | resolve(false) 444 | }) 445 | 446 | /** 447 | * Fetch usernames of online users 448 | * Returns {array} - Returns an array of usernames 449 | **/ 450 | fetchOnline = () => new Promise(async (resolve) => { 451 | this.check_initialize() 452 | 453 | const post_data = { 454 | type: 'fetchOnline', 455 | sessionid: this.sessionid, 456 | name: this.name, 457 | ownerid: this.ownerId 458 | } 459 | 460 | const Json = await this.make_request(post_data) 461 | 462 | this.Load_Response_Struct(Json) 463 | if (Json.success && Json.success == true) { 464 | return resolve(Json.users) 465 | } else { 466 | return resolve(Json.message) 467 | } 468 | }) 469 | 470 | /** 471 | * Gets the last 20 sent messages of that channel 472 | * @param {string} [ChannelName] - The name of the channel, where you want the messages 473 | * Returns {array} the last 20 sent messages of that channel 474 | **/ 475 | ChatGet = (ChannelName) => new Promise(async (resolve) => { 476 | this.check_initialize() 477 | 478 | const post_data = { 479 | type: 'chatget', 480 | channel: ChannelName, 481 | sessionid: this.sessionid, 482 | name: this.name, 483 | ownerid: this.ownerId 484 | } 485 | 486 | const Json = await this.make_request(post_data) 487 | 488 | this.Load_Response_Struct(Json) 489 | if (Json.success && Json.success == true) { 490 | if (Json.messages[0].message == 'not_found') { 491 | return resolve([]) 492 | } else { 493 | return resolve(Json.messages) 494 | } 495 | } else { 496 | return resolve([]) 497 | } 498 | }) 499 | 500 | /** 501 | * Sends a message to the given channel name 502 | * @param {string} [ChannelName] - Channel Name where the message will be sent to 503 | * @param {string} [Message] - Message what will be sent to [ChannelName] 504 | * Returns {bool} - Returns true if the message was sent, otherwise false 505 | **/ 506 | ChatSend = (ChannelName, Message) => new Promise(async (resolve) => { 507 | this.check_initialize() 508 | 509 | const post_data = { 510 | type: 'fetchOnline', 511 | message: Message, 512 | channel: ChannelName, 513 | sessionid: this.sessionid, 514 | name: this.name, 515 | ownerid: this.ownerId 516 | } 517 | 518 | const Json = await this.make_request(post_data) 519 | 520 | this.Load_Response_Struct(Json) 521 | if (Json.success && Json.success == true) { 522 | return resolve(true) 523 | } else { 524 | return resolve(false) 525 | } 526 | }) 527 | 528 | /** 529 | * Logs the IP address,PC Name with a message, if a discord webhook is set up in the app settings, the log will get sent there and the dashboard if not set up it will only be in the dashboard 530 | * @param {string} [message] - Message / Discord Embed Title Message 531 | * Returns None 532 | **/ 533 | log = (message) => new Promise(async (resolve) => { 534 | this.check_initialize() 535 | 536 | const post_data = { 537 | type: 'log', 538 | pcuser: os.userInfo().username, 539 | message, 540 | sessionid: this.sessionid, 541 | name: this.name, 542 | ownerid: this.ownerId 543 | } 544 | 545 | await this.make_request(post_data) 546 | resolve(true) 547 | }) 548 | 549 | 550 | strToByteArray = (hex) => new Promise(async (resolve) => { 551 | try { 552 | const numberChars = hex.length; 553 | const bytes = new Uint8Array(numberChars / 2); 554 | for (let i = 0; i < numberChars; i += 2) { 555 | bytes[i / 2] = parseInt(hex.substr(i, 2), 16); 556 | } 557 | resolve(bytes) 558 | } catch (err) { 559 | console.error('The session has ended, open program again.'); 560 | process.exit(0); 561 | } 562 | }) 563 | 564 | /** 565 | * Check if the current session is initialized 566 | * @returns [true] if client is Initialized. 567 | **/ 568 | check_initialize() { 569 | if (!this.initialized) { 570 | Misc.error('You must initialize the API before using it!') 571 | } 572 | return true 573 | }; 574 | 575 | /** 576 | * Load the response struct for Response of Request 577 | **/ 578 | Load_Response_Struct(data) { 579 | this.response = { 580 | success: data.success, 581 | message: data.message 582 | } 583 | }; 584 | 585 | /** 586 | * Load the response struct for User Data 587 | **/ 588 | Load_User_Data(data) { 589 | this.user_data = { 590 | username: data.username, 591 | ip: data.ip, 592 | hwid: data.hwid, 593 | createdate: data.createdate, 594 | lastlogin: data.lastlogin, 595 | subscriptions: data.subscriptions 596 | } 597 | }; 598 | 599 | /** 600 | * Change Console Application Title 601 | * @param {string} [title] - Your new Title for the App 602 | * Returns Promise Timeout 603 | **/ 604 | setTitle(title) { 605 | process.stdout.write( 606 | String.fromCharCode(27) + ']0;' + title + String.fromCharCode(7) 607 | ) 608 | }; 609 | 610 | /** 611 | * Sleeping / Timeout Function 612 | * @param {number} [ms] - Time in milliseconds 613 | * Returns Promise Timeout 614 | **/ 615 | sleep(ms) { 616 | return new Promise((resolve) => { 617 | setTimeout(resolve, ms) 618 | }) 619 | }; 620 | 621 | /** 622 | * Request the API with the POST Data 623 | * @param {string} [data] - Post Data Array 624 | * Returns {array} - Returns the API Response [NON-ENCRYPTED] 625 | **/ 626 | make_request(data) { 627 | const startTime = Date.now(); // Start the stopwatch 628 | 629 | return new Promise(async (resolve) => { 630 | const request = await axios({ 631 | method: 'POST', 632 | url: 'https://keyauth.win/api/1.1/', 633 | data: new URLSearchParams(data).toString() 634 | }).catch((err) => { 635 | Misc.error(err) 636 | }) 637 | 638 | const endTime = Date.now(); // Stop the stopwatch 639 | 640 | this.responseTime = `${endTime - startTime} ms`; 641 | 642 | if (request && request.data) { 643 | resolve(request.data) 644 | } else { 645 | resolve(null) 646 | }; 647 | }) 648 | } 649 | 650 | 651 | } 652 | 653 | class Misc { 654 | /** 655 | * Get the current user HardwareId 656 | * @returns {string} - Returns user HardwareID 657 | **/ 658 | static GetCurrentHardwareId() { 659 | if (os.platform() != 'win32') return false 660 | 661 | const cmd = execSync('wmic useraccount where name="%username%" get sid').toString('utf-8') 662 | 663 | const system_id = cmd.split('\n')[1].trim() 664 | return system_id 665 | }; 666 | 667 | /** 668 | * Error Print Function 669 | * @param {string} [message] - Message to Show and then exit app. 670 | **/ 671 | static error(message) { 672 | console.log(message) 673 | return process.exit(0) 674 | } 675 | } 676 | 677 | /** 678 | * Export KeyAuth Class to be used in other files 679 | **/ 680 | module.exports = KeyAuth 681 | --------------------------------------------------------------------------------