@velocity-exchange/sdk 0.4.0 → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/lib/browser/adminClient.d.ts +52 -0
- package/lib/browser/adminClient.js +97 -0
- package/lib/browser/config.js +6 -7
- package/lib/browser/constants/numericConstants.d.ts +1 -1
- package/lib/browser/constants/numericConstants.js +1 -1
- package/lib/browser/decode/user.js +5 -1
- package/lib/browser/idl/velocity.d.ts +191 -15
- package/lib/browser/idl/velocity.json +192 -16
- package/lib/browser/orderSubscriber/OrderSubscriber.d.ts +1 -0
- package/lib/browser/orderSubscriber/OrderSubscriber.js +10 -4
- package/lib/browser/pyth/pythLazerSubscriber.js +37 -27
- package/lib/browser/types.d.ts +6 -0
- package/lib/browser/user.d.ts +20 -3
- package/lib/browser/user.js +52 -4
- package/lib/browser/velocityClient.d.ts +46 -10
- package/lib/browser/velocityClient.js +107 -35
- package/lib/node/adminClient.d.ts +52 -0
- package/lib/node/adminClient.d.ts.map +1 -1
- package/lib/node/adminClient.js +97 -0
- package/lib/node/config.d.ts.map +1 -1
- package/lib/node/config.js +6 -7
- package/lib/node/constants/numericConstants.d.ts +1 -1
- package/lib/node/constants/numericConstants.d.ts.map +1 -1
- package/lib/node/constants/numericConstants.js +1 -1
- package/lib/node/decode/user.d.ts.map +1 -1
- package/lib/node/decode/user.js +5 -1
- package/lib/node/idl/velocity.d.ts +191 -15
- package/lib/node/idl/velocity.d.ts.map +1 -1
- package/lib/node/idl/velocity.json +192 -16
- package/lib/node/orderSubscriber/OrderSubscriber.d.ts +1 -0
- package/lib/node/orderSubscriber/OrderSubscriber.d.ts.map +1 -1
- package/lib/node/orderSubscriber/OrderSubscriber.js +10 -4
- package/lib/node/pyth/pythLazerSubscriber.d.ts.map +1 -1
- package/lib/node/pyth/pythLazerSubscriber.js +37 -27
- package/lib/node/types.d.ts +6 -0
- package/lib/node/types.d.ts.map +1 -1
- package/lib/node/user.d.ts +20 -3
- package/lib/node/user.d.ts.map +1 -1
- package/lib/node/user.js +52 -4
- package/lib/node/velocityClient.d.ts +46 -10
- package/lib/node/velocityClient.d.ts.map +1 -1
- package/lib/node/velocityClient.js +107 -35
- package/package.json +7 -3
- package/.prettierignore +0 -1
- package/CHANGELOG.md +0 -132
- package/build-browser.js +0 -58
- package/bunfig.toml +0 -3
- package/esbuild-shims.js +0 -12
- package/scripts/deposit-isolated-positions.ts +0 -110
- package/scripts/find-flagged-users.ts +0 -213
- package/scripts/grpc-client-test-comparison.ts +0 -372
- package/scripts/grpc-multiuser-client-test-comparison.ts +0 -158
- package/scripts/postbuild.js +0 -95
- package/scripts/single-grpc-client-test.ts +0 -284
- package/scripts/withdraw-isolated-positions.ts +0 -174
- package/src/accounts/README_WebSocketAccountSubscriberV2.md +0 -95
- package/src/accounts/README_WebSocketProgramAccountSubscriberV2.md +0 -135
- package/src/accounts/basicUserAccountSubscriber.ts +0 -79
- package/src/accounts/basicUserStatsAccountSubscriber.ts +0 -87
- package/src/accounts/bulkAccountLoader.ts +0 -358
- package/src/accounts/bulkUserStatsSubscription.ts +0 -40
- package/src/accounts/bulkUserSubscription.ts +0 -39
- package/src/accounts/customizedCadenceBulkAccountLoader.ts +0 -250
- package/src/accounts/fetch.ts +0 -200
- package/src/accounts/grpcAccountSubscriber.ts +0 -225
- package/src/accounts/grpcInsuranceFundStakeAccountSubscriber.ts +0 -73
- package/src/accounts/grpcMultiAccountSubscriber.ts +0 -566
- package/src/accounts/grpcMultiUserAccountSubscriber.ts +0 -328
- package/src/accounts/grpcProgramAccountSubscriber.ts +0 -264
- package/src/accounts/grpcUserAccountSubscriber.ts +0 -63
- package/src/accounts/grpcUserStatsAccountSubscriber.ts +0 -65
- package/src/accounts/grpcVelocityClientAccountSubscriber.ts +0 -257
- package/src/accounts/grpcVelocityClientAccountSubscriberV2.ts +0 -941
- package/src/accounts/laserProgramAccountSubscriber.ts +0 -266
- package/src/accounts/oneShotUserAccountSubscriber.ts +0 -88
- package/src/accounts/oneShotUserStatsAccountSubscriber.ts +0 -94
- package/src/accounts/pollingInsuranceFundStakeAccountSubscriber.ts +0 -223
- package/src/accounts/pollingOracleAccountSubscriber.ts +0 -151
- package/src/accounts/pollingTokenAccountSubscriber.ts +0 -147
- package/src/accounts/pollingUserAccountSubscriber.ts +0 -198
- package/src/accounts/pollingUserStatsAccountSubscriber.ts +0 -180
- package/src/accounts/pollingVelocityClientAccountSubscriber.ts +0 -774
- package/src/accounts/testBulkAccountLoader.ts +0 -59
- package/src/accounts/types.ts +0 -428
- package/src/accounts/utils.ts +0 -90
- package/src/accounts/webSocketAccountSubscriber.ts +0 -304
- package/src/accounts/webSocketAccountSubscriberV2.ts +0 -536
- package/src/accounts/webSocketInsuranceFundStakeAccountSubscriber.ts +0 -171
- package/src/accounts/webSocketProgramAccountSubscriber.ts +0 -244
- package/src/accounts/webSocketProgramAccountSubscriberV2.ts +0 -1048
- package/src/accounts/webSocketProgramAccountsSubscriberV2.ts +0 -1042
- package/src/accounts/webSocketUserAccountSubscriber.ts +0 -139
- package/src/accounts/webSocketUserStatsAccountSubsriber.ts +0 -131
- package/src/accounts/webSocketVelocityClientAccountSubscriber.ts +0 -865
- package/src/accounts/webSocketVelocityClientAccountSubscriberV2.ts +0 -897
- package/src/accounts/websocketProgramUserAccountSubscriber.ts +0 -127
- package/src/addresses/marketAddresses.ts +0 -28
- package/src/addresses/pda.ts +0 -620
- package/src/adminClient.ts +0 -8136
- package/src/assert/assert.ts +0 -5
- package/src/auctionSubscriber/auctionSubscriber.ts +0 -86
- package/src/auctionSubscriber/auctionSubscriberGrpc.ts +0 -99
- package/src/auctionSubscriber/index.ts +0 -9
- package/src/auctionSubscriber/types.ts +0 -30
- package/src/bankrun/bankrunConnection.ts +0 -630
- package/src/blockhashSubscriber/BlockhashSubscriber.ts +0 -160
- package/src/blockhashSubscriber/index.ts +0 -6
- package/src/blockhashSubscriber/types.ts +0 -13
- package/src/clock/clockSubscriber.ts +0 -146
- package/src/config.ts +0 -312
- package/src/constants/index.ts +0 -9
- package/src/constants/numericConstants.ts +0 -194
- package/src/constants/perpMarkets.ts +0 -127
- package/src/constants/spotMarkets.ts +0 -104
- package/src/constants/txConstants.ts +0 -2
- package/src/constituentMap/constituentMap.ts +0 -305
- package/src/constituentMap/pollingConstituentAccountSubscriber.ts +0 -99
- package/src/constituentMap/webSocketConstituentAccountSubscriber.ts +0 -121
- package/src/core/VelocityCore.ts +0 -606
- package/src/core/index.ts +0 -12
- package/src/core/instructions/deposit.ts +0 -64
- package/src/core/instructions/fill.ts +0 -51
- package/src/core/instructions/funding.ts +0 -33
- package/src/core/instructions/liquidation.ts +0 -55
- package/src/core/instructions/orders.ts +0 -84
- package/src/core/instructions/perpOrders.ts +0 -312
- package/src/core/instructions/settlement.ts +0 -40
- package/src/core/instructions/trigger.ts +0 -41
- package/src/core/instructions/withdraw.ts +0 -65
- package/src/core/remainingAccounts.ts +0 -320
- package/src/core/signedMsg.ts +0 -109
- package/src/decode/customCoder.ts +0 -148
- package/src/decode/user.ts +0 -401
- package/src/dlob/DLOB.ts +0 -2793
- package/src/dlob/DLOBNode.ts +0 -305
- package/src/dlob/DLOBSubscriber.ts +0 -269
- package/src/dlob/NodeList.ts +0 -244
- package/src/dlob/orderBookLevels.ts +0 -695
- package/src/dlob/types.ts +0 -37
- package/src/events/eventList.ts +0 -114
- package/src/events/eventSubscriber.ts +0 -521
- package/src/events/eventsServerLogProvider.ts +0 -189
- package/src/events/fetchLogs.ts +0 -219
- package/src/events/parse.ts +0 -271
- package/src/events/pollingLogProvider.ts +0 -113
- package/src/events/sort.ts +0 -51
- package/src/events/txEventCache.ts +0 -92
- package/src/events/types.ts +0 -337
- package/src/events/webSocketLogProvider.ts +0 -152
- package/src/factory/bigNum.ts +0 -709
- package/src/factory/oracleClient.ts +0 -73
- package/src/idl/pyth.json +0 -142
- package/src/idl/pyth.ts +0 -97
- package/src/idl/pyth_solana_receiver.json +0 -628
- package/src/idl/token_faucet.json +0 -229
- package/src/idl/token_faucet.ts +0 -197
- package/src/idl/velocity.json +0 -23974
- package/src/idl/velocity.ts +0 -23980
- package/src/index.ts +0 -150
- package/src/indicative-quotes/indicativeQuotesSender.ts +0 -233
- package/src/isomorphic/README.md +0 -19
- package/src/isomorphic/anchor.browser.ts +0 -44
- package/src/isomorphic/anchor.node.ts +0 -1
- package/src/isomorphic/anchor.ts +0 -1
- package/src/isomorphic/anchor29.browser.ts +0 -24
- package/src/isomorphic/anchor29.node.ts +0 -1
- package/src/isomorphic/anchor29.ts +0 -1
- package/src/isomorphic/grpc.browser.ts +0 -4
- package/src/isomorphic/grpc.node.ts +0 -130
- package/src/isomorphic/grpc.ts +0 -1
- package/src/jupiter/jupiterClient.ts +0 -502
- package/src/keypair.ts +0 -32
- package/src/margin/README.md +0 -138
- package/src/marginCalculation.ts +0 -407
- package/src/marinade/idl/idl.json +0 -1962
- package/src/marinade/index.ts +0 -73
- package/src/marinade/types.ts +0 -3925
- package/src/math/amm.ts +0 -1758
- package/src/math/auction.ts +0 -480
- package/src/math/bankruptcy.ts +0 -138
- package/src/math/builder.ts +0 -130
- package/src/math/conversion.ts +0 -45
- package/src/math/exchangeStatus.ts +0 -261
- package/src/math/funding.ts +0 -498
- package/src/math/insurance.ts +0 -171
- package/src/math/liquidation.ts +0 -433
- package/src/math/margin.ts +0 -327
- package/src/math/market.ts +0 -487
- package/src/math/oracles.ts +0 -477
- package/src/math/orders.ts +0 -500
- package/src/math/position.ts +0 -381
- package/src/math/repeg.ts +0 -223
- package/src/math/spotBalance.ts +0 -996
- package/src/math/spotMarket.ts +0 -115
- package/src/math/spotPosition.ts +0 -263
- package/src/math/state.ts +0 -71
- package/src/math/superStake.ts +0 -602
- package/src/math/tiers.ts +0 -73
- package/src/math/trade.ts +0 -879
- package/src/math/utils.ts +0 -183
- package/src/memcmp.ts +0 -279
- package/src/oracles/oracleClientCache.ts +0 -41
- package/src/oracles/oracleId.ts +0 -99
- package/src/oracles/prelaunchOracleClient.ts +0 -63
- package/src/oracles/pythClient.ts +0 -125
- package/src/oracles/pythLazerClient.ts +0 -141
- package/src/oracles/quoteAssetOracleClient.ts +0 -43
- package/src/oracles/strictOraclePrice.ts +0 -40
- package/src/oracles/types.ts +0 -82
- package/src/oracles/utils.ts +0 -45
- package/src/orderParams.ts +0 -117
- package/src/orderSubscriber/OrderSubscriber.ts +0 -378
- package/src/orderSubscriber/PollingSubscription.ts +0 -50
- package/src/orderSubscriber/WebsocketSubscription.ts +0 -137
- package/src/orderSubscriber/grpcSubscription.ts +0 -155
- package/src/orderSubscriber/index.ts +0 -7
- package/src/orderSubscriber/types.ts +0 -64
- package/src/priorityFee/averageOverSlotsStrategy.ts +0 -17
- package/src/priorityFee/averageStrategy.ts +0 -13
- package/src/priorityFee/ewmaStrategy.ts +0 -51
- package/src/priorityFee/heliusPriorityFeeMethod.ts +0 -68
- package/src/priorityFee/index.ts +0 -16
- package/src/priorityFee/maxOverSlotsStrategy.ts +0 -18
- package/src/priorityFee/maxStrategy.ts +0 -8
- package/src/priorityFee/priorityFeeSubscriber.ts +0 -328
- package/src/priorityFee/priorityFeeSubscriberMap.ts +0 -134
- package/src/priorityFee/solanaPriorityFeeMethod.ts +0 -46
- package/src/priorityFee/types.ts +0 -82
- package/src/priorityFee/velocityPriorityFeeMethod.ts +0 -53
- package/src/pyth/constants.ts +0 -9
- package/src/pyth/index.ts +0 -15
- package/src/pyth/pythLazerSubscriber.ts +0 -365
- package/src/pyth/types.ts +0 -4451
- package/src/pyth/utils.ts +0 -13
- package/src/slot/SlotSubscriber.ts +0 -126
- package/src/slot/SlothashSubscriber.ts +0 -163
- package/src/swap/UnifiedSwapClient.ts +0 -315
- package/src/swift/grpcSignedMsgUserAccountSubscriber.ts +0 -81
- package/src/swift/index.ts +0 -10
- package/src/swift/signedMsgUserAccountSubscriber.ts +0 -251
- package/src/swift/swiftOrderSubscriber.ts +0 -351
- package/src/testClient.ts +0 -42
- package/src/titan/titanClient.ts +0 -438
- package/src/token/index.ts +0 -13
- package/src/tokenFaucet.ts +0 -296
- package/src/tx/baseTxSender.ts +0 -651
- package/src/tx/blockhashFetcher/baseBlockhashFetcher.ts +0 -31
- package/src/tx/blockhashFetcher/cachedBlockhashFetcher.ts +0 -105
- package/src/tx/blockhashFetcher/types.ts +0 -9
- package/src/tx/fastSingleTxSender.ts +0 -200
- package/src/tx/forwardOnlyTxSender.ts +0 -193
- package/src/tx/priorityFeeCalculator.ts +0 -117
- package/src/tx/reportTransactionError.ts +0 -176
- package/src/tx/retryTxSender.ts +0 -177
- package/src/tx/txHandler.ts +0 -1009
- package/src/tx/txParamProcessor.ts +0 -225
- package/src/tx/types.ts +0 -117
- package/src/tx/utils.ts +0 -133
- package/src/tx/whileValidTxSender.ts +0 -377
- package/src/types.ts +0 -2575
- package/src/user.ts +0 -5316
- package/src/userConfig.ts +0 -38
- package/src/userMap/PollingSubscription.ts +0 -61
- package/src/userMap/WebsocketSubscription.ts +0 -93
- package/src/userMap/grpcSubscription.ts +0 -93
- package/src/userMap/referrerMap.ts +0 -360
- package/src/userMap/revenueShareEscrowMap.ts +0 -342
- package/src/userMap/userMap.ts +0 -784
- package/src/userMap/userMapConfig.ts +0 -82
- package/src/userMap/userStatsMap.ts +0 -393
- package/src/userName.ts +0 -37
- package/src/userStats.ts +0 -159
- package/src/userStatsConfig.ts +0 -35
- package/src/util/TransactionConfirmationManager.ts +0 -333
- package/src/util/chainClock.ts +0 -65
- package/src/util/computeUnits.ts +0 -101
- package/src/util/digest.ts +0 -33
- package/src/util/ed25519Utils.ts +0 -95
- package/src/util/promiseTimeout.ts +0 -23
- package/src/velocityClient.ts +0 -14802
- package/src/velocityClientConfig.ts +0 -119
- package/src/wallet.ts +0 -91
- package/tests/VelocityCore/builder_escrow.test.ts +0 -121
- package/tests/VelocityCore/decode.test.ts +0 -16
- package/tests/VelocityCore/fill_trigger.test.ts +0 -73
- package/tests/VelocityCore/instructions.test.ts +0 -46
- package/tests/VelocityCore/pdas.test.ts +0 -30
- package/tests/VelocityCore/perp_orders.test.ts +0 -205
- package/tests/VelocityCore/remainingAccounts.test.ts +0 -72
- package/tests/VelocityCore/settlement_liquidation.test.ts +0 -69
- package/tests/accounts/customizedCadenceBulkAccountLoader.test.ts +0 -211
- package/tests/amm/test.ts +0 -2402
- package/tests/auctions/test.ts +0 -81
- package/tests/bn/test.ts +0 -355
- package/tests/builder/builderFee.test.ts +0 -42
- package/tests/ci/verifyConstants.ts +0 -393
- package/tests/decode/test.ts +0 -262
- package/tests/decode/userAccountBufferStrings.ts +0 -102
- package/tests/dlob/helpers.ts +0 -748
- package/tests/dlob/test.ts +0 -6969
- package/tests/dlob/tickSizeStandardization.ts +0 -545
- package/tests/events/parseLogsForCuUsage.ts +0 -139
- package/tests/exchangeStatus/test.ts +0 -45
- package/tests/insurance/test.ts +0 -40
- package/tests/liquidation/test.ts +0 -125
- package/tests/oracles/mmOracleGate.test.ts +0 -379
- package/tests/oracles/pythPegSnap.test.ts +0 -76
- package/tests/sdkParity/enumParity.test.ts +0 -84
- package/tests/sdkParity/marginCategoryFill.test.ts +0 -143
- package/tests/sdkParity/memcmpOffsets.test.ts +0 -139
- package/tests/spot/test.ts +0 -281
- package/tests/tx/TransactionConfirmationManager.test.ts +0 -312
- package/tests/tx/cachedBlockhashFetcher.test.ts +0 -96
- package/tests/tx/priorityFeeCalculator.ts +0 -77
- package/tests/tx/priorityFeeStrategy.ts +0 -95
- package/tests/user/bankruptcy.ts +0 -165
- package/tests/user/feeAndWithdrawLimits.ts +0 -284
- package/tests/user/getMarginCalculation.ts +0 -586
- package/tests/user/helpers.ts +0 -184
- package/tests/user/liquidations.ts +0 -129
- package/tests/user/marginCalculations.test.ts +0 -321
- package/tests/user/test.ts +0 -833
- package/tsconfig.browser.json +0 -18
- package/tsconfig.json +0 -36
|
@@ -1,151 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
DataAndSlot,
|
|
3
|
-
NotSubscribedError,
|
|
4
|
-
OracleEvents,
|
|
5
|
-
OracleAccountSubscriber,
|
|
6
|
-
} from './types';
|
|
7
|
-
import StrictEventEmitter from 'strict-event-emitter-types';
|
|
8
|
-
import { EventEmitter } from 'events';
|
|
9
|
-
import { PublicKey } from '@solana/web3.js';
|
|
10
|
-
import { BulkAccountLoader } from './bulkAccountLoader';
|
|
11
|
-
import { OracleClient, OraclePriceData } from '../oracles/types';
|
|
12
|
-
|
|
13
|
-
/** Tracks a single oracle account's decoded `OraclePriceData` via a shared `BulkAccountLoader`, decoding raw buffers with the source-appropriate `OracleClient` (Pyth/Switchboard/quote-asset/etc.). */
|
|
14
|
-
export class PollingOracleAccountSubscriber implements OracleAccountSubscriber {
|
|
15
|
-
isSubscribed: boolean;
|
|
16
|
-
eventEmitter: StrictEventEmitter<EventEmitter, OracleEvents>;
|
|
17
|
-
publicKey: PublicKey;
|
|
18
|
-
|
|
19
|
-
accountLoader: BulkAccountLoader;
|
|
20
|
-
oracleClient: OracleClient;
|
|
21
|
-
callbackId?: string;
|
|
22
|
-
errorCallbackId?: string;
|
|
23
|
-
|
|
24
|
-
oraclePriceData?: DataAndSlot<OraclePriceData>;
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* @param publicKey Address of the oracle account to track.
|
|
28
|
-
* @param oracleClient Source-specific decoder (Pyth/Switchboard/quote-asset/etc.) used to parse the raw buffer into `OraclePriceData`.
|
|
29
|
-
* @param accountLoader Shared `BulkAccountLoader` this subscriber registers its callback with.
|
|
30
|
-
*/
|
|
31
|
-
public constructor(
|
|
32
|
-
publicKey: PublicKey,
|
|
33
|
-
oracleClient: OracleClient,
|
|
34
|
-
accountLoader: BulkAccountLoader
|
|
35
|
-
) {
|
|
36
|
-
this.isSubscribed = false;
|
|
37
|
-
this.publicKey = publicKey;
|
|
38
|
-
this.oracleClient = oracleClient;
|
|
39
|
-
this.accountLoader = accountLoader;
|
|
40
|
-
this.eventEmitter = new EventEmitter();
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* Registers this account with the shared `BulkAccountLoader`, then polls up to 5 times
|
|
45
|
-
* (once per `load()` cycle) until data has loaded, since the oracle account may not have been
|
|
46
|
-
* fetched by the shared loader on the very first poll. `isSubscribed` is only set `true` if
|
|
47
|
-
* data loaded within those retries.
|
|
48
|
-
* @returns `true` if oracle data loaded within the retry budget, `false` otherwise.
|
|
49
|
-
*/
|
|
50
|
-
async subscribe(): Promise<boolean> {
|
|
51
|
-
if (this.isSubscribed) {
|
|
52
|
-
return true;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
await this.addToAccountLoader();
|
|
56
|
-
|
|
57
|
-
let subscriptionSucceeded = false;
|
|
58
|
-
let retries = 0;
|
|
59
|
-
while (!subscriptionSucceeded && retries < 5) {
|
|
60
|
-
await this.fetch();
|
|
61
|
-
subscriptionSucceeded = this.didSubscriptionSucceed();
|
|
62
|
-
retries++;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
if (subscriptionSucceeded) {
|
|
66
|
-
this.eventEmitter.emit('update');
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
this.isSubscribed = subscriptionSucceeded;
|
|
70
|
-
return subscriptionSucceeded;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
/** Registers this account and an error callback with the `BulkAccountLoader`. A no-op if already registered. */
|
|
74
|
-
async addToAccountLoader(): Promise<void> {
|
|
75
|
-
if (this.callbackId) {
|
|
76
|
-
return;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
this.callbackId = await this.accountLoader.addAccount(
|
|
80
|
-
this.publicKey,
|
|
81
|
-
(buffer, slot) => {
|
|
82
|
-
const oraclePriceData =
|
|
83
|
-
this.oracleClient.getOraclePriceDataFromBuffer(buffer);
|
|
84
|
-
this.oraclePriceData = { data: oraclePriceData, slot };
|
|
85
|
-
// @ts-ignore
|
|
86
|
-
this.eventEmitter.emit('oracleUpdate', oraclePriceData);
|
|
87
|
-
this.eventEmitter.emit('update');
|
|
88
|
-
}
|
|
89
|
-
);
|
|
90
|
-
|
|
91
|
-
this.errorCallbackId = this.accountLoader.addErrorCallbacks((error) => {
|
|
92
|
-
this.eventEmitter.emit('error', error);
|
|
93
|
-
});
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
/** Forces the shared `BulkAccountLoader` to load, then decodes this account's buffer from it if present. Does not check slot ordering against the previously cached value (the loader itself already filters out stale/unchanged buffers before invoking callbacks). */
|
|
97
|
-
async fetch(): Promise<void> {
|
|
98
|
-
await this.accountLoader.load();
|
|
99
|
-
const bufferAndSlot = this.accountLoader.getBufferAndSlot(this.publicKey);
|
|
100
|
-
if (!bufferAndSlot) {
|
|
101
|
-
return;
|
|
102
|
-
}
|
|
103
|
-
const { buffer, slot } = bufferAndSlot;
|
|
104
|
-
if (!buffer) {
|
|
105
|
-
return;
|
|
106
|
-
}
|
|
107
|
-
this.oraclePriceData = {
|
|
108
|
-
data: await this.oracleClient.getOraclePriceDataFromBuffer(buffer),
|
|
109
|
-
slot,
|
|
110
|
-
};
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
/** Unregisters this account (and its error callback) from the `BulkAccountLoader`. A no-op if not subscribed. */
|
|
114
|
-
async unsubscribe(): Promise<void> {
|
|
115
|
-
if (!this.isSubscribed) {
|
|
116
|
-
return;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
if (this.callbackId) {
|
|
120
|
-
this.accountLoader.removeAccount(this.publicKey, this.callbackId);
|
|
121
|
-
this.callbackId = undefined;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
if (this.errorCallbackId) {
|
|
125
|
-
this.accountLoader.removeErrorCallbacks(this.errorCallbackId);
|
|
126
|
-
this.errorCallbackId = undefined;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
this.isSubscribed = false;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
/** Throws `NotSubscribedError` if `subscribe()` has not been called. */
|
|
133
|
-
assertIsSubscribed(): void {
|
|
134
|
-
if (!this.isSubscribed) {
|
|
135
|
-
throw new NotSubscribedError(
|
|
136
|
-
'You must call `subscribe` before using this function'
|
|
137
|
-
);
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
/** Throws `NotSubscribedError` if not subscribed. */
|
|
142
|
-
public getOraclePriceData(): DataAndSlot<OraclePriceData> {
|
|
143
|
-
this.assertIsSubscribed();
|
|
144
|
-
return this.oraclePriceData!;
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
/** True once oracle data has been loaded, independent of `isSubscribed`. */
|
|
148
|
-
didSubscriptionSucceed(): boolean {
|
|
149
|
-
return !!this.oraclePriceData;
|
|
150
|
-
}
|
|
151
|
-
}
|
|
@@ -1,147 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
DataAndSlot,
|
|
3
|
-
NotSubscribedError,
|
|
4
|
-
TokenAccountEvents,
|
|
5
|
-
TokenAccountSubscriber,
|
|
6
|
-
} from './types';
|
|
7
|
-
import StrictEventEmitter from 'strict-event-emitter-types';
|
|
8
|
-
import { EventEmitter } from 'events';
|
|
9
|
-
import { PublicKey } from '@solana/web3.js';
|
|
10
|
-
import { BulkAccountLoader } from './bulkAccountLoader';
|
|
11
|
-
import { Account } from '@solana/spl-token';
|
|
12
|
-
import { parseTokenAccount } from '../token';
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Tracks an SPL token account's parsed balance/owner via a shared `BulkAccountLoader`. Used for
|
|
16
|
-
* things like a spot market's token vault or a user's wallet token account, where a dedicated
|
|
17
|
-
* WebSocket subscription per account is unnecessary.
|
|
18
|
-
*/
|
|
19
|
-
export class PollingTokenAccountSubscriber implements TokenAccountSubscriber {
|
|
20
|
-
isSubscribed: boolean;
|
|
21
|
-
eventEmitter: StrictEventEmitter<EventEmitter, TokenAccountEvents>;
|
|
22
|
-
publicKey: PublicKey;
|
|
23
|
-
|
|
24
|
-
accountLoader: BulkAccountLoader;
|
|
25
|
-
callbackId?: string;
|
|
26
|
-
errorCallbackId?: string;
|
|
27
|
-
|
|
28
|
-
tokenAccountAndSlot?: DataAndSlot<Account>;
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* @param publicKey Address of the SPL token account to track.
|
|
32
|
-
* @param accountLoader Shared `BulkAccountLoader` this subscriber registers its callback with.
|
|
33
|
-
*/
|
|
34
|
-
public constructor(publicKey: PublicKey, accountLoader: BulkAccountLoader) {
|
|
35
|
-
this.isSubscribed = false;
|
|
36
|
-
this.publicKey = publicKey;
|
|
37
|
-
this.accountLoader = accountLoader;
|
|
38
|
-
this.eventEmitter = new EventEmitter();
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* Registers this account with the shared `BulkAccountLoader`, then polls up to 5 times
|
|
43
|
-
* (once per `load()` cycle) until data has loaded, since the token account may not have been
|
|
44
|
-
* fetched by the shared loader on the very first poll. `isSubscribed` is only set `true` if
|
|
45
|
-
* data loaded within those retries.
|
|
46
|
-
* @returns `true` if account data loaded within the retry budget, `false` otherwise.
|
|
47
|
-
*/
|
|
48
|
-
async subscribe(): Promise<boolean> {
|
|
49
|
-
if (this.isSubscribed) {
|
|
50
|
-
return true;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
await this.addToAccountLoader();
|
|
54
|
-
let subscriptionSucceeded = false;
|
|
55
|
-
let retries = 0;
|
|
56
|
-
while (!subscriptionSucceeded && retries < 5) {
|
|
57
|
-
await this.fetch();
|
|
58
|
-
subscriptionSucceeded = this.didSubscriptionSucceed();
|
|
59
|
-
retries++;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
if (subscriptionSucceeded) {
|
|
63
|
-
this.eventEmitter.emit('update');
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
this.isSubscribed = subscriptionSucceeded;
|
|
67
|
-
return subscriptionSucceeded;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
/** Registers this account and an error callback with the `BulkAccountLoader`. A no-op if already registered. */
|
|
71
|
-
async addToAccountLoader(): Promise<void> {
|
|
72
|
-
if (this.callbackId) {
|
|
73
|
-
return;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
this.callbackId = await this.accountLoader.addAccount(
|
|
77
|
-
this.publicKey,
|
|
78
|
-
(buffer, slot: number) => {
|
|
79
|
-
const tokenAccount = parseTokenAccount(buffer, this.publicKey);
|
|
80
|
-
this.tokenAccountAndSlot = { data: tokenAccount, slot };
|
|
81
|
-
// @ts-ignore
|
|
82
|
-
this.eventEmitter.emit('tokenAccountUpdate', tokenAccount);
|
|
83
|
-
this.eventEmitter.emit('update');
|
|
84
|
-
}
|
|
85
|
-
);
|
|
86
|
-
|
|
87
|
-
this.errorCallbackId = this.accountLoader.addErrorCallbacks((error) => {
|
|
88
|
-
this.eventEmitter.emit('error', error);
|
|
89
|
-
});
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
/** Forces the shared `BulkAccountLoader` to load, then reads and parses this account's buffer from it if present. Does not check slot ordering against the previously cached value (the loader itself already filters out stale/unchanged buffers before invoking callbacks). */
|
|
93
|
-
async fetch(): Promise<void> {
|
|
94
|
-
await this.accountLoader.load();
|
|
95
|
-
const bufferAndSlot = this.accountLoader.getBufferAndSlot(this.publicKey);
|
|
96
|
-
if (!bufferAndSlot) {
|
|
97
|
-
return;
|
|
98
|
-
}
|
|
99
|
-
const { buffer, slot } = bufferAndSlot;
|
|
100
|
-
if (!buffer) {
|
|
101
|
-
return;
|
|
102
|
-
}
|
|
103
|
-
this.tokenAccountAndSlot = {
|
|
104
|
-
data: parseTokenAccount(buffer, this.publicKey),
|
|
105
|
-
slot,
|
|
106
|
-
};
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
/** Unregisters this account (and its error callback) from the `BulkAccountLoader`. A no-op if not subscribed. */
|
|
110
|
-
async unsubscribe(): Promise<void> {
|
|
111
|
-
if (!this.isSubscribed) {
|
|
112
|
-
return;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
if (this.callbackId) {
|
|
116
|
-
this.accountLoader.removeAccount(this.publicKey, this.callbackId);
|
|
117
|
-
this.callbackId = undefined;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
if (this.errorCallbackId) {
|
|
121
|
-
this.accountLoader.removeErrorCallbacks(this.errorCallbackId);
|
|
122
|
-
this.errorCallbackId = undefined;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
this.isSubscribed = false;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
/** Throws `NotSubscribedError` if `subscribe()` has not been called. */
|
|
129
|
-
assertIsSubscribed(): void {
|
|
130
|
-
if (!this.isSubscribed) {
|
|
131
|
-
throw new NotSubscribedError(
|
|
132
|
-
'You must call `subscribe` before using this function'
|
|
133
|
-
);
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
/** Throws `NotSubscribedError` if not subscribed. */
|
|
138
|
-
public getTokenAccountAndSlot(): DataAndSlot<Account> {
|
|
139
|
-
this.assertIsSubscribed();
|
|
140
|
-
return this.tokenAccountAndSlot!;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
/** True once account data has been loaded, independent of `isSubscribed`. */
|
|
144
|
-
didSubscriptionSucceed(): boolean {
|
|
145
|
-
return !!this.tokenAccountAndSlot;
|
|
146
|
-
}
|
|
147
|
-
}
|
|
@@ -1,198 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
DataAndSlot,
|
|
3
|
-
NotSubscribedError,
|
|
4
|
-
UserAccountEvents,
|
|
5
|
-
UserAccountSubscriber,
|
|
6
|
-
} from './types';
|
|
7
|
-
import { Connection } from '../bankrun/bankrunConnection';
|
|
8
|
-
import StrictEventEmitter from 'strict-event-emitter-types';
|
|
9
|
-
import { EventEmitter } from 'events';
|
|
10
|
-
import { PublicKey } from '@solana/web3.js';
|
|
11
|
-
import { UserAccount } from '../types';
|
|
12
|
-
import { BulkAccountLoader } from './bulkAccountLoader';
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* `UserAccountSubscriber` backed by a shared `BulkAccountLoader` instead of a dedicated
|
|
16
|
-
* WebSocket subscription. Registers a callback for the user's `UserAccount` and relies on the
|
|
17
|
-
* loader's periodic batched `getMultipleAccounts` polling to detect changes. Prefer this over
|
|
18
|
-
* `WebSocketUserAccountSubscriber` when tracking many users concurrently (e.g. a keeper watching
|
|
19
|
-
* the whole DLOB) to keep the RPC/WS connection count bounded.
|
|
20
|
-
*/
|
|
21
|
-
export class PollingUserAccountSubscriber implements UserAccountSubscriber {
|
|
22
|
-
isSubscribed: boolean;
|
|
23
|
-
connection: Connection;
|
|
24
|
-
eventEmitter: StrictEventEmitter<EventEmitter, UserAccountEvents>;
|
|
25
|
-
userAccountPublicKey: PublicKey;
|
|
26
|
-
|
|
27
|
-
accountLoader: BulkAccountLoader;
|
|
28
|
-
callbackId?: string;
|
|
29
|
-
errorCallbackId?: string;
|
|
30
|
-
|
|
31
|
-
decode: (name: string, buffer: Buffer) => UserAccount;
|
|
32
|
-
|
|
33
|
-
user?: DataAndSlot<UserAccount>;
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* @param connection Connection (or bankrun-compatible shim) used for the one-off `fetch()` fallback.
|
|
37
|
-
* @param userAccountPublicKey Address of the `UserAccount` to track.
|
|
38
|
-
* @param accountLoader Shared `BulkAccountLoader` this subscriber registers its callback with.
|
|
39
|
-
* @param decode Decode function for the raw account buffer (typically the program's Anchor coder).
|
|
40
|
-
*/
|
|
41
|
-
public constructor(
|
|
42
|
-
connection: Connection,
|
|
43
|
-
userAccountPublicKey: PublicKey,
|
|
44
|
-
accountLoader: BulkAccountLoader,
|
|
45
|
-
decode: (name: string, buffer: Buffer) => UserAccount
|
|
46
|
-
) {
|
|
47
|
-
this.isSubscribed = false;
|
|
48
|
-
this.connection = connection;
|
|
49
|
-
this.accountLoader = accountLoader;
|
|
50
|
-
this.eventEmitter = new EventEmitter();
|
|
51
|
-
this.userAccountPublicKey = userAccountPublicKey;
|
|
52
|
-
this.decode = decode;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* Registers this account with the shared `BulkAccountLoader` and, if no data has loaded yet
|
|
57
|
-
* (from a prior fetch or the optional `userAccount` seed), performs a one-off `fetch()` so the
|
|
58
|
-
* subscriber has data before returning. Idempotent: a no-op if already subscribed.
|
|
59
|
-
* @param userAccount Optional pre-fetched account data to seed with (at slot 0) instead of an immediate fetch.
|
|
60
|
-
*/
|
|
61
|
-
async subscribe(userAccount?: UserAccount): Promise<boolean> {
|
|
62
|
-
if (this.isSubscribed) {
|
|
63
|
-
return true;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
if (userAccount) {
|
|
67
|
-
// `slot: 0` keeps {data, slot} atomic: a seeded account always carries a
|
|
68
|
-
// slot (0 = oldest-possible sentinel, overwritten by the first real fetch).
|
|
69
|
-
this.user = { data: userAccount, slot: 0 };
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
await this.addToAccountLoader();
|
|
73
|
-
|
|
74
|
-
await this.fetchIfUnloaded();
|
|
75
|
-
if (this.doesAccountExist()) {
|
|
76
|
-
this.eventEmitter.emit('update');
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
this.isSubscribed = true;
|
|
80
|
-
return true;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
/** Registers this user's account and an error callback with the `BulkAccountLoader`. A no-op if already registered. */
|
|
84
|
-
async addToAccountLoader(): Promise<void> {
|
|
85
|
-
if (this.callbackId) {
|
|
86
|
-
return;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
this.callbackId = await this.accountLoader.addAccount(
|
|
90
|
-
this.userAccountPublicKey,
|
|
91
|
-
(buffer, slot: number) => {
|
|
92
|
-
if (!buffer) {
|
|
93
|
-
return;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
if (this.user && this.user.slot > slot) {
|
|
97
|
-
return;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
const account = this.decode('user', buffer);
|
|
101
|
-
this.user = { data: account, slot };
|
|
102
|
-
this.eventEmitter.emit('userAccountUpdate', account);
|
|
103
|
-
this.eventEmitter.emit('update');
|
|
104
|
-
}
|
|
105
|
-
);
|
|
106
|
-
|
|
107
|
-
this.errorCallbackId = this.accountLoader.addErrorCallbacks((error) => {
|
|
108
|
-
this.eventEmitter.emit('error', error);
|
|
109
|
-
});
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
/** Fetches via `fetch()` only if no data is cached yet; otherwise a no-op. */
|
|
113
|
-
async fetchIfUnloaded(): Promise<void> {
|
|
114
|
-
if (!this.doesAccountExist()) {
|
|
115
|
-
await this.fetch();
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
/** Fetches the account once directly via RPC (independent of the account loader's poll cycle), applying it only if the response's slot is newer than what's cached. Logs and swallows errors (e.g. account not yet initialized) rather than throwing. */
|
|
120
|
-
async fetch(): Promise<void> {
|
|
121
|
-
try {
|
|
122
|
-
const dataAndContext = await this.connection.getAccountInfoAndContext(
|
|
123
|
-
this.userAccountPublicKey,
|
|
124
|
-
this.accountLoader.commitment
|
|
125
|
-
);
|
|
126
|
-
if (
|
|
127
|
-
dataAndContext.value !== null &&
|
|
128
|
-
dataAndContext.context.slot > (this.user?.slot ?? 0)
|
|
129
|
-
) {
|
|
130
|
-
this.user = {
|
|
131
|
-
data: this.decode('user', dataAndContext.value.data),
|
|
132
|
-
slot: dataAndContext.context.slot,
|
|
133
|
-
};
|
|
134
|
-
}
|
|
135
|
-
} catch (e) {
|
|
136
|
-
const err = e instanceof Error ? e : new Error(String(e));
|
|
137
|
-
console.log(
|
|
138
|
-
`PollingUserAccountSubscriber.fetch() UserAccount does not exist: ${err.message}-${err.stack}`
|
|
139
|
-
);
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
/** Type predicate: true once `user` has loaded, narrowing `this.user` to non-undefined for callers that check it first. */
|
|
144
|
-
doesAccountExist(): this is { user: DataAndSlot<UserAccount> } {
|
|
145
|
-
return this.user !== undefined;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
/** Unregisters this account (and its error callback) from the `BulkAccountLoader`. A no-op if not subscribed. */
|
|
149
|
-
async unsubscribe(): Promise<void> {
|
|
150
|
-
if (!this.isSubscribed) {
|
|
151
|
-
return;
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
if (this.callbackId) {
|
|
155
|
-
this.accountLoader.removeAccount(
|
|
156
|
-
this.userAccountPublicKey,
|
|
157
|
-
this.callbackId
|
|
158
|
-
);
|
|
159
|
-
this.callbackId = undefined;
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
if (this.errorCallbackId) {
|
|
163
|
-
this.accountLoader.removeErrorCallbacks(this.errorCallbackId);
|
|
164
|
-
this.errorCallbackId = undefined;
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
this.isSubscribed = false;
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
/** Throws `NotSubscribedError` if `subscribe()` has not been called. */
|
|
171
|
-
assertIsSubscribed(): void {
|
|
172
|
-
if (!this.isSubscribed) {
|
|
173
|
-
throw new NotSubscribedError(
|
|
174
|
-
'You must call `subscribe` before using this function'
|
|
175
|
-
);
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
/** Throws `NotSubscribedError` if not subscribed. Returns undefined only if subscribed but no data has loaded yet. */
|
|
180
|
-
public getUserAccountAndSlot(): DataAndSlot<UserAccount> | undefined {
|
|
181
|
-
this.assertIsSubscribed();
|
|
182
|
-
return this.user;
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
/**
|
|
186
|
-
* Applies an externally-obtained account update (e.g. relayed by the SDK's `User` wrapper) if
|
|
187
|
-
* `slot` is strictly newer than the currently cached slot (equal-slot updates are ignored).
|
|
188
|
-
* @param userAccount Decoded account data to apply.
|
|
189
|
-
* @param slot Slot the data was observed at.
|
|
190
|
-
*/
|
|
191
|
-
public updateData(userAccount: UserAccount, slot: number): void {
|
|
192
|
-
if (!this.user || this.user.slot < slot) {
|
|
193
|
-
this.user = { data: userAccount, slot };
|
|
194
|
-
this.eventEmitter.emit('userAccountUpdate', userAccount);
|
|
195
|
-
this.eventEmitter.emit('update');
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
}
|
|
@@ -1,180 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
DataAndSlot,
|
|
3
|
-
NotSubscribedError,
|
|
4
|
-
UserStatsAccountSubscriber,
|
|
5
|
-
UserStatsAccountEvents,
|
|
6
|
-
} from './types';
|
|
7
|
-
import { VelocityProgram } from '../config';
|
|
8
|
-
import StrictEventEmitter from 'strict-event-emitter-types';
|
|
9
|
-
import { EventEmitter } from 'events';
|
|
10
|
-
import { PublicKey } from '@solana/web3.js';
|
|
11
|
-
import { UserStatsAccount } from '../types';
|
|
12
|
-
import { BulkAccountLoader } from './bulkAccountLoader';
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* `UserStatsAccountSubscriber` backed by a shared `BulkAccountLoader` instead of a dedicated
|
|
16
|
-
* WebSocket subscription, mirroring `PollingUserAccountSubscriber` for `UserStatsAccount`.
|
|
17
|
-
*/
|
|
18
|
-
export class PollingUserStatsAccountSubscriber
|
|
19
|
-
implements UserStatsAccountSubscriber
|
|
20
|
-
{
|
|
21
|
-
isSubscribed: boolean;
|
|
22
|
-
program: VelocityProgram;
|
|
23
|
-
eventEmitter: StrictEventEmitter<EventEmitter, UserStatsAccountEvents>;
|
|
24
|
-
userStatsAccountPublicKey: PublicKey;
|
|
25
|
-
|
|
26
|
-
accountLoader: BulkAccountLoader;
|
|
27
|
-
callbackId?: string;
|
|
28
|
-
errorCallbackId?: string;
|
|
29
|
-
|
|
30
|
-
userStats?: DataAndSlot<UserStatsAccount>;
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* @param program Anchor program used for the one-off `fetch()` fallback and account decoding.
|
|
34
|
-
* @param userStatsAccountPublicKey Address of the `UserStatsAccount` to track.
|
|
35
|
-
* @param accountLoader Shared `BulkAccountLoader` this subscriber registers its callback with.
|
|
36
|
-
*/
|
|
37
|
-
public constructor(
|
|
38
|
-
program: VelocityProgram,
|
|
39
|
-
userStatsAccountPublicKey: PublicKey,
|
|
40
|
-
accountLoader: BulkAccountLoader
|
|
41
|
-
) {
|
|
42
|
-
this.isSubscribed = false;
|
|
43
|
-
this.program = program;
|
|
44
|
-
this.accountLoader = accountLoader;
|
|
45
|
-
this.eventEmitter = new EventEmitter();
|
|
46
|
-
this.userStatsAccountPublicKey = userStatsAccountPublicKey;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* Registers this account with the shared `BulkAccountLoader` and, if no data has loaded yet
|
|
51
|
-
* (from a prior fetch or the optional `userStatsAccount` seed), performs a one-off `fetch()`
|
|
52
|
-
* so the subscriber has data before returning. Idempotent: a no-op if already subscribed.
|
|
53
|
-
* @param userStatsAccount Optional pre-fetched account data to seed with (at slot 0) instead of an immediate fetch.
|
|
54
|
-
*/
|
|
55
|
-
async subscribe(userStatsAccount?: UserStatsAccount): Promise<boolean> {
|
|
56
|
-
if (this.isSubscribed) {
|
|
57
|
-
return true;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
if (userStatsAccount) {
|
|
61
|
-
// `slot: 0` keeps {data, slot} atomic: a seeded account always carries a
|
|
62
|
-
// slot (0 = oldest-possible sentinel, overwritten by the first real fetch).
|
|
63
|
-
this.userStats = { data: userStatsAccount, slot: 0 };
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
await this.addToAccountLoader();
|
|
67
|
-
|
|
68
|
-
await this.fetchIfUnloaded();
|
|
69
|
-
|
|
70
|
-
if (this.doesAccountExist()) {
|
|
71
|
-
this.eventEmitter.emit('update');
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
this.isSubscribed = true;
|
|
75
|
-
return true;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
/** Registers this user stats account and an error callback with the `BulkAccountLoader`. A no-op if already registered. */
|
|
79
|
-
async addToAccountLoader(): Promise<void> {
|
|
80
|
-
if (this.callbackId !== undefined) {
|
|
81
|
-
return;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
this.callbackId = await this.accountLoader.addAccount(
|
|
85
|
-
this.userStatsAccountPublicKey,
|
|
86
|
-
(buffer, slot: number) => {
|
|
87
|
-
if (!buffer) {
|
|
88
|
-
return;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
if (this.userStats && this.userStats.slot > slot) {
|
|
92
|
-
return;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
const account = this.program.coder.accounts.decodeUnchecked(
|
|
96
|
-
'userStats',
|
|
97
|
-
buffer
|
|
98
|
-
);
|
|
99
|
-
this.userStats = { data: account, slot };
|
|
100
|
-
this.eventEmitter.emit('userStatsAccountUpdate', account);
|
|
101
|
-
this.eventEmitter.emit('update');
|
|
102
|
-
}
|
|
103
|
-
);
|
|
104
|
-
|
|
105
|
-
this.errorCallbackId = this.accountLoader.addErrorCallbacks((error) => {
|
|
106
|
-
this.eventEmitter.emit('error', error);
|
|
107
|
-
});
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
/** Fetches via `fetch()` only if no data is cached yet; otherwise a no-op. */
|
|
111
|
-
async fetchIfUnloaded(): Promise<void> {
|
|
112
|
-
if (!this.doesAccountExist()) {
|
|
113
|
-
await this.fetch();
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
/** Fetches the account once directly via `program.account.userStats.fetchAndContext` (independent of the account loader's poll cycle), applying it only if the response's slot is newer than what's cached. Logs and swallows errors rather than throwing. */
|
|
118
|
-
async fetch(): Promise<void> {
|
|
119
|
-
try {
|
|
120
|
-
const dataAndContext = await (
|
|
121
|
-
this.program.account as any
|
|
122
|
-
).userStats.fetchAndContext(
|
|
123
|
-
this.userStatsAccountPublicKey,
|
|
124
|
-
this.accountLoader.commitment
|
|
125
|
-
);
|
|
126
|
-
if (dataAndContext.context.slot > (this.userStats?.slot ?? 0)) {
|
|
127
|
-
this.userStats = {
|
|
128
|
-
data: dataAndContext.data as unknown as UserStatsAccount,
|
|
129
|
-
slot: dataAndContext.context.slot,
|
|
130
|
-
};
|
|
131
|
-
}
|
|
132
|
-
} catch (e) {
|
|
133
|
-
console.log(
|
|
134
|
-
`PollingUserStatsAccountSubscriber.fetch() UserStatsAccount does not exist: ${
|
|
135
|
-
e instanceof Error ? e.message : String(e)
|
|
136
|
-
}`
|
|
137
|
-
);
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
/** Type predicate: true once `userStats` has loaded, narrowing `this.userStats` to non-undefined. */
|
|
142
|
-
doesAccountExist(): this is { userStats: DataAndSlot<UserStatsAccount> } {
|
|
143
|
-
return this.userStats !== undefined;
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
/** Unregisters this account (and its error callback) from the `BulkAccountLoader`. A no-op if not subscribed. */
|
|
147
|
-
async unsubscribe(): Promise<void> {
|
|
148
|
-
if (!this.isSubscribed) {
|
|
149
|
-
return;
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
this.accountLoader.removeAccount(
|
|
153
|
-
this.userStatsAccountPublicKey,
|
|
154
|
-
this.callbackId
|
|
155
|
-
);
|
|
156
|
-
this.callbackId = undefined;
|
|
157
|
-
|
|
158
|
-
this.accountLoader.removeErrorCallbacks(this.errorCallbackId);
|
|
159
|
-
this.errorCallbackId = undefined;
|
|
160
|
-
|
|
161
|
-
this.isSubscribed = false;
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
/** Throws `NotSubscribedError` if `subscribe()` has not been called. */
|
|
165
|
-
assertIsSubscribed(): void {
|
|
166
|
-
if (!this.isSubscribed) {
|
|
167
|
-
throw new NotSubscribedError(
|
|
168
|
-
'You must call `subscribe` before using this function'
|
|
169
|
-
);
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
/** Throws `NotSubscribedError` if not subscribed. Returns undefined only if subscribed but no data has loaded yet. */
|
|
174
|
-
public getUserStatsAccountAndSlot():
|
|
175
|
-
| DataAndSlot<UserStatsAccount>
|
|
176
|
-
| undefined {
|
|
177
|
-
this.assertIsSubscribed();
|
|
178
|
-
return this.userStats;
|
|
179
|
-
}
|
|
180
|
-
}
|