@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.
- package/dist/agent/catalog.cjs +829 -55
- package/dist/agent/catalog.cjs.map +1 -1
- package/dist/agent/catalog.d.ts +93 -22
- package/dist/agent/catalog.js +829 -55
- package/dist/agent/catalog.js.map +1 -1
- package/dist/agent/skills/continuum-dao/SKILL.md +9 -6
- package/dist/agent/skills/continuum-dao-marketplace/SKILL.md +58 -0
- package/dist/agent/skills/hyperliquidOutcome/SKILL.md +47 -0
- package/dist/core/index.cjs +2 -1
- package/dist/core/index.cjs.map +1 -1
- package/dist/core/index.js +2 -1
- package/dist/core/index.js.map +1 -1
- package/dist/index.cjs +2 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/protocols/evm/aerodrome/index.cjs.map +1 -1
- package/dist/protocols/evm/aerodrome/index.js.map +1 -1
- package/dist/protocols/evm/continuum-dao/index.cjs +2311 -62
- package/dist/protocols/evm/continuum-dao/index.cjs.map +1 -1
- package/dist/protocols/evm/continuum-dao/index.d.ts +697 -2
- package/dist/protocols/evm/continuum-dao/index.js +2255 -64
- package/dist/protocols/evm/continuum-dao/index.js.map +1 -1
- package/dist/protocols/evm/hyperliquidOutcome/index.cjs +1294 -0
- package/dist/protocols/evm/hyperliquidOutcome/index.cjs.map +1 -0
- package/dist/protocols/evm/hyperliquidOutcome/index.d.ts +465 -0
- package/dist/protocols/evm/hyperliquidOutcome/index.js +1247 -0
- package/dist/protocols/evm/hyperliquidOutcome/index.js.map +1 -0
- package/dist/protocols/evm/pendle/index.cjs +2 -1
- package/dist/protocols/evm/pendle/index.cjs.map +1 -1
- package/dist/protocols/evm/pendle/index.js +2 -1
- package/dist/protocols/evm/pendle/index.js.map +1 -1
- package/dist/protocols/evm/trueo/index.cjs +569 -9
- package/dist/protocols/evm/trueo/index.cjs.map +1 -1
- package/dist/protocols/evm/trueo/index.d.ts +42 -1
- package/dist/protocols/evm/trueo/index.js +569 -9
- package/dist/protocols/evm/trueo/index.js.map +1 -1
- 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
|
-
|
|
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.
|
|
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
|
|
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
|
|
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.
|
|
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) {
|