@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
|
@@ -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
|
-
|
|
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.
|
|
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
|
|
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
|
|
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.
|
|
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) {
|