pmxt-core 2.37.14 → 2.38.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/dist/exchanges/hyperliquid/auth.d.ts +30 -0
  2. package/dist/exchanges/hyperliquid/auth.js +145 -0
  3. package/dist/exchanges/hyperliquid/config.d.ts +17 -0
  4. package/dist/exchanges/hyperliquid/config.js +28 -0
  5. package/dist/exchanges/hyperliquid/errors.d.ts +18 -0
  6. package/dist/exchanges/hyperliquid/errors.js +61 -0
  7. package/dist/exchanges/hyperliquid/fetcher.d.ts +140 -0
  8. package/dist/exchanges/hyperliquid/fetcher.js +137 -0
  9. package/dist/exchanges/hyperliquid/index.d.ts +31 -0
  10. package/dist/exchanges/hyperliquid/index.js +219 -0
  11. package/dist/exchanges/hyperliquid/normalizer.d.ts +18 -0
  12. package/dist/exchanges/hyperliquid/normalizer.js +339 -0
  13. package/dist/exchanges/hyperliquid/utils.d.ts +41 -0
  14. package/dist/exchanges/hyperliquid/utils.js +76 -0
  15. package/dist/exchanges/kalshi/api.d.ts +1 -1
  16. package/dist/exchanges/kalshi/api.js +1 -1
  17. package/dist/exchanges/limitless/api.d.ts +1 -1
  18. package/dist/exchanges/limitless/api.js +1 -1
  19. package/dist/exchanges/myriad/api.d.ts +1 -1
  20. package/dist/exchanges/myriad/api.js +1 -1
  21. package/dist/exchanges/opinion/api.d.ts +1 -1
  22. package/dist/exchanges/opinion/api.js +1 -1
  23. package/dist/exchanges/polymarket/api-clob.d.ts +1 -1
  24. package/dist/exchanges/polymarket/api-clob.js +1 -1
  25. package/dist/exchanges/polymarket/api-data.d.ts +1 -1
  26. package/dist/exchanges/polymarket/api-data.js +1 -1
  27. package/dist/exchanges/polymarket/api-gamma.d.ts +1 -1
  28. package/dist/exchanges/polymarket/api-gamma.js +1 -1
  29. package/dist/exchanges/probable/api.d.ts +1 -1
  30. package/dist/exchanges/probable/api.js +1 -1
  31. package/dist/index.d.ts +4 -0
  32. package/dist/index.js +5 -1
  33. package/dist/server/exchange-factory.js +6 -0
  34. package/dist/server/openapi.yaml +15 -0
  35. package/package.json +4 -3
@@ -0,0 +1,30 @@
1
+ import { ExchangeCredentials } from '../../BaseExchange';
2
+ export interface HyperliquidSignature {
3
+ r: string;
4
+ s: string;
5
+ v: number;
6
+ }
7
+ export declare function floatToWire(x: number): string;
8
+ export declare class HyperliquidAuth {
9
+ private readonly wallet;
10
+ private readonly isMainnet;
11
+ constructor(credentials: ExchangeCredentials, testnet: boolean);
12
+ getAddress(): string;
13
+ /**
14
+ * Sign an L1 action using the phantom agent EIP-712 scheme.
15
+ *
16
+ * Flow:
17
+ * 1. msgpack-encode the action
18
+ * 2. Append nonce (8 bytes BE) + vault marker
19
+ * 3. keccak256 -> connectionId
20
+ * 4. EIP-712 sign {source, connectionId} with domain "Exchange"
21
+ */
22
+ signL1Action(action: Record<string, unknown>, vaultAddress?: string | null, nonce?: number): Promise<{
23
+ signature: HyperliquidSignature;
24
+ nonce: number;
25
+ }>;
26
+ /**
27
+ * Build and sign a complete exchange request body.
28
+ */
29
+ signExchangeRequest(action: Record<string, unknown>, vaultAddress?: string | null): Promise<Record<string, unknown>>;
30
+ }
@@ -0,0 +1,145 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.HyperliquidAuth = void 0;
4
+ exports.floatToWire = floatToWire;
5
+ const ethers_1 = require("ethers");
6
+ const msgpackr_1 = require("msgpackr");
7
+ const errors_1 = require("../../errors");
8
+ const config_1 = require("./config");
9
+ // Standard msgpack encoder — variableMapSize ensures fixmap/fixarray encoding
10
+ // which matches the Python/Rust msgpack libraries that Hyperliquid's server uses.
11
+ const packr = new msgpackr_1.Packr({ useRecords: false, variableMapSize: true });
12
+ // ----------------------------------------------------------------------------
13
+ // EIP-712 domain and types for Hyperliquid L1 action signing
14
+ // ----------------------------------------------------------------------------
15
+ const EIP712_DOMAIN = {
16
+ name: 'Exchange',
17
+ version: '1',
18
+ chainId: config_1.EXCHANGE_CHAIN_ID,
19
+ verifyingContract: '0x0000000000000000000000000000000000000000',
20
+ };
21
+ const AGENT_TYPES = {
22
+ Agent: [
23
+ { name: 'source', type: 'string' },
24
+ { name: 'connectionId', type: 'bytes32' },
25
+ ],
26
+ };
27
+ // ----------------------------------------------------------------------------
28
+ // msgpack helpers -- Hyperliquid requires int64 encoding for large integers
29
+ // ----------------------------------------------------------------------------
30
+ function convertLargeInts(obj) {
31
+ if (typeof obj === 'number' && Number.isInteger(obj) &&
32
+ (obj >= 0x100000000 || obj < -0x80000000)) {
33
+ return BigInt(obj);
34
+ }
35
+ if (Array.isArray(obj)) {
36
+ return obj.map(convertLargeInts);
37
+ }
38
+ if (typeof obj === 'object' && obj !== null) {
39
+ const result = {};
40
+ for (const [key, val] of Object.entries(obj)) {
41
+ if (val !== undefined) {
42
+ result[key] = convertLargeInts(val);
43
+ }
44
+ }
45
+ return result;
46
+ }
47
+ return obj;
48
+ }
49
+ // ----------------------------------------------------------------------------
50
+ // Action hash -- constructs the connectionId for the phantom agent
51
+ // ----------------------------------------------------------------------------
52
+ function computeActionHash(action, vaultAddress, nonce) {
53
+ // 1. msgpack-encode the action (large ints as int64)
54
+ const actionBytes = packr.pack(convertLargeInts(action));
55
+ // 2. nonce as 8 bytes big-endian
56
+ const nonceBytes = Buffer.alloc(8);
57
+ nonceBytes.writeBigUInt64BE(BigInt(nonce));
58
+ // 3. vault address marker
59
+ const parts = [Buffer.from(actionBytes), nonceBytes];
60
+ if (vaultAddress) {
61
+ parts.push(Buffer.from([0x01]));
62
+ parts.push(Buffer.from(vaultAddress.replace('0x', ''), 'hex'));
63
+ }
64
+ else {
65
+ parts.push(Buffer.from([0x00]));
66
+ }
67
+ // 4. keccak256 of concatenated bytes
68
+ return ethers_1.utils.keccak256(Buffer.concat(parts));
69
+ }
70
+ // ----------------------------------------------------------------------------
71
+ // Price/size formatting -- must match Hyperliquid's wire format
72
+ // ----------------------------------------------------------------------------
73
+ function floatToWire(x) {
74
+ const rounded = x.toFixed(8);
75
+ if (Math.abs(parseFloat(rounded) - x) >= 1e-12) {
76
+ throw new Error(`floatToWire causes rounding: ${x}`);
77
+ }
78
+ return parseFloat(rounded).toString();
79
+ }
80
+ // ----------------------------------------------------------------------------
81
+ // Auth class
82
+ // ----------------------------------------------------------------------------
83
+ class HyperliquidAuth {
84
+ wallet;
85
+ isMainnet;
86
+ constructor(credentials, testnet) {
87
+ if (!credentials.privateKey) {
88
+ throw new errors_1.AuthenticationError('Hyperliquid trading requires a privateKey for EIP-712 signing.', 'Hyperliquid');
89
+ }
90
+ let privateKey = credentials.privateKey;
91
+ if (privateKey.includes('\\n')) {
92
+ privateKey = privateKey.replace(/\\n/g, '\n');
93
+ }
94
+ const stripped = privateKey.startsWith('0x') ? privateKey.slice(2) : privateKey;
95
+ if (!/^[0-9a-fA-F]{64}$/.test(stripped)) {
96
+ throw new errors_1.AuthenticationError('Invalid private key format. Hyperliquid requires a 32-byte hex EVM private key (e.g. 0xabc123...).', 'Hyperliquid');
97
+ }
98
+ this.wallet = new ethers_1.Wallet(privateKey);
99
+ this.isMainnet = !testnet;
100
+ }
101
+ getAddress() {
102
+ return this.wallet.address;
103
+ }
104
+ /**
105
+ * Sign an L1 action using the phantom agent EIP-712 scheme.
106
+ *
107
+ * Flow:
108
+ * 1. msgpack-encode the action
109
+ * 2. Append nonce (8 bytes BE) + vault marker
110
+ * 3. keccak256 -> connectionId
111
+ * 4. EIP-712 sign {source, connectionId} with domain "Exchange"
112
+ */
113
+ async signL1Action(action, vaultAddress = null, nonce = Date.now()) {
114
+ const connectionId = computeActionHash(action, vaultAddress, nonce);
115
+ const message = {
116
+ source: this.isMainnet ? 'a' : 'b',
117
+ connectionId,
118
+ };
119
+ // ethers v5 uses _signTypedData (underscore prefix)
120
+ const sig = await this.wallet._signTypedData(EIP712_DOMAIN, AGENT_TYPES, message);
121
+ const split = ethers_1.utils.splitSignature(sig);
122
+ return {
123
+ signature: {
124
+ r: split.r,
125
+ s: split.s,
126
+ v: split.v,
127
+ },
128
+ nonce,
129
+ };
130
+ }
131
+ /**
132
+ * Build and sign a complete exchange request body.
133
+ */
134
+ async signExchangeRequest(action, vaultAddress = null) {
135
+ const nonce = Date.now();
136
+ const { signature } = await this.signL1Action(action, vaultAddress, nonce);
137
+ return {
138
+ action,
139
+ nonce,
140
+ signature,
141
+ vaultAddress,
142
+ };
143
+ }
144
+ }
145
+ exports.HyperliquidAuth = HyperliquidAuth;
@@ -0,0 +1,17 @@
1
+ export declare const DEFAULT_HYPERLIQUID_BASE_URL = "https://api.hyperliquid.xyz";
2
+ export declare const HYPERLIQUID_TESTNET_BASE_URL = "https://api.hyperliquid-testnet.xyz";
3
+ export declare const HYPERLIQUID_WS_URL = "wss://api.hyperliquid.xyz/ws";
4
+ export declare const HYPERLIQUID_TESTNET_WS_URL = "wss://api.hyperliquid-testnet.xyz/ws";
5
+ export declare const OUTCOME_ASSET_BASE = 100000000;
6
+ export declare const OUTCOME_MULTIPLIER = 10;
7
+ export declare const SIDE_YES = 0;
8
+ export declare const SIDE_NO = 1;
9
+ export declare const EXCHANGE_DOMAIN = "Exchange";
10
+ export declare const EXCHANGE_CHAIN_ID = 1337;
11
+ export declare const MIN_ORDER_VALUE = 10;
12
+ export interface HyperliquidApiConfig {
13
+ baseUrl: string;
14
+ wsUrl: string;
15
+ testnet: boolean;
16
+ }
17
+ export declare function getHyperliquidConfig(baseUrlOverride?: string, testnet?: boolean): HyperliquidApiConfig;
@@ -0,0 +1,28 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MIN_ORDER_VALUE = exports.EXCHANGE_CHAIN_ID = exports.EXCHANGE_DOMAIN = exports.SIDE_NO = exports.SIDE_YES = exports.OUTCOME_MULTIPLIER = exports.OUTCOME_ASSET_BASE = exports.HYPERLIQUID_TESTNET_WS_URL = exports.HYPERLIQUID_WS_URL = exports.HYPERLIQUID_TESTNET_BASE_URL = exports.DEFAULT_HYPERLIQUID_BASE_URL = void 0;
4
+ exports.getHyperliquidConfig = getHyperliquidConfig;
5
+ exports.DEFAULT_HYPERLIQUID_BASE_URL = 'https://api.hyperliquid.xyz';
6
+ exports.HYPERLIQUID_TESTNET_BASE_URL = 'https://api.hyperliquid-testnet.xyz';
7
+ exports.HYPERLIQUID_WS_URL = 'wss://api.hyperliquid.xyz/ws';
8
+ exports.HYPERLIQUID_TESTNET_WS_URL = 'wss://api.hyperliquid-testnet.xyz/ws';
9
+ // HIP-4 Outcome Markets asset ID encoding
10
+ // Asset ID = 100_000_000 + (10 * outcome_id) + side
11
+ // side: 0 = Yes, 1 = No
12
+ exports.OUTCOME_ASSET_BASE = 100_000_000;
13
+ exports.OUTCOME_MULTIPLIER = 10;
14
+ exports.SIDE_YES = 0;
15
+ exports.SIDE_NO = 1;
16
+ // EIP-712 signing constants
17
+ exports.EXCHANGE_DOMAIN = 'Exchange';
18
+ exports.EXCHANGE_CHAIN_ID = 1337;
19
+ // Minimum order value in USDH
20
+ exports.MIN_ORDER_VALUE = 10;
21
+ function getHyperliquidConfig(baseUrlOverride, testnet) {
22
+ const isTestnet = testnet ?? false;
23
+ return {
24
+ baseUrl: baseUrlOverride ?? (isTestnet ? exports.HYPERLIQUID_TESTNET_BASE_URL : exports.DEFAULT_HYPERLIQUID_BASE_URL),
25
+ wsUrl: isTestnet ? exports.HYPERLIQUID_TESTNET_WS_URL : exports.HYPERLIQUID_WS_URL,
26
+ testnet: isTestnet,
27
+ };
28
+ }
@@ -0,0 +1,18 @@
1
+ import { ErrorMapper } from '../../utils/error-mapper';
2
+ import { BadRequest } from '../../errors';
3
+ /**
4
+ * Maps Hyperliquid API errors to PMXT unified error classes.
5
+ *
6
+ * Hyperliquid returns errors as plain strings or JSON objects in
7
+ * the response body. Common patterns:
8
+ * - "User has no account" -> AuthenticationError
9
+ * - "Insufficient margin" -> InsufficientFunds
10
+ * - "Invalid order" -> InvalidOrder
11
+ */
12
+ export declare class HyperliquidErrorMapper extends ErrorMapper {
13
+ constructor();
14
+ protected extractErrorMessage(error: any): string;
15
+ protected mapBadRequestError(message: string, data: any): BadRequest;
16
+ mapError(error: any): ReturnType<ErrorMapper['mapError']>;
17
+ }
18
+ export declare const hyperliquidErrorMapper: HyperliquidErrorMapper;
@@ -0,0 +1,61 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.hyperliquidErrorMapper = exports.HyperliquidErrorMapper = void 0;
7
+ const axios_1 = __importDefault(require("axios"));
8
+ const error_mapper_1 = require("../../utils/error-mapper");
9
+ const errors_1 = require("../../errors");
10
+ /**
11
+ * Maps Hyperliquid API errors to PMXT unified error classes.
12
+ *
13
+ * Hyperliquid returns errors as plain strings or JSON objects in
14
+ * the response body. Common patterns:
15
+ * - "User has no account" -> AuthenticationError
16
+ * - "Insufficient margin" -> InsufficientFunds
17
+ * - "Invalid order" -> InvalidOrder
18
+ */
19
+ class HyperliquidErrorMapper extends error_mapper_1.ErrorMapper {
20
+ constructor() {
21
+ super('Hyperliquid');
22
+ }
23
+ extractErrorMessage(error) {
24
+ if (axios_1.default.isAxiosError(error) && error.response?.data) {
25
+ const data = error.response.data;
26
+ if (typeof data === 'string') {
27
+ return `[${error.response.status}] ${data}`;
28
+ }
29
+ if (data.status === 'err' && data.response) {
30
+ return `[${error.response.status}] ${data.response}`;
31
+ }
32
+ }
33
+ return super.extractErrorMessage(error);
34
+ }
35
+ mapBadRequestError(message, data) {
36
+ const lowerMessage = message.toLowerCase();
37
+ const responseStr = typeof data === 'object' && data?.response
38
+ ? String(data.response).toLowerCase()
39
+ : lowerMessage;
40
+ if (responseStr.includes('insufficient margin') || responseStr.includes('not enough')) {
41
+ return new errors_1.InsufficientFunds(message, this.exchangeName);
42
+ }
43
+ if (responseStr.includes('invalid order') || responseStr.includes('price out of range')) {
44
+ return new errors_1.InvalidOrder(message, this.exchangeName);
45
+ }
46
+ if (responseStr.includes('no account') || responseStr.includes('not authorized')) {
47
+ return new errors_1.AuthenticationError(message, this.exchangeName);
48
+ }
49
+ return super.mapBadRequestError(message, data);
50
+ }
51
+ mapError(error) {
52
+ if (axios_1.default.isAxiosError(error) && error.response?.status === 429) {
53
+ const retryAfter = error.response.headers?.['retry-after'];
54
+ const retryAfterSeconds = retryAfter ? parseInt(retryAfter, 10) : undefined;
55
+ return new errors_1.RateLimitExceeded(this.extractErrorMessage(error), retryAfterSeconds, this.exchangeName);
56
+ }
57
+ return super.mapError(error);
58
+ }
59
+ }
60
+ exports.HyperliquidErrorMapper = HyperliquidErrorMapper;
61
+ exports.hyperliquidErrorMapper = new HyperliquidErrorMapper();
@@ -0,0 +1,140 @@
1
+ import { MarketFilterParams, EventFetchParams, OHLCVParams, TradesParams } from '../../BaseExchange';
2
+ import { IExchangeFetcher, FetcherContext } from '../interfaces';
3
+ export interface HyperliquidRawSideSpec {
4
+ name: string;
5
+ token?: number;
6
+ }
7
+ export interface HyperliquidRawOutcome {
8
+ outcome: number;
9
+ name: string;
10
+ description: string;
11
+ sideSpecs: HyperliquidRawSideSpec[];
12
+ }
13
+ export interface HyperliquidRawQuestion {
14
+ question: number;
15
+ name: string;
16
+ description: string;
17
+ fallbackOutcome: number;
18
+ namedOutcomes: number[];
19
+ settledNamedOutcomes: number[];
20
+ }
21
+ export interface HyperliquidRawOutcomeMeta {
22
+ outcomes: HyperliquidRawOutcome[];
23
+ questions: HyperliquidRawQuestion[];
24
+ }
25
+ export interface HyperliquidRawL2Level {
26
+ px: string;
27
+ sz: string;
28
+ n: number;
29
+ }
30
+ export interface HyperliquidRawL2Book {
31
+ coin: string;
32
+ levels: [HyperliquidRawL2Level[], HyperliquidRawL2Level[]];
33
+ time: number;
34
+ }
35
+ export interface HyperliquidRawCandle {
36
+ t: number;
37
+ T: number;
38
+ s: string;
39
+ i: string;
40
+ o: string;
41
+ c: string;
42
+ h: string;
43
+ l: string;
44
+ v: string;
45
+ n: number;
46
+ }
47
+ export interface HyperliquidRawTrade {
48
+ coin: string;
49
+ side: string;
50
+ px: string;
51
+ sz: string;
52
+ hash: string;
53
+ time: number;
54
+ tid: number;
55
+ }
56
+ export interface HyperliquidRawMid {
57
+ [coin: string]: string;
58
+ }
59
+ export interface HyperliquidRawFill {
60
+ coin: string;
61
+ px: string;
62
+ sz: string;
63
+ side: string;
64
+ time: number;
65
+ startPosition: string;
66
+ dir: string;
67
+ closedPnl: string;
68
+ hash: string;
69
+ oid: number;
70
+ crossed: boolean;
71
+ fee: string;
72
+ tid: number;
73
+ feeToken: string;
74
+ }
75
+ export interface HyperliquidRawOpenOrder {
76
+ coin: string;
77
+ limitPx: string;
78
+ oid: number;
79
+ side: string;
80
+ sz: string;
81
+ timestamp: number;
82
+ origSz: string;
83
+ cloid?: string;
84
+ }
85
+ export interface HyperliquidRawPosition {
86
+ coin: string;
87
+ entryPx: string | null;
88
+ leverage: {
89
+ type: string;
90
+ value: number;
91
+ };
92
+ liquidationPx: string | null;
93
+ marginUsed: string;
94
+ maxTradeSzs: [string, string];
95
+ positionValue: string;
96
+ returnOnEquity: string;
97
+ szi: string;
98
+ unrealizedPnl: string;
99
+ }
100
+ export interface HyperliquidRawUserState {
101
+ assetPositions: Array<{
102
+ position: HyperliquidRawPosition;
103
+ type: string;
104
+ }>;
105
+ crossMarginSummary: {
106
+ accountValue: string;
107
+ totalMarginUsed: string;
108
+ totalNtlPos: string;
109
+ totalRawUsd: string;
110
+ };
111
+ marginSummary: {
112
+ accountValue: string;
113
+ totalMarginUsed: string;
114
+ totalNtlPos: string;
115
+ totalRawUsd: string;
116
+ };
117
+ withdrawable: string;
118
+ }
119
+ export interface HyperliquidRawOutcomeWithQuestion {
120
+ outcome: HyperliquidRawOutcome;
121
+ question: HyperliquidRawQuestion | undefined;
122
+ midPrice: string | undefined;
123
+ }
124
+ export declare class HyperliquidFetcher implements IExchangeFetcher<HyperliquidRawOutcomeWithQuestion, HyperliquidRawQuestion> {
125
+ private readonly ctx;
126
+ private readonly baseUrl;
127
+ constructor(ctx: FetcherContext, baseUrl: string);
128
+ private postInfo;
129
+ fetchRawMarkets(params?: MarketFilterParams): Promise<HyperliquidRawOutcomeWithQuestion[]>;
130
+ fetchRawEvents(params: EventFetchParams): Promise<HyperliquidRawQuestion[]>;
131
+ fetchRawOrderBook(marketId: string): Promise<HyperliquidRawL2Book>;
132
+ fetchRawOHLCV(marketId: string, params: OHLCVParams): Promise<HyperliquidRawCandle[]>;
133
+ fetchRawTrades(marketId: string, _params: TradesParams): Promise<HyperliquidRawTrade[]>;
134
+ fetchRawUserFills(walletAddress: string): Promise<HyperliquidRawFill[]>;
135
+ fetchRawOpenOrders(walletAddress: string): Promise<HyperliquidRawOpenOrder[]>;
136
+ fetchRawUserState(walletAddress: string): Promise<HyperliquidRawUserState>;
137
+ fetchOutcomeMeta(): Promise<HyperliquidRawOutcomeMeta>;
138
+ fetchAllMids(): Promise<HyperliquidRawMid>;
139
+ private getMidForOutcome;
140
+ }
@@ -0,0 +1,137 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.HyperliquidFetcher = void 0;
4
+ const errors_1 = require("./errors");
5
+ const utils_1 = require("./utils");
6
+ // ----------------------------------------------------------------------------
7
+ // Fetcher
8
+ // ----------------------------------------------------------------------------
9
+ class HyperliquidFetcher {
10
+ ctx;
11
+ baseUrl;
12
+ constructor(ctx, baseUrl) {
13
+ this.ctx = ctx;
14
+ this.baseUrl = baseUrl;
15
+ }
16
+ // -- Info endpoint helper --------------------------------------------------
17
+ async postInfo(body) {
18
+ try {
19
+ const response = await this.ctx.http.post(`${this.baseUrl}/info`, body);
20
+ return response.data;
21
+ }
22
+ catch (error) {
23
+ throw errors_1.hyperliquidErrorMapper.mapError(error);
24
+ }
25
+ }
26
+ // -- Markets (outcomes) ----------------------------------------------------
27
+ async fetchRawMarkets(params) {
28
+ const [meta, mids] = await Promise.all([
29
+ this.fetchOutcomeMeta(),
30
+ this.fetchAllMids(),
31
+ ]);
32
+ const questionMap = new Map();
33
+ for (const q of meta.questions) {
34
+ for (const outcomeId of q.namedOutcomes) {
35
+ questionMap.set(outcomeId, q);
36
+ }
37
+ }
38
+ let results = meta.outcomes.map(outcome => ({
39
+ outcome,
40
+ question: questionMap.get(outcome.outcome),
41
+ midPrice: this.getMidForOutcome(mids, outcome.outcome),
42
+ }));
43
+ // Filter settled outcomes out by default (active only)
44
+ if (!params?.status || params.status === 'active') {
45
+ const settledSet = new Set();
46
+ for (const q of meta.questions) {
47
+ for (const settled of q.settledNamedOutcomes) {
48
+ settledSet.add(settled);
49
+ }
50
+ }
51
+ results = results.filter(r => !settledSet.has(r.outcome.outcome));
52
+ }
53
+ // Client-side search
54
+ if (params?.query) {
55
+ const lowerQuery = params.query.toLowerCase();
56
+ results = results.filter(r => r.outcome.name.toLowerCase().includes(lowerQuery) ||
57
+ r.outcome.description.toLowerCase().includes(lowerQuery));
58
+ }
59
+ // Limit
60
+ const limit = params?.limit || 250000;
61
+ const offset = params?.offset || 0;
62
+ return results.slice(offset, offset + limit);
63
+ }
64
+ // -- Events (questions) ----------------------------------------------------
65
+ async fetchRawEvents(params) {
66
+ const meta = await this.fetchOutcomeMeta();
67
+ let results = [...meta.questions];
68
+ // Filter by query
69
+ if (params?.query) {
70
+ const lowerQuery = params.query.toLowerCase();
71
+ results = results.filter(q => q.name.toLowerCase().includes(lowerQuery) ||
72
+ q.description.toLowerCase().includes(lowerQuery));
73
+ }
74
+ // Filter settled
75
+ if (!params?.status || params.status === 'active') {
76
+ results = results.filter(q => q.namedOutcomes.length > q.settledNamedOutcomes.length);
77
+ }
78
+ const limit = params?.limit || 250000;
79
+ const offset = params?.offset || 0;
80
+ return results.slice(offset, offset + limit);
81
+ }
82
+ // -- OrderBook -------------------------------------------------------------
83
+ async fetchRawOrderBook(marketId) {
84
+ const outcomeId = (0, utils_1.fromMarketId)(marketId);
85
+ const coin = (0, utils_1.toCoinNotation)(outcomeId, 'yes');
86
+ return this.postInfo({ type: 'l2Book', coin });
87
+ }
88
+ // -- OHLCV (candles) -------------------------------------------------------
89
+ async fetchRawOHLCV(marketId, params) {
90
+ const outcomeId = (0, utils_1.fromMarketId)(marketId);
91
+ const coin = (0, utils_1.toCoinNotation)(outcomeId, 'yes');
92
+ const now = Date.now();
93
+ const startTime = params.start ? params.start.getTime() : now - 24 * 60 * 60 * 1000;
94
+ const endTime = params.end ? params.end.getTime() : now;
95
+ return this.postInfo({
96
+ type: 'candleSnapshot',
97
+ req: { coin, interval: params.resolution || '1h', startTime, endTime },
98
+ });
99
+ }
100
+ // -- Trades ----------------------------------------------------------------
101
+ async fetchRawTrades(marketId, _params) {
102
+ const outcomeId = (0, utils_1.fromMarketId)(marketId);
103
+ const coin = (0, utils_1.toCoinNotation)(outcomeId, 'yes');
104
+ return this.postInfo({ type: 'recentTrades', coin });
105
+ }
106
+ // -- User data -------------------------------------------------------------
107
+ async fetchRawUserFills(walletAddress) {
108
+ return this.postInfo({
109
+ type: 'userFills',
110
+ user: walletAddress,
111
+ });
112
+ }
113
+ async fetchRawOpenOrders(walletAddress) {
114
+ return this.postInfo({
115
+ type: 'openOrders',
116
+ user: walletAddress,
117
+ });
118
+ }
119
+ async fetchRawUserState(walletAddress) {
120
+ return this.postInfo({
121
+ type: 'clearinghouseState',
122
+ user: walletAddress,
123
+ });
124
+ }
125
+ // -- Shared helpers --------------------------------------------------------
126
+ async fetchOutcomeMeta() {
127
+ return this.postInfo({ type: 'outcomeMeta' });
128
+ }
129
+ async fetchAllMids() {
130
+ return this.postInfo({ type: 'allMids' });
131
+ }
132
+ getMidForOutcome(mids, outcomeId) {
133
+ const yesCoin = (0, utils_1.toCoinNotation)(outcomeId, 'yes');
134
+ return mids[yesCoin];
135
+ }
136
+ }
137
+ exports.HyperliquidFetcher = HyperliquidFetcher;
@@ -0,0 +1,31 @@
1
+ import { PredictionMarketExchange, MarketFilterParams, EventFetchParams, OHLCVParams, TradesParams, ExchangeCredentials, MyTradesParams } from '../../BaseExchange';
2
+ import { UnifiedMarket, UnifiedEvent, OrderBook, PriceCandle, Trade, UserTrade, Balance, Position, Order, CreateOrderParams, BuiltOrder } from '../../types';
3
+ export interface HyperliquidExchangeOptions {
4
+ credentials?: ExchangeCredentials;
5
+ testnet?: boolean;
6
+ }
7
+ export declare class HyperliquidExchange extends PredictionMarketExchange {
8
+ private readonly config;
9
+ private readonly fetcher;
10
+ private readonly normalizer;
11
+ private readonly walletAddress?;
12
+ private readonly auth?;
13
+ constructor(credentials?: ExchangeCredentials | HyperliquidExchangeOptions);
14
+ get name(): string;
15
+ private requireWallet;
16
+ private requireAuth;
17
+ protected fetchMarketsImpl(params?: MarketFilterParams): Promise<UnifiedMarket[]>;
18
+ protected fetchEventsImpl(params: EventFetchParams): Promise<UnifiedEvent[]>;
19
+ fetchOrderBook(outcomeId: string): Promise<OrderBook>;
20
+ fetchOHLCV(outcomeId: string, params: OHLCVParams): Promise<PriceCandle[]>;
21
+ fetchTrades(outcomeId: string, params?: TradesParams): Promise<Trade[]>;
22
+ fetchBalance(): Promise<Balance[]>;
23
+ fetchPositions(): Promise<Position[]>;
24
+ fetchOpenOrders(): Promise<Order[]>;
25
+ fetchMyTrades(params?: MyTradesParams): Promise<UserTrade[]>;
26
+ buildOrder(params: CreateOrderParams): Promise<BuiltOrder>;
27
+ submitOrder(built: BuiltOrder): Promise<Order>;
28
+ createOrder(params: CreateOrderParams): Promise<Order>;
29
+ cancelOrder(orderId: string): Promise<Order>;
30
+ close(): Promise<void>;
31
+ }