import type { TaskContext } from "compose";
import { ASSET_PAIR, DURATION_SEC } from "../lib/constants";
import type { Market } from "../lib/types";
import { computeQuestionId, floorToMarketStart } from "../lib/utils";
import type { TaskPayload as LaunchPayload } from "./launch-market";
import type { TaskPayload as ResolvePayload } from "./resolve-market";
import type { ResponsePayload as PriceData } from "./market-data";
export async function main(context: TaskContext) {
const { collection, callTask } = context;
const nowMs = Date.now();
const currentMarketStart = floorToMarketStart(nowMs);
const markets = await collection<Market>("markets", [
{ path: "endTime", type: "numeric" },
{ path: "resolved", type: "boolean" },
]);
// One HTTP hit — used for BOTH the closePrice of the expiring market
// AND the openPrice of the new one (same 5-min boundary).
const { priceUsd } = await callTask<Record<string, never>, PriceData>(
"market_data",
{},
);
// Resolve any overdue, unresolved markets.
const overdue = await markets.findMany({
endTime: { $lte: nowMs },
resolved: false,
});
for (const market of overdue) {
const close = market.closePrice ?? priceUsd;
// Snapshot closePrice before the chain call so retries produce a
// deterministic outcome even if prices move between attempts.
if (market.closePrice === undefined) {
await markets.setById(market.questionId, { ...market, closePrice: close });
}
await callTask<ResolvePayload, Market>("resolve_market", {
market: { ...market, closePrice: close },
});
}
// Launch the market for the current 5-min bucket if it doesn't exist yet.
const currentQid = computeQuestionId({
assetPair: ASSET_PAIR,
durationSec: DURATION_SEC,
startTimestampSec: Math.floor(currentMarketStart / 1000),
});
if (!(await markets.getById(currentQid))) {
await callTask<LaunchPayload, Market>("launch_market", {
startTime: currentMarketStart,
openPrice: priceUsd,
});
}
}