@continuumdao/ctm-mpc-defi 0.2.50 → 0.2.51

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/dist/agent/catalog.cjs +770 -28
  2. package/dist/agent/catalog.cjs.map +1 -1
  3. package/dist/agent/catalog.d.ts +6 -1
  4. package/dist/agent/catalog.js +770 -28
  5. package/dist/agent/catalog.js.map +1 -1
  6. package/dist/agent/skills/continuum-dao/SKILL.md +2 -0
  7. package/dist/agent/skills/continuum-dao-marketplace/SKILL.md +58 -0
  8. package/dist/agent/skills/hyperliquidOutcome/SKILL.md +47 -0
  9. package/dist/core/index.cjs +2 -1
  10. package/dist/core/index.cjs.map +1 -1
  11. package/dist/core/index.js +2 -1
  12. package/dist/core/index.js.map +1 -1
  13. package/dist/index.cjs +2 -1
  14. package/dist/index.cjs.map +1 -1
  15. package/dist/index.js +2 -1
  16. package/dist/index.js.map +1 -1
  17. package/dist/protocols/evm/aerodrome/index.cjs.map +1 -1
  18. package/dist/protocols/evm/aerodrome/index.js.map +1 -1
  19. package/dist/protocols/evm/continuum-dao/index.cjs +2274 -59
  20. package/dist/protocols/evm/continuum-dao/index.cjs.map +1 -1
  21. package/dist/protocols/evm/continuum-dao/index.d.ts +690 -2
  22. package/dist/protocols/evm/continuum-dao/index.js +2218 -61
  23. package/dist/protocols/evm/continuum-dao/index.js.map +1 -1
  24. package/dist/protocols/evm/hyperliquidOutcome/index.cjs +1294 -0
  25. package/dist/protocols/evm/hyperliquidOutcome/index.cjs.map +1 -0
  26. package/dist/protocols/evm/hyperliquidOutcome/index.d.ts +465 -0
  27. package/dist/protocols/evm/hyperliquidOutcome/index.js +1247 -0
  28. package/dist/protocols/evm/hyperliquidOutcome/index.js.map +1 -0
  29. package/dist/protocols/evm/pendle/index.cjs +2 -1
  30. package/dist/protocols/evm/pendle/index.cjs.map +1 -1
  31. package/dist/protocols/evm/pendle/index.js +2 -1
  32. package/dist/protocols/evm/pendle/index.js.map +1 -1
  33. package/dist/protocols/evm/trueo/index.cjs +569 -9
  34. package/dist/protocols/evm/trueo/index.cjs.map +1 -1
  35. package/dist/protocols/evm/trueo/index.d.ts +42 -1
  36. package/dist/protocols/evm/trueo/index.js +569 -9
  37. package/dist/protocols/evm/trueo/index.js.map +1 -1
  38. package/package.json +6 -1
@@ -1104,6 +1104,506 @@ function suggestTrueoTokensToAdd(args) {
1104
1104
  }
1105
1105
  }
1106
1106
 
1107
+ // src/protocols/evm/hyperliquid/support.ts
1108
+ var HYPERLIQUID_SUPPORTED_CHAIN_IDS = [999, 998];
1109
+ function isHyperliquidChainSupported(chainId) {
1110
+ return HYPERLIQUID_SUPPORTED_CHAIN_IDS.includes(chainId);
1111
+ }
1112
+ function hyperliquidApiBaseUrl(chainId) {
1113
+ return chainId === 998 ? "https://api.hyperliquid-testnet.xyz" : "https://api.hyperliquid.xyz";
1114
+ }
1115
+
1116
+ // src/protocols/evm/hyperliquidOutcome/model.ts
1117
+ function outcomeEncoding(outcome, side) {
1118
+ const o = Math.trunc(outcome);
1119
+ const s = Math.trunc(side);
1120
+ if (!Number.isFinite(o) || o < 0) throw new Error("outcome must be a non-negative integer");
1121
+ if (s !== 0 && s !== 1) throw new Error("side must be 0 or 1");
1122
+ return 10 * o + s;
1123
+ }
1124
+ function outcomeTradeCoin(encoding) {
1125
+ return `#${encoding}`;
1126
+ }
1127
+ function outcomeBalanceCoin(encoding) {
1128
+ return `+${encoding}`;
1129
+ }
1130
+ function outcomeAssetId(encoding) {
1131
+ return 1e8 + encoding;
1132
+ }
1133
+ function outcomeSideName(name, fallback) {
1134
+ const raw = (name ?? "").trim();
1135
+ const stripped = raw.replace(/^template:/i, "").trim();
1136
+ return stripped || fallback;
1137
+ }
1138
+ function parseHyperliquidPipeFields(description) {
1139
+ const out = {};
1140
+ for (const part of (description ?? "").split("|")) {
1141
+ const i = part.indexOf(":");
1142
+ if (i <= 0) continue;
1143
+ const key = part.slice(0, i).trim();
1144
+ const value = part.slice(i + 1).trim();
1145
+ if (key && value) out[key] = value;
1146
+ }
1147
+ return out;
1148
+ }
1149
+ function parseHyperliquidStamp(raw) {
1150
+ const m = /^(\d{4})(\d{2})(\d{2})-(\d{2})(\d{2})$/.exec((raw ?? "").trim());
1151
+ if (!m) return null;
1152
+ const ms = Date.parse(`${m[1]}-${m[2]}-${m[3]}T${m[4]}:${m[5]}:00Z`);
1153
+ return Number.isFinite(ms) ? Math.floor(ms / 1e3) : null;
1154
+ }
1155
+ function formatHyperliquidStamp(unixSec) {
1156
+ if (unixSec == null || !Number.isFinite(unixSec) || unixSec <= 0) return null;
1157
+ const d = new Date(unixSec * 1e3);
1158
+ if (Number.isNaN(d.getTime())) return null;
1159
+ const pad = (n) => String(n).padStart(2, "0");
1160
+ return `${d.getUTCFullYear()}-${pad(d.getUTCMonth() + 1)}-${pad(d.getUTCDate())} ${pad(d.getUTCHours())}:${pad(d.getUTCMinutes())} UTC`;
1161
+ }
1162
+ function questionForOutcome(outcomeId, questions) {
1163
+ for (const q of questions) {
1164
+ if (q.fallbackOutcome === outcomeId) return q;
1165
+ if ((q.namedOutcomes ?? []).includes(outcomeId)) return q;
1166
+ }
1167
+ return void 0;
1168
+ }
1169
+ function formatOutcomeQuestion(outcome, question) {
1170
+ const fields = parseHyperliquidPipeFields(outcome.description);
1171
+ const qFields = parseHyperliquidPipeFields(question?.description);
1172
+ const when = formatHyperliquidStamp(
1173
+ parseHyperliquidStamp(fields.time) ?? parseHyperliquidStamp(qFields.resolutionDeadline)
1174
+ );
1175
+ const whenSuffix = when ? ` at ${when}` : "";
1176
+ if (outcome.name === "template:binaryPrice") {
1177
+ const asset = fields.priceDescription || fields.perp || "the asset";
1178
+ const threshold = fields.threshold ?? "";
1179
+ return `Will ${asset} be above ${threshold}${whenSuffix}?`.replace(/\s+/g, " ").trim();
1180
+ }
1181
+ if (outcome.name === "template:priceTouch") {
1182
+ const asset = fields.priceDescription || fields.perp || "the asset";
1183
+ const target = fields.target ?? "";
1184
+ return `Will ${asset} touch ${target}${when ? ` by ${when}` : ""}?`.replace(/\s+/g, " ").trim();
1185
+ }
1186
+ if (outcome.name === "template:sportsTournamentParticipant") {
1187
+ const participant = fields.participant || "this participant";
1188
+ const competition = qFields.competition;
1189
+ return competition ? `Will ${participant} win ${competition}?` : `Will ${participant} win?`;
1190
+ }
1191
+ const desc = (outcome.description ?? "").trim();
1192
+ if (desc && desc !== "other") return desc;
1193
+ return outcome.name || `Outcome ${outcome.outcome}`;
1194
+ }
1195
+ var STAMP_FIELDS = /* @__PURE__ */ new Set([
1196
+ "time",
1197
+ "dateTime",
1198
+ "resolutionDeadline",
1199
+ "scheduledStart",
1200
+ "scheduledDecision",
1201
+ "expiry",
1202
+ "decisionDeadline"
1203
+ ]);
1204
+ function outcomeTemplateId(name) {
1205
+ return (name ?? "").replace(/^template:/, "").trim();
1206
+ }
1207
+ function outcomeFieldMap(outcome, question) {
1208
+ return {
1209
+ ...parseHyperliquidPipeFields(question?.description),
1210
+ ...parseHyperliquidPipeFields(outcome.description)
1211
+ };
1212
+ }
1213
+ function displayField(key, raw) {
1214
+ if (STAMP_FIELDS.has(key)) {
1215
+ const formatted = formatHyperliquidStamp(parseHyperliquidStamp(raw));
1216
+ if (formatted) return formatted;
1217
+ }
1218
+ return raw.trim();
1219
+ }
1220
+ function fillOutcomeTemplate(text, fields) {
1221
+ return text.replace(/\{([A-Za-z0-9_]+)\}/g, (token, key) => {
1222
+ const raw = fields[key];
1223
+ if (raw == null || !raw.trim()) return token;
1224
+ return displayField(key, raw);
1225
+ });
1226
+ }
1227
+ function stripTemplateMetadata(text) {
1228
+ return (text.split(/\smetadata=/)[0] ?? text).replace(/\bprice price\b/g, "price").trim();
1229
+ }
1230
+ function templateById(templates, id) {
1231
+ return templates?.find((template) => template.id === id);
1232
+ }
1233
+ function parentTemplateId(template) {
1234
+ const role = template?.role;
1235
+ if (!role || typeof role !== "object") return null;
1236
+ const parent = role.questionOutcome?.parent?.trim();
1237
+ return parent || null;
1238
+ }
1239
+ function resolveOutcomeSideLabel(specName, fields, fallback) {
1240
+ const raw = outcomeSideName(specName, fallback);
1241
+ const filled = fillOutcomeTemplate(raw, fields).trim();
1242
+ return filled || fallback;
1243
+ }
1244
+ function recurringRules(outcome, fields) {
1245
+ const when = fields.expiry ? displayField("expiry", fields.expiry) : "expiry";
1246
+ const asset = fields.underlying || "the asset";
1247
+ if (outcome.name === "Recurring" && fields.class === "priceBinary") {
1248
+ const period = fields.period ? ` Period ${fields.period}.` : "";
1249
+ return `Yes if the ${asset} HyperCore mark at ${when} is at or above ${fields.targetPrice ?? "\u2014"}. Otherwise No. The mark is the linear interpolation of the HyperCore mark updates immediately before and after that time.${period}`;
1250
+ }
1251
+ if (outcome.name === "Recurring Fallback") {
1252
+ return `Yes if none of the named ${asset} price buckets contain the mark at ${when}. Otherwise No. Thresholds: ${fields.priceThresholds || "\u2014"}.`;
1253
+ }
1254
+ if (outcome.name === "Recurring Named Outcome") {
1255
+ return `Yes if bucket ${fields.index ?? "\u2014"} is the ${asset} price range that contains the interpolated HyperCore mark at ${when}. Otherwise No. Thresholds: ${fields.priceThresholds || "\u2014"}.`;
1256
+ }
1257
+ return null;
1258
+ }
1259
+ function fallbackRules(outcome, fields) {
1260
+ const id = outcomeTemplateId(outcome.name);
1261
+ const when = fields.time ? displayField("time", fields.time) : "the settlement time";
1262
+ const feed = fields.priceDescription || fields.perp || "the price";
1263
+ if (id === "binaryPrice") {
1264
+ const twap = fields.seconds ? ` Settlement uses the ${fields.seconds}-second TWAP of ${feed} ending at ${when}.` : "";
1265
+ return `Yes if the ${fields.perp || feed} price is above ${fields.threshold ?? "\u2014"} at ${when}. Otherwise No.${twap}`.replace(
1266
+ /\bprice price\b/g,
1267
+ "price"
1268
+ );
1269
+ }
1270
+ if (id === "priceTouch") {
1271
+ return `Yes if the ${fields.perp || feed} price touches ${fields.target ?? "\u2014"} at or before ${when}. Otherwise No. A touch is the ${fields.seconds || "\u2014"}-second TWAP of ${feed} equaling or crossing the target.`;
1272
+ }
1273
+ if (id === "companyIpoConfirmed") {
1274
+ const at = fields.dateTime ? displayField("dateTime", fields.dateTime) : "the deadline";
1275
+ return `Yes if ${fields.company || "the company"}'s first public listing is confirmed at or before ${at}. Otherwise No.`;
1276
+ }
1277
+ if (id === "sportsTournamentParticipant" || id === "sportsContestParticipant2") {
1278
+ return `Yes if ${fields.participant || "this participant"} is the sole winner. Otherwise No.`;
1279
+ }
1280
+ if (id === "sportsContestDraw2") return "Yes if the contest is a draw. Otherwise No.";
1281
+ if (id === "policyRateNoChange") return "Yes if the announced policy value equals the prior value. Otherwise No.";
1282
+ if (id === "policyRateDecrease") return "Yes if the announced policy value is lower than the prior value. Otherwise No.";
1283
+ if (id === "policyRateIncrease") return "Yes if the announced policy value is higher than the prior value. Otherwise No.";
1284
+ if (id === "sportsContestWinner") {
1285
+ const a = fields.shortNameA || fields.participantA || "Side A";
1286
+ const b = fields.shortNameB || fields.participantB || "Side B";
1287
+ return `${a} wins if ${fields.participantA || a} wins the contest. ${b} wins if ${fields.participantB || b} wins. Counted play: ${fields.countedPlay || "the scheduled contest"}.`;
1288
+ }
1289
+ return "";
1290
+ }
1291
+ function formatOutcomeRules(outcome, question, templates) {
1292
+ const fields = outcomeFieldMap(outcome, question);
1293
+ const recurring = recurringRules(outcome, fields);
1294
+ if (recurring) return recurring;
1295
+ const template = templateById(templates, outcomeTemplateId(outcome.name));
1296
+ const ownRaw = template?.description?.trim();
1297
+ const own = ownRaw ? stripTemplateMetadata(fillOutcomeTemplate(ownRaw, fields)) : fallbackRules(outcome, fields);
1298
+ const parentRaw = templateById(templates, parentTemplateId(template) ?? "")?.description?.trim();
1299
+ const parent = parentRaw ? stripTemplateMetadata(fillOutcomeTemplate(parentRaw, fields)) : "";
1300
+ if (own && parent) return `${own}
1301
+
1302
+ ${parent}`;
1303
+ return own;
1304
+ }
1305
+ function outcomeExpiryUnix(outcome, question) {
1306
+ const fields = parseHyperliquidPipeFields(outcome.description);
1307
+ const qFields = parseHyperliquidPipeFields(question?.description);
1308
+ return parseHyperliquidStamp(fields.time) ?? parseHyperliquidStamp(qFields.resolutionDeadline) ?? 0;
1309
+ }
1310
+
1311
+ // src/protocols/evm/hyperliquidOutcome/api.ts
1312
+ var HYPERLIQUID_OUTCOME_MAINNET_CHAIN_ID = 999;
1313
+ var META_TTL_MS = 15e3;
1314
+ var metaCache = /* @__PURE__ */ new Map();
1315
+ function outcomeFetchFailure(error) {
1316
+ if (!(error instanceof Error)) return "request failed";
1317
+ const cause = error.cause;
1318
+ if (cause instanceof Error && cause.message.trim()) return cause.message;
1319
+ return error.message;
1320
+ }
1321
+ function outcomeErrorCode(error) {
1322
+ if (!error || typeof error !== "object") return "";
1323
+ if ("code" in error && error.code != null) return String(error.code);
1324
+ if (error instanceof Error && error.cause && typeof error.cause === "object" && "code" in error.cause) {
1325
+ return String(error.cause.code ?? "");
1326
+ }
1327
+ return "";
1328
+ }
1329
+ var OUTCOME_RETRYABLE_CODES = /* @__PURE__ */ new Set([
1330
+ "UND_ERR_CONNECT_TIMEOUT",
1331
+ "UND_ERR_CONNECT",
1332
+ "ECONNRESET",
1333
+ "ECONNREFUSED",
1334
+ "ETIMEDOUT",
1335
+ "ENOTFOUND",
1336
+ "EAI_AGAIN",
1337
+ "EPIPE"
1338
+ ]);
1339
+ function outcomeFetchRetryable(error) {
1340
+ if (error && typeof error === "object" && "outcomeFinal" in error) return false;
1341
+ const code = outcomeErrorCode(error);
1342
+ return OUTCOME_RETRYABLE_CODES.has(code) || error instanceof Error && (error.message === "fetch failed" || error.message === "connect timeout");
1343
+ }
1344
+ var outcomeHttpsAgent = null;
1345
+ async function outcomeNodeModules() {
1346
+ const importer = new Function("specifier", "return import(specifier)");
1347
+ const [http2, https] = await Promise.all([importer("node:http"), importer("node:https")]);
1348
+ return { http: http2, https };
1349
+ }
1350
+ async function outcomeNodePost(urlString, payload) {
1351
+ const { http: http2, https } = await outcomeNodeModules();
1352
+ if (!outcomeHttpsAgent) {
1353
+ outcomeHttpsAgent = new https.Agent({ keepAlive: true, maxSockets: 4, timeout: 2e4 });
1354
+ }
1355
+ const url = new URL(urlString);
1356
+ const lib = url.protocol === "http:" ? http2 : https;
1357
+ return new Promise((resolve, reject) => {
1358
+ const req = lib.request(
1359
+ {
1360
+ protocol: url.protocol,
1361
+ hostname: url.hostname,
1362
+ port: url.port || void 0,
1363
+ path: `${url.pathname}${url.search}`,
1364
+ method: "POST",
1365
+ headers: {
1366
+ "Content-Type": "application/json",
1367
+ "Content-Length": Buffer.byteLength(payload)
1368
+ },
1369
+ agent: url.protocol === "https:" ? outcomeHttpsAgent ?? void 0 : void 0,
1370
+ timeout: 2e4
1371
+ },
1372
+ (res) => {
1373
+ const chunks = [];
1374
+ res.on("data", (chunk) => chunks.push(chunk));
1375
+ res.on("end", () => {
1376
+ resolve({ status: res.statusCode ?? 0, text: Buffer.concat(chunks).toString("utf8") });
1377
+ });
1378
+ }
1379
+ );
1380
+ req.on("timeout", () => {
1381
+ const error = new Error("connect timeout");
1382
+ error.code = "ETIMEDOUT";
1383
+ req.destroy(error);
1384
+ });
1385
+ req.on("error", reject);
1386
+ req.write(payload);
1387
+ req.end();
1388
+ });
1389
+ }
1390
+ async function outcomeFetchPost(url, payload) {
1391
+ const res = await fetch(url, {
1392
+ method: "POST",
1393
+ headers: { "Content-Type": "application/json" },
1394
+ body: payload,
1395
+ cache: "no-store"
1396
+ });
1397
+ return { status: res.status, text: await res.text() };
1398
+ }
1399
+ async function outcomeInfoPost(chainId, body) {
1400
+ if (!isHyperliquidChainSupported(chainId)) {
1401
+ throw new Error(`Hyperliquid outcome markets require chainId 999 or 998 (got ${chainId}).`);
1402
+ }
1403
+ const url = `${hyperliquidApiBaseUrl(chainId)}/info`;
1404
+ const payload = JSON.stringify(body);
1405
+ let last;
1406
+ for (let attempt = 0; attempt < 3; attempt++) {
1407
+ try {
1408
+ const res = typeof window !== "undefined" || process.env.VITEST ? await outcomeFetchPost(url, payload) : await outcomeNodePost(url, payload);
1409
+ if (res.status < 200 || res.status >= 300) {
1410
+ const error = new Error(`Hyperliquid info API failed (${res.status}): ${res.text.slice(0, 200)}`);
1411
+ if (res.status !== 429 && res.status !== 502 && res.status !== 503 && res.status !== 504) {
1412
+ error.outcomeFinal = true;
1413
+ }
1414
+ throw error;
1415
+ }
1416
+ return JSON.parse(res.text);
1417
+ } catch (error) {
1418
+ last = error;
1419
+ if (!outcomeFetchRetryable(error) || attempt === 2) break;
1420
+ await new Promise((resolve) => setTimeout(resolve, 250 * (attempt + 1)));
1421
+ }
1422
+ }
1423
+ throw new Error(`Hyperliquid info API failed: ${outcomeFetchFailure(last)}`);
1424
+ }
1425
+ function priceNumber(raw) {
1426
+ if (raw == null || raw === "") return null;
1427
+ const n = Number.parseFloat(raw);
1428
+ return Number.isFinite(n) ? n : null;
1429
+ }
1430
+ function priceLabel(n) {
1431
+ if (n == null) return null;
1432
+ if (n >= 0 && n <= 1) return `${(n * 100).toFixed(1)}%`;
1433
+ return String(n);
1434
+ }
1435
+ function marketFromMeta(outcome, question, mids, nowSec, templates) {
1436
+ const fields = outcomeFieldMap(outcome, question);
1437
+ const yesEncoding = outcomeEncoding(outcome.outcome, 0);
1438
+ const noEncoding = outcomeEncoding(outcome.outcome, 1);
1439
+ const yesCoin = outcomeTradeCoin(yesEncoding);
1440
+ const noCoin = outcomeTradeCoin(noEncoding);
1441
+ const yesPrice = priceNumber(mids[yesCoin]);
1442
+ const noPrice = priceNumber(mids[noCoin]);
1443
+ const endOfTrading = outcomeExpiryUnix(outcome, question);
1444
+ const closed = endOfTrading > 0 && nowSec >= endOfTrading;
1445
+ const template = outcome.name.replace(/^template:/, "");
1446
+ const categories = [template, outcome.venue].filter((v) => Boolean(v && v.trim()));
1447
+ return {
1448
+ outcome: outcome.outcome,
1449
+ name: outcome.name,
1450
+ description: outcome.description,
1451
+ question: formatOutcomeQuestion(outcome, question),
1452
+ questionId: question?.question ?? null,
1453
+ sideNames: [
1454
+ resolveOutcomeSideLabel(outcome.sideSpecs?.[0]?.name, fields, "Yes"),
1455
+ resolveOutcomeSideLabel(outcome.sideSpecs?.[1]?.name, fields, "No")
1456
+ ],
1457
+ quoteToken: outcome.quoteToken?.trim() || "USDC",
1458
+ venue: outcome.venue ?? "",
1459
+ categories,
1460
+ yesEncoding,
1461
+ noEncoding,
1462
+ yesCoin,
1463
+ noCoin,
1464
+ yesAssetId: outcomeAssetId(yesEncoding),
1465
+ noAssetId: outcomeAssetId(noEncoding),
1466
+ yesBalanceCoin: outcomeBalanceCoin(yesEncoding),
1467
+ noBalanceCoin: outcomeBalanceCoin(noEncoding),
1468
+ yesPrice,
1469
+ noPrice,
1470
+ yesPriceLabel: priceLabel(yesPrice),
1471
+ noPriceLabel: priceLabel(noPrice),
1472
+ expiresAt: formatHyperliquidStamp(endOfTrading > 0 ? endOfTrading : null),
1473
+ endOfTrading,
1474
+ status: closed ? "closed" : "live",
1475
+ tradable: !closed,
1476
+ rules: formatOutcomeRules(outcome, question, templates)
1477
+ };
1478
+ }
1479
+ async function loadOutcomeSnapshot(chainId) {
1480
+ const hit = metaCache.get(chainId);
1481
+ if (hit && Date.now() - hit.at < META_TTL_MS) {
1482
+ return {
1483
+ outcomes: hit.data.outcomes ?? [],
1484
+ questions: hit.data.questions ?? [],
1485
+ mids: hit.mids,
1486
+ templates: hit.templates
1487
+ };
1488
+ }
1489
+ const meta = await outcomeInfoPost(chainId, { type: "outcomeMeta" });
1490
+ let mids = {};
1491
+ try {
1492
+ mids = await outcomeInfoPost(chainId, { type: "allMids" });
1493
+ } catch {
1494
+ mids = {};
1495
+ }
1496
+ let templates = [];
1497
+ try {
1498
+ const raw = await outcomeInfoPost(chainId, { type: "outcomeTemplates" });
1499
+ templates = Array.isArray(raw) ? raw : [];
1500
+ } catch {
1501
+ templates = [];
1502
+ }
1503
+ metaCache.set(chainId, { at: Date.now(), data: meta, mids, templates });
1504
+ return { outcomes: meta.outcomes ?? [], questions: meta.questions ?? [], mids, templates };
1505
+ }
1506
+ function hyperliquidOutcomeMatchesQuery(market, query) {
1507
+ const q = query.trim().toLowerCase();
1508
+ if (!q) return true;
1509
+ const hay = [market.question, market.description, market.name, market.sideNames.join(" "), String(market.outcome)].join(" ").toLowerCase();
1510
+ return hay.includes(q);
1511
+ }
1512
+ async function listHyperliquidOutcomeMarkets(args) {
1513
+ const chainId = args.chainId ?? HYPERLIQUID_OUTCOME_MAINNET_CHAIN_ID;
1514
+ const snap = await loadOutcomeSnapshot(chainId);
1515
+ const nowSec = Math.floor(Date.now() / 1e3);
1516
+ const markets = snap.outcomes.map(
1517
+ (outcome) => marketFromMeta(
1518
+ outcome,
1519
+ questionForOutcome(outcome.outcome, snap.questions),
1520
+ snap.mids,
1521
+ nowSec,
1522
+ snap.templates
1523
+ )
1524
+ ).filter((market) => hyperliquidOutcomeMatchesQuery(market, args.query ?? ""));
1525
+ return { markets, questions: snap.questions };
1526
+ }
1527
+ async function getHyperliquidOutcomeMarket(args) {
1528
+ const { markets } = await listHyperliquidOutcomeMarkets({ chainId: args.chainId });
1529
+ const found = markets.find((m) => m.outcome === args.outcomeId);
1530
+ if (!found) throw new Error(`Hyperliquid outcome ${args.outcomeId} is not in outcomeMeta.`);
1531
+ return found;
1532
+ }
1533
+ function normalizeLevels(rows) {
1534
+ return (rows ?? []).map((row) => ({
1535
+ px: String(row.px ?? ""),
1536
+ sz: String(row.sz ?? ""),
1537
+ n: Number(row.n ?? 0)
1538
+ }));
1539
+ }
1540
+ var CHANCE_LOOKBACK_MS = 14 * 24 * 60 * 60 * 1e3;
1541
+ async function fetchHyperliquidOutcomeChanceSeries(args) {
1542
+ const chainId = args.chainId ?? HYPERLIQUID_OUTCOME_MAINNET_CHAIN_ID;
1543
+ const endTime = Date.now();
1544
+ const raw = await outcomeInfoPost(chainId, {
1545
+ type: "candleSnapshot",
1546
+ req: {
1547
+ coin: args.coin,
1548
+ interval: "1h",
1549
+ startTime: endTime - CHANCE_LOOKBACK_MS,
1550
+ endTime
1551
+ }
1552
+ });
1553
+ if (!Array.isArray(raw)) return [];
1554
+ return raw.map((bar) => {
1555
+ const timestamp = Number(bar.t);
1556
+ const open = Number(bar.o);
1557
+ const high = Number(bar.h);
1558
+ const low = Number(bar.l);
1559
+ const close = Number(bar.c);
1560
+ if (![timestamp, open, high, low, close].every(Number.isFinite)) return null;
1561
+ return { timestamp, open, high, low, close };
1562
+ }).filter((bar) => bar != null);
1563
+ }
1564
+ async function fetchHyperliquidOutcomeBook(args) {
1565
+ const chainId = args.chainId ?? HYPERLIQUID_OUTCOME_MAINNET_CHAIN_ID;
1566
+ const raw = await outcomeInfoPost(chainId, { type: "l2Book", coin: args.coin });
1567
+ if (raw == null || typeof raw !== "object") {
1568
+ return { coin: args.coin, time: null, bids: [], asks: [] };
1569
+ }
1570
+ return {
1571
+ coin: raw.coin ?? args.coin,
1572
+ time: typeof raw.time === "number" ? raw.time : null,
1573
+ bids: normalizeLevels(raw.levels?.[0]),
1574
+ asks: normalizeLevels(raw.levels?.[1])
1575
+ };
1576
+ }
1577
+
1578
+ // src/protocols/evm/hyperliquidOutcome/mcpSummaries.ts
1579
+ function hyperliquidOutcomeToPredictionFields(market, chainId) {
1580
+ return {
1581
+ venue: "hyperliquid",
1582
+ venueMarketId: String(market.outcome),
1583
+ question: market.question,
1584
+ categories: market.categories,
1585
+ yesPrice: market.yesPrice,
1586
+ noPrice: market.noPrice,
1587
+ yesPriceLabel: market.yesPriceLabel,
1588
+ noPriceLabel: market.noPriceLabel,
1589
+ volume: "\u2014",
1590
+ tvl: null,
1591
+ expiresAt: market.expiresAt,
1592
+ expiresAtLabel: market.expiresAt,
1593
+ tradingEndsLabel: market.expiresAt ? `Settles ${market.expiresAt}` : "Settlement time not listed",
1594
+ endOfTrading: market.endOfTrading,
1595
+ status: market.status,
1596
+ outcome: market.outcome,
1597
+ statusUpdatedAt: null,
1598
+ resolutionLabel: null,
1599
+ chainId,
1600
+ paymentSymbol: market.quoteToken,
1601
+ tradable: market.tradable,
1602
+ rules: market.rules,
1603
+ source: market.name
1604
+ };
1605
+ }
1606
+
1107
1607
  // src/protocols/evm/trueo/predictionSearch.ts
1108
1608
  var PREDICTION_MARKET_V1_KIND = "continuum/prediction-market/v1";
1109
1609
  function trueoMarketToPredictionRow(row) {
@@ -1136,11 +1636,13 @@ function trueoMarketToPredictionRow(row) {
1136
1636
  };
1137
1637
  }
1138
1638
  async function searchPredictionMarkets(args) {
1139
- const venues = (args.venues ?? ["trueo"]).map((v) => v.trim().toLowerCase()).filter(Boolean);
1140
- if (venues.length && !venues.includes("trueo")) {
1639
+ const venues = (args.venues ?? ["trueo", "hyperliquid"]).map((v) => v.trim().toLowerCase()).filter(Boolean);
1640
+ const wantTrueo = venues.includes("trueo");
1641
+ const wantHl = venues.includes("hyperliquid");
1642
+ if (!wantTrueo && !wantHl) {
1141
1643
  return {
1142
1644
  markets: [],
1143
- notes: "No supported venues in the filter. v1 adapter is trueo (Base).",
1645
+ notes: "No supported venues in the filter. Use trueo and/or hyperliquid.",
1144
1646
  hasMore: false,
1145
1647
  includeAll: false,
1146
1648
  nextOffset: 0,
@@ -1150,6 +1652,33 @@ async function searchPredictionMarkets(args) {
1150
1652
  const includeAll = args.includeAll === true;
1151
1653
  const offset = Math.max(args.offset ?? 0, 0);
1152
1654
  const indexBase = Math.max(args.indexBase ?? 0, 0);
1655
+ const limit = args.limit ?? TRUEO_MARKET_PAGE_SIZE;
1656
+ const exclude = new Set((args.excludeAddresses ?? []).map((id) => id.trim().toLowerCase()).filter(Boolean));
1657
+ let hlAll = [];
1658
+ let hlError = "";
1659
+ if (wantHl) {
1660
+ try {
1661
+ const listed = await listHyperliquidOutcomeMarkets({ query: args.query });
1662
+ hlAll = listed.markets.map((market) => hyperliquidOutcomeToPredictionFields(market, HYPERLIQUID_OUTCOME_MAINNET_CHAIN_ID)).filter((row) => !exclude.has(row.venueMarketId.toLowerCase()));
1663
+ } catch (error) {
1664
+ hlError = error instanceof Error ? error.message : "Hyperliquid outcome search failed.";
1665
+ }
1666
+ }
1667
+ if (!wantTrueo) {
1668
+ const page2 = hlAll.slice(offset, offset + limit).map((row, i) => ({ ...row, index: indexBase + i + 1 }));
1669
+ const nextOffset = offset + page2.length;
1670
+ const nextIndexBase2 = indexBase + page2.length;
1671
+ const more2 = nextOffset < hlAll.length;
1672
+ return {
1673
+ markets: page2,
1674
+ hasMore: more2,
1675
+ nextOffset,
1676
+ nextIndexBase: nextIndexBase2,
1677
+ includeAll: false,
1678
+ notes: hlError ? hlError : page2.length ? `Hyperliquid outcome markets. ${more2 ? `More: offset=${nextOffset}, indexBase=${nextIndexBase2}.` : "End of results."} Pick #N then get_prediction_market with venue hyperliquid.` : `No Hyperliquid outcome title match for ${JSON.stringify(args.query?.trim() ?? "")}.`
1679
+ };
1680
+ }
1681
+ const hlPrefix = offset === 0 ? hlAll.slice(0, limit) : [];
1153
1682
  const page = await listTrueoMarketsPage({
1154
1683
  query: args.query,
1155
1684
  category: args.category,
@@ -1158,10 +1687,12 @@ async function searchPredictionMarkets(args) {
1158
1687
  trending: args.trending,
1159
1688
  excludeAddresses: args.excludeAddresses
1160
1689
  });
1161
- const markets = page.markets.map(trueoMarketToPredictionRow).slice(0, args.limit ?? TRUEO_MARKET_PAGE_SIZE).map((row, i) => ({ ...row, index: indexBase + i + 1 }));
1690
+ const trueoRows = page.markets.map(trueoMarketToPredictionRow).filter((row) => !exclude.has(row.venueMarketId.toLowerCase())).slice(0, limit);
1691
+ const markets = [...hlPrefix, ...trueoRows].map((row, i) => ({ ...row, index: indexBase + i + 1 }));
1162
1692
  const nextIndexBase = indexBase + markets.length;
1163
1693
  const scope = trueoSearchScopeNote(includeAll);
1164
1694
  const q = args.query?.trim() ?? "";
1695
+ const hlNote = hlPrefix.length > 0 ? `Included ${hlPrefix.length} Hyperliquid outcome match(es) on this page (venue hyperliquid).` : hlError;
1165
1696
  if (q && markets.length === 0) {
1166
1697
  return {
1167
1698
  markets,
@@ -1169,17 +1700,17 @@ async function searchPredictionMarkets(args) {
1169
1700
  nextOffset: page.nextOffset,
1170
1701
  nextIndexBase,
1171
1702
  includeAll,
1172
- notes: `No live or recent title match for ${JSON.stringify(q)} after scanning the catalog. Do not list trending unless the operator asked. ${scope}`
1703
+ notes: `No live or recent title match for ${JSON.stringify(q)} after scanning Trueo${wantHl ? " and Hyperliquid" : ""}. Do not list trending unless the operator asked. ${scope} ${hlError}`.trim()
1173
1704
  };
1174
1705
  }
1175
- const more = page.hasMore ? `More: search_prediction_markets with offset=${page.nextOffset}, trending=false, indexBase=${nextIndexBase}, excludeAddresses from this page's venueMarketIds.` : "End of results.";
1706
+ const more = page.hasMore ? `More Trueo: search_prediction_markets with offset=${page.nextOffset}, trending=false, indexBase=${nextIndexBase}, excludeAddresses from this page's venueMarketIds.` : "End of Trueo results.";
1176
1707
  return {
1177
1708
  markets,
1178
1709
  hasMore: page.hasMore,
1179
1710
  nextOffset: page.nextOffset,
1180
1711
  nextIndexBase,
1181
1712
  includeAll,
1182
- notes: `${scope} ${more} Pick #N then get_prediction_market.`
1713
+ notes: `${hlNote} ${scope} ${more} Pick #N then get_prediction_market.`.replace(/\s+/g, " ").trim()
1183
1714
  };
1184
1715
  }
1185
1716
  function buildPredictionMarketEnvelope(market) {
@@ -1244,8 +1775,36 @@ async function listMyPredictionMarketsSummary(input) {
1244
1775
  }
1245
1776
  async function getPredictionMarketSummary(input) {
1246
1777
  const venue = (input.venue ?? "trueo").trim().toLowerCase();
1778
+ if (venue === "hyperliquid") {
1779
+ const outcomeId = Number.parseInt(input.venueMarketId, 10);
1780
+ if (!Number.isFinite(outcomeId)) {
1781
+ throw new Error("Hyperliquid venueMarketId must be the outcome id.");
1782
+ }
1783
+ const market2 = await getHyperliquidOutcomeMarket({ outcomeId });
1784
+ const [yesBook, noBook] = await Promise.all([
1785
+ fetchHyperliquidOutcomeBook({ coin: market2.yesCoin }),
1786
+ fetchHyperliquidOutcomeBook({ coin: market2.noCoin })
1787
+ ]);
1788
+ let ohlc2 = [];
1789
+ try {
1790
+ ohlc2 = await fetchHyperliquidOutcomeChanceSeries({ coin: market2.yesCoin });
1791
+ } catch {
1792
+ ohlc2 = [];
1793
+ }
1794
+ const row2 = {
1795
+ ...hyperliquidOutcomeToPredictionFields(market2, HYPERLIQUID_OUTCOME_MAINNET_CHAIN_ID),
1796
+ yesBook,
1797
+ noBook,
1798
+ ohlc: ohlc2
1799
+ };
1800
+ return {
1801
+ kind: PREDICTION_MARKET_V1_KIND,
1802
+ ...row2,
1803
+ envelope: buildPredictionMarketEnvelope(row2)
1804
+ };
1805
+ }
1247
1806
  if (venue !== "trueo") {
1248
- throw new Error(`Venue "${venue}" is not supported. v1 adapter is trueo.`);
1807
+ throw new Error(`Venue "${venue}" is not supported. Use trueo or hyperliquid.`);
1249
1808
  }
1250
1809
  const row = await fetchTrueoMarket({ marketAddress: input.venueMarketId });
1251
1810
  let ohlc = [];
@@ -1322,7 +1881,8 @@ var DEFI_PROTOCOLS_WITH_30MIN_EXPIRY = /* @__PURE__ */ new Set([
1322
1881
  "aerodrome",
1323
1882
  "pendle",
1324
1883
  "derive",
1325
- "trueo"
1884
+ "trueo",
1885
+ "hyperliquidOutcome"
1326
1886
  ]);
1327
1887
  function defiMultisignExpiryUnixSeconds(protocolId, explicitExpiryDate) {
1328
1888
  if (typeof explicitExpiryDate === "number" && Number.isFinite(explicitExpiryDate) && explicitExpiryDate > 0) {