@continuumdao/ctm-mpc-defi 0.2.49 → 0.2.51
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent/catalog.cjs +1344 -22
- package/dist/agent/catalog.cjs.map +1 -1
- package/dist/agent/catalog.d.ts +1528 -9
- package/dist/agent/catalog.js +1316 -23
- package/dist/agent/catalog.js.map +1 -1
- package/dist/agent/skills/continuum-dao/SKILL.md +2 -0
- package/dist/agent/skills/continuum-dao-marketplace/SKILL.md +58 -0
- package/dist/agent/skills/hyperliquidOutcome/SKILL.md +47 -0
- package/dist/agent/skills/trueo/SKILL.md +64 -0
- package/dist/core/index.cjs +3 -1
- package/dist/core/index.cjs.map +1 -1
- package/dist/core/index.js +3 -1
- package/dist/core/index.js.map +1 -1
- package/dist/index.cjs +3 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +3 -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 +2274 -59
- package/dist/protocols/evm/continuum-dao/index.cjs.map +1 -1
- package/dist/protocols/evm/continuum-dao/index.d.ts +690 -2
- package/dist/protocols/evm/continuum-dao/index.js +2218 -61
- 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 +3 -1
- package/dist/protocols/evm/pendle/index.cjs.map +1 -1
- package/dist/protocols/evm/pendle/index.js +3 -1
- package/dist/protocols/evm/pendle/index.js.map +1 -1
- package/dist/protocols/evm/trueo/index.cjs +2868 -0
- package/dist/protocols/evm/trueo/index.cjs.map +1 -0
- package/dist/protocols/evm/trueo/index.d.ts +1295 -0
- package/dist/protocols/evm/trueo/index.js +2725 -0
- package/dist/protocols/evm/trueo/index.js.map +1 -0
- package/package.json +11 -1
|
@@ -0,0 +1,1247 @@
|
|
|
1
|
+
import { hashTypedData, stringToHex, keccak256 } from 'viem';
|
|
2
|
+
import { getClientIdFromKeyGenResult } from '@continuumdao/continuum-node-sdk';
|
|
3
|
+
import { encode } from '@msgpack/msgpack';
|
|
4
|
+
|
|
5
|
+
// src/core/registry.ts
|
|
6
|
+
var modules = [];
|
|
7
|
+
function registerProtocolModule(mod) {
|
|
8
|
+
const existing = modules.findIndex((m) => m.id === mod.id);
|
|
9
|
+
if (existing >= 0) {
|
|
10
|
+
modules[existing] = mod;
|
|
11
|
+
} else {
|
|
12
|
+
modules.push(mod);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// src/protocols/evm/hyperliquid/support.ts
|
|
17
|
+
var HYPERLIQUID_SUPPORTED_CHAIN_IDS = [999, 998];
|
|
18
|
+
function isHyperliquidChainSupported(chainId) {
|
|
19
|
+
return HYPERLIQUID_SUPPORTED_CHAIN_IDS.includes(chainId);
|
|
20
|
+
}
|
|
21
|
+
function hyperliquidApiBaseUrl(chainId) {
|
|
22
|
+
return chainId === 998 ? "https://api.hyperliquid-testnet.xyz" : "https://api.hyperliquid.xyz";
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// src/protocols/evm/hyperliquidOutcome/model.ts
|
|
26
|
+
function outcomeEncoding(outcome, side) {
|
|
27
|
+
const o = Math.trunc(outcome);
|
|
28
|
+
const s = Math.trunc(side);
|
|
29
|
+
if (!Number.isFinite(o) || o < 0) throw new Error("outcome must be a non-negative integer");
|
|
30
|
+
if (s !== 0 && s !== 1) throw new Error("side must be 0 or 1");
|
|
31
|
+
return 10 * o + s;
|
|
32
|
+
}
|
|
33
|
+
function outcomeTradeCoin(encoding) {
|
|
34
|
+
return `#${encoding}`;
|
|
35
|
+
}
|
|
36
|
+
function outcomeBalanceCoin(encoding) {
|
|
37
|
+
return `+${encoding}`;
|
|
38
|
+
}
|
|
39
|
+
function outcomeAssetId(encoding) {
|
|
40
|
+
return 1e8 + encoding;
|
|
41
|
+
}
|
|
42
|
+
function outcomeSideIndex(side) {
|
|
43
|
+
if (side === "no" || side === 1) return 1;
|
|
44
|
+
return 0;
|
|
45
|
+
}
|
|
46
|
+
function outcomeSideName(name, fallback) {
|
|
47
|
+
const raw = (name ?? "").trim();
|
|
48
|
+
const stripped = raw.replace(/^template:/i, "").trim();
|
|
49
|
+
return stripped || fallback;
|
|
50
|
+
}
|
|
51
|
+
function parseHyperliquidPipeFields(description) {
|
|
52
|
+
const out = {};
|
|
53
|
+
for (const part of (description ?? "").split("|")) {
|
|
54
|
+
const i = part.indexOf(":");
|
|
55
|
+
if (i <= 0) continue;
|
|
56
|
+
const key = part.slice(0, i).trim();
|
|
57
|
+
const value = part.slice(i + 1).trim();
|
|
58
|
+
if (key && value) out[key] = value;
|
|
59
|
+
}
|
|
60
|
+
return out;
|
|
61
|
+
}
|
|
62
|
+
function parseHyperliquidStamp(raw) {
|
|
63
|
+
const m = /^(\d{4})(\d{2})(\d{2})-(\d{2})(\d{2})$/.exec((raw ?? "").trim());
|
|
64
|
+
if (!m) return null;
|
|
65
|
+
const ms = Date.parse(`${m[1]}-${m[2]}-${m[3]}T${m[4]}:${m[5]}:00Z`);
|
|
66
|
+
return Number.isFinite(ms) ? Math.floor(ms / 1e3) : null;
|
|
67
|
+
}
|
|
68
|
+
function formatHyperliquidStamp(unixSec) {
|
|
69
|
+
if (unixSec == null || !Number.isFinite(unixSec) || unixSec <= 0) return null;
|
|
70
|
+
const d = new Date(unixSec * 1e3);
|
|
71
|
+
if (Number.isNaN(d.getTime())) return null;
|
|
72
|
+
const pad = (n) => String(n).padStart(2, "0");
|
|
73
|
+
return `${d.getUTCFullYear()}-${pad(d.getUTCMonth() + 1)}-${pad(d.getUTCDate())} ${pad(d.getUTCHours())}:${pad(d.getUTCMinutes())} UTC`;
|
|
74
|
+
}
|
|
75
|
+
function questionForOutcome(outcomeId, questions) {
|
|
76
|
+
for (const q of questions) {
|
|
77
|
+
if (q.fallbackOutcome === outcomeId) return q;
|
|
78
|
+
if ((q.namedOutcomes ?? []).includes(outcomeId)) return q;
|
|
79
|
+
}
|
|
80
|
+
return void 0;
|
|
81
|
+
}
|
|
82
|
+
function formatOutcomeQuestion(outcome, question) {
|
|
83
|
+
const fields = parseHyperliquidPipeFields(outcome.description);
|
|
84
|
+
const qFields = parseHyperliquidPipeFields(question?.description);
|
|
85
|
+
const when = formatHyperliquidStamp(
|
|
86
|
+
parseHyperliquidStamp(fields.time) ?? parseHyperliquidStamp(qFields.resolutionDeadline)
|
|
87
|
+
);
|
|
88
|
+
const whenSuffix = when ? ` at ${when}` : "";
|
|
89
|
+
if (outcome.name === "template:binaryPrice") {
|
|
90
|
+
const asset = fields.priceDescription || fields.perp || "the asset";
|
|
91
|
+
const threshold = fields.threshold ?? "";
|
|
92
|
+
return `Will ${asset} be above ${threshold}${whenSuffix}?`.replace(/\s+/g, " ").trim();
|
|
93
|
+
}
|
|
94
|
+
if (outcome.name === "template:priceTouch") {
|
|
95
|
+
const asset = fields.priceDescription || fields.perp || "the asset";
|
|
96
|
+
const target = fields.target ?? "";
|
|
97
|
+
return `Will ${asset} touch ${target}${when ? ` by ${when}` : ""}?`.replace(/\s+/g, " ").trim();
|
|
98
|
+
}
|
|
99
|
+
if (outcome.name === "template:sportsTournamentParticipant") {
|
|
100
|
+
const participant = fields.participant || "this participant";
|
|
101
|
+
const competition = qFields.competition;
|
|
102
|
+
return competition ? `Will ${participant} win ${competition}?` : `Will ${participant} win?`;
|
|
103
|
+
}
|
|
104
|
+
const desc = (outcome.description ?? "").trim();
|
|
105
|
+
if (desc && desc !== "other") return desc;
|
|
106
|
+
return outcome.name || `Outcome ${outcome.outcome}`;
|
|
107
|
+
}
|
|
108
|
+
var STAMP_FIELDS = /* @__PURE__ */ new Set([
|
|
109
|
+
"time",
|
|
110
|
+
"dateTime",
|
|
111
|
+
"resolutionDeadline",
|
|
112
|
+
"scheduledStart",
|
|
113
|
+
"scheduledDecision",
|
|
114
|
+
"expiry",
|
|
115
|
+
"decisionDeadline"
|
|
116
|
+
]);
|
|
117
|
+
function outcomeTemplateId(name) {
|
|
118
|
+
return (name ?? "").replace(/^template:/, "").trim();
|
|
119
|
+
}
|
|
120
|
+
function outcomeFieldMap(outcome, question) {
|
|
121
|
+
return {
|
|
122
|
+
...parseHyperliquidPipeFields(question?.description),
|
|
123
|
+
...parseHyperliquidPipeFields(outcome.description)
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
function displayField(key, raw) {
|
|
127
|
+
if (STAMP_FIELDS.has(key)) {
|
|
128
|
+
const formatted = formatHyperliquidStamp(parseHyperliquidStamp(raw));
|
|
129
|
+
if (formatted) return formatted;
|
|
130
|
+
}
|
|
131
|
+
return raw.trim();
|
|
132
|
+
}
|
|
133
|
+
function fillOutcomeTemplate(text, fields) {
|
|
134
|
+
return text.replace(/\{([A-Za-z0-9_]+)\}/g, (token, key) => {
|
|
135
|
+
const raw = fields[key];
|
|
136
|
+
if (raw == null || !raw.trim()) return token;
|
|
137
|
+
return displayField(key, raw);
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
function stripTemplateMetadata(text) {
|
|
141
|
+
return (text.split(/\smetadata=/)[0] ?? text).replace(/\bprice price\b/g, "price").trim();
|
|
142
|
+
}
|
|
143
|
+
function templateById(templates, id) {
|
|
144
|
+
return templates?.find((template) => template.id === id);
|
|
145
|
+
}
|
|
146
|
+
function parentTemplateId(template) {
|
|
147
|
+
const role = template?.role;
|
|
148
|
+
if (!role || typeof role !== "object") return null;
|
|
149
|
+
const parent = role.questionOutcome?.parent?.trim();
|
|
150
|
+
return parent || null;
|
|
151
|
+
}
|
|
152
|
+
function resolveOutcomeSideLabel(specName, fields, fallback) {
|
|
153
|
+
const raw = outcomeSideName(specName, fallback);
|
|
154
|
+
const filled = fillOutcomeTemplate(raw, fields).trim();
|
|
155
|
+
return filled || fallback;
|
|
156
|
+
}
|
|
157
|
+
function recurringRules(outcome, fields) {
|
|
158
|
+
const when = fields.expiry ? displayField("expiry", fields.expiry) : "expiry";
|
|
159
|
+
const asset = fields.underlying || "the asset";
|
|
160
|
+
if (outcome.name === "Recurring" && fields.class === "priceBinary") {
|
|
161
|
+
const period = fields.period ? ` Period ${fields.period}.` : "";
|
|
162
|
+
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}`;
|
|
163
|
+
}
|
|
164
|
+
if (outcome.name === "Recurring Fallback") {
|
|
165
|
+
return `Yes if none of the named ${asset} price buckets contain the mark at ${when}. Otherwise No. Thresholds: ${fields.priceThresholds || "\u2014"}.`;
|
|
166
|
+
}
|
|
167
|
+
if (outcome.name === "Recurring Named Outcome") {
|
|
168
|
+
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"}.`;
|
|
169
|
+
}
|
|
170
|
+
return null;
|
|
171
|
+
}
|
|
172
|
+
function fallbackRules(outcome, fields) {
|
|
173
|
+
const id = outcomeTemplateId(outcome.name);
|
|
174
|
+
const when = fields.time ? displayField("time", fields.time) : "the settlement time";
|
|
175
|
+
const feed = fields.priceDescription || fields.perp || "the price";
|
|
176
|
+
if (id === "binaryPrice") {
|
|
177
|
+
const twap = fields.seconds ? ` Settlement uses the ${fields.seconds}-second TWAP of ${feed} ending at ${when}.` : "";
|
|
178
|
+
return `Yes if the ${fields.perp || feed} price is above ${fields.threshold ?? "\u2014"} at ${when}. Otherwise No.${twap}`.replace(
|
|
179
|
+
/\bprice price\b/g,
|
|
180
|
+
"price"
|
|
181
|
+
);
|
|
182
|
+
}
|
|
183
|
+
if (id === "priceTouch") {
|
|
184
|
+
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.`;
|
|
185
|
+
}
|
|
186
|
+
if (id === "companyIpoConfirmed") {
|
|
187
|
+
const at = fields.dateTime ? displayField("dateTime", fields.dateTime) : "the deadline";
|
|
188
|
+
return `Yes if ${fields.company || "the company"}'s first public listing is confirmed at or before ${at}. Otherwise No.`;
|
|
189
|
+
}
|
|
190
|
+
if (id === "sportsTournamentParticipant" || id === "sportsContestParticipant2") {
|
|
191
|
+
return `Yes if ${fields.participant || "this participant"} is the sole winner. Otherwise No.`;
|
|
192
|
+
}
|
|
193
|
+
if (id === "sportsContestDraw2") return "Yes if the contest is a draw. Otherwise No.";
|
|
194
|
+
if (id === "policyRateNoChange") return "Yes if the announced policy value equals the prior value. Otherwise No.";
|
|
195
|
+
if (id === "policyRateDecrease") return "Yes if the announced policy value is lower than the prior value. Otherwise No.";
|
|
196
|
+
if (id === "policyRateIncrease") return "Yes if the announced policy value is higher than the prior value. Otherwise No.";
|
|
197
|
+
if (id === "sportsContestWinner") {
|
|
198
|
+
const a = fields.shortNameA || fields.participantA || "Side A";
|
|
199
|
+
const b = fields.shortNameB || fields.participantB || "Side B";
|
|
200
|
+
return `${a} wins if ${fields.participantA || a} wins the contest. ${b} wins if ${fields.participantB || b} wins. Counted play: ${fields.countedPlay || "the scheduled contest"}.`;
|
|
201
|
+
}
|
|
202
|
+
return "";
|
|
203
|
+
}
|
|
204
|
+
function formatOutcomeRules(outcome, question, templates) {
|
|
205
|
+
const fields = outcomeFieldMap(outcome, question);
|
|
206
|
+
const recurring = recurringRules(outcome, fields);
|
|
207
|
+
if (recurring) return recurring;
|
|
208
|
+
const template = templateById(templates, outcomeTemplateId(outcome.name));
|
|
209
|
+
const ownRaw = template?.description?.trim();
|
|
210
|
+
const own = ownRaw ? stripTemplateMetadata(fillOutcomeTemplate(ownRaw, fields)) : fallbackRules(outcome, fields);
|
|
211
|
+
const parentRaw = templateById(templates, parentTemplateId(template) ?? "")?.description?.trim();
|
|
212
|
+
const parent = parentRaw ? stripTemplateMetadata(fillOutcomeTemplate(parentRaw, fields)) : "";
|
|
213
|
+
if (own && parent) return `${own}
|
|
214
|
+
|
|
215
|
+
${parent}`;
|
|
216
|
+
return own;
|
|
217
|
+
}
|
|
218
|
+
function outcomeExpiryUnix(outcome, question) {
|
|
219
|
+
const fields = parseHyperliquidPipeFields(outcome.description);
|
|
220
|
+
const qFields = parseHyperliquidPipeFields(question?.description);
|
|
221
|
+
return parseHyperliquidStamp(fields.time) ?? parseHyperliquidStamp(qFields.resolutionDeadline) ?? 0;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// src/protocols/evm/hyperliquidOutcome/api.ts
|
|
225
|
+
var HYPERLIQUID_OUTCOME_PROTOCOL_ID = "hyperliquidOutcome";
|
|
226
|
+
var HYPERLIQUID_OUTCOME_MAINNET_CHAIN_ID = 999;
|
|
227
|
+
var META_TTL_MS = 15e3;
|
|
228
|
+
var metaCache = /* @__PURE__ */ new Map();
|
|
229
|
+
function clearHyperliquidOutcomeMetaCache() {
|
|
230
|
+
metaCache.clear();
|
|
231
|
+
}
|
|
232
|
+
function outcomeFetchFailure(error) {
|
|
233
|
+
if (!(error instanceof Error)) return "request failed";
|
|
234
|
+
const cause = error.cause;
|
|
235
|
+
if (cause instanceof Error && cause.message.trim()) return cause.message;
|
|
236
|
+
return error.message;
|
|
237
|
+
}
|
|
238
|
+
function outcomeErrorCode(error) {
|
|
239
|
+
if (!error || typeof error !== "object") return "";
|
|
240
|
+
if ("code" in error && error.code != null) return String(error.code);
|
|
241
|
+
if (error instanceof Error && error.cause && typeof error.cause === "object" && "code" in error.cause) {
|
|
242
|
+
return String(error.cause.code ?? "");
|
|
243
|
+
}
|
|
244
|
+
return "";
|
|
245
|
+
}
|
|
246
|
+
var OUTCOME_RETRYABLE_CODES = /* @__PURE__ */ new Set([
|
|
247
|
+
"UND_ERR_CONNECT_TIMEOUT",
|
|
248
|
+
"UND_ERR_CONNECT",
|
|
249
|
+
"ECONNRESET",
|
|
250
|
+
"ECONNREFUSED",
|
|
251
|
+
"ETIMEDOUT",
|
|
252
|
+
"ENOTFOUND",
|
|
253
|
+
"EAI_AGAIN",
|
|
254
|
+
"EPIPE"
|
|
255
|
+
]);
|
|
256
|
+
function outcomeFetchRetryable(error) {
|
|
257
|
+
if (error && typeof error === "object" && "outcomeFinal" in error) return false;
|
|
258
|
+
const code = outcomeErrorCode(error);
|
|
259
|
+
return OUTCOME_RETRYABLE_CODES.has(code) || error instanceof Error && (error.message === "fetch failed" || error.message === "connect timeout");
|
|
260
|
+
}
|
|
261
|
+
var outcomeHttpsAgent = null;
|
|
262
|
+
async function outcomeNodeModules() {
|
|
263
|
+
const importer = new Function("specifier", "return import(specifier)");
|
|
264
|
+
const [http, https] = await Promise.all([importer("node:http"), importer("node:https")]);
|
|
265
|
+
return { http, https };
|
|
266
|
+
}
|
|
267
|
+
async function outcomeNodePost(urlString, payload) {
|
|
268
|
+
const { http, https } = await outcomeNodeModules();
|
|
269
|
+
if (!outcomeHttpsAgent) {
|
|
270
|
+
outcomeHttpsAgent = new https.Agent({ keepAlive: true, maxSockets: 4, timeout: 2e4 });
|
|
271
|
+
}
|
|
272
|
+
const url = new URL(urlString);
|
|
273
|
+
const lib = url.protocol === "http:" ? http : https;
|
|
274
|
+
return new Promise((resolve, reject) => {
|
|
275
|
+
const req = lib.request(
|
|
276
|
+
{
|
|
277
|
+
protocol: url.protocol,
|
|
278
|
+
hostname: url.hostname,
|
|
279
|
+
port: url.port || void 0,
|
|
280
|
+
path: `${url.pathname}${url.search}`,
|
|
281
|
+
method: "POST",
|
|
282
|
+
headers: {
|
|
283
|
+
"Content-Type": "application/json",
|
|
284
|
+
"Content-Length": Buffer.byteLength(payload)
|
|
285
|
+
},
|
|
286
|
+
agent: url.protocol === "https:" ? outcomeHttpsAgent ?? void 0 : void 0,
|
|
287
|
+
timeout: 2e4
|
|
288
|
+
},
|
|
289
|
+
(res) => {
|
|
290
|
+
const chunks = [];
|
|
291
|
+
res.on("data", (chunk) => chunks.push(chunk));
|
|
292
|
+
res.on("end", () => {
|
|
293
|
+
resolve({ status: res.statusCode ?? 0, text: Buffer.concat(chunks).toString("utf8") });
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
);
|
|
297
|
+
req.on("timeout", () => {
|
|
298
|
+
const error = new Error("connect timeout");
|
|
299
|
+
error.code = "ETIMEDOUT";
|
|
300
|
+
req.destroy(error);
|
|
301
|
+
});
|
|
302
|
+
req.on("error", reject);
|
|
303
|
+
req.write(payload);
|
|
304
|
+
req.end();
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
async function outcomeFetchPost(url, payload) {
|
|
308
|
+
const res = await fetch(url, {
|
|
309
|
+
method: "POST",
|
|
310
|
+
headers: { "Content-Type": "application/json" },
|
|
311
|
+
body: payload,
|
|
312
|
+
cache: "no-store"
|
|
313
|
+
});
|
|
314
|
+
return { status: res.status, text: await res.text() };
|
|
315
|
+
}
|
|
316
|
+
async function outcomeInfoPost(chainId, body) {
|
|
317
|
+
if (!isHyperliquidChainSupported(chainId)) {
|
|
318
|
+
throw new Error(`Hyperliquid outcome markets require chainId 999 or 998 (got ${chainId}).`);
|
|
319
|
+
}
|
|
320
|
+
const url = `${hyperliquidApiBaseUrl(chainId)}/info`;
|
|
321
|
+
const payload = JSON.stringify(body);
|
|
322
|
+
let last;
|
|
323
|
+
for (let attempt = 0; attempt < 3; attempt++) {
|
|
324
|
+
try {
|
|
325
|
+
const res = typeof window !== "undefined" || process.env.VITEST ? await outcomeFetchPost(url, payload) : await outcomeNodePost(url, payload);
|
|
326
|
+
if (res.status < 200 || res.status >= 300) {
|
|
327
|
+
const error = new Error(`Hyperliquid info API failed (${res.status}): ${res.text.slice(0, 200)}`);
|
|
328
|
+
if (res.status !== 429 && res.status !== 502 && res.status !== 503 && res.status !== 504) {
|
|
329
|
+
error.outcomeFinal = true;
|
|
330
|
+
}
|
|
331
|
+
throw error;
|
|
332
|
+
}
|
|
333
|
+
return JSON.parse(res.text);
|
|
334
|
+
} catch (error) {
|
|
335
|
+
last = error;
|
|
336
|
+
if (!outcomeFetchRetryable(error) || attempt === 2) break;
|
|
337
|
+
await new Promise((resolve) => setTimeout(resolve, 250 * (attempt + 1)));
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
throw new Error(`Hyperliquid info API failed: ${outcomeFetchFailure(last)}`);
|
|
341
|
+
}
|
|
342
|
+
function priceNumber(raw) {
|
|
343
|
+
if (raw == null || raw === "") return null;
|
|
344
|
+
const n = Number.parseFloat(raw);
|
|
345
|
+
return Number.isFinite(n) ? n : null;
|
|
346
|
+
}
|
|
347
|
+
function priceLabel(n) {
|
|
348
|
+
if (n == null) return null;
|
|
349
|
+
if (n >= 0 && n <= 1) return `${(n * 100).toFixed(1)}%`;
|
|
350
|
+
return String(n);
|
|
351
|
+
}
|
|
352
|
+
function marketFromMeta(outcome, question, mids, nowSec, templates) {
|
|
353
|
+
const fields = outcomeFieldMap(outcome, question);
|
|
354
|
+
const yesEncoding = outcomeEncoding(outcome.outcome, 0);
|
|
355
|
+
const noEncoding = outcomeEncoding(outcome.outcome, 1);
|
|
356
|
+
const yesCoin = outcomeTradeCoin(yesEncoding);
|
|
357
|
+
const noCoin = outcomeTradeCoin(noEncoding);
|
|
358
|
+
const yesPrice = priceNumber(mids[yesCoin]);
|
|
359
|
+
const noPrice = priceNumber(mids[noCoin]);
|
|
360
|
+
const endOfTrading = outcomeExpiryUnix(outcome, question);
|
|
361
|
+
const closed = endOfTrading > 0 && nowSec >= endOfTrading;
|
|
362
|
+
const template = outcome.name.replace(/^template:/, "");
|
|
363
|
+
const categories = [template, outcome.venue].filter((v) => Boolean(v && v.trim()));
|
|
364
|
+
return {
|
|
365
|
+
outcome: outcome.outcome,
|
|
366
|
+
name: outcome.name,
|
|
367
|
+
description: outcome.description,
|
|
368
|
+
question: formatOutcomeQuestion(outcome, question),
|
|
369
|
+
questionId: question?.question ?? null,
|
|
370
|
+
sideNames: [
|
|
371
|
+
resolveOutcomeSideLabel(outcome.sideSpecs?.[0]?.name, fields, "Yes"),
|
|
372
|
+
resolveOutcomeSideLabel(outcome.sideSpecs?.[1]?.name, fields, "No")
|
|
373
|
+
],
|
|
374
|
+
quoteToken: outcome.quoteToken?.trim() || "USDC",
|
|
375
|
+
venue: outcome.venue ?? "",
|
|
376
|
+
categories,
|
|
377
|
+
yesEncoding,
|
|
378
|
+
noEncoding,
|
|
379
|
+
yesCoin,
|
|
380
|
+
noCoin,
|
|
381
|
+
yesAssetId: outcomeAssetId(yesEncoding),
|
|
382
|
+
noAssetId: outcomeAssetId(noEncoding),
|
|
383
|
+
yesBalanceCoin: outcomeBalanceCoin(yesEncoding),
|
|
384
|
+
noBalanceCoin: outcomeBalanceCoin(noEncoding),
|
|
385
|
+
yesPrice,
|
|
386
|
+
noPrice,
|
|
387
|
+
yesPriceLabel: priceLabel(yesPrice),
|
|
388
|
+
noPriceLabel: priceLabel(noPrice),
|
|
389
|
+
expiresAt: formatHyperliquidStamp(endOfTrading > 0 ? endOfTrading : null),
|
|
390
|
+
endOfTrading,
|
|
391
|
+
status: closed ? "closed" : "live",
|
|
392
|
+
tradable: !closed,
|
|
393
|
+
rules: formatOutcomeRules(outcome, question, templates)
|
|
394
|
+
};
|
|
395
|
+
}
|
|
396
|
+
async function loadOutcomeSnapshot(chainId) {
|
|
397
|
+
const hit = metaCache.get(chainId);
|
|
398
|
+
if (hit && Date.now() - hit.at < META_TTL_MS) {
|
|
399
|
+
return {
|
|
400
|
+
outcomes: hit.data.outcomes ?? [],
|
|
401
|
+
questions: hit.data.questions ?? [],
|
|
402
|
+
mids: hit.mids,
|
|
403
|
+
templates: hit.templates
|
|
404
|
+
};
|
|
405
|
+
}
|
|
406
|
+
const meta = await outcomeInfoPost(chainId, { type: "outcomeMeta" });
|
|
407
|
+
let mids = {};
|
|
408
|
+
try {
|
|
409
|
+
mids = await outcomeInfoPost(chainId, { type: "allMids" });
|
|
410
|
+
} catch {
|
|
411
|
+
mids = {};
|
|
412
|
+
}
|
|
413
|
+
let templates = [];
|
|
414
|
+
try {
|
|
415
|
+
const raw = await outcomeInfoPost(chainId, { type: "outcomeTemplates" });
|
|
416
|
+
templates = Array.isArray(raw) ? raw : [];
|
|
417
|
+
} catch {
|
|
418
|
+
templates = [];
|
|
419
|
+
}
|
|
420
|
+
metaCache.set(chainId, { at: Date.now(), data: meta, mids, templates });
|
|
421
|
+
return { outcomes: meta.outcomes ?? [], questions: meta.questions ?? [], mids, templates };
|
|
422
|
+
}
|
|
423
|
+
function hyperliquidOutcomeMatchesQuery(market, query) {
|
|
424
|
+
const q = query.trim().toLowerCase();
|
|
425
|
+
if (!q) return true;
|
|
426
|
+
const hay = [market.question, market.description, market.name, market.sideNames.join(" "), String(market.outcome)].join(" ").toLowerCase();
|
|
427
|
+
return hay.includes(q);
|
|
428
|
+
}
|
|
429
|
+
async function listHyperliquidOutcomeMarkets(args) {
|
|
430
|
+
const chainId = args.chainId ?? HYPERLIQUID_OUTCOME_MAINNET_CHAIN_ID;
|
|
431
|
+
const snap = await loadOutcomeSnapshot(chainId);
|
|
432
|
+
const nowSec = Math.floor(Date.now() / 1e3);
|
|
433
|
+
const markets = snap.outcomes.map(
|
|
434
|
+
(outcome) => marketFromMeta(
|
|
435
|
+
outcome,
|
|
436
|
+
questionForOutcome(outcome.outcome, snap.questions),
|
|
437
|
+
snap.mids,
|
|
438
|
+
nowSec,
|
|
439
|
+
snap.templates
|
|
440
|
+
)
|
|
441
|
+
).filter((market) => hyperliquidOutcomeMatchesQuery(market, args.query ?? ""));
|
|
442
|
+
return { markets, questions: snap.questions };
|
|
443
|
+
}
|
|
444
|
+
async function getHyperliquidOutcomeMarket(args) {
|
|
445
|
+
const { markets } = await listHyperliquidOutcomeMarkets({ chainId: args.chainId });
|
|
446
|
+
const found = markets.find((m) => m.outcome === args.outcomeId);
|
|
447
|
+
if (!found) throw new Error(`Hyperliquid outcome ${args.outcomeId} is not in outcomeMeta.`);
|
|
448
|
+
return found;
|
|
449
|
+
}
|
|
450
|
+
function normalizeLevels(rows) {
|
|
451
|
+
return (rows ?? []).map((row) => ({
|
|
452
|
+
px: String(row.px ?? ""),
|
|
453
|
+
sz: String(row.sz ?? ""),
|
|
454
|
+
n: Number(row.n ?? 0)
|
|
455
|
+
}));
|
|
456
|
+
}
|
|
457
|
+
var CHANCE_LOOKBACK_MS = 14 * 24 * 60 * 60 * 1e3;
|
|
458
|
+
async function fetchHyperliquidOutcomeChanceSeries(args) {
|
|
459
|
+
const chainId = args.chainId ?? HYPERLIQUID_OUTCOME_MAINNET_CHAIN_ID;
|
|
460
|
+
const endTime = Date.now();
|
|
461
|
+
const raw = await outcomeInfoPost(chainId, {
|
|
462
|
+
type: "candleSnapshot",
|
|
463
|
+
req: {
|
|
464
|
+
coin: args.coin,
|
|
465
|
+
interval: "1h",
|
|
466
|
+
startTime: endTime - CHANCE_LOOKBACK_MS,
|
|
467
|
+
endTime
|
|
468
|
+
}
|
|
469
|
+
});
|
|
470
|
+
if (!Array.isArray(raw)) return [];
|
|
471
|
+
return raw.map((bar) => {
|
|
472
|
+
const timestamp = Number(bar.t);
|
|
473
|
+
const open = Number(bar.o);
|
|
474
|
+
const high = Number(bar.h);
|
|
475
|
+
const low = Number(bar.l);
|
|
476
|
+
const close = Number(bar.c);
|
|
477
|
+
if (![timestamp, open, high, low, close].every(Number.isFinite)) return null;
|
|
478
|
+
return { timestamp, open, high, low, close };
|
|
479
|
+
}).filter((bar) => bar != null);
|
|
480
|
+
}
|
|
481
|
+
async function fetchHyperliquidOutcomeBook(args) {
|
|
482
|
+
const chainId = args.chainId ?? HYPERLIQUID_OUTCOME_MAINNET_CHAIN_ID;
|
|
483
|
+
const raw = await outcomeInfoPost(chainId, { type: "l2Book", coin: args.coin });
|
|
484
|
+
if (raw == null || typeof raw !== "object") {
|
|
485
|
+
return { coin: args.coin, time: null, bids: [], asks: [] };
|
|
486
|
+
}
|
|
487
|
+
return {
|
|
488
|
+
coin: raw.coin ?? args.coin,
|
|
489
|
+
time: typeof raw.time === "number" ? raw.time : null,
|
|
490
|
+
bids: normalizeLevels(raw.levels?.[0]),
|
|
491
|
+
asks: normalizeLevels(raw.levels?.[1])
|
|
492
|
+
};
|
|
493
|
+
}
|
|
494
|
+
function outcomeFromBalanceCoin(coin) {
|
|
495
|
+
if (!coin.startsWith("+")) return null;
|
|
496
|
+
const encoding = Number.parseInt(coin.slice(1), 10);
|
|
497
|
+
if (!Number.isFinite(encoding)) return null;
|
|
498
|
+
const sideDigit = encoding % 10;
|
|
499
|
+
if (sideDigit !== 0 && sideDigit !== 1) return null;
|
|
500
|
+
return { outcome: (encoding - sideDigit) / 10, side: sideDigit === 1 ? "no" : "yes" };
|
|
501
|
+
}
|
|
502
|
+
function outcomeFromTradeCoin(coin) {
|
|
503
|
+
if (!coin.startsWith("#")) return null;
|
|
504
|
+
return outcomeFromBalanceCoin(`+${coin.slice(1)}`);
|
|
505
|
+
}
|
|
506
|
+
async function fetchHyperliquidOutcomeAccount(args) {
|
|
507
|
+
const chainId = args.chainId ?? HYPERLIQUID_OUTCOME_MAINNET_CHAIN_ID;
|
|
508
|
+
const user = args.user.trim();
|
|
509
|
+
if (!user) throw new Error("user address is required");
|
|
510
|
+
const [spot, orders] = await Promise.all([
|
|
511
|
+
outcomeInfoPost(chainId, {
|
|
512
|
+
type: "spotClearinghouseState",
|
|
513
|
+
user
|
|
514
|
+
}),
|
|
515
|
+
outcomeInfoPost(chainId, { type: "openOrders", user })
|
|
516
|
+
]);
|
|
517
|
+
const quoteBalances = [];
|
|
518
|
+
const outcomeBalances = [];
|
|
519
|
+
for (const row of spot.balances ?? []) {
|
|
520
|
+
const coin = String(row.coin ?? "");
|
|
521
|
+
const total = String(row.total ?? "0");
|
|
522
|
+
const hold = String(row.hold ?? "0");
|
|
523
|
+
const parsed = outcomeFromBalanceCoin(coin);
|
|
524
|
+
if (parsed) {
|
|
525
|
+
outcomeBalances.push({ coin, total, hold, outcome: parsed.outcome, side: parsed.side });
|
|
526
|
+
} else if (coin && !coin.startsWith("#")) {
|
|
527
|
+
quoteBalances.push({ coin, total, hold });
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
const openOrders = [];
|
|
531
|
+
for (const row of orders ?? []) {
|
|
532
|
+
const coin = String(row.coin ?? "");
|
|
533
|
+
const parsed = outcomeFromTradeCoin(coin);
|
|
534
|
+
if (!parsed) continue;
|
|
535
|
+
openOrders.push({
|
|
536
|
+
coin,
|
|
537
|
+
side: String(row.side ?? ""),
|
|
538
|
+
limitPx: String(row.limitPx ?? ""),
|
|
539
|
+
sz: String(row.sz ?? ""),
|
|
540
|
+
oid: Number(row.oid ?? 0),
|
|
541
|
+
timestamp: Number(row.timestamp ?? 0),
|
|
542
|
+
outcome: parsed.outcome,
|
|
543
|
+
outcomeSide: parsed.side
|
|
544
|
+
});
|
|
545
|
+
}
|
|
546
|
+
return { quoteBalances, outcomeBalances, openOrders };
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
// src/protocols/evm/hyperliquidOutcome/purpose.ts
|
|
550
|
+
function buildHyperliquidOutcomePurposePrefill(args) {
|
|
551
|
+
const q = (args.question ?? "").trim();
|
|
552
|
+
const topic = q ? ` ${q}` : "";
|
|
553
|
+
const side = args.side === "no" ? "No" : args.side === "yes" ? "Yes" : "";
|
|
554
|
+
switch (args.action) {
|
|
555
|
+
case "order": {
|
|
556
|
+
const dir = args.isBuy === false ? "sell" : "buy";
|
|
557
|
+
const tif = (args.tif ?? "gtc").toUpperCase();
|
|
558
|
+
return `Hyperliquid outcome: ${dir} ${side}${topic} ${args.szHuman ?? ""} @ ${args.limitPxHuman ?? ""} ${tif}`.replace(/\s+/g, " ").trim();
|
|
559
|
+
}
|
|
560
|
+
case "cancel":
|
|
561
|
+
return `Hyperliquid outcome: cancel order ${args.oid ?? ""}${topic}`.replace(/\s+/g, " ").trim();
|
|
562
|
+
case "split":
|
|
563
|
+
return `Hyperliquid outcome: split ${args.amountHuman ?? ""} quote into Yes+No${topic}`.replace(/\s+/g, " ").trim();
|
|
564
|
+
case "merge":
|
|
565
|
+
return `Hyperliquid outcome: merge Yes+No into quote ${args.amountHuman ?? "max"}${topic}`.replace(/\s+/g, " ").trim();
|
|
566
|
+
case "mergeQuestion":
|
|
567
|
+
return `Hyperliquid outcome: merge question into quote ${args.amountHuman ?? "max"}${topic}`.replace(/\s+/g, " ").trim();
|
|
568
|
+
case "negate":
|
|
569
|
+
return `Hyperliquid outcome: negate ${side || "No"}${topic} ${args.amountHuman ?? ""}`.replace(/\s+/g, " ").trim();
|
|
570
|
+
default:
|
|
571
|
+
return "Hyperliquid outcome";
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
// src/core/purpose.ts
|
|
576
|
+
function mergePurposeText(purposeText, purposeSuffix) {
|
|
577
|
+
const t = (purposeText ?? "").trim();
|
|
578
|
+
const suffix = (purposeSuffix ?? "").trim();
|
|
579
|
+
if (!suffix) return t;
|
|
580
|
+
return t ? `${t}
|
|
581
|
+
|
|
582
|
+
${suffix}` : suffix;
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
// src/core/envelope.ts
|
|
586
|
+
function finalizeMultisign(input) {
|
|
587
|
+
const { keyGen, destinationChainID, legs } = input;
|
|
588
|
+
if (legs.length === 0) {
|
|
589
|
+
throw new Error("finalizeMultisign requires at least one leg");
|
|
590
|
+
}
|
|
591
|
+
const ph = (keyGen.pubkeyhex ?? "").trim();
|
|
592
|
+
if (!ph) throw new Error("keyGen pubKey (pubkeyhex) is required");
|
|
593
|
+
const keyList = keyGen.keylist ?? [];
|
|
594
|
+
const clientId = getClientIdFromKeyGenResult(keyGen);
|
|
595
|
+
const first = legs[0];
|
|
596
|
+
const messageHashes = legs.map((l) => l.msgHash);
|
|
597
|
+
const messageRawBatch = legs.map((l) => l.msgRaw);
|
|
598
|
+
const batchMeta = legs.map((l) => ({
|
|
599
|
+
destinationAddress: l.destinationAddress,
|
|
600
|
+
signatureText: l.signatureText,
|
|
601
|
+
...l.audit
|
|
602
|
+
}));
|
|
603
|
+
const proposalTxParams = legs.map((l) => l.proposalTxParams).filter((p) => p != null && typeof p === "object");
|
|
604
|
+
const extraPayload = {
|
|
605
|
+
batchMeta,
|
|
606
|
+
...input.extraJSON ?? {}
|
|
607
|
+
};
|
|
608
|
+
const extraJSON = JSON.stringify(extraPayload, (_, v) => typeof v === "bigint" ? v.toString() : v);
|
|
609
|
+
const bodyForSign = {
|
|
610
|
+
keyList,
|
|
611
|
+
pubKey: ph,
|
|
612
|
+
msgHash: messageHashes[0],
|
|
613
|
+
msgRaw: first.msgRaw,
|
|
614
|
+
destinationChainID,
|
|
615
|
+
destinationAddress: input.destinationAddress ?? first.destinationAddress,
|
|
616
|
+
extraJSON,
|
|
617
|
+
signatureText: first.signatureText,
|
|
618
|
+
purpose: mergePurposeText(input.purposeText, input.purposeSuffix),
|
|
619
|
+
...first.feeSnapshot
|
|
620
|
+
};
|
|
621
|
+
if (legs.length > 1) {
|
|
622
|
+
bodyForSign.messageHashes = messageHashes;
|
|
623
|
+
bodyForSign.messageRawBatch = messageRawBatch;
|
|
624
|
+
}
|
|
625
|
+
if (proposalTxParams.length > 0) {
|
|
626
|
+
bodyForSign.proposalTxParams = proposalTxParams;
|
|
627
|
+
}
|
|
628
|
+
const valueWei = first.valueWei;
|
|
629
|
+
if (valueWei != null && valueWei > 0n) {
|
|
630
|
+
bodyForSign.value = valueWei.toString();
|
|
631
|
+
}
|
|
632
|
+
if (clientId) bodyForSign.clientId = clientId;
|
|
633
|
+
if (input.expiryDate != null && input.expiryDate > 0) {
|
|
634
|
+
bodyForSign.expiryDate = Math.floor(input.expiryDate);
|
|
635
|
+
}
|
|
636
|
+
return { bodyForSign, messageToSign: JSON.stringify(bodyForSign) };
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
// src/core/eip712Multisign.ts
|
|
640
|
+
var EIP712_SIGN_REQUEST_KIND = "eip712";
|
|
641
|
+
function msgHashNo0x(digest) {
|
|
642
|
+
return digest.startsWith("0x") ? digest.slice(2) : digest;
|
|
643
|
+
}
|
|
644
|
+
function eip712SignatureText(domain, primaryType) {
|
|
645
|
+
return JSON.stringify({
|
|
646
|
+
kind: "EIP-712",
|
|
647
|
+
...typeof domain.name === "string" ? { name: domain.name } : {},
|
|
648
|
+
primaryType
|
|
649
|
+
});
|
|
650
|
+
}
|
|
651
|
+
function jsonStringifyForAudit(value) {
|
|
652
|
+
return JSON.stringify(value, (_, v) => typeof v === "bigint" ? v.toString() : v);
|
|
653
|
+
}
|
|
654
|
+
function eip712LegEnvelope(typedData, delivery, digest) {
|
|
655
|
+
return {
|
|
656
|
+
version: 1,
|
|
657
|
+
digest,
|
|
658
|
+
domain: typedData.domain,
|
|
659
|
+
types: typedData.types,
|
|
660
|
+
primaryType: typedData.primaryType,
|
|
661
|
+
message: typedData.message,
|
|
662
|
+
delivery
|
|
663
|
+
};
|
|
664
|
+
}
|
|
665
|
+
function eip712MsgRawHex(envelope) {
|
|
666
|
+
return stringToHex(jsonStringifyForAudit(envelope));
|
|
667
|
+
}
|
|
668
|
+
function buildEip712Multisign(args) {
|
|
669
|
+
if (args.legs.length < 1) {
|
|
670
|
+
throw new Error("buildEip712Multisign requires at least one leg");
|
|
671
|
+
}
|
|
672
|
+
const eip712 = [];
|
|
673
|
+
const legs = args.legs.map((leg) => {
|
|
674
|
+
const digest = hashTypedData({
|
|
675
|
+
domain: leg.typedData.domain,
|
|
676
|
+
types: leg.typedData.types,
|
|
677
|
+
primaryType: leg.typedData.primaryType,
|
|
678
|
+
message: leg.typedData.message
|
|
679
|
+
});
|
|
680
|
+
const envelope = eip712LegEnvelope(leg.typedData, leg.delivery, digest);
|
|
681
|
+
eip712.push(envelope);
|
|
682
|
+
return {
|
|
683
|
+
msgHash: msgHashNo0x(digest),
|
|
684
|
+
msgRaw: eip712MsgRawHex(envelope),
|
|
685
|
+
destinationAddress: args.destinationAddress,
|
|
686
|
+
signatureText: eip712SignatureText(leg.typedData.domain, leg.typedData.primaryType),
|
|
687
|
+
audit: {
|
|
688
|
+
kind: "EIP712",
|
|
689
|
+
primaryType: leg.typedData.primaryType,
|
|
690
|
+
deliveryKind: leg.delivery.kind,
|
|
691
|
+
...leg.audit ?? {}
|
|
692
|
+
},
|
|
693
|
+
feeSnapshot: {}
|
|
694
|
+
};
|
|
695
|
+
});
|
|
696
|
+
return finalizeMultisign({
|
|
697
|
+
keyGen: args.keyGen,
|
|
698
|
+
purposeText: args.purposeText,
|
|
699
|
+
purposeSuffix: args.purposeSuffix,
|
|
700
|
+
destinationChainID: args.destinationChainID,
|
|
701
|
+
destinationAddress: args.destinationAddress,
|
|
702
|
+
extraJSON: {
|
|
703
|
+
signRequestKind: EIP712_SIGN_REQUEST_KIND,
|
|
704
|
+
eip712
|
|
705
|
+
},
|
|
706
|
+
expiryDate: args.expiryDate,
|
|
707
|
+
legs
|
|
708
|
+
});
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
// src/core/expiry.ts
|
|
712
|
+
var DEFAULT_DEFI_MULTISIGN_EXPIRY_SECONDS = 30 * 60;
|
|
713
|
+
var DEFI_PROTOCOLS_WITH_30MIN_EXPIRY = /* @__PURE__ */ new Set([
|
|
714
|
+
"uniswap-v4",
|
|
715
|
+
"gmx",
|
|
716
|
+
"hyperliquid",
|
|
717
|
+
"arcus",
|
|
718
|
+
"curve-dao",
|
|
719
|
+
"aerodrome",
|
|
720
|
+
"pendle",
|
|
721
|
+
"derive",
|
|
722
|
+
"trueo",
|
|
723
|
+
"hyperliquidOutcome"
|
|
724
|
+
]);
|
|
725
|
+
function defiMultisignExpiryUnixSeconds(protocolId, explicitExpiryDate) {
|
|
726
|
+
if (typeof explicitExpiryDate === "number" && Number.isFinite(explicitExpiryDate) && explicitExpiryDate > 0) {
|
|
727
|
+
return Math.floor(explicitExpiryDate);
|
|
728
|
+
}
|
|
729
|
+
if (DEFI_PROTOCOLS_WITH_30MIN_EXPIRY.has(protocolId)) {
|
|
730
|
+
return Math.floor(Date.now() / 1e3) + DEFAULT_DEFI_MULTISIGN_EXPIRY_SECONDS;
|
|
731
|
+
}
|
|
732
|
+
return void 0;
|
|
733
|
+
}
|
|
734
|
+
var EXCHANGE_DOMAIN = {
|
|
735
|
+
name: "Exchange",
|
|
736
|
+
version: "1",
|
|
737
|
+
chainId: 1337,
|
|
738
|
+
verifyingContract: "0x0000000000000000000000000000000000000000"
|
|
739
|
+
};
|
|
740
|
+
var AGENT_TYPES = {
|
|
741
|
+
Agent: [
|
|
742
|
+
{ name: "source", type: "string" },
|
|
743
|
+
{ name: "connectionId", type: "bytes32" }
|
|
744
|
+
]
|
|
745
|
+
};
|
|
746
|
+
function adjustMsgpackValue(value) {
|
|
747
|
+
if (Array.isArray(value)) return value.map(adjustMsgpackValue);
|
|
748
|
+
if (typeof value === "object" && value !== null) {
|
|
749
|
+
const result = {};
|
|
750
|
+
for (const key of Object.keys(value)) {
|
|
751
|
+
const entry = value[key];
|
|
752
|
+
if (entry !== void 0) result[key] = adjustMsgpackValue(entry);
|
|
753
|
+
}
|
|
754
|
+
return result;
|
|
755
|
+
}
|
|
756
|
+
if (typeof value === "number" && Number.isInteger(value) && (value >= 4294967296 || value < -2147483648)) {
|
|
757
|
+
return BigInt(value);
|
|
758
|
+
}
|
|
759
|
+
return value;
|
|
760
|
+
}
|
|
761
|
+
function toUint64Bytes(n) {
|
|
762
|
+
const bytes = new Uint8Array(8);
|
|
763
|
+
new DataView(bytes.buffer).setBigUint64(0, BigInt(n));
|
|
764
|
+
return bytes;
|
|
765
|
+
}
|
|
766
|
+
function concatBytes(...parts) {
|
|
767
|
+
const total = parts.reduce((sum, p) => sum + p.length, 0);
|
|
768
|
+
const out = new Uint8Array(total);
|
|
769
|
+
let offset = 0;
|
|
770
|
+
for (const p of parts) {
|
|
771
|
+
out.set(p, offset);
|
|
772
|
+
offset += p.length;
|
|
773
|
+
}
|
|
774
|
+
return out;
|
|
775
|
+
}
|
|
776
|
+
function hexToBytes(hex) {
|
|
777
|
+
const h = hex.startsWith("0x") ? hex.slice(2) : hex;
|
|
778
|
+
const bytes = new Uint8Array(h.length / 2);
|
|
779
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
780
|
+
bytes[i] = parseInt(h.slice(i * 2, i * 2 + 2), 16);
|
|
781
|
+
}
|
|
782
|
+
return bytes;
|
|
783
|
+
}
|
|
784
|
+
function createHyperliquidL1ActionHash(args) {
|
|
785
|
+
const { action, nonce, vaultAddress, expiresAfter } = args;
|
|
786
|
+
const actionBytes = encode(adjustMsgpackValue(action));
|
|
787
|
+
const nonceBytes = toUint64Bytes(nonce);
|
|
788
|
+
const vaultMarker = vaultAddress ? new Uint8Array([1]) : new Uint8Array([0]);
|
|
789
|
+
const vaultBytes = vaultAddress ? hexToBytes(vaultAddress) : new Uint8Array();
|
|
790
|
+
const expiresMarker = expiresAfter !== void 0 ? new Uint8Array([0]) : new Uint8Array();
|
|
791
|
+
const expiresBytes = expiresAfter !== void 0 ? toUint64Bytes(expiresAfter) : new Uint8Array();
|
|
792
|
+
const bytes = concatBytes(actionBytes, nonceBytes, vaultMarker, vaultBytes, expiresMarker, expiresBytes);
|
|
793
|
+
return keccak256(bytes);
|
|
794
|
+
}
|
|
795
|
+
function buildHyperliquidAgentTypedData(args) {
|
|
796
|
+
const connectionId = createHyperliquidL1ActionHash(args);
|
|
797
|
+
const source = args.isTestnet ? "b" : "a";
|
|
798
|
+
return {
|
|
799
|
+
domain: EXCHANGE_DOMAIN,
|
|
800
|
+
types: AGENT_TYPES,
|
|
801
|
+
primaryType: "Agent",
|
|
802
|
+
message: {
|
|
803
|
+
source,
|
|
804
|
+
connectionId
|
|
805
|
+
},
|
|
806
|
+
connectionId,
|
|
807
|
+
nonce: args.nonce,
|
|
808
|
+
action: args.action
|
|
809
|
+
};
|
|
810
|
+
}
|
|
811
|
+
function hyperliquidFloatToWire(value) {
|
|
812
|
+
const n = typeof value === "number" ? value : Number.parseFloat(String(value).trim());
|
|
813
|
+
if (!Number.isFinite(n)) throw new Error("Invalid Hyperliquid wire number.");
|
|
814
|
+
const rounded = n.toFixed(8);
|
|
815
|
+
if (Math.abs(Number.parseFloat(rounded) - n) >= 1e-12) {
|
|
816
|
+
throw new Error(`Hyperliquid wire value ${value} has too many decimal places.`);
|
|
817
|
+
}
|
|
818
|
+
const normalized = Number(rounded === "-0" ? "0" : rounded);
|
|
819
|
+
return String(normalized);
|
|
820
|
+
}
|
|
821
|
+
function hyperliquidL1TifFromKey(tif) {
|
|
822
|
+
const key = String(tif ?? "gtc").trim().toLowerCase();
|
|
823
|
+
if (key === "alo") return "Alo";
|
|
824
|
+
if (key === "ioc") return "Ioc";
|
|
825
|
+
if (key === "gtc") return "Gtc";
|
|
826
|
+
throw new Error(`Unknown time-in-force: ${tif}. Use alo, gtc, or ioc.`);
|
|
827
|
+
}
|
|
828
|
+
function buildHyperliquidLimitOrderWire(args) {
|
|
829
|
+
return {
|
|
830
|
+
a: args.asset,
|
|
831
|
+
b: args.isBuy,
|
|
832
|
+
p: hyperliquidFloatToWire(args.limitPxHuman),
|
|
833
|
+
s: hyperliquidFloatToWire(args.szHuman),
|
|
834
|
+
r: Boolean(args.reduceOnly),
|
|
835
|
+
t: { limit: { tif: hyperliquidL1TifFromKey(args.tif) } }
|
|
836
|
+
};
|
|
837
|
+
}
|
|
838
|
+
function buildHyperliquidOrderAction(args) {
|
|
839
|
+
return {
|
|
840
|
+
type: "order",
|
|
841
|
+
orders: args.orders,
|
|
842
|
+
grouping: args.grouping ?? "na"
|
|
843
|
+
};
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
// src/protocols/evm/hyperliquidOutcome/multisign.ts
|
|
847
|
+
var ZERO_ADDRESS = "0x0000000000000000000000000000000000000000";
|
|
848
|
+
function assertOutcomeChain(chainId) {
|
|
849
|
+
if (!isHyperliquidChainSupported(chainId)) {
|
|
850
|
+
throw new Error(`Hyperliquid outcome markets require chainId 999 or 998 (got ${chainId}).`);
|
|
851
|
+
}
|
|
852
|
+
}
|
|
853
|
+
function outcomeExchangeLeg(args) {
|
|
854
|
+
const isTestnet = args.chainId === 998;
|
|
855
|
+
const typed = buildHyperliquidAgentTypedData({
|
|
856
|
+
action: args.action,
|
|
857
|
+
nonce: args.nonce,
|
|
858
|
+
isTestnet
|
|
859
|
+
});
|
|
860
|
+
return {
|
|
861
|
+
typedData: {
|
|
862
|
+
domain: typed.domain,
|
|
863
|
+
types: typed.types,
|
|
864
|
+
primaryType: typed.primaryType,
|
|
865
|
+
message: typed.message
|
|
866
|
+
},
|
|
867
|
+
delivery: {
|
|
868
|
+
kind: "hyperliquid_exchange",
|
|
869
|
+
chainId: args.chainId,
|
|
870
|
+
isTestnet,
|
|
871
|
+
action: args.action,
|
|
872
|
+
nonce: args.nonce
|
|
873
|
+
},
|
|
874
|
+
audit: {
|
|
875
|
+
...args.audit,
|
|
876
|
+
nonce: args.nonce,
|
|
877
|
+
connectionId: typed.connectionId
|
|
878
|
+
}
|
|
879
|
+
};
|
|
880
|
+
}
|
|
881
|
+
function finishOutcomeMultisign(args, leg) {
|
|
882
|
+
assertOutcomeChain(args.chainId);
|
|
883
|
+
const expiryDate = defiMultisignExpiryUnixSeconds(HYPERLIQUID_OUTCOME_PROTOCOL_ID, args.expiryDate);
|
|
884
|
+
return buildEip712Multisign({
|
|
885
|
+
keyGen: args.keyGen,
|
|
886
|
+
purposeText: args.purposeText,
|
|
887
|
+
destinationChainID: String(args.chainId),
|
|
888
|
+
destinationAddress: ZERO_ADDRESS,
|
|
889
|
+
expiryDate,
|
|
890
|
+
legs: [leg]
|
|
891
|
+
});
|
|
892
|
+
}
|
|
893
|
+
function buildHyperliquidOutcomeOrderAction(args) {
|
|
894
|
+
const encoding = outcomeEncoding(args.outcome, outcomeSideIndex(args.side));
|
|
895
|
+
const asset = outcomeAssetId(encoding);
|
|
896
|
+
return {
|
|
897
|
+
encoding,
|
|
898
|
+
coin: outcomeTradeCoin(encoding),
|
|
899
|
+
asset,
|
|
900
|
+
action: buildHyperliquidOrderAction({
|
|
901
|
+
orders: [
|
|
902
|
+
buildHyperliquidLimitOrderWire({
|
|
903
|
+
asset,
|
|
904
|
+
isBuy: args.isBuy,
|
|
905
|
+
limitPxHuman: args.limitPxHuman,
|
|
906
|
+
szHuman: args.szHuman,
|
|
907
|
+
tif: args.tif,
|
|
908
|
+
reduceOnly: args.reduceOnly
|
|
909
|
+
})
|
|
910
|
+
],
|
|
911
|
+
grouping: "na"
|
|
912
|
+
})
|
|
913
|
+
};
|
|
914
|
+
}
|
|
915
|
+
async function buildEvmMultisignBodyHyperliquidOutcomeOrder(args) {
|
|
916
|
+
const built = buildHyperliquidOutcomeOrderAction(args);
|
|
917
|
+
const nonce = args.nonce ?? Date.now();
|
|
918
|
+
return finishOutcomeMultisign(
|
|
919
|
+
args,
|
|
920
|
+
outcomeExchangeLeg({
|
|
921
|
+
action: built.action,
|
|
922
|
+
nonce,
|
|
923
|
+
chainId: args.chainId,
|
|
924
|
+
audit: {
|
|
925
|
+
protocol: HYPERLIQUID_OUTCOME_PROTOCOL_ID,
|
|
926
|
+
action: "order",
|
|
927
|
+
outcome: args.outcome,
|
|
928
|
+
side: args.side,
|
|
929
|
+
coin: built.coin,
|
|
930
|
+
asset: built.asset,
|
|
931
|
+
isBuy: args.isBuy,
|
|
932
|
+
limitPxHuman: args.limitPxHuman,
|
|
933
|
+
szHuman: args.szHuman
|
|
934
|
+
}
|
|
935
|
+
})
|
|
936
|
+
);
|
|
937
|
+
}
|
|
938
|
+
function buildHyperliquidOutcomeCancelAction(args) {
|
|
939
|
+
const encoding = outcomeEncoding(args.outcome, outcomeSideIndex(args.side));
|
|
940
|
+
const asset = outcomeAssetId(encoding);
|
|
941
|
+
const oid = Math.trunc(args.oid);
|
|
942
|
+
if (!Number.isFinite(oid) || oid <= 0) throw new Error("oid must be a positive order id");
|
|
943
|
+
return {
|
|
944
|
+
encoding,
|
|
945
|
+
coin: outcomeTradeCoin(encoding),
|
|
946
|
+
asset,
|
|
947
|
+
action: {
|
|
948
|
+
type: "cancel",
|
|
949
|
+
cancels: [{ a: asset, o: oid }]
|
|
950
|
+
}
|
|
951
|
+
};
|
|
952
|
+
}
|
|
953
|
+
async function buildEvmMultisignBodyHyperliquidOutcomeCancel(args) {
|
|
954
|
+
const built = buildHyperliquidOutcomeCancelAction(args);
|
|
955
|
+
const nonce = args.nonce ?? Date.now();
|
|
956
|
+
return finishOutcomeMultisign(
|
|
957
|
+
args,
|
|
958
|
+
outcomeExchangeLeg({
|
|
959
|
+
action: built.action,
|
|
960
|
+
nonce,
|
|
961
|
+
chainId: args.chainId,
|
|
962
|
+
audit: {
|
|
963
|
+
protocol: HYPERLIQUID_OUTCOME_PROTOCOL_ID,
|
|
964
|
+
action: "cancel",
|
|
965
|
+
outcome: args.outcome,
|
|
966
|
+
side: args.side,
|
|
967
|
+
oid: args.oid,
|
|
968
|
+
coin: built.coin,
|
|
969
|
+
asset: built.asset
|
|
970
|
+
}
|
|
971
|
+
})
|
|
972
|
+
);
|
|
973
|
+
}
|
|
974
|
+
function positiveWireAmount(amountHuman, label) {
|
|
975
|
+
const wire = hyperliquidFloatToWire(amountHuman);
|
|
976
|
+
if (Number.parseFloat(wire) <= 0) throw new Error(`${label} must be greater than zero`);
|
|
977
|
+
return wire;
|
|
978
|
+
}
|
|
979
|
+
function buildHyperliquidOutcomeSplitAction(args) {
|
|
980
|
+
const outcome = Math.trunc(args.outcome);
|
|
981
|
+
outcomeEncoding(outcome, 0);
|
|
982
|
+
return {
|
|
983
|
+
type: "userOutcome",
|
|
984
|
+
splitOutcome: { outcome, amount: positiveWireAmount(args.amountHuman, "amount") }
|
|
985
|
+
};
|
|
986
|
+
}
|
|
987
|
+
async function buildEvmMultisignBodyHyperliquidOutcomeSplit(args) {
|
|
988
|
+
const action = buildHyperliquidOutcomeSplitAction(args);
|
|
989
|
+
const nonce = args.nonce ?? Date.now();
|
|
990
|
+
return finishOutcomeMultisign(
|
|
991
|
+
args,
|
|
992
|
+
outcomeExchangeLeg({
|
|
993
|
+
action,
|
|
994
|
+
nonce,
|
|
995
|
+
chainId: args.chainId,
|
|
996
|
+
audit: {
|
|
997
|
+
protocol: HYPERLIQUID_OUTCOME_PROTOCOL_ID,
|
|
998
|
+
action: "split",
|
|
999
|
+
outcome: action.splitOutcome.outcome,
|
|
1000
|
+
amount: action.splitOutcome.amount
|
|
1001
|
+
}
|
|
1002
|
+
})
|
|
1003
|
+
);
|
|
1004
|
+
}
|
|
1005
|
+
function buildHyperliquidOutcomeMergeAction(args) {
|
|
1006
|
+
const outcome = Math.trunc(args.outcome);
|
|
1007
|
+
outcomeEncoding(outcome, 0);
|
|
1008
|
+
const amount = args.amountMax || !args.amountHuman?.trim() ? null : positiveWireAmount(args.amountHuman, "amount");
|
|
1009
|
+
return {
|
|
1010
|
+
type: "userOutcome",
|
|
1011
|
+
mergeOutcome: { outcome, amount }
|
|
1012
|
+
};
|
|
1013
|
+
}
|
|
1014
|
+
async function buildEvmMultisignBodyHyperliquidOutcomeMerge(args) {
|
|
1015
|
+
const action = buildHyperliquidOutcomeMergeAction(args);
|
|
1016
|
+
const nonce = args.nonce ?? Date.now();
|
|
1017
|
+
return finishOutcomeMultisign(
|
|
1018
|
+
args,
|
|
1019
|
+
outcomeExchangeLeg({
|
|
1020
|
+
action,
|
|
1021
|
+
nonce,
|
|
1022
|
+
chainId: args.chainId,
|
|
1023
|
+
audit: {
|
|
1024
|
+
protocol: HYPERLIQUID_OUTCOME_PROTOCOL_ID,
|
|
1025
|
+
action: "merge",
|
|
1026
|
+
outcome: action.mergeOutcome.outcome,
|
|
1027
|
+
amount: action.mergeOutcome.amount
|
|
1028
|
+
}
|
|
1029
|
+
})
|
|
1030
|
+
);
|
|
1031
|
+
}
|
|
1032
|
+
function buildHyperliquidOutcomeMergeQuestionAction(args) {
|
|
1033
|
+
const question = Math.trunc(args.question);
|
|
1034
|
+
if (!Number.isFinite(question) || question < 0) throw new Error("question must be a non-negative integer");
|
|
1035
|
+
const amount = args.amountMax || !args.amountHuman?.trim() ? null : positiveWireAmount(args.amountHuman, "amount");
|
|
1036
|
+
return {
|
|
1037
|
+
type: "userOutcome",
|
|
1038
|
+
mergeQuestion: { question, amount }
|
|
1039
|
+
};
|
|
1040
|
+
}
|
|
1041
|
+
async function buildEvmMultisignBodyHyperliquidOutcomeMergeQuestion(args) {
|
|
1042
|
+
const action = buildHyperliquidOutcomeMergeQuestionAction(args);
|
|
1043
|
+
const nonce = args.nonce ?? Date.now();
|
|
1044
|
+
return finishOutcomeMultisign(
|
|
1045
|
+
args,
|
|
1046
|
+
outcomeExchangeLeg({
|
|
1047
|
+
action,
|
|
1048
|
+
nonce,
|
|
1049
|
+
chainId: args.chainId,
|
|
1050
|
+
audit: {
|
|
1051
|
+
protocol: HYPERLIQUID_OUTCOME_PROTOCOL_ID,
|
|
1052
|
+
action: "mergeQuestion",
|
|
1053
|
+
question: action.mergeQuestion.question,
|
|
1054
|
+
amount: action.mergeQuestion.amount
|
|
1055
|
+
}
|
|
1056
|
+
})
|
|
1057
|
+
);
|
|
1058
|
+
}
|
|
1059
|
+
function buildHyperliquidOutcomeNegateAction(args) {
|
|
1060
|
+
const question = Math.trunc(args.question);
|
|
1061
|
+
const outcome = Math.trunc(args.outcome);
|
|
1062
|
+
if (!Number.isFinite(question) || question < 0) throw new Error("question must be a non-negative integer");
|
|
1063
|
+
outcomeEncoding(outcome, 0);
|
|
1064
|
+
return {
|
|
1065
|
+
type: "userOutcome",
|
|
1066
|
+
negateOutcome: {
|
|
1067
|
+
question,
|
|
1068
|
+
outcome,
|
|
1069
|
+
amount: positiveWireAmount(args.amountHuman, "amount")
|
|
1070
|
+
}
|
|
1071
|
+
};
|
|
1072
|
+
}
|
|
1073
|
+
async function buildEvmMultisignBodyHyperliquidOutcomeNegate(args) {
|
|
1074
|
+
const action = buildHyperliquidOutcomeNegateAction(args);
|
|
1075
|
+
const nonce = args.nonce ?? Date.now();
|
|
1076
|
+
return finishOutcomeMultisign(
|
|
1077
|
+
args,
|
|
1078
|
+
outcomeExchangeLeg({
|
|
1079
|
+
action,
|
|
1080
|
+
nonce,
|
|
1081
|
+
chainId: args.chainId,
|
|
1082
|
+
audit: {
|
|
1083
|
+
protocol: HYPERLIQUID_OUTCOME_PROTOCOL_ID,
|
|
1084
|
+
action: "negate",
|
|
1085
|
+
question: action.negateOutcome.question,
|
|
1086
|
+
outcome: action.negateOutcome.outcome,
|
|
1087
|
+
amount: action.negateOutcome.amount
|
|
1088
|
+
}
|
|
1089
|
+
})
|
|
1090
|
+
);
|
|
1091
|
+
}
|
|
1092
|
+
|
|
1093
|
+
// src/protocols/evm/hyperliquidOutcome/mcpSummaries.ts
|
|
1094
|
+
function hyperliquidOutcomeToPredictionFields(market, chainId) {
|
|
1095
|
+
return {
|
|
1096
|
+
venue: "hyperliquid",
|
|
1097
|
+
venueMarketId: String(market.outcome),
|
|
1098
|
+
question: market.question,
|
|
1099
|
+
categories: market.categories,
|
|
1100
|
+
yesPrice: market.yesPrice,
|
|
1101
|
+
noPrice: market.noPrice,
|
|
1102
|
+
yesPriceLabel: market.yesPriceLabel,
|
|
1103
|
+
noPriceLabel: market.noPriceLabel,
|
|
1104
|
+
volume: "\u2014",
|
|
1105
|
+
tvl: null,
|
|
1106
|
+
expiresAt: market.expiresAt,
|
|
1107
|
+
expiresAtLabel: market.expiresAt,
|
|
1108
|
+
tradingEndsLabel: market.expiresAt ? `Settles ${market.expiresAt}` : "Settlement time not listed",
|
|
1109
|
+
endOfTrading: market.endOfTrading,
|
|
1110
|
+
status: market.status,
|
|
1111
|
+
outcome: market.outcome,
|
|
1112
|
+
statusUpdatedAt: null,
|
|
1113
|
+
resolutionLabel: null,
|
|
1114
|
+
chainId,
|
|
1115
|
+
paymentSymbol: market.quoteToken,
|
|
1116
|
+
tradable: market.tradable,
|
|
1117
|
+
rules: market.rules,
|
|
1118
|
+
source: market.name
|
|
1119
|
+
};
|
|
1120
|
+
}
|
|
1121
|
+
async function hyperliquidOutcomeFetchMarketsSummary(input) {
|
|
1122
|
+
const chainId = input.chainId ?? HYPERLIQUID_OUTCOME_MAINNET_CHAIN_ID;
|
|
1123
|
+
const { markets } = await listHyperliquidOutcomeMarkets({ chainId, query: input.query });
|
|
1124
|
+
const offset = Math.max(input.offset ?? 0, 0);
|
|
1125
|
+
const limit = input.limit ?? 20;
|
|
1126
|
+
const page = markets.slice(offset, offset + limit);
|
|
1127
|
+
const nextOffset = offset + page.length;
|
|
1128
|
+
return {
|
|
1129
|
+
markets: page,
|
|
1130
|
+
hasMore: nextOffset < markets.length,
|
|
1131
|
+
nextOffset,
|
|
1132
|
+
notes: page.length ? `${page.length} Hyperliquid outcome market(s). Yes price is the implied probability. Writes need load_defi_protocol hyperliquidOutcome.` : "No Hyperliquid outcome markets matched."
|
|
1133
|
+
};
|
|
1134
|
+
}
|
|
1135
|
+
async function hyperliquidOutcomeFetchMarketSummary(input) {
|
|
1136
|
+
const chainId = input.chainId ?? HYPERLIQUID_OUTCOME_MAINNET_CHAIN_ID;
|
|
1137
|
+
const market = await getHyperliquidOutcomeMarket({ chainId, outcomeId: input.outcome });
|
|
1138
|
+
const [yesBook, noBook] = await Promise.all([
|
|
1139
|
+
fetchHyperliquidOutcomeBook({ chainId, coin: market.yesCoin }),
|
|
1140
|
+
fetchHyperliquidOutcomeBook({ chainId, coin: market.noCoin })
|
|
1141
|
+
]);
|
|
1142
|
+
return {
|
|
1143
|
+
...market,
|
|
1144
|
+
yesBook,
|
|
1145
|
+
noBook,
|
|
1146
|
+
notes: "Books are HyperCore l2Book for the #encoding coins. Do not add Yes/No shares to the node token list."
|
|
1147
|
+
};
|
|
1148
|
+
}
|
|
1149
|
+
async function hyperliquidOutcomeFetchOrderbookSummary(input) {
|
|
1150
|
+
const chainId = input.chainId ?? HYPERLIQUID_OUTCOME_MAINNET_CHAIN_ID;
|
|
1151
|
+
const side = input.side ?? "yes";
|
|
1152
|
+
const market = await getHyperliquidOutcomeMarket({ chainId, outcomeId: input.outcome });
|
|
1153
|
+
const coin = side === "no" ? market.noCoin : market.yesCoin;
|
|
1154
|
+
const book = await fetchHyperliquidOutcomeBook({ chainId, coin });
|
|
1155
|
+
return {
|
|
1156
|
+
outcome: market.outcome,
|
|
1157
|
+
side,
|
|
1158
|
+
book,
|
|
1159
|
+
notes: `${side} book for ${coin}.`
|
|
1160
|
+
};
|
|
1161
|
+
}
|
|
1162
|
+
async function hyperliquidOutcomeFetchPositionsSummary(input) {
|
|
1163
|
+
const chainId = input.chainId ?? HYPERLIQUID_OUTCOME_MAINNET_CHAIN_ID;
|
|
1164
|
+
const account = await fetchHyperliquidOutcomeAccount({ chainId, user: input.user });
|
|
1165
|
+
return {
|
|
1166
|
+
quoteBalances: account.quoteBalances,
|
|
1167
|
+
outcomeBalances: account.outcomeBalances,
|
|
1168
|
+
notes: "Outcome shares are HyperCore balances (+encoding), not ERC20s. Do not call add_to_token_registry."
|
|
1169
|
+
};
|
|
1170
|
+
}
|
|
1171
|
+
async function hyperliquidOutcomeFetchOpenOrdersSummary(input) {
|
|
1172
|
+
const chainId = input.chainId ?? HYPERLIQUID_OUTCOME_MAINNET_CHAIN_ID;
|
|
1173
|
+
const account = await fetchHyperliquidOutcomeAccount({ chainId, user: input.user });
|
|
1174
|
+
return {
|
|
1175
|
+
openOrders: account.openOrders,
|
|
1176
|
+
notes: account.openOrders.length ? "Cancel with ctm_hyperliquid_outcome_build_cancel_multisign using outcome, side, and oid." : "No open Hyperliquid outcome orders."
|
|
1177
|
+
};
|
|
1178
|
+
}
|
|
1179
|
+
|
|
1180
|
+
// src/protocols/evm/hyperliquidOutcome/index.ts
|
|
1181
|
+
var hyperliquidOutcomeProtocolModule = {
|
|
1182
|
+
id: HYPERLIQUID_OUTCOME_PROTOCOL_ID,
|
|
1183
|
+
chainCategory: "evm",
|
|
1184
|
+
isChainSupported(ctx) {
|
|
1185
|
+
if (ctx.chainCategory !== "evm") return false;
|
|
1186
|
+
const chainId = typeof ctx.chainId === "number" ? ctx.chainId : Number(ctx.chainId);
|
|
1187
|
+
return Number.isFinite(chainId) && isHyperliquidChainSupported(chainId);
|
|
1188
|
+
},
|
|
1189
|
+
isTokenSupported(token) {
|
|
1190
|
+
return token.category === "evm" && token.kind === "native";
|
|
1191
|
+
},
|
|
1192
|
+
actions: [
|
|
1193
|
+
{
|
|
1194
|
+
id: "hyperliquidOutcome.order",
|
|
1195
|
+
protocolId: HYPERLIQUID_OUTCOME_PROTOCOL_ID,
|
|
1196
|
+
chainCategory: "evm",
|
|
1197
|
+
description: "Limit buy or sell a HIP-4 outcome side via Hyperliquid /exchange",
|
|
1198
|
+
commonParams: ["keyGen", "purposeText"],
|
|
1199
|
+
params: {}
|
|
1200
|
+
},
|
|
1201
|
+
{
|
|
1202
|
+
id: "hyperliquidOutcome.cancel",
|
|
1203
|
+
protocolId: HYPERLIQUID_OUTCOME_PROTOCOL_ID,
|
|
1204
|
+
chainCategory: "evm",
|
|
1205
|
+
description: "Cancel a resting HIP-4 outcome order",
|
|
1206
|
+
commonParams: ["keyGen", "purposeText"],
|
|
1207
|
+
params: {}
|
|
1208
|
+
},
|
|
1209
|
+
{
|
|
1210
|
+
id: "hyperliquidOutcome.split",
|
|
1211
|
+
protocolId: HYPERLIQUID_OUTCOME_PROTOCOL_ID,
|
|
1212
|
+
chainCategory: "evm",
|
|
1213
|
+
description: "Split quote tokens into Yes and No shares",
|
|
1214
|
+
commonParams: ["keyGen", "purposeText"],
|
|
1215
|
+
params: {}
|
|
1216
|
+
},
|
|
1217
|
+
{
|
|
1218
|
+
id: "hyperliquidOutcome.merge",
|
|
1219
|
+
protocolId: HYPERLIQUID_OUTCOME_PROTOCOL_ID,
|
|
1220
|
+
chainCategory: "evm",
|
|
1221
|
+
description: "Merge Yes and No shares back into quote tokens",
|
|
1222
|
+
commonParams: ["keyGen", "purposeText"],
|
|
1223
|
+
params: {}
|
|
1224
|
+
},
|
|
1225
|
+
{
|
|
1226
|
+
id: "hyperliquidOutcome.merge-question",
|
|
1227
|
+
protocolId: HYPERLIQUID_OUTCOME_PROTOCOL_ID,
|
|
1228
|
+
chainCategory: "evm",
|
|
1229
|
+
description: "Merge Yes shares across a question into quote tokens",
|
|
1230
|
+
commonParams: ["keyGen", "purposeText"],
|
|
1231
|
+
params: {}
|
|
1232
|
+
},
|
|
1233
|
+
{
|
|
1234
|
+
id: "hyperliquidOutcome.negate",
|
|
1235
|
+
protocolId: HYPERLIQUID_OUTCOME_PROTOCOL_ID,
|
|
1236
|
+
chainCategory: "evm",
|
|
1237
|
+
description: "Convert No shares of one outcome into Yes shares of the other outcomes",
|
|
1238
|
+
commonParams: ["keyGen", "purposeText"],
|
|
1239
|
+
params: {}
|
|
1240
|
+
}
|
|
1241
|
+
]
|
|
1242
|
+
};
|
|
1243
|
+
registerProtocolModule(hyperliquidOutcomeProtocolModule);
|
|
1244
|
+
|
|
1245
|
+
export { HYPERLIQUID_OUTCOME_MAINNET_CHAIN_ID, HYPERLIQUID_OUTCOME_PROTOCOL_ID, buildEvmMultisignBodyHyperliquidOutcomeCancel, buildEvmMultisignBodyHyperliquidOutcomeMerge, buildEvmMultisignBodyHyperliquidOutcomeMergeQuestion, buildEvmMultisignBodyHyperliquidOutcomeNegate, buildEvmMultisignBodyHyperliquidOutcomeOrder, buildEvmMultisignBodyHyperliquidOutcomeSplit, buildHyperliquidOutcomeCancelAction, buildHyperliquidOutcomeMergeAction, buildHyperliquidOutcomeMergeQuestionAction, buildHyperliquidOutcomeNegateAction, buildHyperliquidOutcomeOrderAction, buildHyperliquidOutcomePurposePrefill, buildHyperliquidOutcomeSplitAction, clearHyperliquidOutcomeMetaCache, fetchHyperliquidOutcomeAccount, fetchHyperliquidOutcomeBook, fetchHyperliquidOutcomeChanceSeries, fillOutcomeTemplate, formatHyperliquidStamp, formatOutcomeQuestion, formatOutcomeRules, getHyperliquidOutcomeMarket, hyperliquidOutcomeFetchMarketSummary, hyperliquidOutcomeFetchMarketsSummary, hyperliquidOutcomeFetchOpenOrdersSummary, hyperliquidOutcomeFetchOrderbookSummary, hyperliquidOutcomeFetchPositionsSummary, hyperliquidOutcomeMatchesQuery, hyperliquidOutcomeProtocolModule, hyperliquidOutcomeToPredictionFields, listHyperliquidOutcomeMarkets, outcomeAssetId, outcomeBalanceCoin, outcomeEncoding, outcomeExpiryUnix, outcomeFieldMap, outcomeSideIndex, outcomeSideName, outcomeTemplateId, outcomeTradeCoin, parseHyperliquidPipeFields, parseHyperliquidStamp, questionForOutcome, resolveOutcomeSideLabel };
|
|
1246
|
+
//# sourceMappingURL=index.js.map
|
|
1247
|
+
//# sourceMappingURL=index.js.map
|