@drift-labs/sdk 2.85.0-beta.8 → 2.86.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/VERSION +1 -1
  2. package/bun.lockb +0 -0
  3. package/lib/addresses/pda.d.ts +1 -0
  4. package/lib/addresses/pda.js +8 -1
  5. package/lib/adminClient.d.ts +2 -0
  6. package/lib/adminClient.js +22 -0
  7. package/lib/blockhashSubscriber/BlockhashSubscriber.js +22 -15
  8. package/lib/config.d.ts +3 -0
  9. package/lib/config.js +4 -1
  10. package/lib/constants/perpMarkets.d.ts +1 -0
  11. package/lib/constants/perpMarkets.js +57 -0
  12. package/lib/constants/spotMarkets.d.ts +1 -0
  13. package/lib/constants/spotMarkets.js +20 -0
  14. package/lib/driftClient.d.ts +26 -1
  15. package/lib/driftClient.js +157 -31
  16. package/lib/driftClientConfig.d.ts +2 -1
  17. package/lib/idl/drift.json +161 -2
  18. package/lib/math/oracles.d.ts +2 -0
  19. package/lib/math/oracles.js +14 -1
  20. package/lib/oracles/pythPullClient.js +1 -1
  21. package/lib/tx/baseTxSender.js +1 -0
  22. package/lib/tx/blockhashFetcher/baseBlockhashFetcher.d.ts +8 -0
  23. package/lib/tx/blockhashFetcher/baseBlockhashFetcher.js +13 -0
  24. package/lib/tx/blockhashFetcher/cachedBlockhashFetcher.d.ts +28 -0
  25. package/lib/tx/blockhashFetcher/cachedBlockhashFetcher.js +73 -0
  26. package/lib/tx/blockhashFetcher/types.d.ts +4 -0
  27. package/lib/tx/blockhashFetcher/types.js +2 -0
  28. package/lib/tx/txHandler.d.ts +10 -0
  29. package/lib/tx/txHandler.js +16 -7
  30. package/lib/tx/utils.d.ts +2 -0
  31. package/lib/tx/utils.js +10 -0
  32. package/lib/util/pythPullOracleUtils.d.ts +2 -0
  33. package/lib/util/pythPullOracleUtils.js +15 -0
  34. package/package.json +3 -1
  35. package/src/addresses/pda.ts +13 -0
  36. package/src/adminClient.ts +39 -0
  37. package/src/blockhashSubscriber/BlockhashSubscriber.ts +24 -19
  38. package/src/config.ts +6 -0
  39. package/src/constants/perpMarkets.ts +115 -0
  40. package/src/constants/spotMarkets.ts +41 -0
  41. package/src/driftClient.ts +347 -41
  42. package/src/driftClientConfig.ts +2 -1
  43. package/src/idl/drift.json +161 -2
  44. package/src/math/oracles.ts +17 -0
  45. package/src/oracles/pythPullClient.ts +2 -3
  46. package/src/tx/baseTxSender.ts +1 -0
  47. package/src/tx/blockhashFetcher/baseBlockhashFetcher.ts +19 -0
  48. package/src/tx/blockhashFetcher/cachedBlockhashFetcher.ts +90 -0
  49. package/src/tx/blockhashFetcher/types.ts +5 -0
  50. package/src/tx/txHandler.ts +38 -4
  51. package/src/tx/utils.ts +11 -0
  52. package/src/util/pythPullOracleUtils.ts +11 -0
  53. package/tests/tx/cachedBlockhashFetcher.test.ts +96 -0
@@ -21,6 +21,7 @@ export type SpotMarketConfig = {
21
21
  serumMarket?: PublicKey;
22
22
  phoenixMarket?: PublicKey;
23
23
  launchTs?: number;
24
+ pythFeedId?: string;
24
25
  };
25
26
 
26
27
  export const WRAPPED_SOL_MINT = new PublicKey(
@@ -36,6 +37,8 @@ export const DevnetSpotMarkets: SpotMarketConfig[] = [
36
37
  mint: new PublicKey('8zGuJQqwhZafTah7Uc7Z4tXRnguqkn5KLFAP8oV6PHe2'),
37
38
  precision: new BN(10).pow(SIX),
38
39
  precisionExp: SIX,
40
+ pythFeedId:
41
+ '0xeaa020c61cc479712813461ce153894a96a6c00b21ed0cfc2798d1f9a9e9c94a',
39
42
  },
40
43
  {
41
44
  symbol: 'SOL',
@@ -49,6 +52,8 @@ export const DevnetSpotMarkets: SpotMarketConfig[] = [
49
52
  phoenixMarket: new PublicKey(
50
53
  '78ehDnHgbkFxqXZwdFxa8HK7saX58GymeX2wNGdkqYLp'
51
54
  ),
55
+ pythFeedId:
56
+ '0xef0d8b6fda2ceba41da15d4095d1da392a0d2f8ed0c6c7bc0f4cfac8c280b56d',
52
57
  },
53
58
  {
54
59
  symbol: 'BTC',
@@ -59,6 +64,8 @@ export const DevnetSpotMarkets: SpotMarketConfig[] = [
59
64
  precision: new BN(10).pow(SIX),
60
65
  precisionExp: SIX,
61
66
  serumMarket: new PublicKey('AGsmbVu3MS9u68GEYABWosQQCZwmLcBHu4pWEuBYH7Za'),
67
+ pythFeedId:
68
+ '0xe62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43',
62
69
  },
63
70
  ];
64
71
 
@@ -71,6 +78,8 @@ export const MainnetSpotMarkets: SpotMarketConfig[] = [
71
78
  mint: new PublicKey('EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v'),
72
79
  precision: QUOTE_PRECISION,
73
80
  precisionExp: QUOTE_PRECISION_EXP,
81
+ pythFeedId:
82
+ '0xeaa020c61cc479712813461ce153894a96a6c00b21ed0cfc2798d1f9a9e9c94a',
74
83
  },
75
84
  {
76
85
  symbol: 'SOL',
@@ -84,6 +93,8 @@ export const MainnetSpotMarkets: SpotMarketConfig[] = [
84
93
  phoenixMarket: new PublicKey(
85
94
  '4DoNfFBfF7UokCC2FQzriy7yHK6DY6NVdYpuekQ5pRgg'
86
95
  ),
96
+ pythFeedId:
97
+ '0xef0d8b6fda2ceba41da15d4095d1da392a0d2f8ed0c6c7bc0f4cfac8c280b56d',
87
98
  },
88
99
  {
89
100
  symbol: 'mSOL',
@@ -94,6 +105,8 @@ export const MainnetSpotMarkets: SpotMarketConfig[] = [
94
105
  precision: new BN(10).pow(NINE),
95
106
  precisionExp: NINE,
96
107
  serumMarket: new PublicKey('9Lyhks5bQQxb9EyyX55NtgKQzpM4WK7JCmeaWuQ5MoXD'),
108
+ pythFeedId:
109
+ '0xc2289a6a43d2ce91c6f55caec370f4acc38a2ed477f58813334c6d03749ff2a4',
97
110
  },
98
111
  {
99
112
  symbol: 'wBTC',
@@ -104,6 +117,8 @@ export const MainnetSpotMarkets: SpotMarketConfig[] = [
104
117
  precision: new BN(10).pow(EIGHT),
105
118
  precisionExp: EIGHT,
106
119
  serumMarket: new PublicKey('3BAKsQd3RuhZKES2DGysMhjBdwjZYKYmxRqnSMtZ4KSN'),
120
+ pythFeedId:
121
+ '0xe62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43',
107
122
  },
108
123
  {
109
124
  symbol: 'wETH',
@@ -117,6 +132,8 @@ export const MainnetSpotMarkets: SpotMarketConfig[] = [
117
132
  phoenixMarket: new PublicKey(
118
133
  'Ew3vFDdtdGrknJAVVfraxCA37uNJtimXYPY4QjnfhFHH'
119
134
  ),
135
+ pythFeedId:
136
+ '0xff61491a931112ddf1bd8147cd1b641375f79f5825126d665480874634fd0ace',
120
137
  },
121
138
  {
122
139
  symbol: 'USDT',
@@ -127,6 +144,8 @@ export const MainnetSpotMarkets: SpotMarketConfig[] = [
127
144
  precision: QUOTE_PRECISION,
128
145
  precisionExp: QUOTE_PRECISION_EXP,
129
146
  serumMarket: new PublicKey('B2na8Awyd7cpC59iEU43FagJAPLigr3AP3s38KM982bu'),
147
+ pythFeedId:
148
+ '0x2b89b9dc8fdf9f34709a5b106b472f0f39bb6ca9ce04b0fd7f2e971688e2e53b',
130
149
  },
131
150
  {
132
151
  symbol: 'jitoSOL',
@@ -140,6 +159,8 @@ export const MainnetSpotMarkets: SpotMarketConfig[] = [
140
159
  phoenixMarket: new PublicKey(
141
160
  '5LQLfGtqcC5rm2WuGxJf4tjqYmDjsQAbKo2AMLQ8KB7p'
142
161
  ),
162
+ pythFeedId:
163
+ '0x67be9f519b95cf24338801051f9a808eff0a578ccb388db73b7f6fe1de019ffb',
143
164
  },
144
165
  {
145
166
  symbol: 'PYTH',
@@ -153,6 +174,8 @@ export const MainnetSpotMarkets: SpotMarketConfig[] = [
153
174
  phoenixMarket: new PublicKey(
154
175
  '2sTMN9A1D1qeZLF95XQgJCUPiKe5DiV52jLfZGqMP46m'
155
176
  ),
177
+ pythFeedId:
178
+ '0x0bbf28e9a841a1cc788f6a361b17ca072d0ea3098a1e5df1c3922d06719579ff',
156
179
  },
157
180
  {
158
181
  symbol: 'bSOL',
@@ -163,6 +186,8 @@ export const MainnetSpotMarkets: SpotMarketConfig[] = [
163
186
  precision: new BN(10).pow(NINE),
164
187
  precisionExp: NINE,
165
188
  serumMarket: new PublicKey('ARjaHVxGCQfTvvKjLd7U7srvk6orthZSE6uqWchCczZc'),
189
+ pythFeedId:
190
+ '0x89875379e70f8fbadc17aef315adf3a8d5d160b811435537e03c97e8aac97d9c',
166
191
  },
167
192
  {
168
193
  symbol: 'JTO',
@@ -176,6 +201,8 @@ export const MainnetSpotMarkets: SpotMarketConfig[] = [
176
201
  phoenixMarket: new PublicKey(
177
202
  'BRLLmdtPGuuFn3BU6orYw4KHaohAEptBToi3dwRUnHQZ'
178
203
  ),
204
+ pythFeedId:
205
+ '0xb43660a5f790c69354b0729a5ef9d50d68f1df92107540210b9cccba1f947cc2',
179
206
  },
180
207
  {
181
208
  symbol: 'WIF',
@@ -189,6 +216,8 @@ export const MainnetSpotMarkets: SpotMarketConfig[] = [
189
216
  phoenixMarket: new PublicKey(
190
217
  '6ojSigXF7nDPyhFRgmn3V9ywhYseKF9J32ZrranMGVSX'
191
218
  ),
219
+ pythFeedId:
220
+ '0x4ca4beeca86f0d164160323817a4e42b10010a724c2217c6ee41b54cd4cc61fc',
192
221
  },
193
222
  {
194
223
  symbol: 'JUP',
@@ -202,6 +231,8 @@ export const MainnetSpotMarkets: SpotMarketConfig[] = [
202
231
  '2pspvjWWaf3dNgt3jsgSzFCNvMGPb7t8FrEYvLGjvcCe'
203
232
  ),
204
233
  launchTs: 1706731200000,
234
+ pythFeedId:
235
+ '0x0a0408d619e9380abad35060f9192039ed5042fa6f82301d0e48bb52be830996',
205
236
  },
206
237
  {
207
238
  symbol: 'RNDR',
@@ -213,6 +244,8 @@ export const MainnetSpotMarkets: SpotMarketConfig[] = [
213
244
  precisionExp: EIGHT,
214
245
  serumMarket: new PublicKey('2m7ZLEKtxWF29727DSb5D91erpXPUY1bqhRWRC3wQX7u'),
215
246
  launchTs: 1708964021000,
247
+ pythFeedId:
248
+ '0xab7347771135fc733f8f38db462ba085ed3309955f42554a14fa13e855ac0e2f',
216
249
  },
217
250
  {
218
251
  symbol: 'W',
@@ -226,6 +259,8 @@ export const MainnetSpotMarkets: SpotMarketConfig[] = [
226
259
  '8dFTCTAbtGuHsdDL8WEPrTU6pXFDrU1QSjBTutw8fwZk'
227
260
  ),
228
261
  launchTs: 1712149014000,
262
+ pythFeedId:
263
+ '0xeff7446475e218517566ea99e72a4abec2e1bd8498b43b7d8331e29dcb059389',
229
264
  },
230
265
  {
231
266
  symbol: 'TNSR',
@@ -239,6 +274,8 @@ export const MainnetSpotMarkets: SpotMarketConfig[] = [
239
274
  'AbJCZ9TAJiby5AY3cHcXS2gUdENC6mtsm6m7XpC2ZMvE'
240
275
  ),
241
276
  launchTs: 1712593532000,
277
+ pythFeedId:
278
+ '0x05ecd4597cd48fe13d6cc3596c62af4f9675aee06e2e0b94c06d8bee2b659e05',
242
279
  },
243
280
  {
244
281
  symbol: 'DRIFT',
@@ -252,6 +289,8 @@ export const MainnetSpotMarkets: SpotMarketConfig[] = [
252
289
  '8BV6rrWsUabnTDA3dE6A69oUDJAj3hMhtBHTJyXB7czp'
253
290
  ),
254
291
  launchTs: 1715860800000,
292
+ pythFeedId:
293
+ '0x5c1690b27bb02446db17cdda13ccc2c1d609ad6d2ef5bf4983a85ea8b6f19d07',
255
294
  },
256
295
  {
257
296
  symbol: 'INF',
@@ -262,6 +301,8 @@ export const MainnetSpotMarkets: SpotMarketConfig[] = [
262
301
  precision: new BN(10).pow(NINE),
263
302
  precisionExp: NINE,
264
303
  launchTs: 1716595200000,
304
+ pythFeedId:
305
+ '0xf51570985c642c49c2d6e50156390fdba80bb6d5f7fa389d2f012ced4f7d208f',
265
306
  },
266
307
  {
267
308
  symbol: 'dSOL',
@@ -76,6 +76,7 @@ import {
76
76
  getInsuranceFundStakeAccountPublicKey,
77
77
  getPerpMarketPublicKey,
78
78
  getPhoenixFulfillmentConfigPublicKey,
79
+ getPythPullOraclePublicKey,
79
80
  getReferrerNamePublicKeySync,
80
81
  getSerumFulfillmentConfigPublicKey,
81
82
  getSerumSignerPublicKey,
@@ -127,8 +128,23 @@ import { getMarinadeDepositIx, getMarinadeFinanceProgram } from './marinade';
127
128
  import { getOrderParams } from './orderParams';
128
129
  import { numberToSafeBN } from './math/utils';
129
130
  import { TransactionParamProcessor } from './tx/txParamProcessor';
130
- import { isOracleValid } from './math/oracles';
131
+ import { isOracleValid, trimVaaSignatures } from './math/oracles';
131
132
  import { TxHandler } from './tx/txHandler';
133
+ import {
134
+ wormholeCoreBridgeIdl,
135
+ pythSolanaReceiverIdl,
136
+ DEFAULT_RECEIVER_PROGRAM_ID,
137
+ } from '@pythnetwork/pyth-solana-receiver';
138
+ import { parseAccumulatorUpdateData } from '@pythnetwork/price-service-sdk';
139
+ import {
140
+ DEFAULT_WORMHOLE_PROGRAM_ID,
141
+ getGuardianSetPda,
142
+ } from '@pythnetwork/pyth-solana-receiver/lib/address';
143
+ import { DRIFT_ORACLE_RECEIVER_ID } from './config';
144
+ import { WormholeCoreBridgeSolana } from '@pythnetwork/pyth-solana-receiver/lib/idl/wormhole_core_bridge_solana';
145
+ import { PythSolanaReceiver } from '@pythnetwork/pyth-solana-receiver/lib/idl/pyth_solana_receiver';
146
+ import { getFeedIdUint8Array, trimFeedId } from './util/pythPullOracleUtils';
147
+ import { isVersionedTransaction } from './tx/utils';
132
148
 
133
149
  type RemainingAccountParams = {
134
150
  userAccounts: UserAccount[];
@@ -178,6 +194,9 @@ export class DriftClient {
178
194
 
179
195
  txHandler: TxHandler;
180
196
 
197
+ receiverProgram?: Program<PythSolanaReceiver>;
198
+ wormholeProgram?: Program<WormholeCoreBridgeSolana>;
199
+
181
200
  public get isSubscribed() {
182
201
  return this._isSubscribed && this.accountSubscriber.isSubscribed;
183
202
  }
@@ -227,6 +246,7 @@ export class DriftClient {
227
246
  onSignedCb: this.handleSignedTransaction.bind(this),
228
247
  preSignedCb: this.handlePreSignedTransaction.bind(this),
229
248
  },
249
+ config: config.txHandlerConfig,
230
250
  });
231
251
 
232
252
  if (config.includeDelegates && config.subAccountIds) {
@@ -1800,25 +1820,14 @@ export class DriftClient {
1800
1820
  });
1801
1821
  }
1802
1822
 
1803
- /**
1804
- * Deposit funds into the given spot market
1805
- *
1806
- * @param amount to deposit
1807
- * @param marketIndex spot market index to deposit into
1808
- * @param associatedTokenAccount can be the wallet public key if using native sol
1809
- * @param subAccountId subaccountId to deposit
1810
- * @param reduceOnly if true, deposit must not increase account risk
1811
- */
1812
- public async deposit(
1823
+ public async createDepositTxn(
1813
1824
  amount: BN,
1814
1825
  marketIndex: number,
1815
1826
  associatedTokenAccount: PublicKey,
1816
1827
  subAccountId?: number,
1817
1828
  reduceOnly = false,
1818
1829
  txParams?: TxParams
1819
- ): Promise<TransactionSignature> {
1820
- const additionalSigners: Array<Signer> = [];
1821
-
1830
+ ): Promise<ReturnType<typeof this.buildTransaction>> {
1822
1831
  const spotMarketAccount = this.getSpotMarketAccount(marketIndex);
1823
1832
 
1824
1833
  const isSolMarket = spotMarketAccount.mint.equals(WRAPPED_SOL_MINT);
@@ -1868,11 +1877,36 @@ export class DriftClient {
1868
1877
 
1869
1878
  const tx = await this.buildTransaction(instructions, txParams);
1870
1879
 
1871
- const { txSig, slot } = await this.sendTransaction(
1872
- tx,
1873
- additionalSigners,
1874
- this.opts
1880
+ return tx;
1881
+ }
1882
+
1883
+ /**
1884
+ * Deposit funds into the given spot market
1885
+ *
1886
+ * @param amount to deposit
1887
+ * @param marketIndex spot market index to deposit into
1888
+ * @param associatedTokenAccount can be the wallet public key if using native sol
1889
+ * @param subAccountId subaccountId to deposit
1890
+ * @param reduceOnly if true, deposit must not increase account risk
1891
+ */
1892
+ public async deposit(
1893
+ amount: BN,
1894
+ marketIndex: number,
1895
+ associatedTokenAccount: PublicKey,
1896
+ subAccountId?: number,
1897
+ reduceOnly = false,
1898
+ txParams?: TxParams
1899
+ ): Promise<TransactionSignature> {
1900
+ const tx = await this.createDepositTxn(
1901
+ amount,
1902
+ marketIndex,
1903
+ associatedTokenAccount,
1904
+ subAccountId,
1905
+ reduceOnly,
1906
+ txParams
1875
1907
  );
1908
+
1909
+ const { txSig, slot } = await this.sendTransaction(tx, [], this.opts);
1876
1910
  this.spotMarketLastSlotCache.set(marketIndex, slot);
1877
1911
  return txSig;
1878
1912
  }
@@ -2005,20 +2039,7 @@ export class DriftClient {
2005
2039
  );
2006
2040
  }
2007
2041
 
2008
- /**
2009
- * Creates the User account for a user, and deposits some initial collateral
2010
- * @param amount
2011
- * @param userTokenAccount
2012
- * @param marketIndex
2013
- * @param subAccountId
2014
- * @param name
2015
- * @param fromSubAccountId
2016
- * @param referrerInfo
2017
- * @param donateAmount
2018
- * @param txParams
2019
- * @returns
2020
- */
2021
- public async initializeUserAccountAndDepositCollateral(
2042
+ public async createInitializeUserAccountAndDepositCollateral(
2022
2043
  amount: BN,
2023
2044
  userTokenAccount: PublicKey,
2024
2045
  marketIndex = 0,
@@ -2029,7 +2050,7 @@ export class DriftClient {
2029
2050
  donateAmount?: BN,
2030
2051
  txParams?: TxParams,
2031
2052
  customMaxMarginRatio?: number
2032
- ): Promise<[TransactionSignature, PublicKey]> {
2053
+ ): Promise<[Transaction | VersionedTransaction, PublicKey]> {
2033
2054
  const ixs = [];
2034
2055
 
2035
2056
  const [userAccountPublicKey, initializeUserAccountIx] =
@@ -2039,8 +2060,6 @@ export class DriftClient {
2039
2060
  referrerInfo
2040
2061
  );
2041
2062
 
2042
- const additionalSigners: Array<Signer> = [];
2043
-
2044
2063
  const spotMarket = this.getSpotMarketAccount(marketIndex);
2045
2064
 
2046
2065
  const isSolMarket = spotMarket.mint.equals(WRAPPED_SOL_MINT);
@@ -2134,6 +2153,49 @@ export class DriftClient {
2134
2153
 
2135
2154
  const tx = await this.buildTransaction(ixs, txParams);
2136
2155
 
2156
+ return [tx, userAccountPublicKey];
2157
+ }
2158
+
2159
+ /**
2160
+ * Creates the User account for a user, and deposits some initial collateral
2161
+ * @param amount
2162
+ * @param userTokenAccount
2163
+ * @param marketIndex
2164
+ * @param subAccountId
2165
+ * @param name
2166
+ * @param fromSubAccountId
2167
+ * @param referrerInfo
2168
+ * @param donateAmount
2169
+ * @param txParams
2170
+ * @returns
2171
+ */
2172
+ public async initializeUserAccountAndDepositCollateral(
2173
+ amount: BN,
2174
+ userTokenAccount: PublicKey,
2175
+ marketIndex = 0,
2176
+ subAccountId = 0,
2177
+ name?: string,
2178
+ fromSubAccountId?: number,
2179
+ referrerInfo?: ReferrerInfo,
2180
+ donateAmount?: BN,
2181
+ txParams?: TxParams,
2182
+ customMaxMarginRatio?: number
2183
+ ): Promise<[TransactionSignature, PublicKey]> {
2184
+ const [tx, userAccountPublicKey] =
2185
+ await this.createInitializeUserAccountAndDepositCollateral(
2186
+ amount,
2187
+ userTokenAccount,
2188
+ marketIndex,
2189
+ subAccountId,
2190
+ name,
2191
+ fromSubAccountId,
2192
+ referrerInfo,
2193
+ donateAmount,
2194
+ txParams,
2195
+ customMaxMarginRatio
2196
+ );
2197
+ const additionalSigners: Array<Signer> = [];
2198
+
2137
2199
  const { txSig, slot } = await this.sendTransaction(
2138
2200
  tx,
2139
2201
  additionalSigners,
@@ -4118,6 +4180,10 @@ export class DriftClient {
4118
4180
  const outMarket = this.getSpotMarketAccount(outMarketIndex);
4119
4181
  const inMarket = this.getSpotMarketAccount(inMarketIndex);
4120
4182
 
4183
+ const isExactOut = swapMode === 'ExactOut' || quote.swapMode === 'ExactOut';
4184
+ const amountIn = new BN(quote.inAmount);
4185
+ const exactOutBufferedAmountIn = amountIn.muln(1001).divn(1000); // Add 10bp buffer
4186
+
4121
4187
  if (!quote) {
4122
4188
  const fetchedQuote = await jupiterClient.getQuote({
4123
4189
  inputMint: inMarket.mint,
@@ -4198,7 +4264,7 @@ export class DriftClient {
4198
4264
  const { beginSwapIx, endSwapIx } = await this.getSwapIx({
4199
4265
  outMarketIndex,
4200
4266
  inMarketIndex,
4201
- amountIn: new BN(quote.inAmount),
4267
+ amountIn: isExactOut ? exactOutBufferedAmountIn : amountIn,
4202
4268
  inTokenAccount: inAssociatedTokenAccount,
4203
4269
  outTokenAccount: outAssociatedTokenAccount,
4204
4270
  reduceOnly,
@@ -6844,6 +6910,250 @@ export class DriftClient {
6844
6910
  return undefined;
6845
6911
  }
6846
6912
 
6913
+ public getReceiverProgram(): Program<PythSolanaReceiver> {
6914
+ if (this.receiverProgram === undefined) {
6915
+ this.receiverProgram = new Program(
6916
+ pythSolanaReceiverIdl,
6917
+ DEFAULT_RECEIVER_PROGRAM_ID,
6918
+ this.provider
6919
+ );
6920
+ }
6921
+ return this.receiverProgram;
6922
+ }
6923
+
6924
+ public async postPythPullOracleUpdateAtomic(
6925
+ vaaString: string,
6926
+ feedId: string
6927
+ ): Promise<TransactionSignature> {
6928
+ const postIxs = await this.getPostPythPullOracleUpdateAtomicIxs(
6929
+ vaaString,
6930
+ feedId
6931
+ );
6932
+ const tx = await this.buildTransaction(postIxs);
6933
+ const { txSig } = await this.sendTransaction(tx, [], this.opts);
6934
+
6935
+ return txSig;
6936
+ }
6937
+
6938
+ public async getPostPythPullOracleUpdateAtomicIxs(
6939
+ vaaString: string,
6940
+ feedId: string,
6941
+ numSignatures = 2
6942
+ ): Promise<TransactionInstruction[]> {
6943
+ feedId = trimFeedId(feedId);
6944
+ const accumulatorUpdateData = parseAccumulatorUpdateData(
6945
+ Buffer.from(vaaString, 'base64')
6946
+ );
6947
+ const guardianSetIndex = accumulatorUpdateData.vaa.readUInt32BE(1);
6948
+ const guardianSet = getGuardianSetPda(
6949
+ guardianSetIndex,
6950
+ DEFAULT_WORMHOLE_PROGRAM_ID
6951
+ );
6952
+ const trimmedVaa = trimVaaSignatures(
6953
+ accumulatorUpdateData.vaa,
6954
+ numSignatures
6955
+ );
6956
+
6957
+ const postIxs: TransactionInstruction[] = [];
6958
+ for (const update of accumulatorUpdateData.updates) {
6959
+ postIxs.push(
6960
+ await this.getSinglePostPythPullOracleAtomicIx(
6961
+ {
6962
+ vaa: trimmedVaa,
6963
+ merklePriceUpdate: update,
6964
+ },
6965
+ feedId,
6966
+ guardianSet
6967
+ )
6968
+ );
6969
+ }
6970
+
6971
+ return postIxs;
6972
+ }
6973
+
6974
+ public async getSinglePostPythPullOracleAtomicIx(
6975
+ params: {
6976
+ vaa: Buffer;
6977
+ merklePriceUpdate: {
6978
+ message: Buffer;
6979
+ proof: number[][];
6980
+ };
6981
+ },
6982
+ feedId: string,
6983
+ guardianSet: PublicKey
6984
+ ): Promise<TransactionInstruction> {
6985
+ const feedIdBuffer = getFeedIdUint8Array(feedId);
6986
+ const receiverProgram = this.getReceiverProgram();
6987
+
6988
+ const encodedParams = receiverProgram.coder.types.encode(
6989
+ 'PostUpdateAtomicParams',
6990
+ params
6991
+ );
6992
+
6993
+ return this.program.instruction.postPythPullOracleUpdateAtomic(
6994
+ feedIdBuffer,
6995
+ encodedParams,
6996
+ {
6997
+ accounts: {
6998
+ keeper: this.wallet.publicKey,
6999
+ pythSolanaReceiver: DRIFT_ORACLE_RECEIVER_ID,
7000
+ guardianSet,
7001
+ priceFeed: getPythPullOraclePublicKey(
7002
+ this.program.programId,
7003
+ feedIdBuffer
7004
+ ),
7005
+ },
7006
+ }
7007
+ );
7008
+ }
7009
+
7010
+ public async updatePythPullOracle(
7011
+ vaaString: string,
7012
+ feedId: string
7013
+ ): Promise<TransactionSignature> {
7014
+ feedId = trimFeedId(feedId);
7015
+ const accumulatorUpdateData = parseAccumulatorUpdateData(
7016
+ Buffer.from(vaaString, 'base64')
7017
+ );
7018
+ const guardianSetIndex = accumulatorUpdateData.vaa.readUInt32BE(1);
7019
+ const guardianSet = getGuardianSetPda(
7020
+ guardianSetIndex,
7021
+ DEFAULT_WORMHOLE_PROGRAM_ID
7022
+ );
7023
+
7024
+ const [postIxs, encodedVaaAddress] = await this.getBuildEncodedVaaIxs(
7025
+ accumulatorUpdateData.vaa,
7026
+ guardianSet
7027
+ );
7028
+
7029
+ for (const update of accumulatorUpdateData.updates) {
7030
+ postIxs.push(
7031
+ await this.getUpdatePythPullOracleIxs(
7032
+ {
7033
+ merklePriceUpdate: update,
7034
+ },
7035
+ feedId,
7036
+ encodedVaaAddress.publicKey
7037
+ )
7038
+ );
7039
+ }
7040
+
7041
+ const tx = await this.buildTransaction(postIxs);
7042
+ const { txSig } = await this.sendTransaction(
7043
+ tx,
7044
+ [encodedVaaAddress],
7045
+ this.opts
7046
+ );
7047
+
7048
+ return txSig;
7049
+ }
7050
+
7051
+ public async getUpdatePythPullOracleIxs(
7052
+ params: {
7053
+ merklePriceUpdate: {
7054
+ message: Buffer;
7055
+ proof: number[][];
7056
+ };
7057
+ },
7058
+ feedId: string,
7059
+ encodedVaaAddress: PublicKey
7060
+ ): Promise<TransactionInstruction> {
7061
+ const feedIdBuffer = getFeedIdUint8Array(feedId);
7062
+ const receiverProgram = this.getReceiverProgram();
7063
+
7064
+ const encodedParams = receiverProgram.coder.types.encode(
7065
+ 'PostUpdateParams',
7066
+ params
7067
+ );
7068
+
7069
+ return this.program.instruction.updatePythPullOracle(
7070
+ feedIdBuffer,
7071
+ encodedParams,
7072
+ {
7073
+ accounts: {
7074
+ keeper: this.wallet.publicKey,
7075
+ pythSolanaReceiver: DRIFT_ORACLE_RECEIVER_ID,
7076
+ encodedVaa: encodedVaaAddress,
7077
+ priceFeed: getPythPullOraclePublicKey(
7078
+ this.program.programId,
7079
+ feedIdBuffer
7080
+ ),
7081
+ },
7082
+ }
7083
+ );
7084
+ }
7085
+
7086
+ public async getBuildEncodedVaaIxs(
7087
+ vaa: Buffer,
7088
+ guardianSet: PublicKey
7089
+ ): Promise<[TransactionInstruction[], Keypair]> {
7090
+ const postIxs: TransactionInstruction[] = [];
7091
+
7092
+ if (this.wormholeProgram === undefined) {
7093
+ this.wormholeProgram = new Program(
7094
+ wormholeCoreBridgeIdl,
7095
+ DEFAULT_WORMHOLE_PROGRAM_ID,
7096
+ this.provider
7097
+ );
7098
+ }
7099
+
7100
+ const encodedVaaKeypair = new Keypair();
7101
+ postIxs.push(
7102
+ await this.wormholeProgram.account.encodedVaa.createInstruction(
7103
+ encodedVaaKeypair,
7104
+ vaa.length + 46
7105
+ )
7106
+ );
7107
+
7108
+ // Why do we need this too?
7109
+ postIxs.push(
7110
+ await this.wormholeProgram.methods
7111
+ .initEncodedVaa()
7112
+ .accounts({
7113
+ encodedVaa: encodedVaaKeypair.publicKey,
7114
+ })
7115
+ .instruction()
7116
+ );
7117
+
7118
+ // Split the write into two ixs
7119
+ postIxs.push(
7120
+ await this.wormholeProgram.methods
7121
+ .writeEncodedVaa({
7122
+ index: 0,
7123
+ data: vaa.subarray(0, 755),
7124
+ })
7125
+ .accounts({
7126
+ draftVaa: encodedVaaKeypair.publicKey,
7127
+ })
7128
+ .instruction()
7129
+ );
7130
+
7131
+ postIxs.push(
7132
+ await this.wormholeProgram.methods
7133
+ .writeEncodedVaa({
7134
+ index: 755,
7135
+ data: vaa.subarray(755),
7136
+ })
7137
+ .accounts({
7138
+ draftVaa: encodedVaaKeypair.publicKey,
7139
+ })
7140
+ .instruction()
7141
+ );
7142
+
7143
+ // Verify
7144
+ postIxs.push(
7145
+ await this.wormholeProgram.methods
7146
+ .verifyEncodedVaaV1()
7147
+ .accounts({
7148
+ guardianSet,
7149
+ draftVaa: encodedVaaKeypair.publicKey,
7150
+ })
7151
+ .instruction()
7152
+ );
7153
+
7154
+ return [postIxs, encodedVaaKeypair];
7155
+ }
7156
+
6847
7157
  private handleSignedTransaction(signedTxs: SignedTxData[]) {
6848
7158
  if (this.enableMetricsEvents && this.metricsEventEmitter) {
6849
7159
  this.metricsEventEmitter.emit('txSigned', signedTxs);
@@ -6859,11 +7169,7 @@ export class DriftClient {
6859
7169
  private isVersionedTransaction(
6860
7170
  tx: Transaction | VersionedTransaction
6861
7171
  ): boolean {
6862
- const version = (tx as VersionedTransaction)?.version;
6863
- const isVersionedTx =
6864
- tx instanceof VersionedTransaction || version !== undefined;
6865
-
6866
- return isVersionedTx;
7172
+ return isVersionedTransaction(tx);
6867
7173
  }
6868
7174
 
6869
7175
  sendTransaction(
@@ -10,7 +10,7 @@ import { OracleInfo } from './oracles/types';
10
10
  import { BulkAccountLoader } from './accounts/bulkAccountLoader';
11
11
  import { DriftEnv } from './config';
12
12
  import { TxSender } from './tx/types';
13
- import { TxHandler } from './tx/txHandler';
13
+ import { TxHandler, TxHandlerConfig } from './tx/txHandler';
14
14
 
15
15
  export type DriftClientConfig = {
16
16
  connection: Connection;
@@ -35,6 +35,7 @@ export type DriftClientConfig = {
35
35
  txVersion?: TransactionVersion; // which tx version to use
36
36
  txParams?: TxParams; // default tx params to use
37
37
  enableMetricsEvents?: boolean;
38
+ txHandlerConfig?: TxHandlerConfig;
38
39
  };
39
40
 
40
41
  export type DriftClientSubscriptionConfig =