@lifi/perps-sdk-provider-lighter 21.0.0 → 22.1.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/dist/cjs/LighterProvider.d.ts.map +1 -1
- package/dist/cjs/LighterProvider.js +53 -26
- package/dist/cjs/LighterProvider.js.map +1 -1
- package/dist/cjs/accountConfig.js +3 -4
- package/dist/cjs/accountConfig.js.map +1 -1
- package/dist/cjs/constants.d.ts +3 -1
- package/dist/cjs/constants.d.ts.map +1 -1
- package/dist/cjs/constants.js +4 -1
- package/dist/cjs/constants.js.map +1 -1
- package/dist/cjs/index.d.ts +1 -1
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/index.js +2 -1
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/signers/signActions.d.ts.map +1 -1
- package/dist/cjs/signers/signActions.js +13 -5
- package/dist/cjs/signers/signActions.js.map +1 -1
- package/dist/cjs/types/account.d.ts +3 -3
- package/dist/cjs/types/account.d.ts.map +1 -1
- package/dist/cjs/types/action.d.ts +2 -0
- package/dist/cjs/types/action.d.ts.map +1 -1
- package/dist/cjs/types/action.js +3 -1
- package/dist/cjs/types/action.js.map +1 -1
- package/dist/cjs/types/fill.d.ts +2 -2
- package/dist/cjs/types/fill.d.ts.map +1 -1
- package/dist/cjs/types/order.d.ts +4 -0
- package/dist/cjs/types/order.d.ts.map +1 -1
- package/dist/cjs/utils/apiClient.d.ts +1 -1
- package/dist/cjs/utils/apiClient.d.ts.map +1 -1
- package/dist/cjs/utils/apiClient.js +23 -10
- package/dist/cjs/utils/apiClient.js.map +1 -1
- package/dist/cjs/utils/lighterErrorCode.d.ts +3 -0
- package/dist/cjs/utils/lighterErrorCode.d.ts.map +1 -0
- package/dist/cjs/utils/lighterErrorCode.js +17 -0
- package/dist/cjs/utils/lighterErrorCode.js.map +1 -0
- package/dist/cjs/utils/mapFill.d.ts.map +1 -1
- package/dist/cjs/utils/mapFill.js +2 -2
- package/dist/cjs/utils/mapFill.js.map +1 -1
- package/dist/cjs/utils/mapPosition.js +1 -1
- package/dist/cjs/utils/mapPosition.js.map +1 -1
- package/dist/esm/LighterProvider.d.ts.map +1 -1
- package/dist/esm/LighterProvider.js +79 -35
- package/dist/esm/LighterProvider.js.map +1 -1
- package/dist/esm/accountConfig.js +5 -7
- package/dist/esm/accountConfig.js.map +1 -1
- package/dist/esm/constants.d.ts +24 -3
- package/dist/esm/constants.d.ts.map +1 -1
- package/dist/esm/constants.js +24 -3
- package/dist/esm/constants.js.map +1 -1
- package/dist/esm/index.d.ts +1 -1
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +1 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/signers/signActions.d.ts.map +1 -1
- package/dist/esm/signers/signActions.js +22 -8
- package/dist/esm/signers/signActions.js.map +1 -1
- package/dist/esm/types/account.d.ts +15 -4
- package/dist/esm/types/account.d.ts.map +1 -1
- package/dist/esm/types/action.d.ts +13 -0
- package/dist/esm/types/action.d.ts.map +1 -1
- package/dist/esm/types/action.js +13 -0
- package/dist/esm/types/action.js.map +1 -1
- package/dist/esm/types/fill.d.ts +2 -2
- package/dist/esm/types/fill.d.ts.map +1 -1
- package/dist/esm/types/order.d.ts +12 -0
- package/dist/esm/types/order.d.ts.map +1 -1
- package/dist/esm/utils/apiClient.d.ts +15 -7
- package/dist/esm/utils/apiClient.d.ts.map +1 -1
- package/dist/esm/utils/apiClient.js +41 -18
- package/dist/esm/utils/apiClient.js.map +1 -1
- package/dist/esm/utils/lighterErrorCode.d.ts +7 -0
- package/dist/esm/utils/lighterErrorCode.d.ts.map +1 -0
- package/dist/esm/utils/lighterErrorCode.js +26 -0
- package/dist/esm/utils/lighterErrorCode.js.map +1 -0
- package/dist/esm/utils/mapFill.d.ts.map +1 -1
- package/dist/esm/utils/mapFill.js +3 -2
- package/dist/esm/utils/mapFill.js.map +1 -1
- package/dist/esm/utils/mapPosition.js +1 -1
- package/dist/esm/utils/mapPosition.js.map +1 -1
- package/dist/types/LighterProvider.d.ts.map +1 -1
- package/dist/types/constants.d.ts +24 -3
- package/dist/types/constants.d.ts.map +1 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/signers/signActions.d.ts.map +1 -1
- package/dist/types/types/account.d.ts +15 -4
- package/dist/types/types/account.d.ts.map +1 -1
- package/dist/types/types/action.d.ts +13 -0
- package/dist/types/types/action.d.ts.map +1 -1
- package/dist/types/types/fill.d.ts +2 -2
- package/dist/types/types/fill.d.ts.map +1 -1
- package/dist/types/types/order.d.ts +12 -0
- package/dist/types/types/order.d.ts.map +1 -1
- package/dist/types/utils/apiClient.d.ts +15 -7
- package/dist/types/utils/apiClient.d.ts.map +1 -1
- package/dist/types/utils/lighterErrorCode.d.ts +7 -0
- package/dist/types/utils/lighterErrorCode.d.ts.map +1 -0
- package/dist/types/utils/mapFill.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/LighterProvider.ts +130 -57
- package/src/accountConfig.ts +8 -8
- package/src/constants.ts +26 -2
- package/src/index.ts +1 -0
- package/src/signers/signActions.ts +28 -11
- package/src/types/account.ts +15 -4
- package/src/types/action.ts +15 -0
- package/src/types/fill.ts +3 -2
- package/src/types/order.ts +13 -0
- package/src/utils/apiClient.ts +47 -17
- package/src/utils/lighterErrorCode.ts +30 -0
- package/src/utils/mapFill.ts +5 -3
- package/src/utils/mapPosition.ts +1 -1
package/src/LighterProvider.ts
CHANGED
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
getAssetRegistry,
|
|
5
5
|
getMarketRegistry,
|
|
6
6
|
getProviders,
|
|
7
|
+
isActiveOrderStatus,
|
|
7
8
|
localStorageAdapter,
|
|
8
9
|
PerpsError,
|
|
9
10
|
type PerpsProviderPlugin,
|
|
@@ -66,6 +67,7 @@ import {
|
|
|
66
67
|
DEFAULT_TRADES_LIMIT,
|
|
67
68
|
LIGHTER_ALL_MARKETS_WILDCARD,
|
|
68
69
|
LIGHTER_BASE_FEE_TIER,
|
|
70
|
+
LIGHTER_CLIENT_ORDER_INDEX_ID_PREFIX,
|
|
69
71
|
LIGHTER_FEE_TICK_SCALE,
|
|
70
72
|
LIGHTER_HISTORY_PAGE_SIZE,
|
|
71
73
|
LIGHTER_MAINNET_DEPLOYMENT,
|
|
@@ -88,6 +90,7 @@ import {
|
|
|
88
90
|
import type {
|
|
89
91
|
LtAccount,
|
|
90
92
|
LtAccountLimits,
|
|
93
|
+
LtAccountOrdersResponse,
|
|
91
94
|
LtDepositHistoryItem,
|
|
92
95
|
LtDepositHistoryResponse,
|
|
93
96
|
LtDetailedAccountPosition,
|
|
@@ -103,7 +106,11 @@ import type {
|
|
|
103
106
|
LtWithdrawHistoryItem,
|
|
104
107
|
LtWithdrawHistoryResponse,
|
|
105
108
|
} from './types/index.js'
|
|
106
|
-
import {
|
|
109
|
+
import {
|
|
110
|
+
LT_ACCOUNT_TRADING_MODE_SIMPLE,
|
|
111
|
+
LT_MARGIN_MODE_CROSS,
|
|
112
|
+
LT_MARGIN_MODE_ISOLATED,
|
|
113
|
+
} from './types/index.js'
|
|
107
114
|
import {
|
|
108
115
|
decodeActivityCursor,
|
|
109
116
|
encodeActivityCursor,
|
|
@@ -165,6 +172,13 @@ const orderCountFor = (p: LtDetailedAccountPosition): number =>
|
|
|
165
172
|
*/
|
|
166
173
|
const TX_HASH_PATTERN = /^[0-9a-f]{80}$/
|
|
167
174
|
|
|
175
|
+
/**
|
|
176
|
+
* Lighter's `client_order_index` is an int64 and `accountOrders` takes it as a
|
|
177
|
+
* decimal string, so a prefixed id's suffix goes to the venue verbatim rather
|
|
178
|
+
* than through a lossy JS number.
|
|
179
|
+
*/
|
|
180
|
+
const CLIENT_ORDER_INDEX_PATTERN = /^\d+$/
|
|
181
|
+
|
|
168
182
|
const INACTIVE_ORDERS_LOOKUP_LIMIT = 100
|
|
169
183
|
|
|
170
184
|
/** Activity surfaces whose rows name a market, so they need the market registry. */
|
|
@@ -255,6 +269,12 @@ interface CachedStandardToken {
|
|
|
255
269
|
token: string
|
|
256
270
|
/** Unix seconds — re-create when `Date.now()/1000 + renewBuffer >= expiresAt`. */
|
|
257
271
|
expiresAt: number
|
|
272
|
+
/**
|
|
273
|
+
* Slot and key material that signed `token`, as
|
|
274
|
+
* `apiKeyIndex:accountIndex:apiKeyPrivateKey`. `REGISTER_API_KEY` rotates the
|
|
275
|
+
* stored key, and Lighter rejects a token the superseded key signed.
|
|
276
|
+
*/
|
|
277
|
+
signingKey: string
|
|
258
278
|
}
|
|
259
279
|
|
|
260
280
|
/**
|
|
@@ -359,10 +379,12 @@ export const createLighterProvider = (
|
|
|
359
379
|
indices: { apiKeyIndex: number; accountIndex: number }
|
|
360
380
|
): Promise<string> => {
|
|
361
381
|
const cacheKey = address.toLowerCase()
|
|
382
|
+
const signingKey = `${indices.apiKeyIndex}:${indices.accountIndex}:${apiKeyPrivateKey}`
|
|
362
383
|
const nowSec = Math.floor(Date.now() / 1000)
|
|
363
384
|
const cached = standardTokenByAddress.get(cacheKey)
|
|
364
385
|
if (
|
|
365
386
|
cached !== undefined &&
|
|
387
|
+
cached.signingKey === signingKey &&
|
|
366
388
|
cached.expiresAt - nowSec > tokenRenewBufferSeconds
|
|
367
389
|
) {
|
|
368
390
|
return cached.token
|
|
@@ -377,7 +399,7 @@ export const createLighterProvider = (
|
|
|
377
399
|
lifetimeSeconds: tokenLifetimeSeconds,
|
|
378
400
|
})
|
|
379
401
|
const expiresAt = nowSec + tokenLifetimeSeconds
|
|
380
|
-
standardTokenByAddress.set(cacheKey, { token, expiresAt })
|
|
402
|
+
standardTokenByAddress.set(cacheKey, { token, expiresAt, signingKey })
|
|
381
403
|
return token
|
|
382
404
|
}
|
|
383
405
|
|
|
@@ -415,11 +437,17 @@ export const createLighterProvider = (
|
|
|
415
437
|
return undefined
|
|
416
438
|
}
|
|
417
439
|
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
440
|
+
// Sign with the key registered at call time, not the one read on entry:
|
|
441
|
+
// `REGISTER_API_KEY` can rotate the store while an await below is pending,
|
|
442
|
+
// and Lighter rejects a token the superseded key signed. The entry value
|
|
443
|
+
// stands in only when the record is gone, which no key can re-sign.
|
|
444
|
+
const standardToken = async (): Promise<string> => {
|
|
445
|
+
const current = (await keyStore.get(address)) ?? apiKey
|
|
446
|
+
return getStandardAuthToken(address, current.apiKeyPrivateKey, {
|
|
447
|
+
apiKeyIndex: current.apiKeyIndex,
|
|
448
|
+
accountIndex: current.accountIndex,
|
|
422
449
|
})
|
|
450
|
+
}
|
|
423
451
|
|
|
424
452
|
const stored = await readOnlyTokenManager.get(address, apiKey.accountIndex)
|
|
425
453
|
if (stored !== undefined) {
|
|
@@ -567,7 +595,7 @@ export const createLighterProvider = (
|
|
|
567
595
|
account_index: accountIndex,
|
|
568
596
|
})
|
|
569
597
|
|
|
570
|
-
const
|
|
598
|
+
const fetchActiveOrders = async (
|
|
571
599
|
client: LighterApiClient,
|
|
572
600
|
authToken: string,
|
|
573
601
|
accountIndex: number,
|
|
@@ -581,6 +609,20 @@ export const createLighterProvider = (
|
|
|
581
609
|
return { ...response, orders: wireList(response.orders) }
|
|
582
610
|
}
|
|
583
611
|
|
|
612
|
+
const fetchOrdersByClientOrderIndex = async (
|
|
613
|
+
client: LighterApiClient,
|
|
614
|
+
authToken: string,
|
|
615
|
+
accountIndex: number,
|
|
616
|
+
clientOrderIndex: string
|
|
617
|
+
): Promise<LtOrder[]> => {
|
|
618
|
+
const response = await client.getAuthed<LtAccountOrdersResponse>(
|
|
619
|
+
'/api/v1/accountOrders',
|
|
620
|
+
authToken,
|
|
621
|
+
{ account_index: accountIndex, client_order_indexes: clientOrderIndex }
|
|
622
|
+
)
|
|
623
|
+
return wireList(response.orders)
|
|
624
|
+
}
|
|
625
|
+
|
|
584
626
|
const deriveOrderBearingMarketIds = (account: LtAccount): number[] =>
|
|
585
627
|
account.positions
|
|
586
628
|
.filter((p) => orderCountFor(p) > 0)
|
|
@@ -863,7 +905,8 @@ export const createLighterProvider = (
|
|
|
863
905
|
apiKeyRegistered,
|
|
864
906
|
accountType: account.account_type,
|
|
865
907
|
userTierName: limitsResult?.user_tier_name,
|
|
866
|
-
accountTradingMode:
|
|
908
|
+
accountTradingMode:
|
|
909
|
+
account.account_trading_mode ?? LT_ACCOUNT_TRADING_MODE_SIMPLE,
|
|
867
910
|
assetCollateral,
|
|
868
911
|
// Satisfied when a non-expired read-only token is stored locally
|
|
869
912
|
// (`readOnlyTokenManager.get` filters out expired ones).
|
|
@@ -1017,21 +1060,17 @@ export const createLighterProvider = (
|
|
|
1017
1060
|
registry.sync(),
|
|
1018
1061
|
])
|
|
1019
1062
|
|
|
1020
|
-
const
|
|
1063
|
+
const marketId =
|
|
1021
1064
|
params.marketId === undefined
|
|
1022
|
-
?
|
|
1023
|
-
:
|
|
1065
|
+
? LIGHTER_ALL_MARKETS_WILDCARD
|
|
1066
|
+
: Number(registry.require(params.marketId).id)
|
|
1024
1067
|
|
|
1025
|
-
const
|
|
1026
|
-
|
|
1027
|
-
marketIds.map((id) =>
|
|
1028
|
-
fetchActiveOrdersForMarket(client, t, account.index, id)
|
|
1029
|
-
)
|
|
1030
|
-
)
|
|
1068
|
+
const response = await retryOnRevoked(opts, params.address, token, (t) =>
|
|
1069
|
+
fetchActiveOrders(client, t, account.index, marketId)
|
|
1031
1070
|
)
|
|
1032
1071
|
|
|
1033
1072
|
const { openOrders, triggerOrders } = classifyAndMapOrders(
|
|
1034
|
-
|
|
1073
|
+
response.orders,
|
|
1035
1074
|
(marketIndex) => registry.require(String(marketIndex))
|
|
1036
1075
|
)
|
|
1037
1076
|
|
|
@@ -1065,9 +1104,7 @@ export const createLighterProvider = (
|
|
|
1065
1104
|
: [Number(registry.require(params.marketId).id)]
|
|
1066
1105
|
const responses = await retryOnRevoked(opts, params.address, token, (t) =>
|
|
1067
1106
|
Promise.all(
|
|
1068
|
-
marketIds.map((id) =>
|
|
1069
|
-
fetchActiveOrdersForMarket(client, t, account.index, id)
|
|
1070
|
-
)
|
|
1107
|
+
marketIds.map((id) => fetchActiveOrders(client, t, account.index, id))
|
|
1071
1108
|
)
|
|
1072
1109
|
)
|
|
1073
1110
|
|
|
@@ -1087,6 +1124,14 @@ export const createLighterProvider = (
|
|
|
1087
1124
|
return twaps
|
|
1088
1125
|
},
|
|
1089
1126
|
|
|
1127
|
+
/**
|
|
1128
|
+
* Resolves either reference Lighter hands out for an order: a bare
|
|
1129
|
+
* `order_index` — the value `Order.orderId` carries, as streamed by
|
|
1130
|
+
* `orderUpdates` / `fills` — or a `client_order_index` prefixed with
|
|
1131
|
+
* {@link LIGHTER_CLIENT_ORDER_INDEX_ID_PREFIX}, which a signed placement
|
|
1132
|
+
* step returns and which resolves before the venue has assigned an
|
|
1133
|
+
* `order_index`.
|
|
1134
|
+
*/
|
|
1090
1135
|
async getOrder(
|
|
1091
1136
|
params: ProviderGetOrderParams,
|
|
1092
1137
|
opts?: SDKRequestOptions
|
|
@@ -1108,41 +1153,75 @@ export const createLighterProvider = (
|
|
|
1108
1153
|
registry.sync(),
|
|
1109
1154
|
])
|
|
1110
1155
|
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1156
|
+
const notFound = (): PerpsError =>
|
|
1157
|
+
new PerpsError(
|
|
1158
|
+
PerpsErrorCode.OrderNotFound,
|
|
1159
|
+
`Lighter order ${params.id} not found for ${params.address}`
|
|
1160
|
+
)
|
|
1161
|
+
const detail = (order: LtOrder): Order =>
|
|
1162
|
+
mapOrderDetail(order, registry.require(String(order.market_index)))
|
|
1163
|
+
|
|
1164
|
+
// A tx-hash route would require mapping the caller's executeAction tx
|
|
1165
|
+
// hash → wasm nonce → matching order, which the LI.FI backend did via
|
|
1166
|
+
// its `UserAction` table; the SDK has no equivalent persistence, so we
|
|
1167
|
+
// refuse rather than mis-resolve.
|
|
1117
1168
|
if (TX_HASH_PATTERN.test(params.id)) {
|
|
1118
1169
|
throw new PerpsError(
|
|
1119
1170
|
PerpsErrorCode.OrderNotFound,
|
|
1120
1171
|
`Lighter order id "${params.id}" looks like a tx hash. The SDK ` +
|
|
1121
|
-
`resolves orders by Lighter \`order_index\`
|
|
1122
|
-
|
|
1172
|
+
`resolves orders by Lighter \`order_index\` or by a ` +
|
|
1173
|
+
`\`${LIGHTER_CLIENT_ORDER_INDEX_ID_PREFIX}<index>\` id — surface the ` +
|
|
1174
|
+
`orderId from the orderUpdates / fills WS stream, or the ` +
|
|
1175
|
+
`clientOrderIndex from the signed placement step, and pass it here.`
|
|
1123
1176
|
)
|
|
1124
1177
|
}
|
|
1125
|
-
const predicate: (o: { order_index: number }) => boolean = (o) =>
|
|
1126
|
-
String(o.order_index) === params.id
|
|
1127
1178
|
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
)
|
|
1179
|
+
if (params.id.startsWith(LIGHTER_CLIENT_ORDER_INDEX_ID_PREFIX)) {
|
|
1180
|
+
const clientOrderIndex = params.id.slice(
|
|
1181
|
+
LIGHTER_CLIENT_ORDER_INDEX_ID_PREFIX.length
|
|
1182
|
+
)
|
|
1183
|
+
if (!CLIENT_ORDER_INDEX_PATTERN.test(clientOrderIndex)) {
|
|
1184
|
+
throw new PerpsError(
|
|
1185
|
+
PerpsErrorCode.SDKError,
|
|
1186
|
+
`Lighter order id "${params.id}" names a client order index, but ` +
|
|
1187
|
+
`"${clientOrderIndex}" is not a decimal integer.`
|
|
1138
1188
|
)
|
|
1139
|
-
)
|
|
1140
|
-
|
|
1141
|
-
for (const response of activeResponses) {
|
|
1142
|
-
const hit = response.orders.find(predicate as (o: unknown) => boolean)
|
|
1143
|
-
if (hit !== undefined) {
|
|
1144
|
-
return mapOrderDetail(hit, registry.require(String(hit.market_index)))
|
|
1145
1189
|
}
|
|
1190
|
+
const orders = await retryOnRevoked(opts, params.address, token, (t) =>
|
|
1191
|
+
fetchOrdersByClientOrderIndex(
|
|
1192
|
+
client,
|
|
1193
|
+
t,
|
|
1194
|
+
account.index,
|
|
1195
|
+
clientOrderIndex
|
|
1196
|
+
)
|
|
1197
|
+
)
|
|
1198
|
+
// `accountOrders` reaches active and inactive rows alike, so a reused
|
|
1199
|
+
// client order index can match more than one row. Prefer the live one.
|
|
1200
|
+
const matches = orders
|
|
1201
|
+
.filter((o) => String(o.client_order_index) === clientOrderIndex)
|
|
1202
|
+
.map(detail)
|
|
1203
|
+
const hit =
|
|
1204
|
+
matches.find((o) => isActiveOrderStatus(o.status)) ?? matches[0]
|
|
1205
|
+
if (hit === undefined) {
|
|
1206
|
+
throw notFound()
|
|
1207
|
+
}
|
|
1208
|
+
return hit
|
|
1209
|
+
}
|
|
1210
|
+
|
|
1211
|
+
const byOrderIndex = (o: LtOrder): boolean =>
|
|
1212
|
+
String(o.order_index) === params.id
|
|
1213
|
+
|
|
1214
|
+
const active = await retryOnRevoked(opts, params.address, token, (t) =>
|
|
1215
|
+
fetchActiveOrders(
|
|
1216
|
+
client,
|
|
1217
|
+
t,
|
|
1218
|
+
account.index,
|
|
1219
|
+
LIGHTER_ALL_MARKETS_WILDCARD
|
|
1220
|
+
)
|
|
1221
|
+
)
|
|
1222
|
+
const activeHit = active.orders.find(byOrderIndex)
|
|
1223
|
+
if (activeHit !== undefined) {
|
|
1224
|
+
return detail(activeHit)
|
|
1146
1225
|
}
|
|
1147
1226
|
|
|
1148
1227
|
const inactive = await retryOnRevoked(opts, params.address, token, (t) =>
|
|
@@ -1152,17 +1231,11 @@ export const createLighterProvider = (
|
|
|
1152
1231
|
limit: INACTIVE_ORDERS_LOOKUP_LIMIT,
|
|
1153
1232
|
})
|
|
1154
1233
|
)
|
|
1155
|
-
const
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
if (hit !== undefined) {
|
|
1159
|
-
return mapOrderDetail(hit, registry.require(String(hit.market_index)))
|
|
1234
|
+
const inactiveHit = wireList(inactive.orders).find(byOrderIndex)
|
|
1235
|
+
if (inactiveHit === undefined) {
|
|
1236
|
+
throw notFound()
|
|
1160
1237
|
}
|
|
1161
|
-
|
|
1162
|
-
throw new PerpsError(
|
|
1163
|
-
PerpsErrorCode.OrderNotFound,
|
|
1164
|
-
`Lighter order ${params.id} not found for ${params.address}`
|
|
1165
|
-
)
|
|
1238
|
+
return detail(inactiveHit)
|
|
1166
1239
|
},
|
|
1167
1240
|
|
|
1168
1241
|
async getFills(
|
package/src/accountConfig.ts
CHANGED
|
@@ -5,6 +5,10 @@ import type {
|
|
|
5
5
|
ProviderAction,
|
|
6
6
|
} from '@lifi/perps-types'
|
|
7
7
|
import { ActionType, PerpsErrorCode } from '@lifi/perps-types'
|
|
8
|
+
import {
|
|
9
|
+
LT_ACCOUNT_TRADING_MODE_SIMPLE,
|
|
10
|
+
LT_ACCOUNT_TRADING_MODE_UNIFIED,
|
|
11
|
+
} from './types/action.js'
|
|
8
12
|
|
|
9
13
|
function assertNever(value: never): never {
|
|
10
14
|
throw new Error(
|
|
@@ -27,15 +31,11 @@ const ACCOUNT_TYPE_INT_TO_WIRE: Readonly<Record<number, string>> = {
|
|
|
27
31
|
[LIGHTER_ACCOUNT_TYPE_PREMIUM]: 'premium',
|
|
28
32
|
}
|
|
29
33
|
|
|
30
|
-
//
|
|
31
|
-
//
|
|
32
|
-
// the backend descriptor's ParamOption values. An unmapped int projects to null.
|
|
33
|
-
const LIGHTER_ACCOUNT_MODE_SIMPLE = 0
|
|
34
|
-
const LIGHTER_ACCOUNT_MODE_UNIFIED = 1
|
|
35
|
-
|
|
34
|
+
// Wire strings match the backend descriptor's ParamOption values. An unmapped
|
|
35
|
+
// int projects to null.
|
|
36
36
|
const ACCOUNT_MODE_INT_TO_WIRE: Readonly<Record<number, string>> = {
|
|
37
|
-
[
|
|
38
|
-
[
|
|
37
|
+
[LT_ACCOUNT_TRADING_MODE_SIMPLE]: 'simpleTradingAccount',
|
|
38
|
+
[LT_ACCOUNT_TRADING_MODE_UNIFIED]: 'unifiedTradingAccount',
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
/**
|
package/src/constants.ts
CHANGED
|
@@ -204,13 +204,26 @@ export const LIGHTER_BASE_FEE_TIER: FeeTier = { maker: '0', taker: '0' }
|
|
|
204
204
|
|
|
205
205
|
/**
|
|
206
206
|
* Wildcard `market_id` accepted by Lighter's per-market account endpoints to
|
|
207
|
-
* indicate "every market". Used on `
|
|
208
|
-
* `liquidations`.
|
|
207
|
+
* indicate "every market". Used on `accountActiveOrders`,
|
|
208
|
+
* `accountInactiveOrders` and `liquidations`. It is also the venue's own
|
|
209
|
+
* documented default for `accountActiveOrders.market_id`.
|
|
209
210
|
*
|
|
210
211
|
* @internal
|
|
211
212
|
*/
|
|
212
213
|
export const LIGHTER_ALL_MARKETS_WILDCARD = 255
|
|
213
214
|
|
|
215
|
+
/**
|
|
216
|
+
* Prefix that marks a `getOrder` id as a Lighter `client_order_index` rather
|
|
217
|
+
* than an `order_index`. Compose the id as
|
|
218
|
+
* `` `${LIGHTER_CLIENT_ORDER_INDEX_ID_PREFIX}${clientOrderIndex}` `` from the
|
|
219
|
+
* `clientOrderIndex` a signed placement step returns. Both references are
|
|
220
|
+
* decimal integers, so without the prefix `getOrder` could resolve one as the
|
|
221
|
+
* other and return an unrelated order.
|
|
222
|
+
*
|
|
223
|
+
* @public
|
|
224
|
+
*/
|
|
225
|
+
export const LIGHTER_CLIENT_ORDER_INDEX_ID_PREFIX = 'client_order_index:'
|
|
226
|
+
|
|
214
227
|
/**
|
|
215
228
|
* Lighter caps `limit` at 100 on positionFunding/liquidations endpoints.
|
|
216
229
|
*
|
|
@@ -235,6 +248,16 @@ const LIGHTER_INVALID_AUTH_CODE = 20013
|
|
|
235
248
|
*/
|
|
236
249
|
const LIGHTER_TOKEN_REVOKED_CODE = 61006
|
|
237
250
|
|
|
251
|
+
/**
|
|
252
|
+
* Lighter body `code` for the internal-error envelope it answers when a token's
|
|
253
|
+
* signature does not verify — the message reads `invalid signature`. Only the
|
|
254
|
+
* auth-gated read path treats it as an auth failure; a public read keeps the
|
|
255
|
+
* venue-error classification.
|
|
256
|
+
*
|
|
257
|
+
* @internal
|
|
258
|
+
*/
|
|
259
|
+
const LIGHTER_INVALID_SIGNATURE_CODE = 29500
|
|
260
|
+
|
|
238
261
|
/**
|
|
239
262
|
* Lighter body `code` values that mean success. Lighter is inconsistent per
|
|
240
263
|
* endpoint: `/api/v1/account` returns `code: 200`, most others return `code: 0`.
|
|
@@ -266,6 +289,7 @@ const LIGHTER_REFERRAL_ALREADY_USED_CODE = 41003
|
|
|
266
289
|
export {
|
|
267
290
|
LIGHTER_CODE_ACCOUNT_NOT_FOUND,
|
|
268
291
|
LIGHTER_INVALID_AUTH_CODE,
|
|
292
|
+
LIGHTER_INVALID_SIGNATURE_CODE,
|
|
269
293
|
LIGHTER_MUTATION_SUCCESS_CODE,
|
|
270
294
|
LIGHTER_REFERRAL_ALREADY_USED_CODE,
|
|
271
295
|
LIGHTER_SUCCESS_CODES,
|
package/src/index.ts
CHANGED
|
@@ -230,13 +230,30 @@ async function signStandardWasmAction(
|
|
|
230
230
|
apiKeyIndex: apiKey.apiKeyIndex,
|
|
231
231
|
accountIndex: apiKey.accountIndex,
|
|
232
232
|
})
|
|
233
|
+
const clientOrderIndex = clientOrderIndexOf(step.wasmSignParams)
|
|
233
234
|
return {
|
|
234
235
|
action: step.action,
|
|
235
236
|
wasmSignParams: step.wasmSignParams,
|
|
236
237
|
signedTx,
|
|
238
|
+
...(clientOrderIndex === undefined ? {} : { clientOrderIndex }),
|
|
237
239
|
}
|
|
238
240
|
}
|
|
239
241
|
|
|
242
|
+
/**
|
|
243
|
+
* Project the signed order's `client_order_index` so the caller can resolve it
|
|
244
|
+
* through `getOrder` before the venue assigns an `order_index`. Only the
|
|
245
|
+
* order-placing actions carry one, and the value is read back off the params
|
|
246
|
+
* that were signed rather than generated a second time.
|
|
247
|
+
*/
|
|
248
|
+
function clientOrderIndexOf(
|
|
249
|
+
wasmSignParams: Record<string, unknown>
|
|
250
|
+
): string | undefined {
|
|
251
|
+
const value = wasmSignParams.client_order_index
|
|
252
|
+
return typeof value === 'number' && Number.isInteger(value)
|
|
253
|
+
? String(value)
|
|
254
|
+
: undefined
|
|
255
|
+
}
|
|
256
|
+
|
|
240
257
|
/**
|
|
241
258
|
* REGISTER_API_KEY flow:
|
|
242
259
|
* 1. Look up the user's Lighter accountIndex (Lighter REST).
|
|
@@ -484,11 +501,11 @@ async function executeTokenAuthMutation(
|
|
|
484
501
|
}
|
|
485
502
|
}
|
|
486
503
|
|
|
487
|
-
const { path, params } = buildTokenAuthMutationRequest(step
|
|
504
|
+
const { path, params } = buildTokenAuthMutationRequest(step)
|
|
488
505
|
const { status, data } = await deps.apiClient.postForm<{
|
|
489
506
|
code?: number
|
|
490
507
|
message?: string
|
|
491
|
-
}>(path, params)
|
|
508
|
+
}>(path, authToken, params)
|
|
492
509
|
|
|
493
510
|
const code = data?.code
|
|
494
511
|
const referralAlreadyApplied =
|
|
@@ -533,14 +550,14 @@ async function readAppliedReferralCode(
|
|
|
533
550
|
}
|
|
534
551
|
|
|
535
552
|
/**
|
|
536
|
-
* Map a token-authenticated mutation step to its Lighter endpoint and
|
|
537
|
-
*
|
|
538
|
-
* `
|
|
553
|
+
* Map a token-authenticated mutation step to its Lighter endpoint and form
|
|
554
|
+
* body. The fields come from `wasmSignParams`; the auth token travels in the
|
|
555
|
+
* `Authorization` header, not the body.
|
|
539
556
|
*/
|
|
540
|
-
function buildTokenAuthMutationRequest(
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
557
|
+
function buildTokenAuthMutationRequest(step: WasmBlobActionStep): {
|
|
558
|
+
path: string
|
|
559
|
+
params: ApiParams
|
|
560
|
+
} {
|
|
544
561
|
const kind = step.wasmSignParams.kind
|
|
545
562
|
if (kind === 'changeAccountTier') {
|
|
546
563
|
const { account_index, new_tier } = step.wasmSignParams as {
|
|
@@ -555,7 +572,7 @@ function buildTokenAuthMutationRequest(
|
|
|
555
572
|
}
|
|
556
573
|
return {
|
|
557
574
|
path: '/api/v1/changeAccountTier',
|
|
558
|
-
params: {
|
|
575
|
+
params: { account_index, new_tier },
|
|
559
576
|
}
|
|
560
577
|
}
|
|
561
578
|
if (kind === 'referralUse') {
|
|
@@ -572,7 +589,7 @@ function buildTokenAuthMutationRequest(
|
|
|
572
589
|
}
|
|
573
590
|
return {
|
|
574
591
|
path: '/api/v1/referral/use',
|
|
575
|
-
params: {
|
|
592
|
+
params: { l1_address, referral_code, x },
|
|
576
593
|
}
|
|
577
594
|
}
|
|
578
595
|
throw new PerpsError(
|
package/src/types/account.ts
CHANGED
|
@@ -30,12 +30,18 @@ export type LtAccountPosition = {
|
|
|
30
30
|
/**
|
|
31
31
|
* Funding accrued since the position opened, signed from the account's point
|
|
32
32
|
* of view despite the field name: negative is funding the account paid and
|
|
33
|
-
* positive is funding it received. Lighter resets it to `"0"` on close
|
|
33
|
+
* positive is funding it received. Lighter resets it to `"0"` on close, and
|
|
34
|
+
* marks it `omitempty`, so a position that accrued no funding yet omits the
|
|
35
|
+
* field entirely; an absent value means zero.
|
|
34
36
|
*/
|
|
35
|
-
total_funding_paid_out
|
|
37
|
+
total_funding_paid_out?: string
|
|
36
38
|
margin_mode: number
|
|
37
39
|
allocated_margin: string
|
|
38
|
-
|
|
40
|
+
/**
|
|
41
|
+
* Funding-fee discount accrued on the position. Lighter marks it
|
|
42
|
+
* `omitempty`, so an absent field means zero.
|
|
43
|
+
*/
|
|
44
|
+
total_discount?: string
|
|
39
45
|
/** Lighter bit flags recording which margin settings the account set itself. */
|
|
40
46
|
margin_set_flag?: number
|
|
41
47
|
}
|
|
@@ -125,7 +131,12 @@ export interface LtDetailedAccount {
|
|
|
125
131
|
* treat `0` as "no transaction" and not as the Unix epoch.
|
|
126
132
|
*/
|
|
127
133
|
transaction_time: number
|
|
128
|
-
|
|
134
|
+
/**
|
|
135
|
+
* Lighter marks it `omitempty` here, so an absent field means
|
|
136
|
+
* `LT_ACCOUNT_TRADING_MODE_SIMPLE`, the wire zero value. `LtSubAccount`
|
|
137
|
+
* reports the same member without the omission.
|
|
138
|
+
*/
|
|
139
|
+
account_trading_mode?: number
|
|
129
140
|
account_index: number
|
|
130
141
|
name: string
|
|
131
142
|
description: string
|
package/src/types/action.ts
CHANGED
|
@@ -184,6 +184,21 @@ export const LT_MARGIN_MODE_CROSS = 0
|
|
|
184
184
|
*/
|
|
185
185
|
export const LT_MARGIN_MODE_ISOLATED = 1
|
|
186
186
|
|
|
187
|
+
/**
|
|
188
|
+
* Lighter account-trading-mode value for the Classic/Simple account, which
|
|
189
|
+
* segregates margin per market. The wire zero value, so an omitted
|
|
190
|
+
* `account_trading_mode` means this mode.
|
|
191
|
+
* @public
|
|
192
|
+
*/
|
|
193
|
+
export const LT_ACCOUNT_TRADING_MODE_SIMPLE = 0
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Lighter account-trading-mode value for the Unified Trading Account, which
|
|
197
|
+
* margins across assets.
|
|
198
|
+
* @public
|
|
199
|
+
*/
|
|
200
|
+
export const LT_ACCOUNT_TRADING_MODE_UNIFIED = 1
|
|
201
|
+
|
|
187
202
|
/**
|
|
188
203
|
* Lighter margin-direction value for removing margin.
|
|
189
204
|
* @public
|
package/src/types/fill.ts
CHANGED
|
@@ -41,8 +41,9 @@ export type LtTrade = {
|
|
|
41
41
|
transaction_time: number
|
|
42
42
|
// Per-counterparty position snapshot BEFORE the trade is applied. Signed
|
|
43
43
|
// strings: positive = long, negative = short, "0" / "0.00000" = flat.
|
|
44
|
-
|
|
45
|
-
|
|
44
|
+
// Lighter marks both `omitempty`, so an absent key means flat.
|
|
45
|
+
taker_position_size_before?: string
|
|
46
|
+
maker_position_size_before?: string
|
|
46
47
|
// Per-counterparty entry-quote (notional cost basis) snapshot BEFORE the
|
|
47
48
|
// trade. Paired with `*_position_size_before`, avg entry =
|
|
48
49
|
// entry_quote_before / |position_size_before|. Optional: absent on older
|
package/src/types/order.ts
CHANGED
|
@@ -69,6 +69,19 @@ export interface LtOrdersResponse {
|
|
|
69
69
|
orders: LtOrder[] | null
|
|
70
70
|
}
|
|
71
71
|
|
|
72
|
+
/**
|
|
73
|
+
* Order-lookup response from Lighter's `accountOrders` path. The lookup takes
|
|
74
|
+
* up to 20 `client_order_indexes` rather than a page, so it carries no cursor.
|
|
75
|
+
* It reaches the last 10K active orders with no time bound, and the last 1K
|
|
76
|
+
* inactive orders from the past 24 hours.
|
|
77
|
+
*
|
|
78
|
+
* @public
|
|
79
|
+
*/
|
|
80
|
+
export interface LtAccountOrdersResponse {
|
|
81
|
+
code: number
|
|
82
|
+
orders: LtOrder[] | null
|
|
83
|
+
}
|
|
84
|
+
|
|
72
85
|
/**
|
|
73
86
|
* Single order-book level returned by Lighter. Amounts and prices are decimal
|
|
74
87
|
* strings in the market's native precision; `order_expiry` is an absolute
|