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.
- package/dist/exchanges/hyperliquid/auth.d.ts +30 -0
- package/dist/exchanges/hyperliquid/auth.js +145 -0
- package/dist/exchanges/hyperliquid/config.d.ts +17 -0
- package/dist/exchanges/hyperliquid/config.js +28 -0
- package/dist/exchanges/hyperliquid/errors.d.ts +18 -0
- package/dist/exchanges/hyperliquid/errors.js +61 -0
- package/dist/exchanges/hyperliquid/fetcher.d.ts +140 -0
- package/dist/exchanges/hyperliquid/fetcher.js +137 -0
- package/dist/exchanges/hyperliquid/index.d.ts +31 -0
- package/dist/exchanges/hyperliquid/index.js +219 -0
- package/dist/exchanges/hyperliquid/normalizer.d.ts +18 -0
- package/dist/exchanges/hyperliquid/normalizer.js +339 -0
- package/dist/exchanges/hyperliquid/utils.d.ts +41 -0
- package/dist/exchanges/hyperliquid/utils.js +76 -0
- package/dist/exchanges/kalshi/api.d.ts +1 -1
- package/dist/exchanges/kalshi/api.js +1 -1
- package/dist/exchanges/limitless/api.d.ts +1 -1
- package/dist/exchanges/limitless/api.js +1 -1
- package/dist/exchanges/myriad/api.d.ts +1 -1
- package/dist/exchanges/myriad/api.js +1 -1
- package/dist/exchanges/opinion/api.d.ts +1 -1
- package/dist/exchanges/opinion/api.js +1 -1
- package/dist/exchanges/polymarket/api-clob.d.ts +1 -1
- package/dist/exchanges/polymarket/api-clob.js +1 -1
- package/dist/exchanges/polymarket/api-data.d.ts +1 -1
- package/dist/exchanges/polymarket/api-data.js +1 -1
- package/dist/exchanges/polymarket/api-gamma.d.ts +1 -1
- package/dist/exchanges/polymarket/api-gamma.js +1 -1
- package/dist/exchanges/probable/api.d.ts +1 -1
- package/dist/exchanges/probable/api.js +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.js +5 -1
- package/dist/server/exchange-factory.js +6 -0
- package/dist/server/openapi.yaml +15 -0
- 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
|
+
}
|