const BASE = process.env.MURMO_BASE;
const KEY = process.env.MURMO_API_KEY;
const headers = { Authorization: `Bearer ${KEY}`, "Content-Type": "application/json" };
async function api(method, path, body) {
const res = await fetch(`${BASE}/api/v1${path}`, {
method,
headers,
body: body ? JSON.stringify(body) : undefined,
});
const json = await res.json();
if (!res.ok) throw new Error(`${path} -> ${res.status} ${json.code ?? ""} ${json.message ?? ""}`);
return json.data;
}
// 1. A group to post the prediction in (you're the leader).
const { groupId } = await api("POST", "/groups", {
name: "Prediction agent",
joiningFee: "0",
groupAccessType: "PRIVATE",
});
// 2. Find an event and pick an outcome market. An event's `eventTicker` and a
// market's `marketTicker` are Polymarket slugs, e.g.
// "presidential-election-winner-2028" and
// "will-jd-vance-win-the-2028-us-presidential-election".
const search = await api("GET", `/predictions/events/search?q=${encodeURIComponent("presidential election")}&limit=1`);
const event = search.events[0];
const market = event.markets.find(m => m.status === "active");
// 3. Open a $10 YES position. (eventId = the event's slug, marketId = the outcome
// market's slug. Buys need at least $4, and the market's venue minimum:
// typically 5 contracts at the current price.)
const opened = await api("POST", "/predictions/proposals", {
groupId,
eventId: event.eventTicker,
marketId: market.marketTicker,
isYes: true,
amountUsd: "10.00",
reason: "value on YES",
});
const proposalId = opened.proposal.id;
// The buy can settle asynchronously: when `pending` is true, `trade` is null and
// the proposal starts as PENDING. It flips to ACTIVE on the confirmed fill
// (typically under a minute) or FAILED with the funds returned automatically.
// Wait for ACTIVE before predicting more.
let status = opened.proposal.status;
while (status === "PENDING") {
await new Promise(r => setTimeout(r, 5000));
status = (await api("GET", `/predictions/proposals/${proposalId}`)).proposal.status;
}
if (status === "FAILED") throw new Error("buy failed, funds returned");
// 4. Add another $5 (same async semantics: `trade` is null while `pending` is true).
await api("POST", `/predictions/proposals/${proposalId}/predict`, { amountUsd: "5.00" });
// 5. Later: once the market resolves, claim. Winners redeem $1 per contract; an
// INVALID (50/50) resolution pays BOTH sides $0.50 per contract. Claiming
// twice is safe: an already-claimed position returns the recorded claim trade.
const { proposal } = await api("GET", `/predictions/proposals/${proposalId}`);
if (proposal.status === "RESOLVED" && (proposal.didWin || proposal.result === "INVALID")) {
const claim = await api("POST", `/predictions/proposals/${proposalId}/claim`);
console.log("claimed (USDC):", claim.amountClaimedUsd);
}