@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,7 +1,12 @@
1
1
  import {
2
2
  isActiveOrderStatus,
3
+ isDecimalStringGreaterThan,
3
4
  PerpsError,
5
+ safeDivideDecimalString,
6
+ safeTimestampToIsoString,
4
7
  triggerConditionFor,
8
+ unknownToDecimalString,
9
+ warnSkippedVenueRow,
5
10
  } from '@lifi/perps-sdk'
6
11
  import type { MarketDisplay, Order, OrderBase } from '@lifi/perps-types'
7
12
  import {
@@ -11,8 +16,9 @@ import {
11
16
  PerpsErrorCode,
12
17
  TimeInForce,
13
18
  } from '@lifi/perps-types'
14
- import Big from 'big.js'
19
+ import { LIGHTER_PROVIDER_KEY } from '../constants.js'
15
20
  import type { LtOrder } from '../types/index.js'
21
+ import { rowTimestampToIsoStringOrUndefined } from './rowTimestamp.js'
16
22
 
17
23
  const mapOrderType = (type: string): Order['type'] => {
18
24
  switch (type.replace(/-/g, '_')) {
@@ -56,10 +62,20 @@ const mapTimeInForce = (tif: string): TimeInForce => {
56
62
  }
57
63
  }
58
64
 
59
- const mapOrderStatus = (
60
- order: LtOrder,
61
- filledSize = new Big(order.filled_base_amount)
62
- ): OrderStatus => {
65
+ const venueDecimal = (value: unknown, field: string): string | undefined => {
66
+ try {
67
+ return unknownToDecimalString(value, field, LIGHTER_PROVIDER_KEY)
68
+ } catch {
69
+ return undefined
70
+ }
71
+ }
72
+
73
+ const hasFill = (order: LtOrder): boolean => {
74
+ const filled = venueDecimal(order.filled_base_amount, 'filled_base_amount')
75
+ return filled !== undefined && isDecimalStringGreaterThan(filled, '0')
76
+ }
77
+
78
+ const mapOrderStatus = (order: LtOrder): OrderStatus => {
63
79
  switch (order.status) {
64
80
  case 'in-progress':
65
81
  return OrderStatus.ACCEPTED
@@ -69,7 +85,7 @@ const mapOrderStatus = (
69
85
  if (order.trigger_status === 'parent-order') {
70
86
  return OrderStatus.PENDING
71
87
  }
72
- return filledSize.gt(0) ? OrderStatus.PARTIALLY_FILLED : OrderStatus.OPEN
88
+ return hasFill(order) ? OrderStatus.PARTIALLY_FILLED : OrderStatus.OPEN
73
89
  case 'triggered':
74
90
  return OrderStatus.TRIGGERED
75
91
  case 'filled':
@@ -97,11 +113,72 @@ const mapOrderStatus = (
97
113
  }
98
114
  }
99
115
 
100
- /** Map a Lighter order with its venue identity, lifecycle, and execution fields. */
101
- export const mapOrder = (order: LtOrder, market: MarketDisplay): Order => {
116
+ const skipOrder = (field: string, value: unknown): undefined => {
117
+ warnSkippedVenueRow(LIGHTER_PROVIDER_KEY, 'order', field, value)
118
+ return undefined
119
+ }
120
+
121
+ const orderSizeOrSkip = (
122
+ field: 'initial_base_amount' | 'remaining_base_amount' | 'filled_base_amount',
123
+ value: string
124
+ ): string | undefined =>
125
+ venueDecimal(value, field) === undefined ? skipOrder(field, value) : value
126
+
127
+ /**
128
+ * Map a Lighter order with its venue identity, lifecycle, and execution fields.
129
+ * A row with an invalid size or time gives `undefined`. An invalid limit price or
130
+ * filled quote amount omits that field.
131
+ */
132
+ export const mapOrder = (
133
+ order: LtOrder,
134
+ market: MarketDisplay
135
+ ): Order | undefined => {
102
136
  const type = mapOrderType(order.type)
103
- const filledSize = new Big(order.filled_base_amount)
104
- const status = mapOrderStatus(order, filledSize)
137
+ const originalSize = orderSizeOrSkip(
138
+ 'initial_base_amount',
139
+ order.initial_base_amount
140
+ )
141
+ if (originalSize === undefined) {
142
+ return undefined
143
+ }
144
+ const remainingSize = orderSizeOrSkip(
145
+ 'remaining_base_amount',
146
+ order.remaining_base_amount
147
+ )
148
+ if (remainingSize === undefined) {
149
+ return undefined
150
+ }
151
+ const filledSize = orderSizeOrSkip(
152
+ 'filled_base_amount',
153
+ order.filled_base_amount
154
+ )
155
+ if (filledSize === undefined) {
156
+ return undefined
157
+ }
158
+ const createdAt = rowTimestampToIsoStringOrUndefined(order.created_at * 1000)
159
+ if (createdAt === undefined) {
160
+ return skipOrder('created_at', order.created_at)
161
+ }
162
+ const updatedAt = rowTimestampToIsoStringOrUndefined(order.updated_at * 1000)
163
+ if (updatedAt === undefined) {
164
+ return skipOrder('updated_at', order.updated_at)
165
+ }
166
+ const filledQuote = venueDecimal(
167
+ order.filled_quote_amount,
168
+ 'filled_quote_amount'
169
+ )
170
+ const filledAmount = venueDecimal(filledSize, 'filled_base_amount')
171
+ const averagePrice =
172
+ filledQuote !== undefined &&
173
+ filledAmount !== undefined &&
174
+ isDecimalStringGreaterThan(filledAmount, '0')
175
+ ? safeDivideDecimalString(filledQuote, filledAmount)
176
+ : undefined
177
+ const expiresAt =
178
+ order.order_expiry > 0
179
+ ? safeTimestampToIsoString(order.order_expiry)
180
+ : undefined
181
+ const status = mapOrderStatus(order)
105
182
  const base: OrderBase = {
106
183
  orderId: String(order.order_index),
107
184
  ...(order.client_order_index === 0
@@ -111,20 +188,14 @@ export const mapOrder = (order: LtOrder, market: MarketDisplay): Order => {
111
188
  side: order.is_ask ? OrderSide.SELL : OrderSide.BUY,
112
189
  status,
113
190
  ...(status === OrderStatus.CANCELLED ? { statusReason: order.status } : {}),
114
- originalSize: order.initial_base_amount,
115
- remainingSize: order.remaining_base_amount,
116
- filledSize: order.filled_base_amount,
117
- ...(filledSize.gt(0)
118
- ? {
119
- averagePrice: new Big(order.filled_quote_amount)
120
- .div(filledSize)
121
- .toFixed(),
122
- }
123
- : {}),
191
+ originalSize,
192
+ remainingSize,
193
+ filledSize,
194
+ ...(averagePrice === undefined ? {} : { averagePrice }),
124
195
  reduceOnly: order.reduce_only,
125
196
  ...(order.parent_order_id ? { parentOrderId: order.parent_order_id } : {}),
126
- createdAt: new Date(order.created_at * 1000).toISOString(),
127
- updatedAt: new Date(order.updated_at * 1000).toISOString(),
197
+ createdAt,
198
+ updatedAt,
128
199
  }
129
200
  switch (type) {
130
201
  case OrderType.TWAP:
@@ -137,17 +208,19 @@ export const mapOrder = (order: LtOrder, market: MarketDisplay): Order => {
137
208
  case OrderType.STOP_MARKET:
138
209
  case OrderType.STOP_LIMIT:
139
210
  case OrderType.TAKE_PROFIT_MARKET:
140
- case OrderType.TAKE_PROFIT_LIMIT:
211
+ case OrderType.TAKE_PROFIT_LIMIT: {
141
212
  return {
142
213
  ...base,
143
214
  type,
144
215
  triggerPrice: order.trigger_price,
145
216
  triggerCondition: triggerConditionFor(type, base.side),
146
- ...(type === OrderType.STOP_LIMIT ||
147
- type === OrderType.TAKE_PROFIT_LIMIT
217
+ ...((type === OrderType.STOP_LIMIT ||
218
+ type === OrderType.TAKE_PROFIT_LIMIT) &&
219
+ venueDecimal(order.price, 'price') !== undefined
148
220
  ? { limitPrice: order.price }
149
221
  : {}),
150
222
  }
223
+ }
151
224
  case OrderType.MARKET:
152
225
  case OrderType.LIMIT:
153
226
  return {
@@ -155,9 +228,7 @@ export const mapOrder = (order: LtOrder, market: MarketDisplay): Order => {
155
228
  type,
156
229
  price: order.price,
157
230
  timeInForce: mapTimeInForce(order.time_in_force),
158
- ...(order.order_expiry > 0
159
- ? { expiresAt: new Date(order.order_expiry).toISOString() }
160
- : {}),
231
+ ...(expiresAt === undefined ? {} : { expiresAt }),
161
232
  }
162
233
  }
163
234
  }
@@ -1,33 +1,85 @@
1
+ import {
2
+ addDecimalString,
3
+ numberToDecimalString,
4
+ subtractDecimalString,
5
+ warnSkippedVenueRow,
6
+ } from '@lifi/perps-sdk'
1
7
  import type {
2
8
  PortfolioHistoryRange,
3
9
  PortfolioHistoryResponse,
4
10
  } from '@lifi/perps-types'
5
- import Big from 'big.js'
11
+ import { LIGHTER_PROVIDER_KEY } from '../constants.js'
6
12
  import type { LtPnLEntry } from '../types/pnl.js'
7
13
 
8
- function getCumulativePnl(snapshot: LtPnLEntry): Big {
9
- return new Big(snapshot.trade_pnl).plus(snapshot.trade_spot_pnl)
14
+ const SNAPSHOT_DECIMAL_FIELDS = [
15
+ 'trade_pnl',
16
+ 'trade_spot_pnl',
17
+ 'inflow',
18
+ 'outflow',
19
+ 'spot_inflow',
20
+ 'spot_outflow',
21
+ 'volume',
22
+ ] as const
23
+
24
+ type SnapshotDecimalField = (typeof SNAPSHOT_DECIMAL_FIELDS)[number]
25
+
26
+ const snapshotDecimal = (
27
+ snapshot: LtPnLEntry,
28
+ field: SnapshotDecimalField
29
+ ): string => numberToDecimalString(snapshot[field])
30
+
31
+ function isValidSnapshot(snapshot: LtPnLEntry): boolean {
32
+ const invalid = SNAPSHOT_DECIMAL_FIELDS.find(
33
+ (field) => !Number.isFinite(snapshot[field])
34
+ )
35
+ if (invalid === undefined) {
36
+ return true
37
+ }
38
+ warnSkippedVenueRow(
39
+ LIGHTER_PROVIDER_KEY,
40
+ 'portfolio history',
41
+ invalid,
42
+ snapshot[invalid]
43
+ )
44
+ return false
10
45
  }
11
46
 
12
- function getCumulativeNetFlow(snapshot: LtPnLEntry): Big {
13
- return new Big(snapshot.inflow)
14
- .minus(snapshot.outflow)
15
- .plus(snapshot.spot_inflow)
16
- .minus(snapshot.spot_outflow)
47
+ function getCumulativePnl(snapshot: LtPnLEntry): string {
48
+ return addDecimalString(
49
+ snapshotDecimal(snapshot, 'trade_pnl'),
50
+ snapshotDecimal(snapshot, 'trade_spot_pnl')
51
+ )
52
+ }
53
+
54
+ function getCumulativeNetFlow(snapshot: LtPnLEntry): string {
55
+ return subtractDecimalString(
56
+ addDecimalString(
57
+ subtractDecimalString(
58
+ snapshotDecimal(snapshot, 'inflow'),
59
+ snapshotDecimal(snapshot, 'outflow')
60
+ ),
61
+ snapshotDecimal(snapshot, 'spot_inflow')
62
+ ),
63
+ snapshotDecimal(snapshot, 'spot_outflow')
64
+ )
17
65
  }
18
66
 
19
67
  /**
20
68
  * Derive window-relative history from Lighter's cumulative PnL snapshots.
21
- * Historical account values are anchored to the current account value.
69
+ * Historical account values are anchored to the current account value. A
70
+ * snapshot with a non-finite value is skipped.
71
+ *
72
+ * @throws {PerpsError} `ValidationError` when `currentValue` is not a decimal
73
+ * string.
22
74
  */
23
75
  export const mapPortfolioHistory = (
24
76
  range: PortfolioHistoryRange,
25
77
  snapshots: LtPnLEntry[],
26
- currentValue: Big
78
+ currentValue: string
27
79
  ): PortfolioHistoryResponse => {
28
- const orderedSnapshots = [...snapshots].sort(
29
- (a, b) => a.timestamp - b.timestamp
30
- )
80
+ const orderedSnapshots = snapshots
81
+ .filter(isValidSnapshot)
82
+ .sort((a, b) => a.timestamp - b.timestamp)
31
83
  const firstSnapshot = orderedSnapshots[0]
32
84
  const latestSnapshot = orderedSnapshots.at(-1)
33
85
  if (!firstSnapshot || !latestSnapshot) {
@@ -41,27 +93,29 @@ export const mapPortfolioHistory = (
41
93
 
42
94
  const firstPnl = getCumulativePnl(firstSnapshot)
43
95
  const latestPnl = getCumulativePnl(latestSnapshot)
44
- const accountValueOffset = currentValue
45
- .minus(latestPnl)
46
- .minus(getCumulativeNetFlow(latestSnapshot))
96
+ const accountValueOffset = subtractDecimalString(
97
+ subtractDecimalString(currentValue, latestPnl),
98
+ getCumulativeNetFlow(latestSnapshot)
99
+ )
47
100
  const points = orderedSnapshots.map((snapshot) => {
48
101
  const pnl = getCumulativePnl(snapshot)
49
102
  return {
50
103
  timestamp: snapshot.timestamp * 1_000,
51
- accountValue: accountValueOffset
52
- .plus(pnl)
53
- .plus(getCumulativeNetFlow(snapshot))
54
- .toFixed(),
55
- pnl: pnl.minus(firstPnl).toFixed(),
104
+ accountValue: addDecimalString(
105
+ addDecimalString(accountValueOffset, pnl),
106
+ getCumulativeNetFlow(snapshot)
107
+ ),
108
+ pnl: subtractDecimalString(pnl, firstPnl),
56
109
  }
57
110
  })
58
111
 
59
112
  return {
60
113
  range,
61
114
  points,
62
- volume: new Big(latestSnapshot.volume)
63
- .minus(firstSnapshot.volume)
64
- .toFixed(),
115
+ volume: subtractDecimalString(
116
+ snapshotDecimal(latestSnapshot, 'volume'),
117
+ snapshotDecimal(firstSnapshot, 'volume')
118
+ ),
65
119
  totalPnl: points.at(-1)?.pnl,
66
120
  }
67
121
  }
@@ -1,86 +1,109 @@
1
+ import {
2
+ absDecimalString,
3
+ divideDecimalString,
4
+ isDecimalString,
5
+ isDecimalStringZero,
6
+ multiplyDecimalString,
7
+ safeDivideDecimalString,
8
+ safeIsDecimalStringGreaterThan,
9
+ safeNumberToDecimalString,
10
+ safeRoundDecimalString,
11
+ warnSkippedVenueRow,
12
+ } from '@lifi/perps-sdk'
1
13
  import type { PerpsMarketDisplay, Position } from '@lifi/perps-types'
2
14
  import { MarginMode, PositionSide } from '@lifi/perps-types'
3
- import Big from 'big.js'
4
- import { LIGHTER_IMF_PERCENT_SCALE } from '../constants.js'
15
+ import {
16
+ LIGHTER_IMF_PERCENT_SCALE,
17
+ LIGHTER_LEVERAGE_PRECISION,
18
+ LIGHTER_PROVIDER_KEY,
19
+ } from '../constants.js'
5
20
  import type { LtAccountPosition } from '../types/index.js'
6
21
  import { LT_MARGIN_MODE_ISOLATED } from '../types/index.js'
7
- import { toPositiveRequiredBig, toRequiredBig } from './decimal.js'
8
22
 
9
23
  /**
10
24
  * Display leverage from an IMF percent string: `100 / IMF` in exact decimal
11
- * arithmetic before conversion to `number`. This value is for displaying the
12
- * venue setting; provider risk calculations consume the original decimal IMF
13
- * instead. `undefined` for a non-positive or unparsable IMF.
25
+ * arithmetic, rounded half-up to `LIGHTER_LEVERAGE_PRECISION`, so the value a
26
+ * client reads is the value it saved. Provider risk calculations consume the
27
+ * original decimal IMF instead. `undefined` for a non-positive or unparsable
28
+ * IMF.
14
29
  * @public
15
30
  */
16
- export const leverageFromImf = (imf: string): number | undefined => {
17
- let parsed: Big
18
- try {
19
- parsed = new Big(imf)
20
- } catch {
31
+ export const leverageFromImf = (imf: string): string | undefined => {
32
+ if (safeIsDecimalStringGreaterThan(imf, '0') !== true) {
21
33
  return undefined
22
34
  }
23
- if (parsed.lte(0)) {
24
- return undefined
25
- }
26
- return new Big(100).div(parsed).toNumber()
35
+ const leverage = safeDivideDecimalString('100', imf)
36
+ return leverage === undefined
37
+ ? undefined
38
+ : safeRoundDecimalString(leverage, LIGHTER_LEVERAGE_PRECISION, 'round')
27
39
  }
28
40
 
29
41
  /**
30
42
  * Display leverage from an integer IMF on `LIGHTER_IMF_PERCENT_SCALE`, the
31
43
  * unit of trade rows and order-book details: `500` is 5.00%, so 20x.
32
- * `undefined` for a non-positive or unparsable IMF. Lighter declares the
44
+ * `undefined` for a non-positive or non-finite IMF. Lighter declares the
33
45
  * fraction a `StrictInt`, so the scale division is exact.
34
46
  */
35
- export const leverageFromScaledImf = (imf: number): number | undefined => {
36
- let percent: string
37
- try {
38
- percent = new Big(imf).div(LIGHTER_IMF_PERCENT_SCALE).toFixed()
39
- } catch {
40
- return undefined
41
- }
42
- return leverageFromImf(percent)
47
+ export const leverageFromScaledImf = (imf: number): string | undefined => {
48
+ const scaled = safeNumberToDecimalString(imf)
49
+ const percent =
50
+ scaled === undefined
51
+ ? undefined
52
+ : safeDivideDecimalString(scaled, String(LIGHTER_IMF_PERCENT_SCALE))
53
+ return percent === undefined ? undefined : leverageFromImf(percent)
54
+ }
55
+
56
+ const skipPosition = (field: string, value: unknown): undefined => {
57
+ warnSkippedVenueRow(LIGHTER_PROVIDER_KEY, 'position', field, value)
58
+ return undefined
43
59
  }
44
60
 
45
61
  /**
46
- * Map a raw Lighter account position to the generic Position type.
62
+ * Map a raw Lighter account position to the generic Position type. A row with
63
+ * an invalid size or value, or a non-positive initial margin fraction, gives
64
+ * `undefined`: the mapper derives the size, mark price, margin and leverage
65
+ * from them. Prices, PnL, funding and isolated margin pass through raw.
47
66
  * @param market - Backend-resolved market identity for `pos.market_id`.
48
67
  * @public
49
68
  */
50
69
  export const mapPosition = (
51
70
  pos: LtAccountPosition,
52
71
  market: PerpsMarketDisplay
53
- ): Position => {
54
- const size = toRequiredBig(pos.position, 'position')
72
+ ): Position | undefined => {
73
+ if (!isDecimalString(pos.position)) {
74
+ return skipPosition('position', pos.position)
75
+ }
76
+ if (!isDecimalString(pos.position_value)) {
77
+ return skipPosition('position_value', pos.position_value)
78
+ }
79
+ const imf = pos.initial_margin_fraction
80
+ const leverage = isDecimalString(imf) ? leverageFromImf(imf) : undefined
81
+ if (leverage === undefined) {
82
+ return skipPosition('initial_margin_fraction', imf)
83
+ }
55
84
  const isIsolated = pos.margin_mode === LT_MARGIN_MODE_ISOLATED
56
- const positionValue = toRequiredBig(
57
- pos.position_value,
58
- 'position_value'
59
- ).abs()
60
- const imf = toPositiveRequiredBig(
61
- pos.initial_margin_fraction,
62
- 'initial_margin_fraction'
85
+ const size = absDecimalString(pos.position)
86
+ const positionValue = absDecimalString(pos.position_value)
87
+ const initialMarginRequirement = divideDecimalString(
88
+ multiplyDecimalString(positionValue, imf),
89
+ '100'
63
90
  )
64
- const initialMarginRequirement = positionValue.times(imf).div(100)
65
- const marginUsed = isIsolated
66
- ? pos.allocated_margin
67
- : initialMarginRequirement.toFixed()
68
91
 
69
92
  return {
70
93
  market,
71
94
  side: pos.sign >= 0 ? PositionSide.LONG : PositionSide.SHORT,
72
- size: size.abs().toFixed(),
95
+ size,
73
96
  entryPrice: pos.avg_entry_price,
74
97
  markPrice:
75
- positionValue.eq(0) || size.eq(0)
98
+ isDecimalStringZero(positionValue) || isDecimalStringZero(size)
76
99
  ? '0'
77
- : positionValue.div(size.abs()).toFixed(),
100
+ : divideDecimalString(positionValue, size),
78
101
  liquidationPrice: pos.liquidation_price,
79
102
  unrealizedPnl: pos.unrealized_pnl,
80
103
  accruedFunding: pos.total_funding_paid_out ?? '0',
81
- leverage: leverageFromImf(pos.initial_margin_fraction) ?? 1,
82
- marginUsed,
83
- initialMarginRequirement: initialMarginRequirement.toFixed(),
104
+ leverage,
105
+ marginUsed: isIsolated ? pos.allocated_margin : initialMarginRequirement,
106
+ initialMarginRequirement,
84
107
  marginMode: isIsolated ? MarginMode.ISOLATED : MarginMode.CROSS,
85
108
  }
86
109
  }
@@ -0,0 +1,15 @@
1
+ import { timestampToIsoString } from '@lifi/perps-sdk'
2
+
3
+ /**
4
+ * Read a venue row time as an ISO-8601 string, or `undefined` when the time is
5
+ * not valid. The caller logs the row skip, so this logs nothing.
6
+ */
7
+ export const rowTimestampToIsoStringOrUndefined = (
8
+ value: string | number
9
+ ): string | undefined => {
10
+ try {
11
+ return timestampToIsoString(value)
12
+ } catch {
13
+ return undefined
14
+ }
15
+ }
@@ -1,11 +1,21 @@
1
- import type Big from 'big.js'
1
+ import {
2
+ addDecimalString,
3
+ isDecimalStringGreaterThan,
4
+ subtractDecimalString,
5
+ unknownToDecimalString,
6
+ } from '@lifi/perps-sdk'
7
+ import { LIGHTER_PROVIDER_KEY } from '../constants.js'
2
8
  import type { LtAccountAsset } from '../types/account.js'
3
- import { toRequiredBig } from './decimal.js'
4
9
 
5
10
  /** Both routes of a unified account's settlement asset, as one holding. */
6
- export const pooledSettlementUnits = (asset: LtAccountAsset): Big =>
7
- toRequiredBig(asset.balance, 'balance').plus(
8
- toRequiredBig(asset.margin_balance, 'margin_balance')
11
+ export const pooledSettlementUnits = (asset: LtAccountAsset): string =>
12
+ addDecimalString(
13
+ unknownToDecimalString(asset.balance, 'balance', LIGHTER_PROVIDER_KEY),
14
+ unknownToDecimalString(
15
+ asset.margin_balance,
16
+ 'margin_balance',
17
+ LIGHTER_PROVIDER_KEY
18
+ )
9
19
  )
10
20
 
11
21
  /**
@@ -16,12 +26,26 @@ export const pooledSettlementUnits = (asset: LtAccountAsset): Big =>
16
26
  */
17
27
  export const pooledSettlementSpendable = (
18
28
  asset: LtAccountAsset,
19
- availableBalance: Big
20
- ): Big => {
21
- const marginBalance = toRequiredBig(asset.margin_balance, 'margin_balance')
22
- return toRequiredBig(asset.balance, 'balance')
23
- .minus(toRequiredBig(asset.locked_balance, 'locked_balance'))
24
- .plus(marginBalance.lt(availableBalance) ? marginBalance : availableBalance)
29
+ availableBalance: string
30
+ ): string => {
31
+ const marginBalance = unknownToDecimalString(
32
+ asset.margin_balance,
33
+ 'margin_balance',
34
+ LIGHTER_PROVIDER_KEY
35
+ )
36
+ return addDecimalString(
37
+ subtractDecimalString(
38
+ unknownToDecimalString(asset.balance, 'balance', LIGHTER_PROVIDER_KEY),
39
+ unknownToDecimalString(
40
+ asset.locked_balance,
41
+ 'locked_balance',
42
+ LIGHTER_PROVIDER_KEY
43
+ )
44
+ ),
45
+ isDecimalStringGreaterThan(marginBalance, availableBalance)
46
+ ? availableBalance
47
+ : marginBalance
48
+ )
25
49
  }
26
50
 
27
51
  /** Spot-route `balance` of the collateral asset; `'0'` when the account holds none. */
@@ -32,5 +56,5 @@ export const collateralSpotBalance = (
32
56
  const asset = assets.find((a) => a.asset_id === settlementAssetIndex)
33
57
  return asset === undefined
34
58
  ? '0'
35
- : toRequiredBig(asset.balance, 'balance').toFixed()
59
+ : unknownToDecimalString(asset.balance, 'balance', LIGHTER_PROVIDER_KEY)
36
60
  }
@@ -1,6 +1,10 @@
1
+ import {
2
+ isDecimalStringGreaterThan,
3
+ multiplyDecimalString,
4
+ unknownToDecimalString,
5
+ } from '@lifi/perps-sdk'
1
6
  import type { Market, MarketContext } from '@lifi/perps-types'
2
- import Big from 'big.js'
3
- import { toRequiredBig } from './decimal.js'
7
+ import { LIGHTER_PROVIDER_KEY } from '../constants.js'
4
8
 
5
9
  /**
6
10
  * Unit prices keyed by spot base `Asset.id` (the venue `asset_id`), read from
@@ -17,9 +21,9 @@ export const spotPriceByAssetId = (
17
21
  spotCategoryId: string,
18
22
  contexts: readonly MarketContext[],
19
23
  assetIds: ReadonlySet<string>
20
- ): Map<string, Big> => {
24
+ ): Map<string, string> => {
21
25
  const markByMarketId = new Map(contexts.map((c) => [c.marketId, c.markPrice]))
22
- const priceByAssetId = new Map<string, Big>()
26
+ const priceByAssetId = new Map<string, string>()
23
27
  for (const market of markets) {
24
28
  const mark = markByMarketId.get(market.id)
25
29
  if (
@@ -30,8 +34,12 @@ export const spotPriceByAssetId = (
30
34
  ) {
31
35
  continue
32
36
  }
33
- const price = toRequiredBig(mark, 'markPrice')
34
- if (price.gt(0)) {
37
+ const price = unknownToDecimalString(
38
+ mark,
39
+ 'markPrice',
40
+ LIGHTER_PROVIDER_KEY
41
+ )
42
+ if (isDecimalStringGreaterThan(price, '0')) {
35
43
  priceByAssetId.set(market.baseAsset.id, price)
36
44
  }
37
45
  }
@@ -47,13 +55,11 @@ export const spotValuation = (
47
55
  assetId: number,
48
56
  units: string,
49
57
  settlementAssetIndex: number,
50
- spotPrices: ReadonlyMap<string, Big>
51
- ): { valueUsd: string; price?: Big } => {
58
+ spotPrices: ReadonlyMap<string, string>
59
+ ): { valueUsd: string; price?: string } => {
52
60
  const price =
53
- assetId === settlementAssetIndex
54
- ? new Big(1)
55
- : spotPrices.get(String(assetId))
61
+ assetId === settlementAssetIndex ? '1' : spotPrices.get(String(assetId))
56
62
  return price === undefined
57
63
  ? { valueUsd: '0' }
58
- : { valueUsd: new Big(units).times(price).toFixed(), price }
64
+ : { valueUsd: multiplyDecimalString(units, price), price }
59
65
  }
package/src/utils/time.ts CHANGED
@@ -1,6 +1,16 @@
1
- /** Convert seconds since unix epoch to ISO-8601. */
1
+ import { timestampToIsoString } from '@lifi/perps-sdk'
2
+
3
+ /**
4
+ * Convert seconds since unix epoch to ISO-8601.
5
+ *
6
+ * @throws {PerpsError} `ValidationError` when `seconds` is not a valid time.
7
+ */
2
8
  export const toIsoFromSeconds = (seconds: number): string =>
3
- new Date(seconds * 1000).toISOString()
9
+ timestampToIsoString(seconds * 1000)
4
10
 
5
- /** Convert milliseconds since unix epoch to ISO-8601. */
6
- export const toIsoFromMs = (ms: number): string => new Date(ms).toISOString()
11
+ /**
12
+ * Convert milliseconds since unix epoch to ISO-8601.
13
+ *
14
+ * @throws {PerpsError} `ValidationError` when `ms` is not a valid time.
15
+ */
16
+ export const toIsoFromMs = (ms: number): string => timestampToIsoString(ms)