@drift-labs/sdk 0.2.0-temp.2 → 2.0.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 (278) hide show
  1. package/README.md +27 -29
  2. package/lib/accounts/bulkAccountLoader.d.ts +2 -0
  3. package/lib/accounts/bulkAccountLoader.js +36 -29
  4. package/lib/accounts/bulkUserStatsSubscription.d.ts +7 -0
  5. package/lib/accounts/bulkUserStatsSubscription.js +21 -0
  6. package/lib/accounts/bulkUserSubscription.d.ts +2 -2
  7. package/lib/accounts/bulkUserSubscription.js +0 -1
  8. package/lib/accounts/fetch.d.ts +2 -1
  9. package/lib/accounts/fetch.js +9 -1
  10. package/lib/accounts/{pollingClearingHouseAccountSubscriber.d.ts → pollingDriftClientAccountSubscriber.d.ts} +20 -25
  11. package/lib/accounts/{pollingClearingHouseAccountSubscriber.js → pollingDriftClientAccountSubscriber.js} +45 -49
  12. package/lib/accounts/{pollingOracleSubscriber.d.ts → pollingOracleAccountSubscriber.d.ts} +2 -2
  13. package/lib/accounts/{pollingOracleSubscriber.js → pollingOracleAccountSubscriber.js} +3 -3
  14. package/lib/accounts/pollingUserAccountSubscriber.d.ts +1 -1
  15. package/lib/accounts/pollingUserAccountSubscriber.js +5 -11
  16. package/lib/accounts/pollingUserStatsAccountSubscriber.d.ts +27 -0
  17. package/lib/accounts/pollingUserStatsAccountSubscriber.js +107 -0
  18. package/lib/accounts/types.d.ts +26 -15
  19. package/lib/accounts/webSocketDriftClientAccountSubscriber.d.ts +49 -0
  20. package/lib/accounts/{webSocketClearingHouseAccountSubscriber.js → webSocketDriftClientAccountSubscriber.js} +47 -45
  21. package/lib/accounts/webSocketUserStatsAccountSubsriber.d.ts +20 -0
  22. package/lib/accounts/webSocketUserStatsAccountSubsriber.js +47 -0
  23. package/lib/addresses/marketAddresses.d.ts +1 -3
  24. package/lib/addresses/marketAddresses.js +1 -1
  25. package/lib/addresses/pda.d.ts +15 -9
  26. package/lib/addresses/pda.js +73 -35
  27. package/lib/adminClient.d.ts +65 -0
  28. package/lib/adminClient.js +637 -0
  29. package/lib/config.d.ts +10 -10
  30. package/lib/config.js +26 -22
  31. package/lib/constants/numericConstants.d.ts +29 -12
  32. package/lib/constants/numericConstants.js +40 -21
  33. package/lib/constants/perpMarkets.d.ts +18 -0
  34. package/lib/constants/{markets.js → perpMarkets.js} +20 -9
  35. package/lib/constants/spotMarkets.d.ts +19 -0
  36. package/lib/constants/spotMarkets.js +63 -0
  37. package/lib/dlob/DLOB.d.ts +82 -0
  38. package/lib/dlob/DLOB.js +694 -0
  39. package/lib/dlob/DLOBNode.d.ts +54 -0
  40. package/lib/dlob/DLOBNode.js +77 -0
  41. package/lib/dlob/NodeList.d.ts +27 -0
  42. package/lib/dlob/NodeList.js +144 -0
  43. package/lib/driftClient.d.ts +234 -0
  44. package/lib/driftClient.js +2108 -0
  45. package/lib/{clearingHouseConfig.d.ts → driftClientConfig.d.ts} +9 -9
  46. package/lib/{clearingHouseConfig.js → driftClientConfig.js} +0 -0
  47. package/lib/events/eventList.d.ts +0 -1
  48. package/lib/events/eventList.js +0 -7
  49. package/lib/events/eventSubscriber.d.ts +5 -2
  50. package/lib/events/eventSubscriber.js +25 -11
  51. package/lib/events/fetchLogs.d.ts +13 -2
  52. package/lib/events/fetchLogs.js +45 -14
  53. package/lib/events/pollingLogProvider.d.ts +2 -1
  54. package/lib/events/pollingLogProvider.js +7 -3
  55. package/lib/events/sort.js +8 -11
  56. package/lib/events/txEventCache.d.ts +0 -2
  57. package/lib/events/txEventCache.js +0 -14
  58. package/lib/events/types.d.ts +11 -3
  59. package/lib/events/types.js +8 -0
  60. package/lib/events/webSocketLogProvider.js +1 -1
  61. package/lib/examples/makeTradeExample.js +30 -18
  62. package/lib/factory/bigNum.d.ts +8 -4
  63. package/lib/factory/bigNum.js +109 -19
  64. package/lib/idl/drift.json +8392 -0
  65. package/lib/index.d.ts +29 -12
  66. package/lib/index.js +29 -12
  67. package/lib/math/amm.d.ts +9 -6
  68. package/lib/math/amm.js +91 -38
  69. package/lib/math/conversion.js +1 -1
  70. package/lib/math/exchangeStatus.d.ts +4 -0
  71. package/lib/math/exchangeStatus.js +18 -0
  72. package/lib/math/funding.d.ts +6 -6
  73. package/lib/math/funding.js +23 -21
  74. package/lib/math/insurance.d.ts +4 -0
  75. package/lib/math/insurance.js +27 -0
  76. package/lib/math/margin.d.ts +11 -0
  77. package/lib/math/margin.js +82 -0
  78. package/lib/math/market.d.ts +14 -9
  79. package/lib/math/market.js +70 -10
  80. package/lib/math/oracles.d.ts +4 -0
  81. package/lib/math/oracles.js +36 -8
  82. package/lib/math/orders.d.ts +16 -6
  83. package/lib/math/orders.js +97 -17
  84. package/lib/math/position.d.ts +27 -13
  85. package/lib/math/position.js +91 -37
  86. package/lib/math/repeg.js +17 -8
  87. package/lib/math/spotBalance.d.ts +22 -0
  88. package/lib/math/spotBalance.js +192 -0
  89. package/lib/math/spotMarket.d.ts +4 -0
  90. package/lib/math/spotMarket.js +8 -0
  91. package/lib/math/spotPosition.d.ts +6 -0
  92. package/lib/math/spotPosition.js +23 -0
  93. package/lib/math/trade.d.ts +10 -10
  94. package/lib/math/trade.js +27 -31
  95. package/lib/oracles/pythClient.js +1 -1
  96. package/lib/oracles/quoteAssetOracleClient.js +1 -1
  97. package/lib/oracles/switchboardClient.js +1 -1
  98. package/lib/orderParams.d.ts +4 -4
  99. package/lib/orderParams.js +12 -4
  100. package/lib/serum/serumFulfillmentConfigMap.d.ts +10 -0
  101. package/lib/serum/serumFulfillmentConfigMap.js +17 -0
  102. package/lib/serum/serumSubscriber.d.ts +27 -0
  103. package/lib/serum/serumSubscriber.js +56 -0
  104. package/lib/serum/types.d.ts +11 -0
  105. package/{src/oracles → lib/serum}/types.js +0 -0
  106. package/lib/tokenFaucet.d.ts +1 -0
  107. package/lib/tokenFaucet.js +23 -12
  108. package/lib/tx/retryTxSender.d.ts +1 -1
  109. package/lib/tx/retryTxSender.js +13 -4
  110. package/lib/tx/types.d.ts +1 -1
  111. package/lib/types.d.ts +631 -222
  112. package/lib/types.js +137 -24
  113. package/lib/user.d.ts +228 -0
  114. package/lib/user.js +959 -0
  115. package/lib/userConfig.d.ts +14 -0
  116. package/lib/{clearingHouseUserConfig.js → userConfig.js} +0 -0
  117. package/lib/userMap/userMap.d.ts +41 -0
  118. package/lib/userMap/userMap.js +85 -0
  119. package/lib/userMap/userStatsMap.d.ts +19 -0
  120. package/lib/userMap/userStatsMap.js +68 -0
  121. package/lib/userName.d.ts +1 -0
  122. package/lib/userName.js +3 -2
  123. package/lib/userStats.d.ts +18 -0
  124. package/lib/userStats.js +49 -0
  125. package/lib/userStatsConfig.d.ts +14 -0
  126. package/{src/clearingHouseConfig.js → lib/userStatsConfig.js} +0 -0
  127. package/lib/util/getTokenAddress.d.ts +2 -0
  128. package/lib/util/getTokenAddress.js +9 -0
  129. package/package.json +12 -5
  130. package/src/accounts/bulkAccountLoader.ts +44 -34
  131. package/src/accounts/bulkUserStatsSubscription.ts +33 -0
  132. package/src/accounts/bulkUserSubscription.ts +2 -3
  133. package/src/accounts/fetch.ts +27 -2
  134. package/src/accounts/{pollingClearingHouseAccountSubscriber.ts → pollingDriftClientAccountSubscriber.ts} +65 -75
  135. package/src/accounts/{pollingOracleSubscriber.ts → pollingOracleAccountSubscriber.ts} +2 -2
  136. package/src/accounts/pollingUserAccountSubscriber.ts +5 -12
  137. package/src/accounts/pollingUserStatsAccountSubscriber.ts +166 -0
  138. package/src/accounts/types.ts +35 -15
  139. package/src/accounts/{webSocketClearingHouseAccountSubscriber.ts → webSocketDriftClientAccountSubscriber.ts} +78 -73
  140. package/src/accounts/webSocketUserStatsAccountSubsriber.ts +80 -0
  141. package/src/addresses/marketAddresses.ts +3 -4
  142. package/src/addresses/pda.ts +105 -33
  143. package/src/adminClient.ts +1207 -0
  144. package/src/config.ts +41 -34
  145. package/src/constants/numericConstants.ts +59 -26
  146. package/src/constants/{markets.ts → perpMarkets.ts} +22 -11
  147. package/src/constants/spotMarkets.ts +83 -0
  148. package/src/dlob/DLOB.ts +1120 -0
  149. package/src/dlob/DLOBNode.ts +155 -0
  150. package/src/dlob/NodeList.ts +195 -0
  151. package/src/driftClient.ts +3594 -0
  152. package/src/{clearingHouseConfig.ts → driftClientConfig.ts} +9 -8
  153. package/src/events/eventList.ts +1 -8
  154. package/src/events/eventSubscriber.ts +36 -14
  155. package/src/events/fetchLogs.ts +60 -15
  156. package/src/events/pollingLogProvider.ts +11 -3
  157. package/src/events/sort.ts +11 -15
  158. package/src/events/txEventCache.ts +0 -16
  159. package/src/events/types.ts +27 -2
  160. package/src/events/webSocketLogProvider.ts +1 -1
  161. package/src/examples/makeTradeExample.js +152 -75
  162. package/src/examples/makeTradeExample.ts +44 -28
  163. package/src/factory/bigNum.ts +150 -22
  164. package/src/idl/drift.json +8392 -0
  165. package/src/idl/pyth.json +98 -2
  166. package/src/index.ts +29 -12
  167. package/src/math/amm.ts +161 -48
  168. package/src/math/conversion.ts +2 -2
  169. package/src/math/exchangeStatus.ts +31 -0
  170. package/src/math/funding.ts +41 -31
  171. package/src/math/insurance.ts +35 -0
  172. package/src/math/margin.ts +133 -0
  173. package/src/math/market.ts +143 -14
  174. package/src/math/oracles.ts +63 -9
  175. package/src/math/orders.ts +168 -26
  176. package/src/math/position.ts +133 -59
  177. package/src/math/repeg.ts +19 -9
  178. package/src/math/spotBalance.ts +319 -0
  179. package/src/math/spotMarket.ts +9 -0
  180. package/src/math/spotPosition.ts +47 -0
  181. package/src/math/trade.ts +33 -37
  182. package/src/oracles/pythClient.ts +2 -2
  183. package/src/oracles/quoteAssetOracleClient.ts +2 -2
  184. package/src/oracles/switchboardClient.ts +2 -2
  185. package/src/orderParams.ts +16 -8
  186. package/src/serum/serumFulfillmentConfigMap.ts +26 -0
  187. package/src/serum/serumSubscriber.ts +99 -0
  188. package/src/serum/types.ts +13 -0
  189. package/src/tokenFaucet.ts +38 -15
  190. package/src/tx/retryTxSender.ts +16 -5
  191. package/src/tx/types.ts +2 -1
  192. package/src/types.ts +594 -195
  193. package/src/user.ts +1599 -0
  194. package/src/{clearingHouseUserConfig.ts → userConfig.ts} +5 -5
  195. package/src/userMap/userMap.ts +124 -0
  196. package/src/userMap/userStatsMap.ts +108 -0
  197. package/src/userName.ts +2 -1
  198. package/src/userStats.ts +75 -0
  199. package/src/userStatsConfig.ts +18 -0
  200. package/src/util/getTokenAddress.ts +18 -0
  201. package/tests/bn/test.ts +46 -11
  202. package/tests/dlob/helpers.ts +619 -0
  203. package/tests/dlob/test.ts +4586 -0
  204. package/lib/accounts/webSocketClearingHouseAccountSubscriber.d.ts +0 -49
  205. package/lib/admin.d.ts +0 -44
  206. package/lib/admin.js +0 -438
  207. package/lib/clearingHouse.d.ts +0 -146
  208. package/lib/clearingHouse.js +0 -1154
  209. package/lib/clearingHouseUser.d.ts +0 -187
  210. package/lib/clearingHouseUser.js +0 -634
  211. package/lib/clearingHouseUserConfig.d.ts +0 -14
  212. package/lib/constants/banks.d.ts +0 -16
  213. package/lib/constants/banks.js +0 -41
  214. package/lib/constants/markets.d.ts +0 -19
  215. package/lib/idl/clearing_house.json +0 -4464
  216. package/lib/math/bankBalance.d.ts +0 -9
  217. package/lib/math/bankBalance.js +0 -75
  218. package/lib/math/state.d.ts +0 -8
  219. package/lib/math/state.js +0 -15
  220. package/lib/orders.d.ts +0 -8
  221. package/lib/orders.js +0 -134
  222. package/src/accounts/bulkAccountLoader.js +0 -197
  223. package/src/accounts/bulkUserSubscription.js +0 -33
  224. package/src/accounts/pollingClearingHouseAccountSubscriber.js +0 -311
  225. package/src/accounts/pollingOracleSubscriber.js +0 -93
  226. package/src/accounts/pollingTokenAccountSubscriber.js +0 -90
  227. package/src/accounts/pollingUserAccountSubscriber.js +0 -132
  228. package/src/accounts/types.js +0 -10
  229. package/src/accounts/utils.js +0 -7
  230. package/src/accounts/webSocketAccountSubscriber.js +0 -93
  231. package/src/accounts/webSocketClearingHouseAccountSubscriber.js +0 -233
  232. package/src/accounts/webSocketUserAccountSubscriber.js +0 -62
  233. package/src/addresses/marketAddresses.js +0 -26
  234. package/src/admin.js +0 -517
  235. package/src/admin.ts +0 -730
  236. package/src/clearingHouse.ts +0 -1828
  237. package/src/clearingHouseUser.ts +0 -978
  238. package/src/clearingHouseUserConfig.js +0 -2
  239. package/src/config.js +0 -67
  240. package/src/constants/banks.js +0 -42
  241. package/src/constants/banks.ts +0 -50
  242. package/src/constants/markets.js +0 -42
  243. package/src/constants/numericConstants.js +0 -41
  244. package/src/events/eventSubscriber.js +0 -139
  245. package/src/events/fetchLogs.js +0 -50
  246. package/src/events/pollingLogProvider.js +0 -64
  247. package/src/events/sort.js +0 -44
  248. package/src/events/txEventCache.js +0 -71
  249. package/src/events/types.js +0 -20
  250. package/src/events/webSocketLogProvider.js +0 -41
  251. package/src/factory/bigNum.js +0 -390
  252. package/src/factory/oracleClient.js +0 -20
  253. package/src/idl/clearing_house.json +0 -4464
  254. package/src/index.js +0 -69
  255. package/src/math/amm.js +0 -369
  256. package/src/math/auction.js +0 -42
  257. package/src/math/bankBalance.ts +0 -112
  258. package/src/math/conversion.js +0 -11
  259. package/src/math/funding.js +0 -248
  260. package/src/math/oracles.js +0 -26
  261. package/src/math/repeg.js +0 -128
  262. package/src/math/state.js +0 -15
  263. package/src/math/state.ts +0 -14
  264. package/src/math/trade.js +0 -253
  265. package/src/math/utils.js +0 -26
  266. package/src/math/utils.js.map +0 -1
  267. package/src/mockUSDCFaucet.js +0 -280
  268. package/src/oracles/oracleClientCache.js +0 -19
  269. package/src/oracles/pythClient.js +0 -46
  270. package/src/oracles/quoteAssetOracleClient.js +0 -32
  271. package/src/oracles/switchboardClient.js +0 -69
  272. package/src/orderParams.js +0 -20
  273. package/src/orders.ts +0 -245
  274. package/src/slot/SlotSubscriber.js +0 -39
  275. package/src/tokenFaucet.js +0 -189
  276. package/src/types.js +0 -125
  277. package/src/userName.js +0 -20
  278. package/src/wallet.js +0 -35
@@ -0,0 +1,3594 @@
1
+ import { AnchorProvider, BN, Idl, Program } from '@project-serum/anchor';
2
+ import bs58 from 'bs58';
3
+ import {
4
+ ASSOCIATED_TOKEN_PROGRAM_ID,
5
+ Token,
6
+ TOKEN_PROGRAM_ID,
7
+ } from '@solana/spl-token';
8
+ import {
9
+ StateAccount,
10
+ IWallet,
11
+ PositionDirection,
12
+ UserAccount,
13
+ PerpMarketAccount,
14
+ OrderParams,
15
+ Order,
16
+ SpotMarketAccount,
17
+ SpotPosition,
18
+ MakerInfo,
19
+ TakerInfo,
20
+ OptionalOrderParams,
21
+ DefaultOrderParams,
22
+ OrderType,
23
+ ReferrerInfo,
24
+ MarketType,
25
+ SerumV3FulfillmentConfigAccount,
26
+ isVariant,
27
+ } from './types';
28
+ import * as anchor from '@project-serum/anchor';
29
+ import driftIDL from './idl/drift.json';
30
+
31
+ import {
32
+ Connection,
33
+ PublicKey,
34
+ TransactionSignature,
35
+ ConfirmOptions,
36
+ Transaction,
37
+ TransactionInstruction,
38
+ AccountMeta,
39
+ Keypair,
40
+ LAMPORTS_PER_SOL,
41
+ Signer,
42
+ SystemProgram,
43
+ ComputeBudgetProgram,
44
+ } from '@solana/web3.js';
45
+
46
+ import { TokenFaucet } from './tokenFaucet';
47
+ import { EventEmitter } from 'events';
48
+ import StrictEventEmitter from 'strict-event-emitter-types';
49
+ import {
50
+ getDriftSignerPublicKey,
51
+ getDriftStateAccountPublicKey,
52
+ getInsuranceFundStakeAccountPublicKey,
53
+ getPerpMarketPublicKey,
54
+ getSerumFulfillmentConfigPublicKey,
55
+ getSerumSignerPublicKey,
56
+ getSpotMarketPublicKey,
57
+ getUserAccountPublicKey,
58
+ getUserAccountPublicKeySync,
59
+ getUserStatsAccountPublicKey,
60
+ } from './addresses/pda';
61
+ import {
62
+ DriftClientAccountSubscriber,
63
+ DriftClientAccountEvents,
64
+ DataAndSlot,
65
+ } from './accounts/types';
66
+ import { TxSender } from './tx/types';
67
+ import { wrapInTx } from './tx/utils';
68
+ import { QUOTE_SPOT_MARKET_INDEX, ZERO } from './constants/numericConstants';
69
+ import { findDirectionToClose, positionIsAvailable } from './math/position';
70
+ import { getTokenAmount } from './math/spotBalance';
71
+ import { DEFAULT_USER_NAME, encodeName } from './userName';
72
+ import { OraclePriceData } from './oracles/types';
73
+ import { DriftClientConfig } from './driftClientConfig';
74
+ import { PollingDriftClientAccountSubscriber } from './accounts/pollingDriftClientAccountSubscriber';
75
+ import { WebSocketDriftClientAccountSubscriber } from './accounts/webSocketDriftClientAccountSubscriber';
76
+ import { RetryTxSender } from './tx/retryTxSender';
77
+ import { User } from './user';
78
+ import { UserSubscriptionConfig } from './userConfig';
79
+ import { getMarketsAndOraclesForSubscription } from './config';
80
+ import { WRAPPED_SOL_MINT } from './constants/spotMarkets';
81
+ import { UserStats } from './userStats';
82
+ import { isSpotPositionAvailable } from './math/spotPosition';
83
+
84
+ type RemainingAccountParams = {
85
+ userAccounts: UserAccount[];
86
+ writablePerpMarketIndexes?: number[];
87
+ writableSpotMarketIndexes?: number[];
88
+ readablePerpMarketIndex?: number;
89
+ readableSpotMarketIndexes?: number[];
90
+ useMarketLastSlotCache?: boolean;
91
+ };
92
+
93
+ /**
94
+ * # DriftClient
95
+ * This class is the main way to interact with Drift Protocol. It allows you to subscribe to the various accounts where the Market's state is stored, as well as: opening positions, liquidating, settling funding, depositing & withdrawing, and more.
96
+ */
97
+ export class DriftClient {
98
+ connection: Connection;
99
+ wallet: IWallet;
100
+ public program: Program;
101
+ provider: AnchorProvider;
102
+ opts?: ConfirmOptions;
103
+ users = new Map<number, User>();
104
+ userStats?: UserStats;
105
+ activeSubAccountId: number;
106
+ userAccountSubscriptionConfig: UserSubscriptionConfig;
107
+ accountSubscriber: DriftClientAccountSubscriber;
108
+ eventEmitter: StrictEventEmitter<EventEmitter, DriftClientAccountEvents>;
109
+ _isSubscribed = false;
110
+ txSender: TxSender;
111
+ perpMarketLastSlotCache = new Map<number, number>();
112
+ spotMarketLastSlotCache = new Map<number, number>();
113
+ authority: PublicKey;
114
+
115
+ public get isSubscribed() {
116
+ return this._isSubscribed && this.accountSubscriber.isSubscribed;
117
+ }
118
+
119
+ public set isSubscribed(val: boolean) {
120
+ this._isSubscribed = val;
121
+ }
122
+
123
+ public constructor(config: DriftClientConfig) {
124
+ this.connection = config.connection;
125
+ this.wallet = config.wallet;
126
+ this.opts = config.opts || AnchorProvider.defaultOptions();
127
+ this.provider = new AnchorProvider(
128
+ config.connection,
129
+ config.wallet,
130
+ this.opts
131
+ );
132
+ this.program = new Program(
133
+ driftIDL as Idl,
134
+ config.programID,
135
+ this.provider
136
+ );
137
+
138
+ this.authority = config.authority ?? this.wallet.publicKey;
139
+ const subAccountIds = config.subAccountIds ?? [0];
140
+ this.activeSubAccountId = config.activeSubAccountId ?? subAccountIds[0];
141
+ this.userAccountSubscriptionConfig =
142
+ config.accountSubscription?.type === 'polling'
143
+ ? {
144
+ type: 'polling',
145
+ accountLoader: config.accountSubscription.accountLoader,
146
+ }
147
+ : {
148
+ type: 'websocket',
149
+ };
150
+ this.createUsers(subAccountIds, this.userAccountSubscriptionConfig);
151
+ if (config.userStats) {
152
+ this.userStats = new UserStats({
153
+ driftClient: this,
154
+ userStatsAccountPublicKey: getUserStatsAccountPublicKey(
155
+ this.program.programId,
156
+ this.authority
157
+ ),
158
+ accountSubscription: this.userAccountSubscriptionConfig,
159
+ });
160
+ }
161
+
162
+ let perpMarketIndexes = config.perpMarketIndexes;
163
+ let spotMarketIndexes = config.spotMarketIndexes;
164
+ let oracleInfos = config.oracleInfos;
165
+ if (config.env) {
166
+ const {
167
+ perpMarketIndexes: envPerpMarketIndexes,
168
+ spotMarketIndexes: envSpotMarketIndexes,
169
+ oracleInfos: envOralceInfos,
170
+ } = getMarketsAndOraclesForSubscription(config.env);
171
+ perpMarketIndexes = perpMarketIndexes
172
+ ? perpMarketIndexes
173
+ : envPerpMarketIndexes;
174
+ spotMarketIndexes = spotMarketIndexes
175
+ ? spotMarketIndexes
176
+ : envSpotMarketIndexes;
177
+ oracleInfos = oracleInfos ? oracleInfos : envOralceInfos;
178
+ }
179
+
180
+ if (config.accountSubscription?.type === 'polling') {
181
+ this.accountSubscriber = new PollingDriftClientAccountSubscriber(
182
+ this.program,
183
+ config.accountSubscription.accountLoader,
184
+ perpMarketIndexes ?? [],
185
+ spotMarketIndexes ?? [],
186
+ oracleInfos ?? []
187
+ );
188
+ } else {
189
+ this.accountSubscriber = new WebSocketDriftClientAccountSubscriber(
190
+ this.program,
191
+ config.perpMarketIndexes ?? [],
192
+ config.spotMarketIndexes ?? [],
193
+ config.oracleInfos ?? []
194
+ );
195
+ }
196
+ this.eventEmitter = this.accountSubscriber.eventEmitter;
197
+ this.txSender = new RetryTxSender(
198
+ this.provider,
199
+ config.txSenderConfig?.timeout,
200
+ config.txSenderConfig?.retrySleep,
201
+ config.txSenderConfig?.additionalConnections
202
+ );
203
+ }
204
+
205
+ createUsers(
206
+ subAccountIds: number[],
207
+ accountSubscriptionConfig: UserSubscriptionConfig
208
+ ): void {
209
+ for (const subAccountId of subAccountIds) {
210
+ const user = this.createUser(subAccountId, accountSubscriptionConfig);
211
+ this.users.set(subAccountId, user);
212
+ }
213
+ }
214
+
215
+ createUser(
216
+ subAccountId: number,
217
+ accountSubscriptionConfig: UserSubscriptionConfig
218
+ ): User {
219
+ const userAccountPublicKey = getUserAccountPublicKeySync(
220
+ this.program.programId,
221
+ this.authority,
222
+ subAccountId
223
+ );
224
+
225
+ return new User({
226
+ driftClient: this,
227
+ userAccountPublicKey,
228
+ accountSubscription: accountSubscriptionConfig,
229
+ });
230
+ }
231
+
232
+ public async subscribe(): Promise<boolean> {
233
+ let subscribePromises = this.subscribeUsers().concat(
234
+ this.accountSubscriber.subscribe()
235
+ );
236
+ if (this.userStats !== undefined) {
237
+ subscribePromises = subscribePromises.concat(this.userStats.subscribe());
238
+ }
239
+ this.isSubscribed = (await Promise.all(subscribePromises)).reduce(
240
+ (success, prevSuccess) => success && prevSuccess
241
+ );
242
+ return this.isSubscribed;
243
+ }
244
+
245
+ subscribeUsers(): Promise<boolean>[] {
246
+ return [...this.users.values()].map((user) => user.subscribe());
247
+ }
248
+
249
+ /**
250
+ * Forces the accountSubscriber to fetch account updates from rpc
251
+ */
252
+ public async fetchAccounts(): Promise<void> {
253
+ let promises = [...this.users.values()]
254
+ .map((user) => user.fetchAccounts())
255
+ .concat(this.accountSubscriber.fetch());
256
+ if (this.userStats) {
257
+ promises = promises.concat(this.userStats.fetchAccounts());
258
+ }
259
+ await Promise.all(promises);
260
+ }
261
+
262
+ public async unsubscribe(): Promise<void> {
263
+ let unsubscribePromises = this.unsubscribeUsers().concat(
264
+ this.accountSubscriber.unsubscribe()
265
+ );
266
+ if (this.userStats !== undefined) {
267
+ unsubscribePromises = unsubscribePromises.concat(
268
+ this.userStats.unsubscribe()
269
+ );
270
+ }
271
+ await Promise.all(unsubscribePromises);
272
+ this.isSubscribed = false;
273
+ }
274
+
275
+ unsubscribeUsers(): Promise<void>[] {
276
+ return [...this.users.values()].map((user) => user.unsubscribe());
277
+ }
278
+
279
+ statePublicKey?: PublicKey;
280
+ public async getStatePublicKey(): Promise<PublicKey> {
281
+ if (this.statePublicKey) {
282
+ return this.statePublicKey;
283
+ }
284
+ this.statePublicKey = await getDriftStateAccountPublicKey(
285
+ this.program.programId
286
+ );
287
+ return this.statePublicKey;
288
+ }
289
+
290
+ signerPublicKey?: PublicKey;
291
+ public getSignerPublicKey(): PublicKey {
292
+ if (this.signerPublicKey) {
293
+ return this.signerPublicKey;
294
+ }
295
+ this.signerPublicKey = getDriftSignerPublicKey(this.program.programId);
296
+ return this.signerPublicKey;
297
+ }
298
+
299
+ public getStateAccount(): StateAccount {
300
+ return this.accountSubscriber.getStateAccountAndSlot().data;
301
+ }
302
+
303
+ public getPerpMarketAccount(
304
+ marketIndex: number
305
+ ): PerpMarketAccount | undefined {
306
+ return this.accountSubscriber.getMarketAccountAndSlot(marketIndex)?.data;
307
+ }
308
+
309
+ public getPerpMarketAccounts(): PerpMarketAccount[] {
310
+ return this.accountSubscriber
311
+ .getMarketAccountsAndSlots()
312
+ .map((value) => value.data);
313
+ }
314
+
315
+ public getSpotMarketAccount(
316
+ marketIndex: number
317
+ ): SpotMarketAccount | undefined {
318
+ return this.accountSubscriber.getSpotMarketAccountAndSlot(marketIndex).data;
319
+ }
320
+
321
+ public getSpotMarketAccounts(): SpotMarketAccount[] {
322
+ return this.accountSubscriber
323
+ .getSpotMarketAccountsAndSlots()
324
+ .map((value) => value.data);
325
+ }
326
+
327
+ public getQuoteSpotMarketAccount(): SpotMarketAccount {
328
+ return this.accountSubscriber.getSpotMarketAccountAndSlot(
329
+ QUOTE_SPOT_MARKET_INDEX
330
+ ).data;
331
+ }
332
+
333
+ public getOraclePriceDataAndSlot(
334
+ oraclePublicKey: PublicKey
335
+ ): DataAndSlot<OraclePriceData> | undefined {
336
+ return this.accountSubscriber.getOraclePriceDataAndSlot(oraclePublicKey);
337
+ }
338
+
339
+ public async getSerumV3FulfillmentConfig(
340
+ serumMarket: PublicKey
341
+ ): Promise<SerumV3FulfillmentConfigAccount> {
342
+ const address = await getSerumFulfillmentConfigPublicKey(
343
+ this.program.programId,
344
+ serumMarket
345
+ );
346
+ return (await this.program.account.serumV3FulfillmentConfig.fetch(
347
+ address
348
+ )) as SerumV3FulfillmentConfigAccount;
349
+ }
350
+
351
+ /**
352
+ * Update the wallet to use for drift transactions and linked user account
353
+ * @param newWallet
354
+ * @param subAccountIds
355
+ * @param activeSubAccountId
356
+ */
357
+ public async updateWallet(
358
+ newWallet: IWallet,
359
+ subAccountIds = [0],
360
+ activeSubAccountId = 0
361
+ ): Promise<void> {
362
+ const newProvider = new AnchorProvider(
363
+ this.connection,
364
+ newWallet,
365
+ this.opts
366
+ );
367
+ const newProgram = new Program(
368
+ driftIDL as Idl,
369
+ this.program.programId,
370
+ newProvider
371
+ );
372
+
373
+ // Update provider for txSender with new wallet details
374
+ this.txSender.provider = newProvider;
375
+
376
+ this.wallet = newWallet;
377
+ this.provider = newProvider;
378
+ this.program = newProgram;
379
+ this.authority = newWallet.publicKey;
380
+
381
+ if (this.isSubscribed) {
382
+ await Promise.all(this.unsubscribeUsers());
383
+
384
+ if (this.userStats) {
385
+ await this.userStats.unsubscribe();
386
+
387
+ this.userStats = new UserStats({
388
+ driftClient: this,
389
+ userStatsAccountPublicKey: getUserStatsAccountPublicKey(
390
+ this.program.programId,
391
+ this.authority
392
+ ),
393
+ accountSubscription: this.userAccountSubscriptionConfig,
394
+ });
395
+ }
396
+ }
397
+ this.users.clear();
398
+ this.createUsers(subAccountIds, this.userAccountSubscriptionConfig);
399
+ if (this.isSubscribed) {
400
+ await Promise.all(this.subscribeUsers());
401
+
402
+ if (this.userStats) {
403
+ await this.userStats.subscribe();
404
+ }
405
+ }
406
+
407
+ this.activeSubAccountId = activeSubAccountId;
408
+ this.userStatsAccountPublicKey = undefined;
409
+ }
410
+
411
+ public async switchActiveUser(subAccountId: number): Promise<void> {
412
+ this.activeSubAccountId = subAccountId;
413
+ }
414
+
415
+ public async addUser(subAccountId: number): Promise<void> {
416
+ if (this.users.has(subAccountId)) {
417
+ return;
418
+ }
419
+
420
+ const user = this.createUser(
421
+ subAccountId,
422
+ this.userAccountSubscriptionConfig
423
+ );
424
+ await user.subscribe();
425
+ this.users.set(subAccountId, user);
426
+ }
427
+
428
+ public async initializeUserAccount(
429
+ subAccountId = 0,
430
+ name = DEFAULT_USER_NAME,
431
+ referrerInfo?: ReferrerInfo
432
+ ): Promise<[TransactionSignature, PublicKey]> {
433
+ const [userAccountPublicKey, initializeUserAccountIx] =
434
+ await this.getInitializeUserInstructions(
435
+ subAccountId,
436
+ name,
437
+ referrerInfo
438
+ );
439
+
440
+ const tx = new Transaction();
441
+ if (subAccountId === 0) {
442
+ if (
443
+ !(await this.checkIfAccountExists(this.getUserStatsAccountPublicKey()))
444
+ ) {
445
+ tx.add(await this.getInitializeUserStatsIx());
446
+ }
447
+ }
448
+ tx.add(initializeUserAccountIx);
449
+ const { txSig } = await this.txSender.send(tx, [], this.opts);
450
+ return [txSig, userAccountPublicKey];
451
+ }
452
+
453
+ async getInitializeUserInstructions(
454
+ subAccountId = 0,
455
+ name = DEFAULT_USER_NAME,
456
+ referrerInfo?: ReferrerInfo
457
+ ): Promise<[PublicKey, TransactionInstruction]> {
458
+ const userAccountPublicKey = await getUserAccountPublicKey(
459
+ this.program.programId,
460
+ this.wallet.publicKey,
461
+ subAccountId
462
+ );
463
+
464
+ const remainingAccounts = new Array<AccountMeta>();
465
+ if (referrerInfo !== undefined) {
466
+ remainingAccounts.push({
467
+ pubkey: referrerInfo.referrer,
468
+ isWritable: true,
469
+ isSigner: false,
470
+ });
471
+ remainingAccounts.push({
472
+ pubkey: referrerInfo.referrerStats,
473
+ isWritable: true,
474
+ isSigner: false,
475
+ });
476
+ }
477
+
478
+ const state = this.getStateAccount();
479
+ if (!state.whitelistMint.equals(PublicKey.default)) {
480
+ const associatedTokenPublicKey = await Token.getAssociatedTokenAddress(
481
+ ASSOCIATED_TOKEN_PROGRAM_ID,
482
+ TOKEN_PROGRAM_ID,
483
+ state.whitelistMint,
484
+ this.wallet.publicKey
485
+ );
486
+ remainingAccounts.push({
487
+ pubkey: associatedTokenPublicKey,
488
+ isWritable: false,
489
+ isSigner: false,
490
+ });
491
+ }
492
+
493
+ const nameBuffer = encodeName(name);
494
+ const initializeUserAccountIx =
495
+ await this.program.instruction.initializeUser(subAccountId, nameBuffer, {
496
+ accounts: {
497
+ user: userAccountPublicKey,
498
+ userStats: this.getUserStatsAccountPublicKey(),
499
+ authority: this.wallet.publicKey,
500
+ payer: this.wallet.publicKey,
501
+ rent: anchor.web3.SYSVAR_RENT_PUBKEY,
502
+ systemProgram: anchor.web3.SystemProgram.programId,
503
+ state: await this.getStatePublicKey(),
504
+ },
505
+ remainingAccounts,
506
+ });
507
+
508
+ return [userAccountPublicKey, initializeUserAccountIx];
509
+ }
510
+
511
+ async getInitializeUserStatsIx(): Promise<TransactionInstruction> {
512
+ return await this.program.instruction.initializeUserStats({
513
+ accounts: {
514
+ userStats: this.getUserStatsAccountPublicKey(),
515
+ authority: this.wallet.publicKey,
516
+ payer: this.wallet.publicKey,
517
+ rent: anchor.web3.SYSVAR_RENT_PUBKEY,
518
+ systemProgram: anchor.web3.SystemProgram.programId,
519
+ state: await this.getStatePublicKey(),
520
+ },
521
+ });
522
+ }
523
+
524
+ public async updateUserName(
525
+ name: string,
526
+ subAccountId = 0
527
+ ): Promise<TransactionSignature> {
528
+ const userAccountPublicKey = getUserAccountPublicKeySync(
529
+ this.program.programId,
530
+ this.wallet.publicKey,
531
+ subAccountId
532
+ );
533
+
534
+ const nameBuffer = encodeName(name);
535
+ return await this.program.rpc.updateUserName(subAccountId, nameBuffer, {
536
+ accounts: {
537
+ user: userAccountPublicKey,
538
+ authority: this.wallet.publicKey,
539
+ },
540
+ });
541
+ }
542
+
543
+ public async updateUserCustomMarginRatio(
544
+ marginRatio: number,
545
+ subAccountId = 0
546
+ ): Promise<TransactionSignature> {
547
+ return await this.program.rpc.updateUserCustomMarginRatio(
548
+ subAccountId,
549
+ marginRatio,
550
+ {
551
+ accounts: {
552
+ user: await this.getUserAccountPublicKey(),
553
+ authority: this.wallet.publicKey,
554
+ },
555
+ }
556
+ );
557
+ }
558
+
559
+ public async updateUserDelegate(
560
+ delegate: PublicKey,
561
+ subAccountId = 0
562
+ ): Promise<TransactionSignature> {
563
+ return await this.program.rpc.updateUserDelegate(subAccountId, delegate, {
564
+ accounts: {
565
+ user: await this.getUserAccountPublicKey(),
566
+ authority: this.wallet.publicKey,
567
+ },
568
+ });
569
+ }
570
+
571
+ public async getUserAccountsForDelegate(
572
+ delegate: PublicKey
573
+ ): Promise<UserAccount[]> {
574
+ const programAccounts = await this.program.account.user.all([
575
+ {
576
+ memcmp: {
577
+ offset: 40,
578
+ /** data to match, as base-58 encoded string and limited to less than 129 bytes */
579
+ bytes: bs58.encode(delegate.toBuffer()),
580
+ },
581
+ },
582
+ ]);
583
+
584
+ return programAccounts.map(
585
+ (programAccount) => programAccount.account as UserAccount
586
+ );
587
+ }
588
+
589
+ public async getUserAccountsForAuthority(
590
+ authority: PublicKey
591
+ ): Promise<UserAccount[]> {
592
+ const programAccounts = await this.program.account.user.all([
593
+ {
594
+ memcmp: {
595
+ offset: 8,
596
+ /** data to match, as base-58 encoded string and limited to less than 129 bytes */
597
+ bytes: bs58.encode(authority.toBuffer()),
598
+ },
599
+ },
600
+ ]);
601
+
602
+ return programAccounts.map(
603
+ (programAccount) => programAccount.account as UserAccount
604
+ );
605
+ }
606
+
607
+ public async deleteUser(subAccountId = 0): Promise<TransactionSignature> {
608
+ const userAccountPublicKey = getUserAccountPublicKeySync(
609
+ this.program.programId,
610
+ this.wallet.publicKey,
611
+ subAccountId
612
+ );
613
+
614
+ const txSig = await this.program.rpc.deleteUser({
615
+ accounts: {
616
+ user: userAccountPublicKey,
617
+ userStats: this.getUserStatsAccountPublicKey(),
618
+ authority: this.wallet.publicKey,
619
+ state: await this.getStatePublicKey(),
620
+ },
621
+ });
622
+
623
+ await this.users.get(subAccountId)?.unsubscribe();
624
+ this.users.delete(subAccountId);
625
+
626
+ return txSig;
627
+ }
628
+
629
+ public getUser(subAccountId?: number): User {
630
+ subAccountId = subAccountId ?? this.activeSubAccountId;
631
+ if (!this.users.has(subAccountId)) {
632
+ throw new Error(`Clearing House has no user for user id ${subAccountId}`);
633
+ }
634
+ return this.users.get(subAccountId);
635
+ }
636
+
637
+ public getUsers(): User[] {
638
+ return [...this.users.values()];
639
+ }
640
+
641
+ public getUserStats(): UserStats {
642
+ return this.userStats;
643
+ }
644
+
645
+ userStatsAccountPublicKey: PublicKey;
646
+ public getUserStatsAccountPublicKey(): PublicKey {
647
+ if (this.userStatsAccountPublicKey) {
648
+ return this.userStatsAccountPublicKey;
649
+ }
650
+
651
+ this.userStatsAccountPublicKey = getUserStatsAccountPublicKey(
652
+ this.program.programId,
653
+ this.authority
654
+ );
655
+ return this.userStatsAccountPublicKey;
656
+ }
657
+
658
+ public async getUserAccountPublicKey(): Promise<PublicKey> {
659
+ return this.getUser().userAccountPublicKey;
660
+ }
661
+
662
+ public getUserAccount(subAccountId?: number): UserAccount | undefined {
663
+ return this.getUser(subAccountId).getUserAccount();
664
+ }
665
+
666
+ public getUserAccountAndSlot(
667
+ subAccountId?: number
668
+ ): DataAndSlot<UserAccount> | undefined {
669
+ return this.getUser(subAccountId).getUserAccountAndSlot();
670
+ }
671
+
672
+ public getSpotPosition(marketIndex: number): SpotPosition | undefined {
673
+ return this.getUserAccount().spotPositions.find(
674
+ (spotPosition) => spotPosition.marketIndex === marketIndex
675
+ );
676
+ }
677
+
678
+ public getQuoteAssetTokenAmount(): BN {
679
+ const spotMarket = this.getSpotMarketAccount(QUOTE_SPOT_MARKET_INDEX);
680
+ const spotPosition = this.getSpotPosition(QUOTE_SPOT_MARKET_INDEX);
681
+ return getTokenAmount(
682
+ spotPosition.scaledBalance,
683
+ spotMarket,
684
+ spotPosition.balanceType
685
+ );
686
+ }
687
+
688
+ public getTokenAmount(marketIndex: number): BN {
689
+ const spotPosition = this.getSpotPosition(marketIndex);
690
+ if (spotPosition === undefined) {
691
+ return ZERO;
692
+ }
693
+ const spotMarket = this.getSpotMarketAccount(marketIndex);
694
+ return getTokenAmount(
695
+ spotPosition.scaledBalance,
696
+ spotMarket,
697
+ spotPosition.balanceType
698
+ );
699
+ }
700
+
701
+ getRemainingAccounts(params: RemainingAccountParams): AccountMeta[] {
702
+ const { oracleAccountMap, spotMarketAccountMap, perpMarketAccountMap } =
703
+ this.getRemainingAccountMapsForUsers(params.userAccounts);
704
+
705
+ if (params.useMarketLastSlotCache) {
706
+ const lastUserSlot = this.getUserAccountAndSlot()?.slot;
707
+ for (const [
708
+ marketIndex,
709
+ slot,
710
+ ] of this.perpMarketLastSlotCache.entries()) {
711
+ // if cache has more recent slot than user positions account slot, add market to remaining accounts
712
+ // otherwise remove from slot
713
+ if (slot > lastUserSlot) {
714
+ const marketAccount = this.getPerpMarketAccount(marketIndex);
715
+ perpMarketAccountMap.set(marketIndex, {
716
+ pubkey: marketAccount.pubkey,
717
+ isSigner: false,
718
+ isWritable: false,
719
+ });
720
+ oracleAccountMap.set(marketAccount.amm.oracle.toString(), {
721
+ pubkey: marketAccount.amm.oracle,
722
+ isSigner: false,
723
+ isWritable: false,
724
+ });
725
+ } else {
726
+ this.perpMarketLastSlotCache.delete(marketIndex);
727
+ }
728
+ }
729
+
730
+ for (const [
731
+ marketIndex,
732
+ slot,
733
+ ] of this.spotMarketLastSlotCache.entries()) {
734
+ // if cache has more recent slot than user positions account slot, add market to remaining accounts
735
+ // otherwise remove from slot
736
+ if (slot > lastUserSlot) {
737
+ const marketAccount = this.getSpotMarketAccount(marketIndex);
738
+ spotMarketAccountMap.set(marketIndex, {
739
+ pubkey: marketAccount.pubkey,
740
+ isSigner: false,
741
+ isWritable: false,
742
+ });
743
+ if (!marketAccount.oracle.equals(PublicKey.default)) {
744
+ oracleAccountMap.set(marketAccount.oracle.toString(), {
745
+ pubkey: marketAccount.oracle,
746
+ isSigner: false,
747
+ isWritable: false,
748
+ });
749
+ }
750
+ } else {
751
+ this.spotMarketLastSlotCache.delete(marketIndex);
752
+ }
753
+ }
754
+ }
755
+
756
+ if (params.readablePerpMarketIndex !== undefined) {
757
+ const marketAccount = this.getPerpMarketAccount(
758
+ params.readablePerpMarketIndex
759
+ );
760
+ perpMarketAccountMap.set(params.readablePerpMarketIndex, {
761
+ pubkey: marketAccount.pubkey,
762
+ isSigner: false,
763
+ isWritable: false,
764
+ });
765
+ oracleAccountMap.set(marketAccount.amm.oracle.toString(), {
766
+ pubkey: marketAccount.amm.oracle,
767
+ isSigner: false,
768
+ isWritable: false,
769
+ });
770
+ }
771
+
772
+ if (params.writablePerpMarketIndexes !== undefined) {
773
+ for (const writablePerpMarketIndex of params.writablePerpMarketIndexes) {
774
+ const marketAccount = this.getPerpMarketAccount(
775
+ writablePerpMarketIndex
776
+ );
777
+ perpMarketAccountMap.set(writablePerpMarketIndex, {
778
+ pubkey: marketAccount.pubkey,
779
+ isSigner: false,
780
+ isWritable: true,
781
+ });
782
+ oracleAccountMap.set(marketAccount.amm.oracle.toString(), {
783
+ pubkey: marketAccount.amm.oracle,
784
+ isSigner: false,
785
+ isWritable: false,
786
+ });
787
+ }
788
+ }
789
+
790
+ if (params.readableSpotMarketIndexes !== undefined) {
791
+ for (const readableSpotMarketIndex of params.readableSpotMarketIndexes) {
792
+ const spotMarketAccount = this.getSpotMarketAccount(
793
+ readableSpotMarketIndex
794
+ );
795
+ spotMarketAccountMap.set(readableSpotMarketIndex, {
796
+ pubkey: spotMarketAccount.pubkey,
797
+ isSigner: false,
798
+ isWritable: false,
799
+ });
800
+ if (spotMarketAccount.marketIndex !== 0) {
801
+ oracleAccountMap.set(spotMarketAccount.oracle.toString(), {
802
+ pubkey: spotMarketAccount.oracle,
803
+ isSigner: false,
804
+ isWritable: false,
805
+ });
806
+ }
807
+ }
808
+ }
809
+
810
+ if (params.writableSpotMarketIndexes !== undefined) {
811
+ for (const writableSpotMarketIndex of params.writableSpotMarketIndexes) {
812
+ const spotMarketAccount = this.getSpotMarketAccount(
813
+ writableSpotMarketIndex
814
+ );
815
+ spotMarketAccountMap.set(spotMarketAccount.marketIndex, {
816
+ pubkey: spotMarketAccount.pubkey,
817
+ isSigner: false,
818
+ isWritable: true,
819
+ });
820
+ if (!spotMarketAccount.oracle.equals(PublicKey.default)) {
821
+ oracleAccountMap.set(spotMarketAccount.oracle.toString(), {
822
+ pubkey: spotMarketAccount.oracle,
823
+ isSigner: false,
824
+ isWritable: false,
825
+ });
826
+ }
827
+ }
828
+ }
829
+
830
+ return [
831
+ ...oracleAccountMap.values(),
832
+ ...spotMarketAccountMap.values(),
833
+ ...perpMarketAccountMap.values(),
834
+ ];
835
+ }
836
+
837
+ getRemainingAccountMapsForUsers(userAccounts: UserAccount[]): {
838
+ oracleAccountMap: Map<string, AccountMeta>;
839
+ spotMarketAccountMap: Map<number, AccountMeta>;
840
+ perpMarketAccountMap: Map<number, AccountMeta>;
841
+ } {
842
+ const oracleAccountMap = new Map<string, AccountMeta>();
843
+ const spotMarketAccountMap = new Map<number, AccountMeta>();
844
+ const perpMarketAccountMap = new Map<number, AccountMeta>();
845
+
846
+ for (const userAccount of userAccounts) {
847
+ for (const spotPosition of userAccount.spotPositions) {
848
+ if (!isSpotPositionAvailable(spotPosition)) {
849
+ const spotMarket = this.getSpotMarketAccount(
850
+ spotPosition.marketIndex
851
+ );
852
+ spotMarketAccountMap.set(spotPosition.marketIndex, {
853
+ pubkey: spotMarket.pubkey,
854
+ isSigner: false,
855
+ isWritable: false,
856
+ });
857
+
858
+ if (!spotMarket.oracle.equals(PublicKey.default)) {
859
+ oracleAccountMap.set(spotMarket.oracle.toString(), {
860
+ pubkey: spotMarket.oracle,
861
+ isSigner: false,
862
+ isWritable: false,
863
+ });
864
+ }
865
+
866
+ if (
867
+ !spotPosition.openAsks.eq(ZERO) ||
868
+ !spotPosition.openBids.eq(ZERO)
869
+ ) {
870
+ spotMarketAccountMap.set(spotPosition.marketIndex, {
871
+ pubkey: this.getQuoteSpotMarketAccount().pubkey,
872
+ isSigner: false,
873
+ isWritable: false,
874
+ });
875
+ }
876
+ }
877
+ }
878
+ for (const position of userAccount.perpPositions) {
879
+ if (!positionIsAvailable(position)) {
880
+ const market = this.getPerpMarketAccount(position.marketIndex);
881
+ perpMarketAccountMap.set(position.marketIndex, {
882
+ pubkey: market.pubkey,
883
+ isWritable: false,
884
+ isSigner: false,
885
+ });
886
+ oracleAccountMap.set(market.amm.oracle.toString(), {
887
+ pubkey: market.amm.oracle,
888
+ isWritable: false,
889
+ isSigner: false,
890
+ });
891
+ }
892
+ }
893
+ }
894
+
895
+ return {
896
+ oracleAccountMap,
897
+ spotMarketAccountMap,
898
+ perpMarketAccountMap,
899
+ };
900
+ }
901
+
902
+ public getOrder(orderId: number): Order | undefined {
903
+ return this.getUserAccount()?.orders.find(
904
+ (order) => order.orderId === orderId
905
+ );
906
+ }
907
+
908
+ public getOrderByUserId(userOrderId: number): Order | undefined {
909
+ return this.getUserAccount()?.orders.find(
910
+ (order) => order.userOrderId === userOrderId
911
+ );
912
+ }
913
+
914
+ public async deposit(
915
+ amount: BN,
916
+ marketIndex: number,
917
+ collateralAccountPublicKey: PublicKey,
918
+ subAccountId?: number,
919
+ reduceOnly = false
920
+ ): Promise<TransactionSignature> {
921
+ const tx = new Transaction();
922
+ tx.add(
923
+ ComputeBudgetProgram.requestUnits({
924
+ units: 600_000,
925
+ additionalFee: 0,
926
+ })
927
+ );
928
+
929
+ const additionalSigners: Array<Signer> = [];
930
+
931
+ const spotMarketAccount = this.getSpotMarketAccount(marketIndex);
932
+
933
+ const isSolMarket = spotMarketAccount.mint.equals(WRAPPED_SOL_MINT);
934
+
935
+ const authority = this.authority;
936
+
937
+ const createWSOLTokenAccount =
938
+ isSolMarket && collateralAccountPublicKey.equals(authority);
939
+
940
+ if (createWSOLTokenAccount) {
941
+ const { ixs, signers, pubkey } =
942
+ await this.getWrappedSolAccountCreationIxs(amount, true);
943
+
944
+ collateralAccountPublicKey = pubkey;
945
+
946
+ ixs.forEach((ix) => {
947
+ tx.add(ix);
948
+ });
949
+
950
+ signers.forEach((signer) => additionalSigners.push(signer));
951
+ }
952
+
953
+ const depositCollateralIx = await this.getDepositInstruction(
954
+ amount,
955
+ marketIndex,
956
+ collateralAccountPublicKey,
957
+ subAccountId,
958
+ reduceOnly,
959
+ true
960
+ );
961
+
962
+ tx.add(depositCollateralIx);
963
+
964
+ // Close the wrapped sol account at the end of the transaction
965
+ if (createWSOLTokenAccount) {
966
+ tx.add(
967
+ Token.createCloseAccountInstruction(
968
+ TOKEN_PROGRAM_ID,
969
+ collateralAccountPublicKey,
970
+ authority,
971
+ authority,
972
+ []
973
+ )
974
+ );
975
+ }
976
+
977
+ const { txSig, slot } = await this.txSender.send(
978
+ tx,
979
+ additionalSigners,
980
+ this.opts
981
+ );
982
+ this.spotMarketLastSlotCache.set(marketIndex, slot);
983
+ return txSig;
984
+ }
985
+
986
+ async getDepositInstruction(
987
+ amount: BN,
988
+ marketIndex: number,
989
+ userTokenAccount: PublicKey,
990
+ subAccountId?: number,
991
+ reduceOnly = false,
992
+ userInitialized = true
993
+ ): Promise<TransactionInstruction> {
994
+ const userAccountPublicKey = subAccountId
995
+ ? await getUserAccountPublicKey(
996
+ this.program.programId,
997
+ this.authority,
998
+ subAccountId
999
+ )
1000
+ : await this.getUserAccountPublicKey();
1001
+
1002
+ let remainingAccounts = [];
1003
+ if (userInitialized) {
1004
+ remainingAccounts = this.getRemainingAccounts({
1005
+ userAccounts: [this.getUserAccount()],
1006
+ useMarketLastSlotCache: true,
1007
+ writableSpotMarketIndexes: [marketIndex],
1008
+ });
1009
+ } else {
1010
+ remainingAccounts = this.getRemainingAccounts({
1011
+ userAccounts: [],
1012
+ writableSpotMarketIndexes: [marketIndex],
1013
+ });
1014
+ }
1015
+
1016
+ const spotMarketAccount = this.getSpotMarketAccount(marketIndex);
1017
+
1018
+ return await this.program.instruction.deposit(
1019
+ marketIndex,
1020
+ amount,
1021
+ reduceOnly,
1022
+ {
1023
+ accounts: {
1024
+ state: await this.getStatePublicKey(),
1025
+ spotMarket: spotMarketAccount.pubkey,
1026
+ spotMarketVault: spotMarketAccount.vault,
1027
+ user: userAccountPublicKey,
1028
+ userStats: this.getUserStatsAccountPublicKey(),
1029
+ userTokenAccount: userTokenAccount,
1030
+ authority: this.wallet.publicKey,
1031
+ tokenProgram: TOKEN_PROGRAM_ID,
1032
+ },
1033
+ remainingAccounts,
1034
+ }
1035
+ );
1036
+ }
1037
+
1038
+ private async checkIfAccountExists(account: PublicKey): Promise<boolean> {
1039
+ try {
1040
+ const accountInfo = await this.connection.getAccountInfo(account);
1041
+ return accountInfo != null;
1042
+ } catch (e) {
1043
+ // Doesn't already exist
1044
+ return false;
1045
+ }
1046
+ }
1047
+
1048
+ private async getWrappedSolAccountCreationIxs(
1049
+ amount: BN,
1050
+ isDeposit?: boolean
1051
+ ): Promise<{
1052
+ ixs: anchor.web3.TransactionInstruction[];
1053
+ signers: Signer[];
1054
+ pubkey: PublicKey;
1055
+ }> {
1056
+ const wrappedSolAccount = new Keypair();
1057
+
1058
+ const result = {
1059
+ ixs: [],
1060
+ signers: [],
1061
+ pubkey: wrappedSolAccount.publicKey,
1062
+ };
1063
+
1064
+ const rentSpaceLamports = new BN(LAMPORTS_PER_SOL / 100);
1065
+
1066
+ const lamports = isDeposit
1067
+ ? amount.add(rentSpaceLamports)
1068
+ : rentSpaceLamports;
1069
+
1070
+ const authority = this.wallet.publicKey;
1071
+
1072
+ result.ixs.push(
1073
+ SystemProgram.createAccount({
1074
+ fromPubkey: authority,
1075
+ newAccountPubkey: wrappedSolAccount.publicKey,
1076
+ lamports: lamports.toNumber(),
1077
+ space: 165,
1078
+ programId: TOKEN_PROGRAM_ID,
1079
+ })
1080
+ );
1081
+
1082
+ result.ixs.push(
1083
+ Token.createInitAccountInstruction(
1084
+ TOKEN_PROGRAM_ID,
1085
+ WRAPPED_SOL_MINT,
1086
+ wrappedSolAccount.publicKey,
1087
+ authority
1088
+ )
1089
+ );
1090
+
1091
+ result.signers.push(wrappedSolAccount);
1092
+
1093
+ return result;
1094
+ }
1095
+
1096
+ public getAssociatedTokenAccountCreationIx(
1097
+ tokenMintAddress: PublicKey,
1098
+ associatedTokenAddress: PublicKey
1099
+ ): anchor.web3.TransactionInstruction {
1100
+ const createAssociatedAccountIx =
1101
+ Token.createAssociatedTokenAccountInstruction(
1102
+ ASSOCIATED_TOKEN_PROGRAM_ID,
1103
+ TOKEN_PROGRAM_ID,
1104
+ tokenMintAddress,
1105
+ associatedTokenAddress,
1106
+ this.wallet.publicKey,
1107
+ this.wallet.publicKey
1108
+ );
1109
+
1110
+ return createAssociatedAccountIx;
1111
+ }
1112
+
1113
+ /**
1114
+ * Creates the Clearing House User account for a user, and deposits some initial collateral
1115
+ * @param amount
1116
+ * @param userTokenAccount
1117
+ * @param marketIndex
1118
+ * @param subAccountId
1119
+ * @param name
1120
+ * @param fromSubAccountId
1121
+ * @returns
1122
+ */
1123
+ public async initializeUserAccountAndDepositCollateral(
1124
+ amount: BN,
1125
+ userTokenAccount: PublicKey,
1126
+ marketIndex = 0,
1127
+ subAccountId = 0,
1128
+ name = DEFAULT_USER_NAME,
1129
+ fromSubAccountId?: number,
1130
+ referrerInfo?: ReferrerInfo
1131
+ ): Promise<[TransactionSignature, PublicKey]> {
1132
+ const [userAccountPublicKey, initializeUserAccountIx] =
1133
+ await this.getInitializeUserInstructions(
1134
+ subAccountId,
1135
+ name,
1136
+ referrerInfo
1137
+ );
1138
+
1139
+ const additionalSigners: Array<Signer> = [];
1140
+
1141
+ const spotMarket = this.getSpotMarketAccount(marketIndex);
1142
+
1143
+ const isSolMarket = spotMarket.mint.equals(WRAPPED_SOL_MINT);
1144
+
1145
+ const tx = new Transaction();
1146
+
1147
+ const authority = this.wallet.publicKey;
1148
+
1149
+ const createWSOLTokenAccount =
1150
+ isSolMarket && userTokenAccount.equals(authority);
1151
+
1152
+ if (createWSOLTokenAccount) {
1153
+ const {
1154
+ ixs: startIxs,
1155
+ signers,
1156
+ pubkey,
1157
+ } = await this.getWrappedSolAccountCreationIxs(amount, true);
1158
+
1159
+ userTokenAccount = pubkey;
1160
+
1161
+ startIxs.forEach((ix) => {
1162
+ tx.add(ix);
1163
+ });
1164
+
1165
+ signers.forEach((signer) => additionalSigners.push(signer));
1166
+ }
1167
+
1168
+ const depositCollateralIx =
1169
+ fromSubAccountId != null
1170
+ ? await this.getTransferDepositIx(
1171
+ amount,
1172
+ marketIndex,
1173
+ fromSubAccountId,
1174
+ subAccountId
1175
+ )
1176
+ : await this.getDepositInstruction(
1177
+ amount,
1178
+ marketIndex,
1179
+ userTokenAccount,
1180
+ subAccountId,
1181
+ false,
1182
+ false
1183
+ );
1184
+
1185
+ if (subAccountId === 0) {
1186
+ if (
1187
+ !(await this.checkIfAccountExists(this.getUserStatsAccountPublicKey()))
1188
+ ) {
1189
+ tx.add(await this.getInitializeUserStatsIx());
1190
+ }
1191
+ }
1192
+ tx.add(initializeUserAccountIx).add(depositCollateralIx);
1193
+
1194
+ // Close the wrapped sol account at the end of the transaction
1195
+ if (createWSOLTokenAccount) {
1196
+ tx.add(
1197
+ Token.createCloseAccountInstruction(
1198
+ TOKEN_PROGRAM_ID,
1199
+ userTokenAccount,
1200
+ authority,
1201
+ authority,
1202
+ []
1203
+ )
1204
+ );
1205
+ }
1206
+
1207
+ const { txSig, slot } = await this.txSender.send(
1208
+ tx,
1209
+ additionalSigners,
1210
+ this.opts
1211
+ );
1212
+ this.spotMarketLastSlotCache.set(marketIndex, slot);
1213
+ return [txSig, userAccountPublicKey];
1214
+ }
1215
+
1216
+ public async initializeUserAccountForDevnet(
1217
+ subAccountId = 0,
1218
+ name = DEFAULT_USER_NAME,
1219
+ marketIndex: number,
1220
+ tokenFaucet: TokenFaucet,
1221
+ amount: BN,
1222
+ referrerInfo?: ReferrerInfo
1223
+ ): Promise<[TransactionSignature, PublicKey]> {
1224
+ const [associateTokenPublicKey, createAssociatedAccountIx, mintToIx] =
1225
+ await tokenFaucet.createAssociatedTokenAccountAndMintToInstructions(
1226
+ this.wallet.publicKey,
1227
+ amount
1228
+ );
1229
+
1230
+ const [userAccountPublicKey, initializeUserAccountIx] =
1231
+ await this.getInitializeUserInstructions(
1232
+ subAccountId,
1233
+ name,
1234
+ referrerInfo
1235
+ );
1236
+
1237
+ const depositCollateralIx = await this.getDepositInstruction(
1238
+ amount,
1239
+ marketIndex,
1240
+ associateTokenPublicKey,
1241
+ subAccountId,
1242
+ false,
1243
+ false
1244
+ );
1245
+
1246
+ const tx = new Transaction().add(createAssociatedAccountIx).add(mintToIx);
1247
+
1248
+ if (subAccountId === 0) {
1249
+ if (
1250
+ !(await this.checkIfAccountExists(this.getUserStatsAccountPublicKey()))
1251
+ ) {
1252
+ tx.add(await this.getInitializeUserStatsIx());
1253
+ }
1254
+ }
1255
+ tx.add(initializeUserAccountIx).add(depositCollateralIx);
1256
+
1257
+ const txSig = await this.program.provider.sendAndConfirm(tx, []);
1258
+
1259
+ return [txSig, userAccountPublicKey];
1260
+ }
1261
+
1262
+ public async withdraw(
1263
+ amount: BN,
1264
+ marketIndex: number,
1265
+ userTokenAccount: PublicKey,
1266
+ reduceOnly = false
1267
+ ): Promise<TransactionSignature> {
1268
+ const tx = new Transaction();
1269
+ tx.add(
1270
+ ComputeBudgetProgram.requestUnits({
1271
+ units: 600_000,
1272
+ additionalFee: 0,
1273
+ })
1274
+ );
1275
+
1276
+ const additionalSigners: Array<Signer> = [];
1277
+
1278
+ const spotMarketAccount = this.getSpotMarketAccount(marketIndex);
1279
+
1280
+ const isSolMarket = spotMarketAccount.mint.equals(WRAPPED_SOL_MINT);
1281
+
1282
+ const authority = this.wallet.publicKey;
1283
+
1284
+ const createWSOLTokenAccount =
1285
+ isSolMarket && userTokenAccount.equals(authority);
1286
+
1287
+ if (createWSOLTokenAccount) {
1288
+ const { ixs, signers, pubkey } =
1289
+ await this.getWrappedSolAccountCreationIxs(amount, false);
1290
+
1291
+ userTokenAccount = pubkey;
1292
+
1293
+ ixs.forEach((ix) => {
1294
+ tx.add(ix);
1295
+ });
1296
+
1297
+ signers.forEach((signer) => additionalSigners.push(signer));
1298
+ } else {
1299
+ const accountExists = await this.checkIfAccountExists(userTokenAccount);
1300
+
1301
+ if (!accountExists) {
1302
+ const createAssociatedTokenAccountIx =
1303
+ this.getAssociatedTokenAccountCreationIx(
1304
+ spotMarketAccount.mint,
1305
+ userTokenAccount
1306
+ );
1307
+
1308
+ tx.add(createAssociatedTokenAccountIx);
1309
+ }
1310
+ }
1311
+
1312
+ const withdrawCollateral = await this.getWithdrawIx(
1313
+ amount,
1314
+ spotMarketAccount.marketIndex,
1315
+ userTokenAccount,
1316
+ reduceOnly
1317
+ );
1318
+
1319
+ tx.add(withdrawCollateral);
1320
+
1321
+ // Close the wrapped sol account at the end of the transaction
1322
+ if (createWSOLTokenAccount) {
1323
+ tx.add(
1324
+ Token.createCloseAccountInstruction(
1325
+ TOKEN_PROGRAM_ID,
1326
+ userTokenAccount,
1327
+ authority,
1328
+ authority,
1329
+ []
1330
+ )
1331
+ );
1332
+ }
1333
+
1334
+ const { txSig, slot } = await this.txSender.send(
1335
+ tx,
1336
+ additionalSigners,
1337
+ this.opts
1338
+ );
1339
+ this.spotMarketLastSlotCache.set(marketIndex, slot);
1340
+ return txSig;
1341
+ }
1342
+
1343
+ public async getWithdrawIx(
1344
+ amount: BN,
1345
+ marketIndex: number,
1346
+ userTokenAccount: PublicKey,
1347
+ reduceOnly = false
1348
+ ): Promise<TransactionInstruction> {
1349
+ const userAccountPublicKey = await this.getUserAccountPublicKey();
1350
+
1351
+ const remainingAccounts = this.getRemainingAccounts({
1352
+ userAccounts: [this.getUserAccount()],
1353
+ useMarketLastSlotCache: true,
1354
+ writableSpotMarketIndexes: [marketIndex],
1355
+ readableSpotMarketIndexes: [QUOTE_SPOT_MARKET_INDEX],
1356
+ });
1357
+
1358
+ const spotMarketAccount = this.getSpotMarketAccount(marketIndex);
1359
+
1360
+ return await this.program.instruction.withdraw(
1361
+ marketIndex,
1362
+ amount,
1363
+ reduceOnly,
1364
+ {
1365
+ accounts: {
1366
+ state: await this.getStatePublicKey(),
1367
+ spotMarket: spotMarketAccount.pubkey,
1368
+ spotMarketVault: spotMarketAccount.vault,
1369
+ driftSigner: this.getSignerPublicKey(),
1370
+ user: userAccountPublicKey,
1371
+ userStats: this.getUserStatsAccountPublicKey(),
1372
+ userTokenAccount: userTokenAccount,
1373
+ authority: this.wallet.publicKey,
1374
+ tokenProgram: TOKEN_PROGRAM_ID,
1375
+ },
1376
+ remainingAccounts,
1377
+ }
1378
+ );
1379
+ }
1380
+
1381
+ public async transferDeposit(
1382
+ amount: BN,
1383
+ marketIndex: number,
1384
+ fromSubAccountId: number,
1385
+ toSubAccountId: number
1386
+ ): Promise<TransactionSignature> {
1387
+ const { txSig, slot } = await this.txSender.send(
1388
+ wrapInTx(
1389
+ await this.getTransferDepositIx(
1390
+ amount,
1391
+ marketIndex,
1392
+ fromSubAccountId,
1393
+ toSubAccountId
1394
+ )
1395
+ ),
1396
+ [],
1397
+ this.opts
1398
+ );
1399
+ if (
1400
+ fromSubAccountId === this.activeSubAccountId ||
1401
+ toSubAccountId === this.activeSubAccountId
1402
+ ) {
1403
+ this.spotMarketLastSlotCache.set(marketIndex, slot);
1404
+ }
1405
+ return txSig;
1406
+ }
1407
+
1408
+ public async getTransferDepositIx(
1409
+ amount: BN,
1410
+ marketIndex: number,
1411
+ fromSubAccountId: number,
1412
+ toSubAccountId: number
1413
+ ): Promise<TransactionInstruction> {
1414
+ const fromUser = await getUserAccountPublicKey(
1415
+ this.program.programId,
1416
+ this.wallet.publicKey,
1417
+ fromSubAccountId
1418
+ );
1419
+ const toUser = await getUserAccountPublicKey(
1420
+ this.program.programId,
1421
+ this.wallet.publicKey,
1422
+ toSubAccountId
1423
+ );
1424
+
1425
+ let remainingAccounts;
1426
+ if (this.users.has(fromSubAccountId)) {
1427
+ remainingAccounts = this.getRemainingAccounts({
1428
+ userAccounts: [this.users.get(fromSubAccountId).getUserAccount()],
1429
+ useMarketLastSlotCache: true,
1430
+ writableSpotMarketIndexes: [marketIndex],
1431
+ });
1432
+ } else {
1433
+ const userAccountPublicKey = getUserAccountPublicKeySync(
1434
+ this.program.programId,
1435
+ this.authority,
1436
+ fromSubAccountId
1437
+ );
1438
+
1439
+ const fromUserAccount = (await this.program.account.user.fetch(
1440
+ userAccountPublicKey
1441
+ )) as UserAccount;
1442
+ remainingAccounts = this.getRemainingAccounts({
1443
+ userAccounts: [fromUserAccount],
1444
+ useMarketLastSlotCache: true,
1445
+ writableSpotMarketIndexes: [marketIndex],
1446
+ });
1447
+ }
1448
+
1449
+ return await this.program.instruction.transferDeposit(marketIndex, amount, {
1450
+ accounts: {
1451
+ authority: this.wallet.publicKey,
1452
+ fromUser,
1453
+ toUser,
1454
+ userStats: this.getUserStatsAccountPublicKey(),
1455
+ state: await this.getStatePublicKey(),
1456
+ spotMarketVault: this.getSpotMarketAccount(marketIndex).vault,
1457
+ },
1458
+ remainingAccounts,
1459
+ });
1460
+ }
1461
+
1462
+ public async updateSpotMarketCumulativeInterest(
1463
+ marketIndex: number
1464
+ ): Promise<TransactionSignature> {
1465
+ const { txSig } = await this.txSender.send(
1466
+ wrapInTx(await this.updateSpotMarketCumulativeInterestIx(marketIndex)),
1467
+ [],
1468
+ this.opts
1469
+ );
1470
+ return txSig;
1471
+ }
1472
+
1473
+ public async updateSpotMarketCumulativeInterestIx(
1474
+ marketIndex: number
1475
+ ): Promise<TransactionInstruction> {
1476
+ const spotMarket = this.getSpotMarketAccount(marketIndex);
1477
+ return await this.program.instruction.updateSpotMarketCumulativeInterest({
1478
+ accounts: {
1479
+ state: await this.getStatePublicKey(),
1480
+ spotMarket: spotMarket.pubkey,
1481
+ oracle: spotMarket.oracle,
1482
+ },
1483
+ });
1484
+ }
1485
+
1486
+ public async settleLP(
1487
+ settleeUserAccountPublicKey: PublicKey,
1488
+ marketIndex: number
1489
+ ): Promise<TransactionSignature> {
1490
+ const { txSig } = await this.txSender.send(
1491
+ wrapInTx(await this.settleLPIx(settleeUserAccountPublicKey, marketIndex)),
1492
+ [],
1493
+ this.opts
1494
+ );
1495
+ return txSig;
1496
+ }
1497
+
1498
+ public async settleLPIx(
1499
+ settleeUserAccountPublicKey: PublicKey,
1500
+ marketIndex: number
1501
+ ): Promise<TransactionInstruction> {
1502
+ const settleeUserAccount = (await this.program.account.user.fetch(
1503
+ settleeUserAccountPublicKey
1504
+ )) as UserAccount;
1505
+
1506
+ const remainingAccounts = this.getRemainingAccounts({
1507
+ userAccounts: [settleeUserAccount],
1508
+ writablePerpMarketIndexes: [marketIndex],
1509
+ });
1510
+
1511
+ return this.program.instruction.settleLp(marketIndex, {
1512
+ accounts: {
1513
+ state: await this.getStatePublicKey(),
1514
+ user: settleeUserAccountPublicKey,
1515
+ },
1516
+ remainingAccounts: remainingAccounts,
1517
+ });
1518
+ }
1519
+
1520
+ public async removePerpLpShares(
1521
+ marketIndex: number,
1522
+ sharesToBurn?: BN
1523
+ ): Promise<TransactionSignature> {
1524
+ const { txSig } = await this.txSender.send(
1525
+ wrapInTx(await this.getRemovePerpLpSharesIx(marketIndex, sharesToBurn)),
1526
+ [],
1527
+ this.opts
1528
+ );
1529
+ return txSig;
1530
+ }
1531
+
1532
+ public async removePerpLpSharesInExpiringMarket(
1533
+ marketIndex: number,
1534
+ userAccountPublicKey: PublicKey,
1535
+ sharesToBurn?: BN
1536
+ ): Promise<TransactionSignature> {
1537
+ const { txSig } = await this.txSender.send(
1538
+ wrapInTx(
1539
+ await this.getRemovePerpLpSharesInExpiringMarket(
1540
+ marketIndex,
1541
+ userAccountPublicKey,
1542
+ sharesToBurn
1543
+ )
1544
+ ),
1545
+ [],
1546
+ this.opts
1547
+ );
1548
+ return txSig;
1549
+ }
1550
+
1551
+ public async getRemovePerpLpSharesInExpiringMarket(
1552
+ marketIndex: number,
1553
+ userAccountPublicKey: PublicKey,
1554
+ sharesToBurn?: BN
1555
+ ): Promise<TransactionInstruction> {
1556
+ const userAccount = (await this.program.account.user.fetch(
1557
+ userAccountPublicKey
1558
+ )) as UserAccount;
1559
+
1560
+ const remainingAccounts = this.getRemainingAccounts({
1561
+ userAccounts: [userAccount],
1562
+ useMarketLastSlotCache: true,
1563
+ writablePerpMarketIndexes: [marketIndex],
1564
+ });
1565
+
1566
+ if (sharesToBurn == undefined) {
1567
+ const perpPosition = userAccount.perpPositions.filter(
1568
+ (position) => position.marketIndex === marketIndex
1569
+ )[0];
1570
+ sharesToBurn = perpPosition.lpShares;
1571
+ console.log('burning lp shares:', sharesToBurn.toString());
1572
+ }
1573
+
1574
+ return this.program.instruction.removePerpLpSharesInExpiringMarket(
1575
+ sharesToBurn,
1576
+ marketIndex,
1577
+ {
1578
+ accounts: {
1579
+ state: await this.getStatePublicKey(),
1580
+ user: userAccountPublicKey,
1581
+ },
1582
+ remainingAccounts: remainingAccounts,
1583
+ }
1584
+ );
1585
+ }
1586
+
1587
+ public async getRemovePerpLpSharesIx(
1588
+ marketIndex: number,
1589
+ sharesToBurn?: BN
1590
+ ): Promise<TransactionInstruction> {
1591
+ const userAccountPublicKey = await this.getUserAccountPublicKey();
1592
+
1593
+ const remainingAccounts = this.getRemainingAccounts({
1594
+ userAccounts: [this.getUserAccount()],
1595
+ useMarketLastSlotCache: true,
1596
+ writablePerpMarketIndexes: [marketIndex],
1597
+ });
1598
+
1599
+ if (sharesToBurn == undefined) {
1600
+ const userAccount = this.getUserAccount();
1601
+ const perpPosition = userAccount.perpPositions.filter(
1602
+ (position) => position.marketIndex === marketIndex
1603
+ )[0];
1604
+ sharesToBurn = perpPosition.lpShares;
1605
+ console.log('burning lp shares:', sharesToBurn.toString());
1606
+ }
1607
+
1608
+ return this.program.instruction.removePerpLpShares(
1609
+ sharesToBurn,
1610
+ marketIndex,
1611
+ {
1612
+ accounts: {
1613
+ state: await this.getStatePublicKey(),
1614
+ user: userAccountPublicKey,
1615
+ authority: this.wallet.publicKey,
1616
+ },
1617
+ remainingAccounts: remainingAccounts,
1618
+ }
1619
+ );
1620
+ }
1621
+
1622
+ public async addPerpLpShares(
1623
+ amount: BN,
1624
+ marketIndex: number
1625
+ ): Promise<TransactionSignature> {
1626
+ const { txSig, slot } = await this.txSender.send(
1627
+ wrapInTx(await this.getAddPerpLpSharesIx(amount, marketIndex)),
1628
+ [],
1629
+ this.opts
1630
+ );
1631
+ this.perpMarketLastSlotCache.set(marketIndex, slot);
1632
+ return txSig;
1633
+ }
1634
+
1635
+ public async getAddPerpLpSharesIx(
1636
+ amount: BN,
1637
+ marketIndex: number
1638
+ ): Promise<TransactionInstruction> {
1639
+ const userAccountPublicKey = await this.getUserAccountPublicKey();
1640
+ const remainingAccounts = this.getRemainingAccounts({
1641
+ userAccounts: [this.getUserAccount()],
1642
+ useMarketLastSlotCache: true,
1643
+ writablePerpMarketIndexes: [marketIndex],
1644
+ });
1645
+
1646
+ return this.program.instruction.addPerpLpShares(amount, marketIndex, {
1647
+ accounts: {
1648
+ state: await this.getStatePublicKey(),
1649
+ user: userAccountPublicKey,
1650
+ authority: this.wallet.publicKey,
1651
+ },
1652
+ remainingAccounts: remainingAccounts,
1653
+ });
1654
+ }
1655
+
1656
+ public async openPosition(
1657
+ direction: PositionDirection,
1658
+ amount: BN,
1659
+ marketIndex: number,
1660
+ limitPrice?: BN
1661
+ ): Promise<TransactionSignature> {
1662
+ return await this.placeAndTakePerpOrder({
1663
+ orderType: OrderType.MARKET,
1664
+ marketIndex,
1665
+ direction,
1666
+ baseAssetAmount: amount,
1667
+ price: limitPrice,
1668
+ });
1669
+ }
1670
+
1671
+ public async sendSignedTx(tx: Transaction): Promise<TransactionSignature> {
1672
+ const { txSig } = await this.txSender.send(tx, undefined, this.opts, true);
1673
+
1674
+ return txSig;
1675
+ }
1676
+
1677
+ /**
1678
+ * Sends a market order and returns a signed tx which can fill the order against the vamm, which the caller can use to fill their own order if required.
1679
+ * @param orderParams
1680
+ * @param userAccountPublicKey
1681
+ * @param userAccount
1682
+ * @returns
1683
+ */
1684
+ public async sendMarketOrderAndGetSignedFillTx(
1685
+ orderParams: OptionalOrderParams,
1686
+ userAccountPublicKey: PublicKey,
1687
+ userAccount: UserAccount
1688
+ ): Promise<{ txSig: TransactionSignature; signedFillTx: Transaction }> {
1689
+ const marketIndex = orderParams.marketIndex;
1690
+ const orderId = userAccount.nextOrderId;
1691
+
1692
+ const marketOrderTx = wrapInTx(await this.getPlacePerpOrderIx(orderParams));
1693
+ const fillTx = wrapInTx(
1694
+ await this.getFillPerpOrderIx(userAccountPublicKey, userAccount, {
1695
+ orderId,
1696
+ marketIndex,
1697
+ })
1698
+ );
1699
+
1700
+ // Apply the latest blockhash to the txs so that we can sign before sending them
1701
+ const currentBlockHash = (
1702
+ await this.connection.getLatestBlockhash('finalized')
1703
+ ).blockhash;
1704
+ marketOrderTx.recentBlockhash = currentBlockHash;
1705
+ fillTx.recentBlockhash = currentBlockHash;
1706
+
1707
+ marketOrderTx.feePayer = userAccount.authority;
1708
+ fillTx.feePayer = userAccount.authority;
1709
+
1710
+ const [signedMarketOrderTx, signedFillTx] =
1711
+ await this.provider.wallet.signAllTransactions([marketOrderTx, fillTx]);
1712
+
1713
+ const { txSig, slot } = await this.txSender.send(
1714
+ signedMarketOrderTx,
1715
+ [],
1716
+ this.opts,
1717
+ true
1718
+ );
1719
+
1720
+ this.perpMarketLastSlotCache.set(orderParams.marketIndex, slot);
1721
+
1722
+ return { txSig, signedFillTx };
1723
+ }
1724
+
1725
+ public async placePerpOrder(
1726
+ orderParams: OptionalOrderParams
1727
+ ): Promise<TransactionSignature> {
1728
+ const { txSig, slot } = await this.txSender.send(
1729
+ wrapInTx(await this.getPlacePerpOrderIx(orderParams)),
1730
+ [],
1731
+ this.opts
1732
+ );
1733
+ this.perpMarketLastSlotCache.set(orderParams.marketIndex, slot);
1734
+ return txSig;
1735
+ }
1736
+
1737
+ getOrderParams(
1738
+ optionalOrderParams: OptionalOrderParams,
1739
+ marketType: MarketType
1740
+ ): OrderParams {
1741
+ return Object.assign({}, DefaultOrderParams, optionalOrderParams, {
1742
+ marketType,
1743
+ });
1744
+ }
1745
+
1746
+ public async getPlacePerpOrderIx(
1747
+ orderParams: OptionalOrderParams
1748
+ ): Promise<TransactionInstruction> {
1749
+ orderParams = this.getOrderParams(orderParams, MarketType.PERP);
1750
+ const userAccountPublicKey = await this.getUserAccountPublicKey();
1751
+
1752
+ const remainingAccounts = this.getRemainingAccounts({
1753
+ userAccounts: [this.getUserAccount()],
1754
+ useMarketLastSlotCache: true,
1755
+ readablePerpMarketIndex: orderParams.marketIndex,
1756
+ });
1757
+
1758
+ return await this.program.instruction.placePerpOrder(orderParams, {
1759
+ accounts: {
1760
+ state: await this.getStatePublicKey(),
1761
+ user: userAccountPublicKey,
1762
+ userStats: this.getUserStatsAccountPublicKey(),
1763
+ authority: this.wallet.publicKey,
1764
+ },
1765
+ remainingAccounts,
1766
+ });
1767
+ }
1768
+
1769
+ public async updateAMMs(
1770
+ marketIndexes: number[]
1771
+ ): Promise<TransactionSignature> {
1772
+ const { txSig } = await this.txSender.send(
1773
+ wrapInTx(await this.getUpdateAMMsIx(marketIndexes)),
1774
+ [],
1775
+ this.opts
1776
+ );
1777
+ return txSig;
1778
+ }
1779
+
1780
+ public async getUpdateAMMsIx(
1781
+ marketIndexes: number[]
1782
+ ): Promise<TransactionInstruction> {
1783
+ for (let i = marketIndexes.length; i < 5; i++) {
1784
+ marketIndexes.push(100);
1785
+ }
1786
+ const marketAccountInfos = [];
1787
+ const oracleAccountInfos = [];
1788
+ for (const marketIndex of marketIndexes) {
1789
+ if (marketIndex !== 100) {
1790
+ const market = this.getPerpMarketAccount(marketIndex);
1791
+ marketAccountInfos.push({
1792
+ pubkey: market.pubkey,
1793
+ isWritable: true,
1794
+ isSigner: false,
1795
+ });
1796
+ oracleAccountInfos.push({
1797
+ pubkey: market.amm.oracle,
1798
+ isWritable: false,
1799
+ isSigner: false,
1800
+ });
1801
+ }
1802
+ }
1803
+ const remainingAccounts = oracleAccountInfos.concat(marketAccountInfos);
1804
+
1805
+ return await this.program.instruction.updateAmms(marketIndexes, {
1806
+ accounts: {
1807
+ state: await this.getStatePublicKey(),
1808
+ authority: this.wallet.publicKey,
1809
+ },
1810
+ remainingAccounts,
1811
+ });
1812
+ }
1813
+
1814
+ public async settleExpiredMarket(
1815
+ marketIndex: number
1816
+ ): Promise<TransactionSignature> {
1817
+ const { txSig } = await this.txSender.send(
1818
+ wrapInTx(await this.getSettleExpiredMarketIx(marketIndex)),
1819
+ [],
1820
+ this.opts
1821
+ );
1822
+ return txSig;
1823
+ }
1824
+
1825
+ public async getSettleExpiredMarketIx(
1826
+ marketIndex: number
1827
+ ): Promise<TransactionInstruction> {
1828
+ const marketAccountInfos = [];
1829
+ const oracleAccountInfos = [];
1830
+ const spotMarketAccountInfos = [];
1831
+ const market = this.getPerpMarketAccount(marketIndex);
1832
+ marketAccountInfos.push({
1833
+ pubkey: market.pubkey,
1834
+ isWritable: true,
1835
+ isSigner: false,
1836
+ });
1837
+ oracleAccountInfos.push({
1838
+ pubkey: market.amm.oracle,
1839
+ isWritable: false,
1840
+ isSigner: false,
1841
+ });
1842
+
1843
+ spotMarketAccountInfos.push({
1844
+ pubkey: this.getSpotMarketAccount(QUOTE_SPOT_MARKET_INDEX).pubkey,
1845
+ isSigner: false,
1846
+ isWritable: true,
1847
+ });
1848
+
1849
+ const remainingAccounts = oracleAccountInfos
1850
+ .concat(spotMarketAccountInfos)
1851
+ .concat(marketAccountInfos);
1852
+
1853
+ return await this.program.instruction.settleExpiredMarket(marketIndex, {
1854
+ accounts: {
1855
+ state: await this.getStatePublicKey(),
1856
+ authority: this.wallet.publicKey,
1857
+ },
1858
+ remainingAccounts,
1859
+ });
1860
+ }
1861
+
1862
+ public async settleExpiredMarketPoolsToRevenuePool(
1863
+ perpMarketIndex: number
1864
+ ): Promise<TransactionSignature> {
1865
+ const perpMarketPublicKey = await getPerpMarketPublicKey(
1866
+ this.program.programId,
1867
+ perpMarketIndex
1868
+ );
1869
+
1870
+ const spotMarketPublicKey = await getSpotMarketPublicKey(
1871
+ this.program.programId,
1872
+ QUOTE_SPOT_MARKET_INDEX
1873
+ );
1874
+
1875
+ const ix =
1876
+ await this.program.instruction.settleExpiredMarketPoolsToRevenuePool({
1877
+ accounts: {
1878
+ state: await this.getStatePublicKey(),
1879
+ admin: this.wallet.publicKey,
1880
+ spotMarket: spotMarketPublicKey,
1881
+ perpMarket: perpMarketPublicKey,
1882
+ },
1883
+ });
1884
+
1885
+ const { txSig } = await this.txSender.send(wrapInTx(ix), [], this.opts);
1886
+
1887
+ return txSig;
1888
+ }
1889
+
1890
+ public async cancelOrder(orderId?: number): Promise<TransactionSignature> {
1891
+ const { txSig } = await this.txSender.send(
1892
+ wrapInTx(await this.getCancelOrderIx(orderId)),
1893
+ [],
1894
+ this.opts
1895
+ );
1896
+ return txSig;
1897
+ }
1898
+
1899
+ public async getCancelOrderIx(
1900
+ orderId?: number
1901
+ ): Promise<TransactionInstruction> {
1902
+ const userAccountPublicKey = await this.getUserAccountPublicKey();
1903
+
1904
+ const remainingAccounts = this.getRemainingAccounts({
1905
+ userAccounts: [this.getUserAccount()],
1906
+ useMarketLastSlotCache: true,
1907
+ });
1908
+
1909
+ return await this.program.instruction.cancelOrder(orderId ?? null, {
1910
+ accounts: {
1911
+ state: await this.getStatePublicKey(),
1912
+ user: userAccountPublicKey,
1913
+ authority: this.wallet.publicKey,
1914
+ },
1915
+ remainingAccounts,
1916
+ });
1917
+ }
1918
+
1919
+ public async cancelOrderByUserId(
1920
+ userOrderId: number
1921
+ ): Promise<TransactionSignature> {
1922
+ const { txSig } = await this.txSender.send(
1923
+ wrapInTx(await this.getCancelOrderByUserIdIx(userOrderId)),
1924
+ [],
1925
+ this.opts
1926
+ );
1927
+ return txSig;
1928
+ }
1929
+
1930
+ public async getCancelOrderByUserIdIx(
1931
+ userOrderId: number
1932
+ ): Promise<TransactionInstruction> {
1933
+ const userAccountPublicKey = await this.getUserAccountPublicKey();
1934
+
1935
+ const order = this.getOrderByUserId(userOrderId);
1936
+ const oracle = this.getPerpMarketAccount(order.marketIndex).amm.oracle;
1937
+
1938
+ const remainingAccounts = this.getRemainingAccounts({
1939
+ userAccounts: [this.getUserAccount()],
1940
+ useMarketLastSlotCache: true,
1941
+ });
1942
+
1943
+ return await this.program.instruction.cancelOrderByUserId(userOrderId, {
1944
+ accounts: {
1945
+ state: await this.getStatePublicKey(),
1946
+ user: userAccountPublicKey,
1947
+ authority: this.wallet.publicKey,
1948
+ oracle,
1949
+ },
1950
+ remainingAccounts,
1951
+ });
1952
+ }
1953
+
1954
+ public async cancelOrders(
1955
+ marketType?: MarketType,
1956
+ marketIndex?: number,
1957
+ direction?: PositionDirection
1958
+ ): Promise<TransactionSignature> {
1959
+ const { txSig } = await this.txSender.send(
1960
+ wrapInTx(
1961
+ await this.getCancelOrdersIx(marketType, marketIndex, direction)
1962
+ ),
1963
+ [],
1964
+ this.opts
1965
+ );
1966
+ return txSig;
1967
+ }
1968
+
1969
+ public async getCancelOrdersIx(
1970
+ marketType: MarketType | null,
1971
+ marketIndex: number | null,
1972
+ direction: PositionDirection | null
1973
+ ): Promise<TransactionInstruction> {
1974
+ const userAccountPublicKey = await this.getUserAccountPublicKey();
1975
+
1976
+ const remainingAccounts = this.getRemainingAccounts({
1977
+ userAccounts: [this.getUserAccount()],
1978
+ useMarketLastSlotCache: true,
1979
+ });
1980
+
1981
+ return await this.program.instruction.cancelOrders(
1982
+ marketType ?? null,
1983
+ marketIndex ?? null,
1984
+ direction ?? null,
1985
+ {
1986
+ accounts: {
1987
+ state: await this.getStatePublicKey(),
1988
+ user: userAccountPublicKey,
1989
+ authority: this.wallet.publicKey,
1990
+ },
1991
+ remainingAccounts,
1992
+ }
1993
+ );
1994
+ }
1995
+
1996
+ public async fillPerpOrder(
1997
+ userAccountPublicKey: PublicKey,
1998
+ user: UserAccount,
1999
+ order?: Pick<Order, 'marketIndex' | 'orderId'>,
2000
+ makerInfo?: MakerInfo,
2001
+ referrerInfo?: ReferrerInfo
2002
+ ): Promise<TransactionSignature> {
2003
+ const { txSig } = await this.txSender.send(
2004
+ wrapInTx(
2005
+ await this.getFillPerpOrderIx(
2006
+ userAccountPublicKey,
2007
+ user,
2008
+ order,
2009
+ makerInfo,
2010
+ referrerInfo
2011
+ )
2012
+ ),
2013
+ [],
2014
+ this.opts
2015
+ );
2016
+ return txSig;
2017
+ }
2018
+
2019
+ public async getFillPerpOrderIx(
2020
+ userAccountPublicKey: PublicKey,
2021
+ userAccount: UserAccount,
2022
+ order: Pick<Order, 'marketIndex' | 'orderId'>,
2023
+ makerInfo?: MakerInfo,
2024
+ referrerInfo?: ReferrerInfo
2025
+ ): Promise<TransactionInstruction> {
2026
+ const userStatsPublicKey = getUserStatsAccountPublicKey(
2027
+ this.program.programId,
2028
+ userAccount.authority
2029
+ );
2030
+
2031
+ const fillerPublicKey = await this.getUserAccountPublicKey();
2032
+ const fillerStatsPublicKey = this.getUserStatsAccountPublicKey();
2033
+
2034
+ const marketIndex = order
2035
+ ? order.marketIndex
2036
+ : userAccount.orders.find(
2037
+ (order) => order.orderId === userAccount.nextOrderId - 1
2038
+ ).marketIndex;
2039
+
2040
+ const userAccounts = [userAccount];
2041
+ if (makerInfo !== undefined) {
2042
+ userAccounts.push(makerInfo.makerUserAccount);
2043
+ }
2044
+ const remainingAccounts = this.getRemainingAccounts({
2045
+ userAccounts,
2046
+ writablePerpMarketIndexes: [marketIndex],
2047
+ });
2048
+
2049
+ if (makerInfo) {
2050
+ remainingAccounts.push({
2051
+ pubkey: makerInfo.maker,
2052
+ isWritable: true,
2053
+ isSigner: false,
2054
+ });
2055
+ remainingAccounts.push({
2056
+ pubkey: makerInfo.makerStats,
2057
+ isWritable: true,
2058
+ isSigner: false,
2059
+ });
2060
+ }
2061
+
2062
+ if (referrerInfo) {
2063
+ remainingAccounts.push({
2064
+ pubkey: referrerInfo.referrer,
2065
+ isWritable: true,
2066
+ isSigner: false,
2067
+ });
2068
+ remainingAccounts.push({
2069
+ pubkey: referrerInfo.referrerStats,
2070
+ isWritable: true,
2071
+ isSigner: false,
2072
+ });
2073
+ }
2074
+
2075
+ const orderId = order.orderId;
2076
+ const makerOrderId = makerInfo ? makerInfo.order.orderId : null;
2077
+
2078
+ return await this.program.instruction.fillPerpOrder(orderId, makerOrderId, {
2079
+ accounts: {
2080
+ state: await this.getStatePublicKey(),
2081
+ filler: fillerPublicKey,
2082
+ fillerStats: fillerStatsPublicKey,
2083
+ user: userAccountPublicKey,
2084
+ userStats: userStatsPublicKey,
2085
+ authority: this.wallet.publicKey,
2086
+ },
2087
+ remainingAccounts,
2088
+ });
2089
+ }
2090
+
2091
+ public async placeSpotOrder(
2092
+ orderParams: OptionalOrderParams
2093
+ ): Promise<TransactionSignature> {
2094
+ const { txSig, slot } = await this.txSender.send(
2095
+ wrapInTx(await this.getPlaceSpotOrderIx(orderParams)),
2096
+ [],
2097
+ this.opts
2098
+ );
2099
+ this.spotMarketLastSlotCache.set(orderParams.marketIndex, slot);
2100
+ this.spotMarketLastSlotCache.set(QUOTE_SPOT_MARKET_INDEX, slot);
2101
+ return txSig;
2102
+ }
2103
+
2104
+ public async getPlaceSpotOrderIx(
2105
+ orderParams: OptionalOrderParams
2106
+ ): Promise<TransactionInstruction> {
2107
+ orderParams = this.getOrderParams(orderParams, MarketType.SPOT);
2108
+ const userAccountPublicKey = await this.getUserAccountPublicKey();
2109
+
2110
+ const remainingAccounts = this.getRemainingAccounts({
2111
+ userAccounts: [this.getUserAccount()],
2112
+ useMarketLastSlotCache: true,
2113
+ readableSpotMarketIndexes: [
2114
+ orderParams.marketIndex,
2115
+ QUOTE_SPOT_MARKET_INDEX,
2116
+ ],
2117
+ });
2118
+
2119
+ return await this.program.instruction.placeSpotOrder(orderParams, {
2120
+ accounts: {
2121
+ state: await this.getStatePublicKey(),
2122
+ user: userAccountPublicKey,
2123
+ userStats: this.getUserStatsAccountPublicKey(),
2124
+ authority: this.wallet.publicKey,
2125
+ },
2126
+ remainingAccounts,
2127
+ });
2128
+ }
2129
+
2130
+ public async fillSpotOrder(
2131
+ userAccountPublicKey: PublicKey,
2132
+ user: UserAccount,
2133
+ order?: Order,
2134
+ fulfillmentConfig?: SerumV3FulfillmentConfigAccount,
2135
+ makerInfo?: MakerInfo,
2136
+ referrerInfo?: ReferrerInfo
2137
+ ): Promise<TransactionSignature> {
2138
+ const { txSig } = await this.txSender.send(
2139
+ wrapInTx(
2140
+ await this.getFillSpotOrderIx(
2141
+ userAccountPublicKey,
2142
+ user,
2143
+ order,
2144
+ fulfillmentConfig,
2145
+ makerInfo,
2146
+ referrerInfo
2147
+ )
2148
+ ),
2149
+ [],
2150
+ this.opts
2151
+ );
2152
+ return txSig;
2153
+ }
2154
+
2155
+ public async getFillSpotOrderIx(
2156
+ userAccountPublicKey: PublicKey,
2157
+ userAccount: UserAccount,
2158
+ order?: Order,
2159
+ fulfillmentConfig?: SerumV3FulfillmentConfigAccount,
2160
+ makerInfo?: MakerInfo,
2161
+ referrerInfo?: ReferrerInfo
2162
+ ): Promise<TransactionInstruction> {
2163
+ const userStatsPublicKey = getUserStatsAccountPublicKey(
2164
+ this.program.programId,
2165
+ userAccount.authority
2166
+ );
2167
+
2168
+ const fillerPublicKey = await this.getUserAccountPublicKey();
2169
+ const fillerStatsPublicKey = this.getUserStatsAccountPublicKey();
2170
+
2171
+ const marketIndex = order
2172
+ ? order.marketIndex
2173
+ : userAccount.orders.find(
2174
+ (order) => order.orderId === userAccount.nextOrderId - 1
2175
+ ).marketIndex;
2176
+
2177
+ const userAccounts = [userAccount];
2178
+ if (makerInfo !== undefined) {
2179
+ userAccounts.push(makerInfo.makerUserAccount);
2180
+ }
2181
+ const remainingAccounts = this.getRemainingAccounts({
2182
+ userAccounts,
2183
+ writableSpotMarketIndexes: [marketIndex, QUOTE_SPOT_MARKET_INDEX],
2184
+ });
2185
+
2186
+ if (makerInfo) {
2187
+ remainingAccounts.push({
2188
+ pubkey: makerInfo.maker,
2189
+ isWritable: true,
2190
+ isSigner: false,
2191
+ });
2192
+ remainingAccounts.push({
2193
+ pubkey: makerInfo.makerStats,
2194
+ isWritable: true,
2195
+ isSigner: false,
2196
+ });
2197
+ }
2198
+
2199
+ if (referrerInfo) {
2200
+ remainingAccounts.push({
2201
+ pubkey: referrerInfo.referrer,
2202
+ isWritable: true,
2203
+ isSigner: false,
2204
+ });
2205
+ remainingAccounts.push({
2206
+ pubkey: referrerInfo.referrerStats,
2207
+ isWritable: true,
2208
+ isSigner: false,
2209
+ });
2210
+ }
2211
+
2212
+ const orderId = order.orderId;
2213
+ const makerOrderId = makerInfo ? makerInfo.order.orderId : null;
2214
+
2215
+ this.addSpotFulfillmentAccounts(
2216
+ marketIndex,
2217
+ remainingAccounts,
2218
+ fulfillmentConfig
2219
+ );
2220
+
2221
+ return await this.program.instruction.fillSpotOrder(
2222
+ orderId,
2223
+ fulfillmentConfig ? fulfillmentConfig.fulfillmentType : null,
2224
+ makerOrderId,
2225
+ {
2226
+ accounts: {
2227
+ state: await this.getStatePublicKey(),
2228
+ filler: fillerPublicKey,
2229
+ fillerStats: fillerStatsPublicKey,
2230
+ user: userAccountPublicKey,
2231
+ userStats: userStatsPublicKey,
2232
+ authority: this.wallet.publicKey,
2233
+ },
2234
+ remainingAccounts,
2235
+ }
2236
+ );
2237
+ }
2238
+
2239
+ addSpotFulfillmentAccounts(
2240
+ marketIndex: number,
2241
+ remainingAccounts: AccountMeta[],
2242
+ fulfillmentConfig?: SerumV3FulfillmentConfigAccount
2243
+ ) {
2244
+ if (fulfillmentConfig) {
2245
+ this.addSerumRemainingAccounts(
2246
+ marketIndex,
2247
+ remainingAccounts,
2248
+ fulfillmentConfig
2249
+ );
2250
+ } else {
2251
+ remainingAccounts.push({
2252
+ pubkey: this.getSpotMarketAccount(marketIndex).vault,
2253
+ isWritable: false,
2254
+ isSigner: false,
2255
+ });
2256
+ remainingAccounts.push({
2257
+ pubkey: this.getQuoteSpotMarketAccount().vault,
2258
+ isWritable: false,
2259
+ isSigner: false,
2260
+ });
2261
+ }
2262
+ }
2263
+
2264
+ addSerumRemainingAccounts(
2265
+ marketIndex: number,
2266
+ remainingAccounts: AccountMeta[],
2267
+ fulfillmentConfig: SerumV3FulfillmentConfigAccount
2268
+ ) {
2269
+ remainingAccounts.push({
2270
+ pubkey: fulfillmentConfig.pubkey,
2271
+ isWritable: false,
2272
+ isSigner: false,
2273
+ });
2274
+ remainingAccounts.push({
2275
+ pubkey: fulfillmentConfig.serumProgramId,
2276
+ isWritable: false,
2277
+ isSigner: false,
2278
+ });
2279
+ remainingAccounts.push({
2280
+ pubkey: fulfillmentConfig.serumMarket,
2281
+ isWritable: true,
2282
+ isSigner: false,
2283
+ });
2284
+ remainingAccounts.push({
2285
+ pubkey: fulfillmentConfig.serumRequestQueue,
2286
+ isWritable: true,
2287
+ isSigner: false,
2288
+ });
2289
+ remainingAccounts.push({
2290
+ pubkey: fulfillmentConfig.serumEventQueue,
2291
+ isWritable: true,
2292
+ isSigner: false,
2293
+ });
2294
+ remainingAccounts.push({
2295
+ pubkey: fulfillmentConfig.serumBids,
2296
+ isWritable: true,
2297
+ isSigner: false,
2298
+ });
2299
+ remainingAccounts.push({
2300
+ pubkey: fulfillmentConfig.serumAsks,
2301
+ isWritable: true,
2302
+ isSigner: false,
2303
+ });
2304
+ remainingAccounts.push({
2305
+ pubkey: fulfillmentConfig.serumBaseVault,
2306
+ isWritable: true,
2307
+ isSigner: false,
2308
+ });
2309
+ remainingAccounts.push({
2310
+ pubkey: fulfillmentConfig.serumQuoteVault,
2311
+ isWritable: true,
2312
+ isSigner: false,
2313
+ });
2314
+ remainingAccounts.push({
2315
+ pubkey: fulfillmentConfig.serumOpenOrders,
2316
+ isWritable: true,
2317
+ isSigner: false,
2318
+ });
2319
+ remainingAccounts.push({
2320
+ pubkey: getSerumSignerPublicKey(
2321
+ fulfillmentConfig.serumProgramId,
2322
+ fulfillmentConfig.serumMarket,
2323
+ fulfillmentConfig.serumSignerNonce
2324
+ ),
2325
+ isWritable: false,
2326
+ isSigner: false,
2327
+ });
2328
+ remainingAccounts.push({
2329
+ pubkey: this.getSignerPublicKey(),
2330
+ isWritable: false,
2331
+ isSigner: false,
2332
+ });
2333
+ remainingAccounts.push({
2334
+ pubkey: TOKEN_PROGRAM_ID,
2335
+ isWritable: false,
2336
+ isSigner: false,
2337
+ });
2338
+ remainingAccounts.push({
2339
+ pubkey: this.getSpotMarketAccount(marketIndex).vault,
2340
+ isWritable: true,
2341
+ isSigner: false,
2342
+ });
2343
+ remainingAccounts.push({
2344
+ pubkey: this.getQuoteSpotMarketAccount().vault,
2345
+ isWritable: true,
2346
+ isSigner: false,
2347
+ });
2348
+ remainingAccounts.push({
2349
+ pubkey: this.getStateAccount().srmVault,
2350
+ isWritable: false,
2351
+ isSigner: false,
2352
+ });
2353
+ }
2354
+
2355
+ public async triggerOrder(
2356
+ userAccountPublicKey: PublicKey,
2357
+ user: UserAccount,
2358
+ order: Order
2359
+ ): Promise<TransactionSignature> {
2360
+ const { txSig } = await this.txSender.send(
2361
+ wrapInTx(await this.getTriggerOrderIx(userAccountPublicKey, user, order)),
2362
+ [],
2363
+ this.opts
2364
+ );
2365
+ return txSig;
2366
+ }
2367
+
2368
+ public async getTriggerOrderIx(
2369
+ userAccountPublicKey: PublicKey,
2370
+ userAccount: UserAccount,
2371
+ order: Order
2372
+ ): Promise<TransactionInstruction> {
2373
+ const fillerPublicKey = await this.getUserAccountPublicKey();
2374
+
2375
+ let remainingAccountsParams;
2376
+ if (isVariant(order.marketType, 'perp')) {
2377
+ remainingAccountsParams = {
2378
+ userAccounts: [userAccount],
2379
+ writablePerpMarketIndexes: [order.marketIndex],
2380
+ };
2381
+ } else {
2382
+ remainingAccountsParams = {
2383
+ userAccounts: [userAccount],
2384
+ writableSpotMarketIndexes: [order.marketIndex, QUOTE_SPOT_MARKET_INDEX],
2385
+ };
2386
+ }
2387
+
2388
+ const remainingAccounts = this.getRemainingAccounts(
2389
+ remainingAccountsParams
2390
+ );
2391
+
2392
+ const orderId = order.orderId;
2393
+ return await this.program.instruction.triggerOrder(orderId, {
2394
+ accounts: {
2395
+ state: await this.getStatePublicKey(),
2396
+ filler: fillerPublicKey,
2397
+ user: userAccountPublicKey,
2398
+ authority: this.wallet.publicKey,
2399
+ },
2400
+ remainingAccounts,
2401
+ });
2402
+ }
2403
+
2404
+ public async placeAndTakePerpOrder(
2405
+ orderParams: OptionalOrderParams,
2406
+ makerInfo?: MakerInfo,
2407
+ referrerInfo?: ReferrerInfo
2408
+ ): Promise<TransactionSignature> {
2409
+ const { txSig, slot } = await this.txSender.send(
2410
+ wrapInTx(
2411
+ await this.getPlaceAndTakePerpOrderIx(
2412
+ orderParams,
2413
+ makerInfo,
2414
+ referrerInfo
2415
+ )
2416
+ ),
2417
+ [],
2418
+ this.opts
2419
+ );
2420
+ this.perpMarketLastSlotCache.set(orderParams.marketIndex, slot);
2421
+ return txSig;
2422
+ }
2423
+
2424
+ public async getPlaceAndTakePerpOrderIx(
2425
+ orderParams: OptionalOrderParams,
2426
+ makerInfo?: MakerInfo,
2427
+ referrerInfo?: ReferrerInfo
2428
+ ): Promise<TransactionInstruction> {
2429
+ orderParams = this.getOrderParams(orderParams, MarketType.PERP);
2430
+ const userStatsPublicKey = await this.getUserStatsAccountPublicKey();
2431
+ const userAccountPublicKey = await this.getUserAccountPublicKey();
2432
+
2433
+ const userAccounts = [this.getUserAccount()];
2434
+ if (makerInfo !== undefined) {
2435
+ userAccounts.push(makerInfo.makerUserAccount);
2436
+ }
2437
+ const remainingAccounts = this.getRemainingAccounts({
2438
+ userAccounts,
2439
+ useMarketLastSlotCache: true,
2440
+ writablePerpMarketIndexes: [orderParams.marketIndex],
2441
+ });
2442
+
2443
+ let makerOrderId = null;
2444
+ if (makerInfo) {
2445
+ makerOrderId = makerInfo.order.orderId;
2446
+ remainingAccounts.push({
2447
+ pubkey: makerInfo.maker,
2448
+ isSigner: false,
2449
+ isWritable: true,
2450
+ });
2451
+ remainingAccounts.push({
2452
+ pubkey: makerInfo.makerStats,
2453
+ isSigner: false,
2454
+ isWritable: true,
2455
+ });
2456
+ }
2457
+
2458
+ if (referrerInfo) {
2459
+ remainingAccounts.push({
2460
+ pubkey: referrerInfo.referrer,
2461
+ isWritable: true,
2462
+ isSigner: false,
2463
+ });
2464
+ remainingAccounts.push({
2465
+ pubkey: referrerInfo.referrerStats,
2466
+ isWritable: true,
2467
+ isSigner: false,
2468
+ });
2469
+ }
2470
+
2471
+ return await this.program.instruction.placeAndTakePerpOrder(
2472
+ orderParams,
2473
+ makerOrderId,
2474
+ {
2475
+ accounts: {
2476
+ state: await this.getStatePublicKey(),
2477
+ user: userAccountPublicKey,
2478
+ userStats: userStatsPublicKey,
2479
+ authority: this.wallet.publicKey,
2480
+ },
2481
+ remainingAccounts,
2482
+ }
2483
+ );
2484
+ }
2485
+
2486
+ public async placeAndMakePerpOrder(
2487
+ orderParams: OptionalOrderParams,
2488
+ takerInfo: TakerInfo,
2489
+ referrerInfo?: ReferrerInfo
2490
+ ): Promise<TransactionSignature> {
2491
+ const { txSig, slot } = await this.txSender.send(
2492
+ wrapInTx(
2493
+ await this.getPlaceAndMakePerpOrderIx(
2494
+ orderParams,
2495
+ takerInfo,
2496
+ referrerInfo
2497
+ )
2498
+ ),
2499
+ [],
2500
+ this.opts
2501
+ );
2502
+
2503
+ this.perpMarketLastSlotCache.set(orderParams.marketIndex, slot);
2504
+
2505
+ return txSig;
2506
+ }
2507
+
2508
+ public async getPlaceAndMakePerpOrderIx(
2509
+ orderParams: OptionalOrderParams,
2510
+ takerInfo: TakerInfo,
2511
+ referrerInfo?: ReferrerInfo
2512
+ ): Promise<TransactionInstruction> {
2513
+ orderParams = this.getOrderParams(orderParams, MarketType.PERP);
2514
+ const userStatsPublicKey = this.getUserStatsAccountPublicKey();
2515
+ const userAccountPublicKey = await this.getUserAccountPublicKey();
2516
+
2517
+ const remainingAccounts = this.getRemainingAccounts({
2518
+ userAccounts: [this.getUserAccount(), takerInfo.takerUserAccount],
2519
+ useMarketLastSlotCache: true,
2520
+ writablePerpMarketIndexes: [orderParams.marketIndex],
2521
+ });
2522
+
2523
+ if (referrerInfo) {
2524
+ remainingAccounts.push({
2525
+ pubkey: referrerInfo.referrer,
2526
+ isWritable: true,
2527
+ isSigner: false,
2528
+ });
2529
+ remainingAccounts.push({
2530
+ pubkey: referrerInfo.referrerStats,
2531
+ isWritable: true,
2532
+ isSigner: false,
2533
+ });
2534
+ }
2535
+
2536
+ const takerOrderId = takerInfo.order.orderId;
2537
+ return await this.program.instruction.placeAndMakePerpOrder(
2538
+ orderParams,
2539
+ takerOrderId,
2540
+ {
2541
+ accounts: {
2542
+ state: await this.getStatePublicKey(),
2543
+ user: userAccountPublicKey,
2544
+ userStats: userStatsPublicKey,
2545
+ taker: takerInfo.taker,
2546
+ takerStats: takerInfo.takerStats,
2547
+ authority: this.wallet.publicKey,
2548
+ },
2549
+ remainingAccounts,
2550
+ }
2551
+ );
2552
+ }
2553
+
2554
+ public async placeAndTakeSpotOrder(
2555
+ orderParams: OptionalOrderParams,
2556
+ fulfillmentConfig?: SerumV3FulfillmentConfigAccount,
2557
+ makerInfo?: MakerInfo,
2558
+ referrerInfo?: ReferrerInfo
2559
+ ): Promise<TransactionSignature> {
2560
+ const { txSig, slot } = await this.txSender.send(
2561
+ wrapInTx(
2562
+ await this.getPlaceAndTakeSpotOrderIx(
2563
+ orderParams,
2564
+ fulfillmentConfig,
2565
+ makerInfo,
2566
+ referrerInfo
2567
+ )
2568
+ ),
2569
+ [],
2570
+ this.opts
2571
+ );
2572
+ this.spotMarketLastSlotCache.set(orderParams.marketIndex, slot);
2573
+ this.spotMarketLastSlotCache.set(QUOTE_SPOT_MARKET_INDEX, slot);
2574
+ return txSig;
2575
+ }
2576
+
2577
+ public async getPlaceAndTakeSpotOrderIx(
2578
+ orderParams: OptionalOrderParams,
2579
+ fulfillmentConfig?: SerumV3FulfillmentConfigAccount,
2580
+ makerInfo?: MakerInfo,
2581
+ referrerInfo?: ReferrerInfo
2582
+ ): Promise<TransactionInstruction> {
2583
+ orderParams = this.getOrderParams(orderParams, MarketType.SPOT);
2584
+ const userStatsPublicKey = await this.getUserStatsAccountPublicKey();
2585
+ const userAccountPublicKey = await this.getUserAccountPublicKey();
2586
+
2587
+ const userAccounts = [this.getUserAccount()];
2588
+ if (makerInfo !== undefined) {
2589
+ userAccounts.push(makerInfo.makerUserAccount);
2590
+ }
2591
+ const remainingAccounts = this.getRemainingAccounts({
2592
+ userAccounts,
2593
+ useMarketLastSlotCache: true,
2594
+ writableSpotMarketIndexes: [
2595
+ orderParams.marketIndex,
2596
+ QUOTE_SPOT_MARKET_INDEX,
2597
+ ],
2598
+ });
2599
+
2600
+ let makerOrderId = null;
2601
+ if (makerInfo) {
2602
+ makerOrderId = makerInfo.order.orderId;
2603
+ remainingAccounts.push({
2604
+ pubkey: makerInfo.maker,
2605
+ isSigner: false,
2606
+ isWritable: true,
2607
+ });
2608
+ remainingAccounts.push({
2609
+ pubkey: makerInfo.makerStats,
2610
+ isSigner: false,
2611
+ isWritable: true,
2612
+ });
2613
+ }
2614
+
2615
+ if (referrerInfo) {
2616
+ remainingAccounts.push({
2617
+ pubkey: referrerInfo.referrer,
2618
+ isWritable: true,
2619
+ isSigner: false,
2620
+ });
2621
+ remainingAccounts.push({
2622
+ pubkey: referrerInfo.referrerStats,
2623
+ isWritable: true,
2624
+ isSigner: false,
2625
+ });
2626
+ }
2627
+
2628
+ this.addSpotFulfillmentAccounts(
2629
+ orderParams.marketIndex,
2630
+ remainingAccounts,
2631
+ fulfillmentConfig
2632
+ );
2633
+
2634
+ return await this.program.instruction.placeAndTakeSpotOrder(
2635
+ orderParams,
2636
+ fulfillmentConfig ? fulfillmentConfig.fulfillmentType : null,
2637
+ makerOrderId,
2638
+ {
2639
+ accounts: {
2640
+ state: await this.getStatePublicKey(),
2641
+ user: userAccountPublicKey,
2642
+ userStats: userStatsPublicKey,
2643
+ authority: this.wallet.publicKey,
2644
+ },
2645
+ remainingAccounts,
2646
+ }
2647
+ );
2648
+ }
2649
+
2650
+ public async placeAndMakeSpotOrder(
2651
+ orderParams: OptionalOrderParams,
2652
+ takerInfo: TakerInfo,
2653
+ fulfillmentConfig?: SerumV3FulfillmentConfigAccount,
2654
+ referrerInfo?: ReferrerInfo
2655
+ ): Promise<TransactionSignature> {
2656
+ const { txSig, slot } = await this.txSender.send(
2657
+ wrapInTx(
2658
+ await this.getPlaceAndMakeSpotOrderIx(
2659
+ orderParams,
2660
+ takerInfo,
2661
+ fulfillmentConfig,
2662
+ referrerInfo
2663
+ )
2664
+ ),
2665
+ [],
2666
+ this.opts
2667
+ );
2668
+ this.spotMarketLastSlotCache.set(orderParams.marketIndex, slot);
2669
+ this.spotMarketLastSlotCache.set(QUOTE_SPOT_MARKET_INDEX, slot);
2670
+ return txSig;
2671
+ }
2672
+
2673
+ public async getPlaceAndMakeSpotOrderIx(
2674
+ orderParams: OptionalOrderParams,
2675
+ takerInfo: TakerInfo,
2676
+ fulfillmentConfig?: SerumV3FulfillmentConfigAccount,
2677
+ referrerInfo?: ReferrerInfo
2678
+ ): Promise<TransactionInstruction> {
2679
+ orderParams = this.getOrderParams(orderParams, MarketType.SPOT);
2680
+ const userStatsPublicKey = this.getUserStatsAccountPublicKey();
2681
+ const userAccountPublicKey = await this.getUserAccountPublicKey();
2682
+
2683
+ const remainingAccounts = this.getRemainingAccounts({
2684
+ userAccounts: [this.getUserAccount(), takerInfo.takerUserAccount],
2685
+ useMarketLastSlotCache: true,
2686
+ writableSpotMarketIndexes: [
2687
+ orderParams.marketIndex,
2688
+ QUOTE_SPOT_MARKET_INDEX,
2689
+ ],
2690
+ });
2691
+
2692
+ if (referrerInfo) {
2693
+ remainingAccounts.push({
2694
+ pubkey: referrerInfo.referrer,
2695
+ isWritable: true,
2696
+ isSigner: false,
2697
+ });
2698
+ remainingAccounts.push({
2699
+ pubkey: referrerInfo.referrerStats,
2700
+ isWritable: true,
2701
+ isSigner: false,
2702
+ });
2703
+ }
2704
+
2705
+ this.addSpotFulfillmentAccounts(
2706
+ orderParams.marketIndex,
2707
+ remainingAccounts,
2708
+ fulfillmentConfig
2709
+ );
2710
+
2711
+ const takerOrderId = takerInfo.order.orderId;
2712
+ return await this.program.instruction.placeAndMakeSpotOrder(
2713
+ orderParams,
2714
+ takerOrderId,
2715
+ fulfillmentConfig ? fulfillmentConfig.fulfillmentType : null,
2716
+ {
2717
+ accounts: {
2718
+ state: await this.getStatePublicKey(),
2719
+ user: userAccountPublicKey,
2720
+ userStats: userStatsPublicKey,
2721
+ taker: takerInfo.taker,
2722
+ takerStats: takerInfo.takerStats,
2723
+ authority: this.wallet.publicKey,
2724
+ },
2725
+ remainingAccounts,
2726
+ }
2727
+ );
2728
+ }
2729
+
2730
+ /**
2731
+ * Close an entire position. If you want to reduce a position, use the {@link openPosition} method in the opposite direction of the current position.
2732
+ * @param marketIndex
2733
+ * @returns
2734
+ */
2735
+ public async closePosition(
2736
+ marketIndex: number,
2737
+ limitPrice?: BN
2738
+ ): Promise<TransactionSignature> {
2739
+ const userPosition = this.getUser().getPerpPosition(marketIndex);
2740
+ if (!userPosition) {
2741
+ throw Error(`No position in market ${marketIndex.toString()}`);
2742
+ }
2743
+
2744
+ return await this.placeAndTakePerpOrder({
2745
+ orderType: OrderType.MARKET,
2746
+ marketIndex,
2747
+ direction: findDirectionToClose(userPosition),
2748
+ baseAssetAmount: userPosition.baseAssetAmount.abs(),
2749
+ reduceOnly: true,
2750
+ price: limitPrice,
2751
+ });
2752
+ }
2753
+
2754
+ /**
2755
+ * Modifies an open order by closing it and replacing it with a new order.
2756
+ * @param orderId: The open order to modify
2757
+ * @param newBaseAmount: The new base amount for the order. One of [newBaseAmount|newLimitPrice|newOraclePriceOffset] must be provided.
2758
+ * @param newLimitPice: The new limit price for the order. One of [newBaseAmount|newLimitPrice|newOraclePriceOffset] must be provided.
2759
+ * @param newOraclePriceOffset: The new oracle price offset for the order. One of [newBaseAmount|newLimitPrice|newOraclePriceOffset] must be provided.
2760
+ * @returns
2761
+ */
2762
+ public async modifyPerpOrder(
2763
+ orderId: number,
2764
+ newBaseAmount?: BN,
2765
+ newLimitPrice?: BN,
2766
+ newOraclePriceOffset?: number
2767
+ ): Promise<TransactionSignature> {
2768
+ if (!newBaseAmount && !newLimitPrice && !newOraclePriceOffset) {
2769
+ throw new Error(
2770
+ `Must provide newBaseAmount or newLimitPrice or newOraclePriceOffset to modify order`
2771
+ );
2772
+ }
2773
+
2774
+ const openOrder = this.getUser().getOrder(orderId);
2775
+ if (!openOrder) {
2776
+ throw new Error(`No open order with id ${orderId.toString()}`);
2777
+ }
2778
+ const cancelOrderIx = await this.getCancelOrderIx(orderId);
2779
+
2780
+ const newOrderParams: OptionalOrderParams = {
2781
+ orderType: openOrder.orderType,
2782
+ marketType: openOrder.marketType,
2783
+ direction: openOrder.direction,
2784
+ baseAssetAmount: newBaseAmount || openOrder.baseAssetAmount,
2785
+ price: newLimitPrice || openOrder.price,
2786
+ marketIndex: openOrder.marketIndex,
2787
+ reduceOnly: openOrder.reduceOnly,
2788
+ postOnly: openOrder.postOnly,
2789
+ immediateOrCancel: openOrder.immediateOrCancel,
2790
+ triggerPrice: openOrder.triggerPrice,
2791
+ triggerCondition: openOrder.triggerCondition,
2792
+ oraclePriceOffset: newOraclePriceOffset || openOrder.oraclePriceOffset,
2793
+ auctionDuration: openOrder.auctionDuration,
2794
+ maxTs: openOrder.maxTs,
2795
+ auctionStartPrice: openOrder.auctionStartPrice,
2796
+ auctionEndPrice: openOrder.auctionEndPrice,
2797
+ };
2798
+ const placeOrderIx = await this.getPlacePerpOrderIx(newOrderParams);
2799
+
2800
+ const tx = new Transaction();
2801
+ tx.add(
2802
+ ComputeBudgetProgram.requestUnits({
2803
+ units: 1_000_000,
2804
+ additionalFee: 0,
2805
+ })
2806
+ );
2807
+ tx.add(cancelOrderIx);
2808
+ tx.add(placeOrderIx);
2809
+ const { txSig, slot } = await this.txSender.send(tx, [], this.opts);
2810
+ this.perpMarketLastSlotCache.set(newOrderParams.marketIndex, slot);
2811
+ return txSig;
2812
+ }
2813
+
2814
+ public async settlePNLs(
2815
+ users: {
2816
+ settleeUserAccountPublicKey: PublicKey;
2817
+ settleeUserAccount: UserAccount;
2818
+ }[],
2819
+ marketIndex: number
2820
+ ): Promise<TransactionSignature> {
2821
+ const ixs = [];
2822
+ for (const { settleeUserAccountPublicKey, settleeUserAccount } of users) {
2823
+ ixs.push(
2824
+ await this.settlePNLIx(
2825
+ settleeUserAccountPublicKey,
2826
+ settleeUserAccount,
2827
+ marketIndex
2828
+ )
2829
+ );
2830
+ }
2831
+
2832
+ const tx = new Transaction()
2833
+ .add(
2834
+ ComputeBudgetProgram.requestUnits({
2835
+ units: 1_000_000,
2836
+ additionalFee: 0,
2837
+ })
2838
+ )
2839
+ .add(...ixs);
2840
+
2841
+ const { txSig } = await this.txSender.send(tx, [], this.opts);
2842
+ return txSig;
2843
+ }
2844
+
2845
+ public async settlePNL(
2846
+ settleeUserAccountPublicKey: PublicKey,
2847
+ settleeUserAccount: UserAccount,
2848
+ marketIndex: number
2849
+ ): Promise<TransactionSignature> {
2850
+ const { txSig } = await this.txSender.send(
2851
+ wrapInTx(
2852
+ await this.settlePNLIx(
2853
+ settleeUserAccountPublicKey,
2854
+ settleeUserAccount,
2855
+ marketIndex
2856
+ )
2857
+ ),
2858
+ [],
2859
+ this.opts
2860
+ );
2861
+ return txSig;
2862
+ }
2863
+
2864
+ public async settlePNLIx(
2865
+ settleeUserAccountPublicKey: PublicKey,
2866
+ settleeUserAccount: UserAccount,
2867
+ marketIndex: number
2868
+ ): Promise<TransactionInstruction> {
2869
+ const remainingAccounts = this.getRemainingAccounts({
2870
+ userAccounts: [settleeUserAccount],
2871
+ writablePerpMarketIndexes: [marketIndex],
2872
+ writableSpotMarketIndexes: [QUOTE_SPOT_MARKET_INDEX],
2873
+ });
2874
+
2875
+ return await this.program.instruction.settlePnl(marketIndex, {
2876
+ accounts: {
2877
+ state: await this.getStatePublicKey(),
2878
+ authority: this.wallet.publicKey,
2879
+ user: settleeUserAccountPublicKey,
2880
+ spotMarketVault: this.getQuoteSpotMarketAccount().vault,
2881
+ },
2882
+ remainingAccounts: remainingAccounts,
2883
+ });
2884
+ }
2885
+
2886
+ public async liquidatePerp(
2887
+ userAccountPublicKey: PublicKey,
2888
+ userAccount: UserAccount,
2889
+ marketIndex: number,
2890
+ maxBaseAssetAmount: BN,
2891
+ limitPrice?: BN
2892
+ ): Promise<TransactionSignature> {
2893
+ const { txSig, slot } = await this.txSender.send(
2894
+ wrapInTx(
2895
+ await this.getLiquidatePerpIx(
2896
+ userAccountPublicKey,
2897
+ userAccount,
2898
+ marketIndex,
2899
+ maxBaseAssetAmount,
2900
+ limitPrice
2901
+ )
2902
+ ),
2903
+ [],
2904
+ this.opts
2905
+ );
2906
+ this.perpMarketLastSlotCache.set(marketIndex, slot);
2907
+ return txSig;
2908
+ }
2909
+
2910
+ public async getLiquidatePerpIx(
2911
+ userAccountPublicKey: PublicKey,
2912
+ userAccount: UserAccount,
2913
+ marketIndex: number,
2914
+ maxBaseAssetAmount: BN,
2915
+ limitPrice?: BN
2916
+ ): Promise<TransactionInstruction> {
2917
+ const userStatsPublicKey = getUserStatsAccountPublicKey(
2918
+ this.program.programId,
2919
+ userAccount.authority
2920
+ );
2921
+
2922
+ const liquidatorPublicKey = await this.getUserAccountPublicKey();
2923
+ const liquidatorStatsPublicKey = this.getUserStatsAccountPublicKey();
2924
+
2925
+ const remainingAccounts = this.getRemainingAccounts({
2926
+ userAccounts: [this.getUserAccount(), userAccount],
2927
+ useMarketLastSlotCache: true,
2928
+ writablePerpMarketIndexes: [marketIndex],
2929
+ });
2930
+
2931
+ return await this.program.instruction.liquidatePerp(
2932
+ marketIndex,
2933
+ maxBaseAssetAmount,
2934
+ limitPrice ?? null,
2935
+ {
2936
+ accounts: {
2937
+ state: await this.getStatePublicKey(),
2938
+ authority: this.wallet.publicKey,
2939
+ user: userAccountPublicKey,
2940
+ userStats: userStatsPublicKey,
2941
+ liquidator: liquidatorPublicKey,
2942
+ liquidatorStats: liquidatorStatsPublicKey,
2943
+ },
2944
+ remainingAccounts: remainingAccounts,
2945
+ }
2946
+ );
2947
+ }
2948
+
2949
+ public async liquidateSpot(
2950
+ userAccountPublicKey: PublicKey,
2951
+ userAccount: UserAccount,
2952
+ assetMarketIndex: number,
2953
+ liabilityMarketIndex: number,
2954
+ maxLiabilityTransfer: BN,
2955
+ limitPrice?: BN
2956
+ ): Promise<TransactionSignature> {
2957
+ const { txSig, slot } = await this.txSender.send(
2958
+ wrapInTx(
2959
+ await this.getLiquidateSpotIx(
2960
+ userAccountPublicKey,
2961
+ userAccount,
2962
+ assetMarketIndex,
2963
+ liabilityMarketIndex,
2964
+ maxLiabilityTransfer,
2965
+ limitPrice
2966
+ )
2967
+ ),
2968
+ [],
2969
+ this.opts
2970
+ );
2971
+ this.spotMarketLastSlotCache.set(assetMarketIndex, slot);
2972
+ this.spotMarketLastSlotCache.set(liabilityMarketIndex, slot);
2973
+ return txSig;
2974
+ }
2975
+
2976
+ public async getLiquidateSpotIx(
2977
+ userAccountPublicKey: PublicKey,
2978
+ userAccount: UserAccount,
2979
+ assetMarketIndex: number,
2980
+ liabilityMarketIndex: number,
2981
+ maxLiabilityTransfer: BN,
2982
+ limitPrice?: BN
2983
+ ): Promise<TransactionInstruction> {
2984
+ const userStatsPublicKey = getUserStatsAccountPublicKey(
2985
+ this.program.programId,
2986
+ userAccount.authority
2987
+ );
2988
+
2989
+ const liquidatorPublicKey = await this.getUserAccountPublicKey();
2990
+ const liquidatorStatsPublicKey = await this.getUserStatsAccountPublicKey();
2991
+
2992
+ const remainingAccounts = this.getRemainingAccounts({
2993
+ userAccounts: [this.getUserAccount(), userAccount],
2994
+ useMarketLastSlotCache: true,
2995
+ writableSpotMarketIndexes: [liabilityMarketIndex, assetMarketIndex],
2996
+ });
2997
+
2998
+ return await this.program.instruction.liquidateSpot(
2999
+ assetMarketIndex,
3000
+ liabilityMarketIndex,
3001
+ maxLiabilityTransfer,
3002
+ limitPrice || null,
3003
+ {
3004
+ accounts: {
3005
+ state: await this.getStatePublicKey(),
3006
+ authority: this.wallet.publicKey,
3007
+ user: userAccountPublicKey,
3008
+ userStats: userStatsPublicKey,
3009
+ liquidator: liquidatorPublicKey,
3010
+ liquidatorStats: liquidatorStatsPublicKey,
3011
+ },
3012
+ remainingAccounts: remainingAccounts,
3013
+ }
3014
+ );
3015
+ }
3016
+
3017
+ public async liquidateBorrowForPerpPnl(
3018
+ userAccountPublicKey: PublicKey,
3019
+ userAccount: UserAccount,
3020
+ perpMarketIndex: number,
3021
+ liabilityMarketIndex: number,
3022
+ maxLiabilityTransfer: BN,
3023
+ limitPrice?: BN
3024
+ ): Promise<TransactionSignature> {
3025
+ const { txSig, slot } = await this.txSender.send(
3026
+ wrapInTx(
3027
+ await this.getLiquidateBorrowForPerpPnlIx(
3028
+ userAccountPublicKey,
3029
+ userAccount,
3030
+ perpMarketIndex,
3031
+ liabilityMarketIndex,
3032
+ maxLiabilityTransfer,
3033
+ limitPrice
3034
+ )
3035
+ ),
3036
+ [],
3037
+ this.opts
3038
+ );
3039
+ this.perpMarketLastSlotCache.set(perpMarketIndex, slot);
3040
+ this.spotMarketLastSlotCache.set(liabilityMarketIndex, slot);
3041
+ return txSig;
3042
+ }
3043
+
3044
+ public async getLiquidateBorrowForPerpPnlIx(
3045
+ userAccountPublicKey: PublicKey,
3046
+ userAccount: UserAccount,
3047
+ perpMarketIndex: number,
3048
+ liabilityMarketIndex: number,
3049
+ maxLiabilityTransfer: BN,
3050
+ limitPrice?: BN
3051
+ ): Promise<TransactionInstruction> {
3052
+ const userStatsPublicKey = getUserStatsAccountPublicKey(
3053
+ this.program.programId,
3054
+ userAccount.authority
3055
+ );
3056
+
3057
+ const liquidatorPublicKey = await this.getUserAccountPublicKey();
3058
+ const liquidatorStatsPublicKey = await this.getUserStatsAccountPublicKey();
3059
+
3060
+ const remainingAccounts = this.getRemainingAccounts({
3061
+ userAccounts: [this.getUserAccount(), userAccount],
3062
+ writablePerpMarketIndexes: [perpMarketIndex],
3063
+ writableSpotMarketIndexes: [liabilityMarketIndex],
3064
+ });
3065
+
3066
+ return await this.program.instruction.liquidateBorrowForPerpPnl(
3067
+ perpMarketIndex,
3068
+ liabilityMarketIndex,
3069
+ maxLiabilityTransfer,
3070
+ limitPrice || null,
3071
+ {
3072
+ accounts: {
3073
+ state: await this.getStatePublicKey(),
3074
+ authority: this.wallet.publicKey,
3075
+ user: userAccountPublicKey,
3076
+ userStats: userStatsPublicKey,
3077
+ liquidator: liquidatorPublicKey,
3078
+ liquidatorStats: liquidatorStatsPublicKey,
3079
+ },
3080
+ remainingAccounts: remainingAccounts,
3081
+ }
3082
+ );
3083
+ }
3084
+
3085
+ public async liquidatePerpPnlForDeposit(
3086
+ userAccountPublicKey: PublicKey,
3087
+ userAccount: UserAccount,
3088
+ perpMarketIndex: number,
3089
+ assetMarketIndex: number,
3090
+ maxPnlTransfer: BN,
3091
+ limitPrice?: BN
3092
+ ): Promise<TransactionSignature> {
3093
+ const { txSig, slot } = await this.txSender.send(
3094
+ wrapInTx(
3095
+ await this.getLiquidatePerpPnlForDepositIx(
3096
+ userAccountPublicKey,
3097
+ userAccount,
3098
+ perpMarketIndex,
3099
+ assetMarketIndex,
3100
+ maxPnlTransfer,
3101
+ limitPrice
3102
+ )
3103
+ ),
3104
+ [],
3105
+ this.opts
3106
+ );
3107
+ this.perpMarketLastSlotCache.set(perpMarketIndex, slot);
3108
+ this.spotMarketLastSlotCache.set(assetMarketIndex, slot);
3109
+ return txSig;
3110
+ }
3111
+
3112
+ public async getLiquidatePerpPnlForDepositIx(
3113
+ userAccountPublicKey: PublicKey,
3114
+ userAccount: UserAccount,
3115
+ perpMarketIndex: number,
3116
+ assetMarketIndex: number,
3117
+ maxPnlTransfer: BN,
3118
+ limitPrice?: BN
3119
+ ): Promise<TransactionInstruction> {
3120
+ const userStatsPublicKey = getUserStatsAccountPublicKey(
3121
+ this.program.programId,
3122
+ userAccount.authority
3123
+ );
3124
+
3125
+ const liquidatorPublicKey = await this.getUserAccountPublicKey();
3126
+ const liquidatorStatsPublicKey = await this.getUserStatsAccountPublicKey();
3127
+
3128
+ const remainingAccounts = this.getRemainingAccounts({
3129
+ userAccounts: [this.getUserAccount(), userAccount],
3130
+ writablePerpMarketIndexes: [perpMarketIndex],
3131
+ writableSpotMarketIndexes: [assetMarketIndex],
3132
+ });
3133
+
3134
+ return await this.program.instruction.liquidatePerpPnlForDeposit(
3135
+ perpMarketIndex,
3136
+ assetMarketIndex,
3137
+ maxPnlTransfer,
3138
+ limitPrice || null,
3139
+ {
3140
+ accounts: {
3141
+ state: await this.getStatePublicKey(),
3142
+ authority: this.wallet.publicKey,
3143
+ user: userAccountPublicKey,
3144
+ userStats: userStatsPublicKey,
3145
+ liquidator: liquidatorPublicKey,
3146
+ liquidatorStats: liquidatorStatsPublicKey,
3147
+ },
3148
+ remainingAccounts: remainingAccounts,
3149
+ }
3150
+ );
3151
+ }
3152
+
3153
+ public async resolvePerpBankruptcy(
3154
+ userAccountPublicKey: PublicKey,
3155
+ userAccount: UserAccount,
3156
+ marketIndex: number
3157
+ ): Promise<TransactionSignature> {
3158
+ const { txSig } = await this.txSender.send(
3159
+ wrapInTx(
3160
+ await this.getResolvePerpBankruptcyIx(
3161
+ userAccountPublicKey,
3162
+ userAccount,
3163
+ marketIndex
3164
+ )
3165
+ ),
3166
+ [],
3167
+ this.opts
3168
+ );
3169
+ return txSig;
3170
+ }
3171
+
3172
+ public async getResolvePerpBankruptcyIx(
3173
+ userAccountPublicKey: PublicKey,
3174
+ userAccount: UserAccount,
3175
+ marketIndex: number
3176
+ ): Promise<TransactionInstruction> {
3177
+ const userStatsPublicKey = getUserStatsAccountPublicKey(
3178
+ this.program.programId,
3179
+ userAccount.authority
3180
+ );
3181
+
3182
+ const liquidatorPublicKey = await this.getUserAccountPublicKey();
3183
+ const liquidatorStatsPublicKey = await this.getUserStatsAccountPublicKey();
3184
+
3185
+ const remainingAccounts = this.getRemainingAccounts({
3186
+ userAccounts: [this.getUserAccount(), userAccount],
3187
+ writablePerpMarketIndexes: [marketIndex],
3188
+ writableSpotMarketIndexes: [QUOTE_SPOT_MARKET_INDEX],
3189
+ });
3190
+
3191
+ const spotMarket = this.getSpotMarketAccount(marketIndex);
3192
+
3193
+ return await this.program.instruction.resolvePerpBankruptcy(
3194
+ QUOTE_SPOT_MARKET_INDEX,
3195
+ marketIndex,
3196
+ {
3197
+ accounts: {
3198
+ state: await this.getStatePublicKey(),
3199
+ authority: this.wallet.publicKey,
3200
+ user: userAccountPublicKey,
3201
+ userStats: userStatsPublicKey,
3202
+ liquidator: liquidatorPublicKey,
3203
+ liquidatorStats: liquidatorStatsPublicKey,
3204
+ spotMarketVault: spotMarket.vault,
3205
+ insuranceFundVault: spotMarket.insuranceFund.vault,
3206
+ driftSigner: this.getSignerPublicKey(),
3207
+ tokenProgram: TOKEN_PROGRAM_ID,
3208
+ },
3209
+ remainingAccounts: remainingAccounts,
3210
+ }
3211
+ );
3212
+ }
3213
+
3214
+ public async resolveSpotBankruptcy(
3215
+ userAccountPublicKey: PublicKey,
3216
+ userAccount: UserAccount,
3217
+ marketIndex: number
3218
+ ): Promise<TransactionSignature> {
3219
+ const { txSig } = await this.txSender.send(
3220
+ wrapInTx(
3221
+ await this.getResolveSpotBankruptcyIx(
3222
+ userAccountPublicKey,
3223
+ userAccount,
3224
+ marketIndex
3225
+ )
3226
+ ),
3227
+ [],
3228
+ this.opts
3229
+ );
3230
+ return txSig;
3231
+ }
3232
+
3233
+ public async getResolveSpotBankruptcyIx(
3234
+ userAccountPublicKey: PublicKey,
3235
+ userAccount: UserAccount,
3236
+ marketIndex: number
3237
+ ): Promise<TransactionInstruction> {
3238
+ const userStatsPublicKey = getUserStatsAccountPublicKey(
3239
+ this.program.programId,
3240
+ userAccount.authority
3241
+ );
3242
+
3243
+ const liquidatorPublicKey = await this.getUserAccountPublicKey();
3244
+ const liquidatorStatsPublicKey = await this.getUserStatsAccountPublicKey();
3245
+
3246
+ const remainingAccounts = this.getRemainingAccounts({
3247
+ userAccounts: [this.getUserAccount(), userAccount],
3248
+ writableSpotMarketIndexes: [marketIndex],
3249
+ });
3250
+
3251
+ const spotMarket = this.getSpotMarketAccount(marketIndex);
3252
+
3253
+ return await this.program.instruction.resolveSpotBankruptcy(marketIndex, {
3254
+ accounts: {
3255
+ state: await this.getStatePublicKey(),
3256
+ authority: this.wallet.publicKey,
3257
+ user: userAccountPublicKey,
3258
+ userStats: userStatsPublicKey,
3259
+ liquidatorStats: liquidatorStatsPublicKey,
3260
+ liquidator: liquidatorPublicKey,
3261
+ spotMarketVault: spotMarket.vault,
3262
+ insuranceFundVault: spotMarket.insuranceFund.vault,
3263
+ driftSigner: this.getSignerPublicKey(),
3264
+ tokenProgram: TOKEN_PROGRAM_ID,
3265
+ },
3266
+ remainingAccounts: remainingAccounts,
3267
+ });
3268
+ }
3269
+
3270
+ public async updateFundingRate(
3271
+ perpMarketIndex: number,
3272
+ oracle: PublicKey
3273
+ ): Promise<TransactionSignature> {
3274
+ const { txSig } = await this.txSender.send(
3275
+ wrapInTx(await this.getUpdateFundingRateIx(perpMarketIndex, oracle)),
3276
+ [],
3277
+ this.opts
3278
+ );
3279
+ return txSig;
3280
+ }
3281
+
3282
+ public async getUpdateFundingRateIx(
3283
+ perpMarketIndex: number,
3284
+ oracle: PublicKey
3285
+ ): Promise<TransactionInstruction> {
3286
+ const perpMarketPublicKey = await getPerpMarketPublicKey(
3287
+ this.program.programId,
3288
+ perpMarketIndex
3289
+ );
3290
+ return await this.program.instruction.updateFundingRate(perpMarketIndex, {
3291
+ accounts: {
3292
+ state: await this.getStatePublicKey(),
3293
+ perpMarket: perpMarketPublicKey,
3294
+ oracle: oracle,
3295
+ },
3296
+ });
3297
+ }
3298
+
3299
+ public async settleFundingPayment(
3300
+ userAccountPublicKey: PublicKey
3301
+ ): Promise<TransactionSignature> {
3302
+ const { txSig } = await this.txSender.send(
3303
+ wrapInTx(await this.getSettleFundingPaymentIx(userAccountPublicKey)),
3304
+ [],
3305
+ this.opts
3306
+ );
3307
+ return txSig;
3308
+ }
3309
+
3310
+ public async getSettleFundingPaymentIx(
3311
+ userAccountPublicKey: PublicKey
3312
+ ): Promise<TransactionInstruction> {
3313
+ const userAccount = (await this.program.account.user.fetch(
3314
+ userAccountPublicKey
3315
+ )) as UserAccount;
3316
+
3317
+ const writablePerpMarketIndexes = [];
3318
+ for (const position of userAccount.perpPositions) {
3319
+ if (!positionIsAvailable(position)) {
3320
+ writablePerpMarketIndexes.push(position.marketIndex);
3321
+ }
3322
+ }
3323
+
3324
+ const remainingAccounts = this.getRemainingAccounts({
3325
+ userAccounts: [userAccount],
3326
+ writablePerpMarketIndexes,
3327
+ });
3328
+
3329
+ return await this.program.instruction.settleFundingPayment({
3330
+ accounts: {
3331
+ state: await this.getStatePublicKey(),
3332
+ user: userAccountPublicKey,
3333
+ },
3334
+ remainingAccounts,
3335
+ });
3336
+ }
3337
+
3338
+ public triggerEvent(eventName: keyof DriftClientAccountEvents, data?: any) {
3339
+ this.eventEmitter.emit(eventName, data);
3340
+ }
3341
+
3342
+ public getOracleDataForPerpMarket(marketIndex: number): OraclePriceData {
3343
+ const oracleKey = this.getPerpMarketAccount(marketIndex).amm.oracle;
3344
+ const oracleData = this.getOraclePriceDataAndSlot(oracleKey).data;
3345
+
3346
+ return oracleData;
3347
+ }
3348
+
3349
+ public getOracleDataForSpotMarket(marketIndex: number): OraclePriceData {
3350
+ const oracleKey = this.getSpotMarketAccount(marketIndex).oracle;
3351
+ const oracleData = this.getOraclePriceDataAndSlot(oracleKey).data;
3352
+
3353
+ return oracleData;
3354
+ }
3355
+
3356
+ public async initializeInsuranceFundStake(
3357
+ marketIndex: number
3358
+ ): Promise<TransactionSignature> {
3359
+ const { txSig } = await this.txSender.send(
3360
+ wrapInTx(await this.getInitializeInsuranceFundStakeIx(marketIndex)),
3361
+ [],
3362
+ this.opts
3363
+ );
3364
+ return txSig;
3365
+ }
3366
+
3367
+ public async getInitializeInsuranceFundStakeIx(
3368
+ marketIndex: number
3369
+ ): Promise<TransactionInstruction> {
3370
+ const ifStakeAccountPublicKey = getInsuranceFundStakeAccountPublicKey(
3371
+ this.program.programId,
3372
+ this.wallet.publicKey,
3373
+ marketIndex
3374
+ );
3375
+
3376
+ return await this.program.instruction.initializeInsuranceFundStake(
3377
+ marketIndex,
3378
+ {
3379
+ accounts: {
3380
+ insuranceFundStake: ifStakeAccountPublicKey,
3381
+ spotMarket: this.getSpotMarketAccount(marketIndex).pubkey,
3382
+ userStats: this.getUserStatsAccountPublicKey(),
3383
+ authority: this.wallet.publicKey,
3384
+ payer: this.wallet.publicKey,
3385
+ rent: anchor.web3.SYSVAR_RENT_PUBKEY,
3386
+ systemProgram: anchor.web3.SystemProgram.programId,
3387
+ state: await this.getStatePublicKey(),
3388
+ },
3389
+ }
3390
+ );
3391
+ }
3392
+
3393
+ public async addInsuranceFundStake(
3394
+ marketIndex: number,
3395
+ amount: BN,
3396
+ collateralAccountPublicKey: PublicKey
3397
+ ): Promise<TransactionSignature> {
3398
+ const spotMarket = this.getSpotMarketAccount(marketIndex);
3399
+ const ifStakeAccountPublicKey = getInsuranceFundStakeAccountPublicKey(
3400
+ this.program.programId,
3401
+ this.wallet.publicKey,
3402
+ marketIndex
3403
+ );
3404
+
3405
+ const remainingAccounts = this.getRemainingAccounts({
3406
+ userAccounts: [this.getUserAccount()],
3407
+ useMarketLastSlotCache: true,
3408
+ writableSpotMarketIndexes: [marketIndex],
3409
+ });
3410
+
3411
+ return await this.program.rpc.addInsuranceFundStake(marketIndex, amount, {
3412
+ accounts: {
3413
+ state: await this.getStatePublicKey(),
3414
+ spotMarket: spotMarket.pubkey,
3415
+ insuranceFundStake: ifStakeAccountPublicKey,
3416
+ userStats: this.getUserStatsAccountPublicKey(),
3417
+ authority: this.wallet.publicKey,
3418
+ spotMarketVault: spotMarket.vault,
3419
+ insuranceFundVault: spotMarket.insuranceFund.vault,
3420
+ driftSigner: this.getSignerPublicKey(),
3421
+ userTokenAccount: collateralAccountPublicKey,
3422
+ tokenProgram: TOKEN_PROGRAM_ID,
3423
+ },
3424
+ remainingAccounts,
3425
+ });
3426
+ }
3427
+
3428
+ public async requestRemoveInsuranceFundStake(
3429
+ marketIndex: number,
3430
+ amount: BN
3431
+ ): Promise<TransactionSignature> {
3432
+ const spotMarketAccount = this.getSpotMarketAccount(marketIndex);
3433
+ const ifStakeAccountPublicKey = getInsuranceFundStakeAccountPublicKey(
3434
+ this.program.programId,
3435
+ this.wallet.publicKey,
3436
+ marketIndex
3437
+ );
3438
+
3439
+ const remainingAccounts = this.getRemainingAccounts({
3440
+ userAccounts: [this.getUserAccount()],
3441
+ useMarketLastSlotCache: true,
3442
+ writableSpotMarketIndexes: [marketIndex],
3443
+ });
3444
+
3445
+ return await this.program.rpc.requestRemoveInsuranceFundStake(
3446
+ marketIndex,
3447
+ amount,
3448
+ {
3449
+ accounts: {
3450
+ state: await this.getStatePublicKey(),
3451
+ spotMarket: spotMarketAccount.pubkey,
3452
+ insuranceFundStake: ifStakeAccountPublicKey,
3453
+ userStats: this.getUserStatsAccountPublicKey(),
3454
+ authority: this.wallet.publicKey,
3455
+ insuranceFundVault: spotMarketAccount.insuranceFund.vault,
3456
+ },
3457
+ remainingAccounts,
3458
+ }
3459
+ );
3460
+ }
3461
+
3462
+ public async cancelRequestRemoveInsuranceFundStake(
3463
+ marketIndex: number
3464
+ ): Promise<TransactionSignature> {
3465
+ const spotMarketAccount = this.getSpotMarketAccount(marketIndex);
3466
+ const ifStakeAccountPublicKey = getInsuranceFundStakeAccountPublicKey(
3467
+ this.program.programId,
3468
+ this.wallet.publicKey,
3469
+ marketIndex
3470
+ );
3471
+
3472
+ const remainingAccounts = this.getRemainingAccounts({
3473
+ userAccounts: [this.getUserAccount()],
3474
+ useMarketLastSlotCache: true,
3475
+ writableSpotMarketIndexes: [marketIndex],
3476
+ });
3477
+
3478
+ return await this.program.rpc.cancelRequestRemoveInsuranceFundStake(
3479
+ marketIndex,
3480
+ {
3481
+ accounts: {
3482
+ state: await this.getStatePublicKey(),
3483
+ spotMarket: spotMarketAccount.pubkey,
3484
+ insuranceFundStake: ifStakeAccountPublicKey,
3485
+ userStats: this.getUserStatsAccountPublicKey(),
3486
+ authority: this.wallet.publicKey,
3487
+ insuranceFundVault: spotMarketAccount.insuranceFund.vault,
3488
+ },
3489
+ remainingAccounts,
3490
+ }
3491
+ );
3492
+ }
3493
+
3494
+ public async removeInsuranceFundStake(
3495
+ marketIndex: number,
3496
+ collateralAccountPublicKey: PublicKey
3497
+ ): Promise<TransactionSignature> {
3498
+ const spotMarketAccount = this.getSpotMarketAccount(marketIndex);
3499
+ const ifStakeAccountPublicKey = getInsuranceFundStakeAccountPublicKey(
3500
+ this.program.programId,
3501
+ this.wallet.publicKey,
3502
+ marketIndex
3503
+ );
3504
+
3505
+ const remainingAccounts = this.getRemainingAccounts({
3506
+ userAccounts: [this.getUserAccount()],
3507
+ useMarketLastSlotCache: true,
3508
+ writableSpotMarketIndexes: [marketIndex],
3509
+ });
3510
+
3511
+ return await this.program.rpc.removeInsuranceFundStake(marketIndex, {
3512
+ accounts: {
3513
+ state: await this.getStatePublicKey(),
3514
+ spotMarket: spotMarketAccount.pubkey,
3515
+ insuranceFundStake: ifStakeAccountPublicKey,
3516
+ userStats: this.getUserStatsAccountPublicKey(),
3517
+ authority: this.wallet.publicKey,
3518
+ insuranceFundVault: spotMarketAccount.insuranceFund.vault,
3519
+ driftSigner: this.getSignerPublicKey(),
3520
+ userTokenAccount: collateralAccountPublicKey,
3521
+ tokenProgram: TOKEN_PROGRAM_ID,
3522
+ },
3523
+ remainingAccounts,
3524
+ });
3525
+ }
3526
+
3527
+ public async settleRevenueToInsuranceFund(
3528
+ marketIndex: number
3529
+ ): Promise<TransactionSignature> {
3530
+ const spotMarketAccount = this.getSpotMarketAccount(marketIndex);
3531
+
3532
+ const remainingAccounts = this.getRemainingAccounts({
3533
+ userAccounts: [this.getUserAccount()],
3534
+ useMarketLastSlotCache: true,
3535
+ writableSpotMarketIndexes: [marketIndex],
3536
+ });
3537
+
3538
+ return await this.program.rpc.settleRevenueToInsuranceFund(marketIndex, {
3539
+ accounts: {
3540
+ state: await this.getStatePublicKey(),
3541
+ spotMarket: spotMarketAccount.pubkey,
3542
+ spotMarketVault: spotMarketAccount.vault,
3543
+ driftSigner: this.getSignerPublicKey(),
3544
+ insuranceFundVault: spotMarketAccount.insuranceFund.vault,
3545
+ tokenProgram: TOKEN_PROGRAM_ID,
3546
+ },
3547
+ remainingAccounts,
3548
+ });
3549
+ }
3550
+
3551
+ public async resolvePerpPnlDeficit(
3552
+ spotMarketIndex: number,
3553
+ perpMarketIndex: number
3554
+ ): Promise<TransactionSignature> {
3555
+ const { txSig } = await this.txSender.send(
3556
+ wrapInTx(
3557
+ await this.getResolvePerpPnlDeficitIx(spotMarketIndex, perpMarketIndex)
3558
+ ),
3559
+ [],
3560
+ this.opts
3561
+ );
3562
+ return txSig;
3563
+ }
3564
+
3565
+ public async getResolvePerpPnlDeficitIx(
3566
+ spotMarketIndex: number,
3567
+ perpMarketIndex: number
3568
+ ): Promise<TransactionInstruction> {
3569
+ const remainingAccounts = this.getRemainingAccounts({
3570
+ userAccounts: [this.getUserAccount()],
3571
+ useMarketLastSlotCache: true,
3572
+ writablePerpMarketIndexes: [perpMarketIndex],
3573
+ writableSpotMarketIndexes: [spotMarketIndex],
3574
+ });
3575
+
3576
+ const spotMarket = this.getSpotMarketAccount(spotMarketIndex);
3577
+
3578
+ return await this.program.instruction.resolvePerpPnlDeficit(
3579
+ spotMarketIndex,
3580
+ perpMarketIndex,
3581
+ {
3582
+ accounts: {
3583
+ state: await this.getStatePublicKey(),
3584
+ authority: this.wallet.publicKey,
3585
+ spotMarketVault: spotMarket.vault,
3586
+ insuranceFundVault: spotMarket.insuranceFund.vault,
3587
+ driftSigner: this.getSignerPublicKey(),
3588
+ tokenProgram: TOKEN_PROGRAM_ID,
3589
+ },
3590
+ remainingAccounts: remainingAccounts,
3591
+ }
3592
+ );
3593
+ }
3594
+ }