@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
package/src/userMap/userMap.ts
DELETED
|
@@ -1,784 +0,0 @@
|
|
|
1
|
-
import { BN } from '../isomorphic/anchor';
|
|
2
|
-
import { User } from '../user';
|
|
3
|
-
import { VelocityClient } from '../velocityClient';
|
|
4
|
-
import {
|
|
5
|
-
UserAccount,
|
|
6
|
-
OrderRecord,
|
|
7
|
-
DepositRecord,
|
|
8
|
-
FundingPaymentRecord,
|
|
9
|
-
LiquidationRecord,
|
|
10
|
-
OrderActionRecord,
|
|
11
|
-
SettlePnlRecord,
|
|
12
|
-
NewUserRecord,
|
|
13
|
-
StateAccount,
|
|
14
|
-
} from '../types';
|
|
15
|
-
import { WrappedEvent } from '../events/types';
|
|
16
|
-
import { DLOB } from '../dlob/DLOB';
|
|
17
|
-
import { UserSubscriptionConfig } from '../userConfig';
|
|
18
|
-
import { DataAndSlot, UserEvents } from '../accounts/types';
|
|
19
|
-
import { OneShotUserAccountSubscriber } from '../accounts/oneShotUserAccountSubscriber';
|
|
20
|
-
|
|
21
|
-
import {
|
|
22
|
-
Commitment,
|
|
23
|
-
Connection,
|
|
24
|
-
MemcmpFilter,
|
|
25
|
-
PublicKey,
|
|
26
|
-
RpcResponseAndContext,
|
|
27
|
-
} from '@solana/web3.js';
|
|
28
|
-
import { Buffer } from 'buffer';
|
|
29
|
-
import { ZSTDDecoder } from 'zstddec';
|
|
30
|
-
import {
|
|
31
|
-
getNonIdleUserFilter,
|
|
32
|
-
getUserFilter,
|
|
33
|
-
getUsersWithPoolId,
|
|
34
|
-
} from '../memcmp';
|
|
35
|
-
import {
|
|
36
|
-
SyncConfig,
|
|
37
|
-
UserAccountFilterCriteria as UserFilterCriteria,
|
|
38
|
-
UserMapConfig,
|
|
39
|
-
} from './userMapConfig';
|
|
40
|
-
import { WebsocketSubscription } from './WebsocketSubscription';
|
|
41
|
-
import { PollingSubscription } from './PollingSubscription';
|
|
42
|
-
import { decodeUser } from '../decode/user';
|
|
43
|
-
import { grpcSubscription } from './grpcSubscription';
|
|
44
|
-
import StrictEventEmitter from 'strict-event-emitter-types';
|
|
45
|
-
import { EventEmitter } from 'events';
|
|
46
|
-
|
|
47
|
-
// Velocity's User account is 4496 bytes (8-byte discriminator + 4488 struct);
|
|
48
|
-
// drift's was 4376. This caps the zstd-decompressed buffer in defaultSync — if it's
|
|
49
|
-
// smaller than the real account, the buffer is truncated and decodeUser reads past
|
|
50
|
-
// the end (RangeError: ERR_BUFFER_OUT_OF_BOUNDS). Must be >= the on-chain User size.
|
|
51
|
-
const MAX_USER_ACCOUNT_SIZE_BYTES = 4496;
|
|
52
|
-
|
|
53
|
-
/** Public surface implemented by `UserMap`. */
|
|
54
|
-
export interface UserMapInterface {
|
|
55
|
-
eventEmitter: StrictEventEmitter<EventEmitter, UserEvents>;
|
|
56
|
-
subscribe(): Promise<void>;
|
|
57
|
-
unsubscribe(): Promise<void>;
|
|
58
|
-
addPubkey(
|
|
59
|
-
userAccountPublicKey: PublicKey,
|
|
60
|
-
userAccount?: UserAccount,
|
|
61
|
-
slot?: number,
|
|
62
|
-
accountSubscription?: UserSubscriptionConfig
|
|
63
|
-
): Promise<void>;
|
|
64
|
-
has(key: string): boolean;
|
|
65
|
-
get(key: string): User | undefined;
|
|
66
|
-
getWithSlot(key: string): DataAndSlot<User> | undefined;
|
|
67
|
-
mustGet(
|
|
68
|
-
key: string,
|
|
69
|
-
accountSubscription?: UserSubscriptionConfig
|
|
70
|
-
): Promise<User>;
|
|
71
|
-
mustGetWithSlot(
|
|
72
|
-
key: string,
|
|
73
|
-
accountSubscription?: UserSubscriptionConfig
|
|
74
|
-
): Promise<DataAndSlot<User>>;
|
|
75
|
-
getUserAuthority(key: string): PublicKey | undefined;
|
|
76
|
-
updateWithOrderRecord(record: OrderRecord): Promise<void>;
|
|
77
|
-
values(): IterableIterator<User>;
|
|
78
|
-
valuesWithSlot(): IterableIterator<DataAndSlot<User>>;
|
|
79
|
-
entries(): IterableIterator<[string, User]>;
|
|
80
|
-
entriesWithSlot(): IterableIterator<[string, DataAndSlot<User>]>;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
/**
|
|
84
|
-
* In-memory cache of every `User` account on the program, keyed by the
|
|
85
|
-
* `User` account's own public key.
|
|
86
|
-
*
|
|
87
|
-
* Sync/subscription filtering (see `getFilters`) is built from memcmp
|
|
88
|
-
* filters: the base `getUserFilter()` (matches the `User` account
|
|
89
|
-
* discriminator — required, or every account on chain would match) is always
|
|
90
|
-
* present; `getNonIdleUserFilter()` is added unless `includeIdle` is set (idle
|
|
91
|
-
* accounts are excluded by default to reduce subscription volume);
|
|
92
|
-
* `getUsersWithPoolId(filterByPoolId)` is added when `filterByPoolId` is set;
|
|
93
|
-
* and any `additionalFilters` from the config are appended last. The same
|
|
94
|
-
* filter set is used for the initial `getProgramAccounts` sync and for the
|
|
95
|
-
* live websocket/gRPC subscription, so what you sync is what you keep getting
|
|
96
|
-
* updates for.
|
|
97
|
-
*
|
|
98
|
-
* Automatically does a full `sync()` whenever the program's
|
|
99
|
-
* `StateAccount.numberOfSubAccounts` changes (new/deleted user accounts),
|
|
100
|
-
* unless `disableSyncOnTotalAccountsChange` is set.
|
|
101
|
-
*/
|
|
102
|
-
export class UserMap implements UserMapInterface {
|
|
103
|
-
private userMap = new Map<string, DataAndSlot<User>>();
|
|
104
|
-
velocityClient: VelocityClient;
|
|
105
|
-
eventEmitter: StrictEventEmitter<EventEmitter, UserEvents>;
|
|
106
|
-
private connection: Connection;
|
|
107
|
-
private commitment: Commitment;
|
|
108
|
-
private includeIdle: boolean;
|
|
109
|
-
private filterByPoolId?: number;
|
|
110
|
-
private additionalFilters?: MemcmpFilter[];
|
|
111
|
-
private disableSyncOnTotalAccountsChange: boolean;
|
|
112
|
-
private lastNumberOfSubAccounts?: BN;
|
|
113
|
-
private subscription:
|
|
114
|
-
| PollingSubscription
|
|
115
|
-
| WebsocketSubscription
|
|
116
|
-
| grpcSubscription;
|
|
117
|
-
private stateAccountUpdateCallback = async (state: StateAccount) => {
|
|
118
|
-
if (
|
|
119
|
-
this.lastNumberOfSubAccounts === undefined ||
|
|
120
|
-
!state.numberOfSubAccounts.eq(this.lastNumberOfSubAccounts)
|
|
121
|
-
) {
|
|
122
|
-
await this.sync();
|
|
123
|
-
this.lastNumberOfSubAccounts = state.numberOfSubAccounts;
|
|
124
|
-
}
|
|
125
|
-
};
|
|
126
|
-
private decode: (name: string, buffer: Buffer) => UserAccount;
|
|
127
|
-
private mostRecentSlot = 0;
|
|
128
|
-
private syncConfig: SyncConfig;
|
|
129
|
-
|
|
130
|
-
private syncPromise?: Promise<void>;
|
|
131
|
-
private syncPromiseResolver: () => void = () => {};
|
|
132
|
-
|
|
133
|
-
private throwOnFailedSync: boolean;
|
|
134
|
-
|
|
135
|
-
/**
|
|
136
|
-
* Constructs a new UserMap instance.
|
|
137
|
-
*/
|
|
138
|
-
constructor(config: UserMapConfig) {
|
|
139
|
-
// Type-system guarantees at least one of the two is supplied.
|
|
140
|
-
this.velocityClient = config.velocityClient!;
|
|
141
|
-
if (config.connection) {
|
|
142
|
-
this.connection = config.connection;
|
|
143
|
-
} else {
|
|
144
|
-
this.connection = this.velocityClient.connection;
|
|
145
|
-
}
|
|
146
|
-
this.commitment =
|
|
147
|
-
(config.subscriptionConfig.type === 'websocket' ||
|
|
148
|
-
config.subscriptionConfig.type === 'polling'
|
|
149
|
-
? config.subscriptionConfig.commitment ??
|
|
150
|
-
this.velocityClient.opts?.commitment
|
|
151
|
-
: this.velocityClient.opts?.commitment) ?? 'confirmed';
|
|
152
|
-
this.includeIdle = config.includeIdle ?? false;
|
|
153
|
-
this.filterByPoolId = config.filterByPoolId;
|
|
154
|
-
this.additionalFilters = config.additionalFilters;
|
|
155
|
-
this.disableSyncOnTotalAccountsChange =
|
|
156
|
-
config.disableSyncOnTotalAccountsChange ?? false;
|
|
157
|
-
|
|
158
|
-
let decodeFn: (name: string, buffer: Buffer) => UserAccount;
|
|
159
|
-
if (config.fastDecode ?? true) {
|
|
160
|
-
decodeFn = (_name: string, buffer: Buffer) => decodeUser(buffer);
|
|
161
|
-
} else {
|
|
162
|
-
decodeFn = (
|
|
163
|
-
this.velocityClient.program.account as any
|
|
164
|
-
).user.coder.accounts.decodeUnchecked.bind(
|
|
165
|
-
(this.velocityClient.program.account as any).user.coder.accounts
|
|
166
|
-
);
|
|
167
|
-
}
|
|
168
|
-
this.decode = decodeFn;
|
|
169
|
-
|
|
170
|
-
if (config.subscriptionConfig.type === 'polling') {
|
|
171
|
-
this.subscription = new PollingSubscription({
|
|
172
|
-
userMap: this,
|
|
173
|
-
frequency: config.subscriptionConfig.frequency,
|
|
174
|
-
skipInitialLoad: config.skipInitialLoad,
|
|
175
|
-
});
|
|
176
|
-
} else if (config.subscriptionConfig.type === 'grpc') {
|
|
177
|
-
this.subscription = new grpcSubscription({
|
|
178
|
-
userMap: this,
|
|
179
|
-
grpcConfigs: config.subscriptionConfig.grpcConfigs,
|
|
180
|
-
resubOpts: {
|
|
181
|
-
resubTimeoutMs: config.subscriptionConfig.resubTimeoutMs,
|
|
182
|
-
logResubMessages: config.subscriptionConfig.logResubMessages,
|
|
183
|
-
},
|
|
184
|
-
skipInitialLoad: config.skipInitialLoad,
|
|
185
|
-
decodeFn,
|
|
186
|
-
});
|
|
187
|
-
} else {
|
|
188
|
-
this.subscription = new WebsocketSubscription({
|
|
189
|
-
userMap: this,
|
|
190
|
-
commitment: this.commitment,
|
|
191
|
-
resubOpts: {
|
|
192
|
-
resubTimeoutMs: config.subscriptionConfig.resubTimeoutMs,
|
|
193
|
-
logResubMessages: config.subscriptionConfig.logResubMessages,
|
|
194
|
-
},
|
|
195
|
-
skipInitialLoad: config.skipInitialLoad,
|
|
196
|
-
decodeFn,
|
|
197
|
-
});
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
this.syncConfig = config.syncConfig ?? {
|
|
201
|
-
type: 'default',
|
|
202
|
-
};
|
|
203
|
-
|
|
204
|
-
// Whether to throw an error if the userMap fails to sync. Defaults to false.
|
|
205
|
-
this.throwOnFailedSync = config.throwOnFailedSync ?? false;
|
|
206
|
-
this.eventEmitter = new EventEmitter();
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
/**
|
|
210
|
-
* Populates the map with a full initial `sync()` (no-op if already
|
|
211
|
-
* populated) and starts the configured live subscription
|
|
212
|
-
* (`'websocket'`/`'polling'`/`'grpc'`), plus (unless
|
|
213
|
-
* `disableSyncOnTotalAccountsChange`) a listener that triggers a full
|
|
214
|
-
* re-sync whenever `StateAccount.numberOfSubAccounts` changes.
|
|
215
|
-
*/
|
|
216
|
-
public async subscribe() {
|
|
217
|
-
if (this.size() > 0) {
|
|
218
|
-
return;
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
await this.velocityClient.subscribe();
|
|
222
|
-
this.lastNumberOfSubAccounts =
|
|
223
|
-
this.velocityClient.getStateAccount().numberOfSubAccounts;
|
|
224
|
-
if (!this.disableSyncOnTotalAccountsChange) {
|
|
225
|
-
this.velocityClient.eventEmitter.on(
|
|
226
|
-
'stateAccountUpdate',
|
|
227
|
-
this.stateAccountUpdateCallback
|
|
228
|
-
);
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
await this.subscription.subscribe();
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
/**
|
|
235
|
-
* Adds `userAccountPublicKey` to the map, creating a `User` for it.
|
|
236
|
-
*
|
|
237
|
-
* By default (`accountSubscription` omitted), subscribes it with a
|
|
238
|
-
* `OneShotUserAccountSubscriber` seeded from `userAccount`/`slot` rather
|
|
239
|
-
* than a live per-account websocket subscription — the map already gets
|
|
240
|
-
* live updates in bulk via its own subscription (`WebsocketSubscription`/
|
|
241
|
-
* `PollingSubscription`/`grpcSubscription`), so per-`User` subscriptions
|
|
242
|
-
* here would needlessly multiply RPC load.
|
|
243
|
-
* @param userAccount Optional pre-fetched account data to seed with, skipping an RPC fetch.
|
|
244
|
-
* @param slot Optional slot the data was observed at. Required (directly or via `userAccount`'s subscriber) — throws if no slot can be determined after subscribing.
|
|
245
|
-
* @param accountSubscription Optional override for how the created `User` subscribes; defaults to the one-shot subscriber described above.
|
|
246
|
-
* @throws If no slot is available after subscribing.
|
|
247
|
-
*/
|
|
248
|
-
public async addPubkey(
|
|
249
|
-
userAccountPublicKey: PublicKey,
|
|
250
|
-
userAccount?: UserAccount,
|
|
251
|
-
slot?: number,
|
|
252
|
-
accountSubscription?: UserSubscriptionConfig
|
|
253
|
-
) {
|
|
254
|
-
const user = new User({
|
|
255
|
-
velocityClient: this.velocityClient,
|
|
256
|
-
userAccountPublicKey,
|
|
257
|
-
accountSubscription: accountSubscription ?? {
|
|
258
|
-
type: 'custom',
|
|
259
|
-
// OneShotUserAccountSubscriber used here so we don't load up the RPC with AccountSubscribes
|
|
260
|
-
userAccountSubscriber: new OneShotUserAccountSubscriber(
|
|
261
|
-
this.velocityClient.program,
|
|
262
|
-
userAccountPublicKey,
|
|
263
|
-
userAccount,
|
|
264
|
-
slot,
|
|
265
|
-
this.commitment
|
|
266
|
-
),
|
|
267
|
-
},
|
|
268
|
-
});
|
|
269
|
-
await user.subscribe(userAccount);
|
|
270
|
-
const resolvedSlot = slot ?? user.getUserAccountAndSlot()?.slot;
|
|
271
|
-
if (resolvedSlot === undefined) {
|
|
272
|
-
throw new Error(
|
|
273
|
-
'UserMap.addPubkey: no slot available after subscribing user account'
|
|
274
|
-
);
|
|
275
|
-
}
|
|
276
|
-
this.userMap.set(userAccountPublicKey.toString(), {
|
|
277
|
-
data: user,
|
|
278
|
-
slot: resolvedSlot,
|
|
279
|
-
});
|
|
280
|
-
this.eventEmitter.emit('userUpdate', user);
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
/** Returns true if a `User` account keyed by `key` (the `User` account pubkey, base58) is cached in the map. */
|
|
284
|
-
public has(key: string): boolean {
|
|
285
|
-
return this.userMap.has(key);
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
/**
|
|
289
|
-
* gets the User for a particular userAccountPublicKey, if no User exists, undefined is returned
|
|
290
|
-
* @param key userAccountPublicKey to get User for
|
|
291
|
-
* @returns user User | undefined
|
|
292
|
-
*/
|
|
293
|
-
public get(key: string): User | undefined {
|
|
294
|
-
return this.userMap.get(key)?.data;
|
|
295
|
-
}
|
|
296
|
-
/** Like `get`, but also returns the slot at which the `User` account was last observed. */
|
|
297
|
-
public getWithSlot(key: string): DataAndSlot<User> | undefined {
|
|
298
|
-
return this.userMap.get(key);
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
/**
|
|
302
|
-
* gets the User for a particular userAccountPublicKey, if no User exists, new one is created
|
|
303
|
-
* @param key userAccountPublicKey to get User for
|
|
304
|
-
* @returns User
|
|
305
|
-
*/
|
|
306
|
-
public async mustGet(
|
|
307
|
-
key: string,
|
|
308
|
-
accountSubscription?: UserSubscriptionConfig
|
|
309
|
-
): Promise<User> {
|
|
310
|
-
if (!this.has(key)) {
|
|
311
|
-
await this.addPubkey(
|
|
312
|
-
new PublicKey(key),
|
|
313
|
-
undefined,
|
|
314
|
-
undefined,
|
|
315
|
-
accountSubscription
|
|
316
|
-
);
|
|
317
|
-
}
|
|
318
|
-
const userWithSlot = this.userMap.get(key);
|
|
319
|
-
if (!userWithSlot) {
|
|
320
|
-
throw new Error(`UserMap.mustGet: no user found for ${key}`);
|
|
321
|
-
}
|
|
322
|
-
return userWithSlot.data;
|
|
323
|
-
}
|
|
324
|
-
/** Like `mustGet`, but also returns the slot at which the `User` account was observed. */
|
|
325
|
-
public async mustGetWithSlot(
|
|
326
|
-
key: string,
|
|
327
|
-
accountSubscription?: UserSubscriptionConfig
|
|
328
|
-
): Promise<DataAndSlot<User>> {
|
|
329
|
-
if (!this.has(key)) {
|
|
330
|
-
await this.addPubkey(
|
|
331
|
-
new PublicKey(key),
|
|
332
|
-
undefined,
|
|
333
|
-
undefined,
|
|
334
|
-
accountSubscription
|
|
335
|
-
);
|
|
336
|
-
}
|
|
337
|
-
const userWithSlot = this.userMap.get(key);
|
|
338
|
-
if (!userWithSlot) {
|
|
339
|
-
throw new Error(`UserMap.mustGetWithSlot: no user found for ${key}`);
|
|
340
|
-
}
|
|
341
|
-
return userWithSlot;
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
/** Like `mustGet`, but returns the underlying `UserAccount` data directly (throws if the `User`'s account is not loaded). */
|
|
345
|
-
public async mustGetUserAccount(key: string): Promise<UserAccount> {
|
|
346
|
-
const user = await this.mustGet(key);
|
|
347
|
-
return user.getUserAccountOrThrow();
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
/**
|
|
351
|
-
* gets the Authority for a particular userAccountPublicKey, if no User exists, undefined is returned
|
|
352
|
-
* @param key userAccountPublicKey to get User for
|
|
353
|
-
* @returns authority PublicKey | undefined
|
|
354
|
-
*/
|
|
355
|
-
public getUserAuthority(key: string): PublicKey | undefined {
|
|
356
|
-
const user = this.userMap.get(key);
|
|
357
|
-
if (!user) {
|
|
358
|
-
return undefined;
|
|
359
|
-
}
|
|
360
|
-
return user.data.getUserAccount()?.authority;
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
/**
|
|
364
|
-
* Implements the `DLOBSource` interface: builds a `DLOB` from every
|
|
365
|
-
* subscribed user's open orders.
|
|
366
|
-
* @param slot Slot to consider orders "current" as of (auction/trigger timing).
|
|
367
|
-
*/
|
|
368
|
-
public async getDLOB(slot: number): Promise<DLOB> {
|
|
369
|
-
const dlob = new DLOB();
|
|
370
|
-
await dlob.initFromUserMap(this, slot);
|
|
371
|
-
return dlob;
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
/** Ensures an entry exists in the map for `record.user`, adding it via `addPubkey` if not already present. */
|
|
375
|
-
public async updateWithOrderRecord(record: OrderRecord) {
|
|
376
|
-
if (!this.has(record.user.toString())) {
|
|
377
|
-
await this.addPubkey(record.user);
|
|
378
|
-
}
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
/**
|
|
382
|
-
* Incrementally updates the map in response to a single program event,
|
|
383
|
-
* ensuring an entry exists for every `User` account the event references
|
|
384
|
-
* (deposit/funding/liquidation/order/order-action/settle-pnl/new-user
|
|
385
|
-
* records). Unrecognized event types are silently ignored.
|
|
386
|
-
*/
|
|
387
|
-
public async updateWithEventRecord(record: WrappedEvent<any>) {
|
|
388
|
-
if (record.eventType === 'DepositRecord') {
|
|
389
|
-
const depositRecord = record as DepositRecord;
|
|
390
|
-
await this.mustGet(depositRecord.user.toString());
|
|
391
|
-
} else if (record.eventType === 'FundingPaymentRecord') {
|
|
392
|
-
const fundingPaymentRecord = record as FundingPaymentRecord;
|
|
393
|
-
await this.mustGet(fundingPaymentRecord.user.toString());
|
|
394
|
-
} else if (record.eventType === 'LiquidationRecord') {
|
|
395
|
-
const liqRecord = record as LiquidationRecord;
|
|
396
|
-
|
|
397
|
-
await this.mustGet(liqRecord.user.toString());
|
|
398
|
-
await this.mustGet(liqRecord.liquidator.toString());
|
|
399
|
-
} else if (record.eventType === 'OrderRecord') {
|
|
400
|
-
const orderRecord = record as OrderRecord;
|
|
401
|
-
await this.updateWithOrderRecord(orderRecord);
|
|
402
|
-
} else if (record.eventType === 'OrderActionRecord') {
|
|
403
|
-
const actionRecord = record as OrderActionRecord;
|
|
404
|
-
|
|
405
|
-
if (actionRecord.taker) {
|
|
406
|
-
await this.mustGet(actionRecord.taker.toString());
|
|
407
|
-
}
|
|
408
|
-
if (actionRecord.maker) {
|
|
409
|
-
await this.mustGet(actionRecord.maker.toString());
|
|
410
|
-
}
|
|
411
|
-
} else if (record.eventType === 'SettlePnlRecord') {
|
|
412
|
-
const settlePnlRecord = record as SettlePnlRecord;
|
|
413
|
-
await this.mustGet(settlePnlRecord.user.toString());
|
|
414
|
-
} else if (record.eventType === 'NewUserRecord') {
|
|
415
|
-
const newUserRecord = record as NewUserRecord;
|
|
416
|
-
await this.mustGet(newUserRecord.user.toString());
|
|
417
|
-
}
|
|
418
|
-
}
|
|
419
|
-
|
|
420
|
-
/** Iterates all cached `User` instances. */
|
|
421
|
-
public *values(): IterableIterator<User> {
|
|
422
|
-
for (const dataAndSlot of this.userMap.values()) {
|
|
423
|
-
yield dataAndSlot.data;
|
|
424
|
-
}
|
|
425
|
-
}
|
|
426
|
-
/** Like `values`, but paired with the slot each `User` was last observed at. */
|
|
427
|
-
public valuesWithSlot(): IterableIterator<DataAndSlot<User>> {
|
|
428
|
-
return this.userMap.values();
|
|
429
|
-
}
|
|
430
|
-
|
|
431
|
-
/** Iterates all `[userAccountPublicKey, User]` pairs in the map. */
|
|
432
|
-
public *entries(): IterableIterator<[string, User]> {
|
|
433
|
-
for (const [key, dataAndSlot] of this.userMap.entries()) {
|
|
434
|
-
yield [key, dataAndSlot.data];
|
|
435
|
-
}
|
|
436
|
-
}
|
|
437
|
-
/** Like `entries`, but paired with the slot each `User` was last observed at. */
|
|
438
|
-
public entriesWithSlot(): IterableIterator<[string, DataAndSlot<User>]> {
|
|
439
|
-
return this.userMap.entries();
|
|
440
|
-
}
|
|
441
|
-
|
|
442
|
-
/** Number of `User` accounts currently cached in the map. */
|
|
443
|
-
public size(): number {
|
|
444
|
-
return this.userMap.size;
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
/**
|
|
448
|
-
* Returns a unique list of authorities for all users in the UserMap that meet the filter criteria
|
|
449
|
-
* @param filterCriteria: Users must meet these criteria to be included
|
|
450
|
-
* @returns
|
|
451
|
-
*/
|
|
452
|
-
public getUniqueAuthorities(
|
|
453
|
-
filterCriteria?: UserFilterCriteria
|
|
454
|
-
): PublicKey[] {
|
|
455
|
-
const usersMeetingCriteria = Array.from(this.values()).filter((user) => {
|
|
456
|
-
const userAccount = user.getUserAccountOrThrow();
|
|
457
|
-
if (filterCriteria && filterCriteria.hasOpenOrders) {
|
|
458
|
-
return userAccount.hasOpenOrder;
|
|
459
|
-
}
|
|
460
|
-
return true;
|
|
461
|
-
});
|
|
462
|
-
const userAuths = new Set(
|
|
463
|
-
usersMeetingCriteria.map((user) =>
|
|
464
|
-
user.getUserAccountOrThrow().authority.toBase58()
|
|
465
|
-
)
|
|
466
|
-
);
|
|
467
|
-
const userAuthKeys = Array.from(userAuths).map(
|
|
468
|
-
(userAuth) => new PublicKey(userAuth)
|
|
469
|
-
);
|
|
470
|
-
return userAuthKeys;
|
|
471
|
-
}
|
|
472
|
-
|
|
473
|
-
/** Runs a full sync using the strategy configured in `UserMapConfig.syncConfig` (`'default'` or `'paginated'` — see `SyncConfig`). */
|
|
474
|
-
public async sync() {
|
|
475
|
-
if (this.syncConfig.type === 'default') {
|
|
476
|
-
return this.defaultSync();
|
|
477
|
-
} else {
|
|
478
|
-
return this.paginatedSync();
|
|
479
|
-
}
|
|
480
|
-
}
|
|
481
|
-
|
|
482
|
-
/**
|
|
483
|
-
* Builds the memcmp filter set for both the initial sync and the live
|
|
484
|
-
* subscription: always the `User`-account discriminator filter; plus a
|
|
485
|
-
* non-idle filter unless `includeIdle`; plus a pool-id filter if
|
|
486
|
-
* `filterByPoolId` is set; plus any caller-supplied `additionalFilters`.
|
|
487
|
-
*/
|
|
488
|
-
private getFilters(): MemcmpFilter[] {
|
|
489
|
-
const filters = [getUserFilter()];
|
|
490
|
-
if (!this.includeIdle) {
|
|
491
|
-
filters.push(getNonIdleUserFilter());
|
|
492
|
-
}
|
|
493
|
-
if (this.filterByPoolId !== undefined) {
|
|
494
|
-
filters.push(getUsersWithPoolId(this.filterByPoolId));
|
|
495
|
-
}
|
|
496
|
-
if (this.additionalFilters) {
|
|
497
|
-
filters.push(...this.additionalFilters);
|
|
498
|
-
}
|
|
499
|
-
return filters;
|
|
500
|
-
}
|
|
501
|
-
|
|
502
|
-
/**
|
|
503
|
-
* Syncs the UserMap using the default sync method (single getProgramAccounts call with filters).
|
|
504
|
-
* This method may fail when velocity has too many users. (nodejs response size limits)
|
|
505
|
-
* @returns
|
|
506
|
-
*/
|
|
507
|
-
private async defaultSync() {
|
|
508
|
-
if (this.syncPromise) {
|
|
509
|
-
return this.syncPromise;
|
|
510
|
-
}
|
|
511
|
-
this.syncPromise = new Promise((resolver) => {
|
|
512
|
-
this.syncPromiseResolver = resolver;
|
|
513
|
-
});
|
|
514
|
-
|
|
515
|
-
try {
|
|
516
|
-
const rpcRequestArgs = [
|
|
517
|
-
this.velocityClient.program.programId.toBase58(),
|
|
518
|
-
{
|
|
519
|
-
commitment: this.commitment,
|
|
520
|
-
filters: this.getFilters(),
|
|
521
|
-
encoding: 'base64+zstd',
|
|
522
|
-
withContext: true,
|
|
523
|
-
},
|
|
524
|
-
];
|
|
525
|
-
|
|
526
|
-
// @ts-ignore
|
|
527
|
-
const rpcJSONResponse: any = await this.connection._rpcRequest(
|
|
528
|
-
'getProgramAccounts',
|
|
529
|
-
rpcRequestArgs
|
|
530
|
-
);
|
|
531
|
-
const rpcResponseAndContext: RpcResponseAndContext<
|
|
532
|
-
Array<{ pubkey: PublicKey; account: { data: [string, string] } }>
|
|
533
|
-
> = rpcJSONResponse.result;
|
|
534
|
-
const slot = rpcResponseAndContext.context.slot;
|
|
535
|
-
|
|
536
|
-
this.updateLatestSlot(slot);
|
|
537
|
-
|
|
538
|
-
const programAccountBufferMap = new Map<string, Buffer>();
|
|
539
|
-
const decodingPromises = rpcResponseAndContext.value.map(
|
|
540
|
-
async (programAccount) => {
|
|
541
|
-
const compressedUserData = Buffer.from(
|
|
542
|
-
programAccount.account.data[0],
|
|
543
|
-
'base64'
|
|
544
|
-
);
|
|
545
|
-
const decoder = new ZSTDDecoder();
|
|
546
|
-
await decoder.init();
|
|
547
|
-
const userBuffer = decoder.decode(
|
|
548
|
-
compressedUserData,
|
|
549
|
-
MAX_USER_ACCOUNT_SIZE_BYTES
|
|
550
|
-
);
|
|
551
|
-
programAccountBufferMap.set(
|
|
552
|
-
programAccount.pubkey.toString(),
|
|
553
|
-
Buffer.from(userBuffer)
|
|
554
|
-
);
|
|
555
|
-
}
|
|
556
|
-
);
|
|
557
|
-
|
|
558
|
-
await Promise.all(decodingPromises);
|
|
559
|
-
|
|
560
|
-
const promises = Array.from(programAccountBufferMap.entries()).map(
|
|
561
|
-
([key, buffer]) =>
|
|
562
|
-
(async () => {
|
|
563
|
-
const currAccountWithSlot = this.getWithSlot(key);
|
|
564
|
-
if (currAccountWithSlot) {
|
|
565
|
-
if (slot >= currAccountWithSlot.slot) {
|
|
566
|
-
const userAccount = this.decode('user', buffer);
|
|
567
|
-
this.updateUserAccount(key, userAccount, slot);
|
|
568
|
-
}
|
|
569
|
-
} else {
|
|
570
|
-
const userAccount = this.decode('user', buffer);
|
|
571
|
-
await this.addPubkey(new PublicKey(key), userAccount, slot);
|
|
572
|
-
}
|
|
573
|
-
})()
|
|
574
|
-
);
|
|
575
|
-
|
|
576
|
-
await Promise.all(promises);
|
|
577
|
-
|
|
578
|
-
for (const [key] of this.entries()) {
|
|
579
|
-
if (!programAccountBufferMap.has(key)) {
|
|
580
|
-
const user = this.get(key);
|
|
581
|
-
if (user) {
|
|
582
|
-
await user.unsubscribe();
|
|
583
|
-
this.userMap.delete(key);
|
|
584
|
-
}
|
|
585
|
-
}
|
|
586
|
-
}
|
|
587
|
-
} catch (err) {
|
|
588
|
-
const e = err as Error;
|
|
589
|
-
console.error(`Error in UserMap.sync(): ${e.message} ${e.stack ?? ''}`);
|
|
590
|
-
if (this.throwOnFailedSync) {
|
|
591
|
-
throw e;
|
|
592
|
-
}
|
|
593
|
-
} finally {
|
|
594
|
-
this.syncPromiseResolver();
|
|
595
|
-
this.syncPromise = undefined;
|
|
596
|
-
}
|
|
597
|
-
}
|
|
598
|
-
|
|
599
|
-
/**
|
|
600
|
-
* Syncs the UserMap using the paginated sync method (multiple getMultipleAccounts calls with filters).
|
|
601
|
-
* This method is more reliable when velocity has many users.
|
|
602
|
-
* @returns
|
|
603
|
-
*/
|
|
604
|
-
private async paginatedSync() {
|
|
605
|
-
if (this.syncPromise) {
|
|
606
|
-
return this.syncPromise;
|
|
607
|
-
}
|
|
608
|
-
|
|
609
|
-
this.syncPromise = new Promise<void>((resolve) => {
|
|
610
|
-
this.syncPromiseResolver = resolve;
|
|
611
|
-
});
|
|
612
|
-
|
|
613
|
-
try {
|
|
614
|
-
const accountsPrefetch = await this.connection.getProgramAccounts(
|
|
615
|
-
this.velocityClient.program.programId,
|
|
616
|
-
{
|
|
617
|
-
dataSlice: { offset: 0, length: 0 },
|
|
618
|
-
filters: this.getFilters(),
|
|
619
|
-
}
|
|
620
|
-
);
|
|
621
|
-
const accountPublicKeys = accountsPrefetch.map(
|
|
622
|
-
(account) => account.pubkey
|
|
623
|
-
);
|
|
624
|
-
|
|
625
|
-
const limitConcurrency = async (
|
|
626
|
-
tasks: Array<() => Promise<void>>,
|
|
627
|
-
limit: number
|
|
628
|
-
) => {
|
|
629
|
-
const executing: Promise<void>[] = [];
|
|
630
|
-
const results: Promise<void>[] = [];
|
|
631
|
-
|
|
632
|
-
for (let i = 0; i < tasks.length; i++) {
|
|
633
|
-
const executor = Promise.resolve().then(tasks[i]);
|
|
634
|
-
results.push(executor);
|
|
635
|
-
|
|
636
|
-
if (executing.length < limit) {
|
|
637
|
-
executing.push(executor);
|
|
638
|
-
executor.finally(() => {
|
|
639
|
-
const index = executing.indexOf(executor);
|
|
640
|
-
if (index > -1) {
|
|
641
|
-
executing.splice(index, 1);
|
|
642
|
-
}
|
|
643
|
-
});
|
|
644
|
-
} else {
|
|
645
|
-
await Promise.race(executing);
|
|
646
|
-
}
|
|
647
|
-
}
|
|
648
|
-
|
|
649
|
-
return Promise.all(results);
|
|
650
|
-
};
|
|
651
|
-
|
|
652
|
-
const programAccountBufferMap = new Map<string, Buffer>();
|
|
653
|
-
|
|
654
|
-
// @ts-ignore
|
|
655
|
-
const chunkSize = this.syncConfig.chunkSize ?? 100;
|
|
656
|
-
const tasks: Array<() => Promise<void>> = [];
|
|
657
|
-
for (let i = 0; i < accountPublicKeys.length; i += chunkSize) {
|
|
658
|
-
const chunk = accountPublicKeys.slice(i, i + chunkSize);
|
|
659
|
-
tasks.push(async () => {
|
|
660
|
-
const accountInfos =
|
|
661
|
-
await this.connection.getMultipleAccountsInfoAndContext(chunk, {
|
|
662
|
-
commitment: this.commitment,
|
|
663
|
-
});
|
|
664
|
-
|
|
665
|
-
const accountInfosSlot = accountInfos.context.slot;
|
|
666
|
-
|
|
667
|
-
for (let j = 0; j < accountInfos.value.length; j += 1) {
|
|
668
|
-
const accountInfo = accountInfos.value[j];
|
|
669
|
-
if (accountInfo === null) continue;
|
|
670
|
-
|
|
671
|
-
const publicKeyString = chunk[j].toString();
|
|
672
|
-
const buffer = Buffer.from(accountInfo.data);
|
|
673
|
-
programAccountBufferMap.set(publicKeyString, buffer);
|
|
674
|
-
|
|
675
|
-
const decodedUser = this.decode('user', buffer);
|
|
676
|
-
|
|
677
|
-
const currAccountWithSlot = this.getWithSlot(publicKeyString);
|
|
678
|
-
if (
|
|
679
|
-
currAccountWithSlot &&
|
|
680
|
-
currAccountWithSlot.slot <= accountInfosSlot
|
|
681
|
-
) {
|
|
682
|
-
this.updateUserAccount(
|
|
683
|
-
publicKeyString,
|
|
684
|
-
decodedUser,
|
|
685
|
-
accountInfosSlot
|
|
686
|
-
);
|
|
687
|
-
} else {
|
|
688
|
-
await this.addPubkey(
|
|
689
|
-
new PublicKey(publicKeyString),
|
|
690
|
-
decodedUser,
|
|
691
|
-
accountInfosSlot
|
|
692
|
-
);
|
|
693
|
-
}
|
|
694
|
-
}
|
|
695
|
-
});
|
|
696
|
-
}
|
|
697
|
-
|
|
698
|
-
// @ts-ignore
|
|
699
|
-
const concurrencyLimit = this.syncConfig.concurrencyLimit ?? 10;
|
|
700
|
-
await limitConcurrency(tasks, concurrencyLimit);
|
|
701
|
-
|
|
702
|
-
for (const [key] of this.entries()) {
|
|
703
|
-
if (!programAccountBufferMap.has(key)) {
|
|
704
|
-
const user = this.get(key);
|
|
705
|
-
if (user) {
|
|
706
|
-
await user.unsubscribe();
|
|
707
|
-
this.userMap.delete(key);
|
|
708
|
-
}
|
|
709
|
-
}
|
|
710
|
-
}
|
|
711
|
-
} catch (err) {
|
|
712
|
-
console.error(`Error in UserMap.sync():`, err);
|
|
713
|
-
if (this.throwOnFailedSync) {
|
|
714
|
-
throw err;
|
|
715
|
-
}
|
|
716
|
-
} finally {
|
|
717
|
-
this.syncPromiseResolver();
|
|
718
|
-
this.syncPromise = undefined;
|
|
719
|
-
}
|
|
720
|
-
}
|
|
721
|
-
|
|
722
|
-
/**
|
|
723
|
-
* Tears down the live subscription, unsubscribes and removes every cached
|
|
724
|
-
* `User`, and (if registered) removes the `stateAccountUpdate` listener
|
|
725
|
-
* that triggers auto-resync on `numberOfSubAccounts` changes.
|
|
726
|
-
*/
|
|
727
|
-
public async unsubscribe() {
|
|
728
|
-
await this.subscription.unsubscribe();
|
|
729
|
-
|
|
730
|
-
for (const [key, user] of this.entries()) {
|
|
731
|
-
await user.unsubscribe();
|
|
732
|
-
this.userMap.delete(key);
|
|
733
|
-
}
|
|
734
|
-
|
|
735
|
-
if (this.lastNumberOfSubAccounts) {
|
|
736
|
-
if (!this.disableSyncOnTotalAccountsChange) {
|
|
737
|
-
this.velocityClient.eventEmitter.removeListener(
|
|
738
|
-
'stateAccountUpdate',
|
|
739
|
-
this.stateAccountUpdateCallback
|
|
740
|
-
);
|
|
741
|
-
}
|
|
742
|
-
|
|
743
|
-
this.lastNumberOfSubAccounts = undefined;
|
|
744
|
-
}
|
|
745
|
-
}
|
|
746
|
-
|
|
747
|
-
/**
|
|
748
|
-
* Applies a fresh `userAccount` observation for `key` at `slot`. If the
|
|
749
|
-
* user is already cached, updates in place only if `slot` is at least as
|
|
750
|
-
* new as the cached slot (stale/out-of-order updates are dropped) and
|
|
751
|
-
* emits `'userUpdate'`. If not cached yet, adds it via `addPubkey`.
|
|
752
|
-
* Also advances `getSlot()`'s tracked most-recent slot.
|
|
753
|
-
*/
|
|
754
|
-
public async updateUserAccount(
|
|
755
|
-
key: string,
|
|
756
|
-
userAccount: UserAccount,
|
|
757
|
-
slot: number
|
|
758
|
-
) {
|
|
759
|
-
const userWithSlot = this.getWithSlot(key);
|
|
760
|
-
this.updateLatestSlot(slot);
|
|
761
|
-
if (userWithSlot) {
|
|
762
|
-
if (slot >= userWithSlot.slot) {
|
|
763
|
-
userWithSlot.data.accountSubscriber.updateData(userAccount, slot);
|
|
764
|
-
this.userMap.set(key, {
|
|
765
|
-
data: userWithSlot.data,
|
|
766
|
-
slot,
|
|
767
|
-
});
|
|
768
|
-
this.eventEmitter.emit('userUpdate', userWithSlot.data);
|
|
769
|
-
}
|
|
770
|
-
} else {
|
|
771
|
-
await this.addPubkey(new PublicKey(key), userAccount, slot);
|
|
772
|
-
}
|
|
773
|
-
}
|
|
774
|
-
|
|
775
|
-
/** Advances the map's tracked most-recent slot to `slot` if it's newer. */
|
|
776
|
-
updateLatestSlot(slot: number): void {
|
|
777
|
-
this.mostRecentSlot = Math.max(slot, this.mostRecentSlot);
|
|
778
|
-
}
|
|
779
|
-
|
|
780
|
-
/** Returns the most recent slot at which any account update has been observed. */
|
|
781
|
-
public getSlot(): number {
|
|
782
|
-
return this.mostRecentSlot;
|
|
783
|
-
}
|
|
784
|
-
}
|