@pear-protocol/exchanges-sdk 0.15.0 → 0.16.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.
@@ -86,6 +86,19 @@ async function fetchUserVaultEquities(user, demo = false) {
86
86
  }, demo);
87
87
  }
88
88
  __name(fetchUserVaultEquities, "fetchUserVaultEquities");
89
+ async function fetchBorrowLendUserState(user, demo = false) {
90
+ return fetchInfo({
91
+ type: "borrowLendUserState",
92
+ user
93
+ }, demo);
94
+ }
95
+ __name(fetchBorrowLendUserState, "fetchBorrowLendUserState");
96
+ async function fetchAllBorrowLendReserveStates(demo = false) {
97
+ return fetchInfo({
98
+ type: "allBorrowLendReserveStates"
99
+ }, demo);
100
+ }
101
+ __name(fetchAllBorrowLendReserveStates, "fetchAllBorrowLendReserveStates");
89
102
  async function fetchActiveAssetData(user, coin, demo = false) {
90
103
  return fetchInfo({
91
104
  type: "activeAssetData",
@@ -95,4 +108,4 @@ async function fetchActiveAssetData(user, coin, demo = false) {
95
108
  }
96
109
  __name(fetchActiveAssetData, "fetchActiveAssetData");
97
110
 
98
- export { HyperliquidInfoRequestError, fetchActiveAssetData, fetchAllMids, fetchClearinghouseState, fetchDelegatorSummary, fetchInfo, fetchPerpDexs, fetchSpotClearinghouseState, fetchUserAbstraction, fetchUserRole, fetchUserVaultEquities };
111
+ export { HyperliquidInfoRequestError, fetchActiveAssetData, fetchAllBorrowLendReserveStates, fetchAllMids, fetchBorrowLendUserState, fetchClearinghouseState, fetchDelegatorSummary, fetchInfo, fetchPerpDexs, fetchSpotClearinghouseState, fetchUserAbstraction, fetchUserRole, fetchUserVaultEquities };
package/dist/index.d.ts CHANGED
@@ -1447,6 +1447,7 @@ declare const AccountBalance: z.ZodDiscriminatedUnion<[z.ZodObject<{
1447
1447
  withdrawable: z.ZodString;
1448
1448
  ltv: z.ZodNullable<z.ZodString>;
1449
1449
  borrowCapUsed: z.ZodNullable<z.ZodString>;
1450
+ borrowed: z.ZodString;
1450
1451
  }, z.core.$strip>>;
1451
1452
  tradingEquity: z.ZodNullable<z.ZodString>;
1452
1453
  vaultEquity: z.ZodString;
@@ -1458,6 +1459,13 @@ declare const AccountBalance: z.ZodDiscriminatedUnion<[z.ZodObject<{
1458
1459
  portfolioMarginRatio: z.ZodNullable<z.ZodString>;
1459
1460
  portfolioAccountLeverage: z.ZodNullable<z.ZodString>;
1460
1461
  borrowCapUsed: z.ZodNullable<z.ZodString>;
1462
+ borrowedValue: z.ZodNullable<z.ZodString>;
1463
+ healthFactor: z.ZodNullable<z.ZodString>;
1464
+ borrowLendReserves: z.ZodArray<z.ZodObject<{
1465
+ asset: z.ZodString;
1466
+ borrowYearlyRate: z.ZodString;
1467
+ supplyYearlyRate: z.ZodString;
1468
+ }, z.core.$strip>>;
1461
1469
  perDexState: z.ZodArray<z.ZodObject<{
1462
1470
  dex: z.ZodString;
1463
1471
  collateralToken: z.ZodNullable<z.ZodNumber>;
@@ -2174,8 +2182,9 @@ type HyperliquidUnifiedBalance = z.infer<typeof HyperliquidUnifiedBalance>;
2174
2182
  * accounts measured hold one — `0x0871deb3…` reports USDC at −957,802.15. An unsigned row cannot
2175
2183
  * state that at all, which is the defect that made a borrowed pool indistinguishable from an
2176
2184
  * untouched one.
2177
- * - **two more columns.** `ltv` and `borrowCapUsed` are per-coin risk weights the venue renders
2178
- * beside the quantities, and both are PM-only wire fields.
2185
+ * - **three more columns.** `ltv` and `borrowCapUsed` are per-coin risk weights the venue renders
2186
+ * beside the quantities, and both are PM-only wire fields. `borrowed`, the coin's outstanding
2187
+ * debt, is the third, and PM-only as well.
2179
2188
  *
2180
2189
  * The row is otherwise the shared shape: same keys, same meanings, so a consumer reading
2181
2190
  * `perCollateral` without narrowing still reads `asset` / `equity` / `availableMargin` /
@@ -2188,8 +2197,16 @@ declare const HyperliquidPortfolioMarginCollateral: z.ZodObject<{
2188
2197
  withdrawable: z.ZodString;
2189
2198
  ltv: z.ZodNullable<z.ZodString>;
2190
2199
  borrowCapUsed: z.ZodNullable<z.ZodString>;
2200
+ borrowed: z.ZodString;
2191
2201
  }, z.core.$strip>;
2192
2202
  type HyperliquidPortfolioMarginCollateral = z.infer<typeof HyperliquidPortfolioMarginCollateral>;
2203
+ /** One borrow/lend reserve's yearly rates, named by its coin's wire name. */
2204
+ declare const HyperliquidBorrowLendReserve: z.ZodObject<{
2205
+ asset: z.ZodString;
2206
+ borrowYearlyRate: z.ZodString;
2207
+ supplyYearlyRate: z.ZodString;
2208
+ }, z.core.$strip>;
2209
+ type HyperliquidBorrowLendReserve = z.infer<typeof HyperliquidBorrowLendReserve>;
2193
2210
  declare const HyperliquidPortfolioMarginBalance: z.ZodObject<{
2194
2211
  totalEquity: z.ZodNullable<z.ZodString>;
2195
2212
  withdrawableBalance: z.ZodNullable<z.ZodString>;
@@ -2204,6 +2221,7 @@ declare const HyperliquidPortfolioMarginBalance: z.ZodObject<{
2204
2221
  withdrawable: z.ZodString;
2205
2222
  ltv: z.ZodNullable<z.ZodString>;
2206
2223
  borrowCapUsed: z.ZodNullable<z.ZodString>;
2224
+ borrowed: z.ZodString;
2207
2225
  }, z.core.$strip>>;
2208
2226
  tradingEquity: z.ZodNullable<z.ZodString>;
2209
2227
  vaultEquity: z.ZodString;
@@ -2215,6 +2233,13 @@ declare const HyperliquidPortfolioMarginBalance: z.ZodObject<{
2215
2233
  portfolioMarginRatio: z.ZodNullable<z.ZodString>;
2216
2234
  portfolioAccountLeverage: z.ZodNullable<z.ZodString>;
2217
2235
  borrowCapUsed: z.ZodNullable<z.ZodString>;
2236
+ borrowedValue: z.ZodNullable<z.ZodString>;
2237
+ healthFactor: z.ZodNullable<z.ZodString>;
2238
+ borrowLendReserves: z.ZodArray<z.ZodObject<{
2239
+ asset: z.ZodString;
2240
+ borrowYearlyRate: z.ZodString;
2241
+ supplyYearlyRate: z.ZodString;
2242
+ }, z.core.$strip>>;
2218
2243
  perDexState: z.ZodArray<z.ZodObject<{
2219
2244
  dex: z.ZodString;
2220
2245
  collateralToken: z.ZodNullable<z.ZodNumber>;
@@ -2232,9 +2257,10 @@ type HyperliquidPortfolioMarginBalance = z.infer<typeof HyperliquidPortfolioMarg
2232
2257
  * Both fill the same `HyperliquidBalanceContext` and hand it to the same mapper, which is what
2233
2258
  * makes "same account state, same balance" true across two different transports. The tracker takes
2234
2259
  * every CHANGING value off a channel — `allDexsClearinghouseState`, `spotState`, `webData3`,
2235
- * `allMids`, `activeAssetData` — including vault equity, which rides `webData3`. Its only REST
2236
- * reads happen at `start()`: the static dex metadata, and the staking position, which the venue
2237
- * publishes on no channel at all.
2260
+ * `allMids`, `activeAssetData` — including vault equity, which rides `webData3`, save one:
2261
+ * Portfolio Margin's health factor and reserve rates ride no channel, so it polls them while the
2262
+ * account is in that mode. Its only other REST reads happen at `start()`: the static dex metadata,
2263
+ * and the staking position, which the venue publishes on no channel at all.
2238
2264
  *
2239
2265
  * The snapshot has no channels, so it reads the same figures over REST — vault equity from
2240
2266
  * `userVaultEquities`, which was measured equal to the `webData3` figure on ten live accounts.
@@ -2256,6 +2282,11 @@ declare class HyperliquidAccountTracker extends BaseAccountTracker {
2256
2282
  /** Portfolio Margin's own risk scalars, which ride the same `spotState` frame the balances do. */
2257
2283
  private portfolioMarginRatio;
2258
2284
  private borrowRatioByToken;
2285
+ /** Portfolio Margin's REST-only figures, and the timer that keeps them fresh while in that mode. */
2286
+ private borrowLend;
2287
+ private borrowLendPollTimer;
2288
+ /** Advances on every stop, so a read fired in an earlier poll period is recognised and dropped. */
2289
+ private borrowLendPollGeneration;
2259
2290
  private hasSpotState;
2260
2291
  private hasClearinghouseState;
2261
2292
  private accountType;
@@ -2314,6 +2345,27 @@ declare class HyperliquidAccountTracker extends BaseAccountTracker {
2314
2345
  * vault. Partial equity is worse than no equity, so nothing is published early.
2315
2346
  */
2316
2347
  private buildBalance;
2348
+ /**
2349
+ * Runs the borrow/lend poll exactly while the account is in Portfolio Margin.
2350
+ *
2351
+ * Safe to call on every mode read: a poll already in the state the mode asks for is left alone,
2352
+ * so a steady stream of `webData3` frames neither restarts the timer nor refires a read.
2353
+ */
2354
+ private syncBorrowLendPoll;
2355
+ /**
2356
+ * Stops the borrow/lend poll and drops its figures, so no later mode or session inherits them.
2357
+ *
2358
+ * Every stop opens a new generation — a session ending, a restart, or the account leaving
2359
+ * Portfolio Margin — so a read still in flight from the period just ended can never land.
2360
+ */
2361
+ private stopBorrowLendPoll;
2362
+ /**
2363
+ * Reads the health factor and reserve rates once, then republishes the balance with them.
2364
+ *
2365
+ * A failed read is reported and left for the next tick: the previous figures stay in place, since
2366
+ * one missed read says nothing about the account that the last good one did not.
2367
+ */
2368
+ private refreshBorrowLendState;
2317
2369
  /** The floor of `availableToTrade` over the tracked set; `null` when nothing is tracked. */
2318
2370
  private availableMargin;
2319
2371
  /** Clears both sides of the live balance join and the public account cache without emitting. */
@@ -2778,4 +2830,4 @@ declare class ExchangesSDK {
2778
2830
  private fetchCredentials;
2779
2831
  }
2780
2832
 
2781
- export { ACCOUNT_MODE_LABELS, ASSET_DECIMALS, AccountBalance, AccountBalanceCore, type AccountHealthStatus, AccountMode, type AccountPosition, type AccountSnapshot, type AccountStatus, type AccountSyncer, type AccountSyncerConfig, type AccountTierCallback, type AccountTracker, type AccountTrackerConfig, type AccountWarningReason, type ApiKey, type ApproveIntegratorParams, type ApproveIntegratorResult, AssetId, type AssetSnapshotMap, type BalanceCallback, BaseAccountSyncer, BaseAccountTracker, BaseWs, BinanceAccountSyncer, BinanceAccountTracker, type BinanceAccountTrackerConfig, BinanceExchangeWs, BinanceMultiAssetBalance, BinanceSingleAssetBalance, BinanceUserDataWs, type BinanceUserDataWsConfig, BinanceWsApi, type BinanceWsApiConfig, BybitAccountSyncer, BybitAccountTracker, type BybitAccountTrackerConfig, BybitExchangeWs, BybitIsolatedMarginBalance, BybitPortfolioMarginBalance, BybitRegularMarginBalance, BybitWs, type BybitWsConfig, type CctpDepositAddressParams, type CctpDepositAddressResult, type CctpDepositHistorySettlement, type CctpDepositParams, type CctpDepositRegistrationSettlement, type CctpDepositResult, type CctpDepositSettlement, type ChangeAccountTierParams, type ChangeAccountTierResult, type CheckApiWalletParams, CollateralBalance, type ConnectOptions, type ConnectWalletParams, type ConnectWalletResult, type CreateAccountTrackerConfig, type CreateExchangeWsConfig, DecimalString, type EthereumDepositParams, type EthereumDepositResult, type ExchangeConnection, type ExchangeHelperExchange, type ExchangeHelperMap, type ExchangeState, ExchangeWs, type ExchangeWsByConnector, ExchangesSDK, type ExchangesSDKConfig, FUNXYZ_DEPOSIT_MINIMUMS_USD, FUNXYZ_ETHEREUM_MAINNET_CHAIN_ID, FUNXYZ_EVM_CHAIN_NAMES, type FastWithdrawParams, type FastWithdrawResult, type FillSyncOutcome, type FundingSyncOutcome, type FunxyzAccountState, type FunxyzAsset, type FunxyzDepositAddressParams, type FunxyzDepositAddressResult, type FunxyzDepositCredit, type FunxyzDepositParams, type FunxyzDepositResult, type FunxyzDepositSettlement, type FunxyzDepositSource, type FunxyzMarket, HyperliquidAccountSyncer, HyperliquidAccountTracker, type HyperliquidAccountTrackerConfig, HyperliquidDexState, HyperliquidExchangeWs, type HyperliquidHelper, HyperliquidPortfolioMarginBalance, HyperliquidPortfolioMarginCollateral, HyperliquidStandardBalance, HyperliquidUnifiedBalance, HyperliquidWs, type IntegratorApprovalRequirements, L2_ASSET_DECIMALS, LIGHTER_NATIVE_USDC_ADDRESSES, LIGHTER_QUOTE_ASSETS, LIGHTER_RHC_CHAIN_ID, LIGHTER_RHC_USDG_ADDRESS, LIGHTER_RHC_USDG_DECIMALS, LIGHTER_USDC_DECIMALS, LighterAccountSyncer, type LighterAccountSyncerConfig, type LighterAccountTier, LighterAccountTracker, type LighterAccountTrackerConfig, type LighterApplyReferralCodeParams, type LighterAuthCredentials, LighterClassicBalance, type LighterConnector, type LighterDeposit, type LighterDepositAddressParams, type LighterDepositAddressResult, type LighterDepositCredit, type LighterDepositHistoryParams, type LighterDepositNetwork, type LighterDepositPage, type LighterDepositParams, type LighterDepositProgress, type LighterDepositResult, type LighterDepositStatus, type LighterDepositWalletAdapter, LighterExchangeWs, type LighterHelper, type LighterLookupResult, type LighterOnboardingStatus, type LighterReferral, type LighterReferralTotals, type LighterReferralTradeStats, LighterRhcAssetId, type LighterRhcDepositAddressParams, type LighterRhcDepositAddressResult, type LighterRhcDepositParams, type LighterRhcDepositResult, type LighterRhcDepositWallet, type LighterRhcHelper, type LighterSharedHelper, LighterUnifiedBalance, type LighterUserReferrals, type LighterUserReferralsParams, type LighterWithdraw, type LighterWithdrawHistoryParams, type LighterWithdrawPage, type LighterWithdrawStatus, type LighterWithdrawType, LighterWs, type ListExternalFillsParams, type ListExternalFundingParams, type ListExternalPositionsParams, MAX_API_KEY_INDEX, MIN_LIGHTER_CCTP_DEPOSIT_USDC, MIN_LIGHTER_FAST_WITHDRAW_COLLATERAL, MIN_LIGHTER_RHC_DEPOSIT_USDG, MIN_USER_API_KEY_INDEX, MarginMode, type MessageListener, OkxAccountSyncer, OkxAccountTracker, type OkxAccountTrackerConfig, OkxExchangeWs, OkxFuturesBalance, OkxMultiCurrencyMarginBalance, OkxPortfolioMarginBalance, OkxSpotBalance, OkxWs, type OkxWsConfig, type OpenListener, type PositionsCallback, RouteType, Side, SignedDecimalString, type StateUpdate, type SupportedCredentials, type SyncerThrottle, type TrackedAssetCallback, type TrackedAssetInfo, type WalletClientWithAccount, type WithdrawParams, type WithdrawResult, type WsMessage, createAccountSyncer, createAccountTracker, createExchangeWs, createHyperliquidHelper, createLighterHelper, createLighterRhcHelper, findAvailableApiKeyIndex, getAssetDecimals, getLighterNativeUsdcAddress, listFunxyzTransferableChains, readFunxyzChainSource, readFunxyzMinimumUsd, retrieveLighterAuthToken, usdcAbi };
2833
+ export { ACCOUNT_MODE_LABELS, ASSET_DECIMALS, AccountBalance, AccountBalanceCore, type AccountHealthStatus, AccountMode, type AccountPosition, type AccountSnapshot, type AccountStatus, type AccountSyncer, type AccountSyncerConfig, type AccountTierCallback, type AccountTracker, type AccountTrackerConfig, type AccountWarningReason, type ApiKey, type ApproveIntegratorParams, type ApproveIntegratorResult, AssetId, type AssetSnapshotMap, type BalanceCallback, BaseAccountSyncer, BaseAccountTracker, BaseWs, BinanceAccountSyncer, BinanceAccountTracker, type BinanceAccountTrackerConfig, BinanceExchangeWs, BinanceMultiAssetBalance, BinanceSingleAssetBalance, BinanceUserDataWs, type BinanceUserDataWsConfig, BinanceWsApi, type BinanceWsApiConfig, BybitAccountSyncer, BybitAccountTracker, type BybitAccountTrackerConfig, BybitExchangeWs, BybitIsolatedMarginBalance, BybitPortfolioMarginBalance, BybitRegularMarginBalance, BybitWs, type BybitWsConfig, type CctpDepositAddressParams, type CctpDepositAddressResult, type CctpDepositHistorySettlement, type CctpDepositParams, type CctpDepositRegistrationSettlement, type CctpDepositResult, type CctpDepositSettlement, type ChangeAccountTierParams, type ChangeAccountTierResult, type CheckApiWalletParams, CollateralBalance, type ConnectOptions, type ConnectWalletParams, type ConnectWalletResult, type CreateAccountTrackerConfig, type CreateExchangeWsConfig, DecimalString, type EthereumDepositParams, type EthereumDepositResult, type ExchangeConnection, type ExchangeHelperExchange, type ExchangeHelperMap, type ExchangeState, ExchangeWs, type ExchangeWsByConnector, ExchangesSDK, type ExchangesSDKConfig, FUNXYZ_DEPOSIT_MINIMUMS_USD, FUNXYZ_ETHEREUM_MAINNET_CHAIN_ID, FUNXYZ_EVM_CHAIN_NAMES, type FastWithdrawParams, type FastWithdrawResult, type FillSyncOutcome, type FundingSyncOutcome, type FunxyzAccountState, type FunxyzAsset, type FunxyzDepositAddressParams, type FunxyzDepositAddressResult, type FunxyzDepositCredit, type FunxyzDepositParams, type FunxyzDepositResult, type FunxyzDepositSettlement, type FunxyzDepositSource, type FunxyzMarket, HyperliquidAccountSyncer, HyperliquidAccountTracker, type HyperliquidAccountTrackerConfig, HyperliquidBorrowLendReserve, HyperliquidDexState, HyperliquidExchangeWs, type HyperliquidHelper, HyperliquidPortfolioMarginBalance, HyperliquidPortfolioMarginCollateral, HyperliquidStandardBalance, HyperliquidUnifiedBalance, HyperliquidWs, type IntegratorApprovalRequirements, L2_ASSET_DECIMALS, LIGHTER_NATIVE_USDC_ADDRESSES, LIGHTER_QUOTE_ASSETS, LIGHTER_RHC_CHAIN_ID, LIGHTER_RHC_USDG_ADDRESS, LIGHTER_RHC_USDG_DECIMALS, LIGHTER_USDC_DECIMALS, LighterAccountSyncer, type LighterAccountSyncerConfig, type LighterAccountTier, LighterAccountTracker, type LighterAccountTrackerConfig, type LighterApplyReferralCodeParams, type LighterAuthCredentials, LighterClassicBalance, type LighterConnector, type LighterDeposit, type LighterDepositAddressParams, type LighterDepositAddressResult, type LighterDepositCredit, type LighterDepositHistoryParams, type LighterDepositNetwork, type LighterDepositPage, type LighterDepositParams, type LighterDepositProgress, type LighterDepositResult, type LighterDepositStatus, type LighterDepositWalletAdapter, LighterExchangeWs, type LighterHelper, type LighterLookupResult, type LighterOnboardingStatus, type LighterReferral, type LighterReferralTotals, type LighterReferralTradeStats, LighterRhcAssetId, type LighterRhcDepositAddressParams, type LighterRhcDepositAddressResult, type LighterRhcDepositParams, type LighterRhcDepositResult, type LighterRhcDepositWallet, type LighterRhcHelper, type LighterSharedHelper, LighterUnifiedBalance, type LighterUserReferrals, type LighterUserReferralsParams, type LighterWithdraw, type LighterWithdrawHistoryParams, type LighterWithdrawPage, type LighterWithdrawStatus, type LighterWithdrawType, LighterWs, type ListExternalFillsParams, type ListExternalFundingParams, type ListExternalPositionsParams, MAX_API_KEY_INDEX, MIN_LIGHTER_CCTP_DEPOSIT_USDC, MIN_LIGHTER_FAST_WITHDRAW_COLLATERAL, MIN_LIGHTER_RHC_DEPOSIT_USDG, MIN_USER_API_KEY_INDEX, MarginMode, type MessageListener, OkxAccountSyncer, OkxAccountTracker, type OkxAccountTrackerConfig, OkxExchangeWs, OkxFuturesBalance, OkxMultiCurrencyMarginBalance, OkxPortfolioMarginBalance, OkxSpotBalance, OkxWs, type OkxWsConfig, type OpenListener, type PositionsCallback, RouteType, Side, SignedDecimalString, type StateUpdate, type SupportedCredentials, type SyncerThrottle, type TrackedAssetCallback, type TrackedAssetInfo, type WalletClientWithAccount, type WithdrawParams, type WithdrawResult, type WsMessage, createAccountSyncer, createAccountTracker, createExchangeWs, createHyperliquidHelper, createLighterHelper, createLighterRhcHelper, findAvailableApiKeyIndex, getAssetDecimals, getLighterNativeUsdcAddress, listFunxyzTransferableChains, readFunxyzChainSource, readFunxyzMinimumUsd, retrieveLighterAuthToken, usdcAbi };
@@ -146,7 +146,22 @@ const HyperliquidPortfolioMarginCollateral = z.object({
146
146
  * `borrowed / borrow cap` for this coin, and `null` where the venue renders `--` — no borrow.
147
147
  * NOT clipped at one: `0x0871deb3…` reports 1.0081 on USDT0.
148
148
  */
149
- borrowCapUsed: DecimalString.nullable()
149
+ borrowCapUsed: DecimalString.nullable(),
150
+ /**
151
+ * The coin's outstanding borrow in its own units, and `0` on a coin never borrowed.
152
+ *
153
+ * Read from the wire's `borrowed`, never from a negative `equity`: the USDC row already contains
154
+ * the perps account value, so `equity` is `supplied − borrowed + accountValue` and its negative
155
+ * understates the debt by exactly that wallet.
156
+ */
157
+ borrowed: DecimalString
158
+ });
159
+ const HyperliquidBorrowLendReserve = z.object({
160
+ asset: z.string(),
161
+ /** What borrowing the coin costs per year, as a fraction — `0.05` is 5% APY. */
162
+ borrowYearlyRate: DecimalString,
163
+ /** What supplying the coin earns per year; Portfolio Margin supplies idle balances automatically. */
164
+ supplyYearlyRate: DecimalString
150
165
  });
151
166
  const HyperliquidPortfolioMarginBalance = AccountBalanceCore.extend({
152
167
  accountMode: z.literal("hyperliquid:portfolioMargin"),
@@ -194,7 +209,27 @@ const HyperliquidPortfolioMarginBalance = AccountBalanceCore.extend({
194
209
  * account borrows nothing. Not clipped at one, and never the USDC entry alone.
195
210
  */
196
211
  borrowCapUsed: DecimalString.nullable(),
212
+ /**
213
+ * The venue's Your Borrowed: every row's `borrowed` valued at its own mid, in USD, and `0` when
214
+ * the account borrows nothing. Null when a borrowed coin cannot be priced, since a debt counted
215
+ * as zero would hide exactly the part nobody could value.
216
+ */
217
+ borrowedValue: DecimalString.nullable(),
218
+ /**
219
+ * The venue's Health Factor — LTV-weighted collateral over the borrowed value — at four decimals.
220
+ * Below `1` the account can borrow no more, which is not by itself liquidation: that is
221
+ * `portfolioMarginRatio`. Null when the account borrows nothing, and until the first read lands.
222
+ *
223
+ * Read, never derived, and over REST because no channel carries it: the live tracker refreshes it
224
+ * on a timer, so it can trail the channel-fed figures beside it by up to one poll interval.
225
+ */
226
+ healthFactor: DecimalString.nullable(),
227
+ /**
228
+ * Every reserve's yearly rates, listed whatever the account holds, so what a borrow will cost
229
+ * shows before the first one happens. Empty until the first read lands.
230
+ */
231
+ borrowLendReserves: z.array(HyperliquidBorrowLendReserve),
197
232
  perDexState: z.array(HyperliquidDexState)
198
233
  });
199
234
 
200
- export { HyperliquidDexState, HyperliquidPortfolioMarginBalance, HyperliquidPortfolioMarginCollateral, HyperliquidStandardBalance, HyperliquidUnifiedBalance };
235
+ export { HyperliquidBorrowLendReserve, HyperliquidDexState, HyperliquidPortfolioMarginBalance, HyperliquidPortfolioMarginCollateral, HyperliquidStandardBalance, HyperliquidUnifiedBalance };
@@ -0,0 +1,32 @@
1
+ import { fetchBorrowLendUserState, fetchAllBorrowLendReserveStates } from '../../client/hyperliquid/rest';
2
+
3
+ var __defProp = Object.defineProperty;
4
+ var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
5
+ const EMPTY_BORROW_LEND_STATE = {
6
+ healthFactor: null,
7
+ ratesByToken: /* @__PURE__ */ new Map()
8
+ };
9
+ async function fetchBorrowLendState(user, demo) {
10
+ const [userState, reserves] = await Promise.all([
11
+ fetchBorrowLendUserState(user, demo),
12
+ fetchAllBorrowLendReserveStates(demo)
13
+ ]);
14
+ return {
15
+ healthFactor: userState.healthFactor ?? null,
16
+ ratesByToken: ratesByToken(reserves)
17
+ };
18
+ }
19
+ __name(fetchBorrowLendState, "fetchBorrowLendState");
20
+ function ratesByToken(reserves) {
21
+ const rates = /* @__PURE__ */ new Map();
22
+ for (const [token, reserve] of reserves) {
23
+ rates.set(token, {
24
+ borrowYearlyRate: reserve.borrowYearlyRate,
25
+ supplyYearlyRate: reserve.supplyYearlyRate
26
+ });
27
+ }
28
+ return rates;
29
+ }
30
+ __name(ratesByToken, "ratesByToken");
31
+
32
+ export { EMPTY_BORROW_LEND_STATE, fetchBorrowLendState };
@@ -232,6 +232,9 @@ function mapPortfolioMarginBalance(context) {
232
232
  portfolioMarginRatio: wireMarginRatio(context),
233
233
  portfolioAccountLeverage: portfolioAccountLeverage(context),
234
234
  borrowCapUsed: worstBorrowCapUsed(context),
235
+ borrowedValue: borrowedValueOf(context),
236
+ healthFactor: wireHealthFactor(context),
237
+ borrowLendReserves: borrowLendReserveRows(context),
235
238
  perDexState: context.summary.perDexState,
236
239
  timestamp: context.timestamp
237
240
  };
@@ -256,7 +259,8 @@ function portfolioMarginCollateralRows(context) {
256
259
  availableMargin: context.availableMarginByToken.get(balance.token) ?? null,
257
260
  withdrawable: signed(total.minus(toDecimal(balance.hold)).toFixed()),
258
261
  ltv: balance.ltv === null ? null : toDecimal(balance.ltv).toFixed(),
259
- borrowCapUsed: borrowCapUsedFor(context, balance.token)
262
+ borrowCapUsed: borrowCapUsedFor(context, balance.token),
263
+ borrowed: toDecimal(balance.borrowed).toFixed()
260
264
  });
261
265
  }
262
266
  rows.sort(byRowAsset);
@@ -283,6 +287,22 @@ function worstBorrowCapUsed(context) {
283
287
  return worst.isZero() ? null : worst.toFixed(RATIO_DECIMALS);
284
288
  }
285
289
  __name(worstBorrowCapUsed, "worstBorrowCapUsed");
290
+ function borrowedValueOf(context) {
291
+ let borrowed = ZERO;
292
+ for (const balance of context.spotBalances.values()) {
293
+ const debt = toDecimal(balance.borrowed);
294
+ if (debt.isZero()) {
295
+ continue;
296
+ }
297
+ const mid = context.prices.midForToken(balance.token);
298
+ if (mid === null) {
299
+ return null;
300
+ }
301
+ borrowed = borrowed.plus(debt.times(toDecimal(mid)));
302
+ }
303
+ return borrowed.toFixed();
304
+ }
305
+ __name(borrowedValueOf, "borrowedValueOf");
286
306
  function wireMarginRatio(context) {
287
307
  if (context.portfolioMarginRatio === null) {
288
308
  return null;
@@ -290,6 +310,38 @@ function wireMarginRatio(context) {
290
310
  return toDecimal(context.portfolioMarginRatio).toFixed(RATIO_DECIMALS);
291
311
  }
292
312
  __name(wireMarginRatio, "wireMarginRatio");
313
+ function wireHealthFactor(context) {
314
+ const healthFactor = context.borrowLend.healthFactor;
315
+ if (healthFactor === null) {
316
+ return null;
317
+ }
318
+ return toDecimal(healthFactor).toFixed(RATIO_DECIMALS);
319
+ }
320
+ __name(wireHealthFactor, "wireHealthFactor");
321
+ function borrowLendReserveRows(context) {
322
+ const rows = [];
323
+ for (const [token, rates] of context.borrowLend.ratesByToken) {
324
+ const asset = context.collateralTokenNames.get(token);
325
+ if (asset === void 0) {
326
+ continue;
327
+ }
328
+ rows.push({
329
+ asset,
330
+ borrowYearlyRate: toDecimal(rates.borrowYearlyRate).toFixed(),
331
+ supplyYearlyRate: toDecimal(rates.supplyYearlyRate).toFixed()
332
+ });
333
+ }
334
+ rows.sort(byReserveAsset);
335
+ return rows;
336
+ }
337
+ __name(borrowLendReserveRows, "borrowLendReserveRows");
338
+ function byReserveAsset(left, right) {
339
+ if (left.asset === right.asset) {
340
+ return 0;
341
+ }
342
+ return left.asset < right.asset ? -1 : 1;
343
+ }
344
+ __name(byReserveAsset, "byReserveAsset");
293
345
  function portfolioAccountLeverage(context) {
294
346
  const collateral = haircutCollateral(context);
295
347
  if (collateral === null) {
@@ -2,6 +2,7 @@ import { createExchangeWs } from '../../client';
2
2
  import { fetchPerpDexs, fetchInfo, fetchAllMids, fetchUserAbstraction, fetchSpotClearinghouseState, fetchDelegatorSummary, fetchUserVaultEquities, fetchClearinghouseState, fetchActiveAssetData } from '../../client/hyperliquid/rest';
3
3
  import { BaseAccountTracker } from '../base';
4
4
  import { resolveHyperliquidAccountType } from './account-mode';
5
+ import { EMPTY_BORROW_LEND_STATE, fetchBorrowLendState } from './borrow-lend';
5
6
  import { buildAvailableMarginByToken, buildCollateralTokenNames, minimumAvailableToTrade } from './collateral';
6
7
  import { LOCKED_TRACKED_COINS } from './const';
7
8
  import { toDecimal, ZERO } from './decimal';
@@ -15,6 +16,7 @@ const EMPTY_SPOT_META = {
15
16
  tokens: [],
16
17
  universe: []
17
18
  };
19
+ const BORROW_LEND_POLL_INTERVAL_MS = 3e4;
18
20
  class HyperliquidAccountTracker extends BaseAccountTracker {
19
21
  static {
20
22
  __name(this, "HyperliquidAccountTracker");
@@ -30,6 +32,11 @@ class HyperliquidAccountTracker extends BaseAccountTracker {
30
32
  /** Portfolio Margin's own risk scalars, which ride the same `spotState` frame the balances do. */
31
33
  portfolioMarginRatio = null;
32
34
  borrowRatioByToken = /* @__PURE__ */ new Map();
35
+ /** Portfolio Margin's REST-only figures, and the timer that keeps them fresh while in that mode. */
36
+ borrowLend = EMPTY_BORROW_LEND_STATE;
37
+ borrowLendPollTimer = null;
38
+ /** Advances on every stop, so a read fired in an earlier poll period is recognised and dropped. */
39
+ borrowLendPollGeneration = 0;
33
40
  hasSpotState = false;
34
41
  hasClearinghouseState = false;
35
42
  accountType = null;
@@ -74,8 +81,10 @@ class HyperliquidAccountTracker extends BaseAccountTracker {
74
81
  })));
75
82
  const assetData = await this.fetchActiveAssetEvents(this.coinsForAvailableMargin());
76
83
  const { summary, assetPositions } = aggregateClearinghouseStates(clearinghouseStates);
84
+ const accountType = resolveHyperliquidAccountType(abstraction);
85
+ const borrowLend = accountType === HyperliquidAccountType.PORTFOLIO_MARGIN ? await fetchBorrowLendState(user, this.demo) : EMPTY_BORROW_LEND_STATE;
77
86
  const balance = mapBalance({
78
- accountType: resolveHyperliquidAccountType(abstraction),
87
+ accountType,
79
88
  summary,
80
89
  spotBalances: mapSpotBalances(spotState),
81
90
  prices: SpotPrices.from(spotMeta, mids),
@@ -86,6 +95,7 @@ class HyperliquidAccountTracker extends BaseAccountTracker {
86
95
  availableMarginByToken: buildAvailableMarginByToken(summary.perDexState, assetData.values()),
87
96
  portfolioMarginRatio: spotState.portfolioMarginRatio ?? null,
88
97
  borrowRatioByToken: mapBorrowRatios(spotState),
98
+ borrowLend,
89
99
  timestamp: Date.now()
90
100
  });
91
101
  const positions = Array.from(mapPositions(assetPositions).values());
@@ -116,6 +126,7 @@ class HyperliquidAccountTracker extends BaseAccountTracker {
116
126
  const lifecycleVersion = ++this.lifecycleVersion;
117
127
  const user = this.addrLower();
118
128
  this.destroyCurrentSocket();
129
+ this.stopBorrowLendPoll();
119
130
  this.subscribedCoins.clear();
120
131
  this.resetLiveAccountState();
121
132
  this.dexToMeta.clear();
@@ -148,6 +159,7 @@ class HyperliquidAccountTracker extends BaseAccountTracker {
148
159
  this.messageListenerId = this.ws.addMessageListener((raw) => this.routeRaw(raw));
149
160
  this.openListenerId = this.ws.addOpenListener(() => this.resubscribeAll());
150
161
  this.sendStaticSubs();
162
+ this.syncBorrowLendPoll();
151
163
  const coinsToTrack = /* @__PURE__ */ new Set([
152
164
  ...LOCKED_TRACKED_COINS,
153
165
  ...this.trackedCoins
@@ -159,6 +171,7 @@ class HyperliquidAccountTracker extends BaseAccountTracker {
159
171
  async cleanupConnections() {
160
172
  this.lifecycleVersion += 1;
161
173
  this.destroyCurrentSocket();
174
+ this.stopBorrowLendPoll();
162
175
  this.subscribedCoins.clear();
163
176
  }
164
177
  /** Detaches and destroys the active socket without changing the current lifecycle generation. */
@@ -422,6 +435,7 @@ class HyperliquidAccountTracker extends BaseAccountTracker {
422
435
  this.resetLiveAccountState();
423
436
  }
424
437
  this.accountType = nextAccountType;
438
+ this.syncBorrowLendPoll();
425
439
  this.vaultEquity = sumLiveVaultEquity(data.perpDexStates ?? []);
426
440
  this.hasWebData3 = true;
427
441
  if (!nextAccountType) {
@@ -475,9 +489,68 @@ class HyperliquidAccountTracker extends BaseAccountTracker {
475
489
  availableMarginByToken: buildAvailableMarginByToken(this.lastSummary.perDexState, this.trackedAssetData.values()),
476
490
  portfolioMarginRatio: this.portfolioMarginRatio,
477
491
  borrowRatioByToken: this.borrowRatioByToken,
492
+ borrowLend: this.borrowLend,
478
493
  timestamp: Date.now()
479
494
  });
480
495
  }
496
+ /**
497
+ * Runs the borrow/lend poll exactly while the account is in Portfolio Margin.
498
+ *
499
+ * Safe to call on every mode read: a poll already in the state the mode asks for is left alone,
500
+ * so a steady stream of `webData3` frames neither restarts the timer nor refires a read.
501
+ */
502
+ syncBorrowLendPoll() {
503
+ const wantsPoll = this.accountType === HyperliquidAccountType.PORTFOLIO_MARGIN;
504
+ if (wantsPoll === (this.borrowLendPollTimer !== null)) {
505
+ return;
506
+ }
507
+ if (!wantsPoll) {
508
+ this.stopBorrowLendPoll();
509
+ return;
510
+ }
511
+ void this.refreshBorrowLendState();
512
+ this.borrowLendPollTimer = setInterval(() => void this.refreshBorrowLendState(), BORROW_LEND_POLL_INTERVAL_MS);
513
+ }
514
+ /**
515
+ * Stops the borrow/lend poll and drops its figures, so no later mode or session inherits them.
516
+ *
517
+ * Every stop opens a new generation — a session ending, a restart, or the account leaving
518
+ * Portfolio Margin — so a read still in flight from the period just ended can never land.
519
+ */
520
+ stopBorrowLendPoll() {
521
+ if (this.borrowLendPollTimer !== null) {
522
+ clearInterval(this.borrowLendPollTimer);
523
+ this.borrowLendPollTimer = null;
524
+ }
525
+ this.borrowLendPollGeneration += 1;
526
+ this.borrowLend = EMPTY_BORROW_LEND_STATE;
527
+ }
528
+ /**
529
+ * Reads the health factor and reserve rates once, then republishes the balance with them.
530
+ *
531
+ * A failed read is reported and left for the next tick: the previous figures stay in place, since
532
+ * one missed read says nothing about the account that the last good one did not.
533
+ */
534
+ async refreshBorrowLendState() {
535
+ const generation = this.borrowLendPollGeneration;
536
+ let borrowLend;
537
+ try {
538
+ borrowLend = await fetchBorrowLendState(this.addrLower(), this.demo);
539
+ } catch (err) {
540
+ this.onError(err instanceof Error ? err : new Error(String(err)));
541
+ return;
542
+ }
543
+ if (generation !== this.borrowLendPollGeneration) {
544
+ return;
545
+ }
546
+ this.borrowLend = borrowLend;
547
+ const balance = this.buildBalance();
548
+ if (balance !== void 0) {
549
+ this.onStateUpdate({
550
+ balance
551
+ });
552
+ }
553
+ }
481
554
  /** The floor of `availableToTrade` over the tracked set; `null` when nothing is tracked. */
482
555
  availableMargin() {
483
556
  return minimumAvailableToTrade(this.trackedAssetData.values());
@@ -540,7 +613,9 @@ function mapSpotBalances(spotState) {
540
613
  hold: balance.hold,
541
614
  // An absent `ltv` means "not collateral", which weighs zero — never a zero weight, which is
542
615
  // a stablecoin at full value. The two states stay apart from the wire onward.
543
- ltv: balance.ltv ?? null
616
+ ltv: balance.ltv ?? null,
617
+ // An absent `borrowed` means the coin was never borrowed; the key rides only a borrowed coin.
618
+ borrowed: balance.borrowed ?? null
544
619
  });
545
620
  }
546
621
  return balances;
@@ -13,7 +13,7 @@ export { ACCOUNT_MODE_LABELS, AccountBalanceCore, AccountMode, CollateralBalance
13
13
  export { BaseAccountTracker } from './base';
14
14
  export { BinanceMultiAssetBalance, BinanceSingleAssetBalance } from './binance/balance';
15
15
  export { BybitIsolatedMarginBalance, BybitPortfolioMarginBalance, BybitRegularMarginBalance } from './bybit/balance';
16
- export { HyperliquidDexState, HyperliquidPortfolioMarginBalance, HyperliquidPortfolioMarginCollateral, HyperliquidStandardBalance, HyperliquidUnifiedBalance } from './hyperliquid/balance';
16
+ export { HyperliquidBorrowLendReserve, HyperliquidDexState, HyperliquidPortfolioMarginBalance, HyperliquidPortfolioMarginCollateral, HyperliquidStandardBalance, HyperliquidUnifiedBalance } from './hyperliquid/balance';
17
17
  export { LighterClassicBalance, LighterUnifiedBalance } from './lighter/balance';
18
18
  export { LIGHTER_QUOTE_ASSETS } from './lighter/const';
19
19
  export { OkxFuturesBalance, OkxMultiCurrencyMarginBalance, OkxPortfolioMarginBalance, OkxSpotBalance } from './okx/balance';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pear-protocol/exchanges-sdk",
3
- "version": "0.15.0",
3
+ "version": "0.16.0",
4
4
  "description": "Pear Protocol Exchanges SDK",
5
5
  "private": false,
6
6
  "type": "module",