Deploying an on-chain bot that doesn't rely on an application server

Hi!
I am trying to deploy an onchain bot which is a typescript app, but it doesnt use a server. I tried referring to your existing docs, but it does not have any helpful info. The script looks like this:

/* eslint-disable no-undef */
import { LiquidationStateTracker } from "../bot-classes/viem-reader";
import { PlatformHandler } from "../bot-classes/viem-manager-sdk";
import { wagmiContractConfig } from "../constants";
import { avalanche } from "viem/chains";
import { Address, Hex } from "viem";
import * as envEnc from "@chainlink/env-enc";
envEnc.config();

// Configuration
const RPC_URL = process.env.AVAX_RPC_URL!;
const privateKey = process.env.PRIVATE_KEY!;
const INTERVAL_MS = 2500;
const MAX_RETRIES = 2;
const RETRY_DELAY = 800;

// Define assets to monitor
const ASSETS = {
  bitcon: "0xe62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43" as Hex,
  ethereum:
    "0xff61491a931112ddf1bd8147cd1b641375f79f5825126d665480874634fd0ace" as Hex,
  avalanche:
    "0x93da3352f9f1d105fdfe4971cfa80e9dd777bfc5d0f683ebb6e1294b92137bb7" as Hex,
  solana: "0xef0d8b6fda2ceba41da15d4095d1da392a0d2f8ed0c6c7bc0f4cfac8c280b56d" as Hex,
  ripple: "0xec5d399846a9209f3fe5881d70aae9268c94339ff9817e8d18ff19fa05eea1c8" as Hex,
  binance: "0x2f95862b045670cd22bee3114c39763a4a08beeb663b145d283c31d7d1101c4f" as Hex,
  euro: "0xa995d00bb36a63cef7fd2c287dc105fc8f3d93779f062f09551b0af3e81ec30b" as Hex,
  pounds: "0x84c2dde9633d93d1bcad84e7dc41c9d56578b7ec52fabedc1f335d673df0a7c1" as Hex,
  yen: "0xef2c98c804ba503c6a707e38be4dfbb16683775f195b091252bf24693042fd52" as Hex,
  gold: "0x765d2ba906dbc32ca17cc11f5310a89e9ee1f6420508c63861f2f8ba4ee34bb2" as Hex,
  silver: "0xf2fb02c32b055c805e7238d628e5e9dadef274376114eb1f012337cabe93871e" as Hex,
  us_oil: "0x925ca92ff005ae943c158e3563f59698ce7e75c5a8c8dd43303a0a154887b3e6" as Hex
};

// Track timing for each asset
const lastCycleTimes: Record<string, number> = Object.keys(ASSETS).reduce(
  (acc, asset) => ({
    ...acc,
    [asset]: Date.now(),
  }),
  {}
);

// Initialize handlers (moved inside function to prevent memory leaks)
let liquidationStateTracker: LiquidationStateTracker | null = null;
let platformHandler: PlatformHandler | null = null;
let isMonitoring = false;
let intervalId: NodeJS.Timeout | null = null;

// Helper function to initialize handlers
function initializeHandlers() {
  if (!liquidationStateTracker || !platformHandler) {
    liquidationStateTracker = new LiquidationStateTracker(
      RPC_URL,
      wagmiContractConfig.marketRegistry.address as Address,
      avalanche
    );

    platformHandler = new PlatformHandler(
      wagmiContractConfig.marketRegistry.address as Address,
      avalanche,
      RPC_URL,
      privateKey
    );
  }
}

// Retry mechanism with exponential backoff
async function retryOperation<T>(
  operation: () => Promise<T>,
  retries: number = MAX_RETRIES
): Promise<T> {
  for (let i = 0; i < retries; i++) {
    try {
      return await operation();
    } catch (error) {
      if (i === retries - 1) throw error;
      await new Promise((resolve) =>
        setTimeout(resolve, RETRY_DELAY * Math.pow(2, i))
      );
    }
  }
  throw new Error("Operation failed after retries");
}

async function handlePositions(
  positions: string[],
  handler: PlatformHandler,
  pricefeedId: Hex,
  assetName: string
): Promise<void> {
  if (!positions.length) return;

  try {
    const results = await retryOperation(() =>
      handler.liquidatePositionsByIds(positions, pricefeedId)
    );

    const summary = handler.getBatchLiquidationSummary(results);
    console.log(
      `[${assetName}] Successfully liquidated ${summary.totalSuccessful} positions`
    );
  } catch (error) {
    console.error(`[${assetName}] position error:`, error);
  }
}

async function checkAndLiquidateAsset(
  tracker: LiquidationStateTracker,
  handler: PlatformHandler,
  pricefeedId: Hex,
  assetName: string
): Promise<void> {
  try {
    const roguePositionsResult = await tracker.getLiquidatableIds(pricefeedId);
    
    if (roguePositionsResult.length > 0) {
      await handlePositions(roguePositionsResult, handler, pricefeedId, assetName);
    }
  } catch (error) {
    console.error(`[${assetName}] Cycle error:`, error);
  }
}

async function checkAndLiquidateAll(
  tracker: LiquidationStateTracker,
  handler: PlatformHandler
): Promise<void> {
  try {
    await Promise.allSettled(
      Object.entries(ASSETS).map(async ([assetName, pricefeedId]) => {
        const currentTime = Date.now();
        const timeSinceLastCycle = currentTime - lastCycleTimes[assetName];
        const timeString = new Date().toLocaleTimeString("en-GB");
        console.log(
          `[${timeString}][${assetName}] Time since last cycle: ${timeSinceLastCycle}ms`
        );
        lastCycleTimes[assetName] = currentTime;

        return checkAndLiquidateAsset(tracker, handler, pricefeedId, assetName);
      })
    );
  } catch (error) {
    console.error("Error in main liquidation cycle:", error);
  }
}

function cleanup(): void {
  isMonitoring = false;
  if (intervalId) {
    clearInterval(intervalId);
    intervalId = null;
  }
  liquidationStateTracker = null;
  platformHandler = null;
}

async function startMonitoring(): Promise<void> {
  console.log({ isMonitoring });
  if (isMonitoring) return;

  try {
    isMonitoring = true;
    initializeHandlers();

    if (!liquidationStateTracker || !platformHandler) {
      throw new Error("Failed to initialize handlers");
    }

    console.log("Starting multi-asset monitoring...");
    console.log("Monitoring assets:", Object.keys(ASSETS).join(", "));

    const runCycle = () => {
      if (!isMonitoring || !liquidationStateTracker || !platformHandler) return;
      checkAndLiquidateAll(liquidationStateTracker, platformHandler).catch(
        console.error
      );
    };

    runCycle();
    intervalId = setInterval(runCycle, INTERVAL_MS);

    process.on("SIGINT", cleanup);
    process.on("SIGTERM", cleanup);
    process.on("uncaughtException", (error) => {
      console.error("Uncaught exception:", error);
      cleanup();
    });
  } catch (error) {
    console.error("Monitoring error:", error);
    cleanup();
  }
}

// Start monitoring
startMonitoring();

Build a Dockerfile that runs your script. Get the Dockerfile working on your machine. Then try:

fly machine run .