@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.
Files changed (325) hide show
  1. package/README.md +3 -3
  2. package/lib/browser/adminClient.d.ts +52 -0
  3. package/lib/browser/adminClient.js +97 -0
  4. package/lib/browser/config.js +6 -7
  5. package/lib/browser/constants/numericConstants.d.ts +1 -1
  6. package/lib/browser/constants/numericConstants.js +1 -1
  7. package/lib/browser/decode/user.js +5 -1
  8. package/lib/browser/idl/velocity.d.ts +191 -15
  9. package/lib/browser/idl/velocity.json +192 -16
  10. package/lib/browser/orderSubscriber/OrderSubscriber.d.ts +1 -0
  11. package/lib/browser/orderSubscriber/OrderSubscriber.js +10 -4
  12. package/lib/browser/pyth/pythLazerSubscriber.js +37 -27
  13. package/lib/browser/types.d.ts +6 -0
  14. package/lib/browser/user.d.ts +20 -3
  15. package/lib/browser/user.js +52 -4
  16. package/lib/browser/velocityClient.d.ts +46 -10
  17. package/lib/browser/velocityClient.js +107 -35
  18. package/lib/node/adminClient.d.ts +52 -0
  19. package/lib/node/adminClient.d.ts.map +1 -1
  20. package/lib/node/adminClient.js +97 -0
  21. package/lib/node/config.d.ts.map +1 -1
  22. package/lib/node/config.js +6 -7
  23. package/lib/node/constants/numericConstants.d.ts +1 -1
  24. package/lib/node/constants/numericConstants.d.ts.map +1 -1
  25. package/lib/node/constants/numericConstants.js +1 -1
  26. package/lib/node/decode/user.d.ts.map +1 -1
  27. package/lib/node/decode/user.js +5 -1
  28. package/lib/node/idl/velocity.d.ts +191 -15
  29. package/lib/node/idl/velocity.d.ts.map +1 -1
  30. package/lib/node/idl/velocity.json +192 -16
  31. package/lib/node/orderSubscriber/OrderSubscriber.d.ts +1 -0
  32. package/lib/node/orderSubscriber/OrderSubscriber.d.ts.map +1 -1
  33. package/lib/node/orderSubscriber/OrderSubscriber.js +10 -4
  34. package/lib/node/pyth/pythLazerSubscriber.d.ts.map +1 -1
  35. package/lib/node/pyth/pythLazerSubscriber.js +37 -27
  36. package/lib/node/types.d.ts +6 -0
  37. package/lib/node/types.d.ts.map +1 -1
  38. package/lib/node/user.d.ts +20 -3
  39. package/lib/node/user.d.ts.map +1 -1
  40. package/lib/node/user.js +52 -4
  41. package/lib/node/velocityClient.d.ts +46 -10
  42. package/lib/node/velocityClient.d.ts.map +1 -1
  43. package/lib/node/velocityClient.js +107 -35
  44. package/package.json +7 -3
  45. package/.prettierignore +0 -1
  46. package/CHANGELOG.md +0 -132
  47. package/build-browser.js +0 -58
  48. package/bunfig.toml +0 -3
  49. package/esbuild-shims.js +0 -12
  50. package/scripts/deposit-isolated-positions.ts +0 -110
  51. package/scripts/find-flagged-users.ts +0 -213
  52. package/scripts/grpc-client-test-comparison.ts +0 -372
  53. package/scripts/grpc-multiuser-client-test-comparison.ts +0 -158
  54. package/scripts/postbuild.js +0 -95
  55. package/scripts/single-grpc-client-test.ts +0 -284
  56. package/scripts/withdraw-isolated-positions.ts +0 -174
  57. package/src/accounts/README_WebSocketAccountSubscriberV2.md +0 -95
  58. package/src/accounts/README_WebSocketProgramAccountSubscriberV2.md +0 -135
  59. package/src/accounts/basicUserAccountSubscriber.ts +0 -79
  60. package/src/accounts/basicUserStatsAccountSubscriber.ts +0 -87
  61. package/src/accounts/bulkAccountLoader.ts +0 -358
  62. package/src/accounts/bulkUserStatsSubscription.ts +0 -40
  63. package/src/accounts/bulkUserSubscription.ts +0 -39
  64. package/src/accounts/customizedCadenceBulkAccountLoader.ts +0 -250
  65. package/src/accounts/fetch.ts +0 -200
  66. package/src/accounts/grpcAccountSubscriber.ts +0 -225
  67. package/src/accounts/grpcInsuranceFundStakeAccountSubscriber.ts +0 -73
  68. package/src/accounts/grpcMultiAccountSubscriber.ts +0 -566
  69. package/src/accounts/grpcMultiUserAccountSubscriber.ts +0 -328
  70. package/src/accounts/grpcProgramAccountSubscriber.ts +0 -264
  71. package/src/accounts/grpcUserAccountSubscriber.ts +0 -63
  72. package/src/accounts/grpcUserStatsAccountSubscriber.ts +0 -65
  73. package/src/accounts/grpcVelocityClientAccountSubscriber.ts +0 -257
  74. package/src/accounts/grpcVelocityClientAccountSubscriberV2.ts +0 -941
  75. package/src/accounts/laserProgramAccountSubscriber.ts +0 -266
  76. package/src/accounts/oneShotUserAccountSubscriber.ts +0 -88
  77. package/src/accounts/oneShotUserStatsAccountSubscriber.ts +0 -94
  78. package/src/accounts/pollingInsuranceFundStakeAccountSubscriber.ts +0 -223
  79. package/src/accounts/pollingOracleAccountSubscriber.ts +0 -151
  80. package/src/accounts/pollingTokenAccountSubscriber.ts +0 -147
  81. package/src/accounts/pollingUserAccountSubscriber.ts +0 -198
  82. package/src/accounts/pollingUserStatsAccountSubscriber.ts +0 -180
  83. package/src/accounts/pollingVelocityClientAccountSubscriber.ts +0 -774
  84. package/src/accounts/testBulkAccountLoader.ts +0 -59
  85. package/src/accounts/types.ts +0 -428
  86. package/src/accounts/utils.ts +0 -90
  87. package/src/accounts/webSocketAccountSubscriber.ts +0 -304
  88. package/src/accounts/webSocketAccountSubscriberV2.ts +0 -536
  89. package/src/accounts/webSocketInsuranceFundStakeAccountSubscriber.ts +0 -171
  90. package/src/accounts/webSocketProgramAccountSubscriber.ts +0 -244
  91. package/src/accounts/webSocketProgramAccountSubscriberV2.ts +0 -1048
  92. package/src/accounts/webSocketProgramAccountsSubscriberV2.ts +0 -1042
  93. package/src/accounts/webSocketUserAccountSubscriber.ts +0 -139
  94. package/src/accounts/webSocketUserStatsAccountSubsriber.ts +0 -131
  95. package/src/accounts/webSocketVelocityClientAccountSubscriber.ts +0 -865
  96. package/src/accounts/webSocketVelocityClientAccountSubscriberV2.ts +0 -897
  97. package/src/accounts/websocketProgramUserAccountSubscriber.ts +0 -127
  98. package/src/addresses/marketAddresses.ts +0 -28
  99. package/src/addresses/pda.ts +0 -620
  100. package/src/adminClient.ts +0 -8136
  101. package/src/assert/assert.ts +0 -5
  102. package/src/auctionSubscriber/auctionSubscriber.ts +0 -86
  103. package/src/auctionSubscriber/auctionSubscriberGrpc.ts +0 -99
  104. package/src/auctionSubscriber/index.ts +0 -9
  105. package/src/auctionSubscriber/types.ts +0 -30
  106. package/src/bankrun/bankrunConnection.ts +0 -630
  107. package/src/blockhashSubscriber/BlockhashSubscriber.ts +0 -160
  108. package/src/blockhashSubscriber/index.ts +0 -6
  109. package/src/blockhashSubscriber/types.ts +0 -13
  110. package/src/clock/clockSubscriber.ts +0 -146
  111. package/src/config.ts +0 -312
  112. package/src/constants/index.ts +0 -9
  113. package/src/constants/numericConstants.ts +0 -194
  114. package/src/constants/perpMarkets.ts +0 -127
  115. package/src/constants/spotMarkets.ts +0 -104
  116. package/src/constants/txConstants.ts +0 -2
  117. package/src/constituentMap/constituentMap.ts +0 -305
  118. package/src/constituentMap/pollingConstituentAccountSubscriber.ts +0 -99
  119. package/src/constituentMap/webSocketConstituentAccountSubscriber.ts +0 -121
  120. package/src/core/VelocityCore.ts +0 -606
  121. package/src/core/index.ts +0 -12
  122. package/src/core/instructions/deposit.ts +0 -64
  123. package/src/core/instructions/fill.ts +0 -51
  124. package/src/core/instructions/funding.ts +0 -33
  125. package/src/core/instructions/liquidation.ts +0 -55
  126. package/src/core/instructions/orders.ts +0 -84
  127. package/src/core/instructions/perpOrders.ts +0 -312
  128. package/src/core/instructions/settlement.ts +0 -40
  129. package/src/core/instructions/trigger.ts +0 -41
  130. package/src/core/instructions/withdraw.ts +0 -65
  131. package/src/core/remainingAccounts.ts +0 -320
  132. package/src/core/signedMsg.ts +0 -109
  133. package/src/decode/customCoder.ts +0 -148
  134. package/src/decode/user.ts +0 -401
  135. package/src/dlob/DLOB.ts +0 -2793
  136. package/src/dlob/DLOBNode.ts +0 -305
  137. package/src/dlob/DLOBSubscriber.ts +0 -269
  138. package/src/dlob/NodeList.ts +0 -244
  139. package/src/dlob/orderBookLevels.ts +0 -695
  140. package/src/dlob/types.ts +0 -37
  141. package/src/events/eventList.ts +0 -114
  142. package/src/events/eventSubscriber.ts +0 -521
  143. package/src/events/eventsServerLogProvider.ts +0 -189
  144. package/src/events/fetchLogs.ts +0 -219
  145. package/src/events/parse.ts +0 -271
  146. package/src/events/pollingLogProvider.ts +0 -113
  147. package/src/events/sort.ts +0 -51
  148. package/src/events/txEventCache.ts +0 -92
  149. package/src/events/types.ts +0 -337
  150. package/src/events/webSocketLogProvider.ts +0 -152
  151. package/src/factory/bigNum.ts +0 -709
  152. package/src/factory/oracleClient.ts +0 -73
  153. package/src/idl/pyth.json +0 -142
  154. package/src/idl/pyth.ts +0 -97
  155. package/src/idl/pyth_solana_receiver.json +0 -628
  156. package/src/idl/token_faucet.json +0 -229
  157. package/src/idl/token_faucet.ts +0 -197
  158. package/src/idl/velocity.json +0 -23974
  159. package/src/idl/velocity.ts +0 -23980
  160. package/src/index.ts +0 -150
  161. package/src/indicative-quotes/indicativeQuotesSender.ts +0 -233
  162. package/src/isomorphic/README.md +0 -19
  163. package/src/isomorphic/anchor.browser.ts +0 -44
  164. package/src/isomorphic/anchor.node.ts +0 -1
  165. package/src/isomorphic/anchor.ts +0 -1
  166. package/src/isomorphic/anchor29.browser.ts +0 -24
  167. package/src/isomorphic/anchor29.node.ts +0 -1
  168. package/src/isomorphic/anchor29.ts +0 -1
  169. package/src/isomorphic/grpc.browser.ts +0 -4
  170. package/src/isomorphic/grpc.node.ts +0 -130
  171. package/src/isomorphic/grpc.ts +0 -1
  172. package/src/jupiter/jupiterClient.ts +0 -502
  173. package/src/keypair.ts +0 -32
  174. package/src/margin/README.md +0 -138
  175. package/src/marginCalculation.ts +0 -407
  176. package/src/marinade/idl/idl.json +0 -1962
  177. package/src/marinade/index.ts +0 -73
  178. package/src/marinade/types.ts +0 -3925
  179. package/src/math/amm.ts +0 -1758
  180. package/src/math/auction.ts +0 -480
  181. package/src/math/bankruptcy.ts +0 -138
  182. package/src/math/builder.ts +0 -130
  183. package/src/math/conversion.ts +0 -45
  184. package/src/math/exchangeStatus.ts +0 -261
  185. package/src/math/funding.ts +0 -498
  186. package/src/math/insurance.ts +0 -171
  187. package/src/math/liquidation.ts +0 -433
  188. package/src/math/margin.ts +0 -327
  189. package/src/math/market.ts +0 -487
  190. package/src/math/oracles.ts +0 -477
  191. package/src/math/orders.ts +0 -500
  192. package/src/math/position.ts +0 -381
  193. package/src/math/repeg.ts +0 -223
  194. package/src/math/spotBalance.ts +0 -996
  195. package/src/math/spotMarket.ts +0 -115
  196. package/src/math/spotPosition.ts +0 -263
  197. package/src/math/state.ts +0 -71
  198. package/src/math/superStake.ts +0 -602
  199. package/src/math/tiers.ts +0 -73
  200. package/src/math/trade.ts +0 -879
  201. package/src/math/utils.ts +0 -183
  202. package/src/memcmp.ts +0 -279
  203. package/src/oracles/oracleClientCache.ts +0 -41
  204. package/src/oracles/oracleId.ts +0 -99
  205. package/src/oracles/prelaunchOracleClient.ts +0 -63
  206. package/src/oracles/pythClient.ts +0 -125
  207. package/src/oracles/pythLazerClient.ts +0 -141
  208. package/src/oracles/quoteAssetOracleClient.ts +0 -43
  209. package/src/oracles/strictOraclePrice.ts +0 -40
  210. package/src/oracles/types.ts +0 -82
  211. package/src/oracles/utils.ts +0 -45
  212. package/src/orderParams.ts +0 -117
  213. package/src/orderSubscriber/OrderSubscriber.ts +0 -378
  214. package/src/orderSubscriber/PollingSubscription.ts +0 -50
  215. package/src/orderSubscriber/WebsocketSubscription.ts +0 -137
  216. package/src/orderSubscriber/grpcSubscription.ts +0 -155
  217. package/src/orderSubscriber/index.ts +0 -7
  218. package/src/orderSubscriber/types.ts +0 -64
  219. package/src/priorityFee/averageOverSlotsStrategy.ts +0 -17
  220. package/src/priorityFee/averageStrategy.ts +0 -13
  221. package/src/priorityFee/ewmaStrategy.ts +0 -51
  222. package/src/priorityFee/heliusPriorityFeeMethod.ts +0 -68
  223. package/src/priorityFee/index.ts +0 -16
  224. package/src/priorityFee/maxOverSlotsStrategy.ts +0 -18
  225. package/src/priorityFee/maxStrategy.ts +0 -8
  226. package/src/priorityFee/priorityFeeSubscriber.ts +0 -328
  227. package/src/priorityFee/priorityFeeSubscriberMap.ts +0 -134
  228. package/src/priorityFee/solanaPriorityFeeMethod.ts +0 -46
  229. package/src/priorityFee/types.ts +0 -82
  230. package/src/priorityFee/velocityPriorityFeeMethod.ts +0 -53
  231. package/src/pyth/constants.ts +0 -9
  232. package/src/pyth/index.ts +0 -15
  233. package/src/pyth/pythLazerSubscriber.ts +0 -365
  234. package/src/pyth/types.ts +0 -4451
  235. package/src/pyth/utils.ts +0 -13
  236. package/src/slot/SlotSubscriber.ts +0 -126
  237. package/src/slot/SlothashSubscriber.ts +0 -163
  238. package/src/swap/UnifiedSwapClient.ts +0 -315
  239. package/src/swift/grpcSignedMsgUserAccountSubscriber.ts +0 -81
  240. package/src/swift/index.ts +0 -10
  241. package/src/swift/signedMsgUserAccountSubscriber.ts +0 -251
  242. package/src/swift/swiftOrderSubscriber.ts +0 -351
  243. package/src/testClient.ts +0 -42
  244. package/src/titan/titanClient.ts +0 -438
  245. package/src/token/index.ts +0 -13
  246. package/src/tokenFaucet.ts +0 -296
  247. package/src/tx/baseTxSender.ts +0 -651
  248. package/src/tx/blockhashFetcher/baseBlockhashFetcher.ts +0 -31
  249. package/src/tx/blockhashFetcher/cachedBlockhashFetcher.ts +0 -105
  250. package/src/tx/blockhashFetcher/types.ts +0 -9
  251. package/src/tx/fastSingleTxSender.ts +0 -200
  252. package/src/tx/forwardOnlyTxSender.ts +0 -193
  253. package/src/tx/priorityFeeCalculator.ts +0 -117
  254. package/src/tx/reportTransactionError.ts +0 -176
  255. package/src/tx/retryTxSender.ts +0 -177
  256. package/src/tx/txHandler.ts +0 -1009
  257. package/src/tx/txParamProcessor.ts +0 -225
  258. package/src/tx/types.ts +0 -117
  259. package/src/tx/utils.ts +0 -133
  260. package/src/tx/whileValidTxSender.ts +0 -377
  261. package/src/types.ts +0 -2575
  262. package/src/user.ts +0 -5316
  263. package/src/userConfig.ts +0 -38
  264. package/src/userMap/PollingSubscription.ts +0 -61
  265. package/src/userMap/WebsocketSubscription.ts +0 -93
  266. package/src/userMap/grpcSubscription.ts +0 -93
  267. package/src/userMap/referrerMap.ts +0 -360
  268. package/src/userMap/revenueShareEscrowMap.ts +0 -342
  269. package/src/userMap/userMap.ts +0 -784
  270. package/src/userMap/userMapConfig.ts +0 -82
  271. package/src/userMap/userStatsMap.ts +0 -393
  272. package/src/userName.ts +0 -37
  273. package/src/userStats.ts +0 -159
  274. package/src/userStatsConfig.ts +0 -35
  275. package/src/util/TransactionConfirmationManager.ts +0 -333
  276. package/src/util/chainClock.ts +0 -65
  277. package/src/util/computeUnits.ts +0 -101
  278. package/src/util/digest.ts +0 -33
  279. package/src/util/ed25519Utils.ts +0 -95
  280. package/src/util/promiseTimeout.ts +0 -23
  281. package/src/velocityClient.ts +0 -14802
  282. package/src/velocityClientConfig.ts +0 -119
  283. package/src/wallet.ts +0 -91
  284. package/tests/VelocityCore/builder_escrow.test.ts +0 -121
  285. package/tests/VelocityCore/decode.test.ts +0 -16
  286. package/tests/VelocityCore/fill_trigger.test.ts +0 -73
  287. package/tests/VelocityCore/instructions.test.ts +0 -46
  288. package/tests/VelocityCore/pdas.test.ts +0 -30
  289. package/tests/VelocityCore/perp_orders.test.ts +0 -205
  290. package/tests/VelocityCore/remainingAccounts.test.ts +0 -72
  291. package/tests/VelocityCore/settlement_liquidation.test.ts +0 -69
  292. package/tests/accounts/customizedCadenceBulkAccountLoader.test.ts +0 -211
  293. package/tests/amm/test.ts +0 -2402
  294. package/tests/auctions/test.ts +0 -81
  295. package/tests/bn/test.ts +0 -355
  296. package/tests/builder/builderFee.test.ts +0 -42
  297. package/tests/ci/verifyConstants.ts +0 -393
  298. package/tests/decode/test.ts +0 -262
  299. package/tests/decode/userAccountBufferStrings.ts +0 -102
  300. package/tests/dlob/helpers.ts +0 -748
  301. package/tests/dlob/test.ts +0 -6969
  302. package/tests/dlob/tickSizeStandardization.ts +0 -545
  303. package/tests/events/parseLogsForCuUsage.ts +0 -139
  304. package/tests/exchangeStatus/test.ts +0 -45
  305. package/tests/insurance/test.ts +0 -40
  306. package/tests/liquidation/test.ts +0 -125
  307. package/tests/oracles/mmOracleGate.test.ts +0 -379
  308. package/tests/oracles/pythPegSnap.test.ts +0 -76
  309. package/tests/sdkParity/enumParity.test.ts +0 -84
  310. package/tests/sdkParity/marginCategoryFill.test.ts +0 -143
  311. package/tests/sdkParity/memcmpOffsets.test.ts +0 -139
  312. package/tests/spot/test.ts +0 -281
  313. package/tests/tx/TransactionConfirmationManager.test.ts +0 -312
  314. package/tests/tx/cachedBlockhashFetcher.test.ts +0 -96
  315. package/tests/tx/priorityFeeCalculator.ts +0 -77
  316. package/tests/tx/priorityFeeStrategy.ts +0 -95
  317. package/tests/user/bankruptcy.ts +0 -165
  318. package/tests/user/feeAndWithdrawLimits.ts +0 -284
  319. package/tests/user/getMarginCalculation.ts +0 -586
  320. package/tests/user/helpers.ts +0 -184
  321. package/tests/user/liquidations.ts +0 -129
  322. package/tests/user/marginCalculations.test.ts +0 -321
  323. package/tests/user/test.ts +0 -833
  324. package/tsconfig.browser.json +0 -18
  325. 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
- }