@lifi/perps-sdk 23.0.0 → 24.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 (289) hide show
  1. package/README.md +85 -45
  2. package/dist/cjs/client/PerpsClient.d.ts +3 -2
  3. package/dist/cjs/client/PerpsClient.d.ts.map +1 -1
  4. package/dist/cjs/client/PerpsClient.js +16 -9
  5. package/dist/cjs/client/PerpsClient.js.map +1 -1
  6. package/dist/cjs/decimal/arithmetic.d.ts +13 -0
  7. package/dist/cjs/decimal/arithmetic.d.ts.map +1 -0
  8. package/dist/cjs/decimal/arithmetic.js +48 -0
  9. package/dist/cjs/decimal/arithmetic.js.map +1 -0
  10. package/dist/cjs/decimal/big.d.ts +0 -1
  11. package/dist/cjs/decimal/big.d.ts.map +1 -1
  12. package/dist/cjs/decimal/big.js +0 -4
  13. package/dist/cjs/decimal/big.js.map +1 -1
  14. package/dist/cjs/decimal/compare.d.ts +7 -0
  15. package/dist/cjs/decimal/compare.d.ts.map +1 -0
  16. package/dist/cjs/decimal/compare.js +21 -0
  17. package/dist/cjs/decimal/compare.js.map +1 -0
  18. package/dist/cjs/decimal/convert.d.ts +12 -6
  19. package/dist/cjs/decimal/convert.d.ts.map +1 -1
  20. package/dist/cjs/decimal/convert.js +36 -14
  21. package/dist/cjs/decimal/convert.js.map +1 -1
  22. package/dist/cjs/decimal/decimalStringToBig.d.ts +5 -0
  23. package/dist/cjs/decimal/decimalStringToBig.d.ts.map +1 -0
  24. package/dist/cjs/decimal/decimalStringToBig.js +27 -0
  25. package/dist/cjs/decimal/decimalStringToBig.js.map +1 -0
  26. package/dist/cjs/decimal/format.d.ts.map +1 -1
  27. package/dist/cjs/decimal/format.js +70 -49
  28. package/dist/cjs/decimal/format.js.map +1 -1
  29. package/dist/cjs/decimal/parse.d.ts +4 -3
  30. package/dist/cjs/decimal/parse.d.ts.map +1 -1
  31. package/dist/cjs/decimal/parse.js +44 -19
  32. package/dist/cjs/decimal/parse.js.map +1 -1
  33. package/dist/cjs/index.d.ts +13 -10
  34. package/dist/cjs/index.d.ts.map +1 -1
  35. package/dist/cjs/index.js +66 -7
  36. package/dist/cjs/index.js.map +1 -1
  37. package/dist/cjs/math/fills.d.ts +1 -1
  38. package/dist/cjs/math/fills.d.ts.map +1 -1
  39. package/dist/cjs/math/fills.js +28 -13
  40. package/dist/cjs/math/fills.js.map +1 -1
  41. package/dist/cjs/math/order.d.ts +27 -16
  42. package/dist/cjs/math/order.d.ts.map +1 -1
  43. package/dist/cjs/math/order.js +106 -122
  44. package/dist/cjs/math/order.js.map +1 -1
  45. package/dist/cjs/math/position.d.ts +85 -35
  46. package/dist/cjs/math/position.d.ts.map +1 -1
  47. package/dist/cjs/math/position.js +88 -91
  48. package/dist/cjs/math/position.js.map +1 -1
  49. package/dist/cjs/services/getGasRecommendation.js +1 -1
  50. package/dist/cjs/services/getGasRecommendation.js.map +1 -1
  51. package/dist/cjs/services/getQuote.d.ts +1 -1
  52. package/dist/cjs/services/resolveSubscribeQuote.d.ts.map +1 -1
  53. package/dist/cjs/services/resolveSubscribeQuote.js +3 -2
  54. package/dist/cjs/services/resolveSubscribeQuote.js.map +1 -1
  55. package/dist/cjs/transport/request.d.ts +1 -0
  56. package/dist/cjs/transport/request.d.ts.map +1 -1
  57. package/dist/cjs/transport/request.js +2 -2
  58. package/dist/cjs/transport/request.js.map +1 -1
  59. package/dist/cjs/types/api.d.ts +4 -0
  60. package/dist/cjs/types/api.d.ts.map +1 -1
  61. package/dist/cjs/types/provider.d.ts +9 -5
  62. package/dist/cjs/types/provider.d.ts.map +1 -1
  63. package/dist/cjs/types/withdrawal.d.ts +12 -4
  64. package/dist/cjs/types/withdrawal.d.ts.map +1 -1
  65. package/dist/cjs/utils/createSafeFunction.d.ts +2 -0
  66. package/dist/cjs/utils/createSafeFunction.d.ts.map +1 -0
  67. package/dist/cjs/utils/createSafeFunction.js +15 -0
  68. package/dist/cjs/utils/createSafeFunction.js.map +1 -0
  69. package/dist/cjs/utils/timestampToIsoString.d.ts +3 -0
  70. package/dist/cjs/utils/timestampToIsoString.d.ts.map +1 -0
  71. package/dist/cjs/utils/timestampToIsoString.js +16 -0
  72. package/dist/cjs/utils/timestampToIsoString.js.map +1 -0
  73. package/dist/cjs/utils/warnOnce.d.ts +2 -0
  74. package/dist/cjs/utils/warnOnce.d.ts.map +1 -1
  75. package/dist/cjs/utils/warnOnce.js +13 -1
  76. package/dist/cjs/utils/warnOnce.js.map +1 -1
  77. package/dist/cjs/version.d.ts +1 -1
  78. package/dist/cjs/version.js +1 -1
  79. package/dist/cjs/websocket/PerpsWsClient.d.ts +2 -0
  80. package/dist/cjs/websocket/PerpsWsClient.d.ts.map +1 -1
  81. package/dist/cjs/websocket/PerpsWsClient.js +14 -1
  82. package/dist/cjs/websocket/PerpsWsClient.js.map +1 -1
  83. package/dist/cjs/websocket/wsLog.d.ts +2 -0
  84. package/dist/cjs/websocket/wsLog.d.ts.map +1 -1
  85. package/dist/cjs/websocket/wsLog.js +8 -0
  86. package/dist/cjs/websocket/wsLog.js.map +1 -1
  87. package/dist/cjs/wire/orderAmounts.d.ts.map +1 -1
  88. package/dist/cjs/wire/orderAmounts.js +6 -9
  89. package/dist/cjs/wire/orderAmounts.js.map +1 -1
  90. package/dist/cjs/wire/refuel.d.ts +4 -1
  91. package/dist/cjs/wire/refuel.d.ts.map +1 -1
  92. package/dist/cjs/wire/refuel.js +23 -6
  93. package/dist/cjs/wire/refuel.js.map +1 -1
  94. package/dist/cjs/wire/transferable.js +3 -3
  95. package/dist/cjs/wire/transferable.js.map +1 -1
  96. package/dist/cjs/wire/withdraw.d.ts.map +1 -1
  97. package/dist/cjs/wire/withdraw.js +3 -3
  98. package/dist/cjs/wire/withdraw.js.map +1 -1
  99. package/dist/esm/client/PerpsClient.d.ts +17 -4
  100. package/dist/esm/client/PerpsClient.d.ts.map +1 -1
  101. package/dist/esm/client/PerpsClient.js +30 -8
  102. package/dist/esm/client/PerpsClient.js.map +1 -1
  103. package/dist/esm/decimal/arithmetic.d.ts +84 -0
  104. package/dist/esm/decimal/arithmetic.d.ts.map +1 -0
  105. package/dist/esm/decimal/arithmetic.js +110 -0
  106. package/dist/esm/decimal/arithmetic.js.map +1 -0
  107. package/dist/esm/decimal/big.d.ts +0 -2
  108. package/dist/esm/decimal/big.d.ts.map +1 -1
  109. package/dist/esm/decimal/big.js +0 -4
  110. package/dist/esm/decimal/big.js.map +1 -1
  111. package/dist/esm/decimal/compare.d.ts +45 -0
  112. package/dist/esm/decimal/compare.d.ts.map +1 -0
  113. package/dist/esm/decimal/compare.js +53 -0
  114. package/dist/esm/decimal/compare.js.map +1 -0
  115. package/dist/esm/decimal/convert.d.ts +62 -26
  116. package/dist/esm/decimal/convert.d.ts.map +1 -1
  117. package/dist/esm/decimal/convert.js +76 -25
  118. package/dist/esm/decimal/convert.js.map +1 -1
  119. package/dist/esm/decimal/decimalStringToBig.d.ts +26 -0
  120. package/dist/esm/decimal/decimalStringToBig.d.ts.map +1 -0
  121. package/dist/esm/decimal/decimalStringToBig.js +40 -0
  122. package/dist/esm/decimal/decimalStringToBig.js.map +1 -0
  123. package/dist/esm/decimal/format.d.ts +4 -1
  124. package/dist/esm/decimal/format.d.ts.map +1 -1
  125. package/dist/esm/decimal/format.js +71 -66
  126. package/dist/esm/decimal/format.js.map +1 -1
  127. package/dist/esm/decimal/parse.d.ts +26 -7
  128. package/dist/esm/decimal/parse.d.ts.map +1 -1
  129. package/dist/esm/decimal/parse.js +59 -33
  130. package/dist/esm/decimal/parse.js.map +1 -1
  131. package/dist/esm/index.d.ts +13 -10
  132. package/dist/esm/index.d.ts.map +1 -1
  133. package/dist/esm/index.js +9 -6
  134. package/dist/esm/index.js.map +1 -1
  135. package/dist/esm/math/fills.d.ts +6 -3
  136. package/dist/esm/math/fills.d.ts.map +1 -1
  137. package/dist/esm/math/fills.js +31 -16
  138. package/dist/esm/math/fills.js.map +1 -1
  139. package/dist/esm/math/order.d.ts +85 -65
  140. package/dist/esm/math/order.d.ts.map +1 -1
  141. package/dist/esm/math/order.js +158 -166
  142. package/dist/esm/math/order.js.map +1 -1
  143. package/dist/esm/math/position.d.ts +155 -98
  144. package/dist/esm/math/position.d.ts.map +1 -1
  145. package/dist/esm/math/position.js +157 -155
  146. package/dist/esm/math/position.js.map +1 -1
  147. package/dist/esm/services/getGasRecommendation.js +1 -1
  148. package/dist/esm/services/getGasRecommendation.js.map +1 -1
  149. package/dist/esm/services/getQuote.d.ts +2 -2
  150. package/dist/esm/services/getQuote.js +1 -1
  151. package/dist/esm/services/resolveSubscribeQuote.d.ts +1 -1
  152. package/dist/esm/services/resolveSubscribeQuote.d.ts.map +1 -1
  153. package/dist/esm/services/resolveSubscribeQuote.js +4 -3
  154. package/dist/esm/services/resolveSubscribeQuote.js.map +1 -1
  155. package/dist/esm/transport/request.d.ts +5 -0
  156. package/dist/esm/transport/request.d.ts.map +1 -1
  157. package/dist/esm/transport/request.js +2 -2
  158. package/dist/esm/transport/request.js.map +1 -1
  159. package/dist/esm/types/api.d.ts +9 -0
  160. package/dist/esm/types/api.d.ts.map +1 -1
  161. package/dist/esm/types/provider.d.ts +26 -13
  162. package/dist/esm/types/provider.d.ts.map +1 -1
  163. package/dist/esm/types/withdrawal.d.ts +32 -7
  164. package/dist/esm/types/withdrawal.d.ts.map +1 -1
  165. package/dist/esm/utils/createSafeFunction.d.ts +9 -0
  166. package/dist/esm/utils/createSafeFunction.d.ts.map +1 -0
  167. package/dist/esm/utils/createSafeFunction.js +19 -0
  168. package/dist/esm/utils/createSafeFunction.js.map +1 -0
  169. package/dist/esm/utils/timestampToIsoString.d.ts +15 -0
  170. package/dist/esm/utils/timestampToIsoString.d.ts.map +1 -0
  171. package/dist/esm/utils/timestampToIsoString.js +24 -0
  172. package/dist/esm/utils/timestampToIsoString.js.map +1 -0
  173. package/dist/esm/utils/warnOnce.d.ts +16 -0
  174. package/dist/esm/utils/warnOnce.d.ts.map +1 -1
  175. package/dist/esm/utils/warnOnce.js +24 -0
  176. package/dist/esm/utils/warnOnce.js.map +1 -1
  177. package/dist/esm/version.d.ts +1 -1
  178. package/dist/esm/version.js +1 -1
  179. package/dist/esm/websocket/PerpsWsClient.d.ts +7 -0
  180. package/dist/esm/websocket/PerpsWsClient.d.ts.map +1 -1
  181. package/dist/esm/websocket/PerpsWsClient.js +20 -2
  182. package/dist/esm/websocket/PerpsWsClient.js.map +1 -1
  183. package/dist/esm/websocket/wsLog.d.ts +12 -0
  184. package/dist/esm/websocket/wsLog.d.ts.map +1 -1
  185. package/dist/esm/websocket/wsLog.js +18 -0
  186. package/dist/esm/websocket/wsLog.js.map +1 -1
  187. package/dist/esm/wire/orderAmounts.d.ts.map +1 -1
  188. package/dist/esm/wire/orderAmounts.js +2 -2
  189. package/dist/esm/wire/orderAmounts.js.map +1 -1
  190. package/dist/esm/wire/refuel.d.ts +24 -14
  191. package/dist/esm/wire/refuel.d.ts.map +1 -1
  192. package/dist/esm/wire/refuel.js +38 -17
  193. package/dist/esm/wire/refuel.js.map +1 -1
  194. package/dist/esm/wire/transferable.js +3 -3
  195. package/dist/esm/wire/transferable.js.map +1 -1
  196. package/dist/esm/wire/withdraw.d.ts.map +1 -1
  197. package/dist/esm/wire/withdraw.js +3 -3
  198. package/dist/esm/wire/withdraw.js.map +1 -1
  199. package/dist/types/client/PerpsClient.d.ts +17 -4
  200. package/dist/types/client/PerpsClient.d.ts.map +1 -1
  201. package/dist/types/decimal/arithmetic.d.ts +84 -0
  202. package/dist/types/decimal/arithmetic.d.ts.map +1 -0
  203. package/dist/types/decimal/big.d.ts +0 -2
  204. package/dist/types/decimal/big.d.ts.map +1 -1
  205. package/dist/types/decimal/compare.d.ts +45 -0
  206. package/dist/types/decimal/compare.d.ts.map +1 -0
  207. package/dist/types/decimal/convert.d.ts +62 -26
  208. package/dist/types/decimal/convert.d.ts.map +1 -1
  209. package/dist/types/decimal/decimalStringToBig.d.ts +26 -0
  210. package/dist/types/decimal/decimalStringToBig.d.ts.map +1 -0
  211. package/dist/types/decimal/format.d.ts +4 -1
  212. package/dist/types/decimal/format.d.ts.map +1 -1
  213. package/dist/types/decimal/parse.d.ts +26 -7
  214. package/dist/types/decimal/parse.d.ts.map +1 -1
  215. package/dist/types/index.d.ts +13 -10
  216. package/dist/types/index.d.ts.map +1 -1
  217. package/dist/types/math/fills.d.ts +6 -3
  218. package/dist/types/math/fills.d.ts.map +1 -1
  219. package/dist/types/math/order.d.ts +85 -65
  220. package/dist/types/math/order.d.ts.map +1 -1
  221. package/dist/types/math/position.d.ts +155 -98
  222. package/dist/types/math/position.d.ts.map +1 -1
  223. package/dist/types/services/getQuote.d.ts +2 -2
  224. package/dist/types/services/resolveSubscribeQuote.d.ts +1 -1
  225. package/dist/types/services/resolveSubscribeQuote.d.ts.map +1 -1
  226. package/dist/types/transport/request.d.ts +5 -0
  227. package/dist/types/transport/request.d.ts.map +1 -1
  228. package/dist/types/types/api.d.ts +9 -0
  229. package/dist/types/types/api.d.ts.map +1 -1
  230. package/dist/types/types/provider.d.ts +26 -13
  231. package/dist/types/types/provider.d.ts.map +1 -1
  232. package/dist/types/types/withdrawal.d.ts +32 -7
  233. package/dist/types/types/withdrawal.d.ts.map +1 -1
  234. package/dist/types/utils/createSafeFunction.d.ts +9 -0
  235. package/dist/types/utils/createSafeFunction.d.ts.map +1 -0
  236. package/dist/types/utils/timestampToIsoString.d.ts +15 -0
  237. package/dist/types/utils/timestampToIsoString.d.ts.map +1 -0
  238. package/dist/types/utils/warnOnce.d.ts +16 -0
  239. package/dist/types/utils/warnOnce.d.ts.map +1 -1
  240. package/dist/types/version.d.ts +1 -1
  241. package/dist/types/websocket/PerpsWsClient.d.ts +7 -0
  242. package/dist/types/websocket/PerpsWsClient.d.ts.map +1 -1
  243. package/dist/types/websocket/wsLog.d.ts +12 -0
  244. package/dist/types/websocket/wsLog.d.ts.map +1 -1
  245. package/dist/types/wire/orderAmounts.d.ts.map +1 -1
  246. package/dist/types/wire/refuel.d.ts +24 -14
  247. package/dist/types/wire/refuel.d.ts.map +1 -1
  248. package/dist/types/wire/withdraw.d.ts.map +1 -1
  249. package/package.json +2 -2
  250. package/src/client/PerpsClient.ts +48 -10
  251. package/src/decimal/arithmetic.ts +144 -0
  252. package/src/decimal/big.ts +0 -5
  253. package/src/decimal/compare.ts +67 -0
  254. package/src/decimal/convert.ts +123 -44
  255. package/src/decimal/decimalStringToBig.ts +47 -0
  256. package/src/decimal/format.ts +81 -68
  257. package/src/decimal/parse.ts +73 -36
  258. package/src/index.ts +76 -8
  259. package/src/math/fills.ts +32 -17
  260. package/src/math/order.ts +261 -216
  261. package/src/math/position.ts +277 -194
  262. package/src/services/getGasRecommendation.ts +1 -1
  263. package/src/services/getQuote.ts +2 -2
  264. package/src/services/resolveSubscribeQuote.ts +4 -3
  265. package/src/transport/request.ts +7 -2
  266. package/src/types/api.ts +10 -0
  267. package/src/types/provider.ts +35 -13
  268. package/src/types/withdrawal.ts +35 -8
  269. package/src/utils/createSafeFunction.ts +20 -0
  270. package/src/utils/timestampToIsoString.ts +31 -0
  271. package/src/utils/warnOnce.ts +44 -0
  272. package/src/version.ts +1 -1
  273. package/src/websocket/PerpsWsClient.ts +25 -2
  274. package/src/websocket/wsLog.ts +31 -0
  275. package/src/wire/orderAmounts.ts +3 -2
  276. package/src/wire/refuel.ts +56 -20
  277. package/src/wire/transferable.ts +3 -3
  278. package/src/wire/withdraw.ts +3 -5
  279. package/dist/cjs/decimal/requireDecimal.d.ts +0 -4
  280. package/dist/cjs/decimal/requireDecimal.d.ts.map +0 -1
  281. package/dist/cjs/decimal/requireDecimal.js +0 -17
  282. package/dist/cjs/decimal/requireDecimal.js.map +0 -1
  283. package/dist/esm/decimal/requireDecimal.d.ts +0 -11
  284. package/dist/esm/decimal/requireDecimal.d.ts.map +0 -1
  285. package/dist/esm/decimal/requireDecimal.js +0 -18
  286. package/dist/esm/decimal/requireDecimal.js.map +0 -1
  287. package/dist/types/decimal/requireDecimal.d.ts +0 -11
  288. package/dist/types/decimal/requireDecimal.d.ts.map +0 -1
  289. package/src/decimal/requireDecimal.ts +0 -21
@@ -1,7 +1,9 @@
1
1
  /**
2
2
  * Display-tier position formulas and margin-adjustment predicates. Every
3
- * formula takes and gives `number`; exact decimal arithmetic happens
4
- * internally with `DivBig`.
3
+ * formula takes and gives decimal strings; arithmetic is exact Big.js, with
4
+ * division to 40 decimal places. Each formula throws `ValidationError` on an
5
+ * input that is not a decimal string and has a `safe*` pair that gives
6
+ * `undefined` in place of the throw.
5
7
  *
6
8
  * Sign convention: long = +1, short = -1. Sizes passed to the `estimate*`
7
9
  * helpers are always non-negative magnitudes; direction is carried by
@@ -10,318 +12,399 @@
10
12
 
11
13
  import {
12
14
  MarginMode,
15
+ PerpsErrorCode,
16
+ type PerpsMarket,
13
17
  type Position,
14
18
  PositionMarginAdjustment,
15
19
  } from '@lifi/perps-types'
16
- import { areFinite, DivBig } from '../decimal/big.js'
20
+ import { DivBig } from '../decimal/big.js'
21
+ import { numberToDecimalString } from '../decimal/convert.js'
22
+ import {
23
+ bigToDecimalString,
24
+ decimalStringToBig,
25
+ decimalStringToDivBig,
26
+ } from '../decimal/decimalStringToBig.js'
27
+ import { PerpsError } from '../errors/PerpsError.js'
28
+ import type { LiquidationEstimateParams } from '../types/provider.js'
29
+ import { createSafeFunction } from '../utils/createSafeFunction.js'
17
30
 
18
31
  /**
19
- * Calculate notional value of a position.
32
+ * Notional value of a position: `|size| × price`.
20
33
  *
21
- * @param size - Position size in asset units
22
- * @param price - Current asset price
23
- * @returns Notional value in USD
34
+ * @throws {PerpsError} `ValidationError` when an input is not a decimal string.
24
35
  * @public
25
36
  */
26
- export function calculateNotionalValue(size: number, price: number): number {
27
- if (!areFinite(size, price)) {
28
- return Number.NaN
29
- }
30
- return new DivBig(size).abs().times(price).toNumber()
37
+ export function calculateNotionalValue(size: string, price: string): string {
38
+ return bigToDecimalString(
39
+ decimalStringToBig(size).abs().times(decimalStringToBig(price))
40
+ )
31
41
  }
32
42
 
43
+ /** @public */
44
+ export const safeCalculateNotionalValue = createSafeFunction(
45
+ 'calculateNotionalValue',
46
+ calculateNotionalValue
47
+ )
48
+
33
49
  /**
34
- * Calculate unrealized PnL.
50
+ * Unrealized PnL: `(currentPrice - entryPrice) × size`, where `size` is
51
+ * positive for a long and negative for a short.
35
52
  *
36
- * @param entryPrice - Position entry price
37
- * @param currentPrice - Current market price
38
- * @param size - Position size (positive for long, negative for short)
39
- * @returns Unrealized PnL in USD
53
+ * @throws {PerpsError} `ValidationError` when an input is not a decimal string.
40
54
  * @public
41
55
  */
42
56
  export function calculateUnrealizedPnl(
43
- entryPrice: number,
44
- currentPrice: number,
45
- size: number
46
- ): number {
47
- if (!areFinite(entryPrice, currentPrice, size)) {
48
- return Number.NaN
49
- }
50
- return new DivBig(currentPrice).minus(entryPrice).times(size).toNumber()
57
+ entryPrice: string,
58
+ currentPrice: string,
59
+ size: string
60
+ ): string {
61
+ return bigToDecimalString(
62
+ decimalStringToBig(currentPrice)
63
+ .minus(decimalStringToBig(entryPrice))
64
+ .times(decimalStringToBig(size))
65
+ )
51
66
  }
52
67
 
68
+ /** @public */
69
+ export const safeCalculateUnrealizedPnl = createSafeFunction(
70
+ 'calculateUnrealizedPnl',
71
+ calculateUnrealizedPnl
72
+ )
73
+
53
74
  /**
54
- * Calculate return on equity (ROE) percentage.
75
+ * Return on equity as a percentage (`'10'` for 10%): `pnl ÷ margin × 100`.
76
+ * A zero margin gives `'0'`.
55
77
  *
56
- * @param pnl - Profit/loss in USD
57
- * @param margin - Initial margin in USD
58
- * @returns ROE as percentage (e.g., 10 for 10%)
78
+ * @throws {PerpsError} `ValidationError` when an input is not a decimal string.
59
79
  * @public
60
80
  */
61
- export function calculateRoe(pnl: number, margin: number): number {
62
- if (margin === 0) {
63
- return 0
81
+ export function calculateRoe(pnl: string, margin: string): string {
82
+ const marginBig = decimalStringToBig(margin)
83
+ const pnlBig = decimalStringToDivBig(pnl)
84
+ if (marginBig.eq(0)) {
85
+ return '0'
64
86
  }
65
- if (!areFinite(pnl, margin)) {
66
- return Number.NaN
67
- }
68
- return new DivBig(pnl).div(margin).times(100).toNumber()
87
+ return bigToDecimalString(pnlBig.div(marginBig).times(100))
69
88
  }
70
89
 
90
+ /** @public */
91
+ export const safeCalculateRoe = createSafeFunction('calculateRoe', calculateRoe)
92
+
71
93
  /**
72
- * Calculate required margin for a position.
94
+ * Required margin: `notionalValue ÷ leverage`.
73
95
  *
74
- * @param notionalValue - Position notional value in USD
75
- * @param leverage - Position leverage
76
- * @returns Required margin in USD
96
+ * @throws {PerpsError} `ValidationError` when an input is not a decimal string
97
+ * or `leverage` is zero.
77
98
  * @public
78
99
  */
79
100
  export function calculateRequiredMargin(
80
- notionalValue: number,
81
- leverage: number
82
- ): number {
83
- if (leverage === 0) {
84
- return notionalValue / leverage
85
- }
86
- if (!areFinite(notionalValue, leverage)) {
87
- return Number.NaN
101
+ notionalValue: string,
102
+ leverage: string
103
+ ): string {
104
+ const notionalBig = decimalStringToDivBig(notionalValue)
105
+ const leverageBig = decimalStringToBig(leverage)
106
+ if (leverageBig.eq(0)) {
107
+ throw new PerpsError(
108
+ PerpsErrorCode.ValidationError,
109
+ 'Leverage must not be zero.'
110
+ )
88
111
  }
89
- return new DivBig(notionalValue).div(leverage).toNumber()
112
+ return bigToDecimalString(notionalBig.div(leverageBig))
90
113
  }
91
114
 
115
+ /** @public */
116
+ export const safeCalculateRequiredMargin = createSafeFunction(
117
+ 'calculateRequiredMargin',
118
+ calculateRequiredMargin
119
+ )
120
+
92
121
  /**
93
- * Distance from the current price to the liquidation price, as a percentage of
94
- * the current price.
122
+ * Distance from the current price to the liquidation price, as an absolute
123
+ * percentage of the current price. A zero current price gives `'0'`.
95
124
  *
96
- * @param liquidationPrice - The position's liquidation price
97
- * @param currentPrice - Current market price
98
- * @returns Absolute distance as a percentage, or 0 when current price is zero
125
+ * @throws {PerpsError} `ValidationError` when an input is not a decimal string.
99
126
  * @public
100
127
  */
101
128
  export function calculateLiquidationDistance(params: {
102
- liquidationPrice: number
103
- currentPrice: number
104
- }): number {
105
- const { liquidationPrice, currentPrice } = params
106
- if (currentPrice === 0) {
107
- return 0
129
+ liquidationPrice: string
130
+ currentPrice: string
131
+ }): string {
132
+ const liquidationPrice = decimalStringToDivBig(params.liquidationPrice)
133
+ const currentPrice = decimalStringToBig(params.currentPrice)
134
+ if (currentPrice.eq(0)) {
135
+ return '0'
108
136
  }
109
- return Math.abs((liquidationPrice - currentPrice) / currentPrice) * 100
137
+ return bigToDecimalString(
138
+ liquidationPrice.minus(currentPrice).div(currentPrice).abs().times(100)
139
+ )
110
140
  }
111
141
 
142
+ /** @public */
143
+ export const safeCalculateLiquidationDistance = createSafeFunction(
144
+ 'calculateLiquidationDistance',
145
+ calculateLiquidationDistance
146
+ )
147
+
112
148
  /**
113
- * Effective leverage of an open position.
149
+ * Effective leverage of an open position: `positionValueUsd ÷ marginUsd`.
150
+ * A zero margin gives `'0'`.
114
151
  *
115
- * leverage = positionValueUsd / marginUsd
116
- *
117
- * @param positionValueUsd - Position notional value in USD
118
- * @param marginUsd - Margin backing the position in USD
119
- * @returns Effective leverage, or 0 when margin is zero
152
+ * @throws {PerpsError} `ValidationError` when an input is not a decimal string.
120
153
  * @public
121
154
  */
122
155
  export function calculateEffectiveLeverage(params: {
123
- positionValueUsd: number
124
- marginUsd: number
125
- }): number {
126
- const { positionValueUsd, marginUsd } = params
127
- if (marginUsd === 0) {
128
- return 0
129
- }
130
- if (!areFinite(positionValueUsd, marginUsd)) {
131
- return Number.NaN
156
+ positionValueUsd: string
157
+ marginUsd: string
158
+ }): string {
159
+ const positionValue = decimalStringToDivBig(params.positionValueUsd)
160
+ const margin = decimalStringToBig(params.marginUsd)
161
+ if (margin.eq(0)) {
162
+ return '0'
132
163
  }
133
- return new DivBig(positionValueUsd).div(marginUsd).toNumber()
164
+ return bigToDecimalString(positionValue.div(margin))
134
165
  }
135
166
 
167
+ /** @public */
168
+ export const safeCalculateEffectiveLeverage = createSafeFunction(
169
+ 'calculateEffectiveLeverage',
170
+ calculateEffectiveLeverage
171
+ )
172
+
136
173
  /**
137
174
  * Estimated liquidation price for an isolated-margin position, parameterised
138
175
  * by the venue's maintenance margin rate.
139
176
  *
140
177
  * Standard isolated-margin model (new position, no existing exposure):
141
- * margin_per_unit = entryPrice / leverage
142
- * maintenance_per_unit = entryPrice * mmr
143
- * margin_available = margin_per_unit - maintenance_per_unit
144
- * liq_price = entryPrice - side * margin_available / (1 - mmr * side)
178
+ * margin_available = entryPrice × (1 / leverage - mmr)
179
+ * liq_price = entryPrice - side × margin_available / (1 - mmr × side)
145
180
  *
146
181
  * For existing positions, prefer `Position.liquidationPrice` from the venue.
147
182
  *
148
183
  * @param maintenanceMarginRate - Venue maintenance margin rate as a fraction
149
- * (e.g. 0.01 = 1%).
150
- * @returns Estimated liquidation price, or undefined if the inputs cannot
151
- * produce one (zero leverage, degenerate denominator).
184
+ * (`'0.01'` = 1%).
185
+ * @returns The estimate, or `undefined` for a zero leverage or a zero
186
+ * denominator.
187
+ * @throws {PerpsError} `ValidationError` when an input is not a decimal string.
152
188
  * @public
153
189
  */
154
190
  export function estimateLiquidationPrice(params: {
155
- entryPrice: number
156
- leverage: number
191
+ entryPrice: string
192
+ leverage: string
157
193
  isLong: boolean
158
- maintenanceMarginRate: number
159
- }): number | undefined {
160
- const { entryPrice, leverage, isLong, maintenanceMarginRate } = params
161
- if (leverage === 0) {
194
+ maintenanceMarginRate: string
195
+ }): string | undefined {
196
+ const entryPrice = decimalStringToBig(params.entryPrice)
197
+ const leverage = decimalStringToBig(params.leverage)
198
+ const mmr = decimalStringToBig(params.maintenanceMarginRate)
199
+ const side = params.isLong ? 1 : -1
200
+ const denominator = mmr.times(-side).plus(1)
201
+ if (leverage.eq(0) || denominator.eq(0)) {
162
202
  return undefined
163
203
  }
164
- const side = isLong ? 1 : -1
165
- const denominator = 1 - maintenanceMarginRate * side
166
- if (denominator === 0) {
167
- return undefined
168
- }
169
- if (!areFinite(entryPrice, leverage, maintenanceMarginRate)) {
170
- return Number.NaN
171
- }
172
- const mmr = new DivBig(maintenanceMarginRate)
173
204
  const marginAvailable = new DivBig(1)
174
205
  .div(leverage)
175
206
  .minus(mmr)
176
207
  .times(entryPrice)
177
- return new DivBig(entryPrice)
178
- .minus(marginAvailable.times(side).div(mmr.times(-side).plus(1)))
179
- .toNumber()
208
+ return bigToDecimalString(
209
+ entryPrice.minus(marginAvailable.times(side).div(denominator))
210
+ )
211
+ }
212
+
213
+ /** @public */
214
+ export const safeEstimateLiquidationPrice = createSafeFunction(
215
+ 'estimateLiquidationPrice',
216
+ estimateLiquidationPrice
217
+ )
218
+
219
+ /**
220
+ * Estimate the liquidation price of a new isolated position from the flat
221
+ * `market.maintenanceMarginRate`.
222
+ *
223
+ * @returns The estimate, or `undefined` when the market carries no
224
+ * `maintenanceMarginRate` or `estimateLiquidationPrice` gives `undefined`.
225
+ * @throws {PerpsError} `ValidationError` when an input is not a decimal string.
226
+ * @public
227
+ */
228
+ export function estimateLiquidationPriceAtMarketRate(
229
+ market: PerpsMarket,
230
+ params: LiquidationEstimateParams
231
+ ): string | undefined {
232
+ if (market.maintenanceMarginRate === undefined) {
233
+ return undefined
234
+ }
235
+ return estimateLiquidationPrice({
236
+ entryPrice: params.entryPrice,
237
+ leverage: params.leverage,
238
+ isLong: params.isLong,
239
+ maintenanceMarginRate: numberToDecimalString(market.maintenanceMarginRate),
240
+ })
180
241
  }
181
242
 
243
+ /** @public */
244
+ export const safeEstimateLiquidationPriceAtMarketRate = createSafeFunction(
245
+ 'estimateLiquidationPriceAtMarketRate',
246
+ estimateLiquidationPriceAtMarketRate
247
+ )
248
+
182
249
  /**
183
250
  * Whether an isolated position with this liquidation price is already past
184
251
  * liquidation at `currentPrice`: a long liquidates when the price falls to its
185
252
  * liquidation level, a short when it rises to it. A non-positive price on
186
253
  * either side counts as unknown, so the result is `false`.
187
254
  *
255
+ * @throws {PerpsError} `ValidationError` when a price is not a decimal string.
188
256
  * @public
189
257
  */
190
258
  export function wouldImmediatelyLiquidate(params: {
191
- liquidationPrice: number
192
- currentPrice: number
259
+ liquidationPrice: string
260
+ currentPrice: string
193
261
  isLong: boolean
194
262
  }): boolean {
195
- const { liquidationPrice, currentPrice, isLong } = params
196
- if (currentPrice <= 0 || liquidationPrice <= 0) {
263
+ const liquidationPrice = decimalStringToBig(params.liquidationPrice)
264
+ const currentPrice = decimalStringToBig(params.currentPrice)
265
+ if (currentPrice.lte(0) || liquidationPrice.lte(0)) {
197
266
  return false
198
267
  }
199
- return isLong
200
- ? liquidationPrice >= currentPrice
201
- : liquidationPrice <= currentPrice
268
+ return params.isLong
269
+ ? liquidationPrice.gte(currentPrice)
270
+ : liquidationPrice.lte(currentPrice)
202
271
  }
203
272
 
273
+ /** @public */
274
+ export const safeWouldImmediatelyLiquidate = createSafeFunction(
275
+ 'wouldImmediatelyLiquidate',
276
+ wouldImmediatelyLiquidate
277
+ )
278
+
204
279
  /**
205
- * Estimated average entry price after adding to an existing position.
206
- *
207
- * Weighted average of the current entry and the new fill price, weighted by
208
- * the size of each leg in coin units. Both legs assumed in the same direction
209
- * (this helper is for adding to, not flipping, a position).
280
+ * Estimated average entry price after adding to an existing position: the
281
+ * size-weighted average of the current entry and the fill price. Both legs
282
+ * are in the same direction (an add, not a flip).
210
283
  *
211
- * @param currentSize - Existing position size in coin units (>= 0).
212
- * @param currentEntry - Existing position's average entry price.
213
- * @param addSize - Size being added in coin units (>= 0).
214
- * @param fillPrice - Price the new size is expected to fill at (mid for
215
- * market, limitPrice for limit orders).
216
- * @returns The new weighted-average entry price, or undefined if the inputs
217
- * cannot produce a valid average (zero combined size, non-finite values).
284
+ * @param fillPrice - Expected fill price (mid for market, limit price for
285
+ * limit orders).
286
+ * @returns The new average entry price, or `undefined` when the combined size
287
+ * is not positive.
288
+ * @throws {PerpsError} `ValidationError` when an input is not a decimal string.
218
289
  * @public
219
290
  */
220
291
  export function estimateAverageEntryPrice(params: {
221
- currentSize: number
222
- currentEntry: number
223
- addSize: number
224
- fillPrice: number
225
- }): number | undefined {
226
- const { currentSize, currentEntry, addSize, fillPrice } = params
227
- const totalSize = currentSize + addSize
228
- if (totalSize <= 0) {
292
+ currentSize: string
293
+ currentEntry: string
294
+ addSize: string
295
+ fillPrice: string
296
+ }): string | undefined {
297
+ const currentSize = decimalStringToDivBig(params.currentSize)
298
+ const addSize = decimalStringToBig(params.addSize)
299
+ const currentEntry = decimalStringToBig(params.currentEntry)
300
+ const fillPrice = decimalStringToBig(params.fillPrice)
301
+ const totalSize = currentSize.plus(addSize)
302
+ if (totalSize.lte(0)) {
229
303
  return undefined
230
304
  }
231
- if (!Number.isFinite(currentEntry) || !Number.isFinite(fillPrice)) {
232
- return undefined
233
- }
234
- if (!areFinite(currentSize, addSize)) {
235
- return Number.NaN
236
- }
237
- return new DivBig(currentSize)
238
- .times(currentEntry)
239
- .plus(new DivBig(addSize).times(fillPrice))
240
- .div(new DivBig(currentSize).plus(addSize))
241
- .toNumber()
305
+ return bigToDecimalString(
306
+ currentSize
307
+ .times(currentEntry)
308
+ .plus(addSize.times(fillPrice))
309
+ .div(totalSize)
310
+ )
242
311
  }
243
312
 
313
+ /** @public */
314
+ export const safeEstimateAverageEntryPrice = createSafeFunction(
315
+ 'estimateAverageEntryPrice',
316
+ estimateAverageEntryPrice
317
+ )
318
+
244
319
  /**
245
- * Estimated effective leverage after adding margin and notional.
246
- *
247
- * leverage = totalNotional / totalMargin. The caller computes notional from
248
- * size and price (`calculateNotionalValue`) and supplies the additional
249
- * margin the user is about to put up.
320
+ * Estimated effective leverage after adding margin and notional:
321
+ * `(currentNotional + addNotional) ÷ (currentMargin + addMargin)`.
250
322
  *
251
- * @returns The new effective leverage, or undefined if total margin is
252
- * non-positive.
323
+ * @returns The new leverage, or `undefined` when the total margin is not
324
+ * positive.
325
+ * @throws {PerpsError} `ValidationError` when an input is not a decimal string.
253
326
  * @public
254
327
  */
255
328
  export function estimateNewLeverage(params: {
256
- currentNotional: number
257
- currentMargin: number
258
- addNotional: number
259
- addMargin: number
260
- }): number | undefined {
261
- const { currentNotional, currentMargin, addNotional, addMargin } = params
262
- const totalMargin = currentMargin + addMargin
263
- if (totalMargin <= 0) {
329
+ currentNotional: string
330
+ currentMargin: string
331
+ addNotional: string
332
+ addMargin: string
333
+ }): string | undefined {
334
+ const totalNotional = decimalStringToDivBig(params.currentNotional).plus(
335
+ decimalStringToBig(params.addNotional)
336
+ )
337
+ const totalMargin = decimalStringToBig(params.currentMargin).plus(
338
+ decimalStringToBig(params.addMargin)
339
+ )
340
+ if (totalMargin.lte(0)) {
264
341
  return undefined
265
342
  }
266
- if (!areFinite(currentNotional, currentMargin, addNotional, addMargin)) {
267
- return Number.NaN
268
- }
269
- return new DivBig(currentNotional)
270
- .plus(addNotional)
271
- .div(new DivBig(currentMargin).plus(addMargin))
272
- .toNumber()
343
+ return bigToDecimalString(totalNotional.div(totalMargin))
273
344
  }
274
345
 
346
+ /** @public */
347
+ export const safeEstimateNewLeverage = createSafeFunction(
348
+ 'estimateNewLeverage',
349
+ estimateNewLeverage
350
+ )
351
+
275
352
  /**
276
- * Estimated unrealised PnL at the current mark price.
277
- *
278
- * `pnl = (markPrice - entryPrice) * size * (isLong ? 1 : -1)`
353
+ * Estimated unrealised PnL at the mark price:
354
+ * `(markPrice - entryPrice) × size × (isLong ? 1 : -1)`.
279
355
  *
280
356
  * @param size - Position size as a non-negative magnitude.
357
+ * @throws {PerpsError} `ValidationError` when an input is not a decimal string.
281
358
  * @public
282
359
  */
283
360
  export function estimateUnrealizedPnl(params: {
284
- entryPrice: number
285
- markPrice: number
286
- size: number
361
+ entryPrice: string
362
+ markPrice: string
363
+ size: string
287
364
  isLong: boolean
288
- }): number {
289
- const { entryPrice, markPrice, size, isLong } = params
290
- if (!areFinite(entryPrice, markPrice, size)) {
291
- return Number.NaN
292
- }
293
- return new DivBig(markPrice)
294
- .minus(entryPrice)
295
- .times(size)
296
- .times(isLong ? 1 : -1)
297
- .toNumber()
365
+ }): string {
366
+ return bigToDecimalString(
367
+ decimalStringToBig(params.markPrice)
368
+ .minus(decimalStringToBig(params.entryPrice))
369
+ .times(decimalStringToBig(params.size))
370
+ .times(params.isLong ? 1 : -1)
371
+ )
298
372
  }
299
373
 
374
+ /** @public */
375
+ export const safeEstimateUnrealizedPnl = createSafeFunction(
376
+ 'estimateUnrealizedPnl',
377
+ estimateUnrealizedPnl
378
+ )
379
+
300
380
  /**
301
- * Realised PnL on the portion of a position being closed.
302
- *
303
- * `rPnl = (closePrice - entryPrice) * closeSize * (isLong ? 1 : -1)`
381
+ * Realised PnL on the portion of a position being closed:
382
+ * `(closePrice - entryPrice) × closeSize × (isLong ? 1 : -1)`.
304
383
  *
305
384
  * @param closeSize - Size being closed as a non-negative magnitude.
385
+ * @throws {PerpsError} `ValidationError` when an input is not a decimal string.
306
386
  * @public
307
387
  */
308
388
  export function calculateRealizedPnl(params: {
309
- entryPrice: number
310
- closePrice: number
311
- closeSize: number
389
+ entryPrice: string
390
+ closePrice: string
391
+ closeSize: string
312
392
  isLong: boolean
313
- }): number {
314
- const { entryPrice, closePrice, closeSize, isLong } = params
315
- if (!areFinite(entryPrice, closePrice, closeSize)) {
316
- return Number.NaN
317
- }
318
- return new DivBig(closePrice)
319
- .minus(entryPrice)
320
- .times(closeSize)
321
- .times(isLong ? 1 : -1)
322
- .toNumber()
393
+ }): string {
394
+ return bigToDecimalString(
395
+ decimalStringToBig(params.closePrice)
396
+ .minus(decimalStringToBig(params.entryPrice))
397
+ .times(decimalStringToBig(params.closeSize))
398
+ .times(params.isLong ? 1 : -1)
399
+ )
323
400
  }
324
401
 
402
+ /** @public */
403
+ export const safeCalculateRealizedPnl = createSafeFunction(
404
+ 'calculateRealizedPnl',
405
+ calculateRealizedPnl
406
+ )
407
+
325
408
  /**
326
409
  * Whether this position can take a margin adjustment at all: it holds margin of
327
410
  * its own and its market exposes individual position margin.
@@ -39,7 +39,7 @@ export async function getGasRecommendation(
39
39
  return request<GasRecommendationResponse>(
40
40
  client.config,
41
41
  `${LIFI_API_URL}/gas/suggestion/${params.chainId}`,
42
- {},
42
+ { perpsSdkHeader: false },
43
43
  options
44
44
  )
45
45
  }
@@ -15,7 +15,7 @@ export interface GetQuoteParams {
15
15
  /** Buy consumes asks; sell consumes bids. */
16
16
  side: QuoteSide
17
17
  /** USD notional to fill. */
18
- size: number
18
+ size: string
19
19
  /** Product family used to distinguish spot and perpetual markets. */
20
20
  type: TradeType
21
21
  }
@@ -36,7 +36,7 @@ export interface GetQuoteParams {
36
36
  * provider: 'hyperliquid',
37
37
  * symbol: 'BTC',
38
38
  * side: 'buy',
39
- * size: 10_000,
39
+ * size: '10000',
40
40
  * type: 'perps',
41
41
  * })
42
42
  * console.log(quote.expectedFillPrice, quote.priceImpactBps)
@@ -4,6 +4,7 @@ import {
4
4
  type OrderbookResponse,
5
5
  PerpsErrorCode,
6
6
  } from '@lifi/perps-types'
7
+ import { isDecimalString } from '../decimal/parse.js'
7
8
  import { PerpsError } from '../errors/PerpsError.js'
8
9
  import { buildQuote } from '../math/order.js'
9
10
  import type {
@@ -44,7 +45,7 @@ export const QUOTE_THROTTLE_MS = 100
44
45
  * price, a throwing `onQuote`) is logged via `wsLog.listenerFailure` and the
45
46
  * emission is skipped, on both the immediate and the trailing-timer path.
46
47
  *
47
- * @throws {PerpsError} `ValidationError` when `params.size` is not finite.
48
+ * @throws {PerpsError} `ValidationError` when `params.size` is not a decimal string.
48
49
  * @throws {PerpsError} `MarketNotFound` when no market matches symbol+type.
49
50
  * @internal
50
51
  */
@@ -56,10 +57,10 @@ export async function resolveSubscribeQuote(
56
57
  feeTier: FeeTier,
57
58
  onQuote: QuoteListener
58
59
  ): Promise<() => void> {
59
- if (!Number.isFinite(params.size)) {
60
+ if (!isDecimalString(params.size)) {
60
61
  throw new PerpsError(
61
62
  PerpsErrorCode.ValidationError,
62
- `Quote size must be a finite number, got ${params.size}`
63
+ `Quote size must be a decimal string, got '${params.size}'`
63
64
  )
64
65
  }
65
66
  const market = await resolveQuoteMarket(client, provider, params)