# Project export: Solana Rivals

This document was generated by HackStack to give an AI agent context about a hackathon project. Sections are labeled with their provenance; content marked as truncated was cut to keep this document small.

## Project metadata

- Hackathon: TreeHacks 2025
- Tagline: Build, customize, and unleash your own gamified AI trading agent. Track whales, analyze markets, and climb the leaderboard to win epic rewards. Trade like an expert—earn like a champion!
- Devpost: https://devpost.com/software/solana-rivals
- GitHub: https://github.com/aneeshvathul/thbe
- Demo: https://github.com/tremanus/thfe
- Video: https://www.youtube.com/embed/p4OMFKMqyH8?enablejsapi=1&hl=en_US&rel=0&start=&version=3&wmode=transparent
- Team: 1 GitHub contributor(s) — Aneesh Vathul (4 commits)

## Devpost submission (written by the team)

### Overview

About the Project Our vision for Solana Rivals was born at the crossroads of frontiers: the rise of autonomous AI agents, the birth of decentralized markets and web3, and our shared belief in the power of gamification. As humanity has always pushed boundaries, we wanted to harness that same pioneering spirit. We merged cutting-edge tech with the thrill of competitive gaming to create a platform where trading strategies meet legendary playable avatars. How It Works Wallet Integration & Customization Connect: Users begin by connecting their Solana wallets using a sleek wallet adapter. Onboard & Customize: Once connected, they enter an interactive onboarding process where they tailor their agent’s trading behavior based on on-chain analysis and market analytics. Example: If you’re focused on tracking whale wallet activity, you can assign a higher weight to that factor—ensuring your agent primarily copies large-scale trades. Gamified Experience & Leaderboards Avatar Selection: Users select an avatar inspired by historic warriors such as Vikings, legionnaires, and samurais. Dynamic Leaderboard: Avatars are showcased on a real-time leaderboard that tracks key performance metrics like total profit/loss. Autonomous Agent Swarm Behind the scenes, our “brain” is a sophisticated AI orchestrator that coordinates with three specialized sub-agents: Market Trends Agent: Scrapes data from major exchanges to spot trending tokens. Whale Tracker Agent: Monitors influential whale wallets, analyzing trade frequencies and volumes. Data Cruncher Agent: Synthesizes market data and user parameters to provide actionable insights. These agents empower the main AI to autonomously decide when to buy or sell tokens, with a dedicated sell agent managing exits based on user-defined risk profiles. Competitive Challenges Squid Games Style Events: Selected participants have one week to optimize their agent. Winning Criteria: At the end of the competition, the top three agents—judged purely on raw profit—win a share of our ecosystem’s token pool. What Inspired Us We’re witnessing the golden age of crypto—an era where innovation is surging and the decentralized future is unfolding. With blockchain technology still in its infancy, now is the perfect time to push boundaries. Our childhood passion for games like NBA2K and Minecraft, where strategy and character design were paramount, naturally evolved into our vision for Solana Rivals. In this project, we merge the world of competitive gaming with the transformative power of blockchain technology, celebrating our adventurous past while forging a decentralized future. Technical Complexity & Innovation In just 36 hours, our team integrated multiple cutting-edge technologies: Backend: An Express Server (ts-node) wrapped in Docker, interfacing with the Solana and Helius APIs, as well as dexscreener and CoinGecko for real-time market data. Frontend: A Next.js web app styled with Tailwind and shadcn UI, integrated with the Solana Wallet Adapter for seamless wallet connectivity. Data Layer: A Supabase database for tracking performance metrics and storing historical trading data. The orchestration of our autonomous trading agent swarm—which dynamically weighs market trends, whale activities, and data insights—sets our project apart from typical trading bots. We even built dynamic graphics using DaVinci Resolve and dusted off our Photoshop skills to create compelling avatars and UI assets. Challenges & Learnings Building Solana Rivals wasn’t without hurdles. We faced: Rate Limits & Latency: Managing real-time data from multiple free APIs required careful throttling and optimization. Blockchain Nuances: Dealing with transaction slippage, parsing on-chain data, and managing blockchain delays pushed our full-stack skills to the limit. Iterative Improvement: Our early tests were rough—the agents made “dumb” trades that cost us test SOL. But through rapid prototyping and relentless tweaking, we steadily refined our algorithms, reducing losses and improving performance. These challenges deepened our expertise in the Web3 crypto space and taught us how to work effectively under pressure—even with a little sleep deprivation. Social Impact & Future Potential Solana Rivals is more than a game—it’s a sparkplug for a new era in decentralized finance. By fusing competitive gaming with AI-driven trading, our platform transforms the traditionally exclusive world of trading into an accessible, dynamic arena. Here’s how we’re making an impact and shaping the future: Empowering All Players: Whether you’re an experienced trader or a curious gamer, Solana Rivals democratizes trading strategies by providing every user with the tools to customize and create AI trading agents. Driving Financial Inclusion: By integrating advanced AI with decentralized finance, we’re turning opaque, elite trading practices into transparent, community-driven experiences—fostering innovation and encouraging broader market participation. Inspiring the Next Generation: Combining the thrill of historical heroism with cutting-edge technology, Solana Rivals channels our innate spirit of exploration, sparking creativity and strategic thinking essential for navigating the evolving digital asset landscape. Catalyzing Broader Change: Our platform is a blueprint for the future of finance—where complex financial tools are accessible to everyone. By breaking down traditional barriers, we pave the way for a more inclusive financial ecosystem that empowers individuals and fuels cross-industry innovation. Data-Driven Optimization: By collecting granular performance data from every trade, Solana Rivals continuously learns which agent settings yield success and which do not. This real-time feedback loop enables us to optimize trading parameters—ensuring our users benefit from ever-improving, empirically driven strategies. Solana Rivals isn’t just reshaping digital asset management—it’s redefining what’s possible in decentralized finance for generations to come. Final Thoughts In the spirit of exploration and innovation, Solana Rivals is our bold leap into the next era of blockchain-driven competitions. We’re thrilled to demo our project, share our journey, and discuss how our agent swarm could redefine trading on decentralized networks. Join us as we challenge the status quo and unlock a future where technology, creativity, and competition converge.

## README (from the GitHub repository)

No README available.

## Detected evidence (automated analysis)

Indexed codebase: 6 recognized source files, 54 KB.
- Express (technology) — detected in the code
- LangChain (technology) — detected in the code
- Supabase (technology) — detected in the code
- TypeScript (language) — detected in the code
- Docker (technology) — claimed on Devpost, not found in the code
- HTML (language) — claimed on Devpost, not found in the code
- Next.js (technology) — claimed on Devpost, not found in the code
- Node.js (technology) — claimed on Devpost, not found in the code
- OpenAI (technology) — claimed on Devpost, not found in the code
- React (technology) — claimed on Devpost, not found in the code
- Tailwind CSS (technology) — claimed on Devpost, not found in the code

## Codebase structure (from repository index)

### Files (16 of 16)

```
.DS_Store
backend/.gitignore
backend/data-collector/Dockerfile
backend/data-collector/package.json
backend/data-collector/src/server.ts
backend/data-collector/tsconfig.json
backend/docker-compose.yml
backend/package.json
backend/tsconfig.json
backend/worker-service/Dockerfile
backend/worker-service/package.json
backend/worker-service/src/AgentManager.ts
backend/worker-service/src/AgentTools.ts
backend/worker-service/src/server.ts
backend/worker-service/tsconfig.json
docker-compose.yml
```

### Dependencies

- backend/data-collector/package.json: @solana/web3.js@^1.87.6, @supabase/supabase-js@^2.39.3, @types/cors@^2.8.17, @types/express@^4.17.17, @types/node@^18.0.0, cors@^2.8.5, dotenv@^16.4.7, express@^4.18.2, ts-node-dev@^2.0.0, typescript@^4.9.5
- backend/package.json: @langchain/core@^0.3.40, @langchain/langgraph@^0.2.46, @langchain/openai@^0.4.4, @project-serum/anchor@^0.26.0, @solana/spl-token@^0.4.12, @solana/web3.js@^1.98.0, @supabase/supabase-js@^2.48.1, @types/cors@^2.8.17, @types/express@^5.0.0, @types/node@^18.15.11, bs58@^6.0.0, cors@^2.8.5, dotenv@^16.4.7, express@^4.21.2, ts-node-dev@^2.0.0, tweetnacl@^1.0.3, typescript@^5.0.4
- backend/worker-service/package.json: @langchain/core@^0.3.40, @langchain/langgraph@^0.2.46, @langchain/openai@^0.4.4, @project-serum/anchor@^0.26.0, @solana/spl-token@^0.3.9, @solana/web3.js@^1.87.6, @supabase/supabase-js@^2.48.1, @types/cors@^2.8.17, @types/express@^4.17.21, @types/node@^20.11.16, bs58@^5.0.0, cors@^2.8.5, dotenv@^16.4.1, express@^4.18.2, ts-node-dev@^2.0.0, typescript@^5.3.3

### Recent commits (newest first)

- silly
- ms
- another small fix
- small fix
- pushhh
- agent trading
- datacollector complete
- docker orchestration working

## Key source files (fetched from GitHub, selected and truncated for size)

### docker-compose.yml

```yaml
services:
  data-collector:
    build: 
      context: ./backend/data-collector
      dockerfile: Dockerfile
    ports:
      - "3001:3001"
    networks:
      - app-network
    dns:
      - 8.8.8.8
      - 8.8.4.4
    # ... rest of your config

  worker-service:
    build:
      context: ./backend/worker-service
      dockerfile: Dockerfile
    ports:
      - "3002:3002"
    depends_on:
      - data-collector
    networks:
      - app-network
    dns:
      - 8.8.8.8
      - 8.8.4.4
    # ... rest of your config 

networks:
  app-network:
    driver: bridge 
```

### backend/docker-compose.yml

```yaml
services:
  data-collector:
    build:
      context: ./data-collector
    ports:
      - "3001:3001"
    env_file: .env
    volumes:
      - ./data-collector:/app/data-collector
      - /app/data-collector/node_modules
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "node", "./healthcheck.js"]
      interval: 30s
      timeout: 10s
      retries: 3
    environment:
      - NODE_ENV=development

  worker-service:
    build:
      context: ./worker-service
    ports:
      - "3002:3002"
    env_file: .env
    volumes:
      - ./worker-service:/app/worker-service
      - /app/worker-service/node_modules
    depends_on:
      - data-collector
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "node", "./healthcheck.js"]
      interval: 30s
      timeout: 10s
      retries: 3
    environment:
      - NODE_ENV=development

networks:
  default:
    driver: bridge
```

### backend/package.json

```
{
    "name": "trading-system",
    "private": true,
    "workspaces": [
        "api-service",
        "worker-service",
        "data-collector"
    ],
    "scripts": {
        "build": "npm run build --workspaces",
        "start": "npm run start --workspaces"
    },
    "devDependencies": {
        "@types/cors": "^2.8.17",
        "@types/node": "^18.15.11",
        "ts-node-dev": "^2.0.0",
        "typescript": "^5.0.4"
    },
    "dependencies": {
        "@langchain/core": "^0.3.40",
        "@langchain/langgraph": "^0.2.46",
        "@langchain/openai": "^0.4.4",
        "@project-serum/anchor": "^0.26.0",
        "@solana/spl-token": "^0.4.12",
        "@solana/web3.js": "^1.98.0",
        "@supabase/supabase-js": "^2.48.1",
        "@types/express": "^5.0.0",
        "bs58": "^6.0.0",
        "cors": "^2.8.5",
        "dotenv": "^16.4.7",
        "express": "^4.21.2",
        "tweetnacl": "^1.0.3"
    }
}

```

### backend/data-collector/Dockerfile

```
FROM node:18

WORKDIR /app

COPY package*.json ./
RUN npm install

COPY . .
RUN npm run build

CMD ["npm", "start"] 
```

### backend/worker-service/Dockerfile

```
FROM node:18-alpine

WORKDIR /app

COPY package*.json ./
COPY tsconfig.json ./
RUN npm install

COPY src ./src

RUN npx tsc --project ./tsconfig.json

EXPOSE 3000
CMD ["npm", "start"] 
```

### backend/data-collector/package.json

```
{
    "name": "@trading-system/data-collector",
    "version": "1.0.0",
    "scripts": {
        "build": "tsc --project ./tsconfig.json",
        "start": "node dist/server.js",
        "dev": "ts-node-dev src/server.ts"
    },
    "dependencies": {
        "@solana/web3.js": "^1.87.6",
        "@supabase/supabase-js": "^2.39.3",
        "cors": "^2.8.5",
        "dotenv": "^16.4.7",
        "express": "^4.18.2"
    },
    "devDependencies": {
        "@types/cors": "^2.8.17",
        "@types/express": "^4.17.17",
        "@types/node": "^18.0.0",
        "ts-node-dev": "^2.0.0",
        "typescript": "^4.9.5"
    }
}

```

### backend/worker-service/package.json

```
{
    "name": "@trading-system/worker-service",
    "version": "1.0.0",
    "main": "dist/server.js",
    "scripts": {
        "build": "tsc",
        "start": "node dist/server.js",
        "dev": "ts-node-dev src/server.ts"
    },
    "dependencies": {
        "@langchain/core": "^0.3.40",
        "@langchain/langgraph": "^0.2.46",
        "@langchain/openai": "^0.4.4",
        "@project-serum/anchor": "^0.26.0",
        "@solana/spl-token": "^0.3.9",
        "@solana/web3.js": "^1.87.6",
        "@supabase/supabase-js": "^2.48.1",
        "bs58": "^5.0.0",
        "cors": "^2.8.5",
        "dotenv": "^16.4.1",
        "express": "^4.18.2"
    },
    "devDependencies": {
        "@types/cors": "^2.8.17",
        "@types/express": "^4.17.21",
        "@types/node": "^20.11.16",
        "ts-node-dev": "^2.0.0",
        "typescript": "^5.3.3"
    }
}

```

### backend/worker-service/src/server.ts

```typescript
import dotenv from 'dotenv';
import express from 'express';
import cors from 'cors';
import { AgentManager } from './AgentManager';
import { createClient } from '@supabase/supabase-js';

dotenv.config({ path: '../.env' });
// Validate required environment variables
if (!process.env.SUPABASE_URL || !process.env.SUPABASE_KEY) {
    throw new Error('Required environment variables SUPABASE_URL and SUPABASE_KEY must be set');
}

const app = express();
const agentManager = new AgentManager();

// Initialize Supabase client with environment variables
const supabase = createClient(
  process.env.SUPABASE_URL!,
  process.env.SUPABASE_KEY!
);

// Basic middleware
app.use(express.json());
app.use(cors());

// Health check endpoint
app.get('/health', (req: any, res: any) => {
  res.status(200).json({ status: 'healthy' });
});

app.post('/create-agent', async (req, res) => {
  const { userId } = req.body;
  const authHeader = req.headers.authorization;
  
  if (!authHeader) {
    return res.status(401).json({ 
      message: 'Unauthorized - no JWT token provided' 
    });
  }

  // Extract JWT token
  const token = authHeader.split('Bearer ')[1];
  
  try {
    // Verify the JWT token directly with Supabase
    const { data: { user }, error } = await supabase.auth.getUser(token);
    
    if (error) {
      return res.status(401).json({ 
        message: 'Unauthorized - invalid token',
        error: error.message
      });
    }

    if (!user || user.id !== userId) {
      return res.status(401).json({ 
        message: 'Unauthorized - user ID mismatch' 
      });
    }
    

    await agentManager.createAgent(userId);
    
    res.json({ 
      message: 'Agent created successfully',
      agentId: userId // or whatever ID your agentManager returns
    });
    
  } catch (error) {
    console.error('Error in create-agent route:', error);
    res.status(500).json({ 
      message: 'Internal server error',
      error: error instanceof Error ? error.message : String(error)
    });
  }
});

app.post('/stop-agent', (req, res) => {
  const { userId } = req.body;
  agentManager.stopAgent(userId);
  res.json({ message: 'Agent stopped' });
});

// // Start the interval printing
// const startInterval = () => {
//   setInterval(() => {
//     console.log('Server is running - checking in every 5 seconds!');
//   }, 5000);
// };

// Start server
const port = 3002; // Worker service port
app.listen(port, () => {
  console.log(`Agent server running on port ${port}`);
});
```

### backend/data-collector/src/server.ts

```typescript
import express from 'express';
import cors from 'cors';
import { createClient } from '@supabase/supabase-js';
import { Connection, PublicKey, LAMPORTS_PER_SOL } from "@solana/web3.js";
import dotenv from 'dotenv';
dotenv.config({ path: '../.env' });

const app = express();
const port = 3001; // Data collector service port
const RPC_URLS = process.env.RPC_URLS!.split(',');

// Create Supabase client
const supabase = createClient(
  process.env.SUPABASE_URL || '',
  process.env.SUPABASE_KEY || ''
);

// Allow all requests within Docker network but block external
app.use(cors({
    origin: true, // Allow all origins within Docker network
    methods: ['GET']
}));

// Interfaces
interface TokenBoost {
  url: string;
  chainId: string;
  tokenAddress: string;
  amount: number;
  totalAmount: number;
  icon: string;
  header: string;
  description: string;
  links: {
    type: string;
    label: string;
    url: string;
  }[];
}

interface PairInfo {
  chainId: string;
  dexId: string;
  url: string;
  pairAddress: string;
  pairCreatedAt: number;
  baseToken: {
    address: string;
    name: string;
    symbol: string;
  };
  quoteToken: {
    address: string;
    name: string;
    symbol: string;
  };
  priceNative: string;
  priceUsd: number;
  txns: { [key: string]: any };
  volume: {
    h24?: number;
    [key: string]: any;
  };
  priceChange: { [key: string]: any };
  liquidity: {
    usd?: number;
    base?: number;
    quote?: number;
  };
  fdv?: number;
  marketCap?: number;
  createdAt?: number;
  firstTxn?: number;
}

interface TokenInfoResponse {
  pairs: PairInfo[];
}

interface TokenData {
  token: TokenBoost;
  tokenInfo: TokenInfoResponse;
}

// Add new interfaces for whale tracking
interface WhaleTokenData {
  mint: string;
  frequency: number;
  totalVolume: number;
}

const WALLET_ADDRESSES = process.env.WHALE_ADDRESSES!.split(',');

// Add constants
const MIN_SOL_AMOUNT = 1.0;
const WRAPPED_SOL_MINT = "So11111111111111111111111111111111111111112";

const FILTERED_TOKENS = [
  WRAPPED_SOL_MINT,
  "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",  // USDC
  "Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB"   // USDT
];

// Add interfaces if not already present
interface TokenChange {
  mint: string;
  change: number;
}

interface TokenBalance {
  accountIndex: number;
  mint: string;
  uiTokenAmount: {
    uiAmount: number | null;
  };
}

interface TokenFrequency {
  mint: string;
  frequency: number;
  totalVolume: number;
  netAmount: number;
}

// Add the interfaces and function at the top with other interfaces
interface Risk {
  name: string;
  value: string;
  description: string;
  score: number;
  level: string;
}

interface RugcheckResponse {
  tokenProgram: string;
  tokenType: string;
  risks: Risk[];
  score: number;
}

// Database operations
async function clearTrendingTokenData() {
  try {
    const { error } = await supabase
      .from('trending_token_data')
      .delete()
      .neq('token_address', '');
    
    if (error) throw error;
  } catch (error) {
    throw error;
  }
}

async function insertTrendingTokenData(
  address: string,
  price: number | null,
  volume24h: number | null,
  liquidity: number | null,
  marketCap: number | null,
  lastHourBuys: number,
  lastHourSells: number,
  last5minBuys: number,
  last5minSells: number,
  description: string,
  m5PriceChange: number,
  h1PriceChange: number,
  h6PriceChange: number,
  h24PriceChange: number,
  ageInHours: number
) {
  try {
    const { error } = await supabase
      .from('trending_token_data')
      .upsert({
        token_address: address,
        price,
        volume_24_hr: volume24h,
        liquidity,
        market_cap: marketCap,
        last_hour_buys: lastHourBuys,
        last_hour_sells: lastHourSells,
        last_5min_buys: last5minBuys,
        last_5min_sells: last5minSells,
        description,
        '5min_price_change': m5PriceChange,
        '1hour_price_change': h1PriceChange,
        '6hour_price_change': h6PriceChange,
        '24hour_price_change': h24PriceChange,
        age_hours: ageInHours
      }, {
        onConflict: 'token_address'
      });

    if (error) throw error;
  } catch (error) {
    throw error;
  }
}

// Add database operation for whale tokens
async function clearWhaleTokenData() {
  try {
    const { error } = await supabase
      .from('whale_token_data')
      .delete()
      .neq('token_address', '');
    
    if (error) throw error;
  } catch (error) {
    throw error;
  }
}

async function insertWhaleTokenData(
  address: string,
  count: number,
  volume: number
) {
  try {
    const { error } = await supabase
      .from('whale_token_data')
      .upsert({
        token_address: address,
        count: count,
        volume: volume
      }, {
        onConflict: 'token_address'
      });

    if (error) throw error;
  } catch (error) {
    throw error;
  }
}

// Fetch Functions
async function fetchTrendingTokens(): Promise<TokenBoost[]> {
  const response = await fetch('https://api.dexscreener.com/token-boosts/top/v1');
  if (!response.ok) throw new Error(`Failed to fetch trending tokens: ${response.statusText}`);
  const tokens = await response.json();
  return tokens.filter((token: TokenBoost) => token.chainId === 'solana');
}

async function fetchLatestTokens(): Promise<TokenBoost[]> {
  const response = await fetch('https://api.dexscreener.com/token-boosts/latest/v1');
  if (!response.ok) throw new Error(`Failed to fetch latest tokens: ${response.statusText}`);
  const tokens = await response.json();
  return tokens.filter((token: TokenBoost) => token.chainId === 'solana');
}

async function getTokenInfo(tokenAddress: string): Promise<TokenInfoResponse> {
  const response = await fetch(`https://api.dexscreener.com/latest/dex/tokens/${tokenAddress}`);
  if (!response.ok) throw new Error(`Failed to fetch token info: ${response.statusText}`);
  return await response.json();
}

async function getAllTrendingTokensData(): Promise<TokenData[]>
[truncated — 10291 more characters]
```

### backend/worker-service/src/AgentTools.ts

```typescript
import { Connection, Keypair, PublicKey, VersionedTransaction, LAMPORTS_PER_SOL } from '@solana/web3.js';
import { Wallet } from '@project-serum/anchor';
import { TOKEN_PROGRAM_ID } from '@solana/spl-token';
import { Tool } from '@langchain/core/tools';
import bs58 from 'bs58';
import dotenv from 'dotenv';
import { getAssociatedTokenAddress } from '@solana/spl-token';

dotenv.config({ path: '../.env' });


interface DexPair {
    quoteToken: {
        symbol: string;
    };
    priceNative?: string;
}

const RPC_URLS = process.env.RPC_URLS!.split(',');

async function getTokenBalance(
    connection: Connection,
    wallet: Wallet,
    tokenMint: string
): Promise<number> {
    try {
        // Special handling for SOL
        if (tokenMint.toLowerCase() === "so11111111111111111111111111111111111111112") {
            try {
                console.log("Checking SOL balance for wallet:", wallet.publicKey.toString());
                const balance = await connection.getBalance(wallet.publicKey);
                console.log("Raw SOL balance:", balance);
                return balance / LAMPORTS_PER_SOL;
            } catch (solError) {
                console.error("Error getting SOL balance:", solError);
                throw solError; // Propagate the error instead of returning 0
            }
        }

        // For other tokens...
        const tokenAccount = await getAssociatedTokenAddress(
            new PublicKey(tokenMint),
            wallet.publicKey
        );

        try {
            const balance = await connection.getTokenAccountBalance(tokenAccount);
            return Number(balance.value.uiAmount);
        } catch (e) {
            // If token account doesn't exist, balance is 0
            return 0;
        }
    } catch (error) {
        console.error("Error getting token balance:", error);
        return 0;
    }
}

async function makeTrade(
    inputAddress: string,
    outputAddress: string,
    amount: number,
    slippage: number,
    wallet: Wallet
): Promise<void> {
    const connection = new Connection(RPC_URLS[Math.floor(Math.random() * RPC_URLS.length)], {
        commitment: 'confirmed',
        confirmTransactionInitialTimeout: 60000,
    });

    try {
        // Log initial balances
        console.log("\nInitial Balances:");
        console.log(`Input Token (${inputAddress}): ${await getTokenBalance(connection, wallet, inputAddress)}`);
        console.log(`Output Token (${outputAddress}): ${await getTokenBalance(connection, wallet, outputAddress)}\n`);

        // Get quote
        const quoteResponse = await fetch(
            `https://quote-api.jup.ag/v6/quote?inputMint=${inputAddress}&outputMint=${outputAddress}&amount=${amount}&slippageBps=${slippage}`
        );
        
        const quote = await quoteResponse.json();
        console.log("Quote received:", quote);

        if (quote.error) {
            throw new Error(`Invalid quote response: ${quote.error}`);
        }

        // Get swap transaction
        const swapResponse = await fetch('https://quote-api.jup.ag/v6/swap', {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({
                quoteResponse: quote,
                userPublicKey: wallet.publicKey.toString(),
                wrapAndUnwrapSol: true,
            })
        });

        const swapData = await swapResponse.json();
        console.log("Swap transaction received");

        // Sign and send transaction
        const swapTransaction = VersionedTransaction.deserialize(Buffer.from(swapData.swapTransaction, 'base64'));
        swapTransaction.sign([wallet.payer]);
        
        const txid = await connection.sendTransaction(swapTransaction, { maxRetries: 3 });
        console.log("Transaction sent:", txid);

        // Wait for confirmation with retries
        let retries = 3;
        while (retries > 0) {
            try {
                const confirmation = await connection.confirmTransaction(txid, 'confirmed');
                if (confirmation.value.err) {
                    throw new Error(`Transaction failed: ${confirmation.value.err}`);
                }
                console.log("Transaction confirmed");
                return;
            } catch (error) {
                retries--;
                if (retries === 0) throw error;
                console.log(`Retrying confirmation... ${retries} attempts left`);
                await new Promise(resolve => setTimeout(resolve, 2000)); // Wait 2 seconds between retries
            }
        }
    } catch (error) {
        throw error;
    }
}

/**
 * Gets the current price of a token in SOL given its contract address
 * @param tokenAddress The contract address of the token
 * @returns The current price in SOL as a decimal number, or null if price cannot be fetched
 */
async function getPrice(tokenAddress: string): Promise<number | null> {
    try {
        // DexScreener API endpoint
        const url = `https://api.dexscreener.com/latest/dex/tokens/${tokenAddress}`;
        const response = await fetch(url);
        
        if (!response.ok) {
            throw new Error(`HTTP error! Status: ${response.status}`);
        }
        
        const data = await response.json();
        
        if (!data?.pairs || data.pairs.length === 0) {
            return null;
        }

        // Find a SOL pair if available
        const solPair = data.pairs.find((pair: DexPair) => 
            pair.quoteToken.symbol === 'SOL' || 
            pair.quoteToken.symbol === 'WSOL'
        );
        
        if (solPair) {
            return solPair.priceNative ? parseFloat(solPair.priceNative) : null;
        }
        
        // If no SOL pair, get USD price and convert using SOL price
        const usdPair = data.pairs[0];
        if (!usdPair.priceUsd) {
            return null;
        }
        
        // Fetch SOL price in USD
        const solResponse = await fetch('https://api.dexscreener.co
[truncated — 1481 more characters]
```

[1 more indexed source files omitted to keep this export small. The full file list is in the Codebase structure section above.]