@pear-protocol/exchanges-sdk 0.0.6 → 0.0.8-test.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 (34) hide show
  1. package/README.md +6 -4
  2. package/dist/account/account-manager.d.ts +6 -4
  3. package/dist/account/account-manager.js +22 -5
  4. package/dist/account/exchange-account.d.ts +2 -2
  5. package/dist/account/exchange-account.js +2 -2
  6. package/dist/account/exchanges/base.d.ts +3 -3
  7. package/dist/account/exchanges/binance/mapper.d.ts +1 -1
  8. package/dist/account/exchanges/binance/mapper.js +2 -12
  9. package/dist/account/exchanges/binance/orchestrator.d.ts +2 -4
  10. package/dist/account/exchanges/binance/orchestrator.js +6 -23
  11. package/dist/account/exchanges/binance/rest.d.ts +2 -1
  12. package/dist/account/exchanges/binance/rest.js +4 -1
  13. package/dist/account/exchanges/bybit/mapper.d.ts +1 -1
  14. package/dist/account/exchanges/bybit/mapper.js +2 -11
  15. package/dist/account/exchanges/bybit/orchestrator.d.ts +2 -3
  16. package/dist/account/exchanges/bybit/orchestrator.js +9 -15
  17. package/dist/account/exchanges/bybit/rest.d.ts +2 -1
  18. package/dist/account/exchanges/bybit/rest.js +9 -1
  19. package/dist/account/exchanges/hyperliquid/const.d.ts +3 -0
  20. package/dist/account/exchanges/hyperliquid/const.js +3 -0
  21. package/dist/account/exchanges/hyperliquid/mapper.d.ts +9 -0
  22. package/dist/account/exchanges/hyperliquid/mapper.js +68 -0
  23. package/dist/account/exchanges/hyperliquid/orchestrator.d.ts +38 -0
  24. package/dist/account/exchanges/hyperliquid/orchestrator.js +238 -0
  25. package/dist/account/exchanges/hyperliquid/rest.d.ts +6 -0
  26. package/dist/account/exchanges/hyperliquid/rest.js +11 -0
  27. package/dist/account/exchanges/hyperliquid/types.d.ts +20 -0
  28. package/dist/account/exchanges/hyperliquid/types.js +8 -0
  29. package/dist/account/exchanges/index.js +3 -0
  30. package/dist/account/index.d.ts +2 -2
  31. package/dist/account/types.d.ts +6 -14
  32. package/dist/index.d.ts +1 -1
  33. package/dist/index.js +1 -1
  34. package/package.json +7 -4
package/README.md CHANGED
@@ -8,6 +8,7 @@ Unified SDK for connecting to cryptocurrency derivative exchanges. Provides real
8
8
  |----------|---------------|-------------|
9
9
  | Binance | `binanceusdm` | USDM Futures |
10
10
  | Bybit | `bybit` | Linear Perpetuals |
11
+ | Hyperliquid | `hyperliquid` | Perpetuals |
11
12
 
12
13
  ## Installation
13
14
 
@@ -67,6 +68,7 @@ Credentials are fetched automatically from the backend when you call `connect`.
67
68
  ```typescript
68
69
  await exchanges.account.connect(tradeAccountId, 'binanceusdm');
69
70
  await exchanges.account.connect(tradeAccountId, 'bybit');
71
+ await exchanges.account.connect(tradeAccountId, 'hyperliquid');
70
72
  ```
71
73
 
72
74
  Check connection status:
@@ -91,8 +93,11 @@ const tracker = exchanges.account.trackBalance((balance) => {
91
93
  // Per-asset breakdown
92
94
  for (const asset of balance.assets) {
93
95
  asset.asset; // "USDT", "BTC", etc.
96
+ asset.free; // available amount
97
+ asset.used; // amount in use
98
+ asset.total; // total amount
99
+ asset.walletBalance; // wallet balance
94
100
  asset.usdValue; // USD equivalent
95
- asset.isCollateral; // whether the asset is used as collateral
96
101
  }
97
102
  });
98
103
 
@@ -134,9 +139,6 @@ const tracker = exchanges.account.trackAsset('ETH', (info) => {
134
139
  info.coin; // "ETH"
135
140
  info.leverage; // "20"
136
141
  info.marginType; // "cross" | "isolated"
137
- info.availableToTrade; // [min, max] or null
138
- info.maxTradeSzs; // [min, max] or null
139
- info.collateralToken; // "USDT" or null
140
142
  });
141
143
 
142
144
  const current = tracker.get();
@@ -1,10 +1,11 @@
1
+ import PearSDK from '@pear-protocol/core-sdk';
1
2
  import { Connector } from '@pear-protocol/types';
2
3
  import { Credentials } from '../credentials.js';
3
- import { BalanceCallback, Tracker, AccountBalance, PositionsCallback, AccountPosition, TrackedAssetCallback, TrackedAssetInfo, ExchangeState } from './types.js';
4
- import '@pear-protocol/core-sdk';
4
+ import { BalanceCallback, Tracker, AccountBalance, PositionsCallback, AccountPosition, TrackedAssetCallback, TrackedAssetInfo, MarginMode, ExchangeState } from './types.js';
5
5
 
6
6
  declare class AccountManager {
7
7
  private credentials;
8
+ private sdk;
8
9
  private demo;
9
10
  private activeAccount;
10
11
  private pendingExchange;
@@ -12,12 +13,12 @@ declare class AccountManager {
12
13
  private balanceListeners;
13
14
  private positionListeners;
14
15
  private assetListeners;
15
- constructor(credentials: Credentials, demo?: boolean);
16
+ constructor(credentials: Credentials, sdk: PearSDK, demo?: boolean);
16
17
  connect(tradeAccountId: string, exchange: Connector): Promise<void>;
17
18
  trackBalance(cb: BalanceCallback): Tracker<AccountBalance | null>;
18
19
  trackPositions(cb: PositionsCallback): Tracker<AccountPosition[]>;
19
20
  trackAsset(coin: string, cb: TrackedAssetCallback): Tracker<TrackedAssetInfo | null>;
20
- setLeverage(symbol: string, leverage: string): Promise<void>;
21
+ setLeverage(symbol: string, leverage: string, marginMode?: MarginMode): Promise<void>;
21
22
  getLeverage(symbol: string): string | null;
22
23
  getState(): Readonly<ExchangeState> | null;
23
24
  disconnect(): void;
@@ -28,6 +29,7 @@ declare class AccountManager {
28
29
  private disconnectWs;
29
30
  private maybeDisconnectWs;
30
31
  private ensureWsStarted;
32
+ private resolveCredentials;
31
33
  private startWs;
32
34
  }
33
35
 
@@ -2,6 +2,7 @@ import { ExchangeAccount } from './exchange-account';
2
2
 
3
3
  class AccountManager {
4
4
  credentials;
5
+ sdk;
5
6
  demo;
6
7
  activeAccount = null;
7
8
  pendingExchange = null;
@@ -9,12 +10,13 @@ class AccountManager {
9
10
  balanceListeners = /* @__PURE__ */ new Map();
10
11
  positionListeners = /* @__PURE__ */ new Map();
11
12
  assetListeners = /* @__PURE__ */ new Map();
12
- constructor(credentials, demo = false) {
13
+ constructor(credentials, sdk, demo = false) {
13
14
  this.credentials = credentials;
15
+ this.sdk = sdk;
14
16
  this.demo = demo;
15
17
  }
16
18
  async connect(tradeAccountId, exchange) {
17
- const creds = await this.credentials.getOrFetch(tradeAccountId);
19
+ const creds = await this.resolveCredentials(tradeAccountId, exchange);
18
20
  this.disconnectWs();
19
21
  this.pendingTradeAccountId = tradeAccountId;
20
22
  this.pendingExchange = exchange;
@@ -73,9 +75,9 @@ class AccountManager {
73
75
  }
74
76
  };
75
77
  }
76
- async setLeverage(symbol, leverage) {
78
+ async setLeverage(symbol, leverage, marginMode) {
77
79
  if (!this.activeAccount) throw new Error("No active account");
78
- await this.activeAccount.setLeverage(symbol, leverage);
80
+ await this.activeAccount.setLeverage(symbol, leverage, marginMode);
79
81
  }
80
82
  getLeverage(symbol) {
81
83
  return this.activeAccount?.getLeverage(symbol) ?? null;
@@ -119,7 +121,22 @@ class AccountManager {
119
121
  if (this.activeAccount || !this.pendingExchange || !this.pendingTradeAccountId) return;
120
122
  const exchange = this.pendingExchange;
121
123
  const tradeAccountId = this.pendingTradeAccountId;
122
- this.credentials.getOrFetch(tradeAccountId).then((creds) => this.startWs(exchange, creds)).catch((err) => console.warn("[AccountManager] Failed to start WS:", err));
124
+ this.resolveCredentials(tradeAccountId, exchange).then((creds) => this.startWs(exchange, creds)).catch((err) => console.warn("[AccountManager] Failed to start WS:", err));
125
+ }
126
+ async resolveCredentials(tradeAccountId, exchange) {
127
+ const creds = await this.credentials.getOrFetch(tradeAccountId);
128
+ if (exchange === "hyperliquid") {
129
+ const { accounts } = await this.sdk.info.accounts.list();
130
+ const account = accounts.find((a) => a.id === tradeAccountId);
131
+ if (!account) {
132
+ throw new Error(`Trade account ${tradeAccountId} not found`);
133
+ }
134
+ if (!account.exchangeIdentifier) {
135
+ throw new Error(`Trade account ${tradeAccountId} has no Hyperliquid address`);
136
+ }
137
+ return { ...creds, address: account.exchangeIdentifier };
138
+ }
139
+ return creds;
123
140
  }
124
141
  async startWs(exchange, creds) {
125
142
  const account = new ExchangeAccount({ exchange, demo: this.demo });
@@ -1,5 +1,5 @@
1
1
  import { Connector } from '@pear-protocol/types';
2
- import { AccountConfig, SupportedCredentials, BalanceCallback, AccountBalance, PositionsCallback, AccountPosition, TrackedAssetCallback, TrackedAssetInfo, ExchangeState } from './types.js';
2
+ import { AccountConfig, SupportedCredentials, BalanceCallback, AccountBalance, PositionsCallback, AccountPosition, TrackedAssetCallback, TrackedAssetInfo, MarginMode, ExchangeState } from './types.js';
3
3
 
4
4
  declare class ExchangeAccount {
5
5
  private exchange;
@@ -27,7 +27,7 @@ declare class ExchangeAccount {
27
27
  untrackAsset(id: string): void;
28
28
  getTrackedAsset(coin: string): TrackedAssetInfo | null;
29
29
  getLeverage(symbol: string): string | null;
30
- setLeverage(symbol: string, leverage: string): Promise<void>;
30
+ setLeverage(symbol: string, leverage: string, marginMode?: MarginMode): Promise<void>;
31
31
  getState(): Readonly<ExchangeState>;
32
32
  getExchange(): Connector;
33
33
  private applyStateUpdate;
@@ -120,9 +120,9 @@ class ExchangeAccount {
120
120
  }
121
121
  return null;
122
122
  }
123
- async setLeverage(symbol, leverage) {
123
+ async setLeverage(symbol, leverage, marginMode) {
124
124
  if (!this.orchestrator) throw new Error("Not connected");
125
- await this.orchestrator.setLeverage(symbol, leverage);
125
+ await this.orchestrator.setLeverage(symbol, leverage, marginMode);
126
126
  }
127
127
  getState() {
128
128
  return this.state;
@@ -1,4 +1,4 @@
1
- import { SupportedCredentials, StateUpdate } from '../types.js';
1
+ import { SupportedCredentials, MarginMode, StateUpdate } from '../types.js';
2
2
  import '@pear-protocol/types';
3
3
 
4
4
  type StateUpdateHandler = (update: StateUpdate) => void;
@@ -14,7 +14,7 @@ interface Orchestrator {
14
14
  isConnected: boolean;
15
15
  onAssetTracked: (coin: string) => void;
16
16
  onAssetUntracked: (coin: string) => void;
17
- setLeverage: (symbol: string, leverage: string) => Promise<void>;
17
+ setLeverage: (symbol: string, leverage: string, marginMode?: MarginMode) => Promise<void>;
18
18
  trackAsset: (coin: string) => void;
19
19
  untrackAsset: (coin: string) => boolean;
20
20
  getTrackedCoins: () => ReadonlySet<string>;
@@ -36,7 +36,7 @@ declare abstract class BaseOrchestrator implements Orchestrator {
36
36
  abstract get isConnected(): boolean;
37
37
  abstract onAssetTracked(coin: string): void;
38
38
  abstract onAssetUntracked(coin: string): void;
39
- abstract setLeverage(symbol: string, leverage: string): Promise<void>;
39
+ abstract setLeverage(symbol: string, leverage: string, marginMode?: MarginMode): Promise<void>;
40
40
  trackAsset(coin: string): void;
41
41
  untrackAsset(coin: string): boolean;
42
42
  getTrackedCoins(): ReadonlySet<string>;
@@ -2,7 +2,7 @@ import { AccountBalance, AccountPosition } from '../../types.js';
2
2
  import { BinanceRawAccountStatus, BinanceRawPositionRisk } from './types.js';
3
3
  import '@pear-protocol/types';
4
4
 
5
- declare function mapBalance(raw: BinanceRawAccountStatus, collateralRatios: Map<string, string>): AccountBalance;
5
+ declare function mapBalance(raw: BinanceRawAccountStatus): AccountBalance;
6
6
  declare function mapPositions(raw: BinanceRawPositionRisk[]): Map<string, AccountPosition>;
7
7
  declare function mergePositions(incoming: Map<string, AccountPosition>, existing: Map<string, AccountPosition>): Map<string, AccountPosition>;
8
8
 
@@ -1,7 +1,7 @@
1
1
  import '../../types';
2
2
  import { positionKey } from '../../utils';
3
3
 
4
- function mapBalance(raw, collateralRatios) {
4
+ function mapBalance(raw) {
5
5
  const free = {};
6
6
  const used = {};
7
7
  const total = {};
@@ -11,8 +11,6 @@ function mapBalance(raw, collateralRatios) {
11
11
  const ab = a.availableBalance ?? "0";
12
12
  const mb = a.marginBalance ?? "0";
13
13
  const im = a.initialMargin ?? "0";
14
- const mm = a.maintMargin ?? "0";
15
- const up = a.unrealizedProfit ?? "0";
16
14
  if (parseFloat(wb) === 0 && parseFloat(ab) === 0 && parseFloat(mb) === 0) continue;
17
15
  free[a.asset] = ab;
18
16
  used[a.asset] = im;
@@ -23,13 +21,7 @@ function mapBalance(raw, collateralRatios) {
23
21
  used: im,
24
22
  total: mb,
25
23
  walletBalance: wb,
26
- unrealizedPnl: up,
27
- usdValue: mb,
28
- initialMargin: im,
29
- maintenanceMargin: mm,
30
- isCollateral: a.marginAvailable ?? false,
31
- collateralRatio: collateralRatios.get(a.asset) ?? "1.0000",
32
- borrowedAmount: "0"
24
+ usdValue: mb
33
25
  });
34
26
  }
35
27
  const totalMarginBalance = raw.totalMarginBalance ?? "0";
@@ -38,9 +30,7 @@ function mapBalance(raw, collateralRatios) {
38
30
  const maintMargin = parseFloat(totalMaintMargin);
39
31
  const marginRatio = marginBalance > 0 ? (maintMargin / marginBalance).toFixed(4) : "0.0000";
40
32
  return {
41
- exchange: "binance",
42
33
  accountType: raw.multiAssetsMargin ? "multi-asset" : "single-asset",
43
- marginMode: "cross",
44
34
  free,
45
35
  used,
46
36
  total,
@@ -1,4 +1,4 @@
1
- import { SupportedCredentials } from '../../types.js';
1
+ import { SupportedCredentials, MarginMode } from '../../types.js';
2
2
  import { BaseOrchestrator, OrchestratorConfig } from '../base.js';
3
3
  import '@pear-protocol/types';
4
4
 
@@ -13,8 +13,6 @@ declare class BinanceOrchestrator extends BaseOrchestrator {
13
13
  private creds;
14
14
  private trackedAssetInfos;
15
15
  private currentPositions;
16
- private collateralRatios;
17
- private multiAssetsMargin;
18
16
  private readonly listenKeyRefreshMs;
19
17
  private readonly accountPollMs;
20
18
  constructor(config: BinanceOrchestratorConfig);
@@ -23,7 +21,7 @@ declare class BinanceOrchestrator extends BaseOrchestrator {
23
21
  get isConnected(): boolean;
24
22
  onAssetTracked(coin: string): void;
25
23
  onAssetUntracked(coin: string): void;
26
- setLeverage(symbol: string, leverage: string): Promise<void>;
24
+ setLeverage(symbol: string, leverage: string, marginMode?: MarginMode): Promise<void>;
27
25
  private stopAll;
28
26
  private pollAccountStatus;
29
27
  private handleUserDataEvent;
@@ -2,7 +2,7 @@ import { positionKey } from '../../utils';
2
2
  import { BaseOrchestrator } from '../base';
3
3
  import { DEFAULT_LISTEN_KEY_REFRESH_MS, DEFAULT_ACCOUNT_POLL_MS } from './const';
4
4
  import { mapBalance, mapPositions, mergePositions } from './mapper';
5
- import { fetchAccountStatus, fetchAllPositionRisk, fetchAssetIndex, createListenKey, keepAliveListenKey, fetchPositionRisk, setLeverage } from './rest';
5
+ import { fetchAccountStatus, fetchAllPositionRisk, createListenKey, keepAliveListenKey, fetchPositionRisk, setMarginType, setLeverage } from './rest';
6
6
  import { BinanceWs } from './ws';
7
7
 
8
8
  class BinanceOrchestrator extends BaseOrchestrator {
@@ -12,8 +12,6 @@ class BinanceOrchestrator extends BaseOrchestrator {
12
12
  creds = null;
13
13
  trackedAssetInfos = /* @__PURE__ */ new Map();
14
14
  currentPositions = /* @__PURE__ */ new Map();
15
- collateralRatios = /* @__PURE__ */ new Map();
16
- multiAssetsMargin = false;
17
15
  listenKeyRefreshMs;
18
16
  accountPollMs;
19
17
  constructor(config) {
@@ -33,18 +31,6 @@ class BinanceOrchestrator extends BaseOrchestrator {
33
31
  fetchAllPositionRisk(this.creds, this.demo)
34
32
  ]);
35
33
  if (gen !== this.startGeneration) return;
36
- this.multiAssetsMargin = snapshot.multiAssetsMargin ?? false;
37
- if (this.multiAssetsMargin) {
38
- const indexData = await fetchAssetIndex(this.demo);
39
- if (gen !== this.startGeneration) return;
40
- this.collateralRatios.clear();
41
- for (const item of indexData) {
42
- const asset = item.symbol.replace(/USD$/, "");
43
- if (item.bidRate && parseFloat(item.bidRate) > 0) {
44
- this.collateralRatios.set(asset, item.bidRate);
45
- }
46
- }
47
- }
48
34
  this.handleAccountStatus(snapshot);
49
35
  this.handlePositionRisk(positionRisk, false);
50
36
  const listenKey = await createListenKey(this.creds.api_key, this.demo);
@@ -87,8 +73,6 @@ class BinanceOrchestrator extends BaseOrchestrator {
87
73
  this.creds = null;
88
74
  this.trackedAssetInfos.clear();
89
75
  this.currentPositions.clear();
90
- this.collateralRatios.clear();
91
- this.multiAssetsMargin = false;
92
76
  }
93
77
  get isConnected() {
94
78
  return this.ws?.isConnected ?? false;
@@ -102,10 +86,7 @@ class BinanceOrchestrator extends BaseOrchestrator {
102
86
  const info = {
103
87
  coin,
104
88
  leverage,
105
- marginType,
106
- availableToTrade: null,
107
- maxTradeSzs: null,
108
- collateralToken: null
89
+ marginType
109
90
  };
110
91
  this.trackedAssetInfos.set(coin, info);
111
92
  const trackedAssets = /* @__PURE__ */ new Map();
@@ -119,8 +100,10 @@ class BinanceOrchestrator extends BaseOrchestrator {
119
100
  this.trackedAssetInfos.delete(coin);
120
101
  this.onStateUpdate({ trackedAssets: new Map(this.trackedAssetInfos) });
121
102
  }
122
- async setLeverage(symbol, leverage) {
103
+ async setLeverage(symbol, leverage, marginMode) {
123
104
  if (!this.creds) throw new Error("Not connected");
105
+ const mode = marginMode ?? "cross";
106
+ await setMarginType(this.creds, symbol, mode === "isolated" ? "ISOLATED" : "CROSSED", this.demo);
124
107
  await setLeverage(this.creds, symbol, leverage, this.demo);
125
108
  }
126
109
  stopAll() {
@@ -166,7 +149,7 @@ class BinanceOrchestrator extends BaseOrchestrator {
166
149
  }
167
150
  }
168
151
  handleAccountStatus(result) {
169
- const balance = mapBalance(result, this.collateralRatios);
152
+ const balance = mapBalance(result);
170
153
  this.onStateUpdate({ balance });
171
154
  }
172
155
  handlePositionRisk(risk, merge) {
@@ -8,6 +8,7 @@ declare function fetchAccountStatus(creds: BinanceUSDMCredentialsDTO, demo: bool
8
8
  declare function fetchPositionRisk(creds: BinanceUSDMCredentialsDTO, symbol: string, demo: boolean): Promise<BinanceRawPositionRisk | undefined>;
9
9
  declare function fetchAllPositionRisk(creds: BinanceUSDMCredentialsDTO, demo: boolean): Promise<BinanceRawPositionRisk[]>;
10
10
  declare function setLeverage(creds: BinanceUSDMCredentialsDTO, symbol: string, leverage: string, demo: boolean): Promise<BinanceRawLeverageResponse>;
11
+ declare function setMarginType(creds: BinanceUSDMCredentialsDTO, symbol: string, marginType: 'ISOLATED' | 'CROSSED', demo: boolean): Promise<void>;
11
12
  declare function fetchAssetIndex(demo: boolean): Promise<BinanceAssetIndex[]>;
12
13
 
13
- export { createListenKey, fetchAccountStatus, fetchAllPositionRisk, fetchAssetIndex, fetchPositionRisk, keepAliveListenKey, setLeverage };
14
+ export { createListenKey, fetchAccountStatus, fetchAllPositionRisk, fetchAssetIndex, fetchPositionRisk, keepAliveListenKey, setLeverage, setMarginType };
@@ -69,6 +69,9 @@ async function fetchAllPositionRisk(creds, demo) {
69
69
  async function setLeverage(creds, symbol, leverage, demo) {
70
70
  return signedPost(creds, "/fapi/v1/leverage", { symbol, leverage }, demo);
71
71
  }
72
+ async function setMarginType(creds, symbol, marginType, demo) {
73
+ await signedPost(creds, "/fapi/v1/marginType", { symbol, marginType }, demo);
74
+ }
72
75
  async function fetchAssetIndex(demo) {
73
76
  const baseUrl = demo ? BASE_URL_TESTNET : BASE_URL;
74
77
  const response = await fetch(`${baseUrl}/fapi/v1/assetIndex`);
@@ -78,4 +81,4 @@ async function fetchAssetIndex(demo) {
78
81
  return await response.json();
79
82
  }
80
83
 
81
- export { createListenKey, fetchAccountStatus, fetchAllPositionRisk, fetchAssetIndex, fetchPositionRisk, keepAliveListenKey, setLeverage };
84
+ export { createListenKey, fetchAccountStatus, fetchAllPositionRisk, fetchAssetIndex, fetchPositionRisk, keepAliveListenKey, setLeverage, setMarginType };
@@ -2,7 +2,7 @@ import { AccountBalance, AccountPosition } from '../../types.js';
2
2
  import { BybitRawWallet, BybitRawCoin, BybitRawPosition } from './types.js';
3
3
  import '@pear-protocol/types';
4
4
 
5
- declare function buildBalance(raw: BybitRawWallet, coins: Map<string, BybitRawCoin>, marginMode: string, collateralRatios: Map<string, string>): AccountBalance;
5
+ declare function buildBalance(raw: BybitRawWallet, coins: Map<string, BybitRawCoin>, marginMode: string): AccountBalance;
6
6
  declare function mergeRawCoins(existing: Map<string, BybitRawCoin>, incoming: BybitRawCoin[]): Map<string, BybitRawCoin>;
7
7
  declare function mapSinglePosition(p: BybitRawPosition, size: string): AccountPosition;
8
8
  declare function mapPositions(raw: BybitRawPosition[]): Map<string, AccountPosition>;
@@ -1,7 +1,7 @@
1
1
  import '../../types';
2
2
  import { positionKey } from '../../utils';
3
3
 
4
- function buildBalance(raw, coins, marginMode, collateralRatios) {
4
+ function buildBalance(raw, coins, marginMode) {
5
5
  const free = {};
6
6
  const used = {};
7
7
  const total = {};
@@ -11,7 +11,6 @@ function buildBalance(raw, coins, marginMode, collateralRatios) {
11
11
  const equity = c.equity ?? "0";
12
12
  const positionInitialMargin = c.totalPositionIM === "" ? "0" : c.totalPositionIM ?? "0";
13
13
  const orderInitialMargin = c.totalOrderIM === "" ? "0" : c.totalOrderIM ?? "0";
14
- const positionMaintenanceMargin = c.totalPositionMM === "" ? "0" : c.totalPositionMM ?? "0";
15
14
  const locked = c.locked ?? "0";
16
15
  const equityNum = parseFloat(equity);
17
16
  const positionInitialMarginNum = parseFloat(positionInitialMargin);
@@ -36,19 +35,11 @@ function buildBalance(raw, coins, marginMode, collateralRatios) {
36
35
  used: coinUsed,
37
36
  total: equity,
38
37
  walletBalance: c.walletBalance ?? "0",
39
- unrealizedPnl: c.unrealisedPnl ?? "0",
40
- usdValue: c.usdValue ?? "0",
41
- initialMargin: isPortfolio ? "0" : (positionInitialMarginNum + orderInitialMarginNum).toString(),
42
- maintenanceMargin: positionMaintenanceMargin,
43
- isCollateral: (c.marginCollateral ?? false) && (c.collateralSwitch ?? false),
44
- collateralRatio: collateralRatios.get(c.coin) ?? "1.0000",
45
- borrowedAmount: c.borrowAmount ?? "0"
38
+ usdValue: c.usdValue ?? "0"
46
39
  });
47
40
  }
48
41
  return {
49
- exchange: "bybit",
50
42
  accountType: "UNIFIED",
51
- marginMode,
52
43
  free,
53
44
  used,
54
45
  total,
@@ -1,4 +1,4 @@
1
- import { SupportedCredentials } from '../../types.js';
1
+ import { SupportedCredentials, MarginMode } from '../../types.js';
2
2
  import { BaseOrchestrator } from '../base.js';
3
3
  import '@pear-protocol/types';
4
4
 
@@ -8,13 +8,12 @@ declare class BybitOrchestrator extends BaseOrchestrator {
8
8
  private lastWallet;
9
9
  private trackedAssetInfos;
10
10
  private marginMode;
11
- private collateralRatios;
12
11
  start(credentials: SupportedCredentials): Promise<void>;
13
12
  stop(): void;
14
13
  get isConnected(): boolean;
15
14
  onAssetTracked(coin: string): void;
16
15
  onAssetUntracked(coin: string): void;
17
- setLeverage(symbol: string, leverage: string): Promise<void>;
16
+ setLeverage(symbol: string, leverage: string, marginMode?: MarginMode): Promise<void>;
18
17
  private stopAll;
19
18
  private handleMessage;
20
19
  private handleWalletUpdate;
@@ -1,6 +1,6 @@
1
1
  import { BaseOrchestrator } from '../base';
2
2
  import { mergeRawCoins, buildBalance, mapPositions, mapPositionUpdates } from './mapper';
3
- import { fetchWalletBalance, fetchPositions, fetchAccountInfo, fetchCollateralRatios, fetchPositionLeverage, setLeverage } from './rest';
3
+ import { fetchWalletBalance, fetchPositions, fetchAccountInfo, fetchPositionLeverage, switchMarginMode } from './rest';
4
4
  import { BybitWs } from './ws';
5
5
 
6
6
  class BybitOrchestrator extends BaseOrchestrator {
@@ -9,7 +9,6 @@ class BybitOrchestrator extends BaseOrchestrator {
9
9
  lastWallet = null;
10
10
  trackedAssetInfos = /* @__PURE__ */ new Map();
11
11
  marginMode = "cross";
12
- collateralRatios = /* @__PURE__ */ new Map();
13
12
  async start(credentials) {
14
13
  const gen = ++this.startGeneration;
15
14
  this.manualClose = false;
@@ -17,11 +16,10 @@ class BybitOrchestrator extends BaseOrchestrator {
17
16
  const creds = credentials;
18
17
  this.stopAll();
19
18
  try {
20
- const [wallet, positions, accountInfo, ratios] = await Promise.all([
19
+ const [wallet, positions, accountInfo] = await Promise.all([
21
20
  fetchWalletBalance(creds, this.demo),
22
21
  fetchPositions(creds, this.demo),
23
- fetchAccountInfo(creds, this.demo),
24
- fetchCollateralRatios(creds, this.demo)
22
+ fetchAccountInfo(creds, this.demo)
25
23
  ]);
26
24
  if (gen !== this.startGeneration) return;
27
25
  const MARGIN_MODE_MAP = {
@@ -31,12 +29,11 @@ class BybitOrchestrator extends BaseOrchestrator {
31
29
  };
32
30
  const rawMode = accountInfo?.marginMode ?? "REGULAR_MARGIN";
33
31
  this.marginMode = MARGIN_MODE_MAP[rawMode] ?? "cross";
34
- this.collateralRatios = ratios;
35
32
  if (wallet) {
36
33
  this.lastWallet = wallet;
37
34
  this.rawCoins = mergeRawCoins(/* @__PURE__ */ new Map(), wallet.coin ?? []);
38
35
  this.onStateUpdate({
39
- balance: buildBalance(wallet, this.rawCoins, this.marginMode, this.collateralRatios)
36
+ balance: buildBalance(wallet, this.rawCoins, this.marginMode)
40
37
  });
41
38
  }
42
39
  this.onStateUpdate({ positions: mapPositions(positions) });
@@ -67,7 +64,6 @@ class BybitOrchestrator extends BaseOrchestrator {
67
64
  this.rawCoins.clear();
68
65
  this.lastWallet = null;
69
66
  this.trackedAssetInfos.clear();
70
- this.collateralRatios.clear();
71
67
  this.marginMode = "cross";
72
68
  this.credentials = null;
73
69
  }
@@ -83,10 +79,7 @@ class BybitOrchestrator extends BaseOrchestrator {
83
79
  const info = {
84
80
  coin,
85
81
  leverage,
86
- marginType,
87
- availableToTrade: null,
88
- maxTradeSzs: null,
89
- collateralToken: null
82
+ marginType
90
83
  };
91
84
  this.trackedAssetInfos.set(coin, info);
92
85
  const trackedAssets = /* @__PURE__ */ new Map();
@@ -100,10 +93,11 @@ class BybitOrchestrator extends BaseOrchestrator {
100
93
  this.trackedAssetInfos.delete(coin);
101
94
  this.onStateUpdate({ trackedAssets: new Map(this.trackedAssetInfos) });
102
95
  }
103
- async setLeverage(symbol, leverage) {
96
+ async setLeverage(symbol, leverage, marginMode) {
104
97
  const creds = this.credentials;
105
98
  if (!creds) throw new Error("Not connected");
106
- await setLeverage(creds, symbol, leverage, this.demo);
99
+ const mode = marginMode ?? "cross";
100
+ await switchMarginMode(creds, symbol, mode === "isolated" ? 1 : 0, leverage, this.demo);
107
101
  }
108
102
  stopAll() {
109
103
  this.clearReconnectTimer();
@@ -151,7 +145,7 @@ class BybitOrchestrator extends BaseOrchestrator {
151
145
  } : raw;
152
146
  this.lastWallet = merged;
153
147
  this.onStateUpdate({
154
- balance: buildBalance(merged, this.rawCoins, this.marginMode, this.collateralRatios)
148
+ balance: buildBalance(merged, this.rawCoins, this.marginMode)
155
149
  });
156
150
  }
157
151
  }
@@ -5,7 +5,8 @@ declare function fetchWalletBalance(creds: BybitCredentialsDTO, demo: boolean):
5
5
  declare function fetchPositions(creds: BybitCredentialsDTO, demo: boolean): Promise<BybitRawPosition[]>;
6
6
  declare function fetchPositionLeverage(creds: BybitCredentialsDTO, symbol: string, demo: boolean): Promise<BybitRawPosition | undefined>;
7
7
  declare function setLeverage(creds: BybitCredentialsDTO, symbol: string, leverage: string, demo: boolean): Promise<void>;
8
+ declare function switchMarginMode(creds: BybitCredentialsDTO, symbol: string, tradeMode: 0 | 1, leverage: string, demo: boolean): Promise<void>;
8
9
  declare function fetchAccountInfo(creds: BybitCredentialsDTO, demo: boolean): Promise<BybitRawAccountInfo>;
9
10
  declare function fetchCollateralRatios(creds: BybitCredentialsDTO, demo: boolean): Promise<Map<string, string>>;
10
11
 
11
- export { fetchAccountInfo, fetchCollateralRatios, fetchPositionLeverage, fetchPositions, fetchWalletBalance, setLeverage };
12
+ export { fetchAccountInfo, fetchCollateralRatios, fetchPositionLeverage, fetchPositions, fetchWalletBalance, setLeverage, switchMarginMode };
@@ -87,6 +87,14 @@ async function setLeverage(creds, symbol, leverage, demo) {
87
87
  demo
88
88
  );
89
89
  }
90
+ async function switchMarginMode(creds, symbol, tradeMode, leverage, demo) {
91
+ await signedPost(
92
+ creds,
93
+ "/v5/position/switch-isolated",
94
+ { category: "linear", symbol, tradeMode, buyLeverage: leverage, sellLeverage: leverage },
95
+ demo
96
+ );
97
+ }
90
98
  async function fetchAccountInfo(creds, demo) {
91
99
  return signedGet(creds, "/v5/account/account-info", {}, demo);
92
100
  }
@@ -107,4 +115,4 @@ async function fetchCollateralRatios(creds, demo) {
107
115
  return ratios;
108
116
  }
109
117
 
110
- export { fetchAccountInfo, fetchCollateralRatios, fetchPositionLeverage, fetchPositions, fetchWalletBalance, setLeverage };
118
+ export { fetchAccountInfo, fetchCollateralRatios, fetchPositionLeverage, fetchPositions, fetchWalletBalance, setLeverage, switchMarginMode };
@@ -0,0 +1,3 @@
1
+ declare const LOCKED_TRACKED_COINS: readonly ["BTC"];
2
+
3
+ export { LOCKED_TRACKED_COINS };
@@ -0,0 +1,3 @@
1
+ const LOCKED_TRACKED_COINS = ["BTC"];
2
+
3
+ export { LOCKED_TRACKED_COINS };
@@ -0,0 +1,9 @@
1
+ import { AccountBalance, AccountPosition } from '../../types.js';
2
+ import { AggregatedClearinghouseSummary, HyperliquidCoinBalance, AssetPosition } from './types.js';
3
+ import '@pear-protocol/types';
4
+ import '@nktkas/hyperliquid/api/subscription';
5
+
6
+ declare function mapBalance(summary: AggregatedClearinghouseSummary, spotCoinBalances: Map<string, HyperliquidCoinBalance>, accountType: string, availableToTradeByCollateral: Map<string, number>): AccountBalance;
7
+ declare function mapPositions(raw: AssetPosition[]): Map<string, AccountPosition>;
8
+
9
+ export { mapBalance, mapPositions };
@@ -0,0 +1,68 @@
1
+ import { positionKey } from '../../utils';
2
+ import { COLLATERAL_TOKEN_MAP } from './types';
3
+
4
+ const COLLATERAL_COINS = new Set(Object.values(COLLATERAL_TOKEN_MAP));
5
+ function mapBalance(summary, spotCoinBalances, accountType, availableToTradeByCollateral) {
6
+ const free = {};
7
+ const used = {};
8
+ const total = {};
9
+ const assets = [];
10
+ for (const [, coin] of spotCoinBalances) {
11
+ if (!COLLATERAL_COINS.has(coin.coin)) continue;
12
+ const availableOverride = availableToTradeByCollateral.get(coin.coin);
13
+ const coinFree = availableOverride !== void 0 ? availableOverride.toString() : (coin.total - coin.hold).toString();
14
+ const coinUsed = coin.hold.toString();
15
+ const coinTotal = coin.total.toString();
16
+ free[coin.coin] = coinFree;
17
+ used[coin.coin] = coinUsed;
18
+ total[coin.coin] = coinTotal;
19
+ assets.push({
20
+ asset: coin.coin,
21
+ free: coinFree,
22
+ used: coinUsed,
23
+ total: coinTotal,
24
+ walletBalance: coinTotal,
25
+ usdValue: coinTotal
26
+ });
27
+ }
28
+ const walletBalance = summary.accountValue - summary.totalUnrealizedPnl;
29
+ const marginRatio = summary.accountValue > 0 ? (summary.crossMaintenanceMarginUsed / summary.accountValue).toFixed(4) : "0.0000";
30
+ return {
31
+ accountType,
32
+ free,
33
+ used,
34
+ total,
35
+ availableToTrade: summary.withdrawable.toString(),
36
+ totalEquity: summary.accountValue.toString(),
37
+ walletBalance: walletBalance.toString(),
38
+ unrealizedPnl: summary.totalUnrealizedPnl.toString(),
39
+ initialMarginUsed: summary.totalMarginUsed.toString(),
40
+ maintenanceMargin: summary.crossMaintenanceMarginUsed.toString(),
41
+ marginRatio,
42
+ assets,
43
+ timestamp: Date.now()
44
+ };
45
+ }
46
+ function mapPositions(raw) {
47
+ const positions = /* @__PURE__ */ new Map();
48
+ for (const ap of raw) {
49
+ const pos = ap.position;
50
+ const szi = Number.parseFloat(pos.szi || "0");
51
+ if (szi === 0) continue;
52
+ const coin = pos.coin || "";
53
+ const side = szi > 0 ? "long" : "short";
54
+ positions.set(positionKey(coin, side), {
55
+ symbol: coin,
56
+ side,
57
+ size: szi.toString(),
58
+ entryPrice: pos.entryPx || "0",
59
+ unrealizedPnl: pos.unrealizedPnl || "0",
60
+ leverage: pos.leverage.value.toString(),
61
+ marginType: pos.leverage.type,
62
+ liquidationPrice: pos.liquidationPx || null
63
+ });
64
+ }
65
+ return positions;
66
+ }
67
+
68
+ export { mapBalance, mapPositions };
@@ -0,0 +1,38 @@
1
+ import { InstrumentId } from '@pear-protocol/types';
2
+ import { SupportedCredentials, MarginMode } from '../../types.js';
3
+ import { BaseOrchestrator } from '../base.js';
4
+
5
+ declare class HyperliquidOrchestrator extends BaseOrchestrator {
6
+ private static httpTransport;
7
+ private static wsTransport;
8
+ private infoClient;
9
+ private subsClient;
10
+ private subscriptions;
11
+ private address;
12
+ private activeAssetSubscriptions;
13
+ private trackedAssetData;
14
+ private spotCoinBalances;
15
+ private accountType;
16
+ private symbolToCollateralToken;
17
+ private availableToTradeByCollateral;
18
+ private lastSummary;
19
+ start(credentials: SupportedCredentials): Promise<void>;
20
+ stop(): void;
21
+ get isConnected(): boolean;
22
+ onAssetTracked(coin: string): void;
23
+ onAssetUntracked(coin: string): void;
24
+ setLeverage(id: InstrumentId, leverage: string, marginMode?: MarginMode): Promise<void>;
25
+ protected canUntrackAsset(coin: string): boolean;
26
+ private fetchPerpMetas;
27
+ private subscribeActiveAssetData;
28
+ private stopAll;
29
+ private handleClearinghouseState;
30
+ private handleActiveAssetData;
31
+ private buildTrackedAssetInfo;
32
+ private buildTrackedAssetsMap;
33
+ private buildBalance;
34
+ private handleWebData3;
35
+ private handleSpotState;
36
+ }
37
+
38
+ export { HyperliquidOrchestrator };
@@ -0,0 +1,238 @@
1
+ import { HttpTransport, WebSocketTransport, InfoClient, SubscriptionClient } from '@nktkas/hyperliquid';
2
+ import { BaseOrchestrator } from '../base';
3
+ import { LOCKED_TRACKED_COINS } from './const';
4
+ import { mapPositions, mapBalance } from './mapper';
5
+ import { setLeverage } from './rest';
6
+ import { COLLATERAL_TOKEN_MAP } from './types';
7
+
8
+ class HyperliquidOrchestrator extends BaseOrchestrator {
9
+ static httpTransport = new HttpTransport();
10
+ static wsTransport = new WebSocketTransport({
11
+ reconnect: {
12
+ maxRetries: Number.MAX_SAFE_INTEGER
13
+ }
14
+ });
15
+ infoClient = null;
16
+ subsClient = null;
17
+ subscriptions = [];
18
+ address = null;
19
+ activeAssetSubscriptions = /* @__PURE__ */ new Map();
20
+ trackedAssetData = /* @__PURE__ */ new Map();
21
+ spotCoinBalances = /* @__PURE__ */ new Map();
22
+ accountType = "disabled";
23
+ symbolToCollateralToken = /* @__PURE__ */ new Map();
24
+ availableToTradeByCollateral = /* @__PURE__ */ new Map();
25
+ lastSummary = {
26
+ accountValue: 0,
27
+ totalMarginUsed: 0,
28
+ totalRawUsd: 0,
29
+ crossMaintenanceMarginUsed: 0,
30
+ withdrawable: 0,
31
+ totalUnrealizedPnl: 0
32
+ };
33
+ async start(credentials) {
34
+ const gen = ++this.startGeneration;
35
+ this.manualClose = false;
36
+ this.credentials = credentials;
37
+ const creds = credentials;
38
+ this.address = creds.address;
39
+ await this.stopAll();
40
+ this.trackedAssetData.clear();
41
+ this.spotCoinBalances.clear();
42
+ this.symbolToCollateralToken.clear();
43
+ this.availableToTradeByCollateral.clear();
44
+ try {
45
+ this.infoClient = new InfoClient({ transport: HyperliquidOrchestrator.httpTransport });
46
+ await this.fetchPerpMetas();
47
+ if (gen !== this.startGeneration) return;
48
+ this.subsClient = new SubscriptionClient({ transport: HyperliquidOrchestrator.wsTransport });
49
+ const subs = [];
50
+ subs.push(
51
+ this.subsClient.allDexsClearinghouseState(
52
+ { user: this.address },
53
+ (data) => this.handleClearinghouseState(data)
54
+ )
55
+ );
56
+ subs.push(this.subsClient.spotState({ user: this.address }, (data) => this.handleSpotState(data)));
57
+ subs.push(this.subsClient.webData3({ user: this.address }, (data) => this.handleWebData3(data)));
58
+ this.subscriptions = await Promise.all(subs);
59
+ if (gen !== this.startGeneration) return;
60
+ for (const sub of this.subscriptions) {
61
+ sub.failureSignal.addEventListener("abort", () => {
62
+ if (!this.manualClose) {
63
+ this.onError(new Error("Hyperliquid subscription failed after reconnect"));
64
+ }
65
+ });
66
+ }
67
+ for (const coin of LOCKED_TRACKED_COINS) {
68
+ this.trackAsset(coin);
69
+ }
70
+ for (const coin of this.trackedCoins) {
71
+ if (!LOCKED_TRACKED_COINS.includes(coin)) {
72
+ await this.subscribeActiveAssetData(coin);
73
+ }
74
+ }
75
+ this.reconnectAttempts = 0;
76
+ } catch (err) {
77
+ if (gen !== this.startGeneration) return;
78
+ await this.stopAll();
79
+ this.onError(err instanceof Error ? err : new Error(String(err)));
80
+ this.scheduleReconnect();
81
+ }
82
+ }
83
+ stop() {
84
+ this.manualClose = true;
85
+ this.stopAll();
86
+ this.credentials = null;
87
+ this.address = null;
88
+ }
89
+ get isConnected() {
90
+ return this.subsClient !== null && !this.manualClose;
91
+ }
92
+ onAssetTracked(coin) {
93
+ this.subscribeActiveAssetData(coin).catch((err) => {
94
+ this.onError(err instanceof Error ? err : new Error(String(err)));
95
+ });
96
+ }
97
+ onAssetUntracked(coin) {
98
+ const sub = this.activeAssetSubscriptions.get(coin);
99
+ if (sub) {
100
+ sub.unsubscribe().catch(() => {
101
+ });
102
+ this.activeAssetSubscriptions.delete(coin);
103
+ }
104
+ this.trackedAssetData.delete(coin);
105
+ const trackedAssets = this.buildTrackedAssetsMap();
106
+ this.onStateUpdate({ trackedAssets });
107
+ }
108
+ async setLeverage(id, leverage, marginMode) {
109
+ const creds = this.credentials;
110
+ if (!creds) throw new Error("Not connected");
111
+ const asset = Number.parseInt(id, 10);
112
+ if (Number.isNaN(asset)) throw new Error(`Invalid Hyperliquid asset index: ${id}`);
113
+ const isCross = (marginMode ?? "cross") === "cross";
114
+ await setLeverage(creds, asset, Number.parseFloat(leverage), isCross);
115
+ }
116
+ canUntrackAsset(coin) {
117
+ return !LOCKED_TRACKED_COINS.includes(coin);
118
+ }
119
+ async fetchPerpMetas() {
120
+ if (!this.infoClient) return;
121
+ const allMetas = await this.infoClient.allPerpMetas();
122
+ allMetas.forEach((dexMeta) => {
123
+ const collateralToken = COLLATERAL_TOKEN_MAP[dexMeta.collateralToken];
124
+ dexMeta.universe.forEach((asset) => {
125
+ if (!asset.name) return;
126
+ if (collateralToken && !this.symbolToCollateralToken.has(asset.name)) {
127
+ this.symbolToCollateralToken.set(asset.name, collateralToken);
128
+ }
129
+ });
130
+ });
131
+ }
132
+ async subscribeActiveAssetData(coin) {
133
+ if (!this.subsClient || !this.address) return;
134
+ if (this.activeAssetSubscriptions.has(coin)) return;
135
+ const sub = await this.subsClient.activeAssetData(
136
+ { coin, user: this.address },
137
+ (data) => this.handleActiveAssetData(data)
138
+ );
139
+ sub.failureSignal.addEventListener("abort", () => {
140
+ if (!this.manualClose) {
141
+ this.activeAssetSubscriptions.delete(coin);
142
+ }
143
+ });
144
+ this.activeAssetSubscriptions.set(coin, sub);
145
+ }
146
+ async stopAll() {
147
+ this.clearReconnectTimer();
148
+ const assetUnsubs = Array.from(this.activeAssetSubscriptions.values()).map((s) => s.unsubscribe().catch(() => {
149
+ }));
150
+ this.activeAssetSubscriptions.clear();
151
+ const unsubs = this.subscriptions.map((s) => s.unsubscribe().catch(() => {
152
+ }));
153
+ this.subscriptions = [];
154
+ await Promise.all([...unsubs, ...assetUnsubs]);
155
+ this.subsClient = null;
156
+ this.infoClient = null;
157
+ }
158
+ handleClearinghouseState(data) {
159
+ const states = (data.clearinghouseStates || []).map(([, s]) => s).filter(Boolean);
160
+ const sumStr = (values) => values.reduce((acc, v) => acc + (Number.parseFloat(v || "0") || 0), 0);
161
+ const allAssetPositions = states.flatMap((s) => s.assetPositions || []);
162
+ const totalUnrealizedPnl = allAssetPositions.reduce(
163
+ (acc, ap) => acc + (Number.parseFloat(ap.position.unrealizedPnl || "0") || 0),
164
+ 0
165
+ );
166
+ this.lastSummary = {
167
+ accountValue: sumStr(states.map((s) => s.crossMarginSummary.accountValue)),
168
+ totalMarginUsed: sumStr(states.map((s) => s.crossMarginSummary.totalMarginUsed)),
169
+ totalRawUsd: sumStr(states.map((s) => s.crossMarginSummary.totalRawUsd)),
170
+ crossMaintenanceMarginUsed: sumStr(states.map((s) => s.crossMaintenanceMarginUsed)),
171
+ withdrawable: sumStr(states.map((s) => s.withdrawable)),
172
+ totalUnrealizedPnl
173
+ };
174
+ const balance = this.buildBalance();
175
+ const positions = mapPositions(allAssetPositions);
176
+ const leverageSettings = /* @__PURE__ */ new Map();
177
+ for (const ap of allAssetPositions) {
178
+ const coin = ap.position.coin;
179
+ if (coin && ap.position.leverage?.value) {
180
+ leverageSettings.set(coin, ap.position.leverage.value.toString());
181
+ }
182
+ }
183
+ this.onStateUpdate({ balance, positions, leverageSettings });
184
+ }
185
+ handleActiveAssetData(data) {
186
+ if (!data.coin) return;
187
+ this.trackedAssetData.set(data.coin, data);
188
+ const collateral = this.symbolToCollateralToken.get(data.coin);
189
+ if (collateral && data.availableToTrade) {
190
+ const long = Number.parseFloat(data.availableToTrade[0] || "0");
191
+ const short = Number.parseFloat(data.availableToTrade[1] || "0");
192
+ const minAvailable = Math.min(long, short);
193
+ this.availableToTradeByCollateral.set(collateral, minAvailable);
194
+ const balance = this.buildBalance();
195
+ this.onStateUpdate({ balance });
196
+ }
197
+ const trackedAssets = /* @__PURE__ */ new Map();
198
+ trackedAssets.set(data.coin, this.buildTrackedAssetInfo(data));
199
+ this.onStateUpdate({ trackedAssets });
200
+ }
201
+ buildTrackedAssetInfo(data) {
202
+ return {
203
+ coin: data.coin,
204
+ leverage: (data.leverage?.value ?? 1).toString(),
205
+ marginType: data.leverage?.type === "isolated" ? "isolated" : "cross"
206
+ };
207
+ }
208
+ buildTrackedAssetsMap() {
209
+ const result = /* @__PURE__ */ new Map();
210
+ for (const [coin, data] of this.trackedAssetData) {
211
+ result.set(coin, this.buildTrackedAssetInfo(data));
212
+ }
213
+ return result;
214
+ }
215
+ buildBalance() {
216
+ return mapBalance(this.lastSummary, this.spotCoinBalances, this.accountType, this.availableToTradeByCollateral);
217
+ }
218
+ handleWebData3(data) {
219
+ this.accountType = data.userState.abstraction || "disabled";
220
+ }
221
+ handleSpotState(data) {
222
+ const balances = data?.spotState?.balances;
223
+ if (!balances) return;
224
+ for (const b of balances) {
225
+ const total = Number.parseFloat(b.total || "0");
226
+ const hold = Number.parseFloat(b.hold || "0");
227
+ if (total !== 0 || hold !== 0) {
228
+ this.spotCoinBalances.set(b.coin, { coin: b.coin, total, hold });
229
+ } else {
230
+ this.spotCoinBalances.delete(b.coin);
231
+ }
232
+ }
233
+ const balance = this.buildBalance();
234
+ this.onStateUpdate({ balance });
235
+ }
236
+ }
237
+
238
+ export { HyperliquidOrchestrator };
@@ -0,0 +1,6 @@
1
+ import { HyperliquidAccountCredentials } from '../../types.js';
2
+ import '@pear-protocol/types';
3
+
4
+ declare function setLeverage(creds: HyperliquidAccountCredentials, asset: number, leverage: number, isCross: boolean): Promise<void>;
5
+
6
+ export { setLeverage };
@@ -0,0 +1,11 @@
1
+ import { HttpTransport, ExchangeClient } from '@nktkas/hyperliquid';
2
+ import { privateKeyToAccount } from 'viem/accounts';
3
+
4
+ async function setLeverage(creds, asset, leverage, isCross) {
5
+ const transport = new HttpTransport();
6
+ const wallet = privateKeyToAccount(creds.signer_key);
7
+ const exchangeClient = new ExchangeClient({ transport, wallet });
8
+ await exchangeClient.updateLeverage({ asset, leverage, isCross });
9
+ }
10
+
11
+ export { setLeverage };
@@ -0,0 +1,20 @@
1
+ import { AllDexsClearinghouseStateEvent } from '@nktkas/hyperliquid/api/subscription';
2
+
3
+ type ClearinghouseState = AllDexsClearinghouseStateEvent['clearinghouseStates'][number][1];
4
+ type AssetPosition = ClearinghouseState['assetPositions'][number];
5
+ interface HyperliquidCoinBalance {
6
+ coin: string;
7
+ total: number;
8
+ hold: number;
9
+ }
10
+ interface AggregatedClearinghouseSummary {
11
+ accountValue: number;
12
+ totalMarginUsed: number;
13
+ totalRawUsd: number;
14
+ crossMaintenanceMarginUsed: number;
15
+ withdrawable: number;
16
+ totalUnrealizedPnl: number;
17
+ }
18
+ declare const COLLATERAL_TOKEN_MAP: Record<number, string>;
19
+
20
+ export { type AggregatedClearinghouseSummary, type AssetPosition, COLLATERAL_TOKEN_MAP, type ClearinghouseState, type HyperliquidCoinBalance };
@@ -0,0 +1,8 @@
1
+ const COLLATERAL_TOKEN_MAP = {
2
+ 0: "USDC",
3
+ 235: "USDE",
4
+ 268: "USDT0",
5
+ 360: "USDH"
6
+ };
7
+
8
+ export { COLLATERAL_TOKEN_MAP };
@@ -1,6 +1,7 @@
1
1
  import './base';
2
2
  import { BinanceOrchestrator } from './binance/orchestrator';
3
3
  import { BybitOrchestrator } from './bybit/orchestrator';
4
+ import { HyperliquidOrchestrator } from './hyperliquid/orchestrator';
4
5
 
5
6
  function createOrchestrator(exchange, config) {
6
7
  switch (exchange) {
@@ -8,6 +9,8 @@ function createOrchestrator(exchange, config) {
8
9
  return new BinanceOrchestrator(config);
9
10
  case "bybit":
10
11
  return new BybitOrchestrator(config);
12
+ case "hyperliquid":
13
+ return new HyperliquidOrchestrator(config);
11
14
  default:
12
15
  throw new Error(`Unsupported exchange: ${exchange}`);
13
16
  }
@@ -1,8 +1,8 @@
1
1
  export { AccountManager } from './account-manager.js';
2
2
  export { ExchangeAccount } from './exchange-account.js';
3
3
  export { createOrchestrator } from './exchanges/index.js';
4
- export { AccountBalance, AccountConfig, AccountPosition, AssetBalance, BalanceCallback, ExchangeState, MarginMode, PositionsCallback, StateUpdate, SupportedCredentials, TrackedAssetCallback, TrackedAssetInfo, Tracker } from './types.js';
4
+ export { AccountBalance, AccountConfig, AccountPosition, AssetBalance, BalanceCallback, ExchangeState, HyperliquidAccountCredentials, MarginMode, PositionsCallback, StateUpdate, SupportedCredentials, TrackedAssetCallback, TrackedAssetInfo, Tracker } from './types.js';
5
5
  export { Orchestrator, OrchestratorConfig } from './exchanges/base.js';
6
+ import '@pear-protocol/core-sdk';
6
7
  import '@pear-protocol/types';
7
8
  import '../credentials.js';
8
- import '@pear-protocol/core-sdk';
@@ -1,4 +1,4 @@
1
- import { Connector, BinanceUSDMCredentialsDTO, BybitCredentialsDTO } from '@pear-protocol/types';
1
+ import { Connector, BinanceUSDMCredentialsDTO, BybitCredentialsDTO, HyperliquidCredentialsDTO } from '@pear-protocol/types';
2
2
 
3
3
  type MarginMode = 'cross' | 'isolated';
4
4
  interface AssetBalance {
@@ -7,18 +7,10 @@ interface AssetBalance {
7
7
  used: string;
8
8
  total: string;
9
9
  walletBalance: string;
10
- unrealizedPnl: string;
11
10
  usdValue: string;
12
- initialMargin: string;
13
- maintenanceMargin: string;
14
- isCollateral: boolean;
15
- collateralRatio: string;
16
- borrowedAmount: string;
17
11
  }
18
12
  interface AccountBalance {
19
- exchange: string;
20
13
  accountType: string;
21
- marginMode: string;
22
14
  free: Record<string, string>;
23
15
  used: Record<string, string>;
24
16
  total: Record<string, string>;
@@ -46,9 +38,6 @@ interface TrackedAssetInfo {
46
38
  coin: string;
47
39
  leverage: string;
48
40
  marginType: MarginMode;
49
- availableToTrade: [string, string] | null;
50
- maxTradeSzs: [string, string] | null;
51
- collateralToken: string | null;
52
41
  }
53
42
  interface ExchangeState {
54
43
  balance: AccountBalance | null;
@@ -70,7 +59,10 @@ interface AccountConfig {
70
59
  demo?: boolean;
71
60
  onAuthError?: () => void;
72
61
  }
73
- type SupportedCredentials = BinanceUSDMCredentialsDTO | BybitCredentialsDTO;
62
+ type HyperliquidAccountCredentials = HyperliquidCredentialsDTO & {
63
+ address: string;
64
+ };
65
+ type SupportedCredentials = BinanceUSDMCredentialsDTO | BybitCredentialsDTO | HyperliquidAccountCredentials;
74
66
  interface StateUpdate {
75
67
  balance?: AccountBalance;
76
68
  positions?: Map<string, AccountPosition>;
@@ -78,4 +70,4 @@ interface StateUpdate {
78
70
  trackedAssets?: Map<string, TrackedAssetInfo>;
79
71
  }
80
72
 
81
- export type { AccountBalance, AccountConfig, AccountPosition, AssetBalance, BalanceCallback, ExchangeState, MarginMode, PositionsCallback, StateUpdate, SupportedCredentials, TrackedAssetCallback, TrackedAssetInfo, Tracker };
73
+ export type { AccountBalance, AccountConfig, AccountPosition, AssetBalance, BalanceCallback, ExchangeState, HyperliquidAccountCredentials, MarginMode, PositionsCallback, StateUpdate, SupportedCredentials, TrackedAssetCallback, TrackedAssetInfo, Tracker };
package/dist/index.d.ts CHANGED
@@ -2,7 +2,7 @@ import PearSDK from '@pear-protocol/core-sdk';
2
2
  import { AccountManager } from './account/account-manager.js';
3
3
  export { ExchangeAccount } from './account/exchange-account.js';
4
4
  export { createOrchestrator } from './account/exchanges/index.js';
5
- export { AccountBalance, AccountConfig, AccountPosition, AssetBalance, BalanceCallback, ExchangeState, MarginMode, PositionsCallback, StateUpdate, SupportedCredentials, TrackedAssetCallback, TrackedAssetInfo, Tracker } from './account/types.js';
5
+ export { AccountBalance, AccountConfig, AccountPosition, AssetBalance, BalanceCallback, ExchangeState, HyperliquidAccountCredentials, MarginMode, PositionsCallback, StateUpdate, SupportedCredentials, TrackedAssetCallback, TrackedAssetInfo, Tracker } from './account/types.js';
6
6
  import { Credentials } from './credentials.js';
7
7
  export { Orchestrator, OrchestratorConfig } from './account/exchanges/base.js';
8
8
  import '@pear-protocol/types';
package/dist/index.js CHANGED
@@ -10,7 +10,7 @@ class ExchangesSDK {
10
10
  constructor(config) {
11
11
  this.sdk = config.sdk;
12
12
  this.credentials = new Credentials(config.sdk);
13
- this.account = new AccountManager(this.credentials, config.demo);
13
+ this.account = new AccountManager(this.credentials, config.sdk, config.demo);
14
14
  }
15
15
  destroy() {
16
16
  this.account.destroy();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pear-protocol/exchanges-sdk",
3
- "version": "0.0.6",
3
+ "version": "0.0.8-test.0",
4
4
  "description": "Pear Protocol Exchanges SDK",
5
5
  "private": false,
6
6
  "type": "module",
@@ -22,14 +22,17 @@
22
22
  "scripts": {
23
23
  "build": "tsup",
24
24
  "clean": "rm -rf dist .turbo",
25
- "typecheck": "tsc --noEmit"
25
+ "typecheck": "tsc --noEmit",
26
+ "publish:test": "tsup && bun publish --tag test"
26
27
  },
27
28
  "dependencies": {
29
+ "@nktkas/hyperliquid": "0.30.2",
28
30
  "@pear-protocol/core-sdk": "0.0.8",
29
- "@pear-protocol/types": "0.0.7"
31
+ "@pear-protocol/types": "0.0.7",
32
+ "viem": "^2.43.5"
30
33
  },
31
34
  "devDependencies": {
32
- "@backend/typescript-config": "workspace:*",
35
+ "@backend/typescript-config": "0.0.0",
33
36
  "tsup": "8.5.1",
34
37
  "typescript": "5.9.3"
35
38
  }