@lifi/perps-sdk-provider-lighter 24.0.2 → 26.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.
- package/README.md +55 -0
- package/dist/cjs/LighterProvider.d.ts.map +1 -1
- package/dist/cjs/LighterProvider.js +71 -56
- package/dist/cjs/LighterProvider.js.map +1 -1
- package/dist/cjs/accountConfig.d.ts.map +1 -1
- package/dist/cjs/accountConfig.js +1 -7
- package/dist/cjs/accountConfig.js.map +1 -1
- package/dist/cjs/constants.d.ts +2 -0
- package/dist/cjs/constants.d.ts.map +1 -1
- package/dist/cjs/constants.js +3 -0
- package/dist/cjs/constants.js.map +1 -1
- package/dist/cjs/signers/LighterSigner.d.ts.map +1 -1
- package/dist/cjs/signers/LighterSigner.js +2 -1
- package/dist/cjs/signers/LighterSigner.js.map +1 -1
- package/dist/cjs/types/submit.d.ts.map +1 -1
- package/dist/cjs/utils/activityCursor.d.ts.map +1 -1
- package/dist/cjs/utils/activityCursor.js +29 -6
- package/dist/cjs/utils/activityCursor.js.map +1 -1
- package/dist/cjs/utils/index.d.ts +1 -1
- package/dist/cjs/utils/index.d.ts.map +1 -1
- package/dist/cjs/utils/index.js +2 -7
- package/dist/cjs/utils/index.js.map +1 -1
- package/dist/cjs/utils/mapFill.d.ts.map +1 -1
- package/dist/cjs/utils/mapFill.js +4 -2
- package/dist/cjs/utils/mapFill.js.map +1 -1
- package/dist/cjs/utils/mapOrder.d.ts +4 -11
- package/dist/cjs/utils/mapOrder.d.ts.map +1 -1
- package/dist/cjs/utils/mapOrder.js +112 -120
- package/dist/cjs/utils/mapOrder.js.map +1 -1
- package/dist/cjs/utils/txHash.d.ts +2 -0
- package/dist/cjs/utils/txHash.d.ts.map +1 -0
- package/dist/cjs/utils/txHash.js +10 -0
- package/dist/cjs/utils/txHash.js.map +1 -0
- package/dist/cjs/websocket/LighterWsProvider.d.ts.map +1 -1
- package/dist/cjs/websocket/LighterWsProvider.js +11 -9
- package/dist/cjs/websocket/LighterWsProvider.js.map +1 -1
- package/dist/esm/LighterProvider.d.ts.map +1 -1
- package/dist/esm/LighterProvider.js +78 -65
- package/dist/esm/LighterProvider.js.map +1 -1
- package/dist/esm/accountConfig.d.ts.map +1 -1
- package/dist/esm/accountConfig.js +5 -16
- package/dist/esm/accountConfig.js.map +1 -1
- package/dist/esm/constants.d.ts +9 -3
- package/dist/esm/constants.d.ts.map +1 -1
- package/dist/esm/constants.js +6 -3
- package/dist/esm/constants.js.map +1 -1
- package/dist/esm/signers/LighterSigner.d.ts.map +1 -1
- package/dist/esm/signers/LighterSigner.js +2 -1
- package/dist/esm/signers/LighterSigner.js.map +1 -1
- package/dist/esm/types/submit.d.ts +16 -6
- package/dist/esm/types/submit.d.ts.map +1 -1
- package/dist/esm/types/ws.d.ts +1 -1
- package/dist/esm/utils/activityCursor.d.ts +2 -11
- package/dist/esm/utils/activityCursor.d.ts.map +1 -1
- package/dist/esm/utils/activityCursor.js +35 -7
- package/dist/esm/utils/activityCursor.js.map +1 -1
- package/dist/esm/utils/index.d.ts +1 -1
- package/dist/esm/utils/index.d.ts.map +1 -1
- package/dist/esm/utils/index.js +1 -1
- package/dist/esm/utils/index.js.map +1 -1
- package/dist/esm/utils/mapFill.d.ts.map +1 -1
- package/dist/esm/utils/mapFill.js +5 -3
- package/dist/esm/utils/mapFill.js.map +1 -1
- package/dist/esm/utils/mapOrder.d.ts +6 -68
- package/dist/esm/utils/mapOrder.d.ts.map +1 -1
- package/dist/esm/utils/mapOrder.js +111 -180
- package/dist/esm/utils/mapOrder.js.map +1 -1
- package/dist/esm/utils/txHash.d.ts +8 -0
- package/dist/esm/utils/txHash.d.ts.map +1 -0
- package/dist/esm/utils/txHash.js +18 -0
- package/dist/esm/utils/txHash.js.map +1 -0
- package/dist/esm/websocket/LighterWsProvider.d.ts.map +1 -1
- package/dist/esm/websocket/LighterWsProvider.js +20 -16
- package/dist/esm/websocket/LighterWsProvider.js.map +1 -1
- package/dist/types/LighterProvider.d.ts.map +1 -1
- package/dist/types/accountConfig.d.ts.map +1 -1
- package/dist/types/constants.d.ts +9 -3
- package/dist/types/constants.d.ts.map +1 -1
- package/dist/types/signers/LighterSigner.d.ts.map +1 -1
- package/dist/types/types/submit.d.ts +16 -6
- package/dist/types/types/submit.d.ts.map +1 -1
- package/dist/types/types/ws.d.ts +1 -1
- package/dist/types/utils/activityCursor.d.ts +2 -11
- package/dist/types/utils/activityCursor.d.ts.map +1 -1
- package/dist/types/utils/index.d.ts +1 -1
- package/dist/types/utils/index.d.ts.map +1 -1
- package/dist/types/utils/mapFill.d.ts.map +1 -1
- package/dist/types/utils/mapOrder.d.ts +6 -68
- package/dist/types/utils/mapOrder.d.ts.map +1 -1
- package/dist/types/utils/txHash.d.ts +8 -0
- package/dist/types/utils/txHash.d.ts.map +1 -0
- package/dist/types/websocket/LighterWsProvider.d.ts.map +1 -1
- package/dist/wasm/lighter-signer.wasm +0 -0
- package/package.json +3 -3
- package/src/LighterProvider.ts +87 -98
- package/src/accountConfig.ts +5 -18
- package/src/constants.ts +11 -3
- package/src/signers/LighterSigner.ts +2 -0
- package/src/types/submit.ts +16 -6
- package/src/types/ws.ts +1 -1
- package/src/utils/activityCursor.ts +50 -18
- package/src/utils/index.ts +1 -9
- package/src/utils/mapFill.ts +5 -8
- package/src/utils/mapOrder.ts +130 -207
- package/src/utils/txHash.ts +20 -0
- package/src/websocket/LighterWsProvider.ts +22 -17
- package/dist/cjs/utils/mapTwap.d.ts +0 -4
- package/dist/cjs/utils/mapTwap.d.ts.map +0 -1
- package/dist/cjs/utils/mapTwap.js +0 -31
- package/dist/cjs/utils/mapTwap.js.map +0 -1
- package/dist/esm/utils/mapTwap.d.ts +0 -5
- package/dist/esm/utils/mapTwap.d.ts.map +0 -1
- package/dist/esm/utils/mapTwap.js +0 -25
- package/dist/esm/utils/mapTwap.js.map +0 -1
- package/dist/types/utils/mapTwap.d.ts +0 -5
- package/dist/types/utils/mapTwap.d.ts.map +0 -1
- package/src/utils/mapTwap.ts +0 -31
package/src/utils/mapOrder.ts
CHANGED
|
@@ -1,35 +1,46 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
} from '@lifi/perps-types'
|
|
1
|
+
import {
|
|
2
|
+
isActiveOrderStatus,
|
|
3
|
+
PerpsError,
|
|
4
|
+
triggerConditionFor,
|
|
5
|
+
} from '@lifi/perps-sdk'
|
|
6
|
+
import type { MarketDisplay, Order, OrderBase } from '@lifi/perps-types'
|
|
8
7
|
import {
|
|
9
8
|
OrderSide,
|
|
10
9
|
OrderStatus,
|
|
11
10
|
OrderType,
|
|
11
|
+
PerpsErrorCode,
|
|
12
12
|
TimeInForce,
|
|
13
13
|
} from '@lifi/perps-types'
|
|
14
|
+
import Big from 'big.js'
|
|
14
15
|
import type { LtOrder } from '../types/index.js'
|
|
15
16
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
stop_loss:
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
17
|
+
const mapOrderType = (type: string): Order['type'] => {
|
|
18
|
+
switch (type.replace(/-/g, '_')) {
|
|
19
|
+
case 'market':
|
|
20
|
+
case 'liquidation':
|
|
21
|
+
return OrderType.MARKET
|
|
22
|
+
case 'limit':
|
|
23
|
+
case 'twap_sub':
|
|
24
|
+
return OrderType.LIMIT
|
|
25
|
+
case 'stop_loss':
|
|
26
|
+
return OrderType.STOP_MARKET
|
|
27
|
+
case 'stop_loss_limit':
|
|
28
|
+
return OrderType.STOP_LIMIT
|
|
29
|
+
case 'take_profit':
|
|
30
|
+
return OrderType.TAKE_PROFIT_MARKET
|
|
31
|
+
case 'take_profit_limit':
|
|
32
|
+
return OrderType.TAKE_PROFIT_LIMIT
|
|
33
|
+
case 'twap':
|
|
34
|
+
return OrderType.TWAP
|
|
35
|
+
default:
|
|
36
|
+
throw new PerpsError(
|
|
37
|
+
PerpsErrorCode.SDKError,
|
|
38
|
+
`Unknown Lighter order type: ${type}`
|
|
39
|
+
)
|
|
28
40
|
}
|
|
29
|
-
return map[normalized] ?? OrderType.LIMIT
|
|
30
41
|
}
|
|
31
42
|
|
|
32
|
-
const mapTimeInForce = (tif: string): TimeInForce
|
|
43
|
+
const mapTimeInForce = (tif: string): TimeInForce => {
|
|
33
44
|
switch (tif.replace(/-/g, '_')) {
|
|
34
45
|
case 'good_till_time':
|
|
35
46
|
return TimeInForce.GTT
|
|
@@ -38,222 +49,134 @@ const mapTimeInForce = (tif: string): TimeInForce | undefined => {
|
|
|
38
49
|
case 'post_only':
|
|
39
50
|
return TimeInForce.POST_ONLY
|
|
40
51
|
default:
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
const mapOrderStatus = (status: string): OrderStatus => {
|
|
46
|
-
if (
|
|
47
|
-
status === 'open' ||
|
|
48
|
-
status === 'in-progress' ||
|
|
49
|
-
status === 'in_progress'
|
|
50
|
-
) {
|
|
51
|
-
return OrderStatus.OPEN
|
|
52
|
+
throw new PerpsError(
|
|
53
|
+
PerpsErrorCode.SDKError,
|
|
54
|
+
`Unknown Lighter time in force: ${tif}`
|
|
55
|
+
)
|
|
52
56
|
}
|
|
53
|
-
if (status === 'pending') {
|
|
54
|
-
return OrderStatus.PENDING
|
|
55
|
-
}
|
|
56
|
-
if (status === 'filled') {
|
|
57
|
-
return OrderStatus.FILLED
|
|
58
|
-
}
|
|
59
|
-
if (status.startsWith('canceled')) {
|
|
60
|
-
return OrderStatus.CANCELLED
|
|
61
|
-
}
|
|
62
|
-
return OrderStatus.OPEN
|
|
63
57
|
}
|
|
64
58
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
59
|
+
const mapOrderStatus = (
|
|
60
|
+
order: LtOrder,
|
|
61
|
+
filledSize = new Big(order.filled_base_amount)
|
|
62
|
+
): OrderStatus => {
|
|
63
|
+
switch (order.status) {
|
|
64
|
+
case 'pending':
|
|
65
|
+
case 'in-progress':
|
|
66
|
+
return OrderStatus.ACCEPTED
|
|
67
|
+
case 'open':
|
|
68
|
+
if (order.trigger_status === 'parent-order') {
|
|
69
|
+
return OrderStatus.PENDING
|
|
70
|
+
}
|
|
71
|
+
return filledSize.gt(0) ? OrderStatus.PARTIALLY_FILLED : OrderStatus.OPEN
|
|
72
|
+
case 'triggered':
|
|
73
|
+
return OrderStatus.TRIGGERED
|
|
74
|
+
case 'filled':
|
|
75
|
+
return OrderStatus.FILLED
|
|
76
|
+
case 'canceled-expired':
|
|
77
|
+
return OrderStatus.EXPIRED
|
|
73
78
|
case 'canceled':
|
|
74
|
-
return 'Order cancelled.'
|
|
75
79
|
case 'canceled-post-only':
|
|
76
|
-
return 'Order cancelled: post-only order would have crossed the book.'
|
|
77
80
|
case 'canceled-reduce-only':
|
|
78
|
-
return 'Order cancelled: would not reduce your position.'
|
|
79
81
|
case 'canceled-position-not-allowed':
|
|
80
|
-
return 'Order cancelled: position not allowed.'
|
|
81
82
|
case 'canceled-margin-not-allowed':
|
|
82
|
-
return 'Order cancelled: insufficient margin.'
|
|
83
83
|
case 'canceled-too-much-slippage':
|
|
84
|
-
return 'Order cancelled: slippage exceeded tolerance.'
|
|
85
84
|
case 'canceled-not-enough-liquidity':
|
|
86
|
-
return 'Order cancelled: not enough liquidity to fill.'
|
|
87
85
|
case 'canceled-self-trade':
|
|
88
|
-
return 'Order cancelled: would self-trade against your own resting order.'
|
|
89
|
-
case 'canceled-expired':
|
|
90
|
-
return 'Order expired.'
|
|
91
86
|
case 'canceled-oco':
|
|
92
|
-
return 'Order cancelled: sibling OCO order filled or cancelled first.'
|
|
93
87
|
case 'canceled-child':
|
|
94
|
-
return 'Order cancelled: parent order was cancelled.'
|
|
95
88
|
case 'canceled-liquidation':
|
|
96
|
-
return 'Order cancelled: account was liquidated.'
|
|
97
89
|
case 'canceled-invalid-balance':
|
|
98
|
-
return
|
|
90
|
+
return OrderStatus.CANCELLED
|
|
99
91
|
default:
|
|
100
|
-
|
|
92
|
+
throw new PerpsError(
|
|
93
|
+
PerpsErrorCode.SDKError,
|
|
94
|
+
`Unknown Lighter order status: ${order.status}`
|
|
95
|
+
)
|
|
101
96
|
}
|
|
102
97
|
}
|
|
103
98
|
|
|
104
|
-
/**
|
|
105
|
-
|
|
106
|
-
* Hyperliquid helper of the same name so the backend can split a raw
|
|
107
|
-
* Lighter order list into the same `openOrders` / `triggerOrders`
|
|
108
|
-
* buckets the SDK declares on `OrdersResponse`.
|
|
109
|
-
* @public
|
|
110
|
-
*/
|
|
111
|
-
export const isTriggerType = (type: OrderType): boolean =>
|
|
112
|
-
type === OrderType.TAKE_PROFIT_MARKET ||
|
|
113
|
-
type === OrderType.TAKE_PROFIT_LIMIT ||
|
|
114
|
-
type === OrderType.STOP_MARKET ||
|
|
115
|
-
type === OrderType.STOP_LIMIT
|
|
116
|
-
|
|
117
|
-
/**
|
|
118
|
-
* Decide whether a raw Lighter order belongs in the cross-provider
|
|
119
|
-
* `triggerOrders` bucket. Single source of truth for both the REST
|
|
120
|
-
* `getOrders` path and the `account_all_orders` WS handler.
|
|
121
|
-
*
|
|
122
|
-
* Authoritative signals, in order:
|
|
123
|
-
* 1. `trigger_status` — Lighter's own enum. `'ready' | 'mark-price' |
|
|
124
|
-
* 'parent-order'` all mean "this order has trigger semantics" (TP/SL
|
|
125
|
-
* waiting on price, or a TP/SL leg of a parent placeOrder). `'twap'`
|
|
126
|
-
* is intentionally excluded — TWAP child orders are scheduled resting
|
|
127
|
-
* limits, not TP/SL triggers. `'na'` is "regular order".
|
|
128
|
-
* 2. `trigger_price > 0` — catches cases where `trigger_status`
|
|
129
|
-
* momentarily lags during state transitions (e.g. right after an OCO
|
|
130
|
-
* sibling cancels).
|
|
131
|
-
* 3. `type` — final fallback via the mapped `OrderType`.
|
|
132
|
-
* @public
|
|
133
|
-
*/
|
|
134
|
-
export const isTriggerOrder = (raw: LtOrder): boolean => {
|
|
135
|
-
if (
|
|
136
|
-
raw.trigger_status === 'ready' ||
|
|
137
|
-
raw.trigger_status === 'mark-price' ||
|
|
138
|
-
raw.trigger_status === 'parent-order'
|
|
139
|
-
) {
|
|
140
|
-
return true
|
|
141
|
-
}
|
|
142
|
-
if (raw.trigger_price !== '' && parseFloat(raw.trigger_price) > 0) {
|
|
143
|
-
return true
|
|
144
|
-
}
|
|
145
|
-
return isTriggerType(mapOrderType(raw.type))
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
/**
|
|
149
|
-
* Map a raw Lighter trigger order (stop/take-profit, market or limit) to
|
|
150
|
-
* the generic `TriggerOrder` shape. For market variants the `limitPrice`
|
|
151
|
-
* field is omitted; for limit variants `order.price` is the limit and
|
|
152
|
-
* `order.trigger_price` is the activation level.
|
|
153
|
-
* @public
|
|
154
|
-
*/
|
|
155
|
-
export const mapTriggerOrder = (
|
|
156
|
-
order: LtOrder,
|
|
157
|
-
market: MarketDisplay
|
|
158
|
-
): TriggerOrder => {
|
|
99
|
+
/** Map a Lighter order with its venue identity, lifecycle, and execution fields. */
|
|
100
|
+
export const mapOrder = (order: LtOrder, market: MarketDisplay): Order => {
|
|
159
101
|
const type = mapOrderType(order.type)
|
|
160
|
-
const
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
// `order_index` is the per-(account, market) numeric id Lighter's L2
|
|
164
|
-
// mutating txs (cancel, modify) require. The matching-engine string
|
|
165
|
-
// label `order_id` is internal to the SDK provider and never crosses
|
|
166
|
-
// this boundary — combined with `market.id` (=market_index), the
|
|
167
|
-
// numeric `order_index` uniquely pins a Lighter order.
|
|
102
|
+
const filledSize = new Big(order.filled_base_amount)
|
|
103
|
+
const status = mapOrderStatus(order, filledSize)
|
|
104
|
+
const base: OrderBase = {
|
|
168
105
|
orderId: String(order.order_index),
|
|
106
|
+
...(order.client_order_index === 0
|
|
107
|
+
? {}
|
|
108
|
+
: { clientOrderId: String(order.client_order_index) }),
|
|
169
109
|
market,
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
110
|
+
side: order.is_ask ? OrderSide.SELL : OrderSide.BUY,
|
|
111
|
+
status,
|
|
112
|
+
...(status === OrderStatus.CANCELLED ? { statusReason: order.status } : {}),
|
|
113
|
+
originalSize: order.initial_base_amount,
|
|
114
|
+
remainingSize: order.remaining_base_amount,
|
|
115
|
+
filledSize: order.filled_base_amount,
|
|
116
|
+
...(filledSize.gt(0)
|
|
117
|
+
? {
|
|
118
|
+
averagePrice: new Big(order.filled_quote_amount)
|
|
119
|
+
.div(filledSize)
|
|
120
|
+
.toString(),
|
|
121
|
+
}
|
|
122
|
+
: {}),
|
|
123
|
+
reduceOnly: order.reduce_only,
|
|
124
|
+
...(order.parent_order_id ? { parentOrderId: order.parent_order_id } : {}),
|
|
174
125
|
createdAt: new Date(order.created_at * 1000).toISOString(),
|
|
126
|
+
updatedAt: new Date(order.updated_at * 1000).toISOString(),
|
|
127
|
+
}
|
|
128
|
+
switch (type) {
|
|
129
|
+
case OrderType.TWAP:
|
|
130
|
+
return {
|
|
131
|
+
...base,
|
|
132
|
+
type,
|
|
133
|
+
durationSeconds: order.order_expiry / 1000 - order.created_at,
|
|
134
|
+
startedAt: base.createdAt,
|
|
135
|
+
}
|
|
136
|
+
case OrderType.STOP_MARKET:
|
|
137
|
+
case OrderType.STOP_LIMIT:
|
|
138
|
+
case OrderType.TAKE_PROFIT_MARKET:
|
|
139
|
+
case OrderType.TAKE_PROFIT_LIMIT:
|
|
140
|
+
return {
|
|
141
|
+
...base,
|
|
142
|
+
type,
|
|
143
|
+
triggerPrice: order.trigger_price,
|
|
144
|
+
triggerCondition: triggerConditionFor(type, base.side),
|
|
145
|
+
...(type === OrderType.STOP_LIMIT ||
|
|
146
|
+
type === OrderType.TAKE_PROFIT_LIMIT
|
|
147
|
+
? { limitPrice: order.price }
|
|
148
|
+
: {}),
|
|
149
|
+
}
|
|
150
|
+
case OrderType.MARKET:
|
|
151
|
+
case OrderType.LIMIT:
|
|
152
|
+
return {
|
|
153
|
+
...base,
|
|
154
|
+
type,
|
|
155
|
+
price: order.price,
|
|
156
|
+
timeInForce: mapTimeInForce(order.time_in_force),
|
|
157
|
+
...(order.order_expiry > 0
|
|
158
|
+
? { expiresAt: new Date(order.order_expiry).toISOString() }
|
|
159
|
+
: {}),
|
|
160
|
+
}
|
|
175
161
|
}
|
|
176
162
|
}
|
|
177
163
|
|
|
178
|
-
/**
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
* @public
|
|
182
|
-
*/
|
|
183
|
-
export const mapOrder = (order: LtOrder, market: MarketDisplay): OpenOrder => ({
|
|
184
|
-
orderId: String(order.order_index),
|
|
185
|
-
market,
|
|
186
|
-
side: order.is_ask ? OrderSide.SELL : OrderSide.BUY,
|
|
187
|
-
type: mapOrderType(order.type),
|
|
188
|
-
originalSize: order.initial_base_amount,
|
|
189
|
-
remainingSize: order.remaining_base_amount,
|
|
190
|
-
price: order.price,
|
|
191
|
-
filledSize: order.filled_base_amount,
|
|
192
|
-
reduceOnly: order.reduce_only,
|
|
193
|
-
createdAt: new Date(order.created_at * 1000).toISOString(),
|
|
194
|
-
})
|
|
195
|
-
|
|
196
|
-
/**
|
|
197
|
-
* Walk a flat list of raw Lighter orders and bucket each into the
|
|
198
|
-
* `OpenOrder` / `TriggerOrder` arrays the cross-provider `OrdersResponse`
|
|
199
|
-
* declares, plus a `terminated` list of orderIds whose status has reached
|
|
200
|
-
* a terminal state (filled / cancelled / rejected / expired) — consumers
|
|
201
|
-
* applying WS deltas evict those from the cached buckets. REST callers
|
|
202
|
-
* see `terminated: []` because `accountActiveOrders` returns active only.
|
|
203
|
-
* An order whose market the resolver does not know is skipped.
|
|
204
|
-
* @public
|
|
205
|
-
*/
|
|
206
|
-
export const classifyAndMapOrders = (
|
|
207
|
-
orders: LtOrder[],
|
|
164
|
+
/** Include terminal rows and their ids so consumers can update history and active caches. */
|
|
165
|
+
export const mapOrderUpdates = (
|
|
166
|
+
rawOrders: LtOrder[],
|
|
208
167
|
resolveMarket: (marketIndex: number) => MarketDisplay | undefined
|
|
209
|
-
): {
|
|
210
|
-
|
|
211
|
-
triggerOrders: TriggerOrder[]
|
|
212
|
-
terminated: string[]
|
|
213
|
-
} => {
|
|
214
|
-
const openOrders: OpenOrder[] = []
|
|
215
|
-
const triggerOrders: TriggerOrder[] = []
|
|
168
|
+
): { orders: Order[]; terminated: string[] } => {
|
|
169
|
+
const orders: Order[] = []
|
|
216
170
|
const terminated: string[] = []
|
|
217
|
-
for (const raw of
|
|
218
|
-
const orderId = String(raw.order_index)
|
|
219
|
-
if (!isActiveOrderStatus(mapOrderStatus(raw.status))) {
|
|
220
|
-
terminated.push(orderId)
|
|
221
|
-
continue
|
|
222
|
-
}
|
|
171
|
+
for (const raw of rawOrders) {
|
|
223
172
|
const market = resolveMarket(raw.market_index)
|
|
224
|
-
|
|
225
|
-
|
|
173
|
+
const order = market === undefined ? undefined : mapOrder(raw, market)
|
|
174
|
+
if (!isActiveOrderStatus(order?.status ?? mapOrderStatus(raw))) {
|
|
175
|
+
terminated.push(String(raw.order_index))
|
|
226
176
|
}
|
|
227
|
-
if (
|
|
228
|
-
|
|
229
|
-
} else {
|
|
230
|
-
openOrders.push(mapOrder(raw, market))
|
|
177
|
+
if (order !== undefined) {
|
|
178
|
+
orders.push(order)
|
|
231
179
|
}
|
|
232
180
|
}
|
|
233
|
-
return {
|
|
181
|
+
return { orders, terminated }
|
|
234
182
|
}
|
|
235
|
-
|
|
236
|
-
/**
|
|
237
|
-
* Map a raw Lighter order to the rich Order type — adds status, time-in-force
|
|
238
|
-
* and remaining/filled sizes on top of the OpenOrder fields.
|
|
239
|
-
* @public
|
|
240
|
-
*/
|
|
241
|
-
export const mapOrderDetail = (
|
|
242
|
-
order: LtOrder,
|
|
243
|
-
market: MarketDisplay
|
|
244
|
-
): Order => ({
|
|
245
|
-
orderId: String(order.order_index),
|
|
246
|
-
market,
|
|
247
|
-
side: order.is_ask ? OrderSide.SELL : OrderSide.BUY,
|
|
248
|
-
type: mapOrderType(order.type),
|
|
249
|
-
price: order.price,
|
|
250
|
-
originalSize: order.initial_base_amount,
|
|
251
|
-
remainingSize: order.remaining_base_amount,
|
|
252
|
-
filledSize: order.filled_base_amount,
|
|
253
|
-
timeInForce: mapTimeInForce(order.time_in_force),
|
|
254
|
-
reduceOnly: order.reduce_only,
|
|
255
|
-
status: mapOrderStatus(order.status),
|
|
256
|
-
statusReason: mapStatusReason(order.status),
|
|
257
|
-
createdAt: new Date(order.created_at * 1000).toISOString(),
|
|
258
|
-
updatedAt: new Date(order.updated_at * 1000).toISOString(),
|
|
259
|
-
})
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Trailing hex characters a Lighter placeholder `tx_hash` leaves zero. Lighter
|
|
3
|
+
* reports a 40-byte hash on every fill and transfer row, and a row that settled
|
|
4
|
+
* no transaction carries a sequence pair in the leading 16 bytes and zero in
|
|
5
|
+
* the remaining 24. A settled row fills all 40 bytes.
|
|
6
|
+
*/
|
|
7
|
+
const PLACEHOLDER_ZERO_SUFFIX_LENGTH = 48
|
|
8
|
+
|
|
9
|
+
const ALL_ZERO = /^0+$/
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Report whether a Lighter `tx_hash` is the placeholder the venue sends for a
|
|
13
|
+
* row that settled no transaction. Such a row resolves to no explorer link.
|
|
14
|
+
*
|
|
15
|
+
* @internal
|
|
16
|
+
*/
|
|
17
|
+
export const isPlaceholderTxHash = (txHash: string | undefined): boolean =>
|
|
18
|
+
txHash !== undefined &&
|
|
19
|
+
txHash.length > PLACEHOLDER_ZERO_SUFFIX_LENGTH &&
|
|
20
|
+
ALL_ZERO.test(txHash.slice(-PLACEHOLDER_ZERO_SUFFIX_LENGTH))
|
|
@@ -29,6 +29,7 @@ import {
|
|
|
29
29
|
DEFAULT_LIGHTER_WS_URL,
|
|
30
30
|
LIGHTER_BASE_FEE_TIER,
|
|
31
31
|
LIGHTER_PROVIDER_KEY,
|
|
32
|
+
LIGHTER_SPOT_CATEGORY_ID,
|
|
32
33
|
} from '../constants.js'
|
|
33
34
|
import type { LighterPerpsProvider } from '../LighterProvider.js'
|
|
34
35
|
import type {
|
|
@@ -50,10 +51,10 @@ import type {
|
|
|
50
51
|
} from '../types/index.js'
|
|
51
52
|
import { LIGHTER_RETRY_DEFAULTS, LighterApiClient } from '../utils/apiClient.js'
|
|
52
53
|
import {
|
|
53
|
-
classifyAndMapOrders,
|
|
54
54
|
fetchDetailedAccount,
|
|
55
55
|
mapFill,
|
|
56
56
|
mapMarketContext,
|
|
57
|
+
mapOrderUpdates,
|
|
57
58
|
mapPosition,
|
|
58
59
|
toRequiredBig,
|
|
59
60
|
} from '../utils/index.js'
|
|
@@ -84,18 +85,19 @@ import {
|
|
|
84
85
|
// Orderbook is stateful: the first message is a full snapshot, subsequent
|
|
85
86
|
// messages are deltas where size=0 deletes a level.
|
|
86
87
|
|
|
87
|
-
const LIGHTER_SPOT_MARKET_ID_OFFSET = 2048
|
|
88
|
-
|
|
89
88
|
const LIGHTER_AUTH_CHANNEL = {
|
|
90
89
|
orderUpdates: 'account_all_orders',
|
|
91
90
|
fills: 'account_all_trades',
|
|
92
91
|
positions: 'account_all_positions',
|
|
93
92
|
} as const
|
|
94
93
|
|
|
95
|
-
/** Channels whose
|
|
94
|
+
/** Channels whose wire name or handler reads market identity from the registry. */
|
|
96
95
|
function channelNeedsMarkets(channel: Subscription['channel']): boolean {
|
|
97
96
|
return (
|
|
98
|
-
channel === '
|
|
97
|
+
channel === 'marketContext' ||
|
|
98
|
+
channel === 'orderUpdates' ||
|
|
99
|
+
channel === 'fills' ||
|
|
100
|
+
channel === 'positions'
|
|
99
101
|
)
|
|
100
102
|
}
|
|
101
103
|
|
|
@@ -258,10 +260,10 @@ export class LighterWsProvider extends WsProviderBase<SubState> {
|
|
|
258
260
|
}
|
|
259
261
|
|
|
260
262
|
protected async openChannel(sub: Subscription): Promise<() => void> {
|
|
261
|
-
//
|
|
262
|
-
//
|
|
263
|
-
// gating them on the
|
|
264
|
-
//
|
|
263
|
+
// `marketsContext` resolves to two fixed `/all` channels and `orderbook` is
|
|
264
|
+
// keyed purely by `String(market_id)`, so neither needs the registry;
|
|
265
|
+
// gating them on the sync would let a failed `/markets` fetch kill live
|
|
266
|
+
// price ticks.
|
|
265
267
|
if (channelNeedsMarkets(sub.channel)) {
|
|
266
268
|
await this.registry?.sync()
|
|
267
269
|
}
|
|
@@ -393,18 +395,21 @@ export class LighterWsProvider extends WsProviderBase<SubState> {
|
|
|
393
395
|
]
|
|
394
396
|
}
|
|
395
397
|
if (sub.channel === 'marketContext') {
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
398
|
+
// Lighter market ids carry no type information, so the wire channel comes
|
|
399
|
+
// from the registry row's category, not from the numeric id.
|
|
400
|
+
if (this.registry === undefined) {
|
|
401
|
+
throw new PerpsError(
|
|
402
|
+
PerpsErrorCode.MarketNotFound,
|
|
403
|
+
`Lighter WS: no market registry for marketId '${sub.marketId}'. ` +
|
|
404
|
+
'Construct via `lighterWsProvider({...})` and register with PerpsWsClient.'
|
|
401
405
|
)
|
|
402
406
|
}
|
|
407
|
+
const market = this.registry.require(sub.marketId)
|
|
403
408
|
const prefix =
|
|
404
|
-
|
|
409
|
+
market.categoryId === LIGHTER_SPOT_CATEGORY_ID
|
|
405
410
|
? 'spot_market_stats'
|
|
406
411
|
: 'market_stats'
|
|
407
|
-
return [{ channel: `${prefix}/${id}`, needsAuth: false }]
|
|
412
|
+
return [{ channel: `${prefix}/${market.id}`, needsAuth: false }]
|
|
408
413
|
}
|
|
409
414
|
if (sub.channel === 'orderbook') {
|
|
410
415
|
const id = Number(sub.marketId)
|
|
@@ -598,7 +603,7 @@ export class LighterWsProvider extends WsProviderBase<SubState> {
|
|
|
598
603
|
}
|
|
599
604
|
const raw = collectAuthChannelItems<LtOrder>(msg, 'orders')
|
|
600
605
|
// Unknown market id (absent from the synced snapshot); the order is skipped.
|
|
601
|
-
const data =
|
|
606
|
+
const data = mapOrderUpdates(raw, (marketIndex) =>
|
|
602
607
|
this.registry?.get(String(marketIndex))
|
|
603
608
|
)
|
|
604
609
|
this.emit(`orderUpdates:${address}`, {
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"mapTwap.d.ts","sourceRoot":"","sources":["../../../src/utils/mapTwap.ts"],"names":[],"mappings":"AACA,OAAO,EACL,KAAK,MAAM,EAEX,KAAK,SAAS,EAEf,MAAM,mBAAmB,CAAA;AAE1B,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAA;AAGhD,eAAO,MAAM,cAAc,GAAI,OAAO,OAAO,EAAE,QAAQ,MAAM,KAAG,SAmB/D,CAAA"}
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.mapRunningTwap = void 0;
|
|
7
|
-
const perps_sdk_1 = require("@lifi/perps-sdk");
|
|
8
|
-
const perps_types_1 = require("@lifi/perps-types");
|
|
9
|
-
const big_js_1 = __importDefault(require("big.js"));
|
|
10
|
-
const mapRunningTwap = (order, market) => {
|
|
11
|
-
const filledSize = new big_js_1.default(order.filled_base_amount);
|
|
12
|
-
return {
|
|
13
|
-
twapId: String(order.order_index),
|
|
14
|
-
market: (0, perps_sdk_1.toMarketDisplay)(market),
|
|
15
|
-
side: order.is_ask ? perps_types_1.OrderSide.SELL : perps_types_1.OrderSide.BUY,
|
|
16
|
-
totalSize: order.initial_base_amount,
|
|
17
|
-
filledSize: order.filled_base_amount,
|
|
18
|
-
...(filledSize.eq(0)
|
|
19
|
-
? {}
|
|
20
|
-
: {
|
|
21
|
-
avgFillPrice: new big_js_1.default(order.filled_quote_amount)
|
|
22
|
-
.div(filledSize)
|
|
23
|
-
.toString(),
|
|
24
|
-
}),
|
|
25
|
-
startedAt: new Date(order.created_at * 1000).toISOString(),
|
|
26
|
-
durationSeconds: (order.order_expiry - order.created_at * 1000) / 1000,
|
|
27
|
-
status: perps_types_1.TwapOrderStatus.RUNNING,
|
|
28
|
-
};
|
|
29
|
-
};
|
|
30
|
-
exports.mapRunningTwap = mapRunningTwap;
|
|
31
|
-
//# sourceMappingURL=mapTwap.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"mapTwap.js","sourceRoot":"","sources":["../../../src/utils/mapTwap.ts"],"names":[],"mappings":";;;;;;AAAA,+CAAiD;AACjD,mDAK0B;AAC1B,oDAAwB;AAIjB,MAAM,cAAc,GAAG,CAAC,KAAc,EAAE,MAAc,EAAa,EAAE;IAC1E,MAAM,UAAU,GAAG,IAAI,gBAAG,CAAC,KAAK,CAAC,kBAAkB,CAAC,CAAA;IACpD,OAAO;QACL,MAAM,EAAE,MAAM,CAAC,KAAK,CAAC,WAAW,CAAC;QACjC,MAAM,EAAE,IAAA,2BAAe,EAAC,MAAM,CAAC;QAC/B,IAAI,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,uBAAS,CAAC,IAAI,CAAC,CAAC,CAAC,uBAAS,CAAC,GAAG;QACnD,SAAS,EAAE,KAAK,CAAC,mBAAmB;QACpC,UAAU,EAAE,KAAK,CAAC,kBAAkB;QACpC,GAAG,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC,CAAC;YAClB,CAAC,CAAC,EAAE;YACJ,CAAC,CAAC;gBACE,YAAY,EAAE,IAAI,gBAAG,CAAC,KAAK,CAAC,mBAAmB,CAAC;qBAC7C,GAAG,CAAC,UAAU,CAAC;qBACf,QAAQ,EAAE;aACd,CAAC;QACN,SAAS,EAAE,IAAI,IAAI,CAAC,KAAK,CAAC,UAAU,GAAG,IAAI,CAAC,CAAC,WAAW,EAAE;QAC1D,eAAe,EAAE,CAAC,KAAK,CAAC,YAAY,GAAG,KAAK,CAAC,UAAU,GAAG,IAAI,CAAC,GAAG,IAAI;QACtE,MAAM,EAAE,6BAAe,CAAC,OAAO;KAChC,CAAA;AACH,CAAC,CAAA;AAnBY,QAAA,cAAc,kBAmB1B"}
|
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
import { type Market, type TwapOrder } from '@lifi/perps-types';
|
|
2
|
-
import type { LtOrder } from '../types/order.js';
|
|
3
|
-
/** Map an active Lighter `twap` parent order to the shared running-TWAP model. */
|
|
4
|
-
export declare const mapRunningTwap: (order: LtOrder, market: Market) => TwapOrder;
|
|
5
|
-
//# sourceMappingURL=mapTwap.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"mapTwap.d.ts","sourceRoot":"","sources":["../../../src/utils/mapTwap.ts"],"names":[],"mappings":"AACA,OAAO,EACL,KAAK,MAAM,EAEX,KAAK,SAAS,EAEf,MAAM,mBAAmB,CAAA;AAE1B,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAA;AAEhD,kFAAkF;AAClF,eAAO,MAAM,cAAc,GAAI,OAAO,OAAO,EAAE,QAAQ,MAAM,KAAG,SAmB/D,CAAA"}
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import { toMarketDisplay } from '@lifi/perps-sdk';
|
|
2
|
-
import { OrderSide, TwapOrderStatus, } from '@lifi/perps-types';
|
|
3
|
-
import Big from 'big.js';
|
|
4
|
-
/** Map an active Lighter `twap` parent order to the shared running-TWAP model. */
|
|
5
|
-
export const mapRunningTwap = (order, market) => {
|
|
6
|
-
const filledSize = new Big(order.filled_base_amount);
|
|
7
|
-
return {
|
|
8
|
-
twapId: String(order.order_index),
|
|
9
|
-
market: toMarketDisplay(market),
|
|
10
|
-
side: order.is_ask ? OrderSide.SELL : OrderSide.BUY,
|
|
11
|
-
totalSize: order.initial_base_amount,
|
|
12
|
-
filledSize: order.filled_base_amount,
|
|
13
|
-
...(filledSize.eq(0)
|
|
14
|
-
? {}
|
|
15
|
-
: {
|
|
16
|
-
avgFillPrice: new Big(order.filled_quote_amount)
|
|
17
|
-
.div(filledSize)
|
|
18
|
-
.toString(),
|
|
19
|
-
}),
|
|
20
|
-
startedAt: new Date(order.created_at * 1000).toISOString(),
|
|
21
|
-
durationSeconds: (order.order_expiry - order.created_at * 1000) / 1000,
|
|
22
|
-
status: TwapOrderStatus.RUNNING,
|
|
23
|
-
};
|
|
24
|
-
};
|
|
25
|
-
//# sourceMappingURL=mapTwap.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"mapTwap.js","sourceRoot":"","sources":["../../../src/utils/mapTwap.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;AACjD,OAAO,EAEL,SAAS,EAET,eAAe,GAChB,MAAM,mBAAmB,CAAA;AAC1B,OAAO,GAAG,MAAM,QAAQ,CAAA;AAGxB,kFAAkF;AAClF,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,KAAc,EAAE,MAAc,EAAa,EAAE;IAC1E,MAAM,UAAU,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,kBAAkB,CAAC,CAAA;IACpD,OAAO;QACL,MAAM,EAAE,MAAM,CAAC,KAAK,CAAC,WAAW,CAAC;QACjC,MAAM,EAAE,eAAe,CAAC,MAAM,CAAC;QAC/B,IAAI,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,GAAG;QACnD,SAAS,EAAE,KAAK,CAAC,mBAAmB;QACpC,UAAU,EAAE,KAAK,CAAC,kBAAkB;QACpC,GAAG,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC,CAAC;YAClB,CAAC,CAAC,EAAE;YACJ,CAAC,CAAC;gBACE,YAAY,EAAE,IAAI,GAAG,CAAC,KAAK,CAAC,mBAAmB,CAAC;qBAC7C,GAAG,CAAC,UAAU,CAAC;qBACf,QAAQ,EAAE;aACd,CAAC;QACN,SAAS,EAAE,IAAI,IAAI,CAAC,KAAK,CAAC,UAAU,GAAG,IAAI,CAAC,CAAC,WAAW,EAAE;QAC1D,eAAe,EAAE,CAAC,KAAK,CAAC,YAAY,GAAG,KAAK,CAAC,UAAU,GAAG,IAAI,CAAC,GAAG,IAAI;QACtE,MAAM,EAAE,eAAe,CAAC,OAAO;KAChC,CAAA;AACH,CAAC,CAAA"}
|
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
import { type Market, type TwapOrder } from '@lifi/perps-types';
|
|
2
|
-
import type { LtOrder } from '../types/order.js';
|
|
3
|
-
/** Map an active Lighter `twap` parent order to the shared running-TWAP model. */
|
|
4
|
-
export declare const mapRunningTwap: (order: LtOrder, market: Market) => TwapOrder;
|
|
5
|
-
//# sourceMappingURL=mapTwap.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"mapTwap.d.ts","sourceRoot":"","sources":["../../../src/utils/mapTwap.ts"],"names":[],"mappings":"AACA,OAAO,EACL,KAAK,MAAM,EAEX,KAAK,SAAS,EAEf,MAAM,mBAAmB,CAAA;AAE1B,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAA;AAEhD,kFAAkF;AAClF,eAAO,MAAM,cAAc,GAAI,OAAO,OAAO,EAAE,QAAQ,MAAM,KAAG,SAmB/D,CAAA"}
|
package/src/utils/mapTwap.ts
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import { toMarketDisplay } from '@lifi/perps-sdk'
|
|
2
|
-
import {
|
|
3
|
-
type Market,
|
|
4
|
-
OrderSide,
|
|
5
|
-
type TwapOrder,
|
|
6
|
-
TwapOrderStatus,
|
|
7
|
-
} from '@lifi/perps-types'
|
|
8
|
-
import Big from 'big.js'
|
|
9
|
-
import type { LtOrder } from '../types/order.js'
|
|
10
|
-
|
|
11
|
-
/** Map an active Lighter `twap` parent order to the shared running-TWAP model. */
|
|
12
|
-
export const mapRunningTwap = (order: LtOrder, market: Market): TwapOrder => {
|
|
13
|
-
const filledSize = new Big(order.filled_base_amount)
|
|
14
|
-
return {
|
|
15
|
-
twapId: String(order.order_index),
|
|
16
|
-
market: toMarketDisplay(market),
|
|
17
|
-
side: order.is_ask ? OrderSide.SELL : OrderSide.BUY,
|
|
18
|
-
totalSize: order.initial_base_amount,
|
|
19
|
-
filledSize: order.filled_base_amount,
|
|
20
|
-
...(filledSize.eq(0)
|
|
21
|
-
? {}
|
|
22
|
-
: {
|
|
23
|
-
avgFillPrice: new Big(order.filled_quote_amount)
|
|
24
|
-
.div(filledSize)
|
|
25
|
-
.toString(),
|
|
26
|
-
}),
|
|
27
|
-
startedAt: new Date(order.created_at * 1000).toISOString(),
|
|
28
|
-
durationSeconds: (order.order_expiry - order.created_at * 1000) / 1000,
|
|
29
|
-
status: TwapOrderStatus.RUNNING,
|
|
30
|
-
}
|
|
31
|
-
}
|