@continuumdao/ctm-mpc-defi 0.2.50 → 0.2.52

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