├── .gitignore ├── README.md ├── client ├── .dockerignore ├── .env.example ├── .gitignore ├── Dockerfile ├── README.md ├── package-lock.json ├── package.json ├── public │ ├── favicon.png │ ├── index.html │ ├── logo192.png │ ├── logo512.png │ ├── manifest.json │ ├── robots.txt │ └── web.config ├── scripts │ └── postbuild.js └── src │ ├── actions │ ├── index.js │ └── types.js │ ├── assets │ ├── img │ │ ├── Indsoft_logo.png │ │ ├── XinFin_dark.png │ │ ├── XinFin_white.png │ │ ├── blockdegree_dark.png │ │ ├── blockdegree_favicon.png │ │ ├── blockdegree_icon_white.png │ │ ├── blockdegree_white.png │ │ ├── dummy-certi.png │ │ ├── funders.png │ │ ├── xdcFavicons │ │ │ ├── android-icon-144x144.png │ │ │ ├── android-icon-192x192.png │ │ │ ├── android-icon-36x36.png │ │ │ ├── android-icon-48x48.png │ │ │ ├── android-icon-72x72.png │ │ │ ├── android-icon-96x96.png │ │ │ ├── apple-icon-114x114.png │ │ │ ├── apple-icon-120x120.png │ │ │ ├── apple-icon-144x144.png │ │ │ ├── apple-icon-152x152.png │ │ │ ├── apple-icon-180x180.png │ │ │ ├── apple-icon-57x57.png │ │ │ ├── apple-icon-60x60.png │ │ │ ├── apple-icon-72x72.png │ │ │ ├── apple-icon-76x76.png │ │ │ ├── apple-icon-precomposed.png │ │ │ ├── apple-icon.png │ │ │ ├── browserconfig.xml │ │ │ ├── favicon-16x16.png │ │ │ ├── favicon-32x32.png │ │ │ ├── favicon-96x96.png │ │ │ ├── favicon.ico │ │ │ ├── manifest.json │ │ │ ├── ms-icon-144x144.png │ │ │ ├── ms-icon-150x150.png │ │ │ ├── ms-icon-310x310.png │ │ │ └── ms-icon-70x70.png │ │ └── xdc_logo.png │ └── scss │ │ ├── abstracts │ │ ├── _functions.scss │ │ ├── _mixins.scss │ │ └── _variables.scss │ │ ├── base │ │ ├── _animation.scss │ │ ├── _base.scss │ │ ├── _typography.scss │ │ └── _utilities.scss │ │ ├── components │ │ ├── _accordion.scss │ │ ├── _banner.scss │ │ ├── _button.scss │ │ ├── _card.scss │ │ ├── _center-form.scss │ │ ├── _chart.scss │ │ ├── _custom-card.scss │ │ ├── _highlighted.scss │ │ ├── _icon-card.scss │ │ ├── _modal.scss │ │ ├── _orderbook.scss │ │ ├── _render-input.scss │ │ ├── _select.scss │ │ ├── _table.scss │ │ └── _toast.scss │ │ ├── layouts │ │ ├── _footer.scss │ │ ├── _header.scss │ │ ├── _main-panel.scss │ │ └── _sidebar.scss │ │ ├── main.scss │ │ └── pages │ │ ├── _best-price.scss │ │ ├── _crons.scss │ │ ├── _dashboard.scss │ │ ├── _faq.scss │ │ ├── _home.scss │ │ ├── _management.scss │ │ ├── _market-maker.scss │ │ ├── _message-page.scss │ │ ├── _monitor.scss │ │ ├── _profile.scss │ │ └── _signup.scss │ ├── components │ ├── App.js │ ├── Charts │ │ └── LineChart.js │ ├── DataTable.js │ ├── EditCurrencyCard.js │ ├── Exchange.js │ ├── Footer.js │ ├── FormHelper.js │ ├── IconCard.js │ ├── Liquidity.js │ ├── LiquidityDetails.js │ ├── ManageKeys.js │ ├── StaticTable.js │ ├── SuperAdminComponent.js │ └── Wallet.js │ ├── containers │ ├── AdminManagement.js │ ├── AdminProfile.js │ ├── DailyStats.js │ ├── Dashboard.js │ ├── Header.js │ ├── Login.js │ ├── Management.js │ ├── Monitor.js │ ├── Sidebar.js │ └── Signup.js │ ├── helpers │ ├── Notification.js │ ├── PaymentModal.js │ ├── ResponseHelper.js │ ├── constant.js │ └── withRouter.js │ ├── hooks │ ├── RouteButton.js │ └── ScrollToTop.js │ ├── index.js │ ├── middleware │ ├── RequireLogin.js │ └── RequireLogout.js │ ├── reducers │ ├── AdminProfileReducer.js │ ├── AdminReducer.js │ ├── ArbitrageReducer.js │ ├── Auth.js │ ├── DailyStatsReducer.js │ ├── Exchange.js │ ├── LiquidityBotReducer.js │ ├── LiquidityDetailsBotReducer.js │ ├── ManageKeysReducer.js │ ├── MonitorReducer.js │ ├── SocketReducer.js │ └── index.js │ ├── redux │ └── store.js │ ├── serviceWorker.js │ └── setupTests.js ├── docker-compose.yml ├── getLogins.sh ├── install_docker.sh ├── logos ├── bitfinex.png ├── bitrue.png ├── bittrex.png ├── gateio.png ├── huobi.png └── kucoin.png └── server ├── .dockerignore ├── .env.example ├── .gitignore ├── Dockerfile ├── app.js ├── bin └── www ├── controllers ├── adminController.js ├── cronController.js ├── indexController.js ├── spreadBotController-Old.js └── spreadBotController.js ├── crons ├── scheduledCrons.js └── walletBalanceCron.js ├── helpers ├── MAIL.js ├── RESPONSE.js ├── axiosHelper.js ├── commonHelper.js ├── constant.js ├── creds.json ├── crypto.js ├── data.json ├── databaseHelpers │ └── adminHelper.js ├── decryptedEnv.js ├── errors.js ├── exchangeHelpers │ ├── bitfinex.js │ ├── bitrue.js │ ├── bittrex.js │ ├── gateio.js │ ├── huobi.js │ └── kucoin.js ├── exchangeSocket │ └── bitfinex.js ├── globals.js ├── initialSetup.js ├── logger.js ├── networkLogger.js ├── orderPlacement.js ├── socketClass │ └── wsClass.js └── socket_io.js ├── middlewares ├── errorHandler.js ├── requireAdmin.js ├── requireSuperAdmin.js └── validateRequest.js ├── models ├── admin.js ├── arbitrageOperations.js ├── completedOrders.js ├── dailyData.js ├── dailyStats.js ├── dailyWalletBalances.js ├── exchangeCurrencies.js ├── exchangeData.js ├── exchangePair.js ├── exchangeWalletSnapshot.js ├── spreadBotDetails.js ├── spreadBotGeneratedOrders.js ├── spreadBotMaintainOrders.js └── spreadBotOrders.js ├── package-lock.json ├── package.json ├── public └── stylesheets │ └── style.css ├── routes ├── admin.js ├── index.js └── spreadBot.js ├── services └── redis.js └── views ├── error.jade ├── index.jade └── layout.jade /.gitignore: -------------------------------------------------------------------------------- 1 | /mongodb-data 2 | .env -------------------------------------------------------------------------------- /client/.dockerignore: -------------------------------------------------------------------------------- 1 | node_modules/ -------------------------------------------------------------------------------- /client/.env.example: -------------------------------------------------------------------------------- 1 | REACT_APP_URL=http://localhost:5000/api/ 2 | REACT_APP_WSS=ws://localhost:3002 3 | REACT_APP_NAME=MMBot -------------------------------------------------------------------------------- /client/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .env 16 | .DS_Store 17 | .env.local 18 | .env.development.local 19 | .env.test.local 20 | .env.production.local 21 | 22 | npm-debug.log* 23 | yarn-debug.log* 24 | yarn-error.log* 25 | 26 | serving-build 27 | -------------------------------------------------------------------------------- /client/Dockerfile: -------------------------------------------------------------------------------- 1 | # Use an official Node.js runtime as the base image 2 | FROM node:14 3 | 4 | # Set the working directory in the container 5 | WORKDIR /app 6 | 7 | # Copy package.json and package-lock.json to the container 8 | COPY package*.json ./ 9 | 10 | # Install app dependencies 11 | RUN npm install 12 | 13 | # Copy the rest of the application code to the container 14 | COPY . . 15 | 16 | # Build the React app 17 | # RUN npm run build 18 | 19 | # Command to start the app 20 | CMD ["npm", "start"] 21 | -------------------------------------------------------------------------------- /client/README.md: -------------------------------------------------------------------------------- 1 | This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app). 2 | 3 | ## Available Scripts 4 | 5 | In the project directory, you can run: 6 | 7 | ### `npm start` 8 | 9 | Runs the app in the development mode.
10 | Open [http://localhost:300](http://localhost:3000) to view it in the browser. 11 | 12 | The page will reload if you make edits.
13 | You will also see any lint errors in the console. 14 | 15 | ### `npm test` 16 | 17 | Launches the test runner in the interactive watch mode.
18 | See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information. 19 | 20 | ### `npm run build` 21 | 22 | Builds the app for production to the `build` folder.
23 | It correctly bundles React in production mode and optimizes the build for the best performance. 24 | 25 | The build is minified and the filenames include the hashes.
26 | Your app is ready to be deployed! 27 | 28 | See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information. 29 | 30 | ### `npm run eject` 31 | 32 | **Note: this is a one-way operation. Once you `eject`, you can’t go back!** 33 | 34 | If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project. 35 | 36 | Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own. 37 | 38 | You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it. 39 | 40 | ## Learn More 41 | 42 | You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started). 43 | 44 | To learn React, check out the [React documentation](https://reactjs.org/). 45 | 46 | ### Code Splitting 47 | 48 | This section has moved here: https://facebook.github.io/create-react-app/docs/code-splitting 49 | 50 | ### Analyzing the Bundle Size 51 | 52 | This section has moved here: https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size 53 | 54 | ### Making a Progressive Web App 55 | 56 | This section has moved here: https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app 57 | 58 | ### Advanced Configuration 59 | 60 | This section has moved here: https://facebook.github.io/create-react-app/docs/advanced-configuration 61 | 62 | ### Deployment 63 | 64 | This section has moved here: https://facebook.github.io/create-react-app/docs/deployment 65 | 66 | ### `npm run build` fails to minify 67 | 68 | This section has moved here: https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify 69 | -------------------------------------------------------------------------------- /client/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "corporate-view", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@babel/plugin-proposal-private-property-in-object": "^7.21.11", 7 | "@fortawesome/fontawesome-svg-core": "^1.2.28", 8 | "@fortawesome/free-solid-svg-icons": "^5.13.0", 9 | "@fortawesome/react-fontawesome": "^0.1.9", 10 | "@testing-library/jest-dom": "^4.2.4", 11 | "@testing-library/react": "^9.5.0", 12 | "@testing-library/user-event": "^7.2.1", 13 | "axios": "^0.21.1", 14 | "bootstrap": "^4.5.0", 15 | "dotenv": "^8.2.0", 16 | "form-data": "^3.0.0", 17 | "fs-extra": "^9.0.1", 18 | "lodash": "^4.17.20", 19 | "mdbreact": "^4.26.1", 20 | "namor": "^2.0.2", 21 | "node-sass": "^4.14.1", 22 | "qrcode.react": "^1.0.0", 23 | "react": "^16.13.1", 24 | "react-animated-css": "^1.2.1", 25 | "react-bootstrap": "^1.3.0", 26 | "react-bootstrap-table-next": "^4.0.3", 27 | "react-bootstrap-table2-filter": "^1.3.3", 28 | "react-bootstrap-table2-paginator": "^2.1.2", 29 | "react-bootstrap-table2-toolkit": "^2.1.3", 30 | "react-datepicker": "^3.3.0", 31 | "react-dom": "^16.13.1", 32 | "react-hot-loader": "^4.12.21", 33 | "react-logger": "^1.1.0", 34 | "react-notifications-component": "^2.4.0", 35 | "react-redux": "^7.2.0", 36 | "react-router-dom": "^5.2.0", 37 | "react-scripts": "^4.0.1", 38 | "react-switch": "^5.0.1", 39 | "react-table": "^7.1.0", 40 | "react-toastify": "^6.0.5", 41 | "react-tooltip": "^4.2.10", 42 | "redux": "^4.0.5", 43 | "redux-logger": "^3.0.6", 44 | "redux-thunk": "^2.3.0", 45 | "socket.io-client": "^2.3.0", 46 | "winston": "^3.3.3", 47 | "ws": "^7.3.1" 48 | }, 49 | "scripts": { 50 | "start": "react-scripts start", 51 | "build": "react-scripts build", 52 | "test": "react-scripts test", 53 | "eject": "react-scripts eject", 54 | "postbuild": "node ./scripts/postbuild" 55 | }, 56 | "eslintConfig": { 57 | "extends": "react-app" 58 | }, 59 | "browserslist": { 60 | "production": [ 61 | ">0.2%", 62 | "not dead", 63 | "not op_mini all" 64 | ], 65 | "development": [ 66 | "last 1 chrome version", 67 | "last 1 firefox version", 68 | "last 1 safari version" 69 | ] 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /client/public/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TradingBotX/market-making-bot/b0049041336ee148ed906142c443d7b433bd7ec8/client/public/favicon.png -------------------------------------------------------------------------------- /client/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | -------------------------------------------------------------------------------- /client/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TradingBotX/market-making-bot/b0049041336ee148ed906142c443d7b433bd7ec8/client/public/logo192.png -------------------------------------------------------------------------------- /client/public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TradingBotX/market-making-bot/b0049041336ee148ed906142c443d7b433bd7ec8/client/public/logo512.png -------------------------------------------------------------------------------- /client/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /client/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /client/public/web.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /client/scripts/postbuild.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | const fse = require("fs-extra"); 3 | const path = require("path"); 4 | 5 | const buildFolder = "serving-build"; 6 | const source = "build"; 7 | 8 | const buildFolderPath = path.join(__dirname, "../", buildFolder); 9 | const sourcePath = path.join(__dirname, "../", source); 10 | 11 | fse.ensureDirSync(buildFolderPath); 12 | 13 | fse.emptyDirSync(buildFolderPath); 14 | 15 | fse 16 | .copy(sourcePath, buildFolderPath) 17 | .then(console.log(">>>> successfully builed the code")) 18 | .catch((err) => { 19 | console.log("error in post-build script", err); 20 | process.exit(1); 21 | }); 22 | -------------------------------------------------------------------------------- /client/src/assets/img/Indsoft_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TradingBotX/market-making-bot/b0049041336ee148ed906142c443d7b433bd7ec8/client/src/assets/img/Indsoft_logo.png -------------------------------------------------------------------------------- /client/src/assets/img/XinFin_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TradingBotX/market-making-bot/b0049041336ee148ed906142c443d7b433bd7ec8/client/src/assets/img/XinFin_dark.png -------------------------------------------------------------------------------- /client/src/assets/img/XinFin_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TradingBotX/market-making-bot/b0049041336ee148ed906142c443d7b433bd7ec8/client/src/assets/img/XinFin_white.png -------------------------------------------------------------------------------- /client/src/assets/img/blockdegree_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TradingBotX/market-making-bot/b0049041336ee148ed906142c443d7b433bd7ec8/client/src/assets/img/blockdegree_dark.png -------------------------------------------------------------------------------- /client/src/assets/img/blockdegree_favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TradingBotX/market-making-bot/b0049041336ee148ed906142c443d7b433bd7ec8/client/src/assets/img/blockdegree_favicon.png -------------------------------------------------------------------------------- /client/src/assets/img/blockdegree_icon_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TradingBotX/market-making-bot/b0049041336ee148ed906142c443d7b433bd7ec8/client/src/assets/img/blockdegree_icon_white.png -------------------------------------------------------------------------------- /client/src/assets/img/blockdegree_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TradingBotX/market-making-bot/b0049041336ee148ed906142c443d7b433bd7ec8/client/src/assets/img/blockdegree_white.png -------------------------------------------------------------------------------- /client/src/assets/img/dummy-certi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TradingBotX/market-making-bot/b0049041336ee148ed906142c443d7b433bd7ec8/client/src/assets/img/dummy-certi.png -------------------------------------------------------------------------------- /client/src/assets/img/funders.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TradingBotX/market-making-bot/b0049041336ee148ed906142c443d7b433bd7ec8/client/src/assets/img/funders.png -------------------------------------------------------------------------------- /client/src/assets/img/xdcFavicons/android-icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TradingBotX/market-making-bot/b0049041336ee148ed906142c443d7b433bd7ec8/client/src/assets/img/xdcFavicons/android-icon-144x144.png -------------------------------------------------------------------------------- /client/src/assets/img/xdcFavicons/android-icon-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TradingBotX/market-making-bot/b0049041336ee148ed906142c443d7b433bd7ec8/client/src/assets/img/xdcFavicons/android-icon-192x192.png -------------------------------------------------------------------------------- /client/src/assets/img/xdcFavicons/android-icon-36x36.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TradingBotX/market-making-bot/b0049041336ee148ed906142c443d7b433bd7ec8/client/src/assets/img/xdcFavicons/android-icon-36x36.png -------------------------------------------------------------------------------- /client/src/assets/img/xdcFavicons/android-icon-48x48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TradingBotX/market-making-bot/b0049041336ee148ed906142c443d7b433bd7ec8/client/src/assets/img/xdcFavicons/android-icon-48x48.png -------------------------------------------------------------------------------- /client/src/assets/img/xdcFavicons/android-icon-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TradingBotX/market-making-bot/b0049041336ee148ed906142c443d7b433bd7ec8/client/src/assets/img/xdcFavicons/android-icon-72x72.png -------------------------------------------------------------------------------- /client/src/assets/img/xdcFavicons/android-icon-96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TradingBotX/market-making-bot/b0049041336ee148ed906142c443d7b433bd7ec8/client/src/assets/img/xdcFavicons/android-icon-96x96.png -------------------------------------------------------------------------------- /client/src/assets/img/xdcFavicons/apple-icon-114x114.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TradingBotX/market-making-bot/b0049041336ee148ed906142c443d7b433bd7ec8/client/src/assets/img/xdcFavicons/apple-icon-114x114.png -------------------------------------------------------------------------------- /client/src/assets/img/xdcFavicons/apple-icon-120x120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TradingBotX/market-making-bot/b0049041336ee148ed906142c443d7b433bd7ec8/client/src/assets/img/xdcFavicons/apple-icon-120x120.png -------------------------------------------------------------------------------- /client/src/assets/img/xdcFavicons/apple-icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TradingBotX/market-making-bot/b0049041336ee148ed906142c443d7b433bd7ec8/client/src/assets/img/xdcFavicons/apple-icon-144x144.png -------------------------------------------------------------------------------- /client/src/assets/img/xdcFavicons/apple-icon-152x152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TradingBotX/market-making-bot/b0049041336ee148ed906142c443d7b433bd7ec8/client/src/assets/img/xdcFavicons/apple-icon-152x152.png -------------------------------------------------------------------------------- /client/src/assets/img/xdcFavicons/apple-icon-180x180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TradingBotX/market-making-bot/b0049041336ee148ed906142c443d7b433bd7ec8/client/src/assets/img/xdcFavicons/apple-icon-180x180.png -------------------------------------------------------------------------------- /client/src/assets/img/xdcFavicons/apple-icon-57x57.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TradingBotX/market-making-bot/b0049041336ee148ed906142c443d7b433bd7ec8/client/src/assets/img/xdcFavicons/apple-icon-57x57.png -------------------------------------------------------------------------------- /client/src/assets/img/xdcFavicons/apple-icon-60x60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TradingBotX/market-making-bot/b0049041336ee148ed906142c443d7b433bd7ec8/client/src/assets/img/xdcFavicons/apple-icon-60x60.png -------------------------------------------------------------------------------- /client/src/assets/img/xdcFavicons/apple-icon-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TradingBotX/market-making-bot/b0049041336ee148ed906142c443d7b433bd7ec8/client/src/assets/img/xdcFavicons/apple-icon-72x72.png -------------------------------------------------------------------------------- /client/src/assets/img/xdcFavicons/apple-icon-76x76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TradingBotX/market-making-bot/b0049041336ee148ed906142c443d7b433bd7ec8/client/src/assets/img/xdcFavicons/apple-icon-76x76.png -------------------------------------------------------------------------------- /client/src/assets/img/xdcFavicons/apple-icon-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TradingBotX/market-making-bot/b0049041336ee148ed906142c443d7b433bd7ec8/client/src/assets/img/xdcFavicons/apple-icon-precomposed.png -------------------------------------------------------------------------------- /client/src/assets/img/xdcFavicons/apple-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TradingBotX/market-making-bot/b0049041336ee148ed906142c443d7b433bd7ec8/client/src/assets/img/xdcFavicons/apple-icon.png -------------------------------------------------------------------------------- /client/src/assets/img/xdcFavicons/browserconfig.xml: -------------------------------------------------------------------------------- 1 | 2 | #ffffff -------------------------------------------------------------------------------- /client/src/assets/img/xdcFavicons/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TradingBotX/market-making-bot/b0049041336ee148ed906142c443d7b433bd7ec8/client/src/assets/img/xdcFavicons/favicon-16x16.png -------------------------------------------------------------------------------- /client/src/assets/img/xdcFavicons/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TradingBotX/market-making-bot/b0049041336ee148ed906142c443d7b433bd7ec8/client/src/assets/img/xdcFavicons/favicon-32x32.png -------------------------------------------------------------------------------- /client/src/assets/img/xdcFavicons/favicon-96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TradingBotX/market-making-bot/b0049041336ee148ed906142c443d7b433bd7ec8/client/src/assets/img/xdcFavicons/favicon-96x96.png -------------------------------------------------------------------------------- /client/src/assets/img/xdcFavicons/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TradingBotX/market-making-bot/b0049041336ee148ed906142c443d7b433bd7ec8/client/src/assets/img/xdcFavicons/favicon.ico -------------------------------------------------------------------------------- /client/src/assets/img/xdcFavicons/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "App", 3 | "icons": [ 4 | { 5 | "src": "\/android-icon-36x36.png", 6 | "sizes": "36x36", 7 | "type": "image\/png", 8 | "density": "0.75" 9 | }, 10 | { 11 | "src": "\/android-icon-48x48.png", 12 | "sizes": "48x48", 13 | "type": "image\/png", 14 | "density": "1.0" 15 | }, 16 | { 17 | "src": "\/android-icon-72x72.png", 18 | "sizes": "72x72", 19 | "type": "image\/png", 20 | "density": "1.5" 21 | }, 22 | { 23 | "src": "\/android-icon-96x96.png", 24 | "sizes": "96x96", 25 | "type": "image\/png", 26 | "density": "2.0" 27 | }, 28 | { 29 | "src": "\/android-icon-144x144.png", 30 | "sizes": "144x144", 31 | "type": "image\/png", 32 | "density": "3.0" 33 | }, 34 | { 35 | "src": "\/android-icon-192x192.png", 36 | "sizes": "192x192", 37 | "type": "image\/png", 38 | "density": "4.0" 39 | } 40 | ] 41 | } -------------------------------------------------------------------------------- /client/src/assets/img/xdcFavicons/ms-icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TradingBotX/market-making-bot/b0049041336ee148ed906142c443d7b433bd7ec8/client/src/assets/img/xdcFavicons/ms-icon-144x144.png -------------------------------------------------------------------------------- /client/src/assets/img/xdcFavicons/ms-icon-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TradingBotX/market-making-bot/b0049041336ee148ed906142c443d7b433bd7ec8/client/src/assets/img/xdcFavicons/ms-icon-150x150.png -------------------------------------------------------------------------------- /client/src/assets/img/xdcFavicons/ms-icon-310x310.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TradingBotX/market-making-bot/b0049041336ee148ed906142c443d7b433bd7ec8/client/src/assets/img/xdcFavicons/ms-icon-310x310.png -------------------------------------------------------------------------------- /client/src/assets/img/xdcFavicons/ms-icon-70x70.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TradingBotX/market-making-bot/b0049041336ee148ed906142c443d7b433bd7ec8/client/src/assets/img/xdcFavicons/ms-icon-70x70.png -------------------------------------------------------------------------------- /client/src/assets/img/xdc_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TradingBotX/market-making-bot/b0049041336ee148ed906142c443d7b433bd7ec8/client/src/assets/img/xdc_logo.png -------------------------------------------------------------------------------- /client/src/assets/scss/abstracts/_functions.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TradingBotX/market-making-bot/b0049041336ee148ed906142c443d7b433bd7ec8/client/src/assets/scss/abstracts/_functions.scss -------------------------------------------------------------------------------- /client/src/assets/scss/abstracts/_mixins.scss: -------------------------------------------------------------------------------- 1 | @mixin clearFix { 2 | &::after { 3 | content:''; 4 | clear:both; 5 | display:table; 6 | } 7 | } -------------------------------------------------------------------------------- /client/src/assets/scss/abstracts/_variables.scss: -------------------------------------------------------------------------------- 1 | :root { 2 | --sidebar-width: 20rem; 3 | } 4 | 5 | $color-primary: #343a40; 6 | $color-primary-light: lighten($color-primary, 10%); 7 | $color-primary-lighter: lighten($color-primary, 50%); 8 | 9 | $color-background-primary: #ebebeb; 10 | $color-primary-border: #c0c0c0; 11 | 12 | $color-header: #343a40; 13 | $color-header-light: lighten($color-header, 10%); 14 | $color-header-lighter: lighten($color-header, 20%); 15 | $color-header-dark: darken($color-header, 10%); 16 | 17 | $color-light-grey: lightgrey; 18 | $color-light-grey-light: #cdcdcd; 19 | $color-grey-light-1: #f7f7f7; 20 | $color-grey-dark: #777; 21 | $color-white: #ffffff; 22 | $color-black: #000; 23 | 24 | $color-new-blue: #177bff; 25 | $color-new-green: #25ac37; 26 | $color-new-red: #ff0000; 27 | $color-new-orange: #ff8c00; 28 | 29 | $typo-scale-ratio: 8/9; 30 | 31 | $overall-font-primary: 1.5rem; 32 | $overall-font-secondary: #{$overall-font-primary * $typo-scale-ratio}; 33 | 34 | $table-one-font-primary: 1.6rem; 35 | $table-one-font-secondary: #{$table-one-font-primary * $typo-scale-ratio}; 36 | 37 | $modal-font-primary: 2rem; 38 | $modal-font-secondary: #{$modal-font-primary * $typo-scale-ratio}; 39 | $modal-padding: 2rem; 40 | $modal-width: 50rem; 41 | $modal-height: 20rem; 42 | $modal-margin: 1rem; 43 | 44 | $footer-primary: 2.2rem; 45 | $footer-links: 1.6rem; 46 | $footer-secondary: 1.4rem; 47 | 48 | $sidebar-width: var(--sidebar-width); 49 | $sidebar-width-transaition: 0.5s; 50 | $sidebar-scroller-width: 0.75rem; 51 | $header-height: 5.5rem; 52 | 53 | $lg-width: 992px; 54 | $md-width: 762px; 55 | $sm-width: 580px; 56 | -------------------------------------------------------------------------------- /client/src/assets/scss/base/_animation.scss: -------------------------------------------------------------------------------- 1 | @keyframes moveInLeft { 2 | 0% { 3 | opacity: 0; 4 | transform: translateX(-10rem); 5 | } 6 | 7 | 80% { 8 | transform: translateX(1rem); 9 | } 10 | 11 | 100% { 12 | opacity: 1; 13 | transform: translate((0)); 14 | } 15 | } 16 | 17 | @keyframes moveInRight { 18 | 0% { 19 | opacity: 0; 20 | transform: translateX(10rem); 21 | } 22 | 23 | 80% { 24 | transform: translateX(-1rem); 25 | } 26 | 27 | 100% { 28 | opacity: 1; 29 | transform: translate((0)); 30 | } 31 | } 32 | 33 | @keyframes moveInBottom { 34 | 0% { 35 | opacity: 0; 36 | transform: translateY(3rem); 37 | } 38 | 39 | 100% { 40 | opacity: 1; 41 | transform: translateY((0)); 42 | } 43 | } -------------------------------------------------------------------------------- /client/src/assets/scss/base/_base.scss: -------------------------------------------------------------------------------- 1 | *::after, 2 | ::before { 3 | /* Basic Reset */ 4 | margin: 0px; 5 | padding: 0px; 6 | } 7 | 8 | html { 9 | // defines what 1 rem is 10 | font-size: 62.5%; 11 | } 12 | 13 | body { 14 | margin: 0; 15 | overflow-x: hidden; 16 | background-color: $color-background-primary; 17 | } 18 | -------------------------------------------------------------------------------- /client/src/assets/scss/base/_typography.scss: -------------------------------------------------------------------------------- 1 | .primary-text { 2 | font-size: 3rem; 3 | font-weight: 500; 4 | } 5 | 6 | .secondary-text { 7 | font-size: 1.8rem; 8 | } 9 | -------------------------------------------------------------------------------- /client/src/assets/scss/base/_utilities.scss: -------------------------------------------------------------------------------- 1 | .u-tex-center { 2 | text-align: center; 3 | } 4 | 5 | .u-right { 6 | text-align: right; 7 | } 8 | 9 | .u-pad-right-1 { 10 | padding-right: 1rem; 11 | } 12 | 13 | .justify-center { 14 | justify-content: center; 15 | } 16 | 17 | .u-margin-bottom-8 { 18 | margin-bottom: 8rem; 19 | } 20 | 21 | .u-float-left { 22 | float: left; 23 | } 24 | 25 | .u-float-right { 26 | float: right; 27 | } 28 | 29 | .u-border-btm { 30 | border-bottom: 1px solid $color-light-grey; 31 | } 32 | 33 | .lg-hori-1 { 34 | @media screen and (max-width: 992px) { 35 | padding-bottom: 1rem; 36 | } 37 | } 38 | 39 | .u-back-light { 40 | &:hover { 41 | background-color: $color-primary-light; 42 | } 43 | } 44 | 45 | .u-text-right { 46 | text-align: right; 47 | } 48 | 49 | .u-scroll-x { 50 | width: 100%; 51 | overflow-x: auto; 52 | } 53 | 54 | .u-border-top { 55 | border-top: 1px solid $color-light-grey; 56 | } 57 | 58 | .u-justify-center { 59 | justify-content: center; 60 | } 61 | 62 | .u-text-left { 63 | text-align: left !important; 64 | } 65 | 66 | .u-no-pad-r { 67 | padding-right: 0 !important; 68 | } 69 | 70 | .u-no-pad-l { 71 | padding-left: 0 !important; 72 | } 73 | 74 | .u-no-pad-r--lg { 75 | @media screen and (min-width: $lg-width) { 76 | padding-right: 0 !important; 77 | } 78 | } 79 | 80 | .u-no-pad-l--lg { 81 | @media screen and (min-width: $lg-width) { 82 | padding-left: 0 !important; 83 | } 84 | } 85 | 86 | .u-no-pad-r--md { 87 | @media screen and (min-width: $md-width) { 88 | padding-right: 0 !important; 89 | } 90 | } 91 | 92 | .u-no-pad-l--md { 93 | @media screen and (min-width: $md-width) { 94 | padding-left: 0 !important; 95 | } 96 | } 97 | 98 | .u-no-list { 99 | list-style-type: none; 100 | padding-left: 0; 101 | } 102 | 103 | .u-pad-1 { 104 | padding: 1rem; 105 | } 106 | 107 | .u-margin-top-1 { 108 | margin-top: 1rem; 109 | } 110 | 111 | .u-position-sticky { 112 | position: -webkit-sticky; /* Safari */ 113 | position: sticky; 114 | top: 0; 115 | } 116 | 117 | .u-align-right { 118 | width: 100%; 119 | text-align: right; 120 | } 121 | 122 | .u-width-full { 123 | width: 100%; 124 | } 125 | 126 | .noselect { 127 | -webkit-touch-callout: none; /* iOS Safari */ 128 | -webkit-user-select: none; /* Safari */ 129 | -khtml-user-select: none; /* Konqueror HTML */ 130 | -moz-user-select: none; /* Old versions of Firefox */ 131 | -ms-user-select: none; /* Internet Explorer/Edge */ 132 | user-select: none; /* Non-prefixed version, currently */ 133 | } 134 | 135 | .blue { 136 | color: $color-new-blue; 137 | } 138 | 139 | .red { 140 | color: $color-new-red; 141 | } 142 | 143 | .green { 144 | color: $color-new-green; 145 | } 146 | 147 | .orange { 148 | color: $color-new-orange; 149 | } 150 | 151 | .lighten { 152 | color: $color-light-grey; 153 | } 154 | -------------------------------------------------------------------------------- /client/src/assets/scss/components/_accordion.scss: -------------------------------------------------------------------------------- 1 | .accordion { 2 | .card-header { 3 | font-size: 2rem; 4 | 5 | svg { 6 | color: $color-new-blue; 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /client/src/assets/scss/components/_banner.scss: -------------------------------------------------------------------------------- 1 | .banner { 2 | &__title { 3 | text-align: center; 4 | color: $color-primary; 5 | } 6 | 7 | &__hero { 8 | text-align: center; 9 | img { 10 | width: 100%; 11 | } 12 | } 13 | 14 | &__content { 15 | padding: 0.5rem; 16 | text-align: center; 17 | color: $color-primary; 18 | 19 | ul { 20 | li { 21 | margin: 1rem 0 1rem 0; 22 | } 23 | } 24 | } 25 | 26 | &__footer { 27 | padding: 2rem; 28 | color: $color-primary; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /client/src/assets/scss/components/_button.scss: -------------------------------------------------------------------------------- 1 | .btn-custom { 2 | &:link, 3 | :visited { 4 | display: inline-block; 5 | text-transform: uppercase; 6 | text-decoration: none; 7 | padding: 1.5rem 4rem; 8 | border-radius: 10rem; 9 | transition: all 0.2s; 10 | position: relative; 11 | font-size: 1.6rem; 12 | } 13 | 14 | &--white { 15 | background-color: $color-white; 16 | color: $color-grey-dark; 17 | 18 | &::after { 19 | background-color: white; 20 | } 21 | } 22 | 23 | &:hover { 24 | transform: translateY(-0.3rem); 25 | box-shadow: 0 1rem 2rem rgba($color-black, 0.2); 26 | 27 | &::after { 28 | transform: scaleX(1.4) scaleY(1.6); 29 | opacity: 0; 30 | } 31 | } 32 | 33 | &:active { 34 | transform: translate(-0.1rem); 35 | box-shadow: 0 0.5rem 1rem rgba($color-black, 0.2); 36 | } 37 | 38 | &::after { 39 | content: ""; 40 | display: inline-block; 41 | height: 100%; 42 | width: 100%; 43 | border-radius: 100px; 44 | position: absolute; 45 | top: 0; 46 | left: 0; 47 | z-index: -1; 48 | transition: all 0.3s; 49 | } 50 | 51 | &--animated { 52 | animation: moveInBottom 0.5s ease-out 0.5s; 53 | animation-fill-mode: backwards; 54 | } 55 | } 56 | 57 | .std-btn { 58 | font-size: $overall-font-primary; 59 | margin: 0.5rem; 60 | right: 0; 61 | } 62 | 63 | .route-btn { 64 | display: inline; 65 | margin: 0; 66 | border: 0; 67 | padding: 0; 68 | cursor: pointer; 69 | } 70 | -------------------------------------------------------------------------------- /client/src/assets/scss/components/_card.scss: -------------------------------------------------------------------------------- 1 | .simple-card { 2 | padding: 1rem; 3 | margin: 1rem; 4 | 5 | &--header { 6 | text-align: center; 7 | font-weight: 500; 8 | font-size: $overall-font-primary; 9 | } 10 | 11 | &--body { 12 | padding: 1rem; 13 | margin: 1rem; 14 | font-size: $overall-font-secondary; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /client/src/assets/scss/components/_center-form.scss: -------------------------------------------------------------------------------- 1 | .center-form { 2 | margin-top: 2rem; 3 | border: 1px solid $color-primary-lighter; 4 | padding: 1rem; 5 | border-radius: 1rem; 6 | background-color: $color-white; 7 | 8 | .custom-file-label { 9 | font-size: 1.5rem; 10 | } 11 | 12 | .form-control { 13 | font-size: 1.5rem; 14 | } 15 | 16 | .btn { 17 | font-size: 1.5rem; 18 | } 19 | 20 | &__title { 21 | text-align: center; 22 | font-size: 3rem; 23 | } 24 | 25 | &__content { 26 | padding: 1rem; 27 | font-size: 1.8rem; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /client/src/assets/scss/components/_chart.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TradingBotX/market-making-bot/b0049041336ee148ed906142c443d7b433bd7ec8/client/src/assets/scss/components/_chart.scss -------------------------------------------------------------------------------- /client/src/assets/scss/components/_custom-card.scss: -------------------------------------------------------------------------------- 1 | .custom-card { 2 | background-color: $color-white; 3 | padding: 1rem; 4 | border-radius: 1rem; 5 | color: $color-primary; 6 | height: 286px; 7 | 8 | &__title { 9 | font-size: 3rem; 10 | b { 11 | color: $color-new-blue; 12 | } 13 | text-align: center; 14 | border-bottom: 1px solid $color-light-grey; 15 | } 16 | 17 | &__content { 18 | text-align: center; 19 | font-size: 1.8rem; 20 | img { 21 | width: 20rem; 22 | height: 20rem; 23 | } 24 | 25 | &--text { 26 | padding: 1rem; 27 | } 28 | } 29 | 30 | @media screen and (max-width:762px) { 31 | margin-bottom: 2rem; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /client/src/assets/scss/components/_highlighted.scss: -------------------------------------------------------------------------------- 1 | .highlighted-note { 2 | width: 100%; 3 | background: #354c60; 4 | margin-bottom: 20px; 5 | margin-top: 20px; 6 | padding: 15px; 7 | color: white; 8 | font-weight: 400; 9 | border: 1px solid #82baf6; 10 | 11 | &--body { 12 | .highlighted-note--body { 13 | margin-top: 10px; 14 | } 15 | 16 | a { 17 | color: #04cdfc; 18 | text-decoration: none; 19 | } 20 | } 21 | &--header { 22 | font-size: large; 23 | z-index: 2; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /client/src/assets/scss/components/_icon-card.scss: -------------------------------------------------------------------------------- 1 | .icon-card { 2 | padding: 1rem; 3 | background-color: $color-white; 4 | border-radius: 0.5rem; 5 | height: 8rem; 6 | 7 | &__icon { 8 | padding-left: 2.5rem; 9 | text-align: center; 10 | } 11 | 12 | &__body { 13 | padding-left: 1rem; 14 | width: 100%; 15 | &--title { 16 | font-size: $overall-font-primary; 17 | font-weight: 500; 18 | } 19 | &--text { 20 | font-size: $overall-font-secondary; 21 | padding: 0.5rem; 22 | } 23 | } 24 | 25 | @media screen and (max-width: 987px) { 26 | margin-bottom: 1rem; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /client/src/assets/scss/components/_modal.scss: -------------------------------------------------------------------------------- 1 | .blockdegree-modal { 2 | max-width: $modal-width; 3 | 4 | .modal-content { 5 | max-width: $modal-width; 6 | height: $modal-height; 7 | border-radius: 0.5rem; 8 | } 9 | 10 | @media only screen and (max-width: 580px) { 11 | .blockdegree-modal { 12 | left: 50%; 13 | } 14 | 15 | .blockdegree-modal { 16 | transform: translateX(-50%); 17 | } 18 | } 19 | } 20 | 21 | .description-modal { 22 | &__header { 23 | padding: $modal-padding !important; 24 | background-color: $color-primary; 25 | color: $color-white; 26 | &--title { 27 | font-size: $modal-font-primary; 28 | } 29 | 30 | .close { 31 | color: $color-white; 32 | } 33 | } 34 | 35 | &__body { 36 | padding: $modal-padding; 37 | font-size: $modal-font-secondary; 38 | background-color: $color-light-grey; 39 | color: $color-primary; 40 | border-radius: 0.5rem; 41 | } 42 | } 43 | 44 | .qr-modal { 45 | max-width: $modal-width; 46 | &__header { 47 | padding: $modal-padding !important; 48 | background-color: $color-primary; 49 | color: $color-white; 50 | &--title { 51 | font-size: $modal-font-primary; 52 | } 53 | 54 | .close { 55 | color: $color-white; 56 | } 57 | } 58 | 59 | &__body { 60 | padding: $modal-padding; 61 | font-size: $modal-font-secondary; 62 | background-color: $color-light-grey; 63 | color: $color-primary; 64 | border-radius: 0.5rem; 65 | text-align: center; 66 | 67 | &--img { 68 | canvas { 69 | height: 20rem !important; 70 | width: 20rem !important; 71 | padding: 0.5rem; 72 | } 73 | } 74 | 75 | &--wrap { 76 | margin-top: 2rem; 77 | padding: 2rem; 78 | border-radius: 1rem; 79 | background-color: $color-white; 80 | text-align: center; 81 | input { 82 | width: 100%; 83 | } 84 | 85 | .address { 86 | padding: 0.5rem; 87 | overflow: hidden; 88 | background-color: $color-light-grey; 89 | border-radius: 0.5rem; 90 | } 91 | 92 | .copy-btn { 93 | margin: 0.5rem; 94 | font-size: $modal-font-secondary; 95 | } 96 | } 97 | } 98 | } 99 | 100 | .payment-modal { 101 | &__header { 102 | padding: $modal-padding !important; 103 | background-color: $color-primary; 104 | color: $color-white; 105 | &--title { 106 | font-size: $modal-font-primary; 107 | } 108 | 109 | .close { 110 | color: $color-white; 111 | } 112 | } 113 | 114 | &__body { 115 | display: flex; 116 | flex-direction: column; 117 | justify-content: center; 118 | padding: $modal-padding; 119 | font-size: $modal-font-secondary; 120 | background-color: $color-light-grey; 121 | color: $color-primary; 122 | border-radius: 0.5rem; 123 | vertical-align: middle; 124 | 125 | text-align: center; 126 | 127 | button { 128 | width: 100%; 129 | font-size: $modal-font-secondary; 130 | } 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /client/src/assets/scss/components/_orderbook.scss: -------------------------------------------------------------------------------- 1 | .table-two { 2 | margin: 1rem 0; 3 | justify-content: center; 4 | overflow-x: scroll; 5 | height: fit-content; 6 | 7 | #search-bar-0 { 8 | font-size: $overall-font-secondary; 9 | } 10 | 11 | .react-bootstrap-table-page-btns-ul { 12 | font-size: $overall-font-secondary; 13 | } 14 | 15 | .react-bootstrap-table-pagination { 16 | margin-right: 0; 17 | margin-left: 0; 18 | } 19 | 20 | &__loader { 21 | padding: 1rem; 22 | text-align: center; 23 | 24 | &--text { 25 | padding: 0.5rem; 26 | } 27 | } 28 | 29 | table { 30 | border-collapse: separate; 31 | border-spacing: 0 1px; 32 | border: solid $color-light-grey 1px; 33 | border-radius: 6px; 34 | } 35 | 36 | thead { 37 | background-color: $color-primary; 38 | color: $color-white; 39 | text-align: center; 40 | // font-size: $table-one-font-primary; 41 | font-size: 15px; 42 | 43 | th { 44 | border-bottom: none; 45 | } 46 | } 47 | 48 | tbody { 49 | background-color: $color-white; 50 | color: $color-primary; 51 | // font-size: $table-one-font-secondary; 52 | font-size: 15px; 53 | text-align: center; 54 | } 55 | 56 | tr, 57 | td, 58 | th { 59 | padding: 1.2rem; 60 | border: none; 61 | width: 10rem; 62 | outline: none; 63 | 64 | button { 65 | font-size: $table-one-font-secondary; 66 | } 67 | } 68 | 69 | tr { 70 | border-bottom: 1px solid $color-light-grey; 71 | } 72 | } -------------------------------------------------------------------------------- /client/src/assets/scss/components/_render-input.scss: -------------------------------------------------------------------------------- 1 | .render-input { 2 | margin: 0.5rem; 3 | font-size: $overall-font-primary; 4 | 5 | input { 6 | font-size: $overall-font-primary; 7 | } 8 | 9 | .render-input { 10 | font-size: $overall-font-primary * $typo-scale-ratio; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /client/src/assets/scss/components/_select.scss: -------------------------------------------------------------------------------- 1 | .select { 2 | display: flex; 3 | justify-content: space-between; 4 | margin-bottom: 12px; 5 | select { 6 | width: 40%; 7 | font-size: 15px; 8 | padding: 4px 8px; 9 | outline: none; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /client/src/assets/scss/components/_table.scss: -------------------------------------------------------------------------------- 1 | .table-one { 2 | // padding: 1rem; 3 | justify-content: center; 4 | width: 95%; 5 | margin: 0 auto; 6 | margin-top: 2rem; 7 | height: fit-content; 8 | overflow-x: auto; 9 | 10 | ::-webkit-scrollbar { 11 | display: none; 12 | } 13 | 14 | .action-btn { 15 | padding: 0.5rem; 16 | margin: 0.5rem; 17 | } 18 | 19 | .table--footer { 20 | padding: 1rem; 21 | margin: 1rem; 22 | padding-top: 0; 23 | margin-top: 0; 24 | font-size: $overall-font-secondary; 25 | 26 | .footer-btn { 27 | float: right; 28 | font-size: $overall-font-secondary; 29 | } 30 | 31 | .note { 32 | float: left; 33 | } 34 | } 35 | 36 | #search-bar-0 { 37 | font-size: $overall-font-secondary; 38 | } 39 | 40 | .react-bootstrap-table-page-btns-ul { 41 | font-size: $overall-font-secondary; 42 | } 43 | 44 | .react-bootstrap-table-pagination { 45 | margin-right: 0; 46 | margin-left: 0; 47 | } 48 | 49 | &__loader { 50 | padding: 1rem; 51 | text-align: center; 52 | 53 | &--text { 54 | padding: 0.5rem; 55 | } 56 | } 57 | 58 | table { 59 | border-collapse: separate; 60 | border-spacing: 0 1px; 61 | border: solid $color-light-grey 1px; 62 | border-radius: 0px; 63 | } 64 | 65 | thead { 66 | background-color: $color-primary; 67 | color: $color-white; 68 | font-size: $table-one-font-primary; 69 | 70 | th { 71 | border-bottom: none; 72 | } 73 | } 74 | 75 | tbody { 76 | background-color: $color-white; 77 | color: $color-primary; 78 | font-size: $table-one-font-secondary; 79 | } 80 | 81 | tr, 82 | td, 83 | th { 84 | padding: 1rem; 85 | border: none; 86 | width: 10rem; 87 | outline: none; 88 | 89 | button { 90 | font-size: $table-one-font-secondary; 91 | } 92 | } 93 | 94 | tr { 95 | border-bottom: 1px solid $color-light-grey; 96 | } 97 | } 98 | 99 | .static-table { 100 | th, 101 | tr, 102 | td, 103 | table, 104 | thead, 105 | tbody { 106 | border: none !important; 107 | font-size: 15px; 108 | } 109 | 110 | thead { 111 | border-bottom: 1px solid $color-primary !important; 112 | } 113 | } 114 | 115 | .table-width { 116 | td, 117 | th { 118 | width: 16rem; 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /client/src/assets/scss/components/_toast.scss: -------------------------------------------------------------------------------- 1 | .custom-toast { 2 | .Toastify__toast-body { 3 | font-size: $overall-font-secondary !important; 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /client/src/assets/scss/layouts/_footer.scss: -------------------------------------------------------------------------------- 1 | .footer { 2 | background-color: $color-primary; 3 | color: $color-light-grey; 4 | min-height: 15rem; 5 | display: flex; 6 | justify-content: center; 7 | 8 | hr { 9 | color: $color-light-grey; 10 | } 11 | 12 | .container { 13 | margin: 0; 14 | } 15 | .row { 16 | padding: 0.5rem; 17 | .one, 18 | .two, 19 | .three, 20 | .four { 21 | height: 10rem; 22 | } 23 | 24 | .one, 25 | .two, 26 | .three, 27 | .four { 28 | .title { 29 | padding: 0.5rem; 30 | display: inline-block; 31 | font-size: $footer-primary; 32 | vertical-align: bottom; 33 | color: $color-white; 34 | } 35 | 36 | .desc { 37 | font-size: $footer-secondary; 38 | color: $color-light-grey; 39 | } 40 | } 41 | ul, 42 | li { 43 | list-style-type: none; 44 | padding: 0; 45 | padding: 0.5rem; 46 | 47 | .link { 48 | cursor: pointer; 49 | &:hover { 50 | color: $color-white; 51 | } 52 | } 53 | } 54 | } 55 | 56 | &__bottom { 57 | text-align: center; 58 | padding: 1rem; 59 | margin: 1rem; 60 | margin-top: 2rem; 61 | border-top: 0.5px solid $color-grey-dark; 62 | font-size: $footer-secondary; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /client/src/assets/scss/layouts/_header.scss: -------------------------------------------------------------------------------- 1 | .navbar { 2 | position: fixed; 3 | top: 0; 4 | width: 100%; 5 | padding: 0.5rem; 6 | z-index: 4; 7 | 8 | height: $header-height; 9 | 10 | #responsive-navbar-nav { 11 | background-color: $color-header; 12 | } 13 | 14 | a { 15 | outline: 0; 16 | } 17 | 18 | &__brand { 19 | vertical-align: bottom; 20 | padding-top: 0.5rem; 21 | } 22 | 23 | &-brand { 24 | font-size: 2.5rem; 25 | } 26 | &__links { 27 | a { 28 | outline: 0; 29 | box-sizing: content-box; 30 | color: $color-white !important; 31 | display: inline-block; 32 | margin: 0.25rem; 33 | padding: 0.75rem; 34 | font-size: 1.6rem; 35 | font-weight: 400; 36 | &:hover { 37 | transform: none; 38 | } 39 | } 40 | 41 | .nav-link { 42 | &:active, 43 | &:focus { 44 | color: $color-white !important; 45 | } 46 | } 47 | } 48 | 49 | &__dropdown { 50 | margin: 1rem; 51 | font-size: 1.6rem; 52 | font-weight: 400; 53 | 54 | #basic-nav-dropdown { 55 | color: $color-white; 56 | } 57 | 58 | .dropdown-menu { 59 | background-color: $color-header-light; 60 | padding: 1rem; 61 | margin: 0; 62 | a { 63 | outline: 0; 64 | font-size: 1.4rem; 65 | font-weight: 400; 66 | color: $color-light-grey; 67 | padding: 1rem; 68 | border-radius: 0.5rem; 69 | 70 | &:hover { 71 | background-color: $color-header-lighter; 72 | color: $color-white; 73 | } 74 | } 75 | 76 | .active { 77 | background-color: $color-header-lighter; 78 | color: $color-white; 79 | } 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /client/src/assets/scss/layouts/_main-panel.scss: -------------------------------------------------------------------------------- 1 | .main-panel { 2 | padding: 1rem; 3 | // width: 100%; 4 | background-color: $color-background-primary; 5 | // min-height: 100vh; 6 | padding-top: $header-height; 7 | padding-left: $sidebar-width; 8 | overflow-x: hidden; 9 | overflow-y: auto; 10 | 11 | .banner-wrapper { 12 | .container { 13 | margin: 0; 14 | padding: 0; 15 | max-width: 100%; 16 | 17 | .banner-left, 18 | .banner-right { 19 | background-color: $color-white; 20 | padding: 1rem; 21 | border-radius: 0.5rem; 22 | overflow: hidden; 23 | 24 | @media screen and (min-width: $md-width) { 25 | height: 56rem; 26 | } 27 | } 28 | 29 | .banner-left { 30 | a, 31 | .btn-sign-up { 32 | color: $color-new-blue; 33 | font-weight: 600; 34 | } 35 | } 36 | 37 | .banner-right { 38 | &__tuple { 39 | margin: 0.5rem; 40 | &--title { 41 | font-size: 2.5rem; 42 | font-weight: 500; 43 | } 44 | 45 | &--content { 46 | margin: 0.5rem; 47 | font-size: 1.8rem; 48 | } 49 | } 50 | margin-left: 0; 51 | } 52 | } 53 | 54 | .funding-benefits { 55 | margin: 1.5rem; 56 | padding: 1rem; 57 | background-color: white; 58 | border-radius: 0.5rem; 59 | 60 | &__title { 61 | color: $color-primary; 62 | font-size: 3rem; 63 | font-weight: 500; 64 | text-align: center; 65 | } 66 | 67 | &__content { 68 | &--card { 69 | margin: 1rem; 70 | padding: 1rem; 71 | font-size: $overall-font-secondary; 72 | } 73 | 74 | &--img { 75 | img { 76 | width: 100%; 77 | } 78 | } 79 | } 80 | } 81 | } 82 | 83 | @media screen and (max-width: 580px) { 84 | padding-top: $header-height; 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /client/src/assets/scss/layouts/_sidebar.scss: -------------------------------------------------------------------------------- 1 | .sidebar { 2 | position: fixed; 3 | left: 0; 4 | z-index: 3; 5 | width: $sidebar-width; 6 | transition: width $sidebar-width-transaition; 7 | height: 100%; 8 | font-size: $overall-font-primary; 9 | background-color: $color-primary; 10 | color: white; 11 | padding-top: $header-height; 12 | 13 | &__nav-wrapper { 14 | overflow-y: auto; 15 | overflow-x: hidden; 16 | height: 90%; 17 | 18 | /** 19 | 20 | For mozilla scrollbar 21 | 22 | */ 23 | scrollbar-color: $color-primary $color-primary; 24 | scrollbar-width: thin; 25 | } 26 | 27 | &__nav-wrapper::-webkit-scrollbar { 28 | width: $sidebar-scroller-width; 29 | } 30 | 31 | &__nav-wrapper::-webkit-scrollbar-track { 32 | background: $color-primary; 33 | } 34 | 35 | &__nav-wrapper:hover { 36 | scrollbar-color: $color-primary-lighter $color-primary; 37 | scrollbar-width: $sidebar-scroller-width; 38 | &::-webkit-scrollbar-thumb { 39 | background-color: $color-primary-lighter; /* color of the scroll thumb */ 40 | } 41 | } 42 | 43 | &-nav { 44 | margin: 1rem; 45 | height: 5rem; 46 | text-align: center; 47 | padding: 1rem; 48 | border-radius: 0.5rem; 49 | 50 | &--text { 51 | visibility: hidden; 52 | } 53 | } 54 | 55 | &-nav:hover { 56 | background-color: $white !important; 57 | color: $color-primary !important; 58 | } 59 | 60 | &-nav__active { 61 | background-color: $white !important; 62 | color: $color-primary !important; 63 | pointer-events: none; 64 | } 65 | 66 | &-none { 67 | .sidebar-nav { 68 | display: none !important; 69 | } 70 | } 71 | 72 | &-toggle, 73 | &-toggle:active { 74 | position: absolute; 75 | bottom: 0; 76 | margin-bottom: 1rem; 77 | margin-left: 2rem; 78 | border: 0; 79 | background-color: $white !important; 80 | color: $color-primary !important; 81 | outline: none; 82 | box-shadow: none !important; 83 | } 84 | } 85 | 86 | .sidebar-open { 87 | width: 20rem; 88 | transition: width $sidebar-width-transaition; 89 | 90 | .sidebar-nav { 91 | text-align: left; 92 | .sidebar-nav--icon { 93 | display: inline-block !important; 94 | width: 3rem; 95 | transform: translateX(1rem); 96 | } 97 | .sidebar-nav--text { 98 | display: inline-block !important; 99 | visibility: visible; 100 | margin-left: $overall-font-primary; 101 | transition-duration: 0s; 102 | transition-property: visibility; 103 | transition-delay: calc(#{$sidebar-width-transaition}/ (3 / 2)); 104 | } 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /client/src/assets/scss/main.scss: -------------------------------------------------------------------------------- 1 | @import "~bootstrap/scss/bootstrap"; 2 | 3 | @import "abstracts/functions"; 4 | @import "abstracts/mixins"; 5 | @import "abstracts/variables"; 6 | 7 | @import "base/base"; 8 | @import "base/animation"; 9 | @import "base/typography"; 10 | @import "base/utilities"; 11 | 12 | @import "pages/home"; 13 | @import "pages/signup"; 14 | @import "pages/profile"; 15 | @import "pages/dashboard"; 16 | @import "pages/message-page"; 17 | @import "pages/faq"; 18 | @import "pages/monitor"; 19 | @import "pages/crons"; 20 | @import "pages/best-price"; 21 | @import "pages/management"; 22 | @import "pages/market-maker"; 23 | 24 | @import "components/modal"; 25 | @import "components/button"; 26 | @import "components/banner"; 27 | @import "components/center-form"; 28 | @import "components/table"; 29 | @import "components/highlighted"; 30 | @import "components/toast"; 31 | @import "components/accordion"; 32 | @import "components/custom-card"; 33 | @import "components/orderbook"; 34 | @import "components/chart"; 35 | @import "components/card"; 36 | @import "components/select"; 37 | @import "components/icon-card"; 38 | @import "components/render-input"; 39 | 40 | @import "layouts/header"; 41 | @import "layouts/main-panel"; 42 | @import "layouts/footer"; 43 | @import "layouts/sidebar"; 44 | -------------------------------------------------------------------------------- /client/src/assets/scss/pages/_best-price.scss: -------------------------------------------------------------------------------- 1 | .best-price { 2 | .table-footer { 3 | font-size: $overall-font-secondary; 4 | float: right; 5 | padding: 0 1rem; 6 | text-transform: uppercase; 7 | } 8 | 9 | .table-header { 10 | margin-left: 1rem; 11 | margin-top: 2rem; 12 | font-weight: 500; 13 | font-size: $overall-font-primary; 14 | text-align: center; 15 | border-bottom: 0.5px solid $color-primary-lighter; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /client/src/assets/scss/pages/_crons.scss: -------------------------------------------------------------------------------- 1 | .crons { 2 | .table-footer { 3 | font-size: $overall-font-secondary; 4 | float: right; 5 | padding: 0 1rem; 6 | text-transform: uppercase; 7 | } 8 | 9 | .table-header { 10 | margin-left: 1rem; 11 | margin-top: 2rem; 12 | font-weight: 500; 13 | font-size: $overall-font-primary; 14 | text-align: center; 15 | border-bottom: 0.5px solid $color-primary-lighter; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /client/src/assets/scss/pages/_dashboard.scss: -------------------------------------------------------------------------------- 1 | .dashboard { 2 | .note-text { 3 | background-color: $color-white; 4 | margin: 2rem; 5 | padding: 1rem; 6 | border-radius: 1rem; 7 | font-size: $overall-font-secondary; 8 | color: $color-primary; 9 | a { 10 | outline: none; 11 | } 12 | 13 | &__link { 14 | color: $color-new-blue; 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /client/src/assets/scss/pages/_faq.scss: -------------------------------------------------------------------------------- 1 | .faq-page { 2 | &__title { 3 | color: $color-primary; 4 | padding: 2rem 0 0 2rem; 5 | font-size: 3rem; 6 | font-weight: 600; 7 | } 8 | 9 | &__content { 10 | min-height: 40rem; 11 | font-size: $overall-font-secondary; 12 | margin: 1rem; 13 | padding: 1rem; 14 | border-radius: 1rem; 15 | background-color: $color-white; 16 | 17 | &--accordion { 18 | padding: 2rem; 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /client/src/assets/scss/pages/_home.scss: -------------------------------------------------------------------------------- 1 | .section-about { 2 | background-color: $color-grey-light-1; 3 | padding: 25rem 0; 4 | margin-top: -20vh; 5 | } -------------------------------------------------------------------------------- /client/src/assets/scss/pages/_management.scss: -------------------------------------------------------------------------------- 1 | .management { 2 | .section-title { 3 | font-size: $overall-font-primary; 4 | font-weight: 500; 5 | text-align: center; 6 | color: $color-primary; 7 | margin: 1rem; 8 | border-bottom: 1px solid $color-primary-lighter; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /client/src/assets/scss/pages/_market-maker.scss: -------------------------------------------------------------------------------- 1 | .market-maker { 2 | padding: 1rem; 3 | } 4 | -------------------------------------------------------------------------------- /client/src/assets/scss/pages/_message-page.scss: -------------------------------------------------------------------------------- 1 | .message-page { 2 | display: flex; 3 | justify-content: center; 4 | padding: 1rem; 5 | 6 | .message { 7 | background-color: $color-white; 8 | width: 60%; 9 | padding: 2rem; 10 | margin-top: 3rem; 11 | border-radius: 1rem; 12 | 13 | &__icon { 14 | margin-top: 2rem; 15 | margin-bottom: 2rem; 16 | 17 | display: flex; 18 | justify-content: center; 19 | &.success { 20 | color: $color-new-green; 21 | } 22 | &.error { 23 | color: $color-new-red; 24 | } 25 | } 26 | 27 | &__body { 28 | color: $color-primary; 29 | &--title { 30 | font-size: $overall-font-primary; 31 | font-weight: 600; 32 | } 33 | &--content { 34 | font-size: $overall-font-secondary; 35 | } 36 | } 37 | 38 | @media screen and (max-width: 580px) { 39 | width: 100%; 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /client/src/assets/scss/pages/_monitor.scss: -------------------------------------------------------------------------------- 1 | .monitor { 2 | padding: 1rem; 3 | 4 | .heap-status { 5 | font-size: $overall-font-secondary; 6 | font-weight: 500; 7 | } 8 | 9 | .bordered-form { 10 | margin-left: 2rem; 11 | } 12 | 13 | table { 14 | .profit { 15 | background-color: lighten($color-new-green, 30%); 16 | color: $color-primary; 17 | } 18 | 19 | .loss { 20 | background-color: lighten($color-new-red, 30%); 21 | color: $color-primary; 22 | } 23 | 24 | .neutral { 25 | background-color: lighten($color-light-grey, 10%); 26 | color: $color-primary; 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /client/src/assets/scss/pages/_profile.scss: -------------------------------------------------------------------------------- 1 | .profile-page { 2 | 3 | button, a { 4 | outline: none; 5 | } 6 | 7 | &__title { 8 | color: $color-primary; 9 | padding: 2rem 0 0 2rem; 10 | font-size: 3rem; 11 | font-weight: 600; 12 | } 13 | 14 | &__content { 15 | font-size: $overall-font-secondary; 16 | 17 | &--note { 18 | background-color: $color-white; 19 | margin: 1rem; 20 | padding: 1rem; 21 | border-radius: 1rem; 22 | font-size: $overall-font-secondary; 23 | color: $color-primary; 24 | 25 | a { 26 | outline: none; 27 | } 28 | } 29 | 30 | .card-left, 31 | .card-right { 32 | .title { 33 | font-size: $overall-font-primary; 34 | padding: 0.5rem; 35 | padding-bottom: 1rem; 36 | font-weight: 600; 37 | } 38 | background-color: $color-white; 39 | margin: 1rem; 40 | padding: 1rem; 41 | border-radius: 2rem; 42 | } 43 | 44 | .card-left { 45 | .row { 46 | padding: 0; 47 | margin: 0.5rem; 48 | input { 49 | border-radius: 0.5rem; 50 | border: 1px solid $color-light-grey; 51 | padding: 0.5rem; 52 | margin: 0.5rem 0 0.5rem 0; 53 | width: 100%; 54 | } 55 | 56 | .logo-wrapper { 57 | .logo { 58 | width: 13rem; 59 | height: 13rem; 60 | } 61 | } 62 | } 63 | .field-key { 64 | font-weight: 600; 65 | padding-top: 1rem; 66 | } 67 | } 68 | 69 | .card-right { 70 | img { 71 | width: 100%; 72 | height: 100%; 73 | } 74 | } 75 | } 76 | 77 | @media screen and (max-width: 580px) { 78 | padding: 0rem; 79 | 80 | .col, 81 | .col-lg-6 { 82 | padding: 0rem; 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /client/src/assets/scss/pages/_signup.scss: -------------------------------------------------------------------------------- 1 | .sign-up { 2 | background-color: whitesmoke; 3 | // width: 100%; 4 | padding: 1rem; 5 | min-height: 95vh; 6 | } 7 | -------------------------------------------------------------------------------- /client/src/components/App.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { connect } from "react-redux"; 3 | import { Redirect, Switch, Route } from "react-router-dom"; 4 | import "../assets/scss/main.scss"; 5 | import Header from "../containers/Header"; 6 | import Dashboard from "../containers/Dashboard"; 7 | import SignUp from "../containers/Signup"; 8 | import Login from "../containers/Login"; 9 | import ReactNotification from "react-notifications-component"; 10 | import Footer from "./Footer"; 11 | import { ToastContainer } from "react-toastify"; 12 | 13 | import "react-bootstrap-table2-paginator/dist/react-bootstrap-table2-paginator.min.css"; 14 | import "react-bootstrap-table-next/dist/react-bootstrap-table2.min.css"; 15 | 16 | import "react-notifications-component/dist/theme.css"; 17 | import "react-toastify/dist/ReactToastify.css"; 18 | 19 | import ReactTooltip from "react-tooltip"; 20 | 21 | // import LineChart from "./Charts/LineChart"; 22 | // import ResistanceCard from "./ResistanceCard"; 23 | 24 | import Management from "../containers/Management"; 25 | import Monitor from "../containers/Monitor"; 26 | import Sidebar from "../containers/Sidebar"; 27 | import AdminManagement from "../containers/AdminManagement"; 28 | import AdminProfile from "../containers/AdminProfile"; 29 | import DailyStats from "../containers/DailyStats"; 30 | import Liquidity from "./Liquidity"; 31 | import ManageKeys from "./ManageKeys"; 32 | import LiquidityDetails from "./LiquidityDetails"; 33 | // import OrderBookSharing from "../containers/OrderBookSharing"; 34 | 35 | class App extends React.Component { 36 | renderSwitch() { 37 | if (this.props.auth.isLoggedIn) { 38 | return ( 39 | 40 | 41 | {/* */} 42 | 43 | {/* */} 44 | 45 | 46 | {/* */} 47 | {/* */} 48 | 49 | 50 | 51 | 55 | 56 | 57 | ); 58 | } else { 59 | return ( 60 | <> 61 | 62 | 63 | {/* */} 64 | 65 | 66 | 67 | 68 | ); 69 | } 70 | } 71 | 72 | render() { 73 | return ( 74 |
75 | 76 | 77 | 78 |
79 | 80 | {this.renderSwitch()} 81 | {/* */} 82 | {/*
84 | ); 85 | } 86 | } 87 | 88 | function mapStateToProps({ auth }) { 89 | return { auth }; 90 | } 91 | 92 | export default connect(mapStateToProps, null)(App); 93 | -------------------------------------------------------------------------------- /client/src/components/Charts/LineChart.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import ChartistGraph from "react-chartist"; 3 | 4 | let initalState = { 5 | // data: 0 6 | data: { 7 | labels: [1, 2, 3, 4, 5, 6, 7, 8], 8 | series: [[5, 9, 7, 8, 5, 3, 5, 11]], 9 | }, 10 | options: { 11 | height: "50vh", 12 | showArea: true, 13 | }, 14 | type: "Line", 15 | }; 16 | 17 | class Chart extends Component { 18 | constructor(props) { 19 | super(props); 20 | this.state = initalState; 21 | } 22 | 23 | /** 24 | * 25 | */ 26 | 27 | componentDidMount() { 28 | setInterval(() => { 29 | this.setState({ 30 | data: { 31 | ...this.state.data, 32 | series: [ 33 | this.state.data.series[0].map((e) => { 34 | return e + Math.sign(0.5 * Math.random()) * Math.random(); 35 | }), 36 | ], 37 | }, 38 | }); 39 | }, 2000); 40 | } 41 | 42 | render() { 43 | return ( 44 |
45 | 50 |
51 | ); 52 | } 53 | } 54 | 55 | export default Chart; 56 | -------------------------------------------------------------------------------- /client/src/components/EditCurrencyCard.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from "react"; 2 | import { Form, Col, Button, InputGroup, FormControl } from "react-bootstrap"; 3 | 4 | const EditCurrencyCard = (props) => { 5 | const [exchange, setExchange] = useState(props.exchange); 6 | const [symbol, setSymbol] = useState(props.symbol); 7 | const [name, setName] = useState(props.name); 8 | const [currencyId, setCurrencyId] = useState(props.currencyId); 9 | const [exchangeSymbol, setExchangeSymbol] = useState(props.exchangeSymbol); 10 | const [minimumBalance, setMinimumBalance] = useState(props.minimumBalance); 11 | 12 | return ( 13 | 14 |
15 |
16 | 17 | 18 | setExchange(value)} 23 | /> 24 | 25 | 26 | Exchange 27 | 28 | 29 | 30 | 31 | setSymbol(value)} 36 | /> 37 | 38 | 39 | Symbol 40 | 41 | 42 | 43 | 44 | setName(value)} 49 | /> 50 | 51 | 52 | Name 53 | 54 | 55 | 56 | 57 | setCurrencyId(value)} 62 | disabled 63 | /> 64 | 65 | 66 | Currency Id 67 | 68 | 69 | 70 | 71 | setExchangeSymbol(value)} 76 | /> 77 | 78 | 79 | Exchange Symbol 80 | 81 | 82 | 83 | 84 | setMinimumBalance(value)} 89 | /> 90 | 91 | 92 | Min Balance 93 | 94 | 95 | 96 | 97 |
98 | 115 |
116 |
117 |
118 |
119 | 120 | ); 121 | }; 122 | export default EditCurrencyCard; 123 | -------------------------------------------------------------------------------- /client/src/components/Exchange.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import { connect } from "react-redux"; 3 | import { RemoveExpo, CurrencyDecimalsAmount } from "../helpers/constant"; 4 | 5 | class PairPriceClass extends Component { 6 | constructor(props) { 7 | super(props); 8 | } 9 | 10 | render() { 11 | let n = this.props.value; 12 | let pair = this.props.pair; 13 | let exchange = this.props.exchange; 14 | 15 | if ( 16 | this.props.exchangeReducer.status && 17 | this.props.exchangeReducer.status.exchangePairDecimals[exchange] && 18 | this.props.exchangeReducer.status.exchangePairDecimals[exchange][pair] 19 | ) { 20 | n = parseFloat(n).toFixed( 21 | this.props.exchangeReducer.status.exchangePairDecimals[exchange][pair] 22 | .decimalsPrice 23 | ); 24 | } 25 | return <>{RemoveExpo(n)}; 26 | } 27 | } 28 | 29 | class PairAmountClass extends Component { 30 | constructor(props) { 31 | super(props); 32 | } 33 | 34 | render() { 35 | let n = this.props.value; 36 | let pair = this.props.pair; 37 | let exchange = this.props.exchange; 38 | 39 | let className = ""; 40 | 41 | if ( 42 | this.props.exchangeReducer.status && 43 | this.props.exchangeReducer.status.exchangePairDecimals[exchange] && 44 | this.props.exchangeReducer.status.exchangePairDecimals[exchange][pair] 45 | ) { 46 | n = parseFloat(n).toFixed( 47 | this.props.exchangeReducer.status.exchangePairDecimals[exchange][pair] 48 | .decimalsAmount 49 | ); 50 | if ( 51 | n < 52 | this.props.exchangeReducer.status.exchangePairDecimals[exchange][pair] 53 | .minAmount 54 | ) 55 | className = "lighten"; 56 | } 57 | 58 | return {RemoveExpo(n)}; 59 | } 60 | } 61 | 62 | class CurrencyAmountClass extends Component { 63 | constructor(props) { 64 | super(props); 65 | } 66 | 67 | render() { 68 | let n = this.props.value; 69 | let currency = this.props.currency; 70 | 71 | let className = ""; 72 | 73 | if ( 74 | CurrencyDecimalsAmount[currency] !== null || 75 | CurrencyDecimalsAmount[currency] !== undefined 76 | ) { 77 | n = parseFloat(n).toFixed(CurrencyDecimalsAmount[currency]); 78 | } else { 79 | n = parseFloat(n).toFixed(2); 80 | } 81 | 82 | return {RemoveExpo(n)}; 83 | } 84 | } 85 | 86 | function mapStateToProps({ exchangeReducer }) { 87 | return { exchangeReducer }; 88 | } 89 | 90 | const components = [PairAmountClass, PairPriceClass]; 91 | const connectedComponents = components.map(connect(mapStateToProps)); 92 | const [ConnectedPairAmount, ConnectedPairPrice] = connectedComponents; 93 | 94 | export const PairAmount = ConnectedPairAmount; 95 | export const PairPrice = ConnectedPairPrice; 96 | export const CurrencyAmount = CurrencyAmountClass; 97 | -------------------------------------------------------------------------------- /client/src/components/Footer.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Container, Row, Col } from "react-bootstrap"; 3 | 4 | function Footer() { 5 | return ( 6 |
7 | 8 | 9 | 10 |
About
11 |
12 |
    13 |
  • 14 | CrypBot is a bot. 15 |
    16 |
  • 17 |
18 |
19 | 20 |
21 |
22 |
23 | ); 24 | } 25 | 26 | export default Footer; 27 | -------------------------------------------------------------------------------- /client/src/components/FormHelper.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | import { Row, Col, Card, Button, FormControl } from "react-bootstrap"; 4 | 5 | export const RenderInput = (props) => { 6 | let labelCol = props.labelCol || 4; 7 | let labelColLg = props.labelColLg || labelCol; 8 | let labelColMd = props.labelColMd || labelColLg; 9 | let labelColSm = props.labelColSm || labelColMd; 10 | 11 | return ( 12 | 13 | 14 | {props.label} 15 | 16 | 17 | {props.element} 18 | 19 | 20 | ); 21 | }; 22 | -------------------------------------------------------------------------------- /client/src/components/IconCard.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | import { Row, Col } from "react-bootstrap"; 4 | import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; 5 | import { faCogs } from "@fortawesome/free-solid-svg-icons"; 6 | 7 | export default function IconCard(props) { 8 | const className = "icon-card " + props.className || ""; 9 | return ( 10 |
11 | 12 | 13 | 14 | 15 | 16 |
{props.title}
17 |
{props.text}
18 | 19 |
20 |
21 | ); 22 | } 23 | -------------------------------------------------------------------------------- /client/src/components/StaticTable.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | 3 | import BootstrapTable from "react-bootstrap-table-next"; // React Bootstrap Table Next 4 | import paginationFactory, { 5 | PaginationProvider, 6 | PaginationListStandalone, 7 | } from "react-bootstrap-table2-paginator"; 8 | 9 | class DataTable extends Component { 10 | constructor(props) { 11 | super(props); 12 | this.renderData = this.props.renderData.bind(this); 13 | } 14 | 15 | render() { 16 | let tableData = []; 17 | if (!this.props.data) { 18 | tableData = this.props.prepopulatedData; 19 | } else if (this.props.data && this.props.data.length === 0) { 20 | tableData = this.props.emptyData || []; 21 | } else { 22 | tableData = 23 | this.renderData(this.props.data) || this.props.prepopulatedData; 24 | } 25 | let columns = this.props.columns; 26 | 27 | return ( 28 |
29 |
30 | 48 | {({ paginationProps, paginationTableProps }) => ( 49 |
50 |
51 | {/* */} 52 | 53 | 60 |
61 |
{this.props.tableFooter || <>}
62 |
63 | )} 64 |
65 |
66 |
67 | ); 68 | } 69 | } 70 | 71 | export default DataTable; 72 | -------------------------------------------------------------------------------- /client/src/components/SuperAdminComponent.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import { connect } from "react-redux"; 3 | 4 | class SuperAdminComponent extends Component { 5 | constructor(props) { 6 | super(props); 7 | } 8 | 9 | render() { 10 | let element = <>; 11 | if ( 12 | this.props.auth.isLoggedIn == true && 13 | this.props.auth.data.level === 2 14 | ) { 15 | element = <>{this.props.element}; 16 | } 17 | return <>{element}; 18 | } 19 | } 20 | 21 | function mapStateToProps({ auth }) { 22 | return { auth }; 23 | } 24 | 25 | export default connect(mapStateToProps, null)(SuperAdminComponent); 26 | -------------------------------------------------------------------------------- /client/src/components/Wallet.js: -------------------------------------------------------------------------------- 1 | import { faCogs } from "@fortawesome/free-solid-svg-icons"; 2 | import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; 3 | import React, { Component } from "react"; 4 | import { Card, Button } from "react-bootstrap"; 5 | import { connect } from "react-redux"; 6 | 7 | import * as actions from "../actions/index"; 8 | import { 9 | AddDelimiter, 10 | GetDateLocalFormat, 11 | PairDecimalsAmount, 12 | } from "../helpers/constant"; 13 | import StaticTable from "./StaticTable"; 14 | import DataTable from "./DataTable"; 15 | 16 | const initialState = { balances: { data: [], ts: null } }; 17 | 18 | const rooms = ["wallet"]; 19 | 20 | const columns = [ 21 | { 22 | dataField: "exchange", 23 | text: "Exchange", 24 | }, 25 | { 26 | dataField: "currency", 27 | text: "Currency", 28 | }, 29 | // { 30 | // dataField: "account", 31 | // text: "Account", 32 | // }, 33 | { 34 | dataField: "balance", 35 | text: "Balance", 36 | headerStyle: () => { 37 | return { width: "100px", textAlign: "left" }; 38 | }, 39 | }, 40 | { 41 | dataField: "inTrade", 42 | text: "In Trade", 43 | headerStyle: () => { 44 | return { width: "100px", textAlign: "left" }; 45 | }, 46 | }, 47 | { 48 | dataField: "total", 49 | text: "Total", 50 | headerStyle: () => { 51 | return { width: "100px", textAlign: "left" }; 52 | }, 53 | }, 54 | ]; 55 | 56 | const prepopulatedExchange = [ 57 | { 58 | id: 1, 59 | exchange: ( 60 |
61 | 62 |
LOADING
63 |
64 | ), 65 | }, 66 | ]; 67 | 68 | const EmptyData = [ 69 | { 70 | id: 1, 71 | exchange: ( 72 |
73 |
No Data
74 |
75 | ), 76 | }, 77 | ]; 78 | 79 | class Wallet extends Component { 80 | constructor(props) { 81 | super(props); 82 | 83 | this.state = initialState; 84 | } 85 | 86 | componentDidMount() { 87 | this.props.connectSocket(rooms); 88 | } 89 | 90 | componentWillReceiveProps(nextProps) { 91 | if (nextProps.socketReducer.status !== "connected") { 92 | this.props.connectSocket(rooms); 93 | } else { 94 | if (this.props.socketReducer.socket) 95 | this.props.socketReducer.socket.removeAllListeners(); 96 | 97 | nextProps.socketReducer.socket.emit("joinRooms", rooms); 98 | 99 | nextProps.socketReducer.socket.on("balances", (balances) => { 100 | const exchanges = Object.keys(balances); 101 | let data = [], 102 | ts; 103 | for (let exchange of exchanges) { 104 | data = data.concat(balances[exchange].data); 105 | ts = balances[exchange].ts; 106 | } 107 | this.setState({ balances: { data, ts } }); 108 | }); 109 | } 110 | } 111 | 112 | renderBalances(balances) { 113 | return balances.map((balance, i) => { 114 | return { 115 | id: i, 116 | // account: balance.botName, 117 | exchange: balance.exchange, 118 | currency: balance.currency, 119 | inTrade: AddDelimiter( 120 | parseFloat(balance.inTrade).toFixed( 121 | PairDecimalsAmount[`${balance.currency}-USDT`] 122 | ) 123 | ), 124 | balance: 125 | balance.balance >= balance.minBalance ? ( 126 | 127 | {AddDelimiter( 128 | parseFloat(balance.balance).toFixed( 129 | PairDecimalsAmount[`${balance.currency}-USDT`] 130 | ) 131 | )} 132 | 133 | ) : ( 134 | 135 | {AddDelimiter( 136 | parseFloat(balance.balance).toFixed( 137 | PairDecimalsAmount[`${balance.currency}-USDT`] 138 | ) 139 | )} 140 | 141 | ), 142 | total: AddDelimiter( 143 | parseFloat(balance.total).toFixed( 144 | PairDecimalsAmount[`${balance.currency}-USDT`] 145 | ) 146 | ), 147 | }; 148 | }); 149 | } 150 | 151 | render() { 152 | return ( 153 | <> 154 | 155 |
{this.props.header || ""}
156 |
157 | 165 |
166 |
167 |
168 |
169 | Last Updatded:{" "} 170 | {this.state.balances.ts 171 | ? GetDateLocalFormat(this.state.balances.ts) 172 | : "loading"} 173 |
174 |
175 | 182 |
183 |
184 | 185 | ); 186 | } 187 | } 188 | 189 | function mapStateToProps({ socketReducer, exchangeReducer }) { 190 | return { socketReducer, exchangeReducer }; 191 | } 192 | 193 | function mapDispatchToProps(dispatch) { 194 | return { 195 | connectSocket: () => dispatch(actions.ConnectSocket()), 196 | }; 197 | } 198 | export default connect(mapStateToProps, mapDispatchToProps)(Wallet); 199 | -------------------------------------------------------------------------------- /client/src/containers/AdminManagement.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import { Row, Card, Col, Form, Button } from "react-bootstrap"; 3 | import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; 4 | import { faCogs } from "@fortawesome/free-solid-svg-icons"; 5 | import StaticTable from "../components/StaticTable"; 6 | import { RenderInput } from "../components/FormHelper"; 7 | import * as actions from "../actions/index"; 8 | import { connect } from "react-redux"; 9 | import { AddNoti } from "../helpers/Notification"; 10 | import { Capitalize } from "../helpers/constant"; 11 | 12 | const columns = [ 13 | { 14 | dataField: "email", 15 | text: "Email", 16 | headerStyle: () => { 17 | return { width: "150px" }; 18 | }, 19 | }, 20 | { 21 | dataField: "name", 22 | text: "Name", 23 | }, 24 | { 25 | dataField: "level", 26 | text: "Level", 27 | }, 28 | { 29 | dataField: "alert", 30 | text: "Alert", 31 | }, 32 | ]; 33 | 34 | const prepopulatedAdmin = [ 35 | { 36 | id: 1, 37 | level: ( 38 |
39 | 40 |
LOADING
41 |
42 | ), 43 | }, 44 | ]; 45 | 46 | const EmptyData = [ 47 | { 48 | id: 1, 49 | level: ( 50 |
51 |
No Data
52 |
53 | ), 54 | }, 55 | ]; 56 | 57 | const initialState = { 58 | addAdmin: null, 59 | removeAdmin: null, 60 | }; 61 | class AdminManagement extends Component { 62 | constructor(props) { 63 | super(props); 64 | this.state = initialState; 65 | } 66 | 67 | componentDidMount() { 68 | this.props.getAdmin(); 69 | } 70 | 71 | componentDidUpdate(prevProps) { 72 | let data = Object.keys(this.props.adminReducer); 73 | for (let i = 0; i < data.length; i++) { 74 | let adminData = this.props.adminReducer[data[i]]; 75 | if ( 76 | adminData.success && 77 | prevProps.adminReducer[data[i]].v !== adminData.v 78 | ) { 79 | AddNoti(adminData.success, { type: "info", position: "bottom-right" }); 80 | if (data[i] !== "status") this.props.getAdmin(); 81 | } 82 | if (adminData.error && prevProps.adminReducer[data[i]].v !== adminData.v) { 83 | AddNoti(adminData.error, { type: "error", position: "bottom-right" }); 84 | if (data[i] !== "status") this.props.getAdmin(); 85 | } 86 | } 87 | } 88 | 89 | renderAdminData(allData) { 90 | if (!allData || allData.length === 0) { 91 | return EmptyData; 92 | } 93 | return allData.map((e, i) => { 94 | return { 95 | id: i, 96 | email: e.email, 97 | name: Capitalize(e.name), 98 | level: e.level, 99 | alert: e.alerts || 0, 100 | }; 101 | }); 102 | 103 | } 104 | 105 | render() { 106 | return ( 107 |
108 |
Admin Management
109 | 110 | 111 | 112 |
Admin Info
113 |
114 | 120 |
121 |
122 | 123 | 124 | 125 |
Add Admin
126 |
127 |
128 | 136 | this.setState({ 137 | addAdmin: e.target.value, 138 | }) 139 | } 140 | /> 141 | } 142 | /> 143 |
144 | 151 |
152 | 153 |
154 |
155 | 156 |
Remove Admin
157 |
158 |
159 | 167 | this.setState({ 168 | removeAdmin: e.target.value, 169 | }) 170 | } 171 | /> 172 | } 173 | /> 174 |
175 | 184 |
185 | 186 |
187 |
188 | 189 |
190 |
191 | ); 192 | } 193 | } 194 | 195 | function mapStateToProps({ adminReducer }) { 196 | return { adminReducer }; 197 | } 198 | 199 | function mapDispathToProps(dispatch) { 200 | return { 201 | getAdmin: () => dispatch(actions.GetAdmin()), 202 | addAdmin: (data) => dispatch(actions.AddAdmin(data)), 203 | removeAdmin: (data) => dispatch(actions.RemoveAdmin(data)), 204 | }; 205 | } 206 | export default connect(mapStateToProps, mapDispathToProps)(AdminManagement); 207 | -------------------------------------------------------------------------------- /client/src/containers/AdminProfile.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import * as actions from "../actions/index"; 3 | import { connect } from "react-redux"; 4 | import { AddNoti } from "../helpers/Notification"; 5 | import { Row, Card, Col, Form, Button, FormControl } from "react-bootstrap"; 6 | import { RenderInput } from "../components/FormHelper"; 7 | import { alertLevel } from "../helpers/constant"; 8 | 9 | const initialState = { 10 | alertLevel: null, 11 | name: null, 12 | email: null, 13 | }; 14 | 15 | class AdminProfile extends Component { 16 | constructor(props) { 17 | super(props); 18 | this.state = initialState; 19 | } 20 | 21 | componentDidUpdate(prevProps) { 22 | let data = this.props.adminProfileReducer.updateProfile; 23 | if ( 24 | data.success && 25 | prevProps.adminProfileReducer.updateProfile.v !== data.v 26 | ) { 27 | AddNoti(data.success, { type: "info", position: "bottom-right" }); 28 | } 29 | if ( 30 | data.error && 31 | prevProps.adminProfileReducer.updateProfile.v !== data.v 32 | ) { 33 | AddNoti(data.error, { type: "error", position: "bottom-right" }); 34 | } 35 | // if (this.props.auth.success && prevProps.auth.v !== this.props.auth.v) { 36 | // AddNoti(this.props.auth.success, { type: "info", position: "bottom-right" }); 37 | // } 38 | // if (this.props.auth.error && prevProps.auth.v !== this.props.auth.v) { 39 | // AddNoti(this.props.auth.error, { type: "info", position: "bottom-right" }); 40 | // } 41 | } 42 | 43 | render() { 44 | 45 | if ( 46 | this.props.auth && 47 | this.props.auth.data 48 | ) { 49 | var name = this.props.auth.data.name; 50 | var email = this.props.auth.data.email; 51 | var alerts = this.props.auth.data.alerts; 52 | var level = this.props.auth.data.level; 53 | } 54 | 55 | return ( 56 |
57 | 58 | 59 | 60 |
Admin Alert Level
61 |
62 |
63 | { 69 | this.setState({ 70 | alertLevel: e.target.value, 71 | }); 72 | }} 73 | > 74 | 75 | 76 | 77 | 78 | } 79 | /> 80 |
81 | 90 |
91 | 92 |
93 |
94 | 95 | 96 | 97 |
Admin Profile Info
98 |
99 |
100 | 109 | } 110 | /> 111 | 120 | } 121 | /> 122 | 131 | } 132 | /> 133 | 142 | } 143 | /> 144 | 145 |
146 |
147 | 148 |
149 |
150 | ); 151 | } 152 | } 153 | 154 | function mapStateToProps({ auth, adminProfileReducer }) { 155 | return { auth, adminProfileReducer }; 156 | } 157 | 158 | function mapDispathToProps(dispatch) { 159 | return { 160 | updateMailProfile: (data) => dispatch(actions.UpdateMailProfile(data)), 161 | }; 162 | } 163 | export default connect(mapStateToProps, mapDispathToProps)(AdminProfile); 164 | -------------------------------------------------------------------------------- /client/src/containers/Dashboard.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import { connect } from "react-redux"; 3 | import { Redirect, Switch, Route } from "react-router-dom"; 4 | 5 | class DashboardContainer extends Component { 6 | componentWillMount() {} 7 | 8 | render() { 9 | if (this.props.auth.isLoggedIn === true) { 10 | return ; 11 | } else { 12 | return ; 13 | } 14 | } 15 | } 16 | 17 | function mapStateToProps({ auth, siteStats }) { 18 | return { auth, siteStats }; 19 | } 20 | 21 | export default connect(mapStateToProps, null)(DashboardContainer); 22 | -------------------------------------------------------------------------------- /client/src/containers/Header.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import { connect } from "react-redux"; 3 | import * as actions from "../actions/index"; 4 | import { Navbar, Nav, NavDropdown } from "react-bootstrap"; 5 | import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; 6 | import { 7 | faUser, 8 | faPowerOff, 9 | faDesktop, 10 | faRobot, 11 | } from "@fortawesome/free-solid-svg-icons"; 12 | import RouteButton from "../hooks/RouteButton"; 13 | 14 | const initialAuth = localStorage.getItem("corp-auth-status"); 15 | 16 | const AppName = process.env.REACT_APP_NAME || "CrypBot"; 17 | 18 | class Header extends Component { 19 | constructor(props) { 20 | super(props); 21 | 22 | this.renderAccount = this.renderAccount.bind(this); 23 | this.renderLogin = this.renderLogin.bind(this); 24 | } 25 | 26 | componentWillMount() { 27 | this.props.fetchUserStatus(); 28 | } 29 | 30 | componentDidUpdate() { 31 | if (this.props.auth.isLoggedIn === true) this.props.connectSocket(); 32 | } 33 | 34 | renderLogin() { 35 | return ( 36 |
37 | 49 |
50 | ); 51 | } 52 | 53 | renderAccount() { 54 | return ( 55 |
56 | 87 |
88 | ); 89 | } 90 | 91 | render() { 92 | const vert_align = { 93 | display: "flex", 94 | flexDirection: "column", 95 | }; 96 | 97 | return ( 98 |
99 | 100 | 105 | 106 | 107 |   {AppName} 108 | 109 | 110 | } 111 | /> 112 | 113 | 114 | 118 | {this.props.auth 119 | ? this.props.auth.isLoggedIn === true 120 | ? this.renderAccount() 121 | : this.renderLogin() 122 | : initialAuth == "true" 123 | ? this.renderAccount() 124 | : this.renderLogin()} 125 | 126 | 127 |
128 | ); 129 | } 130 | } 131 | 132 | function mapStateToProps({ auth }) { 133 | return { auth }; 134 | } 135 | 136 | function mapDispatchToProps(dispatch) { 137 | return { 138 | logout: () => dispatch(actions.logout()), 139 | fetchUserStatus: () => dispatch(actions.fetchUserStatus()), 140 | connectSocket: () => dispatch(actions.ConnectSocket()), 141 | }; 142 | } 143 | 144 | export default connect(mapStateToProps, mapDispatchToProps)(Header); 145 | -------------------------------------------------------------------------------- /client/src/containers/Login.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import { connect } from "react-redux"; 3 | import { Redirect } from "react-router-dom"; 4 | import { Form, Button, Container, Row, Col } from "react-bootstrap"; 5 | import { AddNoti } from "../helpers/Notification"; 6 | import RequireLogin from "../middleware/RequireLogin"; 7 | import { GetParamValue } from "../helpers/constant"; 8 | import RequireLogout from "../middleware/RequireLogout"; 9 | 10 | import * as actions from "../actions/index"; 11 | 12 | const initialState = { 13 | email: "", 14 | password: "", 15 | redirect: false, 16 | redirectTo: "/", 17 | error: null, 18 | success: null, 19 | }; 20 | 21 | class SignUp extends Component { 22 | constructor(props) { 23 | super(props); 24 | this.state = initialState; 25 | this.companyLogoInput = React.createRef(); 26 | 27 | this.handleLogInPost = this.handleLogInPost.bind(this); 28 | this.renderRedirect = this.renderRedirect.bind(this); 29 | } 30 | 31 | setRedirect = () => { 32 | this.setState({ 33 | redirect: true, 34 | }); 35 | }; 36 | 37 | renderRedirect = () => { 38 | if (this.state.redirect) { 39 | return ; 40 | } 41 | }; 42 | 43 | handleLogInPost() { 44 | this.props.login({ 45 | email: this.state.email, 46 | password: this.state.password, 47 | }); 48 | } 49 | 50 | componentWillReceiveProps(nextProps) { 51 | let { success, error, v } = nextProps.auth; 52 | 53 | // console.log(":login :auth", v, this.props.auth.v, error); 54 | 55 | if (success && v !== this.props.auth.v) { 56 | AddNoti("Welcome", { type: "success" }); 57 | } 58 | 59 | if (error && v !== this.props.auth.v) { 60 | AddNoti(error, { type: "error" }); 61 | } 62 | } 63 | 64 | render() { 65 | let { loading, isLoggedIn } = this.props.auth; 66 | let btnMsg = "Submit"; 67 | 68 | if (loading) { 69 | btnMsg = "loading"; 70 | } 71 | 72 | if (isLoggedIn) { 73 | this.setState({ redirect: true, redirectTo: "/liquidity" }); 74 | } 75 | 76 | return ( 77 |
78 | {/* */} 79 | {this.renderRedirect()} 80 |
81 | 82 | 83 | 84 | 85 |
86 |
Log In
87 |
88 |
89 |
90 | 91 | Email address 92 | { 97 | this.setState({ email: e.target.value }); 98 | }} 99 | /> 100 | 101 | 102 | 103 | Password 104 | { 109 | this.setState({ password: e.target.value }); 110 | }} 111 | /> 112 | 113 | 114 | 121 |
122 |
123 |
124 | 125 | 126 |
127 |
128 |
129 |
130 | ); 131 | } 132 | } 133 | 134 | function mapStateToProps({ auth }) { 135 | return { auth }; 136 | } 137 | 138 | function mapDispathToProps(dispatch) { 139 | return { 140 | login: (data) => dispatch(actions.login(data)), 141 | }; 142 | } 143 | 144 | export default connect(mapStateToProps, mapDispathToProps)(SignUp); 145 | -------------------------------------------------------------------------------- /client/src/containers/Sidebar.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { connect } from "react-redux"; 3 | import { Button } from "react-bootstrap"; 4 | 5 | import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; 6 | import { 7 | faBookOpen, 8 | faGlobe, 9 | faChartArea, 10 | faGripLines, 11 | faUserShield, 12 | faAnkh, 13 | faPoll, 14 | faUserLock, 15 | faUserCog, 16 | faPercent, 17 | faCalendarDay, 18 | faBahai, 19 | faDollarSign, 20 | faKey, 21 | } from "@fortawesome/free-solid-svg-icons"; 22 | 23 | import SuperAdminComponent from "../components/SuperAdminComponent"; 24 | 25 | import RouteButton from "../hooks/RouteButton"; 26 | 27 | class Sidebar extends React.Component { 28 | constructor() { 29 | super(); 30 | 31 | this.state = { 32 | sidebarCollapsed: false, 33 | activeLink: null, 34 | }; 35 | } 36 | 37 | renderRouteButton(title, icon, to, onClick = null) { 38 | let className = "sidebar-nav"; 39 | 40 | const url = document.location.pathname; 41 | 42 | if (url === to) { 43 | className += " sidebar-nav__active"; 44 | } 45 | 46 | return ( 47 | 50 |
51 | 52 |
53 |
{title}
54 | 55 | } 56 | to={to} 57 | onClick={onClick} 58 | /> 59 | ); 60 | } 61 | 62 | render() { 63 | let sidebarClass = "sidebar"; 64 | 65 | if (!this.state.sidebarCollapsed) { 66 | sidebarClass += " sidebar-open"; 67 | } 68 | 69 | if (!this.props.auth.isLoggedIn) sidebarClass += " sidebar-none"; 70 | return ( 71 |
72 |
73 | {this.renderRouteButton( 74 | "Add Liquidity", 75 | faDollarSign, 76 | "/liquidity", 77 | () => { 78 | this.setState({ activeLink: "/liquidity" }); 79 | } 80 | )} 81 | 82 | {this.renderRouteButton( 83 | "Management", 84 | faUserShield, 85 | "/management", 86 | () => { 87 | this.setState({ activeLink: "/management" }); 88 | } 89 | )} 90 | 91 | {this.renderRouteButton("Manage Keys", faKey, "/manage-keys", () => 92 | this.setState({ activeLink: "/manage-keys" }) 93 | )} 94 | 95 | {/* {this.renderRouteButton( 96 | "Admin Profile", 97 | faUserCog, 98 | "/admin-profile", 99 | () => { 100 | this.setState({ activeLink: "/admin-profile" }); 101 | } 102 | )} */} 103 | 104 | {this.renderRouteButton( 105 | "Daily Stats", 106 | faCalendarDay, 107 | "/daily-stats", 108 | () => { 109 | this.setState({ activeLink: "/daily-stats" }); 110 | } 111 | )} 112 | 113 | {/* { 119 | this.setState({ activeLink: "/admin-management" }); 120 | } 121 | )} 122 | /> */} 123 | 124 | {this.renderRouteButton("Monitor", faChartArea, "/monitor", () => { 125 | this.setState({ activeLink: "/monitor" }); 126 | })} 127 |
128 | 129 |
130 | 149 |
150 |
151 | ); 152 | } 153 | } 154 | 155 | function mapStateToProps({ auth }) { 156 | return { auth }; 157 | } 158 | 159 | export default connect(mapStateToProps, null)(Sidebar); 160 | -------------------------------------------------------------------------------- /client/src/containers/Signup.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import Formdata from "form-data"; 3 | import { Redirect } from "react-router-dom"; 4 | import { Form, Button, Container, Row, Col } from "react-bootstrap"; 5 | import RequireLogout from "../middleware/RequireLogout"; 6 | 7 | import { AxiosInstance, PostConfig } from "../helpers/constant"; 8 | 9 | import { AddNoti } from "../helpers/Notification"; 10 | import ScrollToTop from "../hooks/ScrollToTop"; 11 | import { ParseError } from "../helpers/ResponseHelper"; 12 | 13 | const initialState = { 14 | name: "", 15 | email: "", 16 | password: "", 17 | confirmPassword: "", 18 | redirect: false, 19 | redirectTo: "/", 20 | }; 21 | 22 | class SignUp extends Component { 23 | constructor(props) { 24 | super(props); 25 | this.state = initialState; 26 | this.companyLogoInput = React.createRef(); 27 | 28 | this.handleSignUpPost = this.handleSignUpPost.bind(this); 29 | this.renderRedirect = this.renderRedirect.bind(this); 30 | } 31 | 32 | handleSignUpPost() { 33 | const email = this.state.email; 34 | const password = this.state.password; 35 | 36 | const newForm = { 37 | name: this.state.name, 38 | email, 39 | password, 40 | }; 41 | 42 | AxiosInstance.post("/admin/sign-up", newForm, PostConfig) 43 | .then((resp) => { 44 | resp = resp.data; 45 | if (resp.statusCode === 201) { 46 | AddNoti("Please login to continue!", { 47 | type: "success", 48 | }); 49 | this.setState({ 50 | ...initialState, 51 | redirect: true, 52 | redirectTo: "/login", 53 | }); 54 | } else { 55 | AddNoti(resp.error || resp.message, { 56 | type: "error", 57 | }); 58 | } 59 | }) 60 | .catch((e) => { 61 | AddNoti(ParseError(e), { 62 | type: "error", 63 | }); 64 | }); 65 | } 66 | 67 | renderRedirect = () => { 68 | if (this.state.redirect) { 69 | return ; 70 | } 71 | }; 72 | 73 | render() { 74 | return ( 75 |
76 | 77 | {/* */} 78 | {this.renderRedirect()} 79 |
80 | 81 | 82 | 83 | 84 |
85 |
Sign Up
86 |
87 |
88 |
89 | 90 | Name 91 | { 96 | this.setState({ name: e.target.value }); 97 | }} 98 | /> 99 | 100 | 101 | 102 | Email address 103 | { 108 | this.setState({ email: e.target.value }); 109 | }} 110 | /> 111 | 112 | 113 | 114 | Password 115 | { 120 | this.setState({ password: e.target.value }); 121 | }} 122 | /> 123 | 124 | 125 | 126 | Confirm Password 127 | { 132 | this.setState({ confirmPassword: e.target.value }); 133 | }} 134 | /> 135 | 136 | 137 | 144 |
145 |
146 |
147 | 148 | 149 |
150 |
151 |
152 |
153 | ); 154 | } 155 | } 156 | 157 | export default SignUp; 158 | -------------------------------------------------------------------------------- /client/src/helpers/Notification.js: -------------------------------------------------------------------------------- 1 | import { store } from "react-notifications-component"; 2 | import { toast } from "react-toastify"; 3 | 4 | export const AddNoti = ( 5 | msg, 6 | { 7 | type = "info", 8 | position = "top-right", 9 | duration = 2000, 10 | hideProgressBar = false, 11 | closeOnClick = true, 12 | closeButton=true 13 | } 14 | ) => { 15 | return toast(msg, { 16 | type: type, 17 | position, 18 | autoClose: duration, 19 | className: "custom-toast", 20 | hideProgressBar, 21 | closeOnClick, 22 | closeButton 23 | }); 24 | 25 | // store.addNotification({ 26 | // id: id, 27 | // title: title, 28 | // message: msg, 29 | // type: type, 30 | // insert: "top", 31 | // container: container, 32 | // animationIn: ["animated", "fadeIn"], 33 | // animationOut: ["animated", "fadeOut"], 34 | // dismiss: dismiss, 35 | // }); 36 | }; 37 | 38 | export const RemoveNoti = (id) => { 39 | toast.dismiss(id); 40 | }; 41 | -------------------------------------------------------------------------------- /client/src/helpers/PaymentModal.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Modal from "react-bootstrap/Modal"; 3 | import { Row, Col, Container, Button } from "react-bootstrap"; 4 | 5 | function PaymentModal(props) { 6 | return ( 7 | 15 | 16 | 17 | Select Payment Mode 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | ); 34 | } 35 | 36 | export default PaymentModal; 37 | -------------------------------------------------------------------------------- /client/src/helpers/ResponseHelper.js: -------------------------------------------------------------------------------- 1 | const DefaulErrortMsg = "Something went wrong"; 2 | const DefaultSuccessMsg = "Success"; 3 | 4 | export const ParseError = (e) => { 5 | if (e.errors) { 6 | return e.errors[0].field 7 | ? `${e.errors[0].field}: ${e.errors[0].message}` 8 | : e.errors[0].message; 9 | } 10 | return e 11 | ? e.response 12 | ? e.response.data 13 | ? e.response.data.errors 14 | ? e.response.data.errors[0].field 15 | ? `${e.response.data.errors[0].field}: ${e.response.data.errors[0].message}` 16 | : e.response.data.errors[0].message 17 | : DefaulErrortMsg 18 | : DefaulErrortMsg 19 | : e.message 20 | ? e.message 21 | : DefaulErrortMsg 22 | : DefaulErrortMsg; 23 | }; 24 | 25 | export const ParseSuccess = (e) => { 26 | return e.response 27 | ? e.response.data 28 | ? e.response.data.message 29 | : DefaultSuccessMsg 30 | : DefaultSuccessMsg; 31 | }; 32 | -------------------------------------------------------------------------------- /client/src/helpers/withRouter.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { useParams } from "react-router-dom"; 3 | 4 | const withRouter = (WrappedComponent) => (props) => { 5 | const params = useParams(); 6 | 7 | return ; 8 | }; 9 | 10 | export default withRouter; 11 | -------------------------------------------------------------------------------- /client/src/hooks/RouteButton.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { useHistory } from "react-router-dom"; 3 | 4 | function RouteButton(props) { 5 | const history = useHistory(); 6 | let finalClassName = ""; 7 | 8 | function handleClick() { 9 | history.push(props.to); 10 | } 11 | 12 | if (props.className !== undefined) { 13 | finalClassName = `route-btn ${props.className}`; 14 | } else { 15 | finalClassName = "route-btn"; 16 | } 17 | 18 | return ( 19 |
{ 22 | handleClick(); 23 | if (props.onClick) props.onClick(); 24 | }} 25 | > 26 | {props.value} 27 |
28 | ); 29 | } 30 | 31 | export default RouteButton; 32 | 33 | export const RedirectTo = (to) => { 34 | const history = useHistory(); 35 | history.push(to); 36 | }; 37 | -------------------------------------------------------------------------------- /client/src/hooks/ScrollToTop.js: -------------------------------------------------------------------------------- 1 | import { useEffect } from "react"; 2 | import { useLocation } from "react-router-dom"; 3 | 4 | export default function ScrollToTop() { 5 | const { pathname } = useLocation(); 6 | 7 | useEffect(() => { 8 | window.scrollTo(0, 0); 9 | }, [pathname]); 10 | 11 | return null; 12 | } 13 | -------------------------------------------------------------------------------- /client/src/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom"; 3 | import { Provider } from "react-redux"; 4 | import { BrowserRouter as Router } from "react-router-dom"; 5 | 6 | import App from "./components/App"; 7 | import * as serviceWorker from "./serviceWorker"; 8 | import reduxStore from "./redux/store"; 9 | import { createBrowserHistory } from "history"; 10 | import { AppContainer } from "react-hot-loader"; 11 | 12 | import "react-datepicker/dist/react-datepicker.css"; 13 | 14 | const customHistory = createBrowserHistory(); 15 | 16 | const store = reduxStore(); 17 | 18 | ReactDOM.render( 19 | 20 | 21 | 22 | 23 | 24 | 25 | , 26 | document.getElementById("root") 27 | ); 28 | 29 | if (module.hot) { 30 | module.hot.accept("./components/App", () => { 31 | const NextApp = require("./components/App").default; 32 | 33 | ReactDOM.render( 34 | 35 | 36 | , 37 | document.getElementById("root") 38 | ); 39 | }); 40 | } 41 | serviceWorker.unregister(); 42 | -------------------------------------------------------------------------------- /client/src/middleware/RequireLogin.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Redirect } from "react-router-dom"; 3 | 4 | function RequireLogin() { 5 | const currPath = document.location.pathname; 6 | const toLink = `/login?from=${currPath.slice(1)}`; 7 | const initialAuth = localStorage.getItem("corp-auth-status") == "true"; 8 | 9 | // console.log("INSIDE RequireLogin: ", currPath, toLink, initialAuth); 10 | 11 | if (initialAuth === false) { 12 | return ( 13 | <> 14 | 15 | 16 | ); 17 | } else { 18 | return <>; 19 | } 20 | } 21 | 22 | export default RequireLogin; 23 | -------------------------------------------------------------------------------- /client/src/middleware/RequireLogout.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Redirect } from "react-router-dom"; 3 | 4 | function RequireLogout() { 5 | const currPath = document.location.pathname; 6 | const toLink = `/`; 7 | const initialAuth = localStorage.getItem("corp-auth-status") == "true"; 8 | 9 | if (initialAuth === true) { 10 | return ( 11 | <> 12 | 13 | 14 | ); 15 | } else { 16 | return <>; 17 | } 18 | } 19 | 20 | export default RequireLogout; 21 | -------------------------------------------------------------------------------- /client/src/reducers/AdminProfileReducer.js: -------------------------------------------------------------------------------- 1 | import * as types from "../actions/types"; 2 | 3 | const initialState = { 4 | updateProfile: { 5 | success: null, 6 | error: null, 7 | loading: false, 8 | data: null, 9 | ts: null, 10 | v: 0, 11 | } 12 | } 13 | 14 | export default function (state = initialState, action) { 15 | switch (action.type) { 16 | case types.UPDATE_ADMIN_PROFILE_START: { 17 | return { 18 | ...state, 19 | updateProfile: { 20 | ...state.updateProfile, 21 | loading: true, 22 | success: null, 23 | error: null 24 | } 25 | } 26 | } 27 | 28 | case types.UPDATE_ADMIN_PROFILE_SUCCESS: { 29 | return { 30 | ...state, 31 | updateProfile: { 32 | ...state.updateProfile, 33 | loading: false, 34 | success: action.success, 35 | data: action.data, 36 | v: state.updateProfile.v + 1 37 | } 38 | } 39 | } 40 | 41 | case types.UPDATE_ADMIN_PROFILE_FAIL: { 42 | return { 43 | ...state, 44 | updateProfile: { 45 | ...state.updateProfile, 46 | data: null, 47 | error: action.error, 48 | v: state.updateProfile.v + 1 49 | } 50 | } 51 | } 52 | 53 | case types.UPDATE_ADMIN_PROFILE_FINISH: { 54 | return { 55 | ...state, 56 | updateProfile: { 57 | ...state.updateProfile, 58 | ts: Date.now(), 59 | } 60 | } 61 | } 62 | default: 63 | return state; 64 | } 65 | } -------------------------------------------------------------------------------- /client/src/reducers/AdminReducer.js: -------------------------------------------------------------------------------- 1 | import * as types from "../actions/types"; 2 | 3 | const initialState = { 4 | status: { 5 | success: null, 6 | error: null, 7 | loading: false, 8 | data: null, 9 | ts: null, 10 | v: 0, 11 | }, 12 | addAdmin: { 13 | success: null, 14 | error: null, 15 | loading: false, 16 | data: null, 17 | ts: null, 18 | v: 0, 19 | }, 20 | removeAdmin: { 21 | success: null, 22 | error: null, 23 | loading: false, 24 | data: null, 25 | ts: null, 26 | v: 0, 27 | }, 28 | }; 29 | 30 | export default function (state = initialState, action) { 31 | switch (action.type) { 32 | case types.GET_ADMIN_START: { 33 | return { 34 | ...state, 35 | status: { 36 | ...state.status, 37 | loading: true, 38 | success: null, 39 | error: null, 40 | }, 41 | }; 42 | } 43 | case types.GET_ADMIN_SUCCESS: { 44 | return { 45 | ...state, 46 | status: { 47 | ...state.status, 48 | loading: false, 49 | success: action.success, 50 | data: action.data, 51 | v: state.status.v + 1, 52 | }, 53 | }; 54 | } 55 | case types.GET_ADMIN_FAIL: { 56 | return { 57 | ...state, 58 | status: { 59 | ...state.status, 60 | data: null, 61 | success: null, 62 | error: action.error, 63 | v: state.status.v + 1, 64 | }, 65 | }; 66 | } 67 | case types.GET_ADMIN_FINISH: { 68 | return { 69 | ...state, 70 | status: { 71 | ...state.status, 72 | ts: Date.now(), 73 | }, 74 | }; 75 | } 76 | case types.ADD_ADMIN_START: { 77 | return { 78 | ...state, 79 | addAdmin: { 80 | ...state.addAdmin, 81 | loading: true, 82 | success: null, 83 | error: null, 84 | }, 85 | }; 86 | } 87 | case types.ADD_ADMIN_SUCCESS: { 88 | return { 89 | ...state, 90 | addAdmin: { 91 | ...state.addAdmin, 92 | loading: false, 93 | success: action.success, 94 | data: action.data, 95 | v: state.addAdmin.v + 1, 96 | }, 97 | }; 98 | } 99 | case types.ADD_ADMIN_FAIL: { 100 | return { 101 | ...state, 102 | addAdmin: { 103 | ...state.addAdmin, 104 | data: null, 105 | success: null, 106 | error: action.error, 107 | v: state.addAdmin.v + 1, 108 | }, 109 | }; 110 | } 111 | case types.ADD_ADMIN_FINISH: { 112 | return { 113 | ...state, 114 | addAdmin: { 115 | ...state.addAdmin, 116 | ts: Date.now(), 117 | }, 118 | }; 119 | } 120 | case types.REMOVE_ADMIN_START: { 121 | return { 122 | ...state, 123 | removeAdmin: { 124 | ...state.removeAdmin, 125 | loading: true, 126 | success: null, 127 | data: null, 128 | }, 129 | }; 130 | } 131 | case types.REMOVE_ADMIN_SUCCESS: { 132 | return { 133 | ...state, 134 | removeAdmin: { 135 | ...state.removeAdmin, 136 | success: action.success, 137 | data: action.data, 138 | v: state.removeAdmin.v + 1, 139 | }, 140 | }; 141 | } 142 | case types.REMOVE_ADMIN_FAIL: { 143 | return { 144 | ...state, 145 | removeAdmin: { 146 | ...state.removeAdmin, 147 | data: null, 148 | success: null, 149 | error: action.error, 150 | v: state.removeAdmin.v + 1, 151 | }, 152 | }; 153 | } 154 | case types.REMOVE_ADMIN_FINISH: { 155 | return { 156 | ...state, 157 | removeAdmin: { 158 | ...state.removeAdmin, 159 | ts: Date.now(), 160 | }, 161 | }; 162 | } 163 | default: 164 | return state; 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /client/src/reducers/ArbitrageReducer.js: -------------------------------------------------------------------------------- 1 | import * as types from "../actions/types"; 2 | 3 | const initialData = { 4 | status: { 5 | success: null, 6 | error: null, 7 | data: null, 8 | loading: false, 9 | ts: null, 10 | v: 0, 11 | }, 12 | }; 13 | 14 | export default function (state = initialData, action) { 15 | switch (action.type) { 16 | case types.GET_ARBITRAGE_START: { 17 | return { 18 | ...state, 19 | status: { 20 | ...state.status, 21 | loading: true, 22 | success: null, 23 | error: null, 24 | }, 25 | }; 26 | } 27 | 28 | case types.GET_ARBITRAGE_SUCCESS: { 29 | return { 30 | ...state, 31 | status: { 32 | ...state.status, 33 | loading: false, 34 | success: action.success, 35 | data: action.data, 36 | v: state.status.v + 1, 37 | }, 38 | }; 39 | } 40 | 41 | case types.GET_ARBITRAGE_FAIL: { 42 | return { 43 | ...state, 44 | status: { 45 | ...state.status, 46 | data: null, 47 | error: action.error, 48 | v: state.status.v + 1, 49 | }, 50 | }; 51 | } 52 | 53 | case types.GET_ARBITRAGE_FINISH: { 54 | return { 55 | ...state, 56 | status: { 57 | ...state.status, 58 | ts: Date.now(), 59 | }, 60 | }; 61 | } 62 | 63 | /** 64 | * 65 | * 66 | * 67 | * 68 | * 69 | * 70 | */ 71 | 72 | default: 73 | return state; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /client/src/reducers/Auth.js: -------------------------------------------------------------------------------- 1 | import * as types from "../actions/types"; 2 | 3 | const initialData = { 4 | success: null, 5 | error: null, 6 | data: null, 7 | loading: false, 8 | ts: null, 9 | isLoggedIn: localStorage.getItem("crypbot_auth") || false, 10 | v: 0, 11 | }; 12 | 13 | export default function (state = initialData, action) { 14 | switch (action.type) { 15 | case types.LOGIN_START: { 16 | return { 17 | ...state, 18 | loading: true, 19 | success: null, 20 | error: null, 21 | ts: Date.now(), 22 | }; 23 | } 24 | case types.LOGIN_SUCCESS: { 25 | localStorage.setItem("crypbot_auth", true); 26 | return { 27 | ...state, 28 | success: action.success || null, 29 | data: action.data, 30 | error: null, 31 | isLoggedIn: true, 32 | v: state.v + 1, 33 | }; 34 | } 35 | 36 | case types.LOGIN_FAIL: { 37 | return { 38 | ...state, 39 | success: null, 40 | error: action.error, 41 | v: state.v + 1, 42 | }; 43 | } 44 | 45 | case types.LOGIN_FINISH: { 46 | return { 47 | ...state, 48 | loading: false, 49 | ts: Date.now(), 50 | error: null, 51 | success: null, 52 | }; 53 | } 54 | 55 | case types.LOGOUT_SUCCESS: { 56 | localStorage.setItem("crypbot_auth", false); 57 | return { 58 | ...state, 59 | isLoggedIn: false, 60 | ts: Date.now(), 61 | }; 62 | } 63 | 64 | default: 65 | return state; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /client/src/reducers/DailyStatsReducer.js: -------------------------------------------------------------------------------- 1 | import { actions } from "react-table"; 2 | import * as types from "../actions/types"; 3 | 4 | const initialState = { 5 | getStatsTime: { 6 | success: null, 7 | error: null, 8 | loading: false, 9 | data: null, 10 | ts: null, 11 | v: 0, 12 | }, 13 | getStatsData: { 14 | success: null, 15 | error: null, 16 | loading: false, 17 | data: null, 18 | ts: null, 19 | v: 0, 20 | }, 21 | }; 22 | 23 | export default function (state = initialState, action) { 24 | switch (action.type) { 25 | case types.GET_DAILY_STATS_TIME_START: { 26 | return { 27 | ...state, 28 | getStatsTime: { 29 | ...state.getStatsTime, 30 | success: null, 31 | error: null, 32 | loading: true 33 | } 34 | }; 35 | } 36 | case types.GET_DAILY_STATS_TIME_SUCCESS: { 37 | return { 38 | ...state, 39 | getStatsTime: { 40 | ...state.getStatsTime, 41 | loading: false, 42 | success: action.success, 43 | data: action.data, 44 | v: state.getStatsTime.v + 1 45 | } 46 | }; 47 | } 48 | case types.GET_DAILY_STATS_TIME_FAIL: { 49 | return { 50 | ...state, 51 | getStatsTime: { 52 | data: null, 53 | error: action.error, 54 | v: state.getStatsTime.v + 1 55 | } 56 | }; 57 | } 58 | case types.GET_DAILY_STATS_TIME_FINISH: { 59 | return { 60 | ...state, 61 | getStatsTime: { 62 | ...state.getStatsTime, 63 | ts: Date.now() 64 | } 65 | }; 66 | } 67 | case types.GET_DAILY_STATS_DATA_START: { 68 | return { 69 | ...state, 70 | getStatsData: { 71 | ...state.getStatsData, 72 | success: null, 73 | error: null, 74 | loading: true 75 | } 76 | }; 77 | } 78 | case types.GET_DAILY_STATS_DATA_SUCCESS: { 79 | return { 80 | ...state, 81 | getStatsData: { 82 | ...state.getStatsData, 83 | loading: false, 84 | success: action.success, 85 | data: action.data, 86 | v: state.getStatsData.v + 1 87 | } 88 | }; 89 | } 90 | case types.GET_DAILY_STATS_DATA_FAIL: { 91 | return { 92 | ...state, 93 | getStatsData: { 94 | data: null, 95 | error: action.error, 96 | v: state.getStatsData.v + 1 97 | } 98 | }; 99 | } 100 | case types.GET_DAILY_STATS_DATA_FINISH: { 101 | return { 102 | ...state, 103 | getStatsData: { 104 | ...state.getStatsData, 105 | ts: Date.now() 106 | } 107 | }; 108 | } 109 | default: 110 | return state; 111 | } 112 | } -------------------------------------------------------------------------------- /client/src/reducers/LiquidityBotReducer.js: -------------------------------------------------------------------------------- 1 | import * as types from "../actions/types"; 2 | 3 | const initialState = { 4 | status: { 5 | data: null, 6 | success: null, 7 | v: 0, 8 | ts: Date.now(), 9 | error: null, 10 | loading: true, 11 | }, 12 | cancel: { 13 | data: null, 14 | success: null, 15 | v: 0, 16 | ts: Date.now(), 17 | error: null, 18 | loading: true, 19 | }, 20 | }; 21 | 22 | export default (state = initialState, action) => { 23 | switch (action.type) { 24 | case types.GET_LIQUIDITY_BOT_START: { 25 | return { 26 | ...state, 27 | status: { 28 | ...state.status, 29 | success: null, 30 | data: null, 31 | error: null, 32 | loading: true, 33 | }, 34 | }; 35 | } 36 | case types.GET_LIQUIDITY_BOT_SUCCESS: { 37 | return { 38 | ...state, 39 | status: { 40 | ...state.status, 41 | success: action.success, 42 | data: action.data, 43 | v: state.status.v + 1, 44 | }, 45 | }; 46 | } 47 | 48 | case types.GET_LIQUIDITY_BOT_FAIL: { 49 | return { 50 | ...state, 51 | status: { 52 | ...state.status, 53 | error: action.error, 54 | v: state.status.v + 1, 55 | }, 56 | }; 57 | } 58 | 59 | case types.GET_LIQUIDITY_BOT_FINISH: { 60 | return { 61 | ...state, 62 | status: { 63 | ...state.status, 64 | loading: false, 65 | success: null, 66 | error: null, 67 | ts: Date.now(), 68 | }, 69 | }; 70 | } 71 | 72 | case types.CANCEL_ORDER_LIQUIDITY_BOT_START: { 73 | return { 74 | ...state, 75 | cancel: { 76 | ...state.cancel, 77 | success: null, 78 | data: null, 79 | error: null, 80 | loading: true, 81 | }, 82 | }; 83 | } 84 | case types.CANCEL_ORDER_LIQUIDITY_BOT_SUCCESS: { 85 | return { 86 | ...state, 87 | cancel: { 88 | ...state.cancel, 89 | success: action.success, 90 | data: action.data, 91 | v: state.cancel.v + 1, 92 | }, 93 | }; 94 | } 95 | 96 | case types.CANCEL_ORDER_LIQUIDITY_BOT_FAIL: { 97 | return { 98 | ...state, 99 | cancel: { 100 | ...state.cancel, 101 | error: action.error, 102 | v: state.cancel.v + 1, 103 | }, 104 | }; 105 | } 106 | 107 | case types.CANCEL_ORDER_LIQUIDITY_BOT_FINISH: { 108 | return { 109 | ...state, 110 | cancel: { 111 | ...state.cancel, 112 | loading: false, 113 | success: null, 114 | error: null, 115 | ts: Date.now(), 116 | }, 117 | }; 118 | } 119 | 120 | default: 121 | return state; 122 | } 123 | }; 124 | -------------------------------------------------------------------------------- /client/src/reducers/LiquidityDetailsBotReducer.js: -------------------------------------------------------------------------------- 1 | import * as types from "../actions/types"; 2 | 3 | const initialState = { 4 | status: { 5 | data: null, 6 | success: null, 7 | v: 0, 8 | ts: Date.now(), 9 | error: null, 10 | loading: true, 11 | }, 12 | }; 13 | 14 | export default (state = initialState, action) => { 15 | switch (action.type) { 16 | case types.GET_LIQUIDITY_DETAILS_START: { 17 | return { 18 | ...state, 19 | status: { 20 | ...state.status, 21 | success: null, 22 | data: null, 23 | error: null, 24 | loading: true, 25 | }, 26 | }; 27 | } 28 | 29 | case types.GET_LIQUIDITY_DETAILS_SUCCESS: { 30 | return { 31 | ...state, 32 | status: { 33 | ...state.status, 34 | success: action.success, 35 | data: action.data, 36 | v: state.status.v + 1, 37 | }, 38 | }; 39 | } 40 | 41 | case types.GET_LIQUIDITY_DETAILS_FAIL: { 42 | return { 43 | ...state, 44 | status: { 45 | ...state.status, 46 | error: action.error, 47 | v: state.status.v + 1, 48 | }, 49 | }; 50 | } 51 | 52 | case types.GET_LIQUIDITY_DETAILS_FINISH: { 53 | return { 54 | ...state, 55 | status: { 56 | ...state.status, 57 | loading: false, 58 | success: null, 59 | error: null, 60 | ts: Date.now(), 61 | }, 62 | }; 63 | } 64 | 65 | default: 66 | return state; 67 | } 68 | }; 69 | -------------------------------------------------------------------------------- /client/src/reducers/ManageKeysReducer.js: -------------------------------------------------------------------------------- 1 | import * as types from "../actions/types"; 2 | 3 | const initialState = { 4 | status: { 5 | data: null, 6 | success: null, 7 | v: 0, 8 | ts: Date.now(), 9 | error: null, 10 | loading: true, 11 | }, 12 | delete: { 13 | data: null, 14 | success: null, 15 | v: 0, 16 | ts: Date.now(), 17 | error: null, 18 | loading: true, 19 | }, 20 | }; 21 | 22 | export default (state = initialState, action) => { 23 | switch (action.type) { 24 | case types.GET_KEYS_BOT_START: { 25 | return { 26 | ...state, 27 | status: { 28 | ...state.status, 29 | success: null, 30 | data: null, 31 | error: null, 32 | loading: true, 33 | }, 34 | }; 35 | } 36 | case types.GET_KEYS_BOT_SUCCESS: { 37 | return { 38 | ...state, 39 | status: { 40 | ...state.status, 41 | success: action.success, 42 | data: action.data, 43 | v: state.status.v + 1, 44 | }, 45 | }; 46 | } 47 | 48 | case types.GET_KEYS_BOT_FAIL: { 49 | return { 50 | ...state, 51 | status: { 52 | ...state.status, 53 | error: action.error, 54 | v: state.status.v + 1, 55 | }, 56 | }; 57 | } 58 | 59 | case types.GET_KEYS_BOT_FINISH: { 60 | return { 61 | ...state, 62 | status: { 63 | ...state.status, 64 | loading: false, 65 | success: null, 66 | error: null, 67 | ts: Date.now(), 68 | }, 69 | }; 70 | } 71 | 72 | case types.DELETE_KEYS_BOT_START: { 73 | return { 74 | ...state, 75 | delete: { 76 | ...state.delete, 77 | success: null, 78 | data: null, 79 | error: null, 80 | loading: true, 81 | }, 82 | }; 83 | } 84 | case types.DELETE_KEYS_BOT_SUCCESS: { 85 | return { 86 | ...state, 87 | delete: { 88 | ...state.delete, 89 | success: action.success, 90 | data: action.data, 91 | v: state.delete.v + 1, 92 | }, 93 | }; 94 | } 95 | 96 | case types.DELETE_KEYS_BOT_FAIL: { 97 | return { 98 | ...state, 99 | delete: { 100 | ...state.delete, 101 | error: action.error, 102 | v: state.delete.v + 1, 103 | }, 104 | }; 105 | } 106 | 107 | case types.DELETE_KEYS_BOT_FINISH: { 108 | return { 109 | ...state, 110 | delete: { 111 | ...state.delete, 112 | loading: false, 113 | success: null, 114 | error: null, 115 | ts: Date.now(), 116 | }, 117 | }; 118 | } 119 | 120 | default: 121 | return state; 122 | } 123 | }; 124 | -------------------------------------------------------------------------------- /client/src/reducers/MonitorReducer.js: -------------------------------------------------------------------------------- 1 | import * as types from "../actions/types"; 2 | 3 | const initialData = { 4 | status: { 5 | success: null, 6 | error: null, 7 | data: null, 8 | loading: false, 9 | ts: null, 10 | v: 0, 11 | }, 12 | form: { 13 | success: null, 14 | error: null, 15 | data: null, 16 | loading: false, 17 | ts: null, 18 | v: 0, 19 | }, 20 | }; -------------------------------------------------------------------------------- /client/src/reducers/SocketReducer.js: -------------------------------------------------------------------------------- 1 | import * as types from "../actions/types"; 2 | 3 | const initialState = { 4 | socket: null, 5 | status: "not initiated", 6 | rooms: [], 7 | v: 0, 8 | }; 9 | 10 | const SocketChange = (state = initialState, action) => { 11 | switch (action.type) { 12 | case types.SOCKET_CONNECTED: { 13 | return { 14 | ...state, 15 | socket: action.instance, 16 | status: "connected", 17 | v: state.v + 1, 18 | }; 19 | } 20 | case types.SOCKET_DISCONNECTED: { 21 | return { ...state, status: "disconnected" }; 22 | } 23 | default: { 24 | return state; 25 | } 26 | } 27 | }; 28 | 29 | export default SocketChange; 30 | -------------------------------------------------------------------------------- /client/src/reducers/index.js: -------------------------------------------------------------------------------- 1 | import Auth from "./Auth"; 2 | import SocketReducer from "./SocketReducer"; 3 | import ExchangeReducer from "./Exchange"; 4 | import ArbitrageReducer from "./ArbitrageReducer"; 5 | import AdminReducer from "./AdminReducer"; 6 | import AdminProfileReducer from "./AdminProfileReducer"; 7 | import DailyStatsReducer from "./DailyStatsReducer"; 8 | import LiquidityBotReducer from "./LiquidityBotReducer"; 9 | import ManageKeysReducer from "./ManageKeysReducer"; 10 | import LiquidityDetailsBotReducer from "./LiquidityDetailsBotReducer"; 11 | 12 | export default { 13 | auth: Auth, 14 | socketReducer: SocketReducer, 15 | exchangeReducer: ExchangeReducer, 16 | arbitrageReducer: ArbitrageReducer, 17 | adminReducer: AdminReducer, 18 | adminProfileReducer: AdminProfileReducer, 19 | dailyStatsReducer: DailyStatsReducer, 20 | LiquidityBotReducer: LiquidityBotReducer, 21 | ManageKeysReducer: ManageKeysReducer, 22 | LiquidityDetailsBotReducer: LiquidityDetailsBotReducer, 23 | }; 24 | -------------------------------------------------------------------------------- /client/src/redux/store.js: -------------------------------------------------------------------------------- 1 | import { createStore, combineReducers, applyMiddleware } from "redux"; 2 | import reducers from "../reducers"; 3 | import thunk from "redux-thunk"; 4 | import createLogger from "redux-logger"; 5 | 6 | const ENV = process.env.REACT_APP_ENV; 7 | 8 | export default function configureStore() { 9 | return createStore( 10 | combineReducers({ 11 | ...reducers, 12 | }), 13 | {}, 14 | ENV === "dev" 15 | ? applyMiddleware(thunk, createLogger) 16 | : applyMiddleware(thunk) 17 | ); 18 | } 19 | -------------------------------------------------------------------------------- /client/src/serviceWorker.js: -------------------------------------------------------------------------------- 1 | // This optional code is used to register a service worker. 2 | // register() is not called by default. 3 | 4 | // This lets the app load faster on subsequent visits in production, and gives 5 | // it offline capabilities. However, it also means that developers (and users) 6 | // will only see deployed updates on subsequent visits to a page, after all the 7 | // existing tabs open on the page have been closed, since previously cached 8 | // resources are updated in the background. 9 | 10 | // To learn more about the benefits of this model and instructions on how to 11 | // opt-in, read https://bit.ly/CRA-PWA 12 | 13 | const isLocalhost = Boolean( 14 | window.location.hostname === 'localhost' || 15 | // [::1] is the IPv6 localhost address. 16 | window.location.hostname === '[::1]' || 17 | // 127.0.0.0/8 are considered localhost for IPv4. 18 | window.location.hostname.match( 19 | /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/ 20 | ) 21 | ); 22 | 23 | export function register(config) { 24 | if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) { 25 | // The URL constructor is available in all browsers that support SW. 26 | const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href); 27 | if (publicUrl.origin !== window.location.origin) { 28 | // Our service worker won't work if PUBLIC_URL is on a different origin 29 | // from what our page is served on. This might happen if a CDN is used to 30 | // serve assets; see https://github.com/facebook/create-react-app/issues/2374 31 | return; 32 | } 33 | 34 | window.addEventListener('load', () => { 35 | const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`; 36 | 37 | if (isLocalhost) { 38 | // This is running on localhost. Let's check if a service worker still exists or not. 39 | checkValidServiceWorker(swUrl, config); 40 | 41 | // Add some additional logging to localhost, pointing developers to the 42 | // service worker/PWA documentation. 43 | navigator.serviceWorker.ready.then(() => { 44 | // console.log( 45 | // 'This web app is being served cache-first by a service ' + 46 | // 'worker. To learn more, visit https://bit.ly/CRA-PWA' 47 | // ); 48 | }); 49 | } else { 50 | // Is not localhost. Just register service worker 51 | registerValidSW(swUrl, config); 52 | } 53 | }); 54 | } 55 | } 56 | 57 | function registerValidSW(swUrl, config) { 58 | navigator.serviceWorker 59 | .register(swUrl) 60 | .then(registration => { 61 | registration.onupdatefound = () => { 62 | const installingWorker = registration.installing; 63 | if (installingWorker == null) { 64 | return; 65 | } 66 | installingWorker.onstatechange = () => { 67 | if (installingWorker.state === 'installed') { 68 | if (navigator.serviceWorker.controller) { 69 | // At this point, the updated precached content has been fetched, 70 | // but the previous service worker will still serve the older 71 | // content until all client tabs are closed. 72 | // console.log( 73 | // 'New content is available and will be used when all ' + 74 | // 'tabs for this page are closed. See https://bit.ly/CRA-PWA.' 75 | // ); 76 | 77 | // Execute callback 78 | if (config && config.onUpdate) { 79 | config.onUpdate(registration); 80 | } 81 | } else { 82 | // At this point, everything has been precached. 83 | // It's the perfect time to display a 84 | // "Content is cached for offline use." message. 85 | // console.log('Content is cached for offline use.'); 86 | 87 | // Execute callback 88 | if (config && config.onSuccess) { 89 | config.onSuccess(registration); 90 | } 91 | } 92 | } 93 | }; 94 | }; 95 | }) 96 | .catch(error => { 97 | console.error('Error during service worker registration:', error); 98 | }); 99 | } 100 | 101 | function checkValidServiceWorker(swUrl, config) { 102 | // Check if the service worker can be found. If it can't reload the page. 103 | fetch(swUrl, { 104 | headers: { 'Service-Worker': 'script' }, 105 | }) 106 | .then(response => { 107 | // Ensure service worker exists, and that we really are getting a JS file. 108 | const contentType = response.headers.get('content-type'); 109 | if ( 110 | response.status === 404 || 111 | (contentType != null && contentType.indexOf('javascript') === -1) 112 | ) { 113 | // No service worker found. Probably a different app. Reload the page. 114 | navigator.serviceWorker.ready.then(registration => { 115 | registration.unregister().then(() => { 116 | window.location.reload(); 117 | }); 118 | }); 119 | } else { 120 | // Service worker found. Proceed as normal. 121 | registerValidSW(swUrl, config); 122 | } 123 | }) 124 | .catch(() => { 125 | console.log( 126 | 'No internet connection found. App is running in offline mode.' 127 | ); 128 | }); 129 | } 130 | 131 | export function unregister() { 132 | if ('serviceWorker' in navigator) { 133 | navigator.serviceWorker.ready 134 | .then(registration => { 135 | registration.unregister(); 136 | }) 137 | .catch(error => { 138 | console.error(error.message); 139 | }); 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /client/src/setupTests.js: -------------------------------------------------------------------------------- 1 | // jest-dom adds custom jest matchers for asserting on DOM nodes. 2 | // allows you to do things like: 3 | // expect(element).toHaveTextContent(/react/i) 4 | // learn more: https://github.com/testing-library/jest-dom 5 | import '@testing-library/jest-dom/extend-expect'; 6 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | services: 3 | mongodb: 4 | image: mongo:latest 5 | container_name: mongodb-container 6 | ports: 7 | - "127.0.0.1:27017:27017" # Port mapping to localhost 8 | volumes: 9 | - mongodb-data:/data/db # Mount a local directory into the container 10 | logging: 11 | driver: none 12 | 13 | redis: 14 | image: redis:latest 15 | container_name: redis-container 16 | ports: 17 | - "127.0.0.1:6379:6379" # Port mapping to localhost 18 | logging: 19 | driver: none 20 | 21 | react-app: 22 | build: 23 | context: ./client 24 | dockerfile: Dockerfile 25 | container_name: reactjs-container 26 | ports: 27 | - "3000:3000" 28 | depends_on: 29 | - nodejs-app 30 | environment: 31 | - NODE_ENV=development 32 | 33 | nodejs-app: 34 | build: 35 | context: ./server 36 | dockerfile: Dockerfile 37 | container_name: nodejs-api-container 38 | ports: 39 | - "5000:5000" 40 | - "3002:3002" 41 | depends_on: 42 | - mongodb 43 | - redis 44 | # command: ["pm2-runtime", "start", "npm", "--", "start"] 45 | command: ["npm", "start"] 46 | 47 | volumes: 48 | mongodb-data: 49 | -------------------------------------------------------------------------------- /getLogins.sh: -------------------------------------------------------------------------------- 1 | docker exec -it nodejs-api-container cat /usr/src/app/helpers/creds.json 2 | -------------------------------------------------------------------------------- /install_docker.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Colors for user feedback 4 | GREEN="\033[0;32m" 5 | RESET="\033[0m" 6 | 7 | function installDocker() { 8 | echo -e "Installing Docker\n" 9 | 10 | sudo apt-get update 11 | sudo apt-get install apt-transport-https ca-certificates curl software-properties-common -y 12 | 13 | curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg 14 | echo "deb [signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null 15 | 16 | sudo apt-get update 17 | sudo apt-get install docker-ce docker-ce-cli containerd.io -y 18 | 19 | sudo curl -L "https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose 20 | sudo chmod +x /usr/local/bin/docker-compose 21 | 22 | echo -e "${GREEN}Docker Installed Successfully${RESET}" 23 | } 24 | 25 | function init() { 26 | if ! command -v docker &>/dev/null; then 27 | installDocker 28 | else 29 | echo -e "${GREEN}Docker is already installed.${RESET}" 30 | fi 31 | } 32 | 33 | function main() { 34 | init 35 | } 36 | 37 | main 38 | -------------------------------------------------------------------------------- /logos/bitfinex.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TradingBotX/market-making-bot/b0049041336ee148ed906142c443d7b433bd7ec8/logos/bitfinex.png -------------------------------------------------------------------------------- /logos/bitrue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TradingBotX/market-making-bot/b0049041336ee148ed906142c443d7b433bd7ec8/logos/bitrue.png -------------------------------------------------------------------------------- /logos/bittrex.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TradingBotX/market-making-bot/b0049041336ee148ed906142c443d7b433bd7ec8/logos/bittrex.png -------------------------------------------------------------------------------- /logos/gateio.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TradingBotX/market-making-bot/b0049041336ee148ed906142c443d7b433bd7ec8/logos/gateio.png -------------------------------------------------------------------------------- /logos/huobi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TradingBotX/market-making-bot/b0049041336ee148ed906142c443d7b433bd7ec8/logos/huobi.png -------------------------------------------------------------------------------- /logos/kucoin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TradingBotX/market-making-bot/b0049041336ee148ed906142c443d7b433bd7ec8/logos/kucoin.png -------------------------------------------------------------------------------- /server/.dockerignore: -------------------------------------------------------------------------------- 1 | node_modules/ -------------------------------------------------------------------------------- /server/.env.example: -------------------------------------------------------------------------------- 1 | MONGO_URL=mongodb://mongodb:27017/market-making-bot 2 | 3 | PORT=5000 4 | 5 | WS_PORT=3002 6 | WS_HEARTBEAT=6000 7 | 8 | # Redis Config 9 | REDIS_HOST=redis 10 | REDIS_PORT=6379 11 | REDIS_PREFIX=MMBot 12 | 13 | ACCESS_TOKEN_SECRET=MMBot 14 | 15 | RESET_ADMIN=false -------------------------------------------------------------------------------- /server/.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /logs 3 | 4 | .env -------------------------------------------------------------------------------- /server/Dockerfile: -------------------------------------------------------------------------------- 1 | # Use an official Node.js runtime as the base image 2 | FROM node:16 3 | 4 | # Install PM2 globally 5 | RUN npm install pm2 -g 6 | 7 | # Set the working directory inside the container 8 | WORKDIR /usr/src/app 9 | 10 | # Copy package.json and package-lock.json to the working directory 11 | COPY package*.json ./ 12 | 13 | # Install project dependencies 14 | RUN npm install 15 | RUN npm install bcrypt 16 | 17 | # Copy the rest of the application code 18 | COPY . . 19 | 20 | # Expose the port that your Node.js app will run on 21 | EXPOSE 3000 22 | 23 | # Use PM2 to start your Node.js app 24 | CMD [ "npm", "start" ] 25 | -------------------------------------------------------------------------------- /server/app.js: -------------------------------------------------------------------------------- 1 | const createError = require("http-errors"); 2 | const express = require("express"); 3 | const path = require("path"); 4 | const cors = require("cors"); 5 | const cookieParser = require("cookie-parser"); 6 | const morganLogger = require("morgan"); 7 | const logSymbols = require("log-symbols"); 8 | const mongoose = require("mongoose"); 9 | 10 | require("./helpers/globals"); 11 | 12 | function connectToMongoDB() { 13 | try { 14 | mongoose 15 | .connect(process.env.MONGO_URL, { 16 | useNewUrlParser: true, 17 | useUnifiedTopology: true, 18 | // useCreateIndex: true, 19 | }) 20 | .then(() => { 21 | logger.info("[", logSymbols.success, "] connected to mongodb"); 22 | logger.debug("emitting event in internal bus"); 23 | InternalBus.emit(GlobalEvents.mongodb_connected); 24 | }) 25 | .catch((e) => { 26 | console.log(e); 27 | logger.error(`[*] error while connecting to mongodb:`, e); 28 | logger.info("[*] Retrying connection to mongodb in 5 seconds..."); 29 | setTimeout(connectToMongoDB, 5000); 30 | }); 31 | } catch (err0) { 32 | logger.error( 33 | `[*] error while connecting to mongodb: ${JSON.stringify(err0)}` 34 | ); 35 | logger.error( 36 | `[*] error while connecting to mongodb at :${process.env.MONGO_URL}` 37 | ); 38 | logger.info("[*] Retrying connection to mongodb in 5 seconds..."); 39 | setTimeout(connectToMongoDB, 5000); 40 | } 41 | } 42 | 43 | connectToMongoDB(); 44 | 45 | const { checkSetup } = require("./helpers/initialSetup"); 46 | 47 | checkSetup(); 48 | 49 | require("dotenv").config(); 50 | require("./services/redis"); 51 | require("./helpers/logger"); 52 | require("./helpers/RESPONSE"); 53 | require("./helpers/MAIL"); 54 | require("./helpers/socket_io"); 55 | require("./crons/walletBalanceCron"); 56 | require("./crons/scheduledCrons"); 57 | require("./helpers/exchangeSocket/bitfinex"); 58 | 59 | require("events").EventEmitter.defaultMaxListeners = 20; 60 | 61 | const indexRouter = require("./routes/index"); 62 | const adminRouter = require("./routes/admin"); 63 | const spreadBotRouter = require("./routes/spreadBot"); 64 | const bitrue = require("./helpers/exchangeHelpers/bitrue"); 65 | const bittrex = require("./helpers/exchangeHelpers/bittrex"); 66 | 67 | const NetLogger = require("./helpers/networkLogger").NetLogger; 68 | 69 | const app = express(); 70 | 71 | app.use( 72 | morganLogger(":method :url :status :remote-addr", { 73 | stream: NetLogger.stream, 74 | }) 75 | ); 76 | 77 | // view engine setup 78 | app.set("views", path.join(__dirname, "views")); 79 | app.set("view engine", "jade"); 80 | 81 | app.use(morganLogger("dev")); 82 | app.use(express.json()); 83 | app.use(express.urlencoded({ extended: false })); 84 | app.use(cookieParser()); 85 | app.use(express.static(path.join(__dirname, "public"))); 86 | 87 | app.use(cors()); 88 | 89 | app.use("/", indexRouter); 90 | app.use("/api/admin", adminRouter); 91 | app.use("/api/spreadbot", spreadBotRouter); 92 | 93 | // catch 404 and forward to error handler 94 | app.use(function (req, res, next) { 95 | next(createError(404)); 96 | }); 97 | 98 | // error handler 99 | app.use(function (err, req, res, next) { 100 | // set locals, only providing error in development 101 | res.locals.message = err.message; 102 | res.locals.error = req.app.get("env") === "development" ? err : {}; 103 | 104 | // render the error page 105 | res.status(err.status || 500); 106 | res.render("error"); 107 | }); 108 | 109 | logger.info("[", logSymbols.success, "] server started"); 110 | 111 | async function updatePrices() { 112 | const currencies = [ 113 | "XDC", 114 | "SRX", 115 | "PLI", 116 | "LBT", 117 | "USPLUS", 118 | "FXD", 119 | "PRNT", 120 | "GBEX", 121 | "XTT", 122 | "XSP", 123 | ]; 124 | let i, 125 | orderBook, 126 | query = {}, 127 | pair; 128 | for (i = 0; i < currencies.length; i++) { 129 | pair = `${currencies[i]}-USDT`; 130 | orderBook = await bitrue.orderBook(pair); 131 | if (orderBook.bids[0] && orderBook.asks[0]) 132 | query[pair] = { 133 | bid: [parseFloat(parseFloat(orderBook.bids[0][0]).toFixed(12))], 134 | ask: [parseFloat(parseFloat(orderBook.asks[0][0]).toFixed(12))], 135 | }; 136 | } 137 | InternalBus.emit(GlobalEvents.converter_price, query); 138 | } 139 | 140 | // //EUR price update 141 | // async function updateEURPrice() { 142 | // let query = {}; 143 | // const EURUSDTBook = await bittrex.orderBook("USDT-EUR"); 144 | // if (EURUSDTBook.bids[1] && EURUSDTBook.asks[1]) 145 | // query[`EUR-USDT`] = { 146 | // bid: [parseFloat(parseFloat(1 / EURUSDTBook.asks[1].rate).toFixed(8))], 147 | // ask: [parseFloat(parseFloat(1 / EURUSDTBook.bids[1].rate).toFixed(8))], 148 | // }; 149 | // InternalBus.emit(GlobalEvents.converter_price, query); 150 | // } 151 | 152 | updatePrices(); 153 | // updateEURPrice(); 154 | 155 | setInterval(async () => { 156 | updatePrices(); 157 | }, 300000); 158 | 159 | // setInterval(async () => { 160 | // updateEURPrice(); 161 | // }, 120000); 162 | 163 | module.exports = app; 164 | -------------------------------------------------------------------------------- /server/bin/www: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | /** 4 | * Module dependencies. 5 | */ 6 | 7 | var app = require('../app'); 8 | var debug = require('debug')('server-boiler-plate:server'); 9 | var http = require('http'); 10 | 11 | /** 12 | * Get port from environment and store in Express. 13 | */ 14 | 15 | var port = normalizePort(process.env.PORT || '3000'); 16 | app.set('port', port); 17 | 18 | /** 19 | * Create HTTP server. 20 | */ 21 | 22 | var server = http.createServer(app); 23 | 24 | /** 25 | * Listen on provided port, on all network interfaces. 26 | */ 27 | 28 | server.listen(port); 29 | server.on('error', onError); 30 | server.on('listening', onListening); 31 | 32 | /** 33 | * Normalize a port into a number, string, or false. 34 | */ 35 | 36 | function normalizePort(val) { 37 | var port = parseInt(val, 10); 38 | 39 | if (isNaN(port)) { 40 | // named pipe 41 | return val; 42 | } 43 | 44 | if (port >= 0) { 45 | // port number 46 | return port; 47 | } 48 | 49 | return false; 50 | } 51 | 52 | /** 53 | * Event listener for HTTP server "error" event. 54 | */ 55 | 56 | function onError(error) { 57 | if (error.syscall !== 'listen') { 58 | throw error; 59 | } 60 | 61 | var bind = typeof port === 'string' 62 | ? 'Pipe ' + port 63 | : 'Port ' + port; 64 | 65 | // handle specific listen errors with friendly messages 66 | switch (error.code) { 67 | case 'EACCES': 68 | console.error(bind + ' requires elevated privileges'); 69 | process.exit(1); 70 | break; 71 | case 'EADDRINUSE': 72 | console.error(bind + ' is already in use'); 73 | process.exit(1); 74 | break; 75 | default: 76 | throw error; 77 | } 78 | } 79 | 80 | /** 81 | * Event listener for HTTP server "listening" event. 82 | */ 83 | 84 | function onListening() { 85 | var addr = server.address(); 86 | var bind = typeof addr === 'string' 87 | ? 'pipe ' + addr 88 | : 'port ' + addr.port; 89 | debug('Listening on ' + bind); 90 | } 91 | -------------------------------------------------------------------------------- /server/controllers/indexController.js: -------------------------------------------------------------------------------- 1 | const responseHelper = require("../helpers/RESPONSE"); 2 | const { RedisClient } = require("../services/redis"); 3 | 4 | module.exports = { 5 | getUSDRates: async (req, res) => { 6 | const converterPrice = JSON.parse(await RedisClient.get("converterPrice")); 7 | return responseHelper.successWithData(res, "Done", { converterPrice }); 8 | }, 9 | }; 10 | -------------------------------------------------------------------------------- /server/crons/scheduledCrons.js: -------------------------------------------------------------------------------- 1 | const CronJob = require("cron").CronJob; 2 | const cronController = require("../controllers/cronController"); 3 | const spreadBotController = require("../controllers/spreadBotController"); 4 | 5 | // new CronJob( 6 | // "56 * * * *", 7 | // async () => { 8 | // cronController.checkBalances(); 9 | // }, 10 | // null, 11 | // true, 12 | // "Asia/Kolkata" 13 | // ); 14 | 15 | new CronJob( 16 | "14 * * * * *", 17 | async () => { 18 | await spreadBotController.autoCancelOrders(); 19 | await spreadBotController.runBot(); 20 | await spreadBotController.updateCancellingOrders(); 21 | await spreadBotController.updateOrdersMin(); 22 | await spreadBotController.placeFailedOrders(); 23 | await spreadBotController.checkOrderNumbers(); 24 | }, 25 | null, 26 | true, 27 | "Asia/Kolkata" 28 | ); 29 | 30 | new CronJob( 31 | "*/10 * * * *", 32 | async () => { 33 | cronController.updatedCompletedOrdersStatus(); 34 | // spreadBotController.updateMaintainOrderStatus(); 35 | await spreadBotController.updateOrders10Min(); 36 | }, 37 | null, 38 | true, 39 | "Asia/Kolkata" 40 | ); 41 | 42 | // new CronJob( 43 | // "0 */3 * * *", 44 | // async () => { 45 | // await spreadBotController.maintainBalance(); 46 | // }, 47 | // null, 48 | // true, 49 | // "Asia/Kolkata" 50 | // ); 51 | 52 | // new CronJob( 53 | // "16 2 * * * *", 54 | // async () => { 55 | // await spreadBotController.differenceMail(); 56 | // // await spreadBotController.maintainBalance(); 57 | // }, 58 | // null, 59 | // true, 60 | // "Asia/Kolkata" 61 | // ); 62 | 63 | new CronJob( 64 | "0 8 * * *", 65 | async () => { 66 | await cronController.updateBalance("daily"); 67 | }, 68 | null, 69 | true, 70 | "Asia/Kolkata" 71 | ); 72 | 73 | new CronJob( 74 | "5 * * * *", 75 | async () => { 76 | await cronController.updateBalance("hourly"); 77 | }, 78 | null, 79 | true, 80 | "Asia/Kolkata" 81 | ); 82 | -------------------------------------------------------------------------------- /server/helpers/MAIL.js: -------------------------------------------------------------------------------- 1 | const nodemailer = require("nodemailer"); 2 | require("dotenv").config(); 3 | 4 | let transporter = nodemailer.createTransport({ 5 | host: process.env.MAIL_HOST, 6 | port: process.env.MAIL_PORT, 7 | secure: false, // true for 465, false for other ports 8 | auth: { 9 | user: process.env.MAIL_USERNAME, 10 | pass: process.env.MAIL_PASSWORD, 11 | }, 12 | }); 13 | var mails = {}; 14 | mails.send = async function ( 15 | to, 16 | subject, 17 | html, 18 | attachments, 19 | text = "", 20 | from = "Indsoft Node Monitor Alert " + process.env.MAIL_FROM 21 | ) { 22 | var info = await transporter 23 | .sendMail({ 24 | from: from, // sender address 25 | to: to, // list of receivers 26 | subject: subject, // Subject line 27 | text: text, // plain text body 28 | html: html, // html body 29 | attachments: attachments, 30 | }) 31 | .catch(function (err) { 32 | console.log("sending_mail_error", to, subject, err); 33 | throw err; 34 | }); 35 | 36 | console.log("sending_mail_response", to, subject, info); 37 | return info; 38 | }; 39 | 40 | module.exports = mails; 41 | global.mail = mails; 42 | -------------------------------------------------------------------------------- /server/helpers/RESPONSE.js: -------------------------------------------------------------------------------- 1 | const responseHelper = { 2 | //for all standard errors 3 | error: function (res, message, httpCode = 200) { 4 | res.status(httpCode).json({ 5 | statusCode: 422, 6 | message: message, 7 | }); 8 | }, 9 | 10 | //for database save error 11 | databaseError: function (res, error, httpCode = 200) { 12 | console.log("DB_ERROR : ", error); 13 | if (error && error.code && error.code == 11000) { 14 | res.status(httpCode).json({ 15 | statusCode: 500, 16 | message: "Fields Value should be Unique.", 17 | }); 18 | } else { 19 | res.status(httpCode).json({ 20 | statusCode: 500, 21 | message: "Server Error", 22 | }); 23 | } 24 | }, 25 | 26 | //for standard server error 27 | serverError: function (res, error, httpCode = 200) { 28 | // logger.error(`SERVER_ERROR : `, error); 29 | res.status(httpCode).json({ 30 | statusCode: 500, 31 | message: "Server Error", 32 | }); 33 | }, 34 | 35 | // for bcrypt hash error 36 | bcryptError: function (res, error, httpCode = 200) { 37 | console.log("BCRYPT_ERROR : ", error); 38 | res.status(httpCode).json({ 39 | statusCode: 500, 40 | message: "Bcrypt Error", 41 | }); 42 | }, 43 | 44 | //for succesful request with data 45 | successWithData: function (res, msg, data, httpCode = 200) { 46 | res.status(httpCode).json({ 47 | statusCode: 200, 48 | message: msg, 49 | data: data, 50 | }); 51 | }, 52 | 53 | //for succesful request with data 54 | successWithPaginationData: function ( 55 | res, 56 | msg, 57 | data, 58 | pagination, 59 | httpCode = 200 60 | ) { 61 | res.status(httpCode).json({ 62 | statusCode: 200, 63 | message: msg, 64 | data: data, 65 | pagination, 66 | }); 67 | }, 68 | 69 | //for successful request with message 70 | successWithMessage: function (res, message, httpCode = 200) { 71 | res.status(httpCode).json({ 72 | statusCode: 200, 73 | message: message, 74 | }); 75 | }, 76 | 77 | errorWithMessage: function (message, code) { 78 | return { 79 | status: code || 0, 80 | message: message || "Error occured", 81 | result: {}, 82 | }; 83 | }, 84 | 85 | //for chart response 86 | chartResponse: function (res, data, httpCode = 200) { 87 | res.status(httpCode).json(data); 88 | }, 89 | }; 90 | 91 | global.responseHelper = responseHelper; 92 | module.exports = responseHelper; 93 | -------------------------------------------------------------------------------- /server/helpers/axiosHelper.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | const axios = require("axios"); 3 | const httpsProxyAgent = require("https-proxy-agent"); 4 | 5 | module.exports = { 6 | makeGETRequest: async function (reqObject) { 7 | const config = { 8 | method: "get", 9 | url: reqObject.url, 10 | headers: { 11 | "Content-Type": reqObject.contentType, 12 | }, 13 | json: true, 14 | }; 15 | let result = await axios(config); 16 | return result; 17 | }, 18 | 19 | makePOSTRequest: async function (reqObject) { 20 | const config = { 21 | method: "post", 22 | url: reqObject.url, 23 | headers: { 24 | "Content-Type": reqObject.contentType, 25 | }, 26 | data: reqObject.data, 27 | }; 28 | let result = await axios(config); 29 | return result; 30 | }, 31 | 32 | makeDELETERequest: async function (reqObject) { 33 | const config = { 34 | method: "delete", 35 | url: reqObject.url, 36 | headers: { 37 | "Content-Type": reqObject.contentType, 38 | }, 39 | data: reqObject.data, 40 | }; 41 | let result = await axios(config); 42 | return result; 43 | }, 44 | 45 | makeGETHeaderRequest: async function (reqObject) { 46 | const config = { 47 | method: "get", 48 | url: reqObject.url, 49 | headers: reqObject.headers, 50 | }; 51 | let result = await axios(config); 52 | return result; 53 | }, 54 | 55 | makePOSTHeaderRequest: async function (reqObject) { 56 | const config = { 57 | method: "post", 58 | url: reqObject.url, 59 | headers: reqObject.headers, 60 | data: reqObject.data, 61 | }; 62 | let result = await axios(config); 63 | return result; 64 | }, 65 | 66 | makeDELETEHeaderRequest: async function (reqObject) { 67 | const config = { 68 | method: "delete", 69 | url: reqObject.url, 70 | headers: reqObject.headers, 71 | }; 72 | let result = await axios(config); 73 | return result; 74 | }, 75 | 76 | makePOSTRequestWithToken: async function (reqObject) { 77 | const config = { 78 | method: "post", 79 | url: reqObject.url, 80 | headers: { 81 | "Content-Type": reqObject.contentType, 82 | Authorization: reqObject.token, 83 | }, 84 | data: reqObject.data, 85 | }; 86 | let result = await axios(config); 87 | return result; 88 | }, 89 | 90 | makeGETHeaderRequestProxy: async function (reqObject) { 91 | const agent = new httpsProxyAgent(reqObject.proxy); 92 | const config = { 93 | method: "get", 94 | url: reqObject.url, 95 | headers: reqObject.headers, 96 | httpsAgent: agent, 97 | }; 98 | let result = await axios(config); 99 | return result; 100 | }, 101 | 102 | makePOSTHeaderRequestProxy: async function (reqObject) { 103 | const agent = new httpsProxyAgent(reqObject.proxy); 104 | const config = { 105 | method: "post", 106 | url: reqObject.url, 107 | headers: reqObject.headers, 108 | data: reqObject.data, 109 | httpsAgent: agent, 110 | }; 111 | let result = await axios(config); 112 | return result; 113 | }, 114 | 115 | makePUTHeaderRequest: async function (reqObject) { 116 | const config = { 117 | method: "put", 118 | url: reqObject.url, 119 | headers: reqObject.headers, 120 | data: reqObject.data, 121 | }; 122 | let result = await axios(config); 123 | return result; 124 | }, 125 | }; 126 | -------------------------------------------------------------------------------- /server/helpers/creds.json: -------------------------------------------------------------------------------- 1 | { "email": "", "password": "" } 2 | -------------------------------------------------------------------------------- /server/helpers/crypto.js: -------------------------------------------------------------------------------- 1 | const crypto = require("crypto"); 2 | const algorithm = "aes256"; 3 | 4 | /** 5 | * 6 | * @param {string} text 7 | * @param {string} encryptionKey 8 | */ 9 | const encrypt = function (text, encryptionKey) { 10 | try { 11 | let iv = crypto.randomBytes(16); 12 | let cipher = crypto.createCipheriv( 13 | algorithm, 14 | Buffer.from(encryptionKey), 15 | iv 16 | ); 17 | let encrypted = cipher.update(text); 18 | encrypted = Buffer.concat([encrypted, cipher.final()]); 19 | return iv.toString("hex") + ":" + encrypted.toString("hex"); 20 | } catch (error) { 21 | console.log("encrypt_error", error); 22 | return "error"; 23 | } 24 | }; 25 | 26 | /** 27 | * 28 | * @param {string} encrypted 29 | * @param {string} encryptionKey 30 | */ 31 | const decrypt = function (encrypted, encryptionKey) { 32 | try { 33 | let textParts = encrypted.split(":"); 34 | let iv = Buffer.from(textParts.shift(), "hex"); 35 | let encryptedText = Buffer.from(textParts.join(":"), "hex"); 36 | let decipher = crypto.createDecipheriv( 37 | algorithm, 38 | Buffer.from(encryptionKey), 39 | iv 40 | ); 41 | let decrypted = decipher.update(encryptedText); 42 | decrypted = Buffer.concat([decrypted, decipher.final()]); 43 | return decrypted.toString(); 44 | } catch (error) { 45 | console.log("decrypt_error", error); 46 | return "error"; 47 | } 48 | }; 49 | 50 | exports.AESEncrypt = encrypt; 51 | exports.AESDecrypt = decrypt; 52 | -------------------------------------------------------------------------------- /server/helpers/data.json: -------------------------------------------------------------------------------- 1 | {"secret":"a9029e3bb6f23c30841c63bbc5b04f05"} -------------------------------------------------------------------------------- /server/helpers/databaseHelpers/adminHelper.js: -------------------------------------------------------------------------------- 1 | const admin = require("../../models/admin"); 2 | 3 | module.exports = { 4 | getAdminsByAlertLevel: async (level) => { 5 | try { 6 | const adminData = await admin.find({ 7 | alerts: { $gte: level }, 8 | }); 9 | return adminData; 10 | } catch (error) { 11 | logger.error(`adminHelper_getAdminsByAlertLevel_error : `, error); 12 | return []; 13 | } 14 | }, 15 | 16 | updateAlertLevel: async (email, level) => { 17 | try { 18 | await admin.updateOne( 19 | { 20 | email: email, 21 | }, 22 | { 23 | $set: { 24 | alerts: level, 25 | }, 26 | } 27 | ); 28 | return true; 29 | } catch (error) { 30 | logger.error(`adminHelper_updateAlertLevel_error : `, error); 31 | return "error"; 32 | } 33 | }, 34 | }; 35 | -------------------------------------------------------------------------------- /server/helpers/decryptedEnv.js: -------------------------------------------------------------------------------- 1 | const dailyData = require("../models/dailyData"); 2 | const _ = require("lodash"); 3 | // const { argv } = require("yargs"); 4 | // const secret = argv["secret"]; 5 | // const fs = require("fs"); 6 | // const path = require("path"); 7 | 8 | // const data = fs.readFileSync(path.resolve(__dirname, "data.json"), "UTF-8"); 9 | // const secret = JSON.parse(data).secret; 10 | // if (_.isEmpty(secret)) process.exit(9); 11 | 12 | const { AESDecrypt } = require("./crypto"); 13 | 14 | /** 15 | * 16 | * @param {String} encryptedKey name of field as per .env 17 | */ 18 | const GetDecryptedEnv = async (encryptedKey) => { 19 | const data = await dailyData.findOne({ id: 1 }); 20 | const secret = data.data; 21 | const decoded = AESDecrypt(encryptedKey, secret); 22 | return decoded; 23 | }; 24 | 25 | exports.GetDecryptedEnv = GetDecryptedEnv; 26 | -------------------------------------------------------------------------------- /server/helpers/exchangeSocket/bitfinex.js: -------------------------------------------------------------------------------- 1 | const logSymbols = require("log-symbols"); 2 | 3 | const WsWrapper = require("../socketClass/wsClass"); 4 | 5 | const bitURL = `wss://api-pub.bitfinex.com/ws/2`; 6 | const Subscribed_Pairs = [ 7 | "XDC-USDT", 8 | "USDC-USDT", 9 | "ETH-USDT", 10 | "XRP-USDT", 11 | "BTC-USDT", 12 | ]; 13 | 14 | let validPair = ""; 15 | for (let i = 0; i < Subscribed_Pairs.length; i++) { 16 | let currPair = Subscribed_Pairs[i]; 17 | if (i !== Subscribed_Pairs.length - 1) { 18 | validPair += `${currPair},`; 19 | } else { 20 | validPair += `${currPair}`; 21 | } 22 | } 23 | 24 | validPair = validPair.split(","); 25 | if (validPair.length > 0) { 26 | for (let pair of validPair) { 27 | let [firstCurrency, secondCurrency] = pair 28 | .split("-") 29 | .map((e) => e.toUpperCase()); 30 | if (secondCurrency === "USDT") secondCurrency = "UST"; 31 | if (firstCurrency === "EURT") firstCurrency = "EUT"; 32 | else if (firstCurrency === "USDC") firstCurrency = "UDC"; 33 | let tempPair = ""; 34 | tempPair = `t${firstCurrency}${secondCurrency}`; 35 | let sData = { 36 | pair: tempPair, 37 | orgPair: pair, 38 | }; 39 | setTimeout(() => { 40 | bitfinexSocket(sData); 41 | }, 10000); 42 | } 43 | } 44 | 45 | const bitfinexSocket = (sData) => { 46 | let onopen = function (data) { 47 | logger.info("[", logSymbols.success, "] bitfinex ws connected", sData.pair); 48 | let msg = JSON.stringify({ 49 | event: "subscribe", 50 | channel: "ticker", 51 | symbol: sData.pair, 52 | }); 53 | 54 | setTimeout(() => { 55 | this.socket.send(msg); 56 | }, 1000); 57 | }; 58 | let onclose = function (evt) { 59 | // console.log("bitfinex ws close", evt); 60 | this.socket.removeAllListeners(); 61 | this.reconnect(); 62 | }; 63 | let onerror = function (evt) { 64 | console.log("bitfinex ws error", evt); 65 | }; 66 | let onmessage = async function (evt) { 67 | let wsData = JSON.parse(evt.data); 68 | 69 | if (wsData[1] && wsData[1] != "hb" && wsData[1] != "cs") { 70 | const query = {}; 71 | 72 | let originalPair = ""; 73 | if (["XDC-USD"].includes(sData.orgPair)) { 74 | originalPair = sData.orgPair.replace("-USD", "-USDT"); 75 | } else { 76 | originalPair = sData.orgPair; 77 | } 78 | 79 | query[originalPair] = { 80 | bid: [wsData[1][0]], 81 | ask: [wsData[1][2]], 82 | }; 83 | InternalBus.emit(GlobalEvents.converter_started, originalPair); 84 | InternalBus.emit(GlobalEvents.converter_price, query); 85 | } 86 | }; 87 | 88 | let wSocket = new WsWrapper( 89 | "bitfinex_converter", 90 | bitURL, 91 | { 92 | onClose: onclose, 93 | onOpen: onopen, 94 | onError: onerror, 95 | onMessage: onmessage, 96 | }, 97 | { asks: [], bids: [] } 98 | ); 99 | }; 100 | -------------------------------------------------------------------------------- /server/helpers/globals.js: -------------------------------------------------------------------------------- 1 | const Event = require("events"); 2 | const InternalBus = new Event.EventEmitter(); 3 | const requestIp = require("request-ip"); 4 | 5 | const isset = async function (variable) { 6 | return typeof variable !== typeof undefined ? true : false; 7 | }; 8 | 9 | global.isset = isset; 10 | 11 | const GlobalEvents = Object.freeze({ 12 | mongodb_connected: 0, 13 | exchange_pair_updated: 1, 14 | exchange_orderbook_update: 2, 15 | crons_synced: 3, 16 | admin_logout: 4, 17 | socket_sync: 5, 18 | best_price: 6, 19 | monitor: 7, 20 | converter_price: 8, 21 | converter_started: 9, 22 | exchange_updated: 10, 23 | exchange_deactivated: 11, 24 | volume_bot_updated: 12, 25 | arbitrage_updated: 13, 26 | favourable_orders: 14, 27 | arbitrage_order_placed: 15, 28 | }); 29 | 30 | /** 31 | * Applicaitons internal bus to be used for pushing data across files on certain events 32 | * For: database connection. 33 | * 34 | * @NOTE Theres a cap on the amount of listeners on the app ( max-listernes ); only use when no other option 35 | */ 36 | global.InternalBus = InternalBus; 37 | global.GlobalEvents = GlobalEvents; 38 | 39 | global.timeouts = {}; 40 | global.flags = {}; 41 | 42 | /** 43 | * Global Stack Trace 44 | */ 45 | 46 | Object.defineProperty(global, "__stack", { 47 | get: function () { 48 | const orig = Error.prepareStackTrace; 49 | Error.prepareStackTrace = function (_, stack) { 50 | return stack; 51 | }; 52 | const err = new Error(); 53 | Error.captureStackTrace(err, arguments.callee); 54 | const stack = err.stack; 55 | Error.prepareStackTrace = orig; 56 | return stack; 57 | }, 58 | }); 59 | 60 | Object.defineProperty(Object.prototype, "partialMatch", { 61 | value: function (fields) { 62 | for (let key of Object.keys(fields)) { 63 | if (Object.keys(this).includes(key)) { 64 | if (this[key] === fields[key]) continue; 65 | return false; 66 | } else { 67 | return false; 68 | } 69 | } 70 | return true; 71 | }, 72 | }); 73 | 74 | Object.defineProperty(Array.prototype, "includesPartial", { 75 | value: function (fields) { 76 | for (let i = 0; i < this.length; i++) { 77 | const obj = this[i]; 78 | if (obj.partialMatch(fields)) { 79 | return i; 80 | } 81 | } 82 | return null; 83 | }, 84 | }); 85 | 86 | // Object.defineProperty(global, "__line", { 87 | // get: function () { 88 | // return __stack[1].getLineNumber(); 89 | // }, 90 | // }); 91 | 92 | function getCallerIP(request) { 93 | const clientIp = requestIp.getClientIp(request); 94 | var ip = 95 | request.headers["x-forwarded-for"] || 96 | request.connection.remoteAddress || 97 | request.socket.remoteAddress || 98 | request.connection.socket.remoteAddress; 99 | let ipa = ip.split(",").shift(); 100 | 101 | ip = ipa.split(":").slice(-1).shift(); //in case the ip returned in a format: "::ffff:146.xxx.xxx.xxx" 102 | if (isCorrectIP(ip) === false) { 103 | let ipNew = ipa.split(":").shift(); 104 | if (isCorrectIP(ipNew) === false) { 105 | return "Not Found"; 106 | } else { 107 | return ipNew; 108 | } 109 | } else { 110 | return ip; 111 | } 112 | } 113 | global.getCallerIP = getCallerIP; 114 | 115 | async function isCorrectIP(ip) { 116 | const isIp = require("is-ip"); 117 | if (isIp(ip)) { 118 | return true; 119 | } else { 120 | return false; 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /server/helpers/logger.js: -------------------------------------------------------------------------------- 1 | /** 2 | * logger.js file to print logs in a more structured manner. 3 | * there are two levels of logs 'error', 'info' and 'debug'. 4 | * use info for as how you use console.log 5 | * use debug to print more info which you feel might be required to log in case of debugging 6 | * 7 | * @NOTE logger accepts only one arguement which has to a string or a non-circular json 8 | */ 9 | 10 | const winston = require("winston"); 11 | const path = require("path"); 12 | const fs = require("fs"); 13 | const _ = require("lodash"); 14 | const { argv } = require("yargs"); 15 | const stack = require("callsite"); 16 | 17 | require("winston-daily-rotate-file"); 18 | 19 | const { combine, timestamp, printf } = winston.format; 20 | 21 | const logPath = path.join(__dirname, "../logs"); 22 | const appPath = path.join(logPath, "info"); 23 | const debugPath = path.join(logPath, "debug"); 24 | const errorPath = path.join(logPath, "error"); 25 | const networkPath = path.join(logPath, "network"); 26 | 27 | const defaultLogLevel = "info"; 28 | const validLogLevels = ["info", "debug", "error", "silly", "orders"]; 29 | let logLevel = defaultLogLevel; 30 | 31 | if (argv["log-level"]) { 32 | if (validLogLevels.includes(argv["log-level"]) === true) { 33 | console.warn(`[*] log level: ${argv["log-level"]}`); 34 | logLevel = argv["log-level"]; 35 | } else { 36 | console.warn("[*] invalid log level"); 37 | } 38 | } 39 | 40 | if (!fs.existsSync(logPath)) { 41 | console.log("[*] creating log folders"); 42 | fs.mkdirSync(logPath); 43 | fs.mkdirSync(appPath); 44 | fs.mkdirSync(debugPath); 45 | fs.mkdirSync(errorPath); 46 | fs.mkdirSync(networkPath); 47 | } else { 48 | if (!fs.existsSync(appPath)) fs.mkdirSync(appPath); 49 | if (!fs.existsSync(debugPath)) fs.mkdirSync(debugPath); 50 | if (!fs.existsSync(errorPath)) fs.mkdirSync(errorPath); 51 | if (!fs.existsSync(networkPath)) fs.mkdirSync(networkPath); 52 | } 53 | 54 | const levels = { 55 | error: 0, 56 | info: 1, 57 | debug: 2, 58 | silly: 3, 59 | }; 60 | 61 | const logger = winston.createLogger({ 62 | levels, 63 | format: combine( 64 | timestamp(), 65 | printf(({ level, message, timestamp, error, ...rest }) => { 66 | if (_.isObject(message)) message = JSON.stringify(message); 67 | 68 | let allRest = ""; 69 | rest = rest[Symbol.for("splat")] || []; 70 | 71 | if (rest.length > 0) { 72 | allRest = rest 73 | .map((e) => { 74 | if (_.isObject(e)) { 75 | if (e.stack) { 76 | return e.stack; 77 | } 78 | delete e.apiKey; 79 | delete e.apiSecret; 80 | delete e.token; 81 | delete e.tokenId; 82 | delete e.tokenSecret; 83 | delete e.walletAddress; 84 | delete e.privateKey; 85 | delete e.passPhrase; 86 | return JSON.stringify(e); 87 | } 88 | return e; 89 | }) 90 | .join(" "); 91 | } 92 | 93 | let infoStack = "\nStackTrace::: "; 94 | 95 | stack().forEach(function (site) { 96 | infoStack += `File: ${site.getFileName()}:${site.getLineNumber()} Function: ${ 97 | site.getFunctionName() || "anonymous" 98 | } \n`; 99 | }); 100 | 101 | if (error) { 102 | if (error.stack) 103 | return `${timestamp} ${level}: ${message} Error:${error.stack} ${allRest}`; 104 | else { 105 | return `${timestamp} ${level}: ${infoStack}: ${message} Error: ${JSON.stringify( 106 | error 107 | )} ${allRest}`; 108 | } 109 | } 110 | 111 | if (level === "error") { 112 | return `${timestamp} ${level}: ${infoStack} ${message} ${allRest}`; 113 | } 114 | 115 | return `${timestamp} ${level}: ${message} ${allRest}`; 116 | }) 117 | ), 118 | transports: [ 119 | new winston.transports.DailyRotateFile({ 120 | filename: `${errorPath}/error-%DATE%.log`, 121 | datePattern: "YYYY-MM-DD", 122 | level: "error", 123 | maxSize: "10m", 124 | }), 125 | new winston.transports.DailyRotateFile({ 126 | filename: `${appPath}/info-%DATE%.log`, 127 | datePattern: "YYYY-MM-DD", 128 | level: "info", 129 | maxSize: "10m", 130 | }), 131 | new winston.transports.DailyRotateFile({ 132 | filename: `${debugPath}/debug-%DATE%.log`, 133 | datePattern: "YYYY-MM-DD", 134 | level: "debug", 135 | maxSize: "10m", 136 | }), 137 | // new winston.transports.DailyRotateFile({ 138 | // filename: `${networkPath}/network-%DATE%.log`, 139 | // datePattern: "YYYY-MM-DD", 140 | // level: "silly", 141 | // }), 142 | new winston.transports.Console({ 143 | level: logLevel, 144 | }), 145 | ], 146 | }); 147 | 148 | // logger.debug("in debug file only"); 149 | // logger.info("in files above ( including ) info"); 150 | // logger.error("in all files"); 151 | 152 | // logger.stream = { 153 | // write: function (message, encoding) { 154 | // logger.silly(message); 155 | // }, 156 | // }; 157 | 158 | global.logger = logger; 159 | 160 | // try { 161 | // throw new Error("test error"); 162 | // } catch (e) { 163 | // logger.error("this will thow error", e); 164 | // } 165 | -------------------------------------------------------------------------------- /server/helpers/networkLogger.js: -------------------------------------------------------------------------------- 1 | const winston = require("winston"); 2 | const path = require("path"); 3 | const fs = require("fs"); 4 | const _ = require("lodash"); 5 | 6 | const { combine, timestamp, printf } = winston.format; 7 | 8 | const logPath = path.join(__dirname, "../logs"); 9 | const networkPath = path.join(logPath, "network"); 10 | 11 | if (!fs.existsSync(logPath)) { 12 | console.log("[*] creating log folders"); 13 | fs.mkdirSync(logPath); 14 | fs.mkdirSync(networkPath); 15 | } else { 16 | if (!fs.existsSync(networkPath)) fs.mkdirSync(networkPath); 17 | } 18 | 19 | const NetLogger = winston.createLogger({ 20 | levels: { req: 0 }, 21 | format: combine( 22 | timestamp(), 23 | printf(({ level, message, timestamp, error, ...rest }) => { 24 | if (_.isObject(message)) message = JSON.stringify(message); 25 | 26 | let allRest = ""; 27 | rest = rest[Symbol.for("splat")] || []; 28 | 29 | if (rest.length > 0) { 30 | allRest = rest 31 | .map((e) => { 32 | if (_.isObject(e)) { 33 | if (e.stack) { 34 | return e.stack; 35 | } 36 | return JSON.stringify(e); 37 | } 38 | return e; 39 | }) 40 | .join(" "); 41 | } 42 | 43 | if (error) { 44 | if (error.stack) 45 | return `${timestamp} ${level}: ${message} Error:${error.stack} ${allRest}`; 46 | else { 47 | return `${timestamp} ${level}: ${message} Error:${JSON.stringify( 48 | error 49 | )} ${allRest}`; 50 | } 51 | } 52 | 53 | return `${timestamp} ${level}: ${message} ${allRest}`; 54 | }) 55 | ), 56 | transports: [ 57 | new winston.transports.DailyRotateFile({ 58 | filename: `${networkPath}/network-%DATE%.log`, 59 | datePattern: "YYYY-MM-DD", 60 | level: "req", 61 | }), 62 | ], 63 | }); 64 | 65 | require("winston-daily-rotate-file"); 66 | 67 | NetLogger.stream = { 68 | write: function (message, encoding) { 69 | NetLogger.req(message); 70 | }, 71 | }; 72 | 73 | exports.NetLogger = NetLogger; 74 | -------------------------------------------------------------------------------- /server/helpers/socketClass/wsClass.js: -------------------------------------------------------------------------------- 1 | const WebSocket = require("ws"); 2 | 3 | const defaultListener = (d) => { 4 | console.log("d", d); 5 | }; 6 | 7 | class WsWrapper { 8 | constructor( 9 | name, 10 | wss, 11 | listeners = { 12 | onClose: defaultListener, 13 | onOpen: defaultListener, 14 | onError: defaultListener, 15 | onMessage: defaultListener, 16 | }, 17 | ...rest 18 | ) { 19 | this.socketName = name; 20 | this.wss = wss; 21 | this.listeners = listeners; 22 | this.vars = rest; 23 | this.connect(); 24 | } 25 | 26 | connect() { 27 | this.socket = new WebSocket(this.wss); 28 | this.initiateListeners(); 29 | } 30 | 31 | reconnect() { 32 | this.socket.removeAllListeners(); 33 | this.socket.close(); 34 | this.socket = undefined; 35 | this.connect(); 36 | } 37 | 38 | initiateListeners() { 39 | this.socket.onopen = this.listeners.onOpen.bind(this); 40 | this.socket.onmessage = this.listeners.onMessage.bind(this); 41 | this.socket.onerror = this.listeners.onError.bind(this); 42 | this.socket.onclose = this.listeners.onClose.bind(this); 43 | } 44 | 45 | terminate() { 46 | this.socket.removeAllListeners(); 47 | this.socket.close(); 48 | } 49 | } 50 | 51 | module.exports = WsWrapper; 52 | -------------------------------------------------------------------------------- /server/helpers/socket_io.js: -------------------------------------------------------------------------------- 1 | const app = require("express")(); 2 | const server = require("http").createServer(app); 3 | const io = require("socket.io")(server); 4 | const jwt = require("jsonwebtoken"); 5 | 6 | const { RedisClient } = require("../services/redis"); 7 | 8 | io.listen(process.env.WS_PORT || 3002); 9 | 10 | global.socket_io = io; 11 | 12 | let SOCKET_COUNT = 0; 13 | 14 | /** 15 | * 16 | * SOCKET MIDDDLEWARE STARTS 17 | * 18 | */ 19 | 20 | io.use((socket, next) => { 21 | try { 22 | if (socket.handshake.query.transfer == "test") { 23 | next(); 24 | } else { 25 | if (!socket.handshake.query && !socket.handshake.query.jwt) { 26 | return socket.disconnect(); 27 | } 28 | 29 | const token = socket.handshake.query.jwt; 30 | 31 | const decodedUser = jwt.verify(token, process.env.ACCESS_TOKEN_SECRET); 32 | 33 | if (decodedUser.exp * 1000 < Date.now()) return socket.disconnect(); 34 | 35 | logger.debug(":ws jwt ok"); 36 | 37 | socket.user = decodedUser; 38 | 39 | next(); 40 | } 41 | } catch (e) { 42 | logger.error("socket_connection_error", e); 43 | return socket.disconnect(); 44 | } 45 | }); 46 | 47 | io.use((socket, next) => { 48 | SOCKET_COUNT++; 49 | emitSocketSync(); 50 | next(); 51 | }); 52 | 53 | /**connected to mongodb 54 | * 55 | * SOCKET MIDDDLEWARE STOPS 56 | * 57 | * @note no listerners in socket handler permitted without proper exit calls 58 | */ 59 | 60 | /** 61 | * 62 | * SOCKET CONNECTION HANDLER STARTS 63 | * 64 | */ 65 | io.on("connection", (socket) => { 66 | let last_ping = Date.now(); 67 | let intervalRef; 68 | 69 | emitSocketSync(); 70 | // logger.debug(":ws ", socket.user.email); 71 | 72 | socket.on("ping-transfer", (cb) => { 73 | last_ping = Date.now(); 74 | socket.emit("pong"); 75 | cb(true); 76 | }); 77 | 78 | socket.on("heart-beat", (token) => { 79 | try { 80 | const currDecoded = jwt.verify(token, process.env.ACCESS_TOKEN_SECRET); 81 | if (currDecoded.email !== socket.user.email) return socket.disconnect(); 82 | if (currDecoded.exp * 1000 < Date.now()) return socket.disconnect(); 83 | last_ping = Date.now(); 84 | socket.emit("pong"); 85 | } catch (e) { 86 | logger.error(e); 87 | if (socket) socket.disconnect(); 88 | } 89 | }); 90 | 91 | socket.once("disconnect", () => { 92 | socket = undefined; 93 | clearInterval(intervalRef); 94 | SOCKET_COUNT--; 95 | emitSocketSync(); 96 | }); 97 | 98 | socket.on("joinRooms", (rooms) => { 99 | rooms.forEach((room) => { 100 | console.log("joining room", room); 101 | socket.join(room); 102 | emitStarterData(room, socket); 103 | }); 104 | emitSocketSync(); 105 | }); 106 | 107 | socket.on("leaveAll", () => { 108 | socket.leaveAll(); 109 | }); 110 | 111 | socket.on("get_wallet_balance", () => { 112 | emitStarterData("wallet", socket); 113 | }); 114 | 115 | intervalRef = setInterval(() => { 116 | if (Date.now() - last_ping > 10000) { 117 | socket.disconnect(); 118 | } 119 | }, 5000); 120 | }); 121 | 122 | /** 123 | * 124 | * SOCKET CONNECTION HANDLER STOPS 125 | * 126 | */ 127 | 128 | /** 129 | * 130 | * BROADCASTS STARTS 131 | * 132 | */ 133 | 134 | function emitSocketSync() { 135 | io.emit("socket_count", SOCKET_COUNT); 136 | } 137 | 138 | // setInterval(() => console.log(SOCKET_COUNT), 5000); 139 | 140 | /** 141 | * 142 | * BROADCASTS STOPS 143 | * 144 | */ 145 | 146 | /** 147 | * 148 | * HELPER FUNCS 149 | * 150 | */ 151 | 152 | async function emitStarterData(room, socket) { 153 | try { 154 | switch (room) { 155 | case "op_connect": { 156 | return InternalBus.emit(GlobalEvents.mongodb_connected); 157 | } 158 | 159 | case "monitor": { 160 | return socket.emit("memory_usage", process.memoryUsage()); 161 | } 162 | 163 | case "wallet": { 164 | socket.emit( 165 | "balances", 166 | JSON.parse(await RedisClient.get("wallet_balances")) 167 | ); 168 | const wallet_balance = JSON.parse( 169 | await RedisClient.get("wallet_balances") 170 | ); 171 | const exchanges = Object.keys(wallet_balance); 172 | let data = []; 173 | for (let exchange of exchanges) { 174 | data = [ 175 | ...data, 176 | ...wallet_balance[exchange].data.map((e) => { 177 | return { ...e, exchange }; 178 | }), 179 | ]; 180 | } 181 | 182 | data = data.reduce((acc, e) => { 183 | const index = acc.includesPartial({ 184 | currency: e.currency, 185 | // botName: e.botName, 186 | }); 187 | if (index !== null) { 188 | acc[index].balance += e.balance; 189 | } else { 190 | acc.push({ 191 | ...e, 192 | }); 193 | } 194 | return acc; 195 | }, []); 196 | 197 | socket.emit("live_snapshot", data); 198 | return; 199 | } 200 | 201 | default: 202 | return; 203 | } 204 | } catch (e) { 205 | logger.error(e); 206 | } 207 | } 208 | -------------------------------------------------------------------------------- /server/middlewares/errorHandler.js: -------------------------------------------------------------------------------- 1 | const { CustomError } = require("../helpers/errors"); 2 | 3 | exports.errorHandler = (err, req, res, next) => { 4 | if (err instanceof CustomError) { 5 | logger.error(err); 6 | return res.status(err.statusCode).send({ errors: err.serializeErrors() }); 7 | } 8 | 9 | logger.error("uncaught error", { error: err }); 10 | 11 | res.status(500).send({ 12 | errors: [{ message: "internal error" }], 13 | }); 14 | }; 15 | -------------------------------------------------------------------------------- /server/middlewares/requireAdmin.js: -------------------------------------------------------------------------------- 1 | const jwt = require("jsonwebtoken"); 2 | 3 | module.exports = function (req, res, next) { 4 | const token = req.headers.authorization || req.headers.token; 5 | 6 | jwt.verify( 7 | token, 8 | process.env.ACCESS_TOKEN_SECRET, 9 | async function (err, decoded) { 10 | if (err) { 11 | // logger.error(`requireAdmin_jwt_error`, err, token); 12 | return responseHelper.error(res, "please login again."); 13 | } else { 14 | if (decoded.level !== 1 && decoded.level !== 2) { 15 | return res 16 | .status(401) 17 | .json({ errors: [{ message: "unauthorized" }] }); 18 | } 19 | req.user = decoded; 20 | next(); 21 | } 22 | } 23 | ); 24 | }; 25 | -------------------------------------------------------------------------------- /server/middlewares/requireSuperAdmin.js: -------------------------------------------------------------------------------- 1 | const jwt = require("jsonwebtoken"); 2 | 3 | module.exports = function (req, res, next) { 4 | const token = req.headers.authorization || req.headers.token; 5 | 6 | jwt.verify( 7 | token, 8 | process.env.ACCESS_TOKEN_SECRET, 9 | async function (err, decoded) { 10 | if (err) { 11 | return responseHelper.error(res, "please login again."); 12 | } else { 13 | if (decoded.level !== 2) { 14 | return res 15 | .status(401) 16 | .json({ errors: [{ message: "unauthorized" }] }); 17 | } 18 | req.user = decoded; 19 | next(); 20 | } 21 | } 22 | ); 23 | }; 24 | -------------------------------------------------------------------------------- /server/middlewares/validateRequest.js: -------------------------------------------------------------------------------- 1 | const { validationResult } = require("express-validator"); 2 | const { RequestValidationError } = require("../helpers/errors"); 3 | 4 | exports.validateRequest = (checks) => { 5 | return async function (req, res, next) { 6 | for (let check of checks) { 7 | await check.run(req); 8 | } 9 | 10 | const errors = validationResult(req); 11 | if (!errors.isEmpty()) { 12 | throw new RequestValidationError(errors.array()); 13 | } else { 14 | next(); 15 | } 16 | }; 17 | }; 18 | -------------------------------------------------------------------------------- /server/models/admin.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | const bcrypt = require("bcrypt"); 3 | 4 | const userSchema = mongoose.Schema( 5 | { 6 | name: { type: String, required: true }, 7 | email: { type: String, unique: true, required: true }, 8 | password: { type: String, required: true }, 9 | level: { type: Number, enum: [0, 1, 2], required: true, default: 0 }, // needs to be set manually or can be approved by a level 2 admin 10 | alerts: { type: Number, enum: [0, 1, 2], default: 0 }, //0 no mails, 1 critical, 2 all 11 | }, 12 | { 13 | timestamps: true, 14 | } 15 | ); 16 | 17 | userSchema.methods.generateHash = function (password) { 18 | return bcrypt.hashSync(password, bcrypt.genSaltSync(8), null); 19 | }; 20 | 21 | userSchema.methods.validPassword = function (password) { 22 | return bcrypt.compareSync(password, this.password); 23 | }; 24 | 25 | module.exports = mongoose.model("Admin", userSchema); 26 | -------------------------------------------------------------------------------- /server/models/arbitrageOperations.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | 3 | const arbitrageOperationsSchema = mongoose.Schema( 4 | { 5 | minAmount: {}, 6 | maxAmount: {}, 7 | minPriceBy: [ 8 | { 9 | type: String, 10 | enum: ["total_amount", "price_diff"], 11 | default: "total_amount", 12 | }, 13 | ], 14 | minPrice: { 15 | total_amount: Number, 16 | price_diff: Number, 17 | }, 18 | virtualArbitrage: { type: Boolean, default: false }, 19 | exchangeConnection: {}, 20 | connectionDetails: {}, 21 | status: { type: Boolean, default: false }, 22 | activeOrderTolerance: Number, 23 | statusMsg: String, 24 | }, 25 | { 26 | timestamps: true, 27 | } 28 | ); 29 | 30 | module.exports = mongoose.model( 31 | "ArbitrageOperations", 32 | arbitrageOperationsSchema 33 | ); 34 | -------------------------------------------------------------------------------- /server/models/completedOrders.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | const Schema = mongoose.Schema; 3 | 4 | let completedOrders = new Schema( 5 | { 6 | botType: { type: String, required: true }, 7 | exchange: { type: String, required: true }, 8 | pair: { type: String, required: true }, 9 | type: { type: String, required: true, enum: ["buy", "sell"] }, 10 | account: { type: String, required: true }, 11 | exchangeId: { type: String, required: true }, 12 | price: { type: Number, required: true, default: 0 }, 13 | usdtPrice: { type: Number, required: true, default: 0 }, 14 | XDC: { type: Number, default: 0 }, 15 | USDT: { type: Number, default: 0 }, 16 | USD: { type: Number, default: 0 }, 17 | ETH: { type: Number, default: 0 }, 18 | BTC: { type: Number, default: 0 }, 19 | XRP: { type: Number, default: 0 }, 20 | IDR: { type: Number, default: 0 }, 21 | SGD: { type: Number, default: 0 }, 22 | SRX: { type: Number, default: 0 }, 23 | PLI: { type: Number, default: 0 }, 24 | LBT: { type: Number, default: 0 }, 25 | fees: { type: Number, default: 0 }, 26 | feeCurrency: { type: String, default: "USD" }, 27 | feesUSDT: { type: Number, default: 0 }, 28 | status: { type: String, default: "active" }, 29 | deletedAt: { type: String }, 30 | }, 31 | { 32 | timestamps: true, 33 | } 34 | ); 35 | 36 | //export the model 37 | module.exports = mongoose.model("completedOrders", completedOrders); 38 | -------------------------------------------------------------------------------- /server/models/dailyData.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | 3 | const dailyData = new mongoose.Schema( 4 | { 5 | id: { type: Number }, 6 | data: { type: String }, 7 | }, 8 | { 9 | timestamps: true, 10 | } 11 | ); 12 | 13 | module.exports = mongoose.model("dailyData", dailyData); 14 | -------------------------------------------------------------------------------- /server/models/dailyStats.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | 3 | const dailyStats = new mongoose.Schema( 4 | { 5 | exchange: String, 6 | account: String, 7 | stats: [ 8 | { 9 | currency: String, 10 | yesterdayBalance: Number, 11 | todayBalance: Number, 12 | balanceChange: Number, 13 | type: { type: String, enum: ["profit", "loss"] }, 14 | diffUSDT: Number, 15 | }, 16 | ], 17 | time: String, 18 | }, 19 | { 20 | timestamps: true, 21 | } 22 | ); 23 | 24 | module.exports = mongoose.model("dailyStats", dailyStats); 25 | -------------------------------------------------------------------------------- /server/models/dailyWalletBalances.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | 3 | const dailyWalletBalances = new mongoose.Schema( 4 | { 5 | exchange: String, 6 | account: String, 7 | currency: [ 8 | { 9 | currency: String, 10 | balance: Number, 11 | inTrade: Number, 12 | total: Number, 13 | }, 14 | ], 15 | time: String, 16 | }, 17 | { 18 | timestamps: true, 19 | } 20 | ); 21 | 22 | module.exports = mongoose.model("dailyWalletBalances", dailyWalletBalances); 23 | -------------------------------------------------------------------------------- /server/models/exchangeCurrencies.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | 3 | const exchangeCurrencies = new mongoose.Schema( 4 | { 5 | exchange: { type: String, unique: true, required: true }, 6 | currency: [ 7 | { 8 | symbol: String, 9 | name: String, 10 | currencyId: String, 11 | exchangeSymbol: String, 12 | minimumBalance: Number, 13 | minArbBalance: Number, 14 | }, 15 | ], 16 | }, 17 | { 18 | timestamps: true, 19 | } 20 | ); 21 | 22 | module.exports = mongoose.model("exchangeCurrencies", exchangeCurrencies); 23 | -------------------------------------------------------------------------------- /server/models/exchangeData.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | 3 | const exchangeData = new mongoose.Schema( 4 | { 5 | uniqueId: { type: String, unique: true, required: true }, 6 | exchange: { type: String, unique: true, required: true }, 7 | apiKey: { type: String, default: "" }, 8 | apiSecret: { type: String, default: "" }, 9 | passPhrase: { type: String, default: "" }, 10 | subAccUserId: { type: String, default: "" }, 11 | accountId: { type: String, default: "" }, 12 | }, 13 | { 14 | timestamps: true, 15 | } 16 | ); 17 | 18 | module.exports = mongoose.model("exchangeData", exchangeData); 19 | -------------------------------------------------------------------------------- /server/models/exchangePair.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | const Schema = mongoose.Schema; 3 | 4 | let exchangePair = new Schema( 5 | { 6 | exchange: { type: String, unique: true, required: true }, 7 | pair: [ 8 | { 9 | name: { type: String, required: true }, 10 | decimalsAmount: { type: Number }, 11 | decimalsPrice: { type: Number }, 12 | minAmount: { type: Number, default: 0 }, 13 | maxAmount: { type: Number }, 14 | }, 15 | ], 16 | tradeFee: { type: Number, required: true, default: 0 }, 17 | disabled: { type: Boolean, default: false }, 18 | deletedAt: { type: String }, 19 | }, 20 | { 21 | timestamps: true, 22 | } 23 | ); 24 | 25 | //export the model 26 | module.exports = mongoose.model("exchangePair", exchangePair); 27 | -------------------------------------------------------------------------------- /server/models/exchangeWalletSnapshot.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | 3 | const walletSnapShot = new mongoose.Schema( 4 | { 5 | snapId: { type: String, required: true, index: true }, 6 | exchange: String, 7 | account: String, 8 | currency: [ 9 | { 10 | currency: String, 11 | balance: Number, 12 | inTrade: Number, 13 | total: Number, 14 | }, 15 | ], 16 | }, 17 | { 18 | timestamps: true, 19 | } 20 | ); 21 | 22 | module.exports = mongoose.model("walletSnapShot", walletSnapShot); 23 | -------------------------------------------------------------------------------- /server/models/spreadBotDetails.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | const Schema = mongoose.Schema; 3 | 4 | let spreadBotDetails = new Schema( 5 | { 6 | uniqueId: { type: String, required: true, unique: true }, 7 | exchange: { type: String, required: true }, 8 | pair: { type: String, required: true }, 9 | maxOrders: { type: Number, required: true, default: 0 }, 10 | percentGap: { type: Number, required: true, default: 0 }, 11 | price: { type: Number, required: true, default: 0 }, 12 | usdtPrice: { type: Number, required: true, default: 0 }, 13 | amountBuy: { type: Number, required: true, default: 0 }, 14 | amountSell: { type: Number, required: true, default: 0 }, 15 | placedAmountBuy: { type: Number, required: true, default: 0 }, 16 | filledAmountBuy: { type: Number, required: true, default: 0 }, 17 | placedTotalBuy: { type: Number, default: 0 }, //in USDT 18 | updatedTotalBuy: { type: Number, default: 0 }, //in USDT 19 | placedAmountSell: { type: Number, required: true, default: 0 }, 20 | filledAmountSell: { type: Number, required: true, default: 0 }, 21 | placedTotalSell: { type: Number, default: 0 }, //in USDT 22 | updatedTotalSell: { type: Number, default: 0 }, //in USDT 23 | mappedOrders: [], 24 | status: { type: String, required: true, default: "active" }, 25 | started: { type: Boolean, required: true, default: false }, 26 | // ordersGenerated: { type: Boolean, required: true, default: false }, 27 | balanceToBeMaintanedC1: { type: Number, required: true, default: 0 }, 28 | balanceToBeMaintanedC2: { type: Number, required: true, default: 0 }, 29 | lastSettledAtC1: { type: Number, required: true, default: 0 }, 30 | lastSettledAtC2: { type: Number, required: true, default: 0 }, 31 | deletedAt: { type: String }, 32 | }, 33 | { 34 | timestamps: true, 35 | } 36 | ); 37 | 38 | //export the model 39 | module.exports = mongoose.model("spreadBotDetails", spreadBotDetails); 40 | -------------------------------------------------------------------------------- /server/models/spreadBotGeneratedOrders.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | const Schema = mongoose.Schema; 3 | 4 | let spreadBotGeneratedOrders = new Schema( 5 | { 6 | uniqueId: { type: String, unique: true, required: true }, 7 | currency: { type: String, required: true }, 8 | type: { type: String, required: true }, 9 | usdtPrice: { type: Number, required: true, default: 0 }, 10 | status: { type: String, required: true, default: "active" }, 11 | revOrderId: { type: String, default: "" }, 12 | oppOrderId: { type: String, default: "" }, 13 | mappedOrders: [], 14 | }, 15 | { 16 | timestamps: true, 17 | } 18 | ); 19 | 20 | //export the model 21 | module.exports = mongoose.model( 22 | "spreadBotGeneratedOrders", 23 | spreadBotGeneratedOrders 24 | ); 25 | -------------------------------------------------------------------------------- /server/models/spreadBotMaintainOrders.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | const Schema = mongoose.Schema; 3 | 4 | let spreadBotMaintainOrders = new Schema( 5 | { 6 | orderId: { type: String, required: true }, 7 | uniqueId: { type: String, unique: true, required: true }, 8 | mappingId: { type: String, required: true }, 9 | exchange: { type: String, required: true }, 10 | pair: { type: String, required: true }, 11 | type: { type: String, required: true }, 12 | price: { type: Number, required: true, default: 0 }, 13 | usdtPrice: { type: Number, required: true, default: 0 }, 14 | originalQty: { type: Number, required: true, default: 0 }, 15 | filledQty: { type: Number, required: true, default: 0 }, 16 | total: { type: Number, required: true, default: 0 }, 17 | usdtTotal: { type: Number, required: true, default: 0 }, 18 | updatedTotal: { type: Number, required: true, default: 0 }, 19 | updatedUsdtTotal: { type: Number, required: true, default: 0 }, 20 | fees: { type: Number, default: 0 }, 21 | feeCurrency: { type: String, default: "USD" }, 22 | feesUSDT: { type: Number, default: 0 }, 23 | status: { type: String, required: true, default: "active" }, 24 | currentBalance: { type: Number, required: true, default: 0 }, 25 | currency: { type: String, required: true, default: "" }, 26 | }, 27 | { 28 | timestamps: true, 29 | } 30 | ); 31 | 32 | //export the model 33 | module.exports = mongoose.model( 34 | "spreadBotMaintainOrders", 35 | spreadBotMaintainOrders 36 | ); 37 | -------------------------------------------------------------------------------- /server/models/spreadBotOrders.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | const Schema = mongoose.Schema; 3 | 4 | let spreadBotOrders = new Schema( 5 | { 6 | orderId: { type: String, required: true }, 7 | uniqueId: { type: String, unique: true, required: true }, 8 | mappingId: { type: String, required: true }, 9 | exchange: { type: String, required: true }, 10 | pair: { type: String, required: true }, 11 | type: { type: String, required: true }, 12 | price: { type: Number, required: true, default: 0 }, 13 | usdtPrice: { type: Number, required: true, default: 0 }, 14 | originalQty: { type: Number, required: true, default: 0 }, 15 | filledQty: { type: Number, required: true, default: 0 }, 16 | total: { type: Number, required: true, default: 0 }, 17 | usdtTotal: { type: Number, required: true, default: 0 }, 18 | updatedTotal: { type: Number, required: true, default: 0 }, 19 | updatedUsdtTotal: { type: Number, required: true, default: 0 }, 20 | fees: { type: Number, default: 0 }, 21 | feeCurrency: { type: String, default: "USD" }, 22 | feesUSDT: { type: Number, default: 0 }, 23 | status: { type: String, required: true, default: "active" }, 24 | revOrderId: { type: String, default: "" }, 25 | oppOrderId: { type: String, default: "" }, 26 | cancelling: { type: Boolean, default: false }, 27 | // refId: { type: String }, 28 | }, 29 | { 30 | timestamps: true, 31 | } 32 | ); 33 | 34 | //export the model 35 | module.exports = mongoose.model("spreadBotOrders", spreadBotOrders); 36 | -------------------------------------------------------------------------------- /server/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "server", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "nodemon --max-old-space-size=1024 -r dotenv/config ./bin/www --ignore ./helpers/creds.json", 7 | "start": "nodemon --max-old-space-size=1024 -r dotenv/config ./bin/www --ignore ./helpers/creds.json", 8 | "start-pm2": "pm2 start ./bin/www --name Server --node-args='--max-old-space-size=1024' --time" 9 | }, 10 | "dependencies": { 11 | "axios": "^1.4.0", 12 | "bcrypt": "^5.1.0", 13 | "callsite": "^1.0.0", 14 | "cookie-parser": "~1.4.4", 15 | "cors": "^2.8.5", 16 | "cron": "^2.4.0", 17 | "crypto": "^1.0.1", 18 | "crypto-js": "^4.1.1", 19 | "debug": "^2.6.9", 20 | "dotenv": "^16.0.3", 21 | "events": "^3.3.0", 22 | "exceljs": "^4.3.0", 23 | "express": "~4.16.1", 24 | "express-async-errors": "^3.1.1", 25 | "express-validator": "^7.0.1", 26 | "fs": "0.0.1-security", 27 | "http-build-query": "^0.7.0", 28 | "http-errors": "^1.6.3", 29 | "jade": "~1.11.0", 30 | "jsonwebtoken": "^9.0.1", 31 | "lodash": "^4.17.21", 32 | "log-symbols": "^4.0.0", 33 | "mongoose": "^7.4.1", 34 | "morgan": "^1.9.1", 35 | "nodemailer": "^6.9.2", 36 | "nodemon": "^2.0.22", 37 | "nonce": "^1.0.4", 38 | "path": "^0.12.7", 39 | "pm2": "^5.3.0", 40 | "querystring": "^0.2.1", 41 | "redis": "^3.0.2", 42 | "request-ip": "^3.3.0", 43 | "socket.io": "^2.3.0", 44 | "tempfile": "^3.0.0", 45 | "uuid": "^9.0.0", 46 | "winston": "^3.8.2", 47 | "winston-daily-rotate-file": "^4.7.1", 48 | "ws": "^8.13.0", 49 | "yargs": "^17.7.2" 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /server/public/stylesheets/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding: 50px; 3 | font: 14px "Lucida Grande", Helvetica, Arial, sans-serif; 4 | } 5 | 6 | a { 7 | color: #00B7FF; 8 | } 9 | -------------------------------------------------------------------------------- /server/routes/index.js: -------------------------------------------------------------------------------- 1 | var express = require("express"); 2 | const indexController = require("../controllers/indexController"); 3 | var router = express.Router(); 4 | 5 | /* GET home page. */ 6 | 7 | router.get("/getusdrates", indexController.getUSDRates); 8 | 9 | module.exports = router; 10 | -------------------------------------------------------------------------------- /server/routes/spreadBot.js: -------------------------------------------------------------------------------- 1 | var express = require("express"); 2 | const spreadBotController = require("../controllers/spreadBotController"); 3 | var router = express.Router(); 4 | const RequireAdmin = require("../middlewares/requireAdmin"); 5 | 6 | /* GET home page. */ 7 | router.post("/addorder", RequireAdmin, spreadBotController.addOrder); 8 | router.post("/cancelorder", RequireAdmin, spreadBotController.cancelOrder); 9 | router.get("/getorders", RequireAdmin, spreadBotController.getOrders); 10 | router.post( 11 | "/getorderdetails", 12 | RequireAdmin, 13 | spreadBotController.getOrderDetails 14 | ); 15 | 16 | module.exports = router; 17 | -------------------------------------------------------------------------------- /server/views/error.jade: -------------------------------------------------------------------------------- 1 | extends layout 2 | 3 | block content 4 | h1= message 5 | h2= error.status 6 | pre #{error.stack} 7 | -------------------------------------------------------------------------------- /server/views/index.jade: -------------------------------------------------------------------------------- 1 | extends layout 2 | 3 | block content 4 | h1= title 5 | p Welcome to #{title} 6 | -------------------------------------------------------------------------------- /server/views/layout.jade: -------------------------------------------------------------------------------- 1 | doctype html 2 | html 3 | head 4 | title= title 5 | link(rel='stylesheet', href='/stylesheets/style.css') 6 | body 7 | block content 8 | --------------------------------------------------------------------------------