@drift-labs/sdk 2.84.0-beta.5 → 2.84.0-beta.7

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.
@@ -0,0 +1,60 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.convertPythPrice = exports.PythPullClient = void 0;
4
+ const web3_js_1 = require("@solana/web3.js");
5
+ const anchor_1 = require("@coral-xyz/anchor");
6
+ const numericConstants_1 = require("../constants/numericConstants");
7
+ const pyth_solana_receiver_1 = require("@pythnetwork/pyth-solana-receiver");
8
+ const __1 = require("..");
9
+ class PythPullClient {
10
+ constructor(connection, multiple = numericConstants_1.ONE, stableCoin = false) {
11
+ this.connection = connection;
12
+ this.multiple = multiple;
13
+ this.stableCoin = stableCoin;
14
+ const provider = new anchor_1.AnchorProvider(this.connection,
15
+ //@ts-ignore
16
+ new __1.Wallet(new web3_js_1.Keypair()), {
17
+ commitment: connection.commitment,
18
+ });
19
+ this.receiver = new anchor_1.Program(pyth_solana_receiver_1.pythSolanaReceiverIdl, pyth_solana_receiver_1.DEFAULT_RECEIVER_PROGRAM_ID, provider);
20
+ this.decodeFunc =
21
+ this.receiver.account.priceUpdateV2.coder.accounts.decodeUnchecked.bind(this.receiver.account.priceUpdateV2.coder.accounts);
22
+ }
23
+ async getOraclePriceData(pricePublicKey) {
24
+ const accountInfo = await this.connection.getAccountInfo(pricePublicKey);
25
+ return this.getOraclePriceDataFromBuffer(accountInfo.data);
26
+ }
27
+ getOraclePriceDataFromBuffer(buffer) {
28
+ const message = this.decodeFunc('priceUpdateV2', buffer);
29
+ const priceData = message.priceMessage;
30
+ const confidence = convertPythPrice(priceData.conf, priceData.exponent, this.multiple);
31
+ let price = convertPythPrice(priceData.price, priceData.exponent, this.multiple);
32
+ if (this.stableCoin) {
33
+ price = getStableCoinPrice(price, confidence);
34
+ }
35
+ return {
36
+ price,
37
+ slot: message.postedSlot,
38
+ confidence,
39
+ twap: convertPythPrice(priceData.price, priceData.exponent, this.multiple),
40
+ twapConfidence: convertPythPrice(priceData.price, priceData.exponent, this.multiple),
41
+ hasSufficientNumberOfDataPoints: true,
42
+ };
43
+ }
44
+ }
45
+ exports.PythPullClient = PythPullClient;
46
+ function convertPythPrice(price, exponent, multiple) {
47
+ exponent = Math.abs(exponent);
48
+ const pythPrecision = numericConstants_1.TEN.pow(new anchor_1.BN(exponent).abs()).div(multiple);
49
+ return price.mul(numericConstants_1.PRICE_PRECISION).div(pythPrecision);
50
+ }
51
+ exports.convertPythPrice = convertPythPrice;
52
+ const fiveBPS = new anchor_1.BN(500);
53
+ function getStableCoinPrice(price, confidence) {
54
+ if (price.sub(numericConstants_1.QUOTE_PRECISION).abs().lt(anchor_1.BN.min(confidence, fiveBPS))) {
55
+ return numericConstants_1.QUOTE_PRECISION;
56
+ }
57
+ else {
58
+ return price;
59
+ }
60
+ }
@@ -1,5 +1,5 @@
1
1
  import { AddressLookupTableAccount, BlockhashWithExpiryBlockHeight, Commitment, ConfirmOptions, Connection, Signer, Transaction, TransactionInstruction, TransactionVersion, VersionedTransaction } from '@solana/web3.js';
2
- import { DriftClientMetricsEvents, IWallet, TxParams } from '../types';
2
+ import { DriftClientMetricsEvents, IWallet, MappedRecord, SignedTxData, TxParams } from '../types';
3
3
  export declare const COMPUTE_UNITS_DEFAULT = 200000;
4
4
  export type TxBuildingProps = {
5
5
  instructions: TransactionInstruction | TransactionInstruction[];
@@ -61,6 +61,7 @@ export declare class TxHandler {
61
61
  */
62
62
  prepareTx(tx: Transaction, additionalSigners: Array<Signer>, wallet?: IWallet, confirmationOpts?: ConfirmOptions, preSigned?: boolean, recentBlockhash?: BlockhashWithExpiryBlockHeight): Promise<Transaction>;
63
63
  private isVersionedTransaction;
64
+ private isLegacyTransaction;
64
65
  private getTxSigFromSignedTx;
65
66
  private getBlockhashFromSignedTx;
66
67
  private signTx;
@@ -75,7 +76,7 @@ export declare class TxHandler {
75
76
  private _generateVersionedTransaction;
76
77
  generateLegacyVersionedTransaction(recentBlockhash: BlockhashWithExpiryBlockHeight, ixs: TransactionInstruction[], wallet?: IWallet): VersionedTransaction;
77
78
  generateVersionedTransaction(recentBlockhash: BlockhashWithExpiryBlockHeight, ixs: TransactionInstruction[], lookupTableAccounts: AddressLookupTableAccount[], wallet?: IWallet): VersionedTransaction;
78
- generateLegacyTransaction(ixs: TransactionInstruction[]): Transaction;
79
+ generateLegacyTransaction(ixs: TransactionInstruction[], recentBlockhash?: BlockhashWithExpiryBlockHeight): Transaction;
79
80
  /**
80
81
  * Accepts multiple instructions and builds a transaction for each. Prevents needing to spam RPC with requests for the same blockhash.
81
82
  * @param props
@@ -96,46 +97,48 @@ export declare class TxHandler {
96
97
  buildTransaction(props: TxBuildingProps): Promise<Transaction | VersionedTransaction>;
97
98
  wrapInTx(instruction: TransactionInstruction, computeUnits?: number, computeUnitsPrice?: number): Transaction;
98
99
  /**
99
- * Build a map of transactions from an array of instructions for multiple transactions.
100
+ * Get a map of signed and prepared transactions from an array of legacy transactions
100
101
  * @param txsToSign
101
102
  * @param keys
102
103
  * @param wallet
103
104
  * @param commitment
104
105
  * @returns
105
106
  */
106
- buildTransactionMap(txsToSign: (Transaction | undefined)[], keys: string[], wallet?: IWallet, commitment?: Commitment, recentBlockhash?: BlockhashWithExpiryBlockHeight): Promise<{
107
- [key: string]: Transaction | VersionedTransaction;
107
+ getPreparedAndSignedLegacyTransactionMap<T extends Record<string, Transaction | undefined>>(txsMap: T, wallet?: IWallet, commitment?: Commitment, recentBlockhash?: BlockhashWithExpiryBlockHeight): Promise<{
108
+ signedTxMap: T;
109
+ signedTxData: SignedTxData[];
108
110
  }>;
109
111
  /**
110
- * Get a map of signed and prepared transactions from an array of legacy transactions
112
+ * Get a map of signed transactions from an array of transactions to sign.
111
113
  * @param txsToSign
112
114
  * @param keys
113
115
  * @param wallet
114
- * @param commitment
115
116
  * @returns
116
117
  */
117
- getPreparedAndSignedLegacyTransactionMap(txsToSign: (Transaction | undefined)[], keys: string[], wallet?: IWallet, commitment?: Commitment, recentBlockhash?: BlockhashWithExpiryBlockHeight): Promise<{
118
- [key: string]: Transaction | VersionedTransaction;
118
+ getSignedTransactionMap<T extends Record<string, Transaction | VersionedTransaction | undefined>>(txsToSignMap: T, wallet?: IWallet): Promise<{
119
+ signedTxMap: T;
120
+ signedTxData: SignedTxData[];
119
121
  }>;
120
122
  /**
121
- * Get a map of signed transactions from an array of transactions to sign.
122
- * @param txsToSign
123
- * @param keys
124
- * @param wallet
123
+ * Accepts multiple instructions and builds a transaction for each. Prevents needing to spam RPC with requests for the same blockhash.
124
+ * @param props
125
125
  * @returns
126
126
  */
127
- getSignedTransactionMap(txsToSign: (Transaction | VersionedTransaction | undefined)[], keys: string[], wallet?: IWallet): Promise<{
128
- [key: string]: Transaction | VersionedTransaction | undefined;
129
- }>;
127
+ buildTransactionsMap<T extends Record<string, TransactionInstruction | TransactionInstruction[]>>(props: Omit<TxBuildingProps, 'instructions'> & {
128
+ instructionsMap: T;
129
+ }): Promise<MappedRecord<T, Transaction | VersionedTransaction>>;
130
130
  /**
131
131
  * Builds and signs transactions from a given array of instructions for multiple transactions.
132
132
  * @param props
133
133
  * @returns
134
134
  */
135
- buildAndSignTransactionMap(props: Omit<TxBuildingProps, 'instructions'> & {
136
- keys: string[];
137
- instructions: (TransactionInstruction | TransactionInstruction[])[];
135
+ buildAndSignTransactionMap<T extends Record<string, TransactionInstruction | TransactionInstruction[]>>(props: Omit<TxBuildingProps, 'instructions'> & {
136
+ instructionsMap: T;
138
137
  }): Promise<{
139
- [key: string]: Transaction | VersionedTransaction;
138
+ signedTxMap: Record<string, Transaction>;
139
+ signedTxData: SignedTxData[];
140
+ } | {
141
+ signedTxMap: MappedRecord<T, Transaction | VersionedTransaction>;
142
+ signedTxData: SignedTxData[];
140
143
  }>;
141
144
  }
@@ -82,6 +82,9 @@ class TxHandler {
82
82
  isVersionedTransaction(tx) {
83
83
  return (tx === null || tx === void 0 ? void 0 : tx.message) && true;
84
84
  }
85
+ isLegacyTransaction(tx) {
86
+ return !this.isVersionedTransaction(tx);
87
+ }
85
88
  getTxSigFromSignedTx(signedTx) {
86
89
  if (this.isVersionedTransaction(signedTx)) {
87
90
  return bs58_1.default.encode(Buffer.from(signedTx.signatures[0]));
@@ -152,7 +155,7 @@ class TxHandler {
152
155
  }
153
156
  return;
154
157
  }
155
- const fullTxData = txData.map((tx) => {
158
+ const signedTxData = txData.map((tx) => {
156
159
  const lastValidBlockHeight = this.blockHashToLastValidBlockHeightLookup[tx.blockHash];
157
160
  return {
158
161
  ...tx,
@@ -160,8 +163,9 @@ class TxHandler {
160
163
  };
161
164
  });
162
165
  if (this.onSignedCb) {
163
- this.onSignedCb(fullTxData);
166
+ this.onSignedCb(signedTxData);
164
167
  }
168
+ return signedTxData;
165
169
  }
166
170
  /**
167
171
  * Gets transaction params with extra processing applied, like using the simulated compute units or using a dynamically calculated compute unit price.
@@ -225,8 +229,12 @@ class TxHandler {
225
229
  tx.SIGNATURE_BLOCK_AND_EXPIRY = recentBlockhash;
226
230
  return tx;
227
231
  }
228
- generateLegacyTransaction(ixs) {
229
- return new web3_js_1.Transaction().add(...ixs);
232
+ generateLegacyTransaction(ixs, recentBlockhash) {
233
+ const tx = new web3_js_1.Transaction().add(...ixs);
234
+ if (recentBlockhash) {
235
+ tx.recentBlockhash = recentBlockhash.blockhash;
236
+ }
237
+ return tx;
230
238
  }
231
239
  /**
232
240
  * Accepts multiple instructions and builds a transaction for each. Prevents needing to spam RPC with requests for the same blockhash.
@@ -302,7 +310,7 @@ class TxHandler {
302
310
  return this.generateLegacyVersionedTransaction(recentBlockhash, allIx);
303
311
  }
304
312
  else {
305
- return this.generateLegacyTransaction(allIx);
313
+ return this.generateLegacyTransaction(allIx, recentBlockhash);
306
314
  }
307
315
  }
308
316
  else {
@@ -332,30 +340,6 @@ class TxHandler {
332
340
  }
333
341
  return tx.add(instruction);
334
342
  }
335
- /**
336
- * Build a map of transactions from an array of instructions for multiple transactions.
337
- * @param txsToSign
338
- * @param keys
339
- * @param wallet
340
- * @param commitment
341
- * @returns
342
- */
343
- async buildTransactionMap(txsToSign, keys, wallet, commitment, recentBlockhash) {
344
- var _a, _b;
345
- recentBlockhash = recentBlockhash
346
- ? recentBlockhash
347
- : await this.getLatestBlockhashForTransaction();
348
- this.addHashAndExpiryToLookup(recentBlockhash);
349
- for (const tx of txsToSign) {
350
- if (!tx)
351
- continue;
352
- tx.recentBlockhash = recentBlockhash.blockhash;
353
- tx.feePayer = (_a = wallet === null || wallet === void 0 ? void 0 : wallet.publicKey) !== null && _a !== void 0 ? _a : (_b = this.wallet) === null || _b === void 0 ? void 0 : _b.publicKey;
354
- // @ts-ignore
355
- tx.SIGNATURE_BLOCK_AND_EXPIRY = recentBlockhash;
356
- }
357
- return this.getSignedTransactionMap(txsToSign, keys, wallet);
358
- }
359
343
  /**
360
344
  * Get a map of signed and prepared transactions from an array of legacy transactions
361
345
  * @param txsToSign
@@ -364,13 +348,13 @@ class TxHandler {
364
348
  * @param commitment
365
349
  * @returns
366
350
  */
367
- async getPreparedAndSignedLegacyTransactionMap(txsToSign, keys, wallet, commitment, recentBlockhash) {
351
+ async getPreparedAndSignedLegacyTransactionMap(txsMap, wallet, commitment, recentBlockhash) {
368
352
  var _a, _b;
369
353
  recentBlockhash = recentBlockhash
370
354
  ? recentBlockhash
371
355
  : await this.getLatestBlockhashForTransaction();
372
356
  this.addHashAndExpiryToLookup(recentBlockhash);
373
- for (const tx of txsToSign) {
357
+ for (const tx of Object.values(txsMap)) {
374
358
  if (!tx)
375
359
  continue;
376
360
  tx.recentBlockhash = recentBlockhash.blockhash;
@@ -378,7 +362,7 @@ class TxHandler {
378
362
  // @ts-ignore
379
363
  tx.SIGNATURE_BLOCK_AND_EXPIRY = recentBlockhash;
380
364
  }
381
- return this.getSignedTransactionMap(txsToSign, keys, wallet);
365
+ return this.getSignedTransactionMap(txsMap, wallet);
382
366
  }
383
367
  /**
384
368
  * Get a map of signed transactions from an array of transactions to sign.
@@ -387,44 +371,59 @@ class TxHandler {
387
371
  * @param wallet
388
372
  * @returns
389
373
  */
390
- async getSignedTransactionMap(txsToSign, keys, wallet) {
374
+ async getSignedTransactionMap(txsToSignMap, wallet) {
391
375
  var _a;
392
376
  [wallet] = this.getProps(wallet);
393
- const signedTxMap = {};
394
- const keysWithTx = [];
395
- txsToSign.forEach((tx, index) => {
396
- if (tx == undefined) {
397
- signedTxMap[keys[index]] = undefined;
398
- }
399
- else {
400
- keysWithTx.push(keys[index]);
377
+ const txsToSignEntries = Object.entries(txsToSignMap);
378
+ // Create a map of the same keys as the input map, but with the values set to undefined. We'll populate the filtered (non-undefined) values with signed transactions.
379
+ const signedTxMap = txsToSignEntries.reduce((acc, [key]) => {
380
+ acc[key] = undefined;
381
+ return acc;
382
+ }, {});
383
+ const filteredTxEntries = txsToSignEntries.filter(([_, tx]) => !!tx);
384
+ // Extra handling for legacy transactions
385
+ for (const [_key, tx] of filteredTxEntries) {
386
+ if (this.isLegacyTransaction(tx)) {
387
+ tx.feePayer = wallet.publicKey;
401
388
  }
402
- });
389
+ }
403
390
  (_a = this.preSignedCb) === null || _a === void 0 ? void 0 : _a.call(this);
404
- const filteredTxs = txsToSign
405
- .map((tx) => {
406
- return tx;
407
- })
408
- .filter((tx) => tx !== undefined);
409
- const signedTxs = await wallet.signAllTransactions(filteredTxs);
410
- signedTxs.forEach((signedTx, index) => {
391
+ const signedFilteredTxs = await wallet.signAllTransactions(filteredTxEntries.map(([_, tx]) => tx));
392
+ signedFilteredTxs.forEach((signedTx, index) => {
411
393
  var _a;
412
394
  // @ts-ignore
413
395
  signedTx.SIGNATURE_BLOCK_AND_EXPIRY =
414
396
  // @ts-ignore
415
- (_a = txsToSign[index]) === null || _a === void 0 ? void 0 : _a.SIGNATURE_BLOCK_AND_EXPIRY;
397
+ (_a = filteredTxEntries[index][1]) === null || _a === void 0 ? void 0 : _a.SIGNATURE_BLOCK_AND_EXPIRY;
416
398
  });
417
- this.handleSignedTxData(signedTxs.map((signedTx) => {
399
+ const signedTxData = this.handleSignedTxData(signedFilteredTxs.map((signedTx) => {
418
400
  return {
419
401
  txSig: this.getTxSigFromSignedTx(signedTx),
420
402
  signedTx,
421
403
  blockHash: this.getBlockhashFromSignedTx(signedTx),
422
404
  };
423
405
  }));
424
- signedTxs.forEach((signedTx, index) => {
425
- signedTxMap[keysWithTx[index]] = signedTx;
406
+ filteredTxEntries.forEach(([key], index) => {
407
+ const signedTx = signedFilteredTxs[index];
408
+ // @ts-ignore
409
+ signedTxMap[key] = signedTx;
410
+ });
411
+ return { signedTxMap, signedTxData };
412
+ }
413
+ /**
414
+ * Accepts multiple instructions and builds a transaction for each. Prevents needing to spam RPC with requests for the same blockhash.
415
+ * @param props
416
+ * @returns
417
+ */
418
+ async buildTransactionsMap(props) {
419
+ const builtTxs = await this.buildBulkTransactions({
420
+ ...props,
421
+ instructions: Object.values(props.instructionsMap),
426
422
  });
427
- return signedTxMap;
423
+ return Object.keys(props.instructionsMap).reduce((acc, key, index) => {
424
+ acc[key] = builtTxs[index];
425
+ return acc;
426
+ }, {});
428
427
  }
429
428
  /**
430
429
  * Builds and signs transactions from a given array of instructions for multiple transactions.
@@ -432,10 +431,10 @@ class TxHandler {
432
431
  * @returns
433
432
  */
434
433
  async buildAndSignTransactionMap(props) {
435
- const transactions = await this.buildBulkTransactions(props);
434
+ const builtTxs = await this.buildTransactionsMap(props);
436
435
  const preppedTransactions = await (props.txVersion === 'legacy'
437
- ? this.getPreparedAndSignedLegacyTransactionMap(transactions, props.keys, props.wallet, props.preFlightCommitment)
438
- : this.getSignedTransactionMap(transactions, props.keys, props.wallet));
436
+ ? this.getPreparedAndSignedLegacyTransactionMap(builtTxs, props.wallet, props.preFlightCommitment)
437
+ : this.getSignedTransactionMap(builtTxs, props.wallet));
439
438
  return preppedTransactions;
440
439
  }
441
440
  }
package/lib/types.d.ts CHANGED
@@ -1,6 +1,9 @@
1
1
  /// <reference types="bn.js" />
2
2
  import { PublicKey, Transaction, VersionedTransaction } from '@solana/web3.js';
3
3
  import { BN } from '.';
4
+ export type MappedRecord<A extends Record<string, unknown>, B> = {
5
+ [K in keyof A]: B;
6
+ };
4
7
  export declare enum ExchangeStatus {
5
8
  ACTIVE = 0,
6
9
  DEPOSIT_PAUSED = 1,
@@ -154,6 +157,15 @@ export declare class OracleSource {
154
157
  static readonly PYTH_1M: {
155
158
  pyth1M: {};
156
159
  };
160
+ static readonly PYTH_PULL: {
161
+ pythPull: {};
162
+ };
163
+ static readonly PYTH_1K_PULL: {
164
+ pyth1KPull: {};
165
+ };
166
+ static readonly PYTH_1M_PULL: {
167
+ pyth1MPull: {};
168
+ };
157
169
  static readonly SWITCHBOARD: {
158
170
  switchboard: {};
159
171
  };
@@ -163,6 +175,9 @@ export declare class OracleSource {
163
175
  static readonly PYTH_STABLE_COIN: {
164
176
  pythStableCoin: {};
165
177
  };
178
+ static readonly PYTH_STABLE_COIN_PULL: {
179
+ pythStableCoinPull: {};
180
+ };
166
181
  static readonly Prelaunch: {
167
182
  prelaunch: {};
168
183
  };
@@ -795,6 +810,8 @@ export type SpotMarketAccount = {
795
810
  ordersEnabled: boolean;
796
811
  pausedOperations: number;
797
812
  ifPausedOperations: number;
813
+ maxTokenBorrowsFraction: number;
814
+ minBorrowRate: number;
798
815
  };
799
816
  export type PoolBalance = {
800
817
  scaledBalance: BN;
package/lib/types.js CHANGED
@@ -105,9 +105,13 @@ exports.OracleSource = OracleSource;
105
105
  OracleSource.PYTH = { pyth: {} };
106
106
  OracleSource.PYTH_1K = { pyth1K: {} };
107
107
  OracleSource.PYTH_1M = { pyth1M: {} };
108
+ OracleSource.PYTH_PULL = { pythPull: {} };
109
+ OracleSource.PYTH_1K_PULL = { pyth1KPull: {} };
110
+ OracleSource.PYTH_1M_PULL = { pyth1MPull: {} };
108
111
  OracleSource.SWITCHBOARD = { switchboard: {} };
109
112
  OracleSource.QUOTE_ASSET = { quoteAsset: {} };
110
113
  OracleSource.PYTH_STABLE_COIN = { pythStableCoin: {} };
114
+ OracleSource.PYTH_STABLE_COIN_PULL = { pythStableCoinPull: {} };
111
115
  OracleSource.Prelaunch = { prelaunch: {} };
112
116
  class OrderType {
113
117
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@drift-labs/sdk",
3
- "version": "2.84.0-beta.5",
3
+ "version": "2.84.0-beta.7",
4
4
  "main": "lib/index.js",
5
5
  "types": "lib/index.d.ts",
6
6
  "author": "crispheaney",
@@ -34,12 +34,14 @@
34
34
  "access": "public"
35
35
  },
36
36
  "dependencies": {
37
- "@coral-xyz/anchor": "0.29.0",
37
+ "@coral-xyz/anchor": "0.28.0",
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
- "@solana/spl-token": "^0.3.7",
41
+ "@pythnetwork/pyth-solana-receiver": "^0.7.0",
42
+ "@solana/spl-token": "0.3.7",
42
43
  "@solana/web3.js": "1.92.3",
44
+ "rpc-websockets": "7.5.1",
43
45
  "strict-event-emitter-types": "^2.0.0",
44
46
  "uuid": "^8.3.2",
45
47
  "zstddec": "^0.1.0"
@@ -50,17 +52,17 @@
50
52
  "@types/chai": "^4.3.1",
51
53
  "@types/jest": "^28.1.3",
52
54
  "@types/mocha": "^9.1.1",
53
- "@typescript-eslint/eslint-plugin": "^4.28.0",
54
- "@typescript-eslint/parser": "^4.28.0",
55
+ "@typescript-eslint/eslint-plugin": "4.28.0",
56
+ "@typescript-eslint/parser": "4.28.0",
55
57
  "chai": "^4.3.6",
56
58
  "encoding": "^0.1.13",
57
- "eslint": "^7.29.0",
58
- "eslint-config-prettier": "^8.3.0",
59
- "eslint-plugin-prettier": "^3.4.0",
59
+ "eslint": "7.29.0",
60
+ "eslint-config-prettier": "8.3.0",
61
+ "eslint-plugin-prettier": "3.4.0",
60
62
  "lodash": "^4.17.21",
61
63
  "mocha": "^10.0.0",
62
64
  "object-sizeof": "^2.6.3",
63
- "prettier": "^3.0.1",
65
+ "prettier": "3.0.1",
64
66
  "ts-node": "^10.8.0",
65
67
  "typescript": "^4.9.5"
66
68
  },
@@ -1812,6 +1812,44 @@ export class AdminClient extends DriftClient {
1812
1812
  );
1813
1813
  }
1814
1814
 
1815
+ public async updateSpotMarketMaxTokenBorrows(
1816
+ spotMarketIndex: number,
1817
+ maxTokenBorrows: BN
1818
+ ): Promise<TransactionSignature> {
1819
+ const updateSpotMarketMaxTokenBorrowsIx =
1820
+ await this.getUpdateSpotMarketMaxTokenBorrowsIx(
1821
+ spotMarketIndex,
1822
+ maxTokenBorrows
1823
+ );
1824
+
1825
+ const tx = await this.buildTransaction(updateSpotMarketMaxTokenBorrowsIx);
1826
+
1827
+ const { txSig } = await this.sendTransaction(tx, [], this.opts);
1828
+
1829
+ return txSig;
1830
+ }
1831
+
1832
+ public async getUpdateSpotMarketMaxTokenBorrowsIx(
1833
+ spotMarketIndex: number,
1834
+ maxTokenBorrows: BN
1835
+ ): Promise<TransactionInstruction> {
1836
+ return this.program.instruction.updateSpotMarketMaxTokenBorrows(
1837
+ maxTokenBorrows,
1838
+ {
1839
+ accounts: {
1840
+ admin: this.isSubscribed
1841
+ ? this.getStateAccount().admin
1842
+ : this.wallet.publicKey,
1843
+ state: await this.getStatePublicKey(),
1844
+ spotMarket: await getSpotMarketPublicKey(
1845
+ this.program.programId,
1846
+ spotMarketIndex
1847
+ ),
1848
+ },
1849
+ }
1850
+ );
1851
+ }
1852
+
1815
1853
  public async updateSpotMarketScaleInitialAssetWeightStart(
1816
1854
  spotMarketIndex: number,
1817
1855
  scaleInitialAssetWeightStart: BN
@@ -2506,14 +2544,16 @@ export class AdminClient extends DriftClient {
2506
2544
  spotMarketIndex: number,
2507
2545
  optimalUtilization: number,
2508
2546
  optimalBorrowRate: number,
2509
- optimalMaxRate: number
2547
+ optimalMaxRate: number,
2548
+ minBorrowRate?: number | undefined
2510
2549
  ): Promise<TransactionSignature> {
2511
2550
  const updateSpotMarketBorrowRateIx =
2512
2551
  await this.getUpdateSpotMarketBorrowRateIx(
2513
2552
  spotMarketIndex,
2514
2553
  optimalUtilization,
2515
2554
  optimalBorrowRate,
2516
- optimalMaxRate
2555
+ optimalMaxRate,
2556
+ minBorrowRate
2517
2557
  );
2518
2558
 
2519
2559
  const tx = await this.buildTransaction(updateSpotMarketBorrowRateIx);
@@ -2527,12 +2567,14 @@ export class AdminClient extends DriftClient {
2527
2567
  spotMarketIndex: number,
2528
2568
  optimalUtilization: number,
2529
2569
  optimalBorrowRate: number,
2530
- optimalMaxRate: number
2570
+ optimalMaxRate: number,
2571
+ minBorrowRate?: number | undefined
2531
2572
  ): Promise<TransactionInstruction> {
2532
2573
  return await this.program.instruction.updateSpotMarketBorrowRate(
2533
2574
  optimalUtilization,
2534
2575
  optimalBorrowRate,
2535
2576
  optimalMaxRate,
2577
+ minBorrowRate,
2536
2578
  {
2537
2579
  accounts: {
2538
2580
  admin: this.isSubscribed
@@ -39,15 +39,28 @@ export class BlockhashSubscriber {
39
39
  return this.latestBlockHeightContext;
40
40
  }
41
41
 
42
+ /**
43
+ * Returns the latest cached blockhash, based on an offset from the latest obtained
44
+ * @param offset Offset to use, defaulting to 0
45
+ * @param offsetType If 'seconds', it will use calculate the actual element offset based on the update interval; otherwise it will return a fixed index
46
+ * @returns Cached blockhash at the given offset, or undefined
47
+ */
42
48
  getLatestBlockhash(
43
- offset?: number
49
+ offset = 0,
50
+ offsetType: 'index' | 'seconds' = 'index'
44
51
  ): BlockhashWithExpiryBlockHeight | undefined {
45
52
  if (this.blockhashes.length === 0) {
46
53
  return undefined;
47
54
  }
55
+
56
+ const elementOffset =
57
+ offsetType == 'seconds'
58
+ ? Math.floor((offset * 1000) / this.updateIntervalMs)
59
+ : offset;
60
+
48
61
  const clampedOffset = Math.max(
49
62
  0,
50
- Math.min(this.blockhashes.length - 1, offset ?? 0)
63
+ Math.min(this.blockhashes.length - 1, elementOffset)
51
64
  );
52
65
 
53
66
  return this.blockhashes[this.blockhashes.length - 1 - clampedOffset];
@@ -273,6 +273,16 @@ export const MainnetSpotMarkets: SpotMarketConfig[] = [
273
273
  precisionExp: NINE,
274
274
  launchTs: 1716595200000,
275
275
  },
276
+ {
277
+ symbol: 'USDY',
278
+ marketIndex: 18,
279
+ oracle: new PublicKey('DiqUGbq5CV8Tjcae1whjrX97qPo6gU7BKAvKNFc2vrX8'),
280
+ oracleSource: OracleSource.SWITCHBOARD,
281
+ mint: new PublicKey('A1KLoBrKBde8Ty9qtNQUtq3C2ortoC3u7twggz7sEto6'),
282
+ precision: new BN(10).pow(SIX),
283
+ precisionExp: SIX,
284
+ launchTs: 1718811089000,
285
+ },
276
286
  ];
277
287
 
278
288
  export const SpotMarkets: { [key in DriftEnv]: SpotMarketConfig[] } = {