@lifi/perps-sdk-provider-lighter 33.0.0 → 35.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 +0 -1
- package/dist/cjs/LighterProvider.d.ts.map +1 -1
- package/dist/cjs/LighterProvider.js +53 -47
- package/dist/cjs/LighterProvider.js.map +1 -1
- package/dist/cjs/accountSummary.d.ts.map +1 -1
- package/dist/cjs/accountSummary.js +3 -6
- package/dist/cjs/accountSummary.js.map +1 -1
- package/dist/cjs/signers/createAuthToken.d.ts.map +1 -1
- package/dist/cjs/signers/createAuthToken.js.map +1 -1
- package/dist/cjs/utils/liquidation.js.map +1 -1
- package/dist/cjs/utils/mapFill.js +1 -1
- package/dist/cjs/utils/mapFill.js.map +1 -1
- package/dist/cjs/utils/mapOrder.js +1 -1
- package/dist/cjs/utils/mapOrder.js.map +1 -1
- package/dist/cjs/utils/settlementPool.d.ts +6 -0
- package/dist/cjs/utils/settlementPool.d.ts.map +1 -0
- package/dist/cjs/utils/settlementPool.js +21 -0
- package/dist/cjs/utils/settlementPool.js.map +1 -0
- package/dist/cjs/utils/transferMargin.d.ts +2 -2
- package/dist/cjs/utils/transferMargin.d.ts.map +1 -1
- package/dist/cjs/utils/transferMargin.js +2 -2
- package/dist/cjs/utils/transferMargin.js.map +1 -1
- package/dist/esm/LighterProvider.d.ts +7 -11
- package/dist/esm/LighterProvider.d.ts.map +1 -1
- package/dist/esm/LighterProvider.js +63 -54
- package/dist/esm/LighterProvider.js.map +1 -1
- package/dist/esm/accountSummary.d.ts +2 -1
- package/dist/esm/accountSummary.d.ts.map +1 -1
- package/dist/esm/accountSummary.js +5 -7
- package/dist/esm/accountSummary.js.map +1 -1
- package/dist/esm/signers/createAuthToken.d.ts +1 -2
- package/dist/esm/signers/createAuthToken.d.ts.map +1 -1
- package/dist/esm/signers/createAuthToken.js +1 -2
- package/dist/esm/signers/createAuthToken.js.map +1 -1
- package/dist/esm/utils/liquidation.js +2 -2
- package/dist/esm/utils/liquidation.js.map +1 -1
- package/dist/esm/utils/mapFill.js +1 -1
- package/dist/esm/utils/mapFill.js.map +1 -1
- package/dist/esm/utils/mapOrder.js +1 -1
- package/dist/esm/utils/mapOrder.js.map +1 -1
- package/dist/esm/utils/settlementPool.d.ts +14 -0
- package/dist/esm/utils/settlementPool.d.ts.map +1 -0
- package/dist/esm/utils/settlementPool.js +23 -0
- package/dist/esm/utils/settlementPool.js.map +1 -0
- package/dist/esm/utils/transferMargin.d.ts +2 -2
- package/dist/esm/utils/transferMargin.d.ts.map +1 -1
- package/dist/esm/utils/transferMargin.js +2 -2
- package/dist/esm/utils/transferMargin.js.map +1 -1
- package/dist/types/LighterProvider.d.ts +7 -11
- package/dist/types/LighterProvider.d.ts.map +1 -1
- package/dist/types/accountSummary.d.ts +2 -1
- package/dist/types/accountSummary.d.ts.map +1 -1
- package/dist/types/signers/createAuthToken.d.ts +1 -2
- package/dist/types/signers/createAuthToken.d.ts.map +1 -1
- package/dist/types/utils/settlementPool.d.ts +14 -0
- package/dist/types/utils/settlementPool.d.ts.map +1 -0
- package/dist/types/utils/transferMargin.d.ts +2 -2
- package/dist/types/utils/transferMargin.d.ts.map +1 -1
- package/package.json +3 -3
- package/src/LighterProvider.ts +101 -92
- package/src/accountSummary.ts +10 -10
- package/src/signers/createAuthToken.ts +1 -2
- package/src/utils/liquidation.ts +2 -2
- package/src/utils/mapFill.ts +1 -1
- package/src/utils/mapOrder.ts +1 -1
- package/src/utils/settlementPool.ts +36 -0
- package/src/utils/transferMargin.ts +7 -4
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"settlementPool.d.ts","sourceRoot":"","sources":["../../../src/utils/settlementPool.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,GAAG,MAAM,QAAQ,CAAA;AAC7B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAA;AAGzD,2EAA2E;AAC3E,eAAO,MAAM,qBAAqB,GAAI,OAAO,cAAc,KAAG,GAG3D,CAAA;AAEH;;;;;GAKG;AACH,eAAO,MAAM,yBAAyB,GACpC,OAAO,cAAc,EACrB,kBAAkB,GAAG,KACpB,GAKF,CAAA;AAED,uFAAuF;AACvF,eAAO,MAAM,qBAAqB,GAChC,QAAQ,SAAS,cAAc,EAAE,EACjC,sBAAsB,MAAM,KAC3B,MAKF,CAAA"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type Position } from '@lifi/perps-types';
|
|
1
|
+
import { type DecimalString, type Position } from '@lifi/perps-types';
|
|
2
2
|
/**
|
|
3
3
|
* Lighter removable isolated margin:
|
|
4
4
|
* `allocated_margin + unrealized PnL − initial margin requirement`. Lighter
|
|
@@ -13,5 +13,5 @@ import { type Position } from '@lifi/perps-types';
|
|
|
13
13
|
* @see https://docs.lighter.xyz/trading/liquidations-and-llp-insurance-fund
|
|
14
14
|
* @public
|
|
15
15
|
*/
|
|
16
|
-
export declare function positionRemovableMargin(position: Position):
|
|
16
|
+
export declare function positionRemovableMargin(position: Position): DecimalString | undefined;
|
|
17
17
|
//# sourceMappingURL=transferMargin.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transferMargin.d.ts","sourceRoot":"","sources":["../../../src/utils/transferMargin.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"transferMargin.d.ts","sourceRoot":"","sources":["../../../src/utils/transferMargin.ts"],"names":[],"mappings":"AAKA,OAAO,EACL,KAAK,aAAa,EAElB,KAAK,QAAQ,EACd,MAAM,mBAAmB,CAAA;AA2B1B;;;;;;;;;;;;;GAaG;AACH,wBAAgB,uBAAuB,CACrC,QAAQ,EAAE,QAAQ,GACjB,aAAa,GAAG,SAAS,CAmB3B"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lifi/perps-sdk-provider-lighter",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "35.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": "^
|
|
47
|
+
"@lifi/perps-types": "^20.1.0"
|
|
48
48
|
},
|
|
49
49
|
"peerDependencies": {
|
|
50
|
-
"@lifi/perps-sdk": "^
|
|
50
|
+
"@lifi/perps-sdk": "^23.0.0"
|
|
51
51
|
},
|
|
52
52
|
"publishConfig": {
|
|
53
53
|
"access": "public"
|
package/src/LighterProvider.ts
CHANGED
|
@@ -9,7 +9,9 @@ import {
|
|
|
9
9
|
getMarketsContext,
|
|
10
10
|
getProviders,
|
|
11
11
|
isActiveOrderStatus,
|
|
12
|
+
isDecimalString,
|
|
12
13
|
localStorageAdapter,
|
|
14
|
+
numberToDecimalString,
|
|
13
15
|
PerpsError,
|
|
14
16
|
type PerpsProviderPlugin,
|
|
15
17
|
type PerpsSDKClient,
|
|
@@ -65,7 +67,6 @@ import type {
|
|
|
65
67
|
import {
|
|
66
68
|
ActionType,
|
|
67
69
|
ActivityType,
|
|
68
|
-
isDecimalString,
|
|
69
70
|
MarginMode,
|
|
70
71
|
PerpsErrorCode,
|
|
71
72
|
} from '@lifi/perps-types'
|
|
@@ -124,6 +125,7 @@ import type {
|
|
|
124
125
|
} from './types/index.js'
|
|
125
126
|
import {
|
|
126
127
|
LT_ACCOUNT_TRADING_MODE_SIMPLE,
|
|
128
|
+
LT_ACCOUNT_TRADING_MODE_UNIFIED,
|
|
127
129
|
LT_MARGIN_MODE_CROSS,
|
|
128
130
|
LT_MARGIN_MODE_ISOLATED,
|
|
129
131
|
} from './types/index.js'
|
|
@@ -161,6 +163,11 @@ import {
|
|
|
161
163
|
fetchRegisteredApiKey,
|
|
162
164
|
normalizeLighterPublicKey,
|
|
163
165
|
} from './utils/registeredApiKey.js'
|
|
166
|
+
import {
|
|
167
|
+
collateralSpotBalance,
|
|
168
|
+
pooledSettlementSpendable,
|
|
169
|
+
pooledSettlementUnits,
|
|
170
|
+
} from './utils/settlementPool.js'
|
|
164
171
|
import { spotPriceByAssetId, spotValuation } from './utils/spotPrice.js'
|
|
165
172
|
import { isPlaceholderTxHash } from './utils/txHash.js'
|
|
166
173
|
import { wireList } from './utils/wireList.js'
|
|
@@ -168,7 +175,7 @@ import { wireList } from './utils/wireList.js'
|
|
|
168
175
|
const ZERO_FEE_TIER = { maker: '0', taker: '0' }
|
|
169
176
|
|
|
170
177
|
const tickToFeeString = (tick: number): string =>
|
|
171
|
-
|
|
178
|
+
numberToDecimalString(tick / LIGHTER_FEE_TICK_SCALE)
|
|
172
179
|
|
|
173
180
|
const projectFeeTier = (
|
|
174
181
|
limits: LtAccountLimits
|
|
@@ -268,11 +275,9 @@ interface ReadOnlyCreationBackoff {
|
|
|
268
275
|
* manager are created per plugin instance.
|
|
269
276
|
*
|
|
270
277
|
* Auth-token resolution order for the auth-gated reads:
|
|
271
|
-
* 1.
|
|
272
|
-
* 2. Constructor `authToken` (string or async factory)
|
|
273
|
-
* 3. Persisted long-lived read-only token, keyed on the resolved Lighter
|
|
278
|
+
* 1. Persisted long-lived read-only token, keyed on the resolved Lighter
|
|
274
279
|
* `accountIndex`
|
|
275
|
-
*
|
|
280
|
+
* 2. Fresh 1h create via this instance's WASM signer + the user's registered
|
|
276
281
|
* API key
|
|
277
282
|
*
|
|
278
283
|
* Status reads report the account's state and do not throw for a missing
|
|
@@ -286,9 +291,9 @@ interface ReadOnlyCreationBackoff {
|
|
|
286
291
|
* - `SetupRequired` when none of these sources yields a token. The SDK
|
|
287
292
|
* sends no venue request. `getOrders`, `getOrder`, `getFills`,
|
|
288
293
|
* `getPortfolioHistory` and `getActivity` need a token.
|
|
289
|
-
* - `Unauthorized` when Lighter rejects the token that was sent.
|
|
290
|
-
*
|
|
291
|
-
*
|
|
294
|
+
* - `Unauthorized` when Lighter rejects the token that was sent. A
|
|
295
|
+
* read-only token that Lighter reports as revoked is replaced and the
|
|
296
|
+
* read retried once first.
|
|
292
297
|
* - `AccountNotFound` when the wallet has no Lighter account. This also
|
|
293
298
|
* applies to `getPositions`, `getWithdrawableBalances` and
|
|
294
299
|
* `getAvailableToTrade`, which need no token. `getAvailableToTrade`
|
|
@@ -311,8 +316,6 @@ export interface LighterProviderOptions {
|
|
|
311
316
|
* instance's signer and read-only token manager follow this URL.
|
|
312
317
|
*/
|
|
313
318
|
restUrl?: string
|
|
314
|
-
/** Pre-created Lighter read-only bearer, bypassing SDK token resolution. */
|
|
315
|
-
authToken?: string | (() => string | Promise<string>)
|
|
316
319
|
/** Token lifetime for on-demand standard-token creates (Lighter caps at 8h). Default 1h. */
|
|
317
320
|
tokenLifetimeSeconds?: number
|
|
318
321
|
/** Re-create when the cached standard token's remaining life is below this. Default 60s. */
|
|
@@ -343,8 +346,8 @@ interface CachedStandardToken {
|
|
|
343
346
|
export interface LighterPerpsProvider extends PerpsProviderPlugin {
|
|
344
347
|
/**
|
|
345
348
|
* Resolve a Lighter auth token for `address`, following the resolution order
|
|
346
|
-
* documented on {@link LighterProviderOptions}
|
|
347
|
-
*
|
|
349
|
+
* documented on {@link LighterProviderOptions}. Returns `undefined` when no
|
|
350
|
+
* source can produce a token.
|
|
348
351
|
*/
|
|
349
352
|
resolveAuthToken(address: Address): Promise<string | undefined>
|
|
350
353
|
}
|
|
@@ -380,12 +383,6 @@ export const createLighterProvider = (
|
|
|
380
383
|
const explorerTxBaseUrl = deployment.explorerTxBaseUrl
|
|
381
384
|
const bridgeChainId = deployment.bridgeChainId
|
|
382
385
|
const collateral = deployment.collateral
|
|
383
|
-
const authTokenSource: (() => string | Promise<string>) | undefined =
|
|
384
|
-
typeof options.authToken === 'function'
|
|
385
|
-
? options.authToken
|
|
386
|
-
: options.authToken !== undefined
|
|
387
|
-
? () => options.authToken as string
|
|
388
|
-
: undefined
|
|
389
386
|
const storage = options.storage ?? localStorageAdapter
|
|
390
387
|
const signer = new LighterSigner({
|
|
391
388
|
apiUrl: restUrl,
|
|
@@ -459,32 +456,20 @@ export const createLighterProvider = (
|
|
|
459
456
|
|
|
460
457
|
/**
|
|
461
458
|
* Resolve the bearer token for auth-gated reads. Priority:
|
|
462
|
-
* 1.
|
|
463
|
-
* 2. Constructor-supplied token source.
|
|
464
|
-
* 3. A stored read-only token — preferred for reads: it is read-only, so
|
|
459
|
+
* 1. A stored read-only token — preferred for reads: it is read-only, so
|
|
465
460
|
* forwarding it (incl. to the backend) cannot authorise writes.
|
|
466
|
-
*
|
|
461
|
+
* 2. A read-only token created on first use (via signer + registered API
|
|
467
462
|
* key) and persisted for reuse.
|
|
468
|
-
*
|
|
463
|
+
* 3. A standard auth token (read+write, 8h max) as a last resort — used
|
|
469
464
|
* as the credential that authorises read-only token creation, and as
|
|
470
465
|
* the fallback while creation is failing, bounded by the
|
|
471
466
|
* creation-retry backoff.
|
|
472
467
|
* Returns `undefined` when no source can produce a token.
|
|
473
468
|
*/
|
|
474
469
|
const resolveAuthToken = async (
|
|
475
|
-
|
|
476
|
-
address?: Address,
|
|
470
|
+
address: Address,
|
|
477
471
|
knownApiKey?: LighterApiKey
|
|
478
472
|
): Promise<string | undefined> => {
|
|
479
|
-
if (opts?.lighterAuthToken !== undefined) {
|
|
480
|
-
return opts.lighterAuthToken
|
|
481
|
-
}
|
|
482
|
-
if (authTokenSource !== undefined) {
|
|
483
|
-
return authTokenSource()
|
|
484
|
-
}
|
|
485
|
-
if (address === undefined) {
|
|
486
|
-
return undefined
|
|
487
|
-
}
|
|
488
473
|
const apiKey = knownApiKey ?? (await keyStore.get(address))
|
|
489
474
|
if (apiKey === null) {
|
|
490
475
|
return undefined
|
|
@@ -566,17 +551,15 @@ export const createLighterProvider = (
|
|
|
566
551
|
/** {@link resolveAuthToken}, throwing `SetupRequired` when no source yields a token. */
|
|
567
552
|
const requireAuthToken = async (
|
|
568
553
|
read: string,
|
|
569
|
-
opts: SDKRequestOptions | undefined,
|
|
570
554
|
address: Address,
|
|
571
555
|
knownApiKey?: LighterApiKey
|
|
572
556
|
): Promise<string> => {
|
|
573
|
-
const token = await resolveAuthToken(
|
|
557
|
+
const token = await resolveAuthToken(address, knownApiKey)
|
|
574
558
|
if (token === undefined) {
|
|
575
559
|
throw new PerpsError(
|
|
576
560
|
PerpsErrorCode.SetupRequired,
|
|
577
|
-
`Lighter ${read} requires an auth token.
|
|
578
|
-
'
|
|
579
|
-
'forward `options.lighterAuthToken` on the call.'
|
|
561
|
+
`Lighter ${read} requires an auth token. Register an API key + signer ` +
|
|
562
|
+
'for on-demand creation.'
|
|
580
563
|
)
|
|
581
564
|
}
|
|
582
565
|
return token
|
|
@@ -637,7 +620,6 @@ export const createLighterProvider = (
|
|
|
637
620
|
}
|
|
638
621
|
|
|
639
622
|
const retryOnRevoked = async <T>(
|
|
640
|
-
opts: SDKRequestOptions | undefined,
|
|
641
623
|
address: Address,
|
|
642
624
|
token: string,
|
|
643
625
|
run: (token: string) => Promise<T>
|
|
@@ -645,9 +627,7 @@ export const createLighterProvider = (
|
|
|
645
627
|
try {
|
|
646
628
|
return await run(token)
|
|
647
629
|
} catch (err) {
|
|
648
|
-
|
|
649
|
-
opts?.lighterAuthToken === undefined && authTokenSource === undefined
|
|
650
|
-
if (!(err instanceof LighterTokenRevokedError) || !sdkOwnsToken) {
|
|
630
|
+
if (!(err instanceof LighterTokenRevokedError)) {
|
|
651
631
|
throw err
|
|
652
632
|
}
|
|
653
633
|
const fresh = await replaceRevokedReadOnlyToken(address, token)
|
|
@@ -878,6 +858,20 @@ export const createLighterProvider = (
|
|
|
878
858
|
return setup.find((descriptor) => descriptor.type === action)
|
|
879
859
|
}
|
|
880
860
|
|
|
861
|
+
/**
|
|
862
|
+
* Refuse a SEND_ASSET for a unified account. Its settlement asset is one pool
|
|
863
|
+
* across both routes, so a perps↔spot transfer has nothing to move.
|
|
864
|
+
*/
|
|
865
|
+
const assertSendAssetMode = async (address: Address): Promise<void> => {
|
|
866
|
+
const account = await fetchDetailedAccount(apiClient(), address)
|
|
867
|
+
if (account.account_trading_mode === LT_ACCOUNT_TRADING_MODE_UNIFIED) {
|
|
868
|
+
throw new PerpsError(
|
|
869
|
+
PerpsErrorCode.PooledCategoryTransfer,
|
|
870
|
+
'Lighter unified accounts pool the settlement asset across perps and spot; a transfer between them has nothing to move'
|
|
871
|
+
)
|
|
872
|
+
}
|
|
873
|
+
}
|
|
874
|
+
|
|
881
875
|
/**
|
|
882
876
|
* Refuse an order signature for a tier no ACCOUNT_TYPE option binds. An
|
|
883
877
|
* unreadable descriptor or tier fails open: the venue itself
|
|
@@ -907,11 +901,11 @@ export const createLighterProvider = (
|
|
|
907
901
|
}
|
|
908
902
|
let userTierName: string | undefined
|
|
909
903
|
try {
|
|
910
|
-
const token = await resolveAuthToken(
|
|
904
|
+
const token = await resolveAuthToken(address, localKey)
|
|
911
905
|
const limits =
|
|
912
906
|
token === undefined
|
|
913
907
|
? undefined
|
|
914
|
-
: await retryOnRevoked(
|
|
908
|
+
: await retryOnRevoked(address, token, (resolvedToken) =>
|
|
915
909
|
fetchAccountLimits(
|
|
916
910
|
apiClient(),
|
|
917
911
|
localKey.accountIndex,
|
|
@@ -950,7 +944,7 @@ export const createLighterProvider = (
|
|
|
950
944
|
},
|
|
951
945
|
|
|
952
946
|
resolveAuthToken(address: Address): Promise<string | undefined> {
|
|
953
|
-
return resolveAuthToken(
|
|
947
|
+
return resolveAuthToken(address)
|
|
954
948
|
},
|
|
955
949
|
|
|
956
950
|
async getAccount(
|
|
@@ -980,7 +974,7 @@ export const createLighterProvider = (
|
|
|
980
974
|
normalizeLighterPublicKey(registeredKey.public_key)
|
|
981
975
|
// Status read: a missing or stale credential reports `apiKeyRegistered: false`, not a throw.
|
|
982
976
|
const token = apiKeyRegistered
|
|
983
|
-
? await resolveAuthToken(
|
|
977
|
+
? await resolveAuthToken(params.address, localKey)
|
|
984
978
|
: undefined
|
|
985
979
|
|
|
986
980
|
const registry = getMarketRegistry(requireClient(), providerKey)
|
|
@@ -993,7 +987,7 @@ export const createLighterProvider = (
|
|
|
993
987
|
getMarketsContext(requireClient(), { provider: providerKey }, opts),
|
|
994
988
|
token === undefined
|
|
995
989
|
? Promise.resolve(undefined)
|
|
996
|
-
: retryOnRevoked(
|
|
990
|
+
: retryOnRevoked(params.address, token, (resolvedToken) =>
|
|
997
991
|
fetchAccountLimits(client, account.index, resolvedToken)
|
|
998
992
|
),
|
|
999
993
|
apiKeyRegistered
|
|
@@ -1023,12 +1017,21 @@ export const createLighterProvider = (
|
|
|
1023
1017
|
)
|
|
1024
1018
|
// Each asset has a spot route (`balance`) and a perps route
|
|
1025
1019
|
// (`margin_balance`). The instance's settlement asset is valued 1:1; an
|
|
1026
|
-
// asset no spot market prices keeps a zero USD value.
|
|
1020
|
+
// asset no spot market prices keeps a zero USD value. A unified account
|
|
1021
|
+
// pools both settlement routes into one spot row.
|
|
1022
|
+
const isPooled = (asset: LtAccountAsset): boolean =>
|
|
1023
|
+
account.account_trading_mode === LT_ACCOUNT_TRADING_MODE_UNIFIED &&
|
|
1024
|
+
asset.asset_id === collateral.assetIndex
|
|
1027
1025
|
const heldAssets = account.assets.filter((asset) =>
|
|
1028
|
-
|
|
1026
|
+
(isPooled(asset)
|
|
1027
|
+
? pooledSettlementUnits(asset)
|
|
1028
|
+
: toRequiredBig(asset.balance, 'balance')
|
|
1029
|
+
).gt(0)
|
|
1029
1030
|
)
|
|
1030
|
-
const marginAssets = account.assets.filter(
|
|
1031
|
-
|
|
1031
|
+
const marginAssets = account.assets.filter(
|
|
1032
|
+
(asset) =>
|
|
1033
|
+
!isPooled(asset) &&
|
|
1034
|
+
toRequiredBig(asset.margin_balance, 'margin_balance').gt(0)
|
|
1032
1035
|
)
|
|
1033
1036
|
const spotPrices = spotPriceByAssetId(
|
|
1034
1037
|
registry.markets,
|
|
@@ -1093,6 +1096,16 @@ export const createLighterProvider = (
|
|
|
1093
1096
|
}
|
|
1094
1097
|
})
|
|
1095
1098
|
const balances: Balance[] = heldAssets.map((a) => {
|
|
1099
|
+
if (isPooled(a)) {
|
|
1100
|
+
const units = pooledSettlementUnits(a).toFixed()
|
|
1101
|
+
return {
|
|
1102
|
+
...toBalance(a, LIGHTER_SPOT_CATEGORY_ID, registryAsset(a), units),
|
|
1103
|
+
transferable: calculateTransferable(
|
|
1104
|
+
pooledSettlementSpendable(a, availableBalance).toFixed(),
|
|
1105
|
+
units
|
|
1106
|
+
),
|
|
1107
|
+
}
|
|
1108
|
+
}
|
|
1096
1109
|
const balance = toRequiredBig(a.balance, 'balance')
|
|
1097
1110
|
return {
|
|
1098
1111
|
...toBalance(
|
|
@@ -1131,6 +1144,10 @@ export const createLighterProvider = (
|
|
|
1131
1144
|
crossAssetValue: account.cross_asset_value,
|
|
1132
1145
|
crossInitialMarginRequirement: account.cross_initial_margin_requirement,
|
|
1133
1146
|
totalAssetValue: account.total_asset_value,
|
|
1147
|
+
collateralSpotBalance: collateralSpotBalance(
|
|
1148
|
+
account.assets,
|
|
1149
|
+
collateral.assetIndex
|
|
1150
|
+
),
|
|
1134
1151
|
userTierName: limitsResult?.user_tier_name,
|
|
1135
1152
|
accountTradingMode:
|
|
1136
1153
|
account.account_trading_mode ?? LT_ACCOUNT_TRADING_MODE_SIMPLE,
|
|
@@ -1304,7 +1321,7 @@ export const createLighterProvider = (
|
|
|
1304
1321
|
if (statuses.size === 0) {
|
|
1305
1322
|
return empty
|
|
1306
1323
|
}
|
|
1307
|
-
const token = await requireAuthToken('orders read',
|
|
1324
|
+
const token = await requireAuthToken('orders read', params.address)
|
|
1308
1325
|
|
|
1309
1326
|
const client = apiClient(opts)
|
|
1310
1327
|
const registry = getMarketRegistry(requireClient(), providerKey)
|
|
@@ -1326,7 +1343,6 @@ export const createLighterProvider = (
|
|
|
1326
1343
|
}
|
|
1327
1344
|
}
|
|
1328
1345
|
const [activeResponse, inactiveResponse] = await retryOnRevoked(
|
|
1329
|
-
opts,
|
|
1330
1346
|
params.address,
|
|
1331
1347
|
token,
|
|
1332
1348
|
(t) =>
|
|
@@ -1393,7 +1409,7 @@ export const createLighterProvider = (
|
|
|
1393
1409
|
params: ProviderGetOrderParams,
|
|
1394
1410
|
opts?: SDKRequestOptions
|
|
1395
1411
|
): Promise<Order> {
|
|
1396
|
-
const token = await requireAuthToken('order lookup',
|
|
1412
|
+
const token = await requireAuthToken('order lookup', params.address)
|
|
1397
1413
|
|
|
1398
1414
|
const client = apiClient(opts)
|
|
1399
1415
|
const registry = getMarketRegistry(requireClient(), providerKey)
|
|
@@ -1436,7 +1452,7 @@ export const createLighterProvider = (
|
|
|
1436
1452
|
`"${clientOrderIndex}" is not a decimal integer.`
|
|
1437
1453
|
)
|
|
1438
1454
|
}
|
|
1439
|
-
const orders = await retryOnRevoked(
|
|
1455
|
+
const orders = await retryOnRevoked(params.address, token, (t) =>
|
|
1440
1456
|
fetchOrdersByClientOrderIndex(
|
|
1441
1457
|
client,
|
|
1442
1458
|
t,
|
|
@@ -1460,7 +1476,7 @@ export const createLighterProvider = (
|
|
|
1460
1476
|
const byOrderIndex = (o: LtOrder): boolean =>
|
|
1461
1477
|
String(o.order_index) === params.id
|
|
1462
1478
|
|
|
1463
|
-
const active = await retryOnRevoked(
|
|
1479
|
+
const active = await retryOnRevoked(params.address, token, (t) =>
|
|
1464
1480
|
fetchActiveOrders(
|
|
1465
1481
|
client,
|
|
1466
1482
|
t,
|
|
@@ -1473,7 +1489,7 @@ export const createLighterProvider = (
|
|
|
1473
1489
|
return detail(activeHit)
|
|
1474
1490
|
}
|
|
1475
1491
|
|
|
1476
|
-
const inactive = await retryOnRevoked(
|
|
1492
|
+
const inactive = await retryOnRevoked(params.address, token, (t) =>
|
|
1477
1493
|
client.getAuthed<LtOrdersResponse>('/api/v1/accountInactiveOrders', t, {
|
|
1478
1494
|
account_index: account.index,
|
|
1479
1495
|
market_id: LIGHTER_ALL_MARKETS_WILDCARD,
|
|
@@ -1491,7 +1507,7 @@ export const createLighterProvider = (
|
|
|
1491
1507
|
params: ProviderGetFillsParams,
|
|
1492
1508
|
opts?: SDKRequestOptions
|
|
1493
1509
|
): Promise<FillsResponse> {
|
|
1494
|
-
const token = await requireAuthToken('fills read',
|
|
1510
|
+
const token = await requireAuthToken('fills read', params.address)
|
|
1495
1511
|
|
|
1496
1512
|
const client = apiClient(opts)
|
|
1497
1513
|
const registry = getMarketRegistry(requireClient(), providerKey)
|
|
@@ -1510,12 +1526,8 @@ export const createLighterProvider = (
|
|
|
1510
1526
|
queryParams.cursor = params.cursor
|
|
1511
1527
|
}
|
|
1512
1528
|
|
|
1513
|
-
const response = await retryOnRevoked(
|
|
1514
|
-
|
|
1515
|
-
params.address,
|
|
1516
|
-
token,
|
|
1517
|
-
(tok) =>
|
|
1518
|
-
client.getAuthed<LtTradesResponse>('/api/v1/trades', tok, queryParams)
|
|
1529
|
+
const response = await retryOnRevoked(params.address, token, (tok) =>
|
|
1530
|
+
client.getAuthed<LtTradesResponse>('/api/v1/trades', tok, queryParams)
|
|
1519
1531
|
)
|
|
1520
1532
|
|
|
1521
1533
|
// `get`, not `require`: a market id the backend list no longer carries
|
|
@@ -1545,12 +1557,9 @@ export const createLighterProvider = (
|
|
|
1545
1557
|
params: ProviderGetPortfolioHistoryParams,
|
|
1546
1558
|
opts?: SDKRequestOptions
|
|
1547
1559
|
): Promise<PortfolioHistoryResponse> {
|
|
1548
|
-
const
|
|
1549
|
-
opts?.lighterAuthToken === undefined && authTokenSource === undefined
|
|
1550
|
-
const apiKey = sdkOwnsToken ? await keyStore.get(params.address) : null
|
|
1560
|
+
const apiKey = await keyStore.get(params.address)
|
|
1551
1561
|
const token = await requireAuthToken(
|
|
1552
1562
|
'portfolio history read',
|
|
1553
|
-
opts,
|
|
1554
1563
|
params.address,
|
|
1555
1564
|
apiKey ?? undefined
|
|
1556
1565
|
)
|
|
@@ -1579,23 +1588,24 @@ export const createLighterProvider = (
|
|
|
1579
1588
|
// fallback sits outside `retryOnRevoked`, so a revocation the standard
|
|
1580
1589
|
// token reports never replaces the read-only one. The key is re-read at
|
|
1581
1590
|
// retry time because `REGISTER_API_KEY` can rotate it.
|
|
1582
|
-
const response = await retryOnRevoked(
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1591
|
+
const response = await retryOnRevoked(params.address, token, read).catch(
|
|
1592
|
+
async (err: unknown) => {
|
|
1593
|
+
if (!(err instanceof LighterAuthRejectedError) || apiKey === null) {
|
|
1594
|
+
throw err
|
|
1595
|
+
}
|
|
1596
|
+
const current = (await keyStore.get(params.address)) ?? apiKey
|
|
1597
|
+
return read(
|
|
1598
|
+
await getStandardAuthToken(
|
|
1599
|
+
params.address,
|
|
1600
|
+
current.apiKeyPrivateKey,
|
|
1601
|
+
{
|
|
1602
|
+
apiKeyIndex: current.apiKeyIndex,
|
|
1603
|
+
accountIndex: current.accountIndex,
|
|
1604
|
+
}
|
|
1605
|
+
)
|
|
1606
|
+
)
|
|
1590
1607
|
}
|
|
1591
|
-
|
|
1592
|
-
return read(
|
|
1593
|
-
await getStandardAuthToken(params.address, current.apiKeyPrivateKey, {
|
|
1594
|
-
apiKeyIndex: current.apiKeyIndex,
|
|
1595
|
-
accountIndex: current.accountIndex,
|
|
1596
|
-
})
|
|
1597
|
-
)
|
|
1598
|
-
})
|
|
1608
|
+
)
|
|
1599
1609
|
|
|
1600
1610
|
return mapPortfolioHistory(
|
|
1601
1611
|
params.range,
|
|
@@ -1608,11 +1618,7 @@ export const createLighterProvider = (
|
|
|
1608
1618
|
params: ProviderGetActivityParams,
|
|
1609
1619
|
opts?: SDKRequestOptions
|
|
1610
1620
|
): Promise<ActivitiesResponse> {
|
|
1611
|
-
const token = await requireAuthToken(
|
|
1612
|
-
'activity read',
|
|
1613
|
-
opts,
|
|
1614
|
-
params.address
|
|
1615
|
-
)
|
|
1621
|
+
const token = await requireAuthToken('activity read', params.address)
|
|
1616
1622
|
|
|
1617
1623
|
const inputCursor = decodeActivityCursor(params.cursor)
|
|
1618
1624
|
const client = apiClient(opts)
|
|
@@ -1622,7 +1628,7 @@ export const createLighterProvider = (
|
|
|
1622
1628
|
// Markets identify funding and liquidation rows; assets identify ledger
|
|
1623
1629
|
// rows. A request for one surface must not pull the other's registry.
|
|
1624
1630
|
const [history] = await Promise.all([
|
|
1625
|
-
retryOnRevoked(
|
|
1631
|
+
retryOnRevoked(params.address, token, (t) =>
|
|
1626
1632
|
fetchAllHistory(
|
|
1627
1633
|
client,
|
|
1628
1634
|
t,
|
|
@@ -1874,6 +1880,9 @@ export const createLighterProvider = (
|
|
|
1874
1880
|
if (steps.some((step) => ORDER_PLACEMENT_ACTIONS.has(step.action))) {
|
|
1875
1881
|
await assertOrderTier(address)
|
|
1876
1882
|
}
|
|
1883
|
+
if (steps.some((step) => step.action === ActionType.SEND_ASSET)) {
|
|
1884
|
+
await assertSendAssetMode(address)
|
|
1885
|
+
}
|
|
1877
1886
|
return lighterSignActions(
|
|
1878
1887
|
{
|
|
1879
1888
|
signer,
|
package/src/accountSummary.ts
CHANGED
|
@@ -50,7 +50,8 @@ export const lighterPortfolioValue = (
|
|
|
50
50
|
|
|
51
51
|
/**
|
|
52
52
|
* Roll up settlement equity, spot holdings and non-settlement margin holdings.
|
|
53
|
-
*
|
|
53
|
+
* The settlement asset counts as `totalAssetValue` plus its spot-route balance,
|
|
54
|
+
* so a row that pools both routes is not counted again.
|
|
54
55
|
*
|
|
55
56
|
* @throws {PerpsError} `SDKError` when the account is not a Lighter one.
|
|
56
57
|
* @public
|
|
@@ -61,14 +62,11 @@ export function getAccountSummary(
|
|
|
61
62
|
): AccountSummary {
|
|
62
63
|
const config = lighterConfig(account)
|
|
63
64
|
const settlement = LIGHTER_COLLATERAL_ASSETS[config.provider]
|
|
64
|
-
const holdings = [
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
asset.id !== settlement.displaySymbol
|
|
70
|
-
),
|
|
71
|
-
]
|
|
65
|
+
const holdings = [...account.balances, ...account.collateralBalances].filter(
|
|
66
|
+
({ asset }) =>
|
|
67
|
+
asset.id !== String(settlement.assetIndex) &&
|
|
68
|
+
asset.id !== settlement.displaySymbol
|
|
69
|
+
)
|
|
72
70
|
|
|
73
71
|
let marginUsed = new Big(0)
|
|
74
72
|
let unrealizedPnl = new Big(0)
|
|
@@ -79,7 +77,9 @@ export function getAccountSummary(
|
|
|
79
77
|
|
|
80
78
|
return {
|
|
81
79
|
portfolioValue: lighterPortfolioValue(
|
|
82
|
-
toRequiredBig(config.totalAssetValue, 'totalAssetValue')
|
|
80
|
+
toRequiredBig(config.totalAssetValue, 'totalAssetValue').plus(
|
|
81
|
+
toRequiredBig(config.collateralSpotBalance, 'collateralSpotBalance')
|
|
82
|
+
),
|
|
83
83
|
holdings.map((balance) => balance.valueUsd)
|
|
84
84
|
).toFixed(),
|
|
85
85
|
availableMargin: atLeastZero(
|
|
@@ -33,8 +33,7 @@ export interface CreateAuthTokenInputs {
|
|
|
33
33
|
* endpoints (`getOrders`, `getOrder`, `getActivity`, etc.).
|
|
34
34
|
*
|
|
35
35
|
* This utility carries no SDK-wide coupling — pass an explicit `signer` +
|
|
36
|
-
* `apiKey` and consume the returned bearer however the caller likes
|
|
37
|
-
* (per-call `options.lighterAuthToken`, persisted cache, etc.).
|
|
36
|
+
* `apiKey` and consume the returned bearer however the caller likes.
|
|
38
37
|
* @public
|
|
39
38
|
*/
|
|
40
39
|
export async function createAuthToken(
|
package/src/utils/liquidation.ts
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import {
|
|
11
|
-
estimateLiquidationPrice as
|
|
11
|
+
estimateLiquidationPrice as estimateLiquidationPriceAtRate,
|
|
12
12
|
type LiquidationEstimateParams,
|
|
13
13
|
} from '@lifi/perps-sdk'
|
|
14
14
|
import type { PerpsMarket } from '@lifi/perps-types'
|
|
@@ -30,7 +30,7 @@ export function estimateLiquidationPrice(
|
|
|
30
30
|
if (market.maintenanceMarginRate === undefined) {
|
|
31
31
|
return undefined
|
|
32
32
|
}
|
|
33
|
-
return
|
|
33
|
+
return estimateLiquidationPriceAtRate({
|
|
34
34
|
entryPrice: params.entryPrice,
|
|
35
35
|
leverage: params.leverage,
|
|
36
36
|
isLong: params.isLong,
|
package/src/utils/mapFill.ts
CHANGED
package/src/utils/mapOrder.ts
CHANGED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type Big from 'big.js'
|
|
2
|
+
import type { LtAccountAsset } from '../types/account.js'
|
|
3
|
+
import { toRequiredBig } from './decimal.js'
|
|
4
|
+
|
|
5
|
+
/** Both routes of a unified account's settlement asset, as one holding. */
|
|
6
|
+
export const pooledSettlementUnits = (asset: LtAccountAsset): Big =>
|
|
7
|
+
toRequiredBig(asset.balance, 'balance').plus(
|
|
8
|
+
toRequiredBig(asset.margin_balance, 'margin_balance')
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Spot units a unified account can spend from its settlement pool: the free
|
|
13
|
+
* spot route plus the margin route up to the account's `available_balance`.
|
|
14
|
+
* This is `computeSpotAvailableToTradeMarket` in Lighter's `lighter-ts` for an
|
|
15
|
+
* asset priced 1:1 with a `loan_to_value` of 1. The result can be negative.
|
|
16
|
+
*/
|
|
17
|
+
export const pooledSettlementSpendable = (
|
|
18
|
+
asset: LtAccountAsset,
|
|
19
|
+
availableBalance: Big
|
|
20
|
+
): Big => {
|
|
21
|
+
const marginBalance = toRequiredBig(asset.margin_balance, 'margin_balance')
|
|
22
|
+
return toRequiredBig(asset.balance, 'balance')
|
|
23
|
+
.minus(toRequiredBig(asset.locked_balance, 'locked_balance'))
|
|
24
|
+
.plus(marginBalance.lt(availableBalance) ? marginBalance : availableBalance)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** Spot-route `balance` of the collateral asset; `'0'` when the account holds none. */
|
|
28
|
+
export const collateralSpotBalance = (
|
|
29
|
+
assets: readonly LtAccountAsset[],
|
|
30
|
+
settlementAssetIndex: number
|
|
31
|
+
): string => {
|
|
32
|
+
const asset = assets.find((a) => a.asset_id === settlementAssetIndex)
|
|
33
|
+
return asset === undefined
|
|
34
|
+
? '0'
|
|
35
|
+
: toRequiredBig(asset.balance, 'balance').toFixed()
|
|
36
|
+
}
|
|
@@ -1,9 +1,12 @@
|
|
|
1
|
-
import { PerpsError } from '@lifi/perps-sdk'
|
|
2
1
|
import {
|
|
3
|
-
|
|
4
|
-
type Position,
|
|
2
|
+
PerpsError,
|
|
5
3
|
positionSupportsMarginAdjustment,
|
|
6
4
|
positionSupportsMarginRemoval,
|
|
5
|
+
} from '@lifi/perps-sdk'
|
|
6
|
+
import {
|
|
7
|
+
type DecimalString,
|
|
8
|
+
PerpsErrorCode,
|
|
9
|
+
type Position,
|
|
7
10
|
} from '@lifi/perps-types'
|
|
8
11
|
import Big from 'big.js'
|
|
9
12
|
|
|
@@ -47,7 +50,7 @@ function positivePositionAmount(value: string, field: string): Big {
|
|
|
47
50
|
*/
|
|
48
51
|
export function positionRemovableMargin(
|
|
49
52
|
position: Position
|
|
50
|
-
):
|
|
53
|
+
): DecimalString | undefined {
|
|
51
54
|
if (!positionSupportsMarginAdjustment(position)) {
|
|
52
55
|
return undefined
|
|
53
56
|
}
|