@drift-labs/sdk 0.1.36-master.7 → 0.2.0-master.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (197) hide show
  1. package/lib/accounts/bulkAccountLoader.d.ts +6 -6
  2. package/lib/accounts/bulkAccountLoader.js +81 -95
  3. package/lib/accounts/bulkUserSubscription.js +13 -57
  4. package/lib/accounts/fetch.d.ts +4 -0
  5. package/lib/accounts/fetch.js +18 -0
  6. package/lib/accounts/pollingClearingHouseAccountSubscriber.d.ts +34 -38
  7. package/lib/accounts/pollingClearingHouseAccountSubscriber.js +224 -224
  8. package/lib/accounts/pollingOracleSubscriber.d.ts +3 -3
  9. package/lib/accounts/pollingOracleSubscriber.js +37 -49
  10. package/lib/accounts/pollingTokenAccountSubscriber.d.ts +3 -3
  11. package/lib/accounts/pollingTokenAccountSubscriber.js +35 -50
  12. package/lib/accounts/pollingUserAccountSubscriber.d.ts +7 -13
  13. package/lib/accounts/pollingUserAccountSubscriber.js +71 -134
  14. package/lib/accounts/types.d.ts +34 -41
  15. package/lib/accounts/webSocketAccountSubscriber.d.ts +6 -4
  16. package/lib/accounts/webSocketAccountSubscriber.js +39 -35
  17. package/lib/accounts/webSocketClearingHouseAccountSubscriber.d.ts +33 -28
  18. package/lib/accounts/webSocketClearingHouseAccountSubscriber.js +153 -185
  19. package/lib/accounts/webSocketUserAccountSubscriber.d.ts +5 -11
  20. package/lib/accounts/webSocketUserAccountSubscriber.js +22 -67
  21. package/lib/addresses/marketAddresses.d.ts +4 -0
  22. package/lib/addresses/marketAddresses.js +15 -0
  23. package/lib/addresses/pda.d.ts +12 -0
  24. package/lib/addresses/pda.js +83 -0
  25. package/lib/admin.d.ts +8 -17
  26. package/lib/admin.js +366 -558
  27. package/lib/clearingHouse.d.ts +84 -109
  28. package/lib/clearingHouse.js +778 -899
  29. package/lib/clearingHouseConfig.d.ts +34 -0
  30. package/lib/clearingHouseConfig.js +2 -0
  31. package/lib/clearingHouseUser.d.ts +19 -22
  32. package/lib/clearingHouseUser.js +155 -141
  33. package/lib/clearingHouseUserConfig.d.ts +14 -0
  34. package/lib/clearingHouseUserConfig.js +2 -0
  35. package/lib/config.d.ts +12 -0
  36. package/lib/config.js +35 -4
  37. package/lib/constants/banks.d.ts +16 -0
  38. package/lib/constants/banks.js +34 -0
  39. package/lib/constants/markets.d.ts +8 -3
  40. package/lib/constants/markets.js +13 -206
  41. package/lib/constants/numericConstants.d.ts +16 -0
  42. package/lib/constants/numericConstants.js +22 -6
  43. package/lib/events/eventList.d.ts +22 -0
  44. package/lib/events/eventList.js +77 -0
  45. package/lib/events/eventSubscriber.d.ts +34 -0
  46. package/lib/events/eventSubscriber.js +126 -0
  47. package/lib/events/fetchLogs.d.ts +13 -0
  48. package/lib/events/fetchLogs.js +39 -0
  49. package/lib/events/pollingLogProvider.d.ts +15 -0
  50. package/lib/events/pollingLogProvider.js +53 -0
  51. package/lib/events/sort.d.ts +2 -0
  52. package/lib/events/sort.js +44 -0
  53. package/lib/events/txEventCache.d.ts +24 -0
  54. package/lib/events/txEventCache.js +71 -0
  55. package/lib/events/types.d.ts +49 -0
  56. package/lib/events/types.js +20 -0
  57. package/lib/events/webSocketLogProvider.d.ts +12 -0
  58. package/lib/events/webSocketLogProvider.js +30 -0
  59. package/lib/examples/makeTradeExample.js +26 -27
  60. package/lib/factory/bigNum.d.ts +112 -0
  61. package/lib/factory/bigNum.js +356 -0
  62. package/lib/factory/oracleClient.d.ts +1 -2
  63. package/lib/factory/oracleClient.js +6 -2
  64. package/lib/idl/clearing_house.json +2213 -2951
  65. package/lib/index.d.ts +12 -4
  66. package/lib/index.js +12 -4
  67. package/lib/math/amm.d.ts +19 -29
  68. package/lib/math/amm.js +129 -179
  69. package/lib/math/auction.d.ts +5 -0
  70. package/lib/math/auction.js +39 -0
  71. package/lib/math/bankBalance.d.ts +9 -0
  72. package/lib/math/bankBalance.js +75 -0
  73. package/lib/math/conversion.d.ts +0 -1
  74. package/lib/math/conversion.js +1 -5
  75. package/lib/math/funding.d.ts +6 -6
  76. package/lib/math/funding.js +158 -175
  77. package/lib/math/market.d.ts +6 -6
  78. package/lib/math/market.js +10 -9
  79. package/lib/math/orders.d.ts +5 -0
  80. package/lib/math/orders.js +31 -1
  81. package/lib/math/position.d.ts +7 -5
  82. package/lib/math/position.js +27 -27
  83. package/lib/math/repeg.d.ts +22 -0
  84. package/lib/math/repeg.js +128 -0
  85. package/lib/math/trade.d.ts +5 -4
  86. package/lib/math/trade.js +29 -21
  87. package/lib/mockUSDCFaucet.js +87 -116
  88. package/lib/oracles/oracleClientCache.d.ts +8 -0
  89. package/lib/oracles/oracleClientCache.js +19 -0
  90. package/lib/oracles/pythClient.d.ts +3 -5
  91. package/lib/oracles/pythClient.js +12 -31
  92. package/lib/oracles/quoteAssetOracleClient.d.ts +9 -0
  93. package/lib/oracles/quoteAssetOracleClient.js +21 -0
  94. package/lib/oracles/switchboardClient.d.ts +3 -5
  95. package/lib/oracles/switchboardClient.js +29 -50
  96. package/lib/oracles/types.d.ts +6 -1
  97. package/lib/orders.d.ts +6 -6
  98. package/lib/orders.js +10 -9
  99. package/lib/slot/SlotSubscriber.d.ts +12 -0
  100. package/lib/slot/SlotSubscriber.js +23 -0
  101. package/lib/tx/retryTxSender.d.ts +2 -2
  102. package/lib/tx/retryTxSender.js +108 -123
  103. package/lib/tx/types.d.ts +5 -1
  104. package/lib/tx/utils.d.ts +1 -1
  105. package/lib/tx/utils.js +11 -2
  106. package/lib/types.d.ts +105 -109
  107. package/lib/types.js +13 -1
  108. package/lib/userName.d.ts +4 -0
  109. package/lib/userName.js +20 -0
  110. package/lib/util/computeUnits.js +10 -21
  111. package/lib/util/tps.js +11 -22
  112. package/lib/wallet.js +7 -20
  113. package/package.json +10 -3
  114. package/src/accounts/bulkAccountLoader.ts +21 -15
  115. package/src/accounts/bulkUserSubscription.ts +1 -45
  116. package/src/accounts/fetch.ts +33 -0
  117. package/src/accounts/pollingClearingHouseAccountSubscriber.ts +249 -214
  118. package/src/accounts/pollingOracleSubscriber.ts +16 -8
  119. package/src/accounts/pollingTokenAccountSubscriber.ts +11 -8
  120. package/src/accounts/pollingUserAccountSubscriber.ts +21 -86
  121. package/src/accounts/types.ts +41 -70
  122. package/src/accounts/webSocketAccountSubscriber.ts +33 -16
  123. package/src/accounts/webSocketClearingHouseAccountSubscriber.ts +190 -240
  124. package/src/accounts/webSocketUserAccountSubscriber.ts +11 -71
  125. package/src/addresses/marketAddresses.ts +18 -0
  126. package/src/addresses/pda.ts +118 -0
  127. package/src/admin.ts +205 -346
  128. package/src/clearingHouse.ts +918 -961
  129. package/src/clearingHouseConfig.ts +37 -0
  130. package/src/clearingHouseUser.ts +275 -185
  131. package/src/clearingHouseUserConfig.ts +18 -0
  132. package/src/config.ts +54 -1
  133. package/src/constants/banks.ts +43 -0
  134. package/src/constants/markets.ts +16 -207
  135. package/src/constants/numericConstants.ts +33 -5
  136. package/src/events/eventList.ts +94 -0
  137. package/src/events/eventSubscriber.ts +194 -0
  138. package/src/events/fetchLogs.ts +80 -0
  139. package/src/events/pollingLogProvider.ts +79 -0
  140. package/src/events/sort.ts +65 -0
  141. package/src/events/txEventCache.ts +74 -0
  142. package/src/events/types.ts +98 -0
  143. package/src/events/webSocketLogProvider.ts +38 -0
  144. package/src/examples/makeTradeExample.ts +20 -11
  145. package/src/factory/bigNum.ts +507 -0
  146. package/src/factory/oracleClient.ts +7 -4
  147. package/src/idl/clearing_house.json +2213 -2951
  148. package/src/index.ts +12 -4
  149. package/src/math/amm.ts +229 -245
  150. package/src/math/auction.ts +39 -0
  151. package/src/math/bankBalance.ts +112 -0
  152. package/src/math/conversion.ts +1 -11
  153. package/src/math/funding.ts +12 -9
  154. package/src/math/market.ts +37 -30
  155. package/src/math/orders.ts +38 -0
  156. package/src/math/position.ts +48 -35
  157. package/src/math/repeg.ts +175 -0
  158. package/src/math/trade.ts +45 -35
  159. package/src/math/utils.js +27 -0
  160. package/src/math/utils.js.map +1 -0
  161. package/src/oracles/oracleClientCache.ts +20 -0
  162. package/src/oracles/pythClient.ts +4 -11
  163. package/src/oracles/quoteAssetOracleClient.ts +25 -0
  164. package/src/oracles/switchboardClient.ts +11 -24
  165. package/src/oracles/types.ts +7 -1
  166. package/src/orders.ts +35 -20
  167. package/src/slot/SlotSubscriber.ts +32 -0
  168. package/src/tx/retryTxSender.ts +6 -4
  169. package/src/tx/types.ts +6 -1
  170. package/src/tx/utils.ts +22 -3
  171. package/src/types.ts +108 -122
  172. package/src/userName.ts +20 -0
  173. package/src/util/computeUnits.js +17 -0
  174. package/src/util/computeUnits.js.map +1 -0
  175. package/tests/bn/test.ts +255 -0
  176. package/tsconfig.json +12 -12
  177. package/lib/addresses.d.ts +0 -10
  178. package/lib/addresses.js +0 -93
  179. package/lib/constants/accounts.d.ts +0 -15
  180. package/lib/constants/accounts.js +0 -18
  181. package/lib/factory/clearingHouse.d.ts +0 -35
  182. package/lib/factory/clearingHouse.js +0 -81
  183. package/lib/factory/clearingHouseUser.d.ts +0 -19
  184. package/lib/factory/clearingHouseUser.js +0 -34
  185. package/lib/math/insuranceFund.d.ts +0 -15
  186. package/lib/math/insuranceFund.js +0 -33
  187. package/lib/settlement.d.ts +0 -4
  188. package/lib/settlement.js +0 -10
  189. package/lib/tx/defaultTxSender.d.ts +0 -8
  190. package/lib/tx/defaultTxSender.js +0 -12
  191. package/src/addresses.ts +0 -82
  192. package/src/constants/accounts.ts +0 -26
  193. package/src/factory/clearingHouse.ts +0 -173
  194. package/src/factory/clearingHouseUser.ts +0 -73
  195. package/src/math/insuranceFund.ts +0 -29
  196. package/src/settlement.ts +0 -9
  197. package/src/tx/defaultTxSender.ts +0 -24
@@ -1,28 +1,16 @@
1
1
  import { AnchorProvider, BN, Idl, Program } from '@project-serum/anchor';
2
+ import { TOKEN_PROGRAM_ID } from '@solana/spl-token';
2
3
  import {
3
- ASSOCIATED_TOKEN_PROGRAM_ID,
4
- Token,
5
- TOKEN_PROGRAM_ID,
6
- } from '@solana/spl-token';
7
- import {
8
- MarketsAccount,
9
4
  StateAccount,
10
- DepositHistoryAccount,
11
- FundingPaymentHistoryAccount,
12
- FundingRateHistoryAccount,
13
5
  IWallet,
14
- LiquidationHistoryAccount,
15
6
  PositionDirection,
16
- TradeHistoryAccount,
17
7
  UserAccount,
18
- Market,
19
- OrderHistoryAccount,
20
- OrderStateAccount,
8
+ MarketAccount,
21
9
  OrderParams,
22
10
  Order,
23
- ExtendedCurveHistoryAccount,
24
- UserPositionsAccount,
25
- SettlementStateAccount,
11
+ BankAccount,
12
+ UserBankBalance,
13
+ MakerInfo,
26
14
  } from './types';
27
15
  import * as anchor from '@project-serum/anchor';
28
16
  import clearingHouseIDL from './idl/clearing_house.json';
@@ -31,10 +19,10 @@ import {
31
19
  Connection,
32
20
  PublicKey,
33
21
  TransactionSignature,
34
- Keypair,
35
22
  ConfirmOptions,
36
23
  Transaction,
37
24
  TransactionInstruction,
25
+ AccountMeta,
38
26
  } from '@solana/web3.js';
39
27
 
40
28
  import { MockUSDCFaucet } from './mockUSDCFaucet';
@@ -42,40 +30,34 @@ import { EventEmitter } from 'events';
42
30
  import StrictEventEmitter from 'strict-event-emitter-types';
43
31
  import {
44
32
  getClearingHouseStateAccountPublicKey,
45
- getOrderStateAccountPublicKey,
46
- getSettlementStatePublicKey,
33
+ getMarketPublicKey,
47
34
  getUserAccountPublicKey,
48
- getUserAccountPublicKeyAndNonce,
49
- getUserOrdersAccountPublicKey,
50
- getUserOrdersAccountPublicKeyAndNonce,
51
- } from './addresses';
35
+ getUserAccountPublicKeySync,
36
+ } from './addresses/pda';
52
37
  import {
53
38
  ClearingHouseAccountSubscriber,
54
39
  ClearingHouseAccountEvents,
55
- ClearingHouseAccountTypes,
40
+ DataAndSlot,
56
41
  } from './accounts/types';
57
42
  import { TxSender } from './tx/types';
58
43
  import { wrapInTx } from './tx/utils';
59
- import {
60
- getClearingHouse,
61
- getPollingClearingHouseConfig,
62
- getWebSocketClearingHouseConfig,
63
- } from './factory/clearingHouse';
64
- import { ZERO } from './constants/numericConstants';
65
- import { BulkAccountLoader } from './accounts/bulkAccountLoader';
44
+ import { QUOTE_ASSET_BANK_INDEX, ZERO } from './constants/numericConstants';
45
+ import { findDirectionToClose, positionIsAvailable } from './math/position';
46
+ import { getTokenAmount } from './math/bankBalance';
47
+ import { DEFAULT_USER_NAME, encodeName } from './userName';
48
+ import { OraclePriceData } from './oracles/types';
49
+ import { ClearingHouseConfig } from './clearingHouseConfig';
50
+ import { PollingClearingHouseAccountSubscriber } from './accounts/pollingClearingHouseAccountSubscriber';
51
+ import { WebSocketClearingHouseAccountSubscriber } from './accounts/webSocketClearingHouseAccountSubscriber';
52
+ import { RetryTxSender } from './tx/retryTxSender';
66
53
  import { ClearingHouseUser } from './clearingHouseUser';
67
- import {
68
- getClearingHouseUser,
69
- getPollingClearingHouseUserConfig,
70
- } from './factory/clearingHouseUser';
71
- import { bulkPollingUserSubscribe } from './accounts/bulkUserSubscription';
54
+ import { ClearingHouseUserAccountSubscriptionConfig } from './clearingHouseUserConfig';
55
+ import { getMarketOrderParams } from './orderParams';
56
+ import { getMarketsBanksAndOraclesForSubscription } from './config';
72
57
 
73
58
  /**
74
59
  * # ClearingHouse
75
60
  * This class is the main way to interact with Drift Protocol. It allows you to subscribe to the various accounts where the Market's state is stored, as well as: opening positions, liquidating, settling funding, depositing & withdrawing, and more.
76
- *
77
- * The default way to construct a ClearingHouse instance is using the {@link from} method. This will create an instance using the static {@link WebSocketClearingHouseAccountSubscriber}, which will use a websocket for each state account subscription.
78
- * Alternatively, if you want to implement your own method of subscribing to the state accounts on the blockchain, you can implement a {@link ClearingHouseAccountSubscriber} and use it in the {@link ClearingHouse.constructor}
79
61
  */
80
62
  export class ClearingHouse {
81
63
  connection: Connection;
@@ -83,10 +65,14 @@ export class ClearingHouse {
83
65
  public program: Program;
84
66
  provider: AnchorProvider;
85
67
  opts?: ConfirmOptions;
68
+ users = new Map<number, ClearingHouseUser>();
69
+ activeUserId: number;
70
+ userAccountSubscriptionConfig: ClearingHouseUserAccountSubscriptionConfig;
86
71
  accountSubscriber: ClearingHouseAccountSubscriber;
87
72
  eventEmitter: StrictEventEmitter<EventEmitter, ClearingHouseAccountEvents>;
88
73
  _isSubscribed = false;
89
74
  txSender: TxSender;
75
+ marketLastSlotCache = new Map<number, number>();
90
76
 
91
77
  public get isSubscribed() {
92
78
  return this._isSubscribed && this.accountSubscriber.isSubscribed;
@@ -96,91 +82,137 @@ export class ClearingHouse {
96
82
  this._isSubscribed = val;
97
83
  }
98
84
 
99
- /**
100
- * @deprecated You should use the getClearingHouse factory method instead
101
- * @param connection
102
- * @param wallet
103
- * @param clearingHouseProgramId
104
- * @param opts
105
- * @returns
106
- */
107
- public static from(
108
- connection: Connection,
109
- wallet: IWallet,
110
- clearingHouseProgramId: PublicKey,
111
- opts: ConfirmOptions = AnchorProvider.defaultOptions()
112
- ): ClearingHouse {
113
- const config = getWebSocketClearingHouseConfig(
114
- connection,
115
- wallet,
116
- clearingHouseProgramId,
117
- opts
85
+ public constructor(config: ClearingHouseConfig) {
86
+ this.connection = config.connection;
87
+ this.wallet = config.wallet;
88
+ this.opts = config.opts || AnchorProvider.defaultOptions();
89
+ this.provider = new AnchorProvider(
90
+ config.connection,
91
+ config.wallet,
92
+ this.opts
93
+ );
94
+ this.program = new Program(
95
+ clearingHouseIDL as Idl,
96
+ config.programID,
97
+ this.provider
118
98
  );
119
- return getClearingHouse(config);
120
- }
121
-
122
- public constructor(
123
- connection: Connection,
124
- wallet: IWallet,
125
- program: Program,
126
- accountSubscriber: ClearingHouseAccountSubscriber,
127
- txSender: TxSender,
128
- opts: ConfirmOptions
129
- ) {
130
- this.connection = connection;
131
- this.wallet = wallet;
132
- this.opts = opts;
133
- this.program = program;
134
- this.accountSubscriber = accountSubscriber;
99
+
100
+ const userIds = config.userIds ?? [0];
101
+ this.activeUserId = config.activeUserId ?? userIds[0];
102
+ this.userAccountSubscriptionConfig =
103
+ config.accountSubscription?.type === 'polling'
104
+ ? {
105
+ type: 'polling',
106
+ accountLoader: config.accountSubscription.accountLoader,
107
+ }
108
+ : {
109
+ type: 'websocket',
110
+ };
111
+ this.createUsers(userIds, this.userAccountSubscriptionConfig);
112
+
113
+ let marketIndexes = config.marketIndexes;
114
+ let bankIndexes = config.bankIndexes;
115
+ let oracleInfos = config.oracleInfos;
116
+ if (config.env) {
117
+ const {
118
+ marketIndexes: envMarketIndexes,
119
+ bankIndexes: envBankIndexes,
120
+ oracleInfos: envOralceInfos,
121
+ } = getMarketsBanksAndOraclesForSubscription(config.env);
122
+ marketIndexes = marketIndexes ? marketIndexes : envMarketIndexes;
123
+ bankIndexes = bankIndexes ? bankIndexes : envBankIndexes;
124
+ oracleInfos = oracleInfos ? oracleInfos : envOralceInfos;
125
+ }
126
+
127
+ if (config.accountSubscription?.type === 'polling') {
128
+ this.accountSubscriber = new PollingClearingHouseAccountSubscriber(
129
+ this.program,
130
+ config.accountSubscription.accountLoader,
131
+ marketIndexes ?? [],
132
+ bankIndexes ?? [],
133
+ oracleInfos ?? []
134
+ );
135
+ } else {
136
+ this.accountSubscriber = new WebSocketClearingHouseAccountSubscriber(
137
+ this.program,
138
+ config.marketIndexes ?? [],
139
+ config.bankIndexes ?? [],
140
+ config.oracleInfos ?? []
141
+ );
142
+ }
135
143
  this.eventEmitter = this.accountSubscriber.eventEmitter;
136
- this.txSender = txSender;
144
+ this.txSender = new RetryTxSender(
145
+ this.provider,
146
+ config.txSenderConfig?.timeout,
147
+ config.txSenderConfig?.retrySleep,
148
+ config.txSenderConfig?.additionalConnections
149
+ );
137
150
  }
138
151
 
139
- /**
140
- *
141
- * @param optionalSubscriptions - Optional extra accounts to subcribe to. Always subscribes to base clearing house state and market account state by default. You should only subscribe to optional extra accounts if required, to avoid overloading your RPC.
142
- * @returns Promise<boolean> : SubscriptionSuccess
143
- */
144
- public async subscribe(
145
- optionalSubscriptions?: ClearingHouseAccountTypes[]
146
- ): Promise<boolean> {
147
- this.isSubscribed = await this.accountSubscriber.subscribe(
148
- optionalSubscriptions
152
+ createUsers(
153
+ userIds: number[],
154
+ accountSubscriptionConfig: ClearingHouseUserAccountSubscriptionConfig
155
+ ): void {
156
+ for (const userId of userIds) {
157
+ const user = this.createUser(userId, accountSubscriptionConfig);
158
+ this.users.set(userId, user);
159
+ }
160
+ }
161
+
162
+ createUser(
163
+ userId: number,
164
+ accountSubscriptionConfig: ClearingHouseUserAccountSubscriptionConfig
165
+ ): ClearingHouseUser {
166
+ const userAccountPublicKey = getUserAccountPublicKeySync(
167
+ this.program.programId,
168
+ this.wallet.publicKey,
169
+ userId
170
+ );
171
+
172
+ return new ClearingHouseUser({
173
+ clearingHouse: this,
174
+ userAccountPublicKey,
175
+ accountSubscription: accountSubscriptionConfig,
176
+ });
177
+ }
178
+
179
+ public async subscribe(): Promise<boolean> {
180
+ const subscribePromises = this.subscribeUsers().concat(
181
+ this.accountSubscriber.subscribe()
182
+ );
183
+ this.isSubscribed = (await Promise.all(subscribePromises)).reduce(
184
+ (success, prevSuccess) => success && prevSuccess
149
185
  );
150
186
  return this.isSubscribed;
151
187
  }
152
188
 
153
- /**
154
- * Shorthand function to subscribe to all available Clearing House State Accounts
155
- * @returns Promise<boolean> : SubscriptionSuccess
156
- */
157
- public async subscribeToAll(): Promise<boolean> {
158
- return this.subscribe([
159
- 'curveHistoryAccount',
160
- 'depositHistoryAccount',
161
- 'fundingPaymentHistoryAccount',
162
- 'fundingRateHistoryAccount',
163
- 'liquidationHistoryAccount',
164
- 'tradeHistoryAccount',
165
- 'orderHistoryAccount',
166
- ]);
189
+ subscribeUsers(): Promise<boolean>[] {
190
+ return [...this.users.values()].map((user) => user.subscribe());
167
191
  }
168
192
 
169
193
  /**
170
194
  * Forces the accountSubscriber to fetch account updates from rpc
171
195
  */
172
196
  public async fetchAccounts(): Promise<void> {
173
- await this.accountSubscriber.fetch();
197
+ await Promise.all(
198
+ [...this.users.values()]
199
+ .map((user) => user.fetchAccounts())
200
+ .concat(this.accountSubscriber.fetch())
201
+ );
174
202
  }
175
203
 
176
- /**
177
- * Unsubscribe from all currently subscribed state accounts
178
- */
179
204
  public async unsubscribe(): Promise<void> {
180
- await this.accountSubscriber.unsubscribe();
205
+ const unsubscribePromises = this.unsubscribeUsers().concat(
206
+ this.accountSubscriber.unsubscribe()
207
+ );
208
+ await Promise.all(unsubscribePromises);
181
209
  this.isSubscribed = false;
182
210
  }
183
211
 
212
+ unsubscribeUsers(): Promise<void>[] {
213
+ return [...this.users.values()].map((user) => user.unsubscribe());
214
+ }
215
+
184
216
  statePublicKey?: PublicKey;
185
217
  public async getStatePublicKey(): Promise<PublicKey> {
186
218
  if (this.statePublicKey) {
@@ -193,75 +225,47 @@ export class ClearingHouse {
193
225
  }
194
226
 
195
227
  public getStateAccount(): StateAccount {
196
- return this.accountSubscriber.getStateAccount();
197
- }
198
-
199
- public async getSettlementAccount(): Promise<SettlementStateAccount> {
200
- // @ts-ignore
201
- return await this.program.account.settlementState.fetch(
202
- await getSettlementStatePublicKey(this.program.programId)
203
- );
228
+ return this.accountSubscriber.getStateAccountAndSlot().data;
204
229
  }
205
230
 
206
- public getMarketsAccount(): MarketsAccount {
207
- return this.accountSubscriber.getMarketsAccount();
208
- }
209
-
210
- public getMarket(marketIndex: BN | number): Market {
211
- if (marketIndex instanceof BN) {
212
- marketIndex = marketIndex.toNumber();
213
- }
214
- return this.getMarketsAccount().markets[marketIndex];
231
+ public getMarketAccount(marketIndex: BN | number): MarketAccount | undefined {
232
+ marketIndex = marketIndex instanceof BN ? marketIndex : new BN(marketIndex);
233
+ return this.accountSubscriber.getMarketAccountAndSlot(marketIndex)?.data;
215
234
  }
216
235
 
217
- public getFundingPaymentHistoryAccount(): FundingPaymentHistoryAccount {
218
- return this.accountSubscriber.getFundingPaymentHistoryAccount();
236
+ public getMarketAccounts(): MarketAccount[] {
237
+ return this.accountSubscriber
238
+ .getMarketAccountsAndSlots()
239
+ .map((value) => value.data);
219
240
  }
220
241
 
221
- public getFundingRateHistoryAccount(): FundingRateHistoryAccount {
222
- return this.accountSubscriber.getFundingRateHistoryAccount();
242
+ public getBankAccount(bankIndex: BN | number): BankAccount | undefined {
243
+ bankIndex = bankIndex instanceof BN ? bankIndex : new BN(bankIndex);
244
+ return this.accountSubscriber.getBankAccountAndSlot(bankIndex).data;
223
245
  }
224
246
 
225
- public getTradeHistoryAccount(): TradeHistoryAccount {
226
- return this.accountSubscriber.getTradeHistoryAccount();
247
+ public getQuoteAssetBankAccount(): BankAccount {
248
+ return this.accountSubscriber.getBankAccountAndSlot(QUOTE_ASSET_BANK_INDEX)
249
+ .data;
227
250
  }
228
251
 
229
- public getLiquidationHistoryAccount(): LiquidationHistoryAccount {
230
- return this.accountSubscriber.getLiquidationHistoryAccount();
231
- }
232
-
233
- public getDepositHistoryAccount(): DepositHistoryAccount {
234
- return this.accountSubscriber.getDepositHistoryAccount();
235
- }
236
-
237
- public getCurveHistoryAccount(): ExtendedCurveHistoryAccount {
238
- return this.accountSubscriber.getCurveHistoryAccount();
239
- }
240
-
241
- public getOrderHistoryAccount(): OrderHistoryAccount {
242
- return this.accountSubscriber.getOrderHistoryAccount();
243
- }
244
-
245
- orderStatePublicKey?: PublicKey;
246
- public async getOrderStatePublicKey(): Promise<PublicKey> {
247
- if (this.orderStatePublicKey) {
248
- return this.orderStatePublicKey;
249
- }
250
- this.orderStatePublicKey = await getOrderStateAccountPublicKey(
251
- this.program.programId
252
- );
253
- return this.orderStatePublicKey;
254
- }
255
-
256
- public getOrderStateAccount(): OrderStateAccount {
257
- return this.accountSubscriber.getOrderStateAccount();
252
+ public getOraclePriceDataAndSlot(
253
+ oraclePublicKey: PublicKey
254
+ ): DataAndSlot<OraclePriceData> | undefined {
255
+ return this.accountSubscriber.getOraclePriceDataAndSlot(oraclePublicKey);
258
256
  }
259
257
 
260
258
  /**
261
259
  * Update the wallet to use for clearing house transactions and linked user account
262
260
  * @param newWallet
261
+ * @param userIds
262
+ * @param activeUserId
263
263
  */
264
- public updateWallet(newWallet: IWallet): void {
264
+ public async updateWallet(
265
+ newWallet: IWallet,
266
+ userIds = [0],
267
+ activeUserId = 0
268
+ ): Promise<void> {
265
269
  const newProvider = new AnchorProvider(
266
270
  this.connection,
267
271
  newWallet,
@@ -279,257 +283,358 @@ export class ClearingHouse {
279
283
  this.wallet = newWallet;
280
284
  this.provider = newProvider;
281
285
  this.program = newProgram;
282
- this.userAccountPublicKey = undefined;
283
- this.userAccount = undefined;
284
- this.userOrdersAccountPublicKey = undefined;
285
- this.userOrdersExist = undefined;
286
- }
287
286
 
288
- public async initializeUserAccount(): Promise<
289
- [TransactionSignature, PublicKey]
290
- > {
291
- const [
292
- userPositionsAccount,
293
- userAccountPublicKey,
294
- initializeUserAccountIx,
295
- initializeUserOrdersAccountIx,
296
- ] = await this.getInitializeUserInstructions();
287
+ if (this.isSubscribed) {
288
+ await Promise.all(this.unsubscribeUsers());
289
+ }
290
+ this.users.clear();
291
+ this.createUsers(userIds, this.userAccountSubscriptionConfig);
292
+ if (this.isSubscribed) {
293
+ await Promise.all(this.subscribeUsers());
294
+ }
297
295
 
298
- const tx = new Transaction()
299
- .add(initializeUserAccountIx)
300
- .add(initializeUserOrdersAccountIx);
301
- const txSig = await this.txSender.send(
302
- tx,
303
- [userPositionsAccount],
304
- this.opts
305
- );
306
- return [txSig, userAccountPublicKey];
296
+ this.activeUserId = activeUserId;
307
297
  }
308
298
 
309
- async getInitializeUserInstructions(): Promise<
310
- [Keypair, PublicKey, TransactionInstruction, TransactionInstruction]
311
- > {
312
- const [userAccountPublicKey, userAccountNonce] =
313
- await getUserAccountPublicKeyAndNonce(
314
- this.program.programId,
315
- this.wallet.publicKey
316
- );
317
-
318
- const remainingAccounts = [];
319
- const optionalAccounts = {
320
- whitelistToken: false,
321
- };
299
+ public async switchActiveUser(userId: number): Promise<void> {
300
+ this.activeUserId = userId;
301
+ }
322
302
 
323
- const state = this.getStateAccount();
324
- if (state.whitelistMint) {
325
- optionalAccounts.whitelistToken = true;
326
- const associatedTokenPublicKey = await Token.getAssociatedTokenAddress(
327
- ASSOCIATED_TOKEN_PROGRAM_ID,
328
- TOKEN_PROGRAM_ID,
329
- state.whitelistMint,
330
- this.wallet.publicKey
331
- );
332
- remainingAccounts.push({
333
- pubkey: associatedTokenPublicKey,
334
- isWritable: false,
335
- isSigner: false,
336
- });
303
+ public async addUser(userId: number): Promise<void> {
304
+ if (this.users.has(userId)) {
305
+ return;
337
306
  }
338
307
 
339
- const userPositions = new Keypair();
340
- const initializeUserAccountIx =
341
- await this.program.instruction.initializeUser(
342
- userAccountNonce,
343
- optionalAccounts,
344
- {
345
- accounts: {
346
- user: userAccountPublicKey,
347
- authority: this.wallet.publicKey,
348
- rent: anchor.web3.SYSVAR_RENT_PUBKEY,
349
- systemProgram: anchor.web3.SystemProgram.programId,
350
- userPositions: userPositions.publicKey,
351
- state: await this.getStatePublicKey(),
352
- },
353
- remainingAccounts: remainingAccounts,
354
- }
355
- );
308
+ const user = this.createUser(userId, this.userAccountSubscriptionConfig);
309
+ await user.subscribe();
310
+ this.users.set(userId, user);
311
+ }
356
312
 
357
- const initializeUserOrdersAccountIx =
358
- await this.getInitializeUserOrdersInstruction(userAccountPublicKey);
313
+ public async initializeUserAccount(
314
+ userId = 0,
315
+ name = DEFAULT_USER_NAME
316
+ ): Promise<[TransactionSignature, PublicKey]> {
317
+ const [userAccountPublicKey, initializeUserAccountIx] =
318
+ await this.getInitializeUserInstructions(userId, name);
359
319
 
360
- return [
361
- userPositions,
362
- userAccountPublicKey,
363
- initializeUserAccountIx,
364
- initializeUserOrdersAccountIx,
365
- ];
320
+ const tx = new Transaction().add(initializeUserAccountIx);
321
+ const { txSig } = await this.txSender.send(tx, [], this.opts);
322
+ return [txSig, userAccountPublicKey];
366
323
  }
367
324
 
368
- async getInitializeUserOrdersInstruction(
369
- userAccountPublicKey?: PublicKey
370
- ): Promise<TransactionInstruction> {
371
- if (!userAccountPublicKey) {
372
- userAccountPublicKey = await this.getUserAccountPublicKey();
373
- }
374
-
375
- const [userOrdersAccountPublicKey, userOrdersAccountNonce] =
376
- await getUserOrdersAccountPublicKeyAndNonce(
377
- this.program.programId,
378
- userAccountPublicKey
379
- );
325
+ async getInitializeUserInstructions(
326
+ userId = 0,
327
+ name = DEFAULT_USER_NAME
328
+ ): Promise<[PublicKey, TransactionInstruction]> {
329
+ const userAccountPublicKey = await getUserAccountPublicKey(
330
+ this.program.programId,
331
+ this.wallet.publicKey,
332
+ userId
333
+ );
380
334
 
381
- return await this.program.instruction.initializeUserOrders(
382
- userOrdersAccountNonce,
383
- {
335
+ const nameBuffer = encodeName(name);
336
+ const initializeUserAccountIx =
337
+ await this.program.instruction.initializeUser(userId, nameBuffer, {
384
338
  accounts: {
385
339
  user: userAccountPublicKey,
386
340
  authority: this.wallet.publicKey,
341
+ payer: this.wallet.publicKey,
387
342
  rent: anchor.web3.SYSVAR_RENT_PUBKEY,
388
343
  systemProgram: anchor.web3.SystemProgram.programId,
389
- userOrders: userOrdersAccountPublicKey,
390
344
  state: await this.getStatePublicKey(),
391
345
  },
392
- }
393
- );
346
+ });
347
+
348
+ return [userAccountPublicKey, initializeUserAccountIx];
394
349
  }
395
350
 
396
- userAccountPublicKey?: PublicKey;
397
- /**
398
- * Get the address for the Clearing House User's account. NOT the user's wallet address.
399
- * @returns
400
- */
401
- public async getUserAccountPublicKey(): Promise<PublicKey> {
402
- if (this.userAccountPublicKey) {
403
- return this.userAccountPublicKey;
351
+ public getUser(userId?: number): ClearingHouseUser {
352
+ userId = userId ?? this.activeUserId;
353
+ if (!this.users.has(userId)) {
354
+ throw new Error(`Clearing House has no user for user id ${userId}`);
404
355
  }
356
+ return this.users.get(userId);
357
+ }
405
358
 
406
- this.userAccountPublicKey = await getUserAccountPublicKey(
407
- this.program.programId,
408
- this.wallet.publicKey
409
- );
410
- return this.userAccountPublicKey;
359
+ public getUsers(): ClearingHouseUser[] {
360
+ return [...this.users.values()];
411
361
  }
412
362
 
413
- userAccount?: UserAccount;
414
- public async getUserAccount(): Promise<UserAccount> {
415
- if (this.userAccount) {
416
- return this.userAccount;
417
- }
363
+ public async getUserAccountPublicKey(): Promise<PublicKey> {
364
+ return this.getUser().userAccountPublicKey;
365
+ }
418
366
 
419
- this.userAccount = (await this.program.account.user.fetch(
420
- await this.getUserAccountPublicKey()
421
- )) as UserAccount;
422
- return this.userAccount;
367
+ public getUserAccount(userId?: number): UserAccount | undefined {
368
+ return this.getUser(userId).getUserAccount();
423
369
  }
424
370
 
425
- userOrdersAccountPublicKey?: PublicKey;
426
- /**
427
- * Get the address for the Clearing House User Order's account. NOT the user's wallet address.
428
- * @returns
429
- */
430
- public async getUserOrdersAccountPublicKey(): Promise<PublicKey> {
431
- if (this.userOrdersAccountPublicKey) {
432
- return this.userOrdersAccountPublicKey;
433
- }
371
+ public getUserAccountAndSlot(
372
+ userId?: number
373
+ ): DataAndSlot<UserAccount> | undefined {
374
+ return this.getUser(userId).getUserAccountAndSlot();
375
+ }
434
376
 
435
- this.userOrdersAccountPublicKey = await getUserOrdersAccountPublicKey(
436
- this.program.programId,
437
- await this.getUserAccountPublicKey()
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)
438
383
  );
439
- return this.userOrdersAccountPublicKey;
440
384
  }
441
385
 
442
- userOrdersExist?: boolean;
443
- async userOrdersAccountExists(): Promise<boolean> {
444
- if (this.userOrdersExist) {
445
- return this.userOrdersExist;
386
+ public getQuoteAssetTokenAmount(): BN {
387
+ const bank = this.getBankAccount(QUOTE_ASSET_BANK_INDEX);
388
+ const userBankBalance = this.getUserBankBalance(QUOTE_ASSET_BANK_INDEX);
389
+ return getTokenAmount(
390
+ userBankBalance.balance,
391
+ bank,
392
+ userBankBalance.balanceType
393
+ );
394
+ }
395
+
396
+ getRemainingAccounts(params: {
397
+ writableMarketIndex?: BN;
398
+ writableBankIndex?: BN;
399
+ }): AccountMeta[] {
400
+ const userAccountAndSlot = this.getUserAccountAndSlot();
401
+ if (!userAccountAndSlot) {
402
+ throw Error(
403
+ 'No user account found. Most likely user account does not exist or failed to fetch account'
404
+ );
446
405
  }
447
- const userOrdersAccountRPCResponse =
448
- await this.connection.getParsedAccountInfo(
449
- await this.getUserOrdersAccountPublicKey()
406
+ const { data: userAccount, slot: lastUserPositionsSlot } =
407
+ userAccountAndSlot;
408
+
409
+ const oracleAccountMap = new Map<string, AccountMeta>();
410
+ const bankAccountMap = new Map<number, AccountMeta>();
411
+ const marketAccountMap = new Map<number, AccountMeta>();
412
+ for (const [marketIndexNum, slot] of this.marketLastSlotCache.entries()) {
413
+ // if cache has more recent slot than user positions account slot, add market to remaining accounts
414
+ // otherwise remove from slot
415
+ if (slot > lastUserPositionsSlot) {
416
+ const marketAccount = this.getMarketAccount(marketIndexNum);
417
+ marketAccountMap.set(marketIndexNum, {
418
+ pubkey: marketAccount.pubkey,
419
+ isSigner: false,
420
+ isWritable: false,
421
+ });
422
+ oracleAccountMap.set(marketAccount.amm.oracle.toString(), {
423
+ pubkey: marketAccount.amm.oracle,
424
+ isSigner: false,
425
+ isWritable: false,
426
+ });
427
+ } else {
428
+ this.marketLastSlotCache.delete(marketIndexNum);
429
+ }
430
+ }
431
+
432
+ for (const position of userAccount.positions) {
433
+ if (!positionIsAvailable(position)) {
434
+ const marketIndexNum = position.marketIndex.toNumber();
435
+ const marketAccount = this.getMarketAccount(marketIndexNum);
436
+ marketAccountMap.set(marketIndexNum, {
437
+ pubkey: marketAccount.pubkey,
438
+ isSigner: false,
439
+ // isWritable: false, // TODO
440
+ isWritable: true,
441
+ });
442
+ oracleAccountMap.set(marketAccount.pubkey.toString(), {
443
+ pubkey: marketAccount.amm.oracle,
444
+ isSigner: false,
445
+ isWritable: false,
446
+ });
447
+ }
448
+ }
449
+
450
+ if (params.writableMarketIndex) {
451
+ const marketAccount = this.getMarketAccount(
452
+ params.writableMarketIndex.toNumber()
450
453
  );
454
+ marketAccountMap.set(params.writableMarketIndex.toNumber(), {
455
+ pubkey: marketAccount.pubkey,
456
+ isSigner: false,
457
+ isWritable: true,
458
+ });
459
+ oracleAccountMap.set(marketAccount.amm.oracle.toString(), {
460
+ pubkey: marketAccount.amm.oracle,
461
+ isSigner: false,
462
+ isWritable: false,
463
+ });
464
+ }
465
+
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,
471
+ isSigner: false,
472
+ isWritable: false,
473
+ });
474
+ if (!bankAccount.bankIndex.eq(ZERO)) {
475
+ oracleAccountMap.set(bankAccount.oracle.toString(), {
476
+ pubkey: bankAccount.oracle,
477
+ isSigner: false,
478
+ isWritable: false,
479
+ });
480
+ }
481
+ }
482
+ }
451
483
 
452
- this.userOrdersExist = userOrdersAccountRPCResponse.value !== null;
453
- return this.userOrdersExist;
484
+ if (params.writableBankIndex) {
485
+ const bankAccount = this.getBankAccount(params.writableBankIndex);
486
+ bankAccountMap.set(params.writableBankIndex.toNumber(), {
487
+ pubkey: bankAccount.pubkey,
488
+ isSigner: false,
489
+ isWritable: true,
490
+ });
491
+ if (!bankAccount.bankIndex.eq(ZERO)) {
492
+ oracleAccountMap.set(bankAccount.oracle.toString(), {
493
+ pubkey: bankAccount.oracle,
494
+ isSigner: false,
495
+ isWritable: false,
496
+ });
497
+ }
498
+ }
499
+
500
+ return [
501
+ ...oracleAccountMap.values(),
502
+ ...bankAccountMap.values(),
503
+ ...marketAccountMap.values(),
504
+ ];
454
505
  }
455
506
 
456
- public async depositCollateral(
507
+ public getOrder(orderId: BN | number): Order | undefined {
508
+ const orderIdBN = orderId instanceof BN ? orderId : new BN(orderId);
509
+ return this.getUserAccount()?.orders.find((order) =>
510
+ order.orderId.eq(orderIdBN)
511
+ );
512
+ }
513
+
514
+ public getOrderByUserId(userOrderId: number): Order | undefined {
515
+ return this.getUserAccount()?.orders.find(
516
+ (order) => order.userOrderId === userOrderId
517
+ );
518
+ }
519
+
520
+ public async deposit(
457
521
  amount: BN,
522
+ bankIndex: BN,
458
523
  collateralAccountPublicKey: PublicKey,
459
- userPositionsAccountPublicKey?: PublicKey
524
+ userId?: number,
525
+ reduceOnly = false
460
526
  ): Promise<TransactionSignature> {
461
- const depositCollateralIx = await this.getDepositCollateralInstruction(
527
+ const depositCollateralIx = await this.getDepositInstruction(
462
528
  amount,
529
+ bankIndex,
463
530
  collateralAccountPublicKey,
464
- userPositionsAccountPublicKey
531
+ userId,
532
+ reduceOnly,
533
+ true
465
534
  );
466
535
 
467
536
  const tx = new Transaction().add(depositCollateralIx);
468
537
 
469
- return await this.txSender.send(tx);
538
+ const { txSig } = await this.txSender.send(tx);
539
+ return txSig;
470
540
  }
471
541
 
472
- async getDepositCollateralInstruction(
542
+ async getDepositInstruction(
473
543
  amount: BN,
474
- collateralAccountPublicKey: PublicKey,
475
- userPositionsAccountPublicKey?: PublicKey
544
+ bankIndex: BN,
545
+ userTokenAccount: PublicKey,
546
+ userId?: number,
547
+ reduceOnly = false,
548
+ userInitialized = true
476
549
  ): Promise<TransactionInstruction> {
477
- const userAccountPublicKey = await this.getUserAccountPublicKey();
478
- if (!userPositionsAccountPublicKey) {
479
- userPositionsAccountPublicKey = (await this.getUserAccount()).positions;
550
+ const userAccountPublicKey = userId
551
+ ? await getUserAccountPublicKey(
552
+ this.program.programId,
553
+ this.wallet.publicKey,
554
+ userId
555
+ )
556
+ : await this.getUserAccountPublicKey();
557
+
558
+ let remainingAccounts = [];
559
+ if (userInitialized) {
560
+ remainingAccounts = this.getRemainingAccounts({
561
+ writableBankIndex: bankIndex,
562
+ });
563
+ } else {
564
+ remainingAccounts = [
565
+ {
566
+ pubkey: this.getBankAccount(bankIndex).pubkey,
567
+ isSigner: false,
568
+ isWritable: true,
569
+ },
570
+ ];
480
571
  }
481
572
 
482
- const state = this.getStateAccount();
483
- return await this.program.instruction.depositCollateral(amount, {
484
- accounts: {
485
- state: await this.getStatePublicKey(),
486
- user: userAccountPublicKey,
487
- collateralVault: state.collateralVault,
488
- userCollateralAccount: collateralAccountPublicKey,
489
- authority: this.wallet.publicKey,
490
- tokenProgram: TOKEN_PROGRAM_ID,
491
- markets: state.markets,
492
- fundingPaymentHistory: state.fundingPaymentHistory,
493
- depositHistory: state.depositHistory,
494
- userPositions: userPositionsAccountPublicKey,
495
- },
496
- });
573
+ const bank = this.getBankAccount(bankIndex);
574
+
575
+ return await this.program.instruction.deposit(
576
+ bankIndex,
577
+ amount,
578
+ reduceOnly,
579
+ {
580
+ accounts: {
581
+ state: await this.getStatePublicKey(),
582
+ bank: bank.pubkey,
583
+ bankVault: bank.vault,
584
+ user: userAccountPublicKey,
585
+ userTokenAccount: userTokenAccount,
586
+ authority: this.wallet.publicKey,
587
+ tokenProgram: TOKEN_PROGRAM_ID,
588
+ },
589
+ remainingAccounts,
590
+ }
591
+ );
497
592
  }
498
593
 
499
594
  /**
500
595
  * Creates the Clearing House User account for a user, and deposits some initial collateral
596
+ * @param userId
597
+ * @param name
501
598
  * @param amount
502
- * @param collateralAccountPublicKey
599
+ * @param userTokenAccount
600
+ * @param fromUserId
503
601
  * @returns
504
602
  */
505
603
  public async initializeUserAccountAndDepositCollateral(
506
604
  amount: BN,
507
- collateralAccountPublicKey: PublicKey
605
+ userTokenAccount: PublicKey,
606
+ bankIndex = new BN(0),
607
+ userId = 0,
608
+ name = DEFAULT_USER_NAME,
609
+ fromUserId?: number
508
610
  ): Promise<[TransactionSignature, PublicKey]> {
509
- const [
510
- userPositionsAccount,
511
- userAccountPublicKey,
512
- initializeUserAccountIx,
513
- initializeUserOrdersAccountIx,
514
- ] = await this.getInitializeUserInstructions();
515
-
516
- const depositCollateralIx = await this.getDepositCollateralInstruction(
517
- amount,
518
- collateralAccountPublicKey,
519
- userPositionsAccount.publicKey
520
- );
611
+ const [userAccountPublicKey, initializeUserAccountIx] =
612
+ await this.getInitializeUserInstructions(userId, name);
613
+
614
+ const depositCollateralIx =
615
+ fromUserId != null
616
+ ? await this.getTransferDepositIx(amount, bankIndex, fromUserId, userId)
617
+ : await this.getDepositInstruction(
618
+ amount,
619
+ bankIndex,
620
+ userTokenAccount,
621
+ userId,
622
+ false,
623
+ false
624
+ );
521
625
 
522
626
  const tx = new Transaction()
523
627
  .add(initializeUserAccountIx)
524
- .add(initializeUserOrdersAccountIx)
525
628
  .add(depositCollateralIx);
526
629
 
527
- const txSig = await this.txSender.send(tx, [userPositionsAccount]);
630
+ const { txSig } = await this.txSender.send(tx, []);
528
631
 
529
632
  return [txSig, userAccountPublicKey];
530
633
  }
531
634
 
532
635
  public async initializeUserAccountForDevnet(
636
+ userId = 0,
637
+ name = DEFAULT_USER_NAME,
533
638
  mockUSDCFaucet: MockUSDCFaucet,
534
639
  amount: BN
535
640
  ): Promise<[TransactionSignature, PublicKey]> {
@@ -539,423 +644,356 @@ export class ClearingHouse {
539
644
  amount
540
645
  );
541
646
 
542
- const [
543
- userPositionsAccount,
544
- userAccountPublicKey,
545
- initializeUserAccountIx,
546
- initializeUserOrdersAccountIx,
547
- ] = await this.getInitializeUserInstructions();
647
+ const [userAccountPublicKey, initializeUserAccountIx] =
648
+ await this.getInitializeUserInstructions(userId, name);
548
649
 
549
- const depositCollateralIx = await this.getDepositCollateralInstruction(
650
+ const depositCollateralIx = await this.getDepositInstruction(
550
651
  amount,
652
+ new BN(0),
551
653
  associateTokenPublicKey,
552
- userPositionsAccount.publicKey
654
+ userId,
655
+ false,
656
+ false
553
657
  );
554
658
 
555
659
  const tx = new Transaction()
556
660
  .add(createAssociatedAccountIx)
557
661
  .add(mintToIx)
558
662
  .add(initializeUserAccountIx)
559
- .add(initializeUserOrdersAccountIx)
560
663
  .add(depositCollateralIx);
561
664
 
562
- const txSig = await this.program.provider.sendAndConfirm(tx, [
563
- userPositionsAccount,
564
- ]);
665
+ const txSig = await this.program.provider.sendAndConfirm(tx, []);
565
666
 
566
667
  return [txSig, userAccountPublicKey];
567
668
  }
568
669
 
569
- public async withdrawCollateral(
670
+ public async withdraw(
570
671
  amount: BN,
571
- collateralAccountPublicKey: PublicKey
672
+ bankIndex: BN,
673
+ userTokenAccount: PublicKey,
674
+ reduceOnly = false
572
675
  ): Promise<TransactionSignature> {
573
- return this.txSender.send(
676
+ const { txSig } = await this.txSender.send(
574
677
  wrapInTx(
575
- await this.getWithdrawCollateralIx(amount, collateralAccountPublicKey)
678
+ await this.getWithdrawIx(
679
+ amount,
680
+ bankIndex,
681
+ userTokenAccount,
682
+ reduceOnly
683
+ )
576
684
  ),
577
685
  [],
578
686
  this.opts
579
687
  );
688
+ return txSig;
580
689
  }
581
690
 
582
- public async getWithdrawCollateralIx(
691
+ public async getWithdrawIx(
583
692
  amount: BN,
584
- collateralAccountPublicKey: PublicKey
693
+ bankIndex: BN,
694
+ userTokenAccount: PublicKey,
695
+ reduceOnly = false
585
696
  ): Promise<TransactionInstruction> {
586
697
  const userAccountPublicKey = await this.getUserAccountPublicKey();
587
- const user: any = await this.program.account.user.fetch(
588
- userAccountPublicKey
589
- );
590
698
 
591
- const state = this.getStateAccount();
592
- return await this.program.instruction.withdrawCollateral(amount, {
593
- accounts: {
594
- state: await this.getStatePublicKey(),
595
- user: userAccountPublicKey,
596
- collateralVault: state.collateralVault,
597
- collateralVaultAuthority: state.collateralVaultAuthority,
598
- insuranceVault: state.insuranceVault,
599
- insuranceVaultAuthority: state.insuranceVaultAuthority,
600
- userCollateralAccount: collateralAccountPublicKey,
601
- authority: this.wallet.publicKey,
602
- tokenProgram: TOKEN_PROGRAM_ID,
603
- markets: state.markets,
604
- userPositions: user.positions,
605
- fundingPaymentHistory: state.fundingPaymentHistory,
606
- depositHistory: state.depositHistory,
607
- },
699
+ const remainingAccounts = this.getRemainingAccounts({
700
+ writableBankIndex: bankIndex,
608
701
  });
702
+
703
+ const bank = this.getBankAccount(bankIndex);
704
+
705
+ return await this.program.instruction.withdraw(
706
+ bankIndex,
707
+ amount,
708
+ reduceOnly,
709
+ {
710
+ accounts: {
711
+ state: await this.getStatePublicKey(),
712
+ bank: bank.pubkey,
713
+ bankVault: bank.vault,
714
+ bankVaultAuthority: bank.vaultAuthority,
715
+ user: userAccountPublicKey,
716
+ userTokenAccount: userTokenAccount,
717
+ authority: this.wallet.publicKey,
718
+ tokenProgram: TOKEN_PROGRAM_ID,
719
+ },
720
+ remainingAccounts,
721
+ }
722
+ );
609
723
  }
610
724
 
611
- public async openPosition(
612
- direction: PositionDirection,
725
+ public async transferDeposit(
613
726
  amount: BN,
614
- marketIndex: BN,
615
- limitPrice?: BN,
616
- discountToken?: PublicKey,
617
- referrer?: PublicKey
727
+ bankIndex: BN,
728
+ fromUserId: number,
729
+ toUserId: number
618
730
  ): Promise<TransactionSignature> {
619
- return await this.txSender.send(
731
+ const { txSig } = await this.txSender.send(
620
732
  wrapInTx(
621
- await this.getOpenPositionIx(
622
- direction,
623
- amount,
624
- marketIndex,
625
- limitPrice,
626
- discountToken,
627
- referrer
628
- )
733
+ await this.getTransferDepositIx(amount, bankIndex, fromUserId, toUserId)
629
734
  ),
630
735
  [],
631
736
  this.opts
632
737
  );
738
+ return txSig;
633
739
  }
634
740
 
635
- public async getOpenPositionIx(
636
- direction: PositionDirection,
741
+ public async getTransferDepositIx(
637
742
  amount: BN,
638
- marketIndex: BN,
639
- limitPrice?: BN,
640
- discountToken?: PublicKey,
641
- referrer?: PublicKey
743
+ bankIndex: BN,
744
+ fromUserId: number,
745
+ toUserId: number
642
746
  ): Promise<TransactionInstruction> {
643
- const userAccountPublicKey = await this.getUserAccountPublicKey();
644
- const userAccount = await this.getUserAccount();
645
-
646
- if (limitPrice == undefined) {
647
- limitPrice = new BN(0); // no limit
648
- }
747
+ const fromUser = await getUserAccountPublicKey(
748
+ this.program.programId,
749
+ this.wallet.publicKey,
750
+ fromUserId
751
+ );
752
+ const toUser = await getUserAccountPublicKey(
753
+ this.program.programId,
754
+ this.wallet.publicKey,
755
+ toUserId
756
+ );
649
757
 
650
- const optionalAccounts = {
651
- discountToken: false,
652
- referrer: false,
653
- };
654
- const remainingAccounts = [];
655
- if (discountToken) {
656
- optionalAccounts.discountToken = true;
657
- remainingAccounts.push({
658
- pubkey: discountToken,
659
- isWritable: false,
660
- isSigner: false,
661
- });
662
- }
663
- if (referrer) {
664
- optionalAccounts.referrer = true;
665
- remainingAccounts.push({
666
- pubkey: referrer,
667
- isWritable: true,
668
- isSigner: false,
669
- });
670
- }
758
+ const remainingAccounts = this.getRemainingAccounts({
759
+ writableBankIndex: bankIndex,
760
+ });
671
761
 
672
- const priceOracle =
673
- this.getMarketsAccount().markets[marketIndex.toNumber()].amm.oracle;
762
+ return await this.program.instruction.transferDeposit(bankIndex, amount, {
763
+ accounts: {
764
+ authority: this.wallet.publicKey,
765
+ fromUser,
766
+ toUser,
767
+ state: await this.getStatePublicKey(),
768
+ },
769
+ remainingAccounts,
770
+ });
771
+ }
674
772
 
675
- const state = this.getStateAccount();
676
- return await this.program.instruction.openPosition(
677
- direction,
678
- amount,
679
- marketIndex,
680
- limitPrice,
681
- optionalAccounts,
682
- {
683
- accounts: {
684
- state: await this.getStatePublicKey(),
685
- user: userAccountPublicKey,
686
- authority: this.wallet.publicKey,
687
- markets: state.markets,
688
- userPositions: userAccount.positions,
689
- tradeHistory: state.tradeHistory,
690
- fundingPaymentHistory: state.fundingPaymentHistory,
691
- fundingRateHistory: state.fundingRateHistory,
692
- oracle: priceOracle,
693
- },
694
- remainingAccounts: remainingAccounts,
695
- }
773
+ public async updateBankCumulativeInterest(
774
+ bankIndex: BN
775
+ ): Promise<TransactionSignature> {
776
+ const { txSig } = await this.txSender.send(
777
+ wrapInTx(await this.updateBankCumulativeInterestIx(bankIndex)),
778
+ [],
779
+ this.opts
696
780
  );
781
+ return txSig;
697
782
  }
698
783
 
699
- public async initializeUserOrdersThenPlaceOrder(
700
- orderParams: OrderParams,
701
- discountToken?: PublicKey,
702
- referrer?: PublicKey
784
+ public async updateBankCumulativeInterestIx(
785
+ bankIndex: BN
786
+ ): Promise<TransactionInstruction> {
787
+ const bank = this.getBankAccount(bankIndex);
788
+ return await this.program.instruction.updateBankCumulativeInterest({
789
+ accounts: {
790
+ bank: bank.pubkey,
791
+ },
792
+ });
793
+ }
794
+
795
+ public async openPosition(
796
+ direction: PositionDirection,
797
+ amount: BN,
798
+ marketIndex: BN,
799
+ limitPrice?: BN
703
800
  ): Promise<TransactionSignature> {
704
- const instructions: anchor.web3.TransactionInstruction[] = [];
705
- const userOrdersAccountExists = await this.userOrdersAccountExists();
706
- if (!userOrdersAccountExists) {
707
- instructions.push(await this.getInitializeUserOrdersInstruction());
708
- }
709
- instructions.push(
710
- await this.getPlaceOrderIx(orderParams, discountToken, referrer)
801
+ return await this.placeAndTake(
802
+ getMarketOrderParams(
803
+ marketIndex,
804
+ direction,
805
+ ZERO,
806
+ amount,
807
+ false,
808
+ limitPrice
809
+ )
711
810
  );
712
- const tx = new Transaction();
713
- for (const instruction of instructions) {
714
- tx.add(instruction);
715
- }
716
-
717
- return await this.txSender.send(tx, [], this.opts);
718
811
  }
719
812
 
720
813
  public async placeOrder(
721
- orderParams: OrderParams,
722
- discountToken?: PublicKey,
723
- referrer?: PublicKey
814
+ orderParams: OrderParams
724
815
  ): Promise<TransactionSignature> {
725
- return await this.txSender.send(
726
- wrapInTx(
727
- await this.getPlaceOrderIx(orderParams, discountToken, referrer)
728
- ),
816
+ const { txSig, slot } = await this.txSender.send(
817
+ wrapInTx(await this.getPlaceOrderIx(orderParams)),
729
818
  [],
730
819
  this.opts
731
820
  );
821
+ this.marketLastSlotCache.set(orderParams.marketIndex.toNumber(), slot);
822
+ return txSig;
732
823
  }
733
824
 
734
825
  public async getPlaceOrderIx(
735
- orderParams: OrderParams,
736
- discountToken?: PublicKey,
737
- referrer?: PublicKey
826
+ orderParams: OrderParams
738
827
  ): Promise<TransactionInstruction> {
739
828
  const userAccountPublicKey = await this.getUserAccountPublicKey();
740
- const userAccount = await this.getUserAccount();
741
-
742
- const priceOracle =
743
- this.getMarketsAccount().markets[orderParams.marketIndex.toNumber()].amm
744
- .oracle;
745
829
 
746
- const remainingAccounts = [];
747
- if (orderParams.optionalAccounts.discountToken) {
748
- if (!discountToken) {
749
- throw Error(
750
- 'Optional accounts specified discount token but no discount token present'
751
- );
752
- }
753
-
754
- remainingAccounts.push({
755
- pubkey: discountToken,
756
- isWritable: false,
757
- isSigner: false,
758
- });
759
- }
760
-
761
- if (orderParams.optionalAccounts.referrer) {
762
- if (!referrer) {
763
- throw Error(
764
- 'Optional accounts specified referrer but no referrer present'
765
- );
766
- }
830
+ const priceOracle = this.getMarketAccount(orderParams.marketIndex).amm
831
+ .oracle;
767
832
 
768
- remainingAccounts.push({
769
- pubkey: referrer,
770
- isWritable: false,
771
- isSigner: false,
772
- });
773
- }
774
-
775
- if (!orderParams.oraclePriceOffset.eq(ZERO)) {
776
- remainingAccounts.push({
777
- pubkey: priceOracle,
778
- isWritable: false,
779
- isSigner: false,
780
- });
781
- }
833
+ const remainingAccounts = this.getRemainingAccounts({
834
+ writableMarketIndex: orderParams.marketIndex,
835
+ });
782
836
 
783
- const state = this.getStateAccount();
784
- const orderState = this.getOrderStateAccount();
785
837
  return await this.program.instruction.placeOrder(orderParams, {
786
838
  accounts: {
787
839
  state: await this.getStatePublicKey(),
788
840
  user: userAccountPublicKey,
789
841
  authority: this.wallet.publicKey,
790
- markets: state.markets,
791
- userOrders: await this.getUserOrdersAccountPublicKey(),
792
- userPositions: userAccount.positions,
793
- fundingPaymentHistory: state.fundingPaymentHistory,
794
- fundingRateHistory: state.fundingRateHistory,
795
- orderState: await this.getOrderStatePublicKey(),
796
- orderHistory: orderState.orderHistory,
842
+ oracle: priceOracle,
797
843
  },
798
844
  remainingAccounts,
799
845
  });
800
846
  }
801
847
 
802
848
  public async expireOrders(
803
- userAccountPublicKey: PublicKey,
804
- userOrdersAccountPublicKey: PublicKey
849
+ userAccountPublicKey: PublicKey
805
850
  ): Promise<TransactionSignature> {
806
- return await this.txSender.send(
807
- wrapInTx(
808
- await this.getExpireOrdersIx(
809
- userAccountPublicKey,
810
- userOrdersAccountPublicKey
811
- )
812
- ),
851
+ const { txSig } = await this.txSender.send(
852
+ wrapInTx(await this.getExpireOrdersIx(userAccountPublicKey)),
813
853
  [],
814
854
  this.opts
815
855
  );
856
+ return txSig;
816
857
  }
817
858
 
818
859
  public async getExpireOrdersIx(
819
- userAccountPublicKey: PublicKey,
820
- userOrdersAccountPublicKey: PublicKey
860
+ userAccountPublicKey: PublicKey
821
861
  ): Promise<TransactionInstruction> {
822
862
  const fillerPublicKey = await this.getUserAccountPublicKey();
823
- const userAccount: any = await this.program.account.user.fetch(
824
- userAccountPublicKey
825
- );
826
863
 
827
- const orderState = this.getOrderStateAccount();
828
864
  return await this.program.instruction.expireOrders({
829
865
  accounts: {
830
866
  state: await this.getStatePublicKey(),
831
867
  filler: fillerPublicKey,
832
868
  user: userAccountPublicKey,
833
869
  authority: this.wallet.publicKey,
834
- userPositions: userAccount.positions,
835
- userOrders: userOrdersAccountPublicKey,
836
- orderState: await this.getOrderStatePublicKey(),
837
- orderHistory: orderState.orderHistory,
838
870
  },
839
871
  });
840
872
  }
841
873
 
842
- public async cancelOrder(
843
- orderId: BN,
844
- oracle?: PublicKey
845
- ): Promise<TransactionSignature> {
846
- return await this.txSender.send(
847
- wrapInTx(await this.getCancelOrderIx(orderId, oracle)),
874
+ public async updateAMMs(marketIndexes: BN[]): Promise<TransactionSignature> {
875
+ const { txSig } = await this.txSender.send(
876
+ wrapInTx(await this.getUpdateAMMsIx(marketIndexes)),
877
+ [],
878
+ this.opts
879
+ );
880
+ return txSig;
881
+ }
882
+
883
+ public async getUpdateAMMsIx(
884
+ marketIndexes: BN[]
885
+ ): Promise<TransactionInstruction> {
886
+ for (let i = marketIndexes.length; i < 5; i++) {
887
+ marketIndexes.push(new BN(100));
888
+ }
889
+ const marketAccountInfos = [];
890
+ const oracleAccountInfos = [];
891
+ for (const marketIndex of marketIndexes) {
892
+ if (!marketIndex.eq(new BN(100))) {
893
+ const market = this.getMarketAccount(marketIndex);
894
+ marketAccountInfos.push({
895
+ pubkey: market.pubkey,
896
+ isWritable: true,
897
+ isSigner: false,
898
+ });
899
+ oracleAccountInfos.push({
900
+ pubkey: market.amm.oracle,
901
+ isWritable: false,
902
+ isSigner: false,
903
+ });
904
+ }
905
+ }
906
+ const remainingAccounts = oracleAccountInfos.concat(marketAccountInfos);
907
+
908
+ return await this.program.instruction.updateAmms(marketIndexes, {
909
+ accounts: {
910
+ state: await this.getStatePublicKey(),
911
+ authority: this.wallet.publicKey,
912
+ },
913
+ remainingAccounts,
914
+ });
915
+ }
916
+
917
+ public async cancelOrder(orderId: BN): Promise<TransactionSignature> {
918
+ const { txSig } = await this.txSender.send(
919
+ wrapInTx(await this.getCancelOrderIx(orderId)),
848
920
  [],
849
921
  this.opts
850
922
  );
923
+ return txSig;
851
924
  }
852
925
 
853
- public async getCancelOrderIx(
854
- orderId: BN,
855
- oracle?: PublicKey
856
- ): Promise<TransactionInstruction> {
926
+ public async getCancelOrderIx(orderId: BN): Promise<TransactionInstruction> {
857
927
  const userAccountPublicKey = await this.getUserAccountPublicKey();
858
- const userAccount = await this.getUserAccount();
859
928
 
860
- const state = this.getStateAccount();
861
- const orderState = this.getOrderStateAccount();
929
+ const order = this.getOrder(orderId);
930
+ const oracle = this.getMarketAccount(order.marketIndex).amm.oracle;
862
931
 
863
- const remainingAccounts = [];
864
- if (oracle) {
865
- remainingAccounts.push({
866
- pubkey: oracle,
867
- isWritable: false,
868
- isSigner: false,
869
- });
870
- }
932
+ const remainingAccounts = this.getRemainingAccounts({});
871
933
 
872
934
  return await this.program.instruction.cancelOrder(orderId, {
873
935
  accounts: {
874
936
  state: await this.getStatePublicKey(),
875
937
  user: userAccountPublicKey,
876
938
  authority: this.wallet.publicKey,
877
- markets: state.markets,
878
- userOrders: await this.getUserOrdersAccountPublicKey(),
879
- userPositions: userAccount.positions,
880
- fundingPaymentHistory: state.fundingPaymentHistory,
881
- fundingRateHistory: state.fundingRateHistory,
882
- orderState: await this.getOrderStatePublicKey(),
883
- orderHistory: orderState.orderHistory,
939
+ oracle,
884
940
  },
885
941
  remainingAccounts,
886
942
  });
887
943
  }
888
944
 
889
945
  public async cancelOrderByUserId(
890
- userOrderId: number,
891
- oracle?: PublicKey
946
+ userOrderId: number
892
947
  ): Promise<TransactionSignature> {
893
- return await this.txSender.send(
894
- wrapInTx(await this.getCancelOrderByUserIdIx(userOrderId, oracle)),
948
+ const { txSig } = await this.txSender.send(
949
+ wrapInTx(await this.getCancelOrderByUserIdIx(userOrderId)),
895
950
  [],
896
951
  this.opts
897
952
  );
953
+ return txSig;
898
954
  }
899
955
 
900
956
  public async getCancelOrderByUserIdIx(
901
- userOrderId: number,
902
- oracle?: PublicKey
957
+ userOrderId: number
903
958
  ): Promise<TransactionInstruction> {
904
959
  const userAccountPublicKey = await this.getUserAccountPublicKey();
905
- const userAccount = await this.getUserAccount();
906
960
 
907
- const state = this.getStateAccount();
908
- const orderState = this.getOrderStateAccount();
961
+ const order = this.getOrderByUserId(userOrderId);
962
+ const oracle = this.getMarketAccount(order.marketIndex).amm.oracle;
909
963
 
910
- const remainingAccounts = [];
911
- if (oracle) {
912
- remainingAccounts.push({
913
- pubkey: oracle,
914
- isWritable: false,
915
- isSigner: false,
916
- });
917
- }
964
+ const remainingAccounts = this.getRemainingAccounts({});
918
965
 
919
966
  return await this.program.instruction.cancelOrderByUserId(userOrderId, {
920
967
  accounts: {
921
968
  state: await this.getStatePublicKey(),
922
969
  user: userAccountPublicKey,
923
970
  authority: this.wallet.publicKey,
924
- markets: state.markets,
925
- userOrders: await this.getUserOrdersAccountPublicKey(),
926
- userPositions: userAccount.positions,
927
- fundingPaymentHistory: state.fundingPaymentHistory,
928
- fundingRateHistory: state.fundingRateHistory,
929
- orderState: await this.getOrderStatePublicKey(),
930
- orderHistory: orderState.orderHistory,
971
+ oracle,
931
972
  },
932
973
  remainingAccounts,
933
974
  });
934
975
  }
935
976
 
936
977
  public async cancelAllOrders(
937
- oracles?: PublicKey[],
938
978
  bestEffort?: boolean
939
979
  ): Promise<TransactionSignature> {
940
- return await this.txSender.send(
941
- wrapInTx(await this.getCancelAllOrdersIx(oracles, bestEffort)),
980
+ const { txSig } = await this.txSender.send(
981
+ wrapInTx(await this.getCancelAllOrdersIx(bestEffort)),
942
982
  [],
943
983
  this.opts
944
984
  );
985
+ return txSig;
945
986
  }
946
987
 
947
988
  public async getCancelAllOrdersIx(
948
- oracles: PublicKey[],
949
989
  bestEffort?: boolean
950
990
  ): Promise<TransactionInstruction> {
951
991
  const userAccountPublicKey = await this.getUserAccountPublicKey();
952
- const userAccount = await this.getUserAccount();
953
992
 
954
- const state = this.getStateAccount();
955
- const orderState = this.getOrderStateAccount();
993
+ const remainingAccounts = this.getRemainingAccounts({});
956
994
 
957
- const remainingAccounts = [];
958
- for (const oracle of oracles) {
995
+ for (const order of this.getUserAccount().orders) {
996
+ const oracle = this.getMarketAccount(order.marketIndex).amm.oracle;
959
997
  remainingAccounts.push({
960
998
  pubkey: oracle,
961
999
  isWritable: false,
@@ -968,28 +1006,19 @@ export class ClearingHouse {
968
1006
  state: await this.getStatePublicKey(),
969
1007
  user: userAccountPublicKey,
970
1008
  authority: this.wallet.publicKey,
971
- markets: state.markets,
972
- userOrders: await this.getUserOrdersAccountPublicKey(),
973
- userPositions: userAccount.positions,
974
- fundingPaymentHistory: state.fundingPaymentHistory,
975
- fundingRateHistory: state.fundingRateHistory,
976
- orderState: await this.getOrderStatePublicKey(),
977
- orderHistory: orderState.orderHistory,
978
1009
  },
979
1010
  remainingAccounts,
980
1011
  });
981
1012
  }
982
1013
 
983
1014
  public async cancelOrdersByMarketAndSide(
984
- oracles?: PublicKey[],
985
1015
  bestEffort?: boolean,
986
1016
  marketIndexOnly?: BN,
987
1017
  directionOnly?: PositionDirection
988
1018
  ): Promise<TransactionSignature> {
989
- return await this.txSender.send(
1019
+ const { txSig } = await this.txSender.send(
990
1020
  wrapInTx(
991
1021
  await this.getCancelOrdersByMarketAndSideIx(
992
- oracles,
993
1022
  bestEffort,
994
1023
  marketIndexOnly,
995
1024
  directionOnly
@@ -998,22 +1027,20 @@ export class ClearingHouse {
998
1027
  [],
999
1028
  this.opts
1000
1029
  );
1030
+ return txSig;
1001
1031
  }
1002
1032
 
1003
1033
  public async getCancelOrdersByMarketAndSideIx(
1004
- oracles: PublicKey[],
1005
1034
  bestEffort?: boolean,
1006
1035
  marketIndexOnly?: BN,
1007
1036
  directionOnly?: PositionDirection
1008
1037
  ): Promise<TransactionInstruction> {
1009
1038
  const userAccountPublicKey = await this.getUserAccountPublicKey();
1010
- const userAccount = await this.getUserAccount();
1011
1039
 
1012
- const state = this.getStateAccount();
1013
- const orderState = this.getOrderStateAccount();
1040
+ const remainingAccounts = this.getRemainingAccounts({});
1014
1041
 
1015
- const remainingAccounts = [];
1016
- for (const oracle of oracles) {
1042
+ for (const order of this.getUserAccount().orders) {
1043
+ const oracle = this.getMarketAccount(order.marketIndex).amm.oracle;
1017
1044
  remainingAccounts.push({
1018
1045
  pubkey: oracle,
1019
1046
  isWritable: false,
@@ -1030,13 +1057,6 @@ export class ClearingHouse {
1030
1057
  state: await this.getStatePublicKey(),
1031
1058
  user: userAccountPublicKey,
1032
1059
  authority: this.wallet.publicKey,
1033
- markets: state.markets,
1034
- userOrders: await this.getUserOrdersAccountPublicKey(),
1035
- userPositions: userAccount.positions,
1036
- fundingPaymentHistory: state.fundingPaymentHistory,
1037
- fundingRateHistory: state.fundingRateHistory,
1038
- orderState: await this.getOrderStatePublicKey(),
1039
- orderHistory: orderState.orderHistory,
1040
1060
  },
1041
1061
  remainingAccounts,
1042
1062
  }
@@ -1045,320 +1065,354 @@ export class ClearingHouse {
1045
1065
 
1046
1066
  public async fillOrder(
1047
1067
  userAccountPublicKey: PublicKey,
1048
- userOrdersAccountPublicKey: PublicKey,
1049
- order: Order
1068
+ user: UserAccount,
1069
+ order: Order,
1070
+ makerInfo?: MakerInfo
1050
1071
  ): Promise<TransactionSignature> {
1051
- return await this.txSender.send(
1072
+ const { txSig } = await this.txSender.send(
1052
1073
  wrapInTx(
1053
- await this.getFillOrderIx(
1054
- userAccountPublicKey,
1055
- userOrdersAccountPublicKey,
1056
- order
1057
- )
1074
+ await this.getFillOrderIx(userAccountPublicKey, user, order, makerInfo)
1058
1075
  ),
1059
1076
  [],
1060
1077
  this.opts
1061
1078
  );
1079
+ return txSig;
1062
1080
  }
1063
1081
 
1064
1082
  public async getFillOrderIx(
1065
1083
  userAccountPublicKey: PublicKey,
1066
- userOrdersAccountPublicKey: PublicKey,
1067
- order: Order
1084
+ userAccount: UserAccount,
1085
+ order: Order,
1086
+ makerInfo?: MakerInfo
1068
1087
  ): Promise<TransactionInstruction> {
1069
1088
  const fillerPublicKey = await this.getUserAccountPublicKey();
1070
- const userAccount: any = await this.program.account.user.fetch(
1071
- userAccountPublicKey
1072
- );
1073
1089
 
1074
1090
  const marketIndex = order.marketIndex;
1075
- const oracle = this.getMarket(marketIndex).amm.oracle;
1091
+ const marketAccount = this.getMarketAccount(marketIndex);
1092
+ const oracle = marketAccount.amm.oracle;
1076
1093
 
1077
- const state = this.getStateAccount();
1078
- const orderState = this.getOrderStateAccount();
1094
+ const bankAccountInfos = [
1095
+ {
1096
+ pubkey: this.getQuoteAssetBankAccount().pubkey,
1097
+ isSigner: false,
1098
+ isWritable: true,
1099
+ },
1100
+ ];
1101
+ const marketAccountInfos = [
1102
+ {
1103
+ pubkey: marketAccount.pubkey,
1104
+ isWritable: true,
1105
+ isSigner: false,
1106
+ },
1107
+ ];
1108
+ const oracleAccountInfos = [
1109
+ {
1110
+ pubkey: marketAccount.amm.oracle,
1111
+ isWritable: false,
1112
+ isSigner: false,
1113
+ },
1114
+ ];
1115
+ for (const position of userAccount.positions) {
1116
+ if (
1117
+ !positionIsAvailable(position) &&
1118
+ !position.marketIndex.eq(order.marketIndex)
1119
+ ) {
1120
+ const market = this.getMarketAccount(position.marketIndex);
1121
+ marketAccountInfos.push({
1122
+ pubkey: market.pubkey,
1123
+ isWritable: false,
1124
+ isSigner: false,
1125
+ });
1126
+ oracleAccountInfos.push({
1127
+ pubkey: market.amm.oracle,
1128
+ isWritable: false,
1129
+ isSigner: false,
1130
+ });
1131
+ }
1132
+ }
1133
+ const remainingAccounts = oracleAccountInfos.concat(
1134
+ bankAccountInfos.concat(marketAccountInfos)
1135
+ );
1079
1136
 
1080
- const remainingAccounts = [];
1081
- if (!order.referrer.equals(PublicKey.default)) {
1137
+ if (makerInfo) {
1082
1138
  remainingAccounts.push({
1083
- pubkey: order.referrer,
1139
+ pubkey: makerInfo.maker,
1084
1140
  isWritable: true,
1085
1141
  isSigner: false,
1086
1142
  });
1087
1143
  }
1088
1144
 
1089
1145
  const orderId = order.orderId;
1090
- return await this.program.instruction.fillOrder(orderId, {
1146
+ const makerOrderId = makerInfo ? makerInfo.order.orderId : null;
1147
+ return await this.program.instruction.fillOrder(orderId, makerOrderId, {
1091
1148
  accounts: {
1092
1149
  state: await this.getStatePublicKey(),
1093
1150
  filler: fillerPublicKey,
1094
1151
  user: userAccountPublicKey,
1095
1152
  authority: this.wallet.publicKey,
1096
- markets: state.markets,
1097
- userPositions: userAccount.positions,
1098
- userOrders: userOrdersAccountPublicKey,
1099
- tradeHistory: state.tradeHistory,
1100
- fundingPaymentHistory: state.fundingPaymentHistory,
1101
- fundingRateHistory: state.fundingRateHistory,
1102
- orderState: await this.getOrderStatePublicKey(),
1103
- orderHistory: orderState.orderHistory,
1104
- extendedCurveHistory: state.extendedCurveHistory,
1105
1153
  oracle: oracle,
1106
1154
  },
1107
1155
  remainingAccounts,
1108
1156
  });
1109
1157
  }
1110
1158
 
1111
- public async initializeUserOrdersThenPlaceAndFillOrder(
1112
- orderParams: OrderParams,
1113
- discountToken?: PublicKey,
1114
- referrer?: PublicKey
1115
- ): Promise<TransactionSignature> {
1116
- const instructions: anchor.web3.TransactionInstruction[] = [];
1117
- const userOrdersAccountExists = await this.userOrdersAccountExists();
1118
- if (!userOrdersAccountExists) {
1119
- instructions.push(await this.getInitializeUserOrdersInstruction());
1120
- }
1121
- instructions.push(
1122
- await this.getPlaceAndFillOrderIx(orderParams, discountToken, referrer)
1123
- );
1124
- const tx = new Transaction();
1125
- for (const instruction of instructions) {
1126
- tx.add(instruction);
1127
- }
1128
-
1129
- return await this.txSender.send(tx, [], this.opts);
1130
- }
1131
-
1132
- public async placeAndFillOrder(
1159
+ public async placeAndTake(
1133
1160
  orderParams: OrderParams,
1134
- discountToken?: PublicKey,
1135
- referrer?: PublicKey
1161
+ makerInfo?: MakerInfo
1136
1162
  ): Promise<TransactionSignature> {
1137
- return await this.txSender.send(
1138
- wrapInTx(
1139
- await this.getPlaceAndFillOrderIx(orderParams, discountToken, referrer)
1140
- ),
1163
+ const { txSig, slot } = await this.txSender.send(
1164
+ wrapInTx(await this.getPlaceAndTakeIx(orderParams, makerInfo)),
1141
1165
  [],
1142
1166
  this.opts
1143
1167
  );
1168
+ this.marketLastSlotCache.set(orderParams.marketIndex.toNumber(), slot);
1169
+ return txSig;
1144
1170
  }
1145
1171
 
1146
- public async getPlaceAndFillOrderIx(
1172
+ public async getPlaceAndTakeIx(
1147
1173
  orderParams: OrderParams,
1148
- discountToken?: PublicKey,
1149
- referrer?: PublicKey
1174
+ makerInfo?: MakerInfo
1150
1175
  ): Promise<TransactionInstruction> {
1151
1176
  const userAccountPublicKey = await this.getUserAccountPublicKey();
1152
- const userAccount = await this.getUserAccount();
1153
1177
 
1154
- const priceOracle =
1155
- this.getMarketsAccount().markets[orderParams.marketIndex.toNumber()].amm
1156
- .oracle;
1178
+ const priceOracle = this.getMarketAccount(orderParams.marketIndex).amm
1179
+ .oracle;
1157
1180
 
1158
- const remainingAccounts = [];
1159
- if (orderParams.optionalAccounts.discountToken) {
1160
- if (!discountToken) {
1161
- throw Error(
1162
- 'Optional accounts specified discount token but no discount token present'
1163
- );
1164
- }
1181
+ const remainingAccounts = this.getRemainingAccounts({
1182
+ writableMarketIndex: orderParams.marketIndex,
1183
+ writableBankIndex: QUOTE_ASSET_BANK_INDEX,
1184
+ });
1165
1185
 
1186
+ let makerOrderId = null;
1187
+ if (makerInfo) {
1188
+ makerOrderId = makerInfo.order.orderId;
1166
1189
  remainingAccounts.push({
1167
- pubkey: discountToken,
1168
- isWritable: false,
1190
+ pubkey: makerInfo.maker,
1169
1191
  isSigner: false,
1170
- });
1171
- }
1172
-
1173
- if (orderParams.optionalAccounts.referrer) {
1174
- if (!referrer) {
1175
- throw Error(
1176
- 'Optional accounts specified referrer but no referrer present'
1177
- );
1178
- }
1179
-
1180
- remainingAccounts.push({
1181
- pubkey: referrer,
1182
1192
  isWritable: true,
1183
- isSigner: false,
1184
1193
  });
1185
1194
  }
1186
1195
 
1187
- const state = this.getStateAccount();
1188
- const orderState = this.getOrderStateAccount();
1189
- return await this.program.instruction.placeAndFillOrder(orderParams, {
1190
- accounts: {
1191
- state: await this.getStatePublicKey(),
1192
- user: userAccountPublicKey,
1193
- authority: this.wallet.publicKey,
1194
- markets: state.markets,
1195
- userOrders: await this.getUserOrdersAccountPublicKey(),
1196
- userPositions: userAccount.positions,
1197
- tradeHistory: state.tradeHistory,
1198
- fundingPaymentHistory: state.fundingPaymentHistory,
1199
- fundingRateHistory: state.fundingRateHistory,
1200
- orderState: await this.getOrderStatePublicKey(),
1201
- orderHistory: orderState.orderHistory,
1202
- extendedCurveHistory: state.extendedCurveHistory,
1203
- oracle: priceOracle,
1204
- },
1205
- remainingAccounts,
1206
- });
1196
+ return await this.program.instruction.placeAndTake(
1197
+ orderParams,
1198
+ makerOrderId,
1199
+ {
1200
+ accounts: {
1201
+ state: await this.getStatePublicKey(),
1202
+ user: userAccountPublicKey,
1203
+ authority: this.wallet.publicKey,
1204
+ oracle: priceOracle,
1205
+ },
1206
+ remainingAccounts,
1207
+ }
1208
+ );
1207
1209
  }
1208
1210
 
1209
1211
  /**
1210
1212
  * Close an entire position. If you want to reduce a position, use the {@link openPosition} method in the opposite direction of the current position.
1211
1213
  * @param marketIndex
1212
- * @param discountToken
1213
- * @param referrer
1214
1214
  * @returns
1215
1215
  */
1216
- public async closePosition(
1217
- marketIndex: BN,
1218
- discountToken?: PublicKey,
1219
- referrer?: PublicKey
1216
+ public async closePosition(marketIndex: BN): Promise<TransactionSignature> {
1217
+ const userPosition = this.getUser().getUserPosition(marketIndex);
1218
+ if (!userPosition) {
1219
+ throw Error(`No position in market ${marketIndex.toString()}`);
1220
+ }
1221
+
1222
+ return await this.placeAndTake(
1223
+ getMarketOrderParams(
1224
+ marketIndex,
1225
+ findDirectionToClose(userPosition),
1226
+ ZERO,
1227
+ userPosition.baseAssetAmount,
1228
+ true,
1229
+ undefined
1230
+ )
1231
+ );
1232
+ }
1233
+
1234
+ public async settlePNLs(
1235
+ users: {
1236
+ settleeUserAccountPublicKey: PublicKey;
1237
+ settleeUserAccount: UserAccount;
1238
+ }[],
1239
+ marketIndex: BN
1240
+ ): Promise<TransactionSignature> {
1241
+ const ixs = [];
1242
+ for (const { settleeUserAccountPublicKey, settleeUserAccount } of users) {
1243
+ ixs.push(
1244
+ await this.settlePNLIx(
1245
+ settleeUserAccountPublicKey,
1246
+ settleeUserAccount,
1247
+ marketIndex
1248
+ )
1249
+ );
1250
+ }
1251
+
1252
+ const tx = new Transaction().add(...ixs);
1253
+
1254
+ const { txSig } = await this.txSender.send(tx, [], this.opts);
1255
+ return txSig;
1256
+ }
1257
+
1258
+ public async settlePNL(
1259
+ settleeUserAccountPublicKey: PublicKey,
1260
+ settleeUserAccount: UserAccount,
1261
+ marketIndex: BN
1220
1262
  ): Promise<TransactionSignature> {
1221
- return await this.txSender.send(
1263
+ const { txSig } = await this.txSender.send(
1222
1264
  wrapInTx(
1223
- await this.getClosePositionIx(marketIndex, discountToken, referrer)
1265
+ await this.settlePNLIx(
1266
+ settleeUserAccountPublicKey,
1267
+ settleeUserAccount,
1268
+ marketIndex
1269
+ )
1224
1270
  ),
1225
1271
  [],
1226
1272
  this.opts
1227
1273
  );
1274
+ return txSig;
1228
1275
  }
1229
1276
 
1230
- public async getClosePositionIx(
1231
- marketIndex: BN,
1232
- discountToken?: PublicKey,
1233
- referrer?: PublicKey
1277
+ public async settlePNLIx(
1278
+ settleeUserAccountPublicKey: PublicKey,
1279
+ settleeUserAccount: UserAccount,
1280
+ marketIndex: BN
1234
1281
  ): Promise<TransactionInstruction> {
1235
- const userAccountPublicKey = await this.getUserAccountPublicKey();
1236
- const userAccount = await this.getUserAccount();
1237
-
1238
- const priceOracle =
1239
- this.getMarketsAccount().markets[marketIndex.toNumber()].amm.oracle;
1240
-
1241
- const optionalAccounts = {
1242
- discountToken: false,
1243
- referrer: false,
1244
- };
1245
- const remainingAccounts = [];
1246
- if (discountToken) {
1247
- optionalAccounts.discountToken = true;
1248
- remainingAccounts.push({
1249
- pubkey: discountToken,
1250
- isWritable: false,
1251
- isSigner: false,
1252
- });
1253
- }
1254
- if (referrer) {
1255
- optionalAccounts.referrer = true;
1256
- remainingAccounts.push({
1257
- pubkey: referrer,
1258
- isWritable: true,
1259
- isSigner: false,
1260
- });
1282
+ const marketAccountMap = new Map<number, AccountMeta>();
1283
+ const oracleAccountMap = new Map<string, AccountMeta>();
1284
+ const bankAccountMap = new Map<number, AccountMeta>();
1285
+ for (const position of settleeUserAccount.positions) {
1286
+ if (!positionIsAvailable(position)) {
1287
+ const market = this.getMarketAccount(position.marketIndex);
1288
+ marketAccountMap.set(position.marketIndex.toNumber(), {
1289
+ pubkey: market.pubkey,
1290
+ isWritable: true, // TODO
1291
+ isSigner: false,
1292
+ });
1293
+ oracleAccountMap.set(market.amm.oracle.toString(), {
1294
+ pubkey: market.amm.oracle,
1295
+ isWritable: false,
1296
+ isSigner: false,
1297
+ });
1298
+ }
1261
1299
  }
1262
1300
 
1263
- const state = this.getStateAccount();
1264
- return await this.program.instruction.closePosition(
1265
- marketIndex,
1266
- optionalAccounts,
1267
- {
1268
- accounts: {
1269
- state: await this.getStatePublicKey(),
1270
- user: userAccountPublicKey,
1271
- authority: this.wallet.publicKey,
1272
- markets: state.markets,
1273
- userPositions: userAccount.positions,
1274
- tradeHistory: state.tradeHistory,
1275
- fundingPaymentHistory: state.fundingPaymentHistory,
1276
- fundingRateHistory: state.fundingRateHistory,
1277
- oracle: priceOracle,
1278
- },
1279
- remainingAccounts: remainingAccounts,
1301
+ for (const userBankBalance of settleeUserAccount.bankBalances) {
1302
+ if (!userBankBalance.balance.eq(QUOTE_ASSET_BANK_INDEX)) {
1303
+ const bankAccount = this.getBankAccount(userBankBalance.bankIndex);
1304
+ bankAccountMap.set(userBankBalance.bankIndex.toNumber(), {
1305
+ pubkey: bankAccount.pubkey,
1306
+ isSigner: false,
1307
+ isWritable: false,
1308
+ });
1309
+ if (!bankAccount.bankIndex.eq(ZERO)) {
1310
+ oracleAccountMap.set(bankAccount.oracle.toString(), {
1311
+ pubkey: bankAccount.oracle,
1312
+ isSigner: false,
1313
+ isWritable: false,
1314
+ });
1315
+ }
1280
1316
  }
1281
- );
1282
- }
1317
+ }
1283
1318
 
1284
- public async closeAllPositions(
1285
- userPositionsAccount: UserPositionsAccount,
1286
- discountToken?: PublicKey,
1287
- referrer?: PublicKey
1288
- ): Promise<TransactionSignature> {
1289
- const ixs: TransactionInstruction[] = [];
1290
- for (const userPosition of userPositionsAccount.positions) {
1291
- if (userPosition.baseAssetAmount.eq(ZERO)) {
1292
- continue;
1293
- }
1319
+ const marketAccount = this.getMarketAccount(marketIndex.toNumber());
1320
+ marketAccountMap.set(marketIndex.toNumber(), {
1321
+ pubkey: marketAccount.pubkey,
1322
+ isSigner: false,
1323
+ isWritable: true,
1324
+ });
1325
+ oracleAccountMap.set(marketAccount.amm.oracle.toString(), {
1326
+ pubkey: marketAccount.amm.oracle,
1327
+ isSigner: false,
1328
+ isWritable: false,
1329
+ });
1294
1330
 
1295
- ixs.push(
1296
- await this.getClosePositionIx(
1297
- userPosition.marketIndex,
1298
- discountToken,
1299
- referrer
1300
- )
1301
- );
1302
- }
1331
+ bankAccountMap.set(QUOTE_ASSET_BANK_INDEX.toNumber(), {
1332
+ pubkey: this.getBankAccount(QUOTE_ASSET_BANK_INDEX).pubkey,
1333
+ isSigner: false,
1334
+ isWritable: true,
1335
+ });
1303
1336
 
1304
- const tx = new Transaction().add(...ixs);
1337
+ const remainingAccounts = [
1338
+ ...oracleAccountMap.values(),
1339
+ ...bankAccountMap.values(),
1340
+ ...marketAccountMap.values(),
1341
+ ];
1305
1342
 
1306
- return this.txSender.send(tx, [], this.opts);
1343
+ return await this.program.instruction.settlePnl(marketIndex, {
1344
+ accounts: {
1345
+ state: await this.getStatePublicKey(),
1346
+ authority: this.wallet.publicKey,
1347
+ user: settleeUserAccountPublicKey,
1348
+ },
1349
+ remainingAccounts: remainingAccounts,
1350
+ });
1307
1351
  }
1308
1352
 
1309
1353
  public async liquidate(
1310
1354
  liquidateeUserAccountPublicKey: PublicKey
1311
1355
  ): Promise<TransactionSignature> {
1312
- return this.txSender.send(
1356
+ const { txSig } = await this.txSender.send(
1313
1357
  wrapInTx(await this.getLiquidateIx(liquidateeUserAccountPublicKey)),
1314
1358
  [],
1315
1359
  this.opts
1316
1360
  );
1361
+ return txSig;
1317
1362
  }
1318
1363
 
1319
1364
  public async getLiquidateIx(
1320
1365
  liquidateeUserAccountPublicKey: PublicKey
1321
1366
  ): Promise<TransactionInstruction> {
1322
1367
  const userAccountPublicKey = await this.getUserAccountPublicKey();
1323
-
1324
- const liquidateeUserAccount: any = await this.program.account.user.fetch(
1368
+ const liquidateeUserAccount = (await this.program.account.user.fetch(
1325
1369
  liquidateeUserAccountPublicKey
1326
- );
1327
- const liquidateePositions: any =
1328
- await this.program.account.userPositions.fetch(
1329
- liquidateeUserAccount.positions
1330
- );
1331
- const markets = this.getMarketsAccount();
1370
+ )) as UserAccount;
1332
1371
 
1333
- const remainingAccounts = [];
1334
- for (const position of liquidateePositions.positions) {
1335
- if (!position.baseAssetAmount.eq(new BN(0))) {
1336
- const market = markets.markets[position.marketIndex.toNumber()];
1337
- remainingAccounts.push({
1372
+ const bankAccountInfos = [
1373
+ {
1374
+ pubkey: this.getQuoteAssetBankAccount().pubkey,
1375
+ isSigner: false,
1376
+ isWritable: true,
1377
+ },
1378
+ ];
1379
+ const marketAccountInfos = [];
1380
+ const oracleAccountInfos = [];
1381
+ for (const position of liquidateeUserAccount.positions) {
1382
+ if (!positionIsAvailable(position)) {
1383
+ const market = this.getMarketAccount(position.marketIndex);
1384
+ const marketPublicKey = await getMarketPublicKey(
1385
+ this.program.programId,
1386
+ position.marketIndex
1387
+ );
1388
+ marketAccountInfos.push({
1389
+ pubkey: marketPublicKey,
1390
+ isWritable: true,
1391
+ isSigner: false,
1392
+ });
1393
+ oracleAccountInfos.push({
1338
1394
  pubkey: market.amm.oracle,
1339
1395
  isWritable: false,
1340
1396
  isSigner: false,
1341
1397
  });
1342
1398
  }
1343
1399
  }
1400
+ const remainingAccounts = oracleAccountInfos.concat(
1401
+ bankAccountInfos.concat(marketAccountInfos)
1402
+ );
1344
1403
 
1345
1404
  const state = this.getStateAccount();
1405
+ const quoteAssetBankAccount = this.getQuoteAssetBankAccount();
1346
1406
  return await this.program.instruction.liquidate({
1347
1407
  accounts: {
1348
1408
  state: await this.getStatePublicKey(),
1349
1409
  authority: this.wallet.publicKey,
1350
1410
  user: liquidateeUserAccountPublicKey,
1351
1411
  liquidator: userAccountPublicKey,
1352
- collateralVault: state.collateralVault,
1353
- collateralVaultAuthority: state.collateralVaultAuthority,
1412
+ bankVault: quoteAssetBankAccount.vault,
1413
+ bankVaultAuthority: quoteAssetBankAccount.vaultAuthority,
1354
1414
  insuranceVault: state.insuranceVault,
1355
- insuranceVaultAuthority: state.insuranceVaultAuthority,
1356
1415
  tokenProgram: TOKEN_PROGRAM_ID,
1357
- markets: state.markets,
1358
- userPositions: liquidateeUserAccount.positions,
1359
- tradeHistory: state.tradeHistory,
1360
- liquidationHistory: state.liquidationHistory,
1361
- fundingPaymentHistory: state.fundingPaymentHistory,
1362
1416
  },
1363
1417
  remainingAccounts: remainingAccounts,
1364
1418
  });
@@ -1368,54 +1422,68 @@ export class ClearingHouse {
1368
1422
  oracle: PublicKey,
1369
1423
  marketIndex: BN
1370
1424
  ): Promise<TransactionSignature> {
1371
- return this.txSender.send(
1425
+ const { txSig } = await this.txSender.send(
1372
1426
  wrapInTx(await this.getUpdateFundingRateIx(oracle, marketIndex)),
1373
1427
  [],
1374
1428
  this.opts
1375
1429
  );
1430
+ return txSig;
1376
1431
  }
1377
1432
 
1378
1433
  public async getUpdateFundingRateIx(
1379
1434
  oracle: PublicKey,
1380
1435
  marketIndex: BN
1381
1436
  ): Promise<TransactionInstruction> {
1382
- const state = this.getStateAccount();
1383
1437
  return await this.program.instruction.updateFundingRate(marketIndex, {
1384
1438
  accounts: {
1385
1439
  state: await this.getStatePublicKey(),
1386
- markets: state.markets,
1440
+ market: await getMarketPublicKey(this.program.programId, marketIndex),
1387
1441
  oracle: oracle,
1388
- fundingRateHistory: state.fundingRateHistory,
1389
1442
  },
1390
1443
  });
1391
1444
  }
1392
1445
 
1393
1446
  public async settleFundingPayment(
1394
- userAccount: PublicKey,
1395
- userPositionsAccount: PublicKey
1447
+ userAccount: PublicKey
1396
1448
  ): Promise<TransactionSignature> {
1397
- return this.txSender.send(
1398
- wrapInTx(
1399
- await this.getSettleFundingPaymentIx(userAccount, userPositionsAccount)
1400
- ),
1449
+ const { txSig } = await this.txSender.send(
1450
+ wrapInTx(await this.getSettleFundingPaymentIx(userAccount)),
1401
1451
  [],
1402
1452
  this.opts
1403
1453
  );
1454
+ return txSig;
1404
1455
  }
1405
1456
 
1406
1457
  public async getSettleFundingPaymentIx(
1407
- userAccount: PublicKey,
1408
- userPositionsAccount: PublicKey
1458
+ userAccount: PublicKey
1409
1459
  ): Promise<TransactionInstruction> {
1410
- const state = this.getStateAccount();
1460
+ const user = (await this.program.account.user.fetch(
1461
+ userAccount
1462
+ )) as UserAccount;
1463
+
1464
+ const userPositions = user.positions;
1465
+
1466
+ const remainingAccounts = [];
1467
+ for (const position of userPositions) {
1468
+ if (!positionIsAvailable(position)) {
1469
+ const marketPublicKey = await getMarketPublicKey(
1470
+ this.program.programId,
1471
+ position.marketIndex
1472
+ );
1473
+ remainingAccounts.push({
1474
+ pubkey: marketPublicKey,
1475
+ isWritable: false,
1476
+ isSigner: false,
1477
+ });
1478
+ }
1479
+ }
1480
+
1411
1481
  return await this.program.instruction.settleFundingPayment({
1412
1482
  accounts: {
1413
1483
  state: await this.getStatePublicKey(),
1414
- markets: state.markets,
1415
1484
  user: userAccount,
1416
- userPositions: userPositionsAccount,
1417
- fundingPaymentHistory: state.fundingPaymentHistory,
1418
1485
  },
1486
+ remainingAccounts,
1419
1487
  });
1420
1488
  }
1421
1489
 
@@ -1423,121 +1491,10 @@ export class ClearingHouse {
1423
1491
  this.eventEmitter.emit(eventName, data);
1424
1492
  }
1425
1493
 
1426
- public async settlePositionAndClaimCollateral(
1427
- collateralAccountPublicKey: PublicKey
1428
- ): Promise<TransactionSignature> {
1429
- const settlePositionIx = await this.getSettlePositionIx();
1430
- const claimCollateralIx = await this.getClaimCollateralIx(
1431
- collateralAccountPublicKey
1432
- );
1433
- const tx = new Transaction().add(settlePositionIx).add(claimCollateralIx);
1434
-
1435
- return this.txSender.send(tx, [], this.opts);
1436
- }
1437
-
1438
- public async getSettlePositionIx(): Promise<TransactionInstruction> {
1439
- const userAccountPublicKey = await this.getUserAccountPublicKey();
1440
- const user: any = await this.program.account.user.fetch(
1441
- userAccountPublicKey
1442
- );
1443
- const state = this.getStateAccount();
1444
-
1445
- const settlementState = await getSettlementStatePublicKey(
1446
- this.program.programId
1447
- );
1448
-
1449
- return await this.program.instruction.settlePosition({
1450
- accounts: {
1451
- state: await this.getStatePublicKey(),
1452
- user: userAccountPublicKey,
1453
- markets: state.markets,
1454
- authority: this.wallet.publicKey,
1455
- userPositions: user.positions,
1456
- settlementState,
1457
- fundingPaymentHistory: state.fundingPaymentHistory,
1458
- },
1459
- });
1460
- }
1461
-
1462
- public async getClaimCollateralIx(
1463
- collateralAccountPublicKey: PublicKey
1464
- ): Promise<TransactionInstruction> {
1465
- const userAccountPublicKey = await this.getUserAccountPublicKey();
1466
- const state = this.getStateAccount();
1467
-
1468
- const settlementState = await getSettlementStatePublicKey(
1469
- this.program.programId
1470
- );
1471
-
1472
- return await this.program.instruction.claimCollateral({
1473
- accounts: {
1474
- state: await this.getStatePublicKey(),
1475
- user: userAccountPublicKey,
1476
- collateralVault: state.collateralVault,
1477
- collateralVaultAuthority: state.collateralVaultAuthority,
1478
- userCollateralAccount: collateralAccountPublicKey,
1479
- authority: this.wallet.publicKey,
1480
- tokenProgram: TOKEN_PROGRAM_ID,
1481
- settlementState,
1482
- },
1483
- });
1484
- }
1485
-
1486
- public async getTotalSettlementSize(): Promise<BN> {
1487
- const accountLoader = new BulkAccountLoader(
1488
- this.connection,
1489
- 'processed',
1490
- 50000
1491
- );
1492
- const clearingHouse = getClearingHouse(
1493
- getPollingClearingHouseConfig(
1494
- this.connection,
1495
- this.wallet,
1496
- this.program.programId,
1497
- accountLoader
1498
- )
1499
- );
1500
-
1501
- console.log('loading all users');
1502
- const programUserAccounts =
1503
- (await this.program.account.user.all()) as any[];
1504
- const userArray: ClearingHouseUser[] = [];
1505
- for (const programUserAccount of programUserAccounts) {
1506
- const user = getClearingHouseUser(
1507
- getPollingClearingHouseUserConfig(
1508
- clearingHouse,
1509
- programUserAccount.account.authority,
1510
- accountLoader
1511
- )
1512
- );
1513
- userArray.push(user);
1514
- }
1515
-
1516
- console.log('subscribing all users');
1517
- await bulkPollingUserSubscribe(userArray, accountLoader);
1518
-
1519
- console.log('calculating settlement size');
1520
- const settlementSize = userArray.reduce((collateralToBeSettled, user) => {
1521
- return collateralToBeSettled.add(
1522
- user.getUserAccount().forgoPositionSettlement === 0
1523
- ? user.getSettledPositionValue()
1524
- : ZERO
1525
- );
1526
- }, ZERO);
1527
-
1528
- for (const user of userArray) {
1529
- await user.unsubscribe();
1530
- }
1531
-
1532
- return settlementSize;
1533
- }
1494
+ public getOracleDataForMarket(marketIndex: BN): OraclePriceData {
1495
+ const oracleKey = this.getMarketAccount(marketIndex).amm.oracle;
1496
+ const oracleData = this.getOraclePriceDataAndSlot(oracleKey).data;
1534
1497
 
1535
- public async updateUserForgoSettlement(): Promise<TransactionSignature> {
1536
- return await this.program.rpc.updateUserForgoSettlement({
1537
- accounts: {
1538
- authority: this.wallet.publicKey,
1539
- user: await this.getUserAccountPublicKey(),
1540
- },
1541
- });
1498
+ return oracleData;
1542
1499
  }
1543
1500
  }