@velocity-exchange/sdk 0.4.0 → 0.6.1
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/README.md +3 -3
- package/lib/browser/adminClient.d.ts +52 -0
- package/lib/browser/adminClient.js +97 -0
- package/lib/browser/config.js +6 -7
- package/lib/browser/constants/numericConstants.d.ts +1 -1
- package/lib/browser/constants/numericConstants.js +1 -1
- package/lib/browser/decode/user.js +5 -1
- package/lib/browser/idl/velocity.d.ts +191 -15
- package/lib/browser/idl/velocity.json +192 -16
- package/lib/browser/orderSubscriber/OrderSubscriber.d.ts +1 -0
- package/lib/browser/orderSubscriber/OrderSubscriber.js +10 -4
- package/lib/browser/pyth/pythLazerSubscriber.js +37 -27
- package/lib/browser/types.d.ts +6 -0
- package/lib/browser/user.d.ts +20 -3
- package/lib/browser/user.js +52 -4
- package/lib/browser/velocityClient.d.ts +46 -10
- package/lib/browser/velocityClient.js +107 -35
- package/lib/node/adminClient.d.ts +52 -0
- package/lib/node/adminClient.d.ts.map +1 -1
- package/lib/node/adminClient.js +97 -0
- package/lib/node/config.d.ts.map +1 -1
- package/lib/node/config.js +6 -7
- package/lib/node/constants/numericConstants.d.ts +1 -1
- package/lib/node/constants/numericConstants.d.ts.map +1 -1
- package/lib/node/constants/numericConstants.js +1 -1
- package/lib/node/decode/user.d.ts.map +1 -1
- package/lib/node/decode/user.js +5 -1
- package/lib/node/idl/velocity.d.ts +191 -15
- package/lib/node/idl/velocity.d.ts.map +1 -1
- package/lib/node/idl/velocity.json +192 -16
- package/lib/node/orderSubscriber/OrderSubscriber.d.ts +1 -0
- package/lib/node/orderSubscriber/OrderSubscriber.d.ts.map +1 -1
- package/lib/node/orderSubscriber/OrderSubscriber.js +10 -4
- package/lib/node/pyth/pythLazerSubscriber.d.ts.map +1 -1
- package/lib/node/pyth/pythLazerSubscriber.js +37 -27
- package/lib/node/types.d.ts +6 -0
- package/lib/node/types.d.ts.map +1 -1
- package/lib/node/user.d.ts +20 -3
- package/lib/node/user.d.ts.map +1 -1
- package/lib/node/user.js +52 -4
- package/lib/node/velocityClient.d.ts +46 -10
- package/lib/node/velocityClient.d.ts.map +1 -1
- package/lib/node/velocityClient.js +107 -35
- package/package.json +7 -3
- package/.prettierignore +0 -1
- package/CHANGELOG.md +0 -132
- package/build-browser.js +0 -58
- package/bunfig.toml +0 -3
- package/esbuild-shims.js +0 -12
- package/scripts/deposit-isolated-positions.ts +0 -110
- package/scripts/find-flagged-users.ts +0 -213
- package/scripts/grpc-client-test-comparison.ts +0 -372
- package/scripts/grpc-multiuser-client-test-comparison.ts +0 -158
- package/scripts/postbuild.js +0 -95
- package/scripts/single-grpc-client-test.ts +0 -284
- package/scripts/withdraw-isolated-positions.ts +0 -174
- package/src/accounts/README_WebSocketAccountSubscriberV2.md +0 -95
- package/src/accounts/README_WebSocketProgramAccountSubscriberV2.md +0 -135
- package/src/accounts/basicUserAccountSubscriber.ts +0 -79
- package/src/accounts/basicUserStatsAccountSubscriber.ts +0 -87
- package/src/accounts/bulkAccountLoader.ts +0 -358
- package/src/accounts/bulkUserStatsSubscription.ts +0 -40
- package/src/accounts/bulkUserSubscription.ts +0 -39
- package/src/accounts/customizedCadenceBulkAccountLoader.ts +0 -250
- package/src/accounts/fetch.ts +0 -200
- package/src/accounts/grpcAccountSubscriber.ts +0 -225
- package/src/accounts/grpcInsuranceFundStakeAccountSubscriber.ts +0 -73
- package/src/accounts/grpcMultiAccountSubscriber.ts +0 -566
- package/src/accounts/grpcMultiUserAccountSubscriber.ts +0 -328
- package/src/accounts/grpcProgramAccountSubscriber.ts +0 -264
- package/src/accounts/grpcUserAccountSubscriber.ts +0 -63
- package/src/accounts/grpcUserStatsAccountSubscriber.ts +0 -65
- package/src/accounts/grpcVelocityClientAccountSubscriber.ts +0 -257
- package/src/accounts/grpcVelocityClientAccountSubscriberV2.ts +0 -941
- package/src/accounts/laserProgramAccountSubscriber.ts +0 -266
- package/src/accounts/oneShotUserAccountSubscriber.ts +0 -88
- package/src/accounts/oneShotUserStatsAccountSubscriber.ts +0 -94
- package/src/accounts/pollingInsuranceFundStakeAccountSubscriber.ts +0 -223
- package/src/accounts/pollingOracleAccountSubscriber.ts +0 -151
- package/src/accounts/pollingTokenAccountSubscriber.ts +0 -147
- package/src/accounts/pollingUserAccountSubscriber.ts +0 -198
- package/src/accounts/pollingUserStatsAccountSubscriber.ts +0 -180
- package/src/accounts/pollingVelocityClientAccountSubscriber.ts +0 -774
- package/src/accounts/testBulkAccountLoader.ts +0 -59
- package/src/accounts/types.ts +0 -428
- package/src/accounts/utils.ts +0 -90
- package/src/accounts/webSocketAccountSubscriber.ts +0 -304
- package/src/accounts/webSocketAccountSubscriberV2.ts +0 -536
- package/src/accounts/webSocketInsuranceFundStakeAccountSubscriber.ts +0 -171
- package/src/accounts/webSocketProgramAccountSubscriber.ts +0 -244
- package/src/accounts/webSocketProgramAccountSubscriberV2.ts +0 -1048
- package/src/accounts/webSocketProgramAccountsSubscriberV2.ts +0 -1042
- package/src/accounts/webSocketUserAccountSubscriber.ts +0 -139
- package/src/accounts/webSocketUserStatsAccountSubsriber.ts +0 -131
- package/src/accounts/webSocketVelocityClientAccountSubscriber.ts +0 -865
- package/src/accounts/webSocketVelocityClientAccountSubscriberV2.ts +0 -897
- package/src/accounts/websocketProgramUserAccountSubscriber.ts +0 -127
- package/src/addresses/marketAddresses.ts +0 -28
- package/src/addresses/pda.ts +0 -620
- package/src/adminClient.ts +0 -8136
- package/src/assert/assert.ts +0 -5
- package/src/auctionSubscriber/auctionSubscriber.ts +0 -86
- package/src/auctionSubscriber/auctionSubscriberGrpc.ts +0 -99
- package/src/auctionSubscriber/index.ts +0 -9
- package/src/auctionSubscriber/types.ts +0 -30
- package/src/bankrun/bankrunConnection.ts +0 -630
- package/src/blockhashSubscriber/BlockhashSubscriber.ts +0 -160
- package/src/blockhashSubscriber/index.ts +0 -6
- package/src/blockhashSubscriber/types.ts +0 -13
- package/src/clock/clockSubscriber.ts +0 -146
- package/src/config.ts +0 -312
- package/src/constants/index.ts +0 -9
- package/src/constants/numericConstants.ts +0 -194
- package/src/constants/perpMarkets.ts +0 -127
- package/src/constants/spotMarkets.ts +0 -104
- package/src/constants/txConstants.ts +0 -2
- package/src/constituentMap/constituentMap.ts +0 -305
- package/src/constituentMap/pollingConstituentAccountSubscriber.ts +0 -99
- package/src/constituentMap/webSocketConstituentAccountSubscriber.ts +0 -121
- package/src/core/VelocityCore.ts +0 -606
- package/src/core/index.ts +0 -12
- package/src/core/instructions/deposit.ts +0 -64
- package/src/core/instructions/fill.ts +0 -51
- package/src/core/instructions/funding.ts +0 -33
- package/src/core/instructions/liquidation.ts +0 -55
- package/src/core/instructions/orders.ts +0 -84
- package/src/core/instructions/perpOrders.ts +0 -312
- package/src/core/instructions/settlement.ts +0 -40
- package/src/core/instructions/trigger.ts +0 -41
- package/src/core/instructions/withdraw.ts +0 -65
- package/src/core/remainingAccounts.ts +0 -320
- package/src/core/signedMsg.ts +0 -109
- package/src/decode/customCoder.ts +0 -148
- package/src/decode/user.ts +0 -401
- package/src/dlob/DLOB.ts +0 -2793
- package/src/dlob/DLOBNode.ts +0 -305
- package/src/dlob/DLOBSubscriber.ts +0 -269
- package/src/dlob/NodeList.ts +0 -244
- package/src/dlob/orderBookLevels.ts +0 -695
- package/src/dlob/types.ts +0 -37
- package/src/events/eventList.ts +0 -114
- package/src/events/eventSubscriber.ts +0 -521
- package/src/events/eventsServerLogProvider.ts +0 -189
- package/src/events/fetchLogs.ts +0 -219
- package/src/events/parse.ts +0 -271
- package/src/events/pollingLogProvider.ts +0 -113
- package/src/events/sort.ts +0 -51
- package/src/events/txEventCache.ts +0 -92
- package/src/events/types.ts +0 -337
- package/src/events/webSocketLogProvider.ts +0 -152
- package/src/factory/bigNum.ts +0 -709
- package/src/factory/oracleClient.ts +0 -73
- package/src/idl/pyth.json +0 -142
- package/src/idl/pyth.ts +0 -97
- package/src/idl/pyth_solana_receiver.json +0 -628
- package/src/idl/token_faucet.json +0 -229
- package/src/idl/token_faucet.ts +0 -197
- package/src/idl/velocity.json +0 -23974
- package/src/idl/velocity.ts +0 -23980
- package/src/index.ts +0 -150
- package/src/indicative-quotes/indicativeQuotesSender.ts +0 -233
- package/src/isomorphic/README.md +0 -19
- package/src/isomorphic/anchor.browser.ts +0 -44
- package/src/isomorphic/anchor.node.ts +0 -1
- package/src/isomorphic/anchor.ts +0 -1
- package/src/isomorphic/anchor29.browser.ts +0 -24
- package/src/isomorphic/anchor29.node.ts +0 -1
- package/src/isomorphic/anchor29.ts +0 -1
- package/src/isomorphic/grpc.browser.ts +0 -4
- package/src/isomorphic/grpc.node.ts +0 -130
- package/src/isomorphic/grpc.ts +0 -1
- package/src/jupiter/jupiterClient.ts +0 -502
- package/src/keypair.ts +0 -32
- package/src/margin/README.md +0 -138
- package/src/marginCalculation.ts +0 -407
- package/src/marinade/idl/idl.json +0 -1962
- package/src/marinade/index.ts +0 -73
- package/src/marinade/types.ts +0 -3925
- package/src/math/amm.ts +0 -1758
- package/src/math/auction.ts +0 -480
- package/src/math/bankruptcy.ts +0 -138
- package/src/math/builder.ts +0 -130
- package/src/math/conversion.ts +0 -45
- package/src/math/exchangeStatus.ts +0 -261
- package/src/math/funding.ts +0 -498
- package/src/math/insurance.ts +0 -171
- package/src/math/liquidation.ts +0 -433
- package/src/math/margin.ts +0 -327
- package/src/math/market.ts +0 -487
- package/src/math/oracles.ts +0 -477
- package/src/math/orders.ts +0 -500
- package/src/math/position.ts +0 -381
- package/src/math/repeg.ts +0 -223
- package/src/math/spotBalance.ts +0 -996
- package/src/math/spotMarket.ts +0 -115
- package/src/math/spotPosition.ts +0 -263
- package/src/math/state.ts +0 -71
- package/src/math/superStake.ts +0 -602
- package/src/math/tiers.ts +0 -73
- package/src/math/trade.ts +0 -879
- package/src/math/utils.ts +0 -183
- package/src/memcmp.ts +0 -279
- package/src/oracles/oracleClientCache.ts +0 -41
- package/src/oracles/oracleId.ts +0 -99
- package/src/oracles/prelaunchOracleClient.ts +0 -63
- package/src/oracles/pythClient.ts +0 -125
- package/src/oracles/pythLazerClient.ts +0 -141
- package/src/oracles/quoteAssetOracleClient.ts +0 -43
- package/src/oracles/strictOraclePrice.ts +0 -40
- package/src/oracles/types.ts +0 -82
- package/src/oracles/utils.ts +0 -45
- package/src/orderParams.ts +0 -117
- package/src/orderSubscriber/OrderSubscriber.ts +0 -378
- package/src/orderSubscriber/PollingSubscription.ts +0 -50
- package/src/orderSubscriber/WebsocketSubscription.ts +0 -137
- package/src/orderSubscriber/grpcSubscription.ts +0 -155
- package/src/orderSubscriber/index.ts +0 -7
- package/src/orderSubscriber/types.ts +0 -64
- package/src/priorityFee/averageOverSlotsStrategy.ts +0 -17
- package/src/priorityFee/averageStrategy.ts +0 -13
- package/src/priorityFee/ewmaStrategy.ts +0 -51
- package/src/priorityFee/heliusPriorityFeeMethod.ts +0 -68
- package/src/priorityFee/index.ts +0 -16
- package/src/priorityFee/maxOverSlotsStrategy.ts +0 -18
- package/src/priorityFee/maxStrategy.ts +0 -8
- package/src/priorityFee/priorityFeeSubscriber.ts +0 -328
- package/src/priorityFee/priorityFeeSubscriberMap.ts +0 -134
- package/src/priorityFee/solanaPriorityFeeMethod.ts +0 -46
- package/src/priorityFee/types.ts +0 -82
- package/src/priorityFee/velocityPriorityFeeMethod.ts +0 -53
- package/src/pyth/constants.ts +0 -9
- package/src/pyth/index.ts +0 -15
- package/src/pyth/pythLazerSubscriber.ts +0 -365
- package/src/pyth/types.ts +0 -4451
- package/src/pyth/utils.ts +0 -13
- package/src/slot/SlotSubscriber.ts +0 -126
- package/src/slot/SlothashSubscriber.ts +0 -163
- package/src/swap/UnifiedSwapClient.ts +0 -315
- package/src/swift/grpcSignedMsgUserAccountSubscriber.ts +0 -81
- package/src/swift/index.ts +0 -10
- package/src/swift/signedMsgUserAccountSubscriber.ts +0 -251
- package/src/swift/swiftOrderSubscriber.ts +0 -351
- package/src/testClient.ts +0 -42
- package/src/titan/titanClient.ts +0 -438
- package/src/token/index.ts +0 -13
- package/src/tokenFaucet.ts +0 -296
- package/src/tx/baseTxSender.ts +0 -651
- package/src/tx/blockhashFetcher/baseBlockhashFetcher.ts +0 -31
- package/src/tx/blockhashFetcher/cachedBlockhashFetcher.ts +0 -105
- package/src/tx/blockhashFetcher/types.ts +0 -9
- package/src/tx/fastSingleTxSender.ts +0 -200
- package/src/tx/forwardOnlyTxSender.ts +0 -193
- package/src/tx/priorityFeeCalculator.ts +0 -117
- package/src/tx/reportTransactionError.ts +0 -176
- package/src/tx/retryTxSender.ts +0 -177
- package/src/tx/txHandler.ts +0 -1009
- package/src/tx/txParamProcessor.ts +0 -225
- package/src/tx/types.ts +0 -117
- package/src/tx/utils.ts +0 -133
- package/src/tx/whileValidTxSender.ts +0 -377
- package/src/types.ts +0 -2575
- package/src/user.ts +0 -5316
- package/src/userConfig.ts +0 -38
- package/src/userMap/PollingSubscription.ts +0 -61
- package/src/userMap/WebsocketSubscription.ts +0 -93
- package/src/userMap/grpcSubscription.ts +0 -93
- package/src/userMap/referrerMap.ts +0 -360
- package/src/userMap/revenueShareEscrowMap.ts +0 -342
- package/src/userMap/userMap.ts +0 -784
- package/src/userMap/userMapConfig.ts +0 -82
- package/src/userMap/userStatsMap.ts +0 -393
- package/src/userName.ts +0 -37
- package/src/userStats.ts +0 -159
- package/src/userStatsConfig.ts +0 -35
- package/src/util/TransactionConfirmationManager.ts +0 -333
- package/src/util/chainClock.ts +0 -65
- package/src/util/computeUnits.ts +0 -101
- package/src/util/digest.ts +0 -33
- package/src/util/ed25519Utils.ts +0 -95
- package/src/util/promiseTimeout.ts +0 -23
- package/src/velocityClient.ts +0 -14802
- package/src/velocityClientConfig.ts +0 -119
- package/src/wallet.ts +0 -91
- package/tests/VelocityCore/builder_escrow.test.ts +0 -121
- package/tests/VelocityCore/decode.test.ts +0 -16
- package/tests/VelocityCore/fill_trigger.test.ts +0 -73
- package/tests/VelocityCore/instructions.test.ts +0 -46
- package/tests/VelocityCore/pdas.test.ts +0 -30
- package/tests/VelocityCore/perp_orders.test.ts +0 -205
- package/tests/VelocityCore/remainingAccounts.test.ts +0 -72
- package/tests/VelocityCore/settlement_liquidation.test.ts +0 -69
- package/tests/accounts/customizedCadenceBulkAccountLoader.test.ts +0 -211
- package/tests/amm/test.ts +0 -2402
- package/tests/auctions/test.ts +0 -81
- package/tests/bn/test.ts +0 -355
- package/tests/builder/builderFee.test.ts +0 -42
- package/tests/ci/verifyConstants.ts +0 -393
- package/tests/decode/test.ts +0 -262
- package/tests/decode/userAccountBufferStrings.ts +0 -102
- package/tests/dlob/helpers.ts +0 -748
- package/tests/dlob/test.ts +0 -6969
- package/tests/dlob/tickSizeStandardization.ts +0 -545
- package/tests/events/parseLogsForCuUsage.ts +0 -139
- package/tests/exchangeStatus/test.ts +0 -45
- package/tests/insurance/test.ts +0 -40
- package/tests/liquidation/test.ts +0 -125
- package/tests/oracles/mmOracleGate.test.ts +0 -379
- package/tests/oracles/pythPegSnap.test.ts +0 -76
- package/tests/sdkParity/enumParity.test.ts +0 -84
- package/tests/sdkParity/marginCategoryFill.test.ts +0 -143
- package/tests/sdkParity/memcmpOffsets.test.ts +0 -139
- package/tests/spot/test.ts +0 -281
- package/tests/tx/TransactionConfirmationManager.test.ts +0 -312
- package/tests/tx/cachedBlockhashFetcher.test.ts +0 -96
- package/tests/tx/priorityFeeCalculator.ts +0 -77
- package/tests/tx/priorityFeeStrategy.ts +0 -95
- package/tests/user/bankruptcy.ts +0 -165
- package/tests/user/feeAndWithdrawLimits.ts +0 -284
- package/tests/user/getMarginCalculation.ts +0 -586
- package/tests/user/helpers.ts +0 -184
- package/tests/user/liquidations.ts +0 -129
- package/tests/user/marginCalculations.test.ts +0 -321
- package/tests/user/test.ts +0 -833
- package/tsconfig.browser.json +0 -18
- package/tsconfig.json +0 -36
|
@@ -1,941 +0,0 @@
|
|
|
1
|
-
import StrictEventEmitter from 'strict-event-emitter-types';
|
|
2
|
-
import { EventEmitter } from 'events';
|
|
3
|
-
import { OracleInfo, OraclePriceData } from '../oracles/types';
|
|
4
|
-
import { AccountInfo, PublicKey } from '@solana/web3.js';
|
|
5
|
-
import { findAllMarketAndOracles, VelocityProgram } from '../config';
|
|
6
|
-
import {
|
|
7
|
-
getVelocityStateAccountPublicKey,
|
|
8
|
-
getPerpMarketPublicKey,
|
|
9
|
-
getPerpMarketPublicKeySync,
|
|
10
|
-
getSpotMarketPublicKey,
|
|
11
|
-
getSpotMarketPublicKeySync,
|
|
12
|
-
} from '../addresses/pda';
|
|
13
|
-
import {
|
|
14
|
-
AccountSubscriber,
|
|
15
|
-
DataAndSlot,
|
|
16
|
-
DelistedMarketSetting,
|
|
17
|
-
VelocityClientAccountEvents,
|
|
18
|
-
VelocityClientAccountSubscriber,
|
|
19
|
-
NotSubscribedError,
|
|
20
|
-
GrpcConfigs,
|
|
21
|
-
ResubOpts,
|
|
22
|
-
} from './types';
|
|
23
|
-
import { grpcAccountSubscriber } from './grpcAccountSubscriber';
|
|
24
|
-
import { grpcMultiAccountSubscriber } from './grpcMultiAccountSubscriber';
|
|
25
|
-
import { PerpMarketAccount, SpotMarketAccount, StateAccount } from '../types';
|
|
26
|
-
import {
|
|
27
|
-
getOracleId,
|
|
28
|
-
getPublicKeyAndSourceFromOracleId,
|
|
29
|
-
} from '../oracles/oracleId';
|
|
30
|
-
import { OracleClientCache } from '../oracles/oracleClientCache';
|
|
31
|
-
import { findDelistedPerpMarketsAndOracles } from './utils';
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* `VelocityClientAccountSubscriber` variant that, unlike `grpcVelocityClientAccountSubscriber`
|
|
35
|
-
* (one gRPC stream per account), multiplexes all perp markets onto one `grpcMultiAccountSubscriber`,
|
|
36
|
-
* all spot markets onto a second, and all oracles onto a third — three gRPC streams total instead
|
|
37
|
-
* of one per account. Lower connection/stream overhead at scale; `getOraclePriceDataAndSlot`
|
|
38
|
-
* relies on this class's own `oracleIdToOracleDataMap` rather than the multi-subscriber's account
|
|
39
|
-
* map, since a single oracle pubkey can back multiple `(pubkey, source)` oracle ids (e.g. a market
|
|
40
|
-
* pair sharing an underlying price feed).
|
|
41
|
-
*/
|
|
42
|
-
export class grpcVelocityClientAccountSubscriberV2
|
|
43
|
-
implements VelocityClientAccountSubscriber
|
|
44
|
-
{
|
|
45
|
-
private grpcConfigs: GrpcConfigs;
|
|
46
|
-
private perpMarketsSubscriber?: grpcMultiAccountSubscriber<PerpMarketAccount>;
|
|
47
|
-
private spotMarketsSubscriber?: grpcMultiAccountSubscriber<SpotMarketAccount>;
|
|
48
|
-
private oracleMultiSubscriber?: grpcMultiAccountSubscriber<
|
|
49
|
-
OraclePriceData,
|
|
50
|
-
OracleInfo
|
|
51
|
-
>;
|
|
52
|
-
private perpMarketIndexToAccountPubkeyMap = new Map<number, string>();
|
|
53
|
-
private spotMarketIndexToAccountPubkeyMap = new Map<number, string>();
|
|
54
|
-
private delistedMarketSetting: DelistedMarketSetting;
|
|
55
|
-
|
|
56
|
-
public eventEmitter: StrictEventEmitter<
|
|
57
|
-
EventEmitter,
|
|
58
|
-
VelocityClientAccountEvents
|
|
59
|
-
>;
|
|
60
|
-
public isSubscribed: boolean;
|
|
61
|
-
public isSubscribing: boolean;
|
|
62
|
-
public program: VelocityProgram;
|
|
63
|
-
public perpMarketIndexes: number[];
|
|
64
|
-
public spotMarketIndexes: number[];
|
|
65
|
-
public shouldFindAllMarketsAndOracles: boolean;
|
|
66
|
-
public oracleInfos: OracleInfo[];
|
|
67
|
-
public initialPerpMarketAccountData: Map<number, PerpMarketAccount>;
|
|
68
|
-
public initialSpotMarketAccountData: Map<number, SpotMarketAccount>;
|
|
69
|
-
public initialOraclePriceData: Map<string, OraclePriceData>;
|
|
70
|
-
public perpOracleMap = new Map<number, PublicKey>();
|
|
71
|
-
public perpOracleStringMap = new Map<number, string>();
|
|
72
|
-
public spotOracleMap = new Map<number, PublicKey>();
|
|
73
|
-
public spotOracleStringMap = new Map<number, string>();
|
|
74
|
-
private oracleIdToOracleDataMap = new Map<
|
|
75
|
-
string,
|
|
76
|
-
DataAndSlot<OraclePriceData>
|
|
77
|
-
>();
|
|
78
|
-
public stateAccountSubscriber?: AccountSubscriber<StateAccount>;
|
|
79
|
-
oracleClientCache = new OracleClientCache();
|
|
80
|
-
private resubOpts?: ResubOpts;
|
|
81
|
-
|
|
82
|
-
private subscriptionPromiseResolver: (val: boolean) => void = () => {};
|
|
83
|
-
private subscriptionPromise: Promise<boolean> = Promise.resolve(false);
|
|
84
|
-
|
|
85
|
-
/**
|
|
86
|
-
* @param grpcConfigs gRPC Geyser endpoint/token/commitment config (Yellowstone or LaserStream).
|
|
87
|
-
* @param program Anchor program used to derive PDAs, decode accounts, and resolve oracle clients.
|
|
88
|
-
* @param perpMarketIndexes Perp market indexes to track, if `shouldFindAllMarketsAndOracles` is false.
|
|
89
|
-
* @param spotMarketIndexes Spot market indexes to track, if `shouldFindAllMarketsAndOracles` is false.
|
|
90
|
-
* @param oracleInfos Oracles to track up front, if `shouldFindAllMarketsAndOracles` is false.
|
|
91
|
-
* @param shouldFindAllMarketsAndOracles If true, `subscribe()` first discovers every market/oracle from on-chain state.
|
|
92
|
-
* @param delistedMarketSetting Behavior applied to delisted perp markets/oracles after subscribing.
|
|
93
|
-
* @param resubOpts Resubscription watchdog options passed to the underlying multi-account subscribers.
|
|
94
|
-
*/
|
|
95
|
-
constructor(
|
|
96
|
-
grpcConfigs: GrpcConfigs,
|
|
97
|
-
program: VelocityProgram,
|
|
98
|
-
perpMarketIndexes: number[],
|
|
99
|
-
spotMarketIndexes: number[],
|
|
100
|
-
oracleInfos: OracleInfo[],
|
|
101
|
-
shouldFindAllMarketsAndOracles: boolean,
|
|
102
|
-
delistedMarketSetting: DelistedMarketSetting,
|
|
103
|
-
resubOpts?: ResubOpts
|
|
104
|
-
) {
|
|
105
|
-
this.eventEmitter = new EventEmitter();
|
|
106
|
-
this.isSubscribed = false;
|
|
107
|
-
this.isSubscribing = false;
|
|
108
|
-
this.program = program;
|
|
109
|
-
this.perpMarketIndexes = perpMarketIndexes;
|
|
110
|
-
this.spotMarketIndexes = spotMarketIndexes;
|
|
111
|
-
this.shouldFindAllMarketsAndOracles = shouldFindAllMarketsAndOracles;
|
|
112
|
-
this.oracleInfos = oracleInfos;
|
|
113
|
-
this.initialPerpMarketAccountData = new Map();
|
|
114
|
-
this.initialSpotMarketAccountData = new Map();
|
|
115
|
-
this.initialOraclePriceData = new Map();
|
|
116
|
-
this.perpOracleMap = new Map();
|
|
117
|
-
this.perpOracleStringMap = new Map();
|
|
118
|
-
this.spotOracleMap = new Map();
|
|
119
|
-
this.spotOracleStringMap = new Map();
|
|
120
|
-
this.grpcConfigs = grpcConfigs;
|
|
121
|
-
this.resubOpts = resubOpts;
|
|
122
|
-
this.delistedMarketSetting = delistedMarketSetting;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
chunks = <T>(array: readonly T[], size: number): T[][] => {
|
|
126
|
-
return new Array(Math.ceil(array.length / size))
|
|
127
|
-
.fill(null)
|
|
128
|
-
.map((_, index) => index * size)
|
|
129
|
-
.map((begin) => array.slice(begin, begin + size));
|
|
130
|
-
};
|
|
131
|
-
|
|
132
|
-
/**
|
|
133
|
-
* Batch-fetches every tracked perp market, spot market, and oracle account via chunked
|
|
134
|
-
* `getMultipleAccountsInfo` calls (75 pubkeys per chunk) and stashes the decoded results in
|
|
135
|
-
* `initialPerpMarketAccountData`/`initialSpotMarketAccountData`/`initialOraclePriceData`, which
|
|
136
|
-
* the multi-account subscribers seed from before their gRPC streams deliver live data. Skips
|
|
137
|
-
* data already populated (e.g. by `findAllMarketAndOracles` when `shouldFindAllMarketsAndOracles`
|
|
138
|
-
* is set).
|
|
139
|
-
*/
|
|
140
|
-
async setInitialData(): Promise<void> {
|
|
141
|
-
const connection = this.program.provider.connection;
|
|
142
|
-
|
|
143
|
-
if (
|
|
144
|
-
!this.initialPerpMarketAccountData ||
|
|
145
|
-
this.initialPerpMarketAccountData.size === 0
|
|
146
|
-
) {
|
|
147
|
-
const perpMarketPublicKeys = this.perpMarketIndexes.map((marketIndex) =>
|
|
148
|
-
getPerpMarketPublicKeySync(this.program.programId, marketIndex)
|
|
149
|
-
);
|
|
150
|
-
const perpMarketPublicKeysChunks = this.chunks(perpMarketPublicKeys, 75);
|
|
151
|
-
const perpMarketAccountInfos = (
|
|
152
|
-
await Promise.all(
|
|
153
|
-
perpMarketPublicKeysChunks.map((perpMarketPublicKeysChunk) =>
|
|
154
|
-
connection.getMultipleAccountsInfo(perpMarketPublicKeysChunk)
|
|
155
|
-
)
|
|
156
|
-
)
|
|
157
|
-
).flat();
|
|
158
|
-
this.initialPerpMarketAccountData = new Map(
|
|
159
|
-
perpMarketAccountInfos
|
|
160
|
-
.filter(
|
|
161
|
-
(accountInfo): accountInfo is AccountInfo<Buffer> => !!accountInfo
|
|
162
|
-
)
|
|
163
|
-
.map((accountInfo) => {
|
|
164
|
-
const perpMarket = this.program.coder.accounts.decode(
|
|
165
|
-
'perpMarket',
|
|
166
|
-
accountInfo.data
|
|
167
|
-
);
|
|
168
|
-
return [perpMarket.marketIndex, perpMarket];
|
|
169
|
-
})
|
|
170
|
-
);
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
if (
|
|
174
|
-
!this.initialSpotMarketAccountData ||
|
|
175
|
-
this.initialSpotMarketAccountData.size === 0
|
|
176
|
-
) {
|
|
177
|
-
const spotMarketPublicKeys = this.spotMarketIndexes.map((marketIndex) =>
|
|
178
|
-
getSpotMarketPublicKeySync(this.program.programId, marketIndex)
|
|
179
|
-
);
|
|
180
|
-
const spotMarketPublicKeysChunks = this.chunks(spotMarketPublicKeys, 75);
|
|
181
|
-
const spotMarketAccountInfos = (
|
|
182
|
-
await Promise.all(
|
|
183
|
-
spotMarketPublicKeysChunks.map((spotMarketPublicKeysChunk) =>
|
|
184
|
-
connection.getMultipleAccountsInfo(spotMarketPublicKeysChunk)
|
|
185
|
-
)
|
|
186
|
-
)
|
|
187
|
-
).flat();
|
|
188
|
-
this.initialSpotMarketAccountData = new Map(
|
|
189
|
-
spotMarketAccountInfos
|
|
190
|
-
.filter(
|
|
191
|
-
(accountInfo): accountInfo is AccountInfo<Buffer> => !!accountInfo
|
|
192
|
-
)
|
|
193
|
-
.map((accountInfo) => {
|
|
194
|
-
const spotMarket = this.program.coder.accounts.decode(
|
|
195
|
-
'spotMarket',
|
|
196
|
-
accountInfo.data
|
|
197
|
-
);
|
|
198
|
-
return [spotMarket.marketIndex, spotMarket];
|
|
199
|
-
})
|
|
200
|
-
);
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
const oracleAccountPubkeyChunks = this.chunks(
|
|
204
|
-
this.oracleInfos.map((oracleInfo) => oracleInfo.publicKey),
|
|
205
|
-
75
|
|
206
|
-
);
|
|
207
|
-
const oracleAccountInfos = (
|
|
208
|
-
await Promise.all(
|
|
209
|
-
oracleAccountPubkeyChunks.map((oracleAccountPublicKeysChunk) =>
|
|
210
|
-
connection.getMultipleAccountsInfo(oracleAccountPublicKeysChunk)
|
|
211
|
-
)
|
|
212
|
-
)
|
|
213
|
-
).flat();
|
|
214
|
-
this.initialOraclePriceData = new Map(
|
|
215
|
-
this.oracleInfos.reduce<[string, OraclePriceData][]>(
|
|
216
|
-
(result, oracleInfo, i) => {
|
|
217
|
-
const oracleAccountInfo = oracleAccountInfos[i];
|
|
218
|
-
if (!oracleAccountInfo) {
|
|
219
|
-
return result;
|
|
220
|
-
}
|
|
221
|
-
const oracleClient = this.oracleClientCache.get(
|
|
222
|
-
oracleInfo.source,
|
|
223
|
-
connection,
|
|
224
|
-
this.program
|
|
225
|
-
);
|
|
226
|
-
if (!oracleClient) {
|
|
227
|
-
return result;
|
|
228
|
-
}
|
|
229
|
-
const oraclePriceData = oracleClient.getOraclePriceDataFromBuffer(
|
|
230
|
-
oracleAccountInfo.data
|
|
231
|
-
);
|
|
232
|
-
result.push([
|
|
233
|
-
getOracleId(oracleInfo.publicKey, oracleInfo.source),
|
|
234
|
-
oraclePriceData,
|
|
235
|
-
]);
|
|
236
|
-
return result;
|
|
237
|
-
},
|
|
238
|
-
[]
|
|
239
|
-
)
|
|
240
|
-
);
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
/**
|
|
244
|
-
* Records `_marketIndex` in `perpMarketIndexes` for bookkeeping. Note this does **not**
|
|
245
|
-
* actually add the account to the live `perpMarketsSubscriber` gRPC stream — unlike the
|
|
246
|
-
* WebSocket/per-account subscribers' `addPerpMarket`, no new subscription is created here.
|
|
247
|
-
* @param _marketIndex Perp market index to record.
|
|
248
|
-
*/
|
|
249
|
-
async addPerpMarket(_marketIndex: number): Promise<boolean> {
|
|
250
|
-
if (!this.perpMarketIndexes.includes(_marketIndex)) {
|
|
251
|
-
this.perpMarketIndexes = this.perpMarketIndexes.concat(_marketIndex);
|
|
252
|
-
}
|
|
253
|
-
return true;
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
/** No-op; unlike `addPerpMarket`, does not even record the index. Always resolves `true`. */
|
|
257
|
-
async addSpotMarket(_marketIndex: number): Promise<boolean> {
|
|
258
|
-
return true;
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
/**
|
|
262
|
-
* Adds an oracle to `oracleInfos` and, if the oracle multi-subscriber's gRPC stream is already
|
|
263
|
-
* active, calls `oracleMultiSubscriber.addAccounts` to extend it. A no-op that resolves `true`
|
|
264
|
-
* immediately for the `PublicKey.default` sentinel (quote-asset "no oracle") or an oracle
|
|
265
|
-
* already tracked.
|
|
266
|
-
* @param oracleInfo Oracle pubkey and source to start tracking.
|
|
267
|
-
*/
|
|
268
|
-
async addOracle(oracleInfo: OracleInfo): Promise<boolean> {
|
|
269
|
-
if (this.resubOpts?.logResubMessages) {
|
|
270
|
-
console.log('[grpcVelocityClientAccountSubscriberV2] addOracle');
|
|
271
|
-
}
|
|
272
|
-
if (oracleInfo.publicKey.equals(PublicKey.default)) {
|
|
273
|
-
return true;
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
const exists = this.oracleInfos.some(
|
|
277
|
-
(o) =>
|
|
278
|
-
o.source === oracleInfo.source &&
|
|
279
|
-
o.publicKey.equals(oracleInfo.publicKey)
|
|
280
|
-
);
|
|
281
|
-
if (exists) {
|
|
282
|
-
return true; // Already exists, don't add duplicate
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
this.oracleInfos = this.oracleInfos.concat(oracleInfo);
|
|
286
|
-
|
|
287
|
-
// extend the multi-subscriber's accountPropsMap alongside the pubkey filter, so the
|
|
288
|
-
// oracle decode path has the OracleInfo(s) it needs for the newly added feed (all infos
|
|
289
|
-
// sharing this pubkey, mirroring the fan-out map built in subscribeToOracles)
|
|
290
|
-
const pubkey = oracleInfo.publicKey.toBase58();
|
|
291
|
-
const infosForPubkey = this.oracleInfos.filter(
|
|
292
|
-
(o) => o.publicKey.toBase58() === pubkey
|
|
293
|
-
);
|
|
294
|
-
const accountProps = new Map<string, OracleInfo | OracleInfo[]>([
|
|
295
|
-
[pubkey, infosForPubkey],
|
|
296
|
-
]);
|
|
297
|
-
this.oracleMultiSubscriber?.addAccounts(
|
|
298
|
-
[oracleInfo.publicKey],
|
|
299
|
-
accountProps
|
|
300
|
-
);
|
|
301
|
-
|
|
302
|
-
return true;
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
/**
|
|
306
|
-
* Subscribes the `State` account (single-account gRPC subscriber), batch-seeds all market/
|
|
307
|
-
* oracle accounts via `setInitialData()`, then opens the three multiplexed
|
|
308
|
-
* `grpcMultiAccountSubscriber` streams (perp markets, spot markets, oracles). Applies
|
|
309
|
-
* `delistedMarketSetting` afterward. Idempotent: a no-op if already subscribed, and concurrent
|
|
310
|
-
* calls while a subscribe is in flight share the same result via `subscriptionPromise`.
|
|
311
|
-
*/
|
|
312
|
-
public async subscribe(): Promise<boolean> {
|
|
313
|
-
if (this.isSubscribed) {
|
|
314
|
-
return true;
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
if (this.isSubscribing) {
|
|
318
|
-
return await this.subscriptionPromise;
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
this.isSubscribing = true;
|
|
322
|
-
|
|
323
|
-
this.subscriptionPromise = new Promise((res) => {
|
|
324
|
-
this.subscriptionPromiseResolver = res;
|
|
325
|
-
});
|
|
326
|
-
|
|
327
|
-
try {
|
|
328
|
-
return await this.subscribeInner();
|
|
329
|
-
} catch (err) {
|
|
330
|
-
// tear down any partially-created subscribers so a retry starts clean and doesn't
|
|
331
|
-
// stack duplicate gRPC streams / leak callbacks — unsubscribe() bails while
|
|
332
|
-
// isSubscribed is still false, so it can't recover these on its own
|
|
333
|
-
try {
|
|
334
|
-
await this.stateAccountSubscriber?.unsubscribe();
|
|
335
|
-
await this.oracleMultiSubscriber?.unsubscribe();
|
|
336
|
-
await this.perpMarketsSubscriber?.unsubscribe();
|
|
337
|
-
await this.spotMarketsSubscriber?.unsubscribe();
|
|
338
|
-
} catch (teardownErr) {
|
|
339
|
-
console.error(
|
|
340
|
-
'[grpcVelocityClientAccountSubscriberV2] cleanup after failed subscribe threw',
|
|
341
|
-
teardownErr
|
|
342
|
-
);
|
|
343
|
-
}
|
|
344
|
-
this.stateAccountSubscriber = undefined;
|
|
345
|
-
this.oracleMultiSubscriber = undefined;
|
|
346
|
-
this.perpMarketsSubscriber = undefined;
|
|
347
|
-
this.spotMarketsSubscriber = undefined;
|
|
348
|
-
|
|
349
|
-
// settle the shared promise so concurrent subscribe() callers don't hang forever
|
|
350
|
-
this.isSubscribing = false;
|
|
351
|
-
this.subscriptionPromiseResolver(false);
|
|
352
|
-
throw err;
|
|
353
|
-
}
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
private async subscribeInner(): Promise<boolean> {
|
|
357
|
-
if (this.shouldFindAllMarketsAndOracles) {
|
|
358
|
-
const {
|
|
359
|
-
perpMarketIndexes,
|
|
360
|
-
perpMarketAccounts,
|
|
361
|
-
spotMarketIndexes,
|
|
362
|
-
spotMarketAccounts,
|
|
363
|
-
oracleInfos,
|
|
364
|
-
} = await findAllMarketAndOracles(this.program);
|
|
365
|
-
this.perpMarketIndexes = perpMarketIndexes;
|
|
366
|
-
this.spotMarketIndexes = spotMarketIndexes;
|
|
367
|
-
this.oracleInfos = oracleInfos;
|
|
368
|
-
// front run and set the initial data here to save extra gma call in set initial data
|
|
369
|
-
this.initialPerpMarketAccountData = new Map(
|
|
370
|
-
perpMarketAccounts.map((market) => [market.marketIndex, market])
|
|
371
|
-
);
|
|
372
|
-
this.initialSpotMarketAccountData = new Map(
|
|
373
|
-
spotMarketAccounts.map((market) => [market.marketIndex, market])
|
|
374
|
-
);
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
const statePublicKey = await getVelocityStateAccountPublicKey(
|
|
378
|
-
this.program.programId
|
|
379
|
-
);
|
|
380
|
-
|
|
381
|
-
// create and activate main state account subscription
|
|
382
|
-
this.stateAccountSubscriber =
|
|
383
|
-
await grpcAccountSubscriber.create<StateAccount>(
|
|
384
|
-
this.grpcConfigs,
|
|
385
|
-
'state',
|
|
386
|
-
this.program,
|
|
387
|
-
statePublicKey,
|
|
388
|
-
undefined,
|
|
389
|
-
undefined
|
|
390
|
-
);
|
|
391
|
-
await this.stateAccountSubscriber.subscribe((data: StateAccount) => {
|
|
392
|
-
this.eventEmitter.emit('stateAccountUpdate', data);
|
|
393
|
-
this.eventEmitter.emit('update');
|
|
394
|
-
});
|
|
395
|
-
|
|
396
|
-
// set initial data to avoid spamming getAccountInfo calls in webSocketAccountSubscriber
|
|
397
|
-
await this.setInitialData();
|
|
398
|
-
|
|
399
|
-
// subscribe to perp + spot markets (separate) and oracles
|
|
400
|
-
await Promise.all([
|
|
401
|
-
this.subscribeToPerpMarketAccounts(),
|
|
402
|
-
this.subscribeToSpotMarketAccounts(),
|
|
403
|
-
this.subscribeToOracles(),
|
|
404
|
-
]);
|
|
405
|
-
|
|
406
|
-
this.eventEmitter.emit('update');
|
|
407
|
-
|
|
408
|
-
await this.handleDelistedMarkets();
|
|
409
|
-
|
|
410
|
-
await Promise.all([this.setPerpOracleMap(), this.setSpotOracleMap()]);
|
|
411
|
-
|
|
412
|
-
this.subscriptionPromiseResolver(true);
|
|
413
|
-
|
|
414
|
-
this.isSubscribing = false;
|
|
415
|
-
this.isSubscribed = true;
|
|
416
|
-
|
|
417
|
-
// delete initial data
|
|
418
|
-
this.removeInitialData();
|
|
419
|
-
|
|
420
|
-
return true;
|
|
421
|
-
}
|
|
422
|
-
|
|
423
|
-
/** Fetches the `State` account and all three multiplexed subscribers (perp markets, spot markets, oracles) in sequence. */
|
|
424
|
-
public async fetch(): Promise<void> {
|
|
425
|
-
await this.stateAccountSubscriber?.fetch();
|
|
426
|
-
await this.perpMarketsSubscriber?.fetch();
|
|
427
|
-
await this.spotMarketsSubscriber?.fetch();
|
|
428
|
-
await this.oracleMultiSubscriber?.fetch();
|
|
429
|
-
}
|
|
430
|
-
|
|
431
|
-
/** Throws `NotSubscribedError` if `subscribe()` has not been called. */
|
|
432
|
-
private assertIsSubscribed(): void {
|
|
433
|
-
if (!this.isSubscribed) {
|
|
434
|
-
throw new NotSubscribedError(
|
|
435
|
-
'You must call `subscribe` before using this function'
|
|
436
|
-
);
|
|
437
|
-
}
|
|
438
|
-
}
|
|
439
|
-
|
|
440
|
-
/** Throws `NotSubscribedError` if not subscribed. */
|
|
441
|
-
public getStateAccountAndSlot(): DataAndSlot<StateAccount> {
|
|
442
|
-
this.assertIsSubscribed();
|
|
443
|
-
return this.stateAccountSubscriber!.dataAndSlot!;
|
|
444
|
-
}
|
|
445
|
-
|
|
446
|
-
/** Returns every currently cached (loaded) perp market from the multiplexed subscriber. Does not throw `NotSubscribedError`. */
|
|
447
|
-
public getMarketAccountsAndSlots(): DataAndSlot<PerpMarketAccount>[] {
|
|
448
|
-
const map = this.perpMarketsSubscriber?.getAccountDataMap();
|
|
449
|
-
return Array.from(map?.values() ?? []);
|
|
450
|
-
}
|
|
451
|
-
|
|
452
|
-
/** Returns every currently cached (loaded) spot market from the multiplexed subscriber. Does not throw `NotSubscribedError`. */
|
|
453
|
-
public getSpotMarketAccountsAndSlots(): DataAndSlot<SpotMarketAccount>[] {
|
|
454
|
-
const map = this.spotMarketsSubscriber?.getAccountDataMap();
|
|
455
|
-
return Array.from(map?.values() ?? []);
|
|
456
|
-
}
|
|
457
|
-
|
|
458
|
-
/** Returns the cached perp market, or undefined if `marketIndex` isn't tracked (or hasn't loaded yet). Does not throw `NotSubscribedError`. */
|
|
459
|
-
getMarketAccountAndSlot(
|
|
460
|
-
marketIndex: number
|
|
461
|
-
): DataAndSlot<PerpMarketAccount> | undefined {
|
|
462
|
-
const accountPubkey =
|
|
463
|
-
this.perpMarketIndexToAccountPubkeyMap.get(marketIndex);
|
|
464
|
-
if (!accountPubkey) {
|
|
465
|
-
return undefined;
|
|
466
|
-
}
|
|
467
|
-
return this.perpMarketsSubscriber?.getAccountData(accountPubkey);
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
/** Returns the cached spot market, or undefined if `marketIndex` isn't tracked (or hasn't loaded yet). Does not throw `NotSubscribedError`. */
|
|
471
|
-
getSpotMarketAccountAndSlot(
|
|
472
|
-
marketIndex: number
|
|
473
|
-
): DataAndSlot<SpotMarketAccount> | undefined {
|
|
474
|
-
const accountPubkey =
|
|
475
|
-
this.spotMarketIndexToAccountPubkeyMap.get(marketIndex);
|
|
476
|
-
if (!accountPubkey) {
|
|
477
|
-
return undefined;
|
|
478
|
-
}
|
|
479
|
-
return this.spotMarketsSubscriber?.getAccountData(accountPubkey);
|
|
480
|
-
}
|
|
481
|
-
|
|
482
|
-
/**
|
|
483
|
-
* Looks up cached oracle price data by oracle id (see `getOracleId`), from this class's own
|
|
484
|
-
* `oracleIdToOracleDataMap` rather than `oracleMultiSubscriber.getAccountData` — a single
|
|
485
|
-
* oracle pubkey backing multiple oracle ids (e.g. shared price feeds) means the multi-subscriber's
|
|
486
|
-
* own account map cannot be trusted to disambiguate them correctly.
|
|
487
|
-
* @param oracleId Oracle id string from `getOracleId(publicKey, source)`.
|
|
488
|
-
* @returns Cached price data/slot, or undefined if not tracked. Throws `NotSubscribedError` if not subscribed.
|
|
489
|
-
*/
|
|
490
|
-
public getOraclePriceDataAndSlot(
|
|
491
|
-
oracleId: string
|
|
492
|
-
): DataAndSlot<OraclePriceData> | undefined {
|
|
493
|
-
this.assertIsSubscribed();
|
|
494
|
-
// we need to rely on a map we store in this class because the grpcMultiAccountSubscriber does not track a mapping or oracle ID.
|
|
495
|
-
// DO NOT call getAccountData on the oracleMultiSubscriber, it will not return the correct data in certain cases(BONK spot and perp market subscribed too at once).
|
|
496
|
-
return this.oracleIdToOracleDataMap.get(oracleId);
|
|
497
|
-
}
|
|
498
|
-
|
|
499
|
-
/**
|
|
500
|
-
* Convenience lookup: resolves the oracle price data currently mapped to a perp market's
|
|
501
|
-
* oracle. If the cached market's oracle pubkey has drifted from (or isn't yet in)
|
|
502
|
-
* `perpOracleMap`, triggers a background `setPerpOracleMap()` refresh and still returns the
|
|
503
|
-
* (possibly stale) mapping for this call.
|
|
504
|
-
* @param marketIndex Perp market index whose oracle price to look up.
|
|
505
|
-
*/
|
|
506
|
-
public getOraclePriceDataAndSlotForPerpMarket(
|
|
507
|
-
marketIndex: number
|
|
508
|
-
): DataAndSlot<OraclePriceData> | undefined {
|
|
509
|
-
const perpMarketAccount = this.getMarketAccountAndSlot(marketIndex);
|
|
510
|
-
const oracle = this.perpOracleMap.get(marketIndex);
|
|
511
|
-
const oracleId = this.perpOracleStringMap.get(marketIndex);
|
|
512
|
-
if (!perpMarketAccount || !oracleId) {
|
|
513
|
-
return undefined;
|
|
514
|
-
}
|
|
515
|
-
|
|
516
|
-
if (!oracle || !perpMarketAccount.data.oracle.equals(oracle)) {
|
|
517
|
-
// If the oracle has changed (or not yet cached), update the oracle map in background
|
|
518
|
-
this.setPerpOracleMap();
|
|
519
|
-
}
|
|
520
|
-
|
|
521
|
-
return this.getOraclePriceDataAndSlot(oracleId);
|
|
522
|
-
}
|
|
523
|
-
|
|
524
|
-
/**
|
|
525
|
-
* Convenience lookup: resolves the oracle price data currently mapped to a spot market's
|
|
526
|
-
* oracle. If the cached market's oracle pubkey has drifted from (or isn't yet in)
|
|
527
|
-
* `spotOracleMap`, triggers a background `setSpotOracleMap()` refresh and still returns the
|
|
528
|
-
* (possibly stale) mapping for this call.
|
|
529
|
-
* @param marketIndex Spot market index whose oracle price to look up.
|
|
530
|
-
*/
|
|
531
|
-
public getOraclePriceDataAndSlotForSpotMarket(
|
|
532
|
-
marketIndex: number
|
|
533
|
-
): DataAndSlot<OraclePriceData> | undefined {
|
|
534
|
-
const spotMarketAccount = this.getSpotMarketAccountAndSlot(marketIndex);
|
|
535
|
-
const oracle = this.spotOracleMap.get(marketIndex);
|
|
536
|
-
const oracleId = this.spotOracleStringMap.get(marketIndex);
|
|
537
|
-
if (!spotMarketAccount || !oracleId) {
|
|
538
|
-
return undefined;
|
|
539
|
-
}
|
|
540
|
-
|
|
541
|
-
if (!oracle || !spotMarketAccount.data.oracle.equals(oracle)) {
|
|
542
|
-
// If the oracle has changed (or not yet cached), update the oracle map in background
|
|
543
|
-
this.setSpotOracleMap();
|
|
544
|
-
}
|
|
545
|
-
|
|
546
|
-
return this.getOraclePriceDataAndSlot(oracleId);
|
|
547
|
-
}
|
|
548
|
-
|
|
549
|
-
/** Rebuilds `perpOracleMap`/`perpOracleStringMap` from currently cached perp markets, calling `addOracle` for any oracle not yet tracked by `oracleMultiSubscriber`. */
|
|
550
|
-
async setPerpOracleMap() {
|
|
551
|
-
const perpMarketsMap = this.perpMarketsSubscriber?.getAccountDataMap();
|
|
552
|
-
const perpMarkets = Array.from(perpMarketsMap?.values() ?? []);
|
|
553
|
-
const addOraclePromises = [];
|
|
554
|
-
for (const perpMarket of perpMarkets) {
|
|
555
|
-
if (!perpMarket || !perpMarket.data) {
|
|
556
|
-
continue;
|
|
557
|
-
}
|
|
558
|
-
const perpMarketAccount = perpMarket.data;
|
|
559
|
-
const perpMarketIndex = perpMarketAccount.marketIndex;
|
|
560
|
-
const oracle = perpMarketAccount.oracle;
|
|
561
|
-
const oracleId = getOracleId(oracle, perpMarket.data.oracleSource);
|
|
562
|
-
if (!this.oracleMultiSubscriber?.getAccountDataMap().has(oracleId)) {
|
|
563
|
-
addOraclePromises.push(
|
|
564
|
-
this.addOracle({
|
|
565
|
-
publicKey: oracle,
|
|
566
|
-
source: perpMarket.data.oracleSource,
|
|
567
|
-
})
|
|
568
|
-
);
|
|
569
|
-
}
|
|
570
|
-
this.perpOracleMap.set(perpMarketIndex, oracle);
|
|
571
|
-
this.perpOracleStringMap.set(perpMarketIndex, oracleId);
|
|
572
|
-
}
|
|
573
|
-
await Promise.all(addOraclePromises);
|
|
574
|
-
}
|
|
575
|
-
|
|
576
|
-
/** Rebuilds `spotOracleMap`/`spotOracleStringMap` from currently cached spot markets, calling `addOracle` for any oracle not yet tracked by `oracleMultiSubscriber`. */
|
|
577
|
-
async setSpotOracleMap() {
|
|
578
|
-
const spotMarketsMap = this.spotMarketsSubscriber?.getAccountDataMap();
|
|
579
|
-
const spotMarkets = Array.from(spotMarketsMap?.values() ?? []);
|
|
580
|
-
const addOraclePromises = [];
|
|
581
|
-
for (const spotMarket of spotMarkets) {
|
|
582
|
-
if (!spotMarket || !spotMarket.data) {
|
|
583
|
-
continue;
|
|
584
|
-
}
|
|
585
|
-
const spotMarketAccount = spotMarket.data;
|
|
586
|
-
const spotMarketIndex = spotMarketAccount.marketIndex;
|
|
587
|
-
const oracle = spotMarketAccount.oracle;
|
|
588
|
-
const oracleId = getOracleId(oracle, spotMarketAccount.oracleSource);
|
|
589
|
-
if (!this.oracleMultiSubscriber?.getAccountDataMap().has(oracleId)) {
|
|
590
|
-
addOraclePromises.push(
|
|
591
|
-
this.addOracle({
|
|
592
|
-
publicKey: oracle,
|
|
593
|
-
source: spotMarketAccount.oracleSource,
|
|
594
|
-
})
|
|
595
|
-
);
|
|
596
|
-
}
|
|
597
|
-
this.spotOracleMap.set(spotMarketIndex, oracle);
|
|
598
|
-
this.spotOracleStringMap.set(spotMarketIndex, oracleId);
|
|
599
|
-
}
|
|
600
|
-
await Promise.all(addOraclePromises);
|
|
601
|
-
}
|
|
602
|
-
|
|
603
|
-
/**
|
|
604
|
-
* Creates `perpMarketsSubscriber` (a `grpcMultiAccountSubscriber<PerpMarketAccount>`), seeds
|
|
605
|
-
* it with `initialPerpMarketAccountData`, and subscribes it to every tracked perp market
|
|
606
|
-
* pubkey in one gRPC stream. Registers an `onUnsubscribe` handler that automatically
|
|
607
|
-
* re-invokes this method to resubscribe if the underlying stream drops.
|
|
608
|
-
*/
|
|
609
|
-
async subscribeToPerpMarketAccounts(): Promise<boolean> {
|
|
610
|
-
if (this.resubOpts?.logResubMessages) {
|
|
611
|
-
console.log(
|
|
612
|
-
'[grpcVelocityClientAccountSubscriberV2] subscribeToPerpMarketAccounts'
|
|
613
|
-
);
|
|
614
|
-
}
|
|
615
|
-
const perpMarketIndexToAccountPubkeys: Array<[number, PublicKey]> =
|
|
616
|
-
await Promise.all(
|
|
617
|
-
this.perpMarketIndexes.map(async (marketIndex) => [
|
|
618
|
-
marketIndex,
|
|
619
|
-
await getPerpMarketPublicKey(this.program.programId, marketIndex),
|
|
620
|
-
])
|
|
621
|
-
);
|
|
622
|
-
for (const [
|
|
623
|
-
marketIndex,
|
|
624
|
-
accountPubkey,
|
|
625
|
-
] of perpMarketIndexToAccountPubkeys) {
|
|
626
|
-
this.perpMarketIndexToAccountPubkeyMap.set(
|
|
627
|
-
marketIndex,
|
|
628
|
-
accountPubkey.toBase58()
|
|
629
|
-
);
|
|
630
|
-
}
|
|
631
|
-
|
|
632
|
-
const perpMarketPubkeys = perpMarketIndexToAccountPubkeys.map(
|
|
633
|
-
([_, accountPubkey]) => accountPubkey
|
|
634
|
-
);
|
|
635
|
-
|
|
636
|
-
this.perpMarketsSubscriber =
|
|
637
|
-
await grpcMultiAccountSubscriber.create<PerpMarketAccount>(
|
|
638
|
-
this.grpcConfigs,
|
|
639
|
-
'perpMarket',
|
|
640
|
-
this.program,
|
|
641
|
-
undefined,
|
|
642
|
-
this.resubOpts,
|
|
643
|
-
undefined,
|
|
644
|
-
async () => {
|
|
645
|
-
try {
|
|
646
|
-
if (this.resubOpts?.logResubMessages) {
|
|
647
|
-
console.log(
|
|
648
|
-
'[grpcVelocityClientAccountSubscriberV2] perp markets subscriber unsubscribed; resubscribing'
|
|
649
|
-
);
|
|
650
|
-
}
|
|
651
|
-
await this.subscribeToPerpMarketAccounts();
|
|
652
|
-
} catch (e) {
|
|
653
|
-
console.error('Perp markets resubscribe failed:', e);
|
|
654
|
-
}
|
|
655
|
-
}
|
|
656
|
-
);
|
|
657
|
-
|
|
658
|
-
for (const data of this.initialPerpMarketAccountData.values()) {
|
|
659
|
-
this.perpMarketsSubscriber.setAccountData(data.pubkey.toBase58(), data);
|
|
660
|
-
}
|
|
661
|
-
|
|
662
|
-
await this.perpMarketsSubscriber.subscribe(
|
|
663
|
-
perpMarketPubkeys,
|
|
664
|
-
(_accountId, data) => {
|
|
665
|
-
this.eventEmitter.emit(
|
|
666
|
-
'perpMarketAccountUpdate',
|
|
667
|
-
data as PerpMarketAccount
|
|
668
|
-
);
|
|
669
|
-
this.eventEmitter.emit('update');
|
|
670
|
-
}
|
|
671
|
-
);
|
|
672
|
-
|
|
673
|
-
return true;
|
|
674
|
-
}
|
|
675
|
-
|
|
676
|
-
/**
|
|
677
|
-
* Creates `spotMarketsSubscriber` (a `grpcMultiAccountSubscriber<SpotMarketAccount>`), seeds
|
|
678
|
-
* it with `initialSpotMarketAccountData`, and subscribes it to every tracked spot market
|
|
679
|
-
* pubkey in one gRPC stream. Registers an `onUnsubscribe` handler that automatically
|
|
680
|
-
* re-invokes this method to resubscribe if the underlying stream drops.
|
|
681
|
-
*/
|
|
682
|
-
async subscribeToSpotMarketAccounts(): Promise<boolean> {
|
|
683
|
-
if (this.resubOpts?.logResubMessages) {
|
|
684
|
-
console.log(
|
|
685
|
-
'[grpcVelocityClientAccountSubscriberV2] subscribeToSpotMarketAccounts'
|
|
686
|
-
);
|
|
687
|
-
}
|
|
688
|
-
const spotMarketIndexToAccountPubkeys: Array<[number, PublicKey]> =
|
|
689
|
-
await Promise.all(
|
|
690
|
-
this.spotMarketIndexes.map(async (marketIndex) => [
|
|
691
|
-
marketIndex,
|
|
692
|
-
await getSpotMarketPublicKey(this.program.programId, marketIndex),
|
|
693
|
-
])
|
|
694
|
-
);
|
|
695
|
-
for (const [
|
|
696
|
-
marketIndex,
|
|
697
|
-
accountPubkey,
|
|
698
|
-
] of spotMarketIndexToAccountPubkeys) {
|
|
699
|
-
this.spotMarketIndexToAccountPubkeyMap.set(
|
|
700
|
-
marketIndex,
|
|
701
|
-
accountPubkey.toBase58()
|
|
702
|
-
);
|
|
703
|
-
}
|
|
704
|
-
|
|
705
|
-
const spotMarketPubkeys = spotMarketIndexToAccountPubkeys.map(
|
|
706
|
-
([_, accountPubkey]) => accountPubkey
|
|
707
|
-
);
|
|
708
|
-
|
|
709
|
-
this.spotMarketsSubscriber =
|
|
710
|
-
await grpcMultiAccountSubscriber.create<SpotMarketAccount>(
|
|
711
|
-
this.grpcConfigs,
|
|
712
|
-
'spotMarket',
|
|
713
|
-
this.program,
|
|
714
|
-
undefined,
|
|
715
|
-
this.resubOpts,
|
|
716
|
-
undefined,
|
|
717
|
-
async () => {
|
|
718
|
-
try {
|
|
719
|
-
if (this.resubOpts?.logResubMessages) {
|
|
720
|
-
console.log(
|
|
721
|
-
'[grpcVelocityClientAccountSubscriberV2] spot markets subscriber unsubscribed; resubscribing'
|
|
722
|
-
);
|
|
723
|
-
}
|
|
724
|
-
await this.subscribeToSpotMarketAccounts();
|
|
725
|
-
} catch (e) {
|
|
726
|
-
console.error('Spot markets resubscribe failed:', e);
|
|
727
|
-
}
|
|
728
|
-
}
|
|
729
|
-
);
|
|
730
|
-
|
|
731
|
-
for (const data of this.initialSpotMarketAccountData.values()) {
|
|
732
|
-
this.spotMarketsSubscriber.setAccountData(data.pubkey.toBase58(), data);
|
|
733
|
-
}
|
|
734
|
-
|
|
735
|
-
await this.spotMarketsSubscriber.subscribe(
|
|
736
|
-
spotMarketPubkeys,
|
|
737
|
-
(_accountId, data) => {
|
|
738
|
-
this.eventEmitter.emit(
|
|
739
|
-
'spotMarketAccountUpdate',
|
|
740
|
-
data as SpotMarketAccount
|
|
741
|
-
);
|
|
742
|
-
this.eventEmitter.emit('update');
|
|
743
|
-
}
|
|
744
|
-
);
|
|
745
|
-
|
|
746
|
-
return true;
|
|
747
|
-
}
|
|
748
|
-
|
|
749
|
-
/**
|
|
750
|
-
* Creates `oracleMultiSubscriber` (a `grpcMultiAccountSubscriber<OraclePriceData, OracleInfo>`)
|
|
751
|
-
* and subscribes it to every distinct oracle pubkey in one gRPC stream, decoding buffers with
|
|
752
|
-
* the source-appropriate `OracleClient`. Because multiple `(pubkey, source)` oracle ids can
|
|
753
|
-
* share one pubkey, `oraclePubkeyToInfosMap` fans a single decode out to every matching
|
|
754
|
-
* `OracleInfo`, and results are additionally indexed into `oracleIdToOracleDataMap` (by oracle
|
|
755
|
-
* id, not pubkey) for `getOraclePriceDataAndSlot` to read. Registers an `onUnsubscribe` handler
|
|
756
|
-
* that automatically re-invokes this method to resubscribe if the underlying stream drops.
|
|
757
|
-
*/
|
|
758
|
-
async subscribeToOracles(): Promise<boolean> {
|
|
759
|
-
if (this.resubOpts?.logResubMessages) {
|
|
760
|
-
console.log('grpcVelocityClientAccountSubscriberV2 subscribeToOracles');
|
|
761
|
-
}
|
|
762
|
-
const oraclePubkeyToInfosMap = new Map<string, OracleInfo[]>();
|
|
763
|
-
for (const info of this.oracleInfos) {
|
|
764
|
-
const pubkey = info.publicKey.toBase58();
|
|
765
|
-
if (!oraclePubkeyToInfosMap.has(pubkey)) {
|
|
766
|
-
oraclePubkeyToInfosMap.set(pubkey, []);
|
|
767
|
-
}
|
|
768
|
-
oraclePubkeyToInfosMap.get(pubkey)!.push(info);
|
|
769
|
-
}
|
|
770
|
-
|
|
771
|
-
const oraclePubkeys = Array.from(
|
|
772
|
-
new Set(this.oracleInfos.map((info) => info.publicKey))
|
|
773
|
-
);
|
|
774
|
-
|
|
775
|
-
this.oracleMultiSubscriber = await grpcMultiAccountSubscriber.create<
|
|
776
|
-
OraclePriceData,
|
|
777
|
-
OracleInfo
|
|
778
|
-
>(
|
|
779
|
-
this.grpcConfigs,
|
|
780
|
-
'oracle',
|
|
781
|
-
this.program,
|
|
782
|
-
(buffer: Buffer, pubkey?: string, accountProps?: OracleInfo) => {
|
|
783
|
-
if (!pubkey) {
|
|
784
|
-
throw new Error('Oracle pubkey missing in decode');
|
|
785
|
-
}
|
|
786
|
-
|
|
787
|
-
if (!accountProps) {
|
|
788
|
-
throw new Error('Oracle accountProps missing in decode');
|
|
789
|
-
}
|
|
790
|
-
|
|
791
|
-
const client = this.oracleClientCache.get(
|
|
792
|
-
accountProps.source,
|
|
793
|
-
this.program.provider.connection,
|
|
794
|
-
this.program
|
|
795
|
-
);
|
|
796
|
-
if (!client) {
|
|
797
|
-
throw new Error('Oracle client missing in decode');
|
|
798
|
-
}
|
|
799
|
-
const price = client.getOraclePriceDataFromBuffer(buffer);
|
|
800
|
-
return price;
|
|
801
|
-
},
|
|
802
|
-
this.resubOpts,
|
|
803
|
-
undefined,
|
|
804
|
-
async () => {
|
|
805
|
-
try {
|
|
806
|
-
if (this.resubOpts?.logResubMessages) {
|
|
807
|
-
console.log(
|
|
808
|
-
'[grpcVelocityClientAccountSubscriberV2] oracle subscriber unsubscribed; resubscribing'
|
|
809
|
-
);
|
|
810
|
-
}
|
|
811
|
-
await this.subscribeToOracles();
|
|
812
|
-
} catch (e) {
|
|
813
|
-
console.error('Oracle resubscribe failed:', e);
|
|
814
|
-
}
|
|
815
|
-
},
|
|
816
|
-
oraclePubkeyToInfosMap
|
|
817
|
-
);
|
|
818
|
-
|
|
819
|
-
for (const data of this.initialOraclePriceData.entries()) {
|
|
820
|
-
const { publicKey } = getPublicKeyAndSourceFromOracleId(data[0]);
|
|
821
|
-
this.oracleMultiSubscriber.setAccountData(publicKey.toBase58(), data[1]);
|
|
822
|
-
this.oracleIdToOracleDataMap.set(data[0], {
|
|
823
|
-
data: data[1],
|
|
824
|
-
slot: 0,
|
|
825
|
-
});
|
|
826
|
-
}
|
|
827
|
-
|
|
828
|
-
await this.oracleMultiSubscriber.subscribe(
|
|
829
|
-
oraclePubkeys,
|
|
830
|
-
(accountId, data, context, _b, accountProps) => {
|
|
831
|
-
if (accountProps === undefined) {
|
|
832
|
-
return;
|
|
833
|
-
}
|
|
834
|
-
const oracleId = getOracleId(accountId, accountProps.source);
|
|
835
|
-
this.oracleIdToOracleDataMap.set(oracleId, {
|
|
836
|
-
data,
|
|
837
|
-
slot: context.slot,
|
|
838
|
-
});
|
|
839
|
-
this.eventEmitter.emit(
|
|
840
|
-
'oraclePriceUpdate',
|
|
841
|
-
accountId,
|
|
842
|
-
accountProps.source,
|
|
843
|
-
data
|
|
844
|
-
);
|
|
845
|
-
|
|
846
|
-
this.eventEmitter.emit('update');
|
|
847
|
-
}
|
|
848
|
-
);
|
|
849
|
-
|
|
850
|
-
return true;
|
|
851
|
-
}
|
|
852
|
-
|
|
853
|
-
/**
|
|
854
|
-
* Applies `delistedMarketSetting` to any perp market currently `status: delisted` (and its
|
|
855
|
-
* oracle, if not shared with a live spot market) by removing its pubkey from the relevant
|
|
856
|
-
* `grpcMultiAccountSubscriber` via `removeAccounts`. A no-op if the setting is `Subscribe`.
|
|
857
|
-
* Unlike the WebSocket/polling variants, this does not distinguish `Unsubscribe` from
|
|
858
|
-
* `Discard` — both remove the account from the multiplexed stream (there is no per-account
|
|
859
|
-
* "keep last known data, stop streaming" state to preserve at this granularity).
|
|
860
|
-
*/
|
|
861
|
-
async handleDelistedMarkets(): Promise<void> {
|
|
862
|
-
if (this.delistedMarketSetting === DelistedMarketSetting.Subscribe) {
|
|
863
|
-
return;
|
|
864
|
-
}
|
|
865
|
-
|
|
866
|
-
const { perpMarketIndexes, oracles } = findDelistedPerpMarketsAndOracles(
|
|
867
|
-
Array.from(
|
|
868
|
-
this.perpMarketsSubscriber?.getAccountDataMap().values() || []
|
|
869
|
-
),
|
|
870
|
-
Array.from(this.spotMarketsSubscriber?.getAccountDataMap().values() || [])
|
|
871
|
-
);
|
|
872
|
-
|
|
873
|
-
// Build array of perp market pubkeys to remove
|
|
874
|
-
const perpMarketPubkeysToRemove = perpMarketIndexes
|
|
875
|
-
.map((marketIndex) => {
|
|
876
|
-
const pubkeyString =
|
|
877
|
-
this.perpMarketIndexToAccountPubkeyMap.get(marketIndex);
|
|
878
|
-
return pubkeyString ? new PublicKey(pubkeyString) : null;
|
|
879
|
-
})
|
|
880
|
-
.filter((pubkey): pubkey is PublicKey => pubkey !== null);
|
|
881
|
-
|
|
882
|
-
// Build array of oracle pubkeys to remove
|
|
883
|
-
const oraclePubkeysToRemove = oracles.map((oracle) => oracle.publicKey);
|
|
884
|
-
|
|
885
|
-
// Remove accounts in batches - perp markets
|
|
886
|
-
if (perpMarketPubkeysToRemove.length > 0) {
|
|
887
|
-
await this.perpMarketsSubscriber?.removeAccounts(
|
|
888
|
-
perpMarketPubkeysToRemove
|
|
889
|
-
);
|
|
890
|
-
}
|
|
891
|
-
|
|
892
|
-
// Remove accounts in batches - oracles
|
|
893
|
-
if (oraclePubkeysToRemove.length > 0) {
|
|
894
|
-
await this.oracleMultiSubscriber?.removeAccounts(oraclePubkeysToRemove);
|
|
895
|
-
}
|
|
896
|
-
}
|
|
897
|
-
|
|
898
|
-
/** Clears the seed data stashed by `setInitialData()` once the multi-account subscribers have consumed it, freeing the memory. */
|
|
899
|
-
removeInitialData() {
|
|
900
|
-
this.initialPerpMarketAccountData = new Map();
|
|
901
|
-
this.initialSpotMarketAccountData = new Map();
|
|
902
|
-
this.initialOraclePriceData = new Map();
|
|
903
|
-
}
|
|
904
|
-
|
|
905
|
-
/** Tears down `oracleMultiSubscriber`'s gRPC stream, if active, and clears the reference. */
|
|
906
|
-
async unsubscribeFromOracles(): Promise<void> {
|
|
907
|
-
if (this.oracleMultiSubscriber) {
|
|
908
|
-
await this.oracleMultiSubscriber.unsubscribe();
|
|
909
|
-
this.oracleMultiSubscriber = undefined;
|
|
910
|
-
return;
|
|
911
|
-
}
|
|
912
|
-
}
|
|
913
|
-
|
|
914
|
-
/**
|
|
915
|
-
* Tears down the `State` subscriber and all three multiplexed subscribers (perp markets, spot
|
|
916
|
-
* markets, oracles), then clears every internal map to avoid holding stale references. A no-op
|
|
917
|
-
* if not subscribed.
|
|
918
|
-
*/
|
|
919
|
-
async unsubscribe(): Promise<void> {
|
|
920
|
-
if (!this.isSubscribed) {
|
|
921
|
-
return;
|
|
922
|
-
}
|
|
923
|
-
|
|
924
|
-
this.isSubscribed = false;
|
|
925
|
-
this.isSubscribing = false;
|
|
926
|
-
|
|
927
|
-
await this.stateAccountSubscriber?.unsubscribe();
|
|
928
|
-
await this.unsubscribeFromOracles();
|
|
929
|
-
await this.perpMarketsSubscriber?.unsubscribe();
|
|
930
|
-
await this.spotMarketsSubscriber?.unsubscribe();
|
|
931
|
-
|
|
932
|
-
// Clean up all maps to prevent memory leaks
|
|
933
|
-
this.perpMarketIndexToAccountPubkeyMap.clear();
|
|
934
|
-
this.spotMarketIndexToAccountPubkeyMap.clear();
|
|
935
|
-
this.oracleIdToOracleDataMap.clear();
|
|
936
|
-
this.perpOracleMap.clear();
|
|
937
|
-
this.perpOracleStringMap.clear();
|
|
938
|
-
this.spotOracleMap.clear();
|
|
939
|
-
this.spotOracleStringMap.clear();
|
|
940
|
-
}
|
|
941
|
-
}
|