@lifi/perps-sdk-provider-lighter 19.0.0 → 21.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/dist/cjs/LighterProvider.d.ts.map +1 -1
- package/dist/cjs/LighterProvider.js +30 -6
- package/dist/cjs/LighterProvider.js.map +1 -1
- package/dist/cjs/signers/LighterReadOnlyTokenManager.d.ts.map +1 -1
- package/dist/cjs/signers/LighterReadOnlyTokenManager.js +1 -0
- package/dist/cjs/signers/LighterReadOnlyTokenManager.js.map +1 -1
- package/dist/cjs/types/account.d.ts +8 -3
- package/dist/cjs/types/account.d.ts.map +1 -1
- package/dist/cjs/types/activity.d.ts +7 -6
- package/dist/cjs/types/activity.d.ts.map +1 -1
- package/dist/cjs/types/fill.d.ts +3 -1
- package/dist/cjs/types/fill.d.ts.map +1 -1
- package/dist/cjs/types/order.d.ts +2 -1
- package/dist/cjs/types/order.d.ts.map +1 -1
- package/dist/cjs/utils/apiClient.d.ts +1 -0
- package/dist/cjs/utils/apiClient.d.ts.map +1 -1
- package/dist/cjs/utils/apiClient.js +13 -5
- package/dist/cjs/utils/apiClient.js.map +1 -1
- package/dist/cjs/utils/fetchDetailedAccount.d.ts +2 -2
- package/dist/cjs/utils/fetchDetailedAccount.d.ts.map +1 -1
- package/dist/cjs/utils/fetchDetailedAccount.js +9 -3
- package/dist/cjs/utils/fetchDetailedAccount.js.map +1 -1
- package/dist/cjs/utils/ohlcvInterval.d.ts.map +1 -1
- package/dist/cjs/utils/ohlcvInterval.js +0 -1
- package/dist/cjs/utils/ohlcvInterval.js.map +1 -1
- package/dist/cjs/utils/wireList.d.ts +2 -0
- package/dist/cjs/utils/wireList.d.ts.map +1 -0
- package/dist/cjs/utils/wireList.js +6 -0
- package/dist/cjs/utils/wireList.js.map +1 -0
- package/dist/esm/LighterProvider.d.ts.map +1 -1
- package/dist/esm/LighterProvider.js +30 -6
- package/dist/esm/LighterProvider.js.map +1 -1
- package/dist/esm/constants.d.ts +1 -1
- package/dist/esm/constants.js +1 -1
- package/dist/esm/signers/LighterReadOnlyTokenManager.d.ts.map +1 -1
- package/dist/esm/signers/LighterReadOnlyTokenManager.js +3 -0
- package/dist/esm/signers/LighterReadOnlyTokenManager.js.map +1 -1
- package/dist/esm/types/account.d.ts +15 -3
- package/dist/esm/types/account.d.ts.map +1 -1
- package/dist/esm/types/account.js +1 -0
- package/dist/esm/types/account.js.map +1 -1
- package/dist/esm/types/activity.d.ts +8 -6
- package/dist/esm/types/activity.d.ts.map +1 -1
- package/dist/esm/types/activity.js +1 -0
- package/dist/esm/types/activity.js.map +1 -1
- package/dist/esm/types/fill.d.ts +5 -1
- package/dist/esm/types/fill.d.ts.map +1 -1
- package/dist/esm/types/fill.js +1 -0
- package/dist/esm/types/fill.js.map +1 -1
- package/dist/esm/types/order.d.ts +3 -1
- package/dist/esm/types/order.d.ts.map +1 -1
- package/dist/esm/types/order.js +1 -0
- package/dist/esm/types/order.js.map +1 -1
- package/dist/esm/utils/apiClient.d.ts +8 -8
- package/dist/esm/utils/apiClient.d.ts.map +1 -1
- package/dist/esm/utils/apiClient.js +24 -13
- package/dist/esm/utils/apiClient.js.map +1 -1
- package/dist/esm/utils/fetchDetailedAccount.d.ts +3 -2
- package/dist/esm/utils/fetchDetailedAccount.d.ts.map +1 -1
- package/dist/esm/utils/fetchDetailedAccount.js +10 -3
- package/dist/esm/utils/fetchDetailedAccount.js.map +1 -1
- package/dist/esm/utils/ohlcvInterval.d.ts.map +1 -1
- package/dist/esm/utils/ohlcvInterval.js +1 -2
- package/dist/esm/utils/ohlcvInterval.js.map +1 -1
- package/dist/esm/utils/wireList.d.ts +10 -0
- package/dist/esm/utils/wireList.d.ts.map +1 -0
- package/dist/esm/utils/wireList.js +10 -0
- package/dist/esm/utils/wireList.js.map +1 -0
- package/dist/types/LighterProvider.d.ts.map +1 -1
- package/dist/types/constants.d.ts +1 -1
- package/dist/types/signers/LighterReadOnlyTokenManager.d.ts.map +1 -1
- package/dist/types/types/account.d.ts +15 -3
- package/dist/types/types/account.d.ts.map +1 -1
- package/dist/types/types/activity.d.ts +8 -6
- package/dist/types/types/activity.d.ts.map +1 -1
- package/dist/types/types/fill.d.ts +5 -1
- package/dist/types/types/fill.d.ts.map +1 -1
- package/dist/types/types/order.d.ts +3 -1
- package/dist/types/types/order.d.ts.map +1 -1
- package/dist/types/utils/apiClient.d.ts +8 -8
- package/dist/types/utils/apiClient.d.ts.map +1 -1
- package/dist/types/utils/fetchDetailedAccount.d.ts +3 -2
- package/dist/types/utils/fetchDetailedAccount.d.ts.map +1 -1
- package/dist/types/utils/ohlcvInterval.d.ts.map +1 -1
- package/dist/types/utils/wireList.d.ts +10 -0
- package/dist/types/utils/wireList.d.ts.map +1 -0
- package/package.json +3 -3
- package/src/LighterProvider.ts +53 -16
- package/src/constants.ts +1 -1
- package/src/signers/LighterReadOnlyTokenManager.ts +3 -0
- package/src/types/account.ts +17 -3
- package/src/types/activity.ts +9 -6
- package/src/types/fill.ts +6 -1
- package/src/types/order.ts +4 -1
- package/src/utils/apiClient.ts +33 -13
- package/src/utils/fetchDetailedAccount.ts +13 -6
- package/src/utils/ohlcvInterval.ts +1 -2
- package/src/utils/wireList.ts +9 -0
|
@@ -43,9 +43,12 @@ export interface LighterApiClientOptions {
|
|
|
43
43
|
*
|
|
44
44
|
* Auth-gated endpoints (accountLimits, accountActiveOrders, deposit/history,
|
|
45
45
|
* withdraw/history, positionFunding, liquidations, transfer/history) take the
|
|
46
|
-
* Lighter read-only token
|
|
47
|
-
*
|
|
48
|
-
*
|
|
46
|
+
* Lighter read-only token in the `Authorization` header, which is the only
|
|
47
|
+
* auth channel Lighter's OpenAPI spec declares. The header makes such a read
|
|
48
|
+
* CORS-preflighted; both Lighter hosts answer the `OPTIONS` probe with
|
|
49
|
+
* `Authorization` in `Access-Control-Allow-Headers` and
|
|
50
|
+
* `Access-Control-Max-Age: 86400`, so a browser sends one preflight per day
|
|
51
|
+
* per origin.
|
|
49
52
|
*
|
|
50
53
|
* Lighter signals rate limits through HTTP 429 or HTTP 405. This client never
|
|
51
54
|
* retries such a response. It throws `RateLimitExceeded` and holds all network
|
|
@@ -72,11 +75,7 @@ export declare class LighterApiClient {
|
|
|
72
75
|
private assertNotRateLimited;
|
|
73
76
|
private buildUrl;
|
|
74
77
|
get<T>(path: string, params?: ApiParams): Promise<T>;
|
|
75
|
-
/**
|
|
76
|
-
* Auth-gated GET. The token is appended as the `auth` query parameter (per
|
|
77
|
-
* Lighter's OpenAPI spec); the `Authorization` header is intentionally NOT
|
|
78
|
-
* used — Lighter rejects it.
|
|
79
|
-
*/
|
|
78
|
+
/** Auth-gated GET carrying the Lighter token in the `Authorization` header. */
|
|
80
79
|
getAuthed<T>(path: string, authToken: string, params?: ApiParams): Promise<T>;
|
|
81
80
|
/**
|
|
82
81
|
* GET that surfaces the raw `{status, body}` pair without throwing on
|
|
@@ -87,6 +86,7 @@ export declare class LighterApiClient {
|
|
|
87
86
|
status: number;
|
|
88
87
|
data: T;
|
|
89
88
|
}>;
|
|
89
|
+
private sendGet;
|
|
90
90
|
/**
|
|
91
91
|
* Form-encoded POST to a Lighter mutation endpoint. Single-shot — never
|
|
92
92
|
* retried, since these are money/state writes whose outcome is unknown on a
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"apiClient.d.ts","sourceRoot":"","sources":["../../../src/utils/apiClient.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,UAAU,EACV,KAAK,mBAAmB,EACzB,MAAM,iBAAiB,CAAA;AAQxB,gBAAgB;AAChB,MAAM,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,CAAA;AAejE;;;;GAIG;AACH,qBAAa,wBAAyB,SAAQ,UAAU;CAAG;AAE3D;;;;GAIG;AACH,qBAAa,wBAAyB,SAAQ,UAAU;CAAG;
|
|
1
|
+
{"version":3,"file":"apiClient.d.ts","sourceRoot":"","sources":["../../../src/utils/apiClient.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,UAAU,EACV,KAAK,mBAAmB,EACzB,MAAM,iBAAiB,CAAA;AAQxB,gBAAgB;AAChB,MAAM,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,CAAA;AAejE;;;;GAIG;AACH,qBAAa,wBAAyB,SAAQ,UAAU;CAAG;AAE3D;;;;GAIG;AACH,qBAAa,wBAAyB,SAAQ,UAAU;CAAG;AAwB3D,gBAAgB;AAChB,eAAO,MAAM,sBAAsB,EAAE,mBAgBpC,CAAA;AAsBD;;;;;GAKG;AACH,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,MAAM,CAAA;CAChB;AAED,gBAAgB;AAChB,MAAM,WAAW,uBAAuB;IACtC,MAAM,CAAC,EAAE,WAAW,CAAA;IACpB,MAAM,CAAC,EAAE,mBAAmB,CAAA;IAC5B,SAAS,CAAC,EAAE,OAAO,KAAK,CAAA;IACxB,aAAa,CAAC,EAAE,oBAAoB,CAAA;CACrC;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAQ;IAChC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAyB;IAChD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAqB;IAC5C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA0B;IACpD,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAsB;IACpD,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAc;gBAEhC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,uBAAuB;IAqB9D;;;;OAIG;IACH,OAAO,CAAC,KAAK,EAAE,WAAW,GAAG,GAAG,EAAE,IAAI,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,QAAQ,CAAC;IAIxE,OAAO,CAAC,cAAc;IAOtB,OAAO,CAAC,oBAAoB;IAM5B,OAAO,CAAC,oBAAoB;IAK5B,OAAO,CAAC,QAAQ;IAaV,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,SAAS,GAAG,OAAO,CAAC,CAAC,CAAC;IAI1D,+EAA+E;IACzE,SAAS,CAAC,CAAC,EACf,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,MAAM,EACjB,MAAM,GAAE,SAAc,GACrB,OAAO,CAAC,CAAC,CAAC;IAoBb;;;;OAIG;IACG,aAAa,CAAC,CAAC,EACnB,IAAI,EAAE,MAAM,EACZ,MAAM,CAAC,EAAE,SAAS,GACjB,OAAO,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,CAAC,CAAA;KAAE,CAAC;YAIzB,OAAO;IAqBrB;;;;;OAKG;IACG,QAAQ,CAAC,CAAC,EACd,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,SAAS,GAChB,OAAO,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,CAAC,CAAA;KAAE,CAAC;YAgBzB,UAAU;IAMxB;;;;OAIG;IACH,OAAO,CAAC,QAAQ;CAejB"}
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import type { Address } from 'viem';
|
|
2
|
-
import type {
|
|
2
|
+
import type { LtAccount } from '../types/index.js';
|
|
3
3
|
import type { LighterApiClient } from './apiClient.js';
|
|
4
4
|
/**
|
|
5
5
|
* Look up the Lighter account for an L1 address via `/api/v1/account`,
|
|
6
6
|
* discriminating Lighter's account-not-found body code from generic failures.
|
|
7
|
+
* The returned account carries real arrays for `positions` and `assets`.
|
|
7
8
|
*
|
|
8
9
|
* @public
|
|
9
10
|
*/
|
|
10
|
-
export declare const fetchDetailedAccount: (client: LighterApiClient, address: Address) => Promise<
|
|
11
|
+
export declare const fetchDetailedAccount: (client: LighterApiClient, address: Address) => Promise<LtAccount>;
|
|
11
12
|
//# sourceMappingURL=fetchDetailedAccount.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fetchDetailedAccount.d.ts","sourceRoot":"","sources":["../../../src/utils/fetchDetailedAccount.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAA;AAEnC,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"fetchDetailedAccount.d.ts","sourceRoot":"","sources":["../../../src/utils/fetchDetailedAccount.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAA;AAEnC,OAAO,KAAK,EAAE,SAAS,EAAqB,MAAM,mBAAmB,CAAA;AACrE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAA;AAGtD;;;;;;GAMG;AACH,eAAO,MAAM,oBAAoB,GAC/B,QAAQ,gBAAgB,EACxB,SAAS,OAAO,KACf,OAAO,CAAC,SAAS,CAkCnB,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ohlcvInterval.d.ts","sourceRoot":"","sources":["../../../src/utils/ohlcvInterval.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAA;
|
|
1
|
+
{"version":3,"file":"ohlcvInterval.d.ts","sourceRoot":"","sources":["../../../src/utils/ohlcvInterval.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAA;AAqBtD;;;;;GAKG;AACH,eAAO,MAAM,WAAW,GAAI,UAAU,aAAa,KAAG,MAUrD,CAAA"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Read a Lighter wire list member. Lighter serializes an empty list as JSON
|
|
3
|
+
* `null` rather than `[]`, so every list the SDK reads off a REST body passes
|
|
4
|
+
* through this at the response boundary and every mapper downstream works on a
|
|
5
|
+
* real array.
|
|
6
|
+
*
|
|
7
|
+
* @internal
|
|
8
|
+
*/
|
|
9
|
+
export declare const wireList: <T>(value: T[] | null | undefined) => T[];
|
|
10
|
+
//# sourceMappingURL=wireList.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wireList.d.ts","sourceRoot":"","sources":["../../../src/utils/wireList.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,eAAO,MAAM,QAAQ,GAAI,CAAC,EAAE,OAAO,CAAC,EAAE,GAAG,IAAI,GAAG,SAAS,KAAG,CAAC,EAAiB,CAAA"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lifi/perps-sdk-provider-lighter",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "21.0.0",
|
|
4
4
|
"description": "Lighter provider plugin for @lifi/perps-sdk",
|
|
5
5
|
"homepage": "https://github.com/lifinance/perps-sdk",
|
|
6
6
|
"bugs": {
|
|
@@ -44,10 +44,10 @@
|
|
|
44
44
|
"dependencies": {
|
|
45
45
|
"big.js": "^7.0.1",
|
|
46
46
|
"viem": "^2.55.18",
|
|
47
|
-
"@lifi/perps-types": "^12.0.
|
|
47
|
+
"@lifi/perps-types": "^12.0.1"
|
|
48
48
|
},
|
|
49
49
|
"peerDependencies": {
|
|
50
|
-
"@lifi/perps-sdk": "^
|
|
50
|
+
"@lifi/perps-sdk": "^12.1.0"
|
|
51
51
|
},
|
|
52
52
|
"publishConfig": {
|
|
53
53
|
"access": "public"
|
package/src/LighterProvider.ts
CHANGED
|
@@ -86,15 +86,21 @@ import {
|
|
|
86
86
|
lighterSignActions,
|
|
87
87
|
} from './signers/signActions.js'
|
|
88
88
|
import type {
|
|
89
|
+
LtAccount,
|
|
89
90
|
LtAccountLimits,
|
|
91
|
+
LtDepositHistoryItem,
|
|
90
92
|
LtDepositHistoryResponse,
|
|
91
|
-
LtDetailedAccount,
|
|
92
93
|
LtDetailedAccountPosition,
|
|
94
|
+
LtLiquidation,
|
|
93
95
|
LtLiquidationsResponse,
|
|
96
|
+
LtOrder,
|
|
94
97
|
LtOrdersResponse,
|
|
98
|
+
LtPositionFunding,
|
|
95
99
|
LtPositionFundingsResponse,
|
|
96
100
|
LtTradesResponse,
|
|
101
|
+
LtTransfer,
|
|
97
102
|
LtTransferHistoryResponse,
|
|
103
|
+
LtWithdrawHistoryItem,
|
|
98
104
|
LtWithdrawHistoryResponse,
|
|
99
105
|
} from './types/index.js'
|
|
100
106
|
import { LT_MARGIN_MODE_CROSS, LT_MARGIN_MODE_ISOLATED } from './types/index.js'
|
|
@@ -132,6 +138,7 @@ import {
|
|
|
132
138
|
fetchRegisteredApiKey,
|
|
133
139
|
normalizeLighterPublicKey,
|
|
134
140
|
} from './utils/registeredApiKey.js'
|
|
141
|
+
import { wireList } from './utils/wireList.js'
|
|
135
142
|
|
|
136
143
|
const ZERO_FEE_TIER = { maker: '0', taker: '0' }
|
|
137
144
|
|
|
@@ -560,19 +567,21 @@ export const createLighterProvider = (
|
|
|
560
567
|
account_index: accountIndex,
|
|
561
568
|
})
|
|
562
569
|
|
|
563
|
-
const fetchActiveOrdersForMarket = (
|
|
570
|
+
const fetchActiveOrdersForMarket = async (
|
|
564
571
|
client: LighterApiClient,
|
|
565
572
|
authToken: string,
|
|
566
573
|
accountIndex: number,
|
|
567
574
|
marketId: number
|
|
568
|
-
): Promise<LtOrdersResponse> =>
|
|
569
|
-
client.getAuthed<LtOrdersResponse>(
|
|
575
|
+
): Promise<LtOrdersResponse & { orders: LtOrder[] }> => {
|
|
576
|
+
const response = await client.getAuthed<LtOrdersResponse>(
|
|
570
577
|
'/api/v1/accountActiveOrders',
|
|
571
578
|
authToken,
|
|
572
579
|
{ account_index: accountIndex, market_id: marketId }
|
|
573
580
|
)
|
|
581
|
+
return { ...response, orders: wireList(response.orders) }
|
|
582
|
+
}
|
|
574
583
|
|
|
575
|
-
const deriveOrderBearingMarketIds = (account:
|
|
584
|
+
const deriveOrderBearingMarketIds = (account: LtAccount): number[] =>
|
|
576
585
|
account.positions
|
|
577
586
|
.filter((p) => orderCountFor(p) > 0)
|
|
578
587
|
.map((p) => p.market_id)
|
|
@@ -585,11 +594,15 @@ export const createLighterProvider = (
|
|
|
585
594
|
requested: ActivityType[] | undefined,
|
|
586
595
|
inputCursor: LighterActivityCursor | undefined
|
|
587
596
|
): Promise<{
|
|
588
|
-
deposits: LtDepositHistoryResponse
|
|
589
|
-
withdraws: LtWithdrawHistoryResponse
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
597
|
+
deposits: LtDepositHistoryResponse & { deposits: LtDepositHistoryItem[] }
|
|
598
|
+
withdraws: LtWithdrawHistoryResponse & {
|
|
599
|
+
withdraws: LtWithdrawHistoryItem[]
|
|
600
|
+
}
|
|
601
|
+
fundings: LtPositionFundingsResponse & {
|
|
602
|
+
position_fundings: LtPositionFunding[]
|
|
603
|
+
}
|
|
604
|
+
liquidations: LtLiquidationsResponse & { liquidations: LtLiquidation[] }
|
|
605
|
+
transfers: LtTransferHistoryResponse & { transfers: LtTransfer[] }
|
|
593
606
|
}> => {
|
|
594
607
|
const wantsType = (t: ActivityType): boolean =>
|
|
595
608
|
requested === undefined || requested.includes(t)
|
|
@@ -643,7 +656,6 @@ export const createLighterProvider = (
|
|
|
643
656
|
token,
|
|
644
657
|
{
|
|
645
658
|
account_index: accountIndex,
|
|
646
|
-
market_id: LIGHTER_ALL_MARKETS_WILDCARD,
|
|
647
659
|
limit: LIGHTER_HISTORY_PAGE_SIZE,
|
|
648
660
|
...cursorParam('fundings'),
|
|
649
661
|
}
|
|
@@ -670,7 +682,30 @@ export const createLighterProvider = (
|
|
|
670
682
|
: Promise.resolve(empty({ code: 0, transfers: [] })),
|
|
671
683
|
])
|
|
672
684
|
|
|
673
|
-
return {
|
|
685
|
+
return {
|
|
686
|
+
deposits: {
|
|
687
|
+
...deposits,
|
|
688
|
+
deposits: wireList<LtDepositHistoryItem>(deposits.deposits),
|
|
689
|
+
},
|
|
690
|
+
withdraws: {
|
|
691
|
+
...withdraws,
|
|
692
|
+
withdraws: wireList<LtWithdrawHistoryItem>(withdraws.withdraws),
|
|
693
|
+
},
|
|
694
|
+
fundings: {
|
|
695
|
+
...fundings,
|
|
696
|
+
position_fundings: wireList<LtPositionFunding>(
|
|
697
|
+
fundings.position_fundings
|
|
698
|
+
),
|
|
699
|
+
},
|
|
700
|
+
liquidations: {
|
|
701
|
+
...liquidations,
|
|
702
|
+
liquidations: wireList<LtLiquidation>(liquidations.liquidations),
|
|
703
|
+
},
|
|
704
|
+
transfers: {
|
|
705
|
+
...transfers,
|
|
706
|
+
transfers: wireList<LtTransfer>(transfers.transfers),
|
|
707
|
+
},
|
|
708
|
+
}
|
|
674
709
|
}
|
|
675
710
|
|
|
676
711
|
const resolveAccountExists = async (
|
|
@@ -924,7 +959,7 @@ export const createLighterProvider = (
|
|
|
924
959
|
if (params.market.categoryId === LIGHTER_SPOT_CATEGORY_ID) {
|
|
925
960
|
return undefined
|
|
926
961
|
}
|
|
927
|
-
let account:
|
|
962
|
+
let account: LtAccount
|
|
928
963
|
try {
|
|
929
964
|
account = await fetchDetailedAccount(apiClient(opts), params.address)
|
|
930
965
|
} catch (err) {
|
|
@@ -1117,7 +1152,9 @@ export const createLighterProvider = (
|
|
|
1117
1152
|
limit: INACTIVE_ORDERS_LOOKUP_LIMIT,
|
|
1118
1153
|
})
|
|
1119
1154
|
)
|
|
1120
|
-
const hit = inactive.orders.find(
|
|
1155
|
+
const hit = wireList(inactive.orders).find(
|
|
1156
|
+
predicate as (o: unknown) => boolean
|
|
1157
|
+
)
|
|
1121
1158
|
if (hit !== undefined) {
|
|
1122
1159
|
return mapOrderDetail(hit, registry.require(String(hit.market_index)))
|
|
1123
1160
|
}
|
|
@@ -1170,7 +1207,7 @@ export const createLighterProvider = (
|
|
|
1170
1207
|
// drops only its own row instead of rejecting the whole page. The
|
|
1171
1208
|
// registry warns once per unresolved id. A delisted market still
|
|
1172
1209
|
// resolves, so its rows stay.
|
|
1173
|
-
const items = response.trades.flatMap((t): Fill[] => {
|
|
1210
|
+
const items = wireList(response.trades).flatMap((t): Fill[] => {
|
|
1174
1211
|
const market = registry.get(String(t.market_id))
|
|
1175
1212
|
if (market === undefined) {
|
|
1176
1213
|
return []
|
|
@@ -1297,7 +1334,7 @@ export const createLighterProvider = (
|
|
|
1297
1334
|
}
|
|
1298
1335
|
const price = toBigOrNull(l.trade.price)
|
|
1299
1336
|
const size = toBigOrNull(l.trade.size)
|
|
1300
|
-
const marginMode = l.info.positions.find(
|
|
1337
|
+
const marginMode = wireList(l.info.positions).find(
|
|
1301
1338
|
(p) => p.market_id === l.market_id
|
|
1302
1339
|
)?.margin_mode
|
|
1303
1340
|
// Lighter reports the margin mode as an integer, so the venue
|
package/src/constants.ts
CHANGED
|
@@ -204,7 +204,7 @@ 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 `accountInactiveOrders
|
|
207
|
+
* indicate "every market". Used on `accountInactiveOrders` and
|
|
208
208
|
* `liquidations`.
|
|
209
209
|
*
|
|
210
210
|
* @internal
|
|
@@ -218,9 +218,12 @@ const isLighterReadOnlyToken = (
|
|
|
218
218
|
>
|
|
219
219
|
// Accept a record that carries no `tokenId`: rejecting one would discard a
|
|
220
220
|
// usable token and force an unnecessary replacement.
|
|
221
|
+
// A token carrying a line break cannot go in the `Authorization` header, so
|
|
222
|
+
// reject the record and let the manager create a replacement.
|
|
221
223
|
return (
|
|
222
224
|
typeof token === 'string' &&
|
|
223
225
|
token.length > 0 &&
|
|
226
|
+
!/[\r\n]/.test(token) &&
|
|
224
227
|
typeof expiry === 'number' &&
|
|
225
228
|
Number.isFinite(expiry) &&
|
|
226
229
|
(scope === 'single' || scope === 'all') &&
|
package/src/types/account.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// Account / balance shapes returned by Lighter's `/api/v1/account` and
|
|
2
2
|
// `/api/v1/accountLimits` endpoints.
|
|
3
|
+
// Lighter serializes an empty list as JSON `null`, so list members are nullable.
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* Per-market position row returned by Lighter's account endpoints.
|
|
@@ -35,6 +36,8 @@ export type LtAccountPosition = {
|
|
|
35
36
|
margin_mode: number
|
|
36
37
|
allocated_margin: string
|
|
37
38
|
total_discount: string
|
|
39
|
+
/** Lighter bit flags recording which margin settings the account set itself. */
|
|
40
|
+
margin_set_flag?: number
|
|
38
41
|
}
|
|
39
42
|
|
|
40
43
|
/**
|
|
@@ -126,8 +129,8 @@ export interface LtDetailedAccount {
|
|
|
126
129
|
account_index: number
|
|
127
130
|
name: string
|
|
128
131
|
description: string
|
|
129
|
-
positions: LtDetailedAccountPosition[]
|
|
130
|
-
assets: LtAccountAsset[]
|
|
132
|
+
positions: LtDetailedAccountPosition[] | null
|
|
133
|
+
assets: LtAccountAsset[] | null
|
|
131
134
|
total_asset_value: string
|
|
132
135
|
cross_asset_value: string
|
|
133
136
|
/** Initial margin locked by cross positions; isolated positions carry
|
|
@@ -136,6 +139,17 @@ export interface LtDetailedAccount {
|
|
|
136
139
|
approved_integrators?: LtApprovedIntegrator[]
|
|
137
140
|
}
|
|
138
141
|
|
|
142
|
+
/**
|
|
143
|
+
* A detailed account whose wire lists `fetchDetailedAccount` has normalized to
|
|
144
|
+
* real arrays. Every SDK read of an account works on this shape.
|
|
145
|
+
*
|
|
146
|
+
* @public
|
|
147
|
+
*/
|
|
148
|
+
export type LtAccount = LtDetailedAccount & {
|
|
149
|
+
positions: LtDetailedAccountPosition[]
|
|
150
|
+
assets: LtAccountAsset[]
|
|
151
|
+
}
|
|
152
|
+
|
|
139
153
|
/**
|
|
140
154
|
* Response envelope for a detailed-account lookup. `code` is Lighter's
|
|
141
155
|
* endpoint result code and `total` is the number of account rows returned.
|
|
@@ -145,7 +159,7 @@ export interface LtDetailedAccount {
|
|
|
145
159
|
export interface LtDetailedAccountsResponse {
|
|
146
160
|
code: number
|
|
147
161
|
total: number
|
|
148
|
-
accounts: LtDetailedAccount[]
|
|
162
|
+
accounts: LtDetailedAccount[] | null
|
|
149
163
|
}
|
|
150
164
|
|
|
151
165
|
/**
|
package/src/types/activity.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// Activity history shapes returned by Lighter's REST API
|
|
2
2
|
// (deposits, withdrawals, funding payments, liquidations, transfers).
|
|
3
|
+
// Lighter serializes an empty list as JSON `null`, so list members are nullable.
|
|
3
4
|
|
|
4
5
|
import type { LtAccountPosition } from './account.js'
|
|
5
6
|
|
|
@@ -27,7 +28,7 @@ export interface LtDepositHistoryItem {
|
|
|
27
28
|
*/
|
|
28
29
|
export interface LtDepositHistoryResponse {
|
|
29
30
|
code: number
|
|
30
|
-
deposits: LtDepositHistoryItem[]
|
|
31
|
+
deposits: LtDepositHistoryItem[] | null
|
|
31
32
|
cursor?: string
|
|
32
33
|
}
|
|
33
34
|
|
|
@@ -56,7 +57,7 @@ export interface LtWithdrawHistoryItem {
|
|
|
56
57
|
*/
|
|
57
58
|
export interface LtWithdrawHistoryResponse {
|
|
58
59
|
code: number
|
|
59
|
-
withdraws: LtWithdrawHistoryItem[]
|
|
60
|
+
withdraws: LtWithdrawHistoryItem[] | null
|
|
60
61
|
cursor?: string
|
|
61
62
|
}
|
|
62
63
|
|
|
@@ -80,6 +81,8 @@ export interface LtPositionFunding {
|
|
|
80
81
|
rate: string
|
|
81
82
|
position_size: string
|
|
82
83
|
position_side: string
|
|
84
|
+
/** Funding-fee discount Lighter applied to `change`, as a decimal string. */
|
|
85
|
+
discount?: string
|
|
83
86
|
}
|
|
84
87
|
|
|
85
88
|
/**
|
|
@@ -90,7 +93,7 @@ export interface LtPositionFunding {
|
|
|
90
93
|
*/
|
|
91
94
|
export interface LtPositionFundingsResponse {
|
|
92
95
|
code: number
|
|
93
|
-
position_fundings: LtPositionFunding[]
|
|
96
|
+
position_fundings: LtPositionFunding[] | null
|
|
94
97
|
next_cursor?: string
|
|
95
98
|
}
|
|
96
99
|
|
|
@@ -142,7 +145,7 @@ export type LtLiquidationPosition = Pick<
|
|
|
142
145
|
* @public
|
|
143
146
|
*/
|
|
144
147
|
export interface LtLiquidationInfo {
|
|
145
|
-
positions: LtLiquidationPosition[]
|
|
148
|
+
positions: LtLiquidationPosition[] | null
|
|
146
149
|
risk_info_before: {
|
|
147
150
|
// Lighter's OpenAPI does not require this member, so an account without
|
|
148
151
|
// cross exposure may omit it.
|
|
@@ -176,7 +179,7 @@ export interface LtLiquidation {
|
|
|
176
179
|
*/
|
|
177
180
|
export interface LtLiquidationsResponse {
|
|
178
181
|
code: number
|
|
179
|
-
liquidations: LtLiquidation[]
|
|
182
|
+
liquidations: LtLiquidation[] | null
|
|
180
183
|
next_cursor?: string
|
|
181
184
|
}
|
|
182
185
|
|
|
@@ -212,6 +215,6 @@ export interface LtTransfer {
|
|
|
212
215
|
*/
|
|
213
216
|
export interface LtTransferHistoryResponse {
|
|
214
217
|
code: number
|
|
215
|
-
transfers: LtTransfer[]
|
|
218
|
+
transfers: LtTransfer[] | null
|
|
216
219
|
cursor?: string
|
|
217
220
|
}
|
package/src/types/fill.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
// Trade (fill) shapes returned by Lighter's `/api/v1/trades` endpoint.
|
|
2
|
+
// Lighter serializes an empty list as JSON `null`, so list members are nullable.
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* Public trade/fill row returned by Lighter's `/api/v1/trades` endpoint.
|
|
@@ -54,6 +55,10 @@ export type LtTrade = {
|
|
|
54
55
|
// older trade rows that predate the field.
|
|
55
56
|
taker_initial_margin_fraction_before?: number
|
|
56
57
|
maker_initial_margin_fraction_before?: number
|
|
58
|
+
/** Version of the resting ask order the trade matched against. */
|
|
59
|
+
ask_order_version?: number
|
|
60
|
+
/** Version of the resting bid order the trade matched against. */
|
|
61
|
+
bid_order_version?: number
|
|
57
62
|
}
|
|
58
63
|
|
|
59
64
|
/**
|
|
@@ -65,5 +70,5 @@ export type LtTrade = {
|
|
|
65
70
|
export interface LtTradesResponse {
|
|
66
71
|
code: number
|
|
67
72
|
next_cursor: string
|
|
68
|
-
trades: LtTrade[]
|
|
73
|
+
trades: LtTrade[] | null
|
|
69
74
|
}
|
package/src/types/order.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
// Order shapes returned by Lighter's REST API.
|
|
2
|
+
// Lighter serializes an empty list as JSON `null`, so list members are nullable.
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* Order payload returned by Lighter's REST API. Amounts and prices are decimal
|
|
@@ -46,6 +47,8 @@ export type LtOrder = {
|
|
|
46
47
|
timestamp: number
|
|
47
48
|
created_at: number
|
|
48
49
|
updated_at: number
|
|
50
|
+
/** Lighter bit flags recording the order's wire options. */
|
|
51
|
+
order_flags?: number
|
|
49
52
|
/**
|
|
50
53
|
* Unix timestamp in microseconds. Lighter documents no unit, and its public
|
|
51
54
|
* trade rows report this member in microseconds while the sibling
|
|
@@ -63,7 +66,7 @@ export type LtOrder = {
|
|
|
63
66
|
export interface LtOrdersResponse {
|
|
64
67
|
code: number
|
|
65
68
|
next_cursor: string
|
|
66
|
-
orders: LtOrder[]
|
|
69
|
+
orders: LtOrder[] | null
|
|
67
70
|
}
|
|
68
71
|
|
|
69
72
|
/**
|
package/src/utils/apiClient.ts
CHANGED
|
@@ -48,6 +48,20 @@ const isLighterAuthRejection = (status: number, data: unknown): boolean =>
|
|
|
48
48
|
const isLighterTokenRevoked = (data: unknown): boolean =>
|
|
49
49
|
lighterBodyErrorCode(data) === LIGHTER_TOKEN_REVOKED_CODE
|
|
50
50
|
|
|
51
|
+
/**
|
|
52
|
+
* A CR or LF in a token would split the request when it reaches a
|
|
53
|
+
* `fetchImpl` that does not validate header values itself.
|
|
54
|
+
*/
|
|
55
|
+
const assertHeaderSafe = (authToken: string): string => {
|
|
56
|
+
if (/[\r\n]/.test(authToken)) {
|
|
57
|
+
throw new PerpsError(
|
|
58
|
+
PerpsErrorCode.ValidationError,
|
|
59
|
+
'Lighter auth token carries a line break and cannot go in a header'
|
|
60
|
+
)
|
|
61
|
+
}
|
|
62
|
+
return authToken
|
|
63
|
+
}
|
|
64
|
+
|
|
51
65
|
/** @internal */
|
|
52
66
|
export const LIGHTER_RETRY_DEFAULTS: ResolvedRetryPolicy = {
|
|
53
67
|
enabled: true,
|
|
@@ -115,9 +129,12 @@ export interface LighterApiClientOptions {
|
|
|
115
129
|
*
|
|
116
130
|
* Auth-gated endpoints (accountLimits, accountActiveOrders, deposit/history,
|
|
117
131
|
* withdraw/history, positionFunding, liquidations, transfer/history) take the
|
|
118
|
-
* Lighter read-only token
|
|
119
|
-
*
|
|
120
|
-
*
|
|
132
|
+
* Lighter read-only token in the `Authorization` header, which is the only
|
|
133
|
+
* auth channel Lighter's OpenAPI spec declares. The header makes such a read
|
|
134
|
+
* CORS-preflighted; both Lighter hosts answer the `OPTIONS` probe with
|
|
135
|
+
* `Authorization` in `Access-Control-Allow-Headers` and
|
|
136
|
+
* `Access-Control-Max-Age: 86400`, so a browser sends one preflight per day
|
|
137
|
+
* per origin.
|
|
121
138
|
*
|
|
122
139
|
* Lighter signals rate limits through HTTP 429 or HTTP 405. This client never
|
|
123
140
|
* retries such a response. It throws `RateLimitExceeded` and holds all network
|
|
@@ -198,19 +215,14 @@ export class LighterApiClient {
|
|
|
198
215
|
return this.getChecked<T>(path, params)
|
|
199
216
|
}
|
|
200
217
|
|
|
201
|
-
/**
|
|
202
|
-
* Auth-gated GET. The token is appended as the `auth` query parameter (per
|
|
203
|
-
* Lighter's OpenAPI spec); the `Authorization` header is intentionally NOT
|
|
204
|
-
* used — Lighter rejects it.
|
|
205
|
-
*/
|
|
218
|
+
/** Auth-gated GET carrying the Lighter token in the `Authorization` header. */
|
|
206
219
|
async getAuthed<T>(
|
|
207
220
|
path: string,
|
|
208
221
|
authToken: string,
|
|
209
222
|
params: ApiParams = {}
|
|
210
223
|
): Promise<T> {
|
|
211
|
-
const { status, data } = await this.
|
|
212
|
-
|
|
213
|
-
auth: authToken,
|
|
224
|
+
const { status, data } = await this.sendGet<unknown>(path, params, {
|
|
225
|
+
Authorization: assertHeaderSafe(authToken),
|
|
214
226
|
})
|
|
215
227
|
if (isLighterTokenRevoked(data)) {
|
|
216
228
|
throw new LighterTokenRevokedError(
|
|
@@ -236,12 +248,20 @@ export class LighterApiClient {
|
|
|
236
248
|
async getWithStatus<T>(
|
|
237
249
|
path: string,
|
|
238
250
|
params?: ApiParams
|
|
251
|
+
): Promise<{ status: number; data: T }> {
|
|
252
|
+
return this.sendGet<T>(path, params)
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
private async sendGet<T>(
|
|
256
|
+
path: string,
|
|
257
|
+
params?: ApiParams,
|
|
258
|
+
headers?: Record<string, string>
|
|
239
259
|
): Promise<{ status: number; data: T }> {
|
|
240
260
|
this.assertRequestAllowed()
|
|
241
261
|
const url = this.buildUrl(path, params)
|
|
242
262
|
const response = await fetchWithRetry(
|
|
243
263
|
url,
|
|
244
|
-
{},
|
|
264
|
+
headers === undefined ? {} : { headers },
|
|
245
265
|
{
|
|
246
266
|
policy: this.policy,
|
|
247
267
|
fetchImpl: this.fetchWithHold,
|
|
@@ -279,7 +299,7 @@ export class LighterApiClient {
|
|
|
279
299
|
}
|
|
280
300
|
|
|
281
301
|
private async getChecked<T>(path: string, params?: ApiParams): Promise<T> {
|
|
282
|
-
const { status, data } = await this.
|
|
302
|
+
const { status, data } = await this.sendGet<unknown>(path, params)
|
|
283
303
|
this.assertOk(path, status, data)
|
|
284
304
|
return data as T
|
|
285
305
|
}
|
|
@@ -2,22 +2,24 @@ import { PerpsError } from '@lifi/perps-sdk'
|
|
|
2
2
|
import { PerpsErrorCode } from '@lifi/perps-types'
|
|
3
3
|
import type { Address } from 'viem'
|
|
4
4
|
import { LIGHTER_CODE_ACCOUNT_NOT_FOUND } from '../constants.js'
|
|
5
|
-
import type { LtDetailedAccount } from '../types/index.js'
|
|
5
|
+
import type { LtAccount, LtDetailedAccount } from '../types/index.js'
|
|
6
6
|
import type { LighterApiClient } from './apiClient.js'
|
|
7
|
+
import { wireList } from './wireList.js'
|
|
7
8
|
|
|
8
9
|
/**
|
|
9
10
|
* Look up the Lighter account for an L1 address via `/api/v1/account`,
|
|
10
11
|
* discriminating Lighter's account-not-found body code from generic failures.
|
|
12
|
+
* The returned account carries real arrays for `positions` and `assets`.
|
|
11
13
|
*
|
|
12
14
|
* @public
|
|
13
15
|
*/
|
|
14
16
|
export const fetchDetailedAccount = async (
|
|
15
17
|
client: LighterApiClient,
|
|
16
18
|
address: Address
|
|
17
|
-
): Promise<
|
|
19
|
+
): Promise<LtAccount> => {
|
|
18
20
|
const { status, data } = await client.getWithStatus<{
|
|
19
21
|
code: number
|
|
20
|
-
accounts?: LtDetailedAccount[]
|
|
22
|
+
accounts?: LtDetailedAccount[] | null
|
|
21
23
|
message?: string
|
|
22
24
|
}>('/api/v1/account', { by: 'l1_address', value: address })
|
|
23
25
|
|
|
@@ -35,12 +37,17 @@ export const fetchDetailedAccount = async (
|
|
|
35
37
|
)
|
|
36
38
|
}
|
|
37
39
|
|
|
38
|
-
const accounts = data?.accounts
|
|
39
|
-
|
|
40
|
+
const accounts = wireList(data?.accounts)
|
|
41
|
+
const account = accounts[0]
|
|
42
|
+
if (account === undefined) {
|
|
40
43
|
throw new PerpsError(
|
|
41
44
|
PerpsErrorCode.AccountNotFound,
|
|
42
45
|
`No Lighter account found for address: ${address}`
|
|
43
46
|
)
|
|
44
47
|
}
|
|
45
|
-
return
|
|
48
|
+
return {
|
|
49
|
+
...account,
|
|
50
|
+
positions: wireList(account.positions),
|
|
51
|
+
assets: wireList(account.assets),
|
|
52
|
+
}
|
|
46
53
|
}
|
|
@@ -6,7 +6,7 @@ import { PerpsErrorCode } from '@lifi/perps-types'
|
|
|
6
6
|
* Map our `OhlcvInterval` literal to Lighter's `resolution` enum.
|
|
7
7
|
*
|
|
8
8
|
* Lighter exposes only the timeframes listed below. SDK intervals without a
|
|
9
|
-
* direct match (3m, 2h, 8h, 3d, 1M) raise a validation error rather than
|
|
9
|
+
* direct match (3m, 2h, 8h, 3d, 1w, 1M) raise a validation error rather than
|
|
10
10
|
* silently rounding — the caller picks a supported timeframe.
|
|
11
11
|
*/
|
|
12
12
|
const LIGHTER_SUPPORTED_INTERVALS: Partial<Record<OhlcvInterval, string>> = {
|
|
@@ -18,7 +18,6 @@ const LIGHTER_SUPPORTED_INTERVALS: Partial<Record<OhlcvInterval, string>> = {
|
|
|
18
18
|
'4h': '4h',
|
|
19
19
|
'12h': '12h',
|
|
20
20
|
'1d': '1d',
|
|
21
|
-
'1w': '1w',
|
|
22
21
|
}
|
|
23
22
|
|
|
24
23
|
/**
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Read a Lighter wire list member. Lighter serializes an empty list as JSON
|
|
3
|
+
* `null` rather than `[]`, so every list the SDK reads off a REST body passes
|
|
4
|
+
* through this at the response boundary and every mapper downstream works on a
|
|
5
|
+
* real array.
|
|
6
|
+
*
|
|
7
|
+
* @internal
|
|
8
|
+
*/
|
|
9
|
+
export const wireList = <T>(value: T[] | null | undefined): T[] => value ?? []
|