@drift-labs/sdk 0.1.36-master.3 → 0.1.36-master.6

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 (39) hide show
  1. package/lib/accounts/bulkAccountLoader.d.ts +1 -0
  2. package/lib/accounts/bulkAccountLoader.js +4 -0
  3. package/lib/admin.js +1 -1
  4. package/lib/clearingHouse.d.ts +6 -4
  5. package/lib/clearingHouse.js +46 -8
  6. package/lib/examples/makeTradeExample.js +1 -1
  7. package/lib/factory/clearingHouse.js +4 -4
  8. package/lib/idl/clearing_house.json +72 -1
  9. package/lib/index.d.ts +1 -0
  10. package/lib/index.js +1 -0
  11. package/lib/math/oracles.d.ts +3 -0
  12. package/lib/math/oracles.js +26 -0
  13. package/lib/math/trade.d.ts +10 -6
  14. package/lib/math/trade.js +68 -13
  15. package/lib/mockUSDCFaucet.d.ts +2 -2
  16. package/lib/mockUSDCFaucet.js +3 -3
  17. package/lib/oracles/pythClient.js +1 -0
  18. package/lib/oracles/switchboardClient.js +4 -1
  19. package/lib/oracles/types.d.ts +1 -0
  20. package/lib/tx/defaultTxSender.d.ts +3 -3
  21. package/lib/tx/defaultTxSender.js +1 -1
  22. package/lib/tx/retryTxSender.d.ts +3 -3
  23. package/package.json +2 -2
  24. package/src/accounts/bulkAccountLoader.ts +5 -0
  25. package/src/accounts/webSocketAccountSubscriber.ts +3 -3
  26. package/src/admin.ts +2 -2
  27. package/src/clearingHouse.ts +81 -10
  28. package/src/examples/makeTradeExample.ts +6 -2
  29. package/src/factory/clearingHouse.ts +13 -5
  30. package/src/idl/clearing_house.json +72 -1
  31. package/src/index.ts +1 -0
  32. package/src/math/oracles.ts +36 -0
  33. package/src/math/trade.ts +84 -16
  34. package/src/mockUSDCFaucet.ts +5 -5
  35. package/src/oracles/pythClient.ts +1 -0
  36. package/src/oracles/switchboardClient.ts +7 -2
  37. package/src/oracles/types.ts +1 -0
  38. package/src/tx/defaultTxSender.ts +4 -4
  39. package/src/tx/retryTxSender.ts +3 -3
@@ -1,16 +1,16 @@
1
1
  /// <reference types="node" />
2
2
  import { TxSender } from './types';
3
3
  import { Commitment, ConfirmOptions, RpcResponseAndContext, Signer, SignatureResult, Transaction, TransactionSignature, Connection } from '@solana/web3.js';
4
- import { Provider } from '@project-serum/anchor';
4
+ import { AnchorProvider } from '@project-serum/anchor';
5
5
  declare type ResolveReference = {
6
6
  resolve?: () => void;
7
7
  };
8
8
  export declare class RetryTxSender implements TxSender {
9
- provider: Provider;
9
+ provider: AnchorProvider;
10
10
  timeout: number;
11
11
  retrySleep: number;
12
12
  additionalConnections: Connection[];
13
- constructor(provider: Provider, timeout?: number, retrySleep?: number, additionalConnections?: Connection[]);
13
+ constructor(provider: AnchorProvider, timeout?: number, retrySleep?: number, additionalConnections?: Connection[]);
14
14
  send(tx: Transaction, additionalSigners?: Array<Signer>, opts?: ConfirmOptions): Promise<TransactionSignature>;
15
15
  prepareTx(tx: Transaction, additionalSigners: Array<Signer>, opts: ConfirmOptions): Promise<Transaction>;
16
16
  confirmTransaction(signature: TransactionSignature, commitment?: Commitment): Promise<RpcResponseAndContext<SignatureResult>>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@drift-labs/sdk",
3
- "version": "0.1.36-master.3",
3
+ "version": "0.1.36-master.6",
4
4
  "main": "lib/index.js",
5
5
  "types": "lib/index.d.ts",
6
6
  "author": "crispheaney",
@@ -28,7 +28,7 @@
28
28
  "access": "public"
29
29
  },
30
30
  "dependencies": {
31
- "@project-serum/anchor": "0.19.1-beta.1",
31
+ "@project-serum/anchor": "0.24.2",
32
32
  "@pythnetwork/client": "^2.5.1",
33
33
  "@solana/spl-token": "^0.1.6",
34
34
  "@solana/web3.js": "^1.22.0",
@@ -24,6 +24,7 @@ export class BulkAccountLoader {
24
24
  loadPromise?: Promise<void>;
25
25
  loadPromiseResolver: () => void;
26
26
  lastTimeLoadingPromiseCleared = Date.now();
27
+ mostRecentSlot = 0;
27
28
 
28
29
  public constructor(
29
30
  connection: Connection,
@@ -159,6 +160,10 @@ export class BulkAccountLoader {
159
160
 
160
161
  const newSlot = rpcResponse.result.context.slot;
161
162
 
163
+ if (newSlot > this.mostRecentSlot) {
164
+ this.mostRecentSlot = newSlot;
165
+ }
166
+
162
167
  for (const i in accountsToLoad) {
163
168
  const accountToLoad = accountsToLoad[i];
164
169
  const key = accountToLoad.publicKey.toString();
@@ -1,5 +1,5 @@
1
1
  import { AccountData, AccountSubscriber } from './types';
2
- import { Program } from '@project-serum/anchor';
2
+ import { AnchorProvider, Program } from '@project-serum/anchor';
3
3
  import { AccountInfo, Context, PublicKey } from '@solana/web3.js';
4
4
  import { capitalize } from './utils';
5
5
  import * as Buffer from 'buffer';
@@ -36,7 +36,7 @@ export class WebSocketAccountSubscriber<T> implements AccountSubscriber<T> {
36
36
  (accountInfo, context) => {
37
37
  this.handleRpcResponse(context, accountInfo);
38
38
  },
39
- this.program.provider.opts.commitment
39
+ (this.program.provider as AnchorProvider).opts.commitment
40
40
  );
41
41
  }
42
42
 
@@ -44,7 +44,7 @@ export class WebSocketAccountSubscriber<T> implements AccountSubscriber<T> {
44
44
  const rpcResponse =
45
45
  await this.program.provider.connection.getAccountInfoAndContext(
46
46
  this.accountPublicKey,
47
- this.program.provider.opts.commitment
47
+ (this.program.provider as AnchorProvider).opts.commitment
48
48
  );
49
49
  this.handleRpcResponse(rpcResponse.context, rpcResponse?.value);
50
50
  }
package/src/admin.ts CHANGED
@@ -12,7 +12,7 @@ import {
12
12
  OracleSource,
13
13
  OrderFillerRewardStructure,
14
14
  } from './types';
15
- import { BN, Provider } from '@project-serum/anchor';
15
+ import { BN, AnchorProvider } from '@project-serum/anchor';
16
16
  import * as anchor from '@project-serum/anchor';
17
17
  import {
18
18
  getClearingHouseStateAccountPublicKey,
@@ -34,7 +34,7 @@ export class Admin extends ClearingHouse {
34
34
  connection: Connection,
35
35
  wallet: IWallet,
36
36
  clearingHouseProgramId: PublicKey,
37
- opts: ConfirmOptions = Provider.defaultOptions()
37
+ opts: ConfirmOptions = AnchorProvider.defaultOptions()
38
38
  ): Admin {
39
39
  const config = getWebSocketClearingHouseConfig(
40
40
  connection,
@@ -1,4 +1,4 @@
1
- import { BN, Idl, Program, Provider } from '@project-serum/anchor';
1
+ import { AnchorProvider, BN, Idl, Program } from '@project-serum/anchor';
2
2
  import {
3
3
  ASSOCIATED_TOKEN_PROGRAM_ID,
4
4
  Token,
@@ -71,7 +71,7 @@ export class ClearingHouse {
71
71
  connection: Connection;
72
72
  wallet: IWallet;
73
73
  public program: Program;
74
- provider: Provider;
74
+ provider: AnchorProvider;
75
75
  opts?: ConfirmOptions;
76
76
  accountSubscriber: ClearingHouseAccountSubscriber;
77
77
  eventEmitter: StrictEventEmitter<EventEmitter, ClearingHouseAccountEvents>;
@@ -98,7 +98,7 @@ export class ClearingHouse {
98
98
  connection: Connection,
99
99
  wallet: IWallet,
100
100
  clearingHouseProgramId: PublicKey,
101
- opts: ConfirmOptions = Provider.defaultOptions()
101
+ opts: ConfirmOptions = AnchorProvider.defaultOptions()
102
102
  ): ClearingHouse {
103
103
  const config = getWebSocketClearingHouseConfig(
104
104
  connection,
@@ -245,7 +245,11 @@ export class ClearingHouse {
245
245
  * @param newWallet
246
246
  */
247
247
  public updateWallet(newWallet: IWallet): void {
248
- const newProvider = new Provider(this.connection, newWallet, this.opts);
248
+ const newProvider = new AnchorProvider(
249
+ this.connection,
250
+ newWallet,
251
+ this.opts
252
+ );
249
253
  const newProgram = new Program(
250
254
  clearingHouseIDL as Idl,
251
255
  this.program.programId,
@@ -503,7 +507,7 @@ export class ClearingHouse {
503
507
  .add(initializeUserOrdersAccountIx)
504
508
  .add(depositCollateralIx);
505
509
 
506
- const txSig = await this.program.provider.send(tx, [userPositionsAccount]);
510
+ const txSig = await this.txSender.send(tx, [userPositionsAccount]);
507
511
 
508
512
  return [txSig, userAccountPublicKey];
509
513
  }
@@ -538,7 +542,9 @@ export class ClearingHouse {
538
542
  .add(initializeUserOrdersAccountIx)
539
543
  .add(depositCollateralIx);
540
544
 
541
- const txSig = await this.program.provider.send(tx, [userPositionsAccount]);
545
+ const txSig = await this.program.provider.sendAndConfirm(tx, [
546
+ userPositionsAccount,
547
+ ]);
542
548
 
543
549
  return [txSig, userAccountPublicKey];
544
550
  }
@@ -911,17 +917,19 @@ export class ClearingHouse {
911
917
  }
912
918
 
913
919
  public async cancelAllOrders(
914
- oracles?: PublicKey[]
920
+ oracles?: PublicKey[],
921
+ bestEffort?: boolean
915
922
  ): Promise<TransactionSignature> {
916
923
  return await this.txSender.send(
917
- wrapInTx(await this.getCancelAllOrdersIx(oracles)),
924
+ wrapInTx(await this.getCancelAllOrdersIx(oracles, bestEffort)),
918
925
  [],
919
926
  this.opts
920
927
  );
921
928
  }
922
929
 
923
930
  public async getCancelAllOrdersIx(
924
- oracles: PublicKey[]
931
+ oracles: PublicKey[],
932
+ bestEffort?: boolean
925
933
  ): Promise<TransactionInstruction> {
926
934
  const userAccountPublicKey = await this.getUserAccountPublicKey();
927
935
  const userAccount = await this.getUserAccount();
@@ -938,7 +946,7 @@ export class ClearingHouse {
938
946
  });
939
947
  }
940
948
 
941
- return await this.program.instruction.cancelAllOrders({
949
+ return await this.program.instruction.cancelAllOrders(bestEffort, {
942
950
  accounts: {
943
951
  state: await this.getStatePublicKey(),
944
952
  user: userAccountPublicKey,
@@ -955,6 +963,69 @@ export class ClearingHouse {
955
963
  });
956
964
  }
957
965
 
966
+ public async cancelOrdersByMarketAndSide(
967
+ oracles?: PublicKey[],
968
+ bestEffort?: boolean,
969
+ marketIndexOnly?: BN,
970
+ directionOnly?: PositionDirection
971
+ ): Promise<TransactionSignature> {
972
+ return await this.txSender.send(
973
+ wrapInTx(
974
+ await this.getCancelOrdersByMarketAndSideIx(
975
+ oracles,
976
+ bestEffort,
977
+ marketIndexOnly,
978
+ directionOnly
979
+ )
980
+ ),
981
+ [],
982
+ this.opts
983
+ );
984
+ }
985
+
986
+ public async getCancelOrdersByMarketAndSideIx(
987
+ oracles: PublicKey[],
988
+ bestEffort?: boolean,
989
+ marketIndexOnly?: BN,
990
+ directionOnly?: PositionDirection
991
+ ): Promise<TransactionInstruction> {
992
+ const userAccountPublicKey = await this.getUserAccountPublicKey();
993
+ const userAccount = await this.getUserAccount();
994
+
995
+ const state = this.getStateAccount();
996
+ const orderState = this.getOrderStateAccount();
997
+
998
+ const remainingAccounts = [];
999
+ for (const oracle of oracles) {
1000
+ remainingAccounts.push({
1001
+ pubkey: oracle,
1002
+ isWritable: false,
1003
+ isSigner: false,
1004
+ });
1005
+ }
1006
+
1007
+ return await this.program.instruction.cancelOrdersByMarketAndSide(
1008
+ bestEffort,
1009
+ marketIndexOnly,
1010
+ directionOnly,
1011
+ {
1012
+ accounts: {
1013
+ state: await this.getStatePublicKey(),
1014
+ user: userAccountPublicKey,
1015
+ authority: this.wallet.publicKey,
1016
+ markets: state.markets,
1017
+ userOrders: await this.getUserOrdersAccountPublicKey(),
1018
+ userPositions: userAccount.positions,
1019
+ fundingPaymentHistory: state.fundingPaymentHistory,
1020
+ fundingRateHistory: state.fundingRateHistory,
1021
+ orderState: await this.getOrderStatePublicKey(),
1022
+ orderHistory: orderState.orderHistory,
1023
+ },
1024
+ remainingAccounts,
1025
+ }
1026
+ );
1027
+ }
1028
+
958
1029
  public async fillOrder(
959
1030
  userAccountPublicKey: PublicKey,
960
1031
  userOrdersAccountPublicKey: PublicKey,
@@ -1,4 +1,4 @@
1
- import { BN, Provider } from '@project-serum/anchor';
1
+ import { AnchorProvider, BN } from '@project-serum/anchor';
2
2
  import { Wallet } from '..';
3
3
  import { Token, TOKEN_PROGRAM_ID } from '@solana/spl-token';
4
4
  import { Connection, Keypair, PublicKey } from '@solana/web3.js';
@@ -43,7 +43,11 @@ const main = async () => {
43
43
  const connection = new Connection(rpcAddress);
44
44
 
45
45
  // Set up the Provider
46
- const provider = new Provider(connection, wallet, Provider.defaultOptions());
46
+ const provider = new AnchorProvider(
47
+ connection,
48
+ wallet,
49
+ AnchorProvider.defaultOptions()
50
+ );
47
51
 
48
52
  // Check SOL Balance
49
53
  const lamportsBalance = await connection.getBalance(wallet.publicKey);
@@ -2,7 +2,7 @@ import { ConfirmOptions, Connection, PublicKey } from '@solana/web3.js';
2
2
  import { IWallet } from '../types';
3
3
  import { BulkAccountLoader } from '../accounts/bulkAccountLoader';
4
4
  import { TxSender } from '../tx/types';
5
- import { Idl, Program, Provider } from '@project-serum/anchor';
5
+ import { AnchorProvider, Idl, Program } from '@project-serum/anchor';
6
6
  import { ClearingHouse } from '../clearingHouse';
7
7
  import clearingHouseIDL from '../idl/clearing_house.json';
8
8
  import { WebSocketClearingHouseAccountSubscriber } from '../accounts/webSocketClearingHouseAccountSubscriber';
@@ -53,7 +53,7 @@ export function getWebSocketClearingHouseConfig(
53
53
  connection: Connection,
54
54
  wallet: IWallet,
55
55
  programID: PublicKey,
56
- opts: ConfirmOptions = Provider.defaultOptions(),
56
+ opts: ConfirmOptions = AnchorProvider.defaultOptions(),
57
57
  txSenderConfig?: TxSenderConfig
58
58
  ): WebSocketClearingHouseConfiguration {
59
59
  return {
@@ -71,7 +71,7 @@ export function getPollingClearingHouseConfig(
71
71
  wallet: IWallet,
72
72
  programID: PublicKey,
73
73
  accountLoader: BulkAccountLoader,
74
- opts: ConfirmOptions = Provider.defaultOptions(),
74
+ opts: ConfirmOptions = AnchorProvider.defaultOptions(),
75
75
  txSenderConfig?: TxSenderConfig
76
76
  ): PollingClearingHouseConfiguration {
77
77
  return {
@@ -86,7 +86,11 @@ export function getPollingClearingHouseConfig(
86
86
  }
87
87
 
88
88
  export function getClearingHouse(config: ClearingHouseConfig): ClearingHouse {
89
- const provider = new Provider(config.connection, config.wallet, config.opts);
89
+ const provider = new AnchorProvider(
90
+ config.connection,
91
+ config.wallet,
92
+ config.opts
93
+ );
90
94
  const program = new Program(
91
95
  clearingHouseIDL as Idl,
92
96
  config.programID,
@@ -126,7 +130,11 @@ export function getClearingHouse(config: ClearingHouseConfig): ClearingHouse {
126
130
  }
127
131
 
128
132
  export function getAdmin(config: ClearingHouseConfig): Admin {
129
- const provider = new Provider(config.connection, config.wallet, config.opts);
133
+ const provider = new AnchorProvider(
134
+ config.connection,
135
+ config.wallet,
136
+ config.opts
137
+ );
130
138
  const program = new Program(
131
139
  clearingHouseIDL as Idl,
132
140
  config.programID,
@@ -726,7 +726,78 @@
726
726
  "isSigner": false
727
727
  }
728
728
  ],
729
- "args": []
729
+ "args": [
730
+ {
731
+ "name": "bestEffort",
732
+ "type": "bool"
733
+ }
734
+ ]
735
+ },
736
+ {
737
+ "name": "cancelOrdersByMarketAndSide",
738
+ "accounts": [
739
+ {
740
+ "name": "state",
741
+ "isMut": false,
742
+ "isSigner": false
743
+ },
744
+ {
745
+ "name": "orderState",
746
+ "isMut": false,
747
+ "isSigner": false
748
+ },
749
+ {
750
+ "name": "user",
751
+ "isMut": false,
752
+ "isSigner": false
753
+ },
754
+ {
755
+ "name": "authority",
756
+ "isMut": false,
757
+ "isSigner": true
758
+ },
759
+ {
760
+ "name": "markets",
761
+ "isMut": false,
762
+ "isSigner": false
763
+ },
764
+ {
765
+ "name": "userPositions",
766
+ "isMut": true,
767
+ "isSigner": false
768
+ },
769
+ {
770
+ "name": "userOrders",
771
+ "isMut": true,
772
+ "isSigner": false
773
+ },
774
+ {
775
+ "name": "fundingPaymentHistory",
776
+ "isMut": true,
777
+ "isSigner": false
778
+ },
779
+ {
780
+ "name": "orderHistory",
781
+ "isMut": true,
782
+ "isSigner": false
783
+ }
784
+ ],
785
+ "args": [
786
+ {
787
+ "name": "bestEffort",
788
+ "type": "bool"
789
+ },
790
+ {
791
+ "name": "marketIndexOnly",
792
+ "type": "u64"
793
+ },
794
+ {
795
+ "name": "directionOnly",
796
+ "type": {
797
+ "defined": "PositionDirection"
798
+ }
799
+ }
800
+ ]
730
801
  },
731
802
  {
732
803
  "name": "expireOrders",
package/src/index.ts CHANGED
@@ -26,6 +26,7 @@ export * from './math/funding';
26
26
  export * from './math/insuranceFund';
27
27
  export * from './math/market';
28
28
  export * from './math/position';
29
+ export * from './math/oracles';
29
30
  export * from './math/amm';
30
31
  export * from './math/trade';
31
32
  export * from './math/orders';
@@ -0,0 +1,36 @@
1
+ import { AMM, OracleGuardRails } from '../types';
2
+ import { OraclePriceData } from '../oracles/types';
3
+ import { ONE, ZERO } from '../constants/numericConstants';
4
+ import { BN } from '../index';
5
+
6
+ export function isOracleValid(
7
+ amm: AMM,
8
+ oraclePriceData: OraclePriceData,
9
+ oracleGuardRails: OracleGuardRails,
10
+ slot: number
11
+ ): boolean {
12
+ const isOraclePriceNonPositive = oraclePriceData.price.lt(ZERO);
13
+ const isOraclePriceTooVolatile =
14
+ oraclePriceData.price
15
+ .div(BN.max(ONE, amm.lastOraclePriceTwap))
16
+ .gt(oracleGuardRails.validity.tooVolatileRatio) ||
17
+ amm.lastOraclePriceTwap
18
+ .div(BN.max(ONE, oraclePriceData.price))
19
+ .gt(oracleGuardRails.validity.tooVolatileRatio);
20
+
21
+ const isConfidenceTooLarge = oraclePriceData.price
22
+ .div(BN.max(ONE, oraclePriceData.confidence))
23
+ .lt(oracleGuardRails.validity.confidenceIntervalMaxSize);
24
+
25
+ const oracleIsStale = oraclePriceData.slot
26
+ .sub(new BN(slot))
27
+ .gt(oracleGuardRails.validity.slotsBeforeStale);
28
+
29
+ return !(
30
+ !oraclePriceData.hasSufficientNumberOfDataPoints ||
31
+ oracleIsStale ||
32
+ isOraclePriceNonPositive ||
33
+ isOraclePriceTooVolatile ||
34
+ isConfidenceTooLarge
35
+ );
36
+ }
package/src/math/trade.ts CHANGED
@@ -7,7 +7,11 @@ import {
7
7
  AMM_TO_QUOTE_PRECISION_RATIO,
8
8
  ZERO,
9
9
  } from '../constants/numericConstants';
10
- import { calculateMarkPrice } from './market';
10
+ import {
11
+ calculateBidPrice,
12
+ calculateAskPrice,
13
+ calculateMarkPrice,
14
+ } from './market';
11
15
  import {
12
16
  calculateAmmReservesAfterSwap,
13
17
  calculatePrice,
@@ -16,6 +20,7 @@ import {
16
20
  calculateSpreadReserves,
17
21
  } from './amm';
18
22
  import { squareRootBN } from './utils';
23
+ import { isVariant } from '../types';
19
24
 
20
25
  const MAXPCT = new BN(1000); //percentage units are [0,1000] => [0,1]
21
26
 
@@ -37,6 +42,8 @@ export type PriceImpactUnit =
37
42
  * @param direction
38
43
  * @param amount
39
44
  * @param market
45
+ * @param inputAssetType which asset is being traded
46
+ * @param useSpread whether to consider spread with calculating slippage
40
47
  * @return [pctAvgSlippage, pctMaxSlippage, entryPrice, newPrice]
41
48
  *
42
49
  * 'pctAvgSlippage' => the percentage change to entryPrice (average est slippage in execution) : Precision MARK_PRICE_PRECISION
@@ -51,9 +58,20 @@ export function calculateTradeSlippage(
51
58
  direction: PositionDirection,
52
59
  amount: BN,
53
60
  market: Market,
54
- inputAssetType: AssetType = 'quote'
61
+ inputAssetType: AssetType = 'quote',
62
+ useSpread = true
55
63
  ): [BN, BN, BN, BN] {
56
- const oldPrice = calculateMarkPrice(market);
64
+ let oldPrice: BN;
65
+
66
+ if (useSpread && market.amm.baseSpread > 0) {
67
+ if (isVariant(direction, 'long')) {
68
+ oldPrice = calculateAskPrice(market);
69
+ } else {
70
+ oldPrice = calculateBidPrice(market);
71
+ }
72
+ } else {
73
+ oldPrice = calculateMarkPrice(market);
74
+ }
57
75
  if (amount.eq(ZERO)) {
58
76
  return [ZERO, ZERO, oldPrice, oldPrice];
59
77
  }
@@ -61,7 +79,8 @@ export function calculateTradeSlippage(
61
79
  direction,
62
80
  amount,
63
81
  market,
64
- inputAssetType
82
+ inputAssetType,
83
+ useSpread
65
84
  );
66
85
 
67
86
  const entryPrice = calculatePrice(
@@ -70,10 +89,26 @@ export function calculateTradeSlippage(
70
89
  market.amm.pegMultiplier
71
90
  ).mul(new BN(-1));
72
91
 
92
+ let amm: Parameters<typeof calculateAmmReservesAfterSwap>[0];
93
+ if (useSpread && market.amm.baseSpread > 0) {
94
+ const { baseAssetReserve, quoteAssetReserve } = calculateSpreadReserves(
95
+ market.amm,
96
+ direction
97
+ );
98
+ amm = {
99
+ baseAssetReserve,
100
+ quoteAssetReserve,
101
+ sqrtK: market.amm.sqrtK,
102
+ pegMultiplier: market.amm.pegMultiplier,
103
+ };
104
+ } else {
105
+ amm = market.amm;
106
+ }
107
+
73
108
  const newPrice = calculatePrice(
74
- market.amm.baseAssetReserve.sub(acquiredBase),
75
- market.amm.quoteAssetReserve.sub(acquiredQuote),
76
- market.amm.pegMultiplier
109
+ amm.baseAssetReserve.sub(acquiredBase),
110
+ amm.quoteAssetReserve.sub(acquiredQuote),
111
+ amm.pegMultiplier
77
112
  );
78
113
 
79
114
  if (direction == PositionDirection.SHORT) {
@@ -104,7 +139,7 @@ export function calculateTradeSlippage(
104
139
  * @param inputAssetType
105
140
  * @param useSpread
106
141
  * @return
107
- * | 'acquiredBase' => positive/negative change in user's base : BN TODO-PRECISION
142
+ * | 'acquiredBase' => positive/negative change in user's base : BN AMM_RESERVE_PRECISION
108
143
  * | 'acquiredQuote' => positive/negative change in user's quote : BN TODO-PRECISION
109
144
  */
110
145
  export function calculateTradeAcquiredAmounts(
@@ -150,43 +185,63 @@ export function calculateTradeAcquiredAmounts(
150
185
  * @param market
151
186
  * @param targetPrice
152
187
  * @param pct optional default is 100% gap filling, can set smaller.
188
+ * @param outputAssetType which asset to trade.
189
+ * @param useSpread whether or not to consider the spread when calculating the trade size
153
190
  * @returns trade direction/size in order to push price to a targetPrice,
154
191
  *
155
192
  * [
156
- * direction => direction of trade required, TODO-PRECISION
193
+ * direction => direction of trade required, PositionDirection
157
194
  * tradeSize => size of trade required, TODO-PRECISION
158
- * entryPrice => the entry price for the trade, TODO-PRECISION
159
- * targetPrice => the target price TODO-PRECISION
195
+ * entryPrice => the entry price for the trade, MARK_PRICE_PRECISION
196
+ * targetPrice => the target price MARK_PRICE_PRECISION
160
197
  * ]
161
198
  */
162
199
  export function calculateTargetPriceTrade(
163
200
  market: Market,
164
201
  targetPrice: BN,
165
202
  pct: BN = MAXPCT,
166
- outputAssetType: AssetType = 'quote'
203
+ outputAssetType: AssetType = 'quote',
204
+ useSpread = true
167
205
  ): [PositionDirection, BN, BN, BN] {
168
206
  assert(market.amm.baseAssetReserve.gt(ZERO));
169
207
  assert(targetPrice.gt(ZERO));
170
208
  assert(pct.lte(MAXPCT) && pct.gt(ZERO));
171
209
 
172
210
  const markPriceBefore = calculateMarkPrice(market);
211
+ const bidPriceBefore = calculateBidPrice(market);
212
+ const askPriceBefore = calculateAskPrice(market);
173
213
 
214
+ let direction;
174
215
  if (targetPrice.gt(markPriceBefore)) {
175
216
  const priceGap = targetPrice.sub(markPriceBefore);
176
217
  const priceGapScaled = priceGap.mul(pct).div(MAXPCT);
177
218
  targetPrice = markPriceBefore.add(priceGapScaled);
219
+ direction = PositionDirection.LONG;
178
220
  } else {
179
221
  const priceGap = markPriceBefore.sub(targetPrice);
180
222
  const priceGapScaled = priceGap.mul(pct).div(MAXPCT);
181
223
  targetPrice = markPriceBefore.sub(priceGapScaled);
224
+ direction = PositionDirection.SHORT;
182
225
  }
183
226
 
184
- let direction;
185
227
  let tradeSize;
186
228
  let baseSize;
187
229
 
188
- const baseAssetReserveBefore = market.amm.baseAssetReserve;
189
- const quoteAssetReserveBefore = market.amm.quoteAssetReserve;
230
+ let baseAssetReserveBefore: BN;
231
+ let quoteAssetReserveBefore: BN;
232
+
233
+ if (useSpread && market.amm.baseSpread > 0) {
234
+ const { baseAssetReserve, quoteAssetReserve } = calculateSpreadReserves(
235
+ market.amm,
236
+ direction
237
+ );
238
+ baseAssetReserveBefore = baseAssetReserve;
239
+ quoteAssetReserveBefore = quoteAssetReserve;
240
+ } else {
241
+ baseAssetReserveBefore = market.amm.baseAssetReserve;
242
+ quoteAssetReserveBefore = market.amm.quoteAssetReserve;
243
+ }
244
+
190
245
  const peg = market.amm.pegMultiplier;
191
246
  const invariant = market.amm.sqrtK.mul(market.amm.sqrtK);
192
247
  const k = invariant.mul(MARK_PRICE_PRECISION);
@@ -196,7 +251,20 @@ export function calculateTargetPriceTrade(
196
251
  const biasModifier = new BN(1);
197
252
  let markPriceAfter;
198
253
 
199
- if (markPriceBefore.gt(targetPrice)) {
254
+ if (
255
+ useSpread &&
256
+ targetPrice.lt(askPriceBefore) &&
257
+ targetPrice.gt(bidPriceBefore)
258
+ ) {
259
+ // no trade, market is at target
260
+ if (markPriceBefore.gt(targetPrice)) {
261
+ direction = PositionDirection.SHORT;
262
+ } else {
263
+ direction = PositionDirection.LONG;
264
+ }
265
+ tradeSize = ZERO;
266
+ return [direction, tradeSize, targetPrice, targetPrice];
267
+ } else if (markPriceBefore.gt(targetPrice)) {
200
268
  // overestimate y2
201
269
  baseAssetReserveAfter = squareRootBN(
202
270
  k.div(targetPrice).mul(peg).div(PEG_PRECISION).sub(biasModifier)
@@ -1,5 +1,5 @@
1
1
  import * as anchor from '@project-serum/anchor';
2
- import { Idl, Program, Provider } from '@project-serum/anchor';
2
+ import { AnchorProvider, Idl, Program } from '@project-serum/anchor';
3
3
  import {
4
4
  AccountInfo,
5
5
  ASSOCIATED_TOKEN_PROGRAM_ID,
@@ -25,7 +25,7 @@ export class MockUSDCFaucet {
25
25
  connection: Connection;
26
26
  wallet: IWallet;
27
27
  public program: Program;
28
- provider: Provider;
28
+ provider: AnchorProvider;
29
29
  opts?: ConfirmOptions;
30
30
 
31
31
  public constructor(
@@ -36,8 +36,8 @@ export class MockUSDCFaucet {
36
36
  ) {
37
37
  this.connection = connection;
38
38
  this.wallet = wallet;
39
- this.opts = opts || Provider.defaultOptions();
40
- const provider = new Provider(connection, wallet, this.opts);
39
+ this.opts = opts || AnchorProvider.defaultOptions();
40
+ const provider = new AnchorProvider(connection, wallet, this.opts);
41
41
  this.provider = provider;
42
42
  this.program = new Program(mockUSDCFaucetIDL as Idl, programId, provider);
43
43
  }
@@ -140,7 +140,7 @@ export class MockUSDCFaucet {
140
140
  amount
141
141
  );
142
142
  const tx = new Transaction().add(createAssociatedAccountIx).add(mintToTx);
143
- const txSig = await this.program.provider.send(tx, [], this.opts);
143
+ const txSig = await this.program.provider.sendAndConfirm(tx, [], this.opts);
144
144
  return [associatedTokenPublicKey, txSig];
145
145
  }
146
146