@velocity-exchange/sdk 0.4.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (323) hide show
  1. package/README.md +3 -3
  2. package/lib/browser/adminClient.d.ts +35 -0
  3. package/lib/browser/adminClient.js +65 -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 +146 -3
  9. package/lib/browser/idl/velocity.json +147 -4
  10. package/lib/browser/orderSubscriber/OrderSubscriber.js +3 -0
  11. package/lib/browser/pyth/pythLazerSubscriber.js +37 -27
  12. package/lib/browser/types.d.ts +4 -0
  13. package/lib/browser/user.d.ts +15 -0
  14. package/lib/browser/user.js +37 -1
  15. package/lib/browser/velocityClient.d.ts +46 -10
  16. package/lib/browser/velocityClient.js +107 -35
  17. package/lib/node/adminClient.d.ts +35 -0
  18. package/lib/node/adminClient.d.ts.map +1 -1
  19. package/lib/node/adminClient.js +65 -0
  20. package/lib/node/config.d.ts.map +1 -1
  21. package/lib/node/config.js +6 -7
  22. package/lib/node/constants/numericConstants.d.ts +1 -1
  23. package/lib/node/constants/numericConstants.d.ts.map +1 -1
  24. package/lib/node/constants/numericConstants.js +1 -1
  25. package/lib/node/decode/user.d.ts.map +1 -1
  26. package/lib/node/decode/user.js +5 -1
  27. package/lib/node/idl/velocity.d.ts +146 -3
  28. package/lib/node/idl/velocity.d.ts.map +1 -1
  29. package/lib/node/idl/velocity.json +147 -4
  30. package/lib/node/orderSubscriber/OrderSubscriber.d.ts.map +1 -1
  31. package/lib/node/orderSubscriber/OrderSubscriber.js +3 -0
  32. package/lib/node/pyth/pythLazerSubscriber.d.ts.map +1 -1
  33. package/lib/node/pyth/pythLazerSubscriber.js +37 -27
  34. package/lib/node/types.d.ts +4 -0
  35. package/lib/node/types.d.ts.map +1 -1
  36. package/lib/node/user.d.ts +15 -0
  37. package/lib/node/user.d.ts.map +1 -1
  38. package/lib/node/user.js +37 -1
  39. package/lib/node/velocityClient.d.ts +46 -10
  40. package/lib/node/velocityClient.d.ts.map +1 -1
  41. package/lib/node/velocityClient.js +107 -35
  42. package/package.json +7 -3
  43. package/.prettierignore +0 -1
  44. package/CHANGELOG.md +0 -132
  45. package/build-browser.js +0 -58
  46. package/bunfig.toml +0 -3
  47. package/esbuild-shims.js +0 -12
  48. package/scripts/deposit-isolated-positions.ts +0 -110
  49. package/scripts/find-flagged-users.ts +0 -213
  50. package/scripts/grpc-client-test-comparison.ts +0 -372
  51. package/scripts/grpc-multiuser-client-test-comparison.ts +0 -158
  52. package/scripts/postbuild.js +0 -95
  53. package/scripts/single-grpc-client-test.ts +0 -284
  54. package/scripts/withdraw-isolated-positions.ts +0 -174
  55. package/src/accounts/README_WebSocketAccountSubscriberV2.md +0 -95
  56. package/src/accounts/README_WebSocketProgramAccountSubscriberV2.md +0 -135
  57. package/src/accounts/basicUserAccountSubscriber.ts +0 -79
  58. package/src/accounts/basicUserStatsAccountSubscriber.ts +0 -87
  59. package/src/accounts/bulkAccountLoader.ts +0 -358
  60. package/src/accounts/bulkUserStatsSubscription.ts +0 -40
  61. package/src/accounts/bulkUserSubscription.ts +0 -39
  62. package/src/accounts/customizedCadenceBulkAccountLoader.ts +0 -250
  63. package/src/accounts/fetch.ts +0 -200
  64. package/src/accounts/grpcAccountSubscriber.ts +0 -225
  65. package/src/accounts/grpcInsuranceFundStakeAccountSubscriber.ts +0 -73
  66. package/src/accounts/grpcMultiAccountSubscriber.ts +0 -566
  67. package/src/accounts/grpcMultiUserAccountSubscriber.ts +0 -328
  68. package/src/accounts/grpcProgramAccountSubscriber.ts +0 -264
  69. package/src/accounts/grpcUserAccountSubscriber.ts +0 -63
  70. package/src/accounts/grpcUserStatsAccountSubscriber.ts +0 -65
  71. package/src/accounts/grpcVelocityClientAccountSubscriber.ts +0 -257
  72. package/src/accounts/grpcVelocityClientAccountSubscriberV2.ts +0 -941
  73. package/src/accounts/laserProgramAccountSubscriber.ts +0 -266
  74. package/src/accounts/oneShotUserAccountSubscriber.ts +0 -88
  75. package/src/accounts/oneShotUserStatsAccountSubscriber.ts +0 -94
  76. package/src/accounts/pollingInsuranceFundStakeAccountSubscriber.ts +0 -223
  77. package/src/accounts/pollingOracleAccountSubscriber.ts +0 -151
  78. package/src/accounts/pollingTokenAccountSubscriber.ts +0 -147
  79. package/src/accounts/pollingUserAccountSubscriber.ts +0 -198
  80. package/src/accounts/pollingUserStatsAccountSubscriber.ts +0 -180
  81. package/src/accounts/pollingVelocityClientAccountSubscriber.ts +0 -774
  82. package/src/accounts/testBulkAccountLoader.ts +0 -59
  83. package/src/accounts/types.ts +0 -428
  84. package/src/accounts/utils.ts +0 -90
  85. package/src/accounts/webSocketAccountSubscriber.ts +0 -304
  86. package/src/accounts/webSocketAccountSubscriberV2.ts +0 -536
  87. package/src/accounts/webSocketInsuranceFundStakeAccountSubscriber.ts +0 -171
  88. package/src/accounts/webSocketProgramAccountSubscriber.ts +0 -244
  89. package/src/accounts/webSocketProgramAccountSubscriberV2.ts +0 -1048
  90. package/src/accounts/webSocketProgramAccountsSubscriberV2.ts +0 -1042
  91. package/src/accounts/webSocketUserAccountSubscriber.ts +0 -139
  92. package/src/accounts/webSocketUserStatsAccountSubsriber.ts +0 -131
  93. package/src/accounts/webSocketVelocityClientAccountSubscriber.ts +0 -865
  94. package/src/accounts/webSocketVelocityClientAccountSubscriberV2.ts +0 -897
  95. package/src/accounts/websocketProgramUserAccountSubscriber.ts +0 -127
  96. package/src/addresses/marketAddresses.ts +0 -28
  97. package/src/addresses/pda.ts +0 -620
  98. package/src/adminClient.ts +0 -8136
  99. package/src/assert/assert.ts +0 -5
  100. package/src/auctionSubscriber/auctionSubscriber.ts +0 -86
  101. package/src/auctionSubscriber/auctionSubscriberGrpc.ts +0 -99
  102. package/src/auctionSubscriber/index.ts +0 -9
  103. package/src/auctionSubscriber/types.ts +0 -30
  104. package/src/bankrun/bankrunConnection.ts +0 -630
  105. package/src/blockhashSubscriber/BlockhashSubscriber.ts +0 -160
  106. package/src/blockhashSubscriber/index.ts +0 -6
  107. package/src/blockhashSubscriber/types.ts +0 -13
  108. package/src/clock/clockSubscriber.ts +0 -146
  109. package/src/config.ts +0 -312
  110. package/src/constants/index.ts +0 -9
  111. package/src/constants/numericConstants.ts +0 -194
  112. package/src/constants/perpMarkets.ts +0 -127
  113. package/src/constants/spotMarkets.ts +0 -104
  114. package/src/constants/txConstants.ts +0 -2
  115. package/src/constituentMap/constituentMap.ts +0 -305
  116. package/src/constituentMap/pollingConstituentAccountSubscriber.ts +0 -99
  117. package/src/constituentMap/webSocketConstituentAccountSubscriber.ts +0 -121
  118. package/src/core/VelocityCore.ts +0 -606
  119. package/src/core/index.ts +0 -12
  120. package/src/core/instructions/deposit.ts +0 -64
  121. package/src/core/instructions/fill.ts +0 -51
  122. package/src/core/instructions/funding.ts +0 -33
  123. package/src/core/instructions/liquidation.ts +0 -55
  124. package/src/core/instructions/orders.ts +0 -84
  125. package/src/core/instructions/perpOrders.ts +0 -312
  126. package/src/core/instructions/settlement.ts +0 -40
  127. package/src/core/instructions/trigger.ts +0 -41
  128. package/src/core/instructions/withdraw.ts +0 -65
  129. package/src/core/remainingAccounts.ts +0 -320
  130. package/src/core/signedMsg.ts +0 -109
  131. package/src/decode/customCoder.ts +0 -148
  132. package/src/decode/user.ts +0 -401
  133. package/src/dlob/DLOB.ts +0 -2793
  134. package/src/dlob/DLOBNode.ts +0 -305
  135. package/src/dlob/DLOBSubscriber.ts +0 -269
  136. package/src/dlob/NodeList.ts +0 -244
  137. package/src/dlob/orderBookLevels.ts +0 -695
  138. package/src/dlob/types.ts +0 -37
  139. package/src/events/eventList.ts +0 -114
  140. package/src/events/eventSubscriber.ts +0 -521
  141. package/src/events/eventsServerLogProvider.ts +0 -189
  142. package/src/events/fetchLogs.ts +0 -219
  143. package/src/events/parse.ts +0 -271
  144. package/src/events/pollingLogProvider.ts +0 -113
  145. package/src/events/sort.ts +0 -51
  146. package/src/events/txEventCache.ts +0 -92
  147. package/src/events/types.ts +0 -337
  148. package/src/events/webSocketLogProvider.ts +0 -152
  149. package/src/factory/bigNum.ts +0 -709
  150. package/src/factory/oracleClient.ts +0 -73
  151. package/src/idl/pyth.json +0 -142
  152. package/src/idl/pyth.ts +0 -97
  153. package/src/idl/pyth_solana_receiver.json +0 -628
  154. package/src/idl/token_faucet.json +0 -229
  155. package/src/idl/token_faucet.ts +0 -197
  156. package/src/idl/velocity.json +0 -23974
  157. package/src/idl/velocity.ts +0 -23980
  158. package/src/index.ts +0 -150
  159. package/src/indicative-quotes/indicativeQuotesSender.ts +0 -233
  160. package/src/isomorphic/README.md +0 -19
  161. package/src/isomorphic/anchor.browser.ts +0 -44
  162. package/src/isomorphic/anchor.node.ts +0 -1
  163. package/src/isomorphic/anchor.ts +0 -1
  164. package/src/isomorphic/anchor29.browser.ts +0 -24
  165. package/src/isomorphic/anchor29.node.ts +0 -1
  166. package/src/isomorphic/anchor29.ts +0 -1
  167. package/src/isomorphic/grpc.browser.ts +0 -4
  168. package/src/isomorphic/grpc.node.ts +0 -130
  169. package/src/isomorphic/grpc.ts +0 -1
  170. package/src/jupiter/jupiterClient.ts +0 -502
  171. package/src/keypair.ts +0 -32
  172. package/src/margin/README.md +0 -138
  173. package/src/marginCalculation.ts +0 -407
  174. package/src/marinade/idl/idl.json +0 -1962
  175. package/src/marinade/index.ts +0 -73
  176. package/src/marinade/types.ts +0 -3925
  177. package/src/math/amm.ts +0 -1758
  178. package/src/math/auction.ts +0 -480
  179. package/src/math/bankruptcy.ts +0 -138
  180. package/src/math/builder.ts +0 -130
  181. package/src/math/conversion.ts +0 -45
  182. package/src/math/exchangeStatus.ts +0 -261
  183. package/src/math/funding.ts +0 -498
  184. package/src/math/insurance.ts +0 -171
  185. package/src/math/liquidation.ts +0 -433
  186. package/src/math/margin.ts +0 -327
  187. package/src/math/market.ts +0 -487
  188. package/src/math/oracles.ts +0 -477
  189. package/src/math/orders.ts +0 -500
  190. package/src/math/position.ts +0 -381
  191. package/src/math/repeg.ts +0 -223
  192. package/src/math/spotBalance.ts +0 -996
  193. package/src/math/spotMarket.ts +0 -115
  194. package/src/math/spotPosition.ts +0 -263
  195. package/src/math/state.ts +0 -71
  196. package/src/math/superStake.ts +0 -602
  197. package/src/math/tiers.ts +0 -73
  198. package/src/math/trade.ts +0 -879
  199. package/src/math/utils.ts +0 -183
  200. package/src/memcmp.ts +0 -279
  201. package/src/oracles/oracleClientCache.ts +0 -41
  202. package/src/oracles/oracleId.ts +0 -99
  203. package/src/oracles/prelaunchOracleClient.ts +0 -63
  204. package/src/oracles/pythClient.ts +0 -125
  205. package/src/oracles/pythLazerClient.ts +0 -141
  206. package/src/oracles/quoteAssetOracleClient.ts +0 -43
  207. package/src/oracles/strictOraclePrice.ts +0 -40
  208. package/src/oracles/types.ts +0 -82
  209. package/src/oracles/utils.ts +0 -45
  210. package/src/orderParams.ts +0 -117
  211. package/src/orderSubscriber/OrderSubscriber.ts +0 -378
  212. package/src/orderSubscriber/PollingSubscription.ts +0 -50
  213. package/src/orderSubscriber/WebsocketSubscription.ts +0 -137
  214. package/src/orderSubscriber/grpcSubscription.ts +0 -155
  215. package/src/orderSubscriber/index.ts +0 -7
  216. package/src/orderSubscriber/types.ts +0 -64
  217. package/src/priorityFee/averageOverSlotsStrategy.ts +0 -17
  218. package/src/priorityFee/averageStrategy.ts +0 -13
  219. package/src/priorityFee/ewmaStrategy.ts +0 -51
  220. package/src/priorityFee/heliusPriorityFeeMethod.ts +0 -68
  221. package/src/priorityFee/index.ts +0 -16
  222. package/src/priorityFee/maxOverSlotsStrategy.ts +0 -18
  223. package/src/priorityFee/maxStrategy.ts +0 -8
  224. package/src/priorityFee/priorityFeeSubscriber.ts +0 -328
  225. package/src/priorityFee/priorityFeeSubscriberMap.ts +0 -134
  226. package/src/priorityFee/solanaPriorityFeeMethod.ts +0 -46
  227. package/src/priorityFee/types.ts +0 -82
  228. package/src/priorityFee/velocityPriorityFeeMethod.ts +0 -53
  229. package/src/pyth/constants.ts +0 -9
  230. package/src/pyth/index.ts +0 -15
  231. package/src/pyth/pythLazerSubscriber.ts +0 -365
  232. package/src/pyth/types.ts +0 -4451
  233. package/src/pyth/utils.ts +0 -13
  234. package/src/slot/SlotSubscriber.ts +0 -126
  235. package/src/slot/SlothashSubscriber.ts +0 -163
  236. package/src/swap/UnifiedSwapClient.ts +0 -315
  237. package/src/swift/grpcSignedMsgUserAccountSubscriber.ts +0 -81
  238. package/src/swift/index.ts +0 -10
  239. package/src/swift/signedMsgUserAccountSubscriber.ts +0 -251
  240. package/src/swift/swiftOrderSubscriber.ts +0 -351
  241. package/src/testClient.ts +0 -42
  242. package/src/titan/titanClient.ts +0 -438
  243. package/src/token/index.ts +0 -13
  244. package/src/tokenFaucet.ts +0 -296
  245. package/src/tx/baseTxSender.ts +0 -651
  246. package/src/tx/blockhashFetcher/baseBlockhashFetcher.ts +0 -31
  247. package/src/tx/blockhashFetcher/cachedBlockhashFetcher.ts +0 -105
  248. package/src/tx/blockhashFetcher/types.ts +0 -9
  249. package/src/tx/fastSingleTxSender.ts +0 -200
  250. package/src/tx/forwardOnlyTxSender.ts +0 -193
  251. package/src/tx/priorityFeeCalculator.ts +0 -117
  252. package/src/tx/reportTransactionError.ts +0 -176
  253. package/src/tx/retryTxSender.ts +0 -177
  254. package/src/tx/txHandler.ts +0 -1009
  255. package/src/tx/txParamProcessor.ts +0 -225
  256. package/src/tx/types.ts +0 -117
  257. package/src/tx/utils.ts +0 -133
  258. package/src/tx/whileValidTxSender.ts +0 -377
  259. package/src/types.ts +0 -2575
  260. package/src/user.ts +0 -5316
  261. package/src/userConfig.ts +0 -38
  262. package/src/userMap/PollingSubscription.ts +0 -61
  263. package/src/userMap/WebsocketSubscription.ts +0 -93
  264. package/src/userMap/grpcSubscription.ts +0 -93
  265. package/src/userMap/referrerMap.ts +0 -360
  266. package/src/userMap/revenueShareEscrowMap.ts +0 -342
  267. package/src/userMap/userMap.ts +0 -784
  268. package/src/userMap/userMapConfig.ts +0 -82
  269. package/src/userMap/userStatsMap.ts +0 -393
  270. package/src/userName.ts +0 -37
  271. package/src/userStats.ts +0 -159
  272. package/src/userStatsConfig.ts +0 -35
  273. package/src/util/TransactionConfirmationManager.ts +0 -333
  274. package/src/util/chainClock.ts +0 -65
  275. package/src/util/computeUnits.ts +0 -101
  276. package/src/util/digest.ts +0 -33
  277. package/src/util/ed25519Utils.ts +0 -95
  278. package/src/util/promiseTimeout.ts +0 -23
  279. package/src/velocityClient.ts +0 -14802
  280. package/src/velocityClientConfig.ts +0 -119
  281. package/src/wallet.ts +0 -91
  282. package/tests/VelocityCore/builder_escrow.test.ts +0 -121
  283. package/tests/VelocityCore/decode.test.ts +0 -16
  284. package/tests/VelocityCore/fill_trigger.test.ts +0 -73
  285. package/tests/VelocityCore/instructions.test.ts +0 -46
  286. package/tests/VelocityCore/pdas.test.ts +0 -30
  287. package/tests/VelocityCore/perp_orders.test.ts +0 -205
  288. package/tests/VelocityCore/remainingAccounts.test.ts +0 -72
  289. package/tests/VelocityCore/settlement_liquidation.test.ts +0 -69
  290. package/tests/accounts/customizedCadenceBulkAccountLoader.test.ts +0 -211
  291. package/tests/amm/test.ts +0 -2402
  292. package/tests/auctions/test.ts +0 -81
  293. package/tests/bn/test.ts +0 -355
  294. package/tests/builder/builderFee.test.ts +0 -42
  295. package/tests/ci/verifyConstants.ts +0 -393
  296. package/tests/decode/test.ts +0 -262
  297. package/tests/decode/userAccountBufferStrings.ts +0 -102
  298. package/tests/dlob/helpers.ts +0 -748
  299. package/tests/dlob/test.ts +0 -6969
  300. package/tests/dlob/tickSizeStandardization.ts +0 -545
  301. package/tests/events/parseLogsForCuUsage.ts +0 -139
  302. package/tests/exchangeStatus/test.ts +0 -45
  303. package/tests/insurance/test.ts +0 -40
  304. package/tests/liquidation/test.ts +0 -125
  305. package/tests/oracles/mmOracleGate.test.ts +0 -379
  306. package/tests/oracles/pythPegSnap.test.ts +0 -76
  307. package/tests/sdkParity/enumParity.test.ts +0 -84
  308. package/tests/sdkParity/marginCategoryFill.test.ts +0 -143
  309. package/tests/sdkParity/memcmpOffsets.test.ts +0 -139
  310. package/tests/spot/test.ts +0 -281
  311. package/tests/tx/TransactionConfirmationManager.test.ts +0 -312
  312. package/tests/tx/cachedBlockhashFetcher.test.ts +0 -96
  313. package/tests/tx/priorityFeeCalculator.ts +0 -77
  314. package/tests/tx/priorityFeeStrategy.ts +0 -95
  315. package/tests/user/bankruptcy.ts +0 -165
  316. package/tests/user/feeAndWithdrawLimits.ts +0 -284
  317. package/tests/user/getMarginCalculation.ts +0 -586
  318. package/tests/user/helpers.ts +0 -184
  319. package/tests/user/liquidations.ts +0 -129
  320. package/tests/user/marginCalculations.test.ts +0 -321
  321. package/tests/user/test.ts +0 -833
  322. package/tsconfig.browser.json +0 -18
  323. package/tsconfig.json +0 -36
@@ -1,865 +0,0 @@
1
- import {
2
- AccountSubscriber,
3
- DataAndSlot,
4
- DelistedMarketSetting,
5
- VelocityClientAccountEvents,
6
- VelocityClientAccountSubscriber,
7
- NotSubscribedError,
8
- ResubOpts,
9
- } from './types';
10
- import { PerpMarketAccount, SpotMarketAccount, StateAccount } from '../types';
11
- import StrictEventEmitter from 'strict-event-emitter-types';
12
- import { EventEmitter } from 'events';
13
- import {
14
- getVelocityStateAccountPublicKey,
15
- getPerpMarketPublicKey,
16
- getPerpMarketPublicKeySync,
17
- getSpotMarketPublicKey,
18
- getSpotMarketPublicKeySync,
19
- } from '../addresses/pda';
20
- import { WebSocketAccountSubscriber } from './webSocketAccountSubscriber';
21
- import { AccountInfo, Commitment, PublicKey } from '@solana/web3.js';
22
- import { OracleInfo, OraclePriceData } from '../oracles/types';
23
- import { OracleClientCache } from '../oracles/oracleClientCache';
24
- import * as Buffer from 'buffer';
25
- import { QUOTE_ORACLE_PRICE_DATA } from '../oracles/quoteAssetOracleClient';
26
- import { findAllMarketAndOracles, VelocityProgram } from '../config';
27
- import { findDelistedPerpMarketsAndOracles } from './utils';
28
- import { getOracleId } from '../oracles/oracleId';
29
- import { OracleSource } from '../types';
30
- import { WebSocketAccountSubscriberV2 } from './webSocketAccountSubscriberV2';
31
-
32
- const ORACLE_DEFAULT_ID = getOracleId(
33
- PublicKey.default,
34
- OracleSource.QUOTE_ASSET
35
- );
36
-
37
- async function ensureAccountFetched<T>(
38
- subscriber: AccountSubscriber<T>,
39
- maxAttempts = 5,
40
- delayMs = 200
41
- ): Promise<void> {
42
- for (let attempt = 0; attempt < maxAttempts; attempt++) {
43
- if (subscriber.dataAndSlot?.data !== undefined) {
44
- return;
45
- }
46
- try {
47
- await subscriber.fetch();
48
- } catch {
49
- // retry below
50
- }
51
- if (subscriber.dataAndSlot?.data !== undefined) {
52
- return;
53
- }
54
- await new Promise((resolve) => setTimeout(resolve, delayMs));
55
- }
56
- }
57
-
58
- /**
59
- * Default `VelocityClientAccountSubscriber` implementation: creates one `WebSocketAccountSubscriber`
60
- * per tracked `State`/`PerpMarket`/`SpotMarket`/oracle account, giving the lowest update latency
61
- * at the cost of one WebSocket subscription per account. Before subscribing each per-account
62
- * subscriber, `setInitialData()` batch-fetches all of them via `getMultipleAccountsInfo` so the
63
- * per-account `WebSocketAccountSubscriber.subscribe()` calls can seed from that data instead of
64
- * each issuing its own `getAccountInfo` round trip. `customPerpMarketAccountSubscriber`/
65
- * `customOracleAccountSubscriber` let a caller substitute `WebSocketAccountSubscriberV2` (or any
66
- * other `AccountSubscriber` implementation) for those two account types specifically.
67
- */
68
- export class WebSocketVelocityClientAccountSubscriber
69
- implements VelocityClientAccountSubscriber
70
- {
71
- isSubscribed: boolean;
72
- program: VelocityProgram;
73
- commitment?: Commitment;
74
- perpMarketIndexes: number[];
75
- spotMarketIndexes: number[];
76
- oracleInfos: OracleInfo[];
77
- oracleClientCache = new OracleClientCache();
78
-
79
- resubOpts?: ResubOpts;
80
- shouldFindAllMarketsAndOracles: boolean;
81
-
82
- eventEmitter: StrictEventEmitter<EventEmitter, VelocityClientAccountEvents>;
83
- stateAccountSubscriber?: AccountSubscriber<StateAccount>;
84
- perpMarketAccountSubscribers = new Map<
85
- number,
86
- AccountSubscriber<PerpMarketAccount>
87
- >();
88
- perpOracleMap = new Map<number, PublicKey>();
89
- perpOracleStringMap = new Map<number, string>();
90
- spotMarketAccountSubscribers = new Map<
91
- number,
92
- AccountSubscriber<SpotMarketAccount>
93
- >();
94
- spotOracleMap = new Map<number, PublicKey>();
95
- spotOracleStringMap = new Map<number, string>();
96
- oracleSubscribers = new Map<string, AccountSubscriber<OraclePriceData>>();
97
- delistedMarketSetting: DelistedMarketSetting;
98
-
99
- initialPerpMarketAccountData?: Map<number, PerpMarketAccount>;
100
- initialSpotMarketAccountData?: Map<number, SpotMarketAccount>;
101
- initialOraclePriceData?: Map<string, OraclePriceData>;
102
- customPerpMarketAccountSubscriber?: new (
103
- accountName: string,
104
- program: VelocityProgram,
105
- accountPublicKey: PublicKey,
106
- decodeBuffer?: (buffer: Buffer) => any,
107
- resubOpts?: ResubOpts,
108
- commitment?: Commitment
109
- ) => AccountSubscriber<any>;
110
- customOracleAccountSubscriber?: new (
111
- accountName: string,
112
- program: VelocityProgram,
113
- accountPublicKey: PublicKey,
114
- decodeBuffer?: (buffer: Buffer) => any,
115
- resubOpts?: ResubOpts,
116
- commitment?: Commitment
117
- ) => AccountSubscriber<any>;
118
-
119
- protected isSubscribing = false;
120
- protected subscriptionPromiseResolver: (val: boolean) => void = () => {};
121
- protected subscriptionPromise: Promise<boolean> = Promise.resolve(false);
122
-
123
- /**
124
- * @param program Anchor program used to derive PDAs, decode accounts, and resolve oracle clients.
125
- * @param perpMarketIndexes Perp market indexes to track, if `shouldFindAllMarketsAndOracles` is false.
126
- * @param spotMarketIndexes Spot market indexes to track, if `shouldFindAllMarketsAndOracles` is false.
127
- * @param oracleInfos Oracles to track up front, if `shouldFindAllMarketsAndOracles` is false.
128
- * @param shouldFindAllMarketsAndOracles If true, `subscribe()` first discovers every market/oracle from on-chain state, ignoring the index/info args above.
129
- * @param delistedMarketSetting Behavior applied to delisted perp markets/oracles after subscribing; see `DelistedMarketSetting`.
130
- * @param resubOpts Resubscription watchdog options passed to every per-account `WebSocketAccountSubscriber`.
131
- * @param commitment Commitment for every per-account subscription; defaults to the provider's configured commitment.
132
- * @param customPerpMarketAccountSubscriber Optional alternate `AccountSubscriber` constructor (e.g. `WebSocketAccountSubscriberV2`) used for perp market accounts instead of the default `WebSocketAccountSubscriber`.
133
- * @param customOracleAccountSubscriber Optional alternate `AccountSubscriber` constructor used for oracle accounts instead of the default `WebSocketAccountSubscriber`.
134
- */
135
- public constructor(
136
- program: VelocityProgram,
137
- perpMarketIndexes: number[],
138
- spotMarketIndexes: number[],
139
- oracleInfos: OracleInfo[],
140
- shouldFindAllMarketsAndOracles: boolean,
141
- delistedMarketSetting: DelistedMarketSetting,
142
- resubOpts?: ResubOpts,
143
- commitment?: Commitment,
144
- customPerpMarketAccountSubscriber?: new (
145
- accountName: string,
146
- program: VelocityProgram,
147
- accountPublicKey: PublicKey,
148
- decodeBuffer?: (buffer: Buffer) => any,
149
- resubOpts?: ResubOpts,
150
- commitment?: Commitment
151
- ) => WebSocketAccountSubscriberV2<any> | WebSocketAccountSubscriber<any>,
152
- customOracleAccountSubscriber?: new (
153
- accountName: string,
154
- program: VelocityProgram,
155
- accountPublicKey: PublicKey,
156
- decodeBuffer?: (buffer: Buffer) => any,
157
- resubOpts?: ResubOpts,
158
- commitment?: Commitment
159
- ) => WebSocketAccountSubscriberV2<any> | WebSocketAccountSubscriber<any>
160
- ) {
161
- this.isSubscribed = false;
162
- this.program = program;
163
- this.eventEmitter = new EventEmitter();
164
- this.perpMarketIndexes = perpMarketIndexes;
165
- this.spotMarketIndexes = spotMarketIndexes;
166
- this.oracleInfos = oracleInfos;
167
- this.shouldFindAllMarketsAndOracles = shouldFindAllMarketsAndOracles;
168
- this.delistedMarketSetting = delistedMarketSetting;
169
- this.resubOpts = resubOpts;
170
- this.commitment = commitment;
171
- this.customPerpMarketAccountSubscriber = customPerpMarketAccountSubscriber;
172
- this.customOracleAccountSubscriber = customOracleAccountSubscriber;
173
- }
174
-
175
- /**
176
- * Subscribes the `State` account, batch-seeds all market/oracle accounts via `setInitialData()`,
177
- * then subscribes every per-account `WebSocketAccountSubscriber` (or the custom subscriber
178
- * class, if configured) for perp markets, spot markets, and oracles. Applies
179
- * `delistedMarketSetting` afterward. Idempotent: a no-op if already subscribed, and concurrent
180
- * calls while a subscribe is in flight share the same result via `subscriptionPromise` rather
181
- * than issuing duplicate subscriptions. Always resolves `true` (no retry/failure path — a
182
- * per-account fetch failure surfaces via `ensureAccountFetched`'s retries, not by aborting
183
- * subscribe).
184
- */
185
- public async subscribe(): Promise<boolean> {
186
- if (this.isSubscribed) {
187
- return true;
188
- }
189
-
190
- if (this.isSubscribing) {
191
- return await this.subscriptionPromise;
192
- }
193
-
194
- this.isSubscribing = true;
195
-
196
- this.subscriptionPromise = new Promise((res) => {
197
- this.subscriptionPromiseResolver = res;
198
- });
199
-
200
- if (this.shouldFindAllMarketsAndOracles) {
201
- const {
202
- perpMarketIndexes,
203
- perpMarketAccounts,
204
- spotMarketIndexes,
205
- spotMarketAccounts,
206
- oracleInfos,
207
- } = await findAllMarketAndOracles(this.program);
208
- this.perpMarketIndexes = perpMarketIndexes;
209
- this.spotMarketIndexes = spotMarketIndexes;
210
- this.oracleInfos = oracleInfos;
211
- // front run and set the initial data here to save extra gma call in set initial data
212
- this.initialPerpMarketAccountData = new Map(
213
- perpMarketAccounts.map((market) => [market.marketIndex, market])
214
- );
215
- this.initialSpotMarketAccountData = new Map(
216
- spotMarketAccounts.map((market) => [market.marketIndex, market])
217
- );
218
- }
219
-
220
- const statePublicKey = await getVelocityStateAccountPublicKey(
221
- this.program.programId
222
- );
223
-
224
- // create and activate main state account subscription
225
- this.stateAccountSubscriber = new WebSocketAccountSubscriber(
226
- 'state',
227
- this.program,
228
- statePublicKey,
229
- undefined,
230
- undefined,
231
- this.commitment
232
- );
233
- await this.stateAccountSubscriber.subscribe((data: StateAccount) => {
234
- this.eventEmitter.emit('stateAccountUpdate', data);
235
- this.eventEmitter.emit('update');
236
- });
237
-
238
- // set initial data to avoid spamming getAccountInfo calls in webSocketAccountSubscriber
239
- await this.setInitialData();
240
-
241
- await Promise.all([
242
- // subscribe to market accounts
243
- this.subscribeToPerpMarketAccounts(),
244
- // subscribe to spot market accounts
245
- this.subscribeToSpotMarketAccounts(),
246
- // subscribe to oracles
247
- this.subscribeToOracles(),
248
- ]);
249
-
250
- this.eventEmitter.emit('update');
251
-
252
- await this.handleDelistedMarkets();
253
-
254
- await Promise.all([this.setPerpOracleMap(), this.setSpotOracleMap()]);
255
-
256
- this.isSubscribing = false;
257
- this.isSubscribed = true;
258
- this.subscriptionPromiseResolver(true);
259
-
260
- // delete initial data
261
- this.removeInitialData();
262
-
263
- return true;
264
- }
265
-
266
- chunks = <T>(array: readonly T[], size: number): T[][] => {
267
- return new Array(Math.ceil(array.length / size))
268
- .fill(null)
269
- .map((_, index) => index * size)
270
- .map((begin) => array.slice(begin, begin + size));
271
- };
272
-
273
- /**
274
- * Batch-fetches every tracked perp market, spot market, and oracle account via chunked
275
- * `getMultipleAccountsInfo` calls (75 pubkeys per chunk) and stashes the decoded results in
276
- * `initialPerpMarketAccountData`/`initialSpotMarketAccountData`/`initialOraclePriceData`.
277
- * Each per-account `WebSocketAccountSubscriber.subscribe()` call seeds from this data via
278
- * `setData` instead of issuing its own `getAccountInfo` round trip. Skips markets already
279
- * populated (e.g. by `findAllMarketAndOracles` when `shouldFindAllMarketsAndOracles` is set).
280
- */
281
- async setInitialData(): Promise<void> {
282
- const connection = this.program.provider.connection;
283
-
284
- if (!this.initialPerpMarketAccountData) {
285
- const perpMarketPublicKeys = this.perpMarketIndexes.map((marketIndex) =>
286
- getPerpMarketPublicKeySync(this.program.programId, marketIndex)
287
- );
288
- const perpMarketPublicKeysChunks = this.chunks(perpMarketPublicKeys, 75);
289
- const perpMarketAccountInfos = (
290
- await Promise.all(
291
- perpMarketPublicKeysChunks.map((perpMarketPublicKeysChunk) =>
292
- connection.getMultipleAccountsInfo(perpMarketPublicKeysChunk)
293
- )
294
- )
295
- ).flat();
296
- this.initialPerpMarketAccountData = new Map(
297
- perpMarketAccountInfos
298
- .filter(
299
- (accountInfo): accountInfo is AccountInfo<Buffer> => !!accountInfo
300
- )
301
- .map((accountInfo) => {
302
- const perpMarket = this.program.coder.accounts.decode(
303
- 'perpMarket',
304
- accountInfo.data
305
- );
306
- return [perpMarket.marketIndex, perpMarket];
307
- })
308
- );
309
- }
310
-
311
- if (!this.initialSpotMarketAccountData) {
312
- const spotMarketPublicKeys = this.spotMarketIndexes.map((marketIndex) =>
313
- getSpotMarketPublicKeySync(this.program.programId, marketIndex)
314
- );
315
- const spotMarketPublicKeysChunks = this.chunks(spotMarketPublicKeys, 75);
316
- const spotMarketAccountInfos = (
317
- await Promise.all(
318
- spotMarketPublicKeysChunks.map((spotMarketPublicKeysChunk) =>
319
- connection.getMultipleAccountsInfo(spotMarketPublicKeysChunk)
320
- )
321
- )
322
- ).flat();
323
- this.initialSpotMarketAccountData = new Map(
324
- spotMarketAccountInfos
325
- .filter(
326
- (accountInfo): accountInfo is AccountInfo<Buffer> => !!accountInfo
327
- )
328
- .map((accountInfo) => {
329
- const spotMarket = this.program.coder.accounts.decode(
330
- 'spotMarket',
331
- accountInfo.data
332
- );
333
- return [spotMarket.marketIndex, spotMarket];
334
- })
335
- );
336
- }
337
-
338
- const oracleAccountPubkeyChunks = this.chunks(
339
- this.oracleInfos.map((oracleInfo) => oracleInfo.publicKey),
340
- 75
341
- );
342
- const oracleAccountInfos = (
343
- await Promise.all(
344
- oracleAccountPubkeyChunks.map((oracleAccountPublicKeysChunk) =>
345
- connection.getMultipleAccountsInfo(oracleAccountPublicKeysChunk)
346
- )
347
- )
348
- ).flat();
349
- this.initialOraclePriceData = new Map(
350
- this.oracleInfos.reduce(
351
- (result, oracleInfo, i) => {
352
- const oracleAccountInfo = oracleAccountInfos[i];
353
- if (!oracleAccountInfo) {
354
- return result;
355
- }
356
-
357
- const oracleClient = this.oracleClientCache.get(
358
- oracleInfo.source,
359
- connection,
360
- this.program
361
- );
362
- if (!oracleClient) {
363
- return result;
364
- }
365
-
366
- const oraclePriceData = oracleClient.getOraclePriceDataFromBuffer(
367
- oracleAccountInfo.data
368
- );
369
-
370
- result.push([
371
- getOracleId(oracleInfo.publicKey, oracleInfo.source),
372
- oraclePriceData,
373
- ]);
374
- return result;
375
- },
376
- [] as [string, OraclePriceData][]
377
- )
378
- );
379
- }
380
-
381
- /** Clears the seed data stashed by `setInitialData()` once every per-account subscriber has consumed it, freeing the memory. */
382
- removeInitialData() {
383
- this.initialPerpMarketAccountData = new Map();
384
- this.initialSpotMarketAccountData = new Map();
385
- this.initialOraclePriceData = new Map();
386
- }
387
-
388
- /** Subscribes a `WebSocketAccountSubscriber` for every tracked perp market index, in parallel. */
389
- async subscribeToPerpMarketAccounts(): Promise<boolean> {
390
- await Promise.all(
391
- this.perpMarketIndexes.map((marketIndex) =>
392
- this.subscribeToPerpMarketAccount(marketIndex)
393
- )
394
- );
395
- return true;
396
- }
397
-
398
- /**
399
- * Creates and subscribes the per-account subscriber (custom class if `customPerpMarketAccountSubscriber`
400
- * is set, else `WebSocketAccountSubscriber`) for one perp market, seeding it from
401
- * `initialPerpMarketAccountData` if available, and waits (via `ensureAccountFetched`, up to 5
402
- * retries with a 200ms backoff) for data to actually land before returning.
403
- * @param marketIndex Perp market index to subscribe.
404
- */
405
- async subscribeToPerpMarketAccount(marketIndex: number): Promise<boolean> {
406
- const perpMarketPublicKey = await getPerpMarketPublicKey(
407
- this.program.programId,
408
- marketIndex
409
- );
410
- const AccountSubscriberClass =
411
- this.customPerpMarketAccountSubscriber || WebSocketAccountSubscriber;
412
- const accountSubscriber = new AccountSubscriberClass<PerpMarketAccount>(
413
- 'perpMarket',
414
- this.program,
415
- perpMarketPublicKey,
416
- undefined,
417
- this.resubOpts,
418
- this.commitment
419
- );
420
- const initialPerpMarketData =
421
- this.initialPerpMarketAccountData?.get(marketIndex);
422
- if (initialPerpMarketData) {
423
- accountSubscriber.setData(initialPerpMarketData);
424
- }
425
- await accountSubscriber.subscribe((data: PerpMarketAccount) => {
426
- this.eventEmitter.emit('perpMarketAccountUpdate', data);
427
- this.eventEmitter.emit('update');
428
- });
429
- await ensureAccountFetched(accountSubscriber);
430
- this.perpMarketAccountSubscribers.set(marketIndex, accountSubscriber);
431
- return true;
432
- }
433
-
434
- /** Subscribes a `WebSocketAccountSubscriber` for every tracked spot market index, in parallel. */
435
- async subscribeToSpotMarketAccounts(): Promise<boolean> {
436
- await Promise.all(
437
- this.spotMarketIndexes.map((marketIndex) =>
438
- this.subscribeToSpotMarketAccount(marketIndex)
439
- )
440
- );
441
- return true;
442
- }
443
-
444
- /**
445
- * Creates and subscribes a `WebSocketAccountSubscriber` for one spot market, seeding it from
446
- * `initialSpotMarketAccountData` if available, and waits (via `ensureAccountFetched`) for data
447
- * to land before returning.
448
- * @param marketIndex Spot market index to subscribe.
449
- */
450
- async subscribeToSpotMarketAccount(marketIndex: number): Promise<boolean> {
451
- const marketPublicKey = await getSpotMarketPublicKey(
452
- this.program.programId,
453
- marketIndex
454
- );
455
- const accountSubscriber = new WebSocketAccountSubscriber<SpotMarketAccount>(
456
- 'spotMarket',
457
- this.program,
458
- marketPublicKey,
459
- undefined,
460
- this.resubOpts,
461
- this.commitment
462
- );
463
- const initialSpotMarketData =
464
- this.initialSpotMarketAccountData?.get(marketIndex);
465
- if (initialSpotMarketData) {
466
- accountSubscriber.setData(initialSpotMarketData);
467
- }
468
- await accountSubscriber.subscribe((data: SpotMarketAccount) => {
469
- this.eventEmitter.emit('spotMarketAccountUpdate', data);
470
- this.eventEmitter.emit('update');
471
- });
472
- await ensureAccountFetched(accountSubscriber);
473
- this.spotMarketAccountSubscribers.set(marketIndex, accountSubscriber);
474
- return true;
475
- }
476
-
477
- /** Subscribes an account subscriber for every tracked oracle, in parallel, skipping the `PublicKey.default` (quote-asset "no oracle") sentinel. */
478
- async subscribeToOracles(): Promise<boolean> {
479
- await Promise.all(
480
- this.oracleInfos
481
- .filter((oracleInfo) => !oracleInfo.publicKey.equals(PublicKey.default))
482
- .map((oracleInfo) => this.subscribeToOracle(oracleInfo))
483
- );
484
-
485
- return true;
486
- }
487
-
488
- /**
489
- * Creates and subscribes the per-account subscriber (custom class if `customOracleAccountSubscriber`
490
- * is set, else `WebSocketAccountSubscriber`) for one oracle, decoding buffers with the
491
- * source-appropriate `OracleClient`. Seeds from `initialOraclePriceData` if available and
492
- * waits (via `ensureAccountFetched`) for data to land before returning.
493
- * @param oracleInfo Oracle pubkey and source to subscribe.
494
- * @returns `false` if no `OracleClient` is registered for `oracleInfo.source`; otherwise `true`.
495
- */
496
- async subscribeToOracle(oracleInfo: OracleInfo): Promise<boolean> {
497
- const oracleId = getOracleId(oracleInfo.publicKey, oracleInfo.source);
498
- const client = this.oracleClientCache.get(
499
- oracleInfo.source,
500
- this.program.provider.connection,
501
- this.program
502
- );
503
- if (!client) {
504
- return false;
505
- }
506
- const AccountSubscriberClass =
507
- this.customOracleAccountSubscriber || WebSocketAccountSubscriber;
508
- const accountSubscriber = new AccountSubscriberClass<OraclePriceData>(
509
- 'oracle',
510
- this.program,
511
- oracleInfo.publicKey,
512
- (buffer: Buffer) => {
513
- return client.getOraclePriceDataFromBuffer(buffer);
514
- },
515
- this.resubOpts,
516
- this.commitment
517
- );
518
- const initialOraclePriceData = this.initialOraclePriceData?.get(oracleId);
519
- if (initialOraclePriceData) {
520
- accountSubscriber.setData(initialOraclePriceData);
521
- }
522
- await accountSubscriber.subscribe((data: OraclePriceData) => {
523
- this.eventEmitter.emit(
524
- 'oraclePriceUpdate',
525
- oracleInfo.publicKey,
526
- oracleInfo.source,
527
- data
528
- );
529
- this.eventEmitter.emit('update');
530
- });
531
- await ensureAccountFetched(accountSubscriber);
532
-
533
- this.oracleSubscribers.set(oracleId, accountSubscriber);
534
- return true;
535
- }
536
-
537
- /** Unsubscribes every per-account subscriber tracking a perp market, in parallel. */
538
- async unsubscribeFromMarketAccounts(): Promise<void> {
539
- await Promise.all(
540
- Array.from(this.perpMarketAccountSubscribers.values()).map(
541
- (accountSubscriber) => accountSubscriber.unsubscribe()
542
- )
543
- );
544
- }
545
-
546
- /** Unsubscribes every per-account subscriber tracking a spot market, in parallel. */
547
- async unsubscribeFromSpotMarketAccounts(): Promise<void> {
548
- await Promise.all(
549
- Array.from(this.spotMarketAccountSubscribers.values()).map(
550
- (accountSubscriber) => accountSubscriber.unsubscribe()
551
- )
552
- );
553
- }
554
-
555
- /** Unsubscribes every per-account subscriber tracking an oracle, in parallel. */
556
- async unsubscribeFromOracles(): Promise<void> {
557
- await Promise.all(
558
- Array.from(this.oracleSubscribers.values()).map((accountSubscriber) =>
559
- accountSubscriber.unsubscribe()
560
- )
561
- );
562
- }
563
-
564
- /** Fetches the `State` account and every subscribed perp/spot market's `WebSocketAccountSubscriber` in parallel. A no-op if not subscribed. Oracle accounts are not re-fetched here (they update via their own WS notifications). */
565
- public async fetch(): Promise<void> {
566
- if (!this.isSubscribed) {
567
- return;
568
- }
569
-
570
- if (!this.stateAccountSubscriber) {
571
- return;
572
- }
573
-
574
- const promises = [this.stateAccountSubscriber.fetch()]
575
- .concat(
576
- Array.from(this.perpMarketAccountSubscribers.values()).map(
577
- (subscriber) => subscriber.fetch()
578
- )
579
- )
580
- .concat(
581
- Array.from(this.spotMarketAccountSubscribers.values()).map(
582
- (subscriber) => subscriber.fetch()
583
- )
584
- );
585
-
586
- await Promise.all(promises);
587
- }
588
-
589
- /** Tears down the `State` subscriber and every per-account market/oracle subscriber. A no-op if not subscribed. */
590
- public async unsubscribe(): Promise<void> {
591
- if (!this.isSubscribed) {
592
- return;
593
- }
594
-
595
- await this.stateAccountSubscriber?.unsubscribe();
596
-
597
- await this.unsubscribeFromMarketAccounts();
598
- await this.unsubscribeFromSpotMarketAccounts();
599
- await this.unsubscribeFromOracles();
600
-
601
- this.isSubscribed = false;
602
- }
603
-
604
- /**
605
- * Adds a spot market (and its oracle, via `setSpotOracleMap`) to be tracked, subscribing a new
606
- * `WebSocketAccountSubscriber` for it. Idempotent: returns `true` immediately if already tracked.
607
- * @param marketIndex Spot market index to start tracking.
608
- */
609
- async addSpotMarket(marketIndex: number): Promise<boolean> {
610
- if (this.spotMarketAccountSubscribers.has(marketIndex)) {
611
- return true;
612
- }
613
- const subscriptionSuccess = await this.subscribeToSpotMarketAccount(
614
- marketIndex
615
- );
616
- await this.setSpotOracleMap();
617
- return subscriptionSuccess;
618
- }
619
-
620
- /**
621
- * Adds a perp market (and its oracle, via `setPerpOracleMap`) to be tracked, subscribing a new
622
- * `WebSocketAccountSubscriber` for it. Idempotent: returns `true` immediately if already tracked.
623
- * @param marketIndex Perp market index to start tracking.
624
- */
625
- async addPerpMarket(marketIndex: number): Promise<boolean> {
626
- if (this.perpMarketAccountSubscribers.has(marketIndex)) {
627
- return true;
628
- }
629
- const subscriptionSuccess = await this.subscribeToPerpMarketAccount(
630
- marketIndex
631
- );
632
- await this.setPerpOracleMap();
633
- return subscriptionSuccess;
634
- }
635
-
636
- /**
637
- * Adds an oracle to be tracked, subscribing a new per-account subscriber for it. A no-op that
638
- * resolves `true` immediately for the `PublicKey.default` sentinel (quote-asset "no oracle")
639
- * or an oracle already tracked.
640
- * @param oracleInfo Oracle pubkey and source to start tracking.
641
- */
642
- async addOracle(oracleInfo: OracleInfo): Promise<boolean> {
643
- const oracleId = getOracleId(oracleInfo.publicKey, oracleInfo.source);
644
- if (this.oracleSubscribers.has(oracleId)) {
645
- return true;
646
- }
647
-
648
- if (oracleInfo.publicKey.equals(PublicKey.default)) {
649
- return true;
650
- }
651
-
652
- return this.subscribeToOracle(oracleInfo);
653
- }
654
-
655
- /** Rebuilds `perpOracleMap`/`perpOracleStringMap` from currently cached perp markets, calling `addOracle` for any oracle not yet tracked. */
656
- async setPerpOracleMap() {
657
- const perpMarkets = this.getMarketAccountsAndSlots();
658
- const addOraclePromises = [];
659
- for (const perpMarket of perpMarkets) {
660
- if (!perpMarket || !perpMarket.data) {
661
- continue;
662
- }
663
- const perpMarketAccount = perpMarket.data;
664
- const perpMarketIndex = perpMarketAccount.marketIndex;
665
- const oracle = perpMarketAccount.oracle;
666
- const oracleId = getOracleId(oracle, perpMarket.data.oracleSource);
667
- if (!this.oracleSubscribers.has(oracleId)) {
668
- addOraclePromises.push(
669
- this.addOracle({
670
- publicKey: oracle,
671
- source: perpMarket.data.oracleSource,
672
- })
673
- );
674
- }
675
- this.perpOracleMap.set(perpMarketIndex, oracle);
676
- this.perpOracleStringMap.set(perpMarketIndex, oracleId);
677
- }
678
- await Promise.all(addOraclePromises);
679
- }
680
-
681
- /** Rebuilds `spotOracleMap`/`spotOracleStringMap` from currently cached spot markets, calling `addOracle` for any oracle not yet tracked. */
682
- async setSpotOracleMap() {
683
- const spotMarkets = this.getSpotMarketAccountsAndSlots();
684
- const addOraclePromises = [];
685
- for (const spotMarket of spotMarkets) {
686
- if (!spotMarket || !spotMarket.data) {
687
- continue;
688
- }
689
- const spotMarketAccount = spotMarket.data;
690
- const spotMarketIndex = spotMarketAccount.marketIndex;
691
- const oracle = spotMarketAccount.oracle;
692
- const oracleId = getOracleId(oracle, spotMarketAccount.oracleSource);
693
- if (!this.oracleSubscribers.has(oracleId)) {
694
- addOraclePromises.push(
695
- this.addOracle({
696
- publicKey: oracle,
697
- source: spotMarketAccount.oracleSource,
698
- })
699
- );
700
- }
701
- this.spotOracleMap.set(spotMarketIndex, oracle);
702
- this.spotOracleStringMap.set(spotMarketIndex, oracleId);
703
- }
704
- await Promise.all(addOraclePromises);
705
- }
706
-
707
- /**
708
- * Applies `delistedMarketSetting` to any perp market currently `status: delisted` (and its
709
- * oracle, if not shared with a live spot market): unsubscribes the per-account subscriber, and
710
- * additionally drops it from `perpMarketAccountSubscribers`/`oracleSubscribers` if the setting
711
- * is `Discard`. A no-op if the setting is `Subscribe`.
712
- */
713
- async handleDelistedMarkets(): Promise<void> {
714
- if (this.delistedMarketSetting === DelistedMarketSetting.Subscribe) {
715
- return;
716
- }
717
-
718
- const { perpMarketIndexes, oracles } = findDelistedPerpMarketsAndOracles(
719
- this.getMarketAccountsAndSlots(),
720
- this.getSpotMarketAccountsAndSlots()
721
- );
722
-
723
- for (const perpMarketIndex of perpMarketIndexes) {
724
- await this.perpMarketAccountSubscribers
725
- .get(perpMarketIndex)
726
- ?.unsubscribe();
727
- if (this.delistedMarketSetting === DelistedMarketSetting.Discard) {
728
- this.perpMarketAccountSubscribers.delete(perpMarketIndex);
729
- }
730
- }
731
-
732
- for (const oracle of oracles) {
733
- const oracleId = getOracleId(oracle.publicKey, oracle.source);
734
- await this.oracleSubscribers.get(oracleId)?.unsubscribe();
735
- if (this.delistedMarketSetting === DelistedMarketSetting.Discard) {
736
- this.oracleSubscribers.delete(oracleId);
737
- }
738
- }
739
- }
740
-
741
- /** Throws `NotSubscribedError` if `subscribe()` has not been called. */
742
- assertIsSubscribed(): void {
743
- if (!this.isSubscribed) {
744
- throw new NotSubscribedError(
745
- 'You must call `subscribe` before using this function'
746
- );
747
- }
748
- }
749
-
750
- /** Throws `NotSubscribedError` if not subscribed, or a generic `Error` if subscribed but the `State` account hasn't loaded yet (should not normally happen after `subscribe()` resolves). */
751
- public getStateAccountAndSlot(): DataAndSlot<StateAccount> {
752
- this.assertIsSubscribed();
753
- const dataAndSlot = this.stateAccountSubscriber?.dataAndSlot;
754
- if (!dataAndSlot) {
755
- throw new Error('State account data not available');
756
- }
757
- return dataAndSlot;
758
- }
759
-
760
- /** Throws `NotSubscribedError` if not subscribed. Returns undefined if `marketIndex` isn't tracked (or hasn't loaded yet). */
761
- public getMarketAccountAndSlot(
762
- marketIndex: number
763
- ): DataAndSlot<PerpMarketAccount> | undefined {
764
- this.assertIsSubscribed();
765
- return this.perpMarketAccountSubscribers.get(marketIndex)?.dataAndSlot;
766
- }
767
-
768
- /** Returns every currently cached (loaded) perp market. Does not throw `NotSubscribedError`. */
769
- public getMarketAccountsAndSlots(): DataAndSlot<PerpMarketAccount>[] {
770
- return Array.from(this.perpMarketAccountSubscribers.values())
771
- .map((subscriber) => subscriber.dataAndSlot)
772
- .filter(
773
- (dataAndSlot): dataAndSlot is DataAndSlot<PerpMarketAccount> =>
774
- dataAndSlot !== undefined
775
- );
776
- }
777
-
778
- /** Throws `NotSubscribedError` if not subscribed. Returns undefined if `marketIndex` isn't tracked (or hasn't loaded yet). */
779
- public getSpotMarketAccountAndSlot(
780
- marketIndex: number
781
- ): DataAndSlot<SpotMarketAccount> | undefined {
782
- this.assertIsSubscribed();
783
- return this.spotMarketAccountSubscribers.get(marketIndex)?.dataAndSlot;
784
- }
785
-
786
- /** Returns every currently cached (loaded) spot market. Does not throw `NotSubscribedError`. */
787
- public getSpotMarketAccountsAndSlots(): DataAndSlot<SpotMarketAccount>[] {
788
- return Array.from(this.spotMarketAccountSubscribers.values())
789
- .map((subscriber) => subscriber.dataAndSlot)
790
- .filter(
791
- (dataAndSlot): dataAndSlot is DataAndSlot<SpotMarketAccount> =>
792
- dataAndSlot !== undefined
793
- );
794
- }
795
-
796
- /**
797
- * Looks up cached oracle price data by oracle id (see `getOracleId`). Special-cases the
798
- * quote-asset default oracle id, returning the constant `QUOTE_ORACLE_PRICE_DATA` at slot 0
799
- * rather than a subscriber lookup, since that oracle is never actually subscribed to.
800
- * @param oracleId Oracle id string from `getOracleId(publicKey, source)`.
801
- * @returns Cached price data/slot, or undefined if not tracked. Throws `NotSubscribedError` if not subscribed.
802
- */
803
- public getOraclePriceDataAndSlot(
804
- oracleId: string
805
- ): DataAndSlot<OraclePriceData> | undefined {
806
- this.assertIsSubscribed();
807
- if (oracleId === ORACLE_DEFAULT_ID) {
808
- return {
809
- data: QUOTE_ORACLE_PRICE_DATA,
810
- slot: 0,
811
- };
812
- }
813
- return this.oracleSubscribers.get(oracleId)?.dataAndSlot;
814
- }
815
-
816
- /**
817
- * Convenience lookup: resolves the oracle price data currently mapped to a perp market's
818
- * oracle. If the cached market's oracle pubkey has drifted from (or isn't yet in)
819
- * `perpOracleMap`, triggers a background `setPerpOracleMap()` refresh and still returns the
820
- * (possibly stale) mapping for this call.
821
- * @param marketIndex Perp market index whose oracle price to look up.
822
- */
823
- public getOraclePriceDataAndSlotForPerpMarket(
824
- marketIndex: number
825
- ): DataAndSlot<OraclePriceData> | undefined {
826
- const perpMarketAccount = this.getMarketAccountAndSlot(marketIndex);
827
- const oracle = this.perpOracleMap.get(marketIndex);
828
- const oracleId = this.perpOracleStringMap.get(marketIndex);
829
- if (!perpMarketAccount || !oracleId) {
830
- return undefined;
831
- }
832
-
833
- if (!oracle || !perpMarketAccount.data.oracle.equals(oracle)) {
834
- // If the oracle has changed (or not yet cached), update the oracle map in background
835
- this.setPerpOracleMap();
836
- }
837
-
838
- return this.getOraclePriceDataAndSlot(oracleId);
839
- }
840
-
841
- /**
842
- * Convenience lookup: resolves the oracle price data currently mapped to a spot market's
843
- * oracle. If the cached market's oracle pubkey has drifted from (or isn't yet in)
844
- * `spotOracleMap`, triggers a background `setSpotOracleMap()` refresh and still returns the
845
- * (possibly stale) mapping for this call.
846
- * @param marketIndex Spot market index whose oracle price to look up.
847
- */
848
- public getOraclePriceDataAndSlotForSpotMarket(
849
- marketIndex: number
850
- ): DataAndSlot<OraclePriceData> | undefined {
851
- const spotMarketAccount = this.getSpotMarketAccountAndSlot(marketIndex);
852
- const oracle = this.spotOracleMap.get(marketIndex);
853
- const oracleId = this.spotOracleStringMap.get(marketIndex);
854
- if (!spotMarketAccount || !oracleId) {
855
- return undefined;
856
- }
857
-
858
- if (!oracle || !spotMarketAccount.data.oracle.equals(oracle)) {
859
- // If the oracle has changed (or not yet cached), update the oracle map in background
860
- this.setSpotOracleMap();
861
- }
862
-
863
- return this.getOraclePriceDataAndSlot(oracleId);
864
- }
865
- }