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