@lifi/perps-sdk-provider-lighter 6.0.0 → 8.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 +77 -37
- package/dist/cjs/LighterProvider.js.map +1 -1
- package/dist/cjs/accountSummary.d.ts.map +1 -1
- package/dist/cjs/accountSummary.js +33 -2
- package/dist/cjs/accountSummary.js.map +1 -1
- package/dist/cjs/depositFlow.d.ts +4 -0
- package/dist/cjs/depositFlow.d.ts.map +1 -0
- package/dist/cjs/depositFlow.js +32 -0
- package/dist/cjs/depositFlow.js.map +1 -0
- package/dist/cjs/index.d.ts +1 -0
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/index.js +3 -1
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/types/account.d.ts +1 -0
- package/dist/cjs/types/account.d.ts.map +1 -1
- package/dist/cjs/types/ws.d.ts +5 -0
- package/dist/cjs/types/ws.d.ts.map +1 -1
- package/dist/cjs/utils/decimal.d.ts +5 -0
- package/dist/cjs/utils/decimal.d.ts.map +1 -0
- package/dist/cjs/utils/decimal.js +38 -0
- package/dist/cjs/utils/decimal.js.map +1 -0
- package/dist/cjs/utils/index.d.ts +3 -1
- package/dist/cjs/utils/index.d.ts.map +1 -1
- package/dist/cjs/utils/index.js +7 -1
- package/dist/cjs/utils/index.js.map +1 -1
- package/dist/cjs/utils/mapOpenPositions.d.ts +2 -2
- package/dist/cjs/utils/mapOpenPositions.d.ts.map +1 -1
- package/dist/cjs/utils/mapOpenPositions.js.map +1 -1
- package/dist/cjs/utils/mapPosition.d.ts +3 -2
- package/dist/cjs/utils/mapPosition.d.ts.map +1 -1
- package/dist/cjs/utils/mapPosition.js +30 -9
- package/dist/cjs/utils/mapPosition.js.map +1 -1
- package/dist/cjs/utils/transferMargin.d.ts +3 -0
- package/dist/cjs/utils/transferMargin.d.ts.map +1 -0
- package/dist/cjs/utils/transferMargin.js +17 -0
- package/dist/cjs/utils/transferMargin.js.map +1 -0
- package/dist/cjs/websocket/LighterWsProvider.d.ts.map +1 -1
- package/dist/cjs/websocket/LighterWsProvider.js +20 -9
- package/dist/cjs/websocket/LighterWsProvider.js.map +1 -1
- package/dist/esm/LighterProvider.d.ts.map +1 -1
- package/dist/esm/LighterProvider.js +67 -22
- package/dist/esm/LighterProvider.js.map +1 -1
- package/dist/esm/accountSummary.d.ts +5 -4
- package/dist/esm/accountSummary.d.ts.map +1 -1
- package/dist/esm/accountSummary.js +35 -6
- package/dist/esm/accountSummary.js.map +1 -1
- package/dist/esm/depositFlow.d.ts +12 -0
- package/dist/esm/depositFlow.d.ts.map +1 -0
- package/dist/esm/depositFlow.js +37 -0
- package/dist/esm/depositFlow.js.map +1 -0
- package/dist/esm/index.d.ts +1 -0
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +1 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/types/account.d.ts +3 -0
- package/dist/esm/types/account.d.ts.map +1 -1
- package/dist/esm/types/ws.d.ts +9 -1
- package/dist/esm/types/ws.d.ts.map +1 -1
- package/dist/esm/utils/decimal.d.ts +15 -0
- package/dist/esm/utils/decimal.d.ts.map +1 -0
- package/dist/esm/utils/decimal.js +39 -0
- package/dist/esm/utils/decimal.js.map +1 -0
- package/dist/esm/utils/index.d.ts +3 -1
- package/dist/esm/utils/index.d.ts.map +1 -1
- package/dist/esm/utils/index.js +3 -1
- package/dist/esm/utils/index.js.map +1 -1
- package/dist/esm/utils/mapOpenPositions.d.ts +2 -2
- package/dist/esm/utils/mapOpenPositions.d.ts.map +1 -1
- package/dist/esm/utils/mapOpenPositions.js.map +1 -1
- package/dist/esm/utils/mapPosition.d.ts +10 -2
- package/dist/esm/utils/mapPosition.d.ts.map +1 -1
- package/dist/esm/utils/mapPosition.js +32 -11
- package/dist/esm/utils/mapPosition.js.map +1 -1
- package/dist/esm/utils/transferMargin.d.ts +11 -0
- package/dist/esm/utils/transferMargin.d.ts.map +1 -0
- package/dist/esm/utils/transferMargin.js +22 -0
- package/dist/esm/utils/transferMargin.js.map +1 -0
- package/dist/esm/websocket/LighterWsProvider.d.ts.map +1 -1
- package/dist/esm/websocket/LighterWsProvider.js +24 -13
- package/dist/esm/websocket/LighterWsProvider.js.map +1 -1
- package/dist/types/LighterProvider.d.ts.map +1 -1
- package/dist/types/accountSummary.d.ts +5 -4
- package/dist/types/accountSummary.d.ts.map +1 -1
- package/dist/types/depositFlow.d.ts +12 -0
- package/dist/types/depositFlow.d.ts.map +1 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/types/account.d.ts +3 -0
- package/dist/types/types/account.d.ts.map +1 -1
- package/dist/types/types/ws.d.ts +9 -1
- package/dist/types/types/ws.d.ts.map +1 -1
- package/dist/types/utils/decimal.d.ts +15 -0
- package/dist/types/utils/decimal.d.ts.map +1 -0
- package/dist/types/utils/index.d.ts +3 -1
- package/dist/types/utils/index.d.ts.map +1 -1
- package/dist/types/utils/mapOpenPositions.d.ts +2 -2
- package/dist/types/utils/mapOpenPositions.d.ts.map +1 -1
- package/dist/types/utils/mapPosition.d.ts +10 -2
- package/dist/types/utils/mapPosition.d.ts.map +1 -1
- package/dist/types/utils/transferMargin.d.ts +11 -0
- package/dist/types/utils/transferMargin.d.ts.map +1 -0
- package/dist/types/websocket/LighterWsProvider.d.ts.map +1 -1
- package/package.json +3 -3
- package/src/LighterProvider.ts +107 -21
- package/src/accountSummary.ts +38 -6
- package/src/depositFlow.ts +59 -0
- package/src/index.ts +1 -0
- package/src/types/account.ts +3 -0
- package/src/types/ws.ts +9 -1
- package/src/utils/decimal.ts +52 -0
- package/src/utils/index.ts +3 -1
- package/src/utils/mapOpenPositions.ts +2 -2
- package/src/utils/mapPosition.ts +40 -14
- package/src/utils/transferMargin.ts +35 -0
- package/src/websocket/LighterWsProvider.ts +41 -11
package/src/LighterProvider.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import {
|
|
2
|
+
type DepositFlow,
|
|
2
3
|
explorerTxUrlFromBase,
|
|
3
4
|
getAssetRegistry,
|
|
4
5
|
getMarketRegistry,
|
|
@@ -9,7 +10,9 @@ import {
|
|
|
9
10
|
type ProviderAccountExistsParams,
|
|
10
11
|
type ProviderGetAccountParams,
|
|
11
12
|
type ProviderGetActivityParams,
|
|
13
|
+
type ProviderGetDepositFlowParams,
|
|
12
14
|
type ProviderGetFillsParams,
|
|
15
|
+
type ProviderGetMarketSettingsParams,
|
|
13
16
|
type ProviderGetOrderParams,
|
|
14
17
|
type ProviderGetOrdersParams,
|
|
15
18
|
type ProviderGetPositionsParams,
|
|
@@ -18,6 +21,7 @@ import {
|
|
|
18
21
|
resolveRetryPolicy,
|
|
19
22
|
type SDKRequestOptions,
|
|
20
23
|
type SignActionsContext,
|
|
24
|
+
toPerpsMarketDisplay,
|
|
21
25
|
} from '@lifi/perps-sdk'
|
|
22
26
|
import type {
|
|
23
27
|
AccountConfig,
|
|
@@ -31,6 +35,7 @@ import type {
|
|
|
31
35
|
FillsResponse,
|
|
32
36
|
LighterAccountConfig,
|
|
33
37
|
LighterProviderKey,
|
|
38
|
+
MarketSettings,
|
|
34
39
|
Order,
|
|
35
40
|
OrdersResponse,
|
|
36
41
|
Position,
|
|
@@ -40,7 +45,12 @@ import type {
|
|
|
40
45
|
SignedActionStep,
|
|
41
46
|
SigningMethod,
|
|
42
47
|
} from '@lifi/perps-types'
|
|
43
|
-
import {
|
|
48
|
+
import {
|
|
49
|
+
ActionType,
|
|
50
|
+
ActivityType,
|
|
51
|
+
MarginMode,
|
|
52
|
+
PerpsErrorCode,
|
|
53
|
+
} from '@lifi/perps-types'
|
|
44
54
|
import type { Address } from 'viem'
|
|
45
55
|
import { projectLighterConfigSettings } from './accountConfig.js'
|
|
46
56
|
import { getAccountSummary } from './accountSummary.js'
|
|
@@ -56,6 +66,7 @@ import {
|
|
|
56
66
|
LIGHTER_PROVIDER_KEY,
|
|
57
67
|
LIGHTER_SPOT_CATEGORY_ID,
|
|
58
68
|
} from './constants.js'
|
|
69
|
+
import { lighterDepositFlow } from './depositFlow.js'
|
|
59
70
|
import { createAuthToken } from './signers/createAuthToken.js'
|
|
60
71
|
import type { LighterKeyStore } from './signers/LighterKeyStore.js'
|
|
61
72
|
import type { LighterReadOnlyTokenManagerOptions } from './signers/LighterReadOnlyTokenManager.js'
|
|
@@ -74,6 +85,7 @@ import type {
|
|
|
74
85
|
LtTransferHistoryResponse,
|
|
75
86
|
LtWithdrawHistoryResponse,
|
|
76
87
|
} from './types/index.js'
|
|
88
|
+
import { LT_MARGIN_MODE_ISOLATED } from './types/index.js'
|
|
77
89
|
import {
|
|
78
90
|
decodeActivityCursor,
|
|
79
91
|
encodeActivityCursor,
|
|
@@ -91,12 +103,15 @@ import {
|
|
|
91
103
|
fetchDetailedAccount,
|
|
92
104
|
formatOrderPrice,
|
|
93
105
|
formatOrderSize,
|
|
106
|
+
leverageFromImf,
|
|
94
107
|
lighterAsset,
|
|
95
108
|
mapFill,
|
|
96
109
|
mapOpenPositions,
|
|
97
110
|
mapOrderDetail,
|
|
111
|
+
positionMarginConstraints,
|
|
98
112
|
toIsoFromMs,
|
|
99
113
|
toIsoFromSeconds,
|
|
114
|
+
toRequiredBig,
|
|
100
115
|
} from './utils/index.js'
|
|
101
116
|
|
|
102
117
|
const ZERO_FEE_TIER = { maker: '0', taker: '0' }
|
|
@@ -666,6 +681,24 @@ export const lighterProvider = (
|
|
|
666
681
|
return { deposits, withdraws, fundings, liquidations, transfers }
|
|
667
682
|
}
|
|
668
683
|
|
|
684
|
+
const resolveAccountExists = async (
|
|
685
|
+
address: Address,
|
|
686
|
+
opts?: SDKRequestOptions
|
|
687
|
+
): Promise<boolean> => {
|
|
688
|
+
try {
|
|
689
|
+
await fetchDetailedAccount(apiClient(opts), address)
|
|
690
|
+
return true
|
|
691
|
+
} catch (err) {
|
|
692
|
+
if (
|
|
693
|
+
err instanceof PerpsError &&
|
|
694
|
+
err.code === PerpsErrorCode.AccountNotFound
|
|
695
|
+
) {
|
|
696
|
+
return false
|
|
697
|
+
}
|
|
698
|
+
throw err
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
|
|
669
702
|
return {
|
|
670
703
|
type: providerKey,
|
|
671
704
|
|
|
@@ -734,7 +767,7 @@ export const lighterProvider = (
|
|
|
734
767
|
normalizeLighterPublicKey(registeredKey.public_key)
|
|
735
768
|
|
|
736
769
|
const positions: Position[] = mapOpenPositions(account.positions, (id) =>
|
|
737
|
-
registry.require(String(id))
|
|
770
|
+
toPerpsMarketDisplay(registry.require(String(id)))
|
|
738
771
|
)
|
|
739
772
|
|
|
740
773
|
const totalMarginUsed = positions.reduce(
|
|
@@ -753,16 +786,24 @@ export const lighterProvider = (
|
|
|
753
786
|
categories.find((c) => c.quoteAsset === null)?.id ??
|
|
754
787
|
LIGHTER_SPOT_CATEGORY_ID
|
|
755
788
|
|
|
756
|
-
//
|
|
757
|
-
//
|
|
758
|
-
//
|
|
759
|
-
|
|
789
|
+
// Cross buying power is isolated from per-position allocations. Lighter
|
|
790
|
+
// reports cross equity (already marked by cross uPnL) separately from
|
|
791
|
+
// the initial margin locked by cross positions.
|
|
792
|
+
const availableMargin = toRequiredBig(
|
|
793
|
+
account.cross_asset_value,
|
|
794
|
+
'cross_asset_value'
|
|
795
|
+
).minus(
|
|
796
|
+
toRequiredBig(
|
|
797
|
+
account.cross_initial_margin_requirement,
|
|
798
|
+
'cross_initial_margin_requirement'
|
|
799
|
+
)
|
|
800
|
+
)
|
|
760
801
|
const collateralBalances: Balance[] = [
|
|
761
802
|
{
|
|
762
803
|
categoryId: perpsCategory?.id ?? providerKey,
|
|
763
804
|
asset: perpsCategory?.quoteAsset ?? lighterAsset('USDC', 'USDC'),
|
|
764
|
-
units:
|
|
765
|
-
valueUsd:
|
|
805
|
+
units: availableMargin.toString(),
|
|
806
|
+
valueUsd: availableMargin.toString(),
|
|
766
807
|
},
|
|
767
808
|
]
|
|
768
809
|
// Spot token holdings — non-collateral. USDC value is 1:1; other tokens
|
|
@@ -826,18 +867,17 @@ export const lighterProvider = (
|
|
|
826
867
|
params: ProviderAccountExistsParams,
|
|
827
868
|
opts?: SDKRequestOptions
|
|
828
869
|
): Promise<boolean> {
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
}
|
|
870
|
+
return resolveAccountExists(params.address, opts)
|
|
871
|
+
},
|
|
872
|
+
|
|
873
|
+
async getDepositFlow(
|
|
874
|
+
params: ProviderGetDepositFlowParams,
|
|
875
|
+
opts?: SDKRequestOptions
|
|
876
|
+
): Promise<DepositFlow> {
|
|
877
|
+
return lighterDepositFlow(
|
|
878
|
+
providerKey,
|
|
879
|
+
await resolveAccountExists(params.address, opts)
|
|
880
|
+
)
|
|
841
881
|
},
|
|
842
882
|
|
|
843
883
|
async getPositions(
|
|
@@ -852,7 +892,7 @@ export const lighterProvider = (
|
|
|
852
892
|
])
|
|
853
893
|
|
|
854
894
|
let positions: Position[] = mapOpenPositions(account.positions, (id) =>
|
|
855
|
-
registry.require(String(id))
|
|
895
|
+
toPerpsMarketDisplay(registry.require(String(id)))
|
|
856
896
|
)
|
|
857
897
|
|
|
858
898
|
if (params.marketId !== undefined) {
|
|
@@ -866,6 +906,50 @@ export const lighterProvider = (
|
|
|
866
906
|
}
|
|
867
907
|
},
|
|
868
908
|
|
|
909
|
+
/**
|
|
910
|
+
* Lighter reports a market's margin mode and leverage only on the
|
|
911
|
+
* account's position row, so a market the account never touched (or a
|
|
912
|
+
* missing account) resolves `undefined` rather than a venue default.
|
|
913
|
+
*/
|
|
914
|
+
async getMarketSettings(
|
|
915
|
+
params: ProviderGetMarketSettingsParams,
|
|
916
|
+
opts?: SDKRequestOptions
|
|
917
|
+
): Promise<MarketSettings | undefined> {
|
|
918
|
+
// Spot markets carry no margin mode or leverage.
|
|
919
|
+
if (params.market.categoryId === LIGHTER_SPOT_CATEGORY_ID) {
|
|
920
|
+
return undefined
|
|
921
|
+
}
|
|
922
|
+
let account: LtDetailedAccount
|
|
923
|
+
try {
|
|
924
|
+
account = await fetchDetailedAccount(apiClient(opts), params.address)
|
|
925
|
+
} catch (err) {
|
|
926
|
+
if (
|
|
927
|
+
err instanceof PerpsError &&
|
|
928
|
+
err.code === PerpsErrorCode.AccountNotFound
|
|
929
|
+
) {
|
|
930
|
+
return undefined
|
|
931
|
+
}
|
|
932
|
+
throw err
|
|
933
|
+
}
|
|
934
|
+
const row = account.positions.find(
|
|
935
|
+
(p) => String(p.market_id) === params.market.marketId
|
|
936
|
+
)
|
|
937
|
+
if (!row) {
|
|
938
|
+
return undefined
|
|
939
|
+
}
|
|
940
|
+
const leverage = leverageFromImf(row.initial_margin_fraction)
|
|
941
|
+
if (leverage === undefined) {
|
|
942
|
+
return undefined
|
|
943
|
+
}
|
|
944
|
+
return {
|
|
945
|
+
marginMode:
|
|
946
|
+
row.margin_mode === LT_MARGIN_MODE_ISOLATED
|
|
947
|
+
? MarginMode.ISOLATED
|
|
948
|
+
: MarginMode.CROSS,
|
|
949
|
+
leverage,
|
|
950
|
+
}
|
|
951
|
+
},
|
|
952
|
+
|
|
869
953
|
/**
|
|
870
954
|
* Lighter's `accountActiveOrders` endpoint takes no limit/cursor and
|
|
871
955
|
* returns every active order for the account, so the response is always
|
|
@@ -1235,6 +1319,8 @@ export const lighterProvider = (
|
|
|
1235
1319
|
|
|
1236
1320
|
estimateLiquidationPrice,
|
|
1237
1321
|
|
|
1322
|
+
positionMarginConstraints,
|
|
1323
|
+
|
|
1238
1324
|
projectConfig(
|
|
1239
1325
|
config: AccountConfig,
|
|
1240
1326
|
setup: ProviderAction[],
|
package/src/accountSummary.ts
CHANGED
|
@@ -1,16 +1,18 @@
|
|
|
1
|
-
import { summarizeAccount } from '@lifi/perps-sdk'
|
|
2
1
|
import type {
|
|
3
2
|
AccountResponse,
|
|
4
3
|
AccountSummary,
|
|
5
4
|
Position,
|
|
6
5
|
} from '@lifi/perps-types'
|
|
6
|
+
import { MarginMode } from '@lifi/perps-types'
|
|
7
|
+
import Big from 'big.js'
|
|
7
8
|
|
|
8
9
|
/**
|
|
9
10
|
* Roll a Lighter {@link AccountResponse} up into an {@link AccountSummary}.
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
11
|
+
*
|
|
12
|
+
* Lighter's cross available balance is already marked to market, while an
|
|
13
|
+
* isolated position's unrealized PnL remains inside that position's isolated
|
|
14
|
+
* sub-account. Reconciliation therefore adds every position's locked margin,
|
|
15
|
+
* but adds unrealized PnL only for isolated positions.
|
|
14
16
|
*
|
|
15
17
|
* @public
|
|
16
18
|
*/
|
|
@@ -18,5 +20,35 @@ export function getAccountSummary(
|
|
|
18
20
|
account: AccountResponse,
|
|
19
21
|
positions: Position[]
|
|
20
22
|
): AccountSummary {
|
|
21
|
-
|
|
23
|
+
let availableMargin = new Big(0)
|
|
24
|
+
for (const balance of account.collateralBalances) {
|
|
25
|
+
availableMargin = availableMargin.plus(balance.valueUsd)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
let nonCollateralValue = new Big(0)
|
|
29
|
+
for (const balance of account.balances) {
|
|
30
|
+
nonCollateralValue = nonCollateralValue.plus(balance.valueUsd)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
let marginUsed = new Big(0)
|
|
34
|
+
let unrealizedPnl = new Big(0)
|
|
35
|
+
let isolatedUnrealizedPnl = new Big(0)
|
|
36
|
+
for (const position of positions) {
|
|
37
|
+
marginUsed = marginUsed.plus(position.marginUsed)
|
|
38
|
+
unrealizedPnl = unrealizedPnl.plus(position.unrealizedPnl)
|
|
39
|
+
if (position.marginMode === MarginMode.ISOLATED) {
|
|
40
|
+
isolatedUnrealizedPnl = isolatedUnrealizedPnl.plus(position.unrealizedPnl)
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return {
|
|
45
|
+
portfolioValue: nonCollateralValue
|
|
46
|
+
.plus(availableMargin)
|
|
47
|
+
.plus(marginUsed)
|
|
48
|
+
.plus(isolatedUnrealizedPnl)
|
|
49
|
+
.toString(),
|
|
50
|
+
availableMargin: availableMargin.toString(),
|
|
51
|
+
marginUsed: marginUsed.toString(),
|
|
52
|
+
unrealizedPnl: unrealizedPnl.toString(),
|
|
53
|
+
}
|
|
22
54
|
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type DeclaredDepositAsset,
|
|
3
|
+
type DepositFlow,
|
|
4
|
+
ETHEREUM_NATIVE_GAS,
|
|
5
|
+
ETHEREUM_USDC,
|
|
6
|
+
LIGHTER_USDC,
|
|
7
|
+
ROBINHOOD_NATIVE_GAS,
|
|
8
|
+
ROBINHOOD_USDG,
|
|
9
|
+
} from '@lifi/perps-sdk'
|
|
10
|
+
import { ActionType, type LighterProviderKey } from '@lifi/perps-types'
|
|
11
|
+
import { LIGHTER_PROVIDER_KEY, LIGHTER_RH_PROVIDER_KEY } from './constants.js'
|
|
12
|
+
|
|
13
|
+
interface LighterDepositAssets {
|
|
14
|
+
/** Collateral token on the venue chain, credited to an account that exists. */
|
|
15
|
+
venueCollateral: DeclaredDepositAsset
|
|
16
|
+
/** Collateral the account-opening deposit is made in, on the chain that deposit runs on. */
|
|
17
|
+
firstDepositCollateral: DeclaredDepositAsset
|
|
18
|
+
firstDepositGas: DeclaredDepositAsset
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const DEPOSIT_ASSETS: Record<LighterProviderKey, LighterDepositAssets> = {
|
|
22
|
+
[LIGHTER_PROVIDER_KEY]: {
|
|
23
|
+
venueCollateral: LIGHTER_USDC,
|
|
24
|
+
firstDepositCollateral: ETHEREUM_USDC,
|
|
25
|
+
firstDepositGas: ETHEREUM_NATIVE_GAS,
|
|
26
|
+
},
|
|
27
|
+
[LIGHTER_RH_PROVIDER_KEY]: {
|
|
28
|
+
venueCollateral: ROBINHOOD_USDG,
|
|
29
|
+
firstDepositCollateral: ROBINHOOD_USDG,
|
|
30
|
+
firstDepositGas: ROBINHOOD_NATIVE_GAS,
|
|
31
|
+
},
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Resolve one Lighter instance's deposit flow from whether the account already
|
|
36
|
+
* exists: an existing account takes a swap into the instance's venue collateral,
|
|
37
|
+
* while an address with no account runs the account-opening deposit on the chain
|
|
38
|
+
* that instance's collateral is deposited from.
|
|
39
|
+
*
|
|
40
|
+
* @public
|
|
41
|
+
*/
|
|
42
|
+
export function lighterDepositFlow(
|
|
43
|
+
providerKey: LighterProviderKey,
|
|
44
|
+
accountExists: boolean
|
|
45
|
+
): DepositFlow {
|
|
46
|
+
const assets = DEPOSIT_ASSETS[providerKey]
|
|
47
|
+
|
|
48
|
+
if (accountExists) {
|
|
49
|
+
return { kind: 'lifiSwap', destination: assets.venueCollateral }
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return {
|
|
53
|
+
kind: 'firstDepositPipeline',
|
|
54
|
+
chainId: assets.firstDepositCollateral.chainId,
|
|
55
|
+
gasAsset: assets.firstDepositGas,
|
|
56
|
+
collateral: assets.firstDepositCollateral,
|
|
57
|
+
bridgeAction: ActionType.DEPOSIT,
|
|
58
|
+
}
|
|
59
|
+
}
|
package/src/index.ts
CHANGED
package/src/types/account.ts
CHANGED
|
@@ -79,6 +79,9 @@ export interface LtDetailedAccount {
|
|
|
79
79
|
assets: LtAccountAsset[]
|
|
80
80
|
total_asset_value: string
|
|
81
81
|
cross_asset_value: string
|
|
82
|
+
/** Initial margin locked by cross positions; isolated positions carry
|
|
83
|
+
* their own `allocated_margin` instead. */
|
|
84
|
+
cross_initial_margin_requirement: string
|
|
82
85
|
approved_integrators?: LtApprovedIntegrator[]
|
|
83
86
|
}
|
|
84
87
|
|
package/src/types/ws.ts
CHANGED
|
@@ -150,7 +150,10 @@ export type LtWsAccountAllTradesMessage = LtWsMessage & {
|
|
|
150
150
|
}
|
|
151
151
|
|
|
152
152
|
/**
|
|
153
|
-
* `user_stats/{account_index}` frame; decimal strings in USD.
|
|
153
|
+
* `user_stats/{account_index}` frame; decimal strings in USD. The top-level
|
|
154
|
+
* figures span the whole account (`available_balance` = total withdrawable,
|
|
155
|
+
* including isolated positions' excess margin); `cross_stats` scopes them to
|
|
156
|
+
* the cross pool, whose `available_balance` is the free tradable collateral.
|
|
154
157
|
* @public
|
|
155
158
|
*/
|
|
156
159
|
export type LtWsUserStatsMessage = LtWsMessage & {
|
|
@@ -158,6 +161,11 @@ export type LtWsUserStatsMessage = LtWsMessage & {
|
|
|
158
161
|
collateral?: string
|
|
159
162
|
portfolio_value?: string
|
|
160
163
|
available_balance?: string
|
|
164
|
+
cross_stats?: {
|
|
165
|
+
collateral: string
|
|
166
|
+
portfolio_value: string
|
|
167
|
+
available_balance: string
|
|
168
|
+
}
|
|
161
169
|
}
|
|
162
170
|
}
|
|
163
171
|
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { PerpsError } from '@lifi/perps-sdk'
|
|
2
|
+
import { PerpsErrorCode } from '@lifi/perps-types'
|
|
3
|
+
import Big from 'big.js'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Exact-decimal parse of a venue decimal string; null when absent or
|
|
7
|
+
* unparsable, so callers can gate on presence before deriving.
|
|
8
|
+
* @public
|
|
9
|
+
*/
|
|
10
|
+
export const toBigOrNull = (value: string | undefined): Big | null => {
|
|
11
|
+
if (value === undefined) {
|
|
12
|
+
return null
|
|
13
|
+
}
|
|
14
|
+
try {
|
|
15
|
+
return new Big(value)
|
|
16
|
+
} catch {
|
|
17
|
+
return null
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Parse a required venue decimal and identify the bad field on failure.
|
|
23
|
+
* @public
|
|
24
|
+
*/
|
|
25
|
+
export const toRequiredBig = (
|
|
26
|
+
value: string | undefined,
|
|
27
|
+
field: string
|
|
28
|
+
): Big => {
|
|
29
|
+
const parsed = toBigOrNull(value)
|
|
30
|
+
if (parsed !== null) {
|
|
31
|
+
return parsed
|
|
32
|
+
}
|
|
33
|
+
throw new PerpsError(
|
|
34
|
+
PerpsErrorCode.SDKError,
|
|
35
|
+
`Lighter field \`${field}\` is not a valid decimal.`
|
|
36
|
+
)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** Parse a required venue decimal that must be greater than zero. */
|
|
40
|
+
export const toPositiveRequiredBig = (
|
|
41
|
+
value: string | undefined,
|
|
42
|
+
field: string
|
|
43
|
+
): Big => {
|
|
44
|
+
const parsed = toRequiredBig(value, field)
|
|
45
|
+
if (parsed.lte(0)) {
|
|
46
|
+
throw new PerpsError(
|
|
47
|
+
PerpsErrorCode.SDKError,
|
|
48
|
+
`Lighter field \`${field}\` must be greater than zero.`
|
|
49
|
+
)
|
|
50
|
+
}
|
|
51
|
+
return parsed
|
|
52
|
+
}
|
package/src/utils/index.ts
CHANGED
|
@@ -9,6 +9,7 @@ export {
|
|
|
9
9
|
} from './activityCursor.js'
|
|
10
10
|
export { LighterApiClient } from './apiClient.js'
|
|
11
11
|
export { assetMarginModeInt, isAssetMarginEnabled } from './assetCollateral.js'
|
|
12
|
+
export { toBigOrNull, toRequiredBig } from './decimal.js'
|
|
12
13
|
export { fetchDetailedAccount } from './fetchDetailedAccount.js'
|
|
13
14
|
export { lighterAsset } from './lighterAsset.js'
|
|
14
15
|
export { estimateLiquidationPrice } from './liquidation.js'
|
|
@@ -24,10 +25,11 @@ export {
|
|
|
24
25
|
mapStatusReason,
|
|
25
26
|
mapTriggerOrder,
|
|
26
27
|
} from './mapOrder.js'
|
|
27
|
-
export { mapPosition } from './mapPosition.js'
|
|
28
|
+
export { leverageFromImf, mapPosition } from './mapPosition.js'
|
|
28
29
|
export { mapInterval } from './ohlcvInterval.js'
|
|
29
30
|
export { formatOrderPrice, formatOrderSize } from './orderFormatting.js'
|
|
30
31
|
export { toIsoFromMs, toIsoFromSeconds } from './time.js'
|
|
32
|
+
export { positionMarginConstraints } from './transferMargin.js'
|
|
31
33
|
export {
|
|
32
34
|
leverageToFraction,
|
|
33
35
|
mapOrderTypeToInt,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { PerpsMarketDisplay, Position } from '@lifi/perps-types'
|
|
2
2
|
import type { LtAccountPosition } from '../types/index.js'
|
|
3
3
|
import { mapPosition } from './mapPosition.js'
|
|
4
4
|
|
|
@@ -11,7 +11,7 @@ import { mapPosition } from './mapPosition.js'
|
|
|
11
11
|
*/
|
|
12
12
|
export const mapOpenPositions = (
|
|
13
13
|
positions: LtAccountPosition[],
|
|
14
|
-
resolveMarket: (marketId: number) =>
|
|
14
|
+
resolveMarket: (marketId: number) => PerpsMarketDisplay
|
|
15
15
|
): Position[] =>
|
|
16
16
|
positions
|
|
17
17
|
.filter((p) => Number.parseFloat(p.position) !== 0)
|
package/src/utils/mapPosition.ts
CHANGED
|
@@ -1,7 +1,29 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { PerpsMarketDisplay, Position } from '@lifi/perps-types'
|
|
2
2
|
import { MarginMode, PositionSide } from '@lifi/perps-types'
|
|
3
|
+
import Big from 'big.js'
|
|
3
4
|
import type { LtAccountPosition } from '../types/index.js'
|
|
4
5
|
import { LT_MARGIN_MODE_ISOLATED } from '../types/index.js'
|
|
6
|
+
import { toPositiveRequiredBig, toRequiredBig } from './decimal.js'
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Display leverage from an IMF percent string: `100 / IMF` in exact decimal
|
|
10
|
+
* arithmetic before conversion to `number`. This value is for displaying the
|
|
11
|
+
* venue setting; provider risk calculations consume the original decimal IMF
|
|
12
|
+
* instead. `undefined` for a non-positive or unparsable IMF.
|
|
13
|
+
* @public
|
|
14
|
+
*/
|
|
15
|
+
export const leverageFromImf = (imf: string): number | undefined => {
|
|
16
|
+
let parsed: Big
|
|
17
|
+
try {
|
|
18
|
+
parsed = new Big(imf)
|
|
19
|
+
} catch {
|
|
20
|
+
return undefined
|
|
21
|
+
}
|
|
22
|
+
if (parsed.lte(0)) {
|
|
23
|
+
return undefined
|
|
24
|
+
}
|
|
25
|
+
return new Big(100).div(parsed).toNumber()
|
|
26
|
+
}
|
|
5
27
|
|
|
6
28
|
/**
|
|
7
29
|
* Map a raw Lighter account position to the generic Position type.
|
|
@@ -10,33 +32,37 @@ import { LT_MARGIN_MODE_ISOLATED } from '../types/index.js'
|
|
|
10
32
|
*/
|
|
11
33
|
export const mapPosition = (
|
|
12
34
|
pos: LtAccountPosition,
|
|
13
|
-
market:
|
|
35
|
+
market: PerpsMarketDisplay
|
|
14
36
|
): Position => {
|
|
15
|
-
const size =
|
|
37
|
+
const size = toRequiredBig(pos.position, 'position')
|
|
16
38
|
const isIsolated = pos.margin_mode === LT_MARGIN_MODE_ISOLATED
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
const
|
|
22
|
-
|
|
39
|
+
const positionValue = toRequiredBig(
|
|
40
|
+
pos.position_value,
|
|
41
|
+
'position_value'
|
|
42
|
+
).abs()
|
|
43
|
+
const imf = toPositiveRequiredBig(
|
|
44
|
+
pos.initial_margin_fraction,
|
|
45
|
+
'initial_margin_fraction'
|
|
46
|
+
)
|
|
47
|
+
const initialMarginRequirement = positionValue.times(imf).div(100)
|
|
23
48
|
const marginUsed = isIsolated
|
|
24
49
|
? pos.allocated_margin
|
|
25
|
-
:
|
|
50
|
+
: initialMarginRequirement.toFixed()
|
|
26
51
|
|
|
27
52
|
return {
|
|
28
53
|
market,
|
|
29
54
|
side: pos.sign >= 0 ? PositionSide.LONG : PositionSide.SHORT,
|
|
30
|
-
size:
|
|
55
|
+
size: size.abs().toFixed(),
|
|
31
56
|
entryPrice: pos.avg_entry_price,
|
|
32
57
|
markPrice:
|
|
33
|
-
|
|
58
|
+
positionValue.eq(0) || size.eq(0)
|
|
34
59
|
? '0'
|
|
35
|
-
: (
|
|
60
|
+
: positionValue.div(size.abs()).toFixed(),
|
|
36
61
|
liquidationPrice: pos.liquidation_price,
|
|
37
62
|
unrealizedPnl: pos.unrealized_pnl,
|
|
38
|
-
leverage:
|
|
63
|
+
leverage: leverageFromImf(pos.initial_margin_fraction) ?? 1,
|
|
39
64
|
marginUsed,
|
|
65
|
+
initialMarginRequirement: initialMarginRequirement.toFixed(),
|
|
40
66
|
marginMode: isIsolated ? MarginMode.ISOLATED : MarginMode.CROSS,
|
|
41
67
|
}
|
|
42
68
|
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import {
|
|
2
|
+
MarginMode,
|
|
3
|
+
type Position,
|
|
4
|
+
PositionMarginAdjustment,
|
|
5
|
+
type PositionMarginConstraints,
|
|
6
|
+
} from '@lifi/perps-types'
|
|
7
|
+
import { toPositiveRequiredBig } from './decimal.js'
|
|
8
|
+
|
|
9
|
+
const AMOUNT_INCREMENT = '0.000001'
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Lighter's exact isolated-margin requirement. Lighter publishes no separate
|
|
13
|
+
* notional floor, so the provider-normalized initial-margin requirement is
|
|
14
|
+
* the retained amount.
|
|
15
|
+
*
|
|
16
|
+
* @see https://docs.lighter.xyz/trading/liquidations-and-llp-insurance-fund
|
|
17
|
+
* @public
|
|
18
|
+
*/
|
|
19
|
+
export function positionMarginConstraints(
|
|
20
|
+
position: Position
|
|
21
|
+
): PositionMarginConstraints | undefined {
|
|
22
|
+
if (
|
|
23
|
+
position.marginMode !== MarginMode.ISOLATED ||
|
|
24
|
+
position.market.positionMarginAdjustment === PositionMarginAdjustment.NONE
|
|
25
|
+
) {
|
|
26
|
+
return undefined
|
|
27
|
+
}
|
|
28
|
+
return {
|
|
29
|
+
minimumMarginRequirement: toPositiveRequiredBig(
|
|
30
|
+
position.initialMarginRequirement,
|
|
31
|
+
'initialMarginRequirement'
|
|
32
|
+
).toFixed(),
|
|
33
|
+
amountIncrement: AMOUNT_INCREMENT,
|
|
34
|
+
}
|
|
35
|
+
}
|