@drift-labs/sdk 2.85.0-beta.9 → 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 (51) 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/config.d.ts +3 -0
  8. package/lib/config.js +4 -1
  9. package/lib/constants/perpMarkets.d.ts +1 -0
  10. package/lib/constants/perpMarkets.js +57 -0
  11. package/lib/constants/spotMarkets.d.ts +1 -0
  12. package/lib/constants/spotMarkets.js +20 -0
  13. package/lib/driftClient.d.ts +26 -1
  14. package/lib/driftClient.js +157 -31
  15. package/lib/driftClientConfig.d.ts +2 -1
  16. package/lib/idl/drift.json +161 -2
  17. package/lib/math/oracles.d.ts +2 -0
  18. package/lib/math/oracles.js +14 -1
  19. package/lib/oracles/pythPullClient.js +1 -1
  20. package/lib/tx/baseTxSender.js +1 -0
  21. package/lib/tx/blockhashFetcher/baseBlockhashFetcher.d.ts +8 -0
  22. package/lib/tx/blockhashFetcher/baseBlockhashFetcher.js +13 -0
  23. package/lib/tx/blockhashFetcher/cachedBlockhashFetcher.d.ts +28 -0
  24. package/lib/tx/blockhashFetcher/cachedBlockhashFetcher.js +73 -0
  25. package/lib/tx/blockhashFetcher/types.d.ts +4 -0
  26. package/lib/tx/blockhashFetcher/types.js +2 -0
  27. package/lib/tx/txHandler.d.ts +10 -0
  28. package/lib/tx/txHandler.js +16 -7
  29. package/lib/tx/utils.d.ts +2 -0
  30. package/lib/tx/utils.js +10 -0
  31. package/lib/util/pythPullOracleUtils.d.ts +2 -0
  32. package/lib/util/pythPullOracleUtils.js +15 -0
  33. package/package.json +3 -1
  34. package/src/addresses/pda.ts +13 -0
  35. package/src/adminClient.ts +39 -0
  36. package/src/config.ts +6 -0
  37. package/src/constants/perpMarkets.ts +115 -0
  38. package/src/constants/spotMarkets.ts +41 -0
  39. package/src/driftClient.ts +347 -41
  40. package/src/driftClientConfig.ts +2 -1
  41. package/src/idl/drift.json +161 -2
  42. package/src/math/oracles.ts +17 -0
  43. package/src/oracles/pythPullClient.ts +2 -3
  44. package/src/tx/baseTxSender.ts +1 -0
  45. package/src/tx/blockhashFetcher/baseBlockhashFetcher.ts +19 -0
  46. package/src/tx/blockhashFetcher/cachedBlockhashFetcher.ts +90 -0
  47. package/src/tx/blockhashFetcher/types.ts +5 -0
  48. package/src/tx/txHandler.ts +38 -4
  49. package/src/tx/utils.ts +11 -0
  50. package/src/util/pythPullOracleUtils.ts +11 -0
  51. package/tests/tx/cachedBlockhashFetcher.test.ts +96 -0
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "2.84.0",
2
+ "version": "2.85.0",
3
3
  "name": "drift",
4
4
  "instructions": [
5
5
  {
@@ -2547,6 +2547,86 @@
2547
2547
  }
2548
2548
  ]
2549
2549
  },
2550
+ {
2551
+ "name": "updatePythPullOracle",
2552
+ "accounts": [
2553
+ {
2554
+ "name": "keeper",
2555
+ "isMut": true,
2556
+ "isSigner": true
2557
+ },
2558
+ {
2559
+ "name": "pythSolanaReceiver",
2560
+ "isMut": false,
2561
+ "isSigner": false
2562
+ },
2563
+ {
2564
+ "name": "encodedVaa",
2565
+ "isMut": false,
2566
+ "isSigner": false
2567
+ },
2568
+ {
2569
+ "name": "priceFeed",
2570
+ "isMut": true,
2571
+ "isSigner": false
2572
+ }
2573
+ ],
2574
+ "args": [
2575
+ {
2576
+ "name": "feedId",
2577
+ "type": {
2578
+ "array": [
2579
+ "u8",
2580
+ 32
2581
+ ]
2582
+ }
2583
+ },
2584
+ {
2585
+ "name": "params",
2586
+ "type": "bytes"
2587
+ }
2588
+ ]
2589
+ },
2590
+ {
2591
+ "name": "postPythPullOracleUpdateAtomic",
2592
+ "accounts": [
2593
+ {
2594
+ "name": "keeper",
2595
+ "isMut": true,
2596
+ "isSigner": true
2597
+ },
2598
+ {
2599
+ "name": "pythSolanaReceiver",
2600
+ "isMut": false,
2601
+ "isSigner": false
2602
+ },
2603
+ {
2604
+ "name": "guardianSet",
2605
+ "isMut": false,
2606
+ "isSigner": false
2607
+ },
2608
+ {
2609
+ "name": "priceFeed",
2610
+ "isMut": true,
2611
+ "isSigner": false
2612
+ }
2613
+ ],
2614
+ "args": [
2615
+ {
2616
+ "name": "feedId",
2617
+ "type": {
2618
+ "array": [
2619
+ "u8",
2620
+ 32
2621
+ ]
2622
+ }
2623
+ },
2624
+ {
2625
+ "name": "params",
2626
+ "type": "bytes"
2627
+ }
2628
+ ]
2629
+ },
2550
2630
  {
2551
2631
  "name": "initialize",
2552
2632
  "accounts": [
@@ -5296,6 +5376,47 @@
5296
5376
  "type": "u16"
5297
5377
  }
5298
5378
  ]
5379
+ },
5380
+ {
5381
+ "name": "initializePythPullOracle",
5382
+ "accounts": [
5383
+ {
5384
+ "name": "admin",
5385
+ "isMut": true,
5386
+ "isSigner": true
5387
+ },
5388
+ {
5389
+ "name": "pythSolanaReceiver",
5390
+ "isMut": false,
5391
+ "isSigner": false
5392
+ },
5393
+ {
5394
+ "name": "priceFeed",
5395
+ "isMut": true,
5396
+ "isSigner": false
5397
+ },
5398
+ {
5399
+ "name": "systemProgram",
5400
+ "isMut": false,
5401
+ "isSigner": false
5402
+ },
5403
+ {
5404
+ "name": "state",
5405
+ "isMut": false,
5406
+ "isSigner": false
5407
+ }
5408
+ ],
5409
+ "args": [
5410
+ {
5411
+ "name": "feedId",
5412
+ "type": {
5413
+ "array": [
5414
+ "u8",
5415
+ 32
5416
+ ]
5417
+ }
5418
+ }
5419
+ ]
5299
5420
  }
5300
5421
  ],
5301
5422
  "accounts": [
@@ -12029,6 +12150,44 @@
12029
12150
  "code": 6268,
12030
12151
  "name": "MaxBorrows",
12031
12152
  "msg": "Can not borow more than max borrows"
12153
+ },
12154
+ {
12155
+ "code": 6269,
12156
+ "name": "OracleUpdatesNotMonotonic",
12157
+ "msg": "Updates must be monotonically increasing"
12158
+ },
12159
+ {
12160
+ "code": 6270,
12161
+ "name": "OraclePriceFeedMessageMismatch",
12162
+ "msg": "Trying to update price feed with the wrong feed id"
12163
+ },
12164
+ {
12165
+ "code": 6271,
12166
+ "name": "OracleUnsupportedMessageType",
12167
+ "msg": "The message in the update must be a PriceFeedMessage"
12168
+ },
12169
+ {
12170
+ "code": 6272,
12171
+ "name": "OracleDeserializeMessageFailed",
12172
+ "msg": "Could not deserialize the message in the update"
12173
+ },
12174
+ {
12175
+ "code": 6273,
12176
+ "name": "OracleWrongGuardianSetOwner",
12177
+ "msg": "Wrong guardian set owner in update price atomic"
12178
+ },
12179
+ {
12180
+ "code": 6274,
12181
+ "name": "OracleWrongWriteAuthority",
12182
+ "msg": "Oracle post update atomic price feed account must be drift program"
12183
+ },
12184
+ {
12185
+ "code": 6275,
12186
+ "name": "OracleWrongVaaOwner",
12187
+ "msg": "Oracle vaa owner must be wormhole program"
12032
12188
  }
12033
- ]
12189
+ ],
12190
+ "metadata": {
12191
+ "address": "dRiftyHA39MWEi3m9aunc5MzRF1JYuBsbn6VPcn33UH"
12192
+ }
12034
12193
  }
@@ -1,4 +1,5 @@
1
1
  /// <reference types="bn.js" />
2
+ /// <reference types="node" />
2
3
  import { AMM, OracleGuardRails } from '../types';
3
4
  import { OraclePriceData } from '../oracles/types';
4
5
  import { BN, HistoricalOracleData, PerpMarketAccount } from '../index';
@@ -9,3 +10,4 @@ export declare function isOracleTooDivergent(amm: AMM, oraclePriceData: OraclePr
9
10
  export declare function calculateLiveOracleTwap(histOracleData: HistoricalOracleData, oraclePriceData: OraclePriceData, now: BN, period: BN): BN;
10
11
  export declare function calculateLiveOracleStd(amm: AMM, oraclePriceData: OraclePriceData, now: BN): BN;
11
12
  export declare function getNewOracleConfPct(amm: AMM, oraclePriceData: OraclePriceData, reservePrice: BN, now: BN): BN;
13
+ export declare function trimVaaSignatures(vaa: Buffer, n?: number): Buffer;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getNewOracleConfPct = exports.calculateLiveOracleStd = exports.calculateLiveOracleTwap = exports.isOracleTooDivergent = exports.isOracleValid = exports.getMaxConfidenceIntervalMultiplier = exports.oraclePriceBands = void 0;
3
+ exports.trimVaaSignatures = exports.getNewOracleConfPct = exports.calculateLiveOracleStd = exports.calculateLiveOracleTwap = exports.isOracleTooDivergent = exports.isOracleValid = exports.getMaxConfidenceIntervalMultiplier = exports.oraclePriceBands = void 0;
4
4
  const types_1 = require("../types");
5
5
  const numericConstants_1 = require("../constants/numericConstants");
6
6
  const index_1 = require("../index");
@@ -119,3 +119,16 @@ function getNewOracleConfPct(amm, oraclePriceData, reservePrice, now) {
119
119
  return confIntervalPctResult;
120
120
  }
121
121
  exports.getNewOracleConfPct = getNewOracleConfPct;
122
+ function trimVaaSignatures(vaa, n = 3) {
123
+ const currentNumSignatures = vaa[5];
124
+ if (n > currentNumSignatures) {
125
+ throw new Error("Resulting VAA can't have more signatures than the original VAA");
126
+ }
127
+ const trimmedVaa = Buffer.concat([
128
+ vaa.subarray(0, 6 + n * 66),
129
+ vaa.subarray(6 + currentNumSignatures * 66),
130
+ ]);
131
+ trimmedVaa[5] = n;
132
+ return trimmedVaa;
133
+ }
134
+ exports.trimVaaSignatures = trimVaaSignatures;
@@ -16,7 +16,7 @@ class PythPullClient {
16
16
  new __1.Wallet(new web3_js_1.Keypair()), {
17
17
  commitment: connection.commitment,
18
18
  });
19
- this.receiver = new anchor_1.Program(pyth_solana_receiver_1.pythSolanaReceiverIdl, pyth_solana_receiver_1.DEFAULT_RECEIVER_PROGRAM_ID, provider);
19
+ this.receiver = new anchor_1.Program(pyth_solana_receiver_1.pythSolanaReceiverIdl, __1.DRIFT_ORACLE_RECEIVER_ID, provider);
20
20
  this.decodeFunc =
21
21
  this.receiver.account.priceUpdateV2.coder.accounts.decodeUnchecked.bind(this.receiver.account.priceUpdateV2.coder.accounts);
22
22
  }
@@ -238,6 +238,7 @@ class BaseTxSender {
238
238
  const logs = transactionResult.meta.logMessages;
239
239
  const lastLog = logs[logs.length - 1];
240
240
  const friendlyMessage = (_b = lastLog === null || lastLog === void 0 ? void 0 : lastLog.match(/(failed:) (.+)/)) === null || _b === void 0 ? void 0 : _b[2];
241
+ // @ts-ignore
241
242
  throw new web3_js_1.SendTransactionError({
242
243
  action: 'send',
243
244
  signature: txSig,
@@ -0,0 +1,8 @@
1
+ import { BlockhashWithExpiryBlockHeight, Commitment, Connection } from '@solana/web3.js';
2
+ import { BlockhashFetcher } from './types';
3
+ export declare class BaseBlockhashFetcher implements BlockhashFetcher {
4
+ private connection;
5
+ private blockhashCommitment;
6
+ constructor(connection: Connection, blockhashCommitment: Commitment);
7
+ getLatestBlockhash(): Promise<BlockhashWithExpiryBlockHeight | undefined>;
8
+ }
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.BaseBlockhashFetcher = void 0;
4
+ class BaseBlockhashFetcher {
5
+ constructor(connection, blockhashCommitment) {
6
+ this.connection = connection;
7
+ this.blockhashCommitment = blockhashCommitment;
8
+ }
9
+ async getLatestBlockhash() {
10
+ return this.connection.getLatestBlockhash(this.blockhashCommitment);
11
+ }
12
+ }
13
+ exports.BaseBlockhashFetcher = BaseBlockhashFetcher;
@@ -0,0 +1,28 @@
1
+ import { BlockhashWithExpiryBlockHeight, Commitment, Connection } from '@solana/web3.js';
2
+ import { BlockhashFetcher } from './types';
3
+ /**
4
+ * Fetches the latest blockhash and caches it for a configurable amount of time.
5
+ *
6
+ * - Prevents RPC spam by reusing cached values
7
+ * - Retries on failure with exponential backoff
8
+ * - Prevents concurrent requests for the same blockhash
9
+ */
10
+ export declare class CachedBlockhashFetcher implements BlockhashFetcher {
11
+ private connection;
12
+ private blockhashCommitment;
13
+ private retryCount;
14
+ private retrySleepTimeMs;
15
+ private staleCacheTimeMs;
16
+ private recentBlockhashCache;
17
+ private blockhashFetchingPromise;
18
+ constructor(connection: Connection, blockhashCommitment: Commitment, retryCount: number, retrySleepTimeMs: number, staleCacheTimeMs: number);
19
+ private fetchBlockhashWithRetry;
20
+ private sleep;
21
+ private updateBlockhashCache;
22
+ getLatestBlockhash(): Promise<BlockhashWithExpiryBlockHeight | undefined>;
23
+ private isCacheStale;
24
+ /**
25
+ * Refresh the blockhash cache, await a pending refresh if it exists
26
+ */
27
+ private refreshBlockhash;
28
+ }
@@ -0,0 +1,73 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CachedBlockhashFetcher = void 0;
4
+ /**
5
+ * Fetches the latest blockhash and caches it for a configurable amount of time.
6
+ *
7
+ * - Prevents RPC spam by reusing cached values
8
+ * - Retries on failure with exponential backoff
9
+ * - Prevents concurrent requests for the same blockhash
10
+ */
11
+ class CachedBlockhashFetcher {
12
+ constructor(connection, blockhashCommitment, retryCount, retrySleepTimeMs, staleCacheTimeMs) {
13
+ this.connection = connection;
14
+ this.blockhashCommitment = blockhashCommitment;
15
+ this.retryCount = retryCount;
16
+ this.retrySleepTimeMs = retrySleepTimeMs;
17
+ this.staleCacheTimeMs = staleCacheTimeMs;
18
+ this.recentBlockhashCache = { value: undefined, lastUpdated: 0 };
19
+ this.blockhashFetchingPromise = null;
20
+ }
21
+ async fetchBlockhashWithRetry() {
22
+ for (let i = 0; i < this.retryCount; i++) {
23
+ try {
24
+ return await this.connection.getLatestBlockhash(this.blockhashCommitment);
25
+ }
26
+ catch (err) {
27
+ if (i === this.retryCount - 1) {
28
+ throw new Error('Failed to fetch blockhash after maximum retries');
29
+ }
30
+ await this.sleep(this.retrySleepTimeMs * 2 ** i);
31
+ }
32
+ }
33
+ throw new Error('Failed to fetch blockhash after maximum retries');
34
+ }
35
+ sleep(ms) {
36
+ return new Promise((resolve) => setTimeout(resolve, ms));
37
+ }
38
+ async updateBlockhashCache() {
39
+ const result = await this.fetchBlockhashWithRetry();
40
+ this.recentBlockhashCache = {
41
+ value: result,
42
+ lastUpdated: Date.now(),
43
+ };
44
+ }
45
+ async getLatestBlockhash() {
46
+ if (this.isCacheStale()) {
47
+ await this.refreshBlockhash();
48
+ }
49
+ return this.recentBlockhashCache.value;
50
+ }
51
+ isCacheStale() {
52
+ const lastUpdateTime = this.recentBlockhashCache.lastUpdated;
53
+ return (!lastUpdateTime || Date.now() > lastUpdateTime + this.staleCacheTimeMs);
54
+ }
55
+ /**
56
+ * Refresh the blockhash cache, await a pending refresh if it exists
57
+ */
58
+ async refreshBlockhash() {
59
+ if (!this.blockhashFetchingPromise) {
60
+ this.blockhashFetchingPromise = this.updateBlockhashCache();
61
+ try {
62
+ await this.blockhashFetchingPromise;
63
+ }
64
+ finally {
65
+ this.blockhashFetchingPromise = null;
66
+ }
67
+ }
68
+ else {
69
+ await this.blockhashFetchingPromise;
70
+ }
71
+ }
72
+ }
73
+ exports.CachedBlockhashFetcher = CachedBlockhashFetcher;
@@ -0,0 +1,4 @@
1
+ import { BlockhashWithExpiryBlockHeight } from '@solana/web3.js';
2
+ export interface BlockhashFetcher {
3
+ getLatestBlockhash(): Promise<BlockhashWithExpiryBlockHeight | undefined>;
4
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -13,6 +13,14 @@ export type TxBuildingProps = {
13
13
  recentBlockhash?: BlockhashWithExpiryBlockHeight;
14
14
  wallet?: IWallet;
15
15
  };
16
+ export type TxHandlerConfig = {
17
+ blockhashCachingEnabled?: boolean;
18
+ blockhashCachingConfig?: {
19
+ retryCount: number;
20
+ retrySleepTimeMs: number;
21
+ staleCacheTimeMs: number;
22
+ };
23
+ };
16
24
  /**
17
25
  * This class is responsible for creating and signing transactions.
18
26
  */
@@ -25,6 +33,7 @@ export declare class TxHandler {
25
33
  private preSignedCb?;
26
34
  private onSignedCb?;
27
35
  private blockhashCommitment;
36
+ private blockHashFetcher;
28
37
  constructor(props: {
29
38
  connection: Connection;
30
39
  wallet: IWallet;
@@ -34,6 +43,7 @@ export declare class TxHandler {
34
43
  onSignedCb?: (txSigs: DriftClientMetricsEvents['txSigned']) => void;
35
44
  preSignedCb?: () => void;
36
45
  };
46
+ config?: TxHandlerConfig;
37
47
  });
38
48
  private addHashAndExpiryToLookup;
39
49
  private getProps;
@@ -8,6 +8,9 @@ const web3_js_1 = require("@solana/web3.js");
8
8
  const txParamProcessor_1 = require("./txParamProcessor");
9
9
  const bs58_1 = __importDefault(require("bs58"));
10
10
  const computeUnits_1 = require("../util/computeUnits");
11
+ const cachedBlockhashFetcher_1 = require("./blockhashFetcher/cachedBlockhashFetcher");
12
+ const baseBlockhashFetcher_1 = require("./blockhashFetcher/baseBlockhashFetcher");
13
+ const utils_1 = require("./utils");
11
14
  /**
12
15
  * Explanation for SIGNATURE_BLOCK_AND_EXPIRY:
13
16
  *
@@ -15,12 +18,15 @@ const computeUnits_1 = require("../util/computeUnits");
15
18
  */
16
19
  const DEV_TRY_FORCE_TX_TIMEOUTS = process.env.DEV_TRY_FORCE_TX_TIMEOUTS === 'true' || false;
17
20
  exports.COMPUTE_UNITS_DEFAULT = 200000;
21
+ const BLOCKHASH_FETCH_RETRY_COUNT = 3;
22
+ const BLOCKHASH_FETCH_RETRY_SLEEP = 200;
23
+ const RECENT_BLOCKHASH_STALE_TIME_MS = 2000; // Reuse blockhashes within this timeframe during bursts of tx contruction
18
24
  /**
19
25
  * This class is responsible for creating and signing transactions.
20
26
  */
21
27
  class TxHandler {
22
28
  constructor(props) {
23
- var _a, _b, _c, _d;
29
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p;
24
30
  this.blockHashToLastValidBlockHeightLookup = {};
25
31
  this.returnBlockHeightsWithSignedTxCallbackData = false;
26
32
  this.blockhashCommitment = 'finalized';
@@ -28,11 +34,14 @@ class TxHandler {
28
34
  this.connection = props.connection;
29
35
  this.wallet = props.wallet;
30
36
  this.confirmationOptions = props.confirmationOptions;
37
+ this.blockHashFetcher = ((_a = props === null || props === void 0 ? void 0 : props.config) === null || _a === void 0 ? void 0 : _a.blockhashCachingEnabled)
38
+ ? new cachedBlockhashFetcher_1.CachedBlockhashFetcher(this.connection, this.blockhashCommitment, (_d = (_c = (_b = props === null || props === void 0 ? void 0 : props.config) === null || _b === void 0 ? void 0 : _b.blockhashCachingConfig) === null || _c === void 0 ? void 0 : _c.retryCount) !== null && _d !== void 0 ? _d : BLOCKHASH_FETCH_RETRY_COUNT, (_g = (_f = (_e = props === null || props === void 0 ? void 0 : props.config) === null || _e === void 0 ? void 0 : _e.blockhashCachingConfig) === null || _f === void 0 ? void 0 : _f.retrySleepTimeMs) !== null && _g !== void 0 ? _g : BLOCKHASH_FETCH_RETRY_SLEEP, (_k = (_j = (_h = props === null || props === void 0 ? void 0 : props.config) === null || _h === void 0 ? void 0 : _h.blockhashCachingConfig) === null || _j === void 0 ? void 0 : _j.staleCacheTimeMs) !== null && _k !== void 0 ? _k : RECENT_BLOCKHASH_STALE_TIME_MS)
39
+ : new baseBlockhashFetcher_1.BaseBlockhashFetcher(this.connection, this.blockhashCommitment);
31
40
  // #Optionals
32
41
  this.returnBlockHeightsWithSignedTxCallbackData =
33
- (_b = (_a = props.opts) === null || _a === void 0 ? void 0 : _a.returnBlockHeightsWithSignedTxCallbackData) !== null && _b !== void 0 ? _b : false;
34
- this.onSignedCb = (_c = props.opts) === null || _c === void 0 ? void 0 : _c.onSignedCb;
35
- this.preSignedCb = (_d = props.opts) === null || _d === void 0 ? void 0 : _d.preSignedCb;
42
+ (_m = (_l = props.opts) === null || _l === void 0 ? void 0 : _l.returnBlockHeightsWithSignedTxCallbackData) !== null && _m !== void 0 ? _m : false;
43
+ this.onSignedCb = (_o = props.opts) === null || _o === void 0 ? void 0 : _o.onSignedCb;
44
+ this.preSignedCb = (_p = props.opts) === null || _p === void 0 ? void 0 : _p.preSignedCb;
36
45
  }
37
46
  addHashAndExpiryToLookup(hashAndExpiry) {
38
47
  if (!this.returnBlockHeightsWithSignedTxCallbackData)
@@ -50,8 +59,8 @@ class TxHandler {
50
59
  *
51
60
  * @returns
52
61
  */
53
- getLatestBlockhashForTransaction() {
54
- return this.connection.getLatestBlockhash(this.blockhashCommitment);
62
+ async getLatestBlockhashForTransaction() {
63
+ return this.blockHashFetcher.getLatestBlockhash();
55
64
  }
56
65
  /**
57
66
  * Applies recent blockhash and signs a given transaction
@@ -80,7 +89,7 @@ class TxHandler {
80
89
  return signedTx;
81
90
  }
82
91
  isVersionedTransaction(tx) {
83
- return (tx === null || tx === void 0 ? void 0 : tx.message) && true;
92
+ return (0, utils_1.isVersionedTransaction)(tx);
84
93
  }
85
94
  isLegacyTransaction(tx) {
86
95
  return !this.isVersionedTransaction(tx);
@@ -0,0 +1,2 @@
1
+ import { Transaction, VersionedTransaction } from '@solana/web3.js';
2
+ export declare const isVersionedTransaction: (tx: Transaction | VersionedTransaction) => boolean;
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isVersionedTransaction = void 0;
4
+ const web3_js_1 = require("@solana/web3.js");
5
+ const isVersionedTransaction = (tx) => {
6
+ const version = tx === null || tx === void 0 ? void 0 : tx.version;
7
+ const isVersionedTx = tx instanceof web3_js_1.VersionedTransaction || version !== undefined;
8
+ return isVersionedTx;
9
+ };
10
+ exports.isVersionedTransaction = isVersionedTransaction;
@@ -0,0 +1,2 @@
1
+ export declare function trimFeedId(feedId: string): string;
2
+ export declare function getFeedIdUint8Array(feedId: string): Uint8Array;
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getFeedIdUint8Array = exports.trimFeedId = void 0;
4
+ function trimFeedId(feedId) {
5
+ if (feedId.startsWith('0x')) {
6
+ return feedId.slice(2);
7
+ }
8
+ return feedId;
9
+ }
10
+ exports.trimFeedId = trimFeedId;
11
+ function getFeedIdUint8Array(feedId) {
12
+ const trimmedFeedId = trimFeedId(feedId);
13
+ return Uint8Array.from(Buffer.from(trimmedFeedId, 'hex'));
14
+ }
15
+ exports.getFeedIdUint8Array = getFeedIdUint8Array;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@drift-labs/sdk",
3
- "version": "2.85.0-beta.9",
3
+ "version": "2.86.0-beta.0",
4
4
  "main": "lib/index.js",
5
5
  "types": "lib/index.d.ts",
6
6
  "author": "crispheaney",
@@ -38,6 +38,7 @@
38
38
  "@ellipsis-labs/phoenix-sdk": "^1.4.2",
39
39
  "@project-serum/serum": "^0.13.38",
40
40
  "@pythnetwork/client": "2.5.3",
41
+ "@pythnetwork/price-service-sdk": "^1.7.1",
41
42
  "@pythnetwork/pyth-solana-receiver": "^0.7.0",
42
43
  "@solana/spl-token": "0.3.7",
43
44
  "@solana/web3.js": "1.92.3",
@@ -65,6 +66,7 @@
65
66
  "mocha": "^10.0.0",
66
67
  "object-sizeof": "^2.6.3",
67
68
  "prettier": "3.0.1",
69
+ "sinon": "^18.0.0",
68
70
  "ts-node": "^10.8.0",
69
71
  "typescript": "^4.9.5"
70
72
  },
@@ -236,3 +236,16 @@ export function getPrelaunchOraclePublicKey(
236
236
  programId
237
237
  )[0];
238
238
  }
239
+
240
+ export function getPythPullOraclePublicKey(
241
+ progarmId: PublicKey,
242
+ feedId: Uint8Array
243
+ ): PublicKey {
244
+ return PublicKey.findProgramAddressSync(
245
+ [
246
+ Buffer.from(anchor.utils.bytes.utf8.encode('pyth_pull')),
247
+ Buffer.from(feedId),
248
+ ],
249
+ progarmId
250
+ )[0];
251
+ }
@@ -1,5 +1,6 @@
1
1
  import {
2
2
  PublicKey,
3
+ SystemProgram,
3
4
  SYSVAR_RENT_PUBKEY,
4
5
  TransactionInstruction,
5
6
  TransactionSignature,
@@ -28,6 +29,7 @@ import {
28
29
  getPhoenixFulfillmentConfigPublicKey,
29
30
  getProtocolIfSharesTransferConfigPublicKey,
30
31
  getPrelaunchOraclePublicKey,
32
+ getPythPullOraclePublicKey,
31
33
  } from './addresses/pda';
32
34
  import { squareRootBN } from './math/utils';
33
35
  import { TOKEN_PROGRAM_ID } from '@solana/spl-token';
@@ -43,6 +45,8 @@ import {
43
45
  import { calculateTargetPriceTrade } from './math/trade';
44
46
  import { calculateAmmReservesAfterSwap, getSwapDirection } from './math/amm';
45
47
  import { PROGRAM_ID as PHOENIX_PROGRAM_ID } from '@ellipsis-labs/phoenix-sdk';
48
+ import { DRIFT_ORACLE_RECEIVER_ID } from './config';
49
+ import { getFeedIdUint8Array } from './util/pythPullOracleUtils';
46
50
 
47
51
  export class AdminClient extends DriftClient {
48
52
  public async initialize(
@@ -3528,4 +3532,39 @@ export class AdminClient extends DriftClient {
3528
3532
  },
3529
3533
  });
3530
3534
  }
3535
+
3536
+ public async initializePythPullOracle(
3537
+ feedId: string
3538
+ ): Promise<TransactionSignature> {
3539
+ const initializePythPullOracleIx = await this.getInitializePythPullOracleIx(
3540
+ feedId
3541
+ );
3542
+ const tx = await this.buildTransaction(initializePythPullOracleIx);
3543
+ const { txSig } = await this.sendTransaction(tx, [], this.opts);
3544
+
3545
+ return txSig;
3546
+ }
3547
+
3548
+ public async getInitializePythPullOracleIx(
3549
+ feedId: string
3550
+ ): Promise<TransactionInstruction> {
3551
+ const feedIdBuffer = getFeedIdUint8Array(feedId);
3552
+ return await this.program.instruction.initializePythPullOracle(
3553
+ feedIdBuffer,
3554
+ {
3555
+ accounts: {
3556
+ admin: this.isSubscribed
3557
+ ? this.getStateAccount().admin
3558
+ : this.wallet.publicKey,
3559
+ state: await this.getStatePublicKey(),
3560
+ systemProgram: SystemProgram.programId,
3561
+ priceFeed: getPythPullOraclePublicKey(
3562
+ this.program.programId,
3563
+ feedIdBuffer
3564
+ ),
3565
+ pythSolanaReceiver: DRIFT_ORACLE_RECEIVER_ID,
3566
+ },
3567
+ }
3568
+ );
3569
+ }
3531
3570
  }
package/src/config.ts CHANGED
@@ -19,6 +19,7 @@ type DriftConfig = {
19
19
  PYTH_ORACLE_MAPPING_ADDRESS: string;
20
20
  DRIFT_PROGRAM_ID: string;
21
21
  JIT_PROXY_PROGRAM_ID?: string;
22
+ DRIFT_ORACLE_RECEIVER_ID: string;
22
23
  USDC_MINT_ADDRESS: string;
23
24
  SERUM_V3: string;
24
25
  PHOENIX: string;
@@ -27,11 +28,14 @@ type DriftConfig = {
27
28
  SPOT_MARKETS: SpotMarketConfig[];
28
29
  MARKET_LOOKUP_TABLE: string;
29
30
  SERUM_LOOKUP_TABLE?: string;
31
+ PYTH_PULL_ORACLE_LOOKUP_TABLE?: string;
30
32
  };
31
33
 
32
34
  export type DriftEnv = 'devnet' | 'mainnet-beta';
33
35
 
34
36
  export const DRIFT_PROGRAM_ID = 'dRiftyHA39MWEi3m9aunc5MzRF1JYuBsbn6VPcn33UH';
37
+ export const DRIFT_ORACLE_RECEIVER_ID =
38
+ 'G6EoTTTgpkNBtVXo96EQp2m6uwwVh2Kt6YidjkmQqoha';
35
39
 
36
40
  export const configs: { [key in DriftEnv]: DriftConfig } = {
37
41
  devnet: {
@@ -47,6 +51,7 @@ export const configs: { [key in DriftEnv]: DriftConfig } = {
47
51
  PERP_MARKETS: DevnetPerpMarkets,
48
52
  SPOT_MARKETS: DevnetSpotMarkets,
49
53
  MARKET_LOOKUP_TABLE: 'FaMS3U4uBojvGn5FSDEPimddcXsCfwkKsFgMVVnDdxGb',
54
+ DRIFT_ORACLE_RECEIVER_ID,
50
55
  },
51
56
  'mainnet-beta': {
52
57
  ENV: 'mainnet-beta',
@@ -62,6 +67,7 @@ export const configs: { [key in DriftEnv]: DriftConfig } = {
62
67
  SPOT_MARKETS: MainnetSpotMarkets,
63
68
  MARKET_LOOKUP_TABLE: 'D9cnvzswDikQDf53k4HpQ3KJ9y1Fv3HGGDFYMXnK5T6c',
64
69
  SERUM_LOOKUP_TABLE: 'GPZkp76cJtNL2mphCvT6FXkJCVPpouidnacckR6rzKDN',
70
+ DRIFT_ORACLE_RECEIVER_ID,
65
71
  },
66
72
  };
67
73