@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.
- package/README.md +3 -3
- package/lib/browser/adminClient.d.ts +35 -0
- package/lib/browser/adminClient.js +65 -0
- package/lib/browser/config.js +6 -7
- package/lib/browser/constants/numericConstants.d.ts +1 -1
- package/lib/browser/constants/numericConstants.js +1 -1
- package/lib/browser/decode/user.js +5 -1
- package/lib/browser/idl/velocity.d.ts +146 -3
- package/lib/browser/idl/velocity.json +147 -4
- package/lib/browser/orderSubscriber/OrderSubscriber.js +3 -0
- package/lib/browser/pyth/pythLazerSubscriber.js +37 -27
- package/lib/browser/types.d.ts +4 -0
- package/lib/browser/user.d.ts +15 -0
- package/lib/browser/user.js +37 -1
- package/lib/browser/velocityClient.d.ts +46 -10
- package/lib/browser/velocityClient.js +107 -35
- package/lib/node/adminClient.d.ts +35 -0
- package/lib/node/adminClient.d.ts.map +1 -1
- package/lib/node/adminClient.js +65 -0
- package/lib/node/config.d.ts.map +1 -1
- package/lib/node/config.js +6 -7
- package/lib/node/constants/numericConstants.d.ts +1 -1
- package/lib/node/constants/numericConstants.d.ts.map +1 -1
- package/lib/node/constants/numericConstants.js +1 -1
- package/lib/node/decode/user.d.ts.map +1 -1
- package/lib/node/decode/user.js +5 -1
- package/lib/node/idl/velocity.d.ts +146 -3
- package/lib/node/idl/velocity.d.ts.map +1 -1
- package/lib/node/idl/velocity.json +147 -4
- package/lib/node/orderSubscriber/OrderSubscriber.d.ts.map +1 -1
- package/lib/node/orderSubscriber/OrderSubscriber.js +3 -0
- package/lib/node/pyth/pythLazerSubscriber.d.ts.map +1 -1
- package/lib/node/pyth/pythLazerSubscriber.js +37 -27
- package/lib/node/types.d.ts +4 -0
- package/lib/node/types.d.ts.map +1 -1
- package/lib/node/user.d.ts +15 -0
- package/lib/node/user.d.ts.map +1 -1
- package/lib/node/user.js +37 -1
- package/lib/node/velocityClient.d.ts +46 -10
- package/lib/node/velocityClient.d.ts.map +1 -1
- package/lib/node/velocityClient.js +107 -35
- package/package.json +7 -3
- package/.prettierignore +0 -1
- package/CHANGELOG.md +0 -132
- package/build-browser.js +0 -58
- package/bunfig.toml +0 -3
- package/esbuild-shims.js +0 -12
- package/scripts/deposit-isolated-positions.ts +0 -110
- package/scripts/find-flagged-users.ts +0 -213
- package/scripts/grpc-client-test-comparison.ts +0 -372
- package/scripts/grpc-multiuser-client-test-comparison.ts +0 -158
- package/scripts/postbuild.js +0 -95
- package/scripts/single-grpc-client-test.ts +0 -284
- package/scripts/withdraw-isolated-positions.ts +0 -174
- package/src/accounts/README_WebSocketAccountSubscriberV2.md +0 -95
- package/src/accounts/README_WebSocketProgramAccountSubscriberV2.md +0 -135
- package/src/accounts/basicUserAccountSubscriber.ts +0 -79
- package/src/accounts/basicUserStatsAccountSubscriber.ts +0 -87
- package/src/accounts/bulkAccountLoader.ts +0 -358
- package/src/accounts/bulkUserStatsSubscription.ts +0 -40
- package/src/accounts/bulkUserSubscription.ts +0 -39
- package/src/accounts/customizedCadenceBulkAccountLoader.ts +0 -250
- package/src/accounts/fetch.ts +0 -200
- package/src/accounts/grpcAccountSubscriber.ts +0 -225
- package/src/accounts/grpcInsuranceFundStakeAccountSubscriber.ts +0 -73
- package/src/accounts/grpcMultiAccountSubscriber.ts +0 -566
- package/src/accounts/grpcMultiUserAccountSubscriber.ts +0 -328
- package/src/accounts/grpcProgramAccountSubscriber.ts +0 -264
- package/src/accounts/grpcUserAccountSubscriber.ts +0 -63
- package/src/accounts/grpcUserStatsAccountSubscriber.ts +0 -65
- package/src/accounts/grpcVelocityClientAccountSubscriber.ts +0 -257
- package/src/accounts/grpcVelocityClientAccountSubscriberV2.ts +0 -941
- package/src/accounts/laserProgramAccountSubscriber.ts +0 -266
- package/src/accounts/oneShotUserAccountSubscriber.ts +0 -88
- package/src/accounts/oneShotUserStatsAccountSubscriber.ts +0 -94
- package/src/accounts/pollingInsuranceFundStakeAccountSubscriber.ts +0 -223
- package/src/accounts/pollingOracleAccountSubscriber.ts +0 -151
- package/src/accounts/pollingTokenAccountSubscriber.ts +0 -147
- package/src/accounts/pollingUserAccountSubscriber.ts +0 -198
- package/src/accounts/pollingUserStatsAccountSubscriber.ts +0 -180
- package/src/accounts/pollingVelocityClientAccountSubscriber.ts +0 -774
- package/src/accounts/testBulkAccountLoader.ts +0 -59
- package/src/accounts/types.ts +0 -428
- package/src/accounts/utils.ts +0 -90
- package/src/accounts/webSocketAccountSubscriber.ts +0 -304
- package/src/accounts/webSocketAccountSubscriberV2.ts +0 -536
- package/src/accounts/webSocketInsuranceFundStakeAccountSubscriber.ts +0 -171
- package/src/accounts/webSocketProgramAccountSubscriber.ts +0 -244
- package/src/accounts/webSocketProgramAccountSubscriberV2.ts +0 -1048
- package/src/accounts/webSocketProgramAccountsSubscriberV2.ts +0 -1042
- package/src/accounts/webSocketUserAccountSubscriber.ts +0 -139
- package/src/accounts/webSocketUserStatsAccountSubsriber.ts +0 -131
- package/src/accounts/webSocketVelocityClientAccountSubscriber.ts +0 -865
- package/src/accounts/webSocketVelocityClientAccountSubscriberV2.ts +0 -897
- package/src/accounts/websocketProgramUserAccountSubscriber.ts +0 -127
- package/src/addresses/marketAddresses.ts +0 -28
- package/src/addresses/pda.ts +0 -620
- package/src/adminClient.ts +0 -8136
- package/src/assert/assert.ts +0 -5
- package/src/auctionSubscriber/auctionSubscriber.ts +0 -86
- package/src/auctionSubscriber/auctionSubscriberGrpc.ts +0 -99
- package/src/auctionSubscriber/index.ts +0 -9
- package/src/auctionSubscriber/types.ts +0 -30
- package/src/bankrun/bankrunConnection.ts +0 -630
- package/src/blockhashSubscriber/BlockhashSubscriber.ts +0 -160
- package/src/blockhashSubscriber/index.ts +0 -6
- package/src/blockhashSubscriber/types.ts +0 -13
- package/src/clock/clockSubscriber.ts +0 -146
- package/src/config.ts +0 -312
- package/src/constants/index.ts +0 -9
- package/src/constants/numericConstants.ts +0 -194
- package/src/constants/perpMarkets.ts +0 -127
- package/src/constants/spotMarkets.ts +0 -104
- package/src/constants/txConstants.ts +0 -2
- package/src/constituentMap/constituentMap.ts +0 -305
- package/src/constituentMap/pollingConstituentAccountSubscriber.ts +0 -99
- package/src/constituentMap/webSocketConstituentAccountSubscriber.ts +0 -121
- package/src/core/VelocityCore.ts +0 -606
- package/src/core/index.ts +0 -12
- package/src/core/instructions/deposit.ts +0 -64
- package/src/core/instructions/fill.ts +0 -51
- package/src/core/instructions/funding.ts +0 -33
- package/src/core/instructions/liquidation.ts +0 -55
- package/src/core/instructions/orders.ts +0 -84
- package/src/core/instructions/perpOrders.ts +0 -312
- package/src/core/instructions/settlement.ts +0 -40
- package/src/core/instructions/trigger.ts +0 -41
- package/src/core/instructions/withdraw.ts +0 -65
- package/src/core/remainingAccounts.ts +0 -320
- package/src/core/signedMsg.ts +0 -109
- package/src/decode/customCoder.ts +0 -148
- package/src/decode/user.ts +0 -401
- package/src/dlob/DLOB.ts +0 -2793
- package/src/dlob/DLOBNode.ts +0 -305
- package/src/dlob/DLOBSubscriber.ts +0 -269
- package/src/dlob/NodeList.ts +0 -244
- package/src/dlob/orderBookLevels.ts +0 -695
- package/src/dlob/types.ts +0 -37
- package/src/events/eventList.ts +0 -114
- package/src/events/eventSubscriber.ts +0 -521
- package/src/events/eventsServerLogProvider.ts +0 -189
- package/src/events/fetchLogs.ts +0 -219
- package/src/events/parse.ts +0 -271
- package/src/events/pollingLogProvider.ts +0 -113
- package/src/events/sort.ts +0 -51
- package/src/events/txEventCache.ts +0 -92
- package/src/events/types.ts +0 -337
- package/src/events/webSocketLogProvider.ts +0 -152
- package/src/factory/bigNum.ts +0 -709
- package/src/factory/oracleClient.ts +0 -73
- package/src/idl/pyth.json +0 -142
- package/src/idl/pyth.ts +0 -97
- package/src/idl/pyth_solana_receiver.json +0 -628
- package/src/idl/token_faucet.json +0 -229
- package/src/idl/token_faucet.ts +0 -197
- package/src/idl/velocity.json +0 -23974
- package/src/idl/velocity.ts +0 -23980
- package/src/index.ts +0 -150
- package/src/indicative-quotes/indicativeQuotesSender.ts +0 -233
- package/src/isomorphic/README.md +0 -19
- package/src/isomorphic/anchor.browser.ts +0 -44
- package/src/isomorphic/anchor.node.ts +0 -1
- package/src/isomorphic/anchor.ts +0 -1
- package/src/isomorphic/anchor29.browser.ts +0 -24
- package/src/isomorphic/anchor29.node.ts +0 -1
- package/src/isomorphic/anchor29.ts +0 -1
- package/src/isomorphic/grpc.browser.ts +0 -4
- package/src/isomorphic/grpc.node.ts +0 -130
- package/src/isomorphic/grpc.ts +0 -1
- package/src/jupiter/jupiterClient.ts +0 -502
- package/src/keypair.ts +0 -32
- package/src/margin/README.md +0 -138
- package/src/marginCalculation.ts +0 -407
- package/src/marinade/idl/idl.json +0 -1962
- package/src/marinade/index.ts +0 -73
- package/src/marinade/types.ts +0 -3925
- package/src/math/amm.ts +0 -1758
- package/src/math/auction.ts +0 -480
- package/src/math/bankruptcy.ts +0 -138
- package/src/math/builder.ts +0 -130
- package/src/math/conversion.ts +0 -45
- package/src/math/exchangeStatus.ts +0 -261
- package/src/math/funding.ts +0 -498
- package/src/math/insurance.ts +0 -171
- package/src/math/liquidation.ts +0 -433
- package/src/math/margin.ts +0 -327
- package/src/math/market.ts +0 -487
- package/src/math/oracles.ts +0 -477
- package/src/math/orders.ts +0 -500
- package/src/math/position.ts +0 -381
- package/src/math/repeg.ts +0 -223
- package/src/math/spotBalance.ts +0 -996
- package/src/math/spotMarket.ts +0 -115
- package/src/math/spotPosition.ts +0 -263
- package/src/math/state.ts +0 -71
- package/src/math/superStake.ts +0 -602
- package/src/math/tiers.ts +0 -73
- package/src/math/trade.ts +0 -879
- package/src/math/utils.ts +0 -183
- package/src/memcmp.ts +0 -279
- package/src/oracles/oracleClientCache.ts +0 -41
- package/src/oracles/oracleId.ts +0 -99
- package/src/oracles/prelaunchOracleClient.ts +0 -63
- package/src/oracles/pythClient.ts +0 -125
- package/src/oracles/pythLazerClient.ts +0 -141
- package/src/oracles/quoteAssetOracleClient.ts +0 -43
- package/src/oracles/strictOraclePrice.ts +0 -40
- package/src/oracles/types.ts +0 -82
- package/src/oracles/utils.ts +0 -45
- package/src/orderParams.ts +0 -117
- package/src/orderSubscriber/OrderSubscriber.ts +0 -378
- package/src/orderSubscriber/PollingSubscription.ts +0 -50
- package/src/orderSubscriber/WebsocketSubscription.ts +0 -137
- package/src/orderSubscriber/grpcSubscription.ts +0 -155
- package/src/orderSubscriber/index.ts +0 -7
- package/src/orderSubscriber/types.ts +0 -64
- package/src/priorityFee/averageOverSlotsStrategy.ts +0 -17
- package/src/priorityFee/averageStrategy.ts +0 -13
- package/src/priorityFee/ewmaStrategy.ts +0 -51
- package/src/priorityFee/heliusPriorityFeeMethod.ts +0 -68
- package/src/priorityFee/index.ts +0 -16
- package/src/priorityFee/maxOverSlotsStrategy.ts +0 -18
- package/src/priorityFee/maxStrategy.ts +0 -8
- package/src/priorityFee/priorityFeeSubscriber.ts +0 -328
- package/src/priorityFee/priorityFeeSubscriberMap.ts +0 -134
- package/src/priorityFee/solanaPriorityFeeMethod.ts +0 -46
- package/src/priorityFee/types.ts +0 -82
- package/src/priorityFee/velocityPriorityFeeMethod.ts +0 -53
- package/src/pyth/constants.ts +0 -9
- package/src/pyth/index.ts +0 -15
- package/src/pyth/pythLazerSubscriber.ts +0 -365
- package/src/pyth/types.ts +0 -4451
- package/src/pyth/utils.ts +0 -13
- package/src/slot/SlotSubscriber.ts +0 -126
- package/src/slot/SlothashSubscriber.ts +0 -163
- package/src/swap/UnifiedSwapClient.ts +0 -315
- package/src/swift/grpcSignedMsgUserAccountSubscriber.ts +0 -81
- package/src/swift/index.ts +0 -10
- package/src/swift/signedMsgUserAccountSubscriber.ts +0 -251
- package/src/swift/swiftOrderSubscriber.ts +0 -351
- package/src/testClient.ts +0 -42
- package/src/titan/titanClient.ts +0 -438
- package/src/token/index.ts +0 -13
- package/src/tokenFaucet.ts +0 -296
- package/src/tx/baseTxSender.ts +0 -651
- package/src/tx/blockhashFetcher/baseBlockhashFetcher.ts +0 -31
- package/src/tx/blockhashFetcher/cachedBlockhashFetcher.ts +0 -105
- package/src/tx/blockhashFetcher/types.ts +0 -9
- package/src/tx/fastSingleTxSender.ts +0 -200
- package/src/tx/forwardOnlyTxSender.ts +0 -193
- package/src/tx/priorityFeeCalculator.ts +0 -117
- package/src/tx/reportTransactionError.ts +0 -176
- package/src/tx/retryTxSender.ts +0 -177
- package/src/tx/txHandler.ts +0 -1009
- package/src/tx/txParamProcessor.ts +0 -225
- package/src/tx/types.ts +0 -117
- package/src/tx/utils.ts +0 -133
- package/src/tx/whileValidTxSender.ts +0 -377
- package/src/types.ts +0 -2575
- package/src/user.ts +0 -5316
- package/src/userConfig.ts +0 -38
- package/src/userMap/PollingSubscription.ts +0 -61
- package/src/userMap/WebsocketSubscription.ts +0 -93
- package/src/userMap/grpcSubscription.ts +0 -93
- package/src/userMap/referrerMap.ts +0 -360
- package/src/userMap/revenueShareEscrowMap.ts +0 -342
- package/src/userMap/userMap.ts +0 -784
- package/src/userMap/userMapConfig.ts +0 -82
- package/src/userMap/userStatsMap.ts +0 -393
- package/src/userName.ts +0 -37
- package/src/userStats.ts +0 -159
- package/src/userStatsConfig.ts +0 -35
- package/src/util/TransactionConfirmationManager.ts +0 -333
- package/src/util/chainClock.ts +0 -65
- package/src/util/computeUnits.ts +0 -101
- package/src/util/digest.ts +0 -33
- package/src/util/ed25519Utils.ts +0 -95
- package/src/util/promiseTimeout.ts +0 -23
- package/src/velocityClient.ts +0 -14802
- package/src/velocityClientConfig.ts +0 -119
- package/src/wallet.ts +0 -91
- package/tests/VelocityCore/builder_escrow.test.ts +0 -121
- package/tests/VelocityCore/decode.test.ts +0 -16
- package/tests/VelocityCore/fill_trigger.test.ts +0 -73
- package/tests/VelocityCore/instructions.test.ts +0 -46
- package/tests/VelocityCore/pdas.test.ts +0 -30
- package/tests/VelocityCore/perp_orders.test.ts +0 -205
- package/tests/VelocityCore/remainingAccounts.test.ts +0 -72
- package/tests/VelocityCore/settlement_liquidation.test.ts +0 -69
- package/tests/accounts/customizedCadenceBulkAccountLoader.test.ts +0 -211
- package/tests/amm/test.ts +0 -2402
- package/tests/auctions/test.ts +0 -81
- package/tests/bn/test.ts +0 -355
- package/tests/builder/builderFee.test.ts +0 -42
- package/tests/ci/verifyConstants.ts +0 -393
- package/tests/decode/test.ts +0 -262
- package/tests/decode/userAccountBufferStrings.ts +0 -102
- package/tests/dlob/helpers.ts +0 -748
- package/tests/dlob/test.ts +0 -6969
- package/tests/dlob/tickSizeStandardization.ts +0 -545
- package/tests/events/parseLogsForCuUsage.ts +0 -139
- package/tests/exchangeStatus/test.ts +0 -45
- package/tests/insurance/test.ts +0 -40
- package/tests/liquidation/test.ts +0 -125
- package/tests/oracles/mmOracleGate.test.ts +0 -379
- package/tests/oracles/pythPegSnap.test.ts +0 -76
- package/tests/sdkParity/enumParity.test.ts +0 -84
- package/tests/sdkParity/marginCategoryFill.test.ts +0 -143
- package/tests/sdkParity/memcmpOffsets.test.ts +0 -139
- package/tests/spot/test.ts +0 -281
- package/tests/tx/TransactionConfirmationManager.test.ts +0 -312
- package/tests/tx/cachedBlockhashFetcher.test.ts +0 -96
- package/tests/tx/priorityFeeCalculator.ts +0 -77
- package/tests/tx/priorityFeeStrategy.ts +0 -95
- package/tests/user/bankruptcy.ts +0 -165
- package/tests/user/feeAndWithdrawLimits.ts +0 -284
- package/tests/user/getMarginCalculation.ts +0 -586
- package/tests/user/helpers.ts +0 -184
- package/tests/user/liquidations.ts +0 -129
- package/tests/user/marginCalculations.test.ts +0 -321
- package/tests/user/test.ts +0 -833
- package/tsconfig.browser.json +0 -18
- package/tsconfig.json +0 -36
|
@@ -1,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
|
-
}
|