@drift-labs/sdk 2.96.0-beta.0 → 2.96.0-beta.10

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/VERSION CHANGED
@@ -1 +1 @@
1
- 2.96.0-beta.0
1
+ 2.96.0-beta.10
@@ -778,6 +778,16 @@ exports.MainnetPerpMarkets = [
778
778
  oracleSource: __1.OracleSource.PYTH_PULL,
779
779
  pythFeedId: '0x8963217838ab4cf5cadc172203c1f0b763fbaa45f346d8ee50ba994bbcac3026',
780
780
  },
781
+ {
782
+ fullName: 'LANDO-F1-SGP-WIN-BET',
783
+ category: ['Prediction', 'Sports'],
784
+ symbol: 'LANDO-F1-SGP-WIN-BET',
785
+ baseAssetSymbol: 'LANDO-F1-SGP-WIN',
786
+ marketIndex: 43,
787
+ oracle: new web3_js_1.PublicKey('DpJz7rjTJLxxnuqrqZTUjMWtnaMFAEfZUv5ATdb9HTh1'),
788
+ launchTs: 1726646453000,
789
+ oracleSource: __1.OracleSource.Prelaunch,
790
+ },
781
791
  ];
782
792
  exports.PerpMarkets = {
783
793
  devnet: exports.DevnetPerpMarkets,
@@ -94,7 +94,7 @@ exports.MainnetSpotMarkets = [
94
94
  precision: new __1.BN(10).pow(numericConstants_1.EIGHT),
95
95
  precisionExp: numericConstants_1.EIGHT,
96
96
  serumMarket: new web3_js_1.PublicKey('3BAKsQd3RuhZKES2DGysMhjBdwjZYKYmxRqnSMtZ4KSN'),
97
- pythFeedId: '0xe62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43',
97
+ pythFeedId: '0xc9d8b075a5c69303365ae23633d4e085199bf5c520a3b90fed1322a0342ffc33',
98
98
  },
99
99
  {
100
100
  symbol: 'wETH',
@@ -280,6 +280,7 @@ exports.MainnetSpotMarkets = [
280
280
  precision: new __1.BN(10).pow(numericConstants_1.SIX),
281
281
  precisionExp: numericConstants_1.SIX,
282
282
  launchTs: 1719415157000,
283
+ pythFeedId: '0xc811abc82b4bad1f9bd711a2773ccaa935b03ecef974236942cec5e0eb845a3a',
283
284
  },
284
285
  {
285
286
  symbol: 'POPCAT',
@@ -3,7 +3,7 @@
3
3
  /// <reference types="node" />
4
4
  import { AnchorProvider, BN, Program, ProgramAccount } from '@coral-xyz/anchor';
5
5
  import { Idl as Idl30, Program as Program30 } from '@coral-xyz/anchor-30';
6
- import { StateAccount, IWallet, PositionDirection, UserAccount, PerpMarketAccount, OrderParams, Order, SpotMarketAccount, SpotPosition, MakerInfo, TakerInfo, OptionalOrderParams, ReferrerInfo, MarketType, TxParams, SerumV3FulfillmentConfigAccount, ReferrerNameAccount, OrderTriggerCondition, PerpMarketExtendedInfo, UserStatsAccount, PhoenixV1FulfillmentConfigAccount, ModifyOrderPolicy, SwapReduceOnly, SettlePnlMode, SignedTxData, MappedRecord, OpenbookV2FulfillmentConfigAccount } from './types';
6
+ import { StateAccount, IWallet, PositionDirection, UserAccount, PerpMarketAccount, OrderParams, Order, SpotMarketAccount, SpotPosition, MakerInfo, TakerInfo, OptionalOrderParams, ReferrerInfo, MarketType, TxParams, SerumV3FulfillmentConfigAccount, ReferrerNameAccount, OrderTriggerCondition, PerpMarketExtendedInfo, UserStatsAccount, PhoenixV1FulfillmentConfigAccount, ModifyOrderPolicy, SwapReduceOnly, SettlePnlMode, SignedTxData, MappedRecord, OpenbookV2FulfillmentConfigAccount, SwiftOrderParamsMessage } from './types';
7
7
  import * as anchor from '@coral-xyz/anchor';
8
8
  import { Connection, PublicKey, TransactionSignature, ConfirmOptions, Transaction, TransactionInstruction, AccountMeta, Signer, AddressLookupTableAccount, TransactionVersion, VersionedTransaction, BlockhashWithExpiryBlockHeight } from '@solana/web3.js';
9
9
  import { TokenFaucet } from './tokenFaucet';
@@ -239,6 +239,7 @@ export declare class DriftClient {
239
239
  */
240
240
  getAssociatedTokenAccount(marketIndex: number, useNative?: boolean, tokenProgram?: anchor.web3.PublicKey): Promise<PublicKey>;
241
241
  createAssociatedTokenAccountIdempotentInstruction(account: PublicKey, payer: PublicKey, owner: PublicKey, mint: PublicKey, tokenProgram?: anchor.web3.PublicKey): TransactionInstruction;
242
+ getDepositTxnIx(amount: BN, marketIndex: number, associatedTokenAccount: PublicKey, subAccountId?: number, reduceOnly?: boolean): Promise<TransactionInstruction[]>;
242
243
  createDepositTxn(amount: BN, marketIndex: number, associatedTokenAccount: PublicKey, subAccountId?: number, reduceOnly?: boolean, txParams?: TxParams): Promise<VersionedTransaction | Transaction>;
243
244
  /**
244
245
  * Deposit funds into the given spot market
@@ -261,6 +262,10 @@ export declare class DriftClient {
261
262
  getTokenProgramForSpotMarket(spotMarketAccount: SpotMarketAccount): PublicKey;
262
263
  addTokenMintToRemainingAccounts(spotMarketAccount: SpotMarketAccount, remainingAccounts: AccountMeta[]): void;
263
264
  getAssociatedTokenAccountCreationIx(tokenMintAddress: PublicKey, associatedTokenAddress: PublicKey, tokenProgram: PublicKey): anchor.web3.TransactionInstruction;
265
+ createInitializeUserAccountAndDepositCollateralIxs(amount: BN, userTokenAccount: PublicKey, marketIndex?: number, subAccountId?: number, name?: string, fromSubAccountId?: number, referrerInfo?: ReferrerInfo, donateAmount?: BN, customMaxMarginRatio?: number): Promise<{
266
+ ixs: TransactionInstruction[];
267
+ userAccountPublicKey: PublicKey;
268
+ }>;
264
269
  createInitializeUserAccountAndDepositCollateral(amount: BN, userTokenAccount: PublicKey, marketIndex?: number, subAccountId?: number, name?: string, fromSubAccountId?: number, referrerInfo?: ReferrerInfo, donateAmount?: BN, txParams?: TxParams, customMaxMarginRatio?: number): Promise<[Transaction | VersionedTransaction, PublicKey]>;
265
270
  /**
266
271
  * Creates the User account for a user, and deposits some initial collateral
@@ -340,7 +345,10 @@ export declare class DriftClient {
340
345
  signedSettlePnlTx?: Transaction;
341
346
  }>;
342
347
  placePerpOrder(orderParams: OptionalOrderParams, txParams?: TxParams, subAccountId?: number): Promise<TransactionSignature>;
343
- getPlacePerpOrderIx(orderParams: OptionalOrderParams, subAccountId?: number): Promise<TransactionInstruction>;
348
+ getPlacePerpOrderIx(orderParams: OptionalOrderParams, subAccountId?: number, depositToTradeArgs?: {
349
+ isMakingNewAccount: boolean;
350
+ depositMarketIndex: number;
351
+ }): Promise<TransactionInstruction>;
344
352
  updateAMMs(marketIndexes: number[], txParams?: TxParams): Promise<TransactionSignature>;
345
353
  getUpdateAMMsIx(marketIndexes: number[]): Promise<TransactionInstruction>;
346
354
  settleExpiredMarket(marketIndex: number, txParams?: TxParams): Promise<TransactionSignature>;
@@ -497,6 +505,38 @@ export declare class DriftClient {
497
505
  getPlaceAndTakePerpOrderIx(orderParams: OptionalOrderParams, makerInfo?: MakerInfo | MakerInfo[], referrerInfo?: ReferrerInfo, subAccountId?: number): Promise<TransactionInstruction>;
498
506
  placeAndMakePerpOrder(orderParams: OptionalOrderParams, takerInfo: TakerInfo, referrerInfo?: ReferrerInfo, txParams?: TxParams, subAccountId?: number): Promise<TransactionSignature>;
499
507
  getPlaceAndMakePerpOrderIx(orderParams: OptionalOrderParams, takerInfo: TakerInfo, referrerInfo?: ReferrerInfo, subAccountId?: number): Promise<TransactionInstruction>;
508
+ signTakerOrderParams(orderParamsMessage: SwiftOrderParamsMessage): Promise<Buffer>;
509
+ getEncodedSwiftOrderParamsMessage(orderParamsMessage: SwiftOrderParamsMessage): Buffer;
510
+ decodeSwiftTakerOrderParamsMessage(encodedMessage: Buffer): SwiftOrderParamsMessage;
511
+ signMessage(message: Uint8Array): Promise<Buffer>;
512
+ assembleSwiftServerMessage(message: Buffer, signature: Buffer, takerPubkey: PublicKey, marketIndex: number, marketType: MarketType, slot: number | BN): {
513
+ message: string;
514
+ signature: string;
515
+ taker_pubkey: string;
516
+ market_index: number;
517
+ market_type: 'perp' | 'spot';
518
+ slot: BN;
519
+ };
520
+ placeSwiftTakerOrder(takerOrderParamsMessage: Buffer, takerSignature: Buffer, marketIndex: number, takerInfo: {
521
+ taker: PublicKey;
522
+ takerStats: PublicKey;
523
+ takerUserAccount: UserAccount;
524
+ }, txParams?: TxParams): Promise<TransactionSignature>;
525
+ getPlaceSwiftTakerPerpOrderIx(takerOrderParamsMessage: Buffer, takerSignature: Buffer, marketIndex: number, takerInfo: {
526
+ taker: PublicKey;
527
+ takerStats: PublicKey;
528
+ takerUserAccount: UserAccount;
529
+ }): Promise<TransactionInstruction[]>;
530
+ placeAndMakeSwiftPerpOrder(encodedTakerOrderParamsMessage: Buffer, takerSignature: Buffer, takerExpectedOrderId: number, takerInfo: {
531
+ taker: PublicKey;
532
+ takerStats: PublicKey;
533
+ takerUserAccount: UserAccount;
534
+ }, orderParams: OptionalOrderParams, referrerInfo?: ReferrerInfo, txParams?: TxParams, subAccountId?: number): Promise<TransactionSignature>;
535
+ getPlaceAndMakeSwiftPerpOrderIxs(encodedTakerOrderParamsMessage: Buffer, takerSignature: Buffer, takerExpectedOrderId: number, takerInfo: {
536
+ taker: PublicKey;
537
+ takerStats: PublicKey;
538
+ takerUserAccount: UserAccount;
539
+ }, orderParams: OptionalOrderParams, referrerInfo?: ReferrerInfo, subAccountId?: number): Promise<TransactionInstruction[]>;
500
540
  preparePlaceAndTakeSpotOrder(orderParams: OptionalOrderParams, fulfillmentConfig?: SerumV3FulfillmentConfigAccount, makerInfo?: MakerInfo, referrerInfo?: ReferrerInfo, txParams?: TxParams, subAccountId?: number): Promise<{
501
541
  placeAndTakeSpotOrderTx: anchor.web3.Transaction | anchor.web3.VersionedTransaction;
502
542
  }>;
@@ -68,6 +68,7 @@ const utils_2 = require("./tx/utils");
68
68
  const pyth_solana_receiver_json_1 = __importDefault(require("./idl/pyth_solana_receiver.json"));
69
69
  const on_demand_1 = require("@switchboard-xyz/on-demand");
70
70
  const switchboard_on_demand_30_json_1 = __importDefault(require("./idl/switchboard_on_demand_30.json"));
71
+ const ed = __importStar(require("@noble/ed25519"));
71
72
  /**
72
73
  * # DriftClient
73
74
  * This class is the main way to interact with Drift Protocol. It allows you to subscribe to the various accounts where the Market's state is stored, as well as: opening positions, liquidating, settling funding, depositing & withdrawing, and more.
@@ -109,6 +110,7 @@ class DriftClient {
109
110
  this.txHandler =
110
111
  (_m = config === null || config === void 0 ? void 0 : config.txHandler) !== null && _m !== void 0 ? _m : new txHandler_1.TxHandler({
111
112
  connection: this.connection,
113
+ // @ts-ignore
112
114
  wallet: this.provider.wallet,
113
115
  confirmationOptions: this.opts,
114
116
  opts: {
@@ -1118,7 +1120,7 @@ class DriftClient {
1118
1120
  data: Buffer.from([0x1]),
1119
1121
  });
1120
1122
  }
1121
- async createDepositTxn(amount, marketIndex, associatedTokenAccount, subAccountId, reduceOnly = false, txParams) {
1123
+ async getDepositTxnIx(amount, marketIndex, associatedTokenAccount, subAccountId, reduceOnly = false) {
1122
1124
  const spotMarketAccount = this.getSpotMarketAccount(marketIndex);
1123
1125
  const isSolMarket = spotMarketAccount.mint.equals(spotMarkets_1.WRAPPED_SOL_MINT);
1124
1126
  const signerAuthority = this.wallet.publicKey;
@@ -1135,6 +1137,10 @@ class DriftClient {
1135
1137
  if (createWSOLTokenAccount) {
1136
1138
  instructions.push((0, spl_token_1.createCloseAccountInstruction)(associatedTokenAccount, signerAuthority, signerAuthority, []));
1137
1139
  }
1140
+ return instructions;
1141
+ }
1142
+ async createDepositTxn(amount, marketIndex, associatedTokenAccount, subAccountId, reduceOnly = false, txParams) {
1143
+ const instructions = await this.getDepositTxnIx(amount, marketIndex, associatedTokenAccount, subAccountId, reduceOnly);
1138
1144
  txParams = { ...(txParams !== null && txParams !== void 0 ? txParams : this.txParams), computeUnits: 600000 };
1139
1145
  const tx = await this.buildTransaction(instructions, txParams);
1140
1146
  return tx;
@@ -1242,7 +1248,7 @@ class DriftClient {
1242
1248
  getAssociatedTokenAccountCreationIx(tokenMintAddress, associatedTokenAddress, tokenProgram) {
1243
1249
  return (0, spl_token_1.createAssociatedTokenAccountInstruction)(this.wallet.publicKey, associatedTokenAddress, this.wallet.publicKey, tokenMintAddress, tokenProgram);
1244
1250
  }
1245
- async createInitializeUserAccountAndDepositCollateral(amount, userTokenAccount, marketIndex = 0, subAccountId = 0, name, fromSubAccountId, referrerInfo, donateAmount, txParams, customMaxMarginRatio) {
1251
+ async createInitializeUserAccountAndDepositCollateralIxs(amount, userTokenAccount, marketIndex = 0, subAccountId = 0, name, fromSubAccountId, referrerInfo, donateAmount, customMaxMarginRatio) {
1246
1252
  const ixs = [];
1247
1253
  const [userAccountPublicKey, initializeUserAccountIx] = await this.getInitializeUserInstructions(subAccountId, name, referrerInfo);
1248
1254
  const spotMarket = this.getSpotMarketAccount(marketIndex);
@@ -1288,6 +1294,13 @@ class DriftClient {
1288
1294
  if (createWSOLTokenAccount) {
1289
1295
  ixs.push((0, spl_token_1.createCloseAccountInstruction)(wsolTokenAccount, authority, authority, []));
1290
1296
  }
1297
+ return {
1298
+ ixs,
1299
+ userAccountPublicKey,
1300
+ };
1301
+ }
1302
+ async createInitializeUserAccountAndDepositCollateral(amount, userTokenAccount, marketIndex = 0, subAccountId = 0, name, fromSubAccountId, referrerInfo, donateAmount, txParams, customMaxMarginRatio) {
1303
+ const { ixs, userAccountPublicKey } = await this.createInitializeUserAccountAndDepositCollateralIxs(amount, userTokenAccount, marketIndex, subAccountId, name, fromSubAccountId, referrerInfo, donateAmount, customMaxMarginRatio);
1291
1304
  const tx = await this.buildTransaction(ixs, txParams);
1292
1305
  return [tx, userAccountPublicKey];
1293
1306
  }
@@ -1663,13 +1676,21 @@ class DriftClient {
1663
1676
  this.perpMarketLastSlotCache.set(orderParams.marketIndex, slot);
1664
1677
  return txSig;
1665
1678
  }
1666
- async getPlacePerpOrderIx(orderParams, subAccountId) {
1679
+ async getPlacePerpOrderIx(orderParams, subAccountId, depositToTradeArgs) {
1667
1680
  orderParams = (0, orderParams_1.getOrderParams)(orderParams, { marketType: types_1.MarketType.PERP });
1668
- const user = await this.getUserAccountPublicKey(subAccountId);
1681
+ const isDepositToTradeTx = depositToTradeArgs !== undefined;
1682
+ const user = isDepositToTradeTx
1683
+ ? (0, pda_1.getUserAccountPublicKeySync)(this.program.programId, this.authority, subAccountId)
1684
+ : await this.getUserAccountPublicKey(subAccountId);
1669
1685
  const remainingAccounts = this.getRemainingAccounts({
1670
- userAccounts: [this.getUserAccount(subAccountId)],
1671
- useMarketLastSlotCache: true,
1686
+ userAccounts: (depositToTradeArgs === null || depositToTradeArgs === void 0 ? void 0 : depositToTradeArgs.isMakingNewAccount)
1687
+ ? []
1688
+ : [this.getUserAccount(subAccountId)],
1689
+ useMarketLastSlotCache: false,
1672
1690
  readablePerpMarketIndex: orderParams.marketIndex,
1691
+ readableSpotMarketIndexes: isDepositToTradeTx
1692
+ ? [depositToTradeArgs === null || depositToTradeArgs === void 0 ? void 0 : depositToTradeArgs.depositMarketIndex]
1693
+ : undefined,
1673
1694
  });
1674
1695
  return await this.program.instruction.placePerpOrder(orderParams, {
1675
1696
  accounts: {
@@ -2781,7 +2802,9 @@ class DriftClient {
2781
2802
  if (!txsToSign) {
2782
2803
  return null;
2783
2804
  }
2784
- const signedTxs = (await this.txHandler.getSignedTransactionMap(txsToSign, this.provider.wallet)).signedTxMap;
2805
+ const signedTxs = (await this.txHandler.getSignedTransactionMap(txsToSign,
2806
+ // @ts-ignore
2807
+ this.provider.wallet)).signedTxMap;
2785
2808
  const { txSig, slot } = await this.sendTransaction(signedTxs.placeAndTakeTx, [], this.opts, true);
2786
2809
  this.perpMarketLastSlotCache.set(orderParams.marketIndex, slot);
2787
2810
  return {
@@ -2888,6 +2911,101 @@ class DriftClient {
2888
2911
  remainingAccounts,
2889
2912
  });
2890
2913
  }
2914
+ async signTakerOrderParams(orderParamsMessage) {
2915
+ const takerOrderParamsMessage = Uint8Array.from(this.getEncodedSwiftOrderParamsMessage(orderParamsMessage));
2916
+ return await this.signMessage(takerOrderParamsMessage);
2917
+ }
2918
+ getEncodedSwiftOrderParamsMessage(orderParamsMessage) {
2919
+ return this.program.coder.types.encode('SwiftOrderParamsMessage', orderParamsMessage);
2920
+ }
2921
+ decodeSwiftTakerOrderParamsMessage(encodedMessage) {
2922
+ return this.program.coder.types.decode('SwiftOrderParamsMessage', encodedMessage);
2923
+ }
2924
+ async signMessage(message) {
2925
+ return Buffer.from(await ed.sign(message, this.wallet.payer.secretKey.slice(0, 32)));
2926
+ }
2927
+ assembleSwiftServerMessage(message, signature, takerPubkey, marketIndex, marketType, slot) {
2928
+ return {
2929
+ message: message.toString('base64'),
2930
+ signature: signature.toString('base64'),
2931
+ taker_pubkey: takerPubkey.toBase58(),
2932
+ market_index: marketIndex,
2933
+ market_type: (0, types_1.isVariant)(marketType, 'perp') ? 'perp' : 'spot',
2934
+ slot: typeof slot === 'number' ? new anchor_1.BN(slot) : slot,
2935
+ };
2936
+ }
2937
+ async placeSwiftTakerOrder(takerOrderParamsMessage, takerSignature, marketIndex, takerInfo, txParams) {
2938
+ const ixs = await this.getPlaceSwiftTakerPerpOrderIx(takerOrderParamsMessage, takerSignature, marketIndex, takerInfo);
2939
+ const { txSig } = await this.sendTransaction(await this.buildTransaction(ixs, txParams), [], this.opts);
2940
+ return txSig;
2941
+ }
2942
+ async getPlaceSwiftTakerPerpOrderIx(takerOrderParamsMessage, takerSignature, marketIndex, takerInfo) {
2943
+ const remainingAccounts = this.getRemainingAccounts({
2944
+ userAccounts: [takerInfo.takerUserAccount],
2945
+ useMarketLastSlotCache: true,
2946
+ readablePerpMarketIndex: marketIndex,
2947
+ });
2948
+ const signatureIx = web3_js_1.Ed25519Program.createInstructionWithPublicKey({
2949
+ publicKey: takerInfo.takerUserAccount.authority.toBytes(),
2950
+ signature: Uint8Array.from(takerSignature),
2951
+ message: Uint8Array.from(takerOrderParamsMessage),
2952
+ });
2953
+ const placeTakerSwiftPerpOrderIx = await this.program.instruction.placeSwiftTakerOrder(takerOrderParamsMessage, takerSignature, {
2954
+ accounts: {
2955
+ state: await this.getStatePublicKey(),
2956
+ user: takerInfo.taker,
2957
+ userStats: takerInfo.takerStats,
2958
+ authority: this.wallet.publicKey,
2959
+ ixSysvar: web3_js_1.SYSVAR_INSTRUCTIONS_PUBKEY,
2960
+ },
2961
+ remainingAccounts,
2962
+ });
2963
+ return [signatureIx, placeTakerSwiftPerpOrderIx];
2964
+ }
2965
+ async placeAndMakeSwiftPerpOrder(encodedTakerOrderParamsMessage, takerSignature, takerExpectedOrderId, takerInfo, orderParams, referrerInfo, txParams, subAccountId) {
2966
+ const ixs = await this.getPlaceAndMakeSwiftPerpOrderIxs(encodedTakerOrderParamsMessage, takerSignature, takerExpectedOrderId, takerInfo, orderParams, referrerInfo, subAccountId);
2967
+ const { txSig, slot } = await this.sendTransaction(await this.buildTransaction(ixs, txParams), [], this.opts);
2968
+ this.perpMarketLastSlotCache.set(orderParams.marketIndex, slot);
2969
+ return txSig;
2970
+ }
2971
+ async getPlaceAndMakeSwiftPerpOrderIxs(encodedTakerOrderParamsMessage, takerSignature, takerExpectedOrderId, takerInfo, orderParams, referrerInfo, subAccountId) {
2972
+ const [signatureIx, placeTakerSwiftPerpOrderIx] = await this.getPlaceSwiftTakerPerpOrderIx(encodedTakerOrderParamsMessage, takerSignature, orderParams.marketIndex, takerInfo);
2973
+ orderParams = (0, orderParams_1.getOrderParams)(orderParams, { marketType: types_1.MarketType.PERP });
2974
+ const userStatsPublicKey = this.getUserStatsAccountPublicKey();
2975
+ const user = await this.getUserAccountPublicKey(subAccountId);
2976
+ const remainingAccounts = this.getRemainingAccounts({
2977
+ userAccounts: [
2978
+ this.getUserAccount(subAccountId),
2979
+ takerInfo.takerUserAccount,
2980
+ ],
2981
+ useMarketLastSlotCache: true,
2982
+ writablePerpMarketIndexes: [orderParams.marketIndex],
2983
+ });
2984
+ if (referrerInfo) {
2985
+ remainingAccounts.push({
2986
+ pubkey: referrerInfo.referrer,
2987
+ isWritable: true,
2988
+ isSigner: false,
2989
+ });
2990
+ remainingAccounts.push({
2991
+ pubkey: referrerInfo.referrerStats,
2992
+ isWritable: true,
2993
+ isSigner: false,
2994
+ });
2995
+ }
2996
+ const placeAndMakeIx = await this.program.instruction.placeAndMakePerpOrder(orderParams, takerExpectedOrderId, {
2997
+ accounts: {
2998
+ state: await this.getStatePublicKey(),
2999
+ user,
3000
+ userStats: userStatsPublicKey,
3001
+ taker: takerInfo.taker,
3002
+ takerStats: takerInfo.takerStats,
3003
+ authority: this.wallet.publicKey,
3004
+ },
3005
+ remainingAccounts,
3006
+ });
3007
+ return [signatureIx, placeTakerSwiftPerpOrderIx, placeAndMakeIx];
3008
+ }
2891
3009
  async preparePlaceAndTakeSpotOrder(orderParams, fulfillmentConfig, makerInfo, referrerInfo, txParams, subAccountId) {
2892
3010
  const tx = await this.buildTransaction(await this.getPlaceAndTakeSpotOrderIx(orderParams, fulfillmentConfig, makerInfo, referrerInfo, subAccountId), txParams);
2893
3011
  return {
@@ -15,12 +15,19 @@ export declare class EventSubscriber {
15
15
  private awaitTxPromises;
16
16
  private awaitTxResolver;
17
17
  private logProvider;
18
+ private currentProviderType;
18
19
  eventEmitter: StrictEventEmitter<EventEmitter, EventSubscriberEvents>;
19
20
  private lastSeenSlot;
20
21
  private lastSeenBlockTime;
21
22
  lastSeenTxSig: string;
22
23
  constructor(connection: Connection, program: Program, options?: EventSubscriptionOptions);
24
+ private initializeLogProvider;
23
25
  private populateInitialEventListMap;
26
+ /**
27
+ * Implements fallback logic for reconnecting to LogProvider. Currently terminates at polling,
28
+ * could be improved to try the original type again after some cooldown.
29
+ */
30
+ private updateFallbackProviderType;
24
31
  subscribe(): Promise<boolean>;
25
32
  private handleTxLogs;
26
33
  fetchPreviousTx(fetchMax?: boolean): Promise<void>;
@@ -10,6 +10,7 @@ const webSocketLogProvider_1 = require("./webSocketLogProvider");
10
10
  const events_1 = require("events");
11
11
  const sort_1 = require("./sort");
12
12
  const parse_1 = require("./parse");
13
+ const eventsServerLogProvider_1 = require("./eventsServerLogProvider");
13
14
  class EventSubscriber {
14
15
  constructor(connection, program, options = types_1.DefaultEventSubscriptionOptions) {
15
16
  var _a;
@@ -23,15 +24,37 @@ class EventSubscriber {
23
24
  this.txEventCache = new txEventCache_1.TxEventCache(this.options.maxTx);
24
25
  this.eventListMap = new Map();
25
26
  this.eventEmitter = new events_1.EventEmitter();
26
- if (this.options.logProviderConfig.type === 'websocket') {
27
+ this.currentProviderType = this.options.logProviderConfig.type;
28
+ this.initializeLogProvider();
29
+ }
30
+ initializeLogProvider(subscribe = false) {
31
+ const logProviderConfig = this.options.logProviderConfig;
32
+ if (this.currentProviderType === 'websocket') {
27
33
  this.logProvider = new webSocketLogProvider_1.WebSocketLogProvider(
28
34
  // @ts-ignore
29
35
  this.connection, this.address, this.options.commitment, this.options.logProviderConfig.resubTimeoutMs);
30
36
  }
31
- else {
37
+ else if (this.currentProviderType === 'polling') {
38
+ const frequency = 'frequency' in logProviderConfig
39
+ ? logProviderConfig.frequency
40
+ : logProviderConfig.fallbackFrequency;
41
+ const batchSize = 'batchSize' in logProviderConfig
42
+ ? logProviderConfig.batchSize
43
+ : logProviderConfig.fallbackBatchSize;
32
44
  this.logProvider = new pollingLogProvider_1.PollingLogProvider(
33
45
  // @ts-ignore
34
- this.connection, this.address, options.commitment, this.options.logProviderConfig.frequency, this.options.logProviderConfig.batchSize);
46
+ this.connection, this.address, this.options.commitment, frequency, batchSize);
47
+ }
48
+ else if (this.currentProviderType === 'events-server') {
49
+ this.logProvider = new eventsServerLogProvider_1.EventsServerLogProvider(logProviderConfig.url, this.options.eventTypes, this.options.address ? this.options.address.toString() : undefined);
50
+ }
51
+ else {
52
+ throw new Error(`Invalid log provider type: ${this.currentProviderType}`);
53
+ }
54
+ if (subscribe) {
55
+ this.logProvider.subscribe((txSig, slot, logs, mostRecentBlockTime, txSigIndex) => {
56
+ this.handleTxLogs(txSig, slot, logs, mostRecentBlockTime, this.currentProviderType === 'events-server', txSigIndex);
57
+ }, true);
35
58
  }
36
59
  }
37
60
  populateInitialEventListMap() {
@@ -39,37 +62,51 @@ class EventSubscriber {
39
62
  this.eventListMap.set(eventType, new eventList_1.EventList(eventType, this.options.maxEventsPerType, (0, sort_1.getSortFn)(this.options.orderBy, this.options.orderDir), this.options.orderDir));
40
63
  }
41
64
  }
65
+ /**
66
+ * Implements fallback logic for reconnecting to LogProvider. Currently terminates at polling,
67
+ * could be improved to try the original type again after some cooldown.
68
+ */
69
+ updateFallbackProviderType(reconnectAttempts, maxReconnectAttempts) {
70
+ if (reconnectAttempts < maxReconnectAttempts) {
71
+ return;
72
+ }
73
+ let nextProviderType = this.currentProviderType;
74
+ if (this.currentProviderType === 'events-server') {
75
+ nextProviderType = 'websocket';
76
+ }
77
+ else if (this.currentProviderType === 'websocket') {
78
+ nextProviderType = 'polling';
79
+ }
80
+ else if (this.currentProviderType === 'polling') {
81
+ nextProviderType = 'polling';
82
+ }
83
+ console.log(`EventSubscriber: Failing over providerType ${this.currentProviderType} to ${nextProviderType}`);
84
+ this.currentProviderType = nextProviderType;
85
+ }
42
86
  async subscribe() {
43
87
  try {
44
88
  if (this.logProvider.isSubscribed()) {
45
89
  return true;
46
90
  }
47
91
  this.populateInitialEventListMap();
48
- if (this.options.logProviderConfig.type === 'websocket') {
49
- if (this.options.logProviderConfig.resubTimeoutMs) {
50
- if (this.options.logProviderConfig.maxReconnectAttempts &&
51
- this.options.logProviderConfig.maxReconnectAttempts > 0) {
52
- const logProviderConfig = this.options
53
- .logProviderConfig;
54
- this.logProvider.eventEmitter.on('reconnect', (reconnectAttempts) => {
55
- if (reconnectAttempts > logProviderConfig.maxReconnectAttempts) {
56
- console.log('Failing over to polling');
57
- this.logProvider.eventEmitter.removeAllListeners('reconnect');
58
- this.unsubscribe().then(() => {
59
- this.logProvider = new pollingLogProvider_1.PollingLogProvider(
60
- // @ts-ignore
61
- this.connection, this.address, this.options.commitment, logProviderConfig.fallbackFrequency, logProviderConfig.fallbackBatchSize);
62
- this.logProvider.subscribe((txSig, slot, logs, mostRecentBlockTime) => {
63
- this.handleTxLogs(txSig, slot, logs, mostRecentBlockTime);
64
- }, true);
65
- });
66
- }
67
- });
68
- }
92
+ if (this.options.logProviderConfig.type === 'websocket' ||
93
+ this.options.logProviderConfig.type === 'events-server') {
94
+ const logProviderConfig = this.options
95
+ .logProviderConfig;
96
+ if (this.logProvider.eventEmitter) {
97
+ this.logProvider.eventEmitter.on('reconnect', async (reconnectAttempts) => {
98
+ if (reconnectAttempts > logProviderConfig.maxReconnectAttempts) {
99
+ console.log(`EventSubscriber: Reconnect attempts ${reconnectAttempts}/${logProviderConfig.maxReconnectAttempts}, reconnecting...`);
100
+ this.logProvider.eventEmitter.removeAllListeners('reconnect');
101
+ await this.unsubscribe();
102
+ this.updateFallbackProviderType(reconnectAttempts, logProviderConfig.maxReconnectAttempts);
103
+ this.initializeLogProvider(true);
104
+ }
105
+ });
69
106
  }
70
107
  }
71
- this.logProvider.subscribe((txSig, slot, logs, mostRecentBlockTime) => {
72
- this.handleTxLogs(txSig, slot, logs, mostRecentBlockTime);
108
+ this.logProvider.subscribe((txSig, slot, logs, mostRecentBlockTime, txSigIndex) => {
109
+ this.handleTxLogs(txSig, slot, logs, mostRecentBlockTime, this.currentProviderType === 'events-server', txSigIndex);
73
110
  }, true);
74
111
  return true;
75
112
  }
@@ -79,11 +116,11 @@ class EventSubscriber {
79
116
  return false;
80
117
  }
81
118
  }
82
- handleTxLogs(txSig, slot, logs, mostRecentBlockTime) {
83
- if (this.txEventCache.has(txSig)) {
119
+ handleTxLogs(txSig, slot, logs, mostRecentBlockTime, fromEventsServer = false, txSigIndex = undefined) {
120
+ if (!fromEventsServer && this.txEventCache.has(txSig)) {
84
121
  return;
85
122
  }
86
- const wrappedEvents = this.parseEventsFromLogs(txSig, slot, logs);
123
+ const wrappedEvents = this.parseEventsFromLogs(txSig, slot, logs, txSigIndex);
87
124
  for (const wrappedEvent of wrappedEvents) {
88
125
  this.eventListMap.get(wrappedEvent.eventType).insert(wrappedEvent);
89
126
  }
@@ -134,7 +171,7 @@ class EventSubscriber {
134
171
  this.awaitTxResolver.clear();
135
172
  return await this.logProvider.unsubscribe(true);
136
173
  }
137
- parseEventsFromLogs(txSig, slot, logs) {
174
+ parseEventsFromLogs(txSig, slot, logs, txSigIndex) {
138
175
  const records = [];
139
176
  // @ts-ignore
140
177
  const events = (0, parse_1.parseLogs)(this.program, logs);
@@ -146,7 +183,8 @@ class EventSubscriber {
146
183
  event.data.txSig = txSig;
147
184
  event.data.slot = slot;
148
185
  event.data.eventType = event.name;
149
- event.data.txSigIndex = runningEventIndex;
186
+ event.data.txSigIndex =
187
+ txSigIndex !== undefined ? txSigIndex : runningEventIndex;
150
188
  records.push(event.data);
151
189
  }
152
190
  runningEventIndex++;
@@ -0,0 +1,21 @@
1
+ /// <reference types="node" />
2
+ import { logProviderCallback, EventType, LogProvider } from './types';
3
+ import { EventEmitter } from 'events';
4
+ export declare class EventsServerLogProvider implements LogProvider {
5
+ private readonly url;
6
+ private readonly eventTypes;
7
+ private readonly userAccount?;
8
+ private ws?;
9
+ private callback?;
10
+ private isUnsubscribing;
11
+ private externalUnsubscribe;
12
+ private lastHeartbeat;
13
+ private timeoutId?;
14
+ private reconnectAttempts;
15
+ eventEmitter?: EventEmitter;
16
+ constructor(url: string, eventTypes: EventType[], userAccount?: string);
17
+ isSubscribed(): boolean;
18
+ subscribe(callback: logProviderCallback): Promise<boolean>;
19
+ unsubscribe(external?: boolean): Promise<boolean>;
20
+ private setTimeout;
21
+ }
@@ -0,0 +1,121 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.EventsServerLogProvider = void 0;
4
+ const events_1 = require("events");
5
+ // browser support
6
+ let WebSocketImpl;
7
+ if (typeof window !== 'undefined' && window.WebSocket) {
8
+ WebSocketImpl = window.WebSocket;
9
+ }
10
+ else {
11
+ WebSocketImpl = require('ws');
12
+ }
13
+ const EVENT_SERVER_HEARTBEAT_INTERVAL_MS = 5000;
14
+ const ALLOWED_MISSED_HEARTBEATS = 3;
15
+ class EventsServerLogProvider {
16
+ constructor(url, eventTypes, userAccount) {
17
+ this.url = url;
18
+ this.eventTypes = eventTypes;
19
+ this.userAccount = userAccount;
20
+ this.isUnsubscribing = false;
21
+ this.externalUnsubscribe = false;
22
+ this.lastHeartbeat = 0;
23
+ this.reconnectAttempts = 0;
24
+ this.eventEmitter = new events_1.EventEmitter();
25
+ }
26
+ isSubscribed() {
27
+ return this.ws !== undefined;
28
+ }
29
+ async subscribe(callback) {
30
+ if (this.ws !== undefined) {
31
+ return true;
32
+ }
33
+ this.ws = new WebSocketImpl(this.url);
34
+ this.callback = callback;
35
+ this.ws.addEventListener('open', () => {
36
+ for (const channel of this.eventTypes) {
37
+ const subscribeMessage = {
38
+ type: 'subscribe',
39
+ channel: channel,
40
+ };
41
+ if (this.userAccount) {
42
+ subscribeMessage['user'] = this.userAccount;
43
+ }
44
+ this.ws.send(JSON.stringify(subscribeMessage));
45
+ }
46
+ this.reconnectAttempts = 0;
47
+ });
48
+ this.ws.addEventListener('message', (data) => {
49
+ try {
50
+ if (!this.isUnsubscribing) {
51
+ clearTimeout(this.timeoutId);
52
+ this.setTimeout();
53
+ if (this.reconnectAttempts > 0) {
54
+ console.log('eventsServerLogProvider: Resetting reconnect attempts to 0');
55
+ }
56
+ this.reconnectAttempts = 0;
57
+ }
58
+ const parsedData = JSON.parse(data.data.toString());
59
+ if (parsedData.channel === 'heartbeat') {
60
+ this.lastHeartbeat = Date.now();
61
+ return;
62
+ }
63
+ if (parsedData.message !== undefined) {
64
+ return;
65
+ }
66
+ const event = JSON.parse(parsedData.data);
67
+ this.callback(event.txSig, event.slot, [
68
+ 'Program dRiftyHA39MWEi3m9aunc5MzRF1JYuBsbn6VPcn33UH invoke [1]',
69
+ event.rawLog,
70
+ 'Program dRiftyHA39MWEi3m9aunc5MzRF1JYuBsbn6VPcn33UH success',
71
+ ], undefined, event.txSigIndex);
72
+ }
73
+ catch (error) {
74
+ console.error('Error parsing message:', error);
75
+ }
76
+ });
77
+ this.ws.addEventListener('close', () => {
78
+ console.log('eventsServerLogProvider: WebSocket closed');
79
+ });
80
+ this.ws.addEventListener('error', (error) => {
81
+ console.error('eventsServerLogProvider: WebSocket error:', error);
82
+ });
83
+ this.setTimeout();
84
+ return true;
85
+ }
86
+ async unsubscribe(external = false) {
87
+ this.isUnsubscribing = true;
88
+ this.externalUnsubscribe = external;
89
+ if (this.timeoutId) {
90
+ clearInterval(this.timeoutId);
91
+ this.timeoutId = undefined;
92
+ }
93
+ if (this.ws !== undefined) {
94
+ this.ws.close();
95
+ this.ws = undefined;
96
+ return true;
97
+ }
98
+ else {
99
+ this.isUnsubscribing = false;
100
+ return true;
101
+ }
102
+ }
103
+ setTimeout() {
104
+ this.timeoutId = setTimeout(async () => {
105
+ if (this.isUnsubscribing || this.externalUnsubscribe) {
106
+ // If we are in the process of unsubscribing, do not attempt to resubscribe
107
+ return;
108
+ }
109
+ const timeSinceLastHeartbeat = Date.now() - this.lastHeartbeat;
110
+ if (timeSinceLastHeartbeat >
111
+ EVENT_SERVER_HEARTBEAT_INTERVAL_MS * ALLOWED_MISSED_HEARTBEATS) {
112
+ console.log(`eventServerLogProvider: No heartbeat in ${timeSinceLastHeartbeat}ms, resubscribing on attempt ${this.reconnectAttempts + 1}`);
113
+ await this.unsubscribe();
114
+ this.reconnectAttempts++;
115
+ this.eventEmitter.emit('reconnect', this.reconnectAttempts);
116
+ this.subscribe(this.callback);
117
+ }
118
+ }, EVENT_SERVER_HEARTBEAT_INTERVAL_MS * 2);
119
+ }
120
+ }
121
+ exports.EventsServerLogProvider = EventsServerLogProvider;
@@ -30,7 +30,7 @@ class PollingLogProvider {
30
30
  this.firstFetch = false;
31
31
  const { mostRecentTx, transactionLogs } = response;
32
32
  for (const { txSig, slot, logs } of transactionLogs) {
33
- callback(txSig, slot, logs, response.mostRecentBlockTime);
33
+ callback(txSig, slot, logs, response.mostRecentBlockTime, undefined);
34
34
  }
35
35
  this.mostRecentSeenTx = mostRecentTx;
36
36
  }