@lifi/perps-sdk-provider-lighter 0.2.0-alpha.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/LICENSE +201 -0
- package/README.md +176 -0
- package/dist/cjs/LighterProvider.d.ts +20 -0
- package/dist/cjs/LighterProvider.d.ts.map +1 -0
- package/dist/cjs/LighterProvider.js +592 -0
- package/dist/cjs/LighterProvider.js.map +1 -0
- package/dist/cjs/accountConfig.d.ts +3 -0
- package/dist/cjs/accountConfig.d.ts.map +1 -0
- package/dist/cjs/accountConfig.js +69 -0
- package/dist/cjs/accountConfig.js.map +1 -0
- package/dist/cjs/accountSummary.d.ts +3 -0
- package/dist/cjs/accountSummary.d.ts.map +1 -0
- package/dist/cjs/accountSummary.js +8 -0
- package/dist/cjs/accountSummary.js.map +1 -0
- package/dist/cjs/constants.d.ts +14 -0
- package/dist/cjs/constants.d.ts.map +1 -0
- package/dist/cjs/constants.js +18 -0
- package/dist/cjs/constants.js.map +1 -0
- package/dist/cjs/index.d.ts +11 -0
- package/dist/cjs/index.d.ts.map +1 -0
- package/dist/cjs/index.js +35 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/cjs/package.json +1 -0
- package/dist/cjs/signers/LighterKeyStore.d.ts +18 -0
- package/dist/cjs/signers/LighterKeyStore.d.ts.map +1 -0
- package/dist/cjs/signers/LighterKeyStore.js +54 -0
- package/dist/cjs/signers/LighterKeyStore.js.map +1 -0
- package/dist/cjs/signers/LighterReadOnlyTokenManager.d.ts +56 -0
- package/dist/cjs/signers/LighterReadOnlyTokenManager.d.ts.map +1 -0
- package/dist/cjs/signers/LighterReadOnlyTokenManager.js +127 -0
- package/dist/cjs/signers/LighterReadOnlyTokenManager.js.map +1 -0
- package/dist/cjs/signers/LighterSigner.d.ts +41 -0
- package/dist/cjs/signers/LighterSigner.d.ts.map +1 -0
- package/dist/cjs/signers/LighterSigner.js +175 -0
- package/dist/cjs/signers/LighterSigner.js.map +1 -0
- package/dist/cjs/signers/createAuthToken.d.ts +12 -0
- package/dist/cjs/signers/createAuthToken.d.ts.map +1 -0
- package/dist/cjs/signers/createAuthToken.js +28 -0
- package/dist/cjs/signers/createAuthToken.js.map +1 -0
- package/dist/cjs/signers/index.d.ts +11 -0
- package/dist/cjs/signers/index.d.ts.map +1 -0
- package/dist/cjs/signers/index.js +19 -0
- package/dist/cjs/signers/index.js.map +1 -0
- package/dist/cjs/signers/signActions.d.ts +15 -0
- package/dist/cjs/signers/signActions.d.ts.map +1 -0
- package/dist/cjs/signers/signActions.js +142 -0
- package/dist/cjs/signers/signActions.js.map +1 -0
- package/dist/cjs/signers/wasmLoader.d.ts +42 -0
- package/dist/cjs/signers/wasmLoader.d.ts.map +1 -0
- package/dist/cjs/signers/wasmLoader.js +166 -0
- package/dist/cjs/signers/wasmLoader.js.map +1 -0
- package/dist/cjs/types/account.d.ts +97 -0
- package/dist/cjs/types/account.d.ts.map +1 -0
- package/dist/cjs/types/account.js +3 -0
- package/dist/cjs/types/account.js.map +1 -0
- package/dist/cjs/types/action.d.ts +72 -0
- package/dist/cjs/types/action.d.ts.map +1 -0
- package/dist/cjs/types/action.js +23 -0
- package/dist/cjs/types/action.js.map +1 -0
- package/dist/cjs/types/activity.d.ts +73 -0
- package/dist/cjs/types/activity.d.ts.map +1 -0
- package/dist/cjs/types/activity.js +3 -0
- package/dist/cjs/types/activity.js.map +1 -0
- package/dist/cjs/types/asset.d.ts +122 -0
- package/dist/cjs/types/asset.d.ts.map +1 -0
- package/dist/cjs/types/asset.js +3 -0
- package/dist/cjs/types/asset.js.map +1 -0
- package/dist/cjs/types/fill.d.ts +29 -0
- package/dist/cjs/types/fill.d.ts.map +1 -0
- package/dist/cjs/types/fill.js +3 -0
- package/dist/cjs/types/fill.js.map +1 -0
- package/dist/cjs/types/index.d.ts +9 -0
- package/dist/cjs/types/index.d.ts.map +1 -0
- package/dist/cjs/types/index.js +25 -0
- package/dist/cjs/types/index.js.map +1 -0
- package/dist/cjs/types/order.d.ts +57 -0
- package/dist/cjs/types/order.d.ts.map +1 -0
- package/dist/cjs/types/order.js +3 -0
- package/dist/cjs/types/order.js.map +1 -0
- package/dist/cjs/types/submit.d.ts +39 -0
- package/dist/cjs/types/submit.d.ts.map +1 -0
- package/dist/cjs/types/submit.js +3 -0
- package/dist/cjs/types/submit.js.map +1 -0
- package/dist/cjs/types/ws.d.ts +84 -0
- package/dist/cjs/types/ws.d.ts.map +1 -0
- package/dist/cjs/types/ws.js +3 -0
- package/dist/cjs/types/ws.js.map +1 -0
- package/dist/cjs/utils/activityCursor.d.ts +12 -0
- package/dist/cjs/utils/activityCursor.d.ts.map +1 -0
- package/dist/cjs/utils/activityCursor.js +85 -0
- package/dist/cjs/utils/activityCursor.js.map +1 -0
- package/dist/cjs/utils/apiClient.d.ts +26 -0
- package/dist/cjs/utils/apiClient.d.ts.map +1 -0
- package/dist/cjs/utils/apiClient.js +94 -0
- package/dist/cjs/utils/apiClient.js.map +1 -0
- package/dist/cjs/utils/fetchDetailedAccount.d.ts +5 -0
- package/dist/cjs/utils/fetchDetailedAccount.d.ts.map +1 -0
- package/dist/cjs/utils/fetchDetailedAccount.js +22 -0
- package/dist/cjs/utils/fetchDetailedAccount.js.map +1 -0
- package/dist/cjs/utils/index.d.ts +16 -0
- package/dist/cjs/utils/index.d.ts.map +1 -0
- package/dist/cjs/utils/index.js +45 -0
- package/dist/cjs/utils/index.js.map +1 -0
- package/dist/cjs/utils/lighterAsset.d.ts +3 -0
- package/dist/cjs/utils/lighterAsset.d.ts.map +1 -0
- package/dist/cjs/utils/lighterAsset.js +12 -0
- package/dist/cjs/utils/lighterAsset.js.map +1 -0
- package/dist/cjs/utils/liquidation.d.ts +4 -0
- package/dist/cjs/utils/liquidation.d.ts.map +1 -0
- package/dist/cjs/utils/liquidation.js +16 -0
- package/dist/cjs/utils/liquidation.js.map +1 -0
- package/dist/cjs/utils/mapFill.d.ts +4 -0
- package/dist/cjs/utils/mapFill.d.ts.map +1 -0
- package/dist/cjs/utils/mapFill.js +61 -0
- package/dist/cjs/utils/mapFill.js.map +1 -0
- package/dist/cjs/utils/mapOpenPositions.d.ts +4 -0
- package/dist/cjs/utils/mapOpenPositions.d.ts.map +1 -0
- package/dist/cjs/utils/mapOpenPositions.js +9 -0
- package/dist/cjs/utils/mapOpenPositions.js.map +1 -0
- package/dist/cjs/utils/mapOrder.d.ts +15 -0
- package/dist/cjs/utils/mapOrder.d.ts.map +1 -0
- package/dist/cjs/utils/mapOrder.js +164 -0
- package/dist/cjs/utils/mapOrder.js.map +1 -0
- package/dist/cjs/utils/mapPosition.d.ts +4 -0
- package/dist/cjs/utils/mapPosition.d.ts.map +1 -0
- package/dist/cjs/utils/mapPosition.js +30 -0
- package/dist/cjs/utils/mapPosition.js.map +1 -0
- package/dist/cjs/utils/ohlcvInterval.d.ts +2 -0
- package/dist/cjs/utils/ohlcvInterval.d.ts.map +1 -0
- package/dist/cjs/utils/ohlcvInterval.js +26 -0
- package/dist/cjs/utils/ohlcvInterval.js.map +1 -0
- package/dist/cjs/utils/orderFormatting.d.ts +4 -0
- package/dist/cjs/utils/orderFormatting.d.ts.map +1 -0
- package/dist/cjs/utils/orderFormatting.js +23 -0
- package/dist/cjs/utils/orderFormatting.js.map +1 -0
- package/dist/cjs/utils/time.d.ts +3 -0
- package/dist/cjs/utils/time.d.ts.map +1 -0
- package/dist/cjs/utils/time.js +8 -0
- package/dist/cjs/utils/time.js.map +1 -0
- package/dist/cjs/utils/wireEncoding.d.ts +9 -0
- package/dist/cjs/utils/wireEncoding.d.ts.map +1 -0
- package/dist/cjs/utils/wireEncoding.js +87 -0
- package/dist/cjs/utils/wireEncoding.js.map +1 -0
- package/dist/cjs/websocket/LighterWsProvider.d.ts +45 -0
- package/dist/cjs/websocket/LighterWsProvider.d.ts.map +1 -0
- package/dist/cjs/websocket/LighterWsProvider.js +396 -0
- package/dist/cjs/websocket/LighterWsProvider.js.map +1 -0
- package/dist/esm/LighterProvider.d.ts +105 -0
- package/dist/esm/LighterProvider.d.ts.map +1 -0
- package/dist/esm/LighterProvider.js +684 -0
- package/dist/esm/LighterProvider.js.map +1 -0
- package/dist/esm/accountConfig.d.ts +14 -0
- package/dist/esm/accountConfig.d.ts.map +1 -0
- package/dist/esm/accountConfig.js +135 -0
- package/dist/esm/accountConfig.js.map +1 -0
- package/dist/esm/accountSummary.d.ts +12 -0
- package/dist/esm/accountSummary.d.ts.map +1 -0
- package/dist/esm/accountSummary.js +14 -0
- package/dist/esm/accountSummary.js.map +1 -0
- package/dist/esm/constants.d.ts +74 -0
- package/dist/esm/constants.d.ts.map +1 -0
- package/dist/esm/constants.js +73 -0
- package/dist/esm/constants.js.map +1 -0
- package/dist/esm/index.d.ts +22 -0
- package/dist/esm/index.d.ts.map +1 -0
- package/dist/esm/index.js +22 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/package.json +1 -0
- package/dist/esm/signers/LighterKeyStore.d.ts +24 -0
- package/dist/esm/signers/LighterKeyStore.d.ts.map +1 -0
- package/dist/esm/signers/LighterKeyStore.js +55 -0
- package/dist/esm/signers/LighterKeyStore.js.map +1 -0
- package/dist/esm/signers/LighterReadOnlyTokenManager.d.ts +157 -0
- package/dist/esm/signers/LighterReadOnlyTokenManager.d.ts.map +1 -0
- package/dist/esm/signers/LighterReadOnlyTokenManager.js +194 -0
- package/dist/esm/signers/LighterReadOnlyTokenManager.js.map +1 -0
- package/dist/esm/signers/LighterSigner.d.ts +94 -0
- package/dist/esm/signers/LighterSigner.d.ts.map +1 -0
- package/dist/esm/signers/LighterSigner.js +228 -0
- package/dist/esm/signers/LighterSigner.js.map +1 -0
- package/dist/esm/signers/createAuthToken.d.ts +36 -0
- package/dist/esm/signers/createAuthToken.d.ts.map +1 -0
- package/dist/esm/signers/createAuthToken.js +42 -0
- package/dist/esm/signers/createAuthToken.js.map +1 -0
- package/dist/esm/signers/index.d.ts +11 -0
- package/dist/esm/signers/index.d.ts.map +1 -0
- package/dist/esm/signers/index.js +7 -0
- package/dist/esm/signers/index.js.map +1 -0
- package/dist/esm/signers/signActions.d.ts +54 -0
- package/dist/esm/signers/signActions.d.ts.map +1 -0
- package/dist/esm/signers/signActions.js +193 -0
- package/dist/esm/signers/signActions.js.map +1 -0
- package/dist/esm/signers/wasmLoader.d.ts +79 -0
- package/dist/esm/signers/wasmLoader.d.ts.map +1 -0
- package/dist/esm/signers/wasmLoader.js +180 -0
- package/dist/esm/signers/wasmLoader.js.map +1 -0
- package/dist/esm/types/account.d.ts +113 -0
- package/dist/esm/types/account.d.ts.map +1 -0
- package/dist/esm/types/account.js +4 -0
- package/dist/esm/types/account.js.map +1 -0
- package/dist/esm/types/action.d.ts +146 -0
- package/dist/esm/types/action.d.ts.map +1 -0
- package/dist/esm/types/action.js +50 -0
- package/dist/esm/types/action.js.map +1 -0
- package/dist/esm/types/activity.d.ts +83 -0
- package/dist/esm/types/activity.d.ts.map +1 -0
- package/dist/esm/types/activity.js +4 -0
- package/dist/esm/types/activity.js.map +1 -0
- package/dist/esm/types/asset.d.ts +134 -0
- package/dist/esm/types/asset.d.ts.map +1 -0
- package/dist/esm/types/asset.js +3 -0
- package/dist/esm/types/asset.js.map +1 -0
- package/dist/esm/types/fill.d.ts +31 -0
- package/dist/esm/types/fill.d.ts.map +1 -0
- package/dist/esm/types/fill.js +3 -0
- package/dist/esm/types/fill.js.map +1 -0
- package/dist/esm/types/index.d.ts +9 -0
- package/dist/esm/types/index.d.ts.map +1 -0
- package/dist/esm/types/index.js +12 -0
- package/dist/esm/types/index.js.map +1 -0
- package/dist/esm/types/order.d.ts +61 -0
- package/dist/esm/types/order.d.ts.map +1 -0
- package/dist/esm/types/order.js +3 -0
- package/dist/esm/types/order.js.map +1 -0
- package/dist/esm/types/submit.d.ts +76 -0
- package/dist/esm/types/submit.d.ts.map +1 -0
- package/dist/esm/types/submit.js +3 -0
- package/dist/esm/types/submit.js.map +1 -0
- package/dist/esm/types/ws.d.ts +124 -0
- package/dist/esm/types/ws.d.ts.map +1 -0
- package/dist/esm/types/ws.js +11 -0
- package/dist/esm/types/ws.js.map +1 -0
- package/dist/esm/utils/activityCursor.d.ts +51 -0
- package/dist/esm/utils/activityCursor.d.ts.map +1 -0
- package/dist/esm/utils/activityCursor.js +99 -0
- package/dist/esm/utils/activityCursor.js.map +1 -0
- package/dist/esm/utils/apiClient.d.ts +65 -0
- package/dist/esm/utils/apiClient.d.ts.map +1 -0
- package/dist/esm/utils/apiClient.js +131 -0
- package/dist/esm/utils/apiClient.js.map +1 -0
- package/dist/esm/utils/fetchDetailedAccount.d.ts +11 -0
- package/dist/esm/utils/fetchDetailedAccount.d.ts.map +1 -0
- package/dist/esm/utils/fetchDetailedAccount.js +24 -0
- package/dist/esm/utils/fetchDetailedAccount.js.map +1 -0
- package/dist/esm/utils/index.d.ts +16 -0
- package/dist/esm/utils/index.d.ts.map +1 -0
- package/dist/esm/utils/index.js +17 -0
- package/dist/esm/utils/index.js.map +1 -0
- package/dist/esm/utils/lighterAsset.d.ts +4 -0
- package/dist/esm/utils/lighterAsset.d.ts.map +1 -0
- package/dist/esm/utils/lighterAsset.js +9 -0
- package/dist/esm/utils/lighterAsset.js.map +1 -0
- package/dist/esm/utils/liquidation.d.ts +22 -0
- package/dist/esm/utils/liquidation.d.ts.map +1 -0
- package/dist/esm/utils/liquidation.js +31 -0
- package/dist/esm/utils/liquidation.js.map +1 -0
- package/dist/esm/utils/mapFill.d.ts +10 -0
- package/dist/esm/utils/mapFill.d.ts.map +1 -0
- package/dist/esm/utils/mapFill.js +72 -0
- package/dist/esm/utils/mapFill.js.map +1 -0
- package/dist/esm/utils/mapOpenPositions.d.ts +11 -0
- package/dist/esm/utils/mapOpenPositions.d.ts.map +1 -0
- package/dist/esm/utils/mapOpenPositions.js +12 -0
- package/dist/esm/utils/mapOpenPositions.js.map +1 -0
- package/dist/esm/utils/mapOrder.d.ts +72 -0
- package/dist/esm/utils/mapOrder.d.ts.map +1 -0
- package/dist/esm/utils/mapOrder.js +219 -0
- package/dist/esm/utils/mapOrder.js.map +1 -0
- package/dist/esm/utils/mapPosition.d.ts +9 -0
- package/dist/esm/utils/mapPosition.d.ts.map +1 -0
- package/dist/esm/utils/mapPosition.js +34 -0
- package/dist/esm/utils/mapPosition.js.map +1 -0
- package/dist/esm/utils/ohlcvInterval.d.ts +3 -0
- package/dist/esm/utils/ohlcvInterval.d.ts.map +1 -0
- package/dist/esm/utils/ohlcvInterval.js +30 -0
- package/dist/esm/utils/ohlcvInterval.js.map +1 -0
- package/dist/esm/utils/orderFormatting.d.ts +29 -0
- package/dist/esm/utils/orderFormatting.d.ts.map +1 -0
- package/dist/esm/utils/orderFormatting.js +41 -0
- package/dist/esm/utils/orderFormatting.js.map +1 -0
- package/dist/esm/utils/time.d.ts +5 -0
- package/dist/esm/utils/time.d.ts.map +1 -0
- package/dist/esm/utils/time.js +5 -0
- package/dist/esm/utils/time.js.map +1 -0
- package/dist/esm/utils/wireEncoding.d.ts +72 -0
- package/dist/esm/utils/wireEncoding.d.ts.map +1 -0
- package/dist/esm/utils/wireEncoding.js +129 -0
- package/dist/esm/utils/wireEncoding.js.map +1 -0
- package/dist/esm/websocket/LighterWsProvider.d.ts +97 -0
- package/dist/esm/websocket/LighterWsProvider.d.ts.map +1 -0
- package/dist/esm/websocket/LighterWsProvider.js +489 -0
- package/dist/esm/websocket/LighterWsProvider.js.map +1 -0
- package/dist/types/LighterProvider.d.ts +105 -0
- package/dist/types/LighterProvider.d.ts.map +1 -0
- package/dist/types/accountConfig.d.ts +14 -0
- package/dist/types/accountConfig.d.ts.map +1 -0
- package/dist/types/accountSummary.d.ts +12 -0
- package/dist/types/accountSummary.d.ts.map +1 -0
- package/dist/types/constants.d.ts +74 -0
- package/dist/types/constants.d.ts.map +1 -0
- package/dist/types/index.d.ts +22 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/signers/LighterKeyStore.d.ts +24 -0
- package/dist/types/signers/LighterKeyStore.d.ts.map +1 -0
- package/dist/types/signers/LighterReadOnlyTokenManager.d.ts +157 -0
- package/dist/types/signers/LighterReadOnlyTokenManager.d.ts.map +1 -0
- package/dist/types/signers/LighterSigner.d.ts +94 -0
- package/dist/types/signers/LighterSigner.d.ts.map +1 -0
- package/dist/types/signers/createAuthToken.d.ts +36 -0
- package/dist/types/signers/createAuthToken.d.ts.map +1 -0
- package/dist/types/signers/index.d.ts +11 -0
- package/dist/types/signers/index.d.ts.map +1 -0
- package/dist/types/signers/signActions.d.ts +54 -0
- package/dist/types/signers/signActions.d.ts.map +1 -0
- package/dist/types/signers/wasmLoader.d.ts +79 -0
- package/dist/types/signers/wasmLoader.d.ts.map +1 -0
- package/dist/types/types/account.d.ts +113 -0
- package/dist/types/types/account.d.ts.map +1 -0
- package/dist/types/types/action.d.ts +146 -0
- package/dist/types/types/action.d.ts.map +1 -0
- package/dist/types/types/activity.d.ts +83 -0
- package/dist/types/types/activity.d.ts.map +1 -0
- package/dist/types/types/asset.d.ts +134 -0
- package/dist/types/types/asset.d.ts.map +1 -0
- package/dist/types/types/fill.d.ts +31 -0
- package/dist/types/types/fill.d.ts.map +1 -0
- package/dist/types/types/index.d.ts +9 -0
- package/dist/types/types/index.d.ts.map +1 -0
- package/dist/types/types/order.d.ts +61 -0
- package/dist/types/types/order.d.ts.map +1 -0
- package/dist/types/types/submit.d.ts +76 -0
- package/dist/types/types/submit.d.ts.map +1 -0
- package/dist/types/types/ws.d.ts +124 -0
- package/dist/types/types/ws.d.ts.map +1 -0
- package/dist/types/utils/activityCursor.d.ts +51 -0
- package/dist/types/utils/activityCursor.d.ts.map +1 -0
- package/dist/types/utils/apiClient.d.ts +65 -0
- package/dist/types/utils/apiClient.d.ts.map +1 -0
- package/dist/types/utils/fetchDetailedAccount.d.ts +11 -0
- package/dist/types/utils/fetchDetailedAccount.d.ts.map +1 -0
- package/dist/types/utils/index.d.ts +16 -0
- package/dist/types/utils/index.d.ts.map +1 -0
- package/dist/types/utils/lighterAsset.d.ts +4 -0
- package/dist/types/utils/lighterAsset.d.ts.map +1 -0
- package/dist/types/utils/liquidation.d.ts +22 -0
- package/dist/types/utils/liquidation.d.ts.map +1 -0
- package/dist/types/utils/mapFill.d.ts +10 -0
- package/dist/types/utils/mapFill.d.ts.map +1 -0
- package/dist/types/utils/mapOpenPositions.d.ts +11 -0
- package/dist/types/utils/mapOpenPositions.d.ts.map +1 -0
- package/dist/types/utils/mapOrder.d.ts +72 -0
- package/dist/types/utils/mapOrder.d.ts.map +1 -0
- package/dist/types/utils/mapPosition.d.ts +9 -0
- package/dist/types/utils/mapPosition.d.ts.map +1 -0
- package/dist/types/utils/ohlcvInterval.d.ts +3 -0
- package/dist/types/utils/ohlcvInterval.d.ts.map +1 -0
- package/dist/types/utils/orderFormatting.d.ts +29 -0
- package/dist/types/utils/orderFormatting.d.ts.map +1 -0
- package/dist/types/utils/time.d.ts +5 -0
- package/dist/types/utils/time.d.ts.map +1 -0
- package/dist/types/utils/wireEncoding.d.ts +72 -0
- package/dist/types/utils/wireEncoding.d.ts.map +1 -0
- package/dist/types/websocket/LighterWsProvider.d.ts +97 -0
- package/dist/types/websocket/LighterWsProvider.d.ts.map +1 -0
- package/dist/wasm/lighter-signer.wasm +0 -0
- package/dist/wasm/wasm_exec.js +575 -0
- package/package.json +67 -0
- package/src/LighterProvider.ts +1167 -0
- package/src/accountConfig.ts +162 -0
- package/src/accountSummary.ts +22 -0
- package/src/constants.ts +89 -0
- package/src/index.ts +37 -0
- package/src/signers/LighterKeyStore.ts +79 -0
- package/src/signers/LighterReadOnlyTokenManager.ts +340 -0
- package/src/signers/LighterSigner.ts +400 -0
- package/src/signers/createAuthToken.ts +69 -0
- package/src/signers/index.ts +36 -0
- package/src/signers/signActions.ts +311 -0
- package/src/signers/wasmLoader.ts +293 -0
- package/src/types/account.ts +123 -0
- package/src/types/action.ts +163 -0
- package/src/types/activity.ts +94 -0
- package/src/types/asset.ts +146 -0
- package/src/types/fill.ts +42 -0
- package/src/types/index.ts +12 -0
- package/src/types/order.ts +65 -0
- package/src/types/submit.ts +83 -0
- package/src/types/ws.ts +136 -0
- package/src/utils/activityCursor.ts +150 -0
- package/src/utils/apiClient.ts +185 -0
- package/src/utils/fetchDetailedAccount.ts +46 -0
- package/src/utils/index.ts +38 -0
- package/src/utils/lighterAsset.ts +10 -0
- package/src/utils/liquidation.ts +39 -0
- package/src/utils/mapFill.ts +108 -0
- package/src/utils/mapOpenPositions.ts +18 -0
- package/src/utils/mapOrder.ts +258 -0
- package/src/utils/mapPosition.ts +42 -0
- package/src/utils/ohlcvInterval.ts +34 -0
- package/src/utils/orderFormatting.ts +46 -0
- package/src/utils/time.ts +6 -0
- package/src/utils/wireEncoding.ts +179 -0
- package/src/websocket/LighterWsProvider.ts +725 -0
- package/wasm/lighter-signer.wasm +0 -0
- package/wasm/wasm_exec.js +575 -0
|
@@ -0,0 +1,725 @@
|
|
|
1
|
+
import {
|
|
2
|
+
cachePromise,
|
|
3
|
+
getMarketRegistry,
|
|
4
|
+
type MarketRegistry,
|
|
5
|
+
type PerpsSDKClient,
|
|
6
|
+
type ProviderGetQuoteParams,
|
|
7
|
+
type QuoteListener,
|
|
8
|
+
ReconnectingWebSocket,
|
|
9
|
+
resolveRetryPolicy,
|
|
10
|
+
resolveSubscribeQuote,
|
|
11
|
+
WsProviderBase,
|
|
12
|
+
type WsProviderFactory,
|
|
13
|
+
wsLog,
|
|
14
|
+
} from '@lifi/perps-sdk'
|
|
15
|
+
import type { Fill, Position, Subscription } from '@lifi/perps-types'
|
|
16
|
+
import type { Address } from 'viem'
|
|
17
|
+
import { LIGHTER_BASE_FEE_TIER, LIGHTER_PROVIDER_KEY } from '../constants.js'
|
|
18
|
+
import type {
|
|
19
|
+
LtAccountPosition,
|
|
20
|
+
LtOrder,
|
|
21
|
+
LtTrade,
|
|
22
|
+
LtWsAccountAllOrdersMessage,
|
|
23
|
+
LtWsAccountAllPositionsMessage,
|
|
24
|
+
LtWsAccountAllTradesMessage,
|
|
25
|
+
LtWsMarketStats,
|
|
26
|
+
LtWsMarketStatsAllMessage,
|
|
27
|
+
LtWsMessage,
|
|
28
|
+
LtWsOrderBook,
|
|
29
|
+
LtWsOrderBookMessage,
|
|
30
|
+
LtWsSpotMarketStats,
|
|
31
|
+
LtWsSpotMarketStatsAllMessage,
|
|
32
|
+
} from '../types/index.js'
|
|
33
|
+
import { LIGHTER_RETRY_DEFAULTS, LighterApiClient } from '../utils/apiClient.js'
|
|
34
|
+
import {
|
|
35
|
+
classifyAndMapOrders,
|
|
36
|
+
fetchDetailedAccount,
|
|
37
|
+
mapFill,
|
|
38
|
+
mapPosition,
|
|
39
|
+
} from '../utils/index.js'
|
|
40
|
+
|
|
41
|
+
// Public channels: `prices` (market_stats/all + spot_market_stats/all),
|
|
42
|
+
// `orderbook` (order_book/N).
|
|
43
|
+
// Authenticated channels (require an `authProvider` option):
|
|
44
|
+
// - orderUpdates → account_all_orders/{account_index}
|
|
45
|
+
// - fills → account_all_trades/{account_index}
|
|
46
|
+
// - positions → account_all_positions/{account_index}
|
|
47
|
+
//
|
|
48
|
+
// Auth pattern (per Lighter WS spec): the subscribe payload carries the
|
|
49
|
+
// token directly — `{ type: "subscribe", channel: "...", auth: "<token>" }`.
|
|
50
|
+
// Tokens are created via the Lighter WASM signer (CreateAuthToken) by the
|
|
51
|
+
// caller; we re-request a fresh token on every subscribe send so reconnects
|
|
52
|
+
// after the original token expires automatically pick up a new one.
|
|
53
|
+
//
|
|
54
|
+
// account_index is resolved per-address via `/api/v1/account?by=l1_address`
|
|
55
|
+
// and cached for the lifetime of the provider — Lighter's account index is
|
|
56
|
+
// stable for a given L1 wallet.
|
|
57
|
+
//
|
|
58
|
+
// The market identity for each `market_id` is resolved from the shared
|
|
59
|
+
// MarketRegistry. The canonical `Market.id` for Lighter is `String(market_id)`
|
|
60
|
+
// ("0", "1", …); the full market identity (base/quote `Asset`) is carried
|
|
61
|
+
// verbatim onto mapped orders/fills/positions.
|
|
62
|
+
//
|
|
63
|
+
// Orderbook is stateful: the first message is a full snapshot, subsequent
|
|
64
|
+
// messages are deltas where size=0 deletes a level.
|
|
65
|
+
|
|
66
|
+
const DEFAULT_WS_URL = 'wss://mainnet.zklighter.elliot.ai/stream'
|
|
67
|
+
const DEFAULT_REST_URL = 'https://mainnet.zklighter.elliot.ai'
|
|
68
|
+
|
|
69
|
+
const LIGHTER_AUTH_CHANNEL = {
|
|
70
|
+
orderUpdates: 'account_all_orders',
|
|
71
|
+
fills: 'account_all_trades',
|
|
72
|
+
positions: 'account_all_positions',
|
|
73
|
+
} as const
|
|
74
|
+
|
|
75
|
+
/** Channels whose handlers resolve market identity from the registry. */
|
|
76
|
+
function channelNeedsMarkets(channel: Subscription['channel']): boolean {
|
|
77
|
+
return (
|
|
78
|
+
channel === 'orderUpdates' || channel === 'fills' || channel === 'positions'
|
|
79
|
+
)
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Creates a fresh Lighter auth token for the given L1 address, or returns
|
|
84
|
+
* `undefined` if no API key is registered. Called both on initial subscribe
|
|
85
|
+
* and on every reconnect, so it must always return a token valid for at
|
|
86
|
+
* least the next few minutes.
|
|
87
|
+
*
|
|
88
|
+
* @public
|
|
89
|
+
*/
|
|
90
|
+
export type LighterAuthProvider = (
|
|
91
|
+
address: Address
|
|
92
|
+
) => Promise<string | undefined>
|
|
93
|
+
|
|
94
|
+
interface SubState {
|
|
95
|
+
/** Raw Lighter WS channel name (e.g. `order_book/5`, `account_all_orders/42`). */
|
|
96
|
+
channel: string
|
|
97
|
+
/** L1 address that triggered this sub (auth channels only). */
|
|
98
|
+
address?: Address
|
|
99
|
+
/** Whether this channel needs an `auth` field on each subscribe send. */
|
|
100
|
+
needsAuth: boolean
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
interface OrderbookState {
|
|
104
|
+
bids: Map<string, string>
|
|
105
|
+
asks: Map<string, string>
|
|
106
|
+
assetId: string
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Options for {@link lighterWsProvider} / {@link LighterWsProvider}.
|
|
111
|
+
*
|
|
112
|
+
* @public
|
|
113
|
+
*/
|
|
114
|
+
export interface LighterWsProviderOptions {
|
|
115
|
+
/** REST base URL for `/api/v1/account` lookups. Defaults to mainnet. */
|
|
116
|
+
restUrl?: string
|
|
117
|
+
/**
|
|
118
|
+
* Async function returning a Lighter auth token for an address. Required
|
|
119
|
+
* for authenticated channels (orderUpdates, positions). Without it those
|
|
120
|
+
* subscriptions will throw at subscribe time.
|
|
121
|
+
*/
|
|
122
|
+
authProvider?: LighterAuthProvider
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Lighter realtime WS provider (extends {@link WsProviderBase}): subscribes to
|
|
127
|
+
* Lighter's WS channels (orderbook, prices, orders, positions), attaching auth
|
|
128
|
+
* tokens to gated channels. Construct via {@link lighterWsProvider}.
|
|
129
|
+
*
|
|
130
|
+
* @public
|
|
131
|
+
*/
|
|
132
|
+
export class LighterWsProvider extends WsProviderBase<SubState> {
|
|
133
|
+
private readonly api: LighterApiClient
|
|
134
|
+
private readonly authProvider: LighterAuthProvider | undefined
|
|
135
|
+
private readonly client: PerpsSDKClient | undefined
|
|
136
|
+
|
|
137
|
+
private readonly orderbooks = new Map<number, OrderbookState>()
|
|
138
|
+
private lastPricesByAssetId: Record<string, string> = {}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* `address → market_id → Position`. `subscribed/...` frames reseed it and
|
|
142
|
+
* `update/...` frames upsert into it (zero size deletes), so every emission
|
|
143
|
+
* is a full open-position snapshot per the `PositionsEvent` contract.
|
|
144
|
+
*/
|
|
145
|
+
private readonly positionsByAddress = new Map<string, Map<number, Position>>()
|
|
146
|
+
|
|
147
|
+
private readonly registry: MarketRegistry | undefined
|
|
148
|
+
|
|
149
|
+
private readonly accountIndexCache = new Map<string, number>()
|
|
150
|
+
private readonly accountIndexPromises = new Map<string, Promise<number>>()
|
|
151
|
+
|
|
152
|
+
constructor(
|
|
153
|
+
wsUrl: string = DEFAULT_WS_URL,
|
|
154
|
+
providerKey = 'lighter',
|
|
155
|
+
options: LighterWsProviderOptions = {},
|
|
156
|
+
client?: PerpsSDKClient
|
|
157
|
+
) {
|
|
158
|
+
super(
|
|
159
|
+
new ReconnectingWebSocket(wsUrl, { pingPayload: '{"type":"ping"}' }),
|
|
160
|
+
providerKey
|
|
161
|
+
)
|
|
162
|
+
this.api = new LighterApiClient(options.restUrl ?? DEFAULT_REST_URL, {
|
|
163
|
+
policy: resolveRetryPolicy(
|
|
164
|
+
LIGHTER_RETRY_DEFAULTS,
|
|
165
|
+
client?.config.retry,
|
|
166
|
+
LIGHTER_PROVIDER_KEY
|
|
167
|
+
),
|
|
168
|
+
fetchImpl: client?.config.fetch,
|
|
169
|
+
})
|
|
170
|
+
this.authProvider = options.authProvider
|
|
171
|
+
this.client = client
|
|
172
|
+
this.registry = client && getMarketRegistry(client, providerKey)
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
async subscribeQuote(
|
|
176
|
+
params: ProviderGetQuoteParams,
|
|
177
|
+
onQuote: QuoteListener
|
|
178
|
+
): Promise<() => void> {
|
|
179
|
+
const client = this.client
|
|
180
|
+
if (client === undefined) {
|
|
181
|
+
throw new Error(
|
|
182
|
+
'LighterWsProvider: PerpsSDKClient not provided; cannot stream quotes. ' +
|
|
183
|
+
'Construct via `lighterWsProvider({...})` and register with PerpsWsClient.'
|
|
184
|
+
)
|
|
185
|
+
}
|
|
186
|
+
return resolveSubscribeQuote(
|
|
187
|
+
client,
|
|
188
|
+
this.providerKey,
|
|
189
|
+
this,
|
|
190
|
+
params,
|
|
191
|
+
LIGHTER_BASE_FEE_TIER,
|
|
192
|
+
onQuote
|
|
193
|
+
)
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
protected async openChannel(sub: Subscription): Promise<() => void> {
|
|
197
|
+
// Lighter has no live OHLC channel — there's nothing to subscribe to.
|
|
198
|
+
// Return a no-op unsubscribe so the caller's UX (chart still rendering
|
|
199
|
+
// from REST history + price-tick mid line) is unaffected, instead of
|
|
200
|
+
// throwing and surfacing a console error on every chart mount.
|
|
201
|
+
if (sub.channel === 'candle') {
|
|
202
|
+
return () => {}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// Only the auth channels (orders/fills/positions) resolve markets.
|
|
206
|
+
// `prices`/`orderbook` are keyed purely by `String(market_id)`, so gating
|
|
207
|
+
// them on the registry sync would let a failed `/markets` fetch kill live
|
|
208
|
+
// price ticks.
|
|
209
|
+
if (channelNeedsMarkets(sub.channel)) {
|
|
210
|
+
await this.registry?.sync()
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
const wireChannels = await this.resolveChannel(sub)
|
|
214
|
+
|
|
215
|
+
// Registry keyed by the wire channel (unique per sub), so replay-failure
|
|
216
|
+
// logs name the channel the venue knows. A logical sub may fan out to
|
|
217
|
+
// several wire channels (e.g. `prices`), each registered independently.
|
|
218
|
+
for (const { channel, needsAuth, address } of wireChannels) {
|
|
219
|
+
await this.registerSub(channel, { channel, needsAuth, address })
|
|
220
|
+
}
|
|
221
|
+
await this.rws.ready()
|
|
222
|
+
|
|
223
|
+
return () => {
|
|
224
|
+
for (const { channel } of wireChannels) {
|
|
225
|
+
this.unregisterSub(channel)
|
|
226
|
+
this.rws.send(JSON.stringify({ type: 'unsubscribe', channel }))
|
|
227
|
+
}
|
|
228
|
+
if (sub.channel === 'orderbook') {
|
|
229
|
+
const id = Number(sub.marketId)
|
|
230
|
+
if (Number.isFinite(id)) {
|
|
231
|
+
this.orderbooks.delete(id)
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
if (sub.channel === 'positions') {
|
|
235
|
+
this.positionsByAddress.delete(sub.address.toLowerCase())
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
protected override onClose(): void {
|
|
241
|
+
this.orderbooks.clear()
|
|
242
|
+
this.lastPricesByAssetId = {}
|
|
243
|
+
this.positionsByAddress.clear()
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
protected async sendSubscribe({
|
|
247
|
+
channel,
|
|
248
|
+
needsAuth,
|
|
249
|
+
address,
|
|
250
|
+
}: SubState): Promise<void> {
|
|
251
|
+
const payload: { type: 'subscribe'; channel: string; auth?: string } = {
|
|
252
|
+
type: 'subscribe',
|
|
253
|
+
channel,
|
|
254
|
+
}
|
|
255
|
+
if (needsAuth) {
|
|
256
|
+
if (!this.authProvider || !address) {
|
|
257
|
+
throw new Error(
|
|
258
|
+
`Lighter WS channel '${channel}' requires authentication but no authProvider was supplied.`
|
|
259
|
+
)
|
|
260
|
+
}
|
|
261
|
+
const token = await this.authProvider(address)
|
|
262
|
+
if (!token) {
|
|
263
|
+
throw new Error(
|
|
264
|
+
`Lighter WS channel '${channel}' requires authentication but no token was available for ${address}.`
|
|
265
|
+
)
|
|
266
|
+
}
|
|
267
|
+
payload.auth = token
|
|
268
|
+
}
|
|
269
|
+
this.rws.send(JSON.stringify(payload))
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
protected toKey(sub: Subscription): string {
|
|
273
|
+
switch (sub.channel) {
|
|
274
|
+
case 'prices':
|
|
275
|
+
return 'prices'
|
|
276
|
+
case 'orderbook':
|
|
277
|
+
return `orderbook:${sub.marketId}`
|
|
278
|
+
case 'orderUpdates':
|
|
279
|
+
return `orderUpdates:${sub.address.toLowerCase()}`
|
|
280
|
+
case 'fills':
|
|
281
|
+
return `fills:${sub.address.toLowerCase()}`
|
|
282
|
+
case 'positions':
|
|
283
|
+
return `positions:${sub.address.toLowerCase()}`
|
|
284
|
+
// No live wire sub (openChannel returns a no-op), but a stable key is
|
|
285
|
+
// still needed so the base's fan-out registry can track/release it.
|
|
286
|
+
case 'candle':
|
|
287
|
+
return `candle:${sub.marketId}:${sub.interval}`
|
|
288
|
+
case 'spotBalances':
|
|
289
|
+
throw new Error(`Lighter WS does not support channel: ${sub.channel}.`)
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
private async resolveChannel(sub: Subscription): Promise<
|
|
294
|
+
Array<{
|
|
295
|
+
channel: string
|
|
296
|
+
needsAuth: boolean
|
|
297
|
+
address?: Address
|
|
298
|
+
}>
|
|
299
|
+
> {
|
|
300
|
+
if (sub.channel === 'prices') {
|
|
301
|
+
// Lighter splits stats across two wire channels: perp markets on
|
|
302
|
+
// `market_stats/all`, spot markets on `spot_market_stats/all`. Both feed
|
|
303
|
+
// the single aggregated `prices` emit.
|
|
304
|
+
return [
|
|
305
|
+
{ channel: 'market_stats/all', needsAuth: false },
|
|
306
|
+
{ channel: 'spot_market_stats/all', needsAuth: false },
|
|
307
|
+
]
|
|
308
|
+
}
|
|
309
|
+
if (sub.channel === 'orderbook') {
|
|
310
|
+
const id = Number(sub.marketId)
|
|
311
|
+
if (!Number.isFinite(id)) {
|
|
312
|
+
throw new Error(
|
|
313
|
+
`Lighter WS: unknown market for marketId '${sub.marketId}'. ` +
|
|
314
|
+
'MarketId must be a numeric market_id string.'
|
|
315
|
+
)
|
|
316
|
+
}
|
|
317
|
+
return [{ channel: `order_book/${id}`, needsAuth: false }]
|
|
318
|
+
}
|
|
319
|
+
if (
|
|
320
|
+
sub.channel === 'orderUpdates' ||
|
|
321
|
+
sub.channel === 'fills' ||
|
|
322
|
+
sub.channel === 'positions'
|
|
323
|
+
) {
|
|
324
|
+
const accountIndex = await this.resolveAccountIndex(sub.address)
|
|
325
|
+
const lighterChannel = LIGHTER_AUTH_CHANNEL[sub.channel]
|
|
326
|
+
// `account_all_trades` is publicly readable per the Lighter WS spec —
|
|
327
|
+
// an auth token only filters events to the user's own account, which
|
|
328
|
+
// we don't currently use to scope further. Skip the token here so a
|
|
329
|
+
// user without a registered API key still gets their fill stream.
|
|
330
|
+
const needsAuth = sub.channel !== 'fills'
|
|
331
|
+
return [
|
|
332
|
+
{
|
|
333
|
+
channel: `${lighterChannel}/${accountIndex}`,
|
|
334
|
+
needsAuth,
|
|
335
|
+
address: sub.address,
|
|
336
|
+
},
|
|
337
|
+
]
|
|
338
|
+
}
|
|
339
|
+
throw new Error(
|
|
340
|
+
`Lighter WS does not support channel: ${(sub as { channel: string }).channel}.`
|
|
341
|
+
)
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
private async resolveAccountIndex(address: Address): Promise<number> {
|
|
345
|
+
const addressKey = address.toLowerCase()
|
|
346
|
+
const cached = this.accountIndexCache.get(addressKey)
|
|
347
|
+
if (cached !== undefined) {
|
|
348
|
+
return cached
|
|
349
|
+
}
|
|
350
|
+
const idx = await cachePromise(
|
|
351
|
+
() => this.accountIndexPromises.get(addressKey),
|
|
352
|
+
(p) => {
|
|
353
|
+
if (p === undefined) {
|
|
354
|
+
this.accountIndexPromises.delete(addressKey)
|
|
355
|
+
} else {
|
|
356
|
+
this.accountIndexPromises.set(addressKey, p)
|
|
357
|
+
}
|
|
358
|
+
},
|
|
359
|
+
() => this.fetchAccountIndex(address)
|
|
360
|
+
)
|
|
361
|
+
this.accountIndexCache.set(addressKey, idx)
|
|
362
|
+
return idx
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
private async fetchAccountIndex(address: Address): Promise<number> {
|
|
366
|
+
const account = await fetchDetailedAccount(this.api, address)
|
|
367
|
+
return account.index
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
protected handleMessage(raw: string): void {
|
|
371
|
+
let msg: LtWsMessage
|
|
372
|
+
try {
|
|
373
|
+
msg = JSON.parse(raw) as LtWsMessage
|
|
374
|
+
} catch {
|
|
375
|
+
wsLog.parseFailure(LIGHTER_PROVIDER_KEY, raw)
|
|
376
|
+
return
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
if (!isValidLighterFrame(msg)) {
|
|
380
|
+
wsLog.parseFailure(LIGHTER_PROVIDER_KEY, raw)
|
|
381
|
+
return
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
try {
|
|
385
|
+
this.dispatch(msg)
|
|
386
|
+
} catch (error) {
|
|
387
|
+
wsLog.handlerFailure(LIGHTER_PROVIDER_KEY, error)
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
private dispatch(msg: LtWsMessage): void {
|
|
392
|
+
if (msg.type === 'ping') {
|
|
393
|
+
this.rws.send(JSON.stringify({ type: 'pong' }))
|
|
394
|
+
return
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
if (
|
|
398
|
+
msg.type === 'subscribed/market_stats' ||
|
|
399
|
+
msg.type === 'update/market_stats'
|
|
400
|
+
) {
|
|
401
|
+
this.handleMarketStats((msg as LtWsMarketStatsAllMessage).market_stats)
|
|
402
|
+
return
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
if (
|
|
406
|
+
msg.type === 'subscribed/spot_market_stats' ||
|
|
407
|
+
msg.type === 'update/spot_market_stats'
|
|
408
|
+
) {
|
|
409
|
+
this.handleMarketStats(
|
|
410
|
+
(msg as LtWsSpotMarketStatsAllMessage).spot_market_stats
|
|
411
|
+
)
|
|
412
|
+
return
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
if (
|
|
416
|
+
msg.type === 'subscribed/order_book' ||
|
|
417
|
+
msg.type === 'update/order_book'
|
|
418
|
+
) {
|
|
419
|
+
this.handleOrderBook(
|
|
420
|
+
msg as LtWsOrderBookMessage,
|
|
421
|
+
msg.type === 'subscribed/order_book'
|
|
422
|
+
)
|
|
423
|
+
return
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
if (
|
|
427
|
+
msg.type === 'subscribed/account_all_orders' ||
|
|
428
|
+
msg.type === 'update/account_all_orders'
|
|
429
|
+
) {
|
|
430
|
+
this.handleAccountOrders(msg as LtWsAccountAllOrdersMessage)
|
|
431
|
+
return
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
if (
|
|
435
|
+
msg.type === 'subscribed/account_all_trades' ||
|
|
436
|
+
msg.type === 'update/account_all_trades'
|
|
437
|
+
) {
|
|
438
|
+
this.handleAccountTrades(msg as LtWsAccountAllTradesMessage)
|
|
439
|
+
return
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
if (
|
|
443
|
+
msg.type === 'subscribed/account_all_positions' ||
|
|
444
|
+
msg.type === 'update/account_all_positions'
|
|
445
|
+
) {
|
|
446
|
+
this.handleAccountPositions(
|
|
447
|
+
msg as LtWsAccountAllPositionsMessage,
|
|
448
|
+
msg.type === 'subscribed/account_all_positions'
|
|
449
|
+
)
|
|
450
|
+
return
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
private handleAccountOrders(msg: LtWsAccountAllOrdersMessage): void {
|
|
455
|
+
const address = this.addressFromChannel(msg.channel, 'account_all_orders')
|
|
456
|
+
if (!address) {
|
|
457
|
+
return
|
|
458
|
+
}
|
|
459
|
+
const raw = collectAuthChannelItems<LtOrder>(msg, 'orders')
|
|
460
|
+
// A registry miss warns once and schedules a cooldown-gated refetch (the
|
|
461
|
+
// id may have listed after the snapshot); the order is skipped.
|
|
462
|
+
const data = classifyAndMapOrders(raw, (marketIndex) =>
|
|
463
|
+
this.registry?.get(String(marketIndex))
|
|
464
|
+
)
|
|
465
|
+
this.emit(`orderUpdates:${address}`, {
|
|
466
|
+
channel: 'orderUpdates',
|
|
467
|
+
data,
|
|
468
|
+
})
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
private handleAccountTrades(msg: LtWsAccountAllTradesMessage): void {
|
|
472
|
+
const address = this.addressFromChannel(msg.channel, 'account_all_trades')
|
|
473
|
+
if (!address) {
|
|
474
|
+
return
|
|
475
|
+
}
|
|
476
|
+
const accountIndex = this.accountIndexCache.get(address)
|
|
477
|
+
if (accountIndex === undefined) {
|
|
478
|
+
return
|
|
479
|
+
}
|
|
480
|
+
const raw = collectAuthChannelItems<LtTrade>(msg, 'trades')
|
|
481
|
+
const fills: Fill[] = raw.flatMap((t) => {
|
|
482
|
+
const market = this.registry?.get(String(t.market_id))
|
|
483
|
+
return market ? [mapFill(t, accountIndex, market)] : []
|
|
484
|
+
})
|
|
485
|
+
this.emit(`fills:${address}`, { channel: 'fills', data: fills })
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
private handleAccountPositions(
|
|
489
|
+
msg: LtWsAccountAllPositionsMessage,
|
|
490
|
+
isSnapshot: boolean
|
|
491
|
+
): void {
|
|
492
|
+
const address = this.addressFromChannel(
|
|
493
|
+
msg.channel,
|
|
494
|
+
'account_all_positions'
|
|
495
|
+
)
|
|
496
|
+
if (!address) {
|
|
497
|
+
return
|
|
498
|
+
}
|
|
499
|
+
const raw = collectAuthChannelItems<LtAccountPosition>(msg, 'positions')
|
|
500
|
+
let state = this.positionsByAddress.get(address)
|
|
501
|
+
if (!state || isSnapshot) {
|
|
502
|
+
state = new Map()
|
|
503
|
+
this.positionsByAddress.set(address, state)
|
|
504
|
+
}
|
|
505
|
+
for (const p of raw) {
|
|
506
|
+
if (Number.parseFloat(p.position) === 0) {
|
|
507
|
+
state.delete(p.market_id)
|
|
508
|
+
} else {
|
|
509
|
+
const market = this.registry?.get(String(p.market_id))
|
|
510
|
+
if (market) {
|
|
511
|
+
state.set(p.market_id, mapPosition(p, market))
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
this.emit(`positions:${address}`, {
|
|
516
|
+
channel: 'positions',
|
|
517
|
+
data: [...state.values()],
|
|
518
|
+
})
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
/**
|
|
522
|
+
* Reverse-lookup the L1 address from an auth-channel message. The subscribe
|
|
523
|
+
* payload uses `/` (e.g. `account_all_orders/42`) but the server sends
|
|
524
|
+
* responses with `:` (e.g. `account_all_orders:42`) — both forms are
|
|
525
|
+
* accepted here so reconnect resubscriptions and live updates both route
|
|
526
|
+
* correctly.
|
|
527
|
+
*/
|
|
528
|
+
private addressFromChannel(
|
|
529
|
+
channel: string | undefined,
|
|
530
|
+
prefix: string
|
|
531
|
+
): string | null {
|
|
532
|
+
if (!channel?.startsWith(prefix)) {
|
|
533
|
+
return null
|
|
534
|
+
}
|
|
535
|
+
const sep = channel[prefix.length]
|
|
536
|
+
if (sep !== '/' && sep !== ':') {
|
|
537
|
+
return null
|
|
538
|
+
}
|
|
539
|
+
const idx = Number(channel.slice(prefix.length + 1))
|
|
540
|
+
if (!Number.isFinite(idx)) {
|
|
541
|
+
return null
|
|
542
|
+
}
|
|
543
|
+
for (const [addr, cachedIdx] of this.accountIndexCache) {
|
|
544
|
+
if (cachedIdx === idx) {
|
|
545
|
+
return addr
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
return null
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
private handleMarketStats(
|
|
552
|
+
stats?: Record<string, LtWsMarketStats | LtWsSpotMarketStats>
|
|
553
|
+
): void {
|
|
554
|
+
if (!stats) {
|
|
555
|
+
return
|
|
556
|
+
}
|
|
557
|
+
const updates: Record<string, string> = {}
|
|
558
|
+
for (const entry of Object.values(stats)) {
|
|
559
|
+
updates[String(entry.market_id)] = entry.last_trade_price
|
|
560
|
+
}
|
|
561
|
+
if (Object.keys(updates).length === 0) {
|
|
562
|
+
return
|
|
563
|
+
}
|
|
564
|
+
this.lastPricesByAssetId = { ...this.lastPricesByAssetId, ...updates }
|
|
565
|
+
this.emit('prices', {
|
|
566
|
+
channel: 'prices',
|
|
567
|
+
data: this.lastPricesByAssetId,
|
|
568
|
+
})
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
private handleOrderBook(
|
|
572
|
+
msg: LtWsOrderBookMessage,
|
|
573
|
+
isSnapshot: boolean
|
|
574
|
+
): void {
|
|
575
|
+
const marketId = this.marketIdFromChannel(msg.channel)
|
|
576
|
+
if (marketId === null) {
|
|
577
|
+
return
|
|
578
|
+
}
|
|
579
|
+
const assetId = String(marketId)
|
|
580
|
+
|
|
581
|
+
let state = this.orderbooks.get(marketId)
|
|
582
|
+
if (!state || isSnapshot) {
|
|
583
|
+
state = { bids: new Map(), asks: new Map(), assetId }
|
|
584
|
+
this.orderbooks.set(marketId, state)
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
applyLevels(state.bids, msg.order_book.bids)
|
|
588
|
+
applyLevels(state.asks, msg.order_book.asks)
|
|
589
|
+
|
|
590
|
+
this.emit(`orderbook:${assetId}`, {
|
|
591
|
+
channel: 'orderbook',
|
|
592
|
+
data: {
|
|
593
|
+
provider: this.providerKey,
|
|
594
|
+
marketId: assetId,
|
|
595
|
+
bids: mapToLevels(
|
|
596
|
+
state.bids,
|
|
597
|
+
(a, b) => Number(b.price) - Number(a.price)
|
|
598
|
+
),
|
|
599
|
+
asks: mapToLevels(
|
|
600
|
+
state.asks,
|
|
601
|
+
(a, b) => Number(a.price) - Number(b.price)
|
|
602
|
+
),
|
|
603
|
+
timestamp: Date.now(),
|
|
604
|
+
},
|
|
605
|
+
})
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
private marketIdFromChannel(channel: string | undefined): number | null {
|
|
609
|
+
if (!channel?.startsWith('order_book')) {
|
|
610
|
+
return null
|
|
611
|
+
}
|
|
612
|
+
const tail = channel.slice('order_book'.length)
|
|
613
|
+
if (tail.length < 2 || (tail[0] !== '/' && tail[0] !== ':')) {
|
|
614
|
+
return null
|
|
615
|
+
}
|
|
616
|
+
const n = Number(tail.slice(1))
|
|
617
|
+
return Number.isFinite(n) ? n : null
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
const isObject = (v: unknown): v is Record<string, unknown> =>
|
|
622
|
+
typeof v === 'object' && v !== null
|
|
623
|
+
|
|
624
|
+
/**
|
|
625
|
+
* Minimal presence/type check of the discriminating `type` and the required
|
|
626
|
+
* fields the matching handler dereferences without its own guard (currently
|
|
627
|
+
* only `order_book`, whose handler reads `order_book.bids`/`.asks`). A frame
|
|
628
|
+
* that parses but fails this is a bad frame (log + skip), distinct from a
|
|
629
|
+
* handler that throws on otherwise-shaped data. Frames for other types pass
|
|
630
|
+
* through to `dispatch`, which ignores types it does not recognise.
|
|
631
|
+
*/
|
|
632
|
+
function isValidLighterFrame(msg: LtWsMessage): boolean {
|
|
633
|
+
if (typeof msg.type !== 'string') {
|
|
634
|
+
return false
|
|
635
|
+
}
|
|
636
|
+
if (
|
|
637
|
+
msg.type === 'subscribed/order_book' ||
|
|
638
|
+
msg.type === 'update/order_book'
|
|
639
|
+
) {
|
|
640
|
+
const book = (msg as { order_book?: unknown }).order_book
|
|
641
|
+
return (
|
|
642
|
+
isObject(book) && Array.isArray(book.bids) && Array.isArray(book.asks)
|
|
643
|
+
)
|
|
644
|
+
}
|
|
645
|
+
return true
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
function applyLevels(
|
|
649
|
+
book: Map<string, string>,
|
|
650
|
+
levels: LtWsOrderBook['bids']
|
|
651
|
+
): void {
|
|
652
|
+
for (const level of levels) {
|
|
653
|
+
if (level.size === '0' || Number(level.size) === 0) {
|
|
654
|
+
book.delete(level.price)
|
|
655
|
+
} else {
|
|
656
|
+
book.set(level.price, level.size)
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
function mapToLevels(
|
|
662
|
+
book: Map<string, string>,
|
|
663
|
+
compare: (a: { price: string }, b: { price: string }) => number
|
|
664
|
+
): Array<{ price: string; size: string }> {
|
|
665
|
+
const levels = [...book].map(([price, size]) => ({ price, size }))
|
|
666
|
+
levels.sort(compare)
|
|
667
|
+
return levels
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
/**
|
|
671
|
+
* Extract items from an auth-channel payload field. The field may be:
|
|
672
|
+
* - a flat array (e.g. `trades: []` on the initial subscribed snapshot)
|
|
673
|
+
* - an object indexed by market index with array values
|
|
674
|
+
* (e.g. `orders: { "0": [Order] }` on update messages)
|
|
675
|
+
* - an object indexed by market index with single-object values
|
|
676
|
+
* (e.g. `positions: { "0": Position }` — one position per market)
|
|
677
|
+
*
|
|
678
|
+
* All three shapes are flattened to a single T[] for uniform downstream
|
|
679
|
+
* handling. Returns undefined when the field is absent, so the caller can
|
|
680
|
+
* fall back to a nested `data` wrapper (kept for compatibility with older
|
|
681
|
+
* Lighter WS versions).
|
|
682
|
+
*/
|
|
683
|
+
function extractItems<T>(value: unknown): T[] | undefined {
|
|
684
|
+
if (value === undefined || value === null) {
|
|
685
|
+
return undefined
|
|
686
|
+
}
|
|
687
|
+
if (Array.isArray(value)) {
|
|
688
|
+
return value as T[]
|
|
689
|
+
}
|
|
690
|
+
if (typeof value === 'object') {
|
|
691
|
+
return Object.values(value as Record<string, unknown>).flatMap((v) =>
|
|
692
|
+
Array.isArray(v) ? (v as T[]) : [v as T]
|
|
693
|
+
)
|
|
694
|
+
}
|
|
695
|
+
return undefined
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
function collectAuthChannelItems<T>(
|
|
699
|
+
msg: { [k: string]: unknown },
|
|
700
|
+
field: string
|
|
701
|
+
): T[] {
|
|
702
|
+
return (
|
|
703
|
+
extractItems<T>(msg[field]) ??
|
|
704
|
+
extractItems<T>(
|
|
705
|
+
(msg.data as Record<string, unknown> | undefined)?.[field]
|
|
706
|
+
) ??
|
|
707
|
+
[]
|
|
708
|
+
)
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
/**
|
|
712
|
+
* `WsProviderFactory` constructor for Lighter — pass to
|
|
713
|
+
* `new PerpsWsClient(client, { wsProviders: { lighter: lighterWsProvider({ authProvider }) } })`.
|
|
714
|
+
*
|
|
715
|
+
* Closes over the per-instance options (auth provider, restUrl override) so
|
|
716
|
+
* `PerpsWsClient` can call the returned factory with just
|
|
717
|
+
* `({ provider, wsUrl, markets })` at subscribe time. `markets` is
|
|
718
|
+
* unused — Lighter advertises a single venue, no sub-DEX filtering.
|
|
719
|
+
*
|
|
720
|
+
* @public
|
|
721
|
+
*/
|
|
722
|
+
export const lighterWsProvider =
|
|
723
|
+
(options?: LighterWsProviderOptions): WsProviderFactory =>
|
|
724
|
+
({ provider, wsUrl, client }) =>
|
|
725
|
+
new LighterWsProvider(wsUrl, provider, options, client)
|