diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..9035f77 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,23 @@ +name: CI + +on: + push: + branches: main + pull_request: + +jobs: + my-job: + name: Test + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Setup Bun + uses: oven-sh/setup-bun@v1 + + - name: Install dependencies + run: bun i -p --frozen-lockfile + + - name: Run tests + run: bun test diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..bf2a2dd --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +# Dependencies +/node_modules + +# Environment variables +/.env* \ No newline at end of file diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..5610dff --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,3 @@ +{ + "recommendations": ["biomejs.biome", "oven.bun-vscode"] +} diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..a0d7416 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,13 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "internalConsoleOptions": "openOnSessionStart", + "name": "Debug", + "program": "${workspaceFolder}/src/index.ts", + "request": "launch", + "type": "bun", + "watchMode": "hot" + } + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..a188d42 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,7 @@ +{ + "editor.codeActionsOnSave": { + "source.organizeImports": "always" + }, + "editor.defaultFormatter": "biomejs.biome", + "editor.formatOnSave": true +} diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..604d6ef --- /dev/null +++ b/Dockerfile @@ -0,0 +1,37 @@ +# Use the official Bun image as the base +FROM oven/bun:1 as base +WORKDIR /usr/src/app + +# Install dependencies into a temporary directory to cache them and speed up future builds +FROM base AS install +RUN mkdir -p /temp/dev +COPY package.json bun.lockb /temp/dev/ +RUN cd /temp/dev && bun install --frozen-lockfile + +RUN cd /usr/src/app && ls + +# Install production dependencies only +RUN mkdir -p /temp/prod +COPY package.json bun.lockb /temp/prod/ +RUN cd /temp/prod && bun install --frozen-lockfile --production + +RUN cd /usr/src/app && ls + +# Copy node_modules from the temporary directory, then copy all project files into the image +FROM base AS prerelease +COPY --from=install /temp/dev/node_modules /usr/src/app/node_modules +COPY . /usr/src/app + +RUN cd /usr/src/app && ls + +# Copy production dependencies and source code into the final image +FROM base AS release +COPY --from=install /temp/prod/node_modules /usr/src/app/node_modules +COPY --from=prerelease /usr/src/app/ /usr/src/app/ + +RUN cd /usr/src/app && ls + +# Set the user to 'bun', expose the application's port, and define the entry point +USER bun +EXPOSE 3000/tcp +ENTRYPOINT ["bun", "run", "index.ts"] diff --git a/README.md b/README.md new file mode 100644 index 0000000..b4abed4 --- /dev/null +++ b/README.md @@ -0,0 +1,52 @@ +# Bun WebSockets + +A simple WebSocket server to get started using Bun. + +![](../../actions/workflows/ci.yml/badge.svg) + +[![](https://railway.app/button.svg)](https://railway.app/template/BLofAq?referralCode=bonus) + +## Prerequisites + +- [ Bun](https://bun.sh/) `>=1.0.0` + - [All-in-one toolkit](https://bun.sh/blog/bun-v1.0#bun-is-an-all-in-one-toolkit) + - [JavaScript runtime](https://bun.sh/blog/bun-v1.0#bun-is-a-javascript-runtime) + - [Package manager](https://bun.sh/blog/bun-v1.0#bun-is-a-package-manager) + - [Test runner](https://bun.sh/blog/bun-v1.0#bun-is-a-test-runner) + - [Bundler](https://bun.sh/blog/bun-v1.0#bun-is-a-bundler) + +## Getting Started + +1. **[Deploy on Railway](https://railway.app/template/BLofAq?referralCode=bonus)** or **[use this template](https://github.com/dayblox/bun-ws/generate)** + +2. **Clone** the repository + +3. **Install** dependencies + + ```sh + bun i + ``` + +## Usage + +- **Development** mode (**debug**) + + `F5` + +- Connecting to the Server using a WebSocket client + + ```sh + bunx wscat -c ws://localhost:3000 + ``` + +- Running **tests** (**watch** mode) + + ```sh + bun test --watch + ``` + +- **Production** mode + + ```sh + bun src/index.ts + ``` \ No newline at end of file diff --git a/biome.json b/biome.json new file mode 100644 index 0000000..5719e82 --- /dev/null +++ b/biome.json @@ -0,0 +1,8 @@ +{ + "files": { "ignore": ["node_modules/*"] }, + "javascript": { + "formatter": { + "semicolons": "asNeeded" + } + } +} diff --git a/bun.lockb b/bun.lockb new file mode 100755 index 0000000..7e70dfe Binary files /dev/null and b/bun.lockb differ diff --git a/index.ts b/index.ts new file mode 100644 index 0000000..5f1ea42 --- /dev/null +++ b/index.ts @@ -0,0 +1,3 @@ +import { app } from "./src"; + +app(); diff --git a/package.json b/package.json new file mode 100644 index 0000000..19ec976 --- /dev/null +++ b/package.json @@ -0,0 +1,17 @@ +{ + "name": "bun-ts", + "author": "dayblox", + "description": "Bun + TypeScript starter", + "devDependencies": { + "bun-types": "latest" + }, + "scripts": { + "start": "bun run src/index.ts" + }, + "dependencies": { + "@coral-xyz/anchor": "^0.29.0", + "@hxronetwork/dexterity-ts": "^1.6.33", + "@solana/web3.js": "^1.90.0", + "node-telegram-bot-api": "^0.64.0" + } +} diff --git a/src/api-utils/accountPositioningHandler.ts b/src/api-utils/accountPositioningHandler.ts new file mode 100644 index 0000000..5bce027 --- /dev/null +++ b/src/api-utils/accountPositioningHandler.ts @@ -0,0 +1,23 @@ +import dexterity, { Trader } from "@hxronetwork/dexterity-ts"; +import { TransactionInstruction } from "@solana/web3.js"; + +export const accountPositioningHandler = async ( + AppState: Map, + copiedTrader: Trader, + trader: Trader +) => { + +}; + +const tradeInfoHandler = async (trader: Trader) => { + +}; + +const copyInitialAccountHandler = async ( + trader: Trader, + traderPositions: Map, + copiedTraderPositions: Map, + positioningRatio: number +) => { + +}; diff --git a/src/api-utils/subscritionHandler.ts b/src/api-utils/subscritionHandler.ts new file mode 100644 index 0000000..11bab8f --- /dev/null +++ b/src/api-utils/subscritionHandler.ts @@ -0,0 +1,18 @@ +import { Manifest, Trader } from "@hxronetwork/dexterity-ts"; +import { PublicKey } from "@solana/web3.js"; +import { accountPositioningHandler } from "./accountPositioningHandler"; + +export const handleNewSubscription = async ( + trader: Trader, + manifest: Manifest, + newTrg: string | null, + AppState: Map +) => { + +}; + +export const handleCancelSubscription = async ( + AppState: Map +) => { + +}; diff --git a/src/api-utils/tradeHandler.ts b/src/api-utils/tradeHandler.ts new file mode 100644 index 0000000..4a27e41 --- /dev/null +++ b/src/api-utils/tradeHandler.ts @@ -0,0 +1,12 @@ +import dexterity, { Trader } from "@hxronetwork/dexterity-ts"; +import { sendTradeMessageToUser } from "../bot"; +import { Trade } from "./types"; + +export const tradeHandler = async ( + req: Request, + trader: Trader, + CopiedTrader: undefined | Trader, + AppState: Map +) => { + +}; diff --git a/src/api-utils/types.ts b/src/api-utils/types.ts new file mode 100644 index 0000000..b825ef8 --- /dev/null +++ b/src/api-utils/types.ts @@ -0,0 +1 @@ +export type Trade = {}; diff --git a/src/bot-utils/cancelTraderSubHandler.ts b/src/bot-utils/cancelTraderSubHandler.ts new file mode 100644 index 0000000..4f169cf --- /dev/null +++ b/src/bot-utils/cancelTraderSubHandler.ts @@ -0,0 +1,3 @@ +export async function cancelTraderSub() { + +} diff --git a/src/bot-utils/newTraderSubHandler.ts b/src/bot-utils/newTraderSubHandler.ts new file mode 100644 index 0000000..f10b9d2 --- /dev/null +++ b/src/bot-utils/newTraderSubHandler.ts @@ -0,0 +1,3 @@ +export async function newTraderSubHandler(trader: string) { + +} diff --git a/src/bot.ts b/src/bot.ts new file mode 100644 index 0000000..ea28c45 --- /dev/null +++ b/src/bot.ts @@ -0,0 +1,64 @@ +import TelegramBot from "node-telegram-bot-api"; +import { cancelTraderSub } from "./bot-utils/cancelTraderSubHandler"; +import { newTraderSubHandler } from "./bot-utils/newTraderSubHandler"; + +const token = ""; +const chatId = 1000000; + +const bot = new TelegramBot(token, { polling: true }); + +console.log("Bot is live"); + +bot.onText(/\/start/, (msg) => { + const welcomeMessage = `Welcome to the Trade Copy Bot! 🤖 Here's how you can get started: + + - Use /help to view all the commands. + - Use /newcopy {trg-pubkey} to subscribe to a new trader. + - Use /cancelcopy to cancel the subscription. + + Feel free to explore and let me know if you have any questions!`; + + bot.sendMessage(msg.chat.id, welcomeMessage); +}); + +bot.onText(/\/help/, (msg) => { + const helpMessage = `Here's how to use the bot commands: + + - Use /help to view all the commands. + - Use /newcopy {trg-pubkey} to subscribe to a new trader. + - Use /cancelcopy to cancel the subscription. + + If you have any questions or need further assistance, please reach out!`; + + bot.sendMessage(msg.chat.id, helpMessage); +}); + +bot.onText(/\/newcopy (.+)/, async (msg, match) => { + if (msg.chat.id !== chatId) return; + + if (!match || !match[1]) { + return bot.sendMessage( + chatId, + "Please provide the trader's TRG pubkey in the correct format: /newcopy {trg-pubkey}" + ); + } + + bot.sendMessage(chatId, "Subscribing to trader..."); + + // handle subscribe to trader + // send response to user +}); + +bot.onText(/\/cancelcopy/, async (msg, match) => { + if (msg.chat.id != chatId) return; + + if (!match) return bot.sendMessage(chatId, "Wrong format"); + + // handle cancel subscription + // send response to user +}); + +export const sendTradeMessageToUser = async (tradeInfo: any) => { + // parse tradeInfo into message + // send response to user +}; diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..b256425 --- /dev/null +++ b/src/index.ts @@ -0,0 +1,38 @@ +import { Wallet } from "@coral-xyz/anchor"; +import dexterity from "@hxronetwork/dexterity-ts"; +import { Keypair, PublicKey } from "@solana/web3.js"; +import { + handleCancelSubscription, + handleNewSubscription, +} from "./api-utils/subscritionHandler"; +import { tradeHandler } from "./api-utils/tradeHandler"; + +const AppState = new Map(); + +export const app = async () => { + const server = Bun.serve({ + async fetch(req, server) { + const url = new URL(req.url); + const { pathname, searchParams } = url; + + let response: Response | undefined = new Response( + JSON.stringify({ status: 200 }) + ); + + switch (pathname) { + case "/process-trade": + break; + case "/new-subscription": + break; + case "/cancel-subscription": + break; + default: + break; + } + + if (!response) return new Response(JSON.stringify({ status: 200 })); + return response; + }, + }); + console.log(`${server.url}`); +}; diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..319560f --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,20 @@ +{ + "compilerOptions": { + "allowImportingTsExtensions": true, + "allowJs": true, + "allowSyntheticDefaultImports": true, + "composite": true, + "downlevelIteration": true, + "forceConsistentCasingInFileNames": true, + "jsx": "react-jsx", + "lib": ["ESNext"], + "module": "ESNext", + "moduleDetection": "force", + "moduleResolution": "bundler", + "noEmit": true, + "skipLibCheck": true, + "strict": true, + "target": "ESNext", + "types": ["bun-types"] + } +}