@drift-labs/sdk 0.2.0-master.3 → 0.2.0-master.30

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