@phantom/perps-client 0.1.1
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/CHANGELOG.md +7 -0
- package/README.md +147 -0
- package/jest.config.js +10 -0
- package/package.json +38 -0
- package/src/PerpsClient.test.ts +216 -0
- package/src/PerpsClient.ts +336 -0
- package/src/actions.test.ts +298 -0
- package/src/actions.ts +182 -0
- package/src/api.ts +366 -0
- package/src/constants.ts +65 -0
- package/src/index.ts +16 -0
- package/src/types.ts +227 -0
- package/src/validate.ts +18 -0
- package/tsconfig.json +17 -0
package/src/types.ts
ADDED
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Types for Hyperliquid perpetuals trading via Phantom backend.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Minimal logger interface — satisfied by the MCP server Logger class
|
|
7
|
+
* or any standard logger (console, pino, etc.).
|
|
8
|
+
*/
|
|
9
|
+
export interface PerpsLogger {
|
|
10
|
+
info(message: string): void;
|
|
11
|
+
error(message: string): void;
|
|
12
|
+
debug(message: string): void;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/** No-op logger used when no logger is provided */
|
|
16
|
+
export const noopLogger: PerpsLogger = {
|
|
17
|
+
info: () => {},
|
|
18
|
+
error: () => {},
|
|
19
|
+
debug: () => {},
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export interface PerpAccountBalance {
|
|
23
|
+
accountValue: string;
|
|
24
|
+
availableBalance: string;
|
|
25
|
+
availableToTrade: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface PerpPosition {
|
|
29
|
+
coin: string;
|
|
30
|
+
direction: "long" | "short";
|
|
31
|
+
size: string;
|
|
32
|
+
margin: string;
|
|
33
|
+
entryPrice: string;
|
|
34
|
+
leverage: { type: "isolated" | "cross" | "unknown"; value: number };
|
|
35
|
+
unrealizedPnl: string;
|
|
36
|
+
liquidationPrice: string | null;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface PerpOrder {
|
|
40
|
+
id: string;
|
|
41
|
+
coin: string;
|
|
42
|
+
side: "long" | "short";
|
|
43
|
+
type: "limit" | "take_profit_market" | "stop_market";
|
|
44
|
+
isTrigger: boolean;
|
|
45
|
+
limitPrice: string;
|
|
46
|
+
triggerPrice?: string;
|
|
47
|
+
size: string;
|
|
48
|
+
reduceOnly: boolean;
|
|
49
|
+
timestamp: number;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export interface PerpMarket {
|
|
53
|
+
symbol: string;
|
|
54
|
+
assetId: number;
|
|
55
|
+
maxLeverage: number;
|
|
56
|
+
szDecimals: number;
|
|
57
|
+
price: string;
|
|
58
|
+
fundingRate: string;
|
|
59
|
+
openInterest: string;
|
|
60
|
+
volume24h: string;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export interface HistoricalOrder {
|
|
64
|
+
id: string;
|
|
65
|
+
coin: string;
|
|
66
|
+
type: string;
|
|
67
|
+
timestamp: number;
|
|
68
|
+
price: string;
|
|
69
|
+
size: string;
|
|
70
|
+
tradeValue: string;
|
|
71
|
+
fee: string;
|
|
72
|
+
closedPnl?: string;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export interface FundingActivity {
|
|
76
|
+
/** CAIP-19 encoded transaction ID */
|
|
77
|
+
id?: string;
|
|
78
|
+
type: string;
|
|
79
|
+
/** USDC amount for this deposit or withdrawal */
|
|
80
|
+
amount: string;
|
|
81
|
+
timestamp: number;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export interface OpenPositionParams {
|
|
85
|
+
/** Coin symbol e.g. "BTC", "ETH" */
|
|
86
|
+
market: string;
|
|
87
|
+
direction: "long" | "short";
|
|
88
|
+
/** Size in USD */
|
|
89
|
+
sizeUsd: string;
|
|
90
|
+
leverage: number;
|
|
91
|
+
/** Margin type — defaults to "isolated" (safer). Use "cross" to share account balance across positions. */
|
|
92
|
+
marginType?: "isolated" | "cross";
|
|
93
|
+
orderType: "market" | "limit";
|
|
94
|
+
limitPrice?: string;
|
|
95
|
+
reduceOnly?: boolean;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export interface ClosePositionParams {
|
|
99
|
+
market: string;
|
|
100
|
+
/** 0-100, defaults to 100 (full close) */
|
|
101
|
+
sizePercent?: number;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export interface CancelOrderParams {
|
|
105
|
+
market: string;
|
|
106
|
+
orderId: number;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export interface UpdateLeverageParams {
|
|
110
|
+
market: string;
|
|
111
|
+
leverage: number;
|
|
112
|
+
marginType: "cross" | "isolated";
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export interface ActionResponse {
|
|
116
|
+
status: string;
|
|
117
|
+
data?: unknown;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/** Response from cancel-order, update-leverage, and transfer-usdc-spot-perp endpoints. */
|
|
121
|
+
export interface HlDefaultResponse {
|
|
122
|
+
status: string;
|
|
123
|
+
response: { type: string };
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/** Response from cancel-order endpoint. */
|
|
127
|
+
export interface HlCancelOrderResponse {
|
|
128
|
+
status: string;
|
|
129
|
+
response: {
|
|
130
|
+
type: string;
|
|
131
|
+
data: { statuses: Array<string | { error: string }> };
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Internal types for EIP-712 and action submission
|
|
136
|
+
|
|
137
|
+
export interface Eip712TypedData {
|
|
138
|
+
domain: {
|
|
139
|
+
name: string;
|
|
140
|
+
version: string;
|
|
141
|
+
chainId: number;
|
|
142
|
+
verifyingContract: string;
|
|
143
|
+
};
|
|
144
|
+
primaryType: string;
|
|
145
|
+
types: Record<string, { name: string; type: string }[]>;
|
|
146
|
+
message: Record<string, unknown>;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export interface SignatureComponents {
|
|
150
|
+
r: string;
|
|
151
|
+
s: string;
|
|
152
|
+
v: number;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Low-level Hyperliquid order structs (mirrors wallet2's order.ts)
|
|
156
|
+
|
|
157
|
+
type TimeInForce = "Alo" | "Ioc" | "Gtc";
|
|
158
|
+
|
|
159
|
+
interface LimitOrderType {
|
|
160
|
+
limit: {
|
|
161
|
+
tif: TimeInForce;
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
interface TriggerOrderType {
|
|
166
|
+
trigger: {
|
|
167
|
+
isMarket: boolean;
|
|
168
|
+
triggerPx: string;
|
|
169
|
+
tpsl: "tp" | "sl";
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
export interface HlOrder {
|
|
174
|
+
a: number; // asset id
|
|
175
|
+
b: boolean; // isBuy
|
|
176
|
+
p: string; // price
|
|
177
|
+
s: string; // size
|
|
178
|
+
r: boolean; // reduceOnly
|
|
179
|
+
t: LimitOrderType | TriggerOrderType;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
export interface HlOrderAction {
|
|
183
|
+
type: "order";
|
|
184
|
+
orders: HlOrder[];
|
|
185
|
+
grouping: "na";
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
export interface HlCancelAction {
|
|
189
|
+
type: "cancel";
|
|
190
|
+
cancels: { a: number; o: number }[];
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
export interface HlUpdateLeverageAction {
|
|
194
|
+
type: "updateLeverage";
|
|
195
|
+
asset: number;
|
|
196
|
+
isCross: boolean;
|
|
197
|
+
leverage: number;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
export interface HlUsdClassTransferAction {
|
|
201
|
+
type: "usdClassTransfer";
|
|
202
|
+
hyperliquidChain: "Mainnet" | "Testnet";
|
|
203
|
+
signatureChainId: "0xa4b1" | "0x66eee";
|
|
204
|
+
amount: string;
|
|
205
|
+
toPerp: boolean;
|
|
206
|
+
nonce: number;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
export type HlAction = HlOrderAction | HlCancelAction | HlUpdateLeverageAction | HlUsdClassTransferAction;
|
|
210
|
+
|
|
211
|
+
/** Response from POST /swap/v2/place-order (matches backend OrderResponse DTO) */
|
|
212
|
+
export interface HlOrderResponseStatus {
|
|
213
|
+
resting?: { oid: number };
|
|
214
|
+
filled?: { totalSz: string; avgPx: string; oid: number };
|
|
215
|
+
error?: string;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
export interface HlOrderResponse {
|
|
219
|
+
status: string;
|
|
220
|
+
response: {
|
|
221
|
+
type: string;
|
|
222
|
+
data: {
|
|
223
|
+
statuses: HlOrderResponseStatus[];
|
|
224
|
+
};
|
|
225
|
+
};
|
|
226
|
+
filled?: { totalSz: string; avgPx: string; oid: number };
|
|
227
|
+
}
|
package/src/validate.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validation helpers for PerpsClient string parameters.
|
|
3
|
+
*
|
|
4
|
+
* Numeric types (number, boolean, enum unions) are sufficiently constrained by
|
|
5
|
+
* TypeScript and validated by callers (e.g. the MCP layer). String parameters,
|
|
6
|
+
* however, carry no format guarantee — any string value satisfies `string` — so
|
|
7
|
+
* the client must enforce canonical format before embedding them in signed payloads.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Throws if value is not a canonical positive decimal string (e.g. "100" or "10.5").
|
|
12
|
+
* Rejects leading/trailing whitespace, scientific notation, negative values, and zero.
|
|
13
|
+
*/
|
|
14
|
+
export function assertPositiveDecimalString(value: unknown, name: string): asserts value is string {
|
|
15
|
+
if (typeof value !== "string" || value !== value.trim() || !/^\d+(\.\d+)?$/.test(value) || parseFloat(value) <= 0) {
|
|
16
|
+
throw new Error(`${name} must be a positive number string (e.g. "100" or "10.5")`);
|
|
17
|
+
}
|
|
18
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "../../tsconfig.json",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"target": "ES2020",
|
|
5
|
+
"module": "commonjs",
|
|
6
|
+
"declaration": true,
|
|
7
|
+
"outDir": "./dist",
|
|
8
|
+
"rootDir": "./src",
|
|
9
|
+
"strict": true,
|
|
10
|
+
"esModuleInterop": true,
|
|
11
|
+
"skipLibCheck": true,
|
|
12
|
+
"forceConsistentCasingInFileNames": true,
|
|
13
|
+
"resolveJsonModule": true
|
|
14
|
+
},
|
|
15
|
+
"include": ["src/**/*"],
|
|
16
|
+
"exclude": ["node_modules", "dist"]
|
|
17
|
+
}
|