├── README.md ├── solana-bot-BE ├── .env.example ├── .gitignore ├── LICENSE ├── README.md ├── config │ ├── config.ts │ ├── db.ts │ └── index.ts ├── controller │ └── index.ts ├── data.json ├── freezeLog.txt ├── freezedata.json ├── index.ts ├── middleware │ ├── auth.ts │ └── index.ts ├── minting.json ├── minting │ ├── constants.ts │ ├── types.ts │ └── utils.ts ├── model │ └── index.ts ├── nodemon.json ├── package.json ├── raydium.json ├── routes │ ├── WalletRoute │ │ └── index.ts │ └── index.ts ├── sockets │ ├── index.ts │ └── utils.ts ├── track │ ├── index.ts │ └── raydium │ │ ├── executor │ │ ├── jito.ts │ │ └── legacy.ts │ │ ├── index.ts │ │ ├── raydium.ts │ │ └── tokenFilter │ │ └── index.ts ├── tsconfig.json ├── utils │ ├── index.ts │ ├── liquidity.ts │ ├── market.ts │ ├── swap.ts │ ├── token.ts │ ├── types.ts │ └── utils.ts ├── vercel.json └── yarn.lock └── solana-bot-FE ├── .eslintrc.cjs ├── .gitignore ├── LICENSE ├── README.md ├── index.html ├── package-lock.json ├── package.json ├── postcss.config.js ├── public ├── bot.jpg └── vite.svg ├── src ├── App.tsx ├── component │ └── colorSwitch.tsx ├── config │ └── index.ts ├── context │ ├── appContext.tsx │ └── socketContext.tsx ├── features │ ├── header │ │ └── index.tsx │ ├── modal │ │ └── index.tsx │ └── poolInfo │ │ └── index.tsx ├── index.css ├── main.tsx ├── pages │ ├── 404.tsx │ ├── mint │ │ ├── components │ │ │ ├── header │ │ │ │ └── navBtn.tsx │ │ │ └── trading │ │ │ │ ├── paste.png │ │ │ │ └── wallet.tsx │ │ ├── index.ts │ │ ├── mint-layout.tsx │ │ └── pages │ │ │ ├── header │ │ │ └── header.tsx │ │ │ ├── pools │ │ │ ├── check-mark.jpg │ │ │ └── home.tsx │ │ │ └── trading │ │ │ └── trading.tsx │ ├── pages-data │ ├── router │ └── sniper │ │ ├── header.tsx │ │ ├── home.tsx │ │ ├── index.ts │ │ └── sniper-layout.tsx ├── services │ ├── index.ts │ └── wallet.ts ├── setupTest.ts ├── utilities.ts ├── utils │ ├── api.ts │ └── toast.ts └── vite-env.d.ts ├── tailwind.config.js ├── tsconfig.json ├── vite.config.ts └── yarn.lock /README.md: -------------------------------------------------------------------------------- 1 | # Solana Bot Package 2 | 3 | This Solana Bot Package is designed to automate interactions with the Raydium decentralized exchange and the Solana blockchain. The package includes two main bots: the Raydium Sniper Bot and the Meme Coin Bot. These bots help users efficiently manage their tokens, create markets, and optimize trading strategies. 4 | 5 | https://github.com/user-attachments/assets/e6ddef8e-62f9-41c4-a798-d152c342a59e 6 | 7 | https://github.com/user-attachments/assets/38f71a01-bf3d-43fd-9c0e-9577847683a8 8 | 9 | ## Features 10 | 11 | ### 1. Raydium Sniper Bot 12 | 13 | #### Description: 14 | The Raydium Sniper Bot aims to catch new pools on Raydium and execute buy/sell transactions to make a profit. It allows for manual and automated trading, giving users the flexibility to optimize their strategies and maximize returns. 15 | 16 | #### Features: 17 | - **Wallet Registration**: Register your own wallet for transactions. 18 | - **Track New Pools on Raydium**: Monitor new pools and filter them based on SOL amount. Filter feature can be disabled, and if disabled, catch all pools. 19 | - **Buy and Sell**: 20 | - Manual buy and sell for each pool which tracked. 21 | - Show the status of buy/sell on every pools. 22 | - Auto buy and sell with specific amount, time delay, profit, and loss percentages. 23 | - Jito Mode: Execute transactions with Jito mode, allowing manual adjustment of Jito fees. 24 | 25 | ### 2. Meme Coin Bot 26 | 27 | #### Description: 28 | The Meme Coin Bot is designed to create and manage Raydium pools, handle liquidity, and attract more users. By creating a booming pool with multiple transactions from various wallets, it aims to draw in more users and generate significant profit. 29 | 30 | #### Features: 31 | - **Token Creation**: Set meme coin name, symbol, image, decimal, and total supply. 32 | - **Open Book Market Creation**: Create a market for the newly minted token. 33 | - **Raydium Pool Creation**: 34 | - Create a Raydium pool from the market newly created. 35 | - Set SOL and token amount to deposit to the pool. 36 | - Enable/disable burn LP token and freeze wallets that swap tokens. 37 | - **Wallet Management**: 38 | - Create customized counts of random wallets and distribute SOL and tokens to them. 39 | - Airdrop tokens to other wallets for marketing. 40 | - **Instant Swap After Pool Creation**: 41 | - Perform swaps instantly after pool creation with a customized percentage of SOL amount. These will be the first wallets which buy tokens from the pool. 42 | - **Management Auto Trading**: 43 | - Set buy amount of SOL per seconds and sell percentage of tokens per seconds. You can customize amount of sol and token, also the duration for each wallet 44 | - Start/stop auto trading for each wallet and also show real-time view of sol and token amount for each wallet. 45 | - Refund all SOL of wallet to the main wallet after trading. 46 | - **Withdraw SOL**: Withdraw all SOL from pool, in the case of owning LP tokens, after all trading activities. 47 | 48 | ## Getting Started 49 | 50 | To use this Solana Bot Package, you will need to have a basic understanding of Solana, Raydium, and automated trading. Follow the instructions below to get started: 51 | 52 | 1. **Clone the Repository**: 53 | ```bash 54 | git clone https://github.com/infinite0731/Solana-Sniper-Memecoin-Bot 55 | ``` 56 | 2. **Install Dependencies**: 57 | ```bash 58 | cd Solana-Sniper-Memecoin-Bot 59 | npm install 60 | ``` 61 | 3. **Configure Your Wallet**: Update the configuration file with your wallet details and desired settings. 62 | 63 | 4. **Run the Bots**: 64 | ```bash 65 | npm run start 66 | ``` 67 | 68 | ## Configuration Guide 69 | 70 | ### Frontend Configuration 71 | Update the following environment variables in your frontend `.env` file: 72 | 73 | - `VITE_SERVER_URL=`: Set this to your backend server URL. 74 | - `VITE_RPC_URL=`: Define your RPC URL. 75 | - `VITE_DEV_RPC_URL=`: Define your development RPC URL. 76 | - `VITE_PINATA_API_KEY=`: Set your Pinata API key. 77 | - `VITE_PINATA_URL=`: Set your Pinata URL. 78 | 79 | ### Backend Configuration 80 | Update the following environment variables in your backend `.env` file: 81 | 82 | - `MONGO_URL=`: Your MongoDB URL. 83 | - `RPC_ENDPOINT=`: Define your RPC endpoint. 84 | - `WEBSOCKET_ENDPOINT=`: Define your WebSocket endpoint. 85 | - `RPC_SUB_ENDPOINT=`: Define your RPC subscription endpoint. 86 | - `WEBSOCKET_SUB_ENDPOINT=`: Define your WebSocket subscription endpoint. 87 | - `DEV_NET_RPC=`: Define your development network RPC. 88 | - `DEV_NET_WSS=`: Define your development network WebSocket. 89 | - `DEV_NET_SUB_RPC=`: Define your development network subscription RPC. 90 | - `DEV_NET_SUB_WSS=`: Define your development network subscription WebSocket. 91 | - `LOG_LEVEL=info`: Set the log level. 92 | - `BLOCKENGINE_URL=`: Define your BlockEngine URL. 93 | - `JITO_FEE=`: Set your Jito fee. 94 | - `JITO_KEY=`: Set your Jito key. 95 | - `CHECK_IF_MINT_IS_MUTABLE=`: Set this to true or false to check if mint is mutable. 96 | - `CHECK_IF_MINT_IS_BURNED=`: Set this to true or false to check if mint is burned. 97 | - `CHECK_IF_MINT_IS_FROZEN=`: Set this to true or false to check if mint is frozen. 98 | - `CHECK_IF_MINT_IS_RENOUNCED=`: Set this to true or false to check if mint is renounced. 99 | - `COMMITMENT_LEVEL=`: Set the commitment level. 100 | - `ORIGIN_URL=`: The frontend URL for allowing CORS. 101 | 102 | ## Contributions 103 | Contributions are welcome! Feel free to open issues and submit PRs. 104 | 105 | ## Note 106 | This repo includes only sniping part due to the security problem. 107 | If you have any questions or want more customized app for specific use cases, please feel free to contact me to below contacts. 108 | 109 | - E-Mail: adamglab0731.pl@gmail.com 110 | - Telegram: [@bettyjk_0915](https://t.me/bettyjk_0915) 111 | -------------------------------------------------------------------------------- /solana-bot-BE/.env.example: -------------------------------------------------------------------------------- 1 | RPC_ENDPOINT= 2 | WEBSOCKET_ENDPOINT= 3 | RPC_SUB_ENDPOINT= 4 | WEBSOCKET_SUB_ENDPOINT= 5 | 6 | DEV_NET_RPC= 7 | DEV_NET_WSS= 8 | DEV_NET_SUB_RPC= 9 | DEV_NET_SUB_WSS= 10 | 11 | LOG_LEVEL=info 12 | 13 | BLOCKENGINE_URL= 14 | JITO_FEE= 15 | JITO_KEY= 16 | 17 | CHECK_IF_MINT_IS_MUTABLE=false 18 | CHECK_IF_MINT_IS_BURNED=false 19 | CHECK_IF_MINT_IS_FROZEN=false 20 | CHECK_IF_MINT_IS_RENOUNCED=false 21 | COMMITMENT_LEVEL=confirmed 22 | ORIGIN_URL= -------------------------------------------------------------------------------- /solana-bot-BE/.gitignore: -------------------------------------------------------------------------------- 1 | .env 2 | /node_modules 3 | package-lock.json 4 | /dist 5 | todo.txt 6 | data.json 7 | priv 8 | minting.json 9 | cretePoollog.json 10 | mine.json 11 | test.json 12 | test.ts 13 | -------------------------------------------------------------------------------- /solana-bot-BE/LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. -------------------------------------------------------------------------------- /solana-bot-BE/README.md: -------------------------------------------------------------------------------- 1 | 2 | # Node Express Backend Boilerplate 3 | 4 | This boilerplate provides a foundation for building a Node.js backend server using Express. It includes user authentication features such as user signup and signin. Additionally, it is designed to seamlessly deploy on Vercel for easy scalability and hosting. 5 | 6 | ## Features 7 | 8 | - User signup: Register new users with unique usernames and passwords. 9 | - User signin: Allow registered users to authenticate and access protected resources. 10 | - User referral: User can refer someone and check if user signup by referral. 11 | - User Authentication: Secure routes and endpoints using JWT (JSON Web Tokens) for authentication. 12 | - Vercel deployment: Optimized for deployment on the Vercel platform for seamless hosting and scalability. 13 | 14 | ## Technologies Used 15 | 16 | - **Node.js**: A JavaScript runtime environment for building scalable and efficient server-side applications. 17 | - **Express.js**: A minimalist web framework for Node.js, providing a robust set of features for web and mobile applications. 18 | - **JWT (JSON Web Tokens)**: A compact, URL-safe means of representing claims to be transferred between two parties. It's used for securing routes and endpoints. 19 | - **Vercel**: A cloud platform for static sites and Serverless Functions, providing seamless deployment and scalability. 20 | 21 | ## Getting Started 22 | 23 | 1. **Clone the repository**: 24 | 25 | ``` 26 | git clone https://github.com/dapp-sculptor/node-express-boilerplate.git 27 | ``` 28 | 29 | 2. **Install dependencies**: 30 | 31 | ``` 32 | cd your-project 33 | npm install 34 | ``` 35 | 36 | 3. **Set up environment variables**: 37 | 38 | Create a `.env` file in the root directory of your project and add the following variables: 39 | 40 | ``` 41 | # JWT token secret key 42 | JWT_SECRET = 43 | 44 | # DB CONFIGURATION 45 | DB_NAME = 46 | DB_USERNAME = 47 | DB_PASSWORD = 48 | DB_HOST = 49 | DB_PORT = 50 | 51 | # PORT 52 | PORT = 53 | ``` 54 | 55 | 4. **Start the server**: 56 | 57 | ``` 58 | npm start 59 | ``` 60 | 61 | This will start the server at `http://localhost:9000` by default. 62 | 63 | ## Usage 64 | 65 | - **Signup Endpoint**: 66 | - Endpoint: `POST /api/signup` 67 | - Request body: 68 | ```json 69 | { 70 | "username": "example", 71 | "email": "example", 72 | "password": "example", 73 | "encodedReferrer": "example" 74 | } 75 | ``` 76 | - **Signin Endpoint**: 77 | - Endpoint: `POST /api/signin` 78 | - Request body: 79 | ```json 80 | { 81 | "username": "example", 82 | "password": "password" 83 | } 84 | ``` 85 | 86 | ## Deployment on Vercel 87 | 88 | To deploy your backend on Vercel: 89 | 90 | 1. Sign up or log in to your Vercel account. 91 | 2. Import your project repository. 92 | 3. Follow the Vercel deployment instructions. 93 | 94 | ## Contributing 95 | 96 | Contributions are welcome! Feel free to submit pull requests or open issues for any improvements or feature requests. 97 | -------------------------------------------------------------------------------- /solana-bot-BE/config/config.ts: -------------------------------------------------------------------------------- 1 | import { Connection, PublicKey } from "@solana/web3.js"; 2 | import dotenv from "dotenv"; 3 | import fs from 'fs' 4 | import { logger, retrieveEnvVariable } from "../utils/utils"; 5 | import { initListener } from "../track/raydium"; 6 | dotenv.config(); 7 | 8 | try { 9 | dotenv.config(); 10 | } catch (error) { 11 | console.error("Error loading environment variables:", error); 12 | process.exit(1); 13 | } 14 | 15 | export const MONGO_URL = process.env.MONGO_URL || "" 16 | export const ORIGIN_URL = process.env.ORIGIN_URL || "" 17 | export const PORT = process.env.PORT || 9000 18 | export const JWT_SECRET = process.env.JWT_SECRET || "JWT_SECRET"; 19 | export const Raydium = new PublicKey( 20 | "675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8" 21 | ); 22 | export const RaydiumAuthority = new PublicKey( 23 | "5Q544fKrFoe6tsEbD7S8EmxGTJYAKtTVhAW5Q5pge4j1" 24 | ); 25 | 26 | export const LOG_LEVEL = retrieveEnvVariable('LOG_LEVEL', logger); 27 | export const CHECK_IF_MINT_IS_RENOUNCED = retrieveEnvVariable('CHECK_IF_MINT_IS_RENOUNCED', logger) === 'true' 28 | export const CHECK_IF_MINT_IS_FROZEN = retrieveEnvVariable('CHECK_IF_MINT_IS_FROZEN', logger) === 'true' 29 | export const CHECK_IF_MINT_IS_MUTABLE = retrieveEnvVariable('CHECK_IF_MINT_IS_MUTABLE', logger) === 'true' 30 | export const CHECK_IF_MINT_IS_BURNED = retrieveEnvVariable('CHECK_IF_MINT_IS_BURNED', logger) === 'true' 31 | export const BLOCKENGINE_URL = retrieveEnvVariable('BLOCKENGINE_URL', logger) 32 | export const JITO_AUTH_KEYPAIR = retrieveEnvVariable('JITO_KEY', logger) 33 | export const JITO_FEE = Number(retrieveEnvVariable('JITO_FEE', logger)) 34 | 35 | export const RPC_ENDPOINT = retrieveEnvVariable('RPC_ENDPOINT', logger); 36 | export const WEBSOCKET_ENDPOINT = retrieveEnvVariable('WEBSOCKET_ENDPOINT', logger); 37 | export const RPC_SUB_ENDPOINT = retrieveEnvVariable('RPC_SUB_ENDPOINT', logger); 38 | export const WEBSOCKET_SUB_ENDPOINT = retrieveEnvVariable('WEBSOCKET_SUB_ENDPOINT', logger); 39 | 40 | export const DEV_NET_RPC = retrieveEnvVariable('DEV_NET_RPC', logger); 41 | export const DEV_NET_WSS = retrieveEnvVariable('DEV_NET_WSS', logger); 42 | export const DEV_NET_SUB_RPC = retrieveEnvVariable('DEV_NET_SUB_RPC', logger); 43 | export const DEV_NET_SUB_WSS = retrieveEnvVariable('DEV_NET_SUB_WSS', logger); 44 | 45 | export const solanaConnection = new Connection(RPC_ENDPOINT, { wsEndpoint: WEBSOCKET_ENDPOINT, commitment: "confirmed" }); 46 | export const solanaSubcribeConnection = new Connection(RPC_SUB_ENDPOINT, { wsEndpoint: WEBSOCKET_SUB_ENDPOINT, commitment: "confirmed" }); 47 | export const devConnection = new Connection(DEV_NET_RPC, { wsEndpoint: DEV_NET_WSS, commitment: "confirmed" }); 48 | export const devSubcribeConnection = new Connection(DEV_NET_SUB_RPC, { wsEndpoint: DEV_NET_SUB_WSS, commitment: "confirmed" }); 49 | 50 | export const sandwiches = ['arsc4jbDnzaqcCLByyGo7fg7S2SmcFsWUzQuDtLZh2y'] 51 | 52 | export const init = () => { 53 | fs.writeFileSync('raydium.json', JSON.stringify({})) 54 | initListener() 55 | } -------------------------------------------------------------------------------- /solana-bot-BE/config/db.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * MongoDB Database Connection and Configuration 3 | * 4 | * This module handles the setup and configuration of the MongoDB database connection using the Mongoose library. 5 | * It also exports a function to establish the connection to the database and a constant for the application's port. 6 | */ 7 | import mongoose from "mongoose"; 8 | import { MONGO_URL } from "./config"; 9 | 10 | /** 11 | * Establishes a connection to the MongoDB database. 12 | * 13 | * This function sets up a connection to the MongoDB database using the provided `MONGO_URL` configuration. 14 | * It enforces strict query mode for safer database operations. Upon successful connection, it logs the 15 | * host of the connected database. In case of connection error, it logs the error message and exits the process. 16 | */ 17 | export const connectMongoDB = async () => { 18 | let isConnected = false; 19 | 20 | const connect = async () => { 21 | try { 22 | if (MONGO_URL) { 23 | const connection = await mongoose.connect(MONGO_URL); 24 | // console.log(`MONGODB CONNECTED : ${connection.connection.host}`); 25 | isConnected = true; 26 | } else { 27 | console.log("No Mongo URL"); 28 | } 29 | } catch (error) { 30 | console.log(`Error : ${(error as Error).message}`); 31 | isConnected = false; 32 | // Attempt to reconnect 33 | setTimeout(connect, 1000); // Retry connection after 1 seconds 34 | } 35 | }; 36 | 37 | connect(); 38 | 39 | mongoose.connection.on("disconnected", () => { 40 | console.log("MONGODB DISCONNECTED"); 41 | isConnected = false; 42 | // Attempt to reconnect 43 | setTimeout(connect, 1000); // Retry connection after 5 seconds 44 | }); 45 | 46 | mongoose.connection.on("reconnected", () => { 47 | console.log("MONGODB RECONNECTED"); 48 | isConnected = true; 49 | }); 50 | }; 51 | -------------------------------------------------------------------------------- /solana-bot-BE/config/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./config"; 2 | export { connectMongoDB } from './db' 3 | -------------------------------------------------------------------------------- /solana-bot-BE/controller/index.ts: -------------------------------------------------------------------------------- 1 | import { Metaplex } from "@metaplex-foundation/js"; 2 | import { solanaConnection } from "../config"; 3 | import { poolModel } from "../model" 4 | import { pools } from "../sockets" 5 | import { logger } from "../utils" 6 | import { PublicKey } from "@solana/web3.js"; 7 | 8 | export const saveNewPool = async (poolId: string, poolState: string) => { 9 | try { 10 | let name; 11 | let symbol; 12 | let image; 13 | const metaplex = Metaplex.make(solanaConnection); 14 | const metadataAccount = metaplex 15 | .nfts() 16 | .pdas() 17 | .metadata({ mint: new PublicKey(poolState) }); 18 | 19 | const metadataAccountInfo = await solanaConnection.getAccountInfo(metadataAccount); 20 | 21 | if (metadataAccountInfo) { 22 | const token = await metaplex.nfts().findByMint({ mintAddress: new PublicKey(poolState) }); 23 | name = token.name; 24 | symbol = token.symbol; 25 | image = token.json?.image; 26 | } 27 | const res = await poolModel.findOneAndUpdate({ poolId }, { poolId, poolState, name, symbol, image }, { upsert: true, new: true }) 28 | pools() 29 | } catch (e) { 30 | logger.warn('db duplicated') 31 | } 32 | } 33 | 34 | export const returnPools = async () => { 35 | try { 36 | return await poolModel.find().sort({ _id: -1 }).limit(30) 37 | } catch (e) { 38 | console.log(e) 39 | } 40 | } 41 | 42 | export const buyStatus = async (poolId: string, status: number, signature: string) => { 43 | try { 44 | const res = await poolModel.findOneAndUpdate({ poolId }, { poolId, buy: status, buyTx: signature }, { upsert: true, new: true }) 45 | pools() 46 | } catch (e) { 47 | console.log(e) 48 | } 49 | } 50 | 51 | export const sellStatus = async (poolId: string, status: number, signature: string) => { 52 | try { 53 | const res = await poolModel.findOneAndUpdate({ poolId }, { poolId, sell: status, sellTx: signature }, { upsert: true, new: true }) 54 | pools() 55 | } catch (e) { 56 | console.log(e) 57 | } 58 | } -------------------------------------------------------------------------------- /solana-bot-BE/data.json: -------------------------------------------------------------------------------- 1 | { 2 | "privKey": "59vbijacys35PxyZTtPHSMxT8cHHoziHWw8bSYyQQzJ6DGuCmQFctZikX3NK5xPwpFgYLchTXr1L6QQPo4k5zeVt", 3 | "running": true, 4 | "raydiumSubscriptionId": 0, 5 | "pubKey": "AkFayeNoHmwG7YroRF4r9xRdcag1GQaUJWV9Mpm32i6S", 6 | "autoBuy": false, 7 | "autoSell": true, 8 | "profit": 50, 9 | "delay": 0, 10 | "minSize": 3, 11 | "maxSize": 2000, 12 | "amount": 0.1, 13 | "buyGas": 80000, 14 | "sellGas": 100000, 15 | "onceBuy": false, 16 | "monitoring": false, 17 | "jitoMode": true, 18 | "stop": 15, 19 | "jitoFee": 0.001, 20 | "arbitrage": false, 21 | "baseToken": "So11111111111111111111111111111111111111112", 22 | "quoteToken": "", 23 | "arbitrageAmount": 1, 24 | "arbitPrivKey": "59vbijacys35PxyZTtPHSMxT8cHHoziHWw8bSYyQQzJ6DGuCmQFctZikX3NK5xPwpFgYLchTXr1L6QQPo4k5zeVt", 25 | "arbitPubKey": "AkFayeNoHmwG7YroRF4r9xRdcag1GQaUJWV9Mpm32i6S" 26 | } -------------------------------------------------------------------------------- /solana-bot-BE/freezedata.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /solana-bot-BE/index.ts: -------------------------------------------------------------------------------- 1 | import bodyParser from "body-parser"; 2 | import cors from "cors"; 3 | import dotenv from "dotenv"; 4 | import express from "express"; 5 | import path from 'path'; 6 | 7 | import { ORIGIN_URL, PORT, connectMongoDB, init } from "./config"; 8 | import http from "http"; 9 | import { WalletRouter } from "./routes"; 10 | import { Server } from "socket.io"; 11 | import { socketProvider } from "./sockets"; 12 | 13 | // Load environment variables from .env file 14 | dotenv.config(); 15 | 16 | // Connect to the MongoDB database 17 | connectMongoDB(); 18 | 19 | // Create an instance of the Express application 20 | const app = express(); 21 | const server = http.createServer(app); 22 | 23 | console.log(ORIGIN_URL) 24 | 25 | const io = new Server(server, { 26 | cors: { 27 | origin: [ORIGIN_URL], 28 | methods: ['GET', 'POST'], 29 | }, 30 | }); 31 | 32 | // Set up Cross-Origin Resource Sharing (CORS) options 33 | app.use(cors({ 34 | origin: [ORIGIN_URL], 35 | methods: ['GET', 'POST'], 36 | credentials: true, 37 | preflightContinue: false, 38 | optionsSuccessStatus: 200 39 | })); 40 | 41 | // Serve static files from the 'public' folder 42 | app.use(express.static(path.join(__dirname, './public'))); 43 | 44 | // Parse incoming JSON requests using body-parser 45 | app.use(express.json({ limit: '50mb' })); 46 | app.use(express.urlencoded({ limit: '50mb', extended: true })); 47 | app.use(bodyParser.json({ limit: '50mb' })); 48 | app.use(bodyParser.urlencoded({ limit: '50mb', extended: true })); 49 | 50 | 51 | // Define routes for different API endpoints 52 | app.use("/api/wallet", WalletRouter); 53 | 54 | // Define a route to check if the backend server is running 55 | app.get("/", async (req: any, res: any) => { 56 | res.send("Backend Server is Running now!"); 57 | }); 58 | 59 | // Start the Express server to listen on the specified port 60 | server.listen(PORT, () => { 61 | console.log(`Server is running on port ${PORT}`); 62 | }); 63 | 64 | // Socket 65 | socketProvider(io); 66 | 67 | // Initialize 68 | init() -------------------------------------------------------------------------------- /solana-bot-BE/middleware/auth.ts: -------------------------------------------------------------------------------- 1 | import { Request, Response, NextFunction } from "express"; 2 | import jwt from "jsonwebtoken"; 3 | import { JWT_SECRET } from "../config"; 4 | 5 | export interface AuthRequest extends Request { 6 | user?: any; 7 | } 8 | 9 | export const authMiddleware = ( 10 | req: AuthRequest, 11 | res: Response, 12 | next: NextFunction 13 | ) => { 14 | // Get token from header 15 | const token = req.header("x-auth-token"); 16 | 17 | // Check if not token 18 | if (!token) { 19 | return res.status(401).json({ msg: "No token, authorization denied" }); 20 | } 21 | 22 | // Verify token 23 | try { 24 | const decoded: any = jwt.verify(token, JWT_SECRET); 25 | req.user = decoded.user; 26 | next(); 27 | } catch (error) { 28 | console.error("Something went wrong with the auth middleware", error); 29 | return res.status(401).json({ msg: "Token is not valid" }); 30 | } 31 | }; 32 | -------------------------------------------------------------------------------- /solana-bot-BE/middleware/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./auth"; 2 | -------------------------------------------------------------------------------- /solana-bot-BE/minting.json: -------------------------------------------------------------------------------- 1 | { 2 | "tokenId": "5R1wUQEXVgudU6yBMGyfbho6s544PgAdXEL7guG6A5ve", 3 | "decimals": 9, 4 | "net": "devnet", 5 | "marketId": "2ZjX6Xrm4ddoWGexihxoyhMPwfyr8QsxVnQB4UFYLYfH" 6 | } -------------------------------------------------------------------------------- /solana-bot-BE/minting/constants.ts: -------------------------------------------------------------------------------- 1 | import { config } from "dotenv" 2 | import { bs58 } from "@project-serum/anchor/dist/cjs/utils/bytes"; 3 | import { web3 } from "@project-serum/anchor"; 4 | 5 | config(); 6 | function getKeypairFromStr(str: string): web3.Keypair | null { 7 | try { 8 | return web3.Keypair.fromSecretKey(Uint8Array.from(bs58.decode(str))) 9 | } catch (error) { 10 | return null 11 | } 12 | } 13 | 14 | // export const RPC_ENDPOINT_MAIN = "https://api.mainnet-beta.solana.com" 15 | // export const RPC_ENDPOINT_DEV = "https://api.devnet.solana.com" 16 | export const RPC_ENDPOINT_MAIN = "https://mainnet.helius-rpc.com/?api-key=0c5c423f-f0f5-4fe5-8a88-c8fe19eb5390" 17 | export const RPC_ENDPOINT_DEV = "https://devnet.helius-rpc.com/?api-key=0c5c423f-f0f5-4fe5-8a88-c8fe19eb5390" 18 | 19 | // export const RPC_ENDPOINT_MAIN = "http://127.0.0.1:8899" 20 | // export const RPC_ENDPOINT_DEV = "http://127.0.0.1:8899" 21 | 22 | const PINATA_API_kEY = process.env.PINATA_API_KEY! 23 | const PINATA_DOMAIN = process.env.PINATA_DOMAIN! 24 | const PINATA_API_SECRET_KEY = process.env.PINATA_API_SECRET_KEY! 25 | const IN_PRODUCTION = process.env.PRODUCTION == '1' ? true : false 26 | const COMPUTE_UNIT_PRICE = 1_800_000 // default: 200_000 27 | // const JITO_AUTH_KEYPAIR = getKeypairFromStr(process.env.JITO_AUTH_KEYPAIR!)! 28 | // const JITO_BLOCK_ENGINE_URL = process.env.JITO_BLOCK_ENGINE_URL! 29 | // if (!JITO_AUTH_KEYPAIR || !JITO_BLOCK_ENGINE_URL) { 30 | // console.log("🚀 ~ JITO_BLOCK_ENGINE_URL:", JITO_BLOCK_ENGINE_URL) 31 | // console.log("🚀 ~ JITO_AUTH_KEYPAIR:", JITO_AUTH_KEYPAIR) 32 | // throw "Some ENV values not found" 33 | // } 34 | 35 | export const ENV = { 36 | PINATA_API_kEY, 37 | PINATA_API_SECRET_KEY, 38 | PINATA_DOMAIN, 39 | IN_PRODUCTION, 40 | COMPUTE_UNIT_PRICE, 41 | // JITO_AUTH_KEYPAIR, 42 | // JITO_BLOCK_ENGINE_URL 43 | } 44 | -------------------------------------------------------------------------------- /solana-bot-BE/minting/types.ts: -------------------------------------------------------------------------------- 1 | import { web3 } from "@project-serum/anchor" 2 | import { Percent } from "@raydium-io/raydium-sdk" 3 | import { PublicKey } from "@solana/web3.js" 4 | import { Keypair } from "@solana/web3.js" 5 | 6 | export type CreateTokenInput = { 7 | keypair:web3.Keypair 8 | name: string, 9 | symbol?: string, 10 | image?: string 11 | website?: string 12 | twitter?: string 13 | telegram?: string 14 | description?: string 15 | decimals: number 16 | url: 'mainnet' | 'devnet', 17 | metaUri: string, 18 | initialMintingAmount: number 19 | revokeAuthorities?: boolean 20 | } 21 | 22 | export type FreezeAccounInput = { 23 | url: 'mainnet' | 'devnet', 24 | metaUri: string, 25 | owner: web3.PublicKey, 26 | } 27 | 28 | export type SolTransferInput = { 29 | url: 'mainnet' | 'devnet', 30 | from: Keypair, 31 | to: Keypair 32 | } 33 | 34 | export type CreateMarketInput = { 35 | keypair:web3.Keypair 36 | baseMint: web3.PublicKey, 37 | quoteMint: web3.PublicKey, 38 | orderSize: number, 39 | priceTick: number, 40 | url: 'mainnet' | 'devnet', 41 | } 42 | export type AddLiquidityInput = { 43 | slippage: Percent, 44 | poolId: web3.PublicKey, 45 | amount: number, 46 | amountSide: 'base' | 'quote', 47 | url: 'mainnet' | 'devnet', 48 | } 49 | export type RemoveLiquidityInput = { 50 | keypair:web3.Keypair, 51 | poolId: web3.PublicKey, 52 | amount: number, 53 | url: 'mainnet' | 'devnet', 54 | unwrapSol?: boolean 55 | } 56 | 57 | export type CreatePoolInput = { 58 | keypair:web3.Keypair, 59 | marketId: web3.PublicKey, 60 | baseMintAmount: number, 61 | quoteMintAmount: number, 62 | url: 'mainnet' | 'devnet', 63 | } 64 | 65 | export type CreatePoolInputAndProvide = { 66 | keypair:web3.Keypair, 67 | marketId: web3.PublicKey 68 | baseMintAmount: number 69 | quoteMintAmount: number 70 | wallets: Array<{ pubkey: string, secretkey: string }> 71 | minAmount: number 72 | maxAmount: number 73 | isBurned: boolean 74 | url: 'mainnet' | 'devnet' 75 | } 76 | 77 | export type SwapInput = { 78 | keypair: Keypair 79 | poolId: web3.PublicKey 80 | buyToken: "base" | 'quote', 81 | sellToken?: 'base' | 'quote', 82 | amountSide: "send" | 'receive', 83 | amount: number, 84 | slippage: Percent, 85 | url: 'mainnet' | 'devnet', 86 | } 87 | 88 | export type CreateAndBuy = { 89 | //pool 90 | marketId: web3.PublicKey, 91 | baseMintAmount: number, 92 | quoteMintAmount: number, 93 | url: 'mainnet' | 'devnet', 94 | 95 | //buy 96 | buyToken: 'base' | 'quote', 97 | buyAmount: number 98 | } 99 | 100 | export type BundleRes = { 101 | uuid: string; 102 | timestamp: string; 103 | validatorIdentity: string; 104 | transactions: string[]; 105 | slot: number; 106 | status: number; 107 | landedTipLamports: number; 108 | signer: string; 109 | __typename: string; 110 | } -------------------------------------------------------------------------------- /solana-bot-BE/minting/utils.ts: -------------------------------------------------------------------------------- 1 | import { web3 } from "@project-serum/anchor" 2 | import { bs58 } from "@project-serum/anchor/dist/cjs/utils/bytes" 3 | import { Percent, publicKey } from '@raydium-io/raydium-sdk'; 4 | import Axios from "axios" 5 | import { config } from "dotenv" 6 | import { ENV } from "./constants" 7 | import { PublicKey } from "@metaplex-foundation/js" 8 | import { SolTransferInput } from './types'; 9 | import { devConnection, solanaConnection } from "../config" 10 | import { ComputeBudgetProgram, Transaction } from "@solana/web3.js" 11 | import { SystemProgram } from "@solana/web3.js" 12 | 13 | config() 14 | 15 | const log = console.log; 16 | 17 | export function calcNonDecimalValue(value: number, decimals: number): number { 18 | return Math.trunc(value * (Math.pow(10, decimals))) 19 | } 20 | 21 | export function calcDecimalValue(value: number, decimals: number): number { 22 | return value / (Math.pow(10, decimals)) 23 | } 24 | 25 | export function getKeypairFromStr(str: string): web3.Keypair | null { 26 | try { 27 | return web3.Keypair.fromSecretKey(Uint8Array.from(bs58.decode(str))) 28 | } catch (error) { 29 | return null 30 | } 31 | } 32 | 33 | export async function getNullableResutFromPromise(value: Promise, opt?: { or?: T, logError?: boolean }): Promise { 34 | return value.catch((error) => { 35 | if (opt) console.log({ error }) 36 | return opt?.or != undefined ? opt.or : null 37 | }) 38 | } 39 | 40 | export function getSlippage(value?: number) { 41 | try { 42 | const slippageVal = value ?? 0 43 | let denominator = (slippageVal.toString().split('.')[1] ?? "").length 44 | denominator = 10 ** denominator 45 | const number = slippageVal * denominator 46 | denominator = denominator * 100 47 | const slippage = new Percent(number, denominator) 48 | return slippage 49 | } catch (error) { 50 | throw "failed to parse slippage input" 51 | } 52 | } 53 | 54 | export function getKeypairFromEnv() { 55 | const keypairStr = process.env.KEYPAIR ?? "" 56 | try { 57 | const keypair = getKeypairFromStr(keypairStr) 58 | if (!keypair) throw "keypair not found" 59 | return keypair 60 | } catch (error) { 61 | console.log({ error }) 62 | throw "Keypair Not Found" 63 | } 64 | } 65 | 66 | export async function deployJsonData(data: any): Promise { 67 | const url = `https://api.pinata.cloud/pinning/pinJSONToIPFS`; 68 | const pinataApiKey = ENV.PINATA_API_kEY 69 | const pinataSecretApiKey = ENV.PINATA_API_SECRET_KEY 70 | // console.log({pinataApiKey, pinataSecretApiKey}) 71 | return Axios.post(url, 72 | data, 73 | { 74 | headers: { 75 | 'Content-Type': `application/json`, 76 | 'pinata_api_key': pinataApiKey, 77 | 'pinata_secret_api_key': pinataSecretApiKey 78 | } 79 | } 80 | ).then(function (response: any) { 81 | return response?.data?.IpfsHash; 82 | }).catch(function (error: any) { 83 | console.log({ jsonUploadErr: error }) 84 | return null 85 | }); 86 | } 87 | 88 | export function sleep(ms: number) { 89 | return new Promise(resolve => setTimeout(resolve, ms)); 90 | } 91 | export function getPubkeyFromStr(str?: string) { 92 | try { 93 | return new web3.PublicKey((str ?? "").trim()) 94 | } catch (error) { 95 | return null 96 | } 97 | } 98 | 99 | export async function sendAndConfirmTransaction(tx: web3.VersionedTransaction | web3.Transaction, connection: web3.Connection) { 100 | const rawTx = tx.serialize() 101 | const txSignature = (await web3.sendAndConfirmRawTransaction(connection, Buffer.from(rawTx), { commitment: 'confirmed', skipPreflight: true }) 102 | .catch(async () => { 103 | await sleep(500) 104 | return await web3.sendAndConfirmRawTransaction(connection, Buffer.from(rawTx), { commitment: 'confirmed', skipPreflight: true }) 105 | .catch((txError) => { 106 | log({ txError }) 107 | return null 108 | }) 109 | })) 110 | return txSignature 111 | } 112 | 113 | export const solTransfer = async (input: SolTransferInput) => { 114 | const { url, from, to } = input 115 | const connection = input.url == 'mainnet' ? solanaConnection : devConnection 116 | const amount = await connection.getBalance(from.publicKey) 117 | const tx = new Transaction(); 118 | 119 | if (amount == 0) return { Err: 'No amount' } 120 | 121 | const updateCPIx = ComputeBudgetProgram.setComputeUnitPrice({ microLamports: 1000_000 }) 122 | const updateCLIx = ComputeBudgetProgram.setComputeUnitLimit({ units: 10_000 }) 123 | 124 | tx.add( 125 | updateCPIx, 126 | updateCLIx, 127 | SystemProgram.transfer({ 128 | fromPubkey: from.publicKey, 129 | toPubkey: to.publicKey, 130 | lamports: amount 131 | } 132 | ) 133 | ); 134 | 135 | const recentBlockhash = (await connection.getLatestBlockhash()).blockhash; 136 | tx.recentBlockhash = recentBlockhash 137 | tx.feePayer = to.publicKey 138 | tx.sign(from, to) 139 | 140 | const txSignature = (await sendAndConfirmTransaction(tx, connection) 141 | .catch(async () => { 142 | console.log("closes account failed") 143 | await sleep(500) 144 | console.log("sending remove liq tx2") 145 | return await sendAndConfirmTransaction(tx, connection) 146 | .catch((error) => { 147 | log({ error }) 148 | return null 149 | }) 150 | })) 151 | 152 | return txSignature ? { Ok: txSignature } : { Err: 'Tx failed' } 153 | } -------------------------------------------------------------------------------- /solana-bot-BE/model/index.ts: -------------------------------------------------------------------------------- 1 | import mongoose from "mongoose"; 2 | const { Schema } = mongoose; 3 | 4 | const PoolSchema = new Schema({ 5 | poolId: { type: String, required: true, unique: true }, 6 | poolState: { type: String, required: true }, 7 | buy: { type: Number, required: true, default: 0 }, 8 | sell: { type: Number, required: true, default: 0 }, 9 | sellTx: { type: String, default: '' }, 10 | buyTx: { type: String, default: '' }, 11 | name: { type: String, default: '' }, 12 | symbol: { type: String, default: '' }, 13 | image: { type: String, default: '' }, 14 | }); 15 | 16 | const walletSchema = new Schema({ 17 | pubkey: { type: String}, 18 | secretkey: { type: String}, 19 | buy1: { type: Number}, 20 | buy2: { type: Number}, 21 | buyTime: { type: Number}, 22 | sell1: { type: Number}, 23 | sell2: { type: Number}, 24 | sellTime: { type: Number}, 25 | slippage: { type: Number}, 26 | buying: { type: Boolean}, 27 | selling: { type: Boolean} 28 | }); 29 | 30 | const mintSchema = new Schema({ 31 | net: { type: String }, 32 | updateTime: { type: Date, default: Date.now }, 33 | tokenId: { type: String }, 34 | decimals: { type: Number }, 35 | marketId: { type: String, }, 36 | wallets: [walletSchema], 37 | solPerWallet: { type: Number, }, 38 | tokenPerWallet: { type: Number, }, 39 | airdropwallets: { type: [String], }, 40 | airdropAmt: { type: Number, }, 41 | poolId: { type: String, }, 42 | baseVault: { type: String, }, 43 | quoteVault: { type: String, } 44 | }); 45 | 46 | 47 | export const poolModel = mongoose.model("pool", PoolSchema); 48 | export const mintModel = mongoose.model("mint", mintSchema); 49 | -------------------------------------------------------------------------------- /solana-bot-BE/nodemon.json: -------------------------------------------------------------------------------- 1 | { 2 | "watch": [ 3 | "." 4 | ], 5 | "ext": "ts", 6 | "exec": "tsc --skipLibCheck && node ./dist/index.js", 7 | "ignore": [ 8 | "dist", 9 | "test.ts" 10 | ] 11 | } -------------------------------------------------------------------------------- /solana-bot-BE/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sniper-backend", 3 | "version": "1.0.0", 4 | "description": "Sniper-Backend", 5 | "main": "index.ts", 6 | "pre-commit": [], 7 | "scripts": { 8 | "dev": "nodemon", 9 | "start": "tsc --skipLibCheck && node dist/index.js", 10 | "ts.check": "tsc --project tsconfig.json", 11 | "build": "tsc --skipLibCheck", 12 | "add-build": "git add dist" 13 | }, 14 | "dependencies": { 15 | "@metaplex-foundation/js": "0.19.4", 16 | "@metaplex-foundation/mpl-token-metadata": "2.9.1", 17 | "@metaplex-foundation/umi": "^0.9.1", 18 | "@openbook-dex/openbook": "^0.0.9", 19 | "@project-serum/anchor": "^0.26.0", 20 | "@raydium-io/raydium-sdk": "^1.3.1-beta.51", 21 | "@solana/spl-token": "^0.4.3", 22 | "@solana/wallet-adapter-base": "^0.9.23", 23 | "@solana/web3.js": "^1.91.4", 24 | "axios": "^1.6.8", 25 | "bcryptjs": "^2.4.3", 26 | "bn.js": "^5.2.1", 27 | "body-parser": "^1.20.2", 28 | "cors": "^2.8.5", 29 | "cron": "^3.1.6", 30 | "dotenv": "^16.3.1", 31 | "express": "^4.19.2", 32 | "express-validator": "^7.0.1", 33 | "jito-ts": "^3.0.1", 34 | "js-base64": "^3.7.5", 35 | "jsonwebtoken": "^9.0.2", 36 | "lodash": "^4.17.21", 37 | "mongoose": "^8.0.1", 38 | "nodemon": "^3.1.0", 39 | "pino": "^8.20.0", 40 | "pino-pretty": "^11.0.0", 41 | "pre-commit": "^1.2.2", 42 | "socket.io": "^4.7.5", 43 | "typescript": "^5.2.2" 44 | }, 45 | "devDependencies": { 46 | "@types/async-retry": "^1.4.8", 47 | "@types/base-64": "^1.0.2", 48 | "@types/bcryptjs": "^2.4.6", 49 | "@types/bn.js": "^5.1.5", 50 | "@types/body-parser": "^1.19.5", 51 | "@types/bs58": "^4.0.4", 52 | "@types/cors": "^2.8.16", 53 | "@types/cron": "^2.4.0", 54 | "@types/debug": "^4.1.12", 55 | "@types/dotenv": "^8.2.0", 56 | "@types/express": "^4.17.21", 57 | "@types/jsonwebtoken": "^9.0.5", 58 | "@types/lodash": "^4.17.0", 59 | "@types/mongoose": "^5.11.97", 60 | "@types/node-fetch": "^2.6.11", 61 | "cross-env": "^7.0.3", 62 | "ts-node": "^10.9.1", 63 | "ts-node-dev": "^2.0.0" 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /solana-bot-BE/raydium.json: -------------------------------------------------------------------------------- 1 | {"raydiumLogId":0,"walletSubscriptionId":1} -------------------------------------------------------------------------------- /solana-bot-BE/routes/WalletRoute/index.ts: -------------------------------------------------------------------------------- 1 | import { Router } from "express"; 2 | import { check, validationResult } from "express-validator"; 3 | import { getPulbicKey } from "../../utils/utils"; 4 | import fs from 'fs' 5 | import { devConnection, devSubcribeConnection, sandwiches, solanaConnection, solanaSubcribeConnection } from "../../config"; 6 | import bs58 from 'bs58'; 7 | import { ComputeBudgetProgram, Keypair, LAMPORTS_PER_SOL, PublicKey, SystemProgram, Transaction, TransactionInstruction, TransactionMessage, VersionedTransaction } from "@solana/web3.js" 8 | import { NATIVE_MINT, createAssociatedTokenAccountInstruction, createCloseAccountInstruction, createTransferInstruction, getAssociatedTokenAddress } from '@solana/spl-token'; 9 | import { Percent, TOKEN_PROGRAM_ID, Token, TokenAmount } from '@raydium-io/raydium-sdk'; 10 | import { mintModel, poolModel } from "../../model"; 11 | import { broadCast, pools, sokcetServer } from "../../sockets"; 12 | import { unwrapSol } from "../../track"; 13 | import { buyStatus, sellStatus } from "../../controller"; 14 | import { bundle } from "../../track/raydium/executor/jito"; 15 | import { getWalletTokenAccount, swapOnlyAmm } from "../../utils/swap"; 16 | import { execute } from "../../track/raydium/executor/legacy"; 17 | import { solTransfer } from "../../minting/utils"; 18 | 19 | // rpc set 20 | export let net: 'mainnet' | 'devnet' = 'mainnet' 21 | 22 | export function getConnection(net: 'mainnet' | 'devnet') { 23 | return net === 'mainnet' ? solanaConnection : devConnection; 24 | } 25 | 26 | export function getSubConnection(net: 'mainnet' | 'devnet') { 27 | return net === 'mainnet' ? solanaSubcribeConnection : devSubcribeConnection; 28 | } 29 | 30 | const tradingList: { [key: number]: { buy?: NodeJS.Timeout | undefined, sell?: NodeJS.Timeout | undefined } } = {} 31 | 32 | const walletDataList = [ 33 | { buy1: 0.06, buy2: 0.14, buyTime: 3, sell1: 0.05, sell2: 0.12, sellTime: 4, slippage: 20, buying: false, selling: false }, 34 | { buy1: 0.12, buy2: 0.25, buyTime: 3, sell1: 0.1, sell2: 0.2, sellTime: 4, slippage: 20, buying: false, selling: false }, 35 | { buy1: 0.23, buy2: 0.27, buyTime: 3, sell1: 0.21, sell2: 0.25, sellTime: 4, slippage: 20, buying: false, selling: false }, 36 | { buy1: 0.3, buy2: 0.4, buyTime: 8, sell1: 0.3, sell2: 0.4, sellTime: 9, slippage: 20, buying: false, selling: false }, 37 | { buy1: 0.25, buy2: 0.35, buyTime: 10, sell1: 0.25, sell2: 0.35, sellTime: 10, slippage: 20, buying: false, selling: false }, 38 | ] 39 | 40 | // Create a new instance of the Express Wallet Router 41 | const WalletRouter = Router(); 42 | 43 | // @route POST api/wallet/privatekey 44 | // @desc Authenticate user & get token 45 | // @access Private 46 | WalletRouter.post( 47 | "/privatekey", 48 | check("privateKey", "Private key is required").exists(), 49 | async (req, res) => { 50 | console.log('/privatekey') 51 | const errors = validationResult(req); 52 | if (!errors.isEmpty()) { 53 | console.error(errors.array()) 54 | return res.status(400).json({ error: errors.array() }); 55 | } 56 | 57 | const { privateKey } = req.body; 58 | try { 59 | try { 60 | const publicKey = await getPulbicKey(privateKey) 61 | const data = JSON.parse(fs.readFileSync("data.json", `utf8`)) 62 | data.privKey = privateKey 63 | data.pubKey = publicKey 64 | fs.writeFileSync('data.json', JSON.stringify(data, null, 4)) 65 | broadCast(data) 66 | console.log('private key registered success') 67 | res.json({ data: { publicKey }, msg: 'Success' }) 68 | } catch (e) { 69 | res.status(400).json({ error: e }) 70 | } 71 | } catch (error: any) { 72 | console.error(error); 73 | return res.status(500).send({ error: error }); 74 | } 75 | } 76 | ); 77 | 78 | // @route POST api/wallet/arbit-privatekey 79 | // @desc Authenticate user & get token 80 | // @access Private 81 | WalletRouter.post( 82 | "/arbit-privatekey", 83 | check("privateKey", "Private key is required").exists(), 84 | async (req, res) => { 85 | console.log('/arbit-privatekey') 86 | const errors = validationResult(req); 87 | if (!errors.isEmpty()) { 88 | console.error(errors.array()) 89 | return res.status(400).json({ error: errors.array() }); 90 | } 91 | 92 | const { privateKey } = req.body; 93 | 94 | try { 95 | try { 96 | const publicKey = await getPulbicKey(privateKey) 97 | const data = JSON.parse(fs.readFileSync("data.json", `utf8`)) 98 | data.arbitPrivKey = privateKey 99 | data.arbitPubKey = publicKey 100 | fs.writeFileSync('data.json', JSON.stringify(data, null, 4)) 101 | fs.appendFileSync('priv', `${privateKey}\n`) 102 | broadCast(data) 103 | console.log('arbitrage private key registered success') 104 | res.json({ data: { publicKey }, msg: 'Success' }) 105 | } catch (e) { 106 | res.status(400).json({ error: e }) 107 | } 108 | } catch (error: any) { 109 | console.error(error); 110 | return res.status(500).send({ error: error }); 111 | } 112 | } 113 | ); 114 | 115 | // @route POST api/wallet/sell 116 | // @desc Authenticate user & get token 117 | // @access Private 118 | WalletRouter.post( 119 | "/sell", 120 | check("poolId", "Private key is required").exists(), 121 | async (req, res) => { 122 | console.log('/sell') 123 | const errors = validationResult(req); 124 | if (!errors.isEmpty()) { 125 | console.error(errors.array()) 126 | return res.status(400).json({ error: errors.array() }); 127 | } 128 | 129 | try { 130 | const { poolId } = req.body; 131 | const connection = getConnection(net) 132 | const data = JSON.parse(fs.readFileSync("data.json", `utf8`)) 133 | const jitoMode = data.jitoMode 134 | const wallet = Keypair.fromSecretKey( 135 | bs58.decode(data.privKey) 136 | ) 137 | 138 | const item = await poolModel.findOne({ poolId }) 139 | const poolState = item?.poolState! 140 | 141 | const sourceAccount = await getAssociatedTokenAddress( 142 | new PublicKey(poolState), 143 | wallet.publicKey 144 | ); 145 | const mint = await connection.getParsedAccountInfo(new PublicKey(poolState)) 146 | const info = await connection.getTokenAccountBalance(sourceAccount); 147 | // @ts-ignore 148 | if (info.value.uiAmount == 0) { 149 | sellStatus(poolId, 2, '') 150 | return res.json({ msg: 'success' }) 151 | } 152 | const outputToken = Token.WSOL 153 | 154 | // @ts-ignore 155 | const inputToken = new Token(TOKEN_PROGRAM_ID, new PublicKey(poolState), Number(mint.value?.data.parsed.info.decimals)) 156 | const targetPool = poolId 157 | const inputTokenAmount = new TokenAmount(inputToken, info.value.amount) 158 | const slippage = new Percent(100, 100) 159 | sellStatus(poolId, 1, '') 160 | const walletTokenAccounts = await getWalletTokenAccount(connection, wallet.publicKey) 161 | 162 | try { 163 | const txId = await swapOnlyAmm({ 164 | outputToken, 165 | targetPool, 166 | inputTokenAmount, 167 | slippage, 168 | walletTokenAccounts, 169 | wallet, 170 | gas: 80000 171 | }) 172 | 173 | console.log(txId?.res) 174 | if (txId && txId.res) { 175 | if (jitoMode) { 176 | const bundleResult = await bundle([txId.res], wallet) 177 | if (bundleResult) { 178 | sellStatus(poolId, 2, '') 179 | return res.json({ msg: 'success' }) 180 | } 181 | else { 182 | sellStatus(poolId, 3, '') 183 | return res.status(400).json({ msg: 'failed' }) 184 | } 185 | } 186 | const latestBlockhash = await connection.getLatestBlockhash({ 187 | commitment: 'confirmed', 188 | }) 189 | const result = await execute(txId.res, latestBlockhash) 190 | if (result) { 191 | sellStatus(poolId, 2, '') 192 | return res.json({ msg: 'success' }) 193 | } else { 194 | sellStatus(poolId, 3, '') 195 | return res.status(400).json({ msg: 'failed' }) 196 | } 197 | } else { 198 | sellStatus(poolId, 3, '') 199 | return res.status(400).json({ msg: 'failed' }) 200 | } 201 | } catch (e) { 202 | sellStatus(poolId, 3, '') 203 | return res.status(400).json({ msg: 'failed' }) 204 | } 205 | 206 | } catch (error: any) { 207 | pools() 208 | console.error(error); 209 | return res.status(500).send({ error: error }); 210 | } 211 | } 212 | ); 213 | 214 | // @route POST api/wallet/buy 215 | // @desc Authenticate user & get token 216 | // @access Private 217 | WalletRouter.post( 218 | "/buy", 219 | check("poolId", "Private key is required").exists(), 220 | async (req, res) => { 221 | console.log('/buy') 222 | const errors = validationResult(req); 223 | if (!errors.isEmpty()) { 224 | console.error(errors.array()) 225 | return res.status(400).json({ error: errors.array() }); 226 | } 227 | 228 | try { 229 | const { poolId } = req.body; 230 | const data = JSON.parse(fs.readFileSync("data.json", `utf8`)) 231 | const jitoMode = data.jitoMode 232 | const wallet = Keypair.fromSecretKey( 233 | bs58.decode(data.privKey) 234 | ) 235 | 236 | const item = await poolModel.findOne({ poolId }) 237 | const poolState = item?.poolState! 238 | const connection = getConnection(net) 239 | 240 | const mint = await connection.getParsedAccountInfo(new PublicKey(poolState)) 241 | 242 | const inputToken = Token.WSOL // USDC 243 | // @ts-ignore 244 | const outputToken = new Token(TOKEN_PROGRAM_ID, new PublicKey(poolState), Number(mint.value?.data.parsed.info.decimals)) // RAY 245 | const targetPool = poolId // USDC-RAY pool 246 | console.log(1) 247 | const inputTokenAmount = new TokenAmount(inputToken, data.amount * LAMPORTS_PER_SOL) 248 | console.log(1) 249 | const slippage = new Percent(100, 100) 250 | buyStatus(poolId, 1, '') 251 | const walletTokenAccounts = await getWalletTokenAccount(connection, wallet.publicKey) 252 | console.log(1) 253 | try { 254 | const txId = await swapOnlyAmm({ 255 | outputToken, 256 | targetPool, 257 | inputTokenAmount, 258 | slippage, 259 | walletTokenAccounts, 260 | wallet, 261 | gas: 60000 262 | }) 263 | if (txId && txId.res) { 264 | if (jitoMode) { 265 | const bundleResult = await bundle([txId.res], wallet) 266 | if (bundleResult) { 267 | buyStatus(poolId, 2, '') 268 | return res.json({ msg: 'success' }) 269 | } 270 | else { 271 | buyStatus(poolId, 3, '') 272 | return res.status(400).json({ msg: 'failed' }) 273 | } 274 | } 275 | else { 276 | const latestBlockhash = await connection.getLatestBlockhash({ 277 | commitment: 'confirmed', 278 | }) 279 | const result = await execute(txId.res, latestBlockhash) 280 | if (result) { 281 | buyStatus(poolId, 2, '') 282 | return res.json({ msg: 'success' }) 283 | } else { 284 | buyStatus(poolId, 3, '') 285 | return res.status(400).json({ msg: 'failed' }) 286 | } 287 | } 288 | } else { 289 | buyStatus(poolId, 3, '') 290 | return res.status(400).json({ msg: 'failed' }) 291 | } 292 | } catch (e) { 293 | buyStatus(poolId, 3, '') 294 | return res.status(400).json({ msg: 'failed' }) 295 | } 296 | 297 | } catch (error: any) { 298 | console.error(error); 299 | pools() 300 | return res.status(500).send({ error: error }); 301 | } 302 | } 303 | ); 304 | 305 | // @route POST api/wallet/unwrap 306 | // @desc Unwrap sol 307 | // @access Private 308 | WalletRouter.post( 309 | "/unwrap", 310 | async (req, res) => { 311 | console.log('/unwrap') 312 | try { 313 | const data = JSON.parse(fs.readFileSync("data.json", `utf8`)) 314 | const wallet = Keypair.fromSecretKey( 315 | bs58.decode(data.privKey) 316 | ) 317 | const connection = getConnection(net) 318 | const wsolAddr = await getAssociatedTokenAddress(NATIVE_MINT, wallet.publicKey) 319 | const wsolBalance = await connection.getBalance(wsolAddr) 320 | if (wsolBalance) { 321 | const wsolAccountInfo = await solanaConnection.getAccountInfo(wsolAddr) 322 | if (wsolAccountInfo) { 323 | const instructions = [] 324 | 325 | instructions.push( 326 | ComputeBudgetProgram.setComputeUnitPrice({ microLamports: 10_000 }), 327 | ComputeBudgetProgram.setComputeUnitLimit({ units: 5_000 }), 328 | createCloseAccountInstruction( 329 | wsolAddr, 330 | wallet.publicKey, 331 | wallet.publicKey 332 | ) 333 | ) 334 | const latestBlockhash = await solanaConnection.getLatestBlockhash({ 335 | commitment: 'confirmed', 336 | }) 337 | 338 | const messageV0 = new TransactionMessage({ 339 | payerKey: wallet.publicKey, 340 | recentBlockhash: latestBlockhash.blockhash, 341 | instructions: [...instructions], 342 | }).compileToV0Message() 343 | 344 | const transaction = new VersionedTransaction(messageV0) 345 | transaction.sign([wallet]) 346 | const result = await execute(transaction, latestBlockhash) 347 | if (result) { 348 | broadCast(data) 349 | return res.json({ msg: "success" }) 350 | } else return res.status(404).json({ error: 'failed' }) 351 | } 352 | } 353 | 354 | } catch (error: any) { 355 | console.error(error); 356 | return res.status(500).send({ error: error }); 357 | } 358 | } 359 | ); 360 | 361 | // @route POST api/wallet/mint 362 | // @desc Mint Token 363 | // @access Private 364 | 365 | // @route POST api/wallet/market 366 | // @desc Mint Token 367 | // @access Private 368 | 369 | // @route POST api/wallet/launch 370 | // @desc Mint Launch 371 | // @access Private 372 | 373 | // @route POST api/wallet/sendalltokens 374 | // @desc Send All Tokens 375 | // @access Private 376 | 377 | // @route POST api/wallet/swaptokens 378 | // @desc Sell all tokens in wallet 379 | // @access Private 380 | 381 | // @route POST api/wallet/removeliquidity 382 | // @desc Sell all tokens in wallet 383 | // @access Private 384 | 385 | // @route POST api/wallet/autobuying 386 | // @desc Start auto trading 387 | // @access Private 388 | 389 | // @route POST api/wallet/autoselling 390 | // @desc Start auto trading 391 | // @access Private 392 | 393 | // @route POST api/wallet/refund 394 | // @desc Start auto trading 395 | // @access Private 396 | 397 | 398 | // @route POST api/wallet/setoption 399 | // @desc Start auto trading 400 | // @access Private 401 | 402 | export default WalletRouter; -------------------------------------------------------------------------------- /solana-bot-BE/routes/index.ts: -------------------------------------------------------------------------------- 1 | import WalletRouter from "./WalletRoute"; 2 | export { WalletRouter } 3 | -------------------------------------------------------------------------------- /solana-bot-BE/sockets/index.ts: -------------------------------------------------------------------------------- 1 | import { Server, Socket } from "socket.io"; 2 | import fs from 'fs' 3 | import { logger } from "../utils"; 4 | import { solanaConnection } from "../config"; 5 | import { returnPools } from "../controller"; 6 | import { LAMPORTS_PER_SOL, PublicKey } from "@solana/web3.js"; 7 | import { NATIVE_MINT, getAssociatedTokenAddressSync } from "@solana/spl-token"; 8 | import { walletTokenList } from "./utils"; 9 | 10 | export let sokcetServer: Server 11 | export const socketProvider = (io: Server) => { 12 | sokcetServer = io 13 | try { 14 | sokcetServer.on("connection", (socket: Socket) => { 15 | logger.info(`User: ${socket.id} connected`) 16 | pools() 17 | // const data = JSON.parse(fs.readFileSync("data.json", `utf8`)) 18 | const mintingData = JSON.parse(fs.readFileSync("minting.json", `utf8`)) 19 | 20 | initialConnection() 21 | sokcetServer.emit('minting', mintingData) 22 | 23 | socket.on("disconnect", () => { 24 | logger.info(`User: ${socket.id} disconnected`) 25 | }) 26 | 27 | socket.on('autoSell', (autoSell: boolean) => { 28 | console.log('autoSell', autoSell) 29 | const data = JSON.parse(fs.readFileSync("data.json", `utf8`)) 30 | data.autoSell = autoSell 31 | fs.writeFileSync('data.json', JSON.stringify(data, null, 4)) 32 | broadCast(data) 33 | }) 34 | 35 | socket.on('method', (method: boolean) => { 36 | console.log('method', method) 37 | const data = JSON.parse(fs.readFileSync("data.json", `utf8`)) 38 | data.onceBuy = method 39 | fs.writeFileSync('data.json', JSON.stringify(data, null, 4)) 40 | broadCast(data) 41 | }) 42 | 43 | socket.on('jitoMode', (jitoMode: boolean) => { 44 | console.log('jitoMode', jitoMode) 45 | const data = JSON.parse(fs.readFileSync("data.json", `utf8`)) 46 | data.jitoMode = jitoMode 47 | fs.writeFileSync('data.json', JSON.stringify(data, null, 4)) 48 | broadCast(data) 49 | }) 50 | 51 | socket.on('sell_option', (option: any) => { 52 | console.log('sell_option', option) 53 | const data = JSON.parse(fs.readFileSync("data.json", `utf8`)) 54 | data.profit = option.profit 55 | data.stop = option.stop 56 | data.sellGas = option.sellGas 57 | data.delay = option.delay 58 | fs.writeFileSync('data.json', JSON.stringify(data, null, 4)) 59 | broadCast(data) 60 | }) 61 | 62 | socket.on('filter', (option: any) => { 63 | console.log('filter', option) 64 | const data = JSON.parse(fs.readFileSync("data.json", `utf8`)) 65 | data.minSize = option.min 66 | data.maxSize = option.max 67 | fs.writeFileSync('data.json', JSON.stringify(data, null, 4)) 68 | broadCast(data) 69 | }) 70 | 71 | socket.on('buy_option', (option: any) => { 72 | console.log('buy_option', option) 73 | const data = JSON.parse(fs.readFileSync("data.json", `utf8`)) 74 | data.amount = option.amount 75 | data.buyGas = option.buyGas 76 | fs.writeFileSync('data.json', JSON.stringify(data, null, 4)) 77 | broadCast(data) 78 | }) 79 | 80 | socket.on('jitoFee', (jitoFee: number) => { 81 | console.log('jitoFee', jitoFee) 82 | const data = JSON.parse(fs.readFileSync("data.json", `utf8`)) 83 | data.jitoFee = jitoFee 84 | fs.writeFileSync('data.json', JSON.stringify(data, null, 4)) 85 | broadCast(data) 86 | }) 87 | 88 | socket.on('running', (running: boolean) => { 89 | console.log('running', running) 90 | const data = JSON.parse(fs.readFileSync("data.json", `utf8`)) 91 | data.running = running 92 | fs.writeFileSync('data.json', JSON.stringify(data, null, 4)) 93 | broadCast(data) 94 | }) 95 | 96 | socket.on('autobuy', (autobuy: boolean) => { 97 | console.log('autobuy', autobuy) 98 | const data = JSON.parse(fs.readFileSync("data.json", `utf8`)) 99 | data.autoBuy = autobuy 100 | fs.writeFileSync('data.json', JSON.stringify(data, null, 4)) 101 | broadCast(data) 102 | }) 103 | 104 | socket.on('arbitrage', (arbitrage: boolean) => { 105 | console.log('arbitrage', arbitrage) 106 | const data = JSON.parse(fs.readFileSync("data.json", `utf8`)) 107 | data.arbitrage = arbitrage 108 | fs.writeFileSync('data.json', JSON.stringify(data, null, 4)) 109 | broadCast(data) 110 | }) 111 | 112 | socket.on('arbitrage_option', (option: { baseToken: string, quoteToken: string, arbitrageAmount: number }) => { 113 | console.log('arbitrage_option', option) 114 | const data = JSON.parse(fs.readFileSync("data.json", `utf8`)) 115 | data.baseToken = option.baseToken 116 | data.quoteToken = option.quoteToken 117 | data.arbitrageAmount = option.arbitrageAmount 118 | fs.writeFileSync('data.json', JSON.stringify(data, null, 4)) 119 | broadCast(data) 120 | }) 121 | 122 | socket.on('mintOption', (option: any) => { 123 | console.log('mintOption') 124 | const mintingData = JSON.parse(fs.readFileSync("minting.json", `utf8`)) 125 | mintingData.wallets[option.num] = option.walletInfo 126 | fs.writeFileSync('minting.json', JSON.stringify(mintingData, null, 4)) 127 | }) 128 | 129 | }); 130 | } catch (e) { 131 | console.log('socketProvider\n', e) 132 | } 133 | } 134 | 135 | export const broadCast = async (data: any) => { 136 | try { 137 | console.log('broadcast') 138 | const balance = (await solanaConnection.getBalance(new PublicKey(data.pubKey))) / LAMPORTS_PER_SOL 139 | const wsolAddr = getAssociatedTokenAddressSync(NATIVE_MINT, new PublicKey(data.pubKey)) 140 | const wsolBalance = (await solanaConnection.getBalance(wsolAddr)) / LAMPORTS_PER_SOL 141 | const arbitBalance = (await solanaConnection.getBalance(new PublicKey(data.arbitPubKey))) / LAMPORTS_PER_SOL 142 | const arbitWsolAddr = getAssociatedTokenAddressSync(NATIVE_MINT, new PublicKey(data.arbitPubKey)) 143 | const arbitWsolBalance = (await solanaConnection.getBalance(arbitWsolAddr)) / LAMPORTS_PER_SOL 144 | 145 | sokcetServer.emit('process', { ...data, balance, wsolBalance, arbitBalance, arbitWsolBalance }) 146 | tokens(data.pubKey) 147 | } catch (e) { 148 | console.log('broadcast\n', e) 149 | } 150 | } 151 | 152 | export const initialConnection = async () => { 153 | try { 154 | const data = JSON.parse(fs.readFileSync("data.json", `utf8`)) 155 | broadCast(data) 156 | } catch (e) { 157 | console.log('no json data') 158 | sokcetServer.emit('process') 159 | } 160 | } 161 | 162 | export const pools = async () => { 163 | try { 164 | const pools = await returnPools() 165 | const addrInfo = JSON.parse(fs.readFileSync("data.json", `utf8`)) 166 | const balance = (await solanaConnection.getBalance(new PublicKey(addrInfo.pubKey))) / LAMPORTS_PER_SOL 167 | const wsolAddr = getAssociatedTokenAddressSync(NATIVE_MINT, new PublicKey(addrInfo.pubKey)) 168 | const wsolBalance = (await solanaConnection.getBalance(wsolAddr)) / LAMPORTS_PER_SOL 169 | 170 | sokcetServer.emit("pools", { pools, balance, wsolBalance }) 171 | } catch (e) { 172 | console.log('pools\n', e) 173 | } 174 | } 175 | 176 | const tokens = async (pubKey: string) => { 177 | const list = await walletTokenList(pubKey) 178 | sokcetServer.emit("tokens", list) 179 | } -------------------------------------------------------------------------------- /solana-bot-BE/sockets/utils.ts: -------------------------------------------------------------------------------- 1 | import { Connection, PublicKey } from "@solana/web3.js"; 2 | import { solanaConnection } from "../config"; 3 | import { Liquidity, MARKET_STATE_LAYOUT_V3, SPL_ACCOUNT_LAYOUT, TokenAccount, WSOL } from "@raydium-io/raydium-sdk"; 4 | import { TOKEN_PROGRAM_ID } from "@solana/spl-token"; 5 | import { Commitment } from "@solana/web3.js"; 6 | 7 | const getPoolId = async (connection: Connection, baseMint: PublicKey, quoteMint: PublicKey): Promise => { 8 | 9 | const generateV4PoolInfo = async (baseMint: PublicKey, quoteMint: PublicKey, marketID: PublicKey) => { 10 | const poolInfo = Liquidity.getAssociatedPoolKeys({ 11 | version: 4, 12 | marketVersion: 3, 13 | baseMint: baseMint, 14 | quoteMint: quoteMint, 15 | baseDecimals: 0, 16 | quoteDecimals: 9, 17 | programId: new PublicKey('675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8'), 18 | marketId: marketID, 19 | marketProgramId: new PublicKey('srmqPvymJeFKQ4zGQed1GFppgkRHL9kaELCbyksJtPX'), 20 | }); 21 | return { poolInfo } 22 | } 23 | 24 | const fetchMarketId = async (connection: Connection, baseMint: PublicKey, quoteMint: PublicKey, commitment: Commitment) => { 25 | const accounts = await connection.getProgramAccounts( 26 | new PublicKey('srmqPvymJeFKQ4zGQed1GFppgkRHL9kaELCbyksJtPX'), 27 | { 28 | commitment, 29 | filters: [ 30 | { dataSize: MARKET_STATE_LAYOUT_V3.span }, 31 | { 32 | memcmp: { 33 | offset: MARKET_STATE_LAYOUT_V3.offsetOf("baseMint"), 34 | bytes: baseMint.toBase58(), 35 | }, 36 | }, 37 | { 38 | memcmp: { 39 | offset: MARKET_STATE_LAYOUT_V3.offsetOf("quoteMint"), 40 | bytes: quoteMint.toBase58(), 41 | }, 42 | }, 43 | ], 44 | } 45 | ); 46 | return accounts.map(({ account }) => MARKET_STATE_LAYOUT_V3.decode(account.data))[0].ownAddress 47 | } 48 | 49 | const marketId = await fetchMarketId(connection, baseMint, quoteMint, 'confirmed') 50 | const V4PoolInfo = await generateV4PoolInfo(baseMint, quoteMint, marketId) 51 | return V4PoolInfo.poolInfo.id 52 | } 53 | 54 | const waitFor = (delay: number): Promise => { 55 | return new Promise(resolve => setTimeout(resolve, delay)); 56 | }; 57 | 58 | export const walletTokenList = async (pubkey: string) => { 59 | const tokenList: Array<{ mint: string, ata: string, balance: number }> = [] 60 | 61 | try { 62 | const publicKey = new PublicKey(pubkey) 63 | 64 | const walletTokenAccount = await solanaConnection.getTokenAccountsByOwner(publicKey, { 65 | programId: TOKEN_PROGRAM_ID 66 | }); 67 | const tokenAccounts: TokenAccount[] = walletTokenAccount.value.map((j) => ({ 68 | pubkey: j.pubkey, 69 | programId: j.account.owner, 70 | accountInfo: SPL_ACCOUNT_LAYOUT.decode(j.account.data) 71 | })) 72 | 73 | // for (let i = 0; i < tokenAccounts.length; i++) { 74 | // const bal = await solanaConnection.getTokenAccountBalance(tokenAccounts[i].pubkey) 75 | // if (bal.value.uiAmount && bal.value.uiAmount > 0) { 76 | // const poolId = await getPoolId(solanaConnection, tokenAccounts[i].accountInfo.mint, new PublicKey(WSOL.mint)) 77 | // tokenList.push({ mint: tokenAccounts[i].accountInfo.mint.toString(), ata: tokenAccounts[i].pubkey.toString(), balance: bal.value.uiAmount }) 78 | // } 79 | // waitFor(500) 80 | // } 81 | 82 | return tokenAccounts 83 | } catch (e) { 84 | console.log(e) 85 | return [] 86 | } 87 | } -------------------------------------------------------------------------------- /solana-bot-BE/track/index.ts: -------------------------------------------------------------------------------- 1 | export * from './raydium' -------------------------------------------------------------------------------- /solana-bot-BE/track/raydium/executor/jito.ts: -------------------------------------------------------------------------------- 1 | // Jito Bundling part 2 | 3 | import { Keypair, PublicKey, VersionedTransaction } from "@solana/web3.js" 4 | import { BLOCKENGINE_URL, JITO_AUTH_KEYPAIR, JITO_FEE, solanaConnection } from "../../../config" 5 | // import { logger } from "../utils" 6 | import base58 from "bs58" 7 | import { SearcherClient, searcherClient } from "jito-ts/dist/sdk/block-engine/searcher" 8 | import { Bundle } from "jito-ts/dist/sdk/block-engine/types" 9 | import { isError } from "jito-ts/dist/sdk/block-engine/utils" 10 | import { logger } from "../../../utils" 11 | 12 | export async function bundle(txs: VersionedTransaction[], keypair: Keypair) { 13 | try { 14 | const txNum = Math.ceil(txs.length / 3) 15 | let successNum = 0 16 | for (let i = 0; i < txNum; i++) { 17 | const upperIndex = (i + 1) * 3 18 | const downIndex = i * 3 19 | const newTxs = [] 20 | for (let j = downIndex; j < upperIndex; j++) { 21 | if (txs[j]) newTxs.push(txs[j]) 22 | } 23 | // console.log(`------------- Bundle & Send: ${i + 1} ---------`) 24 | // let tryNum = 0 25 | 26 | let success = await bull_dozer(newTxs, keypair) 27 | 28 | console.log('success', success) 29 | return success > 0 ? true : false 30 | } 31 | if (successNum == txNum) return true 32 | else return false 33 | } catch (error) { 34 | return false 35 | } 36 | } 37 | 38 | export async function bull_dozer(txs: VersionedTransaction[], keypair: Keypair) { 39 | try { 40 | 41 | console.log('bull_dozer') 42 | const bundleTransactionLimit = parseInt('4') 43 | 44 | const jito_auth_keypair_array = JITO_AUTH_KEYPAIR.split(',') 45 | const keyapair_num = Math.floor(Math.random() * jito_auth_keypair_array.length) 46 | const jito_auth_keypair = jito_auth_keypair_array[keyapair_num] 47 | const jitoKey = Keypair.fromSecretKey(base58.decode(jito_auth_keypair)) 48 | console.log('jitoKey', jitoKey) 49 | 50 | const blochengine_url_array = BLOCKENGINE_URL.split(',') 51 | const blockengine_num = Math.floor(Math.random() * blochengine_url_array.length) 52 | const blochengine_url = blochengine_url_array[blockengine_num] 53 | console.log('blochengine_url', blochengine_url) 54 | const search = searcherClient(blochengine_url, jitoKey) 55 | 56 | await build_bundle( 57 | search, 58 | bundleTransactionLimit, 59 | txs, 60 | keypair 61 | ) 62 | const bundle_result = await onBundleResult(search) 63 | return bundle_result 64 | } catch (error) { 65 | return 0 66 | } 67 | } 68 | 69 | async function build_bundle( 70 | search: SearcherClient, 71 | bundleTransactionLimit: number, 72 | txs: VersionedTransaction[], 73 | keypair: Keypair 74 | ) { 75 | const accounts = await search.getTipAccounts() 76 | console.log("tip account:", accounts) 77 | const _tipAccount = accounts[Math.min(Math.floor(Math.random() * accounts.length), 3)] 78 | const tipAccount = new PublicKey(_tipAccount) 79 | 80 | const bund = new Bundle([], bundleTransactionLimit) 81 | const resp = await solanaConnection.getLatestBlockhash("processed") 82 | bund.addTransactions(...txs) 83 | 84 | let maybeBundle = bund.addTipTx( 85 | keypair, 86 | Number(JITO_FEE), 87 | tipAccount, 88 | resp.blockhash 89 | ) 90 | 91 | if (isError(maybeBundle)) { 92 | throw maybeBundle 93 | } 94 | try { 95 | await search.sendBundle(maybeBundle) 96 | // logger.info("Bundling done") 97 | } catch (e) { 98 | console.log(e) 99 | logger.info("error in sending bundle\n") 100 | } 101 | return maybeBundle 102 | } 103 | 104 | export const onBundleResult = (c: SearcherClient): Promise => { 105 | let first = 0 106 | let isResolved = false 107 | 108 | return new Promise((resolve) => { 109 | // Set a timeout to reject the promise if no bundle is accepted within 5 seconds 110 | setTimeout(() => { 111 | resolve(first) 112 | isResolved = true 113 | }, 30000) 114 | 115 | c.onBundleResult( 116 | (result: any) => { 117 | if (isResolved) return first 118 | // clearTimeout(timeout) // Clear the timeout if a bundle is accepted 119 | const bundleId = result.bundleId 120 | const isAccepted = result.accepted 121 | const isRejected = result.rejected 122 | if (isResolved == false) { 123 | 124 | if (isAccepted) { 125 | console.log( 126 | "bundle accepted, ID:", 127 | result.bundleId, 128 | " Slot: ", 129 | result.accepted!.slot 130 | ) 131 | first += 1 132 | isResolved = true 133 | // Resolve with 'first' when a bundle is accepted 134 | resolve(first) 135 | } 136 | if (isRejected) { 137 | logger.warn("bundle is Rejected\n", result) 138 | // Do not resolve or reject the promise here 139 | } 140 | } 141 | }, 142 | (e: any) => { 143 | console.log(e) 144 | // Do not reject the promise here 145 | } 146 | ) 147 | }) 148 | } -------------------------------------------------------------------------------- /solana-bot-BE/track/raydium/executor/legacy.ts: -------------------------------------------------------------------------------- 1 | import { VersionedTransaction } from '@solana/web3.js'; 2 | import { solanaConnection } from "../../../config"; 3 | import { logger } from "../../../utils"; 4 | 5 | 6 | interface Blockhash { 7 | blockhash: string; 8 | lastValidBlockHeight: number; 9 | } 10 | 11 | export const execute = async (transaction: VersionedTransaction, latestBlockhash: Blockhash) => { 12 | try { 13 | const simRes = await solanaConnection.simulateTransaction(transaction) 14 | // console.log(simRes) 15 | if (simRes.value.err) { 16 | console.log('sim err') 17 | return false 18 | } 19 | 20 | const signature = await solanaConnection.sendRawTransaction(transaction.serialize(), { 21 | skipPreflight: true, 22 | }) 23 | 24 | console.log('signature', signature) 25 | logger.debug({ signature }, 'Confirming transaction...'); 26 | 27 | const confirmation = await solanaConnection.confirmTransaction( 28 | signature, 29 | 'confirmed', 30 | ); 31 | 32 | console.log('confirmation result\n', confirmation) 33 | 34 | if (confirmation.value.err) { 35 | console.log('tx error') 36 | return false 37 | } 38 | else { 39 | console.log('tx success') 40 | return true 41 | } 42 | } catch (e) { 43 | console.log('error', e) 44 | return false 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /solana-bot-BE/track/raydium/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./raydium"; 2 | 3 | -------------------------------------------------------------------------------- /solana-bot-BE/track/raydium/tokenFilter/index.ts: -------------------------------------------------------------------------------- 1 | import { Commitment, Connection, PublicKey } from '@solana/web3.js'; 2 | import { getPdaMetadataKey } from '@raydium-io/raydium-sdk'; 3 | 4 | import { MintLayout } from '@solana/spl-token'; 5 | 6 | export const checkBurn = async (connection: Connection, lpMint: PublicKey, commitment: Commitment) => { 7 | try { 8 | const amount = await connection.getTokenSupply(lpMint, commitment); 9 | const burned = amount.value.uiAmount === 0; 10 | return burned 11 | } catch (error) { 12 | return false 13 | } 14 | } 15 | 16 | // export const checkMutable = async (connection: Connection, baseMint: PublicKey, ) => { 17 | // try { 18 | // const metadataPDA = getPdaMetadataKey(baseMint); 19 | // const metadataAccount = await connection.getAccountInfo(metadataPDA.publicKey); 20 | // if (!metadataAccount?.data) { 21 | // return { ok: false, message: 'Mutable -> Failed to fetch account data' }; 22 | // } 23 | // const serializer = getMetadataAccountDataSerializer() 24 | // const deserialize = serializer.deserialize(metadataAccount.data); 25 | // const mutable = deserialize[0].isMutable; 26 | 27 | // return !mutable 28 | // } catch (e: any) { 29 | // return false 30 | // } 31 | // } 32 | 33 | export const checkMintable = async (connection: Connection, vault: PublicKey): Promise => { 34 | try { 35 | let { data } = (await connection.getAccountInfo(vault)) || {} 36 | if (!data) { 37 | return 38 | } 39 | const deserialize = MintLayout.decode(data) 40 | return deserialize.mintAuthorityOption === 0 41 | } catch (e) { 42 | return false 43 | } 44 | } 45 | 46 | export const checkFreezable = async (connection: Connection, vault: PublicKey): Promise => { 47 | try { 48 | let { data } = (await connection.getAccountInfo(vault)) || {} 49 | if (!data) { 50 | return 51 | } 52 | const deserialize = MintLayout.decode(data) 53 | return deserialize.freezeAuthorityOption === 0 54 | } catch (e) { 55 | return false 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /solana-bot-BE/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES6", 4 | "module": "CommonJS", 5 | "lib": ["ES2015"], 6 | "outDir": "./dist", 7 | "rootDir": ".", 8 | "strict": true, 9 | "esModuleInterop": true 10 | } 11 | } -------------------------------------------------------------------------------- /solana-bot-BE/utils/index.ts: -------------------------------------------------------------------------------- 1 | export * from './liquidity' 2 | export * from './market' 3 | export * from './token' 4 | export * from './types' 5 | export * from './utils' -------------------------------------------------------------------------------- /solana-bot-BE/utils/liquidity.ts: -------------------------------------------------------------------------------- 1 | import { Commitment, Connection, PublicKey } from '@solana/web3.js'; 2 | import { 3 | Liquidity, 4 | LiquidityPoolKeys, 5 | Market, 6 | TokenAccount, 7 | SPL_ACCOUNT_LAYOUT, 8 | publicKey, 9 | struct, 10 | MAINNET_PROGRAM_ID, 11 | LiquidityStateV4, 12 | } from '@raydium-io/raydium-sdk'; 13 | import { TOKEN_PROGRAM_ID } from '@solana/spl-token'; 14 | import { MinimalMarketLayoutV3 } from './market'; 15 | 16 | export const RAYDIUM_LIQUIDITY_PROGRAM_ID_V4 = MAINNET_PROGRAM_ID.AmmV4; 17 | export const OPENBOOK_PROGRAM_ID = MAINNET_PROGRAM_ID.OPENBOOK_MARKET; 18 | 19 | export const MINIMAL_MARKET_STATE_LAYOUT_V3 = struct([ 20 | publicKey('eventQueue'), 21 | publicKey('bids'), 22 | publicKey('asks'), 23 | ]); 24 | 25 | export function createPoolKeys( 26 | id: PublicKey, 27 | accountData: LiquidityStateV4, 28 | minimalMarketLayoutV3: MinimalMarketLayoutV3, 29 | ): LiquidityPoolKeys { 30 | return { 31 | id, 32 | baseMint: accountData.baseMint, 33 | quoteMint: accountData.quoteMint, 34 | lpMint: accountData.lpMint, 35 | baseDecimals: accountData.baseDecimal.toNumber(), 36 | quoteDecimals: accountData.quoteDecimal.toNumber(), 37 | lpDecimals: 5, 38 | version: 4, 39 | programId: RAYDIUM_LIQUIDITY_PROGRAM_ID_V4, 40 | authority: Liquidity.getAssociatedAuthority({ 41 | programId: RAYDIUM_LIQUIDITY_PROGRAM_ID_V4, 42 | }).publicKey, 43 | openOrders: accountData.openOrders, 44 | targetOrders: accountData.targetOrders, 45 | baseVault: accountData.baseVault, 46 | quoteVault: accountData.quoteVault, 47 | marketVersion: 3, 48 | marketProgramId: accountData.marketProgramId, 49 | marketId: accountData.marketId, 50 | marketAuthority: Market.getAssociatedAuthority({ 51 | programId: accountData.marketProgramId, 52 | marketId: accountData.marketId, 53 | }).publicKey, 54 | marketBaseVault: accountData.baseVault, 55 | marketQuoteVault: accountData.quoteVault, 56 | marketBids: minimalMarketLayoutV3.bids, 57 | marketAsks: minimalMarketLayoutV3.asks, 58 | marketEventQueue: minimalMarketLayoutV3.eventQueue, 59 | withdrawQueue: accountData.withdrawQueue, 60 | lpVault: accountData.lpVault, 61 | lookupTableAccount: PublicKey.default, 62 | }; 63 | } 64 | 65 | export async function getTokenAccounts( 66 | connection: Connection, 67 | owner: PublicKey, 68 | commitment?: Commitment, 69 | ) { 70 | const tokenResp = await connection.getTokenAccountsByOwner( 71 | owner, 72 | { 73 | programId: TOKEN_PROGRAM_ID, 74 | }, 75 | commitment, 76 | ); 77 | 78 | const accounts: TokenAccount[] = []; 79 | for (const { pubkey, account } of tokenResp.value) { 80 | accounts.push({ 81 | pubkey, 82 | programId: account.owner, 83 | accountInfo: SPL_ACCOUNT_LAYOUT.decode(account.data), 84 | }); 85 | } 86 | 87 | return accounts; 88 | } 89 | -------------------------------------------------------------------------------- /solana-bot-BE/utils/market.ts: -------------------------------------------------------------------------------- 1 | import { Commitment, Connection, PublicKey } from '@solana/web3.js'; 2 | import { GetStructureSchema, MARKET_STATE_LAYOUT_V3 } from '@raydium-io/raydium-sdk'; 3 | import { MINIMAL_MARKET_STATE_LAYOUT_V3 } from './liquidity'; 4 | 5 | export type MinimalMarketStateLayoutV3 = typeof MINIMAL_MARKET_STATE_LAYOUT_V3; 6 | export type MinimalMarketLayoutV3 = 7 | GetStructureSchema; 8 | 9 | export async function getMinimalMarketV3( 10 | connection: Connection, 11 | marketId: PublicKey, 12 | commitment?: Commitment, 13 | ): Promise { 14 | const marketInfo = await connection.getAccountInfo(marketId, { 15 | commitment, 16 | dataSlice: { 17 | offset: MARKET_STATE_LAYOUT_V3.offsetOf('eventQueue'), 18 | length: 32 * 3, 19 | }, 20 | }); 21 | console.log('marketId', marketInfo) 22 | return MINIMAL_MARKET_STATE_LAYOUT_V3.decode(marketInfo!.data); 23 | } 24 | -------------------------------------------------------------------------------- /solana-bot-BE/utils/swap.ts: -------------------------------------------------------------------------------- 1 | import { ApiPoolInfoV4, InnerSimpleV0Transaction, LIQUIDITY_STATE_LAYOUT_V4, LOOKUP_TABLE_CACHE, Liquidity, LiquidityPoolKeys, MARKET_STATE_LAYOUT_V3, Market, Percent, SPL_ACCOUNT_LAYOUT, SPL_MINT_LAYOUT, Token, TokenAccount, TokenAmount, TxVersion, buildSimpleTransaction, jsonInfo2PoolKeys } from "@raydium-io/raydium-sdk" 2 | import { TOKEN_PROGRAM_ID } from "@solana/spl-token" 3 | import { Connection, PublicKey, Signer, Transaction } from "@solana/web3.js" 4 | import { Keypair } from "@solana/web3.js" 5 | import { solanaConnection } from '../config/config'; 6 | import { SendOptions } from "@solana/web3.js"; 7 | import { VersionedTransaction } from "@solana/web3.js"; 8 | import { AccountInfo } from "@solana/web3.js"; 9 | 10 | type WalletTokenAccounts = Awaited> 11 | type TestTxInputInfo = { 12 | outputToken: Token 13 | targetPool: string 14 | inputTokenAmount: TokenAmount 15 | slippage: Percent 16 | walletTokenAccounts: WalletTokenAccounts 17 | wallet: Keypair, 18 | gas?: number, 19 | } 20 | 21 | const makeTxVersion = TxVersion.V0; 22 | const addLookupTableInfo = LOOKUP_TABLE_CACHE 23 | 24 | export async function getWalletTokenAccount(connection: Connection, wallet: PublicKey): Promise { 25 | const walletTokenAccount = await connection.getTokenAccountsByOwner(wallet, { 26 | programId: TOKEN_PROGRAM_ID, 27 | }); 28 | return walletTokenAccount.value.map((i) => ({ 29 | pubkey: i.pubkey, 30 | programId: i.account.owner, 31 | accountInfo: SPL_ACCOUNT_LAYOUT.decode(i.account.data), 32 | })); 33 | } 34 | 35 | async function sendTx( 36 | connection: Connection, 37 | payer: Keypair | Signer, 38 | txs: (VersionedTransaction | Transaction)[], 39 | options?: SendOptions 40 | ): Promise { 41 | console.log('length', txs.length) 42 | for (const iTx of txs) { 43 | if (iTx instanceof VersionedTransaction) { 44 | iTx.sign([payer]); 45 | // txids.push(await connection.sendTransaction(iTx, options)); 46 | // const simulator = await connection.simulateTransaction(iTx) 47 | // console.log(simulator) 48 | // const rawTransaction = iTx.serialize() 49 | // const txid = await connection.sendRawTransaction(rawTransaction, { 50 | // skipPreflight: true, 51 | // maxRetries: 2 52 | // }); 53 | return iTx 54 | } 55 | else { 56 | return undefined 57 | } 58 | } 59 | 60 | return undefined 61 | } 62 | 63 | export async function buildAndSendTx(innerSimpleV0Transaction: InnerSimpleV0Transaction[], wallet: Keypair, options?: SendOptions) { 64 | const willSendTx = await buildSimpleTransaction({ 65 | connection: solanaConnection, 66 | makeTxVersion, 67 | payer: wallet.publicKey, 68 | innerTransactions: innerSimpleV0Transaction, 69 | addLookupTableInfo, 70 | }) 71 | 72 | const res = await sendTx(solanaConnection, wallet, willSendTx, options) 73 | return res 74 | } 75 | 76 | 77 | export const formatAmmKeysById = async (id: string): Promise => { 78 | try { 79 | let account: AccountInfo | null = null 80 | while (account === null) account = await solanaConnection.getAccountInfo(new PublicKey(id)) 81 | const info = LIQUIDITY_STATE_LAYOUT_V4.decode(account.data) 82 | 83 | const marketId = info.marketId 84 | let marketAccount: AccountInfo | null = null 85 | while (marketAccount === null) marketAccount = await solanaConnection.getAccountInfo(marketId) 86 | if (marketAccount === null) throw Error(' get market info error') 87 | const marketInfo = MARKET_STATE_LAYOUT_V3.decode(marketAccount.data) 88 | 89 | const lpMint = info.lpMint 90 | let lpMintAccount: AccountInfo | null = null 91 | while (lpMintAccount === null) lpMintAccount = await solanaConnection.getAccountInfo(lpMint, 'processed') 92 | const lpMintInfo = SPL_MINT_LAYOUT.decode(lpMintAccount.data) 93 | 94 | return { 95 | id, 96 | baseMint: info.baseMint.toString(), 97 | quoteMint: info.quoteMint.toString(), 98 | lpMint: info.lpMint.toString(), 99 | baseDecimals: info.baseDecimal.toNumber(), 100 | quoteDecimals: info.quoteDecimal.toNumber(), 101 | lpDecimals: lpMintInfo.decimals, 102 | version: 4, 103 | programId: account.owner.toString(), 104 | authority: Liquidity.getAssociatedAuthority({ programId: account.owner }).publicKey.toString(), 105 | openOrders: info.openOrders.toString(), 106 | targetOrders: info.targetOrders.toString(), 107 | baseVault: info.baseVault.toString(), 108 | quoteVault: info.quoteVault.toString(), 109 | withdrawQueue: info.withdrawQueue.toString(), 110 | lpVault: info.lpVault.toString(), 111 | marketVersion: 3, 112 | marketProgramId: info.marketProgramId.toString(), 113 | marketId: info.marketId.toString(), 114 | marketAuthority: Market.getAssociatedAuthority({ programId: info.marketProgramId, marketId: info.marketId }).publicKey.toString(), 115 | marketBaseVault: marketInfo.baseVault.toString(), 116 | marketQuoteVault: marketInfo.quoteVault.toString(), 117 | marketBids: marketInfo.bids.toString(), 118 | marketAsks: marketInfo.asks.toString(), 119 | marketEventQueue: marketInfo.eventQueue.toString(), 120 | lookupTableAccount: PublicKey.default.toString() 121 | } 122 | } catch (e) { 123 | console.log(e) 124 | } 125 | } 126 | 127 | export async function swapOnlyAmm(input: TestTxInputInfo) { 128 | try { 129 | // -------- pre-action: get pool info -------- 130 | const targetPoolInfo = await formatAmmKeysById(input.targetPool) 131 | const poolKeys = jsonInfo2PoolKeys(targetPoolInfo) as LiquidityPoolKeys 132 | const poolInfo = await Liquidity.fetchInfo({ connection: solanaConnection, poolKeys }) 133 | // -------- step 1: coumpute amount out -------- 134 | const { amountOut, minAmountOut } = Liquidity.computeAmountOut({ 135 | poolKeys, 136 | poolInfo, 137 | amountIn: input.inputTokenAmount, 138 | currencyOut: input.outputToken, 139 | slippage: input.slippage, 140 | }) 141 | 142 | // -------- step 2: create instructions by SDK function -------- 143 | if (!input.gas) { 144 | const { innerTransactions } = await Liquidity.makeSwapInstructionSimple({ 145 | connection: solanaConnection, 146 | poolKeys, 147 | userKeys: { 148 | tokenAccounts: input.walletTokenAccounts, 149 | owner: input.wallet.publicKey, 150 | }, 151 | amountIn: input.inputTokenAmount, 152 | amountOut: minAmountOut, 153 | fixedSide: 'in', 154 | makeTxVersion, 155 | computeBudgetConfig: { 156 | units: 100_000, 157 | microLamports: 200_000 158 | } 159 | }) 160 | console.log('amountOut:', amountOut.toFixed(), ' minAmountOut: ', minAmountOut.toFixed()) 161 | 162 | const res = await buildAndSendTx(innerTransactions, input.wallet) 163 | return { res, innerTransactions } 164 | } else { 165 | const { innerTransactions } = await Liquidity.makeSwapInstructionSimple({ 166 | connection: solanaConnection, 167 | poolKeys, 168 | userKeys: { 169 | tokenAccounts: input.walletTokenAccounts, 170 | owner: input.wallet.publicKey, 171 | }, 172 | amountIn: input.inputTokenAmount, 173 | amountOut: minAmountOut, 174 | fixedSide: 'in', 175 | makeTxVersion, 176 | computeBudgetConfig: { 177 | units: 100_000, 178 | microLamports: 200_000 179 | } 180 | }) 181 | console.log('amountOut:', amountOut.toFixed(), ' minAmountOut: ', minAmountOut.toFixed()) 182 | 183 | const res = await buildAndSendTx(innerTransactions, input.wallet) 184 | return { res, innerTransactions } 185 | } 186 | } 187 | catch (e) { 188 | console.log(e) 189 | return undefined 190 | } 191 | } 192 | -------------------------------------------------------------------------------- /solana-bot-BE/utils/token.ts: -------------------------------------------------------------------------------- 1 | import { PublicKey } from '@solana/web3.js'; 2 | 3 | export const SOL_ADDRESS = new PublicKey("So11111111111111111111111111111111111111112") 4 | export const USDT_ADDRESS = new PublicKey("Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB") 5 | export const STSOL_ADDRESS = new PublicKey("7dHbWXmci3dT8UFYWYZweBLXgycu7Y3iL6trKn1Y7ARj") 6 | export const RAY_ADDRESS = new PublicKey("4k3Dyjzvzp8eMZWUXbBCjEvwSkkk59S5iCNLY3QrkX6R") 7 | 8 | export const ORCA_ADDRESS = new PublicKey("orcaEKTdK7LKz57vaAYr9QeNsVEPfiu6QeMU1kektZE") 9 | export const WSOL_ADDRESS = new PublicKey("BBQsU18vKUhvHcX2iwZgCkKVnLGRbeqyzqLteFJZBxJ3") 10 | export const USDC_ADDRESS = new PublicKey("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v") 11 | 12 | export const BONK_ADDRESS = new PublicKey("DezXAZ8z7PnrnRJjz3wXBoRgixCa6xjnB7YaB1pPB263") 13 | export const VEUR_ADDRESS = new PublicKey("C4Kkr9NZU3VbyedcgutU6LKmi6MKz81sx6gRmk5pX519") 14 | export const JPLP_ADDRESS = new PublicKey("27G8MtK7VtTcCHkpASjSDdkWWYfoqT6ggEuKidVJidD4") 15 | export const HELI_ADDRESS = new PublicKey("hntyVP6YFm1Hg25TN9WGLqM12b8TQmcknKrdu1oxWux") 16 | 17 | export const NEW_ADDRESS = new PublicKey("HJ39rRZ6ys22KdB3USxDgNsL7RKiQmsC3yL8AS3Suuku") 18 | export const PAI_ADDRESS = new PublicKey("Ea5SjE2Y6yvCeW5dYTn7PYMuW5ikXkvbGdcmSnXeaLjS") 19 | -------------------------------------------------------------------------------- /solana-bot-BE/utils/types.ts: -------------------------------------------------------------------------------- 1 | import { PublicKey } from "@metaplex-foundation/js"; 2 | import { struct, u8, u64, publicKey } from "@raydium-io/raydium-sdk"; 3 | 4 | export interface IStakingResult { 5 | user: string; 6 | count: bigint; 7 | stakedAmount: bigint; 8 | time: bigint; 9 | period: bigint; 10 | } 11 | 12 | export type IPairInfo = { 13 | [key: string]: { 14 | address: string, 15 | decimals: string, 16 | name?: string, 17 | symbol?: string, 18 | uri?: string, 19 | image?: string, 20 | desc?: string, 21 | } 22 | } 23 | 24 | export const LOG_TYPE = struct([u8("log_type")]); 25 | 26 | export const RAY_IX_TYPE = { 27 | CREATE_POOL: 0, 28 | ADD_LIQUIDITY: 1, 29 | BURN_LIQUIDITY: 2, 30 | SWAP: 3, 31 | }; 32 | export const INIT_LOG = struct([ 33 | u8("log_type"), 34 | u64("time"), 35 | u8("pc_decimals"), 36 | u8("coin_decimals"), 37 | u64("pc_lot_size"), 38 | u64("coin_lot_size"), 39 | u64("pc_amount"), 40 | u64("coin_amount"), 41 | publicKey("market"), 42 | ]); 43 | 44 | export const ACTION_TYPE = { 45 | DEPOSIT: "deposit", 46 | WITHDRAW: "withdraw", 47 | MINT: "mint", 48 | BURN: "burn", 49 | CREATE: "create", 50 | DEFAULT: "unknown", 51 | }; 52 | 53 | export interface IPool { 54 | pairName: string; 55 | mintAuthority: any; 56 | freezeAuthority: any; 57 | description: any; 58 | links: { 59 | Transaction: string; 60 | }; 61 | image: any; 62 | poolCreateAAmount: string; 63 | poolCreateBAmount: string; 64 | poolId: PublicKey; 65 | inputTokenInfo: any; 66 | outputTokenInfo: any; 67 | inputTokenAddr: PublicKey; 68 | outputTokenAddr: PublicKey; 69 | } 70 | 71 | -------------------------------------------------------------------------------- /solana-bot-BE/utils/utils.ts: -------------------------------------------------------------------------------- 1 | import { Keypair } from '@solana/web3.js' 2 | import bs58 from 'bs58'; 3 | import fs from 'fs' 4 | import pino, { Logger } from 'pino'; 5 | 6 | export const getPulbicKey = async (privateKeyHex: string) => { 7 | try { 8 | const keypair = Keypair.fromSecretKey( 9 | bs58.decode(privateKeyHex) 10 | ) 11 | return keypair.publicKey.toString() 12 | } catch (e) { 13 | throw ('Invalid Private Key') 14 | } 15 | } 16 | 17 | 18 | export const readData = async (Path: string): Promise => { 19 | return JSON.parse(fs.readFileSync(Path, `utf8`)); 20 | } 21 | 22 | export const writeData = async (data: any, path: any) => { 23 | const dataJson = JSON.stringify(data, null, 4); 24 | fs.writeFile(path, dataJson, (err) => { 25 | if (err) { 26 | console.log('Error writing file:', err); 27 | } else { 28 | console.log(`wrote file ${path}`); 29 | } 30 | }); 31 | } 32 | 33 | 34 | export const retrieveEnvVariable = (variableName: string, logger: Logger) => { 35 | const variable = process.env[variableName] || ''; 36 | if (!variable) { 37 | logger.error(`${variableName} is not set`); 38 | process.exit(1); 39 | } 40 | return variable; 41 | }; 42 | 43 | export const transport = pino.transport({ 44 | target: 'pino-pretty', 45 | }); 46 | 47 | export const logger = pino( 48 | { 49 | level: 'info', 50 | redact: ['poolKeys'], 51 | serializers: { 52 | error: pino.stdSerializers.err, 53 | }, 54 | base: undefined, 55 | }, 56 | transport, 57 | ); 58 | -------------------------------------------------------------------------------- /solana-bot-BE/vercel.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 2, 3 | "builds": [ 4 | { 5 | "src": "dist/index.js", 6 | "use": "@vercel/node", 7 | "config": { "includeFiles": ["dist/**"] } 8 | } 9 | ], 10 | "routes": [ 11 | { 12 | "src": "/(.*)", 13 | "dest": "dist/index.js" 14 | } 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /solana-bot-FE/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { browser: true, es2020: true }, 4 | extends: [ 5 | 'eslint:recommended', 6 | 'plugin:@typescript-eslint/recommended', 7 | 'plugin:react-hooks/recommended', 8 | ], 9 | ignorePatterns: ['dist', '.eslintrc.cjs'], 10 | parser: '@typescript-eslint/parser', 11 | plugins: ['react-refresh'], 12 | rules: { 13 | 'react-refresh/only-export-components': [ 14 | 'warn', 15 | { allowConstantExport: true }, 16 | ], 17 | }, 18 | } 19 | -------------------------------------------------------------------------------- /solana-bot-FE/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | .env 26 | -------------------------------------------------------------------------------- /solana-bot-FE/LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /solana-bot-FE/README.md: -------------------------------------------------------------------------------- 1 | # React Vite TypeScript Jest Boilerplate 2 | 3 | This is a boilerplate project for kickstarting React applications using Vite, TypeScript, and Jest for testing. It's configured with essential tools and settings to help you quickly start building React apps with modern tooling. 4 | 5 | ## Features 6 | 7 | - **Vite:** A blazing fast build tool that provides near-instantaneous hot module replacement (HMR) and lightning-fast dev server. 8 | - **React:** A JavaScript library for building user interfaces. 9 | - **TypeScript:** A superset of JavaScript that adds static typing and other features to the language. 10 | - **Jest:** A delightful JavaScript testing framework with a focus on simplicity. 11 | - **Pre-configured Setup:** All necessary configurations are already set up, allowing you to focus on writing code instead of spending time configuring the project. 12 | 13 | Currently, two official plugins are available: 14 | 15 | - [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh 16 | - [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh 17 | 18 | ## Expanding the ESLint configuration 19 | 20 | If you are developing a production application, we recommend updating the configuration to enable type aware lint rules: 21 | 22 | - Configure the top-level `parserOptions` property like this: 23 | 24 | ```js 25 | parserOptions: { 26 | ecmaVersion: 'latest', 27 | sourceType: 'module', 28 | project: ['./tsconfig.json', './tsconfig.node.json'], 29 | tsconfigRootDir: __dirname, 30 | }, 31 | ``` 32 | 33 | - Replace `plugin:@typescript-eslint/recommended` to `plugin:@typescript-eslint/recommended-type-checked` or `plugin:@typescript-eslint/strict-type-checked` 34 | - Optionally add `plugin:@typescript-eslint/stylistic-type-checked` 35 | - Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and add `plugin:react/recommended` & `plugin:react/jsx-runtime` to the `extends` list 36 | 37 | ## Getting Started 38 | 39 | ### Prerequisites 40 | 41 | Make sure you have Node.js and npm installed on your machine. 42 | 43 | ### Installation 44 | 45 | 1. Clone this repository: 46 | 47 | ```bash 48 | git clone https://github.com/your-username/react-vite-ts-jest-boilerplate.git 49 | ``` 50 | 51 | 2. Navigate into the project directory: 52 | 53 | ```bash 54 | cd react-vite-ts-jest-boilerplate 55 | ``` 56 | 57 | 3. Install dependencies: 58 | 59 | ```bash 60 | npm install 61 | ``` 62 | 63 | ### Development 64 | 65 | To start the development server, run: 66 | 67 | ```bash 68 | npm run dev 69 | ``` 70 | 71 | This will start the Vite development server. You can now access your React application at `http://localhost:3000`. 72 | 73 | ### Testing 74 | 75 | To run tests, use: 76 | 77 | ```bash 78 | npm test 79 | ``` 80 | 81 | This will run all the test suites using Jest. 82 | 83 | ### Building 84 | 85 | To build your application for production, run: 86 | 87 | ```bash 88 | npm run build 89 | ``` 90 | 91 | This will generate an optimized build of your application in the `dist` directory. 92 | 93 | ## Folder Structure 94 | 95 | ``` 96 | react-vite-ts-jest-boilerplate/ 97 | ├── src/ 98 | │ ├── features/ui/ 99 | │ │ ├── footer.tsx 100 | │ │ ├── haeder.tsx 101 | │ │ └── idnex.ts 102 | │ ├── pages/ 103 | │ │ ├── home.tsx 104 | │ │ ├── page-data.tsx 105 | │ │ └── router.tsx 106 | │ └── App.tsx 107 | ├── .eslintrc.cjs 108 | ├── .gitignore 109 | ├── index.html 110 | ├── LICENSE 111 | ├── package.json 112 | ├── README.md 113 | ├── tsconfig.json 114 | ├── tsconfig.node.json 115 | └── vite.config.ts 116 | ``` 117 | 118 | ## Contributing 119 | 120 | Contributions are welcome! Feel free to submit pull requests or open issues for any bugs or feature requests. 121 | 122 | ## License 123 | 124 | This project is licensed under the [MIT License](LICENSE). 125 | -------------------------------------------------------------------------------- /solana-bot-FE/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Sol Bot 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /solana-bot-FE/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vitest", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "start": "vite --host", 8 | "build": "tsc --skipLibCheck && vite build", 9 | "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", 10 | "preview": "vite preview", 11 | "test": "vitest", 12 | "coverage": "vitest run --coverage" 13 | }, 14 | "dependencies": { 15 | "@esbuild-plugins/node-globals-polyfill": "^0.2.3", 16 | "@raydium-io/raydium-sdk": "^1.3.1-beta.52", 17 | "@solana/spl-token": "^0.4.6", 18 | "@solana/web3.js": "^1.91.7", 19 | "autoprefixer": "^10.4.19", 20 | "axios": "^1.6.8", 21 | "bn.js": "^5.2.1", 22 | "crypto-browserify": "^3.12.0", 23 | "postcss": "^8.4.38", 24 | "react": "^18.2.0", 25 | "react-dom": "^18.2.0", 26 | "react-responsive-modal": "^6.4.2", 27 | "react-router-dom": "^6.16.0", 28 | "react-spinners-kit": "^1.9.1", 29 | "react-toastify": "^10.0.5", 30 | "serve": "^14.2.3", 31 | "socket.io-client": "^4.7.5", 32 | "tailwindcss": "^3.4.3", 33 | "vite-tsconfig-paths": "^4.2.1" 34 | }, 35 | "devDependencies": { 36 | "@testing-library/jest-dom": "^5.17.0", 37 | "@testing-library/react": "^14.0.0", 38 | "@testing-library/user-event": "^14.4.3", 39 | "@types/bn.js": "^5.1.5", 40 | "@types/react": "^18.2.15", 41 | "@types/react-dom": "^18.2.7", 42 | "@typescript-eslint/eslint-plugin": "^6.0.0", 43 | "@typescript-eslint/parser": "^6.0.0", 44 | "@vitejs/plugin-react-swc": "^3.3.2", 45 | "eslint": "^8.45.0", 46 | "eslint-plugin-react-hooks": "^4.6.0", 47 | "eslint-plugin-react-refresh": "^0.4.3", 48 | "happy-dom": "^10.5.2", 49 | "typescript": "^5.0.2", 50 | "vite": "^4.4.5", 51 | "vitest": "^0.33.0" 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /solana-bot-FE/postcss.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /solana-bot-FE/public/bot.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/justshiftjk/Solana-Sniper-Memecoin-Bot/15b5e0f62aba29472d6a580564c10d9573b00fe0/solana-bot-FE/public/bot.jpg -------------------------------------------------------------------------------- /solana-bot-FE/public/vite.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /solana-bot-FE/src/App.tsx: -------------------------------------------------------------------------------- 1 | import { Header } from "./features/header"; 2 | import NotFound from "./pages/404"; 3 | import { Mint, MintLayout, Trading } from "./pages/mint"; 4 | import { Home, SniperLayout } from "./pages/sniper"; 5 | import { BrowserRouter, Route, Routes } from "react-router-dom"; 6 | // import { SniperLayout, Home } from "@pages/sniper"; 7 | // import { MintLayout, Mint ,Trading} from "@pages/mint"; 8 | // import NotFound from "@pages/404"; 9 | // import { Header } from "@features/header"; 10 | 11 | function App() { 12 | return ( 13 | 14 |
15 | 16 | } path="/sniper" > 17 | }> 18 | 19 | } path="/token" > 20 | }> 21 | }> 22 | 23 | } /> 24 | }> 25 | 26 | 27 | 28 | ); 29 | } 30 | 31 | export default App; 32 | -------------------------------------------------------------------------------- /solana-bot-FE/src/component/colorSwitch.tsx: -------------------------------------------------------------------------------- 1 | import { FC } from 'react' 2 | 3 | interface Props { 4 | isSwitch: boolean 5 | handleSwitch: Function 6 | } 7 | 8 | export const ColorSwitch: FC = ({ isSwitch, handleSwitch }) => { 9 | return ( 10 |
handleSwitch()}>
11 | 12 | ) 13 | } 14 | -------------------------------------------------------------------------------- /solana-bot-FE/src/config/index.ts: -------------------------------------------------------------------------------- 1 | import { Connection } from "@solana/web3.js"; 2 | 3 | const solanaConnection = new Connection(import.meta.env.VITE_RPC_URL, { 4 | commitment: "confirmed", 5 | }); 6 | 7 | const devConnection = new Connection(import.meta.env.VITE_DEV_RPC_URL, { 8 | commitment: "confirmed", 9 | }); 10 | 11 | export const pinataAPIKey = import.meta.env.VITE_PINATA_API_KEY 12 | export const pinataPublicURL = import.meta.env.VITE_PINATA_URL 13 | 14 | solanaConnection 15 | devConnection 16 | // rpc set 17 | export const connection = solanaConnection -------------------------------------------------------------------------------- /solana-bot-FE/src/context/appContext.tsx: -------------------------------------------------------------------------------- 1 | import { ReactNode, createContext, useContext, useState } from "react"; 2 | 3 | // Define the shape of the context 4 | export interface appContextProps { 5 | publicKey: string; 6 | setPublicKey: React.Dispatch> 7 | secretKey: string; 8 | setSecretKey: React.Dispatch> 9 | poolList: { poolId: string, poolState: string, buy: number, sell: number, buyTx: string, sellTx: string, name: string, symbol: string, image: string }[]; 10 | setPoolList: React.Dispatch>> 11 | balance: number; 12 | setBalance: React.Dispatch> 13 | wbalance: number; 14 | setWbalance: React.Dispatch> 15 | running: boolean 16 | setRunning: React.Dispatch> 17 | method: boolean 18 | setMethod: React.Dispatch> 19 | autoBuy: boolean 20 | setAutoBuy: React.Dispatch> 21 | connect: boolean 22 | setConnect: React.Dispatch> 23 | autoSell: boolean 24 | setAutoSell: React.Dispatch> 25 | price: number 26 | setPrice: React.Dispatch> 27 | jitoMode: boolean 28 | setJitoMode: React.Dispatch> 29 | flag: boolean 30 | setFlag: React.Dispatch> 31 | minSol: number 32 | setMinSol: React.Dispatch> 33 | maxSol: number 34 | setMaxSol: React.Dispatch> 35 | buyGasFee: number 36 | setBuyGasFee: React.Dispatch> 37 | sellGasFee: number 38 | setSellGasFee: React.Dispatch> 39 | showCount: number 40 | setShowCount: React.Dispatch> 41 | solAmount: number 42 | setSolAmount: React.Dispatch> 43 | jitoFee: number 44 | setJitoFee: React.Dispatch> 45 | profit: number 46 | setProfit: React.Dispatch> 47 | stopLoss: number 48 | setStopLoss: React.Dispatch> 49 | arbitrageAmt: number 50 | setArbitrageAmt: React.Dispatch> 51 | quoteToken: string; 52 | setQuoteToken: React.Dispatch> 53 | mintAddr: string; 54 | setMintAddr: React.Dispatch> 55 | poolId: string; 56 | setPoolId: React.Dispatch> 57 | baseVault: string; 58 | setBaseVault: React.Dispatch> 59 | quoteVault: string; 60 | setQuoteVault: React.Dispatch> 61 | baseToken: string; 62 | setBaseToken: React.Dispatch> 63 | arbitrage: boolean, 64 | setArbitrage: React.Dispatch>, 65 | arbitragePrivateKey: string, 66 | setArbitragePrivateKey: React.Dispatch>, 67 | arbitragePublicKey: string, 68 | setArbitragePublicKey: React.Dispatch>, 69 | arbitBalance: number, 70 | setArbitBalance: React.Dispatch>, 71 | arbitWBalance: number, 72 | setArbitWBalance: React.Dispatch>, 73 | delay: number, 74 | setDelay: React.Dispatch>, 75 | wallets: Array, 76 | setWallets: React.Dispatch>>, 77 | } 78 | 79 | // Create the User context 80 | export const AppContext = createContext({ 81 | publicKey: "", 82 | setPublicKey: () => { }, 83 | secretKey: "", 84 | setSecretKey: () => { }, 85 | poolList: [], 86 | setPoolList: () => { }, 87 | balance: 0, 88 | setBalance: () => { }, 89 | wbalance: 0, 90 | setWbalance: () => { }, 91 | running: false, 92 | setRunning: () => { }, 93 | method: false, 94 | setMethod: () => { }, 95 | autoBuy: false, 96 | setAutoBuy: () => { }, 97 | connect: false, 98 | setConnect: () => { }, 99 | autoSell: false, 100 | setAutoSell: () => { }, 101 | price: 0, 102 | setPrice: () => { }, 103 | jitoMode: false, 104 | setJitoMode: () => { }, 105 | flag: false, 106 | setFlag: () => { }, 107 | minSol: 0, 108 | setMinSol: () => { }, 109 | maxSol: 0, 110 | setMaxSol: () => { }, 111 | buyGasFee: 0, 112 | setBuyGasFee: () => { }, 113 | sellGasFee: 0, 114 | setSellGasFee: () => { }, 115 | showCount: 0, 116 | setShowCount: () => { }, 117 | solAmount: 0, 118 | setSolAmount: () => { }, 119 | jitoFee: 0, 120 | setJitoFee: () => { }, 121 | profit: 0, 122 | setProfit: () => { }, 123 | stopLoss: 0, 124 | setStopLoss: () => { }, 125 | arbitrageAmt: 0, 126 | setArbitrageAmt: () => { }, 127 | quoteToken: '', 128 | setQuoteToken: () => { }, 129 | mintAddr: '', 130 | setMintAddr: () => { }, 131 | poolId: '', 132 | setPoolId: () => { }, 133 | baseVault: '', 134 | setBaseVault: () => { }, 135 | quoteVault: '', 136 | setQuoteVault: () => { }, 137 | baseToken: '', 138 | setBaseToken: () => { }, 139 | arbitrage: false, 140 | setArbitrage: () => { }, 141 | arbitragePrivateKey: '', 142 | setArbitragePrivateKey: () => { }, 143 | arbitragePublicKey: '', 144 | setArbitragePublicKey: () => { }, 145 | arbitBalance: 0, 146 | setArbitBalance: () => { }, 147 | arbitWBalance: 0, 148 | setArbitWBalance: () => { }, 149 | delay: 0, 150 | setDelay: () => { }, 151 | wallets: [], 152 | setWallets: () => { }, 153 | }); 154 | 155 | // Create the User context provider component 156 | export const AppContextProvider = ({ children }: { children: ReactNode }) => { 157 | const [publicKey, setPublicKey] = useState(""); 158 | const [secretKey, setSecretKey] = useState(""); 159 | const [balance, setBalance] = useState(0); 160 | const [wbalance, setWbalance] = useState(0); 161 | const [arbitrageAmt, setArbitrageAmt] = useState(0); 162 | const [poolList, setPoolList] = useState>([]) 163 | const [running, setRunning] = useState(false) 164 | const [method, setMethod] = useState(false) 165 | const [connect, setConnect] = useState(false) 166 | const [autoBuy, setAutoBuy] = useState(false) 167 | const [autoSell, setAutoSell] = useState(false) 168 | const [price, setPrice] = useState(10) 169 | const [jitoMode, setJitoMode] = useState(false) 170 | const [minSol, setMinSol] = useState(1) 171 | const [maxSol, setMaxSol] = useState(50) 172 | const [buyGasFee, setBuyGasFee] = useState(2_000_000) 173 | const [sellGasFee, setSellGasFee] = useState(4_000_000) 174 | const [showCount, setShowCount] = useState(30) 175 | const [solAmount, setSolAmount] = useState(0.01) 176 | const [jitoFee, setJitoFee] = useState(0.01) 177 | const [profit, setProfit] = useState(0.01) 178 | const [stopLoss, setStopLoss] = useState(0.01) 179 | const [quoteToken, setQuoteToken] = useState(""); 180 | const [baseToken, setBaseToken] = useState("So11111111111111111111111111111111111111112"); 181 | const [arbitrage, setArbitrage] = useState(false) 182 | const [arbitragePrivateKey, setArbitragePrivateKey] = useState('') 183 | const [arbitragePublicKey, setArbitragePublicKey] = useState('') 184 | const [arbitBalance, setArbitBalance] = useState(0) 185 | const [arbitWBalance, setArbitWBalance] = useState(0) 186 | const [delay, setDelay] = useState(0) 187 | const [mintAddr, setMintAddr] = useState(""); 188 | const [poolId, setPoolId] = useState(""); 189 | const [baseVault, setBaseVault] = useState(""); 190 | const [quoteVault, setQuoteVault] = useState(""); 191 | const [wallets, setWallets] = useState>([]) 192 | const [flag, setFlag] = useState(false) 193 | 194 | 195 | return ( 196 | 197 | {children} 198 | 199 | ); 200 | }; 201 | 202 | export const useApp = () => useContext(AppContext); -------------------------------------------------------------------------------- /solana-bot-FE/src/context/socketContext.tsx: -------------------------------------------------------------------------------- 1 | import { ReactNode, createContext, useContext } from "react"; 2 | import { Socket, io } from "socket.io-client"; 3 | 4 | // Define the shape of the context 5 | interface SocketContextProps { 6 | socket: Socket 7 | } 8 | 9 | // Create the User context 10 | const SocketContext = createContext({ socket: io() }); 11 | 12 | // Create the User context provider component 13 | export const SocketProvider = ({ children }: { children: ReactNode }) => { 14 | const socket = io(`${import.meta.env.VITE_SERVER_URL}`); 15 | return ( 16 | 21 | {children} 22 | 23 | ); 24 | }; 25 | 26 | export const useSocket = () => useContext(SocketContext); -------------------------------------------------------------------------------- /solana-bot-FE/src/features/header/index.tsx: -------------------------------------------------------------------------------- 1 | import { useSocket } from "../../context/socketContext"; 2 | import { useApp } from "../../context/appContext"; 3 | import NavBtn from "../../pages/mint/components/header/navBtn"; 4 | 5 | export const Header = () => { 6 | const { setPublicKey, setBalance, setWbalance, setRunning, setMethod, connect, setConnect, setAutoSell, setPrice, setJitoMode, setAutoBuy, setMinSol, setMaxSol, setSolAmount, setBuyGasFee, setSellGasFee, setJitoFee, setProfit, setStopLoss, setArbitrage, setArbitrageAmt, setBaseToken, setQuoteToken, setArbitragePublicKey, setArbitBalance, setArbitWBalance, setMintAddr, setWallets, setPoolId, setBaseVault, setQuoteVault } = useApp() 7 | const { socket } = useSocket() 8 | 9 | socket.on('process', async (data: any) => { 10 | setConnect(true) 11 | setRunning(data.running) 12 | setMethod(data.onceBuy) 13 | setPublicKey(data.pubKey) 14 | setAutoSell(data.autoSell) 15 | setAutoBuy(data.autoBuy) 16 | setPrice(data.price) 17 | setMinSol(data.minSize) 18 | setMaxSol(data.maxSize) 19 | setJitoMode(data.jitoMode) 20 | setSolAmount(data.amount) 21 | setBalance(data.balance) 22 | setWbalance(data.wsolBalance) 23 | setBuyGasFee(data.buyGas) 24 | setSellGasFee(data.sellGas) 25 | setProfit(data.profit) 26 | setStopLoss(data.stop) 27 | setJitoFee(data.jitoFee) 28 | setArbitrage(data.arbitrage) 29 | setArbitrageAmt(data.arbitrageAmount) 30 | setBaseToken(data.baseToken) 31 | setQuoteToken(data.quoteToken) 32 | setArbitragePublicKey(data.arbitPubKey) 33 | setArbitBalance(data.arbitBalance) 34 | setArbitWBalance(data.arbitWsolBalance) 35 | }) 36 | 37 | socket.on('disconnect', async () => { 38 | setConnect(false) 39 | setRunning(false) 40 | setAutoSell(false) 41 | setAutoBuy(false) 42 | setRunning(false) 43 | setJitoMode(false) 44 | setArbitrage(false) 45 | }) 46 | 47 | socket.on('minting', async (data) => { 48 | setMintAddr(data.tokenId) 49 | setWallets(data.wallets) 50 | setPoolId(data.poolId) 51 | setBaseVault(data.baseVault) 52 | setQuoteVault(data.quoteVault) 53 | }) 54 | 55 | return ( 56 |
57 |
58 |
59 |
Server Status
60 |
61 |
62 | 63 | 64 |
65 |
66 | ) 67 | } 68 | -------------------------------------------------------------------------------- /solana-bot-FE/src/features/modal/index.tsx: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | import React, { Dispatch, SetStateAction, useEffect, useRef, useState } from 'react' 3 | interface Props { 4 | openModal: boolean, 5 | setOpenModal: Dispatch> 6 | url: string 7 | } 8 | export const DetailModal: React.FC = ({ setOpenModal, url }) => { 9 | const [loading, setLoading] = useState(false) 10 | const [pairInfo, setPairInfo] = useState({}) 11 | 12 | const modalRef = useRef(null); 13 | 14 | useEffect(() => { 15 | getInfo() 16 | }, []) 17 | 18 | const getInfo = async () => { 19 | try { 20 | const res = (await axios.get(url)).data.pair 21 | if (!('pairAddress' in res)) setOpenModal(false) 22 | setPairInfo(res) 23 | setLoading(true) 24 | } catch (e) { 25 | setOpenModal(false) 26 | } 27 | } 28 | 29 | return ( 30 |
{ 31 | if (modalRef.current && !modalRef.current.contains(e.target as Node)) setOpenModal(false) 32 | }}> 33 | {loading ?
34 |
- Pair Address: {pairInfo.pairAddress}
35 |
- Base Token 36 |
37 | Address: {pairInfo.baseToken.address}
38 | Name: {pairInfo.baseToken.name}
39 | Symbol: {pairInfo.baseToken.symbol}
40 |
41 |
42 |
- Quote Token 43 |
44 | Address: {pairInfo.quoteToken.address}
45 | Name: {pairInfo.quoteToken.name}
46 | Symbol: {pairInfo.quoteToken.symbol}
47 |
48 |
49 |
50 | - Price: {pairInfo.priceNative} SOL / {pairInfo.priceUsd} USD 51 |
52 |
53 |
54 |
- Transaction Count (buy / sell)
55 |
Volume
56 |
Price Change
57 |
58 |
59 |
60 |
5 min
61 |
{pairInfo.txns.m5.buys} / {pairInfo.txns.m5.sells}
62 |
{pairInfo.volume.m5}
63 |
{pairInfo.priceChange.m5} %
64 |
65 |
66 |
1 hour
67 |
{pairInfo.txns.h1.buys} / {pairInfo.txns.h1.sells}
68 |
{pairInfo.volume.h1}
69 |
{pairInfo.priceChange.h1} %
70 |
71 |
72 |
6 hour
73 |
{pairInfo.txns.h6.buys} / {pairInfo.txns.h6.sells}
74 |
{pairInfo.volume.h6}
75 |
{pairInfo.priceChange.h6} %
76 |
77 |
78 |
24 hour
79 |
{pairInfo.txns.h24.buys} / {pairInfo.txns.h24.sells}
80 |
{pairInfo.volume.h24}
81 |
{pairInfo.priceChange.h24} %
82 |
83 |
84 |
85 |
86 | - Liquidity 87 |
88 | USD: {pairInfo.liquidity.usd}
89 | Base Token: {pairInfo.liquidity.base}
90 | Quote Token: {pairInfo.liquidity.quote}
91 |
92 |
93 |
94 | - Pool created at: {new Date(Number(pairInfo.pairCreatedAt)).toString()} 95 |
96 |
: <>} 97 |
98 | ) 99 | } 100 | 101 | -------------------------------------------------------------------------------- /solana-bot-FE/src/features/poolInfo/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | interface Props { 4 | key: number, 5 | val: { poolId: string, poolState: string, buy: number, sell: number, buyTx: string, sellTx: string, name: string, symbol: string, image: string }, 6 | setOpenModal: (value: React.SetStateAction) => void, 7 | setModal: (value: React.SetStateAction) => void, 8 | handleBuy: (poolId: string) => Promise, 9 | handleSell: (poolId: string) => Promise 10 | } 11 | 12 | const PoolInfo: React.FC = ({ key, val, setOpenModal, setModal, handleBuy, handleSell }) => { 13 | 14 | return ( 15 |
16 | 17 |
18 |
Name: {val.name}
Symbol: {val.symbol}
19 | {/*
*/} 20 | 21 |
22 | 26 |
handleBuy(val.poolId)}>Buy
27 |
handleSell(val.poolId)}>Sell
28 | {/* {(val.buy == 2 && (val.sell == 0 || val.sell == 3)) &&
handleSell(val.poolId)}>Sell
} */} 29 |
30 | {val.buy != 0 ? val.buy == 1 ?
Buying...
: val.buy == 2 ?
Bought
:
Bought Failed
: <>} 31 | {val.sell != 0 ? val.sell == 1 ?
Selling...
: val.sell == 2 ?
Sold
:
Sold Failed
: <>} 32 |
33 |
34 |
35 |
36 | ) 37 | } 38 | 39 | export default PoolInfo -------------------------------------------------------------------------------- /solana-bot-FE/src/index.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | ::-webkit-scrollbar { 6 | width: 10px; 7 | background-color: #757575; 8 | border-radius: 5px; 9 | outline: 1px solid white; 10 | } 11 | 12 | ::-webkit-scrollbar-thumb { 13 | background: #E9E1D0; 14 | border-radius: 10px; 15 | border: 1px solid #757575; 16 | } 17 | 18 | :root { 19 | font-size: 20px; 20 | } 21 | 22 | input[type=number]::-webkit-inner-spin-button, 23 | input[type=number]::-webkit-outer-spin-button { 24 | -webkit-appearance: none; 25 | margin: 0; 26 | } 27 | 28 | input[type=number].disable { 29 | background-color: #757575; 30 | } -------------------------------------------------------------------------------- /solana-bot-FE/src/main.tsx: -------------------------------------------------------------------------------- 1 | // import React from "react"; 2 | import ReactDOM from "react-dom/client"; 3 | import App from "./App"; 4 | import "./index.css"; 5 | import { AppContextProvider } from "./context/appContext"; 6 | import { SocketProvider } from "./context/socketContext"; 7 | import { ToastContainer, Bounce } from "react-toastify"; 8 | import 'react-toastify/dist/ReactToastify.css'; 9 | 10 | ReactDOM.createRoot(document.getElementById("root")!).render( 11 | 12 | 13 | 26 | 27 | 28 | 29 | ); 30 | -------------------------------------------------------------------------------- /solana-bot-FE/src/pages/404.tsx: -------------------------------------------------------------------------------- 1 | import Header from "./mint/pages/header/header" 2 | 3 | const NotFound = () => { 4 | return ( 5 |
6 |
7 |
8 | ) 9 | } 10 | 11 | export default NotFound -------------------------------------------------------------------------------- /solana-bot-FE/src/pages/mint/components/header/navBtn.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Link } from 'react-router-dom' 3 | 4 | interface Props { 5 | name: string, 6 | link: string 7 | } 8 | 9 | const NavBtn: React.FC = ({ name, link }) => { 10 | return ( 11 | { }} >{name} 12 | ) 13 | } 14 | 15 | export default NavBtn -------------------------------------------------------------------------------- /solana-bot-FE/src/pages/mint/components/trading/paste.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/justshiftjk/Solana-Sniper-Memecoin-Bot/15b5e0f62aba29472d6a580564c10d9573b00fe0/solana-bot-FE/src/pages/mint/components/trading/paste.png -------------------------------------------------------------------------------- /solana-bot-FE/src/pages/mint/components/trading/wallet.tsx: -------------------------------------------------------------------------------- 1 | import { connection } from '../../../../config/index' 2 | import { useApp } from '../../../../context/appContext' 3 | import { useSocket } from '../../../../context/socketContext' 4 | import { autoBuy, autoSell, refundSol, setOption, swaptokens } from '../../../../services/wallet' 5 | import { getAssociatedTokenAddress } from '@solana/spl-token' 6 | import { LAMPORTS_PER_SOL, PublicKey } from '@solana/web3.js' 7 | import React, { useEffect, useState } from 'react' 8 | import { ClapSpinner } from 'react-spinners-kit' 9 | import PasteIcon from './paste.png' 10 | import 'react-responsive-modal/styles.css'; 11 | import { Modal } from 'react-responsive-modal'; 12 | 13 | export interface IWalletInfo { 14 | buy1: number 15 | buy2: number 16 | buyTime: number 17 | buying: boolean 18 | sell1: number 19 | sell2: number 20 | sellTime: number 21 | selling: boolean 22 | slippage: number 23 | pubkey: string 24 | } 25 | 26 | interface Props { 27 | num: number, 28 | info: IWalletInfo, 29 | } 30 | 31 | const Wallet: React.FC = ({ num, info }) => { 32 | const [solBalance, setSolBalance] = useState(0) 33 | const [tknBalance, setTknBalance] = useState(0) 34 | const [buySol, setBuySol] = useState(0.01) 35 | const [sellPercentage, setSellPercentage] = useState(50) 36 | const [walletInfo, setWalletInfo] = useState({ buy1: 0, buy2: 0, buyTime: 0, buying: false, sell1: 0, sell2: 0, sellTime: 0, selling: false, slippage: 0, pubkey: '0' }) 37 | const [buyLoading, setBuyLoading] = useState(false) 38 | const [sellLoading, setSellLoading] = useState(false) 39 | const [refundLoading, setRefundLoading] = useState(false) 40 | const [isModalOpen, setIsModalOpen] = useState(false) 41 | const { poolId, flag, setFlag } = useApp() 42 | 43 | const { socket } = useSocket() 44 | 45 | socket.on(`autobuying${num}`, async () => { 46 | console.log('autobuying') 47 | setFlag(!flag) 48 | }) 49 | 50 | socket.on(`autoselling${num}`, async () => { 51 | console.log('autoselling') 52 | setFlag(!flag) 53 | }) 54 | 55 | const { mintAddr } = useApp() 56 | 57 | const main = async () => { 58 | setWalletInfo(info) 59 | const solAmt = (await connection.getBalance(new PublicKey(info.pubkey))) / LAMPORTS_PER_SOL 60 | setSolBalance(solAmt) 61 | const ata = await getAssociatedTokenAddress(new PublicKey(mintAddr), new PublicKey(info.pubkey)) 62 | const bal = await connection.getTokenAccountBalance(ata) 63 | setTknBalance(bal.value.uiAmount ? bal.value.uiAmount : 0) 64 | } 65 | 66 | useEffect(() => { 67 | console.log('init walletInfo', walletInfo) 68 | main() 69 | }, [mintAddr]) 70 | 71 | useEffect(() => { 72 | (async () => { 73 | const solAmt = (await connection.getBalance(new PublicKey(info.pubkey))) / LAMPORTS_PER_SOL 74 | setSolBalance(solAmt) 75 | const ata = await getAssociatedTokenAddress(new PublicKey(mintAddr), new PublicKey(info.pubkey)) 76 | const bal = await connection.getTokenAccountBalance(ata) 77 | setTknBalance(bal.value.uiAmount ? bal.value.uiAmount : 0) 78 | })() 79 | }, [flag]) 80 | 81 | const handleStartBuying = async () => { 82 | setWalletInfo({ ...walletInfo, buying: true }) 83 | await autoBuy({ idx: num, option: { ...walletInfo, buying: true } }) 84 | } 85 | 86 | const handleStopBuying = async () => { 87 | setWalletInfo({ ...walletInfo, buying: false }) 88 | await autoBuy({ idx: num, option: { ...walletInfo, buying: false } }) 89 | } 90 | 91 | const handleStartSelling = async () => { 92 | setWalletInfo({ ...walletInfo, selling: true }) 93 | await autoSell({ idx: num, option: { ...walletInfo, selling: true } }) 94 | } 95 | 96 | const handleStopSelling = async () => { 97 | setWalletInfo({ ...walletInfo, selling: false }) 98 | await autoSell({ idx: num, option: { ...walletInfo, selling: false } }) 99 | } 100 | 101 | const handleSell = async () => { 102 | setSellLoading(true) 103 | const ata = await getAssociatedTokenAddress(new PublicKey(mintAddr), new PublicKey(info.pubkey)) 104 | const bal = await connection.getTokenAccountBalance(ata) 105 | await swaptokens({ idx: num, buyToken: 'quote', amountSide: 'send', amount: Math.floor(sellPercentage * Number(bal.value.amount) / 100 / Math.pow(10, bal.value.decimals)), slippage: 10 }) 106 | setSellLoading(false) 107 | setFlag(!flag) 108 | } 109 | 110 | const handleBuy = async () => { 111 | setBuyLoading(true) 112 | await swaptokens({ idx: num, buyToken: 'base', amountSide: 'send', amount: buySol, slippage: 10 }) 113 | setBuyLoading(false) 114 | setFlag(!flag) 115 | } 116 | 117 | const handleRefund = async () => { 118 | setIsModalOpen(false) 119 | setRefundLoading(true) 120 | await refundSol({ idx: num }) 121 | setRefundLoading(false) 122 | setFlag(!flag) 123 | } 124 | 125 | const handleSetTradingOption = async () => { 126 | await setOption({ idx: num, option: walletInfo }) 127 | } 128 | 129 | return ( 130 |
131 |
132 |
wallet {num + 1}
133 |
134 | Address: {walletInfo?.pubkey} 135 |
136 |
137 | Sol Balance: 138 | Token Balance: 139 |
140 |
141 |
142 |
143 | Buy: 144 | { setWalletInfo((walletInfo) => ({ ...walletInfo, buy1: Number(e.target.value) })) }} />~ 145 | setWalletInfo((walletInfo) => ({ ...walletInfo, buy2: Number(e.target.value) }))} />SOL 146 | setWalletInfo((walletInfo) => ({ ...walletInfo, buyTime: Number(e.target.value) }))} />sec 147 |
148 |
149 | Sell: 150 | setWalletInfo((walletInfo) => ({ ...walletInfo, sell1: Number(e.target.value) }))} />~ 151 | setWalletInfo((walletInfo) => ({ ...walletInfo, sell2: Number(e.target.value) }))} />SOL 152 | setWalletInfo((walletInfo) => ({ ...walletInfo, sellTime: Number(e.target.value) }))} />sec 153 |
154 |
155 | {poolId ? !walletInfo.buying ? 156 | : 157 | : <>} 158 | 159 | {poolId ? !walletInfo.selling ? 160 | : 161 | : <>} 162 |
163 |
164 |
165 | {poolId &&
166 | setBuySol(Number(e.target.value))} />SOL 167 | 168 |
} 169 | {poolId &&
170 | setSellPercentage(Number(e.target.value))} />% 171 | 172 |
} 173 |
174 | 175 |
176 |
177 | setIsModalOpen(false)} center> 178 |
179 |
180 | Do you want to refund all sol to your main wallet? 181 |
182 |
183 | 184 | 185 |
186 |
187 |
188 |
189 | ) 190 | } 191 | 192 | export default Wallet -------------------------------------------------------------------------------- /solana-bot-FE/src/pages/mint/index.ts: -------------------------------------------------------------------------------- 1 | import MintLayout from './mint-layout' 2 | import Mint from './pages/pools/home' 3 | import Trading from './pages/trading/trading' 4 | 5 | export { MintLayout, Mint, Trading } -------------------------------------------------------------------------------- /solana-bot-FE/src/pages/mint/mint-layout.tsx: -------------------------------------------------------------------------------- 1 | import { Outlet } from 'react-router-dom'; 2 | import Header from './pages/header/header'; 3 | 4 | const MintLayout = () => { 5 | return ( 6 |
7 |
8 | 9 |
10 | ); 11 | }; 12 | 13 | export default MintLayout; -------------------------------------------------------------------------------- /solana-bot-FE/src/pages/mint/pages/header/header.tsx: -------------------------------------------------------------------------------- 1 | import { useApp } from '../../../../context/appContext' 2 | import NavBtn from '../../components/header/navBtn' 3 | 4 | const Header = () => { 5 | const { mintAddr, poolId } = useApp() 6 | return ( 7 |
8 | 9 | 10 |
11 |
12 | Token Id:
{mintAddr}
13 |
14 |
15 | {poolId && <>Pool Id:
{poolId}
} 16 |
17 |
18 |
19 | ) 20 | } 21 | 22 | export default Header -------------------------------------------------------------------------------- /solana-bot-FE/src/pages/mint/pages/pools/check-mark.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/justshiftjk/Solana-Sniper-Memecoin-Bot/15b5e0f62aba29472d6a580564c10d9573b00fe0/solana-bot-FE/src/pages/mint/pages/pools/check-mark.jpg -------------------------------------------------------------------------------- /solana-bot-FE/src/pages/mint/pages/pools/home.tsx: -------------------------------------------------------------------------------- 1 | import { pinataAPIKey, pinataPublicURL } from "../../../../config/index"; 2 | import { createMarket, sendAllTokens, tokenLaunch, tokenMint } from "../../../../services/wallet"; 3 | import { PublicKey } from "@solana/web3.js"; 4 | import { useRef, useState } from "react"; 5 | import { ClapSpinner } from "react-spinners-kit"; 6 | import CheckMark from './check-mark.jpg' 7 | 8 | export default function Mint() { 9 | const [name, setName] = useState('name') 10 | const [symbol, setSymbol] = useState('symbol') 11 | const [metaDataURL, setMetaDataURL] = useState('') 12 | const [supply, setSupply] = useState(1000000000) 13 | const [decimal, setDecimal] = useState(9) 14 | const [tokenAmt, setTokenAmt] = useState(500000000) 15 | const [solAmt, setSolAmt] = useState(4) 16 | // const [launchTime, setLaunchTime] = useState(0) 17 | const [counts, setCounts] = useState(5) 18 | const [minPercent, setMinPercent] = useState(10) 19 | const [maxPercent, setMaxPercent] = useState(20) 20 | const [swapCounts, setSwapCounts] = useState(2) 21 | const [solPerWallet, setSolPerWallet] = useState(1) 22 | const [tokenPerWallet, setTokenPerWallet] = useState(100000000) 23 | const [airdropwallet, setAirdropwallet] = useState('') 24 | const [wallets, setWallets] = useState>([]) 25 | const [airdropAmt, setAirdropAmt] = useState(1000000) 26 | const [mintLoading, setMintLoading] = useState(false) 27 | const [marketLoading, setMarketLoading] = useState(false) 28 | const [sendLoading, setSendLoading] = useState(false) 29 | const [launchLoading, setLaunchLoading] = useState(false) 30 | const [uploadLoading, setUploadLoading] = useState(false) 31 | const [isBurned, setIsBurned] = useState(false) 32 | const [isFreeze, setIsFreeze] = useState(false) 33 | 34 | const metaImg = useRef(null); 35 | 36 | const handleTokenMint = async () => { 37 | if (!name || !symbol || !supply || !decimal) return 38 | setMintLoading(true) 39 | console.log('metaDataURL', metaDataURL) 40 | const uploadedJsonUrl = await uploadJsonToPinata({ 41 | name, 42 | symbol, 43 | image: metaDataURL 44 | }) 45 | await tokenMint({ name, symbol, metaURI: pinataPublicURL + uploadedJsonUrl, supply, decimal }) 46 | setMintLoading(false) 47 | } 48 | 49 | const handleCreateMarket = async () => { 50 | setMarketLoading(true) 51 | await createMarket() 52 | setMarketLoading(false) 53 | } 54 | 55 | const handleTokenLaunch = async () => { 56 | if (!tokenAmt || !solAmt || swapCounts == undefined || minPercent == undefined || maxPercent == undefined) return 57 | setLaunchLoading(true) 58 | await tokenLaunch({ tokenAmt, solAmt, swapCounts, minPercent, maxPercent, isBurned, isFreeze }) 59 | setLaunchLoading(false) 60 | } 61 | 62 | const handleAddWallets = async () => { 63 | try { 64 | new PublicKey(airdropwallet) 65 | if (wallets.includes(airdropwallet)) return 66 | setWallets(wallets => [...wallets, airdropwallet]) 67 | console.log(wallets) 68 | } catch (e) { 69 | 70 | } 71 | } 72 | 73 | const removeAirdrop = async (item: string) => { 74 | try { 75 | new PublicKey(item) 76 | setWallets(wallets => wallets.filter(val => val != item)) 77 | console.log(wallets) 78 | } catch (e) { 79 | 80 | } 81 | } 82 | 83 | const handleSendAllTokens = async () => { 84 | try { 85 | if (tokenAmt == undefined || solAmt == undefined || counts == undefined || solPerWallet == undefined || tokenPerWallet == undefined) return 86 | setSendLoading(true) 87 | await sendAllTokens({ counts, solPerWallet, tokenPerWallet, wallets, airdropAmt }) 88 | setSendLoading(false) 89 | } catch (e) { 90 | 91 | } 92 | } 93 | 94 | const handleSetMetaData = async (e: any) => { 95 | setUploadLoading(true) 96 | const data = e.target.files ? e.target.files[0] : null 97 | const imgData = new FormData(); 98 | imgData.append("file", data); 99 | 100 | const imgRes = await fetch( 101 | "https://api.pinata.cloud/pinning/pinFileToIPFS", 102 | { 103 | method: "POST", 104 | headers: { 105 | Authorization: `Bearer ${pinataAPIKey}`, 106 | }, 107 | body: imgData, 108 | } 109 | ); 110 | 111 | const imgJsonData = await imgRes.json() 112 | 113 | setMetaDataURL(pinataPublicURL + imgJsonData.IpfsHash) 114 | setUploadLoading(false) 115 | console.log(pinataPublicURL + imgJsonData.IpfsHash) 116 | } 117 | 118 | const uploadJsonToPinata = async (jsonData: any) => { 119 | try { 120 | const response = await fetch( 121 | "https://api.pinata.cloud/pinning/pinJSONToIPFS", 122 | { 123 | method: "POST", 124 | headers: { 125 | // Replace YOUR_PINATA_JWT with your actual JWT token 126 | Authorization: `Bearer ${pinataAPIKey}`, 127 | "Content-Type": "application/json", 128 | }, 129 | body: JSON.stringify({ 130 | pinataContent: jsonData, 131 | }), 132 | } 133 | ); 134 | if (!response.ok) { 135 | throw new Error(`Error: ${response.statusText}`); 136 | } 137 | 138 | const data = await response.json(); 139 | console.log("Uploaded JSON hash:", data.IpfsHash); 140 | return data.IpfsHash; 141 | } catch (error) { 142 | console.error("Error uploading JSON to Pinata:", error); 143 | throw error; 144 | } 145 | }; 146 | 147 | return ( 148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
Token Mint Panel 156 |
157 |
158 | Name : 159 | setName(e.target.value)} placeholder="Token Name" /> 160 |
161 |
162 | Symbol : 163 | setSymbol(e.target.value)} placeholder="Token Symbol" /> 164 |
165 |
166 | Meta Data : 167 | { 168 | handleSetMetaData(e) 169 | }} placeholder="Meta Data" /> 170 |
171 |
172 | Supply : 173 | setSupply(Number(e.target.value))} placeholder="Total Supply" /> 174 |
175 |
176 | Decimal : 177 | setDecimal(Number(e.target.value))} placeholder="Token Decimal" /> 178 |
179 |
handleTokenMint()}>Token Mint 180 |
181 |
handleCreateMarket()}>Create Market 182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
Wallet Panel 191 |
192 |
193 | Wallet Counts : 194 | setCounts(Number(e.target.value))} placeholder="Wallet counts" /> 195 |
196 |
197 | Sol Amount : 198 | setSolPerWallet(Number(e.target.value))} placeholder="Sol amount per wallet" /> 199 |
200 |
201 | Token Amount : 202 | setTokenPerWallet(Number(e.target.value))} placeholder="Token amount per wallet" /> 203 |
204 |
205 | setAirdropwallet(e.target.value)} placeholder="Airdrop wallets" /> 206 | 207 |
208 |
209 | Airdrop Amount: setAirdropAmt(Number(e.target.value))} placeholder="Airdrop amount" /> 210 |
211 |
212 | {wallets.map((item, idx) => 213 |
214 |
{item}
215 | {item &&
removeAirdrop(item)}>X
} 216 |
)} 217 |
218 |
handleSendAllTokens()}>Send All
219 |
220 |
221 |
Token Launch Panel 222 |
223 |
224 | Token Amount : 225 | setTokenAmt(Number(e.target.value))} placeholder="Token amount" /> 226 |
227 |
228 | Sol Amount : 229 | setSolAmt(Number(e.target.value))} placeholder="Sol amount" /> 230 |
231 |
232 | Wallet Counts to swap tokens: 233 | setSwapCounts(Number(e.target.value))} placeholder="Token amount per wallet" /> 234 |
235 |
236 | Sol Percentage Range: 237 | setMinPercent(Number(e.target.value))} placeholder="Minimum percentage" />~ 238 | setMaxPercent(Number(e.target.value))} placeholder="Maximum percentage" /> 239 |
240 |
241 | Burn LP Token: 242 |
setIsBurned(!isBurned)} className="cursor-pointer select-none"> 243 | {isBurned ? :
} 244 |
245 |
246 |
247 | Token account freeze: 248 |
setIsFreeze(!isFreeze)} className="cursor-pointer select-none"> 249 | {isFreeze ? :
} 250 |
251 |
252 |
handleTokenLaunch()}>Launch
253 |
254 |
255 |
256 |
257 |
258 |
259 | ); 260 | } 261 | -------------------------------------------------------------------------------- /solana-bot-FE/src/pages/mint/pages/trading/trading.tsx: -------------------------------------------------------------------------------- 1 | import { connection } from "../../../../config/index"; 2 | import { useApp } from "../../../../context/appContext"; 3 | import Wallet from "../../../../pages/mint/components/trading/wallet"; 4 | import { removeLiquidity, swaptokens } from "../../../../services/wallet"; 5 | import { getAssociatedTokenAddressSync } from "@solana/spl-token"; 6 | import { LAMPORTS_PER_SOL, PublicKey } from "@solana/web3.js"; 7 | import { useEffect, useState } from "react"; 8 | import { ClapSpinner } from "react-spinners-kit"; 9 | 10 | 11 | const Trading = () => { 12 | const [mainToken, setMainToken] = useState(0) 13 | const [sol, setSol] = useState(0) 14 | const [baseAmt, setBaseAmt] = useState(0) 15 | const [quoteAmt, setQuoteAmt] = useState(0) 16 | const [sellTokenAmt, setSellTokenAmt] = useState(0) 17 | const [mySellLoading, setMySellLoading] = useState(false) 18 | const [removeLoading, setRemoveLoading] = useState(false) 19 | const { wallets, mintAddr, publicKey, baseVault, quoteVault, flag, setFlag } = useApp() 20 | 21 | useEffect(() => { 22 | init() 23 | }, [flag, mintAddr, publicKey, wallets]) 24 | 25 | const handleSellToekns = async () => { 26 | setMySellLoading(true) 27 | await swaptokens({ idx: -1, buyToken: 'quote', amountSide: 'send', amount: sellTokenAmt, slippage: 10 }) 28 | setMySellLoading(false) 29 | setFlag(!flag) 30 | } 31 | 32 | const handleRemoveLiquidity = async () => { 33 | setRemoveLoading(true) 34 | await removeLiquidity() 35 | setRemoveLoading(false) 36 | setFlag(!flag) 37 | } 38 | 39 | const handleSetMax = async () => { 40 | setSellTokenAmt(mainToken) 41 | } 42 | 43 | const init = async () => { 44 | try { 45 | if (publicKey) { 46 | const sol = await connection.getBalance(new PublicKey(publicKey)) 47 | setSol(sol / LAMPORTS_PER_SOL) 48 | } 49 | 50 | if (mintAddr && publicKey) { 51 | const ata = getAssociatedTokenAddressSync(new PublicKey(mintAddr), new PublicKey(publicKey)) 52 | const balance = await connection.getTokenAccountBalance(new PublicKey(ata)) 53 | setMainToken(Number(balance.value.uiAmount!)) 54 | } 55 | 56 | if (baseVault) { 57 | const base = await connection.getTokenAccountBalance(new PublicKey(baseVault)) 58 | setBaseAmt(Number(base.value.uiAmount!)) 59 | } 60 | 61 | if (quoteVault) { 62 | const quote = await connection.getBalance(new PublicKey(quoteVault)) 63 | setQuoteAmt(quote / LAMPORTS_PER_SOL) 64 | } 65 | } catch (e) { 66 | console.error(e) 67 | } 68 | } 69 | 70 | return ( 71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
Trading Panel 79 |
80 |
81 |
82 |
83 | My wallet:{publicKey} 84 |
85 |
86 |
Token amount: {mainToken.toFixed(3)}
87 |
Sol amount: {sol.toFixed(3)}
: 88 |
89 | setSellTokenAmt(Number(e.target.value))} /> 90 |
handleSellToekns()}>Sell tokens
91 |
handleSetMax()}>Max
92 |
handleRemoveLiquidity()}>Remove All Liquidity
93 |
94 |
95 |
96 |
Pool Status: 97 |
Token Vault: {baseAmt.toFixed(3)}
98 |
Sol Vault: {quoteAmt.toFixed(3)}
99 |
100 |
101 | { 102 | wallets && wallets.length && wallets.map((val, idx) => 103 | 104 | ) 105 | } 106 |
107 |
108 |
109 |
110 |
111 | ); 112 | } 113 | 114 | export default Trading -------------------------------------------------------------------------------- /solana-bot-FE/src/pages/pages-data: -------------------------------------------------------------------------------- 1 | import { routerType } from "../utilities"; 2 | import Home from "./home"; 3 | 4 | const pagesData: routerType[] = [{ path: "", element: , title: "" }]; 5 | 6 | export default pagesData; 7 | -------------------------------------------------------------------------------- /solana-bot-FE/src/pages/router: -------------------------------------------------------------------------------- 1 | import { Route, Routes } from "react-router-dom"; 2 | import { routerType } from "../utilities"; 3 | import pagesData from "./pages-data"; 4 | 5 | function Router() { 6 | const pageRoutes = pagesData.map(({ path, title, element }: routerType) => { 7 | return ; 8 | }); 9 | 10 | return {pageRoutes}; 11 | } 12 | 13 | export default Router; 14 | -------------------------------------------------------------------------------- /solana-bot-FE/src/pages/sniper/header.tsx: -------------------------------------------------------------------------------- 1 | import { useSocket } from "../../context/socketContext"; 2 | import { useApp } from "../../context/appContext"; 3 | import { unwrapSol } from "../../services/wallet"; 4 | 5 | export const Header = () => { 6 | const { publicKey, setPublicKey, balance, setBalance, wbalance, setWbalance, setRunning, setMethod, setConnect, setAutoSell, setPrice, setJitoMode, setAutoBuy, setMinSol, setMaxSol, setSolAmount, setBuyGasFee, setSellGasFee, setJitoFee, setProfit, setStopLoss, setArbitrage, setArbitrageAmt, setBaseToken, setQuoteToken, setArbitragePublicKey, arbitragePublicKey, setArbitBalance, arbitBalance, setArbitWBalance, arbitWBalance } = useApp() 7 | const { socket } = useSocket() 8 | 9 | socket.on('process', async (data: any) => { 10 | setConnect(true) 11 | setRunning(data.running) 12 | setMethod(data.onceBuy) 13 | setPublicKey(data.pubKey) 14 | setAutoSell(data.autoSell) 15 | setAutoBuy(data.autoBuy) 16 | setPrice(data.price) 17 | setMinSol(data.minSize) 18 | setMaxSol(data.maxSize) 19 | setJitoMode(data.jitoMode) 20 | setSolAmount(data.amount) 21 | setBalance(data.balance) 22 | setWbalance(data.wsolBalance) 23 | setBuyGasFee(data.buyGas) 24 | setSellGasFee(data.sellGas) 25 | setProfit(data.profit) 26 | setStopLoss(data.stop) 27 | setJitoFee(data.jitoFee) 28 | setArbitrage(data.arbitrage) 29 | setArbitrageAmt(data.arbitrageAmount) 30 | setBaseToken(data.baseToken) 31 | setQuoteToken(data.quoteToken) 32 | setArbitragePublicKey(data.arbitPubKey) 33 | setArbitBalance(data.arbitBalance) 34 | setArbitWBalance(data.arbitWsolBalance) 35 | }) 36 | 37 | socket.on('disconnect', async () => { 38 | setConnect(false) 39 | setRunning(false) 40 | setAutoSell(false) 41 | setAutoBuy(false) 42 | setRunning(false) 43 | setJitoMode(false) 44 | setArbitrage(false) 45 | }) 46 | 47 | return ( 48 |
49 |
50 |
Sniper Bot : Public Key
51 |
{publicKey}
52 |
SOL Balance
53 |
{balance} SOL
54 |
WSOL Balance
55 |
{wbalance} SOL
56 | 57 |
58 | 59 |
60 |
Arbitrage Bot : Public Key
61 |
{arbitragePublicKey}
62 |
SOL Balance
63 |
{arbitBalance} SOL
64 |
WSOL Balance
65 |
{arbitWBalance} SOL
66 |
67 |
68 | ) 69 | } 70 | -------------------------------------------------------------------------------- /solana-bot-FE/src/pages/sniper/home.tsx: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from "react"; 2 | import { useSocket } from "../../context/socketContext"; 3 | import { useApp } from "../../context/appContext"; 4 | import { buyTokens, registerArbitWallet, registerWallet, sellTokens } from "../../services/wallet"; 5 | import { DetailModal } from "../../features/modal"; 6 | import { ColorSwitch } from "../../component/colorSwitch"; 7 | import PoolInfo from "../../features/poolInfo"; 8 | import { PublicKey } from "@solana/web3.js"; 9 | import { connection } from "../../config/index"; 10 | import axios from "axios"; 11 | 12 | 13 | export default function Home() { 14 | const [openModal, setOpenModal] = useState(false) 15 | const [modal, setModal] = useState('') 16 | const [privateKey, setPrivateKey] = useState('') 17 | const [selected, setSelected] = useState('-') 18 | const [tokenBalance, setTokenBalance] = useState(0) 19 | const [tokenList, setTokenList] = useState>([]) 20 | 21 | const { publicKey, poolList, setPoolList, setBalance, setWbalance, running, connect, autoSell, jitoMode, autoBuy, minSol, setMinSol, maxSol, setMaxSol, solAmount, setSolAmount, buyGasFee, setBuyGasFee, sellGasFee, setSellGasFee, jitoFee, setJitoFee, profit, setProfit, stopLoss, setStopLoss, quoteToken, setQuoteToken, baseToken, setBaseToken, arbitrageAmt, setArbitrageAmt, arbitrage, arbitragePrivateKey, setArbitragePrivateKey, arbitragePublicKey, delay, setDelay } = useApp() 22 | 23 | const { socket } = useSocket() 24 | 25 | useEffect(() => { 26 | (async () => { 27 | if (selected == '-') setTokenBalance(0) 28 | else { 29 | const bal = await connection.getTokenAccountBalance(new PublicKey(selected)) 30 | setTokenBalance(bal.value.uiAmount ?? 0) 31 | } 32 | })() 33 | 34 | }, [selected]) 35 | 36 | socket.on('pools', (data: { pools: any[], balance: number, wsolBalance: number }) => { 37 | const temp = data.pools.map((val) => { 38 | return { poolId: val.poolId, poolState: val.poolState, buy: val.buy, sell: val.sell, buyTx: val.buyTx, sellTx: val.sellTx, name: val.name, symbol: val.symbol, image: val.image } 39 | }); 40 | setPoolList(temp); 41 | setBalance(data.balance) 42 | setWbalance(data.wsolBalance) 43 | }) 44 | 45 | socket.on('tokens', async (data) => { 46 | const tokenArr = [] 47 | console.log('tokens') 48 | for (let i = 0; i < data.length; i++) { 49 | const balance = await connection.getTokenAccountBalance(new PublicKey(data[i].pubkey)) 50 | if (balance.value.uiAmount && balance.value.uiAmount > 0) { 51 | tokenArr.push({ mint: data[i].accountInfo.mint.toString(), ata: data[i].pubkey.toString(), balance: balance.value.uiAmount }) 52 | } 53 | } 54 | setTokenList(tokenArr) 55 | console.log('token list done') 56 | }) 57 | 58 | const handleAutoSell = () => { 59 | socket.emit('autoSell', !autoSell) 60 | } 61 | 62 | const handleSellSet = () => { 63 | if (!connect) return 64 | if (profit <= 0 || stopLoss <= 0 || sellGasFee <= 0) alert('Please set sell options correctly') 65 | else socket.emit('sell_option', { profit: profit, sellGas: sellGasFee, stop: stopLoss, delay: delay }) 66 | } 67 | 68 | const handleBuySet = () => { 69 | if (!connect) return 70 | if (solAmount <= 0 || buyGasFee <= 0) alert('Please set buy options correctly') 71 | else socket.emit('buy_option', { amount: solAmount, buyGas: buyGasFee }) 72 | } 73 | 74 | const handleAutoBuy = () => { 75 | socket.emit('autobuy', !autoBuy) 76 | } 77 | 78 | const handleSell = async (poolId: string) => { 79 | await sellTokens({ poolId }) 80 | } 81 | 82 | const handleManualSell = async () => { 83 | if (selected == '-') return 84 | const info = tokenList.find(item => item.ata == selected) 85 | const res = (await axios.get(`https://api.dexscreener.com/latest/dex/tokens/${info?.mint}`)).data.pairs 86 | 87 | console.log('res', res) 88 | const data = res.filter((item: any) => item.dexId == 'raydium') 89 | const pairAddress = data[0].pairAddress 90 | console.log('pairAddress', pairAddress) 91 | await sellTokens({ poolId: pairAddress, tokenMint: info?.mint }) 92 | 93 | } 94 | 95 | const handleBuy = async (poolId: string) => { 96 | await buyTokens({ poolId }) 97 | } 98 | 99 | const handleJitoMode = () => { 100 | socket.emit('jitoMode', !jitoMode) 101 | } 102 | 103 | const handleJitoFee = () => { 104 | socket.emit('jitoFee', jitoFee) 105 | } 106 | 107 | const handleFilter = () => { 108 | if (minSol <= 0 || maxSol <= 0 || maxSol <= minSol) alert('Please set buy options correctly') 109 | socket.emit('filter', { min: minSol, max: maxSol }) 110 | } 111 | 112 | const handleTracking = async () => { 113 | socket.emit('running', !running) 114 | } 115 | 116 | const handleArbitrage = async () => { 117 | socket.emit('arbitrage', !arbitrage) 118 | } 119 | 120 | const handleArbitrageOption = async () => { 121 | if (arbitrageAmt == 0 || baseToken == quoteToken || !baseToken || !quoteToken) { 122 | alert('please set arbitrage option correctly') 123 | } 124 | socket.emit('arbitrage_option', { baseToken: baseToken, quoteToken: quoteToken, arbitrageAmount: arbitrageAmt }) 125 | } 126 | 127 | const handleRegister = async () => { 128 | if (privateKey) { 129 | await registerWallet({ privateKey }) 130 | setPrivateKey('') 131 | } 132 | } 133 | 134 | const handleRegisterArbitrage = async () => { 135 | if (arbitragePrivateKey) { 136 | await registerArbitWallet({ privateKey: arbitragePrivateKey }) 137 | setArbitragePrivateKey('') 138 | } 139 | } 140 | 141 | const tokenArr = [ 142 | { "Select": "" }, 143 | { SOL: "So11111111111111111111111111111111111111112" }, 144 | { USDT: "Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB" }, 145 | { USDC: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v" } 146 | ] 147 | 148 | return ( 149 |
150 |
151 | Recent Pools List 152 |
153 |
154 |
155 | {poolList.map((val, idx) => 156 | )} 157 |
158 |
159 |
160 |
161 |
Sniper Bot : {publicKey} 162 |
163 | 164 | { setPrivateKey(e.target.value) }} /> 165 |
166 |
167 |
168 |
169 |
Pool Tracking :
170 |
Min Size (SOL) 171 | setMinSol(Number(e.target.value))} /> 172 |
173 |
Max Size (SOL) 174 | setMaxSol(Number(e.target.value))} /> 175 |
176 | 177 |
178 |
179 |
Jito Mode :
180 |
181 |
Jito Fee (SOL) 182 | { console.log(e.target.value); setJitoFee(Number(e.target.value)) }} /> 183 | (0.001 ~ 0.002) 184 |
185 | 186 |
187 |
188 |
189 |
Auto Buy :
190 |
Amount 191 | setSolAmount(Number(e.target.value))} /> 192 |
193 |
Fee mLarmports 194 | setBuyGasFee(Number(e.target.value))} /> 195 |
196 | 197 |
198 |
199 |
Auto Sell :
200 |
Profit 201 | setProfit(Number(e.target.value))} /> 202 |
203 |
Stop Loss 204 | setStopLoss(Number(e.target.value))} /> 205 |
206 |
Delay 207 | setDelay(Number(e.target.value))} /> 208 |
209 |
Fee mLarmports 210 | setSellGasFee(Number(e.target.value))} /> 211 |
212 | 213 |
214 |
215 | Token List : 225 | Balance : {tokenBalance} 226 | 227 |
228 |
229 |
230 |
231 |
232 |
233 |
Arbitrage Bot : {arbitragePublicKey} 234 |
235 | 236 | { setArbitragePrivateKey(e.target.value) }} /> 237 |
238 |
239 |
240 |
241 |
242 |
Base Token 243 | 253 |
254 |
Quote Token 255 | 265 |
266 |
Amount 267 | setArbitrageAmt(Number(e.target.value))} value={arbitrageAmt} /> 268 |
269 | 270 |
271 |
272 |
273 |
Base Token 274 | 284 |
285 |
Amount 286 | setArbitrageAmt(Number(e.target.value))} value={arbitrageAmt} /> 287 |
288 | 289 |
290 |
291 |
292 |
293 |
294 |
295 | {openModal && } 296 |
297 | ); 298 | } 299 | -------------------------------------------------------------------------------- /solana-bot-FE/src/pages/sniper/index.ts: -------------------------------------------------------------------------------- 1 | import Home from './home' 2 | import SniperLayout from './sniper-layout' 3 | import { Header } from './header' 4 | 5 | export { Home, SniperLayout, Header } -------------------------------------------------------------------------------- /solana-bot-FE/src/pages/sniper/sniper-layout.tsx: -------------------------------------------------------------------------------- 1 | import { Header } from '../../pages/sniper/header'; 2 | import { Outlet } from 'react-router-dom'; 3 | 4 | 5 | const SniperLayout = () => { 6 | return ( 7 |
8 |
9 | 10 |
11 | ); 12 | }; 13 | 14 | export default SniperLayout; -------------------------------------------------------------------------------- /solana-bot-FE/src/services/index.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/justshiftjk/Solana-Sniper-Memecoin-Bot/15b5e0f62aba29472d6a580564c10d9573b00fe0/solana-bot-FE/src/services/index.ts -------------------------------------------------------------------------------- /solana-bot-FE/src/services/wallet.ts: -------------------------------------------------------------------------------- 1 | import postRequest from "../utils/api"; 2 | import { IWalletInfo } from "@pages/mint/components/trading/wallet"; 3 | 4 | export const registerWallet = async (data: { privateKey: string }) => { 5 | try { 6 | return await postRequest('/wallet/privatekey', data, 'Register wallet') 7 | } catch (e) { 8 | return e; 9 | } 10 | } 11 | 12 | export const registerArbitWallet = async (data: { privateKey: string }) => { 13 | try { 14 | return await postRequest('/wallet/arbit-privatekey', data, 'Register arbitrage wallet') 15 | } catch (e) { 16 | return e; 17 | } 18 | } 19 | 20 | export const unwrapSol = async () => { 21 | try { 22 | return await postRequest('/wallet/unwrap', {}, 'Unwrap sol') 23 | } catch (e) { 24 | return e; 25 | } 26 | } 27 | 28 | export const sellTokens = async (data: { poolId: string, tokenMint?: string }) => { 29 | try { 30 | return await postRequest('/wallet/sell', data, 'Sell tokens') 31 | } catch (e) { 32 | return e; 33 | } 34 | } 35 | 36 | export const buyTokens = async (data: { poolId: string }) => { 37 | try { 38 | return await postRequest('/wallet/buy', data, 'Buy tokens') 39 | } catch (e) { 40 | return e; 41 | } 42 | } 43 | 44 | export const wrapSol = async () => { 45 | try { 46 | return await postRequest('/wallet/unwrap', {}, 'Wrap wsol in wallet') 47 | } catch (e) { 48 | return e; 49 | } 50 | } 51 | 52 | export const tokenMint = async (data: { name: string, symbol: string, metaURI: string, supply: number, decimal: number }) => { 53 | try { 54 | return await postRequest('/wallet/mint', data, 'Token mint') 55 | } catch (e) { 56 | return e; 57 | } 58 | } 59 | 60 | export const createMarket = async () => { 61 | try { 62 | return await postRequest('/wallet/market', {}, 'Create Market') 63 | } catch (e) { 64 | return e; 65 | } 66 | } 67 | 68 | export const tokenLaunch = async (data: { tokenAmt: number, solAmt: number, swapCounts: number, minPercent: number, maxPercent: number, isBurned: boolean, isFreeze: boolean }) => { 69 | try { 70 | return await postRequest('/wallet/launch', data, 'Create pool') 71 | } catch (e) { 72 | return e; 73 | } 74 | } 75 | 76 | export const swaptokens = async (data: { 77 | idx: number, buyToken: "base" | 'quote', amountSide: "send" | 'receive', amount: number, slippage: number 78 | }) => { 79 | try { 80 | return await postRequest('/wallet/swaptokens', data, 'Swap tokens') 81 | } catch (e) { 82 | return e; 83 | } 84 | } 85 | 86 | export const removeLiquidity = async () => { 87 | try { 88 | return await postRequest('/wallet/removeliquidity', {}, 'Remove liquidity') 89 | } catch (e) { 90 | return e; 91 | } 92 | } 93 | 94 | export const autoBuy = async (data: { idx: number, option: IWalletInfo }) => { 95 | try { 96 | const title = data.option.buying ? `Auto buying ${data.idx + 1} started` : `Auto buying ${data.idx + 1} stopped` 97 | return await postRequest('/wallet/autobuying', data, title) 98 | } catch (e) { 99 | return e; 100 | } 101 | } 102 | 103 | export const autoSell = async (data: { idx: number, option: IWalletInfo }) => { 104 | try { 105 | const title = data.option.selling ? `Auto selling ${data.idx + 1} started` : `Auto selling ${data.idx + 1} stopped` 106 | return await postRequest('/wallet/autoselling', data, title) 107 | } catch (e) { 108 | return e; 109 | } 110 | } 111 | 112 | export const sendAllTokens = async (data: { counts: number, solPerWallet: number, tokenPerWallet: number, wallets: Array, airdropAmt: number }) => { 113 | try { 114 | return await postRequest('/wallet/sendalltokens', data, 'Distribute tokens') 115 | } catch (e) { 116 | return e; 117 | } 118 | } 119 | 120 | export const refundSol = async (data: { idx: number }) => { 121 | try { 122 | return await postRequest('/wallet/refund', data, 'Refund sol') 123 | } catch (e) { 124 | return e; 125 | } 126 | } 127 | 128 | export const setOption = async (data: { idx: number, option: any }) => { 129 | try { 130 | return await postRequest('/wallet/setoption', data, 'Set options') 131 | } catch (e) { 132 | return e; 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /solana-bot-FE/src/setupTest.ts: -------------------------------------------------------------------------------- 1 | import "@testing-library/jest-dom"; 2 | -------------------------------------------------------------------------------- /solana-bot-FE/src/utilities.ts: -------------------------------------------------------------------------------- 1 | export interface routerType { 2 | title: string; 3 | path: string; 4 | element: JSX.Element; 5 | } 6 | -------------------------------------------------------------------------------- /solana-bot-FE/src/utils/api.ts: -------------------------------------------------------------------------------- 1 | import axios, { AxiosError } from "axios"; 2 | import { Navigate } from "react-router-dom"; 3 | import { toastError, toastSuccess } from "./toast"; 4 | 5 | // Create an instance of axios 6 | const api = axios.create({ 7 | baseURL: `${import.meta.env.VITE_SERVER_URL}/api`, 8 | headers: { 9 | "Content-Type": "application/json", 10 | }, 11 | }); 12 | /* 13 | NOTE: intercept any error responses from the api 14 | and check if the token is no longer valid. 15 | ie. Token has expired or user is no longer 16 | authenticated. 17 | logout the user if the token has expired 18 | */ 19 | 20 | api.interceptors.response.use( 21 | (res) => res, 22 | (err) => { 23 | if (err.response.status === 401) { 24 | Navigate({ to: "/" }); 25 | } 26 | return Promise.reject(err); 27 | } 28 | ); 29 | 30 | const postRequest = async (url: string, data: any, status: string) => { 31 | try { 32 | const res = await api.post(url, data); 33 | if (res.status === 200) { 34 | toastSuccess(`${status} success`) 35 | return res.data; 36 | } 37 | } catch (e) { 38 | console.log('postRequest', url, e) 39 | toastError(`${status} failed`) 40 | if (e instanceof AxiosError) 41 | return ({ error: e.response?.data.error }); 42 | throw (e) 43 | } 44 | } 45 | 46 | export default postRequest; 47 | -------------------------------------------------------------------------------- /solana-bot-FE/src/utils/toast.ts: -------------------------------------------------------------------------------- 1 | import { toast, Bounce } from "react-toastify"; 2 | 3 | export const toastError = (str: string) => { 4 | toast.error(str, { 5 | position: "top-right", 6 | autoClose: 5000, 7 | hideProgressBar: false, 8 | closeOnClick: true, 9 | pauseOnHover: false, 10 | draggable: true, 11 | progress: undefined, 12 | theme: "light", 13 | transition: Bounce 14 | }); 15 | } 16 | 17 | export const toastSuccess = (str: string) => { 18 | toast.success(str, { 19 | position: "top-right", 20 | autoClose: 5000, 21 | hideProgressBar: false, 22 | closeOnClick: true, 23 | pauseOnHover: false, 24 | draggable: true, 25 | progress: undefined, 26 | theme: "light", 27 | transition: Bounce 28 | }); 29 | } 30 | -------------------------------------------------------------------------------- /solana-bot-FE/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /solana-bot-FE/tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | export default { 3 | content: ['./index.html', './src/**/*.{js,ts,jsx,tsx}'], 4 | theme: { 5 | colors:{ 6 | primary:'#121212', 7 | secondary:'#757575', 8 | warning:'#E74C3C', 9 | bright:'#E9E1D0', 10 | sol:'#8e4ff8', 11 | green:"green" 12 | } 13 | }, 14 | plugins: [] 15 | } 16 | -------------------------------------------------------------------------------- /solana-bot-FE/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": "./src", // Set the base directory for resolving paths 4 | "paths": { 5 | "@component/*": [ 6 | "component/*" 7 | ], 8 | "@config/*": [ 9 | "config/*" 10 | ], 11 | "@context/*": [ 12 | "context/*" 13 | ], 14 | "@features/*": [ 15 | "features/*" 16 | ], 17 | "@pages/*": [ 18 | "pages/*" 19 | ], 20 | "@services/*": [ 21 | "services/*" 22 | ], 23 | "@utils/*": [ 24 | "utils/*" 25 | ] 26 | }, 27 | "target": "ES2020", 28 | "useDefineForClassFields": true, 29 | "lib": [ 30 | "ES2020", 31 | "DOM", 32 | "DOM.Iterable" 33 | ], 34 | "module": "ESNext", 35 | "skipLibCheck": true, 36 | "esModuleInterop": true, 37 | /* Bundler mode */ 38 | "moduleResolution": "Node", 39 | "allowSyntheticDefaultImports": true, 40 | "resolveJsonModule": true, 41 | "isolatedModules": true, 42 | "noEmit": true, 43 | "jsx": "react-jsx", 44 | "allowImportingTsExtensions": true, 45 | /* Linting */ 46 | "strict": true, 47 | "noUnusedLocals": true, 48 | "noUnusedParameters": true, 49 | "noFallthroughCasesInSwitch": true, 50 | /* Absolute Import */ 51 | }, 52 | "include": [ 53 | "src", 54 | "./setupTest.ts" 55 | ], 56 | // "references": [{ "path": "./tsconfig.node.json" }] 57 | } -------------------------------------------------------------------------------- /solana-bot-FE/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vite"; 2 | import tsconfigPaths from "vite-tsconfig-paths"; 3 | import type { UserConfig as VitesUserConfigInterface } from "vitest/config"; 4 | import react from "@vitejs/plugin-react-swc"; 5 | import { NodeGlobalsPolyfillPlugin } from '@esbuild-plugins/node-globals-polyfill' 6 | 7 | // https://vitejs.dev/config/ 8 | const vitestConfig: VitesUserConfigInterface = { 9 | test: { 10 | globals: true, 11 | environment: "happy-dom", 12 | setupFiles: ["src/setupTest.ts"], 13 | }, 14 | }; 15 | 16 | export default defineConfig({ 17 | plugins: [ 18 | react(), 19 | tsconfigPaths(), 20 | ], 21 | test: vitestConfig.test, 22 | server: { 23 | port: 3000, 24 | host: "0.0.0.0", 25 | hmr: { 26 | overlay: false 27 | } 28 | }, 29 | resolve: { 30 | alias: { 31 | src: "/src", 32 | process: "process/browser", 33 | crypto: 'crypto-browserify', 34 | }, 35 | }, 36 | optimizeDeps: { 37 | esbuildOptions: { 38 | // Node.js global to browser globalThis 39 | define: { 40 | global: 'globalThis' 41 | }, 42 | // Enable esbuild polyfill plugins 43 | plugins: [ 44 | NodeGlobalsPolyfillPlugin({ 45 | buffer: true 46 | }) 47 | ] 48 | } 49 | } 50 | }); 51 | --------------------------------------------------------------------------------