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"]
+ }
+}