@drift-labs/sdk 0.2.0-master.4 → 0.2.0-master.40

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