@lifi/perps-sdk-provider-lighter 35.0.0 → 36.0.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 (248) hide show
  1. package/dist/cjs/LighterProvider.d.ts.map +1 -1
  2. package/dist/cjs/LighterProvider.js +101 -58
  3. package/dist/cjs/LighterProvider.js.map +1 -1
  4. package/dist/cjs/accountSummary.d.ts +1 -2
  5. package/dist/cjs/accountSummary.d.ts.map +1 -1
  6. package/dist/cjs/accountSummary.js +9 -16
  7. package/dist/cjs/accountSummary.js.map +1 -1
  8. package/dist/cjs/availableToTrade.d.ts +1 -2
  9. package/dist/cjs/availableToTrade.d.ts.map +1 -1
  10. package/dist/cjs/availableToTrade.js +8 -12
  11. package/dist/cjs/availableToTrade.js.map +1 -1
  12. package/dist/cjs/constants.d.ts +1 -0
  13. package/dist/cjs/constants.d.ts.map +1 -1
  14. package/dist/cjs/constants.js +2 -1
  15. package/dist/cjs/constants.js.map +1 -1
  16. package/dist/cjs/index.d.ts +1 -1
  17. package/dist/cjs/index.d.ts.map +1 -1
  18. package/dist/cjs/index.js +2 -1
  19. package/dist/cjs/index.js.map +1 -1
  20. package/dist/cjs/signers/LighterSigner.d.ts.map +1 -1
  21. package/dist/cjs/signers/LighterSigner.js +2 -6
  22. package/dist/cjs/signers/LighterSigner.js.map +1 -1
  23. package/dist/cjs/types/submit.d.ts +5 -0
  24. package/dist/cjs/types/submit.d.ts.map +1 -1
  25. package/dist/cjs/utils/index.d.ts +3 -3
  26. package/dist/cjs/utils/index.d.ts.map +1 -1
  27. package/dist/cjs/utils/index.js +5 -6
  28. package/dist/cjs/utils/index.js.map +1 -1
  29. package/dist/cjs/utils/mapFill.d.ts +1 -1
  30. package/dist/cjs/utils/mapFill.d.ts.map +1 -1
  31. package/dist/cjs/utils/mapFill.js +44 -31
  32. package/dist/cjs/utils/mapFill.js.map +1 -1
  33. package/dist/cjs/utils/mapOpenPositions.d.ts +3 -1
  34. package/dist/cjs/utils/mapOpenPositions.d.ts.map +1 -1
  35. package/dist/cjs/utils/mapOpenPositions.js +20 -4
  36. package/dist/cjs/utils/mapOpenPositions.js.map +1 -1
  37. package/dist/cjs/utils/mapOrder.d.ts +1 -1
  38. package/dist/cjs/utils/mapOrder.d.ts.map +1 -1
  39. package/dist/cjs/utils/mapOrder.js +64 -26
  40. package/dist/cjs/utils/mapOrder.js.map +1 -1
  41. package/dist/cjs/utils/mapPortfolioHistory.d.ts +1 -2
  42. package/dist/cjs/utils/mapPortfolioHistory.d.ts.map +1 -1
  43. package/dist/cjs/utils/mapPortfolioHistory.js +29 -21
  44. package/dist/cjs/utils/mapPortfolioHistory.js.map +1 -1
  45. package/dist/cjs/utils/mapPosition.d.ts +3 -3
  46. package/dist/cjs/utils/mapPosition.d.ts.map +1 -1
  47. package/dist/cjs/utils/mapPosition.js +35 -35
  48. package/dist/cjs/utils/mapPosition.js.map +1 -1
  49. package/dist/cjs/utils/rowTimestamp.d.ts +2 -0
  50. package/dist/cjs/utils/rowTimestamp.d.ts.map +1 -0
  51. package/dist/cjs/utils/rowTimestamp.js +14 -0
  52. package/dist/cjs/utils/rowTimestamp.js.map +1 -0
  53. package/dist/cjs/utils/settlementPool.d.ts +2 -3
  54. package/dist/cjs/utils/settlementPool.d.ts.map +1 -1
  55. package/dist/cjs/utils/settlementPool.js +8 -7
  56. package/dist/cjs/utils/settlementPool.js.map +1 -1
  57. package/dist/cjs/utils/spotPrice.d.ts +3 -4
  58. package/dist/cjs/utils/spotPrice.d.ts.map +1 -1
  59. package/dist/cjs/utils/spotPrice.js +6 -11
  60. package/dist/cjs/utils/spotPrice.js.map +1 -1
  61. package/dist/cjs/utils/time.d.ts.map +1 -1
  62. package/dist/cjs/utils/time.js +3 -2
  63. package/dist/cjs/utils/time.js.map +1 -1
  64. package/dist/cjs/utils/transferMargin.d.ts.map +1 -1
  65. package/dist/cjs/utils/transferMargin.js +7 -15
  66. package/dist/cjs/utils/transferMargin.js.map +1 -1
  67. package/dist/cjs/utils/withdrawableBalances.d.ts +1 -1
  68. package/dist/cjs/utils/withdrawableBalances.d.ts.map +1 -1
  69. package/dist/cjs/utils/withdrawableBalances.js +12 -17
  70. package/dist/cjs/utils/withdrawableBalances.js.map +1 -1
  71. package/dist/cjs/utils/withdrawalTypes.d.ts +17 -0
  72. package/dist/cjs/utils/withdrawalTypes.d.ts.map +1 -0
  73. package/dist/cjs/utils/withdrawalTypes.js +58 -0
  74. package/dist/cjs/utils/withdrawalTypes.js.map +1 -0
  75. package/dist/cjs/websocket/LighterWsProvider.d.ts.map +1 -1
  76. package/dist/cjs/websocket/LighterWsProvider.js +46 -58
  77. package/dist/cjs/websocket/LighterWsProvider.js.map +1 -1
  78. package/dist/cjs/websocket/orderBookSide.d.ts +19 -0
  79. package/dist/cjs/websocket/orderBookSide.d.ts.map +1 -0
  80. package/dist/cjs/websocket/orderBookSide.js +71 -0
  81. package/dist/cjs/websocket/orderBookSide.js.map +1 -0
  82. package/dist/esm/LighterProvider.d.ts.map +1 -1
  83. package/dist/esm/LighterProvider.js +116 -62
  84. package/dist/esm/LighterProvider.js.map +1 -1
  85. package/dist/esm/accountSummary.d.ts +1 -2
  86. package/dist/esm/accountSummary.d.ts.map +1 -1
  87. package/dist/esm/accountSummary.js +10 -14
  88. package/dist/esm/accountSummary.js.map +1 -1
  89. package/dist/esm/availableToTrade.d.ts +1 -2
  90. package/dist/esm/availableToTrade.d.ts.map +1 -1
  91. package/dist/esm/availableToTrade.js +9 -10
  92. package/dist/esm/availableToTrade.js.map +1 -1
  93. package/dist/esm/constants.d.ts +8 -0
  94. package/dist/esm/constants.d.ts.map +1 -1
  95. package/dist/esm/constants.js +8 -0
  96. package/dist/esm/constants.js.map +1 -1
  97. package/dist/esm/index.d.ts +1 -1
  98. package/dist/esm/index.d.ts.map +1 -1
  99. package/dist/esm/index.js +1 -1
  100. package/dist/esm/index.js.map +1 -1
  101. package/dist/esm/signers/LighterSigner.d.ts.map +1 -1
  102. package/dist/esm/signers/LighterSigner.js +3 -4
  103. package/dist/esm/signers/LighterSigner.js.map +1 -1
  104. package/dist/esm/types/submit.d.ts +14 -0
  105. package/dist/esm/types/submit.d.ts.map +1 -1
  106. package/dist/esm/utils/index.d.ts +3 -3
  107. package/dist/esm/utils/index.d.ts.map +1 -1
  108. package/dist/esm/utils/index.js +2 -3
  109. package/dist/esm/utils/index.js.map +1 -1
  110. package/dist/esm/utils/mapFill.d.ts +1 -1
  111. package/dist/esm/utils/mapFill.d.ts.map +1 -1
  112. package/dist/esm/utils/mapFill.js +51 -35
  113. package/dist/esm/utils/mapFill.js.map +1 -1
  114. package/dist/esm/utils/mapOpenPositions.d.ts +17 -3
  115. package/dist/esm/utils/mapOpenPositions.d.ts.map +1 -1
  116. package/dist/esm/utils/mapOpenPositions.js +31 -5
  117. package/dist/esm/utils/mapOpenPositions.js.map +1 -1
  118. package/dist/esm/utils/mapOrder.d.ts +6 -2
  119. package/dist/esm/utils/mapOrder.d.ts.map +1 -1
  120. package/dist/esm/utils/mapOrder.js +70 -25
  121. package/dist/esm/utils/mapOrder.js.map +1 -1
  122. package/dist/esm/utils/mapPortfolioHistory.d.ts +6 -3
  123. package/dist/esm/utils/mapPortfolioHistory.d.ts.map +1 -1
  124. package/dist/esm/utils/mapPortfolioHistory.js +34 -19
  125. package/dist/esm/utils/mapPortfolioHistory.js.map +1 -1
  126. package/dist/esm/utils/mapPosition.d.ts +12 -8
  127. package/dist/esm/utils/mapPosition.d.ts.map +1 -1
  128. package/dist/esm/utils/mapPosition.js +45 -38
  129. package/dist/esm/utils/mapPosition.js.map +1 -1
  130. package/dist/esm/utils/rowTimestamp.d.ts +6 -0
  131. package/dist/esm/utils/rowTimestamp.d.ts.map +1 -0
  132. package/dist/esm/utils/rowTimestamp.js +14 -0
  133. package/dist/esm/utils/rowTimestamp.js.map +1 -0
  134. package/dist/esm/utils/settlementPool.d.ts +2 -3
  135. package/dist/esm/utils/settlementPool.d.ts.map +1 -1
  136. package/dist/esm/utils/settlementPool.js +8 -7
  137. package/dist/esm/utils/settlementPool.js.map +1 -1
  138. package/dist/esm/utils/spotPrice.d.ts +3 -4
  139. package/dist/esm/utils/spotPrice.d.ts.map +1 -1
  140. package/dist/esm/utils/spotPrice.js +6 -8
  141. package/dist/esm/utils/spotPrice.js.map +1 -1
  142. package/dist/esm/utils/time.d.ts +10 -2
  143. package/dist/esm/utils/time.d.ts.map +1 -1
  144. package/dist/esm/utils/time.js +13 -4
  145. package/dist/esm/utils/time.js.map +1 -1
  146. package/dist/esm/utils/transferMargin.d.ts.map +1 -1
  147. package/dist/esm/utils/transferMargin.js +8 -13
  148. package/dist/esm/utils/transferMargin.js.map +1 -1
  149. package/dist/esm/utils/withdrawableBalances.d.ts +5 -3
  150. package/dist/esm/utils/withdrawableBalances.d.ts.map +1 -1
  151. package/dist/esm/utils/withdrawableBalances.js +17 -17
  152. package/dist/esm/utils/withdrawableBalances.js.map +1 -1
  153. package/dist/esm/utils/withdrawalTypes.d.ts +45 -0
  154. package/dist/esm/utils/withdrawalTypes.d.ts.map +1 -0
  155. package/dist/esm/utils/withdrawalTypes.js +68 -0
  156. package/dist/esm/utils/withdrawalTypes.js.map +1 -0
  157. package/dist/esm/websocket/LighterWsProvider.d.ts.map +1 -1
  158. package/dist/esm/websocket/LighterWsProvider.js +48 -57
  159. package/dist/esm/websocket/LighterWsProvider.js.map +1 -1
  160. package/dist/esm/websocket/orderBookSide.d.ts +33 -0
  161. package/dist/esm/websocket/orderBookSide.d.ts.map +1 -0
  162. package/dist/esm/websocket/orderBookSide.js +76 -0
  163. package/dist/esm/websocket/orderBookSide.js.map +1 -0
  164. package/dist/types/LighterProvider.d.ts.map +1 -1
  165. package/dist/types/accountSummary.d.ts +1 -2
  166. package/dist/types/accountSummary.d.ts.map +1 -1
  167. package/dist/types/availableToTrade.d.ts +1 -2
  168. package/dist/types/availableToTrade.d.ts.map +1 -1
  169. package/dist/types/constants.d.ts +8 -0
  170. package/dist/types/constants.d.ts.map +1 -1
  171. package/dist/types/index.d.ts +1 -1
  172. package/dist/types/index.d.ts.map +1 -1
  173. package/dist/types/signers/LighterSigner.d.ts.map +1 -1
  174. package/dist/types/types/submit.d.ts +14 -0
  175. package/dist/types/types/submit.d.ts.map +1 -1
  176. package/dist/types/utils/index.d.ts +3 -3
  177. package/dist/types/utils/index.d.ts.map +1 -1
  178. package/dist/types/utils/mapFill.d.ts +1 -1
  179. package/dist/types/utils/mapFill.d.ts.map +1 -1
  180. package/dist/types/utils/mapOpenPositions.d.ts +17 -3
  181. package/dist/types/utils/mapOpenPositions.d.ts.map +1 -1
  182. package/dist/types/utils/mapOrder.d.ts +6 -2
  183. package/dist/types/utils/mapOrder.d.ts.map +1 -1
  184. package/dist/types/utils/mapPortfolioHistory.d.ts +6 -3
  185. package/dist/types/utils/mapPortfolioHistory.d.ts.map +1 -1
  186. package/dist/types/utils/mapPosition.d.ts +12 -8
  187. package/dist/types/utils/mapPosition.d.ts.map +1 -1
  188. package/dist/types/utils/rowTimestamp.d.ts +6 -0
  189. package/dist/types/utils/rowTimestamp.d.ts.map +1 -0
  190. package/dist/types/utils/settlementPool.d.ts +2 -3
  191. package/dist/types/utils/settlementPool.d.ts.map +1 -1
  192. package/dist/types/utils/spotPrice.d.ts +3 -4
  193. package/dist/types/utils/spotPrice.d.ts.map +1 -1
  194. package/dist/types/utils/time.d.ts +10 -2
  195. package/dist/types/utils/time.d.ts.map +1 -1
  196. package/dist/types/utils/transferMargin.d.ts.map +1 -1
  197. package/dist/types/utils/withdrawableBalances.d.ts +5 -3
  198. package/dist/types/utils/withdrawableBalances.d.ts.map +1 -1
  199. package/dist/types/utils/withdrawalTypes.d.ts +45 -0
  200. package/dist/types/utils/withdrawalTypes.d.ts.map +1 -0
  201. package/dist/types/websocket/LighterWsProvider.d.ts.map +1 -1
  202. package/dist/types/websocket/orderBookSide.d.ts +33 -0
  203. package/dist/types/websocket/orderBookSide.d.ts.map +1 -0
  204. package/package.json +3 -3
  205. package/src/LighterProvider.ts +236 -100
  206. package/src/accountSummary.ts +41 -22
  207. package/src/availableToTrade.ts +38 -15
  208. package/src/constants.ts +9 -0
  209. package/src/index.ts +1 -0
  210. package/src/signers/LighterSigner.ts +11 -4
  211. package/src/types/submit.ts +15 -0
  212. package/src/utils/index.ts +9 -3
  213. package/src/utils/mapFill.ts +82 -43
  214. package/src/utils/mapOpenPositions.ts +45 -6
  215. package/src/utils/mapOrder.ts +99 -28
  216. package/src/utils/mapPortfolioHistory.ts +78 -24
  217. package/src/utils/mapPosition.ts +67 -44
  218. package/src/utils/rowTimestamp.ts +15 -0
  219. package/src/utils/settlementPool.ts +36 -12
  220. package/src/utils/spotPrice.ts +18 -12
  221. package/src/utils/time.ts +14 -4
  222. package/src/utils/transferMargin.ts +28 -17
  223. package/src/utils/withdrawableBalances.ts +34 -20
  224. package/src/utils/withdrawalTypes.ts +125 -0
  225. package/src/websocket/LighterWsProvider.ts +75 -92
  226. package/src/websocket/orderBookSide.ts +106 -0
  227. package/dist/cjs/utils/decimal.d.ts +0 -5
  228. package/dist/cjs/utils/decimal.d.ts.map +0 -1
  229. package/dist/cjs/utils/decimal.js +0 -38
  230. package/dist/cjs/utils/decimal.js.map +0 -1
  231. package/dist/cjs/utils/liquidation.d.ts +0 -4
  232. package/dist/cjs/utils/liquidation.d.ts.map +0 -1
  233. package/dist/cjs/utils/liquidation.js +0 -16
  234. package/dist/cjs/utils/liquidation.js.map +0 -1
  235. package/dist/esm/utils/decimal.d.ts +0 -15
  236. package/dist/esm/utils/decimal.d.ts.map +0 -1
  237. package/dist/esm/utils/decimal.js +0 -39
  238. package/dist/esm/utils/decimal.js.map +0 -1
  239. package/dist/esm/utils/liquidation.d.ts +0 -22
  240. package/dist/esm/utils/liquidation.d.ts.map +0 -1
  241. package/dist/esm/utils/liquidation.js +0 -31
  242. package/dist/esm/utils/liquidation.js.map +0 -1
  243. package/dist/types/utils/decimal.d.ts +0 -15
  244. package/dist/types/utils/decimal.d.ts.map +0 -1
  245. package/dist/types/utils/liquidation.d.ts +0 -22
  246. package/dist/types/utils/liquidation.d.ts.map +0 -1
  247. package/src/utils/decimal.ts +0 -52
  248. package/src/utils/liquidation.ts +0 -39
@@ -1,31 +1,40 @@
1
1
  import {
2
+ addDecimalString,
3
+ isDecimalString,
4
+ isDecimalStringGreaterThan,
2
5
  PerpsError,
3
6
  positionSupportsMarginAdjustment,
4
7
  positionSupportsMarginRemoval,
8
+ roundDecimalString,
9
+ subtractDecimalString,
5
10
  } from '@lifi/perps-sdk'
6
11
  import {
7
12
  type DecimalString,
8
13
  PerpsErrorCode,
9
14
  type Position,
10
15
  } from '@lifi/perps-types'
11
- import Big from 'big.js'
12
16
 
13
17
  const AMOUNT_DECIMALS = 6
14
18
 
15
- function positionAmount(value: string, field: string): Big {
16
- try {
17
- return new Big(value)
18
- } catch {
19
+ function positionAmount(
20
+ value: DecimalString | undefined,
21
+ field: string
22
+ ): DecimalString {
23
+ if (!isDecimalString(value)) {
19
24
  throw new PerpsError(
20
25
  PerpsErrorCode.ValidationError,
21
- `Invalid decimal string on Position.${field}: '${value}'`
26
+ `Invalid \`Position.${field}\`: '${value}' is not a decimal string.`
22
27
  )
23
28
  }
29
+ return value
24
30
  }
25
31
 
26
- function positivePositionAmount(value: string, field: string): Big {
32
+ function positivePositionAmount(
33
+ value: DecimalString | undefined,
34
+ field: string
35
+ ): DecimalString {
27
36
  const amount = positionAmount(value, field)
28
- if (amount.lte(0)) {
37
+ if (!isDecimalStringGreaterThan(amount, '0')) {
29
38
  throw new PerpsError(
30
39
  PerpsErrorCode.ValidationError,
31
40
  `Position.${field} must be greater than zero.`
@@ -57,16 +66,18 @@ export function positionRemovableMargin(
57
66
  if (!positionSupportsMarginRemoval(position)) {
58
67
  return '0'
59
68
  }
60
- const removable = positivePositionAmount(position.marginUsed, 'marginUsed')
61
- .plus(positionAmount(position.unrealizedPnl, 'unrealizedPnl'))
62
- .minus(
63
- positivePositionAmount(
64
- position.initialMarginRequirement,
65
- 'initialMarginRequirement'
66
- )
69
+ const removable = subtractDecimalString(
70
+ addDecimalString(
71
+ positivePositionAmount(position.marginUsed, 'marginUsed'),
72
+ positionAmount(position.unrealizedPnl, 'unrealizedPnl')
73
+ ),
74
+ positivePositionAmount(
75
+ position.initialMarginRequirement,
76
+ 'initialMarginRequirement'
67
77
  )
68
- if (removable.lte(0)) {
78
+ )
79
+ if (!isDecimalStringGreaterThan(removable, '0')) {
69
80
  return '0'
70
81
  }
71
- return removable.round(AMOUNT_DECIMALS, Big.roundDown).toFixed()
82
+ return roundDecimalString(removable, AMOUNT_DECIMALS, 'truncate')
72
83
  }
@@ -1,15 +1,17 @@
1
1
  import {
2
2
  calculateTransferable,
3
3
  calculateWithdrawMax,
4
+ isDecimalStringGreaterThan,
4
5
  type ProviderWithdrawableBalance,
6
+ subtractDecimalString,
7
+ unknownToDecimalString,
5
8
  } from '@lifi/perps-sdk'
6
- import Big from 'big.js'
9
+ import { LIGHTER_PROVIDER_KEY, LIGHTER_SPOT_CATEGORY_ID } from '../constants.js'
7
10
  import type { LtAccount } from '../types/account.js'
8
- import { toRequiredBig } from './decimal.js'
9
11
 
10
12
  /**
11
- * Split each held asset into the two routes a Lighter withdrawal can name:
12
- * spot draws on `balance` net of `locked_balance`, perps draws on
13
+ * Split each held asset into the two categories a Lighter withdrawal can
14
+ * name: spot draws on `balance` net of `locked_balance`, perps draws on
13
15
  * `margin_balance`. The settlement asset's perps route is also capped by the
14
16
  * account `available_balance`, which excludes the margin open positions use.
15
17
  * Every other asset's perps route reports its full `margin_balance`, uncapped,
@@ -20,42 +22,54 @@ import { toRequiredBig } from './decimal.js'
20
22
  *
21
23
  * @param settlementAssetIndex - L2 asset index of the deployment's collateral
22
24
  * asset, the only asset that `available_balance` is denominated in.
25
+ * @param perpsCategoryId - The instance's perps category id, the
26
+ * `categoryId` its collateral `Balance` rows carry.
23
27
  * @public
24
28
  */
25
29
  export const lighterWithdrawableBalances = (
26
30
  account: Pick<LtAccount, 'assets' | 'available_balance'>,
27
- settlementAssetIndex: number
31
+ settlementAssetIndex: number,
32
+ perpsCategoryId: string
28
33
  ): ProviderWithdrawableBalance[] => {
29
34
  const rows: ProviderWithdrawableBalance[] = []
30
35
  for (const asset of account.assets) {
31
36
  const assetId = String(asset.asset_id)
32
- const spot = toRequiredBig(asset.balance, 'balance').minus(
33
- toRequiredBig(asset.locked_balance, 'locked_balance')
37
+ const spot = subtractDecimalString(
38
+ unknownToDecimalString(asset.balance, 'balance', LIGHTER_PROVIDER_KEY),
39
+ unknownToDecimalString(
40
+ asset.locked_balance,
41
+ 'locked_balance',
42
+ LIGHTER_PROVIDER_KEY
43
+ )
34
44
  )
35
- if (spot.gt(0)) {
36
- const available = spot.toFixed()
45
+ if (isDecimalStringGreaterThan(spot, '0')) {
37
46
  rows.push({
38
47
  assetId,
39
- route: 'spot',
40
- available,
41
- max: calculateWithdrawMax({ available }),
48
+ categoryId: LIGHTER_SPOT_CATEGORY_ID,
49
+ available: spot,
50
+ max: calculateWithdrawMax({ available: spot }),
42
51
  })
43
52
  }
44
- const marginBalance = toRequiredBig(asset.margin_balance, 'margin_balance')
53
+ const marginBalance = unknownToDecimalString(
54
+ asset.margin_balance,
55
+ 'margin_balance',
56
+ LIGHTER_PROVIDER_KEY
57
+ )
45
58
  const perps =
46
59
  asset.asset_id === settlementAssetIndex
47
60
  ? calculateTransferable(
48
- toRequiredBig(
61
+ unknownToDecimalString(
49
62
  account.available_balance,
50
- 'available_balance'
51
- ).toFixed(),
52
- marginBalance.toFixed()
63
+ 'available_balance',
64
+ LIGHTER_PROVIDER_KEY
65
+ ),
66
+ marginBalance
53
67
  )
54
- : marginBalance.toFixed()
55
- if (new Big(perps).gt(0)) {
68
+ : marginBalance
69
+ if (isDecimalStringGreaterThan(perps, '0')) {
56
70
  rows.push({
57
71
  assetId,
58
- route: 'perps',
72
+ categoryId: perpsCategoryId,
59
73
  available: perps,
60
74
  max: calculateWithdrawMax({ available: perps }),
61
75
  })
@@ -0,0 +1,125 @@
1
+ import {
2
+ divideDecimalString,
3
+ isDecimalStringGreaterThan,
4
+ type ProviderWithdrawableBalance,
5
+ unknownToDecimalString,
6
+ type WithdrawalAssetRef,
7
+ type WithdrawalSourceTypes,
8
+ type WithdrawalTypeOption,
9
+ } from '@lifi/perps-sdk'
10
+ import { WithdrawalType } from '@lifi/perps-types'
11
+ import { LIGHTER_PROVIDER_KEY } from '../constants.js'
12
+ import type {
13
+ LtFastwithdrawInfoResponse,
14
+ LtTransferFeeInfoResponse,
15
+ } from '../types/submit.js'
16
+
17
+ const LIGHTER_SUCCESS_CODE = 200
18
+ const L2_COLLATERAL_SCALE = '1000000'
19
+
20
+ /**
21
+ * The `withdrawalOptions` a Lighter withdrawal accepts. `fast` sends the
22
+ * amount to the fast-withdraw operator account `toAccountIndex`; `fee` is the
23
+ * transfer fee in L2 6-decimal integer units.
24
+ *
25
+ * @public
26
+ */
27
+ export type LighterWithdrawalOptions =
28
+ | { mode: 'standard' }
29
+ | { mode: 'fast'; toAccountIndex: number; fee: number }
30
+
31
+ /**
32
+ * The fast-withdraw terms Lighter offers an account.
33
+ *
34
+ * @public
35
+ */
36
+ export interface LighterFastWithdrawal {
37
+ toAccountIndex: number
38
+ fee: number
39
+ /** The lower of `withdraw_limit` and `max_withdrawal_amount`, in collateral units. */
40
+ limit: string
41
+ }
42
+
43
+ /**
44
+ * The fast-withdraw terms from `/fastwithdraw/info` and `/transferFeeInfo`, or
45
+ * `undefined` when either read reports a code other than 200 or no operator
46
+ * account is set.
47
+ *
48
+ * @throws {PerpsError} When a limit is not a decimal.
49
+ * @public
50
+ */
51
+ export const lighterFastWithdrawal = (
52
+ info: LtFastwithdrawInfoResponse,
53
+ feeInfo: LtTransferFeeInfoResponse
54
+ ): LighterFastWithdrawal | undefined => {
55
+ if (
56
+ info.code !== LIGHTER_SUCCESS_CODE ||
57
+ feeInfo.code !== LIGHTER_SUCCESS_CODE ||
58
+ !(info.to_account_index > 0)
59
+ ) {
60
+ return undefined
61
+ }
62
+ const withdrawLimit = unknownToDecimalString(
63
+ info.withdraw_limit,
64
+ 'withdraw_limit',
65
+ LIGHTER_PROVIDER_KEY
66
+ )
67
+ const maxWithdrawalAmount = unknownToDecimalString(
68
+ info.max_withdrawal_amount,
69
+ 'max_withdrawal_amount',
70
+ LIGHTER_PROVIDER_KEY
71
+ )
72
+ const limit = isDecimalStringGreaterThan(withdrawLimit, maxWithdrawalAmount)
73
+ ? maxWithdrawalAmount
74
+ : withdrawLimit
75
+ return {
76
+ toAccountIndex: info.to_account_index,
77
+ fee: feeInfo.transfer_fee_usdc,
78
+ limit: divideDecimalString(limit, L2_COLLATERAL_SCALE),
79
+ }
80
+ }
81
+
82
+ /**
83
+ * The withdrawal types per withdrawable row. Every row offers `STANDARD` up to
84
+ * its `max`. The `fastSource` row also offers `FAST` when `fast` is set, up to
85
+ * the lower of its `max` and `fast.limit`.
86
+ *
87
+ * @public
88
+ */
89
+ export const lighterWithdrawalTypes = (
90
+ rows: ProviderWithdrawableBalance[],
91
+ fastSource: WithdrawalAssetRef,
92
+ fast: LighterFastWithdrawal | undefined
93
+ ): WithdrawalSourceTypes[] =>
94
+ rows.map((row) => {
95
+ const standard: LighterWithdrawalOptions = { mode: 'standard' }
96
+ const options: WithdrawalTypeOption[] = [
97
+ {
98
+ type: WithdrawalType.STANDARD,
99
+ max: row.max,
100
+ withdrawalOptions: standard,
101
+ },
102
+ ]
103
+ if (
104
+ fast !== undefined &&
105
+ row.categoryId === fastSource.categoryId &&
106
+ row.assetId === fastSource.asset.id
107
+ ) {
108
+ const fastOptions: LighterWithdrawalOptions = {
109
+ mode: 'fast',
110
+ toAccountIndex: fast.toAccountIndex,
111
+ fee: fast.fee,
112
+ }
113
+ options.push({
114
+ type: WithdrawalType.FAST,
115
+ max: isDecimalStringGreaterThan(row.max, fast.limit)
116
+ ? fast.limit
117
+ : row.max,
118
+ withdrawalOptions: fastOptions,
119
+ })
120
+ }
121
+ return {
122
+ source: { categoryId: row.categoryId, asset: { id: row.assetId } },
123
+ options,
124
+ }
125
+ })
@@ -1,6 +1,8 @@
1
1
  import {
2
+ addDecimalString,
2
3
  cachePromise,
3
4
  getMarketRegistry,
5
+ isDecimalStringGreaterThan,
4
6
  type MarketRegistry,
5
7
  PerpsError,
6
8
  type PerpsProvider,
@@ -10,7 +12,9 @@ import {
10
12
  ReconnectingWebSocket,
11
13
  resolveRetryPolicy,
12
14
  resolveSubscribeQuote,
15
+ subtractDecimalString,
13
16
  toPerpsMarketDisplay,
17
+ unknownToDecimalString,
14
18
  WsProviderBase,
15
19
  type WsProviderFactory,
16
20
  type WsProviderFactoryParams,
@@ -24,7 +28,6 @@ import {
24
28
  type Position,
25
29
  type Subscription,
26
30
  } from '@lifi/perps-types'
27
- import Big from 'big.js'
28
31
  import type { Address } from 'viem'
29
32
  import { lighterPortfolioValue } from '../accountSummary.js'
30
33
  import {
@@ -48,7 +51,6 @@ import type {
48
51
  LtWsMarketStats,
49
52
  LtWsMarketStatsAllMessage,
50
53
  LtWsMessage,
51
- LtWsOrderBook,
52
54
  LtWsOrderBookMessage,
53
55
  LtWsSpotMarketStats,
54
56
  LtWsSpotMarketStatsAllMessage,
@@ -58,13 +60,14 @@ import type {
58
60
  import { LIGHTER_RETRY_DEFAULTS, LighterApiClient } from '../utils/apiClient.js'
59
61
  import {
60
62
  fetchDetailedAccount,
63
+ isOpenPosition,
61
64
  mapFill,
62
65
  mapMarketContext,
63
66
  mapOrderUpdates,
64
67
  mapPosition,
65
- toRequiredBig,
66
68
  } from '../utils/index.js'
67
69
  import { spotPriceByAssetId, spotValuation } from '../utils/spotPrice.js'
70
+ import { askOrder, bidOrder, OrderBookSide } from './orderBookSide.js'
68
71
 
69
72
  // Public channels: `marketsContext` (market_stats/all + spot_market_stats/all),
70
73
  // `marketContext` (market_stats/N or spot_market_stats/N), `orderbook`
@@ -139,26 +142,16 @@ interface SubState {
139
142
  needsAuth: boolean
140
143
  }
141
144
 
142
- /**
143
- * A maintained book level, keyed in {@link OrderbookState} by its price string.
144
- * `priceNum` is the price parsed once on insert so the emit-time sort orders by
145
- * a cached number instead of re-parsing every price on every comparison.
146
- */
147
- interface BookLevel {
148
- size: string
149
- priceNum: number
150
- }
151
-
152
145
  /** Settlement equity and per-asset route quantities for account valuation. */
153
146
  interface AccountSummaryInputs {
154
- perps?: { equity: Big } & Omit<AccountSummary, 'portfolioValue'>
155
- balances?: Map<number, { spot: Big; margin: Big }>
147
+ perps?: { equity: string } & Omit<AccountSummary, 'portfolioValue'>
148
+ balances?: Map<number, { spot: string; margin: string }>
156
149
  lastPortfolioValue?: string
157
150
  }
158
151
 
159
152
  interface OrderbookState {
160
- bids: Map<string, BookLevel>
161
- asks: Map<string, BookLevel>
153
+ bids: OrderBookSide
154
+ asks: OrderBookSide
162
155
  assetId: string
163
156
  }
164
157
 
@@ -730,38 +723,47 @@ export class LighterWsProvider extends WsProviderBase<SubState> {
730
723
  return
731
724
  }
732
725
 
733
- const portfolio = toRequiredBig(
726
+ const portfolio = unknownToDecimalString(
734
727
  stats.portfolio_value,
735
- 'stats.portfolio_value'
728
+ 'stats.portfolio_value',
729
+ this.providerKey
730
+ )
731
+ const collateral = unknownToDecimalString(
732
+ stats.collateral,
733
+ 'stats.collateral',
734
+ this.providerKey
736
735
  )
737
- const collateral = toRequiredBig(stats.collateral, 'stats.collateral')
738
736
 
739
- let available: Big
740
- let marginUsed: Big
737
+ let available: string
738
+ let marginUsed: string
741
739
  if (stats.cross_stats === undefined) {
742
740
  // Compatibility with legacy gateways that omitted the entire
743
741
  // cross_stats object.
744
- available = toRequiredBig(
742
+ available = unknownToDecimalString(
745
743
  stats.available_balance,
746
- 'stats.available_balance'
744
+ 'stats.available_balance',
745
+ this.providerKey
747
746
  )
748
- marginUsed = portfolio.minus(available)
747
+ marginUsed = subtractDecimalString(portfolio, available)
749
748
  } else {
750
- const crossCollateral = toRequiredBig(
749
+ const crossCollateral = unknownToDecimalString(
751
750
  stats.cross_stats.collateral,
752
- 'stats.cross_stats.collateral'
751
+ 'stats.cross_stats.collateral',
752
+ this.providerKey
753
753
  )
754
- const crossPortfolio = toRequiredBig(
754
+ const crossPortfolio = unknownToDecimalString(
755
755
  stats.cross_stats.portfolio_value,
756
- 'stats.cross_stats.portfolio_value'
756
+ 'stats.cross_stats.portfolio_value',
757
+ this.providerKey
757
758
  )
758
- available = toRequiredBig(
759
+ available = unknownToDecimalString(
759
760
  stats.cross_stats.available_balance,
760
- 'stats.cross_stats.available_balance'
761
+ 'stats.cross_stats.available_balance',
762
+ this.providerKey
761
763
  )
762
- const isolatedMargin = collateral.minus(crossCollateral)
763
- const crossMargin = crossPortfolio.minus(available)
764
- marginUsed = isolatedMargin.plus(crossMargin)
764
+ const isolatedMargin = subtractDecimalString(collateral, crossCollateral)
765
+ const crossMargin = subtractDecimalString(crossPortfolio, available)
766
+ marginUsed = addDecimalString(isolatedMargin, crossMargin)
765
767
  }
766
768
 
767
769
  const inputs = this.accountSummaryInputs.get(address)
@@ -770,9 +772,9 @@ export class LighterWsProvider extends WsProviderBase<SubState> {
770
772
  }
771
773
  inputs.perps = {
772
774
  equity: portfolio,
773
- availableMargin: available.toFixed(),
774
- marginUsed: marginUsed.toFixed(),
775
- unrealizedPnl: portfolio.minus(collateral).toFixed(),
775
+ availableMargin: available,
776
+ marginUsed,
777
+ unrealizedPnl: subtractDecimalString(portfolio, collateral),
776
778
  }
777
779
  this.emitAccountSummary(address)
778
780
  }
@@ -793,14 +795,18 @@ export class LighterWsProvider extends WsProviderBase<SubState> {
793
795
  }
794
796
  const parsed = Object.values(msg.assets ?? {}).map((asset) => ({
795
797
  assetId: asset.asset_id,
796
- spot: toRequiredBig(asset.balance, 'balance'),
798
+ spot: unknownToDecimalString(asset.balance, 'balance', this.providerKey),
797
799
  margin:
798
800
  asset.margin_balance === undefined
799
801
  ? undefined
800
- : toRequiredBig(asset.margin_balance, 'margin_balance'),
802
+ : unknownToDecimalString(
803
+ asset.margin_balance,
804
+ 'margin_balance',
805
+ this.providerKey
806
+ ),
801
807
  }))
802
808
  const balances = isSnapshot
803
- ? new Map<number, { spot: Big; margin: Big }>()
809
+ ? new Map<number, { spot: string; margin: string }>()
804
810
  : inputs.balances
805
811
  if (balances === undefined) {
806
812
  return
@@ -808,7 +814,7 @@ export class LighterWsProvider extends WsProviderBase<SubState> {
808
814
  for (const { assetId, spot, margin } of parsed) {
809
815
  balances.set(assetId, {
810
816
  spot,
811
- margin: margin ?? balances.get(assetId)?.margin ?? new Big(0),
817
+ margin: margin ?? balances.get(assetId)?.margin ?? '0',
812
818
  })
813
819
  }
814
820
  inputs.balances = balances
@@ -827,10 +833,12 @@ export class LighterWsProvider extends WsProviderBase<SubState> {
827
833
  ([assetId, { spot, margin }]) =>
828
834
  [
829
835
  assetId,
830
- assetId === settlementAssetIndex ? spot : spot.plus(margin),
836
+ assetId === settlementAssetIndex
837
+ ? spot
838
+ : addDecimalString(spot, margin),
831
839
  ] as const
832
840
  )
833
- .filter(([, balance]) => balance.gt(0))
841
+ .filter(([, balance]) => isDecimalStringGreaterThan(balance, '0'))
834
842
  const spotPrices = spotPriceByAssetId(
835
843
  this.registry?.markets ?? [],
836
844
  LIGHTER_SPOT_CATEGORY_ID,
@@ -845,14 +853,10 @@ export class LighterWsProvider extends WsProviderBase<SubState> {
845
853
  inputs.perps.equity,
846
854
  held.map(
847
855
  ([assetId, balance]) =>
848
- spotValuation(
849
- assetId,
850
- balance.toFixed(),
851
- settlementAssetIndex,
852
- spotPrices
853
- ).valueUsd
856
+ spotValuation(assetId, balance, settlementAssetIndex, spotPrices)
857
+ .valueUsd
854
858
  )
855
- ).toFixed()
859
+ )
856
860
  if (onlyOnChange && portfolioValue === inputs.lastPortfolioValue) {
857
861
  return
858
862
  }
@@ -887,7 +891,8 @@ export class LighterWsProvider extends WsProviderBase<SubState> {
887
891
  const raw = collectAuthChannelItems<LtTrade>(msg, 'trades')
888
892
  const fills: Fill[] = raw.flatMap((t) => {
889
893
  const market = this.registry?.get(String(t.market_id))
890
- return market ? [mapFill(t, accountIndex, market)] : []
894
+ const mapped = market && mapFill(t, accountIndex, market)
895
+ return mapped ? [mapped] : []
891
896
  })
892
897
  this.emit(`fills:${address}`, { channel: 'fills', data: fills })
893
898
  }
@@ -910,13 +915,18 @@ export class LighterWsProvider extends WsProviderBase<SubState> {
910
915
  this.positionsByAddress.set(address, state)
911
916
  }
912
917
  for (const p of raw) {
913
- if (Number.parseFloat(p.position) === 0) {
914
- state.delete(p.market_id)
915
- } else {
918
+ if (isOpenPosition(p)) {
916
919
  const market = this.registry?.get(String(p.market_id))
917
- if (market) {
918
- state.set(p.market_id, mapPosition(p, toPerpsMarketDisplay(market)))
920
+ const position = market
921
+ ? mapPosition(p, toPerpsMarketDisplay(market))
922
+ : undefined
923
+ if (position) {
924
+ state.set(p.market_id, position)
925
+ } else {
926
+ state.delete(p.market_id)
919
927
  }
928
+ } else {
929
+ state.delete(p.market_id)
920
930
  }
921
931
  }
922
932
  this.emit(`positions:${address}`, {
@@ -1006,20 +1016,24 @@ export class LighterWsProvider extends WsProviderBase<SubState> {
1006
1016
 
1007
1017
  let state = this.orderbooks.get(marketId)
1008
1018
  if (!state || isSnapshot) {
1009
- state = { bids: new Map(), asks: new Map(), assetId }
1019
+ state = {
1020
+ bids: new OrderBookSide(bidOrder, this.providerKey),
1021
+ asks: new OrderBookSide(askOrder, this.providerKey),
1022
+ assetId,
1023
+ }
1010
1024
  this.orderbooks.set(marketId, state)
1011
1025
  }
1012
1026
 
1013
- applyLevels(state.bids, msg.order_book.bids)
1014
- applyLevels(state.asks, msg.order_book.asks)
1027
+ state.bids.apply(msg.order_book.bids)
1028
+ state.asks.apply(msg.order_book.asks)
1015
1029
 
1016
1030
  this.emit(`orderbook:${assetId}`, {
1017
1031
  channel: 'orderbook',
1018
1032
  data: {
1019
1033
  provider: this.providerKey,
1020
1034
  marketId: assetId,
1021
- bids: mapToLevels(state.bids, true),
1022
- asks: mapToLevels(state.asks, false),
1035
+ bids: state.bids.toLevels(),
1036
+ asks: state.asks.toLevels(),
1023
1037
  timestamp: Date.now(),
1024
1038
  },
1025
1039
  })
@@ -1121,37 +1135,6 @@ function isMarketStatsEntry(
1121
1135
  return isObject(value) && typeof value.market_id === 'number'
1122
1136
  }
1123
1137
 
1124
- function applyLevels(
1125
- book: Map<string, BookLevel>,
1126
- levels: LtWsOrderBook['bids']
1127
- ): void {
1128
- for (const level of levels) {
1129
- if (level.size === '0' || Number(level.size) === 0) {
1130
- book.delete(level.price)
1131
- } else {
1132
- const existing = book.get(level.price)
1133
- if (existing) {
1134
- existing.size = level.size
1135
- } else {
1136
- book.set(level.price, {
1137
- size: level.size,
1138
- priceNum: Number(level.price),
1139
- })
1140
- }
1141
- }
1142
- }
1143
- }
1144
-
1145
- function mapToLevels(
1146
- book: Map<string, BookLevel>,
1147
- descending: boolean
1148
- ): Array<{ price: string; size: string }> {
1149
- const entries = [...book].sort(([, a], [, b]) =>
1150
- descending ? b.priceNum - a.priceNum : a.priceNum - b.priceNum
1151
- )
1152
- return entries.map(([price, { size }]) => ({ price, size }))
1153
- }
1154
-
1155
1138
  /**
1156
1139
  * Extract items from an auth-channel payload field. The field may be:
1157
1140
  * - a flat array (e.g. `trades: []` on the initial subscribed snapshot)