@lifi/perps-sdk-provider-lighter 20.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.
Files changed (98) hide show
  1. package/dist/cjs/LighterProvider.d.ts.map +1 -1
  2. package/dist/cjs/LighterProvider.js +30 -6
  3. package/dist/cjs/LighterProvider.js.map +1 -1
  4. package/dist/cjs/signers/LighterReadOnlyTokenManager.d.ts.map +1 -1
  5. package/dist/cjs/signers/LighterReadOnlyTokenManager.js +1 -0
  6. package/dist/cjs/signers/LighterReadOnlyTokenManager.js.map +1 -1
  7. package/dist/cjs/types/account.d.ts +8 -3
  8. package/dist/cjs/types/account.d.ts.map +1 -1
  9. package/dist/cjs/types/activity.d.ts +7 -6
  10. package/dist/cjs/types/activity.d.ts.map +1 -1
  11. package/dist/cjs/types/fill.d.ts +3 -1
  12. package/dist/cjs/types/fill.d.ts.map +1 -1
  13. package/dist/cjs/types/order.d.ts +2 -1
  14. package/dist/cjs/types/order.d.ts.map +1 -1
  15. package/dist/cjs/utils/apiClient.d.ts +1 -0
  16. package/dist/cjs/utils/apiClient.d.ts.map +1 -1
  17. package/dist/cjs/utils/apiClient.js +13 -5
  18. package/dist/cjs/utils/apiClient.js.map +1 -1
  19. package/dist/cjs/utils/fetchDetailedAccount.d.ts +2 -2
  20. package/dist/cjs/utils/fetchDetailedAccount.d.ts.map +1 -1
  21. package/dist/cjs/utils/fetchDetailedAccount.js +9 -3
  22. package/dist/cjs/utils/fetchDetailedAccount.js.map +1 -1
  23. package/dist/cjs/utils/ohlcvInterval.d.ts.map +1 -1
  24. package/dist/cjs/utils/ohlcvInterval.js +0 -1
  25. package/dist/cjs/utils/ohlcvInterval.js.map +1 -1
  26. package/dist/cjs/utils/wireList.d.ts +2 -0
  27. package/dist/cjs/utils/wireList.d.ts.map +1 -0
  28. package/dist/cjs/utils/wireList.js +6 -0
  29. package/dist/cjs/utils/wireList.js.map +1 -0
  30. package/dist/esm/LighterProvider.d.ts.map +1 -1
  31. package/dist/esm/LighterProvider.js +30 -6
  32. package/dist/esm/LighterProvider.js.map +1 -1
  33. package/dist/esm/constants.d.ts +1 -1
  34. package/dist/esm/constants.js +1 -1
  35. package/dist/esm/signers/LighterReadOnlyTokenManager.d.ts.map +1 -1
  36. package/dist/esm/signers/LighterReadOnlyTokenManager.js +3 -0
  37. package/dist/esm/signers/LighterReadOnlyTokenManager.js.map +1 -1
  38. package/dist/esm/types/account.d.ts +15 -3
  39. package/dist/esm/types/account.d.ts.map +1 -1
  40. package/dist/esm/types/account.js +1 -0
  41. package/dist/esm/types/account.js.map +1 -1
  42. package/dist/esm/types/activity.d.ts +8 -6
  43. package/dist/esm/types/activity.d.ts.map +1 -1
  44. package/dist/esm/types/activity.js +1 -0
  45. package/dist/esm/types/activity.js.map +1 -1
  46. package/dist/esm/types/fill.d.ts +5 -1
  47. package/dist/esm/types/fill.d.ts.map +1 -1
  48. package/dist/esm/types/fill.js +1 -0
  49. package/dist/esm/types/fill.js.map +1 -1
  50. package/dist/esm/types/order.d.ts +3 -1
  51. package/dist/esm/types/order.d.ts.map +1 -1
  52. package/dist/esm/types/order.js +1 -0
  53. package/dist/esm/types/order.js.map +1 -1
  54. package/dist/esm/utils/apiClient.d.ts +8 -8
  55. package/dist/esm/utils/apiClient.d.ts.map +1 -1
  56. package/dist/esm/utils/apiClient.js +24 -13
  57. package/dist/esm/utils/apiClient.js.map +1 -1
  58. package/dist/esm/utils/fetchDetailedAccount.d.ts +3 -2
  59. package/dist/esm/utils/fetchDetailedAccount.d.ts.map +1 -1
  60. package/dist/esm/utils/fetchDetailedAccount.js +10 -3
  61. package/dist/esm/utils/fetchDetailedAccount.js.map +1 -1
  62. package/dist/esm/utils/ohlcvInterval.d.ts.map +1 -1
  63. package/dist/esm/utils/ohlcvInterval.js +1 -2
  64. package/dist/esm/utils/ohlcvInterval.js.map +1 -1
  65. package/dist/esm/utils/wireList.d.ts +10 -0
  66. package/dist/esm/utils/wireList.d.ts.map +1 -0
  67. package/dist/esm/utils/wireList.js +10 -0
  68. package/dist/esm/utils/wireList.js.map +1 -0
  69. package/dist/types/LighterProvider.d.ts.map +1 -1
  70. package/dist/types/constants.d.ts +1 -1
  71. package/dist/types/signers/LighterReadOnlyTokenManager.d.ts.map +1 -1
  72. package/dist/types/types/account.d.ts +15 -3
  73. package/dist/types/types/account.d.ts.map +1 -1
  74. package/dist/types/types/activity.d.ts +8 -6
  75. package/dist/types/types/activity.d.ts.map +1 -1
  76. package/dist/types/types/fill.d.ts +5 -1
  77. package/dist/types/types/fill.d.ts.map +1 -1
  78. package/dist/types/types/order.d.ts +3 -1
  79. package/dist/types/types/order.d.ts.map +1 -1
  80. package/dist/types/utils/apiClient.d.ts +8 -8
  81. package/dist/types/utils/apiClient.d.ts.map +1 -1
  82. package/dist/types/utils/fetchDetailedAccount.d.ts +3 -2
  83. package/dist/types/utils/fetchDetailedAccount.d.ts.map +1 -1
  84. package/dist/types/utils/ohlcvInterval.d.ts.map +1 -1
  85. package/dist/types/utils/wireList.d.ts +10 -0
  86. package/dist/types/utils/wireList.d.ts.map +1 -0
  87. package/package.json +2 -2
  88. package/src/LighterProvider.ts +53 -16
  89. package/src/constants.ts +1 -1
  90. package/src/signers/LighterReadOnlyTokenManager.ts +3 -0
  91. package/src/types/account.ts +17 -3
  92. package/src/types/activity.ts +9 -6
  93. package/src/types/fill.ts +6 -1
  94. package/src/types/order.ts +4 -1
  95. package/src/utils/apiClient.ts +33 -13
  96. package/src/utils/fetchDetailedAccount.ts +13 -6
  97. package/src/utils/ohlcvInterval.ts +1 -2
  98. 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 as the `auth` query parameter — NOT as an
47
- * `Authorization` header. This matches Lighter's OpenAPI spec and lets the
48
- * same call work browser-direct and from server-side proxies.
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;AAU3D,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;;;;;;;;;;;;;;;;;;;GAmBG;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;;;;OAIG;IACG,SAAS,CAAC,CAAC,EACf,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,MAAM,EACjB,MAAM,GAAE,SAAc,GACrB,OAAO,CAAC,CAAC,CAAC;IAqBb;;;;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;IAiBvC;;;;;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
+ {"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 { LtDetailedAccount } from '../types/index.js';
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<LtDetailedAccount>;
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,iBAAiB,EAAE,MAAM,mBAAmB,CAAA;AAC1D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAA;AAEtD;;;;;GAKG;AACH,eAAO,MAAM,oBAAoB,GAC/B,QAAQ,gBAAgB,EACxB,SAAS,OAAO,KACf,OAAO,CAAC,iBAAiB,CA6B3B,CAAA"}
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;AAsBtD;;;;;GAKG;AACH,eAAO,MAAM,WAAW,GAAI,UAAU,aAAa,KAAG,MAUrD,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": "20.0.0",
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": {
@@ -47,7 +47,7 @@
47
47
  "@lifi/perps-types": "^12.0.1"
48
48
  },
49
49
  "peerDependencies": {
50
- "@lifi/perps-sdk": "^12.0.0"
50
+ "@lifi/perps-sdk": "^12.1.0"
51
51
  },
52
52
  "publishConfig": {
53
53
  "access": "public"
@@ -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: LtDetailedAccount): number[] =>
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
- fundings: LtPositionFundingsResponse
591
- liquidations: LtLiquidationsResponse
592
- transfers: LtTransferHistoryResponse
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 { deposits, withdraws, fundings, liquidations, transfers }
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: LtDetailedAccount
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(predicate as (o: unknown) => boolean)
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`, `positionFunding`,
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') &&
@@ -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
  /**
@@ -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
  }
@@ -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
  /**
@@ -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 as the `auth` query parameter — NOT as an
119
- * `Authorization` header. This matches Lighter's OpenAPI spec and lets the
120
- * same call work browser-direct and from server-side proxies.
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.getWithStatus<unknown>(path, {
212
- ...params,
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.getWithStatus<unknown>(path, params)
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<LtDetailedAccount> => {
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
- if (accounts === undefined || accounts.length === 0) {
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 accounts[0]
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 ?? []