@drift-labs/sdk 2.60.0-beta.0 → 2.60.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.
@@ -7,13 +7,14 @@ import {
7
7
  } from '../types';
8
8
  import { calculateAssetWeight, calculateLiabilityWeight } from './spotBalance';
9
9
  import { MARGIN_PRECISION } from '../constants/numericConstants';
10
+ import { numberToSafeBN } from './utils';
10
11
 
11
12
  export function castNumberToSpotPrecision(
12
13
  value: number | BN,
13
14
  spotMarket: SpotMarketAccount
14
15
  ): BN {
15
16
  if (typeof value === 'number') {
16
- return new BN(value * Math.pow(10, spotMarket.decimals));
17
+ return numberToSafeBN(value, new BN(Math.pow(10, spotMarket.decimals)));
17
18
  } else {
18
19
  return value.mul(new BN(Math.pow(10, spotMarket.decimals)));
19
20
  }
@@ -267,66 +267,59 @@ export async function findBestLstSuperStakeIxs({
267
267
  }
268
268
 
269
269
  export type JITO_SOL_METRICS_ENDPOINT_RESPONSE = {
270
- data: {
271
- getStakePoolStats: {
272
- tvl: {
273
- // TVL in SOL, BN
274
- data: number;
275
- date: string;
276
- }[];
277
- supply: {
278
- // jitoSOL supply
279
- data: number;
280
- date: string;
281
- }[];
282
- apy: {
283
- data: number;
284
- date: string;
285
- }[];
286
- };
287
- };
270
+ tvl: {
271
+ // TVL in SOL, BN
272
+ data: number;
273
+ date: string;
274
+ }[];
275
+ supply: {
276
+ // jitoSOL supply
277
+ data: number;
278
+ date: string;
279
+ }[];
280
+ apy: {
281
+ data: number;
282
+ date: string;
283
+ }[];
288
284
  };
289
285
 
290
- const JITO_SOL_START_DATE = '2022-10-31T00:00:00Z';
286
+ /**
287
+ * Removes hours, minutes, seconds from a date, and returns the ISO string value (with milliseconds trimmed from the output (required by Jito API))
288
+ * @param inDate
289
+ * @returns
290
+ */
291
+ const getNormalizedDateString = (inDate: Date) => {
292
+ const date = new Date(inDate.getTime());
293
+ date.setUTCHours(0, 0, 0, 0);
294
+ return date.toISOString().slice(0, 19) + 'Z';
295
+ };
296
+
297
+ const get30DAgo = () => {
298
+ const date = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000);
299
+ return date;
300
+ };
291
301
 
292
302
  export async function fetchJitoSolMetrics() {
293
- const res = await fetch('https://kobe.mainnet.jito.network/', {
294
- body: JSON.stringify({
295
- operationName: 'QueryRoot',
296
- variables: {
297
- request: {
298
- bucketType: 'DAILY',
299
- rangeFilter: {
300
- start: JITO_SOL_START_DATE,
301
- end: new Date().toISOString(),
302
- },
303
- sortBy: {
304
- order: 'ASC',
305
- field: 'BLOCK_TIME',
306
- },
307
- },
303
+ const res = await fetch(
304
+ 'https://kobe.mainnet.jito.network/api/v1/stake_pool_stats',
305
+ {
306
+ headers: {
307
+ 'Content-Type': 'application/json',
308
308
  },
309
- query: `
310
- query QueryRoot($request: GetStakePoolStatsRequest!) {
311
- getStakePoolStats(req: $request) {
312
- tvl {
313
- data
314
- date
315
- }
316
- apy {
317
- data
318
- date
319
- }
320
- supply {
321
- data
322
- date
323
- }
324
- }
325
- }
326
- `,
327
- }),
328
- method: 'POST',
329
- });
309
+ body: JSON.stringify({
310
+ bucket_type: 'Daily',
311
+ range_filter: {
312
+ start: getNormalizedDateString(get30DAgo()),
313
+ end: getNormalizedDateString(new Date()),
314
+ },
315
+ sort_by: {
316
+ order: 'Asc',
317
+ field: 'BlockTime',
318
+ },
319
+ }),
320
+ method: 'POST',
321
+ }
322
+ );
330
323
 
331
324
  const data: JITO_SOL_METRICS_ENDPOINT_RESPONSE = await res.json();
332
325
 
@@ -396,14 +389,11 @@ const getJitoSolHistoricalPriceMap = async (timestamps: number[]) => {
396
389
  const jitoSolHistoricalPriceMap = new Map<number, number>();
397
390
  const jitoSolHistoricalPriceInSol = [];
398
391
 
399
- for (let i = 0; i < data.data.getStakePoolStats.supply.length; i++) {
400
- const priceInSol =
401
- data.data.getStakePoolStats.tvl[i].data /
402
- 10 ** 9 /
403
- data.data.getStakePoolStats.supply[i].data;
392
+ for (let i = 0; i < data.supply.length; i++) {
393
+ const priceInSol = data.tvl[i].data / 10 ** 9 / data.supply[i].data;
404
394
  jitoSolHistoricalPriceInSol.push({
405
395
  price: priceInSol,
406
- ts: data.data.getStakePoolStats.tvl[i].date,
396
+ ts: data.tvl[i].date,
407
397
  });
408
398
  }
409
399
 
package/src/math/utils.ts CHANGED
@@ -95,3 +95,26 @@ export const checkSameDate = (dateString1: string, dateString2: string) => {
95
95
 
96
96
  return isSameDate;
97
97
  };
98
+
99
+ export function isBNSafe(number: number): boolean {
100
+ return number <= 0x1fffffffffffff;
101
+ }
102
+
103
+ /**
104
+ * Converts a number to BN makes sure the number is safe to convert to BN (that it does not overflow number after multiplying by precision)
105
+ * @param number the number to convert to BN
106
+ * @param precision the BN precision to use (i.e. QUOTE_PRECISION and BASE_PRECISION from drift sdk)
107
+ */
108
+ export function numberToSafeBN(number: number, precision: BN): BN {
109
+ // check if number has decimals
110
+ const candidate = number * precision.toNumber();
111
+ if (isBNSafe(candidate)) {
112
+ return new BN(candidate);
113
+ } else {
114
+ if (number % 1 === 0) {
115
+ return new BN(number.toString()).mul(precision);
116
+ } else {
117
+ return new BN(number).mul(precision);
118
+ }
119
+ }
120
+ }
package/src/user.ts CHANGED
@@ -60,6 +60,7 @@ import {
60
60
  sigNum,
61
61
  SpotBalanceType,
62
62
  SpotMarketAccount,
63
+ standardizeBaseAssetAmount,
63
64
  } from '.';
64
65
  import {
65
66
  calculateAssetWeight,
@@ -1973,20 +1974,43 @@ export class User {
1973
1974
  */
1974
1975
  public liquidationPrice(
1975
1976
  marketIndex: number,
1976
- positionBaseSizeChange: BN = ZERO
1977
+ positionBaseSizeChange: BN = ZERO,
1978
+ estimatedEntryPrice: BN = ZERO
1977
1979
  ): BN {
1978
1980
  const totalCollateral = this.getTotalCollateral('Maintenance');
1979
1981
  const maintenanceMarginRequirement = this.getMaintenanceMarginRequirement();
1980
- const freeCollateral = BN.max(
1982
+ let freeCollateral = BN.max(
1981
1983
  ZERO,
1982
1984
  totalCollateral.sub(maintenanceMarginRequirement)
1983
1985
  );
1984
1986
 
1987
+ const oracle =
1988
+ this.driftClient.getPerpMarketAccount(marketIndex).amm.oracle;
1989
+
1990
+ const oraclePrice =
1991
+ this.driftClient.getOracleDataForPerpMarket(marketIndex).price;
1992
+
1985
1993
  const market = this.driftClient.getPerpMarketAccount(marketIndex);
1986
1994
  const currentPerpPosition =
1987
1995
  this.getPerpPositionWithLPSettle(marketIndex, undefined, true)[0] ||
1988
1996
  this.getEmptyPosition(marketIndex);
1989
1997
 
1998
+ positionBaseSizeChange = standardizeBaseAssetAmount(
1999
+ positionBaseSizeChange,
2000
+ market.amm.orderStepSize
2001
+ );
2002
+
2003
+ const freeCollateralChangeFromNewPosition =
2004
+ this.calculateEntriesEffectOnFreeCollateral(
2005
+ market,
2006
+ oraclePrice,
2007
+ currentPerpPosition,
2008
+ positionBaseSizeChange,
2009
+ estimatedEntryPrice
2010
+ );
2011
+
2012
+ freeCollateral = freeCollateral.add(freeCollateralChangeFromNewPosition);
2013
+
1990
2014
  let freeCollateralDelta = this.calculateFreeCollateralDeltaForPerp(
1991
2015
  market,
1992
2016
  currentPerpPosition,
@@ -1997,8 +2021,6 @@ export class User {
1997
2021
  return new BN(-1);
1998
2022
  }
1999
2023
 
2000
- const oracle =
2001
- this.driftClient.getPerpMarketAccount(marketIndex).amm.oracle;
2002
2024
  const spotMarketWithSameOracle = this.driftClient
2003
2025
  .getSpotMarketAccounts()
2004
2026
  .find((market) => market.oracle.equals(oracle));
@@ -2031,8 +2053,6 @@ export class User {
2031
2053
  return new BN(-1);
2032
2054
  }
2033
2055
 
2034
- const oraclePrice =
2035
- this.driftClient.getOracleDataForPerpMarket(marketIndex).price;
2036
2056
  const liqPriceDelta = freeCollateral
2037
2057
  .mul(QUOTE_PRECISION)
2038
2058
  .div(freeCollateralDelta);
@@ -2046,6 +2066,78 @@ export class User {
2046
2066
  return liqPrice;
2047
2067
  }
2048
2068
 
2069
+ calculateEntriesEffectOnFreeCollateral(
2070
+ market: PerpMarketAccount,
2071
+ oraclePrice: BN,
2072
+ perpPosition: PerpPosition,
2073
+ positionBaseSizeChange: BN,
2074
+ estimatedEntryPrice: BN
2075
+ ): BN {
2076
+ let freeCollateralChange = ZERO;
2077
+
2078
+ // update free collateral to account for change in pnl from new position
2079
+ if (!estimatedEntryPrice.eq(ZERO) && !positionBaseSizeChange.eq(ZERO)) {
2080
+ const costBasis = oraclePrice
2081
+ .mul(positionBaseSizeChange.abs())
2082
+ .div(BASE_PRECISION);
2083
+ const newPositionValue = estimatedEntryPrice
2084
+ .mul(positionBaseSizeChange.abs())
2085
+ .div(BASE_PRECISION);
2086
+ if (positionBaseSizeChange.gt(ZERO)) {
2087
+ freeCollateralChange = costBasis.sub(newPositionValue);
2088
+ } else {
2089
+ freeCollateralChange = newPositionValue.sub(costBasis);
2090
+ }
2091
+
2092
+ // assume worst fee tier
2093
+ const takerFeeTier =
2094
+ this.driftClient.getStateAccount().perpFeeStructure.feeTiers[0];
2095
+ const takerFee = newPositionValue
2096
+ .muln(takerFeeTier.feeNumerator)
2097
+ .divn(takerFeeTier.feeDenominator);
2098
+ freeCollateralChange = freeCollateralChange.sub(takerFee);
2099
+ }
2100
+
2101
+ const worstCaseBaseAssetAmount =
2102
+ calculateWorstCaseBaseAssetAmount(perpPosition);
2103
+
2104
+ const marginRatioBefore = calculateMarketMarginRatio(
2105
+ market,
2106
+ worstCaseBaseAssetAmount.abs(),
2107
+ 'Maintenance'
2108
+ );
2109
+
2110
+ const newWorstCaseBaseAssetAmount = worstCaseBaseAssetAmount.add(
2111
+ positionBaseSizeChange
2112
+ );
2113
+
2114
+ const newMarginRatio = calculateMarketMarginRatio(
2115
+ market,
2116
+ newWorstCaseBaseAssetAmount.abs(),
2117
+ 'Maintenance'
2118
+ );
2119
+
2120
+ // update free collateral to account for change in margin ratio from position change
2121
+ freeCollateralChange = freeCollateralChange.sub(
2122
+ worstCaseBaseAssetAmount
2123
+ .mul(oraclePrice)
2124
+ .div(BASE_PRECISION)
2125
+ .mul(new BN(newMarginRatio - marginRatioBefore))
2126
+ .div(MARGIN_PRECISION)
2127
+ );
2128
+
2129
+ // update free collateral to account for new margin requirement from position change
2130
+ freeCollateralChange = freeCollateralChange.sub(
2131
+ positionBaseSizeChange
2132
+ .mul(oraclePrice)
2133
+ .div(BASE_PRECISION)
2134
+ .mul(new BN(newMarginRatio))
2135
+ .div(MARGIN_PRECISION)
2136
+ );
2137
+
2138
+ return freeCollateralChange;
2139
+ }
2140
+
2049
2141
  calculateFreeCollateralDeltaForPerp(
2050
2142
  market: PerpMarketAccount,
2051
2143
  perpPosition: PerpPosition,
@@ -2142,7 +2234,8 @@ export class User {
2142
2234
  */
2143
2235
  public liquidationPriceAfterClose(
2144
2236
  positionMarketIndex: number,
2145
- closeQuoteAmount: BN
2237
+ closeQuoteAmount: BN,
2238
+ estimatedEntryPrice: BN = ZERO
2146
2239
  ): BN {
2147
2240
  const currentPosition =
2148
2241
  this.getPerpPositionWithLPSettle(
@@ -2161,7 +2254,11 @@ export class User {
2161
2254
  )
2162
2255
  .neg();
2163
2256
 
2164
- return this.liquidationPrice(positionMarketIndex, closeBaseAmount);
2257
+ return this.liquidationPrice(
2258
+ positionMarketIndex,
2259
+ closeBaseAmount,
2260
+ estimatedEntryPrice
2261
+ );
2165
2262
  }
2166
2263
 
2167
2264
  /**
package/tests/amm/test.ts CHANGED
@@ -31,6 +31,7 @@ import {
31
31
  ContractTier,
32
32
  isOracleValid,
33
33
  OracleGuardRails,
34
+ getNewOracleConfPct,
34
35
  // calculateReservePrice,
35
36
  } from '../../src';
36
37
  import { mockPerpMarkets } from '../dlob/helpers';
@@ -662,7 +663,7 @@ describe('AMM Tests', () => {
662
663
  new BN(suiExample.amm.totalFeeMinusDistributions),
663
664
  new BN(suiExample.amm.netRevenueSinceLastFunding),
664
665
  new BN(suiExample.amm.sqrtK),
665
- new BN(suiExample.amm.sqrtK.sub()),
666
+ new BN(suiExample.amm.sqrtK),
666
667
  new BN(suiExample.amm.maxBaseAssetReserve),
667
668
  new BN(suiExample.amm.markStd),
668
669
  new BN(suiExample.amm.oracleStd),
@@ -723,7 +724,7 @@ describe('AMM Tests', () => {
723
724
  );
724
725
 
725
726
  console.log('starting rr:');
726
- let reservePrice = undefined;
727
+ let reservePrice: BN | undefined = undefined;
727
728
  if (!reservePrice) {
728
729
  reservePrice = calculatePrice(
729
730
  mockAmm.baseAssetReserve,
@@ -748,7 +749,6 @@ describe('AMM Tests', () => {
748
749
  console.log('reservePrice:', reservePrice.toString());
749
750
  console.log('targetMarkSpreadPct:', targetMarkSpreadPct.toString());
750
751
  console.log('confIntervalPct:', confIntervalPct.toString());
751
-
752
752
  console.log('liveOracleStd:', liveOracleStd.toString());
753
753
 
754
754
  const tt = calculateSpread(mockAmm, oraclePriceData, now);
@@ -1138,6 +1138,22 @@ describe('AMM Tests', () => {
1138
1138
  console.log('liveOracleStd:', liveOracleStd.toNumber());
1139
1139
  assert(liveOracleStd.eq(new BN(192962)));
1140
1140
 
1141
+ mockAmm.lastOracleConfPct = new BN(150000);
1142
+ const reservePrice = new BN(13.553 * PRICE_PRECISION.toNumber());
1143
+ const newConfPct = getNewOracleConfPct(
1144
+ mockAmm,
1145
+ oraclePriceData,
1146
+ reservePrice,
1147
+ now
1148
+ );
1149
+ console.log('newConfPct:', newConfPct.toString());
1150
+
1151
+ assert(
1152
+ now.sub(mockAmm.historicalOracleData.lastOraclePriceTwapTs).gt(ZERO)
1153
+ );
1154
+
1155
+ assert(newConfPct.eq(new BN(135000)));
1156
+
1141
1157
  const oracleGuardRails: OracleGuardRails = {
1142
1158
  priceDivergence: {
1143
1159
  markOraclePercentDivergence: PERCENTAGE_PRECISION.divn(10),
package/tests/bn/test.ts CHANGED
@@ -1,8 +1,9 @@
1
- import { BN } from '../../src/index';
1
+ import { BN, numberToSafeBN } from '../../src/index';
2
2
  import { expect } from 'chai';
3
3
  import { BigNum } from '../../src/factory/bigNum';
4
4
  import {
5
5
  AMM_RESERVE_PRECISION_EXP,
6
+ BASE_PRECISION,
6
7
  BASE_PRECISION_EXP,
7
8
  TEN_THOUSAND,
8
9
  } from '../../src/constants/numericConstants';
@@ -324,4 +325,17 @@ describe('BigNum Tests', () => {
324
325
  const val6 = BigNum.from('0', 5);
325
326
  expect(val6.toRounded(3).print()).to.equal('0.00000');
326
327
  });
328
+
329
+ it('test numberToSafeBN', async () => {
330
+ expect(
331
+ numberToSafeBN(32445073.479281776, BASE_PRECISION).toString()
332
+ ).to.equal(new BN('32445073000000000').toString());
333
+ expect(
334
+ // eslint-disable-next-line @typescript-eslint/no-loss-of-precision
335
+ numberToSafeBN(9999999999111111111, BASE_PRECISION).toString()
336
+ ).to.equal(new BN('9999999999111110000000000000').toString());
337
+ expect(numberToSafeBN(123, BASE_PRECISION).toString()).to.equal(
338
+ new BN('123000000000').toString()
339
+ );
340
+ });
327
341
  });
@@ -1,66 +0,0 @@
1
- import { Connection, PublicKey } from '@solana/web3.js';
2
- import {
3
- BASE_PRECISION,
4
- L2Level,
5
- PRICE_PRECISION,
6
- PhoenixSubscriber,
7
- } from '../src';
8
- import { PROGRAM_ID } from '@ellipsis-labs/phoenix-sdk';
9
-
10
- export async function listenToBook(): Promise<void> {
11
- const connection = new Connection('https://api.mainnet-beta.solana.com');
12
-
13
- for (const market of [
14
- '4DoNfFBfF7UokCC2FQzriy7yHK6DY6NVdYpuekQ5pRgg', // SOL/USDC
15
- 'Ew3vFDdtdGrknJAVVfraxCA37uNJtimXYPY4QjnfhFHH', // ETH/USDC
16
- '2sTMN9A1D1qeZLF95XQgJCUPiKe5DiV52jLfZGqMP46m', // PYTH/USDC
17
- 'BRLLmdtPGuuFn3BU6orYw4KHaohAEptBToi3dwRUnHQZ', // JTO/USDC
18
- ]) {
19
- const phoenixSubscriber = new PhoenixSubscriber({
20
- connection,
21
- programId: PROGRAM_ID,
22
- marketAddress: new PublicKey(market),
23
- accountSubscription: {
24
- type: 'websocket',
25
- },
26
- });
27
-
28
- await phoenixSubscriber.subscribe();
29
-
30
- const bids = phoenixSubscriber.getL2Levels('bids');
31
- const asks = phoenixSubscriber.getL2Levels('asks');
32
- let bid: L2Level | null = null;
33
- for (const b of bids) {
34
- bid = b;
35
- break;
36
- }
37
- let ask: L2Level | null = null;
38
- for (const a of asks) {
39
- ask = a;
40
- break;
41
- }
42
-
43
- console.log('market', market);
44
- console.log(
45
- (bid?.size.toNumber() || 0) / BASE_PRECISION.toNumber(),
46
- (bid?.price.toNumber() || 0) / PRICE_PRECISION.toNumber(),
47
- '@',
48
- (ask?.price.toNumber() || (1 << 53) - 1) / PRICE_PRECISION.toNumber(),
49
- (ask?.size.toNumber() || 0) / BASE_PRECISION.toNumber()
50
- );
51
- console.log();
52
-
53
- await phoenixSubscriber.unsubscribe();
54
- }
55
- }
56
-
57
- (async function () {
58
- try {
59
- await listenToBook();
60
- } catch (err) {
61
- console.log('Error: ', err);
62
- process.exit(1);
63
- }
64
-
65
- process.exit(0);
66
- })();
@@ -1 +0,0 @@
1
- export {};
@@ -1,59 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const anchor_1 = require("@coral-xyz/anchor");
4
- const __1 = require("..");
5
- const web3_js_1 = require("@solana/web3.js");
6
- const __2 = require("..");
7
- const env = 'mainnet-beta';
8
- const main = async () => {
9
- // Initialize Drift SDK
10
- const sdkConfig = (0, __2.initialize)({ env });
11
- // Set up the Wallet and Provider
12
- const privateKey = process.env.BOT_PRIVATE_KEY; // stored as an array string
13
- const keypair = web3_js_1.Keypair.fromSecretKey(Uint8Array.from(JSON.parse(privateKey)));
14
- const wallet = new __1.Wallet(keypair);
15
- // Set up the Connection
16
- const rpcAddress = process.env.RPC_ADDRESS; // can use: https://api.devnet.solana.com for devnet; https://api.mainnet-beta.solana.com for mainnet;
17
- const connection = new web3_js_1.Connection(rpcAddress);
18
- // Set up the Provider
19
- const provider = new anchor_1.AnchorProvider(connection,
20
- // @ts-ignore
21
- wallet, anchor_1.AnchorProvider.defaultOptions());
22
- // Set up the Drift Clearing House
23
- const driftPublicKey = new web3_js_1.PublicKey(sdkConfig.DRIFT_PROGRAM_ID);
24
- const bulkAccountLoader = new __2.BulkAccountLoader(connection, 'confirmed', 1000);
25
- const driftClient = new __2.DriftClient({
26
- connection,
27
- wallet: provider.wallet,
28
- programID: driftPublicKey,
29
- ...(0, __2.getMarketsAndOraclesForSubscription)(env),
30
- accountSubscription: {
31
- type: 'polling',
32
- accountLoader: bulkAccountLoader,
33
- },
34
- });
35
- console.log('Subscribing drift client...');
36
- await driftClient.subscribe();
37
- console.log('Loading user map...');
38
- const userMap = new __1.UserMap({
39
- driftClient,
40
- subscriptionConfig: {
41
- type: 'websocket',
42
- commitment: 'processed',
43
- },
44
- skipInitialLoad: false,
45
- includeIdle: false,
46
- });
47
- // fetches all users and subscribes for updates
48
- await userMap.subscribe();
49
- console.log('Loading dlob from user map...');
50
- const slot = await driftClient.connection.getSlot();
51
- const dlob = await userMap.getDLOB(slot);
52
- console.log('number of orders', dlob.getDLOBOrders().length);
53
- dlob.clear();
54
- console.log('Unsubscribing users...');
55
- await userMap.unsubscribe();
56
- console.log('Unsubscribing drift client...');
57
- await driftClient.unsubscribe();
58
- };
59
- main();
@@ -1,2 +0,0 @@
1
- import { PublicKey } from '@solana/web3.js';
2
- export declare const getTokenAddress: (mintAddress: string, userPubKey: string) => Promise<PublicKey>;
@@ -1,83 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getTokenAddress = void 0;
4
- const anchor_1 = require("@coral-xyz/anchor");
5
- const __1 = require("..");
6
- const spl_token_1 = require("@solana/spl-token");
7
- const web3_js_1 = require("@solana/web3.js");
8
- const __2 = require("..");
9
- const spotMarkets_1 = require("../constants/spotMarkets");
10
- const getTokenAddress = (mintAddress, userPubKey) => {
11
- return (0, spl_token_1.getAssociatedTokenAddress)(new web3_js_1.PublicKey(mintAddress), new web3_js_1.PublicKey(userPubKey));
12
- };
13
- exports.getTokenAddress = getTokenAddress;
14
- const env = 'devnet';
15
- const main = async () => {
16
- // Initialize Drift SDK
17
- const sdkConfig = (0, __2.initialize)({ env });
18
- // Set up the Wallet and Provider
19
- const privateKey = process.env.BOT_PRIVATE_KEY; // stored as an array string
20
- const keypair = web3_js_1.Keypair.fromSecretKey(Uint8Array.from(JSON.parse(privateKey)));
21
- const wallet = new __1.Wallet(keypair);
22
- // Set up the Connection
23
- const rpcAddress = process.env.RPC_ADDRESS; // can use: https://api.devnet.solana.com for devnet; https://api.mainnet-beta.solana.com for mainnet;
24
- const connection = new web3_js_1.Connection(rpcAddress);
25
- // Set up the Provider
26
- const provider = new anchor_1.AnchorProvider(connection,
27
- // @ts-ignore
28
- wallet, anchor_1.AnchorProvider.defaultOptions());
29
- // Check SOL Balance
30
- const lamportsBalance = await connection.getBalance(wallet.publicKey);
31
- console.log('SOL balance:', lamportsBalance / 10 ** 9);
32
- // Misc. other things to set up
33
- const usdcTokenAddress = await (0, exports.getTokenAddress)(sdkConfig.USDC_MINT_ADDRESS, wallet.publicKey.toString());
34
- // Set up the Drift Clearing House
35
- const driftPublicKey = new web3_js_1.PublicKey(sdkConfig.DRIFT_PROGRAM_ID);
36
- const bulkAccountLoader = new __2.BulkAccountLoader(connection, 'confirmed', 1000);
37
- const driftClient = new __2.DriftClient({
38
- connection,
39
- wallet: provider.wallet,
40
- programID: driftPublicKey,
41
- ...(0, __2.getMarketsAndOraclesForSubscription)(env),
42
- accountSubscription: {
43
- type: 'polling',
44
- accountLoader: bulkAccountLoader,
45
- },
46
- });
47
- await driftClient.subscribe();
48
- // Set up user client
49
- const user = new __2.User({
50
- driftClient: driftClient,
51
- userAccountPublicKey: await driftClient.getUserAccountPublicKey(),
52
- accountSubscription: {
53
- type: 'polling',
54
- accountLoader: bulkAccountLoader,
55
- },
56
- });
57
- //// Check if user account exists for the current wallet
58
- const userAccountExists = await user.exists();
59
- if (!userAccountExists) {
60
- //// Create a Clearing House account by Depositing some USDC ($10,000 in this case)
61
- const depositAmount = new anchor_1.BN(10000).mul(__2.QUOTE_PRECISION);
62
- await driftClient.initializeUserAccountAndDepositCollateral(depositAmount, await (0, exports.getTokenAddress)(usdcTokenAddress.toString(), wallet.publicKey.toString()), spotMarkets_1.SpotMarkets['devnet'][0].marketIndex);
63
- }
64
- await user.subscribe();
65
- // Get current price
66
- const solMarketInfo = sdkConfig.PERP_MARKETS.find((market) => market.baseAssetSymbol === 'SOL');
67
- const marketIndex = solMarketInfo.marketIndex;
68
- const [bid, ask] = (0, __1.calculateBidAskPrice)(driftClient.getPerpMarketAccount(marketIndex).amm, driftClient.getOracleDataForPerpMarket(marketIndex));
69
- const formattedBidPrice = (0, __2.convertToNumber)(bid, __2.PRICE_PRECISION);
70
- const formattedAskPrice = (0, __2.convertToNumber)(ask, __2.PRICE_PRECISION);
71
- console.log(`Current amm bid and ask price are $${formattedBidPrice} and $${formattedAskPrice}`);
72
- // Estimate the slippage for a $5000 LONG trade
73
- const solMarketAccount = driftClient.getPerpMarketAccount(solMarketInfo.marketIndex);
74
- const slippage = (0, __2.convertToNumber)((0, __2.calculateTradeSlippage)(__2.PositionDirection.LONG, new anchor_1.BN(1).mul(__1.BASE_PRECISION), solMarketAccount, 'base', driftClient.getOracleDataForPerpMarket(solMarketInfo.marketIndex))[0], __2.PRICE_PRECISION);
75
- console.log(`Slippage for a 1 SOL-PERP would be $${slippage}`);
76
- await driftClient.placePerpOrder((0, __1.getMarketOrderParams)({
77
- baseAssetAmount: new anchor_1.BN(1).mul(__1.BASE_PRECISION),
78
- direction: __2.PositionDirection.LONG,
79
- marketIndex: solMarketAccount.marketIndex,
80
- }));
81
- console.log(`Placed a 1 SOL-PERP LONG order`);
82
- };
83
- main();